metadata-scrubber-tool:
implemented fix mypy type checking issues for piexif(it has no stub files) used basedpyright to generate a type stub, but damn that a lot more type related issues fixed most but a few remained so i modified the pyrightconfig with this: "typeCheckingMode": "standard" as it is in strict mode by default.
This commit is contained in:
parent
fc1cf236c6
commit
aabecd547f
|
|
@ -1,5 +0,0 @@
|
|||
# Environment Variables Example
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# Example:
|
||||
# API_KEY=your_api_key_here
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"stubPath": "./typings",
|
||||
"reportMissingTypeStubs": false,
|
||||
"venvPath": ".",
|
||||
"venv": ".venv",
|
||||
"pythonVersion": "3.12",
|
||||
"typeCheckingMode": "standard",
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ def read(
|
|||
raise typer.BadParameter("If you provide --recursive or -r, you must also provide --extension or -ext.")
|
||||
if ext and not recursive:
|
||||
raise typer.BadParameter("If you provide --extension or -ext, you must also provide --recursive or -r.")
|
||||
|
||||
|
||||
for file in get_target_files(file_path, ext) if recursive else [file_path]:
|
||||
try:
|
||||
# Get the correct object from the factory
|
||||
|
|
|
|||
|
|
@ -5,10 +5,23 @@ This module provides the JpegProcessor class which handles EXIF metadata
|
|||
extraction and manipulation for JPEG images using the piexif library.
|
||||
"""
|
||||
|
||||
import piexif # pyright: ignore[reportMissingTypeStubs]
|
||||
from typing import TypedDict
|
||||
|
||||
import piexif
|
||||
from PIL import Image
|
||||
|
||||
from src.utils.exceptions import MetadataNotFoundError, MetadataProcessingError
|
||||
|
||||
# Type alias for EXIF tag values (strings, bytes, ints, tuples, or lists)
|
||||
ExifValue = str | bytes | int | tuple[object, ...] | list[object]
|
||||
|
||||
|
||||
class JpegMetadataResult(TypedDict):
|
||||
"""Return type for JpegProcessor.get_metadata()."""
|
||||
|
||||
data: dict[str, ExifValue]
|
||||
tags_to_delete: list[int]
|
||||
|
||||
|
||||
class JpegProcessor:
|
||||
"""
|
||||
|
|
@ -24,10 +37,10 @@ class JpegProcessor:
|
|||
|
||||
def __init__(self):
|
||||
"""Initialize the JPEG processor with empty data structures."""
|
||||
self.tags_to_delete = []
|
||||
self.data = {}
|
||||
self.tags_to_delete: list[int] = []
|
||||
self.data: dict[str, ExifValue] = {}
|
||||
|
||||
def get_metadata(self, img):
|
||||
def get_metadata(self, img: Image.Image) -> JpegMetadataResult:
|
||||
"""
|
||||
Extract EXIF metadata from a JPEG image.
|
||||
|
||||
|
|
@ -51,10 +64,11 @@ class JpegProcessor:
|
|||
|
||||
# Iterate through the IFD
|
||||
for tag, tag_value in exif_dict[ifd].items():
|
||||
tag_info = piexif.TAGS[ifd].get(tag, {})
|
||||
ifd_tags = piexif.TAGS.get(ifd, {})
|
||||
tag_info = ifd_tags.get(tag, {}) if isinstance(ifd_tags, dict) else {}
|
||||
|
||||
# Get the human-readable name for the tag
|
||||
tag_name = (
|
||||
tag_name = str(
|
||||
tag_info.get("name", "Unknown Tag") if tag_info else "Unknown Tag"
|
||||
)
|
||||
|
||||
|
|
@ -72,7 +86,7 @@ class JpegProcessor:
|
|||
|
||||
return {"data": self.data, "tags_to_delete": self.tags_to_delete}
|
||||
|
||||
def delete_metadata(self, img, tags_to_delete):
|
||||
def delete_metadata(self, img: Image.Image, tags_to_delete: list[int]):
|
||||
"""
|
||||
Remove specified EXIF tags from a JPEG image.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ extraction and manipulation for PNG images, including both EXIF data
|
|||
and PNG textual metadata (PngInfo chunks).
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
from PIL import ExifTags, PngImagePlugin
|
||||
from PIL.Image import Exif, Image
|
||||
|
|
@ -44,11 +44,11 @@ class PngProcessor:
|
|||
|
||||
def __init__(self):
|
||||
"""Initialize the PNG processor with empty data structures."""
|
||||
self.tags_to_delete: List[int] = []
|
||||
self.text_keys_to_delete: List[str] = []
|
||||
self.data: Dict[str, Any] = {}
|
||||
self.tags_to_delete: list[int] = []
|
||||
self.text_keys_to_delete: list[str] = []
|
||||
self.data: dict[str, Any] = {}
|
||||
|
||||
def get_metadata(self, img: Image) -> Dict[str, Any]:
|
||||
def get_metadata(self, img: Image) -> dict[str, Any]:
|
||||
"""
|
||||
Extract metadata from a PNG image.
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class PngProcessor:
|
|||
"text_keys": self.text_keys_to_delete,
|
||||
}
|
||||
|
||||
def delete_metadata(self, img: Image, tags_to_delete: List[int]) -> Exif:
|
||||
def delete_metadata(self, img: Image, tags_to_delete: list[int]) -> Exif:
|
||||
"""
|
||||
Remove EXIF tags from a PNG image.
|
||||
|
||||
|
|
@ -143,8 +143,8 @@ class PngProcessor:
|
|||
raise MetadataProcessingError(f"Error processing PNG EXIF: {str(e)}")
|
||||
|
||||
def get_clean_pnginfo(
|
||||
self, img: Image, keys_to_remove: Optional[List[str]] = None
|
||||
) -> Optional[PngImagePlugin.PngInfo]:
|
||||
self, img: Image, keys_to_remove: list[str] | None = None
|
||||
) -> PngImagePlugin.PngInfo | None:
|
||||
"""
|
||||
Create a new PngInfo with sensitive keys removed.
|
||||
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ def main(
|
|||
help="Show detailed debug logs for every file processed.",
|
||||
),
|
||||
version: bool = typer.Option(
|
||||
None,
|
||||
"--version",
|
||||
"-v",
|
||||
callback=version_callback,
|
||||
None,
|
||||
"--version",
|
||||
"-v",
|
||||
callback=version_callback,
|
||||
is_eager=True,
|
||||
help="Show the application version and exit."
|
||||
),
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ of large batches (1000+ files).
|
|||
|
||||
import logging
|
||||
import threading
|
||||
from collections.abc import Callable, Iterable
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Callable, Iterable, List, Optional
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
|
|
@ -30,8 +30,8 @@ class FileResult:
|
|||
filepath: Path
|
||||
success: bool
|
||||
action: str # "scrubbed", "skipped", "dry-run"
|
||||
output_path: Optional[Path] = None
|
||||
error: Optional[str] = None
|
||||
output_path: Path | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -43,8 +43,8 @@ class BatchSummary:
|
|||
skipped: int = 0
|
||||
failed: int = 0
|
||||
dry_run: bool = False
|
||||
output_dir: Optional[Path] = None
|
||||
results: List[FileResult] = field(default_factory=list)
|
||||
output_dir: Path | None = None
|
||||
results: list[FileResult] = field(default_factory=list)
|
||||
|
||||
|
||||
class BatchProcessor:
|
||||
|
|
@ -58,7 +58,7 @@ class BatchProcessor:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
output_dir: Optional[str] = None,
|
||||
output_dir: str | None = None,
|
||||
dry_run: bool = False,
|
||||
max_workers: int = 4,
|
||||
):
|
||||
|
|
@ -73,7 +73,7 @@ class BatchProcessor:
|
|||
self.output_dir = Path(output_dir) if output_dir else Path("./scrubbed")
|
||||
self.dry_run = dry_run
|
||||
self.max_workers = max_workers
|
||||
self.results: List[FileResult] = []
|
||||
self.results: list[FileResult] = []
|
||||
|
||||
# Thread synchronization
|
||||
self._path_lock = threading.Lock() # Protects unique path generation
|
||||
|
|
@ -92,7 +92,7 @@ class BatchProcessor:
|
|||
Returns:
|
||||
FileResult with success status and details.
|
||||
"""
|
||||
output_path: Optional[Path] = None # Track reserved path for cleanup
|
||||
output_path: Path | None = None # Track reserved path for cleanup
|
||||
|
||||
try:
|
||||
# Dry-run mode: just report what would happen
|
||||
|
|
@ -151,8 +151,8 @@ class BatchProcessor:
|
|||
def process_batch(
|
||||
self,
|
||||
files: Iterable[Path],
|
||||
progress_callback: Optional[Callable[[FileResult], None]] = None,
|
||||
) -> List[FileResult]:
|
||||
progress_callback: Callable[[FileResult], None] | None = None,
|
||||
) -> list[FileResult]:
|
||||
"""
|
||||
Process multiple files concurrently using ThreadPoolExecutor.
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ class BatchProcessor:
|
|||
|
||||
return self.results
|
||||
|
||||
def process_batch_sequential(self, files: Iterable[Path]) -> List[FileResult]:
|
||||
def process_batch_sequential(self, files: Iterable[Path]) -> list[FileResult]:
|
||||
"""
|
||||
Process files sequentially (legacy behavior for debugging).
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ class BatchProcessor:
|
|||
with self._results_lock:
|
||||
self.results.append(result)
|
||||
|
||||
def _cleanup_reserved_path(self, output_path: Optional[Path]) -> None:
|
||||
def _cleanup_reserved_path(self, output_path: Path | None) -> None:
|
||||
"""
|
||||
Remove empty placeholder file created during path reservation.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ format-specific processors (JpegProcessor, PngProcessor).
|
|||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Any, cast
|
||||
|
||||
import piexif # pyright: ignore[reportMissingTypeStubs]
|
||||
from PIL import Image
|
||||
|
|
@ -48,13 +48,13 @@ class ImageHandler(MetadataHandler):
|
|||
filepath: Path to the image file to process.
|
||||
"""
|
||||
super().__init__(filepath)
|
||||
self.processors = {
|
||||
self.processors: dict[str, JpegProcessor | PngProcessor] = {
|
||||
"jpeg": JpegProcessor(),
|
||||
"png": PngProcessor(),
|
||||
}
|
||||
self.tags_to_delete = []
|
||||
self.detected_format: Optional[str] = None
|
||||
self.text_keys_to_delete = []
|
||||
self.tags_to_delete: list[int] = []
|
||||
self.detected_format: str | None = None
|
||||
self.text_keys_to_delete: list[str] = []
|
||||
|
||||
def _detect_format(self) -> str:
|
||||
"""
|
||||
|
|
@ -101,7 +101,8 @@ class ImageHandler(MetadataHandler):
|
|||
self.metadata = result["data"]
|
||||
self.tags_to_delete = result["tags_to_delete"]
|
||||
# Store text keys for PNG processing
|
||||
self.text_keys_to_delete = result.get("text_keys", [])
|
||||
if isinstance(result, PngProcessor):
|
||||
self.text_keys_to_delete = result.get("text_keys", [])
|
||||
return self.metadata
|
||||
|
||||
def wipe(self) -> None:
|
||||
|
|
@ -120,18 +121,16 @@ class ImageHandler(MetadataHandler):
|
|||
raise UnsupportedFormatError(f"Unsupported format: {self.detected_format}")
|
||||
|
||||
with Image.open(Path(self.filepath)) as img:
|
||||
self.processed_metadata = processor.delete_metadata(
|
||||
img, self.tags_to_delete
|
||||
self.processed_metadata = cast(
|
||||
dict[str, Any], processor.delete_metadata(img, self.tags_to_delete)
|
||||
)
|
||||
# For PNG, also get clean PngInfo
|
||||
if self.detected_format == "png" and hasattr(
|
||||
processor, "get_clean_pnginfo"
|
||||
):
|
||||
if isinstance(processor, PngProcessor):
|
||||
self.clean_pnginfo = processor.get_clean_pnginfo(
|
||||
img, self.text_keys_to_delete
|
||||
)
|
||||
|
||||
def save(self, output_path: Optional[str | Path] = None) -> None:
|
||||
def save(self, output_path: str | Path | None = None) -> None:
|
||||
"""
|
||||
Writes the changes to a copy of the original file.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ must implement the read, wipe, and save methods.
|
|||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
|
||||
class MetadataHandler(ABC):
|
||||
|
|
@ -32,11 +32,11 @@ class MetadataHandler(ABC):
|
|||
filepath: Path to the file to process.
|
||||
"""
|
||||
self.filepath = filepath
|
||||
self.metadata: Dict[str, Any] = {}
|
||||
self.processed_metadata: Dict[str, Any] = {}
|
||||
self.metadata: dict[str, Any] = {}
|
||||
self.processed_metadata: dict[str, Any] = {}
|
||||
|
||||
@abstractmethod
|
||||
def read(self) -> Dict[str, Any]:
|
||||
def read(self) -> dict[str, Any]:
|
||||
"""
|
||||
Extract metadata from the file.
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ class MetadataHandler(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save(self, output_path: Optional[str] = None) -> None:
|
||||
def save(self, output_path: str | None = None) -> None:
|
||||
"""
|
||||
Save the processed file with cleaned metadata.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ This module provides functions for displaying metadata and batch processing
|
|||
results in beautifully formatted tables and panels using the Rich library.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
|
|
@ -18,7 +18,7 @@ from src.utils.formatter import clean_value
|
|||
console = Console()
|
||||
|
||||
|
||||
def print_metadata_table(metadata: Dict[str, Any]):
|
||||
def print_metadata_table(metadata: dict[str, Any]):
|
||||
"""
|
||||
Display metadata in a formatted table organized by logical groups.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ This module provides functions to find and yield files for processing,
|
|||
supporting recursive directory traversal with extension filtering.
|
||||
"""
|
||||
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
from typing import Generator
|
||||
|
||||
|
||||
def get_target_files(input_path_str: Path, ext: str) -> Generator[Path, None, None]:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._dump import dump as dump
|
||||
from ._exceptions import *
|
||||
from ._exif import *
|
||||
from ._insert import insert as insert
|
||||
from ._load import load as load
|
||||
from ._remove import remove as remove
|
||||
from ._transplant import transplant as transplant
|
||||
|
||||
__all__ = ["remove", "load", "dump", "transplant", "insert", "VERSION"]
|
||||
|
||||
VERSION: str
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
def split_into_segments(data): # -> list[bytes]:
|
||||
"""Slices JPEG meta data into a list from JPEG binary data.
|
||||
"""
|
||||
...
|
||||
|
||||
def read_exif_from_file(filename): # -> bytes | None:
|
||||
"""Slices JPEG meta data into a list from JPEG binary data.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_exif_seg(segments): # -> None:
|
||||
"""Returns Exif from JPEG meta data list
|
||||
"""
|
||||
...
|
||||
|
||||
def merge_segments(segments, exif=...): # -> bytes:
|
||||
"""Merges Exif with APP0 and APP1 manipulations.
|
||||
"""
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._common import *
|
||||
from ._exif import *
|
||||
|
||||
TIFF_HEADER_LENGTH = ...
|
||||
def dump(exif_dict_original): # -> bytes:
|
||||
"""
|
||||
py:function:: piexif.load(data)
|
||||
|
||||
Return exif as bytes.
|
||||
|
||||
:param dict exif: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
|
||||
:return: Exif
|
||||
:rtype: bytes
|
||||
"""
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
class InvalidImageDataError(ValueError):
|
||||
...
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
"""
|
||||
Type stubs for piexif EXIF tag definitions.
|
||||
"""
|
||||
|
||||
from typing import Final
|
||||
|
||||
class TYPES:
|
||||
"""EXIF data type identifiers."""
|
||||
|
||||
Byte: Final[int] = ...
|
||||
Ascii: Final[int] = ...
|
||||
Short: Final[int] = ...
|
||||
Long: Final[int] = ...
|
||||
Rational: Final[int] = ...
|
||||
SByte: Final[int] = ...
|
||||
Undefined: Final[int] = ...
|
||||
SShort: Final[int] = ...
|
||||
SLong: Final[int] = ...
|
||||
SRational: Final[int] = ...
|
||||
Float: Final[int] = ...
|
||||
DFloat: Final[int] = ...
|
||||
|
||||
TAGS: dict[str, dict[int, dict[str, str | int]]]
|
||||
|
||||
class ImageIFD:
|
||||
"""Exif tag number reference - 0th IFD"""
|
||||
|
||||
ProcessingSoftware: Final[int] = ...
|
||||
NewSubfileType: Final[int] = ...
|
||||
SubfileType: Final[int] = ...
|
||||
ImageWidth: Final[int] = ...
|
||||
ImageLength: Final[int] = ...
|
||||
BitsPerSample: Final[int] = ...
|
||||
Compression: Final[int] = ...
|
||||
PhotometricInterpretation: Final[int] = ...
|
||||
Threshholding: Final[int] = ...
|
||||
CellWidth: Final[int] = ...
|
||||
CellLength: Final[int] = ...
|
||||
FillOrder: Final[int] = ...
|
||||
DocumentName: Final[int] = ...
|
||||
ImageDescription: Final[int] = ...
|
||||
Make: Final[int] = ...
|
||||
Model: Final[int] = ...
|
||||
StripOffsets: Final[int] = ...
|
||||
Orientation: Final[int] = ...
|
||||
SamplesPerPixel: Final[int] = ...
|
||||
RowsPerStrip: Final[int] = ...
|
||||
StripByteCounts: Final[int] = ...
|
||||
XResolution: Final[int] = ...
|
||||
YResolution: Final[int] = ...
|
||||
PlanarConfiguration: Final[int] = ...
|
||||
GrayResponseUnit: Final[int] = ...
|
||||
GrayResponseCurve: Final[int] = ...
|
||||
T4Options: Final[int] = ...
|
||||
T6Options: Final[int] = ...
|
||||
ResolutionUnit: Final[int] = ...
|
||||
TransferFunction: Final[int] = ...
|
||||
Software: Final[int] = ...
|
||||
DateTime: Final[int] = ...
|
||||
Artist: Final[int] = ...
|
||||
HostComputer: Final[int] = ...
|
||||
Predictor: Final[int] = ...
|
||||
WhitePoint: Final[int] = ...
|
||||
PrimaryChromaticities: Final[int] = ...
|
||||
ColorMap: Final[int] = ...
|
||||
HalftoneHints: Final[int] = ...
|
||||
TileWidth: Final[int] = ...
|
||||
TileLength: Final[int] = ...
|
||||
TileOffsets: Final[int] = ...
|
||||
TileByteCounts: Final[int] = ...
|
||||
SubIFDs: Final[int] = ...
|
||||
InkSet: Final[int] = ...
|
||||
InkNames: Final[int] = ...
|
||||
NumberOfInks: Final[int] = ...
|
||||
DotRange: Final[int] = ...
|
||||
TargetPrinter: Final[int] = ...
|
||||
ExtraSamples: Final[int] = ...
|
||||
SampleFormat: Final[int] = ...
|
||||
SMinSampleValue: Final[int] = ...
|
||||
SMaxSampleValue: Final[int] = ...
|
||||
TransferRange: Final[int] = ...
|
||||
ClipPath: Final[int] = ...
|
||||
XClipPathUnits: Final[int] = ...
|
||||
YClipPathUnits: Final[int] = ...
|
||||
Indexed: Final[int] = ...
|
||||
JPEGTables: Final[int] = ...
|
||||
OPIProxy: Final[int] = ...
|
||||
JPEGProc: Final[int] = ...
|
||||
JPEGInterchangeFormat: Final[int] = ...
|
||||
JPEGInterchangeFormatLength: Final[int] = ...
|
||||
JPEGRestartInterval: Final[int] = ...
|
||||
JPEGLosslessPredictors: Final[int] = ...
|
||||
JPEGPointTransforms: Final[int] = ...
|
||||
JPEGQTables: Final[int] = ...
|
||||
JPEGDCTables: Final[int] = ...
|
||||
JPEGACTables: Final[int] = ...
|
||||
YCbCrCoefficients: Final[int] = ...
|
||||
YCbCrSubSampling: Final[int] = ...
|
||||
YCbCrPositioning: Final[int] = ...
|
||||
ReferenceBlackWhite: Final[int] = ...
|
||||
XMLPacket: Final[int] = ...
|
||||
Rating: Final[int] = ...
|
||||
RatingPercent: Final[int] = ...
|
||||
ImageID: Final[int] = ...
|
||||
CFARepeatPatternDim: Final[int] = ...
|
||||
CFAPattern: Final[int] = ...
|
||||
BatteryLevel: Final[int] = ...
|
||||
Copyright: Final[int] = ...
|
||||
ExposureTime: Final[int] = ...
|
||||
ImageResources: Final[int] = ...
|
||||
ExifTag: Final[int] = ...
|
||||
InterColorProfile: Final[int] = ...
|
||||
GPSTag: Final[int] = ...
|
||||
Interlace: Final[int] = ...
|
||||
TimeZoneOffset: Final[int] = ...
|
||||
SelfTimerMode: Final[int] = ...
|
||||
FlashEnergy: Final[int] = ...
|
||||
SpatialFrequencyResponse: Final[int] = ...
|
||||
Noise: Final[int] = ...
|
||||
FocalPlaneXResolution: Final[int] = ...
|
||||
FocalPlaneYResolution: Final[int] = ...
|
||||
FocalPlaneResolutionUnit: Final[int] = ...
|
||||
ImageNumber: Final[int] = ...
|
||||
SecurityClassification: Final[int] = ...
|
||||
ImageHistory: Final[int] = ...
|
||||
ExposureIndex: Final[int] = ...
|
||||
TIFFEPStandardID: Final[int] = ...
|
||||
SensingMethod: Final[int] = ...
|
||||
XPTitle: Final[int] = ...
|
||||
XPComment: Final[int] = ...
|
||||
XPAuthor: Final[int] = ...
|
||||
XPKeywords: Final[int] = ...
|
||||
XPSubject: Final[int] = ...
|
||||
PrintImageMatching: Final[int] = ...
|
||||
DNGVersion: Final[int] = ...
|
||||
DNGBackwardVersion: Final[int] = ...
|
||||
UniqueCameraModel: Final[int] = ...
|
||||
LocalizedCameraModel: Final[int] = ...
|
||||
CFAPlaneColor: Final[int] = ...
|
||||
CFALayout: Final[int] = ...
|
||||
LinearizationTable: Final[int] = ...
|
||||
BlackLevelRepeatDim: Final[int] = ...
|
||||
BlackLevel: Final[int] = ...
|
||||
BlackLevelDeltaH: Final[int] = ...
|
||||
BlackLevelDeltaV: Final[int] = ...
|
||||
WhiteLevel: Final[int] = ...
|
||||
DefaultScale: Final[int] = ...
|
||||
DefaultCropOrigin: Final[int] = ...
|
||||
DefaultCropSize: Final[int] = ...
|
||||
ColorMatrix1: Final[int] = ...
|
||||
ColorMatrix2: Final[int] = ...
|
||||
CameraCalibration1: Final[int] = ...
|
||||
CameraCalibration2: Final[int] = ...
|
||||
ReductionMatrix1: Final[int] = ...
|
||||
ReductionMatrix2: Final[int] = ...
|
||||
AnalogBalance: Final[int] = ...
|
||||
AsShotNeutral: Final[int] = ...
|
||||
AsShotWhiteXY: Final[int] = ...
|
||||
BaselineExposure: Final[int] = ...
|
||||
BaselineNoise: Final[int] = ...
|
||||
BaselineSharpness: Final[int] = ...
|
||||
BayerGreenSplit: Final[int] = ...
|
||||
LinearResponseLimit: Final[int] = ...
|
||||
CameraSerialNumber: Final[int] = ...
|
||||
LensInfo: Final[int] = ...
|
||||
ChromaBlurRadius: Final[int] = ...
|
||||
AntiAliasStrength: Final[int] = ...
|
||||
ShadowScale: Final[int] = ...
|
||||
DNGPrivateData: Final[int] = ...
|
||||
MakerNoteSafety: Final[int] = ...
|
||||
CalibrationIlluminant1: Final[int] = ...
|
||||
CalibrationIlluminant2: Final[int] = ...
|
||||
BestQualityScale: Final[int] = ...
|
||||
RawDataUniqueID: Final[int] = ...
|
||||
OriginalRawFileName: Final[int] = ...
|
||||
OriginalRawFileData: Final[int] = ...
|
||||
ActiveArea: Final[int] = ...
|
||||
MaskedAreas: Final[int] = ...
|
||||
AsShotICCProfile: Final[int] = ...
|
||||
AsShotPreProfileMatrix: Final[int] = ...
|
||||
CurrentICCProfile: Final[int] = ...
|
||||
CurrentPreProfileMatrix: Final[int] = ...
|
||||
ColorimetricReference: Final[int] = ...
|
||||
CameraCalibrationSignature: Final[int] = ...
|
||||
ProfileCalibrationSignature: Final[int] = ...
|
||||
AsShotProfileName: Final[int] = ...
|
||||
NoiseReductionApplied: Final[int] = ...
|
||||
ProfileName: Final[int] = ...
|
||||
ProfileHueSatMapDims: Final[int] = ...
|
||||
ProfileHueSatMapData1: Final[int] = ...
|
||||
ProfileHueSatMapData2: Final[int] = ...
|
||||
ProfileToneCurve: Final[int] = ...
|
||||
ProfileEmbedPolicy: Final[int] = ...
|
||||
ProfileCopyright: Final[int] = ...
|
||||
ForwardMatrix1: Final[int] = ...
|
||||
ForwardMatrix2: Final[int] = ...
|
||||
PreviewApplicationName: Final[int] = ...
|
||||
PreviewApplicationVersion: Final[int] = ...
|
||||
PreviewSettingsName: Final[int] = ...
|
||||
PreviewSettingsDigest: Final[int] = ...
|
||||
PreviewColorSpace: Final[int] = ...
|
||||
PreviewDateTime: Final[int] = ...
|
||||
RawImageDigest: Final[int] = ...
|
||||
OriginalRawFileDigest: Final[int] = ...
|
||||
SubTileBlockSize: Final[int] = ...
|
||||
RowInterleaveFactor: Final[int] = ...
|
||||
ProfileLookTableDims: Final[int] = ...
|
||||
ProfileLookTableData: Final[int] = ...
|
||||
OpcodeList1: Final[int] = ...
|
||||
OpcodeList2: Final[int] = ...
|
||||
OpcodeList3: Final[int] = ...
|
||||
NoiseProfile: Final[int] = ...
|
||||
ZZZTestSlong1: Final[int] = ...
|
||||
ZZZTestSlong2: Final[int] = ...
|
||||
ZZZTestSByte: Final[int] = ...
|
||||
ZZZTestSShort: Final[int] = ...
|
||||
ZZZTestDFloat: Final[int] = ...
|
||||
|
||||
class ExifIFD:
|
||||
"""Exif tag number reference - Exif IFD"""
|
||||
|
||||
ExposureTime: Final[int] = ...
|
||||
FNumber: Final[int] = ...
|
||||
ExposureProgram: Final[int] = ...
|
||||
SpectralSensitivity: Final[int] = ...
|
||||
ISOSpeedRatings: Final[int] = ...
|
||||
OECF: Final[int] = ...
|
||||
SensitivityType: Final[int] = ...
|
||||
StandardOutputSensitivity: Final[int] = ...
|
||||
RecommendedExposureIndex: Final[int] = ...
|
||||
ISOSpeed: Final[int] = ...
|
||||
ISOSpeedLatitudeyyy: Final[int] = ...
|
||||
ISOSpeedLatitudezzz: Final[int] = ...
|
||||
ExifVersion: Final[int] = ...
|
||||
DateTimeOriginal: Final[int] = ...
|
||||
DateTimeDigitized: Final[int] = ...
|
||||
OffsetTime: Final[int] = ...
|
||||
OffsetTimeOriginal: Final[int] = ...
|
||||
OffsetTimeDigitized: Final[int] = ...
|
||||
ComponentsConfiguration: Final[int] = ...
|
||||
CompressedBitsPerPixel: Final[int] = ...
|
||||
ShutterSpeedValue: Final[int] = ...
|
||||
ApertureValue: Final[int] = ...
|
||||
BrightnessValue: Final[int] = ...
|
||||
ExposureBiasValue: Final[int] = ...
|
||||
MaxApertureValue: Final[int] = ...
|
||||
SubjectDistance: Final[int] = ...
|
||||
MeteringMode: Final[int] = ...
|
||||
LightSource: Final[int] = ...
|
||||
Flash: Final[int] = ...
|
||||
FocalLength: Final[int] = ...
|
||||
Temperature: Final[int] = ...
|
||||
Humidity: Final[int] = ...
|
||||
Pressure: Final[int] = ...
|
||||
WaterDepth: Final[int] = ...
|
||||
Acceleration: Final[int] = ...
|
||||
CameraElevationAngle: Final[int] = ...
|
||||
SubjectArea: Final[int] = ...
|
||||
MakerNote: Final[int] = ...
|
||||
UserComment: Final[int] = ...
|
||||
SubSecTime: Final[int] = ...
|
||||
SubSecTimeOriginal: Final[int] = ...
|
||||
SubSecTimeDigitized: Final[int] = ...
|
||||
FlashpixVersion: Final[int] = ...
|
||||
ColorSpace: Final[int] = ...
|
||||
PixelXDimension: Final[int] = ...
|
||||
PixelYDimension: Final[int] = ...
|
||||
RelatedSoundFile: Final[int] = ...
|
||||
InteroperabilityTag: Final[int] = ...
|
||||
FlashEnergy: Final[int] = ...
|
||||
SpatialFrequencyResponse: Final[int] = ...
|
||||
FocalPlaneXResolution: Final[int] = ...
|
||||
FocalPlaneYResolution: Final[int] = ...
|
||||
FocalPlaneResolutionUnit: Final[int] = ...
|
||||
SubjectLocation: Final[int] = ...
|
||||
ExposureIndex: Final[int] = ...
|
||||
SensingMethod: Final[int] = ...
|
||||
FileSource: Final[int] = ...
|
||||
SceneType: Final[int] = ...
|
||||
CFAPattern: Final[int] = ...
|
||||
CustomRendered: Final[int] = ...
|
||||
ExposureMode: Final[int] = ...
|
||||
WhiteBalance: Final[int] = ...
|
||||
DigitalZoomRatio: Final[int] = ...
|
||||
FocalLengthIn35mmFilm: Final[int] = ...
|
||||
SceneCaptureType: Final[int] = ...
|
||||
GainControl: Final[int] = ...
|
||||
Contrast: Final[int] = ...
|
||||
Saturation: Final[int] = ...
|
||||
Sharpness: Final[int] = ...
|
||||
DeviceSettingDescription: Final[int] = ...
|
||||
SubjectDistanceRange: Final[int] = ...
|
||||
ImageUniqueID: Final[int] = ...
|
||||
CameraOwnerName: Final[int] = ...
|
||||
BodySerialNumber: Final[int] = ...
|
||||
LensSpecification: Final[int] = ...
|
||||
LensMake: Final[int] = ...
|
||||
LensModel: Final[int] = ...
|
||||
LensSerialNumber: Final[int] = ...
|
||||
Gamma: Final[int] = ...
|
||||
|
||||
class GPSIFD:
|
||||
"""Exif tag number reference - GPS IFD"""
|
||||
|
||||
GPSVersionID: Final[int] = ...
|
||||
GPSLatitudeRef: Final[int] = ...
|
||||
GPSLatitude: Final[int] = ...
|
||||
GPSLongitudeRef: Final[int] = ...
|
||||
GPSLongitude: Final[int] = ...
|
||||
GPSAltitudeRef: Final[int] = ...
|
||||
GPSAltitude: Final[int] = ...
|
||||
GPSTimeStamp: Final[int] = ...
|
||||
GPSSatellites: Final[int] = ...
|
||||
GPSStatus: Final[int] = ...
|
||||
GPSMeasureMode: Final[int] = ...
|
||||
GPSDOP: Final[int] = ...
|
||||
GPSSpeedRef: Final[int] = ...
|
||||
GPSSpeed: Final[int] = ...
|
||||
GPSTrackRef: Final[int] = ...
|
||||
GPSTrack: Final[int] = ...
|
||||
GPSImgDirectionRef: Final[int] = ...
|
||||
GPSImgDirection: Final[int] = ...
|
||||
GPSMapDatum: Final[int] = ...
|
||||
GPSDestLatitudeRef: Final[int] = ...
|
||||
GPSDestLatitude: Final[int] = ...
|
||||
GPSDestLongitudeRef: Final[int] = ...
|
||||
GPSDestLongitude: Final[int] = ...
|
||||
GPSDestBearingRef: Final[int] = ...
|
||||
GPSDestBearing: Final[int] = ...
|
||||
GPSDestDistanceRef: Final[int] = ...
|
||||
GPSDestDistance: Final[int] = ...
|
||||
GPSProcessingMethod: Final[int] = ...
|
||||
GPSAreaInformation: Final[int] = ...
|
||||
GPSDateStamp: Final[int] = ...
|
||||
GPSDifferential: Final[int] = ...
|
||||
GPSHPositioningError: Final[int] = ...
|
||||
|
||||
class InteropIFD:
|
||||
"""Exif tag number reference - Interoperability IFD"""
|
||||
|
||||
InteroperabilityIndex: Final[int] = ...
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._common import *
|
||||
|
||||
def insert(exif, image, new_file=...): # -> None:
|
||||
"""
|
||||
py:function:: piexif.insert(exif_bytes, filename)
|
||||
|
||||
Insert exif into JPEG.
|
||||
|
||||
:param bytes exif_bytes: Exif as bytes
|
||||
:param str filename: JPEG
|
||||
"""
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._common import *
|
||||
from ._exif import *
|
||||
|
||||
LITTLE_ENDIAN = ...
|
||||
def load(input_data, key_is_name=...): # -> dict[str, Any]:
|
||||
"""
|
||||
py:function:: piexif.load(filename)
|
||||
|
||||
Return exif data as dict. Keys(IFD name), be contained, are "0th", "Exif", "GPS", "Interop", "1st", and "thumbnail". Without "thumbnail", the value is dict(tag name/tag value). "thumbnail" value is JPEG as bytes.
|
||||
|
||||
:param str filename: JPEG or TIFF
|
||||
:return: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
|
||||
:rtype: dict
|
||||
"""
|
||||
...
|
||||
|
||||
class _ExifReader:
|
||||
def __init__(self, data) -> None:
|
||||
...
|
||||
|
||||
def get_ifd_dict(self, pointer, ifd_name, read_unknown=...): # -> dict[Any, Any]:
|
||||
...
|
||||
|
||||
def convert_value(self, val): # -> Any | tuple[Any, Any] | bytes | tuple[Any, ...] | tuple[tuple[Any, Any], ...]:
|
||||
...
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._common import *
|
||||
|
||||
def remove(src, new_file=...): # -> None:
|
||||
"""
|
||||
py:function:: piexif.remove(filename)
|
||||
|
||||
Remove exif from JPEG.
|
||||
|
||||
:param str filename: JPEG
|
||||
"""
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from ._common import *
|
||||
|
||||
def transplant(exif_src, image, new_file=...): # -> None:
|
||||
"""
|
||||
py:function:: piexif.transplant(filename1, filename2)
|
||||
|
||||
Transplant exif from filename1 to filename2.
|
||||
|
||||
:param str filename1: JPEG
|
||||
:param str filename2: JPEG
|
||||
"""
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
def split(data): # -> list[Any]:
|
||||
...
|
||||
|
||||
def merge_chunks(chunks): # -> bytes:
|
||||
...
|
||||
|
||||
def set_vp8x(chunks):
|
||||
...
|
||||
|
||||
def get_file_header(chunks): # -> bytes:
|
||||
...
|
||||
|
||||
def get_exif(data): # -> None:
|
||||
...
|
||||
|
||||
def insert_exif_into_chunks(chunks, exif_bytes):
|
||||
...
|
||||
|
||||
def insert(webp_bytes, exif_bytes): # -> bytes:
|
||||
...
|
||||
|
||||
def remove(webp_bytes): # -> bytes:
|
||||
...
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
class UserComment:
|
||||
ASCII = ...
|
||||
JIS = ...
|
||||
UNICODE = ...
|
||||
ENCODINGS = ...
|
||||
_JIS = ...
|
||||
_UNICODE = ...
|
||||
_PREFIX_SIZE = ...
|
||||
_ASCII_PREFIX = ...
|
||||
_JIS_PREFIX = ...
|
||||
_UNICODE_PREFIX = ...
|
||||
_UNDEFINED_PREFIX = ...
|
||||
@classmethod
|
||||
def load(cls, data):
|
||||
"""
|
||||
Convert "UserComment" value in exif format to str.
|
||||
|
||||
:param bytes data: "UserComment" value from exif
|
||||
:return: u"foobar"
|
||||
:rtype: str(Unicode)
|
||||
:raises: ValueError if the data does not conform to the EXIF specification,
|
||||
or the encoding is unsupported.
|
||||
"""
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def dump(cls, data, encoding=...):
|
||||
"""
|
||||
Convert str to appropriate format for "UserComment".
|
||||
|
||||
:param data: Like u"foobar"
|
||||
:param str encoding: "ascii", "jis", or "unicode"
|
||||
:return: b"ASCII\x00\x00\x00foobar"
|
||||
:rtype: bytes
|
||||
:raises: ValueError if the encoding is unsupported.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue