support extended tidal funcionality & favourite tracks

This commit is contained in:
SrGooglo 2023-08-02 20:40:28 +00:00
parent cf2d4872b6
commit f0227354c6
8 changed files with 133 additions and 51 deletions

View File

@ -9,7 +9,6 @@ import { Icons, createIconRender } from "components/Icons"
import { WithPlayerContext } from "contexts/WithPlayerContext" import { WithPlayerContext } from "contexts/WithPlayerContext"
import FeedModel from "models/feed" import FeedModel from "models/feed"
import PlaylistModel from "models/playlists"
import MusicModel from "models/music" import MusicModel from "models/music"
import SyncModel from "models/sync" import SyncModel from "models/sync"
@ -19,32 +18,13 @@ import PlaylistItem from "components/Music/PlaylistItem"
import "./index.less" import "./index.less"
const MusicNavbar = (props) => { const MusicNavbar = (props) => {
const [loading, setLoading] = React.useState(true)
const [hasTidal, setHasTidal] = React.useState(false)
React.useEffect(() => {
SyncModel.hasServiceLinked("tidal")
.catch(() => {
setHasTidal(false)
setLoading(false)
})
.then((value) => {
setHasTidal(value.active)
setLoading(false)
})
}, [])
if (loading) {
return null
}
return <div className="music_navbar"> return <div className="music_navbar">
<Searcher <Searcher
useUrlQuery useUrlQuery
renderResults={false} renderResults={false}
model={MusicModel.search} model={MusicModel.search}
modelParams={{ modelParams={{
useTidal: hasTidal, useTidal: app.cores.sync.getActiveLinkedServices().tidal,
}} }}
onSearchResult={props.setSearchResults} onSearchResult={props.setSearchResults}
onEmpty={() => props.setSearchResults(false)} onEmpty={() => props.setSearchResults(false)}

View File

@ -3,10 +3,12 @@ import * as antd from "antd"
import PlaylistView from "components/Music/PlaylistView" import PlaylistView from "components/Music/PlaylistView"
import MusicModel from "comty.js/models/music" import MusicModel from "models/music"
export default () => { export default () => {
const [L_Favorites, R_Favorites, E_Favorites] = app.cores.api.useRequest(MusicModel.getFavorites) const [L_Favorites, R_Favorites, E_Favorites] = app.cores.api.useRequest(MusicModel.getFavorites, {
useTidal: app.cores.sync.getActiveLinkedServices().tidal
})
if (E_Favorites) { if (E_Favorites) {
return <antd.Result return <antd.Result

View File

@ -1,4 +1,3 @@
import { Track } from "@shared-classes/DbModels" import { Track } from "@shared-classes/DbModels"
import getEnhancedLyricsFromTrack from "@services/getEnhancedLyricsFromTrack" import getEnhancedLyricsFromTrack from "@services/getEnhancedLyricsFromTrack"

View File

@ -52,27 +52,7 @@ async function searchRoute(req, res) {
query: keywords query: keywords
}) })
tidalResult.tracks.items.forEach((element) => { results.tracks = [...results.tracks, ...tidalResult]
element._id = element.id
const coverUID = element.album.cover.replace(/-/g, "/")
element.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
element.artist = element.artists.map(artist => artist.name).join(", ")
element.metadata = {
title: element.title,
artists: element.artists.map(artist => artist.name).join(", "),
artist: element.artists.map(artist => artist.name).join(", "),
album: element.album.title,
duration: element.duration
}
element.service = "tidal"
results.tracks.push(element)
})
} }
return res.json(results) return res.json(results)

View File

@ -6,17 +6,17 @@ export default async (req, res) => {
return new AuthorizationError(req, res) return new AuthorizationError(req, res)
} }
let likedIds = await TrackLike.find({ let likedTracks = await TrackLike.find({
user_id: req.session.user_id, user_id: req.session.user_id,
}) })
.sort({ created_at: -1 }) .sort({ created_at: -1 })
likedIds = likedIds.map((item) => { const likedTracksIds = likedTracks.map((item) => {
return item.track_id return item.track_id
}) })
let tracks = await Track.find({ let tracks = await Track.find({
_id: [...likedIds], _id: likedTracksIds,
//public: true, //public: true,
}) })
.catch((err) => { .catch((err) => {
@ -26,14 +26,20 @@ export default async (req, res) => {
tracks = tracks.map((item) => { tracks = tracks.map((item) => {
item = item.toObject() item = item.toObject()
const likeIndex = likedTracksIds.indexOf(item._id.toString())
if (likeIndex !== -1) {
item.liked_at = new Date(likedTracks[likeIndex].created_at).getTime()
}
item.liked = true item.liked = true
return item return item
}) })
tracks.sort((a, b) => { tracks.sort((a, b) => {
const indexA = likedIds.indexOf(a._id.toString()) const indexA = likedTracksIds.indexOf(a._id.toString())
const indexB = likedIds.indexOf(b._id.toString()) const indexB = likedTracksIds.indexOf(b._id.toString())
return indexA - indexB return indexA - indexB
}) })

View File

@ -0,0 +1,15 @@
import SecureSyncEntry from "@shared-classes/SecureSyncEntry"
import { AuthorizationError } from "@shared-classes/Errors"
export default async (req, res) => {
if (!req.session) {
return new AuthorizationError(req, res)
}
const tidal_access = await SecureSyncEntry.has(req.session.user_id.toString(), "tidal_access_token")
return res.json({
spotify: null,
tidal: tidal_access,
})
}

View File

@ -0,0 +1,32 @@
import SecureSyncEntry from "@shared-classes/SecureSyncEntry"
import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors"
import TidalAPI from "@shared-classes/TidalAPI"
export default async (req, res) => {
if (!req.session) {
return new AuthorizationError(req, res)
}
try {
const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token")
if (!access_token) {
return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.")
}
let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user")
user_data = JSON.parse(user_data)
const response = await TidalAPI.getFavoriteTracks({
user_id: user_data.id,
country: user_data.countryCode,
access_token: access_token,
})
return res.json(response)
} catch (error) {
return new InternalServerError(req, res, error)
}
}

View File

@ -1,4 +1,5 @@
import axios from "axios" import axios from "axios"
import { DateTime } from "luxon"
const TIDAL_CLIENT_ID = process.env.TIDAL_CLIENT_ID const TIDAL_CLIENT_ID = process.env.TIDAL_CLIENT_ID
const TIDAL_CLIENT_SECRET = process.env.TIDAL_CLIENT_SECRET const TIDAL_CLIENT_SECRET = process.env.TIDAL_CLIENT_SECRET
@ -169,6 +170,73 @@ export default class TidalAPI {
} }
}) })
return response.data return response.data.tracks.items.map((item) => {
item._id = item.id
const coverUID = item.album.cover.replace(/-/g, "/")
item.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
item.artist = item.artists.map(artist => artist.name).join(", ")
item.metadata = {
title: item.title,
artists: item.artists.map(artist => artist.name).join(", "),
artist: item.artists.map(artist => artist.name).join(", "),
album: item.album.title,
duration: item.duration
}
item.service = "tidal"
return item
})
}
static async getFavoriteTracks({
user_id,
country,
access_token,
}) {
const url = `https://api.tidal.com/v1/users/${user_id}/favorites/tracks?countryCode=${country}`
const response = await axios({
method: "GET",
url,
headers: {
"Origin": "http://listen.tidal.com",
Authorization: `Bearer ${access_token}`
},
params: {
order: "DATE",
orderDirection: "DESC"
}
})
return response.data.items.map((item) => {
// get js time
item.item.liked_at = new Date(item.created).getTime()
item.item.service = "tidal"
item.item._id = item.item.id
const coverUID = item.item.album.cover.replace(/-/g, "/")
item.item.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
item.item.artist = item.item.artists.map(artist => artist.name).join(", ")
item.item.metadata = {
title: item.item.title,
artists: item.item.artists.map(artist => artist.name).join(", "),
artist: item.item.artists.map(artist => artist.name).join(", "),
album: item.item.album.title,
duration: item.item.duration
}
item.item.liked = true
item.item._computed = true
return item.item
})
} }
} }