diff --git a/README.md b/README.md index b6ab44e..d6b2d1f 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,17 @@ ## ๐Ÿ“ ๋ณ€๊ฒฝ ์ด๋ ฅ (Changelog) +### v0.4.13 (2026-01-02) +- **Ohli24 CSS ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ˆ˜์ •**: + - ํŽ˜์ด์ง€๋ณ„ ๋…๋ฆฝ wrapper (`.ohli24-list-page`, `.ohli24-request-page`, `.ohli24-queue-page`) ์ ์šฉ์œผ๋กœ ์Šคํƒ€์ผ ๊ฐ„์„ญ ์™„์ „ ์ฐจ๋‹จ + - ์š”์ฒญ(Request) ํŽ˜์ด์ง€์˜ ์—ํ”ผ์†Œ๋“œ ์นด๋“œ ๊ฐ€๋กœ ์ •๋ ฌ ๋ ˆ์ด์•„์›ƒ ๋ณต๊ตฌ ๋ฐ ์ตœ์ ํ™” + - ์š”์ฒญ ํŽ˜์ด์ง€ ๋‚ด ๋ถˆํ•„์š”ํ•œ ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ œ๊ฑฐ ๋ฐ ์™ธ๋ถ€ CSS๋กœ ์ผ์›ํ™” +- **์•ˆ์ •์„ฑ ๊ฐ•ํ™”**: + - **Ohli24**: ํ ์ถ”๊ฐ€ ์‹œ ์ฆ‰์‹œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ๋ฐ DB ๋™๊ธฐํ™” ๋กœ์ง ๊ฐ•ํ™” + - **FfmpegQueue**: ๋กœ์ปฌ ํŒŒ์ผ ์กด์žฌ ์‹œ DB ์ƒํƒœ ๋™๊ธฐํ™” ๋ˆ„๋ฝ ์ˆ˜์ • + - **๊ฒ€์ƒ‰**: Ohli24 ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์œ ํšจ์„ฑ ์ฒดํฌ ๋กœ์ง ์ถ”๊ฐ€๋กœ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๋ฐฉ์ง€ +- **๋ชจ๋“ˆ ๊ฒ€์ƒ‰ ์ง€์›**: `model_base.py` ๋‚ด Ohli24 ๋ฐ Linkkf ์ „์šฉ ๊ฒ€์ƒ‰ ์ง€์› ์ถ”๊ฐ€ + ### v0.4.5 (2026-01-02) - **CSS ํ…Œ๋งˆ ์•„ํ‚คํ…์ฒ˜ ์ „๋ฉด ๊ฐœํŽธ**: - ์‚ฌ์ดํŠธ๋ณ„ ๋…๋ฆฝ ํ…Œ๋งˆ ํŒŒ์ผ ๋ถ„๋ฆฌ (`anilife.css`, `linkkf.css`, `ohli24.css`) diff --git a/info.yaml b/info.yaml index 4e2b19b..47ed7bf 100644 --- a/info.yaml +++ b/info.yaml @@ -1,5 +1,5 @@ title: "์• ๋‹ˆ ๋‹ค์šด๋กœ๋”" -version: "0.4.12" +version: "0.4.13" package_name: "anime_downloader" developer: "projectdx" description: "anime downloader" diff --git a/lib/ffmpeg_queue_v1.py b/lib/ffmpeg_queue_v1.py index f4e43d3..1311750 100644 --- a/lib/ffmpeg_queue_v1.py +++ b/lib/ffmpeg_queue_v1.py @@ -228,8 +228,11 @@ class FfmpegQueue(object): has_ytdl_file = os.path.exists(filepath + ".ytdl") if os.path.exists(filepath) and not (is_ytdlp or has_ytdl_file): + logger.info(f"File already exists: {filepath}") + entity.ffmpeg_status = 8 # COMPLETED_EXIST entity.ffmpeg_status_kor = "ํŒŒ์ผ ์žˆ์Œ" entity.ffmpeg_percent = 100 + entity.download_completed() entity.refresh_status() continue dirname = os.path.dirname(filepath) diff --git a/mod_anilife.py b/mod_anilife.py index 892dcb1..3b77b37 100644 --- a/mod_anilife.py +++ b/mod_anilife.py @@ -1718,6 +1718,22 @@ class AniLifeQueueEntity(FfmpegQueueEntity): if not os.path.exists(self.savepath): os.makedirs(self.savepath) + # [IMMEDIATE SYNC] Update DB with extracted metadata + try: + db_entity = ModelAniLifeItem.get_by_anilife_id(self.info["_id"]) + if db_entity: + logger.debug(f"[SYNC] Syncing metadata for AniLife _id: {self.info.get('_id')}") + db_entity.title = self.content_title + db_entity.season = self.season + db_entity.episode_no = self.epi_queue + db_entity.savepath = self.savepath + db_entity.filename = self.filename + db_entity.filepath = self.filepath + db_entity.quality = self.quality + db_entity.save() + except Exception as sync_err: + logger.error(f"Failed to sync metadata to DB: {sync_err}") + # ์ตœ์ข… ๋น„๋””์˜ค URL ์„ค์ • self.url = vod_url logger.info(f"Final video URL: {self.url}") diff --git a/mod_linkkf.py b/mod_linkkf.py index 7b18bc2..80b8680 100644 --- a/mod_linkkf.py +++ b/mod_linkkf.py @@ -463,7 +463,7 @@ class LogicLinkkf(AnimeModuleBase): logger.error(f"browse_dir error: {e}") return jsonify({"ret": "error", "error": str(e)}), 500 - return jsonify({"ret": "error", "log": f"Unknown sub: {sub}"}) + return super().process_ajax(sub, req) except Exception as e: P.logger.error(f"Exception: {str(e)}") diff --git a/mod_ohli24.py b/mod_ohli24.py index b323b2f..498432a 100644 --- a/mod_ohli24.py +++ b/mod_ohli24.py @@ -181,10 +181,6 @@ class LogicOhli24(AnimeModuleBase): # @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) @@ -199,22 +195,23 @@ class LogicOhli24(AnimeModuleBase): self.current_data = data return jsonify({"ret": "success", "data": data, "code": code}) elif sub == "anime_list": - data = self.get_anime_info(cate, page) + if isinstance(data, dict) and data.get("ret") == "error": + return jsonify(data) return jsonify({"ret": "success", "cate": cate, "page": page, "data": data}) elif sub == "complete_list": - logger.debug("cate:: %s", cate) page = request.form["page"] - data = self.get_anime_info(cate, page) + if isinstance(data, dict) and data.get("ret") == "error": + return jsonify(data) return jsonify({"ret": "success", "cate": cate, "page": page, "data": data}) elif sub == "search": - query = request.form["query"] page = request.form["page"] - data = self.get_search_result(query, page, cate) + if isinstance(data, dict) and data.get("ret") == "error": + return jsonify(data) return jsonify( { "ret": "success", @@ -499,8 +496,8 @@ class LogicOhli24(AnimeModuleBase): logger.error(f"browse_dir error: {e}") return jsonify({"ret": "error", "error": str(e)}), 500 - # ๋งค์นญ๋˜์ง€ ์•Š๋Š” sub ์š”์ฒญ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์‘๋‹ต - return jsonify({"error": f"Unknown sub: {sub}"}), 404 + # Fallback to base class for common subs (setting_save, queue_command, entity_list, etc.) + return super().process_ajax(sub, req) def get_episode(self, clip_id): for _ in self.current_data["episode"]: @@ -965,7 +962,7 @@ class LogicOhli24(AnimeModuleBase): except Exception as e: P.logger.error("Exception:%s", e) P.logger.error(traceback.format_exc()) - return {"ret": "exception", "log": str(e)} + return {"ret": "error", "log": str(e)} def get_anime_info(self, cate, page): print(cate, page) @@ -1010,7 +1007,7 @@ class LogicOhli24(AnimeModuleBase): except Exception as e: P.logger.error("Exception:%s", e) P.logger.error(traceback.format_exc()) - return {"ret": "exception", "log": str(e)} + return {"ret": "error", "log": str(e)} def get_auto_anime_info(self, url: str = ""): try: @@ -1038,7 +1035,7 @@ class LogicOhli24(AnimeModuleBase): except Exception as e: P.logger.error("Exception:%s", e) P.logger.error(traceback.format_exc()) - return {"ret": "exception", "log": str(e)} + return {"ret": "error", "log": str(e)} # @staticmethod def get_search_result(self, query, page, cate): @@ -1089,7 +1086,7 @@ class LogicOhli24(AnimeModuleBase): except Exception as e: P.logger.error(f"Exception: {str(e)}") P.logger.error(traceback.format_exc()) - return {"ret": "exception", "log": str(e)} + return {"ret": "error", "log": str(e)} def process_api(self, sub: str, req: Any) -> Any: try: @@ -1596,18 +1593,61 @@ class Ohli24QueueEntity(AnimeQueueEntity): self.epi_queue: Optional[str] = None self.filepath: Optional[str] = None self.savepath: Optional[str] = None - self.quality: Optional[str] = None + self.quality: Optional[str] = "720P" self.filename: Optional[str] = None self.vtt: Optional[str] = None self.season: int = 1 self.content_title: Optional[str] = None self.srt_url: Optional[str] = None self.headers: Optional[Dict[str, str]] = None - self.cookies_file: Optional[str] = None # yt-dlp์šฉ CDN ์„ธ์…˜ ์ฟ ํ‚ค ํŒŒ์ผ ๊ฒฝ๋กœ - self.need_special_downloader: bool = False # CDN ๋ณด์•ˆ ์šฐํšŒ ๋‹ค์šด๋กœ๋” ํ•„์š” ์—ฌ๋ถ€ - self._discord_sent: bool = False # Discord ์•Œ๋ฆผ ๋ฐœ์†ก ์—ฌ๋ถ€ - # [Lazy Extraction] __init__์—์„œ๋Š” ๋ฌด๊ฑฐ์šด ๋ถ„์„์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - # self.make_episode_info() + self.cookies_file: Optional[str] = None + self.need_special_downloader: bool = False + self._discord_sent: bool = False + + # [Early Extraction] Parse basic metadata immediately so DB append has data + self.parse_metadata() + + def parse_metadata(self) -> None: + """Extract basic info from title for early DB population.""" + try: + title_full = self.info.get("title", "") + if not title_full: + return + + match = re.compile(r"(?P.*?)\s*((?P<season>\d+)๊ธฐ)?\s*((?P<epi_no>\d+)ํ™”)").search(title_full) + if match: + self.content_title = match.group("title").strip() + if match.group("season"): + self.season = int(match.group("season")) + self.epi_queue = int(match.group("epi_no")) + else: + self.content_title = title_full + self.epi_queue = 1 + + # Predict initial filename/filepath for UI + epi_no = self.epi_queue + ret = "%s.S%sE%s.%s-OHNI24.mp4" % ( + self.content_title, + "0%s" % self.season if self.season < 10 else self.season, + "0%s" % epi_no if epi_no < 10 else epi_no, + self.quality, + ) + self.filename = Util.change_text_for_use_filename(ret) + + # Savepath + self.savepath = P.ModelSetting.get("ohli24_download_path") + if P.ModelSetting.get_bool("ohli24_auto_make_folder"): + folder_name = self.content_title + if self.info.get("day", "").find("์™„๊ฒฐ") != -1: + folder_name = "%s %s" % (P.ModelSetting.get("ohli24_finished_insert"), self.content_title) + folder_name = Util.change_text_for_use_filename(folder_name.strip()) + self.savepath = os.path.join(self.savepath, folder_name) + if P.ModelSetting.get_bool("ohli24_auto_make_season_folder"): + self.savepath = os.path.join(self.savepath, "Season %s" % int(self.season)) + + self.filepath = os.path.join(self.savepath, self.filename) + except Exception as e: + logger.error(f"Error in parse_metadata: {e}") def refresh_status(self) -> None: @@ -1700,43 +1740,21 @@ class Ohli24QueueEntity(AnimeQueueEntity): # ------------------------------------------------------------------ # [METADATA PARSING] - Extract title, season, epi info first! # ------------------------------------------------------------------ - # ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ ๋จผ์ € ํŒŒ์‹ฑ (ํŒŒ์ผ๋ช… ์ƒ์„ฑ์€ ํ•ด์ƒ๋„ ๊ฐ์ง€ ํ›„ ์ง„ํ–‰) - match = re.compile(r"(?P<title>.*?)\s*((?P<season>\d+)%s)?\s*((?P<epi_no>\d+)%s)" % ("๊ธฐ", "ํ™”")).search( - self.info["title"] - ) - - epi_no = 1 - self.quality = "720P" # ๊ธฐ๋ณธ๊ฐ’ (ํ•ด์ƒ๋„ ๊ฐ์ง€ ์‹œ ๋ฎ์–ด์“ฐ๊ธฐ) - - if match: - self.content_title = match.group("title").strip() - if "season" in match.groupdict() and match.group("season") is not None: - self.season = int(match.group("season")) - - epi_no = int(match.group("epi_no")) - else: - self.content_title = self.info["title"] - logger.debug("NOT MATCH") - - self.epi_queue = epi_no - # NOTE: ํŒŒ์ผ๋ช…์€ ํ•ด์ƒ๋„ ๊ฐ์ง€ ํ›„ ์ƒ์„ฑ (์•„๋ž˜ Step 2 ์ดํ›„) - - # Savepath ์ƒ์„ฑ (filepath๋Š” ํŒŒ์ผ๋ช… ์ƒ์„ฑ ํ›„ ์„ค์ •) - self.savepath = P.ModelSetting.get("ohli24_download_path") - - if P.ModelSetting.get_bool("ohli24_auto_make_folder"): - if self.info["day"].find("์™„๊ฒฐ") != -1: - folder_name = "%s %s" % ( - P.ModelSetting.get("ohli24_finished_insert"), - self.content_title, - ) - else: - folder_name = self.content_title - folder_name = Util.change_text_for_use_filename(folder_name.strip()) - self.savepath = os.path.join(self.savepath, folder_name) - if P.ModelSetting.get_bool("ohli24_auto_make_season_folder"): - self.savepath = os.path.join(self.savepath, "Season %s" % int(self.season)) - # NOTE: self.filepath๋Š” ํŒŒ์ผ๋ช… ์ƒ์„ฑ ํ›„ ์„ค์ • (Step 2 ์ดํ›„) + # [IMMEDIATE SYNC] Update DB with extracted metadata + try: + db_entity = ModelOhli24Item.get_by_ohli24_id(self.info["_id"]) + if db_entity: + logger.debug(f"[SYNC] Syncing metadata for Ohli24 _id: {self.info.get('_id')}") + db_entity.title = self.content_title + db_entity.season = self.season + db_entity.episode_no = self.epi_queue + db_entity.savepath = self.savepath + db_entity.filename = self.filename + db_entity.filepath = self.filepath + db_entity.save() + except Exception as sync_err: + logger.error(f"Failed to sync metadata to DB: {sync_err}") + if not os.path.exists(self.savepath): os.makedirs(self.savepath) logger.info(f"self.savepath::> {self.savepath}") @@ -1799,7 +1817,8 @@ class Ohli24QueueEntity(AnimeQueueEntity): logger.info(f"Quality set from m3u8: {self.quality}") # [FILENAME GENERATION] - ํ•ด์ƒ๋„ ๊ฐ์ง€ ํ›„ ํŒŒ์ผ๋ช… ์ƒ์„ฑ - if hasattr(self, 'epi_queue'): + # [FILENAME GENERATION] - Re-generate filename after quality detection + if self.epi_queue: epi_no = self.epi_queue ret = "%s.S%sE%s.%s-OHNI24.mp4" % ( self.content_title, @@ -1811,7 +1830,6 @@ class Ohli24QueueEntity(AnimeQueueEntity): self.filepath = os.path.join(self.savepath, self.filename) # [NFD CHECK] Mac/Docker Compatibility - # If NFC (Python standard) file doesn't exist, check NFD (Mac filesystem standard) if not os.path.exists(self.filepath): nfd_filename = unicodedata.normalize('NFD', self.filename) nfd_filepath = os.path.join(self.savepath, nfd_filename) @@ -1820,6 +1838,17 @@ class Ohli24QueueEntity(AnimeQueueEntity): self.filename = nfd_filename self.filepath = nfd_filepath + # [IMMEDIATE SYNC 2] Update filename/filepath after resolution detection + try: + db_entity = ModelOhli24Item.get_by_ohli24_id(self.info["_id"]) + if db_entity: + db_entity.quality = self.quality + db_entity.filename = self.filename + db_entity.filepath = self.filepath + db_entity.save() + except: + pass + logger.info(f"self.filename::> {self.filename}") if not video_url: diff --git a/model_base.py b/model_base.py index 864d7ca..f7dfe27 100644 --- a/model_base.py +++ b/model_base.py @@ -150,7 +150,13 @@ class AnimeQueueEntity(FfmpegQueueEntity): # Anilife uses _id if hasattr(model_class, 'get_by_anilife_id') and info.get('_id'): db_entity = model_class.get_by_anilife_id(info['_id']) - # Linkkf/Ohli24 might use different identifiers + # Ohli24 uses _id (via get_by_ohli24_id) + elif hasattr(model_class, 'get_by_ohli24_id') and info.get('_id'): + db_entity = model_class.get_by_ohli24_id(info['_id']) + # Linkkf uses _id (via get_by_linkkf_id) + elif hasattr(model_class, 'get_by_linkkf_id') and info.get('_id'): + db_entity = model_class.get_by_linkkf_id(info['_id']) + # Other modules might use get_by_id with db_id elif hasattr(model_class, 'get_by_id') and info.get('db_id'): db_entity = model_class.get_by_id(info['db_id']) elif hasattr(model_class, 'get_by_content_code') and info.get('content_code'): diff --git a/static/css/ohli24.css b/static/css/ohli24.css index 9c99c85..95b1ade 100644 --- a/static/css/ohli24.css +++ b/static/css/ohli24.css @@ -1,7 +1,7 @@ /* Ohli24 Theme Variables & Overrides */ :root { --slate-950: #0f172a; - --slate-900: #0f172a; /* Same as 950 for deep background */ + --slate-900: #0f172a; --slate-800: #1e293b; --slate-700: #334155; --blue-500: #3b82f6; @@ -23,17 +23,253 @@ body { background-attachment: fixed !important; } -/* Ohli24 Specific Nav-Pills Overrides */ +/* General Layout Fixes */ +.container-fluid { + padding-left: 8px !important; + padding-right: 8px !important; + max-width: 100%; +} + +/* Nav Pills Customization */ ul.nav.nav-pills.bg-light { background-color: rgba(30, 41, 59, 0.6) !important; - border: 1px solid rgba(255, 255, 255, 0.08) !important; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 50rem !important; + padding: 6px !important; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important; + display: inline-flex !important; + flex-wrap: wrap; + justify-content: center; + width: auto !important; + margin-bottom: 20px; } ul.nav.nav-pills .nav-link { color: #94a3b8 !important; + border-radius: 50rem !important; + padding: 8px 20px !important; } ul.nav.nav-pills .nav-link.active { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; + color: #fff !important; box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4) !important; } + +/* Search Bar Styling */ +.search-container { + background: rgba(30, 41, 59, 0.7); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 12px; + padding: 12px; + margin-bottom: 20px; + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.custom-select, .custom-input { + background-color: rgba(15, 23, 42, 0.6) !important; + border: 1px solid rgba(148, 163, 184, 0.2) !important; + color: #e2e8f0 !important; + border-radius: 8px !important; + height: 38px !important; +} + +.custom-btn { + border-radius: 8px !important; + padding: 0 16px !important; + height: 38px !important; + font-weight: 600 !important; + border: none !important; +} + +.btn-search { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); color: white !important; } +.btn-reset { background: rgba(148, 163, 184, 0.1); color: #94a3b8 !important; } + +/* Episode Card (Mobile-First) - Scoped to List Page */ +.ohli24-list-page .episode-card { + display: flex; + flex-direction: column; + background: linear-gradient(135deg, rgba(30, 41, 59, 0.85) 0%, rgba(15, 23, 42, 0.85) 100%); + border-radius: 12px; + border: 1px solid rgba(148, 163, 184, 0.12); + margin-bottom: 12px; + transition: all 0.2s ease; + overflow: hidden; +} + +.ohli24-list-page .episode-card-body { + display: flex; + flex-wrap: wrap; /* Key for mobile stacking */ + padding: 12px; + width: 100%; + gap: 12px; +} + +.ohli24-list-page .episode-thumb { + width: 60px; + height: 80px; + flex: 0 0 60px; + border-radius: 8px; + overflow: hidden; + background-color: #1e293b; + position: relative; + box-shadow: 0 4px 10px rgba(0,0,0,0.3); +} + +.ohli24-list-page .episode-thumb img { width: 100%; height: 100%; object-fit: cover; } + +.ohli24-list-page .episode-main-info { + flex: 1; + min-width: calc(100% - 84px); /* Force wrapping if needed, but allow sitting next to thumb */ + display: flex; + flex-direction: column; + justify-content: center; +} + +.ohli24-list-page .episode-title { + color: #f1f5f9; + font-weight: 700; + font-size: 15px; + margin-bottom: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ohli24-list-page .episode-subtitle { + font-size: 11px; + color: #94a3b8; + margin-bottom: 6px; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ohli24-list-page .episode-meta { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; + margin-bottom: 6px; +} + +/* Episode Right Column / Actions - Wraps to 100% on mobile */ +.ohli24-list-page .episode-right-col { + flex: 0 0 100%; /* Full width on mobile */ + margin-top: 4px; + padding-top: 12px; + border-top: 1px solid rgba(255, 255, 255, 0.06); +} + +.ohli24-list-page .episode-actions { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 6px; +} + +.ohli24-list-page .episode-actions .btn { + font-size: 12px !important; + padding: 8px 6px !important; + text-align: center; + border-radius: 6px !important; + white-space: nowrap; +} + +/* Status Badges */ +.status-badge { font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: 4px; display: inline-block; } +.status-completed { background: rgba(16, 185, 129, 0.2); color: #34d399; } +.status-wait { background: rgba(245, 158, 11, 0.2); color: #fbbf24; } + +/* Desktop Adaptations - Scoped to List Page */ +@media (min-width: 768px) { + .ohli24-list-page .episode-card { margin-bottom: 10px; } + + .ohli24-list-page .episode-card-body { + flex-direction: row; + align-items: center; + padding: 10px 20px; + gap: 20px; + } + + .ohli24-list-page .episode-top-info { + flex: 1; + align-items: center; + min-width: 0; + } + + .ohli24-list-page .episode-thumb { width: 60px; height: 80px; } + + .ohli24-list-page .episode-main-info { justify-content: center; } + + .ohli24-list-page .episode-title { font-size: 16px; margin-bottom: 2px; } + + .ohli24-list-page .file-path { margin-top: 2px; padding: 4px 8px; display: inline-block; max-width: 100%; } + + .ohli24-list-page .episode-right-col { + width: auto; + margin-top: 0; + padding-top: 0; + border-top: none; + padding-left: 20px; + display: flex; + align-items: center; + min-width: 320px; + } + + .ohli24-list-page .episode-actions { display: flex; flex-wrap: nowrap; gap: 6px; } + .ohli24-list-page .episode-actions .btn { flex: none; width: auto; min-width: 80px; font-size: 11px !important; } +} + +/* Queue Specific Styles - Scoped to Queue Page */ +.ohli24-queue-page .queue-header { + background: linear-gradient(135deg, rgba(30, 41, 59, 0.9) 0%, rgba(15, 23, 42, 0.95) 100%); + border-radius: 12px; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.ohli24-queue-page .queue-item { + background: linear-gradient(135deg, rgba(30, 41, 59, 0.85) 0%, rgba(15, 23, 42, 0.85) 100%); + border-radius: 10px; + border: 1px solid rgba(148, 163, 184, 0.12); + padding: 15px; + display: flex; + align-items: center; + gap: 15px; +} + +.ohli24-queue-page .progress-wrapper { + position: relative; + height: 32px; + background: rgba(0, 0, 0, 0.3); + border-radius: 16px; + overflow: hidden; + width: 300px; +} + +.ohli24-queue-page .progress-bar { height: 100%; transition: width 0.3s ease; } +.ohli24-queue-page .status-waiting { background: linear-gradient(90deg, #94a3b8, #64748b); } +.ohli24-queue-page .status-downloading { background: linear-gradient(90deg, #3b82f6, #60a5fa); } +.ohli24-queue-page .status-completed-bar { background: linear-gradient(90deg, #22c55e, #4ade80); } + +/* Common Modal Fixes */ +.modal-content { + background: rgba(15, 23, 42, 0.95) !important; + border: 1px solid rgba(148, 163, 184, 0.2) !important; + border-radius: 12px !important; +} + +@media (max-width: 768px) { + .ohli24-queue-page .progress-wrapper { width: 100%; } + .ohli24-queue-page .queue-item { flex-direction: column; align-items: stretch; } + .ohli24-queue-page ul.nav.nav-pills.bg-light { margin-top: 40px !important; } +} diff --git a/templates/anime_downloader_ohli24_list.html b/templates/anime_downloader_ohli24_list.html index 38cf9a3..b7d4d94 100644 --- a/templates/anime_downloader_ohli24_list.html +++ b/templates/anime_downloader_ohli24_list.html @@ -4,7 +4,7 @@ <link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/> - <div class="content-cloak"> + <div class="content-cloak ohli24-list-page"> <form id="form_search" class="form-inline" style="text-align:left; width:100%;"> <div class="search-container"> <div class="search-group-left"> @@ -98,9 +98,7 @@ </div> </div> </div> - - <style> -</style> + </div> </div> </div> @@ -513,513 +511,27 @@ </script> - /* General Layout Tweaks */ - .container-fluid { - padding-left: 4px !important; - padding-right: 4px !important; - max-width: 100%; - margin: 0 auto; - } - - .content-cloak { - padding-top: 10px; - } - - /* Search Bar Design */ - .search-container { - background: rgba(30, 41, 59, 0.7); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 12px; - padding: 10px; - margin-bottom: 20px; - display: flex; - flex-wrap: wrap; - gap: 10px; - align-items: center; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); - } - - .search-group-left { - display: flex; - gap: 8px; - flex: 0 0 auto; - } - - .search-group-right { - display: flex; - gap: 8px; - flex: 1; - } - - /* Custom Form Controls */ - .custom-select, .custom-input { - background-color: rgba(15, 23, 42, 0.6) !important; - border: 1px solid rgba(148, 163, 184, 0.2) !important; - color: #e2e8f0 !important; - border-radius: 8px !important; - padding: 6px 12px !important; - height: 38px !important; - font-size: 13px !important; - transition: all 0.2s; - } - - .custom-select:focus, .custom-input:focus { - border-color: #3b82f6 !important; - box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2) !important; - outline: none; - } - - .custom-btn { - border-radius: 8px !important; - padding: 0 16px !important; - height: 38px !important; - font-weight: 600 !important; - display: flex; - align-items: center; - gap: 6px; - transition: all 0.2s; - border: none !important; - } - - .btn-search { - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); - color: white !important; - } - .btn-search:hover { box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3); transform: translateY(-1px); } - - .btn-reset { - background: rgba(148, 163, 184, 0.1); - color: #94a3b8 !important; - } - .btn-reset:hover { background: rgba(148, 163, 184, 0.2); color: white !important; } - - /* List Container - List View */ - #list_div { - display: flex; - flex-direction: column; - gap: 8px; - padding: 0 5px; - width: 100%; - } - - /* Episode Card Style */ - .episode-card { - display: flex; - flex-direction: column; - background: linear-gradient(135deg, rgba(30, 41, 59, 0.85) 0%, rgba(15, 23, 42, 0.85) 100%); - border-radius: 12px; - border: 1px solid rgba(148, 163, 184, 0.12); - overflow: hidden; - transition: all 0.2s ease; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); - position: relative; - } - - .episode-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); - border-color: rgba(96, 165, 250, 0.4); - background: linear-gradient(135deg, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.95) 100%); - z-index: 10; - } - - .episode-card-body { - padding: 10px; - display: flex; - flex-direction: row; /* Mobile Default: Row for proper structure */ - flex-wrap: wrap; /* Allow wrapping on small mobile */ - width: 100%; - } - - /* Inner Components */ - .episode-thumb { - width: 60px; - height: 80px; - flex-shrink: 0; - margin-right: 12px; - border-radius: 8px; - overflow: hidden; - background-color: #1e293b; - } - - .episode-thumb img { - width: 100%; - height: 100%; - object-fit: cover; - } - - .episode-badge { - position: absolute; - top: 5px; - left: 5px; - background: #facc15; /* Amber/Yellow 400 */ - color: #000; - padding: 2px 6px; - border-radius: 4px; - font-size: 11px; - font-weight: 800; - z-index: 5; - box-shadow: 0 2px 4px rgba(0,0,0,0.3); - border: 1px solid rgba(0,0,0,0.1); - pointer-events: none; - text-transform: uppercase; - } - - .episode-main-info { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - justify-content: center; - } - - .episode-title { - color: #f1f5f9; - font-weight: 600; - font-size: 14px; - line-height: 1.4; - margin-bottom: 2px; - word-break: break-all; - } - - .episode-subtitle { - font-size: 12px; - color: #94a3b8; - margin-bottom: 4px; - line-height: 1.3; - display: block; - } - - .episode-meta { - font-size: 11px; - color: #94a3b8; - display: flex; - flex-wrap: wrap; - gap: 6px; - align-items: center; - margin-bottom: 4px; - } - - .meta-tag { - background: rgba(148, 163, 184, 0.1); - padding: 1px 5px; - border-radius: 4px; - color: #cbd5e1; - } - - .status-badge { - font-size: 10px; - font-weight: 700; - padding: 2px 5px; - border-radius: 4px; - text-transform: uppercase; - min-width: 60px; - text-align: center; - } - .status-completed { background: rgba(16, 185, 129, 0.2); color: #34d399; } - .status-wait { background: rgba(245, 158, 11, 0.2); color: #fbbf24; } - .status-downloading { background: rgba(59, 130, 246, 0.2); color: #60a5fa; } - .status-failed { background: rgba(239, 68, 68, 0.2); color: #f87171; } - - .file-path { - font-size: 11px; - color: #64748b; - word-break: break-all; - margin-top: 2px; - display: block; - } - - /* Right Column (Mobile) */ - .episode-right-col { - width: 100%; - margin-top: 10px; - padding-top: 10px; - border-top: 1px solid rgba(255, 255, 255, 0.05); - display: flex; - flex-direction: column; - gap: 8px; - } - - .date-info { - font-size: 11px; - color: #94a3b8; - display: flex; - gap: 10px; - } - - .episode-actions { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 5px; - } - - .episode-actions .btn { - font-size: 11px !important; - padding: 4px 8px !important; - white-space: nowrap; - width: 100%; - } - - /* Desktop List View Transformation */ - @media (min-width: 768px) { - .episode-card { - flex-direction: row; - align-items: center; - padding: 10px; - min-height: 80px; - } - - .episode-card-body { - flex-wrap: nowrap; - align-items: stretch; - padding: 0; - width: 100%; - } - - .episode-thumb { - margin-right: 15px; - margin-bottom: 0; - } - - .episode-main-info { - margin-bottom: 0; - justify-content: center; - } - - .episode-title { - font-size: 15px; - word-break: keep-all; - } - - .episode-right-col { - width: auto; - margin-top: 0; - padding-top: 0; - border-top: none; - margin-left: auto; /* Push to right */ - align-items: flex-end; - justify-content: center; - min-width: 220px; - } - - .date-info { - justify-content: flex-end; - margin-bottom: 5px; - order: -1; - } - - .episode-actions { - display: flex; - flex-direction: row; - gap: 5px; - } - - .episode-actions .btn { - width: auto; - } - - .episode-thumb img { - width: 100%; - height: 100%; - object-fit: cover; - } - } - - /* Hide Original Headers */ - .show-grid { margin-bottom: 20px; } - .container-fluid > div:nth-child(3), /* Head Top HR */ - .container-fluid > div:nth-child(4), /* Headers Row */ - .container-fluid > div:nth-child(5) /* Head Bottom HR */ - { - display: none !important; - } -</style> - -<script type="text/javascript"> -$(document).ready(function(){ - // Smooth Load Trigger - setTimeout(function() { - $('.content-cloak, #menu_module_div, #menu_page_div').addClass('visible'); - }, 100); -}); -</script> <style> -/* Desktop Full Width Override - .container max-width ์ œ๊ฑฐ */ -#main_container.container { - max-width: 100% !important; - padding-left: 8px !important; - padding-right: 8px !important; -} - -/* Mobile Margin Fix */ -@media (max-width: 768px) { - body { overflow-x: hidden !important; padding: 0 !important; margin: 0 !important; } - .container, .container-fluid, .row, form, #program_list, #program_auto_form, #episode_list, .queue-container, #yommi_wrapper, #main_container { - width: 100% !important; max-width: 100% !important; - padding-left: 4px !important; padding-right: 4px !important; - margin-left: 0 !important; margin-right: 0 !important; - box-sizing: border-box !important; - } - .form-group, .form-inline, [class*="col-"] { - flex: 0 0 100% !important; max-width: 100% !important; width: 100% !important; - padding-left: 0 !important; padding-right: 0 !important; - } - .row { margin-left: 0 !important; margin-right: 0 !important; } - .card, .card.p-4, .card.p-lg-5, .card.border-light { - width: calc(100% - 8px) !important; max-width: 100% !important; - padding: 8px !important; margin: 4px !important; - border-radius: 12px !important; box-sizing: border-box !important; - } - .badge { - white-space: normal !important; text-align: left !important; - line-height: 1.4 !important; height: auto !important; display: inline-block !important; - } - - /* Mobile Nav Pills - blur ์ œ๊ฑฐํ•˜๊ณ  solid background ์‚ฌ์šฉ */ - ul.nav.nav-pills.bg-light { - background-color: #1e293b !important; - backdrop-filter: none !important; - -webkit-backdrop-filter: none !important; - border-radius: 12px !important; - padding: 4px !important; - flex-wrap: wrap; - gap: 4px; - } - - ul.nav.nav-pills .nav-link { - padding: 6px 12px !important; - font-size: 12px !important; - } -} - /* Pagination Styling */ - .btn-toolbar { - justify-content: center; - margin: 20px 0; - } - + /* Pagination & Modal Fixes (Moved to template for specific overrides) */ + .btn-toolbar { justify-content: center; margin: 20px 0; } #page1 .btn-group .btn, #page2 .btn-group .btn { background: rgba(30, 41, 59, 0.6); border: 1px solid rgba(255, 255, 255, 0.1); - color: #94a3b8; - padding: 6px 14px; - margin: 0 2px; - border-radius: 6px; - transition: all 0.2s; + color: #94a3b8; padding: 6px 14px; margin: 0 2px; border-radius: 6px; } - - #page1 .btn-group .btn:hover, #page2 .btn-group .btn:hover { - background: rgba(255, 255, 255, 0.1); - color: white; - transform: translateY(-1px); - } - #page1 .btn-group .btn[disabled], #page2 .btn-group .btn[disabled] { - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); - color: white; - border-color: transparent; - opacity: 1; - box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3); - } - - /* JSON Modal ํŒ์—… ํญ ํ™•๋Œ€ */ - .modal-dialog { - max-width: 90% !important; - width: 900px !important; + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); color: white; border-color: transparent; } - .modal-content { - background: rgba(15, 23, 42, 0.95) !important; - border: 1px solid rgba(148, 163, 184, 0.2) !important; - border-radius: 12px !important; - } - - .modal-body pre { - background: rgba(0, 0, 0, 0.5) !important; - color: #4ade80 !important; - padding: 15px !important; - border-radius: 8px !important; - max-height: 70vh !important; - overflow: auto !important; - font-size: 12px !important; - } + .modal-dialog { max-width: 95% !important; width: 900px !important; margin: 10px auto; } + .modal-body pre { background: #000 !important; color: #4ade80 !important; padding: 15px !important; border-radius: 8px !important; max-height: 70vh !important; font-size: 12px !important; } - /* ========== Mobile Video Modal Fix ========== */ @media (max-width: 768px) { - /* ๋ชจ๋‹ฌ ํฌ๊ธฐ ์กฐ์ • - ๋น„๋””์˜ค ๋ชจ๋‹ฌ๋งŒ */ - #videoModal .modal-dialog { - margin: 10px auto !important; - max-width: calc(100vw - 20px) !important; - width: auto !important; - } - - /* ๋น„๋””์˜ค ๋†’์ด ์ œํ•œ */ - #video-player, .video-js { - max-height: 45vh !important; - } - - /* ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ๋†’์ด ์ œํ•œ */ - #playlist-list-container { - max-height: 25vh !important; - } - - /* ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์ปจํŠธ๋กค ๊ฐ„์†Œํ™” */ - .playlist-controls { - padding: 8px 12px !important; - } - .playlist-controls > div { - gap: 8px !important; - } - #current-video-title { - font-size: 12px !important; - min-width: auto !important; - } - #playlist-progress { - font-size: 10px !important; - } - - /* ์•„์ดํ…œ ์•ก์…˜ ๋ฒ„ํŠผ ์Šคํฌ๋กค */ - .item-actions { - flex-wrap: nowrap !important; - overflow-x: auto !important; - -webkit-overflow-scrolling: touch; - padding-bottom: 4px; - } - .action-btn { - flex-shrink: 0; - } + #videoModal .modal-dialog { width: 100% !important; margin: 5px !important; } + .episode-actions { grid-template-columns: repeat(2, 1fr); gap: 6px; } + .episode-card { padding: 10px; } + .episode-thumb { width: 50px; height: 70px; } } - - /* ์ดˆ์†Œํ˜• ๋ชจ๋ฐ”์ผ (400px ๋ฏธ๋งŒ) */ - @media (max-width: 400px) { - #video-player, .video-js { - max-height: 35vh !important; - } - - #playlist-list-container { - max-height: 20vh !important; - } - - .playlist-nav-btn { - width: 32px !important; - height: 32px !important; - font-size: 12px !important; - } - - .playlist-toggle-btn { - padding: 6px 10px !important; - font-size: 11px !important; - } - } - - /* Mobile Nav Pills Fix */ - @media (max-width: 768px) { - ul.nav.nav-pills.bg-light { - margin-top: 50px !important; - margin-bottom: 10px !important; - } - } - </style> {% endblock %} \ No newline at end of file diff --git a/templates/anime_downloader_ohli24_queue.html b/templates/anime_downloader_ohli24_queue.html index 5e993b4..9b90cac 100644 --- a/templates/anime_downloader_ohli24_queue.html +++ b/templates/anime_downloader_ohli24_queue.html @@ -4,7 +4,7 @@ <link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/> -<div class="queue-container content-cloak"> +<div class="queue-container content-cloak ohli24-queue-page"> <!-- ํ—ค๋” ๋ฒ„ํŠผ ๊ทธ๋ฃน --> <div class="queue-header"> <div class="header-title"> @@ -36,390 +36,15 @@ </div> <style> - - /* ์ปจํ…Œ์ด๋„ˆ */ - .queue-container { - max-width: 100%; - margin: 0; - padding: 0; - } - - /* ํ—ค๋” */ - .queue-header { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 15px; - margin-bottom: 20px; - padding: 15px 20px; - background: linear-gradient(135deg, rgba(30, 41, 59, 0.9) 0%, rgba(15, 23, 42, 0.95) 100%); - border-radius: 12px; - border: 1px solid rgba(148, 163, 184, 0.15); - } - - .header-title { - display: flex; - align-items: center; - gap: 10px; - color: #e2e8f0; - font-size: 18px; - font-weight: 600; - } - - .header-title i { - color: #60a5fa; - } - - .queue-count { - background: linear-gradient(135deg, #3b82f6, #2563eb); - color: white; - padding: 2px 10px; - border-radius: 12px; - font-size: 13px; - font-weight: 600; - } - - .header-buttons { - display: flex; - gap: 8px; - flex-wrap: wrap; - } - - .queue-btn { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 14px; - border: none; - border-radius: 8px; - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s ease; - } - - .queue-btn-danger { - background: linear-gradient(135deg, #ef4444, #dc2626); - color: white; - } - - .queue-btn-danger:hover { - background: linear-gradient(135deg, #dc2626, #b91c1c); - transform: translateY(-1px); - } - - .queue-btn-warning { - background: linear-gradient(135deg, #f59e0b, #d97706); - color: white; - } - - .queue-btn-warning:hover { - background: linear-gradient(135deg, #d97706, #b45309); - transform: translateY(-1px); - } - - .queue-btn-info { - background: linear-gradient(135deg, #0ea5e9, #0284c7); - color: white; - } - - .queue-btn-info:hover { - background: linear-gradient(135deg, #0284c7, #0369a1); - transform: translateY(-1px); - } - - /* ๋‹ค์šด๋กœ๋“œ ๋ชฉ๋ก */ - .queue-list { - display: flex; - flex-direction: column; - gap: 10px; - } - - /* ๋‹ค์šด๋กœ๋“œ ์•„์ดํ…œ ์นด๋“œ */ - .queue-item { - display: flex; - align-items: center; - gap: 15px; - padding: 15px; - background: linear-gradient(135deg, rgba(30, 41, 59, 0.85) 0%, rgba(15, 23, 42, 0.85) 100%); - border-radius: 10px; - border: 1px solid rgba(148, 163, 184, 0.12); - transition: all 0.2s ease; - } - - .queue-item:hover { - border-color: rgba(96, 165, 250, 0.4); - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); - } - - /* ์•„์ดํ…œ ๋ฒˆํ˜ธ */ - .item-number { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - min-width: 36px; - height: 36px; - background: linear-gradient(135deg, #4f46e5, #6366f1); - color: white; - font-weight: 600; - font-size: 12px; - border-radius: 8px; - } - - /* ์•„์ดํ…œ ์ •๋ณด */ - .item-info { - flex: 1; - min-width: 0; - } - - .item-filename { - color: #fbbf24; - font-weight: 600; - font-size: 15px; - margin-bottom: 4px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); - } - - .item-meta { - display: flex; - align-items: center; - gap: 12px; - font-size: 12px; - color: #94a3b8; - } - - .item-time { - display: flex; - align-items: center; - gap: 4px; - } - - /* ์ง„ํ–‰๋ฅ  ๋ฐ” */ - .progress-container { - flex: 0 0 350px; - min-width: 200px; - } - - .progress-wrapper { - position: relative; - height: 32px; - background: rgba(0, 0, 0, 0.3); - border-radius: 16px; - overflow: hidden; - } - - .progress-bar { - height: 100%; - border-radius: 12px; - transition: width 0.3s ease; - } - - .progress-bar.status-waiting { - background: linear-gradient(90deg, #94a3b8, #64748b); - } - - .progress-bar.status-downloading { - background: linear-gradient(90deg, #3b82f6, #60a5fa); - } - - .progress-bar.status-completed { - background: linear-gradient(90deg, #22c55e, #4ade80); - } - - .progress-bar.status-failed { - background: linear-gradient(90deg, #ef4444, #f87171); - } - - .progress-label { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - font-size: 13px; - font-weight: 600; - color: white; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); - } - - /* ์•ก์…˜ ๋ฒ„ํŠผ */ - .item-actions { - display: flex; - gap: 6px; - } - - .cancel-btn { - padding: 6px 12px; - background: rgba(239, 68, 68, 0.2); - border: 1px solid rgba(239, 68, 68, 0.4); - color: #f87171; - border-radius: 6px; - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s ease; - } - - .cancel-btn:hover { - background: rgba(239, 68, 68, 0.3); - border-color: #ef4444; - } - - /* ๋นˆ ์ƒํƒœ */ - .empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 60px 20px; - color: #64748b; - } - - .empty-state i { - font-size: 48px; - margin-bottom: 15px; - opacity: 0.5; - } - - .empty-state p { - font-size: 15px; - margin: 0; - } - - /* ๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜• */ + /* Queue specific tweaks */ + #download_list_div { display: flex; flex-direction: column; gap: 10px; width: 100%; } + .queue-item { margin-bottom: 5px; } @media (max-width: 768px) { - .queue-container { - padding: 10px; - } - - .queue-header { - flex-direction: column; - align-items: stretch; - gap: 12px; - padding: 12px 15px; - } - - .header-title { - justify-content: center; - } - - .header-buttons { - justify-content: center; - } - - .queue-btn { - padding: 6px 10px; - font-size: 12px; - } - - .queue-item { - flex-wrap: wrap; - gap: 10px; - padding: 12px; - } - - .item-number { - width: 30px; - min-width: 30px; - height: 30px; - font-size: 11px; - } - - .item-info { - flex: 1 1 calc(100% - 50px); - order: 1; - } - - .item-filename { - font-size: 13px; - } - - .progress-container { - flex: 1 1 100%; - order: 3; - } - - .item-actions { - order: 2; - } + .queue-item { padding: 10px; } + .item-info { flex: 1; } + .progress-wrapper { height: 24px; border-radius: 12px; } + .progress-label { font-size: 11px; } } - - @media (max-width: 400px) { - .queue-btn span { - display: none; - } - - .queue-btn i { - margin: 0; - } - - .header-title span:not(.queue-count) { - font-size: 15px; - } - } - - /* ๋กœ๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ ์˜ค๋ฒ„๋ผ์ด๋“œ */ - #loading { - background: rgba(15, 23, 42, 0.85) !important; - backdrop-filter: blur(8px) !important; - } - - #loading img, - #loading svg { - display: none !important; - visibility: hidden !important; - width: 0 !important; - height: 0 !important; - } - - #loading::before { - content: ''; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 50px; - height: 50px; - border: 4px solid rgba(96, 165, 250, 0.2); - border-top-color: #60a5fa; - border-radius: 50%; - animation: spin 0.8s linear infinite; - z-index: 100001; - } - - @keyframes spin { - to { transform: translate(-50%, -50%) rotate(360deg); } - } - - #modal_loading img, - #modal_loading svg { - display: none !important; - visibility: hidden !important; - width: 0 !important; - height: 0 !important; - } - - #modal_loading::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 40px; - height: 40px; - border-color: #60a5fa; - border-radius: 50%; - animation: spin 0.8s linear infinite; - } - </style> <script src="{{ url_for('.static', filename='js/sjva_ui14.js') }}"></script> @@ -598,73 +223,6 @@ }); </script> -<style> - /* Smooth Load Transition */ - .content-cloak, - #menu_module_div, - #menu_page_div { - opacity: 0; - transition: opacity 0.5s ease-out; - } - - /* Staggered Delays for Natural Top-Down Flow */ - #menu_module_div.visible { - opacity: 1; - transition-delay: 0ms; - } - - #menu_page_div.visible { - opacity: 1; - transition-delay: 150ms; - } - - .content-cloak.visible { - opacity: 1; - transition-delay: 300ms; - } - - /* Navigation Menu Override (Top Sub-menu) */ - ul.nav.nav-pills.bg-light { - background-color: rgba(30, 41, 59, 0.6) !important; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 50rem !important; - padding: 6px !important; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important; - display: inline-flex !important; - flex-wrap: wrap; - justify-content: center; - width: auto !important; - margin-bottom: 20px; - } - - ul.nav.nav-pills .nav-item { margin: 0 2px; } - ul.nav.nav-pills .nav-link { - border-radius: 50rem !important; - padding: 8px 20px !important; - color: #94a3b8 !important; - font-weight: 600; - transition: all 0.3s ease; - } - ul.nav.nav-pills .nav-link:hover { - background-color: rgba(255, 255, 255, 0.1); - color: #fff !important; - transform: translateY(-1px); - } - ul.nav.nav-pills .nav-link.active { - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; - color: #fff !important; - box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4); - } -</style> - -<script type="text/javascript"> -$(document).ready(function(){ - // Smooth Load Trigger - setTimeout(function() { - $('.content-cloak, #menu_module_div, #menu_page_div').addClass('visible'); - }, 100); -}); </script> {% endblock %} \ No newline at end of file diff --git a/templates/anime_downloader_ohli24_request.html b/templates/anime_downloader_ohli24_request.html index 9c4f384..cdc13ae 100644 --- a/templates/anime_downloader_ohli24_request.html +++ b/templates/anime_downloader_ohli24_request.html @@ -23,7 +23,7 @@ <!-- </div>--> </div> </div> - <div id="yommi_wrapper" class="container-fluid mt-2 mt-md-4 mx-auto" style="max-width: 100%;"> + <div id="yommi_wrapper" class="container-fluid ohli24-request-page mt-2 mt-md-4 mx-auto" style="max-width: 100%;"> <form id="program_list"> <div class="card p-2 p-md-4 mb-2 mb-md-4 border-light" style="background: rgba(30,30,40,0.6); backdrop-filter: blur(10px); box-shadow: 0 4px 6px rgba(0,0,0,0.1);"> <div class="form-group mb-0"> diff --git a/templates/anime_downloader_ohli24_search.html b/templates/anime_downloader_ohli24_search.html index 20aa075..725cd89 100644 --- a/templates/anime_downloader_ohli24_search.html +++ b/templates/anime_downloader_ohli24_search.html @@ -147,7 +147,7 @@ let current_screen_movie_data = ret // console.log('ret::>', ret) - if (current_screen_movie_data !== '') { + if (ret.ret === 'success') { if (type === "ing") { make_airing_list(ret.data, page) // observer.observe(); @@ -162,6 +162,8 @@ } // div_visible = true // // console.log(div_visible) + } else { + $.notify('<strong>๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.</strong><br>' + (ret.log || ''), {type: 'danger'}); } next_page = page + 1 } @@ -202,6 +204,11 @@ } function make_airing_list(data, page) { + if (!data || !data.anime_list) { + console.error("Invalid data received for airing list:", data); + $.notify('<strong>๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.</strong>', {type: 'danger'}); + return; + } let str = '' str += '<div class="d-flex justify-content-between align-items-center mb-3">'; @@ -237,6 +244,11 @@ } function make_search_result_list(data, page) { + if (!data || !data.anime_list) { + console.error("Invalid data received for search result list:", data); + $.notify('<strong>๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.</strong>', {type: 'danger'}); + return; + } let str = '' let list = data.anime_list || []; @@ -277,7 +289,11 @@ } function make_screen_movie_list(data, page) { - let str = ''; + if (!data || !data.anime_list) { + console.error("Invalid data received for screen movie list:", data); + return; + } + let str = ''; str += '<div class="d-flex justify-content-between align-items-center mb-3">'; let title = current_cate === 'movie' ? '๊ทน์žฅํŒ' : (current_cate === 'theater' ? '๊ทน์žฅํŒ(๊ตฌ)' : '์™„๊ฒฐ ์• ๋‹ˆ๋ฉ”์ด์…˜');