diff --git a/.vscode/launch.json b/.vscode/launch.json index fdaaf284..d0afa1ef 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,10 +13,13 @@ "-ed", "-emp", "-e2dsa", + "-e2ts", "-a", "ed:wark", "-v", - "srv::r:aed:cnodupe" + "srv::r:aed:cnodupe", + "-v", + "dist:dist:r" ] }, { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f4a637f1..c08c4620 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ }, { "label": "no_dbg", - "command": "${config:python.pythonPath} -m copyparty -ed -emp -e2dsa -a ed:wark -v srv::r:aed:cnodupe ;exit 1", + "command": "${config:python.pythonPath} -m copyparty -ed -emp -e2dsa -e2ts -a ed:wark -v srv::r:aed:cnodupe -v dist:dist:r ;exit 1", "type": "shell" } ] diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 30628444..09798a17 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -257,7 +257,7 @@ def main(): ap2.add_argument("--no-mutagen", action="store_true", help="use ffprobe for tags instead") ap2.add_argument("-mtm", metavar="M=t,t,t", action="append", type=str, help="add/replace metadata mapping") ap2.add_argument("-mte", metavar="M,M,M", type=str, help="tags to index/display (comma-sep.)", - default="circle,album,.tn,artist,title,.dur,.q") + default="circle,album,.tn,artist,title,.bpm,key,.dur,.q") ap2 = ap.add_argument_group('SSL/TLS options') ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls") diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index 5453a6e8..4995ec63 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -96,7 +96,10 @@ a, } #files td { border-bottom: 1px solid #111; +} +#files td+td+td { max-width: 30em; + overflow: hidden; } #files tr+tr td { border-top: 1px solid #383838; @@ -168,7 +171,8 @@ a, margin: -.2em; } #files tbody a.play.act { - color: #af0; + color: #840; + text-shadow: 0 0 .3em #b80; } #blocked { position: fixed; @@ -299,6 +303,20 @@ a, width: calc(100% - 10.5em); background: rgba(0,0,0,0.2); } +@media (min-width: 100em) { + #barpos, + #barbuf { + width: calc(100% - 24em); + left: 10em; + top: .7em; + height: 1.6em; + bottom: auto; + } + #widget { + bottom: -3.2em; + height: 3.2em; + } +} @@ -496,3 +514,42 @@ input[type="checkbox"]:checked+label { position: absolute; z-index: 9; } +#files .cfg { + display: none; + font-size: 2em; + white-space: nowrap; +} +#files th:hover .cfg, +#files th.min .cfg { + display: block; + width: 1em; + border-radius: .2em; + margin: -1.3em auto 0 auto; + background: #444; +} +#files th.min .cfg { + margin: -.6em; +} +#files>thead>tr>th.min span { + position: absolute; + transform: rotate(270deg); + background: linear-gradient(90deg, #222, #444); + margin-left: -4.6em; + padding: .4em; + top: 5.4em; + width: 8em; + text-align: right; + letter-spacing: .04em; +} +#files td:nth-child(2n) { + color: #f5a; +} +#files td.min a { + display: none; +} +#files tr.play td { + background: #fc4; + border-color: transparent; + color: #400; + text-shadow: none; +} \ No newline at end of file diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index d9bbc576..30b4f114 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -58,17 +58,17 @@ - File Name - Size + File Name + Size {%- for k in taglist %} {%- if k.startswith('.') %} - {{ k[1:] }} + {{ k[1:] }} {%- else %} - {{ k[0]|upper }}{{ k[1:] }} + {{ k[0]|upper }}{{ k[1:] }} {%- endif %} {%- endfor %} - T - Date + T + Date diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index da7420a1..15cfd063 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -6,21 +6,6 @@ function dbg(msg) { ebi('path').innerHTML = msg; } -function ev(e) { - e = e || window.event; - if (!e) - return; - - if (e.preventDefault) - e.preventDefault() - - if (e.stopPropagation) - e.stopPropagation(); - - e.returnValue = false; - return e; -} - makeSortable(ebi('files')); @@ -55,7 +40,7 @@ function init_mp() { for (var a = 0, aa = tracks.length; a < aa; a++) ebi('trk' + a).onclick = ev_play; - ret.vol = localStorage.getItem('vol'); + ret.vol = sread('vol'); if (ret.vol !== null) ret.vol = parseFloat(ret.vol); else @@ -67,7 +52,7 @@ function init_mp() { ret.setvol = function (vol) { ret.vol = Math.max(Math.min(vol, 1), 0); - localStorage.setItem('vol', vol); + swrite('vol', vol); if (ret.au) ret.au.volume = ret.expvol(); @@ -460,6 +445,11 @@ function play(tid, call_depth) { mp.au.volume = mp.expvol(); var oid = 'trk' + tid; setclass(oid, 'play act'); + var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); + for (var a = 0, aa = trs.length; a < aa; a++) { + trs[a].className = trs[a].className.replace(/ *play */, ""); + } + ebi(oid).parentElement.parentElement.className += ' play'; try { if (hack_attempt_play) @@ -708,6 +698,7 @@ function autoplay_blocked() { ofiles.innerHTML = html.join('\n'); ofiles.setAttribute("ts", this.ts); + filecols.set_style(); reload_browser(); ebi('unsearch').onclick = unsearch; @@ -741,7 +732,7 @@ function autoplay_blocked() { treefiles.appendChild(ebi('files')); treefiles.appendChild(ebi('epi')); - localStorage.setItem('entreed', 'tree'); + swrite('entreed', 'tree'); get_tree("", get_vpath()); } @@ -911,6 +902,7 @@ function autoplay_blocked() { ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : ""; ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : ""; + filecols.set_style(); reload_tree(); reload_browser(); } @@ -955,12 +947,12 @@ function autoplay_blocked() { ebi('path').style.display = 'inline-block'; treetab.style.display = 'none'; - localStorage.setItem('entreed', 'na'); + swrite('entreed', 'na'); } ebi('entree').onclick = entree; ebi('detree').onclick = detree; - if (window.localStorage && localStorage.getItem('entreed') == 'tree') + if (sread('entreed') == 'tree') entree(); window.onpopstate = function (e) { @@ -973,7 +965,7 @@ function autoplay_blocked() { }; if (window.history && history.pushState) { - var u = get_vpath(); + var u = get_vpath() + window.location.hash; history.replaceState(ebi('files').innerHTML, u, u); } })(); @@ -1032,28 +1024,118 @@ function apply_perms(perms) { function mk_files_header(taglist) { - var html = ['', '', 'File Name', 'Size']; + var html = [ + '', + '', + 'File Name', + 'Size' + ]; for (var a = 0; a < taglist.length; a++) { var tag = taglist[a]; var c1 = tag.slice(0, 1).toUpperCase(); tag = c1 + tag.slice(1); if (c1 == '.') - tag = '' + tag.slice(1); + tag = '' + tag.slice(1); else - tag = '' + tag; + tag = '' + tag; - html.push(tag + ''); + html.push(tag + ''); } html = html.concat([ - 'T', - 'Date', + 'T', + 'Date', '', ]); return html; } +var filecols = (function () { + var hidden = jread('filecols', []); + + var add_btns = function () { + var ths = document.querySelectorAll('#files th>span'); + for (var a = 0, aa = ths.length; a < aa; a++) { + var th = ths[a].parentElement; + var is_hidden = has(hidden, ths[a].textContent); + th.innerHTML = '
' + + (is_hidden ? '+' : '-') + '
' + ths[a].outerHTML; + + th.getElementsByTagName('a')[0].onclick = ev_row_tgl; + } + }; + + var set_style = function () { + add_btns(); + + var ohidden = [], + ths = document.querySelectorAll('#files th'), + ncols = ths.length; + + for (var a = 0; a < ncols; a++) { + var span = ths[a].getElementsByTagName('span'); + if (span.length <= 0) + continue; + + var name = span[0].textContent, + cls = ''; + + if (has(hidden, name)) { + ohidden.push(a); + cls = ' min'; + } + ths[a].className = ths[a].className.replace(/ *min */, " ") + cls; + } + for (var a = 0; a < ncols; a++) { + var cls = has(ohidden, a) ? 'min' : ''; + var tds = document.querySelectorAll('#files>tbody>tr>td:nth-child(' + (a + 1) + ')'); + for (var b = 0, bb = tds.length; b < bb; b++) { + tds[b].setAttribute('class', cls); + if (a < 2) + continue; + + if (cls) { + if (!tds[b].hasAttribute('html')) { + tds[b].setAttribute('html', tds[b].innerHTML); + tds[b].innerHTML = '...'; + } + } + else if (tds[b].hasAttribute('html')) { + tds[b].innerHTML = tds[b].getAttribute('html'); + tds[b].removeAttribute('html'); + } + } + } + }; + set_style(); + + var toggle = function (name) { + var ofs = hidden.indexOf(name); + if (ofs !== -1) + hidden.splice(ofs, 1); + else + hidden.push(name); + + jwrite("filecols", hidden); + set_style(); + }; + + return { + "add_btns": add_btns, + "set_style": set_style, + "toggle": toggle, + }; +})(); + + +function ev_row_tgl(e) { + ev(e); + filecols.toggle(this.parentElement.parentElement.getElementsByTagName('span')[0].textContent); +} + + function reload_browser(not_mp) { + filecols.set_style(); makeSortable(ebi('files')); var parts = get_vpath().split('/'); diff --git a/copyparty/web/md.js b/copyparty/web/md.js index 5a752a9a..6c258b2e 100644 --- a/copyparty/web/md.js +++ b/copyparty/web/md.js @@ -524,11 +524,9 @@ dom_navtgl.onclick = function () { dom_navtgl.innerHTML = hidden ? 'show nav' : 'hide nav'; dom_nav.style.display = hidden ? 'none' : 'block'; - if (window.localStorage) - localStorage.setItem('hidenav', hidden ? 1 : 0); - + swrite('hidenav', hidden ? 1 : 0); redraw(); }; -if (window.localStorage && localStorage.getItem('hidenav') == 1) +if (sread('hidenav') == 1) dom_navtgl.onclick(); diff --git a/copyparty/web/up2k.js b/copyparty/web/up2k.js index f6b750ea..128006f8 100644 --- a/copyparty/web/up2k.js +++ b/copyparty/web/up2k.js @@ -210,7 +210,7 @@ function up2k_init(have_crypto) { } function cfg_get(name) { - var val = localStorage.getItem(name); + var val = sread(name); if (val === null) return parseInt(ebi(name).value); @@ -223,7 +223,7 @@ function up2k_init(have_crypto) { if (!o) return defval; - var val = localStorage.getItem(name); + var val = sread(name); if (val === null) val = defval; else @@ -234,8 +234,7 @@ function up2k_init(have_crypto) { } function bcfg_set(name, val) { - localStorage.setItem( - name, val ? '1' : '0'); + swrite(name, val ? '1' : '0'); var o = ebi(name); if (o) @@ -1033,7 +1032,7 @@ function up2k_init(have_crypto) { return; parallel_uploads = v; - localStorage.setItem('nthread', v); + swrite('nthread', v); obj.style.background = '#444'; return; } diff --git a/copyparty/web/util.js b/copyparty/web/util.js index 1d1575b9..2f3699fd 100644 --- a/copyparty/web/util.js +++ b/copyparty/web/util.js @@ -43,6 +43,21 @@ function ebi(id) { return document.getElementById(id); } +function ev(e) { + e = e || window.event; + if (!e) + return; + + if (e.preventDefault) + e.preventDefault() + + if (e.stopPropagation) + e.stopPropagation(); + + e.returnValue = false; + return e; +} + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith if (!String.prototype.endsWith) { @@ -79,10 +94,10 @@ function sortTable(table, col) { var tb = table.tBodies[0], th = table.tHead.rows[0].cells, tr = Array.prototype.slice.call(tb.rows, 0), - i, reverse = th[col].className == 'sort1' ? -1 : 1; + i, reverse = th[col].className.indexOf('sort1') !== -1 ? -1 : 1; for (var a = 0, thl = th.length; a < thl; a++) - th[a].className = ''; - th[col].className = 'sort' + reverse; + th[a].className = th[a].className.replace(/ *sort-?1 */, " "); + th[col].className += ' sort' + reverse; var stype = th[col].getAttribute('sort'); tr = tr.sort(function (a, b) { if (!a.cells[col]) @@ -107,7 +122,8 @@ function makeSortable(table) { if (th) i = th.length; else return; // if no `` then do nothing while (--i >= 0) (function (i) { - th[i].onclick = function () { + th[i].onclick = function (e) { + ev(e); sortTable(table, i); }; }(i)); @@ -123,16 +139,13 @@ function makeSortable(table) { })(); -function opclick(ev) { - if (ev) //ie - ev.preventDefault(); +function opclick(e) { + ev(e); var dest = this.getAttribute('data-dest'); goto(dest); - // writing a blank value makes ie8 segfault w - if (window.localStorage) - localStorage.setItem('opmode', dest || '.'); + swrite('opmode', dest || undefined); var input = document.querySelector('.opview.act input:not([type="hidden"])') if (input) @@ -167,11 +180,9 @@ function goto(dest) { (function () { goto(); - if (window.localStorage) { - var op = localStorage.getItem('opmode'); - if (op !== null && op !== '.') - goto(op); - } + var op = sread('opmode'); + if (op !== null && op !== '.') + goto(op); })(); @@ -238,3 +249,35 @@ function has(haystack, needle) { return false; } + + +function sread(key) { + if (window.localStorage) + return localStorage.getItem(key); + + return ''; +} + +function swrite(key, val) { + if (window.localStorage) { + if (val === undefined) + localStorage.removeItem(key); + else + localStorage.setItem(key, val); + } +} + +function jread(key, fb) { + var str = sread(key); + if (!str) + return fb; + + return JSON.parse(str); +} + +function jwrite(key, val) { + if (!val) + swrite(key); + else + swrite(key, JSON.stringify(val)); +}