|
|
@@ -1,34 +1,13 @@
|
|
|
#!/usr/bin/env python3
|
|
|
import sys, re, os, subprocess, pathlib, logging
|
|
|
-import langcodes
|
|
|
-import tag, probe
|
|
|
+import tag, scan, lang
|
|
|
|
|
|
# Static config
|
|
|
debug = False
|
|
|
propedit_exec = 'mkvpropedit'
|
|
|
-mux_tag = 'zib.mux'
|
|
|
mux_vers = 1
|
|
|
-lang_tag = 'zib.lang'
|
|
|
-lang_vers = 1
|
|
|
-lang_default = 'eng'
|
|
|
-lang_codecs = [ 'subtitle', 'audio' ]
|
|
|
min_size = 0.9
|
|
|
-video_ext = [ r'mkv', r'mp4', r'm4v' ]
|
|
|
-subs_ext = [ r'srt' ]
|
|
|
-subs_sdh = [ r'sdh' ]
|
|
|
-subs_lang = [ r'\w\w' ]
|
|
|
-
|
|
|
-# Default metadata record
|
|
|
-meta_default = {
|
|
|
- 'path': None,
|
|
|
- 'name': '',
|
|
|
- 'ext': '',
|
|
|
- 'group': '',
|
|
|
- 'lang': None,
|
|
|
- 'sdh': False,
|
|
|
- 'mux_vers': None,
|
|
|
- 'lang_vers': None,
|
|
|
-}
|
|
|
+log_level = logging.WARN
|
|
|
|
|
|
# ENV config
|
|
|
if 'DEBUG' in os.environ:
|
|
|
@@ -41,68 +20,16 @@ if len(sys.argv) < 2:
|
|
|
root = pathlib.Path(sys.argv[1])
|
|
|
|
|
|
# Logging
|
|
|
+logging.basicConfig(stream=sys.stderr)
|
|
|
logger = logging.getLogger('remux')
|
|
|
if debug:
|
|
|
- logging.basicConfig(level=logging.DEBUG, stream=sys.stderr)
|
|
|
-else:
|
|
|
- logging.basicConfig(level=logging.WARN, stream=sys.stderr)
|
|
|
-
|
|
|
-# Tag and probe init
|
|
|
-tags = tag.tag()
|
|
|
-probes = probe.ffprobe()
|
|
|
-
|
|
|
-# Build filename-metadata parsing regex
|
|
|
-re_media = re.compile(
|
|
|
- r'^(.*?)' +
|
|
|
- r'(?:\.(' + '|'.join(subs_lang) + r'))?' +
|
|
|
- r'(?:\.(' + '|'.join(subs_sdh) + r'))?' +
|
|
|
- r'\.(' + '|'.join(video_ext + subs_ext) + r')$' ,
|
|
|
- re.IGNORECASE
|
|
|
-)
|
|
|
-
|
|
|
-def scan(files):
|
|
|
- logger.debug(f"Scanning {len(files)} files")
|
|
|
- media = []
|
|
|
-
|
|
|
- # Scan this directory for media
|
|
|
- for file in files:
|
|
|
- info = meta_default.copy()
|
|
|
- info['path'] = parent.joinpath(file)
|
|
|
- info['name'] = file
|
|
|
-
|
|
|
- # Parse media paths
|
|
|
- m = re_media.search(info['name'])
|
|
|
- if m and len(m.groups()) == 4:
|
|
|
- info['group'] = m.group(1)
|
|
|
-
|
|
|
- try:
|
|
|
- lang = langcodes.Language.get(m.group(2).lower())
|
|
|
- info['lang'] = lang.to_alpha3()
|
|
|
- except:
|
|
|
- if m.group(2):
|
|
|
- logger.warning(f"Unable to match language code: {m.group(2)}")
|
|
|
-
|
|
|
- if m.group(3):
|
|
|
- info['sdh'] = m.group(3).lower() in subs_sdh
|
|
|
+ log_level = logging.DEBUG
|
|
|
+logger.setLevel(log_level)
|
|
|
|
|
|
- info['ext'] = m.group(4).lower()
|
|
|
- del m
|
|
|
-
|
|
|
- # Skip non-media files (and parsing errors)
|
|
|
- if 'ext' not in info:
|
|
|
- logger.debug(f"Skipping non-media file: {info['name']}")
|
|
|
- continue
|
|
|
-
|
|
|
- # Read tags
|
|
|
- info['mux_vers'] = tags.get_int(info['path'], mux_tag)
|
|
|
- info['lang_vers'] = tags.get_int(info['path'], lang_tag)
|
|
|
-
|
|
|
- # Loop end
|
|
|
- media.append(info)
|
|
|
- logger.debug(f"Found file: {info}")
|
|
|
-
|
|
|
- # Return
|
|
|
- return media
|
|
|
+# Module init
|
|
|
+tags = tag.Tag()
|
|
|
+scans = scan.Scan(logLevel=log_level)
|
|
|
+langs = lang.Lang(logLevel=log_level)
|
|
|
|
|
|
def mux(media):
|
|
|
logger.debug(f"Muxing {len(media)} files")
|
|
|
@@ -134,10 +61,10 @@ def mux(media):
|
|
|
subs = []
|
|
|
videos = []
|
|
|
for file in group:
|
|
|
- if file['ext'] in video_ext:
|
|
|
+ if file['ext'] in scans.video_ext:
|
|
|
logger.debug(f"Found video: {file['name']}")
|
|
|
videos.append(file)
|
|
|
- elif file['ext'] in subs_ext:
|
|
|
+ elif file['ext'] in scans.subs_ext:
|
|
|
logger.debug(f"Found subtitle: {file['name']}")
|
|
|
subs.append(file)
|
|
|
else:
|
|
|
@@ -153,7 +80,7 @@ def mux(media):
|
|
|
# Build args
|
|
|
final = videos[0]['path'].parent.joinpath(f'{videos[0]['group']}.mkv')
|
|
|
temp = final.with_suffix('.mkv-remux')
|
|
|
- args = [ 'mkvmerge', '--default-language', lang_default, '-o', temp ]
|
|
|
+ args = [ 'mkvmerge', '--default-language', langs.lang_default, '-o', temp ]
|
|
|
args.append(videos[0]['path'])
|
|
|
|
|
|
# Subtitles get language and SDH metadata
|
|
|
@@ -185,7 +112,7 @@ def mux(media):
|
|
|
continue
|
|
|
|
|
|
# Set tag
|
|
|
- if not tags.set_int(temp, mux_tag, mux_vers):
|
|
|
+ if not tags.set_int(temp, tags.mux_tag, mux_vers):
|
|
|
continue
|
|
|
|
|
|
# Remove sources
|
|
|
@@ -203,7 +130,7 @@ def mux(media):
|
|
|
try:
|
|
|
logger.debug(f"Renaming: {temp.name} -> {final.name}")
|
|
|
temp.rename(final)
|
|
|
- info = meta_default.copy()
|
|
|
+ info = scans.meta_default.copy()
|
|
|
info['path'] = final
|
|
|
info['name'] = final.name
|
|
|
info['ext'] = 'mkv'
|
|
|
@@ -220,56 +147,6 @@ def mux(media):
|
|
|
logger.error(f"Rename error ({final}): {err}")
|
|
|
continue
|
|
|
|
|
|
-def lang(media):
|
|
|
- logger.debug(f"Language processing {len(media)} files")
|
|
|
-
|
|
|
- for file in media:
|
|
|
- logger.debug(f"Lang: {file['name']}")
|
|
|
-
|
|
|
- # Skip files that do not need lang processing
|
|
|
- if not file['ext'] in video_ext:
|
|
|
- logger.debug(f"Skipping non-video item: {file['path']}")
|
|
|
- continue
|
|
|
- if 'lang_vers' in file and file['lang_vers'] and file['lang_vers'] >= lang_vers:
|
|
|
- logger.debug(f"Skipping item with current lang_vers: {file['path']}")
|
|
|
- continue
|
|
|
-
|
|
|
- # Probe streams
|
|
|
- streams = probes.probe(file['path'])
|
|
|
- for stream in streams:
|
|
|
- if not 'index' in stream:
|
|
|
- logger.error(f"No index for stream ({file['name']}): {stream}")
|
|
|
- continue
|
|
|
-
|
|
|
- # Find missing and undefined languages for subtitle and audio streams
|
|
|
- updates = []
|
|
|
- for stream in streams:
|
|
|
- if 'codec_type' not in stream or stream['codec_type'] not in lang_codecs:
|
|
|
- logger.debug(f"Skipping missing or ignored codec_type: {file['name']}:{stream['index']}")
|
|
|
- continue
|
|
|
- if 'TAG:language' not in stream or stream['TAG:language'] in [ None, 'und' ]:
|
|
|
- logger.debug(f"Missing or undefined language in {file['name']}:{stream['codec_type']}:{stream['index']}")
|
|
|
- updates.append(int(stream['index']))
|
|
|
-
|
|
|
- # mkvpropedit
|
|
|
- if updates:
|
|
|
- args = [ propedit_exec, file['path'] ]
|
|
|
- for index in updates:
|
|
|
- # mkvpropedit starts counting at 1
|
|
|
- args.extend([ '--edit', f"track:{index + 1}", '--set', f"language={lang_default}" ])
|
|
|
-
|
|
|
- logger.debug(f"propedit command: {args}")
|
|
|
- propedit = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
- logger.debug(f"propedit result: {propedit}")
|
|
|
- if propedit.returncode != 0:
|
|
|
- logger.error(f"propedit error ({propedit.returncode}): {propedit.stdout.decode('utf-8')}")
|
|
|
- continue
|
|
|
-
|
|
|
- # Set tag
|
|
|
- if not tags.set_int(file['path'], lang_tag, lang_vers):
|
|
|
- continue
|
|
|
- file['lang_vers'] = lang_vers
|
|
|
-
|
|
|
# Execute
|
|
|
logger.debug(f"Root: {root}")
|
|
|
if not root.is_dir():
|
|
|
@@ -277,6 +154,6 @@ if not root.is_dir():
|
|
|
sys.exit(-1)
|
|
|
for (parent, dirs, files) in root.walk():
|
|
|
logger.debug(f"Walking: {parent}")
|
|
|
- media = scan(files)
|
|
|
+ media = scans.scan(files, parent)
|
|
|
mux(media)
|
|
|
- lang(media)
|
|
|
+ langs.lang(media)
|