Как написать свой клиент для YouTube, не привлекая внимания санитаров
Привет, Хабр! Когда‑то давно я наткнулся на ролик, где парень писал консольный клиент для 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
@POST("v1/search?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
suspend fun searchVideos(@Body body: SearchBody): Response
}
// 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)
data class Formats(val itag : Long, val url : String)
Итог
Всё работает достаточно шустро и напрямую с YouTube API.
Я уже реализовал историю просмотра видео. В дальнейшем появятся плейлисты, будет переработан мини-плеер и др.
Спасибо за внимание! Надеюсь, что эта статья будет полезна и для ваших проектов.