feat(db): add video pages and up follower snapshot schema
This commit is contained in:
1 parent
707ed7d7d7
commit
4942d1d161
10 files changed
+521
-13
No files matched your search
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user