Blackified files

This commit is contained in:
Rhet Turnbull
2022-01-22 09:25:08 -08:00
parent edcc7ea34f
commit 3bafdf7bfd
27 changed files with 327 additions and 300 deletions

View File

@@ -42,7 +42,10 @@ _PHOTOS_5_VERSION = "5000" # I've seen both 5001 and 6000. 6000 is most common
# Ranges for model version by Photos version
_PHOTOS_5_MODEL_VERSION = [13000, 13999]
_PHOTOS_6_MODEL_VERSION = [14000, 14999]
_PHOTOS_7_MODEL_VERSION = [15000, 15999] # Monterey developer preview is 15134, 12.1 is 15331
_PHOTOS_7_MODEL_VERSION = [
15000,
15999,
] # Monterey developer preview is 15134, 12.1 is 15331
# some table names differ between Photos 5 and Photos 6
_DB_TABLE_NAMES = {
@@ -260,7 +263,7 @@ EXTENDED_ATTRIBUTE_NAMES_QUOTED = [f"'{x}'" for x in EXTENDED_ATTRIBUTE_NAMES]
OSXPHOTOS_EXPORT_DB = ".osxphotos_export.db"
# bit flags for burst images ("burstPickType")
BURST_PICK_TYPE_NONE = 0b0 # 0: sometimes used for single images with a burst UUID
BURST_PICK_TYPE_NONE = 0b0 # 0: sometimes used for single images with a burst UUID
BURST_NOT_SELECTED = 0b10 # 2: burst image is not selected
BURST_DEFAULT_PICK = 0b100 # 4: burst image is the one Photos picked to be key image before any selections made
BURST_SELECTED = 0b1000 # 8: burst image is selected

View File

@@ -75,37 +75,37 @@ class AdjustmentsInfo:
@property
def plist(self):
"""The actual adjustments plist content as a dict """
"""The actual adjustments plist content as a dict"""
return self._plist
@property
def data(self):
"""The raw adjustments data as a binary blob """
"""The raw adjustments data as a binary blob"""
return self._data
@property
def editor(self):
"""The editor bundle ID for app/plug-in which made the adjustments """
"""The editor bundle ID for app/plug-in which made the adjustments"""
return self._editor_bundle_id
@property
def format_id(self):
"""The value of the adjustmentFormatIdentifier field in the plist """
"""The value of the adjustmentFormatIdentifier field in the plist"""
return self._format_identifier
@property
def base_version(self):
"""Value of adjustmentBaseVersion field """
"""Value of adjustmentBaseVersion field"""
return self._base_version
@property
def format_version(self):
"""The value of the adjustmentFormatVersion in the plist """
"""The value of the adjustmentFormatVersion in the plist"""
return self._format_version
@property
def timestamp(self):
"""The time stamp of the adjustment as timezone aware datetime.datetime object or None if no timestamp """
"""The time stamp of the adjustment as timezone aware datetime.datetime object or None if no timestamp"""
return self._timestamp
@property

View File

@@ -24,12 +24,14 @@ from ._constants import (
from .datetime_utils import get_local_tz
from .query_builder import get_query
__all__ = ["sort_list_by_keys",
"AlbumInfoBaseClass",
"AlbumInfo",
"ImportInfo",
"ProjectInfo",
"FolderInfo"]
__all__ = [
"sort_list_by_keys",
"AlbumInfoBaseClass",
"AlbumInfo",
"ImportInfo",
"ProjectInfo",
"FolderInfo",
]
def sort_list_by_keys(values, sort_keys):

View File

@@ -71,49 +71,51 @@ from .sqlgrep import sqlgrep
from .uti import get_preferred_uti_extension
from .utils import expand_and_validate_filepath, load_function, normalize_fs_path
__all__ = ["verbose_",
"get_photos_db",
"DateTimeISO8601",
"BitMathSize",
"TimeISO8601",
"FunctionCall",
"CLI_Obj",
"deleted_options",
"QUERY_OPTIONS",
"cli",
"export",
"help",
"query",
"print_photo_info",
"export_photo",
"export_photo_to_directory",
"get_filenames_from_template",
"get_dirnames_from_template",
"find_files_in_branch",
"load_uuid_from_file",
"write_export_report",
"cleanup_files",
"write_finder_tags",
"write_extended_attributes",
"run_post_command",
"install",
"uninstall",
"keywords",
"albums",
"persons",
"labels",
"info",
"places",
"dump",
"list_libraries",
"uuid",
"about",
"tutorial",
"repl",
"grep",
"debug_dump",
"snap",
"diff"]
__all__ = [
"verbose_",
"get_photos_db",
"DateTimeISO8601",
"BitMathSize",
"TimeISO8601",
"FunctionCall",
"CLI_Obj",
"deleted_options",
"QUERY_OPTIONS",
"cli",
"export",
"help",
"query",
"print_photo_info",
"export_photo",
"export_photo_to_directory",
"get_filenames_from_template",
"get_dirnames_from_template",
"find_files_in_branch",
"load_uuid_from_file",
"write_export_report",
"cleanup_files",
"write_finder_tags",
"write_extended_attributes",
"run_post_command",
"install",
"uninstall",
"keywords",
"albums",
"persons",
"labels",
"info",
"places",
"dump",
"list_libraries",
"uuid",
"about",
"tutorial",
"repl",
"grep",
"debug_dump",
"snap",
"diff",
]
# global variable to control verbose output
# set via --verbose/-V

View File

@@ -22,14 +22,16 @@ from .phototemplate import (
get_template_help,
)
__all__ = ["ExportCommand",
"template_help",
"tutorial_help",
"rich_text",
"strip_md_header_and_links",
"strip_md_links",
"strip_html_comments",
"get_tutorial_text"]
__all__ = [
"ExportCommand",
"template_help",
"tutorial_help",
"rich_text",
"strip_md_header_and_links",
"strip_md_links",
"strip_html_comments",
"get_tutorial_text",
]
# TODO: The following help text could probably be done as mako template

View File

@@ -1,14 +1,16 @@
""" ConfigOptions class to load/save config settings for osxphotos CLI """
import toml
__all__ = ["ConfigOptionsException",
"ConfigOptionsInvalidError",
"ConfigOptionsLoadError",
"ConfigOptions"]
__all__ = [
"ConfigOptionsException",
"ConfigOptionsInvalidError",
"ConfigOptionsLoadError",
"ConfigOptions",
]
class ConfigOptionsException(Exception):
""" Invalid combination of options. """
"""Invalid combination of options."""
def __init__(self, message):
self.message = message
@@ -24,10 +26,10 @@ class ConfigOptionsLoadError(ConfigOptionsException):
class ConfigOptions:
""" data class to store and load options for osxphotos commands """
"""data class to store and load options for osxphotos commands"""
def __init__(self, name, attrs, ignore=None):
""" init ConfigOptions class
"""init ConfigOptions class
Args:
name: name for these options, will be used for section heading in TOML file when saving/loading from file
@@ -58,7 +60,7 @@ class ConfigOptions:
raise KeyError(f"Missing argument: {attr}")
def validate(self, exclusive=None, inclusive=None, dependent=None, cli=False):
""" validate combinations of otions
"""validate combinations of otions
Args:
exclusive: list of tuples in form [("option_1", "option_2")...] which are exclusive;
@@ -126,7 +128,7 @@ class ConfigOptions:
return True
def write_to_file(self, filename):
""" Write self to TOML file
"""Write self to TOML file
Args:
filename: full path to TOML file to write; filename will be overwritten if it exists
@@ -146,7 +148,7 @@ class ConfigOptions:
toml.dump({self._name: data}, fd)
def load_from_file(self, filename, override=False):
""" Load options from a TOML file.
"""Load options from a TOML file.
Args:
filename: full path to TOML file

View File

@@ -6,67 +6,67 @@ __all__ = ["DateTimeFormatter"]
class DateTimeFormatter:
""" provides property access to formatted datetime.datetime strftime values """
"""provides property access to formatted datetime.datetime strftime values"""
def __init__(self, dt: datetime.datetime):
self.dt = dt
@property
def date(self):
""" ISO date in form 2020-03-22 """
"""ISO date in form 2020-03-22"""
return self.dt.date().isoformat()
@property
def year(self):
""" 4 digit year """
"""4 digit year"""
return f"{self.dt.year}"
@property
def yy(self):
""" 2 digit year """
"""2 digit year"""
return f"{self.dt.strftime('%y')}"
@property
def mm(self):
""" 2 digit month """
"""2 digit month"""
return f"{self.dt.strftime('%m')}"
@property
def month(self):
""" Month as locale's full name """
"""Month as locale's full name"""
return f"{self.dt.strftime('%B')}"
@property
def mon(self):
""" Month as locale's abbreviated name """
"""Month as locale's abbreviated name"""
return f"{self.dt.strftime('%b')}"
@property
def dd(self):
""" 2-digit day of the month """
"""2-digit day of the month"""
return f"{self.dt.strftime('%d')}"
@property
def dow(self):
""" Day of week as locale's name """
"""Day of week as locale's name"""
return f"{self.dt.strftime('%A')}"
@property
def doy(self):
""" Julian day of year starting from 001 """
"""Julian day of year starting from 001"""
return f"{self.dt.strftime('%j')}"
@property
def hour(self):
""" 2-digit hour """
"""2-digit hour"""
return f"{self.dt.strftime('%H')}"
@property
def min(self):
""" 2-digit minute """
"""2-digit minute"""
return f"{self.dt.strftime('%M')}"
@property
def sec(self):
""" 2-digit second """
"""2-digit second"""
return f"{self.dt.strftime('%S')}"

View File

@@ -2,17 +2,19 @@
import datetime
__all__ = ["get_local_tz",
"datetime_has_tz",
"datetime_tz_to_utc",
"datetime_remove_tz",
"datetime_naive_to_utc",
"datetime_naive_to_local",
"datetime_utc_to_local"]
__all__ = [
"get_local_tz",
"datetime_has_tz",
"datetime_tz_to_utc",
"datetime_remove_tz",
"datetime_naive_to_utc",
"datetime_naive_to_local",
"datetime_utc_to_local",
]
def get_local_tz(dt):
""" Return local timezone as datetime.timezone tzinfo for dt
"""Return local timezone as datetime.timezone tzinfo for dt
Args:
dt: datetime.datetime
@@ -30,7 +32,7 @@ def get_local_tz(dt):
def datetime_has_tz(dt):
""" Return True if datetime dt has tzinfo else False
"""Return True if datetime dt has tzinfo else False
Args:
dt: datetime.datetime
@@ -49,7 +51,7 @@ def datetime_has_tz(dt):
def datetime_tz_to_utc(dt):
""" Convert datetime.datetime object with timezone to UTC timezone
"""Convert datetime.datetime object with timezone to UTC timezone
Args:
dt: datetime.datetime object
@@ -72,7 +74,7 @@ def datetime_tz_to_utc(dt):
def datetime_remove_tz(dt):
""" Remove timezone from a datetime.datetime object
"""Remove timezone from a datetime.datetime object
Args:
dt: datetime.datetime object with tzinfo
@@ -91,7 +93,7 @@ def datetime_remove_tz(dt):
def datetime_naive_to_utc(dt):
""" Convert naive (timezone unaware) datetime.datetime
"""Convert naive (timezone unaware) datetime.datetime
to aware timezone in UTC timezone
Args:
@@ -119,7 +121,7 @@ def datetime_naive_to_utc(dt):
def datetime_naive_to_local(dt):
""" Convert naive (timezone unaware) datetime.datetime
"""Convert naive (timezone unaware) datetime.datetime
to aware timezone in local timezone
Args:
@@ -147,7 +149,7 @@ def datetime_naive_to_local(dt):
def datetime_utc_to_local(dt):
""" Convert datetime.datetime object in UTC timezone to local timezone
"""Convert datetime.datetime object in UTC timezone to local timezone
Args:
dt: datetime.datetime object

View File

@@ -17,12 +17,14 @@ import subprocess
from abc import ABC, abstractmethod
from functools import lru_cache # pylint: disable=syntax-error
__all__ = ["escape_str",
"unescape_str",
"terminate_exiftool",
"get_exiftool_path",
"ExifTool",
"ExifToolCaching"]
__all__ = [
"escape_str",
"unescape_str",
"terminate_exiftool",
"get_exiftool_path",
"ExifTool",
"ExifToolCaching",
]
# exiftool -stay_open commands outputs this EOF marker after command is run
EXIFTOOL_STAYOPEN_EOF = "{ready}"

View File

@@ -15,7 +15,7 @@ __all__ = ["FileUtilABC", "FileUtilMacOS", "FileUtil", "FileUtilNoOp"]
class FileUtilABC(ABC):
""" Abstract base class for FileUtil """
"""Abstract base class for FileUtil"""
@classmethod
@abstractmethod
@@ -69,14 +69,14 @@ class FileUtilABC(ABC):
class FileUtilMacOS(FileUtilABC):
""" Various file utilities """
"""Various file utilities"""
@classmethod
def hardlink(cls, src, dest):
""" Hardlinks a file from src path to dest path
src: source path as string
dest: destination path as string
Raises exception if linking fails or either path is None """
"""Hardlinks a file from src path to dest path
src: source path as string
dest: destination path as string
Raises exception if linking fails or either path is None"""
if src is None or dest is None:
raise ValueError("src and dest must not be None", src, dest)
@@ -92,7 +92,7 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def copy(cls, src, dest):
""" Copies a file from src path to dest path
"""Copies a file from src path to dest path
Args:
src: source path as string; must be a valid file path
@@ -126,7 +126,7 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def unlink(cls, filepath):
""" unlink filepath; if it's pathlib.Path, use Path.unlink, otherwise use os.unlink """
"""unlink filepath; if it's pathlib.Path, use Path.unlink, otherwise use os.unlink"""
if isinstance(filepath, pathlib.Path):
filepath.unlink()
else:
@@ -134,7 +134,7 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def rmdir(cls, dirpath):
""" remove directory filepath; dirpath must be empty """
"""remove directory filepath; dirpath must be empty"""
if isinstance(dirpath, pathlib.Path):
dirpath.rmdir()
else:
@@ -142,7 +142,7 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def utime(cls, path, times):
""" Set the access and modified time of path. """
"""Set the access and modified time of path."""
os.utime(path, times)
@classmethod
@@ -188,20 +188,20 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def file_sig(cls, f1):
""" return os.stat signature for file f1 """
"""return os.stat signature for file f1"""
return cls._sig(os.stat(f1))
@classmethod
def convert_to_jpeg(cls, src_file, dest_file, compression_quality=1.0):
""" converts image file src_file to jpeg format as dest_file
"""converts image file src_file to jpeg format as dest_file
Args:
src_file: image file to convert
dest_file: destination path to write converted file to
compression quality: JPEG compression quality in range 0.0 <= compression_quality <= 1.0; default 1.0 (best quality)
Args:
src_file: image file to convert
dest_file: destination path to write converted file to
compression quality: JPEG compression quality in range 0.0 <= compression_quality <= 1.0; default 1.0 (best quality)
Returns:
True if success, otherwise False
Returns:
True if success, otherwise False
"""
converter = ImageConverter()
return converter.write_jpeg(
@@ -210,7 +210,7 @@ class FileUtilMacOS(FileUtilABC):
@classmethod
def rename(cls, src, dest):
""" Copy src to dest
"""Copy src to dest
Args:
src: path to source file
@@ -225,25 +225,25 @@ class FileUtilMacOS(FileUtilABC):
@staticmethod
def _sig(st):
""" return tuple of (mode, size, mtime) of file based on os.stat
Args:
st: os.stat signature
"""return tuple of (mode, size, mtime) of file based on os.stat
Args:
st: os.stat signature
"""
# use int(st.st_mtime) because ditto does not copy fractional portion of mtime
return (stat.S_IFMT(st.st_mode), st.st_size, int(st.st_mtime))
class FileUtil(FileUtilMacOS):
""" Various file utilities """
"""Various file utilities"""
pass
class FileUtilNoOp(FileUtil):
""" No-Op implementation of FileUtil for testing / dry-run mode
all methods with exception of cmp, cmp_file_sig and file_cmp are no-op
cmp and cmp_file_sig functions as FileUtil methods do
file_cmp returns mock data
"""No-Op implementation of FileUtil for testing / dry-run mode
all methods with exception of cmp, cmp_file_sig and file_cmp are no-op
cmp and cmp_file_sig functions as FileUtil methods do
file_cmp returns mock data
"""
@staticmethod

View File

@@ -1,4 +1,3 @@
__all__ = ["MomentInfo"]
"""MomentInfo class with details about photo moments."""

View File

@@ -4,11 +4,13 @@ import pathvalidate
from ._constants import MAX_DIRNAME_LEN, MAX_FILENAME_LEN
__all__ = ["sanitize_filepath",
"is_valid_filepath",
"sanitize_filename",
"sanitize_dirname",
"sanitize_pathpart"]
__all__ = [
"sanitize_filepath",
"is_valid_filepath",
"sanitize_filename",
"sanitize_dirname",
"sanitize_pathpart",
]
def sanitize_filepath(filepath):

View File

@@ -53,7 +53,7 @@ class PersonInfo:
@property
def photos(self):
""" Returns list of PhotoInfo objects associated with this person """
"""Returns list of PhotoInfo objects associated with this person"""
return self._db.photos_by_uuid(self._db._dbfaces_pk[self._pk])
@property
@@ -73,7 +73,7 @@ class PersonInfo:
return []
def asdict(self):
""" Returns dictionary representation of class instance """
"""Returns dictionary representation of class instance"""
keyphoto = self.keyphoto.uuid if self.keyphoto is not None else None
return {
"uuid": self.uuid,
@@ -85,7 +85,7 @@ class PersonInfo:
}
def json(self):
""" Returns JSON representation of class instance """
"""Returns JSON representation of class instance"""
return json.dumps(self.asdict())
def __str__(self):
@@ -203,7 +203,7 @@ class FaceInfo:
@property
def person_info(self):
""" PersonInfo instance for person associated with this face """
"""PersonInfo instance for person associated with this face"""
try:
return self._person
except AttributeError:
@@ -212,7 +212,7 @@ class FaceInfo:
@property
def photo(self):
""" PhotoInfo instance associated with this face """
"""PhotoInfo instance associated with this face"""
try:
return self._photo
except AttributeError:
@@ -294,7 +294,7 @@ class FaceInfo:
return [(x0, y0), (x1, y1)]
def roll_pitch_yaw(self):
""" Roll, pitch, yaw of face in radians as tuple """
"""Roll, pitch, yaw of face in radians as tuple"""
info = self._info
roll = 0 if info["roll"] is None else info["roll"]
pitch = 0 if info["pitch"] is None else info["pitch"]
@@ -304,19 +304,19 @@ class FaceInfo:
@property
def roll(self):
""" Return roll angle in radians of the face region """
"""Return roll angle in radians of the face region"""
roll, _, _ = self.roll_pitch_yaw()
return roll
@property
def pitch(self):
""" Return pitch angle in radians of the face region """
"""Return pitch angle in radians of the face region"""
_, pitch, _ = self.roll_pitch_yaw()
return pitch
@property
def yaw(self):
""" Return yaw angle in radians of the face region """
"""Return yaw angle in radians of the face region"""
_, _, yaw = self.roll_pitch_yaw()
return yaw
@@ -404,7 +404,7 @@ class FaceInfo:
return (int(xr), int(yr))
def asdict(self):
""" Returns dict representation of class instance """
"""Returns dict representation of class instance"""
roll, pitch, yaw = self.roll_pitch_yaw()
return {
"_pk": self._pk,
@@ -453,7 +453,7 @@ class FaceInfo:
}
def json(self):
""" Return JSON representation of FaceInfo instance """
"""Return JSON representation of FaceInfo instance"""
return json.dumps(self.asdict())
def __str__(self):

View File

@@ -48,12 +48,14 @@ from .phototemplate import RenderOptions
from .uti import get_preferred_uti_extension
from .utils import increment_filename, increment_filename_with_count, lineno
__all__ = ["ExportError",
"ExportOptions",
"ExportResults",
"PhotoExporter",
"hexdigest",
"rename_jpeg_files"]
__all__ = [
"ExportError",
"ExportOptions",
"ExportResults",
"PhotoExporter",
"hexdigest",
"rename_jpeg_files",
]
if TYPE_CHECKING:
from .photoinfo import PhotoInfo

View File

@@ -36,25 +36,27 @@ from .fileutil import FileUtil
from .uti import get_preferred_uti_extension
from .utils import _get_os_version, increment_filename
__all__ = ["NSURL_to_path",
"path_to_NSURL",
"check_photokit_authorization",
"request_photokit_authorization",
"PhotoKitError",
"PhotoKitFetchFailed",
"PhotoKitAuthError",
"PhotoKitExportError",
"PhotoKitMediaTypeError",
"ImageData",
"AVAssetData",
"PHAssetResourceData",
"PhotoKitNotificationDelegate",
"PhotoAsset",
"SlowMoVideoExporter",
"VideoAsset",
"LivePhotoRequest",
"LivePhotoAsset",
"PhotoLibrary"]
__all__ = [
"NSURL_to_path",
"path_to_NSURL",
"check_photokit_authorization",
"request_photokit_authorization",
"PhotoKitError",
"PhotoKitFetchFailed",
"PhotoKitAuthError",
"PhotoKitExportError",
"PhotoKitMediaTypeError",
"ImageData",
"AVAssetData",
"PHAssetResourceData",
"PhotoKitNotificationDelegate",
"PhotoAsset",
"SlowMoVideoExporter",
"VideoAsset",
"LivePhotoRequest",
"LivePhotoAsset",
"PhotoLibrary",
]
# NOTE: This requires user have granted access to the terminal (e.g. Terminal.app or iTerm)
# to access Photos. This should happen automatically the first time it's called. I've

View File

@@ -10,9 +10,9 @@ from ..utils import _open_sql_file, normalize_unicode
def _process_comments(self):
""" load the comments and likes data from the database
this is a PhotosDB method that should be imported in
the PhotosDB class definition in photosdb.py
"""load the comments and likes data from the database
this is a PhotosDB method that should be imported in
the PhotosDB class definition in photosdb.py
"""
self._db_hashed_person_id = {}
self._db_comments_uuid = {}
@@ -24,7 +24,7 @@ def _process_comments(self):
@dataclass
class CommentInfo:
""" Class for shared photo comments """
"""Class for shared photo comments"""
datetime: datetime.datetime
user: str
@@ -37,7 +37,7 @@ class CommentInfo:
@dataclass
class LikeInfo:
""" Class for shared photo likes """
"""Class for shared photo likes"""
datetime: datetime.datetime
user: str
@@ -50,16 +50,16 @@ class LikeInfo:
# The following methods do not get imported into PhotosDB
# but will get called by _process_comments
def _process_comments_4(photosdb):
""" process comments and likes info for Photos <= 4
photosdb: PhotosDB instance """
"""process comments and likes info for Photos <= 4
photosdb: PhotosDB instance"""
raise NotImplementedError(
f"Not implemented for database version {photosdb._db_version}."
)
def _process_comments_5(photosdb):
""" process comments and likes info for Photos >= 5
photosdb: PhotosDB instance """
"""process comments and likes info for Photos >= 5
photosdb: PhotosDB instance"""
db = photosdb._tmp_db

View File

@@ -7,10 +7,11 @@ from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
from ..utils import _db_is_locked, _debug, _open_sql_file
from .photosdb_utils import get_db_version
def _process_exifinfo(self):
""" load the exif data from the database
this is a PhotosDB method that should be imported in
the PhotosDB class definition in photosdb.py
"""load the exif data from the database
this is a PhotosDB method that should be imported in
the PhotosDB class definition in photosdb.py
"""
if self._db_version <= _PHOTOS_4_VERSION:
_process_exifinfo_4(self)
@@ -23,15 +24,15 @@ def _process_exifinfo(self):
def _process_exifinfo_4(photosdb):
""" process exif info for Photos <= 4
photosdb: PhotosDB instance """
"""process exif info for Photos <= 4
photosdb: PhotosDB instance"""
photosdb._db_exifinfo_uuid = {}
raise NotImplementedError(f"search info not implemented for this database version")
def _process_exifinfo_5(photosdb):
""" process exif info for Photos >= 5
photosdb: PhotosDB instance """
"""process exif info for Photos >= 5
photosdb: PhotosDB instance"""
db = photosdb._tmp_db

View File

@@ -22,8 +22,7 @@ from .photosdb_utils import get_db_version
def _process_faceinfo(self):
""" Process face information
"""
"""Process face information"""
self._db_faceinfo_pk = {}
self._db_faceinfo_uuid = {}
@@ -36,7 +35,7 @@ def _process_faceinfo(self):
def _process_faceinfo_4(photosdb):
""" Process face information for Photos 4 databases
"""Process face information for Photos 4 databases
Args:
photosdb: an OSXPhotosDB instance
@@ -172,7 +171,7 @@ def _process_faceinfo_4(photosdb):
def _process_faceinfo_5(photosdb):
""" Process face information for Photos 5 databases
"""Process face information for Photos 5 databases
Args:
photosdb: an OSXPhotosDB instance

View File

@@ -22,8 +22,8 @@ from .photosdb_utils import get_db_version
def _process_scoreinfo(self):
""" Process computed photo scores
Note: Only works on Photos version == 5.0
"""Process computed photo scores
Note: Only works on Photos version == 5.0
"""
# _db_scoreinfo_uuid is dict in form {uuid: {score values}}
@@ -38,7 +38,7 @@ def _process_scoreinfo(self):
def _process_scoreinfo_5(photosdb):
""" Process computed photo scores for Photos 5 databases
"""Process computed photo scores for Photos 5 databases
Args:
photosdb: an OSXPhotosDB instance

View File

@@ -35,10 +35,10 @@ from ..utils import _db_is_locked, _debug, _open_sql_file, normalize_unicode
def _process_searchinfo(self):
""" load machine learning/search term label info from a Photos library
db_connection: a connection to the SQLite database file containing the
search terms. In Photos 5, this is called psi.sqlite
Note: Only works on Photos version == 5.0 """
"""load machine learning/search term label info from a Photos library
db_connection: a connection to the SQLite database file containing the
search terms. In Photos 5, this is called psi.sqlite
Note: Only works on Photos version == 5.0"""
# _db_searchinfo_uuid is dict in form {uuid : [list of associated search info records]
self._db_searchinfo_uuid = _db_searchinfo_uuid = {}
@@ -155,7 +155,7 @@ def _process_searchinfo(self):
@property
def labels(self):
""" return list of all search info labels found in the library """
"""return list of all search info labels found in the library"""
if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(f"SearchInfo not implemented for this library version")
return []
@@ -165,7 +165,7 @@ def labels(self):
@property
def labels_normalized(self):
""" return list of all normalized search info labels found in the library """
"""return list of all normalized search info labels found in the library"""
if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(f"SearchInfo not implemented for this library version")
return []
@@ -175,7 +175,7 @@ def labels_normalized(self):
@property
def labels_as_dict(self):
""" return labels as dict of label: count in reverse sorted order (descending) """
"""return labels as dict of label: count in reverse sorted order (descending)"""
if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(f"SearchInfo not implemented for this library version")
return dict()
@@ -187,7 +187,7 @@ def labels_as_dict(self):
@property
def labels_normalized_as_dict(self):
""" return normalized labels as dict of label: count in reverse sorted order (descending) """
"""return normalized labels as dict of label: count in reverse sorted order (descending)"""
if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(f"SearchInfo not implemented for this library version")
return dict()
@@ -201,8 +201,8 @@ def labels_normalized_as_dict(self):
@lru_cache(maxsize=128)
def ints_to_uuid(uuid_0, uuid_1):
""" convert two signed ints into a UUID strings
uuid_0, uuid_1: the two int components of an RFC 4122 UUID """
"""convert two signed ints into a UUID strings
uuid_0, uuid_1: the two int components of an RFC 4122 UUID"""
# assumes uuid imported as uuidlib (to avoid namespace conflict with other uses of uuid)

View File

@@ -16,11 +16,13 @@ from .._constants import (
)
from ..utils import _open_sql_file
__all__ = ["get_db_version",
"get_model_version",
"get_db_model_version",
"UnknownLibraryVersion",
"get_photos_library_version"]
__all__ = [
"get_db_version",
"get_model_version",
"get_db_model_version",
"UnknownLibraryVersion",
"get_photos_library_version",
]
def get_db_version(db_file):

View File

@@ -22,12 +22,14 @@ from .path_utils import sanitize_dirname, sanitize_filename, sanitize_pathpart
from .text_detection import detect_text
from .utils import expand_and_validate_filepath, load_function
__all__ = ["RenderOptions",
"PhotoTemplateParser",
"PhotoTemplate",
"parse_default_kv",
"get_template_help",
"format_str_value"]
__all__ = [
"RenderOptions",
"PhotoTemplateParser",
"PhotoTemplate",
"parse_default_kv",
"get_template_help",
"format_str_value",
]
# TODO: a lot of values are passed from function to function like path_sep--make these all class properties

View File

@@ -14,13 +14,15 @@ from bpylist import archiver
from ._constants import UNICODE_FORMAT
from .utils import normalize_unicode
__all__ = ["PLRevGeoLocationInfo",
"PLRevGeoMapItem",
"PLRevGeoMapItemAdditionalPlaceInfo",
"CNPostalAddress",
"PlaceInfo",
"PlaceInfo4",
"PlaceInfo5"]
__all__ = [
"PLRevGeoLocationInfo",
"PLRevGeoMapItem",
"PLRevGeoMapItemAdditionalPlaceInfo",
"CNPostalAddress",
"PlaceInfo",
"PlaceInfo4",
"PlaceInfo5",
]
# postal address information, returned by PlaceInfo.address
PostalAddress = namedtuple(
@@ -73,7 +75,7 @@ PlaceNames = namedtuple(
# in ZADDITIONALASSETATTRIBUTES.ZREVERSELOCATIONDATA
# These classes are used by bpylist.archiver to unarchive the serialized objects
class PLRevGeoLocationInfo:
""" The top level reverse geolocation object """
"""The top level reverse geolocation object"""
def __init__(
self,
@@ -155,7 +157,7 @@ class PLRevGeoLocationInfo:
class PLRevGeoMapItem:
""" Stores the list of place names, organized by area """
"""Stores the list of place names, organized by area"""
def __init__(self, sortedPlaceInfos, finalPlaceInfos):
self.sortedPlaceInfos = sortedPlaceInfos
@@ -190,7 +192,7 @@ class PLRevGeoMapItem:
class PLRevGeoMapItemAdditionalPlaceInfo:
""" Additional info about individual places """
"""Additional info about individual places"""
def __init__(self, area, name, placeType, dominantOrderType):
self.area = area
@@ -229,7 +231,7 @@ class PLRevGeoMapItemAdditionalPlaceInfo:
class CNPostalAddress:
""" postal address for the reverse geolocation info """
"""postal address for the reverse geolocation info"""
def __init__(
self,
@@ -362,17 +364,17 @@ class PlaceInfo(ABC):
class PlaceInfo4(PlaceInfo):
""" Reverse geolocation place info for a photo (Photos <= 4) """
"""Reverse geolocation place info for a photo (Photos <= 4)"""
def __init__(self, place_names, country_code):
""" place_names: list of place name tuples in ascending order by area
tuple fields are: modelID, place name, place type, area, e.g.
[(5, "St James's Park", 45, 0),
(4, 'Westminster', 16, 22097376),
(3, 'London', 4, 1596146816),
(2, 'England', 2, 180406091776),
(1, 'United Kingdom', 1, 414681432064)]
country_code: two letter country code for the country
"""place_names: list of place name tuples in ascending order by area
tuple fields are: modelID, place name, place type, area, e.g.
[(5, "St James's Park", 45, 0),
(4, 'Westminster', 16, 22097376),
(3, 'London', 4, 1596146816),
(2, 'England', 2, 180406091776),
(1, 'United Kingdom', 1, 414681432064)]
country_code: two letter country code for the country
"""
self._place_names = place_names
self._country_code = country_code
@@ -412,7 +414,7 @@ class PlaceInfo4(PlaceInfo):
)
def _process_place_info(self):
""" Process place_names to set self._name and self._names """
"""Process place_names to set self._name and self._names"""
places = self._place_names
# build a dictionary where key is placetype
@@ -508,38 +510,38 @@ class PlaceInfo4(PlaceInfo):
class PlaceInfo5(PlaceInfo):
""" Reverse geolocation place info for a photo (Photos >= 5) """
"""Reverse geolocation place info for a photo (Photos >= 5)"""
def __init__(self, revgeoloc_bplist):
""" revgeoloc_bplist: a binary plist blob containing
a serialized PLRevGeoLocationInfo object """
"""revgeoloc_bplist: a binary plist blob containing
a serialized PLRevGeoLocationInfo object"""
self._bplist = revgeoloc_bplist
self._plrevgeoloc = archiver.unarchive(revgeoloc_bplist)
self._process_place_info()
@property
def address_str(self):
""" returns the postal address as a string """
"""returns the postal address as a string"""
return self._plrevgeoloc.addressString
@property
def country_code(self):
""" returns the country code """
"""returns the country code"""
return self._plrevgeoloc.countryCode
@property
def ishome(self):
""" returns True if place is user's home address """
"""returns True if place is user's home address"""
return self._plrevgeoloc.isHome
@property
def name(self):
""" returns local place name """
"""returns local place name"""
return self._name
@property
def names(self):
""" returns PlaceNames tuple with detailed reverse geolocation place names """
"""returns PlaceNames tuple with detailed reverse geolocation place names"""
return self._names
@property
@@ -564,7 +566,7 @@ class PlaceInfo5(PlaceInfo):
return postal_address
def _process_place_info(self):
""" Process sortedPlaceInfos to set self._name and self._names """
"""Process sortedPlaceInfos to set self._name and self._names"""
places = self._plrevgeoloc.mapItem.sortedPlaceInfos
# build a dictionary where key is placetype

View File

@@ -1,4 +1,3 @@
__all__ = ["PyReplQuitter", "embed_repl"]
""" Custom Python REPL based on ptpython that allows quitting with custom keywords instead of `quit()` """

View File

@@ -9,7 +9,7 @@ __all__ = ["ScoreInfo"]
@dataclass(frozen=True)
class ScoreInfo:
""" Computed photo score info associated with a photo from the Photos library """
"""Computed photo score info associated with a photo from the Photos library"""
overall: float
curation: float
@@ -38,4 +38,3 @@ class ScoreInfo:
well_chosen_subject: float
well_framed_subject: float
well_timed_shot: float

View File

@@ -1,4 +1,3 @@
__all__ = ["get_preferred_uti_extension", "get_uti_for_extension"]
""" get UTI for a given file extension and the preferred extension for a given UTI """

View File

@@ -24,19 +24,21 @@ from Foundation import NSString
from ._constants import UNICODE_FORMAT
__all__ = ["noop",
"lineno",
"dd_to_dms_str",
"get_system_library_path",
"get_last_library_path",
"list_photo_libraries",
"normalize_fs_path",
"findfiles",
"normalize_unicode",
"increment_filename_with_count",
"increment_filename",
"expand_and_validate_filepath",
"load_function"]
__all__ = [
"noop",
"lineno",
"dd_to_dms_str",
"get_system_library_path",
"get_last_library_path",
"list_photo_libraries",
"normalize_fs_path",
"findfiles",
"normalize_unicode",
"increment_filename_with_count",
"increment_filename",
"expand_and_validate_filepath",
"load_function",
]
_DEBUG = False
@@ -379,7 +381,9 @@ def normalize_unicode(value):
return None
def increment_filename_with_count(filepath: Union[str,pathlib.Path], count: int = 0) -> str:
def increment_filename_with_count(
filepath: Union[str, pathlib.Path], count: int = 0
) -> str:
"""Return filename (1).ext, etc if filename.ext exists
If file exists in filename's parent folder with same stem as filename,