/** * LMS-Material * * Copyright (c) 2018-2026 Craig Drummond * MIT license. */ 'use strict'; const ARTIST_TAB = 0; const ALBUM_TAB = 1; const TRACK_TAB = 2; const NP_PIC_ACT = 1; const NP_INFO_ACT = 2; const NP_BROWSE_CMD = 3; const NP_COPY_DETAILS_CMD = 4; const NP_TOGGLE_ACT = 5; const NP_SHOW_IN_TABS_ACT = 6; const NP_SYNC_ACT = 7; const NP_LYRICS_SCROLL_ACT = 8; const NP_LYRICS_HIGHLIGHT_ACT = 9; const NP_COPY_ACT = 10; const NP_SEARCH_ACT = 11; const NP_ZOOM_ACT = 12; const NP_CUSTOM = 100; const NP_ITEM_ACT = 200; const NP_MIN_WIDTH_FOR_FULL = 780; var currentPlayingTrackPosition = 0; var lmsNowPlaying = Vue.component("lms-now-playing", { template: `
{{timeTooltip.text}}
settings
skip_previous {{playerStatus.iswaiting ? 'pause_circle_outline' : playerStatus.isplaying ? 'pause_circle_filled' : 'play_circle_filled'}} skip_next
{{mobileBarText}}
{{title}} {{SEPARATOR}}
{{formattedTime}}{{SEPARATOR}}{{playerStatus.playlist.current | trackCount(playerStatus.playlist.count)}}
{{formattedTime}}
{{technicalInfo}}
{{repAltBtn.icon}}{{shuffAltBtn.icon}}
{{playerStatus.playlist.current | trackCount(playerStatus.playlist.count)}} queue_music
queue_music
 
{{overlayVolume}}%

{{title}}

{{techInfo ? technicalInfo : ""}}{{playerStatus.playlist.current | trackCount(playerStatus.playlist.count, techInfo ? SEPARATOR : undefined)}}

{{playerStatus.current.time | displayTime}}

{{repAltBtn.icon}} repeat_one repeat all_inclusive skip_previous {{playerStatus.iswaiting ? 'pause_circle_outline' : playerStatus.isplaying ? 'pause_circle_filled' : 'play_circle_filled'}} skip_next {{shuffAltBtn.icon}} shuffle

{{title}}

{{techInfo ? technicalInfo : ""}}{{playerStatus.playlist.current | trackCount(playerStatus.playlist.count, techInfo ? SEPARATOR : undefined)}}

{{playerStatus.current.time | displayTime}}

{{repAltBtn.icon}} repeat_one repeat all_inclusive skip_previous {{playerStatus.iswaiting ? 'pause_circle_outline' : playerStatus.isplaying ? 'pause_circle_filled' : 'play_circle_filled'}} skip_next {{shuffAltBtn.icon}} shuffle
keyboard_arrow_down `, data() { return { coverUrl:DEFAULT_COVER, playerStatus: { isplaying: false, iswaiting: false, sleepTimer: false, dvc: VOL_STD, current: { canseek:1, duration:0, time:undefined, title:undefined, liveEdge:undefined, artist:undefined, artistAndComposer: undefined, artistAndComposerWithContext:undefined, album:undefined, albumName:undefined, albumLine:undefined, technicalInfo:undefined, pospc:0.0, bufpc:100.0, tracknum:undefined, disc:0, year:0, url:undefined, comment:undefined, source: {local:true, text:undefined}, emblem: undefined, maiComposer:undefined, discsubtitle:undefined, grouping:undefined }, playlist: { shuffle:0, repeat: 0, randomplay:0, current:0, count:0 }, }, mobileBarText: undefined, info: { show: false, tab:parseInt(getLocalStorageVal("nptab", TRACK_TAB)), showTabs:false, sync: true, tabs: [ { value:ARTIST_TAB, title:undefined, ctitle:undefined, text:undefined, reqId:0, image: undefined, sections:[ { title:undefined, items:[], min:1, more:undefined, grid:getLocalStorageBool("np-tabs-"+ARTIST_TAB+"-0-grid", false) }, { title:undefined, html:undefined } ] }, { value:ALBUM_TAB, title:undefined, ctitle:undefined, text:undefined, reqId:0, image: undefined, sections:[ { title:undefined, items:[], min:2, more:undefined }, { title:undefined, items:[], min:1, more:undefined } ] }, { value: TRACK_TAB, title:undefined, ctitle:undefined, text:undefined, lines:undefined, scroll:false, highlight:false, reqId:0, image: undefined, sections:[ { title:undefined, html:undefined } ] } ] }, infoTrack: {album_id:undefined, track_id:undefined, path:undefined}, trans: { expand:undefined, collapse:undefined, sync:undefined, unsync:undefined, more:undefined, dstm:undefined, randomMix:undefined, repeatAll:undefined, repeatOne:undefined, repeatOff:undefined, shuffleAll:undefined, shuffleAlbums:undefined, shuffleOff:undefined, play:undefined, pause:undefined, prev:undefined, next:undefined, collapseNp:undefined, expandNp:undefined, menu:undefined, browse:undefined, queue:undefined }, showTotal: true, landscape: false, wide: 0, windowWidth: 10, barInfoWithContextWidth: 10, largeView: false, menu: { show: false, x:0, y:0, items: [], icons:false, tab:undefined, section:undefined, index:undefined }, rating: {value:0, setting:0}, timeTooltip: {show: false, x:0, y:0, text:undefined}, overlayVolume: -1, repAltBtn:{show:false, command:[], icon:undefined, image:undefined, tooltip:undefined}, shuffAltBtn:{show:false, command:[], icon:undefined, image:undefined, tooltip:undefined}, disableBtns:true, disablePrev:true, disableNext:true, dstm:false, showOverlay:false, showOverlayTimer:undefined, currentBgndUrl:"", showBgnd:true }; }, mounted() { this.setZoom(parseFloat(getLocalStorageVal("npInfoZoom", 1.0))); this.showNpBar = undefined; this.desktopBarHeight = getComputedStyle(document.documentElement).getPropertyValue('--desktop-npbar-height'); this.mobileBarThinHeight = getComputedStyle(document.documentElement).getPropertyValue('--mobile-npbar-height-thin'); this.mobileBarThickHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--mobile-npbar-height-thick').replace("px", "")); this.bottomPad = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--bottom-pad').replace("px", "")); if (isNaN(this.bottomPad)) { this.bottomPad = 0; } this.controlBar(); this.info.tabs[TRACK_TAB].scroll=getLocalStorageBool("npScrollLyrics", true); this.info.tabs[TRACK_TAB].highlight=getLocalStorageBool("npHighlightLyrics", true); bus.$on('maiDefaults', function(def, isRevert) { if (undefined!=def.npInfoZoom && (isRevert || undefined==getLocalStorageVal('npInfoZoom', undefined))) { this.setZoom(parseFloat(def.npInfoZoom)); } if (undefined!=def.npScrollLyrics && (isRevert || undefined==getLocalStorageVal('npScrollLyrics', undefined))) { this.info.tabs[TRACK_TAB].scroll = def.npScrollLyrics; } if (undefined!=def.npHighlightLyrics && (isRevert || undefined==getLocalStorageVal('npHighlightLyrics', undefined))) { this.info.tabs[TRACK_TAB].highlight = def.npHighlightLyrics; } if (undefined!=def.showTabs && (isRevert || undefined==getLocalStorageVal('showTabs', undefined))) { this.info.showTabs = def.showTabs; } }.bind(this)); bus.$on('mobileBarChanged', function() { this.controlBar(true); }.bind(this)); bus.$on('customActions', function(val) { this.customActions = getCustomActions("track", false); }.bind(this)); this.info.showTabs=getLocalStorageBool("showTabs", false); bus.$on('closeNowPlaying', function() { if (this.info.show) { bus.$emit('info'); } this.largeView = false; }.bind(this)); bus.$on('expandNowPlaying', function(val) { addBrowserHistoryItem(); if (window.innerHeight>=LMS_MIN_NP_LARGE_INFO_HEIGHT) { if (val) { this.info.show = false; } this.largeView = val; } }.bind(this)); bus.$on('pageChanged', function(page) { if (page=='now-playing') { if (!this.info.show) { this.$forceUpdate(); } } }.bind(this)); bus.$on('info-swipe', function(d, ev) { if (this.info.show) { if ('left'==d) { if (this.info.tab==2) { this.info.tab=0; } else { this.info.tab++; } } else if ('right'==d) { if (this.info.tab==0) { this.info.tab=2; } else { this.info.tab--; } } else if ('down'==d && this.info.show && undefined!=ev && undefined!=ev.target && 'np-mai-img'==ev.target.className) { bus.$emit('info'); } } }.bind(this)); bus.$on('swipeUp', function() { if (this.$store.state.desktopLayout && !this.$store.state.pinQueue && this.$store.state.showQueue) { return; } if ((this.largeView || !this.$store.state.desktopLayout) && !this.info.show) { let elem = document.getElementById("np-track-info"); if (undefined==elem || (elem.scrollHeight-8)<=elem.clientHeight) { bus.$emit('info'); } } }.bind(this)); bus.$on('swipeDown', function() { if (this.largeView && this.$store.state.desktopLayout && (this.$store.state.pinQueue || !this.$store.state.showQueue)) { this.largeView=false; } else if (!this.$store.state.desktopLayout && MBAR_REP_NAV==this.mobileBar && !this.info.show) { this.$store.commit('setPage', this.$store.state.prevPage); } }.bind(this)); var npView = this; this.sizeCheckDelay = 0; // How many resize events have we seen before size checked? window.addEventListener('resize', () => { if (npView.resizeTimeout) { clearTimeout(npView.resizeTimeout); } npView.sizeCheckDelay++; if (npView.sizeCheckDelay>=10) { npView.checkWindowSize(); } else { npView.resizeTimeout = setTimeout(function () { npView.resizeTimeout = undefined; npView.checkWindowSize(); }, 50); } }, false); // Long-press on 'now playing' nav button whilst in now-playing shows track info bus.$on('nav', function(page, longPress) { if ('now-playing'==page) { if (longPress && undefined!=this.$store.state.player) { bus.$emit('dlg.open', 'sleep', this.$store.state.player); } else if (LMS_P_MAI && this.playerStatus && this.playerStatus.current && this.playerStatus.current.artist) { this.largeView = false; this.info.show = !this.info.show; } else if (this.info.show) { this.info.show = false; } } }.bind(this)); bus.$on('escPressed', function() { if (this.$store.state.desktopLayout) { if (this.info.show) { bus.$emit('info'); } else if (this.largeView) { this.largeView = false; } } else if (this.$store.state.page=="now-playing") { if (this.info.show) { bus.$emit('info'); } else { let prev = this.$store.state.prevPage; this.$store.commit('setPage', prev==undefined || prev=='now-playing' ? 'browse' : prev); } } }.bind(this)); this.info.sync=getLocalStorageBool("syncInfo", true); bus.$on('playerStatus', function(playerStatus) { try { nowplayingOnPlayerStatus(this, playerStatus); // can be called before deferred JS is loaded... } catch (e) { // If error, get status 1 second later... setTimeout(function () { bus.$emit('refreshStatus', this.$store.state.player.id); }.bind(this), 1000); } }.bind(this)); // Refresh status now, in case we were mounted after initial status call bus.$emit('refreshStatus'); this.page = document.getElementById("np-page"); bus.$on('themeChanged', function() { this.setBgndCover(); }.bind(this)); this.checkLandscape(); setTimeout(function () { this.checkLandscape(); }.bind(this), 1000); bus.$on('currentCover', function(coverUrl) { this.coverUrl = undefined==coverUrl ? DEFAULT_COVER : coverUrl; this.setBgndCover(); }.bind(this)); bus.$emit('getCurrentCover'); bus.$on('setBgndCover', function() { this.setBgndCover(); }.bind(this)); bus.$on('langChanged', function() { this.initItems(); }.bind(this)); this.initItems(); bus.$on('closeMenu', function() { if (this.menu.show) { this.menu.show = false; } }.bind(this)); bus.$on('closeDialog', function(dlg) { if (dlg == 'info-dialog') { this.info.show = false; } }.bind(this)); bus.$on('info', function() { if (!LMS_P_MAI) { return; } addBrowserHistoryItem(); if ((window.innerHeight>=LMS_MIN_NP_LARGE_INFO_HEIGHT && this.playerStatus.playlist.count>0) || this.info.show) { this.largeView = false; this.info.show = !this.info.show; } }.bind(this)); bus.$on('npclose', function() { this.close(); }.bind(this)); bus.$on('npbrowse', function(cmd, params, title, subtitle) { bus.$emit("browse", cmd, params, title, this.currentView(), undefined, subtitle); this.close(); }.bind(this)); bus.$on('linkClicked', function() { this.close(); }.bind(this)); bus.$on('prefset', function(pref, value, player) { if ("plugin.dontstopthemusic:provider"==pref && player==this.$store.state.player.id) { this.dstm = (""+value)!="0"; } }.bind(this)); bus.$on('showLinkMenu.now-playing', function(x, y, menu) { showMenu(this, {items: menu, x:x, y:y, show:true, icons:true}); }.bind(this)); this.showTotal = getLocalStorageBool('showTotal', true); if (!IS_MOBILE) { bindKey(LMS_TRACK_INFO_KEYBOARD, 'mod'); bindKey(LMS_EXPAND_NP_KEYBOARD, 'mod+shift'); if (undefined!=LMS_P_RP) { for (var i=0; i<=6; ++i) { bindKey(''+i, 'mod+shift'); } } bus.$on('keyboard', function(key, modifier) { if (this.$store.state.visibleMenus.size>0 || this.$store.state.openDialogs.length>1 || (!this.$store.state.desktopLayout && this.$store.state.page!="now-playing")) { return; } if ('mod'==modifier && LMS_TRACK_INFO_KEYBOARD==key && LMS_P_MAI && (this.$store.state.openDialogs.length==0 || this.$store.state.openDialogs[0]=='info-dialog') && (window.innerHeight>=LMS_MIN_NP_LARGE_INFO_HEIGHT || this.info.show)) { this.largeView = false; this.info.show = !this.info.show; } else if ('mod+shift'==modifier) { if (LMS_EXPAND_NP_KEYBOARD==key && this.$store.state.desktopLayout && (window.innerHeight>=LMS_MIN_NP_LARGE_INFO_HEIGHT || this.largeView)) { this.info.show = false; this.largeView = !this.largeView; } else if (1==key.length && !isNaN(key) && undefined!=LMS_P_RP && LMS_STATS_ENABLED && this.$store.state.showRating) { this.rating.value = parseInt(key); this.setRating(); } } }.bind(this)); } bus.$on('releaseSupportChanged', function() { this.initItems(); }.bind(this)); }, methods: { initItems() { this.trans = { dstm:i18n("Don't Stop The Music"), randomMix:i18n("Random mix"), repeatAll:i18n("Repeat queue"), repeatOne:i18n("Repeat single track"), repeatOff:i18n("No repeat"), shuffleAll:i18n("Shuffle tracks"), shuffleAlbums:lmsOptions.supportReleaseTypes ? i18n("Shuffle releases") : i18n("Shuffle albums"), shuffleOff:i18n("No shuffle"), play:i18n("Play"), pause:i18n("Pause"), prev:i18n("Previous track"), next:i18n("Next track"), collapseNp:i18n("Collapse now playing"), expandNp:i18n("Expand now playing"), menu:i18n("Menu"), browse:i18n('Browse'), queue:i18n('Queue') }; this.info.tabs[TRACK_TAB].title=i18n("Track"); this.info.tabs[ARTIST_TAB].title=i18n("Artist"); this.info.tabs[ARTIST_TAB].ctitle=i18n("Composer"); this.info.tabs[ALBUM_TAB].title=lmsOptions.supportReleaseTypes ? i18n('Release') : i18n("Album"); this.info.tabs[ARTIST_TAB].sections[0].title=lmsOptions.supportReleaseTypes ? i18n("Releases") : i18n("Albums"); this.info.tabs[ARTIST_TAB].sections[1].title=i18n("Similar artists"); this.info.tabs[ALBUM_TAB].sections[0].title=i18n("Tracks"); this.info.tabs[ALBUM_TAB].sections[1].title=i18n("Local files"); this.info.tabs[TRACK_TAB].sections[0].title=i18n("Details"); }, showMenu(event) { nowplayingShowMenu(this, event); }, emblemClicked() { if (this.playerStatus.current.source && this.playerStatus.current.source.url) { openWindow(this.playerStatus.current.source.url); } }, trackCountClicked() { if (this.totalTogglesQueue) { this.$store.commit('setShowQueue', !this.$store.state.showQueue); } }, menuAction(item) { nowplayingMenuAction(this, item); }, menuStdAction(item) { nowplayingMenuStdAction(this, item); }, showPic() { let artist = this.playerStatus.current.albumartist ? this.playerStatus.current.albumartist : this.playerStatus.current.artist; let title = artist ? (this.playerStatus.current.albumName ? this.playerStatus.current.albumName + SEPARATOR + artist : artist) : undefined; bus.$emit('dlg.open', 'gallery', [{url:this.coverUrl, title:title}], 0, true); }, doAction(command) { if (this.$store.state.visibleMenus.size>0) { return; } bus.$emit('playerCommand', command); }, setPosition() { let haveTime = this.playerStatus.current && undefined!=this.playerStatus.current.time && undefined!=this.playerStatus.current.duration; let pc = haveTime && this.playerStatus.current.duration>0 ? 100*Math.floor(this.playerStatus.current.time*1000/this.playerStatus.current.duration)/1000 : 0.0; if (pc!=this.playerStatus.current.pospc) { this.playerStatus.current.pospc = pc; } pc = Math.min( haveTime && undefined!=this.playerStatus.current.liveEdge && this.playerStatus.current.duration>0 ? 100*Math.floor((this.playerStatus.current.liveEdge+this.playerStatus.current.time)*1000/this.playerStatus.current.duration)/1000 : 100.0, 100.0); if (pc!=this.playerStatus.current.bufpc) { this.playerStatus.current.bufpc = pc; if (pc>=99.9999) { this.stopLiveEdgeInterval(); } } this.updateLyricsPosition(); }, updateLyricsPosition() { if (this.info.show && this.info.tabs[TRACK_TAB].lines && this.lyricsTimesValid && this.info.tabs[TRACK_TAB].scroll && this.playerStatus.current && undefined!=this.playerStatus.current.time) { let pos = undefined; for (let i=0, loop=this.info.tabs[TRACK_TAB].lines, len=loop.length; i600) { pos = Math.max(0, pos-7); } else if (tab.offsetHeight>300) { pos = Math.max(0, pos-4); } } if (pos==this.info.tabs[TRACK_TAB].pos) { return; } this.info.tabs[TRACK_TAB].pos=pos; if (0==pos && tab) { setElemScrollTop(tab, 0); } else { let adjust = tab ? tab.offsetHeight>700 ? 5 : tab.offsetHeight>350 ? 3 : 2 : 2; let elem = document.getElementById("np-lyrics-"+(pos-(adjust3) { const rect = document.getElementById("pos-slider").getBoundingClientRect(); const evPos = isTouch ? getTouchPos(e) : {x:e.clientX, y:e.clientY}; let pos = evPos.x - rect.x; if (isTouch && ( (evPos.x < (rect.x - 8)) || (evPos.x > (rect.x+rect.width + 8)) || (evPos.y < (rect.y - 8)) || (evPos.y > (rect.y+rect.height + 8))) ) { return; } // Try to detect up-swipes on desktop layout on mobile devices, e.g. newer Android where swipe up to // show navigation bar. if (isTouch && this.$store.state.desktopLayout && !this.largeView && undefined!=this.touchStartPos && (window.innerHeight-8)<=this.touchStartPos.y && this.touchStartPos.y>evPos.y && (this.touchStartPos.y-evPos.y)>4) { return; } pos = Math.min(Math.max(0, pos), rect.width); let val = Math.floor(this.playerStatus.current.duration * pos / rect.width); // On touch devices we get a sliderChanged event from touchSliderEnd and the one from the slider // So, ignore events too close together. See #672 if (undefined!=this.lastTimeEvent && this.lastTimeEvent.isTouch!=isTouch && this.lastTimeEvent.val==val && ((new Date().getTime())-this.lastTimeEvent.time)<250) { return; } this.doAction(['time', val]); this.lastTimeEvent = {time: new Date().getTime(), val: val, isTouch: isTouch}; } }, moveTimeTooltipTouch(e) { this.moveTimeTooltip(getTouchPos(e), true); }, moveTimeTooltip(e, isTouch) { if (this.timeTooltip.show) { if (this.playerStatus.current.duration<=1) { this.hideTimeTooltip(); return; } this.timeTooltip.x = e.x const rect = document.getElementById("pos-slider").getBoundingClientRect(); this.timeTooltip.y = rect.y - (isTouch ? 32 : 0); let pos = e.x - rect.x; pos = Math.min(Math.max(0, pos), rect.width); this.timeTooltip.text=""+formatSeconds(Math.floor(this.playerStatus.current.duration * pos / rect.width)); this.startTooltipTimeout(); } }, touchSliderStart(e) { this.showTimeTooltip(); this.touchStartPos = getTouchPos(e); }, touchSliderEnd(e) { if (this.timeTooltip.show) { this.sliderChanged(e, true); this.hideTimeTooltip(); } this.touchStartPos = undefined; }, setInfoTrack() { this.infoTrack={ title: this.playerStatus.current.title, track_id: this.playerStatus.current.id, artist: this.playerStatus.current.artist, artists: this.playerStatus.current.artists, artist_id: this.playerStatus.current.artist_ids ? this.playerStatus.current.artist_ids[0] : this.playerStatus.current.artist_id, artist_ids: this.playerStatus.current.artist_ids, maiComposer: this.playerStatus.current.maiComposer, composer: this.playerStatus.current.composer, composer_id: this.playerStatus.current.composer_ids ? this.playerStatus.current.composer_ids[0] : this.playerStatus.current.composer_id, composer_ids: this.playerStatus.current.composer_ids, composers: this.playerStatus.current.composers, albumartist: this.playerStatus.current.albumartist, albumartist_id: this.playerStatus.current.albumartist_ids ? this.playerStatus.current.albumartist_ids[0] : this.playerStatus.current.artist_id, albumartist_ids: this.playerStatus.current.albumartist_ids, album: this.playerStatus.current.albumName, album_id: this.playerStatus.current.album_id, url: this.playerStatus.current.url, path: localPath(this.playerStatus.current.url)}; this.infoTrack.empty=undefined==this.infoTrack.title && undefined==this.infoTrack.track_id && undefined==this.infoTrack.artist && undefined==this.infoTrack.artist_id && undefined==this.infoTrack.artist_ids && undefined==this.infoTrack.composer && undefined==this.infoTrack.composer_id && undefined==this.infoTrack.composer_ids && undefined==this.infoTrack.albumartist && undefined==this.infoTrack.albumartist_id && undefined==this.infoTrack.albumartist_ids && undefined==this.infoTrack.album && undefined==this.infoTrack.url; }, currentView() { return this.$store.state.desktopLayout ? this.info.show ? NP_INFO : this.largeView ? NP_EXPANDED : undefined : this.info.show ? NP_INFO : 'now-playing' }, trackInfo() { if (undefined==this.playerStatus.current.id) { bus.$emit('showMessage', i18n('Nothing playing')); return; } let returnView = this.currentView(); this.close(); bus.$emit('trackInfo', {id: "track_id:"+this.playerStatus.current.id, title:this.playerStatus.current.title, image: this.coverUrl}, this.playerStatus.playlist.current, returnView); }, fetchTrackInfo() { nowplayingFetchTrackInfo(this); }, fetchArtistInfo() { nowplayingFetchArtistInfo(this); }, fetchAlbumInfo() { nowplayingFetchAlbumInfo(this); }, isCurrent(data, tab) { return data.id==this.info.tabs[tab].reqId; }, showInfo() { if (!this.info.show || !this.infoTrack) { return; } if (!this.showTabs) { this.fetchTrackInfo(); this.fetchArtistInfo(); this.fetchAlbumInfo(); this.updateLyricsPosition(); } else if (TRACK_TAB==this.info.tab) { this.fetchTrackInfo(); this.updateLyricsPosition(); } else if (ARTIST_TAB==this.info.tab) { this.fetchArtistInfo(); } else { this.fetchAlbumInfo(); } }, close() { if (this.$store.state.desktopLayout) { this.info.show=false; this.largeView=false; } }, startPositionInterval() { this.stopPositionInterval(); this.positionInterval = setInterval(function () { if (undefined!=this.playerStatus.current.time && this.playerStatus.current.time>=0) { var current = new Date(); var diff = (current.getTime()-this.playerStatus.current.updated.getTime())/1000.0; this.playerStatus.current.time = this.playerStatus.current.origTime + diff; currentPlayingTrackPosition = this.playerStatus.current.time; this.setPosition(); if (this.playerStatus.current.duration && this.playerStatus.current.duration>0 && this.playerStatus.current.time>=(this.playerStatus.current.duration+2)) { bus.$emit('refreshStatus'); } } }.bind(this), 1000); }, stopPositionInterval() { if (undefined!==this.positionInterval) { clearInterval(this.positionInterval); this.positionInterval = undefined; } }, startLiveEdgeInterval() { this.stopLiveEdgeInterval(); if (undefined!=this.playerStatus.current.liveEdge && this.playerStatus.current.bufpc<100.0) { this.liveEdgeInterval = setInterval(function () { if (undefined!=this.playerStatus.current.liveEdge) { bus.$emit('refreshStatus'); } else { this.stopLiveEdgeInterval(); } }.bind(this), 10000); } }, stopLiveEdgeInterval() { if (undefined!==this.liveEdgeInterval) { clearInterval(this.liveEdgeInterval); this.liveEdgeInterval = undefined; } }, toggleTime() { if (this.$store.state.visibleMenus.size>0) { return; } this.showTotal = !this.showTotal; setLocalStorageVal("showTotal", this.showTotal); }, setBgndCover() { var url = this.coverUrl; if (undefined==url || url.endsWith(DEFAULT_COVER) || url.endsWith("/music/undefined/cover")) { url=this.drawBackdrop || this.drawInfoBackdrop ? 'material/backdrops/nowplaying.jpg' : ''; } updateBgndImage(this, url); }, playPauseButton(longPress) { if (this.$store.state.visibleMenus.size>0) { return; } if (longPress) { this.doAction(['stop']); bus.$emit('showMessage', i18n('Stop'), 500); } else { this.doAction([this.playerStatus.isplaying ? 'pause' : 'play']); } }, prevButton(skip) { if (this.$store.state.visibleMenus.size>0 || queryParams.party) { return; } if (!this.disablePrev) { if (skip && this.playerStatus.current.time>=this.$store.state.skipBSeconds) { this.doAction(['time', this.playerStatus.current.time-this.$store.state.skipBSeconds]); } else { this.doAction(['button', 'jump_rew']); } } }, nextButton(skip) { if (this.$store.state.visibleMenus.size>0 || queryParams.party) { return; } if (!this.disableNext) { if (skip && (this.playerStatus.current.time+this.$store.state.skipFSeconds)0 || queryParams.party || this.playerStatus.playlist.randomplay==1) { return; } if (this.shuffAltBtn.show) { this.doCommand(this.shuffAltBtn.command, this.shuffAltBtn.tooltip); } else if (this.playerStatus.playlist.shuffle===2) { this.doAction(['playlist', 'shuffle', 0]); } else if (this.playerStatus.playlist.shuffle===1) { this.doAction(['playlist', 'shuffle', 2]); } else { this.doAction(['playlist', 'shuffle', 1]); } }, repeatClicked(longPress) { if (this.$store.state.visibleMenus.size>0 || queryParams.party) { return; } if (this.repAltBtn.show) { this.doCommand(this.repAltBtn.command, this.repAltBtn.tooltip); } else { if (this.playerStatus.playlist.randomplay===1) { /* confirm(i18n("Stop random mix?"), i18n('Stop')).then(res => { if (res) { lmsCommand(this.$store.state.player.id, ["randomplay", "disable"]).then(({data}) => { bus.$emit('refreshStatus'); }); } });*/ bus.$emit('dlg.open', 'rndmix', undefined, true); } else if (this.playerStatus.playlist.repeat===0) { if (LMS_P_DSTM) { if (longPress) { bus.$emit('dlg.open', 'dstm'); } else if (this.dstm) { lmsCommand(this.$store.state.player.id, ["material-skin-client", "save-dstm"]).then(({data}) => { bus.$emit("dstm", this.$store.state.player.id, 0); }); } else { bus.$emit('playerCommand', ['playlist', 'repeat', 2]); } } else { bus.$emit('playerCommand', ['playlist', 'repeat', 2]); } } else if (this.playerStatus.playlist.repeat===1) { bus.$emit('playerCommand', ['playlist', 'repeat', 0]); } else if (this.playerStatus.playlist.repeat===2) { bus.$emit('playerCommand', ['playlist', 'repeat', 1]); if (LMS_P_DSTM) { lmsCommand(this.$store.state.player.id, ["material-skin-client", "get-dstm"]).then(({data}) => { if (data && data.result && undefined!=data.result.provider) { bus.$emit("dstm", this.$store.state.player.id, data.result.provider); } }); } } } }, showSleep() { if (this.$store.state.visibleMenus.size>0 || queryParams.party) { return; } bus.$emit('dlg.open', 'sleep', this.$store.state.player); }, setRating(allowReset) { var val = allowReset && this.rating.value==this.rating.setting && this.rating.value<=1 ? 0 : this.rating.value; // this.rating.value is updated *before* this setRating click handler is called, so we can use its model value to update LMS this.rating.track_id = this.playerStatus.current.id; this.rating.album_id = this.playerStatus.current.album_id; lmsCommand(this.$store.state.player.id, [LMS_P_RP, "setrating", this.playerStatus.current.id, val]).then(({data}) => { if (allowReset && this.rating.track_id==this.playerStatus.current.id) { this.rating.value=val; } logJsonMessage("RESP", data); bus.$emit('refreshStatus'); bus.$emit('ratingChanged', this.rating.track_id, this.rating.album_id); }); }, doCommand(command, msg) { lmsCommand(this.$store.state.player.id, command).then(({data}) => { if (undefined!=msg) { bus.$emit('showMessage', msg); } }); }, clickImage(event) { nowPlayingClickImage(this, event); }, barClicked(ev) { if ((IS_MOBILE || (!this.desktopLayout && MBAR_REP_NAV==this.mobileBar)) && ev && ev.target && (!ev.target.className || !ev.target.className.includes('v-icon'))) { if (!this.desktopLayout) { let touch = getTouchPos(ev); let x = undefined==touch ? ev.x : touch.x; if ((MBAR_THICK==this.mobileBar || MBAR_REP_NAV==this.mobileBar) && x<(window.innerWidth-50)) { this.$store.commit('setPage', 'now-playing'); this.info.show = false; } } else if (window.innerWidth<550 && (ev.x<(window.innerWidth-(window.innerWidth>420 ? 90 : 60)))) { bus.$emit('expandNowPlaying', true); } } }, clearClickTimeout() { if (this.clickTimer) { clearTimeout(this.clickTimer); this.clickTimer = undefined; } }, stopShowOverlayTimeout() { clearTimeout(this.showOverlayTimer); this.showOverlayTimer=undefined; }, resetShowOverlayTimeout() { this.showOverlay = true; clearTimeout(this.showOverlayTimer); this.showOverlayTimer = setTimeout(function () { this.clearShowOverlayTimeout(); }.bind(this), 3*1000); }, clearShowOverlayTimeout() { if (this.showOverlayTimer) { clearTimeout(this.showOverlayTimer); this.showOverlayTimer = undefined; this.showOverlay = false; this.touchStopped(); } }, touchStart(event) { if (event.srcElement.classList.contains("np-title") || event.srcElement.classList.contains("np-text") || event.srcElement.classList.contains("np-text-landscape")) { return; } if (this.$store.state.swipeVolume && !this.menu.show && event.touches && event.touches.length>0 && VOL_STD==this.playerStatus.dvc) { this.touch={x:event.touches[0].clientX, y:event.touches[0].clientY, moving:false}; this.lastSentVolume=-1; } }, touchEnd() { if (this.touch && this.touch.moving && this.overlayVolume>=0 && this.overlayVolume!=this.lastSentVolume && VOL_STD==this.playerStatus.dvc) { bus.$emit('playerCommand', ["mixer", "volume", this.overlayVolume]); } this.touchStopped(); }, touchStopped() { this.touch=undefined; this.overlayVolume=-1; this.lastSentVolume=-1; this.cancelSendVolumeTimer(); }, touchMoving(event) { if (undefined!=this.touch && VOL_STD==this.playerStatus.dvc) { if (Math.abs(event.touches[0].clientX-this.touch.x)<48) { if (!this.touch.moving && Math.abs(event.touches[0].clientY-this.touch.y)>10) { this.touch.moving=true; this.overlayVolume=Math.abs(this.volume); this.lastSentVolume=this.overlayVolume; } const VOL_STEP_PX = 25; if (Math.abs(event.touches[0].clientY-this.touch.y)>=VOL_STEP_PX) { var steps = Math.floor(Math.abs(event.touches[0].clientY-this.touch.y) / VOL_STEP_PX); if (steps>0) { var inc = event.touches[0].clientY100) { this.overlayVolume=100; break; } } this.touch.y += steps*VOL_STEP_PX*(inc ? -1 : 1); this.resetSendVolumeTimer(); } } } } }, cancelSendVolumeTimer() { if (undefined!==this.sendVolumeTimer) { clearTimeout(this.sendVolumeTimer); this.sendVolumeTimer = undefined; } }, resetSendVolumeTimer() { this.cancelSendVolumeTimer(); this.sendVolumeTimer = setTimeout(function () { if (this.overlayVolume!=this.lastSentVolume) { bus.$emit('playerCommand', ["mixer", "volume", this.overlayVolume]); this.lastSentVolume=this.overlayVolume; } }.bind(this), LMS_VOLUME_DEBOUNCE); }, checkWindowSize() { this.checkLandscape(); this.sizeCheckDelay = 0; if (window.innerHeight controls under whole width // wide=2 => controls under text only if (undefined==this.navPad) { let val = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue('--sab').replace('px', '')); this.navPad = undefined==val || isNaN(val) ? 0 : val; } let whRatio = window.innerWidth>1000 ? 0.575 : 0.5; let maxImgHeight = window.innerHeight - (this.$store.state.desktopLayout ? 50 : (this.navPad + 102)); let maxImgWidth = (window.innerWidth*whRatio)-32; this.landscape = window.innerWidth >= (window.innerHeight*queryParams.npRatio) && window.innerWidth>=450; this.wide = window.innerWidth>=600 && window.innerWidth>=(window.innerHeight*1.25) && maxImgWidth>=maxImgHeight ? 2 /*: window.innerHeight>340 ? 1*/ : 0; this.windowWidth = Math.floor(window.innerWidth / 25) * 25; bus.$emit('nowPlayingWide', this.wide); }, itemClicked(tab, section, index, event) { nowplayingItemClicked(this, tab, section, index, event); }, moreClicked(tab, section) { nowplayingMoreClicked(this, tab, section); }, toggleGrid(tab, section) { nowplayingToggleGrid(this, tab, section); }, controlBar(force) { let showNpBar = !this.disableBtns; if (showNpBar!=this.showNpBar || force) { let mbar = this.$store.state.mobileBar; document.documentElement.style.setProperty('--desktop-npbar-height', !showNpBar ? '0px' : this.desktopBarHeight); document.documentElement.style.setProperty('--mobile-npbar-height', !showNpBar || MBAR_NONE==mbar ? '0px' : (MBAR_THIN==mbar ? this.mobileBarThinHeight : (this.mobileBarThickHeight + (MBAR_REP_NAV==mbar ? this.bottomPad : 0) + "px"))); document.documentElement.style.setProperty('--npbar-border-color', !showNpBar ? 'transparent' : 'var(--bottom-toolbar-border-color)'); } }, showTimeTooltip() { this.startTooltipTimeout(); this.timeTooltip.show = true; }, hideTimeTooltip() { this.timeTooltip.show = false; this.cancelTooltipTimeout(); }, startTooltipTimeout() { this.cancelTooltipTimeout(); this.timeTooltip.timeout = setTimeout(function () { this.hideTimeTooltip(); }.bind(this), 2000); }, cancelTooltipTimeout() { if (undefined!==this.timeTooltip.timeout) { clearTimeout(this.timeTooltip.timeout); this.timeTooltip.timeout = undefined; } }, tabChanged(tab) { setLocalStorageVal("nptab", tab); }, showConfigMenu(event) { nowPlayingConfigMenu(this, event); }, tabTextEnd(event) { this.clearClickTimeout(); viewHandleSelectedText(this, event); }, setZoom(zoom) { let z = 1.0; if (undefined!=zoom && zoom>1.0 && zoom<=2.0) { z = zoom; } if (z==this.zoom) { return; } this.zoom = z; setLocalStorageVal("npInfoZoom", this.zoom); document.documentElement.style.setProperty('--np-zoom', this.zoom); document.documentElement.style.setProperty('--np-zoom-list', Math.min(this.zoom, 1.4)); }, headerClicked(ev, tab) { nowplayingMAIMenuClicked(this, ev, tab); } }, filters: { svgIcon: function (name, dark, header) { if (undefined!=header) { return "/material/svg/"+name+"?c="+getComputedStyle(document.getElementById("browse-view")).getPropertyValue("--active-color").replace("#", "")+"&r="+LMS_MATERIAL_REVISION; } return "/material/svg/"+name+"?c="+(dark ? LMS_DARK_SVG : LMS_LIGHT_SVG)+"&r="+LMS_MATERIAL_REVISION; }, emblem: function (e) { return "/material/svg/"+e.name+"?c="+e.color.substr(1)+"&r="+LMS_MATERIAL_REVISION; }, limitStr: function(str) { if (undefined==str || str.length<80) { return str; } return str.substring(0, 80) + "\u2026"; }, trackCount(current, total, sep) { if (undefined==current || undefined==total || total<2) { return ""; } return (undefined==sep ? "" : sep)+i18n("%1 of %2", (current+1), total); }, tooltip: function (str, key, showShortcut) { return showShortcut && undefined!=key ? ttShortcutStr(str, key, false, true) : str; } }, watch: { 'info.show': function(val) { // Indicate that dialog is/isn't shown, so that swipe is controlled bus.$emit('infoDialog', val); this.$store.commit('dialogOpen', {name:'info-dialog', shown:val}); this.setInfoTrack(); this.showInfo(); }, 'info.tab': function(tab) { this.showInfo(); }, 'info.showTabs': function() { setLocalStorageVal("showTabs", this.info.showTabs); }, 'info.sync': function() { setLocalStorageVal("syncInfo", this.info.sync); if (this.info.sync) { this.setInfoTrack(); this.showInfo(); } }, 'largeView': function(val) { if (val) { // Save current style so can reset when largeview disabled if (!this.before) { var elem = document.getElementById("np-bar"); if (elem) { this.before = elem.style; } } this.$nextTick(function () { this.page = document.getElementById("np-page"); }); } else { if (this.before) { this.$nextTick(function () { var elem = document.getElementById("np-bar"); if (elem) { elem.style = this.before; } }); } this.page = undefined; } bus.$emit('nowPlayingExpanded', val); }, 'menu.show': function(newVal) { this.$store.commit('menuVisible', {name:'nowplaying', shown:newVal}); if (newVal) { this.stopShowOverlayTimeout(); } else { this.menu.selection = undefined; clearTextSelection(); if (this.showOverlay) { this.resetShowOverlayTimeout(); } } }, 'disableBtns': function(newVal) { this.controlBar(); }, '$store.state.desktopLayout': function(newVal) { this.controlBar(true); } }, computed: { mobileBar() { return this.$store.state.mobileBar }, page() { return this.$store.state.page; }, techInfo() { return this.$store.state.techInfo && ( !this.$store.state.desktopLayout || this.largeView || !this.showRatings) && ( (!this.repAltBtn.show && !this.shuffAltBtn.show) || !this.$store.state.desktopLayout || this.largeView ) }, technicalInfo() { return undefined==this.playerStatus.current.technicalInfo || this.playerStatus.current.length==0 ? undefined : undefined==this.playerStatus.current.source || this.playerStatus.current.source.other || undefined==this.playerStatus.current.source.text || this.playerStatus.current.source.text.length<1 ? this.playerStatus.current.technicalInfo : (this.playerStatus.current.source.text+SEPARATOR+this.playerStatus.current.technicalInfo); }, formattedTime() { return this.playerStatus && this.playerStatus.current ? !this.showTotal && undefined!=this.playerStatus.current.time && this.playerStatus.current.duration>0 ? formatSeconds(Math.floor(this.playerStatus.current.time))+" / -"+ formatSeconds(Math.floor(this.playerStatus.current.duration-this.playerStatus.current.time)) : (undefined!=this.playerStatus.current.time ? formatSeconds(Math.floor(this.playerStatus.current.time)) : "") + (undefined!=this.playerStatus.current.time && this.playerStatus.current.duration>0 ? " / " : "") + (this.playerStatus.current.duration>0 ? formatSeconds(Math.floor(this.playerStatus.current.duration)) : "") : undefined; }, darkUi() { return this.$store.state.darkUi }, npBarRatings() { if (!this.playerStatus || !this.playerStatus.current) { return false; } if (this.repAltBtn.show || this.shuffAltBtn.show) { return true; // Use same space for these... } return this.showRatings; }, showRatings() { return LMS_STATS_ENABLED && this.$store.state.showRating && this.playerStatus && this.playerStatus.current && this.playerStatus.current.duration && this.playerStatus.current.duration>0 && undefined!=this.playerStatus.current.id && !(""+this.playerStatus.current.id).startsWith("-"); }, maxRating() { return this.$store.state.maxRating }, title() { if (this.$store.state.nowPlayingTrackNum && this.playerStatus.current.tracknum) { return formatTrackNum(this.playerStatus.current)+SEPARATOR+trackTitle(this.playerStatus.current); } return trackTitle(this.playerStatus.current); }, desktopLayout() { return this.$store.state.desktopLayout }, totalTogglesQueue() { return this.$store.state.desktopLayout && !this.$store.state.pinQueue }, pinQueue() { return this.$store.state.pinQueue }, showQueue() { return this.$store.state.showQueue }, noPlayer() { return !this.$store.state.player }, drawBgndImage() { return this.$store.state.nowPlayingBackdrop && undefined!=this.coverUrl && LMS_BLANK_COVER!=this.coverUrl && DEFAULT_COVER!=this.coverUrl && DEFAULT_RADIO_COVER!=this.coverUrl }, drawBackdrop() { return !this.drawBgndImage && this.$store.state.nowPlayingBackdrop && this.$store.state.useDefaultBackdrops }, drawInfoBgndImage() { return this.$store.state.infoBackdrop && undefined!=this.coverUrl && LMS_BLANK_COVER!=this.coverUrl && DEFAULT_COVER!=this.coverUrl && DEFAULT_RADIO_COVER!=this.coverUrl && this.showBgnd }, drawInfoBackdrop() { return !this.drawInfoBgndImage && this.$store.state.infoBackdrop && this.$store.state.useDefaultBackdrops && this.showBgnd }, coloredToolbars() { return this.$store.state.desktopLayout && this.$store.state.coloredToolbars }, keyboardControl() { return this.$store.state.keyboardControl && !IS_MOBILE }, transCvr() { return undefined!=this.coverUrl && (this.coverUrl.includes(DEFAULT_COVER) || this.coverUrl.includes(LMS_BLANK_COVER) || this.coverUrl.includes(DEFAULT_RADIO_COVER)) }, artistAndComposerLine() { return this.$store.state.nowPlayingContext && undefined!=this.playerStatus.current.artistAndComposerWithContext ? this.playerStatus.current.artistAndComposerWithContext : this.playerStatus.current.artistAndComposer }, albumLine() { return this.$store.state.nowPlayingContext && undefined!=this.playerStatus.current.artistAndComposerWithContext && !isEmpty(this.playerStatus.current.albumLine) ? i18n('from %1', this.playerStatus.current.albumLine).replaceAll("", "") : this.playerStatus.current.albumLine }, barInfoWithContext() { if (this.$store.state.nowPlayingContext && this.windowWidth-(this.desktopLayout ? (this.techInfo && this.technicalInfo ? 570 : 450) : 240)>this.barInfoWithContextWidth && undefined!=this.playerStatus.current.artistAndComposerWithContext && this.albumLine) { return replaceBr(this.artistAndComposerLine, " ")+" " + this.albumLine } return undefined; }, skipBSeconds() { return this.$store.state.skipBSeconds }, skipFSeconds() { return this.$store.state.skipFSeconds }, nowPlayingFull() { return this.$store.state.nowPlayingFull && !this.info.show && ( this.$store.state.desktopLayout ? this.largeView : this.$store.state.page == 'now-playing') }, progressBuffer() { return this.playerStatus.current.bufpc<99 ? this.playerStatus.current.bufpc : 0 }, lyricsTimesValid() { // Lyrics timing positions are only valid if time of first line >= 0 // ...see comment in nowplayingFetchTrackInfo return this.info.tabs[TRACK_TAB].lines && this.info.tabs[TRACK_TAB].lines[0].time>=0; } }, beforeDestroy() { this.stopPositionInterval(); this.stopLiveEdgeInterval(); this.clearClickTimeout(); this.cancelTooltipTimeout(); this.clearShowOverlayTimeout(); } });