This commit is contained in:
flaskfarm
2022-10-21 01:41:49 +09:00
parent 56419a8355
commit 2e27ae8f72
20 changed files with 603 additions and 143 deletions

4
.gitignore vendored
View File

@@ -142,4 +142,6 @@ pre_start.sh
false false
*copy.py *copy.py
*.sh *.sh
data/ data/
lib/support/site/tving.py
lib/support/site/wavve.py

View File

@@ -388,3 +388,11 @@ class PluginManager:
except Exception as exception: except Exception as exception:
F.logger.error('Exception:%s', exception) F.logger.error('Exception:%s', exception)
F.logger.error(traceback.format_exc()) 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

View File

@@ -110,8 +110,9 @@ function showModal(data='EMPTY', title='JSON', json=true) {
document.getElementById("modal_title").innerHTML = title; document.getElementById("modal_title").innerHTML = title;
if (json) { if (json) {
data = JSON.stringify(data, null, 2); 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(); $("#large_modal").modal();
} }

View File

@@ -146,8 +146,8 @@ function globalSendCommand(command, arg1, arg2, arg3, modal_title, callback) {
dataType: "json", dataType: "json",
success: function (ret) { success: function (ret) {
if (ret.msg != null) notify(ret.msg, ret.ret); if (ret.msg != null) notify(ret.msg, ret.ret);
if (ret.modal != null) m_modal(ret.modal, modal_title, false); if (ret.modal != null) showModal(ret.modal, modal_title, false);
if (ret.json != null) m_modal(ret.json, modal_title, true); if (ret.json != null) showModal(ret.json, modal_title, true);
if (callback != null) callback(ret); if (callback != null) callback(ret);
} }
}); });

View File

@@ -1,3 +1,7 @@
/*
<div class="d-inline-block"></div>
*/
function j_button_group(h) { function j_button_group(h) {
var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">'; var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">';
str += h str += h

View File

@@ -36,7 +36,7 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro m_tab_head2(name, title, active) %} {% macro m_tab_head(name, title, active) %}
{% if active %} {% if active %}
<a class="nav-item nav-link active" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{title}}</a> <a class="nav-item nav-link active" id="tab_{{name}}" data-toggle="tab" href="#{{name}}" role="tab">{{title}}</a>
{% else %} {% else %}
@@ -176,21 +176,26 @@
{% macro setting_input_textarea_and_buttons(id, left, buttons, value='', col='9', row='3', desc='', disabled=False) %} {% macro setting_input_textarea_and_buttons(id, left, buttons, value='', col='9', row='3', desc='', disabled=False) %}
{{ setting_top(left) }} {{ setting_top(left) }}
<div class="input-group col-sm-{{col}}"> <div class="input-group col-sm-{{col}}">
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}" <div class="col-md-12">
{% if disabled %} <div class="row">
disabled <textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
{% endif %} {% if disabled %}
>{{ value }}</textarea> disabled
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group" style="padding-left:5px; padding-top:0px"> {% endif %}
{% for b in buttons %} >{{ value }}</textarea>
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary" </div>
{% if b|length > 2 %} <div class="row" style="padding:5px;"></div>
{% for d in b[2] %} <div class="row">
data-{{d[0]}}="{{d[1]}}"" {% for b in buttons %}
{% endfor %} <button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
{% endif %} {% if b|length > 2 %}
>{{b[1]}}</button> {% for d in b[2] %}
{% endfor %} data-{{d[0]}}="{{d[1]}}""
{% endfor %}
{% endif %}
>{{b[1]}}</button>
{% endfor %}
</div>
</div> </div>
</div> </div>
{{ setting_bottom(desc) }} {{ 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) %} {% macro setting_radio(id, title, radios, value=None, desc=None, disabled=False) %}
{{ setting_top(title) }} {{ setting_top(title) }}

View File

@@ -20,22 +20,7 @@
</div> </div>
</div> </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">&times;</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 id="confirm_modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@@ -92,27 +77,6 @@
</div> </div>
</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--> <!--command modal-->
{{ macros.m_modal_start('command_modal', '', 'modal-lg') }} {{ macros.m_modal_start('command_modal', '', 'modal-lg') }}
<div> <div>

View File

@@ -38,20 +38,6 @@ class Util(object):
F.logger.debug('Exception:%s', exception) F.logger.debug('Exception:%s', exception)
F.logger.debug(traceback.format_exc()) 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 @staticmethod

View File

@@ -19,6 +19,7 @@ class PluginBase(object):
logic = None logic = None
module_list = None module_list = None
home_module = None home_module = None
vars = []
def __init__(self, setting): def __init__(self, setting):
try: try:

View File

@@ -55,8 +55,8 @@ def default_route(P):
try: try:
plugin_root = os.path.dirname(P.blueprint.template_folder) plugin_root = os.path.dirname(P.blueprint.template_folder)
filepath = os.path.join(plugin_root, *path.split('/')) filepath = os.path.join(plugin_root, *path.split('/'))
from tool_base import ToolBaseFile from support import SupportFile
data = ToolBaseFile.read(filepath) data = SupportFile.read_file(filepath)
return render_template('manual.html', data=data) return render_template('manual.html', data=data)
except Exception as exception: except Exception as exception:
P.logger.error('Exception:%s', 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 P = module.P
if module.socketio_list is None: if module.socketio_list is None:
module.socketio_list = [] 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(): def connect():
try: 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) module.socketio_list.append(request.sid)
socketio_callback('start', '') socketio_callback('start', '')
module.socketio_connect() module.socketio_connect()
@@ -339,10 +341,10 @@ def default_route_socketio_module(module):
P.logger.error(traceback.format_exc()) 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(): def disconnect():
try: 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_list.remove(request.sid)
module.socketio_disconnect() module.socketio_disconnect()
except Exception as exception: except Exception as exception:
@@ -355,7 +357,7 @@ def default_route_socketio_module(module):
if encoding: if encoding:
data = json.dumps(data, cls=AlchemyEncoder) data = json.dumps(data, cls=AlchemyEncoder)
data = json.loads(data) 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 module.socketio_callback = socketio_callback

View File

@@ -53,19 +53,6 @@ class SupportFile(object):
return False return False
@classmethod @classmethod
def text_for_filename(cls, text): def text_for_filename(cls, text):
#text = text.replace('/', '') #text = text.replace('/', '')
@@ -94,6 +81,83 @@ class SupportFile(object):
@classmethod @classmethod

View File

@@ -19,6 +19,19 @@ def demote(user_uid, user_gid):
class SupportSubprocess(object): 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 # 2021-10-25
# timeout 적용 # timeout 적용
@classmethod @classmethod
@@ -26,15 +39,7 @@ class SupportSubprocess(object):
try: try:
logger.debug(f"execute_command_return : {' '.join(command)}") logger.debug(f"execute_command_return : {' '.join(command)}")
if platform.system() == 'Windows': command = cls.command_for_windows(command)
tmp = []
if type(command) == type([]):
for x in command:
if x.find(' ') == -1:
tmp.append(x)
else:
tmp.append(f'"{x}"')
command = ' '.join(tmp)
iter_arg = '' iter_arg = ''
if platform.system() == 'Windows': if platform.system() == 'Windows':
@@ -119,16 +124,7 @@ class SupportSubprocess(object):
def __execute_thread_function(self): def __execute_thread_function(self):
try: try:
if platform.system() == 'Windows': self.command = self.command_for_windows(self.command)
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)
logger.debug(f"{self.command=}") logger.debug(f"{self.command=}")
if platform.system() == 'Windows': 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) 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_communicate()
self.__start_send_callback() self.__start_send_callback()
if self.process is not None: 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") logger.info(f"{self.command} END")
except Exception as e: except Exception as e:
logger.error(f'Exception:{str(e)}') logger.error(f'Exception:{str(e)}')

View File

View 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]

View File

@@ -1,14 +1,16 @@
import os, sys, traceback import os
import sys
import traceback
try: try:
import oauth2client import oauth2client
except: except:
os.system('pip install oauth2client') os.system('pip install oauth2client')
import oauth2client import oauth2client
from oauth2client.file import Storage
from oauth2client import tools from oauth2client import tools
from oauth2client.client import flow_from_clientsecrets from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
try: try:
from apiclient.discovery import build from apiclient.discovery import build
@@ -17,14 +19,18 @@ except:
from apiclient.discovery import build from apiclient.discovery import build
try: try:
import gspread, time import time
from gspread_formatting import cellFormat, textFormat, color, format_cell_range
import gspread
from gspread_formatting import (cellFormat, color, format_cell_range,
textFormat)
except: except:
os.system('pip3 install gspread') os.system('pip3 install gspread')
os.system('pip3 install gspread_formatting') os.system('pip3 install gspread_formatting')
import gspread, time import gspread, time
from gspread_formatting import cellFormat, textFormat, color, format_cell_range 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() logger = get_logger()

View File

@@ -72,9 +72,7 @@ def get_logger(name=None, log_path=None):
file_max_bytes = 1 * 1024 * 1024 file_max_bytes = 1 * 1024 * 1024
if log_path == None: if log_path == None:
log_path = os.path.join(os.getcwd(), 'tmp') log_path = os.path.join(os.getcwd(), 'tmp')
#os.makedirs(log_path, exist_ok=True) os.makedirs(log_path, exist_ok=True)
else:
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) 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() streamHandler = logging.StreamHandler()

View File

@@ -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 __name__ == '__main__':
if platform.system() == 'Windows': if platform.system() == 'Windows':
sys.path += ["C:\SJVA3\lib2", "C:\SJVA3\data\custom", "C:\SJVA3_DEV"] sys.path += ["C:\SJVA3\lib2", "C:\SJVA3\data\custom", "C:\SJVA3_DEV"]
@@ -7,7 +18,6 @@ if __name__ == '__main__':
from support import d, logger from support import d, logger
apikey = '1e7952d0917d6aab1f0293a063697610' apikey = '1e7952d0917d6aab1f0293a063697610'
#apikey = '95a64ebcd8e154aeb96928bf34848826' #apikey = '95a64ebcd8e154aeb96928bf34848826'
@@ -352,7 +362,7 @@ class SupportTving:
ret = f"{title}.{qualityRes}-ST.mp4" ret = f"{title}.{qualityRes}-ST.mp4"
#if episode_data['drm']: #if episode_data['drm']:
# ret = ret.replace('.mp4', '.mkv') # ret = ret.replace('.mp4', '.mkv')
from support.base import SupportFile from support import SupportFile
return SupportFile.text_for_filename(ret) return SupportFile.text_for_filename(ret)
except Exception as e: except Exception as e:
logger.error(f"Exception:{str(e)}") logger.error(f"Exception:{str(e)}")
@@ -411,6 +421,7 @@ class SupportTving:
if __name__ == '__main__': if __name__ == '__main__':
import argparse import argparse
#from support.base import d, get_logger #from support.base import d, get_logger
from lib_wvtool import WVDownloader from lib_wvtool import WVDownloader

View File

@@ -1 +0,0 @@
from .gsheet_base import GoogleSheetBase

View File

@@ -30,15 +30,6 @@ class ToolBaseFile(object):
return False 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 @classmethod
def size(cls, start_path = '.'): def size(cls, start_path = '.'):