Compare commits

...

170 Commits

Author SHA1 Message Date
a8486726f6 2024.08.21 patch.03 2024-08-21 19:39:50 +09:00
391a0ee861 2024.08.21 patch.02 2024-08-21 19:24:29 +09:00
408be433f2 2024.08.21 patch.01 2024-08-21 19:13:43 +09:00
c87e29f085 2024.08.13 19:22 patch.01 2024-08-13 19:26:16 +09:00
b27cd39aa4 2024.08.13 19:22 2024-08-13 19:22:47 +09:00
205c17ae4e edit fix3 2024-02-16 19:00:01 +09:00
e101a02886 error fix2 2024-02-16 15:07:14 +09:00
04c0e34db5 error fix 2024-02-13 15:23:57 +09:00
f1d5f1db68 main -> 2023.10.8 fix "작화" 2023-10-08 22:29:02 +09:00
f0eda8ef87 main -> 2023.10.5 fix 2023-10-05 21:20:18 +09:00
d4fcc9a633 main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-23 22:48:21 +09:00
9ca8dcc3da main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-20 21:57:21 +09:00
301806a906 main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-20 14:40:13 +09:00
eca29b6947 main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-20 14:32:56 +09:00
d07cc820dc main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-19 23:47:52 +09:00
710d70dbfd main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결)a 2023-09-19 23:42:16 +09:00
6f2edeaf89 main -> 2023.09.20 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-09-19 23:37:50 +09:00
769d40e5bb main -> 2023.08.31 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-08-31 00:20:57 +09:00
c53f1f50c9 main -> 2023.08.07 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-08-12 21:22:28 +09:00
9cae04584d main -> 2023.08.07 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-08-07 19:16:59 +09:00
efcadde111 main -> 2023.08.07 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-08-07 19:08:46 +09:00
145e277895 main -> 2023.08.07 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-08-07 19:06:08 +09:00
1b76d36352 main -> 2023.07.01 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-07-01 00:13:09 +09:00
a7cf43e0cc main -> 2023.07.01 ohli24 버그 픽스 (.01. 다운로드 문제 해결) 2023-07-01 00:09:52 +09:00
dd8a68a267 main -> 2023.05.11 ohli24 버그 픽스 (.01. todo: setting_save_after) 2023-05-11 19:41:39 +09:00
6bf816db10 main -> 2023.05.09 ohli24 버그 픽스 (.01. referer url fix) 2023-05-09 20:56:57 +09:00
becfc7feef main -> 2023.05.09 ohli24 버그 픽스 (.01. referer url fix) 2023-05-09 20:54:59 +09:00
25cddecfe9 main -> 2023.04.22 ohli24 버그 픽스 (.01. img xpath fix) 2023-04-22 23:26:19 +09:00
292a3fd388 main -> 2023.03.07 ohli24 버그 픽스 (.01. img xpath fix) 2023-03-07 21:11:49 +09:00
87461cce4a main -> 2023.03.07 ohli24 버그 픽스 (.01. img xpath fix) 2023-03-07 20:35:22 +09:00
080ae6ab0c main -> 2023.03.07 ohli24 버그 픽스 (.01. img xpath fix) 2023-03-07 20:26:38 +09:00
c8284f86b7 main -> 2023.03.03 ohli24 버그 픽스 (.01. img xpath fix) 2023-03-03 18:54:10 +09:00
f4717c74e4 main -> 2023.03.01 ohli24 버그 픽스 (.02. code cleanup) 2023-03-01 19:34:53 +09:00
c6940bbca5 main -> 2023.03.01 ohli24 버그 픽스 (.01. code cleanup) 2023-03-01 18:05:58 +09:00
5506cc2e7f main -> 2023.03.01 ohli24 버그 픽스 (.01. code cleanup) 2023-03-01 18:03:56 +09:00
10bd5e7412 main -> 2023.03.01 ohli24 버그 픽스 (.01. code cleanup) 2023-03-01 18:01:39 +09:00
3162911f1e 2023.02.01 ohli24 버그 픽스 (.04. code cleanup) 2023-02-01 19:35:19 +09:00
7f00ca6055 2023.02.01 ohli24 버그 픽스 (.03. 코드 정리) 2023-02-01 19:22:06 +09:00
367cb85657 2023.02.01 ohli24 버그 픽스 (.02. etc) 2023-02-01 14:53:18 +09:00
9be3e03c2d 2023.02.01 linkkf 버그 픽스 (.01. etc) 2023-02-01 14:50:17 +09:00
2c67b0cacd 2022.01.31 linkkf 버그 픽스 (.07. 기타) 2023-01-31 23:42:51 +09:00
6c198dcc76 2022.01.31 linkkf 버그 픽스 (.06. 기타) 2023-01-31 23:09:12 +09:00
e37a3c652b 2022.01.31 linkkf 버그 픽스 (.05. 기타) 2023-01-31 22:46:52 +09:00
0e45dc8de7 2022.01.31 anilife 버그 픽스 (.04. 기타) 2023-01-31 21:27:04 +09:00
52c0802572 2022.01.31 anilife 버그 픽스 (.04. 기타) 2023-01-31 21:20:17 +09:00
361490fc43 2022.01.31 anilife 버그 픽스 (.04. 기타) 2023-01-31 21:19:40 +09:00
b4641b6591 2022.01.31 anilife 버그 픽스 (.03. 기타) 2023-01-31 19:09:25 +09:00
ab343b92b7 2022.01.31 anilife 버그 픽스 (.02. 기타) 2023-01-31 18:21:25 +09:00
548902e095 2022.01.31 anilife 버그 픽스 (.01) 2023-01-31 17:21:38 +09:00
e92c5f1c47 2022.01.31 anilife 버그 픽스 2023-01-31 15:04:49 +09:00
b079e75029 2022.01.31 anilife 버그 픽스 2023-01-31 01:33:06 +09:00
5483b57a50 2022.01.31 anilife 버그 픽스 2023-01-31 01:20:35 +09:00
fd380c1890 2022.01.30 버그 픽스 2023-01-31 00:26:47 +09:00
957cffe48d 2022.01.30 버그 픽스 2023-01-30 23:23:37 +09:00
022347688d remove cached.. 2023-01-30 19:53:51 +09:00
3675cb0538 2022.01.30 버그 픽스 2023-01-30 19:53:06 +09:00
4fbbbf5122 2022.01.30 버그 픽스 2023-01-30 19:52:16 +09:00
4fc4181e4c 2022.01.30 버그 픽스 2023-01-30 19:50:26 +09:00
d944ca48f2 remove cached.. 2023-01-30 19:49:21 +09:00
9907d6b484 2022.01.30 버그 픽스 2023-01-30 19:41:18 +09:00
cae6985aec 2022.01.30 버그 픽스 2023-01-30 19:26:57 +09:00
a9ea8f193f 2022.01.29 bug fix 2023-01-29 21:21:50 +09:00
ab346ccb19 최신화 정렬 부분 구현(2023. 01. 29)[01] 2023-01-29 00:42:01 +09:00
7a529aa27b ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02-03)
2023-01-28 23:27:16 +09:00
4527e54647 ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02-03)
2023-01-28 20:10:41 +09:00
d6aa3ff42b ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02-03)
2023-01-28 20:03:58 +09:00
1ec1100f52 ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02-02)
2023-01-28 19:59:30 +09:00
dffddb7d28 ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02-01)
2023-01-28 19:57:08 +09:00
21ef7c4bfc ohli24 api update 2023.01.18(01.)
anilife api update 2023.01.18(02.)
2023-01-28 19:50:26 +09:00
af965a7ef9 ohli24 api update 2023.01.18(01.) 2023-01-28 19:31:44 +09:00
09bb3e4a8a ohli24 api update 2023.01.18(01.) 2023-01-28 19:25:16 +09:00
3ac8f3af8e ohli24 api update 2023.01.15(02.) 2023-01-15 17:43:12 +09:00
a3a6a84c97 ohli24 api update 2023.01.15(02.) 2023-01-15 17:10:34 +09:00
1111d37074 ohli24 api update 2023.01.15(02.) 2023-01-15 17:05:52 +09:00
48bb5dd489 ohli24 api update 2023.01.15(02.) 2023-01-15 17:02:14 +09:00
6a309e0e11 ohli24 api update 2023.01.15(01.) 2023-01-15 16:46:48 +09:00
7a82dc2fc7 ohli24 api update 2023.01.13(01.) 2023-01-14 18:22:57 +09:00
b4b567704c ohli24 api update 2023.01.13(01.) 2023-01-13 23:30:54 +09:00
8a78d40c15 ohli24 api update 2023.01.13(01.) 2023-01-13 21:57:59 +09:00
11afb7bf38 ohli24 api update 2023.01.13(01.) 2023-01-13 20:14:11 +09:00
2af1e6d738 ohli24 api update 2022.12.21(01.) 2022-12-21 21:25:56 +09:00
9f1de7aa8c ohli24 api update 2022.11.25(01.) 2022-11-25 19:20:52 +09:00
bfa64d4b8f anilife update 2022.11.09(01.) 2022-11-09 17:27:09 +09:00
027e41be46 2022-10-30 (일부수정) 2022-10-30 05:22:00 +09:00
ce13a3e829 ohli24.net update 2022.10.30(01.) 2022-10-30 00:14:17 +09:00
8e5c73c686 first commit 2022-10-29 16:50:40 +09:00
92771db871 anilife update 2022.10.28(01.) 2022-10-28 16:14:33 +09:00
4fef22f3df anilife update 2022.10.24(03.) 2022-10-25 00:29:16 +09:00
a8138d4a71 anilife update 2022.10.24(02.) 2022-10-24 04:42:17 +09:00
af3c43128d anilife update 2022.10.24(02.) 2022-10-24 04:36:51 +09:00
3b2c92a0e3 anilife update 2022.10.24(02.) 2022-10-24 04:22:25 +09:00
2ddda383c5 anilife update 2022.10.24(02.) 2022-10-24 04:21:49 +09:00
47a6407dfe anilife update 2022.10.24(02.) 2022-10-24 04:13:11 +09:00
af73adf1aa anilife update 2022.10.24(01.) 2022-10-24 03:59:32 +09:00
3102ba50ad anilife update 2022.10.23(01.) 2022-10-24 03:59:23 +09:00
6e9b71f4cc anilife update 2022.10.24(01.) 2022-10-24 01:42:47 +09:00
5ff663e288 anilife update 2022.10.23(01.) 2022-10-24 00:49:35 +09:00
11a933a4f6 anilife update 2022.10.23(01.) 2022-10-24 00:18:12 +09:00
a426a57824 anilife update 2022.10.23(01.) 2022-10-23 23:36:05 +09:00
a900247086 anilife update 2022.10.23(01.) 2022-10-23 23:23:46 +09:00
2aa073f504 anilife update 2022.10.23(01.) 2022-10-23 23:01:29 +09:00
cf5ec2e073 anilife update 2022.10.23(01.) 2022-10-23 23:01:19 +09:00
f033c96878 anilife update 2022.10.23(01.) 2022-10-23 22:51:55 +09:00
64eec69efe anilife update 2022.10.23(01.) 2022-10-23 22:44:58 +09:00
bf964a6def anilife update 2022.10.23(01.) 2022-10-23 22:23:56 +09:00
a6541ce299 anilife update 2022.10.23(01.) 2022-10-23 22:14:04 +09:00
3872bd7706 자기전 정리 2022.10.21(01.) 2022-10-23 21:24:28 +09:00
be00760dc2 anilife update 2022.10.23(01.) 2022-10-23 21:22:33 +09:00
a0ce90148f 자기전 정리 2022.10.21(01.) 2022-10-21 18:11:27 +09:00
8755606986 자기전 정리 2022.10.21(01.) 2022-10-21 18:05:03 +09:00
0462792bfb 자기전 정리 2022.10.21(01.) 2022-10-21 18:01:22 +09:00
b04d361805 자기전 정리 2022.10.17(01.) 2022-10-17 23:01:28 +09:00
9adb4f73ee 2022.10.15(01.) 2022-10-15 19:35:46 +09:00
b13dbeb2d8 애니라이프 추가 및 애니24 수정 2022.09.25
os platform fix
2022-09-27 13:18:21 +09:00
b08f45bdc8 애니라이프 추가 및 애니24 수정 2022.09.25
os platform fix
2022-09-27 13:15:55 +09:00
4e5498d6c8 애니라이프 추가 및 애니24 수정 2022.09.25
os platform fix
2022-09-27 13:08:25 +09:00
507a5b9053 애니라이프 추가 및 애니24 수정 2022.09.25
selenium-stealth package import error fixed
2022-09-25 17:41:42 +09:00
1fa8b3f178 애니라이프 추가 및 애니24 수정 2022.09.25
selenium-stealth package import error fixed
2022-09-25 17:38:23 +09:00
02a7d33f35 애니라이프 추가 및 애니24 수정 2022.09.12 2022-09-12 15:22:03 +09:00
270ea09336 애니라이프 추가 및 애니24 수정 2022-09-09 20:30:23 +09:00
62c790b687 애니라이프 추가 및 애니24 수정 2022-08-26 17:21:32 +09:00
c74530b041 애니라이프 추가 및 애니24 수정 2022-08-22 16:55:30 +09:00
1e6e5d7d0f 애니라이프 추가 및 애니24 수정 2022-08-22 16:33:02 +09:00
7c407afede 애니라이프 추가 및 애니24 수정 2022-08-20 23:21:59 +09:00
12fb2e65ce 애니라이프 추가 및 애니24 수정 2022-08-20 23:12:58 +09:00
7def5579df 애니라이프 추가 및 애니24 수정 2022-08-20 23:01:21 +09:00
801145b625 상영일 버그
bug fixed 2022.08.05
2022-08-05 19:33:24 +09:00
209f69d0a7 bug fixed 2022.06.26 2022-06-26 21:46:16 +09:00
d9baf0704c anime_download style fix 2022-06-07 20:00:29 +09:00
b5a56d8560 bugfix 2022-05-22 15:13:31 +09:00
60fbb7a507 bugfix 2022-05-22 15:06:45 +09:00
ce07ca12e2 bug fix 1 2022-05-08 19:36:16 +09:00
ce1db623e6 소소한 정리 3 2022-04-27 15:29:17 +09:00
a9004c513b 검색오류 수정 2022-04-17 18:00:43 +09:00
18f3f00098 오류 수정 2022-04-17 17:20:45 +09:00
0d359a184f 오류 수정 2022-04-17 14:07:00 +09:00
a63baf44f4 오류 수정 2022-04-17 13:45:37 +09:00
9779d82ad7 video hls url quality 누락시 720p 고정 2022-04-14 23:06:34 +09:00
1bebd593f7 IndexError 2022-04-13 15:19:20 +09:00
62356738bb IndexError 2022-04-13 15:18:08 +09:00
4da03ab7ff blueprint add static_folder 2022-04-12 23:58:58 +09:00
0ad6c9c59a blueprint add static_folder 2022-04-12 23:57:13 +09:00
bfb7436b07 linkkf-yommi 기능 추가및 버그 픽스 3.
infinite 루틴 에러 수정
2022-04-10 20:56:42 +09:00
c091ce916a linkkf-yommi 기능 추가및 버그 픽스 2.
infinite 루틴 에러 수정
2022-04-10 19:58:05 +09:00
602b39d9bf linkkf-yommi 기능 추가및 버그 픽스
infinite 루틴 에러 수정
2022-04-10 15:28:07 +09:00
88d0a8ff05 anime-downloader bug fix 21.
add search routine
check download fix
2022-04-10 14:24:49 +09:00
dec47f900b anime-downloader bug fix 21.
add search routine
check download fix
2022-04-10 14:24:23 +09:00
1d9bbbf922 anime-downloader bug fix 20.
add search routine
check download fix
2022-04-09 19:37:36 +09:00
a797423576 anime-downloader bug fix 19.
check download fix
2022-04-08 22:26:24 +09:00
846ff82a06 anime-downloader bug fix 18.
function fix
2022-04-07 23:15:32 +09:00
8019f078d6 anime-downloader bug fix 17.
lazy image loader
2022-04-07 21:39:07 +09:00
b2d07abecb anime-downloader bug fix 16.
lazy image loader
2022-04-07 21:30:59 +09:00
4d0e55b25f anime-downloader bug fix 15.
lazy image loader
2022-04-07 16:06:36 +09:00
889c0177a8 anime-downloader bug fix 14. 2022-04-07 00:22:00 +09:00
1d53aa670a anime-downloader bug fix 14. 2022-04-07 00:18:25 +09:00
b00c449dfe anime-downloader bug fix 13. 2022-04-07 00:17:06 +09:00
75450f6c9d anime-downloader bug fix 13. 2022-04-07 00:13:44 +09:00
f4fe73486d anime-downloader bug fix 12. 2022-04-07 00:11:44 +09:00
c65c68193d anime-downloader bug fix 11. 2022-04-06 23:58:57 +09:00
ec6b5570b7 anime-downloader bug fix 10. 2022-04-06 23:45:15 +09:00
32ff102fec anime-downloader bug fix 8. 2022-03-28 19:29:26 +09:00
9fe7304500 anime-downloader bug fix 7. 2022-03-28 19:27:26 +09:00
9745d81c6a anime-downloader bug fix 7. 2022-03-28 19:25:27 +09:00
e29d91e36d anime-downloader bug fix 6. 2022-03-28 19:20:26 +09:00
27a08d9384 anime-downloader bug fix 5. 2022-03-28 19:14:25 +09:00
c57c94b448 anime-downloader bug fix 5. 2022-03-28 18:52:51 +09:00
f4956471dc anime-downloader bug fix 4. 2022-03-28 18:43:12 +09:00
c24f0bcde3 anime-downloader bug fix 3. 2022-03-28 14:56:29 +09:00
3400fb7dde anime-downloader bug fix 1. 2022-03-28 14:52:38 +09:00
da56638809 anime-downloader bug fix 1. 2022-03-28 14:40:38 +09:00
33 changed files with 11434 additions and 419 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,7 @@
*.pyo
*.pyc
.idea
.vscode
.gitignore
*.ipynb

BIN
bin/.DS_Store vendored Normal file

Binary file not shown.

BIN
bin/Darwin/chromedriver Executable file

Binary file not shown.

BIN
bin/Linux/chromedriver Executable file

Binary file not shown.

10
info.json Normal file
View File

@@ -0,0 +1,10 @@
{
"version": "0.1.2.0",
"name": "anime_downloader",
"category_name": "vod",
"icon": "",
"developer": "soju6jan && projectdx",
"description": "비디오 다운로드",
"home": "http://yommi.duckdns.org:20080/projectdx/anime-downloader",
"more": ""
}

1
lib/plugin/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .ffmpeg_queue import FfmpegQueueEntity, FfmpegQueue

301
lib/plugin/ffmpeg_queue.py Normal file
View File

@@ -0,0 +1,301 @@
# -*- coding: utf-8 -*-
#########################################################
# python
import abc
import os
import threading
import time
import traceback
from datetime import datetime
from framework import py_queue
# third-party
# sjva 공용
#########################################################
class FfmpegQueueEntity(abc.ABCMeta("ABC", (object,), {"__slots__": ()})):
def __init__(self, P, module_logic, info):
self.P = P
self.module_logic = module_logic
self.entity_id = -1 # FfmpegQueueEntity.static_index
self.info = info
self.url = None
self.ffmpeg_status = -1
self.ffmpeg_status_kor = "대기중"
self.ffmpeg_percent = 0
self.ffmpeg_arg = None
self.cancel = False
self.created_time = datetime.now().strftime("%m-%d %H:%M:%S")
self.savepath = None
self.filename = None
self.filepath = None
self.quality = None
self.headers = None
# FfmpegQueueEntity.static_index += 1
# FfmpegQueueEntity.entity_list.append(self)
def get_video_url(self):
return self.url
def get_video_filepath(self):
return self.filepath
@abc.abstractmethod
def refresh_status(self):
pass
@abc.abstractmethod
def info_dict(self, tmp):
pass
def download_completed(self):
pass
def as_dict(self):
tmp = {}
tmp["entity_id"] = self.entity_id
tmp["url"] = self.url
tmp["ffmpeg_status"] = self.ffmpeg_status
tmp["ffmpeg_status_kor"] = self.ffmpeg_status_kor
tmp["ffmpeg_percent"] = self.ffmpeg_percent
tmp["ffmpeg_arg"] = self.ffmpeg_arg
tmp["cancel"] = self.cancel
tmp["created_time"] = self.created_time # .strftime('%m-%d %H:%M:%S')
tmp["savepath"] = self.savepath
tmp["filename"] = self.filename
tmp["filepath"] = self.filepath
tmp["quality"] = self.quality
# tmp['current_speed'] = self.ffmpeg_arg['current_speed'] if self.ffmpeg_arg is not None else ''
tmp = self.info_dict(tmp)
return tmp
class FfmpegQueue(object):
def __init__(self, P, max_ffmpeg_count):
self.P = P
self.static_index = 1
self.entity_list = []
self.current_ffmpeg_count = 0
self.download_queue = None
self.download_thread = None
self.max_ffmpeg_count = max_ffmpeg_count
if self.max_ffmpeg_count is None or self.max_ffmpeg_count == "":
self.max_ffmpeg_count = 1
def queue_start(self):
try:
if self.download_queue is None:
self.download_queue = py_queue.Queue()
if self.download_thread is None:
self.download_thread = threading.Thread(
target=self.download_thread_function, args=()
)
self.download_thread.daemon = True
self.download_thread.start()
except Exception as exception:
self.P.logger.error("Exception:%s", exception)
self.P.logger.error(traceback.format_exc())
def download_thread_function(self):
while True:
try:
while True:
try:
if self.current_ffmpeg_count < self.max_ffmpeg_count:
break
time.sleep(5)
except Exception as exception:
self.P.logger.error("Exception:%s", exception)
self.P.logger.error(traceback.format_exc())
self.P.logger.error(
"current_ffmpeg_count : %s", self.current_ffmpeg_count
)
self.P.logger.error(
"max_ffmpeg_count : %s", self.max_ffmpeg_count
)
break
entity = self.download_queue.get()
if entity.cancel:
continue
# from .logic_ani24 import LogicAni24
# entity.url = LogicAni24.get_video_url(entity.info['code'])
video_url = entity.get_video_url()
if video_url is None:
entity.ffmpeg_status_kor = "URL실패"
entity.refresh_status()
# plugin.socketio_list_refresh()
continue
import ffmpeg
# max_pf_count = 0
# save_path = ModelSetting.get('download_path')
# if ModelSetting.get('auto_make_folder') == 'True':
# program_path = os.path.join(save_path, entity.info['filename'].split('.')[0])
# save_path = program_path
# try:
# if not os.path.exists(save_path):
# os.makedirs(save_path)
# except:
# logger.debug('program path make fail!!')
# 파일 존재여부 체크
filepath = str(entity.get_video_filepath())
self.P.logger.debug(filepath)
self.P.logger.debug(entity.get_video_filepath())
if os.path.exists(filepath):
entity.ffmpeg_status_kor = "파일 있음"
entity.ffmpeg_percent = 100
entity.refresh_status()
# plugin.socketio_list_refresh()
continue
dirname = os.path.dirname(filepath)
self.P.logger.debug(type(dirname))
self.P.logger.debug(dirname)
if not os.path.exists(dirname):
os.makedirs(dirname)
f = ffmpeg.Ffmpeg(
video_url,
os.path.basename(filepath),
plugin_id=entity.entity_id,
listener=self.ffmpeg_listener,
call_plugin=self.P.package_name,
save_path=dirname,
headers=entity.headers,
)
f.start()
self.current_ffmpeg_count += 1
self.download_queue.task_done()
except Exception as exception:
self.P.logger.error("Exception:%s", exception)
self.P.logger.error(traceback.format_exc())
def ffmpeg_listener(self, **arg):
import ffmpeg
entity = self.get_entity_by_entity_id(arg["plugin_id"])
if entity is None:
return
if arg["type"] == "status_change":
if arg["status"] == ffmpeg.Status.DOWNLOADING:
pass
elif arg["status"] == ffmpeg.Status.COMPLETED:
entity.download_completed()
elif arg["status"] == ffmpeg.Status.READY:
pass
elif arg["type"] == "last":
self.current_ffmpeg_count += -1
elif arg["type"] == "log":
pass
elif arg["type"] == "normal":
pass
entity.ffmpeg_arg = arg
entity.ffmpeg_status = int(arg["status"])
entity.ffmpeg_status_kor = str(arg["status"])
entity.ffmpeg_percent = arg["data"]["percent"]
entity.ffmpeg_arg["status"] = str(arg["status"])
# self.P.logger.debug(arg)
# import plugin
# arg['status'] = str(arg['status'])
# plugin.socketio_callback('status', arg)
entity.refresh_status()
# FfmpegQueueEntity.static_index += 1
# FfmpegQueueEntity.entity_list.append(self)
def add_queue(self, entity):
try:
# entity = QueueEntity.create(info)
# if entity is not None:
# LogicQueue.download_queue.put(entity)
# return True
entity.entity_id = self.static_index
self.static_index += 1
self.entity_list.append(entity)
self.download_queue.put(entity)
return True
except Exception as exception:
self.P.logger.error("Exception:%s", exception)
self.P.logger.error(traceback.format_exc())
return False
def set_max_ffmpeg_count(self, max_ffmpeg_count):
self.max_ffmpeg_count = max_ffmpeg_count
def get_max_ffmpeg_count(self):
return self.max_ffmpeg_count
def command(self, cmd, entity_id):
self.P.logger.debug("command :%s %s", cmd, entity_id)
ret = {}
try:
if cmd == "cancel":
self.P.logger.debug("command :%s %s", cmd, entity_id)
entity = self.get_entity_by_entity_id(entity_id)
if entity is not None:
if entity.ffmpeg_status == -1:
entity.cancel = True
entity.ffmpeg_status_kor = "취소"
# entity.refresh_status()
ret["ret"] = "refresh"
elif entity.ffmpeg_status != 5:
ret["ret"] = "notify"
ret["log"] = "다운로드중 상태가 아닙니다."
else:
idx = entity.ffmpeg_arg["data"]["idx"]
import ffmpeg
ffmpeg.Ffmpeg.stop_by_idx(idx)
entity.refresh_status()
ret["ret"] = "refresh"
elif cmd == "reset":
if self.download_queue is not None:
with self.download_queue.mutex:
self.download_queue.queue.clear()
for _ in self.entity_list:
if _.ffmpeg_status == 5:
import ffmpeg
idx = _.ffmpeg_arg["data"]["idx"]
ffmpeg.Ffmpeg.stop_by_idx(idx)
self.entity_list = []
ret["ret"] = "refresh"
elif cmd == "delete_completed":
new_list = []
for _ in self.entity_list:
if _.ffmpeg_status_kor in ["파일 있음", "취소", "사용자중지"]:
continue
if _.ffmpeg_status != 7:
new_list.append(_)
self.entity_list = new_list
ret["ret"] = "refresh"
elif cmd == "remove":
new_list = []
for _ in self.entity_list:
if _.entity_id == entity_id:
continue
new_list.append(_)
self.entity_list = new_list
ret["ret"] = "refresh"
return ret
except Exception as exception:
self.P.logger.error("Exception:%s", exception)
self.P.logger.error(traceback.format_exc())
def get_entity_by_entity_id(self, entity_id):
for _ in self.entity_list:
if _.entity_id == entity_id:
return _
return None
def get_entity_list(self):
ret = []
for x in self.entity_list:
tmp = x.as_dict()
ret.append(tmp)
return ret

25
lib/utils.py Normal file
View File

@@ -0,0 +1,25 @@
import os
import time
from functools import wraps
try:
from loguru import logger
except:
os.system(f"pip install loguru")
from loguru import logger
def yommi_timeit(func):
@wraps(func)
def timeit_wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
total_time = end_time - start_time
# print(f"Function {func.__name__}{args} {kwargs} Took {total_time:.4f} secs")
logger.opt(colors=True).debug(
f"<red>{func.__name__}{args} {kwargs}</red> function took <green>{total_time:.4f}</green> secs"
)
return result
return timeit_wrapper

73
linkey.py Normal file
View File

@@ -0,0 +1,73 @@
def get_html_selenium(url, referer):
from selenium.webdriver.common.by import By
from selenium import webdriver
from selenium_stealth import stealth
from webdriver_manager.chrome import ChromeDriverManager
import time
import platform
import os
os_platform = platform.system()
options = webdriver.ChromeOptions()
# 크롬드라이버 헤더 옵션추가 (리눅스에서 실행시 필수)
options.add_argument("start-maximized")
# options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
if os_platform == "Darwin":
# 크롬드라이버 경로
driver_path = "./bin/Darwin/chromedriver"
# driver = webdriver.Chrome(executable_path=driver_path, chrome_options=options)
driver = webdriver.Chrome(
ChromeDriverManager().install(), chrome_options=options
)
else:
driver_bin_path = os.path.join(
os.path.dirname(__file__), "bin", f"{os_platform}"
)
driver_path = f"{driver_bin_path}/chromedriver"
driver = webdriver.Chrome(executable_path=driver_path, chrome_options=options)
stealth(
driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get(url)
driver.refresh()
print(f"current_url:: {driver.current_url}")
# logger.debug(f"current_cookie:: {driver.get_cookies()}")
cookies_list = driver.get_cookies()
cookies_dict = {}
for cookie in cookies_list:
cookies_dict[cookie["name"]] = cookie["value"]
print(cookies_dict)
# LogicAniLife.cookies = cookies_list
# # LogicAniLife.headers["Cookie"] = driver.get_cookies()
# LogicAniLife.episode_url = driver.current_url
time.sleep(1)
elem = driver.find_element(By.XPATH, "//*")
source_code = elem.get_attribute("outerHTML")
li_elem = driver.find_element(By.XPATH, "//li[@class='-qHwcFXhj0']//a")
li_elem.click()
time.sleep(20.0)
return source_code.encode("utf-8")
url = "https://smartstore.naver.com/flamingo_k"
get_html_selenium(url=url, referer=None)

1622
logic_anilife.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,67 +6,97 @@
# @Software: PyCharm
import os, traceback
# third-party
from flask import Blueprint
# sjva 공용
from framework.logger import get_logger
from framework import app, path_data
from framework.util import Util
from plugin import get_model_setting, Logic, default_route, PluginUtil
#######################################################################
class P(object):
package_name = __name__.split('.')[0]
package_name = __name__.split(".")[0]
logger = get_logger(package_name)
blueprint = Blueprint(package_name, package_name, url_prefix='/%s' % package_name,
template_folder=os.path.join(os.path.dirname(__file__), 'templates'))
blueprint = Blueprint(
package_name,
package_name,
url_prefix="/%s" % package_name,
template_folder=os.path.join(os.path.dirname(__file__), "templates"),
static_folder="static",
)
menu = {
'main': [package_name, u'애니 다운로드'],
'sub': [
['ohli24', u'OHLI24'], ['linkkf', u'LINKKF'], ['log', u'로그']
"main": [package_name, "애니 다운로드"],
"sub": [
["ohli24", "OHLI24"],
["linkkf", "LINKKF"],
["anilife", "애니라이프"],
["log", "로그"],
],
'category': 'vod',
'sub2': {
'ohli24': [
['setting', u'설정'], ['request', u'요청'], ['queue', u''], ['list', u'목록']
"category": "vod",
"sub2": {
"ohli24": [
["setting", "설정"],
["request", "요청"],
["queue", ""],
["category", "검색"],
["list", "목록"],
],
'linkkf': [
['setting', u'설정'], ['request', u'요청'], ['queue', u''], ['list', u'목록']
"linkkf": [
["setting", "설정"],
["request", "요청"],
["queue", ""],
["category", "검색"],
["list", "목록"],
],
}
"anilife": [
["setting", "설정"],
["request", "요청"],
["category", "검색"],
["queue", ""],
["list", "목록"],
],
},
}
plugin_info = {
'version': '0.1.1.0',
'name': 'anime_downloader',
'category_name': 'vod',
'icon': '',
'developer': 'soju6jan && projectdx',
'description': u'비디오 다운로드',
'home': 'http://yommi.duckdns.org:20080/projectdx/anime-downloader',
'more': '',
"version": "0.1.2.0",
"name": "anime_downloader",
"category_name": "vod",
"icon": "",
"developer": "soju6jan && projectdx",
"description": "비디오 다운로드",
"home": "http://yommi.duckdns.org:20080/projectdx/anime-downloader",
"more": "",
}
ModelSetting = get_model_setting(package_name, logger)
logic = None
module_list = None
home_module = 'ohli24'
home_module = "ohli24"
# 초기화 함수
def initialize():
try:
app.config['SQLALCHEMY_BINDS'][P.package_name] = 'sqlite:///%s' % (
os.path.join(path_data, 'db', '{package_name}.db'.format(package_name=P.package_name)))
app.config["SQLALCHEMY_BINDS"][P.package_name] = "sqlite:///%s" % (
os.path.join(
path_data, "db", "{package_name}.db".format(package_name=P.package_name)
)
)
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)]
P.module_list = [LogicOhli24(P), LogicAniLife(P), LogicLinkkf(P)]
P.logic = Logic(P)
default_route(P)
except Exception as e:
P.logger.error('Exception:%s', e)
P.logger.error("Exception:%s", e)
P.logger.error(traceback.format_exc())

243
static/css/linkkf.css Normal file
View File

@@ -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);
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(241, 242, 243); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<g transform="translate(27.166666666666664,27.166666666666664)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#85a2b6">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.3s"></animateTransform>
</rect>
</g>
<g transform="translate(72.83333333333333,27.166666666666664)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#bbcedd">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.2s"></animateTransform>
</rect>
</g>
<g transform="translate(27.166666666666664,72.83333333333333)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#dce4eb">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="0s"></animateTransform>
</rect>
</g>
<g transform="translate(72.83333333333333,72.83333333333333)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#fdfdfd">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.1s"></animateTransform>
</rect>
</g>
<!-- [ldio] generated by https://loading.io/ --></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

67
static/js/sjva_global1.js Normal file
View File

@@ -0,0 +1,67 @@
function global_sub_request_search(page, move_top=true) {
var formData = get_formdata('#form_search')
formData += '&page=' + page;
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/web_list',
type: "POST",
cache: false,
data: formData,
dataType: "json",
success: function (data) {
current_data = data;
if (move_top)
window.scrollTo(0,0);
make_list(data.list)
make_page_html(data.paging)
}
});
}
function get_formdata(form_id) {
// on, off 일수도 있으니 모두 True, False로 통일하고
// 밑에서는 False인 경우 값이 추가되지 않으니.. 수동으로 넣어줌
var checkboxs = $(form_id + ' input[type=checkbox]');
//for (var i in checkboxs) {
for (var i =0 ; i < checkboxs.length; i++) {
if ( $(checkboxs[i]).is(':checked') ) {
$(checkboxs[i]).val('True');
} else {
$(checkboxs[i]).val('False');
}
}
var formData = $(form_id).serialize();
$.each($(form_id + ' input[type=checkbox]')
.filter(function(idx) {
return $(this).prop('checked') === false
}),
function(idx, el) {
var emptyVal = "False";
formData += '&' + $(el).attr('name') + '=' + emptyVal;
}
);
formData = formData.replace("&global_scheduler=True", "")
formData = formData.replace("&global_scheduler=False", "")
formData = formData.replace("global_scheduler=True&", "")
formData = formData.replace("global_scheduler=False&", "")
return formData;
}
function globalRequestSearch2(page, move_top = true) {
var formData = getFormdata("#form_search")
formData += "&page=" + page
console.log(formData)
$.ajax({
url: "/" + PACKAGE_NAME + "/ajax/" + MODULE_NAME + "/web_list2",
type: "POST",
cache: false,
data: formData,
dataType: "json",
success: function (data) {
current_data = data
if (move_top) window.scrollTo(0, 0)
make_list(data.list)
make_page_html(data.paging)
},
})
}

204
static/js/sjva_ui14.js Normal file
View File

@@ -0,0 +1,204 @@
function m_row_start(padding='10', align='center') {
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
return str;
}
function m_row_start_hover(padding='10', align='center') {
var str = '<div class="row my_hover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
return str;
}
function m_row_start_top(padding='10') {
return m_row_start(padding, 'top');
}
function m_row_start_color(padding='10', align='center', color='') {
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+'; background-color:'+color+'">';
return str;
}
function m_row_start_color2(padding='10', align='center') {
var str = '<div class="row bg-dark text-white" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
return str;
}
function m_row_end() {
var str = '</div>';
return str;
}
//border
function m_col(w, h, align='left') {
var str = '<div class="col-sm-' + w + ' " style="text-align: '+align+'; word-break:break-all;">';
str += h
str += '</div>';
return str
}
function m_col2(w, h, align='left') {
var str = '<div class="col-sm-' + w + ' " style="padding:5px; margin:0px; text-align: '+align+'; word-break:break-all;">';
str += h
str += '</div>';
return str
}
function m_button_group(h) {
var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">';
str += h
str += '</div>';
return str;
}
function m_button(id, text, data) {
var str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn-outline-success" '
for ( var i in data) {
str += ' data-' + data[i].key + '="' + data[i].value+ '" '
}
str += '>' + text + '</button>';
return str;
}
function m_button2(id, text, data, outline_color) {
var str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn-outline-'+outline_color+'" '
for ( var i in data) {
str += ' data-' + data[i].key + '="' + data[i].value+ '" '
}
str += '>' + text + '</button>';
return str;
}
function m_hr(margin='5') {
var str = '<hr style="width: 100%; margin:'+margin+'px;" />';
return str;
}
function m_hr_black() {
var str = '<hr style="width: 100%; color: black; height: 2px; background-color:black;" />';
return str;
}
// 체크박스는 자바로 하면 on/off 스크립트가 안먹힘.
function m_modal(data='EMPTY', title='JSON', json=true) {
document.getElementById("modal_title").innerHTML = title;
if (json) {
data = JSON.stringify(data, null, 2);
}
document.getElementById("modal_body").innerHTML = "<pre>"+ data + "</pre>";;
$("#large_modal").modal();
}
function m_tab_head(name, active) {
if (active) {
var str = '<a class="nav-item nav-link active" id="id_'+name+'" data-toggle="tab" href="#'+name+'" role="tab">'+name+'</a>';
} else {
var str = '<a class="nav-item nav-link" id="id_'+name+'" data-toggle="tab" href="#'+name+'" role="tab">'+name+'</a>';
}
return str;
}
function m_tab_content(name, content, active) {
if (active) {
var str = '<div class="tab-pane fade show active" id="'+name+'" role="tabpanel" >';
} else {
var str = '<div class="tab-pane fade show" id="'+name+'" role="tabpanel" >';
}
str += content;
str += '</div>'
return str;
}
function m_progress(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
function m_progress2(id, width, label) {
var str = '';
str += '<div class="progress" style="height: 25px;">'
str += '<div id="'+id+'" class="progress-bar" style="background-color:yellow;width:'+width+'%"></div>';
str += '<div id="'+id+'_label" class="justify-content-center d-flex w-100 position-absolute" style="margin:0px; margin-top:2px">'+label+'</div>';
str += '</div>'
return str;
}
function make_page_html(data) {
str = ' \
<div class="d-inline-block"></div> \
<div class="row mb-3"> \
<div class="col-sm-12"> \
<div class="btn-toolbar" style="justify-content: center;" role="toolbar" aria-label="Toolbar with button groups" > \
<div class="btn-group btn-group-sm mr-2" role="group" aria-label="First group">'
if (data.prev_page) {
str += '<button id="page" data-page="' + (data.start_page-1) + '" type="button" class="btn btn-secondary">&laquo;</button>'
}
for (var i = data.start_page ; i <= data.last_page ; i++) {
str += '<button id="page" data-page="' + i +'" type="button" class="btn btn-secondary" ';
if (i == data.current_page) {
str += 'disabled';
}
str += '>'+i+'</button>';
}
if (data.next_page) {
str += '<button id="page" data-page="' + (data.last_page+1) + '" type="button" class="btn btn-secondary">&raquo;</button>'
}
str += '</div> \
</div> \
</div> \
</div> \
'
document.getElementById("page1").innerHTML = str;
document.getElementById("page2").innerHTML = str;
}
function use_collapse(div, reverse=false) {
var ret = $('#' + div).prop('checked');
if (reverse) {
if (ret) {
$('#' + div + '_div').collapse('hide')
} else {
$('#' + div + '_div').collapse('show')
}
} else {
if (ret) {
$('#' + div + '_div').collapse('show')
} else {
$('#' + div + '_div').collapse('hide')
}
}
}
// primary, secondary, success, danger, warning, info, light, dark, white
function j_button(id, text, data={}, color='primary', outline=true, small=false, _class='') {
let str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn';
if (outline) {
str += '-outline';
}
str += '-' + color+'';
str += ' ' + _class;
if (small) {
str += ' py-0" style="font-size: 0.8em;"';
} else {
str += '" ';
}
for ( var key in data) {
str += ' data-' + key + '="' + data[key]+ '" '
}
str += '>' + text + '</button>';
return str;
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(241, 242, 243); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<g transform="translate(27.166666666666664,27.166666666666664)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#85a2b6">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.3s"></animateTransform>
</rect>
</g>
<g transform="translate(72.83333333333333,27.166666666666664)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#bbcedd">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.2s"></animateTransform>
</rect>
</g>
<g transform="translate(27.166666666666664,72.83333333333333)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#dce4eb">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="0s"></animateTransform>
</rect>
</g>
<g transform="translate(72.83333333333333,72.83333333333333)">
<rect x="-18.5" y="-18.5" width="37" height="37" fill="#fdfdfd">
<animateTransform attributeName="transform" type="scale" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="1.1;1" begin="-0.1s"></animateTransform>
</rect>
</g>
<!-- [ldio] generated by https://loading.io/ --></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,847 @@
{% extends "base.html" %} {% block content %}
<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 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>-->
<button id="top20" type="button" class="btn btn-grey">Top20</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>
<form id="program_auto_form">
<div id="episode_list"></div>
</form>
</div>
<!--전체-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.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 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 loader = document.getElementById("preloader")
const dismissLoadingScreen = async function () {
console.log("Before the delay")
// await delay(2.5);
loader.style.display = "none"
}
const get_anime_list = (type, page) => {
console.log(`type: ${type}, page: ${page}`)
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 "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 "top20":
url = "/" + package_name + "/ajax/" + sub + "/anime_list"
current_cate = "top20"
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)
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)
}
dismissLoadingScreen()
next_page = page + 1
},
})
}
function make_airing_list(data, page) {
console.log("call make_airing_list()")
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 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 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)
request_url =
"./request?code=" +
data.anime_list[i].code +
"&amp;wr_id=" +
data.anime_list[i].wr_id +
"&amp;bo_table=" +
bo_table[1]
} else {
request_url = "./request?code=" + data.anime_list[i].code
}
tmp = '<div class="col-sm-4">'
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 += 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 = ""
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 id="inner_screen_movie" class="row infinite-scroll">'
for (let i in data.anime_list) {
tmp = '<div class="col-sm-4">'
tmp += '<div class="card">'
tmp += '<img class="card-img-top" src="' + data.anime_list[i].image_link + '" />'
tmp += '<div class="card-body">'
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?code=' +
data.anime_list[i].code +
'" class="btn btn-primary cut-text">' +
data.anime_list[i].title +
"</a>"
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: 10,
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 #ing").on("click", function () {
// {#console.log(this.id)#}
let spinner = document.getElementById("spinner")
spinner.style.visibility = "visible"
get_anime_list("ing", 1)
})
$("#anime_category #complete_anilist").on("click", function () {
// let spinner = document.getElementById("spinner")
// spinner.style.visibility = "visible"
get_anime_list("fin", 1)
})
$("#anime_category #theater").on("click", function () {
// let spinner = document.getElementById("spinner")
// spinner.style.visibility = "visible"
get_anime_list("theater", 1)
})
$("#anime_category #top20").on("click", function () {
// let spinner = document.getElementById("spinner")
// spinner.style.visibility = "visible"
get_anime_list("top20", 1)
})
// 분석 버튼 클릭시 호출
$("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();
const loadNextAnimes = (cate, page) => {
spinner.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#}
if (current_cate === "search") {
make_search_result_list(response.data, response.page)
} else {
make_screen_movie_list(response.data, response.page)
}
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) {
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)
}
}
const debounce = (func, delay) => {
let timeoutId = null
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(func.bind(null, ...args), delay)
}
}
document.addEventListener("scroll", debounce(onScroll, 300))
</script>
<style>
button.code-button {
min-width: 82px !important;
}
.tooltip {
position: relative;
display: block;
}
@media (min-width: 576px) {
.container {
max-width: 100%;
}
}
[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%;
}
#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;
}
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);
}
.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%;
}
.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>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"
/>
{% endblock %}

View File

@@ -0,0 +1,205 @@
{% 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>
<style>
body {
width: 100%;
/*height: 100vh;*/
/*display: flex;*/
align-items: center;
justify-content: center;
background-size: 300% 300%;
background-image: linear-gradient(
-45deg,
rgba(59, 173, 227, 1) 0%,
rgba(87, 111, 230, 1) 25%,
rgba(152, 68, 183, 1) 51%,
rgba(255, 53, 127, 1) 100%
);
animation: AnimateBG 20s ease infinite;
}
#main_container {
background-color: white;
}
@keyframes AnimateBG {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@media (min-width: 576px) {
.container {
max-width: 540px;
min-height: 1080px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
min-height: 1080px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
min-height: 1080px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
min-height: 1080px;
}
}
</style>
{% endblock %}

View File

@@ -0,0 +1,130 @@
{% extends "base.html" %}
{% block content %}
<div>
{{ macros.m_button_group([['reset_btn', '초기화'], ['delete_completed_btn', '완료 목록 삭제'], ['go_ffmpeg_btn', 'Go FFMPEG']])}}
{{ macros.m_row_start('0') }}
{{ macros.m_row_end() }}
{{ macros.m_hr_head_top() }}
{{ macros.m_row_start('0') }}
{{ macros.m_col(1, macros.m_strong('Idx')) }}
{{ macros.m_col(2, macros.m_strong('CreatedTime')) }}
{{ macros.m_col(4, macros.m_strong('Filename')) }}
{{ macros.m_col(3, macros.m_strong('Status')) }}
{{ macros.m_col(2, macros.m_strong('Action')) }}
{{ macros.m_row_end() }}
{{ macros.m_hr_head_bottom() }}
<div id="download_list_div"></div>
</div> <!--전체-->
<script type="text/javascript">
var package_name = "{{arg['package_name'] }}";
var sub = "{{arg['sub'] }}";
var current_data = null;
socket = io.connect(window.location.protocol + "//" + document.domain + ":" + location.port + "/" + package_name + '/' + sub);
$(document).ready(function(){
});
socket.on('start', function(data){
on_start();
});
socket.on('list_refresh', function(data){
on_start()
});
socket.on('status', function(data){
on_status(data)
});
function on_start() {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/entity_list',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (data) {
make_download_list(data)
}
});
}
function on_status(data) {
//console.log(data)
tmp = document.getElementById("progress_"+data.entity_id)
if (tmp != null) {
document.getElementById("progress_"+data.entity_id).style.width = data.ffmpeg_percent+ '%';
document.getElementById("progress_"+data.entity_id+"_label").innerHTML = data.ffmpeg_status_kor + "(" + data.ffmpeg_percent + "%)" + ' ' + ((data.ffmpeg_arg != null)?data.ffmpeg_arg.data.current_speed:'')
}
}
function make_download_list(data) {
str = '';
for (i in data) {
str += m_row_start();
str += m_col(1, data[i].entity_id);
str += m_col(2, data[i].created_time);
str += m_col(4, (data[i].filename != null) ? data[i].filename : '');
label = data[i].ffmpeg_status_kor
if (data[i].ffmpeg_percent != 0) {
label += '(' + data[i].ffmpeg_percent + '%)'
}
tmp = m_progress('progress_'+data[i].entity_id, data[i].ffmpeg_percent, label)
str += m_col(3, tmp);
tmp = m_button('program_cancel_btn', '취소', [{'key':'id', 'value':data[i].entity_id}]);
tmp = m_button_group(tmp)
str += m_col(2, tmp)
str += m_row_end();
if (i != data.length -1) str += m_hr(0);
}
document.getElementById("download_list_div").innerHTML = str;
}
$("body").on('click', '#program_cancel_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'cancel', 'entity_id':entity_id}
queue_command(send_data)
});
$("body").on('click', '#reset_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'reset', 'entity_id':-1}
queue_command(send_data)
});
$("body").on('click', '#delete_completed_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'delete_completed', 'entity_id':-1}
queue_command(send_data)
});
function queue_command(data) {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/queue_command',
type: "POST",
cache: false,
data: data,
dataType: "json",
success: function (ret) {
if (ret.ret == 'notify') {
$.notify('<strong>'+ ret.log +'</strong>', {type: 'warning'});
}
on_start();
}
});
}
$("body").on('click', '#go_ffmpeg_btn', function(e){
e.preventDefault();
$(location).attr('href', '/ffmpeg')
});
</script>
{% endblock %}

View File

@@ -0,0 +1,568 @@
{% extends "base.html" %} {% block content %}
<div>
<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>
<form id="program_list">
{{ macros.setting_input_text_and_buttons('code', '작품 Code',
[['analysis_btn', '분석'], ['go_anilife_btn', 'Go 애니라이프']], desc='예)
"https://anilife.live/detail/id/101 예> 원피스 코드: 101"')
}}
</form>
<form id="program_auto_form">
<div id="episode_list"></div>
</form>
</div>
<!--전체-->
<script type="text/javascript">
const package_name = "{{arg['package_name'] }}";
const sub = "{{arg['sub'] }}";
const anilife_url = "{{arg['anilife_url']}}";
let current_data = null;
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
})
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;
}
const loader = document.getElementById("preloader");
const dismissLoadingScreen = async function () {
console.log("Before the delay")
// await delay(2.5);
loader.style.display = "none";
};
const wait_seconds = function () {
// REFERENCE: https://www.w3schools.com/jsref/met_win_settimeout.asp
let result = setTimeout(dismissLoadingScreen, 2500);
console.log(result)
};
const init = function () {
}
function delay(n) {
return new Promise(function (resolve) {
setTimeout(resolve, n * 1000);
});
}
async function myAsyncFunction() {
//Do what you want here
console.log("Before the delay")
await delay(2.5);
console.log("After the delay")
//Do what you want here too
}
// myAsyncFunction();
// window.addEventListener("DOMContentLoaded", dismissLoadingScreen);
// window.addEventListener("DOMContentLoaded", wait_seconds);
function analyze(wr_id, bo_table) {
// e.preventDefault();
// e.stopPropagation()
// 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)
$("#loader").css("display", 'none')
} else {
$.notify('<strong>분석 실패</strong><br>' + ret.log, {type: 'warning'});
}
}
});
}
function make_program(data) {
current_data = data;
// console.log("current_data::", current_data)
let str = '';
let tmp = '';
tmp = '<div class="form-inline">'
tmp += m_button('check_download_btn', '선택 다운로드 추가', []);
tmp += m_button('all_check_on_btn', '전체 선택', []);
tmp += m_button('all_check_off_btn', '전체 해제', []);
/*
tmp += '&nbsp;&nbsp;&nbsp;&nbsp;<input id="new_title" name="new_title" class="form-control form-control-sm" value="'+data.title+'">'
tmp += '</div>'
tmp += m_button('apply_new_title_btn', '저장폴더명, 파일명 제목 변경', []);
tmp += m_button('search_tvdb_btn', 'TVDB', []);
tmp = m_button_group(tmp)
*/
str += tmp
// program
str += m_hr_black();
str += m_row_start(0);
tmp = ''
if (data.image != null)
tmp = '<img src="' + data.image + '" class="img-fluid">';
str += m_col(3, tmp)
tmp = ''
// tmp += m_row_start(2) + m_col(3, '제목', 'right') + m_col(9, data.title) + m_row_end();
tmp += '<div><p><b style="font-size: 15px; color: midnightblue">'+data.title+'</b></p></div>'
tmp += "<div>" + data.des1 + "</div>"
str += m_col(9, tmp)
str += m_row_end();
str += m_hr_black();
for (i in data.episode) {
str += m_row_start();
tmp = '';
if (data.episode[i].thumbnail)
tmp = '<img src="' + data.episode[i].thumbnail + '" class="img-fluid">'
str += m_col(3, tmp)
tmp = '<strong>' + data.episode[i].ep_num + '화. ' + data.episode[i].title + '</strong>';
tmp += '<br>';
tmp += data.episode[i].date + '<br>';
tmp += '<div class="form-inline">'
tmp += '<input id="checkbox_' + i + '" name="checkbox_' + i + '" type="checkbox" checked data-toggle="toggle" data-on="선 택" data-off="-" data-onstyle="success" data-offstyle="danger" data-size="small">&nbsp;&nbsp;&nbsp;&nbsp;'
tmp += m_button('add_queue_btn', '다운로드 추가', [{'key': 'idx', 'value': i}])
tmp += '</div>'
str += m_col(9, 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("{{arg['anilife_current_code']}}")
// console.log(findGetParameter('wr_id'))
// console.log(params.code)
if (params.code === '' || params.code == null) {
// console.log('null')
dismissLoadingScreen()
return false;
} else {
console.log('here')
document.getElementById("code").value = params.code
document.getElementById("analysis_btn").click();
return true;
}
if ("{{arg['anilife_current_code']}}" !== "") {
if (params.code === null) {
console.log('params.code === null')
document.getElementById("code").value = "{{arg['anilife_current_code']}}";
} else if (params.code === '') {
document.getElementById("code").value = "{{arg['anilife_current_code']}}";
} else {
console.log('params code exist')
console.log(params.code)
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 {
return false;
}
})
$(document).ready(function () {
$("#loader").css("display", 'none')
// console.log('wr_id::', params.wr_id)
});
$("#analysis_btn").unbind("click").bind('click', function (e) {
e.preventDefault();
e.stopPropagation()
$("#loader").css("display", 'block')
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) {
$("#loader").css("display", 'none')
if (ret.ret === 'success' && ret.data != null) {
// {#console.log(ret.code)#}
console.log(ret.data)
make_program(ret.data)
dismissLoadingScreen()
} 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', '#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>
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%;
}
#screen_movie_list {
margin-top: 10px;
}
#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);
}
}
</style>
{% endblock %}

View File

@@ -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('anilife_url', '애니라이프 URL', [['go_btn', 'GO']], value=arg['anilife_url']) }}
{{ macros.setting_input_text('anilife_download_path', '저장 폴더', value=arg['anilife_download_path'], desc='정상적으로 다운 완료 된 파일이 이동할 폴더 입니다. ') }}
{{ macros.setting_input_int('anilife_max_ffmpeg_process_count', '동시 다운로드 수', value=arg['anilife_max_ffmpeg_process_count'], desc='동시에 다운로드 할 에피소드 갯수입니다.') }}
{{ macros.setting_checkbox('anilife_order_desc', '요청 화면 최신순 정렬', value=arg['anilife_order_desc'], desc='On : 최신화부터, Off : 1화부터') }}
{{ macros.setting_checkbox('anilife_auto_make_folder', '제목 폴더 생성', value=arg['anilife_auto_make_folder'], desc='제목으로 폴더를 생성하고 폴더 안에 다운로드합니다.') }}
<div id="anilife_auto_make_folder_div" class="collapse">
{{ macros.setting_input_text('anilife_finished_insert', '완결 표시', col='3', value=arg['anilife_finished_insert'], desc=['완결된 컨텐츠 폴더명 앞에 넣을 문구입니다.']) }}
{{ macros.setting_checkbox('anilife_auto_make_season_folder', '시즌 폴더 생성', value=arg['anilife_auto_make_season_folder'], desc=['On : Season 번호 폴더를 만듭니다.']) }}
</div>
{{ macros.setting_checkbox('anilife_uncompleted_auto_enqueue', '자동으로 다시 받기', value=arg['anilife_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('anilife_interval', '스케쥴링 실행 정보', value=arg['anilife_interval'], col='4', desc=['Interval(minute 단위)이나 Cron 설정']) }}
{{ macros.setting_checkbox('anilife_auto_start', '시작시 자동실행', value=arg['anilife_auto_start'], desc='On : 시작시 자동으로 스케쥴러에 등록 됩니다.') }}
{{ macros.setting_input_textarea('anilife_auto_code_list', '자동 다운로드할 작품 코드', desc=['구분자 | 또는 엔터'], value=arg['anilife_auto_code_list'], row='10') }}
{{ macros.setting_checkbox('anilife_auto_mode_all', '에피소드 모두 받기', value=arg['anilife_auto_mode_all'], desc=['On : 이전 에피소드를 모두 받습니다.', 'Off : 최신 에피소드만 받습니다.']) }}
{{ 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('anilife_auto_make_folder');
});
$('#ani365_auto_make_folder').change(function() {
use_collapse('anilife_auto_make_folder');
});
$("body").on('click', '#go_btn', function(e){
e.preventDefault();
let url = document.getElementById("anilife_url").value
window.open(url, "_blank");
});
</script>
{% endblock %}

View File

@@ -0,0 +1,765 @@
{% extends "base.html" %} {% block content %}
<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>
<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>
<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
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.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 linkkf_url = "{{arg['linkkf_url']}}";
let current_data = null;
let page = 1;
let next_page = Number
let current_cate = ''
let current_query = ''
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_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 '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
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)
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) {
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.episode) {
tmp = '<div class="col-6 col-sm-4 col-md-3">';
tmp += '<div class="card">';
// 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.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 +
// '"><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)
request_url = './request?code=' + data.anime_list[i].code + '&amp;wr_id=' + data.anime_list[i].wr_id + '&amp;bo_table=' + bo_table[1]
} else {
request_url = './request?code=' + data.anime_list[i].code
}
tmp = '<div class="col-sm-4">';
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 = ''
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) {
tmp = '<div class="col-sm-4">';
tmp += '<div class="card">';
tmp += '<img class="card-img-top" src="' + data.anime_list[i].image_link + '" />';
tmp += '<div class="card-body">'
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?code=' + data.anime_list[i].code + '" 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;
}
$("img.lazyload").lazyload({
threshold: 10,
effect: "fadeIn",
});
}
$(document).ready(function () {
// if ( "{{arg['linkkf_current_code']}}" !== "" ) {
// document.getElementById("code").value = "{{arg['linkkf_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 #ing').on("click", function () {
// {#console.log(this.id)#}
let spinner = document.getElementById('spinner');
spinner.style.visibility = 'visible';
get_anime_list("ing", 1)
})
$('#anime_category #complete_anilist').on("click", function () {
// {#console.log(this.id)#}
let spinner = document.getElementById('spinner');
spinner.style.visibility = 'visible';
get_anime_list("fin", 1)
})
$('#anime_category #theater').on("click", function () {
// {#console.log(this.id)#}
let spinner = document.getElementById('spinner');
spinner.style.visibility = 'visible';
get_anime_list("theater", 1)
})
// 분석 버튼 클릭시 호출
$("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_linkkf_btn', function (e) {
e.preventDefault();
window.open("{{arg['linkkf_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();
const loadNextAnimes = (cate, page) => {
spinner.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#}
if (current_cate === 'search') {
make_search_result_list(response.data, response.page);
} else {
make_screen_movie_list(response.data, response.page);
}
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) {
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);
}
};
const debounce = (func, delay) => {
let timeoutId = null;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(func.bind(null, ...args), delay);
};
};
document.addEventListener("scroll", debounce(onScroll, 300));
</script>
<style>
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;
}
.badge-on-image {
position: absolute;
top: 2px;
/*bottom: 2px; !* position where you want it *!*/
right: 2px;
padding: 5px 12px;
}
#airing_list {
display: none;
}
.cut-text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
}
#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;
}
}
.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);
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css">
{% endblock %}

View File

@@ -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 %}

View File

@@ -0,0 +1,131 @@
{% extends "base.html" %}
{% block content %}
<div>
{{ macros.m_button_group([['reset_btn', '초기화'], ['delete_completed_btn', '완료 목록 삭제'], ['go_ffmpeg_btn', 'Go FFMPEG']])}}
{{ macros.m_row_start('0') }}
{{ macros.m_row_end() }}
{{ macros.m_hr_head_top() }}
{{ macros.m_row_start('0') }}
{{ macros.m_col(1, macros.m_strong('Idx')) }}
{{ macros.m_col(2, macros.m_strong('CreatedTime')) }}
{{ macros.m_col(4, macros.m_strong('Filename')) }}
{{ macros.m_col(3, macros.m_strong('Status')) }}
{{ macros.m_col(2, macros.m_strong('Action')) }}
{{ macros.m_row_end() }}
{{ macros.m_hr_head_bottom() }}
<div id="download_list_div"></div>
</div> <!--전체-->
<script type="text/javascript">
var package_name = "{{arg['package_name'] }}";
var sub = "{{arg['sub'] }}";
var current_data = null;
socket = io.connect(window.location.protocol + "//" + document.domain + ":" + location.port + "/" + package_name + '/' + sub);
$(document).ready(function(){
});
socket.on('start', function(data){
on_start();
});
socket.on('list_refresh', function(data){
on_start()
});
socket.on('status', function(data){
console.log(data);
on_status(data)
});
function on_start() {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/entity_list',
type: "POST",
cache: false,
data: {},
dataType: "json",
success: function (data) {
make_download_list(data)
}
});
}
function on_status(data) {
//console.log(data)
tmp = document.getElementById("progress_"+data.entity_id)
if (tmp != null) {
document.getElementById("progress_"+data.entity_id).style.width = data.ffmpeg_percent+ '%';
document.getElementById("progress_"+data.entity_id+"_label").innerHTML = data.ffmpeg_status_kor + "(" + data.ffmpeg_percent + "%)" + ' ' + ((data.ffmpeg_arg != null)?data.ffmpeg_arg.data.current_speed:'')
}
}
function make_download_list(data) {
str = '';
for (i in data) {
str += m_row_start();
str += m_col(1, data[i].entity_id);
str += m_col(2, data[i].created_time);
str += m_col(4, (data[i].filename != null) ? data[i].filename : '');
label = data[i].ffmpeg_status_kor
if (data[i].ffmpeg_percent != 0) {
label += '(' + data[i].ffmpeg_percent + '%)'
}
tmp = m_progress('progress_'+data[i].entity_id, data[i].ffmpeg_percent, label)
str += m_col(3, tmp);
tmp = m_button('program_cancel_btn', '취소', [{'key':'id', 'value':data[i].entity_id}]);
tmp = m_button_group(tmp)
str += m_col(2, tmp)
str += m_row_end();
if (i != data.length -1) str += m_hr(0);
}
document.getElementById("download_list_div").innerHTML = str;
}
$("body").on('click', '#program_cancel_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'cancel', 'entity_id':entity_id}
queue_command(send_data)
});
$("body").on('click', '#reset_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'reset', 'entity_id':-1}
queue_command(send_data)
});
$("body").on('click', '#delete_completed_btn', function(e){
e.preventDefault();
entity_id = $(this).data('id')
send_data = {'command':'delete_completed', 'entity_id':-1}
queue_command(send_data)
});
function queue_command(data) {
$.ajax({
url: '/' + package_name + '/ajax/' + sub + '/queue_command',
type: "POST",
cache: false,
data: data,
dataType: "json",
success: function (ret) {
if (ret.ret == 'notify') {
$.notify('<strong>'+ ret.log +'</strong>', {type: 'warning'});
}
on_start();
}
});
}
$("body").on('click', '#go_ffmpeg_btn', function(e){
e.preventDefault();
$(location).attr('href', '/ffmpeg')
});
</script>
{% endblock %}

View File

@@ -0,0 +1,642 @@
{% 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_linkkf_btn', 'Go 링크 애니']], desc='예)
"https://linkkf.app/코드" 나 "코드"') }}
</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 linkkf_url = "{{arg['linkkf_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 +=
'&nbsp;&nbsp;&nbsp;<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 +=
'&nbsp;&nbsp;&nbsp;<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 mt-sm-3 p-sm-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">&nbsp;&nbsp;&nbsp;&nbsp;';
// 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['linkkf_current_code']}}" !== "") {
if (params.code === null) {
console.log('params.code === null')
document.getElementById("code").value = "{{arg['linkkf_current_code']}}";
} else if (params.code === '') {
document.getElementById("code").value = "{{arg['linkkf_current_code']}}";
} else {
console.log('params code exist')
console.log(params.code)
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 button = document.getElementById('analysis_btn');
const code = document.getElementById("code").value
button.setAttribute("disabled", "disabled");
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)
button.removeAttribute("disabled");
} else {
$.notify('<strong>분석 실패</strong><br>' + ret.log, {type: 'warning'});
}
}
});
});
$("body").on('click', '#go_linkkf_btn', function (e) {
e.preventDefault();
window.open("{{arg['linkkf_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 %}

File diff suppressed because it is too large Load Diff

View File

@@ -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='4', 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=['구분자 | 또는 엔터'], 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 %}

View File

@@ -0,0 +1,921 @@
{% extends "base.html" %} {% block content %}
<!--<div id="preloader"></div>-->
<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>
<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>
<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="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_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 "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
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)
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) {
console.log("call make_airing_list()")
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 +
"&amp;wr_id=" +
data.anime_list[i].wr_id +
"&amp;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 += '<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 = ""
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) {
tmp = '<div class="col-sm-4">'
tmp += '<div class="card">'
tmp += '<img class="card-img-top" src="' + data.anime_list[i].image_link + '" />'
tmp += '<div class="card-body">'
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?code=' +
data.anime_list[i].code +
'" 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
}
$("img.lazyload").lazyload({
threshold: 10,
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 #ing").on("click", function () {
// let spinner = document.getElementById('spinner');
// spinner.style.visibility = 'visible';
get_anime_list("ing", 1)
})
$("#anime_category #complete_anilist").on("click", function () {
// let spinner = document.getElementById("spinner")
// spinner.style.visibility = "visible"
get_anime_list("fin", 1)
})
$("#anime_category #theater").on("click", function () {
// let spinner = document.getElementById("spinner")
// spinner.style.visibility = "visible"
get_anime_list("theater", 1)
})
// 분석 버튼 클릭시 호출
$("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();
const loadNextAnimes = (cate, page) => {
spinner.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#}
if (current_cate === "search") {
make_search_result_list(response.data, response.page)
} else {
make_screen_movie_list(response.data, response.page)
}
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) {
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)
}
}
const debounce = (func, delay) => {
let timeoutId = null
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(func.bind(null, ...args), delay)
}
}
document.addEventListener("scroll", debounce(onScroll, 300))
</script>
<style>
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%;
}
#screen_movie_list {
margin-top: 10px;
}
.card-body {
padding: 0 !important;
}
.card-title {
padding: 0.5rem 0.5rem !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(0.175, 0.885, 0.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);
}
}
</style>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"
/>
{% endblock %}

View File

@@ -34,6 +34,7 @@ socket.on('list_refresh', function(data){
});
socket.on('status', function(data){
console.log(data);
on_status(data)
});

View File

@@ -1,14 +1,36 @@
{% extends "base.html" %}
{% block content %}
{% extends "base.html" %} {% block content %}
<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 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>
<div id="episode_list"></div>
</form>
</div> <!--전체-->
</div>
<!--전체-->
<script type="text/javascript">
const package_name = "{{arg['package_name'] }}";
@@ -16,28 +38,49 @@
const ohli24_url = "{{arg['ohli24_url']}}";
let current_data = null;
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
})
$(document).ready(function(){
if ( "{{arg['ohli24_current_code']}}" !== "" ) {
document.getElementById("code").value = "{{arg['ohli24_current_code']}}";
// 값이 공백이 아니면 분석 버튼 계속 누름
{#document.getElementById("analysis_btn").click();#}
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;
}
// 분석 버튼 클릭시 호출
$("body").on('click', '#analysis_btn', function(e){
e.preventDefault();
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},
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.code)#}
console.log(ret.data)
make_program(ret.data)
} else {
@@ -45,10 +88,11 @@
}
}
});
}
function make_program(data) {
current_data = data;
console.log(current_data)
console.log("current_data::", current_data)
str = '';
tmp = '<div class="form-inline">'
tmp += m_button('check_download_btn', '선택 다운로드 추가', []);
@@ -74,53 +118,121 @@
tmp += m_row_start(2) + m_col(3, '원제', 'right') + m_col(9, data.des._otit) + m_row_end();
tmp += m_row_start(2) + m_col(3, '감독', 'right') + m_col(9, data.des._dir) + m_row_end();
tmp += m_row_start(2) + m_col(3, '제작사', 'right') + m_col(9, data.des._pub) + m_row_end();
{#tmp += m_row_start(2) + m_col(3, '장르', 'right') + m_col(9, data.des._tag.join(' | ')) + m_row_end();#}
tmp += m_row_start(2) + m_col(3, '장르', 'right') + m_col(9, data.des._tag) + m_row_end();
tmp += m_row_start(2) + m_col(3, '분류', 'right') + m_col(9, data.des._classifi) + m_row_end();
tmp += m_row_start(2) + m_col(3, '방영일', 'right') + m_col(9, data.date+'('+data.day+')') + m_row_end();
tmp += m_row_start(2) + m_col(3, '방영일', 'right') + m_col(9, data.date + '(' + data.day + ')') + m_row_end();
tmp += m_row_start(2) + m_col(3, '등급', 'right') + m_col(9, data.des._grade) + m_row_end();
tmp += m_row_start(2) + m_col(3, '총화수', 'right') + m_col(9, data.des._total_chapter ? data.des._total_chapter : '') + m_row_end();
tmp += m_row_start(2) + m_col(3, '상영시간', 'right') + m_col(9, data.des._show_time ? data.des._show_time : '') + m_row_end();
tmp += m_row_start(2) + m_col(3, '줄거리', 'right') + m_col(9, data.ser_description) + m_row_end();
str += m_col(9, tmp)
str += m_row_end();
str += m_hr_black();
for (i in data.episode) {
str += m_row_start(); tmp = '';
str += m_row_start();
tmp = '';
if (data.episode[i].thumbnail)
tmp = '<img src="'+ data.episode[i].thumbnail + '" class="img-fluid">'
tmp = '<img src="' + data.episode[i].thumbnail + '" class="img-fluid">'
str += m_col(3, tmp)
tmp = '<strong>' + data.episode[i].title+ '</strong>';
tmp = '<strong>' + data.episode[i].title + '</strong>';
tmp += '<br>';
tmp += data.episode[i].date + '<br>';
tmp += '<div class="form-inline">'
tmp += '<input id="checkbox_'+i+'" name="checkbox_'+i+'" type="checkbox" checked data-toggle="toggle" data-on="선 택" data-off="-" data-onstyle="success" data-offstyle="danger" data-size="small">&nbsp;&nbsp;&nbsp;&nbsp;'
tmp += m_button('add_queue_btn', '다운로드 추가', [{'key':'idx', 'value':i}])
tmp += '<input id="checkbox_' + i + '" name="checkbox_' + i + '" type="checkbox" checked data-toggle="toggle" data-on="선 택" data-off="-" data-onstyle="success" data-offstyle="danger" data-size="small">&nbsp;&nbsp;&nbsp;&nbsp;'
tmp += m_button('add_queue_btn', '다운로드 추가', [{'key': 'idx', 'value': i}])
tmp += '</div>'
str += m_col(9, tmp)
str += m_row_end();
if (i != data.length -1) str += m_hr(0);
if (i != data.length - 1) str += m_hr(0);
}
document.getElementById("episode_list").innerHTML = str;
$('input[id^="checkbox_"]').bootstrapToggle()
}
$("body").on('click', '#go_ohli24_btn', function(e){
$(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){
$("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){
$("body").on('click', '#all_check_off_btn', function (e) {
e.preventDefault();
$('input[id^="checkbox_"]').bootstrapToggle('off')
});
$("body").on('click', '#add_queue_btn', function(e){
$("body").on('click', '#add_queue_btn', function (e) {
e.preventDefault();
data = current_data.episode[$(this).data('idx')];
console.log('data:::>', data)
@@ -128,9 +240,10 @@
url: '/' + package_name + '/ajax/' + sub + '/add_queue',
type: "POST",
cache: false,
data: {data:JSON.stringify(data)},
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') {
@@ -142,9 +255,293 @@
}
}
});
});
});
});
});
$("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>
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%;
}
#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%;
}
.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 %}

View File

@@ -23,8 +23,8 @@
{{ macros.setting_input_text('ohli24_finished_insert', '완결 표시', col='3', value=arg['ohli24_finished_insert'], desc=['완결된 컨텐츠 폴더명 앞에 넣을 문구입니다.']) }}
{{ macros.setting_checkbox('ohli24_auto_make_season_folder', '시즌 폴더 생성', value=arg['ohli24_auto_make_season_folder'], desc=['On : Season 번호 폴더를 만듭니다.']) }}
</div>
{# {{ macros.setting_checkbox('ani365_incompleted_auto_enqueue', '자동으로 다시 받기', value=arg['ani365_incompleted_auto_enqueue'], desc=['On : 플러그인 로딩시 미완료인 항목은 자동으로 다시 받습니다.']) }}#}
{{ macros.setting_checkbox('ohli24_uncompleted_auto_enqueue', '자동으로 다시 받기', value=arg['ohli24_uncompleted_auto_enqueue'], desc=['On : 플러그인 로딩시 미완료인 항목은 자동으로 다시 받습니다.']) }}
{{ macros.setting_checkbox('ohli24_discord_notify', '디스 코드 알림 받기', value=arg['ohli24_discord_notify'], desc=['On : 새로운 글이 올라올때 디스코드 알림을 보냅니다.']) }}
{{ macros.m_tab_content_end() }}
{{ macros.m_tab_content_start('auto', false) }}