feat(train): marked playlists kept on the phone, their tempo worked out (ADR 0030, D47)

Music's second part: the marked playlists' tracks are downloaded as
Jellyfin keeps them, on unmetered networks only, while the app is open
or resumes, and deleted once no marked playlist holds them. Each
track's tempo is worked out once on the phone: a BPM tag where the file
has one, else a minute decoded with MediaCodec and its spectral flux
autocorrelated (Tempo.kt, tested on the JVM). The music screen shows
what is kept and deletes it after asking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 12:48:53 +01:00
commit 7f18f001f4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 1676 additions and 24 deletions

View file

@ -494,5 +494,12 @@
"musicPlaylistsHint": "Train wählt aus den Titeln der Playlists, die du markierst, und behält sie auf diesem Telefon.",
"musicNoPlaylists": "Auf diesem Server gibt es noch keine Musik-Playlists.",
"musicRetry": "Erneut versuchen",
"musicTracks": "{count, plural, =1{1 Titel} other{{count} Titel}}"
"musicTracks": "{count, plural, =1{1 Titel} other{{count} Titel}}",
"musicKept": "Auf diesem Telefon",
"musicKeptWaiting": "{done} von {total} Titeln: der Rest wartet auf WLAN",
"musicKeptLoading": "Lädt: {done} von {total} Titeln",
"musicKeptDone": "{count} Titel, {size} MB, {tempo} mit Tempo",
"musicKeptDelete": "Löschen",
"musicKeptDeleteTitle": "Die Musik auf diesem Telefon löschen?",
"musicKeptDeleteText": "Alle Playlists werden abgewählt und ihre Titel von diesem Telefon gelöscht. Auf deinem Jellyfin-Server bleiben sie."
}

View file

@ -621,5 +621,15 @@
"musicNoPlaylists": "There are no music playlists on this server yet.",
"musicRetry": "Try again",
"musicTracks": "{count, plural, =1{1 track} other{{count} tracks}}",
"@musicTracks": {"placeholders": {"count": {"type": "int"}}}
"@musicTracks": {"placeholders": {"count": {"type": "int"}}},
"musicKept": "On this phone",
"musicKeptWaiting": "{done} of {total} tracks: the rest waits for Wi-Fi",
"@musicKeptWaiting": {"placeholders": {"done": {"type": "int"}, "total": {"type": "int"}}},
"musicKeptLoading": "Downloading: {done} of {total} tracks",
"@musicKeptLoading": {"placeholders": {"done": {"type": "int"}, "total": {"type": "int"}}},
"musicKeptDone": "{count} tracks, {size} MB, {tempo} with a tempo",
"@musicKeptDone": {"placeholders": {"count": {"type": "int"}, "size": {"type": "String", "example": "230.4"}, "tempo": {"type": "int"}}},
"musicKeptDelete": "Delete",
"musicKeptDeleteTitle": "Delete the music on this phone?",
"musicKeptDeleteText": "Every playlist is unmarked, and its tracks deleted from this phone. They stay on your Jellyfin server."
}

View file

@ -2991,6 +2991,48 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'{count, plural, =1{1 track} other{{count} tracks}}'**
String musicTracks(int count);
/// No description provided for @musicKept.
///
/// In en, this message translates to:
/// **'On this phone'**
String get musicKept;
/// No description provided for @musicKeptWaiting.
///
/// In en, this message translates to:
/// **'{done} of {total} tracks: the rest waits for Wi-Fi'**
String musicKeptWaiting(int done, int total);
/// No description provided for @musicKeptLoading.
///
/// In en, this message translates to:
/// **'Downloading: {done} of {total} tracks'**
String musicKeptLoading(int done, int total);
/// No description provided for @musicKeptDone.
///
/// In en, this message translates to:
/// **'{count} tracks, {size} MB, {tempo} with a tempo'**
String musicKeptDone(int count, String size, int tempo);
/// No description provided for @musicKeptDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get musicKeptDelete;
/// No description provided for @musicKeptDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete the music on this phone?'**
String get musicKeptDeleteTitle;
/// No description provided for @musicKeptDeleteText.
///
/// In en, this message translates to:
/// **'Every playlist is unmarked, and its tracks deleted from this phone. They stay on your Jellyfin server.'**
String get musicKeptDeleteText;
}
class _AppLocalizationsDelegate

View file

@ -1781,4 +1781,32 @@ class AppLocalizationsDe extends AppLocalizations {
);
return '$_temp0';
}
@override
String get musicKept => 'Auf diesem Telefon';
@override
String musicKeptWaiting(int done, int total) {
return '$done von $total Titeln: der Rest wartet auf WLAN';
}
@override
String musicKeptLoading(int done, int total) {
return 'Lädt: $done von $total Titeln';
}
@override
String musicKeptDone(int count, String size, int tempo) {
return '$count Titel, $size MB, $tempo mit Tempo';
}
@override
String get musicKeptDelete => 'Löschen';
@override
String get musicKeptDeleteTitle => 'Die Musik auf diesem Telefon löschen?';
@override
String get musicKeptDeleteText =>
'Alle Playlists werden abgewählt und ihre Titel von diesem Telefon gelöscht. Auf deinem Jellyfin-Server bleiben sie.';
}

View file

@ -1769,6 +1769,34 @@ class AppLocalizationsEn extends AppLocalizations {
);
return '$_temp0';
}
@override
String get musicKept => 'On this phone';
@override
String musicKeptWaiting(int done, int total) {
return '$done of $total tracks: the rest waits for Wi-Fi';
}
@override
String musicKeptLoading(int done, int total) {
return 'Downloading: $done of $total tracks';
}
@override
String musicKeptDone(int count, String size, int tempo) {
return '$count tracks, $size MB, $tempo with a tempo';
}
@override
String get musicKeptDelete => 'Delete';
@override
String get musicKeptDeleteTitle => 'Delete the music on this phone?';
@override
String get musicKeptDeleteText =>
'Every playlist is unmarked, and its tracks deleted from this phone. They stay on your Jellyfin server.';
}
/// The translations for English, as used in the United Kingdom (`en_GB`).