Merge branch 'restful-refactor'
This commit is contained in:
commit
1b063ac24c
@ -7,5 +7,6 @@
|
||||
"trailingComma": "none",
|
||||
"printWidth": 120,
|
||||
"arrowParens": "avoid",
|
||||
"vueIndentScriptAndStyle": false
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"endOfLine": "lf"
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
"@vue/composition-api": "^1.0.0-beta.21",
|
||||
"flag-icon-css": "^3.5.0",
|
||||
"lodash-es": "^4.17.15",
|
||||
"socket.io-client": "^3.1.0",
|
||||
"svg-country-flags": "^1.2.9",
|
||||
"toastify-js": "^1.9.3",
|
||||
"vue": "^2.6.12",
|
||||
|
File diff suppressed because one or more lines are too long
@ -28,10 +28,6 @@ export default {
|
||||
find: 'vue',
|
||||
replacement: 'vue/dist/vue.esm'
|
||||
},
|
||||
{
|
||||
find: 'socket.io-client',
|
||||
replacement: 'socket.io-client/dist/socket.io.min'
|
||||
},
|
||||
{
|
||||
find: '@',
|
||||
replacement: __dirname + '/src'
|
||||
|
@ -69,7 +69,8 @@ export default {
|
||||
// ConfirmModal
|
||||
},
|
||||
mounted() {
|
||||
socket.on('connect', () => {
|
||||
socket.addEventListener('open', (event) => {
|
||||
console.log("Connected to WebSocket")
|
||||
this.isSocketConnected = true
|
||||
})
|
||||
}
|
||||
|
59
src/app.js
59
src/app.js
@ -19,18 +19,42 @@ import router from '@/router'
|
||||
import store from '@/store'
|
||||
|
||||
import { socket } from '@/utils/socket'
|
||||
import { fetchData } from '@/utils/api'
|
||||
import { toast } from '@/utils/toasts'
|
||||
import { isValidURL } from '@/utils/utils'
|
||||
import { sendAddToQueue } from '@/utils/downloads'
|
||||
|
||||
/* ===== App initialization ===== */
|
||||
function startApp() {
|
||||
async function startApp() {
|
||||
new Vue({
|
||||
store,
|
||||
router,
|
||||
i18n,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
const connectResponse = await (await fetch('connect')).json()
|
||||
|
||||
store.dispatch('setAppInfo', connectResponse.update)
|
||||
|
||||
if (connectResponse.autologin) {
|
||||
console.info('Autologin')
|
||||
let arl = localStorage.getItem('arl')
|
||||
const accountNum = localStorage.getItem('accountNum')
|
||||
|
||||
if (arl) {
|
||||
arl = arl.trim()
|
||||
let result
|
||||
|
||||
if (accountNum !== 0) {
|
||||
result = await fetchData('login', { arl, force: true, child: accountNum || 0 })
|
||||
} else {
|
||||
result = await fetchData('login', { arl })
|
||||
}
|
||||
|
||||
loggedIn(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initClient() {
|
||||
@ -52,7 +76,7 @@ document.addEventListener('paste', pasteEvent => {
|
||||
if (router.currentRoute.name === 'Link Analyzer') {
|
||||
socket.emit('analyzeLink', pastedText)
|
||||
} else {
|
||||
if (pastedText.indexOf("\n") != -1) pastedText = pastedText.replace(/\n/g, ';');
|
||||
if (pastedText.indexOf('\n') != -1) pastedText = pastedText.replace(/\n/g, ';')
|
||||
sendAddToQueue(pastedText)
|
||||
}
|
||||
} else {
|
||||
@ -86,26 +110,7 @@ socket.on('message', function(msg) {
|
||||
console.log(msg)
|
||||
})
|
||||
|
||||
socket.on('logging_in', function() {
|
||||
toast(i18n.t('toasts.loggingIn'), 'loading', false, 'login-toast')
|
||||
})
|
||||
|
||||
socket.on('init_autologin', function() {
|
||||
let arl = localStorage.getItem('arl')
|
||||
let accountNum = localStorage.getItem('accountNum')
|
||||
|
||||
if (arl) {
|
||||
arl = arl.trim()
|
||||
|
||||
if (accountNum != 0) {
|
||||
socket.emit('login', arl, true, accountNum)
|
||||
} else {
|
||||
socket.emit('login', arl)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('logged_in', function(data) {
|
||||
function loggedIn(data) {
|
||||
const { status, user } = data
|
||||
|
||||
switch (status) {
|
||||
@ -138,6 +143,15 @@ socket.on('logged_in', function(data) {
|
||||
// $('#settings_picture').attr('src', `https://e-cdns-images.dzcdn.net/images/user/125x125-000000-80-0-0.jpg`)
|
||||
// document.getElementById('home_not_logged_in').classList.remove('hide')
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
socket.on('logging_in', function() {
|
||||
toast(i18n.t('toasts.loggingIn'), 'loading', false, 'login-toast')
|
||||
})
|
||||
|
||||
socket.on('logged_in', function(data) {
|
||||
|
||||
})
|
||||
|
||||
socket.on('logged_out', function() {
|
||||
@ -145,6 +159,7 @@ socket.on('logged_out', function() {
|
||||
|
||||
store.dispatch('logout')
|
||||
})
|
||||
*/
|
||||
|
||||
socket.on('restoringQueue', function () {
|
||||
toast(i18n.t('toasts.restoringQueue'), 'loading', false, 'restoring_queue')
|
||||
|
@ -113,23 +113,20 @@
|
||||
import { computed, defineComponent, reactive, toRefs } from '@vue/composition-api'
|
||||
|
||||
import { links } from '@/data/sidebar'
|
||||
import { socket } from '@/utils/socket'
|
||||
import { useTheme } from '@/use/theme'
|
||||
|
||||
import deemixIcon from '@/assets/deemix-icon.svg'
|
||||
|
||||
export default defineComponent({
|
||||
setup(props, ctx) {
|
||||
const state = reactive({
|
||||
activeTablink: 'home',
|
||||
updateAvailable: false,
|
||||
links
|
||||
})
|
||||
const { THEMES, currentTheme } = useTheme()
|
||||
|
||||
/* === Add update notification near info === */
|
||||
socket.on('updateAvailable', () => {
|
||||
state.updateAvailable = true
|
||||
})
|
||||
const updateAvailable = computed(() => ctx.root.$store.state.appInfo.updateAvailable)
|
||||
|
||||
ctx.root.$router.afterEach((to, from) => {
|
||||
const linkInSidebar = state.links.find(link => link.routerName === to.name)
|
||||
@ -141,6 +138,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
updateAvailable,
|
||||
THEMES,
|
||||
currentTheme,
|
||||
isSlim: computed(() => ctx.root.$store.getters.getSlimSidebar),
|
||||
|
@ -26,9 +26,7 @@
|
||||
<li v-html="$t('about.thanks')"></li>
|
||||
<i18n path="about.upToDate.text" tag="li">
|
||||
<template #newsChannel>
|
||||
<a href="https://t.me/RemixDevNews" target="_blank">{{
|
||||
$t('about.upToDate.newsChannel')
|
||||
}}</a>
|
||||
<a href="https://t.me/RemixDevNews" target="_blank">{{ $t('about.upToDate.newsChannel') }}</a>
|
||||
</template>
|
||||
</i18n>
|
||||
</ul>
|
||||
@ -42,9 +40,7 @@
|
||||
<a href="https://git.rip/RemixDev/deemix" target="_blank">🚀 {{ $t('about.officialRepo') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://git.rip/RemixDev/deemix-webui" target="_blank">
|
||||
💻 {{ $t('about.officialWebuiRepo') }}
|
||||
</a>
|
||||
<a href="https://git.rip/RemixDev/deemix-webui" target="_blank"> 💻 {{ $t('about.officialWebuiRepo') }} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.reddit.com/r/deemix" target="_blank">🤖 {{ $t('about.officialSubreddit') }}</a>
|
||||
@ -63,9 +59,7 @@
|
||||
<ul>
|
||||
<i18n path="about.questions.text" tag="li">
|
||||
<template #subreddit>
|
||||
<a href="https://www.reddit.com/r/deemix" target="_blank">{{
|
||||
$t('about.questions.subreddit')
|
||||
}}</a>
|
||||
<a href="https://www.reddit.com/r/deemix" target="_blank">{{ $t('about.questions.subreddit') }}</a>
|
||||
</template>
|
||||
</i18n>
|
||||
<li>
|
||||
@ -241,7 +235,7 @@ ul {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, reactive, toRefs, onMounted, computed } from '@vue/composition-api'
|
||||
import { computed, defineComponent, onMounted, reactive, toRefs } from '@vue/composition-api'
|
||||
|
||||
import { useOnline } from '@/use/online'
|
||||
|
||||
|
@ -92,7 +92,6 @@ import { orderBy } from 'lodash-es'
|
||||
|
||||
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
|
||||
|
||||
import { socket } from '@/utils/socket'
|
||||
import { sendAddToQueue } from '@/utils/downloads'
|
||||
import { checkNewRelease } from '@/utils/dates'
|
||||
import { formatArtistData, getArtistData } from '@/data/artist'
|
||||
|
@ -6,38 +6,38 @@
|
||||
<div class="release-grid">
|
||||
<div
|
||||
v-for="release in countries"
|
||||
role="button"
|
||||
:aria-label="release.title"
|
||||
class="w-40 h-40 release clickable"
|
||||
@click="getTrackList"
|
||||
:data-title="release.title"
|
||||
:data-id="release.id"
|
||||
:key="release.id"
|
||||
:aria-label="release.title"
|
||||
:data-id="release.id"
|
||||
:data-title="release.title"
|
||||
class="w-40 h-40 release clickable"
|
||||
role="button"
|
||||
@click="getTrackList"
|
||||
>
|
||||
<img class="w-full rounded coverart" :src="release.picture_medium" />
|
||||
<img :src="release.picture_medium" class="w-full rounded coverart" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<button class="btn btn-primary" @click="onChangeCountry">{{ $t('charts.changeCountry') }}</button>
|
||||
<button class="btn btn-primary" @click.stop="addToQueue" :data-link="'https://www.deezer.com/playlist/' + id">
|
||||
<button :data-link="'https://www.deezer.com/playlist/' + id" class="btn btn-primary" @click.stop="addToQueue">
|
||||
{{ $t('charts.download') }}
|
||||
</button>
|
||||
<table class="table table--charts">
|
||||
<tbody>
|
||||
<tr v-for="track in chart" class="track_row">
|
||||
<td class="p-3 text-center cursor-default" :class="{ first: track.position === 1 }">
|
||||
<td :class="{ first: track.position === 1 }" class="p-3 text-center cursor-default">
|
||||
{{ track.position }}
|
||||
</td>
|
||||
<td class="table__icon table__icon--big">
|
||||
<span
|
||||
@click="playPausePreview"
|
||||
class="relative inline-block rounded cursor-pointer"
|
||||
:data-preview="track.preview"
|
||||
class="relative inline-block rounded cursor-pointer"
|
||||
@click="playPausePreview"
|
||||
>
|
||||
<PreviewControls v-if="track.preview" />
|
||||
<img class="rounded coverart" :src="track.album.cover_small" />
|
||||
<img :src="track.album.cover_small" class="rounded coverart" />
|
||||
</span>
|
||||
</td>
|
||||
<td class="table__cell--large">
|
||||
@ -47,16 +47,16 @@
|
||||
}}
|
||||
</td>
|
||||
<router-link
|
||||
tag="td"
|
||||
class="table__cell table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Artist', params: { id: track.artist.id } }"
|
||||
class="table__cell table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.artist.name }}
|
||||
</router-link>
|
||||
<router-link
|
||||
tag="td"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Album', params: { id: track.album.id } }"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.album.title }}
|
||||
</router-link>
|
||||
@ -64,15 +64,15 @@
|
||||
{{ convertDuration(track.duration) }}
|
||||
</td>
|
||||
<td
|
||||
class="cursor-pointer group"
|
||||
@click.stop="addToQueue"
|
||||
:data-link="track.link"
|
||||
role="button"
|
||||
aria-label="download"
|
||||
class="cursor-pointer group"
|
||||
role="button"
|
||||
@click.stop="addToQueue"
|
||||
>
|
||||
<i
|
||||
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
|
||||
:title="$t('globals.download_hint')"
|
||||
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
|
||||
>
|
||||
get_app
|
||||
</i>
|
||||
@ -85,14 +85,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { socket } from '@/utils/socket'
|
||||
import { sendAddToQueue } from '@/utils/downloads'
|
||||
import { convertDuration } from '@/utils/utils'
|
||||
import { getChartsData } from '@/data/charts'
|
||||
import { getChartsData, getChartTracks } from '@/data/charts'
|
||||
|
||||
import PreviewControls from '@components/globals/PreviewControls.vue'
|
||||
import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -116,12 +116,12 @@ export default {
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
socket.on('setChartTracks', this.setTracklist)
|
||||
this.$on('hook:destroyed', () => {
|
||||
socket.off('setChartTracks')
|
||||
})
|
||||
// socket.on('setChartTracks', this.setTracklist)
|
||||
// this.$on('hook:destroyed', () => {
|
||||
// socket.off('setChartTracks')
|
||||
// })
|
||||
|
||||
let chartsData = await getChartsData()
|
||||
let { data: chartsData } = await getChartsData()
|
||||
let worldwideChart
|
||||
|
||||
chartsData = chartsData.filter(item => {
|
||||
@ -147,17 +147,13 @@ export default {
|
||||
|
||||
const {
|
||||
currentTarget: {
|
||||
dataset: { title }
|
||||
},
|
||||
currentTarget: {
|
||||
dataset: { id }
|
||||
dataset: { title, id }
|
||||
}
|
||||
} = event
|
||||
|
||||
this.country = title
|
||||
localStorage.setItem('chart', this.country)
|
||||
this.id = id
|
||||
socket.emit('getChartTracks', this.id)
|
||||
},
|
||||
setTracklist(data) {
|
||||
this.chart = data
|
||||
@ -185,6 +181,15 @@ export default {
|
||||
localStorage.setItem('chart', this.country)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
id(newId) {
|
||||
const isActualChart = newId !== 0
|
||||
|
||||
if (isActualChart) {
|
||||
getChartTracks(newId).then(response => this.setTracklist(response.result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -3,13 +3,13 @@
|
||||
<h1 class="mb-8 text-5xl">
|
||||
{{ $t('favorites.title') }}
|
||||
<div
|
||||
@click="refreshFavorites"
|
||||
class="inline-block clickable"
|
||||
ref="reloadButton"
|
||||
role="button"
|
||||
aria-label="reload"
|
||||
class="inline-block clickable"
|
||||
role="button"
|
||||
@click="refreshFavorites"
|
||||
>
|
||||
<i class="material-icons" :class="{ spin: isRefreshingFavorites }">sync</i>
|
||||
<i :class="{ spin: isRefreshingFavorites }" class="material-icons">sync</i>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
</BaseTab>
|
||||
</BaseTabs>
|
||||
|
||||
<button class="btn btn-primary" v-if="!activeTabEmpty" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
||||
<button v-if="!activeTabEmpty" class="btn btn-primary" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
||||
{{ $t('globals.download', { thing: $tc(`globals.listTabs.${activeTab}N`, getTabLength()) }) }}
|
||||
</button>
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
<div v-if="playlists.length == 0">
|
||||
<h1>{{ $t('favorites.noPlaylists') }}</h1>
|
||||
</div>
|
||||
<div class="release-grid" v-if="playlists.length > 0 || spotifyPlaylists > 0">
|
||||
<div class="release" v-for="release in playlists" :key="release.id">
|
||||
<router-link tag="div" class="cursor-pointer" :to="{ name: 'Playlist', params: { id: release.id } }">
|
||||
<CoverContainer is-rounded :cover="release.picture_medium" :link="release.link" @click.stop="addToQueue" />
|
||||
<div v-if="playlists.length > 0 || spotifyPlaylists > 0" class="release-grid">
|
||||
<div v-for="release in playlists" :key="release.id" class="release">
|
||||
<router-link :to="{ name: 'Playlist', params: { id: release.id } }" class="cursor-pointer" tag="div">
|
||||
<CoverContainer :cover="release.picture_medium" :link="release.link" is-rounded @click.stop="addToQueue" />
|
||||
<p class="primary-text">{{ release.title }}</p>
|
||||
</router-link>
|
||||
|
||||
@ -44,9 +44,9 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="release" v-for="release in spotifyPlaylists" :key="release.id">
|
||||
<router-link tag="div" class="cursor-pointer" :to="{ name: 'Spotify Playlist', params: { id: release.id } }">
|
||||
<CoverContainer is-rounded :cover="release.picture_medium" :link="release.link" @click.stop="addToQueue" />
|
||||
<div v-for="release in spotifyPlaylists" :key="release.id" class="release">
|
||||
<router-link :to="{ name: 'Spotify Playlist', params: { id: release.id } }" class="cursor-pointer" tag="div">
|
||||
<CoverContainer :cover="release.picture_medium" :link="release.link" is-rounded @click.stop="addToQueue" />
|
||||
<p class="primary-text">{{ release.title }}</p>
|
||||
</router-link>
|
||||
|
||||
@ -66,15 +66,15 @@
|
||||
<div v-if="albums.length == 0">
|
||||
<h1>{{ $t('favorites.noAlbums') }}</h1>
|
||||
</div>
|
||||
<div class="release-grid" v-if="albums.length > 0">
|
||||
<div v-if="albums.length > 0" class="release-grid">
|
||||
<router-link
|
||||
tag="div"
|
||||
class="release clickable"
|
||||
v-for="release in albums"
|
||||
:key="release.id"
|
||||
:to="{ name: 'Album', params: { id: release.id } }"
|
||||
class="release clickable"
|
||||
tag="div"
|
||||
>
|
||||
<CoverContainer is-rounded :cover="release.cover_medium" :link="release.link" @click.stop="addToQueue" />
|
||||
<CoverContainer :cover="release.cover_medium" :link="release.link" is-rounded @click.stop="addToQueue" />
|
||||
<p class="primary-text">{{ release.title }}</p>
|
||||
<p class="secondary-text">{{ `${$t('globals.by', { artist: release.artist.name })}` }}</p>
|
||||
</router-link>
|
||||
@ -85,15 +85,15 @@
|
||||
<div v-if="artists.length == 0">
|
||||
<h1>{{ $t('favorites.noArtists') }}</h1>
|
||||
</div>
|
||||
<div class="release-grid" v-if="artists.length > 0">
|
||||
<div v-if="artists.length > 0" class="release-grid">
|
||||
<router-link
|
||||
tag="div"
|
||||
class="release clickable"
|
||||
v-for="release in artists"
|
||||
:key="release.id"
|
||||
:to="{ name: 'Artist', params: { id: release.id } }"
|
||||
class="release clickable"
|
||||
tag="div"
|
||||
>
|
||||
<CoverContainer is-circle :cover="release.picture_medium" :link="release.link" @click.stop="addToQueue" />
|
||||
<CoverContainer :cover="release.picture_medium" :link="release.link" is-circle @click.stop="addToQueue" />
|
||||
<p class="primary-text">{{ release.name }}</p>
|
||||
</router-link>
|
||||
</div>
|
||||
@ -105,17 +105,17 @@
|
||||
</div>
|
||||
<table v-if="tracks.length > 0" class="table">
|
||||
<tr v-for="track in tracks" class="track_row">
|
||||
<td class="p-3 text-center cursor-default" :class="{ first: track.position === 1 }">
|
||||
<td :class="{ first: track.position === 1 }" class="p-3 text-center cursor-default">
|
||||
{{ track.position }}
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
:data-preview="track.preview"
|
||||
class="relative inline-block rounded cursor-pointer"
|
||||
@click="playPausePreview"
|
||||
:data-preview="track.preview"
|
||||
>
|
||||
<PreviewControls v-if="track.preview" />
|
||||
<img class="rounded coverart" :src="track.album.cover_small" />
|
||||
<img :src="track.album.cover_small" class="rounded coverart" />
|
||||
</span>
|
||||
</td>
|
||||
<td class="table__cell--large">
|
||||
@ -125,16 +125,16 @@
|
||||
}}
|
||||
</td>
|
||||
<router-link
|
||||
tag="td"
|
||||
class="table__cell table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Artist', params: { id: track.artist.id } }"
|
||||
class="table__cell table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.artist.name }}
|
||||
</router-link>
|
||||
<router-link
|
||||
tag="td"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Album', params: { id: track.album.id } }"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.album.title }}
|
||||
</router-link>
|
||||
@ -142,16 +142,16 @@
|
||||
{{ convertDuration(track.duration) }}
|
||||
</td>
|
||||
<td
|
||||
class="cursor-pointer group"
|
||||
@click.stop="addToQueue"
|
||||
:data-link="track.link"
|
||||
role="button"
|
||||
aria-label="download"
|
||||
class="cursor-pointer group"
|
||||
role="button"
|
||||
@click.stop="addToQueue"
|
||||
>
|
||||
<div class="table__cell-content table__cell-content--vertical-center">
|
||||
<i
|
||||
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
|
||||
:title="$t('globals.download_hint')"
|
||||
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
|
||||
>
|
||||
get_app
|
||||
</i>
|
||||
@ -164,14 +164,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, reactive, toRefs, watchEffect, ref, computed, watch } from '@vue/composition-api'
|
||||
import { defineComponent, reactive, toRefs, watch } from '@vue/composition-api'
|
||||
|
||||
import PreviewControls from '@components/globals/PreviewControls.vue'
|
||||
import CoverContainer from '@components/globals/CoverContainer.vue'
|
||||
import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
|
||||
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
|
||||
import { BaseTab, BaseTabs } from '@components/globals/BaseTabs'
|
||||
|
||||
import { sendAddToQueue, aggregateDownloadLinks } from '@/utils/downloads'
|
||||
import { aggregateDownloadLinks, sendAddToQueue } from '@/utils/downloads'
|
||||
import { convertDuration } from '@/utils/utils'
|
||||
import { toast } from '@/utils/toasts'
|
||||
import { useFavorites } from '@/use/favorites'
|
||||
@ -197,7 +197,8 @@ export default defineComponent({
|
||||
isRefreshingFavorites,
|
||||
refreshFavorites
|
||||
} = useFavorites()
|
||||
const reloadButton = computed(() => ctx.refs.reloadButton)
|
||||
|
||||
refreshFavorites({ isInitial: true })
|
||||
|
||||
watch(isRefreshingFavorites, (newVal, oldVal) => {
|
||||
// If oldVal is true and newOne is false, it means that a refreshing has just terminated
|
||||
|
@ -783,6 +783,7 @@ import { copyToClipboard } from '@/utils/utils'
|
||||
|
||||
import BaseAccordion from '@/components/globals/BaseAccordion.vue'
|
||||
import TemplateVariablesList from '@components/settings/TemplateVariablesList.vue'
|
||||
import { fetchData, sendToServer } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -949,16 +950,17 @@ export default {
|
||||
this.lastCredentials = JSON.parse(JSON.stringify(credentials))
|
||||
this.spotifyFeatures = JSON.parse(JSON.stringify(credentials))
|
||||
},
|
||||
loggedInViaDeezer(arl) {
|
||||
async loggedInViaDeezer(arl) {
|
||||
this.dispatchARL({ arl })
|
||||
socket.emit('login', arl, true, this.accountNum)
|
||||
// this.login()
|
||||
// const res = await fetchData('login', { arl, force: true, child: this.accountNum })
|
||||
},
|
||||
login() {
|
||||
async login() {
|
||||
let newArl = this.$refs.loginInput.value.trim()
|
||||
|
||||
if (newArl && newArl !== this.arl) {
|
||||
socket.emit('login', newArl, true, this.accountNum)
|
||||
const res = await fetchData('login', { arl: newArl, force: true, child: this.accountNum })
|
||||
this.loggedInViaDeezer(res.arl)
|
||||
}
|
||||
},
|
||||
appLogin(e) {
|
||||
@ -969,9 +971,7 @@ export default {
|
||||
},
|
||||
accountChanged(user, accountNum) {
|
||||
this.$refs.username.innerText = user.name
|
||||
this.$refs.userpicture.src = `https://e-cdns-images.dzcdn.net/images/user/${
|
||||
user.picture
|
||||
}/125x125-000000-80-0-0.jpg`
|
||||
this.$refs.userpicture.src = `https://e-cdns-images.dzcdn.net/images/user/${user.picture}/125x125-000000-80-0-0.jpg`
|
||||
this.accountNum = accountNum
|
||||
|
||||
localStorage.setItem('accountNum', this.accountNum)
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="relative fixed-footer bg-background-main image-header" ref="root">
|
||||
<div ref="root" class="relative fixed-footer bg-background-main image-header">
|
||||
<header
|
||||
:style="{
|
||||
'background-image':
|
||||
@ -30,7 +30,7 @@
|
||||
<i class="material-icons">timer</i>
|
||||
</th>
|
||||
<th class="table__icon table__cell--center clickable">
|
||||
<input @click="toggleAll" class="selectAll" type="checkbox" />
|
||||
<input class="selectAll" type="checkbox" @click="toggleAll" />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -41,15 +41,15 @@
|
||||
<td class="table__cell--x-small table__cell--center">
|
||||
<div class="table__cell-content table__cell-content--vertical-center">
|
||||
<i
|
||||
class="material-icons"
|
||||
v-on="{ click: track.preview ? playPausePreview : false }"
|
||||
:class="{
|
||||
preview_playlist_controls: track.preview,
|
||||
'cursor-pointer': track.preview,
|
||||
disabled: !track.preview
|
||||
}"
|
||||
v-on="{ click: track.preview ? playPausePreview : false }"
|
||||
:data-preview="track.preview"
|
||||
:title="$t('globals.play_hint')"
|
||||
class="material-icons"
|
||||
>
|
||||
play_arrow
|
||||
</i>
|
||||
@ -70,28 +70,28 @@
|
||||
</div>
|
||||
</td>
|
||||
<router-link
|
||||
tag="td"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Artist', params: { id: track.artist.id } }"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.artist.name }}
|
||||
</router-link>
|
||||
<router-link
|
||||
tag="td"
|
||||
v-if="type === 'playlist'"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
:to="{ name: 'Album', params: { id: track.album.id } }"
|
||||
class="table__cell--medium table__cell--center clickable"
|
||||
tag="td"
|
||||
>
|
||||
{{ track.album.title }}
|
||||
</router-link>
|
||||
<td
|
||||
class="table__cell--center"
|
||||
:class="{ 'table__cell--small': type === 'album', 'table__cell--x-small': type === 'playlist' }"
|
||||
class="table__cell--center"
|
||||
>
|
||||
{{ convertDuration(track.duration) }}
|
||||
</td>
|
||||
<td class="table__icon table__cell--center">
|
||||
<input class="clickable" type="checkbox" v-model="track.selected" />
|
||||
<input v-model="track.selected" class="clickable" type="checkbox" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-else-if="track.type == 'disc_separator'" class="table__row-no-highlight" style="opacity: 0.54">
|
||||
@ -112,14 +112,14 @@
|
||||
<td>
|
||||
<i
|
||||
v-if="track.preview_url"
|
||||
@click="playPausePreview"
|
||||
class="material-icons"
|
||||
:class="{
|
||||
preview_playlist_controls: track.preview_url,
|
||||
'cursor-pointer': track.preview_url
|
||||
}"
|
||||
:data-preview="track.preview_url"
|
||||
:title="$t('globals.play_hint')"
|
||||
class="material-icons"
|
||||
@click="playPausePreview"
|
||||
>
|
||||
play_arrow
|
||||
</i>
|
||||
@ -133,17 +133,17 @@
|
||||
<td>{{ track.artists[0].name }}</td>
|
||||
<td>{{ track.album.name }}</td>
|
||||
<td>{{ convertDuration(Math.floor(track.duration_ms / 1000)) }}</td>
|
||||
<td><input class="clickable" type="checkbox" v-model="track.selected" /></td>
|
||||
<td><input v-model="track.selected" class="clickable" type="checkbox" /></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<span v-if="label" style="opacity: 0.4; margin-top: 8px; display: inline-block; font-size: 13px">{{ label }}</span>
|
||||
<footer class="bg-background-main">
|
||||
<button class="mr-2 btn btn-primary" @click.stop="addToQueue" :data-link="link">
|
||||
<button :data-link="link" class="mr-2 btn btn-primary" @click.stop="addToQueue">
|
||||
{{ `${$t('globals.download', { thing: $tc(`globals.listTabs.${type}`, 1) })}` }}
|
||||
</button>
|
||||
<button class="flex items-center btn btn-primary" @click.stop="addToQueue" :data-link="selectedLinks()">
|
||||
<button :data-link="selectedLinks()" class="flex items-center btn btn-primary" @click.stop="addToQueue">
|
||||
{{ $t('tracklist.downloadSelection') }}<i class="ml-2 material-icons">file_download</i>
|
||||
</button>
|
||||
</footer>
|
||||
@ -156,6 +156,7 @@ import { socket } from '@/utils/socket'
|
||||
import { sendAddToQueue } from '@/utils/downloads'
|
||||
import Utils from '@/utils/utils'
|
||||
import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
|
||||
import EventBus from '@/utils/EventBus'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@ -172,9 +173,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
socket.on('show_album', this.showAlbum)
|
||||
socket.on('show_playlist', this.showPlaylist)
|
||||
socket.on('show_spotifyplaylist', this.showSpotifyPlaylist)
|
||||
EventBus.$on('showAlbum', this.showAlbum)
|
||||
EventBus.$on('showPlaylist', this.showPlaylist)
|
||||
EventBus.$on('showSpotifyPlaylist', this.showSpotifyPlaylist)
|
||||
},
|
||||
methods: {
|
||||
playPausePreview,
|
||||
@ -193,17 +194,17 @@ export default {
|
||||
},
|
||||
toggleAll(e) {
|
||||
this.body.forEach(item => {
|
||||
if (item.type == 'track') {
|
||||
if (item.type === 'track') {
|
||||
item.selected = e.currentTarget.checked
|
||||
}
|
||||
})
|
||||
},
|
||||
selectedLinks() {
|
||||
var selected = []
|
||||
const selected = []
|
||||
if (this.body) {
|
||||
this.body.forEach(item => {
|
||||
if (item.type == 'track' && item.selected)
|
||||
selected.push(this.type == 'spotifyPlaylist' ? item.uri : item.link)
|
||||
if (item.type === 'track' && item.selected)
|
||||
selected.push(this.type === 'spotifyPlaylist' ? item.uri : item.link)
|
||||
})
|
||||
}
|
||||
return selected.join(';')
|
||||
@ -305,4 +306,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { socket } from '@/utils/socket'
|
||||
import { getPropertyWithFallback } from '@/utils/utils'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
export function formatArtistData(artistData) {
|
||||
return {
|
||||
@ -36,15 +36,8 @@ function formatArtistReleases(artistReleases) {
|
||||
}
|
||||
|
||||
export function getArtistData(artistID) {
|
||||
socket.emit('getTracklist', {
|
||||
return fetchData('getTracklist', {
|
||||
type: 'artist',
|
||||
id: artistID
|
||||
})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
socket.on('show_artist', data => {
|
||||
socket.off('show_artist')
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1,22 +1,9 @@
|
||||
import { socket } from '@/utils/socket'
|
||||
|
||||
let chartsData = {}
|
||||
let cached = false
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
export function getChartsData() {
|
||||
if (cached) {
|
||||
return chartsData
|
||||
} else {
|
||||
socket.emit('get_charts_data')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
socket.on('init_charts', data => {
|
||||
chartsData = data
|
||||
cached = true
|
||||
|
||||
socket.off('init_charts')
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
return fetchData('getCharts')
|
||||
}
|
||||
|
||||
export function getChartTracks(chartId) {
|
||||
return fetchData('getChartTracks', { id: chartId })
|
||||
}
|
||||
|
@ -1,22 +1,17 @@
|
||||
import { socket } from '@/utils/socket'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
let homeData = {}
|
||||
let cached = false
|
||||
|
||||
export function getHomeData() {
|
||||
export async function getHomeData() {
|
||||
if (cached) {
|
||||
return homeData
|
||||
} else {
|
||||
socket.emit('get_home_data')
|
||||
const data = await fetchData('getHome')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
socket.on('init_home', data => {
|
||||
homeData = data
|
||||
cached = true
|
||||
|
||||
socket.off('init_home')
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,5 @@
|
||||
"@components/*": ["./components/*"]
|
||||
}
|
||||
},
|
||||
"typeAcquisition": {
|
||||
"include": ["socket.io-client"]
|
||||
},
|
||||
"exclude": ["assets/**/*", "styles/**/*"]
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import { socket } from '@/utils/socket'
|
||||
|
||||
// Pages
|
||||
import About from '@components/pages/About.vue'
|
||||
@ -15,6 +14,8 @@ import LinkAnalyzer from '@components/pages/LinkAnalyzer.vue'
|
||||
import Search from '@components/pages/Search.vue'
|
||||
import Settings from '@components/pages/Settings.vue'
|
||||
import Tracklist from '@components/pages/Tracklist.vue'
|
||||
import { fetchData } from '@/utils/api'
|
||||
import EventBus from '@/utils/EventBus'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
@ -125,44 +126,51 @@ const router = new VueRouter({
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
let getTracklistParams = null
|
||||
|
||||
switch (to.name) {
|
||||
case 'Tracklist':
|
||||
getTracklistParams = {
|
||||
type: to.params.type,
|
||||
id: to.params.id
|
||||
}
|
||||
case 'Tracklist': {
|
||||
// const getTracklistParams = {
|
||||
// type: to.params.type,
|
||||
// id: to.params.id
|
||||
// }
|
||||
console.warn('This should never happen.')
|
||||
break
|
||||
case 'Album':
|
||||
getTracklistParams = {
|
||||
}
|
||||
case 'Album': {
|
||||
const getTracklistParams = {
|
||||
type: 'album',
|
||||
id: to.params.id
|
||||
}
|
||||
fetchData('getTracklist', getTracklistParams).then(albumData => {
|
||||
EventBus.$emit('showAlbum', albumData)
|
||||
})
|
||||
break
|
||||
case 'Playlist':
|
||||
getTracklistParams = {
|
||||
}
|
||||
case 'Playlist': {
|
||||
const getTracklistParams = {
|
||||
type: 'playlist',
|
||||
id: to.params.id
|
||||
}
|
||||
fetchData('getTracklist', getTracklistParams).then(playlistData => {
|
||||
EventBus.$emit('showPlaylist', playlistData)
|
||||
})
|
||||
break
|
||||
case 'Spotify Playlist':
|
||||
getTracklistParams = {
|
||||
}
|
||||
case 'Spotify Playlist': {
|
||||
const getTracklistParams = {
|
||||
type: 'spotifyplaylist',
|
||||
id: to.params.id
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
fetchData('getTracklist', getTracklistParams).then(spotifyPlaylistData => {
|
||||
EventBus.$emit('showSpotifyPlaylist', spotifyPlaylistData)
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
if (getTracklistParams) {
|
||||
socket.emit('getTracklist', getTracklistParams)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ref } from '@vue/composition-api'
|
||||
|
||||
import store from '@/store'
|
||||
import { socket } from '@/utils/socket'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
const favoriteArtists = ref([])
|
||||
const favoriteAlbums = ref([])
|
||||
@ -11,22 +11,35 @@ const favoriteTracks = ref([])
|
||||
|
||||
const isRefreshingFavorites = ref(false)
|
||||
|
||||
if (store.getters.isLoggedIn) {
|
||||
refreshFavorites({ isInitial: true })
|
||||
}
|
||||
|
||||
function refreshFavorites({ isInitial = false }) {
|
||||
if (!isInitial) {
|
||||
isRefreshingFavorites.value = true
|
||||
}
|
||||
|
||||
socket.emit('get_favorites_data')
|
||||
fetchData('getUserFavorites').then(setAllFavorites).catch(console.error)
|
||||
|
||||
if (store.getters.isLoggedWithSpotify) {
|
||||
socket.emit('update_userSpotifyPlaylists', store.getters.getSpotifyUser.id)
|
||||
fetchData('getUserSpotifyPlaylists', {
|
||||
spotifyUser: store.getters.getSpotifyUser.id
|
||||
})
|
||||
.then(({ data: spotifyPlaylists }) => {
|
||||
favoriteSpotifyPlaylists.value = spotifyPlaylists
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
function setAllFavorites(data) {
|
||||
const { tracks, albums, artists, playlists } = data
|
||||
|
||||
isRefreshingFavorites.value = false
|
||||
|
||||
favoriteArtists.value = artists
|
||||
favoriteAlbums.value = albums
|
||||
favoritePlaylists.value = playlists
|
||||
favoriteTracks.value = tracks
|
||||
}
|
||||
|
||||
export function useFavorites() {
|
||||
return {
|
||||
favoriteArtists,
|
||||
@ -38,42 +51,3 @@ export function useFavorites() {
|
||||
refreshFavorites
|
||||
}
|
||||
}
|
||||
|
||||
function setAllFavorites(data) {
|
||||
const { tracks, albums, artists, playlists } = data
|
||||
|
||||
favoriteArtists.value = artists
|
||||
favoriteAlbums.value = albums
|
||||
favoritePlaylists.value = playlists
|
||||
favoriteTracks.value = tracks
|
||||
}
|
||||
|
||||
socket.on('updated_userFavorites', data => {
|
||||
setAllFavorites(data)
|
||||
// Commented out because the corresponding emit function is never called at the moment
|
||||
// therefore isRefreshingFavorites is never set to true
|
||||
// isRefreshingFavorites.value = false
|
||||
})
|
||||
socket.on('init_favorites', data => {
|
||||
setAllFavorites(data)
|
||||
isRefreshingFavorites.value = false
|
||||
})
|
||||
|
||||
socket.on('updated_userSpotifyPlaylists', data => {
|
||||
favoriteSpotifyPlaylists.value = data
|
||||
})
|
||||
socket.on('updated_userSpotifyPlaylists', data => {
|
||||
favoriteSpotifyPlaylists.value = data
|
||||
})
|
||||
socket.on('updated_userPlaylists', data => {
|
||||
favoritePlaylists.value = data
|
||||
})
|
||||
socket.on('updated_userAlbums', data => {
|
||||
favoriteAlbums.value = data
|
||||
})
|
||||
socket.on('updated_userArtist', data => {
|
||||
favoriteArtists.value = data
|
||||
})
|
||||
socket.on('updated_userTracks', data => {
|
||||
favoriteTracks.value = data
|
||||
})
|
||||
|
@ -1,15 +1,11 @@
|
||||
import { ref } from '@vue/composition-api'
|
||||
import { socket } from '@/utils/socket'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
const searchResult = ref({})
|
||||
|
||||
function performMainSearch(searchTerm) {
|
||||
socket.emit('mainSearch', { term: searchTerm })
|
||||
|
||||
socket.on('mainSearch', data => {
|
||||
fetchData('mainSearch', { term: searchTerm }).then(data => {
|
||||
searchResult.value = data
|
||||
|
||||
socket.off('mainSearch')
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,16 @@
|
||||
import { ref } from '@vue/composition-api'
|
||||
import { socket } from '@/utils/socket'
|
||||
import { fetchData } from '@/utils/api'
|
||||
|
||||
const result = ref({})
|
||||
|
||||
function performSearch({ term, type, start = 0, nb = 30 }) {
|
||||
socket.emit('search', {
|
||||
fetchData('search', {
|
||||
term,
|
||||
type,
|
||||
start,
|
||||
nb
|
||||
})
|
||||
|
||||
socket.on('search', data => {
|
||||
}).then(data => {
|
||||
result.value = data
|
||||
|
||||
socket.off('search')
|
||||
})
|
||||
}
|
||||
|
||||
|
21
src/utils/api.js
Normal file
21
src/utils/api.js
Normal file
@ -0,0 +1,21 @@
|
||||
export function fetchData(key, data = {}) {
|
||||
const url = new URL(`${window.location.origin}/api/${key}`)
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
url.searchParams.append(key, data[key])
|
||||
})
|
||||
|
||||
return fetch(url.href)
|
||||
.then(response => response.json())
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
export function sendToServer(key, data) {
|
||||
const url = new URL(`${window.location.origin}/api/${key}`)
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
url.searchParams.append(key, data[key])
|
||||
})
|
||||
|
||||
fetch(url.href).catch(console.error)
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import { socket } from '@/utils/socket'
|
||||
import { sendToServer } from '@/utils/api'
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {number} bitrate
|
||||
* @param {number|null} bitrate
|
||||
*/
|
||||
export function sendAddToQueue(url, bitrate = null) {
|
||||
if (!url) throw new Error('No URL given to sendAddToQueue function!')
|
||||
|
||||
socket.emit('addToQueue', { url, bitrate }, () => {})
|
||||
sendToServer('addToQueue', { url, bitrate })
|
||||
}
|
||||
|
||||
export function aggregateDownloadLinks(releases) {
|
||||
|
@ -1,8 +1,35 @@
|
||||
import store from '@/store'
|
||||
import io from 'socket.io-client'
|
||||
class CustomSocket extends WebSocket {
|
||||
constructor(args) {
|
||||
super(args)
|
||||
this.listeners = {}
|
||||
}
|
||||
emit(key, data) {
|
||||
if (this.readyState !== WebSocket.OPEN) return false
|
||||
|
||||
export const socket = io.connect('/')
|
||||
this.send(JSON.stringify({ key, data }))
|
||||
}
|
||||
on(key, cb) {
|
||||
if (Object.keys(this.listeners).indexOf(key) == -1){
|
||||
console.log('on:', key)
|
||||
this.listeners[key] = cb
|
||||
|
||||
socket.on('init_update', data => {
|
||||
store.dispatch('setAppInfo', data)
|
||||
this.addEventListener('message', event => {
|
||||
const messageData = JSON.parse(event.data)
|
||||
|
||||
if (messageData.key === key) {
|
||||
cb(messageData.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
off(key) {
|
||||
if (Object.keys(this.listeners).indexOf(key) != -1){
|
||||
console.log('off:', key)
|
||||
this.removeEventListener('message', this.listeners[key])
|
||||
delete this.listeners[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const socket = new CustomSocket('ws://' + location.host + '/')
|
||||
|
Loading…
Reference in New Issue
Block a user