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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user