diff --git a/lib/controller/video_controller.dart b/lib/controller/video_controller.dart index 3ef662a4..27259b5f 100644 --- a/lib/controller/video_controller.dart +++ b/lib/controller/video_controller.dart @@ -370,7 +370,7 @@ class VideoController { }) async { final tr = Player.inst.currentTrack?.track; if (tr == null) return null; - final dv = await fetchVideoFromYoutube(id, stream: stream, mainStreams: mainStreams); + final dv = await fetchVideoFromYoutube(id, stream: stream, mainStreams: mainStreams, canContinue: () => settings.enableVideoPlayback.value); if (!settings.enableVideoPlayback.value) return null; if (_canExecuteForCurrentTrackOnly(tr)) { currentVideo.value = dv; @@ -392,6 +392,7 @@ class VideoController { String? id, { VideoStreamsResult? mainStreams, VideoStream? stream, + required bool Function() canContinue, }) async { _downloadTimerCancel(); if (id == null || id == '') return null; @@ -410,7 +411,7 @@ class VideoController { _downloadTimer = Timer.periodic(const Duration(seconds: 1), (_) => updateCurrentBytes()); VideoStream? streamToUse = stream; - if (stream == null || mainStreams?.hasExpired() != false) { + if (stream == null || (mainStreams?.hasExpired() ?? true)) { // expired null or true mainStreams = await YoutubeInfoController.video.fetchVideoStreams(id); if (mainStreams != null) { @@ -419,7 +420,7 @@ class VideoController { } } - if (streamToUse == null) { + if (streamToUse == null || !canContinue()) { if (_canExecuteForCurrentTrackOnly(initialTrack)) { currentDownloadedBytes.value = null; _downloadTimerCancel(); diff --git a/lib/youtube/controller/info_controllers/yt_video_info_controller.dart b/lib/youtube/controller/info_controllers/yt_video_info_controller.dart index 8c4ef339..97a17821 100644 --- a/lib/youtube/controller/info_controllers/yt_video_info_controller.dart +++ b/lib/youtube/controller/info_controllers/yt_video_info_controller.dart @@ -29,27 +29,31 @@ class _VideoInfoController { return res; } - /// By default, this will force a network request since most implementations use this as fallback to [fetchVideoPageSync]. + YoutiPieVideoPageResult? fetchVideoPageSync(String videoId) { + final res = YoutiPie.cacheBuilder.forVideoPage(videoId: videoId); + return res.read(); + } + + /// By default, this will force a network request since most implementations use this as fallback to [fetchVideoStreamsSync]. Future fetchVideoStreams(String videoId, {bool forceRequest = true}) async { - final res = await YoutiPie.video.fetchVideoStreams( - id: videoId, - details: forceRequest ? ExecuteDetails.forceRequest() : null, - client: _usedClient, - ); + // -- preparing jsplayer *before* fetching streams is important, fetching *after* will return non-working urls. if (_requiresJSPlayer) { // -- await preparing before returning result if (!YoutiPie.cipher.isPrepared) { await ensureJSPlayerInitialized(); } } + final res = await YoutiPie.video.fetchVideoStreams( + id: videoId, + details: forceRequest ? ExecuteDetails.forceRequest() : null, + client: _usedClient, + ); return res; } - YoutiPieVideoPageResult? fetchVideoPageSync(String videoId) { - final res = YoutiPie.cacheBuilder.forVideoPage(videoId: videoId); - return res.read(); - } - + /// Returns cached streams result if exist. you need to ensure that urls didn't expire ![VideoStreamsResult.hasExpired] before consuming them. + /// + /// Enable [bypassJSCheck] if you gurantee using [VideoStreamsResult.info] only. VideoStreamsResult? fetchVideoStreamsSync(String videoId, {bool bypassJSCheck = false}) { final res = YoutiPie.cacheBuilder.forVideoStreams(videoId: videoId); final cached = res.read(); diff --git a/lib/youtube/controller/youtube_controller.dart b/lib/youtube/controller/youtube_controller.dart index 8124a944..33646d71 100644 --- a/lib/youtube/controller/youtube_controller.dart +++ b/lib/youtube/controller/youtube_controller.dart @@ -1197,6 +1197,7 @@ class _YTDownloadManager with PortsProvider { if (url == null || url.host.isEmpty) return false; final filePath = file.path; + if (_downloadCompleters[filePath] != null) return _downloadCompleters[filePath]!.future; _downloadCompleters[filePath]?.completeIfWasnt(false); _downloadCompleters[filePath] = Completer(); @@ -1291,7 +1292,7 @@ class _YTDownloadManager with PortsProvider { fileStream.add(data); progressPort.send(data.length); } - bool? movedSuccessfully; + bool movedSuccessfully = false; if (moveTo != null && moveToRequiredBytes != null) { try { final fileStats = file.statSync(); @@ -1301,11 +1302,11 @@ class _YTDownloadManager with PortsProvider { moveTo, goodBytesIfCopied: (fileLength) => fileLength >= moveToRequiredBytes - allowance, ); - if (movedFile == null) movedSuccessfully = false; + movedSuccessfully = movedFile != null; } } catch (_) {} } - return sendPort.send(MapEntry(filePath, movedSuccessfully ?? true)); + return sendPort.send(MapEntry(filePath, movedSuccessfully)); } catch (_) { // client force closed return sendPort.send(MapEntry(filePath, false)); @@ -1340,7 +1341,7 @@ class _YTDownloadManager with PortsProvider { void _onFileFinish(String path, bool? value) { if (value != null) _downloadCompleters[path]?.completeIfWasnt(value); - _downloadCompleters[path] = null; + _downloadCompleters[path] = null; // important _progressPorts[path]?.close(); _progressPorts[path] = null; } diff --git a/pubspec.yaml b/pubspec.yaml index 3c0f2e47..4e68c2be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: namida description: A Beautiful and Feature-rich Music Player, With YouTube & Video Support Built in Flutter publish_to: "none" -version: 3.7.5-beta+240801001 +version: 3.7.55-beta+240801001 environment: sdk: ">=3.4.0 <4.0.0"