Skip to content

Commit

Permalink
Support MOV file datetime extraction using hachoir
Browse files Browse the repository at this point in the history
  • Loading branch information
rmk-ch committed Nov 21, 2023
1 parent 7ac6944 commit a0b1773
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 28 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,4 @@ venv.bak/
# mypy
.mypy_cache/
*~$*
/Icon/raspberry_pi1600.png
/Icon/stock-vector-chicken-logo-design-573616531.jpg

*.orig
4 changes: 1 addition & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
# TODO
- Recursive rename
- Unit tests without real images
-
- Recursive rename
15 changes: 14 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ loguru = "0.5.3"
exif = "1.3.2"
plum-py = "0.7.9"
Pillow = "8.3.1"
hachoir = "^3.2.0"

[tool.poetry.dev-dependencies]

Expand Down
34 changes: 26 additions & 8 deletions src/autoImageRenamer/autoImageRenamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
from datetime import datetime
from loguru import logger
import exifread
import hachoir.parser
import hachoir.metadata
import re
import hashlib



def getFileHash(filename: str):
BLOCKSIZE = 65536
hasher = hashlib.md5()
Expand Down Expand Up @@ -84,7 +87,7 @@ def __init__(self, inputFolder, outputFolder, action, interactive):
oldFilePath = os.path.normpath(os.path.join(self.__inputFolder, fileName))

# try various options
times = self.getTimes(oldFilePath)
times = self.getTimes(oldFilePath, fileExt)
if len(times) < 1:
logger.warning(
f"Found no suitable time to rename for file {oldFilePath}. Skipping this file."
Expand Down Expand Up @@ -211,14 +214,17 @@ def act(old, new, methods):
for old, new in finalFilenames.items():
act(old, new, self.__fromMethods[old])

def getTimes(self, filename):
def getTimes(self, filename : str, fileExt : str):

times = dict()
try:
exifTimes = self.getExifTimes(filename)
times.update(exifTimes)
except:
pass
if fileExt.lower() == ".mov":
times["mov"] = self.getFileHachoir(filename)
else:
try:
exifTimes = self.getExifTimes(filename)
times.update(exifTimes)
except:
pass

try:
times["filename"] = self.getFilenameTime(filename)
Expand Down Expand Up @@ -283,7 +289,7 @@ def getExifTimes(self, filename):

return datetime_objs

def getFilenameTime(self, filename):
def getFilenameTime(self, filename : str) -> datetime:
## Extract date and time from file name and return datetime object

filenameWithoutPath = os.path.basename(filename)
Expand Down Expand Up @@ -348,6 +354,18 @@ def getFileCreated(self, filename):
datetime_obj = datetime.fromtimestamp(creationTimestamp)
logger.debug(f"File creation time {datetime_obj} found from {filename}")
return datetime_obj


def getFileHachoir(self, filename):
""" Get hachcoir metadata for MOV files """
try:
parser = hachoir.parser.createParser(filename)
metadata = hachoir.metadata.extractMetadata(parser)
datetime_obj = metadata.get('creation_date')
except Exception as e:
logger.debug(f"Parsing of file {filename} failed with hachoir.")
datetime_obj = None
return datetime_obj

def getFinalRenames(self):
return self.__finalRenames
6 changes: 6 additions & 0 deletions tests/test_ArtificialDatasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,11 @@ def test_emptyFolder_noException(self):
)


@unittest.skip("Found no way to create an artificial MOV file similar to the one created by new IPHONES.... Tested with private dataset")
def test_mov(self):
assert(False)



if __name__ == "__main__":
unittest.main()
28 changes: 15 additions & 13 deletions tests/test_privateDataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import sys
import os

sys.path.append(os.path.abspath("./src"))
from autoImageRenamer import autoImageRenamer


Expand All @@ -21,19 +20,22 @@ def test_predefinedDataset(self):

# Set up expected
mapping = dict()
mapping["2021-01-12.jpg"] = "2021-01-12.jpg" # Date only, no EXIF
mapping["20210605_153227.jpg"] = "2021-06-05_15-32-27.jpg" # Samsung A51
mapping["DSC07863.ARW"] = "2021-06-21_10-46-36.arw" # Sony RX100
mapping["IMG_20210605_152957.jpeg"] = "2021-06-05_15-29-57_001.jpeg" # Signal
# mapping["2021-01-12.jpg"] = "2021-01-12.jpg" # Date only, no EXIF
# mapping["20210605_153227.jpg"] = "2021-06-05_15-32-27.jpg" # Samsung A51
# mapping["DSC07863.ARW"] = "2021-06-21_10-46-36.arw" # Sony RX100
# mapping["IMG_20210605_152957.jpeg"] = "2021-06-05_15-29-57_001.jpeg" # Signal
# mapping[
# "IMG_20210605_152957-0033.jpeg"
# ] = "2021-06-05_15-29-57_002.jpeg" # Copy of previous
# mapping[
# "Photo-2021-06-21-10-17-40_8255.JPG"
# ] = "2021-06-21_10-17-40.jpg" # iPhone
# mapping[
# "Video-2021-06-22-10-12-58_8280.MOV"
# ] = "2021-06-22_10-12-58.mov" # iPhone Video
mapping[
"IMG_20210605_152957-0033.jpeg"
] = "2021-06-05_15-29-57_002.jpeg" # Copy of previous
mapping[
"Photo-2021-06-21-10-17-40_8255.JPG"
] = "2021-06-21_10-17-40.jpg" # iPhone
mapping[
"Video-2021-06-22-10-12-58_8280.MOV"
] = "2021-06-22_10-12-58.mov" # iPhone Video
"IMG_4827.MOV"
] = "2022-06-14_10-23-35.mov"

sourcePath_norm = os.path.normpath(source)
targetPath_norm = os.path.normpath(target)
Expand Down

0 comments on commit a0b1773

Please sign in to comment.