v1.6.4 FFmpeg 경로 설정 추가
FFmpeg 경로 설정 추가 API에서 GET 요청 지원
This commit is contained in:
@@ -20,7 +20,7 @@ API에선 직접 비트레이트를 설정할 수 있습니다.
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
### 공통사항
|
### 공통사항
|
||||||
모든 요청은 `POST`로만 받습니다. 그리고 응답은 `JSON` 형식입니다.
|
모든 요청은 `POST` 또는 `GET`만 받습니다. 그리고 응답은 `JSON` 형식입니다.
|
||||||
모든 요청엔 *플러그인 이름* 정보가 있어야 합니다. `plugin` 키에 담아서 보내면 됩니다. 만약 *플러그인 이름* 정보가 없으면 **403 에러**를 반환합니다.
|
모든 요청엔 *플러그인 이름* 정보가 있어야 합니다. `plugin` 키에 담아서 보내면 됩니다. 만약 *플러그인 이름* 정보가 없으면 **403 에러**를 반환합니다.
|
||||||
요청을 처리하는 과정에서 예외가 발생하면 **500 에러**를 반환합니다. 이건 저한테 로그와 함께 알려주시면 됩니다.
|
요청을 처리하는 과정에서 예외가 발생하면 **500 에러**를 반환합니다. 이건 저한테 로그와 함께 알려주시면 됩니다.
|
||||||
모든 응답에는 `errorCode` 키가 있습니다. 코드의 의미는 아래 문단 참고
|
모든 응답에는 `errorCode` 키가 있습니다. 코드의 의미는 아래 문단 참고
|
||||||
@@ -131,6 +131,12 @@ API에선 직접 비트레이트를 설정할 수 있습니다.
|
|||||||
물론 해당 정보가 없으면 null입니다.
|
물론 해당 정보가 없으면 null입니다.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
v1.6.4
|
||||||
|
* FFmpeg 경로 설정 추가
|
||||||
|
SJVA에 내장된 버전 말고 원하는 버전을 사용할 수 있습니다.
|
||||||
|
* API에서 GET 요청 지원
|
||||||
|
이제 GET 요청으로도 API를 사용할 수 있습니다.
|
||||||
|
|
||||||
v1.6.3
|
v1.6.3
|
||||||
* 프록시 기능을 사용해도 국가차단 우회가 안 되는 문제 수정
|
* 프록시 기능을 사용해도 국가차단 우회가 안 되는 문제 수정
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "1.6.3", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"}
|
{"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "1.6.4", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"}
|
||||||
7
logic.py
7
logic.py
@@ -3,13 +3,14 @@
|
|||||||
# python
|
# python
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
# third-party
|
# third-party
|
||||||
|
|
||||||
# sjva 공용
|
# sjva 공용
|
||||||
from framework import db, path_data
|
from framework import db, path_app_root, path_data
|
||||||
from framework.util import Util
|
from framework.util import Util
|
||||||
|
|
||||||
# 패키지
|
# 패키지
|
||||||
@@ -20,6 +21,7 @@ from .model import ModelSetting
|
|||||||
class Logic(object):
|
class Logic(object):
|
||||||
db_default = {
|
db_default = {
|
||||||
'db_version': '1',
|
'db_version': '1',
|
||||||
|
'ffmpeg_path': 'ffmpeg' if platform.system() != 'Windows' else os.path.join(path_app_root, 'bin', 'Windows', 'ffmpeg.exe'),
|
||||||
'temp_path': os.path.join(path_data, 'download_tmp'),
|
'temp_path': os.path.join(path_data, 'download_tmp'),
|
||||||
'save_path': os.path.join(path_data, 'download'),
|
'save_path': os.path.join(path_data, 'download'),
|
||||||
'default_filename': '%(title)s-%(id)s.%(ext)s',
|
'default_filename': '%(title)s-%(id)s.%(ext)s',
|
||||||
@@ -45,16 +47,15 @@ class Logic(object):
|
|||||||
logger.debug('%s plugin_load', package_name)
|
logger.debug('%s plugin_load', package_name)
|
||||||
Logic.db_init()
|
Logic.db_init()
|
||||||
|
|
||||||
|
# 모듈 설치
|
||||||
try:
|
try:
|
||||||
import glob2
|
import glob2
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# glob2 설치
|
|
||||||
logger.debug('glob2 install')
|
logger.debug('glob2 install')
|
||||||
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'glob2'], universal_newlines=True))
|
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'glob2'], universal_newlines=True))
|
||||||
try:
|
try:
|
||||||
import flask_cors
|
import flask_cors
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# flask-cors 설치
|
|
||||||
logger.debug('flask-cors install')
|
logger.debug('flask-cors install')
|
||||||
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'flask-cors'], universal_newlines=True))
|
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'flask-cors'], universal_newlines=True))
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,19 @@ from flask import jsonify
|
|||||||
|
|
||||||
# 패키지
|
# 패키지
|
||||||
from .plugin import logger
|
from .plugin import logger
|
||||||
from .my_youtube_dl import Status
|
from .my_youtube_dl import MyYoutubeDL, Status
|
||||||
#########################################################
|
#########################################################
|
||||||
|
|
||||||
class LogicNormal(object):
|
class LogicNormal(object):
|
||||||
youtube_dl_list = []
|
youtube_dl_list = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_youtube_dl_version():
|
||||||
|
return MyYoutubeDL.get_version()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_preset_list():
|
def get_preset_list():
|
||||||
preset_list = [
|
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', '최저 화질'],
|
||||||
@@ -27,11 +31,10 @@ class LogicNormal(object):
|
|||||||
['bestaudio/best', '오디오만'],
|
['bestaudio/best', '오디오만'],
|
||||||
['_custom', '사용자 정의']
|
['_custom', '사용자 정의']
|
||||||
]
|
]
|
||||||
return preset_list
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_postprocessor_list():
|
def get_postprocessor_list():
|
||||||
postprocessor_list = [
|
return [
|
||||||
['', '후처리 안함', None],
|
['', '후처리 안함', None],
|
||||||
['mp4', 'MP4', '비디오 변환'],
|
['mp4', 'MP4', '비디오 변환'],
|
||||||
['flv', 'FLV', '비디오 변환'],
|
['flv', 'FLV', '비디오 변환'],
|
||||||
@@ -51,7 +54,6 @@ class LogicNormal(object):
|
|||||||
['vorbis', 'Vorbis', '오디오 추출'],
|
['vorbis', 'Vorbis', '오디오 추출'],
|
||||||
['wav', 'WAV', '오디오 추출']
|
['wav', 'WAV', '오디오 추출']
|
||||||
]
|
]
|
||||||
return postprocessor_list
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_postprocessor():
|
def get_postprocessor():
|
||||||
@@ -64,6 +66,42 @@ class LogicNormal(object):
|
|||||||
extract_audio.append(i[0])
|
extract_audio.append(i[0])
|
||||||
return video_convertor, extract_audio
|
return video_convertor, extract_audio
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def download(**kwagrs):
|
||||||
|
logger.debug(kwagrs)
|
||||||
|
plugin = kwagrs['plugin']
|
||||||
|
url = kwagrs['url']
|
||||||
|
filename = kwagrs['filename']
|
||||||
|
temp_path = kwagrs['temp_path']
|
||||||
|
save_path = kwagrs['save_path']
|
||||||
|
opts = {}
|
||||||
|
if 'format' in kwagrs and kwagrs['format']:
|
||||||
|
opts['format'] = kwagrs['format']
|
||||||
|
postprocessor = []
|
||||||
|
if 'preferedformat' in kwagrs and kwagrs['preferedformat']:
|
||||||
|
postprocessor.append({
|
||||||
|
'key': 'FFmpegVideoConvertor',
|
||||||
|
'preferedformat': kwagrs['preferedformat']
|
||||||
|
})
|
||||||
|
if 'preferredcodec' in kwagrs and kwagrs['preferredcodec']:
|
||||||
|
postprocessor.append({
|
||||||
|
'key': 'FFmpegExtractAudio',
|
||||||
|
'preferredcodec': kwagrs['preferredcodec'],
|
||||||
|
'preferredquality': str(kwagrs['preferredquality'])
|
||||||
|
})
|
||||||
|
if postprocessor:
|
||||||
|
opts['postprocessors'] = postprocessor
|
||||||
|
if 'archive' in kwagrs and kwagrs['archive']:
|
||||||
|
opts['download_archive'] = kwagrs['archive']
|
||||||
|
if 'proxy' in kwagrs and kwagrs['proxy']:
|
||||||
|
opts['proxy'] = kwagrs['proxy']
|
||||||
|
if 'ffmpeg_path' in kwagrs and kwagrs['ffmpeg_path']:
|
||||||
|
opts['ffmpeg_location'] = kwagrs['ffmpeg_path']
|
||||||
|
youtube_dl = MyYoutubeDL(plugin, url, filename, temp_path, save_path, opts)
|
||||||
|
youtube_dl.key = kwagrs.get('key')
|
||||||
|
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
|
||||||
|
return youtube_dl
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_data(youtube_dl):
|
def get_data(youtube_dl):
|
||||||
try:
|
try:
|
||||||
@@ -86,16 +124,16 @@ class LogicNormal(object):
|
|||||||
data['percent'] = '0'
|
data['percent'] = '0'
|
||||||
data['eta'] = youtube_dl.progress_hooks['eta'] if youtube_dl.progress_hooks['eta'] is not None else ''
|
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 ''
|
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'], youtube_dl.progress_hooks['total_bytes']): # 둘 다 값이 있으면
|
if None not in (youtube_dl.progress_hooks['downloaded_bytes'], youtube_dl.progress_hooks['total_bytes']): # 둘 다 값이 있으면
|
||||||
data['downloaded_bytes_str'] = LogicNormal.human_readable_size(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(youtube_dl.progress_hooks['total_bytes'])
|
||||||
data['percent'] = '%.2f' % (float(youtube_dl.progress_hooks['downloaded_bytes']) / float(youtube_dl.progress_hooks['total_bytes']) * 100)
|
data['percent'] = '%.2f' % (float(youtube_dl.progress_hooks['downloaded_bytes']) / float(youtube_dl.progress_hooks['total_bytes']) * 100)
|
||||||
@@ -107,6 +145,10 @@ class LogicNormal(object):
|
|||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_info_dict(url, proxy):
|
||||||
|
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'):
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class Status(Enum):
|
|||||||
return str_list[self.value]
|
return str_list[self.value]
|
||||||
|
|
||||||
|
|
||||||
class Youtube_dl(object):
|
class MyYoutubeDL(object):
|
||||||
_index = 0
|
__index = 0
|
||||||
_last_msg = ''
|
_last_msg = ''
|
||||||
|
|
||||||
def __init__(self, plugin, url, filename, temp_path, save_path=None, opts=None):
|
def __init__(self, plugin, url, filename, temp_path, save_path=None, opts=None):
|
||||||
@@ -58,10 +58,10 @@ class Youtube_dl(object):
|
|||||||
os.makedirs(save_path)
|
os.makedirs(save_path)
|
||||||
self.save_path = save_path
|
self.save_path = save_path
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.index = Youtube_dl._index
|
self.index = MyYoutubeDL.__index
|
||||||
Youtube_dl._index += 1
|
MyYoutubeDL.__index += 1
|
||||||
self._status = Status.READY
|
self.__status = Status.READY
|
||||||
self._thread = None
|
self.__thread = None
|
||||||
self.key = None
|
self.key = None
|
||||||
self.start_time = None # 시작 시간
|
self.start_time = None # 시작 시간
|
||||||
self.end_time = None # 종료 시간
|
self.end_time = None # 종료 시간
|
||||||
@@ -86,8 +86,8 @@ class Youtube_dl(object):
|
|||||||
def start(self):
|
def start(self):
|
||||||
if self.status != Status.READY:
|
if self.status != Status.READY:
|
||||||
return False
|
return False
|
||||||
self._thread = Thread(target=self.run)
|
self.__thread = Thread(target=self.run)
|
||||||
self._thread.start()
|
self.__thread.start()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -96,7 +96,7 @@ class Youtube_dl(object):
|
|||||||
try:
|
try:
|
||||||
self.start_time = datetime.now()
|
self.start_time = datetime.now()
|
||||||
self.status = Status.START
|
self.status = Status.START
|
||||||
info_dict = Youtube_dl.get_info_dict(self.url, self.opts.get('proxy')) # 동영상 정보 가져오기
|
info_dict = MyYoutubeDL.get_info_dict(self.url, self.opts.get('proxy')) # 동영상 정보 가져오기
|
||||||
if info_dict is None: # 가져오기 실패
|
if info_dict is None: # 가져오기 실패
|
||||||
self.status = Status.ERROR
|
self.status = Status.ERROR
|
||||||
return
|
return
|
||||||
@@ -163,7 +163,7 @@ class Youtube_dl(object):
|
|||||||
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 json.loads(Youtube_dl._last_msg)
|
return json.loads(MyYoutubeDL._last_msg)
|
||||||
|
|
||||||
def my_hook(self, d):
|
def my_hook(self, d):
|
||||||
if self.status != Status.STOP:
|
if self.status != Status.STOP:
|
||||||
@@ -188,18 +188,18 @@ class Youtube_dl(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
return self._status
|
return self.__status
|
||||||
|
|
||||||
@status.setter
|
@status.setter
|
||||||
def status(self, value):
|
def status(self, value):
|
||||||
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):
|
||||||
Youtube_dl._last_msg = msg
|
MyYoutubeDL._last_msg = msg
|
||||||
if msg.find('') != -1 or msg.find('{') != -1:
|
if msg.find('') != -1 or msg.find('{') != -1:
|
||||||
return # 과도한 로그 방지
|
return # 과도한 로그 방지
|
||||||
logger.debug(msg)
|
logger.debug(msg)
|
||||||
|
|||||||
164
plugin.py
164
plugin.py
@@ -3,6 +3,7 @@
|
|||||||
# python
|
# python
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
|
import subprocess
|
||||||
|
|
||||||
# third-party
|
# third-party
|
||||||
from flask import Blueprint, request, render_template, redirect, jsonify, abort
|
from flask import Blueprint, request, render_template, redirect, jsonify, abort
|
||||||
@@ -18,7 +19,6 @@ logger = get_logger(package_name)
|
|||||||
from .logic import Logic
|
from .logic import Logic
|
||||||
from .logic_normal import LogicNormal
|
from .logic_normal import LogicNormal
|
||||||
from .model import ModelSetting
|
from .model import ModelSetting
|
||||||
from .my_youtube_dl import Youtube_dl
|
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# 플러그인 공용
|
# 플러그인 공용
|
||||||
@@ -34,7 +34,7 @@ menu = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugin_info = {
|
plugin_info = {
|
||||||
'version': '1.6.3',
|
'version': '1.6.4',
|
||||||
'name': 'youtube-dl',
|
'name': 'youtube-dl',
|
||||||
'category_name': 'vod',
|
'category_name': 'vod',
|
||||||
'developer': 'joyfuI',
|
'developer': 'joyfuI',
|
||||||
@@ -67,7 +67,7 @@ def first_menu(sub):
|
|||||||
|
|
||||||
if sub == 'setting':
|
if sub == 'setting':
|
||||||
arg.update(ModelSetting.to_dict())
|
arg.update(ModelSetting.to_dict())
|
||||||
arg['youtube_dl_version'] = Youtube_dl.get_version()
|
arg['youtube_dl_version'] = LogicNormal.get_youtube_dl_version()
|
||||||
return render_template('%s_%s.html' % (package_name, sub), arg=arg)
|
return render_template('%s_%s.html' % (package_name, sub), arg=arg)
|
||||||
|
|
||||||
elif sub == 'download':
|
elif sub == 'download':
|
||||||
@@ -100,33 +100,33 @@ def ajax(sub):
|
|||||||
return jsonify(ret)
|
return jsonify(ret)
|
||||||
|
|
||||||
# UI 요청
|
# UI 요청
|
||||||
|
elif sub == 'ffmpeg_version':
|
||||||
|
path = request.form['path']
|
||||||
|
ret = subprocess.check_output([path, '-version']).replace('\n', '<br>')
|
||||||
|
return jsonify(ret)
|
||||||
|
|
||||||
elif sub == 'download':
|
elif sub == 'download':
|
||||||
url = request.form['url']
|
|
||||||
filename = request.form['filename']
|
|
||||||
temp_path = ModelSetting.get('temp_path')
|
|
||||||
save_path = ModelSetting.get('save_path')
|
|
||||||
format_code = request.form['format']
|
|
||||||
postprocessor = request.form['postprocessor']
|
postprocessor = request.form['postprocessor']
|
||||||
proxy = ModelSetting.get('proxy')
|
|
||||||
opts = {}
|
|
||||||
if format_code:
|
|
||||||
opts['format'] = format_code
|
|
||||||
video_convertor, extract_audio = LogicNormal.get_postprocessor()
|
video_convertor, extract_audio = LogicNormal.get_postprocessor()
|
||||||
|
preferedformat = None
|
||||||
|
preferredcodec = None
|
||||||
|
preferredquality = None
|
||||||
if postprocessor in video_convertor:
|
if postprocessor in video_convertor:
|
||||||
opts['postprocessors'] = [{
|
preferedformat = postprocessor
|
||||||
'key': 'FFmpegVideoConvertor',
|
|
||||||
'preferedformat': postprocessor
|
|
||||||
}]
|
|
||||||
elif postprocessor in extract_audio:
|
elif postprocessor in extract_audio:
|
||||||
opts['postprocessors'] = [{
|
preferredcodec = postprocessor
|
||||||
'key': 'FFmpegExtractAudio',
|
preferredquality = 192
|
||||||
'preferredcodec': postprocessor,
|
youtube_dl = LogicNormal.download(plugin=package_name,
|
||||||
'preferredquality': '192'
|
url=request.form['url'],
|
||||||
}]
|
filename=request.form['filename'],
|
||||||
if proxy:
|
temp_path=ModelSetting.get('temp_path'),
|
||||||
opts['proxy'] = proxy
|
save_path=ModelSetting.get('save_path'),
|
||||||
youtube_dl = Youtube_dl(package_name, url, filename, temp_path, save_path, opts)
|
format=request.form['format'],
|
||||||
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
|
preferedformat=preferedformat,
|
||||||
|
preferredcodec=preferredcodec,
|
||||||
|
preferredquality=preferredquality,
|
||||||
|
proxy=ModelSetting.get('proxy'),
|
||||||
|
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([])
|
||||||
@@ -154,75 +154,63 @@ def ajax(sub):
|
|||||||
@blueprint.route('/api/<sub>', methods=['GET', 'POST'])
|
@blueprint.route('/api/<sub>', methods=['GET', 'POST'])
|
||||||
@check_api
|
@check_api
|
||||||
def api(sub):
|
def api(sub):
|
||||||
plugin = request.form.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.form.get('url')
|
url = request.values.get('url')
|
||||||
ret = {
|
ret = {
|
||||||
'errorCode': 0,
|
'errorCode': 0,
|
||||||
'info_dict': None
|
'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 = Youtube_dl.get_info_dict(url, ModelSetting.get('proxy'))
|
info_dict = LogicNormal.get_info_dict(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.form.get('key')
|
key = request.values.get('key')
|
||||||
url = request.form.get('url')
|
url = request.values.get('url')
|
||||||
filename = request.form.get('filename', ModelSetting.get('default_filename'))
|
filename = request.values.get('filename', ModelSetting.get('default_filename'))
|
||||||
save_path = request.form.get('save_path', ModelSetting.get('save_path'))
|
save_path = request.values.get('save_path', ModelSetting.get('save_path'))
|
||||||
format_code = request.form.get('format', None)
|
format_code = request.values.get('format', None)
|
||||||
preferedformat = request.form.get('preferedformat', None)
|
preferedformat = request.values.get('preferedformat', None)
|
||||||
preferredcodec = request.form.get('preferredcodec', None)
|
preferredcodec = request.values.get('preferredcodec', None)
|
||||||
preferredquality = request.form.get('preferredquality', 192)
|
preferredquality = request.values.get('preferredquality', 192)
|
||||||
archive = request.form.get('archive', None)
|
archive = request.values.get('archive', None)
|
||||||
start = request.form.get('start', False)
|
start = request.values.get('start', False)
|
||||||
ret = {
|
ret = {
|
||||||
'errorCode': 0,
|
'errorCode': 0,
|
||||||
'index': None
|
'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) # 잘못된 동영상 주소
|
||||||
opts = {}
|
if preferredcodec not in (None, 'best', 'mp3', 'aac', 'flac', 'm4a', 'opus', 'vorbis', 'wav'):
|
||||||
if format_code is not None:
|
return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음
|
||||||
opts['format'] = format_code
|
youtube_dl = LogicNormal.download(plugin=plugin,
|
||||||
postprocessor = []
|
url=url,
|
||||||
if preferedformat is not None:
|
filename=filename,
|
||||||
postprocessor.append({
|
temp_path=ModelSetting.get('temp_path'),
|
||||||
'key': 'FFmpegVideoConvertor',
|
save_path=save_path,
|
||||||
'preferedformat': preferedformat
|
format=format_code,
|
||||||
})
|
preferedformat=preferedformat,
|
||||||
if preferredcodec is not None:
|
preferredcodec=preferredcodec,
|
||||||
if preferredcodec not in ('best', 'mp3', 'aac', 'flac', 'm4a', 'opus', 'vorbis', 'wav'):
|
preferredquality=preferredquality,
|
||||||
return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음
|
archive=archive,
|
||||||
postprocessor.append({
|
proxy=ModelSetting.get('proxy'),
|
||||||
'key': 'FFmpegExtractAudio',
|
ffmpeg_path=ModelSetting.get('ffmpeg_path'),
|
||||||
'preferredcodec': preferredcodec,
|
key=key)
|
||||||
'preferredquality': str(preferredquality)
|
|
||||||
})
|
|
||||||
if postprocessor:
|
|
||||||
opts['postprocessors'] = postprocessor
|
|
||||||
proxy = ModelSetting.get('proxy')
|
|
||||||
if proxy:
|
|
||||||
opts['proxy'] = proxy
|
|
||||||
if archive is not None:
|
|
||||||
opts['download_archive'] = archive
|
|
||||||
youtube_dl = Youtube_dl(plugin, url, filename, ModelSetting.get('temp_path'), save_path, opts)
|
|
||||||
youtube_dl.key = key
|
|
||||||
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
|
|
||||||
ret['index'] = youtube_dl.index
|
ret['index'] = youtube_dl.index
|
||||||
if start:
|
if start:
|
||||||
youtube_dl.start()
|
youtube_dl.start()
|
||||||
@@ -231,50 +219,50 @@ def api(sub):
|
|||||||
|
|
||||||
# 다운로드 시작을 요청하는 API
|
# 다운로드 시작을 요청하는 API
|
||||||
elif sub == 'start':
|
elif sub == 'start':
|
||||||
index = request.form.get('index')
|
index = request.values.get('index')
|
||||||
key = request.form.get('key')
|
key = request.values.get('key')
|
||||||
ret = {
|
ret = {
|
||||||
'errorCode': 0,
|
'errorCode': 0,
|
||||||
'status': None
|
'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.form.get('index')
|
index = request.values.get('index')
|
||||||
key = request.form.get('key')
|
key = request.values.get('key')
|
||||||
ret = {
|
ret = {
|
||||||
'errorCode': 0,
|
'errorCode': 0,
|
||||||
'status': None
|
'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.form.get('index')
|
index = request.values.get('index')
|
||||||
key = request.form.get('key')
|
key = request.values.get('key')
|
||||||
ret = {
|
ret = {
|
||||||
'errorCode': 0,
|
'errorCode': 0,
|
||||||
'status': None,
|
'status': None,
|
||||||
@@ -284,13 +272,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 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['start_time'] = youtube_dl.start_time.strftime('%Y-%m-%dT%H:%M:%S') if youtube_dl.start_time is not None else None
|
ret['start_time'] = youtube_dl.start_time.strftime('%Y-%m-%dT%H:%M:%S') if youtube_dl.start_time is not None else None
|
||||||
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['end_time'] = youtube_dl.end_time.strftime('%Y-%m-%dT%H:%M:%S') if youtube_dl.end_time is not None else None
|
||||||
@@ -301,7 +289,7 @@ def api(sub):
|
|||||||
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 에러(페이지 없음)
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# socketio
|
# socketio
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
// 후처리 변경
|
// 후처리 변경
|
||||||
$('#postprocessor').change(function () {
|
$('#postprocessor').change(function () {
|
||||||
if ($(this).find($('option[value="' + $(this).val() + '"]')).parent().attr('label') === '오디오 추출') {
|
if ($(this).find($(`option[value="${$(this).val()}"]`)).parent().attr('label') === '오디오 추출') {
|
||||||
$('#preset').val('bestaudio/best').change();
|
$('#preset').val('bestaudio/best').change();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/' + package_name + '/ajax/download',
|
url: `/${package_name}/ajax/download`,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
cache: false,
|
cache: false,
|
||||||
data: {
|
data: {
|
||||||
@@ -81,12 +81,15 @@
|
|||||||
format: $('#format').val(),
|
format: $('#format').val(),
|
||||||
postprocessor: $('#postprocessor').val()
|
postprocessor: $('#postprocessor').val()
|
||||||
},
|
},
|
||||||
dataType: 'json',
|
dataType: 'json'
|
||||||
success: function () {
|
}).done(function () {
|
||||||
$.notify('<strong>분석중..</strong>', {
|
$.notify('<strong>분석중..</strong>', {
|
||||||
type: 'info'
|
type: 'info'
|
||||||
});
|
});
|
||||||
}
|
}).fail(function () {
|
||||||
|
$.notify('<strong>다운로드 요청 실패</strong>', {
|
||||||
|
type: 'danger'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
const package_name = '{{ arg["package_name"] }}';
|
const package_name = '{{ arg["package_name"] }}';
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
let socket = io.connect(location.origin + '/' + package_name);
|
let socket = io.connect(`${location.origin}/${package_name}`);
|
||||||
|
|
||||||
socket.on('add', function (data) {
|
socket.on('add', function (data) {
|
||||||
$('#list').append(make_item(data));
|
$('#list').append(make_item(data));
|
||||||
@@ -54,33 +54,31 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/' + package_name + '/ajax/list',
|
url: `/${package_name}/ajax/list`,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
cache: false,
|
cache: false,
|
||||||
data: {},
|
data: {},
|
||||||
dataType: 'json',
|
dataType: 'json'
|
||||||
success: function (data) {
|
}).done(function (data) {
|
||||||
let str = '';
|
let str = '';
|
||||||
for (let i of data) {
|
for (let item of data) {
|
||||||
str += make_item(i);
|
str += make_item(item);
|
||||||
}
|
|
||||||
$('#list').html(str);
|
|
||||||
}
|
}
|
||||||
|
$('#list').html(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#list').on('click', '.youtube-dl_stop', function () {
|
$('#list').on('click', '.youtube-dl_stop', function () {
|
||||||
let index = $(this).data('index');
|
let index = $(this).data('index');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/' + package_name + '/ajax/stop',
|
url: `/${package_name}/ajax/stop`,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
cache: false,
|
cache: false,
|
||||||
data: {
|
data: {
|
||||||
index: index
|
index: index
|
||||||
},
|
},
|
||||||
dataType: 'json',
|
dataType: 'json'
|
||||||
success: function () {
|
}).done(function () {
|
||||||
location.reload(); // 새로고침
|
location.reload();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@@ -158,8 +156,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function status_html(data) {
|
function status_html(data) {
|
||||||
$('#item_' + data.index).html(get_item(data));
|
$(`#item_${data.index}`).html(get_item(data));
|
||||||
$('#detail_' + data.index).html(get_detail(data));
|
$(`#detail_${data.index}`).html(get_detail(data));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
<div>
|
<div>
|
||||||
{{ macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전', value=arg['youtube_dl_version']) }}
|
{{ macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전', value=arg['youtube_dl_version']) }}
|
||||||
<form id="setting">
|
<form id="setting">
|
||||||
{{ macros.setting_input_text('temp_path', '임시 폴더', value=arg['temp_path'], placeholder='임시 폴더 경로', desc='다운로드 파일이 임시로 저장될 폴더 입니다.') }}
|
{{ macros.setting_input_text_and_buttons('ffmpeg_path', 'FFmpeg 경로', [['ffmpeg_version', '버전확인']], value=arg['ffmpeg_path'], placeholder='ffmpeg', desc='SJVA에 내장된 버전 말고 원하는 버전을 사용할 수 있습니다.') }}
|
||||||
{{ macros.setting_input_text('save_path', '저장 폴더', value=arg['save_path'], placeholder='저장 폴더 경로', desc='정상적으로 완료된 파일이 이동할 폴더 입니다.') }}
|
{{ macros.setting_input_text('temp_path', '임시 폴더', value=arg['temp_path'], desc='다운로드 파일이 임시로 저장될 폴더입니다.') }}
|
||||||
{{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder='저장 폴더 경로', desc=['템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template 참고', '기본값은 "%(title)s-%(id)s.%(ext)s"입니다.']) }}
|
{{ macros.setting_input_text('save_path', '저장 폴더', value=arg['save_path'], desc='정상적으로 완료된 파일이 이동할 폴더입니다.') }}
|
||||||
{{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], placeholder='프록시 주소', desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }}
|
{{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], desc=['템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template 참고', '기본값은 "%(title)s-%(id)s.%(ext)s"입니다.']) }}
|
||||||
|
{{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }}
|
||||||
{{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }}
|
{{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }}
|
||||||
{{ macros.setting_button([['global_setting_save_btn', '저장']]) }}
|
{{ macros.setting_button([['global_setting_save_btn', '저장']]) }}
|
||||||
</form>
|
</form>
|
||||||
@@ -16,6 +17,33 @@
|
|||||||
<script>
|
<script>
|
||||||
"use strict";
|
"use strict";
|
||||||
const package_name = '{{ arg["package_name"] }}';
|
const package_name = '{{ arg["package_name"] }}';
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$('#youtube_dl_version').prop('readonly', true);
|
||||||
|
|
||||||
|
// FFmpeg 버전확인
|
||||||
|
$('#ffmpeg_version').click(function () {
|
||||||
|
let ffmpeg_path = $('#ffmpeg_path').val()
|
||||||
|
$.ajax({
|
||||||
|
url: `/${package_name}/ajax/ffmpeg_version`,
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: {
|
||||||
|
path: ffmpeg_path.length === 0 ? 'ffmpeg' : ffmpeg_path
|
||||||
|
},
|
||||||
|
dataType: 'json'
|
||||||
|
}).done(function (data) {
|
||||||
|
$('#modal_title').html(`${ffmpeg_path} -version`);
|
||||||
|
$('#modal_body').html(data);
|
||||||
|
$('#large_modal').modal();
|
||||||
|
}).fail(function () {
|
||||||
|
$.notify('<strong>버전확인 실패</strong>', {
|
||||||
|
type: 'danger'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user