From 27ff1feb9320717a7b87f3a74b578180d4159c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Thu, 13 May 2021 12:56:50 +0200 Subject: [PATCH] chore: manual lint fix --- .editorconfig | 6 +- .eslintrc.js | 134 ++++++++++++------------- package.json | 2 +- src/components/commandHandler.ts | 38 +++---- src/components/deviceprofileBuilder.ts | 76 +++++++------- src/components/documentManager.ts | 12 +-- src/components/fetchhelper.ts | 4 +- src/components/jellyfinActions.ts | 62 ++++++------ src/components/jellyfinApi.ts | 8 +- src/components/maincontroller.ts | 40 ++++---- src/components/playbackManager.ts | 4 +- src/helpers.ts | 86 ++++++++-------- stylelint.config.js | 4 +- webpack.config.ts | 46 ++++----- 14 files changed, 261 insertions(+), 261 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3c44241..9c61627 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ root = true [*] +charset = utf-8 +end_of_line = lf indent_style = space indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintrc.js b/.eslintrc.js index a8d4ca8..c70176a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,97 +16,97 @@ module.exports = { 'plugin:import/warnings', 'plugin:import/typescript' ], + overrides: [ + { + env: { + browser: true, + es6: true, + node: false + }, + files: ['.js', '.ts'], + globals: { + $scope: 'writable', + cast: 'readonly', + PRODUCTION: 'readonly' + } + } + ], plugins: ['prettier', 'promise', 'import', 'jsdoc'], root: true, rules: { + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', curly: 'error', - 'padding-line-between-statements': [ - 'error', - // Always require blank lines after directives (like 'use-strict'), except between directives - { blankLine: 'always', next: '*', prev: 'directive' }, - { blankLine: 'any', prev: 'directive', next: 'directive' }, - // Always require blank lines after import, except between imports - { blankLine: 'always', prev: 'import', next: '*' }, - { blankLine: 'any', prev: 'import', next: 'import' }, - // Always require blank lines before and after every sequence of variable declarations and export - { - blankLine: 'always', - prev: '*', - next: ['const', 'let', 'var', 'export'] - }, - { - blankLine: 'always', - prev: ['const', 'let', 'var', 'export'], - next: '*' - }, - { - blankLine: 'any', - prev: ['const', 'let', 'var', 'export'], - next: ['const', 'let', 'var', 'export'] - }, - // Always require blank lines before and after class declaration, if, do/while, switch, try - { - blankLine: 'always', - prev: '*', - next: ['if', 'class', 'for', 'do', 'while', 'switch', 'try'] - }, - { - blankLine: 'always', - prev: ['if', 'class', 'for', 'do', 'while', 'switch', 'try'], - next: '*' - }, - // Always require blank lines before return statements - { blankLine: 'always', prev: '*', next: 'return' } - ], 'import/newline-after-import': 'error', 'import/order': 'error', - 'jsdoc/require-hyphen-before-param-description': 'error', + 'jsdoc/check-indentation': 'error', + 'jsdoc/check-param-names': 'error', + 'jsdoc/check-property-names': 'error', + 'jsdoc/check-syntax': 'error', + 'jsdoc/check-tag-names': 'error', + 'jsdoc/no-types': 'error', 'jsdoc/require-description': 'warn', - 'jsdoc/require-param-description': 'warn', + 'jsdoc/require-hyphen-before-param-description': 'error', 'jsdoc/require-jsdoc': 'error', + 'jsdoc/require-param-description': 'warn', //TypeScript and IntelliSense already provides us information about the function typings while hovering and // eslint-jsdoc doesn't detect a mismatch between what's declared in the function and what's declared in // JSDOC. 'jsdoc/require-param-type': 'off', 'jsdoc/require-returns-type': 'off', - 'jsdoc/check-indentation': 'error', - 'jsdoc/check-syntax': 'error', - 'jsdoc/check-param-names': 'error', - 'jsdoc/check-property-names': 'error', - 'jsdoc/check-tag-names': 'error', - 'jsdoc/no-types': 'error', 'jsdoc/valid-types': 'off', + 'padding-line-between-statements': [ + 'error', + // Always require blank lines after directives (like 'use-strict'), except between directives + { blankLine: 'always', next: '*', prev: 'directive' }, + { blankLine: 'any', next: 'directive', prev: 'directive' }, + // Always require blank lines after import, except between imports + { blankLine: 'always', next: '*', prev: 'import' }, + { blankLine: 'any', next: 'import', prev: 'import' }, + // Always require blank lines before and after every sequence of variable declarations and export + { + blankLine: 'always', + next: ['const', 'let', 'var', 'export'], + prev: '*' + }, + { + blankLine: 'always', + next: '*', + prev: ['const', 'let', 'var', 'export'] + }, + { + blankLine: 'any', + next: ['const', 'let', 'var', 'export'], + prev: ['const', 'let', 'var', 'export'] + }, + // Always require blank lines before and after class declaration, if, do/while, switch, try + { + blankLine: 'always', + next: ['if', 'class', 'for', 'do', 'while', 'switch', 'try'], + prev: '*' + }, + { + blankLine: 'always', + next: '*', + prev: ['if', 'class', 'for', 'do', 'while', 'switch', 'try'] + }, + // Always require blank lines before return statements + { blankLine: 'always', next: 'return', prev: '*' } + ], + 'prefer-arrow-callback': 'error', + 'prefer-template': 'error', 'promise/no-nesting': 'error', 'promise/no-return-in-finally': 'error', 'promise/prefer-await-to-callbacks': 'error', 'promise/prefer-await-to-then': 'error', - '@typescript-eslint/explicit-function-return-type': 'error', - '@typescript-eslint/prefer-ts-expect-error': 'error', - '@typescript-eslint/no-unused-vars': 'error', - 'prefer-arrow-callback': 'error', - 'prefer-template': 'error', 'sort-keys': [ 'error', 'asc', - { caseSensitive: true, natural: false, minKeys: 2 } + { caseSensitive: false, minKeys: 2, natural: true } ], 'sort-vars': 'error' }, - overrides: [ - { - files: ['.js', '.ts'], - env: { - node: false, - browser: true, - es6: true - }, - globals: { - cast: 'readonly', - PRODUCTION: 'readonly', - $scope: 'writable' - } - } - ], settings: { 'import/parsers': { '@typescript-eslint/parser': ['.ts', '.tsx'] diff --git a/package.json b/package.json index 6196e1a..8698fec 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "lint:code": "eslint --ext .ts,.js,.json .", "lint:css": "stylelint **/*.css", "prepare": "npm run build:production", - "prettier": "prettier --write .", + "prettier": "prettier --check .", "start": "TS_NODE_PROJECT=\"tsconfig-webpack.json\" webpack serve --config webpack.config.ts", "test": "jest --passWithNoTests", "watch": "TS_NODE_PROJECT=\"tsconfig-webpack.json\" webpack --config webpack.config.ts --watch" diff --git a/src/components/commandHandler.ts b/src/components/commandHandler.ts index 3c98f22..0e69dd1 100644 --- a/src/components/commandHandler.ts +++ b/src/components/commandHandler.ts @@ -27,29 +27,29 @@ export abstract class CommandHandler { private static playerManager: framework.PlayerManager; private static playbackManager: playbackManager; private static supportedCommands: SupportedCommands = { + DisplayContent: CommandHandler.displayContentHandler, + Identify: CommandHandler.IdentifyHandler, + InstantMix: CommandHandler.instantMixHandler, + Mute: CommandHandler.MuteHandler, + NextTrack: CommandHandler.nextTrackHandler, + Pause: CommandHandler.PauseHandler, + PlayLast: CommandHandler.playLastHandler, PlayNext: CommandHandler.playNextHandler, PlayNow: CommandHandler.playNowHandler, - PlayLast: CommandHandler.playLastHandler, - Shuffle: CommandHandler.shuffleHandler, - InstantMix: CommandHandler.instantMixHandler, - DisplayContent: CommandHandler.displayContentHandler, - NextTrack: CommandHandler.nextTrackHandler, - PreviousTrack: CommandHandler.previousTrackHandler, - SetAudioStreamIndex: CommandHandler.setAudioStreamIndexHandler, - SetSubtitleStreamIndex: CommandHandler.setSubtitleStreamIndexHandler, - VolumeUp: CommandHandler.VolumeUpHandler, - VolumeDown: CommandHandler.VolumeDownHandler, - ToggleMute: CommandHandler.ToggleMuteHandler, - Identify: CommandHandler.IdentifyHandler, - SetVolume: CommandHandler.SetVolumeHandler, - Seek: CommandHandler.SeekHandler, - Mute: CommandHandler.MuteHandler, - Unmute: CommandHandler.MuteHandler, - Stop: CommandHandler.StopHandler, PlayPause: CommandHandler.PlayPauseHandler, - Pause: CommandHandler.PauseHandler, + PreviousTrack: CommandHandler.previousTrackHandler, + Seek: CommandHandler.SeekHandler, + SetAudioStreamIndex: CommandHandler.setAudioStreamIndexHandler, SetRepeatMode: CommandHandler.SetRepeatModeHandler, - Unpause: CommandHandler.UnpauseHandler + SetSubtitleStreamIndex: CommandHandler.setSubtitleStreamIndexHandler, + SetVolume: CommandHandler.SetVolumeHandler, + Shuffle: CommandHandler.shuffleHandler, + Stop: CommandHandler.StopHandler, + ToggleMute: CommandHandler.ToggleMuteHandler, + Unmute: CommandHandler.MuteHandler, + Unpause: CommandHandler.UnpauseHandler, + VolumeDown: CommandHandler.VolumeDownHandler, + VolumeUp: CommandHandler.VolumeUpHandler }; static configure( diff --git a/src/components/deviceprofileBuilder.ts b/src/components/deviceprofileBuilder.ts index f14414f..7ba7408 100644 --- a/src/components/deviceprofileBuilder.ts +++ b/src/components/deviceprofileBuilder.ts @@ -55,9 +55,9 @@ function createProfileCondition( ): ProfileCondition { return { Condition, + IsRequired, Property, - Value, - IsRequired + Value }; } @@ -75,9 +75,9 @@ function getResponseProfiles(): Array { // This seems related to DLNA, it might not be needed? return [ { - Type: DlnaProfileType.Video, Container: 'm4v', - MimeType: 'video/mp4' + MimeType: 'video/mp4', + Type: DlnaProfileType.Video } ]; } @@ -96,18 +96,18 @@ function getDirectPlayProfiles(): Array { for (const codec of vpxVideoCodecs) { DirectPlayProfiles.push({ + AudioCodec: webmAudioCodecs.join(','), Container: 'webm', Type: DlnaProfileType.Video, - AudioCodec: webmAudioCodecs.join(','), VideoCodec: codec }); } DirectPlayProfiles.push({ + AudioCodec: mp4AudioCodecs.join(','), Container: 'mp4,m4v', Type: DlnaProfileType.Video, - VideoCodec: mp4VideoCodecs.join(','), - AudioCodec: mp4AudioCodecs.join(',') + VideoCodec: mp4VideoCodecs.join(',') }); } @@ -116,9 +116,9 @@ function getDirectPlayProfiles(): Array { for (const audioFormat of supportedAudio) { if (audioFormat === 'mp3') { DirectPlayProfiles.push({ + AudioCodec: audioFormat, Container: audioFormat, - Type: DlnaProfileType.Audio, - AudioCodec: audioFormat + Type: DlnaProfileType.Audio }); } else if (audioFormat === 'webma') { DirectPlayProfiles.push({ @@ -135,8 +135,8 @@ function getDirectPlayProfiles(): Array { // aac also appears in the m4a and m4b container if (audioFormat === 'aac') { DirectPlayProfiles.push({ - Container: 'm4a,m4b', AudioCodec: audioFormat, + Container: 'm4a,m4b', Type: DlnaProfileType.Audio }); } @@ -152,7 +152,6 @@ function getCodecProfiles(): Array { const CodecProfiles: Array = []; const audioConditions: CodecProfile = { - Type: CodecType.Audio, Codec: 'flac', Conditions: [ createProfileCondition( @@ -165,7 +164,8 @@ function getCodecProfiles(): Array { ProfileConditionType.LessThanEqual, '24' ) - ] + ], + Type: CodecType.Audio }; CodecProfiles.push(audioConditions); @@ -176,7 +176,6 @@ function getCodecProfiles(): Array { } const aacConditions: CodecProfile = { - Type: CodecType.VideoAudio, Codec: 'aac', Conditions: [ // Not sure what secondary audio means in this context. Multiple audio tracks? @@ -190,7 +189,8 @@ function getCodecProfiles(): Array { ProfileConditionType.LessThanEqual, '2' ) - ] + ], + Type: CodecType.VideoAudio }; CodecProfiles.push(aacConditions); @@ -200,7 +200,6 @@ function getCodecProfiles(): Array { const h26xProfile: string = getH26xProfileSupport(currentDeviceId); const h26xConditions: CodecProfile = { - Type: CodecType.Video, Codec: 'h264', Conditions: [ createProfileCondition( @@ -224,13 +223,13 @@ function getCodecProfiles(): Array { maxWidth.toString(), true ) - ] + ], + Type: CodecType.Video }; CodecProfiles.push(h26xConditions); const videoConditions: CodecProfile = { - Type: CodecType.Video, Conditions: [ createProfileCondition( ProfileConditionValue.Width, @@ -238,20 +237,21 @@ function getCodecProfiles(): Array { maxWidth.toString(), true ) - ] + ], + Type: CodecType.Video }; CodecProfiles.push(videoConditions); const videoAudioConditions: CodecProfile = { - Type: CodecType.VideoAudio, Conditions: [ createProfileCondition( ProfileConditionValue.IsSecondaryAudio, ProfileConditionType.Equals, 'false' ) - ] + ], + Type: CodecType.VideoAudio }; CodecProfiles.push(videoAudioConditions); @@ -270,14 +270,14 @@ function getTranscodingProfiles(): Array { if (profileOptions.enableHls !== false) { TranscodingProfiles.push({ - Container: 'ts', - Type: DlnaProfileType.Audio, AudioCodec: hlsAudioCodecs.join(','), + BreakOnNonKeyFrames: false, + Container: 'ts', Context: EncodingContext.Streaming, - Protocol: 'hls', MaxAudioChannels: audioChannels.toString(), MinSegments: 1, - BreakOnNonKeyFrames: false + Protocol: 'hls', + Type: DlnaProfileType.Audio }); } @@ -286,12 +286,12 @@ function getTranscodingProfiles(): Array { // audio only profiles here for (const audioFormat of supportedAudio) { TranscodingProfiles.push({ - Container: audioFormat, - Type: DlnaProfileType.Audio, AudioCodec: audioFormat, + Container: audioFormat, Context: EncodingContext.Streaming, + MaxAudioChannels: audioChannels.toString(), Protocol: 'http', - MaxAudioChannels: audioChannels.toString() + Type: DlnaProfileType.Audio }); } @@ -308,29 +308,29 @@ function getTranscodingProfiles(): Array { profileOptions.enableHls !== false ) { TranscodingProfiles.push({ - Container: 'ts', - Type: DlnaProfileType.Video, AudioCodec: hlsAudioCodecs.join(','), - VideoCodec: hlsVideoCodecs.join(','), + BreakOnNonKeyFrames: false, + Container: 'ts', Context: EncodingContext.Streaming, - Protocol: 'hls', MaxAudioChannels: audioChannels.toString(), MinSegments: 1, - BreakOnNonKeyFrames: false + Protocol: 'hls', + Type: DlnaProfileType.Video, + VideoCodec: hlsVideoCodecs.join(',') }); } if (hasVP8Support() || hasVP9Support()) { TranscodingProfiles.push({ - Container: 'webm', - Type: DlnaProfileType.Video, AudioCodec: 'vorbis', - VideoCodec: 'vpx', + Container: 'webm', Context: EncodingContext.Streaming, - Protocol: 'http', // If audio transcoding is needed, limit channels to number of physical audio channels // Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good - MaxAudioChannels: audioChannels.toString() + MaxAudioChannels: audioChannels.toString(), + Protocol: 'http', + Type: DlnaProfileType.Video, + VideoCodec: 'vpx' }); } @@ -370,8 +370,8 @@ export function getDeviceProfile(options: ProfileOptions): DeviceProfile { // MaxStaticBitrate seems to be for offline sync only const profile: DeviceProfile = { - MaxStreamingBitrate: options.bitrateSetting, MaxStaticBitrate: options.bitrateSetting, + MaxStreamingBitrate: options.bitrateSetting, MusicStreamingTranscodingBitrate: Math.min( options.bitrateSetting, 192000 diff --git a/src/components/documentManager.ts b/src/components/documentManager.ts index 9918b27..a4f975b 100644 --- a/src/components/documentManager.ts +++ b/src/components/documentManager.ts @@ -372,17 +372,17 @@ export abstract class DocumentManager { private static async setRandomUserBackdrop(): Promise { const result = await JellyfinApi.authAjaxUser('Items', { dataType: 'json', - type: 'GET', query: { - SortBy: 'Random', - IncludeItemTypes: 'Movie,Series', ImageTypes: 'Backdrop', - Recursive: true, + IncludeItemTypes: 'Movie,Series', Limit: 1, + MaxOfficialRating: 'PG-13', + Recursive: true, + SortBy: 'Random' // Although we're limiting to what the user has access to, // not everyone will want to see adult backdrops rotating on their TV. - MaxOfficialRating: 'PG-13' - } + }, + type: 'GET' }); let src: string | null = null; diff --git a/src/components/fetchhelper.ts b/src/components/fetchhelper.ts index e1bf18d..0fe2282 100644 --- a/src/components/fetchhelper.ts +++ b/src/components/fetchhelper.ts @@ -12,9 +12,9 @@ function getFetchPromise(request: any): Promise { } const fetchRequest: RequestInit = { + credentials: 'same-origin', headers: headers, - method: request.type, - credentials: 'same-origin' + method: request.type }; let contentType = request.contentType; diff --git a/src/components/jellyfinActions.ts b/src/components/jellyfinActions.ts index 9f3b25f..0d408bb 100644 --- a/src/components/jellyfinActions.ts +++ b/src/components/jellyfinActions.ts @@ -79,16 +79,16 @@ export function reportPlaybackStart( broadcastToMessageBus({ //TODO: convert these to use a defined type in the type field - type: 'playbackstart', - data: getSenderReportingData($scope, reportingParams) + data: getSenderReportingData($scope, reportingParams), + type: 'playbackstart' }); restartPingInterval($scope, reportingParams); return JellyfinApi.authAjax('Sessions/Playing', { - type: 'POST', + contentType: 'application/json', data: JSON.stringify(reportingParams), - contentType: 'application/json' + type: 'POST' }); } @@ -108,8 +108,8 @@ export function reportPlaybackProgress( broadcastEventName = 'playbackprogress' ): Promise { broadcastToMessageBus({ - type: broadcastEventName, - data: getSenderReportingData($scope, reportingParams) + data: getSenderReportingData($scope, reportingParams), + type: broadcastEventName }); if (reportToServer === false) { @@ -120,9 +120,9 @@ export function reportPlaybackProgress( lastTranscoderPing = new Date().getTime(); return JellyfinApi.authAjax('Sessions/Playing/Progress', { - type: 'POST', + contentType: 'application/json', data: JSON.stringify(reportingParams), - contentType: 'application/json' + type: 'POST' }); } @@ -140,14 +140,14 @@ export function reportPlaybackStopped( stopPingInterval(); broadcastToMessageBus({ - type: 'playbackstop', - data: getSenderReportingData($scope, reportingParams) + data: getSenderReportingData($scope, reportingParams), + type: 'playbackstop' }); return JellyfinApi.authAjax('Sessions/Playing/Stopped', { - type: 'POST', + contentType: 'application/json', data: JSON.stringify(reportingParams), - contentType: 'application/json' + type: 'POST' }); } @@ -180,12 +180,12 @@ export function pingTranscoder( return JellyfinApi.authAjax( `Sessions/Playing/Ping?playSessionId=${reportingParams.PlaySessionId}`, { - type: 'POST', + contentType: 'application/json', data: JSON.stringify({ // jellyfin <= 10.6 wants it in the post data. PlaySessionId: reportingParams.PlaySessionId }), - contentType: 'application/json' + type: 'POST' } ); } @@ -276,9 +276,9 @@ export function getPlaybackInfo( // TODO: PlayRequestQuery might not be the proper type for this const query: PlayRequestQuery = { - UserId: JellyfinApi.userId ?? undefined, + MaxStreamingBitrate: maxBitrate, StartTimeTicks: startPosition || 0, - MaxStreamingBitrate: maxBitrate + UserId: JellyfinApi.userId ?? undefined }; if (audioStreamIndex != null) { @@ -298,11 +298,11 @@ export function getPlaybackInfo( } return JellyfinApi.authAjax(`Items/${item.Id}/PlaybackInfo`, { - query: query, - type: 'POST', - dataType: 'json', + contentType: 'application/json', data: JSON.stringify(postData), - contentType: 'application/json' + dataType: 'json', + query: query, + type: 'POST' }); } @@ -332,11 +332,11 @@ export function getLiveStream( }; const query: PlayRequestQuery = { - UserId: JellyfinApi.userId ?? undefined, - StartTimeTicks: startPosition || 0, ItemId: item.Id, MaxStreamingBitrate: maxBitrate, - PlaySessionId: playSessionId + PlaySessionId: playSessionId, + StartTimeTicks: startPosition || 0, + UserId: JellyfinApi.userId ?? undefined }; if (audioStreamIndex != null) { @@ -348,11 +348,11 @@ export function getLiveStream( } return JellyfinApi.authAjax('LiveStreams/Open', { - query: query, - type: 'POST', - dataType: 'json', + contentType: 'application/json', data: JSON.stringify(postData), - contentType: 'application/json' + dataType: 'json', + query: query, + type: 'POST' }); } @@ -370,8 +370,8 @@ export async function getDownloadSpeed(byteSize: number): Promise { const now = new Date().getTime(); await JellyfinApi.authAjax(path, { - type: 'GET', - timeout: 5000 + timeout: 5000, + type: 'GET' }); const responseTimeSeconds = (new Date().getTime() - now) / 1000; @@ -418,7 +418,7 @@ export function stopActiveEncodings($scope: GlobalScope): Promise { } return JellyfinApi.authAjax('Videos/ActiveEncodings', { - type: 'DELETE', - query: options + query: options, + type: 'DELETE' }); } diff --git a/src/components/jellyfinApi.ts b/src/components/jellyfinApi.ts index 976fe8a..8221ea0 100644 --- a/src/components/jellyfinApi.ts +++ b/src/components/jellyfinApi.ts @@ -110,8 +110,8 @@ export abstract class JellyfinApi { } const params = { - url: this.createUrl(path), - headers: this.getSecurityHeaders() + headers: this.getSecurityHeaders(), + url: this.createUrl(path) }; return ajax({ ...params, ...args }); @@ -132,8 +132,8 @@ export abstract class JellyfinApi { } const params = { - url: this.createUserUrl(path), - headers: this.getSecurityHeaders() + headers: this.getSecurityHeaders(), + url: this.createUserUrl(path) }; return ajax({ ...params, ...args }); diff --git a/src/components/maincontroller.ts b/src/components/maincontroller.ts index 0b28d4e..b51980c 100644 --- a/src/components/maincontroller.ts +++ b/src/components/maincontroller.ts @@ -210,23 +210,23 @@ export async function reportDeviceCapabilities(): Promise { const maxBitrate = await getMaxBitrate(); const deviceProfile = getDeviceProfile({ - enableHls: true, - bitrateSetting: maxBitrate + bitrateSetting: maxBitrate, + enableHls: true }); const capabilities = { + DeviceProfile: deviceProfile, PlayableMediaTypes: ['Audio', 'Video'], - SupportsPersistentIdentifier: false, SupportsMediaControl: true, - DeviceProfile: deviceProfile + SupportsPersistentIdentifier: false }; hasReportedCapabilities = true; return JellyfinApi.authAjax('Sessions/Capabilities/Full', { - type: 'POST', + contentType: 'application/json', data: JSON.stringify(capabilities), - contentType: 'application/json' + type: 'POST' }); } @@ -243,9 +243,9 @@ export function processMessage(data: any): void { console.log('Invalid message sent from sender. Sending error response'); broadcastToMessageBus({ - type: 'error', message: - 'Missing one or more required params - command,options,userId,accessToken,serverAddress' + 'Missing one or more required params - command,options,userId,accessToken,serverAddress', + type: 'error' }); return; @@ -442,8 +442,8 @@ export async function changeStream( const maxBitrate = await getMaxBitrate(); const deviceProfile = getDeviceProfile({ - enableHls: true, - bitrateSetting: maxBitrate + bitrateSetting: maxBitrate, + enableHls: true }); const audioStreamIndex = params.AudioStreamIndex == null @@ -665,7 +665,7 @@ export function validatePlaybackInfoResult(result: any): boolean { * @param error */ export function showPlaybackInfoErrorMessage(error: string): void { - broadcastToMessageBus({ type: 'playbackerror', message: error }); + broadcastToMessageBus({ message: error, type: 'playbackerror' }); } /** @@ -805,17 +805,17 @@ export function createMediaInformation( mediaInfo.contentId = streamInfo.url; mediaInfo.contentType = streamInfo.contentType; mediaInfo.customData = { - startPositionTicks: streamInfo.startPositionTicks || 0, - itemId: item.Id, - mediaSourceId: streamInfo.mediaSource.Id, audioStreamIndex: streamInfo.audioStreamIndex, - subtitleStreamIndex: streamInfo.subtitleStreamIndex, - playMethod: streamInfo.isStatic ? 'DirectStream' : 'Transcode', - runtimeTicks: streamInfo.mediaSource.RunTimeTicks, - liveStreamId: streamInfo.mediaSource.LiveStreamId, - canSeek: streamInfo.canSeek, canClientSeek: streamInfo.canClientSeek, - playSessionId: playSessionId + canSeek: streamInfo.canSeek, + itemId: item.Id, + liveStreamId: streamInfo.mediaSource.LiveStreamId, + mediaSourceId: streamInfo.mediaSource.Id, + playMethod: streamInfo.isStatic ? 'DirectStream' : 'Transcode', + playSessionId: playSessionId, + runtimeTicks: streamInfo.mediaSource.RunTimeTicks, + startPositionTicks: streamInfo.startPositionTicks || 0, + subtitleStreamIndex: streamInfo.subtitleStreamIndex }; mediaInfo.metadata = getMetadata(item); diff --git a/src/components/playbackManager.ts b/src/components/playbackManager.ts index 8942af3..f59b978 100644 --- a/src/components/playbackManager.ts +++ b/src/components/playbackManager.ts @@ -133,8 +133,8 @@ export class playbackManager { const maxBitrate = await getMaxBitrate(); const deviceProfile = getDeviceProfile({ - enableHls: true, - bitrateSetting: maxBitrate + bitrateSetting: maxBitrate, + enableHls: true }); const playbackInfo = await getPlaybackInfo( item, diff --git a/src/helpers.ts b/src/helpers.ts index 33d4939..962433b 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -39,21 +39,21 @@ export function getReportingParams($scope: GlobalScope): PlaybackProgressInfo { * those fields are always rounded. */ return { - PositionTicks: Math.round(getCurrentPositionTicks($scope)), + AudioStreamIndex: $scope.audioStreamIndex, + CanSeek: $scope.canSeek, + IsMuted: window.volume.muted, IsPaused: window.mediaManager.getPlayerState() === cast.framework.messages.PlayerState.PAUSED, - IsMuted: window.volume.muted, - AudioStreamIndex: $scope.audioStreamIndex, - SubtitleStreamIndex: $scope.subtitleStreamIndex, - VolumeLevel: Math.round(window.volume.level * 100), ItemId: $scope.itemId, - MediaSourceId: $scope.mediaSourceId, - CanSeek: $scope.canSeek, - PlayMethod: $scope.playMethod, LiveStreamId: $scope.liveStreamId, + MediaSourceId: $scope.mediaSourceId, + PlayMethod: $scope.playMethod, PlaySessionId: $scope.playSessionId, - RepeatMode: window.repeatMode + PositionTicks: Math.round(getCurrentPositionTicks($scope)), + RepeatMode: window.repeatMode, + SubtitleStreamIndex: $scope.subtitleStreamIndex, + VolumeLevel: Math.round(window.volume.level * 100) }; } @@ -96,8 +96,8 @@ export function getNextPlaybackItemInfo(): ItemIndex | null { const item = playlist[newIndex]; return { - item: item, - index: newIndex + index: newIndex, + item: item }; } @@ -451,17 +451,17 @@ export function createStreamInfo( const canSeek = (mediaSource.RunTimeTicks || 0) > 0; const info: any = { - url: mediaUrl, - mediaSource: mediaSource, - isStatic: isStatic, - contentType: contentType, - streamContainer: streamContainer, - canSeek: canSeek, - canClientSeek: isStatic || (canSeek && streamContainer == 'm3u8'), audioStreamIndex: mediaSource.DefaultAudioStreamIndex, - subtitleStreamIndex: mediaSource.DefaultSubtitleStreamIndex, + canClientSeek: isStatic || (canSeek && streamContainer == 'm3u8'), + canSeek: canSeek, + contentType: contentType, + isStatic: isStatic, + mediaSource: mediaSource, playerStartPositionTicks: playerStartPositionTicks, - startPositionTicks: startPosition + startPositionTicks: startPosition, + streamContainer: streamContainer, + subtitleStreamIndex: mediaSource.DefaultSubtitleStreamIndex, + url: mediaUrl }; const subtitleStreams = @@ -546,12 +546,12 @@ export function getShuffleItems( item: BaseItemDto ): Promise { const query: ItemQuery = { - UserId: userId, Fields: requiredItemFields, - Limit: 50, Filters: 'IsNotFolder', + Limit: 50, Recursive: true, - SortBy: 'Random' + SortBy: 'Random', + UserId: userId }; if (item.Type == 'MusicArtist') { @@ -582,9 +582,9 @@ export async function getInstantMixItems( item: BaseItemDto ): Promise { const query: any = { - UserId: userId, Fields: requiredItemFields, - Limit: 50 + Limit: 50, + UserId: userId }; let url: string | null = null; @@ -605,9 +605,9 @@ export async function getInstantMixItems( if (url) { return JellyfinApi.authAjax(url, { + dataType: 'json', query: query, - type: 'GET', - dataType: 'json' + type: 'GET' }); } else { throw new Error(`InstantMix: Unknown item type: ${item.Type}`); @@ -634,8 +634,8 @@ export async function getItemsForPlayback( const item = await JellyfinApi.authAjaxUser( `Items/${query.Ids.split(',')[0]}`, { - type: 'GET', - dataType: 'json' + dataType: 'json', + type: 'GET' } ); @@ -645,9 +645,9 @@ export async function getItemsForPlayback( }; } else { return JellyfinApi.authAjaxUser('Items', { + dataType: 'json', query: query, - type: 'GET', - dataType: 'json' + type: 'GET' }); } } @@ -670,9 +670,9 @@ export function getEpisodesForPlayback( query.ExcludeLocationTypes = 'Virtual'; return JellyfinApi.authAjax(`Shows/${seriesId}/Episodes`, { + dataType: 'json', query: query, - type: 'GET', - dataType: 'json' + type: 'GET' }); } @@ -730,25 +730,25 @@ export async function translateRequestedItems( return await getItemsForPlayback(userId, { ArtistIds: firstItem.Id, Filters: 'IsNotFolder', + MediaTypes: 'Audio', Recursive: true, - SortBy: 'SortName', - MediaTypes: 'Audio' + SortBy: 'SortName' }); } else if (firstItem.Type == 'MusicGenre') { return await getItemsForPlayback(userId, { - Genres: firstItem.Name ?? undefined, Filters: 'IsNotFolder', + Genres: firstItem.Name ?? undefined, + MediaTypes: 'Audio', Recursive: true, - SortBy: 'SortName', - MediaTypes: 'Audio' + SortBy: 'SortName' }); } else if (firstItem.IsFolder) { return await getItemsForPlayback(userId, { - ParentId: firstItem.Id, Filters: 'IsNotFolder', + MediaTypes: 'Audio,Video', + ParentId: firstItem.Id, Recursive: true, - SortBy: 'SortName', - MediaTypes: 'Audio,Video' + SortBy: 'SortName' }); } else if (smart && firstItem.Type == 'Episode' && items.length == 1) { const user = await getUser(); @@ -777,8 +777,8 @@ export async function translateRequestedItems( userId, episode.SeriesId, { - IsVirtualUnaired: false, IsMissing: false, + IsVirtualUnaired: false, UserId: userId } ); @@ -857,7 +857,7 @@ export function broadcastToMessageBus(message: BusMessage): void { * Inform the cast sender that we couldn't connect */ export function broadcastConnectionErrorMessage(): void { - broadcastToMessageBus({ type: 'connectionerror', message: '' }); + broadcastToMessageBus({ message: '', type: 'connectionerror' }); } /** diff --git a/stylelint.config.js b/stylelint.config.js index c14e2cc..73562f8 100644 --- a/stylelint.config.js +++ b/stylelint.config.js @@ -1,7 +1,7 @@ module.exports = { - syntax: 'css', extends: ['stylelint-config-standard', 'stylelint-config-prettier'], rules: { 'at-rule-no-unknown': null - } + }, + syntax: 'css' }; diff --git a/webpack.config.ts b/webpack.config.ts index 1014451..8da4200 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -12,22 +12,35 @@ import { version } from './package.json'; const common: webpack.Configuration = { context: path.resolve(__dirname, 'src'), entry: './app.ts', + module: { + rules: [ + { loader: 'html-loader', test: /\.html$/ }, + { + test: /\.(png|svg|jpg|gif)$/, + use: 'file-loader' + }, + { + loader: 'file-loader', + test: /\.(ttf|eot|woff(2)?)(\?[a-z0-9=&.]+)?$/ + }, + { test: /\.css$/i, use: ['style-loader', 'css-loader'] }, + { loader: 'ts-loader', test: /\.tsx?$/ }, + { loader: 'source-map-loader', test: /\.js$/ } + ] + }, output: { filename: '[name].[fullhash].js', path: path.resolve(__dirname, 'dist'), publicPath: './' }, - resolve: { - extensions: ['.ts', '.js'] - }, plugins: [ // @ts-expect-error - Typings mismatch between versions new CleanWebpackPlugin(), new HtmlWebpackPlugin({ + favicon: 'favicon.ico', filename: 'index.html', - template: 'index.html', hash: false, - favicon: 'favicon.ico' + template: 'index.html' }), new ImageMinimizerPlugin({ minimizerOptions: { @@ -46,36 +59,23 @@ const common: webpack.Configuration = { } }) ], - module: { - rules: [ - { test: /\.html$/, loader: 'html-loader' }, - { - test: /\.(png|svg|jpg|gif)$/, - use: 'file-loader' - }, - { - test: /\.(ttf|eot|woff(2)?)(\?[a-z0-9=&.]+)?$/, - loader: 'file-loader' - }, - { test: /\.css$/i, use: ['style-loader', 'css-loader'] }, - { test: /\.tsx?$/, loader: 'ts-loader' }, - { test: /\.js$/, loader: 'source-map-loader' } - ] + resolve: { + extensions: ['.ts', '.js'] } }; const development: webpack.Configuration = { - mode: 'development', - devtool: 'inline-source-map', // @ts-expect-error - Typings mismatch between versions devServer: { - contentBase: path.join(__dirname, 'dist'), compress: true, + contentBase: path.join(__dirname, 'dist'), port: process.env.RECEIVER_PORT ? Number.parseInt(process.env.RECEIVER_PORT, 10) : 9000, publicPath: '/' }, + devtool: 'inline-source-map', + mode: 'development', plugins: [ new DefinePlugin({ PRODUCTION: JSON.stringify(false),