Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save TheKing-OfTime/bb0f918a41a6c6990db8df929e0cabe3 to your computer and use it in GitHub Desktop.
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
// СКРИПТ ДЛЯ МГНОВЕННОГО ЭКСПОРТА ПЛЕЙЛИСТОВ ИЗ НОВОЙ ВЕБ ВЕРСИИ ЯНДЕКС МУЗЫКИ
// 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