From 7e1005dc62c99a6f0bd569fc8f293b334ba721f4 Mon Sep 17 00:00:00 2001 From: franchioping <43936644+franchioping@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:08:51 +0000 Subject: [PATCH 01/46] docs: Update dev branch in CONTRIBUTION.md (#977) change development branch from main to dev --- CONTRIBUTION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index b2823e62..13996cea 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -145,7 +145,7 @@ Do the following: flutter run -d )> ``` -Do debugging/testing/build etc then submit to us with PR against the development branch (master) & we'll review your code +Do debugging/testing/build etc then submit to us with PR against the development branch (dev) & we'll review your code ### Submit Translations @@ -163,4 +163,4 @@ Make sure you're familiar with [Flutter localization](https://docs.flutter.dev/u - Now restart (hot restart if running already) the app in debug mode & go to "Settings" > "Language" & see if your locale shows up - If it does, select it & see if the app is translated properly - Now git commit the changes & push -- Finally, submit a PR against the development branch (dev) & we'll review your code \ No newline at end of file +- Finally, submit a PR against the development branch (dev) & we'll review your code From 02e44fc6b849a873adad382f5d46ed8caf32359f Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Tue, 2 Jan 2024 17:21:12 +0100 Subject: [PATCH 02/46] fix: Black window flash when starting the app (#1003) --- lib/main.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 052e6809..f96920a1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -58,15 +58,6 @@ Future main(List rawArgs) async { await DesktopTools.window.setPreventClose(true); } - await DesktopTools.ensureInitialized( - DesktopWindowOptions( - hideTitleBar: true, - title: "Spotube", - backgroundColor: Colors.transparent, - minimumSize: const Size(300, 700), - ), - ); - await SystemTheme.accentColor.load(); if (!kIsWeb) { @@ -107,6 +98,15 @@ Future main(List rawArgs) async { path: hiveCacheDir, ); + await DesktopTools.ensureInitialized( + DesktopWindowOptions( + hideTitleBar: true, + title: "Spotube", + backgroundColor: Colors.transparent, + minimumSize: const Size(300, 700), + ), + ); + Catcher2( enableLogger: arguments["verbose"], debugConfig: Catcher2Options( From ba4e11a40ab18308437a05333a46eace6f8eeb5a Mon Sep 17 00:00:00 2001 From: franchioping <43936644+franchioping@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:37:51 +0000 Subject: [PATCH 03/46] fix: songs doesn't play when sources with preferred audio codec is empty (#976) * Fix song not playing when m4a or weba is not available (one is available but not the other) for that song * Update lib/services/sourced_track/sources/youtube.dart --------- Co-authored-by: Kingkor Roy Tirtho --- lib/services/sourced_track/sources/youtube.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index 096de2d4..2bcd6e3e 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -79,14 +79,17 @@ class YoutubeSourcedTrack extends SourcedTrack { } static SourceMap toSourceMap(StreamManifest manifest) { - final m4a = manifest.audioOnly + var m4a = manifest.audioOnly .where((audio) => audio.codec.mimeType == "audio/mp4") .sortByBitrate(); - final weba = manifest.audioOnly + var weba = manifest.audioOnly .where((audio) => audio.codec.mimeType == "audio/webm") .sortByBitrate(); + m4a = m4a.isEmpty ? weba.toList() : m4a; + weba = weba.isEmpty ? m4a.toList() : weba; + return SourceMap( m4a: SourceQualityMap( high: m4a.first.url.toString(), From 69559ba24285636e42b2f2231f956c31388c5cf3 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Tue, 2 Jan 2024 17:41:03 +0100 Subject: [PATCH 04/46] fix(macos): Respect Minimize to tray option (#1001) --- macos/Runner/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef643..218f93e0 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -4,6 +4,6 @@ import FlutterMacOS @NSApplicationMain class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true + return false } } From d1ed56926d27043684f44d69cf98a08815ddb830 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 3 Jan 2024 14:08:06 +0600 Subject: [PATCH 05/46] chore: remove build-IPA.yml as no longer needed --- .github/workflows/build-iPA.yml | 42 --------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/build-iPA.yml diff --git a/.github/workflows/build-iPA.yml b/.github/workflows/build-iPA.yml deleted file mode 100644 index 72e68774..00000000 --- a/.github/workflows/build-iPA.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build iPA -on: workflow_dispatch - -jobs: - build: - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@master - - uses: actions/checkout@v4 - - name: submodules-init - uses: snickerbockers/submodules-init@v4 - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - - name: Build - run: | - cp .env.example .env - flutter pub get && dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns - flutter build ios --release --no-codesign --flavor dev - flutter build ios --release --no-codesign --flavor stable - flutter build ios --release --no-codesign --flavor nightly - ln -sf ./build/ios/iphoneos Payload - zip -r9 spotube-dev.ipa Payload/dev.app - zip -r9 spotube-stable.ipa Payload/stable.app - zip -r9 spotube-nightly.ipa Payload/nightly.app - - name: Upload spotube-dev.ipa - uses: actions/upload-artifact@v4 - with: - name: "spotube-dev.ipa" - path: "spotube-dev.ipa" - - name: Upload spotube-stable.ipa - uses: actions/upload-artifact@v4 - with: - name: "spotube-stable.ipa" - path: "spotube-stable.ipa" - - name: Upload spotube-nightly.ipa - uses: actions/upload-artifact@v4 - with: - name: "spotube-nightly.ipa" - path: "spotube-nightly.ipa" From 988a975bf1a675df0cfc7b17776bcec74c67f1f2 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 5 Jan 2024 14:14:15 +0600 Subject: [PATCH 06/46] feat(deep-link): add track opening page --- lib/collections/routes.dart | 10 + .../player/player_track_details.dart | 11 +- lib/components/shared/links/link_text.dart | 3 + .../shared/track_tile/track_options.dart | 4 +- .../shared/track_tile/track_tile.dart | 4 +- lib/hooks/configurators/use_deep_linking.dart | 8 + lib/pages/artist/artist.dart | 2 +- lib/pages/track/track.dart | 227 ++++++++++++++++++ lib/services/queries/queries.dart | 2 + lib/services/queries/tracks.dart | 16 ++ pubspec.lock | 56 +++-- 11 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 lib/pages/track/track.dart create mode 100644 lib/services/queries/tracks.dart diff --git a/lib/collections/routes.dart b/lib/collections/routes.dart index 7816f204..3e2c42e0 100644 --- a/lib/collections/routes.dart +++ b/lib/collections/routes.dart @@ -17,6 +17,7 @@ import 'package:spotube/pages/search/search.dart'; import 'package:spotube/pages/settings/blacklist.dart'; import 'package:spotube/pages/settings/about.dart'; import 'package:spotube/pages/settings/logs.dart'; +import 'package:spotube/pages/track/track.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/components/shared/spotube_page_route.dart'; import 'package:spotube/pages/artist/artist.dart'; @@ -144,6 +145,15 @@ final router = GoRouter( ); }, ), + GoRoute( + path: "/track/:id", + pageBuilder: (context, state) { + final id = state.pathParameters["id"]!; + return SpotubePage( + child: TrackPage(trackId: id), + ); + }, + ), ], ), GoRoute( diff --git a/lib/components/player/player_track_details.dart b/lib/components/player/player_track_details.dart index d6f275fa..66cb9ef5 100644 --- a/lib/components/player/player_track_details.dart +++ b/lib/components/player/player_track_details.dart @@ -4,6 +4,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; +import 'package:spotube/components/shared/links/link_text.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -44,10 +45,12 @@ class PlayerTrackDetails extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), - Text( + LinkText( playback.activeTrack?.name ?? "", + "/track/${playback.activeTrack?.id}", + push: true, overflow: TextOverflow.ellipsis, - style: theme.textTheme.bodyMedium?.copyWith( + style: theme.textTheme.bodyMedium!.copyWith( color: color, ), ), @@ -66,8 +69,10 @@ class PlayerTrackDetails extends HookConsumerWidget { flex: 1, child: Column( children: [ - Text( + LinkText( playback.activeTrack?.name ?? "", + "/track/${playback.activeTrack?.id}", + push: true, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, color: color), ), diff --git a/lib/components/shared/links/link_text.dart b/lib/components/shared/links/link_text.dart index 217b247d..d7b00b72 100644 --- a/lib/components/shared/links/link_text.dart +++ b/lib/components/shared/links/link_text.dart @@ -8,6 +8,7 @@ class LinkText extends StatelessWidget { final TextAlign? textAlign; final TextOverflow? overflow; final String route; + final int? maxLines; final T? extra; final bool push; @@ -19,6 +20,7 @@ class LinkText extends StatelessWidget { this.extra, this.overflow, this.style = const TextStyle(), + this.maxLines, this.push = false, }) : super(key: key); @@ -37,6 +39,7 @@ class LinkText extends StatelessWidget { overflow: overflow, style: style, textAlign: textAlign, + maxLines: maxLines, ); } } diff --git a/lib/components/shared/track_tile/track_options.dart b/lib/components/shared/track_tile/track_options.dart index 8405d6ea..724bc029 100644 --- a/lib/components/shared/track_tile/track_options.dart +++ b/lib/components/shared/track_tile/track_options.dart @@ -43,12 +43,14 @@ class TrackOptions extends HookConsumerWidget { final bool userPlaylist; final String? playlistId; final ObjectRef?>? showMenuCbRef; + final Widget? icon; const TrackOptions({ Key? key, required this.track, this.showMenuCbRef, this.userPlaylist = false, this.playlistId, + this.icon, }) : super(key: key); void actionShare(BuildContext context, Track track) { @@ -207,7 +209,7 @@ class TrackOptions extends HookConsumerWidget { break; } }, - icon: const Icon(SpotubeIcons.moreHorizontal), + icon: icon ?? const Icon(SpotubeIcons.moreHorizontal), headings: [ ListTile( dense: true, diff --git a/lib/components/shared/track_tile/track_tile.dart b/lib/components/shared/track_tile/track_tile.dart index 961f29c9..c3b03f3c 100644 --- a/lib/components/shared/track_tile/track_tile.dart +++ b/lib/components/shared/track_tile/track_tile.dart @@ -193,8 +193,10 @@ class TrackTile extends HookConsumerWidget { children: [ Expanded( flex: 6, - child: Text( + child: LinkText( track.name!, + "/track/${track.id}", + push: true, maxLines: 1, overflow: TextOverflow.ellipsis, ), diff --git a/lib/hooks/configurators/use_deep_linking.dart b/lib/hooks/configurators/use_deep_linking.dart index be6facf9..3b7ec3f3 100644 --- a/lib/hooks/configurators/use_deep_linking.dart +++ b/lib/hooks/configurators/use_deep_linking.dart @@ -48,6 +48,11 @@ void useDeepLinking(WidgetRef ref) { ), ); break; + case "track": + router.push( + "/track/${url.pathSegments.last}", + ); + break; default: break; } @@ -80,6 +85,9 @@ void useDeepLinking(WidgetRef ref) { case "spotify:artist": await router.push("/artist/$endSegment"); break; + case "spotify:track": + await router.push("/track/$endSegment"); + break; case "spotify:playlist": await router.push( "/playlist/$endSegment", diff --git a/lib/pages/artist/artist.dart b/lib/pages/artist/artist.dart index 92470397..d511cb97 100644 --- a/lib/pages/artist/artist.dart +++ b/lib/pages/artist/artist.dart @@ -35,7 +35,7 @@ class ArtistPage extends HookConsumerWidget { ), extendBodyBehindAppBar: true, body: Builder(builder: (context) { - if (artistQuery.hasError) { + if (artistQuery.hasError && artistQuery.data == null) { return Center(child: Text(artistQuery.error.toString())); } return Skeletonizer( diff --git a/lib/pages/track/track.dart b/lib/pages/track/track.dart new file mode 100644 index 00000000..14052c10 --- /dev/null +++ b/lib/pages/track/track.dart @@ -0,0 +1,227 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:skeletonizer/skeletonizer.dart'; +import 'package:spotube/collections/fake.dart'; +import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/shared/heart_button.dart'; +import 'package:spotube/components/shared/image/universal_image.dart'; +import 'package:spotube/components/shared/links/link_text.dart'; +import 'package:spotube/components/shared/page_window_title_bar.dart'; +import 'package:spotube/components/shared/track_tile/track_options.dart'; +import 'package:spotube/extensions/context.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; +import 'package:spotube/services/audio_player/audio_player.dart'; +import 'package:spotube/services/queries/queries.dart'; +import 'package:spotube/utils/type_conversion_utils.dart'; +import 'package:spotube/extensions/constrains.dart'; + +class TrackPage extends HookConsumerWidget { + final String trackId; + const TrackPage({ + Key? key, + required this.trackId, + }) : super(key: key); + + @override + Widget build(BuildContext context, ref) { + final ThemeData(:textTheme, :colorScheme) = Theme.of(context); + final mediaQuery = MediaQuery.of(context); + + final playlist = ref.watch(ProxyPlaylistNotifier.provider); + final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); + + final isActive = playlist.activeTrack?.id == trackId; + + final trackQuery = useQueries.tracks.track(ref, trackId); + + final track = trackQuery.data ?? FakeData.track; + + void onPlay() async { + if (isActive) { + audioPlayer.pause(); + } else { + await playlistNotifier.load([track], autoPlay: true); + } + } + + return Scaffold( + appBar: const PageWindowTitleBar( + automaticallyImplyLeading: true, + backgroundColor: Colors.transparent, + ), + extendBodyBehindAppBar: true, + body: Stack( + children: [ + Positioned.fill( + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: UniversalImage.imageProvider( + TypeConversionUtils.image_X_UrlString( + track.album!.images, + placeholder: ImagePlaceholder.albumArt, + ), + ), + fit: BoxFit.cover, + colorFilter: ColorFilter.mode( + colorScheme.surface.withOpacity(0.5), + BlendMode.srcOver, + ), + alignment: Alignment.topCenter, + ), + ), + ), + ), + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Skeletonizer( + enabled: trackQuery.isLoading, + child: Container( + alignment: Alignment.topCenter, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + colorScheme.surface, + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.2, 1], + ), + ), + child: SafeArea( + child: Wrap( + spacing: 20, + runSpacing: 20, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + runAlignment: WrapAlignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: UniversalImage( + path: TypeConversionUtils.image_X_UrlString( + track.album!.images, + placeholder: ImagePlaceholder.albumArt, + ), + height: 200, + width: 200, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + crossAxisAlignment: mediaQuery.smAndDown + ? CrossAxisAlignment.center + : CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + track.name!, + style: textTheme.titleLarge, + ), + const Gap(10), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(SpotubeIcons.album), + const Gap(5), + Flexible( + child: LinkText( + track.album!.name!, + '/album/${track.album!.id}', + push: true, + extra: track.album, + ), + ), + ], + ), + const Gap(10), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(SpotubeIcons.artist), + const Gap(5), + TypeConversionUtils + .artists_X_ClickableArtists( + track.artists!, + ), + ], + ), + const Gap(10), + ConstrainedBox( + constraints: + const BoxConstraints(maxWidth: 350), + child: Row( + mainAxisSize: mediaQuery.smAndDown + ? MainAxisSize.max + : MainAxisSize.min, + children: [ + const Gap(5), + if (!isActive && + !playlist.tracks.contains(track)) + OutlinedButton.icon( + icon: const Icon(SpotubeIcons.queueAdd), + label: Text(context.l10n.queue), + onPressed: () { + playlistNotifier.addTrack(track); + }, + ), + const Gap(5), + if (!isActive && + !playlist.tracks.contains(track)) + IconButton.outlined( + icon: + const Icon(SpotubeIcons.lightning), + tooltip: context.l10n.play_next, + onPressed: () { + playlistNotifier + .addTracksAtFirst([track]); + }, + ), + const Gap(5), + IconButton.filled( + tooltip: isActive + ? context.l10n.pause_playback + : context.l10n.play, + icon: Icon( + isActive + ? SpotubeIcons.pause + : SpotubeIcons.play, + color: colorScheme.onPrimary, + ), + onPressed: onPlay, + ), + const Gap(5), + if (mediaQuery.smAndDown) + const Spacer() + else + const Gap(20), + TrackHeartButton(track: track), + TrackOptions( + track: track, + userPlaylist: false, + ), + const Gap(5), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/services/queries/queries.dart b/lib/services/queries/queries.dart index cc3ce132..30c23268 100644 --- a/lib/services/queries/queries.dart +++ b/lib/services/queries/queries.dart @@ -4,6 +4,7 @@ import 'package:spotube/services/queries/category.dart'; import 'package:spotube/services/queries/lyrics.dart'; import 'package:spotube/services/queries/playlist.dart'; import 'package:spotube/services/queries/search.dart'; +import 'package:spotube/services/queries/tracks.dart'; import 'package:spotube/services/queries/user.dart'; import 'package:spotube/services/queries/views.dart'; @@ -17,6 +18,7 @@ class Queries { final search = const SearchQueries(); final user = const UserQueries(); final views = const ViewsQueries(); + final tracks = const TracksQueries(); } const useQueries = Queries._(); diff --git a/lib/services/queries/tracks.dart b/lib/services/queries/tracks.dart new file mode 100644 index 00000000..52bab984 --- /dev/null +++ b/lib/services/queries/tracks.dart @@ -0,0 +1,16 @@ +import 'package:fl_query/fl_query.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/hooks/spotify/use_spotify_query.dart'; + +class TracksQueries { + const TracksQueries(); + + Query track(WidgetRef ref, String id) { + return useSpotifyQuery( + "track/$id", + (spotify) => spotify.tracks.get(id), + ref: ref, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 526898d5..b4182d12 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -543,10 +543,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" file_picker: dependency: "direct main" description: @@ -1255,6 +1255,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" + url: "https://pub.dev" + source: hosted + version: "9.0.16" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" lints: dependency: transitive description: @@ -1307,10 +1323,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" media_kit: dependency: "direct main" description: @@ -1379,10 +1395,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" metadata_god: dependency: "direct main" description: @@ -1595,10 +1611,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" plugin_platform_interface: dependency: transitive description: @@ -1635,10 +1651,10 @@ packages: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + sha256: "266ca5be5820feefc777793d0a583acfc8c40834893c87c00c6c09e2cf58ea42" url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "5.0.1" provider: dependency: transitive description: @@ -1780,10 +1796,10 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: @@ -1804,10 +1820,10 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shelf: dependency: transitive description: @@ -2217,10 +2233,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -2233,10 +2249,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.0" web_socket_channel: dependency: transitive description: @@ -2249,10 +2265,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" wikipedia_api: dependency: "direct main" description: From c203ac69ee74ba8722dae3da4b47761cd8d59c34 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 5 Jan 2024 20:02:27 +0600 Subject: [PATCH 07/46] fix: search page vertical scrollbar moves on horizontal scroll #1017 --- .../horizontal_playbutton_card_view.dart | 86 ++++++++++--------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart b/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart index d00e5c4b..2075acbb 100644 --- a/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart +++ b/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart @@ -55,48 +55,52 @@ class HorizontalPlaybuttonCardView extends HookWidget { ), SizedBox( height: height, - child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: items.isEmpty - ? ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: 5, - itemBuilder: (context, index) { - return AlbumCard(FakeData.albumSimple); - }, - ) - : InfiniteList( - scrollController: scrollController, - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(vertical: 8.0), - itemCount: items.length, - onFetchData: onFetchMore, - loadingBuilder: (context) => Skeletonizer( - enabled: true, - child: AlbumCard(FakeData.albumSimple), - ), - isLoading: isLoadingNextPage, - hasReachedMax: !hasNextPage, - itemBuilder: (context, index) { - final item = items[index]; - - return switch (item.runtimeType) { - PlaylistSimple => - PlaylistCard(item as PlaylistSimple), - Album => AlbumCard(item as Album), - Artist => Padding( - padding: - const EdgeInsets.symmetric(horizontal: 12.0), - child: ArtistCard(item as Artist), + child: NotificationListener( + // disable multiple scrollbar to use this + onNotification: (notification) => true, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: items.isEmpty + ? ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: 5, + itemBuilder: (context, index) { + return AlbumCard(FakeData.albumSimple); + }, + ) + : InfiniteList( + scrollController: scrollController, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(vertical: 8.0), + itemCount: items.length, + onFetchData: onFetchMore, + loadingBuilder: (context) => Skeletonizer( + enabled: true, + child: AlbumCard(FakeData.albumSimple), ), - _ => const SizedBox.shrink(), - }; - }), + isLoading: isLoadingNextPage, + hasReachedMax: !hasNextPage, + itemBuilder: (context, index) { + final item = items[index]; + + return switch (item.runtimeType) { + PlaylistSimple => + PlaylistCard(item as PlaylistSimple), + Album => AlbumCard(item as Album), + Artist => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0), + child: ArtistCard(item as Artist), + ), + _ => const SizedBox.shrink(), + }; + }), + ), ), ), ], From 5509cae91c8b1f5cb9fac179060f477397a4a27f Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 5 Jan 2024 20:26:57 +0600 Subject: [PATCH 08/46] fix(android): download failing for permission issues #1015 --- .../download_manager/chunked_download.dart | 1 - .../download_manager/download_manager.dart | 22 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/services/download_manager/chunked_download.dart b/lib/services/download_manager/chunked_download.dart index b2849a3c..9e5e0a98 100644 --- a/lib/services/download_manager/chunked_download.dart +++ b/lib/services/download_manager/chunked_download.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; import 'package:spotube/models/logger.dart'; final logger = getLogger("ChunkedDownload"); diff --git a/lib/services/download_manager/download_manager.dart b/lib/services/download_manager/download_manager.dart index 904f06cf..d7a42430 100644 --- a/lib/services/download_manager/download_manager.dart +++ b/lib/services/download_manager/download_manager.dart @@ -6,6 +6,8 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; import 'package:spotube/models/logger.dart'; import 'package:spotube/services/download_manager/chunked_download.dart'; import 'package:spotube/services/download_manager/download_request.dart'; @@ -77,7 +79,18 @@ class DownloadManager { logger.d("[DownloadManager] $url"); final file = File(savePath.toString()); - partialFilePath = savePath + partialExtension; + + final tmpDirPath = await Directory( + path.join( + (await getTemporaryDirectory()).path, + "spotube-downloads", + ), + ).create(recursive: true); + + partialFilePath = path.join( + tmpDirPath.path, + path.basename(savePath) + partialExtension, + ); partialFile = File(partialFilePath); final fileExist = await file.exists(); @@ -111,7 +124,9 @@ class DownloadManager { await ioSink.addStream(partialChunkFile.openRead()); await partialChunkFile.delete(); await ioSink.close(); - await partialFile.rename(savePath); + + await partialFile.copy(savePath); + await partialFile.delete(); setStatus(task, DownloadStatus.completed); } @@ -125,7 +140,8 @@ class DownloadManager { ); if (response.statusCode == HttpStatus.ok) { - await partialFile.rename(savePath); + await partialFile.copy(savePath); + await partialFile.delete(); setStatus(task, DownloadStatus.completed); } } From 29f162c80100fcb9ad51ee3103f04cf2facc17e9 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 5 Jan 2024 21:25:23 +0600 Subject: [PATCH 09/46] chore: remove the sp_key cookie requirement as no longer necessary --- lib/components/desktop_login/login_form.dart | 15 +--- lib/l10n/app_ar.arb | 2 - lib/l10n/app_bn.arb | 2 - lib/l10n/app_ca.arb | 2 - lib/l10n/app_de.arb | 2 - lib/l10n/app_en.arb | 6 +- lib/l10n/app_es.arb | 2 - lib/l10n/app_fa.arb | 2 - lib/l10n/app_fr.arb | 2 - lib/l10n/app_hi.arb | 2 - lib/l10n/app_it.arb | 2 - lib/l10n/app_ja.arb | 2 - lib/l10n/app_nl.arb | 2 - lib/l10n/app_pl.arb | 2 - lib/l10n/app_pt.arb | 2 - lib/l10n/app_ru.arb | 2 - lib/l10n/app_tr.arb | 2 - lib/l10n/app_uk.arb | 2 - lib/l10n/app_zh.arb | 2 - lib/pages/mobile_login/mobile_login.dart | 7 +- untranslated_messages.json | 87 +++++++++++++++++++- 21 files changed, 92 insertions(+), 57 deletions(-) diff --git a/lib/components/desktop_login/login_form.dart b/lib/components/desktop_login/login_form.dart index f2b183f4..5abb9524 100644 --- a/lib/components/desktop_login/login_form.dart +++ b/lib/components/desktop_login/login_form.dart @@ -17,7 +17,6 @@ class TokenLoginForm extends HookConsumerWidget { final authenticationNotifier = ref.watch(AuthenticationNotifier.provider.notifier); final directCodeController = useTextEditingController(); - final keyCodeController = useTextEditingController(); final mounted = useIsMounted(); final isLoading = useState(false); @@ -37,23 +36,13 @@ class TokenLoginForm extends HookConsumerWidget { keyboardType: TextInputType.visiblePassword, ), const SizedBox(height: 10), - TextField( - controller: keyCodeController, - decoration: InputDecoration( - hintText: context.l10n.spotify_cookie("\"sp_key (or sp_gaid)\""), - labelText: context.l10n.cookie_name_cookie("sp_key (or sp_gaid)"), - ), - keyboardType: TextInputType.visiblePassword, - ), - const SizedBox(height: 20), FilledButton( onPressed: isLoading.value ? null : () async { try { isLoading.value = true; - if (keyCodeController.text.isEmpty || - directCodeController.text.isEmpty) { + if (directCodeController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(context.l10n.fill_in_all_fields), @@ -63,7 +52,7 @@ class TokenLoginForm extends HookConsumerWidget { return; } final cookieHeader = - "sp_dc=${directCodeController.text.trim()}; sp_key=${keyCodeController.text.trim()}"; + "sp_dc=${directCodeController.text.trim()}"; authenticationNotifier.setCredentials( await AuthenticationCredentials.fromCookie( diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 2bdde72a..20b6a47c 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -177,11 +177,9 @@ "step_2": "الخطوة 2", "step_2_steps": "1. بمجرد تسجيل الدخول، اضغط على F12 أو انقر بزر الماوس الأيمن > فحص لفتح أدوات تطوير المتصفح.\n2. ثم انتقل إلى علامة التبويب \"التطبيقات\" (Chrome وEdge وBrave وما إلى ذلك.) أو علامة التبويب \"التخزين\" (Firefox وPalemoon وما إلى ذلك..)\n3. انتقل إلى قسم \"ملفات تعريف الارتباط\" ثم القسم الفرعي \"https://accounts.spotify.com\"", "step_3": "الخطوة 3", - "step_3_steps": "انسخ قيم \"sp_dc\" و \"sp_key\" (أو sp_gaid) الكويز", "success_emoji": "نجاح 🥳", "success_message": "لقد قمت الآن بتسجيل الدخول بنجاح باستخدام حساب Spotify الخاص بك. عمل جيد يا صديقي!", "step_4": "الخطوة 4", - "step_4_steps": "قم بلصق قيم \"sp_dc\" و \"sp_key\" (أو sp_gaid) المنسوخة في الحقول المعنية", "something_went_wrong": "هناك خطأ ما", "piped_instance": "مثيل خادم Piped", "piped_description": "مثيل خادم Piped الذي سيتم استخدامه لمطابقة المقطوعة", diff --git a/lib/l10n/app_bn.arb b/lib/l10n/app_bn.arb index 39f8a1ee..74dc200d 100644 --- a/lib/l10n/app_bn.arb +++ b/lib/l10n/app_bn.arb @@ -175,11 +175,9 @@ "step_2": "ধাপ 2", "step_2_steps": "১. একবার আপনি লগ ইন করলে, ব্রাউজার ডেভটুল খুলতে F12 বা মাউসের রাইট ক্লিক > \"Inspect to open Browser DevTools\" টিপুন।\n২. তারপর \"Application\" ট্যাবে যান (Chrome, Edge, Brave etc..) অথবা \"Storage\" Tab (Firefox, Palemoon etc..)\n৩. \"Cookies \" বিভাগে যান তারপর \"https://accounts.spotify.com\" উপবিভাগে যান", "step_3": "ধাপ 3", - "step_3_steps": "\"sp_dc\" এবং \"sp_key\" (অথবা sp_gaid) কুকিজের মান কপি করুন", "success_emoji": "আমরা সফল🥳", "success_message": "এখন আপনি সফলভাবে আপনার Spotify অ্যাকাউন্ট দিয়ে লগ ইন করেছেন। সাধুভাত আপনাকে", "step_4": "ধাপ 4", - "step_4_steps": "কপি করা \"sp_dc\" এবং \"sp_key\" (অথবা sp_gaid) এর মান সংশ্লিষ্ট ফিল্ডে পেস্ট করুন", "something_went_wrong": "কিছু ভুল হয়েছে", "piped_instance": "Piped সার্ভার এড্রেস", "piped_description": "গান ম্যাচ করার জন্য ব্যবহৃত পাইপড সার্ভার", diff --git a/lib/l10n/app_ca.arb b/lib/l10n/app_ca.arb index 15ca9e31..2c952457 100644 --- a/lib/l10n/app_ca.arb +++ b/lib/l10n/app_ca.arb @@ -175,11 +175,9 @@ "step_2": "Pas 2", "step_2_steps": "1. Una vegada que hagi iniciat sessió, premi F12 o faci clic dret amb el ratolí > Inspeccionar per obrir les eines de desenvolulpador del navegador.\n2. Després vagi a la pestanya \"Application\" (Chrome, Edge, Brave, etc.) o \"Storage\" (Firefox, Palemoon, etc.)\n3. Vagi a la secció \"Cookies\" i després a la subsecció \"https://accounts.spotify.com\"", "step_3": "Pas 3", - "step_3_steps": "Copiï els valors de les Cookies \"sp_dc\" i \"sp_key\" (o sp_gaid)", "success_emoji": "Èxit! 🥳", "success_message": "Ara has iniciat sessió amb èxit al teu compte de Spotify. Bona feina!", "step_4": "Pas 4", - "step_4_steps": "Enganxi els valors coppiats de \"sp_dc\" i \"sp_key\" (o sp_gaid) en els camps respectius", "something_went_wrong": "Quelcom ha sortit malament", "piped_instance": "Instància del servidor Piped", "piped_description": "La instància del servidor Piped a utilitzar per la coincidència de cançons", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 1a13e4a1..59f832ea 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -175,11 +175,9 @@ "step_2": "Schritt 2", "step_2_steps": "1. Wenn du angemeldet bist, drücke F12 oder klicke mit der rechten Maustaste > Inspektion, um die Browser-Entwicklertools zu öffnen.\n2. Gehe dann zum \"Anwendungs\"-Tab (Chrome, Edge, Brave usw.) oder zum \"Storage\"-Tab (Firefox, Palemoon usw.)\n3. Gehe zum Abschnitt \"Cookies\" und dann zum Unterabschnitt \"https://accounts.spotify.com\"", "step_3": "Schritt 3", - "step_3_steps": "Kopiere die Werte der Cookies \"sp_dc\" und \"sp_key\" (oder sp_gaid)", "success_emoji": "Erfolg🥳", "success_message": "Jetzt bist du erfolgreich mit deinem Spotify-Konto angemeldet. Gut gemacht, Kumpel!", "step_4": "Schritt 4", - "step_4_steps": "Füge die kopierten Werte von \"sp_dc\" und \"sp_key\" (oder sp_gaid) in die entsprechenden Felder ein", "something_went_wrong": "Etwas ist schiefgelaufen", "piped_instance": "Piped-Serverinstanz", "piped_description": "Die Piped-Serverinstanz, die zur Titelzuordnung verwendet werden soll", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index bebfafac..82877ea1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -177,11 +177,11 @@ "step_2": "Step 2", "step_2_steps": "1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection", "step_3": "Step 3", - "step_3_steps": "Copy the values of \"sp_dc\" and \"sp_key\" (or sp_gaid) Cookies", + "step_3_steps": "Copy the value of \"sp_dc\" Cookie", "success_emoji": "Success🥳", - "success_message": "Now you're successfully Logged In with your Spotify account. Good Job, mate!", + "success_message": "Now you've successfully Logged in with your Spotify account. Good Job, mate!", "step_4": "Step 4", - "step_4_steps": "Paste the copied \"sp_dc\" and \"sp_key\" (or sp_gaid) values in the respective fields", + "step_4_steps": "Paste the copied \"sp_dc\" value", "something_went_wrong": "Something went wrong", "piped_instance": "Piped Server Instance", "piped_description": "The Piped server instance to use for track matching", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 2fecd8f1..e04b4798 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -175,11 +175,9 @@ "step_2": "Paso 2", "step_2_steps": "1. Una vez que hayas iniciado sesión, presiona F12 o haz clic derecho con el ratón > Inspeccionar para abrir las herramientas de desarrollo del navegador.\n2. Luego ve a la pestaña \"Application\" (Chrome, Edge, Brave, etc.) o \"Storage\" (Firefox, Palemoon, etc.)\n3. Ve a la sección \"Cookies\" y luego la subsección \"https://accounts.spotify.com\"", "step_3": "Paso 3", - "step_3_steps": "Copia los valores de las Cookies \"sp_dc\" y \"sp_key\" (o sp_gaid)", "success_emoji": "¡Éxito! 🥳", "success_message": "Ahora has iniciado sesión con éxito en tu cuenta de Spotify. ¡Buen trabajo!", "step_4": "Paso 4", - "step_4_steps": "Pega los valores copiados de \"sp_dc\" y \"sp_key\" (o sp_gaid) en los campos respectivos", "something_went_wrong": "Algo salió mal", "piped_instance": "Instancia del servidor Piped", "piped_description": "La instancia del servidor Piped a utilizar para la coincidencia de pistas", diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb index 84b9b448..c9586cde 100644 --- a/lib/l10n/app_fa.arb +++ b/lib/l10n/app_fa.arb @@ -177,11 +177,9 @@ "step_2": "گام 2", "step_2_steps": "1. پس از ورود به سیستم، F12 یا کلیک راست ماوس > Inspect را فشار دهید تا ابزارهای توسعه مرورگر باز شود..\n2. سپس به تب \"Application\" (Chrome, Edge, Brave etc..) یا \"Storage\" Tab (Firefox, Palemoon etc..)\n3. به قسمت \"Cookies\" و به پخش \"https://accounts.spotify.com\" بروید", "step_3": "گام 3", - "step_3_steps": "کپی کردن مقادیر \"sp_dc\" و \"sp_key\" (یا sp_gaid) کوکی", "success_emoji": "موفقیت🥳", "success_message": "اکنون با موفقیت با حساب اسپوتیفای خود وارد شده اید", "step_4": "مرحله 4", - "step_4_steps": "مقدار کپی شده را \"sp_dc\" and \"sp_key\" (یا sp_gaid) در فیلد مربوط پر کنید", "something_went_wrong": "اشتباهی رخ داده", "piped_instance": "مشکل در ارتباط با سرور", "piped_description": "مشکل در ارتباط با سرور در دریافت آهنگ ها", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 82997bad..0c3eb653 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -175,11 +175,9 @@ "step_2": "Étape 2", "step_2_steps": "1. Une fois connecté, appuyez sur F12 ou clic droit de la souris > Inspecter pour ouvrir les outils de développement du navigateur.\n2. Ensuite, allez dans l'onglet \"Application\" (Chrome, Edge, Brave, etc.) ou l'onglet \"Stockage\" (Firefox, Palemoon, etc.)\n3. Allez dans la section \"Cookies\", puis dans la sous-section \"https://accounts.spotify.com\"", "step_3": "Étape 3", - "step_3_steps": "Copiez les valeurs des cookies \"sp_dc\" et \"sp_key\" (ou sp_gaid)", "success_emoji": "Succès🥳", "success_message": "Vous êtes maintenant connecté avec succès à votre compte Spotify. Bon travail, mon ami!", "step_4": "Étape 4", - "step_4_steps": "Collez les valeurs copiées de \"sp_dc\" et \"sp_key\" (ou sp_gaid) dans les champs respectifs", "something_went_wrong": "Quelque chose s'est mal passé", "piped_instance": "Instance pipée", "piped_description": "L'instance de serveur Piped à utiliser pour la correspondance des pistes", diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index 4bfff3da..dd27dabf 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -175,11 +175,9 @@ "step_2": "2 चरण", "step_2_steps": "1. जब आप लॉगिन हो जाएँ, तो F12 दबाएं या माउस राइट क्लिक> निरीक्षण करें ताकि ब्राउज़र डेवटूल्स खुलें।\n2. फिर ब्राउज़र के \"एप्लिकेशन\" टैब (Chrome, Edge, Brave आदि) या \"स्टोरेज\" टैब (Firefox, Palemoon आदि) में जाएं\n3. \"कुकीज़\" अनुभाग में जाएं फिर \"https: //accounts.spotify.com\" उप-अनुभाग में जाएं", "step_3": "स्टेप 3", - "step_3_steps": "\"sp_dc\" और \"sp_key\" (या sp_gaid) कुकीज़ के मान कॉपी करें", "success_emoji": "सफलता🥳", "success_message": "अब आप अपने स्पॉटिफाई अकाउंट से सफलतापूर्वक लॉगइन हो गए हैं। अच्छा काम किया!", "step_4": "स्टेप 4", - "step_4_steps": "कॉपी की गई \"sp_dc\" और \"sp_key\" (या sp_gaid) मानों को संबंधित फील्ड में पेस्ट करें", "something_went_wrong": "कुछ गलत हो गया", "piped_instance": "पाइप्ड सर्वर", "piped_description": "पाइप किए गए सर्वर", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 033bb516..3680933a 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -177,11 +177,9 @@ "step_2": "Passo 2", "step_2_steps": "1. Quando sei acceduto premi F12 o premi il tasto destro del Mouse > Ispeziona per aprire gli strumenti di sviluppo del browser.\n2. Vai quindi nel tab \"Applicazione\" (Chrome, Edge, Brave etc..) o tab \"Archiviazione\" (Firefox, Palemoon etc..)\n3. Vai nella sezione \"Cookies\" quindi nella sezione \"https://accounts.spotify.com\"", "step_3": "Passo 3", - "step_3_steps": "Copia il valore dei cookie \"sp_dc\" e \"sp_key\" (o sp_gaid)", "success_emoji": "Successo🥳", "success_message": "Ora hai correttamente effettuato il login al tuo account Spotify. Bel lavoro, amico!", "step_4": "Passo 4", - "step_4_steps": "Incolla i valori copiati di \"sp_dc\" e \"sp_key\" (o sp_gaid) nei campi rispettivi", "something_went_wrong": "Qualcosa è andato storto", "piped_instance": "Istanza Server Piped", "piped_description": "L'istanza server Piped da usare per il match della tracccia", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index ac23728b..39e0dad8 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -175,11 +175,9 @@ "step_2": "ステップ 2", "step_2_steps": "1. ログインしたら、F12を押すか、マウス右クリック > 調査(検証)でブラウザの開発者ツール (devtools) を開きます。\n2. アプリケーション (Application) タブ (Chrome, Edge, Brave など) またはストレージタブ (Firefox, Palemoon など)\n3. Cookies 欄を選択し、https://accounts.spotify.com の枝を選びます", "step_3": "ステップ 3", - "step_3_steps": "sp_dc と sp_key (または or sp_gaid) の値 (Value) をコピーします", "success_emoji": "成功🥳", "success_message": "アカウントへのログインに成功しました。よくできました!", "step_4": "ステップ 4", - "step_4_steps": "コピーした sp_dc と sp_key (または or sp_gaid) の値をそれぞれの入力欄に貼り付けます", "something_went_wrong": "何か誤りがあります", "piped_instance": "Piped サーバーのインスタンス", "piped_description": "曲の一致に使う Piped サーバーのインスタンス", diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 6e50c461..23eee51b 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -177,11 +177,9 @@ "step_2": "Stap 2", "step_2_steps": "1. Zodra je bent aangemeld, druk je op F12 of klik je met de rechtermuisknop > Inspect om de Browser devtools te openen.\n2. Ga vervolgens naar het tabblad \"Toepassing\" (Chrome, Edge, Brave enz..) of naar het tabblad \"Opslag\" (Firefox, Palemoon enz..).\n3. Ga naar de sectie \"Cookies\" en vervolgens naar de subsectie \"https://accounts.spotify.com\".", "step_3": "Stap 3", - "step_3_steps": "Kopieer de waarden van \"sp_dc\" en \"sp_key\" (of sp_gaid) Cookies", "success_emoji": "Succes🥳", "success_message": "Je bent nu succesvol ingelogd met je Spotify account. Goed gedaan, maat!", "step_4": "Stap 4", - "step_4_steps": "Plak de gekopieerde \"sp_dc\" en \"sp_key\" (of sp_gaid) waarden in de respectievelijke velden", "something_went_wrong": "Er ging iets mis", "piped_instance": "Piped-serverinstantie", "piped_description": "De Piped-serverinstantie die moet worden gebruikt voor het matchen van sporen", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index dd173a37..4ae48338 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -175,11 +175,9 @@ "step_2": "Krok 2", "step_2_steps": "1. Jeśli jesteś zalogowany, naciśnij klawisz F12 lub Kliknij prawym przyciskiem myszy > Zbadaj, aby odtworzyć narzędzia developerskie.\n2. Następnie przejdź do zakładki \"Application\" (Chrome, Edge, Brave etc..) lub zakładki \"Storage\" (Firefox, Palemoon etc..)\n3. Przejdź do sekcji \"Cookies\" a następnie do pod-sekcji \"https://accounts.spotify.com\"", "step_3": "Krok 3", - "step_3_steps": "Skopiuj wartości \"sp_dc\" i \"sp_key\" (lub sp_gaid) Ciasteczek", "success_emoji": "Sukces!🥳", "success_message": "Udało ci się zalogować! Dobra robota, stary!", "step_4": "Krok 4", - "step_4_steps": "Wklej wartości \"sp_dc\" i \"sp_key\" (lub sp_gaid) do odpowiednich pul.", "something_went_wrong": "Coś poszło nie tak 🙁", "piped_instance": "Instancja serwera Piped", "piped_description": "Instancja serwera Piped używana jest do dopasowania utworów.", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 705217c1..5ea4cca0 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -175,11 +175,9 @@ "step_2": "Passo 2", "step_2_steps": "1. Uma vez logado, pressione F12 ou clique com o botão direito do mouse > Inspecionar para abrir as ferramentas de desenvolvimento do navegador.\n2. Em seguida, vá para a guia \"Aplicativo\" (Chrome, Edge, Brave, etc.) ou \"Armazenamento\" (Firefox, Palemoon, etc.)\n3. Acesse a seção \"Cookies\" e depois a subseção \"https://accounts.spotify.com\"", "step_3": "Passo 3", - "step_3_steps": "Copie os valores dos Cookies \"sp_dc\" e \"sp_key\" (ou sp_gaid)", "success_emoji": "Sucesso🥳", "success_message": "Agora você está logado com sucesso em sua conta do Spotify. Bom trabalho!", "step_4": "Passo 4", - "step_4_steps": "Cole os valores copiados \"sp_dc\" e \"sp_key\" (ou sp_gaid) nos campos correspondentes", "something_went_wrong": "Algo deu errado", "piped_instance": "Instância do Servidor Piped", "piped_description": "A instância do servidor Piped a ser usada para correspondência de faixas", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 32415863..24120d62 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -175,11 +175,9 @@ "step_2": "Шаг 2", "step_2_steps": "1. После входа в систему нажмите F12 или щелкните правой кнопкой мыши > «Проверить», чтобы открыть инструменты разработчика браузера.\n2. Затем перейдите на вкладку \"Application\" (Chrome, Edge, Brave и т.д..) or \"Storage\" (Firefox, Palemoon и т.д..)\n3. Перейдите в раздел \"Cookies\", а затем в подраздел \"https://accounts.spotify.com\"", "step_3": "Шаг 3", - "step_3_steps": "Скопируйте значения \"sp_dc\" и \"sp_key\" (или sp_gaid) Cookies", "success_emoji": "Успешно 🥳", "success_message": "Теперь вы успешно вошли в свою учетную запись Spotify. Отличная работа, приятель!", "step_4": "Шаг 4", - "step_4_steps": "Вставьте скопированные \"sp_dc\" и \"sp_key\" (или sp_gaid) значения в соответствующие поля", "something_went_wrong": "Что-то пошло не так", "piped_instance": "Экземпляр сервера Piped", "piped_description": "Серверный экземпляр Piped для сопоставления треков", diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index 63646af6..e6b0ce34 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -177,11 +177,9 @@ "step_2": "2. Adım", "step_2_steps": "1. Giriş yaptıktan sonra, Tarayıcı devtools.\n2'yi açmak için F12'ye basın veya Fare Sağ Tıklaması > İncele'ye basın. Ardından \"Uygulama\" Sekmesine (Chrome, Edge, Brave vb.) veya \"Depolama\" Sekmesine (Firefox, Palemoon vb.) gidin\n3. \"Çerezler\" bölümüne ve ardından \"https://accounts.spotify.com\" alt bölümüne gidin", "step_3": "3. Adım", - "step_3_steps": "\"sp_dc\" ve \"sp_key\" (veya sp_gaid) Çerezlerinin değerlerini kopyalayın", "success_emoji": "Başarılı🥳", "success_message": "Şimdi Spotify hesabınızla başarılı bir şekilde oturum açtınız. İyi iş, dostum!", "step_4": "4. Adım", - "step_4_steps": "Kopyalanan \"sp_dc\" ve \"sp_key\" (veya sp_gaid) değerlerini ilgili alanlara yapıştırın", "something_went_wrong": "Bir şeyler ters gitti", "piped_instance": "Piped Sunucu Örneği", "piped_description": "Parça eşleştirme için kullanılacak Piped sunucu örneği", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 2ae29237..a5199a04 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -177,11 +177,9 @@ "step_2": "Крок 2", "step_2_steps": "1. Після входу натисніть F12 або клацніть правою кнопкою миші > Інспектувати, щоб відкрити інструменти розробки браузера.\n2. Потім перейдіть на вкладку 'Програма' (Chrome, Edge, Brave тощо) або вкладку 'Сховище' (Firefox, Palemoon тощо).\n3. Перейдіть до розділу 'Кукі-файли', а потім до підрозділу 'https://accounts.spotify.com'", "step_3": "Крок 3", - "step_3_steps": "Скопіюйте значення кукі-файлів 'sp_dc' та 'sp_key' (або sp_gaid)", "success_emoji": "Успіх🥳", "success_message": "Тепер ви успішно ввійшли у свій обліковий запис Spotify. Гарна робота, друже!", "step_4": "Крок 4", - "step_4_steps": "Вставте скопійовані значення 'sp_dc' та 'sp_key' (або sp_gaid) у відповідні поля", "something_went_wrong": "Щось пішло не так", "piped_instance": "Примірник сервера Piped", "piped_description": "Примірник сервера Piped, який використовуватиметься для зіставлення треків", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 85b57724..30f4a82c 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -175,11 +175,9 @@ "step_2": "步骤 2", "step_2_steps": "1. 一旦你已经完成登录, 按 F12 键或者鼠标右击网页空白区域 > 选择“检查”以打开浏览器开发者工具(DevTools)\n2. 然后选择 \"应用(Application)\" 标签页(Chrome, Edge, Brave 等基于 Chromium 的浏览器) 或 \"存储(Storage)\" 标签页 (Firefox, Palemoon 等基于 Firefox 的浏览器))\n3. 选择 \"Cookies\" 栏目然后选择 \"https://accounts.spotify.com\" 子栏目", "step_3": "步骤 3", - "step_3_steps": "复制名称为 \"sp_dc\" 和 \"sp_key\" (或 sp_gaid) 的值(Cookie Value)", "success_emoji": "成功🥳", "success_message": "你已经成功使用 Spotify 登录。干得漂亮!", "step_4": "步骤 4", - "step_4_steps": "将 \"sp_dc\" 与 \"sp_key\" (或 sp_gaid) 的值分别复制后粘贴到对应的区域", "something_went_wrong": "某些地方出现了问题", "piped_instance": "管道服务器实例", "piped_description": "管道服务器实例用于匹配歌曲", diff --git a/lib/pages/mobile_login/mobile_login.dart b/lib/pages/mobile_login/mobile_login.dart index 7ab0ea2a..8b9bce4c 100644 --- a/lib/pages/mobile_login/mobile_login.dart +++ b/lib/pages/mobile_login/mobile_login.dart @@ -55,12 +55,7 @@ class WebViewLogin extends HookConsumerWidget { final cookies = await CookieManager.instance().getCookies(url: action); final cookieHeader = - cookies.fold("", (previousValue, element) { - if (element.name == "sp_dc" || element.name == "sp_key") { - return "$previousValue; ${element.name}=${element.value}"; - } - return previousValue; - }); + "sp_dc=${cookies.firstWhere((element) => element.name == "sp_dc").value}"; authenticationNotifier.setCredentials( await AuthenticationCredentials.fromCookie(cookieHeader), diff --git a/untranslated_messages.json b/untranslated_messages.json index 9e26dfee..59b26614 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1 +1,86 @@ -{} \ No newline at end of file +{ + "ar": [ + "step_3_steps", + "step_4_steps" + ], + + "bn": [ + "step_3_steps", + "step_4_steps" + ], + + "ca": [ + "step_3_steps", + "step_4_steps" + ], + + "de": [ + "step_3_steps", + "step_4_steps" + ], + + "es": [ + "step_3_steps", + "step_4_steps" + ], + + "fa": [ + "step_3_steps", + "step_4_steps" + ], + + "fr": [ + "step_3_steps", + "step_4_steps" + ], + + "hi": [ + "step_3_steps", + "step_4_steps" + ], + + "it": [ + "step_3_steps", + "step_4_steps" + ], + + "ja": [ + "step_3_steps", + "step_4_steps" + ], + + "nl": [ + "step_3_steps", + "step_4_steps" + ], + + "pl": [ + "step_3_steps", + "step_4_steps" + ], + + "pt": [ + "step_3_steps", + "step_4_steps" + ], + + "ru": [ + "step_3_steps", + "step_4_steps" + ], + + "tr": [ + "step_3_steps", + "step_4_steps" + ], + + "uk": [ + "step_3_steps", + "step_4_steps" + ], + + "zh": [ + "step_3_steps", + "step_4_steps" + ] +} From a76ee0acf2615a41b0527163cbd7e1d20fed226d Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 8 Jan 2024 22:57:03 +0600 Subject: [PATCH 10/46] chore: new liked tracks image --- assets/liked-tracks.jpg | Bin 0 -> 27058 bytes lib/components/library/user_playlists.dart | 30 +++++++++--------- .../sections/header/flexible_header.dart | 3 +- lib/pages/playlist/liked_playlist.dart | 6 +--- 4 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 assets/liked-tracks.jpg diff --git a/assets/liked-tracks.jpg b/assets/liked-tracks.jpg new file mode 100644 index 0000000000000000000000000000000000000000..62dad65ea112e49db84afab026803502c221a249 GIT binary patch literal 27058 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 literal 0 HcmV?d00001 diff --git a/lib/components/library/user_playlists.dart b/lib/components/library/user_playlists.dart index a65c6d0e..32e91ed6 100644 --- a/lib/components/library/user_playlists.dart +++ b/lib/components/library/user_playlists.dart @@ -37,21 +37,21 @@ class UserPlaylists extends HookConsumerWidget { ); final likedTracksPlaylist = useMemoized( - () => PlaylistSimple() - ..name = context.l10n.liked_tracks - ..description = context.l10n.liked_tracks_description - ..type = "playlist" - ..collaborative = false - ..public = false - ..id = "user-liked-tracks" - ..images = [ - Image() - ..height = 300 - ..width = 300 - ..url = - "https://t.scdn.co/images/3099b3803ad9496896c43f22fe9be8c4.png" - ], - [context.l10n]); + () => PlaylistSimple() + ..name = context.l10n.liked_tracks + ..description = context.l10n.liked_tracks_description + ..type = "playlist" + ..collaborative = false + ..public = false + ..id = "user-liked-tracks" + ..images = [ + Image() + ..height = 300 + ..width = 300 + ..url = "assets/liked-tracks.jpg" + ], + [context.l10n], + ); final playlists = useMemoized( () { diff --git a/lib/components/shared/tracks_view/sections/header/flexible_header.dart b/lib/components/shared/tracks_view/sections/header/flexible_header.dart index e16ccbff..19241dc6 100644 --- a/lib/components/shared/tracks_view/sections/header/flexible_header.dart +++ b/lib/components/shared/tracks_view/sections/header/flexible_header.dart @@ -1,6 +1,5 @@ import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -62,7 +61,7 @@ class TrackViewFlexHeader extends HookConsumerWidget { clipBehavior: Clip.hardEdge, decoration: BoxDecoration( image: DecorationImage( - image: CachedNetworkImageProvider(props.image), + image: UniversalImage.imageProvider(props.image), fit: BoxFit.cover, ), ), diff --git a/lib/pages/playlist/liked_playlist.dart b/lib/pages/playlist/liked_playlist.dart index 5972a303..1fb2e1dc 100644 --- a/lib/pages/playlist/liked_playlist.dart +++ b/lib/pages/playlist/liked_playlist.dart @@ -4,7 +4,6 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/components/shared/tracks_view/track_view.dart'; import 'package:spotube/components/shared/tracks_view/track_view_props.dart'; import 'package:spotube/services/queries/queries.dart'; -import 'package:spotube/utils/type_conversion_utils.dart'; class LikedPlaylistPage extends HookConsumerWidget { final PlaylistSimple playlist; @@ -20,10 +19,7 @@ class LikedPlaylistPage extends HookConsumerWidget { return InheritedTrackView( collectionId: playlist.id!, - image: TypeConversionUtils.image_X_UrlString( - playlist.images, - placeholder: ImagePlaceholder.collection, - ), + image: "assets/liked-tracks.jpg", pagination: PaginationProps( hasNextPage: false, isLoading: false, From 3d344bdcddb193e7faf4d292bc5a5286e858d5f6 Mon Sep 17 00:00:00 2001 From: MerkomassDev <70111455+MerkomassDev@users.noreply.github.com> Date: Thu, 11 Jan 2024 05:07:36 +0100 Subject: [PATCH 11/46] docs: Readme.md rephrasing (#914) * Rephrased README.md * Update README.md Put extra emphasis on the fact that Shows and Podcasts can ONLY be accessed with spotify premium * One line fix in README.md last change was unnecessary :) --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2736d1f1..e637f6c6 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Spotube Logo An open source, cross-platform Spotify client compatible across multiple platforms
-utilizing Spotify's data API and YouTube (or Piped.video or JioSaavn) as an audio source,
+utilizing Spotify's data API and YouTube, Piped.video or JioSaavn as an audio source,
eliminating the need for Spotify Premium -Btw it's not another Electron app😉 +Btw it's not just another Electron app 😉 Visit the website Discord Server @@ -26,7 +26,7 @@ Btw it's not another Electron app😉 ## 🌃 Features - 🚫 No ads, thanks to the use of public & free Spotify and YT Music APIs¹ -- ⬇️ Downloadable tracks +- ⬇️ Freely downloadable tracks - 🖥️ 📱 Cross-platform support - 🪶 Small size & less data usage - 🕵️ Anonymous/guest login @@ -40,13 +40,13 @@ Btw it's not another Electron app😉 ### ❌ Unsupported features -- 🗣️ **Spotify Shows & Podcasts:** Shows and Podcasts can **never be supported** because the audio tracks are _only_ available on Spotify and accessing them would require Spotify Premium. +- 🗣️ **Spotify Shows & Podcasts:** Shows and Podcasts will **never be supported** because the audio tracks are _only_ available on Spotify and accessing them would require Spotify Premium. - 🎧 **Spotify Listen Along:** [Coming soon!](https://github.com/KRTirtho/spotube/issues/8) ## 📜 ⬇️ Installation guide -New releases usually appear after 3-4 months.
-This handy table lists all methods you can use to install Spotube: +New versions usually release every 3-4 months.
+This handy table lists all the methods you can use to install Spotube: From 72ed3ce2d17d81cb4c373a35d200124ddd903d62 Mon Sep 17 00:00:00 2001 From: Taha Ghadirian Date: Mon, 22 Jan 2024 06:31:51 +0330 Subject: [PATCH 12/46] actions: Add Up for grab checkbox to issue templates. (#1074) --- .github/ISSUE_TEMPLATE/bug_report.yml | 7 +++++++ .github/ISSUE_TEMPLATE/new_feature.yml | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e0031d17..64ee89d2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -71,3 +71,10 @@ body: description: Anything else you'd like to include? validations: required: false + - type: checkboxes + attributes: + label: Self grab + description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions! + options: + - label: I'm ready to work on this issue! + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/new_feature.yml b/.github/ISSUE_TEMPLATE/new_feature.yml index 9742f91f..7f02ea38 100644 --- a/.github/ISSUE_TEMPLATE/new_feature.yml +++ b/.github/ISSUE_TEMPLATE/new_feature.yml @@ -35,4 +35,11 @@ body: label: Additional information description: Anything else you'd like to include? validations: - required: false \ No newline at end of file + required: false + - type: checkboxes + attributes: + label: Self grab + description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions! + options: + - label: I'm ready to work on this issue! + required: false \ No newline at end of file From db0e4cada61d0442a8851c95e17c17e99651daa7 Mon Sep 17 00:00:00 2001 From: Muhammad Jawad <70428284+m-Jawa-d@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:10:33 +0500 Subject: [PATCH 13/46] docs: update copyright year in README.md (#1100) year changed from 2023 to 2024 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2736d1f1..d009fd92 100644 --- a/README.md +++ b/README.md @@ -304,4 +304,4 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [dart_discord_rpc](https://github.com/alexmercerind/dart_discord_rpc) - Discord Rich Presence for Flutter & Dart apps & games. -

© Copyright Spotube 2023

+

© Copyright Spotube 2024

From 27057ea0c8d83c9701057c18b473f1af4e4e82be Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 22 Jan 2024 17:20:30 +0600 Subject: [PATCH 14/46] fix(macos): system tray shows name and sidebar weird gap #1083 --- .vscode/launch.json | 6 + lib/collections/assets.gen.dart | 3 + lib/components/root/sidebar.dart | 2 +- .../configurators/use_init_sys_tray.dart | 2 +- macos/Podfile | 2 +- macos/Podfile.lock | 135 ++++++++++++------ macos/Runner.xcodeproj/project.pbxproj | 17 +-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 8 files changed, 112 insertions(+), 57 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9add0735..7a1e8b9b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,6 +6,12 @@ "type": "dart", "request": "launch", "program": "lib/main.dart", + }, + { + "name": "spotube (mobile)", + "type": "dart", + "request": "launch", + "program": "lib/main.dart", "args": [ "--flavor", "dev" diff --git a/lib/collections/assets.gen.dart b/lib/collections/assets.gen.dart index ac39cf68..d7149834 100644 --- a/lib/collections/assets.gen.dart +++ b/lib/collections/assets.gen.dart @@ -34,6 +34,8 @@ class Assets { AssetGenImage('assets/bengali-patterns-bg.jpg'); static const AssetGenImage branding = AssetGenImage('assets/branding.png'); static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png'); + static const AssetGenImage likedTracks = + AssetGenImage('assets/liked-tracks.jpg'); static const AssetGenImage placeholder = AssetGenImage('assets/placeholder.png'); static const AssetGenImage spotubeHeroBanner = @@ -74,6 +76,7 @@ class Assets { bengaliPatternsBg, branding, emptyBox, + likedTracks, placeholder, spotubeHeroBanner, spotubeLogoForeground, diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index ac5233ed..9b3fd3ed 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -159,7 +159,7 @@ class Sidebar extends HookConsumerWidget { margin: EdgeInsets.only( bottom: 10, left: 0, - top: kIsMacOS ? 35 : 5, + top: kIsMacOS ? 0 : 5, ), padding: const EdgeInsets.symmetric(horizontal: 6), decoration: BoxDecoration( diff --git a/lib/hooks/configurators/use_init_sys_tray.dart b/lib/hooks/configurators/use_init_sys_tray.dart index db4964ce..8080bea6 100644 --- a/lib/hooks/configurators/use_init_sys_tray.dart +++ b/lib/hooks/configurators/use_init_sys_tray.dart @@ -25,7 +25,7 @@ void useInitSysTray(WidgetRef ref) { } final enabled = !playlist.isFetching; systemTray.value = await DesktopTools.createSystemTrayMenu( - title: DesktopTools.platform.isLinux ? "" : "Spotube", + title: DesktopTools.platform.isWindows ? "Spotube" : "", iconPath: "assets/spotube-logo.png", windowsIconPath: "assets/spotube-logo.ico", items: [ diff --git a/macos/Podfile b/macos/Podfile index fe733905..049abe29 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.13' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 99c0177d..65fe3535 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,101 +1,146 @@ PODS: + - app_links (1.0.0): + - FlutterMacOS - audio_service (0.14.1): - FlutterMacOS - audio_session (0.0.1): - FlutterMacOS - - audioplayers_darwin (0.0.1): + - device_info_plus (0.0.1): - FlutterMacOS - - bitsdojo_window_macos (0.0.1): + - file_selector_macos (0.0.1): - FlutterMacOS - - connectivity_plus_macos (0.0.1): + - flutter_secure_storage_macos (6.1.1): - FlutterMacOS - - ReachabilitySwift - FlutterMacOS (1.0.0) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - macos_ui (0.1.0): + - local_notifier (0.1.0): - FlutterMacOS - - metadata_god (0.0.1): + - media_kit_libs_macos_audio (1.0.4): - FlutterMacOS - - package_info_plus_macos (0.0.1): + - media_kit_native_event_loop (1.0.0): - FlutterMacOS - - path_provider_macos (0.0.1): + - metadata_god (0.0.1) + - package_info_plus (0.0.1): - FlutterMacOS - - ReachabilitySwift (5.0.0) - - shared_preferences_macos (0.0.1): + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - screen_retriever (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter - FlutterMacOS - sqflite (0.0.2): - FlutterMacOS - FMDB (>= 2.7.5) + - system_theme (0.0.1): + - FlutterMacOS + - system_tray (0.0.1): + - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS + - window_manager (0.2.0): + - FlutterMacOS + - window_size (0.0.2): + - FlutterMacOS DEPENDENCIES: + - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`) - audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) - - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) - - connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - - macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`) + - local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`) + - media_kit_libs_macos_audio (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos`) + - media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`) - metadata_god (from `Flutter/ephemeral/.symlinks/plugins/metadata_god/macos`) - - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + - system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`) + - system_tray (from `Flutter/ephemeral/.symlinks/plugins/system_tray/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) + - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) SPEC REPOS: trunk: - FMDB - - ReachabilitySwift EXTERNAL SOURCES: + app_links: + :path: Flutter/ephemeral/.symlinks/plugins/app_links/macos audio_service: :path: Flutter/ephemeral/.symlinks/plugins/audio_service/macos audio_session: :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos - audioplayers_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos - bitsdojo_window_macos: - :path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos - connectivity_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + file_selector_macos: + :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral - macos_ui: - :path: Flutter/ephemeral/.symlinks/plugins/macos_ui/macos + local_notifier: + :path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos + media_kit_libs_macos_audio: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos + media_kit_native_event_loop: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos metadata_god: :path: Flutter/ephemeral/.symlinks/plugins/metadata_god/macos - package_info_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - shared_preferences_macos: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + screen_retriever: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + system_theme: + :path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos + system_tray: + :path: Flutter/ephemeral/.symlinks/plugins/system_tray/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + window_manager: + :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos + window_size: + :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos SPEC CHECKSUMS: + app_links: 4481ed4d71f384b0c3ae5016f4633aa73d32ff67 audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9 audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 - audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c - bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 - connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 + flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - macos_ui: 125c911559d646194386d84c017ad6819122e2db - metadata_god: 55a71136c95eb75ec28142f6fbfc2bcff6f881b1 - package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 + local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff + media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da + media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5 + metadata_god: eceae399d0020475069a5cebc35943ce8562b5d7 + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea - url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc + system_tray: e53c972838c69589ff2e77d6d3abfd71332f9e5d + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 -PODFILE CHECKSUM: a884f6dd3f7494f3892ee6c81feea3a3abbf9153 +PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 9b86152a..f7711c83 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -208,7 +208,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -261,6 +261,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -409,7 +410,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -431,7 +432,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; @@ -489,7 +490,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -536,7 +537,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -558,7 +559,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -579,7 +580,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 741e68bc..8f69f0c6 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Mon, 22 Jan 2024 17:51:12 +0600 Subject: [PATCH 15/46] fix: artist page error #1018 --- .../spotify_endpoints.dart | 56 +++++++++++++++++++ lib/services/queries/artist.dart | 7 ++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/services/custom_spotify_endpoints/spotify_endpoints.dart b/lib/services/custom_spotify_endpoints/spotify_endpoints.dart index 4a55130a..0510e69a 100644 --- a/lib/services/custom_spotify_endpoints/spotify_endpoints.dart +++ b/lib/services/custom_spotify_endpoints/spotify_endpoints.dart @@ -162,4 +162,60 @@ class CustomSpotifyEndpoints { result["tracks"].map((track) => Track.fromJson(track)).toList(), ); } + + Future artist({required String id}) async { + final pathQuery = "$_baseUrl/artists/$id"; + + final res = await _client.get( + Uri.parse(pathQuery), + headers: { + "content-type": "application/json", + if (accessToken.isNotEmpty) "authorization": "Bearer $accessToken", + "accept": "application/json", + }, + ); + + final data = jsonDecode(res.body); + + return Artist.fromJson(_purifyArtistResponse(data)); + } + + Future> relatedArtists({required String id}) async { + final pathQuery = "$_baseUrl/artists/$id/related-artists"; + + final res = await _client.get( + Uri.parse(pathQuery), + headers: { + "content-type": "application/json", + if (accessToken.isNotEmpty) "authorization": "Bearer $accessToken", + "accept": "application/json", + }, + ); + + final data = jsonDecode(res.body); + + return List.castFrom( + data["artists"] + .map((artist) => Artist.fromJson(_purifyArtistResponse(artist))) + .toList(), + ); + } + + Map _purifyArtistResponse(Map data) { + if (data["popularity"] != null) { + data["popularity"] = data["popularity"].toInt(); + } + if (data["followers"]?["total"] != null) { + data["followers"]["total"] = data["followers"]["total"].toInt(); + } + if (data["images"] != null) { + data["images"] = data["images"].map((e) { + e["height"] = e["height"].toInt(); + e["width"] = e["width"].toInt(); + return e; + }).toList(); + } + + return data; + } } diff --git a/lib/services/queries/artist.dart b/lib/services/queries/artist.dart index 1b939c82..5ccc4955 100644 --- a/lib/services/queries/artist.dart +++ b/lib/services/queries/artist.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/hooks/spotify/use_spotify_infinite_query.dart'; import 'package:spotube/hooks/spotify/use_spotify_query.dart'; +import 'package:spotube/provider/custom_spotify_endpoint_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/wikipedia/wikipedia.dart'; import 'package:wikipedia_api/wikipedia_api.dart'; @@ -15,9 +16,10 @@ class ArtistQueries { WidgetRef ref, String artist, ) { + final customSpotify = ref.watch(customSpotifyEndpointProvider); return useSpotifyQuery( "artist-profile/$artist", - (spotify) => spotify.artists.get(artist), + (spotify) => customSpotify.artist(id: artist), ref: ref, ); } @@ -125,10 +127,11 @@ class ArtistQueries { WidgetRef ref, String artist, ) { + final customSpotify = ref.watch(customSpotifyEndpointProvider); return useSpotifyQuery, dynamic>( "artist-related-artist-query/$artist", (spotify) { - return spotify.artists.relatedArtists(artist); + return customSpotify.relatedArtists(id: artist); }, ref: ref, ); From 59e0e6bb659b70831f6e0ae064100381c57f149c Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 22 Jan 2024 18:03:08 +0600 Subject: [PATCH 16/46] fix: track pad horizontal scrolling not working --- .../horizontal_playbutton_card_view.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart b/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart index 2075acbb..dc9d30da 100644 --- a/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart +++ b/lib/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart @@ -60,10 +60,7 @@ class HorizontalPlaybuttonCardView extends HookWidget { onNotification: (notification) => true, child: ScrollConfiguration( behavior: ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, + dragDevices: PointerDeviceKind.values.toSet(), ), child: items.isEmpty ? ListView.builder( From a8e9b824f33add8f6a83f0d147e889eb6beeb442 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 22 Jan 2024 19:02:10 +0600 Subject: [PATCH 17/46] fix: alternative searched sources doesn't play #1059 --- assets/jiosaavn.png | Bin 0 -> 14045 bytes lib/collections/assets.gen.dart | 2 + lib/collections/spotube_icons.dart | 1 + .../player/sibling_tracks_sheet.dart | 54 ++++++++++++++++-- lib/services/sourced_track/enums.dart | 2 +- .../sourced_track/sources/jiosaavn.dart | 25 ++++++-- lib/services/sourced_track/sources/piped.dart | 27 +++++++-- .../sourced_track/sources/youtube.dart | 27 +++++++-- 8 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 assets/jiosaavn.png diff --git a/assets/jiosaavn.png b/assets/jiosaavn.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2d46e4b079fa9d1a60ef10a3aab3dd96750b90 GIT binary patch literal 14045 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 literal 0 HcmV?d00001 diff --git a/lib/collections/assets.gen.dart b/lib/collections/assets.gen.dart index d7149834..2587800e 100644 --- a/lib/collections/assets.gen.dart +++ b/lib/collections/assets.gen.dart @@ -34,6 +34,7 @@ class Assets { AssetGenImage('assets/bengali-patterns-bg.jpg'); static const AssetGenImage branding = AssetGenImage('assets/branding.png'); static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png'); + static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png'); static const AssetGenImage likedTracks = AssetGenImage('assets/liked-tracks.jpg'); static const AssetGenImage placeholder = @@ -76,6 +77,7 @@ class Assets { bengaliPatternsBg, branding, emptyBox, + jiosaavn, likedTracks, placeholder, spotubeHeroBanner, diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index 00010aae..c6acd669 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -109,4 +109,5 @@ abstract class SpotubeIcons { static const normalize = FeatherIcons.barChart2; static const wikipedia = SimpleIcons.wikipedia; static const discord = SimpleIcons.discord; + static const youtube = SimpleIcons.youtube; } diff --git a/lib/components/player/sibling_tracks_sheet.dart b/lib/components/player/sibling_tracks_sheet.dart index cf1429b9..181c363a 100644 --- a/lib/components/player/sibling_tracks_sheet.dart +++ b/lib/components/player/sibling_tracks_sheet.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart' hide Offset; +import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; @@ -19,10 +20,28 @@ import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/services/sourced_track/models/source_info.dart'; import 'package:spotube/services/sourced_track/models/video_info.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart'; +import 'package:spotube/services/sourced_track/sources/jiosaavn.dart'; +import 'package:spotube/services/sourced_track/sources/piped.dart'; import 'package:spotube/services/sourced_track/sources/youtube.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; +final sourceInfoToIconMap = { + YoutubeSourceInfo: const Icon(SpotubeIcons.youtube, color: Color(0xFFFF0000)), + JioSaavnSourceInfo: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(90), + image: DecorationImage( + image: Assets.jiosaavn.provider(), + fit: BoxFit.cover, + ), + ), + ), + PipedSourceInfo: const Icon(SpotubeIcons.piped), +}; + class SiblingTracksSheet extends HookConsumerWidget { final bool floating; const SiblingTracksSheet({ @@ -64,17 +83,34 @@ class SiblingTracksSheet extends HookConsumerWidget { return []; } - final results = await youtubeClient.search.search(searchTerm.trim()); + final resultsYt = await youtubeClient.search.search(searchTerm.trim()); + final resultsJioSaavn = + await jiosaavnClient.search.songs(searchTerm.trim()); - return await Future.wait( - results.map(YoutubeVideoInfo.fromVideo).mapIndexed((i, video) async { + final searchResults = await Future.wait([ + ...resultsJioSaavn.results.mapIndexed((i, song) async { + final siblingType = JioSaavnSourcedTrack.toSiblingType(song); + return siblingType.info; + }), + ...resultsYt + .map(YoutubeVideoInfo.fromVideo) + .mapIndexed((i, video) async { final siblingType = await YoutubeSourcedTrack.toSiblingType(i, video); return siblingType.info; }), - ); + ]); + final activeSourceInfo = + (playlist.activeTrack! as SourcedTrack).sourceInfo; + return searchResults + ..removeWhere((element) => element.id == activeSourceInfo.id) + ..insert( + 0, + activeSourceInfo, + ); }, [ searchTerm, searchMode.value, + playlist.activeTrack, ]); final siblings = useMemoized( @@ -104,6 +140,7 @@ class SiblingTracksSheet extends HookConsumerWidget { final itemBuilder = useCallback( (SourceInfo sourceInfo) { + final icon = sourceInfoToIconMap[sourceInfo.runtimeType]; return ListTile( title: Text(sourceInfo.title), leading: Padding( @@ -118,7 +155,12 @@ class SiblingTracksSheet extends HookConsumerWidget { borderRadius: BorderRadius.circular(5), ), trailing: Text(sourceInfo.duration.toHumanReadableString()), - subtitle: Text(sourceInfo.artist), + subtitle: Row( + children: [ + if (icon != null) icon, + Text(" • ${sourceInfo.artist}"), + ], + ), enabled: playlist.isFetching != true, selected: playlist.isFetching != true && sourceInfo.id == @@ -137,7 +179,7 @@ class SiblingTracksSheet extends HookConsumerWidget { [playlist.isFetching, playlist.activeTrack, siblings], ); - var mediaQuery = MediaQuery.of(context); + final mediaQuery = MediaQuery.of(context); return SafeArea( child: ClipRRect( borderRadius: borderRadius, diff --git a/lib/services/sourced_track/enums.dart b/lib/services/sourced_track/enums.dart index 48ce1cbd..e47ee6bd 100644 --- a/lib/services/sourced_track/enums.dart +++ b/lib/services/sourced_track/enums.dart @@ -15,4 +15,4 @@ enum SourceQualities { low, } -typedef SiblingType = ({SourceInfo info, SourceMap? source}); +typedef SiblingType = ({T info, SourceMap? source}); diff --git a/lib/services/sourced_track/sources/jiosaavn.dart b/lib/services/sourced_track/sources/jiosaavn.dart index a447b0c1..281be998 100644 --- a/lib/services/sourced_track/sources/jiosaavn.dart +++ b/lib/services/sourced_track/sources/jiosaavn.dart @@ -12,6 +12,19 @@ import 'package:spotube/extensions/string.dart'; final jiosaavnClient = JioSaavnClient(); +class JioSaavnSourceInfo extends SourceInfo { + JioSaavnSourceInfo({ + required super.id, + required super.title, + required super.artist, + required super.thumbnail, + required super.pageUrl, + required super.duration, + required super.artistUrl, + required super.album, + }); +} + class JioSaavnSourcedTrack extends SourcedTrack { JioSaavnSourcedTrack({ required super.ref, @@ -70,7 +83,7 @@ class JioSaavnSourcedTrack extends SourcedTrack { static SiblingType toSiblingType(SongResponse result) { final SiblingType sibling = ( - info: SourceInfo( + info: JioSaavnSourceInfo( artist: [ result.primaryArtists, if (result.featuredArtists.isNotEmpty) ", ", @@ -155,12 +168,16 @@ class JioSaavnSourcedTrack extends SourcedTrack { @override Future swapWithSibling(SourceInfo sibling) async { - if (sibling.id == sourceInfo.id || - siblings.none((s) => s.id == sibling.id)) { + if (sibling.id == sourceInfo.id) { return null; } - final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id); + // a sibling source that was fetched from the search results + final isStepSibling = siblings.none((s) => s.id == sibling.id); + + final newSourceInfo = isStepSibling + ? sibling + : siblings.firstWhere((s) => s.id == sibling.id); final newSiblings = siblings.where((s) => s.id != sibling.id).toList() ..insert(0, sourceInfo); diff --git a/lib/services/sourced_track/sources/piped.dart b/lib/services/sourced_track/sources/piped.dart index 0778a7cf..f9e4368d 100644 --- a/lib/services/sourced_track/sources/piped.dart +++ b/lib/services/sourced_track/sources/piped.dart @@ -22,6 +22,19 @@ final pipedProvider = Provider( }, ); +class PipedSourceInfo extends SourceInfo { + PipedSourceInfo({ + required super.id, + required super.title, + required super.artist, + required super.thumbnail, + required super.pageUrl, + required super.duration, + required super.artistUrl, + required super.album, + }); +} + class PipedSourcedTrack extends SourcedTrack { PipedSourcedTrack({ required super.ref, @@ -71,7 +84,7 @@ class PipedSourcedTrack extends SourcedTrack { ref: ref, siblings: [], source: toSourceMap(manifest), - sourceInfo: SourceInfo( + sourceInfo: PipedSourceInfo( id: manifest.id, artist: manifest.uploader, artistUrl: manifest.uploaderUrl, @@ -122,7 +135,7 @@ class PipedSourcedTrack extends SourcedTrack { } final SiblingType sibling = ( - info: SourceInfo( + info: PipedSourceInfo( id: item.id, artist: item.channelName, artistUrl: "https://www.youtube.com/${item.channelId}", @@ -233,12 +246,16 @@ class PipedSourcedTrack extends SourcedTrack { @override Future swapWithSibling(SourceInfo sibling) async { - if (sibling.id == sourceInfo.id || - siblings.none((s) => s.id == sibling.id)) { + if (sibling.id == sourceInfo.id) { return null; } - final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id); + // a sibling source that was fetched from the search results + final isStepSibling = siblings.none((s) => s.id == sibling.id); + + final newSourceInfo = isStepSibling + ? sibling + : siblings.firstWhere((s) => s.id == sibling.id); final newSiblings = siblings.where((s) => s.id != sibling.id).toList() ..insert(0, sourceInfo); diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index 2bcd6e3e..c4105d75 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -17,6 +17,19 @@ final officialMusicRegex = RegExp( caseSensitive: false, ); +class YoutubeSourceInfo extends SourceInfo { + YoutubeSourceInfo({ + required super.id, + required super.title, + required super.artist, + required super.thumbnail, + required super.pageUrl, + required super.duration, + required super.artistUrl, + required super.album, + }); +} + class YoutubeSourcedTrack extends SourcedTrack { YoutubeSourcedTrack({ required super.source, @@ -64,7 +77,7 @@ class YoutubeSourcedTrack extends SourcedTrack { ref: ref, siblings: [], source: toSourceMap(manifest), - sourceInfo: SourceInfo( + sourceInfo: YoutubeSourceInfo( id: item.id.value, artist: item.author, artistUrl: "https://www.youtube.com/channel/${item.channelId}", @@ -117,7 +130,7 @@ class YoutubeSourcedTrack extends SourcedTrack { } final SiblingType sibling = ( - info: SourceInfo( + info: YoutubeSourceInfo( id: item.id, artist: item.channelName, artistUrl: "https://www.youtube.com/channel/${item.channelId}", @@ -217,12 +230,16 @@ class YoutubeSourcedTrack extends SourcedTrack { @override Future swapWithSibling(SourceInfo sibling) async { - if (sibling.id == sourceInfo.id || - siblings.none((s) => s.id == sibling.id)) { + if (sibling.id == sourceInfo.id) { return null; } - final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id); + // a sibling source that was fetched from the search results + final isStepSibling = siblings.none((s) => s.id == sibling.id); + + final newSourceInfo = isStepSibling + ? sibling + : siblings.firstWhere((s) => s.id == sibling.id); final newSiblings = siblings.where((s) => s.id != sibling.id).toList() ..insert(0, sourceInfo); From 682e88e0c55bc0f4708bc0b4681b129e5c61c999 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 22 Jan 2024 19:09:57 +0600 Subject: [PATCH 18/46] fix: releases section is empty when user doesn't follow any artists #1104 --- .../home/sections/new_releases.dart | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/components/home/sections/new_releases.dart b/lib/components/home/sections/new_releases.dart index 77481de1..0f4a046a 100644 --- a/lib/components/home/sections/new_releases.dart +++ b/lib/components/home/sections/new_releases.dart @@ -21,16 +21,21 @@ class HomeNewReleasesSection extends HookConsumerWidget { userArtistsQuery.data?.map((s) => s.id!).toList() ?? const []; final albums = useMemoized( - () => newReleases.pages - .whereType>() - .expand((page) => page.items ?? const []) - .where((album) { - return album.artists - ?.any((artist) => userArtists.contains(artist.id!)) == - true; - }) - .map((album) => TypeConversionUtils.simpleAlbum_X_Album(album)) - .toList(), + () { + final allReleases = newReleases.pages + .whereType>() + .expand((page) => page.items ?? const []) + .map((album) => TypeConversionUtils.simpleAlbum_X_Album(album)); + + final userArtistReleases = allReleases.where((album) { + return album.artists + ?.any((artist) => userArtists.contains(artist.id!)) == + true; + }).toList(); + + if (userArtistReleases.isEmpty) return allReleases.toList(); + return userArtistReleases; + }, [newReleases.pages], ); From 79839329b0970acccb0c566a31eee508adbc8557 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 23 Jan 2024 22:44:00 +0600 Subject: [PATCH 19/46] feat: add spotify friends activity (#1130) * feat: add spotify friend endpoint * feat: add friend activity in home screen * fix: when no friends, dummy UI still shows giving the user a false hope of friendship :'( --- lib/collections/fake.dart | 32 +++++ lib/components/home/sections/friends.dart | 95 ++++++++++++ .../home/sections/friends/friend_item.dart | 136 ++++++++++++++++++ .../shared/page_window_title_bar.dart | 4 +- lib/l10n/app_en.arb | 3 +- lib/main.dart | 4 +- lib/models/spotify_friends.dart | 111 ++++++++++++++ lib/models/spotify_friends.g.dart | 65 +++++++++ lib/pages/home/home.dart | 2 + .../spotify_endpoints.dart | 14 +- lib/services/queries/user.dart | 13 ++ untranslated_messages.json | 51 ++++--- 12 files changed, 507 insertions(+), 23 deletions(-) create mode 100644 lib/components/home/sections/friends.dart create mode 100644 lib/components/home/sections/friends/friend_item.dart create mode 100644 lib/models/spotify_friends.dart create mode 100644 lib/models/spotify_friends.g.dart diff --git a/lib/collections/fake.dart b/lib/collections/fake.dart index 10cf2819..8f5f9e8b 100644 --- a/lib/collections/fake.dart +++ b/lib/collections/fake.dart @@ -1,5 +1,6 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/extensions/track.dart'; +import 'package:spotube/models/spotify_friends.dart'; abstract class FakeData { static final Image image = Image() @@ -164,4 +165,35 @@ abstract class FakeData { ..icons = [image] ..id = "1" ..name = "category"; + + static final friends = SpotifyFriends( + friends: [ + for (var i = 0; i < 3; i++) + SpotifyFriendActivity( + user: const SpotifyFriend( + name: "name", + imageUrl: "imageUrl", + uri: "uri", + ), + track: SpotifyActivityTrack( + name: "name", + artist: const SpotifyActivityArtist( + name: "name", + uri: "uri", + ), + album: const SpotifyActivityAlbum( + name: "name", + uri: "uri", + ), + context: SpotifyActivityContext( + name: "name", + index: i, + uri: "uri", + ), + imageUrl: "imageUrl", + uri: "uri", + ), + ), + ], + ); } diff --git a/lib/components/home/sections/friends.dart b/lib/components/home/sections/friends.dart new file mode 100644 index 00000000..ef24b8d5 --- /dev/null +++ b/lib/components/home/sections/friends.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:skeletonizer/skeletonizer.dart'; +import 'package:spotube/collections/fake.dart'; +import 'package:spotube/components/home/sections/friends/friend_item.dart'; +import 'package:spotube/hooks/utils/use_breakpoint_value.dart'; +import 'package:spotube/models/spotify_friends.dart'; +import 'package:spotube/services/queries/queries.dart'; + +class HomePageFriendsSection extends HookConsumerWidget { + const HomePageFriendsSection({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, ref) { + final friendsQuery = useQueries.user.friendActivity(ref); + final friends = friendsQuery.data?.friends ?? FakeData.friends.friends; + + final groupCount = useBreakpointValue( + sm: 3, + xs: 2, + md: 4, + lg: 5, + xl: 6, + xxl: 7, + ); + + final friendGroup = friends.fold>>( + [], + (previousValue, element) { + if (previousValue.isEmpty) { + return [ + [element] + ]; + } + + final lastGroup = previousValue.last; + if (lastGroup.length < groupCount) { + return [ + ...previousValue.sublist(0, previousValue.length - 1), + [...lastGroup, element] + ]; + } + + return [ + ...previousValue, + [element] + ]; + }, + ); + + if (!friendsQuery.isLoading && + (!friendsQuery.hasData || friendsQuery.data!.friends.isEmpty)) { + return const SliverToBoxAdapter( + child: SizedBox.shrink(), + ); + } + + return Skeletonizer.sliver( + enabled: friendsQuery.isLoading, + child: SliverMainAxisGroup( + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Friends', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + SliverToBoxAdapter( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final group in friendGroup) + Row( + children: [ + for (final friend in group) + Padding( + padding: const EdgeInsets.all(8.0), + child: FriendItem(friend: friend), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/components/home/sections/friends/friend_item.dart b/lib/components/home/sections/friends/friend_item.dart new file mode 100644 index 00000000..fcdadab7 --- /dev/null +++ b/lib/components/home/sections/friends/friend_item.dart @@ -0,0 +1,136 @@ +import 'package:fl_query_hooks/fl_query_hooks.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/shared/image/universal_image.dart'; +import 'package:spotube/models/spotify_friends.dart'; +import 'package:spotube/provider/spotify_provider.dart'; + +class FriendItem extends HookConsumerWidget { + final SpotifyFriendActivity friend; + const FriendItem({ + Key? key, + required this.friend, + }) : super(key: key); + + @override + Widget build(BuildContext context, ref) { + final ThemeData( + textTheme: textTheme, + colorScheme: colorScheme, + ) = Theme.of(context); + + final queryClient = useQueryClient(); + final spotify = ref.watch(spotifyProvider); + + return Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: colorScheme.surfaceVariant.withOpacity(0.3), + borderRadius: BorderRadius.circular(15), + ), + constraints: const BoxConstraints( + minWidth: 300, + ), + height: 80, + child: Row( + children: [ + CircleAvatar( + backgroundImage: UniversalImage.imageProvider( + friend.user.imageUrl, + ), + ), + const Gap(8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + friend.user.name, + style: textTheme.bodyLarge, + ), + RichText( + text: TextSpan( + style: textTheme.bodySmall, + children: [ + TextSpan( + text: friend.track.name, + recognizer: TapGestureRecognizer() + ..onTap = () { + context.push("/track/${friend.track.id}"); + }, + ), + const TextSpan(text: " • "), + const WidgetSpan( + child: Icon( + SpotubeIcons.artist, + size: 12, + ), + ), + TextSpan( + text: " ${friend.track.artist.name}", + recognizer: TapGestureRecognizer() + ..onTap = () { + context.push( + "/artist/${friend.track.artist.id}", + ); + }, + ), + const TextSpan(text: "\n"), + TextSpan( + text: friend.track.context.name, + recognizer: TapGestureRecognizer() + ..onTap = () async { + context.push( + "/${friend.track.context.path}", + extra: !friend.track.context.path + .startsWith("album") + ? null + : await queryClient.fetchQuery( + "album/${friend.track.album.id}", + () => spotify.albums.get( + friend.track.album.id, + ), + ), + ); + }, + ), + const TextSpan(text: " • "), + const WidgetSpan( + child: Icon( + SpotubeIcons.album, + size: 12, + ), + ), + TextSpan( + text: " ${friend.track.album.name}", + recognizer: TapGestureRecognizer() + ..onTap = () async { + final album = + await queryClient.fetchQuery( + "album/${friend.track.album.id}", + () => spotify.albums.get( + friend.track.album.id, + ), + ); + if (context.mounted) { + context.push( + "/album/${friend.track.album.id}", + extra: album, + ); + } + }, + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/components/shared/page_window_title_bar.dart b/lib/components/shared/page_window_title_bar.dart index d8e20184..4f522e0c 100644 --- a/lib/components/shared/page_window_title_bar.dart +++ b/lib/components/shared/page_window_title_bar.dart @@ -2,14 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; -import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/utils/platform.dart'; import 'package:titlebar_buttons/titlebar_buttons.dart'; import 'dart:math'; import 'package:flutter/foundation.dart' show kIsWeb; -import 'dart:io' show Platform, exit; +import 'dart:io' show Platform; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; -import 'package:local_notifier/local_notifier.dart'; class PageWindowTitleBar extends StatefulHookConsumerWidget implements PreferredSizeWidget { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 82877ea1..6b61cbae 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -284,5 +284,6 @@ "discord_rich_presence": "Discord Rich Presence", "browse_all": "Browse All", "genres": "Genres", - "explore_genres": "Explore Genres" + "explore_genres": "Explore Genres", + "friends": "Friends" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f96920a1..b6afa85c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -228,7 +228,9 @@ class SpotubeState extends ConsumerState { builder: (context, child) { return DevicePreview.appBuilder( context, - DragToResizeArea(child: child!), + DesktopTools.platform.isDesktop + ? DragToResizeArea(child: child!) + : child, ); }, themeMode: themeMode, diff --git a/lib/models/spotify_friends.dart b/lib/models/spotify_friends.dart new file mode 100644 index 00000000..b386fb81 --- /dev/null +++ b/lib/models/spotify_friends.dart @@ -0,0 +1,111 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'spotify_friends.g.dart'; + +@JsonSerializable(createToJson: false) +class SpotifyFriend { + final String uri; + final String name; + final String imageUrl; + + const SpotifyFriend({ + required this.uri, + required this.name, + required this.imageUrl, + }); + + factory SpotifyFriend.fromJson(Map json) => + _$SpotifyFriendFromJson(json); + + String get id => uri.split(":").last; +} + +@JsonSerializable(createToJson: false) +class SpotifyActivityArtist { + final String uri; + final String name; + + const SpotifyActivityArtist({required this.uri, required this.name}); + + factory SpotifyActivityArtist.fromJson(Map json) => + _$SpotifyActivityArtistFromJson(json); + + String get id => uri.split(":").last; +} + +@JsonSerializable(createToJson: false) +class SpotifyActivityAlbum { + final String uri; + final String name; + + const SpotifyActivityAlbum({required this.uri, required this.name}); + + factory SpotifyActivityAlbum.fromJson(Map json) => + _$SpotifyActivityAlbumFromJson(json); + + String get id => uri.split(":").last; +} + +@JsonSerializable(createToJson: false) +class SpotifyActivityContext { + final String uri; + final String name; + final num index; + + const SpotifyActivityContext({ + required this.uri, + required this.name, + required this.index, + }); + + factory SpotifyActivityContext.fromJson(Map json) => + _$SpotifyActivityContextFromJson(json); + + String get id => uri.split(":").last; + String get path => uri.split(":").skip(1).join("/"); +} + +@JsonSerializable(createToJson: false) +class SpotifyActivityTrack { + final String uri; + final String name; + final String imageUrl; + final SpotifyActivityArtist artist; + final SpotifyActivityAlbum album; + final SpotifyActivityContext context; + + const SpotifyActivityTrack({ + required this.uri, + required this.name, + required this.imageUrl, + required this.artist, + required this.album, + required this.context, + }); + + factory SpotifyActivityTrack.fromJson(Map json) => + _$SpotifyActivityTrackFromJson(json); + + String get id => uri.split(":").last; +} + +@JsonSerializable(createToJson: false) +class SpotifyFriendActivity { + SpotifyFriend user; + SpotifyActivityTrack track; + + SpotifyFriendActivity({required this.user, required this.track}); + + factory SpotifyFriendActivity.fromJson(Map json) => + _$SpotifyFriendActivityFromJson(json); +} + +@JsonSerializable(createToJson: false) +class SpotifyFriends { + List friends; + + SpotifyFriends({required this.friends}); + + factory SpotifyFriends.fromJson(Map json) => + _$SpotifyFriendsFromJson(json); +} diff --git a/lib/models/spotify_friends.g.dart b/lib/models/spotify_friends.g.dart new file mode 100644 index 00000000..4a32dd09 --- /dev/null +++ b/lib/models/spotify_friends.g.dart @@ -0,0 +1,65 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'spotify_friends.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SpotifyFriend _$SpotifyFriendFromJson(Map json) => + SpotifyFriend( + uri: json['uri'] as String, + name: json['name'] as String, + imageUrl: json['imageUrl'] as String, + ); + +SpotifyActivityArtist _$SpotifyActivityArtistFromJson( + Map json) => + SpotifyActivityArtist( + uri: json['uri'] as String, + name: json['name'] as String, + ); + +SpotifyActivityAlbum _$SpotifyActivityAlbumFromJson( + Map json) => + SpotifyActivityAlbum( + uri: json['uri'] as String, + name: json['name'] as String, + ); + +SpotifyActivityContext _$SpotifyActivityContextFromJson( + Map json) => + SpotifyActivityContext( + uri: json['uri'] as String, + name: json['name'] as String, + index: json['index'] as num, + ); + +SpotifyActivityTrack _$SpotifyActivityTrackFromJson( + Map json) => + SpotifyActivityTrack( + uri: json['uri'] as String, + name: json['name'] as String, + imageUrl: json['imageUrl'] as String, + artist: SpotifyActivityArtist.fromJson( + json['artist'] as Map), + album: + SpotifyActivityAlbum.fromJson(json['album'] as Map), + context: SpotifyActivityContext.fromJson( + json['context'] as Map), + ); + +SpotifyFriendActivity _$SpotifyFriendActivityFromJson( + Map json) => + SpotifyFriendActivity( + user: SpotifyFriend.fromJson(json['user'] as Map), + track: + SpotifyActivityTrack.fromJson(json['track'] as Map), + ); + +SpotifyFriends _$SpotifyFriendsFromJson(Map json) => + SpotifyFriends( + friends: (json['friends'] as List) + .map((e) => SpotifyFriendActivity.fromJson(e as Map)) + .toList(), + ); diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 9b33a66c..eb2ddb94 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -3,6 +3,7 @@ import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/components/home/sections/featured.dart'; +import 'package:spotube/components/home/sections/friends.dart'; import 'package:spotube/components/home/sections/genres.dart'; import 'package:spotube/components/home/sections/made_for_user.dart'; import 'package:spotube/components/home/sections/new_releases.dart'; @@ -31,6 +32,7 @@ class HomePage extends HookConsumerWidget { HomeNewReleasesSection(), ], ), + const HomePageFriendsSection(), const SliverSafeArea(sliver: HomeMadeForUserSection()), ], ), diff --git a/lib/services/custom_spotify_endpoints/spotify_endpoints.dart b/lib/services/custom_spotify_endpoints/spotify_endpoints.dart index 0510e69a..e27b701b 100644 --- a/lib/services/custom_spotify_endpoints/spotify_endpoints.dart +++ b/lib/services/custom_spotify_endpoints/spotify_endpoints.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:spotify/spotify.dart'; +import 'package:spotube/models/spotify_friends.dart'; class CustomSpotifyEndpoints { static const _baseUrl = 'https://api.spotify.com/v1'; @@ -163,6 +164,18 @@ class CustomSpotifyEndpoints { ); } + Future getFriendActivity() async { + final res = await _client.get( + Uri.parse("https://guc-spclient.spotify.com/presence-view/v1/buddylist"), + headers: { + "content-type": "application/json", + "authorization": "Bearer $accessToken", + "accept": "application/json", + }, + ); + return SpotifyFriends.fromJson(jsonDecode(res.body)); + } + Future artist({required String id}) async { final pathQuery = "$_baseUrl/artists/$id"; @@ -174,7 +187,6 @@ class CustomSpotifyEndpoints { "accept": "application/json", }, ); - final data = jsonDecode(res.body); return Artist.fromJson(_purifyArtistResponse(data)); diff --git a/lib/services/queries/user.dart b/lib/services/queries/user.dart index 40799c1e..82af600f 100644 --- a/lib/services/queries/user.dart +++ b/lib/services/queries/user.dart @@ -3,7 +3,9 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/hooks/spotify/use_spotify_query.dart'; +import 'package:spotube/models/spotify_friends.dart'; import 'package:spotube/provider/authentication_provider.dart'; +import 'package:spotube/provider/custom_spotify_endpoint_provider.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class UserQueries { @@ -37,4 +39,15 @@ class UserQueries { ref: ref, ); } + + Query friendActivity(WidgetRef ref) { + final customSpotify = ref.read(customSpotifyEndpointProvider); + return useSpotifyQuery( + "friend-activity", + (spotify) { + return customSpotify.getFriendActivity(); + }, + ref: ref, + ); + } } diff --git a/untranslated_messages.json b/untranslated_messages.json index 59b26614..2fd7ceca 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1,86 +1,103 @@ { "ar": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "bn": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "ca": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "de": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "es": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "fa": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "fr": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "hi": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "it": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "ja": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "nl": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "pl": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "pt": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "ru": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "tr": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "uk": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ], "zh": [ "step_3_steps", - "step_4_steps" + "step_4_steps", + "friends" ] } From e58e18de33d7bc6fb0e4ddd7ccf6ea14472642b1 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 23 Jan 2024 23:13:13 +0600 Subject: [PATCH 20/46] fix: better error message for failing to find lyrics #1085 --- lib/pages/lyrics/plain_lyrics.dart | 2 +- lib/pages/lyrics/synced_lyrics.dart | 2 +- lib/services/queries/lyrics.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/lyrics/plain_lyrics.dart b/lib/pages/lyrics/plain_lyrics.dart index f6eaa5d5..0ac1ac66 100644 --- a/lib/pages/lyrics/plain_lyrics.dart +++ b/lib/pages/lyrics/plain_lyrics.dart @@ -73,7 +73,7 @@ class PlainLyrics extends HookConsumerWidget { return const ShimmerLyrics(); } else if (lyricsQuery.hasError) { return Text( - "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${lyricsQuery.error.toString()}", + "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${lyricsQuery.error.toString().replaceAll("Exception: ", "")}", style: textTheme.bodyLarge?.copyWith( color: palette.bodyTextColor, ), diff --git a/lib/pages/lyrics/synced_lyrics.dart b/lib/pages/lyrics/synced_lyrics.dart index 04d7c04a..0dc0b1f1 100644 --- a/lib/pages/lyrics/synced_lyrics.dart +++ b/lib/pages/lyrics/synced_lyrics.dart @@ -190,7 +190,7 @@ class SyncedLyrics extends HookConsumerWidget { else if (playlist.activeTrack != null && (timedLyricsQuery.hasError)) Text( - "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${timedLyricsQuery.error.toString()}", + "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${timedLyricsQuery.error.toString().replaceAll("Exception: ", "")}", style: bodyTextTheme, ) else if (isUnSyncLyric == true) diff --git a/lib/services/queries/lyrics.dart b/lib/services/queries/lyrics.dart index faa5bdec..618f960f 100644 --- a/lib/services/queries/lyrics.dart +++ b/lib/services/queries/lyrics.dart @@ -63,8 +63,8 @@ class LyricsQueries { /// Special thanks to [raptag](https://github.com/raptag) for discovering this /// jem - Query spotifySynced(WidgetRef ref, Track? track) { - return useSpotifyQuery( + Query spotifySynced(WidgetRef ref, Track? track) { + return useSpotifyQuery( "spotify-synced-lyrics/${track?.id}}", (spotify) async { if (track == null) { From a6cb78380d57ea9f3f68c82aee2c211e38287813 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 00:22:14 +0600 Subject: [PATCH 21/46] chore: show icon with error msg #1085 --- lib/collections/spotube_icons.dart | 1 + lib/l10n/app_en.arb | 3 +- lib/pages/lyrics/plain_lyrics.dart | 23 ++++++++++--- lib/pages/lyrics/synced_lyrics.dart | 21 ++++++++---- untranslated_messages.json | 51 +++++++++++++++++++---------- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index c6acd669..65e6c1a0 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -41,6 +41,7 @@ abstract class SpotubeIcons { static const clock = FeatherIcons.clock; static const lyrics = Icons.lyrics_rounded; static const lyricsOff = Icons.lyrics_outlined; + static const noLyrics = Icons.music_off_outlined; static const logout = FeatherIcons.logOut; static const login = FeatherIcons.logIn; static const dashboard = FeatherIcons.grid; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 6b61cbae..07df5f06 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -285,5 +285,6 @@ "browse_all": "Browse All", "genres": "Genres", "explore_genres": "Explore Genres", - "friends": "Friends" + "friends": "Friends", + "no_lyrics_available": "Sorry, unable find lyrics for this track" } \ No newline at end of file diff --git a/lib/pages/lyrics/plain_lyrics.dart b/lib/pages/lyrics/plain_lyrics.dart index 0ac1ac66..bee5114d 100644 --- a/lib/pages/lyrics/plain_lyrics.dart +++ b/lib/pages/lyrics/plain_lyrics.dart @@ -1,12 +1,15 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:palette_generator/palette_generator.dart'; import 'package:spotify/spotify.dart'; +import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/lyrics/zoom_controls.dart'; import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart'; import 'package:spotube/extensions/constrains.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; @@ -72,10 +75,22 @@ class PlainLyrics extends HookConsumerWidget { if (lyricsQuery.isLoading || lyricsQuery.isRefreshing) { return const ShimmerLyrics(); } else if (lyricsQuery.hasError) { - return Text( - "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${lyricsQuery.error.toString().replaceAll("Exception: ", "")}", - style: textTheme.bodyLarge?.copyWith( - color: palette.bodyTextColor, + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.l10n.no_lyrics_available, + style: textTheme.bodyLarge?.copyWith( + color: palette.bodyTextColor, + ), + textAlign: TextAlign.center, + ), + const Gap(26), + const Icon(SpotubeIcons.noLyrics, size: 60), + ], ), ); } diff --git a/lib/pages/lyrics/synced_lyrics.dart b/lib/pages/lyrics/synced_lyrics.dart index 0dc0b1f1..ddef1c65 100644 --- a/lib/pages/lyrics/synced_lyrics.dart +++ b/lib/pages/lyrics/synced_lyrics.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:palette_generator/palette_generator.dart'; import 'package:spotify/spotify.dart' hide Offset; @@ -7,6 +8,7 @@ import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/lyrics/zoom_controls.dart'; import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart'; import 'package:spotube/extensions/constrains.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/controllers/use_auto_scroll_controller.dart'; import 'package:spotube/components/lyrics/use_synced_lyrics.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; @@ -188,12 +190,19 @@ class SyncedLyrics extends HookConsumerWidget { child: ShimmerLyrics(), ) else if (playlist.activeTrack != null && - (timedLyricsQuery.hasError)) - Text( - "Sorry, no Lyrics were found for `${playlist.activeTrack?.name}` :'(\n${timedLyricsQuery.error.toString().replaceAll("Exception: ", "")}", - style: bodyTextTheme, - ) - else if (isUnSyncLyric == true) + (timedLyricsQuery.hasError)) ...[ + Container( + alignment: Alignment.center, + padding: const EdgeInsets.all(16), + child: Text( + context.l10n.no_lyrics_available, + style: bodyTextTheme, + textAlign: TextAlign.center, + ), + ), + const Gap(26), + const Icon(SpotubeIcons.noLyrics, size: 60), + ] else if (isUnSyncLyric == true) Expanded( child: Center( child: RichText( diff --git a/untranslated_messages.json b/untranslated_messages.json index 2fd7ceca..45a6df11 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -2,102 +2,119 @@ "ar": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "bn": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "ca": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "de": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "es": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "fa": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "fr": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "hi": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "it": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "ja": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "nl": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "pl": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "pt": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "ru": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "tr": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "uk": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ], "zh": [ "step_3_steps", "step_4_steps", - "friends" + "friends", + "no_lyrics_available" ] } From 7c0689c056f75a27de33d2a03cec47836ec4de16 Mon Sep 17 00:00:00 2001 From: Meenbeese Date: Tue, 23 Jan 2024 22:07:33 -0500 Subject: [PATCH 22/46] docs: Reword and improve the support message (#1084) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e637f6c6..11f1db2e 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Btw it's not just another Electron app 😉 - 📖 Open source/libre software - 🔉 Playback control is done locally, not on the server -**¹** It is still **recommended** to support the creators by watching/liking/subscribing to the artists' YouTube channels or liking their tracks on Spotify (or purchasing a Spotify Premium subscription too). +**¹** It is still **recommended** to support creators by engaging with their YouTube channels/Spotify tracks (or preferably by buying their merch/concert tickets/physical media). ### ❌ Unsupported features From c3ebf56ac149b0af8815a5533fe6c386df743440 Mon Sep 17 00:00:00 2001 From: Nabraj Khadka <40161692+iamnabink@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:37:49 +0545 Subject: [PATCH 23/46] =?UTF-8?q?feat(translations):=20add=20Nepali=20(?= =?UTF-8?q?=E0=A4=A8=E0=A5=87=E0=A4=AA=E0=A4=BE=E0=A4=B2=E0=A5=80)=20trans?= =?UTF-8?q?lations=20(#1111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * actions: Add Up for grab checkbox to issue templates. (#1074) * docs: update copyright year in README.md (#1100) year changed from 2023 to 2024 * feat(translations): add Nepali (नेपाली) translations --------- Co-authored-by: Taha Ghadirian Co-authored-by: Muhammad Jawad <70428284+m-Jawa-d@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.yml | 7 + .github/ISSUE_TEMPLATE/new_feature.yml | 9 +- README.md | 2 +- lib/collections/language_codes.dart | 8 +- lib/l10n/app_ne.arb | 288 +++++++++++++++++++++++++ lib/l10n/l10n.dart | 1 + 6 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 lib/l10n/app_ne.arb diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e0031d17..64ee89d2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -71,3 +71,10 @@ body: description: Anything else you'd like to include? validations: required: false + - type: checkboxes + attributes: + label: Self grab + description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions! + options: + - label: I'm ready to work on this issue! + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/new_feature.yml b/.github/ISSUE_TEMPLATE/new_feature.yml index 9742f91f..7f02ea38 100644 --- a/.github/ISSUE_TEMPLATE/new_feature.yml +++ b/.github/ISSUE_TEMPLATE/new_feature.yml @@ -35,4 +35,11 @@ body: label: Additional information description: Anything else you'd like to include? validations: - required: false \ No newline at end of file + required: false + - type: checkboxes + attributes: + label: Self grab + description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions! + options: + - label: I'm ready to work on this issue! + required: false \ No newline at end of file diff --git a/README.md b/README.md index 11f1db2e..791d5da0 100644 --- a/README.md +++ b/README.md @@ -304,4 +304,4 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [dart_discord_rpc](https://github.com/alexmercerind/dart_discord_rpc) - Discord Rich Presence for Flutter & Dart apps & games. -

© Copyright Spotube 2023

+

© Copyright Spotube 2024

diff --git a/lib/collections/language_codes.dart b/lib/collections/language_codes.dart index de6f6d1c..4554de63 100644 --- a/lib/collections/language_codes.dart +++ b/lib/collections/language_codes.dart @@ -452,10 +452,10 @@ abstract class LanguageLocals { // name: "North Ndebele", // nativeName: "isiNdebele", // ), - // "ne": const ISOLanguageName( - // name: "Nepali", - // nativeName: "नेपाली", - // ), + "ne": const ISOLanguageName( + name: "Nepali", + nativeName: "नेपाली", + ), // "ng": const ISOLanguageName( // name: "Ndonga", // nativeName: "Owambo", diff --git a/lib/l10n/app_ne.arb b/lib/l10n/app_ne.arb new file mode 100644 index 00000000..9fca9ea4 --- /dev/null +++ b/lib/l10n/app_ne.arb @@ -0,0 +1,288 @@ +{ + "guest": "अतिथि", + "browse": "ब्राउज़ गर्नुहोस्", + "search": "खोजी गर्नुहोस्", + "library": "पुस्तकालय", + "lyrics": "गीतको शब्द", + "settings": "सेटिङ", + "genre_categories_filter": "शैली वा शैलीहरू फिल्टर गर्नुहोस्...", + "genre": "शैली", + "personalized": "व्यक्तिगत", + "featured": "विशेष", + "new_releases": "नयाँ रिलिज", + "songs": "गीतहरू", + "playing_track": "{track} बज्यो", + "queue_clear_alert": "यो हालको कतारलाई हटाउँछ। {track_length} ट्र्याकहरू हटाईन्छ\nके तपाईं जारी राख्न चाहनुहुन्छ?", + "load_more": "थप लोड गर्नुहोस्", + "playlists": "प्लेलिस्टहरू", + "artists": "कलाकारहरू", + "albums": "आल्बमहरू", + "tracks": "ट्र्याकहरू", + "downloads": "डाउनलोडहरू", + "filter_playlists": "तपाईंको प्लेलिस्टहरू फिल्टर गर्नुहोस्...", + "liked_tracks": "मन परेका ट्र्याकहरू", + "liked_tracks_description": "तपाईंको मन परेका सबै ट्र्याकहरू", + "create_playlist": "प्लेलिस्ट बनाउनुहोस्", + "create_a_playlist": "प्लेलिस्ट बनाउनुहोस्", + "update_playlist": "प्लेलिस्ट अपडेट गर्नुहोस्", + "create": "बनाउनुहोस्", + "cancel": "रद्द गर्नुहोस्", + "update": "अपडेट गर्नुहोस्", + "playlist_name": "प्लेलिस्टको नाम", + "name_of_playlist": "प्लेलिस्टको नाम", + "description": "विवरण", + "public": "सार्वजनिक", + "collaborative": "सहकारी", + "search_local_tracks": "स्थानीय ट्र्याकहरू खोजी गर्नुहोस्...", + "play": "बजाउनुहोस्", + "delete": "मेटाउनुहोस्", + "none": "कुनै पनि होइन", + "sort_a_z": "A-Zमा क्रमबद्ध गर्नुहोस्", + "sort_z_a": "Z-Aमा क्रमबद्ध गर्नुहोस्", + "sort_artist": "कलाकारबाट क्रमबद्ध गर्नुहोस्", + "sort_album": "आल्बमबाट क्रमबद्ध गर्नुहोस्", + "sort_tracks": "ट्र्याकहरूलाई क्रमबद्ध गर्नुहोस्", + "currently_downloading": "हाल डाउनलोड गर्दैछ ({tracks_length})", + "cancel_all": "सब रद्द गर्नुहोस्", + "filter_artist": "कलाकारहरूलाई फिल्टर गर्नुहोस्...", + "followers": "{followers} अनुयायीहरू", + "add_artist_to_blacklist": "कलाकारलाई कालोसूचीमा थप्नुहोस्", + "top_tracks": "शीर्ष ट्र्याकहरू", + "fans_also_like": "अनुयायीहरू पनि लाइक गर्छन्", + "loading": "लोड हुँदैछ...", + "artist": "कलाकार", + "blacklisted": "कालोसूचीमा", + "following": "फल्लो गर्दै", + "follow": "फल्लो गर्नुहोस्", + "artist_url_copied": "कलाकार URL क्लिपबोर्डमा प्रतिलिपि गरिएको छ", + "added_to_queue": "{tracks} ट्र्याकहरूलाई कतारमा थपिएको छ", + "filter_albums": "आल्बमहरूलाई फिल्टर गर्नुहोस्...", + "synced": "सिङ्क गरिएको", + "plain": "साधा", + "shuffle": "शफल", + "search_tracks": "ट्र्याकहरू खोजी गर्नुहोस्...", + "released": "रिलिज गरिएको", + "error": "त्रुटि {error}", + "title": "शीर्षक", + "time": "समय", + "more_actions": "थप कार्यहरू", + "download_count": "डाउनलोड ({count})", + "add_count_to_playlist": "प्लेलिस्टमा थप्नुहोस् ({count})", + "add_count_to_queue": "कतारमा थप्नुहोस् ({count})", + "play_count_next": "प्लेगरी गर्नुहोस् ({count})", + "album": "आल्बम", + "copied_to_clipboard": "{data} क्लिपबोर्डमा प्रतिलिपि गरिएको छ", + "add_to_following_playlists": "{track} लाई तलका प्लेलिस्टमा थप्नुहोस्", + "add": "थप्नुहोस्", + "added_track_to_queue": "{track} लाई कतारमा थपिएको छ", + "add_to_queue": "कतारमा थप्नुहोस्", + "track_will_play_next": "{track} अरूलाई पहिलोमा बज्नेछ", + "play_next": "पछिबजाउनुहोस्", + "removed_track_from_queue": "{track} लाई कतारबाट हटाइएको छ", + "remove_from_queue": "कतारबाट हटाउनुहोस्", + "remove_from_favorites": "पसन्दीदामा बाट हटाउनुहोस्", + "save_as_favorite": "पसन्दीदा बनाउनुहोस्", + "add_to_playlist": "प्लेलिस्टमा थप्नुहोस्", + "remove_from_playlist": "प्लेलिस्टबाट हटाउनुहोस्", + "add_to_blacklist": "कालोसूचीमा थप्नुहोस्", + "remove_from_blacklist": "कालोसूचीबाट हटाउनुहोस्", + "share": "साझा गर्नुहोस्", + "mini_player": "मिनि प्लेयर", + "slide_to_seek": "अगाडि वा पछाडि खोजी गर्नका लागि स्लाइड गर्नुहोस्", + "shuffle_playlist": "प्लेलिस्ट शफल गर्नुहोस्", + "unshuffle_playlist": "प्लेलिस्ट शफल नगर्नुहोस्", + "previous_track": "पूर्व ट्र्याक", + "next_track": "अरू ट्र्याक", + "pause_playback": "प्लेब्याक रोक्नुहोस्", + "resume_playback": "प्लेब्याक पुनः सुरु गर्नुहोस्", + "loop_track": "ट्र्याकलाई दोहोरोपट्टी बजाउनुहोस्", + "repeat_playlist": "प्लेलिस्ट पुनः बजाउनुहोस्", + "queue": "कतार", + "alternative_track_sources": "वैकल्पिक ट्र्याक स्रोतहरू", + "download_track": "ट्र्याक डाउनलोड गर्नुहोस्", + "tracks_in_queue": "कतारमा {tracks} ट्र्याकहरू", + "clear_all": "सब मेटाउनुहोस्", + "show_hide_ui_on_hover": "हवर गरेपछि UI देखाउनुहोस्/लुकाउनुहोस्", + "always_on_top": "सधैं टपमा राख्नुहोस्", + "exit_mini_player": "मिनि प्लेयर बाट बाहिर निस्कनुहोस्", + "download_location": "डाउनलोड स्थान", + "account": "खाता", + "login_with_spotify": "तपाईंको Spotify खातासँग लगइन गर्नुहोस्", + "connect_with_spotify": "Spotify सँग जडान गर्नुहोस्", + "logout": "बाहिर निस्कनुहोस्", + "logout_of_this_account": "यो खाताबाट बाहिर निस्कनुहोस्", + "language_region": "भाषा र क्षेत्र", + "language": "भाषा", + "system_default": "सिस्टम पूर्वनिर्धारित", + "market_place_region": "बजार स्थान", + "recommendation_country":"सिफारिस गरिएको देश", + "appearance": "दृष्टिकोण", + "layout_mode": "लेआउट मोड", + "override_layout_settings": "अनुकूलित प्रतिकृयात्मक लेआउट मोड सेटिङ्गहरू", + "adaptive": "अनुकूलित", + "compact": "संकुचित", + "extended": "बढाइएको", + "theme": "थिम", + "dark": "गाढा", + "light": "प्रकाश", + "system": "सिस्टम", + "accent_color": "एक्सेन्ट रङ्ग", + "sync_album_color": "एल्बम रङ्ग सिङ्क गर्नुहोस्", + "sync_album_color_description": "एल्बम कला को प्रमुख रङ्गलाई एक्सेन्ट रङ्गको रूपमा प्रयोग गर्दछ", + "playback": "प्लेब्याक", + "audio_quality": "आडियो गुणस्तर", + "high": "उच्च", + "low": "न्यून", + "pre_download_play": "पूर्व-डाउनलोड र प्ले गर्नुहोस्", + "pre_download_play_description": "आडियो स्ट्रिम गर्नु नगरी बाइटहरू डाउनलोड गरी बजाउँछ (उच्च ब्यान्डविथ उपयोगकर्ताहरूको लागि सिफारिस गरिएको)", + "skip_non_music": "गीतहरू बाहेक कुनै अनुष्ठान छोड्नुहोस् (स्पन्सरब्लक)", + "blacklist_description": "कालोसूची गीत र कलाकारहरू", + "wait_for_download_to_finish": "कृपया हालको डाउनलोड समाप्त हुन लागि पर्खनुहोस्", + "desktop": "डेस्कटप", + "close_behavior": "बन्द व्यवहार", + "close": "बन्द गर्नुहोस्", + "minimize_to_tray": "ट्रेमा कम गर्नुहोस्", + "show_tray_icon": "सिस्टम ट्रे आइकन देखाउनुहोस्", + "about": "बारेमा", + "u_love_spotube": "हामीले थाहा पारेका छौं तपाईंलाई Spotube मन पर्छ", + "check_for_updates": "अपडेटहरूको लागि जाँच गर्नुहोस्", + "about_spotube": "Spotube को बारेमा", + "blacklist": "कालोसूची", + "please_sponsor": "कृपया स्पन्सर/डोनेट गर्नुहोस्", + "spotube_description": "Spotube, एक हल्का, समृद्ध, स्वतन्त्र Spotify क्लाइयन", + "version": "संस्करण", + "build_number": "निर्माण नम्बर", + "founder": "संस्थापक", + "repository": "पुनरावलोकन स्थल", + "bug_issues": "त्रुटि + समस्याहरू", + "made_with": "❤️ 2021-2024 बाट बनाइएको", + "kingkor_roy_tirtho": "किङ्कोर राय तिर्थो", + "copyright": "© 2021-{current_year} किङ्कोर राय तिर्थो", + "license": "लाइसेन्स", + "add_spotify_credentials": "सुरु हुनका लागि तपाईंको स्पटिफाई क्रेडेन्शियल थप्नुहोस्", + "credentials_will_not_be_shared_disclaimer": "चिन्ता नगर्नुहोस्, तपाईंको कुनै पनि क्रेडेन्शियलहरूले कसैले संग्रह वा साझा गर्नेछैन", + "know_how_to_login": "कसरी लगिन गर्ने भन्ने थाहा छैन?", + "follow_step_by_step_guide": "चरणबद्ध मार्गदर्शनमा साथी बनाउनुहोस्", + "spotify_cookie": "Spotify {name} कुकी", + "cookie_name_cookie": "{name} कुकी", + "fill_in_all_fields": "कृपया सबै क्षेत्रहरू भर्नुहोस्", + "submit": "पेश गर्नुहोस्", + "exit": "बाहिर निस्कनुहोस्", + "previous": "पूर्ववत", + "next": "अरू", + "done": "गरिएको", + "step_1": "कदम 1", + "first_go_to": "पहिलो, जानुहोस्", + "login_if_not_logged_in": "र लगइन/साइनअप गर्नुहोस् जुन तपाईंले लगइन गरेनन्", + "step_2": "कदम 2", + "step_2_steps": "1. एकबार तपाईं लगइन गरे पछि, F12 थिच्नुहोस् वा माउस राइट क्लिक गर्नुहोस् > इन्स्पेक्ट गर्नुहोस् भने ब्राउजर डेभटुलहरू खुलाउनका लागि।\n2. तपाईंको \"एप्लिकेसन\" ट्याबमा जानुहोस् (Chrome, Edge, Brave इत्यादि) वा \"स्टोरेज\" ट्याबमा जानुहोस् (Firefox, Palemoon इत्यादि)\n3. तपाईंको इन्सेक्ट गरेको ब्राउजर डेभटुलहरूमा \"कुकीहरू\" खण्डमा जानुहोस् अनि \"https://accounts.spotify.com\" उपकोणमा जानुहोस्", + "step_3": "कदम 3", + "step_3_steps": "\"sp_dc\" र \"sp_key\" (वा sp_gaid) कुकीहरूको मानहरू प्रतिलिपि गर्नुहोस्", + "success_emoji": "सफलता 🥳", + "success_message": "हाम्रो सानो भाइ, अब तपाईं सफलतापूर्वक आफ्नो Spotify खातामा लगइन गरेका छौं। राम्रो काम गरेको!", + "step_4": "कदम 4", + "step_4_steps": "प्रतिलिपि गरेको \"sp_dc\" र \"sp_key\" (वा sp_gaid) मानहरूलाई आफ्नो ठाउँमा पेस्ट गर्नुहोस्", + "something_went_wrong": "केहि गल्ति भएको छ", + "piped_instance": "पाइपड सर्भर इन्स्ट्यान्स", + "piped_description": "गीत मिलाउको लागि प्रयोग गर्ने पाइपड सर्भर इन्स्ट्यान्स", + "piped_warning": "तिनीहरूमध्ये केहि ठिक गर्न सक्छ। यसलाई आफ्नो जोखिममा प्रयोग गर्नुहोस्", + "generate_playlist": "प्लेलिस्ट बनाउनुहोस्", + "track_exists": "ट्र्याक {track} पहिले नै छ", + "replace_downloaded_tracks": "सबै डाउनलोड गरिएका ट्र्याकहरूलाई परिवर्तन गर्नुहोस्", + "skip_download_tracks": "सबै डाउनलोड गरिएका ट्र्याकहरूलाई छोड्नुहोस्", + "do_you_want_to_replace": "के तपाईंले वर्तमान ट्र्याकलाई परिवर्तन गर्न चाहनुहुन्छ?", + "replace": "परिवर्तन गर्नुहोस्", + "skip": "छोड्नुहोस्", + "select_up_to_count_type": "{count} {type} सम्म चयन गर्नुहोस्", + "select_genres": "जनरहरू चयन गर्नुहोस्", + "add_genres": "जनरहरू थप्नुहोस्", + "country": "देश", + "number_of_tracks_generate": "बनाउनका लागि ट्र्याकहरूको संख्या", + "acousticness": "एकोस्टिकनेस", + "danceability": "नृत्यक्षमता", + "energy": "ऊर्जा", + "instrumentalness": "साजा रहेकोता", + "liveness": "प्राणिकता", + "loudness": "शोर", + "speechiness": "भाषण", + "valence": "मानसिक स्वभाव", + "popularity": "लोकप्रियता", + "key": "कुञ्जी", + "duration": "अवधि (सेकेण्ड)", + "tempo": "गति (बीपीएम)", + "mode": "मोड", + "time_signature": "समय हस्ताक्षर", + "short": "सानो", + "medium": "मध्यम", + "long": "लामो", + "min": "न्यून", + "max": "अधिक", + "target": "लक्ष्य", + "moderate": "मध्यस्थ", + "deselect_all": "सबै छान्नुहोस्", + "select_all": "सबै चयन गर्नुहोस्", + "are_you_sure": "के तपाईं सुनिश्चित हुनुहुन्छ?", + "generating_playlist": "तपाईंको विशेष प्लेलिस्ट बनाइएको छ...", + "selected_count_tracks": "{count} ट्र्याकहरू छन् चयन गरिएका", + "download_warning": "यदि तपाईं सबै ट्र्याकहरूलाई बल्कमा डाउनलोड गर्छनु हो भने तपाईं स्पष्ट रूपमा साङ्गीत चोरी गरिरहेका छन् र यो साङ्गीतको रचनात्मक समाजलाई क्षति पनि पुर्याउँछ। उमेराइएको छ कि तपाईं यसको बारेमा जागरूक छिनुहुन्छ। सधैं, कला गर्दै र कलाकारको कडा परम्परा समर्थन गर्दै आइन्छ।", + "download_ip_ban_warning": "बितिएका डाउनलोड अनुरोधहरूका कारण तपाईंको आइपीले YouTube मा ब्लक हुन सक्छ। आइपी ब्लक भनेको कम्तीमा 2-3 महिनासम्म तपाईं त्यस आइपी यन्त्रबाट YouTube प्रयोग गर्न सक्नुहुन्छ। र यदि यो हुँदैछ भने स्पट्यूबले यसलाई कसैले गरेको बारेमा कुनै दायित्व लिन्छैन।", + "by_clicking_accept_terms": "'स्वीकृत' गरेर तपाईं निम्नलिखित निर्वाचन गर्दैछिन्:", + "download_agreement_1": "म मन्ने छु कि म साङ्गीत चोरी गरिरहेको छु। म बुरो हुँ", + "download_agreement_2": "म कहिल्यै कहिल्यै तिनीहरूलाई समर्थन गर्नेछु र म यो तिनीहरूको कला किन्ने पैसा छैन भने मा मात्र यो गरेको छु", + "download_agreement_3": "म पूरा रूपमा जान्छु कि मेरो आइपी YouTube मा ब्लक हुन सक्छ र म मन्छेहरूले मेरो चासोबाट भएको कुनै दुर्घटनामा स्पट्यूब वा तिनीहरूको मालिकहरू/सहयोगीहरूलाई दायित्वी ठान्छुँभन्ने पूर्ण जानकारी छैन", + "decline": "अस्वीकृत", + "accept": "स्वीकृत", + "details": "विवरण", + "youtube": "YouTube", + "channel": "च्यानल", + "likes": "लाइकहरू", + "dislikes": "असुनुहरू", + "views": "हेरिएको", + "streamUrl": "स्ट्रिम यूआरएल", + "stop": "रोक्नुहोस्", + "sort_newest": "नयाँ थपिएकोमा क्रमबद्ध गर्नुहोस्", + "sort_oldest": "पुरानो थपिएकोमा क्रमबद्ध गर्नुहोस्", + "sleep_timer": "सुत्ने टाइमर", + "mins": "{minutes} मिनेटहरू", + "hours": "{hours} घण्टाहरू", + "hour": "{hours} घण्टा", + "custom_hours": "कस्टम घण्टाहरू", + "logs": "लगहरू", + "developers": "डेभेलपर्स", + "not_logged_in": "तपाईंले लगइन गरेका छैनौं", + "search_mode": "खोज मोड", + "audio_source": "अडियो स्रोत", + "ok": "ठिक छ", + "failed_to_encrypt": "एन्क्रिप्ट गर्न सकिएन", + "encryption_failed_warning": "स्पट्यूबले तपाईंको डेटा सुरक्षित रूपमा स्टोर गर्नका लागि एन्क्रिप्ट गर्न खोजेको छ। तर यसले गरेको छैन। यसले असुरक्षित स्टोरेजमा फल्लब्याक गर्दछ\nयदि तपाईंले लिनक्स प्रयोग गरिरहेका छन् भने कृपया सुनिश्चित गर्नुहोस् कि तपाईंले कुनै सीक्रेट-सर्भिस (गोनोम-किरिङ, केडीइ-वालेट, किपासेक्ससि इत्यादि) इन्स्टल गरेका छौं", + "querying_info": "जानकारी हेर्दै...", + "piped_api_down": "पाइपड एपीआई डाउन छ", + "piped_down_error_instructions": "पाइपड इन्स्ट्यान्स {pipedInstance} हाल डाउन छ\n\nजीसनै इन्स्ट्यान्स परिवर्तन गर्नुहोस् वा 'एपीआई प्रकार' लाइ YouTube आफिसियल एपीआईमा परिवर्तन गर्नुहोस्\n\nपरिवर्तनपछि एप्लिकेसन पुन: सुरु गर्नुहोस्", + "you_are_offline": "तपाईं वर्तमान अफलाइन हुनुहुन्छ", + "connection_restored": "तपाईंको इन्टरनेट कनेक्सन पुन: स्थापित भएको छ", + "use_system_title_bar": "सिस्टम शीर्षक पट्टी प्रयोग गर्नुहोस्", + "crunching_results": "परिणामहरू कपालबाट पीस्दै...", + "search_to_get_results": "परिणामहरू प्राप्त गर्नका लागि खोज्नुहोस्", + "use_amoled_mode": "कृष्ण ब्ल्याक गाढा थिम प्रयोग गर्नुहोस्", + "pitch_dark_theme": "एमोलेड मोड", + "normalize_audio": "अडियो सामान्य गर्नुहोस्", + "change_cover": "कवर परिवर्तन गर्नुहोस्", + "add_cover": "कवर थप्नुहोस्", + "restore_defaults": "पूर्वनिर्धारितहरू पुनः स्थापित गर्नुहोस्", + "download_music_codec": "साङ्गीत कोडेक डाउनलोड गर्नुहोस्", + "streaming_music_codec": "स्ट्रिमिङ साङ्गीत कोडेक", + "login_with_lastfm": "लास्ट.एफ.एम सँग लगइन गर्नुहोस्", + "connect": "जडान गर्नुहोस्", + "disconnect_lastfm": "लास्ट.एफ.एम डिसकनेक्ट गर्नुहोस्", + "disconnect": "डिसकनेक्ट", + "username": "प्रयोगकर्ता नाम", + "password": "पासवर्ड", + "login": "लगइन", + "login_with_your_lastfm": "तपाईंको लास्ट.एफ.एम खातामा लगइन गर्नुहोस्", + "scrobble_to_lastfm": "लास्ट.एफ.एम मा स्क्रबल गर्नुहोस्", + "go_to_album": "आल्बममा जानुहोस्", + "discord_rich_presence": "डिस्कर्ड धनी उपस्थिति", + "browse_all": "सबै हेर्नुहोस्", + "genres": "शैलीहरू", + "explore_genres": "शैलीहरू अन्वेषण गर्नुहोस्" +} \ No newline at end of file diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 47e5eb99..335be545 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -21,6 +21,7 @@ class L10n { const Locale('es', 'ES'), const Locale("fa", "IR"), const Locale('fr', 'FR'), + const Locale('ne', 'NP'), const Locale('hi', 'IN'), const Locale('it', 'IT'), const Locale('ja', 'JP'), From 9b289605c7e803605c83b81e62ecc089ea668b60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:59:20 +0600 Subject: [PATCH 24/46] chore(deps): bump shared_preferences from 2.2.1 to 2.2.2 (#796) Bumps [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.2.2/packages/shared_preferences) --- updated-dependencies: - dependency-name: shared_preferences dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index b4182d12..2278f185 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1772,10 +1772,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 36a2f398..4798aeec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,7 +83,7 @@ dependencies: url: https://github.com/KRTirtho/scrobblenaut.git ref: dart-3-support scroll_to_index: ^3.0.1 - shared_preferences: ^2.0.11 + shared_preferences: ^2.2.2 sidebarx: ^0.15.0 skeleton_text: ^3.0.1 smtc_windows: ^0.1.1 From ab09ae60f06930299e1f17138931fd1604530af4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:00:35 +0600 Subject: [PATCH 25/46] chore(deps): bump visibility_detector from 0.3.3 to 0.4.0+2 (#797) Bumps [visibility_detector](https://github.com/google/flutter.widgets/tree/master/packages) from 0.3.3 to 0.4.0+2. - [Commits](https://github.com/google/flutter.widgets/commits/HEAD/packages) --- updated-dependencies: - dependency-name: visibility_detector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 2278f185..5141b46b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2225,10 +2225,10 @@ packages: dependency: "direct main" description: name: visibility_detector - sha256: "15c54a459ec2c17b4705450483f3d5a2858e733aee893dcee9d75fd04814940d" + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 url: "https://pub.dev" source: hosted - version: "0.3.3" + version: "0.4.0+2" vm_service: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4798aeec..d49c02a1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,7 +94,7 @@ dependencies: url_launcher: ^6.1.7 uuid: ^3.0.7 version: ^3.0.2 - visibility_detector: ^0.3.3 + visibility_detector: ^0.4.0+2 window_manager: ^0.3.1 window_size: git: From 7b6042d01f3eb115fd244409901824b2773c7816 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:02:25 +0600 Subject: [PATCH 26/46] chore(deps): bump google_fonts from 5.1.0 to 6.1.0 (#798) Bumps [google_fonts](https://github.com/material-foundation/flutter-packages/tree/main/packages) from 5.1.0 to 6.1.0. - [Commits](https://github.com/material-foundation/flutter-packages/commits/HEAD/packages) --- updated-dependencies: - dependency-name: google_fonts dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kingkor Roy Tirtho --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 5141b46b..b70b9dc0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1014,10 +1014,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982 + sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "6.1.0" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d49c02a1..b7d2493b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: flutter_svg: ^1.1.6 form_validator: ^2.1.1 fuzzywuzzy: ^1.1.6 - google_fonts: ^5.1.0 + google_fonts: ^6.1.0 go_router: ^11.1.2 hive: ^2.2.3 hive_flutter: ^1.1.0 From a72bb0c73b71cd13ab54307cfbd7426648a490f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:03:56 +0600 Subject: [PATCH 27/46] chore(deps): bump subosito/flutter-action from 2.10.0 to 2.12.0 (#855) Bumps [subosito/flutter-action](https://github.com/subosito/flutter-action) from 2.10.0 to 2.12.0. - [Release notes](https://github.com/subosito/flutter-action/releases) - [Commits](https://github.com/subosito/flutter-action/compare/v2.10.0...v2.12.0) --- updated-dependencies: - dependency-name: subosito/flutter-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kingkor Roy Tirtho --- .github/workflows/spotube-release-binary.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index b561bca8..8fa0e2f5 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -33,7 +33,7 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.10.0 + - uses: subosito/flutter-action@v2.12.0 with: cache: true flutter-version: ${{ env.FLUTTER_VERSION }} @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.10.0 + - uses: subosito/flutter-action@v2.12.0 with: cache: true flutter-version: ${{ env.FLUTTER_VERSION }} @@ -200,7 +200,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.10.0 + - uses: subosito/flutter-action@v2.12.0 with: cache: true flutter-version: ${{ env.FLUTTER_VERSION }} @@ -276,7 +276,7 @@ jobs: runs-on: macos-12 steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.10.0 + - uses: subosito/flutter-action@v2.12.0 with: cache: true flutter-version: ${{ env.FLUTTER_VERSION }} From 1bdae90fb01f0f8cac9925576ce068dba38e5250 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:07:24 +0600 Subject: [PATCH 28/46] chore(deps): bump sidebarx from 0.15.0 to 0.16.3 (#896) Bumps [sidebarx](https://github.com/Frezyx/sidebarx) from 0.15.0 to 0.16.3. - [Release notes](https://github.com/Frezyx/sidebarx/releases) - [Changelog](https://github.com/Frezyx/sidebarx/blob/main/CHANGELOG.md) - [Commits](https://github.com/Frezyx/sidebarx/compare/0.15.0...0.16.3) --- updated-dependencies: - dependency-name: sidebarx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kingkor Roy Tirtho --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index b70b9dc0..3835f711 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1852,10 +1852,10 @@ packages: dependency: "direct main" description: name: sidebarx - sha256: "26a8392ceddb659c8f2c688beba6c04bcbf520b4d5decb143c5fd7253653081f" + sha256: "7042d64844b8e64ca5c17e70d89b49df35b54a26c015b90000da9741eab70bc0" url: "https://pub.dev" source: hosted - version: "0.15.0" + version: "0.16.3" simple_icons: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b7d2493b..d0c51a15 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,8 +83,8 @@ dependencies: url: https://github.com/KRTirtho/scrobblenaut.git ref: dart-3-support scroll_to_index: ^3.0.1 + sidebarx: ^0.16.3 shared_preferences: ^2.2.2 - sidebarx: ^0.15.0 skeleton_text: ^3.0.1 smtc_windows: ^0.1.1 spotify: ^0.12.0 From 2c88561b4433c08c347b444feb33f9171f593ac2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:07:48 +0600 Subject: [PATCH 29/46] chore(deps): bump actions/download-artifact from 3 to 4 (#948) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/spotube-release-binary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index 8fa0e2f5..470544e3 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -386,7 +386,7 @@ jobs: - macos - iOS steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: Spotube-Release-Binaries path: ./Spotube-Release-Binaries From 0dea3249a5cbe36c726a15ea772b8648ac5d5658 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:08:03 +0600 Subject: [PATCH 30/46] chore(deps): bump actions/upload-artifact from 3 to 4 (#949) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/spotube-release-binary.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index 470544e3..ff305d8b 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -89,7 +89,7 @@ jobs: - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -180,7 +180,7 @@ jobs: mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -257,7 +257,7 @@ jobs: mv build/app/outputs/bundle/${{ inputs.channel }}Release/app-${{ inputs.channel }}-release.aab build/Spotube-playstore-all-arch.aab - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -363,7 +363,7 @@ jobs: ln -sf ./build/ios/iphoneos Payload zip -r9 Spotube-iOS.ipa Payload/${{ inputs.channel }}.app - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -401,7 +401,7 @@ jobs: sha256sum Spotube-Release-Binaries/* >> RELEASE.sha256sum sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Spotube-Release-Binaries From 9bd16e096f598397b7199c71f7a291a5d439e99b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:18:10 +0600 Subject: [PATCH 31/46] chore(deps): bump go_router from 11.1.2 to 13.0.1 (#1020) Bumps [go_router](https://github.com/flutter/packages/tree/main/packages) from 11.1.2 to 13.0.1. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/go_router-v13.0.1/packages) --- updated-dependencies: - dependency-name: go_router dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kingkor Roy Tirtho --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3835f711..e2809e74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1006,10 +1006,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: a07c781bf55bf11ae85133338e4850f0b4e33e261c44a66c750fc707d65d8393 + sha256: "3b40e751eaaa855179b416974d59d29669e750d2e50fcdb2b37f1cb0ca8c803a" url: "https://pub.dev" source: hosted - version: "11.1.2" + version: "13.0.1" google_fonts: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d0c51a15..f9da61d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,8 +55,8 @@ dependencies: flutter_svg: ^1.1.6 form_validator: ^2.1.1 fuzzywuzzy: ^1.1.6 + go_router: ^13.0.1 google_fonts: ^6.1.0 - go_router: ^11.1.2 hive: ^2.2.3 hive_flutter: ^1.1.0 hooks_riverpod: ^2.4.3 From fd1899f162395752142d7aa7320d1c39b0995070 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 10:46:42 +0600 Subject: [PATCH 32/46] fix: audio resumes after a phone call even if it was paused before #926 --- android/app/build.gradle | 2 +- .../audio_services/mobile_audio_service.dart | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index df13c9f4..2f85cdeb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 33 + compileSdkVersion 34 ndkVersion "21.4.7075529" diff --git a/lib/services/audio_services/mobile_audio_service.dart b/lib/services/audio_services/mobile_audio_service.dart index 9750fce8..833df89c 100644 --- a/lib/services/audio_services/mobile_audio_service.dart +++ b/lib/services/audio_services/mobile_audio_service.dart @@ -17,6 +17,9 @@ class MobileAudioService extends BaseAudioHandler { AudioSession.instance.then((s) { session = s; session?.configure(const AudioSessionConfiguration.music()); + + bool wasPausedByBeginEvent = false; + s.interruptionEventStream.listen((event) async { if (event.begin) { switch (event.type) { @@ -25,17 +28,23 @@ class MobileAudioService extends BaseAudioHandler { break; case AudioInterruptionType.pause: case AudioInterruptionType.unknown: - await audioPlayer.pause(); - break; + { + wasPausedByBeginEvent = audioPlayer.isPlaying; + await audioPlayer.pause(); + break; + } } } else { switch (event.type) { case AudioInterruptionType.duck: await audioPlayer.setVolume(1.0); break; - case AudioInterruptionType.pause: - case AudioInterruptionType.unknown: + case AudioInterruptionType.pause when wasPausedByBeginEvent: + case AudioInterruptionType.unknown when wasPausedByBeginEvent: await audioPlayer.resume(); + wasPausedByBeginEvent = false; + break; + default: break; } } From 9d74cf5fc250a6a143321d49b8e045519b4c2872 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 11:45:26 +0600 Subject: [PATCH 33/46] fix(macos): download folder unchangeable --- lib/pages/settings/sections/downloads.dart | 2 +- macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- macos/Runner/DebugProfile.entitlements | 40 ++++++++----------- macos/Runner/Release.entitlements | 36 +++++++---------- pubspec.lock | 40 +++++++++---------- untranslated_messages.json | 5 +++ 7 files changed, 60 insertions(+), 67 deletions(-) diff --git a/lib/pages/settings/sections/downloads.dart b/lib/pages/settings/sections/downloads.dart index 12026909..b1e360d0 100644 --- a/lib/pages/settings/sections/downloads.dart +++ b/lib/pages/settings/sections/downloads.dart @@ -18,7 +18,7 @@ class SettingsDownloadsSection extends HookConsumerWidget { final preferences = ref.watch(userPreferencesProvider); final pickDownloadLocation = useCallback(() async { - if (DesktopTools.platform.isMobile) { + if (DesktopTools.platform.isMobile || DesktopTools.platform.isMacOS) { final dirStr = await FilePicker.platform.getDirectoryPath( initialDirectory: preferences.downloadLocation, ); diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index f7711c83..16e242c7 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8f69f0c6..1407feb3 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - com.apple.security.network.client - - - - - - - com.apple.security.files.user-selected.read-write - - - \ No newline at end of file + + com.apple.security.app-sandbox + + com.apple.security.assets.music.read-write + + com.apple.security.cs.allow-jit + + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index dc5df580..f05277de 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -1,24 +1,18 @@ - - com.apple.security.app-sandbox - - com.apple.security.network.server - - com.apple.security.network.client - - - - - - - com.apple.security.files.user-selected.read-write - - - \ No newline at end of file + + com.apple.security.app-sandbox + + com.apple.security.assets.music.read-write + + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/pubspec.lock b/pubspec.lock index e2809e74..1a95f20e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1259,18 +1259,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.dev" source: hosted - version: "9.0.16" + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "2.0.1" lints: dependency: transitive description: @@ -1315,10 +1323,10 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -1483,10 +1491,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_drawing: dependency: transitive description: @@ -1611,10 +1619,10 @@ packages: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -1651,10 +1659,10 @@ packages: dependency: transitive description: name: process - sha256: "266ca5be5820feefc777793d0a583acfc8c40834893c87c00c6c09e2cf58ea42" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" provider: dependency: transitive description: @@ -2245,14 +2253,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa - url: "https://pub.dev" - source: hosted - version: "0.4.0" web_socket_channel: dependency: transitive description: diff --git a/untranslated_messages.json b/untranslated_messages.json index 45a6df11..ae05dfeb 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -69,6 +69,11 @@ "no_lyrics_available" ], + "ne": [ + "friends", + "no_lyrics_available" + ], + "nl": [ "step_3_steps", "step_4_steps", From a752cf4c978d1b05851aabb6c84c7862de551320 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 11:50:48 +0600 Subject: [PATCH 34/46] fix: track index not showing after 200 --- lib/components/shared/track_tile/track_tile.dart | 2 +- .../shared/tracks_view/sections/body/track_view_body.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/components/shared/track_tile/track_tile.dart b/lib/components/shared/track_tile/track_tile.dart index c3b03f3c..d268c783 100644 --- a/lib/components/shared/track_tile/track_tile.dart +++ b/lib/components/shared/track_tile/track_tile.dart @@ -110,7 +110,7 @@ class TrackTile extends HookConsumerWidget { ...?leadingActions, if (index != null && onChanged == null && constrains.mdAndUp) SizedBox( - width: 34, + width: 50, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Text( diff --git a/lib/components/shared/tracks_view/sections/body/track_view_body.dart b/lib/components/shared/tracks_view/sections/body/track_view_body.dart index 20caf4f1..b7149cc2 100644 --- a/lib/components/shared/tracks_view/sections/body/track_view_body.dart +++ b/lib/components/shared/tracks_view/sections/body/track_view_body.dart @@ -8,7 +8,6 @@ import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/collections/fake.dart'; import 'package:spotube/components/shared/expandable_search/expandable_search.dart'; -import 'package:spotube/components/shared/fallbacks/not_found.dart'; import 'package:spotube/components/shared/track_tile/track_tile.dart'; import 'package:spotube/components/shared/tracks_view/sections/body/track_view_body_headers.dart'; import 'package:spotube/components/shared/tracks_view/sections/body/use_is_user_playlist.dart'; From b9417ca3575992673357230dab49e0124dd576b1 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 12:03:31 +0600 Subject: [PATCH 35/46] fix(macos): backbutton and window button overlap and unused empty space on home --- .../shared/page_window_title_bar.dart | 61 ++++++++++++------- .../shared/themed_button_tab_bar.dart | 12 +--- lib/pages/home/home.dart | 10 ++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/lib/components/shared/page_window_title_bar.dart b/lib/components/shared/page_window_title_bar.dart index 4f522e0c..9aa2d4a8 100644 --- a/lib/components/shared/page_window_title_bar.dart +++ b/lib/components/shared/page_window_title_bar.dart @@ -62,28 +62,45 @@ class _PageWindowTitleBarState extends ConsumerState { @override Widget build(BuildContext context) { - return GestureDetector( - onHorizontalDragStart: onDrag, - onVerticalDragStart: onDrag, - child: AppBar( - leading: widget.leading, - automaticallyImplyLeading: widget.automaticallyImplyLeading, - actions: [ - ...?widget.actions, - WindowTitleBarButtons(foregroundColor: widget.foregroundColor), - ], - backgroundColor: widget.backgroundColor, - foregroundColor: widget.foregroundColor, - actionsIconTheme: widget.actionsIconTheme, - centerTitle: widget.centerTitle, - titleSpacing: widget.titleSpacing, - toolbarOpacity: widget.toolbarOpacity, - leadingWidth: widget.leadingWidth, - toolbarTextStyle: widget.toolbarTextStyle, - titleTextStyle: widget.titleTextStyle, - title: widget.title, - ), - ); + final mediaQuery = MediaQuery.of(context); + + return LayoutBuilder(builder: (context, constrains) { + final hasFullscreen = mediaQuery.size.width == constrains.maxWidth; + final hasLeadingOrCanPop = + widget.leading != null || Navigator.canPop(context); + + return GestureDetector( + onHorizontalDragStart: onDrag, + onVerticalDragStart: onDrag, + child: Padding( + padding: EdgeInsets.only( + left: DesktopTools.platform.isMacOS && + hasFullscreen && + hasLeadingOrCanPop + ? 65 + : 0, + ), + child: AppBar( + leading: widget.leading, + automaticallyImplyLeading: widget.automaticallyImplyLeading, + actions: [ + ...?widget.actions, + WindowTitleBarButtons(foregroundColor: widget.foregroundColor), + ], + backgroundColor: widget.backgroundColor, + foregroundColor: widget.foregroundColor, + actionsIconTheme: widget.actionsIconTheme, + centerTitle: widget.centerTitle, + titleSpacing: widget.titleSpacing, + toolbarOpacity: widget.toolbarOpacity, + leadingWidth: widget.leadingWidth, + toolbarTextStyle: widget.toolbarTextStyle, + titleTextStyle: widget.titleTextStyle, + title: widget.title, + ), + ), + ); + }); } } diff --git a/lib/components/shared/themed_button_tab_bar.dart b/lib/components/shared/themed_button_tab_bar.dart index 079a4e8a..d5798189 100644 --- a/lib/components/shared/themed_button_tab_bar.dart +++ b/lib/components/shared/themed_button_tab_bar.dart @@ -1,9 +1,7 @@ import 'package:buttons_tabbar/buttons_tabbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:spotube/hooks/utils/use_breakpoint_value.dart'; import 'package:spotube/hooks/utils/use_brightness_value.dart'; -import 'package:spotube/utils/platform.dart'; class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget { final List tabs; @@ -17,16 +15,8 @@ class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget { Color.lerp(theme.colorScheme.primary, Colors.black, 0.7)!, ); - final breakpoint = useBreakpointValue( - xs: 85.0, - sm: 85.0, - md: 35.0, - others: 0.0, - ); - return Padding( - padding: EdgeInsets.only( - left: kIsMacOS ? breakpoint : 0, + padding: const EdgeInsets.only( top: 8, bottom: 8, ), diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index eb2ddb94..2994eac3 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/components/home/sections/featured.dart'; import 'package:spotube/components/home/sections/friends.dart'; @@ -19,12 +20,15 @@ class HomePage extends HookConsumerWidget { return SafeArea( bottom: false, child: Scaffold( - appBar: DesktopTools.platform.isMobile - ? null - : const PageWindowTitleBar(), + appBar: + DesktopTools.platform.isLinux || DesktopTools.platform.isWindows + ? const PageWindowTitleBar() + : null, body: CustomScrollView( controller: controller, slivers: [ + if (DesktopTools.platform.isMacOS || DesktopTools.platform.isWeb) + const SliverGap(20), const HomeGenresSection(), SliverList.list( children: const [ From eb7477273f159df368e0a15d4c5b8d2aeabdd4de Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 12:11:49 +0600 Subject: [PATCH 36/46] chore: fix wrong audio source result showing up in Alternative track sheet --- .../player/sibling_tracks_sheet.dart | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/lib/components/player/sibling_tracks_sheet.dart b/lib/components/player/sibling_tracks_sheet.dart index 181c363a..58b1ca8c 100644 --- a/lib/components/player/sibling_tracks_sheet.dart +++ b/lib/components/player/sibling_tracks_sheet.dart @@ -82,35 +82,50 @@ class SiblingTracksSheet extends HookConsumerWidget { if (searchTerm.trim().isEmpty) { return []; } - - final resultsYt = await youtubeClient.search.search(searchTerm.trim()); - final resultsJioSaavn = - await jiosaavnClient.search.songs(searchTerm.trim()); - - final searchResults = await Future.wait([ - ...resultsJioSaavn.results.mapIndexed((i, song) async { + if (preferences.audioSource == AudioSource.jiosaavn) { + final resultsJioSaavn = + await jiosaavnClient.search.songs(searchTerm.trim()); + final results = await Future.wait( + resultsJioSaavn.results.mapIndexed((i, song) async { final siblingType = JioSaavnSourcedTrack.toSiblingType(song); return siblingType.info; - }), - ...resultsYt - .map(YoutubeVideoInfo.fromVideo) - .mapIndexed((i, video) async { - final siblingType = await YoutubeSourcedTrack.toSiblingType(i, video); - return siblingType.info; - }), - ]); - final activeSourceInfo = - (playlist.activeTrack! as SourcedTrack).sourceInfo; - return searchResults - ..removeWhere((element) => element.id == activeSourceInfo.id) - ..insert( - 0, - activeSourceInfo, + })); + + final activeSourceInfo = + (playlist.activeTrack! as SourcedTrack).sourceInfo; + + return results + ..removeWhere((element) => element.id == activeSourceInfo.id) + ..insert( + 0, + activeSourceInfo, + ); + } else { + final resultsYt = await youtubeClient.search.search(searchTerm.trim()); + + final searchResults = await Future.wait( + resultsYt + .map(YoutubeVideoInfo.fromVideo) + .mapIndexed((i, video) async { + final siblingType = + await YoutubeSourcedTrack.toSiblingType(i, video); + return siblingType.info; + }), ); + final activeSourceInfo = + (playlist.activeTrack! as SourcedTrack).sourceInfo; + return searchResults + ..removeWhere((element) => element.id == activeSourceInfo.id) + ..insert( + 0, + activeSourceInfo, + ); + } }, [ searchTerm, searchMode.value, playlist.activeTrack, + preferences.audioSource, ]); final siblings = useMemoized( From 624220090572eb643dce37ca8ffd85d2b3f5c9df Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 12:29:35 +0600 Subject: [PATCH 37/46] feat: haptic feedback on long press and reordering actions --- lib/components/player/player_queue.dart | 6 ++++++ .../shared/tracks_view/sections/body/track_view_body.dart | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/components/player/player_queue.dart b/lib/components/player/player_queue.dart index 8142740c..2784fb5f 100644 --- a/lib/components/player/player_queue.dart +++ b/lib/components/player/player_queue.dart @@ -210,6 +210,12 @@ class PlayerQueue extends HookConsumerWidget { itemCount: tracks.length, shrinkWrap: true, buildDefaultDragHandles: false, + onReorderStart: (index) { + HapticFeedback.selectionClick(); + }, + onReorderEnd: (index) { + HapticFeedback.selectionClick(); + }, itemBuilder: (context, i) { final track = tracks.elementAt(i); return AutoScrollTag( diff --git a/lib/components/shared/tracks_view/sections/body/track_view_body.dart b/lib/components/shared/tracks_view/sections/body/track_view_body.dart index b7149cc2..33c8fa82 100644 --- a/lib/components/shared/tracks_view/sections/body/track_view_body.dart +++ b/lib/components/shared/tracks_view/sections/body/track_view_body.dart @@ -1,5 +1,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:gap/gap.dart'; @@ -116,6 +117,7 @@ class TrackViewBodySection extends HookConsumerWidget { }, onLongPress: () { trackViewState.selectTrack(track.id!); + HapticFeedback.selectionClick(); }, onTap: () async { if (trackViewState.isSelecting) { From 1b5d435b86764e156c8f6d203b8ba46dede5dc15 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 12:46:27 +0600 Subject: [PATCH 38/46] cd: add macos pkg format --- .github/workflows/spotube-release-binary.yml | 17 ++++++++++++++++- ios/Runner.xcodeproj/project.pbxproj | 12 ++++++++++++ macos/packaging/pkg/make_config.yaml | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 macos/packaging/pkg/make_config.yaml diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index ff305d8b..27ae683e 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -320,9 +320,24 @@ jobs: npm install -g appdmg mkdir -p build/${{ env.BUILD_VERSION }} appdmg appdmg.json build/Spotube-macos-universal.dmg + flutter_distributor package --platform=macos --targets pkg --skip-clean + mv dist/**/spotube-*-macos.pkg build/Spotube-macos-universal.pkg + + - uses: actions/upload-artifact@v4 + with: + if-no-files-found: error + name: Spotube-Release-Binaries + path: | + build/Spotube-macos-universal.dmg + build/Spotube-macos-universal.pkg + + - name: Debug With SSH When fails + if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }} + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true iOS: - runs-on: macos-latest steps: - uses: actions/checkout@v4 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8b7c2b1e..6edf52e3 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -882,6 +882,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1010,6 +1011,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1032,6 +1034,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1172,6 +1175,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1268,6 +1272,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1360,6 +1365,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1585,6 +1591,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1703,6 +1710,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1816,6 +1824,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2126,6 +2135,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2266,6 +2276,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2400,6 +2411,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/macos/packaging/pkg/make_config.yaml b/macos/packaging/pkg/make_config.yaml new file mode 100644 index 00000000..06d92076 --- /dev/null +++ b/macos/packaging/pkg/make_config.yaml @@ -0,0 +1 @@ +install-path: /Applications From 6351306f87e2ffe273e8b1eb614ebb7a3d7db0e9 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 22:12:13 +0600 Subject: [PATCH 39/46] cd: downgrade upload and download artifact to v3 for merging --- .github/workflows/spotube-release-binary.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index 27ae683e..2b16148f 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -89,7 +89,7 @@ jobs: - name: Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -180,7 +180,7 @@ jobs: mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -257,7 +257,7 @@ jobs: mv build/app/outputs/bundle/${{ inputs.channel }}Release/app-${{ inputs.channel }}-release.aab build/Spotube-playstore-all-arch.aab - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -323,7 +323,7 @@ jobs: flutter_distributor package --platform=macos --targets pkg --skip-clean mv dist/**/spotube-*-macos.pkg build/Spotube-macos-universal.pkg - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -378,7 +378,7 @@ jobs: ln -sf ./build/ios/iphoneos Payload zip -r9 Spotube-iOS.ipa Payload/${{ inputs.channel }}.app - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries @@ -401,7 +401,7 @@ jobs: - macos - iOS steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v3 with: name: Spotube-Release-Binaries path: ./Spotube-Release-Binaries @@ -416,7 +416,7 @@ jobs: sha256sum Spotube-Release-Binaries/* >> RELEASE.sha256sum sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 with: if-no-files-found: error name: Spotube-Release-Binaries From 14d8ea582cd8c362f33dee821d92084a76dd7c4e Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Wed, 24 Jan 2024 22:25:15 +0600 Subject: [PATCH 40/46] cd: add flutter_distributor for macos --- .github/workflows/spotube-release-binary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index 2b16148f..dd20ca5d 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -304,8 +304,8 @@ jobs: - name: Generate Secrets run: | + dart pub global activate flutter_distributor flutter pub get - flutter pub remove media_kit_native_event_loop dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns - name: Build Macos App From 62fde50442f04f93255b5b1b1dcca23d116a13ec Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 20:59:50 +0600 Subject: [PATCH 41/46] fix: alternative source doesn't persist on next restart #840 --- lib/components/root/sidebar.dart | 3 +++ lib/services/sourced_track/sources/jiosaavn.dart | 10 ++++++++++ lib/services/sourced_track/sources/piped.dart | 10 ++++++++++ lib/services/sourced_track/sources/youtube.dart | 10 ++++++++++ macos/Runner.xcodeproj/project.pbxproj | 11 ++++++++++- macos/Runner/RunnerDebug.entitlements | 6 ++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index 9b3fd3ed..a55ef947 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -182,6 +182,9 @@ class Sidebar extends HookConsumerWidget { ), itemTextPadding: const EdgeInsets.only(left: 10), selectedItemTextPadding: const EdgeInsets.only(left: 10), + hoverTextStyle: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.primary, + ), ), ), ), diff --git a/lib/services/sourced_track/sources/jiosaavn.dart b/lib/services/sourced_track/sources/jiosaavn.dart index 281be998..7455f4d7 100644 --- a/lib/services/sourced_track/sources/jiosaavn.dart +++ b/lib/services/sourced_track/sources/jiosaavn.dart @@ -185,6 +185,16 @@ class JioSaavnSourcedTrack extends SourcedTrack { final (:info, :source) = toSiblingType(item); + await SourceMatch.box.put( + id!, + SourceMatch( + id: id!, + sourceType: SourceType.jiosaavn, + createdAt: DateTime.now(), + sourceId: info.id, + ), + ); + return JioSaavnSourcedTrack( ref: ref, siblings: newSiblings, diff --git a/lib/services/sourced_track/sources/piped.dart b/lib/services/sourced_track/sources/piped.dart index f9e4368d..52364ce8 100644 --- a/lib/services/sourced_track/sources/piped.dart +++ b/lib/services/sourced_track/sources/piped.dart @@ -263,6 +263,16 @@ class PipedSourcedTrack extends SourcedTrack { final manifest = await pipedClient.streams(newSourceInfo.id); + await SourceMatch.box.put( + id!, + SourceMatch( + id: id!, + sourceType: SourceType.jiosaavn, + createdAt: DateTime.now(), + sourceId: newSourceInfo.id, + ), + ); + return PipedSourcedTrack( ref: ref, siblings: newSiblings, diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index c4105d75..8708fc03 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -246,6 +246,16 @@ class YoutubeSourcedTrack extends SourcedTrack { final manifest = await youtubeClient.videos.streamsClient.getManifest(newSourceInfo.id); + await SourceMatch.box.put( + id!, + SourceMatch( + id: id!, + sourceType: SourceType.jiosaavn, + createdAt: DateTime.now(), + sourceId: newSourceInfo.id, + ), + ); + return YoutubeSourcedTrack( ref: ref, siblings: newSiblings, diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 16e242c7..e2e72334 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -228,7 +228,7 @@ }; }; buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 12.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -427,7 +427,10 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 88NVGSJ5N3; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Spotube; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", @@ -554,7 +557,10 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 88NVGSJ5N3; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Spotube; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", @@ -575,7 +581,10 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 88NVGSJ5N3; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Spotube; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", diff --git a/macos/Runner/RunnerDebug.entitlements b/macos/Runner/RunnerDebug.entitlements index 3ba6c126..e9de2261 100644 --- a/macos/Runner/RunnerDebug.entitlements +++ b/macos/Runner/RunnerDebug.entitlements @@ -4,8 +4,14 @@ com.apple.security.app-sandbox + com.apple.security.assets.music.read-write + com.apple.security.cs.allow-jit + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + com.apple.security.network.client com.apple.security.network.server From 5fa56387fddc2fbf062126839bf5fdfc9176e7d5 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 21:18:13 +0600 Subject: [PATCH 42/46] chore: move friends above new releases Because friends matters more than Elton John's 69th album (joking) --- lib/pages/home/home.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 2994eac3..dbec0564 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -1,4 +1,6 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; @@ -30,13 +32,9 @@ class HomePage extends HookConsumerWidget { if (DesktopTools.platform.isMacOS || DesktopTools.platform.isWeb) const SliverGap(20), const HomeGenresSection(), - SliverList.list( - children: const [ - HomeFeaturedSection(), - HomeNewReleasesSection(), - ], - ), + const SliverToBoxAdapter(child: HomeFeaturedSection()), const HomePageFriendsSection(), + const SliverToBoxAdapter(child: HomeNewReleasesSection()), const SliverSafeArea(sliver: HomeMadeForUserSection()), ], ), From 8184555ee89fd30aaf886af9fc1d52c142fdebb0 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 21:48:38 +0600 Subject: [PATCH 43/46] feat: improve youtube/piped matching by suffixing "- Topic" --- lib/provider/piped_instances_provider.dart | 10 ++++++++-- lib/services/sourced_track/sources/piped.dart | 2 +- lib/services/sourced_track/sources/youtube.dart | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/provider/piped_instances_provider.dart b/lib/provider/piped_instances_provider.dart index 264b7048..d571f730 100644 --- a/lib/provider/piped_instances_provider.dart +++ b/lib/provider/piped_instances_provider.dart @@ -1,11 +1,17 @@ +import 'package:catcher_2/catcher_2.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:piped_client/piped_client.dart'; import 'package:spotube/services/sourced_track/sources/piped.dart'; final pipedInstancesFutureProvider = FutureProvider>( (ref) async { - final pipedClient = ref.watch(pipedProvider); + try { + final pipedClient = ref.watch(pipedProvider); - return await pipedClient.instanceList(); + return await pipedClient.instanceList(); + } catch (e, stack) { + Catcher2.reportCheckedError(e, stack); + return []; + } }, ); diff --git a/lib/services/sourced_track/sources/piped.dart b/lib/services/sourced_track/sources/piped.dart index 52364ce8..8a1ec1bc 100644 --- a/lib/services/sourced_track/sources/piped.dart +++ b/lib/services/sourced_track/sources/piped.dart @@ -160,7 +160,7 @@ class PipedSourcedTrack extends SourcedTrack { final query = SourcedTrack.getSearchTerm(track); final PipedSearchResult(items: searchResults) = await pipedClient.search( - query, + "$query - Topic", preference.searchMode == SearchMode.youtube ? PipedFilter.video : PipedFilter.musicSongs, diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index 8708fc03..f363937c 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -210,7 +210,7 @@ class YoutubeSourcedTrack extends SourcedTrack { final query = SourcedTrack.getSearchTerm(track); final searchResults = await youtubeClient.search.search( - query, + "$query - Topic", filter: TypeFilters.video, ); From 2168a640af3104a43139c303d78e2c2326a1bda7 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 21:59:10 +0600 Subject: [PATCH 44/46] feat: add create playlist button in add playlist dialog --- .../dialogs/playlist_add_track_dialog.dart | 16 +++++++++++++++- lib/services/mutations/playlist.dart | 6 +++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/components/shared/dialogs/playlist_add_track_dialog.dart b/lib/components/shared/dialogs/playlist_add_track_dialog.dart index 1c8e5aaa..51b77c76 100644 --- a/lib/components/shared/dialogs/playlist_add_track_dialog.dart +++ b/lib/components/shared/dialogs/playlist_add_track_dialog.dart @@ -1,9 +1,11 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; +import 'package:spotube/components/playlist/playlist_create_dialog.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/spotify_provider.dart'; @@ -22,6 +24,7 @@ class PlaylistAddTrackDialog extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final ThemeData(:textTheme) = Theme.of(context); final spotify = ref.watch(spotifyProvider); final userPlaylists = useQueries.playlist.ofMineAll(ref); @@ -69,7 +72,18 @@ class PlaylistAddTrackDialog extends HookConsumerWidget { } return AlertDialog( - title: Text(context.l10n.add_to_playlist), + insetPadding: EdgeInsets.zero, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + context.l10n.add_to_playlist, + style: textTheme.titleMedium, + ), + const Gap(20), + const PlaylistCreateDialogButton(), + ], + ), actions: [ OutlinedButton( child: Text(context.l10n.cancel), diff --git a/lib/services/mutations/playlist.dart b/lib/services/mutations/playlist.dart index 077fff06..f480c565 100644 --- a/lib/services/mutations/playlist.dart +++ b/lib/services/mutations/playlist.dart @@ -94,9 +94,8 @@ class PlaylistMutations { return playlist; }, - refreshInfiniteQueries: [ - "current-user-playlists", - ], + refreshInfiniteQueries: ["current-user-playlists"], + refreshQueries: ["current-user-all-playlists"], ref: ref, onError: (error, recoveryData) { onError?.call(error); @@ -135,6 +134,7 @@ class PlaylistMutations { "playlist/$playlistId", "current-user-playlists", ], + refreshQueries: ["current-user-all-playlists"], ref: ref, onError: (error, recoveryData) { onError?.call(error); From bcba45fbd1def38d1e2d34ba2169bc062be33a11 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 22:27:27 +0600 Subject: [PATCH 45/46] chore: bump version and add changelogs --- CHANGELOG.md | 33 ++++++++++ README.md | 14 ++--- lib/l10n/app_ar.arb | 6 +- lib/l10n/app_bn.arb | 6 +- lib/l10n/app_ca.arb | 6 +- lib/l10n/app_de.arb | 6 +- lib/l10n/app_es.arb | 6 +- lib/l10n/app_fa.arb | 6 +- lib/l10n/app_fr.arb | 6 +- lib/l10n/app_hi.arb | 6 +- lib/l10n/app_it.arb | 6 +- lib/l10n/app_ja.arb | 6 +- lib/l10n/app_ne.arb | 6 +- lib/l10n/app_nl.arb | 6 +- lib/l10n/app_pl.arb | 6 +- lib/l10n/app_pt.arb | 6 +- lib/l10n/app_ru.arb | 6 +- lib/l10n/app_tr.arb | 6 +- lib/l10n/app_uk.arb | 6 +- lib/l10n/app_zh.arb | 6 +- pubspec.yaml | 2 +- untranslated_messages.json | 126 +------------------------------------ 22 files changed, 131 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea429caa..9d784485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [3.5.0](https://personal.github.com/krtirtho/spotube/compare/v3.4.0...v3.5.0) (2024-01-27) + + +### Features + +* add create playlist button in add playlist dialog ([2168a64](https://personal.github.com/krtirtho/spotube/commit/2168a640af3104a43139c303d78e2c2326a1bda7)) +* add spotify friends activity ([#1130](https://personal.github.com/krtirtho/spotube/issues/1130)) ([7983932](https://personal.github.com/krtirtho/spotube/commit/79839329b0970acccb0c566a31eee508adbc8557)) +* **deep-link:** add track opening page ([988a975](https://personal.github.com/krtirtho/spotube/commit/988a975bf1a675df0cfc7b17776bcec74c67f1f2)) +* haptic feedback on long press and reordering actions ([6242200](https://personal.github.com/krtirtho/spotube/commit/624220090572eb643dce37ca8ffd85d2b3f5c9df)) +* improve youtube/piped matching by suffixing "- Topic" ([8184555](https://personal.github.com/krtirtho/spotube/commit/8184555ee89fd30aaf886af9fc1d52c142fdebb0)) +* **translations:** add Nepali (नेपाली) translations ([#1111](https://personal.github.com/krtirtho/spotube/issues/1111)) ([c3ebf56](https://personal.github.com/krtirtho/spotube/commit/c3ebf56ac149b0af8815a5533fe6c386df743440)), closes [#1074](https://personal.github.com/krtirtho/spotube/issues/1074) [#1100](https://personal.github.com/krtirtho/spotube/issues/1100) + + +### Bug Fixes + +* alternative searched sources doesn't play [#1059](https://personal.github.com/krtirtho/spotube/issues/1059) ([a8e9b82](https://personal.github.com/krtirtho/spotube/commit/a8e9b824f33add8f6a83f0d147e889eb6beeb442)) +* alternative source doesn't persist on next restart [#840](https://personal.github.com/krtirtho/spotube/issues/840) ([62fde50](https://personal.github.com/krtirtho/spotube/commit/62fde50442f04f93255b5b1b1dcca23d116a13ec)) +* **android:** download failing for permission issues [#1015](https://personal.github.com/krtirtho/spotube/issues/1015) ([5509cae](https://personal.github.com/krtirtho/spotube/commit/5509cae91c8b1f5cb9fac179060f477397a4a27f)) +* artist page error [#1018](https://personal.github.com/krtirtho/spotube/issues/1018) ([8cd650b](https://personal.github.com/krtirtho/spotube/commit/8cd650b07e5f4c4c2f296bf4374e5ee67fb3eb50)) +* audio resumes after a phone call even if it was paused before [#926](https://personal.github.com/krtirtho/spotube/issues/926) ([fd1899f](https://personal.github.com/krtirtho/spotube/commit/fd1899f162395752142d7aa7320d1c39b0995070)) +* better error message for failing to find lyrics [#1085](https://personal.github.com/krtirtho/spotube/issues/1085) ([e58e18d](https://personal.github.com/krtirtho/spotube/commit/e58e18de33d7bc6fb0e4ddd7ccf6ea14472642b1)) +* Black window flash when starting the app ([#1003](https://personal.github.com/krtirtho/spotube/issues/1003)) ([02e44fc](https://personal.github.com/krtirtho/spotube/commit/02e44fc6b849a873adad382f5d46ed8caf32359f)) +* **linux:** crash after login ([0dfd401](https://personal.github.com/krtirtho/spotube/commit/0dfd40153714b7a4b83ac30f0c56830bc0c05ffd)) +* **macos:** backbutton and window button overlap and unused empty space on home ([b9417ca](https://personal.github.com/krtirtho/spotube/commit/b9417ca3575992673357230dab49e0124dd576b1)) +* **macos:** download folder unchangeable ([9d74cf5](https://personal.github.com/krtirtho/spotube/commit/9d74cf5fc250a6a143321d49b8e045519b4c2872)) +* **macos:** Respect Minimize to tray option ([#1001](https://personal.github.com/krtirtho/spotube/issues/1001)) ([69559ba](https://personal.github.com/krtirtho/spotube/commit/69559ba24285636e42b2f2231f956c31388c5cf3)) +* **macos:** system tray shows name and sidebar weird gap [#1083](https://personal.github.com/krtirtho/spotube/issues/1083) ([27057ea](https://personal.github.com/krtirtho/spotube/commit/27057ea0c8d83c9701057c18b473f1af4e4e82be)) +* releases section is empty when user doesn't follow any artists [#1104](https://personal.github.com/krtirtho/spotube/issues/1104) ([682e88e](https://personal.github.com/krtirtho/spotube/commit/682e88e0c55bc0f4708bc0b4681b129e5c61c999)) +* search page vertical scrollbar moves on horizontal scroll [#1017](https://personal.github.com/krtirtho/spotube/issues/1017) ([c203ac6](https://personal.github.com/krtirtho/spotube/commit/c203ac69ee74ba8722dae3da4b47761cd8d59c34)) +* songs doesn't play when sources with preferred audio codec is empty ([#976](https://personal.github.com/krtirtho/spotube/issues/976)) ([ba4e11a](https://personal.github.com/krtirtho/spotube/commit/ba4e11a40ab18308437a05333a46eace6f8eeb5a)) +* track index not showing after 200 ([a752cf4](https://personal.github.com/krtirtho/spotube/commit/a752cf4c978d1b05851aabb6c84c7862de551320)) +* track pad horizontal scrolling not working ([59e0e6b](https://personal.github.com/krtirtho/spotube/commit/59e0e6bb659b70831f6e0ae064100381c57f149c)) + ## [3.4.0](https://github.com/KRTirtho/spotube/compare/v3.3.0...v3.4.0) (2023-12-30) diff --git a/README.md b/README.md index 791d5da0..18eb55aa 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds. 1. [buttons_tabbar](https://afonsoraposo.com) - A Flutter package that implements a TabBar where each label is a toggle button. 1. [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) - Flutter library to load and cache network images. Can also be used with placeholder and error widgets. -1. [catcher_2](https://github.com/ThexXTURBOXx/catcher_2) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer. +1. [catcher_2](https://github.com/ThexXTURBOXx/catcher_2) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer. 1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections. 1. [cupertino_icons](https://pub.dev/packages/cupertino_icons) - Default icons asset for Cupertino widgets based on Apple styled icons 1. [curved_navigation_bar](https://github.com/rafalbednarczuk/curved_navigation_bar) - Stunning Animating Curved Shape Navigation Bar. Adjustable color, background color, animation curve, animation duration. @@ -222,9 +222,9 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [envied](https://github.com/petercinibulk/envied) - Explicitly reads environment variables into a dart file from a .env file for more security and faster start up times. 1. [file_selector](https://pub.dev/packages/file_selector) - Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. 1. [fl_query](https://fl-query.krtirtho.dev) - Asynchronous data caching, refetching & invalidation library for Flutter -1. [fl_query_hooks](https://fl-query.krtirtho.dev) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter +1. [fl_query_hooks](https://fl-query.krtirtho.dev) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter 1. [fl_query_devtools](https://fl-query.krtirtho.dev) - Devtools support for Fl-Query -1. [fluentui_system_icons](https://github.com/microsoft/fluentui-system-icons/tree/main) - Fluent UI System Icons are a collection of familiar, friendly and modern icons from Microsoft. +1. [fluentui_system_icons](https://github.com/microsoft/fluentui-system-icons/tree/main) - Fluent UI System Icons are a collection of familiar, friendly and modern icons from Microsoft. 1. [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager/tree/develop/flutter_cache_manager) - Generic cache manager for flutter. Saves web files on the storages of the device and saves the cache info using sqflite. 1. [flutter_displaymode](https://github.com/ajinasokan/flutter_displaymode) - A Flutter plugin to set display mode (resolution, refresh rate) on Android platform. Allows to enable high refresh rate on supported devices. 1. [flutter_feather_icons](https://github.com/muj-programmer/flutter_feather_icons) - Feather is a collection of simply beautiful open source icons. Each icon is designed on a 24x24 grid with an emphasis on simplicity, consistency and usability. @@ -235,9 +235,9 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) - Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android. 1. [flutter_svg](https://pub.dev/packages/flutter_svg) - An SVG rendering and widget library for Flutter, which allows painting and displaying Scalable Vector Graphics 1.1 files. 1. [form_validator](https://github.com/TheMisir/form-validator) - Simplest form validation library for flutter's form field widgets -1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs! -1. [google_fonts](https://pub.dev/packages/google_fonts) - A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling. +1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs! 1. [go_router](https://pub.dev/packages/go_router) - A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more +1. [google_fonts](https://pub.dev/packages/google_fonts) - A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling. 1. [hive](https://github.com/hivedb/hive/tree/master/hive) - Lightweight and blazing fast key-value database written in pure Dart. Strongly encrypted using AES-256. 1. [hive_flutter](https://github.com/hivedb/hive/tree/master/hive_flutter) - Extension for Hive. Makes it easier to use Hive in Flutter apps. 1. [hooks_riverpod](https://riverpod.dev) - A simple way to access state from anywhere in your application while robust and testable. @@ -255,13 +255,13 @@ If you are concerned, you can [read the reason of choosing this license](https:/ 1. [package_info_plus](https://plus.fluttercommunity.dev/) - Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android. 1. [palette_generator](https://pub.dev/packages/palette_generator) - Flutter package for generating palette colors from a source image. 1. [path](https://pub.dev/packages/path) - A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web. -1. [path_provider](https://pub.dev/packages/path_provider) - Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. +1. [path_provider](https://pub.dev/packages/path_provider) - Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. 1. [permission_handler](https://pub.dev/packages/permission_handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. 1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video 1. [popover](https://github.com/minikin/popover) - A popover is a transient view that appears above other content onscreen when you tap a control or in an area. 1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter -1. [shared_preferences](https://pub.dev/packages/shared_preferences) - Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. 1. [sidebarx](https://github.com/Frezyx/sidebarx) - flutter multiplatform navigation sidebar / side navigationbar / drawer widget +1. [shared_preferences](https://pub.dev/packages/shared_preferences) - Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. 1. [skeleton_text](https://github.com/101Loop/Skeleton-Text) - A package that provides an easy way to add skeleton text loading animation in Flutter project. This project is a part of 101Loop community. 1. [smtc_windows](https://github.com/KRTirtho/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet. 1. [spotify](https://github.com/rinukkusu/spotify-dart) - An incomplete dart library for interfacing with the Spotify Web API. diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 20b6a47c..eebede99 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "وجود ديسكورد الغني", "browse_all": "تصفح الكل", "genres": "الأنواع الموسيقية", - "explore_genres": "استكشاف الأنواع" + "explore_genres": "استكشاف الأنواع", + "step_3_steps": "انسخ قيمة الكوكي \"sp_dc\"", + "step_4_steps": "الصق قيمة \"sp_dc\" المنسوخة", + "friends": "أصدقاء", + "no_lyrics_available": "عذرًا، تعذر العثور على كلمات الأغنية لهذه العنصر" } \ No newline at end of file diff --git a/lib/l10n/app_bn.arb b/lib/l10n/app_bn.arb index 74dc200d..2711f8d2 100644 --- a/lib/l10n/app_bn.arb +++ b/lib/l10n/app_bn.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "وجود ديسكورد الغني", "browse_all": "تصفح الكل", "genres": "الأنواع الموسيقية", - "explore_genres": "استكشاف الأنواع" + "explore_genres": "استكشاف الأنواع", + "step_3_steps": "কুকি \"sp_dc\" এর মানটি কপি করুন", + "step_4_steps": "কপি করা \"sp_dc\" মানটি পেস্ট করুন", + "friends": "বন্ধু", + "no_lyrics_available": "দুঃখিত, এই ট্র্যাকের জন্য কথা খুঁজে পাওয়া গেলনা" } \ No newline at end of file diff --git a/lib/l10n/app_ca.arb b/lib/l10n/app_ca.arb index 2c952457..f46cfae4 100644 --- a/lib/l10n/app_ca.arb +++ b/lib/l10n/app_ca.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Presència rica de Discord", "browse_all": "Navega per tot", "genres": "Gèneres", - "explore_genres": "Explora els gèneres" + "explore_genres": "Explora els gèneres", + "step_3_steps": "Copia el valor de la cookie \"sp_dc\"", + "step_4_steps": "Pega el valor copiado de \"sp_dc\"", + "friends": "Amics", + "no_lyrics_available": "Ho sentim, no es poden trobar les lletres d'aquesta pista" } \ No newline at end of file diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 59f832ea..ebaa0329 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Discord Rich Presence", "browse_all": "Alles durchsuchen", "genres": "Genres", - "explore_genres": "Genres erkunden" + "explore_genres": "Genres erkunden", + "step_3_steps": "Kopiere den Wert des Cookies \"sp_dc\"", + "step_4_steps": "Füge den kopierten Wert von \"sp_dc\" ein", + "friends": "Freunde", + "no_lyrics_available": "Entschuldigung, Texte für diesen Track konnten nicht gefunden werden" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index e04b4798..476056cb 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Presencia rica en Discord", "browse_all": "Explorar todo", "genres": "Géneros", - "explore_genres": "Explorar géneros" + "explore_genres": "Explorar géneros", + "step_3_steps": "Copia el valor de la cookie \"sp_dc\"", + "step_4_steps": "Pega el valor copiado de \"sp_dc\"", + "friends": "Amigos", + "no_lyrics_available": "Lo siento, no se pueden encontrar las letras de esta pista" } \ No newline at end of file diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb index c9586cde..3a2bcb4b 100644 --- a/lib/l10n/app_fa.arb +++ b/lib/l10n/app_fa.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "حضور غنی دیسکورد", "browse_all": "مرور همه", "genres": "ژانرها", - "explore_genres": "استکشاف ژانرها" + "explore_genres": "استکشاف ژانرها", + "step_3_steps": "مقدار کوکی \"sp_dc\" را کپی کنید", + "step_4_steps": "مقدار کپی شده \"sp_dc\" را الصاق کنید", + "friends": "دوستان", + "no_lyrics_available": "متاسفیم، قادر به یافتن متن این قطعه نیستیم" } \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 0c3eb653..5c24d0fe 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Présence riche de Discord", "browse_all": "Parcourir tout", "genres": "Genres", - "explore_genres": "Explorer les genres" + "explore_genres": "Explorer les genres", + "step_3_steps": "Copiez la valeur du cookie \"sp_dc\"", + "step_4_steps": "Collez la valeur copiée de \"sp_dc\"", + "friends": "Amis", + "no_lyrics_available": "Désolé, impossible de trouver les paroles de cette piste" } \ No newline at end of file diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index dd27dabf..1cf62398 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "डिस्कॉर्ड रिच प्रेजेंस", "browse_all": "सभी को ब्राउज़ करें", "genres": "शैलियाँ", - "explore_genres": "शैलियों का अन्वेषण करें" + "explore_genres": "शैलियों का अन्वेषण करें", + "step_3_steps": "\"sp_dc\" कुकी का मूल्य कॉपी करें", + "step_4_steps": "कॉपी किए गए \"sp_dc\" मूल्य को पेस्ट करें", + "friends": "दोस्त", + "no_lyrics_available": "क्षमा करें, इस ट्रैक के लिए गाने नहीं मिल सके" } \ No newline at end of file diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 3680933a..ec76b914 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -283,5 +283,9 @@ "discord_rich_presence": "Presenza ricca di Discord", "browse_all": "Esplora tutto", "genres": "Generi", - "explore_genres": "Esplora generi" + "explore_genres": "Esplora generi", + "step_3_steps": "Copia il valore del cookie \"sp_dc\"", + "step_4_steps": "Incolla il valore copiato di \"sp_dc\"", + "friends": "Amici", + "no_lyrics_available": "Spiacente, impossibile trovare il testo di questa traccia" } \ No newline at end of file diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 39e0dad8..d16708d7 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "ディスコードリッチプレゼンス", "browse_all": "すべてを閲覧", "genres": "ジャンル", - "explore_genres": "ジャンルを探索" + "explore_genres": "ジャンルを探索", + "step_3_steps": "\"sp_dc\" Cookieの値をコピー", + "step_4_steps": "コピーした\"sp_dc\"の値を貼り付け", + "friends": "友達", + "no_lyrics_available": "申し訳ありませんが、このトラックの歌詞を見つけることができません" } \ No newline at end of file diff --git a/lib/l10n/app_ne.arb b/lib/l10n/app_ne.arb index 9fca9ea4..2d20fc9c 100644 --- a/lib/l10n/app_ne.arb +++ b/lib/l10n/app_ne.arb @@ -115,7 +115,7 @@ "language": "भाषा", "system_default": "सिस्टम पूर्वनिर्धारित", "market_place_region": "बजार स्थान", - "recommendation_country":"सिफारिस गरिएको देश", + "recommendation_country": "सिफारिस गरिएको देश", "appearance": "दृष्टिकोण", "layout_mode": "लेआउट मोड", "override_layout_settings": "अनुकूलित प्रतिकृयात्मक लेआउट मोड सेटिङ्गहरू", @@ -284,5 +284,7 @@ "discord_rich_presence": "डिस्कर्ड धनी उपस्थिति", "browse_all": "सबै हेर्नुहोस्", "genres": "शैलीहरू", - "explore_genres": "शैलीहरू अन्वेषण गर्नुहोस्" + "explore_genres": "शैलीहरू अन्वेषण गर्नुहोस्", + "friends": "साथीहरू", + "no_lyrics_available": "क्षमा गर्दैछौं, यस ट्र्याकका लागि गीतका शब्दहरू फेला परेन" } \ No newline at end of file diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 23eee51b..bf819c57 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -283,5 +283,9 @@ "discord_rich_presence": "Discord Rich Presence", "browse_all": "Alles bekijken", "genres": "Genres", - "explore_genres": "Verken genres" + "explore_genres": "Verken genres", + "step_3_steps": "Kopieer de waarde van de \"sp_dc\"-cookie", + "step_4_steps": "Plak de gekopieerde waarde van \"sp_dc\"", + "friends": "Vrienden", + "no_lyrics_available": "Sorry, kan geen teksten vinden voor deze track" } \ No newline at end of file diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 4ae48338..b7ce8923 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Obecność na Discordzie", "browse_all": "Przeglądaj wszystko", "genres": "Gatunki muzyczne", - "explore_genres": "Eksploruj gatunki" + "explore_genres": "Eksploruj gatunki", + "step_3_steps": "Skopiuj wartość ciasteczka \"sp_dc\"", + "step_4_steps": "Wklej skopiowaną wartość \"sp_dc\"", + "friends": "Przyjaciele", + "no_lyrics_available": "Przepraszamy, nie można znaleźć tekstu dla tego utworu" } \ No newline at end of file diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 5ea4cca0..1c75f734 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Presença rica no Discord", "browse_all": "Navegar por tudo", "genres": "Gêneros", - "explore_genres": "Explorar gêneros" + "explore_genres": "Explorar gêneros", + "step_3_steps": "Copie o valor do cookie \"sp_dc\"", + "step_4_steps": "Cole o valor copiado de \"sp_dc\"", + "friends": "Amigos", + "no_lyrics_available": "Desculpe, não foi possível encontrar a letra desta faixa" } \ No newline at end of file diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 24120d62..7ed67f4f 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Богатое присутствие в Discord", "browse_all": "Просмотреть все", "genres": "Жанры", - "explore_genres": "Исследовать жанры" + "explore_genres": "Исследовать жанры", + "step_3_steps": "Скопируйте значение файла cookie \"sp_dc\"", + "step_4_steps": "Вставьте скопированное значение \"sp_dc\"", + "friends": "Друзья", + "no_lyrics_available": "Извините, не удается найти текст для этого трека" } \ No newline at end of file diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index e6b0ce34..4d9066fd 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Discord Zengin Varlık", "browse_all": "Tümünü Gözat", "genres": "Müzik Türleri", - "explore_genres": "Türleri Keşfet" + "explore_genres": "Türleri Keşfet", + "step_3_steps": "\"sp_dc\" Çerezinin değerini kopyala", + "step_4_steps": "Kopyalanan \"sp_dc\" değerini yapıştır", + "friends": "Arkadaşlar", + "no_lyrics_available": "Üzgünüz, bu parça için şarkı sözleri bulunamıyor" } \ No newline at end of file diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index a5199a04..a4586a5e 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Багата присутність у Discord", "browse_all": "Переглянути все", "genres": "Жанри", - "explore_genres": "Досліджувати жанри" + "explore_genres": "Досліджувати жанри", + "step_3_steps": "Скопіюйте значення cookie \"sp_dc\"", + "step_4_steps": "Вставте скопійоване значення \"sp_dc\"", + "friends": "Друзі", + "no_lyrics_available": "Вибачте, не вдалося знайти текст для цього треку" } \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 30f4a82c..20fdb329 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -282,5 +282,9 @@ "discord_rich_presence": "Discord 丰富展现", "browse_all": "浏览全部", "genres": "音乐类型", - "explore_genres": "探索音乐类型" + "explore_genres": "探索音乐类型", + "step_3_steps": "复制\"sp_dc\" Cookie的值", + "step_4_steps": "粘贴复制的\"sp_dc\"值", + "friends": "朋友", + "no_lyrics_available": "抱歉,无法找到此曲的歌词" } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index f9da61d2..daeaa234 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Open source Spotify client that doesn't require Premium nor uses El publish_to: "none" -version: 3.4.0+27 +version: 3.5.0+28 homepage: https://spotube.krtirtho.dev repository: https://github.com/KRTirtho/spotube diff --git a/untranslated_messages.json b/untranslated_messages.json index ae05dfeb..9e26dfee 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1,125 +1 @@ -{ - "ar": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "bn": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "ca": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "de": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "es": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "fa": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "fr": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "hi": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "it": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "ja": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "ne": [ - "friends", - "no_lyrics_available" - ], - - "nl": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "pl": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "pt": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "ru": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "tr": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "uk": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ], - - "zh": [ - "step_3_steps", - "step_4_steps", - "friends", - "no_lyrics_available" - ] -} +{} \ No newline at end of file From 5d0b5e69a5e43b17561656160fd963de4ff0f833 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 27 Jan 2024 22:39:05 +0600 Subject: [PATCH 46/46] chore: adjust the version --- .github/workflows/spotube-release-binary.yml | 2 +- CHANGELOG.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index dd20ca5d..3ef0ff61 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -4,7 +4,7 @@ on: inputs: version: description: Version to release (x.x.x) - default: 3.4.0 + default: 3.4.1 required: true channel: type: choice diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d784485..8f48b39e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [3.5.0](https://personal.github.com/krtirtho/spotube/compare/v3.4.0...v3.5.0) (2024-01-27) +## [3.4.1](https://personal.github.com/krtirtho/spotube/compare/v3.4.0...v3.4.1) (2024-01-27) ### Features diff --git a/pubspec.yaml b/pubspec.yaml index daeaa234..1b3d15b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Open source Spotify client that doesn't require Premium nor uses El publish_to: "none" -version: 3.5.0+28 +version: 3.4.1+28 homepage: https://spotube.krtirtho.dev repository: https://github.com/KRTirtho/spotube