diff --git a/logic_linkkf.py b/logic_linkkf.py index c47bde5..90d07d0 100644 --- a/logic_linkkf.py +++ b/logic_linkkf.py @@ -5,19 +5,28 @@ # @Site : # @File : logic_linkkf # @Software: PyCharm + import os, sys, traceback, re, json, threading from datetime import datetime import copy +import random +import time +import urllib +from urllib.parse import urlparse # third-party import requests +from lxml import html +from urllib import parse +import urllib +import asyncio # third-party from flask import request, render_template, jsonify from sqlalchemy import or_, and_, func, not_, desc # sjva 공용 -from framework import db, scheduler, path_data, socketio +from framework import app, db, scheduler, path_data, socketio from framework.util import Util from framework.common.util import headers from plugin import ( @@ -26,11 +35,42 @@ from plugin import ( FfmpegQueue, default_route_socketio, ) + +packages = ["beautifulsoup4", "requests-cache", "cloudscraper"] + +for _package in packages: + try: + import _package + + except ModuleNotFoundError: + if _package == "playwright": + pass + # os.system(f"pip3 install playwright") + # os.system(f"playwright install") + else: + print("here") + os.system(f"{app.config['config']['pip']} install {_package}") + # import package + + except ImportError: + # main(["install", package]) + if _package == "playwright": + pass + # os.system(f"pip3 install {package}") + # os.system(f"playwright install") + else: + print("here") + os.system(f"{app.config['config']['pip']} install {_package}") + # import package + from tool_base import d # 패키지 from .plugin import P +logger = P.logger +name = "linkkf" + class LogicLinkkf(LogicModuleBase): db_default = { @@ -41,7 +81,7 @@ class LogicLinkkf(LogicModuleBase): "linkkf_auto_make_season_folder": "True", "linkkf_finished_insert": "[완결]", "linkkf_max_ffmpeg_process_count": "1", - "linkkf_order_desc": "False", + "linkkf_order_desc": "True", "linkkf_auto_start": "False", "linkkf_interval": "* 5 * * *", "linkkf_auto_mode_all": "False", @@ -54,6 +94,12 @@ class LogicLinkkf(LogicModuleBase): } current_headers = None current_data = None + referer = None + download_queue = None + download_thread = None + current_download_count = 0 + + cache_path = os.path.dirname(__file__) session = requests.Session() headers = { @@ -92,17 +138,63 @@ class LogicLinkkf(LogicModuleBase): return render_template("sample.html", title="%s - %s" % (P.package_name, sub)) def process_ajax(self, sub, req): + _data = [] try: if sub == "analysis": - pass + _code = request.form["code"] + + P.ModelSetting.set("linkkf_current_code", _code) + _data = self.get_series_info(_code) + self.current_data = _data + return jsonify({"ret": "success", "data": _data, "code": _code}) elif sub == "anime_list": - pass + data = [] + cate = request.form["type"] + page = request.form["page"] + + data = self.get_anime_info(cate, page) + # self.current_data = data + return jsonify( + {"ret": "success", "cate": cate, "page": page, "data": data} + ) + elif sub == "screen_movie_list": + try: + logger.debug("request:::> %s", request.form["page"]) + page = request.form["page"] + data = self.get_screen_movie_info(page) + dummy_data = {"ret": "success", "data": data} + return jsonify(data) + except Exception as e: + logger.error(f"Exception: {str(e)}") + logger.error(traceback.format_exc()) elif sub == "complete_list": pass elif sub == "search": - pass + data = [] + # cate = request.form["type"] + # page = request.form["page"] + cate = request.form["type"] + query = request.form["query"] + page = request.form["page"] + + data = self.get_search_result(query, page, cate) + # self.current_data = data + return jsonify( + { + "ret": "success", + "cate": cate, + "page": page, + "query": query, + "data": data, + } + ) elif sub == "add_queue": - pass + logger.debug(f"linkkf add_queue routine ===============") + ret = {} + info = json.loads(request.form["data"]) + logger.info(f"info:: {info}") + ret["ret"] = self.add(info) + return jsonify(ret) elif sub == "entity_list": pass elif sub == "queue_command": @@ -116,9 +208,377 @@ class LogicLinkkf(LogicModuleBase): elif sub == "add_whitelist": pass + except Exception as e: + P.logger.error(f"Exception: {str(e)}") + P.logger.error(traceback.format_exc()) + + @staticmethod + def get_html(url: str, timeout: int = 10, cached=False): + + try: + if LogicLinkkf.referer is None: + LogicLinkkf.referer = f"{P.ModelSetting.get('linkkf_url')}" + + # return LogicLinkkfYommi.get_html_requests(url) + return LogicLinkkf.get_html_cloudflare(url, timeout) + + except Exception as e: + logger.error("Exception:%s", e) + logger.error(traceback.format_exc()) + + @staticmethod + def get_html_cloudflare(url: str, timeout: int = 10, cached=False): + logger.debug(f"cloudflare protection bypass {'=' * 30}") + import cloudscraper + + user_agents_list = [ + "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36", + ] + # ua = UserAgent(verify_ssl=False) + + LogicLinkkf.headers["User-Agent"] = random.choice(user_agents_list) + + LogicLinkkf.headers["Referer"] = LogicLinkkf.referer + + # logger.debug(f"headers:: {LogicLinkkfYommi.headers}") + + if LogicLinkkf.session is None: + LogicLinkkf.session = requests.Session() + + # LogicLinkkfYommi.session = requests.Session() + # re_sess = requests.Session() + # logger.debug(LogicLinkkfYommi.session) + + # sess = cloudscraper.create_scraper( + # # browser={"browser": "firefox", "mobile": False}, + # browser={"browser": "chrome", "mobile": False}, + # debug=True, + # sess=LogicLinkkfYommi.session, + # delay=10, + # ) + # scraper = cloudscraper.create_scraper(sess=re_sess) + scraper = cloudscraper.create_scraper( + # debug=True, + delay=10, + sess=LogicLinkkf.session, + browser={ + "custom": "linkkf", + }, + ) + + # print(scraper.get(url, headers=LogicLinkkfYommi.headers).content) + # print(scraper.get(url).content) + # return scraper.get(url, headers=LogicLinkkfYommi.headers).content + # logger.debug(LogicLinkkfYommi.headers) + return scraper.get( + url, + headers=LogicLinkkf.headers, + timeout=10, + ).content.decode("utf8", errors="replace") + + def get_anime_info(self, cate, page): + try: + if cate == "ing": + url = f"{P.ModelSetting.get('linkkf_url')}/airing/page/{page}" + items_xpath = '//div[@class="myui-vodlist__box"]' + title_xpath = './/a[@class="text-fff"]//text()' + elif cate == "movie": + url = f"{P.ModelSetting.get('linkkf_url')}/ani/page/{page}" + items_xpath = '//div[@class="myui-vodlist__box"]' + title_xpath = './/a[@class="text-fff"]//text()' + elif cate == "complete": + url = f"{P.ModelSetting.get('linkkf_url')}/anime-list/page/{page}" + items_xpath = '//div[@class="myui-vodlist__box"]' + title_xpath = './/a[@class="text-fff"]//text()' + elif cate == "top_view": + url = f"{P.ModelSetting.get('linkkf_url')}/topview/page/{page}" + items_xpath = '//div[@class="myui-vodlist__box"]' + title_xpath = './/a[@class="text-fff"]//text()' + + logger.info("url:::> %s", url) + logger.info("test..........................") + # logger.info("test..........................") + if self.referer is None: + self.referer = "https://linkkf.app" + + data = {"ret": "success", "page": page} + response_data = LogicLinkkf.get_html(url, timeout=10) + # P.logger.debug(response_data) + P.logger.debug("debug.....................") + tree = html.fromstring(response_data) + tmp_items = tree.xpath(items_xpath) + + if tree.xpath('//div[@id="wp_page"]//text()'): + data["total_page"] = tree.xpath('//div[@id="wp_page"]//text()')[-1] + else: + data["total_page"] = 0 + data["episode_count"] = len(tmp_items) + data["episode"] = [] + + for item in tmp_items: + entity = dict() + entity["link"] = item.xpath(".//a/@href")[0] + entity["code"] = re.search(r"[0-9]+", entity["link"]).group() + entity["title"] = item.xpath(title_xpath)[0].strip() + entity["image_link"] = item.xpath("./a/@data-original")[0] + entity["chapter"] = ( + item.xpath("./a/span//text()")[0].strip() + if len(item.xpath("./a/span//text()")) > 0 + else "" + ) + # logger.info('entity:::', entity['title']) + data["episode"].append(entity) + + # logger.debug(data) + + return data except Exception as e: P.logger.error("Exception:%s", e) P.logger.error(traceback.format_exc()) + return {"ret": "exception", "log": str(e)} + + def get_series_info(self, code): + from bs4 import BeautifulSoup + + data = {"code": code, "ret": False} + try: + # 이전 데이터가 있다면, 리턴 (# If you have previous data, return) + if ( + LogicLinkkf.current_data is not None + and LogicLinkkf.current_data["code"] == code + and LogicLinkkf.current_data["ret"] + ): + return LogicLinkkf.current_data + url = "%s/%s" % (P.ModelSetting.get("linkkf_url"), code) + logger.info(url) + + logger.debug(LogicLinkkf.headers) + html_content = LogicLinkkf.get_html(url, cached=False) + # html_content = LogicLinkkf.get_html_playwright(url) + # html_content = LogicLinkkf.get_html_cloudflare(url, cached=False) + + sys.setrecursionlimit(10**7) + # logger.info(html_content) + tree = html.fromstring(html_content) + # tree = etree.fromstring( + # html_content, parser=etree.XMLParser(huge_tree=True) + # ) + # tree1 = BeautifulSoup(html_content, "lxml") + + soup = BeautifulSoup(html_content, "html.parser") + # tree = etree.HTML(str(soup)) + # logger.info(tree) + + tmp2 = soup.select("ul > a") + if len(tmp2) == 0: + tmp = soup.select("u > a") + else: + tmp = soup.select("ul > a") + + # logger.debug(f"tmp1 size:=> {str(len(tmp))}") + + try: + tmp = ( + tree.xpath('//div[@class="hrecipe"]/article/center/strong')[0] + .text_content() + .strip() + ) + except IndexError: + tmp = tree.xpath("//article/center/strong")[0].text_content().strip() + + # logger.info(tmp) + match = re.compile(r"(?P\d+)기").search(tmp) + if match: + data["season"] = match.group("season") + else: + data["season"] = "1" + + data["_id"] = str(code) + data["title"] = tmp.replace(data["season"] + "기", "").strip() + data["title"] = data["title"].replace("()", "").strip() + data["title"] = ( + Util.change_text_for_use_filename(data["title"]) + .replace("OVA", "") + .strip() + ) + + try: + data["poster_url"] = tree.xpath( + '//div[@class="myui-content__thumb"]/a/@data-original' + ) + # print(tree.xpath('//div[@class="myui-content__detail"]/text()')) + if len(tree.xpath('//div[@class="myui-content__detail"]/text()')) > 3: + data["detail"] = [ + { + "info": str( + tree.xpath( + "//div[@class='myui-content__detail']/text()" + )[3] + ) + } + ] + else: + data["detail"] = [{"정보없음": ""}] + except Exception as e: + logger.error(e) + data["detail"] = [{"정보없음": ""}] + data["poster_url"] = None + + data["rate"] = tree.xpath('span[@class="tag-score"]') + + tag_score = tree.xpath('//span[@class="taq-score"]')[0].text_content() + # logger.debug(tag_score) + tag_count = ( + tree.xpath('//span[contains(@class, "taq-count")]')[0] + .text_content() + .strip() + ) + data_rate = tree.xpath('//div[@class="rating"]/div/@data-rate') + + tmp2 = soup.select("ul > a") + if len(tmp) == 0: + tmp = soup.select("u > a") + else: + tmp = soup.select("ul > a") + + if tmp is not None: + data["episode_count"] = str(len(tmp)) + else: + data["episode_count"] = "0" + + data["episode"] = [] + # tags = tree.xpath( + # '//*[@id="syno-nsc-ext-gen3"]/article/div[1]/article/a') + # tags = tree.xpath("//ul/a") + tags = soup.select("ul > u > a") + if len(tags) > 0: + pass + else: + tags = soup.select("ul > a") + + logger.debug(len(tags)) + + # logger.info("tags", tags) + # re1 = re.compile(r'\/(?P\d+)') + re1 = re.compile(r"\-([^-])+\.") + + data["save_folder"] = data["title"] + # logger.debug(f"save_folder::> {data['save_folder']}") + + # program = ( + # db.session.query(ModelLinkkfProgram).filter_by(programcode=code).first() + # ) + + idx = 1 + for t in tags: + entity = { + "_id": data["code"], + "program_code": data["code"], + "program_title": data["title"], + "save_folder": Util.change_text_for_use_filename( + data["save_folder"] + ), + "title": t.text.strip(), + # "title": t.text_content().strip(), + } + # entity['code'] = re1.search(t.attrib['href']).group('code') + + # logger.debug(f"title ::>{entity['title']}") + + # 고유id임을 알수 없는 말도 안됨.. + # 에피소드 코드가 고유해야 상태값 갱신이 제대로 된 값에 넣어짐 + p = re.compile(r"([0-9]+)화?") + m_obj = p.match(entity["title"]) + # logger.info(m_obj.group()) + # entity['code'] = data['code'] + '_' +str(idx) + + episode_code = None + # logger.debug(f"m_obj::> {m_obj}") + if m_obj is not None: + episode_code = m_obj.group(1) + entity["code"] = data["code"] + episode_code.zfill(4) + else: + entity["code"] = data["code"] + + aa = t["href"] + if "/player" in aa: + entity["url"] = "https://linkkf.app" + t["href"] + else: + entity["url"] = t["href"] + entity["season"] = data["season"] + + # 저장 경로 저장 + # Todo: db + tmp_save_path = P.ModelSetting.get(f"linkkf_download_path") + if P.ModelSetting.get("linkkf_auto_make_folder") == "True": + program_path = os.path.join(tmp_save_path, entity["save_folder"]) + entity["save_path"] = program_path + if P.ModelSetting.get("linkkf_auto_make_season_folder"): + entity["save_path"] = os.path.join( + entity["save_path"], "Season %s" % int(entity["season"]) + ) + + entity["image"] = data["poster_url"] + + entity["filename"] = LogicLinkkf.get_filename( + data["save_folder"], data["season"], entity["title"] + ) + data["episode"].append(entity) + idx = idx + 1 + + data["ret"] = True + # logger.info('data', data) + self.current_data = data + + return data + + except Exception as e: + logger.error("Exception:%s", e) + logger.error(traceback.format_exc()) + data["log"] = str(e) + data["ret"] = "error" + return data + except IndexError as e: + logger.error("Exception:%s", e) + logger.error(traceback.format_exc()) + data["log"] = str(e) + data["ret"] = "error" + return data + + @staticmethod + def get_filename(maintitle, season, title): + try: + # logger.debug("get_filename()===") + # logger.info("title:: %s", title) + # logger.info("maintitle:: %s", maintitle) + match = re.compile( + r"(?P.*?)\s?((?P<season>\d+)기)?\s?((?P<epi_no>\d+)화?)" + ).search(title) + if match: + epi_no = int(match.group("epi_no")) + if epi_no < 10: + epi_no = "0%s" % epi_no + else: + epi_no = "%s" % epi_no + + if int(season) < 10: + season = "0%s" % season + else: + season = "%s" % season + + # title_part = match.group('title').strip() + # ret = '%s.S%sE%s%s.720p-SA.mp4' % (maintitle, season, epi_no, date_str) + ret = "%s.S%sE%s.720p-LK.mp4" % (maintitle, season, epi_no) + else: + logger.debug("NOT MATCH") + ret = "%s.720p-SA.mp4" % maintitle + + return Util.change_text_for_use_filename(ret) + except Exception as e: + logger.error(f"Exception: {str(e)}") + logger.error(traceback.format_exc()) pass diff --git a/logic_ohli24.py b/logic_ohli24.py index b47541d..209ecd0 100644 --- a/logic_ohli24.py +++ b/logic_ohli24.py @@ -1150,7 +1150,7 @@ class Ohli24QueueEntity(FfmpegQueueEntity): # print(m2.group(1)) dict_string = "{" + m2.group(1) + "}" - logger.info(f"dict_string::> {dict_string}") + # logger.info(f"dict_string::> {dict_string}") tracks = json.loads(dict_string) self.srt_url = tracks["tracks"][0]["file"] @@ -1266,10 +1266,11 @@ class Ohli24QueueEntity(FfmpegQueueEntity): self.savepath, self.filename.replace(".mp4", ".ko.srt") ) - if self.srt_url is not None and not os.path.exists(srt_filepath): - # vtt_data = requests.get(self.vtt, headers=headers).text - # srt_data = convert_vtt_to_srt(vtt_data) - + if ( + self.srt_url is not None + and not os.path.exists(srt_filepath) + and not ("thumbnails.vtt" in self.srt_url) + ): srt_data = requests.get(self.srt_url, headers=headers).text write_file(srt_data, srt_filepath) diff --git a/plugin.py b/plugin.py index d34c197..d113ada 100644 --- a/plugin.py +++ b/plugin.py @@ -89,9 +89,10 @@ def initialize(): PluginUtil.make_info_json(P.plugin_info, __file__) from .logic_ohli24 import LogicOhli24 from .logic_anilife import LogicAniLife + from .logic_linkkf import LogicLinkkf # P.module_list = [LogicOhli24(P), LogicLinkkf(P)] - P.module_list = [LogicOhli24(P), LogicAniLife(P)] + P.module_list = [LogicOhli24(P), LogicAniLife(P), LogicLinkkf(P)] P.logic = Logic(P) default_route(P) except Exception as e: diff --git a/static/css/linkkf.css b/static/css/linkkf.css new file mode 100644 index 0000000..ce25453 --- /dev/null +++ b/static/css/linkkf.css @@ -0,0 +1,243 @@ +button.code-button { + min-width: 82px !important; +} +.tooltip { + position: relative; + display: block; +} + +[data-tooltip-text]:hover { + position: relative; +} + +[data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: 90%; + + content: attr(data-tooltip-text); +} + +[data-tooltip-text]:hover:after { + top: 230%; + left: 0; + opacity: 1; +} +[data-tooltip-text]:hover { + position: relative; +} + +[data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: -210% !important; + + content: attr(data-tooltip-text); +} + +[data-tooltip-text]:hover:after { + top: 130%; + left: 0; + opacity: 1; +} + +#airing_list { + display: none; +} + +.cut-text { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; +} + +@media (min-width: 576px) { + .container { + max-width: 100%; + } +} + +@media (min-width: 1280px) { + #yommi_wrapper { + max-width: 80%; + margin: 0 auto; + } +} + +#screen_movie_list { + margin-top: 10px; +} +/* .spinner {*/ +/* width: 40px;*/ +/* height: 40px;*/ +/* background-color: #333;*/ + +/* margin: 100px auto;*/ +/* -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;*/ +/* animation: sk-rotateplane 1.2s infinite ease-in-out;*/ +/*}*/ + +/*@-webkit-keyframes sk-rotateplane {*/ +/* 0% { -webkit-transform: perspective(120px) }*/ +/* 50% { -webkit-transform: perspective(120px) rotateY(180deg) }*/ +/* 100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }*/ +/*}*/ + +/*@keyframes sk-rotateplane {*/ +/* 0% {*/ +/* transform: perspective(120px) rotateX(0deg) rotateY(0deg);*/ +/* -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)*/ +/* } 50% {*/ +/* transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);*/ +/* -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)*/ +/* } 100% {*/ +/* transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);*/ +/* -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);*/ +/* }*/ + +/*}*/ +.spinner { + width: 40px; + height: 40px; + + position: relative; + margin: 100px auto; +} + +.double-bounce1, +.double-bounce2 { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: #333; + opacity: 0.6; + position: absolute; + top: 0; + left: 0; + + -webkit-animation: sk-bounce 2s infinite ease-in-out; + animation: sk-bounce 2s infinite ease-in-out; +} + +.double-bounce2 { + -webkit-animation-delay: -1s; + animation-delay: -1s; +} + +@-webkit-keyframes sk-bounce { + 0%, + 100% { + -webkit-transform: scale(0); + } + 50% { + -webkit-transform: scale(1); + } +} + +@keyframes sk-bounce { + 0%, + 100% { + transform: scale(0); + -webkit-transform: scale(0); + } + 50% { + transform: scale(1); + -webkit-transform: scale(1); + } +} + +.badge-on-image { + position: absolute; + top: 2px; + /*bottom: 2px; !* position where you want it *!*/ + right: 2px; + padding: 5px 12px; +} + +#inner_screen_movie > div { + margin-bottom: 10px; +} + +.card-body { + padding: 0!important; +} +.new-anime { + border-color: darksalmon; + border-width: 4px; + border-style: dashed; + +} + +.card-title { + padding: 1rem!important; +} + +button#add_whitelist { + float: right; +} + +button.btn-favorite { + background-color: #e0ff42; +} + + +body { + font-family: NanumSquareNeo,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans,Liberation Sans,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; +} +body { + background-image: linear-gradient(90deg, #233f48, #6c6fa2, #768dae); +} + diff --git a/templates/anime_downloader_linkkf_category.html b/templates/anime_downloader_linkkf_category.html index f279139..d913e90 100644 --- a/templates/anime_downloader_linkkf_category.html +++ b/templates/anime_downloader_linkkf_category.html @@ -1,49 +1,57 @@ {% extends "base.html" %} {% block content %} - -<div class="input-group mb-2"> - <input - id="input_search" - type="search" - class="form-control rounded" - placeholder="Search" - aria-label="Search" - aria-describedby="search-addon" - /> - <button id="btn_search" type="button" class="btn btn-outline-primary"> - search - </button> +<div id="preloader" class="loader"> + <div class="loader-inner"> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + </div> </div> +<div id="yommi_wrapper"> + <div class="input-group mb-2"> + <input + id="input_search" + type="search" + class="form-control rounded" + placeholder="Search" + aria-label="Search" + aria-describedby="search-addon" + /> + <button id="btn_search" type="button" class="btn btn-primary">search</button> + </div> -<div> - <div - id="anime_category" - class="btn-group" - role="group" - aria-label="Basic example" - > - <button id="ing" type="button" class="btn btn-success">방영중</button> - <button id="theater" type="button" class="btn btn-primary">극장판</button> - <button id="complete_anilist" type="button" class="btn btn-dark"> - 완결 - </button> + <div> + <div id="anime_category" class="btn-group" role="group" aria-label="Basic example"> + <button id="ing" type="button" class="btn btn-success">방영중</button> + <button id="theater" type="button" class="btn btn-primary">극장판</button> + <button id="complete_anilist" type="button" class="btn btn-dark">완결</button> </div> <form id="airing_list_form"> - <div id="airing_list"></div> + <div id="airing_list"></div> </form> <form id="screen_movie_list_form"> - <div id="screen_movie_list" class="container"></div> + <div id="screen_movie_list" class="container"></div> </form> - <div class="text-center"> - <div id="spinner" class="spinner-border" role="status"> - <span class="sr-only">Loading...</span> - </div> - </div> + <form id="program_auto_form"> - <div id="episode_list"></div> + <div id="episode_list"></div> </form> + </div> </div> <!--전체--> +<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js" @@ -127,28 +135,30 @@ } function make_airing_list(data, page) { + console.log("call make_airing_list()") let str = '' let tmp = '' + //console.log(data) str += '<div>'; str += '<button type="button" class="btn btn-info">Page <span class="badge bg-warning">' + page + '</span></button>'; str += '</div>'; // str += '<div class="card-columns">' str += '<div id="inner_screen_movie" class="row infinite-scroll">'; - for (let i in data.anime_list) { + for (let i in data.episode) { tmp = '<div class="col-6 col-sm-4 col-md-3">'; tmp += '<div class="card">'; - // tmp += '<img class="lozad" data-src="' + data.anime_list[i].image_link + '" />'; - tmp += '<img class="lazyload" src="../static/img_loader_x200.svg" data-original="' + data.anime_list[i].image_link + '" style="cursor: pointer" onclick="location.href=\'./request?code=' + data.anime_list[i].code + '\'"/>'; + // tmp += '<img class="lozad" data-src="' + data.episode[i].image_link + '" />'; + tmp += '<img class="lazyload" src="../static/img_loader_x200.svg" data-original="' + data.episode[i].image_link + '" style="cursor: pointer" onclick="location.href=\'./request?code=' + data.episode[i].code + '\'"/>'; tmp += '<div class="card-body">' // {#tmp += '<button id="code_button" data-code="' + data.episode[i].code + '" type="button" class="btn btn-primary code-button bootstrap-tooltip" data-toggle="button" data-tooltip="true" aria-pressed="true" autocomplete="off" data-placement="top">' +#} // {# '<span data-tooltip-text="'+data.episode[i].title+'">' + data.episode[i].code + '</span></button></div>';#} - tmp += '<h5 class="card-title">' + data.anime_list[i].title + '</h5>'; - tmp += '<p class="card-text">' + data.anime_list[i].code + '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + - data.anime_list[i].code + - '"><i class="bi bi-heart-fill"></i></button></p>'; - tmp += '<a href="./request?code=' + data.anime_list[i].code + '" class="btn btn-primary cut-text">' + data.anime_list[i].title + '</a>'; + tmp += '<h5 class="card-title">' + data.episode[i].title + '</h5>'; + tmp += '<p class="card-text">' + data.episode[i].code + '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + + data.episode[i].code + + '"><i class="bi bi-heart-fill"></i></button></p>'; + tmp += '<a href="./request?code=' + data.episode[i].code + '" class="btn btn-primary cut-text">' + data.episode[i].title + '</a>'; // tmp += // '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + // data.anime_list[i].code + @@ -406,7 +416,7 @@ let data_code = $(this).attr("data-code"); console.log(data_code); $.ajax({ - url: "/" + package_name + "/ajax/"+sub+"/add_whitelist", + url: "/" + package_name + "/ajax/" + sub + "/add_whitelist", type: "POST", cache: false, data: JSON.stringify({data_code: data_code}), @@ -642,6 +652,14 @@ opacity: 1; } + .badge-on-image { + position: absolute; + top: 2px; + /*bottom: 2px; !* position where you want it *!*/ + right: 2px; + padding: 5px 12px; + } + #airing_list { display: none; } @@ -672,6 +690,7 @@ button.btn-favorite { background-color: #e0ff42; } + /*.card-columns {*/ /* @include media-breakpoint-only(lg) {*/ /* column-count: 4;*/ @@ -684,10 +703,12 @@ .container { max-width: 100%; } + .card-columns { column-count: 2; column-gap: 1.25rem; } + .card-columns .card { display: inline-block; } @@ -695,12 +716,16 @@ @media (min-width: 768px) { - .card-columns {column-count: 3;} + .card-columns { + column-count: 3; + } } /* Large devices (desktops, 992px and up) */ @media (min-width: 992px) { - .card-columns {column-count: 3;} + .card-columns { + column-count: 3; + } } /* Extra large devices (large desktops, 1200px and up) */ @@ -709,22 +734,28 @@ column-count: 5; } } + .card { margin-bottom: 0.75rem; } + .card-columns .card { margin-bottom: 0.75rem; } - .card-columns .card img{ + + .card-columns .card img { width: 100%; } + button#add_whitelist { /*top: -70px;*/ position: relative; } + body { - font-family: NanumSquareNeo,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans,Liberation Sans,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; + font-family: NanumSquareNeo, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; } + body { background-image: linear-gradient(90deg, #233f48, #6c6fa2, #768dae); } diff --git a/templates/anime_downloader_linkkf_list.html b/templates/anime_downloader_linkkf_list.html new file mode 100644 index 0000000..797a6c1 --- /dev/null +++ b/templates/anime_downloader_linkkf_list.html @@ -0,0 +1,144 @@ +{% extends "base.html" %} +{% block content %} + +<div> + <form id="form_search" class="form-inline" style="text-align:left"> + <div class="container-fluid"> + <div class="row show-grid"> + <span class="col-md-4"> + <select id="order" name="order" class="form-control form-control-sm"> + <option value="desc">최근순</option> + <option value="asc">오래된순</option> + </select> + <select id="option" name="option" class="form-control form-control-sm"> + <option value="all">전체</option> + <option value="completed">완료</option> + </select> + </span> + <span class="col-md-8"> + <input id="search_word" name="search_word" class="form-control form-control-sm w-75" type="text" placeholder="" aria-label="Search"> + <button id="search" class="btn btn-sm btn-outline-success">검색</button> + <button id="reset_btn" class="btn btn-sm btn-outline-success">리셋</button> + </span> + </div> + </div> + </form> + <div id='page1'></div> + {{ macros.m_hr_head_top() }} + {{ macros.m_row_start('0') }} + {{ macros.m_col(2, macros.m_strong('Poster')) }} + {{ macros.m_col(10, macros.m_strong('Info')) }} + {{ macros.m_row_end() }} + {{ macros.m_hr_head_bottom() }} + <div id="list_div"></div> + <div id='page2'></div> +</div> + +<script type="text/javascript"> +var package_name = "{{arg['package_name']}}"; +var sub = "{{arg['sub']}}"; +var current_data = null; + +$(document).ready(function(){ + global_sub_request_search('1'); +}); + +$("#search").click(function(e) { + e.preventDefault(); + global_sub_request_search('1'); +}); + +$("body").on('click', '#page', function(e){ + e.preventDefault(); + global_sub_request_search($(this).data('page')); +}); + +$("#reset_btn").click(function(e) { + e.preventDefault(); + document.getElementById("order").value = 'desc'; + document.getElementById("option").value = 'all'; + document.getElementById("search_word").value = ''; + global_sub_request_search('1') +}); + + +$("body").on('click', '#json_btn', function(e){ + e.preventDefault(); + var id = $(this).data('id'); + for (i in current_data.list) { + if (current_data.list[i].id == id) { + m_modal(current_data.list[i]) + } + } +}); + +$("body").on('click', '#self_search_btn', function(e){ + e.preventDefault(); + var search_word = $(this).data('title'); + document.getElementById("search_word").value = search_word; + global_sub_request_search('1') +}); + +$("body").on('click', '#remove_btn', function(e) { + e.preventDefault(); + id = $(this).data('id'); + $.ajax({ + url: '/'+package_name+'/ajax/'+sub+ '/db_remove', + type: "POST", + cache: false, + data: {id:id}, + dataType: "json", + success: function (data) { + if (data) { + $.notify('<strong>삭제되었습니다.</strong>', { + type: 'success' + }); + global_sub_request_search(current_data.paging.current_page, false) + } else { + $.notify('<strong>삭제 실패</strong>', { + type: 'warning' + }); + } + } + }); +}); + +$("body").on('click', '#request_btn', function(e){ + e.preventDefault(); + var content_code = $(this).data('content_code'); + $(location).attr('href', '/' + package_name + '/' + sub + '/request?content_code=' + content_code) +}); + + + +function make_list(data) { + //console.log(data) + str = ''; + for (i in data) { + //console.log(data[i]) + str += m_row_start(); + str += m_col(1, data[i].id); + tmp = (data[i].status == 'completed') ? '완료' : '미완료'; + str += m_col(1, tmp); + tmp = data[i].created_time + '(추가)'; + if (data[i].completed_time != null) + tmp += data[i].completed_time + '(완료)'; + str += m_col(3, tmp) + tmp = data[i].savepath + '<br>' + data[i].filename + '<br><br>'; + tmp2 = m_button('json_btn', 'JSON', [{'key':'id', 'value':data[i].id}]); + tmp2 += m_button('request_btn', '작품 검색', [{'key':'content_code', 'value':data[i].content_code}]); + tmp2 += m_button('self_search_btn', '목록 검색', [{'key':'title', 'value':data[i].title}]); + tmp2 += m_button('remove_btn', '삭제', [{'key':'id', 'value':data[i].id}]); + tmp += m_button_group(tmp2) + str += m_col(7, tmp) + str += m_row_end(); + if (i != data.length -1) str += m_hr(); + } + document.getElementById("list_div").innerHTML = str; +} + + + + +</script> +{% endblock %} \ No newline at end of file diff --git a/templates/anime_downloader_linkkf_queue.html b/templates/anime_downloader_linkkf_queue.html new file mode 100644 index 0000000..6ec163d --- /dev/null +++ b/templates/anime_downloader_linkkf_queue.html @@ -0,0 +1,174 @@ +{% extends "base.html" %} +{% block content %} + +<table id="result_table" class="table table-sm tableRowHover"> + <thead class="thead-dark"> + <tr> + <th style="width:5%; text-align:center;">IDX</th> + <th style="width:8%; text-align:center;">Plugin</th> + <th style="width:10%; text-align:center;">시작시간</th> + <th style="width:20%; text-align:center;">파일명</th> + <th style="width:8%; text-align:center;">상태</th> + <th style="width:15%; text-align:center;">진행률</th> + <th style="width:5%; text-align:center;">길이</th> + <th style="width:5%; text-align:center;">PF</th> + <th style="width:8%; text-align:center;">배속</th> + <th style="width:8%; text-align:center;">진행시간</th> + <th style="width:8%; text-align:center;">Action</th> + </tr> + </thead> + <tbody id="list"></tbody> +</table> + +<script type="text/javascript"> + +$(document).ready(function(){ + var socket = io.connect(window.location.href); + + socket.on('on_start', function(data){ + document.getElementById("log").innerHTML += data.data; + document.getElementById("log").scrollTop = document.getElementById("log").scrollHeight; + document.getElementById("log").style.visibility = 'visible'; + $('#loading').hide(); + }); + + socket.on('add', function(data){ + str = make_item(data); + if (current_data == null || current_data.length == 0) { + current_data = Array(); + $("#list").html(str); + } else { + $("#list").html($("#list").html() + str); + } + current_data.push(data); + }); + + socket.on('status_change', function(data) { + button_html(data); + }); + + socket.on('status', function(data){ + status_html(data); + }); + + socket.on('last', function(data){ + status_html(data); + button_html(data); + }); + + globalSendCommand('list', null, null, null, function(data) { + current_data = data; + $("#list").html(''); + console.log(data) + if (data.length == 0) { + str = "<tr><td colspan='10'><h4>작업이 없습니다.</h4><td><tr>"; + } else { + str = '' + for(i in data) { + str += make_item(data[i]); + } + } + $("#list").html(str); + }); +}); + + +$("body").on('click', '#stop_btn', function(e){ + e.stopPropagation(); + e.preventDefault(); + globalSendCommand('stop', $(this).data('idx'), null, null, function(ret){ + refresh_item(ret.data); + }); +}); + + +function refresh_item(data) { + $('#tr1_'+data.idx).html(make_item1(data)); + $('#collapse_'+data.idx).html(make_item2(data)); +} + +function make_item(data) { + str = '<tr id="tr1_'+data.idx+'" style="cursor: pointer;" data-toggle="collapse" data-target="#collapse_'+ data.idx + '" aria-expanded="true" >'; + str += make_item1(data); + str += '</tr>'; + str += '<tr class="collapse tableRowHoverOff" style="cursor: pointer;" id="collapse_' + data.idx + '">'; + str += make_item2(data); + str += '</tr>'; + return str; +} + +function make_item1(data) { + //console.log(data); + str = ''; + str += '<td scope="col" style="width:5%; text-align:center;">'+ data.idx + '</td>'; + str += '<td scope="col" style="width:8%; text-align:center;">'+ data.callback_id + '</td>'; + str += '<td scope="col" style="width:10%; text-align:center;">'+ data.start_time + '</td>'; + str += '<td scope="col" style="width:20%; text-align:center;">'+ data.filename + '</td>'; + str += '<td id="status_'+data.idx+'" scope="col" style="width:8%; text-align:center;">'+ data.status_kor + '</td>'; + var visi = 'hidden'; + if (parseInt(data.percent) > 0) { + visi = 'visible'; + } + str += '<td scope="col" style="width:20%; text-align:center;"><div class="progress"><div id="progress_'+data.idx+'" class="progress-bar" style="visibility: '+visi+'; width:'+data.percent+'%">'+data.percent +'%</div></div></td>'; + str += '<td scope="col" style="width:5%; text-align:center;">'+ data.duration_str + '</td>'; + str += '<td id="current_pf_count_'+data.idx+'" scope="col" style="width:5%; text-align:center;">'+ data.current_pf_count + '</td>'; + str += '<td id="current_speed_'+data.idx+'" scope="col" style="width:8%; text-align:center;">'+ data.current_speed + '</td>'; + str += '<td id="download_time_'+data.idx+'" scope="col" style="width:8%; text-align:center;">'+ data.download_time + '</td>'; + str += '<td id="button_'+data.idx+'" scope="col" style="width:8%; text-align:center;">'; + if (data.status_str == 'DOWNLOADING') { + str += j_button('stop_btn', '중지', {'idx':data.idx}, 'danger', false, false); + } + str += '</td>' + return str; +} + +function make_item2(data) { + str = ''; + str += '<td colspan="11">'; + str += '<div id="detail_'+data.idx+'">'; + str += get_detail(data); + str += '</div>'; + str += '</td>'; + return str +} + + +function get_detail(data) { + var str = j_row_info('URL', data.url); + str += j_row_info('임시경로', data.temp_fullpath); + str += j_row_info('저장경로', data.save_fullpath); + str += j_row_info('진행률(current/total)', data.percent+ '% (' + data.current_duration + ' / ' + data.duration + ')'); + str += j_row_info('현재 비트레이트', data.current_bitrate); + str += j_row_info('종료시간', data.end_time); + str += j_row_info('허용 Packet Fail 수', data.max_pf_count); + str += j_row_info('파일 Exist', data.exist); + if (data.status_str == 'COMPLETED') { + str += j_row_info('파일 크기', data.filesize_str); + str += j_row_info('다운 속도', data.download_speed); + } + return str; +} + +function button_html(data) { + //console.log(data) + str = ''; + if (data.status_str == 'DOWNLOADING') { + str = j_button('stop_btn', '중지', {'idx':data.idx}, 'danger', false, false); + } + $("#button_" + data.idx).html(str); +} + +function status_html(data) { + var progress = document.getElementById("progress_" + data.idx); + progress.style.width = data.percent+ '%'; + progress.innerHTML = data.percent+ '%'; + progress.style.visibility = 'visible'; + document.getElementById("status_" + data.idx).innerHTML = data.status_kor; + document.getElementById("current_pf_count_" + data.idx).innerHTML = data.current_pf_count; + document.getElementById("current_speed_" + data.idx).innerHTML = data.current_speed; + document.getElementById("download_time_" + data.idx).innerHTML = data.download_time; + document.getElementById("detail_" + data.idx).innerHTML = get_detail(data); +} + +</script> +{% endblock %} diff --git a/templates/anime_downloader_linkkf_request.html b/templates/anime_downloader_linkkf_request.html new file mode 100644 index 0000000..e1276c6 --- /dev/null +++ b/templates/anime_downloader_linkkf_request.html @@ -0,0 +1,639 @@ +{% extends "base.html" %} {% block content %} +<div id="anime_downloader_wrapper"> + <div id="preloader"> + <div class='demo'> + <!-- <div class="loader-inner">--> + <div class='circle'> + <div class='inner'></div> + </div> + <div class='circle'> + <div class='inner'></div> + </div> + <div class='circle'> + <div class='inner'></div> + </div> + <div class='circle'> + <div class='inner'></div> + </div> + <div class='circle'> + <div class='inner'></div> + </div> + <!-- </div>--> + </div> + </div> + <div> + <form id="program_list"> + {{ macros.setting_input_text_and_buttons('code', '작품 Code', + [['analysis_btn', '분석'], ['go_ohli24_btn', 'Go OHLI24']], desc='예) + "https://ohli24.net/c/녹을 먹는 비스코" 이나 "녹을 먹는 비스코"') }} + </form> + <form id="program_auto_form"> + <div id="episode_list"></div> + </form> + </div> +</div> +<!--전체--> +<link + href="{{ url_for('.static', filename='css/%s.css' % arg['sub']) +}}" + type="text/css" + rel="stylesheet" +/> +<script src="{{ url_for('.static', filename='js/sjva_ui14.js') }}"></script> + +<script type="text/javascript"> + + const package_name = "{{arg['package_name'] }}"; + const sub = "{{arg['sub'] }}"; + const ohli24_url = "{{arg['ohli24_url']}}"; + + + const params = new Proxy(new URLSearchParams(window.location.search), { + get: (searchParams, prop) => searchParams.get(prop), + }) + + const loader = document.getElementById("preloader"); + + const dismissLoadingScreen = function () { + loader.style.display = "none"; + $('.demo').css("display", "none") + }; + + const wait3seconds = function () { + // REFERENCE: https://www.w3schools.com/jsref/met_win_settimeout.asp + const result = setTimeout(dismissLoadingScreen, 2000); + }; + + window.addEventListener("load", wait3seconds); + + + function findGetParameter(parameterName) { + let result = null, + tmp = []; + const items = location.search.substr(1).split("&"); + for (let index = 0; index < items.length; index++) { + tmp = items[index].split("="); + if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]); + } + return result; + } + + function analyze(wr_id, bo_table) { + // e.preventDefault(); + const code = document.getElementById("code").value + console.log(code) + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/analysis', + type: "POST", + cache: false, + data: {code: code, wr_id: wr_id, bo_table: bo_table}, + dataType: "json", + success: function (ret) { + if (ret.ret === 'success' && ret.data != null) { + // {#console.log(ret.code)#} + console.log(ret.data) + make_program(ret.data) + } else { + $.notify('<strong>분석 실패</strong><br>' + ret.log, {type: 'warning'}); + } + } + }); + } + + function make_program(data) { + current_data = data; + + // $("body").css({"background": "url(" + data.poster_url + ")"}) + + // console.log('current_data:: ', data) + str = ""; + tmp = '<div class="form-inline w-100">'; + tmp += m_button("check_download_btn", "선택 다운로드 추가", []); + tmp += m_button("all_check_on_btn", "전체 선택", []); + tmp += m_button("all_check_off_btn", "전체 해제", []); + tmp += m_button("down_subtitle_btn", "자막만 전체 받기", []) + tmp += + '   <input id="new_title" name="new_title" class="form-control form-control-sm" value="' + + data.title + + '">'; + tmp += "</div>"; + tmp += '<div class="form-inline">'; + tmp += m_button("apply_new_title_btn", "저장폴더명 변경", []); + tmp += + '   <input id="new_season" name="new_season" class="form-control form-control-sm" value="' + + data.season + + '">'; + tmp += m_button("apply_new_season_btn", "시즌 변경 (숫자만 가능)", []); + tmp += m_button("search_tvdb_btn", "TVDB", []); + tmp += m_button("add_whitelist", "스케쥴링 추가", []); + + tmp += "</div>"; + tmp = m_button_group(tmp); + str += tmp; + // program + // str += m_hr_black(); + str += "<div class='card p-lg-5 mt-md-3 p-md-3 border-light'>" + + str += m_row_start(0); + tmp = ""; + if (data.poster_url != null) + tmp = '<img src="' + data.poster_url + '" class="img-fluid">'; + str += m_col(3, tmp); + tmp = ""; + tmp += m_row_start(0); + tmp += m_col(3, "제목", "right"); + tmp += m_col(9, data.title); + tmp += m_row_end(); + tmp += m_row_start(0); + tmp += m_col(3, "시즌", "right"); + tmp += m_col(9, data.season); + tmp += m_row_end(); + for (i in data.detail) { + tmp += m_row_start(0); + key = Object.keys(data.detail[i])[0]; + value = data.detail[i][key]; + tmp += m_col(3, key, "right"); + tmp += m_col(9, value); + tmp += m_row_end(); + } + + str += m_col(9, tmp); + str += m_row_end(); + + // str += m_hr_black(); + str += "</div>" + for (i in data.episode) { + str += m_row_start(); + // tmp = '<img src="' + data.episode[i].image + '" class="img-fluid">' + // str += m_col(3, tmp) + tmp = "<strong>" + data.episode[i].title + "</strong><span>화. </span>"; + tmp += data.episode[i].filename + "<br><p></p>"; + + tmp += '<div class="form-inline">'; + tmp += + '<input id="checkbox_' + + data.episode[i].code + + '" name="checkbox_' + + data.episode[i].code + + '" type="checkbox" checked data-toggle="toggle" data-on="선 택" data-off="-" data-onstyle="success" data-offstyle="danger" data-size="small">    '; + // tmp += m_button('add_queue_btn', '다운로드 추가', [{'key': 'code', 'value': data.episode[i].code}]) + tmp += m_button("add_queue_btn", "다운로드 추가", [ + {key: "idx", value: i}, + ]); + tmp += j_button('insert_download_btn', '다운로드 추가', { + code: data.episode[i]._id, + }); + tmp += j_button( + 'force_insert_download_btn', + '다운로드 추가 (DB무시)', + {code: data.episode[i]._id} + ); + // tmp += '<button id="play_video" name="play_video" class="btn btn-sm btn-outline-primary" data-idx="'+i+'">바로보기</button>'; + tmp += "</div>"; + str += m_col(12, tmp); + str += m_row_end(); + if (i != data.length - 1) str += m_hr(0); + } + document.getElementById("episode_list").innerHTML = str; + $('input[id^="checkbox_"]').bootstrapToggle(); + } + + $(function () { + console.log(params.wr_id) + console.log(findGetParameter('wr_id')) + console.log(params.code) + if (params.code === '') { + + } else { + document.getElementById("code").value = params.code + // {#document.getElementById("analysis_btn").click();#} + } + + if ("{{arg['ohli24_current_code']}}" !== "") { + if (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) + document.getElementById("code").value = params.code + + analyze(params.wr_id, params.bo_table) + // document.getElementById("analysis_btn").click(); + // $('#analysis_btn').trigger('click') + } + // 값이 공백이 아니면 분석 버튼 계속 누름 + // {#document.getElementById("analysis_btn").click();#} + } else { + + } + + }) + + $(document).ready(function () { + + console.log('wr_id::', params.wr_id) + + }); + + $("#analysis_btn").unbind("click").bind('click', function (e) { + e.preventDefault(); + e.stopPropagation() + const code = document.getElementById("code").value + console.log(code) + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/analysis', + type: "POST", + cache: false, + data: {code: code}, + dataType: "json", + success: function (ret) { + if (ret.ret === 'success' && ret.data != null) { + // {#console.log(ret.code)#} + console.log(ret.data) + make_program(ret.data) + } else { + $.notify('<strong>분석 실패</strong><br>' + ret.log, {type: 'warning'}); + } + } + }); + }); + + + $("body").on('click', '#go_ohli24_btn', function (e) { + e.preventDefault(); + window.open("{{arg['ohli24_url']}}", "_blank"); + }); + + $("body").on('click', '#all_check_on_btn', function (e) { + e.preventDefault(); + $('input[id^="checkbox_"]').bootstrapToggle('on') + }); + + $("body").on('click', '#all_check_off_btn', function (e) { + e.preventDefault(); + $('input[id^="checkbox_"]').bootstrapToggle('off') + }); + + $("body").on('click', '#add_queue_btn', function (e) { + e.preventDefault(); + data = current_data.episode[$(this).data('idx')]; + console.log('data:::>', data) + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/add_queue', + type: "POST", + cache: false, + data: {data: JSON.stringify(data)}, + dataType: "json", + success: function (data) { + console.log('#add_queue_btn::data >>', data) + if (data.ret == 'enqueue_db_append' || data.ret == 'enqueue_db_exist') { + $.notify('<strong>다운로드 작업을 추가 하였습니다.</strong>', {type: 'success'}); + } else if (data.ret == 'queue_exist') { + $.notify('<strong>이미 큐에 있습니다. 삭제 후 추가하세요.</strong>', {type: 'warning'}); + } else if (data.ret == 'db_completed') { + $.notify('<strong>DB에 완료 기록이 있습니다.</strong>', {type: 'warning'}); + } else { + $.notify('<strong>추가 실패</strong><br>' + ret.log, {type: 'warning'}); + } + } + }); + + }); + + $("body").on('click', '#check_download_btn', function (e) { + e.preventDefault(); + all = $('input[id^="checkbox_"]'); + let data = []; + let idx; + for (let i in all) { + if (all[i].checked) { + idx = parseInt(all[i].id.split('_')[1]) + data.push(current_data.episode[idx]); + } + } + if (data.length == 0) { + $.notify('<strong>선택하세요.</strong>', {type: 'warning'}); + return; + } + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/add_queue_checked_list', + type: "POST", + cache: false, + data: {data: JSON.stringify(data)}, + dataType: "json", + success: function (data) { + $.notify('<strong>백그라운드로 작업을 추가합니다.</strong>', {type: 'success'}); + } + }); + }); +</script> +<style> + #anime_downloader_wrapper { + font-family: NanumSquareNeo, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + } + + body { + background-image: linear-gradient(90deg, #33242c, #263341, #17273a); + + } + + #anime_downloader_wrapper { + + color: #d6eaf8; + } + + button.code-button { + min-width: 82px !important; + } + + .tooltip { + position: relative; + display: block; + } + + [data-tooltip-text]:hover { + position: relative; + } + + [data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: 90%; + + content: attr(data-tooltip-text); + } + + [data-tooltip-text]:hover:after { + top: 230%; + left: 0; + opacity: 1; + } + + [data-tooltip-text]:hover { + position: relative; + } + + [data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: -210% !important; + + content: attr(data-tooltip-text); + } + + [data-tooltip-text]:hover:after { + top: 130%; + left: 0; + opacity: 1; + } + + .card { + border: none; + box-shadow: inset 1px 1px hsl(0deg 0% 100% / 20%), inset -1px -1px hsl(0deg 0% 100% / 10%), 1px 3px 24px -1px rgb(0 0 0 / 15%); + background-color: transparent; + background-image: linear-gradient(125deg, hsla(0, 0%, 100%, .3), hsla(0, 0%, 100%, .2) 70%); + backdrop-filter: blur(5px); + } + + .card.border-light { + border-radius: 30px 10px; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; + } + + #airing_list { + display: none; + } + + .cut-text { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + } + + #screen_movie_list { + margin-top: 10px; + } + + /*@import url(https://fonts.googleapis.com/css?family=Lato);*/ + /*a {*/ + /* position: fixed;*/ + /* bottom: 2%;*/ + /* display: block;*/ + /* text-align: center;*/ + /* color: #0fa;*/ + /* font-family: "Lato", sans-serif;*/ + /* text-decoration: none !important;*/ + /* width: 100%;*/ + /*}*/ + + /*body, html {*/ + /* width: 100%;*/ + /* height: 100%;*/ + /* overflow: hidden;*/ + /*}*/ + + /*body {*/ + /* background: linear-gradient(90deg, #00b377, #00d68f);*/ + /* box-shadow: inset 0px 0px 90px rgba(0, 0, 0, 0.5);*/ + /* margin: 0px;*/ + /* padding: 0px;*/ + /*}*/ + + .demo { + width: 100px; + height: 102px; + border-radius: 100%; + position: absolute; + top: 45%; + left: calc(50% - 50px); + } + + .circle { + width: 100%; + height: 100%; + position: absolute; + } + + .circle .inner { + width: 100%; + height: 100%; + border-radius: 100%; + border: 5px solid rgba(0, 255, 170, 0.7); + border-right: none; + border-top: none; + backgroudn-clip: padding; + box-shadow: inset 0px 0px 10px rgba(0, 255, 170, 0.15); + } + + @-webkit-keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + .circle:nth-of-type(0) { + transform: rotate(0deg); + } + + .circle:nth-of-type(0) .inner { + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + } + + .circle:nth-of-type(1) { + transform: rotate(70deg); + } + + .circle:nth-of-type(1) .inner { + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + } + + .circle:nth-of-type(2) { + transform: rotate(140deg); + } + + .circle:nth-of-type(2) .inner { + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + } + + .demo { + -webkit-animation: spin 5s infinite linear; + animation: spin 5s infinite linear; + background: rgba(0, 0, 0, 0.2); + background: radial-gradient(#222, #000); + bottom: 0; + left: 0; + overflow: hidden; + /*position: fixed;*/ + right: 0; + /*top: 0;*/ + z-index: 99999; + opacity: 0.5; + margin: 0 auto; + transform: translate(-50%, -50%); + position: absolute; + top: 50%; + + } + + .circle { + width: 100%; + height: 100%; + position: absolute; + } + + .circle .inner { + width: 100%; + height: 100%; + border-radius: 100%; + border: 5px solid rgba(0, 255, 170, 0.7); + border-right: none; + border-top: none; + backgroudn-clip: padding; + box-shadow: inset 0px 0px 10px rgba(0, 255, 170, 0.15); + } + + .loader-inner { + bottom: 0; + height: 60px; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; + width: 100px; + } + + #preloader { + /*background-color: green;*/ + /*color: white;*/ + /*height: 100vh;*/ + /*width: 100%;*/ + /*position: fixed;*/ + /*z-index: 100;*/ + background: rgba(0, 0, 0, 0.2); + background: radial-gradient(#222, #000); + bottom: 0; + left: 0; + overflow: hidden; + position: fixed; + right: 0; + top: 0; + z-index: 99999; + opacity: 0.5; + } +</style> +{% endblock %} diff --git a/templates/anime_downloader_linkkf_search.html b/templates/anime_downloader_linkkf_search.html new file mode 100644 index 0000000..30625b4 --- /dev/null +++ b/templates/anime_downloader_linkkf_search.html @@ -0,0 +1,1183 @@ +{% extends "base.html" %} {% block content %} + <!--<div id="preloader"></div>--> + <div id="anime_downloader_wrapper"> + <div id="preloader" class="loader"> + <div class="loader-inner"> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + <div class="loader-line-wrap"> + <div class="loader-line"></div> + </div> + </div> + </div> + <div id="yommi_wrapper"> + <div class="input-group mb-2"> + <input + id="input_search" + type="search" + class="form-control rounded" + placeholder="Search" + aria-label="Search" + aria-describedby="search-addon" + /> + <button id="btn_search" type="button" class="btn btn-primary"> + search + </button> + </div> + + + <div + id="anime_category" + class="btn-group" + role="group" + aria-label="Linkkf Button" + > + <button id="ing" type="button" class="btn btn-success">방영중</button> + <button id="movie" type="button" class="btn btn-primary">극장판</button> + <button id="complete_anilist" type="button" class="btn btn-dark">완결</button> + <button id="top_view" type="button" class="btn btn-yellow">Top</button> + </div> + <form id="airing_list_form"> + <div id="airing_list"></div> + </form> + <form id="screen_movie_list_form"> + <div id="screen_movie_list" class="container"></div> + </form> + {# <div class="text-center">#} + {# <div id="spinner" class="spinner-border" role="status">#} + {# <span class="sr-only">Loading...</span>#} + {# </div>#} + {# </div>#} + <form id="program_auto_form"> + <div id="episode_list"></div> + </form> + + </div> + </div> + <!--전체--> + + <script + type="text/javascript" + src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js" + ></script> + <script src="{{ url_for('.static', filename='js/sjva_ui14.js') }}"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js" + integrity="sha512-jNDtFf7qgU0eH/+Z42FG4fw3w7DM/9zbgNPe3wfJlCylVDTT3IgKW5r92Vy9IHa6U50vyMz5gRByIu4YIXFtaQ==" + crossorigin="anonymous" referrerpolicy="no-referrer"></script> + <script type="text/javascript"> + const package_name = "{{arg['package_name'] }}"; + const sub = "{{arg['sub'] }}"; + const anilife_url = "{{arg['anilife_url']}}"; + // let current_data = null; + let page = 1; + let next_page = Number + let current_cate = '' + let current_query = '' + + const loader = document.getElementById("preloader"); + + const dismissLoadingScreen = function () { + loader.style.display = "none"; + }; + + const wait3seconds = function () { + // REFERENCE: https://www.w3schools.com/jsref/met_win_settimeout.asp + const result = setTimeout(dismissLoadingScreen, 2000); + }; + + window.addEventListener("load", wait3seconds); + // window.addEventListener("load", dismissLoadingScreen); + + + const observer = lozad('.lozad', { + rootMargin: '10px 0px', // syntax similar to that of CSS Margin + threshold: 0.1, // ratio of element convergence + enableAutoReload: true // it will reload the new image when validating attributes changes + }); + observer.observe(); + + + const get_anime_screen_movie = (page) => { + let data = {page: page}; + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/screen_movie_list', + type: "POST", + data: data, + cache: false, + dataType: "json", + success: (ret) => { + current_screen_movie_data = ret; + total_page = ret.total_page; + // console.log("ret::>", ret); + + if (current_screen_movie_data !== "") { + make_screen_movie_list(ret, page); + $("img.lazyload").lazyload({ + threshold: 100, + effect: "fadeIn", + }); + div_visible = true; + } + next_page = page + 1; + }, + }); + }; + + const get_anime_list = (type, page) => { + //console.log(`type: ${type}, page: ${page}`); + let url = '' + let data = {"page": page, "type": type} + + switch (type) { + case 'ing': + url = '/' + package_name + '/ajax/' + sub + '/anime_list' + current_cate = 'ing' + break; + case 'movie': + url = '/' + package_name + '/ajax/' + sub + '/screen_movie_list' + current_cate = 'movie' + break; + case 'complete': + url = '/' + package_name + '/ajax/' + sub + '/anime_list' + current_cate = 'complete' + break; + case 'top_view': + url = '/' + package_name + '/ajax/' + sub + '/complete_list' + current_cate = 'complete' + break + default: + break; + } + + $.ajax({ + url: url, + type: "POST", + data: data, + cache: false, + dataType: "json", + success: (ret) => { + current_screen_movie_data = ret + console.log('ret::>', ret) + + if (current_screen_movie_data !== '') { + if (type === "ing") { + + //make_airing_list(ret.data, page) + make_screen_movie_list(ret.data, page); + observer.observe(); + + } else if (type === "fin") { + make_screen_movie_list(ret.data, page) + observer.observe(); + } else if (type === "theater") { + make_screen_movie_list(ret.data, page) + observer.observe(); + } else { + make_screen_movie_list(ret.data, page) + } + div_visible = true + console.log(div_visible) + } + next_page = page + 1 + } + }); + } + + function make_airing_list(data, page) { + let str = '' + let tmp = '' + + str += '<div>'; + str += '<button type="button" class="btn btn-info">Page <span class="badge bg-warning">' + page + '</span></button>'; + str += '</div>'; + // str += '<div class="card-columns">' + str += '<div id="inner_screen_movie" class="row infinite-scroll">'; + for (let i in data.anime_list) { + + tmp = '<div class="col-6 col-sm-4 col-md-3">'; + tmp += '<div class="card">'; + // tmp += '<img class="lozad" data-src="' + data.anime_list[i].image_link + '" />'; + tmp += '<img class="lazyload" src="../static/img_loader_x200.svg" data-original="' + data.anime_list[i].image_link + '" style="cursor: pointer" onclick="location.href=\'./request?code=' + data.anime_list[i].code + '\'"/>'; + tmp += '<div class="card-body">' + // {#tmp += '<button id="code_button" data-code="' + data.episode[i].code + '" type="button" class="btn btn-primary code-button bootstrap-tooltip" data-toggle="button" data-tooltip="true" aria-pressed="true" autocomplete="off" data-placement="top">' +#} + // {# '<span data-tooltip-text="'+data.episode[i].title+'">' + data.episode[i].code + '</span></button></div>';#} + tmp += '<h5 class="card-title">' + data.anime_list[i].title + '</h5>'; + tmp += '<p class="card-text">' + data.anime_list[i].code + '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + + data.anime_list[i].code + + '"><i class="bi bi-heart-fill"></i></button></p>'; + tmp += '<a href="./request?code=' + data.anime_list[i].code + '" class="btn btn-primary cut-text">' + data.anime_list[i].title + '</a>'; + // tmp += + // '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + + // data.anime_list[i].code + + // '"><i class="bi bi-heart-fill"></i></button>'; + tmp += '</div><!-- .card -->'; + tmp += '</div>'; + tmp += '</div>'; + str += tmp + + } + str += '</div>'; + // str += '</div><!-- .card-columns -->'; + str += m_hr_black(); + + if (page > 1) { + + const temp = document.createElement('div') + temp.innerHTML = str; + while (temp.firstChild) { + document.getElementById("screen_movie_list").appendChild(temp.firstChild); + } + page++ + + } else { + + document.getElementById("screen_movie_list").innerHTML = str; + + } + + $("img.lazyload").lazyload({ + threshold: 10, + effect: "fadeIn", + }); + + } + + function make_search_result_list(data, page) { + let str = '' + let tmp = '' + + console.log(data.anime_list, page) + + str += '<div>'; + str += '<button type="button" class="btn btn-info">Page <span class="badge bg-warning">' + page + '</span></button>'; + str += '</div>'; + // str += '<div class="card-columns">' + str += '<div id="inner_screen_movie" class="row infinite-scroll">'; + for (let i in data.anime_list) { + 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) + 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 { + request_url = './request?code=' + data.anime_list[i].code + } + } else { + request_url = './request?code=' + data.anime_list[i].code + } + + tmp = '<div class="col-6 col-sm-4 col-md-3">'; + tmp += '<div class="card">'; + tmp += '<img class="card-img-top" src="' + data.anime_list[i].image_link + '" />'; + tmp += '<div class="card-body">' + // {#tmp += '<button id="code_button" data-code="' + data.episode[i].code + '" type="button" class="btn btn-primary code-button bootstrap-tooltip" data-toggle="button" data-tooltip="true" aria-pressed="true" autocomplete="off" data-placement="top">' +#} + // {# '<span data-tooltip-text="'+data.episode[i].title+'">' + data.episode[i].code + '</span></button></div>';#} + tmp += '<h5 class="card-title">' + data.anime_list[i].title + '</h5>'; + tmp += '<p class="card-text">' + data.anime_list[i].code + '</p>'; + tmp += '<a href="' + request_url + '" class="btn btn-primary cut-text">' + data.anime_list[i].title + '</a>'; + tmp += '</div>'; + tmp += '</div>'; + tmp += '</div>'; + str += tmp + + } + str += '</div>'; + str += '</div><!-- .card-columns -->'; + str += m_hr_black(); + + if (page > 1) { + + const temp = document.createElement('div') + temp.innerHTML = str; + while (temp.firstChild) { + document.getElementById("screen_movie_list").appendChild(temp.firstChild); + } + page++ + + } else { + document.getElementById("screen_movie_list").innerHTML = str; + } + + } + + function make_screen_movie_list(data, page) { + let str = ""; + let tmp = ""; + let new_anime = true; + let new_style = '' + console.log('page a: ', page) + console.log(data) + console.log(data.episode) + + let page_elem = ""; + if (page === undefined) { + } else { + page_elem = '<span class="badge bg-warning">' + page + "</span>"; + } + + // str += "<div id='page_caption' style='border-bottom: aqua 1px solid; margin-bottom: 5px'>"; + str += "<div id='page_caption' style='padding-bottom: 3px'>"; + str += + '<button type="button" class="btn btn-info">Page ' + + page_elem + + "</button>"; + str += "</div>"; + str += '<div id="inner_screen_movie" class="row infinite-scroll">'; + for (let i in data.episode) { + if (data.episode[i].code === data.latest_anime_code) { + new_anime = false + } + + if (new_anime && page === '1') { + new_style = 'new-anime'; + } else { + new_style = ''; + } + + tmp = '<div class="col-6 col-sm-4 col-md-3">'; + tmp += '<div class="card ' + new_style + '">'; + // tmp += '<div class="card-header">'; + + // tmp += + // '<img class="card-img-top lazyload" src="./static/img_loader_x200.svg" data-original="' + data.episode[i].image_link + '" />'; + tmp += + '<img class="card-img-top lazy" src="{{ url_for('.static', filename='img_loader_x200.svg') }}" data-lazy-src="' + + data.episode[i].image_link + + '" style="cursor: pointer" onclick="location.href=\'./request?code=' + + data.episode[i].code + + "'\"/>"; + if (current_cate === "ing") { + tmp += + '<span class="badge badge-danger badge-on-image">' + + data.episode[i].chapter + + "</span>"; + } + // tmp += '<div class="card-body '+ new_anime ? 'new-anime' : '' +'">'; + tmp += '<div class="card-body">'; + tmp += '<h5 class="card-title">' + data.episode[i].title + "</h5>"; + tmp += + '<button id="add_whitelist" name="add_whitelist" class="btn btn-sm btn-favorite mb-1" data-code="' + + data.episode[i].code + + '"><p class="card-text">' + + data.episode[i].code + + " <i class=\"bi bi-heart-fill\"></i></p></button>"; + tmp += + '<a href="./request?code=' + + data.episode[i].code + + '" class="btn btn-primary cut-text">' + + data.episode[i].title + + "</a>"; + tmp += "</div>"; + tmp += "</div>"; + // tmp += "</div>" + tmp += "</div>"; + str += tmp; + } + str += "</div>"; + str += m_hr_black(); + + if (page > 1) { + const temp = document.createElement("div"); + temp.innerHTML = str; + while (temp.firstChild) { + document.getElementById("screen_movie_list").appendChild(temp.firstChild); + } + page++; + } else { + document.getElementById("screen_movie_list").innerHTML = str; + } + $("img.lazyload").lazyload({ + threshold: 100, + effect: "fadeIn", + }); + } + + $(document).ready(function () { + + // if ( "{{arg['anilife_current_code']}}" !== "" ) { + // document.getElementById("code").value = "{{arg['anilife_current_code']}}"; + // // 값이 공백이 아니면 분석 버튼 계속 누름 + // document.getElementById("analysis_btn").click(); + // } + $("#input_search").keydown(function (key) { + if (key.keyCode === 13) { + // alert("엔터키를 눌렀습니다."); + $("#btn_search").trigger("click"); + } + }) + + get_anime_list("ing", 1) + + + const observer = lozad('.lozad', { + rootMargin: '10px 0px', // syntax similar to that of CSS Margin + threshold: 0.1, // ratio of element convergence + enableAutoReload: true // it will reload the new image when validating attributes changes + }); + observer.observe(); + + }); + + $("body").on("click", "#btn_search", function (e) { + e.preventDefault(); + let query = $("#input_search").val(); + console.log(query); + current_cate = "search" + current_query = query + + if ($("#input_search").val() === "") { + console.log("search keyword nothing"); + return false; + } + + $.ajax({ + url: "/" + package_name + "/ajax/" + sub + "/search", + type: "POST", + cache: false, + data: {query: query, type: current_cate, page: page}, + // dataType: "json", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + success: function (ret) { + if (ret.ret) { + console.log('ret:::', ret) + make_search_result_list(ret.data, 1); + next_page = page + 1 + } else { + $.notify("<strong>분석 실패</strong><br>" + ret.log, { + type: "warning", + }); + } + }, + }); + }); + + + $("#anime_category").on("click", function (e) { + // console.log($(this)) + // console.log(e) + + switch (e.target.id) { + case "ing": + console.log("ing.....") + + {#spinner_loading.style.display = "block";#} + current_cate = "ing"; + get_anime_list(1, "ing"); + break; + case "movie": + console.log("movie") + current_cate = "movie"; + get_anime_screen_movie(1); + break; + case "complete_anilist": + console.log("complete") + current_cate = "complete"; + get_complete_anilist(1); + break; + case "top_view": + console.log("top_view") + current_cate = "top_view"; + get_anime_list(1, "top_view"); + break; + default: + console.log("default") + spinner_loading.style.display = "block"; + current_cate = "ing"; + get_anime_list(1, "ing"); + break; + + } + }); + + /* + + // 분석 버튼 클릭시 호출 + $("body").on('click', '#analysis_btn', function (e) { + e.preventDefault(); + const code = document.getElementById("code").value + console.log(code) + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/analysis', + type: "POST", + cache: false, + data: {code: code}, + dataType: "json", + success: function (ret) { + if (ret.ret === 'success' && ret.data != null) { + // console.log(ret.code) + console.log(ret.data) + make_program(ret.data) + } else { + $.notify('<strong>분석 실패</strong><br>' + ret.log, {type: 'warning'}); + } + } + }); + }); + */ + + + $("body").on('click', '#go_anilife_btn', function (e) { + e.preventDefault(); + window.open("{{arg['anilife_url']}}", "_blank"); + }); + + $("body").on("click", "#add_whitelist", function (e) { + e.preventDefault(); + let data_code = $(this).attr("data-code"); + console.log(data_code); + $.ajax({ + url: "/" + package_name + "/ajax/" + sub + "/add_whitelist", + type: "POST", + cache: false, + data: JSON.stringify({data_code: data_code}), + contentType: "application/json;charset=UTF-8", + dataType: "json", + success: function (ret) { + if (ret.ret) { + $.notify("<strong>추가하였습니다.</strong><br>", { + type: "success", + }); + // make_program(ret); + } else { + $.notify("<strong>추가 실패</strong><br>" + ret.log, { + type: "warning", + }); + } + }, + }); + }); + + $("body").on('click', '#all_check_on_btn', function (e) { + e.preventDefault(); + $('input[id^="checkbox_"]').bootstrapToggle('on') + }); + + $("body").on('click', '#all_check_off_btn', function (e) { + e.preventDefault(); + $('input[id^="checkbox_"]').bootstrapToggle('off') + }); + + $("body").on('click', '#add_queue_btn', function (e) { + e.preventDefault(); + data = current_data.episode[$(this).data('idx')]; + console.log('data:::>', data) + $.ajax({ + url: '/' + package_name + '/ajax/' + sub + '/add_queue', + type: "POST", + cache: false, + data: {data: JSON.stringify(data)}, + dataType: "json", + success: function (data) { + if (data.ret == 'enqueue_db_append' || data.ret == 'enqueue_db_exist') { + $.notify('<strong>다운로드 작업을 추가 하였습니다.</strong>', {type: 'success'}); + } else if (data.ret == 'queue_exist') { + $.notify('<strong>이미 큐에 있습니다. 삭제 후 추가하세요.</strong>', {type: 'warning'}); + } else if (data.ret == 'db_completed') { + $.notify('<strong>DB에 완료 기록이 있습니다.</strong>', {type: 'warning'}); + } else { + $.notify('<strong>추가 실패</strong><br>' + ret.log, {type: 'warning'}); + } + } + }); + }); + // const observer = lozad(); + // 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) + + const loadNextAnimes = (cate, page, ch) => { + // spinner.style.display = "block"; + loader.style.display = "block"; + let data = {type: cate, page: String(page)}; + let url = '' + switch (cate) { + case 'ing': + url = '/' + package_name + '/ajax/' + sub + '/anime_list' + current_cate = 'ing' + break; + case 'movie': + url = '/' + package_name + '/ajax/' + sub + '/screen_movie_list' + current_cate = 'movie' + break; + case 'theater': + url = '/' + package_name + '/ajax/' + sub + '/anime_list' + current_cate = 'theater' + break; + case 'fin': + url = '/' + package_name + '/ajax/' + sub + '/complete_list' + current_cate = 'fin' + break + case 'search': + url = "/" + package_name + "/ajax/" + sub + "/search" + current_cate = 'search' + data.query = current_query + break; + default: + break; + } + + fetch(url, { + method: "POST", + cache: "no-cache", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams(data), + }) + .then((res) => res.json()) + .then((response) => { + // console.log("Success:", JSON.stringify(response)); + // {#imagesContainer.appendChild()#} + console.log("return page:::> ", String(response.page)); + // {#page = response.page#} + loader.style.display = "block"; + if (current_cate === 'search') { + make_search_result_list(response.data, response.page); + + } else { + make_screen_movie_list(response.data, response.page); + } + + console.log(document.body.scrollHeight) + console.log(ch) + window.scrollBy({ + top: ch + 35, + left: 0, + behavior: 'smooth' + }); + + loader.style.display = "none"; + page++; + next_page++; + }) + .catch((error) => console.error("Error:", error)); + }; + + + const onScroll = (e) => { + console.dir(e.target.scrollingElement.scrollHeight); + 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)); + loadNextAnimes(current_cate, next_page, clientHeight); + /*window.scrollBy({ + top: e.target.scrollingElement.scrollHeight + 200, + left: 0, + behavior: 'smooth' + }); + */ + + + } + }; + + const debounce = (func, delay) => { + let timeoutId = null; + return (...args) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(func.bind(null, ...args), delay); + }; + }; + + document.addEventListener("scroll", debounce(onScroll, 300)); + window.lazyLoadOptions = { + elements_selector: + "img[data-lazy-src],.rocket-lazyload,iframe[data-lazy-src]", + data_src: "lazy-src", + data_srcset: "lazy-srcset", + data_sizes: "lazy-sizes", + class_loading: "lazyloading", + class_loaded: "lazyloaded", + threshold: 50, + callback_loaded: function (element) { + if ( + element.tagName === "IFRAME" && + element.dataset.rocketLazyload == "fitvidscompatible" + ) { + if (element.classList.contains("lazyloaded")) { + if (typeof window.jQuery != "undefined") { + if (jQuery.fn.fitVids) { + jQuery(element).parent().fitVids(); + } + } + } + } + }, + }; + window.addEventListener( + "LazyLoad::Initialized", + function (e) { + var lazyLoadInstance = e.detail.instance; + if (window.MutationObserver) { + var observer = new MutationObserver(function (mutations) { + var image_count = 0; + var iframe_count = 0; + var rocketlazy_count = 0; + mutations.forEach(function (mutation) { + for (var i = 0; i < mutation.addedNodes.length; i++) { + if ( + typeof mutation.addedNodes[i].getElementsByTagName !== "function" + ) { + continue; + } + if ( + typeof mutation.addedNodes[i].getElementsByClassName !== + "function" + ) { + continue; + } + images = mutation.addedNodes[i].getElementsByTagName("img"); + is_image = mutation.addedNodes[i].tagName == "IMG"; + iframes = mutation.addedNodes[i].getElementsByTagName("iframe"); + is_iframe = mutation.addedNodes[i].tagName == "IFRAME"; + rocket_lazy = + mutation.addedNodes[i].getElementsByClassName("rocket-lazyload"); + image_count += images.length; + iframe_count += iframes.length; + rocketlazy_count += rocket_lazy.length; + if (is_image) { + image_count += 1; + } + if (is_iframe) { + iframe_count += 1; + } + } + }); + if (image_count > 0 || iframe_count > 0 || rocketlazy_count > 0) { + lazyLoadInstance.update(); + } + }); + var b = document.getElementsByTagName("body")[0]; + var config = { + childList: !0, + subtree: !0, + }; + observer.observe(b, config); + } + }, + !1 + ); + + </script> + <script + src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js" + integrity="sha512-jNDtFf7qgU0eH/+Z42FG4fw3w7DM/9zbgNPe3wfJlCylVDTT3IgKW5r92Vy9IHa6U50vyMz5gRByIu4YIXFtaQ==" + crossorigin="anonymous" + referrerpolicy="no-referrer" + ></script> + <script + async + src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.7.0/dist/lazyload.min.js" + ></script> + + <style> + #anime_downloader_wrapper { + font-family: NanumSquareNeo, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + } + + body { + background-image: linear-gradient(90deg, #33242c, #263341, #17273a); + + } + + #anime_downloader_wrapper { + + color: #d6eaf8; + } + + button.code-button { + min-width: 82px !important; + } + + .tooltip { + position: relative; + display: block; + } + + + [data-tooltip-text]:hover { + position: relative; + } + + [data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: 90%; + + content: attr(data-tooltip-text); + } + + [data-tooltip-text]:hover:after { + top: 230%; + left: 0; + opacity: 1; + } + + [data-tooltip-text]:hover { + position: relative; + } + + [data-tooltip-text]:after { + -webkit-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + -moz-transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + transition: bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; + + background-color: rgba(0, 0, 0, 0.8); + + -webkit-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + -moz-box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + box-shadow: 0px 0px 3px 1px rgba(50, 50, 50, 0.4); + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + color: #ffffff; + font-size: 12px; + margin-bottom: 10px; + padding: 7px 12px; + position: absolute; + width: auto; + min-width: 50px; + max-width: 300px; + word-wrap: break-word; + + z-index: 9999; + + opacity: 0; + left: -9999px; + top: -210% !important; + + content: attr(data-tooltip-text); + } + + [data-tooltip-text]:hover:after { + top: 130%; + left: 0; + opacity: 1; + } + + #airing_list { + display: none; + } + + .cut-text { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + } + + .badge-on-image { + position: absolute; + top: 2px; + /*bottom: 2px; !* position where you want it *!*/ + right: 2px; + padding: 5px 12px; + } + + #screen_movie_list { + margin-top: 10px; + } + + .card-body { + padding: 0 !important; + } + + .card-title { + padding: 1rem !important; + } + + button#add_whitelist { + float: right; + } + + button.btn-favorite { + background-color: #e0ff42; + } + + /*.card-columns {*/ + /* @include media-breakpoint-only(lg) {*/ + /* column-count: 4;*/ + /* }*/ + /* @include media-breakpoint-only(xl) {*/ + /* column-count: 5;*/ + /* }*/ + /*}*/ + @media (min-width: 576px) { + .container { + max-width: 100%; + } + + .card-columns { + column-count: 2; + column-gap: 1.25rem; + } + + .card-columns .card { + display: inline-block; + } + } + + + @media (min-width: 768px) { + .card-columns { + column-count: 3; + } + } + + /* Large devices (desktops, 992px and up) */ + @media (min-width: 992px) { + .card-columns { + column-count: 3; + } + } + + /* Extra large devices (large desktops, 1200px and up) */ + @media (min-width: 1200px) { + .card-columns { + column-count: 5; + } + + #yommi_wrapper { + max-width: 80%; + margin: 0 auto; + } + } + + .card { + margin-bottom: 0.75rem; + } + + .card-columns .card { + margin-bottom: 0.75rem; + } + + .card-columns .card img { + width: 100%; + } + + button#add_whitelist { + /*top: -70px;*/ + position: relative; + } + + body { + font-family: NanumSquareNeo, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + } + + body { + background-image: linear-gradient(90deg, #233f48, #6c6fa2, #768dae); + } + + #preloader { + /*background-color: green;*/ + /*color: white;*/ + /*height: 100vh;*/ + /*width: 100%;*/ + /*position: fixed;*/ + /*z-index: 100;*/ + background: rgba(0, 0, 0, 0.2); + background: radial-gradient(#222, #000); + bottom: 0; + left: 0; + overflow: hidden; + position: fixed; + right: 0; + top: 0; + z-index: 99999; + opacity: 0.5; + } + + /*.loader {*/ + /* background: rgb(0, 0, 0, 0.8);*/ + /* background: radial-gradient(#222, #000);*/ + /* bottom: 0;*/ + /* left: 0;*/ + /* overflow: hidden;*/ + /* position: fixed;*/ + /* right: 0;*/ + /* top: 0;*/ + /* z-index: 99999;*/ + /*}*/ + + .loader-inner { + bottom: 0; + height: 60px; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; + width: 100px; + } + + .loader-line-wrap { + animation: spin 2000ms cubic-bezier(.175, .885, .32, 1.275) infinite; + box-sizing: border-box; + height: 50px; + left: 0; + overflow: hidden; + position: absolute; + top: 0; + transform-origin: 50% 100%; + width: 100px; + } + + .loader-line { + border: 4px solid transparent; + border-radius: 100%; + box-sizing: border-box; + height: 100px; + left: 0; + margin: 0 auto; + position: absolute; + right: 0; + top: 0; + width: 100px; + } + + .loader-line-wrap:nth-child(1) { + animation-delay: -50ms; + } + + .loader-line-wrap:nth-child(2) { + animation-delay: -100ms; + } + + .loader-line-wrap:nth-child(3) { + animation-delay: -150ms; + } + + .loader-line-wrap:nth-child(4) { + animation-delay: -200ms; + } + + .loader-line-wrap:nth-child(5) { + animation-delay: -250ms; + } + + .loader-line-wrap:nth-child(1) .loader-line { + border-color: hsl(0, 80%, 60%); + height: 90px; + width: 90px; + top: 7px; + } + + .loader-line-wrap:nth-child(2) .loader-line { + border-color: hsl(60, 80%, 60%); + height: 76px; + width: 76px; + top: 14px; + } + + .loader-line-wrap:nth-child(3) .loader-line { + border-color: hsl(120, 80%, 60%); + height: 62px; + width: 62px; + top: 21px; + } + + .loader-line-wrap:nth-child(4) .loader-line { + border-color: hsl(180, 80%, 60%); + height: 48px; + width: 48px; + top: 28px; + } + + .loader-line-wrap:nth-child(5) .loader-line { + border-color: hsl(240, 80%, 60%); + height: 34px; + width: 34px; + top: 35px; + } + + @keyframes spin { + 0%, 15% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } + } + + .card-body { + padding: 0 !important; + } + + .new-anime { + border-color: darksalmon; + border-width: 4px; + border-style: dashed; + + } + + .card-title { + padding: 1rem !important; + } + + button#add_whitelist { + float: right; + } + + button.btn-favorite { + background-color: #e0ff42; + } + + + body { + font-family: NanumSquareNeo, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Noto Sans, Liberation Sans, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + } + + body { + background-image: linear-gradient(90deg, #233f48, #6c6fa2, #768dae); + + </style> + <link + href="{{ url_for('.static', filename='css/bootstrap.min.css') }}" + type="text/css" + rel="stylesheet" + /> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"> +{% endblock %} diff --git a/templates/anime_downloader_linkkf_setting.html b/templates/anime_downloader_linkkf_setting.html new file mode 100644 index 0000000..1b4d11a --- /dev/null +++ b/templates/anime_downloader_linkkf_setting.html @@ -0,0 +1,68 @@ +{% extends "base.html" %} +{% block content %} +<div> + {{ macros.m_button_group([['global_setting_save_btn', '설정 저장']])}} + {{ macros.m_row_start('5') }} + {{ macros.m_row_end() }} + <nav> + {{ macros.m_tab_head_start() }} + {{ macros.m_tab_head2('normal', '일반', true) }} + {{ macros.m_tab_head2('auto', '홈화면 자동', false) }} + {{ macros.m_tab_head2('action', '기타', false) }} + {{ macros.m_tab_head_end() }} + </nav> + <form id="setting"> + <div class="tab-content" id="nav-tabContent"> + {{ macros.m_tab_content_start('normal', true) }} + {{ macros.setting_input_text_and_buttons('linkkf_url', 'linkkf URL', [['go_btn', 'GO']], value=arg['linkkf_url']) }} + {{ macros.setting_input_text('linkkf_download_path', '저장 폴더', value=arg['linkkf_download_path'], desc='정상적으로 다운 완료 된 파일이 이동할 폴더 입니다. ') }} + {{ macros.setting_input_int('linkkf_max_ffmpeg_process_count', '동시 다운로드 수', value=arg['linkkf_max_ffmpeg_process_count'], desc='동시에 다운로드 할 에피소드 갯수입니다.') }} + {{ macros.setting_checkbox('linkkf_order_desc', '요청 화면 최신순 정렬', value=arg['linkkf_order_desc'], desc='On : 최신화부터, Off : 1화부터') }} + {{ macros.setting_checkbox('linkkf_auto_make_folder', '제목 폴더 생성', value=arg['linkkf_auto_make_folder'], desc='제목으로 폴더를 생성하고 폴더 안에 다운로드합니다.') }} + <div id="linkkf_auto_make_folder_div" class="collapse"> + {{ macros.setting_input_text('linkkf_finished_insert', '완결 표시', col='3', value=arg['linkkf_finished_insert'], desc=['완결된 컨텐츠 폴더명 앞에 넣을 문구입니다.']) }} + {{ macros.setting_checkbox('linkkf_auto_make_season_folder', '시즌 폴더 생성', value=arg['linkkf_auto_make_season_folder'], desc=['On : Season 번호 폴더를 만듭니다.']) }} + </div> + {{ macros.setting_checkbox('linkkf_uncompleted_auto_enqueue', '자동으로 다시 받기', value=arg['linkkf_uncompleted_auto_enqueue'], desc=['On : 플러그인 로딩시 미완료인 항목은 자동으로 다시 받습니다.']) }} + {{ macros.m_tab_content_end() }} + + {{ macros.m_tab_content_start('auto', false) }} + {{ macros.setting_global_scheduler_sub_button(arg['scheduler'], arg['is_running']) }} + {{ macros.setting_input_text('linkkf_interval', '스케쥴링 실행 정보', value=arg['linkkf_interval'], col='3', desc=['Inverval(minute 단위)이나 Cron 설정']) }} + {{ macros.setting_checkbox('linkkf_auto_start', '시작시 자동실행', value=arg['linkkf_auto_start'], desc='On : 시작시 자동으로 스케쥴러에 등록됩니다.') }} + {{ macros.setting_input_textarea('linkkf_auto_code_list', '자동 다운로드할 작품 코드', desc=['all 입력시 모두 받기', '구분자 | 또는 엔터'], value=arg['linkkf_auto_code_list'], row='10') }} + {{ macros.setting_checkbox('linkkf_auto_mode_all', '에피소드 모두 받기', value=arg['linkkf_auto_mode_all'], desc=['On : 이전 에피소드를 모두 받습니다.', 'Off : 최신 에피소드만 받습니다.']) }} + {{ macros.m_tab_content_end() }} + + {{ macros.m_tab_content_start('action', false) }} + {{ macros.setting_button([['global_one_execute_sub_btn', '1회 실행']], left='1회 실행' ) }} + {{ macros.setting_button([['global_reset_db_sub_btn', 'DB 초기화']], left='DB정리' ) }} + {{ macros.m_tab_content_end() }} + + </div><!--tab-content--> + </form> +</div> <!--전체--> + +<script type="text/javascript"> +var package_name = "{{arg['package_name'] }}"; +var sub = "{{arg['sub'] }}"; +var current_data = null; + + +$(document).ready(function(){ + use_collapse('linkkf_auto_make_folder'); +}); + +$('#ani365_auto_make_folder').change(function() { + use_collapse('linkkf_auto_make_folder'); +}); + + +$("body").on('click', '#go_btn', function(e){ + e.preventDefault(); + let url = document.getElementById("linkkf_url").value + window.open(url, "_blank"); +}); + +</script> +{% endblock %} \ No newline at end of file diff --git a/templates/anime_downloader_ohli24_category.html b/templates/anime_downloader_ohli24_category.html index 23c2c11..8aee5c7 100644 --- a/templates/anime_downloader_ohli24_category.html +++ b/templates/anime_downloader_ohli24_category.html @@ -44,11 +44,7 @@ <form id="screen_movie_list_form"> <div id="screen_movie_list" class="container"></div> </form> -<!-- <div class="text-center">--> -<!-- <div id="spinner" class="spinner-border" role="status">--> -<!-- <span class="sr-only">Loading...</span>--> -<!-- </div>--> -<!-- </div>--> + <form id="program_auto_form"> <div id="episode_list"></div> </form> @@ -152,6 +148,7 @@ } function make_airing_list(data, page) { + console.log("call make_airing_list()") let str = "" let tmp = ""