mirror of
				https://github.com/9001/copyparty.git
				synced 2025-11-03 21:43:12 +00:00 
			
		
		
		
	Compare commits
	
		
			40 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ff8313d0fb | ||
| 
						 | 
					765294c263 | ||
| 
						 | 
					d6b5351207 | ||
| 
						 | 
					a2009bcc6b | ||
| 
						 | 
					12709a8a0a | ||
| 
						 | 
					c055baefd2 | ||
| 
						 | 
					56522599b5 | ||
| 
						 | 
					664f53b75d | ||
| 
						 | 
					87200d9f10 | ||
| 
						 | 
					5c3d0b6520 | ||
| 
						 | 
					bd49979f4a | ||
| 
						 | 
					7e606cdd9f | ||
| 
						 | 
					8b4b7fa794 | ||
| 
						 | 
					05345ddf8b | ||
| 
						 | 
					66adb470ad | ||
| 
						 | 
					e15c8fd146 | ||
| 
						 | 
					0f09b98a39 | ||
| 
						 | 
					b4d6f4e24d | ||
| 
						 | 
					3217fa625b | ||
| 
						 | 
					e719ff8a47 | ||
| 
						 | 
					9fcf528d45 | ||
| 
						 | 
					1ddbf5a158 | ||
| 
						 | 
					64bf4574b0 | ||
| 
						 | 
					5649d26077 | ||
| 
						 | 
					92f923effe | ||
| 
						 | 
					0d46d548b9 | ||
| 
						 | 
					062df3f0c3 | ||
| 
						 | 
					789fb53b8e | ||
| 
						 | 
					351db5a18f | ||
| 
						 | 
					aabbd271c8 | ||
| 
						 | 
					aae8e0171e | ||
| 
						 | 
					45827a2458 | ||
| 
						 | 
					726030296f | ||
| 
						 | 
					6659ab3881 | ||
| 
						 | 
					c6a103609e | ||
| 
						 | 
					c6b3f035e5 | ||
| 
						 | 
					2b0a7e378e | ||
| 
						 | 
					b75ce909c8 | ||
| 
						 | 
					229c3f5dab | ||
| 
						 | 
					ec73094506 | 
							
								
								
									
										26
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								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:
 | 
				
			||||||
@@ -139,6 +154,8 @@ summary: all planned features work! now please enjoy the bloatening
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
 | 
					* all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
 | 
				
			||||||
* cannot mount something at `/d1/d2/d3` unless `d2` exists inside `d1`
 | 
					* cannot mount something at `/d1/d2/d3` unless `d2` exists inside `d1`
 | 
				
			||||||
 | 
					* dupe files will not have metadata (audio tags etc) displayed in the file listing
 | 
				
			||||||
 | 
					  * because they don't get `up` entries in the db (probably best fix) and `tx_browser` does not `lstat`
 | 
				
			||||||
* probably more, pls let me know
 | 
					* probably more, pls let me know
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## not my bugs
 | 
					## not my bugs
 | 
				
			||||||
@@ -178,9 +195,11 @@ the browser has the following hotkeys
 | 
				
			|||||||
  * `U/O` skip 10sec back/forward
 | 
					  * `U/O` skip 10sec back/forward
 | 
				
			||||||
  * `J/L` prev/next song
 | 
					  * `J/L` prev/next song
 | 
				
			||||||
  * `P` 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
 | 
				
			||||||
@@ -198,6 +217,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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -594,13 +615,14 @@ in the `scripts` folder:
 | 
				
			|||||||
roughly sorted by priority
 | 
					roughly sorted by priority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* readme.md as epilogue
 | 
					* readme.md as epilogue
 | 
				
			||||||
* single sha512 across all up2k chunks? maybe
 | 
					 | 
				
			||||||
* reduce up2k roundtrips
 | 
					* reduce up2k roundtrips
 | 
				
			||||||
  * start from a chunk index and just go
 | 
					  * start from a chunk index and just go
 | 
				
			||||||
  * terminate client on bad data
 | 
					  * terminate client on bad data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
discarded ideas
 | 
					discarded ideas
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* single sha512 across all up2k chunks?
 | 
				
			||||||
 | 
					  * crypto.subtle cannot into streaming, would have to use hashwasm, expensive
 | 
				
			||||||
* separate sqlite table per tag
 | 
					* separate sqlite table per tag
 | 
				
			||||||
  * performance fixed by skipping some indexes (`+mt.k`)
 | 
					  * performance fixed by skipping some indexes (`+mt.k`)
 | 
				
			||||||
* audio fingerprinting
 | 
					* audio fingerprinting
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -305,6 +305,7 @@ def run_argparse(argv, formatter):
 | 
				
			|||||||
    ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
 | 
					    ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
 | 
				
			||||||
    ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval")
 | 
					    ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval")
 | 
				
			||||||
    ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
 | 
					    ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
 | 
				
			||||||
 | 
					    ap2.add_argument("--th-covers", metavar="N,N", type=str, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat for")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ap2 = ap.add_argument_group('database options')
 | 
					    ap2 = ap.add_argument_group('database options')
 | 
				
			||||||
    ap2.add_argument("-e2d", action="store_true", help="enable up2k database")
 | 
					    ap2.add_argument("-e2d", action="store_true", help="enable up2k database")
 | 
				
			||||||
@@ -323,6 +324,9 @@ def run_argparse(argv, formatter):
 | 
				
			|||||||
    ap2.add_argument("-mtp", metavar="M=[f,]bin", action="append", type=str, help="read tag M using bin")
 | 
					    ap2.add_argument("-mtp", metavar="M=[f,]bin", action="append", type=str, help="read tag M using bin")
 | 
				
			||||||
    ap2.add_argument("--srch-time", metavar="SEC", type=int, default=30, help="search deadline")
 | 
					    ap2.add_argument("--srch-time", metavar="SEC", type=int, default=30, help="search deadline")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ap2 = ap.add_argument_group('video streaming options')
 | 
				
			||||||
 | 
					    ap2.add_argument("--vcr", action="store_true", help="enable video streaming")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ap2 = ap.add_argument_group('appearance options')
 | 
					    ap2 = ap.add_argument_group('appearance options')
 | 
				
			||||||
    ap2.add_argument("--css-browser", metavar="L", help="URL to additional CSS to include")
 | 
					    ap2.add_argument("--css-browser", metavar="L", help="URL to additional CSS to include")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
# coding: utf-8
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = (0, 11, 25)
 | 
					VERSION = (0, 11, 29)
 | 
				
			||||||
CODENAME = "the grid"
 | 
					CODENAME = "the grid"
 | 
				
			||||||
BUILD_DT = (2021, 6, 25)
 | 
					BUILD_DT = (2021, 6, 30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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,8 +693,10 @@ 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()]
 | 
					            pwds = [re.escape(x) for x in self.iuser.keys()]
 | 
				
			||||||
            self.re_pwd = re.compile("=(" + "|".join(pwds) + ")([]&; ]|$)")
 | 
					            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})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,8 @@ from .util import *  # noqa  # pylint: disable=unused-wildcard-import
 | 
				
			|||||||
from .authsrv import AuthSrv
 | 
					from .authsrv import AuthSrv
 | 
				
			||||||
from .szip import StreamZip
 | 
					from .szip import StreamZip
 | 
				
			||||||
from .star import StreamTar
 | 
					from .star import StreamTar
 | 
				
			||||||
 | 
					from .vcr import VCR_Direct
 | 
				
			||||||
 | 
					from .th_srv import FMT_FF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if not PY2:
 | 
					if not PY2:
 | 
				
			||||||
    unicode = str
 | 
					    unicode = str
 | 
				
			||||||
@@ -55,7 +57,7 @@ class HttpCli(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def log(self, msg, c=0):
 | 
					    def log(self, msg, c=0):
 | 
				
			||||||
        ptn = self.asrv.re_pwd
 | 
					        ptn = self.asrv.re_pwd
 | 
				
			||||||
        if ptn.search(msg):
 | 
					        if ptn and ptn.search(msg):
 | 
				
			||||||
            msg = ptn.sub(self.unpwd, msg)
 | 
					            msg = ptn.sub(self.unpwd, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.log_func(self.log_src, msg, c)
 | 
					        self.log_func(self.log_src, msg, c)
 | 
				
			||||||
@@ -72,9 +74,13 @@ class HttpCli(object):
 | 
				
			|||||||
        if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
 | 
					        if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
 | 
				
			||||||
            raise Exception("that was close")
 | 
					            raise Exception("that was close")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def j2(self, name, **kwargs):
 | 
					    def j2(self, name, **ka):
 | 
				
			||||||
        tpl = self.conn.hsrv.j2[name]
 | 
					        tpl = self.conn.hsrv.j2[name]
 | 
				
			||||||
        return tpl.render(**kwargs) if kwargs else tpl
 | 
					        if ka:
 | 
				
			||||||
 | 
					            ka["ts"] = self.conn.hsrv.cachebuster()
 | 
				
			||||||
 | 
					            return tpl.render(**ka)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return tpl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
        """returns true if connection can be reused"""
 | 
					        """returns true if connection can be reused"""
 | 
				
			||||||
@@ -181,6 +187,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:
 | 
				
			||||||
@@ -222,7 +231,9 @@ class HttpCli(object):
 | 
				
			|||||||
    def send_headers(self, length, status=200, mime=None, headers={}):
 | 
					    def send_headers(self, length, status=200, mime=None, headers={}):
 | 
				
			||||||
        response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])]
 | 
					        response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if length is not None:
 | 
					        if length is None:
 | 
				
			||||||
 | 
					            self.keepalive = False
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
            response.append("Content-Length: " + unicode(length))
 | 
					            response.append("Content-Length: " + unicode(length))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # close if unknown length, otherwise take client's preference
 | 
					        # close if unknown length, otherwise take client's preference
 | 
				
			||||||
@@ -496,7 +507,7 @@ class HttpCli(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        spd1 = get_spd(nbytes, self.t0)
 | 
					        spd1 = get_spd(nbytes, self.t0)
 | 
				
			||||||
        spd2 = get_spd(self.conn.nbyte, self.conn.t0)
 | 
					        spd2 = get_spd(self.conn.nbyte, self.conn.t0)
 | 
				
			||||||
        return spd1 + " " + spd2
 | 
					        return "{} {} n{}".format(spd1, spd2, self.conn.nreq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle_post_multipart(self):
 | 
					    def handle_post_multipart(self):
 | 
				
			||||||
        self.parser = MultipartParser(self.log, self.sr, self.headers)
 | 
					        self.parser = MultipartParser(self.log, self.sr, self.headers)
 | 
				
			||||||
@@ -758,6 +769,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)
 | 
				
			||||||
@@ -768,9 +785,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)
 | 
				
			||||||
@@ -1376,6 +1391,7 @@ class HttpCli(object):
 | 
				
			|||||||
            "md_plug": "true" if self.args.emp else "false",
 | 
					            "md_plug": "true" if self.args.emp else "false",
 | 
				
			||||||
            "md_chk_rate": self.args.mcr,
 | 
					            "md_chk_rate": self.args.mcr,
 | 
				
			||||||
            "md": boundary,
 | 
					            "md": boundary,
 | 
				
			||||||
 | 
					            "ts": self.conn.hsrv.cachebuster(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        html = template.render(**targs).encode("utf-8", "replace")
 | 
					        html = template.render(**targs).encode("utf-8", "replace")
 | 
				
			||||||
        html = html.split(boundary.encode("utf-8"))
 | 
					        html = html.split(boundary.encode("utf-8"))
 | 
				
			||||||
@@ -1551,11 +1567,20 @@ class HttpCli(object):
 | 
				
			|||||||
            if rem.startswith(".hist/up2k."):
 | 
					            if rem.startswith(".hist/up2k."):
 | 
				
			||||||
                raise Pebkac(403)
 | 
					                raise Pebkac(403)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if "vcr" in self.uparam:
 | 
				
			||||||
 | 
					                ext = abspath.rsplit(".")[-1]
 | 
				
			||||||
 | 
					                if not self.args.vcr or ext not in FMT_FF:
 | 
				
			||||||
 | 
					                    raise Pebkac(403)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                vcr = VCR_Direct(self, abspath)
 | 
				
			||||||
 | 
					                vcr.run()
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            is_dir = stat.S_ISDIR(st.st_mode)
 | 
					            is_dir = stat.S_ISDIR(st.st_mode)
 | 
				
			||||||
            th_fmt = self.uparam.get("th")
 | 
					            th_fmt = self.uparam.get("th")
 | 
				
			||||||
            if th_fmt is not None:
 | 
					            if th_fmt is not None:
 | 
				
			||||||
                if is_dir:
 | 
					                if is_dir:
 | 
				
			||||||
                    for fn in ["folder.png", "folder.jpg"]:
 | 
					                    for fn in self.args.th_covers.split(","):
 | 
				
			||||||
                        fp = os.path.join(abspath, fn)
 | 
					                        fp = os.path.join(abspath, fn)
 | 
				
			||||||
                        if os.path.exists(fp):
 | 
					                        if os.path.exists(fp):
 | 
				
			||||||
                            vrem = "{}/{}".format(vrem.rstrip("/"), fn)
 | 
					                            vrem = "{}/{}".format(vrem.rstrip("/"), fn)
 | 
				
			||||||
@@ -1619,7 +1644,6 @@ class HttpCli(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        url_suf = self.urlq()
 | 
					        url_suf = self.urlq()
 | 
				
			||||||
        is_ls = "ls" in self.uparam
 | 
					        is_ls = "ls" in self.uparam
 | 
				
			||||||
        ts = ""  # "?{}".format(time.time())
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tpl = "browser"
 | 
					        tpl = "browser"
 | 
				
			||||||
        if "b" in self.uparam:
 | 
					        if "b" in self.uparam:
 | 
				
			||||||
@@ -1644,7 +1668,6 @@ class HttpCli(object):
 | 
				
			|||||||
            "vdir": quotep(self.vpath),
 | 
					            "vdir": quotep(self.vpath),
 | 
				
			||||||
            "vpnodes": vpnodes,
 | 
					            "vpnodes": vpnodes,
 | 
				
			||||||
            "files": [],
 | 
					            "files": [],
 | 
				
			||||||
            "ts": ts,
 | 
					 | 
				
			||||||
            "perms": json.dumps(perms),
 | 
					            "perms": json.dumps(perms),
 | 
				
			||||||
            "taglist": [],
 | 
					            "taglist": [],
 | 
				
			||||||
            "tag_order": [],
 | 
					            "tag_order": [],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ class HttpConn(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.t0 = time.time()
 | 
					        self.t0 = time.time()
 | 
				
			||||||
        self.stopping = False
 | 
					        self.stopping = False
 | 
				
			||||||
 | 
					        self.nreq = 0
 | 
				
			||||||
        self.nbyte = 0
 | 
					        self.nbyte = 0
 | 
				
			||||||
        self.workload = 0
 | 
					        self.workload = 0
 | 
				
			||||||
        self.u2idx = None
 | 
					        self.u2idx = None
 | 
				
			||||||
@@ -188,6 +189,7 @@ class HttpConn(object):
 | 
				
			|||||||
                if self.workload >= 2 ** 31:
 | 
					                if self.workload >= 2 ** 31:
 | 
				
			||||||
                    self.workload = 100
 | 
					                    self.workload = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.nreq += 1
 | 
				
			||||||
            cli = HttpCli(self)
 | 
					            cli = HttpCli(self)
 | 
				
			||||||
            if not cli.run():
 | 
					            if not cli.run():
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ from __future__ import print_function, unicode_literals
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					import base64
 | 
				
			||||||
 | 
					import struct
 | 
				
			||||||
import socket
 | 
					import socket
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +27,6 @@ except ImportError:
 | 
				
			|||||||
    sys.exit(1)
 | 
					    sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .__init__ import E, MACOS
 | 
					from .__init__ import E, MACOS
 | 
				
			||||||
from .authsrv import AuthSrv
 | 
					 | 
				
			||||||
from .httpconn import HttpConn
 | 
					from .httpconn import HttpConn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,6 +49,8 @@ class HttpSrv(object):
 | 
				
			|||||||
        self.clients = {}
 | 
					        self.clients = {}
 | 
				
			||||||
        self.workload = 0
 | 
					        self.workload = 0
 | 
				
			||||||
        self.workload_thr_alive = False
 | 
					        self.workload_thr_alive = False
 | 
				
			||||||
 | 
					        self.cb_ts = 0
 | 
				
			||||||
 | 
					        self.cb_v = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        env = jinja2.Environment()
 | 
					        env = jinja2.Environment()
 | 
				
			||||||
        env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
 | 
					        env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
 | 
				
			||||||
@@ -138,11 +141,12 @@ class HttpSrv(object):
 | 
				
			|||||||
                        "shut({}): {}".format(fno, ex),
 | 
					                        "shut({}): {}".format(fno, ex),
 | 
				
			||||||
                        c="1;30",
 | 
					                        c="1;30",
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                if ex.errno not in [10038, 10054, 107, 57, 9]:
 | 
					                if ex.errno not in [10038, 10054, 107, 57, 49, 9]:
 | 
				
			||||||
                    # 10038 No longer considered a socket
 | 
					                    # 10038 No longer considered a socket
 | 
				
			||||||
                    # 10054 Foribly closed by remote
 | 
					                    # 10054 Foribly closed by remote
 | 
				
			||||||
                    #   107 Transport endpoint not connected
 | 
					                    #   107 Transport endpoint not connected
 | 
				
			||||||
                    #    57 Socket is not connected
 | 
					                    #    57 Socket is not connected
 | 
				
			||||||
 | 
					                    #    49 Can't assign requested address (wifi down)
 | 
				
			||||||
                    #     9 Bad file descriptor
 | 
					                    #     9 Bad file descriptor
 | 
				
			||||||
                    raise
 | 
					                    raise
 | 
				
			||||||
            finally:
 | 
					            finally:
 | 
				
			||||||
@@ -177,3 +181,25 @@ class HttpSrv(object):
 | 
				
			|||||||
                    self.clients[cli] = now
 | 
					                    self.clients[cli] = now
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.workload = total
 | 
					            self.workload = total
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cachebuster(self):
 | 
				
			||||||
 | 
					        if time.time() - self.cb_ts < 1:
 | 
				
			||||||
 | 
					            return self.cb_v
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.mutex:
 | 
				
			||||||
 | 
					            if time.time() - self.cb_ts < 1:
 | 
				
			||||||
 | 
					                return self.cb_v
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            v = E.t0
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                with os.scandir(os.path.join(E.mod, "web")) as dh:
 | 
				
			||||||
 | 
					                    for fh in dh:
 | 
				
			||||||
 | 
					                        inf = fh.stat(follow_symlinks=False)
 | 
				
			||||||
 | 
					                        v = max(v, inf.st_mtime)
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            v = base64.urlsafe_b64encode(struct.pack(">xxL", int(v)))
 | 
				
			||||||
 | 
					            self.cb_v = v.decode("ascii")[-4:]
 | 
				
			||||||
 | 
					            self.cb_ts = time.time()
 | 
				
			||||||
 | 
					            return self.cb_v
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1019,7 +1019,8 @@ class Up2k(object):
 | 
				
			|||||||
                                break
 | 
					                                break
 | 
				
			||||||
                        except:
 | 
					                        except:
 | 
				
			||||||
                            # missing; restart
 | 
					                            # missing; restart
 | 
				
			||||||
                            job = None
 | 
					                            if not self.args.nw:
 | 
				
			||||||
 | 
					                                job = None
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    # file contents match, but not the path
 | 
					                    # file contents match, but not the path
 | 
				
			||||||
@@ -1089,6 +1090,9 @@ class Up2k(object):
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _untaken(self, fdir, fname, ts, ip):
 | 
					    def _untaken(self, fdir, fname, ts, ip):
 | 
				
			||||||
 | 
					        if self.args.nw:
 | 
				
			||||||
 | 
					            return fname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO broker which avoid this race and
 | 
					        # TODO broker which avoid this race and
 | 
				
			||||||
        # provides a new filename if taken (same as bup)
 | 
					        # provides a new filename if taken (same as bup)
 | 
				
			||||||
        suffix = ".{:.6f}-{}".format(ts, ip)
 | 
					        suffix = ".{:.6f}-{}".format(ts, ip)
 | 
				
			||||||
@@ -1098,6 +1102,9 @@ class Up2k(object):
 | 
				
			|||||||
    def _symlink(self, src, dst):
 | 
					    def _symlink(self, src, dst):
 | 
				
			||||||
        # TODO store this in linktab so we never delete src if there are links to it
 | 
					        # TODO store this in linktab so we never delete src if there are links to it
 | 
				
			||||||
        self.log("linking dupe:\n  {0}\n  {1}".format(src, dst))
 | 
					        self.log("linking dupe:\n  {0}\n  {1}".format(src, dst))
 | 
				
			||||||
 | 
					        if self.args.nw:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            lsrc = src
 | 
					            lsrc = src
 | 
				
			||||||
            ldst = dst
 | 
					            ldst = dst
 | 
				
			||||||
@@ -1175,6 +1182,10 @@ class Up2k(object):
 | 
				
			|||||||
            if ret > 0:
 | 
					            if ret > 0:
 | 
				
			||||||
                return ret, src
 | 
					                return ret, src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if self.args.nw:
 | 
				
			||||||
 | 
					                # del self.registry[ptop][wark]
 | 
				
			||||||
 | 
					                return ret, dst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            atomic_move(src, dst)
 | 
					            atomic_move(src, dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ANYWIN:
 | 
					            if ANYWIN:
 | 
				
			||||||
@@ -1284,6 +1295,10 @@ class Up2k(object):
 | 
				
			|||||||
        if self.args.dotpart:
 | 
					        if self.args.dotpart:
 | 
				
			||||||
            tnam = "." + tnam
 | 
					            tnam = "." + tnam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.args.nw:
 | 
				
			||||||
 | 
					            job["tnam"] = tnam
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
 | 
					        suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
 | 
				
			||||||
        with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
 | 
					        with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
 | 
				
			||||||
            f, job["tnam"] = f["orz"]
 | 
					            f, job["tnam"] = f["orz"]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										80
									
								
								copyparty/vcr.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								copyparty/vcr.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import print_function, unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import shlex
 | 
				
			||||||
 | 
					import subprocess as sp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .__init__ import PY2
 | 
				
			||||||
 | 
					from .util import fsenc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VCR_Direct(object):
 | 
				
			||||||
 | 
					    def __init__(self, cli, fpath):
 | 
				
			||||||
 | 
					        self.cli = cli
 | 
				
			||||||
 | 
					        self.fpath = fpath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.log_func = cli.log_func
 | 
				
			||||||
 | 
					        self.log_src = cli.log_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def log(self, msg, c=0):
 | 
				
			||||||
 | 
					        self.log_func(self.log_src, "vcr: {}".format(msg), c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        opts = self.cli.uparam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # fmt: off
 | 
				
			||||||
 | 
					        cmd = [
 | 
				
			||||||
 | 
					            "ffmpeg",
 | 
				
			||||||
 | 
					            "-nostdin",
 | 
				
			||||||
 | 
					            "-hide_banner",
 | 
				
			||||||
 | 
					            "-v", "warning",
 | 
				
			||||||
 | 
					            "-i", fsenc(self.fpath),
 | 
				
			||||||
 | 
					            "-vf", "scale=640:-4",
 | 
				
			||||||
 | 
					            "-c:a", "libopus",
 | 
				
			||||||
 | 
					            "-b:a", "128k",
 | 
				
			||||||
 | 
					            "-c:v", "libvpx",
 | 
				
			||||||
 | 
					            "-deadline", "realtime",
 | 
				
			||||||
 | 
					            "-row-mt", "1"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        # fmt: on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "ss" in opts:
 | 
				
			||||||
 | 
					            cmd.extend(["-ss", opts["ss"]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "crf" in opts:
 | 
				
			||||||
 | 
					            cmd.extend(["-b:v", "0", "-crf", opts["crf"]])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            cmd.extend(["-b:v", "{}M".format(opts.get("mbps", 1.2))])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cmd.extend(["-f", "webm", "-"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        comp = str if not PY2 else unicode
 | 
				
			||||||
 | 
					        cmd = [x.encode("utf-8") if isinstance(x, comp) else x for x in cmd]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.log(" ".join([shlex.quote(x.decode("utf-8", "replace")) for x in cmd]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        p = sp.Popen(cmd, stdout=sp.PIPE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cli.send_headers(None, mime="video/webm")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fails = 0
 | 
				
			||||||
 | 
					        while True:
 | 
				
			||||||
 | 
					            self.log("read")
 | 
				
			||||||
 | 
					            buf = p.stdout.read(1024 * 4)
 | 
				
			||||||
 | 
					            if not buf:
 | 
				
			||||||
 | 
					                fails += 1
 | 
				
			||||||
 | 
					                if p.poll() is not None or fails > 30:
 | 
				
			||||||
 | 
					                    self.log("ffmpeg exited")
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                time.sleep(0.1)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fails = 0
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.cli.s.sendall(buf)
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                self.log("client disconnected")
 | 
				
			||||||
 | 
					                p.kill()
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
@@ -119,7 +119,7 @@ window.baguetteBox = (function () {
 | 
				
			|||||||
            var gallery = [];
 | 
					            var gallery = [];
 | 
				
			||||||
            [].forEach.call(tagsNodeList, function (imageElement, imageIndex) {
 | 
					            [].forEach.call(tagsNodeList, function (imageElement, imageIndex) {
 | 
				
			||||||
                var imageElementClickHandler = function (event) {
 | 
					                var imageElementClickHandler = function (event) {
 | 
				
			||||||
                    if (event && event.ctrlKey)
 | 
					                    if (event && (event.ctrlKey || event.metaKey))
 | 
				
			||||||
                        return true;
 | 
					                        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    event.preventDefault ? event.preventDefault() : event.returnValue = false;
 | 
					                    event.preventDefault ? event.preventDefault() : event.returnValue = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -607,7 +607,7 @@ input.eq_gain {
 | 
				
			|||||||
#srch_q {
 | 
					#srch_q {
 | 
				
			||||||
	white-space: pre;
 | 
						white-space: pre;
 | 
				
			||||||
	color: #f80;
 | 
						color: #f80;
 | 
				
			||||||
	height: 1em;
 | 
						min-height: 1em;
 | 
				
			||||||
	margin: .2em 0 -1em 1.6em;
 | 
						margin: .2em 0 -1em 1.6em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#tq_raw {
 | 
					#tq_raw {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,10 @@
 | 
				
			|||||||
	<title>⇆🎉 {{ title }}</title>
 | 
						<title>⇆🎉 {{ title }}</title>
 | 
				
			||||||
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
						<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
	<meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
						<meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
				
			||||||
	<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css{{ ts }}">
 | 
						<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css?_={{ ts }}">
 | 
				
			||||||
	<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css{{ ts }}">
 | 
						<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css?_={{ ts }}">
 | 
				
			||||||
	{%- if css %}
 | 
						{%- if css %}
 | 
				
			||||||
	<link rel="stylesheet" type="text/css" media="screen" href="{{ css }}{{ ts }}">
 | 
						<link rel="stylesheet" type="text/css" media="screen" href="{{ css }}?_={{ ts }}">
 | 
				
			||||||
	{%- endif %}
 | 
						{%- endif %}
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,9 +127,9 @@
 | 
				
			|||||||
			have_tags_idx = {{ have_tags_idx|tojson }},
 | 
								have_tags_idx = {{ have_tags_idx|tojson }},
 | 
				
			||||||
			have_zip = {{ have_zip|tojson }};
 | 
								have_zip = {{ have_zip|tojson }};
 | 
				
			||||||
	</script>
 | 
						</script>
 | 
				
			||||||
	<script src="/.cpr/util.js{{ ts }}"></script>
 | 
						<script src="/.cpr/util.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/browser.js{{ ts }}"></script>
 | 
						<script src="/.cpr/browser.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/up2k.js{{ ts }}"></script>
 | 
						<script src="/.cpr/up2k.js?_={{ ts }}"></script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ ebi('op_up2k').innerHTML = (
 | 
				
			|||||||
	'	<tr>\n' +
 | 
						'	<tr>\n' +
 | 
				
			||||||
	'		<td>\n' +
 | 
						'		<td>\n' +
 | 
				
			||||||
	'			<a href="#" id="nthread_sub">–</a><input\n' +
 | 
						'			<a href="#" id="nthread_sub">–</a><input\n' +
 | 
				
			||||||
	'				class="txtbox" id="nthread" value="2"/><a\n' +
 | 
						'				class="txtbox" id="nthread" value="2" tt="pause uploads by setting it to 0"/><a\n' +
 | 
				
			||||||
	'				href="#" id="nthread_add">+</a><br /> \n' +
 | 
						'				href="#" id="nthread_add">+</a><br /> \n' +
 | 
				
			||||||
	'		</td>\n' +
 | 
						'		</td>\n' +
 | 
				
			||||||
	'	</tr>\n' +
 | 
						'	</tr>\n' +
 | 
				
			||||||
@@ -237,13 +237,17 @@ var mpl = (function () {
 | 
				
			|||||||
		'<a href="#" class="tgl btn" tt="load the next folder and continue">📂 next-folder</a>' +
 | 
							'<a href="#" class="tgl btn" tt="load the next folder and continue">📂 next-folder</a>' +
 | 
				
			||||||
		'</div></div>' +
 | 
							'</div></div>' +
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							'<div><h3>tint</h3><div>' +
 | 
				
			||||||
 | 
							'<input type="text" id="pb_tint" size="3" value="0" tt="background level (0-100) on the seekbar$Nto make buffering less distracting" />' +
 | 
				
			||||||
 | 
							'</div></div>' +
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		'<div><h3>audio equalizer</h3><div id="audio_eq"></div></div>');
 | 
							'<div><h3>audio equalizer</h3><div id="audio_eq"></div></div>');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r = {
 | 
						var r = {
 | 
				
			||||||
		"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', true) && 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 +294,19 @@ var mpl = (function () {
 | 
				
			|||||||
		draw_pb_mode();
 | 
							draw_pb_mode();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function set_tint() {
 | 
				
			||||||
 | 
							var tint = icfg_get('pb_tint', 0);
 | 
				
			||||||
 | 
							if (!tint)
 | 
				
			||||||
 | 
								ebi('barbuf').style.removeProperty('background');
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								ebi('barbuf').style.background = 'rgba(126,163,75,' + (tint / 100.0) + ')';
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ebi('pb_tint').oninput = function (e) {
 | 
				
			||||||
 | 
							swrite('pb_tint', this.value);
 | 
				
			||||||
 | 
							set_tint();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						set_tint();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r.pp = function () {
 | 
						r.pp = function () {
 | 
				
			||||||
		if (!r.os_ctl)
 | 
							if (!r.os_ctl)
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
@@ -522,37 +539,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 ? '▶' : '⏸';
 | 
				
			||||||
@@ -560,7 +578,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;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
@@ -591,7 +609,7 @@ var widget = (function () {
 | 
				
			|||||||
			document.body.removeChild(o);
 | 
								document.body.removeChild(o);
 | 
				
			||||||
		}, 500);
 | 
							}, 500);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	return ret;
 | 
						return r;
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -857,7 +875,10 @@ 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) {
 | 
				
			||||||
			play(0, true);
 | 
								play(0, true);
 | 
				
			||||||
			return mp.fade_in();
 | 
								return mp.fade_in();
 | 
				
			||||||
@@ -868,6 +889,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);
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1186,8 +1220,11 @@ function play(tid, is_ev, seek, call_depth) {
 | 
				
			|||||||
	mp.stopfade(true);
 | 
						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') {
 | 
				
			||||||
@@ -1385,8 +1422,7 @@ function autoplay_blocked(seek) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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('&');
 | 
				
			||||||
@@ -1402,7 +1438,7 @@ function autoplay_blocked(seek) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return play(id[0], false, 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 () {
 | 
				
			||||||
@@ -1508,35 +1544,61 @@ var thegrid = (function () {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	setsz();
 | 
						setsz();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function seltgl(e) {
 | 
						function gclick(e) {
 | 
				
			||||||
		if (e && e.ctrlKey)
 | 
							if (e && (e.ctrlKey || e.metaKey))
 | 
				
			||||||
			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),
 | 
				
			||||||
 | 
								is_vid = /\.(av1|asf|avi|flv|m4v|mkv|mjpeg|mjpg|mpg|mpeg|mpg2|mpeg2|h264|avc|h265|hevc|mov|3gp|mp4|ts|mpegts|nut|ogv|ogm|rm|vob|webm|wmv)(\?|$)/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_vid)
 | 
				
			||||||
 | 
								window.open(href + (href.indexOf('?') === -1 ? '?' : '&') + 'vcr', '_blank');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
@@ -1567,6 +1629,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';
 | 
				
			||||||
@@ -1594,6 +1658,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();
 | 
				
			||||||
@@ -1681,10 +1750,14 @@ 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;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var k = (e.code + ''), pos = -1, n;
 | 
						var k = (e.code + ''), pos = -1, n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e.shiftKey && k != 'KeyA' && k != 'KeyD')
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (k.indexOf('Digit') === 0)
 | 
						if (k.indexOf('Digit') === 0)
 | 
				
			||||||
		pos = parseInt(k.slice(-1)) * 0.1;
 | 
							pos = parseInt(k.slice(-1)) * 0.1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1720,6 +1793,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();
 | 
				
			||||||
@@ -1802,6 +1883,7 @@ document.onkeydown = function (e) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var search_timeout,
 | 
						var search_timeout,
 | 
				
			||||||
 | 
							defer_timeout,
 | 
				
			||||||
		search_in_progress = 0;
 | 
							search_in_progress = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function ev_search_input() {
 | 
						function ev_search_input() {
 | 
				
			||||||
@@ -1816,9 +1898,29 @@ document.onkeydown = function (e) {
 | 
				
			|||||||
		if (id != "q_raw")
 | 
							if (id != "q_raw")
 | 
				
			||||||
			encode_query();
 | 
								encode_query();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clearTimeout(search_timeout);
 | 
							set_vq();
 | 
				
			||||||
		if (Date.now() - search_in_progress > 30 * 1000)
 | 
					
 | 
				
			||||||
 | 
							clearTimeout(defer_timeout);
 | 
				
			||||||
 | 
							defer_timeout = setTimeout(try_search, 2000);
 | 
				
			||||||
 | 
							try_search();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function try_search() {
 | 
				
			||||||
 | 
							if (Date.now() - search_in_progress > 30 * 1000) {
 | 
				
			||||||
 | 
								clearTimeout(defer_timeout);
 | 
				
			||||||
 | 
								clearTimeout(search_timeout);
 | 
				
			||||||
			search_timeout = setTimeout(do_search, 200);
 | 
								search_timeout = setTimeout(do_search, 200);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function set_vq() {
 | 
				
			||||||
 | 
							if (search_in_progress)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var q = ebi('q_raw').value,
 | 
				
			||||||
 | 
								vq = ebi('files').getAttribute('q_raw');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							srch_msg(false, (q == vq) ? '' : 'search results below are from a previous query:\n  ' + (vq ? vq : '(*)'));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function encode_query() {
 | 
						function encode_query() {
 | 
				
			||||||
@@ -1888,7 +1990,8 @@ document.onkeydown = function (e) {
 | 
				
			|||||||
		xhr.setRequestHeader('Content-Type', 'text/plain');
 | 
							xhr.setRequestHeader('Content-Type', 'text/plain');
 | 
				
			||||||
		xhr.onreadystatechange = xhr_search_results;
 | 
							xhr.onreadystatechange = xhr_search_results;
 | 
				
			||||||
		xhr.ts = Date.now();
 | 
							xhr.ts = Date.now();
 | 
				
			||||||
		xhr.send(JSON.stringify({ "q": ebi('q_raw').value }));
 | 
							xhr.q_raw = ebi('q_raw').value;
 | 
				
			||||||
 | 
							xhr.send(JSON.stringify({ "q": xhr.q_raw }));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function xhr_search_results() {
 | 
						function xhr_search_results() {
 | 
				
			||||||
@@ -1959,6 +2062,8 @@ document.onkeydown = function (e) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		ofiles.innerHTML = html.join('\n');
 | 
							ofiles.innerHTML = html.join('\n');
 | 
				
			||||||
		ofiles.setAttribute("ts", this.ts);
 | 
							ofiles.setAttribute("ts", this.ts);
 | 
				
			||||||
 | 
							ofiles.setAttribute("q_raw", this.q_raw);
 | 
				
			||||||
 | 
							set_vq();
 | 
				
			||||||
		mukey.render();
 | 
							mukey.render();
 | 
				
			||||||
		reload_browser();
 | 
							reload_browser();
 | 
				
			||||||
		filecols.set_style(['File Name']);
 | 
							filecols.set_style(['File Name']);
 | 
				
			||||||
@@ -1970,6 +2075,7 @@ document.onkeydown = function (e) {
 | 
				
			|||||||
		ev(e);
 | 
							ev(e);
 | 
				
			||||||
		treectl.show();
 | 
							treectl.show();
 | 
				
			||||||
		ebi('files').innerHTML = orig_html;
 | 
							ebi('files').innerHTML = orig_html;
 | 
				
			||||||
 | 
							ebi('files').removeAttribute('q_raw');
 | 
				
			||||||
		orig_html = null;
 | 
							orig_html = null;
 | 
				
			||||||
		msel.render();
 | 
							msel.render();
 | 
				
			||||||
		reload_browser();
 | 
							reload_browser();
 | 
				
			||||||
@@ -2188,6 +2294,9 @@ var treectl = (function () {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function treego(e) {
 | 
						function treego(e) {
 | 
				
			||||||
 | 
							if (e && (e.ctrlKey || e.metaKey))
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ev(e);
 | 
							ev(e);
 | 
				
			||||||
		if (this.getAttribute('class') == 'hl' &&
 | 
							if (this.getAttribute('class') == 'hl' &&
 | 
				
			||||||
			this.previousSibling.textContent == '-') {
 | 
								this.previousSibling.textContent == '-') {
 | 
				
			||||||
@@ -2965,3 +3074,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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
	<title>📝🎉 {{ title }}</title> <!-- 📜 -->
 | 
						<title>📝🎉 {{ title }}</title> <!-- 📜 -->
 | 
				
			||||||
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
						<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
	<meta name="viewport" content="width=device-width, initial-scale=0.7">
 | 
						<meta name="viewport" content="width=device-width, initial-scale=0.7">
 | 
				
			||||||
	<link href="/.cpr/md.css" rel="stylesheet">
 | 
						<link href="/.cpr/md.css?_={{ ts }}" rel="stylesheet">
 | 
				
			||||||
	{%- if edit %}
 | 
						{%- if edit %}
 | 
				
			||||||
	<link href="/.cpr/md2.css" rel="stylesheet">
 | 
						<link href="/.cpr/md2.css?_={{ ts }}" rel="stylesheet">
 | 
				
			||||||
	{%- endif %}
 | 
						{%- endif %}
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
@@ -146,10 +146,10 @@ var md_opt = {
 | 
				
			|||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	</script>
 | 
						</script>
 | 
				
			||||||
    <script src="/.cpr/util.js"></script>
 | 
					    <script src="/.cpr/util.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/deps/marked.js"></script>
 | 
						<script src="/.cpr/deps/marked.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/md.js"></script>
 | 
						<script src="/.cpr/md.js?_={{ ts }}"></script>
 | 
				
			||||||
	{%- if edit %}
 | 
						{%- if edit %}
 | 
				
			||||||
	<script src="/.cpr/md2.js"></script>
 | 
						<script src="/.cpr/md2.js?_={{ ts }}"></script>
 | 
				
			||||||
	{%- endif %}
 | 
						{%- endif %}
 | 
				
			||||||
</body></html>
 | 
					</body></html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
	<title>📝🎉 {{ title }}</title>
 | 
						<title>📝🎉 {{ title }}</title>
 | 
				
			||||||
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
						<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
	<meta name="viewport" content="width=device-width, initial-scale=0.7">
 | 
						<meta name="viewport" content="width=device-width, initial-scale=0.7">
 | 
				
			||||||
	<link href="/.cpr/mde.css" rel="stylesheet">
 | 
						<link href="/.cpr/mde.css?_={{ ts }}" rel="stylesheet">
 | 
				
			||||||
	<link href="/.cpr/deps/mini-fa.css" rel="stylesheet">
 | 
						<link href="/.cpr/deps/mini-fa.css?_={{ ts }}" rel="stylesheet">
 | 
				
			||||||
	<link href="/.cpr/deps/easymde.css" rel="stylesheet">
 | 
						<link href="/.cpr/deps/easymde.css?_={{ ts }}" rel="stylesheet">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<div id="mw">
 | 
						<div id="mw">
 | 
				
			||||||
@@ -43,7 +43,7 @@ var lightswitch = (function () {
 | 
				
			|||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	</script>
 | 
						</script>
 | 
				
			||||||
    <script src="/.cpr/util.js"></script>
 | 
					    <script src="/.cpr/util.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/deps/easymde.js"></script>
 | 
						<script src="/.cpr/deps/easymde.js?_={{ ts }}"></script>
 | 
				
			||||||
	<script src="/.cpr/mde.js"></script>
 | 
						<script src="/.cpr/mde.js?_={{ ts }}"></script>
 | 
				
			||||||
</body></html>
 | 
					</body></html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
    <title>copyparty</title>
 | 
					    <title>copyparty</title>
 | 
				
			||||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
					    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
				
			||||||
    <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/msg.css">
 | 
					    <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/msg.css?_={{ ts }}">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
    <title>copyparty</title>
 | 
					    <title>copyparty</title>
 | 
				
			||||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
					    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=0.8">
 | 
				
			||||||
    <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/splash.css">
 | 
					    <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/splash.css?_={{ ts }}">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,5 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.onerror = vis_exh;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function goto_up2k() {
 | 
					function goto_up2k() {
 | 
				
			||||||
    if (up2k === false)
 | 
					    if (up2k === false)
 | 
				
			||||||
@@ -16,17 +14,19 @@ function goto_up2k() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// chrome requires https to use crypto.subtle,
 | 
					// chrome requires https to use crypto.subtle,
 | 
				
			||||||
// usually it's undefined but some chromes throw on invoke
 | 
					// usually it's undefined but some chromes throw on invoke
 | 
				
			||||||
var up2k = null;
 | 
					var up2k = null,
 | 
				
			||||||
var sha_js = window.WebAssembly ? 'hw' : 'ac';  // ff53,c57,sa11
 | 
					    sha_js = window.WebAssembly ? 'hw' : 'ac',  // ff53,c57,sa11
 | 
				
			||||||
 | 
					    m = 'will use ' + sha_js + ' instead of native sha512 due to';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try {
 | 
					try {
 | 
				
			||||||
    var cf = crypto.subtle || crypto.webkitSubtle;
 | 
					    var cf = crypto.subtle || crypto.webkitSubtle;
 | 
				
			||||||
    cf.digest('SHA-512', new Uint8Array(1)).then(
 | 
					    cf.digest('SHA-512', new Uint8Array(1)).then(
 | 
				
			||||||
        function (x) { console.log('sha-ok'); up2k = up2k_init(cf); },
 | 
					        function (x) { console.log('sha-ok'); up2k = up2k_init(cf); },
 | 
				
			||||||
        function (x) { console.log('sha-ng:', x); up2k = up2k_init(false); }
 | 
					        function (x) { console.log(m, x); up2k = up2k_init(false); }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
catch (ex) {
 | 
					catch (ex) {
 | 
				
			||||||
    console.log('sha-na:', ex);
 | 
					    console.log(m, ex);
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        up2k = up2k_init(false);
 | 
					        up2k = up2k_init(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -142,7 +142,7 @@ function U2pvis(act, btns) {
 | 
				
			|||||||
    this.tail = -1;
 | 
					    this.tail = -1;
 | 
				
			||||||
    this.wsz = 3;
 | 
					    this.wsz = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.addfile = function (entry, sz) {
 | 
					    this.addfile = function (entry, sz, draw) {
 | 
				
			||||||
        this.tab.push({
 | 
					        this.tab.push({
 | 
				
			||||||
            "hn": entry[0],
 | 
					            "hn": entry[0],
 | 
				
			||||||
            "ht": entry[1],
 | 
					            "ht": entry[1],
 | 
				
			||||||
@@ -156,6 +156,9 @@ function U2pvis(act, btns) {
 | 
				
			|||||||
            "bd0": 0  // upload start
 | 
					            "bd0": 0  // upload start
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.ctr["q"]++;
 | 
					        this.ctr["q"]++;
 | 
				
			||||||
 | 
					        if (!draw)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.drawcard("q");
 | 
					        this.drawcard("q");
 | 
				
			||||||
        if (this.act == "q") {
 | 
					        if (this.act == "q") {
 | 
				
			||||||
            this.addrow(this.tab.length - 1);
 | 
					            this.addrow(this.tab.length - 1);
 | 
				
			||||||
@@ -256,6 +259,41 @@ function U2pvis(act, btns) {
 | 
				
			|||||||
        var obj = ebi('f{0}p'.format(fobj.n)),
 | 
					        var obj = ebi('f{0}p'.format(fobj.n)),
 | 
				
			||||||
            o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
 | 
					            o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!obj) { //} || true) {
 | 
				
			||||||
 | 
					            var msg = [
 | 
				
			||||||
 | 
					                "act", this.act,
 | 
				
			||||||
 | 
					                "in", fo.in,
 | 
				
			||||||
 | 
					                "is_act", this.is_act(fo.in),
 | 
				
			||||||
 | 
					                "head", this.head,
 | 
				
			||||||
 | 
					                "tail", this.tail,
 | 
				
			||||||
 | 
					                "nfile", fobj.n,
 | 
				
			||||||
 | 
					                "name", fobj.name,
 | 
				
			||||||
 | 
					                "sz", fobj.size,
 | 
				
			||||||
 | 
					                "bytesDelta", delta,
 | 
				
			||||||
 | 
					                "bytesDone", fo.bd,
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					                m2 = '',
 | 
				
			||||||
 | 
					                ds = QSA("#u2tab>tbody>tr>td:first-child>a:last-child");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var a = 0; a < msg.length; a += 2)
 | 
				
			||||||
 | 
					                m2 += msg[a] + '=' + msg[a + 1] + ', ';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            console.log(m2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var a = 0, aa = ds.length; a < aa; a++) {
 | 
				
			||||||
 | 
					                var id = ds[a].parentNode.getAttribute('id').slice(1, -1);
 | 
				
			||||||
 | 
					                console.log("dom %d/%d = [%s] in(%s) is_act(%s) %s",
 | 
				
			||||||
 | 
					                    a, aa, id, this.tab[id].in, this.is_act(fo.in), ds[a].textContent);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var a = 0, aa = this.tab.length; a < aa; a++)
 | 
				
			||||||
 | 
					                if (this.is_act(this.tab[a].in))
 | 
				
			||||||
 | 
					                    console.log("tab %d/%d = sz %s", a, aa, this.tab[a].bt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            console.log("a");
 | 
				
			||||||
 | 
					            throw 42;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        obj.innerHTML = fo.hp;
 | 
					        obj.innerHTML = fo.hp;
 | 
				
			||||||
        obj.style.color = '#fff';
 | 
					        obj.style.color = '#fff';
 | 
				
			||||||
        obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
 | 
					        obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
 | 
				
			||||||
@@ -276,12 +314,14 @@ function U2pvis(act, btns) {
 | 
				
			|||||||
        this.drawcard(oldcat);
 | 
					        this.drawcard(oldcat);
 | 
				
			||||||
        this.drawcard(newcat);
 | 
					        this.drawcard(newcat);
 | 
				
			||||||
        if (this.is_act(newcat)) {
 | 
					        if (this.is_act(newcat)) {
 | 
				
			||||||
            this.tail++;
 | 
					            this.tail = Math.max(this.tail, nfile + 1);
 | 
				
			||||||
            if (!ebi('f' + nfile))
 | 
					            if (!ebi('f' + nfile))
 | 
				
			||||||
                this.addrow(nfile);
 | 
					                this.addrow(nfile);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else if (this.is_act(oldcat)) {
 | 
					        else if (this.is_act(oldcat)) {
 | 
				
			||||||
            this.head++;
 | 
					            while (this.head < Math.min(this.tab.length, this.tail) && (this.head == nfile || !this.is_act(this.tab[this.head].in)))
 | 
				
			||||||
 | 
					                this.head++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!bz_act) {
 | 
					            if (!bz_act) {
 | 
				
			||||||
                var tr = ebi("f" + nfile);
 | 
					                var tr = ebi("f" + nfile);
 | 
				
			||||||
                tr.parentNode.removeChild(tr);
 | 
					                tr.parentNode.removeChild(tr);
 | 
				
			||||||
@@ -350,8 +390,21 @@ function U2pvis(act, btns) {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (this.head == -1) {
 | 
					        if (this.head == -1) {
 | 
				
			||||||
            this.head = this.tab.length;
 | 
					            var precard = has(["ok", "ng", "done"], this.act) ? {} : this.act == "bz" ? { "ok": 1, "ng": 1 } : { "ok": 1, "ng": 1, "bz": 1 },
 | 
				
			||||||
            this.tail = this.head - 1;
 | 
					                postcard = has(["ok", "ng", "done"], this.act) ? { "bz": 1, "q": 1 } : this.act == "bz" ? { "q": 1 } : {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var a = 0; a < this.tab.length; a++) {
 | 
				
			||||||
 | 
					                var rt = this.tab[a].in;
 | 
				
			||||||
 | 
					                if (precard[rt]) {
 | 
				
			||||||
 | 
					                    this.head = a + 1;
 | 
				
			||||||
 | 
					                    this.tail = a;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (postcard[rt]) {
 | 
				
			||||||
 | 
					                    this.head = a;
 | 
				
			||||||
 | 
					                    this.tail = a - 1;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (card == "bz") {
 | 
					        if (card == "bz") {
 | 
				
			||||||
            for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
 | 
					            for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
 | 
				
			||||||
@@ -598,14 +651,50 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function read_dirs(rd, pf, dirs, good, bad) {
 | 
					    function rd_flatten(pf, dirs) {
 | 
				
			||||||
 | 
					        var ret = jcp(pf);
 | 
				
			||||||
 | 
					        for (var a = 0; a < dirs.length; a++)
 | 
				
			||||||
 | 
					            ret.push(dirs.fullPath || '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret.sort();
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var rd_missing_ref = [];
 | 
				
			||||||
 | 
					    function read_dirs(rd, pf, dirs, good, bad, spins) {
 | 
				
			||||||
 | 
					        spins = spins || 0;
 | 
				
			||||||
 | 
					        if (++spins == 5)
 | 
				
			||||||
 | 
					            rd_missing_ref = rd_flatten(pf, dirs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (spins == 200) {
 | 
				
			||||||
 | 
					            var missing = rd_flatten(pf, dirs),
 | 
				
			||||||
 | 
					                match = rd_missing_ref.length == missing.length,
 | 
				
			||||||
 | 
					                aa = match ? missing.length : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            missing.sort();
 | 
				
			||||||
 | 
					            for (var a = 0; a < aa; a++)
 | 
				
			||||||
 | 
					                if (rd_missing_ref[a] != missing[a])
 | 
				
			||||||
 | 
					                    match = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (match) {
 | 
				
			||||||
 | 
					                var msg = ['directory iterator got stuck on the following {0} items; good chance your browser is about to spinlock:'.format(missing.length)];
 | 
				
			||||||
 | 
					                for (var a = 0; a < Math.min(20, missing.length); a++)
 | 
				
			||||||
 | 
					                    msg.push(missing[a]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                alert(msg.join('\n-- '));
 | 
				
			||||||
 | 
					                dirs = [];
 | 
				
			||||||
 | 
					                pf = [];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            spins = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!dirs.length) {
 | 
					        if (!dirs.length) {
 | 
				
			||||||
            if (!pf.length)
 | 
					            if (!pf.length)
 | 
				
			||||||
                return gotallfiles(good, bad);
 | 
					                return gotallfiles(good, bad);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            console.log("retry pf, " + pf.length);
 | 
					            console.log("retry pf, " + pf.length);
 | 
				
			||||||
            setTimeout(function () {
 | 
					            setTimeout(function () {
 | 
				
			||||||
                read_dirs(rd, pf, dirs, good, bad);
 | 
					                read_dirs(rd, pf, dirs, good, bad, spins);
 | 
				
			||||||
            }, 50);
 | 
					            }, 50);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -645,7 +734,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
                dirs.shift();
 | 
					                dirs.shift();
 | 
				
			||||||
                rd = null;
 | 
					                rd = null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return read_dirs(rd, pf, dirs, good, bad);
 | 
					            return read_dirs(rd, pf, dirs, good, bad, spins);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -670,41 +759,50 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        if (ask_up && !fsearch && !confirm(msg.join('\n')))
 | 
					        if (ask_up && !fsearch && !confirm(msg.join('\n')))
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var seen = {},
 | 
				
			||||||
 | 
					            evpath = get_evpath(),
 | 
				
			||||||
 | 
					            draw_each = good_files.length < 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (var a = 0; a < st.files.length; a++)
 | 
				
			||||||
 | 
					            seen[st.files[a].name + '\n' + st.files[a].size] = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (var a = 0; a < good_files.length; a++) {
 | 
					        for (var a = 0; a < good_files.length; a++) {
 | 
				
			||||||
            var fobj = good_files[a][0],
 | 
					            var fobj = good_files[a][0],
 | 
				
			||||||
                now = Date.now(),
 | 
					                now = Date.now(),
 | 
				
			||||||
                lmod = fobj.lastModified || now;
 | 
					                lmod = fobj.lastModified || now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var entry = {
 | 
					            var entry = {
 | 
				
			||||||
                "n": parseInt(st.files.length.toString()),
 | 
					                "n": st.files.length,
 | 
				
			||||||
                "t0": now,
 | 
					                "t0": now,
 | 
				
			||||||
                "fobj": fobj,
 | 
					                "fobj": fobj,
 | 
				
			||||||
                "name": good_files[a][1],
 | 
					                "name": good_files[a][1],
 | 
				
			||||||
                "size": fobj.size,
 | 
					                "size": fobj.size,
 | 
				
			||||||
                "lmod": lmod / 1000,
 | 
					                "lmod": lmod / 1000,
 | 
				
			||||||
                "purl": get_evpath(),
 | 
					                "purl": evpath,
 | 
				
			||||||
                "done": false,
 | 
					                "done": false,
 | 
				
			||||||
                "hash": []
 | 
					                "hash": []
 | 
				
			||||||
            };
 | 
					            },
 | 
				
			||||||
 | 
					                key = entry.name + '\n' + entry.size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var skip = false;
 | 
					            if (seen[key])
 | 
				
			||||||
            for (var b = 0; b < st.files.length; b++)
 | 
					 | 
				
			||||||
                if (entry.name == st.files[b].name &&
 | 
					 | 
				
			||||||
                    entry.size == st.files[b].size)
 | 
					 | 
				
			||||||
                    skip = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (skip)
 | 
					 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            seen[key] = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pvis.addfile([
 | 
					            pvis.addfile([
 | 
				
			||||||
                fsearch ? esc(entry.name) : linksplit(
 | 
					                fsearch ? esc(entry.name) : linksplit(
 | 
				
			||||||
                    uricom_dec(entry.purl)[0] + entry.name).join(' '),
 | 
					                    uricom_dec(entry.purl)[0] + entry.name).join(' '),
 | 
				
			||||||
                '📐 hash',
 | 
					                '📐 hash',
 | 
				
			||||||
                ''
 | 
					                ''
 | 
				
			||||||
            ], fobj.size);
 | 
					            ], fobj.size, draw_each);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            st.files.push(entry);
 | 
					            st.files.push(entry);
 | 
				
			||||||
            st.todo.hash.push(entry);
 | 
					            st.todo.hash.push(entry);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (!draw_each) {
 | 
				
			||||||
 | 
					            pvis.drawcard("q");
 | 
				
			||||||
 | 
					            pvis.changecard(pvis.act);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ebi('u2btn').addEventListener('drop', gotfile, false);
 | 
					    ebi('u2btn').addEventListener('drop', gotfile, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -781,7 +879,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            clearTimeout(tto);
 | 
					            clearTimeout(tto);
 | 
				
			||||||
            running = true;
 | 
					            running = true;
 | 
				
			||||||
            while (true) {
 | 
					            while (window['vis_exh']) {
 | 
				
			||||||
                var is_busy = 0 !=
 | 
					                var is_busy = 0 !=
 | 
				
			||||||
                    st.todo.hash.length +
 | 
					                    st.todo.hash.length +
 | 
				
			||||||
                    st.todo.handshake.length +
 | 
					                    st.todo.handshake.length +
 | 
				
			||||||
@@ -954,7 +1052,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            bpend += cdr - car;
 | 
					            bpend += cdr - car;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            reader.onload = function (e) {
 | 
					            function orz(e) {
 | 
				
			||||||
                if (!min_filebuf && nch == 1) {
 | 
					                if (!min_filebuf && nch == 1) {
 | 
				
			||||||
                    min_filebuf = 1;
 | 
					                    min_filebuf = 1;
 | 
				
			||||||
                    var td = Date.now() - t0;
 | 
					                    var td = Date.now() - t0;
 | 
				
			||||||
@@ -964,9 +1062,30 @@ function up2k_init(subtle) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                hash_calc(nch, e.target.result);
 | 
					                hash_calc(nch, e.target.result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            reader.onload = function (e) {
 | 
				
			||||||
 | 
					                try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            reader.onerror = function () {
 | 
					            reader.onerror = function () {
 | 
				
			||||||
                alert('y o u   b r o k e    i t\nerror: ' + reader.error);
 | 
					                var err = reader.error + '';
 | 
				
			||||||
 | 
					                var handled = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (err.indexOf('NotReadableError') !== -1 || // win10-chrome defender
 | 
				
			||||||
 | 
					                    err.indexOf('NotFoundError') !== -1  // macos-firefox permissions
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                    pvis.seth(t.n, 1, 'OS-error');
 | 
				
			||||||
 | 
					                    pvis.seth(t.n, 2, err);
 | 
				
			||||||
 | 
					                    handled = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (handled) {
 | 
				
			||||||
 | 
					                    pvis.move(t.n, 'ng');
 | 
				
			||||||
 | 
					                    st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
 | 
				
			||||||
 | 
					                    st.bytes.uploaded += t.size;
 | 
				
			||||||
 | 
					                    return tasker();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                alert('y o u   b r o k e    i t\nfile: ' + t.name + '\nerror: ' + err);
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            reader.readAsArrayBuffer(
 | 
					            reader.readAsArrayBuffer(
 | 
				
			||||||
                bobslice.call(t.fobj, car, cdr));
 | 
					                bobslice.call(t.fobj, car, cdr));
 | 
				
			||||||
@@ -1047,12 +1166,12 @@ function up2k_init(subtle) {
 | 
				
			|||||||
                console.log('zombie handshake onerror,', t);
 | 
					                console.log('zombie handshake onerror,', t);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            console.log('handshake onerror, retrying');
 | 
					            console.log('handshake onerror, retrying', t);
 | 
				
			||||||
            st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
 | 
					            st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
 | 
				
			||||||
            st.todo.handshake.unshift(t);
 | 
					            st.todo.handshake.unshift(t);
 | 
				
			||||||
            tasker();
 | 
					            tasker();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        xhr.onload = function (e) {
 | 
					        function orz(e) {
 | 
				
			||||||
            if (t.busied != me) {
 | 
					            if (t.busied != me) {
 | 
				
			||||||
                console.log('zombie handshake onload,', t);
 | 
					                console.log('zombie handshake onload,', t);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -1092,6 +1211,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (response.name !== t.name) {
 | 
					                if (response.name !== t.name) {
 | 
				
			||||||
                    // file exists; server renamed us
 | 
					                    // file exists; server renamed us
 | 
				
			||||||
 | 
					                    console.log("server-rename [" + t.name + "] to [" + response.name + "]");
 | 
				
			||||||
                    t.name = response.name;
 | 
					                    t.name = response.name;
 | 
				
			||||||
                    pvis.seth(t.n, 0, linksplit(t.purl + t.name).join(' '));
 | 
					                    pvis.seth(t.n, 0, linksplit(t.purl + t.name).join(' '));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -1196,6 +1316,9 @@ function up2k_init(subtle) {
 | 
				
			|||||||
                        (xhr.responseText && xhr.responseText) ||
 | 
					                        (xhr.responseText && xhr.responseText) ||
 | 
				
			||||||
                        "no further information"));
 | 
					                        "no further information"));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        xhr.onload = function (e) {
 | 
				
			||||||
 | 
					            try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var req = {
 | 
					        var req = {
 | 
				
			||||||
@@ -1236,40 +1359,56 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        if (cdr >= t.size)
 | 
					        if (cdr >= t.size)
 | 
				
			||||||
            cdr = t.size;
 | 
					            cdr = t.size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var xhr = new XMLHttpRequest();
 | 
					        function orz(xhr) {
 | 
				
			||||||
        xhr.upload.onprogress = function (xev) {
 | 
					            var txt = ((xhr.response && xhr.response.err) || xhr.responseText) + '';
 | 
				
			||||||
            pvis.prog(t, npart, xev.loaded);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        xhr.onload = function (xev) {
 | 
					 | 
				
			||||||
            if (xhr.status == 200) {
 | 
					            if (xhr.status == 200) {
 | 
				
			||||||
                pvis.prog(t, npart, cdr - car);
 | 
					                pvis.prog(t, npart, cdr - car);
 | 
				
			||||||
                st.bytes.uploaded += cdr - car;
 | 
					                st.bytes.uploaded += cdr - car;
 | 
				
			||||||
                t.bytes_uploaded += cdr - car;
 | 
					                t.bytes_uploaded += cdr - car;
 | 
				
			||||||
                st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
 | 
					 | 
				
			||||||
                t.postlist.splice(t.postlist.indexOf(npart), 1);
 | 
					 | 
				
			||||||
                if (t.postlist.length == 0) {
 | 
					 | 
				
			||||||
                    t.t4 = Date.now();
 | 
					 | 
				
			||||||
                    pvis.seth(t.n, 1, 'verifying');
 | 
					 | 
				
			||||||
                    st.todo.handshake.unshift(t);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                tasker();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else if (txt.indexOf('already got that') !== -1) {
 | 
				
			||||||
 | 
					                console.log("ignoring dupe-segment error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
                alert("server broke; cu-err {0} on file [{1}]:\n".format(
 | 
					                alert("server broke; cu-err {0} on file [{1}]:\n".format(
 | 
				
			||||||
                    xhr.status, t.name) + (
 | 
					                    xhr.status, t.name) + (txt || "no further information"));
 | 
				
			||||||
                        (xhr.response && xhr.response.err) ||
 | 
					                return;
 | 
				
			||||||
                        (xhr.responseText && xhr.responseText) ||
 | 
					            }
 | 
				
			||||||
                        "no further information"));
 | 
					            st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
 | 
				
			||||||
        };
 | 
					            t.postlist.splice(t.postlist.indexOf(npart), 1);
 | 
				
			||||||
        xhr.open('POST', t.purl + 'chunkpit.php', true);
 | 
					            if (t.postlist.length == 0) {
 | 
				
			||||||
        xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
 | 
					                t.t4 = Date.now();
 | 
				
			||||||
        xhr.setRequestHeader("X-Up2k-Wark", t.wark);
 | 
					                pvis.seth(t.n, 1, 'verifying');
 | 
				
			||||||
        xhr.setRequestHeader('Content-Type', 'application/octet-stream');
 | 
					                st.todo.handshake.unshift(t);
 | 
				
			||||||
        if (xhr.overrideMimeType)
 | 
					            }
 | 
				
			||||||
            xhr.overrideMimeType('Content-Type', 'application/octet-stream');
 | 
					            tasker();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        function do_send() {
 | 
				
			||||||
 | 
					            var xhr = new XMLHttpRequest();
 | 
				
			||||||
 | 
					            xhr.upload.onprogress = function (xev) {
 | 
				
			||||||
 | 
					                pvis.prog(t, npart, xev.loaded);
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            xhr.onload = function (xev) {
 | 
				
			||||||
 | 
					                try { orz(xhr); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            xhr.onerror = function (xev) {
 | 
				
			||||||
 | 
					                if (!window['vis_exh'])
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xhr.responseType = 'text';
 | 
					                console.log('chunkpit onerror, retrying', t);
 | 
				
			||||||
        xhr.send(bobslice.call(t.fobj, car, cdr));
 | 
					                do_send();
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            xhr.open('POST', t.purl + 'chunkpit.php', true);
 | 
				
			||||||
 | 
					            xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
 | 
				
			||||||
 | 
					            xhr.setRequestHeader("X-Up2k-Wark", t.wark);
 | 
				
			||||||
 | 
					            xhr.setRequestHeader('Content-Type', 'application/octet-stream');
 | 
				
			||||||
 | 
					            if (xhr.overrideMimeType)
 | 
				
			||||||
 | 
					                xhr.overrideMimeType('Content-Type', 'application/octet-stream');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            xhr.responseType = 'text';
 | 
				
			||||||
 | 
					            xhr.send(bobslice.call(t.fobj, car, cdr));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        do_send();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /////
 | 
					    /////
 | 
				
			||||||
@@ -1309,6 +1448,17 @@ function up2k_init(subtle) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    tt.init();
 | 
					    tt.init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function bumpthread2(e) {
 | 
				
			||||||
 | 
					        if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (e.code == 'ArrowUp')
 | 
				
			||||||
 | 
					            bumpthread(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (e.code == 'ArrowDown')
 | 
				
			||||||
 | 
					            bumpthread(-1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function bumpthread(dir) {
 | 
					    function bumpthread(dir) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            dir.stopPropagation();
 | 
					            dir.stopPropagation();
 | 
				
			||||||
@@ -1319,7 +1469,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        if (dir.target) {
 | 
					        if (dir.target) {
 | 
				
			||||||
            clmod(obj, 'err', 1);
 | 
					            clmod(obj, 'err', 1);
 | 
				
			||||||
            var v = Math.floor(parseInt(obj.value));
 | 
					            var v = Math.floor(parseInt(obj.value));
 | 
				
			||||||
            if (v < 1 || v > 8 || v !== v)
 | 
					            if (v < 0 || v > 64 || v !== v)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            parallel_uploads = v;
 | 
					            parallel_uploads = v;
 | 
				
			||||||
@@ -1330,11 +1480,11 @@ function up2k_init(subtle) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        parallel_uploads += dir;
 | 
					        parallel_uploads += dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (parallel_uploads < 1)
 | 
					        if (parallel_uploads < 0)
 | 
				
			||||||
            parallel_uploads = 1;
 | 
					            parallel_uploads = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (parallel_uploads > 8)
 | 
					        if (parallel_uploads > 16)
 | 
				
			||||||
            parallel_uploads = 8;
 | 
					            parallel_uploads = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        obj.value = parallel_uploads;
 | 
					        obj.value = parallel_uploads;
 | 
				
			||||||
        bumpthread({ "target": 1 })
 | 
					        bumpthread({ "target": 1 })
 | 
				
			||||||
@@ -1430,6 +1580,7 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        bumpthread(-1);
 | 
					        bumpthread(-1);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ebi('nthread').onkeydown = bumpthread2;
 | 
				
			||||||
    ebi('nthread').addEventListener('input', bumpthread, false);
 | 
					    ebi('nthread').addEventListener('input', bumpthread, false);
 | 
				
			||||||
    ebi('multitask').addEventListener('click', tgl_multitask, false);
 | 
					    ebi('multitask').addEventListener('click', tgl_multitask, false);
 | 
				
			||||||
    ebi('ask_up').addEventListener('click', tgl_ask_up, false);
 | 
					    ebi('ask_up').addEventListener('click', tgl_ask_up, false);
 | 
				
			||||||
@@ -1443,7 +1594,10 @@ function up2k_init(subtle) {
 | 
				
			|||||||
        nodes[a].addEventListener('touchend', nop, false);
 | 
					        nodes[a].addEventListener('touchend', nop, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    set_fsearch();
 | 
					    set_fsearch();
 | 
				
			||||||
    bumpthread({ "target": 1 })
 | 
					    bumpthread({ "target": 1 });
 | 
				
			||||||
 | 
					    if (parallel_uploads < 1)
 | 
				
			||||||
 | 
					        bumpthread(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return { "init_deps": init_deps, "set_fsearch": set_fsearch }
 | 
					    return { "init_deps": init_deps, "set_fsearch": set_fsearch }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,16 +11,6 @@ var is_touch = 'ontouchstart' in window,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// error handler for mobile devices
 | 
					// error handler for mobile devices
 | 
				
			||||||
function hcroak(msg) {
 | 
					 | 
				
			||||||
    document.body.innerHTML = msg;
 | 
					 | 
				
			||||||
    window.onerror = undefined;
 | 
					 | 
				
			||||||
    throw 'fatal_err';
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function croak(msg) {
 | 
					 | 
				
			||||||
    document.body.textContent = msg;
 | 
					 | 
				
			||||||
    window.onerror = undefined;
 | 
					 | 
				
			||||||
    throw msg;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function esc(txt) {
 | 
					function esc(txt) {
 | 
				
			||||||
    return txt.replace(/[&"<>]/g, function (c) {
 | 
					    return txt.replace(/[&"<>]/g, function (c) {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
@@ -32,9 +22,12 @@ function esc(txt) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function vis_exh(msg, url, lineNo, columnNo, error) {
 | 
					function vis_exh(msg, url, lineNo, columnNo, error) {
 | 
				
			||||||
 | 
					    if (!window.onerror)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    window.onerror = undefined;
 | 
					    window.onerror = undefined;
 | 
				
			||||||
    window['vis_exh'] = null;
 | 
					    window['vis_exh'] = null;
 | 
				
			||||||
    var html = ['<h1>you hit a bug!</h1><p>please screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>',
 | 
					    var html = ['<h1>you hit a bug!</h1><p>please send me a screenshot arigathanks gozaimuch: <code>ed/irc.rizon.net</code> or <code>ed#2644</code><br />  (and if you can, press F12 and include the "Console" tab in the screenshot too)</p><p>',
 | 
				
			||||||
        esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>'];
 | 
					        esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
@@ -44,9 +37,13 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
 | 
				
			|||||||
                html.push('<h2>' + find[a] + '</h2>' +
 | 
					                html.push('<h2>' + find[a] + '</h2>' +
 | 
				
			||||||
                    esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
 | 
					                    esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    document.body.style.fontSize = '0.8em';
 | 
					    document.body.innerHTML = html.join('\n');
 | 
				
			||||||
    document.body.style.padding = '0 1em 1em 1em';
 | 
					
 | 
				
			||||||
    hcroak(html.join('\n'));
 | 
					    var s = mknod('style');
 | 
				
			||||||
 | 
					    s.innerHTML = 'body{background:#333;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em} code{color:#bf7;background:#222;padding:.1em;margin:.2em;font-size:1.1em;font-family:monospace,monospace} *{line-height:1.5em}';
 | 
				
			||||||
 | 
					    document.head.appendChild(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throw 'fatal_err';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,6 +64,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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -360,11 +360,11 @@ function get_vpath() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function get_pwd() {
 | 
					function get_pwd() {
 | 
				
			||||||
	var pwd = ('; ' + document.cookie).split('; cppwd=');
 | 
					    var pwd = ('; ' + document.cookie).split('; cppwd=');
 | 
				
			||||||
	if (pwd.length < 2)
 | 
					    if (pwd.length < 2)
 | 
				
			||||||
		return null;
 | 
					        return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return pwd[1].split(';')[0];
 | 
					    return pwd[1].split(';')[0];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -389,6 +389,11 @@ function has(haystack, needle) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function jcp(obj) {
 | 
				
			||||||
 | 
					    return JSON.parse(JSON.stringify(obj));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function sread(key) {
 | 
					function sread(key) {
 | 
				
			||||||
    if (window.localStorage)
 | 
					    if (window.localStorage)
 | 
				
			||||||
        return localStorage.getItem(key);
 | 
					        return localStorage.getItem(key);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,10 @@ import re, os, sys, time, shutil, signal, threading, tarfile, hashlib, platform,
 | 
				
			|||||||
import subprocess as sp
 | 
					import subprocess as sp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
run me with any version of python, i will unpack and run copyparty
 | 
					pls don't edit this file with a text editor,
 | 
				
			||||||
 | 
					  it breaks the compressed stuff at the end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(but please don't edit this file with a text editor
 | 
					run me with any version of python, i will unpack and run copyparty
 | 
				
			||||||
  since that would probably corrupt the binary stuff at the end)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
there's zero binaries! just plaintext python scripts all the way down
 | 
					there's zero binaries! just plaintext python scripts all the way down
 | 
				
			||||||
  so you can easily unpack the archive and inspect it for shady stuff
 | 
					  so you can easily unpack the archive and inspect it for shady stuff
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,6 +108,9 @@ class VHttpSrv(object):
 | 
				
			|||||||
        aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
 | 
					        aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
 | 
				
			||||||
        self.j2 = {x: J2_FILES for x in aliases}
 | 
					        self.j2 = {x: J2_FILES for x in aliases}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cachebuster(self):
 | 
				
			||||||
 | 
					        return "a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VHttpConn(object):
 | 
					class VHttpConn(object):
 | 
				
			||||||
    def __init__(self, args, asrv, log, buf):
 | 
					    def __init__(self, args, asrv, log, buf):
 | 
				
			||||||
@@ -121,6 +124,7 @@ class VHttpConn(object):
 | 
				
			|||||||
        self.log_src = "a"
 | 
					        self.log_src = "a"
 | 
				
			||||||
        self.lf_url = None
 | 
					        self.lf_url = None
 | 
				
			||||||
        self.hsrv = VHttpSrv()
 | 
					        self.hsrv = VHttpSrv()
 | 
				
			||||||
 | 
					        self.nreq = 0
 | 
				
			||||||
        self.nbyte = 0
 | 
					        self.nbyte = 0
 | 
				
			||||||
        self.workload = 0
 | 
					        self.workload = 0
 | 
				
			||||||
        self.ico = None
 | 
					        self.ico = None
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user