From b4641b659121086820421beb1f9cc322e3e69f8a Mon Sep 17 00:00:00 2001 From: projectdx Date: Tue, 31 Jan 2023 19:09:25 +0900 Subject: [PATCH] =?UTF-8?q?2022.01.31=20anilife=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=94=BD=EC=8A=A4=20(.03.=20=EA=B8=B0=ED=83=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/__init__.py | 1 + lib/plugin/ffmpeg_queue.py | 293 ++++++++++++++++++ logic_anilife.py | 28 +- .../anime_downloader_anilife_setting.html | 2 +- 4 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 lib/plugin/__init__.py create mode 100644 lib/plugin/ffmpeg_queue.py diff --git a/lib/plugin/__init__.py b/lib/plugin/__init__.py new file mode 100644 index 0000000..06e3bf6 --- /dev/null +++ b/lib/plugin/__init__.py @@ -0,0 +1 @@ +from .ffmpeg_queue import FfmpegQueueEntity, FfmpegQueue diff --git a/lib/plugin/ffmpeg_queue.py b/lib/plugin/ffmpeg_queue.py new file mode 100644 index 0000000..b8abe38 --- /dev/null +++ b/lib/plugin/ffmpeg_queue.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- +######################################################### +# python +import os, sys, traceback +import threading, time +from datetime import datetime +import abc +from framework import py_queue + + +# third-party +# sjva 공용 +######################################################### + + +class FfmpegQueueEntity(abc.ABCMeta("ABC", (object,), {"__slots__": ()})): + def __init__(self, P, module_logic, info): + self.P = P + self.module_logic = module_logic + self.entity_id = -1 # FfmpegQueueEntity.static_index + self.info = info + self.url = None + self.ffmpeg_status = -1 + self.ffmpeg_status_kor = "대기중" + self.ffmpeg_percent = 0 + self.ffmpeg_arg = None + self.cancel = False + self.created_time = datetime.now().strftime("%m-%d %H:%M:%S") + self.savepath = None + self.filename = None + self.filepath = None + self.quality = None + self.headers = None + # FfmpegQueueEntity.static_index += 1 + # FfmpegQueueEntity.entity_list.append(self) + + def get_video_url(self): + return self.url + + def get_video_filepath(self): + return self.filepath + + @abc.abstractmethod + def refresh_status(self): + pass + + @abc.abstractmethod + def info_dict(self, tmp): + pass + + def download_completed(self): + pass + + def as_dict(self): + tmp = {} + tmp["entity_id"] = self.entity_id + tmp["url"] = self.url + tmp["ffmpeg_status"] = self.ffmpeg_status + tmp["ffmpeg_status_kor"] = self.ffmpeg_status_kor + tmp["ffmpeg_percent"] = self.ffmpeg_percent + tmp["ffmpeg_arg"] = self.ffmpeg_arg + tmp["cancel"] = self.cancel + tmp["created_time"] = self.created_time # .strftime('%m-%d %H:%M:%S') + tmp["savepath"] = self.savepath + tmp["filename"] = self.filename + tmp["filepath"] = self.filepath + tmp["quality"] = self.quality + # tmp['current_speed'] = self.ffmpeg_arg['current_speed'] if self.ffmpeg_arg is not None else '' + tmp = self.info_dict(tmp) + return tmp + + +class FfmpegQueue(object): + def __init__(self, P, max_ffmpeg_count): + self.P = P + self.static_index = 1 + self.entity_list = [] + self.current_ffmpeg_count = 0 + self.download_queue = None + self.download_thread = None + self.max_ffmpeg_count = max_ffmpeg_count + if self.max_ffmpeg_count is None or self.max_ffmpeg_count == "": + self.max_ffmpeg_count = 1 + + def queue_start(self): + try: + if self.download_queue is None: + self.download_queue = py_queue.Queue() + if self.download_thread is None: + self.download_thread = threading.Thread( + target=self.download_thread_function, args=() + ) + self.download_thread.daemon = True + self.download_thread.start() + except Exception as exception: + self.P.logger.error("Exception:%s", exception) + self.P.logger.error(traceback.format_exc()) + + def download_thread_function(self): + while True: + try: + while True: + try: + if self.current_ffmpeg_count < self.max_ffmpeg_count: + break + time.sleep(5) + except Exception as exception: + self.P.logger.error("Exception:%s", exception) + self.P.logger.error(traceback.format_exc()) + self.P.logger.error( + "current_ffmpeg_count : %s", self.current_ffmpeg_count + ) + self.P.logger.error( + "max_ffmpeg_count : %s", self.max_ffmpeg_count + ) + break + entity = self.download_queue.get() + if entity.cancel: + continue + + # from .logic_ani24 import LogicAni24 + # entity.url = LogicAni24.get_video_url(entity.info['code']) + video_url = entity.get_video_url() + if video_url is None: + entity.ffmpeg_status_kor = "URL실패" + entity.refresh_status() + # plugin.socketio_list_refresh() + continue + + import ffmpeg + + # max_pf_count = 0 + # save_path = ModelSetting.get('download_path') + # if ModelSetting.get('auto_make_folder') == 'True': + # program_path = os.path.join(save_path, entity.info['filename'].split('.')[0]) + # save_path = program_path + # try: + # if not os.path.exists(save_path): + # os.makedirs(save_path) + # except: + # logger.debug('program path make fail!!') + # 파일 존재여부 체크 + filepath = entity.get_video_filepath() + if os.path.exists(filepath): + entity.ffmpeg_status_kor = "파일 있음" + entity.ffmpeg_percent = 100 + entity.refresh_status() + # plugin.socketio_list_refresh() + continue + dirname = os.path.dirname(filepath) + if not os.path.exists(dirname): + os.makedirs(dirname) + f = ffmpeg.Ffmpeg( + video_url, + os.path.basename(filepath), + plugin_id=entity.entity_id, + listener=self.ffmpeg_listener, + call_plugin=self.P.package_name, + save_path=dirname, + headers=entity.headers, + ) + f.start() + self.current_ffmpeg_count += 1 + self.download_queue.task_done() + except Exception as exception: + self.P.logger.error("Exception:%s", exception) + self.P.logger.error(traceback.format_exc()) + + def ffmpeg_listener(self, **arg): + import ffmpeg + + entity = self.get_entity_by_entity_id(arg["plugin_id"]) + if entity is None: + return + if arg["type"] == "status_change": + if arg["status"] == ffmpeg.Status.DOWNLOADING: + pass + elif arg["status"] == ffmpeg.Status.COMPLETED: + entity.download_completed() + elif arg["status"] == ffmpeg.Status.READY: + pass + elif arg["type"] == "last": + self.current_ffmpeg_count += -1 + elif arg["type"] == "log": + pass + elif arg["type"] == "normal": + pass + + entity.ffmpeg_arg = arg + entity.ffmpeg_status = int(arg["status"]) + entity.ffmpeg_status_kor = str(arg["status"]) + entity.ffmpeg_percent = arg["data"]["percent"] + entity.ffmpeg_arg["status"] = str(arg["status"]) + # self.P.logger.debug(arg) + # import plugin + # arg['status'] = str(arg['status']) + # plugin.socketio_callback('status', arg) + entity.refresh_status() + + # FfmpegQueueEntity.static_index += 1 + # FfmpegQueueEntity.entity_list.append(self) + + def add_queue(self, entity): + try: + # entity = QueueEntity.create(info) + # if entity is not None: + # LogicQueue.download_queue.put(entity) + # return True + entity.entity_id = self.static_index + self.static_index += 1 + self.entity_list.append(entity) + self.download_queue.put(entity) + return True + except Exception as exception: + self.P.logger.error("Exception:%s", exception) + self.P.logger.error(traceback.format_exc()) + return False + + def set_max_ffmpeg_count(self, max_ffmpeg_count): + self.max_ffmpeg_count = max_ffmpeg_count + + def get_max_ffmpeg_count(self): + return self.max_ffmpeg_count + + def command(self, cmd, entity_id): + self.P.logger.debug("command :%s %s", cmd, entity_id) + ret = {} + try: + if cmd == "cancel": + self.P.logger.debug("command :%s %s", cmd, entity_id) + entity = self.get_entity_by_entity_id(entity_id) + if entity is not None: + if entity.ffmpeg_status == -1: + entity.cancel = True + entity.ffmpeg_status_kor = "취소" + # entity.refresh_status() + ret["ret"] = "refresh" + elif entity.ffmpeg_status != 5: + ret["ret"] = "notify" + ret["log"] = "다운로드중 상태가 아닙니다." + else: + idx = entity.ffmpeg_arg["data"]["idx"] + import ffmpeg + + ffmpeg.Ffmpeg.stop_by_idx(idx) + entity.refresh_status() + ret["ret"] = "refresh" + elif cmd == "reset": + if self.download_queue is not None: + with self.download_queue.mutex: + self.download_queue.queue.clear() + for _ in self.entity_list: + if _.ffmpeg_status == 5: + import ffmpeg + + idx = _.ffmpeg_arg["data"]["idx"] + ffmpeg.Ffmpeg.stop_by_idx(idx) + self.entity_list = [] + ret["ret"] = "refresh" + elif cmd == "delete_completed": + new_list = [] + for _ in self.entity_list: + if _.ffmpeg_status_kor in ["파일 있음", "취소", "사용자중지"]: + continue + if _.ffmpeg_status != 7: + new_list.append(_) + self.entity_list = new_list + ret["ret"] = "refresh" + elif cmd == "remove": + new_list = [] + for _ in self.entity_list: + if _.entity_id == entity_id: + continue + new_list.append(_) + self.entity_list = new_list + ret["ret"] = "refresh" + return ret + except Exception as exception: + self.P.logger.error("Exception:%s", exception) + self.P.logger.error(traceback.format_exc()) + + def get_entity_by_entity_id(self, entity_id): + for _ in self.entity_list: + if _.entity_id == entity_id: + return _ + return None + + def get_entity_list(self): + ret = [] + for x in self.entity_list: + tmp = x.as_dict() + ret.append(tmp) + return ret diff --git a/logic_anilife.py b/logic_anilife.py index b6247f3..91c75a2 100644 --- a/logic_anilife.py +++ b/logic_anilife.py @@ -51,10 +51,11 @@ from framework.util import Util from framework.common.util import headers from plugin import ( LogicModuleBase, - FfmpegQueueEntity, - FfmpegQueue, default_route_socketio, ) + +# 철자가 틀린 부분이 있어서 분리함 +from .lib.plugin import FfmpegQueue, FfmpegQueueEntity from tool_base import d # 패키지 @@ -1025,13 +1026,32 @@ class LogicAniLife(LogicModuleBase): ) def scheduler_function(self): - logger.debug(f"anilife scheduler_function::=========================") + logger.debug(f"anilife scheduler_function:: =========================") content_code_list = P.ModelSetting.get_list("anilife_auto_code_list", "|") - url = f'{P.ModelSetting.get("anilife_url")}/dailyani' + if "all" in content_code_list: + url = f'{P.ModelSetting.get("anilife_url")}/dailyani' ret_data = LogicAniLife.get_auto_anime_info(self, url=url) + elif len(content_code_list) > 0: + for item in content_code_list: + url = P.ModelSetting.get("anilife_url") + "/detail/id/" + item + print("scheduling url: %s", url) + # ret_data = LogicOhli24.get_auto_anime_info(self, url=url) + content_info = self.get_series_info(item) + + logger.debug(content_info) + # exit() + + for episode_info in content_info["episode"]: + add_ret = self.add(episode_info) + if add_ret.startswith("enqueue"): + self.socketio_callback("list_refresh", "") + # logger.debug(f"data: {data}") + # self.current_data = data + # db에서 다운로드 완료 유무 체크 + def plugin_load(self): self.queue = FfmpegQueue( P, P.ModelSetting.get_int("anilife_max_ffmpeg_process_count") diff --git a/templates/anime_downloader_anilife_setting.html b/templates/anime_downloader_anilife_setting.html index 9d781f3..d04fd09 100644 --- a/templates/anime_downloader_anilife_setting.html +++ b/templates/anime_downloader_anilife_setting.html @@ -28,7 +28,7 @@ {{ macros.m_tab_content_start('auto', false) }} {{ macros.setting_global_scheduler_sub_button(arg['scheduler'], arg['is_running']) }} - {{ macros.setting_input_text('anilife_interval', '스케쥴링 실행 정보', value=arg['anilife_interval'], col='3', desc=['Interval(minute 단위)이나 Cron 설정']) }} + {{ macros.setting_input_text('anilife_interval', '스케쥴링 실행 정보', value=arg['anilife_interval'], col='4', desc=['Interval(minute 단위)이나 Cron 설정']) }} {{ macros.setting_checkbox('anilife_auto_start', '시작시 자동실행', value=arg['anilife_auto_start'], desc='On : 시작시 자동으로 스케쥴러에 등록 됩니다.') }} {{ macros.setting_input_textarea('anilife_auto_code_list', '자동 다운로드할 작품 코드', desc=['구분자 | 또는 엔터'], value=arg['anilife_auto_code_list'], row='10') }} {{ macros.setting_checkbox('anilife_auto_mode_all', '에피소드 모두 받기', value=arg['anilife_auto_mode_all'], desc=['On : 이전 에피소드를 모두 받습니다.', 'Off : 최신 에피소드만 받습니다.']) }}