Merge branch 'restful-refactor'

This commit is contained in:
Roberto Tonino 2021-05-05 20:43:34 +02:00
commit 1b063ac24c
24 changed files with 633 additions and 638 deletions

View File

@ -7,5 +7,6 @@
"trailingComma": "none", "trailingComma": "none",
"printWidth": 120, "printWidth": 120,
"arrowParens": "avoid", "arrowParens": "avoid",
"vueIndentScriptAndStyle": false "vueIndentScriptAndStyle": false,
} "endOfLine": "lf"
}

View File

@ -17,7 +17,6 @@
"@vue/composition-api": "^1.0.0-beta.21", "@vue/composition-api": "^1.0.0-beta.21",
"flag-icon-css": "^3.5.0", "flag-icon-css": "^3.5.0",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.15",
"socket.io-client": "^3.1.0",
"svg-country-flags": "^1.2.9", "svg-country-flags": "^1.2.9",
"toastify-js": "^1.9.3", "toastify-js": "^1.9.3",
"vue": "^2.6.12", "vue": "^2.6.12",

File diff suppressed because one or more lines are too long

View File

@ -28,10 +28,6 @@ export default {
find: 'vue', find: 'vue',
replacement: 'vue/dist/vue.esm' replacement: 'vue/dist/vue.esm'
}, },
{
find: 'socket.io-client',
replacement: 'socket.io-client/dist/socket.io.min'
},
{ {
find: '@', find: '@',
replacement: __dirname + '/src' replacement: __dirname + '/src'

View File

@ -69,7 +69,8 @@ export default {
// ConfirmModal // ConfirmModal
}, },
mounted() { mounted() {
socket.on('connect', () => { socket.addEventListener('open', (event) => {
console.log("Connected to WebSocket")
this.isSocketConnected = true this.isSocketConnected = true
}) })
} }

View File

@ -19,18 +19,42 @@ import router from '@/router'
import store from '@/store' import store from '@/store'
import { socket } from '@/utils/socket' import { socket } from '@/utils/socket'
import { fetchData } from '@/utils/api'
import { toast } from '@/utils/toasts' import { toast } from '@/utils/toasts'
import { isValidURL } from '@/utils/utils' import { isValidURL } from '@/utils/utils'
import { sendAddToQueue } from '@/utils/downloads' import { sendAddToQueue } from '@/utils/downloads'
/* ===== App initialization ===== */ /* ===== App initialization ===== */
function startApp() { async function startApp() {
new Vue({ new Vue({
store, store,
router, router,
i18n, i18n,
render: h => h(App) render: h => h(App)
}).$mount('#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() { function initClient() {
@ -52,7 +76,7 @@ document.addEventListener('paste', pasteEvent => {
if (router.currentRoute.name === 'Link Analyzer') { if (router.currentRoute.name === 'Link Analyzer') {
socket.emit('analyzeLink', pastedText) socket.emit('analyzeLink', pastedText)
} else { } else {
if (pastedText.indexOf("\n") != -1) pastedText = pastedText.replace(/\n/g, ';'); if (pastedText.indexOf('\n') != -1) pastedText = pastedText.replace(/\n/g, ';')
sendAddToQueue(pastedText) sendAddToQueue(pastedText)
} }
} else { } else {
@ -82,30 +106,11 @@ function setClientModeKeyBindings() {
/* ===== Socketio listeners ===== */ /* ===== Socketio listeners ===== */
// Debug messages for socketio // Debug messages for socketio
socket.on('message', function(msg) { socket.on('message', function (msg) {
console.log(msg) console.log(msg)
}) })
socket.on('logging_in', function() { function loggedIn(data) {
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) {
const { status, user } = data const { status, user } = data
switch (status) { 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`) // $('#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') // 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() { socket.on('logged_out', function() {
@ -145,40 +159,41 @@ socket.on('logged_out', function() {
store.dispatch('logout') store.dispatch('logout')
}) })
*/
socket.on('restoringQueue', function() { socket.on('restoringQueue', function () {
toast(i18n.t('toasts.restoringQueue'), 'loading', false, 'restoring_queue') toast(i18n.t('toasts.restoringQueue'), 'loading', false, 'restoring_queue')
}) })
socket.on('cancellingCurrentItem', function(uuid) { socket.on('cancellingCurrentItem', function (uuid) {
toast(i18n.t('toasts.cancellingCurrentItem'), 'loading', false, 'cancelling_' + uuid) toast(i18n.t('toasts.cancellingCurrentItem'), 'loading', false, 'cancelling_' + uuid)
}) })
socket.on('currentItemCancelled', function(uuid) { socket.on('currentItemCancelled', function (uuid) {
toast(i18n.t('toasts.currentItemCancelled'), 'done', true, 'cancelling_' + uuid) toast(i18n.t('toasts.currentItemCancelled'), 'done', true, 'cancelling_' + uuid)
}) })
socket.on('startAddingArtist', function(data) { socket.on('startAddingArtist', function (data) {
toast(i18n.t('toasts.startAddingArtist', { artist: data.name }), 'loading', false, 'artist_' + data.id) toast(i18n.t('toasts.startAddingArtist', { artist: data.name }), 'loading', false, 'artist_' + data.id)
}) })
socket.on('finishAddingArtist', function(data) { socket.on('finishAddingArtist', function (data) {
toast(i18n.t('toasts.finishAddingArtist', { artist: data.name }), 'done', true, 'artist_' + data.id) toast(i18n.t('toasts.finishAddingArtist', { artist: data.name }), 'done', true, 'artist_' + data.id)
}) })
socket.on('startConvertingSpotifyPlaylist', function(id) { socket.on('startConvertingSpotifyPlaylist', function (id) {
toast(i18n.t('toasts.startConvertingSpotifyPlaylist'), 'loading', false, 'spotifyplaylist_' + id) toast(i18n.t('toasts.startConvertingSpotifyPlaylist'), 'loading', false, 'spotifyplaylist_' + id)
}) })
socket.on('finishConvertingSpotifyPlaylist', function(id) { socket.on('finishConvertingSpotifyPlaylist', function (id) {
toast(i18n.t('toasts.finishConvertingSpotifyPlaylist'), 'done', true, 'spotifyplaylist_' + id) toast(i18n.t('toasts.finishConvertingSpotifyPlaylist'), 'done', true, 'spotifyplaylist_' + id)
}) })
socket.on('errorMessage', function(error) { socket.on('errorMessage', function (error) {
toast(error, 'error') toast(error, 'error')
}) })
socket.on('queueError', function(queueItem) { socket.on('queueError', function (queueItem) {
if (queueItem.errid) { if (queueItem.errid) {
toast(queueItem.link + ' - ' + i18n.t(`errors.ids.${queueItem.errid}`), 'error') toast(queueItem.link + ' - ' + i18n.t(`errors.ids.${queueItem.errid}`), 'error')
} else { } else {
@ -186,18 +201,18 @@ socket.on('queueError', function(queueItem) {
} }
}) })
socket.on('alreadyInQueue', function(data) { socket.on('alreadyInQueue', function (data) {
toast(i18n.t('toasts.alreadyInQueue', { item: data.title }), 'playlist_add_check') toast(i18n.t('toasts.alreadyInQueue', { item: data.title }), 'playlist_add_check')
}) })
socket.on('loginNeededToDownload', function(data) { socket.on('loginNeededToDownload', function (data) {
toast(i18n.t('toasts.loginNeededToDownload'), 'report') toast(i18n.t('toasts.loginNeededToDownload'), 'report')
}) })
socket.on('startGeneratingItems', function(data) { socket.on('startGeneratingItems', function (data) {
toast(i18n.t('toasts.startGeneratingItems', { n: data.total }), 'loading', false, 'batch_' + data.uuid) toast(i18n.t('toasts.startGeneratingItems', { n: data.total }), 'loading', false, 'batch_' + data.uuid)
}) })
socket.on('finishGeneratingItems', function(data) { socket.on('finishGeneratingItems', function (data) {
toast(i18n.t('toasts.finishGeneratingItems', { n: data.total }), 'done', true, 'batch_' + data.uuid) toast(i18n.t('toasts.finishGeneratingItems', { n: data.total }), 'done', true, 'batch_' + data.uuid)
}) })

View File

@ -113,23 +113,20 @@
import { computed, defineComponent, reactive, toRefs } from '@vue/composition-api' import { computed, defineComponent, reactive, toRefs } from '@vue/composition-api'
import { links } from '@/data/sidebar' import { links } from '@/data/sidebar'
import { socket } from '@/utils/socket'
import { useTheme } from '@/use/theme' import { useTheme } from '@/use/theme'
import deemixIcon from '@/assets/deemix-icon.svg' import deemixIcon from '@/assets/deemix-icon.svg'
export default defineComponent({ export default defineComponent({
setup(props, ctx) { setup(props, ctx) {
const state = reactive({ const state = reactive({
activeTablink: 'home', activeTablink: 'home',
updateAvailable: false,
links links
}) })
const { THEMES, currentTheme } = useTheme() const { THEMES, currentTheme } = useTheme()
/* === Add update notification near info === */ /* === Add update notification near info === */
socket.on('updateAvailable', () => { const updateAvailable = computed(() => ctx.root.$store.state.appInfo.updateAvailable)
state.updateAvailable = true
})
ctx.root.$router.afterEach((to, from) => { ctx.root.$router.afterEach((to, from) => {
const linkInSidebar = state.links.find(link => link.routerName === to.name) const linkInSidebar = state.links.find(link => link.routerName === to.name)
@ -141,6 +138,7 @@ export default defineComponent({
return { return {
...toRefs(state), ...toRefs(state),
updateAvailable,
THEMES, THEMES,
currentTheme, currentTheme,
isSlim: computed(() => ctx.root.$store.getters.getSlimSidebar), isSlim: computed(() => ctx.root.$store.getters.getSlimSidebar),

View File

@ -26,9 +26,7 @@
<li v-html="$t('about.thanks')"></li> <li v-html="$t('about.thanks')"></li>
<i18n path="about.upToDate.text" tag="li"> <i18n path="about.upToDate.text" tag="li">
<template #newsChannel> <template #newsChannel>
<a href="https://t.me/RemixDevNews" target="_blank">{{ <a href="https://t.me/RemixDevNews" target="_blank">{{ $t('about.upToDate.newsChannel') }}</a>
$t('about.upToDate.newsChannel')
}}</a>
</template> </template>
</i18n> </i18n>
</ul> </ul>
@ -42,9 +40,7 @@
<a href="https://git.rip/RemixDev/deemix" target="_blank">🚀 {{ $t('about.officialRepo') }}</a> <a href="https://git.rip/RemixDev/deemix" target="_blank">🚀 {{ $t('about.officialRepo') }}</a>
</li> </li>
<li> <li>
<a href="https://git.rip/RemixDev/deemix-webui" target="_blank"> <a href="https://git.rip/RemixDev/deemix-webui" target="_blank"> 💻 {{ $t('about.officialWebuiRepo') }} </a>
💻 {{ $t('about.officialWebuiRepo') }}
</a>
</li> </li>
<li> <li>
<a href="https://www.reddit.com/r/deemix" target="_blank">🤖 {{ $t('about.officialSubreddit') }}</a> <a href="https://www.reddit.com/r/deemix" target="_blank">🤖 {{ $t('about.officialSubreddit') }}</a>
@ -63,9 +59,7 @@
<ul> <ul>
<i18n path="about.questions.text" tag="li"> <i18n path="about.questions.text" tag="li">
<template #subreddit> <template #subreddit>
<a href="https://www.reddit.com/r/deemix" target="_blank">{{ <a href="https://www.reddit.com/r/deemix" target="_blank">{{ $t('about.questions.subreddit') }}</a>
$t('about.questions.subreddit')
}}</a>
</template> </template>
</i18n> </i18n>
<li> <li>
@ -241,7 +235,7 @@ ul {
</style> </style>
<script> <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' import { useOnline } from '@/use/online'

View File

@ -92,7 +92,6 @@ import { orderBy } from 'lodash-es'
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs' import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
import { socket } from '@/utils/socket'
import { sendAddToQueue } from '@/utils/downloads' import { sendAddToQueue } from '@/utils/downloads'
import { checkNewRelease } from '@/utils/dates' import { checkNewRelease } from '@/utils/dates'
import { formatArtistData, getArtistData } from '@/data/artist' import { formatArtistData, getArtistData } from '@/data/artist'

View File

@ -6,38 +6,38 @@
<div class="release-grid"> <div class="release-grid">
<div <div
v-for="release in countries" 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" :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>
</div> </div>
<div v-else> <div v-else>
<button class="btn btn-primary" @click="onChangeCountry">{{ $t('charts.changeCountry') }}</button> <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') }} {{ $t('charts.download') }}
</button> </button>
<table class="table table--charts"> <table class="table table--charts">
<tbody> <tbody>
<tr v-for="track in chart" class="track_row"> <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 }} {{ track.position }}
</td> </td>
<td class="table__icon table__icon--big"> <td class="table__icon table__icon--big">
<span <span
@click="playPausePreview"
class="relative inline-block rounded cursor-pointer"
:data-preview="track.preview" :data-preview="track.preview"
class="relative inline-block rounded cursor-pointer"
@click="playPausePreview"
> >
<PreviewControls v-if="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> </span>
</td> </td>
<td class="table__cell--large"> <td class="table__cell--large">
@ -47,16 +47,16 @@
}} }}
</td> </td>
<router-link <router-link
tag="td"
class="table__cell table__cell--medium table__cell--center clickable"
:to="{ name: 'Artist', params: { id: track.artist.id } }" :to="{ name: 'Artist', params: { id: track.artist.id } }"
class="table__cell table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.artist.name }} {{ track.artist.name }}
</router-link> </router-link>
<router-link <router-link
tag="td"
class="table__cell--medium table__cell--center clickable"
:to="{ name: 'Album', params: { id: track.album.id } }" :to="{ name: 'Album', params: { id: track.album.id } }"
class="table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.album.title }} {{ track.album.title }}
</router-link> </router-link>
@ -64,15 +64,15 @@
{{ convertDuration(track.duration) }} {{ convertDuration(track.duration) }}
</td> </td>
<td <td
class="cursor-pointer group"
@click.stop="addToQueue"
:data-link="track.link" :data-link="track.link"
role="button"
aria-label="download" aria-label="download"
class="cursor-pointer group"
role="button"
@click.stop="addToQueue"
> >
<i <i
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
:title="$t('globals.download_hint')" :title="$t('globals.download_hint')"
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
> >
get_app get_app
</i> </i>
@ -85,14 +85,14 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'
import { socket } from '@/utils/socket' import { socket } from '@/utils/socket'
import { sendAddToQueue } from '@/utils/downloads' import { sendAddToQueue } from '@/utils/downloads'
import { convertDuration } from '@/utils/utils' import { convertDuration } from '@/utils/utils'
import { getChartsData } from '@/data/charts' import { getChartsData, getChartTracks } from '@/data/charts'
import PreviewControls from '@components/globals/PreviewControls.vue' import PreviewControls from '@components/globals/PreviewControls.vue'
import { playPausePreview } from '@components/globals/TheTrackPreview.vue' import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
import { fetchData } from '@/utils/api'
export default { export default {
components: { components: {
@ -116,12 +116,12 @@ export default {
} }
}, },
async created() { async created() {
socket.on('setChartTracks', this.setTracklist) // socket.on('setChartTracks', this.setTracklist)
this.$on('hook:destroyed', () => { // this.$on('hook:destroyed', () => {
socket.off('setChartTracks') // socket.off('setChartTracks')
}) // })
let chartsData = await getChartsData() let { data: chartsData } = await getChartsData()
let worldwideChart let worldwideChart
chartsData = chartsData.filter(item => { chartsData = chartsData.filter(item => {
@ -147,17 +147,13 @@ export default {
const { const {
currentTarget: { currentTarget: {
dataset: { title } dataset: { title, id }
},
currentTarget: {
dataset: { id }
} }
} = event } = event
this.country = title this.country = title
localStorage.setItem('chart', this.country) localStorage.setItem('chart', this.country)
this.id = id this.id = id
socket.emit('getChartTracks', this.id)
}, },
setTracklist(data) { setTracklist(data) {
this.chart = data this.chart = data
@ -185,6 +181,15 @@ export default {
localStorage.setItem('chart', this.country) localStorage.setItem('chart', this.country)
} }
} }
},
watch: {
id(newId) {
const isActualChart = newId !== 0
if (isActualChart) {
getChartTracks(newId).then(response => this.setTracklist(response.result))
}
}
} }
} }
</script> </script>

View File

@ -3,13 +3,13 @@
<h1 class="mb-8 text-5xl"> <h1 class="mb-8 text-5xl">
{{ $t('favorites.title') }} {{ $t('favorites.title') }}
<div <div
@click="refreshFavorites"
class="inline-block clickable"
ref="reloadButton" ref="reloadButton"
role="button"
aria-label="reload" 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> </div>
</h1> </h1>
@ -19,7 +19,7 @@
</BaseTab> </BaseTab>
</BaseTabs> </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()) }) }} {{ $t('globals.download', { thing: $tc(`globals.listTabs.${activeTab}N`, getTabLength()) }) }}
</button> </button>
@ -27,10 +27,10 @@
<div v-if="playlists.length == 0"> <div v-if="playlists.length == 0">
<h1>{{ $t('favorites.noPlaylists') }}</h1> <h1>{{ $t('favorites.noPlaylists') }}</h1>
</div> </div>
<div class="release-grid" v-if="playlists.length > 0 || spotifyPlaylists > 0"> <div v-if="playlists.length > 0 || spotifyPlaylists > 0" class="release-grid">
<div class="release" v-for="release in playlists" :key="release.id"> <div v-for="release in playlists" :key="release.id" class="release">
<router-link tag="div" class="cursor-pointer" :to="{ name: 'Playlist', params: { id: release.id } }"> <router-link :to="{ name: 'Playlist', params: { id: release.id } }" class="cursor-pointer" tag="div">
<CoverContainer is-rounded :cover="release.picture_medium" :link="release.link" @click.stop="addToQueue" /> <CoverContainer :cover="release.picture_medium" :link="release.link" is-rounded @click.stop="addToQueue" />
<p class="primary-text">{{ release.title }}</p> <p class="primary-text">{{ release.title }}</p>
</router-link> </router-link>
@ -44,9 +44,9 @@
</p> </p>
</div> </div>
<div class="release" v-for="release in spotifyPlaylists" :key="release.id"> <div v-for="release in spotifyPlaylists" :key="release.id" class="release">
<router-link tag="div" class="cursor-pointer" :to="{ name: 'Spotify Playlist', params: { id: release.id } }"> <router-link :to="{ name: 'Spotify Playlist', params: { id: release.id } }" class="cursor-pointer" tag="div">
<CoverContainer is-rounded :cover="release.picture_medium" :link="release.link" @click.stop="addToQueue" /> <CoverContainer :cover="release.picture_medium" :link="release.link" is-rounded @click.stop="addToQueue" />
<p class="primary-text">{{ release.title }}</p> <p class="primary-text">{{ release.title }}</p>
</router-link> </router-link>
@ -66,15 +66,15 @@
<div v-if="albums.length == 0"> <div v-if="albums.length == 0">
<h1>{{ $t('favorites.noAlbums') }}</h1> <h1>{{ $t('favorites.noAlbums') }}</h1>
</div> </div>
<div class="release-grid" v-if="albums.length > 0"> <div v-if="albums.length > 0" class="release-grid">
<router-link <router-link
tag="div"
class="release clickable"
v-for="release in albums" v-for="release in albums"
:key="release.id" :key="release.id"
:to="{ name: 'Album', params: { id: 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="primary-text">{{ release.title }}</p>
<p class="secondary-text">{{ `${$t('globals.by', { artist: release.artist.name })}` }}</p> <p class="secondary-text">{{ `${$t('globals.by', { artist: release.artist.name })}` }}</p>
</router-link> </router-link>
@ -85,15 +85,15 @@
<div v-if="artists.length == 0"> <div v-if="artists.length == 0">
<h1>{{ $t('favorites.noArtists') }}</h1> <h1>{{ $t('favorites.noArtists') }}</h1>
</div> </div>
<div class="release-grid" v-if="artists.length > 0"> <div v-if="artists.length > 0" class="release-grid">
<router-link <router-link
tag="div"
class="release clickable"
v-for="release in artists" v-for="release in artists"
:key="release.id" :key="release.id"
:to="{ name: 'Artist', params: { id: 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> <p class="primary-text">{{ release.name }}</p>
</router-link> </router-link>
</div> </div>
@ -105,17 +105,17 @@
</div> </div>
<table v-if="tracks.length > 0" class="table"> <table v-if="tracks.length > 0" class="table">
<tr v-for="track in tracks" class="track_row"> <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 }} {{ track.position }}
</td> </td>
<td> <td>
<span <span
:data-preview="track.preview"
class="relative inline-block rounded cursor-pointer" class="relative inline-block rounded cursor-pointer"
@click="playPausePreview" @click="playPausePreview"
:data-preview="track.preview"
> >
<PreviewControls v-if="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> </span>
</td> </td>
<td class="table__cell--large"> <td class="table__cell--large">
@ -125,16 +125,16 @@
}} }}
</td> </td>
<router-link <router-link
tag="td"
class="table__cell table__cell--medium table__cell--center clickable"
:to="{ name: 'Artist', params: { id: track.artist.id } }" :to="{ name: 'Artist', params: { id: track.artist.id } }"
class="table__cell table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.artist.name }} {{ track.artist.name }}
</router-link> </router-link>
<router-link <router-link
tag="td"
class="table__cell--medium table__cell--center clickable"
:to="{ name: 'Album', params: { id: track.album.id } }" :to="{ name: 'Album', params: { id: track.album.id } }"
class="table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.album.title }} {{ track.album.title }}
</router-link> </router-link>
@ -142,16 +142,16 @@
{{ convertDuration(track.duration) }} {{ convertDuration(track.duration) }}
</td> </td>
<td <td
class="cursor-pointer group"
@click.stop="addToQueue"
:data-link="track.link" :data-link="track.link"
role="button"
aria-label="download" aria-label="download"
class="cursor-pointer group"
role="button"
@click.stop="addToQueue"
> >
<div class="table__cell-content table__cell-content--vertical-center"> <div class="table__cell-content table__cell-content--vertical-center">
<i <i
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
:title="$t('globals.download_hint')" :title="$t('globals.download_hint')"
class="transition-colors duration-150 ease-in-out material-icons group-hover:text-primary"
> >
get_app get_app
</i> </i>
@ -164,14 +164,14 @@
</template> </template>
<script> <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 PreviewControls from '@components/globals/PreviewControls.vue'
import CoverContainer from '@components/globals/CoverContainer.vue' import CoverContainer from '@components/globals/CoverContainer.vue'
import { playPausePreview } from '@components/globals/TheTrackPreview.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 { convertDuration } from '@/utils/utils'
import { toast } from '@/utils/toasts' import { toast } from '@/utils/toasts'
import { useFavorites } from '@/use/favorites' import { useFavorites } from '@/use/favorites'
@ -197,7 +197,8 @@ export default defineComponent({
isRefreshingFavorites, isRefreshingFavorites,
refreshFavorites refreshFavorites
} = useFavorites() } = useFavorites()
const reloadButton = computed(() => ctx.refs.reloadButton)
refreshFavorites({ isInitial: true })
watch(isRefreshingFavorites, (newVal, oldVal) => { watch(isRefreshingFavorites, (newVal, oldVal) => {
// If oldVal is true and newOne is false, it means that a refreshing has just terminated // If oldVal is true and newOne is false, it means that a refreshing has just terminated

View File

@ -783,6 +783,7 @@ import { copyToClipboard } from '@/utils/utils'
import BaseAccordion from '@/components/globals/BaseAccordion.vue' import BaseAccordion from '@/components/globals/BaseAccordion.vue'
import TemplateVariablesList from '@components/settings/TemplateVariablesList.vue' import TemplateVariablesList from '@components/settings/TemplateVariablesList.vue'
import { fetchData, sendToServer } from '@/utils/api'
export default { export default {
components: { components: {
@ -825,7 +826,7 @@ export default {
get() { get() {
return this.previewVolume return this.previewVolume
}, },
set: debounce(function(value) { set: debounce(function (value) {
this.setPreviewVolume(value) this.setPreviewVolume(value)
}, 20) }, 20)
}, },
@ -949,16 +950,17 @@ export default {
this.lastCredentials = JSON.parse(JSON.stringify(credentials)) this.lastCredentials = JSON.parse(JSON.stringify(credentials))
this.spotifyFeatures = JSON.parse(JSON.stringify(credentials)) this.spotifyFeatures = JSON.parse(JSON.stringify(credentials))
}, },
loggedInViaDeezer(arl) { async loggedInViaDeezer(arl) {
this.dispatchARL({ arl }) this.dispatchARL({ arl })
socket.emit('login', arl, true, this.accountNum)
// this.login() // this.login()
// const res = await fetchData('login', { arl, force: true, child: this.accountNum })
}, },
login() { async login() {
let newArl = this.$refs.loginInput.value.trim() let newArl = this.$refs.loginInput.value.trim()
if (newArl && newArl !== this.arl) { 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) { appLogin(e) {
@ -969,9 +971,7 @@ export default {
}, },
accountChanged(user, accountNum) { accountChanged(user, accountNum) {
this.$refs.username.innerText = user.name this.$refs.username.innerText = user.name
this.$refs.userpicture.src = `https://e-cdns-images.dzcdn.net/images/user/${ this.$refs.userpicture.src = `https://e-cdns-images.dzcdn.net/images/user/${user.picture}/125x125-000000-80-0-0.jpg`
user.picture
}/125x125-000000-80-0-0.jpg`
this.accountNum = accountNum this.accountNum = accountNum
localStorage.setItem('accountNum', this.accountNum) localStorage.setItem('accountNum', this.accountNum)

View File

@ -1,5 +1,5 @@
<template> <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 <header
:style="{ :style="{
'background-image': 'background-image':
@ -30,7 +30,7 @@
<i class="material-icons">timer</i> <i class="material-icons">timer</i>
</th> </th>
<th class="table__icon table__cell--center clickable"> <th class="table__icon table__cell--center clickable">
<input @click="toggleAll" class="selectAll" type="checkbox" /> <input class="selectAll" type="checkbox" @click="toggleAll" />
</th> </th>
</tr> </tr>
</thead> </thead>
@ -41,15 +41,15 @@
<td class="table__cell--x-small table__cell--center"> <td class="table__cell--x-small table__cell--center">
<div class="table__cell-content table__cell-content--vertical-center"> <div class="table__cell-content table__cell-content--vertical-center">
<i <i
class="material-icons" v-on="{ click: track.preview ? playPausePreview : false }"
:class="{ :class="{
preview_playlist_controls: track.preview, preview_playlist_controls: track.preview,
'cursor-pointer': track.preview, 'cursor-pointer': track.preview,
disabled: !track.preview disabled: !track.preview
}" }"
v-on="{ click: track.preview ? playPausePreview : false }"
:data-preview="track.preview" :data-preview="track.preview"
:title="$t('globals.play_hint')" :title="$t('globals.play_hint')"
class="material-icons"
> >
play_arrow play_arrow
</i> </i>
@ -70,28 +70,28 @@
</div> </div>
</td> </td>
<router-link <router-link
tag="td"
class="table__cell--medium table__cell--center clickable"
:to="{ name: 'Artist', params: { id: track.artist.id } }" :to="{ name: 'Artist', params: { id: track.artist.id } }"
class="table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.artist.name }} {{ track.artist.name }}
</router-link> </router-link>
<router-link <router-link
tag="td"
v-if="type === 'playlist'" v-if="type === 'playlist'"
class="table__cell--medium table__cell--center clickable"
:to="{ name: 'Album', params: { id: track.album.id } }" :to="{ name: 'Album', params: { id: track.album.id } }"
class="table__cell--medium table__cell--center clickable"
tag="td"
> >
{{ track.album.title }} {{ track.album.title }}
</router-link> </router-link>
<td <td
class="table__cell--center"
:class="{ 'table__cell--small': type === 'album', 'table__cell--x-small': type === 'playlist' }" :class="{ 'table__cell--small': type === 'album', 'table__cell--x-small': type === 'playlist' }"
class="table__cell--center"
> >
{{ convertDuration(track.duration) }} {{ convertDuration(track.duration) }}
</td> </td>
<td class="table__icon table__cell--center"> <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> </td>
</tr> </tr>
<tr v-else-if="track.type == 'disc_separator'" class="table__row-no-highlight" style="opacity: 0.54"> <tr v-else-if="track.type == 'disc_separator'" class="table__row-no-highlight" style="opacity: 0.54">
@ -112,14 +112,14 @@
<td> <td>
<i <i
v-if="track.preview_url" v-if="track.preview_url"
@click="playPausePreview"
class="material-icons"
:class="{ :class="{
preview_playlist_controls: track.preview_url, preview_playlist_controls: track.preview_url,
'cursor-pointer': track.preview_url 'cursor-pointer': track.preview_url
}" }"
:data-preview="track.preview_url" :data-preview="track.preview_url"
:title="$t('globals.play_hint')" :title="$t('globals.play_hint')"
class="material-icons"
@click="playPausePreview"
> >
play_arrow play_arrow
</i> </i>
@ -133,17 +133,17 @@
<td>{{ track.artists[0].name }}</td> <td>{{ track.artists[0].name }}</td>
<td>{{ track.album.name }}</td> <td>{{ track.album.name }}</td>
<td>{{ convertDuration(Math.floor(track.duration_ms / 1000)) }}</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> </tr>
</template> </template>
</tbody> </tbody>
</table> </table>
<span v-if="label" style="opacity: 0.4; margin-top: 8px; display: inline-block; font-size: 13px">{{ label }}</span> <span v-if="label" style="opacity: 0.4; margin-top: 8px; display: inline-block; font-size: 13px">{{ label }}</span>
<footer class="bg-background-main"> <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) })}` }} {{ `${$t('globals.download', { thing: $tc(`globals.listTabs.${type}`, 1) })}` }}
</button> </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> {{ $t('tracklist.downloadSelection') }}<i class="ml-2 material-icons">file_download</i>
</button> </button>
</footer> </footer>
@ -156,6 +156,7 @@ import { socket } from '@/utils/socket'
import { sendAddToQueue } from '@/utils/downloads' import { sendAddToQueue } from '@/utils/downloads'
import Utils from '@/utils/utils' import Utils from '@/utils/utils'
import { playPausePreview } from '@components/globals/TheTrackPreview.vue' import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
import EventBus from '@/utils/EventBus'
export default { export default {
data() { data() {
@ -172,9 +173,9 @@ export default {
} }
}, },
mounted() { mounted() {
socket.on('show_album', this.showAlbum) EventBus.$on('showAlbum', this.showAlbum)
socket.on('show_playlist', this.showPlaylist) EventBus.$on('showPlaylist', this.showPlaylist)
socket.on('show_spotifyplaylist', this.showSpotifyPlaylist) EventBus.$on('showSpotifyPlaylist', this.showSpotifyPlaylist)
}, },
methods: { methods: {
playPausePreview, playPausePreview,
@ -193,17 +194,17 @@ export default {
}, },
toggleAll(e) { toggleAll(e) {
this.body.forEach(item => { this.body.forEach(item => {
if (item.type == 'track') { if (item.type === 'track') {
item.selected = e.currentTarget.checked item.selected = e.currentTarget.checked
} }
}) })
}, },
selectedLinks() { selectedLinks() {
var selected = [] const selected = []
if (this.body) { if (this.body) {
this.body.forEach(item => { this.body.forEach(item => {
if (item.type == 'track' && item.selected) if (item.type === 'track' && item.selected)
selected.push(this.type == 'spotifyPlaylist' ? item.uri : item.link) selected.push(this.type === 'spotifyPlaylist' ? item.uri : item.link)
}) })
} }
return selected.join(';') return selected.join(';')
@ -305,4 +306,3 @@ export default {
} }
} }
</script> </script>

View File

@ -1,5 +1,5 @@
import { socket } from '@/utils/socket'
import { getPropertyWithFallback } from '@/utils/utils' import { getPropertyWithFallback } from '@/utils/utils'
import { fetchData } from '@/utils/api'
export function formatArtistData(artistData) { export function formatArtistData(artistData) {
return { return {
@ -36,15 +36,8 @@ function formatArtistReleases(artistReleases) {
} }
export function getArtistData(artistID) { export function getArtistData(artistID) {
socket.emit('getTracklist', { return fetchData('getTracklist', {
type: 'artist', type: 'artist',
id: artistID id: artistID
}) })
return new Promise((resolve, reject) => {
socket.on('show_artist', data => {
socket.off('show_artist')
resolve(data)
})
})
} }

View File

@ -1,22 +1,9 @@
import { socket } from '@/utils/socket' import { fetchData } from '@/utils/api'
let chartsData = {}
let cached = false
export function getChartsData() { export function getChartsData() {
if (cached) { return fetchData('getCharts')
return chartsData }
} else {
socket.emit('get_charts_data') export function getChartTracks(chartId) {
return fetchData('getChartTracks', { id: chartId })
return new Promise((resolve, reject) => {
socket.on('init_charts', data => {
chartsData = data
cached = true
socket.off('init_charts')
resolve(data)
})
})
}
} }

View File

@ -1,22 +1,17 @@
import { socket } from '@/utils/socket' import { fetchData } from '@/utils/api'
let homeData = {} let homeData = {}
let cached = false let cached = false
export function getHomeData() { export async function getHomeData() {
if (cached) { if (cached) {
return homeData return homeData
} else { } else {
socket.emit('get_home_data') const data = await fetchData('getHome')
return new Promise((resolve, reject) => { homeData = data
socket.on('init_home', data => { cached = true
homeData = data
cached = true
socket.off('init_home') return data
resolve(data)
})
})
} }
} }

View File

@ -7,8 +7,5 @@
"@components/*": ["./components/*"] "@components/*": ["./components/*"]
} }
}, },
"typeAcquisition": {
"include": ["socket.io-client"]
},
"exclude": ["assets/**/*", "styles/**/*"] "exclude": ["assets/**/*", "styles/**/*"]
} }

View File

@ -1,6 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import { socket } from '@/utils/socket'
// Pages // Pages
import About from '@components/pages/About.vue' 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 Search from '@components/pages/Search.vue'
import Settings from '@components/pages/Settings.vue' import Settings from '@components/pages/Settings.vue'
import Tracklist from '@components/pages/Tracklist.vue' import Tracklist from '@components/pages/Tracklist.vue'
import { fetchData } from '@/utils/api'
import EventBus from '@/utils/EventBus'
Vue.use(VueRouter) Vue.use(VueRouter)
@ -125,44 +126,51 @@ const router = new VueRouter({
}) })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
let getTracklistParams = null
switch (to.name) { switch (to.name) {
case 'Tracklist': case 'Tracklist': {
getTracklistParams = { // const getTracklistParams = {
type: to.params.type, // type: to.params.type,
id: to.params.id // id: to.params.id
} // }
console.warn('This should never happen.')
break break
case 'Album': }
getTracklistParams = { case 'Album': {
const getTracklistParams = {
type: 'album', type: 'album',
id: to.params.id id: to.params.id
} }
fetchData('getTracklist', getTracklistParams).then(albumData => {
EventBus.$emit('showAlbum', albumData)
})
break break
case 'Playlist': }
getTracklistParams = { case 'Playlist': {
const getTracklistParams = {
type: 'playlist', type: 'playlist',
id: to.params.id id: to.params.id
} }
fetchData('getTracklist', getTracklistParams).then(playlistData => {
EventBus.$emit('showPlaylist', playlistData)
})
break break
case 'Spotify Playlist': }
getTracklistParams = { case 'Spotify Playlist': {
const getTracklistParams = {
type: 'spotifyplaylist', type: 'spotifyplaylist',
id: to.params.id id: to.params.id
} }
fetchData('getTracklist', getTracklistParams).then(spotifyPlaylistData => {
EventBus.$emit('showSpotifyPlaylist', spotifyPlaylistData)
})
break break
}
default: default:
break break
} }
if (getTracklistParams) {
socket.emit('getTracklist', getTracklistParams)
}
next() next()
}) })
export default router export default router

View File

@ -1,7 +1,7 @@
import { ref } from '@vue/composition-api' import { ref } from '@vue/composition-api'
import store from '@/store' import store from '@/store'
import { socket } from '@/utils/socket' import { fetchData } from '@/utils/api'
const favoriteArtists = ref([]) const favoriteArtists = ref([])
const favoriteAlbums = ref([]) const favoriteAlbums = ref([])
@ -11,22 +11,35 @@ const favoriteTracks = ref([])
const isRefreshingFavorites = ref(false) const isRefreshingFavorites = ref(false)
if (store.getters.isLoggedIn) {
refreshFavorites({ isInitial: true })
}
function refreshFavorites({ isInitial = false }) { function refreshFavorites({ isInitial = false }) {
if (!isInitial) { if (!isInitial) {
isRefreshingFavorites.value = true isRefreshingFavorites.value = true
} }
socket.emit('get_favorites_data') fetchData('getUserFavorites').then(setAllFavorites).catch(console.error)
if (store.getters.isLoggedWithSpotify) { 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() { export function useFavorites() {
return { return {
favoriteArtists, favoriteArtists,
@ -38,42 +51,3 @@ export function useFavorites() {
refreshFavorites 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
})

View File

@ -1,15 +1,11 @@
import { ref } from '@vue/composition-api' import { ref } from '@vue/composition-api'
import { socket } from '@/utils/socket' import { fetchData } from '@/utils/api'
const searchResult = ref({}) const searchResult = ref({})
function performMainSearch(searchTerm) { function performMainSearch(searchTerm) {
socket.emit('mainSearch', { term: searchTerm }) fetchData('mainSearch', { term: searchTerm }).then(data => {
socket.on('mainSearch', data => {
searchResult.value = data searchResult.value = data
socket.off('mainSearch')
}) })
} }

View File

@ -1,20 +1,16 @@
import { ref } from '@vue/composition-api' import { ref } from '@vue/composition-api'
import { socket } from '@/utils/socket' import { fetchData } from '@/utils/api'
const result = ref({}) const result = ref({})
function performSearch({ term, type, start = 0, nb = 30 }) { function performSearch({ term, type, start = 0, nb = 30 }) {
socket.emit('search', { fetchData('search', {
term, term,
type, type,
start, start,
nb nb
}) }).then(data => {
socket.on('search', data => {
result.value = data result.value = data
socket.off('search')
}) })
} }

21
src/utils/api.js Normal file
View 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)
}

View File

@ -1,13 +1,13 @@
import { socket } from '@/utils/socket' import { sendToServer } from '@/utils/api'
/** /**
* @param {string} url * @param {string} url
* @param {number} bitrate * @param {number|null} bitrate
*/ */
export function sendAddToQueue(url, bitrate = null) { export function sendAddToQueue(url, bitrate = null) {
if (!url) throw new Error('No URL given to sendAddToQueue function!') if (!url) throw new Error('No URL given to sendAddToQueue function!')
socket.emit('addToQueue', { url, bitrate }, () => {}) sendToServer('addToQueue', { url, bitrate })
} }
export function aggregateDownloadLinks(releases) { export function aggregateDownloadLinks(releases) {

View File

@ -1,8 +1,35 @@
import store from '@/store' class CustomSocket extends WebSocket {
import io from 'socket.io-client' 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 => { this.addEventListener('message', event => {
store.dispatch('setAppInfo', data) 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 + '/')