mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: drop fallback support to different sources
This commit is contained in:
parent
4ea0523692
commit
fb0d9620d0
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -110,7 +109,7 @@ final localTracksProvider =
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
).then((value) => value.whereNotNull().toList());
|
).then((value) => value.nonNulls.toList());
|
||||||
|
|
||||||
final tracksFromMetadata = filesWithMetadata
|
final tracksFromMetadata = filesWithMetadata
|
||||||
.map(
|
.map(
|
||||||
|
@ -11,7 +11,6 @@ import 'package:shelf/shelf.dart';
|
|||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/extensions/track.dart';
|
import 'package:spotube/extensions/track.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
|
||||||
import 'package:spotube/models/parser/range_headers.dart';
|
import 'package:spotube/models/parser/range_headers.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/audio_player/state.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
@ -125,14 +124,9 @@ class ServerPlaybackRoutes {
|
|||||||
)
|
)
|
||||||
.catchError((e, stack) async {
|
.catchError((e, stack) async {
|
||||||
AppLogger.reportError(e, stack);
|
AppLogger.reportError(e, stack);
|
||||||
final sourcedTrack = userPreferences.audioSource == AudioSource.youtube &&
|
final sourcedTrack = await ref
|
||||||
e is DioException
|
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
|
||||||
? await ref
|
.refreshStreamingUrl();
|
||||||
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
|
|
||||||
.refreshStreamingUrl()
|
|
||||||
: await ref
|
|
||||||
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
|
|
||||||
.switchToAlternativeSources();
|
|
||||||
|
|
||||||
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
|
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
|
||||||
|
|
||||||
|
@ -41,18 +41,6 @@ class SourcedTrackNotifier
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SourcedTrack?> switchToAlternativeSources() async {
|
|
||||||
if (arg == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await update((prev) async {
|
|
||||||
return await SourcedTrack.fetchFromTrackAltSource(
|
|
||||||
track: arg!.track,
|
|
||||||
ref: ref,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final sourcedTrackProvider = AsyncNotifierProviderFamily<SourcedTrackNotifier,
|
final sourcedTrackProvider = AsyncNotifierProviderFamily<SourcedTrackNotifier,
|
||||||
|
@ -39,7 +39,7 @@ class CategoryPlaylistsNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
|
|||||||
(json) => PlaylistsFeatured.fromJson(json),
|
(json) => PlaylistsFeatured.fromJson(json),
|
||||||
).getPage(limit, offset);
|
).getPage(limit, offset);
|
||||||
|
|
||||||
final items = playlists.items?.whereNotNull().toList() ?? [];
|
final items = playlists.items?.nonNulls.toList() ?? [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
items: items,
|
items: items,
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
import 'package:spotube/services/sourced_track/enums.dart';
|
import 'package:spotube/services/sourced_track/enums.dart';
|
||||||
import 'package:spotube/services/sourced_track/exceptions.dart';
|
|
||||||
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/source_map.dart';
|
import 'package:spotube/services/sourced_track/models/source_map.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/invidious.dart';
|
import 'package:spotube/services/sourced_track/sources/invidious.dart';
|
||||||
@ -17,7 +11,6 @@ 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/piped.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|
||||||
|
|
||||||
abstract class SourcedTrack extends Track {
|
abstract class SourcedTrack extends Track {
|
||||||
final SourceMap source;
|
final SourceMap source;
|
||||||
@ -97,11 +90,8 @@ abstract class SourcedTrack extends Track {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String getSearchTerm(Track track) {
|
static String getSearchTerm(Track track) {
|
||||||
final artists = (track.artists ?? [])
|
final artists =
|
||||||
.map((ar) => ar.name)
|
(track.artists ?? []).map((ar) => ar.name).toList().nonNulls.toList();
|
||||||
.toList()
|
|
||||||
.whereNotNull()
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final title = ServiceUtils.getTitle(
|
final title = ServiceUtils.getTitle(
|
||||||
track.name!,
|
track.name!,
|
||||||
@ -112,100 +102,21 @@ abstract class SourcedTrack extends Track {
|
|||||||
return "$title - ${artists.join(", ")}";
|
return "$title - ${artists.join(", ")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchFromTrackAltSource({
|
|
||||||
required Track track,
|
|
||||||
required Ref ref,
|
|
||||||
}) async {
|
|
||||||
final preferences = ref.read(userPreferencesProvider);
|
|
||||||
try {
|
|
||||||
return switch (preferences.audioSource) {
|
|
||||||
AudioSource.piped ||
|
|
||||||
AudioSource.invidious ||
|
|
||||||
AudioSource.jiosaavn =>
|
|
||||||
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
|
||||||
AudioSource.youtube =>
|
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
|
||||||
};
|
|
||||||
} on TrackNotFoundError catch (_) {
|
|
||||||
return switch (preferences.audioSource) {
|
|
||||||
AudioSource.piped ||
|
|
||||||
AudioSource.youtube ||
|
|
||||||
AudioSource.invidious =>
|
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(
|
|
||||||
track: track,
|
|
||||||
ref: ref,
|
|
||||||
weakMatch: true,
|
|
||||||
),
|
|
||||||
AudioSource.jiosaavn =>
|
|
||||||
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
|
||||||
};
|
|
||||||
} on HttpClientClosedException catch (_) {
|
|
||||||
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
|
||||||
} on VideoUnplayableException catch (_) {
|
|
||||||
return await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
|
||||||
} catch (e) {
|
|
||||||
if (e is DioException || e is ClientException || e is SocketException) {
|
|
||||||
return await JioSaavnSourcedTrack.fetchFromTrack(
|
|
||||||
track: track,
|
|
||||||
ref: ref,
|
|
||||||
weakMatch: preferences.audioSource == AudioSource.jiosaavn,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<SourcedTrack> fetchFromTrack({
|
static Future<SourcedTrack> fetchFromTrack({
|
||||||
required Track track,
|
required Track track,
|
||||||
required Ref ref,
|
required Ref ref,
|
||||||
}) async {
|
}) async {
|
||||||
final preferences = ref.read(userPreferencesProvider);
|
final preferences = ref.read(userPreferencesProvider);
|
||||||
try {
|
return switch (preferences.audioSource) {
|
||||||
return switch (preferences.audioSource) {
|
AudioSource.youtube =>
|
||||||
AudioSource.piped =>
|
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
AudioSource.piped =>
|
||||||
AudioSource.youtube =>
|
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
AudioSource.invidious =>
|
||||||
AudioSource.jiosaavn =>
|
await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
AudioSource.jiosaavn =>
|
||||||
AudioSource.invidious =>
|
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
};
|
||||||
};
|
|
||||||
} on TrackNotFoundError catch (_) {
|
|
||||||
return switch (preferences.audioSource) {
|
|
||||||
AudioSource.piped ||
|
|
||||||
AudioSource.youtube ||
|
|
||||||
AudioSource.invidious =>
|
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(
|
|
||||||
track: track,
|
|
||||||
ref: ref,
|
|
||||||
weakMatch: true,
|
|
||||||
),
|
|
||||||
AudioSource.jiosaavn =>
|
|
||||||
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
|
||||||
};
|
|
||||||
} on HttpClientClosedException catch (_) {
|
|
||||||
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
|
||||||
} on VideoUnplayableException catch (_) {
|
|
||||||
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
|
||||||
} catch (e) {
|
|
||||||
if (e is DioException || e is ClientException || e is SocketException) {
|
|
||||||
return switch (preferences.audioSource) {
|
|
||||||
AudioSource.piped ||
|
|
||||||
AudioSource.invidious =>
|
|
||||||
await YoutubeSourcedTrack.fetchFromTrack(
|
|
||||||
track: track,
|
|
||||||
ref: ref,
|
|
||||||
),
|
|
||||||
_ => await JioSaavnSourcedTrack.fetchFromTrack(
|
|
||||||
track: track,
|
|
||||||
ref: ref,
|
|
||||||
weakMatch: preferences.audioSource == AudioSource.jiosaavn,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<SiblingType>> fetchSiblings({
|
static Future<List<SiblingType>> fetchSiblings({
|
||||||
|
@ -50,6 +50,22 @@ class InvidiousSourcedTrack extends SourcedTrack {
|
|||||||
required Track track,
|
required Track track,
|
||||||
required Ref ref,
|
required Ref ref,
|
||||||
}) async {
|
}) async {
|
||||||
|
// Indicates a stream url refresh
|
||||||
|
if (track is InvidiousSourcedTrack) {
|
||||||
|
final manifest = await ref
|
||||||
|
.read(invidiousProvider)
|
||||||
|
.videos
|
||||||
|
.get(track.sourceInfo.id, local: true);
|
||||||
|
|
||||||
|
return InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: track.siblings,
|
||||||
|
source: toSourceMap(manifest),
|
||||||
|
sourceInfo: track.sourceInfo,
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
|
@ -50,6 +50,19 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
required Track track,
|
required Track track,
|
||||||
required Ref ref,
|
required Ref ref,
|
||||||
}) async {
|
}) async {
|
||||||
|
// Means it wants a refresh of the stream
|
||||||
|
if (track is PipedSourcedTrack) {
|
||||||
|
final manifest =
|
||||||
|
await ref.read(pipedProvider).streams(track.sourceInfo.id);
|
||||||
|
return PipedSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: track.siblings,
|
||||||
|
sourceInfo: track.sourceInfo,
|
||||||
|
source: toSourceMap(manifest),
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
@ -183,11 +196,8 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
: preference.searchMode == SearchMode.youtubeMusic;
|
: preference.searchMode == SearchMode.youtubeMusic;
|
||||||
|
|
||||||
if (isYouTubeMusic) {
|
if (isYouTubeMusic) {
|
||||||
final artists = (track.artists ?? [])
|
final artists =
|
||||||
.map((ar) => ar.name)
|
(track.artists ?? []).map((ar) => ar.name).toList().nonNulls.toList();
|
||||||
.toList()
|
|
||||||
.whereNotNull()
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return await Future.wait(
|
return await Future.wait(
|
||||||
searchResults
|
searchResults
|
||||||
|
@ -268,7 +268,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
final query = SourcedTrack.getSearchTerm(track);
|
final query = SourcedTrack.getSearchTerm(track);
|
||||||
|
|
||||||
final searchResults =
|
final searchResults =
|
||||||
await ref.read(youtubeEngineProvider).searchVideos("$query - Topic");
|
await ref.read(youtubeEngineProvider).searchVideos(query);
|
||||||
|
|
||||||
if (ServiceUtils.onlyContainsEnglish(query)) {
|
if (ServiceUtils.onlyContainsEnglish(query)) {
|
||||||
return await Future.wait(searchResults
|
return await Future.wait(searchResults
|
||||||
|
@ -96,7 +96,7 @@ class NewPipeEngine implements YouTubeEngine {
|
|||||||
Future<List<Video>> searchVideos(String query) async {
|
Future<List<Video>> searchVideos(String query) async {
|
||||||
final results = await NewPipeExtractor.search(
|
final results = await NewPipeExtractor.search(
|
||||||
query,
|
query,
|
||||||
contentFilters: [SearchContentFilters.musicSongs],
|
contentFilters: [SearchContentFilters.videos],
|
||||||
);
|
);
|
||||||
|
|
||||||
final resultsWithVideos = results
|
final resultsWithVideos = results
|
||||||
|
Loading…
Reference in New Issue
Block a user