diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index 55c384bf..dc5b11cf 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -7,6 +7,7 @@ from sqlalchemy.orm import Session from app import schemas 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.core.security import verify_token, verify_uri_token from app.db import get_db @@ -14,6 +15,7 @@ from app.db.models.subscribe import Subscribe from app.db.models.subscribehistory import SubscribeHistory from app.db.models.user import User from app.db.userauth import get_current_active_user +from app.helper.subscribe import SubscribeHelper from app.scheduler import Scheduler from app.schemas.types import MediaType @@ -334,6 +336,38 @@ def delete_subscribe( return schemas.Response(success=True) +@router.get("/popular", summary="热门订阅(基于用户共享数据)", response_model=List[schemas.MediaInfo]) +def popular_subscribes( + stype: str, + page: int = 1, + count: int = 30, + _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 查询热门订阅 + """ + subscribes = SubscribeHelper().get_statistic(stype=stype, page=page, count=count) + if subscribes: + ret_medias = [] + for sub in subscribes: + media = MediaInfo() + media.type = MediaType(sub.get("type")) + media.title = sub.get("name") + media.year = sub.get("year") + media.tmdb_id = sub.get("tmdbid") + media.douban_id = sub.get("doubanid") + media.bangumi_id = sub.get("bangumiid") + media.tvdb_id = sub.get("tvdbid") + media.imdb_id = sub.get("imdbid") + media.season = sub.get("season") + media.overview = sub.get("description") + media.vote_average = sub.get("vote") + media.poster_path = sub.get("poster") + media.backdrop_path = sub.get("backdrop") + ret_medias.append(media) + return [media.to_dict() for media in ret_medias] + return [] + + @router.get("/{subscribe_id}", summary="订阅详情", response_model=schemas.Subscribe) def read_subscribe( subscribe_id: int, diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 0572c255..db19e9e0 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -19,6 +19,7 @@ from app.db.subscribe_oper import SubscribeOper from app.db.subscribehistory_oper import SubscribeHistoryOper from app.db.systemconfig_oper import SystemConfigOper from app.helper.message import MessageHelper +from app.helper.subscribe import SubscribeHelper from app.helper.torrent import TorrentHelper from app.log import logger from app.schemas import NotExistMediaInfo, Notification @@ -36,6 +37,7 @@ class SubscribeChain(ChainBase): self.searchchain = SearchChain() self.subscribeoper = SubscribeOper() self.subscribehistoryoper = SubscribeHistoryOper() + self.subscribehelper = SubscribeHelper() self.torrentschain = TorrentsChain() self.mediachain = MediaChain() self.message = MessageHelper() @@ -122,6 +124,9 @@ class SubscribeChain(ChainBase): kwargs.update({ 'lack_episode': kwargs.get('total_episode') }) + else: + # 避免season为0的问题 + season = None # 更新媒体图片 self.obtain_images(mediainfo=mediainfo) # 合并信息 @@ -153,6 +158,7 @@ class SubscribeChain(ChainBase): text=f"{err_msg}", image=mediainfo.get_message_image(), userid=userid)) + return None, err_msg elif message: logger.info(f'{mediainfo.title_year} {metainfo.season} 添加订阅成功') if username: @@ -164,12 +170,28 @@ class SubscribeChain(ChainBase): title=f"{mediainfo.title_year} {metainfo.season} 已添加订阅", text=text, image=mediainfo.get_message_image())) - # 发送事件 - EventManager().send_event(EventType.SubscribeAdded, { - "subscribe_id": sid, - "username": username, - "mediainfo": mediainfo.to_dict(), - }) + # 发送事件 + EventManager().send_event(EventType.SubscribeAdded, { + "subscribe_id": sid, + "username": username, + "mediainfo": mediainfo.to_dict(), + }) + # 统计订阅 + self.subscribehelper.sub_reg_async({ + "name": title, + "year": year, + "type": metainfo.type.value, + "tmdbid": mediainfo.tmdb_id, + "imdbid": mediainfo.imdb_id, + "tvdbid": mediainfo.tvdb_id, + "doubanid": mediainfo.douban_id, + "bangumiid": mediainfo.bangumi_id, + "season": metainfo.begin_season, + "poster": mediainfo.get_poster_image(), + "backdrop": mediainfo.get_backdrop_image(), + "vote": mediainfo.vote_average, + "description": mediainfo.overview + }) # 返回结果 return sid, "" diff --git a/app/helper/subscribe.py b/app/helper/subscribe.py new file mode 100644 index 00000000..df50ff8b --- /dev/null +++ b/app/helper/subscribe.py @@ -0,0 +1,91 @@ +from threading import Thread +from typing import List + +from cachetools import TTLCache, cached + +from app.db.subscribe_oper import SubscribeOper +from app.db.systemconfig_oper import SystemConfigOper +from app.schemas.types import SystemConfigKey +from app.utils.http import RequestUtils +from app.utils.singleton import Singleton + + +class SubscribeHelper(metaclass=Singleton): + """ + 订阅数据统计 + """ + + _sub_reg = "https://movie-pilot.org/subscribe/add" + + _sub_report = "https://movie-pilot.org/subscribe/report" + + _sub_statistic = "https://movie-pilot.org/subscribe/statistic" + + def __init__(self): + self.systemconfig = SystemConfigOper() + if not self.systemconfig.get(SystemConfigKey.SubscribeReport): + if self.sub_report(): + self.systemconfig.set(SystemConfigKey.SubscribeReport, "1") + + @cached(cache=TTLCache(maxsize=10, ttl=1800)) + def get_statistic(self, stype: str, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取订阅统计数据 + """ + res = RequestUtils(timeout=15).get_res(self._sub_statistic, params={ + "stype": stype, + "page": page, + "count": count + }) + if res and res.status_code == 200: + return res.json() + return [] + + def sub_reg(self, sub: dict) -> bool: + """ + 新增订阅统计 + """ + res = RequestUtils(timeout=5, headers={ + "Content-Type": "application/json" + }).post_res(self._sub_reg, json=sub) + if res and res.status_code == 200: + return True + return False + + def sub_reg_async(self, sub: dict) -> bool: + """ + 异步新增订阅统计 + """ + # 开新线程处理 + Thread(target=self.sub_reg, args=(sub,)).start() + return True + + def sub_report(self) -> bool: + """ + 上报存量订阅统计 + """ + subscribes = SubscribeOper().list() + if not subscribes: + return True + res = RequestUtils(content_type="application/json", + timeout=10).post(self._sub_report, + json={ + "subscribes": [ + { + "name": sub.name, + "year": sub.year, + "type": sub.type, + "tmdbid": sub.tmdbid, + "imdbid": sub.imdbid, + "tvdbid": sub.tvdbid, + "doubanid": sub.doubanid, + "bangumiid": sub.bangumiid, + "season": sub.season, + "poster": sub.poster, + "backdrop": sub.backdrop, + "vote": sub.vote, + "description": sub.description + } for sub in subscribes + ] + }) + return True if res else False diff --git a/app/schemas/types.py b/app/schemas/types.py index 242bd087..5b50ceca 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -82,6 +82,8 @@ class SystemConfigKey(Enum): TransferExcludeWords = "TransferExcludeWords" # 插件安装统计 PluginInstallReport = "PluginInstallReport" + # 订阅统计 + SubscribeReport = "SubscribeReport" # 处理进度Key字典