InquirerVP Duterte trial: Defense presents Davao City assessor as first witnessThe Jerusalem PostThe most Zionist holiday, and the Jews Israel keeps waiting - opinionPunchKano gov to present 2027 budget WednesdayZDF heuteEntdecken Sie das ZDF-NachrichtenstudioColliderOG Harry Potter Stars Officially React to Replacement Cast as Hit Spin-Off Series ReturnsWirtualna PolskaLekarze o stanie poszkodowanych. "Stan pacjentów stabilny"EuronewsRussia: HIV spreads beyond risk groups in 27 regions, epidemiologists sayAntara NewsGov't expects boost in F&B, fashion sectors ahead of year-end holidaysCapital FMLinah Jebii Kilimo dismisses claims she remains in Azimio, reaffirms UDA allegianceIl Fatto Quotidiano“Lavoro come tata per famiglie ricchissime, ma mi rifiuto di salire sui loro jet privati. Ho persino perso un lavoro per questo”: la storia di Samantha JordanABC NewsWATCH: Family's gigantic fern named 'Jeff' goes viral on social mediaسكاي نيوز عربيةسهم إنفيديا يتراجع 5% بعد تحقيق بشأن جنسن هوانغ
The Daily Newsstand · Free, Always
Monday, November 9, 2026

Поиск похожих песен в Spotify скатился в день сурка: как мы собрали свой Similar + Discovery в плеере Kinrox

Translate

В прошлой статье мы рассказывали, к чему приводят неистраченные токены в конце месяца: мы собрали открытый Mac-плеер Kinrox (GitHub), который жрёт 196 МБ оперативки вместо 2 ГБ у официального клиента Spotify, крутит фильмы с управлением через пульт на iPhone и включает скринсейвер «Матрицы» с дивана.

Казалось бы, память спасена, процессор в простое показывает 0%, можно спокойно слушать музыку. Но стоило попользоваться собственным плеером пару вечеров подряд, как вылезла проблема, знакомая почти каждому подписчику стримингов в 2026 году.

Включаешь любимый трек на стыке psybient, organic electronic или downtempo, хочешь найти что-то в том же духе, чего ты ещё не слышал, - а штатные алгоритмы Spotify радостно подсовывают тебе твои же собственные залайканные песни трёхлетней давности вперемешку с попсовыми сборниками для кофеен.

Мы посмотрели на верхнюю панель нашего плеера, переименовали скромную вкладку Music прямо в Spotify, добавили в левую колонку раздел Similar & discovery и решили разобраться: почему родной поиск похожей музыки в Spotify стал таким унылым и КАК собрать собственный прозрачный движок дискавери, если сама Spotify в 2025-2026 годах демонстративно отрубила разработчикам старые рекомендательные API?

Поехали.

Что не так с рекомендациями самого Spotify (и какой сюрприз ждёт в API 2026 года)

Если вы пользуетесь Spotify больше пары лет, то наверняка замечали, как изменилось поведение «Радио по треку», автоплея и «Миксов дня». Вместо музыкальных находок алгоритм запирает слушателя в тесном стеклянном стакане по трём причинам:

  1. Эффект шарманки из собственных лайков. До 60-80% выдачи в стандартных миксах и радио по песне составляют треки, которые уже лежат в ваших Liked tracks или крутились на прошлой неделе. Алгоритм оптимизирует удержание (чтобы вы не нажали Skip), а самый безопасный способ избежать скипа - скормить человеку то, что он уже заведомо лайкнул.

  2. Стягивание к коммерческому мейнстриму. Стоит шагнуть в нишевую электронику, этнику, handpan или редкий эмбиент, как через три-четыре песни радио плавно съезжает к самым популярным трекам смежного макрожанра. Настоящий андеграунд и малые лейблы тонут на дне.

  3. Абсолютный «чёрный ящик». Официальный клиент никогда не объясняет, почему конкретный трек оказался в списке рекомендаций: то ли у него общий сплит-релиз с вашим любимым артистом, то ли он просто попал в витринный промо-хаб.

Ну а теперь начнём разбираться, что происходит, когда ты открываешь документацию Spotify Web API в 2026 году с наивной мыслью: «Сейчас дёрнем /v1/recommendations, передадим seed_tracks, подкрутим target_popularity и сделаем красиво».

Стучимся в API - и получаем HTTP 404 / HTTP 403.

Очень короткий пересказ. В рамках закручивания гаек для приложений в режиме Development Mode (волна ограничений ноября 2024 - февраля 2026 года) Spotify отключила для сторонних разработчиков сразу весь классический стек рекомендаций: эндпоинты GET /v1/recommendations, GET /v1/audio-features, GET /v1/audio-analysis, GET /v1/artists/{id}/related-artists, витринные редакторские подборки и даже чтение треков чужих публичных плейлистов через GET /v1/playlists/{id}/items, если пользователь не владелец и не соавтор этого плейлиста. Конец пересказа.

Интересно. Почти все эти закрытые ручки (audio-features, related-artists, параметрический recommendations) были прямым наследием легендарного музыкального стартапа The Echo Nest, выросшего из MIT Media Lab. В 2014 году Spotify купила The Echo Nest примерно за 100 миллионов долларов. Десять лет открытое API Echo Nest позволяло тысячам инди-разработчиков строить музыкальные карты, генераторы плейлистов по BPM и поисковики редких сцен, пока сама платформа не решила прикрыть лавочку, чтобы на её графе не обучали внешние ИИ-модели. Спасибо за открытую экосистему, было весело.

Итак, прямых кнопок «дай похожих артистов» и «дай рекомендации по треку» в Web API больше нет. Но прежде чем изобретать обходной манёвр для релиза Kinrox 0.5.5, нам пришлось вытащить две собственные занозы в коде плеера.

Разминка перед дискавери: куда пропали чужие плейлисты и почему 30-минутный трек вешал альбом

Когда мы начали готовить интерфейс к новой трёхколоночной навигации (Spotify v3), вскрылись две прекрасные истории.

История первая. Как мы сами спрятали от пользователя все подписанные плейлисты

Открываем левую колонку библиотеки: лайкнутые треки на месте, сохранённые альбомы на месте, а в списке Playlists зияет пустота - видны только личные плейлисты пользователя, тогда как все жанровые подборки, на которые он подписан в Spotify (YOGA | Rhythm & Flow, Guqin/Relaxing Music, Calming Music), бесследно исчезли!

Лезем в SpotifyLibraryStore+Loading.swift и на 54-й строчке находим шедевр собственной перестраховки:

// Как было до 0.5.5:
let editable = remotePlaylists.filter { $0.isEditable(by: library.userID) }

Откуда взялся этот фильтр? В Kinrox работает удобное правило FavoritesPolicy: если ты снимаешь сердечко лайка с песни, плеер автоматически вычищает её и из твоих собственных плейлистов, чтобы она больше не всплывала при шаффле. Поскольку удалять песни из чужих плейлистов нельзя (Spotify вернёт 403 Forbidden), первая версия загрузчика просто... выкидывала все чужие плейлисты прямо на этапе чтения списка GET /v1/me/playlists! Причём в модели LibrarySnapshot уже лежало отдельное вычисляемое свойство editablePlaylists, но кто бы о нём вспомнил.

В 0.5.5 мы разделили сам факт существования плейлиста в медиатеке и доступность его содержимого (itemsAvailable + restrictedPlaylistIDs), а метод ensurePlaylistLoaded(_:requireEditable:) научили различать чтение и запись:

  • при обычном просмотре (requireEditable: false) в левой колонке сохраняются вообще все плейлисты (с фильтрами All | Mine | Subscribed и разворачиванием секции на всю высоту сайдбара по кнопке Show all →);

  • если Spotify в Development Mode отдаёт 403 на попытку прочитать треки чужого закрытого плейлиста, плеер не падает и не прячет подборку, а помечает itemsAvailable = false и выводит спокойную плашку с прямой кнопкой открытия в официальном клиенте;

  • а для поиска живых тематических подборок прямо в сайдбаре появились быстрые чипсы (#Cosmic, #Harp, #Psybient, #Techno), которые догружают доступные плейлисты через поисковый эндпоинт.

История вторая (0.5.4). Детектив с треком на 33 минуты и гонкой на 200 миллисекунд

Посмотрите на верхний левый угол заглавного скриншота: там играет трек Spiritual Ascension (963 Hz) от Meditative Mind общей длительностью 33 минуты 33 секунды. Именно на таких длинных медитативных и эмбиент-альбомах перед релизом 0.5.4 мы поймали фантомный баг: иногда после окончания длинной дорожки плеер не переходил к следующей песне, а молча замирал.

Расследование в SoloistStateReducer и SoloistProcessSupervisor выявило сразу три сцепленных фактора. Например:

  • Раннее опустошение буфера на длинных треках. Консольный движок Spotify Soloist дочитывает последние секунды длинного потока в память заранее, переходит в состояние финального буфера за до 5 секунд до номинальной длительности (duration), а в самом конце присылает кадр со сброшенной позицией 0:00 и статусом paused или idle. Наш старый редьюсер ждал конца трека в более узком коридоре, принимал этот финальный paused на нулевой секунде за ручную паузу пользователя и не вызывал переход к следующему элементу очереди! Расширили окно naturalEnd до 5 секунд и добавили защёлку (latch) терминального завершения.

  • Гонка между закрытием WebSocket и exit(0). В режиме --single-track дочерний процесс soloist после окончания песни штатно завершается с кодом 0. Но ядро macOS закрывает TCP-сокет локального WebSocket на долю секунды раньше, чем доставляет родителю сигнал завершения процесса. В результате наш 200-миллисекундный таймер обрыва связи иногда успевал сработать раньше terminationHandler и отменял штатное переключение трека. Научили супервайзер перед отправкой ошибки сокета проверять, не находится ли процесс в стадии штатного выхода.

  • Усыпление через macOS App Nap. Когда 33-минутный трек играет в фоне при погашенном экране, macOS норовит притормозить таймеры приложения в самый момент склейки между песнями. Добавили удержание системной активности (ProcessInfo.processInfo.beginActivity) на время воспроизведения и переходов, а заодно научили очередь автоматически перешагивать через недоступные в регионе треки (isPlayable == false), чтобы одна заблокированная дорожка в середине альбома не тормозила всю пластинку.

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

КАК мы сделали Similar & Discovery без закрытых эндпоинтов Spotify

Итак, условие задачи: пользователь слушает песню, открывает альбом, выбирает плейлист или кликает на артиста и нажимает кнопку Similar (в верхней деке, в шапке коллекции или переходит во вкладку Similar & discovery слева). Нам нужно выдать ему список реально близких по духу композиций, андеграундных находок и свежих релизов, не имея доступа ни к /v1/recommendations, ни к /v1/related-artists, ни к /v1/audio-features.

Зачем мы вообще решили строить свой прозрачный граф вместо того, чтобы смириться с ограничениями API? Чтобы писать системы, которые понятно поддерживать через два года и которые не разваливаются под реальной нагрузкой или очередной сменой правил платформы.

На пальцах. Если у вас отобрали готовую кнопку «сделай мне хорошо», нужно посмотреть, какие фундаментальные данные Spotify не может закрыть, не сломав собственный поиск и каталог. Таких открытых источников четыре, и на их пересечении мы построили движок MusicDiscoveryEngine (Packages/KinroxMusic/Sources/MusicCore/MusicDiscovery.swift) в связке с SpotifyLibraryStore+Discovery.swift.

Сигнал №1. Совместная встречаемость в плейлистах (playlistCooccurrence, вес 90 + 10 × N)

Кто лучше любого алгоритма знает, какие два трека идеально звучат рядом в ночном сете? Живые люди, которые годами собирали тематические плейлисты руками.

Когда вы ищете похожую музыку по треку или артисту (на скриншоте это David Hohme, Waxman (CA), Slow Ted - With Me - Slow Ted Radio Edit), Kinrox формирует поисковый запрос GET /v1/search?q=<артист + название>&type=playlist,track&limit=5, а параллельно сканирует граф всех ваших локальных и доступных плейлистов в library.playlists.

Если плейлист содержит исходный трек или треки того же артиста, движок вытаскивает его соседей по списку (до 25 треков из каждого найденного читаемого плейлиста) и считает частоту пересечений cooccurrenceCounts[track.id]:

  • Попался трек в одном общем плейлисте с вашим сидом → получает базовый скор 100 баллов (90 + 1 × 10, либо 70 + 5 × N для чисто локального фоллбэка).

  • Встретился сразу в двух или трёх независимых плейлистах → взлетает на самый верх таблицы с весом 110-120 баллов!

  • Сами найденные подборки не пропадают зря: до 3 лучших из них превращаются в готовые карточки секции MIXES & COMPILATIONS над таблицей треков (на скриншоте - карточка slow с кнопками Play и + Queue).

Сигнал №2. Граф сплитов, ремиксов и лейбловых сборников (compilation, вес 80)

В электронной, инструментальной и инди-музыке артисты постоянно выпускают совместные EP, ремиксы друг на друга (как Fear Less - Hraach Remix у того же David Hohme в шестой строчке нашего скриншота) и попадают на сборники родственных лейблов.

Эндпоинт /v1/artists/{id}/related-artists закрыт, но базовый каталог альбомов артиста открыт! Мы делаем запрос с фильтром групп включения:

GET /v1/artists/{id}/albums?include_groups=appears_on,compilation

Параметры appears_on и compilation игнорируют сольные номерные пластинки самого музыканта и возвращают ровно то, что нужно для дискавери: гостевые участия и сборники, где наш артист засветился плечом к плечу с другими музыкантами той же сцены. Мы загружаем треклисты первых трёх таких релизов через GET /v1/albums/{id}/tracks, начисляем им по 80 баллов и выводим с пометкой Compilation: <название релиза>.

Сигнал №3. Скрытые поисковые операторы tag:hipster и tag:new (вес от 95 до 75)

А вот здесь кроется самая крутая фишка, о которой не подозревают 99% обычных пользователей Spotify.

В верхнем ряду фильтров под заголовком Similar & discovery мы сделали три переключателя режимов (MusicDiscoveryMode): Same spirit, Underground и Fresh.

Откуда берутся данные для Underground и Fresh? В поисковом движке Spotify (GET /v1/search?type=album) до сих пор живут два специальных серверных оператора фильтрации альбомов:

  1. tag:hipster - инструктирует сервер Spotify вернуть только те альбомы, которые входят в нижние 10% по общей популярности во всём каталоге! Когда вы нажимаете таблетку Underground, Kinrox берёт имя вашего артиста или выбранный жанровый тег, дописывает к нему tag:hipster, забирает найденные малоизвестные альбомы и разворачивает их треки с приоритетным весом 95 - offset. Никакой попсы и миллионников - только настоящий андеграунд, который в обычном интерфейсе Spotify не всплывёт никогда.

  2. tag:new - фильтрует каталог по альбомам, вышедшим за последние две недели. Нажимаете Fresh - и получаете свежие релизы по вашей нише с тем же приоритетным весом 95 - offset.

Причём тут мы сразу поставили жёсткий предохранитель в коде и закрепили его тестом freshAndUndergroundOnlyLabelConfirmedTracks (BrowserSelectionTests.swift):

// Треки из локального кэша никогда не получают фальшивый бейдж андеграунда или новинки:
// метки .underground и .freshRelease выдаются только трекам из подтверждённого ответа с tag:hipster / tag:new
#expect(freshItem?.reasonKind == .freshRelease)
#expect(cachedItem?.reasonKind != .freshRelease)
#expect(cachedItem?.reasonKind != .underground)

Если в процессе поиска в выдачу подмешались просто похожие треки из локального каталога, движок никогда не приклеит к ним обманчивый ярлык Underground · tag:hipster или Fresh release · tag:new.

Сигнал №4. Жанровые чипсы артиста (sharedGenre и sharedArtist, вес 85 - offset)

Четвёртый слой - детальная жанровая таксономия Spotify. Карточка артиста GET /v1/artists/{id} по-прежнему отдаёт массив узких жанровых тегов genres: [String] (именно оттуда на скриншоте подтянулись кнопки #psybient, #organic electronic, #downtempo, #handpan, #ambient, #melodic techno).

В режиме Same spirit мы используем строгий поисковый оператор genre:"<выбранный жанр>" по типу track, а клик по любому жанровому чипсу мгновенно перестраивает выборку вокруг конкретного оттенка звучания.

Почему это не превращается в кашу: фильтр Hide liked, двухфазный отклик и колонка WHY THIS TRACK

Собрать кандидатов из четырёх источников - полдела. Чтобы выдача радовала каждый день, мы добавили три правила, которых катастрофически не хватает в официальном клиенте:

  1. Галочка Hide liked включена по умолчанию. Первая же строчка в MusicDiscoveryEngine.build формирует множество исключений: let excludedIDs = seedTrackIDs.union(hideLiked ? library.likedTrackIDs : []). Все 760 треков, которые уже лежат в ваших лайках на скриншоте, автоматически вырезаются из выдачи! Вы видите только 394 новых кандидата, а не собственную заезженную пластинку.

  2. Колонка WHY THIS TRACK напротив каждой песни. Никаких секретов от слушателя. У каждого рекомендованного трека справа горит янтарный бейдж, объясняющий, за какие заслуги он сюда попал:

    • In 1 playlists: David Hohme - найден по пересечению в плейлистах;

    • Compilation: ... - вытащен из совместного сборника или appears_on;

    • Underground · tag:hipster - альбом из нижних 10% популярности Spotify;

    • Fresh release · tag:new - релиз последних двух недель;

    • Artist: David Hohme, Waxman (CA) или Genre: Mid-Atlantic Dub - совпадение по коллабораторам или жанровому оператору.

  3. Двухфазное обновление за 0 мс + сетевое дообогащение. Когда вы кликаете Similar или переключаете фильтр, никто не заставляет вас смотреть в пустой экран со спиннером. В MusicViewModel+Discovery.swift метод refreshDiscovery() сначала синхронно за единицы миллисекунд строит локальный снимок по вашей SQLite-базе и графу плейлистов, сразу отрисовывает таблицу, а параллельно запускает асинхронную задачу с проверкой discoveryRequestID и sessionGeneration. Если вы быстро прощёлкали три жанра подряд, старые сетевые запросы мгновенно отменяются через Task.checkCancellation(), не устраивая гонок в UI.

Попутно в 0.5.5 мы перестроили и правую часть окна: теперь там постоянно живёт компактная очередь CompactQueueColumn (Queue). Нашли в Similar & discovery крутую песню, целый микс или альбом? Просто перетаскиваете его мышкой (track:<id>, album:<id>, playlist:<id>) между любыми двумя строчками очереди справа - и альбом сам разворачивается в список треков ровно в указанной позиции.

Сравнение в цифрах: штатный рекомендатор Spotify против Kinrox 0.5.5

Сведём отличия и инженерные параметры в одну таблицу.

Параметр

Официальный клиент Spotify.app

Сторонние клиенты на старом Web API

Kinrox 0.5.5 (Similar & Discovery)

Уровень уверенности

Потребление RAM на macOS (arm64)

~2000 МБ (Electron / Chromium)

Зависит от оболочки

~180-196 МБ (Kinrox) + 24,7-27,3 МиБ (soloist)

Твёрдо (vmmap / top)

Работа рекомендаций в Development Mode (после февраля 2026)

Внутри закрытого бэкенда

Сломано (/v1/recommendations и /v1/related-artists возвращают 403/404)

Работает через 4 открытых сигнала каталога и поиска

Твёрдо (проверено в SpotifyLibraryTests)

Защита от старых лайков в выдаче

Нет (постоянно крутит уже лайкнутые треки)

Ручная фильтрация, если реализована

Встроенный тумблер Hide liked (включён по умолчанию)

Твёрдо

Поиск редкого андеграунда (нижние 10% популярности)

Нет кнопки в UI

Почти никто не выводит в интерфейс

Отдельный режим Underground (tag:hipster)

Твёрдо

Фильтр только свежих релизов (за 14 дней)

Размыт в общем Release Radar

Редко используется

Отдельный режим Fresh (tag:new)

Твёрдо

Объяснение причины рекомендации каждого трека

Нет («чёрный ящик»)

Нет

Явный бейдж Why this track у каждой строки

Твёрдо

Время первого отклика при открытии Similar

Сетевой запрос к бэкенду

Сетевой запрос

Мгновенно (<5 мс) из локального SQLite-графа + фоновое дообогащение

Твёрдо

Куда этот движок может расти дальше (и почему здесь огромный простор для экспериментов)

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

  1. Локальная обратная связь по дослушиваниям и скипам в SQLite. У нас уже работает быстрый локальный кэш SpotifyCacheStore на libsqlite3. Если локально (без всякой отправки данных на внешние серверы!) считать, какие рекомендованные треки вы дослушали до конца или добавили в очередь, а какие переключили на первых 15 секундах, скоринг сможет сам подстраивать веса конкретных плейлистов-доноров, лейблов и тегов под ваше настроение. Уровень уверенности: Похоже (вся инфраструктура БД и событий конца трека в SoloistStateReducer уже готова).

  2. Мульти-сиды («скрестить два трека или двух артистов»). В текущей версии openSimilar отталкивается от одного выбранного объекта (трека, альбома, плейлиста или исполнителя). Но что мешает выделить в таблице сразу два-три трека из разных жанров и найти пересечение их графов компиляций и пользовательских плейлистов? Так можно находить идеальные мосты между, скажем, неоклассикой и даунтемпо. Уровень уверенности: Похоже (MusicDiscoveryEngine.build уже принимает множества seedTrackIDs: Set<String> и seedArtistIDs: Set<String>).

  3. Обогащение через открытые музыкальные базы (MusicBrainz, Discogs, Bandcamp). Spotify знает далеко не всё о реальных связях музыкантов. Поскольку Kinrox - обычное десктопное приложение на macOS, к его графу можно подключить открытое API MusicBrainz или Discogs: узнавать, кто сводил альбом, на каком микро-лейбле выходил винил, в каких сайд-проектах играет перкуссионист, и искать эти имена обратно в каталоге Spotify. Уровень уверенности: Гипотеза (нужно аккуратно сматчить сущности по ISRC и названиям, чтобы не наплодить ложных совпадений).

  4. Управление волной открытий прямо с iPhone-пульта. Сейчас с айфона можно управлять очередью, громкостью и лайками, а в следующих версиях хочется добавить на экран пульта быстрые кнопки Similar, Underground и Fresh - чтобы менять музыкальную волну, вообще не подходя к ноутбуку.

Где это не сработает и о чём стоит помнить

Обозначим реальные ограничения текущего подхода, чтобы никто не ждал чуда там, где его нет:

  • Если вы слушаете совсем изолированного исполнителя, у которого в Spotify нет ни одного совместного сборника (appears_on/compilation), ни одного жанрового тега в профиле и который не попал ни в один читаемый плейлист, сетевой граф вернёт минимум пересечений и движок мягко откатится на локальный каталог вашей медиатеки.

  • Ограничение Spotify Development Mode на чтение чужих плейлистов (GET /v1/playlists/{id}/items → 403 Forbidden для не-владельцев) означает, что для ко-оккурентности по сети движок может заглядывать внутрь ваших собственных и коллаборативных плейлистов, а также альбомов и поисковой выдачи треков, но не внутрь закрытых редакционных плейлистов самой Spotify.

  • Как и прежде, для работы музыкального режима нужен активный аккаунт Spotify Premium, свой Client ID из кабинета разработчика и скачиваемый в один клик официальный движок Spotify Soloist.

ИТОГ. В чём вообще кайф Kinrox и почему мы зовём вас в Open Source

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

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

Во-вторых, единый пульт на iPhone для музыки и сериалов. Когда MacBook подключён к проектору или большому экрану по HDMI, вы лежите на диване и с телефона в локальной сети спокойно листаете треки из Spotify. А когда включили кино или сериал - с того же айфона перематываете заставку на 10 или 30 секунд, меняете аудиодорожку с субтитрами или в одно касание переходите к новой серии.

И в-третьих, самое главное - это стопроцентный Open Source:

  • Скачать готовый нотаризованный билд Kinrox 0.5.5 для Mac (Apple Silicon, macOS 14+): https://kinrox.app/

  • Исходники на Swift 6, движок MusicDiscoveryEngine и все расследования в docs/RESEARCHES: https://github.com/awaik/Kinrox

Весь код открыт специально для того, чтобы над плеером можно было прикалываться и развивать его вместе. Хотите подкрутить формулу скоринга в Similar & discovery или прицепить к ней MusicBrainz? Сделали форк, написали фичу, закинули Pull Request. Считаете, что одного скринсейвера «Матрицы» мало и рядом обязательно должен быть трёхмерный лабиринт из Windows 95, трубы или полёт сквозь звёзды? Присылайте PR - и ваш скринсейвер или ваш алгоритм поиска музыки поедет в следующий релиз приложения!

А в комментариях расскажите: как вы сами сейчас ищете новую музыку, когда рекомендации стримингов начинают крутить одно и то же по десятому кругу?

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.