From 88aeb888b3d22e2781d8e20cbf2dfe382449a85d Mon Sep 17 00:00:00 2001 From: projectdx Date: Thu, 1 Jan 2026 22:58:25 +0900 Subject: [PATCH] refactor: Implement common base module and model for anime downloader plugins and refactor existing modules to use them. --- info.yaml | 2 +- mod_anilife.py | 58 +----- mod_base.py | 192 ++++++++++++++++++ mod_linkkf.py | 95 +-------- mod_ohli24.py | 64 ++---- model_base.py | 76 +++++++ .../anime_downloader_anilife_category.html | 46 ++--- templates/anime_downloader_anilife_queue.html | 10 +- .../anime_downloader_anilife_request.html | 40 ++-- .../anime_downloader_anilife_search.html | 48 ++--- .../anime_downloader_anilife_setting.html | 24 ++- .../anime_downloader_linkkf_category.html | 44 ++-- .../anime_downloader_linkkf_request.html | 30 +-- templates/anime_downloader_linkkf_search.html | 60 +++--- .../anime_downloader_linkkf_setting.html | 24 ++- .../anime_downloader_ohli24_category_old.html | 44 ++-- templates/anime_downloader_ohli24_list.html | 4 +- .../anime_downloader_ohli24_request.html | 42 ++-- templates/anime_downloader_ohli24_search.html | 38 ++-- .../anime_downloader_ohli24_setting.html | 23 ++- 20 files changed, 555 insertions(+), 409 deletions(-) create mode 100644 mod_base.py create mode 100644 model_base.py diff --git a/info.yaml b/info.yaml index a013d3b..1efffdb 100644 --- a/info.yaml +++ b/info.yaml @@ -1,5 +1,5 @@ title: "애니 다운로더" -version: "0.3.7" +version: "0.3.8" package_name: "anime_downloader" developer: "projectdx" description: "anime downloader" diff --git a/mod_anilife.py b/mod_anilife.py index a53062d..f7c99df 100644 --- a/mod_anilife.py +++ b/mod_anilife.py @@ -47,7 +47,7 @@ from sqlalchemy import or_, and_, func, not_, desc from framework import db, scheduler, path_data, socketio from framework.util import Util from framework import F -from plugin import PluginModuleBase +from .mod_base import AnimeModuleBase from .lib.ffmpeg_queue_v1 import FfmpegQueueEntity, FfmpegQueue from support.expand.ffmpeg import SupportFfmpeg from .lib.crawler import Crawler @@ -68,7 +68,7 @@ logger = P.logger name = "anilife" -class LogicAniLife(PluginModuleBase): +class LogicAniLife(AnimeModuleBase): db_default = { "anilife_db_version": "1", "anilife_url": "https://anilife.live", @@ -162,8 +162,7 @@ class LogicAniLife(PluginModuleBase): } def __init__(self, P): - super(LogicAniLife, self).__init__(P, "setting", scheduler_desc="애니라이프 자동 다운로드") - self.name = "anilife" + super(LogicAniLife, self).__init__(P, setup_default=self.db_default, name=name, first_menu='setting', scheduler_desc="애니라이프 자동 다운로드") self.queue = None self.OS_PLATFORM = platform.system() default_route_socketio_module(self, attach="/search") @@ -486,51 +485,7 @@ class LogicAniLife(PluginModuleBase): def db_init(): pass - def process_menu(self, sub, req): - arg = P.ModelSetting.to_dict() - arg["sub"] = self.name - if sub in ["setting", "queue", "list", "search", "request"]: - if sub == "request" and req.args.get("content_code") is not None: - arg["anilife_current_code"] = req.args.get("content_code") - if sub == "setting": - job_id = "%s_%s" % (self.P.package_name, self.name) - arg["scheduler"] = str(scheduler.is_include(job_id)) - arg["is_running"] = str(scheduler.is_running(job_id)) - return render_template( - "{package_name}_{module_name}_{sub}.html".format( - package_name=P.package_name, module_name=self.name, sub=sub - ), - arg=arg, - ) - return render_template("sample.html", title="%s - %s" % (P.package_name, sub)) - def socketio_callback(self, refresh_type, data): - """ - socketio를 통해 클라이언트에 상태 업데이트 전송 - refresh_type: 'add', 'status', 'last', 'list_refresh' 등 - data: entity.as_dict() 데이터 또는 리스트 갱신용 빈 문자열 - """ - try: - from flaskfarm.lib.framework.init_main import socketio - - # /package_name/module_name/queue 네임스페이스로 emit - namespace = f"/{P.package_name}/{self.name}/queue" - - # 큐 페이지 소켓에 직접 emit - socketio.emit(refresh_type, data, namespace=namespace, broadcast=True) - - # 진행 상태인 경우 /framework 네임스페이스로 전역 알림(옵션) - if refresh_type == "status" and isinstance(data, dict): - percent = data.get('percent', 0) - if percent > 0 and percent % 10 == 0: # 10% 단위로 전역 알림 - notify_data = { - "type": "info", - "msg": f"[Anilife] 다운로드중 {percent}% - {data.get('filename', '')}", - } - socketio.emit("notify", notify_data, namespace="/framework", broadcast=True) - - except Exception as e: - logger.error(f"socketio_callback error: {e}") def process_ajax(self, sub, req): try: @@ -606,13 +561,6 @@ class LogicAniLife(PluginModuleBase): if ret["ret"].startswith("enqueue"): self.socketio_callback("list_refresh", "") return jsonify(ret) - elif sub == "entity_list": - return jsonify(self.queue.get_entity_list()) - elif sub == "queue_command": - ret = self.queue.command( - req.form["command"], int(req.form["entity_id"]) - ) - return jsonify(ret) elif sub == "add_queue_checked_list": data = json.loads(request.form["data"]) diff --git a/mod_base.py b/mod_base.py new file mode 100644 index 0000000..1e88671 --- /dev/null +++ b/mod_base.py @@ -0,0 +1,192 @@ +from flask import render_template, request, jsonify +from flaskfarm.lib.plugin import PluginModuleBase +import framework +import os, traceback, time, json +from datetime import datetime + +class AnimeModuleBase(PluginModuleBase): + def __init__(self, P, setup_default=None, **kwargs): + super(AnimeModuleBase, self).__init__(P, **kwargs) + self.P = P # Ensure P is available via self.P + if setup_default: + self.init_module_settings(setup_default) + + def init_module_settings(self, setup_default): + try: + for key, value in setup_default.items(): + if self.P.ModelSetting.get(key) is None: + self.P.ModelSetting.set(key, value) + except Exception as e: + self.P.logger.error(f"Settings Init Error: {e}") + self.P.logger.error(traceback.format_exc()) + + def process_menu(self, sub, req): + from framework import F + try: + # sub can be None from first_menu + if sub is None: + sub = self.first_menu + + arg = self.P.ModelSetting.to_dict() if self.P.ModelSetting is not None else {} + arg["sub"] = self.name + arg["sub2"] = sub + arg["package_name"] = self.P.package_name + arg["module_name"] = self.name + arg['path_data'] = F.config['path_data'] + + # job_id for scheduler + job_id = f"{self.P.package_name}_{self.name}" + arg['is_include'] = F.scheduler.is_include(job_id) + arg['is_running'] = F.scheduler.is_running(job_id) + # Legacy compatibility for some templates + arg["scheduler"] = str(arg['is_include']) + + code = req.args.get("content_code") or req.args.get("code") + if sub == "request" and code is not None: + arg[f"{self.name}_current_code"] = code + + # Check template existence + template_name = f"{self.P.package_name}_{self.name}_{sub}.html" + return render_template(template_name, arg=arg) + + except Exception as e: + self.P.logger.error(f"Menu Error: {e}") + self.P.logger.error(traceback.format_exc()) + return render_template("sample.html", title=f"Error: {e}") + + def process_ajax(self, sub, req): + try: + if sub == 'setting_save': + ret = self.P.ModelSetting.setting_save(req) + return jsonify(ret) + + elif sub == 'scheduler': + go = req.form['scheduler'] + job_id = f"{self.P.package_name}_{self.name}" + if go == 'true': + framework.scheduler.manage_process(job_id, 'sched', {'sub': self.name}) + else: + framework.scheduler.manage_process(job_id, 'cancel', None) + return jsonify(go) + + elif sub in ['immediately_execute', 'one_execute']: + job_id = f"{self.P.package_name}_{self.name}" + framework.scheduler.manage_process(job_id, 'execute', {'sub': self.name}) + return jsonify({'ret': 'success', 'msg': '작업을 시작합니다.'}) + + elif sub == 'reset_db': + return jsonify(self.reset_db()) + + elif sub == 'browse_dir': + # Folder Browser Logic (Matches UI expectation) + path = req.form.get('path') + if not path: + path = '/' + + current_path = os.path.abspath(path) + if not os.path.exists(current_path): + current_path = '/' + + parent_path = os.path.dirname(current_path) + if parent_path == current_path: + parent_path = None + + dirs = [] + try: + for name in os.listdir(current_path): + full_path = os.path.join(current_path, name) + if os.path.isdir(full_path) and not name.startswith('.'): + dirs.append({'name': name, 'path': full_path}) + + dirs.sort(key=lambda x: x['name']) + return jsonify({'ret': 'success', 'directories': dirs, 'current_path': current_path, 'parent_path': parent_path}) + except Exception as e: + return jsonify({'ret': 'fail', 'error': str(e)}) + + elif sub == 'queue_command': + cmd = req.form['command'] + entity_id = int(req.form['entity_id']) + ret = self.queue.command(cmd, entity_id) + return jsonify(ret) + + elif sub == 'entity_list': + return jsonify(self.queue.get_entity_list()) + + elif sub == 'add_whitelist': + # Common whitelist addition + data = req.get_json() if req.is_json else req.form + data_code = data.get('data_code') + if hasattr(self, 'add_whitelist'): + return self.add_whitelist(data_code) + else: + return jsonify({'ret': False, 'log': 'Not implemented'}) + + elif sub == 'command': + command = request.form.get('command') + arg1 = request.form.get('arg1') + arg2 = request.form.get('arg2') + arg3 = request.form.get('arg3') + return self.process_command(command, arg1, arg2, arg3, req) + + except Exception as e: + self.P.logger.error(f"AJAX Error: {e}") + self.P.logger.error(traceback.format_exc()) + return jsonify({'ret': 'fail', 'log': str(e)}) + + def process_command(self, command, arg1, arg2, arg3, req): + try: + if command == "list": + ret = self.queue.get_entity_list() if self.queue else [] + return jsonify(ret) + elif command == "stop": + entity_id = int(arg1) + result = self.queue.command("cancel", entity_id) if self.queue else {"ret": "error"} + return jsonify(result) + elif command == "remove": + entity_id = int(arg1) + result = self.queue.command("remove", entity_id) if self.queue else {"ret": "error"} + return jsonify(result) + elif command in ["reset", "delete_completed"]: + result = self.queue.command(command, 0) if self.queue else {"ret": "error"} + return jsonify(result) + + return jsonify({'ret': 'fail', 'log': f'Unknown command: {command}'}) + except Exception as e: + self.P.logger.error(f"Command Error: {e}") + self.P.logger.error(traceback.format_exc()) + return jsonify({'ret': 'fail', 'log': str(e)}) + + def socketio_callback(self, refresh_type, data): + """ + socketio를 통해 클라이언트에 상태 업데이트 전송 + refresh_type: 'add', 'status', 'last', 'list_refresh' 등 + data: entity.as_dict() 데이터 또는 리스트 갱신용 빈 문자열 + """ + try: + from framework import socketio + + # /package_name/module_name/queue 네임스페이스로 emit + namespace = f"/{self.P.package_name}/{self.name}/queue" + + # 큐 페이지 소켓에 직접 emit + socketio.emit(refresh_type, data, namespace=namespace, broadcast=True) + + except Exception as e: + self.P.logger.error(f"socketio_callback error: {e}") + + def reset_db(self): + try: + # Drop tables logic or delete all rows + # This requires access to specific Models. + # Child class should implement or pass Models? + # Or use self.web_list_model if set + if self.web_list_model: + framework.db.session.query(self.web_list_model).delete() + + # Delete queue items? + # ... + framework.db.session.commit() + return {'ret': 'success', 'msg': 'DB가 초기화되었습니다.'} + except Exception as e: + return {'ret': 'fail', 'msg': str(e)} + diff --git a/mod_linkkf.py b/mod_linkkf.py index 2181dc3..662edb8 100644 --- a/mod_linkkf.py +++ b/mod_linkkf.py @@ -27,7 +27,7 @@ from flaskfarm.lib.support.expand.ffmpeg import SupportFfmpeg # sjva 공용 from framework import db, path_data, scheduler from lxml import html -from plugin import PluginModuleBase +from .mod_base import AnimeModuleBase from requests_cache import CachedSession # cloudscraper는 lazy import로 처리 @@ -51,7 +51,7 @@ logger = P.logger name = "linkkf" -class LogicLinkkf(PluginModuleBase): +class LogicLinkkf(AnimeModuleBase): current_headers = None current_data = None referer = None @@ -76,11 +76,8 @@ class LogicLinkkf(PluginModuleBase): } def __init__(self, P): - super(LogicLinkkf, self).__init__( - P, "setting", scheduler_desc="linkkf 자동 다운로드" - ) + super(LogicLinkkf, self).__init__(P, setup_default=self.db_default, name=name, first_menu='setting', scheduler_desc="linkkf 자동 다운로드") self.queue = None - self.name = name self.db_default = { "linkkf_db_version": "1", "linkkf_url": "https://linkkf.live", @@ -111,23 +108,6 @@ class LogicLinkkf(PluginModuleBase): default_route_socketio_module(self, attach="/setting") self.current_data = None - def process_menu(self, sub, req): - arg = P.ModelSetting.to_dict() - arg["sub"] = self.name - if sub in ["setting", "queue", "category", "list", "request", "search"]: - if sub == "request" and req.args.get("code") is not None: - arg["linkkf_current_code"] = req.args.get("code") - if sub == "setting": - job_id = "%s_%s" % (self.P.package_name, self.name) - arg["scheduler"] = str(scheduler.is_include(job_id)) - arg["is_running"] = str(scheduler.is_running(job_id)) - return render_template( - "{package_name}_{module_name}_{sub}.html".format( - package_name=P.package_name, module_name=self.name, sub=sub - ), - arg=arg, - ) - return render_template("sample.html", title="%s - %s" % (P.package_name, sub)) def process_ajax(self, sub, req): try: @@ -205,17 +185,6 @@ class LogicLinkkf(PluginModuleBase): ret["ret"] = "error" ret["log"] = str(e) return jsonify(ret) - elif sub == "entity_list": - ret = {"list": self.queue.get_entity_list() if self.queue else []} - return jsonify(ret) - elif sub == "queue_command": - cmd = request.form.get("cmd", "") - entity_id = request.form.get("entity_id", "") - if self.queue: - ret = self.queue.command(cmd, int(entity_id) if entity_id else 0) - else: - ret = {"ret": "error", "log": "Queue not initialized"} - return jsonify(ret) elif sub == "add_queue_checked_list": # 선택된 에피소드 일괄 추가 (백그라운드 스레드로 처리) import threading @@ -429,64 +398,6 @@ class LogicLinkkf(PluginModuleBase): P.logger.error(traceback.format_exc()) return jsonify({"ret": "error", "log": str(e)}) - def process_command(self, command, arg1, arg2, arg3, req): - """ - FlaskFarm 프레임워크가 /command 엔드포인트에서 호출하는 함수 - queue 페이지에서 list, stop 등의 명령을 처리 - """ - ret = {"ret": "success"} - # logger.debug(f"process_command - command: {command}, arg1: {arg1}") - - if command == "list": - # 큐 목록 반환 - if self.queue: - ret = [x for x in self.queue.get_entity_list()] - else: - ret = [] - return jsonify(ret) - - elif command == "stop": - # 다운로드 중지 (cancel) - if self.queue and arg1: - try: - entity_id = int(arg1) - result = self.queue.command("cancel", entity_id) - if result: - ret = result - except Exception as e: - ret = {"ret": "error", "log": str(e)} - return jsonify(ret) - - elif command == "remove": - # 개별 항목 삭제 - if self.queue and arg1: - try: - entity_id = int(arg1) - result = self.queue.command("remove", entity_id) - if result: - ret = result - except Exception as e: - ret = {"ret": "error", "log": str(e)} - return jsonify(ret) - - elif command in ["reset", "delete_completed"]: - # 전체 초기화 또는 완료 삭제 - if self.queue: - result = self.queue.command(command, 0) - if result: - ret = result - return jsonify(ret) - - elif command == "queue_list": - # 대기 큐 목록 - if self.queue: - ret = [x for x in self.queue.get_entity_list()] - else: - ret = [] - return jsonify(ret) - - # 기본 응답 - return jsonify(ret) def socketio_callback(self, refresh_type, data): """ diff --git a/mod_ohli24.py b/mod_ohli24.py index 5f44606..164497b 100644 --- a/mod_ohli24.py +++ b/mod_ohli24.py @@ -55,6 +55,8 @@ from .lib.util import Util # from support_site import SupportKakaotv from .setup import * +from .mod_base import AnimeModuleBase +from .model_base import AnimeQueueEntity logger = P.logger @@ -62,7 +64,7 @@ print("*=" * 50) name = "ohli24" -class LogicOhli24(PluginModuleBase): +class LogicOhli24(AnimeModuleBase): current_headers: Optional[Dict[str, str]] = None current_data: Optional[Dict[str, Any]] = None referer: Optional[str] = None @@ -108,7 +110,6 @@ class LogicOhli24(PluginModuleBase): current_download_count = 0 def __init__(self, P: Any) -> None: - super(LogicOhli24, self).__init__(P, "setting", scheduler_desc="ohli24 자동 다운로드") self.name: str = name self.db_default = { @@ -135,6 +136,7 @@ class LogicOhli24(PluginModuleBase): "ohli24_image_url_prefix_episode": "https://www.jetcloud-list.cc/thumbnail/", "ohli24_discord_notify": "True", } + super(LogicOhli24, self).__init__(P, name=name, first_menu='setting', scheduler_desc="ohli24 자동 다운로드", setup_default=self.db_default) self.queue = None # default_route_socketio(P, self) self.web_list_model = ModelOhli24Item @@ -164,9 +166,7 @@ class LogicOhli24(PluginModuleBase): except Exception as e: logger.error(f"Error during stale temp cleanup: {e}") - @staticmethod - def db_init() -> None: - pass + # try: # for key, value in P.Logic.db_default.items(): # if db.session.query(ModelSetting).filter_by(key=key).count() == 0: @@ -176,27 +176,14 @@ class LogicOhli24(PluginModuleBase): # logger.error('Exception:%s', e) # logger.error(traceback.format_exc()) - def process_menu(self, sub: str, req: Any) -> str: - arg = P.ModelSetting.to_dict() - arg["sub"] = self.name - if sub in ["setting", "queue", "list", "category", "request", "search"]: - if sub == "request" and req.args.get("content_code") is not None: - arg["ohli24_current_code"] = req.args.get("content_code") - elif sub == "setting": - job_id = "%s_%s" % (self.P.package_name, self.name) - arg["scheduler"] = str(scheduler.is_include(job_id)) - arg["is_running"] = str(scheduler.is_running(job_id)) - return render_template( - "{package_name}_{module_name}_{sub}.html".format( - package_name=P.package_name, module_name=self.name, sub=sub - ), - arg=arg, - ) - return render_template("sample.html", title="%s - %s" % (P.package_name, sub)) + # @staticmethod def process_ajax(self, sub: str, req: Any) -> Any: try: + ret = super().process_ajax(sub, req) + if ret: return ret + data = [] cate = request.form.get("type", None) page = request.form.get("page", None) @@ -523,13 +510,7 @@ class LogicOhli24(PluginModuleBase): def process_command(self, command, arg1, arg2, arg3, req): ret = {"ret": "success"} - if command == "queue_list": - logger.debug("queue_list") - logger.debug(f"self.queue.get_entity_list():: {self.queue.get_entity_list()}") - ret = [x for x in self.queue.get_entity_list()] - - return ret - elif command == "download_program": + if command == "download_program": _pass = arg2 db_item = ModelOhli24Program.get(arg1) if _pass == "false" and db_item is not None: @@ -549,26 +530,9 @@ class LogicOhli24(PluginModuleBase): db_item.init_for_queue() self.download_queue.put(db_item) ret["msg"] = "다운로드를 추가 하였습니다." + return jsonify(ret) - elif command == "list": - ret = [] - for ins in SupportFfmpeg.get_list(): - ret.append(ins.get_data()) - - elif command == "queue_command": - if arg1 == "cancel": - pass - elif arg1 == "reset": - logger.debug("reset") - # if self.queue is not None: - # with self.queue.mutex: - # self.queue.queue.clear() - - if self.download_queue is not None: - with self.download_queue.mutex: - self.download_queue.queue.clear() - - return jsonify(ret) + return super().process_command(command, arg1, arg2, arg3, req) @staticmethod def add_whitelist(*args): @@ -1567,7 +1531,7 @@ class LogicOhli24(PluginModuleBase): logger.error(traceback.format_exc()) -class Ohli24QueueEntity(FfmpegQueueEntity): +class Ohli24QueueEntity(AnimeQueueEntity): def __init__(self, P: Any, module_logic: LogicOhli24, info: Dict[str, Any]) -> None: super(Ohli24QueueEntity, self).__init__(P, module_logic, info) self._vi: Optional[Any] = None @@ -1590,6 +1554,7 @@ class Ohli24QueueEntity(FfmpegQueueEntity): def refresh_status(self) -> None: + super().refresh_status() # ffmpeg_queue_v1.py에서 실패 처리(-1)된 경우 DB 업데이트 트리거 if getattr(self, 'ffmpeg_status', 0) == -1: reason = getattr(self, 'ffmpeg_status_kor', 'Unknown Error') @@ -1630,6 +1595,7 @@ class Ohli24QueueEntity(FfmpegQueueEntity): return tmp def download_completed(self) -> None: + super().download_completed() logger.debug("download completed.......!!") logger.debug(f"[DB_COMPLETE] Looking up entity by ohli24_id: {self.info.get('_id')}") db_entity = ModelOhli24Item.get_by_ohli24_id(self.info["_id"]) diff --git a/model_base.py b/model_base.py new file mode 100644 index 0000000..6d4f2f7 --- /dev/null +++ b/model_base.py @@ -0,0 +1,76 @@ +from .lib.ffmpeg_queue_v1 import FfmpegQueueEntity +from framework import db +import os, shutil, re +from datetime import datetime + +class AnimeQueueEntity(FfmpegQueueEntity): + def __init__(self, P, module_logic, info): + super(AnimeQueueEntity, self).__init__(P, module_logic, info) + self.P = P + + def refresh_status(self): + """Common status refresh logic""" + if self.ffmpeg_status == -1: + self.ffmpeg_status_kor = "대기" + elif self.ffmpeg_status == 0: + self.ffmpeg_status_kor = "대기" # Waiting in queue + elif self.ffmpeg_status == 1: + self.ffmpeg_status_kor = "분석 중" + elif self.ffmpeg_status == 2: + self.ffmpeg_status_kor = "다운로드 중" + elif self.ffmpeg_status == 3: + self.ffmpeg_status_kor = "변환 중" # post-processing + elif self.ffmpeg_status == 4: + self.ffmpeg_status_kor = "실패" + elif self.ffmpeg_status == 5: + self.ffmpeg_status_kor = "다운로드 중" # downloading + elif self.ffmpeg_status == 6: + self.ffmpeg_status_kor = "취소" + elif self.ffmpeg_status == 7: + self.ffmpeg_status_kor = "완료" + elif self.ffmpeg_status == 8: + self.ffmpeg_status_kor = "완료(이미 있음)" + elif self.ffmpeg_status == 9: + self.ffmpeg_status_kor = "실패(파일 없음)" + + def download_completed(self): + """Common file move logic""" + try: + # LogicCommon to move file + # Specific implementation might vary but usually: + # 1. Check self.savepath + # 2. Check self.filename + # 3. Move self.filepath to dest + + if not self.savepath or not self.filename: + return + + if not os.path.exists(self.savepath): + os.makedirs(self.savepath) + + # Clean filename + # self.filename = Util.change_text_for_use_filename(self.filename) + # (Assuming Util available or do basic replace) + self.filename = re.sub(r'[\\/:*?"<>|]', '', self.filename) + + dest_path = os.path.join(self.savepath, self.filename) + if self.filepath and os.path.exists(self.filepath): + if os.path.exists(dest_path): + self.P.logger.info(f"File exists, removing source: {dest_path}") + # policy: overwrite or skip? usually overwrite or skip + # Here assume overwrite or just move + os.remove(dest_path) # overwrite + + shutil.move(self.filepath, dest_path) + self.filepath = dest_path # Update filepath to new location + self.ffmpeg_status = 7 + self.ffmpeg_status_kor = "완료" + self.end_time = datetime.now() + except Exception as e: + self.P.logger.error(f"Download completed error: {e}") + self.ffmpeg_status = 4 + self.ffmpeg_status_kor = "이동 실패" + + def info_dict(self, tmp): + """Default valid implementation""" + return tmp diff --git a/templates/anime_downloader_anilife_category.html b/templates/anime_downloader_anilife_category.html index a56d7bb..78bd2e8 100644 --- a/templates/anime_downloader_anilife_category.html +++ b/templates/anime_downloader_anilife_category.html @@ -93,14 +93,14 @@ const loader = document.getElementById("preloader"); const dismissLoadingScreen = async function () { - console.log("Before the delay") + // console.log("Before the delay") // await delay(2.5); loader.style.display = "none"; }; const get_anime_list = (type, page) => { - console.log(`type: ${type}, page: ${page}`) + // console.log(`type: ${type}, page: ${page}`) let url = '' let data = {"page": page, "type": type} @@ -137,7 +137,7 @@ dataType: "json", success: (ret) => { current_screen_movie_data = ret - console.log('ret::>', ret) + // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -153,7 +153,7 @@ make_screen_movie_list(ret.data, page) } div_visible = true - console.log(div_visible) + // console.log(div_visible) } dismissLoadingScreen() next_page = page + 1 @@ -216,7 +216,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -226,7 +226,7 @@ if (data.anime_list[i].wr_id !== '') { const re = /bo_table=([^&]+)/ const bo_table = data.anime_list[i].link.match(re) - // console.log(bo_table) + // // console.log(bo_table) request_url = './request?code=' + data.anime_list[i].code + '&wr_id=' + data.anime_list[i].wr_id + '&bo_table=' + bo_table[1] } else { request_url = './request?code=' + data.anime_list[i].code @@ -269,7 +269,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -343,12 +343,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - console.log(query); + // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - console.log("search keyword nothing"); + // console.log("search keyword nothing"); return false; } @@ -361,7 +361,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - console.log('ret:::', ret) + // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -374,28 +374,28 @@ }); $('#anime_category #ing').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("ing", 1) }) $('#anime_category #complete_anilist').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("fin", 1) }) $('#anime_category #theater').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("theater", 1) }) $('#anime_category #top20').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("top20", 1) @@ -405,7 +405,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -414,8 +414,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - console.log(ret.data) + // // console.log(ret.code) + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -443,7 +443,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -507,9 +507,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - console.log("return page:::> ", String(response.page)); + // console.log("return page:::> ", String(response.page)); // {#page = response.page#} if (current_cate === 'search') { make_search_result_list(response.data, response.page); @@ -529,9 +529,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight) { document.getElementById("spinner").style.display = "block"; - console.log("loading"); - console.log("now page::> ", page); - console.log("next_page::> ", String(next_page)); + // console.log("loading"); + // console.log("now page::> ", page); + // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page); } }; diff --git a/templates/anime_downloader_anilife_queue.html b/templates/anime_downloader_anilife_queue.html index 865ee20..3b8be8f 100644 --- a/templates/anime_downloader_anilife_queue.html +++ b/templates/anime_downloader_anilife_queue.html @@ -61,16 +61,16 @@ $(document).ready(function () { const socket_url = window.location.protocol + "//" + document.domain + ":" + location.port + "/anime_downloader/anilife/queue"; - console.log("Connecting to socket:", socket_url); + // console.log("Connecting to socket:", socket_url); const socket = io.connect(socket_url); socket.on('connect', function() { - console.log('Socket connected to anilife queue!'); + // console.log('Socket connected to anilife queue!'); }); // 모든 이벤트 모니터링 (디버깅용) socket.onAny((event, ...args) => { - console.log(`[Socket event: ${event}]`, args); + // console.log(`[Socket event: ${event}]`, args); }); socket.on('start', function (data) { @@ -87,7 +87,7 @@ }, 3000); socket.on('status', function (data) { - console.log("Status update received:", data); + // console.log("Status update received:", data); status_html(data); }); @@ -121,7 +121,7 @@ globalSendCommand('list', null, null, null, function (data) { current_data = data; $("#list").html(''); - console.log(data) + // console.log(data) if (data.length == 0) { str = "

작업이 없습니다.

"; } else { diff --git a/templates/anime_downloader_anilife_request.html b/templates/anime_downloader_anilife_request.html index bde18df..d2c2a17 100644 --- a/templates/anime_downloader_anilife_request.html +++ b/templates/anime_downloader_anilife_request.html @@ -61,7 +61,7 @@ const loader = document.getElementById("preloader"); const dismissLoadingScreen = async function () { - console.log("Before the delay") + // console.log("Before the delay") // await delay(2.5); loader.style.display = "none"; }; @@ -69,7 +69,7 @@ const wait_seconds = function () { // REFERENCE: https://www.w3schools.com/jsref/met_win_settimeout.asp let result = setTimeout(dismissLoadingScreen, 2500); - console.log(result) + // console.log(result) }; const init = function () { @@ -83,11 +83,11 @@ async function myAsyncFunction() { //Do what you want here - console.log("Before the delay") + // console.log("Before the delay") await delay(2.5); - console.log("After the delay") + // console.log("After the delay") //Do what you want here too } @@ -102,7 +102,7 @@ // e.stopPropagation() // e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -111,8 +111,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// console.log(ret.code)#} + // console.log(ret.data) make_program(ret.data) $("#loader").css("display", 'none') } else { @@ -124,7 +124,7 @@ function make_program(data) { current_data = data; - // console.log("current_data::", current_data) + // // console.log("current_data::", current_data) // 에피소드 목록을 완전히 숨긴 상태로 시작 (visibility로 레이아웃 시프트 방지) const episodeList = document.getElementById("episode_list"); @@ -255,9 +255,9 @@ } $(function () { - console.log(params.wr_id) - console.log(findGetParameter('wr_id')) - console.log(params.code) + // console.log(params.wr_id) + // console.log(findGetParameter('wr_id')) + // console.log(params.code) if (params.code === '') { } else { @@ -267,15 +267,15 @@ if ("{{arg['anilife_current_code']}}" !== "") { if (params.code === null) { - console.log('params.code === null') + // console.log('params.code === null') document.getElementById("code").value = "{{arg['anilife_current_code']}}"; } else if (params.code === '') { document.getElementById("code").value = "{{arg['anilife_current_code']}}"; } else { - console.log('params code exist') - console.log(params.code) + // console.log('params code exist') + // console.log(params.code) document.getElementById("code").value = params.code analyze(params.wr_id, params.bo_table) @@ -309,7 +309,7 @@ }, 100); $("#loader").css("display", 'none'); - console.log({{ arg['code'] }}) + // console.log({{ arg['code'] }}) }); $("#analysis_btn").unbind("click").bind('click', function (e) { @@ -317,7 +317,7 @@ e.stopPropagation() $("#loader").css("display", 'block') const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -327,8 +327,8 @@ success: function (ret) { $("#loader").css("display", 'none') if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// console.log(ret.code)#} + // console.log(ret.data) make_program(ret.data) dismissLoadingScreen() @@ -358,7 +358,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -366,7 +366,7 @@ data: {data: JSON.stringify(data)}, dataType: "json", success: function (data) { - console.log('#add_queue_btn::data >>', data) + // console.log('#add_queue_btn::data >>', data) if (data.ret == 'enqueue_db_append' || data.ret == 'enqueue_db_exist') { $.notify('다운로드 작업을 추가 하였습니다.', {type: 'success'}); } else if (data.ret == 'queue_exist') { diff --git a/templates/anime_downloader_anilife_search.html b/templates/anime_downloader_anilife_search.html index e810020..93a5aa4 100644 --- a/templates/anime_downloader_anilife_search.html +++ b/templates/anime_downloader_anilife_search.html @@ -91,14 +91,14 @@ const loader = document.getElementById("preloader"); const dismissLoadingScreen = async function () { - console.log("Before the delay") + // console.log("Before the delay") // await delay(2.5); loader.style.display = "none"; }; const get_anime_list = (type, page) => { - console.log(`type: ${type}, page: ${page}`) + // console.log(`type: ${type}, page: ${page}`) let url = '' let data = {"page": page, "type": type} @@ -138,14 +138,14 @@ $.notify("분석 실패
" + ret.log, { type: "warning", }); - console.log("error") + // console.log("error") dismissLoadingScreen() return false; } current_screen_movie_data = ret - console.log('ret::>', ret) + // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -161,7 +161,7 @@ make_screen_movie_list(ret.data, page) } div_visible = true - console.log(div_visible) + // console.log(div_visible) } dismissLoadingScreen() next_page = page + 1 @@ -230,7 +230,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -240,7 +240,7 @@ if (data.anime_list[i].wr_id !== '') { const re = /bo_table=([^&]+)/ const bo_table = data.anime_list[i].link.match(re) - // console.log(bo_table) + // // console.log(bo_table) request_url = './request?code=' + data.anime_list[i].code + '&wr_id=' + data.anime_list[i].wr_id + '&bo_table=' + bo_table[1] } else { request_url = './request?code=' + data.anime_list[i].code @@ -289,7 +289,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -368,12 +368,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - console.log(query); + // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - console.log("search keyword nothing"); + // console.log("search keyword nothing"); return false; } @@ -386,7 +386,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - console.log('ret:::', ret) + // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -400,28 +400,28 @@ }); $('#anime_category #ing').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("ing", 1) }) $('#anime_category #complete_anilist').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("fin", 1) }) $('#anime_category #theater').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("theater", 1) }) $('#anime_category #top20').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("top20", 1) @@ -431,7 +431,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -440,8 +440,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - console.log(ret.data) + // // console.log(ret.code) + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -470,7 +470,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -534,9 +534,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - console.log("return page:::> ", String(response.page)); + // console.log("return page:::> ", String(response.page)); // {#page = response.page#} if (current_cate === 'search') { make_search_result_list(response.data, response.page); @@ -556,9 +556,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight) { document.getElementById("spinner").style.display = "block"; - console.log("loading"); - console.log("now page::> ", page); - console.log("next_page::> ", String(next_page)); + // console.log("loading"); + // console.log("now page::> ", page); + // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page); } }; diff --git a/templates/anime_downloader_anilife_setting.html b/templates/anime_downloader_anilife_setting.html index 81da086..e3939a9 100644 --- a/templates/anime_downloader_anilife_setting.html +++ b/templates/anime_downloader_anilife_setting.html @@ -281,8 +281,28 @@ } /* Collapse Borders */ - .border-left { - border-left: 3px solid rgba(255,255,255,0.1) !important; + /* Folder Browser Modal Styles */ + .folder-item { + cursor: pointer; + transition: background 0.2s; + border-bottom: 1px solid rgba(255,255,255,0.05); + display: flex !important; + align-items: center; + width: 100%; + overflow: hidden; + } + .folder-item:hover { + background: rgba(255, 255, 255, 0.1); + } + .folder-item span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + min-width: 0; + } + .folder-item.selected { + background: rgba(59, 130, 246, 0.3) !important; } diff --git a/templates/anime_downloader_linkkf_category.html b/templates/anime_downloader_linkkf_category.html index f279139..a6afed2 100644 --- a/templates/anime_downloader_linkkf_category.html +++ b/templates/anime_downloader_linkkf_category.html @@ -70,7 +70,7 @@ const get_anime_list = (type, page) => { - console.log(`type: ${type}, page: ${page}`) + // console.log(`type: ${type}, page: ${page}`) let url = '' let data = {"page": page, "type": type} @@ -103,7 +103,7 @@ dataType: "json", success: (ret) => { current_screen_movie_data = ret - console.log('ret::>', ret) + // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -119,7 +119,7 @@ make_screen_movie_list(ret.data, page) } div_visible = true - console.log(div_visible) + // console.log(div_visible) } next_page = page + 1 } @@ -189,7 +189,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -200,7 +200,7 @@ if (data.anime_list[i].wr_id !== '') { const re = /bo_table=([^&]+)/ const bo_table = data.anime_list[i].link.match(re) - console.log(bo_table) + // console.log(bo_table) request_url = './request?code=' + data.anime_list[i].code + '&wr_id=' + data.anime_list[i].wr_id + '&bo_table=' + bo_table[1] } else { @@ -245,7 +245,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -321,12 +321,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - console.log(query); + // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - console.log("search keyword nothing"); + // console.log("search keyword nothing"); return false; } @@ -339,7 +339,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - console.log('ret:::', ret) + // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -352,21 +352,21 @@ }); $('#anime_category #ing').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("ing", 1) }) $('#anime_category #complete_anilist').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("fin", 1) }) $('#anime_category #theater').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("theater", 1) @@ -376,7 +376,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -385,8 +385,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - console.log(ret.data) + // // console.log(ret.code) + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -404,7 +404,7 @@ $("body").on("click", "#add_whitelist", function (e) { e.preventDefault(); let data_code = $(this).attr("data-code"); - console.log(data_code); + // console.log(data_code); $.ajax({ url: "/" + package_name + "/ajax/"+sub+"/add_whitelist", type: "POST", @@ -440,7 +440,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -504,9 +504,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - console.log("return page:::> ", String(response.page)); + // console.log("return page:::> ", String(response.page)); // {#page = response.page#} if (current_cate === 'search') { make_search_result_list(response.data, response.page); @@ -526,9 +526,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight) { document.getElementById("spinner").style.display = "block"; - console.log("loading"); - console.log("now page::> ", page); - console.log("next_page::> ", String(next_page)); + // console.log("loading"); + // console.log("now page::> ", page); + // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page); } }; diff --git a/templates/anime_downloader_linkkf_request.html b/templates/anime_downloader_linkkf_request.html index 204f3cd..678513d 100644 --- a/templates/anime_downloader_linkkf_request.html +++ b/templates/anime_downloader_linkkf_request.html @@ -83,7 +83,7 @@ function analyze(wr_id, bo_table) { // e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -92,8 +92,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// console.log(ret.code)#} + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -192,9 +192,9 @@ } $(function () { - console.log(params.wr_id) - console.log(findGetParameter('wr_id')) - console.log(params.code) + // console.log(params.wr_id) + // console.log(findGetParameter('wr_id')) + // console.log(params.code) if (params.code === '') { } else { @@ -204,15 +204,15 @@ if ("{{arg['linkkf_current_code']}}" !== "") { if (params.code === null) { - console.log('params.code === null') + // console.log('params.code === null') document.getElementById("code").value = "{{arg['linkkf_current_code']}}"; } else if (params.code === '') { document.getElementById("code").value = "{{arg['linkkf_current_code']}}"; } else { - console.log('params code exist') - console.log(params.code) + // console.log('params code exist') + // console.log(params.code) document.getElementById("code").value = params.code analyze(params.wr_id, params.bo_table) @@ -229,7 +229,7 @@ $(document).ready(function () { - console.log('wr_id::', params.wr_id) + // console.log('wr_id::', params.wr_id) }); @@ -237,7 +237,7 @@ e.preventDefault(); e.stopPropagation() const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -246,8 +246,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// console.log(ret.code)#} + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -302,7 +302,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -310,7 +310,7 @@ data: {data: JSON.stringify(data)}, dataType: "json", success: function (data) { - console.log('#add_queue_btn::data >>', data) + // console.log('#add_queue_btn::data >>', data) if (data.ret == 'enqueue_db_append' || data.ret == 'enqueue_db_exist') { $.notify('다운로드 작업을 추가 하였습니다.', {type: 'success'}); } else if (data.ret == 'queue_exist') { diff --git a/templates/anime_downloader_linkkf_search.html b/templates/anime_downloader_linkkf_search.html index 9268008..9438dcb 100644 --- a/templates/anime_downloader_linkkf_search.html +++ b/templates/anime_downloader_linkkf_search.html @@ -126,7 +126,7 @@ success: (ret) => { current_screen_movie_data = ret; total_page = ret.total_page; - // console.log("ret::>", ret); + // // console.log("ret::>", ret); if (current_screen_movie_data !== "") { make_screen_movie_list(ret, page); @@ -175,7 +175,7 @@ dataType: "json", success: (ret) => { current_screen_movie_data = ret - // console.log('ret::>', ret) + // // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -192,7 +192,7 @@ make_screen_movie_list(ret.data, page) } div_visible = true - // console.log(div_visible) + // // console.log(div_visible) } next_page = page + 1 } @@ -321,9 +321,9 @@ let tmp = ""; let new_anime = true; let new_style = '' - // console.log('page a: ', page) - // console.log(data) - // console.log(data.data) + // // console.log('page a: ', page) + // // console.log(data) + // // console.log(data.data) //console.log(data.episode) let page_elem = ""; @@ -341,8 +341,8 @@ str += "
"; str += '
'; for (let i in data.data) { - // console.log(i) - // console.log(data.data[i]) + // // console.log(i) + // // console.log(data.data[i]) if (data.data[i].postid === data.latest_anime_code) { new_anime = false } @@ -438,12 +438,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - // console.log(query); + // // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - // console.log("search keyword nothing"); + // // console.log("search keyword nothing"); return false; } @@ -456,7 +456,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - // console.log('ret:::', ret) + // // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -470,34 +470,34 @@ $("#anime_category").on("click", function (e) { - // console.log($(this)) - // console.log(e) + // // console.log($(this)) + // // console.log(e) switch (e.target.id) { case "ing": - // console.log("ing.....") + // // console.log("ing.....") // spinner_loading.style.display = "block"; current_cate = "ing"; get_anime_list("ing", 1); break; case "movie": - // console.log("movie") + // // console.log("movie") current_cate = "movie"; get_anime_list("movie", 1); break; case "complete_anilist": - // console.log("complete") + // // console.log("complete") current_cate = "complete"; get_anime_list("complete", 1); break; case "top_view": - // console.log("top_view") + // // console.log("top_view") current_cate = "top_view"; get_anime_list("top_view", 1); break; default: - // console.log("default") + // // console.log("default") spinner_loading.style.display = "block"; current_cate = "ing"; get_anime_list(1, "ing"); @@ -512,7 +512,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - // console.log(code) + // // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -521,8 +521,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - // console.log(ret.data) + // // console.log(ret.code) + // // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -582,7 +582,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - // console.log('data:::>', data) + // // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -606,7 +606,7 @@ // const el = document.querySelector('img'); // const observer = lozad(el); // passing a `NodeList` (e.g. `document.querySelectorAll()`) is also valid // observer.observe(); - // console.log('scroll 세로크기:', document.body.scrollHeight) + // // console.log('scroll 세로크기:', document.body.scrollHeight) const loadNextAnimes = (cate, page, ch) => { // spinner.style.display = "block"; @@ -649,9 +649,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - // console.log("return page:::> ", String(response.page)); + // // console.log("return page:::> ", String(response.page)); // {#page = response.page#} loader.style.display = "block"; if (current_cate === 'search') { @@ -661,8 +661,8 @@ make_screen_movie_list(response.data, response.page); } - // console.log(document.body.scrollHeight) - // console.log(ch) + // // console.log(document.body.scrollHeight) + // // console.log(ch) window.scrollBy({ top: ch + 35, left: 0, @@ -682,9 +682,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight + 170) { // document.getElementById("spinner").style.display = "block"; - // console.log("loading"); - // console.log("now page::> ", page); - // console.log("next_page::> ", String(next_page)); + // // console.log("loading"); + // // console.log("now page::> ", page); + // // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page, clientHeight); /*window.scrollBy({ top: e.target.scrollingElement.scrollHeight + 200, diff --git a/templates/anime_downloader_linkkf_setting.html b/templates/anime_downloader_linkkf_setting.html index 10a2a9d..c4f1f16 100644 --- a/templates/anime_downloader_linkkf_setting.html +++ b/templates/anime_downloader_linkkf_setting.html @@ -293,8 +293,28 @@ } /* Collapse Borders */ - .border-left { - border-left: 3px solid rgba(255,255,255,0.1) !important; + /* Folder Browser Modal Styles */ + .folder-item { + cursor: pointer; + transition: background 0.2s; + border-bottom: 1px solid rgba(255,255,255,0.05); + display: flex !important; + align-items: center; + width: 100%; + overflow: hidden; + } + .folder-item:hover { + background: rgba(255, 255, 255, 0.05); + } + .folder-item span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + min-width: 0; + } + .folder-item.selected { + background: rgba(16, 185, 129, 0.3) !important; } diff --git a/templates/anime_downloader_ohli24_category_old.html b/templates/anime_downloader_ohli24_category_old.html index 066b753..b43fe82 100644 --- a/templates/anime_downloader_ohli24_category_old.html +++ b/templates/anime_downloader_ohli24_category_old.html @@ -106,7 +106,7 @@ const get_anime_list = (type, page) => { - console.log(`type: ${type}, page: ${page}`) + // console.log(`type: ${type}, page: ${page}`) let url = '' let data = {"page": page, "type": type} @@ -139,7 +139,7 @@ dataType: "json", success: (ret) => { current_screen_movie_data = ret - console.log('ret::>', ret) + // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -155,7 +155,7 @@ make_screen_movie_list(ret.data, page) } div_visible = true - console.log(div_visible) + // console.log(div_visible) } next_page = page + 1 } @@ -225,7 +225,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -236,7 +236,7 @@ if (data.anime_list[i].wr_id !== '') { const re = /bo_table=([^&]+)/ const bo_table = data.anime_list[i].link.match(re) - console.log(bo_table) + // console.log(bo_table) if (bo_table != null) { request_url = './request?code=' + data.anime_list[i].code + '&wr_id=' + data.anime_list[i].wr_id + '&bo_table=' + bo_table[1] } else { @@ -284,7 +284,7 @@ let str = '' let tmp = '' - console.log(data.anime_list, page) + // console.log(data.anime_list, page) str += '
'; str += ''; @@ -360,12 +360,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - console.log(query); + // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - console.log("search keyword nothing"); + // console.log("search keyword nothing"); return false; } @@ -378,7 +378,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - console.log('ret:::', ret) + // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -391,21 +391,21 @@ }); $('#anime_category #ing').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("ing", 1) }) $('#anime_category #complete_anilist').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("fin", 1) }) $('#anime_category #theater').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} let spinner = document.getElementById('spinner'); spinner.style.visibility = 'visible'; get_anime_list("theater", 1) @@ -415,7 +415,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -424,8 +424,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - console.log(ret.data) + // // console.log(ret.code) + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -443,7 +443,7 @@ $("body").on("click", "#add_whitelist", function (e) { e.preventDefault(); let data_code = $(this).attr("data-code"); - console.log(data_code); + // console.log(data_code); $.ajax({ url: "/" + package_name + "/ajax/" + sub + "/add_whitelist", type: "POST", @@ -479,7 +479,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -543,9 +543,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - console.log("return page:::> ", String(response.page)); + // console.log("return page:::> ", String(response.page)); // {#page = response.page#} if (current_cate === 'search') { make_search_result_list(response.data, response.page); @@ -565,9 +565,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight) { document.getElementById("spinner").style.display = "block"; - console.log("loading"); - console.log("now page::> ", page); - console.log("next_page::> ", String(next_page)); + // console.log("loading"); + // console.log("now page::> ", page); + // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page); } }; diff --git a/templates/anime_downloader_ohli24_list.html b/templates/anime_downloader_ohli24_list.html index c4ff8ef..faf9258 100644 --- a/templates/anime_downloader_ohli24_list.html +++ b/templates/anime_downloader_ohli24_list.html @@ -147,7 +147,7 @@ }); function global_sub_request_search(page, move_top = true) { - console.log('........................') + // console.log('........................') var formData = get_formdata('#form_search') formData += '&page=' + page; $.ajax({ @@ -157,7 +157,7 @@ data: formData, dataType: "json", success: function (data) { - console.log(data) + // console.log(data) current_data = data; if (move_top) { window.scrollTo(0,0); diff --git a/templates/anime_downloader_ohli24_request.html b/templates/anime_downloader_ohli24_request.html index 744f2c0..da922d1 100644 --- a/templates/anime_downloader_ohli24_request.html +++ b/templates/anime_downloader_ohli24_request.html @@ -91,7 +91,7 @@ {#e.preventDefault();#} const code = document.getElementById("code").value - console.log(code) + // // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -100,8 +100,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// // console.log(ret.code)#} + // // console.log(ret.data) var order_text = (ret.data.list_order === 'desc') ? '최신화부터 (역순)' : '1화부터 (정순)'; if (ret.data.list_order === undefined) { // 로직상 list_order가 없을 수 있으므로 체크 @@ -118,7 +118,7 @@ function make_program(data) { current_data = data; - console.log(data); + // // console.log(data); let str = ""; @@ -226,9 +226,9 @@ } $(function () { - console.log(params.wr_id) - console.log(findGetParameter('wr_id')) - console.log(params.code) + // // console.log(params.wr_id) + // // console.log(findGetParameter('wr_id')) + // // console.log(params.code) if (params.code === '') { } else { @@ -238,15 +238,15 @@ if ("{{arg['ohli24_current_code']}}" !== "") { if (params.code === null) { - console.log('params.code === null') + // // console.log('params.code === null') document.getElementById("code").value = "{{arg['ohli24_current_code']}}"; } else if (params.code === '') { document.getElementById("code").value = "{{arg['ohli24_current_code']}}"; } else { - console.log('params code exist') - console.log(params.code) + // // console.log('params code exist') + // // console.log(params.code) document.getElementById("code").value = params.code analyze(params.wr_id, params.bo_table) @@ -262,15 +262,15 @@ }) $(document).ready(function () { - console.log("{{ arg['code'] }}") - console.log('wr_id::', params.wr_id) + // // console.log("{{ arg['code'] }}") + // // console.log('wr_id::', params.wr_id) if (document.getElementById("code").value !== "") { {#document.getElementById("analysis_btn").click()#} $('#analysis_btn').click(); } - console.log(accessibleCount) + // // console.log(accessibleCount) }); @@ -280,12 +280,12 @@ e.stopPropagation(); accessibleCount = accessibleCount - 1; //count부터 뺀다 - console.log(accessibleCount) + // // console.log(accessibleCount) if (accessibleCount < 0) { alert("이미 작업이 수행중 입니다."); } else { const code = document.getElementById("code").value - console.log(code) + // // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -294,17 +294,17 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // {#console.log(ret.code)#} - console.log(ret.data) + // {#// // console.log(ret.code)#} + // // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); } } }); - console.log(accessibleCount) + // // console.log(accessibleCount) accessibleCount++ - console.log(accessibleCount) + // // console.log(accessibleCount) } @@ -356,7 +356,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -364,7 +364,7 @@ data: {data: JSON.stringify(data)}, dataType: "json", success: function (data) { - console.log('#add_queue_btn::data >>', data) + // // console.log('#add_queue_btn::data >>', data) if (data.ret == 'enqueue_db_append' || data.ret == 'enqueue_db_exist') { $.notify('다운로드 작업을 추가 하였습니다.', {type: 'success'}); } else if (data.ret == 'queue_exist') { diff --git a/templates/anime_downloader_ohli24_search.html b/templates/anime_downloader_ohli24_search.html index 94c2b5d..96f9650 100644 --- a/templates/anime_downloader_ohli24_search.html +++ b/templates/anime_downloader_ohli24_search.html @@ -109,7 +109,7 @@ const get_anime_list = (type, page) => { - console.log(`type: ${type}, page: ${page}`) + // console.log(`type: ${type}, page: ${page}`) let url = '' let data = {"page": page, "type": type} @@ -142,7 +142,7 @@ dataType: "json", success: (ret) => { let current_screen_movie_data = ret - console.log('ret::>', ret) + // console.log('ret::>', ret) if (current_screen_movie_data !== '') { if (type === "ing") { @@ -158,7 +158,7 @@ make_screen_movie_list(ret.data, page) } {#div_visible = true#} - {#console.log(div_visible)#} + {#// console.log(div_visible)#} } next_page = page + 1 } @@ -339,12 +339,12 @@ $("body").on("click", "#btn_search", function (e) { e.preventDefault(); let query = $("#input_search").val(); - console.log(query); + // console.log(query); current_cate = "search" current_query = query if ($("#input_search").val() === "") { - console.log("search keyword nothing"); + // console.log("search keyword nothing"); return false; } @@ -357,7 +357,7 @@ contentType: "application/x-www-form-urlencoded; charset=UTF-8", success: function (ret) { if (ret.ret) { - console.log('ret:::', ret) + // console.log('ret:::', ret) make_search_result_list(ret.data, 1); next_page = page + 1 } else { @@ -370,21 +370,21 @@ }); $('#anime_category #ing').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} // let spinner = document.getElementById('spinner'); // spinner.style.visibility = 'visible'; get_anime_list("ing", 1) }) $('#anime_category #complete_anilist').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} // let spinner = document.getElementById('spinner'); // spinner.style.visibility = 'visible'; get_anime_list("fin", 1) }) $('#anime_category #theater').on("click", function () { - // {#console.log(this.id)#} + // {#// console.log(this.id)#} // let spinner = document.getElementById('spinner'); // spinner.style.visibility = 'visible'; get_anime_list("theater", 1) @@ -394,7 +394,7 @@ $("body").on('click', '#analysis_btn', function (e) { e.preventDefault(); const code = document.getElementById("code").value - console.log(code) + // console.log(code) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/analysis', type: "POST", @@ -403,8 +403,8 @@ dataType: "json", success: function (ret) { if (ret.ret === 'success' && ret.data != null) { - // console.log(ret.code) - console.log(ret.data) + // // console.log(ret.code) + // console.log(ret.data) make_program(ret.data) } else { $.notify('분석 실패
' + ret.log, {type: 'warning'}); @@ -422,7 +422,7 @@ $("body").on("click", "#add_whitelist", function (e) { e.preventDefault(); let data_code = $(this).attr("data-code"); - console.log(data_code); + // console.log(data_code); $.ajax({ url: "/" + package_name + "/ajax/" + sub + "/add_whitelist", type: "POST", @@ -458,7 +458,7 @@ $("body").on('click', '#add_queue_btn', function (e) { e.preventDefault(); data = current_data.episode[$(this).data('idx')]; - console.log('data:::>', data) + // console.log('data:::>', data) $.ajax({ url: '/' + package_name + '/ajax/' + sub + '/add_queue', type: "POST", @@ -522,9 +522,9 @@ }) .then((res) => res.json()) .then((response) => { - // console.log("Success:", JSON.stringify(response)); + // // console.log("Success:", JSON.stringify(response)); // {#imagesContainer.appendChild()#} - console.log("return page:::> ", String(response.page)); + // console.log("return page:::> ", String(response.page)); // {#page = response.page#} if (current_cate === 'search') { make_search_result_list(response.data, response.page); @@ -544,9 +544,9 @@ const {scrollTop, scrollHeight, clientHeight} = e.target.scrollingElement; if (Math.round(scrollHeight - scrollTop) <= clientHeight) { {#document.getElementById("spinner").style.display = "block";#} - console.log("loading"); - console.log("now page::> ", page); - console.log("next_page::> ", String(next_page)); + // console.log("loading"); + // console.log("now page::> ", page); + // console.log("next_page::> ", String(next_page)); loadNextAnimes(current_cate, next_page); } }; diff --git a/templates/anime_downloader_ohli24_setting.html b/templates/anime_downloader_ohli24_setting.html index 9aec3cf..1e065f6 100644 --- a/templates/anime_downloader_ohli24_setting.html +++ b/templates/anime_downloader_ohli24_setting.html @@ -329,20 +329,33 @@ /* Folder Browser Modal Styles */ .folder-item { - transition: background-color 0.15s ease; + cursor: pointer; + transition: background 0.2s; + border-bottom: 1px solid rgba(255,255,255,0.05); + display: flex !important; + align-items: center; + width: 100%; + overflow: hidden; font-size: 0.95rem; margin-bottom: 2px; } - .folder-item:hover { - background-color: rgba(59, 130, 246, 0.2) !important; + background: rgba(59, 130, 246, 0.2) !important; + } + .folder-item span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + min-width: 0; + } + .folder-item.selected { + background: rgba(59, 130, 246, 0.3) !important; } - .folder-item.folder-parent, .folder-item.folder-current { font-weight: 600; } - .folder-item i { font-size: 1.1rem; }