summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libmat2/video.py95
-rwxr-xr-xmat22
-rw-r--r--tests/data/dirty.mp4bin0 -> 383631 bytes
-rw-r--r--tests/test_libmat2.py23
4 files changed, 101 insertions, 19 deletions
diff --git a/libmat2/video.py b/libmat2/video.py
index 85b5b2e..a5029c0 100644
--- a/libmat2/video.py
+++ b/libmat2/video.py
@@ -2,10 +2,37 @@ import os
2import subprocess 2import subprocess
3import logging 3import logging
4 4
5from typing import Dict, Union
6
5from . import exiftool 7from . import exiftool
6 8
7 9
8class AVIParser(exiftool.ExiftoolParser): 10class AbstractFFmpegParser(exiftool.ExiftoolParser):
11 """ Abstract parser for all FFmpeg-based ones, mainly for video. """
12 def remove_all(self) -> bool:
13 cmd = [_get_ffmpeg_path(),
14 '-i', self.filename, # input file
15 '-y', # overwrite existing output file
16 '-map', '0', # copy everything all streams from input to output
17 '-codec', 'copy', # don't decode anything, just copy (speed!)
18 '-loglevel', 'panic', # Don't show log
19 '-hide_banner', # hide the banner
20 '-map_metadata', '-1', # remove supperficial metadata
21 '-map_chapters', '-1', # remove chapters
22 '-disposition', '0', # Remove dispositions (check ffmpeg's manpage)
23 '-fflags', '+bitexact', # don't add any metadata
24 '-flags:v', '+bitexact', # don't add any metadata
25 '-flags:a', '+bitexact', # don't add any metadata
26 self.output_filename]
27 try:
28 subprocess.check_call(cmd)
29 except subprocess.CalledProcessError as e:
30 logging.error("Something went wrong during the processing of %s: %s", self.filename, e)
31 return False
32 return True
33
34
35class AVIParser(AbstractFFmpegParser):
9 mimetypes = {'video/x-msvideo', } 36 mimetypes = {'video/x-msvideo', }
10 meta_whitelist = {'SourceFile', 'ExifToolVersion', 'FileName', 'Directory', 37 meta_whitelist = {'SourceFile', 'ExifToolVersion', 'FileName', 'Directory',
11 'FileSize', 'FileModifyDate', 'FileAccessDate', 38 'FileSize', 'FileModifyDate', 'FileAccessDate',
@@ -24,25 +51,55 @@ class AVIParser(exiftool.ExiftoolParser):
24 'SampleRate', 'AvgBytesPerSec', 'BitsPerSample', 51 'SampleRate', 'AvgBytesPerSec', 'BitsPerSample',
25 'Duration', 'ImageSize', 'Megapixels'} 52 'Duration', 'ImageSize', 'Megapixels'}
26 53
54class MP4Parser(AbstractFFmpegParser):
55 mimetypes = {'video/mp4', }
56 meta_whitelist = {'AudioFormat', 'AvgBitrate', 'Balance', 'TrackDuration',
57 'XResolution', 'YResolution', 'ExifToolVersion',
58 'FileAccessDate', 'FileInodeChangeDate', 'FileModifyDate',
59 'FileName', 'FilePermissions', 'MIMEType', 'FileType',
60 'FileTypeExtension', 'Directory', 'ImageWidth',
61 'ImageSize', 'ImageHeight', 'FileSize', 'SourceFile',
62 'BitDepth', 'Duration', 'AudioChannels',
63 'AudioBitsPerSample', 'AudioSampleRate', 'Megapixels',
64 'MovieDataSize', 'VideoFrameRate', 'MediaTimeScale',
65 'SourceImageHeight', 'SourceImageWidth',
66 'MatrixStructure', 'MediaDuration'}
67 meta_key_value_whitelist = { # some metadata are mandatory :/
68 'CreateDate': '0000:00:00 00:00:00',
69 'CurrentTime': '0 s',
70 'MediaCreateDate': '0000:00:00 00:00:00',
71 'MediaLanguageCode': 'und',
72 'MediaModifyDate': '0000:00:00 00:00:00',
73 'ModifyDate': '0000:00:00 00:00:00',
74 'OpColor': '0 0 0',
75 'PosterTime': '0 s',
76 'PreferredRate': '1',
77 'PreferredVolume': '100.00%',
78 'PreviewDuration': '0 s',
79 'PreviewTime': '0 s',
80 'SelectionDuration': '0 s',
81 'SelectionTime': '0 s',
82 'TrackCreateDate': '0000:00:00 00:00:00',
83 'TrackModifyDate': '0000:00:00 00:00:00',
84 'TrackVolume': '0.00%',
85 }
86
27 def remove_all(self) -> bool: 87 def remove_all(self) -> bool:
28 cmd = [_get_ffmpeg_path(), 88 logging.warning('The format of "%s" (video/mp4) has some mandatory '
29 '-i', self.filename, # input file 89 'metadata fields; mat2 filled them with standard data.',
30 '-y', # overwrite existing output file 90 self.filename)
31 '-loglevel', 'panic', # Don't show log 91 return super().remove_all()
32 '-hide_banner', # hide the banner 92
33 '-codec', 'copy', # don't decode anything, just copy (speed!) 93 def get_meta(self) -> Dict[str, Union[str, dict]]:
34 '-map_metadata', '-1', # remove supperficial metadata 94 meta = super().get_meta()
35 '-map_chapters', '-1', # remove chapters 95
36 '-fflags', '+bitexact', # don't add any metadata 96 ret = dict() # type: Dict[str, Union[str, dict]]
37 '-flags:v', '+bitexact', # don't add any metadata 97 for key, value in meta.items():
38 '-flags:a', '+bitexact', # don't add any metadata 98 if key in self.meta_key_value_whitelist.keys():
39 self.output_filename] 99 if value == self.meta_key_value_whitelist[key]:
40 try: 100 continue
41 subprocess.check_call(cmd) 101 ret[key] = value
42 except subprocess.CalledProcessError as e: 102 return ret
43 logging.error("Something went wrong during the processing of %s: %s", self.filename, e)
44 return False
45 return True
46 103
47 104
48def _get_ffmpeg_path() -> str: # pragma: no cover 105def _get_ffmpeg_path() -> str: # pragma: no cover
diff --git a/mat2 b/mat2
index be63829..a36f62d 100755
--- a/mat2
+++ b/mat2
@@ -20,6 +20,8 @@ __version__ = '0.5.0'
20assert Tuple 20assert Tuple
21assert Union 21assert Union
22 22
23logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
24
23 25
24def __check_file(filename: str, mode: int=os.R_OK) -> bool: 26def __check_file(filename: str, mode: int=os.R_OK) -> bool:
25 if not os.path.exists(filename): 27 if not os.path.exists(filename):
diff --git a/tests/data/dirty.mp4 b/tests/data/dirty.mp4
new file mode 100644
index 0000000..1fc4788
--- /dev/null
+++ b/tests/data/dirty.mp4
Binary files differ
diff --git a/tests/test_libmat2.py b/tests/test_libmat2.py
index 1602480..e3072a8 100644
--- a/tests/test_libmat2.py
+++ b/tests/test_libmat2.py
@@ -521,3 +521,26 @@ class TestCleaning(unittest.TestCase):
521 os.remove('./tests/data/dirty.cleaned.zip') 521 os.remove('./tests/data/dirty.cleaned.zip')
522 os.remove('./tests/data/dirty.cleaned.cleaned.zip') 522 os.remove('./tests/data/dirty.cleaned.cleaned.zip')
523 523
524
525 def test_mp4(self):
526 try:
527 video._get_ffmpeg_path()
528 except RuntimeError:
529 raise unittest.SkipTest
530
531 shutil.copy('./tests/data/dirty.mp4', './tests/data/clean.mp4')
532 p = video.MP4Parser('./tests/data/clean.mp4')
533
534 meta = p.get_meta()
535 self.assertEqual(meta['Encoder'], 'HandBrake 0.9.4 2009112300')
536
537 ret = p.remove_all()
538 self.assertTrue(ret)
539
540 p = video.MP4Parser('./tests/data/clean.cleaned.mp4')
541 self.assertNotIn('Encoder', p.get_meta())
542 self.assertTrue(p.remove_all())
543
544 os.remove('./tests/data/clean.mp4')
545 os.remove('./tests/data/clean.cleaned.mp4')
546 os.remove('./tests/data/clean.cleaned.cleaned.mp4')