From 2f601762554d81dfe1cff1c147fe283677da64a3 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:41:10 +1100 Subject: [PATCH 1/6] Enable tseslint stylistic rule set --- eslint.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 27bfc245..787e4aa0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,7 @@ export default [ jsdoc.configs['flat/recommended'], eslintPluginPrettierRecommended, ...tseslint.configs.strict, + ...tseslint.configs.stylistic, promise.configs['flat/recommended'], importPlugin.flatConfigs.errors, importPlugin.flatConfigs.warnings, From 7efb7ac1c4d7248d7291f136d7923779413ab561 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:41:30 +1100 Subject: [PATCH 2/6] Autofix tseslint stylistic issues --- src/components/__tests__/jellyfinApi.test.ts | 2 +- src/components/codecSupportHelper.ts | 14 +++++------ src/components/commandHandler.ts | 26 ++++++++++---------- src/components/credentialManager.ts | 4 +-- src/components/deviceprofileBuilder.ts | 18 +++++++------- src/components/documentManager.ts | 14 +++++------ src/components/maincontroller.ts | 8 +++--- src/components/playbackManager.ts | 2 +- src/helpers.ts | 10 ++++---- src/types/global.d.ts | 8 ++---- 10 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/components/__tests__/jellyfinApi.test.ts b/src/components/__tests__/jellyfinApi.test.ts index fc905c65..e1390249 100644 --- a/src/components/__tests__/jellyfinApi.test.ts +++ b/src/components/__tests__/jellyfinApi.test.ts @@ -3,7 +3,7 @@ import { JellyfinApi } from '../jellyfinApi'; import { version } from '../../../package.json'; const setupMockCastSenders = (): void => { - const getSenders = (): Array => [{ id: 'thisIsSenderId' }]; // eslint-disable-line @typescript-eslint/no-explicit-any + const getSenders = (): any[] => [{ id: 'thisIsSenderId' }]; // eslint-disable-line @typescript-eslint/no-explicit-any const getInstance = (): any => ({ getSenders }); // eslint-disable-line @typescript-eslint/no-explicit-any // @ts-expect-error cast is already defined globally, however since we're mocking it we need to redefine it. diff --git a/src/components/codecSupportHelper.ts b/src/components/codecSupportHelper.ts index bcf931df..92e017e8 100644 --- a/src/components/codecSupportHelper.ts +++ b/src/components/codecSupportHelper.ts @@ -191,7 +191,7 @@ export function getH265LevelSupport(deviceId: DeviceIds): number { * Get VPX (VP8, VP9) codecs supported by the active Cast device. * @returns Supported VPX codecs. */ -export function getSupportedVPXVideoCodecs(): Array { +export function getSupportedVPXVideoCodecs(): string[] { const codecs = []; if (hasVP8Support()) { @@ -209,7 +209,7 @@ export function getSupportedVPXVideoCodecs(): Array { * Get supported video codecs suitable for use in an MP4 container. * @returns Supported MP4 video codecs. */ -export function getSupportedMP4VideoCodecs(): Array { +export function getSupportedMP4VideoCodecs(): string[] { const codecs = ['h264']; if (hasH265Support()) { @@ -224,7 +224,7 @@ export function getSupportedMP4VideoCodecs(): Array { * Get supported audio codecs suitable for use in an MP4 container. * @returns Supported MP4 audio codecs. */ -export function getSupportedMP4AudioCodecs(): Array { +export function getSupportedMP4AudioCodecs(): string[] { const codecs = []; if (hasEAC3Support()) { @@ -245,7 +245,7 @@ export function getSupportedMP4AudioCodecs(): Array { * Get supported video codecs suitable for use with HLS. * @returns Supported HLS video codecs. */ -export function getSupportedHLSVideoCodecs(): Array { +export function getSupportedHLSVideoCodecs(): string[] { // Currently the server does not support fmp4 which is required // by the HLS spec for streaming H.265 video. return ['h264']; @@ -255,7 +255,7 @@ export function getSupportedHLSVideoCodecs(): Array { * Get supported audio codecs suitable for use with HLS. * @returns All supported HLS audio codecs. */ -export function getSupportedHLSAudioCodecs(): Array { +export function getSupportedHLSAudioCodecs(): string[] { // HLS basically supports whatever MP4 supports. return getSupportedMP4AudioCodecs(); } @@ -264,7 +264,7 @@ export function getSupportedHLSAudioCodecs(): Array { * Get supported audio codecs suitable for use in a WebM container. * @returns All supported WebM audio codecs. */ -export function getSupportedWebMAudioCodecs(): Array { +export function getSupportedWebMAudioCodecs(): string[] { return ['vorbis', 'opus']; } @@ -272,6 +272,6 @@ export function getSupportedWebMAudioCodecs(): Array { * Get supported audio codecs suitable for use in a WebM container. * @returns All supported WebM audio codecs. */ -export function getSupportedAudioCodecs(): Array { +export function getSupportedAudioCodecs(): string[] { return ['opus', 'mp3', 'aac', 'flac', 'webma', 'wav']; } diff --git a/src/components/commandHandler.ts b/src/components/commandHandler.ts index 43c8ba59..6c070119 100644 --- a/src/components/commandHandler.ts +++ b/src/components/commandHandler.ts @@ -55,36 +55,36 @@ export abstract class CommandHandler { } static playNextHandler(data: DataMessage): void { - translateItems(data, data.options, data.command); + translateItems(data, data.options as PlayRequest, data.command); } static playNowHandler(data: DataMessage): void { - translateItems(data, data.options, data.command); + translateItems(data, data.options as PlayRequest, data.command); } static playLastHandler(data: DataMessage): void { - translateItems(data, data.options, data.command); + translateItems(data, data.options as PlayRequest, data.command); } static shuffleHandler(data: DataMessage): void { shuffle( data, - data.options, - (data.options).items[0] + data.options as PlayRequest, + (data.options as PlayRequest).items[0] ); } static instantMixHandler(data: DataMessage): void { instantMix( data, - data.options, - (data.options).items[0] + data.options as PlayRequest, + (data.options as PlayRequest).items[0] ); } static displayContentHandler(data: DataMessage): void { if (!PlaybackManager.isPlaying()) { - DocumentManager.showItemId((data.options).ItemId); + DocumentManager.showItemId((data.options as DisplayRequest).ItemId); } } @@ -103,14 +103,14 @@ export abstract class CommandHandler { static setAudioStreamIndexHandler(data: DataMessage): void { setAudioStreamIndex( PlaybackManager.playbackState, - (data.options).index + (data.options as SetIndexRequest).index ); } static setSubtitleStreamIndexHandler(data: DataMessage): void { setSubtitleStreamIndex( PlaybackManager.playbackState, - (data.options).index + (data.options as SetIndexRequest).index ); } @@ -155,7 +155,7 @@ export abstract class CommandHandler { static SeekHandler(data: DataMessage): void { seek( PlaybackManager.playbackState, - (data.options).position * TicksPerSecond + (data.options as SeekRequest).position * TicksPerSecond ); } @@ -189,7 +189,7 @@ export abstract class CommandHandler { } static SetRepeatModeHandler(data: DataMessage): void { - window.repeatMode = (data.options).RepeatMode; + window.repeatMode = (data.options as SetRepeatModeRequest).RepeatMode; window.reportEventType = 'repeatmodechange'; } @@ -200,7 +200,7 @@ export abstract class CommandHandler { // We should avoid using a defaulthandler that has a purpose other than informing the dev/user // Currently all unhandled commands will be treated as play commands. static defaultHandler(data: DataMessage): void { - translateItems(data, data.options, 'play'); + translateItems(data, data.options as PlayRequest, 'play'); } static processMessage(data: DataMessage, command: string): void { diff --git a/src/components/credentialManager.ts b/src/components/credentialManager.ts index 953c4d77..75b18042 100644 --- a/src/components/credentialManager.ts +++ b/src/components/credentialManager.ts @@ -4,9 +4,7 @@ export interface ServerCredential { serverBasePath?: string; } -interface CredentialStore { - [id: string]: ServerCredential; -} +type CredentialStore = Record; export class credentialManager { /** diff --git a/src/components/deviceprofileBuilder.ts b/src/components/deviceprofileBuilder.ts index 1bf1751a..c535114b 100644 --- a/src/components/deviceprofileBuilder.ts +++ b/src/components/deviceprofileBuilder.ts @@ -68,7 +68,7 @@ function createProfileCondition( * @todo Why does this always return an empty array? * @returns Container profiles. */ -function getContainerProfiles(): Array { +function getContainerProfiles(): ContainerProfile[] { return []; } @@ -76,8 +76,8 @@ function getContainerProfiles(): Array { * Get direct play profiles * @returns Direct play profiles. */ -function getDirectPlayProfiles(): Array { - const DirectPlayProfiles: Array = []; +function getDirectPlayProfiles(): DirectPlayProfile[] { + const DirectPlayProfiles: DirectPlayProfile[] = []; if (currentDeviceId !== DeviceIds.AUDIO) { const mp4VideoCodecs = getSupportedMP4VideoCodecs(); @@ -140,8 +140,8 @@ function getDirectPlayProfiles(): Array { * Get codec profiles * @returns Codec profiles. */ -function getCodecProfiles(): Array { - const CodecProfiles: Array = []; +function getCodecProfiles(): CodecProfile[] { + const CodecProfiles: CodecProfile[] = []; const audioConditions: CodecProfile = { Codec: 'flac', @@ -285,8 +285,8 @@ function getCodecProfiles(): Array { * Get transcoding profiles * @returns Transcoding profiles. */ -function getTranscodingProfiles(): Array { - const TranscodingProfiles: Array = []; +function getTranscodingProfiles(): TranscodingProfile[] { + const TranscodingProfiles: TranscodingProfile[] = []; const hlsAudioCodecs = getSupportedHLSAudioCodecs(); const audioChannels: number = hasSurroundSupport() ? 6 : 2; @@ -364,8 +364,8 @@ function getTranscodingProfiles(): Array { * Get subtitle profiles * @returns Subtitle profiles. */ -function getSubtitleProfiles(): Array { - const subProfiles: Array = []; +function getSubtitleProfiles(): SubtitleProfile[] { + const subProfiles: SubtitleProfile[] = []; if (hasTextTrackSupport(currentDeviceId)) { subProfiles.push({ diff --git a/src/components/documentManager.ts b/src/components/documentManager.ts index c493bff7..c1ea1ca9 100644 --- a/src/components/documentManager.ts +++ b/src/components/documentManager.ts @@ -521,7 +521,7 @@ export abstract class DocumentManager { * @param item - source for the displayed name */ private static setDisplayName(item: BaseItemDto): void { - const name: string = item.EpisodeTitle ?? item.Name; + const name: string = item.EpisodeTitle ?? (item.Name as string); let displayName: string = name; @@ -574,9 +574,9 @@ export abstract class DocumentManager { * @param value - Percentage to set */ private static setPlayedPercentage(value = 0): void { - const element = ( - this.querySelector('.itemProgressBar') - ); + const element = this.querySelector( + '.itemProgressBar' + ) as HTMLInputElement; element.value = value.toString(); } @@ -590,9 +590,9 @@ export abstract class DocumentManager { const element = this.querySelector('.detailImageProgressContainer'); if (value) { - (element).classList.remove('d-none'); + (element as HTMLElement).classList.remove('d-none'); } else { - (element).classList.add('d-none'); + (element as HTMLElement).classList.add('d-none'); } } @@ -643,7 +643,7 @@ export abstract class DocumentManager { * @param item - to look up */ private static setMiscInfo(item: BaseItemDto): void { - const info: Array = []; + const info: string[] = []; if (item.Type == 'Episode') { if (item.PremiereDate) { diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index f5d82680..d370f32c 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -98,7 +98,7 @@ export function onMediaElementPlaying(): void { * @param event - event */ function onMediaElementVolumeChange(event: framework.system.Event): void { - window.volume = (event).data; + window.volume = (event as framework.system.SystemVolumeChangedEvent).data; console.log(`Received volume update: ${window.volume.level}`); if (JellyfinApi.serverAddress !== null) { @@ -415,7 +415,7 @@ export function setSubtitleStreamIndex( const mediaStreams = state.PlaybackMediaSource?.MediaStreams; const subtitleStream = getStreamByIndex( - mediaStreams, + mediaStreams as MediaStream[], 'Subtitle', index ); @@ -707,7 +707,7 @@ export function showPlaybackInfoErrorMessage(error: string): void { * @returns stream */ export function getOptimalMediaSource( - versions: Array + versions: MediaSourceInfo[] ): MediaSourceInfo { let optimalVersion = versions.filter((v) => { checkDirectPlay(v); @@ -763,7 +763,7 @@ export function setTextTrack(index: number | null): void { return; } - const tracks: Array = + const tracks: framework.messages.Track[] = textTracksManager.getTracks(); const subtitleTrack: framework.messages.Track | undefined = tracks.find( (track: framework.messages.Track) => { diff --git a/src/components/playbackManager.ts b/src/components/playbackManager.ts index 9532697d..10d7792b 100644 --- a/src/components/playbackManager.ts +++ b/src/components/playbackManager.ts @@ -54,7 +54,7 @@ export interface PlaybackState { // eslint-disable-next-line @typescript-eslint/no-extraneous-class export abstract class PlaybackManager { private static playerManager: framework.PlayerManager; - private static activePlaylist: Array; + private static activePlaylist: BaseItemDto[]; private static activePlaylistIndex: number; static playbackState: PlaybackState = { diff --git a/src/helpers.ts b/src/helpers.ts index 04a13eb1..b1f00da4 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -330,7 +330,7 @@ export function createStreamInfo( } else { // TODO deal with !TranscodingUrl mediaUrl = JellyfinApi.createUrl( - mediaSource.TranscodingUrl + mediaSource.TranscodingUrl as string ); if (isHlsStream(mediaSource)) { @@ -374,7 +374,7 @@ export function createStreamInfo( // TODO deal with !TranscodingUrl mediaUrl = JellyfinApi.createUrl( - mediaSource.TranscodingUrl + mediaSource.TranscodingUrl as string ); } } @@ -403,7 +403,7 @@ export function createStreamInfo( mediaSource.MediaStreams?.filter((stream: MediaStream) => { return stream.Type === 'Subtitle'; }) ?? []; - const subtitleTracks: Array = []; + const subtitleTracks: framework.messages.Track[] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any subtitleStreams.forEach((subtitleStream: any) => { @@ -450,7 +450,7 @@ export function createStreamInfo( * @returns first first matching stream */ export function getStreamByIndex( - streams: Array, + streams: MediaStream[], type: string, index: number // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -640,7 +640,7 @@ export function getUser(): Promise { */ export async function translateRequestedItems( userId: string, - items: Array, + items: BaseItemDto[], smart = false ): Promise { const firstItem = items[0]; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 6a54b6af..ccb7a6a4 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -9,9 +9,7 @@ import type { } from '@jellyfin/sdk/lib/generated-client'; import { TextTrackEdgeType } from 'chromecast-caf-receiver/cast.framework.messages'; -export interface Dictionary { - [Key: string]: T; -} +export type Dictionary = Record; // Jellyfin Server // Why doesn't the API have a type for this? @@ -85,9 +83,7 @@ export interface DataMessage { command: string; } -interface SupportedCommands { - [command: string]: (data: DataMessage) => void; -} +type SupportedCommands = Record void>; // /From commandHandler interface SubtitleAppearance { From c53e432f2790f32bea305c7c069800d80b8f7f52 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:42:18 +1100 Subject: [PATCH 3/6] Enable stylistic type checked rule set --- eslint.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 787e4aa0..20a351df 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,7 +12,7 @@ export default [ jsdoc.configs['flat/recommended'], eslintPluginPrettierRecommended, ...tseslint.configs.strict, - ...tseslint.configs.stylistic, + ...tseslint.configs.stylisticTypeChecked, promise.configs['flat/recommended'], importPlugin.flatConfigs.errors, importPlugin.flatConfigs.warnings, From d9f23112806a67d5c34200790f88e2c6d1f97342 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:44:30 +1100 Subject: [PATCH 4/6] Auto fix tseslint stylistic type checked issues --- src/components/documentManager.ts | 22 ++++++---------------- src/components/fetchhelper.ts | 2 +- src/components/jellyfinApi.ts | 10 +++++----- src/components/maincontroller.ts | 11 +++-------- src/helpers.ts | 24 +++++++----------------- 5 files changed, 22 insertions(+), 47 deletions(-) diff --git a/src/components/documentManager.ts b/src/components/documentManager.ts index c1ea1ca9..1c420362 100644 --- a/src/components/documentManager.ts +++ b/src/components/documentManager.ts @@ -304,11 +304,7 @@ export abstract class DocumentManager { let src: string | null = null; if (item != null) { - if ( - item.BackdropImageTags && - item.BackdropImageTags.length && - item.Id - ) { + if (item.BackdropImageTags?.length && item.Id) { // get first backdrop of image if applicable src = JellyfinApi.createImageUrl( item.Id, @@ -317,8 +313,7 @@ export abstract class DocumentManager { ); } else if ( item.ParentBackdropItemId && - item.ParentBackdropImageTags && - item.ParentBackdropImageTags.length + item.ParentBackdropImageTags?.length ) { // otherwise get first backdrop from parent src = JellyfinApi.createImageUrl( @@ -376,7 +371,7 @@ export abstract class DocumentManager { let src: string | null = null; let item: BaseItemDto | null = null; - if (result.Items && result.Items[0]) { + if (result.Items?.[0]) { item = result.Items[0]; src = await DocumentManager.getWaitingBackdropUrl(item); } @@ -459,11 +454,7 @@ export abstract class DocumentManager { let backdropUrl: string | null = null; - if ( - item.BackdropImageTags && - item.BackdropImageTags.length && - item.Id - ) { + if (item.BackdropImageTags?.length && item.Id) { backdropUrl = JellyfinApi.createImageUrl( item.Id, 'Backdrop', @@ -471,8 +462,7 @@ export abstract class DocumentManager { ); } else if ( item.ParentBackdropItemId && - item.ParentBackdropImageTags && - item.ParentBackdropImageTags.length + item.ParentBackdropImageTags?.length ) { backdropUrl = JellyfinApi.createImageUrl( item.ParentBackdropItemId, @@ -521,7 +511,7 @@ export abstract class DocumentManager { * @param item - source for the displayed name */ private static setDisplayName(item: BaseItemDto): void { - const name: string = item.EpisodeTitle ?? (item.Name as string); + const name: string = item.EpisodeTitle ?? item.Name!; let displayName: string = name; diff --git a/src/components/fetchhelper.ts b/src/components/fetchhelper.ts index 371a22e7..7122ced1 100644 --- a/src/components/fetchhelper.ts +++ b/src/components/fetchhelper.ts @@ -128,7 +128,7 @@ export async function ajax(request: any): Promise { request.dataType === 'text' || (response.headers.get('Content-Type') || '') .toLowerCase() - .indexOf('text/') === 0 + .startsWith('text/') ) { return response.text(); } else { diff --git a/src/components/jellyfinApi.ts b/src/components/jellyfinApi.ts index eaaf2782..b98690b1 100644 --- a/src/components/jellyfinApi.ts +++ b/src/components/jellyfinApi.ts @@ -58,15 +58,15 @@ export abstract class JellyfinApi { }; if (this.accessToken) { - parameters['Token'] = this.accessToken; + parameters.Token = this.accessToken; } if (this.deviceId) { - parameters['DeviceId'] = this.deviceId; + parameters.DeviceId = this.deviceId; } if (this.deviceName) { - parameters['Device'] = this.deviceName; + parameters.Device = this.deviceName; } let header = 'MediaBrowser'; @@ -93,7 +93,7 @@ export abstract class JellyfinApi { } // Remove leading slashes - while (path.charAt(0) === '/') { + while (path.startsWith('/')) { path = path.substring(1); } @@ -104,7 +104,7 @@ export abstract class JellyfinApi { public static createUserUrl(path: string | null = null): string { if (path) { // Remove leading slashes - while (path.charAt(0) === '/') { + while (path.startsWith('/')) { path = path.substring(1); } diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index d370f32c..1c2774a0 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -414,11 +414,7 @@ export function setSubtitleStreamIndex( const mediaStreams = state.PlaybackMediaSource?.MediaStreams; - const subtitleStream = getStreamByIndex( - mediaStreams as MediaStream[], - 'Subtitle', - index - ); + const subtitleStream = getStreamByIndex(mediaStreams!, 'Subtitle', index); if (!subtitleStream) { console.log( @@ -738,8 +734,7 @@ export function checkDirectPlay(mediaSource: MediaSourceInfo): void { if ( mediaSource.SupportsDirectPlay && mediaSource.Protocol == 'Http' && - (!mediaSource.RequiredHttpHeaders || - !mediaSource.RequiredHttpHeaders.length) + !mediaSource.RequiredHttpHeaders?.length ) { return; } @@ -771,7 +766,7 @@ export function setTextTrack(index: number | null): void { } ); - if (subtitleTrack && subtitleTrack.trackId !== undefined) { + if (subtitleTrack?.trackId !== undefined) { textTracksManager.setActiveByIds([subtitleTrack.trackId]); const subtitleAppearance = window.subtitleAppearance; diff --git a/src/helpers.ts b/src/helpers.ts index b1f00da4..d1f6c057 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -129,13 +129,10 @@ export function getSenderReportingData( nowPlayingItem.PrimaryImageTag = item.AlbumPrimaryImageTag; } - if (item.BackdropImageTags && item.BackdropImageTags.length) { + if (item.BackdropImageTags?.length) { nowPlayingItem.BackdropItemId = item.Id; nowPlayingItem.BackdropImageTag = item.BackdropImageTags[0]; - } else if ( - item.ParentBackdropImageTags && - item.ParentBackdropImageTags.length - ) { + } else if (item.ParentBackdropImageTags?.length) { nowPlayingItem.BackdropItemId = item.ParentBackdropItemId; nowPlayingItem.BackdropImageTag = item.ParentBackdropImageTags[0]; } @@ -219,8 +216,7 @@ export function getMetadata(item: BaseItemDto): any { } else if (item.Type == 'Audio') { metadata = new cast.framework.messages.MusicTrackMediaMetadata(); metadata.songName = item.Name; - metadata.artist = - item.Artists && item.Artists.length ? item.Artists.join(', ') : ''; + metadata.artist = item.Artists?.length ? item.Artists.join(', ') : ''; metadata.albumArtist = item.AlbumArtist; metadata.albumName = item.Album; @@ -263,7 +259,7 @@ export function getMetadata(item: BaseItemDto): any { ).toISOString(); } - if (item.Studios && item.Studios.length) { + if (item.Studios?.length) { metadata.studio = item.Studios[0]; } } @@ -329,9 +325,7 @@ export function createStreamInfo( playerStartPositionTicks = startPosition || 0; } else { // TODO deal with !TranscodingUrl - mediaUrl = JellyfinApi.createUrl( - mediaSource.TranscodingUrl as string - ); + mediaUrl = JellyfinApi.createUrl(mediaSource.TranscodingUrl!); if (isHlsStream(mediaSource)) { mediaUrl += seekParam; @@ -342,9 +336,7 @@ export function createStreamInfo( contentType = `video/${mediaSource.TranscodingContainer}`; streamContainer = mediaSource.TranscodingContainer; - if ( - mediaUrl.toLowerCase().indexOf('copytimestamps=true') != -1 - ) { + if (mediaUrl.toLowerCase().includes('copytimestamps=true')) { startPosition = 0; } } @@ -373,9 +365,7 @@ export function createStreamInfo( contentType = `audio/${mediaSource.TranscodingContainer}`; // TODO deal with !TranscodingUrl - mediaUrl = JellyfinApi.createUrl( - mediaSource.TranscodingUrl as string - ); + mediaUrl = JellyfinApi.createUrl(mediaSource.TranscodingUrl!); } } } From 17be9f62ce7058c5ded00c167d0bf18f45d97118 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:31:45 +1100 Subject: [PATCH 5/6] Manually fix remaining tseslint stylistic type checked issues --- src/components/documentManager.ts | 4 ++-- src/components/maincontroller.ts | 30 +++++++++++++----------------- src/helpers.ts | 30 +++++++++++++++--------------- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/components/documentManager.ts b/src/components/documentManager.ts index 1c420362..44684f09 100644 --- a/src/components/documentManager.ts +++ b/src/components/documentManager.ts @@ -545,7 +545,7 @@ export abstract class DocumentManager { private static setGenres(name: string | null): void { const element = this.querySelector('.genres'); - element.innerHTML = name || ''; + element.innerHTML = name ?? ''; } /** @@ -555,7 +555,7 @@ export abstract class DocumentManager { private static setOverview(name: string | null): void { const element = this.querySelector('.overview'); - element.innerHTML = name || ''; + element.innerHTML = name ?? ''; } /** diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index 1c2774a0..ede46fc5 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -386,11 +386,11 @@ export function setSubtitleStreamIndex( let positionTicks; // FIXME: Possible index error when MediaStreams is undefined. - const currentSubtitleStream = state.mediaSource?.MediaStreams?.filter( + const currentSubtitleStream = state.mediaSource?.MediaStreams?.find( (m: MediaStream) => { return m.Index == state.subtitleStreamIndex && m.Type == 'Subtitle'; } - )[0]; + ); const currentDeliveryMethod = currentSubtitleStream ? currentSubtitleStream.DeliveryMethod @@ -515,17 +515,12 @@ export async function changeStream( // @ts-expect-error is possible here return await PlaybackManager.playItemInternal(state.item, { - audioStreamIndex: - params.AudioStreamIndex == null - ? state.audioStreamIndex - : params.AudioStreamIndex, + audioStreamIndex: params.AudioStreamIndex ?? state.audioStreamIndex, liveStreamId: state.liveStreamId, mediaSourceId: state.mediaSourceId, startPositionTicks: ticks, subtitleStreamIndex: - params.SubtitleStreamIndex == null - ? state.subtitleStreamIndex - : params.SubtitleStreamIndex + params.SubtitleStreamIndex ?? state.subtitleStreamIndex }); } @@ -704,24 +699,25 @@ export function showPlaybackInfoErrorMessage(error: string): void { */ export function getOptimalMediaSource( versions: MediaSourceInfo[] -): MediaSourceInfo { - let optimalVersion = versions.filter((v) => { +): MediaSourceInfo | null { + let optimalVersion = versions.find((v) => { checkDirectPlay(v); return v.SupportsDirectPlay; - })[0]; + }); if (!optimalVersion) { - optimalVersion = versions.filter((v) => { + optimalVersion = versions.find((v) => { return v.SupportsDirectStream; - })[0]; + }); } return ( - optimalVersion || - versions.filter((s) => { + optimalVersion ?? + versions.find((s) => { return s.SupportsTranscoding; - })[0] + }) ?? + null ); } diff --git a/src/helpers.ts b/src/helpers.ts index d1f6c057..ad4f0f47 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -93,11 +93,11 @@ export function getSenderReportingData( const nowPlayingItem = state.NowPlayingItem; nowPlayingItem.ServerId = item.ServerId; - nowPlayingItem.Chapters = item.Chapters || []; + nowPlayingItem.Chapters = item.Chapters ?? []; - const mediaSource = item.MediaSources?.filter((m: MediaSourceInfo) => { + const mediaSource = item.MediaSources?.find((m: MediaSourceInfo) => { return m.Id == reportingData.MediaSourceId; - })[0]; + }); nowPlayingItem.MediaStreams = mediaSource ? mediaSource.MediaStreams @@ -116,7 +116,7 @@ export function getSenderReportingData( nowPlayingItem.Album = item.Album; nowPlayingItem.Artists = item.Artists; - const imageTags = item.ImageTags || {}; + const imageTags = item.ImageTags ?? {}; if (item.SeriesPrimaryImageTag) { nowPlayingItem.PrimaryImageItemId = item.SeriesId; @@ -235,9 +235,9 @@ export function getMetadata(item: BaseItemDto): any { } // previously: p.PersonType == 'Type'.. wtf? - const composer = (item.People || []).filter( + const composer = (item.People ?? []).find( (p: BaseItemPerson) => p.Type == 'Composer' - )[0]; + ); if (composer) { metadata.composer = composer.Name; @@ -322,14 +322,14 @@ export function createStreamInfo( `videos/${item.Id}/stream.${mediaSource.Container}?mediaSourceId=${mediaSource.Id}&api_key=${JellyfinApi.accessToken}&static=true${seekParam}` ); isStatic = true; - playerStartPositionTicks = startPosition || 0; + playerStartPositionTicks = startPosition ?? 0; } else { // TODO deal with !TranscodingUrl mediaUrl = JellyfinApi.createUrl(mediaSource.TranscodingUrl!); if (isHlsStream(mediaSource)) { mediaUrl += seekParam; - playerStartPositionTicks = startPosition || 0; + playerStartPositionTicks = startPosition ?? 0; contentType = 'application/x-mpegURL'; streamContainer = 'm3u8'; } else { @@ -347,13 +347,13 @@ export function createStreamInfo( if (mediaSource.SupportsDirectPlay) { mediaUrl = mediaSource.Path; isStatic = true; - playerStartPositionTicks = startPosition || 0; + playerStartPositionTicks = startPosition ?? 0; } else { const isDirectStream = mediaSource.SupportsDirectStream; if (isDirectStream) { const outputContainer = ( - mediaSource.Container || '' + mediaSource.Container ?? '' ).toLowerCase(); mediaUrl = JellyfinApi.createUrl( @@ -372,7 +372,7 @@ export function createStreamInfo( // TODO: Remove the second half of the expression by supporting changing the mediaElement src dynamically. // It is a pain and will require unbinding all event handlers during the operation - const canSeek = (mediaSource.RunTimeTicks || 0) > 0; + const canSeek = (mediaSource.RunTimeTicks ?? 0) > 0; // eslint-disable-next-line @typescript-eslint/no-explicit-any const info: any = { @@ -445,9 +445,9 @@ export function getStreamByIndex( index: number // eslint-disable-next-line @typescript-eslint/no-explicit-any ): any { - return streams.filter((s) => { + return streams.find((s) => { return s.Type == type && s.Index == index; - })[0]; + }); } // defined for use in the 3 next functions @@ -550,7 +550,7 @@ export async function getItemsForPlayback( query: ItemQuery ): Promise { query.UserId = userId; - query.Limit = query.Limit || 100; + query.Limit = query.Limit ?? 100; query.Fields = requiredItemFields; query.ExcludeLocationTypes = 'Virtual'; @@ -710,7 +710,7 @@ export async function translateRequestedItems( } ); - episodesResult.TotalRecordCount = episodesResult.Items?.length || 0; + episodesResult.TotalRecordCount = episodesResult.Items?.length ?? 0; return episodesResult; } From 9ccd21a83d3bee14e64fff265704c50ccb531150 Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:14:25 +1100 Subject: [PATCH 6/6] Suppress individual no-non-null-assertions issues that have no simple fix --- src/components/documentManager.ts | 1 + src/components/maincontroller.ts | 1 + src/helpers.ts | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/components/documentManager.ts b/src/components/documentManager.ts index 44684f09..f4212876 100644 --- a/src/components/documentManager.ts +++ b/src/components/documentManager.ts @@ -511,6 +511,7 @@ export abstract class DocumentManager { * @param item - source for the displayed name */ private static setDisplayName(item: BaseItemDto): void { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const name: string = item.EpisodeTitle ?? item.Name!; let displayName: string = name; diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index ede46fc5..5a25232b 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -414,6 +414,7 @@ export function setSubtitleStreamIndex( const mediaStreams = state.PlaybackMediaSource?.MediaStreams; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const subtitleStream = getStreamByIndex(mediaStreams!, 'Subtitle', index); if (!subtitleStream) { diff --git a/src/helpers.ts b/src/helpers.ts index ad4f0f47..291ac32d 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -325,6 +325,7 @@ export function createStreamInfo( playerStartPositionTicks = startPosition ?? 0; } else { // TODO deal with !TranscodingUrl + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaUrl = JellyfinApi.createUrl(mediaSource.TranscodingUrl!); if (isHlsStream(mediaSource)) { @@ -365,6 +366,7 @@ export function createStreamInfo( contentType = `audio/${mediaSource.TranscodingContainer}`; // TODO deal with !TranscodingUrl + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaUrl = JellyfinApi.createUrl(mediaSource.TranscodingUrl!); } }