mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-06-08 23:53:57 +02:00
98fdc0ed7c
Requested by @okinaau in issue #242 — brings back the ability to download tracks in lossy format for users on low storage devices. HIGH quality fetches the AAC M4A stream directly from the Tidal server (no lossless download + re-encode), then converts to MP3 or Opus via FFmpeg based on the tidalHighFormat setting (mp3_320, opus_256, or opus_128). - go_backend/tidal.go: restore outputExt .m4a, filename logic, duplicate-check guard, HIGH M4A lyrics/LRC handling, and bitDepth=0/sampleRate=44100 for HIGH quality result - settings.dart + settings.g.dart: re-add tidalHighFormat field (default mp3_320) with JSON serialization - settings_provider.dart: re-add setTidalHighFormat(), remove migration that force-migrated HIGH to LOSSLESS - download_queue_provider.dart: restore HIGH conversion logic for both SAF and non-SAF paths using FFmpegService.convertM4aToLossy - download_settings_page.dart: restore Lossy 320kbps quality tile, format sub-picker tile, _getTidalHighFormatLabel helper, and _showTidalHighFormatPicker bottom sheet - l10n: add 10 keys (downloadLossy320, downloadLossyFormat, downloadLossy320Format, downloadLossy320FormatDesc, downloadLossyMp3, downloadLossyMp3Subtitle, downloadLossyOpus256/Subtitle, downloadLossyOpus128/Subtitle) to ARB and all 13 generated files
287 lines
11 KiB
Dart
287 lines
11 KiB
Dart
import 'package:json_annotation/json_annotation.dart';
|
|
|
|
part 'settings.g.dart';
|
|
|
|
@JsonSerializable()
|
|
class AppSettings {
|
|
final String defaultService;
|
|
final String audioQuality;
|
|
final String filenameFormat;
|
|
final String downloadDirectory;
|
|
final String storageMode; // 'app' or 'saf'
|
|
final String downloadTreeUri; // SAF persistable tree URI
|
|
final bool autoFallback;
|
|
final bool embedMetadata; // Master switch for metadata/cover/lyrics embedding
|
|
final bool embedLyrics;
|
|
final bool maxQualityCover;
|
|
final bool isFirstLaunch;
|
|
final int concurrentDownloads;
|
|
final bool checkForUpdates;
|
|
final String updateChannel;
|
|
final bool hasSearchedBefore;
|
|
final String folderOrganization;
|
|
final bool createPlaylistFolder;
|
|
final bool useAlbumArtistForFolders;
|
|
final bool usePrimaryArtistOnly; // Strip featured artists from folder name
|
|
final bool filterContributingArtistsInAlbumArtist;
|
|
final String historyViewMode;
|
|
final String historyFilterMode;
|
|
final bool askQualityBeforeDownload;
|
|
final String spotifyClientId;
|
|
final String spotifyClientSecret;
|
|
final bool useCustomSpotifyCredentials;
|
|
final String metadataSource;
|
|
final bool enableLogging;
|
|
final bool useExtensionProviders;
|
|
final String? searchProvider;
|
|
final bool separateSingles;
|
|
final String albumFolderStructure;
|
|
final bool showExtensionStore;
|
|
final String locale;
|
|
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/320 kbps)
|
|
final int
|
|
youtubeMp3Bitrate; // YouTube MP3 bitrate (supported: 128/256/320 kbps)
|
|
final bool
|
|
useAllFilesAccess; // Android 13+ only: enable MANAGE_EXTERNAL_STORAGE
|
|
final bool
|
|
autoExportFailedDownloads; // Auto export failed downloads to TXT file
|
|
final String
|
|
downloadNetworkMode; // 'any' = WiFi + Mobile, 'wifi_only' = WiFi only
|
|
final bool
|
|
networkCompatibilityMode; // Try HTTP + allow invalid TLS cert for API requests
|
|
final String
|
|
songLinkRegion; // SongLink userCountry region code used for platform lookup
|
|
|
|
final bool localLibraryEnabled; // Enable local library scanning
|
|
final String localLibraryPath; // Path to scan for audio files
|
|
final String
|
|
localLibraryBookmark; // Base64-encoded iOS security-scoped bookmark
|
|
final bool
|
|
localLibraryShowDuplicates; // Show indicator when searching for existing tracks
|
|
final String
|
|
localLibraryAutoScan; // Auto-scan mode: 'off', 'on_open', 'daily', 'weekly'
|
|
|
|
final bool
|
|
hasCompletedTutorial; // Track if user has completed the app tutorial
|
|
|
|
final List<String>
|
|
lyricsProviders; // Ordered list of enabled lyrics provider IDs
|
|
final bool
|
|
lyricsIncludeTranslationNetease; // Append translated lyrics (Netease)
|
|
final bool
|
|
lyricsIncludeRomanizationNetease; // Append romanized lyrics (Netease)
|
|
final bool
|
|
lyricsMultiPersonWordByWord; // Enable v1/v2 + [bg:] tags for Apple/QQ syllable lyrics
|
|
final String
|
|
musixmatchLanguage; // Optional ISO language code for Musixmatch localized lyrics
|
|
|
|
final String
|
|
lastSeenVersion; // Last app version the user has acknowledged (e.g. '3.7.0')
|
|
|
|
const AppSettings({
|
|
this.defaultService = 'tidal',
|
|
this.audioQuality = 'LOSSLESS',
|
|
this.filenameFormat = '{title} - {artist}',
|
|
this.downloadDirectory = '',
|
|
this.storageMode = 'app',
|
|
this.downloadTreeUri = '',
|
|
this.autoFallback = true,
|
|
this.embedMetadata = true,
|
|
this.embedLyrics = true,
|
|
this.maxQualityCover = true,
|
|
this.isFirstLaunch = true,
|
|
this.concurrentDownloads = 1,
|
|
this.checkForUpdates = true,
|
|
this.updateChannel = 'stable',
|
|
this.hasSearchedBefore = false,
|
|
this.folderOrganization = 'none',
|
|
this.createPlaylistFolder = false,
|
|
this.useAlbumArtistForFolders = true,
|
|
this.usePrimaryArtistOnly = false,
|
|
this.filterContributingArtistsInAlbumArtist = false,
|
|
this.historyViewMode = 'grid',
|
|
this.historyFilterMode = 'all',
|
|
this.askQualityBeforeDownload = true,
|
|
this.spotifyClientId = '',
|
|
this.spotifyClientSecret = '',
|
|
this.useCustomSpotifyCredentials = false,
|
|
this.metadataSource = 'deezer',
|
|
this.enableLogging = false,
|
|
this.useExtensionProviders = true,
|
|
this.searchProvider,
|
|
this.separateSingles = false,
|
|
this.albumFolderStructure = 'artist_album',
|
|
this.showExtensionStore = true,
|
|
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',
|
|
this.networkCompatibilityMode = false,
|
|
this.songLinkRegion = 'US',
|
|
this.localLibraryEnabled = false,
|
|
this.localLibraryPath = '',
|
|
this.localLibraryBookmark = '',
|
|
this.localLibraryShowDuplicates = true,
|
|
this.localLibraryAutoScan = 'off',
|
|
this.hasCompletedTutorial = false,
|
|
this.lyricsProviders = const [
|
|
'lrclib',
|
|
'spotify_api',
|
|
'musixmatch',
|
|
'netease',
|
|
'apple_music',
|
|
'qqmusic',
|
|
],
|
|
this.lyricsIncludeTranslationNetease = false,
|
|
this.lyricsIncludeRomanizationNetease = false,
|
|
this.lyricsMultiPersonWordByWord = false,
|
|
this.musixmatchLanguage = '',
|
|
this.lastSeenVersion = '',
|
|
});
|
|
|
|
AppSettings copyWith({
|
|
String? defaultService,
|
|
String? audioQuality,
|
|
String? filenameFormat,
|
|
String? downloadDirectory,
|
|
String? storageMode,
|
|
String? downloadTreeUri,
|
|
bool? autoFallback,
|
|
bool? embedMetadata,
|
|
bool? embedLyrics,
|
|
bool? maxQualityCover,
|
|
bool? isFirstLaunch,
|
|
int? concurrentDownloads,
|
|
bool? checkForUpdates,
|
|
String? updateChannel,
|
|
bool? hasSearchedBefore,
|
|
String? folderOrganization,
|
|
bool? createPlaylistFolder,
|
|
bool? useAlbumArtistForFolders,
|
|
bool? usePrimaryArtistOnly,
|
|
bool? filterContributingArtistsInAlbumArtist,
|
|
String? historyViewMode,
|
|
String? historyFilterMode,
|
|
bool? askQualityBeforeDownload,
|
|
String? spotifyClientId,
|
|
String? spotifyClientSecret,
|
|
bool? useCustomSpotifyCredentials,
|
|
String? metadataSource,
|
|
bool? enableLogging,
|
|
bool? useExtensionProviders,
|
|
String? searchProvider,
|
|
bool clearSearchProvider = false,
|
|
bool? separateSingles,
|
|
String? albumFolderStructure,
|
|
bool? showExtensionStore,
|
|
String? locale,
|
|
String? lyricsMode,
|
|
String? tidalHighFormat,
|
|
int? youtubeOpusBitrate,
|
|
int? youtubeMp3Bitrate,
|
|
bool? useAllFilesAccess,
|
|
bool? autoExportFailedDownloads,
|
|
String? downloadNetworkMode,
|
|
bool? networkCompatibilityMode,
|
|
String? songLinkRegion,
|
|
bool? localLibraryEnabled,
|
|
String? localLibraryPath,
|
|
String? localLibraryBookmark,
|
|
bool? localLibraryShowDuplicates,
|
|
String? localLibraryAutoScan,
|
|
bool? hasCompletedTutorial,
|
|
List<String>? lyricsProviders,
|
|
bool? lyricsIncludeTranslationNetease,
|
|
bool? lyricsIncludeRomanizationNetease,
|
|
bool? lyricsMultiPersonWordByWord,
|
|
String? musixmatchLanguage,
|
|
String? lastSeenVersion,
|
|
}) {
|
|
return AppSettings(
|
|
defaultService: defaultService ?? this.defaultService,
|
|
audioQuality: audioQuality ?? this.audioQuality,
|
|
filenameFormat: filenameFormat ?? this.filenameFormat,
|
|
downloadDirectory: downloadDirectory ?? this.downloadDirectory,
|
|
storageMode: storageMode ?? this.storageMode,
|
|
downloadTreeUri: downloadTreeUri ?? this.downloadTreeUri,
|
|
autoFallback: autoFallback ?? this.autoFallback,
|
|
embedMetadata: embedMetadata ?? this.embedMetadata,
|
|
embedLyrics: embedLyrics ?? this.embedLyrics,
|
|
maxQualityCover: maxQualityCover ?? this.maxQualityCover,
|
|
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
|
|
concurrentDownloads: concurrentDownloads ?? this.concurrentDownloads,
|
|
checkForUpdates: checkForUpdates ?? this.checkForUpdates,
|
|
updateChannel: updateChannel ?? this.updateChannel,
|
|
hasSearchedBefore: hasSearchedBefore ?? this.hasSearchedBefore,
|
|
folderOrganization: folderOrganization ?? this.folderOrganization,
|
|
createPlaylistFolder: createPlaylistFolder ?? this.createPlaylistFolder,
|
|
useAlbumArtistForFolders:
|
|
useAlbumArtistForFolders ?? this.useAlbumArtistForFolders,
|
|
usePrimaryArtistOnly: usePrimaryArtistOnly ?? this.usePrimaryArtistOnly,
|
|
filterContributingArtistsInAlbumArtist:
|
|
filterContributingArtistsInAlbumArtist ??
|
|
this.filterContributingArtistsInAlbumArtist,
|
|
historyViewMode: historyViewMode ?? this.historyViewMode,
|
|
historyFilterMode: historyFilterMode ?? this.historyFilterMode,
|
|
askQualityBeforeDownload:
|
|
askQualityBeforeDownload ?? this.askQualityBeforeDownload,
|
|
spotifyClientId: spotifyClientId ?? this.spotifyClientId,
|
|
spotifyClientSecret: spotifyClientSecret ?? this.spotifyClientSecret,
|
|
useCustomSpotifyCredentials:
|
|
useCustomSpotifyCredentials ?? this.useCustomSpotifyCredentials,
|
|
metadataSource: metadataSource ?? this.metadataSource,
|
|
enableLogging: enableLogging ?? this.enableLogging,
|
|
useExtensionProviders:
|
|
useExtensionProviders ?? this.useExtensionProviders,
|
|
searchProvider: clearSearchProvider
|
|
? null
|
|
: (searchProvider ?? this.searchProvider),
|
|
separateSingles: separateSingles ?? this.separateSingles,
|
|
albumFolderStructure: albumFolderStructure ?? this.albumFolderStructure,
|
|
showExtensionStore: showExtensionStore ?? this.showExtensionStore,
|
|
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,
|
|
downloadNetworkMode: downloadNetworkMode ?? this.downloadNetworkMode,
|
|
networkCompatibilityMode:
|
|
networkCompatibilityMode ?? this.networkCompatibilityMode,
|
|
songLinkRegion: songLinkRegion ?? this.songLinkRegion,
|
|
localLibraryEnabled: localLibraryEnabled ?? this.localLibraryEnabled,
|
|
localLibraryPath: localLibraryPath ?? this.localLibraryPath,
|
|
localLibraryBookmark: localLibraryBookmark ?? this.localLibraryBookmark,
|
|
localLibraryShowDuplicates:
|
|
localLibraryShowDuplicates ?? this.localLibraryShowDuplicates,
|
|
localLibraryAutoScan: localLibraryAutoScan ?? this.localLibraryAutoScan,
|
|
hasCompletedTutorial: hasCompletedTutorial ?? this.hasCompletedTutorial,
|
|
lyricsProviders: lyricsProviders ?? this.lyricsProviders,
|
|
lyricsIncludeTranslationNetease:
|
|
lyricsIncludeTranslationNetease ??
|
|
this.lyricsIncludeTranslationNetease,
|
|
lyricsIncludeRomanizationNetease:
|
|
lyricsIncludeRomanizationNetease ??
|
|
this.lyricsIncludeRomanizationNetease,
|
|
lyricsMultiPersonWordByWord:
|
|
lyricsMultiPersonWordByWord ?? this.lyricsMultiPersonWordByWord,
|
|
musixmatchLanguage: musixmatchLanguage ?? this.musixmatchLanguage,
|
|
lastSeenVersion: lastSeenVersion ?? this.lastSeenVersion,
|
|
);
|
|
}
|
|
|
|
factory AppSettings.fromJson(Map<String, dynamic> json) =>
|
|
_$AppSettingsFromJson(json);
|
|
Map<String, dynamic> toJson() => _$AppSettingsToJson(this);
|
|
}
|