v0.1.0 최초 공개
This commit is contained in:
15
README.md
15
README.md
@@ -1,2 +1,13 @@
|
|||||||
# youtube-dl
|
# youtube-dl_sjva
|
||||||
SJVA용 youtube-dl 플러그인
|
[SJVA](https://sjva.me/) 용 [youtube-dl](https://ytdl-org.github.io/youtube-dl/) 플러그인입니다.
|
||||||
|
SJVA에서 유튜브 등 동영상 사이트 영상을 다운로드할 수 있습니다.
|
||||||
|
|
||||||
|
## 잡담
|
||||||
|
시놀로지 docker 환경에서 테스트했습니다.
|
||||||
|
|
||||||
|
다른 분들이 만든 플러그인을 참고하며 주먹구구식으로 만들었기 때문에 손볼 곳이 많습니다.
|
||||||
|
일단 어느 정도 코드가 정리되면 그때 화질 선택 등 옵션을 추가할 예정
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
v0.1.0
|
||||||
|
* 최초 공개
|
||||||
|
|||||||
2
__init__.py
Normal file
2
__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from plugin import blueprint, menu, plugin_load, plugin_unload, plugin_info
|
||||||
1
info.json
Normal file
1
info.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"more": "", "version": "0.1.0", "name": "youtube-dl", "developer": "joyfuI", "home": "https://github.com/joyfuI/youtube-dl", "description": "youtube-dl", "icon": "", "category_name": "vod"}
|
||||||
80
logic.py
Normal file
80
logic.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#########################################################
|
||||||
|
# python
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# third-party
|
||||||
|
|
||||||
|
# sjva 공용
|
||||||
|
from framework import db, path_data
|
||||||
|
from framework.util import Util
|
||||||
|
|
||||||
|
# 패키지
|
||||||
|
from .plugin import logger, package_name
|
||||||
|
from .model import ModelSetting
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
class Logic(object):
|
||||||
|
db_default = {
|
||||||
|
'temp_path': os.path.join(path_data, 'download_tmp'),
|
||||||
|
'save_path': os.path.join(path_data, 'download')
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def plugin_load():
|
||||||
|
try:
|
||||||
|
logger.debug('%s plugin_load', package_name)
|
||||||
|
# DB 초기화
|
||||||
|
Logic.db_init()
|
||||||
|
|
||||||
|
# 편의를 위해 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 e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def plugin_unload():
|
||||||
|
try:
|
||||||
|
logger.debug('%s plugin_unload', package_name)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setting_save(req):
|
||||||
|
try:
|
||||||
|
for key, value in req.form.items():
|
||||||
|
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 e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_setting_value(key):
|
||||||
|
try:
|
||||||
|
return db.session.query(ModelSetting).filter_by(key=key).first().value
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
#########################################################
|
||||||
36
model.py
Normal file
36
model.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#########################################################
|
||||||
|
# python
|
||||||
|
import os
|
||||||
|
|
||||||
|
# third-party
|
||||||
|
|
||||||
|
# sjva 공용
|
||||||
|
from framework import db, app, path_app_root
|
||||||
|
|
||||||
|
# 패키지
|
||||||
|
from .plugin import package_name
|
||||||
|
|
||||||
|
db_file = os.path.join(path_app_root, 'data', 'db', '%s.db' % package_name)
|
||||||
|
app.config['SQLALCHEMY_BINDS'][package_name] = 'sqlite:///%s' % (db_file)
|
||||||
|
|
||||||
|
class ModelSetting(db.Model):
|
||||||
|
__tablename__ = 'plugin_%s_setting' % package_name
|
||||||
|
__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 }
|
||||||
|
|
||||||
|
#########################################################
|
||||||
169
plugin.py
Normal file
169
plugin.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#########################################################
|
||||||
|
# python
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# third-party
|
||||||
|
from flask import Blueprint, request, render_template, redirect, jsonify
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
|
# sjva 공용
|
||||||
|
from framework.logger import get_logger
|
||||||
|
from framework import db
|
||||||
|
from framework.util import Util
|
||||||
|
|
||||||
|
# 로그
|
||||||
|
package_name = __name__.split('.')[0]
|
||||||
|
logger = get_logger(package_name)
|
||||||
|
|
||||||
|
# 패키지
|
||||||
|
from .logic import Logic
|
||||||
|
from .model import ModelSetting
|
||||||
|
from .youtube_dl import Youtube_dl, Status
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
blueprint = Blueprint(package_name, package_name, url_prefix='/%s' % package_name, template_folder=os.path.join(os.path.dirname(__file__), 'templates'))
|
||||||
|
|
||||||
|
def plugin_load():
|
||||||
|
Logic.plugin_load()
|
||||||
|
|
||||||
|
def plugin_unload():
|
||||||
|
Logic.plugin_unload()
|
||||||
|
|
||||||
|
plugin_info = {
|
||||||
|
'version': '0.1.0',
|
||||||
|
'name': 'youtube-dl',
|
||||||
|
'category_name': 'vod',
|
||||||
|
'icon': '',
|
||||||
|
'developer': 'joyfuI',
|
||||||
|
'description': 'youtube-dl',
|
||||||
|
'home': 'https://github.com/joyfuI/youtube-dl',
|
||||||
|
'more': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# 메뉴 구성
|
||||||
|
menu = {
|
||||||
|
'main': [package_name, 'youtube-dl'],
|
||||||
|
'sub': [
|
||||||
|
['setting', '설정'], ['download', '다운로드'], ['list', '목록'], ['log', '로그']
|
||||||
|
],
|
||||||
|
'category': 'vod'
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
youtube_dl_list = []
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
# WEB Menu
|
||||||
|
#########################################################
|
||||||
|
@blueprint.route('/')
|
||||||
|
def home():
|
||||||
|
return redirect('/%s/list' % package_name)
|
||||||
|
|
||||||
|
@blueprint.route('/<sub>')
|
||||||
|
@login_required
|
||||||
|
def detail(sub):
|
||||||
|
try:
|
||||||
|
if sub == 'setting':
|
||||||
|
setting_list = db.session.query(ModelSetting).all()
|
||||||
|
arg = Util.db_list_to_dict(setting_list)
|
||||||
|
arg['package_name'] = package_name
|
||||||
|
arg['youtube_dl_path'] = 'youtube-dl'
|
||||||
|
return render_template('%s_setting.html' % (package_name), arg=arg)
|
||||||
|
|
||||||
|
elif sub == 'download':
|
||||||
|
arg = { }
|
||||||
|
arg['package_name'] = package_name
|
||||||
|
arg['file_name'] = '%(title)s-%(id)s.%(ext)s'
|
||||||
|
return render_template('%s_download.html' % (package_name), arg=arg)
|
||||||
|
|
||||||
|
elif sub == 'list':
|
||||||
|
arg = { }
|
||||||
|
arg['package_name'] = package_name
|
||||||
|
return render_template('%s_list.html' % (package_name), arg=arg)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
# For UI
|
||||||
|
#########################################################
|
||||||
|
@blueprint.route('/ajax/<sub>', methods=['GET', 'POST'])
|
||||||
|
def ajax(sub):
|
||||||
|
logger.debug('AJAX %s %s', package_name, sub)
|
||||||
|
try:
|
||||||
|
if sub == 'setting_save':
|
||||||
|
ret = Logic.setting_save(request)
|
||||||
|
return jsonify(ret)
|
||||||
|
|
||||||
|
elif sub == 'youtube_dl_version':
|
||||||
|
ret = subprocess.check_output(['youtube-dl', '--version'])
|
||||||
|
return jsonify(ret)
|
||||||
|
|
||||||
|
elif sub == 'youtube_dl_update':
|
||||||
|
subprocess.call(['curl', '-L', 'https://yt-dl.org/downloads/latest/youtube-dl', '-o', '/usr/local/bin/youtube-dl'])
|
||||||
|
subprocess.call(['chmod', 'a+rx', '/usr/local/bin/youtube-dl'])
|
||||||
|
return jsonify([])
|
||||||
|
|
||||||
|
elif sub == 'download':
|
||||||
|
url = request.form['url']
|
||||||
|
filename = request.form['filename']
|
||||||
|
temp_path = Logic.get_setting_value('temp_path')
|
||||||
|
save_path = Logic.get_setting_value('save_path')
|
||||||
|
youtube_dl = Youtube_dl(url, filename, temp_path, save_path)
|
||||||
|
youtube_dl_list.append(youtube_dl) # 리스트 추가
|
||||||
|
youtube_dl.start()
|
||||||
|
return jsonify([])
|
||||||
|
|
||||||
|
elif sub == 'list':
|
||||||
|
ret = []
|
||||||
|
for i in youtube_dl_list:
|
||||||
|
data = { }
|
||||||
|
data['url'] = i.url
|
||||||
|
data['filename'] = i.filename
|
||||||
|
data['temp_path'] = i.temp_path
|
||||||
|
data['save_path'] = i.save_path
|
||||||
|
data['index'] = i.index
|
||||||
|
data['status_str'] = i.status.name
|
||||||
|
data['status_ko'] = str(i.status)
|
||||||
|
data['format'] = i.format
|
||||||
|
data['end_time'] = ''
|
||||||
|
if i.status == Status.READY: # 다운로드 전
|
||||||
|
data['duration_str'] = ''
|
||||||
|
data['download_time'] = ''
|
||||||
|
data['start_time'] = ''
|
||||||
|
else:
|
||||||
|
data['duration_str'] = '%02d:%02d:%02d' % (i.duration / 60 / 60, i.duration / 60 % 60, i.duration % 60)
|
||||||
|
if i.end_time == None: # 완료 전
|
||||||
|
download_time = datetime.now() - i.start_time
|
||||||
|
else:
|
||||||
|
download_time = i.end_time - i.start_time
|
||||||
|
data['end_time'] = i.end_time.strftime('%m-%d %H:%M:%S')
|
||||||
|
data['download_time'] = '%02d:%02d' % (download_time.seconds / 60, download_time.seconds % 60)
|
||||||
|
data['start_time'] = i.start_time.strftime('%m-%d %H:%M:%S')
|
||||||
|
ret.append(data)
|
||||||
|
return jsonify(ret)
|
||||||
|
|
||||||
|
elif sub == 'stop':
|
||||||
|
index = int(request.form['index'])
|
||||||
|
youtube_dl_list[index].stop()
|
||||||
|
return jsonify([])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Exception:%s', e)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
#########################################################
|
||||||
|
# API
|
||||||
|
#########################################################
|
||||||
|
@blueprint.route('/api/<sub>', methods=['GET', 'POST'])
|
||||||
|
def api(sub):
|
||||||
|
logger.debug('api %s %s', package_name, sub)
|
||||||
43
templates/youtube-dl_download.html
Normal file
43
templates/youtube-dl_download.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소', desc='유튜브, 네이버TV 등 동영상 주소') }}
|
||||||
|
{{ macros.setting_input_text('filename', '파일명', value=arg['file_name']) }}
|
||||||
|
{{ macros.setting_button([['download_start', '다운로드']]) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
var package_name = '{{ arg["package_name"] }}';
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
// 다운로드
|
||||||
|
$('#download_start').click(function (e) {
|
||||||
|
if ($('#url').val().startsWith('http') == false) {
|
||||||
|
$.notify('<strong>URL을 입력하세요.</strong>', {
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/download',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: {
|
||||||
|
url: $('#url').val(),
|
||||||
|
filename: $('#filename').val()
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (data) {
|
||||||
|
$.notify('<strong>분석중..</strong>', {
|
||||||
|
type: 'info'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
130
templates/youtube-dl_list.html
Normal file
130
templates/youtube-dl_list.html
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.row > div {
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom:3px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
align-items: center;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.row > div:nth-child(odd) {
|
||||||
|
align-items: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.row > div:nth-child(even) {
|
||||||
|
align-items: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<table id="result_table" class="table table-sm tableRowHover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:5%">IDX</th>
|
||||||
|
<th style="width:10%">시작시간</th>
|
||||||
|
<th style="width:56%">파일명</th>
|
||||||
|
<th style="width:8%">상태</th>
|
||||||
|
<th style="width:5%">길이</th>
|
||||||
|
<th style="width:8%">진행시간</th>
|
||||||
|
<th style="width:8%">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="list"></tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
$(function () {
|
||||||
|
var package_name = '{{ arg["package_name"] }}';
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/list',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: { },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (data) {
|
||||||
|
$('#list').html('');
|
||||||
|
var str = '';
|
||||||
|
for (var i in data) {
|
||||||
|
str += make_item(data[i]);
|
||||||
|
}
|
||||||
|
$('#list').html(str);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '#stop', function (e) {
|
||||||
|
var index = $(this).data('index');
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/stop',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: {
|
||||||
|
index: index
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (data) {
|
||||||
|
location.reload(); // 새로고침
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function make_item(data) {
|
||||||
|
var str = '<tr style="cursor: pointer;" data-toggle="collapse" data-target="#collapse_' + data.index + '" aria-expanded="true">';
|
||||||
|
str += '<td scope="col" style="width:5%">' + (data.index + 1) + '</td>';
|
||||||
|
str += '<td scope="col" style="width:10%">' + data.start_time + '</td>';
|
||||||
|
str += '<td scope="col" style="width:56%">' + data.filename + '</td>';
|
||||||
|
str += '<td id="status_' + data.index + '" scope="col" style="width:8%">' + data.status_ko + '</td>';
|
||||||
|
str += '<td scope="col" style="width:5%">' + data.duration_str + '</td>';
|
||||||
|
str += '<td id="download_time_' + data.index + '" scope="col" style="width:8%">' + data.download_time + '</td>';
|
||||||
|
str += '<td id="button_' + data.index + '" scope="col" style="width:8%" class="tableRowHoverOff">';
|
||||||
|
if (data.status_str == 'START') {
|
||||||
|
str += '<button id="stop" class="align-middle btn btn-outline-danger btn-sm" data-index="' + data.index + '">중지</button>';
|
||||||
|
}
|
||||||
|
str += '</td>';
|
||||||
|
str += '</tr>';
|
||||||
|
str += '<tr class="collapse tableRowHoverOff" style="cursor: pointer;" id="collapse_' + data.index + '">';
|
||||||
|
str += '<td colspan="8">';
|
||||||
|
str += '<div id="detail_' + data.index + '">';
|
||||||
|
str += get_detail(data);
|
||||||
|
str += '</div>';
|
||||||
|
str += '</td>';
|
||||||
|
str += '</tr>';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_detail(data) {
|
||||||
|
var str = info_html('URL', data.url);
|
||||||
|
str += info_html('임시경로', data.temp_path + '/' + data.filename);
|
||||||
|
str += info_html('저장경로', data.save_path + '/' + data.filename);
|
||||||
|
str += info_html('종료시간', data.end_time);
|
||||||
|
str += info_html('포맷', data.format);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function info_html(left, right) {
|
||||||
|
var str = '<div class="row">';
|
||||||
|
str += '<div class="col-sm-2">';
|
||||||
|
str += '<b>' + left + '</b>';
|
||||||
|
str += '</div>';
|
||||||
|
str += '<div class="col-sm-10">';
|
||||||
|
str += '<div class="input-group col-sm-9">';
|
||||||
|
str += '<span class="text-left" style="padding-left:10px; padding-top:3px">';
|
||||||
|
if (left == 'URL') {
|
||||||
|
str += '<a href="' + right + '" target="_blank">';
|
||||||
|
}
|
||||||
|
str += right;
|
||||||
|
if (left == 'URL') {
|
||||||
|
str += '</a>';
|
||||||
|
}
|
||||||
|
str += '</span></div></div></div>';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
80
templates/youtube-dl_setting.html
Normal file
80
templates/youtube-dl_setting.html
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ macros.setting_input_text_and_buttons('youtube_dl_path', 'youtube-dl 경로', [['youtube_dl_version', '버전확인']], value=arg['youtube_dl_path']) }}
|
||||||
|
<form id="setting">
|
||||||
|
{{ macros.setting_input_text('temp_path', '임시 폴더', value=arg['temp_path'], placeholder='임시 폴더 경로', desc='다운로드 파일이 임시로 저장될 폴더 입니다.') }}
|
||||||
|
{{ macros.setting_input_text('save_path', '저장 폴더', value=arg['save_path'], placeholder='저장 폴더 경로', desc='정상적으로 완료된 파일이 이동할 폴더 입니다.') }}
|
||||||
|
{{ macros.setting_button([['setting_save', '저장']]) }}
|
||||||
|
{{ macros.m_hr() }}
|
||||||
|
{{ macros.setting_button([['youtube_dl_update', '업데이트']], left='youtube-dl 업데이트', desc=['혹시 정상적으로 동영상 주소를 입력했는데 다운로드에 실패한다면 업데이트를 해보세요.']) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
var package_name = '{{ arg["package_name"] }}';
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
// 설정 저장
|
||||||
|
$('#setting_save').click(function (e) {
|
||||||
|
var formData = get_formdata('#setting');
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/setting_save',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: formData,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (ret) {
|
||||||
|
if (ret) {
|
||||||
|
$.notify('<strong>설정을 저장하였습니다.</strong>', {
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$.notify('<strong>설정을 저장하지 못하였습니다.</strong>', {
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 버전
|
||||||
|
$('#youtube_dl_version').click(function (e) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/youtube_dl_version',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: { },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (ret) {
|
||||||
|
$('#modal_title').html('youtube-dl --version');
|
||||||
|
$('#modal_body').html(ret);
|
||||||
|
$('#large_modal').modal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 버전
|
||||||
|
$('#youtube_dl_update').click(function (e) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/youtube_dl_update',
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: { },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (ret) {
|
||||||
|
$.notify('<strong>youtube-dl 업데이트 완료</strong>', {
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
81
youtube_dl.py
Normal file
81
youtube_dl.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# python
|
||||||
|
import os
|
||||||
|
from threading import Thread
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
# 패키지
|
||||||
|
from .plugin import logger
|
||||||
|
|
||||||
|
class Status(Enum):
|
||||||
|
READY = 0
|
||||||
|
START = 1
|
||||||
|
STOP = 2
|
||||||
|
SUCCESS = 3
|
||||||
|
FAILURE = 4
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
str_list = [
|
||||||
|
'준비',
|
||||||
|
'다운로드중',
|
||||||
|
'중지',
|
||||||
|
'완료',
|
||||||
|
'실패'
|
||||||
|
]
|
||||||
|
return str_list[self.value]
|
||||||
|
|
||||||
|
class Youtube_dl(object):
|
||||||
|
_index = 0
|
||||||
|
|
||||||
|
def __init__(self, url, filename, temp_path, save_path):
|
||||||
|
self.url = url
|
||||||
|
self.filename = filename
|
||||||
|
self.temp_path = temp_path
|
||||||
|
self.save_path = save_path
|
||||||
|
self.index = Youtube_dl._index
|
||||||
|
Youtube_dl._index += 1
|
||||||
|
self.status = Status.READY
|
||||||
|
self._thread = None
|
||||||
|
self._process = None
|
||||||
|
self.start_time = None
|
||||||
|
self.end_time = None
|
||||||
|
self.duration = None
|
||||||
|
self.format = None
|
||||||
|
self.errorlevel = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self._thread = Thread(target=self.run)
|
||||||
|
self.start_time = datetime.now()
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
command = [
|
||||||
|
'youtube-dl',
|
||||||
|
'--print-json',
|
||||||
|
'-o', self.temp_path + '/' + self.filename,
|
||||||
|
'--exec', 'mv {} ' + self.save_path + '/',
|
||||||
|
self.url
|
||||||
|
]
|
||||||
|
self._process = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) # youtube-dl 실행
|
||||||
|
data = json.loads(self._process.stdout.readline()) # 파일 정보
|
||||||
|
self.filename = data['_filename'].split('/')[-1]
|
||||||
|
self.duration = data['duration']
|
||||||
|
self.format = data['format']
|
||||||
|
self.status = Status.START
|
||||||
|
self.errorlevel = self._process.wait() # 실행 결과
|
||||||
|
self.end_time = datetime.now()
|
||||||
|
if self.errorlevel == 0: # 다운로드 성공
|
||||||
|
self.status = Status.SUCCESS
|
||||||
|
else: # 다운로드 실패
|
||||||
|
logger.debug('returncode %d', self.errorlevel)
|
||||||
|
if self.status != Status.STOP:
|
||||||
|
self.status = Status.FAILURE
|
||||||
|
logger.debug('rm -f ' + self.temp_path + '/' + ''.join(str.split('.')[:-1]) + '*')
|
||||||
|
os.system('rm -f ' + self.temp_path + '/' + ''.join(str.split('.')[:-1]) + '*') # 임시 파일 삭제
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.status = Status.STOP
|
||||||
|
self._process.terminate()
|
||||||
Reference in New Issue
Block a user