diff --git a/__init__.py b/__init__.py
index e3d9466..1e832e6 100644
--- a/__init__.py
+++ b/__init__.py
@@ -15,9 +15,7 @@ media (Media Tools)
**Submodules:**
-* :mod:`mmod.module.sub1`
-* :class:`mmod.module.sub2`
-* :func:`mmod.module.sub2`
+* :func:`media.get_media_data`
**Unittest:**
@@ -27,214 +25,26 @@ __DEPENDENCIES__ = []
import logging
-
-logger_name = 'FSTOOLS'
+logger_name = 'MEDIA'
logger = logging.getLogger(logger_name)
__DESCRIPTION__ = """The Module {\\tt %s} is designed to help on all issues with media files, like tags (e.g. exif, id3) and transformations.
For more Information read the documentation.""" % __name__.replace('_', '\_')
"""The Module Description"""
-__INTERPRETER__ = (2, 3)
+__INTERPRETER__ = (3, )
"""The Tested Interpreter-Versions"""
-def uid(pathname, max_staleness=3600):
- """
- Function returning a unique id for a given file or path.
-
- :param str pathname: File or Path name for generation of the uid.
- :param int max_staleness: If a file or path is older than that, we may consider
- it stale and return a different uid - this is a
- dirty trick to work around changes never being
- detected. Default is 3600 seconds, use None to
- disable this trickery. See below for more details.
- :returns: An object that changes value if the file changed,
- None is returned if there were problems accessing the file
- :rtype: str
-
- .. note:: Depending on the operating system capabilities and the way the
- file update is done, this function might return the same value
- even if the file has changed. It should be better than just
- using file's mtime though.
- max_staleness tries to avoid the worst for these cases.
-
- .. note:: If this function is used for a path, it will stat all pathes and files rekursively.
-
- Using just the file's mtime to determine if the file has changed is
- not reliable - if file updates happen faster than the file system's
- mtime granularity, then the modification is not detectable because
- the mtime is still the same.
-
- This function tries to improve by using not only the mtime, but also
- other metadata values like file size and inode to improve reliability.
-
- For the calculation of this value, we of course only want to use data
- that we can get rather fast, thus we use file metadata, not file data
- (file content).
-
- >>> print 'UID:', uid(__file__)
- UID: 16a65cc78e1344e596ef1c9536dab2193a402934
- """
- if os.path.isdir(pathname):
- pathlist = dirlist(pathname) + filelist(pathname)
- pathlist.sort()
- else:
- pathlist = [pathname]
- uid = []
- for element in pathlist:
- try:
- st = os.stat(element)
- except (IOError, OSError):
- uid.append(None) # for permanent errors on stat() this does not change, but
- # having a changing value would be pointless because if we
- # can't even stat the file, it is unlikely we can read it.
- else:
- fake_mtime = int(st.st_mtime)
- if not st.st_ino and max_staleness:
- # st_ino being 0 likely means that we run on a platform not
- # supporting it (e.g. win32) - thus we likely need this dirty
- # trick
- now = int(time.time())
- if now >= st.st_mtime + max_staleness:
- # keep same fake_mtime for each max_staleness interval
- fake_mtime = int(now / max_staleness) * max_staleness
- uid.append((
- st.st_mtime, # might have a rather rough granularity, e.g. 2s
- # on FAT, 1s on ext3 and might not change on fast
- # updates
- st.st_ino, # inode number (will change if the update is done
- # by e.g. renaming a temp file to the real file).
- # not supported on win32 (0 ever)
- st.st_size, # likely to change on many updates, but not
- # sufficient alone
- fake_mtime) # trick to workaround file system / platform
- # limitations causing permanent trouble
- )
- if sys.version_info < (3, 0):
- secret = ''
- return hmac.new(secret, repr(uid), hashlib.sha1).hexdigest()
- else:
- secret = b''
- return hmac.new(secret, bytes(repr(uid), 'latin-1'), hashlib.sha1).hexdigest()
-
-
-def uid_filelist(path='.', expression='*', rekursive=True):
- SHAhash = hashlib.md5()
+def get_media_data(full_path):
+ from media import metadata
+ ft = metadata.get_filetype(full_path)
#
- fl = filelist(path, expression, rekursive)
- fl.sort()
- for f in fl:
- if sys.version_info < (3, 0):
- with open(f, 'rb') as fh:
- SHAhash.update(hashlib.md5(fh.read()).hexdigest())
- else:
- with open(f, mode='rb') as fh:
- d = hashlib.md5()
- for buf in iter(partial(fh.read, 128), b''):
- d.update(buf)
- SHAhash.update(d.hexdigest().encode())
- #
- return SHAhash.hexdigest()
-
-
-def filelist(path='.', expression='*', rekursive=True):
- """
- Function returning a list of files below a given path.
-
- :param str path: folder which is the basepath for searching files.
- :param str expression: expression to fit including shell-style wildcards.
- :param bool rekursive: search all subfolders if True.
- :returns: list of filenames including the pathe
- :rtype: list
-
- .. note:: The returned filenames could be relative pathes depending on argument path.
-
- >>> for filename in filelist(path='.', expression='*.py*', rekursive=True):
- ... print filename
- ./__init__.py
- ./__init__.pyc
- """
- li = list()
- if os.path.exists(path):
- logger.debug('FILELIST: path (%s) exists - looking for files to append', path)
- for filename in glob.glob(os.path.join(path, expression)):
- if os.path.isfile(filename):
- li.append(filename)
- for directory in os.listdir(path):
- directory = os.path.join(path, directory)
- if os.path.isdir(directory) and rekursive and not os.path.islink(directory):
- li.extend(filelist(directory, expression))
+ if ft == metadata.FILETYPE_AUDIO:
+ return metadata.get_audio_data(full_path)
+ elif ft == metadata.FILETYPE_IMAGE:
+ return metadata.get_image_data(full_path)
+ elif ft == metadata.FILETYPE_VIDEO:
+ return metadata.get_video_data(full_path)
else:
- logger.warning('FILELIST: path (%s) does not exist - empty filelist will be returned', path)
- return li
-
-
-def dirlist(path='.', rekursive=True):
- """
- Function returning a list of directories below a given path.
-
- :param str path: folder which is the basepath for searching files.
- :param bool rekursive: search all subfolders if True.
- :returns: list of filenames including the pathe
- :rtype: list
-
- .. note:: The returned filenames could be relative pathes depending on argument path.
-
- >>> for dirname in dirlist(path='..', rekursive=True):
- ... print dirname
- ../caching
- ../fstools
- """
- li = list()
- if os.path.exists(path):
- logger.debug('DIRLIST: path (%s) exists - looking for directories to append', path)
- for dirname in os.listdir(path):
- fulldir = os.path.join(path, dirname)
- if os.path.isdir(fulldir):
- li.append(fulldir)
- if rekursive:
- li.extend(dirlist(fulldir))
- else:
- logger.warning('DIRLIST: path (%s) does not exist - empty filelist will be returned', path)
- return li
-
-
-def is_writeable(path):
- """.. warning:: Needs to be documented
- """
- if os.access(path, os.W_OK):
- # path is writable whatever it is, file or directory
- return True
- else:
- # path is not writable whatever it is, file or directory
- return False
-
-
-def mkdir(path):
- """.. warning:: Needs to be documented
- """
- path = os.path.abspath(path)
- if not os.path.exists(os.path.dirname(path)):
- mkdir(os.path.dirname(path))
- if not os.path.exists(path):
- os.mkdir(path)
- return os.path.isdir(path)
-
-
-def open_locked_non_blocking(*args, **kwargs):
- """.. warning:: Needs to be documented (acquire exclusive lock file access). Throws an exception, if file is locked!
- """
- import fcntl
- locked_file_descriptor = open(*args, **kwargs)
- fcntl.lockf(locked_file_descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
- return locked_file_descriptor
-
-
-def open_locked_blocking(*args, **kwargs):
- """.. warning:: Needs to be documented (acquire exclusive lock file access). Blocks until file is free. deadlock!
- """
- import fcntl
- locked_file_descriptor = open(*args, **kwargs)
- fcntl.lockf(locked_file_descriptor, fcntl.LOCK_EX)
- return locked_file_descriptor
+ logger.warning('Filetype not known: %s', full_path)
diff --git a/_testresults_/coverage.xml b/_testresults_/coverage.xml
new file mode 100644
index 0000000..afb7c22
--- /dev/null
+++ b/_testresults_/coverage.xml
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_testresults_/unittest.json b/_testresults_/unittest.json
new file mode 100644
index 0000000..78cfc24
--- /dev/null
+++ b/_testresults_/unittest.json
@@ -0,0 +1,1649 @@
+{
+ "coverage_information": [
+ {
+ "branch_coverage": 97.5,
+ "filepath": "/user_data/data/dirk/prj/unittest/media/pylibs/media",
+ "files": [
+ {
+ "branch_coverage": 97.5,
+ "filepath": "/user_data/data/dirk/prj/unittest/media/pylibs/media/__init__.py",
+ "fragments": [
+ {
+ "coverage_state": "clean",
+ "end": 3,
+ "start": 1
+ },
+ {
+ "coverage_state": "covered",
+ "end": 4,
+ "start": 4
+ },
+ {
+ "coverage_state": "clean",
+ "end": 23,
+ "start": 5
+ },
+ {
+ "coverage_state": "covered",
+ "end": 24,
+ "start": 24
+ },
+ {
+ "coverage_state": "clean",
+ "end": 25,
+ "start": 25
+ },
+ {
+ "coverage_state": "covered",
+ "end": 26,
+ "start": 26
+ },
+ {
+ "coverage_state": "clean",
+ "end": 27,
+ "start": 27
+ },
+ {
+ "coverage_state": "covered",
+ "end": 29,
+ "start": 28
+ },
+ {
+ "coverage_state": "clean",
+ "end": 31,
+ "start": 30
+ },
+ {
+ "coverage_state": "covered",
+ "end": 32,
+ "start": 32
+ },
+ {
+ "coverage_state": "clean",
+ "end": 34,
+ "start": 33
+ },
+ {
+ "coverage_state": "covered",
+ "end": 35,
+ "start": 35
+ },
+ {
+ "coverage_state": "clean",
+ "end": 38,
+ "start": 36
+ },
+ {
+ "coverage_state": "covered",
+ "end": 41,
+ "start": 39
+ },
+ {
+ "coverage_state": "clean",
+ "end": 42,
+ "start": 42
+ },
+ {
+ "coverage_state": "covered",
+ "end": 48,
+ "start": 43
+ },
+ {
+ "coverage_state": "clean",
+ "end": 49,
+ "start": 49
+ },
+ {
+ "coverage_state": "covered",
+ "end": 50,
+ "start": 50
+ },
+ {
+ "coverage_state": "clean",
+ "end": null,
+ "start": 51
+ }
+ ],
+ "line_coverage": 100.0,
+ "name": "media.__init__.py"
+ },
+ {
+ "branch_coverage": 97.5,
+ "filepath": "/user_data/data/dirk/prj/unittest/media/pylibs/media/metadata.py",
+ "fragments": [
+ {
+ "coverage_state": "covered",
+ "end": 5,
+ "start": 1
+ },
+ {
+ "coverage_state": "clean",
+ "end": 7,
+ "start": 6
+ },
+ {
+ "coverage_state": "covered",
+ "end": 9,
+ "start": 8
+ },
+ {
+ "coverage_state": "clean",
+ "end": 10,
+ "start": 10
+ },
+ {
+ "coverage_state": "covered",
+ "end": 13,
+ "start": 11
+ },
+ {
+ "coverage_state": "clean",
+ "end": 14,
+ "start": 14
+ },
+ {
+ "coverage_state": "covered",
+ "end": 17,
+ "start": 15
+ },
+ {
+ "coverage_state": "clean",
+ "end": 18,
+ "start": 18
+ },
+ {
+ "coverage_state": "covered",
+ "end": 41,
+ "start": 19
+ },
+ {
+ "coverage_state": "clean",
+ "end": 42,
+ "start": 42
+ },
+ {
+ "coverage_state": "covered",
+ "end": 44,
+ "start": 43
+ },
+ {
+ "coverage_state": "clean",
+ "end": 46,
+ "start": 45
+ },
+ {
+ "coverage_state": "covered",
+ "end": 54,
+ "start": 47
+ },
+ {
+ "coverage_state": "clean",
+ "end": 56,
+ "start": 55
+ },
+ {
+ "coverage_state": "covered",
+ "end": 73,
+ "start": 57
+ },
+ {
+ "coverage_state": "clean",
+ "end": 75,
+ "start": 74
+ },
+ {
+ "coverage_state": "covered",
+ "end": 85,
+ "start": 76
+ },
+ {
+ "coverage_state": "clean",
+ "end": 87,
+ "start": 86
+ },
+ {
+ "coverage_state": "covered",
+ "end": 89,
+ "start": 88
+ },
+ {
+ "coverage_state": "clean",
+ "end": 91,
+ "start": 90
+ },
+ {
+ "coverage_state": "covered",
+ "end": 93,
+ "start": 92
+ },
+ {
+ "coverage_state": "clean",
+ "end": 94,
+ "start": 94
+ },
+ {
+ "coverage_state": "covered",
+ "end": 98,
+ "start": 95
+ },
+ {
+ "coverage_state": "clean",
+ "end": 99,
+ "start": 99
+ },
+ {
+ "coverage_state": "covered",
+ "end": 101,
+ "start": 100
+ },
+ {
+ "coverage_state": "clean",
+ "end": 102,
+ "start": 102
+ },
+ {
+ "coverage_state": "covered",
+ "end": 104,
+ "start": 103
+ },
+ {
+ "coverage_state": "clean",
+ "end": 105,
+ "start": 105
+ },
+ {
+ "coverage_state": "covered",
+ "end": 107,
+ "start": 106
+ },
+ {
+ "coverage_state": "clean",
+ "end": 108,
+ "start": 108
+ },
+ {
+ "coverage_state": "covered",
+ "end": 111,
+ "start": 109
+ },
+ {
+ "coverage_state": "clean",
+ "end": 113,
+ "start": 112
+ },
+ {
+ "coverage_state": "covered",
+ "end": 116,
+ "start": 114
+ },
+ {
+ "coverage_state": "clean",
+ "end": 117,
+ "start": 117
+ },
+ {
+ "coverage_state": "covered",
+ "end": 119,
+ "start": 118
+ },
+ {
+ "coverage_state": "clean",
+ "end": 120,
+ "start": 120
+ },
+ {
+ "coverage_state": "covered",
+ "end": 125,
+ "start": 121
+ },
+ {
+ "coverage_state": "uncovered",
+ "end": 128,
+ "start": 126
+ },
+ {
+ "coverage_state": "clean",
+ "end": 129,
+ "start": 129
+ },
+ {
+ "coverage_state": "covered",
+ "end": 135,
+ "start": 130
+ },
+ {
+ "coverage_state": "clean",
+ "end": 136,
+ "start": 136
+ },
+ {
+ "coverage_state": "covered",
+ "end": 143,
+ "start": 137
+ },
+ {
+ "coverage_state": "clean",
+ "end": 145,
+ "start": 144
+ },
+ {
+ "coverage_state": "covered",
+ "end": 152,
+ "start": 146
+ },
+ {
+ "coverage_state": "clean",
+ "end": 153,
+ "start": 153
+ },
+ {
+ "coverage_state": "covered",
+ "end": 154,
+ "start": 154
+ },
+ {
+ "coverage_state": "clean",
+ "end": 155,
+ "start": 155
+ },
+ {
+ "coverage_state": "covered",
+ "end": 175,
+ "start": 156
+ },
+ {
+ "coverage_state": "clean",
+ "end": 178,
+ "start": 176
+ },
+ {
+ "coverage_state": "covered",
+ "end": 181,
+ "start": 179
+ },
+ {
+ "coverage_state": "clean",
+ "end": 183,
+ "start": 182
+ },
+ {
+ "coverage_state": "covered",
+ "end": 193,
+ "start": 184
+ },
+ {
+ "coverage_state": "clean",
+ "end": 195,
+ "start": 194
+ },
+ {
+ "coverage_state": "covered",
+ "end": 197,
+ "start": 196
+ },
+ {
+ "coverage_state": "clean",
+ "end": 209,
+ "start": 198
+ },
+ {
+ "coverage_state": "covered",
+ "end": 211,
+ "start": 210
+ },
+ {
+ "coverage_state": "clean",
+ "end": 227,
+ "start": 212
+ },
+ {
+ "coverage_state": "covered",
+ "end": 236,
+ "start": 228
+ },
+ {
+ "coverage_state": "clean",
+ "end": 238,
+ "start": 237
+ },
+ {
+ "coverage_state": "covered",
+ "end": 241,
+ "start": 239
+ },
+ {
+ "coverage_state": "clean",
+ "end": 243,
+ "start": 242
+ },
+ {
+ "coverage_state": "covered",
+ "end": 254,
+ "start": 244
+ },
+ {
+ "coverage_state": "partially-covered",
+ "end": 255,
+ "start": 255
+ },
+ {
+ "coverage_state": "covered",
+ "end": 258,
+ "start": 256
+ },
+ {
+ "coverage_state": "clean",
+ "end": 260,
+ "start": 259
+ },
+ {
+ "coverage_state": "covered",
+ "end": 264,
+ "start": 261
+ },
+ {
+ "coverage_state": "clean",
+ "end": null,
+ "start": 265
+ }
+ ],
+ "line_coverage": 98.44000000000001,
+ "name": "media.metadata.py"
+ }
+ ],
+ "line_coverage": 98.56,
+ "name": "media"
+ }
+ ],
+ "lost_souls": {
+ "item_list": [],
+ "testcase_list": []
+ },
+ "specification": {
+ "comment": "Comment",
+ "item_dict": {
+ "_MR7eOHYYEem_kd-7nxt1sg": {
+ "Heading": "Metadata",
+ "last_change": "2020-01-30T20:32:07.705+01:00",
+ "system_type_uid": "_4-K5EHYYEem_kd-7nxt1sg",
+ "system_uid": "_MR7eOHYYEem_kd-7nxt1sg",
+ "xml_tag": "{http://www.omg.org/spec/ReqIF/20110401/reqif.xsd}SPEC-OBJECT"
+ },
+ "_XzMFcHYZEem_kd-7nxt1sg": {
+ "Description": "A Method shall return the metadata for a given media filename.",
+ "Fitcriterion": "",
+ "Heading": "Method to get Metadata",
+ "ID": "REQ-1",
+ "ReasonForImplementation": "",
+ "last_change": "2020-01-30T20:33:27.982+01:00",
+ "system_type_uid": "_MR7eNHYYEem_kd-7nxt1sg",
+ "system_uid": "_XzMFcHYZEem_kd-7nxt1sg",
+ "xml_tag": "{http://www.omg.org/spec/ReqIF/20110401/reqif.xsd}SPEC-OBJECT"
+ }
+ },
+ "titel": "Title",
+ "uid_list_sorted": [
+ "_MR7eOHYYEem_kd-7nxt1sg",
+ "_XzMFcHYZEem_kd-7nxt1sg"
+ ]
+ },
+ "system_information": {
+ "Architecture": "64bit",
+ "Distribution": "LinuxMint 19.3 tricia",
+ "Hostname": "ahorn",
+ "Kernel": "5.3.0-28-generic (#30~18.04.1-Ubuntu SMP Fri Jan 17 06:14:09 UTC 2020)",
+ "Machine": "x86_64",
+ "Path": "/user_data/data/dirk/prj/unittest/media/unittest",
+ "System": "Linux",
+ "Username": "dirk"
+ },
+ "testobject_information": {
+ "Dependencies": [],
+ "Description": "The Module {\\tt media} is designed to help on all issues with media files, like tags (e.g. exif, id3) and transformations.\nFor more Information read the documentation.",
+ "Name": "media",
+ "State": "Released",
+ "Supported Interpreters": "python3",
+ "Version": "f44d258110e088423a3132bd52534f53"
+ },
+ "testrun_list": [
+ {
+ "heading_dict": {
+ "_MR7eOHYYEem_kd-7nxt1sg": "Metadata",
+ "_XzMFcHYZEem_kd-7nxt1sg": "Method to get Metadata"
+ },
+ "interpreter": "python 3.6.9 (final)",
+ "name": "Default Testsession name",
+ "number_of_failed_tests": 0,
+ "number_of_possibly_failed_tests": 0,
+ "number_of_successfull_tests": 1,
+ "number_of_tests": 1,
+ "testcase_execution_level": 90,
+ "testcase_names": {
+ "0": "Single Test",
+ "10": "Smoke Test (Minumum subset)",
+ "50": "Short Test (Subset)",
+ "90": "Full Test (all defined tests)"
+ },
+ "testcases": {
+ "_XzMFcHYZEem_kd-7nxt1sg": {
+ "args": null,
+ "asctime": "2020-01-30 22:06:34,253",
+ "created": 1580418394.2534032,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "__init__.py",
+ "funcName": "testrun",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 20,
+ "message": "_XzMFcHYZEem_kd-7nxt1sg",
+ "module": "__init__",
+ "moduleLogger": [],
+ "msecs": 253.4031867980957,
+ "msg": "_XzMFcHYZEem_kd-7nxt1sg",
+ "name": "__tLogger__",
+ "pathname": "/user_data/data/dirk/prj/unittest/media/unittest/src/tests/__init__.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 58.33578109741211,
+ "stack_info": null,
+ "testcaseLogger": [
+ {
+ "args": [
+ "None",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,253",
+ "created": 1580418394.2539485,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for unknown.txt is correct (Content None and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "/user_data/data/dirk/prj/unittest/media/unittest/input_data/unknown.txt"
+ ],
+ "asctime": "2020-01-30 22:06:34,253",
+ "created": 1580418394.2535973,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "__init__.py",
+ "funcName": "get_media_data",
+ "levelname": "WARNING",
+ "levelno": 30,
+ "lineno": 50,
+ "message": "Filetype not known: /user_data/data/dirk/prj/unittest/media/unittest/input_data/unknown.txt",
+ "module": "__init__",
+ "msecs": 253.59725952148438,
+ "msg": "Filetype not known: %s",
+ "name": "MEDIA",
+ "pathname": "src/media/__init__.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 58.52985382080078,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for unknown.txt",
+ "None",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,253",
+ "created": 1580418394.2538524,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for unknown.txt): None ()",
+ "module": "test",
+ "msecs": 253.85236740112305,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 58.78496170043945,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for unknown.txt",
+ "None",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,253",
+ "created": 1580418394.2539005,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for unknown.txt): result = None ()",
+ "module": "test",
+ "msecs": 253.90052795410156,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 58.83312225341797,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 253.94845008850098,
+ "msg": "Media data for unknown.txt is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 58.88104438781738,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 4.792213439941406e-05
+ },
+ {
+ "args": [
+ "{'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'size': 8580366, 'time': 1451606398, 'tm_is_subst': True}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,316",
+ "created": 1580418394.3162963,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for audio.mp3 is correct (Content {'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'size': 8580366, 'time': 1451606398, 'tm_is_subst': True} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for audio.mp3",
+ "{ 'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'size': 8580366, 'time': 1451606398, 'tm_is_subst': True }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,315",
+ "created": 1580418394.3159626,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for audio.mp3): { 'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'size': 8580366, 'time': 1451606398, 'tm_is_subst': True } ()",
+ "module": "test",
+ "msecs": 315.962553024292,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 120.8951473236084,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for audio.mp3",
+ "{ 'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'time': 1451606398, 'tm_is_subst': True, 'size': 8580366 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,316",
+ "created": 1580418394.316178,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for audio.mp3): result = { 'duration': 236.094694, 'bitrate': 290743, 'artist': 'Kaleo', 'title': 'No Good', 'album': 'A/B', 'track': 1, 'genre': 'Rock', 'year': 2016, 'time': 1451606398, 'tm_is_subst': True, 'size': 8580366 } ()",
+ "module": "test",
+ "msecs": 316.1780834197998,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 121.11067771911621,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 316.2963390350342,
+ "msg": "Media data for audio.mp3 is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 121.22893333435059,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.000118255615234375
+ },
+ {
+ "args": [
+ "{'duration': 281.991837, 'bitrate': 228298, 'title': 'Video Games (Album Version Remastered)', 'artist': 'Lana Del Rey', 'album': 'Born To Die', 'genre': 'Pop', 'track': 4, 'year': 2012, 'size': 8047290, 'time': 1325375995, 'tm_is_subst': True}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,377",
+ "created": 1580418394.3774264,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for audio_fail_conv.mp3 is correct (Content {'duration': 281.991837, 'bitrate': 228298, 'title': 'Video Games (Album Version Remastered)', 'artist': 'Lana Del Rey', 'album': 'Born To Die', 'genre': 'Pop', 'track': 4, 'year': 2012, 'size': 8047290, 'time': 1325375995, 'tm_is_subst': True} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "'N/A'",
+ "bitrate",
+ "bitrate"
+ ],
+ "asctime": "2020-01-30 22:06:34,376",
+ "created": 1580418394.3767884,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "metadata.py",
+ "funcName": "__get_xxprobe_data__",
+ "levelname": "WARNING",
+ "levelno": 30,
+ "lineno": 142,
+ "message": "Can't convert 'N/A' (bitrate) for bitrate",
+ "module": "metadata",
+ "msecs": 376.7883777618408,
+ "msg": "Can't convert %s (%s) for %s",
+ "name": "MEDIA",
+ "pathname": "src/media/metadata.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 181.72097206115723,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for audio_fail_conv.mp3",
+ "{ 'duration': 281.991837, 'bitrate': 228298, 'title': 'Video Games (Album Version Remastered)', 'artist': 'Lana Del Rey', 'album': 'Born To Die', 'genre': 'Pop', 'track': 4, 'year': 2012, 'size': 8047290, 'time': 1325375995, 'tm_is_subst': True }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,377",
+ "created": 1580418394.3772197,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for audio_fail_conv.mp3): { 'duration': 281.991837, 'bitrate': 228298, 'title': 'Video Games (Album Version Remastered)', 'artist': 'Lana Del Rey', 'album': 'Born To Die', 'genre': 'Pop', 'track': 4, 'year': 2012, 'size': 8047290, 'time': 1325375995, 'tm_is_subst': True } ()",
+ "module": "test",
+ "msecs": 377.21967697143555,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 182.15227127075195,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for audio_fail_conv.mp3",
+ "{ 'duration': 281.991837, 'bitrate': 228298, 'artist': 'Lana Del Rey', 'title': 'Video Games (Album Version Remastered)', 'album': 'Born To Die', 'track': 4, 'genre': 'Pop', 'year': 2012, 'time': 1325375995, 'tm_is_subst': True, 'size': 8047290 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,377",
+ "created": 1580418394.377325,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for audio_fail_conv.mp3): result = { 'duration': 281.991837, 'bitrate': 228298, 'artist': 'Lana Del Rey', 'title': 'Video Games (Album Version Remastered)', 'album': 'Born To Die', 'track': 4, 'genre': 'Pop', 'year': 2012, 'time': 1325375995, 'tm_is_subst': True, 'size': 8047290 } ()",
+ "module": "test",
+ "msecs": 377.32505798339844,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 182.25765228271484,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 377.4263858795166,
+ "msg": "Media data for audio_fail_conv.mp3 is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 182.358980178833,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.00010132789611816406
+ },
+ {
+ "args": [
+ "{'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': {'lon': 12.140646934444444, 'lat': 53.68635940527778}, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'size': 4342955, 'camera': 'HUAWEI: EVA-L09'}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,394",
+ "created": 1580418394.3949149,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for image_exif_gps.jpg is correct (Content {'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': {'lon': 12.140646934444444, 'lat': 53.68635940527778}, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'size': 4342955, 'camera': 'HUAWEI: EVA-L09'} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for image_exif_gps.jpg",
+ "{ 'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': { 'lon': 12.140646934444444, 'lat': 53.68635940527778 }, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'size': 4342955, 'camera': 'HUAWEI: EVA-L09' }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,394",
+ "created": 1580418394.3947024,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for image_exif_gps.jpg): { 'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': { 'lon': 12.140646934444444, 'lat': 53.68635940527778 }, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'size': 4342955, 'camera': 'HUAWEI: EVA-L09' } ()",
+ "module": "test",
+ "msecs": 394.7024345397949,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 199.63502883911133,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_exif_gps.jpg",
+ "{ 'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': { 'lon': 12.140646934444444, 'lat': 53.68635940527778 }, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'camera': 'HUAWEI: EVA-L09', 'size': 4342955 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,394",
+ "created": 1580418394.3948064,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for image_exif_gps.jpg): result = { 'time': 1518783213, 'exposure_program': 'Program Normal', 'exposure_time': 0.000535, 'flash': 'Auto Off', 'aperture': 2.2, 'focal_length': 4.5, 'gps': { 'lon': 12.140646934444444, 'lat': 53.68635940527778 }, 'height': 2240, 'iso': 50, 'orientation': 0, 'width': 3968, 'camera': 'HUAWEI: EVA-L09', 'size': 4342955 } ()",
+ "module": "test",
+ "msecs": 394.8063850402832,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 199.7389793395996,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 394.9148654937744,
+ "msg": "Media data for image_exif_gps.jpg is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 199.84745979309082,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.00010848045349121094
+ },
+ {
+ "args": [
+ "{'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'size': 2837285, 'camera': 'HUAWEI: EVA-L09'}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,398",
+ "created": 1580418394.3987355,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for image_exif_no_gps.jpg is correct (Content {'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'size': 2837285, 'camera': 'HUAWEI: EVA-L09'} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for image_exif_no_gps.jpg",
+ "{ 'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'size': 2837285, 'camera': 'HUAWEI: EVA-L09' }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,398",
+ "created": 1580418394.3985393,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for image_exif_no_gps.jpg): { 'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'size': 2837285, 'camera': 'HUAWEI: EVA-L09' } ()",
+ "module": "test",
+ "msecs": 398.53930473327637,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 203.47189903259277,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_exif_no_gps.jpg",
+ "{ 'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'camera': 'HUAWEI: EVA-L09', 'size': 2837285 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,398",
+ "created": 1580418394.3986442,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for image_exif_no_gps.jpg): result = { 'time': 1515143529, 'exposure_program': 'Program Normal', 'exposure_time': 0.03, 'flash': 'Fired', 'aperture': 2.2, 'focal_length': 4.5, 'height': 3968, 'iso': 160, 'orientation': 0, 'width': 2976, 'camera': 'HUAWEI: EVA-L09', 'size': 2837285 } ()",
+ "module": "test",
+ "msecs": 398.64420890808105,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 203.57680320739746,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 398.73552322387695,
+ "msg": "Media data for image_exif_no_gps.jpg is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 203.66811752319336,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 9.131431579589844e-05
+ },
+ {
+ "args": [
+ "{'size': 1139092, 'time': 1449870515, 'tm_is_subst': True}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,399",
+ "created": 1580418394.399145,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for image_non_exif.jpg is correct (Content {'size': 1139092, 'time': 1449870515, 'tm_is_subst': True} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "/user_data/data/dirk/prj/unittest/media/unittest/input_data/image_non_exif.jpg"
+ ],
+ "asctime": "2020-01-30 22:06:34,398",
+ "created": 1580418394.3989413,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "metadata.py",
+ "funcName": "__get_exif_data__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 152,
+ "message": "/user_data/data/dirk/prj/unittest/media/unittest/input_data/image_non_exif.jpg does not have any exif information",
+ "module": "metadata",
+ "msecs": 398.9412784576416,
+ "msg": "%s does not have any exif information",
+ "name": "MEDIA",
+ "pathname": "src/media/metadata.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 203.873872756958,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_non_exif.jpg",
+ "{ 'size': 1139092, 'time': 1449870515, 'tm_is_subst': True }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,399",
+ "created": 1580418394.3990424,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for image_non_exif.jpg): { 'size': 1139092, 'time': 1449870515, 'tm_is_subst': True } ()",
+ "module": "test",
+ "msecs": 399.04236793518066,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 203.97496223449707,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_non_exif.jpg",
+ "{ 'time': 1449870515, 'tm_is_subst': True, 'size': 1139092 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,399",
+ "created": 1580418394.399093,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for image_non_exif.jpg): result = { 'time': 1449870515, 'tm_is_subst': True, 'size': 1139092 } ()",
+ "module": "test",
+ "msecs": 399.0929126739502,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 204.0255069732666,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 399.14488792419434,
+ "msg": "Media data for image_non_exif.jpg is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 204.07748222351074,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 5.1975250244140625e-05
+ },
+ {
+ "args": [
+ "{'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'size': 1301272, 'camera': 'Canon: Canon EOS 40D'}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,403",
+ "created": 1580418394.4032836,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for image_extraction_failed.jpg is correct (Content {'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'size': 1301272, 'camera': 'Canon: Canon EOS 40D'} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "{0: b'\\x02\\x02\\x00\\x00'}"
+ ],
+ "asctime": "2020-01-30 22:06:34,403",
+ "created": 1580418394.403025,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "metadata.py",
+ "funcName": "__gps_conv__",
+ "levelname": "WARNING",
+ "levelno": 30,
+ "lineno": 258,
+ "message": "GPS data extraction failed for {0: b'\\x02\\x02\\x00\\x00'}",
+ "module": "metadata",
+ "msecs": 403.02491188049316,
+ "msg": "GPS data extraction failed for %s",
+ "name": "MEDIA",
+ "pathname": "src/media/metadata.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 207.95750617980957,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_extraction_failed.jpg",
+ "{ 'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'size': 1301272, 'camera': 'Canon: Canon EOS 40D' }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,403",
+ "created": 1580418394.403149,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for image_extraction_failed.jpg): { 'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'size': 1301272, 'camera': 'Canon: Canon EOS 40D' } ()",
+ "module": "test",
+ "msecs": 403.148889541626,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 208.08148384094238,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for image_extraction_failed.jpg",
+ "{ 'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'camera': 'Canon: Canon EOS 40D', 'size': 1301272 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,403",
+ "created": 1580418394.4032133,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for image_extraction_failed.jpg): result = { 'time': 1226149915, 'exposure_program': 'Program Normal', 'exposure_time': 0.008, 'flash': 'Fill Fired', 'aperture': 7.1, 'focal_length': 170.0, 'height': 2592, 'iso': 400, 'orientation': 1, 'width': 3888, 'camera': 'Canon: Canon EOS 40D', 'size': 1301272 } ()",
+ "module": "test",
+ "msecs": 403.2132625579834,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 208.1458568572998,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 403.28359603881836,
+ "msg": "Media data for image_extraction_failed.jpg is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 208.21619033813477,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 7.033348083496094e-05
+ },
+ {
+ "args": [
+ "{'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,458",
+ "created": 1580418394.4580746,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for video.3gp is correct (Content {'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for video.3gp",
+ "{ 'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,457",
+ "created": 1580418394.457794,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for video.3gp): { 'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345 } ()",
+ "module": "test",
+ "msecs": 457.7939510345459,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 262.7265453338623,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for video.3gp",
+ "{ 'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,457",
+ "created": 1580418394.457974,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for video.3gp): result = { 'width': 800, 'height': 480, 'ratio': 1.6666666666666667, 'duration': 3.964, 'bitrate': 2341765, 'time': 1414948303, 'size': 1160345 } ()",
+ "module": "test",
+ "msecs": 457.9739570617676,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 262.906551361084,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 458.07456970214844,
+ "msg": "Media data for video.3gp is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 263.00716400146484,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.00010061264038085938
+ },
+ {
+ "args": [
+ "{'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,602",
+ "created": 1580418394.6029294,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for video.mp4 is correct (Content {'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for video.mp4",
+ "{ 'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,602",
+ "created": 1580418394.6025841,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for video.mp4): { 'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508 } ()",
+ "module": "test",
+ "msecs": 602.5841236114502,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 407.5167179107666,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for video.mp4",
+ "{ 'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,602",
+ "created": 1580418394.6028209,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for video.mp4): result = { 'width': 1920, 'height': 1080, 'ratio': 1.7777777777777777, 'duration': 12.453, 'bitrate': 17883888, 'time': 1503125482, 'size': 27838508 } ()",
+ "module": "test",
+ "msecs": 602.820873260498,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 407.75346755981445,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 602.9293537139893,
+ "msg": "Media data for video.mp4 is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 407.86194801330566,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.00010848045349121094
+ },
+ {
+ "args": [
+ "{'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,655",
+ "created": 1580418394.6559129,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for video_special_time.avi is correct (Content {'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "'N/A'",
+ "duration",
+ "duration"
+ ],
+ "asctime": "2020-01-30 22:06:34,655",
+ "created": 1580418394.6553006,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "metadata.py",
+ "funcName": "__get_xxprobe_data__",
+ "levelname": "WARNING",
+ "levelno": 30,
+ "lineno": 142,
+ "message": "Can't convert 'N/A' (duration) for duration",
+ "module": "metadata",
+ "msecs": 655.3006172180176,
+ "msg": "Can't convert %s (%s) for %s",
+ "name": "MEDIA",
+ "pathname": "src/media/metadata.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 460.233211517334,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for video_special_time.avi",
+ "{ 'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,655",
+ "created": 1580418394.6557658,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for video_special_time.avi): { 'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622 } ()",
+ "module": "test",
+ "msecs": 655.7657718658447,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 460.69836616516113,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for video_special_time.avi",
+ "{ 'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,655",
+ "created": 1580418394.6558366,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for video_special_time.avi): result = { 'width': 320, 'height': 240, 'ratio': 0.0, 'duration': 26.531264, 'bitrate': 840554, 'time': 1086778620, 'size': 2787622 } ()",
+ "module": "test",
+ "msecs": 655.8365821838379,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 460.7691764831543,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 655.9128761291504,
+ "msg": "Media data for video_special_time.avi is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 460.8454704284668,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 7.62939453125e-05
+ },
+ {
+ "args": [
+ "{'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'size': 2965248, 'time': 1158528375, 'tm_is_subst': True}",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,716",
+ "created": 1580418394.7162106,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "equivalency_chk",
+ "levelname": "INFO",
+ "levelno": 20,
+ "lineno": 142,
+ "message": "Media data for video_no_date.avi is correct (Content {'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'size': 2965248, 'time': 1158528375, 'tm_is_subst': True} and Type is ).",
+ "module": "test",
+ "moduleLogger": [
+ {
+ "args": [
+ "Media data for video_no_date.avi",
+ "{ 'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'size': 2965248, 'time': 1158528375, 'tm_is_subst': True }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,715",
+ "created": 1580418394.7158937,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_result__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 22,
+ "message": "Result (Media data for video_no_date.avi): { 'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'size': 2965248, 'time': 1158528375, 'tm_is_subst': True } ()",
+ "module": "test",
+ "msecs": 715.8937454223633,
+ "msg": "Result (%s): %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 520.8263397216797,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ },
+ {
+ "args": [
+ "Media data for video_no_date.avi",
+ "{ 'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'time': 1158528375, 'tm_is_subst': True, 'size': 2965248 }",
+ ""
+ ],
+ "asctime": "2020-01-30 22:06:34,716",
+ "created": 1580418394.716101,
+ "exc_info": null,
+ "exc_text": null,
+ "filename": "test.py",
+ "funcName": "__report_expectation_equivalency__",
+ "levelname": "DEBUG",
+ "levelno": 10,
+ "lineno": 26,
+ "message": "Expectation (Media data for video_no_date.avi): result = { 'width': 640, 'height': 480, 'ratio': 1.3333333333333333, 'duration': 11.016, 'bitrate': 2153411, 'time': 1158528375, 'tm_is_subst': True, 'size': 2965248 } ()",
+ "module": "test",
+ "msecs": 716.1009311676025,
+ "msg": "Expectation (%s): result = %s (%s)",
+ "name": "__unittest__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 521.033525466919,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread"
+ }
+ ],
+ "msecs": 716.2106037139893,
+ "msg": "Media data for video_no_date.avi is correct (Content %s and Type is %s).",
+ "name": "__tLogger__",
+ "pathname": "src/unittest/test.py",
+ "process": 27732,
+ "processName": "MainProcess",
+ "relativeCreated": 521.1431980133057,
+ "stack_info": null,
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.00010967254638671875
+ }
+ ],
+ "thread": 139823516395328,
+ "threadName": "MainThread",
+ "time_consumption": 0.46280741691589355,
+ "time_finished": "2020-01-30 22:06:34,716",
+ "time_start": "2020-01-30 22:06:34,253"
+ }
+ },
+ "testrun_id": "p3",
+ "time_consumption": 0.46280741691589355,
+ "uid_list_sorted": [
+ "_XzMFcHYZEem_kd-7nxt1sg"
+ ]
+ }
+ ],
+ "unittest_information": {
+ "Version": "8c004e5e652fd9d0db633e752ce36782"
+ }
+}
\ No newline at end of file
diff --git a/_testresults_/unittest.pdf b/_testresults_/unittest.pdf
new file mode 100644
index 0000000..f526a6e
Binary files /dev/null and b/_testresults_/unittest.pdf differ
diff --git a/metadata.py b/metadata.py
new file mode 100644
index 0000000..dffcd8e
--- /dev/null
+++ b/metadata.py
@@ -0,0 +1,264 @@
+import logging
+import os
+from PIL import Image
+import subprocess
+import time
+
+
+logger_name = 'MEDIA'
+logger = logging.getLogger(logger_name)
+
+FILETYPE_AUDIO = 'audio'
+FILETYPE_IMAGE = 'image'
+FILETYPE_VIDEO = 'video'
+
+EXTENTIONS_AUDIO = ['.mp3', ]
+EXTENTIONS_IMAGE = ['.jpg', '.jpeg', '.jpe', '.png', '.tif', '.tiff', '.gif', ]
+EXTENTIONS_VIDEO = ['.avi', '.mpg', '.mpeg', '.mpe', '.mov', '.qt', '.mp4', '.webm', '.ogv', '.flv', '.3gp', ]
+
+KEY_ALBUM = 'album'
+KEY_APERTURE = 'aperture'
+KEY_ARTIST = 'artist'
+KEY_BITRATE = 'bitrate'
+KEY_CAMERA = 'camera'
+KEY_DURATION = 'duration'
+KEY_EXPOSURE_PROGRAM = 'exposure_program'
+KEY_EXPOSURE_TIME = 'exposure_time'
+KEY_FLASH = 'flash'
+KEY_FOCAL_LENGTH = 'focal_length'
+KEY_GENRE = 'genre'
+KEY_GPS = 'gps'
+KEY_HEIGHT = 'height'
+KEY_ISO = 'iso'
+KEY_ORIENTATION = 'orientation'
+KEY_RATIO = 'ratio'
+KEY_SIZE = 'size'
+KEY_TIME = 'time' # USE time.localtime(value) or datetime.fromtimestamp(value) to convert the timestamp
+KEY_TIME_IS_SUBSTITUTION = 'tm_is_subst'
+KEY_TITLE = 'title'
+KEY_TRACK = 'track'
+KEY_WIDTH = 'width'
+KEY_YEAR = 'year'
+
+__KEY_CAMERA_VENDOR__ = 'camera_vendor'
+__KEY_CAMERA_MODEL__ = 'camera_model'
+
+
+def get_filetype(full_path):
+ ext = os.path.splitext(full_path)[1]
+ if ext in EXTENTIONS_AUDIO:
+ return FILETYPE_AUDIO
+ elif ext in EXTENTIONS_IMAGE:
+ return FILETYPE_IMAGE
+ elif ext in EXTENTIONS_VIDEO:
+ return FILETYPE_VIDEO
+
+
+def get_audio_data(full_path):
+ conv_key_dict = {}
+ conv_key_dict['album'] = (str, KEY_ALBUM)
+ conv_key_dict['TAG:album'] = (str, KEY_ALBUM)
+ conv_key_dict['TAG:artist'] = (str, KEY_ARTIST)
+ conv_key_dict['artist'] = (str, KEY_ARTIST)
+ conv_key_dict['bit_rate'] = (__int_conv__, KEY_BITRATE)
+ conv_key_dict['duration'] = (float, KEY_DURATION)
+ conv_key_dict['TAG:genre'] = (str, KEY_GENRE)
+ conv_key_dict['genre'] = (str, KEY_GENRE)
+ conv_key_dict['TAG:title'] = (str, KEY_TITLE)
+ conv_key_dict['title'] = (str, KEY_TITLE)
+ conv_key_dict['TAG:track'] = (__int_conv__, KEY_TRACK)
+ conv_key_dict['track'] = (__int_conv__, KEY_TRACK)
+ conv_key_dict['TAG:date'] = (__int_conv__, KEY_YEAR)
+ conv_key_dict['date'] = (__int_conv__, KEY_YEAR)
+ return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
+
+
+def get_video_data(full_path):
+ conv_key_dict = {}
+ conv_key_dict['creation_time'] = (__vid_datetime_conv__, KEY_TIME)
+ conv_key_dict['TAG:creation_time'] = (__vid_datetime_conv__, KEY_TIME)
+ conv_key_dict['bit_rate'] = (__int_conv__, KEY_BITRATE)
+ conv_key_dict['duration'] = (float, KEY_DURATION)
+ conv_key_dict['height'] = (__int_conv__, KEY_HEIGHT)
+ conv_key_dict['width'] = (__int_conv__, KEY_WIDTH)
+ conv_key_dict['display_aspect_ratio'] = (__ratio_conv__, KEY_RATIO)
+ return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
+
+
+def get_image_data(full_path):
+ return __adapt__data__(__get_exif_data__(full_path), full_path)
+
+
+def __adapt__data__(data, full_path):
+ data[KEY_SIZE] = os.path.getsize(full_path)
+ # Join Camera Vendor and Camera Model
+ if __KEY_CAMERA_MODEL__ in data and __KEY_CAMERA_VENDOR__ in data:
+ model = data.pop(__KEY_CAMERA_MODEL__)
+ vendor = data.pop(__KEY_CAMERA_VENDOR__)
+ data[KEY_CAMERA] = '%s: %s' % (vendor, model)
+ # Add time if not exists
+ if KEY_TIME not in data:
+ if KEY_YEAR in data and KEY_TRACK in data:
+ # Use a date where track 1 is the newest in the given year
+ minute = int(data[KEY_TRACK] / 60)
+ second = (data[KEY_TRACK] - 60 * minute) % 60
+ #
+ data[KEY_TIME] = int(time.mktime((data[KEY_YEAR], 1, 1, 0, 59 - minute, 59 - second, 0, 0, 0)))
+ data[KEY_TIME_IS_SUBSTITUTION] = True
+ else:
+ data[KEY_TIME] = int(os.path.getmtime(full_path))
+ data[KEY_TIME_IS_SUBSTITUTION] = True
+ return data
+
+
+def __get_xxprobe_data__(full_path, conv_key_dict):
+ def _ffprobe_command(full_path):
+ return ['ffprobe', '-v', 'quiet', '-show_format', '-show_streams', full_path]
+
+ def _avprobe_command(full_path):
+ return ['avprobe', '-v', 'quiet', '-show_format', '-show_streams', full_path]
+
+ try:
+ xxprobe_text = subprocess.check_output(_avprobe_command(full_path))
+ except FileNotFoundError:
+ try:
+ xxprobe_text = subprocess.check_output(_ffprobe_command(full_path))
+ except FileNotFoundError:
+ logger.warning('ffprobe and avprobe seem to be not installed')
+ return {}
+ #
+ rv = {}
+ for line in xxprobe_text.decode('utf-8').splitlines():
+ try:
+ key, val = [snippet.strip() for snippet in line.split('=')]
+ except ValueError:
+ continue
+ else:
+ if key in conv_key_dict:
+ tp, name = conv_key_dict[key]
+ try:
+ rv[name] = tp(val)
+ except ValueError:
+ logger.log(logging.WARNING if val else logger.INFO, 'Can\'t convert %s (%s) for %s', repr(val), name, name)
+ return rv
+
+
+def __get_exif_data__(full_path):
+ rv = {}
+ im = Image.open(full_path)
+ try:
+ exif = dict(im._getexif().items())
+ except AttributeError:
+ logger.debug('%s does not have any exif information', full_path)
+ else:
+ conv_key_dict = {}
+ # IMAGE
+ conv_key_dict[0x9003] = (__datetime_conv__, KEY_TIME)
+ conv_key_dict[0x8822] = (__exposure_program_conv__, KEY_EXPOSURE_PROGRAM)
+ conv_key_dict[0x829A] = (__num_denum_conv__, KEY_EXPOSURE_TIME)
+ conv_key_dict[0x9209] = (__flash_conv__, KEY_FLASH)
+ conv_key_dict[0x829D] = (__num_denum_conv__, KEY_APERTURE)
+ conv_key_dict[0x920A] = (__num_denum_conv__, KEY_FOCAL_LENGTH)
+ conv_key_dict[0x8825] = (__gps_conv__, KEY_GPS)
+ conv_key_dict[0xA003] = (__int_conv__, KEY_HEIGHT)
+ conv_key_dict[0x8827] = (__int_conv__, KEY_ISO)
+ conv_key_dict[0x010F] = (str, __KEY_CAMERA_VENDOR__)
+ conv_key_dict[0x0110] = (str, __KEY_CAMERA_MODEL__)
+ conv_key_dict[0x0112] = (__int_conv__, KEY_ORIENTATION)
+ conv_key_dict[0xA002] = (__int_conv__, KEY_WIDTH)
+ for key in conv_key_dict:
+ if key in exif:
+ tp, name = conv_key_dict[key]
+ value = tp(exif[key])
+ if value is not None:
+ rv[name] = value
+ return rv
+
+
+# TODO: Join datetime converter __datetime_conv__ and __vid_datetime_conv_
+def __datetime_conv__(dt):
+ format_string = "%Y:%m:%d %H:%M:%S"
+ return int(time.mktime(time.strptime(dt, format_string)))
+
+
+def __vid_datetime_conv__(dt):
+ try:
+ dt = dt[:dt.index('.')]
+ except ValueError:
+ pass # time string seems to have no '.'
+ dt = dt.replace('T', ' ').replace('/', '').replace('\\', '')
+ if len(dt) == 16:
+ dt += ':00'
+ format_string = '%Y-%m-%d %H:%M:%S'
+ return int(time.mktime(time.strptime(dt, format_string)))
+
+
+def __exposure_program_conv__(n):
+ return {
+ 0: 'Unidentified',
+ 1: 'Manual',
+ 2: 'Program Normal',
+ 3: 'Aperture Priority',
+ 4: 'Shutter Priority',
+ 5: 'Program Creative',
+ 6: 'Program Action',
+ 7: 'Portrait Mode',
+ 8: 'Landscape Mode'
+ }.get(n, None)
+
+
+def __flash_conv__(n):
+ return {
+ 0: 'No',
+ 1: 'Fired',
+ 5: 'Fired (?)', # no return sensed
+ 7: 'Fired (!)', # return sensed
+ 9: 'Fill Fired',
+ 13: 'Fill Fired (?)',
+ 15: 'Fill Fired (!)',
+ 16: 'Off',
+ 24: 'Auto Off',
+ 25: 'Auto Fired',
+ 29: 'Auto Fired (?)',
+ 31: 'Auto Fired (!)',
+ 32: 'Not Available'
+ }.get(n, None)
+
+
+def __int_conv__(value):
+ try:
+ return int(value)
+ except ValueError:
+ for c in ['.', '/', '-']:
+ p = value.find(c)
+ if p >= 0:
+ value = value[:p]
+ return int(value)
+
+
+def __num_denum_conv__(data):
+ num, denum = data
+ return num / denum
+
+
+def __gps_conv__(data):
+ def lat_lon_cal(lon_or_lat):
+ lon_lat = 0.
+ fac = 1.
+ for num, denum in lon_or_lat:
+ lon_lat += float(num) / float(denum) * fac
+ fac *= 1. / 60.
+ return lon_lat
+ try:
+ lon = lat_lon_cal(data[0x0004])
+ lat = lat_lon_cal(data[0x0002])
+ if lon != 0 or lat != 0: # do not use lon and lat equal 0, caused by motorola gps weakness
+ return {'lon': lon, 'lat': lat}
+ except KeyError:
+ logger.warning('GPS data extraction failed for %s', repr(data))
+
+
+def __ratio_conv__(ratio):
+ ratio = ratio.replace('\\', '')
+ num, denum = ratio.split(':')
+ return float(num) / float(denum)
diff --git a/todo.tgz b/todo.tgz
deleted file mode 100644
index 1c33444..0000000
Binary files a/todo.tgz and /dev/null differ