Bug fixes, better lyrics, sorting

This commit is contained in:
exttex 2020-09-28 18:17:23 +02:00
parent 83860ff052
commit 80f6cbf870
14 changed files with 186 additions and 34 deletions

BIN
app/assets/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

BIN
app/assets/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

BIN
app/assets/skip-next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

View File

@ -7,6 +7,7 @@ let tray;
let settings; let settings;
let shouldExit = false; let shouldExit = false;
let playing = false;
//Get path to asset //Get path to asset
function assetPath(a) { function assetPath(a) {
@ -74,26 +75,38 @@ async function createWindow() {
return false; return false;
}); });
//Thumbnail Toolbars
setThumbarButtons();
} }
//Create window //Create window
app.on('ready', async () => { app.on('ready', async () => {
createWindow(); createWindow();
//Create tray //Create Tray
tray = new Tray(assetPath("icon-taskbar.png")); tray = new Tray(assetPath("icon-taskbar.png"));
tray.on('double-click', () => win.show()); tray.on('double-click', () => win.show());
tray.on('click', () => win.show()); tray.on('click', () => win.show());
//Tray menu setTray();
});
//Update tray context menu
function setTray() {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
label: 'Restore', label: 'Restore',
type: 'normal', type: 'normal',
click: () => win.show() click: () => win.show()
}, },
playing ?
{ {
label: 'Play/Pause', label: 'Pause',
type: 'normal',
click: () => win.webContents.send('togglePlayback')
}
: {
label: 'Play',
type: 'normal', type: 'normal',
click: () => win.webContents.send('togglePlayback') click: () => win.webContents.send('togglePlayback')
}, },
@ -117,6 +130,42 @@ app.on('ready', async () => {
} }
]); ]);
tray.setContextMenu(contextMenu); tray.setContextMenu(contextMenu);
}
//Update Thumbnail Toolbars (Windows)
function setThumbarButtons() {
win.setThumbarButtons([
{
tooltip: 'Skip Previous',
icon: assetPath('skip-previous.png'),
click: () => win.webContents.send('skipPrev')
},
//Play/Pause
playing ?
{
tooltip: 'Pause',
icon: assetPath('pause.png'),
click: () => win.webContents.send('togglePlayback')
} :
{
tooltip: 'Play',
icon: assetPath('play.png'),
click: () => win.webContents.send('togglePlayback')
},
//Skip next
{
tooltip: 'Skip Next',
icon: assetPath('skip-next.png'),
click: () => win.webContents.send('skipNext')
},
]);
}
//Playing state change from UI
ipcMain.on('playing', (event, args) => {
playing = args;
setThumbarButtons();
setTray();
}); });
//Update settings from ui //Update settings from ui

View File

@ -371,8 +371,8 @@ export default {
}); });
// /search // /search
document.addEventListener('keypress', (event) => { document.addEventListener('keypress', (e) => {
if (event.keyCode != 47) return; if (e.keyCode != 47) return;
this.$refs.searchBar.focus(); this.$refs.searchBar.focus();
setTimeout(() => { setTimeout(() => {
if (this.searchQuery.startsWith('/')) this.searchQuery = this.searchQuery.substring(1); if (this.searchQuery.startsWith('/')) this.searchQuery = this.searchQuery.substring(1);
@ -394,6 +394,9 @@ export default {
if (this.$root.audio) this.$root.audio.volume = this.volume; if (this.$root.audio) this.$root.audio.volume = this.volume;
this.$root.volume = this.volume; this.$root.volume = this.volume;
}, },
'$root.volume'() {
this.volume = this.$root.volume;
},
//Update position //Update position
'$root.position'() { '$root.position'() {
this.position = (this.$root.position / this.$root.duration()) * 100; this.position = (this.$root.position / this.$root.duration()) * 100;

View File

@ -1,19 +1,35 @@
<template> <template>
<v-list :height='height' class='overflow-y-auto' v-scroll.self='scroll'> <div v-scroll.self='scroll'>
<v-lazy <div class='px-4 pt-2 d-flex' style='max-height: 50px;'>
v-for='(track, index) in tracks' <div class='text-overline px-2 pt-1'>
:key='index + "t" + track.id' {{count}} TRACKS.
max-height="100" </div>
><TrackTile :track='track' @click='play(index)' @remove='removedTrack(index)'> <div style="max-width: 200px;" class='d-flex mx-2'>
</TrackTile> <v-select class='px-2' dense solo :items='sortTypes' @change='sort' label='Sort By'>
</v-lazy> </v-select>
</div>
<div class='text-center' v-if='loading'> <div class='px-2' @click='reverseSort'>
<v-progress-circular indeterminate></v-progress-circular> <v-btn icon>
<v-icon v-if='isReversed'>mdi-sort-reverse-variant</v-icon>
<v-icon v-if='!isReversed'>mdi-sort-variant</v-icon>
</v-btn>
</div>
</div> </div>
<v-list :height='height' class='overflow-y-auto'>
<v-lazy
v-for='(track, index) in tracks'
:key='index + "t" + track.id'
max-height="100"
><TrackTile :track='track' @click='play(index)' @remove='removedTrack(index)'>
</TrackTile>
</v-lazy>
</v-list> <div class='text-center' v-if='loading'>
<v-progress-circular indeterminate></v-progress-circular>
</div>
</v-list>
</div>
</template> </template>
<script> <script>
@ -28,7 +44,15 @@ export default {
return { return {
loading: false, loading: false,
tracks: [], tracks: [],
count: 0 count: 0,
sortTypes: [
'Date Added',
'Name (A-Z)',
'Artist (A-Z)',
'Album (A-Z)'
],
tracksUnsorted: null,
isReversed: false
} }
}, },
props: { props: {
@ -88,8 +112,44 @@ export default {
this.$root.replaceQueue(this.tracks); this.$root.replaceQueue(this.tracks);
}); });
} }
},
//Sort changed
async sort(type) {
let index = this.sortTypes.indexOf(type);
//Preload all tracks
if (this.tracks.length < this.count)
await this.loadAll();
//Copy original
if (!this.tracksUnsorted)
this.tracksUnsorted = JSON.parse(JSON.stringify(this.tracks));
//Using indexes, so it can be translated later
this.isReversed = false;
switch (index) {
//Default
case 0:
this.tracks = JSON.parse(JSON.stringify(this.tracksUnsorted));
break;
//Name
case 1:
this.tracks = this.tracks.sort((a, b) => {return a.title.localeCompare(b.title);});
break;
//Artist
case 2:
this.tracks = this.tracks.sort((a, b) => {return a.artistString.localeCompare(b.artistString);});
break;
//Album
case 3:
this.tracks = this.tracks.sort((a, b) => {return a.album.title.localeCompare(b.album.title);});
break;
}
},
async reverseSort() {
//Preload tracks if not sorted yet
if (this.tracks.length < this.count)
await this.sort(0);
this.isReversed = !this.isReversed;
this.tracks.reverse();
}, },
removedTrack(index) { removedTrack(index) {
this.tracks.splice(index, 1); this.tracks.splice(index, 1);

View File

@ -5,7 +5,12 @@
</div> </div>
<div v-if='!loading && lyrics && lyrics.lyrics.length > 0' class='text-center'> <div v-if='!loading && lyrics && lyrics.lyrics.length > 0' class='text-center'>
<div v-for='(lyric, index) in lyrics.lyrics' :key='lyric.offset' class='my-8 mx-4'> <div
v-for='(lyric, index) in lyrics.lyrics'
:key='lyric.offset'
class='my-6 mx-4 pa-2 rounded'
:class='{"grey darken-3": playingNow(index)}'
@click='seekTo(index)'>
<span <span
class='my-8' class='my-8'
:class='{"text-h6 font-weight-regular": !playingNow(index), "text-h5 font-weight-bold": playingNow(index)}' :class='{"text-h6 font-weight-regular": !playingNow(index), "text-h5 font-weight-bold": playingNow(index)}'
@ -27,7 +32,7 @@
</div> </div>
<!-- Error --> <!-- Error -->
<div v-if='!loading && !lyrics && lyrics.text.length == 0 && lyrics.lyrics.length == 0' class='pa-4 text-center'> <div v-if='!loading && (!lyrics || (lyrics.text.length == 0 && lyrics.lyrics.length == 0))' class='pa-4 text-center'>
<span class='red--text text-h5'> <span class='red--text text-h5'>
Error loading lyrics or lyrics not found! Error loading lyrics or lyrics not found!
</span> </span>
@ -94,10 +99,15 @@ export default {
//Roughly middle //Roughly middle
let offset = window.innerHeight / 2 - 500; let offset = window.innerHeight / 2 - 500;
if (!this.$refs["l"+this.currentLyricIndex]) return;
this.$refs.content.scrollTo({ this.$refs.content.scrollTo({
top: this.$refs["l"+this.currentLyricIndex][0].offsetTop + offset, top: this.$refs["l"+this.currentLyricIndex][0].offsetTop + offset,
behavior: 'smooth' behavior: 'smooth'
}); });
},
//Seek to lyric in song
seekTo(i) {
this.$root.seek(this.lyrics.lyrics[i].offset);
} }
}, },
mounted() { mounted() {

View File

@ -177,7 +177,7 @@ export default {
addLibrary() { addLibrary() {
this.isLibrary = true; this.isLibrary = true;
this.$root.libraryTracks.push(this.track.id); this.$root.libraryTracks.push(this.track.id);
this.$axios.put(`/library/tracks?id=${this.track.id}`); this.$axios.put(`/library/track?id=${this.track.id}`);
}, },
goAlbum() { goAlbum() {
this.$emit('redirect') this.$emit('redirect')
@ -196,7 +196,7 @@ export default {
async removeLibrary() { async removeLibrary() {
this.isLibrary = false; this.isLibrary = false;
this.$root.libraryTracks.splice(this.$root.libraryTracks.indexOf(this.track.id), 1); this.$root.libraryTracks.splice(this.$root.libraryTracks.indexOf(this.track.id), 1);
await this.$axios.delete(`/library/tracks?id=${this.track.id}`); await this.$axios.delete(`/library/track?id=${this.track.id}`);
this.$emit('remove'); this.$emit('remove');
}, },
//Remove from playlist //Remove from playlist

View File

@ -213,6 +213,8 @@ new Vue({
this.configureAudio(); this.configureAudio();
this.state = 1; this.state = 1;
if (autoplay) this.play(); if (autoplay) this.play();
//MediaSession
this.updateMediaSession();
//Loads more tracks if end of list //Loads more tracks if end of list
this.loadSTL(); this.loadSTL();
@ -275,6 +277,7 @@ new Vue({
//Play //Play
this.state = 2; this.state = 2;
this.audio.play(); this.audio.play();
this.updateMediaSession();
await this.savePlaybackInfo(); await this.savePlaybackInfo();
return; return;
} }
@ -282,7 +285,6 @@ new Vue({
this.skip(1); this.skip(1);
this.savePlaybackInfo(); this.savePlaybackInfo();
}); });
this.updateMediaSession();
}, },
//Update media session with current track metadata //Update media session with current track metadata
updateMediaSession() { updateMediaSession() {
@ -417,6 +419,12 @@ new Vue({
state: this.state, state: this.state,
track: this.track track: this.track
}); });
//Update in electron
if (this.settings.electron) {
const {ipcRenderer} = window.require('electron');
ipcRenderer.send('playing', this.state == 2);
}
} }
}, },
async created() { async created() {
@ -511,17 +519,40 @@ new Vue({
//Don't handle keystrokes in text fields //Don't handle keystrokes in text fields
if (e.target.tagName == "INPUT") return; if (e.target.tagName == "INPUT") return;
//K toggle playback //K toggle playback
//e.keyCode === 32 if (e.code == "KeyK" || e.code == "Space") this.$root.toggle();
if (e.keyCode === 75 || e.keyCode === 107) this.$root.toggle();
//L +10s (from YT) //L +10s (from YT)
if (e.keyCode === 108 || e.keyCode === 76) this.$root.seek((this.position + 10000)); if (e.code == "KeyL") this.$root.seek((this.position + 10000));
//J -10s (from YT) //J -10s (from YT)
if (e.keyCode === 106 || e.keyCode === 74) this.$root.seek((this.position - 10000)); if (e.code == "KeyJ") this.$root.seek((this.position - 10000));
//-> +5s (from YT)
if (e.code == "ArrowRight") this.$root.seek((this.position + 5000));
//<- -5s (from YT)
if (e.code == "ArrowLeft") this.$root.seek((this.position - 5000));
// ^ v - Volume
if (e.code == 'ArrowUp' && this.audio) {
if ((this.audio.volume + 0.05) > 1) {
this.audio.volume = 1.00;
this.volume = 1.00;
return;
}
this.audio.volume += 0.05;
this.volume = this.audio.volume;
}
if (e.code == 'ArrowDown' && this.audio) {
if ((this.audio.volume - 0.05) < 0) {
this.audio.volume = 0.00;
this.volume = 0.00;
return;
}
this.audio.volume -= 0.05;
this.volume = this.audio.volume;
}
}); });
}, },
watch: { watch: {
//Watch state for integrations //Watch state for integrations
state() { state() {
this.updateMediaSession();
this.updateState(); this.updateState();
} }
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "freezer", "name": "freezer",
"private": true, "private": true,
"version": "1.0.6", "version": "1.0.7",
"description": "", "description": "",
"main": "background.js", "main": "background.js",
"scripts": { "scripts": {

View File

@ -192,7 +192,7 @@ class DeezerAPI {
url: `/stream/${info}?q=9` url: `/stream/${info}?q=9`
} }
} catch (e) { } catch (e) {
logger.warning('Qualiy fallback: ' + e); logger.warn('Qualiy fallback: ' + e);
//Fallback //Fallback
//9 - FLAC //9 - FLAC
//3 - MP3 320 //3 - MP3 320

View File

@ -80,7 +80,7 @@ class Integrations extends EventEmitter {
//Always accept join requests //Always accept join requests
this.discordRPC.subscribe('ACTIVITY_JOIN_REQUEST', (user) => { this.discordRPC.subscribe('ACTIVITY_JOIN_REQUEST', (user) => {
this.discordRPC.sendJoinInvite(user.user).catch((e) => { this.discordRPC.sendJoinInvite(user.user).catch((e) => {
logger.warning('Unable to accept Discord invite: ' + e); logger.warn('Unable to accept Discord invite: ' + e);
}); });
}); });
//Joined //Joined
@ -93,7 +93,6 @@ class Integrations extends EventEmitter {
}); });
//Connect to discord //Connect to discord
this.discordRPC.login({clientId: CLIENTID}).catch(() => { this.discordRPC.login({clientId: CLIENTID}).catch(() => {
logger.info('Error connecting to Discord!');
//Wait 5s to retry //Wait 5s to retry
setTimeout(() => { setTimeout(() => {
if (!this.discordReady) if (!this.discordReady)

View File

@ -1,7 +1,7 @@
{ {
"name": "freezer", "name": "freezer",
"private": true, "private": true,
"version": "1.0.6", "version": "1.0.7",
"description": "", "description": "",
"scripts": { "scripts": {
"pack": "electron-builder --dir", "pack": "electron-builder --dir",