diff --git a/Bishounen Tanteidan/01/01.vpy b/Bishounen Tanteidan/01/01.vpy
new file mode 100644
index 0000000..71ae6dc
--- /dev/null
+++ b/Bishounen Tanteidan/01/01.vpy	
@@ -0,0 +1,111 @@
+import vapoursynth as vs
+
+from tanteidan_common import (PrettyConfig, PrettySource, antialias, deband, denoise, finalize, regrain)
+
+from yt_common.automation import SelfRunner
+from yt_common.source import waka_replace
+
+from typing import List
+from lvsfunc.dehardsub import HardsubLineFade, HardsubMask, HardsubSign, bounded_dehardsub
+from lvsfunc.types import Range
+
+import os
+
+
+core = vs.core
+
+
+EPNUM: int = int(os.path.basename(os.path.splitext(__file__)[0]))
+CONFIG: PrettyConfig = PrettyConfig(EPNUM)
+SOURCE: PrettySource = PrettySource(CONFIG)
+
+WAKA_REPLACE: List[List[Range]] = [
+    [(31865, 32653)],
+    [],
+]
+
+SIGNS_RU: List[HardsubMask] = [
+    HardsubLineFade([
+        (6586, 6611),
+        (6813, 6839),
+        (6954, 6971),
+        (7092, 7115),
+        (7242, 7289),
+        (7461, 7511),
+        (7512, 7535),
+        (7697, 7727),
+        (8345, 8371),
+        (8833, 8860),
+        (9950, 10114),
+        (11234, 11266),
+        (11747, 11986),
+        (12113, 12136),
+        (28429, 28477),
+        (28502, 28537),
+    ], ((449, 51), (1015, 127)), refframe=0.75),  # wtf waka fr
+    HardsubSign((0, 167), ((67, 71), (793, 253)), refframes=0),
+    HardsubSign((349, 395), ((1492, 733), (263, 100)), refframes=395),
+    HardsubSign((492, 813), ((132, 322), (664, 207)), refframes=492),
+    HardsubSign((2185, 2239), ((634, 302), (641, 111)), refframes=2239),
+    HardsubSign((6426, 6485), ((435, 374), (1087, 104))),
+    HardsubSign((7728, 7787), ((372, 369), (1239, 109))),
+    HardsubSign((8970, 9034), ((1424, 77), (383, 75)), refframes=9034),
+    HardsubSign((9830, 9886), ((1397, 41), (414, 83)), refframes=9886),
+    HardsubSign((10997, 11044), ((1509, 180), (284, 68)), refframes=11044),
+    HardsubSign((12044, 12112), ((1484, 44), (296, 73)), refframes=12112),
+    HardsubSign((13298, 13368), ((559, 375), (824, 102))),
+    HardsubSign((13441, 13476), ((1464, 656), (338, 67))),
+    HardsubSign((13477, 13518), ((158, 657), (323, 78))),
+    HardsubSign((15577, 15648), ((1296, 806), (457, 103))),
+    HardsubSign((15577, 15648), ((143, 66), (341, 103))),
+    HardsubSign((16351, 16422), ((704, 370), (541, 109))),
+    HardsubSign((16651, 16699), ((1058, 404), (462, 88)), refframes=16651),
+    HardsubSign((17554, 17692), ((732, 100), (393, 80)), refframes=17554),
+    HardsubSign((17554, 17692), ((609, 777), (393, 80)), refframes=17554),
+    HardsubSign((18487, 18638), ((474, 72), (979, 90)), refframes=18615),
+    HardsubSign((19099, 19152), ((667, 372), (609, 104))),
+    HardsubSign((20641, 20676), ((516, 0), (1042, 65))),
+    HardsubSign((21124, 21179), ((681, 378), (601, 94))),
+    HardsubSign((22428, 22451), ((555, 93), (823, 135))),
+    HardsubSign((31793, 31864), ((740, 176), (445, 92))),
+    HardsubSign((31793, 31864), ((352, 617), (1229, 92))),
+    HardsubSign((31793, 31864), ((1355, 794), (329, 92))),
+]
+
+
+AA_WEAK: List[Range] = [
+    (6426, 6485),
+    (7728, 7787),
+    (13298, 13368),
+    (16351, 19152),
+    (20641, 20676),
+    (21124, 21179),
+    (23952, 24023),
+]
+
+
+def filter_basic() -> vs.VideoNode:
+    wakas, ref = SOURCE.source()
+    wakas = [w[0] + w for w in wakas]
+    waka = wakas[0]
+    waka, wakas = waka_replace(waka, wakas[1:], WAKA_REPLACE)
+    src = bounded_dehardsub(waka, ref, SIGNS_RU, wakas)
+    src.set_output(1)
+    return src
+
+
+def filter() -> vs.VideoNode:
+    src = filter_basic()
+    den = denoise(src)
+    deb = deband(den)
+    aa = antialias(deb, weak=AA_WEAK)
+    grain = regrain(aa)
+    final = finalize(grain)
+    final.set_output()
+    return final
+
+
+if __name__ == "__main__":
+    SelfRunner(CONFIG, filter, filter_basic)
+else:
+    filter()
diff --git a/Bishounen Tanteidan/02/02.vpy b/Bishounen Tanteidan/02/02.vpy
new file mode 100644
index 0000000..fe6ede2
--- /dev/null
+++ b/Bishounen Tanteidan/02/02.vpy	
@@ -0,0 +1,109 @@
+import vapoursynth as vs
+
+from tanteidan_common import (PrettyConfig, PrettySource, antialias, deband, denoise, finalize, regrain)
+
+from yt_common.automation import SelfRunner
+from yt_common.source import waka_replace
+
+from typing import List
+
+from lvsfunc.dehardsub import HardsubLineFade, HardsubSignFade, HardsubMask, bounded_dehardsub
+from lvsfunc.types import Range
+
+import os
+
+
+core = vs.core
+
+
+EPNUM: int = int(os.path.basename(os.path.splitext(__file__)[0]))
+CONFIG: PrettyConfig = PrettyConfig(EPNUM)
+SOURCE: PrettySource = PrettySource(CONFIG)
+
+WAKA_REPLACE: List[List[Range]] = [
+    [(31864, 32652)],
+    [],
+]
+
+TITLECARDS: List[Range] = [
+    (0, 35),
+    (2949, 2996),
+    (6470, 6505),
+    (8460, 8495),
+    (8976, 9011),
+    (11058, 11141),
+    (13221, 13268),
+    (15780, 15818),
+    (19494, 19529),
+    (21303, 21338),
+    (24000, 24023),
+    (25207, 25242),
+    (26654, 26683),
+    (30290, 30337),
+]
+
+SIGNS_RU: List[HardsubMask] = [
+    HardsubLineFade([
+    ], ((449, 51), (1015, 127)), refframe=0.75),
+    HardsubSignFade([
+        (318, 347),
+        (4607, 4702),
+        (6506, 6547),
+        (31792, 31863),
+        (3714, 3773),
+        (3774, 3851),
+        (4886, 4933),
+        (5972, 6091),
+        (12060, 12083),
+        (12084, 12113),
+        (18132, 18200),
+        (19446, 19493),
+        (24178, 24196),
+        (24201, 24216),
+        (26768, 26815),
+        (26816, 26857),
+    ]),
+
+]
+
+SIGNS_RU += [HardsubSignFade(tc) for tc in TITLECARDS]
+
+AA_WEAK: List[Range] = [
+    (0, 35),
+    (282, 347),
+    (18069, 18440),
+]
+
+AA_WEAK += TITLECARDS
+
+
+AA_NONE: List[Range] = [
+    (5972, 6091),  # this is heavily stylized and i'm not touching it
+]
+
+
+def filter_basic() -> vs.VideoNode:
+    wakas, ref = SOURCE.source()
+    wakas = [w[0] + w for w in wakas]
+    waka = wakas[0]
+    waka, wakas = waka_replace(waka, wakas[1:], WAKA_REPLACE)
+    src = bounded_dehardsub(waka, ref, SIGNS_RU, wakas)
+    src.set_output(1)
+    return src
+
+
+def filter() -> vs.VideoNode:
+    src = filter_basic()
+    den = denoise(src)
+    deb = deband(den)
+    aa = antialias(deb, weak=AA_WEAK, noaa=AA_NONE)
+    grain = regrain(aa)
+    final = finalize(grain)
+    final.set_output()
+    return final
+
+
+if __name__ == "__main__":
+    SelfRunner(CONFIG, filter, filter_basic)
+else:
+    filter()
diff --git a/Bishounen Tanteidan/mypy.ini b/Bishounen Tanteidan/mypy.ini
new file mode 120000
index 0000000..f79cd28
--- /dev/null
+++ b/Bishounen Tanteidan/mypy.ini	
@@ -0,0 +1 @@
+../yt_common/mypy.ini
\ No newline at end of file
diff --git a/Bishounen Tanteidan/setup.py b/Bishounen Tanteidan/setup.py
new file mode 100755
index 0000000..0b4b685
--- /dev/null
+++ b/Bishounen Tanteidan/setup.py	
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+import setuptools
+
+name = "tanteidan_common"
+version = "0.0.0"
+release = "0.0.0"
+
+setuptools.setup(
+    name=name,
+    version=release,
+    author="louis",
+    author_email="louis@poweris.moe",
+    description="yametetomete pretty boy detective club common module",
+    packages=["tanteidan_common"],
+    classifiers=[
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+    ],
+    package_data={
+        'tenteidan-common': ['py.typed', 'workraw-settings', 'final-settings'],
+    },
+    python_requires='>=3.8',
+)
diff --git a/Bishounen Tanteidan/tanteidan_common/__init__.py b/Bishounen Tanteidan/tanteidan_common/__init__.py
new file mode 100644
index 0000000..36c67d6
--- /dev/null
+++ b/Bishounen Tanteidan/tanteidan_common/__init__.py	
@@ -0,0 +1,2 @@
+from .config import PrettyConfig, PrettySource  # noqa: F401
+from .filter import antialias, deband, denoise, finalize, regrain  # noqa: F401
diff --git a/Bishounen Tanteidan/tanteidan_common/config.py b/Bishounen Tanteidan/tanteidan_common/config.py
new file mode 100644
index 0000000..c592162
--- /dev/null
+++ b/Bishounen Tanteidan/tanteidan_common/config.py	
@@ -0,0 +1,54 @@
+import vapoursynth as vs
+
+from yt_common.config import Config
+from yt_common.logging import log
+from yt_common.source import FunimationSource, AMAZON_FILENAME
+
+import os
+
+from typing import List
+
+TITLE: str = "Tanteidan"
+TITLE_LONG: str = "Bishounen Tanteidan"
+TITLE_ENG: str = "Pretty Boy Detective Club"
+RESOLUTION: int = 1080
+SUBGROUP: str = ""
+DATAPATH: str = os.path.dirname(__file__)
+
+WAKA_RU_FILENAME: str = f"{TITLE_ENG.replace(' ', '')}_{{epnum:02d}}_RU_HD.mp4"
+WAKA_FR_FILENAME: str = f"{TITLE_ENG.replace(' ', '')}_{{epnum:02d}}_FR_HD.mp4"
+WAKA_DE_FILENAME: str = f"{TITLE_ENG} E{{epnum:02d}} [{RESOLUTION}p][AAC][JapDub][GerSub][Web-DL].mkv"
+
+
+core = vs.core
+
+
+class PrettyConfig(Config):
+    def __init__(self, epnum: int) -> None:
+        super().__init__(
+            epnum,
+            TITLE,
+            TITLE_LONG,
+            RESOLUTION,
+            DATAPATH
+        )
+
+
+class PrettySource(FunimationSource):
+    def get_amazon(self) -> vs.VideoNode:
+        # ep1 has good funi video, let's just use that
+        if self.config.epnum < 2:
+            log.success("Funi has good video for this episode, skipping Amazon.")
+            raise FileNotFoundError()
+        if not os.path.isfile(self.config.format_filename(AMAZON_FILENAME)):
+            log.warn("Amazon not found, falling back to Funimation")
+            raise FileNotFoundError()
+        log.success("Found Amazon video")
+        return core.ffms2.Source(self.config.format_filename(AMAZON_FILENAME))
+
+    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,
+        ]]
diff --git a/Bishounen Tanteidan/tanteidan_common/filter.py b/Bishounen Tanteidan/tanteidan_common/filter.py
new file mode 100644
index 0000000..b0cec70
--- /dev/null
+++ b/Bishounen Tanteidan/tanteidan_common/filter.py	
@@ -0,0 +1,68 @@
+import vapoursynth as vs
+
+import vsutil
+
+from G41Fun import MaskedDHA
+from debandshit import f3kbilateral
+from havsfunc import LSFmod
+from lvsfunc.mask import detail_mask
+from lvsfunc.misc import replace_ranges
+from lvsfunc.types import Range
+from mvsfunc import BM3D
+from vardefunc import dumb3kdb
+
+from typing import List, Optional, Sequence, Union
+
+from yt_common.antialiasing import combine_mask, sraa_clamp
+from yt_common.deband import morpho_mask
+
+
+core = vs.core
+
+
+def denoise(clip: vs.VideoNode, sigma: Union[float, Sequence[float]] = 0.75) -> vs.VideoNode:
+    den: vs.VideoNode = BM3D(clip, sigma=sigma)
+    return den
+
+
+def deband(clip: vs.VideoNode) -> vs.VideoNode:
+    """
+    mostly stole this from varde
+    """
+    grad_mask, yrangebig = morpho_mask(clip.dfttest.DFTTest(sigma=14, sigma2=10, sbsize=1, sosize=0)
+                                       .rgvs.RemoveGrain(3))
+    y = vsutil.get_y(clip)
+    mask = detail_mask(y, brz_a=0.05, brz_b=0.03)
+    dumb: vs.VideoNode = dumb3kdb(clip)
+    deband_weak = core.std.MaskedMerge(vsutil.get_y(dumb), y, mask)
+    deband_norm = f3kbilateral(y, y=36)
+    deband_strong = f3kbilateral(y, y=65)
+    deband = core.std.MaskedMerge(deband_strong, deband_norm, grad_mask)
+    deband = core.std.MaskedMerge(deband, deband_weak, yrangebig)
+    deband = core.std.ShufflePlanes([deband, dumb], planes=[0, 1, 2], colorfamily=vs.YUV)
+    return deband
+
+
+def antialias(clip: vs.VideoNode, weak: Optional[List[Range]] = None, noaa: Optional[List[Range]] = None,
+              dehalo: bool = True, sharpen: bool = False) -> vs.VideoNode:
+    def _sraa_pp_sharpdehalo(sraa: vs.VideoNode) -> vs.VideoNode:
+        if sharpen:  # all this really seems to do is make the haloing worse, will not be using!
+            y = LSFmod(vsutil.get_y(sraa), strength=70, Smode=3, edgemode=0, source=vsutil.get_y(clip))
+            sraa = core.std.ShufflePlanes([y, sraa], planes=[0, 1, 2], colorfamily=vs.YUV)
+        sraa = MaskedDHA(sraa, rx=1.7, ry=1.7, darkstr=0, brightstr=0.75) if dehalo else sraa
+        return sraa
+    clamp = sraa_clamp(clip, mask=combine_mask(clip, weak or []), postprocess=_sraa_pp_sharpdehalo)
+    return replace_ranges(clamp, clip, noaa or [])
+
+
+def regrain(clip: vs.VideoNode) -> vs.VideoNode:
+    mask_bright = clip.std.PlaneStats().adg.Mask(10)
+    mask_dark = clip.std.PlaneStats().adg.Mask(25)
+    sgrain = core.std.MaskedMerge(clip, clip.grain.Add(var=0.2, constant=True, seed=393), mask_bright)
+    dgrain = core.std.MaskedMerge(clip, clip.grain.Add(var=0.15, constant=False, seed=393), mask_dark)
+    grain = core.std.MergeDiff(dgrain, clip.std.MakeDiff(sgrain))
+    return grain
+
+
+def finalize(clip: vs.VideoNode) -> vs.VideoNode:
+    return vsutil.depth(clip, 10)
diff --git a/Bishounen Tanteidan/tanteidan_common/final-settings b/Bishounen Tanteidan/tanteidan_common/final-settings
new file mode 100644
index 0000000..2774071
--- /dev/null
+++ b/Bishounen Tanteidan/tanteidan_common/final-settings	
@@ -0,0 +1 @@
+x265 --input - --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 2.0 --psy-rdoq 1.8 --rdoq-level 1 --deblock -2:-2 --no-sao --no-open-gop --frames {frames:d} --output {filename:s}.h265
diff --git a/Bishounen Tanteidan/tanteidan_common/py.typed b/Bishounen Tanteidan/tanteidan_common/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/Bishounen Tanteidan/tanteidan_common/workraw-settings b/Bishounen Tanteidan/tanteidan_common/workraw-settings
new file mode 100644
index 0000000..3750a26
--- /dev/null
+++ b/Bishounen Tanteidan/tanteidan_common/workraw-settings	
@@ -0,0 +1,2 @@
+# these don't have to make sense
+x264 - --demuxer y4m --input-depth 10 --output-depth 10 --colormatrix bt709 --colorprim bt709 --transfer bt709 --preset fast --crf 16 --aq-mode 3 --aq-strength 0.85 --qcomp 0.70 --psy-rd 0.85:0.0 --deblock -1:-1 --frames {frames:d} --output {filename:s}.h264