Initial commit

This commit is contained in:
Cadence Ember 2025-03-31 17:51:14 +13:00
parent 5a8186a46c
commit 15ff7e5b47
25 changed files with 2703 additions and 0 deletions

30
scripts/load-tags.js Normal file
View file

@ -0,0 +1,30 @@
// @ts-check
const domino = require("domino")
const sqlite = require("better-sqlite3")
const db = new sqlite("bc-explorer.db", {fileMustExist: true})
require("../db/migrate").migrate(db)
Object.assign(require("../passthrough"), {db})
const {from} = require("../db/orm")
const i = db.prepare("INSERT OR IGNORE INTO item_tag (item_id, tag) VALUES (?, ?)")
;(async () => {
const untaggedItems = from("item").select("item_id", "item_title", "item_url").join("item_tag", "item_id", "left").and("WHERE tag IS NULL").all()
console.log(`Downloading tags for ${untaggedItems.length} purchased items`)
let processed = 1
for (const {item_id, item_title, item_url} of untaggedItems) {
const html = await fetch(item_url).then(res => res.text())
const doc = domino.createDocument(html)
// @ts-ignore
const tags = [...doc.querySelectorAll(".tag").cache].map(e => e.textContent)
console.log(`[${processed}/${untaggedItems.length}] tagging ${item_title} with ${tags.join(", ")}`)
db.transaction(() => {
for (const tag of tags) {
i.run(item_id, tag)
}
})()
processed++
}
})()

View file

@ -0,0 +1,72 @@
// @ts-check
const assert = require("assert/strict")
const fs = require("fs")
const sqlite = require("better-sqlite3")
const har = JSON.parse(fs.readFileSync("scripts/account.har", "utf8"))
;(async () => {
const collection_summary = har.log.entries.find(e => e.request.url === "https://bandcamp.com/api/fan/2/collection_summary")
assert(collection_summary)
const body = JSON.parse(collection_summary.response.content.text)
const {fan_id} = body
const count = Object.keys(body.collection_summary.tralbum_lookup).length
const newestPurchase = Object.values(body.collection_summary.tralbum_lookup).sort((a, b) => new Date(b.purchased).getTime() - new Date(a.purchased).getTime())[0]
const fakeLastToken = `${Math.floor(new Date(newestPurchase.purchased).getTime() / 1000 + 1)}:${newestPurchase.item_id}:${newestPurchase.item_type}::`
const Cookie = collection_summary.request.headers.find(h => h.name === "Cookie").value
assert(Cookie)
const items = await fetch("https://bandcamp.com/api/fancollection/1/collection_items", {
method: "POST",
headers: {
Cookie
},
body: JSON.stringify({
fan_id,
older_than_token: fakeLastToken,
count
})
}).then(res => res.json())
console.log(`Found ${items.items.length} purchased items in your account`)
const db = new sqlite("bc-explorer.db")
require("../db/migrate").migrate(db)
const columns = ["item_id", "item_type", "band_id", "added", "updated", "purchased", "featured_track", "why", "also_collected_count", "item_title", "item_url", "item_art_url", "band_name", "band_url", "featured_track_title", "featured_track_number", "featured_track_duration", "album_id", "album_title", "price", "currency", "label", "label_id"]
const upsert_columns = ["added", "updated", "purchased"]
const preparedItem = db.prepare(`INSERT OR IGNORE INTO item (${columns.join(", ")}) VALUES (${columns.map(x => "@" + x).join(", ")}) ON CONFLICT DO UPDATE SET ${upsert_columns.map(x => `${x} = @${x}`).join(", ")}`)
db.transaction(() => {
for (const item of items.items) {
preparedItem.run({
...item,
purchased: new Date(item.purchased).getTime(),
added: new Date(item.added).getTime(),
updated: new Date(item.updated).getTime()
})
}
})()
const storedItemCount = db.prepare("SELECT count(*) AS count FROM item").pluck().get()
console.log(`Stored ${storedItemCount} purchased items`)
const preparedTrack = db.prepare("INSERT OR IGNORE INTO track (item_id, track_id, title, artist, track_number, duration, mp3) VALUES (@item_id, @track_id, @title, @artist, @track_number, @duration, @mp3)")
db.transaction(() => {
for (const [key, tracklist] of Object.entries(items.tracklists)) {
assert.match(key[0], /[at]/)
for (const track of tracklist) {
preparedTrack.run({
item_id: key.slice(1),
track_id: track.id,
mp3: track.file?.["mp3-v0"],
...track
})
}
}
})()
const storedTrackCount = db.prepare("SELECT count(*) AS count FROM track").pluck().get()
console.log(`Stored ${storedTrackCount} tracks`)
console.log("To load tag data, please now run node ./scripts/load-tags.js")
})()