Данная страница будет полезной для тех, кто решил взять заказ на парсер аудио-треков VK и резко понял, что ничего не понял.

В чем проблема


Знакомо?

https://m.vk.com/mp3/audio_api_unavailable.mp3?extra=AeL2rMfFyZzlD3HkyvfnvNvLx1KOqw5UDfuXCOTvttm4ts1OBJnYELvHyxvODI9fnM9YztD5A3iOyI14sxv2mNiXt3iTzdLInduXzvG9C2uVr3b5mezinfj2lJbpDhGYC25rDxbwsOPQmg1eu2Pbyxr3ntPowNLhDMrrDs8XnKu2sOuOyO8XzMf1otDmBtL6BNvllNjZx3aZuLHpq3aOBvvhzenJnZKTzKnMuwfKBI4TquffrtzKv2nymMyVDu1LzJnuwMLxwMm/BeTcserWlun3ExLVBG#AqSZntu

Если да — то вы пытались парсить мобильную версию сайта и успешно доставали ссылки. Неверные ссылки. Ссылки на 25-секундный голос, сообщающий что все идет не по плану.

Если нет — вам стоит попробовать.

Как получить верный URL


А вот это верный вопрос! Дело в том, что перед воспроизведением записи, вк натравливает на такой url заготовленные js-скрипты. В общем-то ничего сложного — несколько переворотов строк, побитовые сдвиги, даже одно побитовое отрицание. И все это сжато компрессором.

Честно говоря, раньше искать функции, отвечающие за это дело, было сложнее. Судя по всему во Вконтакте завелись кроты)) Иначе как блин объяснить то, что они подписали, буквально повесили вывеску на самом нужном месте:

image

Ладно, ладно, все мы рабы сборщиков…

Без лишних слов, актуальный код


Декодер на JavaScript
var id = 0; //Ваш userid
var n = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN0PQRSTUVWXYZO123456789+/=",
	i = {
		v: function(e) {
			return e.split("").reverse().join("")
		},
		r: function(e, t) {
			e = e.split("");
			for (var i, o = n + n, s = e.length; s--;) i = o.indexOf(e[s]), ~i && (e[s] = o.substr(i - t, 1));
			return e.join("")
		},
		s: function(e, t) {
			var n = e.length;
			if (n) {
				var i = r(e, t),
					o = 0;
				for (e = e.split(""); ++o < n;) e[o] = e.splice(i[n - 1 - o], 1, e[o])[0];
				e = e.join("")
			}
			return e
		},
		i: function(e, t) {
			return i.s(e, t ^ id)
		},
		x: function(e, t) {
			var n = [];
			return t = t.charCodeAt(0), each(e.split(""), function(e, i) {
				n.push(String.fromCharCode(i.charCodeAt(0) ^ t))
			}), n.join("")
		}
	};

function o() {
	return window.wbopen && ~(window.open + "").indexOf("wbopen")
}

function s(e) {
	if (!o() && ~e.indexOf("audio_api_unavailable")) {
		var t = e.split("?extra=")[1].split("#"),
			n = "" === t[1] ? "" : a(t[1]);
		if (t = a(t[0]), "string" != typeof n || !t) return e;
		n = n ? n.split(String.fromCharCode(9)) : [];
		for (var s, r, l = n.length; l--;) {
			if (r = n[l].split(String.fromCharCode(11)), s = r.splice(0, 1, t)[0], !i[s]) return e;
			t = i[s].apply(null, r)
		}
		if (t && "http" === t.substr(0, 4)) return t
	}
	return e
}

function a(e) {
	if (!e || e.length % 4 == 1) return !1;
	for (var t, i, o = 0, s = 0, a = ""; i = e.charAt(s++);) i = n.indexOf(i), ~i && (t = o % 4 ? 64 * t + i : i, o++ % 4) && (a += String.fromCharCode(255 & t >> (-2 * o & 6)));
	return a
}

function r(e, t) {
	var n = e.length,
		i = [];
	if (n) {
		var o = n;
		for (t = Math.abs(t); o--;) t = (n * (o + 1) ^ t + o) % n, i[o] = t
	}
	return i
}


Буква в букву декодер на PHP
global $n, $i, $id;
$n = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN0PQRSTUVWXYZO123456789+/=";
$id = 123456789; //user_id
$i = [
	'v' => function($e) {
		return strrev($e);
	},
	'r' => function($e, $t){
		global $n;
		$e = str_split($e);
		for ($o = $n . $n, $s = count($e); $s--;){
			$i = stripos($o, $e[$s]);
			if(~$i){
				$e[$s] = substr($o, $i - $t, 1);
			}
		}
		return implode("", $e);
	},
	's' => function($e, $t) {
		$n = strlen($e);
		if ($n) {
			$i = r($e, $t);
			$o = 0;
			$e = str_split($e);
			for (; ++$o < $n;){
				$p = array_splice($e, $i[$n - 1 - $o], 1, $e[$o]);
				$e[$o] = $p[0];
			}

			$e = implode("", $e);
		}

		return $e;
	},
	'i' => function($e, $t){
		global $i, $id;
		$k = $i['s'];
		return $k($e, $t ^ $id);
	},
];

function o() {
	return false;
}

function a($e){
	global $n;
	if (!$e || strlen($e) % 4 == 1) {
		return !1;
	}
	$s = 0;
	for ($o = 0, $a = "";$s < strlen($e);) {
		$i = $e[$s++];
		$i = strpos($n, $i);
		if ($i !== false) {
			$t = ($o % 4) ? 64 * $t + $i : $i;
			if ($o++ % 4) {
				$a .= chr(255 & $t >> (-2 * $o & 6));
			}
		}
	}

	return $a;
}

function r($e, $t) {
	$n = strlen($e);
	$i = [];
	if ($n) {
		$o = $n;
		$t = abs($t);
		for (; $o--;){
			$t = ($n * ($o + 1) ^ $t + $o) % $n;
			$i[$o] = $t;
		}
	}
	return $i;
}

function s($e){
	global $i;
	if (!o() && strpos($e, "audio_api_unavailable") !== false) {
		$t = explode("?extra=", $e);
		$t = $t[1];
		$t = explode("#", $t);
		$n = ("" === $t[1]) ? "" : a($t[1]);
		$t = a($t[0]);
		if (!is_string($n) || !$t){ return $e;}
		$n = $n ? explode(chr(9), $n) : [];
		for ($l = count($n); $l--;) {
			$r = explode(chr(11), $n[$l]);
			$s = array_splice($r, 0, 1, $t);
			$s = $s[0];
			if (!$i[$s]){ return $e; }
			$t = $i[$s](...$r);
		}
		if ($t && "http" === substr($t, 0, 4)){ return $t;}
	}
	return $e;
}



В обоих случаях

s("https://m.vk.com/mp3/audio_api_unavailable.mp3?extra=encodeextraurl");

Думаю, при надобности с PHP на другой язык перевести код будет уже проще.

Статья написана с целью снизить кол-во человекоминут в мире, затрачиваемых на эту задачу.

P.s: Актуальное решение всегда можно будет найти здесь: gist.github.com/in4in-dev/09f32f313f11b2c10778d9e2ffe7e60e
P.s2: Пользователь ImIeee также обновляет свое решение в репозитории github.com/vodka2/vkaudio (тут вы найдете решение на Python)

Комментарии (33)


  1. XanderBass
    01.11.2018 17:27
    +9

    Поменяют и спрячут алгоритм теперь после этой публикации.


    1. Taraflex
      01.11.2018 22:04
      +1

      Зато автор публикации получит новый заказ на доработку парсера )))


    1. catanfa
      02.11.2018 01:56
      +3

      предлагаю In4in завести github репозиторий и обновлять свой декодер вслед за вк, в случае изменений со стороны вк.


      1. In4in Автор
        02.11.2018 06:23
        +1

    1. In4in Автор
      02.11.2018 06:23

      Не думаю, что из-за статьи будут что-то менять. В конце концов, они и не прячут декодер — просто скрывают таким образом музыку от обывателя.


      1. riky
        02.11.2018 09:54

        ровно год назад была статья на эту тему.
        интересно за это время сильно изменился механизм?
        habr.com/post/340810


        1. In4in Автор
          02.11.2018 10:09

          Сравнивал вскользь недавно.

          Они добавили привязку к id пользователя
          i: function(e, t) {
          	return i.s(e, t ^ vk.id)
          },


          1. kto-to_tam
            02.11.2018 14:34

            На сколько понимаю, они этим самым проверяют локаль пользователя… и в плейлисте появляется то и дело — «Аудиозапись your_favorite_music недоступна для прослушивания в Вашем регионе.»
            Но можно же, в теории, позаимствовать айди товарища из «нужной» страны… Хотя вроде там более замароченно.


            1. In4in Автор
              02.11.2018 14:35

              Думаю, за это отвечает другой механизм и на такие аудио даже ссылки не выдает. К сожалению, мне такие записи не попадались и как это обойти — ответить трудно. Попробую зайти через впн глянуть.


              1. SandroSmith
                02.11.2018 19:08

                Именно ВПН и позволяет обойти «ваш регион».
                Точнее, если у туннеля выход в Европе — DMCA(или кто там у них) блюдётся. Если в России — слушай на здоровье.


              1. kto-to_tam
                02.11.2018 22:23

                А у меня, к сожалению, такое встречается постоянно… если не пользуюсь русским прокси.
                Именно поэтому перестал пользоваться вк музыкой как таковой… проще уж из других источников слушать, чем слушать трэк через три.


    1. megahertz
      02.11.2018 09:59

      А никто ничего не прячет, похожий на base64 декодер подогнанный под свои нужды


    1. jimmyjonezz
      02.11.2018 10:10

      Всегда есть возможность использовать telegram бота…


  1. mastacamp
    01.11.2018 23:30
    +3

    главное успеть скачать всю свою музыку) я успел) автору большой респект!!!


  1. ilyamodder
    02.11.2018 02:59

    А зачем? Можно же просто взять ID официального приложения и пользоваться API для аудио как раньше.


    1. Rast1234
      02.11.2018 06:39

      Там не просто, надо еще прикинуться, например, андроидом и по-хитрому получить несколтко токенов, чтобы это сработало. На гитхабе видел реализацию, но у меня что-то пошло не так.


      1. ImIeee
        03.11.2018 09:19

        Не эту реализацию случайно — github.com/vodka2/vk-audio-token? Если эту, то можно было создать Issue, кроме того, не так давно я добавил примеры использования полученных токенов. Если другую, можете, если не трудно, написать ссылку? Вообще, на мой взгляд, использование API намного удобнее, чем парсинг сайта.


        1. Rast1234
          03.11.2018 10:35
          +1

          Реализация ваша, да. Спасибо за нее :)
          Я просто ради интереса решил переписать на шарпе, поэтому issue не помогут. Токены получаю от гугла и от вк, но что-то делаю не так — vk api всегда возвращает тот самый 25-секундный семпл. Хочу разобраться и запилить экспортер всего-и-вся-из-вк, но все руки не доходят.


          1. ImIeee
            03.11.2018 11:17

            Сложно сказать, не видя кода, но, возможно, вы делаете запрос к API с другим User-Agent. В принципе, я мог бы посмотреть ваш код, если бы вы выложили его на Github, всё-таки идея хорошая.


            1. Rast1234
              03.11.2018 11:45

              Если не справлюсь, напишу вам) все-таки интересно и самому поковыряться


    1. nokimaro
      02.11.2018 12:51
      +1

      После того как появилась платная подписка на музыку, сделали завязку на google play, и для полноценной работы нужно проходить доп. валидацию с использованием android_id (device_id) при которой api токен меняется на такой же токен с доп. правами, в частности с правами на доступ к audio api, при том что документацию по audio api с сайта вк выпилили.


      1. ImIeee
        03.11.2018 09:31

        Официальный клиент ВК для Android может работать и без валидации, правда, там другие методы API.


  1. Legomegger
    02.11.2018 09:45

    Значит ли это что сейчас опять появятся на пару недель over 9000 приложений музыки в AppStore?


    1. In4in Автор
      02.11.2018 10:12
      +1

      Как ответила мне одна девушка, «У меня целых три таких приложения и я жму там одну кнопочку, а ты тут столько строк какой-то фигни написал»)


      1. Skerrigan
        02.11.2018 10:27
        -1

        «Глаза» то у девушки хоть большие?


    1. navion
      02.11.2018 10:43
      -1

      Даже Google сейчас выпиливает плагины для скачивания музыки из VK.


      1. serg_deep
        02.11.2018 14:37

        .


    1. dimka11
      02.11.2018 12:52

      Такие приложения разве не нарушают условия соглашений?


  1. DevFish
    02.11.2018 09:45

    Красивый код на php)


  1. kolyan222
    02.11.2018 11:57

    Подозреваю, что это шифрование ссылок сделано только для того, что бы у правообладателей не было претензий к сервису.


  1. namikiri
    02.11.2018 13:24
    -2

    Данная страница будет полезной для тех, кто решил взять заказ на парсер аудио-треков VK и резко понял, что ничего не понял.


    А если для себя решил написать, то страница полезной не будет?


  1. ImIeee
    02.11.2018 19:53
    +1

    Давно уже поддерживаю аналогичный код на Github и на Тостере. Даже скрипт для обратного шифрования есть.


    1. In4in Автор
      02.11.2018 19:59

      Добавил к статье :)