vivy: tv: 04
This commit is contained in:
parent
e144906b11
commit
7ed7202602
63
Vivy/04/04.vpy
Normal file
63
Vivy/04/04.vpy
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import vapoursynth as vs
|
||||||
|
|
||||||
|
from lvsfunc.types import Range
|
||||||
|
from lvsfunc.dehardsub import HardsubLine, HardsubSign, HardsubMask, bounded_dehardsub
|
||||||
|
from yt_common import SelfRunner
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
from vivy_common import (VivyConfig, VivySource, antialias, deband, denoise, # noqa: E402
|
||||||
|
finalize, fsrcnnx_rescale, letterbox_edgefix, waka_replace)
|
||||||
|
|
||||||
|
core = vs.core
|
||||||
|
|
||||||
|
|
||||||
|
EPNUM: int = int(os.path.basename(os.path.splitext(__file__)[0]))
|
||||||
|
CONFIG: VivyConfig = VivyConfig(EPNUM)
|
||||||
|
SOURCE: VivySource = VivySource(CONFIG)
|
||||||
|
SIGNS_RU: List[HardsubMask] = [
|
||||||
|
HardsubLine((1278, 3392), ((275, 918), (1356, 112))),
|
||||||
|
HardsubSign((3452, 3572), ((232, 857), (1077, 114)), refframes=3500),
|
||||||
|
HardsubSign((11454, 11489), ((727, 176), (440, 78))),
|
||||||
|
HardsubSign((16803, 16841), ((135, 549), (479, 221))),
|
||||||
|
HardsubLine((29463, 33374), ((275, 890), (1356, 140))),
|
||||||
|
HardsubSign((33950, 34045), ((232, 857), (1077, 114)), refframes=34045),
|
||||||
|
]
|
||||||
|
NOSCALE: List[Range] = []
|
||||||
|
NOAA: List[Range] = []
|
||||||
|
LETTERBOX: List[Range] = [(0, 1151)]
|
||||||
|
WAKA_REPLACE: List[List[Range]] = [
|
||||||
|
[(30119, 30969)],
|
||||||
|
[],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def filter_basic() -> vs.VideoNode:
|
||||||
|
wakas, ref = SOURCE.source()
|
||||||
|
wakas = [w[:33665] + core.std.BlankClip(w, length=21) + w[33665:] for w in wakas]
|
||||||
|
waka = wakas[0]
|
||||||
|
waka, wakas = waka_replace(waka, wakas[1:], WAKA_REPLACE)
|
||||||
|
src = bounded_dehardsub(waka, ref, SIGNS_RU, wakas)
|
||||||
|
return src
|
||||||
|
|
||||||
|
|
||||||
|
def filter() -> vs.VideoNode:
|
||||||
|
src = filter_basic()
|
||||||
|
rescale = fsrcnnx_rescale(src, NOSCALE)
|
||||||
|
den = denoise(rescale)
|
||||||
|
deb = deband(den)
|
||||||
|
aa = antialias(deb, NOAA)
|
||||||
|
edgefix = letterbox_edgefix(aa, LETTERBOX)
|
||||||
|
final = finalize(edgefix)
|
||||||
|
final.set_output()
|
||||||
|
return final
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
SelfRunner(CONFIG, filter, filter_basic)
|
||||||
|
else:
|
||||||
|
filter()
|
35
Vivy/vivy_common/config.py
Normal file
35
Vivy/vivy_common/config.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from yt_common import Config, FunimationSource
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
TITLE: str = "Vivy"
|
||||||
|
TITLE_LONG: str = f"{TITLE} - Fluorite Eye's Song"
|
||||||
|
RESOLUTION: int = 1080
|
||||||
|
SUBGROUP: str = "YameteTomete"
|
||||||
|
DATAPATH: str = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
WAKA_RU_FILENAME: str = f"{TITLE}_{{epnum:02d}}_RU_HD.mp4"
|
||||||
|
WAKA_FR_FILENAME: str = f"{TITLE}_{{epnum:02d}}_FR_HD.mp4"
|
||||||
|
WAKA_DE_FILENAME: str = f"{TITLE} - Fluorite Eyes Song E{{epnum:02d}} [1080p][AAC][JapDub][GerSub][Web-DL].mkv"
|
||||||
|
|
||||||
|
|
||||||
|
class VivyConfig(Config):
|
||||||
|
def __init__(self, epnum: int) -> None:
|
||||||
|
super().__init__(
|
||||||
|
epnum,
|
||||||
|
TITLE,
|
||||||
|
TITLE_LONG,
|
||||||
|
RESOLUTION,
|
||||||
|
DATAPATH
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VivySource(FunimationSource):
|
||||||
|
def get_waka_filenames(self) -> List[str]:
|
||||||
|
return [self.config.format_filename(f) for f in [
|
||||||
|
WAKA_RU_FILENAME,
|
||||||
|
WAKA_FR_FILENAME,
|
||||||
|
WAKA_DE_FILENAME,
|
||||||
|
]]
|
43
yt_common/mypy.ini
Normal file
43
yt_common/mypy.ini
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
[mypy]
|
||||||
|
python_version = 3.9
|
||||||
|
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
disallow_any_generics = True
|
||||||
|
|
||||||
|
disallow_untyped_defs = True
|
||||||
|
disallow_incomplete_defs = True
|
||||||
|
check_untyped_defs = True
|
||||||
|
disallow_untyped_decorators = True
|
||||||
|
|
||||||
|
no_implicit_optional = True
|
||||||
|
strict_optional = True
|
||||||
|
|
||||||
|
warn_redundant_casts = True
|
||||||
|
warn_unused_ignores = True
|
||||||
|
warn_no_return = True
|
||||||
|
warn_return_any = True
|
||||||
|
warn_unreachable = True
|
||||||
|
|
||||||
|
show_none_errors = True
|
||||||
|
ignore_errors = False
|
||||||
|
|
||||||
|
allow_untyped_globals = False
|
||||||
|
allow_redefinition = False
|
||||||
|
implicit_reexport = True
|
||||||
|
strict_equality = True
|
||||||
|
|
||||||
|
show_error_context = False
|
||||||
|
show_column_numbers = True
|
||||||
|
show_error_codes = True
|
||||||
|
color_output = True
|
||||||
|
error_summary = True
|
||||||
|
pretty = True
|
||||||
|
|
||||||
|
mypy_path = .
|
||||||
|
|
||||||
|
[mypy-cytoolz.*]
|
||||||
|
ignore_errors = True
|
||||||
|
|
||||||
|
[mypy-vsutil.*]
|
||||||
|
implicit_reexport = True
|
25
yt_common/setup.py
Executable file
25
yt_common/setup.py
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
name = "yt_common"
|
||||||
|
version = "0.0.0"
|
||||||
|
release = "0.0.0"
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name=name,
|
||||||
|
version=release,
|
||||||
|
author="louis",
|
||||||
|
author_email="louis@poweris.moe",
|
||||||
|
description="yametetomete encodes common module",
|
||||||
|
packages=["yt_common"],
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
package_data={
|
||||||
|
'yt_common': ['py.typed'],
|
||||||
|
},
|
||||||
|
python_requires='>=3.8',
|
||||||
|
)
|
3
yt_common/yt_common/__init__.py
Normal file
3
yt_common/yt_common/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .config import Config # noqa: F401
|
||||||
|
from .automation import SelfRunner # noqa: F401
|
||||||
|
from .source import DehardsubFileFinder, FunimationSource # noqa: F401
|
@ -3,71 +3,20 @@ import vapoursynth as vs
|
|||||||
import acsuite
|
import acsuite
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import functools
|
|
||||||
import glob
|
|
||||||
import signal
|
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import vsutil
|
|
||||||
|
|
||||||
from typing import Any, BinaryIO, Callable, List, Optional, Sequence, Tuple, Union, cast
|
from typing import Any, BinaryIO, Callable, List, Optional, Sequence, Union, cast
|
||||||
|
|
||||||
|
from .config import Config
|
||||||
|
from .log import status, warn, error, success
|
||||||
|
from .source import AMAZON_FILENAME, ER_FILENAME, SUBSPLS_FILENAME, FUNI_INTRO, glob_crc
|
||||||
|
|
||||||
core = vs.core
|
core = vs.core
|
||||||
|
|
||||||
TITLE: str = "Vivy"
|
|
||||||
TITLE_LONG: str = f"{TITLE} - Fluorite Eye's Song"
|
|
||||||
RESOLUTION: int = 1080
|
|
||||||
|
|
||||||
SUBGROUP = "YameteTomete"
|
|
||||||
|
|
||||||
SUBSPLS_FILENAME: str = f"[SubsPlease] {TITLE_LONG} - {{epnum:02d}} ({RESOLUTION}p) [$CRC].mkv"
|
|
||||||
ER_FILENAME: str = f"[Erai-raws] {TITLE_LONG} - {{epnum:02d}} [{RESOLUTION}p][Multiple Subtitle].mkv"
|
|
||||||
FUNI_INTRO: int = 289
|
|
||||||
WAKA_FILENAME: str = f"{TITLE}_{{epnum:02d}}_RU_HD.mp4"
|
|
||||||
AMAZON_FILENAME: str = f"{TITLE_LONG} - {{epnum:02d}} (Amazon Prime CBR {RESOLUTION}p).mkv"
|
|
||||||
|
|
||||||
AUDIO_OVERRIDE: str = "audio.mka"
|
AUDIO_OVERRIDE: str = "audio.mka"
|
||||||
AUDIO_FMT: List[str] = [".mka", ".aac", ".wav", ".flac", ".mp3", ".ogg", ".opus", ".m4a"]
|
|
||||||
AFV_FMT: List[str] = [".mkv", ".mp4", ".webm"]
|
|
||||||
AUDIO_CUT: str = "_audiogetter_cut.mka"
|
AUDIO_CUT: str = "_audiogetter_cut.mka"
|
||||||
|
|
||||||
STATUS: str = '\033[94m'
|
|
||||||
WARNING: str = '\033[93m'
|
|
||||||
ERROR: str = '\033[91m'
|
|
||||||
SUCCESS: str = '\033[92m'
|
|
||||||
RESET: str = '\033[0m'
|
|
||||||
|
|
||||||
|
|
||||||
def glob_crc(pattern: str) -> str:
|
|
||||||
res = glob.glob(glob.escape(pattern).replace("$CRC", "*"))
|
|
||||||
if len(res) == 0:
|
|
||||||
raise FileNotFoundError(f"File matching \"{pattern}\" not found!")
|
|
||||||
return res[0]
|
|
||||||
|
|
||||||
|
|
||||||
def get_ref(epnum: int) -> vs.VideoNode:
|
|
||||||
if epnum >= 4:
|
|
||||||
if os.path.isfile(AMAZON_FILENAME.format(epnum=epnum)):
|
|
||||||
return core.ffms2.Source(AMAZON_FILENAME.format(epnum=epnum))
|
|
||||||
else:
|
|
||||||
print(f"{WARNING}Amazon video not found, dehardsubbing with new funi encode{RESET}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
return core.ffms2.Source(glob_crc(SUBSPLS_FILENAME.format(epnum=epnum)))[FUNI_INTRO:]
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not os.path.isfile(ER_FILENAME.format(epnum=epnum)):
|
|
||||||
raise FileNotFoundError("Failed to find valid reference video")
|
|
||||||
|
|
||||||
return core.ffms2.Source(ER_FILENAME.format(epnum=epnum))[FUNI_INTRO:]
|
|
||||||
|
|
||||||
|
|
||||||
def source(epnum: int) -> Tuple[vs.VideoNode, vs.VideoNode]:
|
|
||||||
waka = vsutil.depth(core.ffms2.Source(WAKA_FILENAME.format(epnum=epnum)), 16)
|
|
||||||
ref = vsutil.depth(get_ref(epnum), 16)
|
|
||||||
return waka, ref
|
|
||||||
|
|
||||||
|
|
||||||
def bin_to_plat(binary: str) -> str:
|
def bin_to_plat(binary: str) -> str:
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
@ -77,7 +26,7 @@ def bin_to_plat(binary: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def forward_signal(signum: int, frame: Any, process: Any) -> None:
|
def forward_signal(signum: int, frame: Any, process: Any) -> None:
|
||||||
print(f"{WARNING}Forwarding SIGINT{RESET}")
|
warn("Forwarding SIGINT")
|
||||||
process.send_signal(signum)
|
process.send_signal(signum)
|
||||||
|
|
||||||
|
|
||||||
@ -106,30 +55,31 @@ class Encoder():
|
|||||||
outfile = self.out_template.format(filename=filename)
|
outfile = self.out_template.format(filename=filename)
|
||||||
|
|
||||||
if os.path.isfile(outfile) and not self.force:
|
if os.path.isfile(outfile) and not self.force:
|
||||||
print(f"{WARNING}Existing output detected, skipping encode!{RESET}")
|
warn("Existing output detected, skipping encode!")
|
||||||
return outfile
|
return outfile
|
||||||
|
|
||||||
params = [p.format(frames=end-start, filename=filename) for p in self.params]
|
params = [p.format(frames=end-start, filename=filename) for p in self.params]
|
||||||
|
|
||||||
print(f"{STATUS}--- RUNNING ENCODE ---{RESET}")
|
status("--- RUNNING ENCODE ---")
|
||||||
|
|
||||||
print("+ " + " ".join([self.binary] + list(params)))
|
print("+ " + " ".join([self.binary] + list(params)))
|
||||||
|
|
||||||
process = subprocess.Popen([self.binary] + list(params), stdin=subprocess.PIPE)
|
process = subprocess.Popen([self.binary] + list(params), stdin=subprocess.PIPE)
|
||||||
|
|
||||||
# i want the encoder to handle any ctrl-c so it exits properly
|
# i want the encoder to handle any ctrl-c so it exits properly
|
||||||
forward_to_proc = functools.partial(forward_signal, process=process)
|
# forward_to_proc = functools.partial(forward_signal, process=process)
|
||||||
signal.signal(signal.SIGINT, forward_to_proc)
|
# signal.signal(signal.SIGINT, forward_to_proc)
|
||||||
|
# turns out this didn't work out the way i had hoped
|
||||||
|
|
||||||
clip[start:end].output(cast(BinaryIO, process.stdin), y4m=True)
|
clip[start:end].output(cast(BinaryIO, process.stdin), y4m=True)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
|
|
||||||
# vapoursynth should handle this itself but just in case
|
# vapoursynth should handle this itself but just in case
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
print(f"{ERROR}--- ENCODE FAILED ---{RESET}")
|
error("--- ENCODE FAILED ---")
|
||||||
raise BrokenPipeError(f"Pipe to {self.binary} broken")
|
raise BrokenPipeError(f"Pipe to {self.binary} broken")
|
||||||
|
|
||||||
print(f"{SUCCESS}--- ENCODE FINISHED ---{RESET}")
|
success("--- ENCODE FINISHED ---")
|
||||||
self.cleanup.append(outfile)
|
self.cleanup.append(outfile)
|
||||||
return outfile
|
return outfile
|
||||||
|
|
||||||
@ -153,13 +103,20 @@ class Encoder():
|
|||||||
|
|
||||||
|
|
||||||
class AudioGetter():
|
class AudioGetter():
|
||||||
|
"""
|
||||||
|
TODO: really should modularize this a bit instead of assuming amazon->funi
|
||||||
|
"""
|
||||||
|
config: Config
|
||||||
|
|
||||||
audio_file: str
|
audio_file: str
|
||||||
audio_start: int
|
audio_start: int
|
||||||
video_src: Optional[vs.VideoNode]
|
video_src: Optional[vs.VideoNode]
|
||||||
|
|
||||||
cleanup: List[str]
|
cleanup: List[str]
|
||||||
|
|
||||||
def __init__(self, epnum: int, override: Optional[str] = None) -> None:
|
def __init__(self, config: Config, override: Optional[str] = None) -> None:
|
||||||
|
self.config = config
|
||||||
|
|
||||||
self.audio_start = 0
|
self.audio_start = 0
|
||||||
self.video_src = None
|
self.video_src = None
|
||||||
self.cleanup = []
|
self.cleanup = []
|
||||||
@ -176,30 +133,29 @@ class AudioGetter():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# look for amazon first
|
# look for amazon first
|
||||||
if os.path.isfile(AMAZON_FILENAME.format(epnum=epnum)):
|
if os.path.isfile(self.config.format_filename(AMAZON_FILENAME)):
|
||||||
self.audio_file = AMAZON_FILENAME.format(epnum=epnum)
|
self.audio_file = self.config.format_filename(AMAZON_FILENAME)
|
||||||
self.video_src = core.ffms2.Source(AMAZON_FILENAME.format(epnum=epnum))
|
self.video_src = core.ffms2.Source(self.audio_file)
|
||||||
print(f"{SUCCESS}Found Amazon audio{RESET}")
|
success("Found Amazon audio")
|
||||||
return
|
return
|
||||||
|
|
||||||
# as of Ep4 SubsPlease is using new funi 128kbps aac while erai has 256kbps still
|
# as of Ep4 SubsPlease is using new funi 128kbps aac while erai has 256kbps still
|
||||||
try:
|
try:
|
||||||
if os.path.isfile(ER_FILENAME.format(epnum=epnum)):
|
if os.path.isfile(self.config.format_filename(ER_FILENAME)):
|
||||||
self.audio_file = ER_FILENAME.format(epnum=epnum)
|
self.audio_file = self.config.format_filename(ER_FILENAME)
|
||||||
self.video_src = core.ffms2.Source(ER_FILENAME.format(epnum=epnum))
|
self.video_src = core.ffms2.Source(self.audio_file)
|
||||||
if os.path.isfile(glob_crc(SUBSPLS_FILENAME.format(epnum=epnum))):
|
elif os.path.isfile(glob_crc(self.config.format_filename(SUBSPLS_FILENAME))):
|
||||||
self.audio_file = glob_crc(SUBSPLS_FILENAME.format(epnum=epnum))
|
self.audio_file = glob_crc(self.config.format_filename(SUBSPLS_FILENAME))
|
||||||
self.video_src = core.ffms2.Source(glob_crc(SUBSPLS_FILENAME.format(epnum=epnum)))
|
self.video_src = core.ffms2.Source(self.audio_file)
|
||||||
if (epnum >= 4):
|
warn("Using SubsPlease, audio may be worse than Erai-Raws")
|
||||||
print(f"{WARNING}Using SubsPlease, audio may be worse than Erai-Raws{RESET}")
|
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError()
|
raise FileNotFoundError()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"{ERROR}Could not find audio{RESET}")
|
error("Could not find audio")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.audio_start = FUNI_INTRO
|
self.audio_start = FUNI_INTRO
|
||||||
print(f"{WARNING}No Amazon audio, falling back to Funi{RESET}")
|
warn("No Amazon audio, falling back to Funi")
|
||||||
|
|
||||||
def trim_audio(self, src: vs.VideoNode,
|
def trim_audio(self, src: vs.VideoNode,
|
||||||
trims: Union[acsuite.Trim, List[acsuite.Trim], None] = None) -> str:
|
trims: Union[acsuite.Trim, List[acsuite.Trim], None] = None) -> str:
|
||||||
@ -234,8 +190,8 @@ class AudioGetter():
|
|||||||
|
|
||||||
|
|
||||||
class SelfRunner():
|
class SelfRunner():
|
||||||
|
config: Config
|
||||||
clip: vs.VideoNode
|
clip: vs.VideoNode
|
||||||
epnum: int
|
|
||||||
|
|
||||||
workraw: bool
|
workraw: bool
|
||||||
|
|
||||||
@ -245,13 +201,13 @@ class SelfRunner():
|
|||||||
encoder: Encoder
|
encoder: Encoder
|
||||||
audio: AudioGetter
|
audio: AudioGetter
|
||||||
|
|
||||||
def __init__(self, epnum: int, final_filter: Callable[[], vs.VideoNode],
|
def __init__(self, config: Config, final_filter: Callable[[], vs.VideoNode],
|
||||||
workraw_filter: Optional[Callable[[], vs.VideoNode]] = None) -> None:
|
workraw_filter: Optional[Callable[[], vs.VideoNode]] = None) -> None:
|
||||||
self.epnum = epnum
|
self.config = config
|
||||||
self.video_clean = False
|
self.video_clean = False
|
||||||
self.audio_clean = False
|
self.audio_clean = False
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=f"Encode {TITLE} Episode {epnum:02d}")
|
parser = argparse.ArgumentParser(description=f"Encode {self.config.title} Episode {self.config.epnum:02d}")
|
||||||
if workraw_filter:
|
if workraw_filter:
|
||||||
parser.add_argument("-w", "--workraw", help="Encode workraw, fast x264", action="store_true")
|
parser.add_argument("-w", "--workraw", help="Encode workraw, fast x264", action="store_true")
|
||||||
parser.add_argument("-s", "--start", nargs='?', type=int, help="Start encode at frame START")
|
parser.add_argument("-s", "--start", nargs='?', type=int, help="Start encode at frame START")
|
||||||
@ -260,12 +216,12 @@ class SelfRunner():
|
|||||||
parser.add_argument("-c", "--encoder", type=str, help="Override detected encoder binary")
|
parser.add_argument("-c", "--encoder", type=str, help="Override detected encoder binary")
|
||||||
parser.add_argument("-f", "--force", help="Overwrite existing intermediaries", action="store_true")
|
parser.add_argument("-f", "--force", help="Overwrite existing intermediaries", action="store_true")
|
||||||
parser.add_argument("-a", "--audio", type=str, help="Force audio file")
|
parser.add_argument("-a", "--audio", type=str, help="Force audio file")
|
||||||
parser.add_argument("-x", "--suffix", type=str, default="premux", help="Change the suffix of the mux")
|
parser.add_argument("-x", "--suffix", type=str, help="Change the suffix of the mux")
|
||||||
parser.add_argument("-d", "--no-metadata", help="No extra metadata in premux", action="store_true")
|
parser.add_argument("-d", "--no-metadata", help="No extra metadata in premux", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
self.workraw = args.workraw if workraw_filter else False
|
self.workraw = args.workraw if workraw_filter else False
|
||||||
self.suffix = args.suffix if not self.workraw else "workraw"
|
self.suffix = args.suffix if args.suffix is not None else "workraw" if self.workraw else "premux"
|
||||||
|
|
||||||
self.clip = workraw_filter() if workraw_filter and self.workraw else final_filter()
|
self.clip = workraw_filter() if workraw_filter and self.workraw else final_filter()
|
||||||
|
|
||||||
@ -295,33 +251,34 @@ class SelfRunner():
|
|||||||
if start >= end:
|
if start >= end:
|
||||||
raise ValueError("Start frame must be before end frame!")
|
raise ValueError("Start frame must be before end frame!")
|
||||||
|
|
||||||
self.encoder = Encoder(epnum, settings_path, args.encoder, args.force)
|
self.encoder = Encoder(self.config.epnum, settings_path, args.encoder, args.force)
|
||||||
self.video_file = self.encoder.encode(self.clip, f"{epnum:02d}_{start}_{end}", start, end)
|
self.video_file = self.encoder.encode(self.clip, f"{self.config.epnum:02d}_{start}_{end}", start, end)
|
||||||
|
|
||||||
print(f"{STATUS}--- LOOKING FOR AUDIO ---{RESET}")
|
status("--- LOOKING FOR AUDIO ---")
|
||||||
self.audio = AudioGetter(self.epnum, args.audio)
|
self.audio = AudioGetter(self.config, args.audio)
|
||||||
|
|
||||||
print(f"{STATUS}--- TRIMMING AUDIO ---{RESET}")
|
status("--- TRIMMING AUDIO ---")
|
||||||
self.audio_file = self.audio.trim_audio(self.clip, (start, end))
|
self.audio_file = self.audio.trim_audio(self.clip, (start, end))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(f"{STATUS}--- MUXING FILE ---{RESET}")
|
status("--- MUXING FILE ---")
|
||||||
if self._mux(f"{TITLE.lower()}_{epnum:02d}_{args.suffix}.mkv", not args.no_metadata,
|
if self._mux(f"{self.config.title.lower()}_{self.config.epnum:02d}_{self.suffix}.mkv",
|
||||||
|
not args.no_metadata,
|
||||||
not args.no_metadata and start == 0 and end == self.clip.num_frames) != 0:
|
not args.no_metadata and start == 0 and end == self.clip.num_frames) != 0:
|
||||||
raise Exception("mkvmerge failed")
|
raise Exception("mkvmerge failed")
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f"{ERROR}--- MUXING FAILED ---{RESET}")
|
error("--- MUXING FAILED ---")
|
||||||
self.audio.do_cleanup()
|
self.audio.do_cleanup()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
print(f"{SUCCESS}--- MUXING SUCCESSFUL ---{RESET}")
|
success("--- MUXING SUCCESSFUL ---")
|
||||||
|
|
||||||
self.audio.do_cleanup()
|
self.audio.do_cleanup()
|
||||||
|
|
||||||
if not args.keep:
|
if not args.keep:
|
||||||
self.encoder.do_cleanup()
|
self.encoder.do_cleanup()
|
||||||
|
|
||||||
print(f"{SUCCESS}--- ENCODE COMPLETE ---{RESET}")
|
success("--- ENCODE COMPLETE ---")
|
||||||
|
|
||||||
def _mux(self, name: str, metadata: bool = True, chapters: bool = True) -> int:
|
def _mux(self, name: str, metadata: bool = True, chapters: bool = True) -> int:
|
||||||
mkvtoolnix_args = [
|
mkvtoolnix_args = [
|
||||||
@ -337,11 +294,11 @@ class SelfRunner():
|
|||||||
]
|
]
|
||||||
if metadata:
|
if metadata:
|
||||||
mkvtoolnix_args += [
|
mkvtoolnix_args += [
|
||||||
"--title", f"[{SUBGROUP}] {TITLE_LONG} - {self.epnum:02d}",
|
"--title", f"[{self.config.subgroup}] {self.config.title_long} - {self.config.epnum:02d}",
|
||||||
]
|
]
|
||||||
|
|
||||||
if chapters:
|
if chapters:
|
||||||
chap = [f for f in ["{self.epnum:02d}.xml", "chapters.xml"] if os.path.isfile(f)]
|
chap = [f for f in [f"{self.config.epnum:02d}.xml", "chapters.xml"] if os.path.isfile(f)]
|
||||||
if len(chap) != 0:
|
if len(chap) != 0:
|
||||||
mkvtoolnix_args += [
|
mkvtoolnix_args += [
|
||||||
"--chapters", chap[0],
|
"--chapters", chap[0],
|
18
yt_common/yt_common/config.py
Normal file
18
yt_common/yt_common/config.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
class Config():
|
||||||
|
epnum: int
|
||||||
|
title: str
|
||||||
|
title_long: str
|
||||||
|
resolution: int
|
||||||
|
datapath: str
|
||||||
|
subgroup: str
|
||||||
|
|
||||||
|
def __init__(self, epnum: int, title: str, title_long: str, resolution: int, datapath: str) -> None:
|
||||||
|
self.epnum = epnum
|
||||||
|
self.title = title
|
||||||
|
self.title_long = title_long
|
||||||
|
self.resolution = resolution
|
||||||
|
self.datapath = datapath
|
||||||
|
|
||||||
|
def format_filename(self, filename: str) -> str:
|
||||||
|
return filename.format(epnum=self.epnum, title=self.title,
|
||||||
|
title_long=self.title_long, resolution=self.resolution)
|
23
yt_common/yt_common/log.py
Normal file
23
yt_common/yt_common/log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# TODO: real logging shit not this jank-ass crap
|
||||||
|
|
||||||
|
STATUS: str = '\033[94m'
|
||||||
|
WARNING: str = '\033[93m'
|
||||||
|
ERROR: str = '\033[91m'
|
||||||
|
SUCCESS: str = '\033[92m'
|
||||||
|
RESET: str = '\033[0m'
|
||||||
|
|
||||||
|
|
||||||
|
def status(s: str) -> None:
|
||||||
|
print(f"{STATUS}{s}{RESET}")
|
||||||
|
|
||||||
|
|
||||||
|
def warn(s: str) -> None:
|
||||||
|
print(f"{WARNING}{s}{RESET}")
|
||||||
|
|
||||||
|
|
||||||
|
def error(s: str) -> None:
|
||||||
|
print(f"{ERROR}{s}{RESET}")
|
||||||
|
|
||||||
|
|
||||||
|
def success(s: str) -> None:
|
||||||
|
print(f"{SUCCESS}{s}{RESET}")
|
0
yt_common/yt_common/py.typed
Normal file
0
yt_common/yt_common/py.typed
Normal file
72
yt_common/yt_common/source.py
Normal file
72
yt_common/yt_common/source.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import vapoursynth as vs
|
||||||
|
|
||||||
|
import vsutil
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from .config import Config
|
||||||
|
from .log import warn, error, success
|
||||||
|
|
||||||
|
core = vs.core
|
||||||
|
|
||||||
|
SUBSPLS_FILENAME: str = "[SubsPlease] {title_long} - {epnum:02d} ({resolution}p) [$CRC].mkv"
|
||||||
|
ER_FILENAME: str = "[Erai-raws] {title_long} - {epnum:02d} [v0][{resolution}p].mkv"
|
||||||
|
FUNI_INTRO: int = 289
|
||||||
|
AMAZON_FILENAME: str = "{title_long} - {epnum:02d} (Amazon Prime CBR {resolution}p).mkv"
|
||||||
|
|
||||||
|
|
||||||
|
def glob_crc(pattern: str) -> str:
|
||||||
|
res = glob.glob(glob.escape(pattern).replace("$CRC", "*"))
|
||||||
|
if len(res) == 0:
|
||||||
|
raise FileNotFoundError(f"File matching \"{pattern}\" not found!")
|
||||||
|
return res[0]
|
||||||
|
|
||||||
|
|
||||||
|
class DehardsubFileFinder(ABC):
|
||||||
|
config: Config
|
||||||
|
|
||||||
|
def __init__(self, config: Config) -> None:
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_waka_filenames(self) -> List[str]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_ref(self) -> vs.VideoNode:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def source(self) -> Tuple[List[vs.VideoNode], vs.VideoNode]:
|
||||||
|
wakas = [vsutil.depth(core.ffms2.Source(self.config.format_filename(f)), 16)
|
||||||
|
for f in self.get_waka_filenames()]
|
||||||
|
ref = vsutil.depth(self.get_ref(), 16)
|
||||||
|
return wakas, ref
|
||||||
|
|
||||||
|
|
||||||
|
class FunimationSource(DehardsubFileFinder):
|
||||||
|
def get_amazon(self) -> vs.VideoNode:
|
||||||
|
if not os.path.isfile(self.config.format_filename(AMAZON_FILENAME)):
|
||||||
|
warn("Amazon not found, falling back to Funimation")
|
||||||
|
raise FileNotFoundError()
|
||||||
|
success("Found Amazon video")
|
||||||
|
return core.ffms2.Source(self.config.format_filename(AMAZON_FILENAME))
|
||||||
|
|
||||||
|
def get_funi_filename(self) -> str:
|
||||||
|
if os.path.isfile(self.config.format_filename(ER_FILENAME)):
|
||||||
|
return self.config.format_filename(ER_FILENAME)
|
||||||
|
|
||||||
|
error("Erai-raws not found, falling back to SubsPlease")
|
||||||
|
return glob_crc(self.config.format_filename(SUBSPLS_FILENAME))
|
||||||
|
|
||||||
|
def get_funi(self) -> vs.VideoNode:
|
||||||
|
return core.ffms2.Source(self.get_funi_filename())[FUNI_INTRO:]
|
||||||
|
|
||||||
|
def get_ref(self) -> vs.VideoNode:
|
||||||
|
try:
|
||||||
|
return self.get_amazon()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return self.get_funi()
|
Loading…
x
Reference in New Issue
Block a user