feat: YouTube customizable bitrate, improved title matching, SpotubeDL engine fallback

- Add configurable YouTube Opus (96-256kbps) and MP3 (96-320kbps) bitrates
- Improve title matching with loose normalization for symbol-heavy tracks
- Add SpotubeDL engine v2 fallback for MP3 requests
- Improve filename sanitization in track metadata screen
- Bump version to 3.6.9+82
This commit is contained in:
zarzet
2026-02-17 17:08:02 +07:00
parent 3ac9ff1dd7
commit 8e6cbcbc2a
36 changed files with 1317 additions and 166 deletions
+22 -4
View File
@@ -39,6 +39,10 @@ class AppSettings {
final String lyricsMode;
final String
tidalHighFormat; // Format for Tidal HIGH quality: 'mp3_320', 'opus_256', or 'opus_128'
final int
youtubeOpusBitrate; // YouTube Opus bitrate (supported: 128/256 kbps)
final int
youtubeMp3Bitrate; // YouTube MP3 bitrate (supported: 128/256/320 kbps)
final bool
useAllFilesAccess; // Android 13+ only: enable MANAGE_EXTERNAL_STORAGE
final bool
@@ -103,6 +107,8 @@ class AppSettings {
this.locale = 'system',
this.lyricsMode = 'embed',
this.tidalHighFormat = 'mp3_320',
this.youtubeOpusBitrate = 256,
this.youtubeMp3Bitrate = 320,
this.useAllFilesAccess = false,
this.autoExportFailedDownloads = false,
this.downloadNetworkMode = 'any',
@@ -113,10 +119,16 @@ class AppSettings {
// Tutorial default
this.hasCompletedTutorial = false,
// Lyrics providers default order
this.lyricsProviders = const ['lrclib', 'musixmatch', 'netease', 'apple_music', 'qqmusic'],
this.lyricsProviders = const [
'lrclib',
'musixmatch',
'netease',
'apple_music',
'qqmusic',
],
this.lyricsIncludeTranslationNetease = false,
this.lyricsIncludeRomanizationNetease = false,
this.lyricsMultiPersonWordByWord = true,
this.lyricsMultiPersonWordByWord = false,
this.musixmatchLanguage = '',
});
@@ -156,6 +168,8 @@ class AppSettings {
String? locale,
String? lyricsMode,
String? tidalHighFormat,
int? youtubeOpusBitrate,
int? youtubeMp3Bitrate,
bool? useAllFilesAccess,
bool? autoExportFailedDownloads,
String? downloadNetworkMode,
@@ -215,6 +229,8 @@ class AppSettings {
locale: locale ?? this.locale,
lyricsMode: lyricsMode ?? this.lyricsMode,
tidalHighFormat: tidalHighFormat ?? this.tidalHighFormat,
youtubeOpusBitrate: youtubeOpusBitrate ?? this.youtubeOpusBitrate,
youtubeMp3Bitrate: youtubeMp3Bitrate ?? this.youtubeMp3Bitrate,
useAllFilesAccess: useAllFilesAccess ?? this.useAllFilesAccess,
autoExportFailedDownloads:
autoExportFailedDownloads ?? this.autoExportFailedDownloads,
@@ -229,9 +245,11 @@ class AppSettings {
// Lyrics providers
lyricsProviders: lyricsProviders ?? this.lyricsProviders,
lyricsIncludeTranslationNetease:
lyricsIncludeTranslationNetease ?? this.lyricsIncludeTranslationNetease,
lyricsIncludeTranslationNetease ??
this.lyricsIncludeTranslationNetease,
lyricsIncludeRomanizationNetease:
lyricsIncludeRomanizationNetease ?? this.lyricsIncludeRomanizationNetease,
lyricsIncludeRomanizationNetease ??
this.lyricsIncludeRomanizationNetease,
lyricsMultiPersonWordByWord:
lyricsMultiPersonWordByWord ?? this.lyricsMultiPersonWordByWord,
musixmatchLanguage: musixmatchLanguage ?? this.musixmatchLanguage,
+5 -1
View File
@@ -44,6 +44,8 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
locale: json['locale'] as String? ?? 'system',
lyricsMode: json['lyricsMode'] as String? ?? 'embed',
tidalHighFormat: json['tidalHighFormat'] as String? ?? 'mp3_320',
youtubeOpusBitrate: (json['youtubeOpusBitrate'] as num?)?.toInt() ?? 256,
youtubeMp3Bitrate: (json['youtubeMp3Bitrate'] as num?)?.toInt() ?? 320,
useAllFilesAccess: json['useAllFilesAccess'] as bool? ?? false,
autoExportFailedDownloads:
json['autoExportFailedDownloads'] as bool? ?? false,
@@ -63,7 +65,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
lyricsIncludeRomanizationNetease:
json['lyricsIncludeRomanizationNetease'] as bool? ?? false,
lyricsMultiPersonWordByWord:
json['lyricsMultiPersonWordByWord'] as bool? ?? true,
json['lyricsMultiPersonWordByWord'] as bool? ?? false,
musixmatchLanguage: json['musixmatchLanguage'] as String? ?? '',
);
@@ -105,6 +107,8 @@ Map<String, dynamic> _$AppSettingsToJson(
'locale': instance.locale,
'lyricsMode': instance.lyricsMode,
'tidalHighFormat': instance.tidalHighFormat,
'youtubeOpusBitrate': instance.youtubeOpusBitrate,
'youtubeMp3Bitrate': instance.youtubeMp3Bitrate,
'useAllFilesAccess': instance.useAllFilesAccess,
'autoExportFailedDownloads': instance.autoExportFailedDownloads,
'downloadNetworkMode': instance.downloadNetworkMode,