From 90e9e624ccafe3cdbafda3dca503301e60b1f24e Mon Sep 17 00:00:00 2001 From: yajrendrag Date: Wed, 4 Dec 2024 01:09:38 -0700 Subject: [PATCH] initial release [0.0.1] --- source/split_mkv/.gitignore | 7 + source/split_mkv/README.md | 9 + source/split_mkv/changelog.md | 3 + source/split_mkv/description.md | 32 +++ source/split_mkv/icon.png | Bin 0 -> 18141 bytes source/split_mkv/info.json | 19 ++ source/split_mkv/init.d/install_mkvtools.sh | 10 + source/split_mkv/plugin.py | 215 ++++++++++++++++++++ source/split_mkv/requirements.txt | 1 + 9 files changed, 296 insertions(+) create mode 100644 source/split_mkv/.gitignore create mode 100644 source/split_mkv/README.md create mode 100644 source/split_mkv/changelog.md create mode 100644 source/split_mkv/description.md create mode 100644 source/split_mkv/icon.png create mode 100644 source/split_mkv/info.json create mode 100644 source/split_mkv/init.d/install_mkvtools.sh create mode 100644 source/split_mkv/plugin.py create mode 100644 source/split_mkv/requirements.txt diff --git a/source/split_mkv/.gitignore b/source/split_mkv/.gitignore new file mode 100644 index 000000000..6f8b2501e --- /dev/null +++ b/source/split_mkv/.gitignore @@ -0,0 +1,7 @@ +**/__pycache__ +*.py[cod] +**/site-packages +settings.json +*.old +*.new +*.testing diff --git a/source/split_mkv/README.md b/source/split_mkv/README.md new file mode 100644 index 000000000..d7966c282 --- /dev/null +++ b/source/split_mkv/README.md @@ -0,0 +1,9 @@ +# Split an MKV file into multiple MKV files based on chapters or time +Plugin for [Unmanic](https://github.com/Unmanic) + +--- + +### Information: + +- [Description](description.md) +- [Changelog](changelog.md) diff --git a/source/split_mkv/changelog.md b/source/split_mkv/changelog.md new file mode 100644 index 000000000..e6369fc5c --- /dev/null +++ b/source/split_mkv/changelog.md @@ -0,0 +1,3 @@ + +**0.0.1** +- initial release diff --git a/source/split_mkv/description.md b/source/split_mkv/description.md new file mode 100644 index 000000000..2e41a3309 --- /dev/null +++ b/source/split_mkv/description.md @@ -0,0 +1,32 @@ +] +--- + +##### Links: + +- [Support](https://unmanic.app/discord) + +--- + +##### Description: + +This plugin operates only on MKV (Mastroska) files and splits the file into multiple constituent MKV files based +on chapter markings or based on a configured time period. The motivation for doing this is when a single MKV file +holdes multiple episodes of a show. The file name is expected to indicate the presence of multiple episodes + +--- + +##### Documentation: + +mkvtoolnix is used to do file splitting. To learn more about this tool see: +- [MKVToolNix Documentation](https://mkvtoolnix.download/docs.html) + +--- + +### Config description: + +#### Split Method +Select Chapters or Time or Combo - Combo tries to use chapter marks but if they are missing it will fall back to a time interval. + +#### Time Period +If Time or Combo is specified as the split method, configure an amount of time per split (episode). Whole minutes only + diff --git a/source/split_mkv/icon.png b/source/split_mkv/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c34822d37a913f20a61334099c35f18824dbc010 GIT binary patch literal 18141 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 DIC^71 literal 0 HcmV?d00001 diff --git a/source/split_mkv/info.json b/source/split_mkv/info.json new file mode 100644 index 000000000..5db9801c2 --- /dev/null +++ b/source/split_mkv/info.json @@ -0,0 +1,19 @@ +{ + "author": "yajrendrag", + "compatibility": [ + 2 + ], + "description": "Splits an MKV file based on chapters or time", + "icon": "https://raw.githubusercontent.com/Unmanic/plugin.encoder_audio_aac/master/icon.png", + "id": "split_mkv", + "name": "Split MKV", + "platform": [ + "all" + ], + "priorities": { + "on_library_management_file_test": 99, + "on_worker_process": 0 + }, + "tags": "MKV,ffmpeg,library file test", + "version": "0.0.4" +} diff --git a/source/split_mkv/init.d/install_mkvtools.sh b/source/split_mkv/init.d/install_mkvtools.sh new file mode 100644 index 000000000..852eb7ed1 --- /dev/null +++ b/source/split_mkv/init.d/install_mkvtools.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Script is executed by the Unmanic container on startup to auto-install dependencies + +if ! command -v mkvmerge &> /dev/null; then + echo "**** Installing mkvtools ****" + apt-get update + apt-get install mkvtoolnix -y +else + echo "**** mkvtoolnix already installed ****" diff --git a/source/split_mkv/plugin.py b/source/split_mkv/plugin.py new file mode 100644 index 000000000..c50cb35dc --- /dev/null +++ b/source/split_mkv/plugin.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" + plugins.__init__.py + + Written by: yajrendrag + Date: 3 Dec 2024, (17:45 PM) + + Copyright: + Copyright (C) 2024 Jay Gardner + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, version 3. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along with this program. + If not, see . + +""" +import logging +import os +import ffmpeg +import hashlib +import shutil +import glob + +from unmanic.libs.unplugins.settings import PluginSettings + +# Configure plugin logger +logger = logging.getLogger("Unmanic.Plugin.split_mkv") + +class Settings(PluginSettings): + settings = { + "split_method": "", + "chapter_time": "", + } + + def __init__(self, *args, **kwargs): + super(Settings, self).__init__(*args, **kwargs) + self.form_settings = { + "split_method": self.__set_split_method_form_settings(), + "chapter_time": self.__set_chapter_time_form_settings(), + } + + def __set_split_method_form_settings(self): + values = { + "label": "Select method of splitting file - chapter marks or time value", + "label": "Enter Choice", + "input_type": "select", + "select_options": [ + { + "value": "chapters", + "label": "Chapter Marks", + }, + { + "value": "time", + "label": "Time Interval", + }, + { + "value": "combo", + "label": "Chapter Marks with fallback of Time Interval", + }, + ], + } + return values + + def __set_chapter_time_form_settings(self): + values = { + "label": "Enter time period in whole minutes upon which to split the file, i.e., each such period will result in another mkv file", + "input_type": "textarea", + } + if self.get_setting('split_method') == 'chapters': + values["display"] = 'hidden' + return values + + +def on_library_management_file_test(data): + """ + Runner function - enables additional actions during the library management file tests. + + The 'data' object argument includes: + path - String containing the full path to the file being tested. + issues - List of currently found issues for not processing the file. + add_file_to_pending_tasks - Boolean, is the file currently marked to be added to the queue for processing. + + :param data: + :return: + + """ + # Get the path to the file + abspath = data.get('path') + + # Configure settings object (maintain compatibility with v1 plugins) + if data.get('library_id'): + settings = Settings(library_id=data.get('library_id')) + else: + settings = Settings() + + split_method = settings.get_setting('split_method') + if split_method != 'chapters': + chapter_time = settings.get_setting('chapter_time') + + if split_method == 'chapters' or split_method == 'combo': + chapters = ffmpeg.probe(f, show_chapters=None)['chapters'] + if chapters: + logger.info("Splitting file '{}' based on presence of '{}' chapters".format(abspath, len(chapters))) + data['add_file_to_pending_tasks'] = True + return data + else: + if split_method != 'combo': + logger.info("No chapters found and split_method = chapters. Aborting") + return data + if split_method == 'combo' or split_method == 'time': + logger.info("Splitting file '{}' based on chapter times of '{}' minutes".format(abspath, chapter_time)) + data['add_file_to_pending_tasks'] = True + return data + +def on_worker_process(data): + """ + Runner function - enables additional configured processing jobs during the worker stages of a task. + + The 'data' object argument includes: + exec_command - A command that Unmanic should execute. Can be empty. + command_progress_parser - A function that Unmanic can use to parse the STDOUT of the command to collect progress stats. Can be empty. + file_in - The source file to be processed by the command. + file_out - The destination that the command should output (may be the same as the file_in if necessary). + original_file_path - The absolute path to the original file. + repeat - Boolean, should this runner be executed again once completed with the same variables. + + :param data: + :return: + + """ + # Default to no FFMPEG command required. This prevents the FFMPEG command from running if it is not required + data['exec_command'] = [] + data['repeat'] = False + + # Configure settings object (maintain compatibility with v1 plugins) + if data.get('library_id'): + settings = Settings(library_id=data.get('library_id')) + else: + settings = Settings() + + # Get the path to the file + abspath = data.get('file_in') + outpath = data.get('file_out') + srcpath = data.get('original_file_path') + + split_method = settings.get_setting('split_method') + if split_method != 'chapters': + chapter_time = settings.get_setting('chapter_time') + + if split_method == 'chapters' or split_method == 'combo': + chapters = ffmpeg.probe(abspath, show_chapters=None)['chapters'] + if chapters: + logger.info("Splitting file '{}' based on presence of '{}' chapters".format(abspath, len(chapters))) + else: + if split_method != 'combo': + logger.info("No chapters found and split_method = chapters. Aborting") + return data + if split_method == 'combo' or split_method == 'time': + logger.info("Splitting file '{}' based on chapter times of '{}' minutes".format(abspath, chapter_time)) + + # Construct command + split_hash = hashlib.md5(os.path.basename(srcpath).encode('utf8')).hexdigest() + tmp_dir = os.path.join('/tmp/unmanic/', '{}'.format(split_hash)) + '/' + split_base = os.path.split(srcpath)[1] + split_base_noext = os.path.splitext(split_base)[0] + '.split' + sfx = os.path.splitext(split_base)[1] + + data['exec_command'] = ['mkvmerge'] + if split_method == 'chapters' or (split_method == 'combo' and chapters): + data['exec_command'] += ['-o', tmp_dir + split_base_noext + sfx, '--split', 'chapters:all', abspath] + return data + if split_method == 'combo' or split_method == 'time': + split_time = str(60 * int(chapter_time)) + 's' + data['exec_command'] += ['-o', tmp_dir + split_base_noext + sfx, '--split', split_time, abspath] + return data + +def on_postprocessor_task_results(data): + """ + Runner function - provides a means for additional postprocessor functions based on the task success. + + The 'data' object argument includes: + final_cache_path - The path to the final cache file that was then used as the source for all destination files. + library_id - The library that the current task is associated with. + task_processing_success - Boolean, did all task processes complete successfully. + file_move_processes_success - Boolean, did all postprocessor movement tasks complete successfully. + destination_files - List containing all file paths created by postprocessor file movements. + source_data - Dictionary containing data pertaining to the original source file. + + :param data: + :return: + """ + + # move files from temp dir in cache to destination dir + logger.info("dest files: '{}'".format(data.get('destination_files'))) + srcpathbase = data.get('source_data')['basename'] + split_hash = hashlib.md5(srcpathbase.encode('utf8')).hexdigest() + tmp_dir = os.path.join('/tmp/unmanic/', '{}'.format(split_hash)) + '/' + dest_file = data.get('destination_files')[0] + dest_dir = os.path.split(dest_file)[0] + '/' + for f in glob.glob(tmp_dir + "/*.mkv"): + shutil.copy2(f, dest_dir) + + # remove temp files and directory + for f in glob.glob(tmp_dir + "/*.mkv"): + os.remove(f) + shutil.rmtree(tmp_dir) + + return diff --git a/source/split_mkv/requirements.txt b/source/split_mkv/requirements.txt new file mode 100644 index 000000000..333541919 --- /dev/null +++ b/source/split_mkv/requirements.txt @@ -0,0 +1 @@ +ffmpeg-python