mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
fix: youtube tracks keeps skipping despite being matched correctly
This commit is contained in:
parent
043eaba81f
commit
698fb6ba27
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user