mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: always fetching SponsorBlock if no segments found & download failing
This commit is contained in:
parent
33c7b64f06
commit
6ced0a0fad
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
|
||||
class ConfirmDownloadDialog extends StatelessWidget {
|
||||
@ -24,8 +25,9 @@ class ConfirmDownloadDialog extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
content: Padding(
|
||||
content: Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
constraints: BoxConstraints(maxWidth: Breakpoints.sm),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -87,7 +89,7 @@ class BulletPoint extends StatelessWidget {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("●"),
|
||||
const Text("\u2022"),
|
||||
const SizedBox(width: 5),
|
||||
Flexible(child: Text(text)),
|
||||
],
|
||||
|
@ -20,6 +20,7 @@ import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/download_manager_provider.dart';
|
||||
import 'package:spotube/provider/blacklist_provider.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||
import 'package:spotube/utils/service_utils.dart';
|
||||
|
||||
final trackCollectionSortState =
|
||||
@ -55,7 +56,9 @@ class TracksTableView extends HookConsumerWidget {
|
||||
final playback = ref.watch(ProxyPlaylistNotifier.notifier);
|
||||
ref.watch(downloadManagerProvider);
|
||||
final downloader = ref.watch(downloadManagerProvider.notifier);
|
||||
TextStyle tableHeadStyle =
|
||||
final apiType =
|
||||
ref.watch(userPreferencesProvider.select((s) => s.youtubeApiType));
|
||||
final tableHeadStyle =
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
|
||||
|
||||
final selected = useState<List<String>>([]);
|
||||
@ -188,7 +191,8 @@ class TracksTableView extends HookConsumerWidget {
|
||||
switch (action) {
|
||||
case "download":
|
||||
{
|
||||
final confirmed = await showDialog(
|
||||
final confirmed = apiType == YoutubeApiType.piped ||
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const ConfirmDownloadDialog();
|
||||
|
@ -14,6 +14,12 @@ final officialMusicRegex = RegExp(
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
class TrackNotFoundException implements Exception {
|
||||
factory TrackNotFoundException(Track track) {
|
||||
throw Exception("Failed to find any results for ${track.name}");
|
||||
}
|
||||
}
|
||||
|
||||
class SpotubeTrack extends Track {
|
||||
final YoutubeVideoInfo ytTrack;
|
||||
final String ytUri;
|
||||
@ -157,7 +163,7 @@ class SpotubeTrack extends Track {
|
||||
} else {
|
||||
siblings = await fetchSiblings(track, client);
|
||||
if (siblings.isEmpty) {
|
||||
throw Exception("Failed to find any results for ${track.name}");
|
||||
throw TrackNotFoundException(track);
|
||||
}
|
||||
(ytVideo, ytStreamUrl) =
|
||||
await client.video(siblings.first.id, siblings.first.searchMode);
|
||||
|
@ -85,7 +85,7 @@ class DownloadManagerProvider extends ChangeNotifier {
|
||||
|
||||
final Ref<DownloadManagerProvider> ref;
|
||||
|
||||
YoutubeEndpoints get yt => ref.read(downloadYoutubeProvider);
|
||||
YoutubeEndpoints get yt => ref.read(youtubeProvider);
|
||||
String get downloadDirectory =>
|
||||
ref.read(userPreferencesProvider.select((s) => s.downloadLocation));
|
||||
|
||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:catcher/catcher.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:palette_generator/palette_generator.dart';
|
||||
@ -68,7 +69,12 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
||||
() async {
|
||||
notificationService = await AudioServices.create(ref, this);
|
||||
|
||||
({String source, List<SkipSegment> segments})? currentSegments;
|
||||
// listeners state
|
||||
final currentSegments =
|
||||
// using source as unique id because alternative track source support
|
||||
ObjectRef<({String source, List<SkipSegment> segments})?>(null);
|
||||
final isPreSearching = ObjectRef(false);
|
||||
final isFetchingSegments = ObjectRef(false);
|
||||
|
||||
audioPlayer.activeSourceChangedStream.listen((newActiveSource) async {
|
||||
try {
|
||||
@ -112,16 +118,14 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
||||
}
|
||||
});
|
||||
|
||||
bool isPreSearching = false;
|
||||
|
||||
listenTo2Percent(int percent) async {
|
||||
if (isPreSearching ||
|
||||
if (isPreSearching.value ||
|
||||
audioPlayer.currentSource == null ||
|
||||
audioPlayer.nextSource == null ||
|
||||
isPlayable(audioPlayer.nextSource!)) return;
|
||||
|
||||
try {
|
||||
isPreSearching = true;
|
||||
isPreSearching.value = true;
|
||||
|
||||
final oldTrack =
|
||||
mapSourcesToTracks([audioPlayer.nextSource!]).firstOrNull;
|
||||
@ -138,51 +142,64 @@ 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) {
|
||||
final oldTrack =
|
||||
mapSourcesToTracks([audioPlayer.nextSource!]).firstOrNull;
|
||||
await removeTrack(oldTrack!.id!);
|
||||
}
|
||||
Catcher.reportCheckedError(e, stackTrace);
|
||||
} finally {
|
||||
isPreSearching = false;
|
||||
isPreSearching.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
audioPlayer.percentCompletedStream(2).listen(listenTo2Percent);
|
||||
|
||||
bool isFetchingSegments = false;
|
||||
|
||||
audioPlayer.positionStream.listen((position) async {
|
||||
try {
|
||||
if (state.activeTrack == null || state.activeTrack is LocalTrack) {
|
||||
isFetchingSegments = false;
|
||||
isFetchingSegments.value = false;
|
||||
return;
|
||||
}
|
||||
// skipping in very first second breaks stream
|
||||
if ((preferences.youtubeApiType == YoutubeApiType.piped &&
|
||||
preferences.searchMode == SearchMode.youtubeMusic) ||
|
||||
!preferences.skipNonMusic) return;
|
||||
|
||||
final notSameSegmentId =
|
||||
currentSegments?.source != audioPlayer.currentSource;
|
||||
|
||||
if (currentSegments == null ||
|
||||
(notSameSegmentId && !isFetchingSegments)) {
|
||||
isFetchingSegments = true;
|
||||
try {
|
||||
currentSegments = (
|
||||
final isYTMusicMode =
|
||||
preferences.youtubeApiType == YoutubeApiType.piped &&
|
||||
preferences.searchMode == SearchMode.youtubeMusic;
|
||||
|
||||
if (isYTMusicMode || !preferences.skipNonMusic) return;
|
||||
|
||||
final isNotSameSegmentId =
|
||||
currentSegments.value?.source != audioPlayer.currentSource;
|
||||
|
||||
if (currentSegments.value == null ||
|
||||
(isNotSameSegmentId && !isFetchingSegments.value)) {
|
||||
isFetchingSegments.value = true;
|
||||
try {
|
||||
currentSegments.value = (
|
||||
source: audioPlayer.currentSource!,
|
||||
segments: await getAndCacheSkipSegments(
|
||||
(state.activeTrack as SpotubeTrack).ytTrack.id,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
currentSegments.value = (
|
||||
source: audioPlayer.currentSource!,
|
||||
segments: [],
|
||||
);
|
||||
} finally {
|
||||
isFetchingSegments = false;
|
||||
isFetchingSegments.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
final (source: _, :segments) = currentSegments!;
|
||||
final (source: _, :segments) = currentSegments.value!;
|
||||
|
||||
// skipping in first 2 second breaks stream
|
||||
if (segments.isEmpty || position < const Duration(seconds: 3)) return;
|
||||
|
||||
for (final segment in segments) {
|
||||
if ((position.inSeconds >= segment.start &&
|
||||
position.inSeconds < segment.end)) {
|
||||
if (position.inSeconds >= segment.start &&
|
||||
position.inSeconds < segment.end) {
|
||||
await audioPlayer.seek(Duration(seconds: segment.end));
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,3 @@ final youtubeProvider = Provider<YoutubeEndpoints>((ref) {
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
return YoutubeEndpoints(preferences);
|
||||
});
|
||||
|
||||
// this provider overrides the API provider to use piped.video for downloading
|
||||
final downloadYoutubeProvider = Provider<YoutubeEndpoints>((ref) {
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
return YoutubeEndpoints(
|
||||
preferences.copyWith(
|
||||
youtubeApiType: YoutubeApiType.piped,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -1,8 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:smtc_windows/smtc_windows.dart'
|
||||
if (dart.library.html) 'package:spotube/services/audio_services/smtc_windows_web.dart';
|
||||
import 'package:smtc_windows/smtc_windows.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
|
Loading…
Reference in New Issue
Block a user