Skip to content

Commit

Permalink
feat: piped instance picker on settings
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Jun 4, 2023
1 parent 3aeb026 commit bed0d3b
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 80 deletions.
1 change: 1 addition & 0 deletions lib/collections/spotube_icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ abstract class SpotubeIcons {
static const colorSync = FeatherIcons.activity;
static const language = FeatherIcons.globe;
static const error = FeatherIcons.alertTriangle;
static const piped = FeatherIcons.cloud;
}
42 changes: 26 additions & 16 deletions lib/components/shared/adaptive/adaptive_select_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class AdaptiveSelectTile<T> extends HookWidget {

final Breakpoints breakAfterOr;

/// Show the smaller value when the breakpoint is reached
///
/// If false, the control will be hidden when the breakpoint is reached
///
/// Defaults to `true`
final bool showValueWhenUnfolded;

const AdaptiveSelectTile({
required this.title,
required this.value,
Expand All @@ -23,6 +30,7 @@ class AdaptiveSelectTile<T> extends HookWidget {
this.subtitle,
this.secondary,
this.breakAfterOr = Breakpoints.md,
this.showValueWhenUnfolded = true,
super.key,
});

Expand All @@ -49,22 +57,24 @@ class AdaptiveSelectTile<T> extends HookWidget {

final control = breakpoint >= breakAfterOr
? rawControl
: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
border: Border.all(
color: theme.colorScheme.primary,
width: 2,
),
borderRadius: BorderRadius.circular(10),
),
child: DefaultTextStyle(
style: TextStyle(
color: theme.colorScheme.primary,
),
child: controlPlaceholder,
),
);
: showValueWhenUnfolded
? Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
border: Border.all(
color: theme.colorScheme.primary,
width: 2,
),
borderRadius: BorderRadius.circular(10),
),
child: DefaultTextStyle(
style: TextStyle(
color: theme.colorScheme.primary,
),
child: controlPlaceholder,
),
)
: const SizedBox.shrink();

return ListTile(
title: title,
Expand Down
4 changes: 3 additions & 1 deletion lib/l10n/app_bn.arb
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,7 @@
"success_message": "এখন আপনি সফলভাবে আপনার Spotify অ্যাকাউন্ট দিয়ে লগ ইন করেছেন। সাধুভাত আপনাকে",
"step_4": "ধাপ 4",
"step_4_steps": "কপি করা \"sp_dc\" এবং \"sp_key\" এর মান সংশ্লিষ্ট ফিল্ডে পেস্ট করুন",
"something_went_wrong": "কিছু ভুল হয়েছে"
"something_went_wrong": "কিছু ভুল হয়েছে",
"piped_instance": "Piped সার্ভার এড্রেস",
"piped_description": "গান ম্যাচ করার জন্য ব্যবহৃত পাইপড সার্ভার\n এগুলোর মধ্যে কিছু ভাল কাজ নাও করতে পারে৷ তাই নিজ দায়িত্বে ব্যবহার করুন"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,7 @@
"success_message": "Now you're 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\" values in the respective fields",
"something_went_wrong": "Something went wrong"
"something_went_wrong": "Something went wrong",
"piped_instance": "Piped Server Instance",
"piped_description": "The Piped server instance to use for track matching\nSome of them might not work well. So use at your own risk"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,7 @@
"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\" dans les champs respectifs",
"something_went_wrong": "Quelque chose s'est mal passé"
"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\nCertaines d'entre elles peuvent ne pas fonctionner correctement. Alors utilisez à vos risques et périls"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_hi.arb
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,7 @@
"success_message": "अब आप अपने स्पॉटिफाई अकाउंट से सफलतापूर्वक लॉगइन हो गए हैं। अच्छा काम किया!",
"step_4": "स्टेप 4",
"step_4_steps": "कॉपी की गई \"sp_dc\" और \"sp_key\" मानों को संबंधित फील्ड में पेस्ट करें",
"something_went_wrong": "कुछ गलत हो गया"
"something_went_wrong": "कुछ गलत हो गया",
"piped_instance": "पाइप्ड सर्वर",
"piped_description": "पाइप किए गए सर्वर\n गानों का मिलान करने के लिए उपयोग किए जाते हैं, हो सकता है कि उनमें से कुछ के साथ ठीक से काम न करें इसलिए अपने जोखिम पर उपयोग करें"
}
5 changes: 0 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/palette_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/youtube.dart';
import 'package:spotube/themes/theme.dart';
import 'package:spotube/utils/custom_toast_handler.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
Expand Down Expand Up @@ -206,10 +205,6 @@ class SpotubeState extends ConsumerState<Spotube> {
void initState() {
super.initState();
SharedPreferences.getInstance().then(((value) => localStorage = value));

/// Doing the initialization here to avoid loading time
/// when in offline mode
PipedSpotube.initialize();
}

@override
Expand Down
22 changes: 13 additions & 9 deletions lib/models/spotube_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:spotube/extensions/album_simple.dart';
import 'package:spotube/extensions/artist_simple.dart';
import 'package:spotube/models/matched_track.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/youtube.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:collection/collection.dart';
Expand Down Expand Up @@ -67,7 +66,10 @@ class SpotubeTrack extends Track {
}
}

static Future<List<PipedSearchItemStream>> fetchSiblings(Track track) async {
static Future<List<PipedSearchItemStream>> fetchSiblings(
Track track,
PipedClient client,
) async {
final artists = (track.artists ?? [])
.map((ar) => ar.name)
.toList()
Expand All @@ -80,7 +82,7 @@ class SpotubeTrack extends Track {
onlyCleanArtist: true,
).trim();

final List<PipedSearchItemStream> siblings = await PipedSpotube.client
final List<PipedSearchItemStream> siblings = await client
.search(
"$title - ${artists.join(", ")}",
PipedFilter.musicSongs,
Expand Down Expand Up @@ -112,18 +114,19 @@ class SpotubeTrack extends Track {
static Future<SpotubeTrack> fetchFromTrack(
Track track,
UserPreferences preferences,
PipedClient client,
) async {
final matchedCachedTrack = await MatchedTrack.box.get(track.id!);
var siblings = <PipedSearchItemStream>[];
PipedStreamResponse ytVideo;
if (matchedCachedTrack != null) {
ytVideo = await PipedSpotube.client.streams(matchedCachedTrack.youtubeId);
ytVideo = await client.streams(matchedCachedTrack.youtubeId);
} else {
siblings = await fetchSiblings(track);
siblings = await fetchSiblings(track, client);
if (siblings.isEmpty) {
throw Exception("Failed to find any results for ${track.name}");
}
ytVideo = await PipedSpotube.client.streams(siblings.first.id);
ytVideo = await client.streams(siblings.first.id);

await MatchedTrack.box.put(
track.id!,
Expand Down Expand Up @@ -167,10 +170,11 @@ class SpotubeTrack extends Track {
Future<SpotubeTrack?> swappedCopy(
PipedSearchItemStream video,
UserPreferences preferences,
PipedClient client,
) async {
if (siblings.none((element) => element.id == video.id)) return null;

final ytVideo = await PipedSpotube.client.streams(video.id);
final ytVideo = await client.streams(video.id);

final ytStream = getStreamInfo(ytVideo, preferences.audioQuality);

Expand Down Expand Up @@ -225,10 +229,10 @@ class SpotubeTrack extends Track {
);
}

Future<SpotubeTrack> populatedCopy() async {
Future<SpotubeTrack> populatedCopy(PipedClient client) async {
if (this.siblings.isNotEmpty) return this;

final siblings = await fetchSiblings(this);
final siblings = await fetchSiblings(this, client);

return SpotubeTrack.fromTrack(
track: this,
Expand Down
38 changes: 38 additions & 0 deletions lib/pages/settings/settings.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:piped_client/piped_client.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/language_codes.dart';

Expand All @@ -21,6 +23,7 @@ import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/provider/piped_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';

class SettingsPage extends HookConsumerWidget {
Expand Down Expand Up @@ -286,6 +289,41 @@ class SettingsPage extends HookConsumerWidget {
}
},
),
Consumer(builder: (context, ref, child) {
final instanceList =
ref.watch(pipedInstancesFutureProvider);

return instanceList.when(
data: (data) {
return AdaptiveSelectTile<String>(
secondary: const Icon(SpotubeIcons.piped),
title: Text(context.l10n.piped_instance),
subtitle: Text(context.l10n.piped_description),
value: preferences.pipedInstance,
showValueWhenUnfolded: false,
options: data
.sortedBy((e) => e.name)
.map(
(e) => DropdownMenuItem(
value: e.apiUrl,
child: Text(e.name),
),
)
.toList(),
onChanged: (value) {
if (value != null) {
preferences.setPipedInstance(value);
}
},
);
},
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stackTrace) =>
Text(error.toString()),
);
}),
SwitchListTile(
secondary: const Icon(SpotubeIcons.download),
title: Text(context.l10n.pre_download_play),
Expand Down
2 changes: 2 additions & 0 deletions lib/provider/downloader_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:spotify/spotify.dart' hide Image, Queue;
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/piped_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

Expand Down Expand Up @@ -50,6 +51,7 @@ class Downloader with ChangeNotifier {
final track = await SpotubeTrack.fetchFromTrack(
baseTrack,
ref.read(userPreferencesProvider),
ref.read(pipedClientProvider),
);

_queue.add(() async {
Expand Down
18 changes: 18 additions & 0 deletions lib/provider/piped_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:piped_client/piped_client.dart';
import 'package:spotube/provider/user_preferences_provider.dart';

PipedClient _defaultClient = PipedClient();

final pipedClientProvider = Provider((ref) {
final instanceUrl =
ref.watch(userPreferencesProvider.select((s) => s.pipedInstance));

if (instanceUrl == "https://pipedapi.kavin.rocks") return _defaultClient;

return PipedClient(instance: instanceUrl);
});

final pipedInstancesFutureProvider = FutureProvider<List<PipedInstance>>(
(ref) async => _defaultClient.instanceList(),
);
10 changes: 8 additions & 2 deletions lib/provider/proxy_playlist/next_fetcher_mixin.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:piped_client/piped_client.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/models/matched_track.dart';
Expand All @@ -11,7 +12,8 @@ import 'package:spotube/services/supabase.dart';

mixin NextFetcher on StateNotifier<ProxyPlaylist> {
Future<List<SpotubeTrack>> fetchTracks(
UserPreferences preferences, {
UserPreferences preferences,
PipedClient pipedClient, {
int count = 3,
int offset = 0,
}) async {
Expand All @@ -25,7 +27,11 @@ mixin NextFetcher on StateNotifier<ProxyPlaylist> {
/// fetch [bareTracks] one by one with 100ms delay
final fetchedTracks = await Future.wait(
bareTracks.mapIndexed((i, track) async {
final future = SpotubeTrack.fetchFromTrack(track, preferences);
final future = SpotubeTrack.fetchFromTrack(
track,
preferences,
pipedClient,
);
if (i == 0) {
return await future;
}
Expand Down
Loading

0 comments on commit bed0d3b

Please sign in to comment.