Files
youtube-dl/plugin.py

475 lines
21 KiB
Python
Raw Normal View History

2020-02-05 20:54:30 +09:00
# -*- coding: utf-8 -*-
# python
import os
import sys
2020-02-05 20:54:30 +09:00
import traceback
import subprocess
2020-02-05 20:54:30 +09:00
# third-party
2020-02-14 00:49:47 +09:00
from flask import Blueprint, request, render_template, redirect, jsonify, abort
2020-02-05 20:54:30 +09:00
from flask_login import login_required
# sjva 공용
from framework.logger import get_logger
2020-10-10 19:35:59 +09:00
from framework import check_api, socketio
2020-02-05 20:54:30 +09:00
# 패키지
2020-02-05 20:54:30 +09:00
package_name = __name__.split('.')[0]
logger = get_logger(package_name)
from .logic import Logic
from .logic_normal import LogicNormal
2020-02-05 20:54:30 +09:00
from .model import ModelSetting
2021-02-11 18:08:47 +09:00
YOUTUBE_DL_PACKAGE = LogicNormal.get_youtube_dl_package(Logic.db_default['youtube_dl_package'], import_pkg=True)
2020-02-05 20:54:30 +09:00
#########################################################
2020-03-01 01:34:59 +09:00
# 플러그인 공용
#########################################################
2021-02-11 18:08:47 +09:00
blueprint = Blueprint(package_name, package_name, url_prefix='/%s' % package_name,
template_folder=os.path.join(os.path.dirname(__file__), 'templates'),
static_folder=os.path.join(os.path.dirname(__file__), 'static'))
if ModelSetting.get_bool('activate_cors'):
try:
from flask_cors import CORS
except ImportError:
logger.debug('flask-cors install')
2021-02-11 18:08:47 +09:00
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'flask-cors'],
universal_newlines=True))
from flask_cors import CORS
CORS(blueprint, resources={r"/youtube-dl/api/*": {"origins": "*"}})
2020-03-01 01:34:59 +09:00
menu = {
2020-07-23 23:00:02 +09:00
'main': [package_name, 'youtube-dl'],
'sub': [
['setting', '설정'], ['download', '다운로드'], ['thumbnail', '썸네일 다운로드'], ['sub', '자막 다운로드'], ['list', '목록'],
['log', '로그']
2020-07-23 23:00:02 +09:00
],
'category': 'vod'
2020-03-01 01:34:59 +09:00
}
2020-02-05 20:54:30 +09:00
plugin_info = {
2021-04-25 13:06:45 +09:00
'version': '2.4.0',
2020-07-23 23:00:02 +09:00
'name': 'youtube-dl',
'category_name': 'vod',
'developer': 'joyfuI',
'description': '유튜브, 네이버TV 등 동영상 사이트에서 동영상 다운로드',
'home': 'https://github.com/joyfuI/youtube-dl',
'more': ''
2020-02-05 20:54:30 +09:00
}
2021-02-11 18:08:47 +09:00
2020-03-01 01:34:59 +09:00
def plugin_load():
2020-07-23 23:00:02 +09:00
Logic.plugin_load()
2020-11-15 14:06:33 +09:00
global YOUTUBE_DL_PACKAGE
2021-01-22 14:56:53 +09:00
YOUTUBE_DL_PACKAGE = LogicNormal.get_youtube_dl_package(ModelSetting.get('youtube_dl_package'), import_pkg=True)
2020-03-01 01:34:59 +09:00
2021-02-11 18:08:47 +09:00
2020-03-01 01:34:59 +09:00
def plugin_unload():
2020-07-23 23:00:02 +09:00
Logic.plugin_unload()
2020-02-05 20:54:30 +09:00
2021-02-11 18:08:47 +09:00
2020-02-05 20:54:30 +09:00
#########################################################
# WEB Menu
#########################################################
@blueprint.route('/')
def home():
2020-07-23 23:00:02 +09:00
return redirect('/%s/list' % package_name)
2020-02-05 20:54:30 +09:00
2021-02-11 18:08:47 +09:00
2020-02-05 20:54:30 +09:00
@blueprint.route('/<sub>')
@login_required
def first_menu(sub):
2020-07-23 23:00:02 +09:00
try:
2021-02-11 18:08:47 +09:00
arg = {
'package_name': package_name,
'template_name': '%s_%s' % (package_name, sub)
}
2020-03-01 01:34:59 +09:00
2020-07-23 23:00:02 +09:00
if sub == 'setting':
arg.update(ModelSetting.to_dict())
2020-11-15 14:06:33 +09:00
arg['package_list'] = LogicNormal.get_youtube_dl_package()
arg['youtube_dl_version'] = LogicNormal.get_youtube_dl_version()
2020-08-08 15:33:14 +09:00
arg['DEFAULT_FILENAME'] = LogicNormal.get_default_filename()
2020-07-23 23:00:02 +09:00
return render_template('%s_%s.html' % (package_name, sub), arg=arg)
2020-02-05 20:54:30 +09:00
2020-07-23 23:00:02 +09:00
elif sub == 'download':
2020-08-08 15:01:46 +09:00
default_filename = ModelSetting.get('default_filename')
2020-10-09 12:50:03 +09:00
arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename()
2020-07-23 23:00:02 +09:00
arg['preset_list'] = LogicNormal.get_preset_list()
arg['postprocessor_list'] = LogicNormal.get_postprocessor_list()
return render_template('%s_%s.html' % (package_name, sub), arg=arg)
2020-02-05 20:54:30 +09:00
elif sub == 'thumbnail':
default_filename = ModelSetting.get('default_filename')
arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename()
return render_template('%s_%s.html' % (package_name, sub), 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('%s_%s.html' % (package_name, sub), arg=arg)
2020-07-23 23:00:02 +09:00
elif sub == 'list':
return render_template('%s_%s.html' % (package_name, sub), arg=arg)
2020-02-05 20:54:30 +09:00
2020-07-23 23:00:02 +09:00
elif sub == 'log':
return render_template('log.html', package=package_name)
except Exception as e:
logger.error('Exception:%s', e)
logger.error(traceback.format_exc())
return render_template('sample.html', title='%s - %s' % (package_name, sub))
2020-02-05 20:54:30 +09:00
2021-02-11 18:08:47 +09:00
2020-02-05 20:54:30 +09:00
#########################################################
# For UI
#########################################################
@blueprint.route('/ajax/<sub>', methods=['POST'])
@login_required
2020-02-05 20:54:30 +09:00
def ajax(sub):
2020-07-23 23:00:02 +09:00
logger.debug('AJAX %s %s', package_name, sub)
try:
# 공통 요청
if sub == 'setting_save':
ret = ModelSetting.setting_save(request)
2020-08-08 15:01:46 +09:00
if request.form['ffmpeg_path'] == 'ffmpeg':
ModelSetting.set('ffmpeg_path', '')
2020-07-23 23:00:02 +09:00
return jsonify(ret)
2020-02-05 20:54:30 +09:00
2020-07-23 23:00:02 +09:00
# UI 요청
elif sub == 'ffmpeg_version':
path = request.form['path']
2020-11-08 16:06:34 +09:00
ret = subprocess.check_output([path, '-version'])
ret = ret.decode().replace('\n', '<br>')
return jsonify(ret)
2020-07-23 23:00:02 +09:00
elif sub == 'download':
postprocessor = request.form['postprocessor']
video_convertor, extract_audio = LogicNormal.get_postprocessor()
preferedformat = None
preferredcodec = None
preferredquality = None
2020-07-23 23:00:02 +09:00
if postprocessor in video_convertor:
preferedformat = postprocessor
2020-07-23 23:00:02 +09:00
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'))
2020-07-23 23:00:02 +09:00
youtube_dl.start()
socketio_emit('add', youtube_dl)
return jsonify([])
2020-02-05 20:54:30 +09:00
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([])
2020-07-23 23:00:02 +09:00
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)
2020-02-05 20:54:30 +09:00
elif sub == 'all_stop':
for i in LogicNormal.youtube_dl_list:
i.stop()
return jsonify([])
2020-07-23 23:00:02 +09:00
elif sub == 'stop':
index = int(request.form['index'])
LogicNormal.youtube_dl_list[index].stop()
return jsonify([])
except Exception as e:
logger.error('Exception:%s', e)
logger.error(traceback.format_exc())
2020-02-05 20:54:30 +09:00
2021-02-11 18:08:47 +09:00
2020-02-05 20:54:30 +09:00
#########################################################
# API
#########################################################
2020-02-14 00:49:47 +09:00
# API 명세는 https://github.com/joyfuI/youtube-dl#api
2020-02-05 20:54:30 +09:00
@blueprint.route('/api/<sub>', methods=['GET', 'POST'])
2020-03-04 11:51:09 +09:00
@check_api
2020-02-05 20:54:30 +09:00
def api(sub):
plugin = request.values.get('plugin')
2020-07-23 23:00:02 +09:00
logger.debug('API %s %s: %s', package_name, sub, plugin)
if not plugin: # 요청한 플러그인명이 빈문자열이거나 None면
abort(403) # 403 에러(거부)
try:
# 동영상 정보를 반환하는 API
if sub == 'info_dict':
url = request.values.get('url')
2020-07-23 23:00:02 +09:00
ret = {
'errorCode': 0,
'info_dict': None
}
if None in (url,):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
2020-07-23 23:00:02 +09:00
if not url.startswith('http'):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
info_dict = LogicNormal.get_info_dict(url, ModelSetting.get('proxy'))
2020-07-23 23:00:02 +09:00
if info_dict is None:
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 10) # 실패
2020-07-23 23:00:02 +09:00
ret['info_dict'] = info_dict
return jsonify(ret)
2020-02-14 00:49:47 +09:00
# 비디오 다운로드 준비를 요청하는 API
2020-07-23 23:00:02 +09:00
elif sub == 'download':
key = request.values.get('key')
url = request.values.get('url')
filename = request.values.get('filename', ModelSetting.get('default_filename'))
save_path = request.values.get('save_path', ModelSetting.get('save_path'))
format_code = request.values.get('format', None)
preferedformat = request.values.get('preferedformat', None)
preferredcodec = request.values.get('preferredcodec', None)
preferredquality = request.values.get('preferredquality', 192)
2020-11-08 22:58:35 +09:00
dateafter = request.values.get('dateafter', None)
2021-04-28 20:45:42 +09:00
playlist = request.values.get('playlist', None)
archive = request.values.get('archive', None)
start = request.values.get('start', False)
cookiefile = request.values.get('cookiefile', None)
2020-07-23 23:00:02 +09:00
ret = {
'errorCode': 0,
'index': None
}
if None in (key, url):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
2020-07-23 23:00:02 +09:00
if not url.startswith('http'):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if preferredcodec not in (None, 'best', 'mp3', 'aac', 'flac', 'm4a', 'opus', 'vorbis', 'wav'):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 5) # 허용되지 않은 값이 있음
if not filename:
2020-08-08 15:33:14 +09:00
filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.download(plugin=plugin,
url=url,
filename=filename,
temp_path=ModelSetting.get('temp_path'),
save_path=save_path,
format=format_code,
preferedformat=preferedformat,
preferredcodec=preferredcodec,
preferredquality=preferredquality,
2020-11-08 22:58:35 +09:00
dateafter=dateafter,
2021-04-28 20:45:42 +09:00
playlist=playlist,
archive=archive,
proxy=ModelSetting.get('proxy'),
ffmpeg_path=ModelSetting.get('ffmpeg_path'),
key=key,
2021-01-11 22:31:35 +09:00
cookiefile=cookiefile)
2020-11-08 22:58:35 +09:00
if youtube_dl is None:
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 10) # 실패
2020-07-23 23:00:02 +09:00
ret['index'] = youtube_dl.index
if start:
youtube_dl.start()
socketio_emit('add', youtube_dl)
return jsonify(ret)
2020-02-14 00:49:47 +09:00
# 썸네일 다운로드 준비를 요청하는 API
elif sub == 'thumbnail':
key = request.values.get('key')
url = request.values.get('url')
filename = request.values.get('filename', ModelSetting.get('default_filename'))
save_path = request.values.get('save_path', ModelSetting.get('save_path'))
all_thumbnails = request.values.get('all_thumbnails', False)
dateafter = request.values.get('dateafter', None)
2021-04-28 20:45:42 +09:00
playlist = request.values.get('playlist', None)
archive = request.values.get('archive', None)
start = request.values.get('start', False)
cookiefile = request.values.get('cookiefile', None)
ret = {
'errorCode': 0,
'index': None
}
if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if not filename:
filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.thumbnail(plugin=plugin,
url=url,
filename=filename,
temp_path=ModelSetting.get('temp_path'),
save_path=save_path,
all_thumbnails=all_thumbnails,
dateafter=dateafter,
2021-04-28 20:45:42 +09:00
playlist=playlist,
archive=archive,
proxy=ModelSetting.get('proxy'),
ffmpeg_path=ModelSetting.get('ffmpeg_path'),
key=key,
cookiefile=cookiefile)
if youtube_dl is None:
return LogicNormal.abort(ret, 10) # 실패
ret['index'] = youtube_dl.index
if start:
youtube_dl.start()
socketio_emit('add', youtube_dl)
return jsonify(ret)
# 자막 다운로드 준비를 요청하는 API
elif sub == 'sub':
key = request.values.get('key')
url = request.values.get('url')
filename = request.values.get('filename', ModelSetting.get('default_filename'))
save_path = request.values.get('save_path', ModelSetting.get('save_path'))
all_subs = request.values.get('all_subs', False)
sub_lang = request.values.get('sub_lang', 'ko')
auto_sub = request.values.get('all_subs', False)
dateafter = request.values.get('dateafter', None)
2021-04-28 20:45:42 +09:00
playlist = request.values.get('playlist', None)
archive = request.values.get('archive', None)
start = request.values.get('start', False)
cookiefile = request.values.get('cookiefile', None)
ret = {
'errorCode': 0,
'index': None
}
if None in (key, url):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
if not url.startswith('http'):
return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소
if not filename:
filename = LogicNormal.get_default_filename()
youtube_dl = LogicNormal.sub(plugin=plugin,
url=url,
filename=filename,
temp_path=ModelSetting.get('temp_path'),
save_path=save_path,
all_subs=all_subs,
sub_lang=sub_lang,
auto_sub=auto_sub,
dateafter=dateafter,
2021-04-28 20:45:42 +09:00
playlist=playlist,
archive=archive,
proxy=ModelSetting.get('proxy'),
ffmpeg_path=ModelSetting.get('ffmpeg_path'),
key=key,
cookiefile=cookiefile)
if youtube_dl is None:
return LogicNormal.abort(ret, 10) # 실패
ret['index'] = youtube_dl.index
if start:
youtube_dl.start()
socketio_emit('add', youtube_dl)
return jsonify(ret)
2020-07-23 23:00:02 +09:00
# 다운로드 시작을 요청하는 API
elif sub == 'start':
index = request.values.get('index')
key = request.values.get('key')
2020-07-23 23:00:02 +09:00
ret = {
'errorCode': 0,
'status': None
}
if None in (index, key):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
2020-07-23 23:00:02 +09:00
index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
2020-07-23 23:00:02 +09:00
youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key:
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
2020-07-23 23:00:02 +09:00
ret['status'] = youtube_dl.status.name
if not youtube_dl.start():
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 10) # 실패
2020-07-23 23:00:02 +09:00
return jsonify(ret)
2020-02-14 00:49:47 +09:00
2020-07-23 23:00:02 +09:00
# 다운로드 중지를 요청하는 API
elif sub == 'stop':
index = request.values.get('index')
key = request.values.get('key')
2020-07-23 23:00:02 +09:00
ret = {
'errorCode': 0,
'status': None
}
if None in (index, key):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
2020-07-23 23:00:02 +09:00
index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
2020-07-23 23:00:02 +09:00
youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key:
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
2020-07-23 23:00:02 +09:00
ret['status'] = youtube_dl.status.name
if not youtube_dl.stop():
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 10) # 실패
2020-07-23 23:00:02 +09:00
return jsonify(ret)
2020-02-14 00:49:47 +09:00
2020-07-23 23:00:02 +09:00
# 현재 상태를 반환하는 API
elif sub == 'status':
index = request.values.get('index')
key = request.values.get('key')
2020-07-23 23:00:02 +09:00
ret = {
'errorCode': 0,
'status': None,
'type': None,
2020-07-23 23:00:02 +09:00
'start_time': None,
'end_time': None,
'temp_path': None,
'save_path': None
}
if None in (index, key):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
2020-07-23 23:00:02 +09:00
index = int(index)
if not (0 <= index < len(LogicNormal.youtube_dl_list)):
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 3) # 인덱스 범위를 벗어남
2020-07-23 23:00:02 +09:00
youtube_dl = LogicNormal.youtube_dl_list[index]
if youtube_dl.key != key:
2021-02-11 18:08:47 +09:00
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
2020-07-23 23:00:02 +09:00
ret['status'] = youtube_dl.status.name
ret['type'] = youtube_dl.type
2021-02-11 18:08:47 +09:00
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
2020-07-23 23:00:02 +09:00
ret['temp_path'] = youtube_dl.temp_path
ret['save_path'] = youtube_dl.save_path
return jsonify(ret)
except Exception as e:
logger.error('Exception:%s', e)
logger.error(traceback.format_exc())
abort(500) # 500 에러(서버 오류)
2021-02-11 18:08:47 +09:00
abort(404) # 404 에러(페이지 없음)
2020-03-01 01:34:59 +09:00
#########################################################
# socketio
#########################################################
2020-03-19 00:32:24 +09:00
def socketio_emit(cmd, data):
2020-07-23 23:00:02 +09:00
socketio.emit(cmd, LogicNormal.get_data(data), namespace='/%s' % package_name, broadcast=True)