ГлавнаяБлогСинхронизация YouTube и Spotify: пишем скрипт на Python
Python

Синхронизация YouTube и Spotify: пишем скрипт на Python

Узнайте, как написать Python-скрипт для синхронизации плейлистов YouTube и Spotify. Разбор кода построчно, от .env до финального запуска.

Al
Редакция Algolitalgolit.ru
12 мин чтения23 июля 2026 г.

Зачем читать эту статью?

Вы уже получили ключи API для YouTube и Spotify. Теперь пришло время собрать из них рабочий скрипт, который автоматически синхронизирует плейлисты. К концу этой главы вы будете понимать каждую строку кода — зачем она нужна и как работает. Никаких пропущенных знаний: если что-то останется непонятным, пишите в комментарии, я исправлю объяснение.

Структура проекта

Проект состоит из трёх файлов и одного конфигурационного файла:

youtube-to-spotify/
├── spotify.py       # знает, как общаться со Spotify
├── youtube.py       # знает, как общаться с YouTube
├── script.py        # мозг — запускает синхронизацию
└── .env             # ваши приватные ключи (никому не показывать)

Мы разберём каждый файл по порядку.

Файл .env

Это не код, а просто текстовый файл, который хранит шесть ключей из предыдущих глав. Создайте в папке проекта файл .env и заполните его:

CLIENT_ID=your_spotify_client_id
CLIENT_SECRET=your_spotify_client_secret
REFRESH_TOKEN=your_spotify_refresh_token
SPOTIFY_PLAYLIST_ID=your_spotify_playlist_id
API_KEY=your_youtube_api_key
YOUTUBE_PLAYLIST_ID=PLGBuKfnErZlAkaUUy57-mR97f8SBgMNHh

Последняя строка уже заполнена — это плейлист 70-х, который мы используем. Остальные замените на свои значения из предыдущих глав. Этот файл остаётся только на вашем компьютере, он никогда не попадает на GitHub (мы проверим это в главе 6).

youtube.py — читаем плейлист YouTube

Этот файл отвечает за общение с YouTube. У него три задачи.

Задача 1: получить видео из плейлиста

def get_playlist_items(self, playlist_id):
    request = self.youtube.playlistItems().list(
        part='snippet',
        playlistId=playlist_id,
        maxResults=50
    )
    videos = []
    while request:
        response = request.execute()
        for item in response['items']:
            title = item['snippet']['title']
            video_id = item['snippet']['resourceId']['videoId']
            videos.append((title, f"https://www.youtube.com/watch?v={video_id}"))
        request = self.youtube.playlistItems().list_next(request, response)
    return videos

API YouTube отдаёт только 50 видео за раз. В нашем плейлисте 150 песен, поэтому цикл while request продолжает запрашивать следующие 50, пока не получит все. В итоге videos — это список из 150 пар (название, ссылка). Пример:

("Stayin' Alive - Bee Gees (Official Music Video)", "https://www.youtube.com/watch?v=I_izvAbhExY")

Задача 2: определить песню и исполнителя из названия

Названия видео на YouTube — настоящий хаос. Каждый автор оформляет их по-своему:

Stayin' Alive - Bee Gees (Official Music Video)
Bee Gees - Stayin' Alive | Lyrics
ABBA - Dancing Queen (Official)
Don't Stop Me Now [Queen] HD

Единого правила нет. Поэтому мы пытаемся их осмыслить:

def extract_song_and_artist(self, title):
    if " - " in title:
        parts = title.split(" - ")
        if len(parts) == 2:
            artist_first = parts[0].strip()
            song_first = parts[1].strip()
            song_second = parts[0].strip()
            artist_second = parts[1].strip()
            return (song_first, artist_first), (song_second, artist_second)
    return (title, ""), (title, "")

Когда в названии есть дефис, мы разделяем строку и возвращаем оба возможных варианта: «Исполнитель — Песня» и «Песня — Исполнитель». Позже основной скрипт пробует оба на Spotify и выбирает тот, который дал совпадение. Если дефиса нет, считаем всё название названием песни и ищем без исполнителя — часто такой широкий поиск всё равно срабатывает.

Задача 3: очистить название от мусора

Перед поиском на Spotify мы удаляем весь мусор YouTube — «(Official Music Video)», «[HD]», «ft.», «#lyrics» и ещё около 40 вариаций, которые сбивают поиск:

def clean_title(self, title):
    title = re.sub(r'\(.*?\)|\[.*?\]|\{.*?\}|#\S+', '', title)
    # ... удаляет "Official Video", "Lyrics", "HD" и т.д.
    title = re.sub(r'\s+', ' ', title).strip()
    return title

Так «Stayin' Alive — Bee Gees (Official Music Video)» превращается в «Stayin Alive — Bee Gees» перед отправкой на Spotify. Гораздо чище для поиска.

spotify.py — общаемся со Spotify

Этот файл управляет всем, что связано со Spotify. Четыре функции наиболее важны для синхронизации.

Оставаться в системе: refresh_access_token

def refresh_access_token(self):
    # Использует CLIENT_ID, CLIENT_SECRET и REFRESH_TOKEN
    # чтобы получить свежий access token при каждом запуске скрипта

Токены доступа Spotify истекают через час. Refresh Token, полученный в главе 2, позволяет автоматически генерировать новый токен при каждом запуске скрипта. Благодаря этому скрипт может работать по расписанию без вашего участия — вам больше никогда не нужно входить в систему.

Поиск песни

def search_song(self, query, limit=10):
    endpoint = f'search?q={query}&type=track&limit=10'
    # ... запрашивает у Spotify 10 лучших результатов
    # ... затем выполняет три прохода на совпадение

Эта функция не просто берёт первый результат и надеется на лучшее. Она делает три прохода:

  • Проход 1 — является ли полный поисковый запрос подстрокой названия трека или исполнителя? Если да, это почти наверняка нужная песня.
  • Проход 2 — встречаются ли два или более имени исполнителя из результата в нашем запросе? Запрос «Stayin Alive Bee Gees» совпадает с «Stayin' Alive by Bee Gees», потому что «Bee Gees» есть в обоих.
  • Проход 3 — встречаются ли хотя бы два слова из нашего запроса в названии трека? Крайний случай, но ловит песни, где имя исполнителя вообще не было в названии YouTube.

Если все три прохода не дали результата, функция возвращает None, и скрипт логирует песню как ненайденную.

Добавление и удаление треков

def add_tracks_to_playlist(self, playlist_id, track_uris):
    # Добавляет треки порциями по 100 (лимит API Spotify)

def remove_tracks_from_playlist(self, playlist_id, track_uris):
    # Удаляет треки порциями по 100

API Spotify принимает не более 100 треков за один запрос, поэтому обе функции автоматически разбивают большие списки на порции. Вам не нужно думать об этом — всё работает само.

Переупорядочивание в соответствии с YouTube

def reorder_playlist_tracks(self, playlist_id, track_uris):
    # Перемещает треки один за другим, пока порядок не совпадёт с YouTube

def reorder_playlist_many_tracks(self, playlist_id, track_uris):
    # Более эффективная версия для плейлистов длиннее 100 треков

Поскольку в нашем плейлисте 150 песен, скрипт автоматически использует reorder_playlist_many_tracks. После каждой синхронизации порядок в вашем плейлисте Spotify будет точно соответствовать порядку в плейлисте YouTube.

script.py — мозг

Это файл, который вы запускаете. Он загружает ключи, подключается к обоим API и вызывает функцию синхронизации.

def main():
    load_dotenv()  # читает ваш .env файл
    client_id = os.getenv('CLIENT_ID')
    client_secret = os.getenv('CLIENT_SECRET')
    # ... загружает все шесть значений
    spotify = SpotifyAPI(client_id, client_secret, refresh_token)
    youtube = YouTubeAPI(api_key)
    sync_playlist(spotify_playlist_id, youtube_playlist_id, spotify, youtube)

load_dotenv() читает файл .env и делает его значения доступными как переменные окружения. os.getenv('CLIENT_ID') забирает их. Затем скрипт проверяет, что ни одно из шести значений не пропущено — если вы забыли заполнить что-то, он скажет, какое именно, вместо того чтобы упасть с непонятной ошибкой.

Функция синхронизации по шагам

def sync_playlist(spotify_playlist_id, youtube_playlist_id, spotify, youtube, remove=True):

Шаг 1 — получить видео с YouTube

youtube_videos = youtube.get_playlist_items(youtube_playlist_id)

Получает все 150 видео из нашего плейлиста 70-х вместе с названиями.

Шаг 2 — найти каждое на Spotify

for i, (title, url) in enumerate(youtube_videos, start=1):
    (song_a, artist_a), (song_b, artist_b) = youtube.extract_song_and_artist(title)
    query_a = f"{youtube.clean_title(song_a)} {youtube.clean_title(artist_a)}".strip()
    query_b = f"{youtube.clean_title(song_b)} {youtube.clean_title(artist_b)}".strip()
    result = spotify.search_song(query_a) or spotify.search_song(query_b)

Для каждого видео мы строим два запроса (оба возможных порядка «Исполнитель/Песня»), очищаем их и пробуем на Spotify. or означает: сначала пробуем query_a, и только если он ничего не дал, пробуем query_b.

Шаг 3 — попробовать альтернативы из скобок

for alt in (youtube.extract_bracket_alternatives(song_a) + ...):
    result = spotify.search_song(alt_query)

Некоторые видео прячут альтернативное название песни в скобках: «Stayin' Alive (From Saturday Night Fever Soundtrack)». Если основные запросы не сработали, мы пробуем искать по тексту внутри скобок как последний шанс.

Шаг 4 — добавить, удалить, переупорядочить

to_add = [uri for uri in found_tracks if uri not in spotify_uris]
spotify.add_tracks_to_playlist(spotify_playlist_id, to_add)
to_remove = [uri for uri in spotify_uris if uri not in found_tracks]
spotify.remove_tracks_from_playlist(spotify_playlist_id, to_remove)
spotify.reorder_playlist_many_tracks(spotify_playlist_id, found_tracks)

Три сравнения списков:

  • Треки, которые есть на YouTube, но нет на Spotify → добавляем
  • Треки, которые есть на Spotify, но нет на YouTube → удаляем
  • Переупорядочиваем оставшиеся, чтобы соответствовать порядку YouTube

Шаг 5 — вывести итог

print(f"YouTube videos: {len(youtube_videos)}")
print(f"Found on Spotify: {len(found_tracks)}")
print(f"Not found: {len(not_found)}")
print(f"Added: {len(to_add)}")
print(f"Removed: {len(to_remove)}")

Когда скрипт завершается, вы получаете чистую сводку всего, что было сделано. При первом запуске число «Added» будет большим. При каждом последующем — только изменения с момента последней синхронизации.

Почему три файла вместо одного?

Вы можете спросить, почему бы не поместить всё в один большой script.py. Краткий ответ: разделение ответственности — каждый файл знает о чём-то одном. youtube.py знает YouTube. spotify.py знает Spotify. script.py знает логику. Если Spotify завтра изменит свой API, вы правите только spotify.py. Если захотите заменить YouTube на другой источник музыки, меняете только youtube.py. Логика в script.py не должна знать и не знает об этих деталях. Так устроено настоящее программное обеспечение. И вы только что это сделали.

Практический вывод

Сейчас у вас есть полное понимание кода. В следующей главе мы установим Python, настроим проект на вашем компьютере и впервые запустим скрипт. Вы увидите, как он вытащит все 150 песен из плейлиста 70-х и на лету соберёт ваш плейлист Spotify. А пока — сохраните этот код, он вам понадобится.

#синхронизация плейлистов#YouTube API#Spotify API#Python скрипт
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →