batchmp 1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. batchmp/__init__.py +0 -0
  2. batchmp/cli/__init__.py +0 -0
  3. batchmp/cli/base/__init__.py +0 -0
  4. batchmp/cli/base/bmp_dispatch.py +60 -0
  5. batchmp/cli/base/bmp_options.py +349 -0
  6. batchmp/cli/base/vchk.py +47 -0
  7. batchmp/cli/bmfp/__init__.py +0 -0
  8. batchmp/cli/bmfp/bmfp_dispatch.py +120 -0
  9. batchmp/cli/bmfp/bmfp_options.py +442 -0
  10. batchmp/cli/renamer/__init__.py +0 -0
  11. batchmp/cli/renamer/renamer_dispatch.py +135 -0
  12. batchmp/cli/renamer/renamer_options.py +355 -0
  13. batchmp/cli/tagger/__init__.py +0 -0
  14. batchmp/cli/tagger/tagger_dispatch.py +143 -0
  15. batchmp/cli/tagger/tagger_options.py +338 -0
  16. batchmp/commons/__init__.py +0 -0
  17. batchmp/commons/chainedhandler.py +102 -0
  18. batchmp/commons/descriptors.py +173 -0
  19. batchmp/commons/progressbar.py +154 -0
  20. batchmp/commons/taskprocessor.py +149 -0
  21. batchmp/commons/utils.py +194 -0
  22. batchmp/ffmptools/__init__.py +0 -0
  23. batchmp/ffmptools/ffcommands/__init__.py +0 -0
  24. batchmp/ffmptools/ffcommands/cmdopt.py +115 -0
  25. batchmp/ffmptools/ffcommands/convert.py +130 -0
  26. batchmp/ffmptools/ffcommands/cuesplit.py +223 -0
  27. batchmp/ffmptools/ffcommands/denoise.py +173 -0
  28. batchmp/ffmptools/ffcommands/fragment.py +121 -0
  29. batchmp/ffmptools/ffcommands/normalize_peak.py +135 -0
  30. batchmp/ffmptools/ffcommands/segment.py +157 -0
  31. batchmp/ffmptools/ffcommands/silencesplit.py +159 -0
  32. batchmp/ffmptools/ffrunner.py +189 -0
  33. batchmp/ffmptools/ffutils.py +300 -0
  34. batchmp/ffmptools/processors/__init__.py +0 -0
  35. batchmp/ffmptools/processors/basefp.py +92 -0
  36. batchmp/ffmptools/processors/ffentry.py +81 -0
  37. batchmp/ffmptools/utils/__init__.py +0 -0
  38. batchmp/ffmptools/utils/cueparse.py +227 -0
  39. batchmp/ffmptools/utils/cuesheet.py +239 -0
  40. batchmp/fstools/__init__.py +0 -0
  41. batchmp/fstools/builders/__init__.py +0 -0
  42. batchmp/fstools/builders/fsb.py +221 -0
  43. batchmp/fstools/builders/fsentry.py +60 -0
  44. batchmp/fstools/builders/fsprms.py +372 -0
  45. batchmp/fstools/dirtools.py +549 -0
  46. batchmp/fstools/fsutils.py +272 -0
  47. batchmp/fstools/rename.py +390 -0
  48. batchmp/fstools/walker.py +79 -0
  49. batchmp/tags/__init__.py +0 -0
  50. batchmp/tags/handlers/__init__.py +0 -0
  51. batchmp/tags/handlers/basehandler.py +99 -0
  52. batchmp/tags/handlers/ffmphandler.py +75 -0
  53. batchmp/tags/handlers/ffmphandlers/__init__.py +0 -0
  54. batchmp/tags/handlers/ffmphandlers/base.py +243 -0
  55. batchmp/tags/handlers/mtghandler.py +56 -0
  56. batchmp/tags/handlers/pmhandler.py +36 -0
  57. batchmp/tags/handlers/tagsholder.py +264 -0
  58. batchmp/tags/output/__init__.py +0 -0
  59. batchmp/tags/output/formatters.py +218 -0
  60. batchmp/tags/processors/__init__.py +0 -0
  61. batchmp/tags/processors/basetp.py +266 -0
  62. batchmp-1.4.dist-info/METADATA +422 -0
  63. batchmp-1.4.dist-info/RECORD +68 -0
  64. batchmp-1.4.dist-info/WHEEL +5 -0
  65. batchmp-1.4.dist-info/entry_points.txt +5 -0
  66. batchmp-1.4.dist-info/licenses/LICENSE +11 -0
  67. batchmp-1.4.dist-info/top_level.txt +1 -0
  68. batchmp-1.4.dist-info/zip-safe +1 -0
@@ -0,0 +1,79 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ import os, sys
16
+ from batchmp.fstools.builders.fsentry import FSEntry, FSEntryType
17
+ from batchmp.fstools.builders.fsprms import FSEntryParamsBase
18
+ from batchmp.fstools.builders.fsb import FSEntryBuilderBase
19
+
20
+ class DWalker:
21
+ ''' Walks content of a directory, generating
22
+ a sequence of structured FS elements (FSEntry)
23
+ '''
24
+ @staticmethod
25
+ def entries(fs_entry_params, walker=os.walk):
26
+ ''' generates a sequence of FSEntries elements
27
+ '''
28
+ # let's walk
29
+ for rpath, dnames, fnames in walker(fs_entry_params.src_dir):
30
+ # set the current dir
31
+ fs_entry_params.rpath = rpath
32
+
33
+ # check levels
34
+ if fs_entry_params.skip_iteration:
35
+ continue
36
+
37
+ # set siblings
38
+ fs_entry_params.fnames = fnames
39
+ fs_entry_params.dnames = dnames
40
+
41
+ # sync dnames for sorting / filtering
42
+ dnames[:] = fs_entry_params.merged_dnames
43
+
44
+ # yield the current folder
45
+ yield from fs_entry_params.fs_entry_builder.build_root_entry(fs_entry_params)
46
+
47
+ ## Files processing ##
48
+ yield from fs_entry_params.fs_entry_builder.build_entry(fs_entry_params)
49
+
50
+ @staticmethod
51
+ def file_entries(fs_entry_params, pass_filter = None):
52
+ if not pass_filter:
53
+ pass_filter = lambda f: True
54
+
55
+ for entry in DWalker.entries(fs_entry_params):
56
+
57
+ if entry.type in (FSEntryType.ROOT, FSEntryType.DIR):
58
+ continue
59
+
60
+ if not pass_filter(entry.realpath):
61
+ continue
62
+ else:
63
+ yield entry
64
+
65
+ @staticmethod
66
+ def dir_entries(fs_entry_params, pass_filter = None):
67
+ if not pass_filter:
68
+ pass_filter = lambda f: True
69
+
70
+ for entry in DWalker.entries(fs_entry_params):
71
+
72
+ if entry.type in (FSEntryType.ROOT, FSEntryType.FILE):
73
+ continue
74
+
75
+ if not pass_filter(entry.realpath):
76
+ continue
77
+ else:
78
+ yield entry
79
+
File without changes
File without changes
@@ -0,0 +1,99 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ """ Tag Handlers responsibility chain
16
+ """
17
+ import os
18
+ from enum import Enum
19
+ from batchmp.commons.chainedhandler import ChainedHandler
20
+ from abc import abstractmethod
21
+ from batchmp.fstools.fsutils import UniqueDirNamesChecker
22
+ from batchmp.commons.descriptors import LazyInstancePropertyDescriptor
23
+
24
+
25
+ class DetauchedArtType(Enum):
26
+ ''' Detached art type specifier
27
+ '''
28
+ PNG, JPEG = 0, 1
29
+ @staticmethod
30
+ def art_ext(type):
31
+ if type == DetauchedArtType.JPEG:
32
+ return '.jpg'
33
+ else:
34
+ return '.png'
35
+
36
+
37
+ class TagHandler(ChainedHandler):
38
+ tag_holder = LazyInstancePropertyDescriptor('batchmp.tags.handlers.tagsholder.TagHolder')
39
+
40
+ def __init__(self):
41
+ self._media_handler = None
42
+
43
+ def __add__(self, tag_handler):
44
+ tag_handler.tag_holder = self.tag_holder
45
+ return super().__add__(tag_handler)
46
+
47
+ def _can_handle(self, path):
48
+ return False
49
+
50
+ # Tag Handler operations
51
+ def save(self):
52
+ self.responder._save()
53
+
54
+ @abstractmethod
55
+ def _save(self):
56
+ ''' implement in specific tag handlers
57
+ '''
58
+ pass
59
+
60
+ # Helpers
61
+ def _reset_handler(self):
62
+ ''' resets the handler
63
+ '''
64
+ self._media_handler = None
65
+ self.tag_holder.reset_tags()
66
+
67
+ def copy_tags(self, tag_holder = None):
68
+ ''' copies tags from a tag_holder
69
+ '''
70
+ self.tag_holder.copy_tags(tag_holder = tag_holder)
71
+
72
+ def clear_tags(self):
73
+ ''' clear tags values
74
+ '''
75
+ self.tag_holder.clear_tags()
76
+
77
+ def detauch_art(self, dir_path = None, type = None):
78
+ ''' detauches art, returning art file path
79
+ '''
80
+ if not type or (type not in DetauchedArtType):
81
+ type = DetauchedArtType.PNG
82
+ art_path = None
83
+ if self.tag_holder.art:
84
+ if not dir_path:
85
+ dir_path = os.path.basename(self._media_handler.path)
86
+ fname = os.path.splitext(os.path.basename(self._media_handler.path))[0] + DetauchedArtType.art_ext(type)
87
+ fname = UniqueDirNamesChecker(dir_path).unique_name(fname)
88
+
89
+ art_path = os.path.join(dir_path, fname)
90
+ with open(art_path, 'wb') as f:
91
+ f.write(self.tag_holder.art)
92
+ return art_path
93
+
94
+
95
+
96
+
97
+
98
+
99
+
@@ -0,0 +1,75 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ import os, shutil
16
+ from batchmp.commons.utils import temp_dir
17
+ from batchmp.tags.handlers.basehandler import TagHandler
18
+ from batchmp.tags.handlers.ffmphandlers.base import FFBaseFormatHandler
19
+ from batchmp.ffmptools.ffutils import FFH
20
+ from batchmp.commons.utils import run_cmd, CmdProcessingError
21
+
22
+
23
+ class FFmpegTagHandler(TagHandler):
24
+ ''' FFmpeg-Based Tag Handler
25
+ '''
26
+ def _can_handle(self, path):
27
+ self._reset_handler()
28
+ media_entry = FFH.media_file_info(path)
29
+ if media_entry:
30
+ self._media_handler = FFBaseFormatHandler(self.tag_holder) #... + FFSpecificFormatHandler() + ...
31
+ if self._media_handler.can_handle(media_entry):
32
+ self.tag_holder.filepath = path
33
+ self._media_handler.parse()
34
+ return True
35
+ return False
36
+
37
+ def _save(self, write_artwork = True):
38
+ ''' saves tags
39
+ '''
40
+ if not self._media_handler:
41
+ return
42
+
43
+ with temp_dir() as tmp:
44
+ tmp_fpath = os.path.join(tmp, os.path.basename(self._media_handler.path))
45
+
46
+ artwork_writer = write_artwork and \
47
+ self._media_handler.artwork_writer_supported_format and \
48
+ self.tag_holder.art
49
+ art_path = self.detauch_art(dir_path = tmp) if artwork_writer else None
50
+
51
+ save_cmd = self._media_handler.build_save_cmd(art_path = art_path)
52
+ save_cmd = ''.join((save_cmd, ' "{}"'.format(tmp_fpath)))
53
+ try:
54
+ failed = False
55
+ output, _ = run_cmd(save_cmd)
56
+ except CmdProcessingError as e:
57
+ if artwork_writer:
58
+ self._save(write_artwork = False)
59
+ return
60
+ else:
61
+ failed = True
62
+ else:
63
+ try:
64
+ shutil.move(tmp_fpath, self._media_handler.path)
65
+ except OSError as e:
66
+ raise e
67
+
68
+ if failed:
69
+ print ('FFMP: could not process {}'.format(self._media_handler.path))
70
+ else:
71
+ if not write_artwork:
72
+ print ('FFMP: skipped artwork for {}'.format(self._media_handler.path))
73
+
74
+
75
+
File without changes
@@ -0,0 +1,243 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ """ FFmpeg generic handler (no format-related specifics)
16
+ """
17
+ import os, shlex
18
+ from batchmp.commons.chainedhandler import ChainedHandler
19
+ from batchmp.ffmptools.ffutils import FFH
20
+ from batchmp.tags.handlers.tagsholder import TagHolder
21
+ from batchmp.commons.utils import temp_dir
22
+ from batchmp.commons.utils import (
23
+ run_cmd,
24
+ CmdProcessingError
25
+ )
26
+
27
+ class FFBaseFormatHandler(ChainedHandler):
28
+ ARTWORK_WRITER_SUPPORTED_FORMATS = ['MP3']
29
+
30
+ ''' Base FFmpeg tags parse
31
+ '''
32
+ def __init__(self, tag_holder):
33
+ self.media_entry = None
34
+ self.tag_holder = tag_holder
35
+
36
+ def __add__(self, tag_handler):
37
+ tag_handler.tag_holder = self.tag_holder
38
+ return super().__add__(tag_handler)
39
+
40
+ def _can_handle(self, media_entry):
41
+ if media_entry and FFH.ffmpeg_supported_media(ffentry = media_entry):
42
+ self.media_entry = media_entry
43
+ return True
44
+ return False
45
+
46
+ @property
47
+ def path(self):
48
+ if self.media_entry:
49
+ return self.media_entry.path
50
+ else:
51
+ return None
52
+
53
+ @property
54
+ def type(self):
55
+ if self.media_entry and self.media_entry.format:
56
+ format = self.media_entry.format.get('format_name')
57
+ if format:
58
+ format = format.split(',')[0]
59
+ return format.upper()
60
+ return None
61
+
62
+ @property
63
+ def artwork_writer_supported_format(self):
64
+ return self.type in self.ARTWORK_WRITER_SUPPORTED_FORMATS
65
+
66
+ # tag handler operations
67
+ def parse(self):
68
+ return self.responder._parse()
69
+
70
+ def build_save_cmd(self, art_path = None):
71
+ return self.responder._build_save_cmd(art_path = art_path)
72
+
73
+ # tag handler ops impl
74
+ def _parse(self):
75
+ ''' parses tags from FFmpeg output
76
+ '''
77
+ self._parse_tags()
78
+ self._parse_stats()
79
+ self._parse_art()
80
+
81
+ def _parse_tags(self):
82
+ # Tags
83
+ if self.media_entry.format:
84
+ tag_info = self.media_entry.format.get('tags')
85
+ if not tag_info:
86
+ tag_info = self.media_entry.audio.get('tags')
87
+ if tag_info:
88
+ tag_info = {k.lower():v for k,v in tag_info.items()}
89
+
90
+ self.tag_holder.title = tag_info.get('title')
91
+ self.tag_holder.album = tag_info.get('album')
92
+ self.tag_holder.artist = tag_info.get('artist')
93
+ self.tag_holder.albumartist = tag_info.get('album_artist')
94
+ self.tag_holder.genre = tag_info.get('genre')
95
+ self.tag_holder.year = tag_info.get('date')
96
+ self.tag_holder.composer = tag_info.get('composer')
97
+ self.tag_holder.encoder = tag_info.get('encoded_by')
98
+
99
+ self.tag_holder.bpm = tag_info.get('tbpm') or tag_info.get('bpm')
100
+ self.tag_holder.comp = tag_info.get('compilation')
101
+ self.tag_holder.grouping = tag_info.get('tit1')
102
+ self.tag_holder.comments = tag_info.get('comment')
103
+ self.tag_holder.lyrics = tag_info.get('lyrics')
104
+
105
+ if 'track' in tag_info:
106
+ track_info = tag_info['track'].split('/')
107
+ if len(track_info) > 0:
108
+ self.tag_holder.track = track_info[0]
109
+ self.tag_holder.tracktotal = track_info[len(track_info) - 1]
110
+ if 'disc' in tag_info:
111
+ disc_info = tag_info['disc'].split('/')
112
+ if len(disc_info) > 0:
113
+ self.tag_holder.disc = disc_info[0]
114
+ self.tag_holder.disctotal = disc_info[len(disc_info) - 1]
115
+
116
+ def _parse_stats(self):
117
+ # Non-taggable fields
118
+ # non-tagable fields defaults
119
+ self.tag_holder.length = 0.0
120
+ self.tag_holder.bitrate = 0
121
+ self.tag_holder.samplerate = 0
122
+ self.tag_holder.channels = 0
123
+ self.tag_holder.bitdepth = 0
124
+
125
+ if self.media_entry.audio:
126
+ self.tag_holder.length = float(self.media_entry.audio.get('duration', 0.0))
127
+ self.tag_holder.bitrate = int(self.media_entry.audio.get('bit_rate', 0))
128
+ self.tag_holder.samplerate = int(self.media_entry.audio.get('sample_rate', 0))
129
+ self.tag_holder.bitdepth = int(self.media_entry.audio.get('bits_per_sample', 0))
130
+ self.tag_holder.channels = int(self.media_entry.audio.get('channels', 0))
131
+
132
+ if self.media_entry.format:
133
+ if self.tag_holder.bitrate == 0:
134
+ self.tag_holder.bitrate = int(self.media_entry.format.get('bit_rate', 0))
135
+
136
+ format = self.media_entry.format.get('format_name')
137
+ if format:
138
+ format = format.upper()
139
+ format_long = self.media_entry.format.get('format_long_name')
140
+ if format_long:
141
+ format = '{0}: {1}'.format(format, format_long)
142
+
143
+ if format:
144
+ self.tag_holder.format = format.upper()
145
+
146
+ if not self.tag_holder.length:
147
+ self.tag_holder.length = float(self.media_entry.format.get('duration', 0.0))
148
+
149
+ def _parse_art(self):
150
+ # Art
151
+ if self.media_entry.artwork:
152
+ self.tag_holder.deferred_art_method = self.artwork_reader
153
+
154
+ def _build_save_cmd(self, art_path = None):
155
+ ''' build save cmd string
156
+ '''
157
+ track_tagger = disc_tagger = ''
158
+ if self.tag_holder.track:
159
+ if self.tag_holder.tracktotal:
160
+ track_tagger = ' -metadata track="{0}/{1}"'.format(self.tag_holder.track,
161
+ self.tag_holder.tracktotal)
162
+ else:
163
+ track_tagger = ' -metadata track="{}"'.format(self.tag_holder.track)
164
+ elif self.tag_holder.tracktotal:
165
+ track_tagger = ' -metadata track="0/{}"'.format(self.tag_holder.tracktotal)
166
+ else:
167
+ track_tagger = ' -metadata track=""'
168
+ if self.tag_holder.disc:
169
+ if self.tag_holder.disctotal:
170
+ disc_tagger = ' -metadata disc="{0}/{1}"'.format(self.tag_holder.disc,
171
+ self.tag_holder.disctotal)
172
+ else:
173
+ disc_tagger = ' -metadata disc="{}"'.format(self.tag_holder.disc)
174
+ elif self.tag_holder.disctotal:
175
+ disc_tagger = ' -metadata disc="0/{}"'.format(self.tag_holder.disctotal)
176
+ else:
177
+ disc_tagger = ' -metadata disc=""'
178
+
179
+ cmd = ''.join(('ffmpeg ',
180
+ ' -v quiet',
181
+ ' -i {}'.format(shlex.quote(self.media_entry.path)),
182
+ ' -i {}'.format(shlex.quote(art_path)) if art_path else '',
183
+ ' -c copy',
184
+ ' -map_metadata 0',
185
+ ' -map 0',
186
+ ' -map 1' if art_path else '',
187
+ ' -metadata title="{}"'.format(self.tag_holder.title
188
+ if self.tag_holder.title else ''),
189
+ ' -metadata album="{}"'.format(self.tag_holder.album
190
+ if self.tag_holder.album else ''),
191
+ ' -metadata artist="{}"'.format(self.tag_holder.artist
192
+ if self.tag_holder.artist else ''),
193
+ ' -metadata album_artist="{}"'.format(self.tag_holder.albumartist
194
+ if self.tag_holder.albumartist else ''),
195
+ ' -metadata genre="{}"'.format(self.tag_holder.genre
196
+ if self.tag_holder.genre else ''),
197
+ ' -metadata year="{}"'.format(self.tag_holder.year
198
+ if self.tag_holder.year else ''),
199
+ ' -metadata composer="{}"'.format(self.tag_holder.composer
200
+ if self.tag_holder.composer else ''),
201
+ ' -metadata encoded_by="{}"'.format(self.tag_holder.encoder
202
+ if self.tag_holder.encoder else ''),
203
+
204
+ ' -metadata BPM="{}"'.format(self.tag_holder.bpm
205
+ if self.tag_holder.bpm else ''),
206
+ ' -metadata TBPM="{}"'.format(self.tag_holder.bpm
207
+ if self.tag_holder.bpm else ''),
208
+ ' -metadata compilation="{}"'.format(self.tag_holder.comp
209
+ if self.tag_holder.comp else ''),
210
+ ' -metadata grouping="{}"'.format(self.tag_holder.grouping
211
+ if self.tag_holder.grouping else ''),
212
+ ' -metadata comment="{}"'.format(self.tag_holder.comments
213
+ if self.tag_holder.comments else ''),
214
+ ' -metadata lyrics="{}"'.format(self.tag_holder.lyrics
215
+ if self.tag_holder.lyrics else ''),
216
+ track_tagger,
217
+ disc_tagger))
218
+ return cmd
219
+
220
+ def artwork_reader(self):
221
+ ''' reads cover art from a media file
222
+ '''
223
+ artwork = None
224
+ if self.media_entry.artwork:
225
+ artwork_stream_idx = self.media_entry.artwork.get('index')
226
+ with temp_dir() as tmp:
227
+ detached_img_path = os.path.join(tmp, 'detached.png')
228
+ cmd = ' '.join(('ffmpeg',
229
+ ' -v quiet',
230
+ ' -i {}'.format(shlex.quote(self.media_entry.path)),
231
+ ' -map 0:{}'.format(artwork_stream_idx),
232
+ ' -an',
233
+ ' -vcodec copy',
234
+ ' {}'.format(detached_img_path)))
235
+ try:
236
+ output, _ = run_cmd(cmd)
237
+ except CmdProcessingError as e:
238
+ pass
239
+ else:
240
+ with open(detached_img_path, 'rb') as img:
241
+ artwork = img.read()
242
+ return artwork
243
+
@@ -0,0 +1,56 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ from mediafile import MediaFile, UnreadableFileError, MutagenError
16
+ from batchmp.tags.handlers.basehandler import TagHandler
17
+
18
+
19
+ class MutagenTagHandler(TagHandler):
20
+ ''' Mutagen-Based Tag Handler
21
+ '''
22
+ def _can_handle(self, path):
23
+ ''' Handles the formats supported by Mutagen
24
+ '''
25
+ self._reset_handler()
26
+ try:
27
+ self._media_handler = MediaFile(path)
28
+ except UnreadableFileError as error:
29
+ return False
30
+ else:
31
+ self.tag_holder.filepath = path
32
+ self._parse_tags()
33
+ return True
34
+
35
+ def _parse_tags(self):
36
+ ''' copies relevant properties from Mutagen MediaFile
37
+ '''
38
+ for field in self._media_handler.readable_fields():
39
+ if field in dir(self.tag_holder):
40
+ attr = getattr(self._media_handler, field)
41
+ if attr:
42
+ setattr(self.tag_holder, field, attr)
43
+ #else:
44
+ # dev test
45
+ # attr = getattr(self._media_handler, field)
46
+ # if attr:
47
+ # print('Ignoring: {0} with value: {1}'.format(field, attr))
48
+
49
+ def _save(self):
50
+ if self._media_handler:
51
+ for field in self.tag_holder.taggable_fields():
52
+ value = getattr(self.tag_holder, field)
53
+ setattr(self._media_handler, field, value)
54
+
55
+ self._media_handler.save()
56
+
@@ -0,0 +1,36 @@
1
+ # coding=utf8
2
+ ## Copyright (c) 2014 Arseniy Kuznetsov
3
+ ##
4
+ ## This program is free software; you can redistribute it and/or
5
+ ## modify it under the terms of the GNU General Public License
6
+ ## as published by the Free Software Foundation; either version 2
7
+ ## of the License, or (at your option) any later version.
8
+ ##
9
+ ## This program is distributed in the hope that it will be useful,
10
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ ## GNU General Public License for more details.
13
+
14
+
15
+ from batchmp.ffmptools.ffutils import FFH
16
+ from batchmp.tags.handlers.basehandler import TagHandler
17
+ from batchmp.fstools.builders.fsentry import FSMediaEntryType
18
+
19
+
20
+ class PlayableMediaHandler(TagHandler):
21
+ ''' Playable Media Cheker
22
+ '''
23
+ def _can_handle(self, path):
24
+ ''' Quick check for right media types to handle
25
+ '''
26
+ media_type = FFH.media_type(fpath = path, fast_scan = True)
27
+
28
+ supported_media = media_type in (FSMediaEntryType.VIDEO, FSMediaEntryType.AUDIO)
29
+ if media_type in (FSMediaEntryType.VIDEO, FSMediaEntryType.AUDIO):
30
+ return True
31
+
32
+ return False
33
+
34
+
35
+
36
+