first build

main
Jan Beníček 2024-07-06 20:33:07 +02:00
parent 91a6cdeac0
commit 6a3fde461e
8 changed files with 274 additions and 0 deletions

5
.env.example Normal file
View File

@ -0,0 +1,5 @@
FFMPEG=C://path
FFMPEG_PARAMS='-c:v av1_nvenc -c:a copy -c:s copy -map 0 -strict experimental -preset p7 -tune hq -multipass fullres -rc vbr'
WORKDIR=D://path
SOURCEDIR=E://path
OUTPUTDIR=F://path

1
.pdm-python Normal file
View File

@ -0,0 +1 @@
C:/Users/jan00/Documents/Programovani/Home/Automatic_Video_Transcoder/.venv/Scripts/python.exe

113
pdm.lock Normal file
View File

@ -0,0 +1,113 @@
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
content_hash = "sha256:c7db77dad7558ae5efaaa39b8bd0c5c77303c3a1a73af2a402a658cd9d3fabb5"
[[package]]
name = "annotated-types"
version = "0.7.0"
requires_python = ">=3.8"
summary = "Reusable constraint types to use with typing.Annotated"
groups = ["default"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
[[package]]
name = "pydantic"
version = "2.8.2"
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["default"]
dependencies = [
"annotated-types>=0.4.0",
"pydantic-core==2.20.1",
"typing-extensions>=4.6.1; python_version < \"3.13\"",
]
files = [
{file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
{file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
]
[[package]]
name = "pydantic-core"
version = "2.20.1"
requires_python = ">=3.8"
summary = "Core functionality for Pydantic validation and serialization"
groups = ["default"]
dependencies = [
"typing-extensions!=4.7.0,>=4.6.0",
]
files = [
{file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
{file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
{file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
{file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
{file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
{file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
{file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
]
[[package]]
name = "pydantic-settings"
version = "2.3.4"
requires_python = ">=3.8"
summary = "Settings management using Pydantic"
groups = ["default"]
dependencies = [
"pydantic>=2.7.0",
"python-dotenv>=0.21.0",
]
files = [
{file = "pydantic_settings-2.3.4-py3-none-any.whl", hash = "sha256:11ad8bacb68a045f00e4f862c7a718c8a9ec766aa8fd4c32e39a0594b207b53a"},
{file = "pydantic_settings-2.3.4.tar.gz", hash = "sha256:c5802e3d62b78e82522319bbc9b8f8ffb28ad1c988a99311d04f2a6051fca0a7"},
]
[[package]]
name = "python-dotenv"
version = "1.0.1"
requires_python = ">=3.8"
summary = "Read key-value pairs from a .env file and set them as environment variables"
groups = ["default"]
files = [
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["default"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]

21
pyproject.toml Normal file
View File

@ -0,0 +1,21 @@
[project]
name = "Automatic_Video_Transcoder"
version = "0.1.0"
description = "Default template for PDM package"
authors = [
{name = "Jan Beníček", email = "jan00@benicek.xyz"},
]
dependencies = [
"pydantic-settings>=2.3.4",
"pydantic>=2.8.2",
]
requires-python = "==3.12.*"
readme = "README.md"
license = {text = "MIT"}
[tool.pdm]
distribution = false
[tool.pdm.scripts]
start = "python src/main.py"

18
src/config.py Normal file
View File

@ -0,0 +1,18 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
class Config(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
ffmpeg: str
ffmpeg_params: str
workdir: str
sourcedir: str
outputdir: str
cfg = Config()
def get_config() -> Config:
return Config()

24
src/ffmpegcmd.py Normal file
View File

@ -0,0 +1,24 @@
import subprocess
def generate_command(ffmpeg: str, params: str, source_file: str, output_file: str):
command = [ffmpeg, "-y", "-i", source_file]
command.extend(params.split())
command.append(output_file)
return command
def transcode(command) -> None:
print(command)
subprocess.run(command, check=True)
def ffmpeg(ffmpeg: str, params: str, source_file: str, output_file: str) -> None:
transcode(
command=generate_command(
ffmpeg=ffmpeg,
params=params,
source_file=source_file,
output_file=output_file,
)
)

92
src/main.py Normal file
View File

@ -0,0 +1,92 @@
from config import get_config
from ffmpegcmd import ffmpeg
import os, shutil, time
conf = get_config()
def reset_workdir(workdir: str):
try:
shutil.rmtree(workdir)
except:
print("workdir remove error")
try:
os.mkdir(workdir)
except:
print("workdir create error")
print("Workdir Cleared")
def videofile_test(file: str) -> bool:
if file.endswith(".mkv") or file.endswith(".avi") or file.endswith(".mp4"):
return True
else:
return False
def copy_file(source: str, destination: str):
if os.path.exists(f"{destination}.filepart"):
os.remove(f"{destination}.filepart")
try:
shutil.copy(source, f"{destination}.filepart")
os.rename(f"{destination}.filepart", destination)
except:
time.sleep(10)
copy_file(source=source, destination=destination)
def process_file(source_dir: str, file: str):
outputdir = source_dir.replace(conf.sourcedir, conf.outputdir)
av1file = f"AV1_{file}"
if not os.path.exists(outputdir):
os.makedirs(outputdir) # create dir if not exist
if os.path.exists(os.path.join(outputdir, file)):
print("Output File Exist --> Skipping")
return False
if videofile_test(file=file):
print(
f"Move video file: {os.path.join(source_dir,file)} --> {os.path.join(conf.workdir, file)}"
)
copy_file(os.path.join(source_dir, file), os.path.join(conf.workdir, file))
print(f"Start transcode File: {file} --> {av1file}")
ffmpeg(
ffmpeg=conf.ffmpeg,
params=conf.ffmpeg_params,
source_file=os.path.join(conf.workdir, file),
output_file=os.path.join(conf.workdir, av1file),
)
print(
f"Move video file: {os.path.join(conf.workdir, av1file)} --> {os.path.join(outputdir, file)}"
)
copy_file(os.path.join(conf.workdir, av1file), os.path.join(outputdir, file))
os.remove(os.path.join(conf.workdir, av1file))
os.remove(os.path.join(conf.workdir, file))
return True
else:
print(
f"Move non video file: {os.path.join(source_dir, file)} --> {os.path.join(outputdir, file)}"
)
copy_file(os.path.join(source_dir, file), os.path.join(outputdir, file))
return False
reset_workdir(workdir=conf.workdir)
for root, _, files in os.walk(conf.sourcedir):
for file in files:
print("--------------------")
print(f"Process file: {os.path.join(root, file)}")
if process_file(root, file):
print("Waiting for terminate script")
time.sleep(10) # testing sleep for stop script time
print("End of waiting for terminating script --> Continuing")

0
tests/__init__.py Normal file
View File