최신 플러그인 구조로 변경

This commit is contained in:
joyfuI
2022-07-10 23:22:28 +09:00
parent 44b792bc28
commit 3ef17ad0b3
9 changed files with 399 additions and 568 deletions

View File

@@ -245,6 +245,10 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로
## Changelog ## Changelog
v4.0.0
- 최신 플러그인 구조로 변경
v3.1.1 v3.1.1
- 디폴트 youtube_dl 패키지를 yt-dlp로 변경 - 디폴트 youtube_dl 패키지를 yt-dlp로 변경

View File

@@ -1 +1,7 @@
from .plugin import blueprint, menu, plugin_info, plugin_load, plugin_unload from .plugin import Plugin
blueprint = Plugin.blueprint
menu = Plugin.menu
plugin_load = Plugin.logic.plugin_load
plugin_unload = Plugin.logic.plugin_unload
plugin_info = Plugin.plugin_info

8
abort.py Normal file
View File

@@ -0,0 +1,8 @@
from flask import jsonify
class LogicAbort:
@staticmethod
def abort(base, code):
base["errorCode"] = code
return jsonify(base)

View File

@@ -1,10 +1,9 @@
{ {
"version": "3.1.1", "version": "4.0.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": ""
"category": "vod"
} }

103
logic.py
View File

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

View File

@@ -1,28 +1,230 @@
import os
import sys
import platform
import traceback import traceback
import subprocess
import sqlite3
from datetime import datetime from datetime import datetime
from flask import jsonify from flask import render_template, jsonify
from framework.logger import get_logger from framework import db, path_app_root, path_data, socketio
from framework.common.plugin import LogicModuleBase, default_route_socketio
from .plugin import Plugin
from .my_youtube_dl import MyYoutubeDL, Status from .my_youtube_dl import MyYoutubeDL, Status
package_name = __name__.split(".", maxsplit=1)[0] logger = Plugin.logger
logger = get_logger(package_name) package_name = Plugin.package_name
ModelSetting = Plugin.ModelSetting
class LogicNormal(object): class LogicMain(LogicModuleBase):
db_default = {
"db_version": "2",
"youtube_dl_package": "1",
"ffmpeg_path": ""
if platform.system() != "Windows"
else os.path.join(path_app_root, "bin", "Windows", "ffmpeg.exe"),
"temp_path": os.path.join(path_data, "download_tmp"),
"save_path": os.path.join(path_data, "download"),
"default_filename": "",
"proxy": "",
}
def __init__(self, plugin):
super(LogicMain, self).__init__(plugin, None)
self.name = package_name # 모듈명
default_route_socketio(plugin, self)
def plugin_load(self):
try:
# youtube-dl 업데이트
youtube_dl = Plugin.youtube_dl_packages[
int(ModelSetting.get("youtube_dl_package"))
]
logger.debug(f"{youtube_dl} upgrade")
logger.debug(
subprocess.check_output(
[sys.executable, "-m", "pip", "install", "--upgrade", youtube_dl],
universal_newlines=True,
)
)
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
def process_menu(self, sub, req):
try:
arg = {
"package_name": package_name,
"sub": sub,
"template_name": f"{package_name}_{sub}",
"package_version": Plugin.plugin_info["version"],
}
if sub == "setting":
arg.update(ModelSetting.to_dict())
arg["package_list"] = Plugin.youtube_dl_packages
arg["youtube_dl_version"] = LogicMain.get_youtube_dl_version()
arg["DEFAULT_FILENAME"] = LogicMain.get_default_filename()
elif sub == "download":
default_filename = ModelSetting.get("default_filename")
arg["filename"] = (
default_filename
if default_filename
else LogicMain.get_default_filename()
)
arg["preset_list"] = LogicMain.get_preset_list()
arg["postprocessor_list"] = LogicMain.get_postprocessor_list()
elif sub == "thumbnail":
default_filename = ModelSetting.get("default_filename")
arg["filename"] = (
default_filename
if default_filename
else LogicMain.get_default_filename()
)
elif sub == "sub":
default_filename = ModelSetting.get("default_filename")
arg["filename"] = (
default_filename
if default_filename
else LogicMain.get_default_filename()
)
elif sub == "list":
pass
return render_template(f"{package_name}_{sub}.html", arg=arg)
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
return render_template("sample.html", title=f"{package_name} - {sub}")
def process_ajax(self, sub, req):
try:
logger.debug("AJAX: %s, %s", sub, req.values)
ret = {"ret": "success"}
if sub == "ffmpeg_version":
path = req.form["path"]
output = subprocess.check_output([path, "-version"])
output = output.decode().replace("\n", "<br>")
ret["data"] = output
elif sub == "download":
postprocessor = req.form["postprocessor"]
video_convertor, extract_audio = LogicMain.get_postprocessor()
preferedformat = None
preferredcodec = None
preferredquality = None
if postprocessor in video_convertor:
preferedformat = postprocessor
elif postprocessor in extract_audio:
preferredcodec = postprocessor
preferredquality = 192
youtube_dl = LogicMain.download(
plugin=package_name,
url=req.form["url"],
filename=req.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
format=req.form["format"],
preferedformat=preferedformat,
preferredcodec=preferredcodec,
preferredquality=preferredquality,
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path")
if req.form["ffmpeg_path"] != "ffmpeg"
else None,
)
youtube_dl.start()
LogicMain.socketio_emit("add", youtube_dl)
elif sub == "thumbnail":
youtube_dl = LogicMain.thumbnail(
plugin=package_name,
url=req.form["url"],
filename=req.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
all_thumbnails=req.form["all_thumbnails"],
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path")
if req.form["ffmpeg_path"] != "ffmpeg"
else None,
)
youtube_dl.start()
LogicMain.socketio_emit("add", youtube_dl)
elif sub == "sub":
youtube_dl = LogicMain.sub(
plugin=package_name,
url=req.form["url"],
filename=req.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
all_subs=req.form["all_subs"],
sub_lang=req.form["sub_lang"],
auto_sub=req.form["auto_sub"],
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path")
if req.form["ffmpeg_path"] != "ffmpeg"
else None,
)
youtube_dl.start()
LogicMain.socketio_emit("add", youtube_dl)
elif sub == "list":
ret["data"] = []
for i in LogicMain.youtube_dl_list:
data = LogicMain.get_data(i)
if data is not None:
ret["data"].append(data)
elif sub == "all_stop":
for i in LogicMain.youtube_dl_list:
i.stop()
elif sub == "stop":
index = int(req.form["index"])
LogicMain.youtube_dl_list[index].stop()
return jsonify(ret)
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
return jsonify({"ret": "danger", "msg": str(error)})
def migration(self):
try:
db_version = ModelSetting.get_int("db_version")
connect = sqlite3.connect(
os.path.join(path_data, "db", f"{package_name}.db")
)
if db_version < 2:
logger.debug("youtube-dlc uninstall")
logger.debug(
subprocess.check_output(
[sys.executable, "-m", "pip", "uninstall", "-y", "youtube-dlc"],
universal_newlines=True,
)
)
connect.commit()
connect.close()
ModelSetting.set("db_version", LogicMain.db_default["db_version"])
db.session.flush()
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
youtube_dl_list = [] youtube_dl_list = []
@staticmethod
def get_youtube_dl_package(index=None, import_pkg=False):
packages = ["youtube-dl", "yt-dlp"]
import_name = ["youtube_dl", "yt_dlp"]
if import_pkg:
return import_name if index is None else import_name[int(index)]
else:
return packages if index is None else packages[int(index)]
@staticmethod @staticmethod
def get_youtube_dl_version(): def get_youtube_dl_version():
try: try:
@@ -79,7 +281,7 @@ class LogicNormal(object):
def get_postprocessor(): def get_postprocessor():
video_convertor = [] video_convertor = []
extract_audio = [] extract_audio = []
for i in LogicNormal.get_postprocessor_list(): for i in LogicMain.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] == "오디오 추출":
@@ -136,7 +338,7 @@ class LogicNormal(object):
plugin, "video", url, filename, temp_path, save_path, opts, dateafter plugin, "video", url, filename, temp_path, save_path, opts, dateafter
) )
youtube_dl.key = kwagrs.get("key") youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicMain.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as error: except Exception as error:
logger.error("Exception:%s", error) logger.error("Exception:%s", error)
@@ -187,7 +389,7 @@ class LogicNormal(object):
dateafter, dateafter,
) )
youtube_dl.key = kwagrs.get("key") youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicMain.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as error: except Exception as error:
logger.error("Exception:%s", error) logger.error("Exception:%s", error)
@@ -235,7 +437,7 @@ class LogicNormal(object):
plugin, "subtitle", url, filename, temp_path, save_path, opts, dateafter plugin, "subtitle", url, filename, temp_path, save_path, opts, dateafter
) )
youtube_dl.key = kwagrs.get("key") youtube_dl.key = kwagrs.get("key")
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicMain.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
except Exception as error: except Exception as error:
logger.error("Exception:%s", error) logger.error("Exception:%s", error)
@@ -284,9 +486,7 @@ class LogicNormal(object):
else "" else ""
) )
data["speed_str"] = ( data["speed_str"] = (
LogicNormal.human_readable_size( LogicMain.human_readable_size(youtube_dl.progress_hooks["speed"], "/s")
youtube_dl.progress_hooks["speed"], "/s"
)
if youtube_dl.progress_hooks["speed"] is not None if youtube_dl.progress_hooks["speed"] is not None
else "" else ""
) )
@@ -303,10 +503,10 @@ class LogicNormal(object):
youtube_dl.progress_hooks["downloaded_bytes"], youtube_dl.progress_hooks["downloaded_bytes"],
youtube_dl.progress_hooks["total_bytes"], youtube_dl.progress_hooks["total_bytes"],
): # 둘 다 값이 있으면 ): # 둘 다 값이 있으면
data["downloaded_bytes_str"] = LogicNormal.human_readable_size( data["downloaded_bytes_str"] = LogicMain.human_readable_size(
youtube_dl.progress_hooks["downloaded_bytes"] youtube_dl.progress_hooks["downloaded_bytes"]
) )
data["total_bytes_str"] = LogicNormal.human_readable_size( data["total_bytes_str"] = LogicMain.human_readable_size(
youtube_dl.progress_hooks["total_bytes"] youtube_dl.progress_hooks["total_bytes"]
) )
data[ data[
@@ -335,6 +535,7 @@ class LogicNormal(object):
return f"{size:.1f} YB{suffix}" return f"{size:.1f} YB{suffix}"
@staticmethod @staticmethod
def abort(base, code): def socketio_emit(cmd, data):
base["errorCode"] = code socketio.emit(
return jsonify(base) cmd, LogicMain.get_data(data), namespace=f"/{package_name}", broadcast=True
)

119
model.py
View File

@@ -1,119 +0,0 @@
import os
import traceback
from framework import app, db, path_data
from framework.logger import get_logger
from framework.util import Util
package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name)
app.config["SQLALCHEMY_BINDS"][package_name] = "sqlite:///%s" % (
os.path.join(path_data, "db", f"{package_name}.db")
)
class ModelSetting(db.Model):
__tablename__ = f"{package_name}_setting"
__table_args__ = {"mysql_collate": "utf8_general_ci"}
__bind_key__ = package_name
id = db.Column(db.Integer, primary_key=True)
key = db.Column(db.String(100), unique=True, nullable=False)
value = db.Column(db.String, nullable=False)
def __init__(self, key, value):
self.key = key
self.value = value
def __repr__(self):
return repr(self.as_dict())
def as_dict(self):
return {x.name: getattr(self, x.name) for x in self.__table__.columns}
@staticmethod
def get(key):
try:
return (
db.session.query(ModelSetting).filter_by(key=key).first().value.strip()
)
except Exception as error:
logger.error("Exception:%s %s", error, key)
logger.error(traceback.format_exc())
@staticmethod
def get_int(key):
try:
return int(ModelSetting.get(key))
except Exception as error:
logger.error("Exception:%s %s", error, key)
logger.error(traceback.format_exc())
@staticmethod
def get_bool(key):
try:
return ModelSetting.get(key) == "True"
except Exception as error:
logger.error("Exception:%s %s", error, key)
logger.error(traceback.format_exc())
@staticmethod
def set(key, value):
try:
item = (
db.session.query(ModelSetting)
.filter_by(key=key)
.with_for_update()
.first()
)
if item is not None:
item.value = value.strip()
db.session.commit()
else:
db.session.add(ModelSetting(key, value.strip()))
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
logger.error("Error Key:%s Value:%s", key, value)
@staticmethod
def to_dict():
try:
return Util.db_list_to_dict(db.session.query(ModelSetting).all())
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
@staticmethod
def setting_save(req):
try:
for key, value in req.form.items():
if key in ["scheduler", "is_running"]:
continue
if key.startswith("tmp_"):
continue
logger.debug("Key:%s Value:%s", key, value)
entity = (
db.session.query(ModelSetting)
.filter_by(key=key)
.with_for_update()
.first()
)
entity.value = value
db.session.commit()
return True
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
return False
@staticmethod
def get_list(key):
try:
value = ModelSetting.get(key)
values = [x.strip().strip() for x in value.replace("\n", "|").split("|")]
values = Util.get_list_except_empty(values)
return values
except Exception as error:
logger.error("Exception:%s %s", error, key)
logger.error(traceback.format_exc())

View File

@@ -8,11 +8,18 @@ from datetime import datetime
from threading import Thread from threading import Thread
from enum import Enum from enum import Enum
from framework.logger import get_logger
import framework.common.celery as celery_shutil import framework.common.celery as celery_shutil
package_name = __name__.split(".", maxsplit=1)[0] from .plugin import Plugin
logger = get_logger(package_name)
logger = Plugin.logger
ModelSetting = Plugin.ModelSetting
youtube_dl_package = Plugin.youtube_dl_packages[
int(ModelSetting.get("youtube_dl_package"))
if ModelSetting.get("youtube_dl_package")
else 1 # LogicMain.db_default["youtube_dl_package"]
].replace("-", "_")
class Status(Enum): class Status(Enum):
@@ -29,7 +36,7 @@ class Status(Enum):
return str_list[self.value] return str_list[self.value]
class MyYoutubeDL(object): class MyYoutubeDL:
DEFAULT_FILENAME = "%(title)s-%(id)s.%(ext)s" DEFAULT_FILENAME = "%(title)s-%(id)s.%(ext)s"
_index = 0 _index = 0
@@ -47,8 +54,6 @@ class MyYoutubeDL(object):
datebefore=None, datebefore=None,
): ):
# from youtube_dl.utils import DateRange # from youtube_dl.utils import DateRange
from .plugin import youtube_dl_package
DateRange = __import__( DateRange = __import__(
f"{youtube_dl_package}.utils", fromlist=["DateRange"] f"{youtube_dl_package}.utils", fromlist=["DateRange"]
).DateRange ).DateRange
@@ -106,8 +111,6 @@ class MyYoutubeDL(object):
def run(self): def run(self):
# import youtube_dl # import youtube_dl
from .plugin import youtube_dl_package
youtube_dl = __import__(youtube_dl_package) youtube_dl = __import__(youtube_dl_package)
try: try:
@@ -164,8 +167,6 @@ class MyYoutubeDL(object):
@staticmethod @staticmethod
def get_version(): def get_version():
# from youtube_dl.version import __version__ # from youtube_dl.version import __version__
from .plugin import youtube_dl_package
__version__ = __import__( __version__ = __import__(
f"{youtube_dl_package}.version", fromlist=["__version__"] f"{youtube_dl_package}.version", fromlist=["__version__"]
).__version__ ).__version__
@@ -175,8 +176,6 @@ class MyYoutubeDL(object):
@staticmethod @staticmethod
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
youtube_dl = __import__(youtube_dl_package) youtube_dl = __import__(youtube_dl_package)
try: try:
@@ -220,13 +219,13 @@ class MyYoutubeDL(object):
@status.setter @status.setter
def status(self, value): def status(self, value):
from .plugin import socketio_emit from .main import LogicMain
self._status = value self._status = value
socketio_emit("status", self) LogicMain.socketio_emit("status", self)
class MyLogger(object): class MyLogger:
def debug(self, msg): def debug(self, msg):
if msg.find(" ETA ") != -1: if msg.find(" ETA ") != -1:
# 과도한 로그 방지 # 과도한 로그 방지

438
plugin.py
View File

@@ -1,274 +1,117 @@
import os import os
import traceback import traceback
import subprocess
from flask import Blueprint, request, render_template, redirect, jsonify, abort from flask import Blueprint, request, jsonify, abort
from flask_login import login_required
from flask_cors import cross_origin
from framework import check_api, socketio from framework import app, path_data
from framework.logger import get_logger from framework.logger import get_logger
from framework.util import Util
from .logic import Logic from framework.common.plugin import (
from .logic_normal import LogicNormal get_model_setting,
from .model import ModelSetting Logic,
default_route_single_module,
package_name = __name__.split(".", maxsplit=1)[0]
logger = get_logger(package_name)
youtube_dl_package = LogicNormal.get_youtube_dl_package(
ModelSetting.get("youtube_dl_package")
if ModelSetting.get("youtube_dl_package")
else Logic.db_default["youtube_dl_package"],
import_pkg=True,
) )
#########################################################
# 플러그인 공용
#########################################################
blueprint = Blueprint(
package_name,
package_name,
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 = { class Plugin:
"main": [package_name, "youtube-dl"], package_name = __name__.split(".", maxsplit=1)[0]
"sub": [ logger = get_logger(package_name)
["setting", "설정"], blueprint = Blueprint(
["download", "다운로드"], package_name,
["thumbnail", "썸네일 다운로드"], package_name,
["sub", "자막 다운로드"], url_prefix=f"/{package_name}",
["list", "목록"], template_folder=os.path.join(os.path.dirname(__file__), "templates"),
["log", "로그"], static_folder=os.path.join(os.path.dirname(__file__), "static"),
], )
"category": "vod",
}
plugin_info = { # 메뉴 정의
"version": "3.1.1", menu = {
"name": "youtube-dl", "main": [package_name, "youtube-dl"],
"category_name": "vod", "sub": [
"developer": "joyfuI", ["setting", "설정"],
"description": "유튜브, 네이버TV 등 동영상 사이트에서 동영상 다운로드", ["download", "다운로드"],
"home": "https://github.com/joyfuI/youtube-dl", ["thumbnail", "썸네일 다운로드"],
"more": "", ["sub", "자막 다운로드"],
} ["list", "목록"],
["log", "로그"],
],
"category": "vod",
}
plugin_info = {
"version": "4.0.0",
"name": package_name,
"category_name": "vod",
"developer": "joyfuI",
"description": "유튜브, 네이버TV 등 동영상 사이트에서 동영상 다운로드",
"home": f"https://github.com/joyfuI/{package_name}",
"more": "",
}
ModelSetting = get_model_setting(package_name, logger)
logic = None
module_list = None
home_module = "list" # 기본모듈
youtube_dl_packages = ["youtube-dl", "yt-dlp"]
def plugin_load(): def initialize():
Logic.plugin_load()
def plugin_unload():
Logic.plugin_unload()
#########################################################
# WEB Menu
#########################################################
@blueprint.route("/")
def home():
return redirect(f"/{package_name}/list")
@blueprint.route("/<sub>")
@login_required
def first_menu(sub):
try: try:
arg = { app.config["SQLALCHEMY_BINDS"][
"package_name": package_name, Plugin.package_name
"template_name": f"{package_name}_{sub}", ] = f"sqlite:///{os.path.join(path_data, 'db', f'{Plugin.package_name}.db')}"
"package_version": plugin_info["version"], Util.save_from_dict_to_json(
} Plugin.plugin_info, os.path.join(os.path.dirname(__file__), "info.json")
)
if sub == "setting": # 로드할 모듈 정의
arg.update(ModelSetting.to_dict()) from .main import LogicMain
arg["package_list"] = LogicNormal.get_youtube_dl_package()
arg["youtube_dl_version"] = LogicNormal.get_youtube_dl_version()
arg["DEFAULT_FILENAME"] = LogicNormal.get_default_filename()
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == "download": Plugin.module_list = [LogicMain(Plugin)]
default_filename = ModelSetting.get("default_filename")
arg["filename"] = (
default_filename
if default_filename
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": Plugin.logic = Logic(Plugin)
default_filename = ModelSetting.get("default_filename") default_route_single_module(Plugin)
arg["filename"] = (
default_filename
if default_filename
else LogicNormal.get_default_filename()
)
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == "sub":
default_filename = ModelSetting.get("default_filename")
arg["filename"] = (
default_filename
if default_filename
else LogicNormal.get_default_filename()
)
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == "list":
return render_template(f"{package_name}_{sub}.html", arg=arg)
elif sub == "log":
return render_template("log.html", package=package_name)
except Exception as error: except Exception as error:
logger.error("Exception:%s", error) Plugin.logger.error("Exception:%s", error)
logger.error(traceback.format_exc()) Plugin.logger.error(traceback.format_exc())
return render_template("sample.html", title=f"{package_name} - {sub}")
#########################################################
# For UI
#########################################################
@blueprint.route("/ajax/<sub>", methods=["POST"])
@login_required
def ajax(sub):
logger.debug("AJAX %s %s", package_name, sub)
try:
# 공통 요청
if sub == "setting_save":
ret = ModelSetting.setting_save(request)
if request.form["ffmpeg_path"] == "ffmpeg":
ModelSetting.set("ffmpeg_path", "")
return jsonify(ret)
# UI 요청
elif sub == "ffmpeg_version":
path = request.form["path"]
ret = subprocess.check_output([path, "-version"])
ret = ret.decode().replace("\n", "<br>")
return jsonify(ret)
elif sub == "download":
postprocessor = request.form["postprocessor"]
video_convertor, extract_audio = LogicNormal.get_postprocessor()
preferedformat = None
preferredcodec = None
preferredquality = None
if postprocessor in video_convertor:
preferedformat = postprocessor
elif postprocessor in extract_audio:
preferredcodec = postprocessor
preferredquality = 192
youtube_dl = LogicNormal.download(
plugin=package_name,
url=request.form["url"],
filename=request.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
format=request.form["format"],
preferedformat=preferedformat,
preferredcodec=preferredcodec,
preferredquality=preferredquality,
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"),
)
youtube_dl.start()
socketio_emit("add", youtube_dl)
return jsonify([])
elif sub == "thumbnail":
youtube_dl = LogicNormal.thumbnail(
plugin=package_name,
url=request.form["url"],
filename=request.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
all_thumbnails=request.form["all_thumbnails"],
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"),
)
youtube_dl.start()
socketio_emit("add", youtube_dl)
return jsonify([])
elif sub == "sub":
youtube_dl = LogicNormal.sub(
plugin=package_name,
url=request.form["url"],
filename=request.form["filename"],
temp_path=ModelSetting.get("temp_path"),
save_path=ModelSetting.get("save_path"),
all_subs=request.form["all_subs"],
sub_lang=request.form["sub_lang"],
auto_sub=request.form["auto_sub"],
proxy=ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"),
)
youtube_dl.start()
socketio_emit("add", youtube_dl)
return jsonify([])
elif sub == "list":
ret = []
for i in LogicNormal.youtube_dl_list:
data = LogicNormal.get_data(i)
if data is not None:
ret.append(data)
return jsonify(ret)
elif sub == "all_stop":
for i in LogicNormal.youtube_dl_list:
i.stop()
return jsonify([])
elif sub == "stop":
index = int(request.form["index"])
LogicNormal.youtube_dl_list[index].stop()
return jsonify([])
except Exception as error:
logger.error("Exception:%s", error)
logger.error(traceback.format_exc())
#########################################################
# API
#########################################################
# API 명세는 https://github.com/joyfuI/youtube-dl#api # API 명세는 https://github.com/joyfuI/youtube-dl#api
@blueprint.route("/api/<sub>", methods=["GET", "POST"]) @Plugin.blueprint.route("/api/<sub>", methods=["GET", "POST"])
@cross_origin()
@check_api
def api(sub): def api(sub):
plugin = request.values.get("plugin") from .main import LogicMain
logger.debug("API %s %s: %s", package_name, sub, plugin) from .abort import LogicAbort
if not plugin: # 요청한 플러그인명이 빈문자열이거나 None면
abort(403) # 403 에러(거부)
try: try:
Plugin.logger.debug("API: %s, %s", sub, request.values)
plugin = request.values.get("plugin")
if not plugin: # 요청한 플러그인명이 빈문자열이거나 None면
abort(403) # 403 에러(거부)
# 동영상 정보를 반환하는 API # 동영상 정보를 반환하는 API
if sub == "info_dict": if sub == "info_dict":
url = request.values.get("url") url = request.values.get("url")
ret = {"errorCode": 0, "info_dict": None} ret = {"errorCode": 0, "info_dict": None}
if None in (url,): if None in (url,):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith("http"): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicAbort.abort(ret, 2) # 잘못된 동영상 주소
info_dict = LogicNormal.get_info_dict(url, ModelSetting.get("proxy")) info_dict = LogicMain.get_info_dict(url, Plugin.ModelSetting.get("proxy"))
if info_dict is None: if info_dict is None:
return LogicNormal.abort(ret, 10) # 실패 return LogicAbort.abort(ret, 10) # 실패
ret["info_dict"] = info_dict ret["info_dict"] = info_dict
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", Plugin.ModelSetting.get("default_filename")
)
save_path = request.values.get(
"save_path", Plugin.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)
@@ -280,9 +123,9 @@ def api(sub):
cookiefile = request.values.get("cookiefile", None) cookiefile = request.values.get("cookiefile", None)
ret = {"errorCode": 0, "index": None} ret = {"errorCode": 0, "index": None}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith("http"): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicAbort.abort(ret, 2) # 잘못된 동영상 주소
if preferredcodec not in ( if preferredcodec not in (
None, None,
"best", "best",
@@ -294,14 +137,14 @@ def api(sub):
"vorbis", "vorbis",
"wav", "wav",
): ):
return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음 return LogicAbort.abort(ret, 5) # 허용되지 않은 값이 있음
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicMain.get_default_filename()
youtube_dl = LogicNormal.download( youtube_dl = LogicMain.download(
plugin=plugin, plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get("temp_path"), temp_path=Plugin.ModelSetting.get("temp_path"),
save_path=save_path, save_path=save_path,
format=format_code, format=format_code,
preferedformat=preferedformat, preferedformat=preferedformat,
@@ -310,27 +153,28 @@ def api(sub):
dateafter=dateafter, dateafter=dateafter,
playlist=playlist, playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get("proxy"), proxy=Plugin.ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"), ffmpeg_path=Plugin.ModelSetting.get("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 LogicAbort.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) LogicMain.socketio_emit("add", youtube_dl)
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", Plugin.ModelSetting.get("default_filename")
)
save_path = request.values.get(
"save_path", Plugin.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)
@@ -339,42 +183,43 @@ def api(sub):
cookiefile = request.values.get("cookiefile", None) cookiefile = request.values.get("cookiefile", None)
ret = {"errorCode": 0, "index": None} ret = {"errorCode": 0, "index": None}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith("http"): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicAbort.abort(ret, 2) # 잘못된 동영상 주소
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicMain.get_default_filename()
youtube_dl = LogicNormal.thumbnail( youtube_dl = LogicMain.thumbnail(
plugin=plugin, plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get("temp_path"), temp_path=Plugin.ModelSetting.get("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=Plugin.ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"), ffmpeg_path=Plugin.ModelSetting.get("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 LogicAbort.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) LogicMain.socketio_emit("add", youtube_dl)
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", Plugin.ModelSetting.get("default_filename")
)
save_path = request.values.get(
"save_path", Plugin.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)
@@ -385,16 +230,16 @@ def api(sub):
cookiefile = request.values.get("cookiefile", None) cookiefile = request.values.get("cookiefile", None)
ret = {"errorCode": 0, "index": None} ret = {"errorCode": 0, "index": None}
if None in (key, url): if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith("http"): if not url.startswith("http"):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 return LogicAbort.abort(ret, 2) # 잘못된 동영상 주소
if not filename: if not filename:
filename = LogicNormal.get_default_filename() filename = LogicMain.get_default_filename()
youtube_dl = LogicNormal.sub( youtube_dl = LogicMain.sub(
plugin=plugin, plugin=plugin,
url=url, url=url,
filename=filename, filename=filename,
temp_path=ModelSetting.get("temp_path"), temp_path=Plugin.ModelSetting.get("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,
@@ -402,18 +247,17 @@ def api(sub):
dateafter=dateafter, dateafter=dateafter,
playlist=playlist, playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get("proxy"), proxy=Plugin.ModelSetting.get("proxy"),
ffmpeg_path=ModelSetting.get("ffmpeg_path"), ffmpeg_path=Plugin.ModelSetting.get("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 LogicAbort.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) LogicMain.socketio_emit("add", youtube_dl)
return jsonify(ret)
# 다운로드 시작을 요청하는 API # 다운로드 시작을 요청하는 API
elif sub == "start": elif sub == "start":
@@ -421,17 +265,16 @@ def api(sub):
key = request.values.get("key") key = request.values.get("key")
ret = {"errorCode": 0, "status": None} ret = {"errorCode": 0, "status": None}
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not 0 <= index < len(LogicNormal.youtube_dl_list): if not 0 <= index < len(LogicMain.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicAbort.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicMain.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicAbort.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 LogicAbort.abort(ret, 10) # 실패
return jsonify(ret)
# 다운로드 중지를 요청하는 API # 다운로드 중지를 요청하는 API
elif sub == "stop": elif sub == "stop":
@@ -439,17 +282,16 @@ def api(sub):
key = request.values.get("key") key = request.values.get("key")
ret = {"errorCode": 0, "status": None} ret = {"errorCode": 0, "status": None}
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not 0 <= index < len(LogicNormal.youtube_dl_list): if not 0 <= index < len(LogicMain.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicAbort.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicMain.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicAbort.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 LogicAbort.abort(ret, 10) # 실패
return jsonify(ret)
# 현재 상태를 반환하는 API # 현재 상태를 반환하는 API
elif sub == "status": elif sub == "status":
@@ -465,13 +307,13 @@ def api(sub):
"save_path": None, "save_path": None,
} }
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicAbort.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)
if not 0 <= index < len(LogicNormal.youtube_dl_list): if not 0 <= index < len(LogicMain.youtube_dl_list):
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남 return LogicAbort.abort(ret, 3) # 인덱스 범위를 벗어남
youtube_dl = LogicNormal.youtube_dl_list[index] youtube_dl = LogicMain.youtube_dl_list[index]
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicAbort.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"] = ( ret["start_time"] = (
@@ -486,18 +328,12 @@ def api(sub):
) )
ret["temp_path"] = youtube_dl.temp_path ret["temp_path"] = youtube_dl.temp_path
ret["save_path"] = youtube_dl.save_path ret["save_path"] = youtube_dl.save_path
return jsonify(ret)
return jsonify(ret)
except Exception as error: except Exception as error:
logger.error("Exception:%s", error) Plugin.logger.error("Exception:%s", error)
logger.error(traceback.format_exc()) Plugin.logger.error(traceback.format_exc())
abort(500) # 500 에러(서버 오류)
abort(404) # 404 에러(페이지 없음)
######################################################### logger = Plugin.logger
# socketio initialize()
#########################################################
def socketio_emit(cmd, data):
socketio.emit(
cmd, LogicNormal.get_data(data), namespace=f"/{package_name}", broadcast=True
)