Привет, Хабр! Когда‑то давно я наткнулся на ролик, где парень писал консольный клиент для YouTube. Идея была просто потрясающей! Написать свой минималистичный и быстрый клиент, который не будет надоедать лентами рекомендаций, рекламой и т. д.

Но вот беда, автор, не найдя возможности использовать YouTube API, решил спарсить одну из фронтенд обёрток над YouTube.

Стоит ли говорить о том, что такое решение сильно теряет в производительности и абсолютно нежизнеспособно на какой‑либо длительный период времени?

И вот относительно недавно я решил написать свой минималистичный ютуб клиент на базе Android приложения.

Часто бывает, что во время прогулок по городу хочется послушать ютуб в фоновом или в PiP режиме, не отвлекаясь от общения с друзьями в мессенджере.
Теперь это возможно!

YouTube в фоне доступен в магазине приложений RuStore

В этой статье я хочу поделиться как бесплатно работать с YouTube API.

Как оказалось, для этого существует невероятно простая Python библиотека InnerTube:

Библиотека обрабатывает низкоуровневое взаимодействие с базовым API InnerTube, используемым каждой службой YouTube.

Что-то типа брутфорса*

Библиотека используется в основе известной утилиты yt-dlp

Приложение ViMusic также использует эту библиотеку и уже более двух лет работает без обновлений. Так что вcё надежно, как швейцарские часы!

Простой пример использования InnerTube. При выводе мы получим данные в формате JSON, которые в дальнейшем можно обработать по вашему усмотрению.

import innertube

client = innertube.InnerTube("WEB")

# Поиск списка видео по запросу
client.search(query="уроки программирования на python")

# Получить данные для воспроизведения видео Rick Astley - Never Gonna Give You Up
client.player(video_id="dQw4w9WgXcQ")

Тащить целиком эту библиотеку в наше Android приложение совсем необязательно.

Достаточно будет понять, какие параметры и тело запроса нам нужно передавать.
Если порыться в коде проекта InnerTube, то можно найти файл config.py с параметрами для создания запроса к WEB клиенту.

К слову, существует множество вариантов клиентов, таких как TVLITE, ANDROID, IOS и др.
У всех них будут разные версии, и где-то могут отличаться возвращаемые данные.

REFERER_YOUTUBE: str = "https://www.youtube.com/"
USER_AGENT_WEB: str = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
)

config: Config = Config(
    base_url="https://youtubei.googleapis.com/youtubei/v1/",
    clients=[
        ClientContext(
            client_id=1,
            client_name="WEB",
            client_version="2.20230728.00.00",
            user_agent=USER_AGENT_WEB,
            referer=REFERER_YOUTUBE,
            api_key="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
        )
    ],
)

Ну а теперь переходим к запросам из Android приложения. Стандартом для работы с сетью, естественно, является Retrofit.

Самым сложным для меня оказалось конвертировать JSON объект с 40 тысячами строк ответа в дата классы.

interface InnerTubeService {
    @POST("v1/player")
    suspend fun getPlayerInfo(@Body body: PlayerBody): Response<PlayerResponse>

    @POST("v1/search?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
    suspend fun searchVideos(@Body body: SearchBody): Response<SearchResponse>
}

// Body для поиска видео по названию
val searchBody = SearchBody(
                contentCheckOk = true, context = Context(
                    client = Client(
                        androidSdkVersion = 31,
                        clientName = "WEB",
                        clientScreen = "WATCH",
                        clientVersion = "2.20230728.00.00",
                        gl = "US",
                        hl = "en"
                    ), thirdParty = ThirdParty(embedUrl = "https://www.youtube.com/")
                ), playbackContext = PlaybackContext(
                    contentPlaybackContext = ContentPlaybackContext(
                        signatureTimestamp = 19250
                    )
                ), racyCheckOk = true, query = query
            )

//Body для получения данных проигрывателя
val playerBody = PlayerBody(
                contentCheckOk = true, context = Context(
                    client = Client(
                        androidSdkVersion = 31,
                        clientName = "TVLITE",
                        clientScreen = "WATCH",
                        clientVersion = "2",
                        gl = "US",
                        hl = "en"
                    ), thirdParty = ThirdParty(embedUrl = "https://www.youtube.com/")
                ), playbackContext = PlaybackContext(
                    contentPlaybackContext = ContentPlaybackContext(
                        signatureTimestamp = 19250
                    )
                ), racyCheckOk = true, videoId = videoId
            )

Вот так выглядит ответ с потоковой ссылкой для просмотра.

data class PlayerResponse(val streamingData : StreamingData,
                          val videoDetails : VideoDetails)

data class StreamingData(val formats : List<Formats>)

data class Formats(val itag : Long, val url : String)

Итог

Всё работает достаточно шустро и напрямую с YouTube API.

Я уже реализовал историю просмотра видео. В дальнейшем появятся плейлисты, будет переработан мини-плеер и др.

Спасибо за внимание! Надеюсь, что эта статья будет полезна и для ваших проектов.