Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- """
- Usage:
- ./convert.py source_folder destination_folder
- Depends:
- apt install ffmpeg
- pip install subliminal
- """
- import filecmp
- import os
- import shlex
- import shutil
- import subprocess
- import sys
- OPENSUBTITLES_USER = "{{ opensubtitles_user }}"
- OPENSUBTITLES_PASS = "{{ opensubtitles_pass }}"
- TMP_FOLDER = "/tmp"
- EXT_SRT = ".srt"
- EXT_MKV = ".mkv"
- EXT_VIDEOS = [".avi", ".m4v", ".mp4", ".mkv"]
- def download_subtitles(src_path):
- subprocess.check_call([
- "subliminal",
- "--opensubtitles", OPENSUBTITLES_USER, OPENSUBTITLES_PASS,
- "download",
- "--language", "en", # "--language", "fr",
- "--age", "2w",
- "--min-score", "42",
- src_path
- ])
- def make_ffprobe_args(src_path):
- return [
- "ffprobe",
- "-i", src_path,
- "-show_entries", "format=duration",
- "-v", "quiet",
- "-of", "csv=p=0"
- ]
- def make_ffmpeg_args(src_path, dst_path):
- return [
- "ffmpeg",
- "-y", # overwrite if target exists
- "-loglevel", "warning", # don't spam
- "-stats", # display stats when processing
- "-i", src_path,
- "-map_chapters", "-1", # remove DVD chapter infos (buggy)
- "-movflags", "+faststart", # put metadata at the beginning of the file
- "-flags", "+global_header", # (seems recommended)
- "-map", "0", # convert all streams
- "-dn", # except data streams (unsupported by mkv)
- "-c", "copy", # by default, just copy
- "-c:s", "srt", # for subtitles, convert to SRT
- "-c:a", "aac", # for audio, convert to AAC
- "-c:v:0", "libx264", # for the first video stream, use x264
- "-maxrate", "3M", # no more than 3Mbps
- "-bufsize", "3M", # same lookahead
- "-profile:v:0", "main", # be compatible with...
- "-level", "4.0", # ... most devices
- "-tune", "film", # tweak for movies
- "-threads", "3", # don't use all the CPU
- dst_path
- ]
- class Context:
- def __init__(self):
- # not done yet
- self.actions = 1
- def reset(self):
- self.actions = 0
- def did_something(self):
- self.actions += 1
- def done(self):
- return self.actions == 0
- def check_conversion(src_path, dst_path):
- # Newer?
- if os.path.getmtime(dst_path) < os.path.getmtime(src_path):
- print "check (newer)", dst_path
- return False
- # Incomplete?
- try:
- src_output = subprocess.check_output(make_ffprobe_args(src_path))
- dst_output = subprocess.check_output(make_ffprobe_args(dst_path))
- except subprocess.CalledProcessError:
- print "check (fail)", dst_path
- return False
- src_secs = float(src_output)
- dst_secs = float(dst_output)
- # Allow two seconds difference
- if abs(src_secs - dst_secs) > 2:
- print "check (length)", dst_path
- return False
- return True
- def sync_convert(context, src, dst, item, filename, dst_files):
- src_path = os.path.join(src, item)
- converted_filename = filename + EXT_MKV
- tmp_converted_path = os.path.join(TMP_FOLDER, converted_filename)
- dst_path = os.path.join(dst, converted_filename)
- if converted_filename in dst_files:
- dst_files.remove(converted_filename)
- if check_conversion(src_path, dst_path):
- return
- print "convert", src_path
- subprocess.check_call(make_ffmpeg_args(src_path, tmp_converted_path))
- shutil.move(tmp_converted_path, dst_path)
- context.did_something()
- def sync_copy(context, src, dst, item, dst_files):
- src_path = os.path.join(src, item)
- dst_path = os.path.join(dst, item)
- if item in dst_files:
- dst_files.remove(item)
- if filecmp.cmp(src_path, dst_path):
- return
- print "copy", src_path
- shutil.copyfile(src_path, dst_path)
- context.did_something()
- def sync_files(context, src, dst, src_files, dst_files):
- for item in src_files:
- filename, extension = os.path.splitext(item)
- extension = extension.lower()
- if extension == EXT_SRT:
- sync_copy(context, src, dst, item, dst_files)
- elif extension in EXT_VIDEOS:
- sync_convert(context, src, dst, item, filename, dst_files)
- else:
- print "unknown file", os.path.join(src, item)
- for item in dst_files:
- path = os.path.join(dst, item)
- print "remove", path
- os.remove(path)
- context.did_something()
- def sync_folders(context, dst, src_folders, dst_folders):
- src_set = set(src_folders)
- dst_set = set(dst_folders)
- missing = src_set - dst_set
- for folder in missing:
- path = os.path.join(dst, folder)
- print "create folder", path
- os.mkdir(path, 0755)
- context.did_something()
- extras = dst_set - src_set
- for folder in extras:
- path = os.path.join(dst, folder)
- print "remove folder", path
- shutil.rmtree(path)
- context.did_something()
- def convert_folder(context, src, dst):
- # Fetch entries
- (_, src_folders, src_files) = os.walk(src).next()
- (_, dst_folders, dst_files) = os.walk(dst).next()
- src_folders.sort()
- dst_folders.sort()
- src_files.sort()
- dst_files.sort()
- # Add/remove folders
- sync_folders(context, dst, src_folders, dst_folders)
- # Add/remove files
- sync_files(context, src, dst, src_files, dst_files)
- # Recurse
- for folder in src_folders:
- convert_folder(context,
- os.path.join(src, folder),
- os.path.join(dst, folder))
- def already_running():
- cmd = shlex.split("pgrep -u {} -f {}".format(os.getuid(), __file__))
- pids = subprocess.check_output(cmd).strip().split('\n')
- return len(pids) > 1
- def main(argv):
- if len(argv) != 3:
- print "usage:", argv[0], "src", "dst"
- sys.exit(-1)
- if already_running():
- print "already running, exiting"
- sys.exit(-1)
- context = Context()
- while not context.done():
- context.reset()
- download_subtitles(argv[1])
- convert_folder(context, argv[1], argv[2])
- if __name__ == "__main__":
- main(sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement