ui tweaks, scrobble matcher trailing dupes
This commit is contained in:
parent
63df0ab0c2
commit
079e126648
@ -1,13 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Selector.Model.Extensions
|
namespace Selector.Model.Extensions
|
||||||
{
|
{
|
||||||
public static class ScrobbleRepositoryExtensions
|
public static class ScrobbleRepositoryExtensions
|
||||||
{
|
{
|
||||||
|
public static int CountToday(this IScrobbleRepository repo, string userId = null, string username = null, string track = null, string album = null, string artist = null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(userId))
|
||||||
|
{
|
||||||
|
return repo.Count(userId: userId, from: DateTime.Now.ToUniversalTime().Date,
|
||||||
|
artistName: artist,
|
||||||
|
albumName: album,
|
||||||
|
trackName: track);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(username))
|
||||||
|
{
|
||||||
|
return repo.Count(username: username, from: DateTime.Now.ToUniversalTime().Date,
|
||||||
|
artistName: artist,
|
||||||
|
albumName: album,
|
||||||
|
trackName: track);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("user");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,3 +83,29 @@ input[type=text], input[type=email], input[type=tel], input[type=password] {
|
|||||||
color: $text-color;
|
color: $text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #333232;
|
||||||
|
color: $text-color;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 4px 4px 2px $shadow-color;
|
||||||
|
transition: box-shadow 0.5s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
offset: 4px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.daily-scrobbles-card {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 250px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 10px;
|
||||||
|
font-size: 45px;
|
||||||
|
}
|
||||||
|
}
|
@ -5,20 +5,6 @@
|
|||||||
$text-color: white;
|
$text-color: white;
|
||||||
$shadow-color: #1e1e1e;
|
$shadow-color: #1e1e1e;
|
||||||
|
|
||||||
.card {
|
|
||||||
background-color: #333232;
|
|
||||||
color: $text-color;
|
|
||||||
margin: 5px;
|
|
||||||
padding: 15px;
|
|
||||||
box-shadow: 4px 4px 2px $shadow-color;
|
|
||||||
transition: box-shadow 0.5s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
offset: 4px 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.now-playing-card {
|
.now-playing-card {
|
||||||
// max-width: 300px;
|
// max-width: 300px;
|
||||||
|
|
||||||
|
@ -1,13 +1,23 @@
|
|||||||
@page
|
@page
|
||||||
@model IndexModel
|
@model IndexModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Home page";
|
ViewData["Title"] = "Selector";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">Welcome</h1>
|
<h1 class="display-4">run that</h1>
|
||||||
|
|
||||||
<p>Selector is a tool for monitoring Spotify usage.</p>
|
<p>Selector is a live dashboard that presents Spotify data with Last.fm listening stats.</p>
|
||||||
|
|
||||||
<a href="/now" class="dash-underline-lg link-dark" style="">Now Playing</a>
|
<a href="/now" class="dash-underline-lg link-dark" style="">Now Playing</a>
|
||||||
|
|
||||||
|
@if(Model.DailyScrobbles is not null)
|
||||||
|
{
|
||||||
|
<div class="card daily-scrobbles-card">
|
||||||
|
<h3>Today</h3>
|
||||||
|
<p style="margin: 30px">@Model.DailyScrobbles</p>
|
||||||
|
<img src="/last-fm.png" class="lastfm-logo" v-else>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
@ -6,6 +6,9 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Selector.Model;
|
||||||
|
using Selector.Model.Extensions;
|
||||||
|
|
||||||
namespace Selector.Web.Pages
|
namespace Selector.Web.Pages
|
||||||
{
|
{
|
||||||
@ -13,15 +16,29 @@ namespace Selector.Web.Pages
|
|||||||
public class IndexModel : PageModel
|
public class IndexModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly ILogger<IndexModel> _logger;
|
private readonly ILogger<IndexModel> _logger;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly IScrobbleRepository _scrobbleRepo;
|
||||||
|
|
||||||
public IndexModel(ILogger<IndexModel> logger)
|
public IndexModel(ILogger<IndexModel> logger, UserManager<ApplicationUser> userManager, IScrobbleRepository scrobbleRepo)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_userManager = userManager;
|
||||||
|
_scrobbleRepo = scrobbleRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public int? DailyScrobbles { get; set; }
|
||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{
|
{
|
||||||
|
if(User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
var user = _userManager.GetUserAsync(User).Result;
|
||||||
|
if(user.ScrobbleSavingEnabled())
|
||||||
|
{
|
||||||
|
DailyScrobbles = _scrobbleRepo.CountToday(userId: user.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@page
|
@page
|
||||||
@model NowModel
|
@model NowModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Now";
|
ViewData["Title"] = "Now - Selector";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Selector</title>
|
<title>@ViewData["Title"]</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
<link href="~/css/index.min.css" rel="stylesheet">
|
<link href="~/css/index.min.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
@ -33,13 +33,15 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
© 2021 - Selector.Web - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
© 2021 - Selector.Web - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
||||||
</div>
|
</div>
|
||||||
<img src="/andy.png"
|
<a href="https://sarsoo.xyz/selector/">
|
||||||
alt="AP"
|
<img src="/andy.png"
|
||||||
width="120px"
|
alt="AP"
|
||||||
style="display: block;
|
width="120px"
|
||||||
margin-left: auto;
|
style="display: block;
|
||||||
margin-right: auto;
|
margin-left: auto;
|
||||||
padding: 8px">
|
margin-right: auto;
|
||||||
|
padding: 8px">
|
||||||
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
|
@ -7,7 +7,7 @@ Chart.register(LineController, CategoryScale, LinearScale, TimeSeriesScale, Poin
|
|||||||
const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
||||||
|
|
||||||
export let PlayCountChartCard: Vue.Component = {
|
export let PlayCountChartCard: Vue.Component = {
|
||||||
props: ['data_points', 'title', 'chart_id'],
|
props: ['data_points', 'title', 'chart_id', 'link'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chartData: {
|
chartData: {
|
||||||
@ -33,6 +33,7 @@ export let PlayCountChartCard: Vue.Component = {
|
|||||||
<div class="card info-card chart-card">
|
<div class="card info-card chart-card">
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<canvas :id="chartId"></canvas>
|
<canvas :id="chartId"></canvas>
|
||||||
|
<lastfm-logo :link="link" />
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -56,6 +57,9 @@ export let PlayCountChartCard: Vue.Component = {
|
|||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
|
yAxis: {
|
||||||
|
suggestedMin: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -55,13 +55,13 @@ const app = Vue.createApp({
|
|||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
showArtistChart(){
|
showArtistChart(){
|
||||||
return this.playCount !== null && this.playCount !== undefined && this.playCount.artistCountData.length > 0;
|
return this.playCount !== null && this.playCount !== undefined && this.playCount.artistCountData.length > 3;
|
||||||
},
|
},
|
||||||
showAlbumChart() {
|
showAlbumChart() {
|
||||||
return this.playCount !== null && this.playCount !== undefined && this.playCount.albumCountData.length > 0;
|
return this.playCount !== null && this.playCount !== undefined && this.playCount.albumCountData.length > 3;
|
||||||
},
|
},
|
||||||
showTrackChart(){
|
showTrackChart(){
|
||||||
return this.playCount !== null && this.playCount !== undefined && this.playCount.trackCountData.length > 0;
|
return this.playCount !== null && this.playCount !== undefined && this.playCount.trackCountData.length > 3;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -23,27 +23,36 @@ namespace Selector
|
|||||||
var toAdd = new List<Scrobble>();
|
var toAdd = new List<Scrobble>();
|
||||||
var toRemove = new List<Scrobble>();
|
var toRemove = new List<Scrobble>();
|
||||||
|
|
||||||
|
var toApplyOverrun = false;
|
||||||
|
|
||||||
if (toApplyIter.MoveNext())
|
if (toApplyIter.MoveNext())
|
||||||
{
|
{
|
||||||
if (existing.Any())
|
if (existing.Any())
|
||||||
{
|
{
|
||||||
foreach (var currentExisting in existing)
|
foreach (var currentExisting in existing)
|
||||||
{
|
{
|
||||||
while (toApplyIter.Current.Timestamp < currentExisting.Timestamp)
|
if (!toApplyOverrun)
|
||||||
{
|
{
|
||||||
toAdd.Add(toApplyIter.Current);
|
while (toApplyIter.Current.Timestamp < currentExisting.Timestamp)
|
||||||
|
|
||||||
toApplyIter.MoveNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MatchTime(currentExisting, toApplyIter.Current))
|
|
||||||
{
|
|
||||||
if (matchContents)
|
|
||||||
{
|
{
|
||||||
MatchData(currentExisting, toApplyIter.Current);
|
toAdd.Add(toApplyIter.Current);
|
||||||
|
|
||||||
|
toApplyIter.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
toApplyIter.MoveNext();
|
if (MatchTime(currentExisting, toApplyIter.Current))
|
||||||
|
{
|
||||||
|
if (matchContents)
|
||||||
|
{
|
||||||
|
MatchData(currentExisting, toApplyIter.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
toApplyOverrun = !toApplyIter.MoveNext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toRemove.Add(currentExisting);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -51,7 +60,7 @@ namespace Selector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toApplyIter.Current is not null)
|
if (toApplyIter.Current is not null && !toApplyOverrun)
|
||||||
{
|
{
|
||||||
toAdd.Add(toApplyIter.Current);
|
toAdd.Add(toApplyIter.Current);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user