mirror of
https://github.com/spotiflacapp/SpotiFLAC-Mobile.git
synced 2026-06-01 03:15:17 +07:00
feat: add createPlaylistFolder setting for playlist source folder prefix
When enabled, playlist downloads are placed inside a subfolder named after the playlist before the normal folder organization structure (e.g. Playlist/<artist>/<album>/). The setting is a no-op when folder organization is already set to 'By Playlist'. Includes model field, JSON serialization, settings notifier, download queue path logic, UI toggle in download settings, and localizations for all 13 languages.
This commit is contained in:
parent
aca0bbb819
commit
497ba342c0
21 changed files with 380 additions and 36 deletions
|
|
@ -4659,6 +4659,30 @@ abstract class AppLocalizations {
|
|||
/// **'Artist Name Filters'**
|
||||
String get downloadArtistNameFilters;
|
||||
|
||||
/// Setting title for adding a playlist folder prefix before the normal organization structure
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create playlist source folder'**
|
||||
String get downloadCreatePlaylistSourceFolder;
|
||||
|
||||
/// Subtitle when playlist source folder prefix is enabled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Playlist downloads use Playlist/ plus your normal folder structure.'**
|
||||
String get downloadCreatePlaylistSourceFolderEnabled;
|
||||
|
||||
/// Subtitle when playlist source folder prefix is disabled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Playlist downloads use the normal folder structure only.'**
|
||||
String get downloadCreatePlaylistSourceFolderDisabled;
|
||||
|
||||
/// Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'By Playlist already places downloads inside a playlist folder.'**
|
||||
String get downloadCreatePlaylistSourceFolderRedundant;
|
||||
|
||||
/// Setting title for SongLink country region
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -2712,6 +2712,22 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2685,6 +2685,22 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2685,6 +2685,22 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2686,6 +2686,22 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2684,6 +2684,22 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2692,6 +2692,22 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Buat folder sumber playlist';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Unduhan dari playlist memakai Playlist/ lalu struktur folder normal Anda.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Unduhan dari playlist hanya memakai struktur folder normal.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'Mode Berdasarkan Playlist sudah menaruh unduhan ke dalam folder playlist.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2671,6 +2671,22 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2664,6 +2664,22 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2684,6 +2684,22 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2685,6 +2685,22 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2743,6 +2743,22 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2696,6 +2696,22 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -2685,6 +2685,22 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get downloadArtistNameFilters => 'Artist Name Filters';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolder =>
|
||||
'Create playlist source folder';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderEnabled =>
|
||||
'Playlist downloads use Playlist/ plus your normal folder structure.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderDisabled =>
|
||||
'Playlist downloads use the normal folder structure only.';
|
||||
|
||||
@override
|
||||
String get downloadCreatePlaylistSourceFolderRedundant =>
|
||||
'By Playlist already places downloads inside a playlist folder.';
|
||||
|
||||
@override
|
||||
String get downloadSongLinkRegion => 'SongLink Region';
|
||||
|
||||
|
|
|
|||
|
|
@ -3586,6 +3586,22 @@
|
|||
"@downloadArtistNameFilters": {
|
||||
"description": "Setting title for artist folder filter options"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolder": "Create playlist source folder",
|
||||
"@downloadCreatePlaylistSourceFolder": {
|
||||
"description": "Setting title for adding a playlist folder prefix before the normal organization structure"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderEnabled": "Playlist downloads use Playlist/ plus your normal folder structure.",
|
||||
"@downloadCreatePlaylistSourceFolderEnabled": {
|
||||
"description": "Subtitle when playlist source folder prefix is enabled"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderDisabled": "Playlist downloads use the normal folder structure only.",
|
||||
"@downloadCreatePlaylistSourceFolderDisabled": {
|
||||
"description": "Subtitle when playlist source folder prefix is disabled"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderRedundant": "By Playlist already places downloads inside a playlist folder.",
|
||||
"@downloadCreatePlaylistSourceFolderRedundant": {
|
||||
"description": "Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist"
|
||||
},
|
||||
"downloadSongLinkRegion": "SongLink Region",
|
||||
"@downloadSongLinkRegion": {
|
||||
"description": "Setting title for SongLink country region"
|
||||
|
|
|
|||
|
|
@ -1797,6 +1797,22 @@
|
|||
"@downloadUseAlbumArtistForFolders": {
|
||||
"description": "Setting - choose whether artist folders use Album Artist or Track Artist"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolder": "Buat folder sumber playlist",
|
||||
"@downloadCreatePlaylistSourceFolder": {
|
||||
"description": "Setting title for adding a playlist folder prefix before the normal organization structure"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderEnabled": "Unduhan dari playlist memakai Playlist/ lalu struktur folder normal Anda.",
|
||||
"@downloadCreatePlaylistSourceFolderEnabled": {
|
||||
"description": "Subtitle when playlist source folder prefix is enabled"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderDisabled": "Unduhan dari playlist hanya memakai struktur folder normal.",
|
||||
"@downloadCreatePlaylistSourceFolderDisabled": {
|
||||
"description": "Subtitle when playlist source folder prefix is disabled"
|
||||
},
|
||||
"downloadCreatePlaylistSourceFolderRedundant": "Mode Berdasarkan Playlist sudah menaruh unduhan ke dalam folder playlist.",
|
||||
"@downloadCreatePlaylistSourceFolderRedundant": {
|
||||
"description": "Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist"
|
||||
},
|
||||
"downloadUsePrimaryArtistOnly": "Primary artist only for folders",
|
||||
"@downloadUsePrimaryArtistOnly": {
|
||||
"description": "Setting - strip featured artists from folder name"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class AppSettings {
|
|||
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;
|
||||
|
|
@ -96,6 +97,7 @@ class AppSettings {
|
|||
this.updateChannel = 'stable',
|
||||
this.hasSearchedBefore = false,
|
||||
this.folderOrganization = 'none',
|
||||
this.createPlaylistFolder = false,
|
||||
this.useAlbumArtistForFolders = true,
|
||||
this.usePrimaryArtistOnly = false,
|
||||
this.filterContributingArtistsInAlbumArtist = false,
|
||||
|
|
@ -159,6 +161,7 @@ class AppSettings {
|
|||
String? updateChannel,
|
||||
bool? hasSearchedBefore,
|
||||
String? folderOrganization,
|
||||
bool? createPlaylistFolder,
|
||||
bool? useAlbumArtistForFolders,
|
||||
bool? usePrimaryArtistOnly,
|
||||
bool? filterContributingArtistsInAlbumArtist,
|
||||
|
|
@ -215,6 +218,7 @@ class AppSettings {
|
|||
updateChannel: updateChannel ?? this.updateChannel,
|
||||
hasSearchedBefore: hasSearchedBefore ?? this.hasSearchedBefore,
|
||||
folderOrganization: folderOrganization ?? this.folderOrganization,
|
||||
createPlaylistFolder: createPlaylistFolder ?? this.createPlaylistFolder,
|
||||
useAlbumArtistForFolders:
|
||||
useAlbumArtistForFolders ?? this.useAlbumArtistForFolders,
|
||||
usePrimaryArtistOnly: usePrimaryArtistOnly ?? this.usePrimaryArtistOnly,
|
||||
|
|
@ -255,8 +259,7 @@ class AppSettings {
|
|||
localLibraryBookmark: localLibraryBookmark ?? this.localLibraryBookmark,
|
||||
localLibraryShowDuplicates:
|
||||
localLibraryShowDuplicates ?? this.localLibraryShowDuplicates,
|
||||
localLibraryAutoScan:
|
||||
localLibraryAutoScan ?? this.localLibraryAutoScan,
|
||||
localLibraryAutoScan: localLibraryAutoScan ?? this.localLibraryAutoScan,
|
||||
hasCompletedTutorial: hasCompletedTutorial ?? this.hasCompletedTutorial,
|
||||
lyricsProviders: lyricsProviders ?? this.lyricsProviders,
|
||||
lyricsIncludeTranslationNetease:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
|||
updateChannel: json['updateChannel'] as String? ?? 'stable',
|
||||
hasSearchedBefore: json['hasSearchedBefore'] as bool? ?? false,
|
||||
folderOrganization: json['folderOrganization'] as String? ?? 'none',
|
||||
createPlaylistFolder: json['createPlaylistFolder'] as bool? ?? false,
|
||||
useAlbumArtistForFolders: json['useAlbumArtistForFolders'] as bool? ?? true,
|
||||
usePrimaryArtistOnly: json['usePrimaryArtistOnly'] as bool? ?? false,
|
||||
filterContributingArtistsInAlbumArtist:
|
||||
|
|
@ -100,6 +101,7 @@ Map<String, dynamic> _$AppSettingsToJson(
|
|||
'updateChannel': instance.updateChannel,
|
||||
'hasSearchedBefore': instance.hasSearchedBefore,
|
||||
'folderOrganization': instance.folderOrganization,
|
||||
'createPlaylistFolder': instance.createPlaylistFolder,
|
||||
'useAlbumArtistForFolders': instance.useAlbumArtistForFolders,
|
||||
'usePrimaryArtistOnly': instance.usePrimaryArtistOnly,
|
||||
'filterContributingArtistsInAlbumArtist':
|
||||
|
|
|
|||
|
|
@ -1651,12 +1651,23 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
String folderOrganization, {
|
||||
bool separateSingles = false,
|
||||
String albumFolderStructure = 'artist_album',
|
||||
bool createPlaylistFolder = false,
|
||||
bool useAlbumArtistForFolders = true,
|
||||
bool usePrimaryArtistOnly = false,
|
||||
bool filterContributingArtistsInAlbumArtist = false,
|
||||
String? playlistName,
|
||||
}) async {
|
||||
String baseDir = state.outputDir;
|
||||
if (createPlaylistFolder &&
|
||||
folderOrganization != 'playlist' &&
|
||||
playlistName != null &&
|
||||
playlistName.isNotEmpty) {
|
||||
final playlistFolder = _sanitizeFolderName(playlistName);
|
||||
if (playlistFolder.isNotEmpty) {
|
||||
baseDir = '$baseDir${Platform.pathSeparator}$playlistFolder';
|
||||
await _ensureDirExists(baseDir, label: 'Playlist folder');
|
||||
}
|
||||
}
|
||||
final normalizedAlbumArtist = normalizeOptionalString(track.albumArtist);
|
||||
var folderArtist = useAlbumArtistForFolders
|
||||
? normalizedAlbumArtist ?? track.artistName
|
||||
|
|
@ -1809,11 +1820,19 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
String folderOrganization, {
|
||||
bool separateSingles = false,
|
||||
String albumFolderStructure = 'artist_album',
|
||||
bool createPlaylistFolder = false,
|
||||
bool useAlbumArtistForFolders = true,
|
||||
bool usePrimaryArtistOnly = false,
|
||||
bool filterContributingArtistsInAlbumArtist = false,
|
||||
String? playlistName,
|
||||
}) async {
|
||||
final playlistPrefix =
|
||||
createPlaylistFolder &&
|
||||
folderOrganization != 'playlist' &&
|
||||
playlistName != null &&
|
||||
playlistName.isNotEmpty
|
||||
? _sanitizeFolderName(playlistName)
|
||||
: '';
|
||||
final normalizedAlbumArtist = normalizeOptionalString(track.albumArtist);
|
||||
var folderArtist = useAlbumArtistForFolders
|
||||
? normalizedAlbumArtist ?? track.artistName
|
||||
|
|
@ -1833,34 +1852,40 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
|
||||
if (albumFolderStructure == 'artist_album_singles') {
|
||||
if (isSingle) {
|
||||
return '$artistName/Singles';
|
||||
return _joinRelativePath(playlistPrefix, '$artistName/Singles');
|
||||
}
|
||||
final albumName = _sanitizeFolderName(track.albumName);
|
||||
return '$artistName/$albumName';
|
||||
return _joinRelativePath(playlistPrefix, '$artistName/$albumName');
|
||||
}
|
||||
|
||||
if (isSingle) {
|
||||
return 'Singles';
|
||||
return _joinRelativePath(playlistPrefix, 'Singles');
|
||||
}
|
||||
|
||||
final albumName = _sanitizeFolderName(track.albumName);
|
||||
final year = _extractYear(track.releaseDate);
|
||||
switch (albumFolderStructure) {
|
||||
case 'album_only':
|
||||
return 'Albums/$albumName';
|
||||
return _joinRelativePath(playlistPrefix, 'Albums/$albumName');
|
||||
case 'artist_year_album':
|
||||
final yearAlbum = year != null ? '[$year] $albumName' : albumName;
|
||||
return 'Albums/$artistName/$yearAlbum';
|
||||
return _joinRelativePath(
|
||||
playlistPrefix,
|
||||
'Albums/$artistName/$yearAlbum',
|
||||
);
|
||||
case 'year_album':
|
||||
final yearAlbum = year != null ? '[$year] $albumName' : albumName;
|
||||
return 'Albums/$yearAlbum';
|
||||
return _joinRelativePath(playlistPrefix, 'Albums/$yearAlbum');
|
||||
default:
|
||||
return 'Albums/$artistName/$albumName';
|
||||
return _joinRelativePath(
|
||||
playlistPrefix,
|
||||
'Albums/$artistName/$albumName',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (folderOrganization == 'none') {
|
||||
return '';
|
||||
return playlistPrefix;
|
||||
}
|
||||
|
||||
switch (folderOrganization) {
|
||||
|
|
@ -1870,18 +1895,30 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
}
|
||||
return '';
|
||||
case 'artist':
|
||||
return _sanitizeFolderName(folderArtist);
|
||||
return _joinRelativePath(
|
||||
playlistPrefix,
|
||||
_sanitizeFolderName(folderArtist),
|
||||
);
|
||||
case 'album':
|
||||
return _sanitizeFolderName(track.albumName);
|
||||
return _joinRelativePath(
|
||||
playlistPrefix,
|
||||
_sanitizeFolderName(track.albumName),
|
||||
);
|
||||
case 'artist_album':
|
||||
final artistName = _sanitizeFolderName(folderArtist);
|
||||
final albumName = _sanitizeFolderName(track.albumName);
|
||||
return '$artistName/$albumName';
|
||||
return _joinRelativePath(playlistPrefix, '$artistName/$albumName');
|
||||
default:
|
||||
return '';
|
||||
return playlistPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
String _joinRelativePath(String prefix, String suffix) {
|
||||
if (prefix.isEmpty) return suffix;
|
||||
if (suffix.isEmpty) return prefix;
|
||||
return '$prefix/$suffix';
|
||||
}
|
||||
|
||||
String _determineOutputExt(String quality, String service) {
|
||||
if (service.toLowerCase() == 'youtube') {
|
||||
if (quality.toLowerCase().contains('mp3')) {
|
||||
|
|
@ -3547,6 +3584,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
settings.folderOrganization,
|
||||
separateSingles: settings.separateSingles,
|
||||
albumFolderStructure: settings.albumFolderStructure,
|
||||
createPlaylistFolder: settings.createPlaylistFolder,
|
||||
useAlbumArtistForFolders: settings.useAlbumArtistForFolders,
|
||||
usePrimaryArtistOnly: settings.usePrimaryArtistOnly,
|
||||
filterContributingArtistsInAlbumArtist:
|
||||
|
|
@ -3562,6 +3600,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
settings.folderOrganization,
|
||||
separateSingles: settings.separateSingles,
|
||||
albumFolderStructure: settings.albumFolderStructure,
|
||||
createPlaylistFolder: settings.createPlaylistFolder,
|
||||
useAlbumArtistForFolders: settings.useAlbumArtistForFolders,
|
||||
usePrimaryArtistOnly: settings.usePrimaryArtistOnly,
|
||||
filterContributingArtistsInAlbumArtist:
|
||||
|
|
@ -3905,10 +3944,12 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
|||
settings.folderOrganization,
|
||||
separateSingles: settings.separateSingles,
|
||||
albumFolderStructure: settings.albumFolderStructure,
|
||||
createPlaylistFolder: settings.createPlaylistFolder,
|
||||
useAlbumArtistForFolders: settings.useAlbumArtistForFolders,
|
||||
usePrimaryArtistOnly: settings.usePrimaryArtistOnly,
|
||||
filterContributingArtistsInAlbumArtist:
|
||||
settings.filterContributingArtistsInAlbumArtist,
|
||||
playlistName: item.playlistName,
|
||||
);
|
||||
final fallbackResult = await runDownload(
|
||||
useSaf: false,
|
||||
|
|
|
|||
|
|
@ -375,6 +375,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
|||
_saveSettings();
|
||||
}
|
||||
|
||||
void setCreatePlaylistFolder(bool enabled) {
|
||||
state = state.copyWith(createPlaylistFolder: enabled);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setUseAlbumArtistForFolders(bool enabled) {
|
||||
state = state.copyWith(useAlbumArtistForFolders: enabled);
|
||||
_saveSettings();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
|
|||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/settings.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
|
|
@ -436,7 +437,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
],
|
||||
SettingsItem(
|
||||
title: context.l10n.youtubeOpusBitrateTitle,
|
||||
subtitle: '${settings.youtubeOpusBitrate}kbps (128/256/320)',
|
||||
subtitle:
|
||||
'${settings.youtubeOpusBitrate}kbps (128/256/320)',
|
||||
onTap: () => _showYoutubeBitratePicker(
|
||||
context: context,
|
||||
title: context.l10n.youtubeOpusBitrateTitle,
|
||||
|
|
@ -515,8 +517,12 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
icon: Icons.translate_outlined,
|
||||
title: context.l10n.downloadNeteaseIncludeTranslation,
|
||||
subtitle: settings.lyricsIncludeTranslationNetease
|
||||
? context.l10n.downloadNeteaseIncludeTranslationEnabled
|
||||
: context.l10n.downloadNeteaseIncludeTranslationDisabled,
|
||||
? context
|
||||
.l10n
|
||||
.downloadNeteaseIncludeTranslationEnabled
|
||||
: context
|
||||
.l10n
|
||||
.downloadNeteaseIncludeTranslationDisabled,
|
||||
value: settings.lyricsIncludeTranslationNetease,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
|
|
@ -526,8 +532,12 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
icon: Icons.text_fields_outlined,
|
||||
title: context.l10n.downloadNeteaseIncludeRomanization,
|
||||
subtitle: settings.lyricsIncludeRomanizationNetease
|
||||
? context.l10n.downloadNeteaseIncludeRomanizationEnabled
|
||||
: context.l10n.downloadNeteaseIncludeRomanizationDisabled,
|
||||
? context
|
||||
.l10n
|
||||
.downloadNeteaseIncludeRomanizationEnabled
|
||||
: context
|
||||
.l10n
|
||||
.downloadNeteaseIncludeRomanizationDisabled,
|
||||
value: settings.lyricsIncludeRomanizationNetease,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
|
|
@ -627,6 +637,15 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
settings.folderOrganization,
|
||||
),
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.playlist_play_outlined,
|
||||
title: context.l10n.downloadCreatePlaylistSourceFolder,
|
||||
subtitle: _getPlaylistFolderSubtitle(settings),
|
||||
value: settings.createPlaylistFolder,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setCreatePlaylistFolder(value),
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.person_search_outlined,
|
||||
title: context.l10n.downloadUseAlbumArtistForFolders,
|
||||
|
|
@ -642,7 +661,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
.read(settingsProvider.notifier)
|
||||
.setUseAlbumArtistForFolders(value),
|
||||
),
|
||||
SettingsItem(
|
||||
SettingsItem(
|
||||
icon: Icons.filter_alt_outlined,
|
||||
title: context.l10n.downloadArtistNameFilters,
|
||||
subtitle: _getArtistFolderFilterSubtitle(
|
||||
|
|
@ -1407,6 +1426,16 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
}
|
||||
}
|
||||
|
||||
String _getPlaylistFolderSubtitle(AppSettings settings) {
|
||||
if (settings.folderOrganization == 'playlist') {
|
||||
return context.l10n.downloadCreatePlaylistSourceFolderRedundant;
|
||||
}
|
||||
if (settings.createPlaylistFolder) {
|
||||
return context.l10n.downloadCreatePlaylistSourceFolderEnabled;
|
||||
}
|
||||
return context.l10n.downloadCreatePlaylistSourceFolderDisabled;
|
||||
}
|
||||
|
||||
String _getArtistFolderFilterSubtitle(
|
||||
BuildContext context, {
|
||||
required bool usePrimaryArtistOnly,
|
||||
|
|
@ -1776,17 +1805,17 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegion,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegion,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegionDesc,
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegionDesc,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -1847,12 +1876,12 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
context.l10n.downloadFolderOrganization,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
child: Text(
|
||||
context.l10n.downloadFolderOrganization,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
|
|
|
|||
Loading…
Reference in a new issue