1.0.6 - Discord integration, logging, bugfixes

This commit is contained in:
exttex
2020-09-28 12:04:19 +02:00
parent 863c1aff40
commit 83860ff052
23 changed files with 1648 additions and 129 deletions

View File

@ -124,18 +124,24 @@
<v-icon>mdi-arrow-right</v-icon>
</v-btn>
<v-text-field
hide-details
flat
prepend-inner-icon="mdi-magnify"
<!-- Search -->
<v-autocomplete
hide-details
prepend-inner-icon="mdi-magnify"
flat
single-line
solo
clearable
hide-no-data
placeholder='Search or paste Deezer URL. Use "/" to quickly focus.'
v-model="searchQuery"
ref='searchBar'
:loading='searchLoading'
@keyup='search'>
</v-text-field>
@keyup='search'
ref='searchBar'
v-model='searchQuery'
:search-input.sync='searchInput'
:items='suggestions'
></v-autocomplete>
</v-app-bar>
<!-- Main -->
@ -269,7 +275,10 @@ export default {
showPlayer: false,
position: '0.00',
searchQuery: '',
searchLoading: false
searchLoading: false,
searchInput: null,
suggestions: [],
preventDoubleEnter: false
}
},
methods: {
@ -288,19 +297,24 @@ export default {
},
async search(event) {
//KeyUp event, enter
if (event.keyCode !== 13) return;
if (event && event.keyCode !== 13) return;
//Prevent double navigation
if (this.preventDoubleEnter) return;
this.preventDoubleEnter = true;
setInterval(() => {this.preventDoubleEnter = false}, 50);
//Check if url
if (this.searchQuery.startsWith('http')) {
let query = this.searchInput;
if (query.startsWith('http')) {
this.searchLoading = true;
let url = new URL(this.searchQuery);
let url = new URL(query);
//Normal link
if (url.hostname == 'www.deezer.com' || url.hostname == 'deezer.com' || url.hostname == 'deezer.page.link') {
//Share link
if (url.hostname == 'deezer.page.link') {
let res = await this.$axios.get('/fullurl?url=' + encodeURIComponent(this.searchQuery));
let res = await this.$axios.get('/fullurl?url=' + encodeURIComponent(query));
url = new URL(res.data.url);
}
@ -332,7 +346,7 @@ export default {
this.searchLoading = false;
} else {
//Normal search
this.$router.push({path: '/search', query: {q: this.searchQuery}});
this.$router.push({path: '/search', query: {q: query}});
}
},
seek(val) {
@ -383,6 +397,33 @@ export default {
//Update position
'$root.position'() {
this.position = (this.$root.position / this.$root.duration()) * 100;
},
//Autofill
searchInput(query) {
//Filters
if (query && query.startsWith('/')) {
query = query.substring(1);
this.searchInput = query;
}
if (!query || (query && query.startsWith('http'))) {
this.searchLoading = false;
this.suggestions = [];
return;
}
this.searchLoading = true;
//Prevent spam
setTimeout(() => {
if (query != this.searchInput) return;
this.$axios.get('/suggestions/' + encodeURIComponent(query)).then((res) => {
if (query != this.searchInput) return;
this.suggestions = res.data;
this.searchLoading = false;
});
}, 300);
},
searchQuery(q) {
this.searchInput = q;
this.search(null);
}
}
};

View File

@ -1,6 +1,6 @@
<template>
<div>
<v-list-item @click='click' v-if='!card' :class='{dense: tiny}'>
<v-list-item @click='click' v-if='!card'>
<v-list-item-avatar>
<v-img :src='artist.picture.thumb'></v-img>
</v-list-item-avatar>

View File

@ -108,6 +108,17 @@ export default {
dShow() {
if (!this.dShow) this.$emit('close');
}
},
mounted() {
//Auto download
if (!this.$root.settings.downloadDialog) {
this.download();
setInterval(() => {
this.$emit('close');
this.dShow = false;
}, 50);
}
}
}
</script>

View File

@ -74,10 +74,6 @@ export default {
},
//Play track
async play(index) {
if (this.tracks.length < this.count) {
await this.loadAll();
}
this.$root.queue.source = {
text: 'Loved tracks',
source: 'playlist',
@ -85,6 +81,15 @@ export default {
};
this.$root.replaceQueue(this.tracks);
this.$root.playIndex(index);
//Load all tracks
if (this.tracks.length < this.count) {
this.loadAll().then(() => {
this.$root.replaceQueue(this.tracks);
});
}
},
removedTrack(index) {
this.tracks.splice(index, 1);

View File

@ -85,7 +85,7 @@ export default {
},
//Scroll to currently playing lyric
scrollLyric() {
if (!this.lyrics) return;
if (!this.lyrics || !this.lyrics.lyrics || this.lyrics.lyrics.length == 0) return;
//Prevent janky scrolling
if (this.currentLyricIndex == this.currentLyric()) return;

View File

@ -109,14 +109,12 @@ export default {
methods: {
async play() {
let playlist = this.playlist;
//Load playlist tracks
if (playlist.tracks.length != playlist.trackCount) {
let data = await this.$axios.get(`/playlist/${playlist.id}?full=iguess`);
playlist = data.data;
}
//Error handling
//Load if no tracks
if (!playlist || playlist.tracks.length == 0)
playlist = (await this.$axios.get(`/playlist/${playlist.id}?full=iguess`)).data;
if (!playlist) return;
//Play
this.$root.queue.source = {
text: playlist.title,
source: 'playlist',
@ -124,6 +122,12 @@ export default {
};
this.$root.replaceQueue(playlist.tracks);
this.$root.playIndex(0);
//Load all tracks
if (playlist.tracks.length != playlist.trackCount) {
let data = await this.$axios.get(`/playlist/${playlist.id}?full=iguess`);
playlist = data.data;
}
},
//On click navigate to details
click() {

View File

@ -180,12 +180,14 @@ export default {
this.$axios.put(`/library/tracks?id=${this.track.id}`);
},
goAlbum() {
this.$emit('redirect')
this.$router.push({
path: '/album',
query: {album: JSON.stringify(this.track.album)}
});
},
goArtist(a) {
this.$emit('redirect');
this.$router.push({
path: '/artist',
query: {artist: JSON.stringify(a)}

View File

@ -131,9 +131,11 @@ new Vue({
this.play();
},
seek(t) {
if (!this.audio) return;
if (!this.audio || isNaN(t) || !t) return;
//ms -> s
this.audio.currentTime = (t / 1000);
this.updateState();
},
//Current track duration
@ -161,11 +163,11 @@ new Vue({
this.savePlaybackInfo();
},
//Skip n tracks, can be negative
skip(n) {
async skip(n) {
let newIndex = this.queue.index + n;
//Out of bounds
if (newIndex < 0 || newIndex >= this.queue.data.length) return;
this.playIndex(newIndex);
await this.playIndex(newIndex);
},
//Skip wrapper with shuffle
skipNext() {
@ -253,7 +255,7 @@ new Vue({
}
//Repeat list
if (this.queue.index == this.queue.data.length - 1) {
if (this.repeat == 1 && this.queue.index == this.queue.data.length - 1) {
this.skip(-(this.queue.data.length - 1));
return;
}
@ -403,6 +405,18 @@ new Vue({
this.logListenId = this.track.id;
await this.$axios.post(`/log`, this.track);
},
//Send state update to integrations
async updateState() {
//Wait for duration
if (this.state == 2 && (this.duration() == null || isNaN(this.duration())))
await new Promise((res) => setTimeout(res, 1000));
this.$socket.emit('stateChange', {
position: this.position,
duration: this.duration(),
state: this.state,
track: this.track
});
}
},
async created() {
@ -471,6 +485,12 @@ new Vue({
this.sockets.subscribe('download', (data) => {
this.download = data;
});
//Play at offset (for integrations)
this.sockets.subscribe('playOffset', async (data) => {
this.queue.data.splice(this.queue.index + 1, 0, data.track);
await this.skip(1);
this.seek(data.position);
});
r();
},
@ -499,6 +519,12 @@ new Vue({
if (e.keyCode === 106 || e.keyCode === 74) this.$root.seek((this.position - 10000));
});
},
watch: {
//Watch state for integrations
state() {
this.updateState();
}
},
router,
vuetify,

View File

@ -144,6 +144,7 @@
><TrackTile
:track='track'
@click='$root.playIndex(index)'
@redirect='close'
></TrackTile>
</v-lazy>
@ -161,14 +162,13 @@
></AlbumTile>
<!-- Artists -->
<h3>Artists:</h3>
<v-list dense>
<v-list>
<ArtistTile
v-for='(artist, index) in $root.track.artists'
:artist='artist'
:key="index + 'a' + artist.id"
@clicked='$emit("close")'
tiny
class='text-left'
></ArtistTile>
</v-list>
<!-- Meta -->
@ -179,6 +179,7 @@
<h3>Source: {{$root.playbackInfo.source}}</h3>
<h3>Format: {{$root.playbackInfo.format}}</h3>
<h3>Quality: {{$root.playbackInfo.quality}}</h3>
<h3>ID: {{$root.track.id}}</h3>
</v-list>
</v-tab-item>
<!-- Lyrics -->

View File

@ -87,9 +87,8 @@ export default {
methods: {
async playIndex(index) {
//Load tracks
if (this.playlist.tracks.length < this.playlist.trackCount) {
if (this.playlist.tracks.length == 0)
await this.loadAllTracks();
}
this.$root.queue.source = {
text: this.playlist.title,
@ -98,6 +97,13 @@ export default {
};
this.$root.replaceQueue(this.playlist.tracks);
this.$root.playIndex(index);
//Load rest of tracks on background
if (this.playlist.tracks.length < this.playlist.trackCount) {
this.loadAllTracks().then(() => {
this.$root.replaceQueue(this.playlist.tracks);
});
}
},
play() {
this.playIndex(0);

View File

@ -28,6 +28,17 @@
append-icon='mdi-open-in-app'
@click:append='selectDownloadPath'
></v-text-field>
<!-- Download dialog -->
<v-list-item>
<v-list-item-action>
<v-checkbox v-model='$root.settings.downloadDialog'></v-checkbox>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Show download dialog</v-list-item-title>
<v-list-item-subtitle>Always show download confirm dialog before downloading.</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<!-- Create artist folder -->
<v-list-item>
@ -57,6 +68,10 @@
hint='Variables: %title%, %artists%, %artist%, %feats%, %trackNumber%, %0trackNumber%, %album%'
></v-text-field>
<!-- Accounts -->
<v-subheader>Integrations</v-subheader>
<v-divider></v-divider>
<!-- Log listening -->
<v-list-item>
<v-list-item-action>
@ -69,11 +84,46 @@
</v-list-item>
<!-- LastFM -->
<v-list-item @click='connectLastFM' v-if='!$root.settings.lastFM'>
<v-list-item-avatar>
<v-img src='lastfm.svg'></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>Login with LastFM</v-list-item-title>
<v-list-item-subtitle>Connect your LastFM account to allow scrobbling.</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item v-if='$root.settings.lastFM' @click='disconnectLastFM'>
<v-list-item-avatar>
<v-icon>mdi-logout</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class='red--text'>Disconnect LastFM</v-list-item-title>
</v-list-item-content>
</v-list-item>
<!-- Discord -->
<v-list-item>
<v-list-item-action>
<v-checkbox v-model='$root.settings.enableDiscord' @click='snackbarText = "Requires restart to apply!"; snackbar = true'></v-checkbox>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Discord Rich Presence</v-list-item-title>
<v-list-item-subtitle>Enable Discord Rich Presence, requires restart to toggle!</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<!-- Discord Join Button -->
<v-list-item>
<v-list-item-action>
<v-checkbox v-model='$root.settings.discordJoin' @click='snackbarText = "Requires restart to apply!"; snackbar = true'></v-checkbox>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Discord Join Button</v-list-item-title>
<v-list-item-subtitle>Enable Discord join button for syncing tracks, requires restart to toggle!</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<!-- Misc -->
<v-subheader>Other</v-subheader>
<v-divider></v-divider>
<!-- Minimize to tray -->
<v-list-item v-if='$root.settings.electron'>
@ -108,6 +158,22 @@
Save
</v-btn>
<!-- Info snackbar -->
<v-snackbar v-model="snackbar">
{{ snackbarText }}
<template v-slot:action="{ attrs }">
<v-btn
color="primary"
text
v-bind="attrs"
@click="snackbar = false"
>
Dismiss
</v-btn>
</template>
</v-snackbar>
</div>
</template>
@ -124,7 +190,9 @@ export default {
],
streamingQuality: null,
downloadQuality: null,
devToolsCounter: 0
devToolsCounter: 0,
snackbarText: null,
snackbar: false
}
},
methods: {
@ -178,6 +246,12 @@ export default {
async connectLastFM() {
let res = await this.$axios.get('/lastfm');
window.location.replace(res.data.url);
},
//Disconnect LastFM
async disconnectLastFM() {
this.$root.settings.lastFM = null;
await this.$root.saveSettings();
window.location.reload();
}
},
mounted() {