diff --git a/src/components/jellyfinActions.ts b/src/components/jellyfinActions.ts index 85f20bb4..b886df44 100644 --- a/src/components/jellyfinActions.ts +++ b/src/components/jellyfinActions.ts @@ -232,9 +232,9 @@ export async function getPlaybackInfo( maxBitrate: number, deviceProfile: DeviceProfile, startPosition: number, - mediaSourceId: string, - audioStreamIndex: number, - subtitleStreamIndex: number, + mediaSourceId: string | null, + audioStreamIndex: number | null, + subtitleStreamIndex: number | null, liveStreamId: string | null = null // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index c95df311..4046740f 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -28,7 +28,7 @@ import { JellyfinApi } from './jellyfinApi'; import { PlaybackManager, PlaybackState } from './playbackManager'; import { CommandHandler } from './commandHandler'; import { getMaxBitrateSupport } from './codecSupportHelper'; -import { PlayRequest } from '~/types/global'; +import { PlayRequest, StreamInfo } from '~/types/global'; window.castReceiverContext = cast.framework.CastReceiverContext.getInstance(); window.playerManager = window.castReceiverContext.getPlayerManager(); @@ -808,7 +808,6 @@ export function setTextTrack(index: number | null): void { } } -// TODO no any types /** * createMediaInformation * @param playSessionId - playSessionId @@ -819,8 +818,7 @@ export function setTextTrack(index: number | null): void { export function createMediaInformation( playSessionId: string, item: BaseItemDto, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - streamInfo: any + streamInfo: StreamInfo ): framework.messages.MediaInformation { const mediaInfo = new cast.framework.messages.MediaInformation(); @@ -831,12 +829,12 @@ export function createMediaInformation( canClientSeek: streamInfo.canClientSeek, canSeek: streamInfo.canSeek, itemId: item.Id, - liveStreamId: streamInfo.mediaSource.LiveStreamId, - mediaSourceId: streamInfo.mediaSource.Id, + liveStreamId: streamInfo.mediaSource?.LiveStreamId ?? null, + mediaSourceId: streamInfo.mediaSource?.Id ?? null, playMethod: streamInfo.isStatic ? 'DirectStream' : 'Transcode', playSessionId: playSessionId, - runtimeTicks: streamInfo.mediaSource.RunTimeTicks, - startPositionTicks: streamInfo.startPositionTicks || 0, + runtimeTicks: streamInfo.mediaSource?.RunTimeTicks ?? null, + startPositionTicks: streamInfo.startPositionTicks ?? 0, subtitleStreamIndex: streamInfo.subtitleStreamIndex }; @@ -845,7 +843,7 @@ export function createMediaInformation( mediaInfo.streamType = cast.framework.messages.StreamType.BUFFERED; mediaInfo.tracks = streamInfo.tracks; - if (streamInfo.mediaSource.RunTimeTicks) { + if (streamInfo.mediaSource?.RunTimeTicks) { mediaInfo.duration = Math.floor( ticksToSeconds(streamInfo.mediaSource.RunTimeTicks) ); diff --git a/src/components/playbackManager.ts b/src/components/playbackManager.ts index 0337d209..8acb9348 100644 --- a/src/components/playbackManager.ts +++ b/src/components/playbackManager.ts @@ -4,6 +4,7 @@ import type { PlayMethod } from '@jellyfin/sdk/lib/generated-client'; import { RepeatMode } from '@jellyfin/sdk/lib/generated-client'; +import { MediaInformationCustomData } from 'chromecast-caf-receiver/cast.framework.messages'; import { AppStatus } from '../types/appStatus'; import { broadcastConnectionErrorMessage, @@ -33,7 +34,7 @@ export interface PlaybackState { mediaType: string | null | undefined; itemId: string; - audioStreamIndex: null; + audioStreamIndex: number | null; subtitleStreamIndex: number | null; mediaSource: MediaSourceInfo | null; mediaSourceId: string; @@ -199,7 +200,7 @@ export abstract class PlaybackManager { // Would set private, but some refactorings need to happen first. static async playItemInternal( item: BaseItemDto, - options: any // eslint-disable-line @typescript-eslint/no-explicit-any + options: MediaInformationCustomData ): Promise { DocumentManager.setAppStatus(AppStatus.Loading); diff --git a/src/helpers.ts b/src/helpers.ts index ba7712a2..00f34d0f 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -22,7 +22,7 @@ import { } from '@jellyfin/sdk/lib/utils/api'; import { JellyfinApi } from './components/jellyfinApi'; import { PlaybackManager, PlaybackState } from './components/playbackManager'; -import { BusMessage } from './types/global'; +import { BusMessage, StreamInfo } from './types/global'; type InstantMixApiRequest = | InstantMixApiGetInstantMixFromAlbumRequest @@ -309,8 +309,7 @@ export function createStreamInfo( item: BaseItemDto, mediaSource: MediaSourceInfo, startPosition: number | null - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { +): StreamInfo { let mediaUrl; let contentType; @@ -332,7 +331,7 @@ export function createStreamInfo( if (type == 'video') { contentType = `video/${mediaSource.Container}`; - if (mediaSource.SupportsDirectPlay) { + if (mediaSource.SupportsDirectPlay && mediaSource.Path) { mediaUrl = mediaSource.Path; isStatic = true; } else if (mediaSource.SupportsDirectStream) { @@ -363,7 +362,7 @@ export function createStreamInfo( } else { contentType = `audio/${mediaSource.Container}`; - if (mediaSource.SupportsDirectPlay) { + if (mediaSource.SupportsDirectPlay && mediaSource.Path) { mediaUrl = mediaSource.Path; isStatic = true; playerStartPositionTicks = startPosition ?? 0; @@ -394,9 +393,8 @@ export function createStreamInfo( // It is a pain and will require unbinding all event handlers during the operation const canSeek = (mediaSource.RunTimeTicks ?? 0) > 0; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const info: any = { - audioStreamIndex: mediaSource.DefaultAudioStreamIndex, + const info: StreamInfo = { + audioStreamIndex: mediaSource.DefaultAudioStreamIndex ?? null, canClientSeek: isStatic || (canSeek && streamContainer == 'm3u8'), canSeek: canSeek, contentType: contentType, @@ -405,7 +403,7 @@ export function createStreamInfo( playerStartPositionTicks: playerStartPositionTicks, startPositionTicks: startPosition, streamContainer: streamContainer, - subtitleStreamIndex: mediaSource.DefaultSubtitleStreamIndex, + subtitleStreamIndex: mediaSource.DefaultSubtitleStreamIndex ?? null, url: mediaUrl }; @@ -431,6 +429,10 @@ export function createStreamInfo( ? subtitleStream.DeliveryUrl : JellyfinApi.createUrl(subtitleStream.DeliveryUrl); + if (!info.subtitleStreamIndex) { + return; + } + const track = new cast.framework.messages.Track( info.subtitleStreamIndex, cast.framework.messages.TrackType.TEXT diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 6f4bf2b5..34c25518 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -5,9 +5,13 @@ import { import { SystemVolumeData } from 'chromecast-caf-receiver/cast.framework.system'; import type { BaseItemDto, + MediaSourceInfo, RepeatMode } from '@jellyfin/sdk/lib/generated-client'; -import { TextTrackEdgeType } from 'chromecast-caf-receiver/cast.framework.messages'; +import type { + TextTrackEdgeType, + Track +} from 'chromecast-caf-receiver/cast.framework.messages'; // Messagebus message export interface BusMessage { @@ -70,6 +74,22 @@ interface SubtitleAppearance { textSize: 'smaller' | 'small' | 'large' | 'larger' | 'extralarge'; } +interface StreamInfo { + tracks?: Track[]; + audioStreamIndex: number | null; + canClientSeek: boolean; + canSeek: boolean; + contentType: string; + isStatic: boolean; + mediaSource?: MediaSourceInfo; + playerStartPositionTicks?: number; + startPositionTicks: number | null; + streamContainer?: string | null; + subtitleStreamIndex: number | null; + subtitleStreamUrl?: string; + url: string; +} + declare global { export interface Window { mediaElement: HTMLElement | null; @@ -86,16 +106,16 @@ declare global { declare module 'chromecast-caf-receiver/cast.framework.messages' { interface MediaInformationCustomData { - audioStreamIndex: string; + audioStreamIndex: number | null; canClientSeek: boolean; canSeek: boolean; itemId: string | undefined; - liveStreamId: number; - mediaSourceId: number; + liveStreamId: string | null; + mediaSourceId: string | null; playMethod: 'DirectStream' | 'Transcode'; playSessionId: string; - runtimeTicks: number; + runtimeTicks: number | null; startPositionTicks: number; - subtitleStreamIndex: number; + subtitleStreamIndex: number | null; } }