diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0adb3aa2..138be641 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ android:label="Clipious" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" + android:usesCleartextTraffic="true" android:banner="@drawable/banner"> getDislikes(String videoId) async { - Uri uri = Uri.parse(urlGetDislikes + videoId); + final customUrl = + db.getSettings(returnYoutubeDislikeUrlSettingName)?.value ?? ''; + + Uri uri = Uri.parse((customUrl.trim().isNotEmpty + ? '${customUrl}votes?videoId=' + : urlGetDislikes) + + videoId); final response = await http.get(uri); return Dislike.fromJson(handleResponse(response)); diff --git a/lib/settings/models/db/settings.dart b/lib/settings/models/db/settings.dart index 9e206af7..9f49ba32 100644 --- a/lib/settings/models/db/settings.dart +++ b/lib/settings/models/db/settings.dart @@ -14,6 +14,7 @@ const playerAutoplayOnLoad = 'player-autoplay-on-load'; const playRecommendedNextSettingName = 'play-recommended-next'; const useProxySettingName = 'use-proxy'; const useReturnYoutubeDislikeSettingName = 'use-return-youtube-dislike'; +const returnYoutubeDislikeUrlSettingName = 'custom-return-youtube-dislike-url'; const blackBackgroundSettingName = 'black-background'; const subtitleSizeSettingName = 'subtitles-size'; const rememberLastSubtitle = 'remember-last-subtitle'; diff --git a/lib/settings/states/settings.dart b/lib/settings/states/settings.dart index eb816e9b..5b3bd276 100644 --- a/lib/settings/states/settings.dart +++ b/lib/settings/states/settings.dart @@ -411,6 +411,9 @@ class SettingsCubit extends Cubit { setFullscreenOnRotate(bool b) async => await _set(fullScreenOnLandscapeSettingName, b); + setReturnYoutubeDislikeUrl(String url) async => + await _set(returnYoutubeDislikeUrlSettingName, url); + Future _set(String name, T value) async { var settings = Map.from(state.settings); if (value == null) { @@ -531,6 +534,9 @@ class SettingsState with _$SettingsState { bool get useReturnYoutubeDislike => _get(useReturnYoutubeDislikeSettingName)?.value == 'true'; + String get returnYoutubeDislikeUrl => + _get(returnYoutubeDislikeUrlSettingName)?.value ?? ''; + double get subtitleSize => double.parse(_get(subtitleSizeSettingName)?.value ?? subtitleDefaultSize); diff --git a/lib/settings/views/screens/browsing.dart b/lib/settings/views/screens/browsing.dart index a02cde92..f028e9c0 100644 --- a/lib/settings/views/screens/browsing.dart +++ b/lib/settings/views/screens/browsing.dart @@ -25,6 +25,86 @@ class BrowsingSettingsScreen extends StatelessWidget { content: SizedBox(width: 300, child: AppCustomizer()))); } + customRydInstanceDialog(BuildContext context) async { + SettingsCubit cubit = context.read(); + final controller = + TextEditingController(text: cubit.state.returnYoutubeDislikeUrl); + final formKey = GlobalKey(); + + var locals = AppLocalizations.of(context)!; + String? newUrl = await showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) => Dialog( + child: Form( + key: formKey, + child: SizedBox( + width: 400, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('URL'), + const SizedBox( + height: 10, + ), + Text(locals.rydCustomInstanceDescription), + TextFormField( + validator: (value) { + if (value == null || value.trim().isEmpty) { + return null; + } + final trimmed = value.trim(); + if (!trimmed.startsWith("http://") && + !trimmed.startsWith("https://")) { + return locals.returnYoutubeUrlValidation; + } + + return null; + }, + controller: controller, + autocorrect: false, + enableSuggestions: false, + enableIMEPersonalizedLearning: false, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(locals.cancel), + ), + TextButton( + onPressed: () async { + if (formKey.currentState!.validate()) { + Navigator.of(context).pop(controller.text); + } + }, + child: Text(locals.add), + ), + ], + ), + ], + ), + ), + ), + ), + )); + + if (newUrl != null) { + newUrl = newUrl.trim(); + if (newUrl.isNotEmpty && !newUrl.endsWith("/")) { + newUrl += "/"; + } + + cubit.setReturnYoutubeDislikeUrl(newUrl); + } + } + showSelectLanguage(BuildContext context, SettingsState controller) { var localsList = AppLocalizations.supportedLocales; var localsStrings = @@ -147,13 +227,6 @@ class BrowsingSettingsScreen extends StatelessWidget { cubit.getLocaleDisplayName() ?? locals.followSystem), onPressed: (ctx) => showSelectLanguage(ctx, _), ), - SettingsTile.switchTile( - leading: const Icon(Icons.thumb_down), - title: const Text('Return YouTube Dislike'), - description: Text(locals.returnYoutubeDislikeDescription), - initialValue: _.useReturnYoutubeDislike, - onToggle: cubit.toggleReturnYoutubeDislike, - ), SettingsTile.navigation( leading: const Icon(Icons.manage_search), title: Text(locals.searchHistory), @@ -179,6 +252,23 @@ class BrowsingSettingsScreen extends StatelessWidget { ), ], ), + SettingsSection(title: Text('Return YouTube Dislike'), tiles: [ + SettingsTile.switchTile( + leading: const Icon(Icons.thumb_down), + title: Text(locals.enabled), + description: Text(locals.returnYoutubeDislikeDescription), + initialValue: _.useReturnYoutubeDislike, + onToggle: cubit.toggleReturnYoutubeDislike, + ), + SettingsTile( + enabled: _.useReturnYoutubeDislike, + leading: const Icon(Icons.network_ping), + title: Text(locals.rydCustomInstance), + description: Text( + '${_.returnYoutubeDislikeUrl.isNotEmpty ? '${locals.currentServer(_.returnYoutubeDislikeUrl)}\n' : ''}${locals.rydCustomInstanceDescription}'), + onPressed: (context) => customRydInstanceDialog(context), + ) + ]) ], ), ); diff --git a/lib/settings/views/tv/screens/settings.dart b/lib/settings/views/tv/screens/settings.dart index 6b600e07..0d06d87c 100644 --- a/lib/settings/views/tv/screens/settings.dart +++ b/lib/settings/views/tv/screens/settings.dart @@ -7,6 +7,7 @@ import 'package:invidious/app/states/app.dart'; import 'package:invidious/extensions.dart'; import 'package:invidious/router.dart'; import 'package:invidious/utils.dart'; +import 'package:invidious/utils/views/tv/components/tv_button.dart'; import 'package:invidious/utils/views/tv/components/tv_overscan.dart'; import 'package:locale_names/locale_names.dart'; import 'package:logging/logging.dart'; @@ -107,6 +108,80 @@ class TVSettingsScreen extends StatelessWidget { )); } + showCustomRydUrl(BuildContext context) { + var locals = AppLocalizations.of(context)!; + var cubit = context.read(); + final controller = + TextEditingController(text: cubit.state.returnYoutubeDislikeUrl); + + FocusNode focusNode = FocusNode(); + showTvDialog( + title: 'URL', + context: context, + actions: [ + TvButton( + onPressed: (context) { + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16), + child: Text(locals.cancel), + ), + ), + TvButton( + onPressed: (context) async { + String newUrl = controller.text.trim(); + + if (newUrl.isNotEmpty && + !newUrl.startsWith("http://") && + !newUrl.startsWith("https://")) { + showTvAlertdialog(context, locals.error, + [Text(locals.customizeAppLayoutExplanation)]); + return; + } + + newUrl = newUrl.trim(); + if (newUrl.isNotEmpty && !newUrl.endsWith("/")) { + newUrl += "/"; + } + + cubit.setReturnYoutubeDislikeUrl(newUrl); + Navigator.of(context).pop(); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16), + child: Text(locals.save), + ), + ), + ], + builder: (BuildContext context) => [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + autofocus: true, + focusNode: focusNode, + textInputAction: TextInputAction.next, + controller: controller, + autocorrect: false, + enableSuggestions: false, + enableIMEPersonalizedLearning: false, + ), + const Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [], + ), + ], + ), + ) + ], + ); + } + @override Widget build(BuildContext context) { AppLocalizations locals = AppLocalizations.of(context)!; @@ -146,6 +221,13 @@ class TVSettingsScreen extends StatelessWidget { trailing: Switch( onChanged: (value) {}, value: _.useReturnYoutubeDislike), ), + SettingsTile( + title: locals.rydCustomInstance, + description: + '${_.returnYoutubeDislikeUrl.isNotEmpty ? '${locals.currentServer(_.returnYoutubeDislikeUrl)}\n' : ''}${locals.rydCustomInstanceDescription}', + enabled: _.useReturnYoutubeDislike, + onSelected: (context) => showCustomRydUrl(context), + ), SettingsTile( title: locals.searchHistory, description: locals.searchHistoryDescription,