Repository navigation
Feat/lyrics romanization - #23
4 commits merged into
Conversation
…jabi lines An opt-in setting (lyrics_romanize, off by default) shows a Latin reading under each lyrics line, between the line and its translation, in the QML client and the Svelte UI. - crates/core/src/romanize.rs: script detected once per song; Korean (Revised Romanization), Cyrillic (7 languages), Devanagari and Gurmukhi are rule/table based, Chinese uses the pinyin crate (plain table only). Japanese songs are left alone so kanji-only lines never get pinyin. - Applied in get_lyrics on the daemon and the Tauri host only: the lyrics cache and downloaded .lrc files stay untouched, and old cache entries still parse. - Ported from PeekMusic (Metrolist fork, GPL-3.0) with fixes: merged Korean final/initial pairs, Hindi inherent vowel and schwa deletion, Russian -ого. Release ryotunesd grows by 302 KiB (+1.0 %).
Flipping lyrics_romanize now emits a one-shot lyrics-changed event from the daemon and the Tauri host. Open lyrics views (sidebar, Now Playing, mini player) refetch the current track from the cache and swap the lines in place: no loading state, the offset is kept, and the view lands back on the active line before the frame is drawn. A hidden view refetches when it is shown again, and a fetch already in flight is followed by one more.
The reading takes the line's state colour a step softer and drops the plain line's trailing leading; songs with readings get more space between line pairs.
|
A note for the Japanese follow-up, after looking into providers and local dictionaries. Providers. Boidu's TTML sometimes carries Apple's transliteration: a Local dictionaries, measured in isolated release binaries (same profile as the workspace):
vibrato's readings were right on every test line (一言 hitokoto, 越し goshi, 未だに imadani). What's left is post-processing: joining っ and auxiliaries onto the previous word (uba ta → ubatta, desho u → deshou), plus a particle rule (は/へ/を → wa/e/o), which I already applied in the test. IPADIC's license allows redistribution with its notice. 59 MiB can't stay resident in the daemon. The workable shape would be to load the dictionary only for a Japanese track, romanize, free it and trim, then keep the result with that track's cached lyrics so each song pays the ~190 ms once. So the choice is roughly: |
|
Merged, thank you. |
|
Thank you for the merge! Just wanted to remind, the Japanese romanization isn't added yet due to the libraries needed for that being kind of big (check previous comment). Id probably go for the vibrato library, if you're okay with that I'll add that soon. |
What changed
An opt-in setting, Settings ▸ Playback ▸ Romanized lyrics (
lyrics_romanize, off by default), shows a Latin reading under each lyrics line, between the line and its translation. It covers Korean, Chinese, Cyrillic (Russian, Ukrainian, Belarusian, Serbian, Macedonian, Bulgarian, Kyrgyz), Hindi and Punjabi.crates/core/src/romanize.rs(new): the script is detected once per song. Korean uses Revised Romanization with final/initial merges. Cyrillic picks the language from letters only that language uses. Devanagari and Gurmukhi use tables, with inherent-vowel/schwa deletion and Gurmukhi gemination. Chinese uses thepinyincrate (plain table only,default-features = false).LyricLine.romanized: Option<String>,#[serde(default)]: old cache entries still parse, and an unset reading is not serialized.get_lyricsinryotunesdfor both the core and the Spotify paths, and in the Tauri host). The lyrics cache and downloaded.lrc/embedded lyrics stay untouched.lyrics-changedevent. Open lyrics views (sidebar, Now Playing, mini player) refetch from the cache and swap the lines in place: no loading state, the timing offset is kept, and the view lands back on the active line. A hidden view refetches when it is shown again.romanize.rsmodule header the waycrates/spotifycredits Sonora. Fixes made while porting: merged Korean pairs are written once (국내 → gungnae, not gungnnae), Hindi keeps and drops the inherent vowel correctly (कमल → kamal, not kml), and Russian-ого/-его→-ovo/-evoonly at word end (много stays mnogo).Why
Lyrics in these scripts can't be sung along by anyone who doesn't read them. The Spotify models already carried
romanizedfields from Sonora, but nothing filled them, and the main provider chain never had a reading.Surface area
lyrics (QML
LyricsPanelin Now Playing, sidebar and mini-player; SvelteLyricsView), settings (QML Settings ▸ Playback, Svelte Settings ▸ Playback), provider-independent core (ryotunes-core), daemon/Tauriget_lyrics+set_settingValidation
./scripts/release-check.shcd ui && pnpm check && pnpm buildcargo fmt --all -- --checkromanize.rs(per language, the Japanese guard, identical-line suppression, cache back-compat)ryotunesdon Ryoku/Hyprland. The toggle flipped with lyrics open in the sidebar, Now Playing and the mini playerThe Svelte UI is updated for parity and passes
pnpm check, but I could not run it: on Linuxryotunesonly launches the native client.Regression contracts
ryoku-desktopsafetyget_lyricsreply while the setting is on, and one refetch per open lyrics view when it is flipped.Evidence
Release
ryotunesd: 30,064,840 → 30,374,504 bytes (+302 KiB, +1.0 %).pinyinalone in an isolated binary: +256 KiB, +136 KiB RSS. No RustSec advisory.Cargo.lockgains onlypinyin 0.11.0, which has no dependencies.Steady state, native client in Now Playing with a synced Korean/Chinese track playing, 60 s per phase after 10 s settling, setting flipped over the daemon socket:
ryotunesdCPUryotunesdPSSWithin run-to-run noise. The +1.5 MiB in the client is the extra text items. Nothing keeps working after the flip.
Follow-up
Japanese romanization, pending a decision on its cost.
kakasiadds ~1.5 MiB binary and ~1.2 MiB RSS, is unmaintained since 2022 and misreads common lyrics (君の名は → "kun no mei ha").lindera+ IPADIC downloads a 12.3 MB archive at build time.