feat(ingest): db-only library ingest and telegram manual ingest

This commit is contained in:
kairee committed 2026-11-01 13:50:03 +08:00
1 parent d2adb65e08
commit 62abbd7ea1
6 files changed
+866 -28

No files matched your search

+89
View File
@@ -24,6 +24,17 @@ class UpsertOutcome:
moved_folder: bool
@dataclass(frozen=True)
class IngestOutcome:
"""入库(候选确认或手动)结果。"""
aid: int
status: str
previous_status: str | None
created: bool
recovered: bool
@dataclass(frozen=True)
class FolderRow:
media_id: int
@@ -399,6 +410,84 @@ class Repository:
return [dict(row) for row in rows]
return self.db.read(run)
def ingest_video(
self,
*,
aid: int,
bvid: str | None,
title: str,
up_mid: int | None,
up_name: str | None,
pubdate: int,
duration: int | None = None,
pages: int | None = None,
cover: str | None = None,
source: str = "manual",
now: int | None = None,
) -> IngestOutcome:
"""把一条视频写入库并置为 ``active``(候选确认 / Telegram 手动入库共用)。
不写 B 站收藏夹、不设置 ``folder_id``;同时删除同 aid 的候选行。
既有的 ``removed`` / ``dropped`` / ``unfaved`` 状态都会被重新激活。
"""
now = self._now() if now is None else int(now)
aid = int(aid)
def run(conn: sqlite3.Connection) -> IngestOutcome:
previous = conn.execute(
"SELECT status FROM video WHERE aid = ?", (aid,)
).fetchone()
previous_status = str(previous["status"]) if previous else None
conn.execute(
"""
INSERT INTO video(aid, bvid, title, up_mid, up_name, pubdate,
duration, pages, cover, folder_id, fav_time, status,
source, first_seen, last_sync, attr)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, 'active', ?, ?, ?, 0)
ON CONFLICT(aid) DO UPDATE SET
bvid = COALESCE(excluded.bvid, video.bvid),
title = CASE
WHEN excluded.title != '' THEN excluded.title
ELSE video.title
END,
up_mid = COALESCE(excluded.up_mid, video.up_mid),
up_name = COALESCE(excluded.up_name, video.up_name),
pubdate = CASE
WHEN excluded.pubdate > 0 THEN excluded.pubdate
ELSE video.pubdate
END,
duration = COALESCE(excluded.duration, video.duration),
pages = COALESCE(excluded.pages, video.pages),
cover = COALESCE(excluded.cover, video.cover),
status = 'active',
source = COALESCE(video.source, excluded.source),
last_sync = excluded.last_sync
""",
(
aid,
bvid,
title or "(无标题)",
up_mid,
up_name,
int(pubdate or 0),
None if duration is None else int(duration),
None if pages is None else int(pages),
cover,
source,
now,
now,
),
)
conn.execute("DELETE FROM candidate WHERE aid = ?", (aid,))
return IngestOutcome(
aid=aid,
status="active",
previous_status=previous_status,
created=previous is None,
recovered=previous_status in {"removed", "dropped", "unfaved"},
)
return self.db.write(run)
# ------------------------------------------------------- up profile
def apply_up_follower(