Хабрахабр

[Из песочницы] Как я библиотеку для сервиса «Яндекс.Музыка» писал

Обо мне

С детства увлекаюсь компьютерами, с класса 7-го начал познавать само программирование. Всем привет, я обычный учащийся по специальности "техник-программист". Являюсь владельцем подписки на Яндексу Музыку уже больше года и в целом доволен сервисом (правда сейчас в плейлисте дня сплошные повторы).

Предыстория

Там ребятки задают точно такой же вопрос, как и я: "А где API?". Уж не помню точно, из-за чего я решил поискать официальную документацию API данного сервиса, вроде бота хотел для Telegram написать, но столкнулся с тем, что её нет… Спустя некоторое время наткнулся на issue в репозитории yandex/audio-js. Интегрировать к своему любимому плееру невозможно! Не многие горят желанием слушать музыку через браузер, они хотят приложение, но приложения под Linux тоже нет!

Естественно, мне нужно как-то работать с сервисом, городить костыли вокруг веб-приложения не вариант. Тут я загорелся идеей сделать это. Я оказался прав! Я понимал, что имея такой сервис, имея мобильные приложения и приложения под Windows (из Microsoft Store) просто невозможно не иметь своё внутреннее API для взаимодействия.

Обязательно к прочтению перед основной частью

Ниже будут описаны различные спорные моменты, решения разработчиков и в целом то, как это написали, как они этим пользуются. Я отдаю себе отчёт в том, что, изучая их непубличное API я роюсь в чужих грязных вещах. Не будем забывать, что это никто не должен был видеть. Местами я был просто шокирован, но я уверен, что если они так сделали, то на это были свои причины! Вы можете с ним согласить или нет. Так же хочу сказать, что всё написанное ниже моё мнение.

Подготовка

API веб-приложения

Это было вовсе не сложно. Выше я уже написал, что нашёл API. 1/. Первым делом я глянул на их веб-приложение, их эндпоинт на момент написания статьи находится здесь: https://music.yandex.ru/api/v2. Так же прошу обратить внимание на указание версии API, оно есть. У них достаточно длинные урлы получаются в которых участвую данные, а еще и форму отправляют.

Тут нет OAuth. Надо понимать, что то, что я нашел, используется ими только в веб-приложении. В общем для библиотеки не годится в связи с костылями в авторизации. Точнее оно скорее и есть, но там, в недрах нашей сессии на сайте.

API приложений

Телефон брать было лень, следовательно, до мобильных приложений я дошёл бы в последнюю очередь. Отправился я искать дальше. Вследствие чего я приступил к изучению того, как оно работает. На то время компьютер работал под ОС Windows 10, и я активно пользовался официальным приложением Яндекс Музыки из Microsoft Store.

Можно было использовать Wireshark, но я остановился на HTTP Analyzer. Для изучения мне понадобился сниффер, чтобы отслеживать весь трафик приложения. Он мне кажется более легковесным и отлично подходящим под мою задачу.

Запросы текут ручьем. Включаем сниффер, заходим в приложение и готово. Сидим, разбираемся, пытаемся вызвать каждый обработчик что есть в этом приложении и узнать о всех существующих методах, их аргументах и конечно же JSON ответах.

Скриншот одного из запросов

Более того, обратите внимание на заголовки. Из скриншота выше сразу можно заметить совершенно другой адрес API — api.music.yandex.net. Помимо информации о моём клиенте с которого был выполнен запрос там есть OAuth токен — то, что надо!

Изучение API

Я писал классы-обёртки для объектов сервиса получаемого от API, реализовывал отправку запросов, разбирался с параметрами и местами просто догадывался что это название может означать. Изучение проходило совместно с написанием кода. На этом этапе я и повстречал различные вещи, которые не ожидал тут увидеть.

Остальные же являются классами Яндекс Музыки, что говорит о масштабности данного сервиса и уровню абстракций. На момент написания статьи библиотека содержит 83 класса и лишь некоторые из них вспомогательные.

И это далеко не все, что есть в API (об этом ниже). Была реализована отправка ~47 методов.

Боль

Но потом, в один прекрасный момент, всё, бомбанул. На первых порах я старался не обращать внимание, просто удивлялся, ведь это Яндекс, как такое может быть. Начну, пожалуй, с него.

  1. Два объекта с разным уровнем вложения поля

    Новая версия объекта

    На свою полную версию. Сам объект является что ль "ссылкой" на самого себя. Хорошая практика, так делают многие, но она не везде соблюдается (пункт 9). При запросе списка треков нам отдают их ID, по которым мы можем получить более подробную информацию.

    Старая версия объекта

    Мне кажется, комментарии излишни и всё видно на скриншотах. Реализовав в самом начале класс для данного объекта я думал, что буду использовать его везде, но как бы не так!

    Я никак не исправлял подобного рода косяки в своей библиотеке, поэтому имея класс TrackShort теперь есть TrackShortOld.

    Кстати, оба этих объекта живут в одном методе, в методе получения landing'a.

  2. Версии API, методов

    Вообще, как мы обычно указываем версию? Я не просто так попросил Вас обратить внимание на то, как указывается версия в API для веб-приложения. Наверное, одним из следующих способов:

    • вынести версию на отдельный поддомен;
    • вынести версию в часть запроса;
    • передавать желаемую версию API параметром к запросу.

    У нас есть метод landing3 — актуальная его версия на момент написания статьи. В Яндекс решили в данном случае сделать иначе. Но никто не запрещает отправить запрос на landing2 — совершенно другая структура, другие объекты.

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

  3. Работая с новым, не отказываемся от старого

    Их на самом деле не много (плейлист, артист, трек, альбом). Увидел я это когда писал отправку методов "Мне нравится" для всех объектов что есть. Какого было моё удивление, когда я увидел разные подходы к одному и тому же действию.

    Артистов мы лайкаем так: https://api.music.yandex.net/users/<USER_ID>/likes/artists/add и в форме передаем artist-id.

    Треки мы лайкаем так: https://api.music.yandex.net/users/<USER_ID>/likes/tracks/add-multiple и в форме track-ids.

    Ни с какими другими типами этот метод не используется, но они все существуют (стоило просто попробовать отправить запрос)! Если Вы не заметили, то при лайке трека используется метод add-multiple, а не add. Ведь данный метод универсален. И именно их я реализовал в своей библиотеке вместо add. Можно добавить как один трек, так и несколько.

  4. Что такое уникальный идентификатор трека

    Иногда трек в нескольких альбомах, иногда альбома нет. Прошло уже много времени, но я до сих пор не пойму, когда надо отправлять просто id трека, а когда конкатенацию id и album_id через двоеточие (id:album_id). Слишком непонятные кейсы глядя со стороны, я не знаю как они с этим справляются (или не справляются, багусик 2).

  5. Необязательность многих полей

    Если есть проблема, то она связана с обязательностью поля. Мне накидали пару issues. Я не перестаю удивляться, как, на мой взгляд, обязательные поля просто не возвращаются API.

    • album_id класса TrackID и TrackShort;
    • order_id класса AutoRenewable (подписка);
    • next_revision в Feed;
    • cover_uri в Track;
    • birthday в Account;
    • tags в Playlist.

    Возможно, данный пункт высосан из пальца. Список можно продолжать дальше, но всё есть в истории коммитов.

  6. Схожесть методов за исключением некоторых полей в ответе

    Ответ статуса аккаунта (api.music.yandex.net/account/status):

    Ответ статуса аккаунта

    Ответ радио статуса аккаунта (https://api.music.yandex.net/rotor/account/status):

    Ответ радио статуса аккаунта

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

    Не знаю как в Яндексе, но я у себя слил в один класс.

  7. Дык один или много?

    Я всегда считал, что если метод возвращает список, то даже если результатом является один элемент, то вернётся список содержащий этот элемент и никак иначе, а тут и то, и другое.

    feature и features

    То feature вернется, то features, то feature и features.

  8. Неправильное использование методов

    Они пошли дальше. Выше я написал о том, что используют то один, то другой метод для осуществления одного действия.

    Отправка удаляемых треков, когда бек прекрасно знает о них

    Вполне возможно, что это я не понял, как и всё остальное, но метод работает и без лишней информации. На метод удаления треков из плейлиста, помимо самого ID плейлиста и рамок с и по какой трек удалить, они зачем-то передают треки, которые будут удалены. А какие треки были удалены лучше узнать на беке, нежели передавать параметром.

  9. Очень тяжёлые запросы

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

    Гляньте как они беспощадно отдают подробную информацию всех моих треков из плейлиста "Мне нравится" в одном запросе:

    Тяжёлый запрос

    Bytes Received: 3,75M, а это ещё обложки загрузить! Оно отдало все 396 треков!

Багусики

  1. Загрузка всех треков в кеш из "Мне нравится"

    Спасибо за визуализацию стека, но я думал мне просто загрузит 100 последних треков из плейлиста. При достижении лимита происходило добавление в конец и удалялось с начала. Произошло это в мобильном клиенте под Android (смотреть видео).

  2. Видать не один я путаюсь, когда надо отправить id, а когда id:album_id

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

Дальше бан на 24 часа. Количество попыток на активацию подарочного кода — 10.

В зависимости от приложения, с которого Вы сидите, Вам делают разные предложения о покупке подписки.

Лимит на количество треков в кеше иллюзия, просто число, а уже приложение не дает загрузить больше (багусик 2).

Все эти умные плейлисты, предложения, текста и цвета кнопок приходят от API — вот он, настоящий RESTFull.

Время начала рекламы и сама реклама возвращается даже если у Вас есть подписка.

Ссылка на XML, содержащий данные о расположении файла для загрузки живёт 1 минуту, потом 410 ошибка.

Ведь сталкивался со всем этим я на протяжении нескольких месяцев. Написал лишь то, что вспомнил. Постарался восстановить ключевые моменты. Все мои записи — это сообщения в телеграмме, ибо когда я натыкался на что-то этакое, я делился с друзьями.

Возможно, это и не косяки вовсе, но всё что я написал выше кажется лично для меня странным. Я ни в коем случае не хотел сказать как всё плохо, как-то выставить специально косяки на публику.

Поделился с Вами тем, как писал библиотеку для закрытого API сервиса "Яндекс.Музыка" и с какими вещами столкнулся во время разработки.

Теперь Вы знаете, как и на чём работает их приложение для Windows, а следовательно, и моя библиотека.

Туго идёт, да и надо себе ещё успеть компанию для прохождения производственной технологической практики найти. Кстати, сейчас я стараюсь это всё задокументировать, документируя свою библиотеку я автоматически документирую их API.

Спасибо что дочитали аж до сюда!

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть