mirror of
https://github.com/9001/copyparty.git
synced 2025-10-25 00:53:47 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5649d26077 | ||
|
|
92f923effe | ||
|
|
0d46d548b9 | ||
|
|
062df3f0c3 | ||
|
|
789fb53b8e | ||
|
|
351db5a18f | ||
|
|
aabbd271c8 | ||
|
|
aae8e0171e | ||
|
|
45827a2458 | ||
|
|
726030296f | ||
|
|
6659ab3881 | ||
|
|
c6a103609e | ||
|
|
c6b3f035e5 | ||
|
|
2b0a7e378e | ||
|
|
b75ce909c8 | ||
|
|
229c3f5dab | ||
|
|
ec73094506 | ||
|
|
c7650c9326 | ||
|
|
d94c6d4e72 | ||
|
|
3cc8760733 | ||
|
|
a2f6973495 | ||
|
|
f8648fa651 | ||
|
|
177aa038df | ||
|
|
e0a14ec881 | ||
|
|
9366512f2f | ||
|
|
ea38b8041a | ||
|
|
f1870daf0d | ||
|
|
9722441aad | ||
|
|
9d014087f4 | ||
|
|
83b4038b85 | ||
|
|
1e0a448feb | ||
|
|
fb81de3b36 | ||
|
|
aa4f352301 | ||
|
|
f1a1c2ea45 | ||
|
|
6249bd4163 | ||
|
|
2579dc64ce | ||
|
|
356512270a | ||
|
|
bed27f2b43 | ||
|
|
54013d861b | ||
|
|
ec100210dc |
32
README.md
32
README.md
@@ -20,6 +20,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||||||
|
|
||||||
* top
|
* top
|
||||||
* [quickstart](#quickstart)
|
* [quickstart](#quickstart)
|
||||||
|
* [on debian](#on-debian)
|
||||||
* [notes](#notes)
|
* [notes](#notes)
|
||||||
* [status](#status)
|
* [status](#status)
|
||||||
* [bugs](#bugs)
|
* [bugs](#bugs)
|
||||||
@@ -68,6 +69,7 @@ some recommended options:
|
|||||||
* `-e2dsa` enables general file indexing, see [search configuration](#search-configuration)
|
* `-e2dsa` enables general file indexing, see [search configuration](#search-configuration)
|
||||||
* `-e2ts` enables audio metadata indexing (needs either FFprobe or mutagen), see [optional dependencies](#optional-dependencies)
|
* `-e2ts` enables audio metadata indexing (needs either FFprobe or mutagen), see [optional dependencies](#optional-dependencies)
|
||||||
* `-v /mnt/music:/music:r:afoo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, with user `foo` as `a`dmin (read/write), password `bar`
|
* `-v /mnt/music:/music:r:afoo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, with user `foo` as `a`dmin (read/write), password `bar`
|
||||||
|
* the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
|
||||||
* replace `:r:afoo` with `:rfoo` to only make the folder readable by `foo` and nobody else
|
* replace `:r:afoo` with `:rfoo` to only make the folder readable by `foo` and nobody else
|
||||||
* in addition to `r`ead and `a`dmin, `w`rite makes a folder write-only, so cannot list/access files in it
|
* in addition to `r`ead and `a`dmin, `w`rite makes a folder write-only, so cannot list/access files in it
|
||||||
* `--ls '**,*,ln,p,r'` to crash on startup if any of the volumes contain a symlink which point outside the volume, as that could give users unintended access
|
* `--ls '**,*,ln,p,r'` to crash on startup if any of the volumes contain a symlink which point outside the volume, as that could give users unintended access
|
||||||
@@ -77,6 +79,19 @@ you may also want these, especially on servers:
|
|||||||
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to reverse-proxy behind nginx (for better https)
|
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to reverse-proxy behind nginx (for better https)
|
||||||
|
|
||||||
|
|
||||||
|
### on debian
|
||||||
|
|
||||||
|
recommended steps to enable audio metadata and thumbnails (from images and videos):
|
||||||
|
|
||||||
|
* as root, run the following:
|
||||||
|
`apt install python3 python3-pip python3-dev ffmpeg`
|
||||||
|
|
||||||
|
* then, as the user which will be running copyparty (so hopefully not root), run this:
|
||||||
|
`python3 -m pip install --user -U Pillow pillow-avif-plugin`
|
||||||
|
|
||||||
|
(skipped `pyheif-pillow-opener` because apparently debian is too old to build it)
|
||||||
|
|
||||||
|
|
||||||
## notes
|
## notes
|
||||||
|
|
||||||
general:
|
general:
|
||||||
@@ -168,25 +183,28 @@ summary: all planned features work! now please enjoy the bloatening
|
|||||||
## hotkeys
|
## hotkeys
|
||||||
|
|
||||||
the browser has the following hotkeys
|
the browser has the following hotkeys
|
||||||
|
* `B` toggle breadcrumbs / directory tree
|
||||||
* `I/K` prev/next folder
|
* `I/K` prev/next folder
|
||||||
* `P` parent folder
|
* `M` parent folder
|
||||||
* `G` toggle list / grid view
|
* `G` toggle list / grid view
|
||||||
* `T` toggle thumbnails / icons
|
* `T` toggle thumbnails / icons
|
||||||
* when playing audio:
|
* when playing audio:
|
||||||
* `0..9` jump to 10%..90%
|
* `0..9` jump to 10%..90%
|
||||||
* `U/O` skip 10sec back/forward
|
* `U/O` skip 10sec back/forward
|
||||||
* `J/L` prev/next song
|
* `J/L` prev/next song
|
||||||
* `M` play/pause (also starts playing the folder)
|
* `P` play/pause (also starts playing the folder)
|
||||||
|
* when tree-sidebar is open:
|
||||||
|
* `A/D` adjust tree width
|
||||||
* in the grid view:
|
* in the grid view:
|
||||||
* `S` toggle multiselect
|
* `S` toggle multiselect
|
||||||
* `A/D` zoom
|
* shift+`A/D` zoom
|
||||||
|
|
||||||
|
|
||||||
## tree-mode
|
## tree-mode
|
||||||
|
|
||||||
by default there's a breadcrumbs path; you can replace this with a tree-browser sidebar thing by clicking the 🌲
|
by default there's a breadcrumbs path; you can replace this with a tree-browser sidebar thing by clicking the `🌲` or pressing the `B` hotkey
|
||||||
|
|
||||||
click `[-]` and `[+]` to adjust the size, and the `[a]` toggles if the tree should widen dynamically as you go deeper or stay fixed-size
|
click `[-]` and `[+]` (or hotkeys `A`/`D`) to adjust the size, and the `[a]` toggles if the tree should widen dynamically as you go deeper or stay fixed-size
|
||||||
|
|
||||||
|
|
||||||
## thumbnails
|
## thumbnails
|
||||||
@@ -197,6 +215,8 @@ it does static images with Pillow and uses FFmpeg for video files, so you may wa
|
|||||||
|
|
||||||
images named `folder.jpg` and `folder.png` become the thumbnail of the folder they're in
|
images named `folder.jpg` and `folder.png` become the thumbnail of the folder they're in
|
||||||
|
|
||||||
|
in the grid/thumbnail view, if the audio player panel is open, songs will start playing when clicked
|
||||||
|
|
||||||
|
|
||||||
## zip downloads
|
## zip downloads
|
||||||
|
|
||||||
@@ -280,6 +300,8 @@ up2k has saved a few uploads from becoming corrupted in-transfer already; caught
|
|||||||
|
|
||||||
* you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
|
* you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
|
||||||
|
|
||||||
|
* if you are using media hotkeys to switch songs and are getting tired of seeing the OSD popup which Windows doesn't let you disable, consider https://ocv.me/dev/?media-osd-bgone.ps1
|
||||||
|
|
||||||
|
|
||||||
# searching
|
# searching
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
det(tf)
|
det(tf)
|
||||||
except:
|
except:
|
||||||
pass
|
pass # mute
|
||||||
finally:
|
finally:
|
||||||
os.unlink(tf)
|
os.unlink(tf)
|
||||||
|
|
||||||
|
|||||||
123
bin/mtag/audio-key-slicing.py
Executable file
123
bin/mtag/audio-key-slicing.py
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import subprocess as sp
|
||||||
|
|
||||||
|
import keyfinder
|
||||||
|
|
||||||
|
from copyparty.util import fsenc
|
||||||
|
|
||||||
|
"""
|
||||||
|
dep: github/mixxxdj/libkeyfinder
|
||||||
|
dep: pypi/keyfinder
|
||||||
|
dep: ffmpeg
|
||||||
|
|
||||||
|
note: this is a janky edition of the regular audio-key.py,
|
||||||
|
slicing the files at 20sec intervals and keeping 5sec from each,
|
||||||
|
surprisingly accurate but still garbage (446 ok, 69 bad, 13% miss)
|
||||||
|
|
||||||
|
it is fast tho
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_duration():
|
||||||
|
# TODO provide ffprobe tags to mtp as json
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
dur = sp.check_output([
|
||||||
|
"ffprobe",
|
||||||
|
"-hide_banner",
|
||||||
|
"-v", "fatal",
|
||||||
|
"-show_streams",
|
||||||
|
"-show_format",
|
||||||
|
fsenc(sys.argv[1])
|
||||||
|
])
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
dur = dur.decode("ascii", "replace").split("\n")
|
||||||
|
dur = [x.split("=")[1] for x in dur if x.startswith("duration=")]
|
||||||
|
dur = [float(x) for x in dur if re.match(r"^[0-9\.,]+$", x)]
|
||||||
|
return list(sorted(dur))[-1] if dur else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_segs(dur):
|
||||||
|
# keep first 5s of each 20s,
|
||||||
|
# keep entire last segment
|
||||||
|
ofs = 0
|
||||||
|
segs = []
|
||||||
|
while True:
|
||||||
|
seg = [ofs, 5]
|
||||||
|
segs.append(seg)
|
||||||
|
if dur - ofs < 20:
|
||||||
|
seg[-1] = int(dur - seg[0])
|
||||||
|
break
|
||||||
|
|
||||||
|
ofs += 20
|
||||||
|
|
||||||
|
return segs
|
||||||
|
|
||||||
|
|
||||||
|
def slice(tf):
|
||||||
|
dur = get_duration()
|
||||||
|
dur = min(dur, 600) # max 10min
|
||||||
|
segs = get_segs(dur)
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-nostdin",
|
||||||
|
"-hide_banner",
|
||||||
|
"-v", "fatal",
|
||||||
|
"-y"
|
||||||
|
]
|
||||||
|
|
||||||
|
for seg in segs:
|
||||||
|
cmd.extend([
|
||||||
|
"-ss", str(seg[0]),
|
||||||
|
"-i", fsenc(sys.argv[1])
|
||||||
|
])
|
||||||
|
|
||||||
|
filt = ""
|
||||||
|
for n, seg in enumerate(segs):
|
||||||
|
filt += "[{}:a:0]atrim=duration={}[a{}]; ".format(n, seg[1], n)
|
||||||
|
|
||||||
|
prev = "a0"
|
||||||
|
for n in range(1, len(segs)):
|
||||||
|
nxt = "b{}".format(n)
|
||||||
|
filt += "[{}][a{}]acrossfade=d=0.5[{}]; ".format(prev, n, nxt)
|
||||||
|
prev = nxt
|
||||||
|
|
||||||
|
cmd.extend([
|
||||||
|
"-filter_complex", filt[:-2],
|
||||||
|
"-map", "[{}]".format(nxt),
|
||||||
|
"-sample_fmt", "s16",
|
||||||
|
tf
|
||||||
|
])
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
# print(cmd)
|
||||||
|
sp.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def det(tf):
|
||||||
|
slice(tf)
|
||||||
|
print(keyfinder.key(tf).camelot())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".flac", delete=False) as f:
|
||||||
|
f.write(b"h")
|
||||||
|
tf = f.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
det(tf)
|
||||||
|
finally:
|
||||||
|
os.unlink(tf)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,18 +1,54 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import subprocess as sp
|
||||||
import keyfinder
|
import keyfinder
|
||||||
|
|
||||||
|
from copyparty.util import fsenc
|
||||||
|
|
||||||
"""
|
"""
|
||||||
dep: github/mixxxdj/libkeyfinder
|
dep: github/mixxxdj/libkeyfinder
|
||||||
dep: pypi/keyfinder
|
dep: pypi/keyfinder
|
||||||
dep: ffmpeg
|
dep: ffmpeg
|
||||||
|
|
||||||
note: cannot fsenc
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
try:
|
# tried trimming the first/last 5th, bad idea,
|
||||||
print(keyfinder.key(sys.argv[1]).camelot())
|
# misdetects 9a law field (Sphere Caliber) as 10b,
|
||||||
except:
|
# obvious when mixing 9a ghostly parapara ship
|
||||||
pass
|
|
||||||
|
|
||||||
|
def det(tf):
|
||||||
|
# fmt: off
|
||||||
|
sp.check_call([
|
||||||
|
"ffmpeg",
|
||||||
|
"-nostdin",
|
||||||
|
"-hide_banner",
|
||||||
|
"-v", "fatal",
|
||||||
|
"-y", "-i", fsenc(sys.argv[1]),
|
||||||
|
"-t", "300",
|
||||||
|
"-sample_fmt", "s16",
|
||||||
|
tf
|
||||||
|
])
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
print(keyfinder.key(tf).camelot())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".flac", delete=False) as f:
|
||||||
|
f.write(b"h")
|
||||||
|
tf = f.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
det(tf)
|
||||||
|
except:
|
||||||
|
pass # mute
|
||||||
|
finally:
|
||||||
|
os.unlink(tf)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 11, 22)
|
VERSION = (0, 11, 28)
|
||||||
CODENAME = "the grid"
|
CODENAME = "the grid"
|
||||||
BUILD_DT = (2021, 6, 21)
|
BUILD_DT = (2021, 6, 28)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -693,6 +693,11 @@ class AuthSrv(object):
|
|||||||
self.user = user
|
self.user = user
|
||||||
self.iuser = {v: k for k, v in user.items()}
|
self.iuser = {v: k for k, v in user.items()}
|
||||||
|
|
||||||
|
self.re_pwd = None
|
||||||
|
pwds = [re.escape(x) for x in self.iuser.keys()]
|
||||||
|
if pwds:
|
||||||
|
self.re_pwd = re.compile("=(" + "|".join(pwds) + ")([]&; ]|$)")
|
||||||
|
|
||||||
# import pprint
|
# import pprint
|
||||||
# pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount})
|
# pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount})
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import json
|
|||||||
import string
|
import string
|
||||||
import socket
|
import socket
|
||||||
import ctypes
|
import ctypes
|
||||||
import traceback
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import calendar
|
import calendar
|
||||||
|
|
||||||
@@ -50,12 +49,21 @@ class HttpCli(object):
|
|||||||
self.tls = hasattr(self.s, "cipher")
|
self.tls = hasattr(self.s, "cipher")
|
||||||
|
|
||||||
self.bufsz = 1024 * 32
|
self.bufsz = 1024 * 32
|
||||||
|
self.hint = None
|
||||||
self.absolute_urls = False
|
self.absolute_urls = False
|
||||||
self.out_headers = {"Access-Control-Allow-Origin": "*"}
|
self.out_headers = {"Access-Control-Allow-Origin": "*"}
|
||||||
|
|
||||||
def log(self, msg, c=0):
|
def log(self, msg, c=0):
|
||||||
|
ptn = self.asrv.re_pwd
|
||||||
|
if ptn and ptn.search(msg):
|
||||||
|
msg = ptn.sub(self.unpwd, msg)
|
||||||
|
|
||||||
self.log_func(self.log_src, msg, c)
|
self.log_func(self.log_src, msg, c)
|
||||||
|
|
||||||
|
def unpwd(self, m):
|
||||||
|
a, b = m.groups()
|
||||||
|
return "=\033[7m {} \033[27m{}".format(self.asrv.iuser[a], b)
|
||||||
|
|
||||||
def _check_nonfatal(self, ex):
|
def _check_nonfatal(self, ex):
|
||||||
return ex.code < 400 or ex.code in [404, 429]
|
return ex.code < 400 or ex.code in [404, 429]
|
||||||
|
|
||||||
@@ -72,6 +80,7 @@ class HttpCli(object):
|
|||||||
"""returns true if connection can be reused"""
|
"""returns true if connection can be reused"""
|
||||||
self.keepalive = False
|
self.keepalive = False
|
||||||
self.headers = {}
|
self.headers = {}
|
||||||
|
self.hint = None
|
||||||
try:
|
try:
|
||||||
headerlines = read_header(self.sr)
|
headerlines = read_header(self.sr)
|
||||||
if not headerlines:
|
if not headerlines:
|
||||||
@@ -130,6 +139,9 @@ class HttpCli(object):
|
|||||||
if v is not None:
|
if v is not None:
|
||||||
self.log("[H] {}: \033[33m[{}]".format(k, v), 6)
|
self.log("[H] {}: \033[33m[{}]".format(k, v), 6)
|
||||||
|
|
||||||
|
if "&" in self.req and "?" not in self.req:
|
||||||
|
self.hint = "did you mean '?' instead of '&'"
|
||||||
|
|
||||||
# split req into vpath + uparam
|
# split req into vpath + uparam
|
||||||
uparam = {}
|
uparam = {}
|
||||||
if "?" not in self.req:
|
if "?" not in self.req:
|
||||||
@@ -169,6 +181,9 @@ class HttpCli(object):
|
|||||||
self.rvol, self.wvol, self.avol = [[], [], []]
|
self.rvol, self.wvol, self.avol = [[], [], []]
|
||||||
self.asrv.vfs.user_tree(self.uname, self.rvol, self.wvol, self.avol)
|
self.asrv.vfs.user_tree(self.uname, self.rvol, self.wvol, self.avol)
|
||||||
|
|
||||||
|
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
|
||||||
|
self.out_headers["Set-Cookie"] = self.get_pwd_cookie(pwd)[0]
|
||||||
|
|
||||||
ua = self.headers.get("user-agent", "")
|
ua = self.headers.get("user-agent", "")
|
||||||
self.is_rclone = ua.startswith("rclone/")
|
self.is_rclone = ua.startswith("rclone/")
|
||||||
if self.is_rclone:
|
if self.is_rclone:
|
||||||
@@ -199,6 +214,9 @@ class HttpCli(object):
|
|||||||
|
|
||||||
self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
|
self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
|
||||||
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
|
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
|
||||||
|
if self.hint:
|
||||||
|
msg += "hint: {}\r\n".format(self.hint)
|
||||||
|
|
||||||
self.reply(msg.encode("utf-8", "replace"), status=ex.code)
|
self.reply(msg.encode("utf-8", "replace"), status=ex.code)
|
||||||
return self.keepalive
|
return self.keepalive
|
||||||
except Pebkac:
|
except Pebkac:
|
||||||
@@ -623,7 +641,7 @@ class HttpCli(object):
|
|||||||
penalty = 0.7
|
penalty = 0.7
|
||||||
t_idle = t0 - idx.p_end
|
t_idle = t0 - idx.p_end
|
||||||
if idx.p_dur > 0.7 and t_idle < penalty:
|
if idx.p_dur > 0.7 and t_idle < penalty:
|
||||||
m = "rate-limit ({:.1f} sec), cost {:.2f}, idle {:.2f}"
|
m = "rate-limit {:.1f} sec, cost {:.2f}, idle {:.2f}"
|
||||||
raise Pebkac(429, m.format(penalty, idx.p_dur, t_idle))
|
raise Pebkac(429, m.format(penalty, idx.p_dur, t_idle))
|
||||||
|
|
||||||
if "srch" in body:
|
if "srch" in body:
|
||||||
@@ -743,6 +761,12 @@ class HttpCli(object):
|
|||||||
pwd = self.parser.require("cppwd", 64)
|
pwd = self.parser.require("cppwd", 64)
|
||||||
self.parser.drop()
|
self.parser.drop()
|
||||||
|
|
||||||
|
ck, msg = self.get_pwd_cookie(pwd)
|
||||||
|
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
|
||||||
|
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_pwd_cookie(self, pwd):
|
||||||
if pwd in self.asrv.iuser:
|
if pwd in self.asrv.iuser:
|
||||||
msg = "login ok"
|
msg = "login ok"
|
||||||
dt = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24 * 365)
|
dt = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24 * 365)
|
||||||
@@ -753,9 +777,7 @@ class HttpCli(object):
|
|||||||
exp = "Fri, 15 Aug 1997 01:00:00 GMT"
|
exp = "Fri, 15 Aug 1997 01:00:00 GMT"
|
||||||
|
|
||||||
ck = "cppwd={}; Path=/; Expires={}; SameSite=Lax".format(pwd, exp)
|
ck = "cppwd={}; Path=/; Expires={}; SameSite=Lax".format(pwd, exp)
|
||||||
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
|
return [ck, msg]
|
||||||
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
|
|
||||||
return True
|
|
||||||
|
|
||||||
def handle_mkdir(self):
|
def handle_mkdir(self):
|
||||||
new_dir = self.parser.require("name", 512)
|
new_dir = self.parser.require("name", 512)
|
||||||
@@ -1305,7 +1327,7 @@ class HttpCli(object):
|
|||||||
ext = "folder"
|
ext = "folder"
|
||||||
exact = True
|
exact = True
|
||||||
|
|
||||||
bad = re.compile(r"[](){}/[]|^[0-9_-]*$")
|
bad = re.compile(r"[](){}/ []|^[0-9_-]*$")
|
||||||
n = ext.split(".")[::-1]
|
n = ext.split(".")[::-1]
|
||||||
if not exact:
|
if not exact:
|
||||||
n = n[:-1]
|
n = n[:-1]
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import print_function, unicode_literals
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,19 @@ def parse_ffprobe(txt):
|
|||||||
ret = {} # processed
|
ret = {} # processed
|
||||||
md = {} # raw tags
|
md = {} # raw tags
|
||||||
|
|
||||||
|
is_audio = fmt.get("format_name") in ["mp3", "ogg", "flac", "wav"]
|
||||||
|
if fmt.get("filename", "").split(".")[-1].lower() in ["m4a", "aac"]:
|
||||||
|
is_audio = True
|
||||||
|
|
||||||
|
# if audio file, ensure audio stream appears first
|
||||||
|
if (
|
||||||
|
is_audio
|
||||||
|
and len(streams) > 2
|
||||||
|
and streams[1].get("codec_type") != "audio"
|
||||||
|
and streams[2].get("codec_type") == "audio"
|
||||||
|
):
|
||||||
|
streams = [fmt, streams[2], streams[1]] + streams[3:]
|
||||||
|
|
||||||
have = {}
|
have = {}
|
||||||
for strm in streams:
|
for strm in streams:
|
||||||
typ = strm.get("codec_type")
|
typ = strm.get("codec_type")
|
||||||
@@ -134,9 +147,7 @@ def parse_ffprobe(txt):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if typ == "video":
|
if typ == "video":
|
||||||
if strm.get("DISPOSITION:attached_pic") == "1" or fmt.get(
|
if strm.get("DISPOSITION:attached_pic") == "1" or is_audio:
|
||||||
"format_name"
|
|
||||||
) in ["mp3", "ogg", "flac"]:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
kvm = [
|
kvm = [
|
||||||
@@ -180,7 +191,7 @@ def parse_ffprobe(txt):
|
|||||||
|
|
||||||
k = k[4:].strip()
|
k = k[4:].strip()
|
||||||
v = v.strip()
|
v = v.strip()
|
||||||
if k and v:
|
if k and v and k not in md:
|
||||||
md[k] = [v]
|
md[k] = [v]
|
||||||
|
|
||||||
for k in [".q", ".vq", ".aq"]:
|
for k in [".q", ".vq", ".aq"]:
|
||||||
|
|||||||
@@ -653,7 +653,7 @@ class Up2k(object):
|
|||||||
try:
|
try:
|
||||||
parser = MParser(parser)
|
parser = MParser(parser)
|
||||||
except:
|
except:
|
||||||
self.log("invalid argument: " + parser, 1)
|
self.log("invalid argument (could not find program): " + parser, 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
for tag in entags:
|
for tag in entags:
|
||||||
@@ -901,7 +901,7 @@ class Up2k(object):
|
|||||||
except:
|
except:
|
||||||
self.log("WARN: could not list files; DB corrupt?\n" + min_ex())
|
self.log("WARN: could not list files; DB corrupt?\n" + min_ex())
|
||||||
|
|
||||||
elif ver > DB_VER:
|
if (ver or 0) > DB_VER:
|
||||||
m = "database is version {}, this copyparty only supports versions <= {}"
|
m = "database is version {}, this copyparty only supports versions <= {}"
|
||||||
raise Exception(m.format(ver, DB_VER))
|
raise Exception(m.format(ver, DB_VER))
|
||||||
|
|
||||||
|
|||||||
@@ -1030,7 +1030,13 @@ def guess_mime(url, fallback="application/octet-stream"):
|
|||||||
except:
|
except:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
return MIMES.get(ext) or mimetypes.guess_type(url)[0] or fallback
|
ret = MIMES.get(ext) or mimetypes.guess_type(url)[0] or fallback
|
||||||
|
|
||||||
|
if ";" not in ret:
|
||||||
|
if ret.startswith("text/") or ret.endswith("/javascript"):
|
||||||
|
ret += "; charset=UTF-8"
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def runcmd(*argv):
|
def runcmd(*argv):
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
<div id="epi" class="logue">{{ logues[1] }}</div>
|
<div id="epi" class="logue">{{ logues[1] }}</div>
|
||||||
|
|
||||||
<h2><a href="?h">control-panel</a></h2>
|
<h2><a href="/?h">control-panel</a></h2>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ var mpl = (function () {
|
|||||||
"pb_mode": sread('pb_mode') || 'loop-folder',
|
"pb_mode": sread('pb_mode') || 'loop-folder',
|
||||||
"preload": bcfg_get('au_preload', true),
|
"preload": bcfg_get('au_preload', true),
|
||||||
"clip": bcfg_get('au_npclip', false),
|
"clip": bcfg_get('au_npclip', false),
|
||||||
"os_ctl": bcfg_get('au_os_ctl', false) && have_mctl,
|
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
|
||||||
"osd_cv": bcfg_get('au_osd_cv', true),
|
"osd_cv": bcfg_get('au_osd_cv', true),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,6 +290,13 @@ var mpl = (function () {
|
|||||||
draw_pb_mode();
|
draw_pb_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.pp = function () {
|
||||||
|
if (!r.os_ctl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
navigator.mediaSession.playbackState = mp.au && !mp.au.paused ? "playing" : "paused";
|
||||||
|
};
|
||||||
|
|
||||||
r.announce = function () {
|
r.announce = function () {
|
||||||
if (!r.os_ctl)
|
if (!r.os_ctl)
|
||||||
return;
|
return;
|
||||||
@@ -320,18 +327,31 @@ var mpl = (function () {
|
|||||||
|
|
||||||
if (cover) {
|
if (cover) {
|
||||||
cover += (cover.indexOf('?') === -1 ? '?' : '&') + 'th=j';
|
cover += (cover.indexOf('?') === -1 ? '?' : '&') + 'th=j';
|
||||||
|
|
||||||
|
var pwd = get_pwd();
|
||||||
|
if (pwd)
|
||||||
|
cover += '&pw=' + uricom_enc(pwd);
|
||||||
|
|
||||||
tags.artwork = [{ "src": cover, type: "image/jpeg" }];
|
tags.artwork = [{ "src": cover, type: "image/jpeg" }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.mediaSession.metadata = new MediaMetadata(tags);
|
navigator.mediaSession.metadata = new MediaMetadata(tags);
|
||||||
navigator.mediaSession.playbackState = mp.au.paused ? "paused" : "playing";
|
|
||||||
navigator.mediaSession.setActionHandler('play', playpause);
|
navigator.mediaSession.setActionHandler('play', playpause);
|
||||||
navigator.mediaSession.setActionHandler('pause', playpause);
|
navigator.mediaSession.setActionHandler('pause', playpause);
|
||||||
navigator.mediaSession.setActionHandler('seekbackward', function () { seek_au_rel(-10); });
|
navigator.mediaSession.setActionHandler('seekbackward', function () { seek_au_rel(-10); });
|
||||||
navigator.mediaSession.setActionHandler('seekforward', function () { seek_au_rel(10); });
|
navigator.mediaSession.setActionHandler('seekforward', function () { seek_au_rel(10); });
|
||||||
navigator.mediaSession.setActionHandler('previoustrack', prev_song);
|
navigator.mediaSession.setActionHandler('previoustrack', prev_song);
|
||||||
navigator.mediaSession.setActionHandler('nexttrack', next_song);
|
navigator.mediaSession.setActionHandler('nexttrack', next_song);
|
||||||
|
r.pp();
|
||||||
|
};
|
||||||
|
|
||||||
|
r.stop = function () {
|
||||||
|
if (!r.os_ctl || !navigator.mediaSession.metadata)
|
||||||
|
return;
|
||||||
|
|
||||||
|
navigator.mediaSession.metadata = null;
|
||||||
|
navigator.mediaSession.playbackState = "paused";
|
||||||
};
|
};
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@@ -340,14 +360,15 @@ var mpl = (function () {
|
|||||||
|
|
||||||
// extract songs + add play column
|
// extract songs + add play column
|
||||||
function MPlayer() {
|
function MPlayer() {
|
||||||
this.id = Date.now();
|
var r = this;
|
||||||
this.au = null;
|
r.id = Date.now();
|
||||||
this.au_native = null;
|
r.au = null;
|
||||||
this.au_native2 = null;
|
r.au_native = null;
|
||||||
this.au_ogvjs = null;
|
r.au_native2 = null;
|
||||||
this.au_ogvjs2 = null;
|
r.au_ogvjs = null;
|
||||||
this.tracks = {};
|
r.au_ogvjs2 = null;
|
||||||
this.order = [];
|
r.tracks = {};
|
||||||
|
r.order = [];
|
||||||
|
|
||||||
var re_audio = /\.(opus|ogg|m4a|aac|mp3|wav|flac)$/i,
|
var re_audio = /\.(opus|ogg|m4a|aac|mp3|wav|flac)$/i,
|
||||||
trs = QSA('#files tbody tr');
|
trs = QSA('#files tbody tr');
|
||||||
@@ -362,32 +383,33 @@ function MPlayer() {
|
|||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
var tid = link.getAttribute('id');
|
var tid = link.getAttribute('id');
|
||||||
this.order.push(tid);
|
r.order.push(tid);
|
||||||
this.tracks[tid] = url;
|
r.tracks[tid] = url;
|
||||||
tds[0].innerHTML = '<a id="a' + tid + '" href="#a' + tid + '" class="play">play</a></td>';
|
tds[0].innerHTML = '<a id="a' + tid + '" href="#a' + tid + '" class="play">play</a></td>';
|
||||||
ebi('a' + tid).onclick = ev_play;
|
ebi('a' + tid).onclick = ev_play;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vol = sread('vol');
|
r.vol = sread('vol');
|
||||||
if (this.vol !== null)
|
if (r.vol !== null)
|
||||||
this.vol = parseFloat(this.vol);
|
r.vol = parseFloat(r.vol);
|
||||||
else
|
else
|
||||||
this.vol = 0.5;
|
r.vol = 0.5;
|
||||||
|
|
||||||
this.expvol = function () {
|
r.expvol = function (v) {
|
||||||
return 0.5 * this.vol + 0.5 * this.vol * this.vol;
|
return 0.5 * v + 0.5 * v * v;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setvol = function (vol) {
|
r.setvol = function (vol) {
|
||||||
this.vol = Math.max(Math.min(vol, 1), 0);
|
r.vol = Math.max(Math.min(vol, 1), 0);
|
||||||
swrite('vol', vol);
|
swrite('vol', vol);
|
||||||
|
r.stopfade(true);
|
||||||
|
|
||||||
if (this.au)
|
if (r.au)
|
||||||
this.au.volume = this.expvol();
|
r.au.volume = r.expvol(r.vol);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.read_order = function () {
|
r.read_order = function () {
|
||||||
var order = [],
|
var order = [],
|
||||||
links = QSA('#files>tbody>tr>td:nth-child(1)>a');
|
links = QSA('#files>tbody>tr>td:nth-child(1)>a');
|
||||||
|
|
||||||
@@ -398,24 +420,71 @@ function MPlayer() {
|
|||||||
|
|
||||||
order.push(tid.slice(1));
|
order.push(tid.slice(1));
|
||||||
}
|
}
|
||||||
this.order = order;
|
r.order = order;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.preload = function (url) {
|
r.fdir = 0;
|
||||||
|
r.fvol = -1;
|
||||||
|
r.ftid = -1;
|
||||||
|
r.ftimer = null;
|
||||||
|
r.fade_in = function () {
|
||||||
|
r.fvol = 0;
|
||||||
|
r.fdir = 0.025;
|
||||||
|
if (r.au) {
|
||||||
|
r.ftid = r.au.tid;
|
||||||
|
r.au.play();
|
||||||
|
mpl.pp();
|
||||||
|
fader();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
r.fade_out = function () {
|
||||||
|
r.fvol = r.vol;
|
||||||
|
r.fdir = -0.05;
|
||||||
|
r.ftid = r.au.tid;
|
||||||
|
fader();
|
||||||
|
};
|
||||||
|
r.stopfade = function (hard) {
|
||||||
|
clearTimeout(r.ftimer);
|
||||||
|
if (hard)
|
||||||
|
r.ftid = -1;
|
||||||
|
}
|
||||||
|
function fader() {
|
||||||
|
r.stopfade();
|
||||||
|
if (!r.au || r.au.tid !== r.ftid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var done = true;
|
||||||
|
r.fvol += r.fdir;
|
||||||
|
if (r.fvol < 0) {
|
||||||
|
r.fvol = 0;
|
||||||
|
r.au.pause();
|
||||||
|
mpl.pp();
|
||||||
|
}
|
||||||
|
else if (r.fvol > r.vol)
|
||||||
|
r.fvol = r.vol;
|
||||||
|
else
|
||||||
|
done = false;
|
||||||
|
|
||||||
|
r.au.volume = r.expvol(r.fvol);
|
||||||
|
if (!done)
|
||||||
|
setTimeout(fader, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
r.preload = function (url) {
|
||||||
var au = null;
|
var au = null;
|
||||||
if (need_ogv_for(url)) {
|
if (need_ogv_for(url)) {
|
||||||
au = mp.au_ogvjs2;
|
au = mp.au_ogvjs2;
|
||||||
if (!au && window['OGVPlayer']) {
|
if (!au && window['OGVPlayer']) {
|
||||||
au = new OGVPlayer();
|
au = new OGVPlayer();
|
||||||
au.preload = "auto";
|
au.preload = "auto";
|
||||||
this.au_ogvjs2 = au;
|
r.au_ogvjs2 = au;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
au = mp.au_native2;
|
au = mp.au_native2;
|
||||||
if (!au) {
|
if (!au) {
|
||||||
au = new Audio();
|
au = new Audio();
|
||||||
au.preload = "auto";
|
au.preload = "auto";
|
||||||
this.au_native2 = au;
|
r.au_native2 = au;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (au) {
|
if (au) {
|
||||||
@@ -453,37 +522,38 @@ function get_np() {
|
|||||||
|
|
||||||
// toggle player widget
|
// toggle player widget
|
||||||
var widget = (function () {
|
var widget = (function () {
|
||||||
var ret = {},
|
var r = {},
|
||||||
widget = ebi('widget'),
|
widget = ebi('widget'),
|
||||||
wtico = ebi('wtico'),
|
wtico = ebi('wtico'),
|
||||||
nptxt = ebi('nptxt'),
|
nptxt = ebi('nptxt'),
|
||||||
npirc = ebi('npirc'),
|
npirc = ebi('npirc'),
|
||||||
touchmode = false,
|
touchmode = false,
|
||||||
side_open = false,
|
|
||||||
was_paused = true;
|
was_paused = true;
|
||||||
|
|
||||||
ret.open = function () {
|
r.is_open = false;
|
||||||
if (side_open)
|
|
||||||
|
r.open = function () {
|
||||||
|
if (r.is_open)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
widget.className = 'open';
|
widget.className = 'open';
|
||||||
side_open = true;
|
r.is_open = true;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
ret.close = function () {
|
r.close = function () {
|
||||||
if (!side_open)
|
if (!r.is_open)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
widget.className = '';
|
widget.className = '';
|
||||||
side_open = false;
|
r.is_open = false;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
ret.toggle = function (e) {
|
r.toggle = function (e) {
|
||||||
ret.open() || ret.close();
|
r.open() || r.close();
|
||||||
ev(e);
|
ev(e);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
ret.paused = function (paused) {
|
r.paused = function (paused) {
|
||||||
if (was_paused != paused) {
|
if (was_paused != paused) {
|
||||||
was_paused = paused;
|
was_paused = paused;
|
||||||
ebi('bplay').innerHTML = paused ? '▶' : '⏸';
|
ebi('bplay').innerHTML = paused ? '▶' : '⏸';
|
||||||
@@ -491,7 +561,7 @@ var widget = (function () {
|
|||||||
};
|
};
|
||||||
wtico.onclick = function (e) {
|
wtico.onclick = function (e) {
|
||||||
if (!touchmode)
|
if (!touchmode)
|
||||||
ret.toggle(e);
|
r.toggle(e);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -522,7 +592,7 @@ var widget = (function () {
|
|||||||
document.body.removeChild(o);
|
document.body.removeChild(o);
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
return ret;
|
return r;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
@@ -568,12 +638,15 @@ var pbar = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.drawbuf = function () {
|
r.drawbuf = function () {
|
||||||
|
var bc = r.buf,
|
||||||
|
bctx = bc.ctx;
|
||||||
|
|
||||||
|
bctx.clearRect(0, 0, bc.w, bc.h);
|
||||||
|
|
||||||
if (!mp.au)
|
if (!mp.au)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var bc = r.buf,
|
var sm = bc.w * 1.0 / mp.au.duration,
|
||||||
bctx = bc.ctx,
|
|
||||||
sm = bc.w * 1.0 / mp.au.duration,
|
|
||||||
gk = bc.h + '' + light;
|
gk = bc.h + '' + light;
|
||||||
|
|
||||||
if (gradh != gk) {
|
if (gradh != gk) {
|
||||||
@@ -581,7 +654,6 @@ var pbar = (function () {
|
|||||||
grad = glossy_grad(bc, 85, [35, 40, 37, 35], light ? [45, 56, 50, 45] : [42, 51, 47, 42]);
|
grad = glossy_grad(bc, 85, [35, 40, 37, 35], light ? [45, 56, 50, 45] : [42, 51, 47, 42]);
|
||||||
}
|
}
|
||||||
bctx.fillStyle = grad;
|
bctx.fillStyle = grad;
|
||||||
bctx.clearRect(0, 0, bc.w, bc.h);
|
|
||||||
for (var a = 0; a < mp.au.buffered.length; a++) {
|
for (var a = 0; a < mp.au.buffered.length; a++) {
|
||||||
var x1 = sm * mp.au.buffered.start(a),
|
var x1 = sm * mp.au.buffered.start(a),
|
||||||
x2 = sm * mp.au.buffered.end(a);
|
x2 = sm * mp.au.buffered.end(a);
|
||||||
@@ -591,15 +663,17 @@ var pbar = (function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
r.drawpos = function () {
|
r.drawpos = function () {
|
||||||
|
var bc = r.buf,
|
||||||
|
pc = r.pos,
|
||||||
|
pctx = pc.ctx;
|
||||||
|
|
||||||
|
pctx.clearRect(0, 0, pc.w, pc.h);
|
||||||
|
|
||||||
if (!mp.au || isNaN(mp.au.duration) || isNaN(mp.au.currentTime))
|
if (!mp.au || isNaN(mp.au.duration) || isNaN(mp.au.currentTime))
|
||||||
return; // not-init || unsupp-codec
|
return; // not-init || unsupp-codec
|
||||||
|
|
||||||
var bc = r.buf,
|
var sm = bc.w * 1.0 / mp.au.duration;
|
||||||
pc = r.pos,
|
|
||||||
pctx = pc.ctx,
|
|
||||||
sm = bc.w * 1.0 / mp.au.duration;
|
|
||||||
|
|
||||||
pctx.clearRect(0, 0, pc.w, pc.h);
|
|
||||||
pctx.fillStyle = light ? 'rgba(0,64,0,0.15)' : 'rgba(204,255,128,0.15)';
|
pctx.fillStyle = light ? 'rgba(0,64,0,0.15)' : 'rgba(204,255,128,0.15)';
|
||||||
for (var p = 1, mins = mp.au.duration / 10; p <= mins; p++)
|
for (var p = 1, mins = mp.au.duration / 10; p <= mins; p++)
|
||||||
pctx.fillRect(Math.floor(sm * p * 10), 0, 2, pc.h);
|
pctx.fillRect(Math.floor(sm * p * 10), 0, 2, pc.h);
|
||||||
@@ -730,9 +804,8 @@ function seek_au_sec(seek) {
|
|||||||
|
|
||||||
mp.au.currentTime = seek;
|
mp.au.currentTime = seek;
|
||||||
|
|
||||||
// ogv.js breaks on .play() during playback
|
if (mp.au.paused)
|
||||||
if (mp.au === mp.au_native)
|
mp.fade_in();
|
||||||
mp.au.play();
|
|
||||||
|
|
||||||
mpui.progress_updater();
|
mpui.progress_updater();
|
||||||
}
|
}
|
||||||
@@ -754,25 +827,29 @@ function next_song(e) {
|
|||||||
}
|
}
|
||||||
function prev_song(e) {
|
function prev_song(e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
|
|
||||||
|
if (mp.au && !mp.au.paused && mp.au.currentTime > 3)
|
||||||
|
return seek_au_sec(0);
|
||||||
|
|
||||||
return song_skip(-1);
|
return song_skip(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function playpause(e) {
|
function playpause(e) {
|
||||||
|
// must be event-chain
|
||||||
ev(e);
|
ev(e);
|
||||||
if (mp.au) {
|
if (mp.au) {
|
||||||
if (mp.au.paused)
|
if (mp.au.paused)
|
||||||
mp.au.play();
|
mp.fade_in();
|
||||||
else
|
else
|
||||||
mp.au.pause();
|
mp.fade_out();
|
||||||
|
|
||||||
mpui.progress_updater();
|
mpui.progress_updater();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
play(0);
|
play(0, true);
|
||||||
|
|
||||||
if (navigator.mediaSession)
|
mpl.pp();
|
||||||
navigator.mediaSession.playbackState = mp.au.paused ? "paused" : "playing";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -781,9 +858,13 @@ function playpause(e) {
|
|||||||
ebi('bplay').onclick = playpause;
|
ebi('bplay').onclick = playpause;
|
||||||
ebi('bprev').onclick = prev_song;
|
ebi('bprev').onclick = prev_song;
|
||||||
ebi('bnext').onclick = next_song;
|
ebi('bnext').onclick = next_song;
|
||||||
ebi('barpos').onclick = function (e) {
|
|
||||||
|
var bar = ebi('barpos');
|
||||||
|
|
||||||
|
bar.onclick = function (e) {
|
||||||
if (!mp.au) {
|
if (!mp.au) {
|
||||||
return play(0);
|
play(0, true);
|
||||||
|
return mp.fade_in();
|
||||||
}
|
}
|
||||||
|
|
||||||
var rect = pbar.buf.can.getBoundingClientRect(),
|
var rect = pbar.buf.can.getBoundingClientRect(),
|
||||||
@@ -791,6 +872,19 @@ function playpause(e) {
|
|||||||
|
|
||||||
seek_au_mul(x * 1.0 / rect.width);
|
seek_au_mul(x * 1.0 / rect.width);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!is_touch)
|
||||||
|
bar.onwheel = function (e) {
|
||||||
|
var dist = Math.sign(e.deltaY) * 10;
|
||||||
|
if (Math.abs(e.deltaY) < 30 && !e.deltaMode)
|
||||||
|
dist = e.deltaY;
|
||||||
|
|
||||||
|
if (!dist || !mp.au)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
seek_au_rel(dist);
|
||||||
|
ev(e);
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
@@ -858,7 +952,12 @@ var mpui = (function () {
|
|||||||
// event from play button next to a file in the list
|
// event from play button next to a file in the list
|
||||||
function ev_play(e) {
|
function ev_play(e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
play(this.getAttribute('id').slice(1));
|
|
||||||
|
var fade = !mp.au || mp.au.paused;
|
||||||
|
play(this.getAttribute('id').slice(1), true);
|
||||||
|
if (fade)
|
||||||
|
mp.fade_in();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1097,13 +1196,18 @@ var audio_eq = (function () {
|
|||||||
|
|
||||||
|
|
||||||
// plays the tid'th audio file on the page
|
// plays the tid'th audio file on the page
|
||||||
function play(tid, seek, call_depth) {
|
function play(tid, is_ev, seek, call_depth) {
|
||||||
if (mp.order.length == 0)
|
if (mp.order.length == 0)
|
||||||
return console.log('no audio found wait what');
|
return console.log('no audio found wait what');
|
||||||
|
|
||||||
|
mp.stopfade(true);
|
||||||
|
|
||||||
var tn = tid;
|
var tn = tid;
|
||||||
if ((tn + '').indexOf('f-') === 0)
|
if ((tn + '').indexOf('f-') === 0) {
|
||||||
tn = mp.order.indexOf(tn);
|
tn = mp.order.indexOf(tn);
|
||||||
|
if (tn < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tn >= mp.order.length) {
|
if (tn >= mp.order.length) {
|
||||||
if (mpl.pb_mode == 'loop-folder') {
|
if (mpl.pb_mode == 'loop-folder') {
|
||||||
@@ -1142,7 +1246,7 @@ function play(tid, seek, call_depth) {
|
|||||||
}
|
}
|
||||||
else if (window['OGVPlayer']) {
|
else if (window['OGVPlayer']) {
|
||||||
mp.au = mp.au_ogvjs = new OGVPlayer();
|
mp.au = mp.au_ogvjs = new OGVPlayer();
|
||||||
attempt_play = false;
|
attempt_play = is_ev;
|
||||||
mp.au.addEventListener('error', evau_error, true);
|
mp.au.addEventListener('error', evau_error, true);
|
||||||
mp.au.addEventListener('progress', pbar.drawpos);
|
mp.au.addEventListener('progress', pbar.drawpos);
|
||||||
mp.au.addEventListener('ended', next_song);
|
mp.au.addEventListener('ended', next_song);
|
||||||
@@ -1155,7 +1259,7 @@ function play(tid, seek, call_depth) {
|
|||||||
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
|
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
|
||||||
|
|
||||||
import_js('/.cpr/deps/ogv.js', function () {
|
import_js('/.cpr/deps/ogv.js', function () {
|
||||||
play(tid, seek, 1);
|
play(tid, false, seek, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1176,7 +1280,7 @@ function play(tid, seek, call_depth) {
|
|||||||
|
|
||||||
mp.au.tid = tid;
|
mp.au.tid = tid;
|
||||||
mp.au.src = url + (url.indexOf('?') < 0 ? '?cache' : '&cache');
|
mp.au.src = url + (url.indexOf('?') < 0 ? '?cache' : '&cache');
|
||||||
mp.au.volume = mp.expvol();
|
mp.au.volume = mp.expvol(mp.vol);
|
||||||
var oid = 'a' + tid;
|
var oid = 'a' + tid;
|
||||||
setclass(oid, 'play act');
|
setclass(oid, 'play act');
|
||||||
var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||||
@@ -1267,7 +1371,8 @@ function show_modal(html) {
|
|||||||
|
|
||||||
|
|
||||||
// hide fullscreen message
|
// hide fullscreen message
|
||||||
function unblocked() {
|
function unblocked(e) {
|
||||||
|
ev(e);
|
||||||
var dom = ebi('blocked');
|
var dom = ebi('blocked');
|
||||||
if (dom)
|
if (dom)
|
||||||
dom.parentNode.removeChild(dom);
|
dom.parentNode.removeChild(dom);
|
||||||
@@ -1282,28 +1387,25 @@ function autoplay_blocked(seek) {
|
|||||||
|
|
||||||
var go = ebi('blk_go'),
|
var go = ebi('blk_go'),
|
||||||
na = ebi('blk_na'),
|
na = ebi('blk_na'),
|
||||||
fn = mp.tracks[mp.au.tid].split(/\//).pop();
|
tid = mp.au.tid,
|
||||||
|
fn = mp.tracks[tid].split(/\//).pop();
|
||||||
|
|
||||||
fn = uricom_dec(fn.replace(/\+/g, ' '))[0];
|
fn = uricom_dec(fn.replace(/\+/g, ' '))[0];
|
||||||
|
|
||||||
go.textContent = 'Play "' + fn + '"';
|
go.textContent = 'Play "' + fn + '"';
|
||||||
go.onclick = function (e) {
|
go.onclick = function (e) {
|
||||||
if (e) e.preventDefault();
|
unblocked(e);
|
||||||
unblocked();
|
// chrome 91 may permanently taint on a failed play()
|
||||||
mp.au.play();
|
// depending on win10 settings or something? idk
|
||||||
if (seek)
|
mp.au_native = mp.au_ogvjs = null;
|
||||||
seek_au_sec(seek);
|
play(tid, true, seek);
|
||||||
else
|
mp.fade_in();
|
||||||
mpui.progress_updater();
|
|
||||||
|
|
||||||
mpl.announce();
|
|
||||||
};
|
};
|
||||||
na.onclick = unblocked;
|
na.onclick = unblocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// autoplay linked track
|
function play_linked() {
|
||||||
(function () {
|
|
||||||
var v = location.hash;
|
var v = location.hash;
|
||||||
if (v && v.indexOf('#af-') === 0) {
|
if (v && v.indexOf('#af-') === 0) {
|
||||||
var id = v.slice(2).split('&');
|
var id = v.slice(2).split('&');
|
||||||
@@ -1317,9 +1419,9 @@ function autoplay_blocked(seek) {
|
|||||||
if (!m)
|
if (!m)
|
||||||
return play(id[0]);
|
return play(id[0]);
|
||||||
|
|
||||||
return play(id[0], parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
|
return play(id[0], false, parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
|
||||||
}
|
}
|
||||||
})();
|
};
|
||||||
|
|
||||||
|
|
||||||
var thegrid = (function () {
|
var thegrid = (function () {
|
||||||
@@ -1425,35 +1527,57 @@ var thegrid = (function () {
|
|||||||
}
|
}
|
||||||
setsz();
|
setsz();
|
||||||
|
|
||||||
function seltgl(e) {
|
function gclick(e) {
|
||||||
if (e && e.ctrlKey)
|
if (e && e.ctrlKey)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ev(e);
|
|
||||||
var oth = ebi(this.getAttribute('ref')),
|
var oth = ebi(this.getAttribute('ref')),
|
||||||
td = oth.parentNode.nextSibling,
|
href = this.getAttribute('href'),
|
||||||
|
aplay = ebi('a' + oth.getAttribute('id')),
|
||||||
|
is_img = /\.(gif|jpe?g|png|webp)(\?|$)/i.test(href),
|
||||||
|
in_tree = null,
|
||||||
|
have_sel = QS('#files tr.sel'),
|
||||||
|
td = oth.closest('td').nextSibling,
|
||||||
tr = td.parentNode;
|
tr = td.parentNode;
|
||||||
|
|
||||||
td.click();
|
if (/\/(\?|$)/.test(href)) {
|
||||||
this.setAttribute('class', tr.getAttribute('class'));
|
var ta = QSA('#treeul a.hl+ul>li>a+a'),
|
||||||
}
|
txt = oth.textContent.slice(0, -1);
|
||||||
|
|
||||||
function bgopen(e) {
|
for (var a = 0, aa = ta.length; a < aa; a++) {
|
||||||
|
if (ta[a].textContent == txt) {
|
||||||
|
in_tree = ta[a];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.sel) {
|
||||||
|
td.click();
|
||||||
|
this.setAttribute('class', tr.getAttribute('class'));
|
||||||
|
}
|
||||||
|
else if (widget.is_open && aplay)
|
||||||
|
aplay.click();
|
||||||
|
|
||||||
|
else if (in_tree && !have_sel)
|
||||||
|
in_tree.click();
|
||||||
|
|
||||||
|
else if (!is_img && have_sel)
|
||||||
|
window.open(href, '_blank');
|
||||||
|
|
||||||
|
else return true;
|
||||||
ev(e);
|
ev(e);
|
||||||
var url = this.getAttribute('href');
|
|
||||||
window.open(url, '_blank');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.loadsel = function () {
|
r.loadsel = function () {
|
||||||
if (r.dirty)
|
if (r.dirty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ths = QSA('#ggrid>a'),
|
var ths = QSA('#ggrid>a');
|
||||||
have_sel = !!QS('#files tr.sel');
|
|
||||||
|
|
||||||
for (var a = 0, aa = ths.length; a < aa; a++) {
|
for (var a = 0, aa = ths.length; a < aa; a++) {
|
||||||
ths[a].onclick = r.sel ? seltgl : have_sel ? bgopen : null;
|
var tr = ebi(ths[a].getAttribute('ref')).closest('tr');
|
||||||
ths[a].setAttribute('class', ebi(ths[a].getAttribute('ref')).parentNode.parentNode.getAttribute('class'));
|
ths[a].setAttribute('class', tr.getAttribute('class'));
|
||||||
}
|
}
|
||||||
var uns = QS('#ggrid a[ref="unsearch"]');
|
var uns = QS('#ggrid a[ref="unsearch"]');
|
||||||
if (uns)
|
if (uns)
|
||||||
@@ -1484,6 +1608,8 @@ var thegrid = (function () {
|
|||||||
|
|
||||||
if (r.thumbs) {
|
if (r.thumbs) {
|
||||||
ihref += (ihref.indexOf('?') === -1 ? '?' : '&') + 'th=' + (have_webp ? 'w' : 'j');
|
ihref += (ihref.indexOf('?') === -1 ? '?' : '&') + 'th=' + (have_webp ? 'w' : 'j');
|
||||||
|
if (href == "#")
|
||||||
|
ihref = '/.cpr/ico/⏏️';
|
||||||
}
|
}
|
||||||
else if (isdir) {
|
else if (isdir) {
|
||||||
ihref = '/.cpr/ico/folder';
|
ihref = '/.cpr/ico/folder';
|
||||||
@@ -1511,6 +1637,11 @@ var thegrid = (function () {
|
|||||||
ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>');
|
ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>');
|
||||||
}
|
}
|
||||||
ebi('ggrid').innerHTML = html.join('\n');
|
ebi('ggrid').innerHTML = html.join('\n');
|
||||||
|
|
||||||
|
var ths = QSA('#ggrid>a');
|
||||||
|
for (var a = 0, aa = ths.length; a < aa; a++)
|
||||||
|
ths[a].onclick = gclick;
|
||||||
|
|
||||||
r.dirty = false;
|
r.dirty = false;
|
||||||
r.bagit();
|
r.bagit();
|
||||||
r.loadsel();
|
r.loadsel();
|
||||||
@@ -1598,19 +1729,25 @@ document.onkeydown = function (e) {
|
|||||||
if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
|
if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.isComposing)
|
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var k = (e.code + ''), pos = -1, n;
|
||||||
|
|
||||||
|
if (e.shiftKey && k != 'KeyA' && k != 'KeyD')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var k = (e.code + ''), pos = -1;
|
|
||||||
if (k.indexOf('Digit') === 0)
|
if (k.indexOf('Digit') === 0)
|
||||||
pos = parseInt(k.slice(-1)) * 0.1;
|
pos = parseInt(k.slice(-1)) * 0.1;
|
||||||
|
|
||||||
if (pos !== -1)
|
if (pos !== -1)
|
||||||
return seek_au_mul(pos) || true;
|
return seek_au_mul(pos) || true;
|
||||||
|
|
||||||
var n = k == 'KeyJ' ? -1 : k == 'KeyL' ? 1 : 0;
|
if (k == 'KeyJ')
|
||||||
if (n !== 0)
|
return prev_song() || true;
|
||||||
return song_skip(n) || true;
|
|
||||||
|
if (k == 'KeyL')
|
||||||
|
return next_song() || true;
|
||||||
|
|
||||||
if (k == 'KeyP')
|
if (k == 'KeyP')
|
||||||
return playpause() || true;
|
return playpause() || true;
|
||||||
@@ -1635,6 +1772,14 @@ document.onkeydown = function (e) {
|
|||||||
if (k == 'KeyT')
|
if (k == 'KeyT')
|
||||||
return ebi('thumbs').click();
|
return ebi('thumbs').click();
|
||||||
|
|
||||||
|
if (!treectl.hidden && (!e.shiftKey || !thegrid.en)) {
|
||||||
|
if (k == 'KeyA')
|
||||||
|
return QS('#twig').click();
|
||||||
|
|
||||||
|
if (k == 'KeyD')
|
||||||
|
return QS('#twobytwo').click();
|
||||||
|
}
|
||||||
|
|
||||||
if (thegrid.en) {
|
if (thegrid.en) {
|
||||||
if (k == 'KeyS')
|
if (k == 'KeyS')
|
||||||
return ebi('gridsel').click();
|
return ebi('gridsel').click();
|
||||||
@@ -2833,8 +2978,10 @@ function reload_mp() {
|
|||||||
mp.au.pause();
|
mp.au.pause();
|
||||||
mp.au = null;
|
mp.au = null;
|
||||||
}
|
}
|
||||||
|
mpl.stop();
|
||||||
widget.close();
|
widget.close();
|
||||||
mp = new MPlayer();
|
mp = new MPlayer();
|
||||||
|
setTimeout(pbar.onresize, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2878,3 +3025,4 @@ function reload_browser(not_mp) {
|
|||||||
reload_browser(true);
|
reload_browser(true);
|
||||||
mukey.render();
|
mukey.render();
|
||||||
msel.render();
|
msel.render();
|
||||||
|
play_linked();
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<div>{{ logues[1] }}</div><br />
|
<div>{{ logues[1] }}</div><br />
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
<h2><a href="{{ url_suf }}{{ url_suf and '&' or '?' }}h">control-panel</a></h2>
|
<h2><a href="/{{ url_suf }}{{ url_suf and '&' or '?' }}h">control-panel</a></h2>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -740,9 +740,17 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
function handshakes_permitted() {
|
function handshakes_permitted() {
|
||||||
var lim = multitask ? 1 : 0;
|
var lim = multitask ? 1 : 0;
|
||||||
return lim >=
|
|
||||||
|
if (lim <
|
||||||
st.todo.upload.length +
|
st.todo.upload.length +
|
||||||
st.busy.upload.length;
|
st.busy.upload.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var cd = st.todo.handshake.length ? st.todo.handshake[0].cooldown : 0;
|
||||||
|
if (cd && cd - Date.now() > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashing_permitted() {
|
function hashing_permitted() {
|
||||||
@@ -1155,6 +1163,15 @@ function up2k_init(subtle) {
|
|||||||
if (rsp.indexOf('<pre>') === 0)
|
if (rsp.indexOf('<pre>') === 0)
|
||||||
rsp = rsp.slice(5);
|
rsp = rsp.slice(5);
|
||||||
|
|
||||||
|
if (rsp.indexOf('rate-limit ') !== -1) {
|
||||||
|
var penalty = rsp.replace(/.*rate-limit /, "").split(' ')[0];
|
||||||
|
console.log("rate-limit: " + penalty);
|
||||||
|
t.cooldown = Date.now() + parseFloat(penalty) * 1000;
|
||||||
|
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
||||||
|
st.todo.handshake.unshift(t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
st.bytes.uploaded += t.size;
|
st.bytes.uploaded += t.size;
|
||||||
if (rsp.indexOf('partial upload exists') !== -1 ||
|
if (rsp.indexOf('partial upload exists') !== -1 ||
|
||||||
rsp.indexOf('file already exists') !== -1) {
|
rsp.indexOf('file already exists') !== -1) {
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ function ev(e) {
|
|||||||
if (e.stopPropagation)
|
if (e.stopPropagation)
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (e.stopImmediatePropagation)
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
e.returnValue = false;
|
e.returnValue = false;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -359,6 +362,15 @@ function get_vpath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_pwd() {
|
||||||
|
var pwd = ('; ' + document.cookie).split('; cppwd=');
|
||||||
|
if (pwd.length < 2)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return pwd[1].split(';')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function unix2iso(ts) {
|
function unix2iso(ts) {
|
||||||
return new Date(ts * 1000).toISOString().replace("T", " ").slice(0, -5);
|
return new Date(ts * 1000).toISOString().replace("T", " ").slice(0, -5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,6 @@
|
|||||||
}
|
}
|
||||||
#ggrid>a[href$="/"]:before {
|
#ggrid>a[href$="/"]:before {
|
||||||
content: '📂';
|
content: '📂';
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
margin: -.1em -.4em;
|
|
||||||
text-shadow: 0 0 .1em #000;
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -27,8 +22,11 @@
|
|||||||
#ggrid>a:before {
|
#ggrid>a:before {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: -.1em -.4em;
|
padding: .3em 0;
|
||||||
|
margin: -.4em;
|
||||||
text-shadow: 0 0 .1em #000;
|
text-shadow: 0 0 .1em #000;
|
||||||
|
background: linear-gradient(135deg,rgba(255,255,255,0) 50%,rgba(255,255,255,0.2));
|
||||||
|
border-radius: .3em;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,15 @@ cat warks | while IFS= read -r x; do sqlite3 up2k.db "delete from mt where w = '
|
|||||||
# dump all dbs
|
# dump all dbs
|
||||||
find -iname up2k.db | while IFS= read -r x; do sqlite3 "$x" 'select substr(w,1,12), rd, fn from up' | sed -r 's/\|/ \| /g' | while IFS= read -r y; do printf '%s | %s\n' "$x" "$y"; done; done
|
find -iname up2k.db | while IFS= read -r x; do sqlite3 "$x" 'select substr(w,1,12), rd, fn from up' | sed -r 's/\|/ \| /g' | while IFS= read -r y; do printf '%s | %s\n' "$x" "$y"; done; done
|
||||||
|
|
||||||
|
# unschedule mtp scan for all files somewhere under "enc/"
|
||||||
|
sqlite3 -readonly up2k.db 'select substr(up.w,1,16) from up inner join mt on mt.w = substr(up.w,1,16) where rd like "enc/%" and +mt.k = "t:mtp"' > keys; awk '{printf "delete from mt where w = \"%s\" and +k = \"t:mtp\";\n", $0}' <keys | tee /dev/stderr | sqlite3 up2k.db
|
||||||
|
|
||||||
|
# compare metadata key "key" between two databases
|
||||||
|
sqlite3 -readonly up2k.db.key-full 'select w, v from mt where k = "key" order by w' > k1; sqlite3 -readonly up2k.db 'select w, v from mt where k = "key" order by w' > k2; ok=0; ng=0; while IFS='|' read w k2; do k1="$(grep -E "^$w" k1 | sed -r 's/.*\|//')"; [ "$k1" = "$k2" ] && ok=$((ok+1)) || { ng=$((ng+1)); printf '%3s %3s %s\n' "$k1" "$k2" "$(sqlite3 -readonly up2k.db.key-full "select * from up where substr(w,1,16) = '$w'" | sed -r 's/\|/ | /g')"; }; done < <(cat k2); echo "match $ok diff $ng"
|
||||||
|
|
||||||
|
# actually this is much better
|
||||||
|
sqlite3 -readonly up2k.db.key-full 'select w, v from mt where k = "key" order by w' > k1; sqlite3 -readonly up2k.db 'select mt.w, mt.v, up.rd, up.fn from mt inner join up on mt.w = substr(up.w,1,16) where mt.k = "key" order by up.rd, up.fn' > k2; ok=0; ng=0; while IFS='|' read w k2 path; do k1="$(grep -E "^$w" k1 | sed -r 's/.*\|//')"; [ "$k1" = "$k2" ] && ok=$((ok+1)) || { ng=$((ng+1)); printf '%3s %3s %s\n' "$k1" "$k2" "$path"; }; done < <(cat k2); echo "match $ok diff $ng"
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## media
|
## media
|
||||||
@@ -200,3 +209,4 @@ mk() { rm -rf /tmp/foo; sudo -u ed bash -c 'mkdir /tmp/foo; echo hi > /tmp/foo/b
|
|||||||
mk && t0="$(date)" && while true; do date -s "$(date '+ 1 hour')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
|
mk && t0="$(date)" && while true; do date -s "$(date '+ 1 hour')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
|
||||||
mk && sudo -u ed flock /tmp/foo sleep 40 & sleep 1; ps aux | grep -E 'sleep 40$' && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
|
mk && sudo -u ed flock /tmp/foo sleep 40 & sleep 1; ps aux | grep -E 'sleep 40$' && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
|
||||||
mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; tar -cf/dev/null /tmp/foo; done; echo "$t0"
|
mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; tar -cf/dev/null /tmp/foo; done; echo "$t0"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user