Example #1
0
def get_iphoto_or_aperture_pictures(plistpath: Path, photo_class):
    # The structure of iPhoto and Aperture libraries for the base photo list are excactly the same.
    if not plistpath.exists():
        return []
    s = plistpath.open('rt', encoding='utf-8').read()
    # There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
    s = remove_invalid_xml(s, replace_with='')
    # It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find
    # any & char that is not a &-based entity (&, ", etc.). based on TextMate's XML
    # bundle's regexp
    s, count = re.subn(r'&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)', '', s)
    if count:
        logging.warning("%d invalid XML entities replacement made", count)
    parser = IPhotoPlistParser()
    try:
        plist = parser.parse(io.BytesIO(s.encode('utf-8')))
    except Exception:
        logging.warning("iPhoto plist parsing choked on data: %r",
                        parser.lastdata)
        raise
    result = []
    for key, photo_data in plist['Master Image List'].items():
        if photo_data['MediaType'] != 'Image':
            continue
        photo_path = Path(photo_data['ImagePath'])
        photo = photo_class(photo_path, key)
        result.append(photo)
    return result
Example #2
0
def get_iphoto_or_aperture_pictures(plistpath: Path, photo_class):
    # The structure of iPhoto and Aperture libraries for the base photo list are excactly the same.
    if not plistpath.exists():
        return []
    s = plistpath.open("rt", encoding="utf-8").read()
    # There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
    s = remove_invalid_xml(s, replace_with="")
    # It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find
    # any & char that is not a &-based entity (&, ", etc.). based on TextMate's XML
    # bundle's regexp
    s, count = re.subn(r"&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)", "", s)
    if count:
        logging.warning("%d invalid XML entities replacement made", count)
    parser = IPhotoPlistParser()
    try:
        plist = parser.parse(io.BytesIO(s.encode("utf-8")))
    except Exception:
        logging.warning("iPhoto plist parsing choked on data: %r", parser.lastdata)
        raise
    result = []
    for key, photo_data in plist["Master Image List"].items():
        if photo_data["MediaType"] != "Image":
            continue
        photo_path = Path(photo_data["ImagePath"])
        photo = photo_class(photo_path, key)
        result.append(photo)
    return result
Example #3
0
 def copy_or_move(self, dupe, copy: bool, destination: str, dest_type: DestType):
     source_path = dupe.path
     location_path = first(p for p in self.directories if dupe.path in p)
     dest_path = Path(destination)
     if dest_type in {DestType.Relative, DestType.Absolute}:
         # no filename, no windows drive letter
         source_base = source_path.remove_drive_letter()[:-1]
         if dest_type == DestType.Relative:
             source_base = source_base[location_path:]
         dest_path = dest_path + source_base
     if not dest_path.exists():
         dest_path.makedirs()
     # Add filename to dest_path. For file move/copy, it's not required, but for folders, yes.
     dest_path = dest_path + source_path[-1]
     logging.debug("Copy/Move operation from '%s' to '%s'", source_path, dest_path)
     # Raises an EnvironmentError if there's a problem
     if copy:
         smart_copy(source_path, dest_path)
     else:
         smart_move(source_path, dest_path)
         self.clean_empty_dirs(source_path[:-1])
Example #4
0
 def copy_or_move(self, dupe, copy: bool, destination: str, dest_type: DestType):
     source_path = dupe.path
     location_path = first(p for p in self.directories if dupe.path in p)
     dest_path = Path(destination)
     if dest_type in {DestType.Relative, DestType.Absolute}:
         # no filename, no windows drive letter
         source_base = source_path.remove_drive_letter().parent()
         if dest_type == DestType.Relative:
             source_base = source_base[location_path:]
         dest_path = dest_path[source_base]
     if not dest_path.exists():
         dest_path.makedirs()
     # Add filename to dest_path. For file move/copy, it's not required, but for folders, yes.
     dest_path = dest_path[source_path.name]
     logging.debug("Copy/Move operation from '%s' to '%s'", source_path, dest_path)
     # Raises an EnvironmentError if there's a problem
     if copy:
         smart_copy(source_path, dest_path)
     else:
         smart_move(source_path, dest_path)
         self.clean_empty_dirs(source_path.parent())
Example #5
0
def get_iphoto_or_aperture_pictures(plistpath: Path, photo_class):
    # The structure of iPhoto and Aperture libraries for the base photo list are excactly the same.
    if not plistpath.exists():
        return []
    s = plistpath.open('rt', encoding='utf-8').read()
    # There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
    s = remove_invalid_xml(s, replace_with='')
    # It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find
    # any & char that is not a &-based entity (&, ", etc.). based on TextMate's XML
    # bundle's regexp
    s, count = re.subn(r'&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)', '', s)
    if count:
        logging.warning("%d invalid XML entities replacement made", count)
    plist = plistlib.readPlistFromBytes(s.encode('utf-8'))
    result = []
    for key, photo_data in plist['Master Image List'].items():
        if photo_data['MediaType'] != 'Image':
            continue
        photo_path = Path(photo_data['ImagePath'])
        photo = photo_class(photo_path, key)
        result.append(photo)
    return result