Skip to content

Commit

Permalink
Merge pull request #4733 from robert-hamilton36/LyricsSupport
Browse files Browse the repository at this point in the history
Add Lyric support
  • Loading branch information
thornbill authored Apr 21, 2024
2 parents 58ab898 + 9a56230 commit 3f967f7
Show file tree
Hide file tree
Showing 13 changed files with 456 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/apps/experimental/routes/legacyRoutes/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
controller: 'list',
view: 'list.html'
}
}, {
path: 'lyrics',
pageProps: {
controller: 'lyrics',
view: 'lyrics.html'
}
}, {
path: 'mypreferencesmenu.html',
pageProps: {
Expand Down
6 changes: 6 additions & 0 deletions src/apps/stable/routes/legacyRoutes/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
controller: 'livetv/livetvsuggested',
view: 'livetv.html'
}
}, {
path: 'lyrics',
pageProps: {
controller: 'lyrics',
view: 'lyrics.html'
}
}, {
path: 'music.html',
pageProps: {
Expand Down
34 changes: 34 additions & 0 deletions src/components/itemContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ export function getCommands(options) {
id: 'delete',
icon: 'delete'
});

if (item.Type === 'Audio' && item.HasLyrics && window.location.href.includes(item.Id)) {
commands.push({
name: globalize.translate('DeleteLyrics'),
id: 'deleteLyrics',
icon: 'delete_sweep'
});
}
}

// Books are promoted to major download Button and therefor excluded in the context menu
Expand Down Expand Up @@ -313,6 +321,14 @@ export function getCommands(options) {
});
}

if (item.HasLyrics) {
commands.push({
name: globalize.translate('ViewLyrics'),
id: 'lyrics',
icon: 'lyrics'
});
}

return commands;
}

Expand Down Expand Up @@ -495,6 +511,9 @@ function executeCommand(item, id, options) {
case 'delete':
deleteItem(apiClient, item).then(getResolveFunction(resolve, id, true, true), getResolveFunction(resolve, id));
break;
case 'deleteLyrics':
deleteLyrics(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
break;
case 'share':
navigator.share({
title: item.Name,
Expand All @@ -510,6 +529,15 @@ function executeCommand(item, id, options) {
appRouter.showItem(item.AlbumArtists[0].Id, item.ServerId);
getResolveFunction(resolve, id)();
break;
case 'lyrics': {
if (options.isMobile) {
appRouter.show('lyrics');
} else {
appRouter.showItem(item.Id, item.ServerId);
}
getResolveFunction(resolve, id)();
break;
}
case 'playallfromhere':
getResolveFunction(resolve, id)();
break;
Expand Down Expand Up @@ -636,6 +664,12 @@ function deleteItem(apiClient, item) {
});
}

function deleteLyrics(apiClient, item) {
return import('../scripts/deleteHelper').then((deleteHelper) => {
return deleteHelper.deleteLyrics(item);
});
}

function refresh(apiClient, item) {
import('./refreshdialog/refreshdialog').then(({ default: RefreshDialog }) => {
new RefreshDialog({
Expand Down
39 changes: 39 additions & 0 deletions src/components/nowPlayingBar/nowPlayingBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ let positionSlider;
let toggleAirPlayButton;
let toggleRepeatButton;
let toggleRepeatButtonIcon;
let lyricButton;

let lastUpdateTime = 0;
let lastPlayerState = {};
Expand All @@ -42,6 +43,9 @@ let currentRuntimeTicks = 0;

let isVisibilityAllowed = true;

let lyricPageActive = false;
let isAudio = false;

function getNowPlayingBarHtml() {
let html = '';

Expand Down Expand Up @@ -82,6 +86,8 @@ function getNowPlayingBarHtml() {

html += `<button is="paper-icon-button-light" class="btnAirPlay mediaButton" title="${globalize.translate('AirPlay')}"><span class="material-icons airplay" aria-hidden="true"></span></button>`;

html += `<button is="paper-icon-button-light" class="openLyricsButton mediaButton" title="${globalize.translate('Lyrics')}"><span class="material-icons lyrics" style="top:0.1em" aria-hidden="true"></span></button>`;

html += `<button is="paper-icon-button-light" class="toggleRepeatButton mediaButton" title="${globalize.translate('Repeat')}"><span class="material-icons repeat" aria-hidden="true"></span></button>`;
html += `<button is="paper-icon-button-light" class="btnShuffleQueue mediaButton" title="${globalize.translate('Shuffle')}"><span class="material-icons shuffle" aria-hidden="true"></span></button>`;

Expand Down Expand Up @@ -146,6 +152,7 @@ function bindEvents(elem) {
toggleRepeatButton = elem.querySelector('.toggleRepeatButton');
volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider');
volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer');
lyricButton = nowPlayingBarElement.querySelector('.openLyricsButton');

muteButton.addEventListener('click', function () {
if (currentPlayer) {
Expand Down Expand Up @@ -212,6 +219,14 @@ function bindEvents(elem) {
}
});

lyricButton.addEventListener('click', function() {
if (lyricPageActive) {
appRouter.back();
} else {
appRouter.show('lyrics');
}
});

toggleRepeatButton = elem.querySelector('.toggleRepeatButton');
toggleRepeatButton.addEventListener('click', function () {
switch (playbackManager.getRepeatMode()) {
Expand Down Expand Up @@ -363,6 +378,7 @@ function updatePlayerStateInternal(event, state, player) {
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player));

updateNowPlayingInfo(state);
updateLyricButton();
}

function updateRepeatModeDisplay(repeatMode) {
Expand Down Expand Up @@ -453,6 +469,22 @@ function updatePlayerVolumeState(isMuted, volumeLevel) {
}
}

function updateLyricButton() {
if (!isEnabled) {
return;
}

isAudio ? showButton(lyricButton) : hideButton(lyricButton);
setLyricButtonActiveStatus();
}

function setLyricButtonActiveStatus() {
if (!isEnabled) {
return;
}
lyricButton.classList.toggle('buttonActive', lyricPageActive);
}

function seriesImageUrl(item, options) {
if (!item) {
throw new Error('item cannot be null!');
Expand Down Expand Up @@ -595,6 +627,9 @@ function updateNowPlayingInfo(state) {
function onPlaybackStart(e, state) {
console.debug('nowplaying event: ' + e.type);
const player = this;

isAudio = state.NowPlayingItem.Type === 'Audio';

onStateChanged.call(player, e, state);
}

Expand Down Expand Up @@ -698,6 +733,7 @@ function onStateChanged(event, state) {
}

getNowPlayingBar();
updateLyricButton();
updatePlayerStateInternal(event, state, player);
}

Expand Down Expand Up @@ -754,6 +790,7 @@ function refreshFromPlayer(player, type) {
}

function bindToPlayer(player) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
if (player === currentPlayer) {
return;
}
Expand Down Expand Up @@ -786,6 +823,8 @@ Events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());

document.addEventListener('viewbeforeshow', function (e) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
setLyricButtonActiveStatus();
if (!e.detail.options.enableMediaControl) {
if (isVisibilityAllowed) {
isVisibilityAllowed = false;
Expand Down
8 changes: 7 additions & 1 deletion src/components/remotecontrol/remotecontrol.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ function updateNowPlayingInfo(context, state, serverId) {
contextButton.addEventListener('click', function () {
itemContextMenu.show(Object.assign({
item: fullItem,
user: user
user: user,
isMobile: layoutManager.mobile
}, options))
.catch(() => { /* no-op */ });
});
Expand Down Expand Up @@ -323,6 +324,7 @@ export default function () {
context.querySelector('.remoteControlSection').classList.add('hide');
}

buttonVisible(context.querySelector('.btnLyrics'), item?.Type === 'Audio' && !layoutManager.mobile);
buttonVisible(context.querySelector('.btnStop'), item != null);
buttonVisible(context.querySelector('.btnNextTrack'), item != null);
buttonVisible(context.querySelector('.btnPreviousTrack'), item != null);
Expand Down Expand Up @@ -769,6 +771,10 @@ export default function () {
playbackManager.fastForward(currentPlayer);
}
});
context.querySelector('.btnLyrics').addEventListener('click', function () {
appRouter.show('lyrics');
});

for (const shuffleButton of context.querySelectorAll('.btnShuffleQueue')) {
shuffleButton.addEventListener('click', function () {
if (currentPlayer) {
Expand Down
5 changes: 5 additions & 0 deletions src/controllers/itemDetails/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ <h2 class="sectionTitle sectionTitle-cards padded-right"></h2>
</div>
</div>

<div id="lyricsSection" class="verticalSection-extrabottompadding detailVerticalSection lyricsContainer hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${Lyrics}</h2>
<div is="emby-itemscontainer" class="vertical-list itemsContainer"></div>
</div>

<div class="verticalSection detailVerticalSection moreFromArtistSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right"></h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">
Expand Down
39 changes: 36 additions & 3 deletions src/controllers/itemDetails/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { intervalToDuration } from 'date-fns';
import DOMPurify from 'dompurify';
import markdownIt from 'markdown-it';
import escapeHtml from 'escape-html';
import markdownIt from 'markdown-it';
import isEqual from 'lodash-es/isEqual';

import { appHost } from 'components/apphost';
Expand Down Expand Up @@ -1055,6 +1055,7 @@ function renderDetails(page, item, apiClient, context) {
renderOverview(page, item);
renderMiscInfo(page, item);
reloadUserDataButtons(page, item);
renderLyricsContainer(page, item, apiClient);

// Don't allow redirection to other websites from the TV layout
if (!layoutManager.tv && appHost.supports('externallinks')) {
Expand All @@ -1069,6 +1070,38 @@ function enableScrollX() {
return browser.mobile && window.screen.availWidth <= 1000;
}

function renderLyricsContainer(view, item, apiClient) {
const lyricContainer = view.querySelector('.lyricsContainer');
if (lyricContainer && item.HasLyrics) {
if (item.Type !== 'Audio') {
lyricContainer.classList.add('hide');
return;
}
//get lyrics
apiClient.ajax({
url: apiClient.getUrl('Audio/' + item.Id + '/Lyrics'),
type: 'GET',
dataType: 'json'
}).then((response) => {
if (!response.Lyrics) {
lyricContainer.classList.add('hide');
return;
}
lyricContainer.classList.remove('hide');
const itemsContainer = lyricContainer.querySelector('.itemsContainer');
if (itemsContainer) {
const html = response.Lyrics.reduce((htmlAccumulator, lyric) => {
htmlAccumulator += escapeHtml(lyric.Text) + '<br/>';
return htmlAccumulator;
}, '');
itemsContainer.innerHTML = html;
}
}).catch(() => {
lyricContainer.classList.add('hide');
});
}
}

function renderMoreFromSeason(view, item, apiClient) {
const section = view.querySelector('.moreFromSeasonSection');

Expand Down Expand Up @@ -1119,7 +1152,7 @@ function renderMoreFromArtist(view, item, apiClient) {
const section = view.querySelector('.moreFromArtistSection');

if (section) {
if (item.Type !== 'MusicArtist' && (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length)) {
if (item.Type !== 'MusicArtist' && item.Type !== 'Audio' && (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length)) {
section.classList.add('hide');
return;
}
Expand Down Expand Up @@ -1174,7 +1207,7 @@ function renderSimilarItems(page, item, context) {
const similarCollapsible = page.querySelector('#similarCollapsible');

if (similarCollapsible) {
if (item.Type != 'Movie' && item.Type != 'Trailer' && item.Type != 'Series' && item.Type != 'Program' && item.Type != 'Recording' && item.Type != 'MusicAlbum' && item.Type != 'MusicArtist' && item.Type != 'Playlist') {
if (item.Type != 'Movie' && item.Type != 'Trailer' && item.Type != 'Series' && item.Type != 'Program' && item.Type != 'Recording' && item.Type != 'MusicAlbum' && item.Type != 'MusicArtist' && item.Type != 'Playlist' && item.Type != 'Audio') {
similarCollapsible.classList.add('hide');
return;
}
Expand Down
6 changes: 6 additions & 0 deletions src/controllers/lyrics.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div id="lyricPage" data-role="page" class="page lyricPage" data-backbutton="true">
<div>
<div class="dynamicLyricsContainer padded-bottom-page">
</div>
</div>
</div>
Loading

0 comments on commit 3f967f7

Please sign in to comment.