Add games library with IGDB, file explorer and static files script
This commit is contained in:
1 parent
a16444451a
commit
ec57bd73b3
65 files changed
+2230
-352
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import argparse
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
from pillow_heif import register_heif_opener
|
||||
|
||||
from jsonfile import PROJECT_ROOT
|
||||
|
||||
FILES_DIR = PROJECT_ROOT / "static" / "files"
|
||||
ORIGINALS_DIR = PROJECT_ROOT / "tools" / "content" / ".cache" / "heic-originals"
|
||||
HEIC_EXTENSIONS = {".heic", ".heif"}
|
||||
PHOTO_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"}
|
||||
JPEG_QUALITY = 90
|
||||
GPS_TAG = 0x8825
|
||||
|
||||
register_heif_opener()
|
||||
|
||||
|
||||
def visible_files(folder: Path, extensions: set[str]) -> list[Path]:
|
||||
return sorted(
|
||||
path
|
||||
for path in folder.rglob("*")
|
||||
if path.is_file() and path.suffix.lower() in extensions and not any(part.startswith(".") for part in path.relative_to(folder).parts)
|
||||
)
|
||||
|
||||
|
||||
def save_without_metadata(image: Image.Image, target: Path) -> None:
|
||||
upright = ImageOps.exif_transpose(image)
|
||||
if target.suffix.lower() in {".jpg", ".jpeg"}:
|
||||
upright.convert("RGB").save(target, "JPEG", quality=JPEG_QUALITY, optimize=True)
|
||||
else:
|
||||
upright.save(target)
|
||||
|
||||
|
||||
def keep_original(source: Path) -> Path:
|
||||
backup = ORIGINALS_DIR / source.relative_to(FILES_DIR)
|
||||
backup.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.move(source, backup)
|
||||
return backup
|
||||
|
||||
|
||||
def convert_heic(source: Path, delete_originals: bool) -> str:
|
||||
target = source.with_suffix(".jpg")
|
||||
if target.exists():
|
||||
return f"skipped {source.relative_to(FILES_DIR)}, {target.name} already exists"
|
||||
with Image.open(source) as image:
|
||||
save_without_metadata(image, target)
|
||||
if delete_originals:
|
||||
source.unlink()
|
||||
return f"converted {target.relative_to(FILES_DIR)}"
|
||||
return f"converted {target.relative_to(FILES_DIR)}, original kept in {keep_original(source).relative_to(PROJECT_ROOT)}"
|
||||
|
||||
|
||||
def has_location(path: Path) -> bool:
|
||||
try:
|
||||
with Image.open(path) as image:
|
||||
return bool(image.getexif().get_ifd(GPS_TAG))
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def strip_location(path: Path) -> str:
|
||||
with Image.open(path) as image:
|
||||
image.load()
|
||||
save_without_metadata(image, path)
|
||||
return f"removed location from {path.relative_to(FILES_DIR)}"
|
||||
|
||||
|
||||
def report(action) -> int:
|
||||
try:
|
||||
print(action())
|
||||
return 0
|
||||
except (OSError, ValueError) as error:
|
||||
print(f"Error: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Convert HEIC photos in static/files to JPEG and remove location data from photos")
|
||||
parser.add_argument("--delete-originals", action="store_true", help="delete converted HEIC files instead of moving them to tools/content/.cache")
|
||||
parser.add_argument("--keep-location", action="store_true", help="leave GPS data in existing photos")
|
||||
arguments = parser.parse_args()
|
||||
|
||||
if not FILES_DIR.exists():
|
||||
print(f"{FILES_DIR.relative_to(PROJECT_ROOT)} does not exist", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
heic_files = visible_files(FILES_DIR, HEIC_EXTENSIONS)
|
||||
located_photos = [] if arguments.keep_location else [photo for photo in visible_files(FILES_DIR, PHOTO_EXTENSIONS) if has_location(photo)]
|
||||
if not heic_files and not located_photos:
|
||||
print("Nothing to do: no HEIC files and no photos with location data")
|
||||
return 0
|
||||
|
||||
failed = 0
|
||||
for source in heic_files:
|
||||
failed += report(lambda: convert_heic(source, arguments.delete_originals))
|
||||
for photo in located_photos:
|
||||
failed += report(lambda: strip_location(photo))
|
||||
print(f"Done: {len(heic_files) + len(located_photos) - failed} changed, {failed} failed")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user