feat(db): add video pages and up follower snapshot schema

This commit is contained in:
kairee committed 2026-11-01 11:14:36 +08:00
1 parent 707ed7d7d7
commit 4942d1d161
10 files changed
+521 -13

No files matched your search

+103 -2
View File
@@ -229,9 +229,9 @@ class Repository:
conn.execute(
"""
INSERT INTO video(aid, bvid, title, up_mid, up_name, pubdate,
duration, cover, folder_id, fav_time, status,
duration, pages, cover, folder_id, fav_time, status,
source, first_seen, last_sync, attr)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(aid) DO UPDATE SET
bvid = COALESCE(excluded.bvid, video.bvid),
title = CASE
@@ -248,6 +248,7 @@ class Repository:
ELSE video.pubdate
END,
duration = COALESCE(excluded.duration, video.duration),
pages = COALESCE(excluded.pages, video.pages),
cover = COALESCE(excluded.cover, video.cover),
folder_id = CASE
WHEN video.folder_id IS NULL THEN excluded.folder_id
@@ -267,6 +268,7 @@ class Repository:
resource.up_name,
int(resource.pubdate or 0),
resource.duration,
resource.pages,
resource.cover,
folder_id,
resource.fav_time,
@@ -397,6 +399,105 @@ class Repository:
return [dict(row) for row in rows]
return self.db.read(run)
# ------------------------------------------------------- up profile
def apply_up_follower(
self,
*,
mid: int,
name: str | None,
follower: int,
following: int | None = None,
now: int | None = None,
backfilled: bool = False,
) -> int:
"""写入 UP 粉丝快照,并补齐该 UP 尚未记录粉丝数的视频。
``follower_first`` 只在首次插入时写入;``follower_latest`` 每次刷新都会
更新。视频上的 ``up_follower`` 是入库时快照,已有值不会被覆盖。返回本次
补齐的视频数。
"""
now = self._now() if now is None else int(now)
mid = int(mid)
follower = max(0, int(follower))
following_value = None if following is None else max(0, int(following))
backfill_flag = 1 if backfilled else 0
def run(conn: sqlite3.Connection) -> int:
conn.execute(
"""
INSERT INTO up_profile(mid, name, follower_first, following_first,
first_captured_at, follower_latest, last_refreshed)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(mid) DO UPDATE SET
name = COALESCE(NULLIF(excluded.name, ''), up_profile.name),
follower_latest = excluded.follower_latest,
last_refreshed = excluded.last_refreshed
""",
(mid, name, follower, following_value, now, follower, now),
)
cursor = conn.execute(
"""
UPDATE video
SET up_follower = ?, up_follower_at = ?, up_follower_backfilled = ?
WHERE up_mid = ? AND up_follower IS NULL
""",
(follower, now, backfill_flag, mid),
)
return max(cursor.rowcount, 0)
return self.db.write(run)
def get_up_profile(self, mid: int) -> dict[str, Any] | None:
def run(conn: sqlite3.Connection) -> dict[str, Any] | None:
row = conn.execute(
"SELECT * FROM up_profile WHERE mid = ?", (int(mid),)
).fetchone()
return dict(row) if row else None
return self.db.read(run)
def up_mids_missing_follower(self) -> list[dict[str, Any]]:
"""追踪中但仍有视频缺少入库粉丝数的 UP。"""
def run(conn: sqlite3.Connection) -> list[dict[str, Any]]:
rows = conn.execute(
"""
SELECT up_mid, COALESCE(MAX(up_name), '') AS up_name,
COUNT(*) AS missing
FROM video
WHERE status IN ('active', 'unfaved')
AND up_mid IS NOT NULL
AND up_follower IS NULL
GROUP BY up_mid
ORDER BY up_mid
"""
).fetchall()
return [dict(row) for row in rows]
return self.db.read(run)
def up_follower_coverage(self) -> dict[str, int]:
"""返回追踪视频的粉丝快照覆盖情况。"""
def run(conn: sqlite3.Connection) -> dict[str, int]:
row = conn.execute(
"""
SELECT COUNT(*) AS total,
SUM(CASE WHEN up_follower IS NOT NULL THEN 1 ELSE 0 END) AS covered,
COUNT(DISTINCT CASE WHEN up_follower IS NOT NULL
THEN up_mid END) AS ups_covered
FROM video
WHERE status IN ('active', 'unfaved')
AND up_mid IS NOT NULL
"""
).fetchone()
total = int(row["total"] or 0)
covered = int(row["covered"] or 0)
return {
"total": total,
"covered": covered,
"missing": max(0, total - covered),
"ups_covered": int(row["ups_covered"] or 0),
}
return self.db.read(run)
def folder_resources(self, media_id: int) -> list[dict[str, Any]]:
def run(conn: sqlite3.Connection) -> list[dict[str, Any]]:
rows = conn.execute(