Skip to content

Commit

Permalink
fix: now playing like/unlike (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdrani authored May 30, 2024
1 parent 74460f0 commit 7aceed7
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 28 deletions.
3 changes: 2 additions & 1 deletion src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { activeOpenTab, sendMessage } from './utils/messaging.js'
import { getQueueList, setQueueList } from './services/queue.js'
import { createArtistDiscoPlaylist } from './services/artist-disco.js'
import { playSharedTrack, seekTrackToPosition } from './services/player.js'
import { checkIfTracksInCollection, updateLikedTracks } from './services/track.js'
import { checkIfTracksInCollection, updateLikedTracks, getAlbum } from './services/track.js'

let ENABLED = true
let popupPort = null
Expand Down Expand Up @@ -192,6 +192,7 @@ chrome.runtime.onMessage.addListener(({ key, values }, _, sendResponse) => {
'queue.get': getQueueList,
'play.shared': playSharedTrack,
'play.seek': seekTrackToPosition,
'tracks.album': getAlbum,
'tracks.update': updateLikedTracks,
'tracks.liked': checkIfTracksInCollection,
'artist.disco': createArtistDiscoPlaylist
Expand Down
9 changes: 1 addition & 8 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,13 @@ window.addEventListener('message', async (event) => {

const { requestType, payload } = event.data
const messageHandlers = {
'play.seek': sendBackgroundMessage,
'play.shared': sendBackgroundMessage,
'queue.set': sendBackgroundMessage,
'queue.get': sendBackgroundMessage,
'artist.disco': sendBackgroundMessage,
'tracks.liked': sendBackgroundMessage,
'tracks.update': sendBackgroundMessage,
'storage.populate': () => getState(null),
'storage.get': ({ key }) => getState(key),
'storage.delete': ({ key }) => removeState(key),
'storage.set': ({ key, values }) => setState({ key, values })
}

const handlerFn = messageHandlers[requestType]
const handlerFn = messageHandlers?.[requestType] ?? sendBackgroundMessage
if (!handlerFn) return

const response = await handlerFn(payload)
Expand Down
48 changes: 44 additions & 4 deletions src/models/heart-icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,39 @@ export default class HeartIcon {
})
}

async #dispatchGetTrackIdFromAlbum({ songId, albumId }) {
const { state, data } = await this._dispatcher.sendEvent({
eventType: 'tracks.album',
detail: { key: 'tracks.album', values: { albumId } }
})

if (state == 'error') return null

const foundTrack = data.tracks.items.find((track) => {
const artists = track.artists.map((artist) => artist.name).join(',')
const songTitle = `${track.name} by ${artists}`
return songTitle == songId
})

if (!foundTrack) return null

return foundTrack.id
}

async #dispatchLikedTracks() {
const method = (await this.#isHeartIconHighlighted()) ? 'DELETE' : 'PUT'
const { trackId: id } = await currentData.readTrack()
const { id: songId, trackId, type } = await currentData.readTrack()

let likedTrackId = trackId
if (type == 'album') {
likedTrackId = await this.#dispatchGetTrackIdFromAlbum({ songId, albumId: trackId })
}

if (!likedTrackId) return false

await this._dispatcher.sendEvent({
eventType: 'tracks.update',
detail: { key: 'tracks.update', values: { id, method } }
detail: { key: 'tracks.update', values: { id: likedTrackId, method } }
})

return method == 'PUT'
Expand Down Expand Up @@ -104,6 +130,9 @@ export default class HeartIcon {
const trackState = store.checkInCollection(this._id)
if (trackState !== null) return trackState

if (this._type == 'album') {
this._id = await this.#dispatchGetTrackIdFromAlbum(this._id)
}
const response = await this.#dispatchIsInCollection(this._id)

const saved = response?.data?.at(0)
Expand All @@ -113,10 +142,21 @@ export default class HeartIcon {

async highlightIcon(highlight) {
this._id = null
const { trackId } = await currentData.readTrack()
const { trackId, type = 'track', id: songId } = await currentData.readTrack()
this._id = trackId

const shouldUpdate = highlight ?? (await this.#getIsTrackLiked())
let shouldUpdate = false

if (typeof highlight !== 'undefined') {
shouldUpdate = highlight
} else {
if (type == 'album') {
this._id = await this.#dispatchGetTrackIdFromAlbum({ songId, albumId: trackId })
}
if (!this._id) return

shouldUpdate = await this.#getIsTrackLiked()
}

highlightIconTimer({
fill: true,
Expand Down
19 changes: 16 additions & 3 deletions src/services/track.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { setOptions, request } from '../utils/request.js'

const API_URL = 'https://api.spotify.com/v1/me/tracks'
const ALBUM_URL = 'https://api.spotify.com/v1/albums'
const TRACK_URL = 'https://api.spotify.com/v1/me/tracks'

export function getAlbum({ albumId }) {
return new Promise(async (resolve, reject) => {
try {
const options = await setOptions({ method: 'GET' })
const response = await request({ url: `${ALBUM_URL}/${albumId}`, options })
resolve(response)
} catch (error) {
reject(error)
}
})
}

// !! method must be either PUT or DELETE !!
export function updateLikedTracks({ id, method }) {
return new Promise(async (resolve, reject) => {
try {
const options = await setOptions({ method })
const response = await request({ url: `${API_URL}?ids=${id}`, options })
const response = await request({ url: `${TRACK_URL}?ids=${id}`, options })
resolve(response)
} catch (error) {
reject(error)
Expand All @@ -20,7 +33,7 @@ export function checkIfTracksInCollection({ ids }) {
return new Promise(async (resolve, reject) => {
try {
const options = await setOptions({ method: 'GET' })
const url = `${API_URL}/contains?ids=${ids}`
const url = `${TRACK_URL}/contains?ids=${ids}`
const response = await request({ url, options })
resolve(response)
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/stores/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class CacheStore {

getValue({ key, value }) {
const result = this.getKey(key)
if (result) return result
if (result && result !== '{}') return result

return this.update({ key, value })
}
Expand Down
16 changes: 5 additions & 11 deletions src/utils/song.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,20 @@ export const currentSongInfo = () => {
.querySelector('[data-testid="now-playing-widget"]')
?.getAttribute('aria-label')
const image = document.querySelector('[data-testid="CoverSlotCollapsed__container"] img')
const anchor = document.querySelector('[data-testid="CoverSlotCollapsed__container"] a')
const contextType = anchor?.getAttribute('data-context-item-type')
const anchor = document.querySelector('[data-testid="now-playing-widget"] a')

// Remove 'Now playing: ' prefix
const id = songLabel?.split('Now playing: ')?.at(1)
const cover = getImage(image?.src)

if (!contextType) return { id, cover }

const params = new URLSearchParams(anchor?.href)
const trackId = params?.get('uri')?.split(`${contextType}:`).at(1)
const [, type, trackId] = new URL(anchor?.href).pathname.split('/')

return {
id,
cover,
...(contextType && {
type: contextType,
trackId,
url: `${location.origin}/${contextType}/${trackId}`
})
type: type ?? 'track',
trackId,
url: anchor.href
}
}

Expand Down

0 comments on commit 7aceed7

Please sign in to comment.