Added track preview funciton
This commit is contained in:
parent
c8625db3e0
commit
c4e45462bd
22
public/css/modules/track-preview.css
Normal file
22
public/css/modules/track-preview.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.preview_controls {
|
||||||
|
opacity: 0;
|
||||||
|
display: block;
|
||||||
|
background: rgba(0, 0, 0, .5);
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 56px;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 5px;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.preview_playlist_controls{
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.single-cover {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
}
|
@ -14,3 +14,4 @@
|
|||||||
@import './modules/settings.css';
|
@import './modules/settings.css';
|
||||||
@import './modules/main-search.css';
|
@import './modules/main-search.css';
|
||||||
@import './modules/download-tab.css';
|
@import './modules/download-tab.css';
|
||||||
|
@import './modules/track-preview.css';
|
||||||
|
@ -16,19 +16,19 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<aside id="sidebar" role="navigation">
|
<aside id="sidebar" role="navigation">
|
||||||
<i class="material-icons side_icon">menu</i>
|
<span id="main_home_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
||||||
<span id="main_search_tablink" class="main_tablinks" role="link" aria-label="home"><i></i></span>
|
class="material-icons side_icon">home</i><span class="main_tablinks_text">Home</span></span>
|
||||||
<span id="main_home_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_search_tablink" class="main_tablinks" role="link" aria-label="search"><i
|
||||||
class="material-icons side_icon">home</i><span class="main_tablinks_text">Home</span></span>
|
class="material-icons side_icon">search</i><span class="main_tablinks_text">Search</span></span>
|
||||||
<span id="main_charts_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_charts_tablink" class="main_tablinks" role="link" aria-label="charts"><i
|
||||||
class="material-icons side_icon">bubble_chart</i><span class="main_tablinks_text">Charts</span></span>
|
class="material-icons side_icon">bubble_chart</i><span class="main_tablinks_text">Charts</span></span>
|
||||||
<span id="main_favorites_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_favorites_tablink" class="main_tablinks" role="link" aria-label="favorites"><i
|
||||||
class="material-icons side_icon">album</i><span class="main_tablinks_text">Favorites</span></span>
|
class="material-icons side_icon">album</i><span class="main_tablinks_text">Favorites</span></span>
|
||||||
<span id="main_analyzer_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_analyzer_tablink" class="main_tablinks" role="link" aria-label="link analyzer"><i
|
||||||
class="material-icons side_icon">link</i><span class="main_tablinks_text">Link Analyzer</span></span>
|
class="material-icons side_icon">link</i><span class="main_tablinks_text">Link Analyzer</span></span>
|
||||||
<span id="main_settings_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_settings_tablink" class="main_tablinks" role="link" aria-label="settings"><i
|
||||||
class="material-icons side_icon">settings</i><span class="main_tablinks_text">Settings</span></span>
|
class="material-icons side_icon">settings</i><span class="main_tablinks_text">Settings</span></span>
|
||||||
<span id="main_about_tablink" class="main_tablinks" role="link" aria-label="home"><i
|
<span id="main_about_tablink" class="main_tablinks" role="link" aria-label="info"><i
|
||||||
class="material-icons side_icon">info</i><span class="main_tablinks_text">Info</span></span>
|
class="material-icons side_icon">info</i><span class="main_tablinks_text">Info</span></span>
|
||||||
</aside>
|
</aside>
|
||||||
<main id="main_content">
|
<main id="main_content">
|
||||||
@ -159,8 +159,9 @@ <h1>No Tracks found</h1>
|
|||||||
<th style="width: 56px;"></th>
|
<th style="width: 56px;"></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="track in results.trackTab.data" class="track_row">
|
<tr v-for="track in results.trackTab.data" class="track_row">
|
||||||
<td style="width: 48px; text-align: center;"><img class="rounded coverart"
|
<td style="width: 48px; text-align: center;">
|
||||||
v-bind:src="track.album.cover_small"></td>
|
<a href="#" @click="playPausePreview" v-bind:class="'rounded' + (track.preview ? ' single-cover' : '')" v-bind:data-preview="track.preview"><i @mouseenter="previewMouseEnter" @mouseleave="previewMouseLeave" v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img class="rounded coverart" v-bind:src="track.album.cover_small">
|
||||||
|
</td>
|
||||||
<td class="breakline">{{track.title + (track.title_version ? ' '+track.title_version : '')}}</td>
|
<td class="breakline">{{track.title + (track.title_version ? ' '+track.title_version : '')}}</td>
|
||||||
<td class="breakline clickable" @click="artistView" v-bind:data-id="track.artist.id">
|
<td class="breakline clickable" @click="artistView" v-bind:data-id="track.artist.id">
|
||||||
{{track.artist.name}}</td>
|
{{track.artist.name}}</td>
|
||||||
@ -533,11 +534,10 @@ <h2 class="inline-flex"><span v-if="metadata">{{ metadata }}</span><span class="
|
|||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="track in body">
|
<template v-for="track in body">
|
||||||
<tr v-if="track.type == 'track'">
|
<tr v-if="track.type == 'track'">
|
||||||
<td><i class="material-icons">play_arrow</i></td>
|
<td><i @click=playPausePreview v-bind:class="'material-icons' + (track.preview ? ' preview_playlist_controls' : '')" v-bind:data-preview="track.preview">play_arrow</i></td>
|
||||||
<td>{{ track.track_position }}</td>
|
<td>{{ track.track_position }}</td>
|
||||||
<td class="inline-flex"><i v-if="track.explicit_lyrics"
|
<td class="inline-flex"><i v-if="track.explicit_lyrics"
|
||||||
class="material-icons">explicit</i>{{ track.title }} <span
|
class="material-icons">explicit</i>{{ track.title + (track.title_version && track.title.indexOf(track.title_version) == -1 ? ' '+track.title_version : '') }} </td>
|
||||||
v-if="track.title_version">{{track.title_version}}</span></td>
|
|
||||||
<td class="clickable" @click="artistView" v-bind:data-id="track.artist.id">
|
<td class="clickable" @click="artistView" v-bind:data-id="track.artist.id">
|
||||||
{{ track.artist.name }}</td>
|
{{ track.artist.name }}</td>
|
||||||
<td class="clickable" v-if="type == 'Playlist'" @click="albumView" v-bind:data-id="track.album.id">
|
<td class="clickable" v-if="type == 'Playlist'" @click="albumView" v-bind:data-id="track.album.id">
|
||||||
@ -578,7 +578,12 @@ <h2 class="inline-flex"><span v-if="metadata">{{ metadata }}</span><span class="
|
|||||||
<div id="download_list"></div>
|
<div id="download_list"></div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div id="modal_quality" class="smallmodal">
|
|
||||||
|
<audio id="preview-track">
|
||||||
|
<source id="preview-track_source" src="" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
|
||||||
|
<div id="modal_quality" class="smallmodal">
|
||||||
<!-- Modal content -->
|
<!-- Modal content -->
|
||||||
<div class="smallmodal-content">
|
<div class="smallmodal-content">
|
||||||
<button class="quality-button" data-quality-value="9">Download FLAC</button><br>
|
<button class="quality-button" data-quality-value="9">Download FLAC</button><br>
|
||||||
@ -598,4 +603,4 @@ <h2 class="inline-flex"><span v-if="metadata">{{ metadata }}</span><span class="
|
|||||||
|
|
||||||
<script type="module" src="/public/js/app.js"></script>
|
<script type="module" src="/public/js/app.js"></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,6 +4,7 @@ import Downloads from './modules/downloads.js'
|
|||||||
import QualityModal from './modules/quality-modal.js'
|
import QualityModal from './modules/quality-modal.js'
|
||||||
import { Tabs } from './modules/tabs.js'
|
import { Tabs } from './modules/tabs.js'
|
||||||
import Search from './modules/search.js'
|
import Search from './modules/search.js'
|
||||||
|
import { initTrackPreview } from './modules/track-preview.js'
|
||||||
|
|
||||||
/* ===== Socketio listeners ===== */
|
/* ===== Socketio listeners ===== */
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ function startApp() {
|
|||||||
QualityModal.init()
|
QualityModal.init()
|
||||||
Tabs.linkListeners()
|
Tabs.linkListeners()
|
||||||
Search.linkListeners()
|
Search.linkListeners()
|
||||||
|
initTrackPreview()
|
||||||
|
|
||||||
if (localStorage.getItem('arl')) {
|
if (localStorage.getItem('arl')) {
|
||||||
let arl = localStorage.getItem('arl')
|
let arl = localStorage.getItem('arl')
|
||||||
|
@ -2,6 +2,7 @@ import { socket } from './socket.js'
|
|||||||
import { artistView, albumView, playlistView } from './tabs.js'
|
import { artistView, albumView, playlistView } from './tabs.js'
|
||||||
import Downloads from './downloads.js'
|
import Downloads from './downloads.js'
|
||||||
import QualityModal from './quality-modal.js'
|
import QualityModal from './quality-modal.js'
|
||||||
|
import { playPausePreview, previewMouseEnter, previewMouseLeave } from './track-preview.js'
|
||||||
|
|
||||||
const MainSearch = new Vue({
|
const MainSearch = new Vue({
|
||||||
data: {
|
data: {
|
||||||
@ -52,6 +53,9 @@ const MainSearch = new Vue({
|
|||||||
artistView,
|
artistView,
|
||||||
albumView,
|
albumView,
|
||||||
playlistView,
|
playlistView,
|
||||||
|
playPausePreview,
|
||||||
|
previewMouseEnter,
|
||||||
|
previewMouseLeave,
|
||||||
handleClickTopResult(event) {
|
handleClickTopResult(event) {
|
||||||
let topResultType = this.results.allTab.TOP_RESULT[0].type
|
let topResultType = this.results.allTab.TOP_RESULT[0].type
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import TracklistTab from './tracklist-tab.js'
|
|||||||
import { socket } from './socket.js'
|
import { socket } from './socket.js'
|
||||||
import SettingsTab from './settings-tab.js'
|
import SettingsTab from './settings-tab.js'
|
||||||
import MainSearch from './main-search.js'
|
import MainSearch from './main-search.js'
|
||||||
|
import { stopStackedTabsPreview } from './track-preview.js'
|
||||||
|
|
||||||
/* ===== Globals ====== */
|
/* ===== Globals ====== */
|
||||||
window.search_selected = ''
|
window.search_selected = ''
|
||||||
@ -182,6 +183,7 @@ function showTab(type, id, back = false) {
|
|||||||
tabcontent[i].style.display = 'none'
|
tabcontent[i].style.display = 'none'
|
||||||
}
|
}
|
||||||
document.getElementById(tab).style.display = 'block'
|
document.getElementById(tab).style.display = 'block'
|
||||||
|
stopStackedTabsPreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses:
|
// Uses:
|
||||||
@ -204,4 +206,5 @@ function backTab() {
|
|||||||
socket.emit('getTracklist', { type: tabObj.type, id: tabObj.id })
|
socket.emit('getTracklist', { type: tabObj.type, id: tabObj.id })
|
||||||
showTab(tabObj.type, tabObj.id, true)
|
showTab(tabObj.type, tabObj.id, true)
|
||||||
}
|
}
|
||||||
|
stopStackedTabsPreview()
|
||||||
}
|
}
|
||||||
|
91
public/js/modules/track-preview.js
Normal file
91
public/js/modules/track-preview.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
/* ===== Globals ====== */
|
||||||
|
window.preview_max_volume = 1
|
||||||
|
|
||||||
|
/* ===== Locals ===== */
|
||||||
|
let preview_track = document.getElementById('preview-track')
|
||||||
|
let preview_stopped = true
|
||||||
|
|
||||||
|
// init stuff
|
||||||
|
export function initTrackPreview(){
|
||||||
|
preview_track.volume = 1
|
||||||
|
/*preview_max_volume = parseFloat(localStorage.getItem("previewVolume"))
|
||||||
|
if (preview_max_volume === null){
|
||||||
|
preview_max_volume = 0.8
|
||||||
|
localStorage.setItem("previewVolume", preview_max_volume)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// start playing when track loaded
|
||||||
|
preview_track.addEventListener('canplay', function(){
|
||||||
|
preview_track.play()
|
||||||
|
preview_stopped = false
|
||||||
|
$(preview_track).animate({volume: preview_max_volume}, 500)
|
||||||
|
})
|
||||||
|
|
||||||
|
// auto fadeout when at the end of the song
|
||||||
|
preview_track.addEventListener('timeupdate', function(){
|
||||||
|
if (preview_track.currentTime > preview_track.duration-1){
|
||||||
|
$(preview_track).animate({volume: 0}, 800)
|
||||||
|
preview_stopped = true
|
||||||
|
$('a[playing] > .preview_controls').css({opacity:0})
|
||||||
|
$("*").removeAttr("playing")
|
||||||
|
$('.preview_controls').text("play_arrow")
|
||||||
|
$('.preview_playlist_controls').text("play_arrow")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// on modal closing
|
||||||
|
export function stopStackedTabsPreview(){
|
||||||
|
if ($('.preview_playlist_controls').filter(function(){return $(this).attr("playing")}).length > 0){
|
||||||
|
$(preview_track).animate({volume: 0}, 800)
|
||||||
|
preview_stopped = true
|
||||||
|
$(".preview_playlist_controls").removeAttr("playing")
|
||||||
|
$('.preview_playlist_controls').text("play_arrow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on hover event
|
||||||
|
export function previewMouseEnter(e){
|
||||||
|
$(e.currentTarget).css({opacity: 1})
|
||||||
|
}
|
||||||
|
export function previewMouseLeave(e){
|
||||||
|
let obj = e.currentTarget
|
||||||
|
if (($(obj).parent().attr("playing") && preview_stopped) || !$(obj).parent().attr("playing")){
|
||||||
|
$(obj).css({opacity: 0}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on click event
|
||||||
|
export function playPausePreview(e){
|
||||||
|
e.preventDefault()
|
||||||
|
console.log("PlayPause")
|
||||||
|
let obj = e.currentTarget
|
||||||
|
var icon = (obj.tagName == "I" ? $(obj) : $(obj).children('i'))
|
||||||
|
if ($(obj).attr("playing")){
|
||||||
|
if (preview_track.paused){
|
||||||
|
preview_track.play()
|
||||||
|
preview_stopped = false
|
||||||
|
icon.text("pause")
|
||||||
|
$(preview_track).animate({volume: preview_max_volume}, 500)
|
||||||
|
}else{
|
||||||
|
preview_stopped = true
|
||||||
|
icon.text("play_arrow")
|
||||||
|
$(preview_track).animate({volume: 0}, 250, "swing", ()=>{ preview_track.pause() })
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$("*").removeAttr("playing")
|
||||||
|
$(obj).attr("playing",true)
|
||||||
|
$('.preview_controls').text("play_arrow")
|
||||||
|
$('.preview_playlist_controls').text("play_arrow")
|
||||||
|
$('.preview_controls').css({opacity:0})
|
||||||
|
icon.text("pause")
|
||||||
|
icon.css({opacity: 1})
|
||||||
|
preview_stopped = false
|
||||||
|
$(preview_track).animate({volume: 0}, 250, "swing", ()=>{
|
||||||
|
preview_track.pause()
|
||||||
|
$('#preview-track_source').prop("src", $(obj).data("preview"))
|
||||||
|
preview_track.load()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { socket } from './socket.js'
|
|||||||
import { artistView, albumView } from './tabs.js'
|
import { artistView, albumView } from './tabs.js'
|
||||||
import Downloads from './downloads.js'
|
import Downloads from './downloads.js'
|
||||||
import QualityModal from './quality-modal.js'
|
import QualityModal from './quality-modal.js'
|
||||||
|
import { playPausePreview } from './track-preview.js'
|
||||||
|
|
||||||
const TracklistTab = new Vue({
|
const TracklistTab = new Vue({
|
||||||
data: {
|
data: {
|
||||||
@ -19,6 +20,7 @@ const TracklistTab = new Vue({
|
|||||||
methods: {
|
methods: {
|
||||||
artistView,
|
artistView,
|
||||||
albumView,
|
albumView,
|
||||||
|
playPausePreview,
|
||||||
reset() {
|
reset() {
|
||||||
this.title = 'Loading...'
|
this.title = 'Loading...'
|
||||||
this.image = ''
|
this.image = ''
|
||||||
|
Loading…
Reference in New Issue
Block a user