audio feature cards, added chart.js
This commit is contained in:
parent
bfb2a5d2cd
commit
4405cf885a
@ -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,
|
||||
|
@ -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")}");
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
||||
|
11
Selector.Web/package-lock.json
generated
11
Selector.Web/package-lock.json
generated
@ -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",
|
||||
|
@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "^5.0.11",
|
||||
"bootstrap": "^5.1.3",
|
||||
"chart.js": "^3.6.0",
|
||||
"vue": "^3.2.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
31
Selector.Web/scripts/Helper.ts
Normal file
31
Selector.Web/scripts/Helper.ts
Normal 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";
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -10,15 +10,6 @@
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./wwwroot/js",
|
||||
"baseUrl": ".",
|
||||
"rootDir": "./scripts",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./scripts/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"scripts/**/*"
|
||||
]
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user