[web] Switch to Spotify Web SDK

This commit is contained in:
Alain Nussbaumer 2025-08-17 12:28:32 +10:00
parent b612e12aca
commit 978a9b6a96
12 changed files with 397 additions and 424 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
"dependencies": {
"@aacassandra/vue3-progressbar": "^1.0.3",
"@mdi/js": "^7.4.47",
"@spotify/web-api-ts-sdk": "^1.2.0",
"@ts-pro/vue-eternal-loading": "^1.3.1",
"axios": "^1.11.0",
"bulma": "^1.0.4",
@ -21,7 +22,6 @@
"mdi-vue": "^3.0.13",
"pinia": "^3.0.3",
"reconnectingwebsocket": "^1.0.0",
"spotify-web-api-js": "^1.5.2",
"vue": "^3.5.18",
"vue-i18n": "^11.1.11",
"vue-router": "^4.5.1",
@ -31,12 +31,12 @@
},
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^6.0.8",
"@vitejs/plugin-vue": "^6.0.0",
"eslint": "^9.32.0",
"@vitejs/plugin-vue": "^6.0.1",
"eslint": "^9.33.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^10.3.0",
"eslint-plugin-vue": "^10.4.0",
"prettier": "^3.6.2",
"sass": "^1.89.2",
"vite": "^7.0.6"
"sass": "^1.90.0",
"vite": "^7.1.2"
}
}

View File

@ -1,3 +1,4 @@
import { SpotifyApi } from '@spotify/web-api-ts-sdk'
import api from '@/api'
export default {
@ -14,6 +15,11 @@ export default {
return api.get('./api/spotify-logout')
},
spotify() {
return api.get('./api/spotify')
return api.get('./api/spotify').then((configuration) => {
const sdk = SpotifyApi.withAccessToken(configuration.webapi_client_id, {
access_token: configuration.webapi_token
})
return { api: sdk, configuration }
})
}
}

View File

@ -14,9 +14,9 @@
<script>
import ListProperties from '@/components/ListProperties.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import player from '@/api/player'
import queue from '@/api/queue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
export default {
@ -90,16 +90,17 @@ export default {
watch: {
item() {
if (this.item?.data_kind === 'spotify') {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(this.servicesStore.spotify.webapi_token)
spotifyApi
.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1))
.then((response) => {
return services.spotify().then(({ api }) => {
const trackId = this.item.path.slice(
this.item.path.lastIndexOf(':') + 1
)
return api.tracks.get(trackId).then((response) => {
this.spotifyTrack = response
})
} else {
this.spotifyTrack = {}
})
}
this.spotifyTrack = {}
return {}
}
},
methods: {

View File

@ -28,7 +28,6 @@ import ControlImage from '@/components/ControlImage.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
import PaneHero from '@/components/PaneHero.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
@ -43,13 +42,9 @@ export default {
PaneHero
},
beforeRouteEnter(to, from, next) {
const spotifyApi = new SpotifyWebApi()
services.spotify().then((data) => {
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
.getAlbum(to.params.id, {
market: useServicesStore().spotify.webapi_country
})
services.spotify().then(({ api, configuration }) => {
api.albums
.get(to.params.id, configuration.webapi_country)
.then((album) => {
next((vm) => {
vm.album = album

View File

@ -28,10 +28,8 @@ import ControlButton from '@/components/ControlButton.vue'
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
import PaneTitle from '@/components/PaneTitle.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
const PAGE_SIZE = 50
@ -45,17 +43,16 @@ export default {
PaneTitle
},
beforeRouteEnter(to, from, next) {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
services.spotify().then(({ api, configuration }) => {
Promise.all([
spotifyApi.getArtist(to.params.id),
spotifyApi.getArtistAlbums(to.params.id, {
include_groups: 'album,single',
limit: PAGE_SIZE,
market: useServicesStore().spotify.webapi_country,
offset: 0
})
api.artists.get(to.params.id),
api.artists.albums(
to.params.id,
'album,single',
configuration.webapi_country,
PAGE_SIZE,
0
)
]).then(([artist, albums]) => {
next((vm) => {
vm.artist = artist
@ -66,9 +63,6 @@ export default {
})
})
},
setup() {
return { servicesStore: useServicesStore() }
},
data() {
return {
albums: [],
@ -93,15 +87,15 @@ export default {
this.offset += data.limit
},
load({ loaded }) {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
.getArtistAlbums(this.artist.id, {
include_groups: 'album,single',
limit: PAGE_SIZE,
offset: this.offset
})
services.spotify().then(({ api, configuration }) => {
api.artists
.albums(
this.artist.id,
'album,single',
configuration.webapi_country,
PAGE_SIZE,
this.offset
)
.then((albums) => {
this.appendAlbums(albums)
loaded(albums.items.length, PAGE_SIZE)

View File

@ -41,7 +41,6 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import PaneTitle from '@/components/PaneTitle.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import services from '@/api/services'
@ -55,18 +54,15 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
services.spotify().then(({ api, configuration }) => {
Promise.all([
spotifyApi.getNewReleases({
country: data.webapi_country,
limit: 3
}),
spotifyApi.getFeaturedPlaylists({
country: data.webapi_country,
limit: 3
})
api.browse.getNewReleases(configuration.webapi_country, 3),
api.browse.getFeaturedPlaylists(
configuration.webapi_country,
null,
null,
3
)
]).then((response) => {
next((vm) => {
vm.albums = response[0].albums.items

View File

@ -14,7 +14,6 @@
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import PaneTitle from '@/components/PaneTitle.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import services from '@/api/services'
@ -27,14 +26,9 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
.getFeaturedPlaylists({
country: data.webapi_country,
limit: 50
})
services.spotify().then(({ api, configuration }) => {
api.browse
.getFeaturedPlaylists(configuration.webapi_country, null, null, 50)
.then((response) => {
next((vm) => {
vm.playlists = response.playlists.items

View File

@ -14,7 +14,6 @@
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import PaneTitle from '@/components/PaneTitle.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import services from '@/api/services'
@ -27,14 +26,9 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
.getNewReleases({
country: data.webapi_country,
limit: 50
})
services.spotify().then(({ api, configuration }) => {
api.browse
.getNewReleases(configuration.webapi_country, 50)
.then((response) => {
next((vm) => {
vm.albums = response.albums.items

View File

@ -37,9 +37,8 @@ import ControlButton from '@/components/ControlButton.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
import PaneTitle from '@/components/PaneTitle.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import { useServicesStore } from '@/stores/services'
import services from '@/api/services'
const PAGE_SIZE = 50
@ -53,28 +52,27 @@ export default {
PaneTitle
},
beforeRouteEnter(to, from, next) {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(useServicesStore().spotify.webapi_token)
Promise.all([
spotifyApi.getPlaylist(to.params.id),
spotifyApi.getPlaylistTracks(to.params.id, {
limit: PAGE_SIZE,
market: useServicesStore().$state.spotify.webapi_country,
offset: 0
})
]).then(([playlist, tracks]) => {
next((vm) => {
vm.playlist = playlist
vm.tracks = []
vm.total = 0
vm.offset = 0
vm.appendTracks(tracks)
services.spotify().then(({ api, configuration }) => {
Promise.all([
api.playlists.getPlaylist(to.params.id),
api.playlists.getPlaylistItems(
to.params.id,
configuration.webapi_country,
null,
PAGE_SIZE,
0
)
]).then(([playlist, tracks]) => {
next((vm) => {
vm.playlist = playlist
vm.tracks = []
vm.total = 0
vm.offset = 0
vm.appendTracks(tracks)
})
})
})
},
setup() {
return { servicesStore: useServicesStore() }
},
data() {
return {
offset: 0,
@ -118,18 +116,20 @@ export default {
this.offset += data.limit
},
load({ loaded }) {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(this.servicesStore.spotify.webapi_token)
spotifyApi
.getPlaylistTracks(this.playlist.id, {
limit: PAGE_SIZE,
market: this.servicesStore.spotify.webapi_country,
offset: this.offset
})
.then((data) => {
this.appendTracks(data)
loaded(data.items.length, PAGE_SIZE)
})
services.spotify().then(({ api, configuration }) => {
api.playlists
.getPlaylistItems(
this.playlist.id,
configuration.webapi_country,
null,
PAGE_SIZE,
this.offset
)
.then((data) => {
this.appendTracks(data)
loaded(data.items.length, PAGE_SIZE)
})
})
},
play() {
this.showDetailsModal = false

View File

@ -20,7 +20,6 @@ import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import ListArtistsSpotify from '@/components/ListArtistsSpotify.vue'
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import services from '@/api/services'
import { useSearchStore } from '@/stores/search'
@ -95,16 +94,17 @@ export default {
}
},
searchItems() {
return services.spotify().then((data) => {
this.parameters.market = data.webapi_country
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
return spotifyApi.search(
this.searchStore.query,
this.types,
this.parameters
return services
.spotify()
.then(({ api, configuration }) =>
api.search(
this.searchStore.query,
this.types,
configuration.webapi_country,
this.parameters.limit,
this.parameters.offset
)
)
})
},
searchLibrary() {
this.$router.push({ name: 'search-library' })

View File

@ -7,18 +7,8 @@ export const useServicesStore = defineStore('ServicesStore', {
this.lastfm = await services.lastfm()
},
initialiseSpotify() {
services.spotify().then((data) => {
this.spotify = data
if (this.spotifyTimerId > 0) {
clearTimeout(this.spotifyTimerId)
this.spotifyTimerId = 0
}
if (data.webapi_token_expires_in > 0 && data.webapi_token) {
this.spotifyTimerId = setTimeout(
() => this.initialiseSpotify(),
1000 * data.webapi_token_expires_in
)
}
services.spotify().then(({ configuration }) => {
this.spotify = configuration
})
}
},
@ -41,5 +31,5 @@ export const useServicesStore = defineStore('ServicesStore', {
requiredSpotifyScopes: (state) =>
state.spotify.webapi_required_scope?.split(' ') ?? []
},
state: () => ({ lastfm: {}, spotify: {}, spotifyTimerId: 0 })
state: () => ({ lastfm: {}, spotify: {} })
})