?KKp+X?4F;Yyh=L#j
xD_M7IZ@Syljmcea%&s%tGadmFfLVBv{{vxQ$bCF3@jd_m002ovPDHLkV1l6k&rARS
From 8eed22c8a744ddea67afbdcb946834fe4e58f56b Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Fri, 6 Sep 2024 09:19:52 +0800
Subject: [PATCH 05/14] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=AD=97=E7=AC=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/bangumicoll/__init__.py | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 7cb76b7..b19b256 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -20,9 +20,9 @@ from app.utils.http import RequestUtils
class BangumiColl(_PluginBase):
# 插件名称
- plugin_name = "bangumi收藏订阅"
+ plugin_name = "Bangumi收藏订阅"
# 插件描述
- plugin_desc = "将bangumi用户收藏添加到订阅"
+ plugin_desc = "将Bangumi用户收藏添加到订阅"
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
@@ -79,7 +79,7 @@ class BangumiColl(_PluginBase):
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
- logger.info(f"bangumi收藏订阅启动,立即运行一次")
+ logger.info(f"Bangumi收藏订阅启动,立即运行一次")
self._scheduler.add_job(
func=self.bangumi_coll,
trigger='date',
@@ -374,7 +374,7 @@ class BangumiColl(_PluginBase):
def bangumi_coll(self):
"""
- 订阅bangumi用户收藏
+ 订阅Bangumi用户收藏
"""
if not self._uid:
logger.error("请设置UID")
@@ -390,13 +390,13 @@ class BangumiColl(_PluginBase):
res = RequestUtils(headers=headers).get_res(url=addr)
res = res.json().get("data")
if not res:
- logger.error(f"bangumi用户:{self._uid} ,未查询到数据")
+ logger.error(f"Bangumi用户:{self._uid} ,未查询到数据")
except Exception as e:
- logger.error(f"获取bangumi收藏数据失败:{addr} 失败:{str(e)}")
+ logger.error(f"获取Bangumi收藏数据失败:{addr} 失败:{str(e)}")
# 解析出必要数据
items: Dict[int, Dict[str, Any]] = {}
- logger.info(f"解析bangumi条目信息...")
+ logger.info(f"解析Bangumi条目信息...")
for item in res:
if item.get("type") not in self._collection_type:
continue
@@ -416,7 +416,7 @@ class BangumiColl(_PluginBase):
new_sub = items.keys() - db_sub.keys()
# 移除条目, 这里暂时不做
# del_sub = dbrid.keys() - items.keys()
- logger.info(f"解析bangumi条目信息完成,共{len(items)}条,新增{len(new_sub)}条")
+ logger.info(f"解析Bangumi条目信息完成,共{len(items)}条,新增{len(new_sub)}条")
# # 执行移除操作
# if del_sub:
@@ -431,7 +431,7 @@ class BangumiColl(_PluginBase):
self.add_subscribe(new_sub)
# 结束
- logger.info(f"bangumi收藏订阅执行完成")
+ logger.info(f"Bangumi收藏订阅执行完成")
@@ -442,9 +442,9 @@ class BangumiColl(_PluginBase):
if not meta.name:
logger.warn(f"{item.get('name_cn')} 未识别到有效数据")
continue
- # 由于bangumi的api不包含季度信息,不传入bangumi条目id,默认使用tmdb
+ # 由于Bangumi的api不包含季度信息,不传入Bangumi条目id,默认使用tmdb
mediainfo: MediaInfo = self.chain.recognize_media(meta=meta)
- # 对比bangumi和tmdb的信息确定季度
+ # 对比Bangumi和tmdb的信息确定季度
for info in mediainfo.season_info:
# 对比日期, 误差默认7天
if not self.are_dates(item.get("date"), info.get("air_date")):
From fd44eb70b037aad4fa1f0e1c1a4bb8f1f25ba5f6 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Mon, 9 Sep 2024 05:34:19 +0800
Subject: [PATCH 06/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9:=20UA,=20=E9=94=99?=
=?UTF-8?q?=E8=AF=AF=E5=AD=97=E7=AC=A6=20=E6=96=B0=E5=A2=9E:=20=E8=87=AA?=
=?UTF-8?q?=E5=8A=A8=E5=8F=96=E6=B6=88=E8=AE=A2=E9=98=85=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 +-
plugins/bangumicoll/__init__.py | 97 +++++++++++++++++++++++----------
2 files changed, 71 insertions(+), 29 deletions(-)
diff --git a/package.json b/package.json
index d81f56b..9a6f9e7 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,12 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.0",
+ "version": "1.1",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 2,
"history": {
+ "v1.1": "新增根据收藏状态移除由此插件添加的订阅",
"v1.0": "将bangumi用户收藏添加到 MP 订阅,部分功能未实现"
}
}
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index b19b256..52a0dbc 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -1,12 +1,9 @@
import datetime
-
from typing import Optional, Any, List, Dict
-
import pytz
+
from apscheduler.schedulers.background import BackgroundScheduler
-
from apscheduler.triggers.cron import CronTrigger
-
from app.chain.subscribe import SubscribeChain
from app.core.config import settings
@@ -16,6 +13,9 @@ from app.log import logger
from app.plugins import _PluginBase
from app.db.site_oper import SiteOper
from app.utils.http import RequestUtils
+from app.db.subscribe_oper import SubscribeOper
+from app.helper.subscribe import SubscribeHelper
+from app.schemas.types import NotificationType
class BangumiColl(_PluginBase):
@@ -26,7 +26,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.0"
+ plugin_version = "1.1"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -41,6 +41,8 @@ class BangumiColl(_PluginBase):
# 私有变量
_scheduler: Optional[BackgroundScheduler] = None
siteoper: SiteOper = None
+ subscribehelper: SubscribeHelper = None
+ subscribeoper: SubscribeOper = None
# 配置属性
_enabled: bool = False
@@ -51,14 +53,14 @@ class BangumiColl(_PluginBase):
_exclude: str = ""
_uid: str = ""
_collection_type = []
- _collection: Dict = {}
_save_path: str = ""
_sites: list = []
-
def init_plugin(self, config: dict = None):
self.subscribechain = SubscribeChain()
self.siteoper = SiteOper()
+ self.subscribehelper = SubscribeHelper()
+ self.subscribeoper = SubscribeOper()
# 停止现有任务
self.stop_service()
@@ -73,7 +75,6 @@ class BangumiColl(_PluginBase):
self._exclude = config.get("exclude")
self._uid = config.get("uid")
self._collection_type = config.get("collection_type") or [3]
- self._collection = config.get("collection")
self._save_path = config.get("save_path")
self._sites = config.get("sites")
@@ -110,7 +111,6 @@ class BangumiColl(_PluginBase):
"cron": self._cron,
"uid": self._uid,
"collection_type": self._collection_type,
- "collection": self._collection,
"include": self._include,
"exclude": self._exclude,
"save_path": self._save_path,
@@ -159,7 +159,7 @@ class BangumiColl(_PluginBase):
'component': 'VSwitch',
'props': {
'model': 'notify',
- 'label': '发送通知',
+ 'label': '自动取消订阅并通知',
},
}
],
@@ -323,6 +323,7 @@ class BangumiColl(_PluginBase):
pass
# 注册定时任务
+
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
@@ -382,11 +383,11 @@ class BangumiColl(_PluginBase):
addr = f"https://api.bgm.tv/v0/users/{self._uid}/collections?subject_type=2"
headers = {
- "User-Agent": "jxxghp/MoviePilot-Plugins (https://github.com/jxxghp/MoviePilot-Plugins)"
+ "User-Agent": "wikrin/MoviePilot-Plugins (https://github.com/wikrin/MoviePilot-Plugins)"
}
try:
- logger.info(f"查询bangumi条目信息:{addr} ...")
+ logger.info(f"查询bangumi条目信息:{addr}")
res = RequestUtils(headers=headers).get_res(url=addr)
res = res.json().get("data")
if not res:
@@ -399,7 +400,11 @@ class BangumiColl(_PluginBase):
logger.info(f"解析Bangumi条目信息...")
for item in res:
if item.get("type") not in self._collection_type:
+ logger.debug(
+ f"条目: {item['subject'].get('name_cn')} 类型:{item.get('type')} 不符合"
+ )
continue
+
# 条目id
subject_id = item.get("subject_id")
# 主标题
@@ -411,32 +416,44 @@ class BangumiColl(_PluginBase):
## 这里在后面添加排除规则
items.update({subject_id: {"name": name, "name_cn": name_cn, "date": date}})
## 获取此插件添加的订阅
- db_sub = {i.bangumiid: i.id for i in self.subscribechain.subscribeoper.list() if i.bangumiid and i.username == "Bangumi订阅"}
+ db_sub = {
+ i.bangumiid: i.id
+ for i in self.subscribechain.subscribeoper.list()
+ if i.bangumiid and i.username == "Bangumi订阅"
+ }
# 新增条目
new_sub = items.keys() - db_sub.keys()
- # 移除条目, 这里暂时不做
- # del_sub = dbrid.keys() - items.keys()
+ logger.debug(f"待新增条目:{new_sub}")
+ # 移除条目
+ del_sub = db_sub.keys() - items.keys()
+ logger.debug(f"待移除条目:{del_sub}")
+
logger.info(f"解析Bangumi条目信息完成,共{len(items)}条,新增{len(new_sub)}条")
- # # 执行移除操作
- # if del_sub:
- # del_items = {dbrid[i]: i for i in del_sub}
- # logger.info(f"开始移除订阅...")
- # self.delete_subscribe(del_items)
-
+ # 执行移除操作
+ if del_sub and self._notify:
+ # 数据库id为键,bgm条目id为值
+ del_items = {db_sub[i]: i for i in del_sub}
+ logger.info(f"开始移除订阅...")
+ self.delete_subscribe(del_items)
+
# 执行添加操作
if new_sub:
+ # bgm条目id为键,bgm条目信息为值
new_sub = {i: items[i] for i in new_sub}
logger.info(f"开始添加订阅...")
self.add_subscribe(new_sub)
-
+
# 结束
logger.info(f"Bangumi收藏订阅执行完成")
-
-
# 添加订阅
+
def add_subscribe(self, items: Dict[int, Dict[str, Any]]):
+ '''
+ 添加订阅
+ :param items: bgm条目id为键,bgm条目信息为值
+ '''
for subject_id, item in items.items():
meta = MetaInfo(item.get("name_cn"))
if not meta.name:
@@ -478,9 +495,33 @@ class BangumiColl(_PluginBase):
username="Bangumi订阅",
**kwargs,
)
-
- def delete_subscribe(self, del_items: dict):
- pass
+
+ # 移除订阅
+
+ def delete_subscribe(self, del_items: Dict[int, int]):
+ '''
+ 删除订阅
+ :param del_items: 数据库id为键,bgm条目id为值
+ '''
+ args = [i for i in del_items.keys()]
+ for arg in args:
+ subscribe_id = int(arg)
+ subscribe = self.subscribeoper.get(subscribe_id)
+ if subscribe:
+ self.subscribeoper.delete(subscribe_id)
+ # 统计订阅
+ self.subscribehelper.sub_done_async(
+ {"tmdbid": subscribe.tmdbid, "doubanid": subscribe.doubanid}
+ )
+ # 发送通知
+ self.post_message(
+ mtype=NotificationType.Subscribe,
+ title=f"{subscribe.name}({subscribe.year}) 第{subscribe.season}季 已取消订阅",
+ text="原因: 未在Bangumi收藏中找到该条目\n"
+ + f"订阅用户: {subscribe.username}\n"
+ + f"创建时间: {subscribe.date}",
+ image=subscribe.backdrop,
+ )
@staticmethod
def are_dates(date_str1, date_str2, threshold_days: int = 7) -> bool:
@@ -488,7 +529,7 @@ class BangumiColl(_PluginBase):
对比两个日期字符串是否接近
:param date_str1: 第一个日期字符串,格式为'YYYY-MM-DD'
:param date_str2: 第二个日期字符串,格式为'YYYY-MM-DD'
- :param threshold_days: 阈值天数,默认为1天
+ :param threshold_days: 阈值天数,默认为7天
:return: 如果两个日期之间的差异小于等于阈值天数,则返回True,否则返回False
"""
# 将日期字符串转换为datetime对象
From eab8d9d4a76a39a8829203459d45a192bf5a28c0 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Sat, 28 Sep 2024 00:16:57 +0800
Subject: [PATCH 07/14] =?UTF-8?q?fix:=20=E5=B7=B2=E5=AE=8C=E6=88=90?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E8=A2=AB=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?=
=?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=B7=BB=E5=8A=A0=E8=80=8C=E4=BA=92=E7=9B=B8?=
=?UTF-8?q?=E8=A6=86=E7=9B=96=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 ++-
plugins/bangumicoll/__init__.py | 23 ++++++++++++++++++-----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 9a6f9e7..651a73d 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,12 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.1",
+ "version": "1.2",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 2,
"history": {
+ "v1.2": "修复已完成订阅条目重复添加的问题",
"v1.1": "新增根据收藏状态移除由此插件添加的订阅",
"v1.0": "将bangumi用户收藏添加到 MP 订阅,部分功能未实现"
}
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 52a0dbc..adddc34 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -16,6 +16,9 @@ from app.utils.http import RequestUtils
from app.db.subscribe_oper import SubscribeOper
from app.helper.subscribe import SubscribeHelper
from app.schemas.types import NotificationType
+from app.db import db_query
+from app.db.models.subscribehistory import SubscribeHistory
+from sqlalchemy.orm import Session
class BangumiColl(_PluginBase):
@@ -26,7 +29,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.1"
+ plugin_version = "1.2"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -421,8 +424,10 @@ class BangumiColl(_PluginBase):
for i in self.subscribechain.subscribeoper.list()
if i.bangumiid and i.username == "Bangumi订阅"
}
+ ## 获取历史订阅
+ db_hist = self.get_subscribe_history()
# 新增条目
- new_sub = items.keys() - db_sub.keys()
+ new_sub = items.keys() - db_sub.keys() - db_hist
logger.debug(f"待新增条目:{new_sub}")
# 移除条目
del_sub = db_sub.keys() - items.keys()
@@ -481,7 +486,7 @@ class BangumiColl(_PluginBase):
# 额外参数
kwargs = {
"save_path": self._save_path,
- "sites": self._sites,
+ "sites": str(self._sites),
}
# 添加到订阅
self.subscribechain.add(
@@ -527,8 +532,7 @@ class BangumiColl(_PluginBase):
def are_dates(date_str1, date_str2, threshold_days: int = 7) -> bool:
"""
对比两个日期字符串是否接近
- :param date_str1: 第一个日期字符串,格式为'YYYY-MM-DD'
- :param date_str2: 第二个日期字符串,格式为'YYYY-MM-DD'
+ :param date_str: 日期字符串,格式为'YYYY-MM-DD'
:param threshold_days: 阈值天数,默认为7天
:return: 如果两个日期之间的差异小于等于阈值天数,则返回True,否则返回False
"""
@@ -544,3 +548,12 @@ class BangumiColl(_PluginBase):
# 比较差异和阈值
return delta <= threshold
+
+ @db_query
+ def get_subscribe_history(self, db: Session = None) -> set:
+ '''
+ 获取已完成的订阅
+ '''
+ result = db.query(SubscribeHistory).filter(SubscribeHistory.bangumiid != None).all()
+ return set([i.bangumiid for i in result])
+
\ No newline at end of file
From f140c1197abcb7ca5a29636615052382e8172d43 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Wed, 9 Oct 2024 22:13:33 +0800
Subject: [PATCH 08/14] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dtmdbapi=E8=BF=94?=
=?UTF-8?q?=E5=9B=9ENone=E5=AF=BC=E8=87=B4=E6=8F=92=E4=BB=B6=E5=B4=A9?=
=?UTF-8?q?=E6=BA=83=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 ++-
plugins/bangumicoll/__init__.py | 15 ++++++++++-----
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 651a73d..b648f0f 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,12 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.2",
+ "version": "1.2.1",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 2,
"history": {
+ "v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题",
"v1.2": "修复已完成订阅条目重复添加的问题",
"v1.1": "新增根据收藏状态移除由此插件添加的订阅",
"v1.0": "将bangumi用户收藏添加到 MP 订阅,部分功能未实现"
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index adddc34..01d48ca 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -29,7 +29,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.2"
+ plugin_version = "1.2.1"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -464,8 +464,12 @@ class BangumiColl(_PluginBase):
if not meta.name:
logger.warn(f"{item.get('name_cn')} 未识别到有效数据")
continue
- # 由于Bangumi的api不包含季度信息,不传入Bangumi条目id,默认使用tmdb
+ # 设置默认年份, 避免出现多个结果使用早期条目
+ meta.year = item.get("date")[:4]
mediainfo: MediaInfo = self.chain.recognize_media(meta=meta)
+ # 识别失败则跳过
+ if not mediainfo:
+ continue
# 对比Bangumi和tmdb的信息确定季度
for info in mediainfo.season_info:
# 对比日期, 误差默认7天
@@ -548,12 +552,13 @@ class BangumiColl(_PluginBase):
# 比较差异和阈值
return delta <= threshold
-
+
@db_query
def get_subscribe_history(self, db: Session = None) -> set:
'''
获取已完成的订阅
'''
- result = db.query(SubscribeHistory).filter(SubscribeHistory.bangumiid != None).all()
+ result = (
+ db.query(SubscribeHistory).filter(SubscribeHistory.bangumiid != None).all()
+ )
return set([i.bangumiid for i in result])
-
\ No newline at end of file
From cdc1079af695bc9591182a0a353af1024de4e973 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Fri, 11 Oct 2024 01:37:40 +0800
Subject: [PATCH 09/14] =?UTF-8?q?feat:=20=E8=AE=A2=E9=98=85=E5=A4=B1?=
=?UTF-8?q?=E8=B4=A5=E7=9A=84=E6=9D=A1=E7=9B=AE=E6=B1=87=E6=80=BB=E8=BE=93?=
=?UTF-8?q?=E5=87=BA=E5=88=B0=E6=97=A5=E5=BF=97=20feat:=20=E5=AF=B9?=
=?UTF-8?q?=E5=B7=B2=E5=AD=98=E5=9C=A8=E7=9A=84=E6=9D=A1=E7=9B=AE=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0Bangumi=E6=9D=A1=E7=9B=AEID=20fix:=20=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E9=80=9A=E8=BF=87=E5=85=B6=E4=BB=96=E6=96=B9=E5=BC=8F?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=9A=84=E8=AE=A2=E9=98=85=E9=87=8D=E5=A4=8D?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 9 +++---
plugins/bangumicoll/__init__.py | 57 ++++++++++++++++++++++-----------
2 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/package.json b/package.json
index b648f0f..07ae074 100644
--- a/package.json
+++ b/package.json
@@ -3,15 +3,14 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.2.1",
+ "version": "1.2.2",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
- "level": 2,
+ "level": 1,
"history": {
+ "v1.2.2": "新增: 订阅添加失败总览 修复: 其他方式添加的订阅反复添加的问题",
"v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题",
- "v1.2": "修复已完成订阅条目重复添加的问题",
- "v1.1": "新增根据收藏状态移除由此插件添加的订阅",
- "v1.0": "将bangumi用户收藏添加到 MP 订阅,部分功能未实现"
+ "v1.1": "新增根据收藏状态移除由此插件添加的订阅"
}
}
}
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 01d48ca..3f6d76a 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -29,7 +29,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.2.1"
+ plugin_version = "1.2.2"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -39,7 +39,7 @@ class BangumiColl(_PluginBase):
# 加载顺序
plugin_order = 23
# 可使用的用户级别
- auth_level = 2
+ auth_level = 1
# 私有变量
_scheduler: Optional[BackgroundScheduler] = None
@@ -325,8 +325,7 @@ class BangumiColl(_PluginBase):
def get_page(self):
pass
- # 注册定时任务
-
+ # 注册定时任务
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
@@ -418,11 +417,11 @@ class BangumiColl(_PluginBase):
date = item['subject'].get('date')
## 这里在后面添加排除规则
items.update({subject_id: {"name": name, "name_cn": name_cn, "date": date}})
- ## 获取此插件添加的订阅
+ ## 获取已添加的订阅
db_sub = {
i.bangumiid: i.id
for i in self.subscribechain.subscribeoper.list()
- if i.bangumiid and i.username == "Bangumi订阅"
+ if i.bangumiid
}
## 获取历史订阅
db_hist = self.get_subscribe_history()
@@ -447,21 +446,28 @@ class BangumiColl(_PluginBase):
# bgm条目id为键,bgm条目信息为值
new_sub = {i: items[i] for i in new_sub}
logger.info(f"开始添加订阅...")
- self.add_subscribe(new_sub)
+ msg = self.add_subscribe(new_sub)
+ if msg:
+ # 订阅失败的条目打印至日志
+ logger.info("\n".ljust(49, ' ').join(list(msg.values())))
# 结束
logger.info(f"Bangumi收藏订阅执行完成")
- # 添加订阅
-
- def add_subscribe(self, items: Dict[int, Dict[str, Any]]):
+ # 添加订阅
+ def add_subscribe(self, items: Dict[int, Dict[str, Any]]) -> Dict:
'''
添加订阅
:param items: bgm条目id为键,bgm条目信息为值
'''
+ # 记录失败条目
+ fail_items = {}
for subject_id, item in items.items():
meta = MetaInfo(item.get("name_cn"))
if not meta.name:
+ fail_items.update(
+ {subject_id: f"{item.get('name_cn')} 未识别到有效数据"}
+ )
logger.warn(f"{item.get('name_cn')} 未识别到有效数据")
continue
# 设置默认年份, 避免出现多个结果使用早期条目
@@ -469,6 +475,9 @@ class BangumiColl(_PluginBase):
mediainfo: MediaInfo = self.chain.recognize_media(meta=meta)
# 识别失败则跳过
if not mediainfo:
+ fail_items.update(
+ {subject_id: f"{item.get('name_cn')} 媒体信息识别失败"}
+ )
continue
# 对比Bangumi和tmdb的信息确定季度
for info in mediainfo.season_info:
@@ -481,19 +490,28 @@ class BangumiColl(_PluginBase):
# 更新集数信息
mediainfo.number_of_episodes = info.get("episode_count")
- # 检查是否已经订阅
- subflag = self.subscribechain.exists(mediainfo=mediainfo, meta=meta)
- if subflag:
- logger.info(f'{mediainfo.title_year} {meta.season} 正在订阅中')
+ # 检查是否已经订阅, 添加bangumiid
+ sid = self.subscribeoper.list_by_tmdbid(
+ mediainfo.tmdb_id, mediainfo.number_of_seasons
+ )
+ if sid:
+ logger.info(f"{mediainfo.title_year} {meta.season} 正在订阅中")
+ if len(sid) == 1:
+ self.subscribeoper.update(
+ sid=sid[0].id, payload={"bangumiid": subject_id}
+ )
+ logger.info(
+ f"{mediainfo.title_year} {meta.season} Bangumi条目id更新成功"
+ )
continue
# 额外参数
kwargs = {
"save_path": self._save_path,
- "sites": str(self._sites),
+ "sites": self._sites,
}
# 添加到订阅
- self.subscribechain.add(
+ sid, msg = self.subscribechain.add(
title=mediainfo.title,
year=mediainfo.year,
mtype=mediainfo.type,
@@ -504,9 +522,12 @@ class BangumiColl(_PluginBase):
username="Bangumi订阅",
**kwargs,
)
+ if not sid:
+ fail_items.update({subject_id: f"{item.get('name_cn')} {msg}"})
+ continue
+ return fail_items
- # 移除订阅
-
+ # 移除订阅
def delete_subscribe(self, del_items: Dict[int, int]):
'''
删除订阅
From f4ba85aaa36850d8b8c61f5d78b9d9d1b313af92 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Sat, 12 Oct 2024 03:40:14 +0800
Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=AE=A2?=
=?UTF-8?q?=E9=98=85,=20=E5=BD=93=E4=BF=A1=E6=81=AF=E4=B8=8D=E4=B8=80?=
=?UTF-8?q?=E8=87=B4=E4=BC=98=E5=85=88BGM?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 +-
plugins/bangumicoll/__init__.py | 107 ++++++++++++++++++++++++++------
2 files changed, 89 insertions(+), 21 deletions(-)
diff --git a/package.json b/package.json
index 07ae074..fae2e2c 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,12 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.2.2",
+ "version": "1.3",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 1,
"history": {
+ "v1.3": "添加订阅逻辑优化",
"v1.2.2": "新增: 订阅添加失败总览 修复: 其他方式添加的订阅反复添加的问题",
"v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题",
"v1.1": "新增根据收藏状态移除由此插件添加的订阅"
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 3f6d76a..e09c33b 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -29,7 +29,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.2.2"
+ plugin_version = "1.3"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -383,19 +383,11 @@ class BangumiColl(_PluginBase):
logger.error("请设置UID")
return
- addr = f"https://api.bgm.tv/v0/users/{self._uid}/collections?subject_type=2"
- headers = {
- "User-Agent": "wikrin/MoviePilot-Plugins (https://github.com/wikrin/MoviePilot-Plugins)"
- }
-
- try:
- logger.info(f"查询bangumi条目信息:{addr}")
- res = RequestUtils(headers=headers).get_res(url=addr)
- res = res.json().get("data")
- if not res:
- logger.error(f"Bangumi用户:{self._uid} ,未查询到数据")
- except Exception as e:
- logger.error(f"获取Bangumi收藏数据失败:{addr} 失败:{str(e)}")
+ # 获取收藏列表
+ res = self.get_bgm_res(addr="UserCollections", id=self._uid)
+ res = res.json().get("data")
+ if not res:
+ logger.error(f"Bangumi用户:{self._uid} ,没有任何收藏")
# 解析出必要数据
items: Dict[int, Dict[str, Any]] = {}
@@ -415,8 +407,19 @@ class BangumiColl(_PluginBase):
name_cn = item['subject'].get('name_cn')
# 放送时间
date = item['subject'].get('date')
+ # 集数
+ eps = item['subject'].get('eps')
## 这里在后面添加排除规则
- items.update({subject_id: {"name": name, "name_cn": name_cn, "date": date}})
+ items.update(
+ {
+ subject_id: {
+ "name": name,
+ "name_cn": name_cn,
+ "date": date,
+ "eps": eps,
+ }
+ }
+ )
## 获取已添加的订阅
db_sub = {
i.bangumiid: i.id
@@ -460,6 +463,7 @@ class BangumiColl(_PluginBase):
添加订阅
:param items: bgm条目id为键,bgm条目信息为值
'''
+
# 记录失败条目
fail_items = {}
for subject_id, item in items.items():
@@ -490,6 +494,63 @@ class BangumiColl(_PluginBase):
# 更新集数信息
mediainfo.number_of_episodes = info.get("episode_count")
+ # 总集数
+ total_episode = len(
+ mediainfo.seasons.get(mediainfo.number_of_seasons) or []
+ )
+ # 额外参数
+ kwargs = {
+ "save_path": self._save_path,
+ "sites": self._sites,
+ "total_episode": total_episode,
+ }
+
+ # 对比BGM 和 TMDB 季度和集数
+ if (
+ meta.begin_season
+ and mediainfo.number_of_seasons != meta.begin_season
+ or (item.get('eps') != 0 and total_episode != item.get('eps'))
+ or (item.get('eps') == 0 and not total_episode >= 12)
+ ):
+ '''
+ 1. 标题识别到季度且与tmdb不一致
+ 2. eps 字段为0且总集数小于12
+ 3. eps 字段不为0且总集数与Bangumi集数不一致
+ '''
+
+ def get_eps(id: str, addr: str = "getEpisodes") -> tuple:
+ """
+ 获取Bangumi条目的集数信息
+ :param id: bangumi条目id
+ :param addr: API地址
+ """
+ ep: int = 1
+ sort: int = 1
+ total: int = 24
+ res = self.get_bgm_res(addr=addr, id=id)
+ res = res.json()
+ data = res.get("data")
+ if data:
+ # 当前季的集数
+ ep = data[0].get("ep")
+ # 系列的累计集数
+ sort = data[0].get("sort")
+ # 当前集的总集数
+ total = res.get("total")
+ begin_ep = sort
+ total_ep = sort + total - ep
+ return begin_ep, total_ep
+
+ if meta.begin_season:
+ # 使用标题识别到的季号
+ mediainfo.number_of_seasons = meta.begin_season
+
+ begin_ep, total_ep = get_eps(id=subject_id)
+ logger.info(
+ f"{mediainfo.title_year} 识别到Bangumi与TMDB的季数和集数不一致"
+ )
+ kwargs.update({"start_episode": begin_ep, "total_episode": total_ep})
+
# 检查是否已经订阅, 添加bangumiid
sid = self.subscribeoper.list_by_tmdbid(
mediainfo.tmdb_id, mediainfo.number_of_seasons
@@ -505,11 +566,6 @@ class BangumiColl(_PluginBase):
)
continue
- # 额外参数
- kwargs = {
- "save_path": self._save_path,
- "sites": self._sites,
- }
# 添加到订阅
sid, msg = self.subscribechain.add(
title=mediainfo.title,
@@ -553,6 +609,17 @@ class BangumiColl(_PluginBase):
image=subscribe.backdrop,
)
+ @staticmethod
+ def get_bgm_res(addr: str, id: int | str):
+ url = {
+ "UserCollections": f"https://api.bgm.tv/v0/users/{str(id)}/collections?subject_type=2",
+ "getEpisodes": f"https://api.bgm.tv/v0/episodes?subject_id={str(id)}&type=0&limit=1",
+ }
+ headers = {
+ "User-Agent": "wikrin/MoviePilot-Plugins (https://github.com/wikrin/MoviePilot-Plugins)"
+ }
+ return RequestUtils(headers=headers).get_res(url=url[addr])
+
@staticmethod
def are_dates(date_str1, date_str2, threshold_days: int = 7) -> bool:
"""
From 2d79912a963243df19288ef54dcdb7a3f872c714 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Sat, 12 Oct 2024 23:24:35 +0800
Subject: [PATCH 11/14] =?UTF-8?q?frat:=20=E5=85=BC=E5=AE=B9V2=20fix:=20?=
=?UTF-8?q?=E6=92=A4=E9=94=80=E5=AD=A3=E5=8F=B7=E8=B7=9F=E9=9A=8F=E5=89=A7?=
=?UTF-8?q?=E9=9B=86=E6=A0=87=E9=A2=98,=20=E4=BC=9A=E5=AF=BC=E8=87=B4?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E6=9C=AA=E4=B8=8B=E8=BD=BD=E5=AE=8C=E6=95=B4?=
=?UTF-8?q?=E5=B0=B1=E5=AE=8C=E6=88=90=E8=AE=A2=E9=98=85=20other:=20?=
=?UTF-8?q?=E5=8C=85=E5=AF=BC=E5=85=A5=E6=8E=92=E5=BA=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 7 +--
plugins/bangumicoll/__init__.py | 89 +++++++++++++++++++++++----------
2 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/package.json b/package.json
index fae2e2c..0c43c28 100644
--- a/package.json
+++ b/package.json
@@ -3,15 +3,16 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.3",
+ "version": "1.3.1",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 1,
+ "v2": true,
"history": {
+ "v1.3.1": "修复因修改季号导致未下载剧集而完成订阅的问题",
"v1.3": "添加订阅逻辑优化",
"v1.2.2": "新增: 订阅添加失败总览 修复: 其他方式添加的订阅反复添加的问题",
- "v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题",
- "v1.1": "新增根据收藏状态移除由此插件添加的订阅"
+ "v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题"
}
}
}
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index e09c33b..41009a1 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -1,23 +1,25 @@
import datetime
-from typing import Optional, Any, List, Dict
+import json
import pytz
+from typing import Any, Dict, List, Optional, Type
+
+from app.chain.subscribe import SubscribeChain, Subscribe
+from app.core.config import settings
+from app.core.context import MediaInfo
+from app.core.metainfo import MetaInfo
+from app.db.models.subscribehistory import SubscribeHistory
+from app.db.site_oper import SiteOper
+from app.db.subscribe_oper import SubscribeOper
+from app.db import db_query
+from app.helper.subscribe import SubscribeHelper
+from app.log import logger
+from app.plugins import _PluginBase
+from app.schemas.types import NotificationType
+from app.utils.http import RequestUtils
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
-from app.chain.subscribe import SubscribeChain
-from app.core.config import settings
-
-from app.core.context import MediaInfo
-from app.core.metainfo import MetaInfo
-from app.log import logger
-from app.plugins import _PluginBase
-from app.db.site_oper import SiteOper
-from app.utils.http import RequestUtils
-from app.db.subscribe_oper import SubscribeOper
-from app.helper.subscribe import SubscribeHelper
-from app.schemas.types import NotificationType
-from app.db import db_query
-from app.db.models.subscribehistory import SubscribeHistory
+from sqlalchemy import JSON
from sqlalchemy.orm import Session
@@ -29,7 +31,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.3"
+ plugin_version = "1.3.1"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -466,6 +468,13 @@ class BangumiColl(_PluginBase):
# 记录失败条目
fail_items = {}
+ # 格式化站点
+ sites = (
+ self._sites
+ if self.are_types_equal(attribute_name='sites')
+ else json.dumps(self._sites)
+ )
+
for subject_id, item in items.items():
meta = MetaInfo(item.get("name_cn"))
if not meta.name:
@@ -501,7 +510,7 @@ class BangumiColl(_PluginBase):
# 额外参数
kwargs = {
"save_path": self._save_path,
- "sites": self._sites,
+ "sites": sites,
"total_episode": total_episode,
}
@@ -517,6 +526,7 @@ class BangumiColl(_PluginBase):
2. eps 字段为0且总集数小于12
3. eps 字段不为0且总集数与Bangumi集数不一致
'''
+ logger.info(f"{mediainfo.title_year} 与Bangumi的集数不一致")
def get_eps(id: str, addr: str = "getEpisodes") -> tuple:
"""
@@ -537,19 +547,29 @@ class BangumiColl(_PluginBase):
sort = data[0].get("sort")
# 当前集的总集数
total = res.get("total")
- begin_ep = sort
- total_ep = sort + total - ep
+ begin_ep = sort - ep + 1
+ total_ep = sort - ep + total
return begin_ep, total_ep
- if meta.begin_season:
- # 使用标题识别到的季号
- mediainfo.number_of_seasons = meta.begin_season
-
begin_ep, total_ep = get_eps(id=subject_id)
- logger.info(
- f"{mediainfo.title_year} 识别到Bangumi与TMDB的季数和集数不一致"
+ prev_eps = [i for i in range(1, begin_ep)]
+ # 更新参数
+ note = (
+ prev_eps
+ if self.are_types_equal(attribute_name='note')
+ else json.dumps(prev_eps)
+ )
+ kwargs.update(
+ {
+ "total_episode": total_ep, # 总集数
+ "start_episode": begin_ep, # 开始集数
+ "lack_episode": total_ep - begin_ep + 1, # 缺失集数
+ "note": note, # 忽略之前季度的集数
+ }
+ )
+ logger.info(
+ f"{mediainfo.title_year} 更新总集数为: {total_ep},开始集数为: {begin_ep}"
)
- kwargs.update({"start_episode": begin_ep, "total_episode": total_ep})
# 检查是否已经订阅, 添加bangumiid
sid = self.subscribeoper.list_by_tmdbid(
@@ -650,3 +670,20 @@ class BangumiColl(_PluginBase):
db.query(SubscribeHistory).filter(SubscribeHistory.bangumiid != None).all()
)
return set([i.bangumiid for i in result])
+
+ @staticmethod
+ def are_types_equal(
+ attribute_name: str, expected_type: Type[Any] = JSON(), class_=Subscribe
+ ) -> bool:
+ """
+ 比较类中属性的类型与expected_type是否一致
+ :param class_: 类
+ :param attribute_name: 属性名称
+ :param expected_type: 期望的类型
+ """
+ column = class_.__table__.columns.get(attribute_name)
+ if column is None:
+ raise AttributeError(
+ f"Class: {class_.__name__} 没有属性: '{attribute_name}'"
+ )
+ return isinstance(column.type, type(expected_type))
From b10cfe36a5c59a356f22ab54098632e81014e5f0 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Sun, 13 Oct 2024 15:00:09 +0800
Subject: [PATCH 12/14] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?=
=?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 +-
plugins/bangumicoll/__init__.py | 482 +++++++++++++-------------------
2 files changed, 198 insertions(+), 287 deletions(-)
diff --git a/package.json b/package.json
index 0c43c28..2e84c46 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,13 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.3.1",
+ "version": "1.4",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 1,
"v2": true,
"history": {
+ "v1.4": "结构优化",
"v1.3.1": "修复因修改季号导致未下载剧集而完成订阅的问题",
"v1.3": "添加订阅逻辑优化",
"v1.2.2": "新增: 订阅添加失败总览 修复: 其他方式添加的订阅反复添加的问题",
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 41009a1..21d071e 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -16,7 +16,6 @@ from app.log import logger
from app.plugins import _PluginBase
from app.schemas.types import NotificationType
from app.utils.http import RequestUtils
-
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from sqlalchemy import JSON
@@ -31,7 +30,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.3.1"
+ plugin_version = "1.4"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -69,45 +68,43 @@ class BangumiColl(_PluginBase):
# 停止现有任务
self.stop_service()
+ self.load_config(config)
- # 配置
+ if self._onlyonce:
+ self.schedule_once()
+
+ def load_config(self, config: dict):
+ """加载配置"""
if config:
- self._enabled = config.get("enabled")
- self._cron = config.get("cron")
- self._notify = config.get("notify")
- self._onlyonce = config.get("onlyonce")
- self._include = config.get("include")
- self._exclude = config.get("exclude")
- self._uid = config.get("uid")
- self._collection_type = config.get("collection_type") or [3]
- self._save_path = config.get("save_path")
- self._sites = config.get("sites")
+ self._enabled = config.get("enabled", self._enabled)
+ self._cron = config.get("cron", self._cron)
+ self._notify = config.get("notify", self._notify)
+ self._onlyonce = config.get("onlyonce", self._onlyonce)
+ self._include = config.get("include", self._include)
+ self._exclude = config.get("exclude", self._exclude)
+ self._uid = config.get("uid", self._uid)
+ self._collection_type = config.get("collection_type", [3])
+ self._save_path = config.get("save_path", self._save_path)
+ self._sites = config.get("sites", self._sites)
- if self._onlyonce:
- self._scheduler = BackgroundScheduler(timezone=settings.TZ)
- logger.info(f"Bangumi收藏订阅启动,立即运行一次")
- self._scheduler.add_job(
- func=self.bangumi_coll,
- trigger='date',
- run_date=datetime.datetime.now(tz=pytz.timezone(settings.TZ))
- + datetime.timedelta(seconds=3),
- )
+ def schedule_once(self):
+ """调度一次性任务"""
+ self._scheduler = BackgroundScheduler(timezone=settings.TZ)
+ logger.info("Bangumi收藏订阅,立即运行一次")
+ self._scheduler.add_job(
+ func=self.bangumi_coll,
+ trigger='date',
+ run_date=datetime.datetime.now(tz=pytz.timezone(settings.TZ))
+ + datetime.timedelta(seconds=3),
+ )
+ self._scheduler.start()
- # 启动任务
- if self._scheduler.get_jobs():
- self._scheduler.print_jobs()
- self._scheduler.start()
-
- if self._onlyonce:
- # 关闭一次性开关
- self._onlyonce = False
- # 保存设置
- self.__update_config()
+ # 关闭一次性开关
+ self._onlyonce = False
+ self.__update_config()
def __update_config(self):
- """
- 更新设置
- """
+ """更新设置"""
self.update_config(
{
"enabled": self._enabled,
@@ -123,12 +120,6 @@ class BangumiColl(_PluginBase):
}
)
- def get_api(self):
- pass
-
- def get_command(self):
- pass
-
def get_form(self):
# 列出所有站点
sites_options = [
@@ -324,254 +315,129 @@ class BangumiColl(_PluginBase):
"sites": [],
}
- def get_page(self):
- pass
-
- # 注册定时任务
def get_service(self) -> List[Dict[str, Any]]:
- """
- 注册插件公共服务
- [{
- "id": "服务ID",
- "name": "服务名称",
- "trigger": "触发器:cron/interval/date/CronTrigger.from_crontab()",
- "func": self.xxx,
- "kwargs": {} # 定时器参数
- }]
- """
- if self._enabled and self._cron:
+ """注册插件公共服务"""
+ if self._enabled:
+ trigger = CronTrigger.from_crontab(self._cron) if self._cron else "interval"
+ kwargs = {"hours": 6} if not self._cron else {}
return [
{
"id": "BangumiColl",
"name": "Bangumi收藏订阅",
- "trigger": CronTrigger.from_crontab(self._cron),
+ "trigger": trigger,
"func": self.bangumi_coll,
- "kwargs": {},
- }
- ]
- elif self._enabled:
- return [
- {
- "id": "BangumiColl",
- "name": "Bangumi收藏订阅",
- "trigger": "interval",
- "func": self.bangumi_coll,
- "kwargs": {"hours": 6},
+ "kwargs": kwargs,
}
]
return []
def stop_service(self):
- """
- 退出插件
- """
+ """退出插件"""
try:
if self._scheduler:
self._scheduler.remove_all_jobs()
- if self._scheduler.running:
- self._scheduler.shutdown()
+ self._scheduler.shutdown()
self._scheduler = None
except Exception as e:
- logger.error("退出插件失败:%s" % str(e))
+ logger.error(f"退出插件失败:{str(e)}")
+
+ def get_api(self):
+ pass
+
+ def get_command(self):
+ pass
+
+ def get_page(self):
+ pass
def get_state(self):
return self._enabled
def bangumi_coll(self):
- """
- 订阅Bangumi用户收藏
- """
+ """订阅Bangumi用户收藏"""
if not self._uid:
logger.error("请设置UID")
return
- # 获取收藏列表
- res = self.get_bgm_res(addr="UserCollections", id=self._uid)
- res = res.json().get("data")
- if not res:
- logger.error(f"Bangumi用户:{self._uid} ,没有任何收藏")
+ try:
+ res = self.get_bgm_res(addr="UserCollections", id=self._uid)
+ items = self.parse_collection_items(res)
- # 解析出必要数据
- items: Dict[int, Dict[str, Any]] = {}
- logger.info(f"解析Bangumi条目信息...")
- for item in res:
+ # 新增和移除条目
+ self.manage_subscriptions(items)
+
+ logger.info("Bangumi收藏订阅执行完成")
+ except Exception as e:
+ logger.error(f"执行失败: {str(e)}")
+
+ def parse_collection_items(self, response) -> Dict[int, Dict[str, Any]]:
+ """解析获取的收藏条目"""
+ data = response.json().get("data")
+ if not data:
+ logger.error(f"Bangumi用户:{self._uid} ,没有任何收藏")
+ return {}
+
+ items = {}
+ logger.info("解析Bangumi条目信息...")
+ for item in data:
if item.get("type") not in self._collection_type:
logger.debug(
f"条目: {item['subject'].get('name_cn')} 类型:{item.get('type')} 不符合"
)
continue
- # 条目id
- subject_id = item.get("subject_id")
- # 主标题
- name = item['subject'].get('name')
- # 中文标题
- name_cn = item['subject'].get('name_cn')
- # 放送时间
- date = item['subject'].get('date')
- # 集数
- eps = item['subject'].get('eps')
- ## 这里在后面添加排除规则
- items.update(
- {
- subject_id: {
- "name": name,
- "name_cn": name_cn,
- "date": date,
- "eps": eps,
- }
- }
- )
- ## 获取已添加的订阅
+ items[item.get("subject_id")] = {
+ "name": item['subject'].get('name'),
+ "name_cn": item['subject'].get('name_cn'),
+ "date": item['subject'].get('date'),
+ "eps": item['subject'].get('eps'),
+ }
+ return items
+
+ def manage_subscriptions(self, items: Dict[int, Dict[str, Any]]):
+ """管理订阅的新增和删除"""
db_sub = {
i.bangumiid: i.id
for i in self.subscribechain.subscribeoper.list()
if i.bangumiid
}
- ## 获取历史订阅
db_hist = self.get_subscribe_history()
- # 新增条目
new_sub = items.keys() - db_sub.keys() - db_hist
- logger.debug(f"待新增条目:{new_sub}")
- # 移除条目
del_sub = db_sub.keys() - items.keys()
+
+ logger.debug(f"待新增条目:{new_sub}")
logger.debug(f"待移除条目:{del_sub}")
- logger.info(f"解析Bangumi条目信息完成,共{len(items)}条,新增{len(new_sub)}条")
-
- # 执行移除操作
if del_sub and self._notify:
- # 数据库id为键,bgm条目id为值
del_items = {db_sub[i]: i for i in del_sub}
- logger.info(f"开始移除订阅...")
+ logger.info("开始移除订阅...")
self.delete_subscribe(del_items)
- # 执行添加操作
if new_sub:
- # bgm条目id为键,bgm条目信息为值
- new_sub = {i: items[i] for i in new_sub}
- logger.info(f"开始添加订阅...")
- msg = self.add_subscribe(new_sub)
+ logger.info("开始添加订阅...")
+ msg = self.add_subscribe({i: items[i] for i in new_sub})
if msg:
- # 订阅失败的条目打印至日志
logger.info("\n".ljust(49, ' ').join(list(msg.values())))
- # 结束
- logger.info(f"Bangumi收藏订阅执行完成")
-
# 添加订阅
def add_subscribe(self, items: Dict[int, Dict[str, Any]]) -> Dict:
- '''
- 添加订阅
- :param items: bgm条目id为键,bgm条目信息为值
- '''
-
- # 记录失败条目
+ """添加订阅"""
fail_items = {}
- # 格式化站点
- sites = (
- self._sites
- if self.are_types_equal(attribute_name='sites')
- else json.dumps(self._sites)
- )
-
- for subject_id, item in items.items():
+ for self._subid, item in items.items():
meta = MetaInfo(item.get("name_cn"))
if not meta.name:
- fail_items.update(
- {subject_id: f"{item.get('name_cn')} 未识别到有效数据"}
- )
+ fail_items[self._subid] = f"{item.get('name_cn')} 未识别到有效数据"
logger.warn(f"{item.get('name_cn')} 未识别到有效数据")
continue
- # 设置默认年份, 避免出现多个结果使用早期条目
- meta.year = item.get("date")[:4]
- mediainfo: MediaInfo = self.chain.recognize_media(meta=meta)
- # 识别失败则跳过
+
+ meta.year = item.get("date")[:4] if item.get("date") else None
+ mediainfo = self.chain.recognize_media(meta=meta)
if not mediainfo:
- fail_items.update(
- {subject_id: f"{item.get('name_cn')} 媒体信息识别失败"}
- )
+ fail_items[self._subid] = f"{item.get('name_cn')} 媒体信息识别失败"
continue
- # 对比Bangumi和tmdb的信息确定季度
- for info in mediainfo.season_info:
- # 对比日期, 误差默认7天
- if not self.are_dates(item.get("date"), info.get("air_date")):
- continue
- else:
- # 更新季度信息
- mediainfo.number_of_seasons = info.get("season_number")
- # 更新集数信息
- mediainfo.number_of_episodes = info.get("episode_count")
- # 总集数
- total_episode = len(
- mediainfo.seasons.get(mediainfo.number_of_seasons) or []
- )
- # 额外参数
- kwargs = {
- "save_path": self._save_path,
- "sites": sites,
- "total_episode": total_episode,
- }
+ self.update_media_info(item, mediainfo)
- # 对比BGM 和 TMDB 季度和集数
- if (
- meta.begin_season
- and mediainfo.number_of_seasons != meta.begin_season
- or (item.get('eps') != 0 and total_episode != item.get('eps'))
- or (item.get('eps') == 0 and not total_episode >= 12)
- ):
- '''
- 1. 标题识别到季度且与tmdb不一致
- 2. eps 字段为0且总集数小于12
- 3. eps 字段不为0且总集数与Bangumi集数不一致
- '''
- logger.info(f"{mediainfo.title_year} 与Bangumi的集数不一致")
-
- def get_eps(id: str, addr: str = "getEpisodes") -> tuple:
- """
- 获取Bangumi条目的集数信息
- :param id: bangumi条目id
- :param addr: API地址
- """
- ep: int = 1
- sort: int = 1
- total: int = 24
- res = self.get_bgm_res(addr=addr, id=id)
- res = res.json()
- data = res.get("data")
- if data:
- # 当前季的集数
- ep = data[0].get("ep")
- # 系列的累计集数
- sort = data[0].get("sort")
- # 当前集的总集数
- total = res.get("total")
- begin_ep = sort - ep + 1
- total_ep = sort - ep + total
- return begin_ep, total_ep
-
- begin_ep, total_ep = get_eps(id=subject_id)
- prev_eps = [i for i in range(1, begin_ep)]
- # 更新参数
- note = (
- prev_eps
- if self.are_types_equal(attribute_name='note')
- else json.dumps(prev_eps)
- )
- kwargs.update(
- {
- "total_episode": total_ep, # 总集数
- "start_episode": begin_ep, # 开始集数
- "lack_episode": total_ep - begin_ep + 1, # 缺失集数
- "note": note, # 忽略之前季度的集数
- }
- )
- logger.info(
- f"{mediainfo.title_year} 更新总集数为: {total_ep},开始集数为: {begin_ep}"
- )
-
- # 检查是否已经订阅, 添加bangumiid
sid = self.subscribeoper.list_by_tmdbid(
mediainfo.tmdb_id, mediainfo.number_of_seasons
)
@@ -579,55 +445,114 @@ class BangumiColl(_PluginBase):
logger.info(f"{mediainfo.title_year} {meta.season} 正在订阅中")
if len(sid) == 1:
self.subscribeoper.update(
- sid=sid[0].id, payload={"bangumiid": subject_id}
+ sid=sid[0].id, payload={"bangumiid": self._subid}
)
logger.info(
f"{mediainfo.title_year} {meta.season} Bangumi条目id更新成功"
)
continue
- # 添加到订阅
sid, msg = self.subscribechain.add(
title=mediainfo.title,
year=mediainfo.year,
mtype=mediainfo.type,
tmdbid=mediainfo.tmdb_id,
- bangumiid=subject_id,
+ bangumiid=self._subid,
season=mediainfo.number_of_seasons,
exist_ok=True,
username="Bangumi订阅",
- **kwargs,
+ **self.prepare_kwargs(item, meta.begin_season, mediainfo),
)
if not sid:
- fail_items.update({subject_id: f"{item.get('name_cn')} {msg}"})
- continue
+ fail_items[self._subid] = f"{item.get('name_cn')} {msg}"
+
return fail_items
+ def prepare_kwargs(self, item: dict, meta_season: int, mediainfo: MediaInfo):
+ """准备额外参数"""
+ kwargs = {
+ "save_path": self._save_path,
+ "sites": (
+ self._sites
+ if self.are_types_equal(attribute_name='sites')
+ else json.dumps(self._sites)
+ ),
+ }
+
+ if self.check_series_info(meta_season, item.get("eps", 0), mediainfo):
+ begin_ep, total_ep = self.get_eps()
+ prev_eps: list = [i for i in range(1, begin_ep)]
+ kwargs.update(
+ {
+ "total_episode": total_ep,
+ "start_episode": begin_ep,
+ "lack_episode": total_ep - begin_ep + 1,
+ "note": (
+ prev_eps
+ if self.are_types_equal("note")
+ else json.dumps(prev_eps)
+ ),
+ }
+ )
+ logger.info(
+ f"{mediainfo.title_year} 更新总集数为: {total_ep},开始集数为: {begin_ep}"
+ )
+
+ return kwargs
+
+ @staticmethod
+ def check_series_info(meta_season: int, bgm_eps: int, mediainfo: MediaInfo) -> bool:
+ """检查系列信息是否不一致"""
+ total_episode = len(mediainfo.seasons.get(mediainfo.number_of_seasons) or [])
+ return (
+ meta_season
+ and mediainfo.number_of_seasons != meta_season
+ or (bgm_eps != 0 and total_episode != bgm_eps)
+ or (bgm_eps == 0 and not total_episode >= 12)
+ )
+
+ def update_media_info(self, item, mediainfo):
+ """更新媒体信息"""
+ for info in mediainfo.season_info:
+ if self.are_dates(item.get("date"), info.get("air_date")):
+ mediainfo.number_of_seasons = info.get("season_number")
+ mediainfo.number_of_episodes = info.get("episode_count")
+ break
+
+ def get_eps(self) -> tuple:
+ """获取Bangumi条目的集数信息"""
+ try:
+ res = self.get_bgm_res(addr="getEpisodes", id=self._subid)
+ data = res.json().get("data", [{}])[0]
+ ep = data.get("ep", 1)
+ sort = data.get("sort", 1)
+ total = res.json().get("total", 24)
+ begin_ep = sort - ep + 1
+ total_ep = sort - ep + total
+ return begin_ep, total_ep
+ except Exception as e:
+ logger.error(f"获取集数信息失败: {str(e)}")
+ return 1, 24 # 默认值
+
# 移除订阅
def delete_subscribe(self, del_items: Dict[int, int]):
- '''
- 删除订阅
- :param del_items: 数据库id为键,bgm条目id为值
- '''
- args = [i for i in del_items.keys()]
- for arg in args:
- subscribe_id = int(arg)
- subscribe = self.subscribeoper.get(subscribe_id)
- if subscribe:
- self.subscribeoper.delete(subscribe_id)
- # 统计订阅
- self.subscribehelper.sub_done_async(
- {"tmdbid": subscribe.tmdbid, "doubanid": subscribe.doubanid}
- )
- # 发送通知
- self.post_message(
- mtype=NotificationType.Subscribe,
- title=f"{subscribe.name}({subscribe.year}) 第{subscribe.season}季 已取消订阅",
- text="原因: 未在Bangumi收藏中找到该条目\n"
- + f"订阅用户: {subscribe.username}\n"
- + f"创建时间: {subscribe.date}",
- image=subscribe.backdrop,
- )
+ """删除订阅"""
+ for subscribe_id in del_items.keys():
+ try:
+ subscribe = self.subscribeoper.get(subscribe_id)
+ if subscribe:
+ self.subscribeoper.delete(subscribe_id)
+ self.subscribehelper.sub_done_async(
+ {"tmdbid": subscribe.tmdbid, "doubanid": subscribe.doubanid}
+ )
+ self.post_message(
+ mtype=NotificationType.Subscribe,
+ title=f"{subscribe.name}({subscribe.year}) 第{subscribe.season}季 已取消订阅",
+ text=f"原因: 未在Bangumi收藏中找到该条目\n订阅用户: {subscribe.username}\n创建时间: {subscribe.date}",
+ image=subscribe.backdrop,
+ )
+ except Exception as e:
+ logger.error(f"删除订阅失败 {subscribe_id}: {str(e)}")
@staticmethod
def get_bgm_res(addr: str, id: int | str):
@@ -642,45 +567,30 @@ class BangumiColl(_PluginBase):
@staticmethod
def are_dates(date_str1, date_str2, threshold_days: int = 7) -> bool:
- """
- 对比两个日期字符串是否接近
- :param date_str: 日期字符串,格式为'YYYY-MM-DD'
- :param threshold_days: 阈值天数,默认为7天
- :return: 如果两个日期之间的差异小于等于阈值天数,则返回True,否则返回False
- """
- # 将日期字符串转换为datetime对象
+ """对比两个日期字符串是否接近"""
date1 = datetime.datetime.strptime(date_str1, '%Y-%m-%d')
date2 = datetime.datetime.strptime(date_str2, '%Y-%m-%d')
-
- # 计算两个日期之间的差异
- delta = abs(date1 - date2)
-
- # 将阈值转换为timedelta对象
- threshold = datetime.timedelta(days=threshold_days)
-
- # 比较差异和阈值
- return delta <= threshold
+ return abs((date1 - date2).days) <= threshold_days
@db_query
def get_subscribe_history(self, db: Session = None) -> set:
- '''
- 获取已完成的订阅
- '''
- result = (
- db.query(SubscribeHistory).filter(SubscribeHistory.bangumiid != None).all()
- )
- return set([i.bangumiid for i in result])
+ """获取已完成的订阅"""
+ try:
+ result = (
+ db.query(SubscribeHistory)
+ .filter(SubscribeHistory.bangumiid.isnot(None))
+ .all()
+ )
+ return {i.bangumiid for i in result}
+ except Exception as e:
+ logger.error(f"获取订阅历史失败: {str(e)}")
+ return set()
@staticmethod
def are_types_equal(
attribute_name: str, expected_type: Type[Any] = JSON(), class_=Subscribe
) -> bool:
- """
- 比较类中属性的类型与expected_type是否一致
- :param class_: 类
- :param attribute_name: 属性名称
- :param expected_type: 期望的类型
- """
+ """比较类中属性的类型与expected_type是否一致"""
column = class_.__table__.columns.get(attribute_name)
if column is None:
raise AttributeError(
From 6d6de8b22f4e0175feebc05f2da521639cb26d48 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Mon, 21 Oct 2024 20:37:01 +0800
Subject: [PATCH 13/14] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E4=BB=8EBGM?=
=?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=9A=84=E6=80=BB=E9=9B=86=E6=95=B0=E4=BC=9A?=
=?UTF-8?q?=E8=A2=ABTMDB=E8=A6=86=E7=9B=96=E7=9A=84=E9=97=AE=E9=A2=98,=20?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BC=80=E5=85=B3=E9=80=89=E9=A1=B9=E4=BB=A5?=
=?UTF-8?q?=E6=8E=A7=E5=88=B6=20=20=20=20=20=20=20=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E5=BA=95=E9=83=A8=E8=AF=B4=E6=98=8E.=20other:=20=E5=88=86?=
=?UTF-8?q?=E7=A6=BB=E7=BB=84=E4=BB=B6,=20=E7=BB=84=E4=BB=B6=E6=94=AF?=
=?UTF-8?q?=E6=8C=81HTML=E6=A0=BC=E5=BC=8F=E7=9A=84=E6=96=87=E6=9C=AC?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 8 +-
plugins/bangumicoll/__init__.py | 314 +++++-------------------
plugins/bangumicoll/page_components.py | 318 +++++++++++++++++++++++++
3 files changed, 386 insertions(+), 254 deletions(-)
create mode 100644 plugins/bangumicoll/page_components.py
diff --git a/package.json b/package.json
index 2e84c46..82e637b 100644
--- a/package.json
+++ b/package.json
@@ -3,17 +3,15 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.4",
+ "version": "1.5",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 1,
"v2": true,
"history": {
+ "v1.5": "修复总集数会同步TMDB变动的问题,增加开关选项",
"v1.4": "结构优化",
- "v1.3.1": "修复因修改季号导致未下载剧集而完成订阅的问题",
- "v1.3": "添加订阅逻辑优化",
- "v1.2.2": "新增: 订阅添加失败总览 修复: 其他方式添加的订阅反复添加的问题",
- "v1.2.1": "修复tmdb没有查询到条目导致插件崩溃的问题"
+ "v1.3.1": "修复因修改季号导致未下载剧集而完成订阅的问题"
}
}
}
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 21d071e..192d236 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -1,11 +1,20 @@
+# 基础库
import datetime
import json
-import pytz
from typing import Any, Dict, List, Optional, Type
+# 第三方库
+from apscheduler.schedulers.background import BackgroundScheduler
+from apscheduler.triggers.cron import CronTrigger
+import pytz
+from sqlalchemy import JSON
+from sqlalchemy.orm import Session
+
+# 项目库
from app.chain.subscribe import SubscribeChain, Subscribe
from app.core.config import settings
from app.core.context import MediaInfo
+from app.core.meta import MetaBase
from app.core.metainfo import MetaInfo
from app.db.models.subscribehistory import SubscribeHistory
from app.db.site_oper import SiteOper
@@ -16,10 +25,6 @@ from app.log import logger
from app.plugins import _PluginBase
from app.schemas.types import NotificationType
from app.utils.http import RequestUtils
-from apscheduler.schedulers.background import BackgroundScheduler
-from apscheduler.triggers.cron import CronTrigger
-from sqlalchemy import JSON
-from sqlalchemy.orm import Session
class BangumiColl(_PluginBase):
@@ -30,7 +35,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.4"
+ plugin_version = "1.5"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -50,6 +55,7 @@ class BangumiColl(_PluginBase):
# 配置属性
_enabled: bool = False
+ _total_change: bool = False
_cron: str = ""
_notify: bool = False
_onlyonce: bool = False
@@ -76,16 +82,19 @@ class BangumiColl(_PluginBase):
def load_config(self, config: dict):
"""加载配置"""
if config:
- self._enabled = config.get("enabled", self._enabled)
- self._cron = config.get("cron", self._cron)
- self._notify = config.get("notify", self._notify)
- self._onlyonce = config.get("onlyonce", self._onlyonce)
- self._include = config.get("include", self._include)
- self._exclude = config.get("exclude", self._exclude)
- self._uid = config.get("uid", self._uid)
- self._collection_type = config.get("collection_type", [3])
- self._save_path = config.get("save_path", self._save_path)
- self._sites = config.get("sites", self._sites)
+ # 遍历配置中的键并设置相应的属性
+ for key in (
+ "enabled",
+ "total_change",
+ "cron",
+ "notify",
+ "onlyonce",
+ "uid",
+ "collection_type",
+ "save_path",
+ "sites",
+ ):
+ setattr(self, f"_{key}", config.get(key, getattr(self, f"_{key}")))
def schedule_once(self):
"""调度一次性任务"""
@@ -109,6 +118,7 @@ class BangumiColl(_PluginBase):
{
"enabled": self._enabled,
"notify": self._notify,
+ "total_change": self._total_change,
"onlyonce": self._onlyonce,
"cron": self._cron,
"uid": self._uid,
@@ -121,199 +131,15 @@ class BangumiColl(_PluginBase):
)
def get_form(self):
+ from .page_components import form
+
# 列出所有站点
sites_options = [
{"title": site.name, "value": site.id}
for site in self.siteoper.list_order_by_pri()
]
- return [
- {
- 'component': 'VForm',
- 'content': [
- {
- 'component': 'VRow',
- 'content': [
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 4},
- 'content': [
- {
- 'component': 'VSwitch',
- 'props': {
- 'model': 'enabled',
- 'label': '启用插件',
- },
- }
- ],
- },
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 4},
- 'content': [
- {
- 'component': 'VSwitch',
- 'props': {
- 'model': 'notify',
- 'label': '自动取消订阅并通知',
- },
- }
- ],
- },
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 4},
- 'content': [
- {
- 'component': 'VSwitch',
- 'props': {
- 'model': 'onlyonce',
- 'label': '立即运行一次',
- },
- }
- ],
- },
- ],
- },
- {
- 'component': 'VRow',
- 'content': [
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VTextField',
- 'props': {
- 'model': 'cron',
- 'label': '执行周期',
- 'placeholder': '5位cron表达式,留空自动',
- },
- }
- ],
- },
- ],
- },
- {
- 'component': 'VRow',
- 'content': [
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VTextField',
- 'props': {
- 'model': 'uid',
- 'label': 'UID/用户名',
- 'placeholder': '设置了用户名填写用户名,否则填写UID',
- },
- },
- ],
- },
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VSelect',
- 'props': {
- 'model': 'collection_type',
- 'label': '收藏类型',
- 'chips': True,
- 'multiple': True,
- 'items': [
- {'title': '在看', 'value': 3},
- {'title': '想看', 'value': 1},
- ],
- },
- }
- ],
- },
- ],
- },
- {
- 'component': 'VRow',
- 'content': [
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VTextField',
- 'props': {
- 'model': 'include',
- 'label': '包含',
- 'placeholder': '暂未实现',
- },
- }
- ],
- },
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VTextField',
- 'props': {
- 'model': 'exclude',
- 'label': '排除',
- 'placeholder': '暂未实现',
- },
- }
- ],
- },
- ],
- },
- {
- 'component': 'VRow',
- 'content': [
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VTextField',
- 'props': {
- 'model': 'save_path',
- 'label': '保存目录',
- 'placeholder': '留空自动',
- },
- }
- ],
- },
- {
- 'component': 'VCol',
- 'props': {'cols': 12, 'md': 6},
- 'content': [
- {
- 'component': 'VSelect',
- 'props': {
- 'model': 'sites',
- 'label': '选择站点',
- 'chips': True,
- 'multiple': True,
- 'items': sites_options,
- },
- }
- ],
- },
- ],
- },
- ],
- }
- ], {
- "enabled": False,
- "notify": False,
- "onlyonce": False,
- "cron": "",
- "uid": "",
- "collection_type": [3],
- "include": "",
- "exclude": "",
- "save_path": "",
- "sites": [],
- }
+ return form(sites_options)
def get_service(self) -> List[Dict[str, Any]]:
"""注册插件公共服务"""
@@ -372,27 +198,22 @@ class BangumiColl(_PluginBase):
def parse_collection_items(self, response) -> Dict[int, Dict[str, Any]]:
"""解析获取的收藏条目"""
- data = response.json().get("data")
+ data = response.json().get("data", [])
if not data:
logger.error(f"Bangumi用户:{self._uid} ,没有任何收藏")
return {}
- items = {}
logger.info("解析Bangumi条目信息...")
- for item in data:
- if item.get("type") not in self._collection_type:
- logger.debug(
- f"条目: {item['subject'].get('name_cn')} 类型:{item.get('type')} 不符合"
- )
- continue
-
- items[item.get("subject_id")] = {
+ return {
+ item.get("subject_id"): {
"name": item['subject'].get('name'),
"name_cn": item['subject'].get('name_cn'),
"date": item['subject'].get('date'),
"eps": item['subject'].get('eps'),
}
- return items
+ for item in data
+ if item.get("type") in self._collection_type
+ }
def manage_subscriptions(self, items: Dict[int, Dict[str, Any]]):
"""管理订阅的新增和删除"""
@@ -422,6 +243,7 @@ class BangumiColl(_PluginBase):
# 添加订阅
def add_subscribe(self, items: Dict[int, Dict[str, Any]]) -> Dict:
"""添加订阅"""
+
fail_items = {}
for self._subid, item in items.items():
meta = MetaInfo(item.get("name_cn"))
@@ -432,6 +254,7 @@ class BangumiColl(_PluginBase):
meta.year = item.get("date")[:4] if item.get("date") else None
mediainfo = self.chain.recognize_media(meta=meta)
+ meta.total_episode = item.get("eps", 0)
if not mediainfo:
fail_items[self._subid] = f"{item.get('name_cn')} 媒体信息识别失败"
continue
@@ -442,33 +265,28 @@ class BangumiColl(_PluginBase):
mediainfo.tmdb_id, mediainfo.number_of_seasons
)
if sid:
- logger.info(f"{mediainfo.title_year} {meta.season} 正在订阅中")
+ logger.info(f"{mediainfo.title_year} 正在订阅中")
if len(sid) == 1:
self.subscribeoper.update(
sid=sid[0].id, payload={"bangumiid": self._subid}
)
- logger.info(
- f"{mediainfo.title_year} {meta.season} Bangumi条目id更新成功"
- )
+ logger.info(f"{mediainfo.title_year} Bangumi条目id更新成功")
continue
sid, msg = self.subscribechain.add(
title=mediainfo.title,
year=mediainfo.year,
- mtype=mediainfo.type,
- tmdbid=mediainfo.tmdb_id,
bangumiid=self._subid,
- season=mediainfo.number_of_seasons,
exist_ok=True,
username="Bangumi订阅",
- **self.prepare_kwargs(item, meta.begin_season, mediainfo),
+ **self.prepare_kwargs(meta, mediainfo),
)
if not sid:
fail_items[self._subid] = f"{item.get('name_cn')} {msg}"
return fail_items
- def prepare_kwargs(self, item: dict, meta_season: int, mediainfo: MediaInfo):
+ def prepare_kwargs(self, meta: MetaBase, mediainfo: MediaInfo) -> Dict:
"""准备额外参数"""
kwargs = {
"save_path": self._save_path,
@@ -479,14 +297,24 @@ class BangumiColl(_PluginBase):
),
}
- if self.check_series_info(meta_season, item.get("eps", 0), mediainfo):
- begin_ep, total_ep = self.get_eps()
- prev_eps: list = [i for i in range(1, begin_ep)]
+ total_episode = len(mediainfo.seasons.get(mediainfo.number_of_seasons) or [])
+ if (
+ meta.begin_season
+ and mediainfo.number_of_seasons != meta.begin_season
+ or total_episode != meta.total_episode
+ ):
+ meta = self.get_eps(meta)
+ total_ep: int = meta.end_episode if meta.end_episode else total_episode
+ lock_eps: int = total_ep - meta.begin_episode + 1
+ prev_eps: list = [i for i in range(1, meta.begin_episode)]
kwargs.update(
{
"total_episode": total_ep,
- "start_episode": begin_ep,
- "lack_episode": total_ep - begin_ep + 1,
+ "start_episode": meta.begin_episode,
+ "lack_episode": lock_eps,
+ "manual_total_episode": (
+ 1 if meta.total_episode and self._total_change else 0
+ ), # 手动修改过总集数
"note": (
prev_eps
if self.are_types_equal("note")
@@ -495,23 +323,12 @@ class BangumiColl(_PluginBase):
}
)
logger.info(
- f"{mediainfo.title_year} 更新总集数为: {total_ep},开始集数为: {begin_ep}"
+ f"{mediainfo.title_year} 更新总集数为: {total_ep},开始集数为: {meta.begin_episode}"
)
return kwargs
- @staticmethod
- def check_series_info(meta_season: int, bgm_eps: int, mediainfo: MediaInfo) -> bool:
- """检查系列信息是否不一致"""
- total_episode = len(mediainfo.seasons.get(mediainfo.number_of_seasons) or [])
- return (
- meta_season
- and mediainfo.number_of_seasons != meta_season
- or (bgm_eps != 0 and total_episode != bgm_eps)
- or (bgm_eps == 0 and not total_episode >= 12)
- )
-
- def update_media_info(self, item, mediainfo):
+ def update_media_info(self, item: dict, mediainfo: MediaInfo):
"""更新媒体信息"""
for info in mediainfo.season_info:
if self.are_dates(item.get("date"), info.get("air_date")):
@@ -519,20 +336,19 @@ class BangumiColl(_PluginBase):
mediainfo.number_of_episodes = info.get("episode_count")
break
- def get_eps(self) -> tuple:
+ def get_eps(self, meta: MetaBase) -> MetaBase:
"""获取Bangumi条目的集数信息"""
try:
res = self.get_bgm_res(addr="getEpisodes", id=self._subid)
data = res.json().get("data", [{}])[0]
- ep = data.get("ep", 1)
- sort = data.get("sort", 1)
- total = res.json().get("total", 24)
- begin_ep = sort - ep + 1
- total_ep = sort - ep + total
- return begin_ep, total_ep
+ prev = data.get("sort", 1) - data.get("ep", 1)
+ total = res.json().get("total", None)
+ meta.begin_episode = prev + 1
+ meta.end_episode = prev + total if total else None
except Exception as e:
logger.error(f"获取集数信息失败: {str(e)}")
- return 1, 24 # 默认值
+ finally:
+ return meta
# 移除订阅
def delete_subscribe(self, del_items: Dict[int, int]):
diff --git a/plugins/bangumicoll/page_components.py b/plugins/bangumicoll/page_components.py
new file mode 100644
index 0000000..044299c
--- /dev/null
+++ b/plugins/bangumicoll/page_components.py
@@ -0,0 +1,318 @@
+from bs4 import BeautifulSoup
+
+
+def form(sites_options) -> list:
+ return [
+ {
+ 'component': 'VForm',
+ 'content': [
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 3},
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'enabled',
+ 'label': '启用插件',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 3},
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'notify',
+ 'label': '自动取消订阅并通知',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 3},
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'total_change',
+ 'label': '不跟随TMDB变动',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 3},
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'onlyonce',
+ 'label': '立即运行一次',
+ },
+ }
+ ],
+ },
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 8, 'md': 4},
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'cron',
+ 'label': '执行周期',
+ 'placeholder': '5位cron表达式,留空自动',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 8, 'md': 4},
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'uid',
+ 'label': 'UID/用户名',
+ 'placeholder': '设置了用户名填写用户名,否则填写UID',
+ },
+ },
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 8, 'md': 4},
+ 'content': [
+ {
+ 'component': 'VSelect',
+ 'props': {
+ 'model': 'collection_type',
+ 'label': '收藏类型',
+ 'chips': True,
+ 'multiple': True,
+ 'items': [
+ {'title': '在看', 'value': 3},
+ {'title': '想看', 'value': 1},
+ ],
+ },
+ }
+ ],
+ },
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 6},
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'include',
+ 'label': '包含',
+ 'placeholder': '暂未实现',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 6},
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'exclude',
+ 'label': '排除',
+ 'placeholder': '暂未实现',
+ },
+ }
+ ],
+ },
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 6},
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'save_path',
+ 'label': '保存目录',
+ 'placeholder': '留空自动',
+ },
+ }
+ ],
+ },
+ {
+ 'component': 'VCol',
+ 'props': {'cols': 12, 'md': 6},
+ 'content': [
+ {
+ 'component': 'VSelect',
+ 'props': {
+ 'model': 'sites',
+ 'label': '选择站点',
+ 'chips': True,
+ 'multiple': True,
+ 'items': sites_options,
+ },
+ }
+ ],
+ },
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ },
+ 'content': [
+ {
+ 'component': 'VAlert',
+ 'props': {
+ 'type': 'info',
+ 'variant': 'tonal',
+ },
+ 'content': parse_html(
+ '注意: 该插件仅会将公开的收藏添加到订阅。
'
+ ),
+ }
+ ],
+ }
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ },
+ 'content': [
+ {
+ 'component': 'VAlert',
+ 'props': {
+ 'type': 'info',
+ 'variant': 'tonal',
+ },
+ 'content': parse_html(
+ '注意: 开启自动取消订阅并通知后,已添加的订阅在下一次执行时若不在已选择的收藏类型中,将会被取消订阅。
'
+ ),
+ }
+ ],
+ }
+ ],
+ },
+ ],
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ },
+ 'content': [
+ {
+ 'component': 'VAlert',
+ 'props': {
+ 'type': 'info',
+ 'variant': 'tonal',
+ },
+ 'content': parse_html(
+ '注意: 开启不跟随TMDB变动后,从Bangumi API获取的总集数将不再跟随TMDB的集数变动。
'
+ ),
+ },
+ ],
+ },
+ ],
+ },
+ ], {
+ "enabled": False,
+ "total_change": False,
+ "notify": False,
+ "onlyonce": False,
+ "cron": "",
+ "uid": "",
+ "collection_type": [3],
+ "include": "",
+ "exclude": "",
+ "save_path": "",
+ "sites": [],
+ }
+
+
+def parse_html(html_string: str) -> list:
+ soup = BeautifulSoup(html_string, 'html.parser')
+ result: list = []
+
+ # 定义需要直接转为文本的标签
+ inline_text_tags = {'strong', 'u', 'em', 'b', 'i'}
+
+ def process_element(element: BeautifulSoup):
+ # 处理纯文本节点
+ if element.name is None:
+ text = element.strip()
+ return text if text else ""
+
+ # 处理HTML标签
+ component = element.name
+ props = {attr: element[attr] for attr in element.attrs}
+ content = []
+
+ # 递归处理子元素
+ for child in element.children:
+ child_content = process_element(child)
+ if isinstance(child_content, str):
+ content.append({'component': 'span', 'text': child_content})
+ elif child_content: # 只有在child_content不为空时添加
+ content.append(child_content)
+
+ # 构建标签对象
+ tag_data = {
+ 'component': component,
+ 'props': props,
+ 'content': content if component not in inline_text_tags else [],
+ }
+
+ if content and component in inline_text_tags:
+ tag_data['text'] = ' '.join(
+ item['text'] for item in content if 'text' in item
+ )
+
+ return tag_data
+
+ # 遍历所有子元素
+ for element in soup.children:
+ element_content = process_element(element)
+ if element_content: # 只增加非空内容
+ result.append(element_content)
+
+ return result
From ef2f91f22291d5d5b06e66516c6729c899394a04 Mon Sep 17 00:00:00 2001
From: Attente <19653207+wikrin@users.noreply.github.com>
Date: Wed, 23 Oct 2024 07:08:03 +0800
Subject: [PATCH 14/14] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=94=A8?=
=?UTF-8?q?=E6=88=B7=E5=88=A0=E9=99=A4=E7=AB=99=E7=82=B9,=E5=90=8C?=
=?UTF-8?q?=E6=AD=A5=E5=88=A0=E9=99=A4=E7=AB=99=E7=82=B9=E5=88=97=E8=A1=A8?=
=?UTF-8?q?,=20=E4=B8=8D=E4=BC=9A=E5=86=8D=E7=95=99=E4=B8=8B=E7=BC=96?=
=?UTF-8?q?=E5=8F=B7=20fix:=20=E4=BF=AE=E5=A4=8D=E5=9B=A0=E6=9C=AA?=
=?UTF-8?q?=E4=BC=A0=E9=80=92=E5=8F=82=E6=95=B0=E5=AF=BC=E8=87=B4=E7=9A=84?=
=?UTF-8?q?=E5=AD=A3=E5=BA=A6=E8=AF=86=E5=88=AB=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 3 ++-
plugins/bangumicoll/__init__.py | 25 +++++++++++++++++++++----
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 82e637b..cf47843 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,13 @@
"name": "Bangumi收藏订阅",
"description": "Bangumi用户收藏添加到订阅",
"labels": "订阅",
- "version": "1.5",
+ "version": "1.5.1",
"icon": "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png",
"author": "Attente",
"level": 1,
"v2": true,
"history": {
+ "v1.5.1": "修复季度信息未传递的问题. 新增站点列表同步删除",
"v1.5": "修复总集数会同步TMDB变动的问题,增加开关选项",
"v1.4": "结构优化",
"v1.3.1": "修复因修改季号导致未下载剧集而完成订阅的问题"
diff --git a/plugins/bangumicoll/__init__.py b/plugins/bangumicoll/__init__.py
index 192d236..e101103 100644
--- a/plugins/bangumicoll/__init__.py
+++ b/plugins/bangumicoll/__init__.py
@@ -14,6 +14,7 @@ from sqlalchemy.orm import Session
from app.chain.subscribe import SubscribeChain, Subscribe
from app.core.config import settings
from app.core.context import MediaInfo
+from app.core.event import eventmanager, Event
from app.core.meta import MetaBase
from app.core.metainfo import MetaInfo
from app.db.models.subscribehistory import SubscribeHistory
@@ -23,7 +24,7 @@ from app.db import db_query
from app.helper.subscribe import SubscribeHelper
from app.log import logger
from app.plugins import _PluginBase
-from app.schemas.types import NotificationType
+from app.schemas.types import EventType, NotificationType
from app.utils.http import RequestUtils
@@ -35,7 +36,7 @@ class BangumiColl(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/wikrin/MoviePilot-Plugins/main/icons/bangumi_b.png"
# 插件版本
- plugin_version = "1.5"
+ plugin_version = "1.5.1"
# 插件作者
plugin_author = "Attente"
# 作者主页
@@ -47,7 +48,7 @@ class BangumiColl(_PluginBase):
# 可使用的用户级别
auth_level = 1
- # 私有变量
+ # 私有属性
_scheduler: Optional[BackgroundScheduler] = None
siteoper: SiteOper = None
subscribehelper: SubscribeHelper = None
@@ -95,6 +96,12 @@ class BangumiColl(_PluginBase):
"sites",
):
setattr(self, f"_{key}", config.get(key, getattr(self, f"_{key}")))
+ # 获得所有站点
+ site_ids = {site.id for site in self.siteoper.list_order_by_pri()}
+ # 过滤已删除的站点
+ self._sites = [site_id for site_id in self._sites if site_id in site_ids]
+ # 更新配置
+ self.__update_config()
def schedule_once(self):
"""调度一次性任务"""
@@ -138,7 +145,6 @@ class BangumiColl(_PluginBase):
{"title": site.name, "value": site.id}
for site in self.siteoper.list_order_by_pri()
]
-
return form(sites_options)
def get_service(self) -> List[Dict[str, Any]]:
@@ -167,6 +173,16 @@ class BangumiColl(_PluginBase):
except Exception as e:
logger.error(f"退出插件失败:{str(e)}")
+ @eventmanager.register(EventType.SiteDeleted)
+ def site_deleted(self, event: Event):
+ """
+ 删除对应站点
+ """
+ site_id = event.event_data.get("site_id")
+ if site_id in self._sites:
+ self._sites.remove(site_id)
+ self.__update_config()
+
def get_api(self):
pass
@@ -276,6 +292,7 @@ class BangumiColl(_PluginBase):
sid, msg = self.subscribechain.add(
title=mediainfo.title,
year=mediainfo.year,
+ season=mediainfo.number_of_seasons,
bangumiid=self._subid,
exist_ok=True,
username="Bangumi订阅",