Введение


Обо мне


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


Предыстория


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


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


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


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


Основная часть


Подготовка


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


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


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


API приложений


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


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


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


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


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


Изучение API


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


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


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


Боль


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


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


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


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


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


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


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


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


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


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



Багусики


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


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


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


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



Заметки


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


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


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


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


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


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


Заключение


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


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


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


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


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


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

Комментарии (41)


  1. pankraty
    05.08.2019 20:13
    +3

    Касаемо удаления треков из плейлиста. Представим, что реализовано через указание ID плейлиста и номеров С и По. А дальше смоделируем следующие ситуации:


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

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


    1. batyrmastyr
      06.08.2019 09:34

      Так автор и удивляется, что от диапазона не полностью отказались.


      1. pankraty
        06.08.2019 09:45

        На метод удаления треков из плейлиста, помимо самого ID плейлиста и рамок с и по какой трек удалить, они зачем-то передают треки, которые будут удалены.


        По-моему, тут явно удивление тому, зачем передавать треки, а не зачем передавать «с» и «по».


        1. batyrmastyr
          06.08.2019 10:12

          Да, вы правы.


    1. MarshalX Автор
      06.08.2019 16:07

      Большое спасибо за Ваш комментарий! Теперь для меня в этом их решении появился смысл.


  1. and7ey
    05.08.2019 21:58

    Мне вот не хватает в Яндекс Музыке отсутствия поддержки Google Assistant (нет, колонку с Алисой я покупать не буду :). Свой клиент что ли писать?

    Яндекс тут свои запросы к серверу никак не «шифрует»? Ключей-секретов никаких не использует?


    1. prs123
      06.08.2019 11:30

      Так с google assistant крайне непросто вот так связаться. да и зачем? там наверняка могло бы быть две команды: пауза и играть


      1. and7ey
        06.08.2019 11:43

        В чем сложность? https://developer.android.com/guide/topics/media-apps/interacting-with-assistant.html


        Команд может быть гораздо больше — например, "включи плейлист такой-то".
        Мне с колонкой Google Home было бы очень удобно — стартовать всё голосом, чтоб даже не брать телефон в руки.


    1. MarshalX Автор
      06.08.2019 16:11

      Яндекс тут свои запросы к серверу никак не «шифрует»? Ключей-секретов никаких не использует?

      Не совсем понял Ваш вопрос. Обычное веб API с аутентификацией по токену. Из всего шифрования что тут есть — это HTTPS. Токен получается на oauth.yandex.ru


  1. Diversus
    05.08.2019 23:35
    +1

    Вы не хотите стать сотрудником Яндекса? Надо же кому-то написать документацию к их API Яндекс Музыка :)


    1. TIMOHIUS
      06.08.2019 11:44

      Скорее всего api у них задукоментировано, просто документация не открытая.


    1. MarshalX Автор
      06.08.2019 16:12

      Вы не хотите стать сотрудником Яндекса?

      Пройти свою технологическую практику осенью у них — моя мечта.


      1. Diversus
        06.08.2019 16:18

        Хех. Задал вопрос с юмором, а попал оказывается в точку :)

        Пройти свою технологическую практику осенью у них — моя мечта.

        Так в чем же дело? Пишите письма, выходите на HR/руководителей проекта.
        Дайте ссылку на эту публикацию и говорите, что хотите сделать API Яндекс Музыки лучше. Пробуйте и все получится.


      1. arkamax
        06.08.2019 17:57
        +1

        Попробуйте задокументировать все это на английском (при необходимости — выучить), и податься на internship к примеру в Google… или еще куда. Перспективы поинтереснее будут. P.S. Это реально и не гербалайф. «Вы не забиваете 100% тех шайб, которые боитесь бросить» (У. Гретцки)


  1. Gorniv
    06.08.2019 08:25

    А я еще на Apple Music API жаловался :D
    У них, кстати, все достаточно хорошо и четко, кроме пары багов, в паре мест возращается не совсем тот объект.


  1. warner
    06.08.2019 10:22

    Вот кстати как раз на днях решил загрузить плейлист «Мне нравится» на телефон, так как собирался в места с очень плохим интернетом. Приложение радостно отрапортовало в статус-баре, что скачано почти 400 треков. И каково же было моё удивление, когда я потом включил оффлайн режим и увидел, что на самом деле у меня всего 88 песен. Приехал домой, попробовал снова скачать — такая-же беда. Максимально удалось 99 треков загрузить.


    1. warner
      06.08.2019 13:56

      UPD: в техподдержке сказали, что ограничение на 99 треков это потому что у меня подписка на яндекс музыку за 99 рублей, в более дорогой такого нет.


      1. myrrc
        06.08.2019 15:50

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


        1. TheKnight
          06.08.2019 16:06

          1700 рублей за год. Правда у меня в сохраненых не так уж много треков, всего лишь 189.


        1. nightvich
          06.08.2019 20:01

          У google бесплатно, они считают, если вы загружаете к ним треки, то вы их как-то купили, соответственно, можно пользоваться бесплатно в рамках play.music


      1. MarshalX Автор
        06.08.2019 16:15

        Честно говоря немного удивлён. Всегда считал, что у всех лимит в 100 треков. Да и год назад где-то даже читал об этом перед покупкой подписки. А они пишут где-нибудь об этом вообще?


        1. ganzmavag
          06.08.2019 18:26

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


  1. Magn
    06.08.2019 10:31

    но приложения под Linux тоже нет

    Существует MellowPlayer для страждущих. Пользуюсь, работает.


    1. MarshalX Автор
      06.08.2019 16:16

      Разве это многим лучше, чем слушать через браузер?


    1. penton7
      06.08.2019 17:28

      Попробовал я это приложение, тестировал на ю.музик, не особо удобное, ничем не отличается если бы я держал открытую вкладку в браузере. Плюс если в браузере я могу включить adblock (реклама без подписки почти каждый трек), то в этом такого функционала нету. Возможно с другими приложениями оно предоставляет расширенный функционал, то с youtube music нету вообще смысла его использовать.


      1. Propp
        06.08.2019 20:47

        С Youtube Music в Mellow Player все плохо. Для меня киллер-фича Mellow Player — это управление проигрыванием с помощью медиа-клавиш и интеграция с виджетом плеера системы. Для я.музыки и, к примеру, youtube все работает, для youtube music — нет.


        1. penton7
          06.08.2019 23:25

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


          1. Skerrigan
            07.08.2019 05:53

            Такой не пробовали?
            Вроде бы есть поддержка всех ОС, есть хоткеи (но косые — не различает между собой «правый/левый shift-ctrl-alt»). Темная шкурка в наличии.


          1. Propp
            07.08.2019 07:34

            Еще мне как-то рекомендовали такое расширение для управления плеером в браузере медиа-клавишами: github.com/berrberr/streamkeys (если я правильно понял, что вам нужно)


  1. TIMOHIUS
    06.08.2019 11:38

    В яндекс музыке очень интересно устроен механиз построения ссылки до mp3 трека, пару месяцев назад разбирал это. В итоге на питоне есть несколько строк кода, которые могут стянуть трек по id трека =)


    1. Win332
      06.08.2019 15:06

      Да, вот реализовал когда-то на C#
      github.com/Winster332/Yandex.Music.Api/blob/master/Yandex.Music.Api/YandexMusicApi.cs#L310
      310 строка начало метода. Замысловато, но не эффективно. У спотифай и соундклауда не много сложнее


      1. MarshalX Автор
        06.08.2019 16:40

        А у меня вот так: yandex_music/download_info.py#L57


  1. Daniyar94
    06.08.2019 14:11

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

    А вот это не круто, я им плачу не для того что бы мне скрытый трафик накручивало на мобильном интернете


  1. Win332
    06.08.2019 15:01

    Хм, тоже когда-то писал либу для яндекс.музыки. Точнее это API клиент с возможностью скачивания, стриминга, поиска по альбомам, артистам, свои треки и т.д…
    Может кому пригодится: github.com/Winster332/Yandex.Music.Api
    Ну и воспроизвести музыку тоже можно через терминал, напрямую интегрируясь с яндекс.музыкой: github.com/Winster332/Yandex.Music.Terminal
    И телеграм бот для яндекс музыки на этой либе: github.com/Winster332/Lofi
    Правда телеграм бот кривой, может в какой-то момент упасть(не стал заморачиваться с API телеги).
    Кстати еще если покопаться в разных респонсах яндекс музыки, можно заметить что они используют MassTransit в своей инфраструктуре


    1. MarshalX Автор
      06.08.2019 16:21

      Ого, не нашел. Перед тем как приступить к работе я шерстил google и github на наличие того, что я хочу сделать. Встретил только либу 15 года на JS. Тогда и я оставлю ссылочку на своё творение.

      Исходники — github
      Документация — readthedocs.io


  1. Pavenci
    06.08.2019 16:17

    Приложения под Macos тоже нет официального, а очень хотелось бы.


  1. dmitry_dvm
    06.08.2019 23:55

    За "7. Дык один или много?" я бы увольнял по статье. Думаю это следствие нетипизированных языков на бэке и халатности.



  1. B_bird
    07.08.2019 10:32

    Полезность вашей библиотеки напрямую зависит от возможности и желания ее править с каждым релизом api.
    Я проходил это всё давным давно когда делал своё расширение для хрома, каждый релиз в те времена ломал всё, потому что даже базовые сущности периодически нещадно рефакторились. Спасибо разработчикам, сделали api для расширений на самой витрине (консоль разработчика, далее externalAPI.help()), с тех пор проблем не было, все работает как часы.


  1. FUNNYDMAN
    08.08.2019 19:16

    Привет. Нашли место для прохождения практики?