feat(app): кнопка «Распознать трек» (Shazam) + история распознанных

- кнопка распознавания в плеере: видна только на музыкальных станциях без
  метаданных эфира (track == null), показывает спиннер и результат через Toast
- распознанный трек отображается в плеере и пишется в ОТДЕЛЬНУЮ историю
  распознанных (не дублируется в историю эфирных треков — гейт по ключу)
- экран Истории: переключатель «Треки эфира | Распознанные», два списка
- Room: таблица recognized_track (миграция 7→8), DAO/репозиторий
- ShazamRepository → POST /shazam/recognize/{stationId}, маппинг 503/400 в текст
- MusicGenres.isMusicStation — клиентский гейт (синхронизирован с бэкендом)
- bump backend submodule (модуль shazam)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nk
2026-06-07 18:38:17 +03:00
parent 251809df33
commit 69682268f3
16 changed files with 371 additions and 28 deletions

View File

@@ -8,6 +8,7 @@ import com.radiola.data.remote.dto.GenresResponseDto
import com.radiola.data.remote.dto.HistoryResponseDto
import com.radiola.data.remote.dto.MagicLinkRequestDto
import com.radiola.data.remote.dto.MagicLinkVerifyDto
import com.radiola.data.remote.dto.RecognizeResponseDto
import com.radiola.data.remote.dto.TrackStatsDto
import com.radiola.data.remote.dto.UserSettingsDto
import kotlinx.serialization.json.JsonObject
@@ -30,6 +31,10 @@ interface RadiolaApi {
@GET("now-playing")
suspend fun getNowPlaying(): List<BackendNowPlayingDto>
// Распознавание играющего трека через Shazam (бэкенд сам тянет аудио из потока).
@POST("shazam/recognize/{stationId}")
suspend fun recognizeTrack(@Path("stationId") stationId: Int): RecognizeResponseDto
// Сабмит обложки, найденной клиентом в iTunes (см. CoverEnrichmentManager).
@POST("covers/submit")
suspend fun submitCover(@Body dto: com.radiola.data.remote.dto.SubmitCoverDto): com.radiola.data.remote.dto.SubmitCoverResponse

View File

@@ -0,0 +1,12 @@
package com.radiola.data.remote.dto
import kotlinx.serialization.Serializable
@Serializable
data class RecognizeResponseDto(
val matched: Boolean,
val artist: String? = null,
val song: String? = null,
val coverUrl: String? = null,
val album: String? = null
)