Add embedded player

This commit is contained in:
Cadence Ember 2025-04-06 23:16:33 +12:00
parent 70ce8ab72b
commit aa1095eef2
11 changed files with 160 additions and 110 deletions

View file

@ -8,7 +8,7 @@ block view
.d-grid.gx8.gy12.jc-center.break-word(style="grid-template-columns: repeat(auto-fit, 210px)")
each item in items
div
a.album-grid-link(href=item.item_url target="_blank")
a.s-link.album-grid-link(href=`/api/play/${item.item_type}/${item.item_id}` hx-target="#player" hx-select="#player" hx-indicator="null" hx-push-url="false")
img(loading="lazy" src=item.item_art_url width=210 height=210)
p.fs-body3.mb8= item.item_title
.d-flex.fw-wrap.g4

View file

@ -4,6 +4,7 @@ html
meta(charset="utf-8")
<meta name="viewport" content="width=device-width, initial-scale=1" />
title BC Explorer
link(rel="icon" href="/favicon.png")
link(rel="stylesheet" type="text/css" href="/static/stacks.min.css")
script(src="/static/htmx.js")
meta(name="htmx-config" content='{"requestClass":"is-loading"}')

View file

@ -22,6 +22,7 @@ html
link(rel="stylesheet" type="text/css" href="/static/stacks.min.css")
script(src="/static/htmx.js")
script(src="/static/wordcloud.js")
script(defer src="/static/player-marker.js")
meta(name="htmx-config" content='{"requestClass":"is-loading"}')
style.
.themed {
@ -69,108 +70,112 @@ html
svg {
flex-shrink: 0;
}
body.themed.theme-system.overflow-y-scroll(hx-boost="true")
header.s-topbar.ps-sticky.t0
.s-topbar--container.wmx9
.s-topbar--logo
!= icons.get("compass-solid", 24)
.ml4 BC Explorer
.fl-grow1
nav
ul.s-navigation
li: +navi("arrange", "album", "album", "Album")
li: +navi("arrange", "artist", "people-tag", "Artist")
li: +navi("arrange", "label", "flower", "Label")
//- asana, flower, component, circle-spark, rhombus, sphere, union-alt, color-wheel, community, combine
li: +navi("arrange", "tag", "label", "Tag")
li: +navi("arrange", "track", "music-note", "Track")
.px16
nav
ul.s-navigation.s-navigation__toggle.g0
li: +navi("shape", "grid").brr0!= icons.get("view-grid")
li: +navi("shape", "list").blr0!= icons.get("align-justify")
.fl-grow1
#player(hx-preserve)
button.s-btn.s-btn__outlined.s-btn__xs!= icons.get("play")
body.themed.theme-system.overflow-y-scroll(hx-boost="true" hx-swap="outerHTML" hx-target="#page" hx-select="#page")
#page
header.s-topbar.ps-sticky.t0
.s-topbar--container.wmx9
.s-topbar--logo
!= icons.get("compass-solid", 24)
.ml4 BC Explorer
.fl-grow1
nav
ul.s-navigation
li: +navi("arrange", "album", "album", "Album")
li: +navi("arrange", "artist", "people-tag", "Artist")
li: +navi("arrange", "label", "flower", "Label")
//- asana, flower, component, circle-spark, rhombus, sphere, union-alt, color-wheel, community, combine
li: +navi("arrange", "tag", "label", "Tag")
li: +navi("arrange", "track", "music-note", "Track")
.px16
nav
ul.s-navigation.s-navigation__toggle.g0
li: +navi("shape", "grid").brr0!= icons.get("view-grid")
li: +navi("shape", "list").blr0!= icons.get("align-justify")
.fl-grow1
.d-flex.py24.px16.g24.fs-body1
.fl-grow1
block view
.d-flex.py24.px16.g24.fs-body1
main.fl-grow1
block view
div
.ps-sticky.d-flex.fd-column.g12.wmx4(style="top: 80px")
if arrange === "tag"
include tag-status.pug
aside.ws3
.ps-sticky.d-flex.fd-column.g12(style="top: 80px")
if arrange === "tag"
include tag-status.pug
if filter && filter_field
.s-sidebarwidget.s-sidebarwidget__blue.d-flex.ai-center.gx16.jc-space-between.p8.pl16
!= icons.get("search")
.fl-grow1 Searching for #[strong= searchText]
a.s-btn.s-notice--btn(href=and({filter: null, filter_field: null, filter_fuzzy: null})) Clear
else
form.d-flex.ai-stretch.gx8.jc-space-between.baw0(hx-indicator="#search-submit")
input.s-input(name="filter" placeholder="Search" autocomplete="off").fl-grow1
input(type="hidden" name="filter_field" value=
( arrange === "artist" ? "band_name"
: arrange === "label" ? "band_url"
: arrange === "tag" ? "tag"
: "item_title"))
input(type="hidden" name="filter_fuzzy" value="true")
input(type="hidden" name="arrange" value=arrange)
input(type="hidden" name="shape" value=shape)
button.s-btn.s-btn__xs.s-btn__icon.s-btn__outlined.s-btn__muted#search-submit(style="height: 38px")!= icons.get("search")
if filter && filter_field
.s-sidebarwidget.s-sidebarwidget__blue.d-flex.ai-center.gx16.jc-space-between.p8.pl16
!= icons.get("search")
.fl-grow1 Searching for #[strong= searchText]
a.s-btn.s-notice--btn(href=and({filter: null, filter_field: null, filter_fuzzy: null})) Clear
else
form.d-flex.ai-stretch.gx8.jc-space-between.baw0(hx-indicator="#search-submit")
input.s-input(name="filter" placeholder="Search" autocomplete="off").fl-grow1
input(type="hidden" name="filter_field" value=
( arrange === "artist" ? "band_name"
: arrange === "label" ? "band_url"
: arrange === "tag" ? "tag"
: "item_title"))
input(type="hidden" name="filter_fuzzy" value="true")
input(type="hidden" name="arrange" value=arrange)
input(type="hidden" name="shape" value=shape)
button.s-btn.s-btn__xs.s-btn__icon.s-btn__outlined.s-btn__muted#search-submit(style="height: 38px")!= icons.get("search")
.s-sidebarwidget.wmn3
.s-sidebarwidget--header Collection
table.s-sidebarwidget--content.s-sidebarwidget__items
tr.s-sidebarwidget--item
th items
td= count.total
tr.s-sidebarwidget--item
th runtime
td= count.runtime
tr.s-sidebarwidget--item
th format
td
= count.albums
span.fc-black-400= ` albums`
span.fc-black-250= ` / `
= count.singles
span.fc-black-400= ` singles`
tr.s-sidebarwidget--item
th price
td
= count.free
span.fc-black-400= ` free`
span.fc-black-250= ` / `
= count.paid
span.fc-black-400= ` paid`
tr.s-sidebarwidget--item
th tracks
td
= count.tracks
span.pl8.fc-black-250= ` / `
span.fc-black-400 avg #{count.avgTracks}
tr.s-sidebarwidget--item
th tags
td
= count.tags
span.pl8.fc-black-250= ` / `
span.fc-black-400 avg #{count.avgTags}
span.fc-black-250= ` / `
span.fc-black-400 lonely #{count.lonelyTags}
tr.s-sidebarwidget--item
th value
td
= `${count.displayCurrencySymbol}${count.value} `
span.fc-black-400 #{count.displayCurrency}
tr.s-sidebarwidget--item
th diversity
//- supernova red-500, warm yellow-500, hot orange-500
//- 0-9 black, 10-99 yellow, 100-999 orange, 1000+ red
td.w100
.s-progress.d-grid.g2.h4.mtn6(style=`grid-template-columns: ${count.taste.map(t => t + "fr").join(" ")}`).bg-white.fc-black-400.fs-fine.lh-xxl
.s-progress--bar.bg-black-400(title=`${count.taste[0]} labels with <20 fans`)= count.taste[0]
.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]
#player-marker.pe-none.myn6
.s-sidebarwidget
.s-sidebarwidget--header Collection
table.s-sidebarwidget--content.s-sidebarwidget__items
tr.s-sidebarwidget--item
th items
td= count.total
tr.s-sidebarwidget--item
th runtime
td= count.runtime
tr.s-sidebarwidget--item
th format
td
= count.albums
span.fc-black-400= ` albums`
span.fc-black-250= ` / `
= count.singles
span.fc-black-400= ` singles`
tr.s-sidebarwidget--item
th price
td
= count.free
span.fc-black-400= ` free`
span.fc-black-250= ` / `
= count.paid
span.fc-black-400= ` paid`
tr.s-sidebarwidget--item
th tracks
td
= count.tracks
span.pl8.fc-black-250= ` / `
span.fc-black-400 avg #{count.avgTracks}
tr.s-sidebarwidget--item
th tags
td
= count.tags
span.pl8.fc-black-250= ` / `
span.fc-black-400 avg #{count.avgTags}
span.fc-black-250= ` / `
span.fc-black-400 lonely #{count.lonelyTags}
tr.s-sidebarwidget--item
th value
td
= `${count.displayCurrencySymbol}${count.value} `
span.fc-black-400 #{count.displayCurrency}
tr.s-sidebarwidget--item
th diversity
//- supernova red-500, warm yellow-500, hot orange-500
//- 0-9 black, 10-99 yellow, 100-999 orange, 1000+ red
td.w100
.s-progress.d-grid.g2.h4.mtn6(style=`grid-template-columns: ${count.taste.map(t => t + "fr").join(" ")}`).bg-white.fc-black-400.fs-fine.lh-xxl
.s-progress--bar.bg-black-400(title=`${count.taste[0]} labels with <20 fans`)= count.taste[0]
.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]
#player-container.ps-fixed.r16.ws3(hx-preserve="true")
#player

View file

@ -1,14 +1,14 @@
if downloader.total > 0 || downloader.outcome
#tag-download
if !downloader.running && !downloader.outcome
form.s-notice.s-notice__info.d-flex.ai-center.p8.gx16.pl16(role="status" hx-swap="outerHTML" hx-target="#tag-download" hx-post="/api/tag-download")
form.s-notice.s-notice__info.d-flex.ai-center.p8.gx16.pl16(role="status" hx-target="#tag-download" hx-post="/api/tag-download")
!= icons.get("info-circle")
div Tag data needs to be downloaded. This will take a while.
input(type="hidden" name="account" value=account)
button.s-btn.s-btn__outlined Download now
else if !downloader.outcome
.s-notice.p16(role="status" hx-swap="outerHTML" hx-target="#tag-download" hx-get=`/api/tag-download?account=${account}` hx-trigger="every 5s" hx-indicator="null")
.s-notice.p16(role="status" hx-target="#tag-download" hx-get=`/api/tag-download?account=${account}` hx-trigger="every 5s" hx-indicator="null")
.d-flex.gx16.ai-center
!= icons.get("cloud-download")
.fl-grow1 Downloading tags...
@ -23,7 +23,7 @@ if downloader.total > 0 || downloader.outcome
span= downloader.total
else if downloader.outcome === "Success"
.s-notice.s-notice__success.p8.gx16.pl16.d-flex.ai-center.wmn3
.s-notice.s-notice__success.p8.gx16.pl16.d-flex.ai-center
title#title(hx-swap-oob="true") * Tags downloaded! | BC Explorer
!= icons.get("cloud-check")
.fl-grow1 Tags downloaded.
@ -31,7 +31,7 @@ if downloader.total > 0 || downloader.outcome
a.s-btn.s-btn__outlined(href="") Refresh
else
.s-notice.s-notice__danger.p8.gx16.pl16.d-flex.ai-center.wmn3
.s-notice.s-notice__danger.p8.gx16.pl16.d-flex.ai-center
title#title(hx-swap-oob="true") * Tag download failed! | BC Explorer
!= icons.get("cloud-xmark")
.fl-grow1= downloader.outcome

5
pug/player.pug Normal file
View file

@ -0,0 +1,5 @@
#player
.s-sidebarwidget(style="overflow: hidden")
div(style="margin: -1px; margin-bottom: -11px").ps-relative
a.ps-absolute.bg-white.bar0.t0.r0.s-btn.s-btn__icon.s-btn__muted.s-btn__sm.px16(href=`/api/play/${item_type}/${item_id}` hx-target="#player" hx-select="#player" hx-push-url="false").fc-theme-primary!= icons.get("refresh-double")
iframe(style="border: 0; width: 100%; height: 424px;" src=`https://bandcamp.com/EmbeddedPlayer/${item_type}=${item_id}/size=large/bgcol=ffffff/linkcol=63b2cc/artwork=none/transparent=true/`)