From 99908e7349c3edd427835c61fcd7970d71f42030 Mon Sep 17 00:00:00 2001 From: jlw4049 Date: Sun, 21 Jul 2024 23:01:21 -0400 Subject: [PATCH] Initial commit --- .gitignore | 5 + build.py | 61 +++++++++++++ dv_check.py | 234 +++++++++++++++++++++++++++++++++++++++++++++++ poetry.lock | 241 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 21 +++++ 5 files changed, 562 insertions(+) create mode 100644 .gitignore create mode 100644 build.py create mode 100644 dv_check.py create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..975bdd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +pyinstaller_build/ +custom-pyinstaller/ +.venv/ +__pycache__/ +.vscode/ diff --git a/build.py b/build.py new file mode 100644 index 0000000..cddfacf --- /dev/null +++ b/build.py @@ -0,0 +1,61 @@ +from pathlib import Path +from subprocess import run +import os +import shutil +import sys + + +def build_app(): + # Change directory to the project's root directory + project_root = Path(__file__).parent + os.chdir(project_root) + + # Ensure we're in a virtual env, if we are, install dependencies using Poetry + if sys.prefix == sys.base_prefix: + raise Exception("You must activate your virtual environment first") + else: + # Use Poetry to install dependencies + run(["poetry", "install"]) + + # Define the PyInstaller output path + pyinstaller_folder = project_root / "pyinstaller_build" + + # Delete the old build folder if it exists + shutil.rmtree(pyinstaller_folder, ignore_errors=True) + + # Create a folder for the PyInstaller output + pyinstaller_folder.mkdir(exist_ok=True) + + # Define paths before changing directory + tool = project_root / "dv_check.py" + + # Change directory so PyInstaller outputs all of its files in its own folder + os.chdir(pyinstaller_folder) + + # Run PyInstaller command with Poetry's virtual environment + build_job = run( + [ + "poetry", + "run", + "pyinstaller", + "--onefile", + str(tool), + ] + ) + + # Ensure the output of the .exe + success = "Did not complete successfully" + exe_path = project_root / pyinstaller_folder / "dist" / "dv_check.exe" + if exe_path.is_file() and str(build_job.returncode) == "0": + success = f"\nSuccess!\nPath to exe: {str(exe_path)}" + + # Change directory back to the original directory + os.chdir(project_root) + + # Return a success message + return success + + +if __name__ == "__main__": + build = build_app() + print(build) diff --git a/dv_check.py b/dv_check.py new file mode 100644 index 0000000..ecde61c --- /dev/null +++ b/dv_check.py @@ -0,0 +1,234 @@ +import argparse +import json +import subprocess +import sys +from pathlib import Path +from pymediainfo import MediaInfo +from tkinter import Tk, messagebox +from typing import Optional, Tuple + +__version__ = "0.1.0" + + +def exit_print(message: Optional[str], exit_code: Optional[int]) -> None: + print(message if message else "") + sys.exit(exit_code if exit_code else 0) + + +def cli() -> Tuple[Path, Path, Path, bool, bool]: + parser = argparse.ArgumentParser(prog="DV Check") + parser.add_argument("-f", "--ffmpeg", type=str, help="Path to FFMPEG executable.") + parser.add_argument( + "-d", "--dovi-tool", type=str, help="Path to dovi_tool executable." + ) + parser.add_argument( + "-p", + "--prompt", + action="store_true", + help="Prompt user with a GUI prompt if FEL.", + ) + parser.add_argument( + "-c", + "--create-fel-mkv", + action="store_true", + help="Create FEL output mkv if FEL layer exists (input name_el.ext).", + ) + parser.add_argument("input", type=str, help="Path to input file.") + + args = parser.parse_args() + + if not args.input: + exit_print("Program requires an input file", 1) + + if not args.ffmpeg: + exit_print("Program requires FFEMPG", 1) + + if not args.dovi_tool: + exit_print("Program requires dovi_tool", 1) + + return ( + Path(args.input), + Path(args.ffmpeg), + Path(args.dovi_tool), + args.prompt, + args.create_fel_mkv, + ) + + +def detect_el_layer(file_input: Path) -> bool: + if not file_input.exists() or not file_input.is_file(): + exit_print("Input file not found", 1) + + if file_input.suffix not in (".mp4", ".mkv"): + exit_print("Only MP4/MKV input's are supported", 1) + + media_info = MediaInfo.parse(file_input) + try: + video_track = media_info.video_tracks[0] + + hdr_format = video_track.hdr_format + if not hdr_format: + exit_print("No HDR detected", 0) + + if "Dolby Vision" in hdr_format: + hdr_format_settings = video_track.hdr_format_settings + if not hdr_format_settings: + exit_print("No HDR detected", 0) + + if "EL" in hdr_format_settings: + return True + else: + exit_print("EL layer not detected", 0) + else: + exit_print("No dolby vision metadata detected", 0) + + except IndexError: + exit_print("Input has no video track", 1) + + return False + + +def read_rpu( + file_input: Path, ffmpeg_path: Path, dovi_tool_path: Path +) -> Optional[str]: + # output_el_hevc = file_input.with_suffix(".hevc").with_name(f"{file_input.stem}_el") + output_bin = file_input.with_suffix(".bin") + + # Change the working directory to the directory containing the input file + file_input_parent = file_input.parent + + # Define the ffmpeg command + ffmpeg_command = [ + str(ffmpeg_path), + "-ss", + "00:00:10", + "-i", + str(file_input), + "-t", + "00:00:01", + "-map", + "0:v:0", + "-c:v:0", + "copy", + "-bsf:v", + "hevc_mp4toannexb", + "-f", + "hevc", + "-", + "-hide_banner", + "-v", + "panic", + ] + + # Define the dovi_tool command + dovi_tool_command = [dovi_tool_path, "extract-rpu", "-", "-o", str(output_bin)] + + # Run the ffmpeg command and pipe its output to the dovi_tool command + with subprocess.Popen( + ffmpeg_command, cwd=file_input_parent, stdout=subprocess.PIPE + ) as ffmpeg_proc: + with subprocess.Popen( + dovi_tool_command, + cwd=file_input_parent, + stdin=ffmpeg_proc.stdout, + stdout=subprocess.DEVNULL, + ) as dovi_proc: + ffmpeg_proc.stdout.close() # Close the ffmpeg stdout to allow it to receive a SIGPIPE if dovi_proc exits + dovi_proc.communicate() # Wait for the dovi_tool process to complete + + if output_bin.exists(): + rpu_data_cmd = [dovi_tool_path, "info", "-f", "0", "-i", str(output_bin)] + rpu_data_job = subprocess.run(rpu_data_cmd, check=True, capture_output=True) + rpu_stdout_data = json.loads( + rpu_data_job.stdout.decode().split("Parsing RPU file...")[1] + ) + el_type = rpu_stdout_data.get("el_type") + + # clean up files + output_bin.unlink() + + return el_type + + +def prompt_user() -> None: + root = Tk() + root.withdraw() + root.attributes('-topmost', True) + messagebox.showinfo( + "Alert", + "FEL source detected, please switch to a FEL enabled template to process this encode.", + parent=root + ) + root.destroy() + root.mainloop() + + +def create_fel(file_input: Path, ffmpeg_path: Path, dovi_tool_path: Path) -> None: + ffmpeg_command = [ + str(ffmpeg_path), + "-i", + str(file_input), + "-map", + "0:v:0", + "-c:v:0", + "copy", + "-bsf:v", + "hevc_mp4toannexb", + "-f", + "hevc", + "-", + "-hide_banner", + "-v", + "panic", + "-stats", + ] + + el_output_path = file_input.with_name(f"{file_input.stem}_el.hevc") + + dovi_tool_command = [ + dovi_tool_path, + "demux", + "--el-only", + "-", + "-e", + str(el_output_path), + ] + + with subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) as ffmpeg_proc: + with subprocess.Popen( + dovi_tool_command, + stdin=ffmpeg_proc.stdout, + ) as dovi_proc: + ffmpeg_proc.stdout.close() # Close the ffmpeg stdout to allow it to receive a SIGPIPE if dovi_proc exits + dovi_proc.communicate() # Wait for the dovi_tool process to complete + + ffmpeg_mux_command = [ + str(ffmpeg_path), + "-y", + "-i", + str(el_output_path), + "-c", + "copy", + "-f", + "hevc", + str(el_output_path.with_suffix(".mkv")), + ] + subprocess.run(ffmpeg_mux_command) + + +if __name__ == "__main__": + file_input, ffmpeg, dovi_tool, prompt, create_fel_mkv = cli() + detect_el = detect_el_layer(file_input) + if detect_el: + rpu = read_rpu(file_input, ffmpeg, dovi_tool) + if rpu: + rpu = rpu.strip() + print(rpu) + if prompt and rpu == "FEL": + prompt_user() + if create_fel_mkv: + create_fel(file_input, ffmpeg, dovi_tool) + else: + print("No detected EL layer") + + exit_print("", 0) diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..8d3fded --- /dev/null +++ b/poetry.lock @@ -0,0 +1,241 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "altgraph" +version = "0.17.4" +description = "Python graph (network) package" +optional = false +python-versions = "*" +files = [ + {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, + {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, +] + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "macholib" +version = "1.16.3" +description = "Mach-O header analysis and editing" +optional = false +python-versions = "*" +files = [ + {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, + {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, +] + +[package.dependencies] +altgraph = ">=0.17" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pefile" +version = "2023.2.7" +description = "Python PE parsing module" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, + {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pyinstaller" +version = "6.3.0" +description = "PyInstaller bundles a Python application and all its dependencies into a single package." +optional = false +python-versions = "<3.13,>=3.8" +files = [] +develop = true + +[package.dependencies] +altgraph = "*" +macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +packaging = ">=22.0" +pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2021.4" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} +setuptools = ">=42.0.0" + +[package.extras] +completion = ["argcomplete"] +hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] + +[package.source] +type = "directory" +url = "custom-pyinstaller" + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2024.7" +description = "Community maintained hooks for PyInstaller" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"}, + {file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"}, +] + +[package.dependencies] +packaging = ">=22.0" +setuptools = ">=42.0.0" + +[[package]] +name = "pymediainfo" +version = "6.1.0" +description = "A Python wrapper for the mediainfo library." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymediainfo-6.1.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:69d5d3cac056c24a68a1129216aec82a222e804a35f88567c08585e8ae7ef2b3"}, + {file = "pymediainfo-6.1.0-py3-none-win32.whl", hash = "sha256:19e6baeca3db71f12ed568f056de899fd02f380990fb10f43a26c32b67362dd3"}, + {file = "pymediainfo-6.1.0-py3-none-win_amd64.whl", hash = "sha256:f3f6bad666c65ac993dd8d64f45a2b26ba2acd50f9875f74cebb624dbf2f8da0"}, + {file = "pymediainfo-6.1.0.tar.gz", hash = "sha256:186a0b41a94524f0984d085ca6b945c79a254465b7097f2560dc0c04e8d1d8a5"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, +] + +[[package]] +name = "setuptools" +version = "71.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, +] + +[package.extras] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[metadata] +lock-version = "2.0" +python-versions = "3.11.5" +content-hash = "64a77b7a2ce42ced7fff0036bfc4582241955b3da0b1e46f5fcd474300e04328" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f443946 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "dv-tools" +version = "0.1.0" +description = "Dovi vision tools" +authors = ["jlw4049 "] +license = "MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "3.11.5" +pymediainfo = "^6.1.0" + + +[tool.poetry.group.dev.dependencies] +black = "^24.4.2" +pyinstaller = {path = "custom-pyinstaller", develop = true} +pyinstaller-hooks-contrib = "^2024.7" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api"