feat:支持订阅绑定类别和自定义识别词

This commit is contained in:
jxxghp
2024-10-09 15:21:32 +08:00
parent e31df15b5e
commit 7ea01c1109
13 changed files with 191 additions and 124 deletions

View File

@@ -207,7 +207,8 @@ class DownloadChain(ChainBase):
save_path: str = None, save_path: str = None,
userid: Union[str, int] = None, userid: Union[str, int] = None,
username: str = None, username: str = None,
downloader: str = None) -> Optional[str]: downloader: str = None,
media_category: str = None) -> Optional[str]:
""" """
下载及发送通知 下载及发送通知
:param context: 资源上下文 :param context: 资源上下文
@@ -219,6 +220,7 @@ class DownloadChain(ChainBase):
:param userid: 用户ID :param userid: 用户ID
:param username: 调用下载的用户名/插件名 :param username: 调用下载的用户名/插件名
:param downloader: 下载器 :param downloader: 下载器
:param media_category: 自定义媒体类别
""" """
_torrent = context.torrent_info _torrent = context.torrent_info
_media = context.media_info _media = context.media_info
@@ -318,7 +320,8 @@ class DownloadChain(ChainBase):
userid=userid, userid=userid,
username=username, username=username,
channel=channel.value if channel else None, channel=channel.value if channel else None,
date=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) date=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
media_category=media_category
) )
# 登记下载文件 # 登记下载文件
@@ -382,7 +385,8 @@ class DownloadChain(ChainBase):
channel: MessageChannel = None, channel: MessageChannel = None,
source: str = None, source: str = None,
userid: str = None, userid: str = None,
username: str = None username: str = None,
media_category: str = None
) -> Tuple[List[Context], Dict[Union[int, str], Dict[int, NotExistMediaInfo]]]: ) -> Tuple[List[Context], Dict[Union[int, str], Dict[int, NotExistMediaInfo]]]:
""" """
根据缺失数据,自动种子列表中组合择优下载 根据缺失数据,自动种子列表中组合择优下载
@@ -393,6 +397,7 @@ class DownloadChain(ChainBase):
:param source: 通知来源 :param source: 通知来源
:param userid: 用户ID :param userid: 用户ID
:param username: 调用下载的用户名/插件名 :param username: 调用下载的用户名/插件名
:param media_category: 自定义媒体类别
:return: 已经下载的资源列表、剩余未下载到的剧集 no_exists[tmdb_id/douban_id] = {season: NotExistMediaInfo} :return: 已经下载的资源列表、剩余未下载到的剧集 no_exists[tmdb_id/douban_id] = {season: NotExistMediaInfo}
""" """
# 已下载的项目 # 已下载的项目
@@ -461,7 +466,8 @@ class DownloadChain(ChainBase):
if context.media_info.type == MediaType.MOVIE: if context.media_info.type == MediaType.MOVIE:
logger.info(f"开始下载电影 {context.torrent_info.title} ...") logger.info(f"开始下载电影 {context.torrent_info.title} ...")
if self.download_single(context, save_path=save_path, channel=channel, if self.download_single(context, save_path=save_path, channel=channel,
source=source, userid=userid, username=username): source=source, userid=userid, username=username,
media_category=media_category):
# 下载成功 # 下载成功
logger.info(f"{context.torrent_info.title} 添加下载成功") logger.info(f"{context.torrent_info.title} 添加下载成功")
downloaded_list.append(context) downloaded_list.append(context)
@@ -543,14 +549,16 @@ class DownloadChain(ChainBase):
channel=channel, channel=channel,
source=source, source=source,
userid=userid, userid=userid,
username=username username=username,
media_category=media_category
) )
else: else:
# 下载 # 下载
logger.info(f"开始下载 {torrent.title} ...") logger.info(f"开始下载 {torrent.title} ...")
download_id = self.download_single(context, save_path=save_path, download_id = self.download_single(context, save_path=save_path,
channel=channel, source=source, channel=channel, source=source,
userid=userid, username=username) userid=userid, username=username,
media_category=media_category)
if download_id: if download_id:
# 下载成功 # 下载成功
@@ -618,7 +626,8 @@ class DownloadChain(ChainBase):
logger.info(f"开始下载 {meta.title} ...") logger.info(f"开始下载 {meta.title} ...")
download_id = self.download_single(context, save_path=save_path, download_id = self.download_single(context, save_path=save_path,
channel=channel, source=source, channel=channel, source=source,
userid=userid, username=username) userid=userid, username=username,
media_category=media_category)
if download_id: if download_id:
# 下载成功 # 下载成功
logger.info(f"{meta.title} 添加下载成功") logger.info(f"{meta.title} 添加下载成功")
@@ -704,7 +713,8 @@ class DownloadChain(ChainBase):
channel=channel, channel=channel,
source=source, source=source,
userid=userid, userid=userid,
username=username username=username,
media_category=media_category
) )
if not download_id: if not download_id:
continue continue

View File

@@ -1,8 +1,7 @@
import json import copy
import random import random
import time import time
from datetime import datetime from datetime import datetime
from json import JSONDecodeError
from typing import Dict, List, Optional, Union, Tuple from typing import Dict, List, Optional, Union, Tuple
from app.chain import ChainBase from app.chain import ChainBase
@@ -332,9 +331,11 @@ class SubscribeChain(ChainBase):
# 优先级过滤规则 # 优先级过滤规则
if subscribe.best_version: if subscribe.best_version:
rule_groups = self.systemconfig.get(SystemConfigKey.BestVersionFilterRuleGroups) rule_groups = subscribe.filter_groups \
or self.systemconfig.get(SystemConfigKey.BestVersionFilterRuleGroups)
else: else:
rule_groups = self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups) rule_groups = subscribe.filter_groups \
or self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups)
# 搜索,同时电视剧会过滤掉不需要的剧集 # 搜索,同时电视剧会过滤掉不需要的剧集
contexts = self.searchchain.process(mediainfo=mediainfo, contexts = self.searchchain.process(mediainfo=mediainfo,
@@ -381,7 +382,8 @@ class SubscribeChain(ChainBase):
no_exists=no_exists, no_exists=no_exists,
userid=subscribe.username, userid=subscribe.username,
username=subscribe.username, username=subscribe.username,
save_path=subscribe.save_path save_path=subscribe.save_path,
media_category=subscribe.media_category
) )
# 判断是否应完成订阅 # 判断是否应完成订阅
@@ -478,21 +480,17 @@ class SubscribeChain(ChainBase):
# 如果订阅未指定站点信息,直接返回默认站点 # 如果订阅未指定站点信息,直接返回默认站点
if not subscribe.sites: if not subscribe.sites:
return default_sites return default_sites
try: # 尝试解析订阅中的站点数据
# 尝试解析订阅中的站点数据 user_sites = subscribe.sites
user_sites = subscribe.sites # 计算 user_sites 和 default_sites 的交集
# 计算 user_sites 和 default_sites 的交集 intersection_sites = [site for site in user_sites if site in default_sites]
intersection_sites = [site for site in user_sites if site in default_sites] # 如果交集与原始订阅不一致,更新数据库
# 如果交集与原始订阅不一致,更新数据库 if set(intersection_sites) != set(user_sites):
if set(intersection_sites) != set(user_sites): self.subscribeoper.update(subscribe.id, {
self.subscribeoper.update(subscribe.id, { "sites": intersection_sites
"sites": intersection_sites })
}) # 如果交集为空,返回默认站点
# 如果交集为空,返回默认站点 return intersection_sites if intersection_sites else default_sites
return intersection_sites if intersection_sites else default_sites
except JSONDecodeError:
# 如果 JSON 解析失败,返回默认站点
return default_sites
def get_subscribed_sites(self) -> Optional[List[int]]: def get_subscribed_sites(self) -> Optional[List[int]]:
""" """
@@ -521,8 +519,6 @@ class SubscribeChain(ChainBase):
if not torrents: if not torrents:
logger.warn('没有缓存资源,无法匹配订阅') logger.warn('没有缓存资源,无法匹配订阅')
return return
# 记录重新识别过的种子
_recognize_cached = []
# 所有订阅 # 所有订阅
subscribes = self.subscribeoper.list('R') subscribes = self.subscribeoper.list('R')
# 遍历订阅 # 遍历订阅
@@ -541,12 +537,9 @@ class SubscribeChain(ChainBase):
# 订阅的站点域名列表 # 订阅的站点域名列表
domains = [] domains = []
if subscribe.sites: if subscribe.sites:
try: domains = self.siteoper.get_domains_by_ids(subscribe.sites)
siteids = subscribe.sites # 自定义识别词
if siteids: custom_words = subscribe.custom_words.split("\n") if subscribe.custom_words else []
domains = self.siteoper.get_domains_by_ids(siteids)
except JSONDecodeError:
pass
# 识别媒体信息 # 识别媒体信息
mediainfo: MediaInfo = self.recognize_media(meta=meta, mtype=meta.type, mediainfo: MediaInfo = self.recognize_media(meta=meta, mtype=meta.type,
tmdbid=subscribe.tmdbid, tmdbid=subscribe.tmdbid,
@@ -612,34 +605,62 @@ class SubscribeChain(ChainBase):
continue continue
logger.debug(f'开始匹配站点:{domain},共缓存了 {len(contexts)} 个种子...') logger.debug(f'开始匹配站点:{domain},共缓存了 {len(contexts)} 个种子...')
for context in contexts: for context in contexts:
# 检查是否匹配 # 提取信息
torrent_meta = context.meta_info torrent_meta = copy.deepcopy(context.meta_info)
torrent_mediainfo = context.media_info torrent_mediainfo = copy.deepcopy(context.media_info)
torrent_info = context.torrent_info torrent_info = context.torrent_info
# 先判断是否有没识别的种子 # 不在订阅站点范围的不处理
if not torrent_mediainfo or (not torrent_mediainfo.tmdb_id and not torrent_mediainfo.douban_id): sub_sites = self.get_sub_sites(subscribe)
_cache_key = f"{torrent_info.title}_{torrent_info.description}" if sub_sites and torrent_info.site not in sub_sites:
if _cache_key not in _recognize_cached: logger.debug(f"{torrent_info.site_name} - {torrent_info.title} 不符合订阅站点要求")
_recognize_cached.append(_cache_key) continue
# 匹配订阅参数
if not self.torrenthelper.filter_torrent(torrent_info=torrent_info,
filter_params=self.get_params(subscribe)):
continue
# 先判断是否有没识别的种子,有则重新识别;如果订阅有自定义识别词,则不使用预识别的信息
if not torrent_mediainfo \
or (not torrent_mediainfo.tmdb_id and not torrent_mediainfo.douban_id) \
or subscribe.custom_words:
if not subscribe.custom_words:
logger.info( logger.info(
f'{torrent_info.site_name} - {torrent_info.title} 订阅缓存为未识别状态,尝试重新识别...') f'{torrent_info.site_name} - {torrent_info.title} 订阅缓存为未识别状态,'
# 重新识别(不使用缓存) f'尝试重新识别媒体信息...')
else:
logger.info(
f'{torrent_info.site_name} - {torrent_info.title} 因订阅存在自定义识别词,'
f'正在重新识别元数据和媒体信息...')
# 重新识别元数据
torrent_meta = MetaInfo(title=torrent_info.title, subtitle=torrent_info.description,
custom_words=custom_words)
# 重新识别媒体信息
if subscribe.custom_words:
torrent_mediainfo = self.recognize_media(meta=torrent_meta)
else:
# 不使用识别缓存
torrent_mediainfo = self.recognize_media(meta=torrent_meta, cache=False) torrent_mediainfo = self.recognize_media(meta=torrent_meta, cache=False)
if not torrent_mediainfo: if torrent_mediainfo:
logger.warn( # 更新种子缓存
f'{torrent_info.site_name} - {torrent_info.title} 重新识别失败,尝试通过标题匹配...') context.media_info = torrent_mediainfo
if self.torrenthelper.match_torrent(mediainfo=mediainfo, if not torrent_mediainfo:
torrent_meta=torrent_meta, # 通过标题匹配兜底
torrent=torrent_info): logger.warn(
# 匹配成功 f'{torrent_info.site_name} - {torrent_info.title} 重新识别失败,尝试通过标题匹配...')
logger.info( if self.torrenthelper.match_torrent(mediainfo=mediainfo,
f'{mediainfo.title_year} 通过标题匹配到资源:{torrent_info.site_name} - {torrent_info.title}') torrent_meta=torrent_meta,
# 更新缓存 torrent=torrent_info):
# 匹配成功
logger.info(
f'{mediainfo.title_year} 通过标题匹配到可用资源:{torrent_info.site_name} - {torrent_info.title}')
if not subscribe.custom_words:
# 更新种子缓存
torrent_mediainfo = mediainfo torrent_mediainfo = mediainfo
context.media_info = mediainfo context.media_info = mediainfo
else: else:
continue continue
# 直接比对媒体信息 # 直接比对媒体信息
if torrent_mediainfo and (torrent_mediainfo.tmdb_id or torrent_mediainfo.douban_id): if torrent_mediainfo and (torrent_mediainfo.tmdb_id or torrent_mediainfo.douban_id):
@@ -652,30 +673,10 @@ class SubscribeChain(ChainBase):
and torrent_mediainfo.douban_id != mediainfo.douban_id: and torrent_mediainfo.douban_id != mediainfo.douban_id:
continue continue
logger.info( logger.info(
f'{mediainfo.title_year} 通过媒体信ID匹配到资源{torrent_info.site_name} - {torrent_info.title}') f'{mediainfo.title_year} 通过媒体信ID匹配到可用资源:{torrent_info.site_name} - {torrent_info.title}')
else: else:
continue continue
# 过滤规则
if subscribe.best_version:
rule_groups = self.systemconfig.get(SystemConfigKey.BestVersionFilterRuleGroups)
else:
rule_groups = self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups)
result: List[TorrentInfo] = self.filter_torrents(
rule_groups=rule_groups,
torrent_list=[torrent_info],
mediainfo=torrent_mediainfo)
if result is not None and not result:
# 不符合过滤规则
logger.debug(f"{torrent_info.title} 不匹配过滤规则")
continue
# 不在订阅站点范围的不处理
sub_sites = self.get_sub_sites(subscribe)
if sub_sites and torrent_info.site not in sub_sites:
logger.debug(f"{torrent_info.site_name} - {torrent_info.title} 不符合订阅站点要求")
continue
# 如果是电视剧 # 如果是电视剧
if torrent_mediainfo.type == MediaType.TV: if torrent_mediainfo.type == MediaType.TV:
# 有多季的不要 # 有多季的不要
@@ -714,6 +715,22 @@ class SubscribeChain(ChainBase):
logger.debug(f'{subscribe.name} 正在洗版,{torrent_info.title} 不是整季') logger.debug(f'{subscribe.name} 正在洗版,{torrent_info.title} 不是整季')
continue continue
# 优先级过滤规则
if subscribe.best_version:
rule_groups = subscribe.filter_groups \
or self.systemconfig.get(SystemConfigKey.BestVersionFilterRuleGroups)
else:
rule_groups = subscribe.filter_groups \
or self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups)
result: List[TorrentInfo] = self.filter_torrents(
rule_groups=rule_groups,
torrent_list=[torrent_info],
mediainfo=torrent_mediainfo)
if result is not None and not result:
# 不符合过滤规则
logger.debug(f"{torrent_info.title} 不匹配过滤规则")
continue
# 洗版时,优先级小于已下载优先级的不要 # 洗版时,优先级小于已下载优先级的不要
if subscribe.best_version: if subscribe.best_version:
if subscribe.current_priority \ if subscribe.current_priority \
@@ -721,11 +738,6 @@ class SubscribeChain(ChainBase):
logger.info(f'{subscribe.name} 正在洗版,{torrent_info.title} 优先级低于或等于已下载优先级') logger.info(f'{subscribe.name} 正在洗版,{torrent_info.title} 优先级低于或等于已下载优先级')
continue continue
# 匹配订阅参数
if not self.torrenthelper.filter_torrent(torrent_info=torrent_info,
filter_params=self.get_params(subscribe)):
continue
# 匹配成功 # 匹配成功
logger.info(f'{mediainfo.title_year} 匹配成功:{torrent_info.title}') logger.info(f'{mediainfo.title_year} 匹配成功:{torrent_info.title}')
_match_context.append(context) _match_context.append(context)
@@ -743,7 +755,8 @@ class SubscribeChain(ChainBase):
no_exists=no_exists, no_exists=no_exists,
userid=subscribe.username, userid=subscribe.username,
username=subscribe.username, username=subscribe.username,
save_path=subscribe.save_path) save_path=subscribe.save_path,
media_category=subscribe.media_category)
# 判断是否要完成订阅 # 判断是否要完成订阅
self.finish_subscribe_or_not(subscribe=subscribe, meta=meta, mediainfo=mediainfo, self.finish_subscribe_or_not(subscribe=subscribe, meta=meta, mediainfo=mediainfo,
downloads=downloads, lefts=lefts) downloads=downloads, lefts=lefts)
@@ -1191,8 +1204,6 @@ class SubscribeChain(ChainBase):
download_his = self.downloadhis.get_by_mediaid(tmdbid=subscribe.tmdbid, doubanid=subscribe.doubanid) download_his = self.downloadhis.get_by_mediaid(tmdbid=subscribe.tmdbid, doubanid=subscribe.doubanid)
if download_his: if download_his:
for his in download_his: for his in download_his:
# 种子链接
torrent_url = f"{his.torrent_site}{his.torrent_name}"
# 查询下载文件 # 查询下载文件
files = self.downloadhis.get_files_by_hash(his.hash) files = self.downloadhis.get_files_by_hash(his.hash)
if files: if files:

View File

@@ -1,4 +1,3 @@
import json
import re import re
import threading import threading
from pathlib import Path from pathlib import Path
@@ -118,6 +117,9 @@ class TransferChain(ChainBase):
if mediainfo: if mediainfo:
# 补充图片 # 补充图片
self.obtain_images(mediainfo) self.obtain_images(mediainfo)
# 更新自定义媒体类别
if downloadhis.media_category:
mediainfo.category = downloadhis.media_category
else: else:
# 非MoviePilot下载的任务按文件识别 # 非MoviePilot下载的任务按文件识别
mediainfo = None mediainfo = None

View File

@@ -14,7 +14,7 @@ class WordsMatcher(metaclass=Singleton):
def __init__(self): def __init__(self):
self.systemconfig = SystemConfigOper() self.systemconfig = SystemConfigOper()
def prepare(self, title: str) -> Tuple[str, List[str]]: def prepare(self, title: str, custom_words: List[str] = None) -> Tuple[str, List[str]]:
""" """
预处理标题,支持三种格式 预处理标题,支持三种格式
1屏蔽词 1屏蔽词
@@ -23,7 +23,7 @@ class WordsMatcher(metaclass=Singleton):
""" """
appley_words = [] appley_words = []
# 读取自定义识别词 # 读取自定义识别词
words: List[str] = self.systemconfig.get(SystemConfigKey.CustomIdentifiers) or [] words: List[str] = custom_words or self.systemconfig.get(SystemConfigKey.CustomIdentifiers) or []
for word in words: for word in words:
if not word or word.startswith("#"): if not word or word.startswith("#"):
continue continue

View File

@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import Tuple from typing import Tuple, List
import regex as re import regex as re
@@ -10,17 +10,18 @@ from app.log import logger
from app.schemas.types import MediaType from app.schemas.types import MediaType
def MetaInfo(title: str, subtitle: str = None) -> MetaBase: def MetaInfo(title: str, subtitle: str = None, custom_words: List[str] = None) -> MetaBase:
""" """
根据标题和副标题识别元数据 根据标题和副标题识别元数据
:param title: 标题、种子名、文件名 :param title: 标题、种子名、文件名
:param subtitle: 副标题、描述 :param subtitle: 副标题、描述
:param custom_words: 自定义识别词列表
:return: MetaAnime、MetaVideo :return: MetaAnime、MetaVideo
""" """
# 原标题 # 原标题
org_title = title org_title = title
# 预处理标题 # 预处理标题
title, apply_words = WordsMatcher().prepare(title) title, apply_words = WordsMatcher().prepare(title, custom_words=custom_words)
# 获取标题中媒体信息 # 获取标题中媒体信息
title, metainfo = find_metainfo(title) title, metainfo = find_metainfo(title)
# 判断是否处理文件 # 判断是否处理文件

View File

@@ -1,11 +1,9 @@
import json
from typing import Any, Self, List, Tuple, Optional, Generator from typing import Any, Self, List, Tuple, Optional, Generator
from sqlalchemy import create_engine, QueuePool, and_, inspect from sqlalchemy import create_engine, QueuePool, and_, inspect
from sqlalchemy.orm import declared_attr, sessionmaker, Session, scoped_session, as_declarative from sqlalchemy.orm import declared_attr, sessionmaker, Session, scoped_session, as_declarative
from app.core.config import settings from app.core.config import settings
from app.utils.object import ObjectUtils
# 数据库引擎 # 数据库引擎
Engine = create_engine( Engine = create_engine(

View File

@@ -47,6 +47,8 @@ class DownloadHistory(Base):
date = Column(String) date = Column(String)
# 附加信息 # 附加信息
note = Column(JSON) note = Column(JSON)
# 自定义媒体类别
media_category = Column(String)
@staticmethod @staticmethod
@db_query @db_query

View File

@@ -74,6 +74,12 @@ class Subscribe(Base):
search_imdbid = Column(Integer, default=0) search_imdbid = Column(Integer, default=0)
# 是否手动修改过总集数 0否 1是 # 是否手动修改过总集数 0否 1是
manual_total_episode = Column(Integer, default=0) manual_total_episode = Column(Integer, default=0)
# 自定义识别词
custom_words = Column(String)
# 自定义媒体类别
media_category = Column(String)
# 过滤规则组
filter_groups = Column(JSON, default=list)
@staticmethod @staticmethod
@db_query @db_query

View File

@@ -326,10 +326,16 @@ class Monitor(metaclass=Singleton):
download_hash = download_history.download_hash download_hash = download_history.download_hash
# 识别媒体信息 # 识别媒体信息
if download_history and download_history.tmdbid: if download_history and (download_history.tmdbid or download_history.doubanid):
# 下载记录中已存在识别信息
mediainfo: MediaInfo = self.mediaChain.recognize_media(mtype=MediaType(download_history.type), mediainfo: MediaInfo = self.mediaChain.recognize_media(mtype=MediaType(download_history.type),
tmdbid=download_history.tmdbid, tmdbid=download_history.tmdbid,
doubanid=download_history.doubanid) doubanid=download_history.doubanid)
if mediainfo:
# 更新自定义媒体类别
if download_history.media_category:
mediainfo.category = download_history.media_category
else: else:
mediainfo: MediaInfo = self.mediaChain.recognize_by_meta(file_meta) mediainfo: MediaInfo = self.mediaChain.recognize_by_meta(file_meta)
if not mediainfo: if not mediainfo:

View File

@@ -1,7 +1,6 @@
import json
from typing import Optional, List, Dict, Any from typing import Optional, List, Dict, Any
from pydantic import BaseModel, validator from pydantic import BaseModel
class Subscribe(BaseModel): class Subscribe(BaseModel):
@@ -65,17 +64,12 @@ class Subscribe(BaseModel):
search_imdbid: Optional[int] = 0 search_imdbid: Optional[int] = 0
# 时间 # 时间
date: Optional[str] = None date: Optional[str] = None
# 自定义识别词
@validator('sites', pre=True) custom_words: Optional[str] = None
def parse_json_fields(cls, value): # 自定义媒体类别
if value: media_category: Optional[str] = None
if isinstance(value, str): # 过滤规则组
try: filter_groups: Optional[List[str]] = []
return json.loads(value)
except json.JSONDecodeError:
return []
return value
return []
class Config: class Config:
orm_mode = True orm_mode = True

View File

@@ -1,7 +1,6 @@
import json
from typing import Optional from typing import Optional
from pydantic import BaseModel, validator from pydantic import BaseModel
# Shared properties # Shared properties
@@ -23,17 +22,6 @@ class UserBase(BaseModel):
# 个性化设置 # 个性化设置
settings: Optional[dict] = {} settings: Optional[dict] = {}
@validator('permissions', 'settings', pre=True)
def parse_json_fields(cls, value):
if value:
if isinstance(value, str):
try:
return json.loads(value)
except json.JSONDecodeError:
return {}
return value
return {}
class Config: class Config:
orm_mode = True orm_mode = True

View File

@@ -20,9 +20,9 @@ depends_on = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
# 站点数据统计增加站点名称
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
with op.batch_alter_table("siteuserdata") as batch_op: op.add_column('siteuserdata', sa.Column('name', sa.String(), nullable=True))
batch_op.add_column(sa.Column('name', sa.VARCHAR))
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@@ -0,0 +1,49 @@
"""2.0.3
Revision ID: e2dbe1421fa4
Revises: 0fb94bf69b38
Create Date: 2024-10-09 13:44:13.926529
"""
import contextlib
from alembic import op
import sqlalchemy as sa
from app.db import SessionFactory
from app.db.models import UserConfig
# revision identifiers, used by Alembic.
revision = 'e2dbe1421fa4'
down_revision = '0fb94bf69b38'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# 支持订阅自定义媒体类别和过滤规则组、自定义识别词
with contextlib.suppress(Exception):
op.add_column('downloadhistory', sa.Column('media_category', sa.String(), nullable=True))
op.add_column('subscribe', sa.Column('custom_words', sa.String(), nullable=True))
op.add_column('subscribe', sa.Column('media_category', sa.String(), nullable=True))
op.add_column('subscribe', sa.Column('filter_groups', sa.JSON(), nullable=True))
# 将String转换为JSON类型
with contextlib.suppress(Exception):
op.alter_column('subscribe', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('downloadhistory', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('mediaserveritem', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('message', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('plugindata', 'value', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('site', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('sitestatistic', 'note', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('systemconfig', 'value', existing_type=sa.String(), type_=sa.JSON())
op.alter_column('userconfig', 'value', existing_type=sa.String(), type_=sa.JSON())
# 清空用户配置表中不兼容的数据
with SessionFactory() as db:
UserConfig.truncate(db)
# ### end Alembic commands ###
def downgrade() -> None:
pass