fix v2 Emby instance fetch

This commit is contained in:
thsrite
2024-10-17 10:10:17 +08:00
parent c000068a58
commit 30e628a467
8 changed files with 848 additions and 678 deletions

View File

@@ -57,11 +57,12 @@
"name": "Emby媒体标签",
"description": "自动给媒体库媒体添加标签。",
"labels": "Emby",
"version": "1.2.1",
"version": "1.3",
"icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/tag.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.3": "适配v2多媒体服务器",
"v1.2": "支持指定特殊媒体名称添加标签",
"v1.1": "添加远程交互命令",
"v1.0": "自动给媒体库媒体添加标签"
@@ -71,11 +72,12 @@
"name": "Emby观影报告",
"description": "推送Emby观影报告需Emby安装Playback Report 插件。",
"labels": "Emby",
"version": "1.8.1",
"version": "1.9",
"icon": "Pydiocells_A.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.9": "适配v2多媒体服务器",
"v1.8": "推送微信增加时间戳,防止使用缓存",
"v1.7": "分块发送,兼容微信推送",
"v1.6": "fix #67",
@@ -118,11 +120,12 @@
"name": "Emby弹幕下载",
"description": "通知Emby Danmu插件下载弹幕。",
"labels": "Emby,媒体库",
"version": "1.2.1",
"version": "1.3",
"icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/danmu.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.3": "适配v2多媒体服务器",
"v1.2": "检查本地弹幕文件是否已存在,避免重复下载。",
"v1.1": "解析Emby日志判断已配置弹幕源是否全部匹配失败。",
"v1.0": "通知Emby Danmu插件下载弹幕。"
@@ -132,11 +135,12 @@
"name": "Emby视频类型检查",
"description": "定期检查Emby媒体库中是否包含指定的视频类型发送通知。",
"labels": "Emby,媒体库",
"version": "1.0.1",
"version": "1.1",
"icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/extendtype.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.1": "适配v2多媒体服务器",
"v1.0": "定期检查Emby媒体库中是否包含指定的视频类型发送通知。"
}
},
@@ -144,11 +148,12 @@
"name": "Emby有声书整理",
"description": "还在为Emby有声书整理烦恼吗入库存在很多单集",
"labels": "Emby,媒体库",
"version": "1.1.1",
"version": "1.2",
"icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/audiobook.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.2": "适配v2多媒体服务器",
"v1.1": "整理完锁定,防止数据被刷新",
"v1.0": "还在为Emby有声书整理烦恼吗入库存在很多单集。"
}
@@ -157,11 +162,12 @@
"name": "Emby合集媒体排序",
"description": "Emby保留按照加入时间倒序的前提下把合集中的媒体按照发布日期排序修改加入时间已到达顺序排列的目的。",
"labels": "媒体库",
"version": "1.1.1",
"version": "1.2",
"icon": "Element_A.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.2": "适配v2多媒体服务器",
"v1.1": "优化处理逻辑",
"v1.0": "保留按照加入时间倒序的前提下,把合集中的媒体放一块,不用到处找。"
}
@@ -170,11 +176,12 @@
"name": "Emby剧集演员同步",
"description": "同步剧演员信息到集演员信息。",
"labels": "Emby,媒体库",
"version": "1.3.1",
"version": "1.4",
"icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/embyactorsync.png",
"author": "thsrite",
"level": 1,
"history": {
"v1.4": "适配v2多媒体服务器",
"v1.3": "剧集优先使用季演员。",
"v1.2": "交互命令返回处理完成信息。",
"v1.1": "支持交互命令手动同步单个剧集 /as 媒体库名 剧集名。",

View File

@@ -11,7 +11,6 @@ from app.core.event import eventmanager, Event
from app.helper.mediaserver import MediaServerHelper
from app.log import logger
from app.plugins import _PluginBase
from app.modules.emby import Emby
from app.schemas.types import EventType, MediaType
from app.utils.http import RequestUtils
@@ -24,7 +23,7 @@ class EmbyActorSync(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/embyactorsync.png"
# 插件版本
plugin_version = "1.3.1"
plugin_version = "1.4"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -38,7 +37,8 @@ class EmbyActorSync(_PluginBase):
_onlyonce = False
_enabled = False
_librarys = None
_mediaservers = None
mediaserver_helper = None
_EMBY_HOST = None
_EMBY_USER = None
@@ -51,20 +51,7 @@ class EmbyActorSync(_PluginBase):
if config:
self._enabled = config.get("enabled")
self._onlyonce = config.get("onlyonce")
self._librarys = config.get("librarys") or []
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
self._mediaservers = config.get("mediaservers") or []
# 加载模块
if self._onlyonce:
@@ -98,7 +85,7 @@ class EmbyActorSync(_PluginBase):
{
"enabled": self._enabled,
"onlyonce": self._onlyonce,
"librarys": self._librarys,
"mediaservers": self._mediaservers,
}
)
@@ -130,72 +117,85 @@ class EmbyActorSync(_PluginBase):
"""
Emby剧集演员同步
"""
# 获取媒体库信息
librarys = Emby().get_librarys()
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
# 匹配需要的媒体库
for library in librarys:
if library.type != MediaType.TV.value:
continue
if self._librarys and library.name not in self._librarys:
continue
if library_name and library.name != library_name:
continue
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 获取媒体库媒体列表
library_items = self.__get_items(library.id)
if not library_items:
logger.error(f"获取媒体库:{library.name}的媒体列表失败")
continue
# 获取媒体库信息
librarys = emby_server.instance.get_librarys()
logger.info(f"开始同步媒体库:{library.name}ID{library.id}")
# 匹配需要的媒体库
for library in librarys:
if library.type != MediaType.TV.value:
continue
if library_name and library.name != library_name:
continue
# 遍历媒体列表获取媒体的ID和名称
for item in library_items:
if media_name:
# 电影弹幕
matches = re.findall(r'^(.+?)\s\(\d{4}\)$', item.get("Name").strip())
if (not matches and media_name != item.get("Name")) or (matches and str(matches[0]) != media_name):
continue
# 获取媒体库媒体列表
library_items = self.__get_items(library.id)
if not library_items:
logger.error(f"获取媒体库:{library.name}的媒体列表失败")
continue
logger.info(f"开始同步媒体:{item.get('Name')}ID{item.get('Id')}")
item_info = self.__get_item_info(item.get("Id"))
seasons = self.__get_items(item.get("Id"))
for season in seasons:
season_info = self.__get_item_info(season.get("Id"))
peoples = season_info.get("People") or item_info.get("People")
season_items = self.__get_items(season.get("Id"))
for season_item in season_items:
retry = 0
while retry < 3:
season_item_info = self.__get_item_info(season_item.get("Id"))
try:
if season_item_info.get("People") == peoples:
logger.warn(
f"媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 演员信息已更新")
retry = 3
continue
season_item_info.update({
"People": peoples
})
season_item_info["LockedFields"].append("Cast")
flag = self.__update_item_info(season_item.get("Id"), season_item_info)
logger.info(
f"更新媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 成功:{flag}")
if flag:
retry = 3
time.sleep(0.5)
else:
logger.info(f"开始同步媒体{library.name}ID{library.id}")
# 遍历媒体列表获取媒体的ID和名称
for item in library_items:
if media_name:
# 电影弹幕
matches = re.findall(r'^(.+?)\s\(\d{4}\)$', item.get("Name").strip())
if (not matches and media_name != item.get("Name")) or (matches and str(matches[0]) != media_name):
continue
logger.info(f"开始同步媒体:{item.get('Name')}ID{item.get('Id')}")
item_info = self.__get_item_info(item.get("Id"))
seasons = self.__get_items(item.get("Id"))
for season in seasons:
season_info = self.__get_item_info(season.get("Id"))
peoples = season_info.get("People") or item_info.get("People")
season_items = self.__get_items(season.get("Id"))
for season_item in season_items:
retry = 0
while retry < 3:
season_item_info = self.__get_item_info(season_item.get("Id"))
try:
if season_item_info.get("People") == peoples:
logger.warn(
f"媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 演员信息已更新")
retry = 3
continue
season_item_info.update({
"People": peoples
})
season_item_info["LockedFields"].append("Cast")
flag = self.__update_item_info(season_item.get("Id"), season_item_info)
logger.info(
f"更新媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 成功:{flag}")
if flag:
retry = 3
time.sleep(0.5)
else:
retry += 1
except Exception as e:
retry += 1
except Exception as e:
retry += 1
logger.error(
f"更新媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 信息出错:{e} 开始重试...{retry} / 3")
if event:
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {media_name} 同步完成",
userid=event.event_data.get("user"))
logger.info(f"Emby剧集演员同步完成")
logger.error(
f"更新媒体:{item.get('Name')} {season_item_info.get('SeasonName')} {season_item_info.get('IndexNumber')} {season_item_info.get('Name')} 信息出错:{e} 开始重试...{retry} / 3")
if event:
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {media_name} 同步完成",
userid=event.event_data.get("user"))
logger.info(f"{emby_name} 剧集演员同步完成")
def __update_item_info(self, item_id, data):
headers = {
@@ -256,9 +256,6 @@ class EmbyActorSync(_PluginBase):
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
"""
librarys = Emby().get_librarys()
library_options = [{'title': library.name, 'value': library.name} for library in librarys if
library.type == MediaType.TV.value]
return [
{
"component": "VForm",
@@ -301,26 +298,30 @@ class EmbyActorSync(_PluginBase):
]
},
{
"component": "VRow",
"content": [
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'model': 'librarys',
'label': '媒体库',
'items': library_options
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
},
],
}
]
},
{
'component': 'VRow',
@@ -348,7 +349,7 @@ class EmbyActorSync(_PluginBase):
], {
"enabled": False,
"onlyonce": False,
"librarys": [],
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -28,7 +28,7 @@ class EmbyAudioBook(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/audiobook.png"
# 插件版本
plugin_version = "1.1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -49,6 +49,7 @@ class EmbyAudioBook(_PluginBase):
_cron = None
_library_id = None
_msgtype = None
_mediaservers = None
mediaserver_helper = None
_EMBY_HOST = None
@@ -70,20 +71,7 @@ class EmbyAudioBook(_PluginBase):
self._notify = config.get("notify")
self._rename = config.get("rename")
self._msgtype = config.get("msgtype")
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
self._mediaservers = config.get("mediaservers") or []
# 停止现有任务
self.stop_service()
@@ -132,6 +120,7 @@ class EmbyAudioBook(_PluginBase):
"cron": self._cron,
"notify": self._notify,
"msgtype": self._msgtype,
"mediaservers": self._mediaservers,
})
def check(self):
@@ -139,48 +128,63 @@ class EmbyAudioBook(_PluginBase):
logger.error("请设置有声书文件夹ID")
return
# 获取所有有声书
items = self.__get_items(parent_id=int(self._library_id))
if not items:
logger.error(f"获取媒体库 {self._library_id} 有声书列表失败!")
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
# 检查有声书是否需要整理
for item in items:
book_items = self.__get_items(item.get("Id"))
if not book_items:
logger.error(f"获取 {item.get('Name')} {item.get('Id')} 有声书失败!")
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 获取所有有声书
items = self.__get_items(parent_id=int(self._library_id))
if not items:
logger.error(f"获取媒体库 {self._library_id} 有声书列表失败!")
return
# 检查有声书是否需要整理
__need_zl = False
for book_item in book_items:
if not book_item.get("AlbumId"):
__need_zl = True
break
for item in items:
book_items = self.__get_items(item.get("Id"))
if not book_items:
logger.error(f"获取 {item.get('Name')} {item.get('Id')} 有声书失败!")
return
# 需要整理的提示需要整理
if __need_zl:
logger.info(f"有声书 {item.get('Name')} 需要整理,共 {len(book_items)}")
# self.__zl(items, -1)
# 发送通知
if self._notify:
mtype = NotificationType.Manual
if self._msgtype:
mtype = NotificationType.__getitem__(str(self._msgtype)) or NotificationType.Manual
self.post_message(title="Emby有声书整理",
mtype=mtype,
text=f"有声书 {item.get('Name')} 需要整理,共 {len(book_items)}")
else:
# 不需要整理的锁定
other_book_info = self.__get_item_info(item.get("Id"))
other_book_info.update({
"LockData": True,
})
self.__update_item_info(item.get("Id"), other_book_info)
logger.info(f"有声书 {item.get('Name')} 不需要整理,已锁定")
# 检查有声书是否需要整理
__need_zl = False
for book_item in book_items:
if not book_item.get("AlbumId"):
__need_zl = True
break
logger.info("Emby有声书整理服务执行完毕")
# 需要整理的提示需要整理
if __need_zl:
logger.info(f"有声书 {item.get('Name')} 需要整理,共 {len(book_items)}")
# self.__zl(items, -1)
# 发送通知
if self._notify:
mtype = NotificationType.Manual
if self._msgtype:
mtype = NotificationType.__getitem__(str(self._msgtype)) or NotificationType.Manual
self.post_message(title="Emby有声书整理",
mtype=mtype,
text=f"有声书 {item.get('Name')} 需要整理,共 {len(book_items)}")
else:
# 不需要整理的锁定
other_book_info = self.__get_item_info(item.get("Id"))
other_book_info.update({
"LockData": True,
})
self.__update_item_info(item.get("Id"), other_book_info)
logger.info(f"有声书 {item.get('Name')} 不需要整理,已锁定")
logger.info(f"{emby_name} 有声书整理服务执行完毕")
@eventmanager.register(EventType.PluginAction)
def audiobook(self, event: Event = None):
@@ -573,6 +577,32 @@ class EmbyAudioBook(_PluginBase):
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -604,6 +634,7 @@ class EmbyAudioBook(_PluginBase):
"cron": "",
"msgtype": "",
"library_id": "",
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -27,7 +27,7 @@ class EmbyCollectionSort(_PluginBase):
# 插件图标
plugin_icon = "Element_A.png"
# 插件版本
plugin_version = "1.1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -45,6 +45,8 @@ class EmbyCollectionSort(_PluginBase):
_cron = None
_sort_type = None
_collection_library_id = None
_mediaservers = None
mediaserver_helper = None
_EMBY_HOST = None
_EMBY_USER = None
@@ -62,20 +64,8 @@ class EmbyCollectionSort(_PluginBase):
self._cron = config.get("cron")
self._sort_type = config.get("sort_type") or "asc"
self._collection_library_id = config.get("collection_library_id")
self._mediaservers = config.get("mediaservers") or []
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 加载模块
if self._enabled or self._onlyonce:
# 定时服务
@@ -121,6 +111,7 @@ class EmbyCollectionSort(_PluginBase):
"enabled": self._enabled,
"sort_type": self._sort_type,
"collection_library_id": self._collection_library_id,
"mediaservers": self._mediaservers,
}
)
@@ -128,93 +119,104 @@ class EmbyCollectionSort(_PluginBase):
"""
更改合集媒体入库时间
"""
if "emby" not in settings.MEDIASERVER:
logger.error("未配置Emby媒体服务器")
return
if not self._collection_library_id:
logger.error("未配置合集所在媒体库")
return
# 获取合集列表
collections = self.__get_items(self._collection_library_id)
handle_times = []
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
for collection in collections:
logger.info(f"开始处理合集: {collection.get('Name')} {collection.get('Id')}")
items = self.__get_items(collection.get("Id"))
item_dict = []
for item in items:
item_info = self.__get_item_info(item.get("Id"))
item_dict.append({"Name": item.get("Name"), "Id": item.get("Id"), "item_info": item_info})
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 按照发布时间排序
sorted_items = sorted(item_dict, key=lambda x: x.get("item_info").get("PremiereDate"),
reverse=self._sort_type == "降序")
# 初始化时间
current_time = datetime.strptime(sorted_items[0]["item_info"]["DateCreated"], "%Y-%m-%dT%H:%M:%S.%f0Z")
# 获取合集列表
collections = self.__get_items(self._collection_library_id)
handle_times = []
# 更新每个 item 的 DateCreated规则为
updated_items = []
for collection in collections:
logger.info(f"开始处理合集: {collection.get('Name')} {collection.get('Id')}")
items = self.__get_items(collection.get("Id"))
item_dict = []
for item in items:
item_info = self.__get_item_info(item.get("Id"))
item_dict.append({"Name": item.get("Name"), "Id": item.get("Id"), "item_info": item_info})
while sorted_items:
sub_update_items = []
# 按照发布时间排序
sorted_items = sorted(item_dict, key=lambda x: x.get("item_info").get("PremiereDate"),
reverse=self._sort_type == "降序")
# 初始化时间
current_time = datetime.strptime(sorted_items[0]["item_info"]["DateCreated"], "%Y-%m-%dT%H:%M:%S.%f0Z")
# 更新每个 item 的 DateCreated规则为
updated_items = []
while sorted_items:
sub_update_items = []
for item in sorted_items:
with lock:
new_date_created = current_time.strftime("%Y-%m-%dT%H:%M:%S.%f0Z")
# 时间相同,跳过
if str(new_date_created) == str(item['item_info']['DateCreated']):
logger.debug(
f"合集媒体: {item.get('Name')} 原入库时间 {item['item_info']['DateCreated']} 新入库时间 {new_date_created} 时间相同,跳过")
handle_times.append(str(current_time))
sub_update_items.append(str(current_time))
# 时间减一秒,用于下一个 item 的更新
current_time -= timedelta(seconds=1)
continue
if str(current_time) in handle_times:
logger.warn(
f"合集媒体: {item.get('Name')} {current_time} 时间已被占用,开始增加 {len(sorted_items) + 1} 秒,重新尝试处理")
# 处理完成的 items 从列表中移除
handle_times = [str(_time) for _time in handle_times if _time not in sub_update_items]
# 如果时间已被占用,增加 len(sorted_items) + 1 秒
current_time += timedelta(seconds=len(sorted_items) + 1)
# 重置已处理的 items 列表和 handle_times 集合
updated_items.clear()
# 时间已被占用,跳出 for 循环
break
for item in sorted_items:
with lock:
new_date_created = current_time.strftime("%Y-%m-%dT%H:%M:%S.%f0Z")
# 时间相同,跳过
if str(new_date_created) == str(item['item_info']['DateCreated']):
logger.debug(
f"合集媒体: {item.get('Name')} 原入库时间 {item['item_info']['DateCreated']} 新入库时间 {new_date_created} 时间相同,跳过")
f"合集媒体: {item.get('Name')} 原入库时间 {item['item_info']['DateCreated']} 新入库时间 {new_date_created}")
item["item_info"]["DateCreated"] = new_date_created
updated_items.append(item["item_info"])
handle_times.append(str(current_time))
sub_update_items.append(str(current_time))
# 时间减一秒,用于下一个 item 的更新
current_time -= timedelta(seconds=1)
continue
else:
# 所有 item 处理完成,跳出 while 循环
break
time.sleep(1)
if str(current_time) in handle_times:
logger.warn(
f"合集媒体: {item.get('Name')} {current_time} 时间已被占用,开始增加 {len(sorted_items) + 1} 秒,重新尝试处理")
# 处理完成的 items 从列表中移除
handle_times = [str(_time) for _time in handle_times if _time not in sub_update_items]
# 如果时间已被占用,增加 len(sorted_items) + 1 秒
current_time += timedelta(seconds=len(sorted_items) + 1)
# 重置已处理的 items 列表和 handle_times 集合
updated_items.clear()
# 时间已被占用,跳出 for 循环
break
if not updated_items:
logger.warn(f"合集: {collection.get('Name')} {collection.get('Id')} 无需更新入库时间")
continue
logger.debug(
f"合集媒体: {item.get('Name')} 原入库时间 {item['item_info']['DateCreated']} 新入库时间 {new_date_created}")
item["item_info"]["DateCreated"] = new_date_created
updated_items.append(item["item_info"])
handle_times.append(str(current_time))
sub_update_items.append(str(current_time))
# 时间减一秒,用于下一个 item 的更新
current_time -= timedelta(seconds=1)
else:
# 所有 item 处理完成,跳出 while 循环
break
time.sleep(1)
logger.debug(f"获取合集排序后最新的入库时间: {current_time}")
if not updated_items:
logger.warn(f"合集: {collection.get('Name')} {collection.get('Id')} 无需更新入库时间")
continue
# 更新入库时间
for item_info in updated_items:
update_flag = self.__update_item_info(item_info.get("Id"), item_info)
if update_flag:
logger.info(f"{item_info.get('Name')} 更新入库时间到{item_info.get('DateCreated')}成功")
else:
logger.error(f"{item_info.get('Name')} 更新入库时间到{item_info.get('DateCreated')}失败")
logger.debug(f"获取合集排序后最新的入库时间: {current_time}")
logger.info(f"合集处理完成: {collection.get('Name')} {collection.get('Id')}")
# 更新入库时间
for item_info in updated_items:
update_flag = self.__update_item_info(item_info.get("Id"), item_info)
if update_flag:
logger.info(f"{item_info.get('Name')} 更新入库时间到{item_info.get('DateCreated')}成功")
else:
logger.error(f"{item_info.get('Name')} 更新入库时间到{item_info.get('DateCreated')}失败")
logger.info(f"合集处理完成: {collection.get('Name')} {collection.get('Id')}")
logger.info(f"更新Emby合集媒体排序完成")
logger.info(f"更新 {emby_name} 合集媒体排序完成")
@eventmanager.register(EventType.PluginAction)
def remote_sync(self, event: Event):
@@ -378,7 +380,32 @@ class EmbyCollectionSort(_PluginBase):
},
],
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -408,6 +435,7 @@ class EmbyCollectionSort(_PluginBase):
"sort_type": "降序",
"cron": "5 1 * * *",
"collection_library_id": "",
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -20,7 +20,7 @@ class EmbyDanmu(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/danmu.png"
# 插件版本
plugin_version = "1.2.1"
plugin_version = "1.3"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -36,6 +36,7 @@ class EmbyDanmu(_PluginBase):
_enabled = False
_library_task = {}
_danmu_source = []
_mediaservers = None
mediaserver_helper = None
_EMBY_HOST = None
@@ -49,19 +50,7 @@ class EmbyDanmu(_PluginBase):
# 读取配置
if config:
self._enabled = config.get("enabled")
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
self._mediaservers = config.get("mediaservers") or []
self._danmu_source = self.__get_danmu_source()
@@ -95,145 +84,210 @@ class EmbyDanmu(_PluginBase):
userid=event.event_data.get("user"))
return
library_name = args_list[0]
library_item_name = args_list[1]
library_item_season = None
if len(args_list) == 3:
library_item_season = int(args_list[2])
logger.info(
f"开始下载弹幕文件:{library_name} - {library_item_name} {f'(季{library_item_season})' if library_item_season else ''}")
# 获取媒体库信息
librarys = self.__get_librarys()
library_id = None
library_options = None
library_type = None
# 匹配需要的媒体库
for library in librarys:
if library.get("Name") == library_name:
logger.info(f"找到媒体库:{library_name}ID{library.get('Id')}")
library_type = library.get("CollectionType")
library_id = library.get("Id")
library_options = library.get("LibraryOptions")
break
if not library_id or not library_options:
logger.error(f"未找到媒体库:{library_name}")
self.post_message(channel=event.event_data.get("channel"),
title=f"未找到媒体库:{library_name}",
userid=event.event_data.get("user"))
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
# 开启Danmu插件
# 检查是否已经禁用了Danmu插件如禁用则先启用
enabled_danmu = False
library_disabled_subtitle_fetchers = library_options.get("DisabledSubtitleFetchers", [])
if "Danmu" in library_disabled_subtitle_fetchers:
library_disabled_subtitle_fetchers.remove("Danmu")
library_options.update({
"DisabledSubtitleFetchers": library_disabled_subtitle_fetchers,
})
enabled_danmu = True
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 启用Danmu插件
if enabled_danmu:
update_flag = self.__update_library(library_id, library_options)
if update_flag:
logger.info(f"已启用媒体库:{library_name}的Danmu插件")
else:
logger.error(f"启用媒体库:{library_name}的Danmu插件失败")
library_name = args_list[0]
library_item_name = args_list[1]
library_item_season = None
if len(args_list) == 3:
library_item_season = int(args_list[2])
logger.info(
f"开始下载弹幕文件:{library_name} - {library_item_name} {f'(季{library_item_season})' if library_item_season else ''}")
# 获取媒体库信息
librarys = self.__get_librarys()
library_id = None
library_options = None
library_type = None
# 匹配需要的媒体库
for library in librarys:
if library.get("Name") == library_name:
logger.info(f"找到媒体库:{library_name}ID{library.get('Id')}")
library_type = library.get("CollectionType")
library_id = library.get("Id")
library_options = library.get("LibraryOptions")
break
if not library_id or not library_options:
logger.error(f"未找到媒体库:{library_name}")
self.post_message(channel=event.event_data.get("channel"),
title=f"启用媒体库:{library_name}的Danmu插件失败",
title=f"未找到媒体库:{library_name}",
userid=event.event_data.get("user"))
return
else:
logger.info(f"媒体库:{library_name}的Danmu插件已启用")
break
# 媒体库设置为正在任务,不关闭弹幕插件
_library_task = self._library_task.get(library_id, [])
_library_task.append(library_item_name)
self._library_task[library_id] = _library_task
# 开启Danmu插件
# 检查是否已经禁用了Danmu插件如禁用则先启用
enabled_danmu = False
library_disabled_subtitle_fetchers = library_options.get("DisabledSubtitleFetchers", [])
if "Danmu" in library_disabled_subtitle_fetchers:
library_disabled_subtitle_fetchers.remove("Danmu")
library_options.update({
"DisabledSubtitleFetchers": library_disabled_subtitle_fetchers,
})
enabled_danmu = True
try:
# 获取媒体库媒体列表
library_items = self.__get_items(library_id)
if not library_items:
logger.error(f"获取媒体库:{library_name}媒体列表失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"获取媒体库:{library_name}媒体列表失败",
userid=event.event_data.get("user"))
# 启用Danmu插件
if enabled_danmu:
update_flag = self.__update_library(library_id, library_options)
if update_flag:
logger.info(f"已启用媒体库:{library_name}Danmu插件")
else:
logger.error(f"启用媒体库:{library_name}Danmu插件失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"启用媒体库:{library_name}的Danmu插件失败",
userid=event.event_data.get("user"))
return
else:
found_item = False
# 遍历媒体列表获取媒体的ID和名称
for item in library_items:
if library_type == "tvshows":
if item.get("Name") == library_item_name:
found_item = True
logger.info(f"找到媒体:{library_item_name}ID{item.get('Id')}")
logger.info(f"媒体库:{library_name}的Danmu插件已启用")
# 电视剧弹幕
seasons = self.__get_items(item.get("Id"))
if len(seasons) == 1:
season_item = seasons[0]
if library_item_season and season_item.get("IndexNumber") != library_item_season:
found_item = False
break
# 媒体库设置为正在任务,不关闭弹幕插件
_library_task = self._library_task.get(library_id, [])
_library_task.append(library_item_name)
self._library_task[library_id] = _library_task
# 通知Danmu插件获取弹幕
season_id = season_item.get("Id")
# 判断本地弹幕是否存在
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=True)
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
break
try:
# 获取媒体库媒体列表
library_items = self.__get_items(library_id)
if not library_items:
logger.error(f"获取媒体库:{library_name}的媒体列表失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"获取媒体库:{library_name}的媒体列表失败",
userid=event.event_data.get("user"))
else:
found_item = False
# 遍历媒体列表获取媒体的ID和名称
for item in library_items:
if library_type == "tvshows":
if item.get("Name") == library_item_name:
found_item = True
logger.info(f"找到媒体:{library_item_name}ID{item.get('Id')}")
danmu_flag = self.__download_danmu(season_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
# 电视剧弹幕
seasons = self.__get_items(item.get("Id"))
if len(seasons) == 1:
season_item = seasons[0]
if library_item_season and season_item.get("IndexNumber") != library_item_season:
found_item = False
break
# 通知Danmu插件获取弹幕
season_id = season_item.get("Id")
# 判断本地弹幕是否存在
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=False)
if danmu_cnt == 0:
logger.error(
f"{library_name} {library_item_name} Emby已配置弹幕源全部匹配弹幕失败")
only_check=True)
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name} {season_item.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name} Emby已配置弹幕源全部匹配弹幕失败",
title=f"{library_name} {library_item_name} {season_item.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}")
break
danmu_flag = self.__download_danmu(season_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=False)
if danmu_cnt == 0:
logger.error(
f"{library_name} {library_item_name} Emby已配置弹幕源全部匹配弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name} {season_item.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}",
title=f"{library_name} {library_item_name} Emby已配置弹幕源全部匹配弹幕失败",
userid=event.event_data.get("user"))
else:
logger.error(
f"{library_name} {library_item_name} 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"{library_name} {library_item_name} 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season_item.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕失败",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} {library_item_name}{season_item.get('IndexNumber')}季 的弹幕失败",
userid=event.event_data.get("user"))
else:
for season in seasons:
# 指定季度则只获取指定季度的弹幕
if library_item_season:
found_item = False
if season.get("IndexNumber") == library_item_season:
found_item = True
for season in seasons:
# 指定季度则只获取指定季度的弹幕
if library_item_season:
found_item = False
if season.get("IndexNumber") == library_item_season:
found_item = True
season_id = season.get("Id")
# 判断本地弹幕是否存在
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=True)
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
break
# 通知Danmu插件获取弹幕
danmu_flag = self.__download_danmu(season_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=False)
if danmu_cnt == 0:
logger.error(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 Emby已配置弹幕源全部匹配弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 Emby已配置弹幕源全部匹配弹幕失败",
userid=event.event_data.get("user"))
else:
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕失败",
userid=event.event_data.get("user"))
break
else:
# 未指定季度则获取全部季度的弹幕
season_id = season.get("Id")
# 判断本地弹幕是否存在
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
@@ -244,7 +298,7 @@ class EmbyDanmu(_PluginBase):
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
break
continue
# 通知Danmu插件获取弹幕
danmu_flag = self.__download_danmu(season_id)
@@ -281,150 +335,100 @@ class EmbyDanmu(_PluginBase):
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕失败",
userid=event.event_data.get("user"))
break
else:
# 未指定季度则获取全部季度的弹幕
season_id = season.get("Id")
# 判断本地弹幕是否存在
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=True)
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部存在:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
continue
# 通知Danmu插件获取弹幕
danmu_flag = self.__download_danmu(season_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
danmu_cnt, season_item_cnt = self.__check_danmu_exists(season_id,
only_check=False)
if danmu_cnt == 0:
logger.error(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 Emby已配置弹幕源全部匹配弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 Emby已配置弹幕源全部匹配弹幕失败",
userid=event.event_data.get("user"))
else:
if season_item_cnt == danmu_cnt:
logger.info(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件已全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {library_item_name}{season.get('IndexNumber')}季 弹幕文件未全部下载完成:{danmu_cnt}/{season_item_cnt}",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} {library_item_name}{season.get('IndexNumber')}季 的弹幕失败",
userid=event.event_data.get("user"))
else:
# 电影弹幕
matches = re.findall(r'^(.+?)\s\(\d{4}\)', item.get("Name"), re.MULTILINE)
if matches and str(matches[0]) == library_item_name:
found_item = True
movie_id = item.get("Id")
movie_items = self.__get_items(movie_id)
if not movie_items:
logger.error(f"获取 {library_name} {item.get('Name')}的媒体列表失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"获取电影:{library_name} {item.get('Name')}的媒体列表失败",
userid=event.event_data.get("user"))
else:
movie_id = movie_items[0].get("Id")
# 获取媒体详情
item_info = self.__get_item_info(movie_id)
item_path = item_info.get("Path")
parent_path = Path(item_path).parent
logger.info(f"开始检查路径 {parent_path} 下是是否有弹幕文件")
# 检查是否有弹幕文件
danmu_path_pattern = Path(item_path).stem + "*.xml"
if len(list(parent_path.glob(danmu_path_pattern))) >= 1:
logger.info(f"{parent_path} 下已存在弹幕文件:{danmu_path_pattern}")
else:
# 电影弹幕
matches = re.findall(r'^(.+?)\s\(\d{4}\)', item.get("Name"), re.MULTILINE)
if matches and str(matches[0]) == library_item_name:
found_item = True
movie_id = item.get("Id")
movie_items = self.__get_items(movie_id)
if not movie_items:
logger.error(f"获取 {library_name} {item.get('Name')}的媒体列表失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 弹幕已存在",
title=f"获取电影:{library_name} {item.get('Name')}的媒体列表失败",
userid=event.event_data.get("user"))
else:
# 通知Danmu插件获取弹幕
danmu_flag = self.__download_danmu(movie_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {item.get('Name')} {movie_id} 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {item.get('Name')} 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
retry_cnt = 3
while len(
list(parent_path.glob(danmu_path_pattern))) == 0 and retry_cnt > 0:
# 解析日志判断是否全部失败
if self.__check_all_failed_by_log(item_name=item_info.get("Name"),
item_year=item_info.get(
"ProductionYear")):
logger.error(f"解析日志判断已配置弹幕源全部匹配弹幕失败")
retry_cnt = -1
else:
retry_cnt -= 1
logger.warn(
f"{parent_path} 下未找到弹幕文件:{danmu_path_pattern}等待60秒后重试 ({retry_cnt}次)")
time.sleep(60)
movie_id = movie_items[0].get("Id")
# 获取媒体详情
item_info = self.__get_item_info(movie_id)
item_path = item_info.get("Path")
parent_path = Path(item_path).parent
logger.info(f"开始检查路径 {parent_path} 下是是否有弹幕文件")
# 检查是否有弹幕文件
danmu_path_pattern = Path(item_path).stem + "*.xml"
if len(list(parent_path.glob(danmu_path_pattern))) >= 1:
logger.info(f"{parent_path} 下已找到弹幕文件:{danmu_path_pattern}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 下载弹幕文件成功",
userid=event.event_data.get("user"))
else:
logger.error(f"{parent_path} 下未找到弹幕文件:{danmu_path_pattern}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 已配置弹幕源全部匹配弹幕失败",
userid=event.event_data.get("user"))
if len(list(parent_path.glob(danmu_path_pattern))) >= 1:
logger.info(f"{parent_path} 下已存在弹幕文件:{danmu_path_pattern}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 弹幕已存在",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {item.get('Name')} {movie_id} 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} 电影 {item.get('Name')} {movie_id} 的弹幕失败",
userid=event.event_data.get("user"))
if not found_item:
logger.error(
f"未找到媒体:{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}")
self.post_message(channel=event.event_data.get("channel"),
title=f"未找到媒体:{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}",
userid=event.event_data.get("user"))
except Exception as e:
logger.error(
f"{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}获取弹幕任务出错:{str(e)}")
# 通知Danmu插件获取弹幕
danmu_flag = self.__download_danmu(movie_id)
if danmu_flag:
logger.info(
f"已通知弹幕插件获取 {library_name} {item.get('Name')} {movie_id} 的弹幕")
self.post_message(channel=event.event_data.get("channel"),
title=f"开始通知Emby下载 {library_name} {item.get('Name')} 弹幕,异步执行,请耐心等候执行完成消息",
userid=event.event_data.get("user"))
retry_cnt = 3
while len(
list(parent_path.glob(danmu_path_pattern))) == 0 and retry_cnt > 0:
# 解析日志判断是否全部失败
if self.__check_all_failed_by_log(item_name=item_info.get("Name"),
item_year=item_info.get(
"ProductionYear")):
logger.error(f"解析日志判断已配置弹幕源全部匹配弹幕失败")
retry_cnt = -1
else:
retry_cnt -= 1
logger.warn(
f"{parent_path} 下未找到弹幕文件:{danmu_path_pattern}等待60秒后重试 ({retry_cnt}次)")
time.sleep(60)
# 判断当前媒体库是否有其他任务在执行
self._library_task[library_id].remove(library_item_name)
if len(self._library_task[library_id]) == 0:
# 关闭弹幕插件
logger.info(
f"{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}获取弹幕任务完成,关闭弹幕插件")
# 禁用媒体库的Danmu插件
library_disabled_subtitle_fetchers = library_options.get("DisabledSubtitleFetchers", [])
library_disabled_subtitle_fetchers.append("Danmu")
library_options.update({
"DisabledSubtitleFetchers": library_disabled_subtitle_fetchers,
})
update_flag = self.__update_library(library_id, library_options)
if update_flag:
logger.info(f"已禁用媒体库:{library_name} Danmu插件")
else:
logger.error(f"禁用媒体库:{library_name} Danmu插件失败")
if len(list(parent_path.glob(danmu_path_pattern))) >= 1:
logger.info(f"{parent_path} 下已找到弹幕文件:{danmu_path_pattern}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 下载弹幕文件成功",
userid=event.event_data.get("user"))
else:
logger.error(f"{parent_path} 下未找到弹幕文件:{danmu_path_pattern}")
self.post_message(channel=event.event_data.get("channel"),
title=f"{library_name} {item.get('Name')} 已配置弹幕源全部匹配弹幕失败",
userid=event.event_data.get("user"))
else:
logger.error(
f"通知弹幕插件获取 {library_name} {item.get('Name')} {movie_id} 的弹幕失败")
self.post_message(channel=event.event_data.get("channel"),
title=f"通知弹幕插件获取 {library_name} 电影 {item.get('Name')} {movie_id} 的弹幕失败",
userid=event.event_data.get("user"))
if not found_item:
logger.error(
f"未找到媒体:{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}")
self.post_message(channel=event.event_data.get("channel"),
title=f"未找到媒体:{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}",
userid=event.event_data.get("user"))
except Exception as e:
logger.error(
f"{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}获取弹幕任务出错:{str(e)}")
# 判断当前媒体库是否有其他任务在执行
self._library_task[library_id].remove(library_item_name)
if len(self._library_task[library_id]) == 0:
# 关闭弹幕插件
logger.info(
f"{library_name} {library_item_name} {f'{library_item_season}' if library_item_season else ''}获取弹幕任务完成,关闭弹幕插件")
# 禁用媒体库的Danmu插件
library_disabled_subtitle_fetchers = library_options.get("DisabledSubtitleFetchers", [])
library_disabled_subtitle_fetchers.append("Danmu")
library_options.update({
"DisabledSubtitleFetchers": library_disabled_subtitle_fetchers,
})
update_flag = self.__update_library(library_id, library_options)
if update_flag:
logger.info(f"已禁用媒体库:{library_name} Danmu插件")
else:
logger.error(f"禁用媒体库:{library_name} Danmu插件失败")
def get_state(self) -> bool:
return self._enabled
@@ -705,6 +709,32 @@ class EmbyDanmu(_PluginBase):
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -751,6 +781,7 @@ class EmbyDanmu(_PluginBase):
}
], {
"enabled": False,
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -25,7 +25,7 @@ class EmbyExtendType(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/extendtype.png"
# 插件版本
plugin_version = "1.0.1"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -46,6 +46,7 @@ class EmbyExtendType(_PluginBase):
_librarys = None
_extend = None
_msgtype = None
_mediaservers = None
# 退出事件
_event = threading.Event()
@@ -66,19 +67,7 @@ class EmbyExtendType(_PluginBase):
self._notify = config.get("notify")
self._extend = config.get("extend")
self._msgtype = config.get("msgtype")
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
self._mediaservers = config.get("mediaservers") or []
# 停止现有任务
self.stop_service()
@@ -123,30 +112,44 @@ class EmbyExtendType(_PluginBase):
logger.error("视频类型为空,不进行检查")
return
if not self._librarys:
logger.error("媒体库为空,不进行检查")
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
logger.info(f"开始检查媒体库 {self._librarys} 中是否包含 {self._extend} 类型")
for library in self._librarys:
library_name, library_id = library.split(" ")
logger.info(f"开始检查媒体库 {library_name} 中是否包含 {self._extend} 类型")
library_extends = self.__get_extend_type(library_id)
if library_extends:
for extend in self._extend.split(","):
if extend in [item.get("Name") for item in library_extends]:
logger.info(f"媒体库 {library_name} 中包含 {extend} 类型")
# 发送通知
if self._notify:
mtype = NotificationType.Manual
if self._msgtype:
mtype = NotificationType.__getitem__(str(self._msgtype)) or NotificationType.Manual
self.post_message(title="Emby视频类型检查",
mtype=mtype,
text=f"媒体库 {library_name} 命中 {extend} 视频类型")
logger.info(f"媒体库 {library_name} 中全部视频类型检查完毕")
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
logger.info(f"媒体库 {self._librarys} 中全部视频类型检查完毕")
# 获取媒体库信息
librarys = emby_server.instance.get_librarys()
for library in librarys:
logger.info(f"开始检查媒体库 {emby_name} {library.name} 中是否包含 {self._extend} 类型")
library_id = library.id
library_name = library.name
logger.info(f"开始检查媒体库 {library_name} 中是否包含 {self._extend} 类型")
library_extends = self.__get_extend_type(library_id)
if library_extends:
for extend in self._extend.split(","):
if extend in [item.get("Name") for item in library_extends]:
logger.info(f"媒体库 {library_name} 中包含 {extend} 类型")
# 发送通知
if self._notify:
mtype = NotificationType.Manual
if self._msgtype:
mtype = NotificationType.__getitem__(str(self._msgtype)) or NotificationType.Manual
self.post_message(title="Emby视频类型检查",
mtype=mtype,
text=f"媒体库 {library_name} 命中 {extend} 视频类型")
logger.info(f"{emby_name} 媒体库 {library_name} 中全部视频类型检查完毕")
logger.info(f"{emby_name} 媒体库中全部视频类型检查完毕")
def __get_extend_type(self, parent_id) -> list:
"""
@@ -179,6 +182,7 @@ class EmbyExtendType(_PluginBase):
"extend": self._extend,
"notify": self._notify,
"msgtype": self._msgtype,
"mediaservers": self._mediaservers,
})
def get_state(self) -> bool:
@@ -325,7 +329,7 @@ class EmbyExtendType(_PluginBase):
{
'component': 'VCol',
'props': {
'cols': 12,
'cols': 12
},
'content': [
{
@@ -333,13 +337,16 @@ class EmbyExtendType(_PluginBase):
'props': {
'multiple': True,
'chips': True,
'model': 'librarys',
'label': '媒体库',
'items': library_items
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
},
}
]
},
]
@@ -350,7 +357,7 @@ class EmbyExtendType(_PluginBase):
"notify": False,
"cron": "",
"extend": "",
"librarys": [],
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -23,7 +23,7 @@ class EmbyMetaTag(_PluginBase):
# 插件图标
plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/tag.png"
# 插件版本
plugin_version = "1.2.1"
plugin_version = "1.3"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -41,6 +41,8 @@ class EmbyMetaTag(_PluginBase):
_cron = None
_tag_confs = None
_name_tag_confs = None
_mediaservers = None
mediaserver_helper = None
_EMBY_HOST = None
_EMBY_USER = None
@@ -62,19 +64,7 @@ class EmbyMetaTag(_PluginBase):
self._cron = config.get("cron")
self._tag_confs = config.get("tag_confs")
self._name_tag_confs = config.get("name_tag_confs")
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_HOST = emby_server.config.get("host")
self._EMBY_APIKEY = emby_server.config.get("apikey")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
self._mediaservers = config.get("mediaservers") or []
_tags = {}
if self._tag_confs:
@@ -147,6 +137,7 @@ class EmbyMetaTag(_PluginBase):
"enabled": self._enabled,
"tag_confs": self._tag_confs,
"name_tag_confs": self._name_tag_confs,
"mediaservers": self._mediaservers,
}
)
@@ -154,85 +145,96 @@ class EmbyMetaTag(_PluginBase):
"""
给设定媒体库打标签
"""
if "emby" not in settings.MEDIASERVER:
logger.error("未配置Emby媒体服务器")
return
if (not self._tags or len(self._tags.keys()) == 0) and (
not self._media_tags or len(self._media_tags.keys()) == 0):
logger.error("未配置Emby媒体标签")
return
# 媒体库标签
if self._tags and len(self._tags.keys()) > 0:
# 获取emby 媒体
librarys = Emby().get_librarys()
if not librarys:
logger.error("获取媒体库失败")
return
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
# 遍历媒体库,获取媒体库媒体
for library in librarys:
# 获取媒体库标签
library_tags = self._tags.get(library.name)
if not library_tags:
continue
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self._EMBY_USER = emby_server.instance.get_user()
self._EMBY_APIKEY = emby_server.config.config.get("apikey")
self._EMBY_HOST = emby_server.config.config.get("host")
if not self._EMBY_HOST.endswith("/"):
self._EMBY_HOST += "/"
if not self._EMBY_HOST.startswith("http"):
self._EMBY_HOST = "http://" + self._EMBY_HOST
# 获取媒体库媒体
library_items = Emby().get_items(library.id)
if not library_items:
continue
# 媒体库标签
if self._tags and len(self._tags.keys()) > 0:
# 获取emby 媒体库
librarys = emby_server.instance.get_librarys()
if not librarys:
logger.error("获取媒体库失败")
return
for library_item in library_items:
if not library_item:
continue
# 获取item的tag
item_tags = self.__get_item_tags(library_item.item_id) or []
# 获取缺少的tag
add_tags = []
for library_tag in library_tags:
if not item_tags or library_tag not in item_tags:
add_tags.append(library_tag)
# 添加标签
if add_tags:
tags = [{"Name": str(add_tag)} for add_tag in add_tags]
tags = {"Tags": tags}
add_flag = self.__add_tag(library_item.item_id, tags)
logger.info(f"{library.name} 添加标签成功:{library_item.title} {tags} {add_flag}")
# 特殊媒体名标签
if self._media_tags and len(self._media_tags.keys()) > 0:
for media_name, media_tags in self._media_tags.items():
match_medias = []
# 根据Series/Movie搜索媒体
for media_type in self._media_type.get(media_name):
match_medias += self.__get_medias_by_name(media_name, media_type)
# 遍历媒体 补充缺失tag
for media in match_medias:
if not media:
# 遍历媒体库,获取媒体库媒体
for library in librarys:
# 获取媒体库标签
library_tags = self._tags.get(library.name)
if not library_tags:
continue
# 获取item的tag
item_tags = self.__get_item_tags(media.get("Id")) or []
# 获取媒体库媒体
library_items = emby_server.instance.get_items(library.id)
if not library_items:
continue
# 获取缺少的tag
add_tags = []
for media_tag in media_tags:
if not item_tags or media_tag not in item_tags:
add_tags.append(media_tag)
for library_item in library_items:
if not library_item:
continue
# 获取itemtag
item_tags = self.__get_item_tags(library_item.item_id) or []
# 添加标签
if add_tags:
tags = [{"Name": str(add_tag)} for add_tag in add_tags]
tags = {"Tags": tags}
add_flag = self.__add_tag(media.get("Id"), tags)
logger.info(f"特殊媒体添加标签成功:{media.get('Name')} {tags} {add_flag}")
# 获取缺少的tag
add_tags = []
for library_tag in library_tags:
if not item_tags or library_tag not in item_tags:
add_tags.append(library_tag)
logger.info("Emby媒体标签任务完成")
# 添加标签
if add_tags:
tags = [{"Name": str(add_tag)} for add_tag in add_tags]
tags = {"Tags": tags}
add_flag = self.__add_tag(library_item.item_id, tags)
logger.info(f"{library.name} 添加标签成功:{library_item.title} {tags} {add_flag}")
# 特殊媒体名标签
if self._media_tags and len(self._media_tags.keys()) > 0:
for media_name, media_tags in self._media_tags.items():
match_medias = []
# 根据Series/Movie搜索媒体
for media_type in self._media_type.get(media_name):
match_medias += self.__get_medias_by_name(media_name, media_type)
# 遍历媒体 补充缺失tag
for media in match_medias:
if not media:
continue
# 获取item的tag
item_tags = self.__get_item_tags(media.get("Id")) or []
# 获取缺少的tag
add_tags = []
for media_tag in media_tags:
if not item_tags or media_tag not in item_tags:
add_tags.append(media_tag)
# 添加标签
if add_tags:
tags = [{"Name": str(add_tag)} for add_tag in add_tags]
tags = {"Tags": tags}
add_flag = self.__add_tag(media.get("Id"), tags)
logger.info(f"特殊媒体添加标签成功:{media.get('Name')} {tags} {add_flag}")
logger.info(f"{emby_name} 媒体标签任务完成")
@eventmanager.register(EventType.PluginAction)
def remote_sync(self, event: Event):
@@ -423,6 +425,32 @@ class EmbyMetaTag(_PluginBase):
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -452,6 +480,7 @@ class EmbyMetaTag(_PluginBase):
"cron": "5 1 * * *",
"tag_confs": "",
"name_tag_confs": "",
"mediaservers": [],
}
def get_page(self) -> List[dict]:

View File

@@ -34,7 +34,7 @@ class EmbyReporter(_PluginBase):
# 插件图标
plugin_icon = "Pydiocells_A.png"
# 插件版本
plugin_version = "1.8.1"
plugin_version = "1.9"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -58,6 +58,8 @@ class EmbyReporter(_PluginBase):
_emby_host = None
_emby_api_key = None
show_time = True
_mediaservers = None
_scheduler: Optional[BackgroundScheduler] = None
mediaserver_helper = None
PLAYBACK_REPORTING_TYPE_MOVIE = "ItemName"
@@ -69,12 +71,6 @@ class EmbyReporter(_PluginBase):
# 停止现有任务
self.stop_service()
self.mediaserver_helper = MediaServerHelper()
emby_server = self.mediaserver_helper.get_service(name="Emby")
if not emby_server:
logger.error("未配置Emby媒体服务器")
return
self.host = emby_server.config.get("host")
self.api_key = emby_server.config.get("apikey")
if config:
self._enabled = config.get("enabled")
@@ -88,10 +84,7 @@ class EmbyReporter(_PluginBase):
self.show_time = config.get("show_time")
self._emby_host = config.get("emby_host")
self._emby_api_key = config.get("emby_api_key")
if self._emby_host and self._emby_api_key:
self.host = f"http://{self._emby_host}" if not str(self._emby_host).startswith(
"http") else self._emby_host
self.api_key = self._emby_api_key
self._mediaservers = config.get("mediaservers") or []
if self._enabled or self._onlyonce:
# 定时服务
@@ -137,47 +130,62 @@ class EmbyReporter(_PluginBase):
if not self._type:
return
# 获取当前时间并格式化
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
# 获取数据
success, movies = self.get_report(types=self.PLAYBACK_REPORTING_TYPE_MOVIE, days=int(self._days),
limit=int(self._cnt))
if not success:
exit(movies)
logger.info(f"获取到电影 {movies}")
success, tvshows = self.get_report(types=self.PLAYBACK_REPORTING_TYPE_TVSHOWS, days=int(self._days),
limit=int(self._cnt))
if not success:
exit(tvshows)
logger.info(f"获取到电视剧 {tvshows}")
# 绘制海报
report_path = self.draw(res_path=self._res_dir,
movies=movies,
tvshows=tvshows,
show_time=self.show_time)
if not report_path:
logger.error("生成海报失败")
emby_servers = self.mediaserver_helper.get_services(name_filters=self._mediaservers, type_filter="emby")
if not emby_servers:
logger.error("未配置Emby媒体服务器")
return
# 示例调用
self.__split_image_by_height(report_path, "/public/report", [250, 330, 335])
for emby_name, emby_server in emby_servers.items():
logger.info(f"开始处理媒体服务器 {emby_name}")
self.host = emby_server.config.get("host")
self.api_key = emby_server.config.get("apikey")
if not self.host.endswith("/"):
self.host += "/"
if not self.host.startswith("http"):
self.host = "http://" + self.host
# 分块推送
for i in range(2, 4):
report_path_part = f"/public/report_part_{i}.jpg"
report_url = self._mp_host + report_path_part.replace("/public", "") + f"?_timestamp={current_time}"
mtype = NotificationType.MediaServer
if self._type:
mtype = NotificationType.__getitem__(str(self._type)) or NotificationType.MediaServer
# 获取当前时间并格式化
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
self.post_message(
title=f'Movies 近{self._days}日观影排行' if i == 2 else f'TV Shows 近{self._days}日观影排行',
mtype=mtype,
image=report_url)
logger.info(f"Emby观影记录推送成功 {report_url}")
# 获取数据
success, movies = self.get_report(types=self.PLAYBACK_REPORTING_TYPE_MOVIE, days=int(self._days),
limit=int(self._cnt))
if not success:
exit(movies)
logger.info(f"获取到电影 {movies}")
success, tvshows = self.get_report(types=self.PLAYBACK_REPORTING_TYPE_TVSHOWS, days=int(self._days),
limit=int(self._cnt))
if not success:
exit(tvshows)
logger.info(f"获取到电视剧 {tvshows}")
# 绘制海报
report_path = self.draw(res_path=self._res_dir,
movies=movies,
tvshows=tvshows,
show_time=self.show_time,
emby_name=emby_name)
if not report_path:
logger.error("生成海报失败")
break
# 示例调用
self.__split_image_by_height(report_path, f"/public/report_{emby_name}", [250, 330, 335])
# 分块推送
for i in range(2, 4):
report_path_part = f"/public/report_{emby_name}_part_{i}.jpg"
report_url = self._mp_host + report_path_part.replace("/public", "") + f"?_timestamp={current_time}"
mtype = NotificationType.MediaServer
if self._type:
mtype = NotificationType.__getitem__(str(self._type)) or NotificationType.MediaServer
self.post_message(
title=f'Movies 近{self._days}日观影排行' if i == 2 else f'TV Shows 近{self._days}日观影排行',
mtype=mtype,
image=report_url)
logger.info(f"{emby_name} 观影记录推送成功 {report_url}")
@staticmethod
def __split_image_by_height(image_path, output_path_prefix, heights):
@@ -228,7 +236,8 @@ class EmbyReporter(_PluginBase):
"show_time": self.show_time,
"emby_host": self._emby_host,
"emby_api_key": self._emby_api_key,
"res_dir": self._res_dir
"res_dir": self._res_dir,
"mediaservers": self._mediaservers,
})
def get_state(self) -> bool:
@@ -475,6 +484,32 @@ class EmbyReporter(_PluginBase):
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12
},
'content': [
{
'component': 'VSelect',
'props': {
'multiple': True,
'chips': True,
'clearable': True,
'model': 'mediaservers',
'label': '媒体服务器',
'items': [{"title": config.name, "value": config.name}
for config in self.mediaserver_helper.get_configs().values() if
config.type == "emby"]
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -530,7 +565,8 @@ class EmbyReporter(_PluginBase):
"emby_api_key": "",
"mp_host": "",
"show_time": True,
"type": ""
"type": "",
"mediaservers": [],
}
def get_page(self) -> List[dict]:
@@ -549,7 +585,7 @@ class EmbyReporter(_PluginBase):
except Exception as e:
logger.error("退出插件失败:%s" % str(e))
def draw(self, res_path, movies, tvshows, show_time=True):
def draw(self, res_path, movies, tvshows, show_time=True, emby_name=None):
# 默认路径 默认图
if not res_path:
res_path = os.path.join(Path(__file__).parent, "res")
@@ -668,7 +704,7 @@ class EmbyReporter(_PluginBase):
continue
if index >= 0:
save_path = "/public/report.jpg"
save_path = f"/public/report_{emby_name}.jpg"
if Path(save_path).exists():
Path.unlink(Path(save_path))
bg.save(save_path)