Вводные слова


Как бы ни ругали Apple за закрытость платформы и самой экосистемы, некоторые их решения являются исключением. На рынке много стриминговых сервисов, но предоставляющих полноценный SDK для стриминга в сторонних продуктах крайне мало, для российского рынка список официально доступных SDK ограничивается Deezer и Apple Music. Конечно, когда на наш рынок придёт Spotify, на один доступный SDK станет больше, но пока есть два игрока и только один из них имеет широкую пользовательскую базу.


Так получилось, что я имел опыт работы с Deezer SDK под Android и прямо сейчас активно работаю с SDK Apple Music (MusicKit) под iOS. И главное отличие от опыта с Deezer состоит в том, что MusicKit — это верхушка айсберга, она же публично доступный API. В отличие от Deezer, где повторить большую часть функциональности официального приложения — это просто длительный процесс, повторить функциональность даже веб-страницы Apple Music, используя только публичный API, невозможно. Даже если Apple использует MusicKit в своих решениях, то кроме него она использует ещё массу недокументированных API запросов и приватных API, которые простым смертным использовать запрещено.


В статье я расскажу про работу с MusicKit с точки зрения разработчика в контексте реализации достаточно "простых задач": поискать в каталоге, показать картинки в результатах поиска, получить песни, рекомендации и даже проиграть какую-то музыку. Забегая вперёд скажу, что многое из рассказанного будет справедливо и для работы с Apple Music в Android и Javascript.


Если понадобится, готов дать детальные ответы с кодом на вопросы в комментариях.


Отделим мух от котлет


При работе с MusicKit под iOS придётся взаимодействовать со следующими сущностями:


  • StoreKit для авторизации в Apple Music и предложения подписки на сервис (реферальной, если получится подписаться на Affiliate программу)
  • HTTP API для поиска и получения различной информации из каталога
  • iTunes Search API для поиска в каталоге iTunes (очень полезная альтернатива HTTP API). Apple обозначает лимит на максимум 20 запросов в минуту, этого чаще всего достаточно при осуществлении запросов с устройств пользователей
  • Media Player для проигрывания и управления очередью

Так же при подключении к Affiliate программе можно запросить доступ к дампу метаданных Enterprise Partner Feed, который Apple советует использовать вместо частых запросов к iTunes Search API.


И, на всякий случай, Music.app — это приложение "Музыка" на iOS устройствах, которое является официальным плеером Apple Music.


Источники сведений, в порядке убывания их полезности:


  • Официальная документация
  • Поиск по всей документации Apple
  • Метод научного эксперимента 1 с HTTP API
  • Developer Console в любимом браузере, натравленная на веб-страницу интересующей сущности в Apple Music
  • Форумы разработчиков на сайте Apple
  • StackOverflow

Поиск. Картинки


Самым очевидным способом проверить можно ли что-то сделать в своём приложении является факт того, что что-то подобное имеется в официальном приложении. Вот возьмём, например, поиск. Ищем группу "The Police" и имеем в выдаче Music.app фотографию группы с улыбающимся Стингом:


police search

Прекрасно, воспользуемся API поиска. В выдаче имеем ключ artwork с вложенной ссылкой url для всего, кроме артистов. Странно. Проверяем ещё раз, в Music.app всё есть. Более того, в веб-интерфейсе фотография тоже есть, а ссылка на веб-страницу имеется в json-выдаче API поиска.


iTunes Search API картинку так же не отдаёт, но так же включает в выдачу адрес веб-странице по ключу artistLinkUrl.


Ну что поделать, придётся скачивать web-страницу и доставать из неё адрес картинки, благо разработчики позаботились и прописали адрес картинки в мета-информацию:


<meta property="og:image" content="https://is5-ssl.mzstatic.com/image/thumb/Features113/v4/bb/a2/66/bba266dd-570b-bff7-d7c6-777982582964/mzl.tdrwskof.jpg/1200x630cw.png" id="ember52310559" class="ember-view">

Формально говоря, мы уже нарушили правила Apple, которые запрещают "scraping" их веб-страниц. Фактически, так делают все сторонние приложения за исключением тех, что ищут картинки с использованием сторонних сервисов.


Есть и ещё один минус: мы запрашиваем страницу размером в несколько сотен килобайт. Для приложения и без того гоняющего большие объёмы данных (проигрывающего музыку), терпимо (особенно при учёте, что данные передаются в сжатом виде), но в общем случае не очень.


Размеры картинок можно подгонять под свои нужды: в url имеются шаблоны для замены ({w}, {h}), а на веб-странице ссылка на картинку имеет тот же формат, так что сложностей получить картинку нужного размера нет. Если хочется получить закруглённую картинку, то нужно добавить cw перед расширением.


Бонус про картинки для отображения в плеере


Во время проигрывания плеер из Media Player оперирует конструкцией MPMediaItem, которая, в частности, предоставляет объект Artwork. Очень удобно для отображения картинки трека в плеере. Проблема лишь в том, что на практике данный объект, как его ни крути, не даёт картинки для "стримящейся" музыки (а это любая музыка из Apple Music), поэтому приходится "матчить" проигрываемый MPMediaItem c песнями, которые добавлялись в очередь и скачивать изображение аналогично альбомам.


Поиск по жанрам


В iTunes и приложении Apple Music имеются списки жанров и топы песен в оных. Найти жанр не должно составить труда, ведь мы можем искать по всему чему угодно?


Это далеко не так. API не даёт искать по жанрам и не даёт списка жанров. Есть возможность запросить информацию (из полезного — название) жанра по его идентификатору,
но большая часть сведений в API содержит как раз имя жанра, а не идентификатор.


Благо для iTunes предоставляется выгрузка всех жанров (совсем всех, включая "жанры" мобильных приложений). Идентификаторы жанров (как и прочие идентификаторы из iTunes) полностью соответствуют идентификаторам в Apple Music. Выгрузка доступна для всех регионов (регион указывается в ключе cc, а не storefront как в Apple Music API), но фактически отличаются лишь названия (они переведены). Структура жанров иерархична и имеются повторения названий жанров.


В общем, поиск по жанрам необходимо делать локально, причём учитывать, что "фанк джаз", "джаз-фанк" и "jazz funk" — это одно и тоже. Не сложно, но и не так просто, как попросить API поиска искать по жанрам.


Найдя жанр, можно запросить чарт (список самых популярных песен) для данного жанра. Для редких жанров вполне можно получить в выдаче всего 3 песни.


Забавный факт: для некоторых альбомов и артистов (Ed Sheeran из известных) в качестве одного из жанров указывается просто верхнеуровневый жанр "Музыка". Если хочется делать рекомендации на базе жанров, то данный жанр стоит игнорировать.


Песни артистов


Когда приложению необходимо проиграть песни артиста (музыкальной группы) чаще всего это означает, что необходимо проиграть популярные песни данной группы. Apple Music даёт возможность запросить песни как связь типа "songs" 2 для артиста. На выходе имеем максимум 20 песен 3, явно отсортированных по популярности.


Иногда всё же хочется получить чуть больше композиций. С очень популярными группами пройдёт вариант получения плейлистов, типа "[Artist Name]: главное" ("[Artist Name] Essentials") и "[Artist Name]: в деталях" ("[Artist Name]: Next Steps"), но в общем случае такой вариант не подходит. Другой вариант: взять 20 популярных песен, а остальное добить из альбомов, благо Apple Music даёт возможность получить сразу все альбомы артиста (связи типа albums) и все песни в них (include="tracks") за один запрос.


Но если получить все песни артиста, то возникает другая проблема: необходимо из всех этих песен отобрать популярные. К сожалению, API информации о популярности не предоставляет 4. Брать случайные песни из всего списка можно, но на практике такой подход приводит к частому проигрыванию совсем неинтересных записей (проходных лайвов, записей репетиций, скитов и прочего), что особо заметно на очень популярных группах 5.


Попробуем получить популярные песни другим образом. Ещё до появления MusicKit компания Apple начала предоставлять API поиска в iTunes Store. Как уже говорилось, каталог в Apple Music и iTunes Store одинаков 6 и, соответственно, идентификаторы песен тоже одинаковы. Данный API возвращает по умолчанию 50 песен, но допустимо запросить до 200 песен. Результаты в выдаче так же отсортированы по популярности.


Интересно, что сама компания Apple не использует ни один из этих вариантов в собственном веб-интерфейсе. Они используют недокументированный (читай, приватный) API 7, в выдаче которого есть всё то, чего не хватает, включая идентификатор (а не только название) жанра и индекс популярности. Использовать данный API на практике я не решился 8, поскольку идентификаторы жанров можно получить обратным поиском по именам жанров 9, а численный индекс популярности не так уж и важен, когда получаешь отсортированный по популярности список композиций.


Забавный факт, который я называю "проблемой Хью Лори" заключается в том, что в каталоге Apple Music имеется масса немузыкального контента. В частности, различные компиляции из комедийных шоу и стэндапы. Это с одной стороны прекрасно (мы платили за музыку, а получили ещё и смешные диалоги со Стивеном Фраем, которые можно послушать перед сном), а с другой приводит к прослушиванию какой-то болтовни, когда просил включить музыку. Соответственно, когда контекст работы приложения прямо говорит о том, что пользователь хочет слушать музыку, приходится самостоятельно убирать из полученного списка "песен" те, чьи жанры обозначены как "spoken word" и "comedy".


Рекомендации


Проигрывание рекомендаций (они же "похожие песни", "радиостанции", "вам понравится" и т.д.) стало базовой функциональностью стриминговых плееров. Условно рекомендации можно разделить на два вида 10: для пользователя (на базе истории прослушивания, покупок и лайков) и для музыкального объекта (артиста, трека, реже альбома и плейлиста).


С предпочтениями пользователя в Apple Music формально 11 всё в порядке: можно запросить список персональных рекомендаций, в который включаются "персональные миксы" (плейлисты) и рекомендуемые альбомы. Из альбомов список песен можно получить, используя API запроса треков (связь tracks), для плейлистов аналогично. Следует учитывать, что треки могут включать не только песни (type = "songs"), но и видео-клипы, потому дополнительная фильтрация полученных данных не повредит.


С другими же рекомендациями всё чуть сложнее. Если посмотреть в Music.app, то там отображаются "похожие артисты" и можно включить "радиостанцию" для данного артиста. Последняя проигрывает микс из артиста и рекомендаций к нему.


police similar

Кажется, всё просто: наверняка для артистов есть связь "похожие артисты". Спешу расстроить, такой связи нет. В схеме данных Enterprise Partner Feed сведения о похожести так же отсутствуют.


Хорошо, значит мы можем запросить "радиостанцию" для артиста и получить список песен? И снова нет, радиостанцию запросить мы действительно можем, её идентификатор находится в выдаче связей артиста. Но в сведениях о станции нет сведений о проигрываемых песнях, альбомах или артистах, есть только служебный объект с информацией для проигрывания playParams. Идея с playParams простая: скармливаем данный словарь 12 в очередь проигрывания и слушаем.


Это всё хорошо, если хочется играть только радиостанцию (как делает Music.app), но что если хочется смешать песни из радиостанции 13 с чем-то ещё? Очевидно, мы каким-то образом должны получить список похожих артистов.


В Media Player важную роль играют очереди проигрывания, в частности MPMusicPlayerControllerMutableQueue. Очередь позволяет: вставить дескрипторы после определённого проигрываемого объекта, удалить указанные объекты, а так же получить список всеъ объектов в очереди. При запросе объектов на выходе мы должны получить массив из MPMediaItem. А MPMediaItem, в частности включает идентификатор песни. Вот есть только одна проблема: конструкторы очередей не являются публичным API, переведу: Media Player может дать готовую очередь (иногда редактируемую), а самому создать её невозможно 14.


Хорошо, немного копнуть в сторону костылестроения может быть полезно. Если Media Player может дать мне изменяемую очередь, то её нужно взять. Для этого придётся работать с текущим (синглтон) плеером, так что действовать нужно быстро, иначе пользователю может проиграться что-то не то, а плеер успеет ещё и предзагрузить ненужные пока песни. Изменяем текущую очередь проигрывания (добавляем дескриптор станции), смотрим что получилось, откатываем измения в очереди новой транзакцией (удаляем то, что фактически добавилось в очередь).


К сожалению обнаруживаем добавление только двух объектов MPMediaItem в очередь. Оба выглядят как песни (есть имя артиста, название трека, длина трека), но не включают идентификатор песни. Если посмотреть в Music.app, то обнаружим, что там всё сходится: включив радиостанцию будет проигрываться песня, а в очереди будет только одна песня к проигрыванию. При переходе к следующей песне очередь обновляется.


Дальше пытаться строить костыли с радиостанциями смысла нет, приходится использовать другие варианты.


Вспомним, что, в Music.app и веб-интерфейсе отображаются похожие артисты. Похожие артисты доступны только для самых популярных групп 15. Опять приходится прибегать к запрещённому приёму парсинга веб-страницы. Из плюсов: мы уже скачиваем веб-страницу для получения картинки, значит мы можем получить из неё сразу и похожих артистов.


В коде html странице видим большой json-объект, из которого забираем сначала идентификаторы похожих артистов (["data"]["relationships"]["artistContemporaries"]):


web page data

а потом по этим идентификаторам получаем данные артистов из того же json (["included"] с типом lockup/artist):


web page included

Имея идентификаторы артистов мы можем запросить их треки. API позволяет запросить несколько артистов по их идентификаторам за один запрос с включёнными связями. Увы, на практике API возвращает ошибку, сообщающую о невозможности добавления списка треков к результату. Так можно делать только при запросе артистов по одному или при прямом запросе песен для отдельно взятого артиста. В этом случае можно воспользоваться уже упомянутым iTunes Search API, который отдаст чуть больше полезной информации.


Списки "похожих артистов" в Apple Music достаточно хороши, однако на практике часто оказываются пустыми. В ситуации пустого списка рекомендаций приходится использовать сторонние сервисы. Классика — Last.fm, чуть изощерённее — Spotify. Результаты обоих нужно матчить с объектами в каталоге Apple Music. Решение "в лоб" заключается в поиске каждого артиста по имени с получением ровно одного результата с последующим получением песен как описано выше. Решения чуть интереснее, но накладнее состоят в использовании musicbrainz для матчинга. Musicbrainz предоставляет информацию о идентификаторах 16 в других сервисах, т.е. можно связать идентификатор в Spotify с идентификатором в Apple Music. В ситуации с Last.fm всё ещё лучше: Last.fm возвращает в списке рекомендаций идентификаторы из musicbrainz. Накладность заключается в лимитах на использование musicbrainz, при большом трафике придётся поднимать собственный клон musicbrainz (благо, это возможно) или разрабатывать собственный API вокруг базы данных, дампы которой можно спокойно скачать.


Для новых групп даже Last.fm не даст рекомендаций, запасным вариантом будут "жанровые" рекомендации. Берём жанры артиста (на базе названия получаем идентификаторы из уже загруженного списка всех жанров), получаем для каждого жанра по чарту, смешиваем полученные песни. Это лучше, чем ничего.


Две вещи неразлучные: плеер и очередь


За проигрывание в iOS из Apple Music отвечает Media Player, он позволяет работать с музыкой из Apple Music и персональным каталогом пользователя 17. Фреймворк высокоуровневый и в ситуации работы со стриминговым контентом спуститься на уровень ниже 18 не получится.


Фреймворк предоставляет два плеера с общим программным интерфейсом: один играет музыку непосредственно в приложении Music.app, а другой работает более-менее независимо. Использовать первый для чего-то серьёзного бессмысленно 19. А документация нагло лжёт 20, что второй плеер не работает в фоновом режиме


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


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


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


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


С другой стороны, много всего делать не нужно: информация о проигрывании на экране блокировки просто есть и все кнопки в ней работают, Apple Watch и Siri умеют управлять проигрыванием, а стриминг через AirPlay на Apple TV работает из коробки 22.


Повторить функциональность и интерфейс очереди Music.app в своём приложении возможно, но кажущаяся простота drag'n'drop-а элементов очереди на деле оказывается жонглированием транзакциями изменения очереди и синхронизацией желаемой очереди и реальной очереди 23 плеера.


Пара слов про SDK для других платформ


Фактически, работа с API ничем не отличается на других платформах, а вот SDK сделаны не совсем под копирку с iOS и, местами, более удобны 24, хотя общая идея тандема очереди и плеера сохраняется.


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


MusicKit JS же предоставляет специальные методы-обёртки для работы с API Apple Music,
но при использовании обёрток без чтения документации по самому API не обойтись. Работа с очередью и плеером не удобнее iOS SDK, но с небольшими надстройками может стать терпимой .


Пара слов о симуляторе


Работать в симуляторе с MusicKit можно только в разрезе общения с HTTP API, но даже в этом случае придётся стабить токен пользователя для проведения запросов, а не получать его через StoreKit. Проигрывать музыку из Apple Music в симуляторе вовсе невозможно, ибо отсутствует приложение Music.app.


На деле приходится разрабатывать собственные надстройки над SDK и при запуске в симуляторе использовать альтернативные (mock) реализации. Фактически, при разработке симулятор оказывается полезен только для работы над пользовательским интерфейсом. Собственная абстракция будет полезна и при автоматизированном создании скриншотов для приложения (да и вообще при создании скриншотов с помощью симулятора), поскольку иметь в арсенале оба поколения "больших" iPad Pro только для того, чтобы сделать на обоих скриншоты своего плеера, крайне накладно и глупо.


В качестве интересного, но не очень полезного, эксперимента можно сделать альтернативную реализацию собственного высокоуровневого плеера на базе MusicKit JS.


Про Enterprise Partner Feed


С одной стороны Apple предоставляет по запросу дамп метаданных и говорит использовать его, дабы снизить нагрузку на API и получить сведения, которых в API нет. С другой стороны, мы имеем явную проблему курицы и яйца: доступ к Affiliate программе предоставляется при указании ссылки на приложение. Достаточно сложно разрабатывать приложение, используя данные, которые можно получить, только имея опубликованное в App Store приложение.


И, конечно, данные не дают первому встречному. Каждое заявление проверяют в течение пяти рабочих дней. На момент написания данной статьи получить дамп метаданных в свои руки мне так и не удалось. Apple завернуло моё заявление, не указав точных причин. Скорее всего, на промо-сайте приложения они увидели словосочетание "Apple Music" без упоминания о том, что я к Apple не имею отношения. Или они где-то увидели, что я использую сведения с веб-страниц, хотя при проверке приложения это проблем не вызывало. Конечно, попытки мои не закончились и сама Apple поощряет повторную подачу заявления, но факт в том, что дампа на руках нет.


Можно пофантазировать и даже сделать тестовую базу, поскольку секрета из схемы данных никто не делает, однако само наличие дампа на руках жизнь не упростит. Разрабатывать свой бэкенд и отправлять все запросы из приложения к нему вместо Apple Music не только затратно (как с точки зрения времени на разработку, так и на финансирование инфраструктуры серверной части), но и имеет скрытые проблемы. Так, фид обновляется раз в неделю, а новая музыка в Apple Music появляется ежедневно. Как участнику одного из музыкальных коллективов мне бы хотелось, чтобы свежий релиз появился сразу везде, а не только в официальных приложениях.


Вообще использование Enterprise Partner Feed видится полезным в следующих случаях:


  • большое количество активных пользователей и проявление ограничений со стороны Apple Music API или iTunes Search API
  • необходимо обработать большой объём данных ("пережувать данные"), чтобы предоставить какую-то интересную функциональность
  • есть много денег и много свободного времени

Очевидные советы


Приведу несколько банальных советов, которые кому-то могут пригодиться:


  1. Кешируйте по максимуму. Жанры можно кешировать на годы, песни на недели, а результаты поиска на дни. Картинки можно
    так же кешировать на годы, если они есть. В ситуации картинок крайне рекомендую кешировать факт их отсутствия, дабы не запрашивать веб-страницу вновь в ложной надежде
  2. Экспериментируйте в дебаг-режиме на устройстве, но не забывайте, что дебаг может неочевидным образом изменять поведение программы и замедлять её работу. Скажем, получить таймаут при изменении очереди в дебаг-режиме крайне просто, даже если просто автоматически выводишь информацию в консоль без фактической остановки на дебаг-точке.
  3. Учитывайте, что SDK покрывает самые очевидные и простые сценарии в настоящий момент, в остальных ситуациях нужно подключать смекалку. Поскольку Apple 25 делает медиа SDK максимально стабильными и расширяемыми 26, ожидать появления всего нужного в ближайшее время не приходится, но вполне вероятно, что часть текущих приватных API станет публичными, когда разработчики SDK посчитают их достаточно стабильными.
  4. Если вы платите за аккаунт разработчика, то можете задать два технических "code level" вопроса в год без дополнительной платы. Так же полезными являются официальные "Q&A" и "Tech Notes", поискать по ним можно в разделе для разработчиков, а общий список доступен на легаси-сайте документации.
  5. Не бросайтесь сразу делать свой бэкенд. Понимаю, сильно хочется, но это излишне и даже может навредить. Часто API (Last.fm, musicbrainz, iTunes Search API из упоминаемых в статье) делают ограничение по IP адресу источника запроса. Передача пользовательского токена для доступа к Apple Music на бэкенд не выглядит хорошей идеей. А переносить все данные из открытых источников "себе" не всегда возможно и далеко не дёшево, не смотря на бесплатность и открытость самих данных 27.
  6. Изображения, получаемые из Apple Music API, ровно как и превью песен являются материалами, охраняемыми авторским правом. Ревью-команда App Store отклонит ваше приложение, если в превью играется музыка (на которую нет специального соглашения) и на скриншоте отображаются картинки, например, альбомов (на которые так же нет специального разрешения от правообладателя). Предыдущее моё музыкальное приложение 28 спокойно проходило ревью, не смотря на то, что на скриншотах содержало массу картинок альбомов и фотографий артистов, которые получались не самым лучшим с точки зрения авторского права образом 29. Сейчас же Apple за этим следит чуть строже 30.
  7. Учитывайте, что веб-интерфейс Apple Music — приложение на Ember. Если собираетесь парсить сведения, то нужно убедиться, что они имеются в html странице до того как в дело вступают модификации DOM со стороны Javascript.
  8. Если парсите веб-страницы, то сделайте так, чтобы приложение не показывало ошибок при изменении структуры этих страниц. Лучше не отобразить картинку или воспользоваться запасным вариантом получения данных, если парсинг не удался.

Послесловие


Не смотря на шероховатости SDK, недостаточность сведений и отсутствие по настоящему полезных batch запросов в API, имеющихся в арсенале средств достаточно для разработки интересных программных продуктов, выходящих за рамки «альтернативных плееров» для Apple Music. Хочется верить, что и другие игроки рынка, включая Яндекс и Гугл начнут предоставлять публичные SDK для музыкальных сервисов. Будем надеяться на светлое будущее с большим выбором музыкальных SDK, а пока закатаем рукава и будем делать интересные вещи в рамках имеющихся ограничений.


Говорят, ограничения даже помогают.


Бесполезные примечания


1 Он же метод (научного тыка)


2 Заметьте, что документация о такой связи умалчивает


3 Конечно, в Music.app и веб-интерфейсе при просмотре альбомов есть индикация популярности песен (популярные песни отмечены звёздочками), но в публичном API эта информация (даже простой флаг "популярно") отсутствует


4 Играться с параметром limit не получится: Specified limit for relationship 'songs' exceeds maximum of 20


5 The Beatles


6 Если быть более точным, то всё что есть в Apple Music есть в iTunes Store. В выдаче API iTunes Store присутствует флаг `isStreamablez, по которому можно понять есть объект в Apple Music или нет.


7 Пример запроса: https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewArtistSeeAll?cc=ru&ids=424279384&section=0


8 Хотя уверен, что он достаточно стабилен и, скорее всего, используется в декстопных приложениях iTunes и на устаревших (необновляемых) устройствах с iTunes Store


9 А список жанров нам в любом случае нужен для поиска по ним


10 Классифицируем классификаторы, всё как в лучших университетах нашей страны!


11 Формально, поскольку рекомендации в Apple Music среди пользовательской базы часто воспринимаются негативно: они игнорируют дизлайки, пропуски песен и не всегда похожи на то, что хотел бы услышать пользователь


12 Естественно, предварительно обернув в MPMusicPlayerPlayParametersQueueDescriptor


13 Станции в Apple Music бывают разных типов, в том числе (живые) с настоящими ведущими. Такие нас не очень интересуют в контексте рекомендаций.


14 Если, конечно, хочется попасть в App Store


15 Не ожидайте увидеть (Триаду) в рекомендациях для (Нигатива) и (Сансару) в рекомендациях для (Курары). Вообще не ожидайте увидеть для них рекомендации.


16 Чаще всего это просто ссылки на веб-страницы, но из них достаточно просто достать настоящие идентификаторы


17 Персональный каталог состоит из того, что пользователь купил в iTunes Store и загрузил в iTunes Match


18 И тем более получить с помощью публичного API URL для стриминга


19 Исключение — игры или программы-утилиты, где проигрывание музыки не является основной задачей. Делегировать, так делегировать.


20 Документация говорит: (When your app moves to the background, the music player stops playing the current media.). В действительности под капотом плеер использует AVPlayer, который может работать в фоне, достаточно только указать необходимые флаги в Info.plist или просто поставить галочку в нужном месте и всё будет сделано автоматически.


21 Тогда всё же хочется делать чуть более общую реализацию и свою надстройку над очередью.


22 При этом везде отображаются правильные обложки, которые самому из MPMediaItem получить не удаётся


23 Которую так же приходится получать с помощью транзакции изменения очереди


24 Скорее всего это вызвано тем, что данные SDK подключаются как внешние библиотеки, что несколько развязывает руки разработчикам, позволяя сильно не заботиться об обратной совместимости. В ситуации с iOS Media Player является частью операционной системы, соответственно требования по стабильности API более высокие.


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


26 Фактически раньше тем же SDK можно было управлять только локальной библиотекой пользователя, теперь добавилась поддержка Apple Music. Apple не создала новый фреймворк, а улучшила существующий.


27 Дорого не только с точки зрения железа, но и самой дорогой части — стоимости труда программиста. Конечно, можно взять сведения из Enterprise Partner Feed, musicbrainz, всё это обработать и на каждый случай предоставлять свой API. Вполне вероятно, что это будет экономить трафик пользователя (хотя в музыкальном приложении это вопрос не первостепенный) и вообще работать быстрее. Однако стоит подождать до того момента, когда наберётся критическая масса пользователей и реальных их проблем.


28 R.I.P.


29 С помощью API поиска Bing


30 Оберегая нас от исков со стороны правообладателей

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


  1. nezdhanov
    26.08.2019 09:17

    Данная статья это перевод чего?


    1. pavel_manylov Автор
      26.08.2019 09:17

      это не перевод, это дамп из моей головы


  1. cjbars
    26.08.2019 10:52
    +1

    Очень полезные раскопки. За идею матчить через musicbrainz отдельное Спасибо, я уж и позабыл про его использование


  1. Gorniv
    26.08.2019 11:55
    +1

    Спасибо интересная статья, за musicbrainz — спасибо, буду смотреть.
    Я около 7 месяцев пилю свой плеер, с частью проблем столкнулся, с частью нет, например, с лимитами у меня вроде все хорошо.
    Есть еще куча интересных проблем) Например, добавление в библиотеку пользователя, получение песен и плейлистов из библиотеки работает, но не показывает какие из них скачены. Из плеера я не смог получить список песен, которые в очереди сейчас играют. Методы работы с текущей проигрываемой песней — глючат, а получение лирики видимо требует дополнительный запрос, который нигде не описан и видимо является приватным, так что у меня всегда возвращает nil кроме пары странных песен. На картинку артиста я в итоге на данный момент забил)
    iPad, на котором тестируют приложения, из коробки не умеет авторизацию в Apple music и я сделал специальный обработчик с просьбой пойти и поиграть музыку в стандартном плеере(потратил дней 10 не переписки и review). Я, кстати, скоро напишу статью о своих мучениях, а заинтересованных прошу в мой профиль — там есть ссылка на мой плеер.


    1. pavel_manylov Автор
      26.08.2019 12:05

      > Из плеера я не смог получить список песен, которые в очереди сейчас играют

      Вот с этим проблем не было, если добавляются именно песни (по идентификаторам в iTunes Store), то в очереди будут `MPMediaItem` с соответствующими идентификаторами

      > а получение лирики видимо требует дополнительный запрос

      Тексты песен берутся из мета-тагов и не доступны для Apple Music (только если артист сам не прописал в мета-таги тексты). Apple Music в приложении берёт тексты из musixmatch, у них есть API и SDK (можно в частности получать тексты при установленном приложении musixmatch), однако имеются вопросы с авторскими правами при таком использовании. Разработчик приложения Soor был вынужден выключить показ песен за неимением формальной договорённости с musixmatch, потому что иначе приложение не проходило ревью в App Store

      > iPad, на котором тестируют приложения, из коробки не умеет авторизацию в Apple music
      Тут не очень понимаю в чём дело (локальная проблема с конкретным устройством?), будет интересно почитать подробности

      А вот с плейлистами полная беда, даже абсурд какой-то: нельзя удалять плейлисты, которые были созданы в собственном приложении. Пока я не вижу альтернатив ведению собственных плейлистов в приложении с возможностью экспорта в Music.app и импортирования в одну сторону плейлистов из Music.app в приложение.


      1. Gorniv
        26.08.2019 13:13

        Вот с этим проблем не было, если добавляются именно песни (по идентификаторам в iTunes Store), то в очереди будут `MPMediaItem` с соответствующими идентификаторами

        Как получить список песен в очереди? я не смог, можете код кинуть?) Задача в том, что допустим запустили Music.app, оттуда какие-то песни(Apple Music) и потом стартуют приложение, как получить песни, которые сейчас в списке на воспроизведение?
        Тут не очень понимаю в чём дело (локальная проблема с конкретным устройством?), будет интересно почитать подробности

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

        печаль(


        1. pavel_manylov Автор
          26.08.2019 13:43

          Если работаем с плеером приложения (MPMusicPlayerApplicationController), то провести транзакцию, которая не изменяет состояние очереди, но получает из неё объекты.

          В вашем же случае речь идёт про интеграцию с Music.app, значит приходится использовать системный плеер, который не предоставляет методов для продвинутой работы с очередью. Тут ничего особо не поделать, можно лишь получать текущий проигрываемый трек (и подписываться на смену трека).

          Могу лишь предложить перейти на использование applicationQueuePlayer, но это означает полный отказ от управления состоянием приложения Music.app и не даст обработать указанный сценарий перехода из Music.app в ваше приложение.

          P.S. Есть, конечно, идиотский вариант: остановить проигрывание и поскипать треки туда-сюда проверяя какой трек обозначается как текущий, после чего вернуться на изначальный трек и продолжить проигрывания с заданной точки.


          1. Gorniv
            26.08.2019 14:08

            системный, жаль, но я так и предполагал( а приватные методы использовать нерешаюсь.


  1. pavel_manylov Автор
    26.08.2019 12:04

    (удалено, неправильная ветка)