pylint, black 적용

This commit is contained in:
joyfuI
2022-04-30 18:57:23 +09:00
parent d2afafd77d
commit 15d98a96ec
17 changed files with 993 additions and 833 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
.idea/
.venv/ .venv/
__pycache__/ __pycache__/
test/ test/

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"files.associations": {
"*.html": "jinja-html"
}
}

View File

@@ -12,20 +12,21 @@ from framework.util import Util
from .logic_normal import LogicNormal from .logic_normal import LogicNormal
from .model import ModelSetting from .model import ModelSetting
package_name = __name__.split('.')[0] package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name) logger = get_logger(package_name)
class Logic(object): class Logic(object):
db_default = { db_default = {
'db_version': '2', "db_version": "2",
'youtube_dl_package': '0', "youtube_dl_package": "0",
'ffmpeg_path': '' if platform.system() != 'Windows' else os.path.join(path_app_root, 'bin', 'Windows', "ffmpeg_path": ""
'ffmpeg.exe'), if platform.system() != "Windows"
'temp_path': os.path.join(path_data, 'download_tmp'), else os.path.join(path_app_root, "bin", "Windows", "ffmpeg.exe"),
'save_path': os.path.join(path_data, 'download'), "temp_path": os.path.join(path_data, "download_tmp"),
'default_filename': '', "save_path": os.path.join(path_data, "download"),
'proxy': '' "default_filename": "",
"proxy": "",
} }
@staticmethod @staticmethod
@@ -37,51 +38,66 @@ class Logic(object):
db.session.commit() db.session.commit()
Logic.migration() Logic.migration()
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def plugin_load(): def plugin_load():
try: try:
logger.debug('%s plugin_load', package_name) logger.debug("%s plugin_load", package_name)
Logic.db_init() Logic.db_init()
# youtube-dl 업데이트 # youtube-dl 업데이트
youtube_dl = LogicNormal.get_youtube_dl_package(ModelSetting.get('youtube_dl_package')) youtube_dl = LogicNormal.get_youtube_dl_package(
logger.debug('%s upgrade' % youtube_dl) ModelSetting.get("youtube_dl_package")
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', '--upgrade', youtube_dl], )
universal_newlines=True)) logger.debug(f"{youtube_dl} upgrade")
logger.debug(
subprocess.check_output(
[sys.executable, "-m", "pip", "install", "--upgrade", youtube_dl],
universal_newlines=True,
)
)
# 편의를 위해 json 파일 생성 # 편의를 위해 json 파일 생성
from .plugin import plugin_info from .plugin import plugin_info
Util.save_from_dict_to_json(plugin_info, os.path.join(os.path.dirname(__file__), 'info.json'))
Util.save_from_dict_to_json(
plugin_info, os.path.join(os.path.dirname(__file__), "info.json")
)
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def plugin_unload(): def plugin_unload():
try: try:
logger.debug('%s plugin_unload', package_name) logger.debug("%s plugin_unload", package_name)
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def migration(): def migration():
try: try:
db_version = ModelSetting.get_int('db_version') db_version = ModelSetting.get_int("db_version")
connect = sqlite3.connect(os.path.join(path_data, 'db', '%s.db' % package_name)) connect = sqlite3.connect(
os.path.join(path_data, "db", f"{package_name}.db")
)
if db_version < 2: if db_version < 2:
logger.debug('youtube-dlc uninstall') logger.debug("youtube-dlc uninstall")
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'uninstall', '-y', 'youtube-dlc'], logger.debug(
universal_newlines=True)) subprocess.check_output(
[sys.executable, "-m", "pip", "uninstall", "-y", "youtube-dlc"],
universal_newlines=True,
)
)
connect.commit() connect.commit()
connect.close() connect.close()
ModelSetting.set('db_version', Logic.db_default['db_version']) ModelSetting.set("db_version", Logic.db_default["db_version"])
db.session.flush() db.session.flush()
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())

View File

@@ -7,7 +7,7 @@ from framework.logger import get_logger
from .my_youtube_dl import MyYoutubeDL, Status from .my_youtube_dl import MyYoutubeDL, Status
package_name = __name__.split('.')[0] package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name) logger = get_logger(package_name)
@@ -16,8 +16,8 @@ class LogicNormal(object):
@staticmethod @staticmethod
def get_youtube_dl_package(index=None, import_pkg=False): def get_youtube_dl_package(index=None, import_pkg=False):
packages = ['youtube-dl', 'yt-dlp'] packages = ["youtube-dl", "yt-dlp"]
import_name = ['youtube_dl', 'yt_dlp'] import_name = ["youtube_dl", "yt_dlp"]
if import_pkg: if import_pkg:
return import_name if index is None else import_name[int(index)] return import_name if index is None else import_name[int(index)]
else: else:
@@ -28,9 +28,9 @@ class LogicNormal(object):
try: try:
return MyYoutubeDL.get_version() return MyYoutubeDL.get_version()
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return '패키지 임포트 실패' return "패키지 임포트 실패"
@staticmethod @staticmethod
def get_default_filename(): def get_default_filename():
@@ -39,37 +39,40 @@ class LogicNormal(object):
@staticmethod @staticmethod
def get_preset_list(): def get_preset_list():
return [ return [
['bestvideo+bestaudio/best', '최고 화질'], ["bestvideo+bestaudio/best", "최고 화질"],
['bestvideo[height<=1080]+bestaudio/best[height<=1080]', '1080p'], ["bestvideo[height<=1080]+bestaudio/best[height<=1080]", "1080p"],
['worstvideo+worstaudio/worst', '최저 화질'], ["worstvideo+worstaudio/worst", "최저 화질"],
['bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]', '최고 화질(mp4)'], ["bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]", "최고 화질(mp4)"],
['bestvideo[ext=mp4][height<=1080]+bestaudio[ext=m4a]/best[ext=mp4][height<=1080]', '1080p(mp4)'], [
['bestvideo[filesize<50M]+bestaudio/best[filesize<50M]', '50MB 미만'], "bestvideo[ext=mp4][height<=1080]+bestaudio[ext=m4a]/best[ext=mp4][height<=1080]",
['bestaudio/best', '오디오만'], "1080p(mp4)",
['_custom', '사용자 정의'] ],
["bestvideo[filesize<50M]+bestaudio/best[filesize<50M]", "50MB 미만"],
["bestaudio/best", "오디오만"],
["_custom", "사용자 정의"],
] ]
@staticmethod @staticmethod
def get_postprocessor_list(): def get_postprocessor_list():
return [ return [
['', '후처리 안함', None], ["", "후처리 안함", None],
['mp4', 'MP4', '비디오 변환'], ["mp4", "MP4", "비디오 변환"],
['flv', 'FLV', '비디오 변환'], ["flv", "FLV", "비디오 변환"],
['webm', 'WebM', '비디오 변환'], ["webm", "WebM", "비디오 변환"],
['ogg', 'Ogg', '비디오 변환'], ["ogg", "Ogg", "비디오 변환"],
['mkv', 'MKV', '비디오 변환'], ["mkv", "MKV", "비디오 변환"],
['ts', 'TS', '비디오 변환'], ["ts", "TS", "비디오 변환"],
['avi', 'AVI', '비디오 변환'], ["avi", "AVI", "비디오 변환"],
['wmv', 'WMV', '비디오 변환'], ["wmv", "WMV", "비디오 변환"],
['mov', 'MOV', '비디오 변환'], ["mov", "MOV", "비디오 변환"],
['gif', 'GIF', '비디오 변환'], ["gif", "GIF", "비디오 변환"],
['mp3', 'MP3', '오디오 추출'], ["mp3", "MP3", "오디오 추출"],
['aac', 'AAC', '오디오 추출'], ["aac", "AAC", "오디오 추출"],
['flac', 'FLAC', '오디오 추출'], ["flac", "FLAC", "오디오 추출"],
['m4a', 'M4A', '오디오 추출'], ["m4a", "M4A", "오디오 추출"],
['opus', 'Opus', '오디오 추출'], ["opus", "Opus", "오디오 추출"],
['vorbis', 'Vorbis', '오디오 추출'], ["vorbis", "Vorbis", "오디오 추출"],
['wav', 'WAV', '오디오 추출'] ["wav", "WAV", "오디오 추출"],
] ]
@staticmethod @staticmethod
@@ -77,9 +80,9 @@ class LogicNormal(object):
video_convertor = [] video_convertor = []
extract_audio = [] extract_audio = []
for i in LogicNormal.get_postprocessor_list(): for i in LogicNormal.get_postprocessor_list():
if i[2] == '비디오 변환': if i[2] == "비디오 변환":
video_convertor.append(i[0]) video_convertor.append(i[0])
elif i[2] == '오디오 추출': elif i[2] == "오디오 추출":
extract_audio.append(i[0]) extract_audio.append(i[0])
return video_convertor, extract_audio return video_convertor, extract_audio
@@ -87,50 +90,56 @@ class LogicNormal(object):
def download(**kwagrs): def download(**kwagrs):
try: try:
logger.debug(kwagrs) logger.debug(kwagrs)
plugin = kwagrs['plugin'] plugin = kwagrs["plugin"]
url = kwagrs['url'] url = kwagrs["url"]
filename = kwagrs['filename'] filename = kwagrs["filename"]
temp_path = kwagrs['temp_path'] temp_path = kwagrs["temp_path"]
save_path = kwagrs['save_path'] save_path = kwagrs["save_path"]
opts = {} opts = {}
if 'format' in kwagrs and kwagrs['format']: if "format" in kwagrs and kwagrs["format"]:
opts['format'] = kwagrs['format'] opts["format"] = kwagrs["format"]
postprocessor = [] postprocessor = []
if 'preferedformat' in kwagrs and kwagrs['preferedformat']: if "preferedformat" in kwagrs and kwagrs["preferedformat"]:
postprocessor.append({ postprocessor.append(
'key': 'FFmpegVideoConvertor', {
'preferedformat': kwagrs['preferedformat'] "key": "FFmpegVideoConvertor",
}) "preferedformat": kwagrs["preferedformat"],
if 'preferredcodec' in kwagrs and kwagrs['preferredcodec']: }
postprocessor.append({ )
'key': 'FFmpegExtractAudio', if "preferredcodec" in kwagrs and kwagrs["preferredcodec"]:
'preferredcodec': kwagrs['preferredcodec'], postprocessor.append(
'preferredquality': str(kwagrs['preferredquality']) {
}) "key": "FFmpegExtractAudio",
"preferredcodec": kwagrs["preferredcodec"],
"preferredquality": str(kwagrs["preferredquality"]),
}
)
if postprocessor: if postprocessor:
opts['postprocessors'] = postprocessor opts["postprocessors"] = postprocessor
if 'playlist' in kwagrs and kwagrs['playlist']: if "playlist" in kwagrs and kwagrs["playlist"]:
if kwagrs['playlist'] == 'reverse': if kwagrs["playlist"] == "reverse":
opts['playlistreverse'] = True opts["playlistreverse"] = True
elif kwagrs['playlist'] == 'random': elif kwagrs["playlist"] == "random":
opts['playlistrandom'] = True opts["playlistrandom"] = True
else: else:
opts['playlist_items'] = kwagrs['playlist'] opts["playlist_items"] = kwagrs["playlist"]
if 'archive' in kwagrs and kwagrs['archive']: if "archive" in kwagrs and kwagrs["archive"]:
opts['download_archive'] = kwagrs['archive'] opts["download_archive"] = kwagrs["archive"]
if 'proxy' in kwagrs and kwagrs['proxy']: if "proxy" in kwagrs and kwagrs["proxy"]:
opts['proxy'] = kwagrs['proxy'] opts["proxy"] = kwagrs["proxy"]
if 'ffmpeg_path' in kwagrs and kwagrs['ffmpeg_path']: if "ffmpeg_path" in kwagrs and kwagrs["ffmpeg_path"]:
opts['ffmpeg_location'] = kwagrs['ffmpeg_path'] opts["ffmpeg_location"] = kwagrs["ffmpeg_path"]
if 'cookiefile' in kwagrs and kwagrs['cookiefile']: if "cookiefile" in kwagrs and kwagrs["cookiefile"]:
opts['cookiefile'] = kwagrs['cookiefile'] opts["cookiefile"] = kwagrs["cookiefile"]
dateafter = kwagrs.get('dateafter') dateafter = kwagrs.get("dateafter")
youtube_dl = MyYoutubeDL(plugin, 'video', url, filename, temp_path, save_path, opts, dateafter) youtube_dl = MyYoutubeDL(
youtube_dl.key = kwagrs.get('key') plugin, "video", url, filename, temp_path, save_path, opts, dateafter
)
youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
@@ -138,40 +147,50 @@ class LogicNormal(object):
def thumbnail(**kwagrs): def thumbnail(**kwagrs):
try: try:
logger.debug(kwagrs) logger.debug(kwagrs)
plugin = kwagrs['plugin'] plugin = kwagrs["plugin"]
url = kwagrs['url'] url = kwagrs["url"]
filename = kwagrs['filename'] filename = kwagrs["filename"]
temp_path = kwagrs['temp_path'] temp_path = kwagrs["temp_path"]
save_path = kwagrs['save_path'] save_path = kwagrs["save_path"]
opts = { opts = {"skip_download": True}
'skip_download': True if (
} "all_thumbnails" in kwagrs
if 'all_thumbnails' in kwagrs and str(kwagrs['all_thumbnails']).lower() != 'false': and str(kwagrs["all_thumbnails"]).lower() != "false"
opts['write_all_thumbnails'] = True ):
opts["write_all_thumbnails"] = True
else: else:
opts['writethumbnail'] = True opts["writethumbnail"] = True
if 'playlist' in kwagrs and kwagrs['playlist']: if "playlist" in kwagrs and kwagrs["playlist"]:
if kwagrs['playlist'] == 'reverse': if kwagrs["playlist"] == "reverse":
opts['playlistreverse'] = True opts["playlistreverse"] = True
elif kwagrs['playlist'] == 'random': elif kwagrs["playlist"] == "random":
opts['playlistrandom'] = True opts["playlistrandom"] = True
else: else:
opts['playlist_items'] = kwagrs['playlist'] opts["playlist_items"] = kwagrs["playlist"]
if 'archive' in kwagrs and kwagrs['archive']: if "archive" in kwagrs and kwagrs["archive"]:
opts['download_archive'] = kwagrs['archive'] opts["download_archive"] = kwagrs["archive"]
if 'proxy' in kwagrs and kwagrs['proxy']: if "proxy" in kwagrs and kwagrs["proxy"]:
opts['proxy'] = kwagrs['proxy'] opts["proxy"] = kwagrs["proxy"]
if 'ffmpeg_path' in kwagrs and kwagrs['ffmpeg_path']: if "ffmpeg_path" in kwagrs and kwagrs["ffmpeg_path"]:
opts['ffmpeg_location'] = kwagrs['ffmpeg_path'] opts["ffmpeg_location"] = kwagrs["ffmpeg_path"]
if 'cookiefile' in kwagrs and kwagrs['cookiefile']: if "cookiefile" in kwagrs and kwagrs["cookiefile"]:
opts['cookiefile'] = kwagrs['cookiefile'] opts["cookiefile"] = kwagrs["cookiefile"]
dateafter = kwagrs.get('dateafter') dateafter = kwagrs.get("dateafter")
youtube_dl = MyYoutubeDL(plugin, 'thumbnail', url, filename, temp_path, save_path, opts, dateafter) youtube_dl = MyYoutubeDL(
youtube_dl.key = kwagrs.get('key') plugin,
"thumbnail",
url,
filename,
temp_path,
save_path,
opts,
dateafter,
)
youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
@@ -179,45 +198,47 @@ class LogicNormal(object):
def sub(**kwagrs): def sub(**kwagrs):
try: try:
logger.debug(kwagrs) logger.debug(kwagrs)
plugin = kwagrs['plugin'] plugin = kwagrs["plugin"]
url = kwagrs['url'] url = kwagrs["url"]
filename = kwagrs['filename'] filename = kwagrs["filename"]
temp_path = kwagrs['temp_path'] temp_path = kwagrs["temp_path"]
save_path = kwagrs['save_path'] save_path = kwagrs["save_path"]
opts = { opts = {"skip_download": True}
'skip_download': True sub_lang = map(
} lambda x: x.strip(), kwagrs["sub_lang"].split(",")
sub_lang = map(lambda x: x.strip(), kwagrs['sub_lang'].split(',')) # 문자열을 리스트로 변환 ) # 문자열을 리스트로 변환
if 'all_subs' in kwagrs and str(kwagrs['all_subs']).lower() != 'false': if "all_subs" in kwagrs and str(kwagrs["all_subs"]).lower() != "false":
opts['allsubtitles'] = True opts["allsubtitles"] = True
else: else:
opts['subtitleslangs'] = sub_lang opts["subtitleslangs"] = sub_lang
if 'auto_sub' in kwagrs and str(kwagrs['auto_sub']).lower() != 'false': if "auto_sub" in kwagrs and str(kwagrs["auto_sub"]).lower() != "false":
opts['writeautomaticsub'] = True opts["writeautomaticsub"] = True
else: else:
opts['writesubtitles'] = True opts["writesubtitles"] = True
if 'playlist' in kwagrs and kwagrs['playlist']: if "playlist" in kwagrs and kwagrs["playlist"]:
if kwagrs['playlist'] == 'reverse': if kwagrs["playlist"] == "reverse":
opts['playlistreverse'] = True opts["playlistreverse"] = True
elif kwagrs['playlist'] == 'random': elif kwagrs["playlist"] == "random":
opts['playlistrandom'] = True opts["playlistrandom"] = True
else: else:
opts['playlist_items'] = kwagrs['playlist'] opts["playlist_items"] = kwagrs["playlist"]
if 'archive' in kwagrs and kwagrs['archive']: if "archive" in kwagrs and kwagrs["archive"]:
opts['download_archive'] = kwagrs['archive'] opts["download_archive"] = kwagrs["archive"]
if 'proxy' in kwagrs and kwagrs['proxy']: if "proxy" in kwagrs and kwagrs["proxy"]:
opts['proxy'] = kwagrs['proxy'] opts["proxy"] = kwagrs["proxy"]
if 'ffmpeg_path' in kwagrs and kwagrs['ffmpeg_path']: if "ffmpeg_path" in kwagrs and kwagrs["ffmpeg_path"]:
opts['ffmpeg_location'] = kwagrs['ffmpeg_path'] opts["ffmpeg_location"] = kwagrs["ffmpeg_path"]
if 'cookiefile' in kwagrs and kwagrs['cookiefile']: if "cookiefile" in kwagrs and kwagrs["cookiefile"]:
opts['cookiefile'] = kwagrs['cookiefile'] opts["cookiefile"] = kwagrs["cookiefile"]
dateafter = kwagrs.get('dateafter') dateafter = kwagrs.get("dateafter")
youtube_dl = MyYoutubeDL(plugin, 'subtitle', url, filename, temp_path, save_path, opts, dateafter) youtube_dl = MyYoutubeDL(
youtube_dl.key = kwagrs.get('key') plugin, "subtitle", url, filename, temp_path, save_path, opts, dateafter
)
youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
@@ -225,50 +246,79 @@ class LogicNormal(object):
def get_data(youtube_dl): def get_data(youtube_dl):
try: try:
data = {} data = {}
data['plugin'] = youtube_dl.plugin data["plugin"] = youtube_dl.plugin
data['url'] = youtube_dl.url data["url"] = youtube_dl.url
data['filename'] = youtube_dl.filename data["filename"] = youtube_dl.filename
data['temp_path'] = youtube_dl.temp_path data["temp_path"] = youtube_dl.temp_path
data['save_path'] = youtube_dl.save_path data["save_path"] = youtube_dl.save_path
data['index'] = youtube_dl.index data["index"] = youtube_dl.index
data['status_str'] = youtube_dl.status.name data["status_str"] = youtube_dl.status.name
data['status_ko'] = str(youtube_dl.status) data["status_ko"] = str(youtube_dl.status)
data['end_time'] = '' data["end_time"] = ""
data['extractor'] = youtube_dl.type + ( data["extractor"] = youtube_dl.type + (
' - ' + youtube_dl.info_dict['extractor'] if youtube_dl.info_dict['extractor'] is not None else '') " - " + youtube_dl.info_dict["extractor"]
data['title'] = youtube_dl.info_dict['title'] if \ if youtube_dl.info_dict["extractor"] is not None
youtube_dl.info_dict['title'] is not None else youtube_dl.url else ""
data['uploader'] = youtube_dl.info_dict['uploader'] if youtube_dl.info_dict['uploader'] is not None else '' )
data['uploader_url'] = youtube_dl.info_dict['uploader_url'] if \ data["title"] = (
youtube_dl.info_dict['uploader_url'] is not None else '' youtube_dl.info_dict["title"]
data['downloaded_bytes_str'] = '' if youtube_dl.info_dict["title"] is not None
data['total_bytes_str'] = '' else youtube_dl.url
data['percent'] = '0' )
data['eta'] = youtube_dl.progress_hooks['eta'] if youtube_dl.progress_hooks['eta'] is not None else '' data["uploader"] = (
data['speed_str'] = LogicNormal.human_readable_size(youtube_dl.progress_hooks['speed'], '/s') if \ youtube_dl.info_dict["uploader"]
youtube_dl.progress_hooks['speed'] is not None else '' if youtube_dl.info_dict["uploader"] is not None
else ""
)
data["uploader_url"] = (
youtube_dl.info_dict["uploader_url"]
if youtube_dl.info_dict["uploader_url"] is not None
else ""
)
data["downloaded_bytes_str"] = ""
data["total_bytes_str"] = ""
data["percent"] = "0"
data["eta"] = (
youtube_dl.progress_hooks["eta"]
if youtube_dl.progress_hooks["eta"] is not None
else ""
)
data["speed_str"] = (
LogicNormal.human_readable_size(
youtube_dl.progress_hooks["speed"], "/s"
)
if youtube_dl.progress_hooks["speed"] is not None
else ""
)
if youtube_dl.status == Status.READY: # 다운로드 전 if youtube_dl.status == Status.READY: # 다운로드 전
data['start_time'] = '' data["start_time"] = ""
data['download_time'] = '' data["download_time"] = ""
else: else:
if youtube_dl.end_time is None: # 완료 전 if youtube_dl.end_time is None: # 완료 전
download_time = datetime.now() - youtube_dl.start_time download_time = datetime.now() - youtube_dl.start_time
else: else:
download_time = youtube_dl.end_time - youtube_dl.start_time download_time = youtube_dl.end_time - youtube_dl.start_time
data['end_time'] = youtube_dl.end_time.strftime('%m-%d %H:%M:%S') data["end_time"] = youtube_dl.end_time.strftime("%m-%d %H:%M:%S")
if None not in (youtube_dl.progress_hooks['downloaded_bytes'], if None not in (
youtube_dl.progress_hooks['total_bytes']): # 둘 다 값이 있으면 youtube_dl.progress_hooks["downloaded_bytes"],
data['downloaded_bytes_str'] = LogicNormal.human_readable_size( youtube_dl.progress_hooks["total_bytes"],
youtube_dl.progress_hooks['downloaded_bytes'] ): # 둘 다 값이 있으면
data["downloaded_bytes_str"] = LogicNormal.human_readable_size(
youtube_dl.progress_hooks["downloaded_bytes"]
) )
data['total_bytes_str'] = LogicNormal.human_readable_size(youtube_dl.progress_hooks['total_bytes']) data["total_bytes_str"] = LogicNormal.human_readable_size(
data['percent'] = '%.2f' % (float(youtube_dl.progress_hooks['downloaded_bytes']) youtube_dl.progress_hooks["total_bytes"]
/ float(youtube_dl.progress_hooks['total_bytes']) * 100) )
data['start_time'] = youtube_dl.start_time.strftime('%m-%d %H:%M:%S') data[
data['download_time'] = '%02d:%02d' % (download_time.seconds / 60, download_time.seconds % 60) "percent"
] = f"{float(youtube_dl.progress_hooks['downloaded_bytes']) / float(youtube_dl.progress_hooks['total_bytes']) * 100:.2f}"
data["start_time"] = youtube_dl.start_time.strftime("%m-%d %H:%M:%S")
data[
"download_time"
] = f"{download_time.seconds / 60:02d}:{download_time.seconds % 60:02d}"
return data return data
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
@@ -277,14 +327,14 @@ class LogicNormal(object):
return MyYoutubeDL.get_info_dict(url, proxy) return MyYoutubeDL.get_info_dict(url, proxy)
@staticmethod @staticmethod
def human_readable_size(size, suffix=''): def human_readable_size(size, suffix=""):
for unit in ('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB'): for unit in ("Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"):
if size < 1024.0: if size < 1024.0:
return '%3.1f %s%s' % (size, unit, suffix) return f"{size:3.1f} {unit}{suffix}"
size /= 1024.0 size /= 1024.0
return '%.1f %s%s' % (size, 'YB', suffix) return f"{size:.1f} YB{suffix}"
@staticmethod @staticmethod
def abort(base, code): def abort(base, code):
base['errorCode'] = code base["errorCode"] = code
return jsonify(base) return jsonify(base)

View File

@@ -5,14 +5,16 @@ from framework import app, db, path_data
from framework.logger import get_logger from framework.logger import get_logger
from framework.util import Util from framework.util import Util
package_name = __name__.split('.')[0] package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name) logger = get_logger(package_name)
app.config['SQLALCHEMY_BINDS'][package_name] = 'sqlite:///%s' % (os.path.join(path_data, 'db', '%s.db' % package_name)) app.config["SQLALCHEMY_BINDS"][package_name] = "sqlite:///%s" % (
os.path.join(path_data, "db", f"{package_name}.db")
)
class ModelSetting(db.Model): class ModelSetting(db.Model):
__tablename__ = '%s_setting' % package_name __tablename__ = f"{package_name}_setting"
__table_args__ = {'mysql_collate': 'utf8_general_ci'} __table_args__ = {"mysql_collate": "utf8_general_ci"}
__bind_key__ = package_name __bind_key__ = package_name
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@@ -32,9 +34,11 @@ class ModelSetting(db.Model):
@staticmethod @staticmethod
def get(key): def get(key):
try: try:
return db.session.query(ModelSetting).filter_by(key=key).first().value.strip() return (
db.session.query(ModelSetting).filter_by(key=key).first().value.strip()
)
except Exception as e: except Exception as e:
logger.error('Exception:%s %s', e, key) logger.error("Exception:%s %s", e, key)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
@@ -42,54 +46,64 @@ class ModelSetting(db.Model):
try: try:
return int(ModelSetting.get(key)) return int(ModelSetting.get(key))
except Exception as e: except Exception as e:
logger.error('Exception:%s %s', e, key) logger.error("Exception:%s %s", e, key)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def get_bool(key): def get_bool(key):
try: try:
return ModelSetting.get(key) == 'True' return ModelSetting.get(key) == "True"
except Exception as e: except Exception as e:
logger.error('Exception:%s %s', e, key) logger.error("Exception:%s %s", e, key)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def set(key, value): def set(key, value):
try: try:
item = db.session.query(ModelSetting).filter_by(key=key).with_for_update().first() item = (
db.session.query(ModelSetting)
.filter_by(key=key)
.with_for_update()
.first()
)
if item is not None: if item is not None:
item.value = value.strip() item.value = value.strip()
db.session.commit() db.session.commit()
else: else:
db.session.add(ModelSetting(key, value.strip())) db.session.add(ModelSetting(key, value.strip()))
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
logger.error('Error Key:%s Value:%s', key, value) logger.error("Error Key:%s Value:%s", key, value)
@staticmethod @staticmethod
def to_dict(): def to_dict():
try: try:
return Util.db_list_to_dict(db.session.query(ModelSetting).all()) return Util.db_list_to_dict(db.session.query(ModelSetting).all())
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@staticmethod @staticmethod
def setting_save(req): def setting_save(req):
try: try:
for key, value in req.form.items(): for key, value in req.form.items():
if key in ['scheduler', 'is_running']: if key in ["scheduler", "is_running"]:
continue continue
if key.startswith('tmp_'): if key.startswith("tmp_"):
continue continue
logger.debug('Key:%s Value:%s', key, value) logger.debug("Key:%s Value:%s", key, value)
entity = db.session.query(ModelSetting).filter_by(key=key).with_for_update().first() entity = (
db.session.query(ModelSetting)
.filter_by(key=key)
.with_for_update()
.first()
)
entity.value = value entity.value = value
db.session.commit() db.session.commit()
return True return True
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -97,9 +111,9 @@ class ModelSetting(db.Model):
def get_list(key): def get_list(key):
try: try:
value = ModelSetting.get(key) value = ModelSetting.get(key)
values = [x.strip().strip() for x in value.replace('\n', '|').split('|')] values = [x.strip().strip() for x in value.replace("\n", "|").split("|")]
values = Util.get_list_except_empty(values) values = Util.get_list_except_empty(values)
return values return values
except Exception as e: except Exception as e:
logger.error('Exception:%s %s', e, key) logger.error("Exception:%s %s", e, key)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
import os import os
import traceback import traceback
import tempfile import tempfile
import json
from glob import glob from glob import glob
from datetime import datetime from datetime import datetime
from threading import Thread from threading import Thread
@@ -12,7 +11,7 @@ from enum import Enum
from framework.logger import get_logger from framework.logger import get_logger
import framework.common.celery as celery_shutil import framework.common.celery as celery_shutil
package_name = __name__.split('.')[0] package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name) logger = get_logger(package_name)
@@ -26,30 +25,33 @@ class Status(Enum):
COMPLETED = 6 COMPLETED = 6
def __str__(self): def __str__(self):
str_list = [ str_list = ["??", "???", "?????", "??", "???", "??", "??"]
'준비',
'분석중',
'다운로드중',
'실패',
'변환중',
'중지',
'완료'
]
return str_list[self.value] return str_list[self.value]
class MyYoutubeDL(object): class MyYoutubeDL(object):
DEFAULT_FILENAME = '%(title)s-%(id)s.%(ext)s' DEFAULT_FILENAME = "%(title)s-%(id)s.%(ext)s"
_index = 0 _index = 0
_last_msg = ''
def __init__(self, plugin, type_name, url, filename, temp_path, save_path=None, opts=None, dateafter=None, def __init__(
datebefore=None): self,
plugin,
type_name,
url,
filename,
temp_path,
save_path=None,
opts=None,
dateafter=None,
datebefore=None,
):
# from youtube_dl.utils import DateRange # from youtube_dl.utils import DateRange
from .plugin import youtube_dl_package from .plugin import youtube_dl_package
DateRange = __import__('%s.utils' % youtube_dl_package, fromlist=[
'DateRange']).DateRange DateRange = __import__(
f"{youtube_dl_package}.utils", fromlist=["DateRange"]
).DateRange
if save_path is None: if save_path is None:
save_path = temp_path save_path = temp_path
@@ -61,13 +63,13 @@ class MyYoutubeDL(object):
self.filename = filename self.filename = filename
if not os.path.isdir(temp_path): if not os.path.isdir(temp_path):
os.makedirs(temp_path) os.makedirs(temp_path)
self.temp_path = tempfile.mkdtemp(prefix='youtube-dl_', dir=temp_path) self.temp_path = tempfile.mkdtemp(prefix="youtube-dl_", dir=temp_path)
if not os.path.isdir(save_path): if not os.path.isdir(save_path):
os.makedirs(save_path) os.makedirs(save_path)
self.save_path = save_path self.save_path = save_path
self.opts = opts self.opts = opts
if dateafter or datebefore: if dateafter or datebefore:
self.opts['daterange'] = DateRange(start=dateafter, end=datebefore) self.opts["daterange"] = DateRange(start=dateafter, end=datebefore)
self.index = MyYoutubeDL._index self.index = MyYoutubeDL._index
MyYoutubeDL._index += 1 MyYoutubeDL._index += 1
self._status = Status.READY self._status = Status.READY
@@ -77,10 +79,10 @@ class MyYoutubeDL(object):
self.end_time = None # 종료 시간 self.end_time = None # 종료 시간
# info_dict에서 얻는 정보 # info_dict에서 얻는 정보
self.info_dict = { self.info_dict = {
'extractor': None, # 타입 "extractor": None, # ??
'title': None, # 제목 "title": None, # ??
'uploader': None, # 업로더 "uploader": None, # ???
'uploader_url': None # 업로더 주소 "uploader_url": None, # ??? ??
} }
# info_dict에서 얻는 정보(entries) # info_dict에서 얻는 정보(entries)
# self.info_dict['playlist_index'] = None # self.info_dict['playlist_index'] = None
@@ -89,10 +91,10 @@ class MyYoutubeDL(object):
# self.info_dict['thumbnail'] = None # 썸네일 # self.info_dict['thumbnail'] = None # 썸네일
# progress_hooks에서 얻는 정보 # progress_hooks에서 얻는 정보
self.progress_hooks = { self.progress_hooks = {
'downloaded_bytes': None, # 다운로드한 크기 "downloaded_bytes": None, # ????? ??
'total_bytes': None, # 전체 크기 "total_bytes": None, # ?? ??
'eta': None, # 예상 시간(s) "eta": None, # ?? ??(s)
'speed': None # 다운로드 속도(bytes/s) "speed": None, # ???? ??(bytes/s)
} }
def start(self): def start(self):
@@ -105,34 +107,36 @@ class MyYoutubeDL(object):
def run(self): def run(self):
# import youtube_dl # import youtube_dl
from .plugin import youtube_dl_package from .plugin import youtube_dl_package
youtube_dl = __import__('%s' % youtube_dl_package)
youtube_dl = __import__(youtube_dl_package)
try: try:
self.start_time = datetime.now() self.start_time = datetime.now()
self.status = Status.START self.status = Status.START
# 동영상 정보 가져오기 # 동영상 정보 가져오기
info_dict = MyYoutubeDL.get_info_dict( info_dict = MyYoutubeDL.get_info_dict(
self.url, self.opts.get('proxy'), self.opts.get('cookiefile')) self.url, self.opts.get("proxy"), self.opts.get("cookiefile")
)
if info_dict is None: if info_dict is None:
self.status = Status.ERROR self.status = Status.ERROR
return return
self.info_dict['extractor'] = info_dict['extractor'] self.info_dict["extractor"] = info_dict["extractor"]
self.info_dict['title'] = info_dict.get('title', info_dict['id']) self.info_dict["title"] = info_dict.get("title", info_dict["id"])
self.info_dict['uploader'] = info_dict.get('uploader', '') self.info_dict["uploader"] = info_dict.get("uploader", "")
self.info_dict['uploader_url'] = info_dict.get('uploader_url', '') self.info_dict["uploader_url"] = info_dict.get("uploader_url", "")
ydl_opts = { ydl_opts = {
'logger': MyLogger(), "logger": MyLogger(),
'progress_hooks': [self.my_hook], "progress_hooks": [self.my_hook],
# 'match_filter': self.match_filter_func, # 'match_filter': self.match_filter_func,
'outtmpl': os.path.join(self.temp_path, self.filename), "outtmpl": os.path.join(self.temp_path, self.filename),
'ignoreerrors': True, "ignoreerrors": True,
'cachedir': False "cachedir": False,
} }
ydl_opts.update(self.opts) ydl_opts.update(self.opts)
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([self.url]) ydl.download([self.url])
if self.status in (Status.START, Status.FINISHED): # 다운로드 성공 if self.status in (Status.START, Status.FINISHED): # 다운로드 성공
for i in glob(self.temp_path + '/**/*', recursive=True): for i in glob(self.temp_path + "/**/*", recursive=True):
path = i.replace(self.temp_path, self.save_path, 1) path = i.replace(self.temp_path, self.save_path, 1)
if os.path.isdir(i): if os.path.isdir(i):
if not os.path.isdir(path): if not os.path.isdir(path):
@@ -142,7 +146,7 @@ class MyYoutubeDL(object):
self.status = Status.COMPLETED self.status = Status.COMPLETED
except Exception as e: except Exception as e:
self.status = Status.ERROR self.status = Status.ERROR
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
finally: finally:
# 임시폴더 삭제 # 임시폴더 삭제
@@ -161,8 +165,10 @@ class MyYoutubeDL(object):
def get_version(): def get_version():
# from youtube_dl.version import __version__ # from youtube_dl.version import __version__
from .plugin import youtube_dl_package from .plugin import youtube_dl_package
__version__ = __import__('%s.version' % youtube_dl_package, fromlist=[
'__version__']).__version__ __version__ = __import__(
f"{youtube_dl_package}.version", fromlist=["__version__"]
).__version__
return __version__ return __version__
@@ -170,21 +176,19 @@ class MyYoutubeDL(object):
def get_info_dict(url, proxy=None, cookiefile=None): def get_info_dict(url, proxy=None, cookiefile=None):
# import youtube_dl # import youtube_dl
from .plugin import youtube_dl_package from .plugin import youtube_dl_package
youtube_dl = __import__('%s' % youtube_dl_package)
youtube_dl = __import__(youtube_dl_package)
try: try:
ydl_opts = { ydl_opts = {"extract_flat": "in_playlist", "logger": MyLogger()}
'extract_flat': 'in_playlist',
'logger': MyLogger()
}
if proxy: if proxy:
ydl_opts['proxy'] = proxy ydl_opts["proxy"] = proxy
if cookiefile: if cookiefile:
ydl_opts['cookiefile'] = cookiefile ydl_opts["cookiefile"] = cookiefile
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False) info = ydl.extract_info(url, download=False)
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
return info return info
@@ -192,22 +196,22 @@ class MyYoutubeDL(object):
def my_hook(self, d): def my_hook(self, d):
if self.status != Status.STOP: if self.status != Status.STOP:
self.status = { self.status = {
'downloading': Status.DOWNLOADING, "downloading": Status.DOWNLOADING,
'error': Status.ERROR, "error": Status.ERROR,
'finished': Status.FINISHED # 다운로드 완료. 변환 시작 "finished": Status.FINISHED, # ???? ??. ?? ??
}[d['status']] }[d["status"]]
if d['status'] != 'error': if d["status"] != "error":
self.filename = os.path.basename(d.get('filename')) self.filename = os.path.basename(d.get("filename"))
self.progress_hooks['downloaded_bytes'] = d.get('downloaded_bytes') self.progress_hooks["downloaded_bytes"] = d.get("downloaded_bytes")
self.progress_hooks['total_bytes'] = d.get('total_bytes') self.progress_hooks["total_bytes"] = d.get("total_bytes")
self.progress_hooks['eta'] = d.get('eta') self.progress_hooks["eta"] = d.get("eta")
self.progress_hooks['speed'] = d.get('speed') self.progress_hooks["speed"] = d.get("speed")
def match_filter_func(self, info_dict): def match_filter_func(self, info_dict):
self.info_dict['playlist_index'] = info_dict['playlist_index'] self.info_dict["playlist_index"] = info_dict["playlist_index"]
self.info_dict['duration'] = info_dict['duration'] self.info_dict["duration"] = info_dict["duration"]
self.info_dict['format'] = info_dict['format'] self.info_dict["format"] = info_dict["format"]
self.info_dict['thumbnail'] = info_dict['thumbnail'] self.info_dict["thumbnail"] = info_dict["thumbnail"]
return None return None
@property @property
@@ -219,13 +223,12 @@ class MyYoutubeDL(object):
from .plugin import socketio_emit from .plugin import socketio_emit
self._status = value self._status = value
socketio_emit('status', self) socketio_emit("status", self)
class MyLogger(object): class MyLogger(object):
def debug(self, msg): def debug(self, msg):
MyYoutubeDL._last_msg = msg if msg.find("\x1B") != -1 or msg.find("{") != -1:
if msg.find('') != -1 or msg.find('{') != -1:
# 과도한 로그 방지 # 과도한 로그 방지
return return
logger.debug(msg) logger.debug(msg)

499
plugin.py
View File

@@ -13,38 +13,47 @@ from .logic import Logic
from .logic_normal import LogicNormal from .logic_normal import LogicNormal
from .model import ModelSetting from .model import ModelSetting
package_name = __name__.split('.')[0] package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name) logger = get_logger(package_name)
youtube_dl_package = LogicNormal.get_youtube_dl_package( youtube_dl_package = LogicNormal.get_youtube_dl_package(
ModelSetting.get('youtube_dl_package') if ModelSetting.get('youtube_dl_package') else Logic.db_default[ ModelSetting.get("youtube_dl_package")
'youtube_dl_package'], import_pkg=True) if ModelSetting.get("youtube_dl_package")
else Logic.db_default["youtube_dl_package"],
import_pkg=True,
)
######################################################### #########################################################
# 플러그인 공용 # 플러그인 공용
######################################################### #########################################################
blueprint = Blueprint(package_name, package_name, url_prefix='/%s' % package_name, blueprint = Blueprint(
template_folder=os.path.join( package_name,
os.path.dirname(__file__), 'templates'), package_name,
static_folder=os.path.join(os.path.dirname(__file__), 'static')) url_prefix=f"/{package_name}",
template_folder=os.path.join(os.path.dirname(__file__), "templates"),
static_folder=os.path.join(os.path.dirname(__file__), "static"),
)
menu = { menu = {
'main': [package_name, 'youtube-dl'], "main": [package_name, "youtube-dl"],
'sub': [ "sub": [
['setting', '설정'], ['download', '다운로드'], ['thumbnail', ["setting", "설정"],
'썸네일 다운로드'], ['sub', '자막 다운로드'], ['list', '목록'], ["download", "다운로드"],
['log', '로그'] ["thumbnail", "썸네일 다운로드"],
["sub", "자막 다운로드"],
["list", "목록"],
["log", "로그"],
], ],
'category': 'vod' "category": "vod",
} }
plugin_info = { plugin_info = {
'version': '3.1.0', "version": "3.1.0",
'name': 'youtube-dl', "name": "youtube-dl",
'category_name': 'vod', "category_name": "vod",
'developer': 'joyfuI', "developer": "joyfuI",
'description': '유튜브, 네이버TV 등 동영상 사이트에서 동영상 다운로드', "description": "유튜브, 네이버TV 등 동영상 사이트에서 동영상 다운로드",
'home': 'https://github.com/joyfuI/youtube-dl', "home": "https://github.com/joyfuI/youtube-dl",
'more': '' "more": "",
} }
@@ -59,79 +68,91 @@ def plugin_unload():
######################################################### #########################################################
# WEB Menu # WEB Menu
######################################################### #########################################################
@blueprint.route('/') @blueprint.route("/")
def home(): def home():
return redirect('/%s/list' % package_name) return redirect(f"/{package_name}/list")
@blueprint.route('/<sub>') @blueprint.route("/<sub>")
@login_required @login_required
def first_menu(sub): def first_menu(sub):
try: try:
arg = { arg = {
'package_name': package_name, "package_name": package_name,
'template_name': '%s_%s' % (package_name, sub) "template_name": f"{package_name}_{sub}",
} }
if sub == 'setting': if sub == "setting":
arg.update(ModelSetting.to_dict()) arg.update(ModelSetting.to_dict())
arg['package_list'] = LogicNormal.get_youtube_dl_package() arg["package_list"] = LogicNormal.get_youtube_dl_package()
arg['youtube_dl_version'] = LogicNormal.get_youtube_dl_version() arg["youtube_dl_version"] = LogicNormal.get_youtube_dl_version()
arg['DEFAULT_FILENAME'] = LogicNormal.get_default_filename() arg["DEFAULT_FILENAME"] = LogicNormal.get_default_filename()
return render_template('%s_%s.html' % (package_name, sub), arg=arg) return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == 'download': elif sub == "download":
default_filename = ModelSetting.get('default_filename') default_filename = ModelSetting.get("default_filename")
arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename() arg["filename"] = (
arg['preset_list'] = LogicNormal.get_preset_list() default_filename
arg['postprocessor_list'] = LogicNormal.get_postprocessor_list() if default_filename
return render_template('%s_%s.html' % (package_name, sub), arg=arg) else LogicNormal.get_default_filename()
)
arg["preset_list"] = LogicNormal.get_preset_list()
arg["postprocessor_list"] = LogicNormal.get_postprocessor_list()
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == 'thumbnail': elif sub == "thumbnail":
default_filename = ModelSetting.get('default_filename') default_filename = ModelSetting.get("default_filename")
arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename() arg["filename"] = (
return render_template('%s_%s.html' % (package_name, sub), arg=arg) default_filename
if default_filename
else LogicNormal.get_default_filename()
)
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == 'sub': elif sub == "sub":
default_filename = ModelSetting.get('default_filename') default_filename = ModelSetting.get("default_filename")
arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename() arg["filename"] = (
return render_template('%s_%s.html' % (package_name, sub), arg=arg) default_filename
if default_filename
else LogicNormal.get_default_filename()
)
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == 'list': elif sub == "list":
return render_template('%s_%s.html' % (package_name, sub), arg=arg) return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == 'log': elif sub == "log":
return render_template('log.html', package=package_name) return render_template("log.html", package=package_name)
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return render_template('sample.html', title='%s - %s' % (package_name, sub)) return render_template("sample.html", title=f"{package_name} - {sub}")
######################################################### #########################################################
# For UI # For UI
######################################################### #########################################################
@blueprint.route('/ajax/<sub>', methods=['POST']) @blueprint.route("/ajax/<sub>", methods=["POST"])
@login_required @login_required
def ajax(sub): def ajax(sub):
logger.debug('AJAX %s %s', package_name, sub) logger.debug("AJAX %s %s", package_name, sub)
try: try:
# 공통 요청 # 공통 요청
if sub == 'setting_save': if sub == "setting_save":
ret = ModelSetting.setting_save(request) ret = ModelSetting.setting_save(request)
if request.form['ffmpeg_path'] == 'ffmpeg': if request.form["ffmpeg_path"] == "ffmpeg":
ModelSetting.set('ffmpeg_path', '') ModelSetting.set("ffmpeg_path", "")
return jsonify(ret) return jsonify(ret)
# UI 요청 # UI 요청
elif sub == 'ffmpeg_version': elif sub == "ffmpeg_version":
path = request.form['path'] path = request.form["path"]
ret = subprocess.check_output([path, '-version']) ret = subprocess.check_output([path, "-version"])
ret = ret.decode().replace('\n', '<br>') ret = ret.decode().replace("\n", "<br>")
return jsonify(ret) return jsonify(ret)
elif sub == 'download': elif sub == "download":
postprocessor = request.form['postprocessor'] postprocessor = request.form["postprocessor"]
video_convertor, extract_audio = LogicNormal.get_postprocessor() video_convertor, extract_audio = LogicNormal.get_postprocessor()
preferedformat = None preferedformat = None
preferredcodec = None preferredcodec = None
@@ -141,56 +162,56 @@ def ajax(sub):
elif postprocessor in extract_audio: elif postprocessor in extract_audio:
preferredcodec = postprocessor preferredcodec = postprocessor
preferredquality = 192 preferredquality = 192
youtube_dl = LogicNormal.download(plugin=package_name, youtube_dl = LogicNormal.download(
url=request.form['url'], plugin=package_name,
filename=request.form['filename'], url=request.form["url"],
temp_path=ModelSetting.get( filename=request.form["filename"],
'temp_path'), temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get( save_path=ModelSetting.get("save_path"),
'save_path'), format=request.form["format"],
format=request.form['format'],
preferedformat=preferedformat, preferedformat=preferedformat,
preferredcodec=preferredcodec, preferredcodec=preferredcodec,
preferredquality=preferredquality, preferredquality=preferredquality,
proxy=ModelSetting.get('proxy'), proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get('ffmpeg_path')) ffmpeg_path=ModelSetting.get("ffmpeg_path"),
)
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify([]) return jsonify([])
elif sub == 'thumbnail': elif sub == "thumbnail":
youtube_dl = LogicNormal.thumbnail(plugin=package_name, youtube_dl = LogicNormal.thumbnail(
url=request.form['url'], plugin=package_name,
filename=request.form['filename'], url=request.form["url"],
temp_path=ModelSetting.get( filename=request.form["filename"],
'temp_path'), temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get( save_path=ModelSetting.get("save_path"),
'save_path'), all_thumbnails=request.form["all_thumbnails"],
all_thumbnails=request.form['all_thumbnails'], proxy=ModelSetting.get("proxy"),
proxy=ModelSetting.get('proxy'), ffmpeg_path=ModelSetting.get("ffmpeg_path"),
ffmpeg_path=ModelSetting.get('ffmpeg_path')) )
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify([]) return jsonify([])
elif sub == 'sub': elif sub == "sub":
youtube_dl = LogicNormal.sub(plugin=package_name, youtube_dl = LogicNormal.sub(
url=request.form['url'], plugin=package_name,
filename=request.form['filename'], url=request.form["url"],
temp_path=ModelSetting.get( filename=request.form["filename"],
'temp_path'), temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get( save_path=ModelSetting.get("save_path"),
'save_path'), all_subs=request.form["all_subs"],
all_subs=request.form['all_subs'], sub_lang=request.form["sub_lang"],
sub_lang=request.form['sub_lang'], auto_sub=request.form["auto_sub"],
auto_sub=request.form['auto_sub'], proxy=ModelSetting.get("proxy"),
proxy=ModelSetting.get('proxy'), ffmpeg_path=ModelSetting.get("ffmpeg_path"),
ffmpeg_path=ModelSetting.get('ffmpeg_path')) )
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify([]) return jsonify([])
elif sub == 'list': elif sub == "list":
ret = [] ret = []
for i in LogicNormal.youtube_dl_list: for i in LogicNormal.youtube_dl_list:
data = LogicNormal.get_data(i) data = LogicNormal.get_data(i)
@@ -198,17 +219,17 @@ def ajax(sub):
ret.append(data) ret.append(data)
return jsonify(ret) return jsonify(ret)
elif sub == 'all_stop': elif sub == "all_stop":
for i in LogicNormal.youtube_dl_list: for i in LogicNormal.youtube_dl_list:
i.stop() i.stop()
return jsonify([]) return jsonify([])
elif sub == 'stop': elif sub == "stop":
index = int(request.form['index']) index = int(request.form["index"])
LogicNormal.youtube_dl_list[index].stop() LogicNormal.youtube_dl_list[index].stop()
return jsonify([]) return jsonify([])
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@@ -216,67 +237,70 @@ def ajax(sub):
# API # API
######################################################### #########################################################
# API 명세는 https://github.com/joyfuI/youtube-dl#api # API 명세는 https://github.com/joyfuI/youtube-dl#api
@blueprint.route('/api/<sub>', methods=['GET', 'POST']) @blueprint.route("/api/<sub>", methods=["GET", "POST"])
@cross_origin() @cross_origin()
@check_api @check_api
def api(sub): def api(sub):
plugin = request.values.get('plugin') plugin = request.values.get("plugin")
logger.debug('API %s %s: %s', package_name, sub, plugin) logger.debug("API %s %s: %s", package_name, sub, plugin)
if not plugin: # 요청한 플러그인명이 빈문자열이거나 None면 if not plugin: # 요청한 플러그인명이 빈문자열이거나 None면
abort(403) # 403 에러(거부) abort(403) # 403 에러(거부)
try: try:
# 동영상 정보를 반환하는 API # 동영상 정보를 반환하는 API
if sub == 'info_dict': if sub == "info_dict":
url = request.values.get('url') url = request.values.get("url")
ret = { ret = {"errorCode": 0, "info_dict": None}
'errorCode': 0,
'info_dict': None
}
if None in (url,): if None in (url,):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
info_dict = LogicNormal.get_info_dict( info_dict = LogicNormal.get_info_dict(url, ModelSetting.get("proxy"))
url, ModelSetting.get('proxy'))
if info_dict is None: if info_dict is None:
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
ret['info_dict'] = info_dict ret["info_dict"] = info_dict
return jsonify(ret) return jsonify(ret)
# 비디오 다운로드 준비를 요청하는 API # 비디오 다운로드 준비를 요청하는 API
elif sub == 'download': elif sub == "download":
key = request.values.get('key') key = request.values.get("key")
url = request.values.get('url') url = request.values.get("url")
filename = request.values.get( filename = request.values.get(
'filename', ModelSetting.get('default_filename')) "filename", ModelSetting.get("default_filename")
save_path = request.values.get( )
'save_path', ModelSetting.get('save_path')) save_path = request.values.get("save_path", ModelSetting.get("save_path"))
format_code = request.values.get('format', None) format_code = request.values.get("format", None)
preferedformat = request.values.get('preferedformat', None) preferedformat = request.values.get("preferedformat", None)
preferredcodec = request.values.get('preferredcodec', None) preferredcodec = request.values.get("preferredcodec", None)
preferredquality = request.values.get('preferredquality', 192) preferredquality = request.values.get("preferredquality", 192)
dateafter = request.values.get('dateafter', None) dateafter = request.values.get("dateafter", None)
playlist = request.values.get('playlist', None) playlist = request.values.get("playlist", None)
archive = request.values.get('archive', None) archive = request.values.get("archive", None)
start = request.values.get('start', False) start = request.values.get("start", False)
cookiefile = request.values.get('cookiefile', None) cookiefile = request.values.get("cookiefile", None)
ret = { ret = {"errorCode": 0, "index": None}
'errorCode': 0,
'index': None
}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if preferredcodec not in (None, 'best', 'mp3', 'aac', 'flac', 'm4a', 'opus', 'vorbis', 'wav'): if preferredcodec not in (
None,
"best",
"mp3",
"aac",
"flac",
"m4a",
"opus",
"vorbis",
"wav",
):
return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음 return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.download(plugin=plugin, youtube_dl = LogicNormal.download(
plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get( temp_path=ModelSetting.get("temp_path"),
'temp_path'),
save_path=save_path, save_path=save_path,
format=format_code, format=format_code,
preferedformat=preferedformat, preferedformat=preferedformat,
@@ -285,97 +309,91 @@ def api(sub):
dateafter=dateafter, dateafter=dateafter,
playlist=playlist, playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get('proxy'), proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get( ffmpeg_path=ModelSetting.get("ffmpeg_path"),
'ffmpeg_path'),
key=key, key=key,
cookiefile=cookiefile) cookiefile=cookiefile,
)
if youtube_dl is None: if youtube_dl is None:
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
ret['index'] = youtube_dl.index ret["index"] = youtube_dl.index
if start: if start:
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify(ret) return jsonify(ret)
# 썸네일 다운로드 준비를 요청하는 API # 썸네일 다운로드 준비를 요청하는 API
elif sub == 'thumbnail': elif sub == "thumbnail":
key = request.values.get('key') key = request.values.get("key")
url = request.values.get('url') url = request.values.get("url")
filename = request.values.get( filename = request.values.get(
'filename', ModelSetting.get('default_filename')) "filename", ModelSetting.get("default_filename")
save_path = request.values.get( )
'save_path', ModelSetting.get('save_path')) save_path = request.values.get("save_path", ModelSetting.get("save_path"))
all_thumbnails = request.values.get('all_thumbnails', False) all_thumbnails = request.values.get("all_thumbnails", False)
dateafter = request.values.get('dateafter', None) dateafter = request.values.get("dateafter", None)
playlist = request.values.get('playlist', None) playlist = request.values.get("playlist", None)
archive = request.values.get('archive', None) archive = request.values.get("archive", None)
start = request.values.get('start', False) start = request.values.get("start", False)
cookiefile = request.values.get('cookiefile', None) cookiefile = request.values.get("cookiefile", None)
ret = { ret = {"errorCode": 0, "index": None}
'errorCode': 0,
'index': None
}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.thumbnail(plugin=plugin, youtube_dl = LogicNormal.thumbnail(
plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get( temp_path=ModelSetting.get("temp_path"),
'temp_path'),
save_path=save_path, save_path=save_path,
all_thumbnails=all_thumbnails, all_thumbnails=all_thumbnails,
dateafter=dateafter, dateafter=dateafter,
playlist=playlist, playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get('proxy'), proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get( ffmpeg_path=ModelSetting.get("ffmpeg_path"),
'ffmpeg_path'),
key=key, key=key,
cookiefile=cookiefile) cookiefile=cookiefile,
)
if youtube_dl is None: if youtube_dl is None:
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
ret['index'] = youtube_dl.index ret["index"] = youtube_dl.index
if start: if start:
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify(ret) return jsonify(ret)
# 자막 다운로드 준비를 요청하는 API # 자막 다운로드 준비를 요청하는 API
elif sub == 'sub': elif sub == "sub":
key = request.values.get('key') key = request.values.get("key")
url = request.values.get('url') url = request.values.get("url")
filename = request.values.get( filename = request.values.get(
'filename', ModelSetting.get('default_filename')) "filename", ModelSetting.get("default_filename")
save_path = request.values.get( )
'save_path', ModelSetting.get('save_path')) save_path = request.values.get("save_path", ModelSetting.get("save_path"))
all_subs = request.values.get('all_subs', False) all_subs = request.values.get("all_subs", False)
sub_lang = request.values.get('sub_lang', 'ko') sub_lang = request.values.get("sub_lang", "ko")
auto_sub = request.values.get('all_subs', False) auto_sub = request.values.get("all_subs", False)
dateafter = request.values.get('dateafter', None) dateafter = request.values.get("dateafter", None)
playlist = request.values.get('playlist', None) playlist = request.values.get("playlist", None)
archive = request.values.get('archive', None) archive = request.values.get("archive", None)
start = request.values.get('start', False) start = request.values.get("start", False)
cookiefile = request.values.get('cookiefile', None) cookiefile = request.values.get("cookiefile", None)
ret = { ret = {"errorCode": 0, "index": None}
'errorCode': 0,
'index': None
}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.sub(plugin=plugin, youtube_dl = LogicNormal.sub(
plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get( temp_path=ModelSetting.get("temp_path"),
'temp_path'),
save_path=save_path, save_path=save_path,
all_subs=all_subs, all_subs=all_subs,
sub_lang=sub_lang, sub_lang=sub_lang,
@@ -383,93 +401,93 @@ def api(sub):
dateafter=dateafter, dateafter=dateafter,
playlist=playlist, playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get('proxy'), proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get( ffmpeg_path=ModelSetting.get("ffmpeg_path"),
'ffmpeg_path'),
key=key, key=key,
cookiefile=cookiefile) cookiefile=cookiefile,
)
if youtube_dl is None: if youtube_dl is None:
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
ret['index'] = youtube_dl.index ret["index"] = youtube_dl.index
if start: if start:
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit("add", youtube_dl)
return jsonify(ret) return jsonify(ret)
# 다운로드 시작을 요청하는 API # 다운로드 시작을 요청하는 API
elif sub == 'start': elif sub == "start":
index = request.values.get('index') index = request.values.get("index")
key = request.values.get('key') key = request.values.get("key")
ret = { ret = {"errorCode": 0, "status": None}
'errorCode': 0,
'status': None
}
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)): if not 0 <= index < len(LogicNormal.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
ret['status'] = youtube_dl.status.name ret["status"] = youtube_dl.status.name
if not youtube_dl.start(): if not youtube_dl.start():
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
return jsonify(ret) return jsonify(ret)
# 다운로드 중지를 요청하는 API # 다운로드 중지를 요청하는 API
elif sub == 'stop': elif sub == "stop":
index = request.values.get('index') index = request.values.get("index")
key = request.values.get('key') key = request.values.get("key")
ret = { ret = {"errorCode": 0, "status": None}
'errorCode': 0,
'status': None
}
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)): if not 0 <= index < len(LogicNormal.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
ret['status'] = youtube_dl.status.name ret["status"] = youtube_dl.status.name
if not youtube_dl.stop(): if not youtube_dl.stop():
return LogicNormal.abort(ret, 10) # 실패 return LogicNormal.abort(ret, 10) # 실패
return jsonify(ret) return jsonify(ret)
# 현재 상태를 반환하는 API # 현재 상태를 반환하는 API
elif sub == 'status': elif sub == "status":
index = request.values.get('index') index = request.values.get("index")
key = request.values.get('key') key = request.values.get("key")
ret = { ret = {
'errorCode': 0, "errorCode": 0,
'status': None, "status": None,
'type': None, "type": None,
'start_time': None, "start_time": None,
'end_time': None, "end_time": None,
'temp_path': None, "temp_path": None,
'save_path': None "save_path": None,
} }
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)): if not 0 <= index < len(LogicNormal.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
ret['status'] = youtube_dl.status.name ret["status"] = youtube_dl.status.name
ret['type'] = youtube_dl.type ret["type"] = youtube_dl.type
ret['start_time'] = youtube_dl.start_time.strftime('%Y-%m-%dT%H:%M:%S') if \ ret["start_time"] = (
youtube_dl.start_time is not None else None youtube_dl.start_time.strftime("%Y-%m-%dT%H:%M:%S")
ret['end_time'] = youtube_dl.end_time.strftime('%Y-%m-%dT%H:%M:%S') if \ if youtube_dl.start_time is not None
youtube_dl.end_time is not None else None else None
ret['temp_path'] = youtube_dl.temp_path )
ret['save_path'] = youtube_dl.save_path ret["end_time"] = (
youtube_dl.end_time.strftime("%Y-%m-%dT%H:%M:%S")
if youtube_dl.end_time is not None
else None
)
ret["temp_path"] = youtube_dl.temp_path
ret["save_path"] = youtube_dl.save_path
return jsonify(ret) return jsonify(ret)
except Exception as e: except Exception as e:
logger.error('Exception:%s', e) logger.error("Exception:%s", e)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
abort(500) # 500 에러(서버 오류) abort(500) # 500 에러(서버 오류)
abort(404) # 404 에러(페이지 없음) abort(404) # 404 에러(페이지 없음)
@@ -479,5 +497,6 @@ def api(sub):
# socketio # socketio
######################################################### #########################################################
def socketio_emit(cmd, data): def socketio_emit(cmd, data):
socketio.emit(cmd, LogicNormal.get_data(data), namespace='/%s' % socketio.emit(
package_name, broadcast=True) cmd, LogicNormal.get_data(data), namespace=f"/{package_name}", broadcast=True
)

View File

@@ -1,4 +1,4 @@
"use strict"; 'use strict';
const url = document.getElementById('url'); const url = document.getElementById('url');
const preset = document.getElementById('preset'); const preset = document.getElementById('preset');
@@ -37,12 +37,15 @@ download_btn.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}, },
body: get_formdata('#download') body: get_formdata('#download'),
}).then(response => response.json()).then(() => { })
.then((response) => response.json())
.then(() => {
notify('분석중..', 'info'); notify('분석중..', 'info');
}).catch(() => { })
.catch(() => {
notify('다운로드 요청 실패', 'danger'); notify('다운로드 요청 실패', 'danger');
}); });
}); });

View File

@@ -1,4 +1,4 @@
"use strict"; 'use strict';
const all_stop_btn = document.getElementById('all_stop_btn'); const all_stop_btn = document.getElementById('all_stop_btn');
const list_tbody = document.getElementById('list_tbody'); const list_tbody = document.getElementById('list_tbody');
@@ -17,9 +17,11 @@ fetch(`/${package_name}/ajax/list`, {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
} },
}).then(response => response.json()).then((data) => { })
.then((response) => response.json())
.then((data) => {
let str = ''; let str = '';
for (const item of data) { for (const item of data) {
str += make_item(item); str += make_item(item);
@@ -34,9 +36,11 @@ all_stop_btn.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
} },
}).then(response => response.json()).then(() => { })
.then((response) => response.json())
.then(() => {
location.reload(); location.reload();
}); });
}); });
@@ -52,12 +56,14 @@ list_tbody.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}, },
body: new URLSearchParams({ body: new URLSearchParams({
index: target.dataset.index index: target.dataset.index,
}),
}) })
}).then(response => response.json()).then(() => { .then((response) => response.json())
.then(() => {
location.reload(); location.reload();
}); });
}); });
@@ -90,7 +96,11 @@ function get_item(data) {
str += `<td><div class="progress"><div class="progress-bar" style="visibility: ${visi}; width: ${data.percent}%">${data.percent}%</div></div></td>`; str += `<td><div class="progress"><div class="progress-bar" style="visibility: ${visi}; width: ${data.percent}%">${data.percent}%</div></div></td>`;
str += `<td>${data.download_time}</td>`; str += `<td>${data.download_time}</td>`;
str += '<td class="tableRowHoverOff">'; str += '<td class="tableRowHoverOff">';
if (data.status_str === 'START' || data.status_str === 'DOWNLOADING' || data.status_str === 'FINISHED') { if (
data.status_str === 'START' ||
data.status_str === 'DOWNLOADING' ||
data.status_str === 'FINISHED'
) {
str += `<button class="align-middle btn btn-outline-danger btn-sm youtubeDl-stop" data-index="${data.index}">중지</button>`; str += `<button class="align-middle btn btn-outline-danger btn-sm youtubeDl-stop" data-index="${data.index}">중지</button>`;
} }
str += '</td>'; str += '</td>';
@@ -106,7 +116,10 @@ function get_detail(data) {
if (data.status_str === 'DOWNLOADING') { if (data.status_str === 'DOWNLOADING') {
str += info_html('', '<b>현재 다운로드 중인 파일에 대한 정보</b>'); str += info_html('', '<b>현재 다운로드 중인 파일에 대한 정보</b>');
str += info_html('파일명', data.filename); str += info_html('파일명', data.filename);
str += info_html('진행률(current/total)', `${data.percent}% (${data.downloaded_bytes_str} / ${data.total_bytes_str})`); str += info_html(
'진행률(current/total)',
`${data.percent}% (${data.downloaded_bytes_str} / ${data.total_bytes_str})`
);
str += info_html('남은 시간', `${data.eta}`); str += info_html('남은 시간', `${data.eta}`);
str += info_html('다운 속도', data.speed_str); str += info_html('다운 속도', data.speed_str);
} }
@@ -115,7 +128,7 @@ function get_detail(data) {
function info_html(left, right, option) { function info_html(left, right, option) {
let str = '<div class="row">'; let str = '<div class="row">';
const link = (left === 'URL' || left === '업로더'); const link = left === 'URL' || left === '업로더';
str += '<div class="col-sm-2">'; str += '<div class="col-sm-2">';
str += `<b>${left}</b>`; str += `<b>${left}</b>`;
str += '</div>'; str += '</div>';

View File

@@ -1,4 +1,4 @@
"use strict"; 'use strict';
const ffmpeg_path = document.getElementById('ffmpeg_path'); const ffmpeg_path = document.getElementById('ffmpeg_path');
const ffmpeg_version_btn = document.getElementById('ffmpeg_version_btn'); const ffmpeg_version_btn = document.getElementById('ffmpeg_version_btn');
@@ -22,16 +22,19 @@ ffmpeg_version_btn.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}, },
body: new URLSearchParams({ body: new URLSearchParams({
path: ffmpeg path: ffmpeg,
}),
}) })
}).then(response => response.json()).then((data) => { .then((response) => response.json())
.then((data) => {
modal_title.innerHTML = `${ffmpeg} -version`; modal_title.innerHTML = `${ffmpeg} -version`;
modal_body.innerHTML = data; modal_body.innerHTML = data;
$('#large_modal').modal(); $('#large_modal').modal();
}).catch(() => { })
.catch(() => {
notify('버전확인 실패', 'danger'); notify('버전확인 실패', 'danger');
}); });
}); });
@@ -47,15 +50,25 @@ ffmpeg_path_btn.addEventListener('click', (event) => {
// 임시 폴더 경로 선택 // 임시 폴더 경로 선택
temp_path_btn.addEventListener('click', (event) => { temp_path_btn.addEventListener('click', (event) => {
event.preventDefault(); event.preventDefault();
m_select_local_file_modal("저장 경로 선택", temp_path.value, true, (result) => { m_select_local_file_modal(
'저장 경로 선택',
temp_path.value,
true,
(result) => {
temp_path.value = result; temp_path.value = result;
}); }
);
}); });
// 저장 폴더 경로 선택 // 저장 폴더 경로 선택
save_path_btn.addEventListener('click', (event) => { save_path_btn.addEventListener('click', (event) => {
event.preventDefault(); event.preventDefault();
m_select_local_file_modal("저장 경로 선택", save_path.value, true, (result) => { m_select_local_file_modal(
'저장 경로 선택',
save_path.value,
true,
(result) => {
save_path.value = result; save_path.value = result;
}); }
);
}); });

View File

@@ -1,4 +1,4 @@
"use strict"; 'use strict';
const url = document.getElementById('url'); const url = document.getElementById('url');
const download_btn = document.getElementById('download_btn'); const download_btn = document.getElementById('download_btn');
@@ -20,12 +20,15 @@ download_btn.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}, },
body: get_formdata('#download') body: get_formdata('#download'),
}).then(response => response.json()).then(() => { })
.then((response) => response.json())
.then(() => {
notify('분석중..', 'info'); notify('분석중..', 'info');
}).catch(() => { })
.catch(() => {
notify('다운로드 요청 실패', 'danger'); notify('다운로드 요청 실패', 'danger');
}); });
}); });

View File

@@ -1,4 +1,4 @@
"use strict"; 'use strict';
const url = document.getElementById('url'); const url = document.getElementById('url');
const download_btn = document.getElementById('download_btn'); const download_btn = document.getElementById('download_btn');
@@ -15,12 +15,15 @@ download_btn.addEventListener('click', (event) => {
method: 'POST', method: 'POST',
cache: 'no-cache', cache: 'no-cache',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}, },
body: get_formdata('#download') body: get_formdata('#download'),
}).then(response => response.json()).then(() => { })
.then((response) => response.json())
.then(() => {
notify('분석중..', 'info'); notify('분석중..', 'info');
}).catch(() => { })
.catch(() => {
notify('다운로드 요청 실패', 'danger'); notify('다운로드 요청 실패', 'danger');
}); });
}); });

View File

@@ -1,11 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %} {% block content %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('.static', filename='%s.css' % arg['template_name']) }}"> <link
rel="stylesheet"
href="{{ url_for('.static', filename='%s.css' % arg['template_name']) }}"
/>
{{ macros.m_row_start() }} {{ macros.m_row_start() }} {{ macros.m_button('all_stop_btn', '전체 중지') }} {{
{{ macros.m_button('all_stop_btn', '전체 중지') }} macros.m_row_end() }}
{{ macros.m_row_end() }}
<div class="d-inline-block"></div> <div class="d-inline-block"></div>
<table class="table table-sm tableRowHover"> <table class="table table-sm tableRowHover">
@@ -26,7 +27,7 @@
</table> </table>
<script> <script>
"use strict"; 'use strict';
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
</script> </script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script> <script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>

View File

@@ -1,19 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %} {% block content %}
{% block content %}
<form id="setting"> <form id="setting">
{{ macros.setting_radio('youtube_dl_package', 'youtube-dl', arg['package_list'], value=arg['youtube_dl_package'], desc='사용할 youtube-dl 패키지를 선택합니다. 설정 저장 후 재시작이 필요합니다.') }} {{ macros.setting_radio('youtube_dl_package', 'youtube-dl',
{{ macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전', value=arg['youtube_dl_version'], disabled=True) }} arg['package_list'], value=arg['youtube_dl_package'], desc='사용할 youtube-dl
{{ macros.setting_input_text_and_buttons('ffmpeg_path', 'FFmpeg 경로', [['ffmpeg_version_btn', '버전확인'], ['ffmpeg_path_btn', '파일 선택']], value=arg['ffmpeg_path'], placeholder='ffmpeg', desc='SJVA에 내장된 버전 말고 원하는 버전을 사용할 수 있습니다.') }} 패키지를 선택합니다. 설정 저장 후 재시작이 필요합니다.') }} {{
{{ macros.setting_input_text_and_buttons('temp_path', '임시 폴더', [['temp_path_btn', '경로 선택']], value=arg['temp_path'], desc='다운로드 파일이 임시로 저장될 폴더입니다.') }} macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전',
{{ macros.setting_input_text_and_buttons('save_path', '저장 폴더', [['save_path_btn', '경로 선택']], value=arg['save_path'], desc='정상적으로 완료된 파일이 이동할 폴더입니다.') }} value=arg['youtube_dl_version'], disabled=True) }} {{
{{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder=arg['DEFAULT_FILENAME'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }} macros.setting_input_text_and_buttons('ffmpeg_path', 'FFmpeg 경로',
{{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }} [['ffmpeg_version_btn', '버전확인'], ['ffmpeg_path_btn', '파일 선택']],
{{ macros.setting_button([['global_setting_save_btn', '저장']]) }} value=arg['ffmpeg_path'], placeholder='ffmpeg', desc='SJVA에 내장된 버전 말고
원하는 버전을 사용할 수 있습니다.') }} {{
macros.setting_input_text_and_buttons('temp_path', '임시 폴더',
[['temp_path_btn', '경로 선택']], value=arg['temp_path'], desc='다운로드
파일이 임시로 저장될 폴더입니다.') }} {{
macros.setting_input_text_and_buttons('save_path', '저장 폴더',
[['save_path_btn', '경로 선택']], value=arg['save_path'], desc='정상적으로
완료된 파일이 이동할 폴더입니다.') }} {{
macros.setting_input_text('default_filename', '기본 파일명',
value=arg['default_filename'], placeholder=arg['DEFAULT_FILENAME'],
desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template
참고') }} {{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'],
desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로
두면 프록시를 사용하지 않습니다.']) }} {{
macros.setting_button([['global_setting_save_btn', '저장']]) }}
</form> </form>
<script> <script>
"use strict"; 'use strict';
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
</script> </script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script> <script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>

View File

@@ -1,19 +1,23 @@
{% extends "base.html" %} {% extends "base.html" %} {% block content %}
{% block content %}
<form id="download"> <form id="download">
{{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소', desc='유튜브, 네이버TV 등 동영상 주소') }} {{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소',
{{ macros.setting_input_text('filename', '파일명', value=arg['filename'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }} desc='유튜브, 네이버TV 등 동영상 주소') }} {{
{{ macros.setting_checkbox('all_subs', '모든 자막 다운로드', value='False') }} macros.setting_input_text('filename', '파일명', value=arg['filename'],
desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template
참고') }} {{ macros.setting_checkbox('all_subs', '모든 자막 다운로드',
value='False') }}
<div id="all_subs_div" class="collapse show"> <div id="all_subs_div" class="collapse show">
{{ macros.setting_input_text('sub_lang', '자막 언어', value='ko', desc=['두 자리 국가 코드', '콤마(,)를 구분자로 여러 개 지정 가능']) }} {{ macros.setting_input_text('sub_lang', '자막 언어', value='ko', desc=['두
자리 국가 코드', '콤마(,)를 구분자로 여러 개 지정 가능']) }}
</div> </div>
{{ macros.setting_checkbox('auto_sub', '자동생성 자막 다운로드', value='False', desc='유튜브 전용') }} {{ macros.setting_checkbox('auto_sub', '자동생성 자막 다운로드',
{{ macros.setting_button([['download_btn', '다운로드']]) }} value='False', desc='유튜브 전용') }} {{
macros.setting_button([['download_btn', '다운로드']]) }}
</form> </form>
<script> <script>
"use strict"; 'use strict';
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
</script> </script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script> <script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>

View File

@@ -1,15 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %} {% block content %}
{% block content %}
<form id="download"> <form id="download">
{{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소', desc='유튜브, 네이버TV 등 동영상 주소') }} {{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소',
{{ macros.setting_input_text('filename', '파일명', value=arg['filename'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }} desc='유튜브, 네이버TV 등 동영상 주소') }} {{
{{ macros.setting_checkbox('all_thumbnails', '모든 썸네일 다운로드', value='False') }} macros.setting_input_text('filename', '파일명', value=arg['filename'],
{{ macros.setting_button([['download_btn', '다운로드']]) }} desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template
참고') }} {{ macros.setting_checkbox('all_thumbnails', '모든 썸네일 다운로드',
value='False') }} {{ macros.setting_button([['download_btn', '다운로드']]) }}
</form> </form>
<script> <script>
"use strict"; 'use strict';
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
</script> </script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script> <script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>