パブリックミュージックAPIなしでVKから音楽をドラッグする

それがすべて始まった方法



それは夕方でした、何もすることはありませんでした...より正確には、私はちょうど夫婦の前でオーディオブックをダウンロードしたかった、そして驚きが私を待っていました。 ケイトモバイルのキャッシュがオフになりました。 どうして? どうする もちろん、キャッシュとオーディオ録音を使用してアプリケーションを作成します。 ただし、最初にVKがaudio%user_id%_%track_id%という形式のリンクをmp3への直接リンクに変換する方法を理解する必要があります。 私はこのアプリケーションに付属するものを書いおらず 、特定のプレイリストをダウンロードする方法はカットの下で読むことができます。



デボジムjs



明らかなことから始めます-オーディオ録音のタブを開き、コードを見てください。 各オーディオ録音のボタンのonclickが表示されます。



onclick="return getAudioPlayer().toggleAudio(this, event)"
      
      





audioplayer.jsを開き、プリティプリントをオンにして、toggleAudio()関数を探します。



toggleAudio()
 AudioPlayer.prototype.toggleAudio = function(t, e) { if (vk && vk.widget && !vk.id && window.Widgets) return Widgets.oauth(), !1; if (domClosest("_audio_row__tt", e.target)) return cancelEvent(e); var i = domClosest("_audio_row", t) , o = AudioUtils.getAudioFromEl(i, !0); if (window.getSelection && window.getSelection().rangeCount) { var a = window.getSelection().getRangeAt(0); if (a && a.startOffset != a.endOffset) return !1 } if (e && hasClass(e.target, "mem_link")) return nav.go(attr(e.target, "href"), e, { navigateToUploader: !0 }), cancelEvent(e); if (hasClass(e.target, "_audio_row__title_inner") && o.lyrics && !o.isInAttach) return AudioUtils.toggleAudioLyrics(i, o), cancelEvent(e); if (hasClass(e.target, "audio_row__performer")) return checkEvent(e) || vk.widget ? !0 : (AudioUtils.audioSearchPerformer(e.target, o.performer, e), cancelEvent(e)); var s = cur.cancelClick || e && (hasClass(e.target, "audio_lyrics") || domClosest("_audio_duration_wrap", e.target) || domClosest("_audio_inline_player", e.target) || domClosest("audio_performer", e.target)); if (cur._sliderMouseUpNowEl && cur._sliderMouseUpNowEl == geByClass1("audio_inline_player_progress", i) && (s = !0), delete cur.cancelClick, delete cur._sliderMouseUpNowEl, s) return !0; if (AudioUtils.isClaimedAudio(o) || o.isReplaceable) { var r = AudioUtils.getAudioExtra(o) , l = r.claim; if (l) return void (hasClass(i, "no_actions") || o.isInEditBox || showAudioClaimWarning(o, l, AudioUtils.replaceWithOriginal.bind(AudioUtils, i, o))) } if (o.isPlaying) this.pause(); else { var n = AudioUtils.getContextPlaylist(i); this.play(o.fullId, n.playlist, o.context || n.context), cur.audioPage && cur.audioPage.onUserAction(o, n.playlist) } AudioUtils.onRowOver(i, !1, !0) }
      
      







最初の命令にブレークポイントを置き、ボタンをクリックします。 デバッグモードに入り、段階的にコードの実行を開始します。



数ステップ後の変数oには、エグゼキューター、名前、ハッシュ、あらゆる種類のIDなどの有用な情報がたくさん含まれていることがわかります。 ただし、メインプロパティであるurlは含まれません。

先に進んで、toggleAudio()を終了し、イベントを処理する関数のcommon.jsファイルに入ります。 ええ、それはリンクを非同期に取得することを意味します。ネットワークアクティビティを確認する必要があります。 nステップ後、興味深いリクエストが[ネットワーク]タブに表示されていることがわかります。



Request URL:https://vk.com/al_audio.php

Request Method:POST



Form Data:

act:reload_audio

al:1

ids: id %user_id%_%track_id%,








そして、それほど興味深い答えはありません。



4089188939145<!><!>0<!>6854<!>0<!><!json>[[456239119,%my_id%,"https:\/\/vk.com\/mp3\/audio_api_unavailable.mp3?extra=ofvLohaZvtDKnOPmEtHWl2rJCLLMrJiZy1i4D3blohn4AZLflLqZtMn3utbJmeTrCdq2Be1LyJ1HDvqTBKnUne8YrJfzzKrVENKXzL9JmMHgEgz3u3nbDwfuBMDiywLJBOrfl2fQwJDRmvrFzwDbwtGWvwThDxy6lxLHt1KOlvDODhbTAgjOzffOzdvTBOvOms9nvO9Ix1bxrNqUwgnfAfLWnwfLDLb0CLLxAvCVr2r4ExbHzhPTlKS2p3zcodu3yxm4Ea#CWS1mZi"," ",".. ",4705,0,0,"",0,2,"","[]","6adb4186ee0c1d3ad0\/5102a312745ae505a7\/08eb0e4bd407e74e76\/d313ec4b6051942649\/","",[]],[456239118,%my_id%,"https:\/\/vk.com\/mp3\/audio_api_unavailable.mp3?extra=AdbkuuLOywjuou0Zme1yrdzJsI9Tm2qYvIO2AJaWnMnIztKOzgLxmMfbu3rgyJ8Tme5MBNrKlMLpChPIBLLFngjvu3zZAxztuOXJztfgrK9vq1rznLCYDJznBMrMCMT4rO9PytD3oc5kAhyYm1jMmZeZlxqZsKfWyKO9DNGUmvfuBtfOudjXrvbmyZLVltvICvPIver5zg5Zm2jWmJvWsue5nuXWzgPnBZq6l2n3x30OCgCVC2SVmxjqzvD6Egrlnc1zyq#CWSYmdC"," "," ",359,0,0,"",0,18,"","[]","57c59cffa93d47effa\/836d457cff34e02fa0\/6374a8e457c763a8c6\/96db3ddc8c210b1fb0\/","",[]],[456239117,%my_id%,"https:\/\/vk.com\/mp3\/audio_api_unavailable.mp3?extra=AgDYnY1TmwmOm2vTos5Ylu9Juwzkugv4zMzZyJbVlKjcqI9LB2vdzhe6DufVqY9KA1uZy3bfnhm4AgDOAwHIzMDztMTxsLjHn2K9veXuttvKq2fxogTWxY1fzJvumJrADLDlCdnLAv8UAfPiB2zxEKTrttLTrtK2ntrLCLnOww5rtI1Rq2vzsgr2vMnYp1P5BeOOx3rIEs8WBI10yLntCNzOCKrLBtznBMzRC29ewMOOC3uTueuYl29OztnWDhCXoffz#CWSYntC","Sal sér hon standa (Völuspá, 64-66)","Nytt Land",272,0,0,"",0,82,"","[]","4881448c55978a3374\/40a707901f551a4572\/778fe59467e6a629a9\/02a308905303098496\/","https:\/\/pp.userapi.com\/c837628\/v837628453\/829d0\/kLoB-0G_r78.jpg,https:\/\/pp.userapi.com\/c837628\/v837628453\/829cf\/C39pJ5b-tw.jpg",80],[456239116,%my_id%,"https:\/\/vk.com\/mp3\/audio_api_unavailable.mp3?extra=l1yUmI1MCc8Vsxq2qxHYoM1Ku2i9C1y\/EdzWzZfADhrpDgGYrwvJwwTRtZzHDwL3A2HZqKXXrOfHtZDVDKzZn1zIrKLJD3PVBKqOzxLylKDjvZuYqKf5qLG3rhn4nJnHs1rlt1PUy29mCvLuuxD6DJHJAtGXvNfjrMzkt29Wme93mKuWze55x1PotO01lMqZnuHfzJLgBM1Jluzqogvkr3KYs1fNmtn6qODYyx0XnxDZDNnvvO9Lsgn0tdrKDc9Kowm2#CWSOmJy","After Dark "," Mr.Kitty",351,0,0,"",0,18,"","[]","353d934506a14abed5\/5745fb63e4e5f4abb4\/2ed8c9b81df35317d7\/01404a5db986cf16b8\/","",[]],[456239115,%my_id%,"https:\/\/vk.com\/mp3\/audio_api_unavailable.mp3?extra=mJfWyY1SBMm\/m1HYDc5YzgO5BwvdndC5nZDyvNzWyMvsEgfwyMvmsJzXm2fqEhq5mJvXvw50qIO4xZKTEefVsxq3yMfkltLpnJvWzfDrCZHgywiXEgzZqwntsJvMn21Nss9psKDkBKHKlY1LzI1vlJPyEuu3l3nyEhjsBNuWAhrlq3rezZfxmJn4A2zwtZbowhq3B1CWlZe4ytHZnhzhu19Lt29fvJDkCfLOzgvewI43rtjWmd1YBKLysOe2uMfKztbr#CWSZmJG","Storm","Godspeed You! Black Emperor",1352,0,0,"",0,34,"","[]","81b9d46c10d9df8d03\/5299d303c627944df7\/5ec696a0453d27253e\/ce7a1e0600cff40c53\/","",[]]]<!><!bool><!>7212f741260c90ab47







これはjsonであることがわかります。



いずれかの要素のJSON



素晴らしい、今では少なくともいくつかのリンクがありますが、あまりうまく機能していないようです。次に何をすればいいですか?



リンクを解読する



スクリプトをさらに進めて、何も生じないことを理解します。ある段階で、リンクは単にaudio_api_unavailableからmp3へのリンクに変わります。 それで、どこかで何かが起こっています!

しかし、正確にはどこですか? setUrl()関数の1つで、関数は話す名前audioUnmaskSource()で呼び出されます



機能コード
  function i() { return window.wbopen && ~(window.open + "").indexOf("wbopen") } function o(t) { if (!i() && ~t.indexOf("audio_api_unavailable")) { var e = t.split("?extra=")[1].split("#") , o = "" === e[1] ? "" : a(e[1]); if (e = a(e[0]), "string" != typeof o || !e) return t; o = o ? o.split(String.fromCharCode(9)) : []; for (var s, r, n = o.length; n--; ) { if (r = o[n].split(String.fromCharCode(11)), s = r.splice(0, 1, e)[0], !l[s]) return t; e = l[s].apply(null, r) } if (e && "http" === e.substr(0, 4)) return e } return t } function a(t) { if (!t || t.length % 4 == 1) return !1; for (var e, i, o = 0, a = 0, s = ""; i = t.charAt(a++); ) i = r.indexOf(i), ~i && (e = o % 4 ? 64 * e + i : i, o++ % 4) && (s += String.fromCharCode(255 & e >> (-2 * o & 6))); return s } function s(t, e) { var i = t.length , o = []; if (i) { var a = i; for (e = Math.abs(e); a--; ) o[a] = (e += e * (a + i) / e) % i | 0 } return o } Object.defineProperty(e, "__esModule", { value: !0 }), e.audioUnmaskSource = o; var r = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN0PQRSTUVWXYZO123456789+/=" , l = { v: function(t) { return t.split("").reverse().join("") }, r: function(t, e) { t = t.split(""); for (var i, o = r + r, a = t.length; a--; ) i = o.indexOf(t[a]), ~i && (t[a] = o.substr(i - e, 1)); return t.join("") }, s: function(t, e) { var i = t.length; if (i) { var o = s(t, e) , a = 0; for (t = t.split(""); ++a < i; ) t[a] = t.splice(o[i - 1 - a], 1, t[a])[0]; t = t.join("") } return t }, x: function(t, e) { var i = []; return e = e.charCodeAt(0), each(t.split(""), function(t, o) { i.push(String.fromCharCode(o.charCodeAt(0) ^ e)) }), i.join("") } } }
      
      







o(t)にブレークポイントを設定し、何が来て何が起こるかを確認します。 tは、audio_api ...、in?extra = 2つのパラメーターの形式のリンクです。 私が理解したように、1つは暗号化されたリンクを含み、2つ目はキーのようなものです。 アルゴリズムを元に戻すか、これをすべて暗号化する方法を正確に理解するか、o( 'https:// ... audio_api _...')を呼び出すだけです。 だから私はそれをすることにし、出力でmp3への直接リンクを得ました



暗号化されたリンクを取得します



リンクを解読することを学びました。暗号化されたリンクを取得する方法を知っています。 暗号化されたリンクを返すメソッドに渡す必要があるIDを取得するにはどうすればよいですか? ネットワークアクティビティを見てみましょう。 前に理解したように、オーディオAPIとの対話はal_audio.phpを介して行われます。 このリクエストにフィルターを設定し、音声録音でページを再度リロードすると、新しいリクエストが表示されます



Request URL:https://vk.com/al_audio.php

Request Method:POST

Status Code:200

Form Data:

access_hash:

act:load_section

al:1

claim:0

offset:30

owner_id:my_id

playlist_id:-1

type:playlist








それに応じて、プレイリストに関するデータを含む、以前と同じ形式の大きなjsonを取得します。 オフセットフィールドは、プレイリストに関するデータを受信するオフセットを担当します。 さらに、回答には有用なデータが含まれています:hasMoreおよびnextOffsetフィールド。



すべてをまとめる



プレイリストデータを取得する方法、暗号化されたリンクを取得する方法、およびそれらを復号化する方法を知っています。 すべてをまとめることが残っています。 これは私に起こったことの例です。 node.js v8.1.3でパフォーマンスを確認しました。



UPD:



Veberが指摘したように、VKは何らかの形でOperaと特別な方法で動作するため、クロム/ FirelisからCookieを取得する方が良いです。



UPD 2:VKはリンクをマスクする方法を更新しました。コードはIDと競合しています。12/ 17/17のコミットで




All Articles