Full Code of 9001/copyparty for AI

hovudstraum 2cebda32973d cached
448 files
4.6 MB
1.2M tokens
2403 symbols
1 requests
Download .txt
Showing preview only (4,919K chars total). Download the full file or copy to clipboard to get everything.
Repository: 9001/copyparty
Branch: hovudstraum
Commit: 2cebda32973d
Files: 448
Total size: 4.6 MB

Directory structure:
gitextract_364x004l/

├── .eslintrc.json
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── something-else.md
│   ├── branch-rename.md
│   └── pull_request_template.md
├── .gitignore
├── .vscode/
│   ├── launch.json
│   ├── launch.py
│   ├── settings.json
│   └── tasks.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── bin/
│   ├── README.md
│   ├── bubbleparty.sh
│   ├── dbtool.py
│   ├── handlers/
│   │   ├── README.md
│   │   ├── caching-proxy.py
│   │   ├── ip-ok.py
│   │   ├── never404.py
│   │   ├── nooo.py
│   │   ├── randpic.py
│   │   ├── redirect.py
│   │   └── sorry.py
│   ├── hooks/
│   │   ├── README.md
│   │   ├── discord-announce.py
│   │   ├── image-noexif.py
│   │   ├── import-me.py
│   │   ├── into-the-cache-it-goes.py
│   │   ├── msg-log.py
│   │   ├── notify.py
│   │   ├── notify2.py
│   │   ├── podcast-normalizer.py
│   │   ├── qbittorrent-magnet.py
│   │   ├── reject-and-explain.py
│   │   ├── reject-extension.py
│   │   ├── reject-mimetype.py
│   │   ├── reject-ramdisk.py
│   │   ├── reloc-by-ext.py
│   │   ├── usb-eject.js
│   │   ├── usb-eject.py
│   │   ├── wget-i.py
│   │   ├── wget.py
│   │   ├── xiu-sha.py
│   │   └── xiu.py
│   ├── mtag/
│   │   ├── README.md
│   │   ├── audio-bpm.py
│   │   ├── audio-key-slicing.py
│   │   ├── audio-key.py
│   │   ├── cksum.py
│   │   ├── exe.py
│   │   ├── file-ext.py
│   │   ├── geotag.py
│   │   ├── guestbook-read.py
│   │   ├── guestbook.py
│   │   ├── image-noexif.py
│   │   ├── install-deps.sh
│   │   ├── media-hash.py
│   │   ├── mousepad.py
│   │   ├── rclone-upload.py
│   │   ├── res/
│   │   │   ├── twitter-unmute.user.js
│   │   │   ├── yt-ipr.conf
│   │   │   └── yt-ipr.user.js
│   │   ├── sleep.py
│   │   ├── very-bad-idea.py
│   │   ├── vidchk.py
│   │   ├── wget.py
│   │   └── yt-ipr.py
│   ├── partyfuse-streaming.py
│   ├── partyfuse.py
│   ├── partyfuse2.py
│   ├── partyjournal.py
│   ├── prisonparty.sh
│   ├── u2c.py
│   ├── unforget.py
│   ├── up2k.sh
│   └── zmq-recv.py
├── contrib/
│   ├── README.md
│   ├── apache/
│   │   └── copyparty.conf
│   ├── cfssl.sh
│   ├── copyparty.bat
│   ├── explorer-nothumbs-nofoldertypes.reg
│   ├── flameshot.sh
│   ├── haproxy/
│   │   └── copyparty.conf
│   ├── index.html
│   ├── ios/
│   │   └── upload-to-copyparty.shortcut
│   ├── ishare.iscu
│   ├── lighttpd/
│   │   ├── subdomain.conf
│   │   └── subpath.conf
│   ├── media-osd-bgone.ps1
│   ├── nginx/
│   │   └── copyparty.conf
│   ├── nixos/
│   │   └── modules/
│   │       └── copyparty.nix
│   ├── openrc/
│   │   └── copyparty
│   ├── package/
│   │   ├── arch/
│   │   │   └── PKGBUILD
│   │   ├── makedeb-mpr/
│   │   │   ├── PKGBUILD
│   │   │   ├── copyparty.conf
│   │   │   ├── copyparty.service
│   │   │   ├── index.md
│   │   │   └── prisonparty.service
│   │   ├── nix/
│   │   │   ├── copyparty/
│   │   │   │   ├── default.nix
│   │   │   │   ├── pin.json
│   │   │   │   └── update.py
│   │   │   ├── overlay.nix
│   │   │   └── partftpy/
│   │   │       ├── default.nix
│   │   │       ├── pin.json
│   │   │       └── update.py
│   │   └── rpm/
│   │       └── copyparty.spec
│   ├── plugins/
│   │   ├── README.md
│   │   ├── banner.js
│   │   ├── browser-icons.css
│   │   ├── graft-thumbs.js
│   │   ├── meadup.js
│   │   ├── minimal-up2k.html
│   │   ├── minimal-up2k.js
│   │   ├── quickmove.js
│   │   ├── rave.js
│   │   ├── up2k-hook-ytid.js
│   │   └── up2k-hooks.js
│   ├── podman-systemd/
│   │   ├── README.md
│   │   ├── copyparty.conf
│   │   └── copyparty.container
│   ├── rc/
│   │   └── copyparty
│   ├── send-to-cpp.contextlet.json
│   ├── setup-ashell.sh
│   ├── sharex.sxcu
│   ├── sharex12.sxcu
│   ├── systemd/
│   │   ├── cfssl.service
│   │   ├── copyparty-user.service
│   │   ├── copyparty.conf
│   │   ├── copyparty.example.conf
│   │   ├── copyparty.service
│   │   ├── copyparty@.service
│   │   ├── index.md
│   │   ├── prisonparty.service
│   │   └── prisonparty@.service
│   ├── themes/
│   │   └── bsod.css
│   ├── traefik/
│   │   └── copyparty.yaml
│   ├── webdav-cfg.bat
│   ├── windows/
│   │   └── copyparty-ctmp.bat
│   └── zfs-tune.py
├── copyparty/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── authsrv.py
│   ├── bos/
│   │   ├── __init__.py
│   │   ├── bos.py
│   │   └── path.py
│   ├── broker_mp.py
│   ├── broker_mpw.py
│   ├── broker_thr.py
│   ├── broker_util.py
│   ├── cert.py
│   ├── cfg.py
│   ├── dxml.py
│   ├── fsutil.py
│   ├── ftpd.py
│   ├── httpcli.py
│   ├── httpconn.py
│   ├── httpsrv.py
│   ├── ico.py
│   ├── mdns.py
│   ├── metrics.py
│   ├── mtag.py
│   ├── multicast.py
│   ├── pwhash.py
│   ├── qrkode.py
│   ├── res/
│   │   ├── __init__.py
│   │   └── insecure.pem
│   ├── sftpd.py
│   ├── smbd.py
│   ├── ssdp.py
│   ├── star.py
│   ├── stolen/
│   │   ├── __init__.py
│   │   ├── dnslib/
│   │   │   ├── README.md
│   │   │   ├── __init__.py
│   │   │   ├── bimap.py
│   │   │   ├── bit.py
│   │   │   ├── buffer.py
│   │   │   ├── dns.py
│   │   │   ├── label.py
│   │   │   ├── lex.py
│   │   │   └── ranges.py
│   │   ├── ifaddr/
│   │   │   ├── README.md
│   │   │   ├── __init__.py
│   │   │   ├── _posix.py
│   │   │   ├── _shared.py
│   │   │   └── _win32.py
│   │   ├── qrcodegen.py
│   │   └── surrogateescape.py
│   ├── sutil.py
│   ├── svchub.py
│   ├── szip.py
│   ├── tcpsrv.py
│   ├── tftpd.py
│   ├── th_cli.py
│   ├── th_srv.py
│   ├── u2idx.py
│   ├── up2k.py
│   ├── util.py
│   └── web/
│       ├── Makefile
│       ├── Makefile.s1
│       ├── a/
│       │   └── __init__.py
│       ├── baguettebox.js
│       ├── browser.css
│       ├── browser.html
│       ├── browser.js
│       ├── browser2.html
│       ├── cf.html
│       ├── dbg-audio.js
│       ├── idp.html
│       ├── md.css
│       ├── md.html
│       ├── md.js
│       ├── md2.css
│       ├── md2.js
│       ├── mde.css
│       ├── mde.html
│       ├── mde.js
│       ├── msg.html
│       ├── opds.xml
│       ├── opds_osd.xml
│       ├── rups.css
│       ├── rups.html
│       ├── rups.js
│       ├── shares.css
│       ├── shares.html
│       ├── shares.js
│       ├── splash.css
│       ├── splash.html
│       ├── splash.js
│       ├── svcs.html
│       ├── svcs.js
│       ├── tl/
│       │   ├── chi.js
│       │   ├── cze.js
│       │   ├── deu.js
│       │   ├── epo.js
│       │   ├── fin.js
│       │   ├── fra.js
│       │   ├── grc.js
│       │   ├── hun.js
│       │   ├── ita.js
│       │   ├── jpn.js
│       │   ├── kor.js
│       │   ├── nld.js
│       │   ├── nno.js
│       │   ├── nor.js
│       │   ├── pol.js
│       │   ├── por.js
│       │   ├── rus.js
│       │   ├── spa.js
│       │   ├── swe.js
│       │   ├── tur.js
│       │   ├── ukr.js
│       │   └── vie.js
│       ├── ui.css
│       ├── up2k.js
│       ├── util.js
│       └── w.hash.js
├── docs/
│   ├── README.md
│   ├── TODO.md
│   ├── bad-codecs.md
│   ├── biquad.html
│   ├── bufsize.txt
│   ├── changelog.md
│   ├── chungus.conf
│   ├── chunksizes.py
│   ├── copyparty.d/
│   │   ├── foo/
│   │   │   ├── another.conf
│   │   │   └── sibling.conf
│   │   ├── more-users/
│   │   │   ├── david.conf
│   │   │   └── james.conf
│   │   └── some.conf
│   ├── cursed-usecases/
│   │   └── README.md
│   ├── design.txt
│   ├── devnotes.md
│   ├── example.conf
│   ├── example2.conf
│   ├── examples/
│   │   ├── README.md
│   │   ├── docker/
│   │   │   ├── basic-docker-compose/
│   │   │   │   ├── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   ├── idp/
│   │   │   │   └── copyparty.conf
│   │   │   ├── idp-authelia-traefik/
│   │   │   │   ├── README.md
│   │   │   │   ├── authelia/
│   │   │   │   │   ├── configuration.yml
│   │   │   │   │   └── users_database.yml
│   │   │   │   ├── cpp/
│   │   │   │   │   └── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   ├── idp-authentik-traefik/
│   │   │   │   ├── README.md
│   │   │   │   ├── based-on/
│   │   │   │   │   ├── docker-compose-authentik.yml
│   │   │   │   │   └── docker-compose-traefik.yml
│   │   │   │   ├── cpp/
│   │   │   │   │   └── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   └── portainer.md
│   │   └── windows.md
│   ├── hls.html
│   ├── idp.md
│   ├── lics.txt
│   ├── multisearch.html
│   ├── music-analysis.sh
│   ├── notes.bat
│   ├── notes.md
│   ├── notes.sh
│   ├── nuitka.txt
│   ├── pretend-youre-qnap.patch
│   ├── protocol-reference.sh
│   ├── rclone.md
│   ├── rice/
│   │   ├── README.md
│   │   └── rtl.patch
│   ├── synology-dsm.md
│   ├── tcp-debug.sh
│   ├── unirange.py
│   ├── up2k.txt
│   ├── versus.md
│   └── xff.md
├── flake.nix
├── pyproject.toml
├── scripts/
│   ├── bench/
│   │   └── filehash.sh
│   ├── copyparty-android.sh
│   ├── copyparty-repack.sh
│   ├── deps-docker/
│   │   ├── Dockerfile
│   │   ├── Makefile
│   │   ├── busy-mp3.sh
│   │   ├── codemirror.patch
│   │   ├── easymde-ln.patch
│   │   ├── easymde-marked6.patch
│   │   ├── easymde.patch
│   │   ├── genprism.py
│   │   ├── genprism.sh
│   │   ├── markdown-it.patch
│   │   ├── marked-ln.patch
│   │   ├── marked.patch
│   │   ├── mini-fa.css
│   │   ├── mini-fa.sh
│   │   ├── shiftbase.py
│   │   ├── showdown.patch
│   │   └── zopfli.makefile
│   ├── docker/
│   │   ├── Dockerfile.ac
│   │   ├── Dockerfile.dj
│   │   ├── Dockerfile.djd
│   │   ├── Dockerfile.djf
│   │   ├── Dockerfile.djff
│   │   ├── Dockerfile.dju
│   │   ├── Dockerfile.im
│   │   ├── Dockerfile.iv
│   │   ├── Dockerfile.min
│   │   ├── Dockerfile.min.pip
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── base/
│   │   │   ├── Dockerfile.zlibng
│   │   │   ├── Makefile
│   │   │   ├── arbeidspakke.sh
│   │   │   ├── build-no265.sh
│   │   │   ├── patch/
│   │   │   │   └── ffmpeg/
│   │   │   │       ├── aac-lc-only.patch
│   │   │   │       └── libavcodec/
│   │   │   │           ├── aacps.c
│   │   │   │           ├── aacsbr.c
│   │   │   │           ├── aacsbr_fixed.c
│   │   │   │           └── aacsbrdata.h
│   │   │   └── verchk.sh
│   │   ├── devnotes.md
│   │   ├── innvikler.sh
│   │   └── make.sh
│   ├── fusefuzz.py
│   ├── genhelp.sh
│   ├── genlic.py
│   ├── help2html.py
│   ├── help2txt.sh
│   ├── install-githooks.sh
│   ├── lics/
│   │   ├── 1.r13
│   │   ├── 2.r13
│   │   ├── 3.r13
│   │   ├── 4.r13
│   │   ├── 5.r13
│   │   ├── 6.r13
│   │   ├── README.md
│   │   └── rot.py
│   ├── logpack.sh
│   ├── make-pypi-release.sh
│   ├── make-pyz.sh
│   ├── make-rpm.sh
│   ├── make-sfx.sh
│   ├── make-tgz-release.sh
│   ├── patches/
│   │   ├── pyftpdlib-fe80.patch
│   │   └── pyftpdlib-win313.patch
│   ├── prep.sh
│   ├── profile.py
│   ├── py2/
│   │   └── queue/
│   │       └── __init__.py
│   ├── pyinstaller/
│   │   ├── README.md
│   │   ├── build.sh
│   │   ├── depchk.sh
│   │   ├── deps.sha512
│   │   ├── deps.txt
│   │   ├── ffmpeg.patch
│   │   ├── ffmpeg.txt
│   │   ├── icon.sh
│   │   ├── loader.py
│   │   ├── loader.rc
│   │   ├── notes.txt
│   │   ├── up2k.rc
│   │   ├── up2k.sh
│   │   ├── up2k.spec
│   │   └── up2k.spec.sh
│   ├── rls.sh
│   ├── run-tests.sh
│   ├── sfx.ls
│   ├── sfx.py
│   ├── sfx.sh
│   ├── speedtest-fs.py
│   ├── strip_hints/
│   │   └── a.py
│   ├── test/
│   │   ├── ptrav.py
│   │   ├── race.py
│   │   ├── smoketest.py
│   │   └── tftp.sh
│   ├── tl/
│   │   ├── 1.sh
│   │   └── 1.txt
│   ├── tl.js
│   ├── tl.py
│   ├── tlcheck.sh
│   ├── toc.sh
│   ├── uncomment.py
│   └── ziploader.py
├── setup.py
└── tests/
    ├── __init__.py
    ├── ptrav.py
    ├── res/
    │   └── idp/
    │       ├── 1.conf
    │       ├── 2.conf
    │       ├── 3.conf
    │       ├── 4.conf
    │       ├── 5.conf
    │       ├── 6.conf
    │       ├── 7.conf
    │       └── 8.conf
    ├── run.py
    ├── test_cp.py
    ├── test_dedup.py
    ├── test_dots.py
    ├── test_dxml.py
    ├── test_hooks.py
    ├── test_httpcli.py
    ├── test_idp.py
    ├── test_metrics.py
    ├── test_mv.py
    ├── test_shr.py
    ├── test_utils.py
    ├── test_vfs.py
    ├── test_webdav.py
    └── util.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.json
================================================
{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12
    },
    "rules": {
    }
}


================================================
FILE: .gitattributes
================================================
* text eol=lf

*.reg text eol=crlf

*.png binary
*.gif binary
*.gz binary


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: '9001'

---

<!-- NOTE:
**please use english, or include an english translation.** aside from that,  
include all of the information that might possibly be necessary to help diagnose your issue,
especially things like the server config and server logs; that said,
any parts below which are obviously and definitely irrelevant can be skipped -->

### Describe the bug
a description of what the bug is

### To Reproduce
List of steps to reproduce the issue, or, if it's hard to reproduce, then at least a detailed explanation of what you did to run into it

### Expected behavior
a description of what you expected to happen

### Screenshots
if applicable, add screenshots to help explain your problem, such as the kickass crashpage :^)

### Server details (if you are using docker/podman)
remove the ones that are not relevant:
* **server OS / version:** 
* **how you're running copyparty:** (docker/podman/something-else)
* **docker image:** (variant, version, and arch if you know)
* **copyparty arguments and/or config-file:** 

### Server details (if you're NOT using docker/podman)
remove the ones that are not relevant:
* **server OS / version:** 
* **what copyparty did you grab:** (sfx/exe/pip/arch/...)
* **how you're running it:** (in a terminal, as a systemd-service, ...)
* run copyparty with `--version` and grab the last 3 lines (they start with `copyparty`, `CPython`, `sqlite`) and paste them below this line:
* **copyparty arguments and/or config-file:** 

### Client details
if the issue is possibly on the client-side, then mention some of the following:
* the device type and model: 
* OS version: 
* browser version: 

### The rest of the stack
if you are connecting directly to copyparty then that's cool, otherwise please mention everything else between copyparty and the browser (reverseproxy, tunnels, etc.)

### Server log
if the issue might be server-related, include everything that appears in the copyparty log during startup, and also anything else you think might be relevant

### Additional context
any other context about the problem here


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: '9001'

---

<!-- NOTE:
**please use english, or include an english translation.** aside from that,  
all of the below are optional, consider them as inspiration, delete and rewrite at will -->

**is your feature request related to a problem? Please describe.**
a description of what the problem is, for example, `I'm always frustrated when [...]` or `Why is it not possible to [...]`

**Describe the idea / solution you'd like**
a description of what you want to happen

**Describe any alternatives you've considered**
a description of any alternative solutions or features you've considered

**Additional context**
add any other context or screenshots about the feature request here


================================================
FILE: .github/ISSUE_TEMPLATE/something-else.md
================================================
---
name: Something else
about: "┐(゚∀゚)┌"
title: ''
labels: ''
assignees: ''

---




================================================
FILE: .github/branch-rename.md
================================================
modernize your local checkout of the repo like so,
```sh
git branch -m master hovudstraum
git fetch origin
git branch -u origin/hovudstraum hovudstraum
git remote set-head origin -a
```


================================================
FILE: .github/pull_request_template.md
================================================
To show that your contribution is compatible with the MIT License, please include the following text somewhere in this PR description:  
This PR complies with the DCO; https://developercertificate.org/  


================================================
FILE: .gitignore
================================================
# python
__pycache__/
*.py[cod]
*$py.class
MANIFEST.in
MANIFEST
copyparty.egg-info/
.venv/

/buildenv/
/build/
/dist/
/py2/
/sfx*
/pyz/
/unt/
/log/

# ide
*.sublime-workspace

# winmerge
*.bak

# apple pls
.DS_Store

# derived
copyparty/res/COPYING.txt
copyparty/web/deps/
srv/
scripts/docker/base/b/
scripts/docker/base/cver*
scripts/docker/base/test-aac/
scripts/docker/base/whl/
scripts/docker/i/
scripts/deps-docker/uncomment.py
contrib/package/arch/pkg/
contrib/package/arch/src/

# state/logs
up.*.txt
.hist/
scripts/docker/*.out
scripts/docker/*.err
/perf.*

# nix build output link
result
result-*

# IDEA config
.idea/


================================================
FILE: .vscode/launch.json
================================================
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Run copyparty",
            "type": "debugpy",
            "request": "launch",
            "module": "copyparty",
            "console": "integratedTerminal",
            "cwd": "${workspaceFolder}",
            "justMyCode": false,
            "env": {
                "PYDEVD_DISABLE_FILE_VALIDATION": "1",
                "PYTHONWARNINGS": "always"  //error
            },
            "args": [
                //"-nw",  // no-write; for testing uploads without writing to disk
                //"-q",  // quiet; speedboost when console output is not needed

                // # increase debugger performance:
                //"no-htp",
                //"hash-mt=0",
                //"mtag-mt=1",
                //"th-mt=1",

                // # listen for FTP and TFTP
                "--ftp=3921",
                "--ftp-pr=12000-12099",
                "--tftp=3969",

                // # listen on all IPv6, all IPv4, and unix-socket
                "-i::,unix:777:a.sock",

                // # misc
                "--dedup",
                "-e2dsa",
                "-e2ts",
                "--rss",
                "--shr=/shr",
                "--stats",
                "-z",

                // # users + volumes
                "-aed:wark",
                "-vdist:dist:r",
                "-vsrv::r:rw,ed",
                "-vsrv/junk:junk:r:A,ed",
                "--ver"
            ]
        },
        {
            "name": "Run active unit test",
            "type": "debugpy",
            "request": "launch",
            "module": "unittest",
            "console": "integratedTerminal",
            "cwd": "${workspaceFolder}",
            "args": [
                "-v",
                "${file}"
            ]
        },
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": false
        }
    ]
}


================================================
FILE: .vscode/launch.py
================================================
#!/usr/bin/env python3

# takes arguments from launch.json
# is used by no_dbg in tasks.json
# launches 10x faster than mspython debugpy
# and is stoppable with ^C

import re
import os
import sys

print(sys.executable)

import json5
import shlex
import subprocess as sp


with open(".vscode/launch.json", "r", encoding="utf-8") as f:
    tj = f.read()

oj = json5.loads(tj)
argv = oj["configurations"][0]["args"]

try:
    sargv = " ".join([shlex.quote(x) for x in argv])
    print(sys.executable + " -m copyparty " + sargv + "\n")
except:
    pass

argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv]

sfx = ""
if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]):
    sfx = sys.argv[1]
    sys.argv = [sys.argv[0]] + sys.argv[2:]

argv += sys.argv[1:]

if sfx:
    argv = [sys.executable, sfx] + argv
    sp.check_call(argv)
elif re.search(" -j ?[0-9]", " ".join(argv)):
    argv = [sys.executable, "-Wa", "-m", "copyparty"] + argv
    sp.check_call(argv)
else:
    sys.path.insert(0, os.getcwd())
    from copyparty.__main__ import main as copyparty

    try:
        copyparty(["a"] + argv)
    except SystemExit as ex:
        if ex.code:
            raise

print("\n\033[32mokke\033[0m")
sys.exit(1)


================================================
FILE: .vscode/settings.json
================================================
{
    "workbench.colorCustomizations": {
        // https://ocv.me/dot/bifrost.html
        "terminal.background": "#1e1e1e",
        "terminal.foreground": "#d2d2d2",
        "terminalCursor.background": "#93A1A1",
        "terminalCursor.foreground": "#93A1A1",
        "terminal.ansiBlack": "#404040",
        "terminal.ansiRed": "#f03669",
        "terminal.ansiGreen": "#b8e346",
        "terminal.ansiYellow": "#ffa402",
        "terminal.ansiBlue": "#02a2ff",
        "terminal.ansiMagenta": "#f65be3",
        "terminal.ansiCyan": "#3da698",
        "terminal.ansiWhite": "#d2d2d2",
        "terminal.ansiBrightBlack": "#606060",
        "terminal.ansiBrightRed": "#c75b79",
        "terminal.ansiBrightGreen": "#c8e37e",
        "terminal.ansiBrightYellow": "#ffbe4a",
        "terminal.ansiBrightBlue": "#71cbff",
        "terminal.ansiBrightMagenta": "#b67fe3",
        "terminal.ansiBrightCyan": "#9cf0ed",
        "terminal.ansiBrightWhite": "#ffffff",
    },
    "python.terminal.activateEnvironment": false,
    "python.analysis.enablePytestSupport": false,
    "python.analysis.typeCheckingMode": "standard",
    "python.testing.pytestEnabled": false,
    "python.testing.unittestEnabled": true,
    "python.testing.unittestArgs": [
        "-v",
        "-s",
        "./tests",
        "-p",
        "test_*.py"
    ],
    // python3 -m isort --py=27 --profile=black ~/dev/copyparty/{copyparty,tests}/*.py && python3 -m black -t py27 ~/dev/copyparty/{copyparty,tests,bin}/*.py $(find ~/dev/copyparty/copyparty/stolen -iname '*.py')
    "editor.formatOnSave": false,
    "[html]": {
        "editor.formatOnSave": false,
        "editor.autoIndent": "keep",
    },
    "[css]": {
        "editor.formatOnSave": false,
    },
    "files.associations": {
        "*.makefile": "makefile"
    },
}

================================================
FILE: .vscode/tasks.json
================================================
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "pre",
            "command": "true;rm -rf inc/* inc/.hist/;mkdir -p inc;",
            "type": "shell"
        },
        {
            "label": "no_dbg",
            "type": "shell",
            "command": "${config:python.pythonPath}",
            "args": [
                "-Wa", //-We
                ".vscode/launch.py"
            ]
        }
    ]
}

================================================
FILE: CODE_OF_CONDUCT.md
================================================
in the words of Abraham Lincoln:

> Be excellent to each other... and... PARTY ON, DUDES!

more specifically I'll paraphrase some examples from a german automotive corporation as they cover all the bases without being too wordy

## Examples of unacceptable behavior
* intimidation, harassment, trolling
* insulting, derogatory, harmful or prejudicial comments
* posting private information without permission
* political or personal attacks

## Examples of expected behavior
* being nice, friendly, welcoming, inclusive, mindful and empathetic
* acting considerate, modest, respectful
* using polite and inclusive language
* criticize constructively and accept constructive criticism
* respect different points of view

## finally and even more specifically,
* parse opinions and feedback objectively without prejudice
  * it's the message that matters, not who said it

aaand that's how you say `be nice` in a way that fills half a floppy w


================================================
FILE: CONTRIBUTING.md
================================================
* **found a bug?** [create an issue!](https://github.com/9001/copyparty/issues) or let me know in the [discord](https://discord.gg/25J8CdTT6G) :>
* **fixed a bug?** create a PR or post a patch! big thx in advance :>
* **have a cool idea?** let's discuss it! anywhere's fine, you choose.

but please:



# do not use AI / LLM when writing code

copyparty is 100% organic, free-range, human-written software!

> ⚠ you are now entering a no-copilot zone

the *only* place where LLM/AI *may* be accepted is for [localization](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#translations) if you are fluent and have confirmed that the translation is accurate.

sorry for the harsh tone, but this is important to me 🙏



# contribution ideas


## documentation

I think we can agree that the documentation leaves a LOT to be desired. I've realized I'm not exactly qualified for this 😅 but maybe the [soon-to-come setup GUI](https://github.com/9001/copyparty/issues/57) will make this more manageable. The best documentation is the one that never had to be written, right? :> so I suppose we can give this a wait-and-see approach for a bit longer.


## crazy ideas & features

assuming they won't cause too much problems or side-effects :> 

i think someone was working on a way to list directories over DNS for example...

if you wanna have a go at coding it up yourself then maybe mention the idea on discord before you get too far, otherwise just go nuts 👍


## others

aside from documentation and ideas, some other things that would be cool to have some help with is:

* **translations** -- the copyparty web-UI has translations in [copyparty/web/tl](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl); if you'd like to [add a translation](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#translations) for another language then that'd be welcome! and if that language has a grammar that doesn't fit into the way the strings are assembled, then we'll fix that as we go :>

  * but please note that support for [RTL (Right-to-Left) languages](https://en.wikipedia.org/wiki/Right-to-left_script) is currently not planned, since the javascript is a bit too jank for that

* **UI ideas** -- at some point I was thinking of rewriting the UI in react/preact/something-not-vanilla-javascript, but I'll admit the comfiness of not having any build stage combined with raw performance has kinda convinced me otherwise :p but I'd be very open to ideas on how the UI could be improved, or be more intuitive.

* **docker improvements** -- I don't really know what I'm doing when it comes to containers, so I'm sure there's a *huge* room for improvement here, mainly regarding how you're supposed to use the container with kubernetes / docker-compose / any of the other popular ways to do things. At some point I swear I'll start learning about docker so I can pick up clach04's [docker-compose draft](https://github.com/9001/copyparty/issues/38) and learn how that stuff ticks, unless someone beats me to it!

* **packaging** for various linux distributions -- this could either be as simple as just plopping the sfx.py in the right place and calling that from systemd (the archlinux package [originally did this](https://github.com/9001/copyparty/pull/18)); maybe with a small config-file which would cause copyparty to load settings from `/etc/copyparty.d` (like the [archlinux package](https://github.com/9001/copyparty/tree/hovudstraum/contrib/package/arch) does with `copyparty.conf`), or it could be a proper installation of the copyparty python package into /usr/lib or similar (the archlinux package [eventually went for this approach](https://github.com/9001/copyparty/pull/26))

  * [fpm](https://github.com/jordansissel/fpm) can probably help with the technical part of it, but someone needs to handle distro relations :-)

* **software integration** -- I'm sure there's a lot of usecases where copyparty could complement something else, or the other way around, so any ideas or any work in this regard would be dope. This doesn't necessarily have to be code inside copyparty itself;

  * [hooks](https://github.com/9001/copyparty/tree/hovudstraum/bin/hooks) -- these are small programs which are called by copyparty when certain things happen (files are uploaded, someone hits a 404, etc.), and could be a fun way to add support for more usecases

  * [parser plugins](https://github.com/9001/copyparty/tree/hovudstraum/bin/mtag) -- if you want to have copyparty analyze and index metadata for some oddball file-formats, then additional plugins would be neat :>


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 ed <oss@ocv.me>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<img src="https://github.com/9001/copyparty/raw/hovudstraum/docs/logo.svg" width="250" align="right"/>

### 💾🎉 copyparty

turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser

* server only needs Python (2 or 3), all dependencies optional
* 🔌 protocols: [http(s)](#the-browser) // [webdav](#webdav-server) // [sftp](#sftp-server) // [ftp(s)](#ftp-server) // [tftp](#tftp-server) // [smb/cifs](#smb-server)
* 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)

👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running on a nuc in my basement

📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)

🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm) // 👉 **[feature-showcase](https://a.ocv.me/pub/demo/showcase-hq.webm)** ([youtube](https://www.youtube.com/watch?v=15_-hgsX2V0))

built in Norway 🇳🇴 with contributions from [not-norway](https://github.com/9001/copyparty/graphs/contributors)


## readme toc

* top
    * [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
        * [mirrors](#mirrors) - other places to download copyparty from
        * [at home](#at-home) - make it accessible over the internet
        * [on servers](#on-servers) - you may also want these, especially on servers
    * [features](#features) - also see [comparison to similar software](./docs/versus.md)
    * [testimonials](#testimonials) - small collection of user feedback
* [motivations](#motivations) - project goals / philosophy
    * [notes](#notes) - general notes
* [bugs](#bugs) - roughly sorted by chance of encounter
    * [not my bugs](#not-my-bugs) - same order here too
* [breaking changes](#breaking-changes) - upgrade notes
* [FAQ](#FAQ) - "frequently" asked questions
* [accounts and volumes](#accounts-and-volumes) - per-folder, per-user permissions
    * [shadowing](#shadowing) - hiding specific subfolders
    * [dotfiles](#dotfiles) - unix-style hidden files/folders
* [the browser](#the-browser) - accessing a copyparty server using a web-browser
    * [tabs](#tabs) - the main tabs in the ui
    * [hotkeys](#hotkeys) - the browser has the following hotkeys
    * [navpane](#navpane) - switching between breadcrumbs or navpane
    * [thumbnails](#thumbnails) - press `g` or `田` to toggle grid-view instead of the file listing
    * [zip downloads](#zip-downloads) - download folders (or file selections) as `zip` or `tar` files
    * [uploading](#uploading) - drag files/folders into the web-browser to upload
        * [file-search](#file-search) - dropping files into the browser also lets you see if they exist on the server
        * [unpost](#unpost) - undo/delete accidental uploads
        * [self-destruct](#self-destruct) - uploads can be given a lifetime
        * [race the beam](#race-the-beam) - download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm))
        * [incoming files](#incoming-files) - the control-panel shows the ETA for all incoming files
    * [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
    * [shares](#shares) - share a file or folder by creating a temporary link
    * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
    * [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
    * [opds feeds](#opds-feeds) - browse and download files from your e-book reader
    * [recent uploads](#recent-uploads) - list all recent uploads
    * [media player](#media-player) - plays almost every audio format there is
        * [playlists](#playlists) - create and play [m3u8](https://en.wikipedia.org/wiki/M3U) playlists
        * [creating a playlist](#creating-a-playlist) - with a standalone mediaplayer or copyparty
        * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
        * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
    * [textfile viewer](#textfile-viewer) - with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/))
    * [markdown viewer](#markdown-viewer) - and there are *two* editors
        * [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
    * [other tricks](#other-tricks)
    * [searching](#searching) - search by size, date, path/name, mp3-tags, ...
* [server config](#server-config) - using arguments or config files, or a mix of both
    * [version-checker](#version-checker) - sleep better at night
    * [logging](#logging) - serverlog is sent to stdout by default
    * [zeroconf](#zeroconf) - announce enabled services on the LAN ([pic](https://user-images.githubusercontent.com/241032/215344737-0eae8d98-9496-4256-9aa8-cd2f6971810d.png))
        * [mdns](#mdns) - LAN domain-name and feature announcer
        * [ssdp](#ssdp) - windows-explorer announcer
    * [qr-code](#qr-code) - print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/194728533-6f00849b-c6ac-43c6-9359-83e454d11e00.png) for quick access
    * [ftp server](#ftp-server) - an FTP server can be started using `--ftp 3921`
    * [sftp server](#sftp-server) - goes roughly 700 MiB/s (slower than webdav and ftp)
    * [webdav server](#webdav-server) - with read-write support
        * [connecting to webdav from windows](#connecting-to-webdav-from-windows) - using the GUI
    * [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
    * [smb server](#smb-server) - unsafe, slow, not recommended for wan
    * [browser ux](#browser-ux) - tweaking the ui
    * [opengraph](#opengraph) - discord and social-media embeds
    * [file deduplication](#file-deduplication) - enable symlink-based upload deduplication
    * [file indexing](#file-indexing) - enable music search, upload-undo, and better dedup
        * [exclude-patterns](#exclude-patterns) - to save some time
        * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
        * [periodic rescan](#periodic-rescan) - filesystem monitoring
    * [upload rules](#upload-rules) - set upload rules using volflags
    * [compress uploads](#compress-uploads) - files can be autocompressed on upload
    * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
    * [other flags](#other-flags)
    * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
    * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
        * [metadata from xattrs](#metadata-from-xattrs) - unix extended file attributes
    * [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
    * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
        * [zeromq](#zeromq) - event-hooks can send zeromq messages
        * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
    * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
    * [ip auth](#ip-auth) - autologin based on IP range (CIDR)
        * [restrict to ip](#restrict-to-ip) - limit a user to certain IP ranges (CIDR)
    * [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
        * [generic header auth](#generic-header-auth) - other ways to auth by header
    * [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
    * [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
    * [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed
    * [themes](#themes)
    * [complete examples](#complete-examples)
    * [listen on port 80 and 443](#listen-on-port-80-and-443) - become a *real* webserver
    * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
        * [real-ip](#real-ip) - teaching copyparty how to see client IPs
        * [reverse-proxy performance](#reverse-proxy-performance)
    * [permanent cloudflare tunnel](#permanent-cloudflare-tunnel) - if you have a domain and want to get your copyparty online real quick
    * [prometheus](#prometheus) - metrics/stats can be enabled
    * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
        * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
        * [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
        * [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
        * [feature beefybits](#feature-beefybits) - force-enable features with known issues on your OS/env
* [packages](#packages) - the party might be closer than you think
    * [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
    * [fedora package](#fedora-package) - does not exist yet
    * [homebrew formulae](#homebrew-formulae) - `brew install copyparty ffmpeg`
    * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
    * [nixos module](#nixos-module)
* [browser support](#browser-support) - TLDR: yes
* [server hall of fame](#server-hall-of-fame) - unexpected things that run copyparty
* [client examples](#client-examples) - interact with copyparty using non-browser clients
    * [folder sync](#folder-sync) - sync folders to/from copyparty
    * [mount as drive](#mount-as-drive) - a remote copyparty server as a local filesystem
* [android app](#android-app) - upload to copyparty with one tap
* [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
* [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
    * [client-side](#client-side) - when uploading files
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
    * [gotchas](#gotchas) - behavior that might be unexpected
    * [cors](#cors) - cross-site request config
    * [filekeys](#filekeys) - prevent filename bruteforcing
        * [dirkeys](#dirkeys) - share specific folders in a volume
    * [password hashing](#password-hashing) - you can hash passwords
    * [https](#https) - both HTTP and HTTPS are accepted
* [recovering from crashes](#recovering-from-crashes)
    * [client crashes](#client-crashes)
        * [firefox wsod](#firefox-wsod) - firefox 87 can crash during uploads
* [HTTP API](#HTTP-API) - see [devnotes](./docs/devnotes.md#http-api)
* [dependencies](#dependencies) - mandatory deps
    * [optional dependencies](#optional-dependencies) - enable bonus features
        * [dependency chickenbits](#dependency-chickenbits) - prevent loading an optional dependency
        * [dependency unvendoring](#dependency-unvendoring) - force use of system modules
    * [optional gpl stuff](#optional-gpl-stuff)
* [sfx](#sfx) - the self-contained "binary" (recommended!)
    * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
    * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
* [install on android](#install-on-android)
* [install on iOS](#install-on-iOS)
* [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
* [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)


## quickstart

just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉

> ℹ️ the sfx is a [self-extractor](https://github.com/9001/copyparty/issues/270) which unpacks an embedded `tar.gz` into `$TEMP` -- if this looks too scary, you can use the [zipapp](#zipapp) which has slightly worse performance

* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
* or install [on arch](#arch-package) / [homebrew](#homebrew-formulae) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
* or if you are on android, [install copyparty in termux](#install-on-android)
* or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS)
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
* or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
* or if your OS is dead, give the [bootable flashdrive / cd-rom](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) a spin
* or if you don't trust copyparty yet and want to isolate it a little, then...
  * ...maybe [prisonparty](./bin/prisonparty.sh) to create a tiny [chroot](https://wiki.archlinux.org/title/Chroot) (very portable),
  * ...or [bubbleparty](./bin/bubbleparty.sh) to wrap it in [bubblewrap](https://github.com/containers/bubblewrap) (much better)
* or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
  * docker has all deps built-in, so skip this step:

enable thumbnails (images/audio/video), media indexing, and audio transcoding by installing some recommended deps:

* **Alpine:** `apk add py3-pillow ffmpeg`
* **Debian:** `apt install --no-install-recommends python3-pil ffmpeg`
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg --allowerasing`
* **FreeBSD:** `pkg install py39-sqlite3 py39-pillow ffmpeg`
* **MacOS:** `port install py-Pillow ffmpeg`
* **MacOS** (alternative): `brew install pillow ffmpeg`
* **Windows:** `python -m pip install --user -U Pillow`
  * install [python](https://www.python.org/downloads/windows/) and [ffmpeg](#optional-dependencies) manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
  * copyparty.exe comes with `Pillow` and only needs [ffmpeg](#optional-dependencies) for mediatags/videothumbs
* see [optional dependencies](#optional-dependencies) to enable even more features

running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)

or see [some usage examples](#complete-examples) for inspiration, or the [complete windows example](./docs/examples/windows.md)

some recommended options:
* `-e2dsa` enables general [file indexing](#file-indexing)
* `-e2ts` enables audio metadata indexing (needs either FFprobe or Mutagen)
* `-v /mnt/music:/music:r:rw,foo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, and read-write for user `foo`, password `bar`
  * replace `:r:rw,foo` with `:r,foo` to only make the folder readable by `foo` and nobody else
  * see [accounts and volumes](#accounts-and-volumes) (or [`--help-accounts`](https://copyparty.eu/cli/#accounts-help-page)) for the syntax and other permissions


### mirrors

other places to download copyparty from  (non-github links):

* https://copyparty.eu/ (hetzner, finland, official mirror):
  * https://copyparty.eu/py = https://copyparty.eu/copyparty-sfx.py = the sfx
  * https://copyparty.eu/en = https://copyparty.eu/copyparty-en.py = the english-only sfx
  * https://copyparty.eu/pyz = https://copyparty.eu/copyparty.pyz = the zipapp
  * https://copyparty.eu/enz = https://copyparty.eu/copyparty-en.pyz = the enterprise pyz
  * https://copyparty.eu/cli = online cli helptext


### at home

make it accessible over the internet  by starting a [cloudflare quicktunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) like so:

first download [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) and then start the tunnel with `cloudflared tunnel --url http://127.0.0.1:3923`

as the tunnel starts, it will show a URL which you can share to let anyone browse your stash or upload files to you

but if you have a domain, then you probably want to skip the random autogenerated URL and instead make a [permanent cloudflare tunnel](#permanent-cloudflare-tunnel)

since people will be connecting through cloudflare, run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly


### on servers

you may also want these, especially on servers:

* [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
* [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
* [contrib/podman-systemd/](contrib/podman-systemd/) to run copyparty in a Podman container as a systemd service (see guide inside)
* [contrib/openrc/copyparty](contrib/openrc/copyparty) to run copyparty on Alpine / Gentoo
* [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
* [nixos module](#nixos-module) to run copyparty on NixOS hosts
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to [reverse-proxy](#reverse-proxy) behind nginx (for better https)

and remember to open the ports you want; here's a complete example including every feature copyparty has to offer:
```
firewall-cmd --permanent --add-port={80,443,3921,3922,3923,3945,3990}/tcp  # --zone=libvirt
firewall-cmd --permanent --add-port=12000-12099/tcp  # --zone=libvirt
firewall-cmd --permanent --add-port={69,1900,3969,5353}/udp  # --zone=libvirt
firewall-cmd --reload
```
(69:tftp, 1900:ssdp, 3921:ftp, 3922:sftp, 3923:http/https, 3945:smb, 3969:tftp, 3990:ftps, 5353:mdns, 12000:passive-ftp)


## features

also see [comparison to similar software](./docs/versus.md)

* backend stuff
  * ☑ IPv6 + unix-sockets
  * ☑ [multiprocessing](#performance) (actual multithreading)
  * ☑ volumes (mountpoints)
  * ☑ [accounts](#accounts-and-volumes)
  * ☑ [ftp server](#ftp-server)
  * ☑ [tftp server](#tftp-server)
  * ☑ [webdav server](#webdav-server)
  * ☑ [smb/cifs server](#smb-server)
  * ☑ [qr-code](#qr-code) for quick access
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
  * ☑ [event hooks](#event-hooks) / script runner
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
  * ☑ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64, SGI IRIX)
* upload
  * ☑ basic: plain multipart, ie6 support
  * ☑ [up2k](#uploading): js, resumable, multithreaded
    * **no filesize limit!** even on Cloudflare
  * ☑ stash: simple PUT filedropper
  * ☑ filename randomizer
  * ☑ write-only folders
  * ☑ [unpost](#unpost): undo/delete accidental uploads
  * ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
  * ☑ [race the beam](#race-the-beam) (almost like peer-to-peer)
  * ☑ symlink/discard duplicates (content-matching)
* download
  * ☑ single files in browser
  * ☑ [folders as zip / tar files](#zip-downloads)
  * ☑ [FUSE client](https://github.com/9001/copyparty/tree/hovudstraum/bin#partyfusepy) (read-only)
* browser
  * ☑ [navpane](#navpane) (directory tree sidebar)
  * ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
  * ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
    * ☑ play video files as audio (converted on server)
    * ☑ create and play [m3u8 playlists](#playlists)
  * ☑ image gallery with webm player
    * ☑ and cbz manga/comics reader
  * ☑ [textfile browser](#textfile-viewer) with syntax highlighting
    * ☑ realtime streaming of growing files (logfiles and such)
  * ☑ [thumbnails](#thumbnails)
    * ☑ ...of images using Pillow, pyvips, or FFmpeg
    * ☑ ...of RAW images using rawpy
    * ☑ ...of videos using FFmpeg
    * ☑ ...of audio (spectrograms) using FFmpeg
    * ☑ cache eviction (max-age; maybe max-size eventually)
  * ☑ multilingual UI (english, norwegian, chinese, [add your own](./docs/rice/#translations)))
  * ☑ SPA (browse while uploading)
* server indexing
  * ☑ [locate files by contents](#file-search)
  * ☑ search by name/path/date/size
  * ☑ [search by ID3-tags etc.](#searching)
* client support
  * ☑ [folder sync](#folder-sync) (one-way only; full sync will never be supported)
  * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
  * ☑ [opengraph](#opengraph) (discord embeds)
* markdown
  * ☑ [viewer](#markdown-viewer)
  * ☑ editor (sure why not)
  * ☑ [variables](#markdown-vars)

PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙


## testimonials

small collection of user feedback

`good enough`, `surprisingly correct`, `certified good software`, `just works`, `why`, `wow this is better than nextcloud`

* UI просто ужасно. Если буду описывать детально не смогу удержаться в рамках приличий


# motivations

project goals / philosophy

* inverse unix philosophy -- do all the things, and do an *okay* job
  * quick drop-in service to get a lot of features in a pinch
  * some of [the alternatives](./docs/versus.md) might be a better fit for you
* run anywhere, support everything
  * as many web-browsers and python versions as possible
    * every browser should at least be able to browse, download, upload files
    * be a good emergency solution for transferring stuff between ancient boxes
  * minimal dependencies
    * but optional dependencies adding bonus-features are ok
    * everything being plaintext makes it possible to proofread for malicious code
  * no preparations / setup necessary, just run the sfx (which is also plaintext)
* adaptable, malleable, hackable
  * no build steps; modify the js/python without needing node.js or anything like that

becoming rich is specifically *not* a motivation, but if you wanna donate then see my [github profile](https://github.com/9001) regarding donations for my FOSS stuff in general (also THANKS!)


## notes

general notes:
* paper-printing is affected by dark/light-mode! use lightmode for color, darkmode for grayscale
  * because no browsers currently implement the media-query to do this properly orz

browser-specific:
* iPhone/iPad: use Firefox to download files
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
* Android-Firefox: takes a while to select files (their fix for ☝️)
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
* Desktop-Firefox: [may stop you from unplugging USB flashdrives](https://bugzilla.mozilla.org/show_bug.cgi?id=1792598) until you visit `about:memory` and click `Minimize memory usage`

server-os-specific:
* RHEL8 / Rocky8: you can run copyparty using `/usr/libexec/platform-python`

server notes:
* pypy is supported but regular cpython is faster if you enable the database


# bugs

roughly sorted by chance of encounter

* general:
  * `--th-ff-jpg` may fix video thumbnails on some FFmpeg versions (macos, some linux)
  * `--th-ff-swr` may fix audio thumbnails on some FFmpeg versions
  * if the `up2k.db` (filesystem index) is on a samba-share or network disk, you'll get unpredictable behavior if the share is disconnected for a bit
    * use `--hist` or the `hist` volflag (`-v [...]:c,hist=/tmp/foo`) to place the db and thumbnails on a local disk instead
    * or, if you only want to move the db (and not the thumbnails), then use `--dbpath` or the `dbpath` volflag
  * all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
  * probably more, pls let me know

* python 3.4 and older (including 2.7):
  * many rare and exciting edge-cases because [python didn't handle EINTR yet](https://peps.python.org/pep-0475/)
    * downloads from copyparty may suddenly fail, but uploads *should* be fine

* python 2.7 on Windows:
  * cannot index non-ascii filenames with `-e2d`
  * cannot handle filenames with mojibake

if you have a new exciting bug to share, see [reporting bugs](#reporting-bugs)


## not my bugs

same order here too

* [Chrome issue 1317069](https://bugs.chromium.org/p/chromium/issues/detail?id=1317069) -- if you try to upload a folder which contains symlinks by dragging it into the browser, the symlinked files will not get uploaded

* [Chrome issue 1352210](https://bugs.chromium.org/p/chromium/issues/detail?id=1352210) -- plaintext http may be faster at filehashing than https (but also extremely CPU-intensive)

* [Chrome issue 383568268](https://issues.chromium.org/issues/383568268) -- filereaders in webworkers can OOM / crash the browser-tab
  * copyparty has a workaround which seems to work well enough

* [Firefox issue 1790500](https://bugzilla.mozilla.org/show_bug.cgi?id=1790500) -- entire browser can crash after uploading ~4000 small files

* Windows: Uploading from a webbrowser may fail with "directory iterator got stuck" due to the [max path length](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry); try moving the files somewhere shorter before uploading

* Android: music playback randomly stops due to [battery usage settings](#fix-unreliable-playback-on-android)

* iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
  * `AudioContext` will probably never be a viable workaround as apple introduces new issues faster than they fix current ones

* iPhones: music volume goes on a rollercoaster during song changes
  * nothing I can do about it because `AudioContext` is still broken in safari

* iPhones: the preload feature (in the media-player-options tab) can cause a tiny audio glitch 20sec before the end of each song, but disabling it may cause worse iOS bugs to appear instead
  * just a hunch, but disabling preloading may cause playback to stop entirely, or possibly mess with bluetooth speakers
  * tried to add a tooltip regarding this but looks like apple broke my tooltips

* iPhones: preloaded awo files make safari log MEDIA_ERR_NETWORK errors as playback starts, but the song plays just fine so eh whatever
  * awo, opus-weba, is apple's new take on opus support, replacing opus-caf which was technically limited to cbr opus

* iPhones: preloading another awo file may cause playback to stop
  * can be somewhat mitigated with `mp.au.play()` in `mp.onpreload` but that can hit a race condition in safari that starts playing the same audio object twice in parallel...

* Windows: folders cannot be accessed if the name ends with `.`
  * python or windows bug

* Windows: msys2-python 3.8.6 occasionally throws `RuntimeError: release unlocked lock` when leaving a scoped mutex in up2k
  * this is an msys2 bug, the regular windows edition of python is fine

* VirtualBox: sqlite throws `Disk I/O Error` when running in a VM and the up2k database is in a vboxsf
  * use `--hist` or the `hist` volflag (`-v [...]:c,hist=/tmp/foo`) to place the db and thumbnails inside the vm instead
    * or, if you only want to move the db (and not the thumbnails), then use `--dbpath` or the `dbpath` volflag
  * also happens on mergerfs, so put the db elsewhere

* Ubuntu: dragging files from certain folders into firefox or chrome is impossible
  * due to snap security policies -- see `snap connections firefox` for the allowlist, `removable-media` permits all of `/mnt` and `/media` apparently


# breaking changes

upgrade notes

* `1.9.16` (2023-11-04):
  * `--stats`/prometheus: `cpp_bans` renamed to `cpp_active_bans`, and that + `cpp_uptime` are gauges
* `1.6.0` (2023-01-29):
  * http-api: delete/move is now `POST` instead of `GET`
  * everything other than `GET` and `HEAD` must pass [cors validation](#cors)
* `1.5.0` (2022-12-03): [new chunksize formula](https://github.com/9001/copyparty/commit/54e1c8d261df) for files larger than 128 GiB
  * **users:** upgrade to the latest [cli uploader](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) if you use that
  * **devs:** update third-party up2k clients (if those even exist)


# FAQ

"frequently" asked questions

* CopyParty?
  * nope! the name is either copyparty (all-lowercase) or Copyparty -- it's [one word](https://en.wiktionary.org/wiki/copyparty) after all :>

* what is a volflag?
  * per-volume configuration; many (not all) global-options can be set as volflags, and most (not all) volflags can be set as global-options; [complete list of volflags](https://copyparty.eu/cli/#flags-help-page)

* what is a volume?
  * a mapping from a URL (`/music/`) to a folder on your server's local filesystem (`C:\Users\ed\Music`) which can then be accessed through copyparty, depending on the permissions and options you set on it -- see [accounts and volumes](#accounts-and-volumes)

* can I change the 🌲 spinning pine-tree loading animation?
  * [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(

* is it possible to block read-access to folders unless you know the exact URL for a particular file inside?
  * yes, using the [`g` permission](#accounts-and-volumes), see the examples there
  * you can also do this with linux filesystem permissions; `chmod 111 music` will make it possible to access files and folders inside the `music` folder but not list the immediate contents -- also works with other software, not just copyparty

* can I link someone to a password-protected volume/file by including the password in the URL?
  * yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
    * if you have enabled `--usernames` then do `?pw=username:password` instead
    * `?pw` can be disabled with `--pw-urlp=A` but this breaks support for many clients

* how do I stop `.hist` folders from appearing everywhere on my HDD?
  * by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)

* can I make copyparty download a file to my server if I give it a URL?
  * yes, using [hooks](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/wget.py)

* firefox refuses to connect over https, saying "Secure Connection Failed" or "SEC_ERROR_BAD_SIGNATURE", but the usual button to "Accept the Risk and Continue" is not shown
  * firefox has corrupted its certstore; fix this by exiting firefox, then find and delete the file named `cert9.db` somewhere in your firefox profile folder

* the server keeps saying `thank you for playing` when I try to access the website
  * you've gotten banned for malicious traffic! if this happens by mistake, and you're running a reverse-proxy and/or something like cloudflare, see [real-ip](#real-ip) on how to fix this

* copyparty seems to think I am using http, even though the URL is https
  * your reverse-proxy is not sending the `X-Forwarded-Proto: https` header; this could be because your reverse-proxy itself is confused. Ensure that none of the intermediates (such as cloudflare) are terminating https before the traffic hits your entrypoint

* thumbnails are broken (you get a colorful square which says the filetype instead)
  * you need to install `FFmpeg` or `Pillow`; see [thumbnails](#thumbnails)

* thumbnails are broken, specifically for photos and videos taken by iphones
  * the [docker image](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker) and [bootable flashdrive](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) are not able to read heif/heic images and h265/HEVC video due to [legal reasons](docs/bad-codecs.md)

* thumbnails are broken (some images appear, but other files just get a blank box, and/or the broken-image placeholder)
  * probably due to a reverse-proxy messing with the request URLs and stripping the query parameters (`?th=w`), so check your URL rewrite rules
  * could also be due to incorrect caching settings in reverse-proxies and/or CDNs, so make sure that nothing is set to ignore the query string
  * could also be due to misbehaving privacy-related browser extensions, so try to disable those

* i want to learn python and/or programming and am considering looking at the copyparty source code in that occasion
  * ```bash
     _|  _      __   _  _|_
    (_| (_)     | | (_)  |_
    ```


# accounts and volumes

per-folder, per-user permissions  - if your setup is getting complex, consider making a [config file](./docs/example.conf) instead of using arguments
* much easier to manage, and you can modify the config at runtime with `systemctl reload copyparty` or more conveniently using the `[reload cfg]` button in the control-panel (if the user has `a`/admin in any volume)
  * changes to the `[global]` config section requires a restart to take effect

a quick summary can be seen using [`--help-accounts`](https://copyparty.eu/cli/#accounts-help-page)

configuring accounts/volumes with arguments:
* `-a usr:pwd` adds account `usr` with password `pwd`
* `-v .::r` adds current-folder `.` as the webroot, `r`eadable by anyone
  * the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
  * granting the same permissions to multiple accounts:  
    `-v .::r,usr1,usr2:rw,usr3,usr4` = usr1/2 read-only, 3/4 read-write

permissions:
* `r` (read): browse folder contents, download files, download as zip/tar, see filekeys/dirkeys
* `w` (write): upload files, move/copy files *into* this folder
* `m` (move): move files/folders *from* this folder
* `d` (delete): delete files/folders
* `.` (dots): user can ask to show dotfiles in directory listings
* `g` (get): only download files, cannot see folder contents or zip/tar
* `G` (upget): same as `g` except uploaders get to see their own [filekeys](#filekeys) (see `fk` in examples below)
* `h` (html): same as `g` except folders return their index.html, and filekeys are not necessary for index.html
* `a` (admin): can see upload time, uploader IPs, config-reload
* `A` ("all"): same as `rwmda.` (read/write/move/delete/admin/dotfiles)

examples:
* add accounts named u1, u2, u3 with passwords p1, p2, p3: `-a u1:p1 -a u2:p2 -a u3:p3`
* make folder `/srv` the root of the filesystem, read-only by anyone: `-v /srv::r`
* make folder `/mnt/music` available at `/music`, read-only for u1 and u2, read-write for u3: `-v /mnt/music:music:r,u1,u2:rw,u3`
  * unauthorized users accessing the webroot can see that the `music` folder exists, but cannot open it
* make folder `/mnt/incoming` available at `/inc`, write-only for u1, read-move for u2: `-v /mnt/incoming:inc:w,u1:rm,u2`
  * unauthorized users accessing the webroot can see that the `inc` folder exists, but cannot open it
  * `u1` can open the `inc` folder, but cannot see the contents, only upload new files to it
  * `u2` can browse it and move files *from* `/inc` into any folder where `u2` has write-access
* make folder `/mnt/ss` available at `/i`, read-write for u1, get-only for everyone else, and enable filekeys: `-v /mnt/ss:i:rw,u1:g:c,fk=4`
  * `c,fk=4` sets the `fk` ([filekey](#filekeys)) volflag to 4, meaning each file gets a 4-character accesskey
  * `u1` can upload files, browse the folder, and see the generated filekeys
  * other users cannot browse the folder, but can access the files if they have the full file URL with the filekey
  * replacing the `g` permission with `wg` would let anonymous users upload files, but not see the required filekey to access it
  * replacing the `g` permission with `wG` would let anonymous users upload files, receiving a working direct link in return

if you want to grant access to all users who are logged in, the group `acct` will always contain all known users, so for example `-v /mnt/music:music:r,@acct`

* to do the opposite, granting access to everyone who is NOT logged in. `*,-@acct` does the trick, for example `-v /srv/welcome:welcome:r,*,-@acct`
* single users can also be subtracted from a group: `@admins,-james`

anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour

and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`

* you can also `PRTY_CONFIG=foobar.conf python copyparty-sfx.py` (convenient in docker etc)

```yaml
[accounts]
  u1: p1  # create account "u1" with password "p1"
  u2: p2  #  (note that comments must have
  u3: p3  #   two spaces before the # sign)

[groups]
  g1: u1, u2  # create a group

[/]     # this URL will be mapped to...
  /srv  # ...this folder on the server filesystem
  accs:
    r: *  # read-only for everyone, no account necessary

[/music]       # create another volume at this URL,
  /mnt/music   # which is mapped to this folder
  accs:
    r: u1, u2  # only these accounts can read,
    r: @g1     # (exactly the same, just with a group instead)
    r: @acct   # (alternatively, ALL users who are logged in)
    rw: u3     # and only u3 can read-write

[/inc]
  /mnt/incoming
  accs:
    w: u1   # u1 can upload but not see/download any files,
    rm: u2  # u2 can browse + move files out of this volume

[/i]
  /mnt/ss
  accs:
    rw: u1  # u1 can read-write,
    g: *    # everyone can access files if they know the URL
  flags:
    fk: 4   # each file URL will have a 4-character password
```


## shadowing

hiding specific subfolders  by mounting another volume on top of them

for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mnt` as the webroot, but another volume is mounted at `/web/certs` -- so visitors can only see the contents of `/mnt` and `/mnt/web` (at URLs `/` and `/web`), but not `/mnt/web/certs` because URL `/web/certs` is mapped to `/var/empty`

the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead

> ℹ️ this also works for single files, because files can also be volumes


## dotfiles

unix-style hidden files/folders  by starting the name with a dot

anyone can access these if they know the name, but they normally don't appear in directory listings

a client can request to see dotfiles in directory listings if global option `-ed` is specified, or the volume has volflag `dots`, or the user has permission `.`

dotfiles do not appear in search results unless one of the above is true, **and** the global option / volflag `dotsrch` is set

> even if user has permission to see dotfiles, they are default-hidden unless `--see-dots` is set, and/or user has enabled the `dotfiles` option in the settings tab

config file example, where the same permission to see dotfiles is given in two different ways just for reference:

```yaml
[/foo]
  /srv/foo
  accs:
    r.: ed   # user "ed" has read-access + dot-access in this volume;
             # dotfiles are visible in listings, but not in searches
  flags:
    dotsrch  # dotfiles will now appear in search results too
    dots     # another way to let everyone see dotfiles in this vol
```


# the browser

accessing a copyparty server using a web-browser

![copyparty-browser-fs8](https://user-images.githubusercontent.com/241032/192042695-522b3ec7-6845-494a-abdb-d1c0d0e23801.png)


## tabs

the main tabs in the ui
* `[🔎]` [search](#searching) by size, date, path/name, mp3-tags ...
* `[🧯]` [unpost](#unpost): undo/delete accidental uploads
* `[🚀]` and `[🎈]` are the [uploaders](#uploading)
* `[📂]` mkdir: create directories
* `[📝]` new-file: create a new textfile
* `[📟]` send-msg: either to server-log or into textfiles if `--urlform save`
* `[🎺]` audio-player config options
* `[⚙️]` general client config options


## hotkeys

the browser has the following hotkeys  (always qwerty)
* `?` show hotkeys help
* `B` toggle breadcrumbs / [navpane](#navpane)
* `I/K` prev/next folder
* `M` parent folder (or unexpand current)
* `V` toggle folders / textfiles in the navpane
* `G` toggle list / [grid view](#thumbnails) -- same as `田` bottom-right
* `T` toggle thumbnails / icons
* `ESC` close various things
* `ctrl-K` delete selected files/folders
* `ctrl-X` cut selected files/folders
* `ctrl-C` copy selected files/folders to clipboard
* `ctrl-V` paste (move/copy)
* `Y` download selected files
* `F2` [rename](#batch-rename) selected file/folder
* when a file/folder is selected (in not-grid-view):
  * `Up/Down` move cursor
  * shift+`Up/Down` select and move cursor
  * ctrl+`Up/Down` move cursor and scroll viewport
  * `Space` toggle file selection
  * `Ctrl-A` toggle select all
* when a textfile is open:
  * `I/K` prev/next textfile
  * `S` toggle selection of open file
  * `M` close textfile
* when playing audio:
  * `J/L` prev/next song
  * `U/O` skip 10sec back/forward
  * `0..9` jump to 0%..90%
  * `P` play/pause (also starts playing the folder)
  * `Y` download file
* when viewing images / playing videos:
  * `J/L, Left/Right` prev/next file
  * `Home/End` first/last file
  * `F` toggle fullscreen
  * `S` toggle selection
  * `R` rotate clockwise (shift=ccw)
  * `Y` download file
  * `Esc` close viewer
  * videos:
    * `U/O` skip 10sec back/forward
    * `0..9` jump to 0%..90%
    * `P/K/Space` play/pause
    * `M` mute
    * `C` continue playing next video
    * `V` loop entire file
    * `[` loop range (start)
    * `]` loop range (end)
* when the navpane is open:
  * `A/D` adjust tree width
* in the [grid view](#thumbnails):
  * `S` toggle multiselect
  * shift+`A/D` zoom
* in the markdown editor:
  * `^s` save
  * `^h` header
  * `^k` autoformat table
  * `^u` jump to next unicode character
  * `^e` toggle editor / preview
  * `^up, ^down` jump paragraphs


## navpane

switching between breadcrumbs or navpane

click the `🌲` or pressing the `B` hotkey to toggle between breadcrumbs path (default), or a navpane (tree-browser sidebar thing)

* `[+]` and `[-]` (or hotkeys `A`/`D`) adjust the size
* `[🎯]` jumps to the currently open folder
* `[📃]` toggles between showing folders and textfiles
* `[📌]` shows the name of all parent folders in a docked panel
* `[a]` toggles automatic widening as you go deeper
* `[↵]` toggles wordwrap
* `[👀]` show full name on hover (if wordwrap is off)


## thumbnails

press `g` or `田` to toggle grid-view instead of the file listing  and `t` toggles icons / thumbnails
* can be made default globally with `--grid` or per-volume with volflag `grid`
* enable by adding `?imgs` to a link, or disable with `?imgs=0`

![copyparty-thumbs-fs8](https://user-images.githubusercontent.com/241032/129636211-abd20fa2-a953-4366-9423-1c88ebb96ba9.png)

it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video files, so you may want to `--no-thumb` or maybe just `--no-vthumb` depending on how dangerous your users are
* pyvips is 3x faster than Pillow, Pillow is 3x faster than FFmpeg
* disable thumbnails for specific volumes with volflag `dthumb` for all, or `dvthumb` / `dathumb` / `dithumb` for video/audio/images only
* for installing FFmpeg on windows, see [optional dependencies](#optional-dependencies)

audio files are converted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)

images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
* the order is significant, so if both `cover.png` and `folder.jpg` exist in a folder, it will pick the first matching `--th-covers` entry (`folder.jpg`)
* and, if you enable [file indexing](#file-indexing), it will also try those names as dotfiles (`.folder.jpg` and so), and then fallback on the first picture in the folder (if it has any pictures at all)

enabling `multiselect` lets you click files to select them, and then shift-click another file for range-select
* `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
  * the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`

to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` and `.elf` files respectively, do this: `--ext-th=exe=/icons/exe.png --ext-th=elf=/icons/elf.gif`
* optionally as separate volflags for each mapping; see config file example below
* the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
  * be careful with svg; chrome will crash if you have too many unique svg files showing on the same page (the limit is 250 or so) -- showing the same handful of svg files thousands of times is ok however

note:
* heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) but this is not possible with the docker-images due to [legal reasons](docs/bad-codecs.md)

config file example:

```yaml
[global]
  no-thumb   # disable ALL thumbnails and audio transcoding
  no-vthumb  # only disable video thumbnails

[/music]
  /mnt/nas/music
  accs:
    r: *     # everyone can read
  flags:
    dthumb   # disable ALL thumbnails and audio transcoding
    dvthumb  # only disable video thumbnails
    ext-th:  exe=/ico/exe.png  # /ico/exe.png is the thumbnail of *.exe
    ext-th:  elf=/ico/elf.gif  # ...and /ico/elf.gif is used for *.elf
    th-covers:  folder.png,folder.jpg,cover.png,cover.jpg  # the default
```


## zip downloads

download folders (or file selections) as `zip` or `tar` files

select which type of archive you want in the `[⚙️] config` tab:

| name | url-suffix | description |
|--|--|--|
| `tar` | `?tar` | plain gnutar, works great with `curl \| tar -xv` |
| `pax` | `?tar=pax` | pax-format tar, futureproof, not as fast |
| `tgz` | `?tar=gz` | gzip compressed gnu-tar (slow), for `curl \| tar -xvz` |
| `txz` | `?tar=xz` | gnu-tar with xz / lzma compression (v.slow) |
| `zip` | `?zip` | works everywhere, glitchy filenames on win7 and older |
| `zip_dos` | `?zip=dos` | traditional cp437 (no unicode) to fix glitchy filenames |
| `zip_crc` | `?zip=crc` | cp437 with crc32 computed early for truly ancient software |

* gzip default level is `3` (0=fast, 9=best), change with `?tar=gz:9`
* xz default level is `1` (0=fast, 9=best), change with `?tar=xz:9`
* bz2 default level is `2` (1=fast, 9=best), change with `?tar=bz2:9`
* hidden files ([dotfiles](#dotfiles)) are excluded unless account is allowed to list them
  * `up2k.db` and `dir.txt` is always excluded
* bsdtar supports streaming unzipping: `curl foo?zip | bsdtar -xv`
  * good, because copyparty's zip is faster than tar on small files
    * but `?tar` is better for large files, especially if the total exceeds 4 GiB
* `zip_crc` will take longer to download since the server has to read each file twice
  * this is only to support MS-DOS PKZIP v2.04g (october 1993) and older
    * how are you accessing copyparty actually

you can also zip a selection of files or folders by clicking them in the browser, that brings up a selection editor and zip button in the bottom right

![copyparty-zipsel-fs8](https://user-images.githubusercontent.com/241032/129635374-e5136e01-470a-49b1-a762-848e8a4c9cdc.png)

cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
* super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
* and url-param `&nodot` skips dotfiles/dotfolders; they are included by default if your account has permission to see them
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images (`&p` for audio waveforms)
  * can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`


## uploading

drag files/folders into the web-browser to upload

dragdrop is the recommended way, but you may also:

* select some files (not folders) in your file explorer and press CTRL-V inside the browser window
* use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
* upload using [curl, sharex, ishare, ...](#client-examples)

when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
* `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
* `[🚀] up2k`, the good / fancy one

NB: you can undo/delete your own uploads with `[🧯]` [unpost](#unpost) (and this is also where you abort unfinished uploads, but you have to refresh the page first)

up2k has several advantages:
* you can drop folders into the browser (files are added recursively)
* files are processed in chunks, and each chunk is checksummed
  * uploads autoresume if they are interrupted by network issues
  * uploads resume if you reboot your browser or pc, just upload the same files again
  * server detects any corruption; the client reuploads affected chunks
  * the client doesn't upload anything that already exists on the server
  * no filesize limit, even when a proxy limits the request size (for example Cloudflare)
* much higher speeds than ftp/scp/tarpipe on some internet connections (mainly american ones) thanks to parallel connections
* the last-modified timestamp of the file is preserved

> it is perfectly safe to restart / upgrade copyparty while someone is uploading to it!  
> all known up2k clients will resume just fine 💪

see [up2k](./docs/devnotes.md#up2k) for details on how it works, or watch a [demo video](https://a.ocv.me/pub/demo/pics-vids/#gf-0f6f5c0d)

![copyparty-upload-fs8](https://user-images.githubusercontent.com/241032/129635371-48fc54ca-fa91-48e3-9b1d-ba413e4b68cb.png)

**protip:** you can avoid scaring away users with [contrib/plugins/minimal-up2k.js](contrib/plugins/minimal-up2k.js) which makes it look [much simpler](https://user-images.githubusercontent.com/241032/118311195-dd6ca380-b4ef-11eb-86f3-75a3ff2e1332.png)

**protip:** if you enable `favicon` in the `[⚙️] settings` tab (by typing something into the textbox), the icon in the browser tab will indicate upload progress -- also, the `[🔔]` and/or `[🔊]` switches enable visible and/or audible notifications on upload completion

the up2k UI is the epitome of polished intuitive experiences:
* "parallel uploads" specifies how many chunks to upload at the same time
* `[🏃]` analysis of other files should continue while one is uploading
* `[🥔]` shows a simpler UI for faster uploads from slow devices
* `[🛡️]` decides when to overwrite existing files on the server
  * `🛡️` = never (generate a new filename instead)
  * `🕒` = overwrite if the server-file is older
  * `♻️` = always overwrite if the files are different
* `[🎲]` generate random filenames during upload
* `[🔎]` switch between upload and [file-search](#file-search) mode
  * ignore `[🔎]` if you add files by dragging them into the browser

and then there's the tabs below it,
* `[ok]` is the files which completed successfully
* `[ng]` is the ones that failed / got rejected (already exists, ...)
* `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order
* `[busy]` files which are currently hashing, pending-upload, or uploading
  * plus up to 3 entries each from `[done]` and `[que]` for context
* `[que]` is all the files that are still queued

note that since up2k has to read each file twice, `[🎈] bup` can *theoretically* be up to 2x faster in some extreme cases (files bigger than your ram, combined with an internet connection faster than the read-speed of your HDD, or if you're uploading from a cuo2duo)

if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button

if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option `--u2sz` (the default is 96 MiB to support Cloudflare)

if you want to replace existing files on the server with new uploads by default, run with `--u2ow 2` (only works if users have the delete-permission, and can still be disabled with `🛡️` in the UI)


### file-search

dropping files into the browser also lets you see if they exist on the server

![copyparty-fsearch-fs8](https://user-images.githubusercontent.com/241032/129635361-c79286f0-b8f1-440e-aaf4-6e929428fac9.png)

when you drag/drop files into the browser, you will see two dropzones: `Upload` and `Search`

> on a phone? toggle the `[🔎]` switch green before tapping the big yellow Search button to select your files

the files will be hashed on the client-side, and each hash is sent to the server, which checks if that file exists somewhere

files go into `[ok]` if they exist (and you get a link to where it is), otherwise they land in `[ng]`
* the main reason filesearch is combined with the uploader is cause the code was too spaghetti to separate it out somewhere else, this is no longer the case but now i've warmed up to the idea too much

if you have a "wark" (file-identifier/checksum) then you can also search for that in the [🔎] tab by putting `w = kFpDiztbZc8Z1Lzi` in the `raw` field


### unpost

undo/delete accidental uploads  using the `[🧯]` tab in the UI

![copyparty-unpost-fs8](https://user-images.githubusercontent.com/241032/129635368-3afa6634-c20f-418c-90dc-ec411f3b3897.png)

you can unpost even if you don't have regular move/delete access, however only for files uploaded within the past `--unpost` seconds (default 12 hours) and the server must be running with `-e2d`

config file example:

```yaml
[global]
  e2d            # enable up2k database (remember uploads)
  unpost: 43200  # 12 hours (default)
```


### self-destruct

uploads can be given a lifetime,  after which they expire / self-destruct

the feature must be enabled per-volume with the `lifetime` [upload rule](#upload-rules) which sets the upper limit for how long a file gets to stay on the server

clients can specify a shorter expiration time using the [up2k ui](#uploading) -- the relevant options become visible upon navigating into a folder with `lifetimes` enabled -- or by using the `life` [upload modifier](./docs/devnotes.md#write)

specifying a custom expiration time client-side will affect the timespan in which unposts are permitted, so keep an eye on the estimates in the up2k ui


### race the beam

download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm))  -- it's almost like peer-to-peer

requires the file to be uploaded using up2k (which is the default drag-and-drop uploader), alternatively the command-line program


### incoming files

the control-panel shows the ETA for all incoming files  , but only for files being uploaded into volumes where you have read-access

![copyparty-cpanel-upload-eta-or8](https://github.com/user-attachments/assets/fd275ffa-698c-4fca-a307-4d2181269a6a)


## file manager

cut/paste, rename, and delete files/folders (if you have permission)

file selection: click somewhere on the line (not the link itself), then:
* `space` to toggle
* `up/down` to move
* `shift-up/down` to move-and-select
* `ctrl-shift-up/down` to also scroll
* shift-click another line for range-select

* cut: select some files and `ctrl-x`
* copy: select some files and `ctrl-c`
* paste: `ctrl-v` in another folder
* rename: `F2`

you can copy/move files across browser tabs (cut/copy in one tab, paste in another)


## shares

share a file or folder by creating a temporary link

when enabled in the server settings (`--shr`), click the bottom-right `share` button to share the folder you're currently in, or alternatively:
* select a folder first to share that folder instead
* select one or more files to share only those files

this feature was made with [identity providers](#identity-providers) in mind -- configure your reverseproxy to skip the IdP's access-control for a given URL prefix and use that to safely share specific files/folders sans the usual auth checks

when creating a share, the creator can choose any of the following options:

* password-protection
* expire after a certain time; `0` or blank means infinite
* allow visitors to upload (if the user who creates the share has write-access)

semi-intentional limitations:

* cleanup of expired shares only works when global option `e2d` is set, and/or at least one volume on the server has volflag `e2d`
* only folders from the same volume are shared; if you are sharing a folder which contains other volumes, then the contents of those volumes will not be available
* if you change [password hashing](#password-hashing) settings after creating a password-protected share, then that share will stop working
* related to [IdP volumes being forgotten on shutdown](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#idp-volumes-are-forgotten-on-shutdown), any shares pointing into a user's IdP volume will be unavailable until that user makes their first request after a restart
* no option to "delete after first access" because tricky
  * when linking something to discord (for example) it'll get accessed by their scraper and that would count as a hit
  * browsers wouldn't be able to resume a broken download unless the requester's IP gets allowlisted for X minutes (ref. tricky)

specify `--shr /foobar` to enable this feature; a toplevel virtual folder named `foobar` is then created, and that's where all the shares will be served from

* you can name it whatever, `foobar` is just an example
* if you're using config files, put `shr: /foobar` inside the `[global]` section instead

users can delete their own shares in the controlpanel, and a list of privileged users (`--shr-adm`) are allowed to see and/or delet any share on the server

the volflag `--shr-who` lets you control who can create a share from that volume, either `no` (nobody), `a` (people with admin permission), or `auth` (people who are logged in) 

after a share has expired, it remains visible in the controlpanel for `--shr-rt` minutes (default is 1 day), and the owner can revive it by extending the expiration time there

**security note:** using this feature does not mean that you can skip the [accounts and volumes](#accounts-and-volumes) section -- you still need to restrict access to volumes that you do not intend to share with unauthenticated users! it is not sufficient to use rules in the reverseproxy to restrict access to just the `/share` folder.


## batch rename

select some files and press `F2` to bring up the rename UI

![batch-rename-fs8](https://user-images.githubusercontent.com/241032/128434204-eb136680-3c07-4ec7-92e0-ae86af20c241.png)

quick explanation of the buttons,  
* `[✅ apply rename]` confirms and begins renaming
* `[❌ cancel]` aborts and closes the rename window
* `[↺ reset]` reverts any filename changes back to the original name
* `[decode]` does a URL-decode on the filename, fixing stuff like `&amp;` and `%20`
* `[advanced]` toggles advanced mode

advanced mode: rename files based on rules to decide the new names, based on the original name (regex), or based on the tags collected from the file (artist/title/...), or a mix of both

in advanced mode,  
* `[case]` toggles case-sensitive regex
* `regex` is the regex pattern to apply to the original filename; any files which don't match will be skipped
* `format` is the new filename, taking values from regex capturing groups and/or from file tags
  * very loosely based on foobar2000 syntax
* `presets` lets you save rename rules for later

available functions:
* `$lpad(text, length, pad_char)`
* `$rpad(text, length, pad_char)`

two counters are available; `.n.s` is the nth file in the selection, and `.n.d` the nth file in the folder, for example rename-output `file(.n.d).(ext)` gives `file5.bin`, and `beach-$lpad((.n.s),3,0).(ext)` is `beach-017.jpg` and the initial value of each counter can be set in the textboxes underneath the preset dropdown

so,

say you have a file named [`meganeko - Eclipse - 07 Sirius A.mp3`](https://www.youtube.com/watch?v=-dtb0vDPruI) (absolutely fantastic album btw) and the tags are: `Album:Eclipse`, `Artist:meganeko`, `Title:Sirius A`, `tn:7`

you could use just regex to rename it:
* `regex` = `(.*) - (.*) - ([0-9]{2}) (.*)`
* `format` = `(3). (1) - (4)`
* `output` = `07. meganeko - Sirius A.mp3`

or you could use just tags:
* `format` = `$lpad((tn),2,0). (artist) - (title).(ext)`
* `output` = `7. meganeko - Sirius A.mp3`

or a mix of both:
* `regex` = ` - ([0-9]{2}) `
* `format` = `(1). (artist) - (title).(ext)`
* `output` = `07. meganeko - Sirius A.mp3`

the metadata keys you can use in the format field are the ones in the file-browser table header (whatever is collected with `-mte` and `-mtp`)


## rss feeds

monitor a folder with your RSS reader  , optionally recursive

must be enabled per-volume with volflag `rss` or globally with `--rss`

the feed includes itunes metadata for use with podcast readers such as [AntennaPod](https://antennapod.org/)

a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3

url parameters:

* `pw=hunter2` for password auth
  * if you enabled `--usernames` then do `pw=username:password` instead
* `nopw` disables embedding the password (if provided) into item-URLs in the feed
* `nopw=a` disables mentioning the password anywhere at all in the feed; may break some readers
* `recursive` to also include subfolders
* `title=foo` changes the feed title (default: folder name)
* `fext=mp3,opus` only include mp3 and opus files (default: all)
* `nf=30` only show the first 30 results (default: 250)
* `sort=m` sort by mtime (file last-modified), newest first (default)
  * `u` = upload-time; NOTE: non-uploaded files have upload-time `0`
  * `n` = filename
  * `a` = filesize
  * uppercase = reverse-sort; `M` = oldest file first


## opds feeds

browse and download files from your e-book reader

enabled with the `opds` volflag or `--opds` global option

add `?opds` to the end of the url you would like to browse, then input that in your opds client.
for example: `https://copyparty.example/books/?opds`.

to log in with a password, enter it into either of the username or password fields in your client.

- if you've enabled `--usernames`, then you need to enter both username and password .

note: some clients (e.g. Moon+ Reader) will not send the password when downloading cover images, which will
cause your ip to be banned by copyparty. to work around this, you can grant the [`g` permission](#accounts-and-volumes)
to unauthenticated requests and enable [filekeys](#filekeys) to prevent guessing filenames. for example:
`-vbooks:books:r,ed:g:c,fk,opds`

by default, not all file types will be listed in opds feeds. to change this, add the extension to 
`--opds-exts` (volflag: `opds_exts`), or empty the list to list everything


## recent uploads

list all recent uploads  by clicking "show recent uploads" in the controlpanel

will show uploader IP and upload-time if the visitor has the admin permission

* global-option `--ups-when` makes upload-time visible to all users, and not just admins

* global-option `--ups-who` (volflag `ups_who`) specifies who gets access (0=nobody, 1=admins, 2=everyone), default=2

note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them

config file example:

```yaml
[global]
  ups-when    # everyone can see upload times
  ups-who: 1  # but only admins can see the list,
              # so ups-when doesn't take effect
```


## media player

plays almost every audio format there is  (if the server has FFmpeg installed for on-demand transcoding)

the following audio formats are usually always playable, even without FFmpeg: `aac|flac|m4a|mp3|ogg|opus|wav`

some highlights:
* OS integration; control playback from your phone's lockscreen ([windows](https://user-images.githubusercontent.com/241032/233213022-298a98ba-721a-4cf1-a3d4-f62634bc53d5.png) // [iOS](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) // [android](https://user-images.githubusercontent.com/241032/233212311-a7368590-08c7-4f9f-a1af-48ccf3f36fad.png))
* shows the audio waveform in the seekbar
* not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
* videos can be played as audio, without wasting bandwidth on the video
* adding `?v` to the end of an audio/video/image link will make it open in the mediaplayer

click the `play` link next to an audio file, or copy the link target to [share it](https://a.ocv.me/pub/demo/music/Ubiktune%20-%20SOUNDSHOCK%202%20-%20FM%20FUNK%20TERRROR!!/#af-1fbfba61&t=18) (optionally with a timestamp to start playing from, like that example does)

open the `[🎺]` media-player-settings tab to configure it,
* "switches":
  * `[🔁]` repeats one single song forever
  * `[🔀]` shuffles the files inside each folder
  * `[preload]` starts loading the next track when it's about to end, reduces the silence between songs
  * `[full]` does a full preload by downloading the entire next file; good for unreliable connections, bad for slow connections
  * `[~s]` toggles the seekbar waveform display
  * `[/np]` enables buttons to copy the now-playing info as an irc message
  * `[📻]` enables buttons to create an [m3u playlist](#playlists) with the selected songs
  * `[os-ctl]` makes it possible to control audio playback from the lockscreen of your device (enables [mediasession](https://developer.mozilla.org/en-US/docs/Web/API/MediaSession))
  * `[seek]` allows seeking with lockscreen controls (buggy on some devices)
  * `[art]` shows album art on the lockscreen
  * `[🎯]` keeps the playing song scrolled into view (good when using the player as a taskbar dock)
  * `[⟎]` shrinks the playback controls
* "buttons":
  * `[uncache]` may fix songs that won't play correctly due to bad files in browser cache
* "at end of folder":
  * `[loop]` keeps looping the folder
  * `[next]` plays into the next folder
* "transcode":
  * `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
  * `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
  * `[oth]` converts all other known formats into opus (if supported by browser) or mp3
    * `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|m4b|m4r|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
* "transcode to":
  * `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
  * `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
  * `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the most part
  * `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
  * `[flac]` -- lossless but compressed, for LAN and/or fiber playback on electrostatic headphones
  * `[wav]` -- lossless and uncompressed, for LAN and/or fiber playback on electrostatic headphones connected to very old equipment
    * `flac` and `wav` must be enabled with `--allow-flac` / `--allow-wav` to allow spending the disk space
* "tint" reduces the contrast of the playback bar


### playlists

create and play [m3u8](https://en.wikipedia.org/wiki/M3U) playlists  -- see example [text](https://a.ocv.me/pub/demo/music/?doc=example-playlist.m3u) and [player](https://a.ocv.me/pub/demo/music/#m3u=example-playlist.m3u)

click a file with the extension `m3u` or `m3u8` (for example `mixtape.m3u` or `touhou.m3u8` ) and you get two choices: Play / Edit

playlists can include songs across folders anywhere on the server, but filekeys/dirkeys are NOT supported, so the listener must have read-access or get-access to the files


### creating a playlist

with a standalone mediaplayer or copyparty

you can use foobar2000, deadbeef, just about any standalone player should work -- but you might need to edit the filepaths in the playlist so they fit with the server-URLs

alternatively, you can create the playlist using copyparty itself:

* open the `[🎺]` media-player-settings tab and enable the `[📻]` create-playlist feature -- this adds two new buttons in the bottom-right tray, `[📻add]` and `[📻copy]` which appear when you listen to music, or when you select a few audiofiles

* click the `📻add` button while a song is playing (or when you've selected some songs) and they'll be added to "the list" (you can't see it yet)

* at any time, click `📻copy` to send the playlist to your clipboard
  * you can then continue adding more songs if you'd like
  * if you want to wipe the playlist and start from scratch, just refresh the page

* create a new textfile, name it `something.m3u` and paste the playlist there


### audio equalizer

and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)

can also boost the volume in general, or increase/decrease stereo width (like [crossfeed](https://www.foobar2000.org/components/view/foo_dsp_meiercf) just worse)

has the convenient side-effect of reducing the pause between songs, so gapless albums play better with the eq enabled (just make it flat)

not available on iPhones / iPads because AudioContext currently breaks background audio playback on iOS (15.7.8)


### fix unreliable playback on android

due to phone / app settings,  android phones may randomly stop playing music when the power saver kicks in, especially at the end of an album -- you can fix it by [disabling power saving](https://user-images.githubusercontent.com/241032/235262123-c328cca9-3930-4948-bd18-3949b9fd3fcf.png) in the [app settings](https://user-images.githubusercontent.com/241032/235262121-2ffc51ae-7821-4310-a322-c3b7a507890c.png) of the browser you use for music streaming (preferably a dedicated one)


## textfile viewer

with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/))  , and terminal colors work too

click `-txt-` next to a textfile to open the viewer, which has the following toolbar buttons:

* `✏️ edit` opens the textfile editor
* `📡 follow` starts monitoring the file for changes, streaming new lines in realtime
  * similar to `tail -f`
  * [link directly](https://a.ocv.me/pub/demo/logtail/?doc=lipsum.txt&tail) to a file with tailing enabled by adding `&tail` to the textviewer URL


## markdown viewer

and there are *two* editors

![copyparty-md-read-fs8](https://user-images.githubusercontent.com/241032/115978057-66419080-a57d-11eb-8539-d2be843991aa.png)

there is a built-in extension for inline clickable thumbnails;
* enable it by adding `<!-- th -->` somewhere in the doc
* add thumbnails with `!th[l](your.jpg)` where `l` means left-align (`r` = right-align)
* a single line with `---` clears the float / inlining
* in the case of README.md being displayed below a file listing, thumbnails will open in the gallery viewer

other notes,
* the document preview has a max-width which is the same as an A4 paper when printed


### markdown vars

dynamic docs with serverside variable expansion  to replace stuff like `{{self.ip}}` with the client's IP, or `{{srv.htime}}` with the current time on the server

see [./srv/expand/](./srv/expand/) for usage and examples


## other tricks

* you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`

* enabling the audio equalizer can help make gapless albums fully gapless in some browsers (chrome), so consider leaving it on with all the values at zero

* get a plaintext file listing by adding `?ls=t` to a URL, or a compact colored one with `?ls=v` (for unix terminals)

* if you are using media hotkeys to switch songs and are getting tired of seeing the OSD popup which Windows doesn't let you disable, consider [./contrib/media-osd-bgone.ps1](contrib/#media-osd-bgoneps1)

* click the bottom-left `π` to open a javascript prompt for debugging

* files named `.prologue.html` / `.epilogue.html` will be rendered before/after directory listings unless `--no-logues`

* files named `descript.ion` / `DESCRIPT.ION` are parsed and displayed in the file listing, or as the epilogue if nonstandard

* files named `README.md` / `readme.md` will be rendered after directory listings unless `--no-readme` (but `.epilogue.html` takes precedence)

  * and `PREADME.md` / `preadme.md` is shown above directory listings unless `--no-readme` or `.prologue.html`

* `README.md` and `*logue.html` can contain placeholder values which are replaced server-side before embedding into directory listings; see [`--help-exp`](https://copyparty.eu/cli/#exp-help-page)


## searching

search by size, date, path/name, mp3-tags, ...

![copyparty-search-fs8](https://user-images.githubusercontent.com/241032/129635365-c0ff2a9f-0ee5-4fc3-8bb6-006033cf67b8.png)

when started with `-e2dsa` copyparty will scan/index all your files. This avoids duplicates on upload, and also makes the volumes searchable through the web-ui:
* make search queries by `size`/`date`/`directory-path`/`filename`, or...
* drag/drop a local file to see if the same contents exist somewhere on the server, see [file-search](#file-search)

path/name queries are space-separated, AND'ed together, and words are negated with a `-` prefix, so for example:
* path: `shibayan -bossa` finds all files where one of the folders contain `shibayan` but filters out any results where `bossa` exists somewhere in the path
* name: `demetori styx` gives you [good stuff](https://www.youtube.com/watch?v=zGh0g14ZJ8I&list=PL3A147BD151EE5218&index=9)

the `raw` field allows for more complex stuff such as `( tags like *nhato* or tags like *taishi* ) and ( not tags like *nhato* or not tags like *taishi* )` which finds all songs by either nhato or taishi, excluding collabs (terrible example, why would you do that)

for the above example to work, add the commandline argument `-e2ts` to also scan/index tags from music files, which brings us over to:


# server config

using arguments or config files, or a mix of both:
* config files (`-c some.conf`) can set additional commandline arguments; see [./docs/example.conf](docs/example.conf) and [./docs/example2.conf](docs/example2.conf)
* `kill -s USR1` (same as `systemctl reload copyparty`) to reload accounts and volumes from config files without restarting
  * or click the `[reload cfg]` button in the control-panel if the user has `a`/admin in any volume
  * changes to the `[global]` config section requires a restart to take effect

**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with [`--help`](https://copyparty.eu/cli/) (or click that link) to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in [`--help-flags`](https://copyparty.eu/cli/#flags-help-page) can be used in volumes as volflags (per-volume configuration).
* if running in docker/podman, try this: `docker run --rm -it copyparty/ac --help`
* or if you prefer plaintext, https://copyparty.eu/helptext.txt


## version-checker

sleep better at night  by telling copyparty to periodically check whether your version has a [known vulnerability](https://github.com/9001/copyparty/security/advisories)

this feature can be enabled by setting the global-option `--vc-url` to one of the following URLs; all of them provide the same information, so which one you choose is whatever
* `https://api.copyparty.eu/advisories`
* `https://api.github.com/repos/9001/copyparty/security-advisories?per_page=9`

> to see what happens when a bad version is detected, try `--vc-url https://api.copyparty.eu/advisories-test`

also consider the following options:
* global-option `--vc-age` is how often (in hours) to check that URL; default is 3
* global-option `--vc-exit` can be enabled to panic and immediately exit if a vulnerability is indicated
  * if `--vc-exit` is not enabled, it just shows a warning on the controlpanel for all users with permission `a` or `A`

config file example:

```yaml
[global]
  vc-url: https://api.copyparty.eu/advisories
  vc-age: 3  # how many hours to wait between each check
  vc-exit    # emergency-exit if current version is vulnerable
```


## logging

serverlog is sent to stdout by default  (but logging to a file is also possible)

"stdout" usually means either the terminal, or journalctl, or whatever is collecting logs from your docker containers, so that depends on your setup

* [-q](https://copyparty.eu/cli/#g-q) disables logging to stdout, and may improve performance a little bit
  * combine it with `-lo logfolder/cpp-%Y-%m-%d.txt` to log to a file instead
  * the `%Y-%m-%d` makes it create a new logfile every day, with the date as filename
* `-lo whatever.txt` can be used without `-q` to log to both at the same time
  * by default, the logfile will have colors if the terminal does (usually the case)
  * use the [textfile-viewer](https://github.com/user-attachments/assets/8a828947-2fae-4df9-bd2a-3de46f42d478) or `less -R` in a terminal to see colors correctly
* if you want [no colors](https://youtu.be/biW5UVGkPMA?t=148):
  * `--flo 2` disables colors for just the logfile
  * `--no-ansi` disables colors for both the terminal and logfile

config file example:

```yaml
[global]
  log-date: %Y-%m-%d  # show dates on stdout too
  lo: /var/log/cpp/%Y-%m-%d.txt  # logfile path
  flo: 2  # just text (no colors) in logfile
  q       # disable stdout; use logfile only
```


## zeroconf

announce enabled services on the LAN ([pic](https://user-images.githubusercontent.com/241032/215344737-0eae8d98-9496-4256-9aa8-cd2f6971810d.png))  -- `-z` enables both [mdns](#mdns) and [ssdp](#ssdp)

* `--z-on` / `--z-off` limits the feature to certain networks

config file example:

```yaml
[global]
  z      # enable all zeroconf features (mdns, ssdp)
  zm     # only enables mdns (does nothing since we already have z)
  z-on: 192.168.0.0/16, 10.1.2.0/24  # restrict to certain subnets
```


### mdns

LAN domain-name and feature announcer

uses [multicast dns](https://en.wikipedia.org/wiki/Multicast_DNS) to give copyparty a domain which any machine on the LAN can use to access it

all enabled services ([webdav](#webdav-server), [ftp](#ftp-server), [smb](#smb-server)) will appear in mDNS-aware file managers (KDE, gnome, macOS, ...)

the domain will be `partybox.local` if the machine's hostname is `partybox` unless `--name` specifies something else

and the web-UI will be available at http://partybox.local:3923/

* if you want to get rid of the `:3923` so you can use http://partybox.local/ instead then see [listen on port 80 and 443](#listen-on-port-80-and-443)


### ssdp

windows-explorer announcer

uses [ssdp](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) to make copyparty appear in the windows file explorer on all machines on the LAN

doubleclicking the icon opens the "connect" page which explains how to mount copyparty as a local filesystem

if copyparty does not appear in windows explorer, use `--zsv` to see why:

* maybe the discovery multicast was sent from an IP which does not intersect with the server subnets


## qr-code

print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/194728533-6f00849b-c6ac-43c6-9359-83e454d11e00.png) for quick access,  great between phones on android hotspots which keep changing the subnet

* `--qr` enables it
* `--qrs` does https instead of http
* `--qrl lootbox/?pw=hunter2` appends to the url, linking to the `lootbox` folder with password `hunter2`
* `--qrz 1` forces 1x zoom instead of autoscaling to fit the terminal size
  * 1x may render incorrectly on some terminals/fonts, but 2x should always work
* `--qr-pin 1` makes the qr-code stick to the bottom of the console (never scrolls away)
* `--qr-file qr.txt:1:2` writes a small qr-code to `qr.txt`
* `--qr-file qr.txt:2:2` writes a big qr-code to `qr.txt`
* `--qr-file qr.svg:1:2` writes a vector-graphics qr-code to `qr.svg`
* `--qr-file qr.png:8:4:333333:ffcc55` writes an 8x-magnified yellow-on-gray `qr.png`
* `--qr-file qr.png:8:4::ffffff` writes an 8x-magnified white-on-transparent `qr.png`

it uses the server hostname if [mdns](#mdns) is enabled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain


## ftp server

an FTP server can be started using `--ftp 3921`,  and/or `--ftps` for explicit TLS (ftpes)

* based on [pyftpdlib](https://github.com/giampaolo/pyftpdlib)
* needs a dedicated port (cannot share with the HTTP/HTTPS API)
* uploads are not resumable -- delete and restart if necessary
* runs in active mode by default, you probably want `--ftp-pr 12000-13000`
  * if you enable both `ftp` and `ftps`, the port-range will be divided in half
  * some older software (filezilla on debian-stable) cannot passive-mode with TLS
* login with any username + your password, or put your password in the username field
  * unless you enabled `--usernames`

some recommended FTP / FTPS clients; `wark` = example password:
* https://winscp.net/eng/download.php
* https://filezilla-project.org/ struggles a bit with ftps in active-mode, but is fine otherwise
* https://rclone.org/ does FTPS with `tls=false explicit_tls=true`
* `lftp -u k,wark -p 3921 127.0.0.1 -e ls`
* `lftp -u k,wark -p 3990 127.0.0.1 -e 'set ssl:verify-certificate no; ls'`
* `curl ftp://127.0.0.1:3921/` (plaintext ftp)
* `curl --ssl-reqd ftp://127.0.0.1:3990/` (encrypted ftps)

config file example, which restricts FTP to only use ports 3921 and 12000-12099 so all of those ports must be opened in your firewall:

```yaml
[global]
  ftp: 3921
  ftp-pr: 12000-12099
```


## sftp server

goes roughly 700 MiB/s (slower than webdav and ftp)

> this is **not** [ftps](#ftp-server) (which copyparty also supports); [ftps](#ftp-server) is ftp-tls (think http/https), while **sftp** is ssh-based and (preferably) uses ssh-keys for authentication

the sftp-server requires the optional dependency [paramiko](https://pypi.org/project/paramiko/);
* if you are **not** using docker, then install paramiko somehow
* if you **are** using docker, then use one of the following image variants: `ac` / `im` / `iv` / `dj`

enable sftpd with `--sftp 3922` to listen on port 3922;
* use global-option `sftp-key` to associate an ssh-key with a user;
  * commandline: `--sftp-key 'david ssh-ed25519 AAAAC3NzaC...'`
  * config-file: `sftp-key: david ssh-ed25519 AAAAC3NzaC...`
* `--sftp-pw` enables login with passwords (default is ssh-keys only)
* `--sftp-anon foo` enables login with username `foo` and no password; gives the same access/permissions as the website does when not logged in

see the [sftp section in --help](https://copyparty.eu/cli/#g-sftp) for the other options


## webdav server

with read-write support,  supports winXP and later, macos, nautilus/gvfs  ... a great way to [access copyparty straight from the file explorer in your OS](#mount-as-drive)

click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to see connection instructions for windows, linux, macos

general usage:
* login with any username + your password, or put your password in the username field (password field can be empty/whatever)
  * unless you enabled `--usernames`

on macos, connect from finder:
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/

to be able to edit existing files, the client must have the Delete-permission, and some webdav clients will also require the [daw](https://copyparty.eu/cli/#g-daw) volflag or global-option (not necessary if the client sends the `x-oc-mtime` header). Without `daw`, those clients will fail to modify existing files and instead create new copies with names like `notes.txt-1771978661.726032-3i9GPghL.txt`. **NOTE:** Enabling `daw` will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution. Another alternative is the [dav-port](https://copyparty.eu/cli/#g-dav-port) option

> note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)


### connecting to webdav from windows

using the GUI  (winXP or later):
* rightclick [my computer] -> [map network drive] -> Folder: `http://192.168.123.1:3923/`
  * on winXP only, click the `Sign up for online storage` hyperlink instead and put the URL there
  * providing your password as the username is recommended; the password field can be anything or empty
    * unless you enabled `--usernames`

the webdav client that's built into windows has the following list of bugs; you can avoid all of these by connecting with rclone instead:
* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password
  * or just type your password into the username field instead to get around it entirely
* connecting to a folder which allows anonymous read will make writing impossible, as windows has decided it doesn't need to login
  * workaround: connect twice; first to a folder which requires auth, then to the folder you actually want, and leave both of those mounted
  * or set the server-option `--dav-auth` to force password-auth for all webdav clients
* win7+ may open a new tcp connection for every file and sometimes forgets to close them, eventually needing a reboot
  * maybe NIC-related (??), happens with win10-ltsc on e1000e but not virtio
* windows cannot access folders which contain filenames with invalid unicode or forbidden characters (`<>:"/\|?*`), or names ending with `.`
* winxp cannot show unicode characters outside of *some range*
  * latin-1 is fine, hiragana is not (not even as shift-jis on japanese xp)


## tftp server

a TFTP server (read/write) can be started using `--tftp 3969`  (you probably want [ftp](#ftp-server) instead unless you are *actually* communicating with hardware from the 90s (in which case we should definitely hang some time))

> that makes this the first RTX DECT Base that has been updated using copyparty 🎉

* based on [partftpy](https://github.com/9001/partftpy)
* no accounts; read from world-readable folders, write to world-writable, overwrite in world-deletable
* needs a dedicated port (cannot share with the HTTP/HTTPS API)
  * run as root (or see below) to use the spec-recommended port `69` (nice)
* can reply from a predefined portrange (good for firewalls)
* only supports the binary/octet/image transfer mode (no netascii)
* [RFC 7440](https://datatracker.ietf.org/doc/html/rfc7440) is **not** supported, so will be extremely slow over WAN
  * assuming default blksize (512), expect 1100 KiB/s over 100BASE-T, 400-500 KiB/s over wifi, 200 on bad wifi

most clients expect to find TFTP on port 69, but on linux and macos you need to be root to listen on that. Alternatively, listen on 3969 and use NAT on the server to forward 69 to that port;
* on linux: `iptables -t nat -A PREROUTING -i eth0 -p udp --dport 69 -j REDIRECT --to-port 3969`

some recommended TFTP clients:
* curl (cross-platform, read/write)
  * get: `curl --tftp-blksize 1428 tftp://127.0.0.1:3969/firmware.bin`
  * put: `curl --tftp-blksize 1428 -T firmware.bin tftp://127.0.0.1:3969/`
* windows: `tftp.exe` (you probably already have it)
  * `tftp -i 127.0.0.1 put firmware.bin`
* linux: `tftp-hpa`, `atftp`
  * `atftp --option "blksize 1428" 127.0.0.1 3969 -p -l firmware.bin -r firmware.bin`
  * `tftp -v -m binary 127.0.0.1 3969 -c put firmware.bin`


## smb server

unsafe, slow, not recommended for wan,  enable with `--smb` for read-only or `--smbw` for read-write

click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to see connection instructions for windows, linux, macos

dependencies: `python3 -m pip install --user -U impacket==0.13.0`
* newer versions of impacket will hopefully work just fine but there is monkeypatching so maybe not

some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
* not entirely confident that read-only is read-only
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
  * account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
  * [shadowing](#shadowing) probably works as expected but no guarantees
* not compatible with pw-hashing or `--usernames`

and some minor issues,
* clients only see the first ~400 files in big folders;
  * this was originally due to [impacket#1433](https://github.com/SecureAuthCorp/impacket/issues/1433) which was fixed in impacket-0.12, so you can disable the workaround with `--smb-nwa-1` but then you get unacceptably poor performance instead
* hot-reload of server config (`/?reload=cfg`) does not include the `[global]` section (commandline args)
* listens on the first IPv4 `-i` interface only (default = :: = 0.0.0.0 = all)
* login doesn't work on winxp, but anonymous access is ok -- remove all accounts from copyparty config for that to work
  * win10 onwards does not allow connecting anonymously / without accounts
* python3 only
* slow (the builtin webdav support in windows is 5x faster, and rclone-webdav is 30x faster)
  * those numbers are specifically for copyparty's smb-server (because it sucks); other smb-servers should be similar to webdav

known client bugs:
* on win7 only, `--smb1` is much faster than smb2 (default) because it keeps rescanning folders on smb2
  * however smb1 is buggy and is not enabled by default on win10 onwards
* windows cannot access folders which contain filenames with invalid unicode or forbidden characters (`<>:"/\|?*`), or names ending with `.`

the smb protocol listens on TCP port 445, which is a privileged port on linux and macos, which would require running copyparty as root. However, this can be avoided by listening on another port using `--smb-port 3945` and then using NAT on the server to forward the traffic from 445 to there;
* on linux: `iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 445 -j REDIRECT --to-port 3945`

authenticate with one of the following:
* username `$username`, password `$password`
* username `$password`, password `k`


## browser ux

tweaking the ui

* set default sort order globally with `--sort` or per-volume with the `sort` volflag; specify one or more comma-separated columns to sort by, and prefix the column name with `-` for reverse sort
  * the column names you can use are visible as tooltips when hovering over the column headers in the directory listing, for example `href ext sz ts tags/.up_at tags/Circle tags/.tn tags/Artist tags/Title`
  * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Circle,tags/.tn,tags/Artist,tags/Title,href`
  * to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`

see [./docs/rice](./docs/rice) for more, including:
* how to [hide ui-elements](./docs/rice/README.md#hide-ui-elements)
* [custom fonts](./docs/rice/README.md#custom-fonts)
* [custom loading-spinner](./docs/rice/README.md#boring-loader-spinner)
* adding stuff (css/`<meta>`/...) [to the html `<head>` tag](./docs/rice/README.md#head)
* [adding your own translation](./docs/rice/README.md#translations)


## opengraph

discord and social-media embeds

can be enabled globally with `--og` or per-volume with volflag `og`

note that this disables hotlinking because the opengraph spec demands it; to sneak past this intentional limitation, you can enable opengraph selectively by user-agent, for example `--og-ua '(Discord|Twitter|Slack)bot'` (or volflag `og_ua`)

you can also hotlink files regardless by appending `?raw` to the url

> WARNING: if you plan to use WebDAV, then `--og-ua` / `og_ua` must be configured

if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)


## file deduplication

enable symlink-based upload deduplication  globally with `--dedup` or per-volume with volflag `dedup`

by default, when someone tries to upload a file that already exists on the server, the upload will be politely declined, and the server will copy the existing file over to where the upload would have gone

if you enable deduplication with `--dedup` then it'll create a symlink instead of a full copy, thus reducing disk space usage

* on the contrary, if your server is hooked up to s3-glacier or similar storage where reading is expensive, and you cannot use `--safe-dedup=1` because you have other software tampering with your files, so you want to entirely disable detection of duplicate data instead, then you can specify `--no-clone` globally or `noclone` as a volflag

**warning:** when enabling dedup, you should also:
* enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
* ...and/or `--hardlink-only` to use hardlink-based deduplication instead of symlinks; see explanation below
* ...and/or `--reflink` to use CoW/reflink-based dedup (much safer than hardlink, but OS/FS-dependent)

it will not be safe to rename/delete files if you only enable dedup and none of the above; if you enable indexing then it is not *necessary* to also do hardlinks (but you may still want to)

by default, deduplication is done based on symlinks (symbolic links); these are tiny files which are pointers to the nearest full copy of the file

you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`, and you can choose to use reflinks with `--reflink` or volflag `reflink`

advantages of using reflinks (CoW, copy-on-write):
* entirely safe (when your filesystem supports it correctly); either file can be edited or deleted without affecting other copies
* only linux 5.3 or newer, only python 3.14 or newer, only some filesystems (btrfs probably ok, maybe xfs too, but zfs had bugs)

advantages of using hardlinks:
* hardlinks are more compatible with other software; they behave entirely like regular files
* you can safely move and rename files using other file managers
  * symlinks need to be managed by copyparty to ensure the destinations remain correct

advantages of using symlinks (default):
* each symlink can have its own last-modified timestamp, but a single timestamp is shared by all hardlinks
* symlinks make it more obvious to other software that the file is not a regular file, so this can be less dangerous
  * hardlinks look like regular files, so other software may assume they are safe to edit without affecting the other copies

**warning:** if you edit the contents of a deduplicated file, then you will also edit all other copies of that file! This is especially surprising with hardlinks, because they look like regular files, but that same file exists in multiple locations

global-option `--xlink` / volflag `xlink` additionally enables deduplication across volumes, but this is probably buggy and not recommended

config file example:

```yaml
[global]
  e2dsa  # scan and index filesystem on startup
  dedup  # symlink-based deduplication for all volumes

[/media]
  /mnt/nas/media
  flags:
    hardlinkonly  # this vol does hardlinks instead of symlinks
```


## file indexing

enable music search, upload-undo, and better dedup

file indexing relies on two database tables, the up2k filetree (`-e2d`) and the metadata tags (`-e2t`), stored in `.hist/up2k.db`. Configuration can be done through arguments, volflags, or a mix of both.

through arguments:
* `-e2d` enables file indexing on upload
* `-e2ds` also scans writable folders for new files on startup
* `-e2dsa` also scans all mounted volumes (including readonly ones)
* `-e2t` enables metadata indexing on upload
* `-e2ts` also scans for tags in all files that don't have tags yet
* `-e2tsr` also deletes all existing tags, doing a full reindex
* `-e2v` verifies file integrity at startup, comparing hashes from the db
* `-e2vu` patches the database with the new hashes from the filesystem
* `-e2vp` panics and kills copyparty instead

the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`, `d2ts`, `d2v` for disabling:
* `-v ~/music::r:c,e2ds,e2tsr` does a full reindex of everything on startup
* `-v ~/music::r:c,d2d` disables **all** indexing, even if any `-e2*` are on
* `-v ~/music::r:c,d2t` disables all `-e2t*` (tags), does not affect `-e2d*`
* `-v ~/music::r:c,d2ds` disables on-boot scans; only index new uploads
* `-v ~/music::r:c,d2ts` same except only affecting tags

note:
* upload-times can be displayed in the file listing by enabling the `.up_at` metadata key, either globally with `-e2d -mte +.up_at` or per-volume with volflags `e2d,mte=+.up_at` (will have a ~17% performance impact on directory listings)
  * and file checksums can be shown with global-option `-e2d -mte +w` or volflag `e2d,mte=+w` (always active for users with permission `a`)
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise

config file example (these options are recommended btw):

```yaml
[global]
  e2dsa  # scan and index all files in all volumes on startup
  e2ts   # check newly-discovered or uploaded files for media tags
```

### exclude-patterns

to save some time,  you can provide a regex pattern for filepaths to only index by filename/path/size/last-modified (and not the hash of the file contents) by setting `--no-hash '\.iso$'` or the volflag `:c,nohash=\.iso$`, this has the following consequences:
* initial indexing is way faster, especially when the volume is on a network disk
* makes it impossible to [file-search](#file-search)
* if someone uploads the same file contents, the upload will not be detected as a dupe, so it will not get symlinked or rejected

similarly, you can fully ignore files/folders using `--no-idx [...]` and `:c,noidx=\.iso$`

NOTE: `no-idx` and/or `no-hash` prevents deduplication of those files

* when running on macos, all the usual apple metadata files are excluded by default

if you set `--no-hash [...]` globally, you can enable hashing for specific volumes using flag `:c,nohash=`

to exclude certain filepaths from search-results, use `--srch-excl` or volflag `srch_excl` instead of `--no-idx`, for example `--srch-excl 'password|logs/[0-9]'`

config file example:

```yaml
[/games]
  /mnt/nas/games
  flags:
    noidx: \.iso$  # skip indexing iso-files
    srch_excl: password|logs/[0-9]  # filter search results
```

### filesystem guards

avoid traversing into other filesystems  using `--xdev` / volflag `:c,xdev`, skipping any symlinks or bind-mounts to another HDD for example

and/or you can `--xvol` / `:c,xvol` to ignore all symlinks leaving the volume's top directory, but still allow bind-mounts pointing elsewhere

* symlinks are permitted with `xvol` if they point into another volume where the user has the same level of access

these options will reduce performance; unlikely worst-case estimates are 14% reduction for directory listings, 35% for download-as-tar

as of copyparty v1.7.0 these options also prevent file access at runtime -- in previous versions it was just hints for the indexer

### periodic rescan

filesystem monitoring;  if copyparty is not the only software doing stuff on your filesystem, you may want to enable periodic rescans to keep the index up to date

argument `--re-maxage 60` will rescan all volumes every 60 sec, same as volflag `:c,scan=60` to specify it per-volume

uploads are disabled while a rescan is happening, so rescans will be delayed by `--db-act` (default 10 sec) when there is write-activity going on (uploads, renames, ...)

note: folder-thumbnails are selected during filesystem indexing, so periodic rescans can be used to keep them accurate as images are uploaded/deleted (or manually do a rescan with the `reload` button in the controlpanel)

config file example:

```yaml
[global]
  re-maxage: 3600

[/pics]
  /mnt/nas/pics
  flags:
    scan: 900
```


## upload rules

set upload rules using volflags,  some examples:

* `:c,sz=1k-3m` sets allowed filesize between 1 KiB and 3 MiB inclusive (suffixes: `b`, `k`, `m`, `g`)
* `:c,df=4g` block uploads if there would be less than 4 GiB free disk space afterwards
* `:c,vmaxb=1g` block uploads if total volume size would exceed 1 GiB afterwards
* `:c,vmaxn=4k` block uploads if volume would contain more than 4096 files afterwards
* `:c,nosub` disallow uploading into subdirectories; goes well with `rotn` and `rotf`:
* `:c,rotn=1000,2` moves uploads into subfolders, up to 1000 files in each folder before making a new one, two levels deep (must be at least 1)
* `:c,rotf=%Y/%m/%d/%H` enforces files to be uploaded into a structure of subfolders according to that date format
  * `:c,rotf_tz=Europe/Oslo` sets the timezone (default is UTC unless global-option `rotf-tz` is changed)
  * if someone uploads to `/foo/bar` the path would be rewritten to `/foo/bar/2021/08/06/23` for example
  * but the actual value is not verified, just the structure, so the uploader can choose any values which conform to the format string
    * just to avoid additional complexity in up2k which is enough of a mess already
* `:c,lifetime=300` delete uploaded files when they become 5 minutes old

you can also set transaction limits which apply per-IP and per-volume, but these assume `-j 1` (default) otherwise the limits will be messed up, for example `-j 4` would allow anywhere between 1x and 4x the limits you set depending on which processing node the client gets routed to

* `:c,maxn=250,3600` allows 250 files over 1 hour from each IP (tracked per-volume)
* `:c,maxb=1g,300` allows 1 GiB total over 5 minutes from each IP (tracked per-volume)

notes:
* `vmaxb` and `vmaxn` requires either the `e2ds` volflag or `-e2dsa` global-option

config file example:

```yaml
[/inc]
  /mnt/nas/uploads
  accs:
    w: *    # anyone can upload here
    rw: ed  # only user "ed" can read-write
  flags:
    e2ds       # filesystem indexing is required for many of these:
    sz: 1k-3m  # accept upload only if filesize in this range
    df: 4g     # free disk space cannot go lower than this
    vmaxb: 1g  # volume can never exceed 1 GiB
    vmaxn: 4k  # ...or 4000 files, whichever comes first
    nosub      # must upload to toplevel folder
    lifetime: 300   # uploads are deleted after 5min
    maxn: 250,3600  # each IP can upload 250 files in 1 hour
    maxb: 1g,300    # each IP can upload 1 GiB over 5 minutes
```


## compress uploads

files can be autocompressed on upload,  either on user-request (if config allows) or forced by server-config

* volflag `gz` allows gz compression
* volflag `xz` allows lzma compression
* volflag `pk` **forces** compression on all files
* url parameter `pk` requests compression with server-default algorithm
* url parameter `gz` or `xz` requests compression with a specific algorithm
* url parameter `xz` requests xz compression

things to note,
* the `gz` and `xz` arguments take a single optional argument, the compression level (range 0 to 9)
* the `pk` volflag takes the optional argument `ALGORITHM,LEVEL` which will then be forced for all uploads, for example `gz,9` or `xz,0`
* default compression is gzip level 9
* all upload methods except up2k are supported
* the files will be indexed after compression, so dupe-detection and file-search will not work as expected

some examples,
* `-v inc:inc:w:c,pk=xz,0`  
  folder named inc, shared at inc, write-only for everyone, forces xz compression at level 0
* `-v inc:inc:w:c,pk`  
  same write-only inc, but forces gz compression (default) instead of xz
* `-v inc:inc:w:c,gz`  
  allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`


## chmod and chown

per-volume filesystem-permissions and ownership

by default:
* all folders are chmod 755
* files are usually chmod 644 (umask-defined)
* user/group is whatever copyparty is running as

this can be configured per-volume:
* volflag `chmod_f` sets file permissions; default=`644` (usually)
* volflag `chmod_d` sets directory permissions; default=`755`
* volflag `uid` sets the owner user-id
* volflag `gid` sets the owner group-id

notes:
* `gid` can only be set to one of the groups which the copyparty process is a member of
* `uid` can only be set if copyparty is running as root (i appreciate your faith)


## other flags

* `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
  * needs https://pypi.org/project/python-magic/ `python3 -m pip install --user -U python-magic`
  * on windows grab this instead `python3 -m pip install --user -U python-magic-bin`
* `cachectl` changes how webbrowser will cache responses (the `Cache-Control` response-header); default is `no-cache` which will prevent repeated downloading of the same file unless necessary (browser will ask copyparty if the file has changed)
  * adding `?cache` to a link will override this with "fully cache this for 69 seconds"; `?cache=321` is 321 seconds, and `?cache=i` is 7 days


## database location

in-volume (`.hist/up2k.db`, default) or somewhere else

copyparty creates a subfolder named `.hist` inside each volume where it stores the database, thumbnails, and some other stuff

this can instead be kept in a single place using the `--hist` argument, or the `hist=` volflag, or a mix of both:
* `--hist ~/.cache/copyparty -v ~/music::r:c,hist=-` sets `~/.cache/copyparty` as the default place to put volume info, but `~/music` gets the regular `.hist` subfolder (`-` restores default behavior)

by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else

if your storage backend is unreliable (NFS or bad HDDs), you can specify one or more "landmarks" to look for before doing anything database-related. A landmark is a file which is always expected to exist inside the volume. This avoids spurious filesystem rescans in the event of an outage. One line per landmark (see example below)

note:
* putting the hist-folders on an SSD is strongly recommended for performance
* markdown edits are always stored in a local `.hist` subdirectory
* on windows the volflag path is cyglike, so `/c/temp` means `C:\temp` but use regular paths for `--hist`
  * you can use cygpaths for volumes too, `-v C:\Users::r` and `-v /c/users::r` both work

config file example:

```yaml
[global]
  hist: ~/.cache/copyparty  # put db/thumbs/etc. here by default

[/pics]
  /mnt/nas/pics
  flags:
    hist: -  # restore the default (/mnt/nas/pics/.hist/)
    hist: /mnt/nas/cache/pics/  # can be absolute path
    landmark: me.jpg  # /mnt/nas/pics/me.jpg must be readable to enable db
    landmark: info/a.txt^=ok  # and this textfile must start with "ok"
```


## metadata from audio files

set `-e2t` to index tags on upload

`-mte` decides which tags to index and display in the browser (and also the display order), this can be changed per-volume:
* `-v ~/music::r:c,mte=title,artist` indexes and displays *title* followed by *artist*

if you add/remove a tag from `mte` you will need to run with `-e2tsr` once to rebuild the database, otherwise only new files will be affected

but instead of using `-mte`, `-mth` is a better way to hide tags in the browser: these tags will not be displayed by default, but they still get indexed and become searchable, and users can choose to unhide them in the `[⚙️] config` pane

`-mtm` can be used to add or redefine a metadata mapping, say you have media files with `foo` and `bar` tags and you want them to display as `qux` in the browser (preferring `foo` if both are present), then do `-mtm qux=foo,bar` and now you can `-mte artist,title,qux`

tags that start with a `.` such as `.bpm` and `.dur`(ation) indicate numeric value

see the beautiful mess of a dictionary in [mtag.py](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/mtag.py) for the default mappings (should cover mp3,opus,flac,m4a,wav,aif,)

`--no-mutagen` disables Mutagen and uses FFprobe instead, which...
* is about 20x slower than Mutagen
* catches a few tags that Mutagen doesn't
  * melodic key, video resolution, framerate, pixfmt
* avoids pulling any GPL code into copyparty
* more importantly runs FFprobe on incoming files which is bad if your FFmpeg has a cve

`--mtag-to` sets the tag-scan timeout; very high default (60 sec) to cater for zfs and other randomly-freezing filesystems. Lower values like 10 are usually safe, allowing for faster processing of tricky files


### metadata from xattrs

unix extended file attributes  (Linux-only) can be indexed into the db and made searchable;

* `--db-xattr user.foo,user.bar` will index the xattrs `user.foo` and `user.bar`,
* `--db-xattr user.foo=foo,user.bar=bar` will index them with the names `foo` and `bar`,
* `--db-xattr ~~user.foo,user.bar` will index everything *except* `user.foo` and `user.bar`,
* `--db-xattr ~~` will index everything

however note that the tags must also be enabled with `-mte` so here are some complete examples:
* `-e2ts --db-xattr user.foo,user.bar -mte +user.foo,user.bar`
* `-e2ts --db-xattr user.foo=foo,user.bar=bar -mte +foo,bar`

as for actually adding the xattr `user.foo` to a file in the first place,
* `setfattr -n user.foo -v 'utsikt fra fløytoppen' photo.jpg`


## file parser plugins

provide custom parsers to index additional tags,  also see [./bin/mtag/README.md](./bin/mtag/README.md)

copyparty can invoke external programs to collect additional metadata for files using `mtp` (either as argument or volflag), there is a default timeout of 60sec, and only files which contain audio get analyzed by default (see ay/an/ad below)

* `-mtp .bpm=~/bin/audio-bpm.py` will execute `~/bin/audio-bpm.py` with the audio file as argument 1 to provide the `.bpm` tag, if that does not exist in the audio metadata
* `-mtp key=f,t5,~/bin/audio-key.py` uses `~/bin/audio-key.py` to get the `key` tag, replacing any existing metadata tag (`f,`), aborting if it takes longer than 5sec (`t5,`)
* `-v ~/music::r:c,mtp=.bpm=~/bin/audio-bpm.py:c,mtp=key=f,t5,~/bin/audio-key.py` both as a per-volume config wow this is getting ugly

*but wait, there's more!* `-mtp` can be used for non-audio files as well using the `a` flag: `ay` only do audio files (default), `an` only do non-audio files, or `ad` do all files (d as in dontcare)

* "audio file" also means videos btw, as long as there is an audio stream
* `-mtp ext=an,~/bin/file-ext.py` runs `~/bin/file-ext.py` to get the `ext` tag only if file is not audio (`an`)
* `-mtp arch,built,ver,orig=an,eexe,edll,~/bin/exe.py` runs `~/bin/exe.py` to get properties about windows-binaries only if file is not audio (`an`) and file extension is exe or dll
* if you want to daisychain parsers, use the `p` flag to set processing order
  * `-mtp foo=p1,~/a.py` runs before `-mtp foo=p2,~/b.py` and will forward all the tags detected so far as json to the stdin of b.py
* option `c0` disables capturing of stdout/stderr, so copyparty will not receive any tags from the process at all -- instead the invoked program is free to print whatever to the console, just using copyparty as a launcher
  * `c1` captures stdout only, `c2` only stderr, and `c3` (default) captures both
* you can control how the parser is killed if it times out with option `kt` killing the entire process tree (default), `km` just the main process, or `kn` let it continue running until copyparty is terminated

if something doesn't work, try `--mtag-v` for verbose error messages

config file example; note that `mtp` is an additive option so all of the mtp options will take effect:

```yaml
[/music]
  /mnt/nas/music
  flags:
    mtp: .bpm=~/bin/audio-bpm.py  # assign ".bpm" (numeric) with script
    mtp: key=f,t5,~/bin/audio-key.py  # force/overwrite, 5sec timeout
    mtp: ext=an,~/bin/file-ext.py  # will only run on non-audio files
    mtp: arch,built,ver,orig=an,eexe,edll,~/bin/exe.py  # only exe/dll
```


## event hooks

trigger a program on uploads, renames etc ([examples](./bin/hooks/))

you can set hooks before and/or after an event happens, and currently you can hook uploads, moves/renames, and deletes

there's a bunch of flags and stuff, see [`--help-hooks`](https://copyparty.eu/cli/#hooks-help-page)

if you want to write your own hooks, see [devnotes](./docs/devnotes.md#event-hooks)


### zeromq

event-hooks can send zeromq messages  instead of running programs

to send a 0mq message every time a file is uploaded,

* `--xau zmq:pub:tcp://*:5556` sends a PUB to any/all connected SUB clients
* `--xau t3,zmq:push:tcp://*:5557` sends a PUSH to exactly one connected PULL client
* `--xau t3,j,zmq:req:tcp://localhost:5555` sends a REQ to the connected REP client

the PUSH and REQ examples have `t3` (timeout after 3 seconds) because they block if there's no clients to talk to

* the REQ example does `t3,j` to send extended upload-info as json instead of just the filesystem-path

see [zmq-recv.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/zmq-recv.py) if you need something to receive the messages with

config file example; note that the hooks are additive options, so all of the xau options will take effect:

```yaml
[global]
  xau: zmq:pub:tcp://*:5556`  # send a PUB to any/all connected SUB clients
  xau: t3,zmq:push:tcp://*:5557`  # send PUSH to exactly one connected PULL cli
  xau: t3,j,zmq:req:tcp://localhost:5555`  # send REQ to the connected REP cli
```


### upload events

the older, more powerful approach ([examples](./bin/mtag/)):

```
-v /mnt/inc:inc:w:c,e2d,e2t,mte=+x1:c,mtp=x1=ad,kn,/usr/bin/notify-send
```

that was the commandline example; here's the config file example:

```yaml
[/inc]
  /mnt/inc
  accs:
    w: *
  flags:
    e2d, e2t  # enable indexing of uploaded files and their tags
    mte: +x1
    mtp: x1=ad,kn,/usr/bin/notify-send
```

so filesystem location `/mnt/inc` shared at `/inc`, write-only for everyone, appending `x1` to the list of tags to index (`mte`), and using `/usr/bin/notify-send` to "provide" tag `x1` for any filetype (`ad`) with kill-on-timeout disabled (`kn`)

that'll run the command `notify-send` with the path to the uploaded file as the first and only argument (so on linux it'll show a notification on-screen)

note that this is way more complicated than the new [event hooks](#event-hooks) but this approach has the following advantages:
* non-blocking and multithreaded; doesn't hold other uploads back
* you get access to tags from FFmpeg and other mtp parsers
* only trigger on new unique files, not dupes

note that it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1`

for reference, if you were to do this using event hooks instead, it would be like this: `-e2d --xau notify-send,hello,--`


## handlers

redefine behavior with plugins ([examples](./bin/handlers/))

replace 404 and 403 errors with something completely different (that's it for now)

as for client-side stuff, there is [plugins for modifying UI/UX](./contrib/plugins/)


## ip auth

autologin based on IP range (CIDR)  , using the global-option `--ipu`

for example, if everyone with an IP that starts with `192.168.123` should automatically log in as the user `spartacus`, then you can either specify `--ipu=192.168.123.0/24=spartacus` as a commandline option, or put this in a config file:

```yaml
[global]
  ipu: 192.168.123.0/24=spartacus
```

repeat the option to map additional subnets

**be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`


### restrict to ip

limit a user to certain IP ranges (CIDR)  , using the global-option `--ipr`

for example, if the user `spartacus` should get rejected if they're not connecting from an IP that starts with `192.168.123` or `172.16`, then you can either specify `--ipr=192.168.123.0/24,172.16.0.0/16=spartacus` as a commandline option, or put this in a config file:

```yaml
[global]
  ipr: 192.168.123.0/24,172.16.0.0/16=spartacus
```

repeat the option to map additional users


## identity providers

replace copyparty passwords with oauth and such

you can disable the built-in password-based login system, and instead replace it with a separate piece of software (an identity provider) which will then handle authenticating / authorizing of users; this makes it possible to login with passkeys / fido2 / webauthn / yubikey / ldap / active directory / oauth / many other single-sign-on contraptions

* the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header

  * `--auth-ord` configured auth precedence, for example to allow overriding the IdP with a copyparty password

* the login/logout links/buttons can be replaced with links to your IdP with `--idp-login` and `--idp-logout` , for example `--idp-login /idp/login/?redir={dst}` will expand `{dst}` to the page the user was on when clicking Login

* if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party

some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)

there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)

a more complete example of the copyparty configuration options [look like this](./docs/examples/docker/idp/copyparty.conf)

but if you just want to let users change their own passwords, then you probably want [user-changeable passwords](#user-changeable-passwords) instead


### generic header auth

other ways to auth by header

if you have a middleware which adds a header with a user identifier, for example tailscale's `Tailscale-User-Login: alice.m@forest.net` then you can automatically auth as `alice` by defining that mapping with `--idp-hm-usr '^Tailscale-User-Login^alice.m@forest.net^alice'` or the following config file:

```yaml
[global]
  idp-hm-usr: ^Tailscale-User-Login^alice.m@forest.net^alice
```

repeat the whole `idp-hm-usr` option to add more mappings


## user-changeable passwords

if permitted, users can change their own passwords  in the control-panel

* not compatible with [identity providers](#identity-providers)

* must be enabled with `--chpw` because account-sharing is a popular usecase

  * if you want to enable the feature but deny password-changing for a specific list of accounts, you can do that with `--chpw-no name1,name2,name3,...`

* to perform a password reset, edit the server config and give the user another password there, then do a [config reload](#server-config) or server restart

* the custom passwords are kept in a textfile at filesystem-path `--chpw-db`, by default `chpw.json` in the copyparty config folder

  * if you run multiple copyparty instances with different users you *almost definitely* want to specify separate DBs for each instance

  * if [password hashing](#password-hashing) is enabled, the passwords in the db are also hashed

    * ...which means that all user-defined passwords will be forgotten if you change password-hashing settings


## using the cloud as storage

connecting to an aws s3 bucket and similar

there is no built-in support for this, but you can use FUSE-software such as [rclone](https://rclone.org/) / [geesefs](https://github.com/yandex-cloud/geesefs) / [JuiceFS](https://juicefs.com/en/) to first mount your cloud storage as a local disk, and then let copyparty use (a folder in) that disk as a volume

if copyparty is unable to access the local folder that rclone/geesefs/JuiceFS provides (for example if it looks invisible) then you may need to run rclone with `--allow-other` and/or enable `user_allow_other` in `/etc/fuse.conf`

you will probably get decent speeds with the default config, however most likely restricted to using one TCP connection per file, so the upload-client won't be able to send multiple chunks in parallel

> before [v1.13.5](https://github.com/9001/copyparty/releases/tag/v1.13.5) it was recommended to use the volflag `sparse` to force-allow multiple chunks in parallel; this would improve the upload-speed from `1.5 MiB/s` to over `80 MiB/s` at the risk of provoking latent bugs in S3 or JuiceFS. But v1.13.5 added chunk-stitching, so this is now probably much less important. On the contrary, `nosparse` *may* now increase performance in some cases. Please try all three options (default, `sparse`, `nosparse`) as the optimal choice depends on your network conditions and software stack (both the FUSE-driver and cloud-server)

someone has also tested geesefs in combination with [gocryptfs](https://nuetzlich.net/gocryptfs/) with surprisingly good results, getting 60 MiB/s upload speeds on a gbit line, but JuiceFS won with 80 MiB/s using its built-in encryption

you may improve performance by specifying larger values for `--iobuf` / `--s-rd-sz` / `--s-wr-sz`

> if you've experimented with this and made interesting observations, please share your findings so we can add a section with specific recommendations :-)


## hiding from google

tell search engines you don't wanna be indexed,  either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:

* `--no-robots` adds HTTP (`X-Robots-Tag`) and HTML (`<meta>`) headers with `noindex, nofollow` globally
* volflag `[...]:c,norobots` does the same thing for that single volume
* volflag `[...]:c,robots` ALLOWS search-engine crawling for that volume, even if `--no-robots` is set globally

also, `--force-js` disables the plain HTML folder listing, making things harder to parse for *some* search engines -- note that crawlers which understand javascript (such as google) will not be affected


## themes

you can change the default theme with `--theme 2`, and add your own themes by modifying `browser.css` or providing your own css to `--css-browser`, then telling copyparty they exist by increasing `--themes`

<table><tr><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864907-17e2ac7d-319d-4f25-8718-2f376f614b51.png"><img src="https://user-images.githubusercontent.com/241032/165867551-fceb35dd-38f0-42bb-bef3-25ba651ca69b.png"></a>
0. classic dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/168644399-68938de5-da9b-445f-8d92-b51c74b5f345.png"><img src="https://user-images.githubusercontent.com/241032/168644404-8e1a2fdc-6e59-4c41-905e-ba5399ed686f.png"></a>
2. flat pm-monokai</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864901-db13a429-a5da-496d-8bc6-ce838547f69d.png"><img src="https://user-images.githubusercontent.com/241032/165867560-aa834aef-58dc-4abe-baef-7e562b647945.png"></a>
4. vice</td></tr><tr><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864905-692682eb-6fb4-4d40-b6fe-27d2c7d3e2a7.png"><img src="https://user-images.githubusercontent.com/241032/165867555-080b73b6-6d85-41bb-a7c6-ad277c608365.png"></a>
1. classic light</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/168645276-fb02fd19-190a-407a-b8d3-d58fee277e02.png"><img src="https://user-images.githubusercontent.com/241032/168645280-f0662b3c-9764-4875-a2e2-d91cc8199b23.png"></a>
3. flat light
</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864898-10ce7052-a117-4fcf-845b-b56c91687908.png"><img src="https://user-images.githubusercontent.com/241032/165867562-f3003d45-dd2a-4564-8aae-fed44c1ae064.png"></a>
5. <a href="https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/">hotdog stand</a></td></tr></table>

the classname of the HTML tag is set according to the selected theme, which is used to set colors as css variables ++

* each theme *generally* has a dark theme (even numbers) and a light theme (odd numbers), showing in pairs
* the first theme (theme 0 and 1) is `html.a`, second theme (2 and 3) is `html.b`
* if a light theme is selected, `html.y` is set, otherwise `html.z` is
* so if the dark edition of the 2nd theme is selected, you use any of `html.b`, `html.z`, `html.bz` to specify rules

see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where the color variables are set, and there's layout-specific stuff near the bottom

if you want to change the fonts, see [./docs/rice/](./docs/rice/)


## complete examples

* see [running on windows](./docs/examples/windows.md) for a fancy windows setup

  * or use any of the examples below, just replace `python copyparty-sfx.py` with `copyparty.exe` if you're using the exe edition

* allow anyone to download or upload files into the current folder:  
  `python copyparty-sfx.py`

  * enable searching and music indexing with `-e2dsa -e2ts`

  * start an FTP server on port 3921 with `--ftp 3921`

  * announce it on your LAN with `-z` so it appears in windows/Linux file managers

* anyone can upload, but nobody can see any files (even the uploader):  
  `python copyparty-sfx.py -e2dsa -v .::w`

  * block uploads if there's less than 4 GiB free disk space with `--df 4`

  * show a popup on new uploads with `--xau bin/hooks/notify.py`

* anyone can upload, and receive "secret" links for each upload they do:  
  `python copyparty-sfx.py -e2dsa -v .::wG:c,fk=8`

* anyone can browse (`r`), only `kevin` (password `okgo`) can upload/move/delete (`A`) files:  
  `python copyparty-sfx.py -e2dsa -a kevin:okgo -v .::r:A,kevin`

* read-only music server:  
  `python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts --no-robots --force-js --theme 2`
  
  * ...with bpm and key scanning  
    `-mtp .bpm=f,audio-bpm.py -mtp key=f,audio-key.py`
  
  * ...with a read-write folder for `kevin` whose password is `okgo`  
    `-a kevin:okgo -v /mnt/nas/inc:/inc:rw,kevin`
  
  * ...with logging to disk  
    `-lo log/cpp-%Y-%m%d-%H%M%S.txt.xz`


## listen on port 80 and 443

become a *real* webserver  which people can access by just going to your IP or domain without specifying a port

**if you're on windows,** then you just need to add the commandline argument `-p 80,443` and you're done! nice

**if you're on macos,** sorry, I don't know

**if you're on Linux,** you have the following 4 options:

* **option 1:** set up a [reverse-proxy](#reverse-proxy) -- this one makes a lot of sense if you're running on a proper headless server, because that way you get real HTTPS too

* **option 2:** NAT to port 3923 -- this is cumbersome since you'll need to do it every time you reboot, and the exact command may depend on your linux distribution:
  ```bash
  iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3923
  iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 3923
  ```

* **option 3:** disable the [security policy](https://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html) which prevents the use of 80 and 443; this is *probably* fine:
  ```
  setcap CAP_NET_BIND_SERVICE=+eip $(realpath $(which python))
  python copyparty-sfx.py -p 80,443
  ```

* **option 4:** run copyparty as root (please don't)


## reverse-proxy

running copyparty next to other websites  hosted on an existing webserver such as nginx, caddy, or apache

you can either:
* give copyparty its own domain or subdomain (recommended)
* or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
  * if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below

when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP. The best/safest approach is to configure your reverse-proxy so it gives copyparty a header which only contains the client's true/real IP-address, and then setting `--xff-hdr theHeaderName --rproxy 1` but alternatively, if you want/need to let copyparty handle this, look out for red and yellow log messages which explain how to do that. Basically, the log will say this:

> set `--xff-hdr` to the name of the http-header to read the IP from (usually `x-forwarded-for`, but cloudflare uses `cf-connecting-ip`), and then `--xff-src` to the IP of the reverse-proxy so copyparty will trust the xff-hdr. You will also need to configure `--rproxy` to `1` if the header only contains one IP (the correct one) or to a *negative value* if it contains multiple; `-1` being the rightmost and most trusted IP (the nearest proxy, so usually not the correct one), `-2` being the second-closest hop, and so on

Note that `--rp-loc` in particular will not work at all unless you configure the above correctly

some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which *could* be a nice speed boost, depending on a lot of factors
* **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
* depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2

for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/dev/shm/party.sock` (permission `770` means only members of group `www` can access it)

example webserver / reverse-proxy configs:

* [apache config](contrib/apache/copyparty.conf)
* caddy uds: `caddy reverse-proxy --from :8080 --to unix///dev/shm/party.sock`
* caddy tcp: `caddy reverse-proxy --from :8081 --to http://127.0.0.1:3923`
* [haproxy config](contrib/haproxy/copyparty.conf)
* [lighttpd subdomain](contrib/lighttpd/subdomain.conf) -- entire domain/subdomain
* [lighttpd subpath](contrib/lighttpd/subpath.conf) -- location-based (not optimal, but in case you need it)
* [nginx config](contrib/nginx/copyparty.conf) -- recommended
* [traefik config](contrib/traefik/copyparty.yaml) -- only use v3.6.7 or newer [due to CVE-2025-66490](https://github.com/9001/copyparty/issues/1205)


### real-ip

teaching copyparty how to see client IPs  when running behind a reverse-proxy, or a WAF, or another protection service such as cloudflare

if you (and maybe everybody else) keep getting a message that says `thank you for playing`, then you've gotten banned for malicious traffic. This ban applies to the IP address that copyparty *thinks* identifies the shady client -- so, depending on your setup, you might have to tell copyparty where to find the correct IP

for most common setups, there should be a helpful message in the server-log explaining what to do, but see [docs/xff.md](docs/xff.md) if you want to learn more, including a quick hack to **just make it work** (which is **not** recommended, but hey...)


### reverse-proxy performance

most reverse-proxies support connecting to copyparty either using uds/unix-sockets (`/dev/shm/party.sock`, faster/recommended) or using tcp (`127.0.0.1`)

with copyparty listening on a uds / unix-socket / unix-domain-socket and the reverse-proxy connecting to that:

| index.html   | upload      | download    | software |
| ------------ | ----------- | ----------- | -------- |
| 28'900 req/s | 6'900 MiB/s | 7'400 MiB/s | no-proxy |
| 18'750 req/s | 3'500 MiB/s | 2'370 MiB/s | haproxy |
|  9'900 req/s | 3'750 MiB/s | 2'200 MiB/s | caddy |
| 18'700 req/s | 2'200 MiB/s | 1'570 MiB/s | nginx |
|  9'700 req/s | 1'750 MiB/s | 1'830 MiB/s | apache |
|  9'900 req/s | 1'300 MiB/s | 1'470 MiB/s | lighttpd |

when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-fasioned way), speeds are a bit worse:

| index.html   | upload      | download    | software |
| ------------ | ----------- | ----------- | -------- |
| 21'200 req/s | 5'700 MiB/s | 6'700 MiB/s | no-proxy |
| 14'500 req/s | 1'700 MiB/s | 2'170 MiB/s | haproxy |
| 11'100 req/s | 2'750 MiB/s | 2'000 MiB/s | traefik |
|  8'400 req/s | 2'300 MiB/s | 1'950 MiB/s | caddy |
| 13'400 req/s | 1'100 MiB/s | 1'480 MiB/s | nginx |
|  8'400 req/s | 1'000 MiB/s | 1'000 MiB/s | apache |
|  6'500 req/s | 1'270 MiB/s | 1'500 MiB/s | lighttpd |

in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)

* if these results are bullshit because my config examples are bad, please submit corrections!


## permanent cloudflare tunnel

if you have a domain and want to get your copyparty online real quick,  either from your home-PC behind a CGNAT or from a server without an existing [reverse-proxy](#reverse-proxy) setup, one approach is to create a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/) (formerly "Argo Tunnel")

I'd recommend making a `Locally-managed tunnel` for more control, but if you prefer to make a `Remotely-managed tunnel` then this is currently how:

* `cloudflare dashboard` » `zero trust` » `networks` » `tunnels` » `create a tunnel` » `cloudflared` » choose a cool `subdomain` and leave the `path` blank, and use `service type` = `http` and `URL` = `127.0.0.1:3923`

* and if you want to just run the tunnel without installing it, skip the `cloudflared service install BASE64` step and instead do `cloudflared --no-autoupdate tunnel run --token BASE64`

NOTE: since people will be connecting through cloudflare, as mentioned in [real-ip](#real-ip) you should run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly

config file example:

```yaml
[global]
  xff-hdr: cf-connecting-ip
```


## prometheus

metrics/stats can be enabled  at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)

must be enabled with `--stats` since it reduces startup time a tiny bit, and you probably want `-e2dsa` too

the endpoint is only accessible by `admin` accounts, meaning the `a` in `rwmda` in the following example commandline: `python3 -m copyparty -a ed:wark -v /mnt/nas::rwmda,ed --stats -e2dsa`

follow a guide for setting up `node_exporter` except have it read from copyparty instead; example `/etc/prometheus/prometheus.yml` below

```yaml
scrape_configs:
  - job_name: copyparty
    metrics_path: /.cpr/metrics
    basic_auth:
      password: wark
    static_configs:
      - targets: ['192.168.123.1:3923']
```

currently the following metrics are available,
* `cpp_uptime_seconds` time since last copyparty restart
* `cpp_boot_unixtime_seconds` same but as an absolute timestamp
* `cpp_active_dl` number of active downloads
* `cpp_http_conns` number of open http(s) connections
* `cpp_http_reqs` number of http(s) requests handled
* `cpp_sus_reqs` number of 403/422/malicious requests
* `cpp_active_bans` number of currently banned IPs
* `cpp_total_bans` number of IPs banned since last restart

these are available unless `--nos-vst` is specified:
* `cpp_db_idle_seconds` time since last database activity (upload/rename/delete)
* `cpp_db_act_seconds` same but as an absolute timestamp
* `cpp_idle_vols` number of volumes which are idle / ready
* `cpp_busy_vols` number of volumes which are busy / indexing
* `cpp_offline_vols` number of volumes which are offline / unavailable
* `cpp_hashing_files` number of files queued for hashing / indexing
* `cpp_tagq_files` number of files queued for metadata scanning
* `cpp_mtpq_files` number of files queued for plugin-based analysis

and these are available per-volume only:
* `cpp_disk_size_bytes` total HDD size
* `cpp_disk_free_bytes` free HDD space

and these are per-volume and `total`:
* `cpp_vol_bytes` size of all files in volume
* `cpp_vol_files` number of files
* `cpp_dupe_bytes` disk space presumably saved by deduplication
* `cpp_dupe_files` number of dupe files
* `cpp_unf_bytes` currently unfinished / incoming uploads

some of the metrics have additional requirements to function correctly,
* `cpp_vol_*` requires either the `e2ds` volflag or `-e2dsa` global-option

the following options are available to disable some of the metrics:
* `--nos-hdd` disables `cpp_disk_*` which can prevent spinning up HDDs
* `--nos-vol` disables `cpp_vol_*` which reduces server startup time
* `--nos-vst` disables volume state, reducing the worst-case prometheus query time by 0.5 sec
* `--nos-dup` disables `cpp_dupe_*` which reduces the server load caused by prometheus queries
* `--nos-unf` disables `cpp_unf_*` for no particular purpose

note: the following metrics are counted incorrectly if multiprocessing is enabled with `-j`: `cpp_http_conns`, `cpp_http_reqs`, `cpp_sus_reqs`, `cpp_active_bans`, `cpp_total_bans`


## other extremely specific features

you'll never find a use for these:


### custom mimetypes

change the association of a file extension

using commandline args, you can do something like `--mime gif=image/jif` and `--mime ts=text/x.typescript` (can be specified multiple times)

in a config file, this is the same as:

```yaml
[global]
  mime: gif=image/jif
  mime: ts=text/x.typescript
```

run copyparty with `--mimes` to list all the default mappings


### GDPR compliance

imagine using copyparty professionally...  **TINLA/IANAL; EU laws are hella confusing**

* remember to disable logging, or configure logrotation to an acceptable timeframe with `-lo cpp-%Y-%m%d.txt.xz` or similar

* if running with the database enabled (recommended), then have it forget uploader-IPs after some time using `--forget-ip 43200`
  * don't set it too low; [unposting](#unpost) a file is no longer possible after this takes effect

* if you actually *are* a lawyer then I'm open for feedback, would be fun


### feature chickenbits

buggy feature? rip it out  by setting any of the following environment variables to disable its associated bell or whistle,

| env-var              | what it does |
| -------------------- | ------------ |
| `PRTY_NO_CTYPES`     | do not use features from external libraries such as kernel32 |
| `PRTY_NO_DB_LOCK`    | do not lock session/shares-databases for exclusive access |
| `PRTY_NO_IFADDR`     | disable ip/nic discovery by poking into your OS with ctypes |
| `PRTY_NO_IMPRESO`    | do not try to load js/css files using `importlib.resources` |
| `PRTY_NO_IPV6`       | disable some ipv6 support (should not be necessary since windows 2000) |
| `PRTY_NO_LZMA`       | disable streaming xz compression of incoming uploads |
| `PRTY_NO_MP`         | disable all use of the python `multiprocessing` module (actual multithreading, cpu-count for parsers/thumbnailers) |
| `PRTY_NO_SQLITE`     | disable all database-related functionality (file indexing, metadata indexing, most file deduplication logic) |
| `PRTY_NO_TLS`        | disable native HTTPS support; if you still want to accept HTTPS connections then TLS must now be terminated by a reverse-proxy |
| `PRTY_NO_TPOKE`      | disable systemd-tmpfilesd avoider |
| `PRTY_UNSAFE_STATE`  | allow storing secrets into emergency-fallback locations |

example: `PRTY_NO_IFADDR=1 python3 copyparty-sfx.py`


### feature beefybits

force-enable features with known issues on your OS/env  by setting any of the following environment variables, also affectionately known as `fuckitbits` or `hail-mary-bits`

| env-var                  | what it does |
| ------------------------ | ------------ |
| `PRTY_FORCE_MP`          | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
| `PRTY_FORCE_MAGIC`       | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |


# packages

the party might be closer than you think

if your distro/OS is not mentioned below, there might be some hints in the [«on servers»](#on-servers) section


## arch package

`pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))

it comes with a [systemd service](./contrib/systemd/copyparty@.service) as well as a [user service](./contrib/systemd/copyparty-user.service), and expects to find a [config file](./contrib/systemd/copyparty.example.conf) in `/etc/copyparty/copyparty.conf` or `~/.config/copyparty/copyparty.conf`

after installing, start either the system service or the user service and navigate to http://127.0.0.1:3923 for further instructions (unless you already edited the config files, in which case you are good to go, probably)

> to start the systemd service, either do `systemctl start --user copyparty` to start it as your own user, or `systemctl start copyparty@bob` to use unix-user `bob`


## fedora package

does not exist yet;  there are rumours that it is being packaged! keep an eye on this space...


## homebrew formulae

`brew install copyparty ffmpeg`  -- https://formulae.brew.sh/formula/copyparty

should work on all macs (both intel and apple silicon) and all relevant macos versions

the homebrew package is maintained by the homebrew team (thanks!)


## nix package

`nix profile install github:9001/copyparty`

requires a [flake-enabled](https://nixos.wiki/wiki/Flakes) installation of nix

some recommended dependencies are enabled by default; [override the package](https://github.com/9001/copyparty/blob/hovudstraum/contrib/package/nix/copyparty/default.nix#L3-L22) if you want to add/remove some features/deps

`ffmpeg-full` was chosen over `ffmpeg-headless` mainly because we need `withWebp` (and `withOpenmpt` is also nice) and being able to use a cached build felt more important than optimizing for size at the time -- PRs welcome if you disagree 👍


## nixos module

for [flake-enabled](https://nixos.wiki/wiki/Flakes) installations of NixOS:

```nix
{
  # add copyparty flake to your inputs
  inputs.copyparty.url = "github:9001/copyparty";

  # ensure that copyparty is an allowed argument to the outputs function
  outputs = { self, nixpkgs, copyparty }: {
    nixosConfigurations.yourHostName = nixpkgs.lib.nixosSystem {
      modules = [
        # load the copyparty NixOS module
        copyparty.nixosModules.default
        ({ pkgs, ... }: {
          # add the copyparty overlay to expose the package to the module
          nixpkgs.overlays = [ copyparty.overlays.default ];
          # (optional) install the package globally
          environment.systemPackages = [ pkgs.copyparty ];
          # configure the copyparty module
          services.copyparty.enable = true;
        })
      ];
    };
  };
}
```

if you don't use a flake in your configuration, you can use other dependency management tools like [npins](https://github.com/andir/npins), [niv](https://github.com/nmattia/niv), or even plain [`fetchTarball`](https://nix.dev/manual/nix/stable/language/builtins#builtins-fetchTarball), like so:

```nix
{ pkgs, ... }:

let
  # npins example, adjust for your setup. copyparty should be a path to the downloaded repo
  # for niv, just replace the npins folder import with the sources.nix file
  copyparty = (import ./npins).copyparty;

  # or with fetchTarball:
  copyparty = fetchTarball "https://github.com/9001/copyparty/archive/hovudstraum.tar.gz";
in

{
  # load the copyparty NixOS module
  imports = [ "${copyparty}/contrib/nixos/modules/copyparty.nix" ];

  # add the copyparty overlay to expose the package to the module
  nixpkgs.overlays = [ (import "${copyparty}/contrib/package/nix/overlay.nix") ];
  # (optional) install the package globally
  environment.systemPackages = [ pkgs.copyparty ];
  # configure the copyparty module
  services.copyparty.enable = true;
}
```

copyparty on NixOS is configured via `services.copyparty` options, for example:
```nix
services.copyparty = {
  enable = true;
  # the user to run the service as
  user = "copyparty"; 
  # the group to run the service as
  group = "copyparty"; 
  # directly maps to values in the [global] section of the copyparty config.
  # see `copyparty --help` for available options
  settings = {
    i = "0.0.0.0";
    # use lists to set multiple values
    p = [ 3210 3211 ];
    # use booleans to set binary flags
    no-reload = true;
    # using 'false' will do nothing and omit the value when generating a config
    ignored-flag = false;
  };

  # create users
  accounts = {
    # specify the account name as the key
    ed = {
      # provide the path to a file containing the password, keeping it out of /nix/store
      # must be readable by the copyparty service user
      passwordFile = "/run/keys/copyparty/ed_password";
    };
    # or do both in one go
    k.passwordFile = "/run/keys/copyparty/k_password";
  };

  # create a group
  groups = {
    # users "ed" and "k" are part of the group g1
    g1 = [ "ed" "k" ];
  };

  # create a volume
  volumes = {
    # create a volume at "/" (the webroot), which will
    "/" = {
      # share the contents of "/srv/copyparty"
      path = "/srv/copyparty";
      # see `copyparty --help-accounts` for available options
      access = {
        # everyone gets read-access, but
        r = "*";
        # users "ed" and "k" get read-write
        rw = [ "ed" "k" ];
      };
      # see `copyparty --help-flags` for available options
      flags = {
        # "fk" enables filekeys (necessary for upget permission) (4 chars long)
        fk = 4;
        # scan for new files every 60sec
        scan = 60;
        # volflag "e2d" enables the uploads database
        e2d = true;
        # "d2t" disables multimedia parsers (in case the uploads are malicious)
        d2t = true;
        # skips hashing file contents if path matches *.iso
        nohash = "\.iso$";
      };
    };
  };
  # you may increase the open file limit for the process
  openFilesLimit = 8192;
};
```

the passwordFile at /run/keys/copyparty/ could for example be generated by [agenix](https://github.com/ryantm/agenix), or you could just dump it in the nix store instead if that's acceptable


# browser support

TLDR: yes

![copyparty-ie4-fs8](https://user-images.githubusercontent.com/241032/118192791-fb31fe00-b446-11eb-9647-898ea8efc1f7.png)

`ie` = internet-explorer, `ff` = firefox, `c` = chrome, `iOS` = iPhone/iPad, `Andr` = Android

| feature         | ie6 | ie9  | ie10 | ie11 | ff 52 | c 49 | iOS | Andr |
| --------------- | --- | ---- | ---- | ---- | ----- | ---- | --- | ---- |
| browse files    | yep | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| thumbnail view  |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| basic uploader  | yep | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| up2k            |  -  |  -   | `*1` | `*1` |  yep  | yep  | yep | yep  |
| make directory  | yep | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| send message    | yep | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| set sort order  |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| zip selection   |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| file search     |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| file rename     |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| file cut/paste  |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| unpost uploads  |  -  |  -   | yep  | yep  |  yep  | yep  | yep | yep  |
| navpane         |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| image viewer    |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| video player    |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| markdown editor |  -  |  -   | `*2` | `*2` |  yep  | yep  | yep | yep  |
| markdown viewer |  -  | `*2` | `*2` | `*2` |  yep  | yep  | yep | yep  |
| play mp3/m4a    |  -  | yep  | yep  | yep  |  yep  | yep  | yep | yep  |
| play ogg/opus   |  -  |  -   |  -   |  -   |  yep  | yep  | `*3` | yep |
| **= feature =** | ie6 | ie9  | ie10 | ie11 | ff 52 | c 49 | iOS | Andr |

* internet explorer 6 through 8 behave the same
* firefox 52 and chrome 49 are the final winxp versions
* `*1` yes, but extremely slow (ie10: `1 MiB/s`, ie11: `270 KiB/s`)
* `*2` only able to do plaintext documents (no markdown rendering)
* `*3` iOS 11 and newer, opus only, and requires FFmpeg on the server

quick summary of more eccentric web-browsers trying to view a directory index:

| browser | will it blend |
| ------- | ------------- |
| **links** (2.21/macports) | can browse, login, upload/mkdir/msg |
| **lynx** (2.8.9/macports) | can browse, login, upload/mkdir/msg |
| **w3m** (0.5.3/macports)  | can browse, login, upload at 100kB/s, mkdir/msg |
| **netsurf** (3.10/arch)   | is basically ie6 with much better css (javascript has almost no effect) | 
| **opera** (11.60/winxp)   | OK: thumbnails, image-viewer, zip-selection, rename/cut/paste. NG: up2k, navpane, markdown, audio |
| **ie4** and **netscape** 4.0  | can browse, upload with `?b=u`, auth with `&pw=wark` |
| **ncsa mosaic** 2.7       | does not get a pass, [pic1](https://user-images.githubusercontent.com/241032/174189227-ae816026-cf6f-4be5-a26e-1b3b072c1b2f.png) - [pic2](https://user-images.githubusercontent.com/241032/174189225-5651c059-5152-46e9-ac26-7e98e497901b.png) |
| **SerenityOS** (7e98457)  | hits a page fault, works with `?b=u`, file upload not-impl |
| **sony psp** 5.50         | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
| **nintendo 3ds**          | can browse, upload, view thumbnails (thx bnjmn) |
| **Nintendo Wii (Opera 9.0 "Internet Channel")**          | can browse, can't upload or download (no local storage), can view images - works best with `?b=u`, default view broken |

<p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>


# server hall of fame

unexpected things that run copyparty:

* an old [allwinner](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/aallwinner.jpg) android tv-box (ziptie-strapped to an HDD) running a firmware which flips the CPU into Big-Endian mode early during boot
  * copyparty is [certified BE ready](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/be-ready.png) -- thanks, [Øl Telecom](http://ol-tele.com/)!
* an [SGI O2 (photo)](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.jpg?cache) with a grand total of 64 MiB RAM running SGI IRIX; [screenshot](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.png?cache)
  * thanks again to the wonderful people at [Øl Telecom](http://ol-tele.com/)
* a [wristwatch](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/clockyparty.jpg)


# client examples

interact with copyparty using non-browser clients

* javascript: dump some state into a file (two separate examples)
  * `await fetch('//127.0.0.1:3923/', {method:"PUT", body: JSON.stringify(foo)});`
  * `var xhr = new XMLHttpRequest(); xhr.open('POST', '//127.0.0.1:3923/msgs?raw'); xhr.send('foo');`

* curl/wget: upload some files (post=file, chunk=stdin)
  * `post(){ curl -F f=@"$1" http://127.0.0.1:3923/?pw=wark;}`  
    `post movie.mkv`  (gives HTML in return)
  * `post(){ curl -F f=@"$1" 'http://127.0.0.1:3923/?want=url&pw=wark';}`  
    `post movie.mkv`  (gives hotlink in return)
  * `post(){ curl -H pw:wark -H rand:8 -T "$1" http://127.0.0.1:3923/;}`  
    `post movie.mkv`  (randomized filename)
  * `post(){ wget --header='pw: wark' --post-file="$1" -O- http://127.0.0.1:3923/?raw;}`  
    `post movie.mkv`
  * `chunk(){ curl -H pw:wark -T- http://127.0.0.1:3923/;}`  
    `chunk <movie.mkv`

* curl: append to existing file with `?apnd`
  * `log(){ curl -H pw:wark -T- http://127.0.0.1:3923/logfile.txt?apnd;}`  
    `echo hey | log`

* bash: when curl and wget is not available or too boring
  * `(printf 'PUT /junk?pw=wark HTTP/1.1\r\n\r\n'; cat movie.mkv) | nc 127.0.0.1 3923`
  * `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`

* python: [u2c.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
  * file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
  * can be downloaded from copyparty: controlpanel -> connect -> [u2c.py](http://127.0.0.1:3923/.cpr/a/u2c.py)
  * see [./bin/README.md#u2cpy](bin/README.md#u2cpy)

* FUSE: mount a copyparty server as a local filesystem
  * cross-platform python client available in [./bin/](bin/)
  * able to mount nginx and iis directory listings too, not just copyparty
  * can be downloaded from copyparty: controlpanel -> connect -> [partyfuse.py](http://127.0.0.1:3923/.cpr/a/partyfuse.py)
  * [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)

* sharex (screenshot utility): see [./contrib/sharex.sxcu](./contrib/#sharexsxcu)
  * and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
  * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)

* [Custom Uploader](https://f-droid.org/en/packages/com.nyx.custom_uploader/) (an Android app) as an alternative to copyparty's own [PartyUP!](#android-app)
  * works if you set UploadURL to `https://your.com/foo/?want=url&pw=hunter2` and FormDataName `f`

* contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)

* [igloo irc](https://iglooirc.com/): Method: `post` Host: `https://you.com/up/?want=url&pw=hunter2` Multipart: `yes` File parameter: `f`

copyparty returns a truncated sha512sum of your PUT/POST as base64; you can generate the same checksum locally to verify uploads:

    b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|tr '+/' '-_'|head -c44;}
    b512 <movie.mkv

you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, url-param `?pw=hunter2`, or with basic-authentication (either as the username or password)

> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)

* unless you've enabled `--usernames`, then it's `PW: usr:pwd`, cookie `cppwd=usr:pwd`, url-param `?pw=usr:pwd`

NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename


## folder sync

sync folders to/from copyparty

NOTE: full bidirectional sync, like what [nextcloud](https://docs.nextcloud.com/server/latest/user_manual/sv/files/desktop_mobile_sync.html) and [syncthing](https://syncthing.net/) does, will never be supported! Only single-direction sync (server-to-client, or client-to-server) is possible with copyparty

* if you want bidirectional sync, then copyparty and syncthing *should* be entirely safe to combine; they should be able to collaborate on the same folders without causing any trouble for eachother. Many people do this, and there have been no issues so far. But, if you *do* encounter any problems, please [file a copyparty bug](https://github.com/9001/copyparty/issues/new/choose) and I'll try to help -- just keep in mind I've never used syncthing before :-)

the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)

if you want to sync with `u2c.py` then:
* the `e2dsa` option (either globally or volflag) must be enabled on the server for the volumes you're syncing into
* ...but DON'T enable global-options `no-hash` or `no-idx` (or volflags `nohash` / `noidx`), or at least make sure they are configured so they do not affect anything you are syncing into
* ...and u2c needs the delete-permission, so either `rwd` at minimum, or just `A` which is the same as `rwmd.a`
  * quick reminder that `a` and `A` are different permissions, and `.` is very useful for sync

alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare

* starting from rclone v1.63, rclone is faster than u2c.py on low-latency connections
  * but this is only true for the initial upload; u2c will be faster for periodic syncing


## mount as drive

a remote copyparty server as a local filesystem;  go to the control-panel and click `connect` to see a list of commands to do that

alternatively, some alternatives roughly sorted by speed (unreproducible benchmark), best first:

* [rclone-webdav](./docs/rclone.md) (25s), read/WRITE (rclone v1.63 or later)
* [rclone-http](./docs/rclone.md) (26s), read-only
* [partyfuse.py](./bin/#partyfusepy) (26s), read-only
* [rclone-ftp](./docs/rclone.md) (47s), read/WRITE
* davfs2 (103s), read/WRITE
* [win10-webdav](#webdav-server) (138s), read/WRITE
* [win10-smb2](#smb-server) (387s), read/WRITE

most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead

if you have volumes that are accessible without a password, then some webdav clients (such as davfs2) require the global-option `--dav-auth` to access any password-protected areas


# android app

upload to copyparty with one tap

<a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>

the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet

if you want to run the copyparty server on your android device, see [install on android](#install-on-android)


# iOS shortcuts

there is no iPhone app, but  the following shortcuts are almost as good:

* [upload to copyparty](https://www.icloud.com/shortcuts/41e98dd985cb4d3bb433222bc1e9e770) ([offline](https://github.com/9001/copyparty/raw/hovudstraum/contrib/ios/upload-to-copyparty.shortcut)) ([png](https://user-images.githubusercontent.com/241032/226118053-78623554-b0ed-482e-98e4-6d57ada58ea4.png)) based on the [original](https://www.icloud.com/shortcuts/ab415d5b4de3467b9ce6f151b439a5d7) by [Daedren](https://github.com/Daedren) (thx!)
  * can strip exif, upload files, pics, vids, links, clipboard
  * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
  * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there

if you want to run the copyparty server on your iPhone or iPad, see [install on iOS](#install-on-iOS)


# performance

defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload

below are some tweaks roughly ordered by usefulness:

* disabling HTTP/2 and HTTP/3 can make uploads 5x faster, depending on server/client software
* `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
* `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
  * and also makes thumbnails load faster, regardless of e2d/e2t
* `--dedup` enables deduplication and thus avoids writing to the HDD if someone uploads a dupe
* `--safe-dedup 1` makes deduplication much faster during upload by skipping verification of file contents; safe if there is no other software editing/moving the files in the volumes
* `--no-dirsz` shows the size of folder inodes instead of the total size of the contents, giving about 30% faster folder listings
* `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
* if your volumes are on a network-disk such as NFS / SMB / s3, specifying larger values for `--iobuf` and/or `--s-rd-sz` and/or `--s-wr-sz` may help; try setting all of them to `524288` or `1048576` or `4194304`
* `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
* when running on AlpineLinux or other musl-based distro, try mimalloc for higher performance (and twice as much RAM usage); `apk add mimalloc2` and run copyparty with env-var `LD_PRELOAD=/usr/lib/libmimalloc-secure.so.2`
  * note that mimalloc requires special care when combined with prisonparty and/or bubbleparty/bubblewrap; you must give it access to `/proc` and `/sys` otherwise you'll encounter issues with FFmpeg (audio transcoding, thumbnails)
* `-j0` (usually *not* recommended) enables multiprocessing (actual multithreading), can reduce latency to `20+80/numCores` percent and generally improve performance in cpu-intensive workloads, for example:
  * lots of connections (many users or heavy clients)
  * simultaneous downloads and uploads saturating a 20gbps connection
  * if `-e2d` is enabled, `-j2` gives 4x performance for directory listings; `-j4` gives 16x
  
  ...however it will probably *reduce* performance in most cases, since it also increases the server/filesystem/HDD load during uploads, and adds an overhead to internal communication, so keeping the default is generally best
* using [pypy](https://www.pypy.org/) instead of [cpython](https://www.python.org/) *can* be 70% faster for some workloads, but slower for many others
  * and pypy can sometimes crash on startup with `-j0` (TODO make issue)

* if you are running the copyparty server **on Windows or Macos:**
  * `--casechk=n` makes it much faster, but also awakens [the usual surprises](https://github.com/9001/copyparty/issues/781) you expect from a case-insensitive filesystem
    * this is the same as `casechk: n` in a config-file


## client-side

when uploading files,

* when uploading from very fast storage (NVMe SSD) with chrome/firefox, enable `[wasm]` in the `[⚙️] settings` tab to more effectively use all CPU-cores for hashing
  * don't do this on Safari (runs faster without)
  * don't do this on older browsers; likely to provoke browser-bugs (browser eats all RAM and crashes)
  * can be made default-enabled serverside with `--nosubtle 137` (chrome v137+) or `--nosubtle 2` (chrome+firefox)

* chrome is recommended (unfortunately), at least compared to firefox:
  * up to 90% faster when hashing, especially on SSDs
  * up to 40% faster when uploading over extremely fast internets
  * but [u2c.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) can be 40% faster than chrome again

* if you're cpu-bottlenecked, or the browser is maxing a cpu core:
  * up to 30% faster uploads if you hide the upload status list by switching away from the `[🚀]` up2k ui-tab (or closing it)
    * optionally you can switch to the lightweight potato ui by clicking the `[🥔]`
    * switching to another browser-tab also works, the favicon will update every 10 seconds in that case
  * unlikely to be a problem, but can happen when uploading many small files, or your internet is too fast, or PC too slow


# security

there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements  ; an `@everyone` for all important updates (at the lack of better ideas)

some notes on hardening

* set `--rproxy 0` *if and only if* your copyparty is directly facing the internet (not through a reverse-proxy)
  * cors doesn't work right otherwise
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
  * this returns html documents and svg images as plaintext, and also disables markdown rendering
  * the `nohtml` volflag also enables `noscript` which, on its own, prevents *most* javascript from running; enabling just `noscript` without `nohtml` makes it probably-safe (see below) to view html and svg files, but `nohtml` is necessary to block javascript in markdown documents
    * "probably-safe" because it relies on `Content-Security-Policy` so it depends on the reverseproxy to forward it, and the browser to understand it, but `nohtml` (the nuclear option) always works
* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or [`--help-bind`](https://copyparty.eu/cli/#bind-help-page)

safety profiles:

* option `-s` is a shortcut to set the following options:
  * `--no-thumb` disables thumbnails and audio transcoding to stop copyparty from running `FFmpeg`/`Pillow`/`VIPS` on uploaded files, which is a [good idea](https://www.cvedetails.com/vulnerability-list.php?vendor_id=3611) if anonymous upload is enabled
  * `--no-mtag-ff` uses `mutagen` to grab music tags instead of `FFmpeg`, which is safer and faster but less accurate
  * `--dotpart` hides uploads from directory listings while they're still incoming
  * `--no-robots` and `--force-js` makes life harder for crawlers, see [hiding from google](#hiding-from-google)

* option `-ss` is a shortcut for the above plus:
  * `--unpost 0`, `--no-del`, `--no-mv` disables all move/delete support
  * `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance
    * however note if you edit one file it will also affect the other copies
  * `--vague-403` returns a "404 not found" instead of "401 unauthorized" which is a common enterprise meme
  * `-nih` removes the server hostname from directory listings

* option `-sss` is a shortcut for the above plus:
  * `--no-dav` disables webdav support
  * `--no-logues` and `--no-readme` disables support for readme's and prologues / epilogues in directory listings, which otherwise lets people upload arbitrary (but sandboxed) `<script>` tags
  * `-lo cpp-%Y-%m%d-%H%M%S.txt.xz` enables logging to disk
  * `-ls **,*,ln,p,r` does a scan on startup for any dangerous symlinks

other misc notes:

* you can disable directory listings by giving permission `g` instead of `r`, only accepting direct URLs to files
  * you may want [filekeys](#filekeys) to prevent filename bruteforcing
  * permission `h` instead of `r` makes copyparty behave like a traditional webserver with directory listing/index disabled, returning index.html instead
    * compatibility with filekeys: index.html itself can be retrieved without the correct filekey, but all other files are protected


## gotchas

behavior that might be unexpected

* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `PREADME.md` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
* users can submit `<script>`s which autorun (in a sandbox) for other visitors in a few ways;
  * uploading a `README.md` -- avoid with `--no-readme`
  * renaming `some.html` to `.epilogue.html` -- avoid with either `--no-logues` or `--no-dot-ren`
  * the directory-listing embed is sandboxed (so any malicious scripts can't do any damage) but the markdown editor is not 100% safe, see below
* markdown documents can contain html and `<script>`s; attempts are made to prevent scripts from executing (unless `-emp` is specified) but this is not 100% bulletproof, so setting the `nohtml` volflag is still the safest choice
  * or eliminate the problem entirely by only giving write-access to trustworthy people :^)


## cors

cross-site request config

by default, except for `GET` and `HEAD` operations, all requests must either:
* not contain an `Origin` header at all
* or have an `Origin` matching the server domain
* or the header `PW` with your password as value

cors can be configured with `--acao` and `--acam`, or the protections entirely disabled with `--allow-csrf`


## filekeys

prevent filename bruteforcing

volflag `fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404

by default, filekeys are generated based on salt (`--fk-salt`) + filesystem-path + file-size + inode (if not windows); add volflag `fka` to generate slightly weaker filekeys which will not be invalidated if the file is edited (only salt + path)

permissions `wG` (write + upget) lets users upload files and receive their own filekeys, still without being able to see other uploads

### dirkeys

share specific folders in a volume  without giving away full read-access to the rest -- the visitor only needs the `g` (get) permission to view the link

volflag `dk` generates dirkeys (per-directory accesskeys) for all folders, granting read-access to that folder; by default only that folder itself, no subfolders

volflag `dky` disables the actual key-check, meaning anyone can see the contents of a folder where they have `g` access, but not its subdirectories

* `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (as if their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users

volflag `dks` lets people enter subfolders as well, and also enables download-as-zip/tar

if you enable dirkeys, it is probably a good idea to enable filekeys too, otherwise it will be impossible to hotlink files from a folder which was accessed using a dirkey

dirkeys are generated based on another salt (`--dk-salt`) + filesystem-path and have a few limitations:
* the key does not change if the contents of the folder is modified
  * if you need a new dirkey, either change the salt or rename the folder
* linking to a textfile (so it opens in the textfile viewer) is not possible if recipient doesn't have read-access


## password hashing

you can hash passwords  before putting them into config files / providing them as arguments; see [`--help-pwhash`](https://copyparty.eu/cli/#pwhash-help-page) for all the details

`--ah-alg argon2` enables it, and if you have any plaintext passwords then it'll print the hashed versions on startup so you can replace them

optionally also specify `--ah-cli` to enter an interactive mode where it will hash passwords without ever writing the plaintext ones to disk

the default configs take about 0.4 sec and 256 MiB RAM to process a new password on a decent laptop

when generating hashes using `--ah-cli` for docker or systemd services, make sure it is using the same `--ah-salt` by:
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
* setting the same `--ah-salt` in both environments

> ⚠️ if you have enabled `--usernames` then provide the password as `username:password` when hashing it, for example `ed:hunter2`


## https

both HTTP and HTTPS are accepted  by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)

copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well -- but note that HTTP/1 is usually faster than both HTTP/2 and HTTP/3

if [cfssl](https://github.com/cloudflare/cfssl/releases/latest) is installed, copyparty will automatically create a CA and server-cert on startup
* the certs are written to `--crt-dir` for distribution, see `--help` for the other `--crt` options
* this will be a self-signed certificate so you must install your `ca.pem` into all your browsers/devices
* if you want to avoid the hassle of distributing certs manually, please consider using a reverse proxy

to install cfssl on windows:
* [download](https://github.com/cloudflare/cfssl/releases/latest) `cfssl_windows_amd64.exe`, `cfssljson_windows_amd64.exe`, `cfssl-certinfo_windows_amd64.exe`
* rename them to `cfssl.exe`, `cfssljson.exe`, `cfssl-certinfo.exe`
* put them in PATH, for example inside `c:\windows\system32`


# recovering from crashes

## client crashes

### firefox wsod

firefox 87 can crash during uploads  -- the entire browser goes, including all other browser tabs, everything turns white

however you can hit `F12` in the up2k tab and use the devtools to see how far you got in the uploads:

* get a complete list of all uploads, organized by status (ok / no-good / busy / queued):  
  `var tabs = { ok:[], ng:[], bz:[], q:[] }; for (var a of up2k.ui.tab) tabs[a.in].push(a); tabs`

* list of filenames which failed:  
  `​var ng = []; for (var a of up2k.ui.tab) if (a.in != 'ok') ng.push(a.hn.split('<a href=\"').slice(-1)[0].split('\">')[0]); ng`

* send the list of filenames to copyparty for safekeeping:  
  `await fetch('/inc', {method:'PUT', body:JSON.stringify(ng,null,1)})`


# HTTP API

see [devnotes](./docs/devnotes.md#http-api)


# dependencies

mandatory deps:
* `jinja2` (is built into the SFX)


## optional dependencies

enable bonus features  by installing these python-packages from pypi or so:

enable [hashed passwords](#password-hashing) in config: `argon2-cffi`

enable [ftp-server](#ftp-server):
* for just plaintext FTP, `pyftpdlib` (is built into the SFX)
* with TLS encryption, `pyftpdlib pyopenssl`

enable [sftp-server](#sftp-server): `paramiko`

enable [music tags](#metadata-from-audio-files):
* either `mutagen` (fast, pure-python, skips a few tags, makes copyparty GPL? idk)
* or `ffprobe` (20x slower, more accurate, possibly dangerous depending on your distro and users)

enable [thumbnails](#thumbnails) of...
* **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
* **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
* **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats)

enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`

enable [smb](#smb-server) support (**not** recommended): `impacket==0.13.0`

`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram
* to install `pyvips` on Linux: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
* to install `pyvips` on windows: `pip install --user -U "pyvips[binary]"`

to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`


### dependency chickenbits

prevent loading an optional dependency  , for example if:

* you have an incompatible version installed and it causes problems
* you just don't want copyparty to use it, maybe to save ram

set any of the following environment variables to disable its associated optional feature,

| env-var              | what it does |
| -------------------- | ------------ |
| `PRTY_NO_ARGON2`     | disable argon2-cffi password hashing |
| `PRTY_NO_CFSSL`      | never attempt to generate self-signed certificates using [cfssl](https://github.com/cloudflare/cfssl) |
| `PRTY_NO_FFMPEG`     | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips |
| `PRTY_NO_FFPROBE`    | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
| `PRTY_NO_MAGIC`      | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
| `PRTY_NO_MUTAGEN`    | do not use [mutagen](https://pypi.org/project/mutagen/) for reading metadata from media files; will fallback to ffprobe |
| `PRTY_NO_PARAMIKO`   | disable sftp server ([paramiko](https://www.paramiko.org/)-based) |
| `PRTY_NO_PARTFTPY`   | disable tftp server ([partftpy](https://github.com/9001/partftpy)-based) |
| `PRTY_NO_PIL`        | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
| `PRTY_NO_PILF`       | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
| `PRTY_NO_PIL_AVIF`   | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
| `PRTY_NO_PIL_HEIF`   | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pillow-heif/) |
| `PRTY_NO_PIL_JXL`    | disable 3rd-party Pillow plugin for [JXL support](https://pypi.org/project/pillow-jxl-plugin/) |
| `PRTY_NO_PIL_WEBP`   | disable use of native webp support in Pillow |
| `PRTY_NO_PSUTIL`     | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
| `PRTY_NO_PYFTPD`     | disable ftp(s) server ([pyftpdlib](https://pypi.org/project/pyftpdlib/)-based) |
| `PRTY_NO_RAW`        | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
| `PRTY_NO_VIPS`       | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |

example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`

* `PRTY_NO_PIL` saves ram
* `PRTY_NO_VIPS` saves ram and startup time
* python2.7 on windows: `PRTY_NO_FFMPEG` + `PRTY_NO_FFPROBE` saves startup time


### dependency unvendoring

force use of system modules  instead of the vendored versions:

| env-var              | what it does |
| -------------------- | ------------ |
| `PRTY_SYS_ALL`       | all of the below |
| `PRTY_SYS_DNSLIB`    | replace [stolen/dnslib](./copyparty/stolen/dnslib) with [upstream](https://pypi.org/project/dnslib/) |
| `PRTY_SYS_IFADDR`    | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) |
| `PRTY_SYS_QRCG`      | replace [stolen/qrcodegen.py](./copyparty/stolen/qrcodegen.py) with [upstream](https://github.com/nayuki/QR-Code-generator/
Download .txt
gitextract_364x004l/

├── .eslintrc.json
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── something-else.md
│   ├── branch-rename.md
│   └── pull_request_template.md
├── .gitignore
├── .vscode/
│   ├── launch.json
│   ├── launch.py
│   ├── settings.json
│   └── tasks.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── bin/
│   ├── README.md
│   ├── bubbleparty.sh
│   ├── dbtool.py
│   ├── handlers/
│   │   ├── README.md
│   │   ├── caching-proxy.py
│   │   ├── ip-ok.py
│   │   ├── never404.py
│   │   ├── nooo.py
│   │   ├── randpic.py
│   │   ├── redirect.py
│   │   └── sorry.py
│   ├── hooks/
│   │   ├── README.md
│   │   ├── discord-announce.py
│   │   ├── image-noexif.py
│   │   ├── import-me.py
│   │   ├── into-the-cache-it-goes.py
│   │   ├── msg-log.py
│   │   ├── notify.py
│   │   ├── notify2.py
│   │   ├── podcast-normalizer.py
│   │   ├── qbittorrent-magnet.py
│   │   ├── reject-and-explain.py
│   │   ├── reject-extension.py
│   │   ├── reject-mimetype.py
│   │   ├── reject-ramdisk.py
│   │   ├── reloc-by-ext.py
│   │   ├── usb-eject.js
│   │   ├── usb-eject.py
│   │   ├── wget-i.py
│   │   ├── wget.py
│   │   ├── xiu-sha.py
│   │   └── xiu.py
│   ├── mtag/
│   │   ├── README.md
│   │   ├── audio-bpm.py
│   │   ├── audio-key-slicing.py
│   │   ├── audio-key.py
│   │   ├── cksum.py
│   │   ├── exe.py
│   │   ├── file-ext.py
│   │   ├── geotag.py
│   │   ├── guestbook-read.py
│   │   ├── guestbook.py
│   │   ├── image-noexif.py
│   │   ├── install-deps.sh
│   │   ├── media-hash.py
│   │   ├── mousepad.py
│   │   ├── rclone-upload.py
│   │   ├── res/
│   │   │   ├── twitter-unmute.user.js
│   │   │   ├── yt-ipr.conf
│   │   │   └── yt-ipr.user.js
│   │   ├── sleep.py
│   │   ├── very-bad-idea.py
│   │   ├── vidchk.py
│   │   ├── wget.py
│   │   └── yt-ipr.py
│   ├── partyfuse-streaming.py
│   ├── partyfuse.py
│   ├── partyfuse2.py
│   ├── partyjournal.py
│   ├── prisonparty.sh
│   ├── u2c.py
│   ├── unforget.py
│   ├── up2k.sh
│   └── zmq-recv.py
├── contrib/
│   ├── README.md
│   ├── apache/
│   │   └── copyparty.conf
│   ├── cfssl.sh
│   ├── copyparty.bat
│   ├── explorer-nothumbs-nofoldertypes.reg
│   ├── flameshot.sh
│   ├── haproxy/
│   │   └── copyparty.conf
│   ├── index.html
│   ├── ios/
│   │   └── upload-to-copyparty.shortcut
│   ├── ishare.iscu
│   ├── lighttpd/
│   │   ├── subdomain.conf
│   │   └── subpath.conf
│   ├── media-osd-bgone.ps1
│   ├── nginx/
│   │   └── copyparty.conf
│   ├── nixos/
│   │   └── modules/
│   │       └── copyparty.nix
│   ├── openrc/
│   │   └── copyparty
│   ├── package/
│   │   ├── arch/
│   │   │   └── PKGBUILD
│   │   ├── makedeb-mpr/
│   │   │   ├── PKGBUILD
│   │   │   ├── copyparty.conf
│   │   │   ├── copyparty.service
│   │   │   ├── index.md
│   │   │   └── prisonparty.service
│   │   ├── nix/
│   │   │   ├── copyparty/
│   │   │   │   ├── default.nix
│   │   │   │   ├── pin.json
│   │   │   │   └── update.py
│   │   │   ├── overlay.nix
│   │   │   └── partftpy/
│   │   │       ├── default.nix
│   │   │       ├── pin.json
│   │   │       └── update.py
│   │   └── rpm/
│   │       └── copyparty.spec
│   ├── plugins/
│   │   ├── README.md
│   │   ├── banner.js
│   │   ├── browser-icons.css
│   │   ├── graft-thumbs.js
│   │   ├── meadup.js
│   │   ├── minimal-up2k.html
│   │   ├── minimal-up2k.js
│   │   ├── quickmove.js
│   │   ├── rave.js
│   │   ├── up2k-hook-ytid.js
│   │   └── up2k-hooks.js
│   ├── podman-systemd/
│   │   ├── README.md
│   │   ├── copyparty.conf
│   │   └── copyparty.container
│   ├── rc/
│   │   └── copyparty
│   ├── send-to-cpp.contextlet.json
│   ├── setup-ashell.sh
│   ├── sharex.sxcu
│   ├── sharex12.sxcu
│   ├── systemd/
│   │   ├── cfssl.service
│   │   ├── copyparty-user.service
│   │   ├── copyparty.conf
│   │   ├── copyparty.example.conf
│   │   ├── copyparty.service
│   │   ├── copyparty@.service
│   │   ├── index.md
│   │   ├── prisonparty.service
│   │   └── prisonparty@.service
│   ├── themes/
│   │   └── bsod.css
│   ├── traefik/
│   │   └── copyparty.yaml
│   ├── webdav-cfg.bat
│   ├── windows/
│   │   └── copyparty-ctmp.bat
│   └── zfs-tune.py
├── copyparty/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── authsrv.py
│   ├── bos/
│   │   ├── __init__.py
│   │   ├── bos.py
│   │   └── path.py
│   ├── broker_mp.py
│   ├── broker_mpw.py
│   ├── broker_thr.py
│   ├── broker_util.py
│   ├── cert.py
│   ├── cfg.py
│   ├── dxml.py
│   ├── fsutil.py
│   ├── ftpd.py
│   ├── httpcli.py
│   ├── httpconn.py
│   ├── httpsrv.py
│   ├── ico.py
│   ├── mdns.py
│   ├── metrics.py
│   ├── mtag.py
│   ├── multicast.py
│   ├── pwhash.py
│   ├── qrkode.py
│   ├── res/
│   │   ├── __init__.py
│   │   └── insecure.pem
│   ├── sftpd.py
│   ├── smbd.py
│   ├── ssdp.py
│   ├── star.py
│   ├── stolen/
│   │   ├── __init__.py
│   │   ├── dnslib/
│   │   │   ├── README.md
│   │   │   ├── __init__.py
│   │   │   ├── bimap.py
│   │   │   ├── bit.py
│   │   │   ├── buffer.py
│   │   │   ├── dns.py
│   │   │   ├── label.py
│   │   │   ├── lex.py
│   │   │   └── ranges.py
│   │   ├── ifaddr/
│   │   │   ├── README.md
│   │   │   ├── __init__.py
│   │   │   ├── _posix.py
│   │   │   ├── _shared.py
│   │   │   └── _win32.py
│   │   ├── qrcodegen.py
│   │   └── surrogateescape.py
│   ├── sutil.py
│   ├── svchub.py
│   ├── szip.py
│   ├── tcpsrv.py
│   ├── tftpd.py
│   ├── th_cli.py
│   ├── th_srv.py
│   ├── u2idx.py
│   ├── up2k.py
│   ├── util.py
│   └── web/
│       ├── Makefile
│       ├── Makefile.s1
│       ├── a/
│       │   └── __init__.py
│       ├── baguettebox.js
│       ├── browser.css
│       ├── browser.html
│       ├── browser.js
│       ├── browser2.html
│       ├── cf.html
│       ├── dbg-audio.js
│       ├── idp.html
│       ├── md.css
│       ├── md.html
│       ├── md.js
│       ├── md2.css
│       ├── md2.js
│       ├── mde.css
│       ├── mde.html
│       ├── mde.js
│       ├── msg.html
│       ├── opds.xml
│       ├── opds_osd.xml
│       ├── rups.css
│       ├── rups.html
│       ├── rups.js
│       ├── shares.css
│       ├── shares.html
│       ├── shares.js
│       ├── splash.css
│       ├── splash.html
│       ├── splash.js
│       ├── svcs.html
│       ├── svcs.js
│       ├── tl/
│       │   ├── chi.js
│       │   ├── cze.js
│       │   ├── deu.js
│       │   ├── epo.js
│       │   ├── fin.js
│       │   ├── fra.js
│       │   ├── grc.js
│       │   ├── hun.js
│       │   ├── ita.js
│       │   ├── jpn.js
│       │   ├── kor.js
│       │   ├── nld.js
│       │   ├── nno.js
│       │   ├── nor.js
│       │   ├── pol.js
│       │   ├── por.js
│       │   ├── rus.js
│       │   ├── spa.js
│       │   ├── swe.js
│       │   ├── tur.js
│       │   ├── ukr.js
│       │   └── vie.js
│       ├── ui.css
│       ├── up2k.js
│       ├── util.js
│       └── w.hash.js
├── docs/
│   ├── README.md
│   ├── TODO.md
│   ├── bad-codecs.md
│   ├── biquad.html
│   ├── bufsize.txt
│   ├── changelog.md
│   ├── chungus.conf
│   ├── chunksizes.py
│   ├── copyparty.d/
│   │   ├── foo/
│   │   │   ├── another.conf
│   │   │   └── sibling.conf
│   │   ├── more-users/
│   │   │   ├── david.conf
│   │   │   └── james.conf
│   │   └── some.conf
│   ├── cursed-usecases/
│   │   └── README.md
│   ├── design.txt
│   ├── devnotes.md
│   ├── example.conf
│   ├── example2.conf
│   ├── examples/
│   │   ├── README.md
│   │   ├── docker/
│   │   │   ├── basic-docker-compose/
│   │   │   │   ├── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   ├── idp/
│   │   │   │   └── copyparty.conf
│   │   │   ├── idp-authelia-traefik/
│   │   │   │   ├── README.md
│   │   │   │   ├── authelia/
│   │   │   │   │   ├── configuration.yml
│   │   │   │   │   └── users_database.yml
│   │   │   │   ├── cpp/
│   │   │   │   │   └── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   ├── idp-authentik-traefik/
│   │   │   │   ├── README.md
│   │   │   │   ├── based-on/
│   │   │   │   │   ├── docker-compose-authentik.yml
│   │   │   │   │   └── docker-compose-traefik.yml
│   │   │   │   ├── cpp/
│   │   │   │   │   └── copyparty.conf
│   │   │   │   └── docker-compose.yml
│   │   │   └── portainer.md
│   │   └── windows.md
│   ├── hls.html
│   ├── idp.md
│   ├── lics.txt
│   ├── multisearch.html
│   ├── music-analysis.sh
│   ├── notes.bat
│   ├── notes.md
│   ├── notes.sh
│   ├── nuitka.txt
│   ├── pretend-youre-qnap.patch
│   ├── protocol-reference.sh
│   ├── rclone.md
│   ├── rice/
│   │   ├── README.md
│   │   └── rtl.patch
│   ├── synology-dsm.md
│   ├── tcp-debug.sh
│   ├── unirange.py
│   ├── up2k.txt
│   ├── versus.md
│   └── xff.md
├── flake.nix
├── pyproject.toml
├── scripts/
│   ├── bench/
│   │   └── filehash.sh
│   ├── copyparty-android.sh
│   ├── copyparty-repack.sh
│   ├── deps-docker/
│   │   ├── Dockerfile
│   │   ├── Makefile
│   │   ├── busy-mp3.sh
│   │   ├── codemirror.patch
│   │   ├── easymde-ln.patch
│   │   ├── easymde-marked6.patch
│   │   ├── easymde.patch
│   │   ├── genprism.py
│   │   ├── genprism.sh
│   │   ├── markdown-it.patch
│   │   ├── marked-ln.patch
│   │   ├── marked.patch
│   │   ├── mini-fa.css
│   │   ├── mini-fa.sh
│   │   ├── shiftbase.py
│   │   ├── showdown.patch
│   │   └── zopfli.makefile
│   ├── docker/
│   │   ├── Dockerfile.ac
│   │   ├── Dockerfile.dj
│   │   ├── Dockerfile.djd
│   │   ├── Dockerfile.djf
│   │   ├── Dockerfile.djff
│   │   ├── Dockerfile.dju
│   │   ├── Dockerfile.im
│   │   ├── Dockerfile.iv
│   │   ├── Dockerfile.min
│   │   ├── Dockerfile.min.pip
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── base/
│   │   │   ├── Dockerfile.zlibng
│   │   │   ├── Makefile
│   │   │   ├── arbeidspakke.sh
│   │   │   ├── build-no265.sh
│   │   │   ├── patch/
│   │   │   │   └── ffmpeg/
│   │   │   │       ├── aac-lc-only.patch
│   │   │   │       └── libavcodec/
│   │   │   │           ├── aacps.c
│   │   │   │           ├── aacsbr.c
│   │   │   │           ├── aacsbr_fixed.c
│   │   │   │           └── aacsbrdata.h
│   │   │   └── verchk.sh
│   │   ├── devnotes.md
│   │   ├── innvikler.sh
│   │   └── make.sh
│   ├── fusefuzz.py
│   ├── genhelp.sh
│   ├── genlic.py
│   ├── help2html.py
│   ├── help2txt.sh
│   ├── install-githooks.sh
│   ├── lics/
│   │   ├── 1.r13
│   │   ├── 2.r13
│   │   ├── 3.r13
│   │   ├── 4.r13
│   │   ├── 5.r13
│   │   ├── 6.r13
│   │   ├── README.md
│   │   └── rot.py
│   ├── logpack.sh
│   ├── make-pypi-release.sh
│   ├── make-pyz.sh
│   ├── make-rpm.sh
│   ├── make-sfx.sh
│   ├── make-tgz-release.sh
│   ├── patches/
│   │   ├── pyftpdlib-fe80.patch
│   │   └── pyftpdlib-win313.patch
│   ├── prep.sh
│   ├── profile.py
│   ├── py2/
│   │   └── queue/
│   │       └── __init__.py
│   ├── pyinstaller/
│   │   ├── README.md
│   │   ├── build.sh
│   │   ├── depchk.sh
│   │   ├── deps.sha512
│   │   ├── deps.txt
│   │   ├── ffmpeg.patch
│   │   ├── ffmpeg.txt
│   │   ├── icon.sh
│   │   ├── loader.py
│   │   ├── loader.rc
│   │   ├── notes.txt
│   │   ├── up2k.rc
│   │   ├── up2k.sh
│   │   ├── up2k.spec
│   │   └── up2k.spec.sh
│   ├── rls.sh
│   ├── run-tests.sh
│   ├── sfx.ls
│   ├── sfx.py
│   ├── sfx.sh
│   ├── speedtest-fs.py
│   ├── strip_hints/
│   │   └── a.py
│   ├── test/
│   │   ├── ptrav.py
│   │   ├── race.py
│   │   ├── smoketest.py
│   │   └── tftp.sh
│   ├── tl/
│   │   ├── 1.sh
│   │   └── 1.txt
│   ├── tl.js
│   ├── tl.py
│   ├── tlcheck.sh
│   ├── toc.sh
│   ├── uncomment.py
│   └── ziploader.py
├── setup.py
└── tests/
    ├── __init__.py
    ├── ptrav.py
    ├── res/
    │   └── idp/
    │       ├── 1.conf
    │       ├── 2.conf
    │       ├── 3.conf
    │       ├── 4.conf
    │       ├── 5.conf
    │       ├── 6.conf
    │       ├── 7.conf
    │       └── 8.conf
    ├── run.py
    ├── test_cp.py
    ├── test_dedup.py
    ├── test_dots.py
    ├── test_dxml.py
    ├── test_hooks.py
    ├── test_httpcli.py
    ├── test_idp.py
    ├── test_metrics.py
    ├── test_mv.py
    ├── test_shr.py
    ├── test_utils.py
    ├── test_vfs.py
    ├── test_webdav.py
    └── util.py
Download .txt
SYMBOL INDEX (2403 symbols across 162 files)

FILE: bin/dbtool.py
  function die (line 17) | def die(msg):
  function read_ver (line 22) | def read_ver(db):
  function ls (line 36) | def ls(db):
  function compare (line 50) | def compare(n1, d1, n2, d2, verbose):
  function copy_mtp (line 145) | def copy_mtp(d1, d2, tag, rm):
  function examples (line 193) | def examples():
  function main (line 209) | def main():

FILE: bin/handlers/caching-proxy.py
  function main (line 14) | def main(cli: "HttpCli", vn, rem):

FILE: bin/handlers/ip-ok.py
  function main (line 4) | def main(cli, vn, rem):

FILE: bin/handlers/never404.py
  function main (line 4) | def main(cli, vn, rem):

FILE: bin/handlers/nooo.py
  function say_no (line 4) | def say_no():
  function main (line 10) | def main(cli, vn, rem):

FILE: bin/handlers/randpic.py
  function main (line 12) | def main(cli, vn, rem):

FILE: bin/handlers/redirect.py
  function send_http_302_temporary_redirect (line 4) | def send_http_302_temporary_redirect(cli, new_path):
  function send_http_301_permanent_redirect (line 15) | def send_http_301_permanent_redirect(cli, new_path):
  function send_errorpage_with_redirect_link (line 23) | def send_errorpage_with_redirect_link(cli, new_path):
  function main (line 33) | def main(cli, vn, rem):

FILE: bin/handlers/sorry.py
  function main (line 4) | def main(cli, vn, rem):

FILE: bin/hooks/discord-announce.py
  function main (line 46) | def main():

FILE: bin/hooks/image-noexif.py
  function fsenc (line 41) | def fsenc(p):
  function main (line 45) | def main():

FILE: bin/hooks/import-me.py
  function main (line 41) | def main(ka: dict[str, Any]) -> dict[str, Any]:

FILE: bin/hooks/into-the-cache-it-goes.py
  function main (line 79) | def main():
  function download_with_curl (line 104) | def download_with_curl(url, pw):
  function download_with_wget (line 114) | def download_with_wget(url, pw):
  function download_with_python (line 126) | def download_with_python(url, pw):

FILE: bin/hooks/msg-log.py
  function write_ascending (line 69) | def write_ascending(filepath, msg_text):
  function write_descending (line 74) | def write_descending(filepath, msg_text):
  function main (line 109) | def main(argv=None):

FILE: bin/hooks/notify.py
  function humansize (line 37) | def humansize(n):
  function main (line 41) | def main():

FILE: bin/hooks/notify2.py
  function humansize (line 37) | def humansize(n):
  function main (line 41) | def main():

FILE: bin/hooks/podcast-normalizer.py
  function fsenc (line 68) | def fsenc(p):
  function main (line 72) | def main():

FILE: bin/hooks/qbittorrent-magnet.py
  function main (line 68) | def main():

FILE: bin/hooks/reject-and-explain.py
  function main (line 39) | def main():

FILE: bin/hooks/reject-extension.py
  function main (line 26) | def main():

FILE: bin/hooks/reject-mimetype.py
  function main (line 33) | def main():

FILE: bin/hooks/reject-ramdisk.py
  function main (line 52) | def main(ka: dict[str, Any]) -> dict[str, Any]:

FILE: bin/hooks/reloc-by-ext.py
  function main (line 49) | def main():

FILE: bin/hooks/usb-eject.js
  function usbclick (line 3) | function usbclick() {
  function eject_cb (line 9) | function eject_cb() {
  function add_eject_2 (line 18) | function add_eject_2(a) {
  function add_eject (line 47) | function add_eject() {

FILE: bin/hooks/usb-eject.py
  function main (line 35) | def main():

FILE: bin/hooks/wget-i.py
  function do_stuff (line 54) | def do_stuff(inf):
  function main (line 99) | def main(inf):

FILE: bin/hooks/wget.py
  function main (line 47) | def main():

FILE: bin/hooks/xiu-sha.py
  function fsenc (line 42) | def fsenc(p):
  function humantime (line 49) | def humantime(ts):
  function find_files_root (line 53) | def find_files_root(inf):
  function find_vol_root (line 64) | def find_vol_root(inf):
  function main (line 68) | def main():

FILE: bin/hooks/xiu.py
  function main (line 35) | def main():

FILE: bin/mtag/audio-bpm.py
  function det (line 23) | def det(tf):
  function main (line 78) | def main():

FILE: bin/mtag/audio-key-slicing.py
  function get_duration (line 26) | def get_duration():
  function get_segs (line 46) | def get_segs(dur):
  function slice (line 63) | def slice(tf):
  function det (line 105) | def det(tf):
  function main (line 110) | def main():

FILE: bin/mtag/audio-key.py
  function det (line 29) | def det(tf):
  function main (line 57) | def main():

FILE: bin/mtag/cksum.py
  function fsenc (line 18) | def fsenc(p):
  function main (line 28) | def main():

FILE: bin/mtag/exe.py
  function unk (line 14) | def unk(v):
  class PE2 (line 18) | class PE2(pefile.PE):
    method __init__ (line 19) | def __init__(self, *a, **ka):
    method noop (line 38) | def noop(*a, **ka):

FILE: bin/mtag/geotag.py
  function main (line 23) | def main():

FILE: bin/mtag/guestbook-read.py
  function main (line 38) | def main():

FILE: bin/mtag/guestbook.py
  function main (line 43) | def main():

FILE: bin/mtag/image-noexif.py
  function fsenc (line 53) | def fsenc(p):
  function main (line 57) | def main():

FILE: bin/mtag/media-hash.py
  function fsenc (line 15) | def fsenc(p):
  function det (line 24) | def det():
  function main (line 65) | def main():

FILE: bin/mtag/mousepad.py
  function main (line 23) | def main():

FILE: bin/mtag/rclone-upload.py
  function fsenc (line 13) | def fsenc(p):
  function main (line 49) | def main():

FILE: bin/mtag/res/twitter-unmute.user.js
  function grunnur (line 12) | function grunnur() {

FILE: bin/mtag/res/yt-ipr.user.js
  function main (line 9) | function main() {

FILE: bin/mtag/very-bad-idea.py
  function main (line 97) | def main():
  function open_post (line 115) | def open_post(txt):
  function open_url (line 142) | def open_url(txt):
  function try_mpv (line 175) | def try_mpv(url):
  function try_streamlink (line 187) | def try_streamlink(url):

FILE: bin/mtag/vidchk.py
  function fsenc (line 13) | def fsenc(p):
  function wfilter (line 46) | def wfilter(lines):
  function errchk (line 50) | def errchk(so, se, rc, dbg):
  function main (line 69) | def main():

FILE: bin/mtag/wget.py
  function main (line 37) | def main():

FILE: bin/mtag/yt-ipr.py
  function main (line 29) | def main():
  function get_expiration (line 52) | def get_expiration(url):
  function parse_youtube (line 58) | def parse_youtube(pd):
  function parse_freg (line 87) | def parse_freg(pd):
  function freg_conv (line 99) | def freg_conv(pd):

FILE: bin/partyfuse-streaming.py
  function print (line 74) | def print(*args, **kwargs):
  function termsafe (line 81) | def termsafe(txt):
  function threadless_log (line 90) | def threadless_log(msg):
  function boring_log (line 94) | def boring_log(msg):
  function rice_tid (line 99) | def rice_tid():
  function fancy_log (line 105) | def fancy_log(msg):
  function null_log (line 109) | def null_log(msg):
  function hexler (line 113) | def hexler(binary):
  function register_wtf8 (line 119) | def register_wtf8():
  function enwin (line 136) | def enwin(txt):
  function dewin (line 145) | def dewin(txt):
  class RecentLog (line 154) | class RecentLog(object):
    method __init__ (line 155) | def __init__(self):
    method put (line 164) | def put(self, msg):
    method printer (line 175) | def printer(self):
  function html_dec (line 208) | def html_dec(txt):
  class CacheNode (line 219) | class CacheNode(object):
    method __init__ (line 220) | def __init__(self, tag, data):
  class Gateway (line 226) | class Gateway(object):
    method __init__ (line 227) | def __init__(self, ar):
    method quotep (line 263) | def quotep(self, path):
    method newconn (line 267) | def newconn(self):
    method getconn_unix (line 285) | def getconn_unix(self, key=None):
    method getconn_winfsp (line 294) | def getconn_winfsp(self, key="x"):
    method putconn_winfsp (line 328) | def putconn_winfsp(self, c, path):
    method closeconn (line 332) | def closeconn(self, c):
    method sendreq (line 349) | def sendreq(self, meth, path, headers, **kwargs):
    method listdir (line 385) | def listdir(self, path):
    method download_file_range (line 414) | def download_file_range(self, path, ofs1, ofs2):
    method parse_html (line 462) | def parse_html(self, datasrc):
    method stat_dir (line 511) | def stat_dir(self, ts, sz=4096):
    method stat_file (line 523) | def stat_file(self, ts, sz):
  class CPPF (line 536) | class CPPF(Operations):
    method __init__ (line 537) | def __init__(self, ar):
    method _describe (line 551) | def _describe(self):
    method clean_dircache (line 566) | def clean_dircache(self):
    method get_cached_dir (line 579) | def get_cached_dir(self, dirpath):
    method get_cached_file (line 613) | def get_cached_file(self, path, get1, get2, file_sz):
    method _readdir (line 802) | def _readdir(self, path, fh=None):
    method readdir (line 817) | def readdir(self, path, fh=None):
    method read (line 820) | def read(self, path, length, offset, fh=None):
    method getattr (line 873) | def getattr(self, path, fh=None):
    method access (line 927) | def access(self, path, mode):
    method flush (line 931) | def flush(self, path, fh):
    method getxattr (line 935) | def getxattr(self, *args):
    method listxattr (line 939) | def listxattr(self, *args):
    method open (line 943) | def open(self, path, flags):
    method opendir (line 947) | def opendir(self, fh):
    method release (line 951) | def release(self, ino, fi):
    method releasedir (line 955) | def releasedir(self, ino, fi):
    method statfs (line 959) | def statfs(self, path):
    method _open (line 965) | def _open(self, path):
    method open (line 981) | def open(self, path, flags):
    method opendir (line 985) | def opendir(self, path):
    method flush (line 989) | def flush(self, path, fh):
    method release (line 992) | def release(self, ino, fi):
    method releasedir (line 995) | def releasedir(self, ino, fi):
    method access (line 998) | def access(self, path, mode):
  class TheArgparseFormatter (line 1008) | class TheArgparseFormatter(
  function main (line 1014) | def main():

FILE: bin/partyfuse.py
  function print (line 70) | def print(*args, **kwargs):
  function nullfun (line 86) | def nullfun(*a):
  function termsafe (line 113) | def termsafe(txt):
  function threadless_log (line 121) | def threadless_log(fmt, *a):
  function rice_tid (line 129) | def rice_tid():
  function fancy_log (line 140) | def fancy_log(fmt, *a):
  function register_wtf8 (line 145) | def register_wtf8():
  function enwin (line 162) | def enwin(txt):
  function dewin (line 166) | def dewin(txt):
  class RecentLog (line 170) | class RecentLog(object):
    method __init__ (line 171) | def __init__(self, ar):
    method put (line 181) | def put(self, fmt, *a):
    method printer (line 200) | def printer(self):
  function get_tid (line 233) | def get_tid():
  function html_dec (line 237) | def html_dec(txt):
  class CacheNode (line 248) | class CacheNode(object):
    method __init__ (line 249) | def __init__(self, tag, data):
  class Gateway (line 255) | class Gateway(object):
    method __init__ (line 256) | def __init__(self, ar):
    method quotep (line 327) | def quotep(self, path):
    method getconn (line 331) | def getconn(self, tid=None):
    method closeconn (line 351) | def closeconn(self, tid=None):
    method sendreq (line 359) | def sendreq(self, meth, path, headers, **kwargs):
    method listdir (line 386) | def listdir(self, path):
    method download_file_range (line 416) | def download_file_range(self, path, ofs1, ofs2):
    method parse_jls (line 435) | def parse_jls(self, sck):
    method parse_cpp (line 462) | def parse_cpp(self, sck):
    method parse_nginx (line 510) | def parse_nginx(self, sck):
    method parse_iis (line 559) | def parse_iis(self, sck):
    method stat_dir (line 614) | def stat_dir(self, ts, sz):
    method stat_file (line 626) | def stat_file(self, ts, sz):
  class CPPF (line 639) | class CPPF(Operations):
    method __init__ (line 640) | def __init__(self, ar):
    method _describe (line 655) | def _describe(self):
    method clean_dircache (line 671) | def clean_dircache(self):
    method get_cached_dir (line 685) | def get_cached_dir(self, dirpath):
    method get_cached_file (line 721) | def get_cached_file(self, path, get1, get2, file_sz):
    method _readdir (line 904) | def _readdir(self, path, fh=None):
    method readdir (line 917) | def readdir(self, path, fh=None):
    method read (line 927) | def read(self, path, length, offset, fh=None):
    method getattr (line 979) | def getattr(self, path, fh=None):
    method access (line 1029) | def access(self, path, mode):
    method flush (line 1033) | def flush(self, path, fh):
    method getxattr (line 1037) | def getxattr(self, *args):
    method listxattr (line 1041) | def listxattr(self, *args):
    method open (line 1045) | def open(self, path, flags):
    method opendir (line 1049) | def opendir(self, fh):
    method release (line 1053) | def release(self, ino, fi):
    method releasedir (line 1057) | def releasedir(self, ino, fi):
    method statfs (line 1061) | def statfs(self, path):
    method _open (line 1069) | def _open(self, path):
    method open (line 1085) | def open(self, path, flags):
    method opendir (line 1089) | def opendir(self, path):
    method flush (line 1093) | def flush(self, path, fh):
    method release (line 1096) | def release(self, ino, fi):
    method releasedir (line 1099) | def releasedir(self, ino, fi):
    method access (line 1102) | def access(self, path, mode):
  class TheArgparseFormatter (line 1112) | class TheArgparseFormatter(
  function main (line 1118) | def main():

FILE: bin/partyfuse2.py
  function threadless_log (line 71) | def threadless_log(msg):
  function boring_log (line 75) | def boring_log(msg):
  function rice_tid (line 80) | def rice_tid():
  function fancy_log (line 86) | def fancy_log(msg):
  function null_log (line 90) | def null_log(msg):
  function get_tid (line 101) | def get_tid():
  function html_dec (line 105) | def html_dec(txt):
  function register_wtf8 (line 114) | def register_wtf8():
  function enwin (line 131) | def enwin(txt):
  function dewin (line 140) | def dewin(txt):
  class CacheNode (line 149) | class CacheNode(object):
    method __init__ (line 150) | def __init__(self, tag, data):
  class Stat (line 156) | class Stat(fuse.Stat):
    method __init__ (line 157) | def __init__(self):
  class Gateway (line 170) | class Gateway(object):
    method __init__ (line 171) | def __init__(self, base_url, pw):
    method quotep (line 191) | def quotep(self, path):
    method getconn (line 195) | def getconn(self, tid=None):
    method closeconn (line 207) | def closeconn(self, tid=None):
    method sendreq (line 215) | def sendreq(self, *args, **ka):
    method listdir (line 233) | def listdir(self, path):
    method download_file_range (line 249) | def download_file_range(self, path, ofs1, ofs2):
    method parse_jls (line 268) | def parse_jls(self, datasrc):
    method stat_dir (line 292) | def stat_dir(self, ts, sz=4096):
    method stat_file (line 302) | def stat_file(self, ts, sz):
  class CPPF (line 312) | class CPPF(Fuse):
    method __init__ (line 313) | def __init__(self, *args, **kwargs):
    method init2 (line 325) | def init2(self):
    method clean_dircache (line 330) | def clean_dircache(self):
    method get_cached_dir (line 343) | def get_cached_dir(self, dirpath):
    method get_cached_file (line 378) | def get_cached_file(self, path, get1, get2, file_sz):
    method _readdir (line 533) | def _readdir(self, path):
    method readdir (line 547) | def readdir(self, path, offset):
    method open (line 552) | def open(self, path, flags):
    method read (line 563) | def read(self, path, length, offset, fh=None, *args):
    method getattr (line 590) | def getattr(self, path):
  function main (line 624) | def main():

FILE: bin/partyjournal.py
  class APF (line 32) | class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptio...
  function s3dec (line 40) | def s3dec(v):
  function quotep (line 48) | def quotep(txt):
  function html_escape (line 56) | def html_escape(s, quote=False, crlf=False):
  function main (line 71) | def main():

FILE: bin/u2c.py
  class _UTC (line 78) | class _UTC(datetime.tzinfo):
    method utcoffset (line 79) | def utcoffset(self, dt):
    method tzname (line 82) | def tzname(self, dt):
    method dst (line 85) | def dst(self, dt):
  function ub64enc (line 94) | def ub64enc(bs):
  class Fatal (line 103) | class Fatal(Exception):
  class Daemon (line 107) | class Daemon(threading.Thread):
    method __init__ (line 108) | def __init__(self, target, name=None, a=None):
    method run (line 115) | def run(self):
  class HSQueue (line 124) | class HSQueue(Queue):
    method _init (line 125) | def _init(self, maxsize):
    method _qsize (line 130) | def _qsize(self):
    method _put (line 133) | def _put(self, item):
    method _get (line 139) | def _get(self):
  class HCli (line 143) | class HCli(object):
    method __init__ (line 144) | def __init__(self, ar):
    method _connect (line 179) | def _connect(self, timeout):
    method req (line 193) | def req(self, meth, vpath, hdrs, body=None, ctype=None):
  class File (line 247) | class File(object):
    method __init__ (line 250) | def __init__(self, top, rel, size, lmod):
  class FileSlice (line 280) | class FileSlice(object):
    method __init__ (line 283) | def __init__(self, file, cids):
    method subchunk (line 304) | def subchunk(self, maxsz, nth):
    method unsub (line 321) | def unsub(self):
    method _open (line 326) | def _open(self):
    method close (line 342) | def close(self, *a, **ka):
    method tell (line 345) | def tell(self):
    method _seek (line 348) | def _seek(self, ofs, wh=0):
    method _read (line 364) | def _read(self, sz):
    method _seek0 (line 372) | def _seek0(self, ofs, wh=0):
    method _read0 (line 376) | def _read0(self, sz):
  class MTHash (line 381) | class MTHash(object):
    method __init__ (line 382) | def __init__(self, cores):
    method hash (line 394) | def hash(self, f, fsz, chunksz, pcb=None, pcb_opaque=None):
    method worker (line 429) | def worker(self):
    method hash_at (line 439) | def hash_at(self, nch):
  function safe_print (line 464) | def safe_print(*a, **ka):
  function eprint (line 470) | def eprint(*a, **ka):
  function flushing_print (line 481) | def flushing_print(*a, **ka):
  function termsize (line 496) | def termsize():
  class CTermsize (line 531) | class CTermsize(object):
    method __init__ (line 532) | def __init__(self):
    method worker (line 545) | def worker(self):
    method ev_sig (line 557) | def ev_sig(self, *a, **ka):
    method scroll_region (line 560) | def scroll_region(self, margin):
  function undns (line 574) | def undns(url):
  function _scd (line 600) | def _scd(err, top):
  function _lsd (line 612) | def _lsd(err, top):
  function walkdir (line 629) | def walkdir(err, top, excl, seen):
  function walkdirs (line 653) | def walkdirs(err, tops, excl):
  function quotep (line 695) | def quotep(btxt):
  function humansize (line 705) | def humansize(sz, terse=False):
  function up2k_chunksize (line 722) | def up2k_chunksize(filesize):
  function get_hashlist (line 737) | def get_hashlist(file, pcb, mth):
  function printlink (line 781) | def printlink(ar, purl, name, fk):
  function handshake (line 804) | def handshake(ar, file, search):
  function upload (line 896) | def upload(fsl, stats, maxsz):
  class Ctl (line 945) | class Ctl(object):
    method _scan (line 951) | def _scan(self):
    method __init__ (line 984) | def __init__(self, ar, stats=None):
    method _safe (line 1040) | def _safe(self):
    method _fancy (line 1093) | def _fancy(self):
    method cleanup_vt100 (line 1186) | def cleanup_vt100(self):
    method cb_hasher (line 1193) | def cb_hasher(self, file, ofs):
    method hasher (line 1196) | def hasher(self):
    method _check_if_done (line 1331) | def _check_if_done(self):
    method handshaker (line 1338) | def handshaker(self):
    method uploader (line 1459) | def uploader(self):
    method up_done (line 1522) | def up_done(self, file):
  class APF (line 1527) | class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptio...
  function main (line 1531) | def main():

FILE: bin/unforget.py
  class APF (line 27) | class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptio...
  function s3enc (line 35) | def s3enc(rd: str, fn: str) -> tuple[str, str]:
  function main (line 48) | def main():

FILE: bin/zmq-recv.py
  function sub_server (line 35) | def sub_server():
  function pull_server (line 45) | def pull_server():
  function rep_server (line 54) | def rep_server():

FILE: contrib/package/nix/copyparty/update.py
  function get_formatted_hash (line 24) | def get_formatted_hash(binary):
  function version_from_tar_gz (line 32) | def version_from_tar_gz(path):
  function remote_release_pin (line 42) | def remote_release_pin():
  function local_release_pin (line 56) | def local_release_pin(path):
  function main (line 65) | def main():

FILE: contrib/package/nix/partftpy/update.py
  function get_formatted_hash (line 19) | def get_formatted_hash(binary):
  function remote_release_pin (line 27) | def remote_release_pin():
  function main (line 41) | def main():

FILE: contrib/plugins/banner.js
  function bannerdiv (line 24) | function bannerdiv(border) {

FILE: contrib/plugins/meadup.js
  function initKeybaord (line 16) | function initKeybaord(BASE_URL, HAMBAGA, consoleLog, consoleError) {

FILE: contrib/plugins/quickmove.js
  function ask_for_confirmation_and_then_move (line 37) | function ask_for_confirmation_and_then_move() {
  function move_selected_files (line 47) | function move_selected_files() {
  function move_next_file (line 66) | function move_next_file() {
  function our_hotkey_handler (line 102) | function our_hotkey_handler(e) {
  function enable_plugin (line 126) | function enable_plugin() {

FILE: contrib/plugins/rave.js
  function rave_load (line 51) | function rave_load() {
  function rave_unload (line 80) | function rave_unload() {
  function readbeats (line 85) | function readbeats() {
  function hrand (line 105) | function hrand() {
  function raver (line 109) | function raver() {
  function untz (line 192) | function untz() {

FILE: contrib/plugins/up2k-hook-ytid.js
  function up2k_namefilter (line 9) | function up2k_namefilter(good_files, nil_files, bad_files, hooks) {
  function bstrpos (line 19) | function bstrpos(buf, ptn) {
  function a_up2k_namefilter (line 40) | async function a_up2k_namefilter(good_files, nil_files, bad_files, hooks) {

FILE: contrib/plugins/up2k-hooks.js
  function up2k_namefilter (line 3) | function up2k_namefilter(good_files, nil_files, bad_files, hooks) {

FILE: contrib/zfs-tune.py
  function min_ex (line 40) | def min_ex(max_lines: int = 8, reverse: bool = False) -> str:
  function set_pagesize (line 50) | def set_pagesize(db_path):
  function main (line 78) | def main():

FILE: copyparty/__init__.py
  class EnvParams (line 139) | class EnvParams(object):
    method __init__ (line 140) | def __init__(self) -> None:

FILE: copyparty/__main__.py
  class RiceFormatter (line 105) | class RiceFormatter(argparse.HelpFormatter):
    method __init__ (line 106) | def __init__(self, *args: Any, **kwargs: Any) -> None:
    method _get_help_string (line 112) | def _get_help_string(self, action: argparse.Action) -> str:
    method _fill_text (line 133) | def _fill_text(self, text: str, width: int, indent: str) -> str:
    method __add_whitespace (line 137) | def __add_whitespace(self, idx: int, iWSpace: int, text: str) -> str:
    method _split_lines (line 140) | def _split_lines(self, text: str, width: int) -> list[str]:
  class Dodge11874 (line 159) | class Dodge11874(RiceFormatter):
    method __init__ (line 160) | def __init__(self, *args: Any, **kwargs: Any) -> None:
  class BasicDodge11874 (line 165) | class BasicDodge11874(
    method __init__ (line 168) | def __init__(self, *args: Any, **kwargs: Any) -> None:
  function lprint (line 173) | def lprint(*a: Any, **ka: Any) -> None:
  function warn (line 183) | def warn(msg: str) -> None:
  function init_E (line 187) | def init_E(EE: EnvParams) -> None:
  function get_srvname (line 287) | def get_srvname(verbose) -> str:
  function get_salt (line 312) | def get_salt(name: str, nbytes: int) -> str:
  function ensure_locale (line 323) | def ensure_locale() -> None:
  function ensure_webdeps (line 345) | def ensure_webdeps() -> None:
  function configure_ssl_ver (line 361) | def configure_ssl_ver(al: argparse.Namespace) -> None:
  function configure_ssl_ciphers (line 405) | def configure_ssl_ciphers(al: argparse.Namespace) -> None:
  function args_from_cfg (line 430) | def args_from_cfg(cfg_path: str) -> list[str]:
  function expand_cfg (line 459) | def expand_cfg(argv) -> list[str]:
  function quotecheck (line 482) | def quotecheck(al):
  function sighandler (line 497) | def sighandler(sig: Optional[int] = None, frame: Optional[FrameType] = N...
  function disable_quickedit (line 508) | def disable_quickedit() -> None:
  function sfx_tpoke (line 558) | def sfx_tpoke(top: str):
  function showlic (line 577) | def showlic() -> None:
  function get_sects (line 591) | def get_sects():
  function build_flags_desc (line 1170) | def build_flags_desc():
  function add_general (line 1184) | def add_general(ap, nc, srvname):
  function add_qr (line 1212) | def add_qr(ap, tty):
  function add_fs (line 1231) | def add_fs(ap):
  function add_share (line 1242) | def add_share(ap):
  function add_upload (line 1254) | def add_upload(ap):
  function add_network (line 1303) | def add_network(ap):
  function add_tls (line 1335) | def add_tls(ap, cert_path):
  function add_cert (line 1346) | def add_cert(ap, cert_path):
  function add_auth (line 1365) | def add_auth(ap):
  function add_chpw (line 1401) | def add_chpw(ap):
  function add_zeroconf (line 1411) | def add_zeroconf(ap):
  function add_zc_mdns (line 1421) | def add_zc_mdns(ap):
  function add_zc_ssdp (line 1446) | def add_zc_ssdp(ap):
  function add_sftp (line 1456) | def add_sftp(ap):
  function add_ftp (line 1472) | def add_ftp(ap):
  function add_webdav (line 1486) | def add_webdav(ap):
  function add_tftp (line 1499) | def add_tftp(ap):
  function add_smb (line 1513) | def add_smb(ap):
  function add_opds (line 1528) | def add_opds(ap):
  function add_handlers (line 1534) | def add_handlers(ap):
  function add_hooks (line 1541) | def add_hooks(ap):
  function add_stats (line 1557) | def add_stats(ap):
  function add_yolo (line 1568) | def add_yolo(ap):
  function add_optouts (line 1578) | def add_optouts(ap):
  function add_safety (line 1606) | def add_safety(ap):
  function add_salt (line 1643) | def add_salt(ap, fk_salt, dk_salt, ah_salt):
  function add_shutdown (line 1657) | def add_shutdown(ap):
  function add_logging (line 1664) | def add_logging(ap):
  function add_admin (line 1688) | def add_admin(ap):
  function add_thumbnail (line 1702) | def add_thumbnail(ap):
  function add_transcoding (line 1743) | def add_transcoding(ap):
  function add_tail (line 1756) | def add_tail(ap):
  function add_rss (line 1766) | def add_rss(ap):
  function add_db_general (line 1776) | def add_db_general(ap, hcores):
  function add_db_metadata (line 1808) | def add_db_metadata(ap):
  function add_txt (line 1827) | def add_txt(ap):
  function add_og (line 1841) | def add_og(ap):
  function add_ui (line 1859) | def add_ui(ap, retry: int):
  function add_debug (line 1925) | def add_debug(ap):
  function run_argparse (line 1952) | def run_argparse(
  function main (line 2049) | def main(argv: Optional[list[str]] = None) -> None:

FILE: copyparty/authsrv.py
  class CfgEx (line 117) | class CfgEx(Exception):
  class AXS (line 121) | class AXS(object):
    method __init__ (line 122) | def __init__(
    method __repr__ (line 144) | def __repr__(self) -> str:
  class Lim (line 149) | class Lim(object):
    method __init__ (line 150) | def __init__(
    method log (line 191) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method set_rotf (line 195) | def set_rotf(self, fmt: str, tz: str) -> None:
    method all (line 205) | def all(
    method chk_sz (line 237) | def chk_sz(self, sz: int) -> None:
    method chk_vsz (line 244) | def chk_vsz(
    method chk_df (line 263) | def chk_df(self, abspath: str, sz: int, already_written: bool = False)...
    method chk_rem (line 291) | def chk_rem(self, rem: str) -> None:
    method rot (line 295) | def rot(self, path: str) -> tuple[str, str]:
    method dive (line 317) | def dive(self, path: str, lvs: int) -> Optional[str]:
    method nup (line 361) | def nup(self, ip: str) -> None:
    method bup (line 367) | def bup(self, ip: str, nbytes: int) -> None:
    method chk_nup (line 376) | def chk_nup(self, ip: str) -> None:
    method chk_bup (line 388) | def chk_bup(self, ip: str) -> None:
  class VFS (line 403) | class VFS(object):
    method __init__ (line 406) | def __init__(
    method __repr__ (line 471) | def __repr__(self) -> str:
    method get_all_vols (line 479) | def get_all_vols(
    method add (line 502) | def add(self, src: str, dst: str, dst0: str) -> "VFS":
    method _copy_flags (line 539) | def _copy_flags(self, name: str) -> dict[str, Any]:
    method bubble_flags (line 554) | def bubble_flags(self) -> None:
    method _find (line 563) | def _find(self, vpath: str) -> tuple["VFS", str]:
    method can_access (line 579) | def can_access(
    method get_perms (line 591) | def get_perms(self, vpath: str, uname: str) -> str:
    method get (line 598) | def get(
    method _get_share_src (line 637) | def _get_share_src(self, vrem: str) -> tuple["VFS", str]:
    method _get_dbv (line 645) | def _get_dbv(self, vrem: str) -> tuple["VFS", str]:
    method casechk (line 653) | def casechk(self, rem: str, do_stat: bool) -> bool:
    method _canonical_null (line 679) | def _canonical_null(self, rem: str, resolve: bool = True) -> str:
    method _dcanonical_null (line 682) | def _dcanonical_null(self, rem: str) -> str:
    method _canonical (line 685) | def _canonical(self, rem: str, resolve: bool = True) -> str:
    method _dcanonical (line 693) | def _dcanonical(self, rem: str) -> str:
    method _canonical_shr (line 702) | def _canonical_shr(self, rem: str, resolve: bool = True) -> str:
    method _dcanonical_shr (line 725) | def _dcanonical_shr(self, rem: str) -> str:
    method _ls_nope (line 744) | def _ls_nope(
    method _ls_shr (line 749) | def _ls_shr(
    method _ls (line 764) | def _ls(
    method walk (line 812) | def walk(
    method zipgen (line 906) | def zipgen(
    method chk_ap (line 959) | def chk_ap(self, ap: str, st: Optional[os.stat_result] = None) -> Opti...
    method check_landmarks (line 1008) | def check_landmarks(self) -> bool:
  class AuthSrv (line 1062) | class AuthSrv(object):
    method __init__ (line 1065) | def __init__(
    method log (line 1120) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method laggy_iter (line 1124) | def laggy_iter(self, iterable: Iterable[Any]) -> Generator[Any, None, ...
    method vf0 (line 1134) | def vf0(self):
    method vf0b (line 1137) | def vf0b(self):
    method idp_checkin (line 1140) | def idp_checkin(
    method _update_idp_db (line 1171) | def _update_idp_db(self, uname: str, gname: str) -> None:
    method _map_volume_idp (line 1187) | def _map_volume_idp(
    method _map_volume (line 1276) | def _map_volume(
    method _e (line 1316) | def _e(self, desc: Optional[str] = None) -> None:
    method _l (line 1328) | def _l(self, ln: str, c: int, desc: str) -> None:
    method _all_un_gn (line 1342) | def _all_un_gn(
    method _parse_config_file (line 1366) | def _parse_config_file(
    method _parse_config_file_2 (line 1392) | def _parse_config_file_2(
    method _read_vol_str_idp (line 1586) | def _read_vol_str_idp(
    method _read_vol_str (line 1665) | def _read_vol_str(self, lvl: str, unames: list[str], axs: AXS) -> None:
    method _read_volflag (line 1701) | def _read_volflag(
    method reload (line 1759) | def reload(self, verbosity: int = 9) -> None:
    method _reload (line 1769) | def _reload(self, verbosity: int = 9) -> None:
    method setup_auth_ord (line 3307) | def setup_auth_ord(self) -> None:
    method load_idp_db (line 3319) | def load_idp_db(self, quiet=False) -> None:
    method load_sessions (line 3358) | def load_sessions(self, quiet=False) -> None:
    method forget_session (line 3404) | def forget_session(self, broker: Optional["BrokerCli"], uname: str) ->...
    method _forget_session (line 3411) | def _forget_session(self, uname: str) -> None:
    method chpw (line 3427) | def chpw(self, broker: Optional["BrokerCli"], uname, pw) -> tuple[bool...
    method setup_chpw (line 3476) | def setup_chpw(self, acct: dict[str, str]) -> None:
    method setup_pwhash (line 3534) | def setup_pwhash(self, acct: dict[str, str]) -> None:
    method chk_sqlite_threadsafe (line 3582) | def chk_sqlite_threadsafe(self) -> str:
    method dbg_ls (line 3599) | def dbg_ls(self) -> None:
    method cgen (line 3722) | def cgen(self) -> None:
  function derive_args (line 3913) | def derive_args(args: argparse.Namespace) -> None:
  function n_du_who (line 3918) | def n_du_who(s: str) -> int:
  function n_ver_who (line 3932) | def n_ver_who(s: str) -> int:
  function split_cfg_ln (line 3942) | def split_cfg_ln(ln: str) -> dict[str, Any]:
  function expand_config_file (line 3964) | def expand_config_file(
  function upgrade_cfg_fmt (line 4047) | def upgrade_cfg_fmt(

FILE: copyparty/bos/bos.py
  function chmod (line 26) | def chmod(p: str, mode: int) -> None:
  function chown (line 30) | def chown(p: str, uid: int, gid: int) -> None:
  function listdir (line 34) | def listdir(p: str = ".") -> list[str]:
  function makedirs (line 38) | def makedirs(name: str, vf: dict[str, Any] = MKD_755, exist_ok: bool = T...
  function mkdir (line 65) | def mkdir(p: str, mode: int = 0o755) -> None:
  function open (line 69) | def open(p: str, *a, **ka) -> int:
  function readlink (line 73) | def readlink(p: str) -> str:
  function rename (line 77) | def rename(src: str, dst: str) -> None:
  function replace (line 81) | def replace(src: str, dst: str) -> None:
  function rmdir (line 85) | def rmdir(p: str) -> None:
  function stat (line 89) | def stat(p: str) -> os.stat_result:
  function unlink (line 93) | def unlink(p: str) -> None:
  function utime (line 97) | def utime(
  function utime_c (line 106) | def utime_c(
  function lstat (line 146) | def lstat(p: str) -> os.stat_result:

FILE: copyparty/bos/path.py
  function abspath (line 9) | def abspath(p: str) -> str:
  function exists (line 13) | def exists(p: str) -> bool:
  function getmtime (line 17) | def getmtime(p: str, follow_symlinks: bool = True) -> float:
  function getsize (line 24) | def getsize(p: str) -> int:
  function isfile (line 28) | def isfile(p: str) -> bool:
  function isdir (line 32) | def isdir(p: str) -> bool:
  function islink (line 36) | def islink(p: str) -> bool:
  function lexists (line 40) | def lexists(p: str) -> bool:
  function realpath (line 44) | def realpath(p: str) -> str:

FILE: copyparty/broker_mp.py
  class MProcess (line 22) | class MProcess(mp.Process):
    method __init__ (line 23) | def __init__(
  class BrokerMp (line 35) | class BrokerMp(object):
    method __init__ (line 38) | def __init__(self, hub: "SvcHub") -> None:
    method shutdown (line 62) | def shutdown(self) -> None:
    method reload (line 79) | def reload(self) -> None:
    method reload_sessions (line 84) | def reload_sessions(self) -> None:
    method collector (line 88) | def collector(self, proc: MProcess) -> None:
    method ask (line 118) | def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQ...
    method wask (line 130) | def wask(self, dest: str, *args: Any) -> list[Union[ExceptionalQueue, ...
    method say (line 143) | def say(self, dest: str, *args: Any) -> None:
    method periodic (line 167) | def periodic(self) -> None:

FILE: copyparty/broker_mpw.py
  class MpWorker (line 25) | class MpWorker(BrokerCli):
    method __init__ (line 28) | def __init__(
    method signal_handler (line 70) | def signal_handler(self, sig: Optional[int], frame: Optional[FrameType...
    method _log_enabled (line 74) | def _log_enabled(self, src: str, msg: str, c: Union[int, str] = 0) -> ...
    method _log_disabled (line 77) | def _log_disabled(self, src: str, msg: str, c: Union[int, str] = 0) ->...
    method logw (line 80) | def logw(self, msg: str, c: Union[int, str] = 0) -> None:
    method main (line 83) | def main(self) -> None:
    method ask (line 121) | def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQ...
    method say (line 130) | def say(self, dest: str, *args: Any, retq_id=0) -> None:

FILE: copyparty/broker_thr.py
  class BrokerThr (line 19) | class BrokerThr(BrokerCli):
    method __init__ (line 22) | def __init__(self, hub: "SvcHub") -> None:
    method shutdown (line 39) | def shutdown(self) -> None:
    method noop (line 43) | def noop(self) -> None:
    method ask (line 46) | def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQ...
    method say (line 55) | def say(self, dest: str, *args: Any) -> None:

FILE: copyparty/broker_util.py
  class ExceptionalQueue (line 21) | class ExceptionalQueue(Queue, object):
    method get (line 22) | def get(self, block: bool = True, timeout: Optional[float] = None) -> ...
  class NotExQueue (line 35) | class NotExQueue(object):
    method __init__ (line 40) | def __init__(self, rv: Any) -> None:
    method get (line 43) | def get(self) -> Any:
  class BrokerCli (line 47) | class BrokerCli(object):
    method __init__ (line 59) | def __init__(self) -> None:
    method ask (line 62) | def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQ...
    method say (line 65) | def say(self, dest: str, *args: Any) -> None:
  function try_exec (line 69) | def try_exec(want_retval: Union[bool, int], func: Any, *args: list[Any])...

FILE: copyparty/cert.py
  function _sp_err (line 23) | def _sp_err(exe, what, rc, so, se, sin):
  function ensure_cert (line 36) | def ensure_cert(log: "RootLogger", args) -> None:
  function _read_crt (line 79) | def _read_crt(args, fn):
  function _gen_ca (line 101) | def _gen_ca(log: "RootLogger", args):
  function _gen_srv (line 143) | def _gen_srv(log: "RootLogger", args, netdevs: dict[str, Netdev]):
  function gencert (line 249) | def gencert(log: "RootLogger", args, netdevs: dict[str, Netdev]):

FILE: copyparty/cfg.py
  function vf_bmap (line 12) | def vf_bmap() -> dict[str, str]:
  function vf_vmap (line 85) | def vf_vmap() -> dict[str, str]:
  function vf_cmap (line 170) | def vf_cmap() -> dict[str, str]:

FILE: copyparty/dxml.py
  class BadXML (line 14) | class BadXML(Exception):
  function get_ET (line 18) | def get_ET() -> ET.XMLParser:
  class _DXMLParser (line 44) | class _DXMLParser(XMLParser):  # type: ignore
    method __init__ (line 45) | def __init__(self) -> None:
    method nope (line 55) | def nope(self, *a: Any, **ka: Any) -> None:
  class _NG (line 59) | class _NG(XMLParser):  # type: ignore
    method __int__ (line 60) | def __int__(self) -> None:
  function parse_xml (line 67) | def parse_xml(txt: str) -> ET.Element:
  function selftest (line 76) | def selftest() -> bool:
  function mktnod (line 110) | def mktnod(name: str, text: str) -> ET.Element:
  function mkenod (line 116) | def mkenod(name: str, sub_el: Optional[ET.Element] = None) -> ET.Element:

FILE: copyparty/fsutil.py
  class Fstab (line 21) | class Fstab(object):
    method __init__ (line 22) | def __init__(self, log: "RootLogger", args: argparse.Namespace, verbos...
    method log (line 35) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method get (line 40) | def get(self, path: str) -> tuple[str, str]:
    method _winpath (line 76) | def _winpath(self, path: str) -> str:
    method build_fallback (line 85) | def build_fallback(self) -> None:
    method _from_sp_mount (line 89) | def _from_sp_mount(self) -> dict[str, str]:
    method _from_proc (line 107) | def _from_proc(self) -> dict[str, str]:
    method build_tab (line 119) | def build_tab(self) -> None:
    method relabel (line 160) | def relabel(self, path: str, nval: str) -> None:
    method get_unix (line 187) | def get_unix(self, path: str) -> tuple[str, str]:
    method get_w32 (line 207) | def get_w32(self, path: str) -> tuple[str, str]:
  function ramdisk_chk (line 221) | def ramdisk_chk(asrv: AuthSrv) -> None:

FILE: copyparty/ftpd.py
  class FSE (line 49) | class FSE(FilesystemError):
    method __init__ (line 50) | def __init__(self, msg: str, severity: int = 0) -> None:
  class FtpAuth (line 55) | class FtpAuth(DummyAuthorizer):
    method __init__ (line 56) | def __init__(self, hub: "SvcHub") -> None:
    method validate_authentication (line 60) | def validate_authentication(
    method get_home_dir (line 121) | def get_home_dir(self, username: str) -> str:
    method has_user (line 124) | def has_user(self, username: str) -> bool:
    method has_perm (line 128) | def has_perm(self, username: str, perm: int, path: Optional[str] = Non...
    method get_perms (line 131) | def get_perms(self, username: str) -> str:
    method get_msg_login (line 134) | def get_msg_login(self, username: str) -> str:
    method get_msg_quit (line 137) | def get_msg_quit(self, username: str) -> str:
  class FtpFs (line 141) | class FtpFs(AbstractedFS):
    method __init__ (line 142) | def __init__(
    method log (line 157) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method v2a (line 160) | def v2a(
    method rv2a (line 213) | def rv2a(
    method ftp2fs (line 223) | def ftp2fs(self, ftppath: str) -> str:
    method fs2ftp (line 227) | def fs2ftp(self, fspath: str) -> str:
    method validpath (line 231) | def validpath(self, path: str) -> bool:
    method open (line 238) | def open(self, filename: str, mode: str) -> typing.IO[Any]:
    method chdir (line 309) | def chdir(self, path: str) -> None:
    method mkdir (line 331) | def mkdir(self, path: str) -> None:
    method listdir (line 335) | def listdir(self, path: str) -> list[str]:
    method rmdir (line 381) | def rmdir(self, path: str) -> None:
    method remove (line 389) | def remove(self, path: str) -> None:
    method rename (line 399) | def rename(self, src: str, dst: str) -> None:
    method chmod (line 410) | def chmod(self, path: str, mode: str) -> None:
    method stat (line 413) | def stat(self, path: str) -> os.stat_result:
    method utime (line 428) | def utime(self, path: str, timeval: float) -> None:
    method lstat (line 432) | def lstat(self, path: str) -> os.stat_result:
    method isfile (line 436) | def isfile(self, path: str) -> bool:
    method islink (line 446) | def islink(self, path: str) -> bool:
    method isdir (line 450) | def isdir(self, path: str) -> bool:
    method getsize (line 460) | def getsize(self, path: str) -> int:
    method getmtime (line 464) | def getmtime(self, path: str) -> float:
    method realpath (line 468) | def realpath(self, path: str) -> str:
    method lexists (line 471) | def lexists(self, path: str) -> bool:
    method get_user_by_uid (line 475) | def get_user_by_uid(self, uid: int) -> str:
    method get_group_by_uid (line 478) | def get_group_by_uid(self, gid: int) -> str:
  class FtpHandler (line 482) | class FtpHandler(FTPHandler):
    method __init__ (line 488) | def __init__(self, conn: Any, server: Any, ioloop: Any = None) -> None:
    method ftp_STOR (line 516) | def ftp_STOR(self, file: str, mode: str = "w") -> Any:
    method log_transfer (line 556) | def log_transfer(
  class SftpHandler (line 592) | class SftpHandler(FtpHandler, TLS_FTPHandler):
  class Ftpd (line 599) | class Ftpd(object):
    method __init__ (line 600) | def __init__(self, hub: "SvcHub") -> None:
  function join (line 672) | def join(p1: str, p2: str) -> str:

FILE: copyparty/httpcli.py
  function _build_zip_xcode (line 195) | def _build_zip_xcode() -> Sequence[str]:
  function _arg2cfg (line 207) | def _arg2cfg(txt: str) -> str:
  class HttpCli (line 211) | class HttpCli(object):
    method __init__ (line 216) | def __init__(self, conn: "HttpConn") -> None:
    method log (line 284) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method unpwd (line 294) | def unpwd(self, m: Match[str]) -> str:
    method _assert_safe_rem (line 299) | def _assert_safe_rem(self, rem: str) -> None:
    method _gen_fk (line 305) | def _gen_fk(self, alg: int, salt: str, fspath: str, fsize: int, inode:...
    method j2s (line 310) | def j2s(self, name: str, **ka: Any) -> str:
    method j2j (line 331) | def j2j(self, name: str) -> jinja2.Template:
    method run (line 334) | def run(self) -> bool:
    method dip (line 960) | def dip(self) -> str:
    method cbonk (line 966) | def cbonk(self, g: Garda, v: str, reason: str, descr: str) -> bool:
    method is_banned (line 1012) | def is_banned(self) -> bool:
    method permit_caching (line 1031) | def permit_caching(self) -> None:
    method k304 (line 1040) | def k304(self) -> bool:
    method no304 (line 1044) | def no304(self) -> bool:
    method _build_html_head (line 1048) | def _build_html_head(self, kv: dict[str, Any]) -> None:
    method send_headers (line 1071) | def send_headers(
    method reply (line 1139) | def reply(
    method loud_reply (line 1200) | def loud_reply(self, body: str, *args: Any, **kwargs: Any) -> None:
    method terse_reply (line 1207) | def terse_reply(self, body: bytes, status: int = 200) -> None:
    method urlq (line 1224) | def urlq(self, add: dict[str, str], rm: list[str]) -> str:
    method ourlq (line 1249) | def ourlq(self) -> str:
    method redirect (line 1265) | def redirect(
    method _cors (line 1292) | def _cors(self) -> bool:
    method handle_get (line 1356) | def handle_get(self) -> bool:
    method tx_rss (line 1528) | def tx_rss(self) -> bool:
    method tx_zls (line 1685) | def tx_zls(self, abspath) -> bool:
    method tx_zget (line 1702) | def tx_zget(self, abspath) -> bool:
    method handle_propfind (line 1746) | def handle_propfind(self) -> bool:
    method handle_proppatch (line 1992) | def handle_proppatch(self) -> bool:
    method handle_lock (line 2051) | def handle_lock(self) -> bool:
    method handle_unlock (line 2120) | def handle_unlock(self) -> bool:
    method handle_mkcol (line 2136) | def handle_mkcol(self) -> bool:
    method handle_cpmv (line 2160) | def handle_cpmv(self) -> bool:
    method _applesan (line 2191) | def _applesan(self) -> bool:
    method send_chunk (line 2204) | def send_chunk(self, txt: str, enc: str, bmax: int) -> str:
    method handle_options (line 2220) | def handle_options(self) -> bool:
    method handle_delete (line 2238) | def handle_delete(self) -> bool:
    method handle_put (line 2244) | def handle_put(self) -> bool:
    method handle_post (line 2264) | def handle_post(self) -> bool:
    method handle_smsg (line 2379) | def handle_smsg(self) -> bool:
    method get_xml_enc (line 2410) | def get_xml_enc(self, txt: str) -> str:
    method get_body_reader (line 2425) | def get_body_reader(self) -> tuple[Generator[bytes, None, None], int]:
    method dump_to_file (line 2439) | def dump_to_file(self, is_put: bool) -> tuple[int, str, str, str, int,...
    method handle_stash (line 2797) | def handle_stash(self, is_put: bool) -> bool:
    method bakflip (line 2831) | def bakflip(
    method _spd (line 2884) | def _spd(self, nbytes: int, add: bool = True) -> str:
    method handle_post_multipart (line 2892) | def handle_post_multipart(self) -> bool:
    method handle_zip_post (line 2934) | def handle_zip_post(self) -> bool:
    method handle_post_json (line 2959) | def handle_post_json(self) -> bool:
    method handle_search (line 3076) | def handle_search(self, body: dict[str, Any]) -> bool:
    method handle_post_binary (line 3140) | def handle_post_binary(self) -> bool:
    method handle_chpw (line 3357) | def handle_chpw(self) -> bool:
    method handle_login (line 3379) | def handle_login(self) -> bool:
    method handle_logout (line 3420) | def handle_logout(self) -> bool:
    method get_pwd_cookie (line 3435) | def get_pwd_cookie(self, pwd: str) -> tuple[bool, str]:
    method set_idp_cookie (line 3488) | def set_idp_cookie(self, ases) -> None:
    method handle_mkdir (line 3501) | def handle_mkdir(self) -> bool:
    method _mkdir (line 3508) | def _mkdir(self, vpath: str, dav: bool = False) -> bool:
    method handle_new_md (line 3547) | def handle_new_md(self) -> bool:
    method upload_flags (line 3621) | def upload_flags(self, vfs: VFS) -> tuple[int, int, list[str], list[st...
    method handle_plain_upload (line 3643) | def handle_plain_upload(
    method handle_text_upload (line 4052) | def handle_text_upload(self) -> bool:
    method _chk_lastmod (line 4265) | def _chk_lastmod(self, file_ts: int) -> tuple[str, bool, bool]:
    method _use_dirkey (line 4289) | def _use_dirkey(self, vn: VFS, ap: str) -> bool:
    method _use_filekey (line 4315) | def _use_filekey(self, vn: VFS, ap: str, st: os.stat_result) -> bool:
    method _add_logues (line 4343) | def _add_logues(
    method _expand (line 4380) | def _expand(self, txt: str, phs: list[str]) -> str:
    method _can_tail (line 4404) | def _can_tail(self, volflags: dict[str, Any]) -> bool:
    method _can_zip (line 4418) | def _can_zip(self, volflags: dict[str, Any]) -> str:
    method tx_res (line 4431) | def tx_res(self, req_path: str) -> bool:
    method tx_file (line 4567) | def tx_file(self, oh_k: str, req_path: str, ptop: Optional[str] = None...
    method tx_tail (line 4838) | def tx_tail(
    method tx_pipe (line 4972) | def tx_pipe(
    method tx_zip (line 5125) | def tx_zip(
    method tx_ico (line 5260) | def tx_ico(self, ext: str, exact: bool = False) -> bool:
    method tx_svg (line 5284) | def tx_svg(self, txt: str, small: bool = False) -> bool:
    method tx_qr (line 5294) | def tx_qr(self):
    method tx_md (line 5321) | def tx_md(self, vn: VFS, fs_path: str) -> bool:
    method tx_svcs (line 5436) | def tx_svcs(self) -> bool:
    method tx_mounts (line 5489) | def tx_mounts(self) -> bool:
    method setck (line 5659) | def setck(self) -> bool:
    method set_cfg_reset (line 5673) | def set_cfg_reset(self) -> bool:
    method tx_404 (line 5683) | def tx_404(self, is_403: bool = False) -> bool:
    method on40x (line 5734) | def on40x(self, mods: list[str], vn: VFS, rem: str) -> str:
    method scanvol (line 5748) | def scanvol(self) -> bool:
    method handle_reload (line 5776) | def handle_reload(self) -> bool:
    method tx_stack (line 5790) | def tx_stack(self) -> bool:
    method tx_tree (line 5808) | def tx_tree(self) -> bool:
    method gen_tree (line 5834) | def gen_tree(self, top: str, target: str, dk: str) -> dict[str, Any]:
    method get_dls (line 5908) | def get_dls(self) -> list[list[Any]]:
    method tx_dls (line 5936) | def tx_dls(self) -> bool:
    method tx_ups (line 5953) | def tx_ups(self) -> bool:
    method tx_rups (line 6120) | def tx_rups(self) -> bool:
    method tx_idp (line 6259) | def tx_idp(self) -> bool:
    method tx_shares (line 6285) | def tx_shares(self) -> bool:
    method handle_eshare (line 6333) | def handle_eshare(self) -> bool:
    method handle_share (line 6383) | def handle_share(self, req: dict[str, str]) -> bool:
    method handle_rm (line 6505) | def handle_rm(self, req: list[str]) -> bool:
    method handle_mv (line 6536) | def handle_mv(self) -> bool:
    method _mv (line 6548) | def _mv(self, vsrc: str, vdst: str, overwrite: bool) -> bool:
    method handle_cp (line 6569) | def handle_cp(self) -> bool:
    method _cp (line 6581) | def _cp(self, vsrc: str, vdst: str, overwrite: bool) -> bool:
    method handle_fs_abrt (line 6601) | def handle_fs_abrt(self):
    method tx_ls_vols (line 6610) | def tx_ls_vols(self) -> bool:
    method tx_ls (line 6653) | def tx_ls(self, ls: dict[str, Any]) -> bool:
    method tx_browser (line 6725) | def tx_browser(self) -> bool:

FILE: copyparty/httpconn.py
  class HttpConn (line 41) | class HttpConn(object):
    method __init__ (line 47) | def __init__(
    method shutdown (line 89) | def shutdown(self) -> None:
    method set_rproxy (line 96) | def set_rproxy(self, ip: Optional[str] = None) -> str:
    method log (line 109) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method get_u2idx (line 112) | def get_u2idx(self) -> Optional[U2idx]:
    method _detect_https (line 121) | def _detect_https(self) -> bool:
    method run (line 149) | def run(self) -> None:

FILE: copyparty/httpsrv.py
  function load_jinja2_resource (line 99) | def load_jinja2_resource(E: EnvParams, name: str):
  class HttpSrv (line 104) | class HttpSrv(object):
    method __init__ (line 110) | def __init__(self, broker: "BrokerCli", nid: Optional[int]) -> None:
    method post_init (line 236) | def post_init(self) -> None:
    method set_bad_ver (line 243) | def set_bad_ver(self) -> None:
    method set_netdevs (line 246) | def set_netdevs(self, netdevs: dict[str, Netdev]) -> None:
    method start_threads (line 253) | def start_threads(self, n: int) -> None:
    method stop_threads (line 261) | def stop_threads(self, n: int) -> None:
    method periodic (line 270) | def periodic(self) -> None:
    method listen (line 284) | def listen(self, sck: socket.socket, nlisteners: int) -> None:
    method thr_listen (line 312) | def thr_listen(self, srv_sck: socket.socket) -> None:
    method accept (line 437) | def accept(self, sck: socket.socket, addr: tuple[str, int]) -> None:
    method thr_poolw (line 475) | def thr_poolw(self) -> None:
    method shutdown (line 497) | def shutdown(self) -> None:
    method thr_client (line 524) | def thr_client(self, sck: socket.socket, addr: tuple[str, int]) -> None:
    method cachebuster (line 571) | def cachebuster(self) -> str:
    method get_u2idx (line 593) | def get_u2idx(self, ident: str) -> Optional[U2idx]:
    method put_u2idx (line 614) | def put_u2idx(self, ident: str, u2idx: U2idx) -> None:
    method read_dls (line 621) | def read_dls(
    method write_dls (line 633) | def write_dls(

FILE: copyparty/ico.py
  class Ico (line 14) | class Ico(object):
    method __init__ (line 15) | def __init__(self, args: argparse.Namespace) -> None:
    method get (line 18) | def get(self, ext: str, as_thumb: bool, chrome: bool) -> tuple[str, by...

FILE: copyparty/mdns.py
  class MDNS_Sck (line 79) | class MDNS_Sck(MC_Sck):
    method __init__ (line 80) | def __init__(
  class MDNS (line 99) | class MDNS(MCast):
    method __init__ (line 100) | def __init__(self, hub: "SvcHub", ngen: int) -> None:
    method log (line 135) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method build_svcs (line 138) | def build_svcs(self) -> tuple[dict[str, dict[str, Any]], set[str]]:
    method build_replies (line 197) | def build_replies(self) -> None:
    method send_probes (line 284) | def send_probes(self) -> None:
    method run (line 310) | def run(self) -> None:
    method run2 (line 338) | def run2(self) -> None:
    method stop (line 402) | def stop(self, panic=False) -> None:
    method eat (line 415) | def eat(self, buf: bytes, addr: tuple[str, int], sck: socket.socket) -...
    method process (line 578) | def process(self) -> None:
    method _tx (line 609) | def _tx(self, srv: MDNS_Sck, msg: bytes, cooldown: float) -> bool:

FILE: copyparty/metrics.py
  class Metrics (line 15) | class Metrics(object):
    method __init__ (line 16) | def __init__(self, hsrv: "HttpSrv") -> None:
    method tx (line 19) | def tx(self, cli: "HttpCli") -> bool:

FILE: copyparty/mtag.py
  function have_ff (line 48) | def have_ff(scmd: str) -> bool:
  class MParser (line 74) | class MParser(object):
    method __init__ (line 75) | def __init__(self, cmdline: str) -> None:
  function au_unpk (line 133) | def au_unpk(
  function ffprobe (line 216) | def ffprobe(
  function parse_ffprobe (line 232) | def parse_ffprobe(
  function get_cover_from_epub (line 387) | def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO...
  function _get_cover_from_epub2 (line 444) | def _get_cover_from_epub2(
  class MTag (line 463) | class MTag(object):
    method __init__ (line 464) | def __init__(self, log_func: "RootLogger", args: argparse.Namespace) -...
    method log (line 581) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method normalize_tags (line 584) | def normalize_tags(
    method compare (line 623) | def compare(self, abspath: str) -> dict[str, Union[str, float]]:
    method _get_xattr (line 661) | def _get_xattr(
    method _get_main (line 688) | def _get_main(
    method get_mutagen (line 704) | def get_mutagen(self, abspath: str) -> dict[str, Union[str, float]]:
    method get_ffprobe (line 766) | def get_ffprobe(self, abspath: str) -> dict[str, Union[str, float]]:
    method get_bin (line 779) | def get_bin(

FILE: copyparty/multicast.py
  class NoIPs (line 30) | class NoIPs(Exception):
  class MC_Sck (line 34) | class MC_Sck(object):
    method __init__ (line 37) | def __init__(
  class MCast (line 58) | class MCast(object):
    method __init__ (line 59) | def __init__(
    method log (line 93) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method create_servers (line 96) | def create_servers(self) -> list[str]:
    method setup_socket (line 235) | def setup_socket(self, srv: MC_Sck) -> None:
    method hop (line 312) | def hop(self, srv: MC_Sck, on: bool) -> bool:
    method hopper (line 339) | def hopper(self):
    method map_client (line 356) | def map_client(self, cip: str) -> Optional[MC_Sck]:

FILE: copyparty/pwhash.py
  class PWHash (line 23) | class PWHash(object):
    method __init__ (line 24) | def __init__(self, args: argparse.Namespace):
    method _cache_hash (line 56) | def _cache_hash(self, plain: str) -> str:
    method _gen_sha2 (line 76) | def _gen_sha2(self, plain: str) -> str:
    method _gen_scrypt (line 85) | def _gen_scrypt(self, plain: str) -> str:
    method _gen_argon2 (line 110) | def _gen_argon2(self, plain: str) -> str:
    method stdin (line 141) | def stdin(self) -> None:
    method cli (line 148) | def cli(self) -> None:

FILE: copyparty/qrkode.py
  function _qrgen (line 29) | def _qrgen(data: Union[bytes, Sequence[int]]) -> "QrCode":
  function qr2txt (line 43) | def qr2txt(qr: QrCode, zoom: int = 1, pad: int = 4) -> str:
  function qr2png (line 71) | def qr2png(
  function qr2svg (line 98) | def qr2svg(qr: QrCode, border: int) -> str:

FILE: copyparty/sftpd.py
  class SSH_Srv (line 54) | class SSH_Srv(paramiko.ServerInterface):
    method __init__ (line 55) | def __init__(self, hub: "SvcHub", addr: Any):
    method log (line 75) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method get_allowed_auths (line 78) | def get_allowed_auths(self, username: str) -> str:
    method get_banner (line 81) | def get_banner(self) -> tuple[Optional[str], Optional[str]]:
    method check_channel_request (line 93) | def check_channel_request(self, kind: str, chanid: int) -> int:
    method check_auth_none (line 100) | def check_auth_none(self, username: str) -> int:
    method _check_auth_none (line 107) | def _check_auth_none(self, uname: str) -> int:
    method check_auth_password (line 127) | def check_auth_password(self, username: str, password: str) -> int:
    method _check_auth_password (line 134) | def _check_auth_password(self, uname: str, pw: str) -> int:
    method check_auth_publickey (line 205) | def check_auth_publickey(self, username: str, key: paramiko.PKey) -> int:
    method _check_auth_publickey (line 212) | def _check_auth_publickey(self, uname: str, key: paramiko.PKey) -> int:
  class SFTP_FH (line 253) | class SFTP_FH(paramiko.SFTPHandle):
    method __init__ (line 254) | def __init__(self, flags: int = 0) -> None:
    method stat (line 260) | def stat(self):
    method chattr (line 267) | def chattr(self, attr):
  class SFTP_Srv (line 279) | class SFTP_Srv(paramiko.SFTPServerInterface):
    method __init__ (line 280) | def __init__(self, ssh: paramiko.ServerInterface, *a, **ka):
    method log (line 308) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method v2a (line 311) | def v2a(
    method list_folder (line 360) | def list_folder(self, path: str) -> list[SATTR] | int:
    method _list_folder (line 372) | def _list_folder(self, path: str) -> list[SATTR] | int:
    method stat (line 423) | def stat(self, path: str) -> SATTR | int:
    method lstat (line 430) | def lstat(self, path: str) -> SATTR | int:
    method _stat (line 437) | def _stat(self, vp: str) -> SATTR | int:
    method open (line 459) | def open(self, path: str, flags: int, attr: SATTR) -> paramiko.SFTPHan...
    method _open (line 466) | def _open(self, vp: str, iflag: int, attr: SATTR) -> paramiko.SFTPHand...
    method remove (line 586) | def remove(self, path: str) -> int:
    method _remove (line 593) | def _remove(self, vp: str) -> int:
    method rename (line 627) | def rename(self, oldpath: str, newpath: str) -> int:
    method _rename (line 634) | def _rename(self, svp: str, dvp: str) -> int:
    method mkdir (line 651) | def mkdir(self, path: str, attr: SATTR) -> int:
    method _mkdir (line 658) | def _mkdir(self, vp: str, attr: SATTR) -> int:
    method rmdir (line 675) | def rmdir(self, path: str) -> int:
    method _rmdir (line 682) | def _rmdir(self, vp: str) -> int:
    method chattr (line 697) | def chattr(self, path: str, attr: SATTR) -> int:
    method _chattr (line 704) | def _chattr(self, vp: str, attr: SATTR) -> int:
    method symlink (line 719) | def symlink(self, target_path: str, path: str) -> int:
    method readlink (line 722) | def readlink(self, path: str) -> str | int:
    method canonicalize (line 725) | def canonicalize(self, path: str) -> str:
  class Sftpd (line 729) | class Sftpd(object):
    method __init__ (line 730) | def __init__(self, hub: "SvcHub") -> None:
    method log (line 786) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method _bind (line 789) | def _bind(self, ip: str) -> None:
    method _accept (line 817) | def _accept(self, srv: socket.socket) -> None:
    method _accept2 (line 825) | def _accept2(self, cli, addr) -> None:
    method run (line 848) | def run(self):
    method _run_select (line 858) | def _run_select(self):
    method _run_poll (line 864) | def _run_poll(self):

FILE: copyparty/smbd.py
  class SMB (line 28) | class SMB(object):
    method __init__ (line 29) | def __init__(self, hub: "SvcHub") -> None:
    method nlog (line 126) | def nlog(self, msg: str, c: Union[int, str] = 0) -> None:
    method start (line 129) | def start(self) -> None:
    method _auth_cb (line 132) | def _auth_cb(self, *a, **ka):
    method _uname (line 151) | def _uname(self) -> str:
    method _v2a (line 178) | def _v2a(
    method _listdir (line 196) | def _listdir(self, vpath: str, *a: Any, **ka: Any) -> list[str]:
    method _open (line 229) | def _open(
    method _close (line 288) | def _close(self, fd: int) -> None:
    method _rename (line 308) | def _rename(self, vp1: str, vp2: str) -> None:
    method _mkdir (line 332) | def _mkdir(self, vpath: str) -> None:
    method _stat (line 344) | def _stat(self, vpath: str, *a: Any, **ka: Any) -> os.stat_result:
    method _unlink (line 356) | def _unlink(self, vpath: str) -> None:
    method _utime (line 371) | def _utime(self, vpath: str, times: tuple[float, float]) -> None:
    method _p_exists (line 383) | def _p_exists(self, vpath: str) -> bool:
    method _p_getsize (line 394) | def _p_getsize(self, vpath: str) -> int:
    method _p_isdir (line 398) | def _p_isdir(self, vpath: str) -> bool:
    method _p_join (line 407) | def _p_join(self, *a) -> str:
    method _hook (line 413) | def _hook(self, *a: Any, **ka: Any) -> None:
    method _disarm (line 418) | def _disarm(self) -> None:
    method _is_in_file_jail (line 444) | def _is_in_file_jail(self, *a: Any) -> bool:
  function yeet (line 449) | def yeet(msg: str) -> None:

FILE: copyparty/ssdp.py
  class SSDP_Sck (line 26) | class SSDP_Sck(MC_Sck):
    method __init__ (line 27) | def __init__(self, *a):
  class SSDPr (line 32) | class SSDPr(object):
    method __init__ (line 35) | def __init__(self, broker: "BrokerCli") -> None:
    method reply (line 39) | def reply(self, hc: "HttpCli") -> bool:
    method tx_device (line 46) | def tx_device(self, hc: "HttpCli") -> bool:
  class SSDPd (line 90) | class SSDPd(MCast):
    method __init__ (line 93) | def __init__(self, hub: "SvcHub", ngen: int) -> None:
    method log (line 107) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method run (line 110) | def run(self) -> None:
    method run2 (line 143) | def run2(self) -> None:
    method stop (line 183) | def stop(self) -> None:
    method eat (line 193) | def eat(self, buf: bytes, addr: tuple[str, int]) -> None:

FILE: copyparty/star.py
  class QFile (line 21) | class QFile(object):  # inherit io.StringIO for painful typing
    method __init__ (line 24) | def __init__(self) -> None:
    method write (line 29) | def write(self, buf: Optional[bytes]) -> None:
  class StreamTar (line 42) | class StreamTar(StreamArc):
    method __init__ (line 45) | def __init__(
    method gen (line 93) | def gen(self) -> Generator[Optional[bytes], None, None]:
    method ser (line 115) | def ser(self, f: dict[str, Any]) -> None:
    method _gen (line 134) | def _gen(self) -> None:

FILE: copyparty/stolen/dnslib/bimap.py
  class BimapError (line 6) | class BimapError(Exception):
  class Bimap (line 10) | class Bimap(object):
    method __init__ (line 11) | def __init__(self, name, forward, error=AttributeError):
    method get (line 17) | def get(self, k, default=None):
    method __getitem__ (line 23) | def __getitem__(self, k):
    method __getattr__ (line 32) | def __getattr__(self, k):

FILE: copyparty/stolen/dnslib/bit.py
  function get_bits (line 6) | def get_bits(data, offset, bits=1):
  function set_bits (line 11) | def set_bits(data, value, offset, bits=1):

FILE: copyparty/stolen/dnslib/buffer.py
  class BufferError (line 7) | class BufferError(Exception):
  class Buffer (line 11) | class Buffer(object):
    method __init__ (line 12) | def __init__(self, data=b""):
    method remaining (line 16) | def remaining(self):
    method get (line 19) | def get(self, length):
    method hex (line 30) | def hex(self):
    method pack (line 33) | def pack(self, fmt, *args):
    method append (line 37) | def append(self, s):
    method update (line 41) | def update(self, ptr, fmt, *args):
    method unpack (line 45) | def unpack(self, fmt):
    method __len__ (line 55) | def __len__(self):

FILE: copyparty/stolen/dnslib/dns.py
  class DNSError (line 21) | class DNSError(Exception):
  function unknown_qtype (line 25) | def unknown_qtype(name, key, forward):
  function label (line 73) | def label(label, origin=None):
  class DNSRecord (line 80) | class DNSRecord(object):
    method parse (line 82) | def parse(cls, packet) -> "DNSRecord":
    method question (line 105) | def question(cls, qname, qtype="A", qclass="IN"):
    method __init__ (line 110) | def __init__(
    method reply (line 125) | def reply(self, ra=1, aa=1):
    method add_question (line 131) | def add_question(self, *q) -> None:
    method add_answer (line 135) | def add_answer(self, *rr) -> None:
    method add_auth (line 139) | def add_auth(self, *auth) -> None:
    method add_ar (line 143) | def add_ar(self, *ar) -> None:
    method set_header_qa (line 147) | def set_header_qa(self) -> None:
    method get_q (line 153) | def get_q(self):
    method get_a (line 158) | def get_a(self):
    method pack (line 163) | def pack(self) -> bytes:
    method truncate (line 177) | def truncate(self):
    method format (line 180) | def format(self, prefix="", sort=False):
    method __repr__ (line 191) | def __repr__(self):
  class DNSHeader (line 197) | class DNSHeader(object):
    method parse (line 206) | def parse(cls, buffer):
    method __init__ (line 215) | def __init__(self, id=None, bitmap=None, q=0, a=0, auth=0, ar=0, **arg...
    method get_qr (line 247) | def get_qr(self):
    method set_qr (line 250) | def set_qr(self, val):
    method get_opcode (line 255) | def get_opcode(self):
    method set_opcode (line 258) | def set_opcode(self, val):
    method get_aa (line 263) | def get_aa(self):
    method set_aa (line 266) | def set_aa(self, val):
    method get_tc (line 271) | def get_tc(self):
    method set_tc (line 274) | def set_tc(self, val):
    method get_rd (line 279) | def get_rd(self):
    method set_rd (line 282) | def set_rd(self, val):
    method get_ra (line 287) | def get_ra(self):
    method set_ra (line 290) | def set_ra(self, val):
    method get_z (line 295) | def get_z(self):
    method set_z (line 298) | def set_z(self, val):
    method get_ad (line 303) | def get_ad(self):
    method set_ad (line 306) | def set_ad(self, val):
    method get_cd (line 311) | def get_cd(self):
    method set_cd (line 314) | def set_cd(self, val):
    method get_rcode (line 319) | def get_rcode(self):
    method set_rcode (line 322) | def set_rcode(self, val):
    method pack (line 327) | def pack(self, buffer):
    method __repr__ (line 330) | def __repr__(self):
  class DNSQuestion (line 373) | class DNSQuestion(object):
    method parse (line 375) | def parse(cls, buffer):
    method __init__ (line 385) | def __init__(self, qname=None, qtype=1, qclass=1) -> None:
    method set_qname (line 390) | def set_qname(self, qname):
    method get_qname (line 396) | def get_qname(self):
    method pack (line 401) | def pack(self, buffer):
    method __repr__ (line 405) | def __repr__(self):
  class RR (line 415) | class RR(object):
    method parse (line 422) | def parse(cls, buffer):
    method __init__ (line 434) | def __init__(self, rname=None, rtype=1, rclass=1, ttl=0, rdata=None) -...
    method set_rname (line 441) | def set_rname(self, rname):
    method get_rname (line 447) | def get_rname(self):
    method pack (line 452) | def pack(self, buffer):
    method __repr__ (line 462) | def __repr__(self):
  class RD (line 474) | class RD(object):
    method parse (line 476) | def parse(cls, buffer, length):
    method __init__ (line 483) | def __init__(self, data=b"") -> None:
    method pack (line 487) | def pack(self, buffer):
    method __repr__ (line 490) | def __repr__(self):
  function _force_bytes (line 502) | def _force_bytes(x):
  class TXT (line 509) | class TXT(RD):
    method parse (line 511) | def parse(cls, buffer, length):
    method __init__ (line 530) | def __init__(self, data) -> None:
    method pack (line 538) | def pack(self, buffer):
    method __repr__ (line 545) | def __repr__(self):
  class A (line 549) | class A(RD):
    method parse (line 554) | def parse(cls, buffer, length):
    method __init__ (line 561) | def __init__(self, data) -> None:
    method pack (line 567) | def pack(self, buffer):
    method __repr__ (line 570) | def __repr__(self):
  function _parse_ipv6 (line 574) | def _parse_ipv6(a):
  function _format_ipv6 (line 582) | def _format_ipv6(a):
  class AAAA (line 607) | class AAAA(RD):
    method parse (line 611) | def parse(cls, buffer, length):
    method __init__ (line 618) | def __init__(self, data) -> None:
    method pack (line 624) | def pack(self, buffer):
    method __repr__ (line 627) | def __repr__(self):
  class CNAME (line 631) | class CNAME(RD):
    method parse (line 633) | def parse(cls, buffer, length):
    method __init__ (line 640) | def __init__(self, label=None) -> None:
    method set_label (line 643) | def set_label(self, label):
    method get_label (line 649) | def get_label(self):
    method pack (line 654) | def pack(self, buffer):
    method __repr__ (line 657) | def __repr__(self):
  class PTR (line 663) | class PTR(CNAME):
  class SRV (line 667) | class SRV(RD):
    method parse (line 673) | def parse(cls, buffer, length):
    method __init__ (line 681) | def __init__(self, priority=0, weight=0, port=0, target=None) -> None:
    method set_target (line 687) | def set_target(self, target):
    method get_target (line 693) | def get_target(self):
    method pack (line 698) | def pack(self, buffer):
    method __repr__ (line 702) | def __repr__(self):
  function decode_type_bitmap (line 708) | def decode_type_bitmap(type_bitmap):
  function encode_type_bitmap (line 722) | def encode_type_bitmap(rrlist):
  class NSEC (line 745) | class NSEC(RD):
    method parse (line 747) | def parse(cls, buffer, length):
    method __init__ (line 756) | def __init__(self, label, rrlist) -> None:
    method set_label (line 760) | def set_label(self, label):
    method get_label (line 766) | def get_label(self):
    method pack (line 771) | def pack(self, buffer):
    method __repr__ (line 775) | def __repr__(self):

FILE: copyparty/stolen/dnslib/label.py
  function set_avahi_379 (line 17) | def set_avahi_379():
  function log_avahi_379 (line 22) | def log_avahi_379(args):
  class DNSLabelError (line 31) | class DNSLabelError(Exception):
  class DNSLabel (line 35) | class DNSLabel(object):
    method __init__ (line 36) | def __init__(self, label):
    method add (line 55) | def add(self, name):
    method idna (line 61) | def idna(self):
    method _decode (line 64) | def _decode(self, s):
    method __str__ (line 72) | def __str__(self):
    method __repr__ (line 75) | def __repr__(self):
    method __hash__ (line 78) | def __hash__(self):
    method __ne__ (line 81) | def __ne__(self, other):
    method __eq__ (line 84) | def __eq__(self, other):
    method __len__ (line 90) | def __len__(self):
  class DNSBuffer (line 94) | class DNSBuffer(Buffer):
    method __init__ (line 95) | def __init__(self, data=b""):
    method decode_name (line 99) | def decode_name(self, last=-1):
    method encode_name (line 140) | def encode_name(self, name):
    method encode_name_nocompress (line 162) | def encode_name_nocompress(self, name):

FILE: copyparty/stolen/dnslib/lex.py
  class Lexer (line 13) | class Lexer(object):
    method __init__ (line 18) | def __init__(self, f, debug=False):
    method __iter__ (line 33) | def __iter__(self):
    method next_token (line 36) | def next_token(self):
    method parse (line 42) | def parse(self):
    method read (line 48) | def read(self, n=1):
    method peek (line 60) | def peek(self, n=1):
    method pushback (line 75) | def pushback(self, s):
    method readescaped (line 80) | def readescaped(self):
    method lexStart (line 104) | def lexStart(self):

FILE: copyparty/stolen/dnslib/ranges.py
  function check_instance (line 16) | def check_instance(name, val, types):
  function check_bytes (line 23) | def check_bytes(name, val):
  function range_property (line 27) | def range_property(attr, min, max):
  function B (line 42) | def B(attr):
  function H (line 46) | def H(attr):
  function I (line 50) | def I(attr):
  function ntuple_range (line 54) | def ntuple_range(attr, n, min, max):
  function IP4 (line 76) | def IP4(attr):
  function IP6 (line 80) | def IP6(attr):

FILE: copyparty/stolen/ifaddr/__init__.py
  function nope (line 16) | def nope(include_unconfigured=False):

FILE: copyparty/stolen/ifaddr/_posix.py
  class ifaddrs (line 18) | class ifaddrs(ctypes.Structure):
  function get_adapters (line 33) | def get_adapters(include_unconfigured: bool = False) -> Iterable[shared....

FILE: copyparty/stolen/ifaddr/_shared.py
  class Adapter (line 23) | class Adapter(object):
    method __init__ (line 34) | def __init__(
    method __repr__ (line 56) | def __repr__(self) -> str:
  class IP (line 73) | class IP(object):
    method __init__ (line 78) | def __init__(
    method is_IPv4 (line 101) | def is_IPv4(self) -> bool:
    method is_IPv6 (line 109) | def is_IPv6(self) -> bool:
    method __repr__ (line 116) | def __repr__(self) -> str:
  class sockaddr (line 131) | class sockaddr(ctypes.Structure):
  class sockaddr_in (line 138) | class sockaddr_in(ctypes.Structure):
  class sockaddr_in6 (line 147) | class sockaddr_in6(ctypes.Structure):
  class sockaddr (line 159) | class sockaddr(ctypes.Structure):  # type: ignore
  class sockaddr_in (line 162) | class sockaddr_in(ctypes.Structure):  # type: ignore
  class sockaddr_in6 (line 170) | class sockaddr_in6(ctypes.Structure):  # type: ignore
  function sockaddr_to_ip (line 180) | def sockaddr_to_ip(
  function ipv6_prefixlength (line 199) | def ipv6_prefixlength(address: ipaddress.IPv6Address) -> int:

FILE: copyparty/stolen/ifaddr/_win32.py
  class SOCKET_ADDRESS (line 20) | class SOCKET_ADDRESS(ctypes.Structure):
  class IP_ADAPTER_UNICAST_ADDRESS (line 27) | class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure):
  class IP_ADAPTER_ADDRESSES (line 46) | class IP_ADAPTER_ADDRESSES(ctypes.Structure):
  function enumerate_interfaces_of_adapter (line 68) | def enumerate_interfaces_of_adapter(
  function get_adapters (line 90) | def get_adapters(include_unconfigured: bool = False) -> Iterable[shared....

FILE: copyparty/stolen/qrcodegen.py
  function num_char_count_bits (line 25) | def num_char_count_bits(ver: int) -> int:
  class Ecc (line 29) | class Ecc(object):
    method __init__ (line 33) | def __init__(self, i: int, fb: int) -> None:
  class QrSegment (line 49) | class QrSegment(object):
    method make_seg (line 51) | def make_seg(data: Union[bytes, Sequence[int]]) -> "QrSegment":
    method __init__ (line 60) | def __init__(self, numch: int, bitdata: Sequence[int]) -> None:
    method get_total_bits (line 67) | def get_total_bits(segs: Sequence["QrSegment"], ver: int) -> Optional[...
  class QrCode (line 77) | class QrCode(object):
    method encode_binary (line 79) | def encode_binary(data: Union[bytes, Sequence[int]]) -> "QrCode":
    method encode_segments (line 83) | def encode_segments(
    method __init__ (line 141) | def __init__(
    method _draw_function_patterns (line 176) | def _draw_function_patterns(self) -> None:
    method _draw_format_bits (line 204) | def _draw_format_bits(self, mask: int) -> None:
    method _draw_ver (line 229) | def _draw_ver(self) -> None:
    method _draw_finder_pattern (line 248) | def _draw_finder_pattern(self, x: int, y: int) -> None:
    method _draw_alignment_pattern (line 258) | def _draw_alignment_pattern(self, x: int, y: int) -> None:
    method _set_function_module (line 263) | def _set_function_module(self, x: int, y: int, isdark: bool) -> None:
    method _add_ecc_and_interleave (line 267) | def _add_ecc_and_interleave(self, data: bytearray) -> bytes:
    method _draw_codewords (line 303) | def _draw_codewords(self, data: bytes) -> None:
    method _apply_mask (line 323) | def _apply_mask(self, mask: int) -> None:
    method _get_penalty_score (line 331) | def _get_penalty_score(self) -> int:
    method _get_alignment_pattern_positions (line 412) | def _get_alignment_pattern_positions(self) -> List[int]:
    method _get_num_raw_data_modules (line 429) | def _get_num_raw_data_modules(ver: int) -> int:
    method _get_num_data_codewords (line 440) | def _get_num_data_codewords(ver: int, ecl: Ecc) -> int:
    method _reed_solomon_compute_divisor (line 448) | def _reed_solomon_compute_divisor(degree: int) -> bytes:
    method _reed_solomon_compute_remainder (line 471) | def _reed_solomon_compute_remainder(data: bytes, divisor: bytes) -> by...
    method _reed_solomon_multiply (line 482) | def _reed_solomon_multiply(x: int, y: int) -> int:
    method _finder_penalty_count_patterns (line 492) | def _finder_penalty_count_patterns(self, runhistory: collections.deque...
    method _finder_penalty_terminate_and_count (line 504) | def _finder_penalty_terminate_and_count(
    method _finder_penalty_add_history (line 517) | def _finder_penalty_add_history(
  class _BitBuffer (line 556) | class _BitBuffer(list):  # type: ignore
    method append_bits (line 557) | def append_bits(self, val: int, n: int) -> None:
  function _get_bit (line 564) | def _get_bit(x: int, i: int) -> bool:
  class DataTooLongError (line 568) | class DataTooLongError(ValueError):

FILE: copyparty/stolen/surrogateescape.py
  function surrogateescape_handler (line 35) | def surrogateescape_handler(exc: Any) -> tuple[str, int]:
  class NotASurrogateError (line 62) | class NotASurrogateError(Exception):
  function replace_surrogate_encode (line 66) | def replace_surrogate_encode(mystring: str) -> str:
  function replace_surrogate_decode (line 92) | def replace_surrogate_decode(mybytes: bytes) -> str:
  function encodefilename (line 113) | def encodefilename(fn: str) -> bytes:
  function decodefilename (line 153) | def decodefilename(fn: bytes) -> str:
  function register_surrogateescape (line 170) | def register_surrogateescape() -> None:

FILE: copyparty/sutil.py
  class StreamArc (line 24) | class StreamArc(object):
    method __init__ (line 25) | def __init__(
    method gen (line 38) | def gen(self) -> Generator[Optional[bytes], None, None]:
    method stop (line 41) | def stop(self) -> None:
  function gfilter (line 45) | def gfilter(
  function enthumb (line 84) | def enthumb(
  function errdesc (line 109) | def errdesc(

FILE: copyparty/svchub.py
  class SvcHub (line 112) | class SvcHub(object):
    method __init__ (line 123) | def __init__(
    method _db_onfail_ses (line 489) | def _db_onfail_ses(self) -> None:
    method _db_onfail_idp (line 492) | def _db_onfail_idp(self) -> None:
    method setup_db (line 495) | def setup_db(self, which: str) -> None:
    method _create_session_db (line 588) | def _create_session_db(self, cur: "sqlite3.Cursor") -> int:
    method _create_idp_db (line 603) | def _create_idp_db(self, cur: "sqlite3.Cursor") -> int:
    method _verify_db (line 616) | def _verify_db(
    method setup_share_db (line 667) | def setup_share_db(self) -> None:
    method start_ftpd (line 772) | def start_ftpd(self) -> None:
    method restart_sftpd (line 784) | def restart_sftpd(self) -> None:
    method restart_ftpd (line 797) | def restart_ftpd(self) -> None:
    method restart_tftpd (line 812) | def restart_tftpd(self) -> None:
    method thr_httpsrv_up (line 823) | def thr_httpsrv_up(self) -> None:
    method sigterm (line 846) | def sigterm(self) -> None:
    method sticky_qr (line 849) | def sticky_qr(self) -> None:
    method _unsticky_qr (line 852) | def _unsticky_qr(self, flush=True) -> None:
    method _sticky_qr (line 857) | def _sticky_qr(self, force: bool = False) -> None:
    method _qr_thr (line 900) | def _qr_thr(self):
    method cb_httpsrv_up (line 927) | def cb_httpsrv_up(self) -> None:
    method after_httpsrv_up (line 955) | def after_httpsrv_up(self) -> None:
    method _feature_test (line 960) | def _feature_test(self) -> None:
    method _check_env (line 1014) | def _check_env(self) -> None:
    method _process_config (line 1069) | def _process_config(self) -> bool:
    method _ipa2re (line 1295) | def _ipa2re(self, txt) -> Optional[re.Pattern]:
    method _setlimits (line 1302) | def _setlimits(self) -> None:
    method _logname (line 1342) | def _logname(self) -> str:
    method _setup_logfile (line 1352) | def _setup_logfile(self, printed: str) -> None:
    method run (line 1399) | def run(self) -> None:
    method start_zeroconf (line 1438) | def start_zeroconf(self) -> None:
    method reload (line 1465) | def reload(self, rescan_all_vols: bool, up2k: bool) -> str:
    method _reload_sessions (line 1480) | def _reload_sessions(self) -> None:
    method stop_thr (line 1485) | def stop_thr(self) -> None:
    method kill9 (line 1496) | def kill9(self, delay: float = 0.0) -> None:
    method signal_handler (line 1507) | def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
    method shutdown (line 1529) | def shutdown(self) -> None:
    method _log_disabled (line 1600) | def _log_disabled(self, src: str, msg: str, c: Union[int, str] = 0) ->...
    method _set_next_day (line 1651) | def _set_next_day(self, dt: datetime) -> None:
    method _log_enabled (line 1659) | def _log_enabled(self, src: str, msg: str, c: Union[int, str] = 0) -> ...
    method _log_en_f2 (line 1724) | def _log_en_f2(self, src: str, msg: str, c: Union[int, str] = 0) -> None:
    method pr (line 1787) | def pr(self, *a: Any, **ka: Any) -> None:
    method check_mp_support (line 1795) | def check_mp_support(self) -> str:
    method check_mp_enable (line 1811) | def check_mp_enable(self) -> bool:
    method sd_notify (line 1837) | def sd_notify(self) -> None:
    method log_stacks (line 1856) | def log_stacks(self) -> None:
    method check_ver (line 1869) | def check_ver(self) -> None:

FILE: copyparty/szip.py
  function dostime2unix (line 19) | def dostime2unix(buf: bytes) -> int:
  function unixtime2dos (line 38) | def unixtime2dos(ts: int) -> bytes:
  function gen_fdesc (line 48) | def gen_fdesc(sz: int, crc32: int, z64: bool) -> bytes:
  function gen_hdr (line 55) | def gen_hdr(
  function gen_ecdr (line 144) | def gen_ecdr(
  function gen_ecdr64 (line 174) | def gen_ecdr64(
  function gen_ecdr64_loc (line 200) | def gen_ecdr64_loc(ecdr64_pos: int) -> bytes:
  class StreamZip (line 215) | class StreamZip(StreamArc):
    method __init__ (line 216) | def __init__(
    method _ct (line 233) | def _ct(self, buf: bytes) -> bytes:
    method ser (line 237) | def ser(self, f: dict[str, Any]) -> Generator[bytes, None, None]:
    method gen (line 278) | def gen(self) -> Generator[bytes, None, None]:

FILE: copyparty/tcpsrv.py
  class TcpSrv (line 46) | class TcpSrv(object):
    method __init__ (line 52) | def __init__(self, hub: "SvcHub"):
    method nlog (line 246) | def nlog(self, msg: str, c: Union[int, str] = 0) -> None:
    method _listen (line 249) | def _listen(self, ip: str, port: int) -> None:
    method run (line 359) | def run(self) -> None:
    method _distribute_netdevs (line 421) | def _distribute_netdevs(self):
    method shutdown (line 429) | def shutdown(self) -> None:
    method netmon (line 439) | def netmon(self):
    method detect_interfaces (line 460) | def detect_interfaces(self, listen_ips: list[str]) -> dict[str, Netdev]:
    method _extdevs_nix (line 512) | def _extdevs_nix(self) -> Generator[str, None, None]:
    method _defroute (line 520) | def _defroute(self) -> str:
    method _set_wintitle (line 545) | def _set_wintitle(self, vs: dict[str, dict[str, int]]) -> None:
    method _qr (line 580) | def _qr(self, t1: dict[str, list[int]], t2: dict[str, list[int]]) -> str:
    method _qr2file (line 674) | def _qr2file(self, qrc: QrCode, txt: str):
    method _h2i (line 694) | def _h2i(self, hs):

FILE: copyparty/tftpd.py
  class SimpleNamespace (line 8) | class SimpleNamespace(object):
    method __init__ (line 9) | def __init__(self, **attr):
  function noop (line 67) | def noop(*a, **ka) -> None:
  function _serverInitial (line 71) | def _serverInitial(self, pkt: Any, raddress: str, rport: int) -> bool:
  class Tftpd (line 85) | class Tftpd(object):
    method __init__ (line 86) | def __init__(self, hub: "SvcHub") -> None:
    method nlog (line 198) | def nlog(self, msg: str, c: Union[int, str] = 0) -> None:
    method _start (line 201) | def _start(self, ip, ports):
    method stop (line 252) | def stop(self):
    method _v2a (line 259) | def _v2a(
    method _ls (line 279) | def _ls(self, vpath: str, raddress: str, rport: int, force=False) -> Any:
    method _open (line 347) | def _open(self, vpath: str, mode: str, *a: Any, **ka: Any) -> Any:
    method _mkdir (line 401) | def _mkdir(self, vpath: str, *a) -> None:
    method _unlink (line 410) | def _unlink(self, vpath: str) -> None:
    method _access (line 425) | def _access(self, *a: Any) -> bool:
    method _p_abspath (line 428) | def _p_abspath(self, vpath: str) -> str:
    method _p_normpath (line 431) | def _p_normpath(self, *a: Any) -> str:
    method _p_exists (line 434) | def _p_exists(self, vpath: str) -> bool:
    method _p_isdir (line 442) | def _p_isdir(self, vpath: str) -> bool:
    method _hook (line 450) | def _hook(self, *a: Any, **ka: Any) -> None:
    method _disarm (line 455) | def _disarm(self, fos: SimpleNamespace) -> None:
  function yeet (line 487) | def yeet(msg: str) -> None:

FILE: copyparty/th_cli.py
  class ThumbCli (line 26) | class ThumbCli(object):
    method __init__ (line 27) | def __init__(self, hsrv: "HttpSrv") -> None:
    method log (line 59) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method get (line 62) | def get(self, dbv: VFS, rem: str, mtime: float, fmt: str) -> Optional[...

FILE: copyparty/th_srv.py
  function thumb_path (line 215) | def thumb_path(histpath: str, rem: str, mtime: float, fmt: str, ffa: set...
  class ThumbSrv (line 252) | class ThumbSrv(object):
    method __init__ (line 253) | def __init__(self, hub: "SvcHub") -> None:
    method log (line 344) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method shutdown (line 347) | def shutdown(self) -> None:
    method _fire_sentinels (line 351) | def _fire_sentinels(self):
    method stopped (line 355) | def stopped(self) -> bool:
    method getres (line 359) | def getres(self, vn: VFS, fmt: str) -> tuple[int, int]:
    method get (line 364) | def get(self, ptop: str, rem: str, mtime: float, fmt: str) -> Optional...
    method getcfg (line 420) | def getcfg(self) -> dict[str, set[str]]:
    method volcfgi (line 431) | def volcfgi(self, vn: VFS) -> str:
    method volcfga (line 441) | def volcfga(self, vn: VFS) -> str:
    method writevolcfg (line 451) | def writevolcfg(self, histpath: str) -> None:
    method wait4ram (line 472) | def wait4ram(self, need: float, ttpath: str) -> None:
    method worker (line 489) | def worker(self) -> None:
    method fancy_pillow (line 616) | def fancy_pillow(self, im: "Image.Image", fmt: str, vn: VFS) -> "Image...
    method conv_image_pil (line 643) | def conv_image_pil(self, im: "Image.Image", tpath: str, fmt: str, vn: ...
    method conv_pil (line 672) | def conv_pil(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_image_vips (line 677) | def conv_image_vips(
    method conv_vips (line 710) | def conv_vips(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_raw (line 718) | def conv_raw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_ffmpeg (line 748) | def conv_ffmpeg(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> ...
    method _ffmpeg_im (line 763) | def _ffmpeg_im(
    method _ffmpeg_im_o (line 799) | def _ffmpeg_im_o(self, tpath: str, vn: VFS, cmd: list[bytes]) -> None:
    method _run_ff (line 823) | def _run_ff(self, cmd: list[bytes], vn: VFS, kto: str, oom: int = 400)...
    method conv_waves (line 879) | def conv_waves(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> N...
    method conv_emb_cv (line 939) | def conv_emb_cv(
    method conv_spec (line 947) | def conv_spec(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_mp3 (line 1029) | def conv_mp3(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_flac (line 1068) | def conv_flac(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_wav (line 1093) | def conv_wav(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method conv_opus (line 1128) | def conv_opus(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
    method _conv_owa (line 1148) | def _conv_owa(
    method _conv_caf (line 1184) | def _conv_caf(
    method big_tags (line 1275) | def big_tags(self, raw_tags: dict[str, list[str]]) -> list[bytes]:
    method poke (line 1285) | def poke(self, tdir: str) -> None:
    method cleaner (line 1297) | def cleaner(self) -> None:
    method clean (line 1316) | def clean(self, histpath: str) -> int:
    method _clean (line 1349) | def _clean(self, cat: str, thumbpath: str) -> int:

FILE: copyparty/u2idx.py
  class U2idx (line 45) | class U2idx(object):
    method __init__ (line 46) | def __init__(self, hsrv: "HttpSrv") -> None:
    method log (line 74) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method _open_db_std (line 77) | def _open_db_std(self, *args, **kwargs):
    method _open_db_icase (line 82) | def _open_db_icase(self, *args, **kwargs):
    method shutdown (line 87) | def shutdown(self) -> None:
    method fsearch (line 107) | def fsearch(
    method get_shr (line 126) | def get_shr(self) -> Optional["sqlite3.Cursor"]:
    method get_cur (line 141) | def get_cur(self, vn: VFS) -> Optional["sqlite3.Cursor"]:
    method search (line 183) | def search(
    method run_query (line 358) | def run_query(
    method terminator (line 520) | def terminator(self, identifier: str, done_flag: list[bool]) -> None:

FILE: copyparty/up2k.py
  class Dbw (line 120) | class Dbw(object):
    method __init__ (line 121) | def __init__(self, c: "sqlite3.Cursor", n: int, nf: int, t: float) -> ...
  class Mpqe (line 128) | class Mpqe(object):
    method __init__ (line 131) | def __init__(
  class Up2k (line 149) | class Up2k(object):
    method __init__ (line 150) | def __init__(self, hub: "SvcHub") -> None:
    method init_vols (line 240) | def init_vols(self) -> None:
    method unpp (line 246) | def unpp(self) -> None:
    method reload (line 252) | def reload(self, rescan_all_vols: bool) -> None:
    method _reload_thr (line 260) | def _reload_thr(self) -> None:
    method _reload (line 277) | def _reload(self, rescan_all_vols: bool) -> None:
    method deferred_init (line 292) | def deferred_init(self) -> None:
    method log (line 319) | def log(self, msg: str, c: Union[int, str] = 0) -> None:
    method _gen_fk (line 325) | def _gen_fk(self, alg: int, salt: str, fspath: str, fsize: int, inode:...
    method _block (line 330) | def _block(self, why: str) -> None:
    method _unblock (line 334) | def _unblock(self) -> None:
    method get_state (line 340) | def get_state(self, get_q: bool, uname: str) -> str:
    method _active_uploads (line 380) | def _active_uploads(self, uname: str) -> list[tuple[float, int, int, s...
    method find_job_by_ap (line 407) | def find_job_by_ap(self, ptop: str, ap: str) -> str:
    method get_unfinished_by_user (line 424) | def get_unfinished_by_user(self, uname, ip) -> dict[str, Any]:
    method get_unfinished (line 476) | def get_unfinished(self) -> str:
    method get_volsize (line 502) | def get_volsize(self, ptop: str) -> tuple[int, int]:
    method get_volsizes (line 506) | def get_volsizes(self, ptops: list[str]) -> list[tuple[int, int]]:
    method _get_volsize (line 514) | def _get_volsize(self, ptop: str) -> tuple[int, int]:
    method rescan (line 527) | def rescan(
    method _rescan (line 533) | def _rescan(
    method _sched_rescan (line 548) | def _sched_rescan(self) -> None:
    method _check_forget_ip (line 648) | def _check_forget_ip(self) -> float:
    method _check_lifetimes (line 686) | def _check_lifetimes(self) -> float:
    method _check_shares (line 735) | def _check_shares(self) -> float:
    method _check_xiu (line 789) | def _check_xiu(self) -> float:
    method _run_xius (line 822) | def _run_xius(self, vol: VFS, cds: list[int]):
    method _run_xiu (line 829) | def _run_xiu(self, vol: VFS, cd: int):
    method _vis_job_progress (line 866) | def _vis_job_progress(self, job: dict[str, Any]) -> str:
    method _vis_reg_progress (line 871) | def _vis_reg_progress(self, reg: dict[str, dict[str, Any]]) -> list[str]:
    method _expr_idx_filter (line 879) | def _expr_idx_filter(self, flags: dict[str, Any]) -> tuple[bool, dict[...
    method init_indexes (line 889) | def init_indexes(
    method register_vpath (line 1130) | def register_vpath(
    method _verify_db_cache (line 1340) | def _verify_db_cache(self, cur: "sqlite3.Cursor", vpath: str) -> None:
    method _build_file_index (line 1369) | def _build_file_index(self, vol: VFS, all_vols: list[VFS]) -> tuple[bo...
    method _fika (line 1480) | def _fika(self, db: Dbw) -> None:
    method _build_dir (line 1497) | def _build_dir(
    method _drop_lost (line 1843) | def _drop_lost(self, cur: "sqlite3.Cursor", top: str, excl: list[str])...
    method _verify_integrity (line 1931) | def _verify_integrity(self, vol: VFS) -> int:
    method _build_tags_index (line 2070) | def _build_tags_index(self, vol: VFS) -> tuple[int, int, bool]:
    method _drop_caches (line 2092) | def _drop_caches(self) -> None:
    method _set_tagscan (line 2106) | def _set_tagscan(self, cur: "sqlite3.Cursor", need: bool) -> bool:
    method _build_tags_index_2 (line 2121) | def _build_tags_index_2(self, ptop: str) -> tuple[int, int, bool]:
    method _e2ts_q (line 2186) | def _e2ts_q(
    method _spool_warks (line 2270) | def _spool_warks(
    method _unspool (line 2300) | def _unspool(self, tf: tempfile.SpooledTemporaryFile[bytes]) -> None:
    method _flush_mpool (line 2311) | def _flush_mpool(self, wcur: "sqlite3.Cursor") -> list[str]:
    method _run_all_mtp (line 2320) | def _run_all_mtp(self, gid: int) -> None:
    method _run_one_mtp (line 2345) | def _run_one_mtp(self, ptop: str, gid: int) -> None:
    method _get_parsers (line 2493) | def _get_parsers(
    method _start_mpool (line 2539) | def _start_mpool(self) -> Queue[Mpqe]:
    method _stop_mpool (line 2554) | def _stop_mpool(self, mpool: Queue[Mpqe]) -> None:
    method _tag_thr (line 2563) | def _tag_thr(self, q: Queue[Mpqe]) -> None:
    method _log_tag_err (line 2603) | def _log_tag_err(self, parser: Any, abspath: str, ex: Any) -> None:
    method _tagscan_file (line 2607) | def _tagscan_file(
    method _tag_file (line 2645) | def _tag_file(
    method _trace (line 2684) | def _trace(self, msg: str) -> None:
    method _open_db_wd (line 2687) | def _open_db_wd(self, db_path: str) -> "sqlite3.Cursor":
    method _open_db_timeout (line 2697) | def _open_db_timeout(self, db_path, ok: list[int]) -> None:
    method _log_sqlite_incompat (line 2707) | def _log_sqlite_incompat(self, db_path, t0) -> None:
    method _orz (line 2729) | def _orz(self, db_path: str) -> "sqlite3.Cursor":
    method _open_db (line 2737) | def _open_db(self, db_path: str) -> "sqlite3.Cursor":
    method _delete_db (line 2785) | def _delete_db(self, db_path: str):
    method _backup_db (line 2793) | def _backup_db(
    method _read_ver (line 2817) | def _read_ver(self, cur: "sqlite3.Cursor") -> Optional[int]:
    method _try_create_db (line 2829) | def _try_create_db(
    method _create_db (line 2841) | def _create_db(
    method _upgrade_v4 (line 2880) | def _upgrade_v4(self, cur: "sqlite3.Cursor") -> None:
    method _upgrade_v5 (line 2891) | def _upgrade_v5(self, cur: "sqlite3.Cursor") -> None:
    method _add_dhash_tab (line 2900) | def _add_dhash_tab(self, cur: "sqlite3.Cursor") -> None:
    method _add_xiu_tab (line 2917) | def _add_xiu_tab(self, cur: "sqlite3.Cursor") -> None:
    method _add_cv_tab (line 2940) | def _add_cv_tab(self, cur: "sqlite3.Cursor") -> None:
    method _add_idx_up_vp (line 2961) | def _add_idx_up_vp(self, cur: "sqlite3.Cursor", db_path: str) -> None:
    method _add_ds_tab (line 2979) | def _add_ds_tab(self, cur: "sqlite3.Cursor") -> None:
    method wake_rescanner (line 2995) | def wake_rescanner(self):
    method handle_json (line 2999) | def handle_json(
    method _handle_json (line 3031) | def _handle_json(self, cj: dict[str, Any], depth: int = 1) -> dict[str...
    method _untaken (line 3510) | def _untaken(self, fdir: str, job: dict[str, Any], ts: float) -> str:
    method _symlink (line 3570) | def _symlink(
    method handle_chunks (line 3686) | def handle_chunks(
    method fast_confirm_chunks (line 3775) | def fast_confirm_chunks(
    method confirm_chunks (line 3789) | def confirm_chunks(
    method _confirm_chunks (line 3796) | def _confirm_chunks(
    method finish_upload (line 3830) | def finish_upload(self, ptop: str, wark: str, busy_aps: dict[str, int]...
    method _finish_upload (line 3839) | def _finish_upload(self, ptop: str, wark: str) -> None:
    method regdrop (line 3914) | def regdrop(self, ptop: str, wark: str) -> None:
    method idx_wark (line 3930) | def idx_wark(
    method db_rm (line 3983) | def db_rm(
    method db_add (line 4010) | def db_add(
    method handle_fs_abrt (line 4139) | def handle_fs_abrt(self, akey: str) -> None:
    method handle_rm (line 4142) | def handle_rm(
    method _handle_rm (line 4172) | def _handle_rm(
    method handle_cp (line 4365) | def handle_cp(self, abrt: str, uname: str, ip: str, svp: str, dvp: str...
    method _cp_file (line 4429) | def _cp_file(
    method handle_mv (line 4591) | def handle_mv(self, abrt: str, uname: str, ip: str, svp: str, dvp: str...
    method _mv_file (line 4683) | def _mv_file(
    method _copy_tags (line 4917) | def _copy_tags(
    method _find_from_vpath (line 4929) | def _find_from_vpath(
    method _forget_file (line 4958) | def _forget_file(
    method _relink (line 5018) | def _relink(
    method _get_wark (line 5145) | def _get_wark(self, cj: dict[str, Any]) -> str:
    method _hashlist_from_file (line 5170) | def _hashlist_from_file(
    method _ex_hash (line 5209) | def _ex_hash(self, ex: Exception, ap: str) -> None:
    method _new_upload (line 5219) | def _new_upload(self, job: dict[str, Any], vfs: VFS, depth: int) -> di...
    method _snapshot (line 5358) | def _snapshot(self) -> None:
    method do_snapshot (line 5371) | def do_snapshot(self) -> None:
    method _snap_reg (line 5377) | def _snap_reg(self, ptop: str, reg: dict[str, dict[str, Any]]) -> None:
    method _tagger (line 5461) | def _tagger(self) -> None:
    method _hasher (line 5511) | def _hasher(self) -> None:
    method _hash_t (line 5530) | def _hash_t(
    method hash_file (line 5580) | def hash_file(
    method do_fx_backlog (line 5609) | def do_fx_backlog(self):
    method hook_fx (line 5616) | def hook_fx(self, act: str, hr: dict[str, str], req_vp: str) -> None:
    method shutdown (line 5651) | def shutdown(self) -> None:
  function up2k_chunksize (line 5694) | def up2k_chunksize(filesize: int) -> int:
  function up2k_wark_from_hashlist (line 5707) | def up2k_wark_from_hashlist(salt: str, filesize: int, hashes: list[str])...
  function up2k_wark_from_metadata (line 5716) | def up2k_wark_from_metadata(salt: str, sz: int, lastmod: int, rd: str, f...

FILE: copyparty/util.py
  function noop (line 61) | def noop(*a, **ka):
  class _UTC (line 74) | class _UTC(tzinfo):
    method utcoffset (line 75) | def utcoffset(self, dt):
    method tzname (line 78) | def tzname(self, dt):
    method dst (line 81) | def dst(self, dt):
  function _ens (line 102) | def _ens(want: str) -> tuple[int, ...]:
  function get_adapters (line 176) | def get_adapters(include_unconfigured=False):
  class RootLogger (line 234) | class RootLogger(Protocol):
    method __call__ (line 235) | def __call__(self, src: str, msg: str, c: Union[int, str] = 0) -> None:
  class NamedLogger (line 238) | class NamedLogger(Protocol):
    method __call__ (line 239) | def __call__(self, msg: str, c: Union[int, str] = 0) -> None:
  function inet_pton (line 281) | def inet_pton(fam, ip):
  function inet_pton (line 289) | def inet_pton(fam, ip):
  function spack (line 302) | def spack(fmt: bytes, *a: Any) -> bytes:
  function sunpack (line 305) | def sunpack(fmt: bytes, a: bytes) -> tuple[Any, ...]:
  function umktrans (line 334) | def umktrans(s1, s2):
  function _add_mimes (line 478) | def _add_mimes() -> set[str]:
  function read_ram (line 600) | def read_ram() -> tuple[float, float]:
  function py_desc (line 639) | def py_desc() -> str:
  function expat_ver (line 659) | def expat_ver() -> str:
  function _sqlite_ver (line 668) | def _sqlite_ver() -> str:
  function ub64enc (line 740) | def ub64enc(bs: bytes) -> bytes:
  function ub64dec (line 744) | def ub64dec(bs: bytes) -> bytes:
  function b64enc (line 748) | def b64enc(bs: bytes) -> bytes:
  function b64dec (line 751) | def b64dec(bs: bytes) -> bytes:
  class NotUTF8 (line 768) | class NotUTF8(Exception):
  function read_utf8 (line 772) | def read_utf8(log: Optional["NamedLogger"], ap: Union[str, bytes], stric...
  class Daemon (line 803) | class Daemon(threading.Thread):
    method __init__ (line 804) | def __init__(
    method run (line 820) | def run(self):
  class Netdev (line 829) | class Netdev(object):
    method __init__ (line 830) | def __init__(self, ip: str, idx: int, name: str, desc: str):
    method __str__ (line 836) | def __str__(self):
    method __repr__ (line 839) | def __repr__(self):
    method __lt__ (line 842) | def __lt__(self, rhs):
    method __eq__ (line 845) | def __eq__(self, rhs):
  class Cooldown (line 849) | class Cooldown(object):
    method __init__ (line 850) | def __init__(self, maxage: float) -> None:
    method poke (line 856) | def poke(self, key: str) -> bool:
  class HLog (line 875) | class HLog(logging.Handler):
    method __init__ (line 876) | def __init__(self, log_func: "RootLogger") -> None:
    method __repr__ (line 882) | def __repr__(self) -> str:
    method flush (line 886) | def flush(self) -> None:
    method emit (line 889) | def emit(self, record: logging.LogRecord) -> None:
  class NetMap (line 919) | class NetMap(object):
    method __init__ (line 920) | def __init__(
    method map (line 966) | def map(self, ip: str) -> str:
    method _map (line 979) | def _map(self, ip: str) -> str:
  class UnrecvEOF (line 990) | class UnrecvEOF(OSError):
  class _Unrecv (line 994) | class _Unrecv(object):
    method __init__ (line 999) | def __init__(self, s: socket.socket, log: Optional["NamedLogger"]) -> ...
    method recv (line 1006) | def recv(self, nbytes: int, spins: int = 1) -> bytes:
    method recv_ex (line 1033) | def recv_ex(self, nbytes: int, raise_on_trunc: bool = True) -> bytes:
    method unrecv (line 1056) | def unrecv(self, buf: bytes) -> None:
  class _LUnrecv (line 1062) | class _LUnrecv(object):
    method __init__ (line 1067) | def __init__(self, s: socket.socket, log: Optional["NamedLogger"]) -> ...
    method recv (line 1073) | def recv(self, nbytes: int, spins: int) -> bytes:
    method recv_ex (line 1091) | def recv_ex(self, nbytes: int, raise_on_trunc: bool = True) -> bytes:
    method unrecv (line 1115) | def unrecv(self, buf: bytes) -> None:
  class CachedSet (line 1128) | class CachedSet(object):
    method __init__ (line 1129) | def __init__(self, maxage: float) -> None:
    method add (line 1134) | def add(self, v: Any) -> None:
    method cln (line 1137) | def cln(self) -> None:
  class CachedDict (line 1149) | class CachedDict(object):
    method __init__ (line 1150) | def __init__(self, maxage: float) -> None:
    method set (line 1155) | def set(self, k: str, v: Any) -> None:
    method get (line 1167) | def get(self, k: str) -> Optional[tuple[str, Any]]:
  class FHC (line 1179) | class FHC(object):
    class CE (line 1180) | class CE(object):
      method __init__ (line 1181) | def __init__(self, fh: typing.BinaryIO) -> None:
    method __init__ (line 1186) | def __init__(self) -> None:
    method close (line 1190) | def close(self, path: str) -> None:
    method clean (line 1202) | def clean(self) -> None:
    method pop (line 1217) | def pop(self, path: str) -> typing.BinaryIO:
    method put (line 1220) | def put(self, path: str, fh: typing.BinaryIO) -> None:
  class ProgressPrinter (line 1235) | class ProgressPrinter(threading.Thread):
    method __init__ (line 1240) | def __init__(self, log: "NamedLogger", args: argparse.Namespace) -> None:
    method run (line 1249) | def run(self) -> None:
  class MTHash (line 1286) | class MTHash(object):
    method __init__ (line 1287) | def __init__(self, cores: int):
    method hash (line 1303) | def hash(
    method worker (line 1347) | def worker(self) -> None:
    method hash_at (line 1357) | def hash_at(self, nch: int) -> tuple[int, str, int, int]:
  class HMaccas (line 1383) | class HMaccas(object):
    method __init__ (line 1384) | def __init__(self, keypath: str, retlen: int) -> None:
    method b (line 1397) | def b(self, msg: bytes) -> str:
    method s (line 1409) | def s(self, msg: str) -> str:
  class Magician (line 1413) | class Magician(object):
    method __init__ (line 1414) | def __init__(self) -> None:
    method ext (line 1419) | def ext(self, fpath: str) -> str:
  class Garda (line 1457) | class Garda(object):
    method __init__ (line 1460) | def __init__(self, cfg: str, uniq: bool = True) -> None:
    method cln (line 1474) | def cln(self, ip: str) -> None:
    method allcln (line 1493) | def allcln(self) -> None:
    method bonk (line 1499) | def bonk(self, ip: str, prev: str) -> tuple[int, str]:
  function _spopen (line 1533) | def _spopen(c, *a, **ka):
  function uprint (line 1541) | def uprint(msg: str) -> None:
  function nuprint (line 1551) | def nuprint(msg: str) -> None:
  function dedent (line 1555) | def dedent(txt: str) -> str:
  function rice_tid (line 1566) | def rice_tid() -> str:
  function trace (line 1572) | def trace(*args: Any, **kwargs: Any) -> None:
  function alltrace (line 1591) | def alltrace(verbose: bool = True) -> str:
  function start_stackmon (line 1623) | def start_stackmon(arg_str: str, nid: int) -> None:
  function stackmon (line 1630) | def stackmon(fp: str, ival: float, suffix: str) -> None:
  function start_log_thrs (line 1669) | def start_log_thrs(
  function log_thrs (line 1681) | def log_thrs(log: Callable[[str, str, int], None], ival: float, name: st...
  function _sigblock (line 1698) | def _sigblock():
  function vol_san (line 1707) | def vol_san(vols: list["VFS"], txt: bytes) -> bytes:
  function min_ex (line 1734) | def min_ex(max_lines: int = 8, reverse: bool = False) -> str:
  function ren_open (line 1744) | def ren_open(fname: str, *args: Any, **kwargs: Any) -> tuple[typing.IO[A...
  class MultipartParser (line 1846) | class MultipartParser(object):
    method __init__ (line 1847) | def __init__(
    method _read_header (line 1876) | def _read_header(self) -> tuple[str, Optional[str]]:
    method _read_data (line 1954) | def _read_data(self) -> Generator[bytes, None, None]:
    method _run_gen (line 2002) | def _run_gen(
    method _read_value (line 2028) | def _read_value(self, iterable: Iterable[bytes], max_len: int) -> bytes:
    method parse (line 2037) | def parse(self) -> None:
    method require (line 2059) | def require(self, field_name: str, max_len: int) -> str:
    method drop (line 2071) | def drop(self) -> None:
  function get_boundary (line 2079) | def get_boundary(headers: dict[str, str]) -> str:
  function read_header (line 2091) | def read_header(sr: Unrecv, t_idle: int, t_tot: int) -> list[str]:
  function rand_name (line 2123) | def rand_name(fdir: str, fn: str, rnd: int) -> str:
  function _gen_filekey (line 2144) | def _gen_filekey(alg: int, salt: str, fspath: str, fsize: int, inode: in...
  function _gen_filekey_w (line 2154) | def _gen_filekey_w(alg: int, salt: str, fspath: str, fsize: int, inode: ...
  function gen_filekey_dbg (line 2161) | def gen_filekey_dbg(
  function formatdate (line 2201) | def formatdate(ts: Optional[float] = None) -> str:
  function gencookie (line 2207) | def gencookie(
  function gen_content_disposition (line 2228) | def gen_content_disposition(fn: str) -> str:
  function humansize (line 2251) | def humansize(sz: float, terse: bool = False) -> str:
  function unhumanize (line 2265) | def unhumanize(sz: str) -> int:
  function get_spd (line 2276) | def get_spd(nbyte: int, t0: float, t: Optional[float] = None) -> str:
  function s2hms (line 2286) | def s2hms(s: float, optional_h: bool = False) -> str:
  function djoin (line 2296) | def djoin(*paths: str) -> str:
  function uncyg (line 2301) | def uncyg(path: str) -> str:
  function undot (line 2311) | def undot(path: str) -> str:
  function sanitize_fn (line 2327) | def sanitize_fn(fn: str) -> str:
  function sanitize_to (line 2334) | def sanitize_to(fn: str, tl: dict[int, int]) -> str:
  function sanitize_vpath (line 2346) | def sanitize_vpath(vp: str) -> str:
  function relchk (line 2354) | def relchk(rp: str) -> str:
  function absreal (line 2369) | def absreal(fpath: str) -> str:
  function u8safe (line 2382) | def u8safe(txt: str) -> str:
  function exclude_dotfiles (line 2389) | def exclude_dotfiles(filepaths: list[str]) -> list[str]:
  function exclude_dotfiles_ls (line 2393) | def exclude_dotfiles_ls(
  function odfusion (line 2399) | def odfusion(
  function ipnorm (line 2419) | def ipnorm(ip: str) -> str:
  function find_prefix (line 2427) | def find_prefix(ips: list[str], cidrs: list[str]) -> list[str]:
  function html_sh_esc (line 2436) | def html_sh_esc(s: str) -> str:
  function json_hesc (line 2442) | def json_hesc(s: str) -> str:
  function html_escape (line 2446) | def html_escape(s: str, quot: bool = False, crlf: bool = False) -> str:
  function html_bescape (line 2457) | def html_bescape(s: bytes, quot: bool = False, crlf: bool = False) -> by...
  function _quotep2 (line 2468) | def _quotep2(txt: str) -> str:
  function _quotep3 (line 2477) | def _quotep3(txt: str) -> str:
  function _quotep3b (line 2494) | def _quotep3b(txt: str) -> str:
  function unquote (line 2509) | def unquote(btxt: bytes) -> bytes:
  function unquotep (line 2531) | def unquotep(txt: str) -> str:
  function vroots (line 2538) | def vroots(vp1: str, vp2: str) -> tuple[str, str]:
  function vsplit (line 2555) | def vsplit(vpath: str) -> tuple[str, str]:
  function vjoin (line 2563) | def vjoin(rd: str, fn: str) -> str:
  function ujoin (line 2571) | def ujoin(rd: str, fn: str) -> str:
  function str_anchor (line 2578) | def str_anchor(txt) -> tuple[int, str]:
  function log_reloc (line 2593) | def log_reloc(
  function pathmod (line 2608) | def pathmod(
  function _w8dec2 (line 2659) | def _w8dec2(txt: bytes) -> str:
  function _w8enc2 (line 2664) | def _w8enc2(txt: str) -> bytes:
  function _w8dec3 (line 2669) | def _w8dec3(txt: bytes) -> str:
  function _w8enc3 (line 2674) | def _w8enc3(txt: str) -> bytes:
  function _msdec (line 2679) | def _msdec(txt: bytes) -> str:
  function _msaenc (line 2684) | def _msaenc(txt: str) -> bytes:
  function _uncify (line 2688) | def _uncify(txt: str) -> str:
  function _msenc (line 2696) | def _msenc(txt: str) -> bytes:
  function w8b64dec (line 2709) | def w8b64dec(txt: str) -> str:
  function w8b64enc (line 2714) | def w8b64enc(txt: str) -> str:
  function _not_actually_mbcs_enc (line 2732) | def _not_actually_mbcs_enc(txt: str) -> bytes:
  function _not_actually_mbcs_dec (line 2735) | def _not_actually_mbcs_dec(txt: bytes) -> str:
  function s3enc (line 2743) | def s3enc(mem_cur: "sqlite3.Cursor", rd: str, fn: str) -> tuple[str, str]:
  function s3dec (line 2756) | def s3dec(rd: str, fn: str) -> tuple[str, str]:
  function db_ex_chk (line 2763) | def db_ex_chk(log: "NamedLogger", ex: Exception, db_path: str) -> bool:
  function lsof (line 2771) | def lsof(log: "NamedLogger", abspath: str) -> None:
  function set_fperms (line 2780) | def set_fperms(f: Union[typing.BinaryIO, typing.IO[Any]], vf: dict[str, ...
  function set_ap_perms (line 2788) | def set_ap_perms(ap: str, vf: dict[str, Any]) -> None:
  function trystat_shutil_copy2 (line 2796) | def trystat_shutil_copy2(log: "NamedLogger", src: bytes, dst: bytes) -> ...
  function _fs_mvrm (line 2814) | def _fs_mvrm(
  function atomic_move (line 2880) | def atomic_move(log: "NamedLogger", src: str, dst: str, flags: dict[str,...
  function wunlink (line 2905) | def wunlink(log: "NamedLogger", abspath: str, flags: dict[str, Any]) -> ...
  function get_df (line 2913) | def get_df(abspath: str, prune: bool) -> tuple[int, int, str]:
  function siocoutq (line 2944) | def siocoutq(sck: socket.socket) -> int:
  function siocoutq (line 2958) | def siocoutq(sck: socket.socket) -> int:
  function shut_socket (line 2962) | def shut_socket(log: "NamedLogger", sck: socket.socket, timeout: int = 3...
  function read_socket (line 2997) | def read_socket(
  function read_socket_unbounded (line 3015) | def read_socket_unbounded(sr: Unrecv, bufsz: int) -> Generator[bytes, No...
  function read_socket_chunked (line 3023) | def read_socket_chunked(
  function list_ips (line 3068) | def list_ips() -> list[str]:
  function build_netmap (line 3080) | def build_netmap(csv: str, defer_mutex: bool = False):
  function load_ipu (line 3135) | def load_ipu(
  function load_ipr (line 3162) | def load_ipr(
  function yieldfile (line 3183) | def yieldfile(fn: str, bufsz: int) -> Generator[bytes, None, None]:
  function justcopy (line 3194) | def justcopy(
  function eol_conv (line 3214) | def eol_conv(
  function hashcopy (line 3225) | def hashcopy(
  function sendfile_py (line 3250) | def sendfile_py(
  function sendfile_kern (line 3286) | def sendfile_kern(
  function statdir (line 3341) | def statdir(
  function dir_is_empty (line 3392) | def dir_is_empty(logger: "RootLogger", scandir: bool, top: str):
  function rmdirs (line 3398) | def rmdirs(
  function rmdirs_up (line 3428) | def rmdirs_up(top: str, stop: str) -> tuple[list[str], list[str]]:
  function unescape_cookie (line 3446) | def unescape_cookie(orig: str, name: str) -> str:
  function guess_mime (line 3476) | def guess_mime(
  function safe_mime (line 3510) | def safe_mime(mime: str) -> str:
  function getalive (line 3517) | def getalive(pids: list[int], pgid: int) -> list[int]:
  function killtree (line 3536) | def killtree(root: int) -> None:
  function _find_nice (line 3585) | def _find_nice() -> str:
  function runcmd (line 3609) | def runcmd(
  function chkcmd (line 3683) | def chkcmd(argv: Union[list[bytes], list[str]], **ka: Any) -> tuple[str,...
  function mchkcmd (line 3692) | def mchkcmd(argv: Union[list[bytes], list[str]], timeout: float = 10) ->...
  function retchk (line 3703) | def retchk(
  function _parsehook (line 3756) | def _parsehook(
  function runihook (line 3829) | def runihook(
  function _zmq_hook (line 3897) | def _zmq_hook(
  function _runhook (line 3998) | def _runhook(
  function runhook (line 4102) | def runhook(
  function loadpy (line 4161) | def loadpy(ap: str, hot: bool) -> Any:
  function gzip_orig_sz (line 4187) | def gzip_orig_sz(fn: str) -> int:
  function gzip_file_orig_sz (line 4192) | def gzip_file_orig_sz(f) -> int:
  function align_tab (line 4200) | def align_tab(lines: list[str]) -> list[str]:
  function visual_length (line 4216) | def visual_length(txt: str) -> int:
  function wrap (line 4261) | def wrap(txt: str, maxlen: int, maxlen2: int) -> list[str]:
  function termsize (line 4295) | def termsize() -> tuple[int, int]:
  function hidedir (line 4328) | def hidedir(dp) -> None:
  function _lock_file_noop (line 4343) | def _lock_file_noop(ap: str) -> bool:
  function _lock_file_ioctl (line 4347) | def _lock_file_ioctl(ap: str) -> bool:
  function _lock_file_windows (line 4376) | def _lock_file_windows(ap: str) -> bool:
  function _open_nolock_windows (line 4407) | def _open_nolock_windows(bap: Union[str, bytes], *a, **ka) -> typing.Bin...
  function _pkg_resource_exists (line 4451) | def _pkg_resource_exists(pkg: str, name: str) -> bool:
  function stat_resource (line 4460) | def stat_resource(E: EnvParams, name: str):
  function _find_impresource (line 4467) | def _find_impresource(pkg: types.ModuleType, name: str):
  function _has_resource (line 4480) | def _has_resource(name: str):
  function has_resource (line 4507) | def has_resource(E: EnvParams, name: str):
  function load_resource (line 4511) | def load_resource(E: EnvParams, name: str, mode="rb") -> IO[bytes]:
  class Pebkac (line 4541) | class Pebkac(Exception):
    method __init__ (line 4542) | def __init__(
    method __repr__ (line 4549) | def __repr__(self) -> str:
  class WrongPostKey (line 4553) | class WrongPostKey(Pebkac):
    method __init__ (line 4554) | def __init__(

FILE: copyparty/web/baguettebox.js
  function run (line 146) | function run(selector, userOptions) {
  function bindImageClickListeners (line 152) | function bindImageClickListeners(selector, userOptions) {
  function bindCbzClickListeners (line 199) | function bindCbzClickListeners(tagsNodeList, userOptions) {
  function fillCbzGallery (line 237) | function fillCbzGallery(gallery, cbzElement, eventHandler) {
  function clearCachedData (line 278) | function clearCachedData() {
  function removeFromCache (line 284) | function removeFromCache(selector) {
  function buildOverlay (line 301) | function buildOverlay() {
  function halp (line 345) | function halp() {
  function keyDownHandler (line 384) | function keyDownHandler(e) {
  function anim (line 454) | function anim() {
  function toggleReadDir (line 464) | function toggleReadDir() {
  function setVmode (line 479) | function setVmode() {
  function tglVmode (line 510) | function tglVmode() {
  function findfile (line 525) | function findfile() {
  function tglfull (line 535) | function tglfull() {
  function setzoom (line 550) | function setzoom() {
  function tglsel (line 556) | function tglsel() {
  function dlpic (line 563) | function dlpic() {
  function selbg (line 568) | function selbg() {
  function btnState (line 586) | function btnState(btn, sel) {
  function keyUpHandler (line 593) | function keyUpHandler(e) {
  method passive (line 608) | get passive() {
  function bindEvents (line 622) | function bindEvents() {
  function unbindEvents (line 646) | function unbindEvents() {
  function prepareOverlay (line 671) | function prepareOverlay(gallery, userOptions) {
  function setOptions (line 695) | function setOptions(newOptions) {
  function showOverlay (line 735) | function showOverlay(chosenImageIndex) {
  function hideOverlay (line 784) | function hideOverlay(e, dtor) {
  function unvid (line 831) | function unvid(keep) {
  function unfig (line 849) | function unfig(keep) {
  function lerr (line 867) | function lerr() {
  function loadImage (line 889) | function loadImage(index, callback) {
  function ppHandler (line 962) | function ppHandler() {
  function showRightImage (line 971) | function showRightImage(e) {
  function showLeftImage (line 977) | function showLeftImage(e) {
  function showNextImageIgnoreReadDir (line 983) | function showNextImageIgnoreReadDir(e) {
  function showPreviousImageIgnoreReadDir (line 988) | function showPreviousImageIgnoreReadDir(e) {
  function showFirstImage (line 993) | function showFirstImage(e) {
  function showLastImage (line 1000) | function showLastImage(e) {
  function show (line 1007) | function show(index, gallery) {
  function rotn (line 1060) | function rotn(n, asap) {
  function rotl (line 1119) | function rotl() {
  function rotr (line 1122) | function rotr() {
  function unrot (line 1125) | function unrot() {
  function vid (line 1143) | function vid() {
  function vidimg (line 1150) | function vidimg() {
  function playvid (line 1157) | function playvid(play) {
  function playpause (line 1172) | function playpause() {
  function relseek (line 1178) | function relseek(sec) {
  function vidEnd (line 1186) | function vidEnd() {
  function setloop (line 1191) | function setloop(side) {
  function loopchk (line 1208) | function loopchk() {
  function urltime (line 1219) | function urltime(txt) {
  function mp_ctl (line 1223) | function mp_ctl() {
  function show_buttons (line 1235) | function show_buttons(v) {
  function bounceAnimation (line 1241) | function bounceAnimation(direction) {
  function updateOffset (line 1249) | function updateOffset(noTransition) {
  function preloadNext (line 1321) | function preloadNext(index) {
  function preloadPrev (line 1330) | function preloadPrev(index) {
  function bind (line 1339) | function bind(element, event, callback, options) {
  function unbind (line 1343) | function unbind(element, event, callback, options) {
  function destroyPlugin (line 1347) | function destroyPlugin() {

FILE: copyparty/web/browser.js
  function langtest (line 717) | function langtest() {
  function langtest2 (line 722) | function langtest2() {
  function opclick (line 1080) | function opclick(e) {
  function goto (line 1100) | function goto(dest) {
  function read_sbw (line 1144) | function read_sbw() {
  function check_image_support (line 1157) | function check_image_support(format, uri) {
  function set_files_html (line 1184) | function set_files_html(html) {
  function draw_pb_mode (line 1405) | function draw_pb_mode() {
  function set_pb_mode (line 1414) | function set_pb_mode(e) {
  function set_tint (line 1421) | function set_tint() {
  function setaufollow (line 1528) | function setaufollow() {
  function announce (line 1533) | function announce() {
  function MPlayer (line 1646) | function MPlayer() {
  function ft2dict (line 1862) | function ft2dict(tr, skip) {
  function canvas_cfg (line 2007) | function canvas_cfg(can) {
  function glossy_grad (line 2024) | function glossy_grad(can, h, s, l) {
  function mmove (line 2078) | function mmove(e) {
  function menter (line 2091) | function menter() {
  function mleave (line 2095) | function mleave() {
  function mousedown (line 2314) | function mousedown(e) {
  function mousemove (line 2318) | function mousemove(e) {
  function seek_au_mul (line 2361) | function seek_au_mul(mul) {
  function seek_au_rel (line 2366) | function seek_au_rel(sec) {
  function seek_au_sec (line 2371) | function seek_au_sec(seek) {
  function song_skip (line 2389) | function song_skip(n, dirskip) {
  function next_song (line 2405) | function next_song(e) {
  function last_song (line 2424) | function last_song(e) {
  function prev_song (line 2438) | function prev_song(e) {
  function dl_song (line 2450) | function dl_song() {
  function sel_song (line 2462) | function sel_song() {
  function playpause (line 2472) | function playpause(e) {
  function mplay (line 2490) | function mplay(e) {
  function mpause (line 2498) | function mpause(e) {
  function repreload (line 2570) | function repreload() {
  function updater_impl (line 2574) | function updater_impl() {
  function ev_play (line 2674) | function ev_play(e) {
  function start_actx (line 2688) | function start_actx() {
  function setvis (line 2732) | function setvis(vis) {
  function add_ss (line 2889) | function add_ss() {
  function add_eq (line 2895) | function add_eq() {
  function eq_step (line 2978) | function eq_step(e) {
  function adj_band (line 2994) | function adj_band(that, step) {
  function adj_drc (line 3020) | function adj_drc() {
  function adj_ss (line 3045) | function adj_ss() {
  function eq_mod (line 3068) | function eq_mod(e) {
  function eq_keydown (line 3073) | function eq_keydown(e) {
  function showdrc (line 3079) | function showdrc() {
  function play (line 3167) | function play(tid, is_ev, seek) {
  function scroll2playing (line 3316) | function scroll2playing() {
  function evau_end (line 3325) | function evau_end(e) {
  function evau_error (line 3337) | function evau_error(e) {
  function autoplay_blocked (line 3427) | function autoplay_blocked(seek) {
  function scan_hash (line 3448) | function scan_hash(v) {
  function eval_hash (line 3475) | function eval_hash() {
  function setsb (line 3595) | function setsb() {
  function setacmp (line 3602) | function setacmp() {
  function read_dsort (line 3617) | function read_dsort(txt) {
  function getsort (line 3644) | function getsort() {
  function sortfiles (line 3657) | function sortfiles(nodes) {
  function fmt_ren (line 3749) | function fmt_ren(re, md, fmt) {
  function enre_rw_edit (line 3847) | function enre_rw_edit() {
  function fs_abrt (line 3853) | function fs_abrt() {
  function setexp (line 4031) | function setexp(a, b) {
  function setdef (line 4043) | function setdef() {
  function shspf (line 4078) | function shspf() {
  function shr_cb (line 4102) | function shr_cb() {
  function sadv (line 4299) | function sadv() {
  function rn_ok (line 4308) | function rn_ok(n, ok) {
  function rn_reset (line 4313) | function rn_reset(n) {
  function rn_cancel (line 4318) | function rn_cancel(e) {
  function spresets (line 4340) | function spresets() {
  function rn_apply (line 4437) | function rn_apply(e) {
  function rn_apply_loop (line 4458) | function rn_apply_loop() {
  function deleter (line 4511) | function deleter(err) {
  function delete_cb (line 4528) | function delete_cb() {
  function paster (line 4785) | function paster() {
  function paste_cb (line 4810) | function paste_cb() {
  function okgo (line 4821) | function okgo() {
  function rn_apply (line 4843) | function rn_apply(e) {
  function rn_skip (line 4852) | function rn_skip(e) {
  function rn_cancel (line 4861) | function rn_cancel(e) {
  function setcnmt (line 4870) | function setcnmt(sel) {
  function onmsg (line 4908) | function onmsg(msg) {
  function loading (line 5156) | function loading(e) {
  function load_cb (line 5173) | function load_cb(e) {
  function render (line 5183) | function render(doc, no_push) {
  function setln (line 5560) | function setln(v) {
  function setsz (line 5572) | function setsz(v) {
  function gclick1 (line 5583) | function gclick1(e) {
  function gclick2 (line 5590) | function gclick2(e) {
  function gclick (line 5597) | function gclick(e, dbl) {
  function loadgrid (line 5725) | function loadgrid() {
  function th_onload (line 5953) | function th_onload(el) {
  function tree_scrollto (line 5958) | function tree_scrollto(e) {
  function tree_scrolltoo (line 5965) | function tree_scrolltoo(q) {
  function tree_neigh (line 5985) | function tree_neigh(n, ratelimit) {
  function tree_up (line 6021) | function tree_up(justgo) {
  function hkhelp (line 6045) | function hkhelp() {
  function fselfunw (line 6073) | function fselfunw(e, ae, d, rem) {
  function srch_msg (line 6422) | function srch_msg(err, txt) {
  function ev_search_input (line 6432) | function ev_search_input() {
  function ev_search_keydown (line 6457) | function ev_search_keydown(e) {
  function try_search (line 6462) | function try_search(v) {
  function set_vq (line 6471) | function set_vq() {
  function encode_query (line 6481) | function encode_query() {
  function do_search (line 6570) | function do_search() {
  function xhr_search_results (line 6584) | function xhr_search_results() {
  function unsearch (line 6676) | function unsearch(e) {
  function moar (line 6686) | function moar(e) {
  function ev_load_m3u (line 6696) | function ev_load_m3u(e) {
  function load_m3u (line 6712) | function load_m3u(url) {
  function render_m3u (line 6721) | function render_m3u() {
  function aligngriditems (line 6784) | function aligngriditems() {
  function setwrap (line 6889) | function setwrap(v) {
  function setidxh (line 6895) | function setidxh(v) {
  function unmenter (line 6949) | function unmenter() {
  function onscroll (line 6971) | function onscroll() {
  function onscroll2 (line 6976) | function onscroll2() {
  function onresize (line 7060) | function onresize(e) {
  function get_tree (line 7099) | function get_tree(top, dst, rst) {
  function reload_tree (line 7205) | function reload_tree() {
  function compy (line 7253) | function compy() {
  function menter (line 7264) | function menter(e) {
  function mleave (line 7275) | function mleave(e) {
  function bad_proxy (line 7280) | function bad_proxy(e) {
  function treegrow (line 7343) | function treegrow(e) {
  function recvls (line 7358) | function recvls() {
  function asdf (line 7620) | function asdf() {
  function memo_dk (line 7685) | function memo_dk(vp, k) {
  function parsetree (line 7771) | function parsetree(res, top) {
  function scaletree (line 7812) | function scaletree(e) {
  function enspin (line 7868) | function enspin(i) {
  function apply_perms (line 7912) | function apply_perms(res) {
  function tr2id (line 8027) | function tr2id(tr) {
  function find_file_col (line 8037) | function find_file_col(txt) {
  function mk_files_header (line 8058) | function mk_files_header(taglist) {
  function hcols_click (line 8110) | function hcols_click(e) {
  function set_key_notation (line 8308) | function set_key_notation() {
  function load_notation (line 8313) | function load_notation(notation) {
  function render (line 8324) | function render() {
  function try_render (line 8353) | function try_render() {
  function freshen (line 8397) | function freshen() {
  function freshen (line 8454) | function freshen() {
  function onch (line 8464) | function onch(e) {
  function setfmt (line 8468) | function setfmt(fmt) {
  function freshen (line 8479) | function freshen() {
  function setlang (line 8489) | function setlang(e) {
  function render (line 8531) | function render() {
  function try_render (line 8564) | function try_render() {
  function change_fmt (line 8573) | function change_fmt(e) {
  function cb (line 8882) | function cb() {
  function cb (line 8940) | function cb() {
  function show_md (line 9033) | function show_md(md, name, div, url, depth) {
  function set_tabindex (line 9115) | function set_tabindex() {
  function show_readme (line 9122) | function show_readme(md, n) {
  function sandbox (line 9135) | function sandbox(tgt, rules, allow, cls, html) {
  function ev_row_tgl (line 9268) | function ev_row_tgl(e) {
  function unpost_load_cb (line 9293) | function unpost_load_cb() {
  function linklist (line 9360) | function linklist() {
  function unpost_delete_cb (line 9370) | function unpost_delete_cb() {
  function goto_unpost (line 9489) | function goto_unpost(e) {
  function wintitle (line 9494) | function wintitle(txt, noname) {
  function persist_scroll (line 9524) | function persist_scroll() {
  function restore_scroll (line 9538) | function restore_scroll() {
  function mktemp (line 9623) | function mktemp(is_dir) {
  function show (line 9717) | function show(x, y, target, isGrid) {
  function reload_mp (line 9813) | function reload_mp() {
  function reload_browser (line 9840) | function reload_browser() {
  function unbox (line 9894) | function unbox() {
  function getpp (line 9911) | function getpp(e) {
  function sel_toggle (line 9916) | function sel_toggle(el, m) {
  function bob (line 9928) | function bob(b1, b2) {
  function sel_start (line 9933) | function sel_start(e) {
  function start_drag (line 9955) | function start_drag() {
  function sel_move (line 9966) | function sel_move(e) {
  function sel_end (line 9998) | function sel_end(e) {
  function dsel_init (line 10013) | function dsel_init() {
  function detectSilence (line 10065) | function detectSilence() {

FILE: copyparty/web/dbg-audio.js
  function draw (line 37) | function draw() {

FILE: copyparty/web/md.js
  function copydom (line 71) | function copydom(src, dst, lv) {
  function convert_markdown (line 196) | function convert_markdown(md_text, dest_dom) {
  function init_toc (line 359) | function init_toc() {

FILE: copyparty/web/md2.js
  function genmapq (line 35) | function genmapq(dom, query) {
  function genmap (line 76) | function genmap(dom, oldmap) {
  function Modpoll (line 237) | function Modpoll() {
  function save (line 337) | function save(e) {
  function save_cb (line 374) | function save_cb() {
  function run_savechk (line 410) | function run_savechk(lastmod, txt, btn, ntry) {
  function savechk_cb (line 424) | function savechk_cb() {
  function linebounds (line 472) | function linebounds(just_car, greedy_growth) {
  function getsel (line 507) | function getsel() {
  function setsel (line 517) | function setsel(s) {
  function md_cut (line 532) | function md_cut(cut) {
  function md_indent (line 546) | function md_indent(dedent) {
  function md_header (line 563) | function md_header(dedent) {
  function md_home (line 580) | function md_home(shift) {
  function md_newline (line 611) | function md_newline() {
  function md_backspace (line 636) | function md_backspace() {
  function md_p_jump (line 665) | function md_p_jump(down) {
  function reLastIndexOf (line 688) | function reLastIndexOf(txt, ptn, end) {
  function fmt_json (line 703) | function fmt_json(e) {
  function fmt_json2 (line 712) | function fmt_json2() {
  function fmt_table (line 736) | function fmt_table(e) {
  function fmt_table2 (line 745) | function fmt_table2() {
  function mark_uni (line 893) | function mark_uni(e) {
  function iter_uni (line 909) | function iter_uni(e) {
  function cfg_uni (line 931) | function cfg_uni(e) {

FILE: copyparty/web/mde.js
  function set_jumpto (line 73) | function set_jumpto() {
  function jumpto (line 77) | function jumpto(ev) {
  function md_changed (line 92) | function md_changed(mde, on_srv) {
  function save (line 103) | function save(mde) {
  function save_cb (line 136) | function save_cb() {
  function save_chk (line 182) | function save_chk() {

FILE: copyparty/web/rups.js
  function render (line 5) | function render() {
  function ask (line 41) | function ask(e) {

FILE: copyparty/web/shares.js
  function rm (line 9) | function rm() {
  function bump (line 18) | function bump() {
  function cb (line 28) | function cb() {
  function qr (line 40) | function qr(e) {
  function showqr (line 55) | function showqr(href) {

FILE: copyparty/web/splash.js
  function redo (line 81) | function redo(msg) {
  function mok (line 87) | function mok(v) {
  function stars (line 95) | function stars() {

FILE: copyparty/web/svcs.js
  function add_dls (line 14) | function add_dls() {
  function esetos (line 34) | function esetos(e) {
  function setos (line 39) | function setos(os) {
  function setpw (line 57) | function setpw(e) {
  function askpw (line 77) | function askpw() {

FILE: copyparty/web/up2k.js
  function goto_up2k (line 17) | function goto_up2k() {
  function up2k_flagbus (line 60) | function up2k_flagbus() {
  function U2pvis (line 157) | function U2pvis(act, btns, uc, st) {
  function Donut (line 630) | function Donut(uc, st) {
  function sfx_nice (line 751) | function sfx_nice() {
  function fsearch_explain (line 783) | function fsearch_explain(n) {
  function up2k_init (line 794) | function up2k_init(subtle) {
  function warn_uploader_busy (line 3433) | function warn_uploader_busy(e) {

FILE: copyparty/web/util.js
  function mknod (line 89) | function mknod(et, eid, html) {
  function qsr (line 102) | function qsr(sel) {
  function esc (line 112) | function esc(txt) {
  function basenames (line 122) | function basenames(txt) {
  function vis_exh (line 168) | function vis_exh(msg, url, lineNo, columnNo, error) {
  function ignex (line 290) | function ignex(all) {
  function noop (line 305) | function noop() { }
  function ctrl (line 308) | function ctrl(e) {
  function anymod (line 313) | function anymod(e, shift_ok) {
  function ev (line 319) | function ev(e) {
  function noope (line 344) | function noope(e) {
  function import_js (line 433) | function import_js(url, cb, ecb) {
  function unsmart (line 448) | function unsmart(txt) {
  function namesan (line 457) | function namesan(txt, win, fslash) {
  function crc32 (line 496) | function crc32(str) {
  function randstr (line 505) | function randstr(len) {
  function clmod (line 523) | function clmod(el, cls, add) {
  function clgot (line 555) | function clgot(el, cls) {
  function setcvar (line 567) | function setcvar(k, v) {
  function yscroll (line 586) | function yscroll() {
  function xscroll (line 599) | function xscroll() {
  function showsort (line 614) | function showsort(tab) {
  function st_cmp_num (line 649) | function st_cmp_num(a, b) {
  function st_cmp_nat (line 658) | function st_cmp_nat(a, b) {
  function st_cmp_gen (line 667) | function st_cmp_gen(a, b) {
  function sortTable (line 676) | function sortTable(table, col, cb) {
  function makeSortable (line 745) | function makeSortable(table, cb) {
  function assert_vp (line 759) | function assert_vp(path) {
  function linksplit (line 769) | function linksplit(rp, base, id) {
  function vsplit (line 813) | function vsplit(vp) {
  function vjoin (line 825) | function vjoin(p1, p2) {
  function addq (line 848) | function addq(url, q) {
  function uricom_enc (line 857) | function uricom_enc(txt, do_fb_enc) {
  function url_enc (line 870) | function url_enc(txt) {
  function uricom_dec (line 881) | function uricom_dec(txt) {
  function uricom_sdec (line 892) | function uricom_sdec(txt) {
  function uricom_adec (line 903) | function uricom_adec(arr, li) {
  function get_evpath (line 914) | function get_evpath() {
  function noq_href (line 927) | function noq_href(el) {
  function pad2 (line 932) | function pad2(v) {
  function unix2iso (line 937) | function unix2iso(ts) {
  function unix2iso_localtime (line 942) | function unix2iso_localtime(ts) {
  function s2ms (line 955) | function s2ms(s) {
  function f2f (line 970) | function f2f(val, nd) {
  function humansize (line 983) | function humansize(b, terse) {
  function humansize_su (line 989) | function humansize_su(b) {
  function humansize_0 (line 994) | function humansize_0(b) {
  function humansize_1 (line 997) | function humansize_1(b) {
  function humansize_2g (line 1000) | function humansize_2g(b) {
  function humansize_3g (line 1004) | function humansize_3g(b) {
  function humansize_4g (line 1008) | function humansize_4g(b) {
  function humansize_5g (line 1012) | function humansize_5g(b) {
  function humansize_2 (line 1016) | function humansize_2(b) {
  function humansize_3 (line 1019) | function humansize_3(b) {
  function humansize_4 (line 1022) | function humansize_4(b) {
  function humansize_5 (line 1025) | function humansize_5(b) {
  function humansize_2c (line 1028) | function humansize_2c(b) {
  function humansize_3c (line 1032) | function humansize_3c(b) {
  function humansize_4c (line 1036) | function humansize_4c(b) {
  function humansize_5c (line 1040) | function humansize_5c(b) {
  function humansize_fuzzy (line 1044) | function humansize_fuzzy(b) {
  function humantime (line 1073) | function humantime(v) {
  function shumantime (line 1086) | function shumantime(v, long) {
  function lhumantime (line 1112) | function lhumantime(v) {
  function clamp (line 1134) | function clamp(v, a, b) {
  function has (line 1139) | function has(haystack, needle) {
  function apop (line 1150) | function apop(arr, v) {
  function jcp1 (line 1157) | function jcp1(obj) {
  function jcp2 (line 1162) | function jcp2(src) {
  function sdrop (line 1184) | function sdrop(key) {
  function sread (line 1191) | function sread(key, al) {
  function swrite (line 1201) | function swrite(key, val) {
  function jread (line 1211) | function jread(key, fb) {
  function jwrite (line 1225) | function jwrite(key, val) {
  function icfg_get (line 1232) | function icfg_get(name, defval) {
  function fcfg_get (line 1236) | function fcfg_get(name, defval) {
  function scfg_get (line 1249) | function scfg_get(name, defval) {
  function bcfg_get (line 1262) | function bcfg_get(name, defval) {
  function bcfg_set (line 1277) | function bcfg_set(name, val) {
  function bcfg_upd_ui (line 1283) | function bcfg_upd_ui(name, val) {
  function bcfg_bind (line 1296) | function bcfg_bind(obj, oname, cname, defval, cb, un_ev) {
  function scfg_bind (line 1314) | function scfg_bind(obj, oname, cname, defval, cb) {
  function setck (line 1329) | function setck(v, cb) {
  function hist_push (line 1343) | function hist_push(url) {
  function hist_replace (line 1351) | function hist_replace(url) {
  function sethash (line 1359) | function sethash(hv) {
  function dl_file (line 1369) | function dl_file(url) {
  function cliptxt (line 1381) | function cliptxt(txt, ok) {
  function Debounce (line 1402) | function Debounce(delay) {
  function lf2br (line 1676) | function lf2br(txt) {
  function hunpre (line 1686) | function hunpre(txt) {
  function unpre (line 1689) | function unpre(txt) {
  function winpopup (line 2021) | function winpopup(txt) {
  function repl_load (line 2033) | function repl_load() {
  function repl (line 2086) | function repl(e) {
  function load_md_plug (line 2123) | function load_md_plug(md_text, plug_type, defer) {
  function md_thumbs (line 2175) | function md_thumbs(md) {
  function md_th_set (line 2203) | function md_th_set() {
  function md_th_click (line 2208) | function md_th_click(e) {
  function cprop (line 2284) | function cprop(name) {
  function bchrome (line 2289) | function bchrome() {
  function xhrchk (line 2316) | function xhrchk(xhr, prefix, e404, lvl, tag) {

FILE: copyparty/web/w.hash.js
  function hex2u8 (line 4) | function hex2u8(txt) {
  function esc (line 7) | function esc(txt) {
  function load_fb (line 30) | function load_fb() {

FILE: docs/chunksizes.py
  function humansize (line 9) | def humansize(sz, terse=False):
  function up2k_chunksize (line 24) | def up2k_chunksize(filesize):
  function main (line 37) | def main():

FILE: scripts/deps-docker/genprism.py
  function read_json (line 12) | def read_json(path):
  function get_prism_version (line 16) | def get_prism_version(prism_path):
  function get_prism_components (line 22) | def get_prism_components(prism_path):
  function parse_prism_configuration (line 28) | def parse_prism_configuration(url_str):
  function paths_of_component (line 35) | def paths_of_component(prism_path, kind, components, name, minified):
  function read_component_contents (line 68) | def read_component_contents(kv_paths):
  function get_language_dependencies (line 72) | def get_language_dependencies(components, name):
  function make_header (line 83) | def make_header(prism_path, url):
  function make_core (line 89) | def make_core(prism_path, components, minified):
  function make_theme (line 94) | def make_theme(prism_path, components, name, minified):
  function make_language (line 99) | def make_language(prism_path, components, name, minified):
  function make_languages (line 104) | def make_languages(prism_path, components, names, minified):
  function make_plugin (line 122) | def make_plugin(prism_path, components, name, minified):
  function make_plugins (line 127) | def make_plugins(prism_path, components, names, minified):
  function make_code (line 132) | def make_code(prism_path, url, minified):
  function join_code (line 155) | def join_code(kv_code):
  function write_code (line 166) | def write_code(kv_code, js_out, css_out):
  function parse_args (line 178) | def parse_args():
  function main (line 191) | def main():

FILE: scripts/deps-docker/shiftbase.py
  function main (line 7) | def main():

FILE: scripts/docker/base/patch/ffmpeg/libavcodec/aacps.c
  function hybrid_analysis (line 15) | static void hybrid_analysis(PSDSPContext *dsp, INTFLOAT out[91][32][2],
  function hybrid_synthesis (line 19) | static void hybrid_synthesis(PSDSPContext *dsp, INTFLOAT out[2][38][64],
  function decorrelation (line 22) | static void decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const I...
  function av_cold (line 26) | av_cold void AAC_RENAME(ff_ps_init)(void) {}

FILE: scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr.c
  function make_bands (line 23) | static void make_bands(int16_t* bands, int start, int stop, int num_band...
  function sbr_dequant (line 25) | static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) {}
  function sbr_hf_inverse_filter (line 27) | static void sbr_hf_inverse_filter(SBRDSPContext *dsp,
  function sbr_chirp (line 31) | static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) {}
  function sbr_gain_calc (line 33) | static void sbr_gain_calc(SpectralBandReplication *sbr,
  function sbr_hf_assemble (line 36) | static void sbr_hf_assemble(float Y1[38][64][2],

FILE: scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr_fixed.c
  function fixed_log (line 25) | static int fixed_log(int x) {return 1;}
  function make_bands (line 27) | static void make_bands(int16_t* bands, int start, int stop, int num_band...
  function sbr_dequant (line 29) | static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) {}
  function sbr_hf_inverse_filter (line 31) | static void sbr_hf_inverse_filter(SBRDSPContext *dsp,
  function sbr_chirp (line 35) | static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) {}
  function sbr_gain_calc (line 37) | static void sbr_gain_calc(SpectralBandReplication *sbr,
  function sbr_hf_assemble (line 40) | static void sbr_hf_assemble(int Y1[38][64][2],

FILE: scripts/fusefuzz.py
  function chk (line 14) | def chk(fsz, rsz, ofs0, shift, ofs, rf, vf):
  function main (line 34) | def main():

FILE: scripts/help2html.py
  function readclip (line 19) | def readclip():
  function cnv (line 36) | def cnv(src):
  function main (line 130) | def main():

FILE: scripts/pyinstaller/loader.py
  function confirm (line 42) | def confirm(rv):
  function ckck (line 54) | def ckck():
  function meicln (line 78) | def meicln(mod):
  function meichk (line 104) | def meichk():

FILE: scripts/sfx.py
  function eprint (line 57) | def eprint(*a, **ka):
  function msg (line 62) | def msg(*a, **ka):
  function testptn1 (line 72) | def testptn1():
  function testptn2 (line 83) | def testptn2():
  function testptn3 (line 97) | def testptn3():
  function testchk (line 110) | def testchk(cdata):
  function encode (line 164) | def encode(data, size, cksum, ver, ts):
  function makesfx (line 236) | def makesfx(tar_src, ver, ts):
  function u8 (line 245) | def u8(gen):
  function yieldfile (line 255) | def yieldfile(fn):
  function hashfile (line 262) | def hashfile(fn):
  function unpack (line 270) | def unpack():
  function get_payload (line 364) | def get_payload():
  function confirm (line 390) | def confirm(rv):
  function run (line 403) | def run(tmp, j2, ftp):
  function run_i (line 431) | def run_i(ld):
  function run_s (line 443) | def run_s(ld):
  function main (line 460) | def main():

FILE: scripts/speedtest-fs.py
  function get_spd (line 20) | def get_spd(nbyte, nfiles, nsec):
  class Inf (line 31) | class Inf(object):
    method __init__ (line 32) | def __init__(self, t0):
    method msg (line 49) | def msg(self, fn, n_read):
    method err (line 53) | def err(self, fn):
    method print_msgs (line 57) | def print_msgs(self):
    method report (line 72) | def report(self, fn, n_byte, n_sec):
    method done (line 78) | def done(self):
  function get_files (line 83) | def get_files(dir_path):
  function worker (line 95) | def worker(q, inf, read_sz):
  function sighandler (line 120) | def sighandler(signo, frame):
  function main (line 124) | def main():

FILE: scripts/strip_hints/a.py
  function pr (line 14) | def pr(m):
  function uh (line 19) | def uh(top):
  function uh1 (line 49) | def uh1(fp):
  function uh2 (line 57) | def uh2(fp):

FILE: scripts/test/ptrav.py
  function genlen (line 12) | def genlen(ubase, port, ntot, nth, wlen):
  function main (line 49) | def main():

FILE: scripts/test/race.py
  class Conn (line 11) | class Conn(object):
    method __init__ (line 12) | def __init__(self, ip, port):
    method get (line 16) | def get(self, vpath):
    method get_json (line 33) | def get_json(self, vpath):
  class CState (line 38) | class CState(threading.Thread):
    method __init__ (line 39) | def __init__(self, cs):
    method run (line 45) | def run(self):
  function allget (line 70) | def allget(cs, urls):
  function main (line 81) | def main():

FILE: scripts/test/smoketest.py
  class Cpp (line 16) | class Cpp(object):
    method __init__ (line 17) | def __init__(self, args):
    method _run (line 29) | def _run(self):
    method stop (line 32) | def stop(self, wait):
    method clean (line 39) | def clean(self):
    method await_idle (line 45) | def await_idle(self, ub, timeout):
  function tc1 (line 63) | def tc1(vflags):
  function run (line 219) | def run(tc, *a):
  function main (line 229) | def main():

FILE: scripts/tl.js
  function langmod (line 12) | function langmod() {

FILE: scripts/tl.py
  function generate_javascript (line 32) | def generate_javascript(lang3, native_name, tl_browser):
  function die (line 134) | def die(*a):
  function main (line 139) | def main():

FILE: scripts/uncomment.py
  function uncomment (line 17) | def uncomment(fpath):
  function main (line 83) | def main():

FILE: scripts/ziploader.py
  function msg (line 13) | def msg(*a, **ka):
  function confirm (line 21) | def confirm(rv):
  function run (line 34) | def run():
  function main (line 40) | def main():

FILE: setup.py
  class clean2 (line 44) | class clean2(Command):
    method initialize_options (line 48) | def initialize_options(self):
    method finalize_options (line 51) | def finalize_options(self):
    method run (line 54) | def run(self):

FILE: tests/ptrav.py
  function nolog (line 17) | def nolog(*a, **ka):
  function hdr (line 21) | def hdr(query):
  function curl (line 26) | def curl(args, asrv, url, binary=False):
  function genlen (line 36) | def genlen(ubase, ntot, nth, wlen):
  function main (line 66) | def main():

FILE: tests/run.py
  function rp (line 11) | def rp():

FILE: tests/test_cp.py
  class TestDedup (line 17) | class TestDedup(tu.TC):
    method setUp (line 18) | def setUp(self):
    method tearDown (line 21) | def tearDown(self):
    method reset (line 27) | def reset(self):
    method cinit (line 44) | def cinit(self):
    method test (line 53) | def test(self):
    method do_cp (line 88) | def do_cp(self, action):
    method propfind (line 99) | def propfind(self):
    method log (line 109) | def log(self, src, msg, c=0):

FILE: tests/test_dedup.py
  class TestDedup (line 18) | class TestDedup(tu.TC):
    method setUp (line 19) | def setUp(self):
    method tearDown (line 36) | def tearDown(self):
    method reset (line 42) | def reset(self):
    method cinit (line 50) | def cinit(self):
    method test_a (line 59) | def test_a(self):
    method test (line 100) | def test(self):
    method do_tc (line 142) | def do_tc(self, cm1, cm2, cm3, irm):
    method do_post (line 172) | def do_post(self, dn, fn, fi, first):
    method do_post_hs (line 178) | def do_post_hs(self, dn, fn, fi, first, replace=False):
    method do_post_data (line 193) | def do_post_data(self, dn, fn, fi, first, sfn, hs):
    method handshake (line 205) | def handshake(self, dn, fn, fi, replace=False):
    method put_chunk (line 218) | def put_chunk(self, dn, wark, chash, data):
    method curl (line 235) | def curl(self, url, binary=False, meth=None):
    method log (line 246) | def log(self, src, msg, c=0):

FILE: tests/test_dots.py
  function hdr (line 26) | def hdr(query, uname, extra=""):
  class TestDots (line 31) | class TestDots(unittest.TestCase):
    method __init__ (line 32) | def __init__(self, *a, **ka):
    method setUp (line 37) | def setUp(self):
    method tearDown (line 41) | def tearDown(self):
    method cinit (line 47) | def cinit(self):
    method test_dots (line 60) | def test_dots(self):
    method test_dk_fk (line 153) | def test_dk_fk(self):
    method tardir (line 371) | def tardir(self, url, uname):
    method tarsel (line 381) | def tarsel(self, url, uname, sel):
    method curl (line 392) | def curl(self, url, uname, binary=False, req=b""):
    method log (line 402) | def log(self, src, msg, c=0):

FILE: tests/test_dxml.py
  function _parse (line 14) | def _parse(txt):
  class TestDXML (line 22) | class TestDXML(unittest.TestCase):
    method test_qbe (line 23) | def test_qbe(self):
    method test_ent_file (line 32) | def test_ent_file(self):
    method test_ent_ext (line 45) | def test_ent_ext(self):
    method test_dtd (line 53) | def test_dtd(self):
    method test3 (line 64) | def test3(self):
    method test4 (line 81) | def test4(self):
    method test5 (line 125) | def test5(self):

FILE: tests/test_hooks.py
  function hdr (line 21) | def hdr(query):
  class TestHooks (line 26) | class TestHooks(tu.TC):
    method setUp (line 27) | def setUp(self):
    method tearDown (line 31) | def tearDown(self):
    method reset (line 35) | def reset(self):
    method cinit (line 43) | def cinit(self):
    method test (line 49) | def test(self):
    method test2 (line 81) | def test2(self):
    method makehook (line 116) | def makehook(self, hs):
    method put (line 120) | def put(self, url):
    method bup (line 130) | def bup(self, url):
    method curl (line 149) | def curl(self, url, binary=False):
    method log (line 158) | def log(self, src, msg, c=0):

FILE: tests/test_httpcli.py
  function hdr (line 21) | def hdr(query):
  class TestHttpCli (line 26) | class TestHttpCli(unittest.TestCase):
    method setUp (line 27) | def setUp(self):
    method tearDown (line 31) | def tearDown(self):
    method test (line 35) | def test(self):
    method can_rw (line 201) | def can_rw(self, fp):
    method in_dive (line 211) | def in_dive(self, top, fp):
    method put (line 227) | def put(self, url):
    method curl (line 237) | def curl(self, url, binary=False):
    method propfind (line 246) | def propfind(self, url, depth=1):
    method log (line 253) | def log(self, src, msg, c=0):

FILE: tests/test_idp.py
  class TestVFS (line 14) | class TestVFS(unittest.TestCase):
    method dump (line 15) | def dump(self, vfs):
    method log (line 18) | def log(self, src, msg, c=0):
    method nav (line 31) | def nav(self, au, vp):
    method assertAxs (line 34) | def assertAxs(self, axs, expected):
    method assertAxsAt (line 43) | def assertAxsAt(self, au, vp, expected):
    method assertNodes (line 47) | def assertNodes(self, vfs, expected):
    method assertNodesAt (line 51) | def assertNodesAt(self, au, vp, expected):
    method assertApEq (line 55) | def assertApEq(self, ap, rhs):
    method prep (line 61) | def prep(self):
    method test_1 (line 72) | def test_1(self):
    method test_2 (line 87) | def test_2(self):
    method test_3 (line 105) | def test_3(self):
    method test_4 (line 131) | def test_4(self):
    method test_5 (line 169) | def test_5(self):
    method test_6 (line 199) | def test_6(self):
    method test_7 (line 240) | def test_7(self):
    method test_8 (line 279) | def test_8(self):

FILE: tests/test_metrics.py
  function hdr (line 19) | def hdr(query):
  class TestMetrics (line 24) | class TestMetrics(tu.TC):
    method setUp (line 25) | def setUp(self):
    method tearDown (line 29) | def tearDown(self):
    method cinit (line 35) | def cinit(self):
    method test (line 51) | def test(self):
    method curl (line 97) | def curl(self, url, binary=False):
    method log (line 106) | def log(self, src, msg, c=0):

FILE: tests/test_mv.py
  class TestDedup (line 23) | class TestDedup(tu.TC):
    method setUp (line 24) | def setUp(self):
    method tearDown (line 27) | def tearDown(self):
    method reset (line 33) | def reset(self):
    method cinit (line 41) | def cinit(self):
    method test (line 50) | def test(self):
    method do_mv (line 126) | def do_mv(self, src, dst):
    method do_post (line 137) | def do_post(self, dn, fn, fi, first):
    method handshake (line 162) | def handshake(self, dn, fn, fi):
    method put_chunk (line 173) | def put_chunk(self, dn, wark, chash, data):
    method curl (line 190) | def curl(self, url, binary=False):
    method log (line 199) | def log(self, src, msg, c=0):

FILE: tests/test_shr.py
  class TestShr (line 20) | class TestShr(unittest.TestCase):
    method log (line 21) | def log(self, src, msg, c=0):
    method assertLD (line 33) | def assertLD(self, url, auth, els, edl):
    method setUp (line 56) | def setUp(self):
    method tearDown (line 77) | def tearDown(self):
    method cinit (line 81) | def cinit(self):
    method test1 (line 85) | def test1(self):
    method test2 (line 139) | def test2(self):
    method ls (line 196) | def ls(self, url: str, auth: bool):
    method curl (line 208) | def curl(self, url: str, binary=False):
    method post_json (line 217) | def post_json(self, url: str, data):

FILE: tests/test_utils.py
  class TestUtils (line 12) | class TestUtils(unittest.TestCase):
    method cmp (line 13) | def cmp(self, orig, t1, t2):
    method test_quotep (line 17) | def test_quotep(self):
    method test_unquote (line 26) | def test_unquote(self):

FILE: tests/test_vfs.py
  class TestVFS (line 17) | class TestVFS(unittest.TestCase):
    method setUp (line 18) | def setUp(self):
    method tearDown (line 21) | def tearDown(self):
    method dump (line 25) | def dump(self, vfs):
    method unfoo (line 28) | def unfoo(self, foo):
    method undot (line 34) | def undot(self, vfs, query, response):
    method ls (line 40) | def ls(self, vfs, vpath, uname):
    method log (line 52) | def log(self, src, msg, c=0):
    method assertAxs (line 55) | def assertAxs(self, dct, lst):
    method wipe_vfs (line 60) | def wipe_vfs(self, td):
    method test (line 77) | def test(self):

FILE: tests/test_webdav.py
  class TestHttpCli (line 103) | class TestHttpCli(TC):
    method setUp (line 104) | def setUp(self):
    method tearDown (line 108) | def tearDown(self):
    method test (line 113) | def test(self):
    method req (line 275) | def req(self, q):
    method log (line 282) | def log(self, src, msg, c=0):

FILE: tests/util.py
  function nah (line 26) | def nah(*a, **ka):
  function eprint (line 30) | def eprint(*a, **ka):
  function randbytes (line 46) | def randbytes(n):
  function runcmd (line 50) | def runcmd(argv):
  function chkcmd (line 58) | def chkcmd(argv):
  function get_ramdisk (line 66) | def get_ramdisk():
  function pfind2ls (line 128) | def pfind2ls(xml):
  class TC (line 132) | class TC(unittest.TestCase):
    method __init__ (line 133) | def __init__(self, *a, **ka):
    method assertStart (line 136) | def assertStart(self, member, container, msg=None):
  class Cfg (line 142) | class Cfg(Namespace):
    method __init__ (line 143) | def __init__(self, a=None, v=None, c=None, **ka0):
  class NullBroker (line 239) | class NullBroker(object):
    method __init__ (line 240) | def __init__(self, args, asrv):
    method say (line 244) | def say(self, *args):
    method ask (line 247) | def ask(self, *args):
  class VSock (line 251) | class VSock(object):
    method __init__ (line 252) | def __init__(self, buf):
    method recv (line 258) | def recv(self, sz):
    method send (line 263) | def send(self, buf):
    method getsockname (line 267) | def getsockname(self):
    method settimeout (line 270) | def settimeout(self, a):
  class VHub (line 274) | class VHub(object):
    method __init__ (line 275) | def __init__(self, args, asrv, log):
    method reload (line 282) | def reload(self, a, b):
  class VBrokerThr (line 286) | class VBrokerThr(BrokerThr):
    method __init__ (line 287) | def __init__(self, hub):
  class VHttpSrv (line 294) | class VHttpSrv(object):
    method __init__ (line 295) | def __init__(self, args, asrv, log):
    method cachebuster (line 321) | def cachebuster(self):
    method get_u2idx (line 324) | def get_u2idx(self):
    method shutdown (line 328) | def shutdown(self):
  class VHttpSrvUp2k (line 333) | class VHttpSrvUp2k(VHttpSrv):
    method __init__ (line 334) | def __init__(self, args, asrv, log):
    method shutdown (line 339) | def shutdown(self):
  class VHttpConn (line 345) | class VHttpConn(object):
    method __init__ (line 346) | def __init__(self, args, asrv, log, buf, use_up2k=False):
    method setbuf (line 378) | def setbuf(self, buf):
    method shutdown (line 383) | def shutdown(self):
Condensed preview — 448 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,173K chars).
[
  {
    "path": ".eslintrc.json",
    "chars": 186,
    "preview": "{\n    \"env\": {\n        \"browser\": true,\n        \"es2021\": true\n    },\n    \"extends\": \"eslint:recommended\",\n    \"parserOp"
  },
  {
    "path": ".gitattributes",
    "chars": 74,
    "preview": "* text eol=lf\n\n*.reg text eol=crlf\n\n*.png binary\n*.gif binary\n*.gz binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 2167,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: '9001'\n\n---\n\n<!-- NOTE:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 792,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: '9001'\n\n---\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/something-else.md",
    "chars": 84,
    "preview": "---\nname: Something else\nabout: \"┐(゚∀゚)┌\"\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/branch-rename.md",
    "chars": 186,
    "preview": "modernize your local checkout of the repo like so,\n```sh\ngit branch -m master hovudstraum\ngit fetch origin\ngit branch -u"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 204,
    "preview": "To show that your contribution is compatible with the MIT License, please include the following text somewhere in this P"
  },
  {
    "path": ".gitignore",
    "chars": 628,
    "preview": "# python\n__pycache__/\n*.py[cod]\n*$py.class\nMANIFEST.in\nMANIFEST\ncopyparty.egg-info/\n.venv/\n\n/buildenv/\n/build/\n/dist/\n/p"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 2083,
    "preview": "{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Run copyparty\",\n            \"type\": \"de"
  },
  {
    "path": ".vscode/launch.py",
    "chars": 1227,
    "preview": "#!/usr/bin/env python3\n\n# takes arguments from launch.json\n# is used by no_dbg in tasks.json\n# launches 10x faster than "
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1811,
    "preview": "{\n    \"workbench.colorCustomizations\": {\n        // https://ocv.me/dot/bifrost.html\n        \"terminal.background\": \"#1e1"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 429,
    "preview": "{\n    \"version\": \"2.0.0\",\n    \"tasks\": [\n        {\n            \"label\": \"pre\",\n            \"command\": \"true;rm -rf inc/*"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 942,
    "preview": "in the words of Abraham Lincoln:\n\n> Be excellent to each other... and... PARTY ON, DUDES!\n\nmore specifically I'll paraph"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4602,
    "preview": "* **found a bug?** [create an issue!](https://github.com/9001/copyparty/issues) or let me know in the [discord](https://"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2019 ed <oss@ocv.me>\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 189631,
    "preview": "<img src=\"https://github.com/9001/copyparty/raw/hovudstraum/docs/logo.svg\" width=\"250\" align=\"right\"/>\n\n### 💾🎉 copyparty"
  },
  {
    "path": "SECURITY.md",
    "chars": 350,
    "preview": "# Security Policy\n\nif you hit something extra juicy pls let me know on one of the following:\n* email -- `copyparty@ocv.z"
  },
  {
    "path": "bin/README.md",
    "chars": 3727,
    "preview": "# [`u2c.py`](u2c.py)\n* command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)\n* file uploads, file-search, a"
  },
  {
    "path": "bin/bubbleparty.sh",
    "chars": 468,
    "preview": "#!/bin/bash\n# usage: ./bubbleparty.sh ./copyparty-sfx.py ....\nbwrap \\\n  --unshare-all \\\n  --ro-bind /usr /usr \\\n  --ro-b"
  },
  {
    "path": "bin/dbtool.py",
    "chars": 8681,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport time\nimport shutil\nimport sqlite3\nimport argparse\n\nDB_VER1 = 3\nDB_VE"
  },
  {
    "path": "bin/handlers/README.md",
    "chars": 1440,
    "preview": "replace the standard 404 / 403 responses with plugins\n\n\n# usage\n\nload plugins either globally with `--on404 ~/dev/copypa"
  },
  {
    "path": "bin/handlers/caching-proxy.py",
    "chars": 1193,
    "preview": "# assume each requested file exists on another webserver and\n# download + mirror them as they're requested\n# (basically "
  },
  {
    "path": "bin/handlers/ip-ok.py",
    "chars": 146,
    "preview": "# disable permission checks and allow access if client-ip is 1.2.3.4\n\n\ndef main(cli, vn, rem):\n    if cli.ip == \"1.2.3.4"
  },
  {
    "path": "bin/handlers/never404.py",
    "chars": 233,
    "preview": "# create a dummy file and let copyparty return it\n\n\ndef main(cli, vn, rem):\n    print(\"hello\", cli.ip)\n\n    abspath = vn"
  },
  {
    "path": "bin/handlers/nooo.py",
    "chars": 282,
    "preview": "# reply with an endless \"noooooooooooooooooooooooo\"\n\n\ndef say_no():\n    yield b\"n\"\n    while True:\n        yield b\"o\" * "
  },
  {
    "path": "bin/handlers/randpic.py",
    "chars": 986,
    "preview": "import os\nimport random\nfrom urllib.parse import quote\n\n\n# assuming /foo/bar/ is a valid URL but /foo/bar/randpic.png do"
  },
  {
    "path": "bin/handlers/redirect.py",
    "chars": 1910,
    "preview": "# if someone hits a 404, redirect them to another location\n\n\ndef send_http_302_temporary_redirect(cli, new_path):\n    \"\""
  },
  {
    "path": "bin/handlers/sorry.py",
    "chars": 202,
    "preview": "# sends a custom response instead of the usual 404\n\n\ndef main(cli, vn, rem):\n    msg = f\"sorry {cli.ip} but {cli.vpath} "
  },
  {
    "path": "bin/hooks/README.md",
    "chars": 4051,
    "preview": "standalone programs which are executed by copyparty when an event happens (upload, file rename, delete, ...)\n\nthese prog"
  },
  {
    "path": "bin/hooks/discord-announce.py",
    "chars": 2240,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nimport json\nimport requests\nfrom copyparty.util import humansize, quotep\n\n\n_ = r\"\"\"\na"
  },
  {
    "path": "bin/hooks/image-noexif.py",
    "chars": 1697,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport subprocess as sp\n\n\n_ = r\"\"\"\nremove exif tags from uploaded images; t"
  },
  {
    "path": "bin/hooks/import-me.py",
    "chars": 1718,
    "preview": "#!/usr/bin/env python3\n\nfrom typing import Any\n\n_ = r\"\"\"\nthe fastest hook in the west\n(runs directly inside copyparty, n"
  },
  {
    "path": "bin/hooks/into-the-cache-it-goes.py",
    "chars": 3561,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nimport json\nimport shutil\nimport platform\nimport subprocess as sp\nfrom urllib.parse i"
  },
  {
    "path": "bin/hooks/msg-log.py",
    "chars": 3884,
    "preview": "#!/usr/bin/env python\n# coding: utf-8\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\nfrom __future__ import print_func"
  },
  {
    "path": "bin/hooks/notify.py",
    "chars": 1558,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport subprocess as sp\nfrom plyer import notification\n\n\n_ = r\"\"\"\nshow os n"
  },
  {
    "path": "bin/hooks/notify2.py",
    "chars": 1858,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport sys\nimport subprocess as sp\nfrom datetime import datetime, timezone"
  },
  {
    "path": "bin/hooks/podcast-normalizer.py",
    "chars": 3418,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport sys\nimport subprocess as sp\n\n\n_ = r\"\"\"\nsends all uploaded audio fil"
  },
  {
    "path": "bin/hooks/qbittorrent-magnet.py",
    "chars": 3806,
    "preview": "#!/usr/bin/env python3\n# coding: utf-8\n\nimport os\nimport sys\nimport json\nimport shutil\nimport subprocess as sp\n\n\n_ = r\"\""
  },
  {
    "path": "bin/hooks/reject-and-explain.py",
    "chars": 1794,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport re\nimport sys\n\n\n_ = r\"\"\"\nreject file upload (with a nice explanatio"
  },
  {
    "path": "bin/hooks/reject-extension.py",
    "chars": 801,
    "preview": "#!/usr/bin/env python3\n\nimport sys\n\n\n_ = r\"\"\"\nreject file uploads by file extension\n\nexample usage as global config:\n   "
  },
  {
    "path": "bin/hooks/reject-mimetype.py",
    "chars": 977,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nimport magic\n\n\n_ = r\"\"\"\nreject file uploads by mimetype\n\ndependencies (linux, macos):"
  },
  {
    "path": "bin/hooks/reject-ramdisk.py",
    "chars": 2141,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport threading\nfrom argparse import Namespace\n\nfrom jinja2.nodes import Name\nfrom co"
  },
  {
    "path": "bin/hooks/reloc-by-ext.py",
    "chars": 3974,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport re\nimport sys\n\n\n_ = r\"\"\"\nrelocate/redirect incoming uploads accordi"
  },
  {
    "path": "bin/hooks/usb-eject.js",
    "chars": 1744,
    "preview": "// see usb-eject.py for usage\n\nfunction usbclick() {\n    var o = QS('#treeul a[dst=\"/usb/\"]') || QS('#treepar a[dst=\"/us"
  },
  {
    "path": "bin/hooks/usb-eject.py",
    "chars": 2017,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport stat\nimport subprocess as sp\nimport sys\nfrom urllib.parse import unquote_to_byt"
  },
  {
    "path": "bin/hooks/wget-i.py",
    "chars": 3043,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport threading\nimport subprocess as sp\n\n\n_ = r\"\"\"\nuse copyparty as a file downloader"
  },
  {
    "path": "bin/hooks/wget.py",
    "chars": 2063,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport json\nimport subprocess as sp\n\n\n_ = r\"\"\"\nuse copyparty as a file down"
  },
  {
    "path": "bin/hooks/xiu-sha.py",
    "chars": 2943,
    "preview": "#!/usr/bin/env python3\n\nimport hashlib\nimport json\nimport sys\nfrom datetime import datetime, timezone\n\n\n_ = r\"\"\"\nthis ho"
  },
  {
    "path": "bin/hooks/xiu.py",
    "chars": 1256,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport sys\n\n\n_ = r\"\"\"\nthis hook prints absolute filepaths + total size\n\nuse this wit"
  },
  {
    "path": "bin/mtag/README.md",
    "chars": 3780,
    "preview": "standalone programs which take an audio file as argument\n\nyou may want to forget about all this fancy complicated stuff "
  },
  {
    "path": "bin/mtag/audio-bpm.py",
    "chars": 2033,
    "preview": "#!/usr/bin/env python\n\nimport os\nimport sys\nimport vamp\nimport tempfile\nimport numpy as np\nimport subprocess as sp\n\nfrom"
  },
  {
    "path": "bin/mtag/audio-key-slicing.py",
    "chars": 2411,
    "preview": "#!/usr/bin/env python\n\nimport re\nimport os\nimport sys\nimport tempfile\nimport subprocess as sp\n\nimport keyfinder\n\nfrom co"
  },
  {
    "path": "bin/mtag/audio-key.py",
    "chars": 1240,
    "preview": "#!/usr/bin/env python\n\nimport os\nimport sys\nimport tempfile\nimport subprocess as sp\n\ntry:\n    import keyfinder\n\n    PKF "
  },
  {
    "path": "bin/mtag/cksum.py",
    "chars": 1983,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nimport json\nimport struct\nimport base64\nimport hashlib\n\ntry:\n    from zlib_ng import "
  },
  {
    "path": "bin/mtag/exe.py",
    "chars": 2103,
    "preview": "#!/usr/bin/env python\n\nimport sys\nimport time\nimport json\nimport pefile\n\n\"\"\"\nretrieve exe info,\nexample for multivalue p"
  },
  {
    "path": "bin/mtag/file-ext.py",
    "chars": 122,
    "preview": "#!/usr/bin/env python\n\nimport sys\n\n\"\"\"\nexample that just prints the file extension\n\"\"\"\n\nprint(sys.argv[1].split(\".\")[-1]"
  },
  {
    "path": "bin/mtag/geotag.py",
    "chars": 1335,
    "preview": "import json\nimport re\nimport sys\n\nfrom copyparty.util import fsenc, runcmd\n\n\n\"\"\"\nuses exiftool to geotag images based on"
  },
  {
    "path": "bin/mtag/guestbook-read.py",
    "chars": 1609,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nfetch latest msg from guestbook and return as tag\n\nexample copyparty config to use this:\n  -"
  },
  {
    "path": "bin/mtag/guestbook.py",
    "chars": 3060,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nstore messages from users in an sqlite database\nwhich can be read from another mtp for examp"
  },
  {
    "path": "bin/mtag/image-noexif.py",
    "chars": 2602,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nremove exif tags from uploaded images\n\ndependencies:\n  exiftool\n\nabout:\n  creates a \"noexif\""
  },
  {
    "path": "bin/mtag/install-deps.sh",
    "chars": 8797,
    "preview": "#!/bin/bash\nset -e\n\n\n# install dependencies for audio-*.py\n#\n# linux/alpine: requires gcc g++ make cmake patchelf {pytho"
  },
  {
    "path": "bin/mtag/media-hash.py",
    "chars": 1306,
    "preview": "#!/usr/bin/env python\n\nimport re\nimport sys\nimport json\nimport time\nimport base64\nimport hashlib\nimport subprocess as sp"
  },
  {
    "path": "bin/mtag/mousepad.py",
    "chars": 704,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport subprocess as sp\n\n\n\"\"\"\nmtp test -- opens a texteditor\n\nusage:\n  -vsr"
  },
  {
    "path": "bin/mtag/rclone-upload.py",
    "chars": 1810,
    "preview": "#!/usr/bin/env python\n\nimport json\nimport os\nimport subprocess as sp\nimport sys\nimport time\n\ntry:\n    from copyparty.uti"
  },
  {
    "path": "bin/mtag/res/twitter-unmute.user.js",
    "chars": 688,
    "preview": "// ==UserScript==\n// @name         twitter-unmute\n// @namespace    http://ocv.me/\n// @version      0.1\n// @description  "
  },
  {
    "path": "bin/mtag/res/yt-ipr.conf",
    "chars": 1350,
    "preview": "# example config file to use copyparty as a youtube manifest collector,\n# use with copyparty like:  python copyparty.py "
  },
  {
    "path": "bin/mtag/res/yt-ipr.user.js",
    "chars": 1440,
    "preview": "// ==UserScript==\n// @name    youtube-playerdata-hub\n// @match   https://youtube.com/*\n// @match   https://*.youtube.com"
  },
  {
    "path": "bin/mtag/sleep.py",
    "chars": 106,
    "preview": "#!/usr/bin/env python\n\nimport time\nimport random\n\nv = random.random() * 6\ntime.sleep(v)\nprint(f\"{v:.2f}\")\n"
  },
  {
    "path": "bin/mtag/very-bad-idea.py",
    "chars": 6270,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nWARNING -- DANGEROUS PLUGIN --\n  if someone is able to upload files to a copyparty which is\n"
  },
  {
    "path": "bin/mtag/vidchk.py",
    "chars": 3456,
    "preview": "#!/usr/bin/env python3\n\nimport json\nimport re\nimport os\nimport sys\nimport subprocess as sp\n\ntry:\n    from copyparty.util"
  },
  {
    "path": "bin/mtag/wget.py",
    "chars": 2421,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nDEPRECATED -- replaced by event hooks;\nhttps://github.com/9001/copyparty/blob/hovudstraum/bi"
  },
  {
    "path": "bin/mtag/yt-ipr.py",
    "chars": 5515,
    "preview": "#!/usr/bin/env python\n\nimport re\nimport os\nimport sys\nimport gzip\nimport json\nimport base64\nimport string\nimport urllib."
  },
  {
    "path": "bin/partyfuse-streaming.py",
    "chars": 33878,
    "preview": "#!/usr/bin/env python3\nfrom __future__ import print_function, unicode_literals\n\n\"\"\"partyfuse-streaming: remote copyparty"
  },
  {
    "path": "bin/partyfuse.py",
    "chars": 37921,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"partyfuse: remote copyparty as a local filesystem\"\"\"\n__author__ = \"ed <copyparty@ocv.me>\"\n__c"
  },
  {
    "path": "bin/partyfuse2.py",
    "chars": 18528,
    "preview": "#!/usr/bin/env python3\nfrom __future__ import print_function, unicode_literals\n\n\"\"\"partyfuse2: remote copyparty as a loc"
  },
  {
    "path": "bin/partyjournal.py",
    "chars": 4136,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\npartyjournal.py: chronological history of uploads\n2021-12-31, v0.1, ed <irc.rizon.net>, MIT-"
  },
  {
    "path": "bin/prisonparty.sh",
    "chars": 4237,
    "preview": "#!/bin/bash\nset -e\n\n# runs copyparty (or any other program really) in a chroot\n#\n# assumption: these directories, and ev"
  },
  {
    "path": "bin/u2c.py",
    "chars": 54596,
    "preview": "#!/usr/bin/env python3\nfrom __future__ import print_function, unicode_literals\n\nS_VERSION = \"2.19\"\nS_BUILD_DT = \"2026-01"
  },
  {
    "path": "bin/unforget.py",
    "chars": 3009,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\nunforget.py: rebuild db from logfiles\n2022-09-07, v0.1, ed <irc.rizon.net>, MIT-Licensed\nhtt"
  },
  {
    "path": "bin/up2k.sh",
    "chars": 4393,
    "preview": "#!/bin/bash\nset -e\n\n# upload $datalen worth of generated data with a random filename,\n# using a single connection for al"
  },
  {
    "path": "bin/zmq-recv.py",
    "chars": 2354,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nimport zmq\n\n\"\"\"\nzmq-recv.py: demo zmq receiver\n2025-01-22, v1.0, ed <irc.rizon.net>, "
  },
  {
    "path": "contrib/README.md",
    "chars": 4157,
    "preview": "### [`plugins/`](plugins/)\n* example extensions\n\n### [`copyparty.bat`](copyparty.bat)\n* launches copyparty with no argum"
  },
  {
    "path": "contrib/apache/copyparty.conf",
    "chars": 873,
    "preview": "# if you would like to use unix-sockets (recommended),\n# you must run copyparty with one of the following:\n#\n#   -i unix"
  },
  {
    "path": "contrib/cfssl.sh",
    "chars": 2171,
    "preview": "#!/bin/bash\nset -e\n\ncat >/dev/null <<'EOF'\n\nNOTE: copyparty is now able to do this automatically;\nhowever you may wish t"
  },
  {
    "path": "contrib/copyparty.bat",
    "chars": 592,
    "preview": "exec python \"$(dirname \"$0\")\"/copyparty.py\n\n@rem on linux, the above will execute and the script will terminate\n@rem on "
  },
  {
    "path": "contrib/explorer-nothumbs-nofoldertypes.reg",
    "chars": 1106,
    "preview": "Windows Registry Editor Version 5.00\r\n\r\n; this will do 3 things, all optional:\r\n;  1) disable thumbnails\r\n;  2) delete "
  },
  {
    "path": "contrib/flameshot.sh",
    "chars": 289,
    "preview": "#!/bin/bash\nset -e\n\n# take a screenshot with flameshot and send it to copyparty;\n# the image url will be placed on your "
  },
  {
    "path": "contrib/haproxy/copyparty.conf",
    "chars": 552,
    "preview": "# this config is essentially two separate examples;\n#\n#   foo1 connects to copyparty using tcp, and\n#   foo2 uses unix-s"
  },
  {
    "path": "contrib/index.html",
    "chars": 856,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>💾🎉 redirect</title>\n\t<meta http-equiv=\"X-UA-Com"
  },
  {
    "path": "contrib/ishare.iscu",
    "chars": 220,
    "preview": "{\n  \"Name\": \"copyparty\",\n  \"RequestURL\": \"http://127.0.0.1:3923/screenshots/\",\n  \"Headers\": {\n    \"pw\": \"PUT_YOUR_PASSWO"
  },
  {
    "path": "contrib/lighttpd/subdomain.conf",
    "chars": 924,
    "preview": "# example usage for benchmarking:\n#\n#   taskset -c 1 lighttpd -Df ~/dev/copyparty/contrib/lighttpd/subdomain.conf\n#\n# li"
  },
  {
    "path": "contrib/lighttpd/subpath.conf",
    "chars": 1111,
    "preview": "# example usage for benchmarking:\n#\n#   taskset -c 1 lighttpd -Df ~/dev/copyparty/contrib/lighttpd/subpath.conf\n#\n# ligh"
  },
  {
    "path": "contrib/media-osd-bgone.ps1",
    "chars": 3100,
    "preview": "# media-osd-bgone.ps1: disable media-control OSD on win10do\n# v1.1, 2021-06-25, ed <irc.rizon.net>, MIT-licensed\n# http"
  },
  {
    "path": "contrib/nginx/copyparty.conf",
    "chars": 3600,
    "preview": "# look for \"max clients:\" when starting copyparty, as nginx should\n# not accept more consecutive clients than what copyp"
  },
  {
    "path": "contrib/nixos/modules/copyparty.nix",
    "chars": 13142,
    "preview": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  mkKeyValue =\n    key: value:\n    if value == true then\n      # sets "
  },
  {
    "path": "contrib/openrc/copyparty",
    "chars": 752,
    "preview": "#!/sbin/openrc-run\n\n# this will start `/usr/local/bin/copyparty-sfx.py`\n# and share '/mnt' with anonymous read+write\n#\n#"
  },
  {
    "path": "contrib/package/arch/PKGBUILD",
    "chars": 2458,
    "preview": "# Maintainer: icxes <dev.null@need.moe>\n# Contributor: Morgan Adamiec <morganamilo@archlinux.org>\n# NOTE: You generally "
  },
  {
    "path": "contrib/package/makedeb-mpr/PKGBUILD",
    "chars": 2060,
    "preview": "# Contributor: Beethoven <beethovenisadog@protonmail.com>\n\n\npkgname=copyparty\npkgver=1.20.12\npkgrel=1\npkgdesc=\"File serv"
  },
  {
    "path": "contrib/package/makedeb-mpr/copyparty.conf",
    "chars": 304,
    "preview": "## import all *.conf files from the current folder (/etc/copyparty.d)\n% ./\n\n# add additional .conf files to this folder;"
  },
  {
    "path": "contrib/package/makedeb-mpr/copyparty.service",
    "chars": 995,
    "preview": "# this will start `/usr/bin/copyparty-sfx.py`\n# and read config from `/etc/copyparty.d/*.conf`\n#\n# you probably want to:"
  },
  {
    "path": "contrib/package/makedeb-mpr/index.md",
    "chars": 154,
    "preview": "this is `/var/lib/copyparty-jail`, the fallback webroot when copyparty has not yet been configured\n\nplease add some `*.c"
  },
  {
    "path": "contrib/package/makedeb-mpr/prisonparty.service",
    "chars": 1124,
    "preview": "# this will start `/usr/bin/copyparty-sfx.py`\n# in a chroot, preventing accidental access elsewhere,\n# and read copypart"
  },
  {
    "path": "contrib/package/nix/copyparty/default.nix",
    "chars": 5332,
    "preview": "{\n  lib,\n  buildPythonApplication,\n  fetchurl,\n  util-linux,\n  python,\n  setuptools,\n  jinja2,\n  impacket,\n  pyopenssl,\n"
  },
  {
    "path": "contrib/package/nix/copyparty/pin.json",
    "chars": 195,
    "preview": "{\n    \"url\": \"https://github.com/9001/copyparty/releases/download/v1.20.12/copyparty-1.20.12.tar.gz\",\n    \"version\": \"1."
  },
  {
    "path": "contrib/package/nix/copyparty/update.py",
    "chars": 2309,
    "preview": "#!/usr/bin/env python3\n\n# Update the Nix package pin\n#\n# Usage: ./update.sh [PATH]\n# When the [PATH] is not set, it will"
  },
  {
    "path": "contrib/package/nix/overlay.nix",
    "chars": 838,
    "preview": "final: prev:\nlet\n  fullAttrs = {\n    withHashedPasswords = true;\n    withCertgen = true;\n    withThumbnails = true;\n    "
  },
  {
    "path": "contrib/package/nix/partftpy/default.nix",
    "chars": 590,
    "preview": "{\n  lib,\n  buildPythonPackage,\n  fetchurl,\n  setuptools,\n}:\nlet\n  pinData = lib.importJSON ./pin.json;\nin\n\nbuildPythonPa"
  },
  {
    "path": "contrib/package/nix/partftpy/pin.json",
    "chars": 187,
    "preview": "{\n    \"url\": \"https://github.com/9001/partftpy/releases/download/v0.4.0/partftpy-0.4.0.tar.gz\",\n    \"version\": \"0.4.0\",\n"
  },
  {
    "path": "contrib/package/nix/partftpy/update.py",
    "chars": 1263,
    "preview": "#!/usr/bin/env python3\n\n# Update the Nix package pin\n#\n# Usage: ./update.sh\n\nimport base64\nimport json\nimport hashlib\nim"
  },
  {
    "path": "contrib/package/rpm/copyparty.spec",
    "chars": 2479,
    "preview": "Name:           copyparty\nVersion:        $pkgver\nRelease:        $pkgrel\nLicense:        MIT\nGroup:          Utilities\n"
  },
  {
    "path": "contrib/plugins/README.md",
    "chars": 1553,
    "preview": "# example resource files\n\ncan be provided to copyparty to tweak things\n\n\n\n## example `.epilogue.html`\nsave one of these "
  },
  {
    "path": "contrib/plugins/banner.js",
    "chars": 3680,
    "preview": "(function() {\n\n// usage: copy this to '.banner.js' in your webroot,\n// and run copyparty with the following arguments:\n/"
  },
  {
    "path": "contrib/plugins/browser-icons.css",
    "chars": 1115,
    "preview": "/* video, alternative 1:\n   top-left icon, just like the other formats\n================================================="
  },
  {
    "path": "contrib/plugins/graft-thumbs.js",
    "chars": 3415,
    "preview": "// USAGE:\n//   place this file somewhere in the webroot and then\n//   python3 -m copyparty --js-browser /.res/graft-thum"
  },
  {
    "path": "contrib/plugins/meadup.js",
    "chars": 14134,
    "preview": "// USAGE:\n//   place this file somewhere in the webroot and then\n//   python3 -m copyparty --js-browser /memes/meadup.js"
  },
  {
    "path": "contrib/plugins/minimal-up2k.html",
    "chars": 1425,
    "preview": "<!--\n  NOTE: DEPRECATED; please use the javascript version instead:\n  https://github.com/9001/copyparty/blob/hovudstraum"
  },
  {
    "path": "contrib/plugins/minimal-up2k.js",
    "chars": 1860,
    "preview": "/*\n\nmakes the up2k ui REALLY minimal by hiding a bunch of stuff\n\nalmost the same as minimal-up2k.html except this one..."
  },
  {
    "path": "contrib/plugins/quickmove.js",
    "chars": 4646,
    "preview": "\"use strict\";\n\n\n// USAGE:\n//   place this file somewhere in the webroot, \n//   for example in a folder named \".res\" to h"
  },
  {
    "path": "contrib/plugins/rave.js",
    "chars": 5241,
    "preview": "/* untz untz untz untz */\n\n(function () {\n\n    var can, ctx, W, H, fft, buf, bars, barw, pv,\n        hue = 0,\n        ib"
  },
  {
    "path": "contrib/plugins/up2k-hook-ytid.js",
    "chars": 11190,
    "preview": "// way more specific example --\n// assumes all files dropped into the uploader have a youtube-id somewhere in the filena"
  },
  {
    "path": "contrib/plugins/up2k-hooks.js",
    "chars": 1397,
    "preview": "// hooks into up2k\n\nfunction up2k_namefilter(good_files, nil_files, bad_files, hooks) {\n    // is called when stuff is d"
  },
  {
    "path": "contrib/podman-systemd/README.md",
    "chars": 6474,
    "preview": "# copyparty with Podman and Systemd\n\nUse this configuration if you want to run copyparty in a Podman container, with the"
  },
  {
    "path": "contrib/podman-systemd/copyparty.conf",
    "chars": 2132,
    "preview": "[global]\n  e2dsa  # enable file indexing and filesystem scanning\n  e2ts   # and enable multimedia indexing\n  ansi   # an"
  },
  {
    "path": "contrib/podman-systemd/copyparty.container",
    "chars": 1500,
    "preview": "[Container]\n# It's recommended to replace :latest with a specific version\n# for example: docker.io/copyparty/ac:1.19.15\n"
  },
  {
    "path": "contrib/rc/copyparty",
    "chars": 725,
    "preview": "#!/bin/sh\n#\n# PROVIDE: copyparty\n# REQUIRE: networking\n# KEYWORD:\n\n. /etc/rc.subr\n\nname=\"copyparty\"\nrcvar=\"copyparty_ena"
  },
  {
    "path": "contrib/send-to-cpp.contextlet.json",
    "chars": 616,
    "preview": "{\n    \"code\": \"// https://addons.mozilla.org/en-US/firefox/addon/contextlets/\\n// https://github.com/davidmhammond/conte"
  },
  {
    "path": "contrib/setup-ashell.sh",
    "chars": 2015,
    "preview": "#!/bin/bash\n#\n# this script will install copyparty onto an iOS device (iPhone/iPad)\n#\n# step 1: install a-Shell:\n#   htt"
  },
  {
    "path": "contrib/sharex.sxcu",
    "chars": 388,
    "preview": "{\n  \"Version\": \"15.0.0\",\n  \"Name\": \"copyparty\",\n  \"DestinationType\": \"ImageUploader\",\n  \"RequestMethod\": \"POST\",\n  \"Requ"
  },
  {
    "path": "contrib/sharex12.sxcu",
    "chars": 287,
    "preview": "{\n  \"Name\": \"copyparty\",\n  \"DestinationType\": \"ImageUploader, TextUploader, FileUploader\",\n  \"RequestURL\": \"http://127.0"
  },
  {
    "path": "contrib/systemd/cfssl.service",
    "chars": 820,
    "preview": "# NOTE: this is now a built-in feature in copyparty\n# but you may still want this if you have specific needs\n#\n# systemd"
  },
  {
    "path": "contrib/systemd/copyparty-user.service",
    "chars": 842,
    "preview": "# this will start `/usr/bin/copyparty`\n# and read config from `$HOME/.config/copyparty.conf`\n#\n# unless you add -q to di"
  },
  {
    "path": "contrib/systemd/copyparty.conf",
    "chars": 134,
    "preview": "[global]\n  i: 127.0.0.1\n\n[accounts]\n  user: password\n\n[/]\n  /var/lib/copyparty-jail\n  accs:\n    r: *\n    rwdma: user\n  f"
  },
  {
    "path": "contrib/systemd/copyparty.example.conf",
    "chars": 2218,
    "preview": "# not actually YAML but lets pretend:\n# -*- mode: yaml -*-\n# vim: ft=yaml:\n\n\n# put this file in /etc/\n\n\n[global]\n  e2dsa"
  },
  {
    "path": "contrib/systemd/copyparty.service",
    "chars": 3660,
    "preview": "# this will start `/usr/local/bin/copyparty-sfx.py` and\n# read copyparty config from `/etc/copyparty.conf`, for example:"
  },
  {
    "path": "contrib/systemd/copyparty@.service",
    "chars": 839,
    "preview": "# this will start `/usr/bin/copyparty`\n# and read config from `/etc/copyparty/copyparty.conf`\n#\n# the %i refers to whate"
  },
  {
    "path": "contrib/systemd/index.md",
    "chars": 649,
    "preview": "this is `/var/lib/copyparty-jail`, the fallback webroot when copyparty has not yet been configured\n\nplease edit `/etc/co"
  },
  {
    "path": "contrib/systemd/prisonparty.service",
    "chars": 1424,
    "preview": "# this will start `/usr/local/bin/copyparty-sfx.py`\n# in a chroot, preventing accidental access elsewhere,\n# and share '"
  },
  {
    "path": "contrib/systemd/prisonparty@.service",
    "chars": 1177,
    "preview": "# this will start `/usr/bin/copyparty`\n# in a chroot, preventing accidental access elsewhere,\n# and read copyparty confi"
  },
  {
    "path": "contrib/themes/bsod.css",
    "chars": 2090,
    "preview": "/* copy bsod.* into a folder named \".themes\" in your webroot and then\n     --themes=10 --theme=9 --css-browser=/.themes/"
  },
  {
    "path": "contrib/traefik/copyparty.yaml",
    "chars": 511,
    "preview": "# ./traefik --configFile=copyparty.yaml\n\nentryPoints:\n  web:\n    address: :8080\n    transport:\n      # don't disconnect "
  },
  {
    "path": "contrib/webdav-cfg.bat",
    "chars": 1449,
    "preview": "@echo off\nrem removes the 47.6 MiB filesize limit when downloading from webdav\nrem + optionally allows/enables password-"
  },
  {
    "path": "contrib/windows/copyparty-ctmp.bat",
    "chars": 110,
    "preview": "rem run copyparty.exe on machines with busted environment variables\ncmd /v /c \"set TMP=\\tmp && copyparty.exe\"\n"
  },
  {
    "path": "contrib/zfs-tune.py",
    "chars": 3294,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport sqlite3\nimport sys\nimport traceback\n\n\n\"\"\"\nwhen the up2k-database is stored on a"
  },
  {
    "path": "copyparty/__init__.py",
    "chars": 3273,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\nimport platform\nimport sys\nimport tim"
  },
  {
    "path": "copyparty/__main__.py",
    "chars": 164413,
    "preview": "#!/usr/bin/env python3\n# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\n\"\"\"copyparty: http file "
  },
  {
    "path": "copyparty/__version__.py",
    "chars": 259,
    "preview": "# coding: utf-8\n\nVERSION = (1, 20, 12)\nCODENAME = \"sftp is fine too\"\nBUILD_DT = (2026, 3, 11)\n\nS_VERSION = \".\".join(map("
  },
  {
    "path": "copyparty/authsrv.py",
    "chars": 148340,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport base64\nimport hashlib\nim"
  },
  {
    "path": "copyparty/bos/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "copyparty/bos/bos.py",
    "chars": 3831,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\nimport time\n\nfrom ..util import SYMTI"
  },
  {
    "path": "copyparty/bos/path.py",
    "chars": 870,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\n\nfrom ..util import SYMTIME, fsdec, f"
  },
  {
    "path": "copyparty/broker_mp.py",
    "chars": 5543,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport threading\nimport time\nimport traceback\n\n"
  },
  {
    "path": "copyparty/broker_mpw.py",
    "chars": 3882,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport os\nimport signal\nimport "
  },
  {
    "path": "copyparty/broker_thr.py",
    "chars": 2067,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\nimport threading\n\nfrom .__init__ impo"
  },
  {
    "path": "copyparty/broker_util.py",
    "chars": 1925,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\n\nfrom queue import Queue\n\nfrom "
  },
  {
    "path": "copyparty/cert.py",
    "chars": 8550,
    "preview": "import calendar\nimport errno\nimport json\nimport os\nimport shutil\nimport time\n\nfrom .__init__ import ANYWIN\nfrom .util im"
  },
  {
    "path": "copyparty/cfg.py",
    "chars": 19596,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\n# awk -F\\\" '/add_argument\\(\"-[^-]/{print(substr"
  },
  {
    "path": "copyparty/dxml.py",
    "chars": 2826,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport importlib\nimport sys\nimport xml.etree.El"
  },
  {
    "path": "copyparty/fsutil.py",
    "chars": 8607,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport json\nimport os\nimport re"
  },
  {
    "path": "copyparty/ftpd.py",
    "chars": 20306,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport errno\nimport logging\nimp"
  },
  {
    "path": "copyparty/httpcli.py",
    "chars": 271942,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse  # typechk\nimport copy\nimport e"
  },
  {
    "path": "copyparty/httpconn.py",
    "chars": 8138,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse  # typechk\nimport os\nimport re\n"
  },
  {
    "path": "copyparty/httpsrv.py",
    "chars": 20284,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport hashlib\nimport math\nimport os\nimport re\n"
  },
  {
    "path": "copyparty/ico.py",
    "chars": 3936,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse  # typechk\nimport colorsys\nimpo"
  },
  {
    "path": "copyparty/mdns.py",
    "chars": 19867,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport errno\nimport os\nimport random\nimport sel"
  },
  {
    "path": "copyparty/metrics.py",
    "chars": 9188,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport json\nimport time\n\nfrom .__init__ import "
  },
  {
    "path": "copyparty/mtag.py",
    "chars": 25643,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport json\nimport os\nimport re"
  },
  {
    "path": "copyparty/multicast.py",
    "chars": 12876,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport socket\nimport time\n\nimport ipaddress\nfro"
  },
  {
    "path": "copyparty/pwhash.py",
    "chars": 4507,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport base64\nimport hashlib\nim"
  },
  {
    "path": "copyparty/qrkode.py",
    "chars": 2986,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\n\ntry:\n    if os.environ.get(\"PRTY_SYS"
  },
  {
    "path": "copyparty/res/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "copyparty/res/insecure.pem",
    "chars": 2876,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDD84b31Brvmfcc\nGTwXuf5n5NW6KxIhLWVZgsCJq+l"
  },
  {
    "path": "copyparty/sftpd.py",
    "chars": 29912,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport errno\nimport hashlib\nimport logging\nimpo"
  },
  {
    "path": "copyparty/smbd.py",
    "chars": 15320,
    "preview": "# coding: utf-8\n\nimport inspect\nimport logging\nimport os\nimport random\nimport stat\nimport sys\nimport time\nfrom types imp"
  },
  {
    "path": "copyparty/ssdp.py",
    "chars": 7319,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport errno\nimport re\nimport select\nimport soc"
  },
  {
    "path": "copyparty/star.py",
    "chars": 4157,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport re\nimport stat\nimport tarfile\n\nfrom queu"
  },
  {
    "path": "copyparty/stolen/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "copyparty/stolen/dnslib/README.md",
    "chars": 133,
    "preview": "`dnslib` but heavily simplified/feature-stripped\n\nL: MIT\nCopyright (c) 2010 - 2017 Paul Chakravarti\nhttps://github.com/p"
  },
  {
    "path": "copyparty/stolen/dnslib/__init__.py",
    "chars": 159,
    "preview": "# coding: utf-8\n\n\"\"\"\nL: MIT\nCopyright (c) 2010 - 2017 Paul Chakravarti\nhttps://github.com/paulc/dnslib/tree/0.9.23\n\"\"\"\n\n"
  },
  {
    "path": "copyparty/stolen/dnslib/bimap.py",
    "chars": 1182,
    "preview": "# coding: utf-8\n\nimport types\n\n\nclass BimapError(Exception):\n    pass\n\n\nclass Bimap(object):\n    def __init__(self, name"
  },
  {
    "path": "copyparty/stolen/dnslib/bit.py",
    "chars": 348,
    "preview": "# coding: utf-8\n\nfrom __future__ import print_function\n\n\ndef get_bits(data, offset, bits=1):\n    mask = ((1 << bits) - 1"
  },
  {
    "path": "copyparty/stolen/dnslib/buffer.py",
    "chars": 1407,
    "preview": "# coding: utf-8\n\nimport binascii\nimport struct\n\n\nclass BufferError(Exception):\n    pass\n\n\nclass Buffer(object):\n    def "
  },
  {
    "path": "copyparty/stolen/dnslib/dns.py",
    "chars": 21181,
    "preview": "# coding: utf-8\n\nfrom __future__ import print_function\n\nimport binascii\nfrom itertools import chain\n\nfrom .bimap import "
  },
  {
    "path": "copyparty/stolen/dnslib/label.py",
    "chars": 5536,
    "preview": "# coding: utf-8\n\nfrom __future__ import print_function\n\nimport re\n\nfrom .bit import get_bits, set_bits\nfrom .buffer impo"
  },
  {
    "path": "copyparty/stolen/dnslib/lex.py",
    "chars": 2615,
    "preview": "# coding: utf-8\n\nfrom __future__ import print_function\n\nimport collections\n\ntry:\n    from StringIO import StringIO\nexcep"
  },
  {
    "path": "copyparty/stolen/dnslib/ranges.py",
    "chars": 1810,
    "preview": "# coding: utf-8\n\nimport sys\n\nif sys.version_info < (3,):\n    int_types = (\n        int,\n        long,\n    )\n    byte_typ"
  },
  {
    "path": "copyparty/stolen/ifaddr/README.md",
    "chars": 215,
    "preview": "`ifaddr` with py2.7 support enabled by make-sfx.sh which strips py3 hints using strip_hints and removes the `^if True:` "
  },
  {
    "path": "copyparty/stolen/ifaddr/__init__.py",
    "chars": 1101,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\n\"\"\"\nL: BSD-2-Clause\nCopyright (c) 2014 Stefan C"
  },
  {
    "path": "copyparty/stolen/ifaddr/_posix.py",
    "chars": 2778,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport collections\nimport ctypes.util\nimport os"
  },
  {
    "path": "copyparty/stolen/ifaddr/_shared.py",
    "chars": 6793,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport ctypes\nimport platform\nimport socket\nimp"
  },
  {
    "path": "copyparty/stolen/ifaddr/_win32.py",
    "chars": 4196,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport ctypes\nfrom ctypes import wintypes\n\nif T"
  },
  {
    "path": "copyparty/stolen/qrcodegen.py",
    "chars": 21791,
    "preview": "# coding: utf-8\n\n# modified copy of Project Nayuki's qrcodegen (MIT-licensed);\n# https://github.com/nayuki/QR-Code-gener"
  },
  {
    "path": "copyparty/stolen/surrogateescape.py",
    "chars": 5718,
    "preview": "# coding: utf-8\n\n\"\"\"\nThis is Victor Stinner's pure-Python implementation of PEP 383: the \"surrogateescape\" error\nhandler"
  },
  {
    "path": "copyparty/sutil.py",
    "chars": 3711,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\nimport tempfile\nfrom datetime import "
  },
  {
    "path": "copyparty/svchub.py",
    "chars": 65210,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport argparse\nimport atexit\nimport errno\nimpo"
  },
  {
    "path": "copyparty/szip.py",
    "chars": 9414,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport calendar\nimport stat\nimport time\n\nfrom ."
  },
  {
    "path": "copyparty/tcpsrv.py",
    "chars": 22908,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport os\nimport re\nimport socket\nimport sys\nim"
  },
  {
    "path": "copyparty/tftpd.py",
    "chars": 14879,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\ntry:\n    from types import SimpleNamespace\nexce"
  },
  {
    "path": "copyparty/th_cli.py",
    "chars": 6010,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport errno\nimport os\nimport stat\n\nfrom .__ini"
  },
  {
    "path": "copyparty/th_srv.py",
    "chars": 45160,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport hashlib\nimport io\nimport logging\nimport "
  },
  {
    "path": "copyparty/u2idx.py",
    "chars": 15754,
    "preview": "# coding: utf-8\nfrom __future__ import print_function, unicode_literals\n\nimport calendar\nimport os\nimport re\nimport thre"
  }
]

// ... and 248 more files (download for full content)

About this extraction

This page contains the full source code of the 9001/copyparty GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 448 files (4.6 MB), approximately 1.2M tokens, and a symbol index with 2403 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!