From 648b8e1a79f5d450b8e372c3965acd2be8aa2f26 Mon Sep 17 00:00:00 2001
From: louis <louis@poweris.moe>
Date: Tue, 13 Apr 2021 13:15:57 -0400
Subject: [PATCH] vivy: tv: 01

---
 .gitignore                          |  4 ++
 Vivy/01/01.vpy                      | 46 ++++++++++++++++++
 Vivy/funi_ac.py                     | 32 +++++++++++++
 Vivy/mypy.ini                       | 41 ++++++++++++++++
 Vivy/vivy_common/__init__.py        |  3 ++
 Vivy/vivy_common/dehardsub.py       | 73 +++++++++++++++++++++++++++++
 Vivy/vivy_common/filter.py          | 49 +++++++++++++++++++
 Vivy/vivy_common/py.typed           |  0
 Vivy/vivy_common/shaders/.gitignore |  1 +
 Vivy/vivy_common/util.py            | 27 +++++++++++
 Vivy/x265-settings                  |  1 +
 11 files changed, 277 insertions(+)
 create mode 100644 Vivy/01/01.vpy
 create mode 100755 Vivy/funi_ac.py
 create mode 100644 Vivy/mypy.ini
 create mode 100644 Vivy/vivy_common/__init__.py
 create mode 100644 Vivy/vivy_common/dehardsub.py
 create mode 100644 Vivy/vivy_common/filter.py
 create mode 100644 Vivy/vivy_common/py.typed
 create mode 100644 Vivy/vivy_common/shaders/.gitignore
 create mode 100644 Vivy/vivy_common/util.py
 create mode 100644 Vivy/x265-settings

diff --git a/.gitignore b/.gitignore
index 194f4ad..3e4b3b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,8 @@
 *.ffindex
 *.png
 *.aac
+*.m4a
+*.h265
 cudnn_data/
 *.webm
 ffmpeg2pass*.log
@@ -12,3 +14,5 @@ ffmpeg2pass*.log
 *.m2ts
 *.lwi
 **/.vspreview/
+**/results/
+**/__pycache__/
diff --git a/Vivy/01/01.vpy b/Vivy/01/01.vpy
new file mode 100644
index 0000000..a66787c
--- /dev/null
+++ b/Vivy/01/01.vpy
@@ -0,0 +1,46 @@
+import vapoursynth as vs
+
+from typing import List
+
+import os
+import sys
+sys.path.append("..")
+
+from vivy_common import (HardsubSign, Range, bounded_dehardsub, antialias, deband, denoise,  # noqa: E402
+                         finalize, fsrcnnx_rescale, source)
+
+core = vs.core
+
+
+EPNUM: int = int(os.path.basename(os.path.splitext(__file__)[0]))
+SIGNS_RU: List[HardsubSign] = [
+    HardsubSign((5283, 5403), ((226, 786), (1214, 102))),
+    HardsubSign((6778, 6888), ((588, 454), (434, 63))),
+    HardsubSign((9017, 9070), ((975, 301), (157, 24))),
+    HardsubSign((22276, 22350), ((480, 75), (959, 66))),
+    HardsubSign((25715, 25904), ((623, 73), (681, 67))),
+    HardsubSign((35223, 36108), ((775, 866), (1138, 211))),
+    HardsubSign((37572, 37666), ((259, 856), (987, 103))),
+]
+CREDITS: List[Range] = [(35151, 37306)]
+PIXELSHIT: List[Range] = [
+    (2534, 2605),
+    (1937, 2021),
+    (6607, 6762),
+    (14160, 14227),
+    (16609, 16674),
+    (20112, 20558),
+]
+NOSCALE: List[Range] = CREDITS + PIXELSHIT
+NOAA: List[Range] = PIXELSHIT
+
+waka, funi = source(EPNUM)
+waka = waka[:37301] + core.std.BlankClip(waka, length=6) + waka[37301:]
+
+src = bounded_dehardsub(waka, funi, SIGNS_RU)
+rescale = fsrcnnx_rescale(src, NOSCALE)
+den = denoise(rescale)
+deb = deband(den)
+aa = antialias(deb, NOAA)
+final = finalize(aa)
+final.set_output()
diff --git a/Vivy/funi_ac.py b/Vivy/funi_ac.py
new file mode 100755
index 0000000..e090747
--- /dev/null
+++ b/Vivy/funi_ac.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+import vapoursynth as vs
+
+import acsuite
+import os
+import sys
+
+from subprocess import DEVNULL, call
+
+from vivy_common.util import FUNI_FILENAME, FUNI_INTRO, glob_crc
+
+EPNUM: int = int(sys.argv[1])
+
+ac = acsuite.AC()
+core = vs.core
+
+funi: str = glob_crc(os.path.join(f"{EPNUM:02d}", FUNI_FILENAME.format(epnum=EPNUM)))
+src = core.ffms2.Source(funi)
+
+if __name__ == "__main__":
+    funi_audio: str = f"{EPNUM:02d}/funi.aac"
+    if not os.path.exists(funi_audio):
+        call(["ffmpeg",
+              "-i", funi,
+              "-vn",
+              "-sn",
+              "-map_metadata", "-1",
+              "-c:a", "copy",
+              funi_audio],
+             stdout=DEVNULL, stderr=DEVNULL
+             )
+    ac.eztrim(src, (FUNI_INTRO, 0), funi_audio, f"{EPNUM:02d}/{EPNUM:02d}_cut.aac")
diff --git a/Vivy/mypy.ini b/Vivy/mypy.ini
new file mode 100644
index 0000000..18f16c0
--- /dev/null
+++ b/Vivy/mypy.ini
@@ -0,0 +1,41 @@
+[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 = False
+strict_equality = True
+
+show_error_context = False
+show_column_numbers = True
+show_error_codes = True
+color_output = True
+error_summary = True
+pretty = True
+
+[mypy-cytoolz.*]
+ignore_errors = True
+
+[mypy-vsutil.*]
+implicit_reexport = True
diff --git a/Vivy/vivy_common/__init__.py b/Vivy/vivy_common/__init__.py
new file mode 100644
index 0000000..d4ca774
--- /dev/null
+++ b/Vivy/vivy_common/__init__.py
@@ -0,0 +1,3 @@
+from .dehardsub import HardsubSign, bounded_dehardsub  # noqa: F401
+from .filter import antialias, deband, denoise, finalize, fsrcnnx_rescale  # noqa: F401
+from .util import Range, glob_crc, source  # noqa: F401
diff --git a/Vivy/vivy_common/dehardsub.py b/Vivy/vivy_common/dehardsub.py
new file mode 100644
index 0000000..364f7fa
--- /dev/null
+++ b/Vivy/vivy_common/dehardsub.py
@@ -0,0 +1,73 @@
+import vapoursynth as vs
+import kagefunc as kgf
+import lvsfunc as lvf
+
+from typing import List, NamedTuple, Optional, Tuple, Union
+
+from .util import Range
+
+core = vs.core
+
+
+class Position(NamedTuple):
+    x: int
+    y: int
+
+
+class Size(NamedTuple):
+    x: int
+    y: int
+
+
+class BoundingBox():
+    pos: Position
+    size: Size
+
+    def __init__(self, pos: Position, size: Size):
+        self.pos = pos
+        self.size = size
+
+
+class HardsubSign():
+    range: Range
+    bound: Optional[BoundingBox]
+    refframe: Optional[int]
+
+    def __init__(self,
+                 range: Range,
+                 bound: Union[BoundingBox, Tuple[Tuple[int, int], Tuple[int, int]], None],
+                 refframe: Optional[int] = None):
+        self.range = range
+        self.refframe = refframe
+        if bound is None:
+            self.bound = None
+        elif isinstance(bound, BoundingBox):
+            self.bound = bound
+        else:
+            self.bound = BoundingBox(Position(bound[0][0], bound[0][1]), Size(bound[1][0], bound[1][1]))
+
+    def get_hardsub_mask(self, hrdsb: vs.VideoNode, ref: vs.VideoNode) -> vs.VideoNode:
+        if self.refframe is not None:
+            return kgf.hardsubmask_fades(hrdsb[self.refframe], ref[self.refframe], highpass=2000)
+        return kgf.hardsubmask_fades(hrdsb, ref, highpass=2000)
+
+    def get_bound_mask(self, ref: vs.VideoNode) -> vs.VideoNode:
+        if self.bound is not None:
+            mask = kgf.squaremask(ref, self.bound.size.x, self.bound.size.y,
+                                  self.bound.pos.x, self.bound.pos.y)
+        else:
+            mask = kgf.squaremask(ref, ref.width, ref.height, 0, 0)
+
+        assert isinstance(mask, vs.VideoNode)
+        return mask
+
+
+def bounded_dehardsub(hrdsb: vs.VideoNode, ref: vs.VideoNode, signs: List[HardsubSign]) -> vs.VideoNode:
+    bound = hrdsb
+    for sign in signs:
+        dhs = core.std.MaskedMerge(hrdsb, ref, sign.get_hardsub_mask(hrdsb, ref))
+        bound = lvf.misc.replace_ranges(bound,
+                                        core.std.MaskedMerge(hrdsb, dhs, sign.get_bound_mask(hrdsb)),
+                                        [sign.range])
+
+    return bound
diff --git a/Vivy/vivy_common/filter.py b/Vivy/vivy_common/filter.py
new file mode 100644
index 0000000..a5191b7
--- /dev/null
+++ b/Vivy/vivy_common/filter.py
@@ -0,0 +1,49 @@
+import vapoursynth as vs
+import kagefunc as kgf
+import lvsfunc as lvf
+import vardefunc as vdf
+
+from mvsfunc import BM3D
+from typing import List, Optional
+
+from .util import Range
+
+import os
+import vsutil
+
+core = vs.core
+
+FSRCNNX = os.path.join(os.path.dirname(__file__), "shaders/FSRCNNX_x2_56-16-4-1.glsl")
+
+
+def fsrcnnx_rescale(src: vs.VideoNode, noscale: Optional[List[Range]] = None) -> vs.VideoNode:
+    def _vdf_fsrcnnx(clip: vs.VideoNode, width: int, height: int) -> vs.VideoNode:
+        clip = core.std.ShufflePlanes([vsutil.depth(clip.resize.Point(vsutil.get_w(864), 864), 16),
+                                       src.resize.Bicubic(vsutil.get_w(864), 864)],
+                                      planes=[0, 1, 2], colorfamily=vs.YUV)
+
+        return vsutil.get_y(vsutil.depth(vdf.fsrcnnx_upscale(clip, width, height, FSRCNNX), 32))
+
+    descale = lvf.scale.descale(src, height=864, upscaler=_vdf_fsrcnnx, kernel=lvf.kernels.Bicubic()) \
+        .resize.Bicubic(format=vs.YUV420P16)
+    return lvf.misc.replace_ranges(descale, src, noscale) if noscale else descale
+
+
+def denoise(clip: vs.VideoNode) -> vs.VideoNode:
+    bm3d = BM3D(clip, sigma=[1.5, 0], depth=16)
+    knl = core.knlm.KNLMeansCL(clip, d=3, a=2, h=0.4, channels="UV", device_type='gpu', device_id=0)
+    return core.std.ShufflePlanes([bm3d, knl], planes=[0, 1, 2], colorfamily=vs.YUV)
+
+
+def deband(clip: vs.VideoNode) -> vs.VideoNode:
+    return clip.neo_f3kdb.Deband(range=18, y=32, cb=24, cr=24, grainy=24, grainc=0, output_depth=16, sample_mode=4)
+
+
+def antialias(clip: vs.VideoNode, noaa: Optional[List[Range]] = None) -> vs.VideoNode:
+    clamp = lvf.aa.nneedi3_clamp(clip)
+    return lvf.misc.replace_ranges(clamp, clip, noaa) if noaa else clamp
+
+
+def finalize(clip: vs.VideoNode) -> vs.VideoNode:
+    grain = kgf.adaptive_grain(clip, 0.1)
+    return vsutil.depth(grain, 10)
diff --git a/Vivy/vivy_common/py.typed b/Vivy/vivy_common/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/Vivy/vivy_common/shaders/.gitignore b/Vivy/vivy_common/shaders/.gitignore
new file mode 100644
index 0000000..e8e3dc8
--- /dev/null
+++ b/Vivy/vivy_common/shaders/.gitignore
@@ -0,0 +1 @@
+FSRCNNX_x2_56-16-4-1.glsl
diff --git a/Vivy/vivy_common/util.py b/Vivy/vivy_common/util.py
new file mode 100644
index 0000000..fb8e7fc
--- /dev/null
+++ b/Vivy/vivy_common/util.py
@@ -0,0 +1,27 @@
+import vapoursynth as vs
+
+import glob
+import vsutil
+
+from typing import Tuple, Union
+
+core = vs.core
+
+Range = Union[int, Tuple[int, int]]
+
+FUNI_FILENAME: str = "[SubsPlease] Vivy - Fluorite Eye's Song - {epnum:02d} (1080p) [$CRC].mkv"
+FUNI_INTRO: int = 289
+WAKA_FILENAME: str = "Vivy_{epnum:02d}_RU_HD.mp4"
+
+
+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 source(epnum: int) -> Tuple[vs.VideoNode, vs.VideoNode]:
+    waka = vsutil.depth(core.ffms2.Source(WAKA_FILENAME.format(epnum=epnum)), 16)
+    funi = vsutil.depth(core.ffms2.Source(glob_crc(FUNI_FILENAME.format(epnum=epnum))), 16)[FUNI_INTRO:]
+    return waka, funi
diff --git a/Vivy/x265-settings b/Vivy/x265-settings
new file mode 100644
index 0000000..9ec68bf
--- /dev/null
+++ b/Vivy/x265-settings
@@ -0,0 +1 @@
+--y4m --input-depth 10 --output-depth 10 --input-csp i420 --profile main10 --colormatrix bt709 --colorprim bt709 --transfer bt709 --preset slower --rc-lookahead 72 --keyint 360 --min-keyint 23 --subme 5 --qcomp 0.7 --crf 15 --aq-mode 3 --aq-strength 0.9 --bframes 16 --psy-rd 0.95 --psy-rdoq 1.8 --rdoq-level 1 --deblock -2:-2 --no-sao