A self-hosted photo proofing & delivery tool for photographers. Clients browse and mark their favorites in a modern web album — no cloud upload, no messenger recompression. Deliver remotely over IPv6, or straight across the room on your own Wi-Fi.
English · 中文
- Professional proofing UI — an iOS-style album that elevates your brand, with a PhotoSwipe viewer (zoom / pan / gestures / keyboard).
- Full RAW support — auto-generates previews for CR2 / CR3 / NEF / ARW / DNG / ORF / RW2 / PEF / SR2, decoded with bundled libraw (via
rawpy) — no external tools to install. - One-tap selection — clients favorite photos in the browser; picks are kept as a per-album manifest (no file copying until you export).
- Two ways to reach you — a public IPv6 link your client can open from anywhere (no port-forwarding, no NAT setup), plus a LAN link for in-person proofing that needs nothing from your ISP. PicShare detects both and labels which is which; you never pick a protocol.
- Works everywhere — phones, tablets, and desktop browsers.
- Fast tiered cache — multi-threaded thumbnail prewarm, plus on-demand large (1600px) and RAW "original" (HD) tiers with lazy loading.
The desktop window uses your OS's native WebView (via pywebview):
- macOS — WKWebView, built in. Nothing to install.
- Windows — the WebView2 Runtime (bundled with Windows 10/11; if the window won't open, install the Evergreen WebView2 Runtime).
- Linux — WebKit2GTK, e.g.
sudo apt install gir1.2-webkit2-4.1 libwebkit2gtk-4.1-0(package names vary by distro).
Grab the package for your platform from the Releases page — no Python required.
| Platform | Download | How to install |
|---|---|---|
| macOS (Apple Silicon) | PicShare-<ver>-macos-arm64.dmg |
Open the DMG, drag PicShare onto Applications |
| macOS (Intel) | PicShare-<ver>-macos-x64.dmg |
Same as above |
| Windows 10/11 (x64) | PicShare-Setup-<ver>-x64.exe |
Double-click, follow the wizard — installs per-user, no admin rights needed |
| Linux (x64) | PicShare-<ver>-linux-x64.tar.gz |
Extract, run ./PicShare |
Blocked on first launch? That is expected — here is how to allow it
PicShare is not code-signed (Apple and Windows certificates are a paid, per-year expense), so both systems warn about software from an "unidentified developer". The warning is about the missing signature — nothing was found in the app.
- macOS — double-click PicShare once (it gets blocked), then open System Settings → Privacy & Security, scroll down, click Open Anyway. On macOS 13 and earlier: System Preferences → Security & Privacy → General → Open Anyway. Terminal alternative:
xattr -dr com.apple.quarantine /Applications/PicShare.app - Windows — SmartScreen shows "Windows protected your PC". Click More info → Run anyway.
You only need to do this once.
Launch PicShare, then in the desktop window:
- Click Select to choose your photo root directory (the folder that holds your album subfolders).
- Click the 网络 (Network) chip and check which addresses are available — 🌐 public (client can be anywhere) and/or 📶 LAN (same Wi-Fi only).
- In 🔗 Share management, pick an album, set an expiry → Generate & copy link (no passcode by default; enable one for sensitive albums).
- Send the link (
http://[...]:5000/share/<token>) to your client. If you added a passcode, send it separately (buttons let you re-copy the link / passcode anytime).
- Photographer — run the app, choose your photo root, then generate a dedicated share link (with optional expiry and passcode) for an album and send it to the matching client.
- Client — open the link to browse, mark favorites, and view full-resolution photos (RAW is served as an on-demand high-res JPEG). No album name to type. A skippable one-screen guide appears on first open, and the ? in the top bar brings it back.
- Collect — click Export in the desktop app to copy the selected originals into
被标记的照片/<album>/, then it opens in your file manager.
PicShare uses a capability-URL model rather than guessable album names:
- Each album is bound to an unguessable random token (
/share/<token>); the album is derived server-side, so clients cannot specify an album name in the URL and can neither see nor guess others' albums. - The token is a strong random credential (192-bit). For most cases one link is enough — no passcode needed.
- Optionally add a passcode for sensitive albums (off by default): a random 4-character alphanumeric code, sent separately from the link (it is not embedded in the URL, so forwarding the link doesn't leak it). The client types it once in the browser.
- Links can carry an expiry (3 / 7 / 14 days, default 3; auto-invalidates).
- You can revoke any link at any time, or re-copy its link / passcode.
- Tokens and passcodes (if set) live in
<root>/._picshare/tokens.json(a local hidden file), managed by the desktop app and read-only-verified by the web server.
This repo has been refactored from the original single file into a standard
src-layout Python project.
src/picshare/
├── __main__.py # `python -m picshare` entry
├── desktop.py # desktop entry: pywebview admin window + waitress public server
├── config.py # ServerState config, extensions, cache/data dirs
├── settings.py # user-level settings persistence (remembers the root dir, etc.)
├── status.py # unified logging (logging → console / file / run-log panel)
├── network.py # address detection: public IPv6 + LAN IPv4 (Windows / macOS / Linux)
├── paths.py # safe_join path-safety helper
├── preview.py # thumbnail / large / RAW-HD generation (rawpy fallback + cache version stamp)
├── tokens.py # access-token storage & verification (capability URLs)
├── selections.py # client selection manifest storage
├── admin/ # admin side (pywebview, in-process js_api, no public HTTP endpoints)
│ ├── api.py # Python API exposed to the admin page
│ └── templates.py # admin single-page HTML (album cards / sharing / run log)
└── web/ # public web service (client /share access)
├── app.py # Flask app & token-scoped routes
├── templates.py # album / landing / passcode page templates
└── static/photoswipe/ # bundled PhotoSwipe viewer (offline)
Requires Python 3.10+.
pip install -e .
picshare # or: python -m picshareTo produce the distributable packages yourself — PyInstaller cannot cross-compile, so each one has to be built on its own OS:
./scripts/build-macos.sh # → dist/PicShare.app + dist/PicShare-<ver>-macos-<arch>.dmg
.\scripts\build-windows.ps1 # → dist/PicShare/ + dist/PicShare-Setup-<ver>-x64.exe (needs Inno Setup 6.5+)
./scripts/build-linux.sh # → dist/PicShare/Access control is token-based (see above), which is far stronger than "guess the album name." A few inherent limits still apply:
- The link is the credential — a capability URL is a bearer secret; anyone with the link can access it. For sensitive albums, add a passcode and/or expiry, and avoid sharing links on public channels.
- The service now also listens on IPv4 — anyone on the same LAN who has the link can open it. Since the token has always been the credential and the IPv6 side is exposed to the whole internet, this narrows rather than widens the exposure — but on an untrusted network (a hotel, a shared studio), prefer a passcode.
- Plain HTTP — TLS is not enabled; an on-path attacker could intercept tokens and photos. Strong encryption needs a reverse proxy / tunnel (e.g. Caddy, Cloudflare Tunnel) — a future direction.
- No passcode rate-limiting — passcode checks aren't rate-limited yet, so a short passcode is theoretically brute-forceable by an attacker who already holds the token; rate-limiting / lockout is a planned hardening.
Always make sure the chosen photo root contains only the photos you're willing to deliver.
Source-available under the PolyForm Noncommercial License 1.0.0 — you may use, modify, and share it for noncommercial purposes only. Commercial use is not permitted.
Note: because of the noncommercial restriction, this is source-available, not OSI "open source."