Pull to refresh

Получение ссылок на аудио без VKApi

Reading time3 min
Views43K
Данная страница будет полезной для тех, кто решил взять заказ на парсер аудио-треков 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)
Tags:
Hubs:
Total votes 51: ↑49 and ↓2+47
Comments41

Articles