fix: youtube tracks keeps skipping despite being matched correctly

This commit is contained in:
Kingkor Roy Tirtho 2025-02-05 00:36:23 +06:00
parent 043eaba81f
commit 698fb6ba27
6 changed files with 90 additions and 21 deletions

View File

@ -197,7 +197,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
),
AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
crossFadeState: preferences.audioSource != AudioSource.youtube
crossFadeState: preferences.audioSource == AudioSource.youtube
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: const SizedBox.shrink(),

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/local_track.dart';
@ -103,16 +104,19 @@ class AudioPlayerStreamListeners {
StreamSubscription subscribeToPosition() {
String lastTrack = ""; // used to prevent multiple calls to the same track
return audioPlayer.positionStream.listen((event) async {
final percentProgress =
(event.inSeconds / max(audioPlayer.duration.inSeconds, 1)) * 100;
try {
if (event < const Duration(seconds: 3) ||
if (percentProgress < 80 ||
audioPlayerState.playlist.index == -1 ||
audioPlayerState.playlist.index ==
audioPlayerState.tracks.length - 1) {
return;
}
final nextTrack = SpotubeMedia.fromMedia(audioPlayerState
.playlist.medias
.elementAt(audioPlayerState.playlist.index + 1));
final nextTrack = SpotubeMedia.fromMedia(
audioPlayerState.playlist.medias
.elementAt(audioPlayerState.playlist.index + 1),
);
if (lastTrack == nextTrack.track.id || nextTrack.track is LocalTrack) {
return;

View File

@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:math';
import 'package:dio/dio.dart' hide Response;
import 'package:dio/dio.dart' as dio_lib;
@ -10,6 +11,7 @@ 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';
@ -22,6 +24,20 @@ import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
const _deviceClients = {
YoutubeApiClient.android,
YoutubeApiClient.ios,
YoutubeApiClient.mweb,
YoutubeApiClient.safari,
};
String? get _randomUserAgent => _deviceClients
.elementAt(
Random().nextInt(_deviceClients.length),
)
.payload["context"]["client"]["userAgent"];
class ServerPlaybackRoutes {
final Ref ref;
@ -47,9 +63,8 @@ class ServerPlaybackRoutes {
var options = Options(
headers: {
...headers,
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
"Cache-Control": "max-age=0",
"user-agent": _randomUserAgent,
"Cache-Control": "max-age=3600",
"Connection": "keep-alive",
"host": Uri.parse(track.url).host,
},
@ -100,18 +115,35 @@ class ServerPlaybackRoutes {
);
}
final res =
await dio.get<Uint8List>(track.url, options: options).catchError(
(e, stack) async {
final sourcedTrack = await ref
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
.switchToAlternativeSources();
final res = await dio
.get<Uint8List>(
track.url,
options: options.copyWith(headers: {
...?options.headers,
"user-agent": _randomUserAgent,
}),
)
.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();
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
return await dio.get<Uint8List>(sourcedTrack!.url, options: options);
},
);
return await dio.get<Uint8List>(
sourcedTrack!.url,
options: options.copyWith(headers: {
...?options.headers,
"user-agent": _randomUserAgent,
}),
);
});
final bytes = res.data;

View File

@ -29,6 +29,19 @@ class SourcedTrackNotifier
return sourcedTrack;
}
Future<SourcedTrack?> refreshStreamingUrl() async {
if (arg == null) {
return null;
}
return await update((prev) async {
return await SourcedTrack.fetchFromTrack(
track: state.value!,
ref: ref,
);
});
}
Future<SourcedTrack?> switchToAlternativeSources() async {
if (arg == null) {
return null;

View File

@ -38,7 +38,7 @@ class AppLogger {
if (!kDebugMode) return;
logging.hierarchicalLoggingEnabled = true;
logging.Logger('YoutubeExplode.StreamsClient')
..level = logging.Level.ALL
..level = logging.Level.SEVERE
..onRecord.listen(
(record) {
log.log(

View File

@ -50,7 +50,6 @@ class YoutubeSourcedTrack extends SourcedTrack {
ytClients: [
YoutubeApiClient.android,
YoutubeApiClient.mweb,
YoutubeApiClient.safari,
],
);
}
@ -59,6 +58,23 @@ class YoutubeSourcedTrack extends SourcedTrack {
required Track track,
required Ref ref,
}) async {
// Indicates the track is requesting a stream refresh
if (track is YoutubeSourcedTrack) {
final manifest = await _getStreamManifest(track.sourceInfo.id);
final sourcedTrack = YoutubeSourcedTrack(
ref: ref,
siblings: track.siblings,
source: toSourceMap(manifest),
sourceInfo: track.sourceInfo,
track: track,
);
AppLogger.log.i("Refreshing ${track.name}: ${sourcedTrack.url}");
return sourcedTrack;
}
final database = ref.read(databaseProvider);
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
@ -94,7 +110,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
}
final item = await youtubeClient.videos.get(cachedSource.sourceId);
final manifest = await _getStreamManifest(cachedSource.sourceId);
return YoutubeSourcedTrack(
final sourcedTrack = YoutubeSourcedTrack(
ref: ref,
siblings: [],
source: toSourceMap(manifest),
@ -110,6 +126,10 @@ class YoutubeSourcedTrack extends SourcedTrack {
),
track: track,
);
AppLogger.log.i("${track.name}: ${sourcedTrack.url}");
return sourcedTrack;
}
static SourceMap toSourceMap(StreamManifest manifest) {