Merge pull request #6936 from thornbill/bdi-lyrics

This commit is contained in:
Bill Thornton
2025-06-26 21:39:25 -04:00
committed by GitHub
4 changed files with 40 additions and 55 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<div id="lyricPage" data-role="page" class="page lyricPage" data-backbutton="true">
<div>
<div class="dynamicLyricsContainer padded-bottom-page">
<div class="lyricsContainer padded-bottom-page">
</div>
</div>
</div>
+38 -53
View File
@@ -1,20 +1,21 @@
import { getLyricsApi } from '@jellyfin/sdk/lib/utils/api/lyrics-api';
import escapeHtml from 'escape-html';
import { AutoScroll } from 'apps/stable/features/lyrics/constants/autoScroll';
import autoFocuser from 'components/autoFocuser';
import { appRouter } from '../components/router/appRouter';
import { appRouter } from 'components/router/appRouter';
import layoutManager from 'components/layoutManager';
import { playbackManager } from '../components/playback/playbackmanager';
import { playbackManager } from 'components/playback/playbackmanager';
import scrollManager from 'components/scrollManager';
import focusManager from 'components/focusManager';
import keyboardNavigation from 'scripts/keyboardNavigation';
import globalize from 'lib/globalize';
import { ServerConnections } from 'lib/jellyfin-apiclient';
import keyboardNavigation from 'scripts/keyboardNavigation';
import LibraryMenu from 'scripts/libraryMenu';
import Events from 'utils/events';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import '../styles/lyrics.scss';
import { AutoScroll } from './lyrics.types';
let currentPlayer;
let currentItem;
@@ -23,21 +24,17 @@ let savedLyrics;
let isDynamicLyric = false;
let autoScroll = AutoScroll.Instant;
function dynamicLyricHtmlReducer(htmlAccumulator, lyric, index) {
if (layoutManager.tv) {
htmlAccumulator += `<button class="lyricsLine dynamicLyric listItem show-focus" id="lyricPosition${index}" data-lyrictime="${lyric.Start}">${escapeHtml(lyric.Text)}</button>`;
} else {
htmlAccumulator += `<div class="lyricsLine dynamicLyric" id="lyricPosition${index}" data-lyrictime="${lyric.Start}">${escapeHtml(lyric.Text)}</div>`;
}
return htmlAccumulator;
}
function lyricHtmlReducer(htmlAccumulator, lyric, index) {
const elem = layoutManager.tv ? 'button' : 'div';
const classes = [];
if (isDynamicLyric) classes.push('dynamicLyric');
if (layoutManager.tv) classes.push('listItem', 'show-focus');
const lyricTime = typeof lyric.Start !== 'undefined' ? `data-lyrictime="${lyric.Start}"` : '';
htmlAccumulator += `<${elem} class="lyricsLine ${classes.join(' ')}" id="lyricPosition${index}" ${lyricTime}>
<bdi>${escapeHtml(lyric.Text)}</bdi>
</${elem}>`;
function staticLyricHtmlReducer(htmlAccumulator, lyric, index) {
if (layoutManager.tv) {
htmlAccumulator += `<button class="lyricsLine listItem show-focus" id="lyricPosition${index}">${escapeHtml(lyric.Text)}</button>`;
} else {
htmlAccumulator += `<div class="lyricsLine" id="lyricPosition${index}">${escapeHtml(lyric.Text)}</div>`;
}
return htmlAccumulator;
}
@@ -96,37 +93,31 @@ export default function (view) {
}
function renderNoLyricMessage() {
const itemsContainer = view.querySelector('.dynamicLyricsContainer');
const itemsContainer = view.querySelector('.lyricsContainer');
if (itemsContainer) {
const html = `<h1> ${globalize.translate('HeaderNoLyrics')} </h1>`;
const html = `<h1>${globalize.translate('HeaderNoLyrics')}</h1>`;
itemsContainer.innerHTML = html;
}
autoFocuser.autoFocus();
}
function renderDynamicLyrics(lyrics) {
const itemsContainer = view.querySelector('.dynamicLyricsContainer');
function renderLyrics(lyrics) {
const itemsContainer = view.querySelector('.lyricsContainer');
if (itemsContainer) {
const html = lyrics.reduce(dynamicLyricHtmlReducer, '');
const html = lyrics.reduce(lyricHtmlReducer, '');
itemsContainer.innerHTML = html;
}
const lyricLineArray = itemsContainer.querySelectorAll('.lyricsLine');
if (isDynamicLyric) {
const lyricLineArray = itemsContainer.querySelectorAll('.lyricsLine');
// attaches click event listener to change playtime to lyric start
lyricLineArray.forEach(element => {
element.addEventListener('click', () => onLyricClick(element.getAttribute('data-lyrictime')));
});
// attaches click event listener to change playtime to lyric start
lyricLineArray.forEach(element => {
element.addEventListener('click', () => onLyricClick(element.getAttribute('data-lyrictime')));
});
const currentIndex = getLyricIndex(getCurrentPlayTime(), lyrics);
updateAllLyricLines(currentIndex, savedLyrics);
}
function renderStaticLyrics(lyrics) {
const itemsContainer = view.querySelector('.dynamicLyricsContainer');
if (itemsContainer) {
const html = lyrics.reduce(staticLyricHtmlReducer, '');
itemsContainer.innerHTML = html;
const currentIndex = getLyricIndex(getCurrentPlayTime(), lyrics);
updateAllLyricLines(currentIndex, savedLyrics);
}
}
@@ -135,28 +126,22 @@ export default function (view) {
isDynamicLyric = Object.prototype.hasOwnProperty.call(lyrics[0], 'Start');
if (isDynamicLyric) {
renderDynamicLyrics(savedLyrics);
} else {
renderStaticLyrics(savedLyrics);
}
renderLyrics(savedLyrics);
autoFocuser.autoFocus(view);
}
function getLyrics(serverId, itemId) {
const apiClient = ServerConnections.getApiClient(serverId);
const lyricsApi = getLyricsApi(toApi(apiClient));
return apiClient.ajax({
url: apiClient.getUrl('Audio/' + itemId + '/Lyrics'),
type: 'GET',
dataType: 'json'
}).then((response) => {
if (!response.Lyrics) {
throw new Error();
}
return response.Lyrics;
});
return lyricsApi.getLyrics({ itemId })
.then(({ data }) => {
if (!data.Lyrics?.length) {
throw new Error('No lyrics returned');
}
return data.Lyrics;
});
}
function bindToPlayer(player) {
+1 -1
View File
@@ -6,7 +6,7 @@
justify-content: center;
}
.dynamicLyricsContainer {
.lyricsContainer {
display: flex;
flex-direction: column;
}