Compare commits

..

No commits in common. "8dc24a77637ffd56924d75e8189e963bcaa016d1" and "199a44a8a7102fdd6ca912d7c2f10f834b61cefb" have entirely different histories.

8 changed files with 11 additions and 102 deletions

View file

@ -24,9 +24,6 @@
.s-navigation--item.is-loading svg, .s-tag.is-loading svg, .s-sidebarwidget.is-loading svg {
visibility: hidden;
}
.s-btn.is-loading:not(.s-btn__icon) svg {
display: none;
}
.s-btn__icon.is-loading {
--_li-offset: 0.7em;
--_il-size: 1.5em;

View file

@ -42,52 +42,26 @@ html
p.fs-fine (you #[em should] download every mp3, though, because the Bandcamp TOS says they can take away your online access at any time)
h2 Why?
p
| I mainly wanted to arrange my collection as labels instead of album covers. I've bought music from a variety of artists, but it's hard to find all of them amidst the enormous backlogs from
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?filter=Singto+Conley&filter_field=band_name&arrange=album&shape=grid")
span.s-tag--sponsor!= icons.get("people-tag", 18)
| Singto Conley
|
| and
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?filter=lostfrog&filter_field=band_url&arrange=album&shape=grid")
span.s-tag--sponsor!= icons.get("flower", 18)
| Lost Frog Productions
| . Giving each label the same amount of space makes my collection more fun to browse because I'm not scrolling past the same things over and over again.
p I mainly wanted to arrange my collection as labels instead of album covers. I've bought music from a variety of artists, but it's hard to find all of them amidst the enormous backlogs from Singto Conley and Lost Frog Productions. Giving each label the same amount of space makes my collection more fun to browse because I'm not scrolling past the same things over and over again.
p It's also faster to browse through because there's no "load more", everything shows up instantly.
p I also wanted as many different facets as possible for browsing my collection: albums, artists, labels, and tags. I want to be able to jump from one place to another. What else has this artist produced? Who has produced music in these tags? What else have I written reviews for?
p Skimming your collection is easy because there's no "load more", everything shows up instantly. The inline music player sticks around while you keep exploring.
h2 Tips and tricks
p
| All the little tags under things, like
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?arrange=album&shape=grid&filter_field=band_url&filter=overheaven")
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?account=cloudrac3r&arrange=album&shape=grid&filter_field=band_url&filter=overheaven")
span.s-tag--sponsor!= icons.get("album", 18)
| 39
|
| the number of albums, or
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?arrange=artist&shape=grid&filter_field=band_url&filter=lostfrog")
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?account=cloudrac3r&arrange=artist&shape=grid&filter_field=band_url&filter=lostfrog")
span.s-tag--sponsor!= icons.get("people-tag", 18)
| 92
|
| the artists in a label, are clickable. If you click them it applies a filter and switches tabs to show you who they are.
p
| While having a filter active, you can click the tabs at the top to use the same filter in a different facet. For example, you can go to the
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?arrange=tag&shape=grid")
span.s-tag--sponsor!= icons.get("label", 18)
| tag cloud
| , search "breakcore", click it to see all the
|
a.s-tag.s-tag__sm.bg-black-200(href="/cloudrac3r/?filter=breakcore&filter_field=tag&filter_fuzzy=true&arrange=album&shape=grid")
span.s-tag--sponsor!= icons.get("album", 18)
| breakcore
|
| albums - there's a lot, right? Yes, but it's not because I'm enthusiastic about breakcore. From that page, switch to the labels tab. Turns out most of it's just from the same label, so there's not much diversity there.
p While having a filter active, you can click the tabs at the top to use the same filter in a different facet. For example, you can go to the #[a(href="/cloudrac3r/?arrange=tag&shape=grid") tag cloud], search "breakcore", click it to #[a(href="/cloudrac3r/?filter=breakcore&filter_field=tag&filter_fuzzy=true&arrange=album&shape=grid") see all the breakcore albums] - there's a lot, right? Yes, but it's not because I'm enthusiastic about breakcore. Switch to #[a(href="/cloudrac3r/?filter=breakcore&filter_field=tag&arrange=label&shape=grid") the labels view]. Most of the breakcore is just from the same label, so there's not actually much diversity there.
p The tag cloud view takes this diversity into account automatically. Everything is grouped by label before being counted, to ensure that a large backlog from a single label does not take up an unworthy amount of "space" and crowd out the other singles that you're quite fond of. It's the same with the statistics heat bar. If you have #[a(href="http://localhost:2239/cloudrac3r/?arrange=album&shape=grid&filter_field=band_url&filter=louiezong") all of Louie Zong's albums], they'll only count for one red hot entry rather than over 150 entries.
p Don't take the statistics too seriously! Some are a bit silly, like lonely tags, which are tags that only exist on a single item in your collection. Some are best guesses, like monetary value, which doesn't know if you chose to pay more than the minimum price or paid less via a discography discount or album code. Some are just for fun, like diversity, which shows whether you prefer popular or unpopular labels. Each label is one slot in the bar and it gets a hotter colour if it's more popular. Popularity is measured by the average number of other people who bought the same items as you.
p Oh, and there's an inline music player which sticks around while you keep exploring.
footer.mt32.d-flex.fw-wrap.gx8.ai-center
a(href="https://cadence.moe") Created by Cadence

View file

@ -63,19 +63,3 @@
.s-progress--bar.bg-yellow-400(title=`${count.taste[1]} labels with 20-199 fans`)= count.taste[1]
.s-progress--bar.bg-orange-400(title=`${count.taste[2]} labels with 200-1999 fans`)= count.taste[2]
.s-progress--bar.bg-red-400(title=`${count.taste[3]} labels with >2000 fans`)= count.taste[3]
tr.s-sidebarwidget--item
th currency
td.w100
- var totalLabels = count.currencies.reduce((a, c) => a + c[1], 0)
- var palette = ["red", "orange", "green", "blue", "purple", "bronze"]
- var i = 0
.s-progress.d-grid.g2.h4.mtn6(style=`grid-template-columns: ${count.currencies.map(t => t[1] + "fr").join(" ")}`).bg-white.fc-black-400.fs-fine.lh-xxl
each c in count.currencies
.s-progress--bar.wmn-initial(title=`bought from ${c[1]} labels with ${c[0]}` class=`fc-${palette[i]}-400 bg-${palette[i++]}-400`)
- if (i >= palette.length) i = 0
if c[1] / totalLabels >= 0.08
= c[0]
else if c[1] / totalLabels >= 0.06
= c[0].slice(0, 2)
else
= c[0][0]

View file

@ -45,15 +45,6 @@ html
.ml6 Switch account
li.s-menu--divider(role="separator")
li.s-menu--title Settings
if query && query.show
li(role="menuitem")
.s-block-link.d-flex.ai-center.py2
span.fl-grow1.d-flex.ai-center
!= icons.get("eye")
.pl6 Showing all items
a.s-btn.s-btn__filled.s-btn__sm.d-flex.ai-center(href=and({show: null}))
!= icons.get("undo", 16)
span.pl6 Undo
li(role="menuitem")
if arrange === "tag"
form.s-block-link.d-flex.ai-center(hx-post="/api/settings/location-tags" hx-trigger="change" hx-indicator="#location-tags-loading" hx-select="#tag-page" hx-target="#tag-page")
@ -153,9 +144,6 @@ html
include collection-stats.pug
main.fl-grow1
if items && items.length >= 1000
//- a great deal of performance is spent on htmx's bfcache emulation when navigating away from large pages
div(hx-history="false")
block view
#player-container.ps-fixed.r16.ws340.z-modal(class="md:t64 md:l16 md:r16 md:b16" hx-preserve="true")

View file

@ -1,17 +0,0 @@
extends includes/layout.pug
block view
.mx-auto.w100.wmx11.fs-body1.d-flex.jc-center#content
.s-notice.s-notice__warning
.d-flex.gx16.ai-center
!= icons.get("warning-triangle")
div This page has #{itemCount} items, which might slow down your browser.
.d-flex.gx8.mt16
button.s-btn.s-btn__filled(hx-get=and({}) hx-headers='{"BCEX-Show": "true"}') Show them anyway
a.s-btn.s-btn__outlined(href=and({show: true})) Always show
p.mt16 Other things to try:
ul.mb4
li Search for specific items
if arrange !== "label"
li View items grouped together, like the #[a(href=and({arrange: "label"})) labels tab]
li Use #[a(href=and({shape: "list"}) hx-headers='{"BCEX-Show": "true"}') list view], which may be faster

View file

@ -1,6 +1,6 @@
// @ts-check
const {defineEventHandler, getQuery, getValidatedQuery, sendRedirect, createError, getValidatedRouterParams, getCookie, getRequestHeader} = require("h3")
const {defineEventHandler, getQuery, getValidatedQuery, sendRedirect, createError, getValidatedRouterParams, getCookie} = require("h3")
const {router, db, sync} = require("../passthrough")
/** @type {import("../pug-sync")} */
@ -73,7 +73,7 @@ router.get("/:account/", defineEventHandler({
handler: async event => {
const {account} = await getValidatedRouterParams(event, schema.schema.account.parse)
try {
var {arrange, shape, filter, filter_field, filter_fuzzy, show} = await getValidatedQuery(event, schema.schema.appQuery.parse)
var {arrange, shape, filter, filter_field, filter_fuzzy} = await getValidatedQuery(event, schema.schema.appQuery.parse)
if (filter_field === "why" && arrange !== "album") throw new Error("filter not compatible with arrangement")
} catch (e) {
return sendRedirect(event, "?arrange=album&shape=grid", 302)
@ -125,12 +125,6 @@ router.get("/:account/", defineEventHandler({
throw e
}
show ||= getRequestHeader(event, "BCEX-Show")
const itemWarningLimit = arrange === "track" ? 5000 : 1000
if (items.length >= itemWarningLimit && !show) {
return pugSync.render(event, "too-many-items.pug", {itemCount: items.length, account, query})
}
const locals = {
items,
account,

View file

@ -13,21 +13,12 @@ const currencyExchange = new Map([
["AUD", 0.63],
["BRL", 0.17],
["CAD", 0.7],
["CZK", 0.045],
["CHF", 1.13],
["DKK", 0.15],
["EUR", 1.08],
["GBP", 1.3],
["HKD", 0.13],
["HUF", 0.0028],
["ILS", 0.27],
["MXN", 0.05],
["JPY", 0.0067],
["NOK", 0.1],
["NZD", 0.57],
["PLN", 0.27],
["SEK", 0.1],
["SGD", 0.76],
["USD", 1],
])
const currencies = [...currencyExchange.keys()]
@ -41,7 +32,7 @@ pugSync.beforeInclude("includes/layout.pug", async (from, event, locals) => {
pugSync.beforeInclude("includes/collection-stats.pug", async (from, event, {account, currency}) => {
let displayCurrency = currency || getCookie(event, "bcex-currency") || ""
if (!currencyExchange.has(displayCurrency)) displayCurrency = "NZD"
const currencyRoundTo = (currencyExchange.get(displayCurrency) || 1) < 0.01 ? 1000 : 10
const currencyRoundTo = displayCurrency === "JPY" ? 1000 : 10
return {
count: {
@ -60,8 +51,7 @@ pugSync.beforeInclude("includes/collection-stats.pug", async (from, event, {acco
return (currencyExchange.get(c.currency) || 0.6) * c.price / (currencyExchange.get(displayCurrency) || 1) / currencyRoundTo
}).reduce((a, c) => a + c, 0)) * currencyRoundTo,
displayCurrency,
taste: db.prepare("with popularity (a) as (select avg(also_collected_count) from item WHERE account = ? group by band_url) select sum(iif(a >= 0 and a < 20, 1, 0)) as cold, sum(iif(a >= 20 and a < 200, 1, 0)) as warm, sum(iif(a >= 200 and a < 2000, 1, 0)) as hot, sum(iif(a >= 2000, 1, 0)) as supernova from popularity").raw().get(account),
currencies: db.prepare("select currency, count(*) as count from (select currency, band_url from item where account = ? group by band_url) group by currency order by count desc").raw().all(account)
taste: db.prepare("with popularity (a) as (select avg(also_collected_count) from item WHERE account = ? group by band_url) select sum(iif(a >= 0 and a < 20, 1, 0)) as cold, sum(iif(a >= 20 and a < 200, 1, 0)) as warm, sum(iif(a >= 200 and a < 2000, 1, 0)) as hot, sum(iif(a >= 2000, 1, 0)) as supernova from popularity").raw().get(account)
}
}
})

View file

@ -10,8 +10,7 @@ const schema = {
shape: z.enum(["grid", "list"]),
filter_field: z.enum(["band_name", "band_url", "item_title", "item_id", "tag", "why"]).optional(),
filter: z.string().regex(/^[^%]+$/).optional(),
filter_fuzzy: z.enum(["true"]).optional(),
show: z.string().optional()
filter_fuzzy: z.enum(["true"]).optional()
}),
account: z.object({
account