It is a wrapper around pywikibot to make uploading to Wikimedia Commons from CLI simpler.
Install
From PyPI:
pip install pwb-wrapper-for-simpler-uploading-to-commons
With shell autocompletion support:
pip install 'pwb-wrapper-for-simpler-uploading-to-commons[autocomplete]'
Usage
Installed CLI command:
pwb-upload --file my.jpg --source https://example.com --license 'PD-old' --category 'Sunsets in Batumi' --date '2025-12-27' --desc 'A beautiful sunset from the beach' --target myrenamed.jpg
For multiple categories, repeat --category:
pwb-upload --file my.jpg --category 'Sunsets in Batumi' --category 'Evening in Georgia' --date '2025-12-27'
Use folder name in category:
pwb-upload --category "Kaspiy ${PWD##*/}" --license 'PD-scan|PD-old-assumed-expired' --source https://dspace.nplg.gov.ge/handle/1234/387883 --dateParse
Category and prefix is the same:
S='xxx' pwb-upload --category "$S" --prefix "$S "
Default source is {{own}}.
Default license is cc-by-4.0.
--author for example [[User:Globustut|Globustut]]
--date in format YYYY-MM-DD.
--dateParse extracts year from a filename (1xxx or 2xxx).
--date and --dateParse are mutually exclusive.
--target (file name on Commons) is optional.
--prefix (prefix for file name on Commons) is optional.
--i lets you start from a specific index.
--recursive includes files from subfolders when no file is specified.
You can upload with minimal arguments:
pwb-upload --file my.jpg --category 'Sunsets in Batumi' --date '2025-12-27'
Or pass the file as positional argument:
pwb-upload my.jpg --category 'Sunsets in Batumi' --date '2025-12-27'
Or upload without category, date, and dateParse (set them later on Commons):
pwb-upload my.jpg
If no file is specified, it uploads eligible files from the current folder (no subfolders by default):
pwb-upload --category 'My category' --author '[[User:Globustut|Globustut]]' --license 'self|CC-BY-4.0|author=Globustut' --date 2005
To include subfolders:
pwb-upload --recursive
For local development (without installation), you can still run:
./upload.py --file my.jpg
Author: for the current user use me.
MP4 videos
MP4 inputs are automatically converted with FFmpeg to WebM with AV1 video and Opus audio before uploading:
pwb-upload --category 'Bagrati Cathedral after restoration' Bagrati-Cathedral2026september-cross-ans-walking-man.mp4
This uploads Bagrati-Cathedral2026september-cross-ans-walking-man.webm. Conversion also applies to --file, folder uploads, and --recursive, including uppercase .MP4 extensions. A custom --target gets a .webm extension, and --prefix works as usual.
Install an FFmpeg build with the libsvtav1 and libopus encoders and ensure ffmpeg is on PATH. Check availability with ffmpeg -hide_banner -encoders. Other file formats do not require FFmpeg.
Encoding uses AV1 CRF 28, preset 6, 10-bit YUV 4:2:0, and Opus at 160 kb/s and 48 kHz. The first video stream and optional first audio stream are selected; silent videos work too. Resolution and frame rate are not intentionally reduced. The terminal shows an updating encoding progress line, warnings, and errors; FFmpeg metadata and SVT-AV1 startup and memory reports are suppressed. These codecs are supported by Commons video formats.
The original MP4 and any existing neighboring WebM remain untouched. The converted file is stored in the system temporary directory and removed when the upload finishes, fails, or is interrupted. Set TMPDIR to a directory with enough free space for large videos. Upload retries reuse the conversion; a new invocation encodes again. Conversion failures stop before uploading and exit with an error.
As a temporary workaround for an SVT-AV1 3.1.2 encoder hang, FFmpeg is stopped if its frame count, media timestamp, and output byte count do not advance for 60 seconds. The script discards the partial output and retries conversion once with fewer encoder workers (-svtav1-params lp=1), retaining the codec and quality settings. This retry can encode more slowly. A second stall exits with an error instead of uploading incomplete media. Encoding can run longer than 60 seconds while progress continues. Remove this workaround once a fixed encoder version is required and verified.
Autocompletion
Enable completion for the installed command:
eval "$(register-python-argcomplete pwb-upload)"
Release to PyPI
Release scripts in this repository:
release_minor.shbumpsx.y.z->x.(y+1).0release_patch.shbumpsx.y.z->x.y.(z+1)release-added.shuses already staged files and already bumped version inpyproject.toml(nogit add .)
Call them from git (one-time setup):
git config alias.release-minor '!f() { repo=$(git rev-parse --show-toplevel) && bash "$repo/release_minor.sh" "$@"; }; f'
git config alias.release-patch '!f() { repo=$(git rev-parse --show-toplevel) && bash "$repo/release_patch.sh" "$@"; }; f'
git config alias.release-added '!f() { repo=$(git rev-parse --show-toplevel) && bash "$repo/release-added.sh" "$@"; }; f'
Then run:
git release-minor 'Release message'
git release-patch 'Release message'
git release-added 'Release message'
Build distribution files:
python3 -m build
Upload to TestPyPI:
python3 -m twine upload --repository testpypi dist/*
Upload to PyPI:
python3 -m twine upload dist/*
Wikidata item about this tool https://www.wikidata.org/wiki/Q137601716
Commons category https://commons.wikimedia.org/wiki/Category:Uploaded_with_pwb_wrapper_script_by_Vitaly_Zdanevich
See also
My another Python script for uploading to Commons from gThumb
My web extension for uploading to Commons
This tool is mentioned at my blog post https://diff.wikimedia.org/2026/04/09/preserve-old-public-domain-newspapers-and-books-from-archive-websites/
Metadata
Release files for pwb-wrapper-for-simpler-uploading-to-commons 1.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pwb_wrapper_for_simpler_uploading_to_commons-1.8.0.tar.gz | 12.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pwb_wrapper_for_simpler_uploading_to_commons-1.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.6 kB
Release files / pwb_wrapper_for_simpler_uploading_to_commons-1.8.0.tar.gz
| Download URL | pwb_wrapper_for_simpler_uploading_to_commons-1.8.0.tar.gz |
|---|---|
| Size | 12.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cea2572f70bb86b4bf70a82ca6e1b5677ee4797fd75aae14b3eb1cce17a231a1
|
|
BLAKE2b-256 checksum How to use checksums |
60f6cafe699d5d2ba72456a921808c7f7a131bb356f1d7a4f3017b8a4291ceca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / pwb_wrapper_for_simpler_uploading_to_commons-1.8.0-py3-none-any.whl
| Download URL | pwb_wrapper_for_simpler_uploading_to_commons-1.8.0-py3-none-any.whl |
|---|---|
| Size | 12.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
43aa0d5befe78350a26346c6ce16661d53c19ec9808effd50f6b803ab4624b02
|
|
BLAKE2b-256 checksum How to use checksums |
8cdf708e4cebde42a9f7c92655293736b549f37886661914c3df5a0143fcb141
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|