mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
support extended tidal funcionality & favourite tracks
This commit is contained in:
parent
cf2d4872b6
commit
f0227354c6
@ -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)}
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user