Fix track data import, clean up code

This commit is contained in:
Cadence Ember 2025-04-04 20:39:04 +13:00
parent cf6310b89a
commit 6fb5a943db
2 changed files with 33 additions and 24 deletions

View file

@ -16,7 +16,7 @@ but the idea is you can more easily search your whole collection and play it str
## import more reliable statistics ## import more reliable statistics
the default data is pretty close, but you can do this to get the exact data by default, the data is mostly correct, but you can do this to get the exact data
1. log into bandcamp in your browser 1. log into bandcamp in your browser
2. in the top right, click the button to view your collection, should take you to a url like https://bandcamp.com/cloudrac3r 2. in the top right, click the button to view your collection, should take you to a url like https://bandcamp.com/cloudrac3r

View file

@ -8,24 +8,33 @@ const {sync, db, from, router} = require("../passthrough")
const pugSync = sync.require("../pug-sync") const pugSync = sync.require("../pug-sync")
const insertTag = db.prepare("INSERT OR IGNORE INTO item_tag (account, item_id, tag) VALUES (?, ?, ?)") const insertTag = db.prepare("INSERT OR IGNORE INTO item_tag (account, item_id, tag) VALUES (?, ?, ?)")
const insertTrack = db.prepare("INSERT OR IGNORE INTO track (account, item_id, track_id, title, artist, track_number, duration) VALUES (?, ?, ?, ?, ?, ?, ?)") const insertTrack = db.prepare("INSERT OR IGNORE INTO track (account, item_id, track_id, title, artist, track_number, duration) VALUES (@account, @item_id, @track_id, @title, @artist, @track_number, @duration)")
class TagDownloader extends sync.reloadClassMethods(() => TagDownloader) { class TagDownloader extends sync.reloadClassMethods(() => TagDownloader) {
constructor(account) { constructor(account) {
super() super()
this.account = account this.account = account
this.processed = 0 this.processed = 0
this.untaggedItems = db.prepare("SELECT account, item_id, item_title, item_url, band_name FROM item LEFT JOIN item_tag USING (account, item_id) WHERE account = ? AND tag IS NULL").all(account) this.untaggedItems = []
this.total = this.untaggedItems.length this.total = this.untaggedItems.length
this.running = false this.running = false
this.outcome = null this.outcome = null
this.check()
}
check() {
if (this.running) return // don't reduce the total while items are being processed, this will break the progress bar
this.untaggedItems = db.prepare("SELECT account, item_id, item_title, item_url, band_name FROM item LEFT JOIN item_tag USING (account, item_id) WHERE account = ? AND tag IS NULL").all(this.account)
this.total = this.untaggedItems.length
} }
async _start() { async _start() {
if (this.running) return if (this.running) return
this.running = true this.running = true
this.outcome = null
this.processed = 0
try { try {
for (const {account, item_id, item_title, item_url, band_name} of this.untaggedItems) { for (const {account, item_id, item_type, item_title, item_url, band_name} of this.untaggedItems) {
const res = await fetch(item_url) const res = await fetch(item_url)
// delete unreachable items, otherwise it will perpetually try to download tags for them // delete unreachable items, otherwise it will perpetually try to download tags for them
@ -47,21 +56,18 @@ class TagDownloader extends sync.reloadClassMethods(() => TagDownloader) {
})() })()
// @ts-ignore // @ts-ignore
const tracks = [...doc.querySelectorAll(".track_row_view").cache] const tracklist = JSON.parse(doc.querySelector("script[data-tralbum]").getAttribute("data-tralbum")).trackinfo
db.transaction(() => { db.transaction(() => {
for (const track of tracks) { for (const track of tracklist) {
const track_number = parseInt(track.querySelector(".track_number").textContent) insertTrack.run({
let title = track.querySelector(".track-title").textContent account,
let artist = band_name item_id,
const match = title.match(/^([^-]*) - (.*)$/) track_id: track.id,
if (match) { title: track.title,
artist = match[1] artist: track.artist || band_name,
title = match[2] track_number: track.track_num,
} duration: track.duration
const duration = track.querySelector(".time").textContent.split(":").reverse().reduce((a, c, i) => 60**i * c + a, 0) })
console.log(track_number, title, artist, duration)
if (!track_number || !title || !artist || !duration) continue
insertTrack.run(account, item_id, track_number, title, artist, track_number, duration)
} }
})() })()
@ -75,6 +81,10 @@ class TagDownloader extends sync.reloadClassMethods(() => TagDownloader) {
this.running = false this.running = false
} }
} }
resolve() {
this.outcome = null
}
} }
const downloadManager = new class { const downloadManager = new class {
@ -83,14 +93,13 @@ const downloadManager = new class {
/** @param {string} account */ /** @param {string} account */
check(account) { check(account) {
return this.inProgressTagDownloads.get(account) || (() => { const downloader = this.inProgressTagDownloads.get(account) || (() => {
const downloader = new TagDownloader(account) const downloader = new TagDownloader(account)
this.inProgressTagDownloads.set(account, downloader) this.inProgressTagDownloads.set(account, downloader)
setTimeout(() => {
this.resolve(account)
})
return downloader return downloader
})() })()
downloader.check()
return downloader
} }
/** @param {string} account */ /** @param {string} account */
@ -116,13 +125,13 @@ const schema = z.object({
router.get("/api/tag-download", defineEventHandler(async event => { router.get("/api/tag-download", defineEventHandler(async event => {
const {account} = await getValidatedQuery(event, schema.parse) const {account} = await getValidatedQuery(event, schema.parse)
const downloader = downloadManager.check(account) const downloader = downloadManager.check(account)
return pugSync.render(event, "includes/tag-status.pug", {downloadManager, downloader, account}) return pugSync.render(event, "includes/tag-status.pug", {downloader, account})
})) }))
router.post("/api/tag-download", defineEventHandler(async event => { router.post("/api/tag-download", defineEventHandler(async event => {
const {account} = await readValidatedBody(event, schema.parse) const {account} = await readValidatedBody(event, schema.parse)
const downloader = downloadManager.start(account) const downloader = downloadManager.start(account)
return pugSync.render(event, "includes/tag-status.pug", {downloadManager, downloader, account}) return pugSync.render(event, "includes/tag-status.pug", {downloader, account})
})) }))
module.exports.downloadManager = downloadManager module.exports.downloadManager = downloadManager