parent
4e0c4d7023
commit
90901c99f8
1 changed files with 241 additions and 0 deletions
@ -0,0 +1,241 @@ |
||||
# commons-upload-tool based on github.com/fastily/pwiki |
||||
# purpose: batch upload your self-made photos and videos to Wikimedia Commons |
||||
# author: mtheiler |
||||
# date: 2022-04-20 |
||||
# further down there you are able to insert your own defaults e.g. username, ... |
||||
|
||||
import argparse |
||||
import getpass |
||||
import logging |
||||
import re |
||||
import os |
||||
|
||||
from datetime import date, datetime |
||||
from pathlib import Path |
||||
from textwrap import dedent |
||||
from typing import Any |
||||
|
||||
# PIL is the Python Imaging Library which provides the python interpreter with image editing capabilities |
||||
from PIL import Image |
||||
from PIL.Image import Exif |
||||
from PIL.ExifTags import TAGS |
||||
from PIL.ExifTags import GPSTAGS |
||||
|
||||
from rich.logging import RichHandler |
||||
|
||||
# see: https://github.com/fastily/pwiki |
||||
from pwiki import wgen |
||||
from pwiki.wiki import Wiki |
||||
|
||||
log = logging.getLogger(__name__) |
||||
|
||||
_MTC_FILE = Path.home() / ".scu.px.txt" |
||||
|
||||
|
||||
def _make_wiki(*args: Any) -> Wiki: |
||||
"""Convienence method, creates a new `Wiki` pointed at Commons with `args` |
||||
|
||||
Returns: |
||||
Wiki: The Wiki resulting object |
||||
""" |
||||
return Wiki("commons.wikimedia.org", *args) |
||||
|
||||
def getExifTags(rawEXIF): |
||||
"""Gets Exif Tags from the raw EXIF data of the image |
||||
and extracts DateTime and the GPS data """ |
||||
|
||||
exifTags = {} # place to store the EXIF data |
||||
gpsTags = {} # place to store the GPS data |
||||
|
||||
#pulling out the EXIF tags. |
||||
for tag, value in rawEXIF.items(): |
||||
decoded = TAGS.get(tag,tag) |
||||
exifTags[decoded] = value |
||||
|
||||
dateTime = exifTags['DateTimeDigitized'] |
||||
|
||||
rawGPS = exifTags['GPSInfo'] |
||||
|
||||
# Pulling out the GPS specific tags. |
||||
# see: https://pillow.readthedocs.io/en/stable/reference/ExifTags.html |
||||
for gpstag , value in rawGPS.items(): |
||||
decoded = TAGS.get(gpstag,gpstag) |
||||
gpsTags[decoded] = value |
||||
|
||||
latitude_direction=gpsTags[1] # N or S |
||||
(lat_degrees, lat_minutes, lat_seconds) = gpsTags[2] # latitude |
||||
longitude_direction=gpsTags[3] # W or E |
||||
(long_degrees, long_minutes, long_seconds) = gpsTags[4] # longitude |
||||
|
||||
# see: https://commons.wikimedia.org/wiki/Template:Location |
||||
commons_location= dedent(f"""\ |
||||
{{{{Location|{lat_degrees}|{lat_minutes}|{lat_seconds}|{latitude_direction}|{long_degrees}|{long_minutes}|{long_seconds}|{longitude_direction}}}}} |
||||
""") |
||||
return (dateTime, commons_location) |
||||
|
||||
|
||||
|
||||
def _main() -> None: |
||||
"""Main driver, to be run if this script is invoked directly.""" |
||||
|
||||
for lg in (logging.getLogger("pwiki"), log): |
||||
lg.addHandler(RichHandler(rich_tracebacks=True)) |
||||
lg.setLevel(logging.INFO) |
||||
|
||||
verbose = True |
||||
|
||||
cli_parser = argparse.ArgumentParser(description="Simple Commons Uploader 2 (MTh)") |
||||
cli_parser.add_argument('--user', type=str, help="username to use") |
||||
cli_parser.add_argument('--pw', type=str, help="password to use") |
||||
cli_parser.add_argument('--cat', type=str, help="commons category to use") |
||||
cli_parser.add_argument('--fnx', type=str, help="part of the filename on commons") |
||||
cli_parser.add_argument("-i", action='store_true', help="force interactive login") |
||||
cli_parser.add_argument("--wgen", action='store_true', help="run wgen password manager") |
||||
cli_parser.add_argument('dirs', metavar='folders', type=Path, nargs='*', help='folders with files to upload') |
||||
|
||||
# uncomment any of the following lines to set your own defaults |
||||
|
||||
# local directory to find pictures to upload |
||||
# myTest1 = Path('./test') |
||||
|
||||
# local list of directories. |
||||
# cli_parser.set_defaults(dirs=[myTest1]) |
||||
|
||||
# this string will become part of the constructed filename on Commons |
||||
# cli_parser.set_defaults(fnx='test') |
||||
|
||||
# Commons Category |
||||
# cli_parser.set_defaults(cat='testCategory') |
||||
|
||||
# Wikipedia Username |
||||
# cli_parser.set_defaults(user='my username') |
||||
|
||||
# Wikipedia Password |
||||
# cli_parser.set_defaults(pw='my very secret password') # Password |
||||
|
||||
args = cli_parser.parse_args() |
||||
|
||||
if args.fnx: |
||||
fileNameExt=args.fnx |
||||
|
||||
if args.wgen: |
||||
wgen.setup_px(_MTC_FILE, False) |
||||
return |
||||
|
||||
if args.i: |
||||
wiki = _make_wiki(input("Please login to continue.\nUsername: "), getpass.getpass()) |
||||
elif args.user: |
||||
if not args.pw: |
||||
log.critical("No password specified, please pass the --pw flag with a pasword.") |
||||
return |
||||
wiki = _make_wiki(args.user, args.pw) |
||||
elif _MTC_FILE.is_file(): |
||||
wiki = _make_wiki(*wgen.load_px(_MTC_FILE).popitem()) |
||||
else: |
||||
wiki = _make_wiki() |
||||
|
||||
if args.cat: |
||||
commomsCategory=args.cat |
||||
result=wiki.exists(f"Category:{commomsCategory}") |
||||
if not result: |
||||
log.critical(f"Category:{commomsCategory} does not exists") |
||||
return |
||||
|
||||
|
||||
if not args.dirs: |
||||
log.critical("You didn't specify and directories to upload!") |
||||
return |
||||
|
||||
ext_list = {"." + e for e in wiki.uploadable_filetypes()} |
||||
fails = [] |
||||
uploadedFileNames: List[str] |
||||
uploadedFileNames = [] |
||||
|
||||
for base_dir in args.dirs: |
||||
if not base_dir.is_dir(): |
||||
continue |
||||
|
||||
i = 1 |
||||
if verbose: |
||||
print('===Dir=',base_dir) |
||||
for f in base_dir.iterdir(): |
||||
if not f.is_file() or (file_ext := f.suffix.lower()) not in ext_list: |
||||
continue |
||||
|
||||
# construct an image description from filename without path and without suffix |
||||
imageDescription = (os.path.basename(f)).removesuffix(file_ext) |
||||
|
||||
# date |
||||
timestamp = None |
||||
if file_ext in (".jpg", ".jpeg"): |
||||
try: |
||||
with Image.open(f) as img: |
||||
rawEXIF = img.getexif() #Get the EXIF data from the image. |
||||
|
||||
exifDateTime, gpsLocation = getExifTags(rawEXIF) |
||||
d, time = exifDateTime.split() |
||||
myDate=d.replace(':', '-') |
||||
timestamp = f"{myDate} {time}" |
||||
|
||||
except Exception as e: |
||||
log.warning("Could not parse EXIF for %s", f, exc_info=True) |
||||
|
||||
|
||||
desc = dedent(f"""\ |
||||
=={{{{int:filedesc}}}}== |
||||
{{{{Information |
||||
|description={imageDescription} |
||||
|date={timestamp or datetime.fromtimestamp(f.stat().st_mtime).strftime('%Y-%m-%d %H:%M:%S')} |
||||
|source={{{{Own}}}} |
||||
|author=~~~ |
||||
}}}} |
||||
|
||||
{gpsLocation} |
||||
|
||||
=={{{{int:license-header}}}}== |
||||
{{{{Self|Cc-by-sa-4.0}}}} |
||||
|
||||
[[Category:{commomsCategory}]]""") |
||||
# [[Category:Files by {wiki.username}]]""") |
||||
|
||||
if verbose: |
||||
print("This will be saved on Commons:") |
||||
print(desc) |
||||
|
||||
filenameCommons=f"{imageDescription}_{myDate}_{fileNameExt}{file_ext}" |
||||
if verbose: |
||||
print("filenameCommons=",filenameCommons) |
||||
|
||||
# todo Argument einführen, wenn gesetzt, dann kein Upload |
||||
# Upload to Commons using pwiki, beim Test auskommentiert |
||||
if not wiki.upload(f, filenameCommons, desc, summary="fileupload with python-script"): |
||||
fails.append(f) |
||||
i += 1 |
||||
uploadedFileNames.append(filenameCommons) |
||||
|
||||
if fails: |
||||
log.warning("Failed to upload %d files: %s", len(fails), fails) |
||||
else: |
||||
log.info("Finished with no failures") |
||||
|
||||
# wiki.save_cookies() mth |
||||
|
||||
# append filenames to gallery |
||||
text4Gallery = f"""\n\n== {commomsCategory} {myDate} ==\n""" |
||||
text4Gallery += "\n<gallery widths=200>" |
||||
|
||||
for oneUploadedFilename in uploadedFileNames: |
||||
text4Gallery += f"""\n{oneUploadedFilename} | {oneUploadedFilename}""" |
||||
|
||||
text4Gallery += "\n</gallery>" |
||||
|
||||
myGallery=f"""User:{wiki.username}/gallery""" |
||||
|
||||
if verbose: |
||||
print('Gallery=', myGallery) |
||||
print(text4Gallery) |
||||
|
||||
wiki.edit(myGallery, append=text4Gallery, summary="edit gallery with python-script") |
||||
|
||||
if __name__ == '__main__': |
||||
_main() |
Loading…
Reference in new issue