diff --git a/README.md b/README.md index 1993c64..fa8f76c 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,22 @@ ## π λ³κ²½ μ΄λ ₯ (Changelog) +### v0.5.1 (2026-01-04) +- **Ohli24 λ μ΄μμ νμ€ν**: + - λͺ¨λ Ohli24 νμ΄μ§(Setting, Search, Queue, List, Request)μ μΌκ΄λ 1400px max-width λ° μ€μ μ λ ¬ μ μ© + - `ohli24.css`μ κ³΅ν΅ wrapper(`.ohli24-common-wrapper`) λ° ν€λ μ€νμΌ μΆκ° + - List/Queue νμ΄μ§ `visible` ν΄λμ€ λλ½ μμ (content-cloak νΈλ¦¬κ±° μΆκ°) + - Request νμ΄μ§ μνΌμλ μΉ΄λ μμ°¨ λ λλ§ λ¬Έμ ν΄κ²° (`requestAnimationFrame` μ¬μ©) +- **Anilife ν΄λ°± μ²΄μΈ κ°μ **: + - `get_html` ν¨μμ **Zendriver subprocess ν΄λ°±** μΆκ° (Daemon μ€ν¨ μ μλ μ ν) + - Playwright ν΄λ°±μ **Camoufox**λ‘ λ³κ²½ (λ κ°λ ₯ν μν°λ΄ μ°ν) + - 3λ¨κ³ ν΄λ°±: Zendriver Daemon β Zendriver Subprocess β Camoufox +- **Anilife λ°μν λ μ΄μμ**: + - Request νμ΄μ§μ `anilife-common-wrapper` ν΄λμ€ μ μ© + - λͺ¨λ°μΌ(`<992px`): 100% λλΉ / λ°μ€ν¬ν(`β₯992px`): 1400px max-width +- **Linkkf CSS μ€λΉ**: + - `linkkf.css`μ κ³΅ν΅ wrapper μ€νμΌ μΆκ° (ν₯ν λ μ΄μμ νμ€ν λλΉ) + ### v0.5.0 (2026-01-03) - **Ohli24 λΉλμ€ νλ μ΄μ΄ UI μ λ©΄ κ°νΈ**: - **ν리미μ κΈλμ€λͺ¨νΌμ¦ λμμΈ**: νλ μ΄μ΄ λͺ¨λ¬ λ° νλ μ΄λ¦¬μ€νΈ 컨νΈλ‘€μ ν¬λͺ μ 리 ν λ§ μ μ© diff --git a/info.yaml b/info.yaml index 73a512b..c4af2be 100644 --- a/info.yaml +++ b/info.yaml @@ -1,5 +1,5 @@ title: "μ λ λ€μ΄λ‘λ" -version: "0.5.15" +version: "0.5.16" package_name: "anime_downloader" developer: "projectdx" description: "anime downloader" diff --git a/mod_anilife.py b/mod_anilife.py index 5f0f475..0017885 100644 --- a/mod_anilife.py +++ b/mod_anilife.py @@ -217,7 +217,7 @@ class LogicAniLife(AnimeModuleBase): start_time = time.time() data = "" try: - # --- Zendriver Daemon μ΅μ μ΅μ ν (v0.5.0) --- + # --- Layer 1: Zendriver Daemon (μ΅μ ) --- from .mod_ohli24 import LogicOhli24 if LogicOhli24.is_zendriver_daemon_running(): logger.info(f"[Anilife] Trying Zendriver Daemon: {url}") @@ -229,17 +229,86 @@ class LogicAniLife(AnimeModuleBase): else: logger.warning(f"[Anilife] Daemon failed in {elapsed:.2f}s: {daemon_res.get('error', 'Unknown')}") - # --- Fallback: Playwright --- - logger.info("[Anilife] Falling back to Playwright...") - from .lib.crawler import Crawler - res = asyncio.run( - Crawler().get_html_playwright( - url, engine="chromium", headless=headless - ) - ) - elapsed = time.time() - start_time - logger.info(f"[Anilife] Playwright finished in {elapsed:.2f}s") - return res + # --- Layer 2: Zendriver Subprocess Fallback (Ohli24μ λμΌ) --- + logger.info(f"[Anilife] Trying Zendriver subprocess: {url}") + if LogicOhli24.ensure_zendriver_installed(): + try: + import subprocess + script_path = os.path.join(os.path.dirname(__file__), "lib", "zendriver_ohli24.py") + browser_path = P.ModelSetting.get("ohli24_zendriver_browser_path") + + cmd = [sys.executable, script_path, url, str(30)] + if browser_path: + cmd.append(browser_path) + + with open(os.devnull, 'w') as devnull: + old_stderr = sys.stderr + sys.stderr = devnull + try: + result = subprocess.run( + cmd, + capture_output=False, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + timeout=60 + ) + finally: + sys.stderr = old_stderr + + if result.returncode == 0 and result.stdout.strip(): + import json as json_lib + zd_result = json_lib.loads(result.stdout.strip()) + if zd_result.get("success") and zd_result.get("html"): + elapsed = time.time() - start_time + logger.info(f"[Anilife] Zendriver subprocess success in {elapsed:.2f}s, HTML len: {len(zd_result['html'])}") + return zd_result["html"] + else: + logger.warning(f"[Anilife] Zendriver subprocess failed: {zd_result.get('error', 'Unknown')}") + else: + logger.warning(f"[Anilife] Zendriver subprocess returncode: {result.returncode}") + + except subprocess.TimeoutExpired: + logger.warning(f"[Anilife] Zendriver subprocess timed out") + except Exception as e: + logger.warning(f"[Anilife] Zendriver subprocess exception: {e}") + + # --- Layer 3: Camoufox Fallback (μ΅ν μλ¨) --- + logger.info("[Anilife] Falling back to Camoufox...") + try: + import subprocess + script_path = os.path.join(os.path.dirname(__file__), "lib", "camoufox_ohli24.py") + + with open(os.devnull, 'w') as devnull: + old_stderr = sys.stderr + sys.stderr = devnull + try: + result = subprocess.run( + [sys.executable, script_path, url, str(30)], + capture_output=False, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + timeout=60 + ) + finally: + sys.stderr = old_stderr + + if result.returncode == 0 and result.stdout.strip(): + import json as json_lib + cf_result = json_lib.loads(result.stdout.strip()) + if cf_result.get("success") and cf_result.get("html"): + elapsed = time.time() - start_time + logger.info(f"[Anilife] Camoufox success in {elapsed:.2f}s, HTML len: {len(cf_result['html'])}") + return cf_result["html"] + else: + logger.warning(f"[Anilife] Camoufox failed: {cf_result.get('error', 'Unknown')}") + else: + logger.warning(f"[Anilife] Camoufox returncode: {result.returncode}") + except subprocess.TimeoutExpired: + logger.warning("[Anilife] Camoufox timed out") + except Exception as e: + logger.warning(f"[Anilife] Camoufox exception: {e}") except Exception as e: logger.error("Exception:%s", e) diff --git a/mod_ohli24.py b/mod_ohli24.py index ad7595c..b4423bc 100644 --- a/mod_ohli24.py +++ b/mod_ohli24.py @@ -1305,9 +1305,17 @@ class LogicOhli24(AnimeModuleBase): # logger.debug(item.xpath(".//div[@class='post-title']/text()").join()) entity["title"] = "".join(item.xpath(".//div[@class='post-title']/text()")).strip() - original_img = item.xpath(".//div[@class='img-item']/img/@src")[0].replace( - "..", P.ModelSetting.get("ohli24_url") - ) + # Use multiple image attributes for lazy-loading support + img_attributes = [".//div[@class='img-item']/img/@src", ".//div[@class='img-item']/img/@data-src", ".//div[@class='img-item']/img/@data-ezsrc"] + original_img = "" + for attr in img_attributes: + matches = item.xpath(attr) + if matches and matches[0].strip(): + original_img = matches[0].replace("..", P.ModelSetting.get("ohli24_url")) + break + + if not original_img: + original_img = "https://via.placeholder.com/200x300?text=No+Image" # Use Image Proxy entity["image_link"] = "/%s/api/%s/image_proxy?url=%s" % ( @@ -1712,17 +1720,17 @@ class LogicOhli24(AnimeModuleBase): epi_no = int(match.group("epi_no")) # Use glob pattern for quality: *-OHNI24.mp4 matches any quality + # Sanitize ONLY the fixed text parts to avoid breaking glob wildcards (*) + if match: + content_title_clean = Util.change_text_for_use_filename(content_title) filename_pattern = "%s.S%sE%s.*-OHNI24.mp4" % ( - content_title, + content_title_clean, "0%s" % season if season < 10 else season, "0%s" % epi_no if epi_no < 10 else epi_no, ) else: - # Fallback pattern for non-standard titles - filename_pattern = "%s.*-OHNI24.mp4" % title - - # Sanitize pattern (but keep glob wildcards) - filename_pattern = Util.change_text_for_use_filename(filename_pattern) + title_clean = Util.change_text_for_use_filename(title) + filename_pattern = "%s.*-OHNI24.mp4" % title_clean # Get save path savepath = P.ModelSetting.get("ohli24_download_path") diff --git a/static/css/anilife.css b/static/css/anilife.css index 311c148..a7b0942 100644 --- a/static/css/anilife.css +++ b/static/css/anilife.css @@ -6,3 +6,26 @@ --notify-warning-bg: rgba(120, 53, 15, 0.95); --notify-danger-bg: rgba(127, 29, 29, 0.95); } + +/* Common Layout Wrapper - Responsive */ +.anilife-common-wrapper { + max-width: 100%; + margin: 0 auto; + width: 100%; + transition: opacity 0.5s ease-out; +} + +/* Desktop: 1400px max-width */ +@media (min-width: 992px) { + .anilife-common-wrapper { + max-width: 1400px; + } +} + +.anilife-common-wrapper.content-cloak { + opacity: 0; +} + +.anilife-common-wrapper.visible { + opacity: 1; +} diff --git a/static/css/linkkf.css b/static/css/linkkf.css index 02537ee..456a10a 100644 --- a/static/css/linkkf.css +++ b/static/css/linkkf.css @@ -35,3 +35,19 @@ ul.nav.nav-pills .nav-link.active { background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3) !important; } + +/* Common Layout Wrapper */ +.linkkf-common-wrapper { + max-width: 1400px; + margin: 0 auto; + width: 100%; + transition: opacity 0.5s ease-out; +} + +.linkkf-common-wrapper.content-cloak { + opacity: 0; +} + +.linkkf-common-wrapper.visible { + opacity: 1; +} diff --git a/static/css/mobile_custom.css b/static/css/mobile_custom.css index c98e594..abe598d 100644 --- a/static/css/mobile_custom.css +++ b/static/css/mobile_custom.css @@ -49,22 +49,44 @@ overflow-x: hidden !important; } - /* Global Navigation Pills Fix */ + /* Global Navigation Pills Fix & Premium Styling */ ul.nav.nav-pills.bg-light { - margin-top: 50px !important; - margin-bottom: 10px !important; - width: 100% !important; - display: flex !important; - flex-wrap: wrap !important; - justify-content: center !important; - border-radius: 10px !important; - padding: 4px !important; - gap: 2px !important; + 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; /* Pill shape container */ + padding: 6px !important; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important; + display: inline-flex !important; /* Fit content */ + flex-wrap: wrap; /* allow wrap on small screens */ + justify-content: center; + width: auto !important; /* Prevent full width */ + margin-bottom: 20px; + margin-top: 50px !important; /* Mobile spacing */ } - + + ul.nav.nav-pills .nav-item { + margin: 0 2px; + } + ul.nav.nav-pills .nav-link { - padding: 5px 12px !important; - font-size: 13px !important; + border-radius: 50rem !important; + padding: 8px 20px !important; + color: #94a3b8 !important; /* Muted text */ + 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); } /* Layout Expansion on Mobile */ diff --git a/static/css/ohli24.css b/static/css/ohli24.css index 6557c6d..7798e0f 100644 --- a/static/css/ohli24.css +++ b/static/css/ohli24.css @@ -21,6 +21,21 @@ body { color: #f1f5f9; background-image: linear-gradient(135deg, #1f2937, #111827, #0f172a) !important; background-attachment: fixed !important; + font-family: 'NamumSquareNeo', system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif; +} + +/* Glass Card Container */ +.glass-card { + background: rgba(30, 41, 59, 0.7) !important; + backdrop-filter: blur(12px) !important; + border: 1px solid rgba(255, 255, 255, 0.08) !important; + border-radius: 16px !important; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + transition: transform 0.3s ease, box-shadow 0.3s ease !important; +} + +.glass-card:hover { + border-color: rgba(96, 165, 250, 0.5) !important; } /* General Layout Fixes */ @@ -30,6 +45,59 @@ body { max-width: 100%; } +.ohli24-common-wrapper { + max-width: 1400px; + margin: 0 auto; + width: 100%; + transition: opacity 0.5s ease-out; +} + +.ohli24-common-wrapper.content-cloak { + opacity: 0; +} + +.ohli24-common-wrapper.visible { + opacity: 1; +} + +/* Common Header Styles */ +.ohli24-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + flex-wrap: wrap; + gap: 1rem; +} + +.ohli24-header-left { + display: flex; + align-items: center; +} + +.ohli24-icon-box { + width: 48px; + height: 48px; + background: rgba(59, 130, 246, 0.1); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid rgba(59, 130, 246, 0.2); + margin-right: 1rem; +} + +.ohli24-header-title { + margin-bottom: 0; + color: white; + font-weight: 700; +} + +.ohli24-header-subtitle { + font-size: 0.875rem; + color: #94a3b8; +} + /* Nav Pills Customization */ ul.nav.nav-pills.bg-light { background-color: rgba(30, 41, 59, 0.6) !important; diff --git a/templates/anime_downloader_anilife_request.html b/templates/anime_downloader_anilife_request.html index 3827f66..5a281ad 100644 --- a/templates/anime_downloader_anilife_request.html +++ b/templates/anime_downloader_anilife_request.html @@ -22,7 +22,7 @@ } -