✨ (spotify) add spotify player
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div class="chilled-music">
|
||||
<transition name="fade">
|
||||
<img v-show="!ready" src="@/assets/loader.svg" :alt="$t('loading')" />
|
||||
<img
|
||||
v-show="!ready"
|
||||
class="loader"
|
||||
src="@/assets/loader.svg"
|
||||
:alt="$t('music.loading')"
|
||||
/>
|
||||
</transition>
|
||||
<section v-show="false">
|
||||
<div
|
||||
@@ -99,29 +104,3 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
animation: rotating 4s linear infinite;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
{
|
||||
"en": {
|
||||
"loading": "loading..."
|
||||
},
|
||||
"fr": {
|
||||
"loading": "chargement..."
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api'
|
||||
import { useInterval } from '../hooks/useInterval'
|
||||
import { useInterval } from '@/hooks/useInterval'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntervalInput',
|
||||
|
||||
36
src/components/SpotifyMusic.vue
Normal file
36
src/components/SpotifyMusic.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="spotify-music">
|
||||
<transition name="fade">
|
||||
<img
|
||||
v-show="!ready"
|
||||
class="loader"
|
||||
src="@/assets/loader.svg"
|
||||
:alt="$t('music.loading')"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watchEffect } from '@vue/composition-api'
|
||||
import { useSpotify } from '@/hooks/useSpotify'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SpotifyMusic',
|
||||
props: {
|
||||
play: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { ready, play, pause } = useSpotify()
|
||||
|
||||
watchEffect(() => {
|
||||
props.play ? play() : pause()
|
||||
})
|
||||
|
||||
return { ready, play, pause }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -20,16 +20,25 @@
|
||||
:class="{ selected: hasMusic, disabled: !hasMusic }"
|
||||
>
|
||||
<span v-t="'chill'"></span>
|
||||
<SpotifyMusic
|
||||
v-if="hasSpotify"
|
||||
:play="play"
|
||||
@play="onPlay"
|
||||
@pause="onPause"
|
||||
/>
|
||||
<ChilledMusic
|
||||
class="chilled-music"
|
||||
v-if="hasMusic"
|
||||
v-else-if="hasMusic"
|
||||
:play="play"
|
||||
@play="play"
|
||||
@pause="pause"
|
||||
@play="onPlay"
|
||||
@pause="onPause"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<p v-if="!hasSpotify">
|
||||
connect with <a href="#" @click.prevent="setHasSpotify(true)">Spotify</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +49,8 @@ import { useMusic } from '@/hooks/useMusic'
|
||||
export default defineComponent({
|
||||
name: 'ZoneMusic',
|
||||
components: {
|
||||
ChilledMusic: () => import('@/components/ChilledMusic.vue')
|
||||
ChilledMusic: () => import('@/components/ChilledMusic.vue'),
|
||||
SpotifyMusic: () => import('@/components/SpotifyMusic.vue')
|
||||
},
|
||||
props: {
|
||||
play: {
|
||||
@@ -49,17 +59,31 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(_, { emit }) {
|
||||
const { isMusicAvailable, hasMusic, setHasMusic } = useMusic()
|
||||
const {
|
||||
isMusicAvailable,
|
||||
hasSpotify,
|
||||
setHasSpotify,
|
||||
hasMusic,
|
||||
setHasMusic
|
||||
} = useMusic()
|
||||
|
||||
const play = () => {
|
||||
const onPlay = () => {
|
||||
emit('play')
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
const onPause = () => {
|
||||
emit('pause')
|
||||
}
|
||||
|
||||
return { isMusicAvailable, hasMusic, setHasMusic, play, pause }
|
||||
return {
|
||||
isMusicAvailable,
|
||||
hasSpotify,
|
||||
setHasSpotify,
|
||||
hasMusic,
|
||||
setHasMusic,
|
||||
onPlay,
|
||||
onPause
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -110,6 +134,10 @@ label[data-pm-holder] {
|
||||
.disabled {
|
||||
color: desaturate($color, 70%);
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $serif-font-family;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
|
||||
@@ -5,12 +5,20 @@ import { State, RootActions } from '@/store'
|
||||
export const useMusic = () => {
|
||||
const isIOS =
|
||||
navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)
|
||||
const { hasMusic } = useGetters<GetterTree<State, State>>(['hasMusic'])
|
||||
const { setHasMusic } = useActions<RootActions>(['setHasMusic'])
|
||||
const { hasMusic, hasSpotify } = useGetters<GetterTree<State, State>>([
|
||||
'hasMusic',
|
||||
'hasSpotify'
|
||||
])
|
||||
const { setHasMusic, setHasSpotify } = useActions<RootActions>([
|
||||
'setHasMusic',
|
||||
'setHasSpotify'
|
||||
])
|
||||
|
||||
return {
|
||||
isMusicAvailable: !isIOS,
|
||||
hasMusic,
|
||||
setHasMusic
|
||||
setHasMusic,
|
||||
hasSpotify,
|
||||
setHasSpotify
|
||||
}
|
||||
}
|
||||
|
||||
89
src/hooks/useSpotify.ts
Normal file
89
src/hooks/useSpotify.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/* eslint-disable */
|
||||
// @ts-ignore
|
||||
// @ts-nocheck
|
||||
import '@/lib/spotify-player'
|
||||
import { useGetters, useActions } from 'vuex-composition-helpers'
|
||||
import { GetterTree } from 'vuex'
|
||||
import { State, RootActions } from '@/store'
|
||||
import {
|
||||
redirectToSpotifyConnect,
|
||||
playOnSpotify,
|
||||
pauseOnSpotify
|
||||
} from '@/utils/spotify'
|
||||
import { ref, reactive } from '@vue/composition-api'
|
||||
|
||||
export const useSpotify = () => {
|
||||
const ready = ref(false)
|
||||
const deviceId = ref<string | null>(null)
|
||||
const spotify = reactive({
|
||||
player: null
|
||||
})
|
||||
const { accessToken, hasValidAccessToken } = useGetters<
|
||||
GetterTree<State, State>
|
||||
>(['accessToken', 'hasValidAccessToken'])
|
||||
|
||||
window.onSpotifyWebPlaybackSDKReady = async () => {
|
||||
if (!hasValidAccessToken.value) {
|
||||
redirectToSpotifyConnect()
|
||||
return
|
||||
}
|
||||
|
||||
spotify.player = new Spotify.Player({
|
||||
name: 'binome',
|
||||
getOAuthToken: (callback) => callback(accessToken.value)
|
||||
})
|
||||
spotify.player.addListener('ready', ({ device_id }) => {
|
||||
deviceId.value = device_id
|
||||
ready.value = true
|
||||
})
|
||||
await spotify.player.connect()
|
||||
}
|
||||
|
||||
const play = async () => {
|
||||
try {
|
||||
if (spotify.player && ready.value) {
|
||||
const playerState = await spotify.player.getCurrentState()
|
||||
console.log(playerState)
|
||||
|
||||
if (!playerState) {
|
||||
playOnSpotify(accessToken.value, deviceId.value)
|
||||
return
|
||||
}
|
||||
await spotify.player.resume()
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error)
|
||||
}
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
if (ready.value) {
|
||||
pauseOnSpotify(accessToken.value, deviceId.value)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ready,
|
||||
play,
|
||||
pause
|
||||
}
|
||||
}
|
||||
|
||||
export const useSpotifyConnect = (hash: string) => {
|
||||
window.onSpotifyWebPlaybackSDKReady = () => {}
|
||||
const { setAccessToken, setTokenExpire } = useActions<RootActions>([
|
||||
'setAccessToken',
|
||||
'setTokenExpire'
|
||||
])
|
||||
hash = hash.replace('#', '')
|
||||
const connection = [...hash.split('&').values()]
|
||||
.map((entry) => entry.split('='))
|
||||
.reduce((acc, entry) => {
|
||||
acc[entry[0]] = entry[1]
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
setAccessToken(connection['access_token'])
|
||||
const now = new Date()
|
||||
setTokenExpire(now.setTime(now.getTime() + connection['expires_in'] * 1000))
|
||||
}
|
||||
571
src/lib/spotify-player.js
Normal file
571
src/lib/spotify-player.js
Normal file
@@ -0,0 +1,571 @@
|
||||
/* eslint-disable */
|
||||
/* Spotify JS-SDK - v1.7.1-d2b664d */
|
||||
!(function(e) {
|
||||
function t(r) {
|
||||
if (n[r]) return n[r].exports
|
||||
var s = (n[r] = { i: r, l: !1, exports: {} })
|
||||
return e[r].call(s.exports, s, s.exports, t), (s.l = !0), s.exports
|
||||
}
|
||||
var n = {}
|
||||
;(t.m = e),
|
||||
(t.c = n),
|
||||
(t.d = function(e, n, r) {
|
||||
t.o(e, n) ||
|
||||
Object.defineProperty(e, n, {
|
||||
configurable: !1,
|
||||
enumerable: !0,
|
||||
get: r
|
||||
})
|
||||
}),
|
||||
(t.n = function(e) {
|
||||
var n =
|
||||
e && e.__esModule
|
||||
? function() {
|
||||
return e.default
|
||||
}
|
||||
: function() {
|
||||
return e
|
||||
}
|
||||
return t.d(n, 'a', n), n
|
||||
}),
|
||||
(t.o = function(e, t) {
|
||||
return Object.prototype.hasOwnProperty.call(e, t)
|
||||
}),
|
||||
(t.p = ''),
|
||||
t((t.s = 2))
|
||||
})([
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
var r = {
|
||||
SPOTIFY_MESSAGE: 'SP_MESSAGE',
|
||||
ACCOUNT_ERROR: 'ACCOUNT_ERROR',
|
||||
AUTH_ERROR: 'AUTH_ERROR',
|
||||
CONNECT: 'CONNECT',
|
||||
CONNECTED: 'CONNECTED',
|
||||
CURRENT_STATE: 'CURRENT_STATE',
|
||||
DISCONNECT: 'DISCONNECT',
|
||||
EVENT: 'EVENT',
|
||||
GET_CURRENT_STATE: 'GET_CURRENT_STATE',
|
||||
GET_TOKEN: 'GET_TOKEN',
|
||||
GET_VOLUME: 'GET_VOLUME',
|
||||
INIT: 'INIT',
|
||||
LOADED: 'LOADED',
|
||||
NEXT_TRACK: 'NEXT_TRACK',
|
||||
PAUSE: 'PAUSE',
|
||||
PLAYBACK_ERROR: 'PLAYBACK_ERROR',
|
||||
PLAYER_INIT_ERROR: 'PLAYER_INIT_ERROR',
|
||||
PLAYER_READY: 'PLAYER_READY',
|
||||
PLAYER_NOT_READY: 'PLAYER_NOT_READY',
|
||||
PLAYER_STATE_CHANGED: 'PLAYER_STATE_CHANGED',
|
||||
PREV_TRACK: 'PREV_TRACK',
|
||||
RESUME: 'RESUME',
|
||||
SEEK: 'SEEK',
|
||||
SET_NAME: 'SET_NAME',
|
||||
SET_VOLUME: 'SET_VOLUME',
|
||||
TOGGLE_PLAY: 'TOGGLE_PLAY',
|
||||
TOKEN: 'TOKEN',
|
||||
VOLUME: 'VOLUME',
|
||||
accountError: function(e) {
|
||||
return this._createEventMessage(r.ACCOUNT_ERROR, { message: e })
|
||||
},
|
||||
authError: function(e) {
|
||||
return this._createEventMessage(r.AUTH_ERROR, e)
|
||||
},
|
||||
playbackError: function(e) {
|
||||
return this._createEventMessage(r.PLAYBACK_ERROR, e)
|
||||
},
|
||||
playerReady: function(e) {
|
||||
return this._createEventMessage(r.PLAYER_READY, e)
|
||||
},
|
||||
playerNotReady: function(e) {
|
||||
return this._createEventMessage(r.PLAYER_NOT_READY, e)
|
||||
},
|
||||
connect: function() {
|
||||
return this._createMessage(r.CONNECT)
|
||||
},
|
||||
connected: function(e, t) {
|
||||
return this._createMessage(r.CONNECTED, { connected: e, ref: t })
|
||||
},
|
||||
disconnect: function() {
|
||||
return this._createMessage(r.DISCONNECT)
|
||||
},
|
||||
init: function(e) {
|
||||
return this._createMessage(r.INIT, e)
|
||||
},
|
||||
playerInitError: function(e) {
|
||||
return this._createEventMessage(r.PLAYER_INIT_ERROR, e)
|
||||
},
|
||||
getToken: function() {
|
||||
return this._createMessage(r.GET_TOKEN)
|
||||
},
|
||||
token: function(e, t) {
|
||||
return this._createMessage(r.TOKEN, { token: e, ref: t })
|
||||
},
|
||||
pause: function() {
|
||||
return this._createMessage(r.PAUSE)
|
||||
},
|
||||
resume: function() {
|
||||
return this._createMessage(r.RESUME)
|
||||
},
|
||||
togglePlay: function() {
|
||||
return this._createMessage(r.TOGGLE_PLAY)
|
||||
},
|
||||
seek: function(e) {
|
||||
return this._createMessage(r.SEEK, e)
|
||||
},
|
||||
nextTrack: function(e) {
|
||||
return this._createMessage(r.NEXT_TRACK, e)
|
||||
},
|
||||
previousTrack: function(e) {
|
||||
return this._createMessage(r.PREV_TRACK, e)
|
||||
},
|
||||
getCurrentState: function() {
|
||||
return this._createMessage(r.GET_CURRENT_STATE)
|
||||
},
|
||||
currentState: function(e, t) {
|
||||
return this._createMessage(r.CURRENT_STATE, { state: e, ref: t })
|
||||
},
|
||||
playerStateChanged: function(e) {
|
||||
return this._createEventMessage(r.PLAYER_STATE_CHANGED, e)
|
||||
},
|
||||
getVolume: function() {
|
||||
return this._createMessage(r.GET_VOLUME)
|
||||
},
|
||||
volume: function(e, t) {
|
||||
return this._createMessage(r.VOLUME, { volume: e, ref: t })
|
||||
},
|
||||
setVolume: function(e) {
|
||||
return this._createMessage(r.SET_VOLUME, e)
|
||||
},
|
||||
setName: function(e) {
|
||||
return this._createMessage(r.SET_NAME, e)
|
||||
},
|
||||
embeddedLoaded: function() {
|
||||
return this._createMessage(r.LOADED)
|
||||
},
|
||||
_createEventMessage: function(e, t) {
|
||||
return this._createMessage(r.EVENT, { name: e, eventData: t })
|
||||
},
|
||||
_createMessage: function(e, t) {
|
||||
return {
|
||||
type: r.SPOTIFY_MESSAGE,
|
||||
body: { topic: e, data: t ? JSON.parse(JSON.stringify(t)) : null }
|
||||
}
|
||||
}
|
||||
}
|
||||
e.exports = r
|
||||
},
|
||||
function(e, t) {
|
||||
var n
|
||||
n = (function() {
|
||||
return this
|
||||
})()
|
||||
try {
|
||||
n = n || Function('return this')() || (0, eval)('this')
|
||||
} catch (e) {
|
||||
'object' == typeof window && (n = window)
|
||||
}
|
||||
e.exports = n
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
function r() {
|
||||
var e = n(3)
|
||||
if (!document.body) throw new Error("Document doesn't have a body")
|
||||
if (
|
||||
((window.Spotify = { Player: e.setupPlayerEnv(window) }),
|
||||
window.onSpotifyWebPlaybackSDKReady)
|
||||
)
|
||||
window.onSpotifyWebPlaybackSDKReady()
|
||||
else {
|
||||
if (!window.onSpotifyPlayerAPIReady)
|
||||
throw new Error('onSpotifyWebPlaybackSDKReady is not defined')
|
||||
window.onSpotifyPlayerAPIReady()
|
||||
}
|
||||
}
|
||||
'complete' === document.readyState
|
||||
? r()
|
||||
: window.addEventListener('load', r)
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
var r = n(4),
|
||||
s = n(5),
|
||||
o = n(0),
|
||||
i = n(6),
|
||||
a = function(e, t) {
|
||||
var n,
|
||||
a = 'https://sdk.scdn.co/embedded/index.html',
|
||||
u = s.defer(),
|
||||
c = new r(),
|
||||
_ =
|
||||
t ||
|
||||
function(t) {
|
||||
var n = e.document.createElement('iframe')
|
||||
return (
|
||||
(n.src = t),
|
||||
n.setAttribute('alt', 'Audio Playback Container'),
|
||||
n.setAttribute('tabIndex', '-1'),
|
||||
n.style.setProperty('position', 'absolute', 'important'),
|
||||
n.style.setProperty('left', '-1px', 'important'),
|
||||
n.style.setProperty('width', '0', 'important'),
|
||||
n.style.setProperty('height', '0', 'important'),
|
||||
n.style.setProperty('border', 'none', 'important'),
|
||||
n.style.setProperty('outline', 'none', 'important'),
|
||||
(n.allow = 'encrypted-media; autoplay'),
|
||||
e.document.body.appendChild(n),
|
||||
n.contentWindow
|
||||
)
|
||||
},
|
||||
h = function(t) {
|
||||
t === o.LOADED && (c.stopListening(e, h), u.resolve())
|
||||
}
|
||||
c.listen(e, h), (n = _(a))
|
||||
var f = function(t) {
|
||||
;(this._options = this._sanitizeOptions(t)),
|
||||
(this._handleMessages = this._handleMessages.bind(this)),
|
||||
(this._messageHandlers = {}),
|
||||
(this._eventListeners = {}),
|
||||
this._setupMessageHandlers(),
|
||||
(this._connectionRequests = {}),
|
||||
(this._getCurrentStateRequests = {}),
|
||||
(this._getVolumeRequests = {}),
|
||||
(this.isLoaded = u.promise.then(
|
||||
function() {
|
||||
c.listen(e, this._handleMessages),
|
||||
this._sendMessage(o.init(this._options))
|
||||
}.bind(this)
|
||||
))
|
||||
}
|
||||
return (
|
||||
(f.prototype._sanitizeOptions = function(t) {
|
||||
var n = (e && e.location && e.location.hostname) || ''
|
||||
return {
|
||||
name: t.name || n,
|
||||
id: i.get(),
|
||||
getOAuthToken: t.getOAuthToken || t.getOauthToken,
|
||||
volume: t.volume || 1
|
||||
}
|
||||
}),
|
||||
(f.prototype._setupMessageHandlers = function() {
|
||||
;(this._messageHandlers[o.GET_TOKEN] = this._onGetToken.bind(this)),
|
||||
(this._messageHandlers[o.EVENT] = this._onEvent.bind(this)),
|
||||
(this._messageHandlers[o.CONNECTED] = this._onConnected.bind(
|
||||
this
|
||||
)),
|
||||
(this._messageHandlers[
|
||||
o.CURRENT_STATE
|
||||
] = this._onCurrentState.bind(this)),
|
||||
(this._messageHandlers[o.VOLUME] = this._onVolume.bind(this))
|
||||
}),
|
||||
(f.prototype.connect = function() {
|
||||
return this.isLoaded.then(
|
||||
function() {
|
||||
var e = this._sendMessage(o.connect())
|
||||
return (
|
||||
(this._connectionRequests[e] = s.defer()),
|
||||
this._connectionRequests[e].promise
|
||||
)
|
||||
}.bind(this)
|
||||
)
|
||||
}),
|
||||
(f.prototype.on = function(e, t) {
|
||||
if (!e) throw new TypeError('Argument `eventName` is required.')
|
||||
if ('function' != typeof t)
|
||||
throw new TypeError('Argument `listener` must be a function.')
|
||||
return (
|
||||
(this._eventListeners[e] = this._eventListeners[e] || []),
|
||||
-1 === this._eventListeners[e].indexOf(t) &&
|
||||
(this._eventListeners[e].push(t), !0)
|
||||
)
|
||||
}),
|
||||
(f.prototype.addListener = function(e, t) {
|
||||
return this.on(e, t)
|
||||
}),
|
||||
(f.prototype.removeListener = function(e, t) {
|
||||
if (!e) throw new TypeError('Argument `eventName` is required.')
|
||||
if (1 === arguments.length)
|
||||
return (this._eventListeners[e] = []), !0
|
||||
if ('function' != typeof t)
|
||||
throw new TypeError('Argument `listener` must be a function.')
|
||||
var n = this._eventListeners[e]
|
||||
return (
|
||||
!(!n || !n.length) &&
|
||||
((this._eventListeners[e] = n.filter(function(e) {
|
||||
return e !== t
|
||||
})),
|
||||
!0)
|
||||
)
|
||||
}),
|
||||
(f.prototype.getCurrentState = function() {
|
||||
return this.isLoaded.then(
|
||||
function() {
|
||||
var e = this._sendMessage(o.getCurrentState())
|
||||
return (
|
||||
(this._getCurrentStateRequests[e] = s.defer()),
|
||||
this._getCurrentStateRequests[e].promise
|
||||
)
|
||||
}.bind(this)
|
||||
)
|
||||
}),
|
||||
(f.prototype.getVolume = function() {
|
||||
return this.isLoaded.then(
|
||||
function() {
|
||||
var e = this._sendMessage(o.getVolume())
|
||||
return (
|
||||
(this._getVolumeRequests[e] = s.defer()),
|
||||
this._getVolumeRequests[e].promise
|
||||
)
|
||||
}.bind(this)
|
||||
)
|
||||
}),
|
||||
(f.prototype.setName = function(e) {
|
||||
return this._sendMessageWhenLoaded(o.setName(e))
|
||||
}),
|
||||
(f.prototype.setVolume = function(e) {
|
||||
return this._sendMessageWhenLoaded(o.setVolume(e))
|
||||
}),
|
||||
(f.prototype.pause = function() {
|
||||
return this._sendMessageWhenLoaded(o.pause())
|
||||
}),
|
||||
(f.prototype.resume = function() {
|
||||
return this._sendMessageWhenLoaded(o.resume())
|
||||
}),
|
||||
(f.prototype.togglePlay = function() {
|
||||
return this._sendMessageWhenLoaded(o.togglePlay())
|
||||
}),
|
||||
(f.prototype.seek = function(e) {
|
||||
return this._sendMessageWhenLoaded(o.seek(e))
|
||||
}),
|
||||
(f.prototype.previousTrack = function(e) {
|
||||
return this._sendMessageWhenLoaded(o.previousTrack(e))
|
||||
}),
|
||||
(f.prototype.nextTrack = function(e) {
|
||||
return this._sendMessageWhenLoaded(o.nextTrack(e))
|
||||
}),
|
||||
(f.prototype.disconnect = function() {
|
||||
return this._sendMessageWhenLoaded(o.disconnect())
|
||||
}),
|
||||
(f.prototype._getListeners = (function() {
|
||||
var e = {}
|
||||
return (
|
||||
(e[o.ACCOUNT_ERROR] = 'account_error'),
|
||||
(e[o.AUTH_ERROR] = 'authentication_error'),
|
||||
(e[o.PLAYBACK_ERROR] = 'playback_error'),
|
||||
(e[o.PLAYER_INIT_ERROR] = 'initialization_error'),
|
||||
(e[o.PLAYER_READY] = 'ready'),
|
||||
(e[o.PLAYER_NOT_READY] = 'not_ready'),
|
||||
(e[o.PLAYER_STATE_CHANGED] = 'player_state_changed'),
|
||||
function(t) {
|
||||
return this._eventListeners[e[t]] || []
|
||||
}
|
||||
)
|
||||
})()),
|
||||
(f.prototype._onEvent = function(e) {
|
||||
this._getListeners(e.name).forEach(function(t) {
|
||||
t(e.eventData)
|
||||
})
|
||||
}),
|
||||
(f.prototype._onGetToken = function(e, t) {
|
||||
var n = this._options.getOAuthToken
|
||||
if ('function' != typeof n) {
|
||||
var r = 'getOAuthToken is not a function'
|
||||
if (this._getListeners(o.PLAYER_INIT_ERROR).length)
|
||||
return void this._onEvent({
|
||||
name: o.PLAYER_INIT_ERROR,
|
||||
eventData: { message: r }
|
||||
})
|
||||
throw new Error(r)
|
||||
}
|
||||
new s(n).then(
|
||||
function(e) {
|
||||
this._sendMessage(o.token(e, t))
|
||||
}.bind(this)
|
||||
)
|
||||
}),
|
||||
(f.prototype._onConnected = function(e) {
|
||||
e.ref in this._connectionRequests &&
|
||||
(this._connectionRequests[e.ref].resolve(e.connected),
|
||||
delete this._connectionRequests[e.ref])
|
||||
}),
|
||||
(f.prototype._onCurrentState = function(e) {
|
||||
e.ref in this._getCurrentStateRequests &&
|
||||
(this._getCurrentStateRequests[e.ref].resolve(e.state),
|
||||
delete this._getCurrentStateRequests[e.ref])
|
||||
}),
|
||||
(f.prototype._onVolume = function(e) {
|
||||
e.ref in this._getVolumeRequests &&
|
||||
(this._getVolumeRequests[e.ref].resolve(e.volume),
|
||||
delete this._getVolumeRequests[e.ref])
|
||||
}),
|
||||
(f.prototype._handleMessages = function(e, t, n) {
|
||||
e in this._messageHandlers && this._messageHandlers[e](t, n)
|
||||
}),
|
||||
(f.prototype._sendMessage = function(e) {
|
||||
return c.send(n, e, a)
|
||||
}),
|
||||
(f.prototype._sendMessageWhenLoaded = function(e) {
|
||||
return this.isLoaded.then(this._sendMessage.bind(this, e))
|
||||
}),
|
||||
f
|
||||
)
|
||||
}
|
||||
e.exports.setupPlayerEnv = a
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
var r = n(0),
|
||||
s = function() {
|
||||
;(this._onMessageCallback = function() {}),
|
||||
(this._receiveMessage = this._receiveMessage.bind(this)),
|
||||
(this._messageId = 0)
|
||||
}
|
||||
;(s.prototype.listen = function(e, t) {
|
||||
;(this._onMessageCallback = t),
|
||||
e.addEventListener('message', this._receiveMessage)
|
||||
}),
|
||||
(s.prototype.stopListening = function(e) {
|
||||
e.removeEventListener('message', this._receiveMessage)
|
||||
}),
|
||||
(s.prototype.send = function(e, t, n) {
|
||||
return e.postMessage(this._addMessageId(t), n || '*'), t.seq
|
||||
}),
|
||||
(s.prototype._addMessageId = function(e) {
|
||||
return (e.seq = this._messageId++), e
|
||||
}),
|
||||
(s.prototype._receiveMessage = function(e) {
|
||||
var t = e.data
|
||||
t.type === r.SPOTIFY_MESSAGE &&
|
||||
t.body &&
|
||||
t.body.topic &&
|
||||
this._onMessageCallback(t.body.topic, t.body.data, t.seq)
|
||||
}),
|
||||
(e.exports = s)
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
;(function(t) {
|
||||
var n = t.Promise
|
||||
if (!n) throw new Error('Fatal: no Promise implementation available.')
|
||||
var r
|
||||
;(r = Object.defineProperty
|
||||
? function(e, t, n) {
|
||||
return e[t]
|
||||
? e
|
||||
: (Object.defineProperty(e, t, {
|
||||
value: n,
|
||||
configurable: !0,
|
||||
writable: !0
|
||||
}),
|
||||
e)
|
||||
}
|
||||
: function(e, t, n) {
|
||||
return e[t] ? e : ((e[t] = n), e)
|
||||
}),
|
||||
r(n, 'defer', function() {
|
||||
var e = {}
|
||||
return (
|
||||
(e.promise = new n(function(t, n) {
|
||||
;(e.resolve = t), (e.reject = n)
|
||||
})),
|
||||
e
|
||||
)
|
||||
}),
|
||||
r(n.prototype, 'spread', function(e, t) {
|
||||
return this.then(function(e) {
|
||||
return n.all(e)
|
||||
}).then(function(t) {
|
||||
return 1 === t.length ? e(t[0]) : e.apply(this, t)
|
||||
}, t)
|
||||
}),
|
||||
(e.exports = n)
|
||||
}.call(t, n(1)))
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
;(function(t) {
|
||||
function r() {
|
||||
var e = new t.Uint8Array(16)
|
||||
return t.crypto.getRandomValues(e), i.toHex(e.join(''), 40).slice(0, 40)
|
||||
}
|
||||
function s() {
|
||||
for (var e = new Array(27), t = e.length; t--; )
|
||||
e[t] = Math.floor(8 * Math.random())
|
||||
return i.toHex(e.join(''), 40)
|
||||
}
|
||||
function o(e, n) {
|
||||
if (e && 'string' == typeof e) return e
|
||||
if (n) return u()
|
||||
var r = t.localStorage.getItem(c)
|
||||
return r || ((r = u()), t.localStorage.setItem(c, r)), r
|
||||
}
|
||||
var i = n(7),
|
||||
a =
|
||||
'function' == typeof t.Uint8Array &&
|
||||
void 0 !== t.crypto &&
|
||||
'function' == typeof t.crypto.getRandomValues,
|
||||
u = a ? r : s,
|
||||
c = '_spharmony_device_id'
|
||||
e.exports = {
|
||||
get: o,
|
||||
generate: u,
|
||||
generateWithCrypto: r,
|
||||
generateWithRandom: s
|
||||
}
|
||||
}.call(t, n(1)))
|
||||
},
|
||||
function(e, t, n) {
|
||||
'use strict'
|
||||
function r(e, t, n) {
|
||||
for (var r = 0, s = 0; s < e.length; ++s) {
|
||||
var o = e[s] * t + r
|
||||
;(e[s] = o % n), (r = ~~(o / n))
|
||||
}
|
||||
for (; r; ) e.push(r % n), (r = ~~(r / n))
|
||||
}
|
||||
function s(e, t, n, r) {
|
||||
var s,
|
||||
o,
|
||||
i = 0
|
||||
for (o = 0; o < t.length; ++o)
|
||||
(s = ~~e[o] + t[o] * n + i), (e[o] = s % r), (i = ~~(s / r))
|
||||
for (; i; ) (s = ~~e[o] + i), (e[o] = s % r), (i = ~~(s / r)), ++o
|
||||
}
|
||||
function o(e, t, n) {
|
||||
for (var o = [0], i = [1], a = 0; a < e.length; ++a)
|
||||
s(o, i, e[a], n), r(i, t, n)
|
||||
return o
|
||||
}
|
||||
function i(e, t) {
|
||||
for (var n = [], r = 0; r < e.length; ++r) n.push(t[e[r]])
|
||||
return n.reverse()
|
||||
}
|
||||
function a(e, t) {
|
||||
for (; e.length < t; ) e.push(0)
|
||||
return e
|
||||
}
|
||||
var u = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
c = {},
|
||||
_ = {}
|
||||
!(function() {
|
||||
var e, t
|
||||
for (e = 0, t = u.length; e < t; ++e) _[u[e]] = e
|
||||
for (e = 0; e < 16; ++e) c['0123456789abcdef'[e]] = e
|
||||
for (e = 0; e < 16; ++e) c['0123456789ABCDEF'[e]] = e
|
||||
})(),
|
||||
(e.exports = {
|
||||
fromBytes: function(e, t) {
|
||||
return i(a(o(e.slice(0).reverse(), 256, 62), t), u).join('')
|
||||
},
|
||||
toBytes: function(e, t) {
|
||||
return a(o(i(e, _), 62, 256), t).reverse()
|
||||
},
|
||||
toHex: function(e, t) {
|
||||
return i(a(o(i(e, _), 62, 16), t), u).join('')
|
||||
},
|
||||
fromHex: function(e, t) {
|
||||
return i(a(o(i(e, c), 16, 62), t), u).join('')
|
||||
}
|
||||
})
|
||||
}
|
||||
])
|
||||
@@ -8,5 +8,8 @@
|
||||
"title": "Change!",
|
||||
"body": "{dev}, your turn"
|
||||
}
|
||||
},
|
||||
"music": {
|
||||
"loading": "loading..."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
"title": "Changement !",
|
||||
"body": "À votre tour {dev}."
|
||||
}
|
||||
},
|
||||
"music": {
|
||||
"loading": "loading..."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ const routes: RouteConfig[] = [
|
||||
path: '/Privacy',
|
||||
name: 'Privacy',
|
||||
component: () => import('@/views/Privacy.vue')
|
||||
},
|
||||
{
|
||||
path: '/spotify',
|
||||
name: 'Spotify',
|
||||
component: () => import('@/views/SpotifyCallback.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@ Vue.use(Vuex)
|
||||
export interface State {
|
||||
interval: number
|
||||
hasMusic: boolean
|
||||
hasSpotify: boolean
|
||||
dev1: string | null
|
||||
dev2: string | null
|
||||
accessToken: string | null
|
||||
tokenExpire: Date | null
|
||||
}
|
||||
|
||||
export interface RootActions extends ActionTree<State, State> {
|
||||
@@ -16,6 +19,8 @@ export interface RootActions extends ActionTree<State, State> {
|
||||
setHasMusic: (ctx: ActionContext<State, State>, payload: boolean) => void
|
||||
setDev1: (ctx: ActionContext<State, State>, payload: string) => void
|
||||
setDev2: (ctx: ActionContext<State, State>, payload: string) => void
|
||||
setAccessToken: (ctx: ActionContext<State, State>, payload: string) => void
|
||||
setTokenExpire: (ctx: ActionContext<State, State>, payload: Date) => void
|
||||
}
|
||||
|
||||
const vuexLocal = new VuexPersistence<State>({
|
||||
@@ -25,21 +30,35 @@ const vuexLocal = new VuexPersistence<State>({
|
||||
|
||||
const SET_INTERVAL = 'SET_INTERVAL'
|
||||
const WITH_MUSIC = 'WITH_MUSIC'
|
||||
const WITH_SPOTIFY = 'WITH_SPOTIFY'
|
||||
const SET_DEV_1 = 'SET_DEV_1'
|
||||
const SET_DEV_2 = 'SET_DEV_2'
|
||||
const SET_ACCESS_TOKEN = 'SET_ACCESS_TOKEN'
|
||||
const SET_TOKEN_EXPIRE = 'SET_TOKEN_EXPIRE'
|
||||
|
||||
const store = new Vuex.Store<State>({
|
||||
state: {
|
||||
interval: 5,
|
||||
hasMusic: false,
|
||||
hasSpotify: false,
|
||||
dev1: null,
|
||||
dev2: null
|
||||
dev2: null,
|
||||
accessToken: null,
|
||||
tokenExpire: null
|
||||
},
|
||||
getters: {
|
||||
interval: ({ interval }) => interval,
|
||||
hasMusic: ({ hasMusic }) => hasMusic,
|
||||
hasSpotify: ({ hasSpotify }) => hasSpotify,
|
||||
dev1: ({ dev1 }) => dev1,
|
||||
dev2: ({ dev2 }) => dev2
|
||||
dev2: ({ dev2 }) => dev2,
|
||||
accessToken: ({ accessToken }) => accessToken,
|
||||
hasValidAccessToken: ({ accessToken, tokenExpire }) => {
|
||||
if (!accessToken || !tokenExpire) {
|
||||
return false
|
||||
}
|
||||
return new Date() < tokenExpire
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
[SET_INTERVAL](state, interval: number) {
|
||||
@@ -48,11 +67,20 @@ const store = new Vuex.Store<State>({
|
||||
[WITH_MUSIC](state, hasMusic: boolean) {
|
||||
state.hasMusic = hasMusic
|
||||
},
|
||||
[WITH_SPOTIFY](state, hasSpotify: boolean) {
|
||||
state.hasSpotify = hasSpotify
|
||||
},
|
||||
[SET_DEV_1](state, dev1: string) {
|
||||
state.dev1 = dev1
|
||||
},
|
||||
[SET_DEV_2](state, dev2: string) {
|
||||
state.dev2 = dev2
|
||||
},
|
||||
[SET_ACCESS_TOKEN](state, accessToken: string) {
|
||||
state.accessToken = accessToken
|
||||
},
|
||||
[SET_TOKEN_EXPIRE](state, tokenExpire: Date) {
|
||||
state.tokenExpire = tokenExpire
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -64,11 +92,20 @@ const store = new Vuex.Store<State>({
|
||||
setHasMusic({ commit }, hasMusic: boolean) {
|
||||
commit(WITH_MUSIC, hasMusic)
|
||||
},
|
||||
setHasSpotify({ commit }, hasSpotify: boolean) {
|
||||
commit(WITH_SPOTIFY, hasSpotify)
|
||||
},
|
||||
setDev1({ commit }, dev1: string) {
|
||||
commit(SET_DEV_1, dev1)
|
||||
},
|
||||
setDev2({ commit }, dev2: string) {
|
||||
commit(SET_DEV_2, dev2)
|
||||
},
|
||||
setAccessToken({ commit }, accessToken: string) {
|
||||
commit(SET_ACCESS_TOKEN, accessToken)
|
||||
},
|
||||
setTokenExpire({ commit }, tokenExpire: Date) {
|
||||
commit(SET_TOKEN_EXPIRE, tokenExpire)
|
||||
}
|
||||
},
|
||||
plugins: [vuexLocal.plugin]
|
||||
|
||||
@@ -60,3 +60,16 @@ a {
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
img.loader {
|
||||
animation: rotating 4s linear infinite;
|
||||
}
|
||||
|
||||
16
src/views/SpotifyCallback.vue
Normal file
16
src/views/SpotifyCallback.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="spotify-callback"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api'
|
||||
import { useSpotifyConnect } from '../hooks/useSpotify'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SpotifyCallback',
|
||||
setup(_, { root }) {
|
||||
useSpotifyConnect(root.$router.currentRoute.hash)
|
||||
root.$router.replace({ name: 'Home' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user