1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Refactor item text lines

This commit is contained in:
Bill Thornton 2025-01-14 13:42:17 -05:00
parent 68c4a02500
commit fe22de4f89
6 changed files with 167 additions and 103 deletions

View file

@ -0,0 +1,102 @@
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { describe, expect, it } from 'vitest';
import type { ItemDto } from 'types/base/models/item-dto';
import { getItemTextLines } from './itemText';
describe('getItemTextLines', () => {
it('Should return undefined if item is invalid', () => {
let lines = getItemTextLines({});
expect(lines).toBeUndefined();
lines = getItemTextLines(null);
expect(lines).toBeUndefined();
lines = getItemTextLines(undefined);
expect(lines).toBeUndefined();
});
it('Should return the name and index number', () => {
const item: ItemDto = {
Name: 'Item Name'
};
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(1);
expect(lines?.[0]).toBe(item.Name);
item.MediaType = MediaType.Video;
item.IndexNumber = 5;
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(1);
expect(lines?.[0]).toBe(`${item.IndexNumber} - ${item.Name}`);
item.ParentIndexNumber = 2;
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(1);
expect(lines?.[0]).toBe(`${item.ParentIndexNumber}.${item.IndexNumber} - ${item.Name}`);
});
it('Should add artist names', () => {
let item: ItemDto = {
Name: 'Item Name',
ArtistItems: [
{ Name: 'Artist 1' },
{ Name: 'Artist 2' }
]
};
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
expect(lines?.[0]).toBe(item.Name);
expect(lines?.[1]).toBe('Artist 1, Artist 2');
item = {
Name: 'Item Name',
Artists: [
'Artist 1',
'Artist 2'
]
};
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
expect(lines?.[0]).toBe(item.Name);
expect(lines?.[1]).toBe('Artist 1, Artist 2');
});
it('Should add album or series name', () => {
let item: ItemDto = {
Name: 'Item Name',
SeriesName: 'Series'
};
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
expect(lines?.[0]).toBe(item.SeriesName);
expect(lines?.[1]).toBe(item.Name);
item = {
Name: 'Item Name',
Album: 'Album'
};
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
expect(lines?.[0]).toBe(item.Album);
expect(lines?.[1]).toBe(item.Name);
});
it('Should add production year', () => {
const item = {
Name: 'Item Name',
ProductionYear: 2025
};
const lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
expect(lines?.[0]).toBe(item.Name);
expect(lines?.[1]).toBe(String(item.ProductionYear));
});
});

View file

@ -0,0 +1,44 @@
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import type { ItemDto } from 'types/base/models/item-dto';
/**
* Gets lines of text used to describe an item for display.
* @param nowPlayingItem The item to describe
* @param isYearIncluded Should the production year be included
* @returns The list of strings describing the item for display
*/
export function getItemTextLines(
nowPlayingItem: ItemDto | null | undefined,
isYearIncluded = true
) {
let line1 = nowPlayingItem?.Name;
if (nowPlayingItem?.MediaType === MediaType.Video) {
if (nowPlayingItem.IndexNumber != null) {
line1 = nowPlayingItem.IndexNumber + ' - ' + line1;
}
if (nowPlayingItem.ParentIndexNumber != null) {
line1 = nowPlayingItem.ParentIndexNumber + '.' + line1;
}
}
let line2: string | null | undefined;
if (nowPlayingItem?.ArtistItems?.length) {
line2 = nowPlayingItem.ArtistItems.map(a => a.Name).join(', ');
} else if (nowPlayingItem?.Artists?.length) {
line2 = nowPlayingItem.Artists.join(', ');
} else if (nowPlayingItem?.SeriesName || nowPlayingItem?.Album) {
line2 = line1;
line1 = nowPlayingItem.SeriesName || nowPlayingItem.Album;
} else if (nowPlayingItem?.ProductionYear && isYearIncluded) {
line2 = String(nowPlayingItem.ProductionYear);
}
if (!line1) return;
const lines = [ line1 ];
if (line2) lines.push(line2);
return lines;
}

View file

@ -1,8 +1,8 @@
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type'; import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { getImageUrl } from 'apps/stable/features/playback/utils/image'; import { getImageUrl } from 'apps/stable/features/playback/utils/image';
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
import { PlaybackSubscriber } from 'apps/stable/features/playback/utils/playbackSubscriber'; import { PlaybackSubscriber } from 'apps/stable/features/playback/utils/playbackSubscriber';
import { getNowPlayingNames } from 'components/playback/nowplayinghelper';
import type { PlaybackManager } from 'components/playback/playbackmanager'; import type { PlaybackManager } from 'components/playback/playbackmanager';
import { MILLISECONDS_PER_SECOND, TICKS_PER_MILLISECOND } from 'constants/time'; import { MILLISECONDS_PER_SECOND, TICKS_PER_MILLISECOND } from 'constants/time';
import browser from 'scripts/browser'; import browser from 'scripts/browser';
@ -110,11 +110,11 @@ class MediaSessionSubscriber extends PlaybackSubscriber {
} }
const album = item.Album || undefined; const album = item.Album || undefined;
const [ line1, line2 ] = getNowPlayingNames(item, false) || []; const [ line1, line2 ] = getItemTextLines(item, false) || [];
// The artist will be the second line if present or the first line otherwise // The artist will be the second line if present or the first line otherwise
const artist = (line2 || line1)?.text; const artist = line2 || line1;
// The title will be the first line if there are two lines // The title will be the first line if there are two lines
const title = (line2 && line1)?.text; const title = line2 && line1;
if (hasNavigatorSession) { if (hasNavigatorSession) {
if ( if (

View file

@ -1,11 +1,13 @@
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
import { appRouter, isLyricsPage } from 'components/router/appRouter'; import { appRouter, isLyricsPage } from 'components/router/appRouter';
import datetime from '../../scripts/datetime'; import datetime from '../../scripts/datetime';
import Events from '../../utils/events.ts'; import Events from '../../utils/events.ts';
import browser from '../../scripts/browser'; import browser from '../../scripts/browser';
import imageLoader from '../images/imageLoader'; import imageLoader from '../images/imageLoader';
import layoutManager from '../layoutManager'; import layoutManager from '../layoutManager';
import { playbackManager } from '../playback/playbackmanager'; import { playbackManager } from '../playback/playbackmanager';
import nowPlayingHelper from '../playback/nowplayinghelper';
import { appHost } from '../apphost'; import { appHost } from '../apphost';
import dom from '../../scripts/dom'; import dom from '../../scripts/dom';
import globalize from 'lib/globalize'; import globalize from 'lib/globalize';
@ -17,7 +19,6 @@ import appFooter from '../appFooter/appFooter';
import itemShortcuts from '../shortcuts'; import itemShortcuts from '../shortcuts';
import './nowPlayingBar.scss'; import './nowPlayingBar.scss';
import '../../elements/emby-slider/emby-slider'; import '../../elements/emby-slider/emby-slider';
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
let currentPlayer; let currentPlayer;
let currentPlayerSupportedCommands = []; let currentPlayerSupportedCommands = [];
@ -474,24 +475,21 @@ function setLyricButtonActiveStatus() {
function updateNowPlayingInfo(state) { function updateNowPlayingInfo(state) {
const nowPlayingItem = state.NowPlayingItem; const nowPlayingItem = state.NowPlayingItem;
const textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; const textLines = nowPlayingItem ? getItemTextLines(nowPlayingItem) : undefined;
nowPlayingTextElement.innerHTML = ''; nowPlayingTextElement.innerHTML = '';
if (textLines) { if (textLines) {
const itemText = document.createElement('div'); const itemText = document.createElement('div');
const secondaryText = document.createElement('div'); const secondaryText = document.createElement('div');
secondaryText.classList.add('nowPlayingBarSecondaryText'); secondaryText.classList.add('nowPlayingBarSecondaryText');
if (textLines.length > 1) { if (textLines.length > 1 && textLines[1]) {
textLines[1].secondary = true; const text = document.createElement('a');
if (textLines[1].text) { text.innerText = textLines[1];
const text = document.createElement('a'); secondaryText.appendChild(text);
text.innerText = textLines[1].text;
secondaryText.appendChild(text);
}
} }
if (textLines[0].text) { if (textLines[0]) {
const text = document.createElement('a'); const text = document.createElement('a');
text.innerText = textLines[0].text; text.innerText = textLines[0];
itemText.appendChild(text); itemText.appendChild(text);
} }
nowPlayingTextElement.appendChild(itemText); nowPlayingTextElement.appendChild(itemText);

View file

@ -1,78 +0,0 @@
export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) {
let topItem = nowPlayingItem;
let bottomItem = null;
let topText = nowPlayingItem.Name;
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') {
topItem = {
Id: nowPlayingItem.AlbumId,
Name: nowPlayingItem.Album,
Type: 'MusicAlbum',
IsFolder: true
};
}
if (nowPlayingItem.MediaType === 'Video') {
if (nowPlayingItem.IndexNumber != null) {
topText = nowPlayingItem.IndexNumber + ' - ' + topText;
}
if (nowPlayingItem.ParentIndexNumber != null) {
topText = nowPlayingItem.ParentIndexNumber + '.' + topText;
}
}
let bottomText = '';
if (nowPlayingItem.ArtistItems?.length) {
bottomItem = {
Id: nowPlayingItem.ArtistItems[0].Id,
Name: nowPlayingItem.ArtistItems[0].Name,
Type: 'MusicArtist',
IsFolder: true
};
bottomText = nowPlayingItem.ArtistItems.map(function (a) {
return a.Name;
}).join(', ');
} else if (nowPlayingItem.Artists?.length) {
bottomText = nowPlayingItem.Artists.join(', ');
} else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
bottomText = topText;
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
bottomItem = topItem;
if (nowPlayingItem.SeriesId) {
topItem = {
Id: nowPlayingItem.SeriesId,
Name: nowPlayingItem.SeriesName,
Type: 'Series',
IsFolder: true
};
} else {
topItem = null;
}
} else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) {
bottomText = nowPlayingItem.ProductionYear;
}
const list = [];
list.push({
text: topText,
item: topItem
});
if (bottomText) {
list.push({
text: bottomText,
item: bottomItem
});
}
return list;
}
export default {
getNowPlayingNames: getNowPlayingNames
};

View file

@ -1,10 +1,13 @@
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
import datetime from '../../scripts/datetime'; import datetime from '../../scripts/datetime';
import { clearBackdrop, setBackdrops } from '../backdrop/backdrop'; import { clearBackdrop, setBackdrops } from '../backdrop/backdrop';
import listView from '../listview/listview'; import listView from '../listview/listview';
import imageLoader from '../images/imageLoader'; import imageLoader from '../images/imageLoader';
import { playbackManager } from '../playback/playbackmanager'; import { playbackManager } from '../playback/playbackmanager';
import nowPlayingHelper from '../playback/nowplayinghelper';
import Events from '../../utils/events.ts'; import Events from '../../utils/events.ts';
import { appHost } from '../apphost'; import { appHost } from '../apphost';
import globalize from '../../lib/globalize'; import globalize from '../../lib/globalize';
@ -22,7 +25,6 @@ import ServerConnections from '../ServerConnections';
import toast from '../toast/toast'; import toast from '../toast/toast';
import { appRouter } from '../router/appRouter'; import { appRouter } from '../router/appRouter';
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils'; import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
let showMuteButton = true; let showMuteButton = true;
let showVolumeSlider = true; let showVolumeSlider = true;
@ -86,15 +88,11 @@ function showSubtitleMenu(context, player, button) {
}); });
} }
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
return nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo).map(function (i) {
return escapeHtml(i.text);
}).join('<br/>');
}
function updateNowPlayingInfo(context, state, serverId) { function updateNowPlayingInfo(context, state, serverId) {
const item = state.NowPlayingItem; const item = state.NowPlayingItem;
const displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : ''; const displayName = item ?
getItemTextLines(item).map(escapeHtml).join(' - ') :
'';
if (item) { if (item) {
const nowPlayingServerId = (item.ServerId || serverId); const nowPlayingServerId = (item.ServerId || serverId);
if (item.Type == 'AudioBook' || item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') { if (item.Type == 'AudioBook' || item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') {