Last active
October 19, 2025 17:06
-
-
Save TheKing-OfTime/bb0f918a41a6c6990db8df929e0cabe3 to your computer and use it in GitHub Desktop.
Export any Yandex Music Web playlist tracks to a file. Console script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // СКРИПТ ДЛЯ МГНОВЕННОГО ЭКСПОРТА ПЛЕЙЛИСТОВ ИЗ НОВОЙ ВЕБ ВЕРСИИ ЯНДЕКС МУЗЫКИ | |
| // Created by: TheKingOfTime | |
| // Creation date: 18-05-2025 | |
| // Version: 1.1 | |
| // License: MIT | |
| // Как использовать: | |
| // 1. Откройте веб версию Яндекс Музыки в браузере. На главной странице. | |
| // 2. Откройте консоль разработчика (F12 или Ctrl+Shift+I). | |
| // 3. Перейдите на вкладку "Консоль". | |
| // 4. Скопируйте и вставьте этот код в консоль. | |
| // 5. Нажмите Enter. | |
| // После этого каждый раз при переходе в любой плейлист (именно плейлист. НЕ альбом), он будет автоматически экспортироваться в файл. | |
| // Чтобы отключить скрипт, просто обновите страницу. | |
| const EXPORT_IMMEDIATELY = true; // true - экспортировать сразу после загрузки страницы плейлиста автоматически | |
| // false - экспортировать только по вызову функции exportToFile(playlistID) | |
| // playlistID - ID плейлиста, который нужно экспортировать. Найти его можно вписав в консоли allSongs. | |
| // Если playlistID не указан, экспортируются все плейлисты. Если USE_JSON = true, то экспортируются все плейлисты в один файл. Иначе - в разные файлы. | |
| const USE_JSON = false; // true - экспортировать в json формате а не в txt формате. | |
| const USE_RICH_DATA = false; // true - экспортировать с полными данными о треке. Ровно так, как они приходят из API. Работает только если USE_JSON = true. | |
| // Учтите, что в этом случае файл может быть очень большим, так как в нем будут все данные о треке. | |
| // Кроме того, при использовании USE_RICH_DATA, в выходном файле может содержаться информация о вашем аккаунте например (ваш логин, имя, фамилия), поэтому не рекомендуется его выкладывать в открытый доступ. | |
| let allSongs = {}; | |
| // Патчим функцию fetch для перехвата запросов к API Яндекс Музыки | |
| const originalFetch = window.fetch; | |
| window.fetch = async function (input, init) { | |
| let url; | |
| let modified = false; | |
| if (typeof input === 'string') { | |
| if (input.startsWith('https://api.music.yandex.ru/playlist/') || input.startsWith('https://api.music.yandex.net/playlist/') || input.startsWith('https://api.music.yandex.kz/playlist/') || input.startsWith('https://api.music.yandex.by/playlist/')) { | |
| url = new URL(input); | |
| modified = true; | |
| } | |
| } else if (input instanceof Request) { | |
| if (input.url.startsWith('https://api.music.yandex.ru/playlist/') || input.url.startsWith('https://api.music.yandex.net/playlist/') || input.url.startsWith('https://api.music.yandex.kz/playlist/') || input.url.startsWith('https://api.music.yandex.by/playlist/')) { | |
| url = new URL(input.url); | |
| modified = true; | |
| } | |
| } | |
| if (modified && url.searchParams.get('richTracks') === 'false') { | |
| url.searchParams.set('richTracks', 'true'); | |
| console.log('[Fetch перехвачен] URL изменён:', url.toString()); | |
| let request; | |
| if (typeof input === 'string') { | |
| request = url.toString(); | |
| } else { | |
| request = new Request(url.toString(), { | |
| method: input.method, | |
| headers: input.headers, | |
| body: input.body, | |
| mode: input.mode, | |
| credentials: input.credentials, | |
| cache: input.cache, | |
| redirect: input.redirect, | |
| referrer: input.referrer, | |
| referrerPolicy: input.referrerPolicy, | |
| integrity: input.integrity, | |
| keepalive: input.keepalive, | |
| signal: input.signal, | |
| duplex: input.duplex | |
| }); | |
| } | |
| const response = await originalFetch.call(this, request, init); | |
| try { | |
| const clone = response.clone(); | |
| const data = await clone.json(); | |
| handleTracks(data); | |
| } catch (e) { | |
| console.warn('[Fetch ответ] Не удалось прочитать тело:', e); | |
| } | |
| return response; | |
| } | |
| // Если ничего не подменяем | |
| return await originalFetch.call(this, input, init); | |
| }; | |
| function pad(numberString, size) { | |
| let padded = numberString; | |
| while (padded.length < size) { | |
| padded = `0${padded}`; | |
| } | |
| return padded; | |
| } | |
| function msToHMS(ms) { | |
| const seconds = Math.floor((ms / 1000) % 60); | |
| const minutes = Math.floor(ms / 1000 / 60); // Добавьте `% 60` если хотите получить минуты в пределах 0-59 | |
| // const hours = Math.floor((ms / 1000 / 3600 ) % 24) | |
| return [ | |
| // pad(hours.toString(), 2), | |
| pad(minutes.toString(), 2), | |
| pad(seconds.toString(), 2), | |
| ].join(':'); | |
| } | |
| // Функция для подготовки данных в формате TXT | |
| function prepareTxt(tracks) { | |
| return tracks.map(track => `${track.title}, ${track.artists}, ${track.duration}`).join('\n'); | |
| } | |
| // Функция для подготовки данных всех плейлистов в формате JSON | |
| function prepareJsonPlaylists(playlists) { | |
| const playlistsData = {}; | |
| Object.entries(playlists).forEach(([playlistID, tracks]) => { | |
| playlistsData[playlistID] = (USE_RICH_DATA ? tracks.map(track=>track.richData) : tracks); | |
| }) | |
| return JSON.stringify(playlistsData); | |
| } | |
| // Функция для подготовки данных в формате JSON | |
| function prepareJson(tracks) { | |
| return JSON.stringify(USE_RICH_DATA ? tracks.map(track=>track.richData) : tracks); | |
| } | |
| // Функция для экспорта списка в файл | |
| function exportToFile(playlistID=undefined) { | |
| if (!Object.keys(allSongs)?.length) { | |
| console.warn('Нет плейлистов для экспорта'); | |
| return; | |
| } | |
| if (playlistID && !allSongs[playlistID]) { | |
| console.warn('Плейлист ' + playlistID + ' не найден для экспорта'); | |
| return; | |
| } | |
| if (!playlistID && !USE_JSON) { | |
| Object.keys(allSongs).forEach(key=> { | |
| exportToFile(key); | |
| }); | |
| return; | |
| } | |
| let data; | |
| if (!playlistID && USE_JSON) { | |
| data = prepareJsonPlaylists(allSongs); | |
| } else { | |
| const tracks = allSongs[playlistID]; | |
| data = USE_JSON ? prepareJson(tracks) : prepareTxt(tracks) | |
| } | |
| const blob = new Blob([data], { type: USE_JSON ? 'application/json' : 'text/plain' }); | |
| const fileName = `songs_list_${playlistID ?? 'all'}.${USE_JSON ? 'json' : 'txt'}` | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = fileName; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| console.log('Список песен экспортирован в файл ' + fileName); | |
| } | |
| // Функция для обработки полученных данных | |
| function handleTracks(fetchedData) { | |
| allSongs[fetchedData.playlistUuid] = fetchedData?.tracks?.map(playlistTrack => { | |
| return { | |
| id: playlistTrack.id, | |
| artists: playlistTrack.track.artists.map(artist=>artist.name).join(', '), | |
| duration: msToHMS(playlistTrack.track.durationMs), | |
| title: playlistTrack.track.title, | |
| richData: USE_RICH_DATA ? playlistTrack.track : undefined, | |
| } | |
| }); | |
| console.log(`Альбом ${fetchedData.title} добавлен в allSongs по ID ${fetchedData.playlistUuid}`); | |
| if (EXPORT_IMMEDIATELY) exportToFile(fetchedData.playlistUuid); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment