From 98ee68daf27b5ce380ca1e05e9141ebe056e3472 Mon Sep 17 00:00:00 2001 From: Til Schmitter Date: Mon, 11 May 2026 22:53:18 +0200 Subject: [PATCH] music widget --- contrib/plugins/rave.js | 2 +- copyparty/web/browser.css | 125 ++++++++++++++++++++++++++----------- copyparty/web/browser.html | 21 +++++++ copyparty/web/browser.js | 92 ++++++++++++++++++++------- copyparty/web/ui.css | 2 +- 5 files changed, 180 insertions(+), 62 deletions(-) diff --git a/contrib/plugins/rave.js b/contrib/plugins/rave.js index c6fed874..8e4ea94c 100644 --- a/contrib/plugins/rave.js +++ b/contrib/plugins/rave.js @@ -119,7 +119,7 @@ body.untz { if (--uofs >= 0) { document.body.style.marginLeft = hrand() * uofs + 'px'; ebi('tree').style.marginLeft = hrand() * uofs + 'px'; - for (var a of QSA('#ops>a, #path>a, #pctl>a')) + for (var a of QSA('#ops>a, #path>a, .pctl>a')) a.style.transform = 'translate(' + hrand() * uofs * 1 + 'px, ' + hrand() * uofs * 0.7 + 'px) rotate(' + Math.random() * uofs * 0.7 + 'deg)' } diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index 68e961ea..b20e3415 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -1020,6 +1020,7 @@ tr.play td:nth-child(1) a { text-shadow: none; font-family: monospace; } +.thumbed, #ggrid>a.thumbed { .thumb, .th_ext { @@ -1500,10 +1501,12 @@ html:not(.e):not(.d) #up_quick .btn.on { overflow: hidden; text-overflow: ellipsis; margin-left: .3em; + padding: 1.2em 0; } #progbar { display: none; margin: 0 .3em; + font-size: 20px; } #progbar.vis { display: block; @@ -1550,11 +1553,11 @@ html:not(.e):not(.d) #up_quick .btn.on { margin-left: .1em; padding: .1em; } -#pctl .icon { +.pctl .icon { width: 1.5em; height: 1.5em; } -#pctl .icon.btn { +.pctl .icon.btn { padding: .2em; margin: .1em; } @@ -1563,17 +1566,17 @@ html:not(.e):not(.d) #up_quick .btn.on { vertical-align: top; height: 1.5em; } -#pctl svg { +.pctl svg { width: 1.5em; height: 1.5em; } -#pctl .btn { +.pctl .btn { font-size: 1em; padding: 0; opacity: .4; cursor: pointer; } -#pctl .btn.on { +.pctl .btn.on { opacity: 1; } #pvol { @@ -1615,6 +1618,72 @@ html:not(.e):not(.d) #up_quick .btn.on { max-width: 5.8em; border-radius: 0; } + +#music { + margin: 0; + border-radius: 0; + border: none; + background: #ccc; + background: var(--bg-u2); +} +#music a { + cursor: pointer; +} +html.b #music { + background: color-mix(in oklab, var(--bg-u2) 70%, transparent); + backdrop-filter: blur(50px); +} +#h_music { + position: absolute; + top: 0; + left: 0; +} +#mu_inner { + align-content: center; + text-align: center; +} +#mu_th { + cursor: default !important; + display: flex; + position: relative; + height: 35vh; +} +#mu_th svg { + margin: 0 auto; +} +#mu_th span { + font-size: 8vh; +} +#mu_th img { + opacity: 0; + margin: 0 auto; +} +#mu_tn { + display: block; + margin-top: .5em; + min-height: 2em; + font-size: 1.3em; +} +#mu_pbc { + font-size: 1.6em; +} +#mu_pbb { + height: 3em; + margin: .5em 0; + width: 80vw; + display: block; + position: relative; + align-content: center; + text-align: left; +} +#mu_vol { + position: absolute; + right: 1em; + bottom: 1em; + font-size: 1.2em; +} + + .opview { display: none; margin-bottom: 1em; @@ -2868,15 +2937,25 @@ html.c .modalcontent { .modalheader:hover { color: var(--fg); } +#cfg_mu, .close{ position: absolute; cursor: default; top: 0; right: 0; - margin: .5em; + margin: .3em; color: var(--fg); box-shadow: 0 0 .3em var(--mp-sh); border: 1px solid var(--bg-u3); + height: 1.8em; + aspect-ratio: 1 / 1; + text-align: center; + display: block; + padding: 0; + align-content: center; +} +#cfg_mu { + right: 2.3em; } .closepane { position: absolute; @@ -4228,6 +4307,7 @@ html.e { --g-sel-fg: #fff; } html.e * { + --radius: 0; border-radius: 0 !important; } html.e #ggrid > a.sel { @@ -4257,14 +4337,13 @@ html.e body { background: var(--bg); } html.e summary, -html.e #pctl a, +html.e .pctl a, html.e #repl, html.e #wfp a, html.e .btn, html.e .eq_step, html.e input[type="submit"] { box-shadow: var(--shadow-outset); - border-radius: var(--radius); background: var(--bg); border: 0; } @@ -4280,7 +4359,7 @@ html.e input[type="submit"]:active { } html.e summary:hover, html.e #ops a:hover, -html.e #pctl a:hover, +html.e .pctl a:hover, html.e #repl:hover, html.e #wfp a:hover, html.e .btn:hover, @@ -4301,7 +4380,7 @@ html.e tr:focus { box-shadow: none; } html.e summary:focus, -html.e #pctl a:focus, +html.e .pctl a:focus, html.e #repl:hover, html.e #wfp a:focus, html.e .btn:focus, @@ -4391,11 +4470,6 @@ html.e #u2cards a.act { } html.e #u2btn { border: var(--border-dashed-black); - border-radius: var(--border-radius); -} -html.e #ops, -html.e #ops a { - border-radius: var(--radius); } @media only screen and (max-width: 600px) { html.e #acc_info { @@ -4480,21 +4554,8 @@ html.e :focus, html.e :focus + label { border: 0 !important; outline-offset: 1px; - border-radius: var(--radius) !important; box-shadow: inherit; } -html.e #opa_x { - text-shadow: 0 0 0 var(--transparent) !important; - color: var(--bg) !important; - display: flex; -} -html.e #opa_x:before { - content: "⨯"; - color: var(--fg) !important; - margin-top: -0.1em; - font-size: 1.75em; - position: absolute; -} html.e .opbox { margin: -.3em 0 0; box-shadow: var(--shadow-inset-bottom), var(--shadow-inset-left), @@ -4505,7 +4566,6 @@ html.e .opbox { } html.e #srch_form { margin: 0; - border-radius: var(--radius); } html.e #op_unpost { max-width: 100vw; @@ -4555,7 +4615,6 @@ html.e select { box-shadow: var(--shadow-input) !important; box-sizing: border-box; padding: 3px 4px; - border-radius: var(--radius); border: 0; } html.e #gfiles { @@ -4579,11 +4638,9 @@ html.e #ghead { gap: 0.4em; padding-top: .2em; top: 2.3em; - border-radius: 0px; } html.e #ghead a { margin: 0; - border-radius: var(--radius); } html.e #treeToggleBtn { margin: 0; @@ -4685,7 +4742,6 @@ html.e .ntree a:first-child { aspect-ratio: 1/1; text-align: center; align-content: center; - border-radius: var(--radius) !important; padding: 0.057em; border: 1px solid var(--black); } @@ -4722,7 +4778,6 @@ html.e #tree li { html.e .ntree a:hover { outline-offset: -2px; color: var(--fg); - border-radius: var(--radius) !important; } html.e #treepar { width: calc(-1em + var(--nav-sz) - var(--sbw)); @@ -4823,7 +4878,6 @@ html.e #wfm.act + #wzip1 + #wzip + #wnp { border-left-width: 1px; } html.e #barpos { - /* border-radius: var(--radius); */ box-shadow: var(--shadow-inset); } html.e #goh + span { @@ -4854,7 +4908,6 @@ html.e #doc { box-shadow: var(--shadow-inset); background: var(--inset-bg); margin: 0.2em; - border-radius: var(--radius); } html.e #detree { diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index 90b9b795..ee5152da 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -177,6 +177,27 @@ + + diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 04dab6d7..bea1d43e 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -964,11 +964,11 @@ ebi('widget').innerHTML = ( '
' + '
' + - '
' + + '
' + ' ' + svg_prev + '' + ' ' + svg_play + '' + ' ' + svg_next + '' + - ' ' + + ' ' + '
' + ' ' + ' ' + @@ -996,6 +996,12 @@ ebi('widget').innerHTML = ( '
' ); +ebi('mu_pbc').innerHTML = ( + '' + svg_prev + '' + + '' + svg_play + '' + + '' + svg_next + '' +); + ebi('wtoggle').addEventListener('wheel', function (e) { scroll_v_to_h(e, this); }); @@ -1547,6 +1553,8 @@ function modaltoggle(dest, show){ if (show == false || show == 't' && QS('#' + dest + '.vis')) dest = ''; + var layoutchange = dest == 'music' || QS('#music.vis'); + swrite('opmode', dest || null); goto(dest); @@ -1556,6 +1564,9 @@ function modaltoggle(dest, show){ tt.skip = true; input.focus(); } + + if(layoutchange) + onwidgetresize(); } function opclick(e) { var dest = this.getAttribute('data-dest'); @@ -1595,7 +1606,7 @@ function goto(dest) { if (dest) { var lnk = QS('#ops>a[data-dest=' + dest + ']'), - nps = lnk.getAttribute('data-perm'); + nps = lnk && lnk.getAttribute('data-perm'); nps = nps && nps.length ? nps.split(' ') : []; @@ -1673,7 +1684,7 @@ window.onhashchange = function() { return; if(!clgot(p_modal, 'vis')){ console.log('forcing modal open due to subheader hash'); - modaltoggle(p_modal.id); + modaltoggle(p_modal.id, true); } ebi(location.hash.slice(1)).scrollIntoView(); } @@ -2481,7 +2492,8 @@ var widget = (function () { r.paused = function (paused) { if (was_paused != paused) { was_paused = paused; - ebi('bplay').innerHTML = paused ? svg_play : svg_pause; + ebi('bplay').innerHTML = ebi('bplay2').innerHTML = + paused ? svg_play : svg_pause; } }; r.setvis = function () { @@ -2812,7 +2824,7 @@ var pbar = (function () { var m1 = pctx.measureText(t1), m1b = pctx.measureText(t1 + ":88"), m2 = pctx.measureText(t2), - yt = pc.h * 0.7, + yt = pc.h * 0.65, xt1 = pc.w - (m1.width + 12), xt2 = x < m1.width * 1.4 ? (x + 12) : (Math.min(pc.w - m1b.width, x - 12) - m2.width); @@ -3118,9 +3130,9 @@ function mpause(e) { // hook up the widget buttons (function () { - ebi('bplay').onclick = playpause; - ebi('bprev').onclick = prev_song; - ebi('bnext').onclick = next_song; + ebi('bplay').onclick = ebi('bplay2').onclick = playpause; + ebi('bprev').onclick = ebi('bprev2').onclick = prev_song; + ebi('bnext').onclick = ebi('bnext2').onclick = next_song; var bar = ebi('barpos'); @@ -3870,10 +3882,18 @@ function play(tid, is_ev, seek) { } mp.au.osrc = decodeURI(mp.tracks[tid].split('/').pop()); - ebi('trackname').innerHTML = esc(uricom_dec(mp.au.osrc)); - ebi('trackname').setAttribute('tt', ebi('trackname').innerHTML); + var tname = esc(uricom_dec(mp.au.osrc)); + ebi('trackname').innerHTML = tname; + ebi('trackname').setAttribute('tt', tname); afilt.apply(); + // popup player + ebi('mu_tn').innerHTML = tname.replace(/\..*$/, ''); + var m_ext = tname.match(/[^\.]*$/); + if(m_ext){ + QS('#mu_th>svg').style.color = intToHSL(hashCode(m_ext[0])) + QS('#mu_th>span').innerHTML = m_ext[0]; + } setTimeout(function () { mpl.unbuffer(url); }, 500); @@ -3895,6 +3915,15 @@ function play(tid, is_ev, seek) { clmod(t_tr, 'play', 1); clmod(ebi('wtoggle'), 'np', mpl.clip); clmod(ebi('wtoggle'), 'm3u', mpl.m3uen); + + try{ + var gridimg = QS('a[ref=' + t_tr.childNodes[1].firstChild.id + '] img') + QS('#mu_th>img').setAttribute('src', gridimg.src.replace(/th=.*(&|$)/, 'th=wf3&')); + } + catch(ex){ + console.log(ex); + } + if (thegrid) thegrid.loadsel(); @@ -6547,17 +6576,6 @@ window.thegrid = (function () { drag.initgrid(); } - function hashCode (str) { - var hash = 0; - for(var i = 0; i < str.length; i++){ - hash = str.charCodeAt(i) + 130 * ((hash << 5) - hash); - } - return hash - } - function intToHSL(i){ - return 'hsl(' + i % 360 + 'deg 100% 50%)' - } - r.bagit = function (isrc) { console.log('init image viewer'); @@ -6746,6 +6764,17 @@ function testImage(el) { tester.src=URL; } +function hashCode (str) { + var hash = 0; + for(var i = 0; i < str.length; i++){ + hash = str.charCodeAt(i) + 130 * ((hash << 5) - hash); + } + return hash +} +function intToHSL(i){ + return 'hsl(' + i % 360 + 'deg 100% 50%)' +} + function tree_scrollto(e) { ev(e); tree_scrolltoo('#treeul a.hl'); @@ -7714,9 +7743,21 @@ var filecolwidth = (function () { onresize100.add(filecolwidth, true); function onwidgetresize(){ + var mumodal = QS('#music.vis'); var widget = ebi('widget'); var bar = ebi('pctl'); var pbarthinpos = ebi('pbarthinpos'); + + if(mumodal){ + var pb_container = ebi('mu_pbb'); + pb_container.appendChild(ebi('progbar')); + pb_container.appendChild(ebi('altprogbar')); + ebi('altprogbar').maxWidth = ''; + ebi('mu_vol').appendChild(ebi('pvolbg')); + pbar.onresize(); + return; + } + var width = widget.offsetWidth; var thin = width < 800; //px @@ -7740,8 +7781,7 @@ function onwidgetresize(){ thin = true; } } - gtc = 'max-content max-content max-content ' + (thin ? '' : '20%') + ' auto max-content max-content max-content'; - if(thin && bar.children.length > gtc.split(' ').length){ + if(thin && pbarthinpos.children.length < 2){ //thin pbarthinpos.appendChild(ebi('progbar')); pbarthinpos.appendChild(ebi('altprogbar')); @@ -7752,6 +7792,10 @@ function onwidgetresize(){ clmod(widget, 'thin', thin); + if(ebi('mu_vol').children.length > 0){ + bar.appendChild(ebi('pvolbg')); + } + pbar.onresize(); // keep path scrolled right diff --git a/copyparty/web/ui.css b/copyparty/web/ui.css index f7fb85b9..92ceb749 100644 --- a/copyparty/web/ui.css +++ b/copyparty/web/ui.css @@ -452,7 +452,7 @@ html.y #tth { } *:focus-visible, *:focus-visible+label, -#pctl *:focus-visible, +.pctl *:focus-visible, .btn:focus-visible { /* box-shadow: 0 .1em .2em #fc0 inset; */ outline: var(--a) solid .1em;