audio feature cards, added chart.js

This commit is contained in:
andy 2021-11-11 19:54:28 +00:00
parent bfb2a5d2cd
commit 4405cf885a
10 changed files with 193 additions and 38 deletions

View File

@ -13,7 +13,7 @@ namespace Selector.Cache
public class CachingAudioFeatureInjector : AudioFeatureInjector
{
private readonly IDatabaseAsync Db;
public TimeSpan CacheExpiry { get; set; } = TimeSpan.FromDays(1);
public TimeSpan CacheExpiry { get; set; } = TimeSpan.FromDays(14);
public CachingAudioFeatureInjector(
IPlayerWatcher watcher,

View File

@ -15,6 +15,7 @@ namespace Selector.Cache
private readonly IPlayerWatcher Watcher;
private readonly IDatabaseAsync Db;
private readonly ILogger<CacheWriter> Logger;
public TimeSpan CacheExpiry { get; set; } = TimeSpan.FromMinutes(10);
public CancellationToken CancelToken { get; set; }
@ -43,7 +44,7 @@ namespace Selector.Cache
Logger.LogTrace($"Caching current for [{e.Id}/{e.SpotifyUsername}]");
var resp = await Db.StringSetAsync(Key.CurrentlyPlaying(e.Id), payload);
var resp = await Db.StringSetAsync(Key.CurrentlyPlaying(e.Id), payload, expiry: CacheExpiry);
Logger.LogDebug($"Cached current for [{e.Id}/{e.SpotifyUsername}], {(resp ? "value set" : "value NOT set")}");

View File

@ -1,7 +1,12 @@
.app {
display: block;
}
.card {
background-color: grey;
color: white;
margin: 5px;
padding: 15px;
box-shadow: 4px 4px 2px #5e5e5e;
}
@ -13,20 +18,42 @@
.cover-art {
box-shadow: 4px 4px 2px #5e5e5e;
margin-bottom: 15px;
}
img {
margin: 15px;
// margin: 15px;
}
}
.info-card {
margin-top: 10px;
margin-bottom: 10px;
img {
// margin: 15px;
}
}
@media only screen and (min-width: 768px) {
.app {
display: flex;
align-items: flex-start;
}
.now-playing-card {
position: fixed;
z-index: 10;
right: 20px;
bottom: 20px;
width: 300px;
width: 250px;
}
}
@media only screen and (min-width: 768px) {
.info-card {
max-width: 300px;
}
}
@ -36,19 +63,4 @@
img {
width: 21px;
}
}
.info-card {
margin-top: 10px;
margin-bottom: 10px;
img {
margin: 15px;
}
}
@media only screen and (min-width: 768px) {
.info-card {
max-width: 300px;
}
}

View File

@ -6,12 +6,14 @@
<div class="text-center">
<h1 class="display-4">Now</h1>
<div id="app">
<now-playing-card :track="currentlyPlaying.track" v-if="currentlyPlaying !== null && currentlyPlaying !== undefined"></now-playing-card>
<now-playing-card v-else></now-playing-card>
<div id="app" class="app col-12">
<now-playing-card :track="currentlyPlaying.track" v-if="currentlyPlaying !== null && currentlyPlaying !== undefined" ></now-playing-card>
<now-playing-card v-else ></now-playing-card>
<popularity :track="currentlyPlaying.track" v-if="currentlyPlaying !== null && currentlyPlaying !== undefined && currentlyPlaying.track != null && currentlyPlaying.track != undefined" />
<info-card v-for="card in cards" :html="card.html" />
<popularity :track="currentlyPlaying.track" v-if="currentlyPlaying !== null && currentlyPlaying !== undefined && currentlyPlaying.track != null && currentlyPlaying.track != undefined" ></popularity>
<audio-feature-card :feature="trackFeatures" v-if="trackFeatures !== null && trackFeatures !== undefined" /></audio-feature-card>
<audio-feature-chart-card :feature="trackFeatures" v-if="trackFeatures !== null && trackFeatures !== undefined" /></audio-feature-chart-card>
<info-card v-for="card in cards" :html="card.html"></info-card>
</div>
</div>

View File

@ -11,6 +11,7 @@
"dependencies": {
"@microsoft/signalr": "^5.0.11",
"bootstrap": "^5.1.3",
"chart.js": "^3.6.0",
"vue": "^3.2.21"
},
"devDependencies": {
@ -943,6 +944,11 @@
"node": ">=8"
}
},
"node_modules/chart.js": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.0.tgz",
"integrity": "sha512-iOzzDKePL+bj+ccIsVAgWQehCXv8xOKGbaU2fO/myivH736zcx535PGJzQGanvcSGVOqX6yuLZsN3ygcQ35UgQ=="
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
@ -5175,6 +5181,11 @@
}
}
},
"chart.js": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.0.tgz",
"integrity": "sha512-iOzzDKePL+bj+ccIsVAgWQehCXv8xOKGbaU2fO/myivH736zcx535PGJzQGanvcSGVOqX6yuLZsN3ygcQ35UgQ=="
},
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",

View File

@ -22,6 +22,7 @@
"dependencies": {
"@microsoft/signalr": "^5.0.11",
"bootstrap": "^5.1.3",
"chart.js": "^3.6.0",
"vue": "^3.2.21"
},
"devDependencies": {

View File

@ -0,0 +1,31 @@
const keyStrings: string[] = [
"C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#",
"A",
"A#",
"B",
];
export function KeyString(keyCode: number): string
{
return keyStrings[keyCode];
}
export function ModeString(modeCode: number): string
{
if(modeCode === 1)
{
return "Major";
}
else {
return "Minor";
}
}

View File

@ -1,4 +1,8 @@
import * as Vue from "vue";
import { KeyString, ModeString } from "../Helper";
import { Chart, RadarController, RadialLinearScale, PointElement, LineElement } from "chart.js";
Chart.register(RadarController, RadialLinearScale, PointElement, LineElement);
export let PopularityCard: Vue.Component = {
props: ['track'],
@ -17,9 +21,108 @@ export let SpotifyLogoLink: Vue.Component = {
template:
`
<a :href="link" target="_blank" class="spotify-logo" v-if="link != null && link != undefined">
<img src="/Spotify_Icon_RGB_White.png">
<img src="/Spotify_Icon_RGB_White.png" >
</a>
<img src="/Spotify_Icon_RGB_White.png" v-else>
<img src="/Spotify_Icon_RGB_White.png" class="spotify-logo" v-else>
`
}
export let AudioFeatureCard: Vue.Component = {
props: ['feature'],
computed: {
Key(): string
{
return KeyString(this.feature.key);
},
Mode(): string
{
return ModeString(this.feature.mode);
}
},
template:
`
<div class="card info-card">
<h3>Info</h3>
<h5>Key: <b>{{ Key }} {{ Mode }}</b></h5>
<h5>Tempo: <b>{{ feature.tempo }} BPM</b></h5>
<h5>Time: <b>{{ feature.timeSignature }}/4</b></h5>
<h5>Loudness: <b>{{ feature.loudness }} dB</b></h5>
<spotify-logo />
</div>
`
}
export let AudioFeatureChartCard: Vue.Component = {
props: ['feature'],
data() {
return {
chartData: {
labels: [
'Energy',
'Dance',
'Speech',
'Live',
'Instrumental',
'Acoustic',
'Valence'
],
datasets: [{
// label: '# of Votes',
data: [
this.feature.energy,
this.feature.danceability,
this.feature.speechiness,
this.feature.liveness,
this.feature.instrumentalness,
this.feature.acousticness,
this.feature.valence
],
}]
}
}
},
template:
`
<div class="card info-card">
<canvas id="feature-chart"></canvas>
<spotify-logo />
</div>
`,
mounted() {
new Chart("feature-chart", {
type: "radar",
data: this.chartData,
options: {
// plugins: {
// legend: {
// labels: {
// color: "white"
// }
// }
// },
elements: {
line: {
borderWidth: 4
},
point: {
radius: 4
}
},
scales: {
r: {
angleLines: {
display: false
},
beginAtZero: true,
suggestedMin: 0,
suggestedMax: 1,
ticks: {
display: false
}
}
}
}
})
}
}

View File

@ -2,7 +2,7 @@ import * as signalR from "@microsoft/signalr";
import * as Vue from "vue";
import { TrackAudioFeatures, CurrentlyPlayingDTO } from "./HubInterfaces";
import NowPlayingCard from "./Now/NowPlayingCard";
import { PopularityCard, SpotifyLogoLink } from "./Now/Spotify";
import { AudioFeatureCard, AudioFeatureChartCard, PopularityCard, SpotifyLogoLink } from "./Now/Spotify";
import BaseInfoCard from "./Now/BaseInfoCard";
const connection = new signalR.HubConnectionBuilder()
@ -38,6 +38,7 @@ const app = Vue.createApp({
{
console.log(context);
this.currentlyPlaying = context;
this.trackFeatures = null;
this.cards = [];
if(context.track !== null && context.track !== undefined)
@ -55,6 +56,8 @@ const app = Vue.createApp({
});
app.component("now-playing-card", NowPlayingCard);
app.component("audio-feature-card", AudioFeatureCard);
app.component("audio-feature-chart-card", AudioFeatureChartCard);
app.component("info-card", BaseInfoCard);
app.component("popularity", PopularityCard);
app.component("spotify-logo", SpotifyLogoLink);

View File

@ -10,15 +10,6 @@
"allowJs": true,
"moduleResolution": "node",
"outDir": "./wwwroot/js",
"baseUrl": ".",
"rootDir": "./scripts",
"paths": {
"@/*": [
"./scripts/*"
]
}
},
"include": [
"scripts/**/*"
]
"baseUrl": "."
}
}