mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55: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(
|
AnimatedCrossFade(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
crossFadeState: preferences.audioSource != AudioSource.youtube
|
crossFadeState: preferences.audioSource == AudioSource.youtube
|
||||||
? CrossFadeState.showFirst
|
? CrossFadeState.showFirst
|
||||||
: CrossFadeState.showSecond,
|
: CrossFadeState.showSecond,
|
||||||
firstChild: const SizedBox.shrink(),
|
firstChild: const SizedBox.shrink(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
@ -103,16 +104,19 @@ class AudioPlayerStreamListeners {
|
|||||||
StreamSubscription subscribeToPosition() {
|
StreamSubscription subscribeToPosition() {
|
||||||
String lastTrack = ""; // used to prevent multiple calls to the same track
|
String lastTrack = ""; // used to prevent multiple calls to the same track
|
||||||
return audioPlayer.positionStream.listen((event) async {
|
return audioPlayer.positionStream.listen((event) async {
|
||||||
|
final percentProgress =
|
||||||
|
(event.inSeconds / max(audioPlayer.duration.inSeconds, 1)) * 100;
|
||||||
try {
|
try {
|
||||||
if (event < const Duration(seconds: 3) ||
|
if (percentProgress < 80 ||
|
||||||
audioPlayerState.playlist.index == -1 ||
|
audioPlayerState.playlist.index == -1 ||
|
||||||
audioPlayerState.playlist.index ==
|
audioPlayerState.playlist.index ==
|
||||||
audioPlayerState.tracks.length - 1) {
|
audioPlayerState.tracks.length - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final nextTrack = SpotubeMedia.fromMedia(audioPlayerState
|
final nextTrack = SpotubeMedia.fromMedia(
|
||||||
.playlist.medias
|
audioPlayerState.playlist.medias
|
||||||
.elementAt(audioPlayerState.playlist.index + 1));
|
.elementAt(audioPlayerState.playlist.index + 1),
|
||||||
|
);
|
||||||
|
|
||||||
if (lastTrack == nextTrack.track.id || nextTrack.track is LocalTrack) {
|
if (lastTrack == nextTrack.track.id || nextTrack.track is LocalTrack) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:dio/dio.dart' hide Response;
|
import 'package:dio/dio.dart' hide Response;
|
||||||
import 'package:dio/dio.dart' as dio_lib;
|
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/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/extensions/track.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/models/parser/range_headers.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/audio_player/state.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/enums.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
import 'package:spotube/utils/service_utils.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 {
|
class ServerPlaybackRoutes {
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
@ -47,9 +63,8 @@ class ServerPlaybackRoutes {
|
|||||||
var options = Options(
|
var options = Options(
|
||||||
headers: {
|
headers: {
|
||||||
...headers,
|
...headers,
|
||||||
"User-Agent":
|
"user-agent": _randomUserAgent,
|
||||||
"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=3600",
|
||||||
"Cache-Control": "max-age=0",
|
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"host": Uri.parse(track.url).host,
|
"host": Uri.parse(track.url).host,
|
||||||
},
|
},
|
||||||
@ -100,18 +115,35 @@ class ServerPlaybackRoutes {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final res =
|
final res = await dio
|
||||||
await dio.get<Uint8List>(track.url, options: options).catchError(
|
.get<Uint8List>(
|
||||||
(e, stack) async {
|
track.url,
|
||||||
final sourcedTrack = await ref
|
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)
|
.read(sourcedTrackProvider(SpotubeMedia(track)).notifier)
|
||||||
.switchToAlternativeSources();
|
.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;
|
final bytes = res.data;
|
||||||
|
|
||||||
|
@ -29,6 +29,19 @@ class SourcedTrackNotifier
|
|||||||
return sourcedTrack;
|
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 {
|
Future<SourcedTrack?> switchToAlternativeSources() async {
|
||||||
if (arg == null) {
|
if (arg == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -38,7 +38,7 @@ class AppLogger {
|
|||||||
if (!kDebugMode) return;
|
if (!kDebugMode) return;
|
||||||
logging.hierarchicalLoggingEnabled = true;
|
logging.hierarchicalLoggingEnabled = true;
|
||||||
logging.Logger('YoutubeExplode.StreamsClient')
|
logging.Logger('YoutubeExplode.StreamsClient')
|
||||||
..level = logging.Level.ALL
|
..level = logging.Level.SEVERE
|
||||||
..onRecord.listen(
|
..onRecord.listen(
|
||||||
(record) {
|
(record) {
|
||||||
log.log(
|
log.log(
|
||||||
|
@ -50,7 +50,6 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
ytClients: [
|
ytClients: [
|
||||||
YoutubeApiClient.android,
|
YoutubeApiClient.android,
|
||||||
YoutubeApiClient.mweb,
|
YoutubeApiClient.mweb,
|
||||||
YoutubeApiClient.safari,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -59,6 +58,23 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
required Track track,
|
required Track track,
|
||||||
required Ref ref,
|
required Ref ref,
|
||||||
}) async {
|
}) 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 database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
@ -94,7 +110,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
}
|
}
|
||||||
final item = await youtubeClient.videos.get(cachedSource.sourceId);
|
final item = await youtubeClient.videos.get(cachedSource.sourceId);
|
||||||
final manifest = await _getStreamManifest(cachedSource.sourceId);
|
final manifest = await _getStreamManifest(cachedSource.sourceId);
|
||||||
return YoutubeSourcedTrack(
|
final sourcedTrack = YoutubeSourcedTrack(
|
||||||
ref: ref,
|
ref: ref,
|
||||||
siblings: [],
|
siblings: [],
|
||||||
source: toSourceMap(manifest),
|
source: toSourceMap(manifest),
|
||||||
@ -110,6 +126,10 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
),
|
),
|
||||||
track: track,
|
track: track,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
AppLogger.log.i("${track.name}: ${sourcedTrack.url}");
|
||||||
|
|
||||||
|
return sourcedTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SourceMap toSourceMap(StreamManifest manifest) {
|
static SourceMap toSourceMap(StreamManifest manifest) {
|
||||||
|
Loading…
Reference in New Issue
Block a user