diff --git a/packages/app/src/pages/music/components/explore/index.jsx b/packages/app/src/pages/music/components/explore/index.jsx index 8e3cdaef..56a62e51 100755 --- a/packages/app/src/pages/music/components/explore/index.jsx +++ b/packages/app/src/pages/music/components/explore/index.jsx @@ -9,7 +9,6 @@ import { Icons, createIconRender } from "components/Icons" import { WithPlayerContext } from "contexts/WithPlayerContext" import FeedModel from "models/feed" -import PlaylistModel from "models/playlists" import MusicModel from "models/music" import SyncModel from "models/sync" @@ -19,32 +18,13 @@ import PlaylistItem from "components/Music/PlaylistItem" import "./index.less" 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
props.setSearchResults(false)} diff --git a/packages/app/src/pages/music/components/favorites/index.jsx b/packages/app/src/pages/music/components/favorites/index.jsx index e89e8ce0..73eb6e0f 100644 --- a/packages/app/src/pages/music/components/favorites/index.jsx +++ b/packages/app/src/pages/music/components/favorites/index.jsx @@ -3,10 +3,12 @@ import * as antd from "antd" import PlaylistView from "components/Music/PlaylistView" -import MusicModel from "comty.js/models/music" +import MusicModel from "models/music" 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) { return { - 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) - }) + results.tracks = [...results.tracks, ...tidalResult] } return res.json(results) diff --git a/packages/music_server/src/controllers/tracks/routes/get/liked.js b/packages/music_server/src/controllers/tracks/routes/get/liked.js index c851d195..d834f1a9 100644 --- a/packages/music_server/src/controllers/tracks/routes/get/liked.js +++ b/packages/music_server/src/controllers/tracks/routes/get/liked.js @@ -6,17 +6,17 @@ export default async (req, res) => { return new AuthorizationError(req, res) } - let likedIds = await TrackLike.find({ + let likedTracks = await TrackLike.find({ user_id: req.session.user_id, }) .sort({ created_at: -1 }) - likedIds = likedIds.map((item) => { + const likedTracksIds = likedTracks.map((item) => { return item.track_id }) let tracks = await Track.find({ - _id: [...likedIds], + _id: likedTracksIds, //public: true, }) .catch((err) => { @@ -26,14 +26,20 @@ export default async (req, res) => { tracks = tracks.map((item) => { 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 return item }) tracks.sort((a, b) => { - const indexA = likedIds.indexOf(a._id.toString()) - const indexB = likedIds.indexOf(b._id.toString()) + const indexA = likedTracksIds.indexOf(a._id.toString()) + const indexB = likedTracksIds.indexOf(b._id.toString()) return indexA - indexB }) diff --git a/packages/sync_server/src/controllers/main/routes/get/active_services.js b/packages/sync_server/src/controllers/main/routes/get/active_services.js new file mode 100644 index 00000000..786001d9 --- /dev/null +++ b/packages/sync_server/src/controllers/main/routes/get/active_services.js @@ -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, + }) +} \ No newline at end of file diff --git a/packages/sync_server/src/controllers/services/routes/get/tidal/favorites/tracks.js b/packages/sync_server/src/controllers/services/routes/get/tidal/favorites/tracks.js new file mode 100644 index 00000000..2f43b1e5 --- /dev/null +++ b/packages/sync_server/src/controllers/services/routes/get/tidal/favorites/tracks.js @@ -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) + } +} \ No newline at end of file diff --git a/shared/classes/TidalAPI/index.js b/shared/classes/TidalAPI/index.js index de0249a4..34e93480 100644 --- a/shared/classes/TidalAPI/index.js +++ b/shared/classes/TidalAPI/index.js @@ -1,4 +1,5 @@ import axios from "axios" +import { DateTime } from "luxon" const TIDAL_CLIENT_ID = process.env.TIDAL_CLIENT_ID 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 + }) } } \ No newline at end of file