diff --git a/fastlane/metadata/android/en-US/changelogs/264.txt b/fastlane/metadata/android/en-US/changelogs/264.txt new file mode 100644 index 00000000..2ca63d74 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/264.txt @@ -0,0 +1,3 @@ +Add Dislike count to vidoes (opt-in in options) +Fix search for languages like russion +No need to restart the app after changing theme settings anymore \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png index 1466292b..a8af2c2e 100644 Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/lib/controllers/appController.dart b/lib/controllers/appController.dart new file mode 100644 index 00000000..08a48453 --- /dev/null +++ b/lib/controllers/appController.dart @@ -0,0 +1,6 @@ +import 'package:get/get.dart'; +import 'package:invidious/utils.dart'; + +class AppController extends GetxController { + static AppController? to() => safeGet(); +} diff --git a/lib/controllers/playerController.dart b/lib/controllers/playerController.dart index fd99f0e2..8aaa1159 100644 --- a/lib/controllers/playerController.dart +++ b/lib/controllers/playerController.dart @@ -189,9 +189,9 @@ class PlayerController extends GetxController { bool isHls = video.hlsUrl != null; String videoUrl = isHls - ? '${video.hlsUrl!}${service.useProxy ? '?local=true' : ''}' + ? '${video.hlsUrl!}${service.useProxy() ? '?local=true' : ''}' : useDash - ? '${video.dashUrl}${service.useProxy ? '?local=true' : ''}' + ? '${video.dashUrl}${service.useProxy() ? '?local=true' : ''}' : video.formatStreams[video.formatStreams.length - 1].url; log.info('Playing url (dash ${useDash}, hasHls ? ${video.hlsUrl != null}) ${videoUrl}'); diff --git a/lib/controllers/settingsController.dart b/lib/controllers/settingsController.dart index d62bc499..92b0a5a2 100644 --- a/lib/controllers/settingsController.dart +++ b/lib/controllers/settingsController.dart @@ -12,6 +12,7 @@ import '../models/country.dart'; import '../models/db/server.dart'; import '../models/db/settings.dart'; import '../utils.dart'; +import 'appController.dart'; const String subtitleDefaultSize = '14'; @@ -50,6 +51,7 @@ class SettingsController extends GetxController { db.saveSetting(SettingsValue(DYNAMIC_THEME, value.toString())); useDynamicTheme = value; update(); + updateApp(); } toggleDash(bool value) { @@ -106,6 +108,7 @@ class SettingsController extends GetxController { db.saveSetting(SettingsValue(BLACK_BACKGROUND, value.toString())); blackBackground = value; update(); + AppController.to()?.update(); } changeSubtitleSize({required bool increase}) { @@ -137,6 +140,7 @@ class SettingsController extends GetxController { db.saveSetting(SettingsValue(THEME_MODE, theme.name)); } update(); + updateApp(); } setLocale(List locals, List localStrings, String? locale) { @@ -157,6 +161,11 @@ class SettingsController extends GetxController { db.saveSetting(SettingsValue(LOCALE, toSave)); } update(); + updateApp(); + } + + updateApp(){ + AppController.to()?.update(); } String? getLocaleDisplayName() { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index df0f297c..a8a4f5e0 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -319,7 +319,7 @@ "@useDynamicTheme": { "description": "" }, - "useDynamicThemeDescription": "Use Material You colors (only available on Android 12+). Requires app restart", + "useDynamicThemeDescription": "Use Material You colors (only available on Android 12+)", "@useDynamicThemeDescription": { "description": "" }, @@ -473,7 +473,7 @@ "@useProxy": { "description": "label for settings switch to proxy videos from server" }, - "useProxyDescription": "By proxying video streams from the server, you can bypass regional blocks or ISP blocking YouTube. Requires app restart", + "useProxyDescription": "By proxying video streams from the server, you can bypass regional blocks or ISP blocking YouTube", "@useProxyDescription": { "description": "Description for the use proxy settings" }, @@ -501,7 +501,7 @@ "@blackBackground": { "description": "Settings name for black background" }, - "blackBackgroundDescription": "For dark theme on OLED screen. Requires app restart.", + "blackBackgroundDescription": "For dark theme on OLED screen", "@blackBackgroundDescription": { "description": "Description for dark background setting" }, diff --git a/lib/main.dart b/lib/main.dart index 9d091d3c..701f8d64 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,6 +25,7 @@ import 'package:invidious/views/tv/tvWelcomeWizard.dart'; import 'package:invidious/views/welcomeWizard.dart'; import 'package:logging/logging.dart'; +import 'controllers/appController.dart'; import 'database.dart'; import 'myRouteObserver.dart'; @@ -56,7 +57,6 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { bool showWizard = false; - bool useDynamicTheme = db.getSettings(DYNAMIC_THEME)?.value == 'true'; try { db.getCurrentlySelectedServer(); } catch (err) { @@ -64,84 +64,91 @@ class MyApp extends StatelessWidget { } // TODO: implement build - return DynamicColorBuilder(builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { - ColorScheme lightColorScheme; - ColorScheme darkColorScheme; + return GetBuilder( + init: AppController(), + builder: (_) { - if (useDynamicTheme && lightDynamic != null && darkDynamic != null) { - // On Android S+ devices, use the provided dynamic color scheme. - // (Recommended) Harmonize the dynamic color scheme' built-in semantic colors. - lightColorScheme = lightDynamic.harmonized(); - // (Optional) Customize the scheme as desired. For example, one might - // want to use a brand color to override the dynamic [ColorScheme.secondary]. - // lightColorScheme = lightColorScheme.copyWith(secondary: brandColor); - // (Optional) If applicable, harmonize custom colors. - // lightCustomColors = lightCustomColors.harmonized(lightColorScheme); + bool useDynamicTheme = db.getSettings(DYNAMIC_THEME)?.value == 'true'; - // Repeat for the dark color scheme. - darkColorScheme = darkDynamic.harmonized(); - // darkColorScheme = darkColorScheme.copyWith(secondary: brandColor); - // darkCustomColors = darkCustomColors.harmonized(darkColorScheme); + return DynamicColorBuilder(builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { + ColorScheme lightColorScheme; + ColorScheme darkColorScheme; - // _isDemoUsingDynamicColors = true; // ignore, only for demo purposes - } else { - // Otherwise, use fallback schemes. - lightColorScheme = ColorScheme.fromSeed( - seedColor: brandColor, - ); - darkColorScheme = ColorScheme.fromSeed( - seedColor: brandColor, - brightness: Brightness.dark, - ); - } + if (useDynamicTheme && lightDynamic != null && darkDynamic != null) { + // On Android S+ devices, use the provided dynamic color scheme. + // (Recommended) Harmonize the dynamic color scheme' built-in semantic colors. + lightColorScheme = lightDynamic.harmonized(); + // (Optional) Customize the scheme as desired. For example, one might + // want to use a brand color to override the dynamic [ColorScheme.secondary]. + // lightColorScheme = lightColorScheme.copyWith(secondary: brandColor); + // (Optional) If applicable, harmonize custom colors. + // lightCustomColors = lightCustomColors.harmonized(lightColorScheme); - if (db.getSettings(BLACK_BACKGROUND)?.value == 'true') { - darkColorScheme = darkColorScheme.copyWith(background: Colors.black); - } + // Repeat for the dark color scheme. + darkColorScheme = darkDynamic.harmonized(); + // darkColorScheme = darkColorScheme.copyWith(secondary: brandColor); + // darkCustomColors = darkCustomColors.harmonized(darkColorScheme); - List? localeString = db.getSettings(LOCALE)?.value.split('_'); - Locale? locale = localeString != null ? Locale.fromSubtags(languageCode: localeString[0], scriptCode: localeString.length >= 2 ? localeString[1] : null) : null; + // _isDemoUsingDynamicColors = true; // ignore, only for demo purposes + } else { + // Otherwise, use fallback schemes. + lightColorScheme = ColorScheme.fromSeed( + seedColor: brandColor, + ); + darkColorScheme = ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.dark, + ); + } - return GetMaterialApp( - locale: locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - scaffoldMessengerKey: scaffoldKey, - navigatorKey: globalNavigator, - debugShowCheckedModeBanner: false, - themeMode: ThemeMode.values.firstWhere((element) => element.name == db.getSettings(THEME_MODE)?.value, orElse: () => ThemeMode.system), - title: 'Clipious', - theme: ThemeData( - useMaterial3: true, - colorScheme: lightColorScheme, - ), - darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme), - home: Shortcuts( - shortcuts: { - LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), - }, - child: isTv - ? showWizard - ? const TvWelcomeWizard() - : const TvHome() - : Stack( - children: [ - MiniPlayerAware( - child: Navigator( - observers: [MyRouteObserver()], - key: navigatorKey, - initialRoute: '/', - onGenerateRoute: (settings) { - if (settings.name == '/') { - return GetPageRoute(page: () => showWizard ? const WelcomeWizard() : const Home()); - } - }), - ), - const MiniPlayer() - ], - ), - )); - }); + if (db.getSettings(BLACK_BACKGROUND)?.value == 'true') { + darkColorScheme = darkColorScheme.copyWith(background: Colors.black); + } + + List? localeString = db.getSettings(LOCALE)?.value.split('_'); + Locale? locale = localeString != null ? Locale.fromSubtags(languageCode: localeString[0], scriptCode: localeString.length >= 2 ? localeString[1] : null) : null; + + return GetMaterialApp( + locale: locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + scaffoldMessengerKey: scaffoldKey, + navigatorKey: globalNavigator, + debugShowCheckedModeBanner: false, + themeMode: ThemeMode.values.firstWhere((element) => element.name == db.getSettings(THEME_MODE)?.value, orElse: () => ThemeMode.system), + title: 'Clipious', + theme: ThemeData( + useMaterial3: true, + colorScheme: lightColorScheme, + ), + darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme), + home: Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), + }, + child: isTv + ? showWizard + ? const TvWelcomeWizard() + : const TvHome() + : Stack( + children: [ + MiniPlayerAware( + child: Navigator( + observers: [MyRouteObserver()], + key: navigatorKey, + initialRoute: '/', + onGenerateRoute: (settings) { + if (settings.name == '/') { + return GetPageRoute(page: () => showWizard ? const WelcomeWizard() : const Home()); + } + }), + ), + const MiniPlayer() + ], + ), + )); + }); + }); } } diff --git a/lib/service.dart b/lib/service.dart index 7f3f9134..a320dc54 100644 --- a/lib/service.dart +++ b/lib/service.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'package:fbroadcast/fbroadcast.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_web_auth/flutter_web_auth.dart'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; @@ -16,7 +14,6 @@ import 'package:invidious/models/sponsorSegment.dart'; import 'package:invidious/models/userFeed.dart'; import 'package:invidious/models/video.dart'; import 'package:invidious/models/videoInList.dart'; -import 'package:invidious/views/subscriptions.dart'; import 'package:logging/logging.dart'; import 'models/channel.dart'; @@ -57,7 +54,6 @@ const MAX_PING = 9007199254740991; class Service { final log = Logger('Service'); - final bool useProxy = db.getSettings(USE_PROXY)?.value == 'true'; handleResponse(Response response) { var body = utf8.decode(response.bodyBytes); @@ -83,17 +79,19 @@ class Service { } } + bool useProxy() { + return db.getSettings(USE_PROXY)?.value == 'true'; + } + Uri buildUrl(String baseUrl, {Map? pathParams, Map? query}) { try { - String url = '${db - .getCurrentlySelectedServer() - .url}$baseUrl'; + String url = '${db.getCurrentlySelectedServer().url}$baseUrl'; pathParams?.forEach((key, value) { url = url.replaceAll(key, value); }); - if (useProxy) { + if (useProxy()) { query ??= {}; query['local'] = 'true'; } @@ -152,9 +150,7 @@ class Service { String url = '$serverUrl/authorize_token?scopes=:feed,:subscriptions*,:playlists*&callback_url=clipious-auth://'; final result = await FlutterWebAuth.authenticate(url: url, callbackUrlScheme: 'clipious-auth'); - final token = Uri - .parse(result) - .queryParameters['token']; + final token = Uri.parse(result).queryParameters['token']; Server? server = db.getServer(serverUrl); @@ -183,9 +179,7 @@ class Service { } Future> getTrending({String? type}) async { - String countryCode = db - .getSettings(BROWSING_COUNTRY) - ?.value ?? 'US'; + String countryCode = db.getSettings(BROWSING_COUNTRY)?.value ?? 'US'; // parse.queryParameters['region'] = countryCode; Map? query = {'region': countryCode}; @@ -251,7 +245,6 @@ class Service { url += '&categories=[${categories.map((e) => '"${e.name}"').join(",")}]'; } - log.info('Calling $url'); final response = await http.get(Uri.parse(url)); Iterable i = handleResponse(response); @@ -430,18 +423,14 @@ class Service { } Future pingServer(String url) async { - int start = DateTime - .now() - .millisecondsSinceEpoch; + int start = DateTime.now().millisecondsSinceEpoch; String fullUri = '$url${STATS}'; log.info('calling ${fullUri}'); final response = await http.get(Uri.parse(fullUri), headers: {'Content-Type': 'application/json; charset=utf-16'}); try { handleResponse(response); - var diff = DateTime - .now() - .millisecondsSinceEpoch - start; + var diff = DateTime.now().millisecondsSinceEpoch - start; return Duration(milliseconds: diff); } catch (err) { log.info(err); diff --git a/lib/views/settings.dart b/lib/views/settings.dart index cca3418d..82cd9ee8 100644 --- a/lib/views/settings.dart +++ b/lib/views/settings.dart @@ -152,7 +152,7 @@ class Settings extends StatelessWidget { ), SettingsTile( title: Text(locals.appLanguage), - value: Text('${_.getLocaleDisplayName() ?? locals.followSystem}. ${locals.requiresRestart}'), + value: Text(_.getLocaleDisplayName() ?? locals.followSystem), onPressed: (context) => showSelectLanguage(context, _), ), SettingsTile.switchTile( @@ -218,7 +218,7 @@ class Settings extends StatelessWidget { ), SettingsTile( title: Text(locals.themeBrightness), - value: Text('${_.getThemeLabel(locals, _.themeMode)}. ${locals.requiresRestart}'), + value: Text(_.getThemeLabel(locals, _.themeMode)), onPressed: (context) => selectTheme(context, _), ), SettingsTile.switchTile( diff --git a/lib/views/tv/tvSettings.dart b/lib/views/tv/tvSettings.dart index ff77b831..0e562a50 100644 --- a/lib/views/tv/tvSettings.dart +++ b/lib/views/tv/tvSettings.dart @@ -128,7 +128,7 @@ class TVSettings extends StatelessWidget { ), SettingsTile( title: locals.appLanguage, - description: '${_.getLocaleDisplayName() ?? locals.followSystem}. ${locals.requiresRestart}', + description: _.getLocaleDisplayName() ?? locals.followSystem, onSelected: (context) => showSelectLanguage(context, _), ), SettingsTile( @@ -204,7 +204,7 @@ class TVSettings extends StatelessWidget { SettingsTitle(title: locals.appearance), SettingsTile( title: locals.themeBrightness, - description: '${_.getThemeLabel(locals, _.themeMode)}. ${locals.requiresRestart}', + description: _.getThemeLabel(locals, _.themeMode), onSelected: (context) => selectTheme(context, _), ), SettingsTile( diff --git a/pubspec.yaml b/pubspec.yaml index 1a013e72..ad44e452 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.7.17+264 +version: 1.7.16+264 environment: sdk: '>=2.19.1 <3.0.0'