Ctrl K
ComunidadGuías…

tests/test_portrait_artwork.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""从单人作品的本地封面与高清帧挑头像:只看单人、不在回收站的作品,脸最清楚的那张赢。"""
from __future__ import annotations

import hashlib
import io
import shutil
import sqlite3
import tempfile
import unittest
from pathlib import Path

from PIL import Image, ImageDraw

from peach import portrait_artwork
from peach.avatar_provider import AvatarCandidateCache
from support.ledger import fresh_ledger

PERFORMER = 7792
CO_STAR = 9001
RECORD = {"kind": "performer", "entity_id": PERFORMER, "canonical": "演示女优"}


def picture(width: int, height: int, colour: str) -> bytes:
    """一张有内容的 JPEG;`colour` 决定哈希,几张图在候选缓存里不串。"""
    buffer = io.BytesIO()
    image = Image.new("RGB", (width, height), colour)
    ImageDraw.Draw(image).rectangle((0, 0, width, height // 2), fill="black")
    image.save(buffer, format="JPEG")
    return buffer.getvalue()


class CoverFallbackTests(unittest.TestCase):
    def setUp(self):
        self.folder = Path(tempfile.mkdtemp()).resolve()
        self.addCleanup(shutil.rmtree, self.folder, True)
        self.covers = self.folder / "covers"
        self.posters = self.folder / "posters"
        self.covers.mkdir()
        self.posters.mkdir()
        self.cache = AvatarCandidateCache(self.folder / "candidates")
        self.connection = sqlite3.connect(fresh_ledger(self.folder))
        self.addCleanup(self.connection.close)
        with self.connection:
            for entity_id, name in ((PERFORMER, "演示女优"), (CO_STAR, "共演")):
                self.connection.execute(
                    "INSERT INTO entity(id,kind,canonical_name,normalized_name,created_at,updated_at) "
                    "VALUES(?,'performer',?,?,'t','t')", (entity_id, name, name))

    def add_asset(self, asset_id: int, code: str, *, performers=(PERFORMER,),
                  disposal: str | None = None) -> None:
        with self.connection:
            self.connection.execute(
                "INSERT INTO asset(id,location,path,name,medium,code,disposal) "
                "VALUES(?,'R:',?,?,'video',?,?)",
                (asset_id, f"R:\\media\\{code}.mp4", f"{code}.mp4", code, disposal))
            for entity_id in performers:
                self.connection.execute(
                    "INSERT INTO asset_entity(asset_id,entity_id,role,source) "
                    "VALUES(?,?,'performer','r18:performer')", (asset_id, entity_id))

    def pick(self, probe=None) -> dict | None:
        return portrait_artwork.cover_fallback(
            self.connection, RECORD, self.cache, self.covers, probe=probe, poster_root=self.posters)

    def test_only_solo_works_outside_the_recycle_bin_are_candidates(self):
        self.add_asset(1, "ABW-001")
        (self.covers / "ABW-001.jpg").write_bytes(picture(800, 538, "blue"))
        self.add_asset(2, "ABW-002", performers=(PERFORMER, CO_STAR))
        (self.covers / "ABW-002.jpg").write_bytes(picture(1600, 1076, "green"))
        self.add_asset(3, "ABW-003", disposal="trash")
        (self.covers / "ABW-003.jpg").write_bytes(picture(1600, 1076, "red"))
        chosen = self.pick()
        self.assertIsNotNone(chosen)
        self.assertEqual((chosen["provider"], chosen["external_id"], chosen["source_kind"]),
                         ("cover-fallback", "ABW-001", "single_performer_cover"))
        self.assertEqual(chosen["evidence"], "单人作品 ABW-001 完整封面")
        self.assertEqual((chosen["width"], chosen["height"]), (800, 538))
        self.assertTrue(Path(chosen["object_path"]).is_file())

    def test_small_covers_and_other_kinds_of_entity_give_nothing(self):
        self.add_asset(1, "ABW-001")
        (self.covers / "ABW-001.jpg").write_bytes(picture(300, 200, "blue"))
        self.assertIsNone(self.pick())
        (self.covers / "ABW-001.jpg").write_bytes(picture(800, 538, "blue"))
        self.assertIsNone(portrait_artwork.cover_fallback(
            self.connection, {**RECORD, "kind": "studio"}, self.cache, self.covers))

    def test_the_clearest_face_beats_the_biggest_picture(self):
        self.add_asset(1, "ABW-001")
        cover = picture(1600, 1076, "blue")
        frame = picture(640, 360, "green")
        (self.covers / "ABW-001.jpg").write_bytes(cover)
        (self.posters / "1_0003.jpg").write_bytes(frame)
        faces = {
            hashlib.sha256(cover).hexdigest(): {"face": {"w": 0.05}, "px": [1600, 1076]},
            hashlib.sha256(frame).hexdigest(): {"face": {"w": 0.5}, "px": [640, 360]},
        }
        chosen = self.pick(probe=lambda path: faces[hashlib.sha256(path.read_bytes()).hexdigest()])
        self.assertIsNotNone(chosen)
        self.assertEqual((chosen["provider"], chosen["source_kind"], chosen["face_width"]),
                         ("poster-fallback", "single_performer_frame", 320))
        self.assertEqual(chosen["evidence"], "单人作品 ABW-001 高清帧 1_0003")


if __name__ == "__main__":
    unittest.main()