Skip to content

Commit

Permalink
feat: let users select the downloads directory (#953)
Browse files Browse the repository at this point in the history
Fixes #928
  • Loading branch information
Feichtmeier authored Oct 14, 2024
1 parent 498f32e commit 9d8063f
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 32 deletions.
1 change: 1 addition & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ const kDownloads = 'downloads.json';
const kFeedsWithDownloads = 'feedswithdownloads.json';
const kCoverStore = 'coverStore.json';
const kDirectoryProperty = 'directory';
const kDownloadsCustomDir = 'downloadsCustomDir';
const kRadioUrl = 'de1.api.radio-browser.info';
const kRadioBrowserBaseUrl = 'all.api.radio-browser.info';
const kLastAudio = 'lastAudio';
Expand Down
1 change: 1 addition & 0 deletions lib/extensions/build_context_x.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import '../constants.dart';
extension BuildContextX on BuildContext {
ThemeData get theme => Theme.of(this);
ColorScheme get colorScheme => theme.colorScheme;
TextTheme get textTheme => theme.textTheme;

Size get mediaQuerySize => MediaQuery.sizeOf(this);

Expand Down
3 changes: 3 additions & 0 deletions lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@
}
},
"downloadsOnly": "Nur Downloads",
"downloadsDirectory": "Ort der Downloads",
"downloadsDirectoryDescription": "Stell sicher dass MusicPod Zugriff auf diesen Ort hat",
"downloadsChangeWarning": "Das Ändern des Download-Orts löscht alle bisherigen Downloads. Möchtest du fortfahren?",
"moreOptions": "Mehr Optionen",
"noRadioServerFound": "Es wurde kein Radio-Server gefunden",
"connectedTo": "Verbunden mit",
Expand Down
3 changes: 3 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@
}
},
"downloadsOnly": "Downloads only",
"downloadsDirectory": "Location of your downloads",
"downloadsDirectoryDescription": "Make sure MusicPod can access this directory!",
"downloadsChangeWarning": "Changing the downloads directory deletes all current downloads. Do you want to proceed?",
"moreOptions": "More options",
"noRadioServerFound": "No radio server found",
"connectedTo": "Connected to",
Expand Down
2 changes: 0 additions & 2 deletions lib/library/library_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,6 @@ class LibraryModel extends SafeChangeNotifier {
void removePodcastUpdate(String feedUrl) =>
_service.removePodcastUpdate(feedUrl);

String? get downloadsDir => _service.downloadsDir;

int get downloadsLength => _service.downloads.length;

String? getDownload(String? url) =>
Expand Down
42 changes: 28 additions & 14 deletions lib/library/library_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,17 @@ class LibraryService {
}

void removeDownload({required String url, required String feedUrl}) {
_deleteDownload(url);

if (_downloads.containsKey(url)) {
_downloads.remove(url);
_feedsWithDownloads.remove(feedUrl);

_updateDownloads();
}
}

void _deleteDownload(String url) {
final path = _downloads[url];

if (path != null) {
Expand All @@ -328,20 +339,26 @@ class LibraryService {
file.deleteSync();
}
}
}

if (_downloads.containsKey(url)) {
_downloads.remove(url);
_feedsWithDownloads.remove(feedUrl);
void _updateDownloads() {
writeStringMap(_downloads, kDownloads)
.then(
(_) => writeStringIterable(
iterable: _feedsWithDownloads,
filename: kFeedsWithDownloads,
),
)
.then((_) => _propertiesChangedController.add(true));
}

writeStringMap(_downloads, kDownloads)
.then(
(_) => writeStringIterable(
iterable: _feedsWithDownloads,
filename: kFeedsWithDownloads,
),
)
.then((_) => _propertiesChangedController.add(true));
void removeAllDownloads() {
for (var download in _downloads.entries) {
_deleteDownload(download.key);
}
_downloads.clear();
_feedsWithDownloads.clear();
_updateDownloads();
}

void _removeFeedWithDownload(String feedUrl) {
Expand All @@ -353,8 +370,6 @@ class LibraryService {
).then((_) => _propertiesChangedController.add(true));
}

String? _downloadsDir;
String? get downloadsDir => _downloadsDir;
Map<String, List<Audio>> _podcasts = {};
Map<String, List<Audio>> get podcasts => _podcasts;
int get podcastsLength => _podcasts.length;
Expand Down Expand Up @@ -479,7 +494,6 @@ class LibraryService {
(await readAudioMap(kLikedAudiosFileName)).entries.firstOrNull?.value ??
<Audio>[];

_downloadsDir = await getDownloadsDir();
_downloads = await readStringMap(kDownloads);
_feedsWithDownloads = Set.from(
await readStringIterable(filename: kFeedsWithDownloads) ?? <String>{},
Expand Down
11 changes: 10 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'library/library_service.dart';
import 'local_audio/local_audio_model.dart';
import 'local_audio/local_audio_service.dart';
import 'notifications/notifications_service.dart';
import 'persistence_utils.dart';
import 'player/player_model.dart';
import 'player/player_service.dart';
import 'podcasts/download_model.dart';
Expand Down Expand Up @@ -72,11 +73,14 @@ Future<void> main(List<String> args) async {
);
}

final downloadsDefaultDir = await getDownloadsDefaultDir();

registerServicesAndViewModels(
sharedPreferences: sharedPreferences,
args: args,
version: version,
enableDiscord: enableDiscord,
downloadsDefaultDir: downloadsDefaultDir,
);

runApp(
Expand All @@ -87,6 +91,7 @@ Future<void> main(List<String> args) async {
}

void registerServicesAndViewModels({
required String? downloadsDefaultDir,
required SharedPreferences sharedPreferences,
required List<String> args,
required String version,
Expand Down Expand Up @@ -122,7 +127,10 @@ void registerServicesAndViewModels({
dispose: (s) async => s.dispose(),
)
..registerLazySingleton<SettingsService>(
() => SettingsService(sharedPreferences: di<SharedPreferences>()),
() => SettingsService(
sharedPreferences: di<SharedPreferences>(),
downloadsDefaultDir: downloadsDefaultDir,
),
dispose: (s) async => s.dispose(),
)
..registerLazySingleton<LibraryService>(
Expand Down Expand Up @@ -206,6 +214,7 @@ void registerServicesAndViewModels({
)
..registerLazySingleton<DownloadModel>(
() => DownloadModel(
settingsService: di<SettingsService>(),
libraryService: di<LibraryService>(),
dio: di<Dio>(),
),
Expand Down
2 changes: 1 addition & 1 deletion lib/persistence_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Future<String?> getMusicDir() async {
return null;
}

Future<String?> getDownloadsDir() async {
Future<String?> getDownloadsDefaultDir() async {
String? path;
if (Platform.isLinux) {
path = getUserDirectory('DOWNLOAD')?.path;
Expand Down
29 changes: 23 additions & 6 deletions lib/podcasts/download_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ import 'package:safe_change_notifier/safe_change_notifier.dart';
import '../common/data/audio.dart';
import '../l10n/l10n.dart';
import '../library/library_service.dart';
import '../settings/settings_service.dart';

class DownloadModel extends SafeChangeNotifier {
DownloadModel({
required LibraryService libraryService,
required SettingsService settingsService,
required Dio dio,
}) : _service = libraryService,
}) : _libraryService = libraryService,
_settingsService = settingsService,
_dio = dio;

final LibraryService _service;
final LibraryService _libraryService;
final SettingsService _settingsService;
final Dio _dio;

final _values = <String, double?>{};
Expand All @@ -42,9 +46,9 @@ class DownloadModel extends SafeChangeNotifier {
required Audio? audio,
}) async {
if (audio?.url != null &&
_service.downloadsDir != null &&
_settingsService.downloadsDir != null &&
audio?.website != null) {
_service.removeDownload(url: audio!.url!, feedUrl: audio.website!);
_libraryService.removeDownload(url: audio!.url!, feedUrl: audio.website!);
if (_values.containsKey(audio.url)) {
_values.update(audio.url!, (value) => null);
}
Expand All @@ -53,11 +57,20 @@ class DownloadModel extends SafeChangeNotifier {
}
}

Future<void> deleteAllDownloads() async {
if (_settingsService.downloadsDir != null) {
_libraryService.removeAllDownloads();
_values.clear;

notifyListeners();
}
}

Future<void> startDownload({
required BuildContext context,
required Audio? audio,
}) async {
final downloadsDir = _service.downloadsDir;
final downloadsDir = _settingsService.downloadsDir;
if (audio?.url == null || downloadsDir == null) return;
final url = audio!.url!;

Expand Down Expand Up @@ -86,7 +99,11 @@ class DownloadModel extends SafeChangeNotifier {
name: audio.title ?? '',
).then((response) {
if (response?.statusCode == 200 && audio.website != null) {
_service.addDownload(url: url, path: path, feedUrl: audio.website!);
_libraryService.addDownload(
url: url,
path: path,
feedUrl: audio.website!,
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
Expand Down
4 changes: 3 additions & 1 deletion lib/podcasts/view/download_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import '../../common/view/progress.dart';
import '../../extensions/build_context_x.dart';
import '../../l10n/l10n.dart';
import '../../library/library_model.dart';
import '../../settings/settings_model.dart';
import '../download_model.dart';

class DownloadButton extends StatelessWidget with WatchItMixin {
Expand All @@ -31,7 +32,8 @@ class DownloadButton extends StatelessWidget with WatchItMixin {
final download = watchPropertyValue(
(LibraryModel m) => m.getDownload(audio?.url) != null,
);
final downloadsDir = watchPropertyValue((LibraryModel m) => m.downloadsDir);
final downloadsDir =
watchPropertyValue((SettingsModel m) => m.downloadsDir);

return Stack(
alignment: Alignment.center,
Expand Down
33 changes: 33 additions & 0 deletions lib/settings/settings_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as p;

import 'package:github/github.dart';
import 'package:safe_change_notifier/safe_change_notifier.dart';
Expand All @@ -23,6 +25,37 @@ class SettingsModel extends SafeChangeNotifier {
String? get directory => _service.directory;
Future<void> setDirectory(String value) async => _service.setDirectory(value);

String? get downloadsDir => _service.downloadsDir;
Future<void> setDownloadsCustomDir({
required Function() onSuccess,
required Function(String e) onFail,
}) async {
String? dirError;
String? directoryPath;

try {
directoryPath = await getPathOfDirectory();
if (directoryPath == null) return;
final maybeDir = Directory(directoryPath);
if (!maybeDir.existsSync()) return;
maybeDir.statSync();
File(p.join(directoryPath, 'test'))
..createSync()
..deleteSync();
} catch (e) {
dirError = e.toString();
}

if (dirError != null) {
onFail(dirError);
} else {
if (directoryPath != null) {
await _service.setDownloadsCustomDir(directoryPath);
onSuccess();
}
}
}

bool get neverShowFailedImports => _service.neverShowFailedImports;
void setNeverShowFailedImports(bool value) =>
_service.setNeverShowFailedImports(value);
Expand Down
16 changes: 14 additions & 2 deletions lib/settings/settings_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import '../common/data/close_btn_action.dart';
import '../constants.dart';

class SettingsService {
SettingsService({required SharedPreferences sharedPreferences})
: _preferences = sharedPreferences;
SettingsService({
required String? downloadsDefaultDir,
required SharedPreferences sharedPreferences,
}) : _preferences = sharedPreferences,
_downloadsDefaultDir = downloadsDefaultDir;

final String? _downloadsDefaultDir;
final SharedPreferences _preferences;
final _propertiesChangedController = StreamController<bool>.broadcast();
Stream<bool> get propertiesChanged => _propertiesChangedController.stream;
Expand Down Expand Up @@ -82,6 +86,14 @@ class SettingsService {
});
}

String? get downloadsDir =>
_preferences.getString(kDownloadsCustomDir) ?? _downloadsDefaultDir;
Future<void> setDownloadsCustomDir(String directory) async {
await _preferences.setString(kDownloadsCustomDir, directory).then((saved) {
if (saved) _propertiesChangedController.add(true);
});
}

CloseBtnAction get closeBtnActionIndex =>
_preferences.getString(kCloseBtnAction) == null
? CloseBtnAction.alwaysAsk
Expand Down
Loading

0 comments on commit 9d8063f

Please sign in to comment.