From c010e73097a8b0cda8d05bc7d04bd0a7f7fb9224 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Tue, 15 Jul 2025 00:44:06 +1000 Subject: [PATCH] Fix playback not working for offline content --- app/(auth)/player/direct-player.tsx | 2 +- modules/vlc-player/ios/VlcPlayerView.swift | 13 +++++++------ utils/jellyfin/media/getStreamUrl.ts | 12 +++++------- utils/profiles/native.js | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index 5b1e18ad..7e527d9c 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -550,7 +550,7 @@ export default function page() { source={{ uri: stream?.url || "", autoplay: true, - isNetwork: true, + isNetwork: !offline, startPosition, externalSubtitles, initOptions, diff --git a/modules/vlc-player/ios/VlcPlayerView.swift b/modules/vlc-player/ios/VlcPlayerView.swift index b75bc5bb..34f1d272 100644 --- a/modules/vlc-player/ios/VlcPlayerView.swift +++ b/modules/vlc-player/ios/VlcPlayerView.swift @@ -143,7 +143,7 @@ extension VLCPlayerWrapper: VLCMediaPlayerDelegate { guard let vlcPlayerView = self.playerView.superview as? VlcPlayerView, !vlcPlayerView.initialSeekPerformed, vlcPlayerView.startPosition > 0, - vlcPlayerView.isTranscoding, + vlcPlayerView.shouldPerformInitialSeek, player.isSeekable else { return } vlcPlayerView.initialSeekPerformed = true // Use a logger from the VlcPlayerView if available, or create a new one @@ -167,7 +167,9 @@ class VlcPlayerView: ExpoView { private var externalSubtitles: [[String: String]]? var hasSource = false var initialSeekPerformed = false - var isTranscoding: Bool = false + // A flag variable determinging if we should perform the initial seek. Its either transcoding or offline playback. that makes + var shouldPerformInitialSeek: Bool = false + // MARK: - Initialization required init(appContext: AppContext? = nil) { @@ -265,12 +267,11 @@ class VlcPlayerView: ExpoView { return } - if uri.contains("m3u8") { - self.isTranscoding = true - } let autoplay = source["autoplay"] as? Bool ?? false let isNetwork = source["isNetwork"] as? Bool ?? false + // Set shouldPeformIntial based on isTranscoding and is not a network stream + self.shouldPerformInitialSeek = uri.contains("m3u8") || !isNetwork self.onVideoLoadStart?(["target": self.reactTag ?? NSNull()]) let media: VLCMedia! @@ -295,7 +296,7 @@ class VlcPlayerView: ExpoView { if autoplay { logger.info("Playing...") // The Video is not transcoding so it its safe to seek to the start position. - if !self.isTranscoding { + if !self.shouldPerformInitialSeek { self.vlc.player.time = VLCTime(number: NSNumber(value: self.startPosition * 1000)) } self.play() diff --git a/utils/jellyfin/media/getStreamUrl.ts b/utils/jellyfin/media/getStreamUrl.ts index 6960c388..fbabf2a4 100644 --- a/utils/jellyfin/media/getStreamUrl.ts +++ b/utils/jellyfin/media/getStreamUrl.ts @@ -1,12 +1,10 @@ -import generateDeviceProfile from "@/utils/profiles/native"; import type { Api } from "@jellyfin/sdk"; import type { BaseItemDto, MediaSourceInfo, - PlaybackInfoResponse, } from "@jellyfin/sdk/lib/generated-client/models"; import { getMediaInfoApi } from "@jellyfin/sdk/lib/utils/api"; -import { Alert } from "react-native"; +import generateDeviceProfile from "@/utils/profiles/native"; export const getStreamUrl = async ({ api, @@ -33,7 +31,7 @@ export const getStreamUrl = async ({ subtitleStreamIndex?: number; height?: number; mediaSourceId?: string | null; - download?: bool; + download?: boolean; deviceId?: string | null; }): Promise<{ url: string | null; @@ -73,8 +71,8 @@ export const getStreamUrl = async ({ } sessionId = res.data.PlaySessionId || null; - mediaSource = res.data.MediaSources[0]; - let transcodeUrl = mediaSource.TranscodingUrl; + mediaSource = res.data.MediaSources?.[0]; + let transcodeUrl = mediaSource?.TranscodingUrl; if (transcodeUrl) { if (download) { @@ -125,7 +123,7 @@ export const getStreamUrl = async ({ return { url: directPlayUrl, - sessionId: sessionId || playSessionId, + sessionId: sessionId || playSessionId || null, mediaSource, }; }; diff --git a/utils/profiles/native.js b/utils/profiles/native.js index 7a40dd8a..670d9f89 100644 --- a/utils/profiles/native.js +++ b/utils/profiles/native.js @@ -62,7 +62,7 @@ export const generateDeviceProfile = async () => { DirectPlayProfiles: [ { Type: MediaTypes.Video, - Container: "mp4,mkv,avi,mov,flv,ts,m2ts,webm,ogv,3gp,hls", + Container: "mkv,avi,mov,flv,ts,m2ts,webm,ogv,3gp,hls", VideoCodec: "h264,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1,avi,mpeg,mpeg2video", AudioCodec: "aac,ac3,eac3,mp3,flac,alac,opus,vorbis,wma,dts",