Add OpenClaw skills, platform kit, and template docs
Made-with: Cursor
This commit is contained in:
3
content-manager/content_manager/db/__init__.py
Normal file
3
content-manager/content_manager/db/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from content_manager.db.connection import get_conn, init_db
|
||||
|
||||
__all__ = ["get_conn", "init_db"]
|
||||
122
content-manager/content_manager/db/articles_repository.py
Normal file
122
content-manager/content_manager/db/articles_repository.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""articles 表:仅负责 SQL 读写,不含业务规则。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
|
||||
def insert_article(
|
||||
conn: sqlite3.Connection,
|
||||
title: str,
|
||||
body: str,
|
||||
content_html: Optional[str],
|
||||
status: str,
|
||||
source: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
llm_target: Optional[str],
|
||||
extra_json: Optional[str],
|
||||
created_at: int,
|
||||
updated_at: int,
|
||||
) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO articles (
|
||||
title, body, content_html, status, source, account_id, error_msg, llm_target, extra_json,
|
||||
created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
title,
|
||||
body,
|
||||
content_html,
|
||||
status,
|
||||
source,
|
||||
account_id,
|
||||
error_msg,
|
||||
llm_target,
|
||||
extra_json,
|
||||
created_at,
|
||||
updated_at,
|
||||
),
|
||||
)
|
||||
return int(cur.lastrowid)
|
||||
|
||||
|
||||
def update_article_body(
|
||||
conn: sqlite3.Connection,
|
||||
article_id: int,
|
||||
title: str,
|
||||
body: str,
|
||||
updated_at: int,
|
||||
) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE articles SET title = ?, body = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(title, body, updated_at, article_id),
|
||||
)
|
||||
|
||||
|
||||
def fetch_by_id(conn: sqlite3.Connection, article_id: int) -> Optional[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, title, body, content_html, status, source, account_id, error_msg,
|
||||
llm_target, extra_json, created_at, updated_at
|
||||
FROM articles WHERE id = ?
|
||||
""",
|
||||
(article_id,),
|
||||
)
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
def exists_id(conn: sqlite3.Connection, article_id: int) -> bool:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT id FROM articles WHERE id = ?", (article_id,))
|
||||
return cur.fetchone() is not None
|
||||
|
||||
|
||||
def list_recent(conn: sqlite3.Connection, limit: int) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT
|
||||
id, title, body, content_html,
|
||||
status, source, account_id, error_msg, llm_target, extra_json,
|
||||
created_at, updated_at
|
||||
FROM articles ORDER BY updated_at DESC, id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(int(limit),),
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
|
||||
|
||||
def delete_by_id(conn: sqlite3.Connection, article_id: int) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM articles WHERE id = ?", (article_id,))
|
||||
return int(cur.rowcount)
|
||||
|
||||
|
||||
def update_feedback(
|
||||
conn: sqlite3.Connection,
|
||||
article_id: int,
|
||||
status: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
updated_at: int,
|
||||
) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE articles
|
||||
SET status = ?, account_id = ?, error_msg = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(status, account_id, error_msg, updated_at, article_id),
|
||||
)
|
||||
113
content-manager/content_manager/db/connection.py
Normal file
113
content-manager/content_manager/db/connection.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""数据库连接与初始化(建表、文章旧库迁移、提示词种子)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from content_manager.config import get_db_path
|
||||
from content_manager.db.schema import (
|
||||
ARTICLES_TABLE_SQL,
|
||||
IMAGES_TABLE_SQL,
|
||||
PROMPT_TEMPLATE_USAGE_TABLE_SQL,
|
||||
PROMPT_TEMPLATES_TABLE_SQL,
|
||||
VIDEOS_TABLE_SQL,
|
||||
)
|
||||
from content_manager.util.timeutil import now_unix, parse_ts_to_unix
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
def get_conn() -> sqlite3.Connection:
|
||||
return sqlite3.connect(get_db_path())
|
||||
|
||||
|
||||
def _is_legacy_articles_table(cur: sqlite3.Cursor) -> bool:
|
||||
cur.execute("PRAGMA table_info(articles)")
|
||||
rows = cur.fetchall()
|
||||
if not rows:
|
||||
return False
|
||||
for _cid, name, ctype, _nn, _d, _pk in rows:
|
||||
if name == "id" and ctype and ctype.upper() == "INTEGER":
|
||||
return False
|
||||
cur.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='articles'")
|
||||
row = cur.fetchone()
|
||||
if row and row[0] and "TEXT" in row[0] and "id" in row[0]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _migrate_legacy_articles(conn: sqlite3.Connection) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.executescript(
|
||||
"""
|
||||
CREATE TABLE _articles_migrated (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
content_html TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
account_id TEXT,
|
||||
error_msg TEXT,
|
||||
llm_target TEXT,
|
||||
extra_json TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
)
|
||||
cur.execute(
|
||||
"SELECT id, title, content, content_html, status, account_id, error_msg, created_at, updated_at FROM articles"
|
||||
)
|
||||
ts = now_unix()
|
||||
for row in cur.fetchall():
|
||||
_oid, title, content, content_html, status, account_id, error_msg, cat, uat = row
|
||||
body = content or ""
|
||||
ch = content_html if content_html else None
|
||||
cts = parse_ts_to_unix(cat) or ts
|
||||
uts = parse_ts_to_unix(uat) or ts
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO _articles_migrated (
|
||||
title, body, content_html, status, source, account_id, error_msg,
|
||||
created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, 'import', ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
title or "",
|
||||
body,
|
||||
ch,
|
||||
(status or "draft"),
|
||||
account_id,
|
||||
error_msg,
|
||||
cts,
|
||||
uts,
|
||||
),
|
||||
)
|
||||
cur.execute("DROP TABLE articles")
|
||||
cur.execute("ALTER TABLE _articles_migrated RENAME TO articles")
|
||||
conn.commit()
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='articles'")
|
||||
if cur.fetchone():
|
||||
if _is_legacy_articles_table(cur):
|
||||
_migrate_legacy_articles(conn)
|
||||
else:
|
||||
cur.executescript(ARTICLES_TABLE_SQL)
|
||||
cur.executescript(IMAGES_TABLE_SQL)
|
||||
cur.executescript(VIDEOS_TABLE_SQL)
|
||||
cur.executescript(PROMPT_TEMPLATES_TABLE_SQL)
|
||||
cur.executescript(PROMPT_TEMPLATE_USAGE_TABLE_SQL)
|
||||
from content_manager.db.prompts_repository import seed_prompt_templates_if_empty
|
||||
|
||||
seed_prompt_templates_if_empty(conn.cursor())
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
88
content-manager/content_manager/db/images_repository.py
Normal file
88
content-manager/content_manager/db/images_repository.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""images 表:仅保存文件相对路径等元数据。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
|
||||
def insert_row(
|
||||
conn: sqlite3.Connection,
|
||||
file_path: str,
|
||||
title: Optional[str],
|
||||
status: str,
|
||||
source: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
extra_json: Optional[str],
|
||||
created_at: int,
|
||||
updated_at: int,
|
||||
) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO images (
|
||||
file_path, title, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(file_path, title, status, source, account_id, error_msg, extra_json, created_at, updated_at),
|
||||
)
|
||||
return int(cur.lastrowid)
|
||||
|
||||
|
||||
def update_file_path(conn: sqlite3.Connection, image_id: int, file_path: str, updated_at: int) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"UPDATE images SET file_path = ?, updated_at = ? WHERE id = ?",
|
||||
(file_path, updated_at, image_id),
|
||||
)
|
||||
|
||||
|
||||
def fetch_by_id(conn: sqlite3.Connection, image_id: int) -> Optional[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, file_path, title, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
FROM images WHERE id = ?
|
||||
""",
|
||||
(image_id,),
|
||||
)
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
def list_recent(conn: sqlite3.Connection, limit: int) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, file_path, title, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
FROM images ORDER BY updated_at DESC, id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(int(limit),),
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
|
||||
|
||||
def delete_by_id(conn: sqlite3.Connection, image_id: int) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM images WHERE id = ?", (image_id,))
|
||||
return int(cur.rowcount)
|
||||
|
||||
|
||||
def update_feedback(
|
||||
conn: sqlite3.Connection,
|
||||
image_id: int,
|
||||
status: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
updated_at: int,
|
||||
) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE images
|
||||
SET status = ?, account_id = ?, error_msg = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(status, account_id, error_msg, updated_at, image_id),
|
||||
)
|
||||
114
content-manager/content_manager/db/prompts_repository.py
Normal file
114
content-manager/content_manager/db/prompts_repository.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""提示词模板:表内数据访问与种子。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
import sqlite3
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from content_manager.constants import PROMPT_TEMPLATE_SEEDS, PUBLISH_PLATFORM_CN
|
||||
from content_manager.util.timeutil import now_unix
|
||||
|
||||
|
||||
def seed_prompt_templates_if_empty(cur: sqlite3.Cursor) -> None:
|
||||
ts = now_unix()
|
||||
for platform, templates in PROMPT_TEMPLATE_SEEDS.items():
|
||||
cur.execute("SELECT COUNT(*) FROM prompt_templates WHERE platform = ?", (platform,))
|
||||
count = int(cur.fetchone()[0] or 0)
|
||||
if count > 0:
|
||||
continue
|
||||
for idx, tpl in enumerate(templates, start=1):
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO prompt_templates (platform, name, template_text, is_active, created_at, updated_at)
|
||||
VALUES (?, ?, ?, 1, ?, ?)
|
||||
""",
|
||||
(platform, f"{PUBLISH_PLATFORM_CN.get(platform, platform)}模板{idx}", tpl, ts, ts),
|
||||
)
|
||||
|
||||
|
||||
def count_by_platform(cur: sqlite3.Cursor, platform: str) -> int:
|
||||
cur.execute("SELECT COUNT(*) FROM prompt_templates WHERE platform = ?", (platform,))
|
||||
return int(cur.fetchone()[0] or 0)
|
||||
|
||||
|
||||
def fetch_active_templates(conn: sqlite3.Connection, platform: str) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, platform, name, template_text
|
||||
FROM prompt_templates
|
||||
WHERE platform = ? AND is_active = 1
|
||||
ORDER BY id ASC
|
||||
""",
|
||||
(platform,),
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
|
||||
|
||||
def fetch_common_fallback(conn: sqlite3.Connection) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, platform, name, template_text
|
||||
FROM prompt_templates
|
||||
WHERE platform = 'common' AND is_active = 1
|
||||
ORDER BY id ASC
|
||||
"""
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
|
||||
|
||||
def pick_random_template(rows: List[Tuple[Any, ...]]) -> Optional[Dict[str, Any]]:
|
||||
if not rows:
|
||||
return None
|
||||
rid, p, name, text = random.choice(rows)
|
||||
return {"id": int(rid), "platform": p, "name": name, "template_text": text}
|
||||
|
||||
|
||||
def insert_usage(
|
||||
conn: sqlite3.Connection,
|
||||
template_id: int,
|
||||
llm_target: str,
|
||||
platform: str,
|
||||
topic: str,
|
||||
article_id: Optional[int],
|
||||
) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO prompt_template_usage (template_id, llm_target, platform, topic, article_id, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(template_id, llm_target, platform, topic, article_id, now_unix()),
|
||||
)
|
||||
|
||||
|
||||
def list_templates(
|
||||
conn: sqlite3.Connection,
|
||||
platform: Optional[str],
|
||||
limit: int,
|
||||
) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
if platform:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, platform, name, is_active, updated_at
|
||||
FROM prompt_templates
|
||||
WHERE platform = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(platform, int(limit)),
|
||||
)
|
||||
else:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, platform, name, is_active, updated_at
|
||||
FROM prompt_templates
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(int(limit),),
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
73
content-manager/content_manager/db/schema.py
Normal file
73
content-manager/content_manager/db/schema.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""建表 SQL(不含迁移逻辑)。"""
|
||||
|
||||
ARTICLES_TABLE_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS articles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
content_html TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
account_id TEXT,
|
||||
error_msg TEXT,
|
||||
llm_target TEXT,
|
||||
extra_json TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
IMAGES_TABLE_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS images (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_path TEXT NOT NULL,
|
||||
title TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
account_id TEXT,
|
||||
error_msg TEXT,
|
||||
extra_json TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
VIDEOS_TABLE_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS videos (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_path TEXT NOT NULL,
|
||||
title TEXT,
|
||||
duration_ms INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
account_id TEXT,
|
||||
error_msg TEXT,
|
||||
extra_json TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
PROMPT_TEMPLATES_TABLE_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS prompt_templates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
platform TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
template_text TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
PROMPT_TEMPLATE_USAGE_TABLE_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS prompt_template_usage (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
template_id INTEGER NOT NULL,
|
||||
llm_target TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
topic TEXT NOT NULL,
|
||||
article_id INTEGER,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
"""
|
||||
100
content-manager/content_manager/db/videos_repository.py
Normal file
100
content-manager/content_manager/db/videos_repository.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""videos 表:仅保存文件相对路径等元数据。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
|
||||
def insert_row(
|
||||
conn: sqlite3.Connection,
|
||||
file_path: str,
|
||||
title: Optional[str],
|
||||
duration_ms: Optional[int],
|
||||
status: str,
|
||||
source: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
extra_json: Optional[str],
|
||||
created_at: int,
|
||||
updated_at: int,
|
||||
) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO videos (
|
||||
file_path, title, duration_ms, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
file_path,
|
||||
title,
|
||||
duration_ms,
|
||||
status,
|
||||
source,
|
||||
account_id,
|
||||
error_msg,
|
||||
extra_json,
|
||||
created_at,
|
||||
updated_at,
|
||||
),
|
||||
)
|
||||
return int(cur.lastrowid)
|
||||
|
||||
|
||||
def update_file_path(conn: sqlite3.Connection, video_id: int, file_path: str, updated_at: int) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"UPDATE videos SET file_path = ?, updated_at = ? WHERE id = ?",
|
||||
(file_path, updated_at, video_id),
|
||||
)
|
||||
|
||||
|
||||
def fetch_by_id(conn: sqlite3.Connection, video_id: int) -> Optional[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, file_path, title, duration_ms, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
FROM videos WHERE id = ?
|
||||
""",
|
||||
(video_id,),
|
||||
)
|
||||
return cur.fetchone()
|
||||
|
||||
|
||||
def list_recent(conn: sqlite3.Connection, limit: int) -> List[Tuple[Any, ...]]:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, file_path, title, duration_ms, status, source, account_id, error_msg, extra_json, created_at, updated_at
|
||||
FROM videos ORDER BY updated_at DESC, id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(int(limit),),
|
||||
)
|
||||
return list(cur.fetchall())
|
||||
|
||||
|
||||
def delete_by_id(conn: sqlite3.Connection, video_id: int) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM videos WHERE id = ?", (video_id,))
|
||||
return int(cur.rowcount)
|
||||
|
||||
|
||||
def update_feedback(
|
||||
conn: sqlite3.Connection,
|
||||
video_id: int,
|
||||
status: str,
|
||||
account_id: Optional[str],
|
||||
error_msg: Optional[str],
|
||||
updated_at: int,
|
||||
) -> None:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE videos
|
||||
SET status = ?, account_id = ?, error_msg = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(status, account_id, error_msg, updated_at, video_id),
|
||||
)
|
||||
Reference in New Issue
Block a user