Привет, Хабр! Когда‑то давно я наткнулся на ролик, где парень писал консольный клиент для YouTube. Идея была просто потрясающей! Написать свой минималистичный и быстрый клиент, который не будет надоедать лентами рекомендаций, рекламой и т. д.
Но вот беда, автор, не найдя возможности использовать YouTube API, решил спарсить одну из фронтенд обёрток над YouTube.
Стоит ли говорить о том, что такое решение сильно теряет в производительности и абсолютно нежизнеспособно на какой‑либо длительный период времени?
И вот относительно недавно я решил написать свой минималистичный ютуб клиент на базе Android приложения.
Часто бывает, что во время прогулок по городу хочется послушать ютуб в фоновом или в PiP режиме, не отвлекаясь от общения с друзьями в мессенджере.
Теперь это возможно!

В этой статье я хочу поделиться как бесплатно работать с 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.
Я уже реализовал историю просмотра видео. В дальнейшем появятся плейлисты, будет переработан мини-плеер и др.
Спасибо за внимание! Надеюсь, что эта статья будет полезна и для ваших проектов.
