fixed webpack rmSync is not a function & added cached playback

This commit is contained in:
KRTirtho 2021-05-03 13:25:17 +06:00
parent 0dd41812ee
commit 35da4d5fd4
5 changed files with 51 additions and 35 deletions

View File

@ -14,17 +14,10 @@ import spotifyApi from "./initializations/spotifyApi";
import showError from "./helpers/showError"; import showError from "./helpers/showError";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { confDir } from "./conf"; import { confDir, LocalStorageKeys } from "./conf";
import spotubeIcon from "../assets/icon.svg"; import spotubeIcon from "../assets/icon.svg";
import preferencesContext, { PreferencesContextProperties } from "./context/preferencesContext"; import preferencesContext, { PreferencesContextProperties } from "./context/preferencesContext";
export enum LocalStorageKeys {
credentials = "credentials",
refresh_token = "refresh_token",
preferences = "user-preferences",
volume = "volume",
}
export interface Credentials { export interface Credentials {
clientId: string; clientId: string;
clientSecret: string; clientSecret: string;

View File

@ -4,14 +4,14 @@ import React, { ReactElement, useContext, useEffect, useRef, useState } from "re
import playerContext, { CurrentPlaylist } from "../context/playerContext"; import playerContext, { CurrentPlaylist } from "../context/playerContext";
import { shuffleArray } from "../helpers/shuffleArray"; import { shuffleArray } from "../helpers/shuffleArray";
import NodeMpv from "node-mpv"; import NodeMpv from "node-mpv";
import { getYoutubeTrack } from "../helpers/getYoutubeTrack"; import { getYoutubeTrack, YoutubeTrack } from "../helpers/getYoutubeTrack";
import PlayerProgressBar from "./PlayerProgressBar"; import PlayerProgressBar from "./PlayerProgressBar";
import { random as shuffleIcon, play, pause, backward, forward, stop, heartRegular, heart, musicNode } from "../icons"; import { random as shuffleIcon, play, pause, backward, forward, stop, heartRegular, heart, musicNode } from "../icons";
import IconButton from "./shared/IconButton"; import IconButton from "./shared/IconButton";
import showError from "../helpers/showError"; import showError from "../helpers/showError";
import useTrackReaction from "../hooks/useTrackReaction"; import useTrackReaction from "../hooks/useTrackReaction";
import ManualLyricDialog from "./ManualLyricDialog"; import ManualLyricDialog from "./ManualLyricDialog";
import { LocalStorageKeys } from "../app"; import { LocalStorageKeys } from "../conf";
export const audioPlayer = new NodeMpv( export const audioPlayer = new NodeMpv(
{ {
@ -35,6 +35,7 @@ function Player(): ReactElement {
const [realPlaylist, setRealPlaylist] = useState<CurrentPlaylist["tracks"]>([]); const [realPlaylist, setRealPlaylist] = useState<CurrentPlaylist["tracks"]>([]);
const [isStopped, setIsStopped] = useState<boolean>(false); const [isStopped, setIsStopped] = useState<boolean>(false);
const [openLyrics, setOpenLyrics] = useState<boolean>(false); const [openLyrics, setOpenLyrics] = useState<boolean>(false);
const [currentYtTrack, setCurrentYtTrack] = useState<YoutubeTrack>();
const playlistTracksIds = currentPlaylist?.tracks.map((t) => t.track.id); const playlistTracksIds = currentPlaylist?.tracks.map((t) => t.track.id);
const volumeHandler = useEventHandler<QAbstractSliderSignals>( const volumeHandler = useEventHandler<QAbstractSliderSignals>(
{ {
@ -49,6 +50,8 @@ function Player(): ReactElement {
); );
const playerRunning = audioPlayer.isRunning(); const playerRunning = audioPlayer.isRunning();
const titleRef = useRef<QLabel>(); const titleRef = useRef<QLabel>();
const cachedPlaylist = localStorage.getItem(LocalStorageKeys.cachedPlaylist);
const cachedTrack = localStorage.getItem(LocalStorageKeys.cachedTrack);
// initial Effect // initial Effect
useEffect(() => { useEffect(() => {
@ -61,21 +64,31 @@ function Player(): ReactElement {
} catch (error) { } catch (error) {
showError(error, "[Failed starting audio player]: "); showError(error, "[Failed starting audio player]: ");
} }
})(); })().then(() => {
if (cachedPlaylist && !currentPlaylist) {
setCurrentPlaylist(JSON.parse(cachedPlaylist));
}
if (cachedTrack && !currentTrack) {
setCurrentTrack(JSON.parse(cachedTrack));
}
});
return () => { return () => {
if (playerRunning) { if (playerRunning) {
audioPlayer.quit().catch((e: any) => console.log(e)); audioPlayer.quit().catch((e: unknown) => console.log(e));
} }
}; };
}, []); }, []);
// track change effect // track change effect
useEffect(() => { useEffect(() => {
// caching current track
localStorage.setItem(LocalStorageKeys.cachedTrack, JSON.stringify(currentTrack ?? ""));
(async () => { (async () => {
try { try {
if (currentTrack && playerRunning) { if (currentTrack && playerRunning) {
const youtubeTrack = await getYoutubeTrack(currentTrack); const youtubeTrack = await getYoutubeTrack(currentTrack);
setCurrentYtTrack(youtubeTrack);
await audioPlayer.load(youtubeTrack.youtube_uri, "replace"); await audioPlayer.load(youtubeTrack.youtube_uri, "replace");
await audioPlayer.play(); await audioPlayer.play();
setIsPaused(false); setIsPaused(false);
@ -94,6 +107,8 @@ function Player(): ReactElement {
// changing shuffle to default // changing shuffle to default
useEffect(() => { useEffect(() => {
setShuffle(false); setShuffle(false);
// caching playlist
localStorage.setItem(LocalStorageKeys.cachedPlaylist, JSON.stringify(currentPlaylist ?? ""));
}, [currentPlaylist]); }, [currentPlaylist]);
useEffect(() => { useEffect(() => {
@ -191,10 +206,10 @@ function Player(): ReactElement {
<GridView enabled={!!currentTrack} style="flex: 1; max-height: 120px;"> <GridView enabled={!!currentTrack} style="flex: 1; max-height: 120px;">
<GridRow> <GridRow>
<GridColumn width={2}> <GridColumn width={2}>
<Text ref={titleRef} wordWrap> <Text ref={titleRef} wordWrap openExternalLinks>
{artistsNames && currentTrack {artistsNames && currentTrack
? ` ? `
<p><b>${currentTrack.name}</b> - ${artistsNames[0]} ${artistsNames.length > 1 ? "feat. " + artistsNames.slice(1).join(", ") : ""}</p> <p><b><a href="${currentYtTrack?.youtube_uri}"}>${currentTrack.name}</a></b> - ${artistsNames[0]} ${artistsNames.length > 1 ? "feat. " + artistsNames.slice(1).join(", ") : ""}</p>
` `
: `<b>Oh, dear don't waste time</b>`} : `<b>Oh, dear don't waste time</b>`}
</Text> </Text>

View File

@ -1,23 +1,32 @@
import dotenv from "dotenv" import dotenv from "dotenv";
import { homedir } from "os"; import { homedir } from "os";
import { join } from "path"; 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 trace = process.argv.find(arg => arg === "--trace") ?? false; export const trace = process.argv.find((arg) => arg === "--trace") ?? false;
export const redirectURI = "http://localhost:4304/auth/spotify/callback" export const redirectURI = "http://localhost:4304/auth/spotify/callback";
export const confDir = join(homedir(), ".config", "spotube") export const confDir = join(homedir(), ".config", "spotube");
export const cacheDir = join(homedir(), ".cache", "spotube") export const cacheDir = join(homedir(), ".cache", "spotube");
export enum QueryCacheKeys{ export enum QueryCacheKeys {
categories="categories", categories = "categories",
categoryPlaylists = "categoryPlaylists", categoryPlaylists = "categoryPlaylists",
featuredPlaylists = "featuredPlaylists", featuredPlaylists = "featuredPlaylists",
genrePlaylists="genrePlaylists", genrePlaylists = "genrePlaylists",
playlistTracks="playlistTracks", playlistTracks = "playlistTracks",
userPlaylists = "user-palylists", userPlaylists = "user-palylists",
userSavedTracks = "user-saved-tracks", userSavedTracks = "user-saved-tracks",
search = "search", search = "search",
searchPlaylist = "searchPlaylist", searchPlaylist = "searchPlaylist",
searchSongs = "searchSongs" searchSongs = "searchSongs",
} }
export enum LocalStorageKeys {
credentials = "credentials",
refresh_token = "refresh_token",
preferences = "user-preferences",
volume = "volume",
cachedPlaylist = "cached-playlist",
cachedTrack = "cached-track"
}

View File

@ -1,6 +1,6 @@
import path from "path"; import path from "path";
import isUrl from "is-url"; import isUrl from "is-url";
import * as fs from "fs" import fs from "fs";
import axios from "axios"; import axios from "axios";
import { Stream } from "stream"; import { Stream } from "stream";
import { streamToBuffer } from "./streamToBuffer"; import { streamToBuffer } from "./streamToBuffer";
@ -8,7 +8,6 @@ import Jimp from "jimp";
import du from "du"; import du from "du";
import { cacheDir } from "../conf"; import { cacheDir } from "../conf";
interface ImageDimensions { interface ImageDimensions {
height: number; height: number;
width: number; width: number;
@ -22,20 +21,20 @@ export async function getCachedImageBuffer(name: string, dims?: ImageDimensions)
const cacheImgFolder = path.join(cacheDir, "images"); const cacheImgFolder = path.join(cacheDir, "images");
// for clearing up the cache if it reaches out of the size // for clearing up the cache if it reaches out of the size
const cacheName = `${isUrl(name) ? name.split("/").slice(-1)[0] : name}.cnim`; const cacheName = `${isUrl(name) ? name.split("/").slice(-1)[0] : name}.cnim`;
const cachePath = path.join(cacheImgFolder, cacheName); const cacheImgPath = path.join(cacheImgFolder, cacheName);
// checking if the cached image already exists or not // checking if the cached image already exists or not
if (fs.existsSync(cachePath)) { if (fs.existsSync(cacheImgPath)) {
// automatically removing cache after a certain 50 MB oversize // automatically removing cache after a certain 50 MB oversize
if ((await du(cacheImgFolder)) > MB_5) { if ((await du(cacheImgFolder)) > MB_5) {
fs.rmSync(cacheImgFolder, { recursive: true, force: true }); fs.rmdirSync(cacheImgFolder, { recursive: true });
} }
const cachedImg = await fsm.readFile(cachePath); const cachedImg = await fsm.readFile(cacheImgPath);
const cachedImgMeta = (await Jimp.read(cachedImg)).bitmap; const cachedImgMeta = (await Jimp.read(cachedImg)).bitmap;
// if the dimensions are changed then the previously cached // if the dimensions are changed then the previously cached
// images are removed and replaced with a new one // images are removed and replaced with a new one
if (dims && (cachedImgMeta.height !== dims.height || cachedImgMeta.width !== dims?.width)) { if (dims && (cachedImgMeta.height !== dims.height || cachedImgMeta.width !== dims?.width)) {
fs.rmSync(cachePath); fs.unlinkSync(cacheImgPath);
return await imageResizeAndWrite(cachedImg, { cacheFolder: cacheImgFolder, cacheName, dims }); return await imageResizeAndWrite(cachedImg, { cacheFolder: cacheImgFolder, cacheName, dims });
} }
return cachedImg; return cachedImg;
@ -61,7 +60,7 @@ export async function getCachedImageBuffer(name: string, dims?: ImageDimensions)
async function imageResizeAndWrite(img: Buffer, { cacheFolder, cacheName, dims }: { dims: ImageDimensions; cacheFolder: string; cacheName: string }): Promise<Buffer> { async function imageResizeAndWrite(img: Buffer, { cacheFolder, cacheName, dims }: { dims: ImageDimensions; cacheFolder: string; cacheName: string }): Promise<Buffer> {
// caching the images by resizing if the max/fixed (Width/Height) // caching the images by resizing if the max/fixed (Width/Height)
// is available in the args // is available in the args
const resizedImg = (await Jimp.read(img)).resize(dims.width, dims.height) const resizedImg = (await Jimp.read(img)).resize(dims.width, dims.height);
const resizedImgBuffer = await resizedImg.getBufferAsync(resizedImg._originalMime); const resizedImgBuffer = await resizedImg.getBufferAsync(resizedImg._originalMime);
await fsm.writeFile(path.join(cacheFolder, cacheName), resizedImgBuffer); await fsm.writeFile(path.join(cacheFolder, cacheName), resizedImgBuffer);
return resizedImgBuffer; return resizedImgBuffer;

View File

@ -1,6 +1,6 @@
import chalk from "chalk"; import chalk from "chalk";
import { useContext, useEffect } from "react"; import { useContext, useEffect } from "react";
import { LocalStorageKeys } from "../app"; import { LocalStorageKeys } from "../conf";
import authContext from "../context/authContext"; import authContext from "../context/authContext";
import showError from "../helpers/showError"; import showError from "../helpers/showError";
import spotifyApi from "../initializations/spotifyApi"; import spotifyApi from "../initializations/spotifyApi";