refactor: drop fallback support to different sources

This commit is contained in:
Kingkor Roy Tirtho 2025-02-11 21:30:58 +06:00
parent 4ea0523692
commit fb0d9620d0
9 changed files with 50 additions and 132 deletions

View File

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -110,7 +109,7 @@ final localTracksProvider =
return null;
}
}),
).then((value) => value.whereNotNull().toList());
).then((value) => value.nonNulls.toList());
final tracksFromMetadata = filesWithMetadata
.map(

View File

@ -11,7 +11,6 @@ import 'package:shelf/shelf.dart';
import 'package:spotube/extensions/artist_simple.dart';
import 'package:spotube/extensions/image.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/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/audio_player/state.dart';
@ -125,14 +124,9 @@ class ServerPlaybackRoutes {
)
.catchError((e, stack) async {
AppLogger.reportError(e, stack);
final sourcedTrack = userPreferences.audioSource == AudioSource.youtube &&
e is DioException
? await ref
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
.refreshStreamingUrl()
: await ref
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
.switchToAlternativeSources();
final sourcedTrack = await ref
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
.refreshStreamingUrl();
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);

View File

@ -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,

View File

@ -39,7 +39,7 @@ class CategoryPlaylistsNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
(json) => PlaylistsFeatured.fromJson(json),
).getPage(limit, offset);
final items = playlists.items?.whereNotNull().toList() ?? [];
final items = playlists.items?.nonNulls.toList() ?? [];
return (
items: items,

View File

@ -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:spotify/spotify.dart';
import 'package:spotube/models/database/database.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/exceptions.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/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/youtube.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
abstract class SourcedTrack extends Track {
final SourceMap source;
@ -97,11 +90,8 @@ abstract class SourcedTrack extends Track {
}
static String getSearchTerm(Track track) {
final artists = (track.artists ?? [])
.map((ar) => ar.name)
.toList()
.whereNotNull()
.toList();
final artists =
(track.artists ?? []).map((ar) => ar.name).toList().nonNulls.toList();
final title = ServiceUtils.getTitle(
track.name!,
@ -112,100 +102,21 @@ abstract class SourcedTrack extends Track {
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({
required Track track,
required Ref ref,
}) async {
final preferences = ref.read(userPreferencesProvider);
try {
return switch (preferences.audioSource) {
AudioSource.piped =>
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.youtube =>
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.jiosaavn =>
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.invidious =>
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;
}
return switch (preferences.audioSource) {
AudioSource.youtube =>
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.piped =>
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.invidious =>
await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref),
AudioSource.jiosaavn =>
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
};
}
static Future<List<SiblingType>> fetchSiblings({

View File

@ -50,6 +50,22 @@ class InvidiousSourcedTrack extends SourcedTrack {
required Track track,
required Ref ref,
}) 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 cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))

View File

@ -50,6 +50,19 @@ class PipedSourcedTrack extends SourcedTrack {
required Track track,
required Ref ref,
}) 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 cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
@ -183,11 +196,8 @@ class PipedSourcedTrack extends SourcedTrack {
: preference.searchMode == SearchMode.youtubeMusic;
if (isYouTubeMusic) {
final artists = (track.artists ?? [])
.map((ar) => ar.name)
.toList()
.whereNotNull()
.toList();
final artists =
(track.artists ?? []).map((ar) => ar.name).toList().nonNulls.toList();
return await Future.wait(
searchResults

View File

@ -268,7 +268,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
final query = SourcedTrack.getSearchTerm(track);
final searchResults =
await ref.read(youtubeEngineProvider).searchVideos("$query - Topic");
await ref.read(youtubeEngineProvider).searchVideos(query);
if (ServiceUtils.onlyContainsEnglish(query)) {
return await Future.wait(searchResults

View File

@ -96,7 +96,7 @@ class NewPipeEngine implements YouTubeEngine {
Future<List<Video>> searchVideos(String query) async {
final results = await NewPipeExtractor.search(
query,
contentFilters: [SearchContentFilters.musicSongs],
contentFilters: [SearchContentFilters.videos],
);
final resultsWithVideos = results