first build
parent
91a6cdeac0
commit
6a3fde461e
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
C:/Users/jan00/Documents/Programovani/Home/Automatic_Video_Transcoder/.venv/Scripts/python.exe
|
|
@ -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"},
|
||||
]
|
|
@ -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"
|
|
@ -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()
|
|
@ -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,
|
||||
)
|
||||
)
|
|
@ -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")
|
Loading…
Reference in New Issue