Merge commit 'bba47d2fe95f318d35a986a17aff2bdbc97ed5e5'

This commit is contained in:
xbh 2025-10-26 22:42:27 +08:00
commit 74494013f9
45 changed files with 19883 additions and 50 deletions

View File

@ -22,27 +22,28 @@ import sys
import os import os
import logging import logging
import argparse import argparse
from pathlib import Path from datetime import datetime, date, timedelta
from datetime import datetime, date
import config import config
# 添加项目路径到 Python 路径 # 添加项目路径到 Python 路径
sys.path.append(os.path.join(os.path.dirname(__file__), 'handlers', 'Rankings')) sys.path.append(os.path.join(os.path.dirname(__file__), 'handlers', 'Rankings'))
from rank_data_scraper import DouyinPlayVVScraper from handlers.Rankings.rank_data_scraper import DouyinPlayVVScraper
# 配置日志的函数 # 配置日志的函数
def setup_logging(): def setup_logging(quiet_mode=False):
"""设置日志配置""" """设置日志配置"""
# 确保logs目录存在 # 确保logs目录存在
import os
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
logs_dir = os.path.join(script_dir, 'handlers', 'Rankings', 'logs') logs_dir = os.path.join(script_dir, 'handlers', 'Rankings', 'logs')
os.makedirs(logs_dir, exist_ok=True) os.makedirs(logs_dir, exist_ok=True)
# 在安静模式下只记录WARNING及以上级别的日志到控制台
console_level = logging.WARNING if quiet_mode else logging.INFO
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO, # 文件日志仍然记录所有INFO级别
format='%(asctime)s - %(levelname)s - %(message)s', format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[ handlers=[
logging.FileHandler(os.path.join(logs_dir, 'scheduler.log'), encoding='utf-8'), logging.FileHandler(os.path.join(logs_dir, 'scheduler.log'), encoding='utf-8'),
@ -50,14 +51,55 @@ def setup_logging():
] ]
) )
# 如果是安静模式,调整控制台处理器的级别
if quiet_mode:
for handler in logging.getLogger().handlers:
if isinstance(handler, logging.StreamHandler) and not isinstance(handler, logging.FileHandler):
handler.setLevel(console_level)
class DouyinAutoScheduler: class DouyinAutoScheduler:
def __init__(self): def __init__(self):
self.is_running = False self.is_running = False
def _normalize_play_vv(self, play_vv):
"""标准化播放量数据类型,将字符串转换为数字"""
if isinstance(play_vv, str):
try:
return int(play_vv.replace(',', '').replace('', '0000').replace('亿', '00000000'))
except:
return 0
elif not isinstance(play_vv, (int, float)):
return 0
return play_vv
def _deduplicate_videos_by_mix_name(self, videos, include_rank=False):
"""按短剧名称去重,保留播放量最高的记录"""
unique_data = {}
for video in videos:
mix_name = video.get("mix_name", "")
if mix_name:
# 标准化播放量数据类型
play_vv = self._normalize_play_vv(video.get("play_vv", 0))
if mix_name not in unique_data or play_vv > unique_data[mix_name].get("play_vv", 0):
if include_rank:
# 用于昨天数据的格式
unique_data[mix_name] = {
"play_vv": play_vv,
"video_id": str(video.get("_id", "")),
"rank": 0 # 稍后计算排名
}
else:
# 用于今天数据的格式,直接更新原视频对象
video["play_vv"] = play_vv
unique_data[mix_name] = video
return unique_data
def run_douyin_scraper(self): def run_douyin_scraper(self):
"""执行抖音播放量抓取任务""" """执行抖音播放量抓取任务"""
try: try:
logging.info("🚀 开始执行抖音播放量抓取任务...") logging.warning("🚀 开始执行抖音播放量抓取任务...")
# 设置环境变量,确保自动模式 # 设置环境变量,确保自动模式
os.environ['AUTO_CONTINUE'] = '1' os.environ['AUTO_CONTINUE'] = '1'
@ -89,7 +131,7 @@ class DouyinAutoScheduler:
from datetime import timedelta from datetime import timedelta
# 获取集合 # 获取集合
douyin_collection = db['Rankings_list'] # 使用真实抓取的数据 douyin_collection = db['Ranking_storage_list'] # 使用定时器抓取的数据
rankings_collection = db['Ranking_storage'] rankings_collection = db['Ranking_storage']
today = date.today() today = date.today()
@ -107,8 +149,18 @@ class DouyinAutoScheduler:
try: try:
logging.info("🔄 正在生成时间轴对比榜单...") logging.info("🔄 正在生成时间轴对比榜单...")
# 获取今天的数据,按短剧名称去重,只保留播放量最高的 # 获取最新批次的数据
today_videos_raw = list(douyin_collection.find({}).sort("play_vv", -1)) latest_batch = douyin_collection.find_one(sort=[("batch_time", -1)])
if not latest_batch:
logging.warning("⚠️ 未找到任何数据")
return False
latest_batch_time = latest_batch.get("batch_time")
logging.info(f"📊 找到最新批次时间: {latest_batch_time}")
# 只获取最新批次的数据
today_videos_raw = list(douyin_collection.find({"batch_time": latest_batch_time}).sort("play_vv", -1))
logging.info(f"📊 最新批次数据数量: {len(today_videos_raw)}")
# 按短剧名称去重,每个短剧只保留播放量最高的一条 # 按短剧名称去重,每个短剧只保留播放量最高的一条
unique_videos = {} unique_videos = {}
@ -140,7 +192,7 @@ class DouyinAutoScheduler:
} }
logging.info(f"📊 找到昨天的榜单数据,共 {len(yesterday_data)} 个短剧") logging.info(f"📊 找到昨天的榜单数据,共 {len(yesterday_data)} 个短剧")
else: else:
logging.info("📊 未找到昨天的榜单数据,将作为首次生成") logging.info("📊 未找到昨天的原始数据,将作为首次生成")
if today_videos: if today_videos:
# 先计算所有视频的播放量差值 # 先计算所有视频的播放量差值
@ -315,8 +367,6 @@ class DouyinAutoScheduler:
def main(): def main():
"""主函数""" """主函数"""
import argparse
try: try:
parser = argparse.ArgumentParser(description='抖音播放量自动抓取定时器') parser = argparse.ArgumentParser(description='抖音播放量自动抓取定时器')
parser.add_argument('--test', action='store_true', help='测试模式 - 立即执行一次') parser.add_argument('--test', action='store_true', help='测试模式 - 立即执行一次')
@ -325,23 +375,51 @@ def main():
args = parser.parse_args() args = parser.parse_args()
# 设置日志配置 # 设置日志配置 - 只在定时器模式下启用静默模式
setup_logging() quiet_mode = not (args.test or args.once or args.ranking_only)
setup_logging(quiet_mode=quiet_mode)
print("正在初始化定时器...") print("正在初始化定时器...")
scheduler = DouyinAutoScheduler() scheduler = DouyinAutoScheduler()
if args.test: if args.test:
scheduler._is_timer_mode = False
print("执行测试模式...") print("执行测试模式...")
scheduler.run_test() scheduler.run_test()
elif args.once: elif args.once:
scheduler._is_timer_mode = False
print("执行单次模式...") print("执行单次模式...")
scheduler.run_once() scheduler.run_once()
elif args.ranking_only: elif args.ranking_only:
scheduler._is_timer_mode = False
print("执行榜单生成模式...") print("执行榜单生成模式...")
scheduler.run_ranking_only() scheduler.run_ranking_only()
else: else:
scheduler._is_timer_mode = True
print("启动定时器模式...") print("启动定时器模式...")
# 显示定时器信息使用print确保能看到
from datetime import datetime
current_time = datetime.now()
print(f"🕐 当前时间:{current_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"⏰ 执行规则:每小时整点执行抖音播放量抓取")
# 计算下次执行时间
next_hour = current_time.replace(minute=0, second=0, microsecond=0)
if current_time.minute > 0 or current_time.second > 0:
next_hour = next_hour.replace(hour=next_hour.hour + 1)
if next_hour.hour >= 24:
from datetime import timedelta
next_hour = next_hour.replace(hour=0) + timedelta(days=1)
wait_seconds = (next_hour - current_time).total_seconds()
wait_minutes = int(wait_seconds // 60)
print(f"⏰ 下次执行时间:{next_hour.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"⏳ 距离下次执行:{wait_minutes} 分钟 ({int(wait_seconds)} 秒)")
print("💡 定时器正在等待中,将在整点自动执行任务...")
print("⏹️ 按 Ctrl+C 停止定时器")
scheduler.setup_schedule() scheduler.setup_schedule()
scheduler.start_scheduler() scheduler.start_scheduler()

View File

@ -18,4 +18,25 @@ LOG_DIR = 'logs'
# 定时器配置 # 定时器配置
SCHEDULER_TIME = "24:00" # 定时器执行时间,格式为 HH:MM (24小时制) SCHEDULER_TIME = "24:00" # 定时器执行时间,格式为 HH:MM (24小时制)
# TOS/火山云对象存储配置
TOS_CONFIG = {
'access_key_id': os.getenv('TOS_ACCESS_KEY_ID', 'AKLTYjQyYmE1ZDAwZTY5NGZiOWI3ODZkZDhhOWE4MzVjODE'),
'access_key_secret': os.getenv('TOS_ACCESS_KEY_SECRET', 'WlRKa05EbGhZVEUyTXpjNU5ESmpPRGt5T0RJNFl6QmhPR0pqTVRjMVpUWQ=='),
'endpoint': 'https://tos-cn-beijing.volces.com',
'region': 'cn-beijing',
'bucket_name': os.getenv('TOS_BUCKET_NAME', 'km1'),
'self_domain': os.getenv('TOS_SELF_DOMAIN', 'oss.xintiao85.com'),
'disable_ssl_warnings': True
}
# API配置兼容现有代码
API_CONFIG = {
'huoshan': {
'AccessKey': TOS_CONFIG['access_key_id'],
'SecretKey': TOS_CONFIG['access_key_secret']
},
'OSS_BUCKET_NAME': TOS_CONFIG['bucket_name'],
'OSS_HOST': TOS_CONFIG['self_domain']
}
print(f"Successfully loaded configuration for environment: {APP_ENV}") print(f"Successfully loaded configuration for environment: {APP_ENV}")

View File

@ -0,0 +1,43 @@
{
"episodes": [
{
"video_id": "7389531100718107954",
"episode_num": 0
},
{
"video_id": "7393209049208130851",
"episode_num": 0
},
{
"video_id": "7398121035452763432",
"episode_num": 0
},
{
"video_id": "7404808266888252698",
"episode_num": 0
},
{
"video_id": "7410761253204905235",
"episode_num": 0
},
{
"video_id": "7416596680776158515",
"episode_num": 0
},
{
"video_id": "7426956763892665654",
"episode_num": 0
},
{
"video_id": "7429208304389328180",
"episode_num": 0
},
{
"video_id": "7429519648518966555",
"episode_num": 0
}
],
"total_count": 9,
"last_update": "2025-10-22T09:55:41.380145",
"mix_name": "觉醒"
}

View File

@ -0,0 +1,123 @@
{
"episodes": [
{
"video_id": "7421044063190699303",
"episode_num": 0
},
{
"video_id": "7425556502057897270",
"episode_num": 0
},
{
"video_id": "7428086785537821963",
"episode_num": 0
},
{
"video_id": "7438273827555134774",
"episode_num": 0
},
{
"video_id": "7431047488955632907",
"episode_num": 0
},
{
"video_id": "7434029345796066623",
"episode_num": 0
},
{
"video_id": "7438157166382550313",
"episode_num": 0
},
{
"video_id": "7445146231225077003",
"episode_num": 0
},
{
"video_id": "7448086473322106152",
"episode_num": 0
},
{
"video_id": "7451825341225585954",
"episode_num": 0
},
{
"video_id": "7463783714389085476",
"episode_num": 0
},
{
"video_id": "7461502376516586752",
"episode_num": 0
},
{
"video_id": "7471109492738952483",
"episode_num": 0
},
{
"video_id": "7473376886383512884",
"episode_num": 0
},
{
"video_id": "7476035323416415542",
"episode_num": 0
},
{
"video_id": "7477826100815514889",
"episode_num": 0
},
{
"video_id": "7480121799733202186",
"episode_num": 0
},
{
"video_id": "7482669047008611610",
"episode_num": 0
},
{
"video_id": "7483737890845920549",
"episode_num": 0
},
{
"video_id": "7486742756103130394",
"episode_num": 0
},
{
"video_id": "7487446983901269274",
"episode_num": 0
},
{
"video_id": "7488238211139996978",
"episode_num": 0
},
{
"video_id": "7489006391865216265",
"episode_num": 0
},
{
"video_id": "7493926858061352202",
"episode_num": 0
},
{
"video_id": "7498645852182203700",
"episode_num": 0
},
{
"video_id": "7502686428054244660",
"episode_num": 0
},
{
"video_id": "7504263612656078116",
"episode_num": 0
},
{
"video_id": "7509429118845340982",
"episode_num": 0
},
{
"video_id": "7509743276300291340",
"episode_num": 0
}
],
"total_count": 29,
"last_update": "2025-10-22T09:56:01.930652",
"mix_name": "《田螺姑娘》系列短剧"
}

View File

@ -0,0 +1,51 @@
{
"episodes": [
{
"video_id": "7472763101620751626",
"episode_num": 0
},
{
"video_id": "7472763403182886156",
"episode_num": 0
},
{
"video_id": "7472763009627065619",
"episode_num": 0
},
{
"video_id": "7472764867905195327",
"episode_num": 0
},
{
"video_id": "7472763523450293516",
"episode_num": 0
},
{
"video_id": "7472763290104450315",
"episode_num": 0
},
{
"video_id": "7472762896833908031",
"episode_num": 0
},
{
"video_id": "7472763068376780071",
"episode_num": 0
},
{
"video_id": "7472763556648193334",
"episode_num": 0
},
{
"video_id": "7472763183409777939",
"episode_num": 0
},
{
"video_id": "7472763426813529380",
"episode_num": 0
}
],
"total_count": 11,
"last_update": "2025-10-22T09:56:08.613429",
"mix_name": "兴安岭诡事"
}

View File

@ -0,0 +1,27 @@
{
"episodes": [
{
"video_id": "7462779038784933158",
"episode_num": 0
},
{
"video_id": "7469001162868444443",
"episode_num": 0
},
{
"video_id": "7469998771519130889",
"episode_num": 0
},
{
"video_id": "7470709002284862720",
"episode_num": 0
},
{
"video_id": "7471924777410645283",
"episode_num": 0
}
],
"total_count": 5,
"last_update": "2025-10-22T09:55:36.943794",
"mix_name": "《青蛇传》"
}

View File

@ -0,0 +1,123 @@
{
"episodes": [
{
"video_id": "7438832661914717475",
"episode_num": 0
},
{
"video_id": "7441055420505918760",
"episode_num": 0
},
{
"video_id": "7441797752599711010",
"episode_num": 0
},
{
"video_id": "7444025334355201314",
"episode_num": 0
},
{
"video_id": "7444767906782514432",
"episode_num": 0
},
{
"video_id": "7445511144686587170",
"episode_num": 0
},
{
"video_id": "7448480282623216934",
"episode_num": 0
},
{
"video_id": "7449224455496256777",
"episode_num": 0
},
{
"video_id": "7449966534602018074",
"episode_num": 0
},
{
"video_id": "7452190854900370715",
"episode_num": 0
},
{
"video_id": "7452932931934342437",
"episode_num": 0
},
{
"video_id": "7453673782092303666",
"episode_num": 0
},
{
"video_id": "7454416566910864650",
"episode_num": 0
},
{
"video_id": "7458870546697080101",
"episode_num": 0
},
{
"video_id": "7459612621348572443",
"episode_num": 0
},
{
"video_id": "7460354830322699571",
"episode_num": 0
},
{
"video_id": "7461097063850249499",
"episode_num": 0
},
{
"video_id": "7461840320993463590",
"episode_num": 0
},
{
"video_id": "7462583778557349147",
"episode_num": 0
},
{
"video_id": "7463322906186091813",
"episode_num": 0
},
{
"video_id": "7467405224320322854",
"episode_num": 0
},
{
"video_id": "7468147493620960549",
"episode_num": 0
},
{
"video_id": "7468887846011653402",
"episode_num": 0
},
{
"video_id": "7469630119456361779",
"episode_num": 0
},
{
"video_id": "7471114379258318106",
"episode_num": 0
},
{
"video_id": "7471856794772491547",
"episode_num": 0
},
{
"video_id": "7472598861073157403",
"episode_num": 0
},
{
"video_id": "7473341167099186441",
"episode_num": 0
},
{
"video_id": "7474085833558052159",
"episode_num": 0
}
],
"total_count": 29,
"last_update": "2025-10-22T09:55:53.324636",
"mix_name": "我在地府开当铺"
}

View File

@ -0,0 +1,87 @@
{
"episodes": [
{
"video_id": "7472623062152695074",
"episode_num": 0
},
{
"video_id": "7457112388907666727",
"episode_num": 0
},
{
"video_id": "7457505550188743973",
"episode_num": 0
},
{
"video_id": "7457874864729345318",
"episode_num": 0
},
{
"video_id": "7458965679874542858",
"episode_num": 0
},
{
"video_id": "7459348765586001178",
"episode_num": 0
},
{
"video_id": "7459760717202345254",
"episode_num": 0
},
{
"video_id": "7460119944940883227",
"episode_num": 0
},
{
"video_id": "7460847335602490675",
"episode_num": 0
},
{
"video_id": "7461235696414559514",
"episode_num": 0
},
{
"video_id": "7461939199000825151",
"episode_num": 0
},
{
"video_id": "7462718793974910271",
"episode_num": 0
},
{
"video_id": "7463090318058081555",
"episode_num": 0
},
{
"video_id": "7463442602558246207",
"episode_num": 0
},
{
"video_id": "7464950323786861874",
"episode_num": 0
},
{
"video_id": "7466027689028193574",
"episode_num": 0
},
{
"video_id": "7467918209505676585",
"episode_num": 0
},
{
"video_id": "7469002478999096610",
"episode_num": 0
},
{
"video_id": "7471609858727447808",
"episode_num": 0
},
{
"video_id": "7472348174464650536",
"episode_num": 0
}
],
"total_count": 20,
"last_update": "2025-10-22T09:56:14.396524",
"mix_name": "《鲛人珠》"
}

View File

@ -0,0 +1,47 @@
{
"episodes": [
{
"video_id": "7445594903796518201",
"episode_num": 0
},
{
"video_id": "7446432501297384763",
"episode_num": 0
},
{
"video_id": "7454551962659376443",
"episode_num": 0
},
{
"video_id": "7461234093703875898",
"episode_num": 0
},
{
"video_id": "7466059018256026940",
"episode_num": 0
},
{
"video_id": "7471994773054819641",
"episode_num": 0
},
{
"video_id": "7474591461695147324",
"episode_num": 0
},
{
"video_id": "7477189231987772729",
"episode_num": 0
},
{
"video_id": "7478673086884187449",
"episode_num": 0
},
{
"video_id": "7482757832807386426",
"episode_num": 0
}
],
"total_count": 10,
"last_update": "2025-10-22T09:55:40.776573",
"mix_name": "中式百妖集·白骨夫人"
}

View File

@ -0,0 +1,79 @@
{
"episodes": [
{
"video_id": "7484932173439536421",
"episode_num": 0
},
{
"video_id": "7485664110688898343",
"episode_num": 0
},
{
"video_id": "7486419411969068314",
"episode_num": 0
},
{
"video_id": "7487121293100305718",
"episode_num": 0
},
{
"video_id": "7487526141658467620",
"episode_num": 0
},
{
"video_id": "7488271435824123173",
"episode_num": 0
},
{
"video_id": "7488650316636917044",
"episode_num": 0
},
{
"video_id": "7489021106041392418",
"episode_num": 0
},
{
"video_id": "7489378816629001510",
"episode_num": 0
},
{
"video_id": "7489762229294730530",
"episode_num": 0
},
{
"video_id": "7490506362162580771",
"episode_num": 0
},
{
"video_id": "7491612455748128063",
"episode_num": 0
},
{
"video_id": "7492356791385214249",
"episode_num": 0
},
{
"video_id": "7493098513320938764",
"episode_num": 0
},
{
"video_id": "7493470039182445843",
"episode_num": 0
},
{
"video_id": "7494213861059579170",
"episode_num": 0
},
{
"video_id": "7494607366281809179",
"episode_num": 0
},
{
"video_id": "7496444061780299017",
"episode_num": 0
}
],
"total_count": 18,
"last_update": "2025-10-22T09:56:10.891927",
"mix_name": "忘川引"
}

View File

@ -0,0 +1,151 @@
{
"episodes": [
{
"video_id": "7508787409220308287",
"episode_num": 0
},
{
"video_id": "7510681348156214582",
"episode_num": 0
},
{
"video_id": "7511991741650701607",
"episode_num": 0
},
{
"video_id": "7513617213908159783",
"episode_num": 0
},
{
"video_id": "7514504440183885067",
"episode_num": 0
},
{
"video_id": "7515713983450451211",
"episode_num": 0
},
{
"video_id": "7516611820073930020",
"episode_num": 0
},
{
"video_id": "7517661121336184127",
"episode_num": 0
},
{
"video_id": "7518746065160424743",
"episode_num": 0
},
{
"video_id": "7519749002439429417",
"episode_num": 0
},
{
"video_id": "7520472849899932964",
"episode_num": 0
},
{
"video_id": "7521204206330547497",
"episode_num": 0
},
{
"video_id": "7524573752886054196",
"episode_num": 0
},
{
"video_id": "7530578815811128603",
"episode_num": 0
},
{
"video_id": "7531758004685901065",
"episode_num": 0
},
{
"video_id": "7532765269714816306",
"episode_num": 0
},
{
"video_id": "7535386089935129871",
"episode_num": 0
},
{
"video_id": "7537427761216998708",
"episode_num": 0
},
{
"video_id": "7538524602385878324",
"episode_num": 0
},
{
"video_id": "7540051891523177737",
"episode_num": 0
},
{
"video_id": "7541107806611475752",
"episode_num": 0
},
{
"video_id": "7541853195853712640",
"episode_num": 0
},
{
"video_id": "7542682704836332835",
"episode_num": 0
},
{
"video_id": "7543822135823830287",
"episode_num": 0
},
{
"video_id": "7544261185663798555",
"episode_num": 0
},
{
"video_id": "7544908029926313225",
"episode_num": 0
},
{
"video_id": "7545999867550747914",
"episode_num": 0
},
{
"video_id": "7548507331529657626",
"episode_num": 0
},
{
"video_id": "7550700558206635274",
"episode_num": 0
},
{
"video_id": "7551695429147086126",
"episode_num": 0
},
{
"video_id": "7552568123484900646",
"episode_num": 0
},
{
"video_id": "7552961927568313609",
"episode_num": 0
},
{
"video_id": "7553727704906943771",
"episode_num": 0
},
{
"video_id": "7555956738030177562",
"episode_num": 0
},
{
"video_id": "7557016037955325194",
"episode_num": 0
},
{
"video_id": "7558378239337467174",
"episode_num": 0
}
],
"total_count": 36,
"last_update": "2025-10-22T09:55:32.073567",
"mix_name": "末世系列"
}

View File

@ -0,0 +1,47 @@
{
"episodes": [
{
"video_id": "7488147760089287973",
"episode_num": 0
},
{
"video_id": "7488147616237096219",
"episode_num": 0
},
{
"video_id": "7488146990686014757",
"episode_num": 0
},
{
"video_id": "7488146612695485706",
"episode_num": 0
},
{
"video_id": "7488145907737775369",
"episode_num": 0
},
{
"video_id": "7488510699275029810",
"episode_num": 0
},
{
"video_id": "7488511105959071013",
"episode_num": 0
},
{
"video_id": "7488511733053607177",
"episode_num": 0
},
{
"video_id": "7488885747047468315",
"episode_num": 0
},
{
"video_id": "7488885905084714249",
"episode_num": 0
}
],
"total_count": 10,
"last_update": "2025-10-22T09:56:06.856199",
"mix_name": "全国首部奇幻探秘Al微短剧"
}

View File

@ -0,0 +1,143 @@
{
"episodes": [
{
"video_id": "7486805231804681530",
"episode_num": 0
},
{
"video_id": "7487578743611985210",
"episode_num": 0
},
{
"video_id": "7488320388464774460",
"episode_num": 0
},
{
"video_id": "7489343145566555449",
"episode_num": 0
},
{
"video_id": "7490919031881469241",
"episode_num": 0
},
{
"video_id": "7491659592909327675",
"episode_num": 0
},
{
"video_id": "7492774825606483258",
"episode_num": 0
},
{
"video_id": "7495283384688594233",
"episode_num": 0
},
{
"video_id": "7496485765267754300",
"episode_num": 0
},
{
"video_id": "7497545408492014905",
"episode_num": 0
},
{
"video_id": "7499453865651014971",
"episode_num": 0
},
{
"video_id": "7500569260890099004",
"episode_num": 0
},
{
"video_id": "7502050015763189050",
"episode_num": 0
},
{
"video_id": "7502796678514642233",
"episode_num": 0
},
{
"video_id": "7505018729597963577",
"episode_num": 0
},
{
"video_id": "7505763063816670521",
"episode_num": 0
},
{
"video_id": "7507631537329458490",
"episode_num": 0
},
{
"video_id": "7508730844816280890",
"episode_num": 0
},
{
"video_id": "7513187407958150460",
"episode_num": 0
},
{
"video_id": "7516524242646125884",
"episode_num": 0
},
{
"video_id": "7518011787930520889",
"episode_num": 0
},
{
"video_id": "7520976901600447801",
"episode_num": 0
},
{
"video_id": "7537305227914546490",
"episode_num": 0
},
{
"video_id": "7546210488913595705",
"episode_num": 0
},
{
"video_id": "7546922516716539196",
"episode_num": 0
},
{
"video_id": "7547625203083185468",
"episode_num": 0
},
{
"video_id": "7549924545768295739",
"episode_num": 0
},
{
"video_id": "7553215200825134394",
"episode_num": 0
},
{
"video_id": "7554737168394374459",
"episode_num": 0
},
{
"video_id": "7555479694759284027",
"episode_num": 0
},
{
"video_id": "7556555317988724026",
"episode_num": 0
},
{
"video_id": "7562121519012285755",
"episode_num": 0
},
{
"video_id": "7563238756892757307",
"episode_num": 0
},
{
"video_id": "7564756828878753061",
"episode_num": 0
}
],
"total_count": 34,
"last_update": "2025-10-26T14:48:56.017802",
"mix_name": "【中式百妖集·阴医】"
}

View File

@ -0,0 +1,67 @@
{
"episodes": [
{
"video_id": "7498696558876331318",
"episode_num": 0
},
{
"video_id": "7498696627905940747",
"episode_num": 0
},
{
"video_id": "7498696682746514751",
"episode_num": 0
},
{
"video_id": "7498696464852471051",
"episode_num": 0
},
{
"video_id": "7498991164272479551",
"episode_num": 0
},
{
"video_id": "7499073383687114024",
"episode_num": 0
},
{
"video_id": "7499073497541528867",
"episode_num": 0
},
{
"video_id": "7499073601295944975",
"episode_num": 0
},
{
"video_id": "7499073974123498804",
"episode_num": 0
},
{
"video_id": "7499074026569059599",
"episode_num": 0
},
{
"video_id": "7499074347429006626",
"episode_num": 0
},
{
"video_id": "7499074195519802658",
"episode_num": 0
},
{
"video_id": "7501976370621287718",
"episode_num": 0
},
{
"video_id": "7502295085464227082",
"episode_num": 0
},
{
"video_id": "7502419858789911846",
"episode_num": 0
}
],
"total_count": 15,
"last_update": "2025-10-22T09:55:36.327209",
"mix_name": "盲盒千岁"
}

View File

@ -0,0 +1,47 @@
{
"episodes": [
{
"video_id": "7504244447165615360",
"episode_num": 0
},
{
"video_id": "7504244610508524834",
"episode_num": 0
},
{
"video_id": "7504244482838138147",
"episode_num": 0
},
{
"video_id": "7504244233549761844",
"episode_num": 0
},
{
"video_id": "7504244628758007040",
"episode_num": 0
},
{
"video_id": "7504244597929889076",
"episode_num": 0
},
{
"video_id": "7504244625457024296",
"episode_num": 0
},
{
"video_id": "7504244460637654307",
"episode_num": 0
},
{
"video_id": "7504244631295528227",
"episode_num": 0
},
{
"video_id": "7504244436004654371",
"episode_num": 0
}
],
"total_count": 10,
"last_update": "2025-10-22T09:55:42.069016",
"mix_name": "白骨精传奇"
}

View File

@ -0,0 +1,155 @@
{
"episodes": [
{
"video_id": "7506067244128963892",
"episode_num": 0
},
{
"video_id": "7506450698897296692",
"episode_num": 0
},
{
"video_id": "7506711648292375846",
"episode_num": 0
},
{
"video_id": "7508950346337815817",
"episode_num": 0
},
{
"video_id": "7509774388443499803",
"episode_num": 0
},
{
"video_id": "7510136494552550697",
"episode_num": 0
},
{
"video_id": "7511916836351511862",
"episode_num": 0
},
{
"video_id": "7514267874987363618",
"episode_num": 0
},
{
"video_id": "7514880725972225319",
"episode_num": 0
},
{
"video_id": "7516734442199682339",
"episode_num": 0
},
{
"video_id": "7517445470608690447",
"episode_num": 0
},
{
"video_id": "7517854243118730536",
"episode_num": 0
},
{
"video_id": "7519774016958172443",
"episode_num": 0
},
{
"video_id": "7520504060600929571",
"episode_num": 0
},
{
"video_id": "7521579659352395062",
"episode_num": 0
},
{
"video_id": "7522327810527055143",
"episode_num": 0
},
{
"video_id": "7524266961459481891",
"episode_num": 0
},
{
"video_id": "7524619540563512616",
"episode_num": 0
},
{
"video_id": "7525332357025402151",
"episode_num": 0
},
{
"video_id": "7527197348515171584",
"episode_num": 0
},
{
"video_id": "7527580712619363618",
"episode_num": 0
},
{
"video_id": "7527875102319316258",
"episode_num": 0
},
{
"video_id": "7529722113029147944",
"episode_num": 0
},
{
"video_id": "7529741695441833268",
"episode_num": 0
},
{
"video_id": "7530556459571039507",
"episode_num": 0
},
{
"video_id": "7532003677117091107",
"episode_num": 0
},
{
"video_id": "7533520661294239012",
"episode_num": 0
},
{
"video_id": "7534591926373190912",
"episode_num": 0
},
{
"video_id": "7535986779401325843",
"episode_num": 0
},
{
"video_id": "7538632635627703606",
"episode_num": 0
},
{
"video_id": "7538634729889877290",
"episode_num": 0
},
{
"video_id": "7540121743873035555",
"episode_num": 0
},
{
"video_id": "7540870569168784655",
"episode_num": 0
},
{
"video_id": "7542402995653283112",
"episode_num": 0
},
{
"video_id": "7546532809460976948",
"episode_num": 0
},
{
"video_id": "7547533101929467151",
"episode_num": 0
},
{
"video_id": "7547910476743609652",
"episode_num": 0
}
],
"total_count": 37,
"last_update": "2025-10-22T09:56:06.222702",
"mix_name": "九尾狐男妖爱上我"
}

View File

@ -0,0 +1,235 @@
{
"episodes": [
{
"video_id": "7506725945898847515",
"episode_num": 0
},
{
"video_id": "7506726188392402186",
"episode_num": 0
},
{
"video_id": "7506726062122929417",
"episode_num": 0
},
{
"video_id": "7506725983026777354",
"episode_num": 0
},
{
"video_id": "7506726108860124443",
"episode_num": 0
},
{
"video_id": "7506726665033223433",
"episode_num": 0
},
{
"video_id": "7506726346563800370",
"episode_num": 0
},
{
"video_id": "7506726016790940965",
"episode_num": 0
},
{
"video_id": "7506726671379221787",
"episode_num": 0
},
{
"video_id": "7506726090564586761",
"episode_num": 0
},
{
"video_id": "7506726187318725898",
"episode_num": 0
},
{
"video_id": "7506726296039230730",
"episode_num": 0
},
{
"video_id": "7506726309150592294",
"episode_num": 0
},
{
"video_id": "7506726284358094107",
"episode_num": 0
},
{
"video_id": "7506726237205777714",
"episode_num": 0
},
{
"video_id": "7506726182524685605",
"episode_num": 0
},
{
"video_id": "7506726228850674954",
"episode_num": 0
},
{
"video_id": "7506726166506605862",
"episode_num": 0
},
{
"video_id": "7506726208801901851",
"episode_num": 0
},
{
"video_id": "7506726298337758501",
"episode_num": 0
},
{
"video_id": "7506726295435218213",
"episode_num": 0
},
{
"video_id": "7506726377392016650",
"episode_num": 0
},
{
"video_id": "7506726273427754278",
"episode_num": 0
},
{
"video_id": "7506726355774524682",
"episode_num": 0
},
{
"video_id": "7506726266536594726",
"episode_num": 0
},
{
"video_id": "7506726314951462181",
"episode_num": 0
},
{
"video_id": "7506726196588203290",
"episode_num": 0
},
{
"video_id": "7506726170638028070",
"episode_num": 0
},
{
"video_id": "7506726491531660594",
"episode_num": 0
},
{
"video_id": "7506726608372239643",
"episode_num": 0
},
{
"video_id": "7506726485236141362",
"episode_num": 0
},
{
"video_id": "7506726498439564553",
"episode_num": 0
},
{
"video_id": "7506726478663486758",
"episode_num": 0
},
{
"video_id": "7506726546317659419",
"episode_num": 0
},
{
"video_id": "7506726541062130982",
"episode_num": 0
},
{
"video_id": "7506726526709157147",
"episode_num": 0
},
{
"video_id": "7506726570220883251",
"episode_num": 0
},
{
"video_id": "7506726627510897958",
"episode_num": 0
},
{
"video_id": "7506726589883747622",
"episode_num": 0
},
{
"video_id": "7506726632447593765",
"episode_num": 0
},
{
"video_id": "7506726607525039369",
"episode_num": 0
},
{
"video_id": "7506726659584822565",
"episode_num": 0
},
{
"video_id": "7506726454617525541",
"episode_num": 0
},
{
"video_id": "7506726418294869285",
"episode_num": 0
},
{
"video_id": "7506726571374333210",
"episode_num": 0
},
{
"video_id": "7506726517443956019",
"episode_num": 0
},
{
"video_id": "7506726555305905418",
"episode_num": 0
},
{
"video_id": "7506726472007109914",
"episode_num": 0
},
{
"video_id": "7506726648675323173",
"episode_num": 0
},
{
"video_id": "7506726475693919515",
"episode_num": 0
},
{
"video_id": "7506726506924739850",
"episode_num": 0
},
{
"video_id": "7506726523471220019",
"episode_num": 0
},
{
"video_id": "7506726476633476379",
"episode_num": 0
},
{
"video_id": "7506726532715433226",
"episode_num": 0
},
{
"video_id": "7506726554542640394",
"episode_num": 0
},
{
"video_id": "7506726533835410698",
"episode_num": 0
},
{
"video_id": "7506726608326118683",
"episode_num": 0
}
],
"total_count": 57,
"last_update": "2025-10-22T09:55:59.359473",
"mix_name": "如意坊"
}

View File

@ -0,0 +1,55 @@
{
"episodes": [
{
"video_id": "7549025803313384746",
"episode_num": 0
},
{
"video_id": "7549025817850826003",
"episode_num": 0
},
{
"video_id": "7549025811861409043",
"episode_num": 0
},
{
"video_id": "7549025819251756342",
"episode_num": 0
},
{
"video_id": "7549025818131893547",
"episode_num": 0
},
{
"video_id": "7549025828185705767",
"episode_num": 0
},
{
"video_id": "7549025818874252563",
"episode_num": 0
},
{
"video_id": "7549025854819405097",
"episode_num": 0
},
{
"video_id": "7549025851971521828",
"episode_num": 0
},
{
"video_id": "7549025854999891239",
"episode_num": 0
},
{
"video_id": "7549025873840590134",
"episode_num": 0
},
{
"video_id": "7549025884955577643",
"episode_num": 0
}
],
"total_count": 12,
"last_update": "2025-10-22T09:55:37.752763",
"mix_name": "新平妖传"
}

View File

@ -0,0 +1,83 @@
{
"episodes": [
{
"video_id": "7517472781332696332",
"episode_num": 0
},
{
"video_id": "7517971761476979980",
"episode_num": 0
},
{
"video_id": "7518395078306483510",
"episode_num": 0
},
{
"video_id": "7518867709761686803",
"episode_num": 0
},
{
"video_id": "7519654070961949988",
"episode_num": 0
},
{
"video_id": "7520748725438811446",
"episode_num": 0
},
{
"video_id": "7521798836566641983",
"episode_num": 0
},
{
"video_id": "7529928581993041215",
"episode_num": 0
},
{
"video_id": "7530321831966805284",
"episode_num": 0
},
{
"video_id": "7531079565049941289",
"episode_num": 0
},
{
"video_id": "7532255656586399017",
"episode_num": 0
},
{
"video_id": "7533366600439762195",
"episode_num": 0
},
{
"video_id": "7534435304296516918",
"episode_num": 0
},
{
"video_id": "7535534271813979431",
"episode_num": 0
},
{
"video_id": "7536713189078175016",
"episode_num": 0
},
{
"video_id": "7538565796382002467",
"episode_num": 0
},
{
"video_id": "7538868180136037647",
"episode_num": 0
},
{
"video_id": "7540042632618167592",
"episode_num": 0
},
{
"video_id": "7543715550619241743",
"episode_num": 0
}
],
"total_count": 19,
"last_update": "2025-10-22T09:55:49.993319",
"mix_name": "苏祥云"
}

View File

@ -0,0 +1,19 @@
{
"episodes": [
{
"video_id": "7527336931760295168",
"episode_num": 0
},
{
"video_id": "7529100313467325711",
"episode_num": 0
},
{
"video_id": "7539690162612079872",
"episode_num": 0
}
],
"total_count": 3,
"last_update": "2025-10-22T09:55:17.087205",
"mix_name": "《小宝穿越|课本古诗文》"
}

View File

@ -0,0 +1,19 @@
{
"episodes": [
{
"video_id": "7476841679304051968",
"episode_num": 0
},
{
"video_id": "7519893460598541609",
"episode_num": 0
},
{
"video_id": "7548075831491415359",
"episode_num": 0
}
],
"total_count": 3,
"last_update": "2025-10-22T09:56:11.433105",
"mix_name": "僵尸师姐"
}

View File

@ -0,0 +1,55 @@
{
"episodes": [
{
"video_id": "7529542793484815642",
"episode_num": 0
},
{
"video_id": "7529837310423190825",
"episode_num": 0
},
{
"video_id": "7530392006963498279",
"episode_num": 0
},
{
"video_id": "7531919686112529716",
"episode_num": 0
},
{
"video_id": "7533566057529740559",
"episode_num": 0
},
{
"video_id": "7536843946534669631",
"episode_num": 0
},
{
"video_id": "7538371048060898600",
"episode_num": 0
},
{
"video_id": "7540606531264433418",
"episode_num": 0
},
{
"video_id": "7542079999621319986",
"episode_num": 0
},
{
"video_id": "7543609660469824822",
"episode_num": 0
},
{
"video_id": "7546200368829484339",
"episode_num": 0
},
{
"video_id": "7548447317729234239",
"episode_num": 0
}
],
"total_count": 12,
"last_update": "2025-10-22T09:55:50.726907",
"mix_name": "青云修仙传"
}

View File

@ -0,0 +1,195 @@
{
"episodes": [
{
"video_id": "7533908493456182538",
"episode_num": 0
},
{
"video_id": "7534294543475150118",
"episode_num": 0
},
{
"video_id": "7534686345395014950",
"episode_num": 0
},
{
"video_id": "7535429627486031144",
"episode_num": 0
},
{
"video_id": "7535787648519097634",
"episode_num": 0
},
{
"video_id": "7536148527018167604",
"episode_num": 0
},
{
"video_id": "7536756145902374159",
"episode_num": 0
},
{
"video_id": "7536916803734359296",
"episode_num": 0
},
{
"video_id": "7537291155520621858",
"episode_num": 0
},
{
"video_id": "7537711561570290998",
"episode_num": 0
},
{
"video_id": "7538026362255281447",
"episode_num": 0
},
{
"video_id": "7538291293873556778",
"episode_num": 0
},
{
"video_id": "7538394363068452115",
"episode_num": 0
},
{
"video_id": "7538755908755590463",
"episode_num": 0
},
{
"video_id": "7539159291803045159",
"episode_num": 0
},
{
"video_id": "7539494201172512063",
"episode_num": 0
},
{
"video_id": "7539497676841241898",
"episode_num": 0
},
{
"video_id": "7539878435678653750",
"episode_num": 0
},
{
"video_id": "7541743859181636910",
"episode_num": 0
},
{
"video_id": "7542114403907308810",
"episode_num": 0
},
{
"video_id": "7542826819817377070",
"episode_num": 0
},
{
"video_id": "7542829529639734574",
"episode_num": 0
},
{
"video_id": "7543242228785876275",
"episode_num": 0
},
{
"video_id": "7543611905496059174",
"episode_num": 0
},
{
"video_id": "7543976834791165210",
"episode_num": 0
},
{
"video_id": "7544364656991784219",
"episode_num": 0
},
{
"video_id": "7545110659919383859",
"episode_num": 0
},
{
"video_id": "7545458257129655567",
"episode_num": 0
},
{
"video_id": "7545864375643409704",
"episode_num": 0
},
{
"video_id": "7546225454894599464",
"episode_num": 0
},
{
"video_id": "7546575160715889966",
"episode_num": 0
},
{
"video_id": "7546934519836806409",
"episode_num": 0
},
{
"video_id": "7547336647730335027",
"episode_num": 0
},
{
"video_id": "7547711167158897958",
"episode_num": 0
},
{
"video_id": "7548073308894317875",
"episode_num": 0
},
{
"video_id": "7548371214998244658",
"episode_num": 0
},
{
"video_id": "7548819838773349670",
"episode_num": 0
},
{
"video_id": "7549193055358733606",
"episode_num": 0
},
{
"video_id": "7549574668735270190",
"episode_num": 0
},
{
"video_id": "7549952043016883502",
"episode_num": 0
},
{
"video_id": "7550323254922202378",
"episode_num": 0
},
{
"video_id": "7550694134588263720",
"episode_num": 0
},
{
"video_id": "7551058050275167540",
"episode_num": 0
},
{
"video_id": "7551425021642607906",
"episode_num": 0
},
{
"video_id": "7551790578816552226",
"episode_num": 0
},
{
"video_id": "7555439509588938024",
"episode_num": 0
},
{
"video_id": "7555845189366074662",
"episode_num": 0
}
],
"total_count": 47,
"last_update": "2025-10-22T09:55:27.641577",
"mix_name": "《风水之王》"
}

View File

@ -0,0 +1,279 @@
{
"episodes": [
{
"video_id": "7535701272712777002",
"episode_num": 0
},
{
"video_id": "7535701290232401194",
"episode_num": 0
},
{
"video_id": "7535701286658821430",
"episode_num": 0
},
{
"video_id": "7535701275858570532",
"episode_num": 0
},
{
"video_id": "7535701262310985003",
"episode_num": 0
},
{
"video_id": "7535701270364048682",
"episode_num": 0
},
{
"video_id": "7535701309303901481",
"episode_num": 0
},
{
"video_id": "7535701337573461289",
"episode_num": 0
},
{
"video_id": "7535701337984519460",
"episode_num": 0
},
{
"video_id": "7535701328048311571",
"episode_num": 0
},
{
"video_id": "7535701353595800874",
"episode_num": 0
},
{
"video_id": "7535701350357699903",
"episode_num": 0
},
{
"video_id": "7535701363007769898",
"episode_num": 0
},
{
"video_id": "7535701370368691492",
"episode_num": 0
},
{
"video_id": "7535701350932335911",
"episode_num": 0
},
{
"video_id": "7535701373900377385",
"episode_num": 0
},
{
"video_id": "7535701392267283775",
"episode_num": 0
},
{
"video_id": "7535701391872888106",
"episode_num": 0
},
{
"video_id": "7535701400672619795",
"episode_num": 0
},
{
"video_id": "7535701415142935827",
"episode_num": 0
},
{
"video_id": "7535701423242120484",
"episode_num": 0
},
{
"video_id": "7535701444511403305",
"episode_num": 0
},
{
"video_id": "7535701443542535465",
"episode_num": 0
},
{
"video_id": "7535701456305786153",
"episode_num": 0
},
{
"video_id": "7535701442590428455",
"episode_num": 0
},
{
"video_id": "7535701468439973162",
"episode_num": 0
},
{
"video_id": "7535701494906031399",
"episode_num": 0
},
{
"video_id": "7535701504049548580",
"episode_num": 0
},
{
"video_id": "7535701470788750635",
"episode_num": 0
},
{
"video_id": "7535701485867322687",
"episode_num": 0
},
{
"video_id": "7535701502833200403",
"episode_num": 0
},
{
"video_id": "7535701525734231359",
"episode_num": 0
},
{
"video_id": "7535701525520239914",
"episode_num": 0
},
{
"video_id": "7535701540334587190",
"episode_num": 0
},
{
"video_id": "7535701524362693930",
"episode_num": 0
},
{
"video_id": "7535701557594066217",
"episode_num": 0
},
{
"video_id": "7535701561180163367",
"episode_num": 0
},
{
"video_id": "7535701557178879268",
"episode_num": 0
},
{
"video_id": "7535701577760263463",
"episode_num": 0
},
{
"video_id": "7535701575201738026",
"episode_num": 0
},
{
"video_id": "7535701602775108905",
"episode_num": 0
},
{
"video_id": "7535701587893833014",
"episode_num": 0
},
{
"video_id": "7535701608986873127",
"episode_num": 0
},
{
"video_id": "7535701629786508580",
"episode_num": 0
},
{
"video_id": "7535701644424531239",
"episode_num": 0
},
{
"video_id": "7535701627827752211",
"episode_num": 0
},
{
"video_id": "7535701660933492009",
"episode_num": 0
},
{
"video_id": "7535701645422873897",
"episode_num": 0
},
{
"video_id": "7535701646941179179",
"episode_num": 0
},
{
"video_id": "7535701666885045540",
"episode_num": 0
},
{
"video_id": "7535701674799779135",
"episode_num": 0
},
{
"video_id": "7535701687755869476",
"episode_num": 0
},
{
"video_id": "7535701681867214099",
"episode_num": 0
},
{
"video_id": "7535701718235942180",
"episode_num": 0
},
{
"video_id": "7535701716738559295",
"episode_num": 0
},
{
"video_id": "7535701714779884841",
"episode_num": 0
},
{
"video_id": "7535701730080656679",
"episode_num": 0
},
{
"video_id": "7535701718118567211",
"episode_num": 0
},
{
"video_id": "7535701734463720743",
"episode_num": 0
},
{
"video_id": "7535701751924542756",
"episode_num": 0
},
{
"video_id": "7535701727887084842",
"episode_num": 0
},
{
"video_id": "7535701766235622710",
"episode_num": 0
},
{
"video_id": "7535701764960521508",
"episode_num": 0
},
{
"video_id": "7535701790038199606",
"episode_num": 0
},
{
"video_id": "7535701815388540223",
"episode_num": 0
},
{
"video_id": "7535701795494989098",
"episode_num": 0
},
{
"video_id": "7535701809969532214",
"episode_num": 0
},
{
"video_id": "7543884485494885666",
"episode_num": 0
}
],
"total_count": 68,
"last_update": "2025-10-22T09:56:42.812592",
"mix_name": "奶团太后宫心计"
}

View File

@ -0,0 +1,55 @@
{
"episodes": [
{
"video_id": "7538319264500010274",
"episode_num": 0
},
{
"video_id": "7538319253661895976",
"episode_num": 0
},
{
"video_id": "7538319258040667426",
"episode_num": 0
},
{
"video_id": "7538319250730142991",
"episode_num": 0
},
{
"video_id": "7538319251308956928",
"episode_num": 0
},
{
"video_id": "7538319255717088546",
"episode_num": 0
},
{
"video_id": "7538319260087635200",
"episode_num": 0
},
{
"video_id": "7538319313824943360",
"episode_num": 0
},
{
"video_id": "7538319304874282280",
"episode_num": 0
},
{
"video_id": "7538319315666357519",
"episode_num": 0
},
{
"video_id": "7538319302512921890",
"episode_num": 0
},
{
"video_id": "7538319312289926400",
"episode_num": 0
}
],
"total_count": 12,
"last_update": "2025-10-22T09:56:12.150347",
"mix_name": "燕赤伏妖"
}

View File

@ -0,0 +1,87 @@
{
"episodes": [
{
"video_id": "7540121867537894694",
"episode_num": 0
},
{
"video_id": "7540121873649093926",
"episode_num": 0
},
{
"video_id": "7540121890577272115",
"episode_num": 0
},
{
"video_id": "7540121898751986990",
"episode_num": 0
},
{
"video_id": "7540121893479664934",
"episode_num": 0
},
{
"video_id": "7540121902363233545",
"episode_num": 0
},
{
"video_id": "7540121896306707721",
"episode_num": 0
},
{
"video_id": "7540121918884547878",
"episode_num": 0
},
{
"video_id": "7540121908944162099",
"episode_num": 0
},
{
"video_id": "7540121914937822510",
"episode_num": 0
},
{
"video_id": "7540121923020246310",
"episode_num": 0
},
{
"video_id": "7540121952229330186",
"episode_num": 0
},
{
"video_id": "7540121960240434470",
"episode_num": 0
},
{
"video_id": "7540121958432689458",
"episode_num": 0
},
{
"video_id": "7540121974115241267",
"episode_num": 0
},
{
"video_id": "7540121980037582126",
"episode_num": 0
},
{
"video_id": "7540121986127727922",
"episode_num": 0
},
{
"video_id": "7540121993606171931",
"episode_num": 0
},
{
"video_id": "7540121997993397550",
"episode_num": 0
},
{
"video_id": "7540121998798671150",
"episode_num": 0
}
],
"total_count": 20,
"last_update": "2025-10-22T09:55:40.062057",
"mix_name": "废柴仙尊在凡间逆袭"
}

View File

@ -0,0 +1,59 @@
{
"episodes": [
{
"video_id": "7520060104645823780",
"episode_num": 0
},
{
"video_id": "7526440964852911360",
"episode_num": 0
},
{
"video_id": "7531224944424897807",
"episode_num": 0
},
{
"video_id": "7536966604467342634",
"episode_num": 0
},
{
"video_id": "7540495853090245898",
"episode_num": 0
},
{
"video_id": "7546582568850754825",
"episode_num": 0
},
{
"video_id": "7550653403244875044",
"episode_num": 0
},
{
"video_id": "7553221689224711466",
"episode_num": 0
},
{
"video_id": "7555476643674869028",
"episode_num": 0
},
{
"video_id": "7557563705563581723",
"episode_num": 0
},
{
"video_id": "7561372042944105770",
"episode_num": 0
},
{
"video_id": "7563638353325821203",
"episode_num": 0
},
{
"video_id": "7564982296051338534",
"episode_num": 0
}
],
"total_count": 13,
"last_update": "2025-10-25T12:53:08.640840",
"mix_name": "暗黑神话《葫芦兄弟》大电影"
}

View File

@ -0,0 +1,335 @@
{
"episodes": [
{
"video_id": "7542848851212848399",
"episode_num": 0
},
{
"video_id": "7542848809219509539",
"episode_num": 0
},
{
"video_id": "7542848845315607848",
"episode_num": 0
},
{
"video_id": "7542848836981640482",
"episode_num": 0
},
{
"video_id": "7542848861065301248",
"episode_num": 0
},
{
"video_id": "7542848884834323746",
"episode_num": 0
},
{
"video_id": "7542848870422711587",
"episode_num": 0
},
{
"video_id": "7542848880208022824",
"episode_num": 0
},
{
"video_id": "7542848850625662223",
"episode_num": 0
},
{
"video_id": "7542848915637357858",
"episode_num": 0
},
{
"video_id": "7542848899275443508",
"episode_num": 0
},
{
"video_id": "7542848861832793344",
"episode_num": 0
},
{
"video_id": "7542848917688405248",
"episode_num": 0
},
{
"video_id": "7542848921295539490",
"episode_num": 0
},
{
"video_id": "7542848955193822498",
"episode_num": 0
},
{
"video_id": "7542848980061785359",
"episode_num": 0
},
{
"video_id": "7542848967848004898",
"episode_num": 0
},
{
"video_id": "7542848965998415104",
"episode_num": 0
},
{
"video_id": "7542848979311070504",
"episode_num": 0
},
{
"video_id": "7542848967923584308",
"episode_num": 0
},
{
"video_id": "7542848984814062848",
"episode_num": 0
},
{
"video_id": "7542848924772470050",
"episode_num": 0
},
{
"video_id": "7542849015503768847",
"episode_num": 0
},
{
"video_id": "7542849044486343970",
"episode_num": 0
},
{
"video_id": "7542849026937408820",
"episode_num": 0
},
{
"video_id": "7542849016988536064",
"episode_num": 0
},
{
"video_id": "7542849050052152576",
"episode_num": 0
},
{
"video_id": "7542849050303827234",
"episode_num": 0
},
{
"video_id": "7542849070256098560",
"episode_num": 0
},
{
"video_id": "7542849073359883554",
"episode_num": 0
},
{
"video_id": "7542849080590978339",
"episode_num": 0
},
{
"video_id": "7542849054955277620",
"episode_num": 0
},
{
"video_id": "7542849095237422376",
"episode_num": 0
},
{
"video_id": "7542849079081012495",
"episode_num": 0
},
{
"video_id": "7542849104309751092",
"episode_num": 0
},
{
"video_id": "7542849117307915554",
"episode_num": 0
},
{
"video_id": "7542849134680739106",
"episode_num": 0
},
{
"video_id": "7542849125507779892",
"episode_num": 0
},
{
"video_id": "7542849118029286691",
"episode_num": 0
},
{
"video_id": "7542849137717349684",
"episode_num": 0
},
{
"video_id": "7542849160806927650",
"episode_num": 0
},
{
"video_id": "7542849105559604520",
"episode_num": 0
},
{
"video_id": "7542849158164565263",
"episode_num": 0
},
{
"video_id": "7542849186220297487",
"episode_num": 0
},
{
"video_id": "7542849155958394146",
"episode_num": 0
},
{
"video_id": "7542849176644717839",
"episode_num": 0
},
{
"video_id": "7542849167325039924",
"episode_num": 0
},
{
"video_id": "7542849189500226850",
"episode_num": 0
},
{
"video_id": "7542849227328621839",
"episode_num": 0
},
{
"video_id": "7542849207896395008",
"episode_num": 0
},
{
"video_id": "7542849211025444111",
"episode_num": 0
},
{
"video_id": "7542849162627386639",
"episode_num": 0
},
{
"video_id": "7542849207703555328",
"episode_num": 0
},
{
"video_id": "7542849259792502031",
"episode_num": 0
},
{
"video_id": "7542849258521644323",
"episode_num": 0
},
{
"video_id": "7542849225709735183",
"episode_num": 0
},
{
"video_id": "7542849302184365364",
"episode_num": 0
},
{
"video_id": "7542849280399232271",
"episode_num": 0
},
{
"video_id": "7542849289127464232",
"episode_num": 0
},
{
"video_id": "7542849295699987747",
"episode_num": 0
},
{
"video_id": "7542849251013954850",
"episode_num": 0
},
{
"video_id": "7542849316457565475",
"episode_num": 0
},
{
"video_id": "7542849309453077794",
"episode_num": 0
},
{
"video_id": "7542849330223287604",
"episode_num": 0
},
{
"video_id": "7542849366608973071",
"episode_num": 0
},
{
"video_id": "7542849356844649743",
"episode_num": 0
},
{
"video_id": "7542849376066997544",
"episode_num": 0
},
{
"video_id": "7542849360560737536",
"episode_num": 0
},
{
"video_id": "7542849368232086819",
"episode_num": 0
},
{
"video_id": "7542849392085110050",
"episode_num": 0
},
{
"video_id": "7542849341845720335",
"episode_num": 0
},
{
"video_id": "7542849423311637795",
"episode_num": 0
},
{
"video_id": "7542849409881558287",
"episode_num": 0
},
{
"video_id": "7542849389480529167",
"episode_num": 0
},
{
"video_id": "7542849421172657442",
"episode_num": 0
},
{
"video_id": "7542849419784359168",
"episode_num": 0
},
{
"video_id": "7542849444547415348",
"episode_num": 0
},
{
"video_id": "7542849467016383778",
"episode_num": 0
},
{
"video_id": "7542849454877986048",
"episode_num": 0
},
{
"video_id": "7542849456861973760",
"episode_num": 0
},
{
"video_id": "7542849403191594255",
"episode_num": 0
},
{
"video_id": "7543842316474207529",
"episode_num": 0
}
],
"total_count": 82,
"last_update": "2025-10-22T09:56:26.875722",
"mix_name": "200斤王妃天天想和离Q版"
}

View File

@ -0,0 +1,59 @@
{
"episodes": [
{
"video_id": "7546071652224765234",
"episode_num": 0
},
{
"video_id": "7546071610189548827",
"episode_num": 0
},
{
"video_id": "7546071665881500954",
"episode_num": 0
},
{
"video_id": "7546071681165495561",
"episode_num": 0
},
{
"video_id": "7546071674475515145",
"episode_num": 0
},
{
"video_id": "7546071695342243123",
"episode_num": 0
},
{
"video_id": "7546071699012193587",
"episode_num": 0
},
{
"video_id": "7546071697456254234",
"episode_num": 0
},
{
"video_id": "7546071708185283878",
"episode_num": 0
},
{
"video_id": "7546071712543165723",
"episode_num": 0
},
{
"video_id": "7546071720415857947",
"episode_num": 0
},
{
"video_id": "7546071728376565043",
"episode_num": 0
},
{
"video_id": "7546071716959685939",
"episode_num": 0
}
],
"total_count": 13,
"last_update": "2025-10-22T09:55:35.570013",
"mix_name": "末日来袭"
}

View File

@ -0,0 +1,35 @@
{
"episodes": [
{
"video_id": "7545715527935610150",
"episode_num": 0
},
{
"video_id": "7546851498567716123",
"episode_num": 0
},
{
"video_id": "7548440120316218662",
"episode_num": 0
},
{
"video_id": "7549782003667782954",
"episode_num": 0
},
{
"video_id": "7551038580261391616",
"episode_num": 0
},
{
"video_id": "7552606420504464650",
"episode_num": 0
},
{
"video_id": "7555059788590206234",
"episode_num": 0
}
],
"total_count": 7,
"last_update": "2025-10-22T09:56:07.724877",
"mix_name": "《南城诡事》"
}

View File

@ -0,0 +1,283 @@
{
"episodes": [
{
"video_id": "7553983620604579118",
"episode_num": 0
},
{
"video_id": "7553983636282953011",
"episode_num": 0
},
{
"video_id": "7553983647192288563",
"episode_num": 0
},
{
"video_id": "7553983646374448411",
"episode_num": 0
},
{
"video_id": "7553983671213133107",
"episode_num": 0
},
{
"video_id": "7553983656532937994",
"episode_num": 0
},
{
"video_id": "7553983683934326067",
"episode_num": 0
},
{
"video_id": "7553983686446697737",
"episode_num": 0
},
{
"video_id": "7553983694885752110",
"episode_num": 0
},
{
"video_id": "7553983711918755122",
"episode_num": 0
},
{
"video_id": "7553983719787285770",
"episode_num": 0
},
{
"video_id": "7553983712300403978",
"episode_num": 0
},
{
"video_id": "7553983719380487450",
"episode_num": 0
},
{
"video_id": "7553983734438038830",
"episode_num": 0
},
{
"video_id": "7553983745687178546",
"episode_num": 0
},
{
"video_id": "7553983751466831150",
"episode_num": 0
},
{
"video_id": "7553983762921524530",
"episode_num": 0
},
{
"video_id": "7553983765584923914",
"episode_num": 0
},
{
"video_id": "7553983782336957723",
"episode_num": 0
},
{
"video_id": "7553983772199275785",
"episode_num": 0
},
{
"video_id": "7553983806231891209",
"episode_num": 0
},
{
"video_id": "7553983819997547803",
"episode_num": 0
},
{
"video_id": "7553983816046595366",
"episode_num": 0
},
{
"video_id": "7553983829485112627",
"episode_num": 0
},
{
"video_id": "7553983842286161178",
"episode_num": 0
},
{
"video_id": "7553983841656950042",
"episode_num": 0
},
{
"video_id": "7553983832865737993",
"episode_num": 0
},
{
"video_id": "7553983844874013978",
"episode_num": 0
},
{
"video_id": "7553983845054352678",
"episode_num": 0
},
{
"video_id": "7553983863744187699",
"episode_num": 0
},
{
"video_id": "7553983873361677618",
"episode_num": 0
},
{
"video_id": "7553983883587423514",
"episode_num": 0
},
{
"video_id": "7553983889304194330",
"episode_num": 0
},
{
"video_id": "7553983897294359818",
"episode_num": 0
},
{
"video_id": "7553983891145575707",
"episode_num": 0
},
{
"video_id": "7553983921776512266",
"episode_num": 0
},
{
"video_id": "7553983915900374299",
"episode_num": 0
},
{
"video_id": "7553983931104693542",
"episode_num": 0
},
{
"video_id": "7553983944425737523",
"episode_num": 0
},
{
"video_id": "7553983937924680998",
"episode_num": 0
},
{
"video_id": "7553983956136381705",
"episode_num": 0
},
{
"video_id": "7553983954274077961",
"episode_num": 0
},
{
"video_id": "7553983968228494602",
"episode_num": 0
},
{
"video_id": "7553983980819713326",
"episode_num": 0
},
{
"video_id": "7553983971994995995",
"episode_num": 0
},
{
"video_id": "7553983994157632814",
"episode_num": 0
},
{
"video_id": "7553983997076966706",
"episode_num": 0
},
{
"video_id": "7553984007311019315",
"episode_num": 0
},
{
"video_id": "7553984019042422026",
"episode_num": 0
},
{
"video_id": "7553984009651408137",
"episode_num": 0
},
{
"video_id": "7553984031956798770",
"episode_num": 0
},
{
"video_id": "7553984033814859054",
"episode_num": 0
},
{
"video_id": "7553984062147431690",
"episode_num": 0
},
{
"video_id": "7553984067348352266",
"episode_num": 0
},
{
"video_id": "7553984082489658675",
"episode_num": 0
},
{
"video_id": "7553984074881256714",
"episode_num": 0
},
{
"video_id": "7553984094493773094",
"episode_num": 0
},
{
"video_id": "7553984092258241830",
"episode_num": 0
},
{
"video_id": "7553984101271850267",
"episode_num": 0
},
{
"video_id": "7553984113179462950",
"episode_num": 0
},
{
"video_id": "7553984119844228379",
"episode_num": 0
},
{
"video_id": "7553984134859722035",
"episode_num": 0
},
{
"video_id": "7553984126999711022",
"episode_num": 0
},
{
"video_id": "7553984154476514587",
"episode_num": 0
},
{
"video_id": "7553984147111415049",
"episode_num": 0
},
{
"video_id": "7553984154166168870",
"episode_num": 0
},
{
"video_id": "7553984160797363502",
"episode_num": 0
},
{
"video_id": "7553984182108572955",
"episode_num": 0
},
{
"video_id": "7553984168468811046",
"episode_num": 0
}
],
"total_count": 69,
"last_update": "2025-10-22T09:56:34.803954",
"mix_name": "我在荒年当女帝"
}

View File

@ -0,0 +1,59 @@
{
"episodes": [
{
"video_id": "7554009575922355482",
"episode_num": 0
},
{
"video_id": "7554009571266809139",
"episode_num": 0
},
{
"video_id": "7554009591776840970",
"episode_num": 0
},
{
"video_id": "7554009593425218866",
"episode_num": 0
},
{
"video_id": "7554009609409662217",
"episode_num": 0
},
{
"video_id": "7554312903608913195",
"episode_num": 0
},
{
"video_id": "7554312915164187940",
"episode_num": 0
},
{
"video_id": "7554312925851176234",
"episode_num": 0
},
{
"video_id": "7554312927336041791",
"episode_num": 0
},
{
"video_id": "7554312932851617078",
"episode_num": 0
},
{
"video_id": "7555847841084706111",
"episode_num": 0
},
{
"video_id": "7555847841931955510",
"episode_num": 0
},
{
"video_id": "7559429054969957684",
"episode_num": 0
}
],
"total_count": 13,
"last_update": "2025-10-22T09:56:17.655969",
"mix_name": "传武"
}

View File

@ -0,0 +1,87 @@
{
"episodes": [
{
"video_id": "7554750058999188776",
"episode_num": 0
},
{
"video_id": "7554750089743355151",
"episode_num": 0
},
{
"video_id": "7554750101374143784",
"episode_num": 0
},
{
"video_id": "7554750092658429199",
"episode_num": 0
},
{
"video_id": "7554750091874045224",
"episode_num": 0
},
{
"video_id": "7554750074610339107",
"episode_num": 0
},
{
"video_id": "7554750093157600512",
"episode_num": 0
},
{
"video_id": "7554750110882729251",
"episode_num": 0
},
{
"video_id": "7554751305978268963",
"episode_num": 0
},
{
"video_id": "7554750135679356195",
"episode_num": 0
},
{
"video_id": "7554750126988872995",
"episode_num": 0
},
{
"video_id": "7554750136304422144",
"episode_num": 0
},
{
"video_id": "7554750151248694568",
"episode_num": 0
},
{
"video_id": "7554750138007276835",
"episode_num": 0
},
{
"video_id": "7554750160660679970",
"episode_num": 0
},
{
"video_id": "7554750166654356770",
"episode_num": 0
},
{
"video_id": "7554750176326454562",
"episode_num": 0
},
{
"video_id": "7554750194152197416",
"episode_num": 0
},
{
"video_id": "7554750204159773967",
"episode_num": 0
},
{
"video_id": "7554750213030776064",
"episode_num": 0
}
],
"total_count": 20,
"last_update": "2025-10-22T09:55:34.882918",
"mix_name": "我的治愈系游戏"
}

View File

@ -0,0 +1,23 @@
{
"episodes": [
{
"video_id": "7555012459468295464",
"episode_num": 0
},
{
"video_id": "7555402801766092086",
"episode_num": 0
},
{
"video_id": "7555406069112130856",
"episode_num": 0
},
{
"video_id": "7559152702782049576",
"episode_num": 0
}
],
"total_count": 4,
"last_update": "2025-10-22T09:55:32.571577",
"mix_name": "我在山海世界当神仙奶奶"
}

View File

@ -0,0 +1,215 @@
{
"episodes": [
{
"video_id": "7555848626619043087",
"episode_num": 0
},
{
"video_id": "7555848620394745103",
"episode_num": 0
},
{
"video_id": "7555848626723884288",
"episode_num": 0
},
{
"video_id": "7555848639554374947",
"episode_num": 0
},
{
"video_id": "7555848651201908020",
"episode_num": 0
},
{
"video_id": "7555848646504303872",
"episode_num": 0
},
{
"video_id": "7555848634936479016",
"episode_num": 0
},
{
"video_id": "7555848658214833443",
"episode_num": 0
},
{
"video_id": "7555848701277670656",
"episode_num": 0
},
{
"video_id": "7555848676313206050",
"episode_num": 0
},
{
"video_id": "7555848687017151759",
"episode_num": 0
},
{
"video_id": "7555848690846551330",
"episode_num": 0
},
{
"video_id": "7555848704461196544",
"episode_num": 0
},
{
"video_id": "7555848729824152832",
"episode_num": 0
},
{
"video_id": "7555848716045847842",
"episode_num": 0
},
{
"video_id": "7555848730759531828",
"episode_num": 0
},
{
"video_id": "7555848731841662242",
"episode_num": 0
},
{
"video_id": "7555848757745618228",
"episode_num": 0
},
{
"video_id": "7555848764313947426",
"episode_num": 0
},
{
"video_id": "7555848763940572456",
"episode_num": 0
},
{
"video_id": "7555848769703644468",
"episode_num": 0
},
{
"video_id": "7555848788842171663",
"episode_num": 0
},
{
"video_id": "7555848805493558562",
"episode_num": 0
},
{
"video_id": "7555848796584938787",
"episode_num": 0
},
{
"video_id": "7555848822841232655",
"episode_num": 0
},
{
"video_id": "7555848807460752675",
"episode_num": 0
},
{
"video_id": "7555848826909625634",
"episode_num": 0
},
{
"video_id": "7555848831548525859",
"episode_num": 0
},
{
"video_id": "7555848844282449204",
"episode_num": 0
},
{
"video_id": "7555848849298820367",
"episode_num": 0
},
{
"video_id": "7555848861130951970",
"episode_num": 0
},
{
"video_id": "7555848854726348072",
"episode_num": 0
},
{
"video_id": "7555848849340910882",
"episode_num": 0
},
{
"video_id": "7555848876754849064",
"episode_num": 0
},
{
"video_id": "7555848903296371968",
"episode_num": 0
},
{
"video_id": "7555848872417971471",
"episode_num": 0
},
{
"video_id": "7555848898049363234",
"episode_num": 0
},
{
"video_id": "7555848895910235426",
"episode_num": 0
},
{
"video_id": "7555848919880699136",
"episode_num": 0
},
{
"video_id": "7555848946078207267",
"episode_num": 0
},
{
"video_id": "7555848941703613711",
"episode_num": 0
},
{
"video_id": "7555848946078305571",
"episode_num": 0
},
{
"video_id": "7555848977900375336",
"episode_num": 0
},
{
"video_id": "7555848968379419939",
"episode_num": 0
},
{
"video_id": "7555848963484683572",
"episode_num": 0
},
{
"video_id": "7559890467404434724",
"episode_num": 0
},
{
"video_id": "7555848989376089359",
"episode_num": 0
},
{
"video_id": "7555848987807386880",
"episode_num": 0
},
{
"video_id": "7555848993603898664",
"episode_num": 0
},
{
"video_id": "7555849016483777827",
"episode_num": 0
},
{
"video_id": "7555849024037801216",
"episode_num": 0
},
{
"video_id": "7555849027057667368",
"episode_num": 0
}
],
"total_count": 52,
"last_update": "2025-10-22T09:55:47.709471",
"mix_name": "婉心计"
}

View File

@ -0,0 +1,251 @@
{
"episodes": [
{
"video_id": "7556193043586600201",
"episode_num": 0
},
{
"video_id": "7556193049286675762",
"episode_num": 0
},
{
"video_id": "7556193069050105139",
"episode_num": 0
},
{
"video_id": "7556193070857899302",
"episode_num": 0
},
{
"video_id": "7556193075085741350",
"episode_num": 0
},
{
"video_id": "7556193078072134962",
"episode_num": 0
},
{
"video_id": "7556193079070379310",
"episode_num": 0
},
{
"video_id": "7556193078973975818",
"episode_num": 0
},
{
"video_id": "7556193108963118382",
"episode_num": 0
},
{
"video_id": "7556193112775822638",
"episode_num": 0
},
{
"video_id": "7556193121483115802",
"episode_num": 0
},
{
"video_id": "7556193118438067494",
"episode_num": 0
},
{
"video_id": "7556193118383623462",
"episode_num": 0
},
{
"video_id": "7556193123634908462",
"episode_num": 0
},
{
"video_id": "7556193146061868338",
"episode_num": 0
},
{
"video_id": "7556193153837960458",
"episode_num": 0
},
{
"video_id": "7556193142836448521",
"episode_num": 0
},
{
"video_id": "7556193162352512282",
"episode_num": 0
},
{
"video_id": "7556193174033616138",
"episode_num": 0
},
{
"video_id": "7556193192723402034",
"episode_num": 0
},
{
"video_id": "7556193187744845107",
"episode_num": 0
},
{
"video_id": "7556193208854646025",
"episode_num": 0
},
{
"video_id": "7556193223312510254",
"episode_num": 0
},
{
"video_id": "7556193225766112550",
"episode_num": 0
},
{
"video_id": "7556193223954287922",
"episode_num": 0
},
{
"video_id": "7556193234033069338",
"episode_num": 0
},
{
"video_id": "7556193250365771054",
"episode_num": 0
},
{
"video_id": "7556193252056059182",
"episode_num": 0
},
{
"video_id": "7556193259912006963",
"episode_num": 0
},
{
"video_id": "7556193260188847369",
"episode_num": 0
},
{
"video_id": "7556193250890075418",
"episode_num": 0
},
{
"video_id": "7556193288026361114",
"episode_num": 0
},
{
"video_id": "7556193288236190985",
"episode_num": 0
},
{
"video_id": "7556193301905345802",
"episode_num": 0
},
{
"video_id": "7556193302542961970",
"episode_num": 0
},
{
"video_id": "7556193321299823881",
"episode_num": 0
},
{
"video_id": "7556193328434269450",
"episode_num": 0
},
{
"video_id": "7556193321668922633",
"episode_num": 0
},
{
"video_id": "7556193341751266569",
"episode_num": 0
},
{
"video_id": "7556193355420405019",
"episode_num": 0
},
{
"video_id": "7556193355697458482",
"episode_num": 0
},
{
"video_id": "7556193360436841737",
"episode_num": 0
},
{
"video_id": "7556193361883974921",
"episode_num": 0
},
{
"video_id": "7556193358012534025",
"episode_num": 0
},
{
"video_id": "7556193378002668846",
"episode_num": 0
},
{
"video_id": "7556193386659728691",
"episode_num": 0
},
{
"video_id": "7556193404892253478",
"episode_num": 0
},
{
"video_id": "7556193406922312970",
"episode_num": 0
},
{
"video_id": "7556193422680329498",
"episode_num": 0
},
{
"video_id": "7556193423468842266",
"episode_num": 0
},
{
"video_id": "7556193432524410158",
"episode_num": 0
},
{
"video_id": "7556193450471804187",
"episode_num": 0
},
{
"video_id": "7556193451281222939",
"episode_num": 0
},
{
"video_id": "7556193461007928586",
"episode_num": 0
},
{
"video_id": "7556193466187861294",
"episode_num": 0
},
{
"video_id": "7556193487763328265",
"episode_num": 0
},
{
"video_id": "7556193490787421467",
"episode_num": 0
},
{
"video_id": "7556193478774918410",
"episode_num": 0
},
{
"video_id": "7556193479311887625",
"episode_num": 0
},
{
"video_id": "7556193503307484425",
"episode_num": 0
},
{
"video_id": "7556193515902995721",
"episode_num": 0
}
],
"total_count": 61,
"last_update": "2025-10-22T09:56:50.400526",
"mix_name": "我靠唱歌打脸全团"
}

View File

@ -0,0 +1,115 @@
{
"episodes": [
{
"video_id": "7497105467341147428",
"episode_num": 0
},
{
"video_id": "7501492304370601250",
"episode_num": 0
},
{
"video_id": "7502772081621273894",
"episode_num": 0
},
{
"video_id": "7503839169832062259",
"episode_num": 0
},
{
"video_id": "7504986016153062656",
"episode_num": 0
},
{
"video_id": "7506112308838403343",
"episode_num": 0
},
{
"video_id": "7506782177625394432",
"episode_num": 0
},
{
"video_id": "7507204856379378959",
"episode_num": 0
},
{
"video_id": "7507541015005744424",
"episode_num": 0
},
{
"video_id": "7507946206644210959",
"episode_num": 0
},
{
"video_id": "7508945450654141731",
"episode_num": 0
},
{
"video_id": "7514129646461078799",
"episode_num": 0
},
{
"video_id": "7519344535044066560",
"episode_num": 0
},
{
"video_id": "7521994647779970338",
"episode_num": 0
},
{
"video_id": "7524264312450698536",
"episode_num": 0
},
{
"video_id": "7527207390493723938",
"episode_num": 0
},
{
"video_id": "7531374405576248591",
"episode_num": 0
},
{
"video_id": "7532778856979451136",
"episode_num": 0
},
{
"video_id": "7535583078481136911",
"episode_num": 0
},
{
"video_id": "7536884524291263759",
"episode_num": 0
},
{
"video_id": "7538866617654365480",
"episode_num": 0
},
{
"video_id": "7539751142214438144",
"episode_num": 0
},
{
"video_id": "7549904246364376335",
"episode_num": 0
},
{
"video_id": "7554992404740214016",
"episode_num": 0
},
{
"video_id": "7555715283915312384",
"episode_num": 0
},
{
"video_id": "7558050978226982179",
"episode_num": 0
},
{
"video_id": "7560551213957500195",
"episode_num": 0
}
],
"total_count": 27,
"last_update": "2025-10-22T09:56:16.947762",
"mix_name": "绝境逆袭"
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,562 @@
from typing import Any, Optional
import mimetypes
from io import StringIO
import os
import tos
import urllib3
from urllib3.exceptions import InsecureRequestWarning
from config import API_CONFIG
# 火山对象存储
class TOSClient:
def __init__(
self,
access_key_id: str,
access_key_secret: str,
endpoint: str,
region: str,
bucket_name: str,
self_domain: str,
disable_ssl_warnings: bool = True
):
"""
初始化OSS客户端
Args:
access_key_id: ak
access_key_secret: sk
endpoint: OSS访问端点 (: https://oss-cn-hangzhou.aliyuncs.com)
bucket_name: 存储桶名称
self_domain: 自定义域名
disable_ssl_warnings: 是否禁用SSL警告
"""
# 禁用SSL警告如果需要
if disable_ssl_warnings:
urllib3.disable_warnings(InsecureRequestWarning)
sts_token: str = "token_test"
self.bucket_name = bucket_name
self.self_domain = self_domain
self.endpoint = endpoint
self.client = tos.TosClientV2(
ak=access_key_id,
sk=access_key_secret,
endpoint=self_domain,
region=region,
is_custom_domain=True,
# bucket_name,
# security_token=sts_token,
connection_time=30, socket_timeout=60, max_retry_count=3
)
def get_base_url(self, object_key: str) -> str:
"""获取基础URL不带签名参数"""
# endpoint = self.endpoint.replace('https://', '').replace('http://', '')
return f"https://{self.self_domain}/{object_key}"
def generate_url(self, object_key: str, expires: int = 3600) -> str:
"""生成带签名的临时访问URL"""
# 生成签名URL
pre_signed_url_output = self.client.pre_signed_url(
tos.HttpMethodType.Http_Method_Get,
bucket=self.bucket_name,
key=object_key,
expires=expires)
return pre_signed_url_output.signed_url
def upload_string(
self,
content_str: str,
object_key: str,
headers: Optional[dict] = None,
return_url: bool = True,
) -> str:
"""
上传本地文件到OSS
Args:
local_file_path: 本地文件路径
object_key: OSS对象键(路径)如果为None则使用本地文件名
headers: 自定义HTTP头
Returns:
str: 文件在OSS的公开URL
Raises:
Exception: 如果上传失败
"""
try:
# if headers is None:
# headers = {}
# if content_type and 'Content-Type' not in headers:
# headers['Content-Type'] = content_type
content = StringIO(content_str)
result = self.client.put_object(
bucket=self.bucket_name,
key=object_key,
content_type='text/plain',
content=content,
)
# HTTP状态码
print('upload_string http status code:{}'.format(result.status_code))
# 请求ID。请求ID是本次请求的唯一标识建议在日志中添加此参数
# print('request_id: {}'.format(result.request_id))
# hash_crc64_ecma 表示该对象的64位CRC值, 可用于验证上传对象的完整性
# print('crc64: {}'.format(result.hash_crc64_ecma))
if result.status_code != 200:
raise Exception(f"上传失败HTTP状态码: {result.status_code}")
return self.get_base_url(object_key) if return_url else object_key # 修改返回逻辑
except Exception as e:
raise Exception(f"上传文件到OSS失败: {str(e)}")
def upload_file(
self,
local_file_path: str,
object_key: Optional[str] = None,
headers: Optional[dict] = None,
return_url: bool = True,
expires: int = 3600 # 新增参数默认1小时
) -> str:
"""
上传本地文件到OSS
Args:
local_file_path: 本地文件路径
object_key: OSS对象键(路径)如果为None则使用本地文件名
headers: 自定义HTTP头
Returns:
str: 文件在OSS的公开URL
Raises:
Exception: 如果上传失败
"""
if not os.path.exists(local_file_path):
raise FileNotFoundError(f"本地文件不存在: {local_file_path}")
# 如果没有指定object_key则使用文件名
if object_key is None:
object_key = os.path.basename(local_file_path)
# 自动设置Content-Type
content_type, _ = mimetypes.guess_type(local_file_path)
try:
# file_name为本地文件的完整路径。
result = self.client.put_object_from_file(
bucket=self.bucket_name,
key=object_key,
content_type=content_type or '',
file_path=local_file_path,
)
if result.status_code != 200:
raise Exception(f"上传失败HTTP状态码: {result.status_code}")
return self.get_base_url(object_key) if return_url else object_key # 修改返回逻辑
except Exception as e:
raise Exception(f"上传文件到OSS失败: {str(e)}")
def upload_bytes(
self,
data: bytes,
object_key: str,
content_type: Optional[str] = None,
headers: Optional[dict] = None,
return_url: bool = True,
expires: int = 3600 # 新增参数
) -> str:
"""
上传字节数据到OSS
Args:
data: 要上传的字节数据
object_key: OSS对象键(路径)
content_type: 内容类型 (: image/jpeg)
headers: 自定义HTTP头
Returns:
str: 文件在OSS的公开URL
Raises:
Exception: 如果上传失败
"""
try:
result = self.client.put_object(
bucket=self.bucket_name,
key=object_key,
content_type=content_type or 'application/octet-stream',
content=data,
)
if result.status_code != 200:
raise Exception(f"上传失败HTTP状态码: {result.status_code}")
return self.get_base_url(object_key) if return_url else object_key # 修改返回逻辑
except Exception as e:
raise Exception(f"上传字节数据到OSS失败: {str(e)}")
def upload_from_url(
self,
url: str,
object_key: str,
headers: Optional[dict] = None,
timeout: int = 30,
return_url: bool = True,
expires: int = 3600 # 新增参数
) -> str:
"""
从网络URL下载文件并上传到OSS
Args:
url: 网络文件URL
object_key: OSS对象键(路径)
headers: 自定义HTTP头
timeout: 下载超时时间()
return_url: 是否返回完整URL
Returns:
str: 文件在OSS的公开URL或object_key
Raises:
Exception: 如果下载或上传失败
"""
import requests
from io import BytesIO
if not url.startswith(('http://', 'https://')):
raise ValueError("URL必须以http://或https://开头")
try:
# 下载文件
response = requests.get(url, stream=True, timeout=timeout)
response.raise_for_status()
# 获取内容类型
content_type = response.headers.get('Content-Type', '')
if not content_type:
content_type = mimetypes.guess_type(url)[0] or 'application/octet-stream'
# 上传到OSS
return self.upload_bytes(
data=response.content,
object_key=object_key,
content_type=content_type,
headers=headers,
return_url=return_url,
expires=expires # 传递参数
)
except requests.exceptions.RequestException as e:
raise Exception(f"下载网络文件失败: {str(e)}")
except Exception as e:
raise Exception(f"上传网络文件到OSS失败: {str(e)}")
def _format_object_key(self, object_key: str) -> str:
"""
格式化OSS对象键(路径)
"""
# 如果object_key包含self_domain截取self_domain后面的字符作为新的object_key
if self.self_domain and self.self_domain in object_key:
# 找到self_domain在object_key中的位置截取后面的部分
domain_index = object_key.find(self.self_domain)
if domain_index != -1:
# 截取self_domain后面的部分去掉开头的斜杠
object_key = object_key[domain_index + len(self.self_domain):].lstrip('/')
return object_key
# 删除文件
def delete_file(self, object_key: str) -> bool:
"""
删除OSS上的文件
Args:
object_key: OSS对象键(路径)
Returns:
bool: 删除是否成功
"""
try:
self.client.delete_object(
bucket=self.bucket_name,
key=self._format_object_key(object_key),
)
return True
except Exception as e:
print(f"删除文件失败: {str(e)}")
return False
def download_file(self, object_key: str) -> bytes:
"""
从TOS下载文件并返回文件数据
Args:
object_key: OSS对象键(路径)
Returns:
bytes: 文件的字节数据
Raises:
Exception: 如果下载失败
"""
try:
object_key = self._format_object_key(object_key)
object_stream = self.client.get_object(
bucket=self.bucket_name,
key=object_key,
)
content = object_stream.read() or b''
if not content:
raise Exception(f"文件内容为空: {object_key}")
return content
except tos.exceptions.TosClientError as e:
# 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
print('TOS下载 fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
raise Exception(f"下载异常: {object_key} {e.message}")
except tos.exceptions.TosServerError as e:
# 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
print('TOS下载 fail with server error, code: {}'.format(e.code))
# request id 可定位具体问题,强烈建议日志中保存
print('TOS下载 error with request id: {}'.format(e.request_id))
print('TOS下载 error with message: {}'.format(e.message))
print('TOS下载 error with http code: {}'.format(e.status_code))
print('TOS下载 error with ec: {}'.format(e.ec))
print('TOS下载 error with request url: {}'.format(e.request_url))
raise Exception(f"下载异常: {object_key} {e.message}")
except Exception as e:
raise Exception(f"下载文件失败: {str(e)}")
class TOSChunkUploader:
"""TOS分片上传类"""
def __init__(self, tos_client: TOSClient):
"""
初始化分片上传器
Args:
tos_client: TOS客户端实例
"""
self.client = tos_client.client
self.bucket_name = tos_client.bucket_name
self.self_domain = tos_client.self_domain
def init_multipart_upload(self, object_key: str, content_type: Optional[str] = None) -> Optional[str]:
"""
初始化分片上传
Args:
object_key: 对象键
content_type: 内容类型
Returns:
str: 上传ID
Raises:
Exception: 如果初始化失败
"""
try:
# 设置默认内容类型
if not content_type:
content_type = mimetypes.guess_type(object_key)[0] or 'application/octet-stream'
# 初始化分片上传
result = self.client.create_multipart_upload(
bucket=self.bucket_name,
key=object_key,
content_type=content_type
)
return result.upload_id
except tos.exceptions.TosClientError as e:
raise Exception(f"初始化分片上传失败(客户端错误): {e.message}")
except tos.exceptions.TosServerError as e:
raise Exception(f"初始化分片上传失败(服务端错误): {e.message}")
except Exception as e:
raise Exception(f"初始化分片上传失败: {str(e)}")
def upload_part(self, object_key: str, upload_id: str, part_number: int, data: bytes) -> dict:
"""
上传分片
Args:
object_key: 对象键
upload_id: 上传ID
part_number: 分片号(从1开始)
data: 分片数据
Returns:
dict: 包含完整分片信息的字典
Raises:
Exception: 如果上传失败
"""
try:
from io import BytesIO
import hashlib
# 计算分片大小
part_size = len(data)
# 计算CRC64如果需要的话这里先设为None
hash_crc64_ecma = None
# 上传分片
result = self.client.upload_part(
bucket=self.bucket_name,
key=object_key,
upload_id=upload_id,
part_number=part_number,
content=BytesIO(data)
)
return {
'part_number': part_number,
'etag': result.etag,
'part_size': part_size,
'hash_crc64_ecma': hash_crc64_ecma,
'is_completed': True
}
except tos.exceptions.TosClientError as e:
raise Exception(f"上传分片失败(客户端错误): {e.message}")
except tos.exceptions.TosServerError as e:
raise Exception(f"上传分片失败(服务端错误): {e.message}")
except Exception as e:
raise Exception(f"上传分片失败: {str(e)}")
def complete_multipart_upload(self, object_key: str, upload_id: str, parts: list) -> str:
"""
完成分片上传
Args:
object_key: 对象键
upload_id: 上传ID
parts: 分片信息列表每个元素包含part_number和etag
Returns:
str: 文件的完整URL
Raises:
Exception: 如果完成上传失败
"""
try:
# 按分片号排序
sorted_parts = sorted(parts, key=lambda x: x['part_number'])
# 构建分片列表并计算偏移量
part_list = []
current_offset = 0
for part in sorted_parts:
part_list.append(tos.models2.PartInfo(
part_number=part['part_number'],
etag=part['etag'],
part_size=part.get('part_size'),
offset=current_offset,
hash_crc64_ecma=part.get('hash_crc64_ecma'),
is_completed=part.get('is_completed', True)
))
# 更新偏移量
if part.get('part_size'):
current_offset += part['part_size']
# 完成分片上传
result = self.client.complete_multipart_upload(
bucket=self.bucket_name,
key=object_key,
upload_id=upload_id,
parts=part_list
)
# 返回完整URL
return f"https://{self.self_domain}/{object_key}"
except tos.exceptions.TosClientError as e:
raise Exception(f"完成分片上传失败(客户端错误): {e.message}")
except tos.exceptions.TosServerError as e:
raise Exception(f"完成分片上传失败(服务端错误): {e.message}")
except Exception as e:
raise Exception(f"完成分片上传失败: {str(e)}")
def abort_multipart_upload(self, object_key: str, upload_id: str) -> bool:
"""
取消分片上传
Args:
object_key: 对象键
upload_id: 上传ID
Returns:
bool: 是否取消成功
"""
try:
self.client.abort_multipart_upload(
bucket=self.bucket_name,
key=object_key,
upload_id=upload_id
)
return True
except tos.exceptions.TosClientError as e:
print(f"取消分片上传失败(客户端错误): {e.message}")
return False
except tos.exceptions.TosServerError as e:
print(f"取消分片上传失败(服务端错误): {e.message}")
return False
except Exception as e:
print(f"取消分片上传失败: {str(e)}")
return False
def list_parts(self, object_key: str, upload_id: str) -> list:
"""
列出已上传的分片
Args:
object_key: 对象键
upload_id: 上传ID
Returns:
list: 已上传的分片列表
"""
try:
result = self.client.list_parts(
bucket=self.bucket_name,
key=object_key,
upload_id=upload_id
)
parts = []
for part in result.parts:
parts.append({
'part_number': part.part_number,
'etag': part.etag,
'size': part.size,
'last_modified': part.last_modified
})
return parts
except Exception as e:
print(f"列出分片失败: {str(e)}")
return []
# 创建OSS客户端
from config import TOS_CONFIG
oss_client = TOSClient(
access_key_id=TOS_CONFIG['access_key_id'],
access_key_secret=TOS_CONFIG['access_key_secret'],
endpoint=TOS_CONFIG['endpoint'],
region=TOS_CONFIG['region'],
bucket_name=TOS_CONFIG['bucket_name'],
self_domain=TOS_CONFIG['self_domain'],
disable_ssl_warnings=TOS_CONFIG['disable_ssl_warnings']
)
# 创建分片上传器
chunk_uploader = TOSChunkUploader(oss_client)

View File

@ -129,7 +129,14 @@ def format_mix_item(doc):
"request_id": doc.get("request_id", ""), "request_id": doc.get("request_id", ""),
"rank": doc.get("rank", 0), "rank": doc.get("rank", 0),
"cover_image_url": doc.get("cover_image_url", ""), "cover_image_url": doc.get("cover_image_url", ""),
"cover_backup_urls": doc.get("cover_backup_urls", []) # 新增字段
"series_author": doc.get("series_author", ""),
"desc": doc.get("desc", ""),
"updated_to_episode": doc.get("updated_to_episode", 0),
"cover_backup_urls": doc.get("cover_backup_urls", []),
"mix_id": doc.get("mix_id", ""),
"episode_video_ids": doc.get("episode_video_ids", []),
"episode_details": doc.get("episode_details", [])
} }
def get_mix_list(page=1, limit=20, sort_by="playcount"): def get_mix_list(page=1, limit=20, sort_by="playcount"):

View File

@ -42,8 +42,7 @@
"play_vv": 120000000, "play_vv": 120000000,
"request_id": "request_xxx", "request_id": "request_xxx",
"rank": 1, "rank": 1,
"cover_image_url": "https://p3.douyinpic.com/xxx", "cover_image_url": "https://p3.douyinpic.com/xxx"
"cover_backup_urls": ["url1", "url2"]
} }
``` ```
@ -199,7 +198,6 @@ GET /api/rank/videos?page=1&limit=20&sort=growth&start_date=2025-10-16&end_date=
"request_id": "request_xxx", "request_id": "request_xxx",
"rank": 1, "rank": 1,
"cover_image_url": "https://p3.douyinpic.com/xxx", "cover_image_url": "https://p3.douyinpic.com/xxx",
"cover_backup_urls": ["url1", "url2"],
"growth": 5000000, "growth": 5000000,
"growth_rate": 4.35 "growth_rate": 4.35
} }
@ -252,8 +250,7 @@ GET /api/rank/top?limit=10
"play_vv": 120000000, "play_vv": 120000000,
"request_id": "request_xxx", "request_id": "request_xxx",
"rank": 1, "rank": 1,
"cover_image_url": "https://p3.douyinpic.com/xxx", "cover_image_url": "https://p3.douyinpic.com/xxx"
"cover_backup_urls": ["url1", "url2"]
} }
], ],
"total": 10, "total": 10,
@ -297,8 +294,7 @@ GET /api/rank/search?q=关键词&page=1&limit=10
"play_vv": 120000000, "play_vv": 120000000,
"request_id": "request_xxx", "request_id": "request_xxx",
"rank": 1, "rank": 1,
"cover_image_url": "https://p3.douyinpic.com/xxx", "cover_image_url": "https://p3.douyinpic.com/xxx"
"cover_backup_urls": ["url1", "url2"]
} }
], ],
"keyword": "关键词", "keyword": "关键词",
@ -347,8 +343,7 @@ GET /api/rank/detail?id=674f1234567890abcdef
"play_vv": 120000000, "play_vv": 120000000,
"request_id": "request_xxx", "request_id": "request_xxx",
"rank": 1, "rank": 1,
"cover_image_url": "https://p3.douyinpic.com/xxx", "cover_image_url": "https://p3.douyinpic.com/xxx"
"cover_backup_urls": ["url1", "url2"]
}, },
"update_time": "2025-10-17 15:30:00" "update_time": "2025-10-17 15:30:00"
} }
@ -887,8 +882,7 @@ wx.request({
- 提供搜索建议 - 提供搜索建议
### 3. 图片加载 ### 3. 图片加载
- 优先使用 `cover_image_url` - 使用 `cover_image_url` 作为封面图片
- 备用 `cover_backup_urls` 作为备选
- 添加图片加载失败处理 - 添加图片加载失败处理
### 4. 数据更新 ### 4. 数据更新