From 1d318ac983e003af6304a93a7b7a545a9d4970fe Mon Sep 17 00:00:00 2001 From: xuzhi Date: Sun, 9 Jun 2024 15:36:33 +0000 Subject: [PATCH] Initialize plugin ZvideoHelper --- icons/zvideo.png | Bin 0 -> 23744 bytes package.json | 12 + plugins/zvideohelper/DoubanHelper.py | 154 ++++++++ plugins/zvideohelper/__init__.py | 543 +++++++++++++++++++++++++++ 4 files changed, 709 insertions(+) create mode 100644 icons/zvideo.png create mode 100644 plugins/zvideohelper/DoubanHelper.py create mode 100644 plugins/zvideohelper/__init__.py diff --git a/icons/zvideo.png b/icons/zvideo.png new file mode 100644 index 0000000000000000000000000000000000000000..bc334b05115e7f9426dee532572bbf7f85389195 GIT binary patch literal 23744 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 DgOgNZ literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 7c8ad5c..ce2796d 100644 --- a/package.json +++ b/package.json @@ -789,5 +789,17 @@ "icon": "Calibre_B.png", "author": "jxxghp", "level": 1 + }, + "ZvideoHelper": { + "name": "极影视助手", + "description": "极影视功能扩展", + "labels": "媒体库", + "version": "1.0", + "icon": "zvideo.png", + "author": "DzAvril", + "level": 1, + "history": { + "v1.0": "同步极影视在看/已看状态到豆瓣" + } } } diff --git a/plugins/zvideohelper/DoubanHelper.py b/plugins/zvideohelper/DoubanHelper.py new file mode 100644 index 0000000..ca3e259 --- /dev/null +++ b/plugins/zvideohelper/DoubanHelper.py @@ -0,0 +1,154 @@ +import re +from typing import List, Tuple +from urllib.parse import unquote + +import requests +from bs4 import BeautifulSoup +from http.cookies import SimpleCookie +from app.core.config import settings +from app.core.meta import MetaBase +from app.helper.cookiecloud import CookieCloudHelper +from app.log import logger +from app.utils.http import RequestUtils + + +class DoubanHelper: + + def __init__(self, user_cookie: str = None): + if not user_cookie: + self.cookiecloud = CookieCloudHelper() + cookie_dict, msg = self.cookiecloud.download() + if cookie_dict is None: + logger.error(f"获取cookiecloud数据错误 {msg}") + self.cookies = cookie_dict.get("douban.com") + else: + self.cookies = user_cookie + self.cookies = {k: v.value for k, v in SimpleCookie(self.cookies).items()} + user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57' + self.headers = { + 'User-Agent': user_agent, + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Encoding': 'gzip, deflate, sdch', + 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4,en-GB;q=0.2,zh-TW;q=0.2', + 'Connection': 'keep-alive', + 'DNT': '1', + 'HOST': 'www.douban.com' + } + + if self.cookies.get('__utmz'): + self.cookies.pop("__utmz") + + # 移除用户传进来的comment-key + if self.cookies.get('ck'): + self.cookies.pop("ck") + + # 获取最新的ck + self.set_ck() + + self.ck = self.cookies.get('ck') + logger.debug(f"ck:{self.ck} cookie:{self.cookies}") + + if not self.cookies: + logger.error(f"cookie获取为空,请检查插件配置或cookie cloud") + if not self.ck: + logger.error(f"请求ck失败,请检查传入的cookie登录状态") + + def set_ck(self): + self.headers["Cookie"] = ";".join([f"{key}={value}" for key, value in self.cookies.items()]) + response = requests.get("https://www.douban.com/", headers=self.headers) + ck_str = response.headers.get('Set-Cookie', '') + logger.debug(ck_str) + if not ck_str: + logger.error('获取ck失败,检查豆瓣登录状态') + self.cookies['ck'] = '' + return + cookie_parts = ck_str.split(";") + ck = cookie_parts[0].split("=")[1].strip() + logger.debug(ck) + self.cookies['ck'] = ck + + def get_subject_id(self, title: str = None, meta: MetaBase = None) -> Tuple | None: + if not title: + title = meta.title + year = meta.year + url = f"https://www.douban.com/search?cat=1002&q={title}" + response = RequestUtils(headers=self.headers).get_res(url) + if not response.status_code == 200: + logger.error(f"搜索 {title} 失败 状态码:{response.status_code}") + return None + # self.headers["Cookie"] = response.cookies + soup = BeautifulSoup(response.text.encode('utf-8'), 'lxml') + title_divs = soup.find_all("div", class_="title") + subject_items: List = [] + # 遍历所有找到的div标签 + for div in title_divs: + item = {} + + # title + a_tag = div.find_all("a")[0] + item["title"] = a_tag.string + item["title"] = item["title"].strip() + + # year 原名:피라미드 게임 / 朴昭妍 / 金知妍 / 2024 + span_tag = div.find_all(class_="subject-cast")[0] + year: str = span_tag.string[-4:] + if year.isdigit(): + item["year"] = year + + # subject_id + link = unquote(a_tag["href"]) + if link.count("subject/"): + pattern = r"subject/(\d+)/" + match = re.search(pattern, link) + if match: + subject_id = match.group(1) + item["subject_id"] = subject_id + subject_items.append(item) + + if not subject_items: + logger.error(f"找不到 {title} 相关条目 搜索结果html:{response.text.encode('utf-8')}") + for subject_item in subject_items: + logger.debug(f"{subject_item['title']} {subject_item['subject_id']}") + return subject_item["title"], subject_item["subject_id"] + return None, None + + def set_watching_status(self, subject_id: str, status: str = "do", private: bool = True) -> bool: + self.headers["Referer"] = f"https://movie.douban.com/subject/{subject_id}/" + self.headers["Origin"] = "https://movie.douban.com" + self.headers["Host"] = "movie.douban.com" + self.headers["Cookie"] = ";".join([f"{key}={value}" for key, value in self.cookies.items()]) + data_json = { + "ck": self.ck, + "interest": "do", + "rating": "", + "foldcollect": "U", + "tags": "", + "comment": "" + } + if private: + data_json["private"] = "on" + data_json["interest"] = status + response = requests.post( + url=f"https://movie.douban.com/j/subject/{subject_id}/interest", + headers=self.headers, + data=data_json) + if not response: + return False + if response.status_code == 200: + # 正常情况 {"r":0} + ret = response.json().get("r") + r = False if (isinstance(ret, bool) and ret is False) else True + if r: + return True + # 未开播 {"r": false} + else: + logger.error(f"douban_id: {subject_id} 未开播") + return False + logger.error(response.text) + return False + + +if __name__ == "__main__": + doubanHelper = DoubanHelper() + subject_title, subject_id = doubanHelper.get_subject_id("秘密森林2") + doubanHelper.set_watching_status(subject_id=subject_id, status="do", private=True) diff --git a/plugins/zvideohelper/__init__.py b/plugins/zvideohelper/__init__.py new file mode 100644 index 0000000..67a1315 --- /dev/null +++ b/plugins/zvideohelper/__init__.py @@ -0,0 +1,543 @@ +from datetime import datetime, timedelta +import sqlite3 +import json +from app.plugins.zvideohelper.DoubanHelper import * +from enum import Enum + +import pytz +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger +from app.schemas.types import EventType, NotificationType +from app.core.event import eventmanager, Event +from pathlib import Path + +from app.core.config import settings +from app.plugins import _PluginBase +from typing import Any, List, Dict, Tuple, Optional +from app.log import logger + + +# 豆瓣状态 +class DoubanStatus(Enum): + WATCHING = "do" + DONE = "collect" + +class ZvideoHelper(_PluginBase): + # 插件名称 + plugin_name = "极影视助手" + # 插件描述 + plugin_desc = "极影视功能扩展" + # 插件图标 + plugin_icon = "zvideo.png" + # 插件版本 + plugin_version = "1.0" + # 插件作者 + plugin_author = "DzAvril" + # 作者主页 + author_url = "https://github.com/DzAvril" + # 插件配置项ID前缀 + plugin_config_prefix = "zvideohelper" + # 加载顺序 + plugin_order = 1 + # 可使用的用户级别 + auth_level = 1 + + # 私有属性 + _enabled = False + _cron = None + _notify = False + _onlyonce = False + _sync_douban_status = False + _clean_cache = False + _douban_helper = None + _cached_data: dict = {} + _db_path = "" + _cookie = "" + # 定时器 + _scheduler: Optional[BackgroundScheduler] = None + + def init_plugin(self, config: dict = None): + # 停止现有任务 + self.stop_service() + + if config: + self._enabled = config.get("enabled") + self._cron = config.get("cron") + self._notify = config.get("notify") + self._onlyonce = config.get("onlyonce") + self._db_path = config.get("db_path") + self._cookie = config.get("cookie") + self._sync_douban_status = config.get("sync_douban_status") + self._clean_cache = config.get("clean_cache") + self._douban_helper = DoubanHelper(user_cookie=self._cookie) + + # 获取历史数据 + self._cached_data = ( + self.get_data("zvideohelper") + if self.get_data("zvideohelper") != None + else dict() + ) + # 加载模块 + if self._onlyonce: + if self._clean_cache: + self._cached_data = {} + self.save_data("zvideohelper", self._cached_data) + self._clean_cache = False + # 检查数据库路径是否存在 + path = Path(self._db_path) + if not path.exists(): + logger.error(f"极影视数据库路径不存在: {self._db_path}") + self._onlyonce = False + self._clean_cache = False + self._update_config() + if self._notify: + self.post_message( + mtype=NotificationType.SiteMessage, + title=f"【极影视助手】", + text=f"极影视数据库路径不存在: {self._db_path}", + ) + return + + self._scheduler = BackgroundScheduler(timezone=settings.TZ) + logger.info(f"极影视助手服务启动,立即运行一次") + self._scheduler.add_job( + func=self.do_job, + trigger="date", + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + + timedelta(seconds=3), + name="极影视助手", + ) + # 关闭一次性开关 + self._onlyonce = False + self._update_config() + + # 启动任务 + if self._scheduler.get_jobs(): + self._scheduler.print_jobs() + self._scheduler.start() + + def get_state(self) -> bool: + return self._enabled + + def _update_config(self): + self.update_config( + { + "onlyonce": False, + "cron": self._cron, + "enabled": self._enabled, + "notify": self._notify, + "db_path": self._db_path, + "cookie": self._cookie, + "sync_douban_status": self._sync_douban_status, + "clean_cache": self._clean_cache, + } + ) + + @staticmethod + def get_command() -> List[Dict[str, Any]]: + """ + 定义远程控制命令 + :return: 命令关键字、事件、描述、附带数据 + """ + return [ + { + "cmd": "/sync_zvideo_to_douban", + "event": EventType.PluginAction, + "desc": "同步极影视观影状态", + "category": "", + "data": {"action": "sync_zvideo_to_douban"}, + } + ] + + + @eventmanager.register(EventType.PluginAction) + def handle_command(self, event: Event): + if event: + event_data = event.event_data + if not event_data or event_data.get("action") != "sync_zvideo_to_douban": + return + logger.info("收到命令,开始同步极影视观影状态 ...") + self.post_message( + channel=event.event_data.get("channel"), + title="开始同步极影视观影状态 ...", + userid=event.event_data.get("user"), + ) + self.do_job() + if event: + self.post_message( + channel=event.event_data.get("channel"), + title="同步极影视观影状态完成!", + userid=event.event_data.get("user"), + ) + + def get_api(self) -> List[Dict[str, Any]]: + 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: + return [ + { + "id": "ZvideoHelper", + "name": "极影视助手", + "trigger": CronTrigger.from_crontab(self._cron), + "func": self.do_job, + "kwargs": {}, + } + ] + + def do_job(self): + if self._sync_douban_status: + self.set_douban_watching() + self.set_douban_done() + # 缓存数据 + self.save_data("zvideohelper", self._cached_data) + + def set_douban_watching(self): + watching_douban_id = [] + try: + # 连接到SQLite数据库 + conn = sqlite3.connect(self._db_path) + + # 创建一个游标对象 + cursor = conn.cursor() + + # 查询表格zvideo_playlist中的collection_id列 + cursor.execute("SELECT collection_id FROM zvideo_playlist") + collection_ids = cursor.fetchall() + + # 去重collection_id + collection_ids = set([collection_id[0] for collection_id in collection_ids]) + + # 创建一个列表来保存符合条件的meta_info列的JSON对象 + meta_info_list = [] + + # 查询zvideo_collection表中对应的行并筛选type == 200的记录,只有电视剧才有在看状态 + for collection_id in collection_ids: + cursor.execute( + "SELECT meta_info FROM zvideo_collection WHERE collection_id = ? AND type = 200", + (collection_id,), + ) + rows = cursor.fetchall() + + # 将meta_info列的信息转换为JSON对象并保存到列表中 + for row in rows: + try: + meta_info_json = json.loads(row[0]) + meta_info_list.append(meta_info_json) + except json.JSONDecodeError as e: + logger.error( + f"An error occurred while decoding JSON for collection_id {collection_id}: {e}" + ) + + for meta_info in meta_info_list: + douban_id = meta_info["relation"]["douban"]["douban_id"] + title = meta_info["title"] + if self._cached_data.get(title) != None: + logger.info(f"已处理过: {title},跳过...") + continue + if douban_id == 0: + douban_id = self.get_douban_id_by_name(title) + if douban_id != None: + watching_douban_id.append((title, douban_id)) + else: + logger.error(f"未找到豆瓣ID: {title}") + + except sqlite3.Error as e: + logger.error(f"An error occurred: {e}") + + finally: + # 确保游标和连接在使用完后关闭 + if cursor: + cursor.close() + if conn: + conn.close() + message = "" + for item in watching_douban_id: + status = DoubanStatus.WATCHING.value + ret = self._douban_helper.set_watching_status( + subject_id=item[1], status=status, private=True + ) + if ret: + self._cached_data[item[0]] = status + logger.info(f"title: {item[0]}, douban_id: {item[1]},已标记为在看") + message += f"{item[0]},已标记为在看\n" + else: + logger.error( + f"title: {item[0]}, douban_id: {item[1]},标记在看失败" + ) + message += f"{item[0]},***标记在看失败***\n" + if self._notify: + self.post_message( + mtype=NotificationType.SiteMessage, + title="【极影视助手】", + text=message, + ) + + def set_douban_done(self): + watching_douban_id = [] + try: + # 连接到SQLite数据库 + conn = sqlite3.connect(self._db_path) + + # 创建一个游标对象 + cursor = conn.cursor() + + # 通过表格`zvideo_collecion_tags`的`tag_name==是否看过`找到对应的`collcetion_id`,在到`zvideo_collection`中查找将其标记为已看 + cursor.execute( + "SELECT collection_id FROM zvideo_collection_tags WHERE tag_name='是否看过'" + ) + collection_ids = cursor.fetchall() + + # 去重collection_id + collection_ids = set([collection_id[0] for collection_id in collection_ids]) + + # 创建一个列表来保存符合条件的meta_info列的JSON对象 + meta_info_list = [] + + for collection_id in collection_ids: + cursor.execute( + "SELECT meta_info FROM zvideo_collection WHERE collection_id = ?", + (collection_id,), + ) + rows = cursor.fetchall() + + # 将meta_info列的信息转换为JSON对象并保存到列表中 + for row in rows: + try: + meta_info_json = json.loads(row[0]) + meta_info_list.append(meta_info_json) + except json.JSONDecodeError as e: + logger.error( + f"An error occurred while decoding JSON for collection_id {collection_id}: {e}" + ) + + for meta_info in meta_info_list: + douban_id = meta_info["relation"]["douban"]["douban_id"] + title = meta_info["title"] + if self._cached_data.get(title) == DoubanStatus.DONE.value: + logger.info(f"已处理过: {title},跳过...") + continue + if douban_id == 0: + douban_id = self.get_douban_id_by_name(title) + if douban_id != None: + watching_douban_id.append((title, douban_id)) + else: + logger.error(f"未找到豆瓣ID: {title}") + + except sqlite3.Error as e: + logger.error(f"An error occurred: {e}") + + finally: + # 确保游标和连接在使用完后关闭 + if cursor: + cursor.close() + if conn: + conn.close() + message = "" + for item in watching_douban_id: + status = DoubanStatus.DONE.value + ret = self._douban_helper.set_watching_status( + subject_id=item[1], status=status, private=True + ) + if ret: + self._cached_data[item[0]] = status + logger.info(f"title: {item[0]}, douban_id: {item[1]},已标记为已看") + message += f"{item[0]},已标记为已看\n" + else: + logger.error( + f"title: {item[0]}, douban_id: {item[1]}, 标记已看失败" + ) + message += f"{item[0]},***标记已看失败***\n" + if self._notify: + self.post_message( + mtype=NotificationType.SiteMessage, + title="【极影视助手】", + text=message, + ) + + def get_douban_id_by_name(self, title): + logger.info(f"正在查询:{title}") + subject_name, subject_id = self._douban_helper.get_subject_id(title=title) + logger.info(f"查询到:subject_name: {subject_name}, subject_id: {subject_id}") + return subject_id + + def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: + 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": "VCol", + "props": {"cols": 12, "md": 4}, + "content": [ + { + "component": "VSwitch", + "props": { + "model": "sync_douban_status", + "label": "同步豆瓣在看/已看", + }, + } + ], + }, + { + "component": "VCol", + "props": {"cols": 12, "md": 4}, + "content": [ + { + "component": "VSwitch", + "props": { + "model": "clean_cache", + "label": "清理缓存数据", + }, + } + ], + }, + { + "component": "VCol", + "props": {"cols": 12, "md": 4}, + "content": [ + { + "component": "VTextField", + "props": {"model": "cron", "label": "执行周期"}, + } + ], + }, + ], + }, + { + "component": "VRow", + "content": [ + { + "component": "VCol", + "props": {"cols": 12}, + "content": [ + { + "component": "VTextarea", + "props": { + "model": "cookie", + "label": "豆瓣cookie", + "rows": 1, + "placeholder": "留空则从cookiecloud获取", + }, + } + ], + } + ], + }, + { + "component": "VRow", + "content": [ + { + "component": "VCol", + "props": {"cols": 12}, + "content": [ + { + "component": "VTextarea", + "props": { + "model": "db_path", + "label": "极影视数据库路径", + "rows": 1, + "placeholder": "极影视路径为/zspace/zsrp/sqlite/zvideo/zvideo.db,需先映射路径", + }, + } + ], + } + ], + }, + { + "component": "VRow", + "content": [ + { + "component": "VCol", + "props": { + "cols": 12, + }, + "content": [ + { + "component": "VAlert", + "props": { + "type": "info", + "variant": "tonal", + "text": "本插件基于极影视数据库扩展功能,需开启ssh后通过portainer、1panel等工具映射极影视数据库路径", + }, + } + ], + } + ], + }, + ], + } + ], { + "enabled": False, + "notify": False, + "onlyonce": False, + "cron": "0 0 * * *", + } + + def get_page(self) -> List[dict]: + pass + + def stop_service(self): + """ + 退出插件 + """ + try: + if self._scheduler: + self._scheduler.remove_all_jobs() + if self._scheduler.running: + self._scheduler.shutdown() + self._scheduler = None + except Exception as e: + logger.error("退出插件失败:%s" % str(e))