This commit is contained in:
joyfuI
2020-06-29 19:18:56 +09:00
parent 23c1521588
commit 806f4c616a
8 changed files with 101 additions and 121 deletions

View File

@@ -132,6 +132,8 @@ API에선 직접 비트레이트를 설정할 수 있습니다.
물론 해당 정보가 없으면 null입니다. 물론 해당 정보가 없으면 null입니다.
## Changelog ## Changelog
v1.5.1
v1.5.0 v1.5.0
* 프록시 설정 추가 * 프록시 설정 추가
* API에 archive 추가 * API에 archive 추가

View File

@@ -1 +1 @@
{"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "1.5.0", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"} {"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "1.5.1", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"}

View File

@@ -47,13 +47,13 @@ class Logic(object):
try: try:
import glob2 import glob2
except Exception as e: except ImportError:
# glob2 설치 # glob2 설치
logger.debug('glob2 install') logger.debug('glob2 install')
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'glob2'], universal_newlines=True)) logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'glob2'], universal_newlines=True))
try: try:
import flask_cors import flask_cors
except Exception as e: except ImportError:
# flask-cors 설치 # flask-cors 설치
logger.debug('flask-cors install') logger.debug('flask-cors install')
logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'flask-cors'], universal_newlines=True)) logger.debug(subprocess.check_output([sys.executable, '-m', 'pip', 'install', 'flask-cors'], universal_newlines=True))

View File

@@ -77,15 +77,15 @@ class LogicNormal(object):
data['status_str'] = youtube_dl.status.name data['status_str'] = youtube_dl.status.name
data['status_ko'] = str(youtube_dl.status) data['status_ko'] = str(youtube_dl.status)
data['end_time'] = '' data['end_time'] = ''
data['extractor'] = youtube_dl.extractor if youtube_dl.extractor is not None else '' data['extractor'] = youtube_dl.info_dict['extractor'] if youtube_dl.info_dict['extractor'] is not None else ''
data['title'] = youtube_dl.title if youtube_dl.title is not None else youtube_dl.url data['title'] = youtube_dl.info_dict['title'] if youtube_dl.info_dict['title'] is not None else youtube_dl.url
data['uploader'] = youtube_dl.uploader if youtube_dl.uploader is not None else '' data['uploader'] = youtube_dl.info_dict['uploader'] if youtube_dl.info_dict['uploader'] is not None else ''
data['uploader_url'] = youtube_dl.uploader_url if youtube_dl.uploader_url is not None else '' data['uploader_url'] = youtube_dl.info_dict['uploader_url'] if youtube_dl.info_dict['uploader_url'] is not None else ''
data['downloaded_bytes_str'] = '' data['downloaded_bytes_str'] = ''
data['total_bytes_str'] = '' data['total_bytes_str'] = ''
data['percent'] = '0' data['percent'] = '0'
data['eta'] = youtube_dl.eta if youtube_dl.eta is not None else '' data['eta'] = youtube_dl.progress_hooks['eta'] if youtube_dl.progress_hooks['eta'] is not None else ''
data['speed_str'] = LogicNormal.human_readable_size(youtube_dl.speed, '/s') if youtube_dl.speed is not None else '' data['speed_str'] = LogicNormal.human_readable_size(youtube_dl.progress_hooks['speed'], '/s') if youtube_dl.progress_hooks['speed'] is not None else ''
if youtube_dl.status == Status.READY: # 다운로드 전 if youtube_dl.status == Status.READY: # 다운로드 전
data['start_time'] = '' data['start_time'] = ''
data['download_time'] = '' data['download_time'] = ''
@@ -95,10 +95,10 @@ class LogicNormal(object):
else: else:
download_time = youtube_dl.end_time - youtube_dl.start_time download_time = youtube_dl.end_time - youtube_dl.start_time
data['end_time'] = youtube_dl.end_time.strftime('%m-%d %H:%M:%S') data['end_time'] = youtube_dl.end_time.strftime('%m-%d %H:%M:%S')
if None not in (youtube_dl.downloaded_bytes, youtube_dl.total_bytes): # 둘 다 값이 있으면 if None not in (youtube_dl.progress_hooks['downloaded_bytes'], youtube_dl.progress_hooks['total_bytes']): # 둘 다 값이 있으면
data['downloaded_bytes_str'] = LogicNormal.human_readable_size(youtube_dl.downloaded_bytes) data['downloaded_bytes_str'] = LogicNormal.human_readable_size(youtube_dl.progress_hooks['downloaded_bytes'])
data['total_bytes_str'] = LogicNormal.human_readable_size(youtube_dl.total_bytes) data['total_bytes_str'] = LogicNormal.human_readable_size(youtube_dl.progress_hooks['total_bytes'])
data['percent'] = '%.2f' % (float(youtube_dl.downloaded_bytes) / float(youtube_dl.total_bytes) * 100) data['percent'] = '%.2f' % (float(youtube_dl.progress_hooks['downloaded_bytes']) / float(youtube_dl.progress_hooks['total_bytes']) * 100)
data['start_time'] = youtube_dl.start_time.strftime('%m-%d %H:%M:%S') data['start_time'] = youtube_dl.start_time.strftime('%m-%d %H:%M:%S')
data['download_time'] = '%02d:%02d' % (download_time.seconds / 60, download_time.seconds % 60) data['download_time'] = '%02d:%02d' % (download_time.seconds / 60, download_time.seconds % 60)
return data return data

View File

@@ -43,7 +43,7 @@ class Youtube_dl(object):
_index = 0 _index = 0
_last_msg = '' _last_msg = ''
def __init__(self, plugin, url, filename, temp_path, save_path, format_code=None, postprocessor=None, proxy='', archive=None): def __init__(self, plugin, url, filename, temp_path, save_path, opts):
self.plugin = plugin self.plugin = plugin
self.url = url self.url = url
self.filename = filename self.filename = filename
@@ -53,10 +53,7 @@ class Youtube_dl(object):
if not os.path.isdir(save_path): if not os.path.isdir(save_path):
os.makedirs(save_path) os.makedirs(save_path)
self.save_path = save_path self.save_path = save_path
self.format_code = format_code self.opts = opts
self.postprocessor = postprocessor
self.proxy = proxy
self.archive = archive
self.index = Youtube_dl._index self.index = Youtube_dl._index
Youtube_dl._index += 1 Youtube_dl._index += 1
self._status = Status.READY self._status = Status.READY
@@ -64,21 +61,23 @@ class Youtube_dl(object):
self.key = None self.key = None
self.start_time = None # 시작 시간 self.start_time = None # 시작 시간
self.end_time = None # 종료 시간 self.end_time = None # 종료 시간
# info_dict에서 얻는 정보 self.info_dict = { # info_dict에서 얻는 정보
self.extractor = None # 타입 'extractor': None, # 타입
self.title = None # 제목 'title': None, # 제목
self.uploader = None # 업로더 'uploader': None, # 업로더
self.uploader_url = None # 업로더 주소 'uploader_url': None # 업로더 주소
}
# info_dict에서 얻는 정보(entries) # info_dict에서 얻는 정보(entries)
# self.playlist_index = None # self.info_dict['playlist_index'] = None
# self.duration = None # 길이 # self.info_dict['duration'] = None # 길이
# self.format = None # 포맷 # self.info_dict['format'] = None # 포맷
# self.thumbnail = None # 썸네일 # self.info_dict['thumbnail'] = None # 썸네일
# progress_hooks에서 얻는 정보 self.progress_hooks = { # progress_hooks에서 얻는 정보
self.downloaded_bytes = None # 다운로드한 크기 'downloaded_bytes': None, # 다운로드한 크기
self.total_bytes = None # 전체 크기 'total_bytes': None, # 전체 크기
self.eta = None # 예상 시간(s) 'eta': None, # 예상 시간(s)
self.speed = None # 다운로드 속도(bytes/s) 'speed': None # 다운로드 속도(bytes/s)
}
def start(self): def start(self):
if self.status != Status.READY: if self.status != Status.READY:
@@ -97,10 +96,10 @@ class Youtube_dl(object):
if info_dict is None: # 가져오기 실패 if info_dict is None: # 가져오기 실패
self.status = Status.ERROR self.status = Status.ERROR
return return
self.extractor = info_dict['extractor'] self.info_dict['extractor'] = info_dict['extractor']
self.title = info_dict['title'] self.info_dict['title'] = info_dict['title']
self.uploader = info_dict['uploader'] self.info_dict['uploader'] = info_dict['uploader']
self.uploader_url = info_dict['uploader_url'] self.info_dict['uploader_url'] = info_dict['uploader_url']
ydl_opts = { ydl_opts = {
'logger': MyLogger(), 'logger': MyLogger(),
'progress_hooks': [self.my_hook], 'progress_hooks': [self.my_hook],
@@ -109,14 +108,14 @@ class Youtube_dl(object):
'ignoreerrors': True, 'ignoreerrors': True,
'cachedir': False 'cachedir': False
} }
if self.format_code is not None: if 'format' in self.opts:
ydl_opts['format'] = self.format_code ydl_opts['format'] = self.opts['format']
if self.postprocessor is not None: if 'postprocessors' in self.opts:
ydl_opts['postprocessors'] = self.postprocessor ydl_opts['postprocessors'] = self.opts['postprocessors']
if not self.proxy: if 'proxy' in self.opts:
ydl_opts['proxy'] = self.proxy ydl_opts['proxy'] = self.opts['proxy']
if self.archive is not None: if 'download_archive' in self.opts:
ydl_opts['download_archive'] = self.archive ydl_opts['download_archive'] = self.opts['download_archive']
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([self.url]) ydl.download([self.url])
if self.status == Status.FINISHED: # 다운로드 성공 if self.status == Status.FINISHED: # 다운로드 성공
@@ -134,6 +133,7 @@ class Youtube_dl(object):
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
finally: finally:
celery_shutil.rmtree(self.temp_path) # 임시폴더 삭제 celery_shutil.rmtree(self.temp_path) # 임시폴더 삭제
if self.status != Status.STOP:
self.end_time = datetime.now() self.end_time = datetime.now()
def stop(self): def stop(self):
@@ -175,16 +175,16 @@ class Youtube_dl(object):
}[d['status']] }[d['status']]
if d['status'] != 'error': if d['status'] != 'error':
self.filename = os.path.basename(d.get('filename')) self.filename = os.path.basename(d.get('filename'))
self.downloaded_bytes = d.get('downloaded_bytes') self.progress_hooks['downloaded_bytes'] = d.get('downloaded_bytes')
self.total_bytes = d.get('total_bytes') self.progress_hooks['total_bytes'] = d.get('total_bytes')
self.eta = d.get('eta') self.progress_hooks['eta'] = d.get('eta')
self.speed = d.get('speed') self.progress_hooks['speed'] = d.get('speed')
def match_filter_func(self, info_dict): def match_filter_func(self, info_dict):
self.playlist_index = info_dict['playlist_index'] self.info_dict['playlist_index'] = info_dict['playlist_index']
self.duration = info_dict['duration'] self.info_dict['duration'] = info_dict['duration']
self.format = info_dict['format'] self.info_dict['format'] = info_dict['format']
self.thumbnail = info_dict['thumbnail'] self.info_dict['thumbnail'] = info_dict['thumbnail']
return None return None
@property @property

View File

@@ -34,7 +34,7 @@ menu = {
} }
plugin_info = { plugin_info = {
'version': '1.5.0', 'version': '1.5.1',
'name': 'youtube-dl', 'name': 'youtube-dl',
'category_name': 'vod', 'category_name': 'vod',
'developer': 'joyfuI', 'developer': 'joyfuI',
@@ -94,10 +94,12 @@ def first_menu(sub):
def ajax(sub): def ajax(sub):
logger.debug('AJAX %s %s', package_name, sub) logger.debug('AJAX %s %s', package_name, sub)
try: try:
# 공통 요청
if sub == 'setting_save': if sub == 'setting_save':
ret = ModelSetting.setting_save(request) ret = ModelSetting.setting_save(request)
return jsonify(ret) return jsonify(ret)
# UI 요청
elif sub == 'download': elif sub == 'download':
url = request.form['url'] url = request.form['url']
filename = request.form['filename'] filename = request.form['filename']
@@ -105,7 +107,6 @@ def ajax(sub):
save_path = ModelSetting.get('save_path') save_path = ModelSetting.get('save_path')
format_code = request.form['format'] if request.form['format'] else None format_code = request.form['format'] if request.form['format'] else None
postprocessor = request.form['postprocessor'] if request.form['postprocessor'] else None postprocessor = request.form['postprocessor'] if request.form['postprocessor'] else None
proxy = ModelSetting.get('proxy')
video_convertor, extract_audio = LogicNormal.get_postprocessor() video_convertor, extract_audio = LogicNormal.get_postprocessor()
if postprocessor in video_convertor: if postprocessor in video_convertor:
postprocessor = [{ postprocessor = [{
@@ -118,7 +119,12 @@ def ajax(sub):
'preferredcodec': postprocessor, 'preferredcodec': postprocessor,
'preferredquality': '192' 'preferredquality': '192'
}] }]
youtube_dl = Youtube_dl(package_name, url, filename, temp_path, save_path, format_code, postprocessor, proxy) opts = {
'format': format_code,
'postprocessors': postprocessor,
'proxy': ModelSetting.get('proxy')
}
youtube_dl = Youtube_dl(package_name, url, filename, temp_path, save_path, opts)
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
youtube_dl.start() youtube_dl.start()
socketio_emit('add', youtube_dl) socketio_emit('add', youtube_dl)
@@ -204,8 +210,13 @@ def api(sub):
'preferredcodec': preferredcodec, 'preferredcodec': preferredcodec,
'preferredquality': str(preferredquality) 'preferredquality': str(preferredquality)
}) })
proxy = ModelSetting.get('proxy') opts = {
youtube_dl = Youtube_dl(plugin, url, filename, temp_path, save_path, format_code, postprocessor, proxy, archive) 'format': format_code,
'postprocessors': postprocessor,
'proxy': ModelSetting.get('proxy'),
'download_archive': archive
}
youtube_dl = Youtube_dl(plugin, url, filename, temp_path, save_path, opts)
youtube_dl.key = key youtube_dl.key = key
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
ret['index'] = youtube_dl.index ret['index'] = youtube_dl.index
@@ -260,14 +271,7 @@ def api(sub):
elif sub == 'status': elif sub == 'status':
index = request.form.get('index') index = request.form.get('index')
key = request.form.get('key') key = request.form.get('key')
ret = { ret = {'errorCode': 0}
'errorCode': 0,
'status': None,
'start_time': None,
'end_time': None,
'temp_path': None,
'save_path': None
}
if None in (index, key): if None in (index, key):
return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음
index = int(index) index = int(index)

View File

@@ -4,7 +4,7 @@
<style> <style>
.row > div { .row > div {
padding-top: 3px; padding-top: 3px;
padding-bottom:3px; padding-bottom: 3px;
} }
.row { .row {
align-items: center; align-items: center;
@@ -21,15 +21,15 @@
<table id="result_table" class="table table-sm tableRowHover"> <table id="result_table" class="table table-sm tableRowHover">
<thead> <thead>
<tr> <tr>
<th style="width:5%">IDX</th> <th style="width: 5%">IDX</th>
<th style="width:8%">Plugin</th> <th style="width: 8%">Plugin</th>
<th style="width:10%">시작시간</th> <th style="width: 10%">시작시간</th>
<th style="width:10%">타입</th> <th style="width: 10%">타입</th>
<th style="width:28%">제목</th> <th style="width: 28%">제목</th>
<th style="width:8%">상태</th> <th style="width: 8%">상태</th>
<th style="width:15%">진행률</th> <th style="width: 15%">진행률</th>
<th style="width:8%">진행시간</th> <th style="width: 8%">진행시간</th>
<th style="width:8%">Action</th> <th style="width: 8%">Action</th>
</tr> </tr>
</thead> </thead>
<tbody id="list"></tbody> <tbody id="list"></tbody>
@@ -40,7 +40,7 @@
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
$(function () { $(function () {
let socket = io.connect(location.origin + "/" + package_name); let socket = io.connect(location.origin + '/' + package_name);
socket.on('add', function (data) { socket.on('add', function (data) {
$('#list').append(make_item(data)); $('#list').append(make_item(data));
@@ -84,12 +84,12 @@
}); });
function make_item(data) { function make_item(data) {
let str = '<tr id="item_' + data.index + '" style="cursor: pointer;" data-toggle="collapse" data-target="#collapse_' + data.index + '" aria-expanded="true">'; let str = `<tr id="item_${data.index}" aria-expanded="true" style="cursor: pointer" data-toggle="collapse" data-target="#collapse_${data.index}">`;
str += get_item(data); str += get_item(data);
str += '</tr>'; str += '</tr>';
str += '<tr class="collapse tableRowHoverOff" style="cursor: pointer;" id="collapse_' + data.index + '">'; str += `<tr id="collapse_${data.index}" class="collapse tableRowHoverOff" style="cursor: pointer">`;
str += '<td colspan="9">'; str += '<td colspan="9">';
str += '<div id="detail_' + data.index + '">'; str += `<div id="detail_${data.index}">`;
str += get_detail(data); str += get_detail(data);
str += '</div>'; str += '</div>';
str += '</td>'; str += '</td>';
@@ -98,21 +98,21 @@
} }
function get_item(data) { function get_item(data) {
let str = '<td>' + (data.index + 1) + '</td>'; let str = `<td>${data.index + 1}</td>`;
str += '<td>' + data.plugin + '</td>'; str += `<td>${data.plugin}</td>`;
str += '<td>' + data.start_time + '</td>'; str += `<td>${data.start_time}</td>`;
str += '<td>' + data.extractor + '</td>'; str += `<td>${data.extractor}</td>`;
str += '<td>' + data.title + '</td>'; str += `<td>${data.title}</td>`;
str += '<td>' + data.status_ko + '</td>'; str += `<td>${data.status_ko}</td>`;
let visi = 'hidden'; let visi = 'hidden';
if (parseInt(data.percent) > 0 && data.status_str !== 'STOP') { if (parseInt(data.percent) > 0 && data.status_str !== 'STOP') {
visi = 'visible'; visi = 'visible';
} }
str += '<td><div class="progress"><div class="progress-bar" style="visibility: ' + visi + '; width:' + data.percent + '%">' + data.percent + '%</div></div></td>'; str += `<td><div class="progress"><div class="progress-bar" style="visibility: ${visi}; width: ${data.percent}%">${data.percent}%</div></div></td>`;
str += '<td>' + data.download_time + '</td>'; str += `<td>${data.download_time}</td>`;
str += '<td class="tableRowHoverOff">'; str += '<td class="tableRowHoverOff">';
if (data.status_str === 'START' || data.status_str === 'DOWNLOADING' || data.status_str === 'FINISHED') { if (data.status_str === 'START' || data.status_str === 'DOWNLOADING' || data.status_str === 'FINISHED') {
str += '<button class="align-middle btn btn-outline-danger btn-sm youtube-dl_stop" data-index="' + data.index + '">중지</button>'; str += `<button class="align-middle btn btn-outline-danger btn-sm youtube-dl_stop" data-index="${data.index}">중지</button>`;
} }
str += '</td>'; str += '</td>';
return str; return str;
@@ -127,8 +127,8 @@
if (data.status_str === 'DOWNLOADING') { if (data.status_str === 'DOWNLOADING') {
str += info_html('', '<b>현재 다운로드 중인 파일에 대한 정보</b>'); str += info_html('', '<b>현재 다운로드 중인 파일에 대한 정보</b>');
str += info_html('파일명', data.filename); str += info_html('파일명', data.filename);
str += info_html('진행률(current/total)', data.percent + '% (' + data.downloaded_bytes_str + ' / ' + data.total_bytes_str + ')'); str += info_html('진행률(current/total)', `${data.percent}% (${data.downloaded_bytes_str} / ${data.total_bytes_str})`);
str += info_html('남은 시간', data.eta + '초'); str += info_html('남은 시간', `${data.eta}`);
str += info_html('다운 속도', data.speed_str); str += info_html('다운 속도', data.speed_str);
} }
return str; return str;
@@ -138,13 +138,13 @@
let str = '<div class="row">'; let str = '<div class="row">';
let link = (left === 'URL' || left === '업로더'); let link = (left === 'URL' || left === '업로더');
str += '<div class="col-sm-2">'; str += '<div class="col-sm-2">';
str += '<b>' + left + '</b>'; str += `<b>${left}</b>`;
str += '</div>'; str += '</div>';
str += '<div class="col-sm-10">'; str += '<div class="col-sm-10">';
str += '<div class="input-group col-sm-9">'; str += '<div class="input-group col-sm-9">';
str += '<span class="text-left" style="padding-left:10px; padding-top:3px">'; str += '<span class="text-left" style="padding-left: 10px; padding-top: 3px">';
if (link) { if (link) {
str += '<a href="' + option + '" target="_blank">'; str += `<a href="${option}" target="_blank">`;
} }
str += right; str += right;
if (link) { if (link) {

View File

@@ -9,39 +9,13 @@
{{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder='저장 폴더 경로', desc=['템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template 참고', '기본값은 "%(title)s-%(id)s.%(ext)s"입니다.']) }} {{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder='저장 폴더 경로', desc=['템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template 참고', '기본값은 "%(title)s-%(id)s.%(ext)s"입니다.']) }}
{{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], placeholder='프록시 주소', desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }} {{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], placeholder='프록시 주소', desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }}
{{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }} {{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }}
{{ macros.setting_button([['setting_save', '저장']]) }} {{ macros.setting_button([['global_setting_save_btn', '저장']]) }}
</form> </form>
</div> </div>
<script> <script>
"use strict"; "use strict";
const package_name = '{{ arg["package_name"] }}'; const package_name = '{{ arg["package_name"] }}';
$(function () {
// 설정 저장
$('#setting_save').click(function () {
let 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;
});
});
</script> </script>
{% endblock %} {% endblock %}