From 86b3f20c347fe89171d173a159f876e978fc4a55 Mon Sep 17 00:00:00 2001 From: Santiago Ramirez Date: Sun, 23 Jun 2024 11:29:19 -0400 Subject: [PATCH] Function for interactive login in the browser [BETA] --- .gitignore | 2 + lib/l10n/app_en.arb | 3 +- lib/l10n/app_es.arb | 3 +- lib/modules/desktop_login/login_form.dart | 151 ++++++++++++++++------ pubspec.lock | 14 +- pubspec.yaml | 1 + untranslated_messages.json | 102 ++++++++++++++- 7 files changed, 228 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 4f9ebc28..45fb66da 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ android/key.properties **/pb_data tm.json +.local-chrome +.vs \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 04fc8566..837d7691 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -325,5 +325,6 @@ "connect_client_alert": "You're being controlled by {client}", "this_device": "This Device", "remote": "Remote", - "stats": "Stats" + "stats": "Stats", + "continue_in_browser": "Continue in browser" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 6558c743..10737056 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -325,5 +325,6 @@ "add_library_location": "Añadir a la biblioteca", "remove_library_location": "Eliminar de la biblioteca", "local_tab": "Local", - "stats": "Estadísticas" + "stats": "Estadísticas", + "continue_in_browser": "Continuar en el navegador" } \ No newline at end of file diff --git a/lib/modules/desktop_login/login_form.dart b/lib/modules/desktop_login/login_form.dart index 6091829c..70949f07 100644 --- a/lib/modules/desktop_login/login_form.dart +++ b/lib/modules/desktop_login/login_form.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:puppeteer/protocol/network.dart'; +import 'package:puppeteer/puppeteer.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/authentication_provider.dart'; @@ -16,8 +18,12 @@ class TokenLoginForm extends HookConsumerWidget { Widget build(BuildContext context, ref) { final authenticationNotifier = ref.watch(authenticationProvider.notifier); final directCodeController = useTextEditingController(); + final platform = Theme.of(context).platform; + final isDesktop = + platform == TargetPlatform.linux || platform == TargetPlatform.windows; final isLoading = useState(false); + final showManualConf = useState(false); return ConstrainedBox( constraints: const BoxConstraints( @@ -25,46 +31,115 @@ class TokenLoginForm extends HookConsumerWidget { ), child: Column( children: [ - TextField( - controller: directCodeController, - decoration: InputDecoration( - hintText: context.l10n.spotify_cookie("\"sp_dc\""), - labelText: context.l10n.cookie_name_cookie("sp_dc"), - ), - keyboardType: TextInputType.visiblePassword, - ), const SizedBox(height: 10), - FilledButton( - onPressed: isLoading.value - ? null - : () async { - try { - isLoading.value = true; - if (directCodeController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.fill_in_all_fields), - behavior: SnackBarBehavior.floating, - ), - ); - return; - } - final cookieHeader = - "sp_dc=${directCodeController.text.trim()}"; + Visibility( + visible: showManualConf.value, + child: Column( + children: [ + TextField( + controller: directCodeController, + decoration: InputDecoration( + hintText: context.l10n.spotify_cookie("\"sp_dc\""), + labelText: context.l10n.cookie_name_cookie("sp_dc"), + ), + keyboardType: TextInputType.visiblePassword, + ), + const SizedBox(height: 10), + FilledButton( + onPressed: isLoading.value + ? null + : () async { + try { + isLoading.value = true; + if (directCodeController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text(context.l10n.fill_in_all_fields), + behavior: SnackBarBehavior.floating, + ), + ); + return; + } + final cookieHeader = + "sp_dc=${directCodeController.text.trim()}"; - authenticationNotifier.setCredentials( - await AuthenticationCredentials.fromCookie( - cookieHeader), - ); - if (context.mounted) { - onDone?.call(); - } - } finally { - isLoading.value = false; - } - }, - child: Text(context.l10n.submit), - ) + authenticationNotifier.setCredentials( + await AuthenticationCredentials.fromCookie( + cookieHeader), + ); + if (context.mounted) { + onDone?.call(); + } + } finally { + isLoading.value = false; + } + }, + child: Text(context.l10n.submit), + ) + ], + )), + Visibility( + visible: isDesktop && !showManualConf.value, + child: Column( + children: [ + SizedBox( + width: double.infinity, + height: 48.0, + child: ElevatedButton( + onPressed: isLoading.value + ? null + : () async { + final browser = + await puppeteer.launch(headless: false); + try { + List cookies = []; + final page = await browser.newPage(); + + await page.goto( + 'https://accounts.spotify.com/en/login', + wait: Until.domContentLoaded); + + while (browser.isConnected) { + cookies = await page.cookies(); + for (final cookie in cookies) { + if (cookie.name == "sp_dc") { + await browser.close(); + final cookieHeader = + "sp_dc=${cookie.value.trim()}"; + + authenticationNotifier.setCredentials( + await AuthenticationCredentials + .fromCookie(cookieHeader), + ); + + if (context.mounted) { + onDone?.call(); + } + + return; + } + } + } + } catch (_) { + showManualConf.value = true; + } finally { + isLoading.value = false; + await browser.close(); + } + }, + style: ButtonStyle( + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15.0), + ), + ), + ), + child: Text(context.l10n.continue_in_browser), + ), + ), + ], + )) ], ), ); diff --git a/pubspec.lock b/pubspec.lock index c1866e7d..10123201 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1258,18 +1258,18 @@ packages: dependency: "direct main" description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.7.1" + version: "6.8.0" leak_tracker: dependency: transitive description: @@ -1711,13 +1711,13 @@ packages: source: hosted version: "1.2.3" puppeteer: - dependency: transitive + dependency: "direct main" description: name: puppeteer - sha256: "6833edca01b1e9dcdd9a6e41bad84b706dfba4366d095c4edff64b00c02ac472" + sha256: c45c51b4ad8d70acdffeb1cfb9d16b60a7eaab7bfef314dd5b02c3607269b556 url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.11.0" quiver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index bd1717c8..52401df8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -121,6 +121,7 @@ dependencies: tray_manager: ^0.2.2 http: ^1.2.1 riverpod: ^2.5.1 + puppeteer: ^3.11.0 dev_dependencies: build_runner: ^2.4.11 diff --git a/untranslated_messages.json b/untranslated_messages.json index 9e26dfee..9f60b39e 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1 +1,101 @@ -{} \ No newline at end of file +{ + "ar": [ + "continue_in_browser" + ], + + "bn": [ + "continue_in_browser" + ], + + "ca": [ + "continue_in_browser" + ], + + "cs": [ + "continue_in_browser" + ], + + "de": [ + "continue_in_browser" + ], + + "eu": [ + "continue_in_browser" + ], + + "fa": [ + "continue_in_browser" + ], + + "fi": [ + "continue_in_browser" + ], + + "fr": [ + "continue_in_browser" + ], + + "hi": [ + "continue_in_browser" + ], + + "id": [ + "continue_in_browser" + ], + + "it": [ + "continue_in_browser" + ], + + "ja": [ + "continue_in_browser" + ], + + "ka": [ + "continue_in_browser" + ], + + "ko": [ + "continue_in_browser" + ], + + "ne": [ + "continue_in_browser" + ], + + "nl": [ + "continue_in_browser" + ], + + "pl": [ + "continue_in_browser" + ], + + "pt": [ + "continue_in_browser" + ], + + "ru": [ + "continue_in_browser" + ], + + "th": [ + "continue_in_browser" + ], + + "tr": [ + "continue_in_browser" + ], + + "uk": [ + "continue_in_browser" + ], + + "vi": [ + "continue_in_browser" + ], + + "zh": [ + "continue_in_browser" + ] +}