Sångare-förslag + schema/antal/källor per typ + YT Music-skrivfix
Some checks failed
build-and-push / build (push) Failing after 12s

- Ny batch-typ 'singers': baseras på de 10 senaste unika sångarna ur
  gillat (YT Music) + spelat (Jellyfin); Gemini ger per sångare 5
  topplåtar + 3 relaterade artister, filtrerade mot Lidarr-biblioteket
  och gillalistan; klick på kortet öppnar modal med YT-länkar och
  Hämta-knappar
- Förslags-scheman per typ: intervall med enhet (timmar/dagar/månader),
  antal förslag (1-25) och källo-toggles; gamla intervalHours/
  trackIntervalDays migreras automatiskt; schemaläggaren kör alla tre
- Inställningar: Förslag-kortet omgjort till hopfällbara sektioner per
  typ (details/summary) så det inte blir överväldigande
- ytmusic: tydligt fel om oauth.json inte kan skrivas (root-ägd mapp
  var orsaken till 'inte kopplad än' — ägarskap fixat på Pi5)
- 28 tester gröna

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01824ZrvG2mDYYrmwqypLNup
This commit is contained in:
2026-08-01 02:19:58 +02:00
parent 6ff8e4d7ca
commit ac93b25827
12 changed files with 714 additions and 228 deletions

View File

@@ -1,10 +1,12 @@
<script setup>
import { onMounted, ref } from 'vue'
import MusicCard from '../components/MusicCard.vue'
import SingerModal from '../components/SingerModal.vue'
const batches = ref([])
const selected = ref(null)
const loading = ref(false)
const openSinger = ref(null)
async function load() {
const res = await fetch('/api/suggest/batches')
@@ -42,7 +44,7 @@ onMounted(load)
@click="pick(b.id)"
>
<strong>#{{ b.id }}</strong> {{ fmt(b.createdAt) }}
<span class="dim">{{ b.type === 'tracks' ? 'låtar' : 'artister/album' }} · {{ b.count }} förslag · {{ b.trigger }}</span>
<span class="dim">{{ { tracks: 'låtar', singers: 'sångare' }[b.type] ?? 'artister/album' }} · {{ b.count }} förslag · {{ b.trigger }}</span>
</button>
</aside>
@@ -50,16 +52,20 @@ onMounted(load)
<template v-if="selected">
<p class="dim">
Batch #{{ selected.id }} ({{ fmt(selected.createdAt) }}) signaler:
{{ selected.signalCounts.bibliotek }} i biblioteket,
{{ selected.signalCounts.spelat }} nyligen spelade,
{{ selected.signalCounts.tillagt }} nyligen tillagda YT Music
{{ Object.entries(selected.signalCounts ?? {}).map(([k, v]) => `${k}: ${v}`).join(', ') }}
</p>
<div class="grid">
<MusicCard v-for="(r, i) in selected.items" :key="`${selected.id}-${r.mbid ?? i}`" :item="r" />
<MusicCard
v-for="(r, i) in selected.items"
:key="`${selected.id}-${r.mbid ?? i}`"
:item="r"
@detail="openSinger = $event"
/>
</div>
</template>
<p v-else-if="loading" class="dim">Laddar</p>
</div>
<SingerModal v-if="openSinger" :singer="openSinger" @close="openSinger = null" />
</section>
</template>