feat(p3): add discovery, telegram notifications, and one-click favorites
Implement WBI video search with legacy fallback, classification scoring, candidate pool, permanent seen-aid dedupe, blacklist filtering, and batched Telegram notifications. Add write client for fav deal/sort, target-folder selection, insertion position calculation, read-back verification, fav_action locking, retry on failure, ignore and blacklist actions. Wire discovery, candidate cleanup, Telegram long polling, and new discover/candidates CLI commands into the scheduler. Add migration v3 for candidate duration and index stats.
This commit is contained in:
1 parent
6a21b5d905
commit
d971e718e9
31 files changed
+2886
-55
No files matched your search
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""候选分类纯函数测试: uv run python tests/test_classify.py"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
from reaction_board.classify import ( # noqa: E402
|
||||
clean_title,
|
||||
contains_any,
|
||||
normalize_title,
|
||||
parse_duration,
|
||||
passes_required_title,
|
||||
score_item,
|
||||
)
|
||||
|
||||
CLASSIFY = {
|
||||
"require_title": "咻咻满",
|
||||
"self_mid": 37754047,
|
||||
"positive": ["reaction", "re", "反应", "看", "听", "初听", "首次", "点评", "锐评", "路人", "震惊", "双人"],
|
||||
"charge": ["充电", "加急", "专场"],
|
||||
"negative": ["歌切", "切片", "纯享", "搬运", "完整版"],
|
||||
"duration_boost": {"gt_30min": 1, "gt_60min": 2},
|
||||
}
|
||||
|
||||
|
||||
def test_title_helpers():
|
||||
assert normalize_title("咻咻满 REACTION") == "咻咻满 reaction"
|
||||
assert clean_title('<em class="keyword">咻咻满</em> reaction') == "咻咻满 reaction"
|
||||
assert passes_required_title("【咻咻满】初听", "咻咻满")
|
||||
assert passes_required_title("咻咻满", "咻咻满")
|
||||
assert not passes_required_title("其他 UP 的 reaction", "咻咻满")
|
||||
assert contains_any("咻咻满 REACTION", ["reaction"])
|
||||
assert not contains_any("咻咻满", [])
|
||||
|
||||
|
||||
def test_parse_duration():
|
||||
assert parse_duration("105:5") == 105 * 60 + 5
|
||||
assert parse_duration("1:02:03") == 3723
|
||||
assert parse_duration("15:30") == 930
|
||||
assert parse_duration(600) == 600
|
||||
assert parse_duration("bad") == 0
|
||||
assert parse_duration(None) == 0
|
||||
assert parse_duration(-5) == 0
|
||||
|
||||
|
||||
def test_score_rules():
|
||||
def score(title, up_mid=100, duration=0):
|
||||
return score_item(
|
||||
title=title, up_mid=up_mid,
|
||||
duration_seconds=duration, classify=CLASSIFY,
|
||||
)
|
||||
|
||||
assert score("咻咻满 reaction", duration=60) == 2
|
||||
assert score("咻咻满 充电", duration=60) == 2
|
||||
assert score("咻咻满 reaction 充电", duration=60) == 4
|
||||
assert score("咻咻满 reaction", duration=61 * 60) == 4
|
||||
assert score("咻咻满 reaction", duration=31 * 60) == 3
|
||||
assert score("咻咻满 reaction", duration=30 * 60) == 2
|
||||
assert score("咻咻满 切片", duration=60) == -5
|
||||
assert score("咻咻满 reaction 切片", duration=60) == -3
|
||||
assert score("咻咻满 reaction", up_mid=37754047, duration=60) == -1
|
||||
assert score("咻咻满", duration=0) == 0
|
||||
assert score("咻咻满", duration=61 * 60) == 2
|
||||
|
||||
|
||||
def main():
|
||||
test_title_helpers()
|
||||
test_parse_duration()
|
||||
test_score_rules()
|
||||
print("OK: 候选标题归一化、时长解析与分类打分测试全部通过")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user