diff --git a/Makefile b/Makefile index 25ac3a6d..48626312 100644 --- a/Makefile +++ b/Makefile @@ -45,4 +45,11 @@ gensums: sh -c scripts/gensums.sh migrate: - dart run drift_dev make-migrations \ No newline at end of file + dart run drift_dev make-migrations + +dmg: + flutter build macos &&\ + if [ -f dist/Spotube-macos-universal.dmg ];\ + then rm dist/Spotube-macos-universal.dmg;\ + fi &&\ + appdmg appdmg.json dist/Spotube-macos-universal.dmg \ No newline at end of file diff --git a/lib/hooks/configurators/use_check_yt_dlp_installed.dart b/lib/hooks/configurators/use_check_yt_dlp_installed.dart index df0bae9f..1d948258 100644 --- a/lib/hooks/configurators/use_check_yt_dlp_installed.dart +++ b/lib/hooks/configurators/use_check_yt_dlp_installed.dart @@ -1,9 +1,12 @@ +import 'dart:io'; + import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/modules/settings/youtube_engine_not_installed_dialog.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart'; void useCheckYtDlpInstalled(WidgetRef ref) { @@ -17,8 +20,12 @@ void useCheckYtDlpInstalled(WidgetRef ref) { ), ); + final customPath = + KVStoreService.getYoutubeEnginePath(YoutubeClientEngine.ytDlp); + if (youtubeEngine == YoutubeClientEngine.ytDlp && !await YtDlpEngine.isInstalled() && + (customPath == null || !await File(customPath).exists()) && context.mounted) { await showDialog( context: context, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 60cf300e..98dd5d5f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -418,6 +418,9 @@ "no_logs_found": "No logs found", "youtube_engine": "YouTube Engine", "youtube_engine_not_installed_title": "{engine} is not installed", - "youtube_engine_not_installed_message": "{engine} is not installed in your system.\nPlease install it and make sure it's available in the PATH variable\n\nAfter installing, restart the app", - "download": "Download" + "youtube_engine_not_installed_message": "{engine} is not installed in your system.", + "youtube_engine_set_path": "Make sure it's available in the PATH variable or\nset the absolute path to the {engine} executable below", + "youtube_engine_unix_issue_message": "In macOS/Linux/unix like OS's, setting path on .zshrc/.bashrc/.bash_profile etc. won't work.\nYou need to set the path in the shell configuration file", + "download": "Download", + "file_not_found": "File not found" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index ec16d401..9af53cf2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -84,10 +84,15 @@ Future main(List rawArgs) async { MetadataGod.initialize(); } + await KVStoreService.initialize(); + if (kIsDesktop) { await windowManager.setPreventClose(true); await YtDlp.instance - .setBinaryLocation("yt-dlp${kIsWindows ? '.exe' : ''}") + .setBinaryLocation( + KVStoreService.getYoutubeEnginePath(YoutubeClientEngine.ytDlp) ?? + "yt-dlp${kIsWindows ? '.exe' : ''}", + ) .catchError((e, stack) => null); await FlutterDiscordRPC.initialize(Env.discordAppId); } @@ -96,7 +101,6 @@ Future main(List rawArgs) async { await SMTCWindows.initialize(); } - await KVStoreService.initialize(); await EncryptedKvStoreService.initialize(); final database = AppDatabase(); diff --git a/lib/modules/settings/youtube_engine_not_installed_dialog.dart b/lib/modules/settings/youtube_engine_not_installed_dialog.dart index 7165f56b..bc18bc66 100644 --- a/lib/modules/settings/youtube_engine_not_installed_dialog.dart +++ b/lib/modules/settings/youtube_engine_not_installed_dialog.dart @@ -1,9 +1,18 @@ +import 'dart:io'; + +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/form/text_form_field.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/database/database.dart'; +import 'package:spotube/services/kv_store/kv_store.dart'; +import 'package:spotube/utils/platform.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:yt_dlp_dart/yt_dlp_dart.dart'; const engineDownloadUrls = { YoutubeClientEngine.ytDlp: @@ -19,6 +28,9 @@ class YouTubeEngineNotInstalledDialog extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final controller = useTextEditingController(); + final formKey = useMemoized(() => GlobalKey(), []); + return AlertDialog( title: Row( spacing: 8, @@ -31,33 +43,77 @@ class YouTubeEngineNotInstalledDialog extends HookConsumerWidget { ), ], ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 10, - children: [ - Text( - context.l10n.youtube_engine_not_installed_message(engine.label), - ), - if (engineDownloadUrls[engine] != null) - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text("${context.l10n.download}:"), - Button.link( - child: Text(engineDownloadUrls[engine]!.split("?").first), - onPressed: () async { - launchUrl(Uri.parse(engineDownloadUrls[engine]!)); - }, - ), - ], + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8, + children: [ + Text( + context.l10n.youtube_engine_not_installed_message(engine.label), ), - ], + if (engineDownloadUrls[engine] != null) + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text("${context.l10n.download}:"), + Button.link( + child: Text(engineDownloadUrls[engine]!.split("?").first), + onPressed: () async { + launchUrl(Uri.parse(engineDownloadUrls[engine]!)); + }, + ), + ], + ), + Text(context.l10n.youtube_engine_set_path(engine.label)), + const Gap(8), + FormBuilder( + key: formKey, + child: TextFormBuilderField( + name: "path", + controller: controller, + placeholder: Text(switch (context.theme.platform) { + TargetPlatform.macOS => "e.g. /opt/homebrew/bin/yt-dlp", + TargetPlatform.windows => + r"e.g. C:\Program Files\yt-dlp\yt-dlp.exe", + _ => "e.g. /home/user/.local/bin/yt-dlp", + }), + ), + ), + if (kIsMacOS || kIsLinux) + Text(context.l10n.youtube_engine_unix_issue_message), + ], + ), ), actions: [ + Button.text( + onPressed: () { + if (!context.mounted) return; + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.cancel), + ), Button.secondary( - onPressed: () => Navigator.of(context).pop(), - child: Text(context.l10n.ok), + onPressed: () async { + if (controller.text.isNotEmpty) { + if (!await File(controller.text).exists() && context.mounted) { + formKey.currentState?.fields["path"] + ?.invalidate(context.l10n.file_not_found); + return; + } + await KVStoreService.setYoutubeEnginePath( + engine, + controller.text, + ); + if (engine == YoutubeClientEngine.ytDlp) { + await YtDlp.instance.setBinaryLocation(controller.text); + } + } + if (!context.mounted) return; + Navigator.of(context).pop(true); + }, + child: Text(context.l10n.save), ), ], ); diff --git a/lib/pages/settings/sections/playback.dart b/lib/pages/settings/sections/playback.dart index 7c35a843..451005b1 100644 --- a/lib/pages/settings/sections/playback.dart +++ b/lib/pages/settings/sections/playback.dart @@ -214,12 +214,12 @@ class SettingsPlaybackSection extends HookConsumerWidget { if (value == YoutubeClientEngine.ytDlp && !await YtDlpEngine.isInstalled() && context.mounted) { - await showDialog( + final hasInstalled = await showDialog( context: context, builder: (context) => YouTubeEngineNotInstalledDialog(engine: value), ); - return; + if (hasInstalled != true) return; } preferencesNotifier.setYoutubeClientEngine(value); }, diff --git a/lib/services/kv_store/kv_store.dart b/lib/services/kv_store/kv_store.dart index efe83abf..e334322e 100644 --- a/lib/services/kv_store/kv_store.dart +++ b/lib/services/kv_store/kv_store.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:encrypt/encrypt.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:spotube/models/database/database.dart'; import 'package:spotube/services/wm_tools/wm_tools.dart'; import 'package:uuid/uuid.dart'; @@ -87,4 +88,31 @@ abstract class KVStoreService { sharedPreferences.getBool('hasMigratedToDrift') ?? false; static Future setHasMigratedToDrift(bool value) async => await sharedPreferences.setBool('hasMigratedToDrift', value); + + static Map? get _youtubeEnginePaths { + final jsonRaw = sharedPreferences.getString('ytDlpPath'); + + if (jsonRaw == null) { + return null; + } + + return jsonDecode(jsonRaw); + } + + static String? getYoutubeEnginePath(YoutubeClientEngine engine) { + return _youtubeEnginePaths?[engine.name]; + } + + static Future setYoutubeEnginePath( + YoutubeClientEngine engine, + String path, + ) async { + await sharedPreferences.setString( + 'ytDlpPath', + jsonEncode({ + ...?_youtubeEnginePaths, + engine.name: path, + }), + ); + } } diff --git a/pubspec.lock b/pubspec.lock index 368ec7eb..2d9b48ce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2737,11 +2737,9 @@ packages: yt_dlp_dart: dependency: "direct main" description: - path: "." - ref: "4199bb019542bae361fbb38b3448b3583fbca022" - resolved-ref: "4199bb019542bae361fbb38b3448b3583fbca022" - url: "https://github.com/KRTirtho/yt_dlp_dart.git" - source: git + path: "../yt_dlp_dart" + relative: true + source: path version: "1.0.0" sdks: dart: ">=3.6.1 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index b1e07595..d742b221 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -141,9 +141,7 @@ dependencies: url: https://github.com/Hexer10/youtube_explode_dart.git ref: e519db65ad0b0a40b12f69285932f9db509da3cf yt_dlp_dart: - git: - url: https://github.com/KRTirtho/yt_dlp_dart.git - ref: 4199bb019542bae361fbb38b3448b3583fbca022 + path: ../yt_dlp_dart dev_dependencies: build_runner: ^2.4.13 diff --git a/untranslated_messages.json b/untranslated_messages.json index 1bab9001..0e65d0d3 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -19,7 +19,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "bn": [ @@ -42,7 +45,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ca": [ @@ -65,7 +71,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "cs": [ @@ -88,7 +97,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "de": [ @@ -111,7 +123,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "es": [ @@ -134,7 +149,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "eu": [ @@ -157,7 +175,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "fa": [ @@ -180,7 +201,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "fi": [ @@ -203,7 +227,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "fr": [ @@ -226,7 +253,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "hi": [ @@ -249,7 +279,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "id": [ @@ -272,7 +305,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "it": [ @@ -295,7 +331,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ja": [ @@ -318,7 +357,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ka": [ @@ -341,7 +383,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ko": [ @@ -364,7 +409,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ne": [ @@ -387,7 +435,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "nl": [ @@ -410,7 +461,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "pl": [ @@ -433,7 +487,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "pt": [ @@ -456,7 +513,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "ru": [ @@ -479,7 +539,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "th": [ @@ -502,7 +565,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "tr": [ @@ -525,7 +591,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "uk": [ @@ -548,7 +617,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "vi": [ @@ -571,7 +643,10 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ], "zh": [ @@ -594,6 +669,9 @@ "youtube_engine", "youtube_engine_not_installed_title", "youtube_engine_not_installed_message", - "download" + "youtube_engine_set_path", + "youtube_engine_unix_issue_message", + "download", + "file_not_found" ] } diff --git a/windows/packaging/exe/inno_setup.iss b/windows/packaging/exe/inno_setup.iss index f995d9e9..b6694aaf 100644 --- a/windows/packaging/exe/inno_setup.iss +++ b/windows/packaging/exe/inno_setup.iss @@ -22,8 +22,8 @@ SetupIconFile={{SETUP_ICON_FILE}} WizardStyle=modern WizardSmallImageFile="..\\..\\assets\\spotube-logo.bmp" PrivilegesRequired={{PRIVILEGES_REQUIRED}} -ArchitecturesAllowed=x64 -ArchitecturesInstallIn64BitMode=x64 +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible [Languages] {% for locale in LOCALES %}