From bc8ae59474e371ae34d92bfedd6f58760ad8c9f2 Mon Sep 17 00:00:00 2001 From: lostf1sh Date: Mon, 22 Jun 2026 19:55:51 +0300 Subject: [PATCH] fix: tag NetEase songs as remote source, request lossless, fix lyrics glyphs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NetEase library sync (#2431): - syncUnifiedLibrarySongsFromNetease() built SongEntity without sourceType, so rows defaulted to SourceType.LOCAL. NetEase tracks showed up in the local media library, the incremental-sync deletion diff was a no-op (getAllNeteaseSongIds filters source_type=2), and logout cleanup via clearAllNeteaseSongs deleted nothing. Now set sourceType = NETEASE and artistsJson, matching the SyncWorker path. - getSongUrl defaulted to "exhigh" with no lossless tier, so SVIP accounts were capped to lower quality. Default to "lossless" and fall back through exhigh/higher/standard so non-privileged accounts still resolve a URL. Lyrics card glyphs (#2427): - Extended Unicode glyphs (e.g. Icelandic æ ð þ) rendered as tofu because the lyrics text used the bundled Google Sans Rounded variable font, which drops those codepoints at runtime. Render lyrics with the platform default font (fontFamily = null), which has full Latin Extended coverage. --- .../data/netease/NeteaseRepository.kt | 21 ++++++++++++++++--- .../components/player/FullPlayerContent.kt | 5 ++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/theveloper/pixelplay/data/netease/NeteaseRepository.kt b/app/src/main/java/com/theveloper/pixelplay/data/netease/NeteaseRepository.kt index 324f8701e..aca90a4e7 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/netease/NeteaseRepository.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/netease/NeteaseRepository.kt @@ -13,7 +13,10 @@ import com.theveloper.pixelplay.data.database.NeteasePlaylistEntity import com.theveloper.pixelplay.data.database.NeteaseSongEntity import com.theveloper.pixelplay.data.database.SongArtistCrossRef import com.theveloper.pixelplay.data.database.SongEntity +import com.theveloper.pixelplay.data.database.SourceType +import com.theveloper.pixelplay.data.database.serializeArtistRefs import com.theveloper.pixelplay.data.database.toSong +import com.theveloper.pixelplay.data.model.ArtistRef import com.theveloper.pixelplay.data.model.Song import com.theveloper.pixelplay.data.network.netease.NeteaseApiService import com.theveloper.pixelplay.data.preferences.PlaylistPreferencesRepository @@ -486,7 +489,7 @@ class NeteaseRepository @Inject constructor( // ─── Song URL Resolution ─────────────────────────────────────────── - suspend fun getSongUrl(songId: Long, quality: String = "exhigh"): Result { + suspend fun getSongUrl(songId: Long, quality: String = "lossless"): Result { val now = System.currentTimeMillis() val lastAttempt = lastSongUrlAttemptAtMs[songId] if (lastAttempt != null && now - lastAttempt < songUrlRequestCooldownMs) { @@ -507,7 +510,9 @@ class NeteaseRepository @Inject constructor( val result = withContext(Dispatchers.IO) { runCatching { - val qualityFallbacks = linkedSetOf(quality, "higher", "standard") + // Try the requested level first (e.g. "lossless" for SVIP accounts), + // then degrade gracefully so non-privileged accounts still resolve a URL. + val qualityFallbacks = linkedSetOf(quality, "exhigh", "higher", "standard") var lastFailure: String? = null for (level in qualityFallbacks) { @@ -685,6 +690,14 @@ class NeteaseRepository @Inject constructor( ) } + val neteaseArtistRefs = artistNames.mapIndexed { index, artistName -> + ArtistRef( + id = toUnifiedArtistId(artistName), + name = artistName, + isPrimary = index == 0 + ) + } + val albumId = toUnifiedAlbumId(neteaseSong.albumId, neteaseSong.album) val albumName = neteaseSong.album.ifBlank { "Unknown Album" } albums.putIfAbsent( @@ -725,7 +738,9 @@ class NeteaseRepository @Inject constructor( bitrate = neteaseSong.bitrate, sampleRate = null, telegramChatId = null, - telegramFileId = null + telegramFileId = null, + artistsJson = serializeArtistRefs(neteaseArtistRefs), + sourceType = SourceType.NETEASE ) ) } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt index cb6e3d400..8a30f6f41 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt @@ -956,7 +956,10 @@ fun FullPlayerContent( onDismissLyricsSearch = { playerViewModel.resetLyricsSearchState() }, lyricsSyncOffset = lyricsSyncOffset, onLyricsSyncOffsetChange = { currentSong?.id?.let { songId -> playerViewModel.setLyricsSyncOffset(songId, it) } }, - lyricsTextStyle = MaterialTheme.typography.titleLarge, + // Use the platform default font (fontFamily = null) for lyrics so extended + // Unicode glyphs (e.g. Icelandic æ ð þ) render instead of tofu. The bundled + // Google Sans Rounded variable font drops these codepoints at runtime. (#2427) + lyricsTextStyle = MaterialTheme.typography.titleLarge.copy(fontFamily = null), colorScheme = LocalMaterialTheme.current, onBackClick = { showLyricsSheet = false }, onSaveLyricsToFile = playerViewModel::saveLyricsToFile,