Spotify container

This commit is contained in:
Daniel Kluge 2022-05-12 18:48:40 +02:00
parent a1300e851d
commit 1e90ecf826
6 changed files with 102 additions and 20 deletions

30
package-lock.json generated
View File

@ -10,6 +10,7 @@
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"dependencies": { "dependencies": {
"buffer": "^6.0.3", "buffer": "^6.0.3",
"fast-average-color": "^7.1.0",
"fast-xml-parser": "^4.0.0-beta.8", "fast-xml-parser": "^4.0.0-beta.8",
"gatsby": "^4.4.0", "gatsby": "^4.4.0",
"gatsby-plugin-react-svg": "^3.1.0", "gatsby-plugin-react-svg": "^3.1.0",
@ -2767,6 +2768,11 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/@types/offscreencanvas": {
"version": "2019.6.4",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz",
"integrity": "sha512-u8SAgdZ8ROtkTF+mfZGOscl0or6BSj9A4g37e6nvxDc+YB/oDut0wHkK2PBBiC2bNR8TS0CPV+1gAk4fNisr1Q=="
},
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -7475,6 +7481,17 @@
"url": "https://github.com/sponsors/jaydenseric" "url": "https://github.com/sponsors/jaydenseric"
} }
}, },
"node_modules/fast-average-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-7.1.0.tgz",
"integrity": "sha512-eTc18sdbr2P2xZFMhvWmo+T7MJ403k4jSiapTMoOcvkptu7SWOU+TzN4tFL4B9sIKUSXA63Xu7Mpc4fmncDZ+Q==",
"dependencies": {
"@types/offscreencanvas": "^2019.6.4"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/fast-copy": { "node_modules/fast-copy": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.1.tgz",
@ -18909,6 +18926,11 @@
} }
} }
}, },
"@types/offscreencanvas": {
"version": "2019.6.4",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz",
"integrity": "sha512-u8SAgdZ8ROtkTF+mfZGOscl0or6BSj9A4g37e6nvxDc+YB/oDut0wHkK2PBBiC2bNR8TS0CPV+1gAk4fNisr1Q=="
},
"@types/parse-json": { "@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -22482,6 +22504,14 @@
"resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz",
"integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==" "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ=="
}, },
"fast-average-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-7.1.0.tgz",
"integrity": "sha512-eTc18sdbr2P2xZFMhvWmo+T7MJ403k4jSiapTMoOcvkptu7SWOU+TzN4tFL4B9sIKUSXA63Xu7Mpc4fmncDZ+Q==",
"requires": {
"@types/offscreencanvas": "^2019.6.4"
}
},
"fast-copy": { "fast-copy": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.1.tgz",

View File

@ -24,6 +24,7 @@
}, },
"dependencies": { "dependencies": {
"buffer": "^6.0.3", "buffer": "^6.0.3",
"fast-average-color": "^7.1.0",
"fast-xml-parser": "^4.0.0-beta.8", "fast-xml-parser": "^4.0.0-beta.8",
"gatsby": "^4.4.0", "gatsby": "^4.4.0",
"gatsby-plugin-react-svg": "^3.1.0", "gatsby-plugin-react-svg": "^3.1.0",

View File

@ -1,16 +1,22 @@
import * as React from "react" import * as React from "react"
//import useWebSocket from "react-use-websocket"; //import useWebSocket from "react-use-websocket";
import { connect } from "mqtt" import { connect } from "mqtt"
import FastAverageColor from 'fast-average-color';
import type { SecretsMQTT, SongInfo } from "../lib/interfaces" import type { SecretsMQTT, SongInfo } from "../lib/interfaces"
import * as styles from "../styles/containers/Spotify.module.css" import * as styles from "../styles/containers/Spotify.module.css"
const fac = new FastAverageColor();
const Spotify = ({ mqtt, Alternative }: { mqtt: SecretsMQTT, Alternative: any }) => { const Spotify = ({ mqtt, Alternative }: { mqtt: SecretsMQTT, Alternative: any }) => {
const [lastSongInfo, setLastSongInfo] = React.useState<SongInfo | undefined>(undefined) const [lastSongInfo, setLastSongInfo] = React.useState<SongInfo>({
playbackState: "STOPPED"
});
const [color, setColors] = React.useState<{bg?: string; text?: string}>({});
const handleMessage = (_topic: string, message: string) => { const handleMessage = (_topic: string, message: string) => {
try { try {
const songInfo: SongInfo = JSON.parse(message.toString()); const songInfo: SongInfo = JSON.parse(message.toString());
setLastSongInfo(songInfo); setLastSongInfo((lastInfo) => {return {...lastInfo, ...songInfo}});
} catch { } catch {
console.warn(`Can't parse song info: ${message.toString()}`); console.warn(`Can't parse song info: ${message.toString()}`);
} }
@ -20,7 +26,7 @@ const Spotify = ({ mqtt, Alternative }: { mqtt: SecretsMQTT, Alternative: any })
const client = connect(mqtt.url, { const client = connect(mqtt.url, {
username: mqtt.username, username: mqtt.username,
password: mqtt.password, password: mqtt.password,
protocol: "mqtt" protocol: "ws"
}); });
client.on("message", handleMessage); client.on("message", handleMessage);
@ -34,11 +40,35 @@ const Spotify = ({ mqtt, Alternative }: { mqtt: SecretsMQTT, Alternative: any })
return () => { client.end() } return () => { client.end() }
}, []) }, [])
if (true || !lastSongInfo || lastSongInfo.playbackState !== "PLAYING") { React.useEffect(() => {
(async () => {
if (lastSongInfo.playbackState !== "STOPPED" && lastSongInfo.cover && lastSongInfo.cover.startsWith("http") && document) {
const { value, isDark } = await fac.getColorAsync(lastSongInfo.cover);
value[3] = 0.8;
setColors({
bg: `rgba(${value.join(",")})`,
text: `var(--textColor${isDark ? "Light" : "Dark"})`
});
} else {
setColors({});
}
})();
}, [lastSongInfo]);
if (lastSongInfo.playbackState === "STOPPED") {
return Alternative; return Alternative;
} }
return <div className={`container ${styles.container}`}> return <div className={`container ${styles.container}`} style={{
background: color.bg,
color: color.text
}}>
{lastSongInfo.cover ? <img src={lastSongInfo.cover} alt="Cover" className={styles.cover} /> : <div></div>}
<div className={styles.meta}>
<span className={styles.title}>{lastSongInfo.title || "Unbekannt"}</span><br/>
<span>{lastSongInfo.artist ? lastSongInfo.artist.join(", ") || "Unbekannt" : "Unbekannt"}</span><br/>
<span>{lastSongInfo.album || "Unbekannt"}</span>
</div>
</div> </div>
} }

View File

@ -72,9 +72,10 @@ export interface PlantState {
} }
} }
export interface SongInfo { export type SongInfo = {
playbackState: "PLAYING" | "PAUSE" | "STOPPED"; playbackState: "PLAYING" | "PAUSE" | "STOPPED";
title: string; title?: string;
artists: string[]; artist?: string[];
album: string; album?: string;
cover?: string;
} }

View File

@ -6,16 +6,31 @@
width:100%; width:100%;
height:100%; height:100%;
padding:5px; padding:5px;
background: var(--spotifyColor); background: var(--containerBg);
border-radius: 5px; border-radius: 5px;
} }
.container p { .meta {
max-width: 21vw; max-width: 21vw;
color: inherit; color: inherit;
word-wrap: normal; word-wrap: normal;
padding: 5px 15px;
height:min-content;
margin: auto 0;
font-size: larger;
}
.meta span:not(:first-child) {
margin-top: 5px;
display: inline-block;
} }
.title { .title {
font-size: 180%; font-size: 170%;
font-weight: bold; font-weight: bold;
color: inherit; color: inherit;
max-height: calc((19vh - 10px) / 2);
display: inline-block;
overflow-y: hidden;
}
.cover {
height: calc(19vh - 10px);
border-radius: 5px;
} }

View File

@ -4,17 +4,22 @@
box-sizing: border-box; box-sizing: border-box;
} }
:root {
--textColorLight: #b0b0b0;
--textColorDark: #000000;
}
:root, :root[data-theme="day"] { :root, :root[data-theme="day"] {
--containerBg: rgba(255,255,255,0.5); --containerBg: rgba(255,255,255,0.5);
--textColor: #000000; --textColor: var(--textColorDark);
color: #000000; color: var(--textColorDark);
--iconColor: #000000; --iconColor: var(--textColorDark);
} }
:root[data-theme="night"] { :root[data-theme="night"] {
--containerBg: rgba(20,20,20,0.8); --containerBg: rgba(20,20,20,0.8);
--textColor: #b0b0b0; --textColor: var(--textColorLight);
color: #b0b0b0; color: var(--textColorLight);
--iconColor: #b0b0b0; --iconColor: var(--textColorLight);
} }
body, main { body, main {