mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Fixed bug of infinite spotify category fetching
This commit is contained in:
parent
67bb01526e
commit
5a0486bc4f
93
src/app.tsx
93
src/app.tsx
@ -4,12 +4,13 @@ import { QIcon, QKeyEvent, QMainWindow, QMainWindowSignals, WidgetEventTypes, Wi
|
|||||||
import nodeguiIcon from "../assets/nodegui.jpg";
|
import nodeguiIcon from "../assets/nodegui.jpg";
|
||||||
import { MemoryRouter } from "react-router";
|
import { MemoryRouter } from "react-router";
|
||||||
import Routes from "./routes";
|
import Routes from "./routes";
|
||||||
import SpotifyWebApi from "spotify-web-api-node";
|
|
||||||
import { LocalStorage } from "node-localstorage";
|
import { LocalStorage } from "node-localstorage";
|
||||||
import authContext from "./context/authContext";
|
import authContext from "./context/authContext";
|
||||||
import playerContext, { CurrentPlaylist, CurrentTrack } from "./context/playerContext";
|
import playerContext, { CurrentPlaylist, CurrentTrack } from "./context/playerContext";
|
||||||
import Player, { audioPlayer } from "./components/Player";
|
import Player, { audioPlayer } from "./components/Player";
|
||||||
import { redirectURI } from "./conf";
|
import express from "express";
|
||||||
|
import open from "open";
|
||||||
|
import spotifyApi from "./initializations/spotifyApi";
|
||||||
|
|
||||||
export enum CredentialKeys {
|
export enum CredentialKeys {
|
||||||
credentials = "credentials",
|
credentials = "credentials",
|
||||||
@ -31,32 +32,30 @@ function RootApp() {
|
|||||||
|
|
||||||
const windowEvents = useEventHandler<QMainWindowSignals>(
|
const windowEvents = useEventHandler<QMainWindowSignals>(
|
||||||
{
|
{
|
||||||
async KeyRelease(nativeEv) {
|
async KeyRelease(nativeEv) {
|
||||||
try {
|
try {
|
||||||
|
if (nativeEv) {
|
||||||
if (nativeEv) {
|
const event = new QKeyEvent(nativeEv);
|
||||||
const event = new QKeyEvent(nativeEv);
|
const eventKey = event.key();
|
||||||
const eventKey = event.key();
|
console.log("eventKey:", eventKey);
|
||||||
console.log('eventKey:', eventKey)
|
if (audioPlayer.isRunning() && currentTrack)
|
||||||
if(audioPlayer.isRunning() && currentTrack)
|
switch (eventKey) {
|
||||||
switch (eventKey) {
|
case 32: //space
|
||||||
case 32: //space
|
(await audioPlayer.isPaused()) ? await audioPlayer.play() : await audioPlayer.pause();
|
||||||
await audioPlayer.isPaused() ?
|
break;
|
||||||
await audioPlayer.play() : await audioPlayer.pause();
|
case 16777236: //arrow-right
|
||||||
break;
|
(await audioPlayer.isSeekable()) && (await audioPlayer.seek(+5));
|
||||||
case 16777236: //arrow-right
|
break;
|
||||||
await audioPlayer.isSeekable() && await audioPlayer.seek(+5);
|
case 16777234: //arrow-left
|
||||||
break;
|
(await audioPlayer.isSeekable()) && (await audioPlayer.seek(-5));
|
||||||
case 16777234: //arrow-left
|
break;
|
||||||
await audioPlayer.isSeekable() && await audioPlayer.seek(-5);
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in window events: ", error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in window events: ", error)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[currentTrack]
|
[currentTrack]
|
||||||
@ -68,7 +67,6 @@ function RootApp() {
|
|||||||
const [access_token, setAccess_token] = useState<string>("");
|
const [access_token, setAccess_token] = useState<string>("");
|
||||||
const [currentPlaylist, setCurrentPlaylist] = useState<CurrentPlaylist>();
|
const [currentPlaylist, setCurrentPlaylist] = useState<CurrentPlaylist>();
|
||||||
|
|
||||||
const spotifyApi = new SpotifyWebApi({ redirectUri: redirectURI, ...credentials });
|
|
||||||
const cachedCredentials = localStorage.getItem(CredentialKeys.credentials);
|
const cachedCredentials = localStorage.getItem(CredentialKeys.credentials);
|
||||||
|
|
||||||
const setExpireTime = (expirationDuration: number) => setExpires_in(Date.now() + expirationDuration);
|
const setExpireTime = (expirationDuration: number) => setExpires_in(Date.now() + expirationDuration);
|
||||||
@ -89,18 +87,37 @@ function RootApp() {
|
|||||||
windowRef.current?.removeEventListener(WidgetEventTypes.Close, onWindowClose);
|
windowRef.current?.removeEventListener(WidgetEventTypes.Close, onWindowClose);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
// for user code login
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLoggedIn && credentials && !localStorage.getItem(CredentialKeys.refresh_token)) {
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.get<null, null, null, { code: string }>("/auth/spotify/callback", async (req, res) => {
|
||||||
|
try {
|
||||||
|
spotifyApi.setClientId(credentials.clientId);
|
||||||
|
spotifyApi.setClientSecret(credentials.clientSecret);
|
||||||
|
const { body: authRes } = await spotifyApi.authorizationCodeGrant(req.query.code);
|
||||||
|
setAccess_token(authRes.access_token);
|
||||||
|
setExpireTime(authRes.expires_in);
|
||||||
|
localStorage.setItem(CredentialKeys.refresh_token, authRes.refresh_token);
|
||||||
|
return res.end();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fullfil code grant flow: ", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = app.listen(4304, () => {
|
||||||
|
console.log("Server is running");
|
||||||
|
open(spotifyApi.createAuthorizeURL(["user-library-read", "user-library-modify"], "xxxyyysssddd")).catch((e) => console.error("Opening IPC connection with browser failed: ", e));
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
server.close(() => console.log("Closed server"));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isLoggedIn, credentials]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoggedIn) {
|
|
||||||
spotifyApi
|
|
||||||
.clientCredentialsGrant()
|
|
||||||
.then(({ body: { access_token } }) => {
|
|
||||||
setAccess_token(access_token);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Spotify Client Credential not granted for: ", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (cachedCredentials) {
|
if (cachedCredentials) {
|
||||||
setCredentials(JSON.parse(cachedCredentials));
|
setCredentials(JSON.parse(cachedCredentials));
|
||||||
}
|
}
|
||||||
@ -110,7 +127,7 @@ function RootApp() {
|
|||||||
<Window ref={windowRef} on={windowEvents} windowState={WindowState.WindowMaximized} windowIcon={winIcon} windowTitle="Spotube" minSize={minSize}>
|
<Window ref={windowRef} on={windowEvents} windowState={WindowState.WindowMaximized} windowIcon={winIcon} windowTitle="Spotube" minSize={minSize}>
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<authContext.Provider value={{ isLoggedIn, setIsLoggedIn, access_token, expires_in, setAccess_token, setExpires_in: setExpireTime, ...credentials }}>
|
<authContext.Provider value={{ isLoggedIn, setIsLoggedIn, access_token, expires_in, setAccess_token, setExpires_in: setExpireTime, ...credentials }}>
|
||||||
<playerContext.Provider value={{ spotifyApi, currentPlaylist, currentTrack, setCurrentPlaylist, setCurrentTrack }}>
|
<playerContext.Provider value={{ currentPlaylist, currentTrack, setCurrentPlaylist, setCurrentTrack }}>
|
||||||
<View style={`flex: 1; flex-direction: 'column'; justify-content: 'center'; align-items: 'stretch'; height: '100%';`}>
|
<View style={`flex: 1; flex-direction: 'column'; justify-content: 'center'; align-items: 'stretch'; height: '100%';`}>
|
||||||
<Routes />
|
<Routes />
|
||||||
{isLoggedIn && <Player />}
|
{isLoggedIn && <Player />}
|
||||||
|
@ -5,34 +5,34 @@ import authContext from "../context/authContext";
|
|||||||
import { useHistory } from "react-router";
|
import { useHistory } from "react-router";
|
||||||
import CachedImage from "./shared/CachedImage";
|
import CachedImage from "./shared/CachedImage";
|
||||||
import { CursorShape } from "@nodegui/nodegui";
|
import { CursorShape } from "@nodegui/nodegui";
|
||||||
|
import useSpotifyApi from "../hooks/useSpotifyApi";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const { spotifyApi, currentPlaylist } = useContext(playerContext);
|
const { currentPlaylist } = useContext(playerContext);
|
||||||
const { isLoggedIn, access_token } = useContext(authContext);
|
const spotifyApi = useSpotifyApi();
|
||||||
|
const { access_token } = useContext(authContext);
|
||||||
const [categories, setCategories] = useState<SpotifyApi.CategoryObject[]>([]);
|
const [categories, setCategories] = useState<SpotifyApi.CategoryObject[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
if (access_token) {
|
||||||
try {
|
(async () => {
|
||||||
if (access_token) {
|
try {
|
||||||
spotifyApi.setAccessToken(access_token);
|
|
||||||
const categoriesReceived = await spotifyApi.getCategories({ country: "US" });
|
const categoriesReceived = await spotifyApi.getCategories({ country: "US" });
|
||||||
setCategories(categoriesReceived.body.categories.items);
|
setCategories(categoriesReceived.body.categories.items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Spotify featured playlist loading failed: ", error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
console.error("Spotify featured playlist loading failed: ", error);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [access_token]);
|
}, [access_token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea style={`flex-grow: 1; border: none; flex: 1;`}>
|
<ScrollArea style={`flex-grow: 1; border: none; flex: 1;`}>
|
||||||
<View style={`flex-direction: 'column'; justify-content: 'center'; flex: 1;`}>
|
<View style={`flex-direction: 'column'; justify-content: 'center'; flex: 1;`}>
|
||||||
{currentPlaylist && <CategoryCard key={((Math.random() * Date.now()) / Math.random()) * 100} id="current" name="Currently Playing" />}
|
{currentPlaylist && <CategoryCard key={((Math.random() * Date.now()) / Math.random()) * 100} id="current" name="Currently Playing" />}
|
||||||
{isLoggedIn &&
|
{categories.map((category, index) => {
|
||||||
categories.map(({ id, name }, index) => {
|
return <CategoryCard key={((index * Date.now()) / Math.random()) * 100} {...category} />;
|
||||||
return <CategoryCard key={((index * Date.now()) / Math.random()) * 100} id={id} name={name} />;
|
})}
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
);
|
);
|
||||||
@ -48,21 +48,25 @@ interface CategoryCardProps {
|
|||||||
function CategoryCard({ id, name }: CategoryCardProps) {
|
function CategoryCard({ id, name }: CategoryCardProps) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [playlists, setPlaylists] = useState<SpotifyApi.PlaylistObjectSimplified[]>([]);
|
const [playlists, setPlaylists] = useState<SpotifyApi.PlaylistObjectSimplified[]>([]);
|
||||||
const { access_token, isLoggedIn } = useContext(authContext);
|
const { currentPlaylist } = useContext(playerContext);
|
||||||
const { spotifyApi, currentPlaylist } = useContext(playerContext);
|
const spotifyApi = useSpotifyApi();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
if (id !== "current") {
|
if (id !== "current") {
|
||||||
spotifyApi.setAccessToken(access_token);
|
|
||||||
const playlistsRes = await spotifyApi.getPlaylistsForCategory(id, { limit: 4 });
|
const playlistsRes = await spotifyApi.getPlaylistsForCategory(id, { limit: 4 });
|
||||||
setPlaylists(playlistsRes.body.playlists.items);
|
mounted && setPlaylists(playlistsRes.body.playlists.items);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to get playlists of category ${name} for: `, error);
|
console.error(`Failed to get playlists of category ${name} for: `, error);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function goToGenre() {
|
function goToGenre() {
|
||||||
@ -103,10 +107,9 @@ function CategoryCard({ id, name }: CategoryCardProps) {
|
|||||||
{(playlists.length > 0 || id === "current") && <Button id="anchor-heading" cursor={CursorShape.PointingHandCursor} on={{ MouseButtonRelease: goToGenre }} text={name} />}
|
{(playlists.length > 0 || id === "current") && <Button id="anchor-heading" cursor={CursorShape.PointingHandCursor} on={{ MouseButtonRelease: goToGenre }} text={name} />}
|
||||||
<View id="child-view">
|
<View id="child-view">
|
||||||
{id === "current" && currentPlaylist && <PlaylistCard key={(Date.now() / Math.random()) * 100} {...currentPlaylist} />}
|
{id === "current" && currentPlaylist && <PlaylistCard key={(Date.now() / Math.random()) * 100} {...currentPlaylist} />}
|
||||||
{isLoggedIn &&
|
{playlists.map((playlist, index) => {
|
||||||
playlists.map((playlist, index) => {
|
return <PlaylistCard key={((index * Date.now()) / Math.random()) * 100} id={playlist.id} name={playlist.name} thumbnail={playlist.images[0].url} />;
|
||||||
return <PlaylistCard key={((index * Date.now()) / Math.random()) * 100} id={playlist.id} name={playlist.name} thumbnail={playlist.images[0].url} />;
|
})}
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ import PlayerProgressBar from "./PlayerProgressBar";
|
|||||||
import { random as shuffleIcon, play, pause, backward, forward, stop, heartRegular } from "../icons";
|
import { random as shuffleIcon, play, pause, backward, forward, stop, heartRegular } from "../icons";
|
||||||
import IconButton from "./shared/IconButton";
|
import IconButton from "./shared/IconButton";
|
||||||
import authContext from "../context/authContext";
|
import authContext from "../context/authContext";
|
||||||
|
import useSpotifyApi from "../hooks/useSpotifyApi";
|
||||||
|
|
||||||
export const audioPlayer = new NodeMpv(
|
export const audioPlayer = new NodeMpv(
|
||||||
{
|
{
|
||||||
@ -16,14 +17,15 @@ export const audioPlayer = new NodeMpv(
|
|||||||
auto_restart: true,
|
auto_restart: true,
|
||||||
time_update: 1,
|
time_update: 1,
|
||||||
binary: process.env.MPV_EXECUTABLE ?? "/usr/bin/mpv",
|
binary: process.env.MPV_EXECUTABLE ?? "/usr/bin/mpv",
|
||||||
debug: true,
|
// debug: true,
|
||||||
verbose: true,
|
// verbose: true,
|
||||||
},
|
},
|
||||||
["--ytdl-raw-options-set=format=140,http-chunk-size=300000"]
|
["--ytdl-raw-options-set=format=140,http-chunk-size=300000"]
|
||||||
);
|
);
|
||||||
|
|
||||||
function Player(): ReactElement {
|
function Player(): ReactElement {
|
||||||
const { currentTrack, currentPlaylist, setCurrentTrack, setCurrentPlaylist, spotifyApi } = useContext(playerContext);
|
const { currentTrack, currentPlaylist, setCurrentTrack, setCurrentPlaylist } = useContext(playerContext);
|
||||||
|
const spotifyApi = useSpotifyApi();
|
||||||
const { access_token } = useContext(authContext);
|
const { access_token } = useContext(authContext);
|
||||||
const initVolume = parseFloat(localStorage.getItem("volume") ?? "55");
|
const initVolume = parseFloat(localStorage.getItem("volume") ?? "55");
|
||||||
const [isPaused, setIsPaused] = useState(true);
|
const [isPaused, setIsPaused] = useState(true);
|
||||||
@ -67,19 +69,18 @@ function Player(): ReactElement {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
(async () => {
|
// (async () => {
|
||||||
try {
|
// try {
|
||||||
if (access_token) {
|
// if (access_token) {
|
||||||
spotifyApi.setAccessToken(access_token);
|
// const userSavedTrack = await spotifyApi.getMySavedTracks();
|
||||||
const userSavedTrack = await spotifyApi.getMySavedTracks();
|
// console.log("userSavedTrack:", userSavedTrack);
|
||||||
console.log("userSavedTrack:", userSavedTrack);
|
// }
|
||||||
}
|
// } catch (error) {
|
||||||
} catch (error) {
|
// console.error("Failed to get spotify user saved tracks: ", error);
|
||||||
console.error("Failed to get spotify user saved tracks: ", error);
|
// }
|
||||||
}
|
// })();
|
||||||
})();
|
// }, [access_token]);
|
||||||
}, [access_token]);
|
|
||||||
|
|
||||||
// track change effect
|
// track change effect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -2,7 +2,7 @@ import { ScrollArea, Text, View } from "@nodegui/react-nodegui";
|
|||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { useLocation, useParams } from "react-router";
|
import { useLocation, useParams } from "react-router";
|
||||||
import authContext from "../context/authContext";
|
import authContext from "../context/authContext";
|
||||||
import playerContext from "../context/playerContext";
|
import useSpotifyApi from "../hooks/useSpotifyApi";
|
||||||
import BackButton from "./BackButton";
|
import BackButton from "./BackButton";
|
||||||
import { PlaylistCard } from "./Home";
|
import { PlaylistCard } from "./Home";
|
||||||
|
|
||||||
@ -11,20 +11,24 @@ function PlaylistGenreView() {
|
|||||||
const location = useLocation<{ name: string }>();
|
const location = useLocation<{ name: string }>();
|
||||||
const [playlists, setPlaylists] = useState<SpotifyApi.PlaylistObjectSimplified[]>([]);
|
const [playlists, setPlaylists] = useState<SpotifyApi.PlaylistObjectSimplified[]>([]);
|
||||||
const { access_token, isLoggedIn } = useContext(authContext);
|
const { access_token, isLoggedIn } = useContext(authContext);
|
||||||
const { spotifyApi } = useContext(playerContext);
|
const spotifyApi = useSpotifyApi();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
if (access_token) {
|
if (access_token) {
|
||||||
spotifyApi.setAccessToken(access_token);
|
|
||||||
const playlistsRes = await spotifyApi.getPlaylistsForCategory(id);
|
const playlistsRes = await spotifyApi.getPlaylistsForCategory(id);
|
||||||
setPlaylists(playlistsRes.body.playlists.items);
|
mounted && setPlaylists(playlistsRes.body.playlists.items);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to get playlists of category ${name} for: `, error);
|
console.error(`Failed to get playlists of category ${name} for: `, error);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
}, [access_token]);
|
}, [access_token]);
|
||||||
|
|
||||||
const playlistGenreViewStylesheet = `
|
const playlistGenreViewStylesheet = `
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import React, { FC, useContext, useEffect, useState } from "react";
|
import React, { FC, useContext, useEffect, useState } from "react";
|
||||||
import { BoxView, Button, GridView, ScrollArea, Text, View } from "@nodegui/react-nodegui";
|
import { Button, ScrollArea, Text, View } from "@nodegui/react-nodegui";
|
||||||
import BackButton from "./BackButton";
|
import BackButton from "./BackButton";
|
||||||
import { useLocation, useParams } from "react-router";
|
import { useLocation, useParams } from "react-router";
|
||||||
import { Direction, QAbstractButtonSignals, QIcon } from "@nodegui/nodegui";
|
import { QAbstractButtonSignals, QIcon } from "@nodegui/nodegui";
|
||||||
import { WidgetEventListeners } from "@nodegui/react-nodegui/dist/components/View/RNView";
|
import { WidgetEventListeners } from "@nodegui/react-nodegui/dist/components/View/RNView";
|
||||||
import authContext from "../context/authContext";
|
import authContext from "../context/authContext";
|
||||||
import playerContext from "../context/playerContext";
|
import playerContext from "../context/playerContext";
|
||||||
import IconButton from "./shared/IconButton";
|
import IconButton from "./shared/IconButton";
|
||||||
import { heartRegular, play, stop } from "../icons";
|
import { heartRegular, play, stop } from "../icons";
|
||||||
import { audioPlayer } from "./Player";
|
import { audioPlayer } from "./Player";
|
||||||
|
import useSpotifyApi from "../hooks/useSpotifyApi";
|
||||||
|
|
||||||
export interface PlaylistTrackRes {
|
export interface PlaylistTrackRes {
|
||||||
name: string;
|
name: string;
|
||||||
@ -16,13 +17,10 @@ export interface PlaylistTrackRes {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlaylistViewProps {
|
const PlaylistView: FC = () => {
|
||||||
// audioPlayer: any;
|
const { isLoggedIn } = useContext(authContext);
|
||||||
}
|
const { setCurrentTrack, currentPlaylist, currentTrack, setCurrentPlaylist } = useContext(playerContext);
|
||||||
|
const spotifyApi = useSpotifyApi();
|
||||||
const PlaylistView: FC<PlaylistViewProps> = () => {
|
|
||||||
const { isLoggedIn, access_token } = useContext(authContext);
|
|
||||||
const { spotifyApi, setCurrentTrack, currentPlaylist, currentTrack, setCurrentPlaylist } = useContext(playerContext);
|
|
||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
const location = useLocation<{ name: string; thumbnail: string }>();
|
const location = useLocation<{ name: string; thumbnail: string }>();
|
||||||
const [tracks, setTracks] = useState<SpotifyApi.PlaylistTrackObject[]>([]);
|
const [tracks, setTracks] = useState<SpotifyApi.PlaylistTrackObject[]>([]);
|
||||||
@ -31,7 +29,6 @@ const PlaylistView: FC<PlaylistViewProps> = () => {
|
|||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
spotifyApi.setAccessToken(access_token);
|
|
||||||
const tracks = await spotifyApi.getPlaylistTracks(params.id);
|
const tracks = await spotifyApi.getPlaylistTracks(params.id);
|
||||||
setTracks(tracks.body.items);
|
setTracks(tracks.body.items);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -15,16 +15,18 @@ function CachedImage({ src, alt, ...props }: CachedImageProps) {
|
|||||||
const [imageProcessError, setImageProcessError] = useState<boolean>(false);
|
const [imageProcessError, setImageProcessError] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
setImageBuffer(await getCachedImageBuffer(src, props.maxSize ?? props.size));
|
mounted && setImageBuffer(await getCachedImageBuffer(src, props.maxSize ?? props.size));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setImageProcessError(false);
|
mounted && setImageProcessError(false);
|
||||||
console.log("Cached Image Error:", error);
|
console.log("Cached Image Error:", error);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return () => {
|
return () => {
|
||||||
imgRef.current?.close();
|
imgRef.current?.close();
|
||||||
|
mounted = false;
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
return !imageProcessError && imageBuffer ? (
|
return !imageProcessError && imageBuffer ? (
|
||||||
|
@ -3,4 +3,4 @@ import { join } from "path";
|
|||||||
|
|
||||||
const env = dotenv.config({path: join(process.cwd(), ".env")}).parsed as any
|
const env = dotenv.config({path: join(process.cwd(), ".env")}).parsed as any
|
||||||
export const clientId = "";
|
export const clientId = "";
|
||||||
export const redirectURI = "http%3A%2F%2F/localhost:4304/auth/spotify/callback/"
|
export const redirectURI = "http://localhost:4304/auth/spotify/callback"
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import React, { Dispatch, SetStateAction } from "react";
|
import React, { Dispatch, SetStateAction } from "react";
|
||||||
import SpotifyWebApi from "spotify-web-api-node";
|
|
||||||
|
|
||||||
export type CurrentTrack = SpotifyApi.TrackObjectFull;
|
export type CurrentTrack = SpotifyApi.TrackObjectFull;
|
||||||
|
|
||||||
export type CurrentPlaylist = { tracks: SpotifyApi.PlaylistTrackObject[]; id: string; name: string; thumbnail: string };
|
export type CurrentPlaylist = { tracks: SpotifyApi.PlaylistTrackObject[]; id: string; name: string; thumbnail: string };
|
||||||
|
|
||||||
export interface PlayerContext {
|
export interface PlayerContext {
|
||||||
spotifyApi: SpotifyWebApi;
|
|
||||||
currentPlaylist?: CurrentPlaylist;
|
currentPlaylist?: CurrentPlaylist;
|
||||||
currentTrack?: CurrentTrack;
|
currentTrack?: CurrentTrack;
|
||||||
setCurrentPlaylist: Dispatch<SetStateAction<CurrentPlaylist | undefined>>;
|
setCurrentPlaylist: Dispatch<SetStateAction<CurrentPlaylist | undefined>>;
|
||||||
setCurrentTrack: Dispatch<SetStateAction<CurrentTrack | undefined>>;
|
setCurrentTrack: Dispatch<SetStateAction<CurrentTrack | undefined>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const playerContext = React.createContext<PlayerContext>({ spotifyApi: new SpotifyWebApi(), setCurrentPlaylist() {}, setCurrentTrack() {} });
|
const playerContext = React.createContext<PlayerContext>({ setCurrentPlaylist() {}, setCurrentTrack() {} });
|
||||||
|
|
||||||
export default playerContext;
|
export default playerContext;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { DependencyList, useContext, useEffect } from "react";
|
import { DependencyList, useContext, useEffect } from "react";
|
||||||
import authContext from "../context/authContext";
|
import authContext from "../context/authContext";
|
||||||
import { CredentialKeys } from "../app";
|
import { CredentialKeys } from "../app";
|
||||||
import playerContext from "../context/playerContext";
|
|
||||||
import SpotifyWebApi from "spotify-web-api-node";
|
import SpotifyWebApi from "spotify-web-api-node";
|
||||||
|
|
||||||
interface UseAccessTokenResult {
|
interface UseAccessTokenResult {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useContext, useEffect } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
|
import { CredentialKeys } from "../app";
|
||||||
import authContext from "../context/authContext";
|
import authContext from "../context/authContext";
|
||||||
import spotifyApi from "../initializations/spotifyApi";
|
import spotifyApi from "../initializations/spotifyApi";
|
||||||
import useAccessToken from "./useAccessToken";
|
|
||||||
|
|
||||||
function useSpotifyApi() {
|
function useSpotifyApi() {
|
||||||
const { isLoggedIn, clientId, clientSecret } = useContext(authContext);
|
const { access_token, clientId, clientSecret, expires_in, isLoggedIn, setExpires_in, setAccess_token } = useContext(authContext);
|
||||||
const { access_token } = useAccessToken(spotifyApi, [clientId, clientSecret]);
|
const refreshToken = localStorage.getItem(CredentialKeys.refresh_token);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoggedIn && clientId && clientSecret) {
|
if (isLoggedIn && clientId && clientSecret) {
|
||||||
@ -13,6 +13,17 @@ function useSpotifyApi() {
|
|||||||
spotifyApi.setClientSecret(clientSecret);
|
spotifyApi.setClientSecret(clientSecret);
|
||||||
spotifyApi.setAccessToken(access_token);
|
spotifyApi.setAccessToken(access_token);
|
||||||
}
|
}
|
||||||
|
const isExpiredToken = Date.now() > expires_in;
|
||||||
|
if (isLoggedIn && isExpiredToken && refreshToken) {
|
||||||
|
spotifyApi.setRefreshToken(refreshToken);
|
||||||
|
spotifyApi
|
||||||
|
.refreshAccessToken()
|
||||||
|
.then(({ body: { access_token, expires_in } }) => {
|
||||||
|
setAccess_token(access_token);
|
||||||
|
setExpires_in(expires_in);
|
||||||
|
})
|
||||||
|
.catch();
|
||||||
|
}
|
||||||
}, [access_token, clientId, clientSecret]);
|
}, [access_token, clientId, clientSecret]);
|
||||||
|
|
||||||
return spotifyApi;
|
return spotifyApi;
|
||||||
|
Loading…
Reference in New Issue
Block a user