diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 678e722aa8f..73100a57d4a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,7 +10,8 @@ "dbaeumer.vscode-eslint", "lokalise.i18n-ally", "ryanluker.vscode-coverage-gutters", - "yoavbls.pretty-ts-errors" + "yoavbls.pretty-ts-errors", + "SonarSource.sonarlint-vscode" ], "unwantedRecommendations": [ "octref.vetur", diff --git a/.vscode/settings.json b/.vscode/settings.json index 7514901342f..3d1709bfd5a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -38,5 +38,11 @@ "vue": "html" }, "vue.autoInsert.dotValue": true, - "vue.server.fullCompletionList": true + "vue.server.fullCompletionList": true, + "sonarlint.output.showAnalyzerLogs": true, + "sonarlint.output.showVerboseLogs": true, + "sonarlint.connectedMode.project": { + "connectionId": "jellyfin-vue", + "projectKey": "jellyfin_jellyfin-vue" + } } diff --git a/frontend/src/components/Buttons/MarkPlayedButton.vue b/frontend/src/components/Buttons/MarkPlayedButton.vue index 07ae682e7b5..a838ee13fc8 100644 --- a/frontend/src/components/Buttons/MarkPlayedButton.vue +++ b/frontend/src/components/Buttons/MarkPlayedButton.vue @@ -37,13 +37,13 @@ async function togglePlayed(): Promise { if (isPlayed.value) { isPlayed.value = false; await remote.sdk.newUserApi(getPlaystateApi).markUnplayedItem({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', itemId: props.item.Id }); } else { isPlayed.value = true; await remote.sdk.newUserApi(getPlaystateApi).markPlayedItem({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', itemId: props.item.Id }); } diff --git a/frontend/src/components/Buttons/SubtitleSelectionButton.vue b/frontend/src/components/Buttons/SubtitleSelectionButton.vue index 9ab508538b9..f4e19d45623 100644 --- a/frontend/src/components/Buttons/SubtitleSelectionButton.vue +++ b/frontend/src/components/Buttons/SubtitleSelectionButton.vue @@ -57,7 +57,7 @@ const tracks = computed(() => { srcIndex: -1, type: SubtitleDeliveryMethod.External }, - ...(subs || []) + ...(subs ?? []) ]; }); diff --git a/frontend/src/components/Item/MediaDetail/MediaDetailContent.vue b/frontend/src/components/Item/MediaDetail/MediaDetailContent.vue index 9ec332e0caf..b3823bea320 100644 --- a/frontend/src/components/Item/MediaDetail/MediaDetailContent.vue +++ b/frontend/src/components/Item/MediaDetail/MediaDetailContent.vue @@ -36,7 +36,7 @@ const properties = computed(() => { props.stream.Width && props.stream.Height ? `${props.stream.Width}x${props.stream.Height}` : undefined; - const framerate = props.stream.AverageFrameRate || props.stream.RealFrameRate; + const framerate = props.stream.AverageFrameRate ?? props.stream.RealFrameRate; const language = props.stream.Language ? getLocaleName(props.stream.Language, locale.value) : undefined; diff --git a/frontend/src/components/Item/Metadata/MetadataEditor.vue b/frontend/src/components/Item/Metadata/MetadataEditor.vue index 6996f81cadd..d80051001a3 100644 --- a/frontend/src/components/Item/Metadata/MetadataEditor.vue +++ b/frontend/src/components/Item/Metadata/MetadataEditor.vue @@ -340,7 +340,7 @@ async function getGenres(parentId: string): Promise { * Save metadata for the current item */ async function saveMetadata(): Promise { - if (!metadata.value || !metadata.value.Id) { + if (!metadata.value?.Id) { return; } diff --git a/frontend/src/components/Item/Metadata/RefreshMetadataDialog.vue b/frontend/src/components/Item/Metadata/RefreshMetadataDialog.vue index af94a5cd174..8ebb1ec4692 100644 --- a/frontend/src/components/Item/Metadata/RefreshMetadataDialog.vue +++ b/frontend/src/components/Item/Metadata/RefreshMetadataDialog.vue @@ -140,7 +140,7 @@ async function refreshMetadata(): Promise { taskManager.startTask({ type: TaskType.LibraryRefresh, id: props.item.Id || '', - data: props.item.Name || 'ID ' + props.item.Id, + data: props.item.Name ?? `ID ${props.item.Id}`, progress: 0 }); diff --git a/frontend/src/components/Layout/AppBar/SearchField.vue b/frontend/src/components/Layout/AppBar/SearchField.vue index 8bee0bfa644..86b6ed2573b 100644 --- a/frontend/src/components/Layout/AppBar/SearchField.vue +++ b/frontend/src/components/Layout/AppBar/SearchField.vue @@ -20,7 +20,7 @@ const router = useRouter(); const searchQuery = computed({ get(): string { - return route.query.q?.toString() || ''; + return route.query.q?.toString() ?? ''; }, async set(value) { await router.replace({ diff --git a/frontend/src/components/Layout/Carousel/Carousel.vue b/frontend/src/components/Layout/Carousel/Carousel.vue index 9d3682d969e..cda7c353a77 100644 --- a/frontend/src/components/Layout/Carousel/Carousel.vue +++ b/frontend/src/components/Layout/Carousel/Carousel.vue @@ -96,7 +96,7 @@ function setControlledSwiper (instance: SwiperType): void { * See https://github.com/nolimits4web/swiper/issues/2629 and https://github.com/surmon-china/vue-awesome-swiper/issues/483 */ function onSlideChange(): void { - currentIndex.value = swiperInstance.value?.realIndex || 0; + currentIndex.value = swiperInstance.value?.realIndex ?? 0; if (swiperInstance.value?.isBeginning || swiperInstance.value?.isEnd) { swiperInstance.value?.updateSlides(); diff --git a/frontend/src/components/Layout/Carousel/Item/ItemsCarousel.vue b/frontend/src/components/Layout/Carousel/Item/ItemsCarousel.vue index 9668b51e5a4..86e4bfdda3a 100644 --- a/frontend/src/components/Layout/Carousel/Item/ItemsCarousel.vue +++ b/frontend/src/components/Layout/Carousel/Item/ItemsCarousel.vue @@ -134,7 +134,7 @@ watch( const itemData = ( await remote.sdk.newUserApi(getUserLibraryApi).getItem({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', itemId: id }) ).data; diff --git a/frontend/src/components/Layout/Navigation/NavigationDrawer.vue b/frontend/src/components/Layout/Navigation/NavigationDrawer.vue index c7e2564f082..a6bb49e9582 100644 --- a/frontend/src/components/Layout/Navigation/NavigationDrawer.vue +++ b/frontend/src/components/Layout/Navigation/NavigationDrawer.vue @@ -55,7 +55,7 @@ const { t } = useI18n(); const drawer = inject>('NavigationDrawer'); const transparentLayout = computed(() => { - return route.meta.transparentLayout || false; + return route.meta.transparentLayout ?? false; }); const drawerItems = computed(() => { @@ -63,7 +63,7 @@ const drawerItems = computed(() => { if (view.Id) { return { icon: getLibraryIcon(view.CollectionType), - title: view.Name || '', + title: view.Name ?? '', to: `/library/${view.Id}` }; } diff --git a/frontend/src/components/Layout/VirtualGrid/VirtualGrid.vue b/frontend/src/components/Layout/VirtualGrid/VirtualGrid.vue index 034fb192df0..0bdd657fe08 100644 --- a/frontend/src/components/Layout/VirtualGrid/VirtualGrid.vue +++ b/frontend/src/components/Layout/VirtualGrid/VirtualGrid.vue @@ -136,7 +136,7 @@ const contentSize = computed(() => { }); const rootStyles = computed(() => Object.fromEntries([ - ...Object.entries(contentSize.value || {}).map(([property, value]) => [ + ...Object.entries(contentSize.value ?? {}).map(([property, value]) => [ property, `${value}px` ]), diff --git a/frontend/src/components/Layout/VirtualGrid/pipeline.ts b/frontend/src/components/Layout/VirtualGrid/pipeline.ts index 620e505fbd0..dccb5d93de3 100644 --- a/frontend/src/components/Layout/VirtualGrid/pipeline.ts +++ b/frontend/src/components/Layout/VirtualGrid/pipeline.ts @@ -93,14 +93,14 @@ export function getScrollParents( /** * Parent.assignedSlot.parentElement find the correct parent if the grid is inside a native web component */ - parent = parent.assignedSlot?.parentElement || parent.parentElement; + parent = parent.assignedSlot?.parentElement ?? parent.parentElement; } - const fallback = document.scrollingElement || document.documentElement; + const fallback = document.scrollingElement ?? document.documentElement; return { - vertical: vertical || fallback, - horizontal: horizontal || fallback + vertical: vertical ?? fallback, + horizontal: horizontal ?? fallback }; } diff --git a/frontend/src/components/Playback/DraggableQueue.vue b/frontend/src/components/Playback/DraggableQueue.vue index 3b373f498ae..86322e24ef4 100644 --- a/frontend/src/components/Playback/DraggableQueue.vue +++ b/frontend/src/components/Playback/DraggableQueue.vue @@ -69,7 +69,7 @@ onMounted(() => { if (typeof oldIndex === 'number') { const item = playbackManager.queue[oldIndex]; - if (item && item.Id && typeof e.newIndex === 'number') { + if (item?.Id && typeof e.newIndex === 'number') { playbackManager.changeItemPosition(item.Id, e.newIndex); } } diff --git a/frontend/src/components/Playback/PlayerElement.vue b/frontend/src/components/Playback/PlayerElement.vue index 09cc7e9a2d0..55bef95c842 100644 --- a/frontend/src/components/Playback/PlayerElement.vue +++ b/frontend/src/components/Playback/PlayerElement.vue @@ -7,7 +7,7 @@ :is="mediaElementType" v-show="mediaElementType === 'video' && teleportTarget" ref="mediaElementRef" - :poster="posterUrl" + :poster="String(posterUrl)" autoplay crossorigin="anonymous" playsinline @@ -99,13 +99,13 @@ const teleportTarget = computed< } }); -const posterUrl = computed(() => +const posterUrl = computed(() => !isNil(playbackManager.currentItem) && playbackManager.currentlyPlayingMediaType === 'Video' ? getImageInfo(playbackManager.currentItem, { preferBackdrop: true - }).url || '' - : '' + }).url + : undefined ); /** diff --git a/frontend/src/components/Playback/TrackList.vue b/frontend/src/components/Playback/TrackList.vue index 00aac5e003e..2c49e3116df 100644 --- a/frontend/src/components/Playback/TrackList.vue +++ b/frontend/src/components/Playback/TrackList.vue @@ -141,7 +141,7 @@ const tracks = ref(); async function fetch(): Promise { tracks.value = ( await remote.sdk.newUserApi(getItemsApi).getItems({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', parentId: props.item.Id, sortBy: ['SortName'], sortOrder: [SortOrder.Ascending], diff --git a/frontend/src/pages/genre/_itemId/index.vue b/frontend/src/pages/genre/_itemId/index.vue index ff59a74433c..957a22e2303 100644 --- a/frontend/src/pages/genre/_itemId/index.vue +++ b/frontend/src/pages/genre/_itemId/index.vue @@ -85,15 +85,11 @@ const genres = ref([]); onMounted(async () => { const { itemId } = route.params as { itemId: string }; - const typesQuery = route.query.type; - - const includeItemTypes = ( - typesQuery == undefined - ? [] - : (typeof typesQuery === 'string' - ? [typesQuery] - : typesQuery) - ) as BaseItemKind[]; + const typesQuery = route.query.type as BaseItemKind ?? []; + + const includeItemTypes: BaseItemKind[] = typeof typesQuery === 'string' + ? [typesQuery] + : typesQuery; loading.value = true; diff --git a/frontend/src/pages/index.vue b/frontend/src/pages/index.vue index 2b79c6acee4..8cc2676fef3 100644 --- a/frontend/src/pages/index.vue +++ b/frontend/src/pages/index.vue @@ -96,7 +96,7 @@ const homeSections = computed(() => { latestMediaSections.push({ name: 'latestLibrary', libraryName: userView.Name, - libraryId: userView.Id || '', + libraryId: userView.Id ?? '', shape: getShapeFromCollectionType(userView.CollectionType), type: 'latestmedia' }); diff --git a/frontend/src/pages/playback/music/index.vue b/frontend/src/pages/playback/music/index.vue index bd0981d750c..d82e6e203e6 100644 --- a/frontend/src/pages/playback/music/index.vue +++ b/frontend/src/pages/playback/music/index.vue @@ -166,7 +166,7 @@ watch( () => { if (swiperInstance.value && !isNil(playbackManager.currentItemIndex)) { swiperInstance.value.slideTo(playbackManager.currentItemIndex); - route.meta.title = playbackManager.currentItem?.Name || ''; + route.meta.title = playbackManager.currentItem?.Name ?? ''; } }, { immediate: true } @@ -183,7 +183,7 @@ watch(isVisualizing, async () => { * Handle slide changes */ function onSlideChange(): void { - const index = swiperInstance.value?.activeIndex || 0; + const index = swiperInstance.value?.activeIndex ?? 0; playbackManager.currentItemIndex = index; } diff --git a/frontend/src/pages/search.vue b/frontend/src/pages/search.vue index 93ee8c474ac..de8ededae2b 100644 --- a/frontend/src/pages/search.vue +++ b/frontend/src/pages/search.vue @@ -96,7 +96,7 @@ const memo = reactive( ]) ); -const searchQuery = computed(() => route.query?.q?.toString() || ''); +const searchQuery = computed(() => route.query?.q?.toString() ?? ''); const items = computed(() => memo.get(searchQuery.value)?.items ?? []); const people = computed(() => memo.get(searchQuery.value)?.people ?? []); const movies = computed(() => @@ -123,7 +123,7 @@ const performDebouncedSearch = useDebounceFn(async (searchTerm: string) => { const itemSearch = ( await remote.sdk.newUserApi(getItemsApi).getItemsByUserId({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', searchTerm, includeItemTypes: [ BaseItemKind.Movie, diff --git a/frontend/src/pages/server/login.vue b/frontend/src/pages/server/login.vue index 69ea8cc39fe..430c26f1c6c 100644 --- a/frontend/src/pages/server/login.vue +++ b/frontend/src/pages/server/login.vue @@ -108,7 +108,7 @@ const route = useRoute(); const router = useRouter(); const remote = useRemote(); const api = remote.sdk.oneTimeSetup( - remote.auth.currentServer?.PublicAddress || '' + remote.auth.currentServer?.PublicAddress ?? '' ); route.meta.title = t('login.login'); diff --git a/frontend/src/pages/wizard.vue b/frontend/src/pages/wizard.vue index cbd6eae33ed..bf6ea9a5b68 100644 --- a/frontend/src/pages/wizard.vue +++ b/frontend/src/pages/wizard.vue @@ -125,7 +125,7 @@ const heading = computed(() => { async function completeWizard(): Promise { try { const api = remote.sdk.oneTimeSetup( - remote.auth.currentServer?.PublicAddress || '' + remote.auth.currentServer?.PublicAddress ?? '' ); await getStartupApi(api).completeWizard(); diff --git a/frontend/src/store/clientSettings.ts b/frontend/src/store/clientSettings.ts index a80d29ea8ef..f31e6975063 100644 --- a/frontend/src/store/clientSettings.ts +++ b/frontend/src/store/clientSettings.ts @@ -25,7 +25,7 @@ interface ClientSettingsState { */ const navigatorLanguage = useNavigatorLanguage(); const BROWSER_LANGUAGE = computed(() => { - const rawString = navigatorLanguage.language.value || ''; + const rawString = navigatorLanguage.language.value ?? ''; /** * Removes the culture info from the language string, so 'es-ES' is recognised as 'es' */ diff --git a/frontend/src/store/playbackManager.ts b/frontend/src/store/playbackManager.ts index 7ad55275a56..390ff06cc07 100644 --- a/frontend/src/store/playbackManager.ts +++ b/frontend/src/store/playbackManager.ts @@ -303,7 +303,7 @@ class PlaybackManagerStore { srcLang: sub.Language ?? undefined, type: sub.DeliveryMethod ?? SubtitleDeliveryMethod.Drop, srcIndex: sub.srcIndex, - codec: sub.Codec || undefined + codec: sub.Codec })); } } @@ -466,7 +466,7 @@ class PlaybackManagerStore { public set currentVolume(newVolume: number) { newVolume = newVolume > 100 ? 100 : newVolume; newVolume = newVolume < 0 ? 0 : newVolume; - this.isMuted = newVolume === 0 ? true : false; + this.isMuted = newVolume === 0; if (this._state.isRemotePlayer) { this._state.remoteCurrentVolume = newVolume; @@ -747,9 +747,9 @@ class PlaybackManagerStore { }; public stop = (): void => { - const sessionId = String(this._state.playSessionId || ''); + const sessionId = String(this._state.playSessionId ?? ''); const time = Number(this.currentTime); - const itemId = String(this.currentItem?.Id || ''); + const itemId = String(this.currentItem?.Id ?? ''); const volume = Number(this.currentVolume); Object.assign(this._state, this._defaultState); @@ -860,7 +860,7 @@ class PlaybackManagerStore { if (item) { return ( await remote.sdk.newUserApi(getMediaInfoApi).getPostedPlaybackInfo({ - itemId: item?.Id || '', + itemId: item?.Id ?? '', userId: remote.auth.currentUserId, autoOpenLiveStream: true, playbackInfoDto: { DeviceProfile: playbackProfile }, @@ -965,8 +965,8 @@ class PlaybackManagerStore { mediaSourceId: mediaSource.Id, deviceId: remote.sdk.deviceInfo.id, api_key: remote.auth.currentUserToken, - Tag: mediaSource.ETag || '', - LiveStreamId: mediaSource.LiveStreamId || '' + Tag: mediaSource.ETag ?? '', + LiveStreamId: mediaSource.LiveStreamId ?? '' }; const parameters = new URLSearchParams(directOptions).toString(); @@ -1063,42 +1063,42 @@ class PlaybackManagerStore { src: getImageInfo(this.currentItem, { width: 96 - }).url || '', + }).url ?? '', sizes: '96x96' }, { src: getImageInfo(this.currentItem, { width: 128 - }).url || '', + }).url ?? '', sizes: '128x128' }, { src: getImageInfo(this.currentItem, { width: 192 - }).url || '', + }).url ?? '', sizes: '192x192' }, { src: getImageInfo(this.currentItem, { width: 256 - }).url || '', + }).url ?? '', sizes: '256x256' }, { src: getImageInfo(this.currentItem, { width: 384 - }).url || '', + }).url ?? '', sizes: '384x384' }, { src: getImageInfo(this.currentItem, { width: 512 - }).url || '', + }).url ?? '', sizes: '512x512' } ] diff --git a/frontend/src/store/playerElement.ts b/frontend/src/store/playerElement.ts index dce87d65403..e1a1ec54e01 100644 --- a/frontend/src/store/playerElement.ts +++ b/frontend/src/store/playerElement.ts @@ -192,7 +192,7 @@ class PlayerElementStore { * If VTT found, applying it */ mediaElementRef.value.textTracks[vttIdx].mode = 'showing'; - } else if (ass !== undefined && ass.src) { + } else if (ass?.src) { /** * If SSA, using Subtitle Opctopus */ diff --git a/frontend/src/store/taskManager.ts b/frontend/src/store/taskManager.ts index 485b2713a45..2e42e092fc4 100644 --- a/frontend/src/store/taskManager.ts +++ b/frontend/src/store/taskManager.ts @@ -158,12 +158,10 @@ class TaskManagerStore { progress }); } - } else { - if (progress >= 0 && progress < 100) { - taskPayload.progress = progress; - } else if (progress >= 0) { - this.finishTask(data.ItemId); - } + } else if (progress >= 0 && progress < 100) { + taskPayload.progress = progress; + } else if (progress >= 0) { + this.finishTask(data.ItemId); } } }; diff --git a/frontend/src/store/userLibraries.ts b/frontend/src/store/userLibraries.ts index cedc588d339..787ceeb3591 100644 --- a/frontend/src/store/userLibraries.ts +++ b/frontend/src/store/userLibraries.ts @@ -130,7 +130,7 @@ class UserLibrariesStore { const userViewsResponse = await remote.sdk .newUserApi(getUserViewsApi) .getUserViews({ - userId: remote.auth.currentUserId || '' + userId: remote.auth.currentUserId ?? '' }); this._state.value.views = userViewsResponse.data.Items ?? []; @@ -145,7 +145,7 @@ class UserLibrariesStore { try { const audioResumes = ( await remote.sdk.newUserApi(getItemsApi).getResumeItems({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', fields: [ ItemFields.PrimaryImageAspectRatio, ItemFields.MediaSources, @@ -174,7 +174,7 @@ class UserLibrariesStore { try { const videoResumes = ( await remote.sdk.newUserApi(getItemsApi).getResumeItems({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', fields: [ ItemFields.PrimaryImageAspectRatio, ItemFields.MediaSources, @@ -234,7 +234,7 @@ class UserLibrariesStore { try { const latestMedia = ( await remote.sdk.newUserApi(getUserLibraryApi).getLatestMedia({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', fields: [ ItemFields.PrimaryImageAspectRatio, ItemFields.MediaSources, @@ -262,7 +262,7 @@ class UserLibrariesStore { try { const carouselItems = ( await remote.sdk.newUserApi(getUserLibraryApi).getLatestMedia({ - userId: remote.auth.currentUserId || '', + userId: remote.auth.currentUserId ?? '', fields: [ ItemFields.Overview, ItemFields.PrimaryImageAspectRatio, diff --git a/frontend/src/utils/browser-detection.ts b/frontend/src/utils/browser-detection.ts index d6e0007d7ab..96c0f315a4c 100644 --- a/frontend/src/utils/browser-detection.ts +++ b/frontend/src/utils/browser-detection.ts @@ -116,7 +116,7 @@ export function safariVersion(): number | undefined { * This works for iOS Safari and desktop Safari, which contain something * like "Version/13.0" indicating the major Safari or iOS version. */ - let match = userAgent.match(/Version\/(\d+)/); + let match = /Version\/(\d+)/.exec(userAgent); if (match) { return Number.parseInt(match[1], /* Base= */ 10); @@ -126,7 +126,7 @@ export function safariVersion(): number | undefined { * This works for all other browsers on iOS, which contain something like * "OS 13_3" indicating the major & minor iOS version. */ - match = userAgent.match(/OS (\d+)(?:_\d+)?/); + match = /OS (\d+)(?:_\d+)?/.exec(userAgent); if (match) { return Number.parseInt(match[1], /* Base= */ 10); diff --git a/frontend/src/utils/images.ts b/frontend/src/utils/images.ts index ac3534565f8..7746a3c4175 100644 --- a/frontend/src/utils/images.ts +++ b/frontend/src/utils/images.ts @@ -245,7 +245,7 @@ export function getImageInfo( } else if (isPerson(item)) { imgType = ImageType.Primary; imgTag = item.PrimaryImageTag; - } else if (preferThumb && item.ImageTags && item.ImageTags.Thumb) { + } else if (preferThumb && item.ImageTags?.Thumb) { imgType = ImageType.Thumb; imgTag = item.ImageTags.Thumb; } else if ( @@ -255,7 +255,7 @@ export function getImageInfo( ) { imgType = ImageType.Banner; imgTag = item.ImageTags.Banner; - } else if (preferLogo && item.ImageTags && item.ImageTags.Logo) { + } else if (preferLogo && item.ImageTags?.Logo) { imgType = ImageType.Logo; imgTag = item.ImageTags.Logo; } else if (preferBackdrop && item.BackdropImageTags?.[0]) { @@ -303,11 +303,7 @@ export function getImageInfo( imgType = ImageType.Backdrop; imgTag = item.ParentBackdropImageTags[0]; itemId = item.ParentBackdropItemId; - } else if ( - item.ImageTags && - item.ImageTags.Primary && - (item.Type !== BaseItemKind.Episode || item.ChildCount !== 0) - ) { + } else if (item.ImageTags?.Primary && (item.Type !== BaseItemKind.Episode || item.ChildCount !== 0)) { imgType = ImageType.Primary; imgTag = item.ImageTags.Primary; height = @@ -332,15 +328,14 @@ export function getImageInfo( : undefined; } else if ( item.Type === BaseItemKind.Season && - item.ImageTags && - item.ImageTags.Thumb + item.ImageTags?.Thumb ) { imgType = ImageType.Thumb; imgTag = item.ImageTags.Thumb; } else if (item.BackdropImageTags && item.BackdropImageTags.length > 0) { imgType = ImageType.Backdrop; imgTag = item.BackdropImageTags[0]; - } else if (item.ImageTags && item.ImageTags.Thumb) { + } else if (item.ImageTags?.Thumb) { imgType = ImageType.Thumb; imgTag = item.ImageTags.Thumb; } else if (item.SeriesThumbImageTag && inheritThumb !== false) { @@ -435,7 +430,7 @@ export function getLogo( if (tag) { imgType = ImageType.Logo; imgTag = tag; - } else if (item.ImageTags && item.ImageTags.Logo) { + } else if (item.ImageTags?.Logo) { imgType = ImageType.Logo; imgTag = item.ImageTags.Logo; } else if (item.ParentLogoImageTag && item.ParentLogoItemId) { @@ -447,7 +442,7 @@ export function getLogo( if (imgTag && imgType && itemId) { const remote = useRemote(); - url = new URL(remote.sdk.api?.getItemImageUrl(itemId, imgType) || ''); + url = new URL(remote.sdk.api?.getItemImageUrl(itemId, imgType) ?? ''); const parameters: Record = { imgTag, diff --git a/frontend/src/utils/items.ts b/frontend/src/utils/items.ts index 8c5a18d1279..7dfd08865a7 100644 --- a/frontend/src/utils/items.ts +++ b/frontend/src/utils/items.ts @@ -255,10 +255,7 @@ export function canPlay(item: BaseItemDto | undefined): boolean { * Check if an item can be resumed */ export function canResume(item: BaseItemDto): boolean { - return item?.UserData?.PlaybackPositionTicks && - item.UserData.PlaybackPositionTicks > 0 - ? true - : false; + return Boolean(item?.UserData?.PlaybackPositionTicks && item.UserData.PlaybackPositionTicks > 0); } /** * Determine if an item can be mark as played diff --git a/frontend/src/utils/playback-profiles/helpers/codec-profiles.ts b/frontend/src/utils/playback-profiles/helpers/codec-profiles.ts index 0e4debcba9a..5bf0661351b 100644 --- a/frontend/src/utils/playback-profiles/helpers/codec-profiles.ts +++ b/frontend/src/utils/playback-profiles/helpers/codec-profiles.ts @@ -300,7 +300,7 @@ export function getCodecProfiles( ); } - const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString(); + const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() ?? '').toString(); if (globalMaxVideoBitrate) { h264CodecProfileConditions.push( diff --git a/frontend/src/utils/playback-profiles/helpers/mp4-video-formats.ts b/frontend/src/utils/playback-profiles/helpers/mp4-video-formats.ts index b0ca319a256..8dc440d218c 100644 --- a/frontend/src/utils/playback-profiles/helpers/mp4-video-formats.ts +++ b/frontend/src/utils/playback-profiles/helpers/mp4-video-formats.ts @@ -18,9 +18,8 @@ import { */ export function hasH264Support(videoTestElement: HTMLVideoElement): boolean { return !!( - videoTestElement.canPlayType && videoTestElement - .canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') + .canPlayType?.('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') .replace(/no/, '') ); } @@ -96,9 +95,8 @@ export function hasAv1Support(videoTestElement: HTMLVideoElement): boolean { } return !!( - videoTestElement.canPlayType && videoTestElement - .canPlayType('video/webm; codecs="av01.0.15M.10"') + .canPlayType?.('video/webm; codecs="av01.0.15M.10"') .replace(/no/, '') ); } @@ -124,8 +122,7 @@ function hasVc1Support(videoTestElement: HTMLVideoElement): boolean { */ export function hasVp8Support(videoTestElement: HTMLVideoElement): boolean { return !!( - videoTestElement.canPlayType && - videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, '') + videoTestElement.canPlayType?.('video/webm; codecs="vp8"').replace(/no/, '') ); } @@ -137,8 +134,7 @@ export function hasVp8Support(videoTestElement: HTMLVideoElement): boolean { */ export function hasVp9Support(videoTestElement: HTMLVideoElement): boolean { return !!( - videoTestElement.canPlayType && - videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, '') + videoTestElement.canPlayType?.('video/webm; codecs="vp9"').replace(/no/, '') ); } diff --git a/frontend/src/utils/store-sync.ts b/frontend/src/utils/store-sync.ts index 3060b59f3d2..d717ff9a4e7 100644 --- a/frontend/src/utils/store-sync.ts +++ b/frontend/src/utils/store-sync.ts @@ -63,22 +63,20 @@ export async function updateDisplayPreferences( displayPreferencesId ); - const newDisplayPreferences = Object.assign( - {}, - currentDisplayPreferences, - displayPreferences - ); + const newDisplayPreferences = { + ...currentDisplayPreferences, + ...displayPreferences + }; // If either old or new preferences have custom settings, merge them if ( currentDisplayPreferences.CustomPrefs !== undefined || newDisplayPreferences.CustomPrefs !== undefined ) { - const mergedCustomPrefs = Object.assign( - {}, - currentDisplayPreferences.CustomPrefs ?? {}, - displayPreferences.CustomPrefs ?? {} - ); + const mergedCustomPrefs = { + ...currentDisplayPreferences.CustomPrefs, + ...displayPreferences.CustomPrefs + }; newDisplayPreferences.CustomPrefs = Object.fromEntries( Object.entries(mergedCustomPrefs)