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