From 4f681091ab7f85bf02509a361da608414be35695 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 10 May 2021 21:07:08 +1200 Subject: [PATCH] Lyrics auto-update on track change, fixes #714. Some tracks still refuse, but most work now. Refactor to functional component --- src/js/services/genius/actions.js | 34 +-- src/js/views/modals/KioskMode.js | 364 +++++++++++++----------------- 2 files changed, 170 insertions(+), 228 deletions(-) diff --git a/src/js/services/genius/actions.js b/src/js/services/genius/actions.js index 022bfeea..826d3e7d 100755 --- a/src/js/services/genius/actions.js +++ b/src/js/services/genius/actions.js @@ -152,8 +152,7 @@ export function getTrackLyrics(uri, path) { return (dispatch, getState) => { dispatch(coreActions.itemLoaded({ uri, - lyrics: null, - lyrics_path: null, + lyrics: undefined, })); const url = `//${getState().mopidy.host}:${getState().mopidy.port}/iris/http/get_lyrics?path=${path}&connection_id=${getState().pusher.connection_id}`; @@ -181,23 +180,24 @@ export function getTrackLyrics(uri, path) { .then((data) => { if (data.result) { const html = $(data.result); - let lyrics = html.find('.lyrics'); - if (lyrics.length > 0) { - lyrics = lyrics.first(); - lyrics.find('a').replaceWith((k, v) => v); + // Give the JS thread a moment to render the complex page (lots of JS within it) + setTimeout(() => { + let lyrics = html.find('div[class^="Lyrics__Container"], div.lyrics'); + if (lyrics.length > 0) { + lyrics = lyrics.first(); + lyrics.find('a').replaceWith((k, v) => v); - let lyrics_html = lyrics.html(); - lyrics_html = lyrics_html.replace(/(\[)/g, '['); - lyrics_html = lyrics_html.replace(/(\])/g, ']'); + let lyrics_html = lyrics.html(); + lyrics_html = lyrics_html.replace(/(\[)/g, '['); + lyrics_html = lyrics_html.replace(/(\])/g, ']'); - // console.debug(lyrics_html); - - dispatch(coreActions.itemLoaded({ - uri, - lyrics: lyrics_html, - lyrics_path: path, - })); - } + dispatch(coreActions.itemLoaded({ + uri, + lyrics: lyrics_html, + lyrics_path: path, + })); + } + }, 500); } else { dispatch(coreActions.handleException( 'Could not get track lyrics', diff --git a/src/js/views/modals/KioskMode.js b/src/js/views/modals/KioskMode.js index 926f90e4..7aed4de7 100755 --- a/src/js/views/modals/KioskMode.js +++ b/src/js/views/modals/KioskMode.js @@ -1,6 +1,5 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import React, { useState, useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import Link from '../../components/Link'; import Modal from './Modal'; import Thumbnail from '../../components/Thumbnail'; @@ -13,10 +12,14 @@ import * as mopidyActions from '../../services/mopidy/actions'; import * as geniusActions from '../../services/genius/actions'; import { isLoading } from '../../util/helpers'; import { i18n, I18n } from '../../locale'; +import { makeItemSelector } from '../../util/selectors'; -const LyricsScroller = ({ content = '', time_position = 1, duration = 100 }) => { +const LyricsScroller = ({ + content = '', + time_position = 1, + duration = 100, +}) => { const percent = ((time_position / duration) * 110).toFixed(4); - return (
{ - const { - current_track, - stream_title, - show_lyrics, - genius_authorized, - geniusActions: { - findTrackLyrics, - }, - } = this.props; - - if (!prev_stream_title && stream_title) { - this.setWindowTitle(); - } else if (!prev_current_track && current_track) { - this.setWindowTitle(); - - if (show_lyrics && genius_authorized && current_track && current_track.artists && !current_track.lyrics_results) { - findTrackLyrics(current_track); - } - } else if (show_lyrics !== show_lyrics && show_lyrics && current_track) { - if (genius_authorized && current_track && current_track.artists && !current_track.lyrics_results) { - findTrackLyrics(current_track); - } - } - } - - setWindowTitle = () => { - const { - current_track, - stream_title, - uiActions: { - setWindowTitle, - }, - } = this.props; +const KioskMode = () => { + const dispatch = useDispatch(); + const play_state = useSelector((state) => state.mopidy.play_state); + const time_position = useSelector((state) => state.mopidy.time_position); + const core_current_track = useSelector((state) => state.core.current_track); + const itemSelector = makeItemSelector(core_current_track?.uri); + const current_track = useSelector(itemSelector); + const stream_title = useSelector((state) => state.core.stream_title); + const genius_available = useSelector((state) => state.genius.authorization); + const show_lyrics = useSelector((state) => state.ui.show_lyrics); + const lyrics_enabled = show_lyrics && genius_available; + const { images = [] } = current_track || {}; + const setWindowTitle = () => { if (stream_title) { const stream = stream_title.split(' - '); - setWindowTitle(i18n('modal.kiosk.title_window', { name: stream[1], artist: stream[0] })); + dispatch(uiActions.setWindowTitle( + i18n('modal.kiosk.title_window', { name: stream[1], artist: stream[0] }), + )); } else if (current_track) { const artist = current_track.artists.map((artist) => artist.name).join(', '); - setWindowTitle(i18n('modal.kiosk.title_window', { name: current_track.name, artist })); + dispatch(uiActions.setWindowTitle( + i18n('modal.kiosk.title_window', { name: current_track.name, artist }) + )); } else { - setWindowTitle(i18n('modal.kiosk.title')); + dispatch(uiActions.setWindowTitle(i18n('modal.kiosk.title'))); } - } + }; - togglePlay(e) { - if (this.props.play_state == 'playing') { - this.props.mopidyActions.pause(); - } else { - this.props.mopidyActions.play(); - } - } + const fetchLyrics = () => { + if (!show_lyrics || !genius_available) return; + if (!current_track || !current_track?.artists?.length) return; + if (current_track.lyrics) return; - toggleLyrics = () => { - const { - show_lyrics, - uiActions, - genius_authorized, - current_track, - } = this.props; - - if (!genius_authorized) { - uiActions.createNotification({ level: 'warning', content: `${i18n('track.want_lyrics')} ${i18n('settings.title')}` }); + // We got results, but failed to load the lyrics, so re-try + if (current_track.lyrics_results && current_track.lyrics === null) { + dispatch(geniusActions.getTrackLyrics(current_track.uri, current_track.path)); return; } - uiActions.set({ show_lyrics: !show_lyrics }); - if ( - !show_lyrics - && this.props.genius_authorized - && current_track - && current_track.artists - && !current_track.lyrics_results) { - this.props.geniusActions.findTrackLyrics(current_track); - } - } - renderPlayButton() { - let button = ; - if (this.props.play_state == 'playing') { - button = ; - } - return button; - } + dispatch(geniusActions.findTrackLyrics(current_track.uri)); + }; - render() { - const { - show_lyrics, - current_track, - stream_title, - load_queue, - genius_authorized, - time_position, - } = this.props; - const lyrics_enabled = show_lyrics && genius_authorized; - if (current_track && current_track.images) { - var { images } = current_track; - } else { - var images = []; - } + useEffect(() => { + setWindowTitle(); + }, []); - const extraControls = ( -
- {lyrics_enabled ? - : } -
- -
+ useEffect(() => { + if (current_track) { + setWindowTitle(); + fetchLyrics(); + } + }, [current_track?.uri]); + + useEffect(() => { + if (lyrics_enabled) { + fetchLyrics(); + } + }, [lyrics_enabled]); + + const toggleLyrics = () => { + if (!genius_available) { + uiActions.createNotification({ + level: 'warning', + content: `${i18n('track.want_lyrics')} ${i18n('settings.title')}`, + }); + return; + } + dispatch(uiActions.set({ show_lyrics: !show_lyrics })); + fetchLyrics(); + }; + + const extraControls = ( +
+ {lyrics_enabled ? + : } +
+
- ); +
+ ); - return ( - dispatch(mopidyActions.play())} + > + + + ); + if (play_state === 'playing') { + playButton = ( + - { this.renderPlayButton() } - -
-
- -
-
- -
- - - + + ); } + + return ( + + + +
+ +
+
+ +
+
+
+ {stream_title && {stream_title}} + {!stream_title && current_track && {current_track.name}} + {!stream_title && !current_track && -} +
+ +
+
+ +
+
+ + {playButton} + +
+
+ +
+
+ +
+ + +
+ ); } -const mapStateToProps = (state) => { - const { - core: { - stream_title, - current_track: core_current_track, - items, - }, - mopidy: { - play_state, - time_position, - }, - ui: { - load_queue, - show_lyrics, - }, - genius: { - authorization: genius_authorized, - }, - } = state; - - const current_track = core_current_track && items[core_current_track.uri] !== undefined - ? items[core_current_track.uri] - : null; - - return { - play_state, - current_track, - stream_title, - time_position, - load_queue, - show_lyrics, - genius_authorized, - }; -}; - -const mapDispatchToProps = (dispatch) => ({ - uiActions: bindActionCreators(uiActions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch), - geniusActions: bindActionCreators(geniusActions, dispatch), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(KioskMode); +export default KioskMode;