Skip to content

Commit

Permalink
feat: start radio support
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Jan 31, 2024
1 parent 5d0b5e6 commit 4defeef
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 27 deletions.
1 change: 1 addition & 0 deletions lib/collections/spotube_icons.dart
Expand Up @@ -111,4 +111,5 @@ abstract class SpotubeIcons {
static const wikipedia = SimpleIcons.wikipedia;
static const discord = SimpleIcons.discord;
static const youtube = SimpleIcons.youtube;
static const radio = FeatherIcons.radio;
}
79 changes: 77 additions & 2 deletions lib/components/shared/track_tile/track_options.dart
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart' hide Page;
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
Expand All @@ -10,6 +11,7 @@ import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/library/user_local_tracks.dart';
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart';
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
import 'package:spotube/components/shared/dialogs/track_details_dialog.dart';
import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
Expand All @@ -20,7 +22,9 @@ import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/services/mutations/mutations.dart';
import 'package:spotube/services/queries/search.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

enum TrackOptionValue {
Expand All @@ -36,6 +40,7 @@ enum TrackOptionValue {
favorite,
details,
download,
startRadio,
}

class TrackOptions extends HookConsumerWidget {
Expand Down Expand Up @@ -82,6 +87,67 @@ class TrackOptions extends HookConsumerWidget {
);
}

void actionStartRadio(
BuildContext context,
WidgetRef ref,
Track track,
) async {
final playback = ref.read(ProxyPlaylistNotifier.notifier);
final playlist = ref.read(ProxyPlaylistNotifier.provider);
final spotify = ref.read(spotifyProvider);
final pages = await QueryClient.of(context)
.fetchInfiniteQueryJob<List<Page>, dynamic, int, SearchParams>(
job: SearchQueries.queryJob(SearchType.playlist.name),
args: (
spotify: spotify,
searchType: SearchType.playlist,
query: "${track.name} Radio"
),
) ??
[];

final radios = pages.expand((e) => e.items ?? <PlaylistSimple>[]).toList();

final artists = track.artists!.map((e) => e.name);

final radio = radios.firstWhere(
(e) =>
e.name == "${track.name} Radio" &&
artists.where((a) => e.name!.contains(a!)).length >= 2,
orElse: () => radios.first,
);

bool replaceQueue = false;

if (context.mounted && playlist.tracks.isNotEmpty) {
replaceQueue = await showPromptDialog(
context: context,
title: context.l10n.how_to_start_radio,
message: context.l10n.replace_queue_question,
okText: context.l10n.replace,
cancelText: context.l10n.add_to_queue,
);
}

if (replaceQueue) {
await playback.stop();
await playback.load([track], autoPlay: true);
} else {
await playback.addTrack(track);
}

final tracks =
await spotify.playlists.getTracksByPlaylistId(radio.id!).all();

await playback.addTracks(
tracks.toList()
..removeWhere((e) {
final isDuplicate = playlist.tracks.any((t) => t.id == e.id);
return e.id == track.id || isDuplicate;
}),
);
}

@override
Widget build(BuildContext context, ref) {
final scaffoldMessenger = ScaffoldMessenger.of(context);
Expand Down Expand Up @@ -207,6 +273,9 @@ class TrackOptions extends HookConsumerWidget {
case TrackOptionValue.download:
await downloadManager.addToQueue(track);
break;
case TrackOptionValue.startRadio:
actionStartRadio(context, ref, track);
break;
}
},
icon: icon ?? const Icon(SpotubeIcons.moreHorizontal),
Expand Down Expand Up @@ -287,12 +356,18 @@ class TrackOptions extends HookConsumerWidget {
: context.l10n.save_as_favorite,
),
),
if (auth != null)
if (auth != null) ...[
PopSheetEntry(
value: TrackOptionValue.startRadio,
leading: const Icon(SpotubeIcons.radio),
title: Text(context.l10n.start_a_radio),
),
PopSheetEntry(
value: TrackOptionValue.addToPlaylist,
leading: const Icon(SpotubeIcons.playlistAdd),
title: Text(context.l10n.add_to_playlist),
),
],
if (userPlaylist && auth != null)
PopSheetEntry(
value: TrackOptionValue.removeFromPlaylist,
Expand Down
5 changes: 4 additions & 1 deletion lib/l10n/app_en.arb
Expand Up @@ -286,5 +286,8 @@
"genres": "Genres",
"explore_genres": "Explore Genres",
"friends": "Friends",
"no_lyrics_available": "Sorry, unable find lyrics for this track"
"no_lyrics_available": "Sorry, unable find lyrics for this track",
"start_a_radio": "Start a Radio",
"how_to_start_radio": "How do you want to start the radio?",
"replace_queue_question": "Do you want to replace the current queue or append to it?"
}
70 changes: 47 additions & 23 deletions lib/services/queries/search.dart
@@ -1,36 +1,60 @@
import 'package:fl_query/fl_query.dart';
import 'package:fl_query_hooks/fl_query_hooks.dart';
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_infinite_query.dart';
import 'package:spotube/provider/spotify_provider.dart';

typedef SearchParams = ({
SpotifyApi spotify,
SearchType searchType,
String query
});

class SearchQueries {
const SearchQueries();

static final queryJob =
InfiniteQueryJob.withVariableKey<List<Page>, dynamic, int, SearchParams>(
baseQueryKey: "search-query",
task: (variableKey, page, args) => args!.spotify.search.get(
args.query,
types: [args.searchType],
).getPage(10, page),
initialPage: 0,
nextPage: (lastPage, lastPageData) {
if (lastPageData.isEmpty) return null;
if ((lastPageData.first.isLast ||
(lastPageData.first.items ?? []).length < 10)) {
return null;
}
return lastPageData.first.nextOffset;
},
enabled: false,
);

InfiniteQuery<List<Page>, dynamic, int> query(
WidgetRef ref,
String query,
String queryStr,
SearchType searchType,
) {
return useSpotifyInfiniteQuery<List<Page>, dynamic, int>(
"search-query/${searchType.name}",
(page, spotify) {
if (query.trim().isEmpty) return [];
final queryString = query;
return spotify.search.get(
queryString,
types: [searchType],
).getPage(10, page);
},
enabled: false,
ref: ref,
initialPage: 0,
nextPage: (lastPage, lastPageData) {
if (lastPageData.isEmpty) return null;
if ((lastPageData.first.isLast ||
(lastPageData.first.items ?? []).length < 10)) {
return null;
}
return lastPageData.first.nextOffset;
},
final spotify = ref.watch(spotifyProvider);
final query = useInfiniteQueryJob<List<Page>, dynamic, int, SearchParams>(
job: queryJob(searchType.name),
args: (spotify: spotify, searchType: searchType, query: queryStr),
);

useEffect(() {
return ref.listenManual(
spotifyProvider,
(previous, next) {
if (previous != next) {
query.refreshAll();
}
},
).close;
}, [query]);

return query;
}
}
110 changes: 109 additions & 1 deletion untranslated_messages.json
@@ -1 +1,109 @@
{}
{
"ar": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"bn": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"ca": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"de": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"es": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"fa": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"fr": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"hi": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"it": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"ja": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"ne": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"nl": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"pl": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"pt": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"ru": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"tr": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"uk": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
],

"zh": [
"start_a_radio",
"how_to_start_radio",
"replace_queue_question"
]
}

0 comments on commit 4defeef

Please sign in to comment.