update
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -142,4 +142,6 @@ pre_start.sh
|
||||
false
|
||||
*copy.py
|
||||
*.sh
|
||||
data/
|
||||
data/
|
||||
lib/support/site/tving.py
|
||||
lib/support/site/wavve.py
|
||||
|
||||
@@ -388,3 +388,11 @@ class PluginManager:
|
||||
except Exception as exception:
|
||||
F.logger.error('Exception:%s', exception)
|
||||
F.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_plugin_instance(cls, package_name):
|
||||
try:
|
||||
return cls.all_package_list[package_name]['P']
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -110,8 +110,9 @@ function showModal(data='EMPTY', title='JSON', json=true) {
|
||||
document.getElementById("modal_title").innerHTML = title;
|
||||
if (json) {
|
||||
data = JSON.stringify(data, null, 2);
|
||||
}
|
||||
document.getElementById("modal_body").innerHTML = "<pre>"+ data + "</pre>";;
|
||||
}
|
||||
document.getElementById("modal_body").innerHTML = '<pre style="white-space: pre-wrap;">' +data + '</pre>';
|
||||
|
||||
$("#large_modal").modal();
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,8 @@ function globalSendCommand(command, arg1, arg2, arg3, modal_title, callback) {
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.msg != null) notify(ret.msg, ret.ret);
|
||||
if (ret.modal != null) m_modal(ret.modal, modal_title, false);
|
||||
if (ret.json != null) m_modal(ret.json, modal_title, true);
|
||||
if (ret.modal != null) showModal(ret.modal, modal_title, false);
|
||||
if (ret.json != null) showModal(ret.json, modal_title, true);
|
||||
if (callback != null) callback(ret);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
<div class="d-inline-block"></div>
|
||||
|
||||
*/
|
||||
function j_button_group(h) {
|
||||
var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">';
|
||||
str += h
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro m_tab_head2(name, title, active) %}
|
||||
{% macro m_tab_head(name, title, active) %}
|
||||
{% if active %}
|
||||
<a class="nav-item nav-link active" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{title}}</a>
|
||||
{% else %}
|
||||
@@ -176,21 +176,26 @@
|
||||
{% macro setting_input_textarea_and_buttons(id, left, buttons, value='', col='9', row='3', desc='', disabled=False) %}
|
||||
{{ setting_top(left) }}
|
||||
<div class="input-group col-sm-{{col}}">
|
||||
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
||||
{% if disabled %}
|
||||
disabled
|
||||
{% endif %}
|
||||
>{{ value }}</textarea>
|
||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group" style="padding-left:5px; padding-top:0px">
|
||||
{% for b in buttons %}
|
||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
|
||||
{% if b|length > 2 %}
|
||||
{% for d in b[2] %}
|
||||
data-{{d[0]}}="{{d[1]}}""
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>{{b[1]}}</button>
|
||||
{% endfor %}
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
||||
{% if disabled %}
|
||||
disabled
|
||||
{% endif %}
|
||||
>{{ value }}</textarea>
|
||||
</div>
|
||||
<div class="row" style="padding:5px;"></div>
|
||||
<div class="row">
|
||||
{% for b in buttons %}
|
||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
|
||||
{% if b|length > 2 %}
|
||||
{% for d in b[2] %}
|
||||
data-{{d[0]}}="{{d[1]}}""
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>{{b[1]}}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ setting_bottom(desc) }}
|
||||
@@ -370,13 +375,6 @@ option을 script로 넣을 때 사용
|
||||
|
||||
|
||||
<!-- 삭제해야함 --------------------------------------------------------->
|
||||
{% macro m_tab_head(name, active) %}
|
||||
{% if active %}
|
||||
<a class="nav-item nav-link active" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{name}}</a>
|
||||
{% else %}
|
||||
<a class="nav-item nav-link" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{name}}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro setting_radio(id, title, radios, value=None, desc=None, disabled=False) %}
|
||||
{{ setting_top(title) }}
|
||||
|
||||
@@ -20,22 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="normal_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="normal_modal_title"></h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="normal_modal_body" style="word-break:break-all;">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
|
||||
<!--<button type="button" class="btn btn-primary">Save changes</button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="confirm_modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@@ -92,27 +77,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 예고편 Player Modal: START -->
|
||||
<div class="modal fade" id="video_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="video_modal_title" style="word-break:break-all;">예고편 재생: </h4>
|
||||
</div>
|
||||
<div class="modal-body" id="modal_body" style="word-break:break-all;">
|
||||
<span id="video_player_body"></span>
|
||||
</div>
|
||||
<!--</div>-->
|
||||
<div class="modal-footer">
|
||||
<button type="button" id='video_close_btn' class="btn btn-default" data-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 예고편 Player Modal: END -->
|
||||
|
||||
|
||||
<!--command modal-->
|
||||
{{ macros.m_modal_start('command_modal', '', 'modal-lg') }}
|
||||
<div>
|
||||
|
||||
@@ -38,20 +38,6 @@ class Util(object):
|
||||
F.logger.debug('Exception:%s', exception)
|
||||
F.logger.debug(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def save_from_dict_to_json(d, filename):
|
||||
from tool_base import ToolUtil
|
||||
ToolUtil.save_dict(d, filename)
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def change_text_for_use_filename(text):
|
||||
from tool_base import ToolBaseFile
|
||||
return ToolBaseFile.text_for_filename(text)
|
||||
|
||||
|
||||
# 토렌트 인포에서 최대 크기 파일과 폴더명을 리턴한다
|
||||
@staticmethod
|
||||
|
||||
@@ -19,6 +19,7 @@ class PluginBase(object):
|
||||
logic = None
|
||||
module_list = None
|
||||
home_module = None
|
||||
vars = []
|
||||
|
||||
def __init__(self, setting):
|
||||
try:
|
||||
|
||||
@@ -55,8 +55,8 @@ def default_route(P):
|
||||
try:
|
||||
plugin_root = os.path.dirname(P.blueprint.template_folder)
|
||||
filepath = os.path.join(plugin_root, *path.split('/'))
|
||||
from tool_base import ToolBaseFile
|
||||
data = ToolBaseFile.read(filepath)
|
||||
from support import SupportFile
|
||||
data = SupportFile.read_file(filepath)
|
||||
return render_template('manual.html', data=data)
|
||||
except Exception as exception:
|
||||
P.logger.error('Exception:%s', exception)
|
||||
@@ -321,16 +321,18 @@ def default_route_single_module(P):
|
||||
|
||||
|
||||
|
||||
|
||||
def default_route_socketio_module(module):
|
||||
# 웹은 페이지
|
||||
# 파이썬은 모듈 단위일 떄 attach 사용
|
||||
# var socket11 = io.connect(window.location.href);
|
||||
def default_route_socketio_module(module, attach=''):
|
||||
P = module.P
|
||||
if module.socketio_list is None:
|
||||
module.socketio_list = []
|
||||
|
||||
@F.socketio.on('connect', namespace=f'/{P.package_name}/{module.name}')
|
||||
@F.socketio.on('connect', namespace=f'/{P.package_name}/{module.name}{attach}')
|
||||
def connect():
|
||||
try:
|
||||
P.logger.debug(f'socket_connect : {P.package_name} - {module.name}')
|
||||
P.logger.debug(f'socket_connect : {P.package_name} - {module.name}{attach}')
|
||||
module.socketio_list.append(request.sid)
|
||||
socketio_callback('start', '')
|
||||
module.socketio_connect()
|
||||
@@ -339,10 +341,10 @@ def default_route_socketio_module(module):
|
||||
P.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@F.socketio.on('disconnect', namespace='/{package_name}/{sub}'.format(package_name=P.package_name, sub=module.name))
|
||||
@F.socketio.on('disconnect', namespace=f'/{P.package_name}/{module.name}{attach}')
|
||||
def disconnect():
|
||||
try:
|
||||
P.logger.debug('socket_disconnect : %s - %s', P.package_name, module.name)
|
||||
P.logger.debug(f'socket_disconnect : {P.package_name} - {module.name}{attach}')
|
||||
module.socketio_list.remove(request.sid)
|
||||
module.socketio_disconnect()
|
||||
except Exception as exception:
|
||||
@@ -355,7 +357,7 @@ def default_route_socketio_module(module):
|
||||
if encoding:
|
||||
data = json.dumps(data, cls=AlchemyEncoder)
|
||||
data = json.loads(data)
|
||||
F.socketio.emit(cmd, data, namespace='/{package_name}/{sub}'.format(package_name=P.package_name, sub=module.name), broadcast=True)
|
||||
F.socketio.emit(cmd, data, namespace=f'/{P.package_name}/{module.name}{attach}', broadcast=True)
|
||||
|
||||
module.socketio_callback = socketio_callback
|
||||
|
||||
|
||||
@@ -53,19 +53,6 @@ class SupportFile(object):
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def text_for_filename(cls, text):
|
||||
#text = text.replace('/', '')
|
||||
@@ -94,6 +81,83 @@ class SupportFile(object):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -19,6 +19,19 @@ def demote(user_uid, user_gid):
|
||||
|
||||
class SupportSubprocess(object):
|
||||
|
||||
@classmethod
|
||||
def command_for_windows(cls, command: list) -> str or list:
|
||||
if platform.system() == 'Windows':
|
||||
tmp = []
|
||||
if type(command) == type([]):
|
||||
for x in command:
|
||||
if x.find(' ') == -1:
|
||||
tmp.append(x)
|
||||
else:
|
||||
tmp.append(f'"{x}"')
|
||||
command = ' '.join(tmp)
|
||||
return command
|
||||
|
||||
# 2021-10-25
|
||||
# timeout 적용
|
||||
@classmethod
|
||||
@@ -26,15 +39,7 @@ class SupportSubprocess(object):
|
||||
|
||||
try:
|
||||
logger.debug(f"execute_command_return : {' '.join(command)}")
|
||||
if platform.system() == 'Windows':
|
||||
tmp = []
|
||||
if type(command) == type([]):
|
||||
for x in command:
|
||||
if x.find(' ') == -1:
|
||||
tmp.append(x)
|
||||
else:
|
||||
tmp.append(f'"{x}"')
|
||||
command = ' '.join(tmp)
|
||||
command = cls.command_for_windows(command)
|
||||
|
||||
iter_arg = ''
|
||||
if platform.system() == 'Windows':
|
||||
@@ -119,16 +124,7 @@ class SupportSubprocess(object):
|
||||
|
||||
def __execute_thread_function(self):
|
||||
try:
|
||||
if platform.system() == 'Windows':
|
||||
tmp = []
|
||||
if type(self.command) == type([]):
|
||||
for x in self.command:
|
||||
if x.find(' ') == -1:
|
||||
tmp.append(x)
|
||||
else:
|
||||
tmp.append(f'"{x}"')
|
||||
self.command = ' '.join(tmp)
|
||||
|
||||
self.command = self.command_for_windows(self.command)
|
||||
logger.debug(f"{self.command=}")
|
||||
if platform.system() == 'Windows':
|
||||
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||
@@ -141,7 +137,11 @@ class SupportSubprocess(object):
|
||||
self.__start_communicate()
|
||||
self.__start_send_callback()
|
||||
if self.process is not None:
|
||||
self.process.wait()
|
||||
if self.timeout != None:
|
||||
self.process.wait(timeout=self.timeout)
|
||||
self.process_close()
|
||||
else:
|
||||
self.process.wait()
|
||||
logger.info(f"{self.command} END")
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
|
||||
0
lib/support/expand/__init__.py
Normal file
0
lib/support/expand/__init__.py
Normal file
425
lib/support/expand/ffmpeg.py
Normal file
425
lib/support/expand/ffmpeg.py
Normal file
@@ -0,0 +1,425 @@
|
||||
import enum
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from support import SupportSubprocess, SupportUtil, logger
|
||||
|
||||
|
||||
class SupportFfmpeg(object):
|
||||
__instance_list = []
|
||||
__ffmpeg_path = None
|
||||
|
||||
__idx = 1
|
||||
total_callback_function = None
|
||||
temp_path = None
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, __ffmpeg_path, temp_path, total_callback_function, max_pf_count=-1):
|
||||
cls.__ffmpeg_path = __ffmpeg_path
|
||||
cls.temp_path = temp_path
|
||||
cls.total_callback_function = total_callback_function
|
||||
cls.max_pf_count = max_pf_count
|
||||
|
||||
# retry : 재시도 횟수
|
||||
# max_error_packet_count : 이 숫자 초과시 중단
|
||||
# where : 호출 모듈
|
||||
def __init__(self, url, filename, save_path=None, max_pf_count=None, headers=None, timeout_minute=60, proxy=None, callback_id=None, callback_function=None):
|
||||
self.__idx = str(SupportFfmpeg.__idx)
|
||||
SupportFfmpeg.__idx += 1
|
||||
|
||||
self.url = url
|
||||
self.filename = filename
|
||||
self.save_path = save_path
|
||||
self.max_pf_count = max_pf_count
|
||||
self.headers = headers
|
||||
self.timeout_minute = int(timeout_minute)
|
||||
self.proxy = proxy
|
||||
self.callback_id = callback_id
|
||||
if callback_id == None:
|
||||
self.callback_id = str(self.__idx)
|
||||
self.callback_function = callback_function
|
||||
|
||||
self.temp_fullpath = os.path.join(self.temp_path, filename)
|
||||
self.save_fullpath = os.path.join(self.save_path, filename)
|
||||
self.thread = None
|
||||
self.process = None
|
||||
self.log_thread = None
|
||||
self.status = SupportFfmpeg.Status.READY
|
||||
self.duration = 0
|
||||
self.duration_str = ''
|
||||
self.current_duration = 0
|
||||
self.percent = 0
|
||||
#self.log = []
|
||||
self.current_pf_count = 0
|
||||
self.current_bitrate = ''
|
||||
self.current_speed = ''
|
||||
self.start_time = None
|
||||
self.end_time = None
|
||||
self.download_time = None
|
||||
self.start_event = threading.Event()
|
||||
self.exist = False
|
||||
self.filesize = 0
|
||||
self.filesize_str = ''
|
||||
self.download_speed = ''
|
||||
|
||||
|
||||
SupportFfmpeg.__instance_list.append(self)
|
||||
if len(SupportFfmpeg.__instance_list) > 30:
|
||||
for instance in SupportFfmpeg.__instance_list:
|
||||
if instance.thread is None and instance.status != SupportFfmpeg.Status.READY:
|
||||
SupportFfmpeg.__instance_list.remove(instance)
|
||||
break
|
||||
else:
|
||||
logger.debug('remove fail %s %s', instance.thread, self.status)
|
||||
|
||||
def start(self):
|
||||
self.thread = threading.Thread(target=self.thread_fuction, args=())
|
||||
self.thread.start()
|
||||
self.start_time = datetime.now()
|
||||
return self.get_data()
|
||||
|
||||
def start_and_wait(self):
|
||||
self.start()
|
||||
self.thread.join(timeout=60*70)
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
self.status = SupportFfmpeg.Status.USER_STOP
|
||||
self.kill()
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def kill(self):
|
||||
try:
|
||||
if self.process is not None and self.process.poll() is None:
|
||||
import psutil
|
||||
process = psutil.Process(self.process.pid)
|
||||
for proc in process.children(recursive=True):
|
||||
proc.kill()
|
||||
process.kill()
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def thread_fuction(self):
|
||||
try:
|
||||
if self.proxy is None:
|
||||
if self.headers is None:
|
||||
command = [self.__ffmpeg_path, '-y', '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
headers_command = []
|
||||
for key, value in self.headers.items():
|
||||
if key.lower() == 'user-agent':
|
||||
headers_command.append('-user_agent')
|
||||
headers_command.append(value)
|
||||
pass
|
||||
else:
|
||||
headers_command.append('-headers')
|
||||
if platform.system() == 'Windows':
|
||||
headers_command.append('\'%s:%s\''%(key,value))
|
||||
else:
|
||||
headers_command.append(f'{key}:{value}')
|
||||
command = [self.__ffmpeg_path, '-y'] + headers_command + ['-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
else:
|
||||
command = [self.__ffmpeg_path, '-y', '-http_proxy', self.proxy, '-i', self.url, '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
now = str(datetime.now()).replace(':', '').replace('-', '').replace(' ', '-')
|
||||
filename = ('%s' % now) + '.mp4'
|
||||
self.temp_fullpath = os.path.join(self.temp_path, filename)
|
||||
command.append(self.temp_fullpath)
|
||||
else:
|
||||
command.append(self.temp_fullpath)
|
||||
|
||||
try:
|
||||
logger.debug(' '.join(command))
|
||||
if os.path.exists(self.temp_fullpath):
|
||||
for f in SupportFfmpeg.__instance_list:
|
||||
if f.__idx != self.__idx and f.temp_fullpath == self.temp_fullpath and f.status in [SupportFfmpeg.Status.DOWNLOADING, SupportFfmpeg.Status.READY]:
|
||||
self.status = SupportFfmpeg.Status.ALREADY_DOWNLOADING
|
||||
return
|
||||
except:
|
||||
pass
|
||||
logger.error(' '.join(command))
|
||||
command = SupportSubprocess.command_for_windows(command)
|
||||
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf8')
|
||||
|
||||
self.status = SupportFfmpeg.Status.READY
|
||||
self.log_thread = threading.Thread(target=self.log_thread_fuction, args=())
|
||||
self.log_thread.start()
|
||||
self.start_event.wait(timeout=60)
|
||||
if self.log_thread is None:
|
||||
if self.status == SupportFfmpeg.Status.READY:
|
||||
self.status = SupportFfmpeg.Status.ERROR
|
||||
self.kill()
|
||||
elif self.status == SupportFfmpeg.Status.READY:
|
||||
self.status = SupportFfmpeg.Status.ERROR
|
||||
self.kill()
|
||||
else:
|
||||
process_ret = self.process.wait(timeout=60*self.timeout_minute)
|
||||
if process_ret is None: # timeout
|
||||
if self.status != SupportFfmpeg.Status.COMPLETED and self.status != SupportFfmpeg.Status.USER_STOP and self.status != SupportFfmpeg.Status.PF_STOP:
|
||||
self.status = SupportFfmpeg.Status.TIME_OVER
|
||||
self.kill()
|
||||
else:
|
||||
if self.status == SupportFfmpeg.Status.DOWNLOADING:
|
||||
self.status = SupportFfmpeg.Status.FORCE_STOP
|
||||
self.end_time = datetime.now()
|
||||
self.download_time = self.end_time - self.start_time
|
||||
try:
|
||||
if self.status == SupportFfmpeg.Status.COMPLETED:
|
||||
if self.save_fullpath != self.temp_fullpath:
|
||||
if os.path.exists(self.save_fullpath):
|
||||
os.remove(self.save_fullpath)
|
||||
if platform.system() != 'Windows':
|
||||
os.system('chmod 777 "%s"' % self.temp_fullpath)
|
||||
shutil.move(self.temp_fullpath, self.save_fullpath)
|
||||
self.filesize = os.stat(self.save_fullpath).st_size
|
||||
else:
|
||||
if os.path.exists(self.temp_fullpath):
|
||||
os.remove(self.temp_fullpath)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
arg = {'type':'last', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
self.process = None
|
||||
self.thread = None
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
try:
|
||||
self.status = SupportFfmpeg.Status.EXCEPTION
|
||||
arg = {'type':'last', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def log_thread_fuction(self):
|
||||
with self.process.stdout:
|
||||
for line in iter(self.process.stdout.readline, ''):
|
||||
line = line.strip()
|
||||
#logger.error(line)
|
||||
try:
|
||||
if self.status == SupportFfmpeg.Status.READY:
|
||||
if line.find('Server returned 404 Not Found') != -1 or line.find('Unknown error') != -1:
|
||||
self.status = SupportFfmpeg.Status.WRONG_URL
|
||||
self.start_event.set()
|
||||
elif line.find('No such file or directory') != -1:
|
||||
self.status = SupportFfmpeg.Status.WRONG_DIRECTORY
|
||||
self.start_event.set()
|
||||
else:
|
||||
match = re.compile(r'Duration\:\s(\d{2})\:(\d{2})\:(\d{2})\.(\d{2})\,\sstart').search(line)
|
||||
if match:
|
||||
self.duration_str = '%s:%s:%s' % ( match.group(1), match.group(2), match.group(3))
|
||||
self.duration = int(match.group(4))
|
||||
self.duration += int(match.group(3)) * 100
|
||||
self.duration += int(match.group(2)) * 100 * 60
|
||||
self.duration += int(match.group(1)) * 100 * 60 * 60
|
||||
if match:
|
||||
self.status = SupportFfmpeg.Status.READY
|
||||
arg = {'type':'status_change', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
continue
|
||||
match = re.compile(r'time\=(\d{2})\:(\d{2})\:(\d{2})\.(\d{2})\sbitrate\=\s*(?P<bitrate>\d+).*?[$|\s](\s?speed\=\s*(?P<speed>.*?)x)?').search(line)
|
||||
if match:
|
||||
self.status = SupportFfmpeg.Status.DOWNLOADING
|
||||
arg = {'type':'status_change', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
self.start_event.set()
|
||||
elif self.status == SupportFfmpeg.Status.DOWNLOADING:
|
||||
if line.find('PES packet size mismatch') != -1:
|
||||
self.current_pf_count += 1
|
||||
if self.current_pf_count > self.max_pf_count:
|
||||
self.status = SupportFfmpeg.Status.PF_STOP
|
||||
self.kill()
|
||||
continue
|
||||
if line.find('HTTP error 403 Forbidden') != -1:
|
||||
self.status = SupportFfmpeg.Status.HTTP_FORBIDDEN
|
||||
self.kill()
|
||||
continue
|
||||
match = re.compile(r'time\=(\d{2})\:(\d{2})\:(\d{2})\.(\d{2})\sbitrate\=\s*(?P<bitrate>\d+).*?[$|\s](\s?speed\=\s*(?P<speed>.*?)x)?').search(line)
|
||||
if match:
|
||||
self.current_duration = int(match.group(4))
|
||||
self.current_duration += int(match.group(3)) * 100
|
||||
self.current_duration += int(match.group(2)) * 100 * 60
|
||||
self.current_duration += int(match.group(1)) * 100 * 60 * 60
|
||||
try:
|
||||
self.percent = int(self.current_duration * 100 / self.duration)
|
||||
except: pass
|
||||
self.current_bitrate = match.group('bitrate')
|
||||
self.current_speed = match.group('speed')
|
||||
self.download_time = datetime.now() - self.start_time
|
||||
arg = {'type':'normal', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
continue
|
||||
match = re.compile(r'video\:\d*kB\saudio\:\d*kB').search(line)
|
||||
if match:
|
||||
self.status = SupportFfmpeg.Status.COMPLETED
|
||||
self.end_time = datetime.now()
|
||||
self.download_time = self.end_time - self.start_time
|
||||
self.percent = 100
|
||||
arg = {'type':'status_change', 'status':self.status, 'data' : self.get_data()}
|
||||
self.send_to_listener(**arg)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
self.start_event.set()
|
||||
self.log_thread = None
|
||||
|
||||
|
||||
def get_data(self):
|
||||
data = {
|
||||
'url' : self.url,
|
||||
'filename' : self.filename,
|
||||
'max_pf_count' : self.max_pf_count,
|
||||
'callback_id' : self.callback_id,
|
||||
'temp_path' : self.temp_path,
|
||||
'save_path' : self.save_path,
|
||||
'temp_fullpath' : self.temp_fullpath,
|
||||
'save_fullpath' : self.save_fullpath,
|
||||
'status' : int(self.status),
|
||||
'status_str' : self.status.name,
|
||||
'status_kor' : str(self.status),
|
||||
'duration' : self.duration,
|
||||
'duration_str' : self.duration_str,
|
||||
'current_duration' : self.current_duration,
|
||||
'percent' : self.percent,
|
||||
'current_pf_count' : self.current_pf_count,
|
||||
'idx' : self.__idx,
|
||||
#'log' : self.log,
|
||||
'current_bitrate' : self.current_bitrate,
|
||||
'current_speed' : self.current_speed,
|
||||
'start_time' : '' if self.start_time is None else str(self.start_time).split('.')[0][5:],
|
||||
'end_time' : '' if self.end_time is None else str(self.end_time).split('.')[0][5:],
|
||||
'download_time' : '' if self.download_time is None else '%02d:%02d' % (self.download_time.seconds/60, self.download_time.seconds%60),
|
||||
'exist' : os.path.exists(self.save_fullpath),
|
||||
}
|
||||
if self.status == SupportFfmpeg.Status.COMPLETED:
|
||||
data['filesize'] = self.filesize
|
||||
data['filesize_str'] = SupportUtil.sizeof_fmt(self.filesize)
|
||||
if self.download_time.seconds != 0:
|
||||
data['download_speed'] = SupportUtil.sizeof_fmt(self.filesize/self.download_time.seconds, suffix='Bytes/Second')
|
||||
else:
|
||||
data['download_speed'] = '0Bytes/Second'
|
||||
return data
|
||||
|
||||
def send_to_listener(self, **arg):
|
||||
if self.total_callback_function != None:
|
||||
self.total_callback_function(**arg)
|
||||
if self.callback_function is not None:
|
||||
arg['callback_id'] = self.callback_id
|
||||
self.callback_function(**arg)
|
||||
|
||||
|
||||
@classmethod
|
||||
def stop_by_idx(cls, idx):
|
||||
try:
|
||||
for __instance in SupportFfmpeg.__instance_list:
|
||||
if __instance.__idx == idx:
|
||||
__instance.stop()
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
def get_instance_by_idx(cls, idx):
|
||||
try:
|
||||
for __instance in SupportFfmpeg.__instance_list:
|
||||
if __instance.__idx == idx:
|
||||
return __instance
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_instance_by_callback_id(cls, callback_id):
|
||||
try:
|
||||
for __instance in SupportFfmpeg.__instance_list:
|
||||
if __instance.callback_id == callback_id:
|
||||
return __instance
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
def all_stop(cls):
|
||||
try:
|
||||
for __instance in SupportFfmpeg.__instance_list:
|
||||
__instance.stop()
|
||||
except Exception as e:
|
||||
logger.error(f'Exception:{str(e)}')
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
def get_list(cls):
|
||||
return cls.__instance_list
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Status(enum.Enum):
|
||||
READY = 0
|
||||
WRONG_URL = 1
|
||||
WRONG_DIRECTORY = 2
|
||||
EXCEPTION = 3
|
||||
ERROR = 4
|
||||
HTTP_FORBIDDEN = 11
|
||||
|
||||
DOWNLOADING = 5
|
||||
|
||||
USER_STOP = 6
|
||||
COMPLETED = 7
|
||||
TIME_OVER = 8
|
||||
PF_STOP = 9
|
||||
FORCE_STOP = 10 #강제중단
|
||||
ALREADY_DOWNLOADING = 12 #이미 목록에 있고 다운로드중
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
def __str__(self):
|
||||
kor = ['준비', 'URL에러', '폴더에러', '실패(Exception)', '실패(에러)', '다운로드중', '사용자중지', '완료', '시간초과', 'PF중지', '강제중지',
|
||||
'403에러', '임시파일이 이미 있음']
|
||||
return kor[int(self)]
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def get_instance(value):
|
||||
tmp = [
|
||||
SupportFfmpeg.Status.READY,
|
||||
SupportFfmpeg.Status.WRONG_URL,
|
||||
SupportFfmpeg.Status.WRONG_DIRECTORY,
|
||||
SupportFfmpeg.Status.EXCEPTION,
|
||||
SupportFfmpeg.Status.ERROR,
|
||||
SupportFfmpeg.Status.DOWNLOADING,
|
||||
SupportFfmpeg.Status.USER_STOP,
|
||||
SupportFfmpeg.Status.COMPLETED,
|
||||
SupportFfmpeg.Status.TIME_OVER,
|
||||
SupportFfmpeg.Status.PF_STOP,
|
||||
SupportFfmpeg.Status.FORCE_STOP,
|
||||
SupportFfmpeg.Status.HTTP_FORBIDDEN,
|
||||
SupportFfmpeg.Status.ALREADY_DOWNLOADING ]
|
||||
return tmp[value]
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import os, sys, traceback
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import oauth2client
|
||||
except:
|
||||
os.system('pip install oauth2client')
|
||||
import oauth2client
|
||||
|
||||
|
||||
from oauth2client.file import Storage
|
||||
from oauth2client import tools
|
||||
from oauth2client.client import flow_from_clientsecrets
|
||||
from oauth2client.file import Storage
|
||||
|
||||
try:
|
||||
from apiclient.discovery import build
|
||||
@@ -17,14 +19,18 @@ except:
|
||||
from apiclient.discovery import build
|
||||
|
||||
try:
|
||||
import gspread, time
|
||||
from gspread_formatting import cellFormat, textFormat, color, format_cell_range
|
||||
import time
|
||||
|
||||
import gspread
|
||||
from gspread_formatting import (cellFormat, color, format_cell_range,
|
||||
textFormat)
|
||||
except:
|
||||
os.system('pip3 install gspread')
|
||||
os.system('pip3 install gspread_formatting')
|
||||
import gspread, time
|
||||
from gspread_formatting import cellFormat, textFormat, color, format_cell_range
|
||||
from support.base import get_logger, d
|
||||
|
||||
from support import d, get_logger
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
@@ -72,9 +72,7 @@ def get_logger(name=None, log_path=None):
|
||||
file_max_bytes = 1 * 1024 * 1024
|
||||
if log_path == None:
|
||||
log_path = os.path.join(os.getcwd(), 'tmp')
|
||||
#os.makedirs(log_path, exist_ok=True)
|
||||
else:
|
||||
os.makedirs(log_path, exist_ok=True)
|
||||
os.makedirs(log_path, exist_ok=True)
|
||||
fileHandler = logging.handlers.RotatingFileHandler(filename=os.path.join(log_path, f'{name}.log'), maxBytes=file_max_bytes, backupCount=5, encoding='utf8', delay=True)
|
||||
streamHandler = logging.StreamHandler()
|
||||
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import os, sys, traceback, time, urllib.parse, requests, json, base64, re, platform
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import urllib.parse
|
||||
|
||||
import requests
|
||||
|
||||
if __name__ == '__main__':
|
||||
if platform.system() == 'Windows':
|
||||
sys.path += ["C:\SJVA3\lib2", "C:\SJVA3\data\custom", "C:\SJVA3_DEV"]
|
||||
@@ -7,7 +18,6 @@ if __name__ == '__main__':
|
||||
|
||||
from support import d, logger
|
||||
|
||||
|
||||
apikey = '1e7952d0917d6aab1f0293a063697610'
|
||||
#apikey = '95a64ebcd8e154aeb96928bf34848826'
|
||||
|
||||
@@ -352,7 +362,7 @@ class SupportTving:
|
||||
ret = f"{title}.{qualityRes}-ST.mp4"
|
||||
#if episode_data['drm']:
|
||||
# ret = ret.replace('.mp4', '.mkv')
|
||||
from support.base import SupportFile
|
||||
from support import SupportFile
|
||||
return SupportFile.text_for_filename(ret)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception:{str(e)}")
|
||||
@@ -411,6 +421,7 @@ class SupportTving:
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
#from support.base import d, get_logger
|
||||
from lib_wvtool import WVDownloader
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .gsheet_base import GoogleSheetBase
|
||||
@@ -30,15 +30,6 @@ class ToolBaseFile(object):
|
||||
return False
|
||||
|
||||
|
||||
@classmethod
|
||||
def text_for_filename(cls, text):
|
||||
#text = text.replace('/', '')
|
||||
# 2021-07-31 X:X
|
||||
#text = text.replace(':', ' ')
|
||||
text = re.sub('[\\/:*?\"<>|]', ' ', text).strip()
|
||||
text = re.sub("\s{2,}", ' ', text)
|
||||
return text
|
||||
|
||||
|
||||
@classmethod
|
||||
def size(cls, start_path = '.'):
|
||||
|
||||
Reference in New Issue
Block a user