mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: non-banger songs breaking the queue if sources not found
Now fallbacks to another audio source if not found in one
This commit is contained in:
parent
cd669e22c1
commit
90f7c531cd
@ -144,8 +144,7 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
// Removing tracks that were not found to avoid queue interruption
|
||||
// TODO: Add a flag to enable/disable skip not found tracks
|
||||
if (e is TrackNotFoundException) {
|
||||
if (e is TrackNotFoundError) {
|
||||
final oldTrack =
|
||||
mapSourcesToTracks([audioPlayer.nextSource!]).firstOrNull;
|
||||
await removeTrack(oldTrack!.id!);
|
||||
|
@ -1,7 +1,12 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
class TrackNotFoundException implements Exception {
|
||||
factory TrackNotFoundException(Track track) {
|
||||
throw Exception("Failed to find any results for ${track.name}");
|
||||
class TrackNotFoundError extends Error {
|
||||
final Track track;
|
||||
|
||||
TrackNotFoundError(this.track);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '[TrackNotFoundError] ${track.name} - ${track.artists?.map((e) => e.name).join(", ")}';
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
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/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_state.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/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;
|
||||
@ -101,9 +107,8 @@ abstract class SourcedTrack extends Track {
|
||||
required Track track,
|
||||
required Ref ref,
|
||||
}) async {
|
||||
final preferences = ref.read(userPreferencesProvider);
|
||||
try {
|
||||
final preferences = ref.read(userPreferencesProvider);
|
||||
|
||||
return switch (preferences.audioSource) {
|
||||
AudioSource.piped =>
|
||||
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||
@ -112,8 +117,35 @@ abstract class SourcedTrack extends Track {
|
||||
AudioSource.jiosaavn =>
|
||||
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||
};
|
||||
} on TrackNotFoundError catch (_) {
|
||||
return switch (preferences.audioSource) {
|
||||
AudioSource.piped ||
|
||||
AudioSource.youtube =>
|
||||
await JioSaavnSourcedTrack.fetchFromTrack(
|
||||
track: track,
|
||||
ref: ref,
|
||||
weakMatch: true,
|
||||
),
|
||||
AudioSource.jiosaavn =>
|
||||
await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||
};
|
||||
} on HttpClientClosedException catch (_) {
|
||||
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||
} catch (e) {
|
||||
return YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||
if (e is DioException || e is ClientException || e is SocketException) {
|
||||
if (preferences.audioSource == AudioSource.jiosaavn) {
|
||||
return await JioSaavnSourcedTrack.fetchFromTrack(
|
||||
track: track,
|
||||
ref: ref,
|
||||
weakMatch: true,
|
||||
);
|
||||
}
|
||||
return await JioSaavnSourcedTrack.fetchFromTrack(
|
||||
track: track,
|
||||
ref: ref,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,15 +37,17 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
||||
static Future<SourcedTrack> fetchFromTrack({
|
||||
required Track track,
|
||||
required Ref ref,
|
||||
bool weakMatch = false,
|
||||
}) async {
|
||||
final cachedSource = await SourceMatch.box.get(track.id);
|
||||
|
||||
if (cachedSource == null ||
|
||||
cachedSource.sourceType != SourceType.jiosaavn) {
|
||||
final siblings = await fetchSiblings(ref: ref, track: track);
|
||||
final siblings =
|
||||
await fetchSiblings(ref: ref, track: track, weakMatch: weakMatch);
|
||||
|
||||
if (siblings.isEmpty) {
|
||||
throw TrackNotFoundException(track);
|
||||
throw TrackNotFoundError(track);
|
||||
}
|
||||
|
||||
await SourceMatch.box.put(
|
||||
@ -119,6 +121,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
||||
static Future<List<SiblingType>> fetchSiblings({
|
||||
required Track track,
|
||||
required Ref ref,
|
||||
bool weakMatch = false,
|
||||
}) async {
|
||||
final query = SourcedTrack.getSearchTerm(track);
|
||||
|
||||
@ -126,9 +129,12 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
||||
await jiosaavnClient.search.songs(query, limit: 20);
|
||||
|
||||
final trackArtistNames = track.artists?.map((ar) => ar.name).toList();
|
||||
return results
|
||||
|
||||
final matchedResults = results
|
||||
.where(
|
||||
(s) {
|
||||
s.name?.unescapeHtml().contains(track.name!) ?? false;
|
||||
|
||||
final sameName = s.name?.unescapeHtml() == track.name;
|
||||
final artistNames = [
|
||||
s.primaryArtists,
|
||||
@ -139,12 +145,27 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
||||
(artist) =>
|
||||
trackArtistNames?.any((ar) => artist == ar) ?? false,
|
||||
);
|
||||
if (weakMatch) {
|
||||
final containsName =
|
||||
s.name?.unescapeHtml().contains(track.name!) ?? false;
|
||||
final containsPrimaryArtist = s.primaryArtists
|
||||
.unescapeHtml()
|
||||
.contains(trackArtistNames?.first ?? "");
|
||||
|
||||
return containsName && containsPrimaryArtist;
|
||||
}
|
||||
|
||||
return sameName && sameArtists;
|
||||
},
|
||||
)
|
||||
.map(toSiblingType)
|
||||
.toList();
|
||||
|
||||
if (weakMatch && matchedResults.isEmpty) {
|
||||
return results.map(toSiblingType).toList();
|
||||
}
|
||||
|
||||
return matchedResults;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -55,7 +55,7 @@ class PipedSourcedTrack extends SourcedTrack {
|
||||
if (cachedSource == null) {
|
||||
final siblings = await fetchSiblings(ref: ref, track: track);
|
||||
if (siblings.isEmpty) {
|
||||
throw TrackNotFoundException(track);
|
||||
throw TrackNotFoundError(track);
|
||||
}
|
||||
|
||||
await SourceMatch.box.put(
|
||||
@ -160,13 +160,16 @@ class PipedSourcedTrack extends SourcedTrack {
|
||||
final query = SourcedTrack.getSearchTerm(track);
|
||||
|
||||
final PipedSearchResult(items: searchResults) = await pipedClient.search(
|
||||
"$query - Topic",
|
||||
query,
|
||||
preference.searchMode == SearchMode.youtube
|
||||
? PipedFilter.video
|
||||
: PipedFilter.musicSongs,
|
||||
);
|
||||
|
||||
final isYouTubeMusic = preference.searchMode == SearchMode.youtubeMusic;
|
||||
// when falling back to piped API make sure to use the YouTube mode
|
||||
final isYouTubeMusic = preference.audioSource != AudioSource.piped
|
||||
? false
|
||||
: preference.searchMode == SearchMode.youtubeMusic;
|
||||
|
||||
if (isYouTubeMusic) {
|
||||
final artists = (track.artists ?? [])
|
||||
|
@ -48,7 +48,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
if (cachedSource == null || cachedSource.sourceType != SourceType.youtube) {
|
||||
final siblings = await fetchSiblings(ref: ref, track: track);
|
||||
if (siblings.isEmpty) {
|
||||
throw TrackNotFoundException(track);
|
||||
throw TrackNotFoundError(track);
|
||||
}
|
||||
|
||||
await SourceMatch.box.put(
|
||||
|
@ -1619,10 +1619,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: piped_client
|
||||
sha256: "8b96e1f9d8533c1da7eff7fbbd4bf188256fc76a20900d378b52be09418ea771"
|
||||
sha256: "87b04b2ebf4e008cfbb0ac85e9920ab3741f5aa697be2dd44919658a3297a4bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
version: "0.1.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -76,7 +76,7 @@ dependencies:
|
||||
path: ^1.8.0
|
||||
path_provider: ^2.0.8
|
||||
permission_handler: ^11.0.1
|
||||
piped_client: ^0.1.0
|
||||
piped_client: ^0.1.1
|
||||
popover: ^0.2.6+3
|
||||
scrobblenaut:
|
||||
git:
|
||||
|
@ -77,6 +77,7 @@
|
||||
],
|
||||
|
||||
"nl": [
|
||||
"audio_source",
|
||||
"start_a_radio",
|
||||
"how_to_start_radio",
|
||||
"replace_queue_question",
|
||||
|
Loading…
Reference in New Issue
Block a user