diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bf92c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +bin +include +lib +lib64 +pyvenv.cfg +share +**/@eaDir +**/__pycache__ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bf92c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +bin +include +lib +lib64 +pyvenv.cfg +share +**/@eaDir +**/__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3698f12 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Video Converter + +In Synology NAS, a lot of video types are not natively supported by the system. In order to solve the compatibility issue, I create this project to automatically traverse the video files under the given directory and invoking the `ffmpeg` framework to convert the video files to the targeting format (mp4). + +Also the `ffmpeg` command passes the parameter `-qscale 0` which does not lose any video quality. + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bf92c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +bin +include +lib +lib64 +pyvenv.cfg +share +**/@eaDir +**/__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3698f12 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Video Converter + +In Synology NAS, a lot of video types are not natively supported by the system. In order to solve the compatibility issue, I create this project to automatically traverse the video files under the given directory and invoking the `ffmpeg` framework to convert the video files to the targeting format (mp4). + +Also the `ffmpeg` command passes the parameter `-qscale 0` which does not lose any video quality. + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..feb6ded --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ffmpeg==1.4 +ffprobe==0.5 +future==0.18.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bf92c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +bin +include +lib +lib64 +pyvenv.cfg +share +**/@eaDir +**/__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3698f12 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Video Converter + +In Synology NAS, a lot of video types are not natively supported by the system. In order to solve the compatibility issue, I create this project to automatically traverse the video files under the given directory and invoking the `ffmpeg` framework to convert the video files to the targeting format (mp4). + +Also the `ffmpeg` command passes the parameter `-qscale 0` which does not lose any video quality. + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..feb6ded --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ffmpeg==1.4 +ffprobe==0.5 +future==0.18.2 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..83b0659 --- /dev/null +++ b/src/main.py @@ -0,0 +1,89 @@ +import os +import glob +import ffmpeg +from subprocess import PIPE, run, Popen +import re +import threading +import time + +# Global variable to store the movie file being processed +inprogress_file = "" + + +def shell_command(command): + process = Popen(command, stdout=PIPE, stderr=PIPE, + universal_newlines=True, shell=True) + return process.communicate() + + +def is_video_unsupported(file: str): + output = shell_command("ffmpeg -i '"+file + "' -f ffmetadata") + video_metadata_search = re.search('Video:(.*)\n', output[1], re.IGNORECASE) + video_metadata = "" + if video_metadata_search: + video_metadata = video_metadata_search.group(1) + if(video_metadata.find("progressive") != -1): + return True + return False + + +def get_file_size(file: str): + if not os.path.exists(file): + return "N/A", 0 + total_bytes = os.path.getsize(file) + total_kbs = int(total_bytes / 1024) + total_mbs = int(total_kbs / 1024) + total_gbs = int(total_mbs / 1024) + return "{}G{}M".format((total_gbs % 1024), (total_mbs % 1024)), total_bytes + + +def retrieve_unsupported_videos(path): + files = glob.glob(path + '/**/*.mp4', recursive=True) + unsupported_videos = [] + for file in files: + if file.find("@eaDir") == -1 and is_video_unsupported(file): + unsupported_videos.append(file) + return unsupported_videos + + +def convert_movie_to_mp4(file: str): + temp_file = file + "_inprogress.mp4" + global inprogress_file + inprogress_file = temp_file + output = shell_command("ffmpeg -i '"+file+"' -qscale 0 '"+temp_file+"'") + if os.path.exists(temp_file) and not is_video_unsupported(temp_file): + original_size, _ = get_file_size(file) + new_size, _ = get_file_size(temp_file) + print("Succeeds to convert the {} file to mp4, size changed from {} to {}".format( + file, original_size, new_size)) + try: + os.remove(file) + os.rename(temp_file, file) + except OSError as e: + print("Error: %s : %s" % (file, e.strerror)) + return False + return True + else: + print("Fails to convert the {} file to mp4 due to the error {}".format( + file, output)) + return False + + +def stat_tracker(): + while True: + global inprogress_file + if inprogress_file != "" and os.path.exists(inprogress_file): + file = inprogress_file.replace("_inprogress.mp4", "") + original_readable_size, original_raw = get_file_size(file) + new_readable_size, new_raw = get_file_size(inprogress_file) + print("Converting {}: {:.0%} - ({} / {})".format(file, (new_raw / + original_raw), new_readable_size, original_readable_size)) + time.sleep(175) + time.sleep(5) + + +if __name__ == "__main__": + threading.Thread(target=stat_tracker, daemon=True).start() + movies = retrieve_unsupported_videos("/nas/video") + for movie in movies: + convert_movie_to_mp4(movie)