Skip to content

Commit

Permalink
fix: always fetching SponsorBlock if no segments found & download fai…
Browse files Browse the repository at this point in the history
…ling
  • Loading branch information
KRTirtho committed Aug 25, 2023
1 parent 33c7b64 commit 6ced0a0
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 49 deletions.
6 changes: 4 additions & 2 deletions lib/components/shared/dialogs/confirm_download_dialog.dart
@@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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)),
],
Expand Down
18 changes: 11 additions & 7 deletions lib/components/shared/track_table/tracks_table_view.dart
Expand Up @@ -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 =
Expand Down Expand Up @@ -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>>([]);
Expand Down Expand Up @@ -188,12 +191,13 @@ class TracksTableView extends HookConsumerWidget {
switch (action) {
case "download":
{
final confirmed = await showDialog(
context: context,
builder: (context) {
return const ConfirmDownloadDialog();
},
);
final confirmed = apiType == YoutubeApiType.piped ||
await showDialog(
context: context,
builder: (context) {
return const ConfirmDownloadDialog();
},
);
if (confirmed != true) return;
await downloader
.batchAddToQueue(selectedTracks.toList());
Expand Down
8 changes: 7 additions & 1 deletion lib/models/spotube_track.dart
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion lib/provider/download_manager_provider.dart
Expand Up @@ -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));

Expand Down
69 changes: 43 additions & 26 deletions lib/provider/proxy_playlist/proxy_playlist_provider.dart
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
if (state.activeTrack == null || state.activeTrack is LocalTrack) {
isFetchingSegments.value = false;
return;
}
try {
if (state.activeTrack == null || state.activeTrack is LocalTrack) {
isFetchingSegments = false;
return;
}
// skipping in very first second breaks stream
if ((preferences.youtubeApiType == YoutubeApiType.piped &&
preferences.searchMode == SearchMode.youtubeMusic) ||
!preferences.skipNonMusic) return;
final isYTMusicMode =
preferences.youtubeApiType == YoutubeApiType.piped &&
preferences.searchMode == SearchMode.youtubeMusic;

if (isYTMusicMode || !preferences.skipNonMusic) return;

final notSameSegmentId =
currentSegments?.source != audioPlayer.currentSource;
final isNotSameSegmentId =
currentSegments.value?.source != audioPlayer.currentSource;

if (currentSegments == null ||
(notSameSegmentId && !isFetchingSegments)) {
isFetchingSegments = true;
if (currentSegments.value == null ||
(isNotSameSegmentId && !isFetchingSegments.value)) {
isFetchingSegments.value = true;
try {
currentSegments = (
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));
}
}
Expand Down
10 changes: 0 additions & 10 deletions lib/provider/youtube_provider.dart
Expand Up @@ -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,
),
);
});
3 changes: 1 addition & 2 deletions lib/services/audio_services/windows_audio_service.dart
@@ -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';
Expand Down

0 comments on commit 6ced0a0

Please sign in to comment.