Parcourir la source

adding useful scripts

Nicole Portas il y a 2 mois
commit
f1dd315015
2 fichiers modifiés avec 225 ajouts et 0 suppressions
  1. 141 0
      exif_change_date.py
  2. 84 0
      repack_slicer.sh

+ 141 - 0
exif_change_date.py

@@ -0,0 +1,141 @@
+import os
+import sys
+from datetime import datetime
+from pathlib import Path
+from PIL import Image, TiffImagePlugin
+import piexif
+from concurrent.futures import ThreadPoolExecutor, as_completed
+
+def get_arbitrary_date():
+    """Prompts the user for a date. Returns Unix epoch if blank, or the parsed target date."""
+    while True:
+        user_input = input("Enter the target date and time (YYYY-MM-DD HH:MM:SS) [Press Enter for 1970-01-01 00:00:00]: ").strip()
+        
+        if not user_input:
+            return "1970:01:01 00:00:00"
+            
+        try:
+            # Parse user's hyphenated input
+            dt = datetime.strptime(user_input, "%Y-%m-%d %H:%M:%S")
+            # Return colon-separated EXIF format
+            return dt.strftime("%Y:%m:%d %H:%M:%S")
+        except ValueError:
+            print("Incorrect format. You must use YYYY-MM-DD HH:MM:SS or press Enter for 1970. Try again.", file=sys.stderr)
+
+def update_file_timestamps(file_path, exif_date_str):
+    """Updates EXIF metadata/native chunks based on format, and sets filesystem mtime/atime."""
+    try:
+        # Match the colon format returned by get_arbitrary_date
+        dt = datetime.strptime(exif_date_str, "%Y:%m:%d %H:%M:%S")
+        target_timestamp = dt.timestamp()
+    except ValueError as e:
+        return f"Failed to parse internal date string {exif_date_str}: {e}"
+        
+    file_suffix = file_path.suffix.lower()
+    meta_updated = False
+
+    # 1. Handle JPEG/WEBP via piexif
+    if file_suffix in {'.jpg', '.jpeg', '.webp'}:
+        try:
+            try:
+                exif_dict = piexif.load(str(file_path))
+            except Exception:
+                exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "Interop": {}, "1st": {}, "thumbnail": None}
+
+            date_bytes = exif_date_str.encode('utf-8')
+            exif_dict["0th"][piexif.ImageIFD.DateTime] = date_bytes
+            exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = date_bytes
+            exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized] = date_bytes
+
+            exif_bytes = piexif.dump(exif_dict)
+            piexif.insert(exif_bytes, str(file_path))
+            meta_updated = True
+        except Exception as e:
+            return f"Warning: Failed to update EXIF for JPEG/WEBP {file_path.name}: {e}"
+
+    # 2. Handle PNG via Pillow
+    elif file_suffix == '.png':
+        try:
+            with Image.open(file_path) as img:
+                exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "Interop": {}, "1st": {}, "thumbnail": None}
+                date_bytes = exif_date_str.encode('utf-8')
+                exif_dict["0th"][piexif.ImageIFD.DateTime] = date_bytes
+                exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = date_bytes
+                exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized] = date_bytes
+                
+                exif_bytes = piexif.dump(exif_dict)
+                img.save(file_path, exif=exif_bytes)
+                meta_updated = True
+        except Exception as e:
+            return f"Warning: Failed to inject EXIF chunk into PNG {file_path.name}: {e}"
+
+    # 3. Handle TIFF via structured TiffImagePlugin tags
+    elif file_suffix in {'.tiff', '.tif'}:
+        try:
+            with Image.open(file_path) as img:
+                tiff_info = TiffImagePlugin.ImageFileDirectoryV2()
+                tiff_info[306] = exif_date_str
+                tiff_info[36867] = exif_date_str
+                tiff_info[36868] = exif_date_str
+                
+                img.save(file_path, tiffinfo=tiff_info)
+                meta_updated = True
+        except Exception as e:
+            return f"Warning: Failed to update TIFF tags for {file_path.name}: {e}"
+
+    # 4. Apply filesystem updates (mtime and atime) to everything
+    try:
+        os.utime(str(file_path), (target_timestamp, target_timestamp))
+        status = "META + FS" if meta_updated else "FS Only"
+        return f"Updated ({status}): {file_path.name}"
+    except Exception as e:
+        return f"Failed to update filesystem time for {file_path.name}: {e}"
+
+def main():
+    try:
+        current_dir = Path.cwd()
+    except FileNotFoundError:
+        pwd_env = os.environ.get('PWD')
+        if pwd_env and os.path.exists(pwd_env):
+            current_dir = Path(pwd_env)
+        else:
+            print("Error: The current directory does not exist. 'cd' into a valid directory and try again.", file=sys.stderr)
+            sys.exit(1)
+
+    valid_extensions = {
+        '.jpg', '.jpeg', '.png', '.webp', '.tiff', '.tif', 
+        '.bmp', '.gif', '.heic', '.heif', '.cr2', '.nef'
+    }
+    
+    try:
+        image_files = [
+            f for f in current_dir.rglob('*') 
+            if f.is_file() and f.suffix.lower() in valid_extensions
+        ]
+    except FileNotFoundError:
+        print(f"Error: Directory path '{current_dir}' is unreachable.", file=sys.stderr)
+        sys.exit(1)
+
+    if not image_files:
+        print("No matching images found in this directory or subdirectories.")
+        return
+
+    print(f"Found {len(image_files)} images recursively under {current_dir}")
+    target_date = get_arbitrary_date()
+
+    print(f"\nProcessing files concurrently using target date: {target_date}...")
+    
+    max_workers = min(32, (os.cpu_count() or 1) * 5)
+    
+    with ThreadPoolExecutor(max_workers=max_workers) as executor:
+        future_to_img = {executor.submit(update_file_timestamps, img_path, target_date): img_path for img_path in image_files}
+        
+        for future in as_completed(future_to_img):
+            result_message = future.result()
+            if result_message:
+                print(result_message)
+        
+    print("\nFinished processing all files.")
+
+if __name__ == "__main__":
+    main()

+ 84 - 0
repack_slicer.sh

@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+set -e
+
+# ==============================================================================
+# CONFIGURATION MODULE: ADD MISSING DEBIAN PACKAGES HERE
+# ==============================================================================
+# Space-separated list of Debian packages to scrape for dynamic libraries.
+PACKAGES_TO_INJECT="libglu1-mesa"
+# ==============================================================================
+
+# Argument check
+TARGET_APPIMAGE="$1"
+if [ -z "$TARGET_APPIMAGE" ] || [ ! -f "$TARGET_APPIMAGE" ]; then
+    echo "Error: Pass the path to the broken OrcaSlicer AppImage as the first argument."
+    exit 1
+fi
+
+EXTRACT_DIR="/tmp/orcaslicer_unpack"
+FINAL_BIN="/bin/Orcaslicer-new"
+WORKSPACE_DEB="/tmp/lib_extract_workspace"
+
+echo "=== Step 1: Purging old workspaces ==="
+rm -rf "$EXTRACT_DIR" /tmp/appimagetool* "$WORKSPACE_DEB"
+
+echo "=== Step 2: Unpacking the raw AppImage ==="
+cd /tmp
+"$TARGET_APPIMAGE" --appimage-extract
+mv /tmp/squashfs-root "$EXTRACT_DIR"
+
+# Force structure targets inside their layout to cover all RPATH scenarios
+mkdir -p "$EXTRACT_DIR/usr/lib"
+mkdir -p "$EXTRACT_DIR/bin"
+mkdir -p "$EXTRACT_DIR/usr/bin"
+
+echo "=== Step 3: Extracting and Shotgun-Injecting Modular Libraries ==="
+mkdir -p "$WORKSPACE_DEB"
+
+for PKG in $PACKAGES_TO_INJECT; do
+    echo "  -> Processing package: $PKG"
+    mkdir -p "$WORKSPACE_DEB/$PKG"
+    cd "$WORKSPACE_DEB/$PKG"
+    
+    # Grab the binary package file from APT repositories
+    apt-get download "$PKG"
+    
+    # Extract the internal archive filesystem cleanly 
+    dpkg-deb -x *.deb .
+    
+    # Rip out every compiled library inside the package and force it into every level
+    find . -type f -name "*.so*" | while read -r SO_FILE; do
+        SO_NAME=$(basename "$SO_FILE")
+        echo "     + Shotgun-injecting $SO_NAME into all layout paths..."
+        
+        # Blanket copy to completely defeat hardcoded binary RPATH limits
+        cp "$SO_FILE" "$EXTRACT_DIR/"
+        cp "$SO_FILE" "$EXTRACT_DIR/bin/"
+        cp "$SO_FILE" "$EXTRACT_DIR/usr/bin/"
+        cp "$SO_FILE" "$EXTRACT_DIR/usr/lib/"
+        
+        # Auto-build matching unversioned symlinks across all target paths
+        BASE_SO=$(echo "$SO_NAME" | grep -o '^lib[^.]*\.so\.[0-9]\+') || true
+        if [ -n "$BASE_SO" ] && [ "$BASE_SO" != "$SO_NAME" ]; then
+            ln -sf "$SO_NAME" "$EXTRACT_DIR/$BASE_SO"
+            ln -sf "$SO_NAME" "$EXTRACT_DIR/bin/$BASE_SO"
+            ln -sf "$SO_NAME" "$EXTRACT_DIR/usr/bin/$BASE_SO"
+            ln -sf "$SO_NAME" "$EXTRACT_DIR/usr/lib/$BASE_SO"
+        fi
+    done
+done
+
+echo "=== Step 4: Provisioning AppImage Builder Runtime ==="
+cd /tmp
+curl -LO https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
+chmod +x appimagetool-x86_64.AppImage
+
+echo "=== Step 5: Compiling fresh container output ==="
+export ARCH=x86_64
+./appimagetool-x86_64.AppImage "$EXTRACT_DIR" "$FINAL_BIN"
+chmod +x "$FINAL_BIN"
+
+echo "=== Step 6: Cleaning environment artifacts ==="
+rm -rf "$EXTRACT_DIR" /tmp/appimagetool-x86_64.AppImage "$WORKSPACE_DEB"
+
+echo "=== Success! Modified executable dropped to: $FINAL_BIN ==="