def __str__(self):
        if self.file_name is not None:
            s = "{}\n".format(os.path.split(self.file_name)[1])
        else:
            s = self.ext
        if self.datetime:  # type: datetime.datetime
            s += "Datetime in metadata: {}\n".format(
                self.datetime.strftime("%c"))
            if not datetime_roughly_equal(self.datetime, self.fs_datetime):
                s += "Differs from datetime on file system: {}\n".format(
                    self.fs_datetime.strftime("%c"))
        else:
            s += "Datetime on file system: {}\n".format(
                self.fs_datetime.strftime("%c"))

        s += "Disk cache after metadata read:\n[{}]\n".format(self.in_memory)
        if self.minimum_read_size_in_bytes_datetime is not None:
            s += "Minimum read size to extract datetime: {} of {}\n".format(
                format_size_for_user(self.minimum_read_size_in_bytes_datetime),
                format_size_for_user(self.file_size),
            )
        if self.minimum_read_size_in_bytes_thumbnail:
            s += "Minimum read size to extract thumbnail: {} of {}\n".format(
                format_size_for_user(
                    self.minimum_read_size_in_bytes_thumbnail),
                format_size_for_user(self.file_size),
            )
        if self.minimum_metadata_read_size_in_bytes_all is not None:
            s += "Minimum read size to extract variety of tags: {}\n".format(
                format_size_for_user(
                    self.minimum_metadata_read_size_in_bytes_all))
        else:
            s += "Could not extract variety of tags with minimal read\n"
        return s
    def ctime_mtime_differ(self) -> bool:
        """
        :return: True if the creation time and file system date
         modified time are not roughly the same. If the creation
         date is unknown (zero), the result will be False.
        """

        if not self._mdatatime:
            return False

        return not datetime_roughly_equal(self._mdatatime, self._mtime)
    def mdatatime(self, value: float) -> None:

        # Do not allow the value to be set to anything other than the modification time
        # if we are instructed to never read the metadata date time
        if self.never_read_mdatatime:
            value = self._mtime

        self._mdatatime = value

        # Only set the creation time if there is a value to set
        if value:
            self.mdatatime_caused_ctime_change = not datetime_roughly_equal(
                self.ctime, value)
            self.ctime = value
            if not self._datetime:
                self._datetime = datetime.fromtimestamp(value)
                self._no_datetime_metadata = False
Example #4
0
    def matching_pair(
        self, name: str, extension: str, date_time: datetime
    ) -> SyncRawJpegMatch:
        """
        Checks to see if the image matches an image that has already been
        downloaded.

        Image name (minus extension), exif date time are checked. Exif
        date times should be within 30 seconds of each other, because
        need to allow for the fact that RAW and jpegs might not be
        written to the memory card(s) at the same time.

        :return: Returns SyncRawJpegStatus.error_already_downloaded
         and a sequence number if the name, extension, and exif values
         match (i.e. it has already been downloaded).

         Returns SyncRawJpegStatus.matching_pair and a sequence number
         if name and exif values match, but the extension is different
         (i.e. a matching RAW + JPG image).

         Returns SyncRawJpegStatus.error_datetime_mismatch and a
         sequence number of None if photos detected with the same
         filenames, but taken at different times.

         Returns SyncRawJpegStatus.no_match and a sequence number
         of None if no match
        """

        if name in self.photos:
            if datetime_roughly_equal(self.photos[name].date_time, date_time, 30):
                if extension in self.photos[name].extension:
                    return SyncRawJpegMatch(
                        SyncRawJpegStatus.error_already_downloaded,
                        self.photos[name].sequence_number_used,
                    )
                else:
                    return SyncRawJpegMatch(
                        SyncRawJpegStatus.matching_pair,
                        self.photos[name].sequence_number_used,
                    )
            else:
                return SyncRawJpegMatch(SyncRawJpegStatus.error_datetime_mismatch, None)
        return SyncRawJpegMatch(SyncRawJpegStatus.no_match, None)
Example #5
0
    def date_time(self,
                  missing: Optional[str] = '',
                  ignore_file_modify_date: bool = False) -> datetime.datetime:
        """
        Use pymediainfo (if present) to extract file encoding date.

        Also use ExifTool if appropriate.

        :param ignore_file_modify_date: if True, don't return the file
        modification date
        :return  python datetime format the date and time the video was
        recorded, else missing
        """

        if have_pymediainfo:
            try:
                d = self.media_info.to_data()['tracks'][0][
                    'encoded_date']  # type: str
            except KeyError:
                logging.debug(
                    'Failed to extract date time from %s using pymediainfo: trying ExifTool',
                    self.full_file_name)
                return super().date_time(
                    missing=missing,
                    ignore_file_modify_date=ignore_file_modify_date)
            else:
                # format of date string is something like:
                # UTC 2016-05-09 03:28:03
                try:
                    if d.startswith('UTC'):
                        u = d[4:]
                        a = arrow.get(u, "YYYY-MM-DD HH:mm:ss")  # type: Arrow
                        dt_mi = a.to('local')
                        dt = dt_mi.datetime  # type: datetime.datetime

                        # Compare the value returned by mediainfo against that
                        # returned by ExifTool, if and only if there is a time zone
                        # setting in the video file that ExifTool can extract
                        tz = self._get('TimeZone', None)
                        if tz is None:
                            logging.debug(
                                "Using pymediainfo datetime (%s), because ExifTool did "
                                "not detect a time zone in %s", dt_mi,
                                self.full_file_name)
                        if tz is not None:
                            dt_et = super().date_time(
                                missing=None, ignore_file_modify_date=True)
                            if dt_et is not None:
                                hour = tz // 60 * -1
                                minute = tz % 60 * -1
                                if arrow_shift_support:
                                    adjusted_dt_mi = dt_mi.shift(
                                        hours=hour, minutes=minute).naive
                                else:
                                    adjusted_dt_mi = dt_mi.replace(
                                        hours=hour, minutes=minute).naive
                                if datetime_roughly_equal(
                                        adjusted_dt_mi, dt_et):
                                    logging.debug(
                                        "Favoring ExifTool datetime metadata (%s) "
                                        "over mediainfo (%s) for %s, because it includes "
                                        "a timezone", dt_et, adjusted_dt_mi,
                                        self.full_file_name)
                                    dt = dt_et
                                else:
                                    logging.debug(
                                        "Although ExifTool located a time zone"
                                        "in %s's metadata, using the mediainfo result, "
                                        "because the two results are different. Mediainfo: %s / "
                                        "%s  (before / after). ExifTool: %s. Time zone: %s",
                                        self.full_file_name, dt,
                                        adjusted_dt_mi, dt_et, tz)

                    else:
                        dt = datetime.datetime.strptime(d, "%Y-%m-%d %H:%M:%S")
                except (ValueError, OverflowError):
                    logging.warning(
                        "Error parsing date time metadata %s for video %s. Will try ExifTool.",
                        d, self.full_file_name)
                    return super().date_time(missing)
                except arrow.arrow.parser.ParserError:
                    logging.warning(
                        "Error parsing date time metadata using Arrow %s for video %s. Will try "
                        "ExifTool.", d, self.full_file_name)
                    return super().date_time(missing)
                except Exception as e:
                    logging.error(
                        "Unknown error parsing date time metadata %s for video %s. %s. "
                        "Will try ExifTool.", d, self.full_file_name, e)
                    return super().date_time(missing)
                except:
                    logging.error(
                        "Unknown error parsing date time metadata %s for video %s. "
                        "Will try ExifTool.", d, self.full_file_name)
                    return super().date_time(missing)
                else:
                    return dt

        else:
            return super().date_time(missing)