417 lines
14 KiB
HTML
417 lines
14 KiB
HTML
{% extends "base.html" %}
|
|
{% import "macro.html" as macros %}
|
|
|
|
{% block content %}
|
|
<!-- Google Fonts: Inter -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
|
|
<style>
|
|
/* Premium Modern Design System (Sync with List View) */
|
|
:root {
|
|
--bg-body: #0f172a;
|
|
--surface: rgba(30, 41, 59, 0.7);
|
|
--surface-opaque: #1e293b;
|
|
--border: rgba(255, 255, 255, 0.1);
|
|
--text-main: #f8fafc;
|
|
--text-muted: #94a3b8;
|
|
--accent-primary: #38bdf8;
|
|
--accent-secondary: #818cf8;
|
|
--success: #10b981;
|
|
--glow: rgba(56, 189, 248, 0.3);
|
|
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
}
|
|
|
|
/* Plugin-owned loading override (independent from Flaskfarm default loader assets) */
|
|
#loading,
|
|
#modal_loading {
|
|
display: none;
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 3000010 !important;
|
|
background: rgba(7, 16, 35, 0.46);
|
|
backdrop-filter: blur(2px);
|
|
}
|
|
|
|
#loading img,
|
|
#modal_loading img {
|
|
display: none !important;
|
|
}
|
|
|
|
#loading::before,
|
|
#modal_loading::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
width: 58px;
|
|
height: 58px;
|
|
margin-left: -29px;
|
|
margin-top: -29px;
|
|
border-radius: 50%;
|
|
border: 3px solid rgba(255, 255, 255, 0.24);
|
|
border-top-color: var(--accent-primary);
|
|
border-right-color: var(--accent-secondary);
|
|
animation: gdm-loader-spin 0.9s linear infinite;
|
|
}
|
|
|
|
#loading::after,
|
|
#modal_loading::after {
|
|
content: "LOADING";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: calc(50% + 44px);
|
|
transform: translateX(-50%);
|
|
color: var(--text-main);
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.12em;
|
|
}
|
|
|
|
#loading[style*="display: block"],
|
|
#loading[style*="display: inline-block"],
|
|
#modal_loading[style*="display: block"],
|
|
#modal_loading[style*="display: inline-block"] {
|
|
display: block !important;
|
|
}
|
|
|
|
@keyframes gdm-loader-spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
#gommi_download_manager_queue_setting {
|
|
font-family: var(--font-sans);
|
|
color: var(--text-main);
|
|
padding-bottom: 3rem;
|
|
}
|
|
|
|
/* Navigation Override (SJVA Menu Page) */
|
|
#menu_page_div {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
/* Redesigned Navigation Menu (tabs) */
|
|
#menu_page_div .nav-pills {
|
|
margin-top: 2px !important;
|
|
margin-bottom: 12px !important;
|
|
background: rgba(255, 255, 255, 0.05) !important;
|
|
backdrop-filter: blur(12px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 12px !important;
|
|
padding: 6px !important;
|
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3) !important;
|
|
display: inline-flex !important;
|
|
width: auto !important;
|
|
}
|
|
|
|
/* Global Navigation Spacing Adjustments (Fix for extra gap) */
|
|
#menu_module_div {
|
|
padding-top: 0 !important;
|
|
}
|
|
#menu_module_div .nav-pills {
|
|
margin-top: 0 !important;
|
|
margin-bottom: 5px !important;
|
|
}
|
|
@media (min-width: 769px) {
|
|
#main_container {
|
|
margin-top: 0 !important;
|
|
padding-top: 0 !important;
|
|
}
|
|
}
|
|
|
|
#menu_page_div .nav-link {
|
|
color: var(--text-muted);
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
padding: 0.6rem 1.25rem;
|
|
border-radius: 10px;
|
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
#menu_page_div .nav-link:hover {
|
|
color: var(--text-main);
|
|
background: rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
#menu_page_div .nav-link.active {
|
|
background: white !important;
|
|
color: #0f172a !important;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
#menu_page_div .nav-pills {
|
|
display: flex;
|
|
width: 100%;
|
|
}
|
|
#menu_page_div .nav-link {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 0.6rem 0.5rem;
|
|
}
|
|
}
|
|
|
|
/* Form Styling */
|
|
.settings-container {
|
|
background: var(--surface);
|
|
backdrop-filter: blur(12px);
|
|
border: 1px solid var(--border);
|
|
border-radius: 20px;
|
|
padding: 2rem;
|
|
}
|
|
|
|
.page-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 2rem;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 1.75rem;
|
|
font-weight: 700;
|
|
letter-spacing: -0.025em;
|
|
background: linear-gradient(135deg, #fff 0%, #cbd5e1 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
/* Buttons */
|
|
.btn-premium {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 0.6rem 1.25rem;
|
|
border-radius: 12px;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
transition: all 0.2s;
|
|
cursor: pointer;
|
|
border: 1px solid var(--accent-primary);
|
|
background: var(--accent-primary);
|
|
color: #000;
|
|
box-shadow: 0 4px 12px var(--glow);
|
|
}
|
|
|
|
.btn-premium:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 20px var(--glow);
|
|
opacity: 0.9;
|
|
color: #000;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* Form Controls Override */
|
|
.form-control, .custom-select {
|
|
background-color: rgba(0, 0, 0, 0.2) !important;
|
|
border: 1px solid var(--border) !important;
|
|
color: var(--text-main) !important;
|
|
border-radius: 10px;
|
|
padding: 0.75rem 1rem;
|
|
height: auto;
|
|
}
|
|
|
|
.form-control:focus {
|
|
border-color: var(--accent-primary) !important;
|
|
box-shadow: 0 0 0 2px var(--glow) !important;
|
|
}
|
|
|
|
label {
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
color: var(--text-main);
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.form-text {
|
|
color: var(--text-muted) !important;
|
|
font-size: 0.8rem;
|
|
margin-top: 0.4rem;
|
|
}
|
|
|
|
h5.mb-0 {
|
|
font-weight: 700;
|
|
letter-spacing: -0.01em;
|
|
color: var(--accent-primary);
|
|
margin-bottom: 1.5rem !important;
|
|
display: block;
|
|
width: 100%;
|
|
border-left: 4px solid var(--accent-primary);
|
|
padding-left: 1rem;
|
|
}
|
|
|
|
hr {
|
|
border-top: 1px solid var(--border);
|
|
margin: 2.5rem 0;
|
|
}
|
|
</style>
|
|
|
|
<div id="gommi_download_manager_queue_setting" class="mt-4">
|
|
<div class="page-header">
|
|
<h1 class="page-title">GDM Settings</h1>
|
|
<div class="header-actions">
|
|
<button type="button" class="btn-premium" id="btn-self-update" style="background: var(--accent-secondary); border-color: var(--accent-secondary);">
|
|
<i class="fa fa-refresh"></i> Update
|
|
</button>
|
|
<button type="button" class="btn-premium" id="globalSettingSaveBtn">
|
|
<i class="fa fa-save"></i> Save Changes
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-container">
|
|
<form id="setting">
|
|
<!-- Basic Setting -->
|
|
<h5 class="mb-4">General Settings</h5>
|
|
|
|
<div class="form-group">
|
|
<label>Save Path</label>
|
|
<input type="text" name="save_path" class="form-control" value="{{arg['save_path']}}">
|
|
<small class="form-text">{PATH_DATA} will be replaced by the actual data path.</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Temp Path</label>
|
|
<input type="text" name="temp_path" class="form-control" value="{{arg['temp_path']}}">
|
|
<small class="form-text">Temporary storage path for files during download.</small>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label>Max Concurrent Downloads</label>
|
|
<input type="number" name="max_concurrent" class="form-control" value="{{arg['max_concurrent']}}">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label>Speed Limit</label>
|
|
<select name="max_download_rate" class="custom-select">
|
|
<option value="0" {% if arg['max_download_rate'] == '0' %}selected{% endif %}>Unlimited</option>
|
|
<option value="1M" {% if arg['max_download_rate'] == '1M' %}selected{% endif %}>1 MB/s</option>
|
|
<option value="3M" {% if arg['max_download_rate'] == '3M' %}selected{% endif %}>3 MB/s</option>
|
|
<option value="5M" {% if arg['max_download_rate'] == '5M' %}selected{% endif %}>5 MB/s</option>
|
|
<option value="6M" {% if arg['max_download_rate'] == '6M' %}selected{% endif %}>6 MB/s</option>
|
|
<option value="7M" {% if arg['max_download_rate'] == '7M' %}selected{% endif %}>7 MB/s</option>
|
|
<option value="8M" {% if arg['max_download_rate'] == '8M' %}selected{% endif %}>8 MB/s</option>
|
|
<option value="9M" {% if arg['max_download_rate'] == '9M' %}selected{% endif %}>9 MB/s</option>
|
|
<option value="10M" {% if arg['max_download_rate'] == '10M' %}selected{% endif %}>10 MB/s</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<!-- Downloader Setting -->
|
|
<h5 class="mb-4">External Tool Paths</h5>
|
|
|
|
<div class="form-group">
|
|
<label>aria2c Path</label>
|
|
<input type="text" name="aria2c_path" class="form-control" value="{{arg['aria2c_path']}}">
|
|
<small class="form-text">Executable path for aria2c (used for high-speed downloads).</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>aria2c Connections</label>
|
|
<input type="number" name="aria2c_connections" class="form-control" value="{{arg['aria2c_connections']}}">
|
|
<small class="form-text">Concurrent connections per download (default: 16).</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>ffmpeg Path</label>
|
|
<input type="text" name="ffmpeg_path" class="form-control" value="{{arg['ffmpeg_path']}}">
|
|
<small class="form-text">Executable path for ffmpeg (used for HLS streams).</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>yt-dlp Path</label>
|
|
<input type="text" name="yt_dlp_path" class="form-control" value="{{arg['yt_dlp_path']}}">
|
|
<small class="form-text">If empty, the Python module will be used.</small>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<!-- Retry Setting -->
|
|
<h5 class="mb-4">Error Handling</h5>
|
|
|
|
<div class="form-group custom-control custom-switch mb-3">
|
|
<input type="checkbox" name="auto_retry" class="custom-control-input" id="auto_retry" {% if arg['auto_retry'] == 'True' or arg['auto_retry'] == True %}checked{% endif %}>
|
|
<label class="custom-control-label" for="auto_retry">Enable Auto-Retry</label>
|
|
<small class="form-text d-block">Automatically retry failed downloads.</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Max Retry Count</label>
|
|
<input type="number" name="max_retry" class="form-control" value="{{arg['max_retry']}}">
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block tail_js %}
|
|
<script type="text/javascript">
|
|
const package_name = "{{arg['package_name']}}";
|
|
const sub = "{{arg['module_name']}}";
|
|
|
|
$(document).ready(function(){
|
|
// Handled by common framework
|
|
});
|
|
|
|
$("body").on('click', '#globalSettingSaveBtn', function(e){
|
|
e.preventDefault();
|
|
var formData = get_formdata('#setting');
|
|
$.ajax({
|
|
url: `/${package_name}/ajax/${sub}/setting_save`,
|
|
type: "POST",
|
|
cache: false,
|
|
data: formData,
|
|
dataType: "json",
|
|
success: function(ret) {
|
|
if (ret.ret == 'success') {
|
|
$.notify('<strong>Settings Saved Successfully</strong>', {type:'success'});
|
|
} else {
|
|
$.notify('<strong>Save Failed: ' + ret.msg + '</strong>', {type:'danger'});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Self Update
|
|
$("body").on('click', '#btn-self-update', function(e){
|
|
e.preventDefault();
|
|
if (!confirm('최신 코드를 다운로드하고 플러그인을 리로드하시겠습니까?')) return;
|
|
|
|
var btn = $(this);
|
|
var originalText = btn.html();
|
|
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 업데이트 중...');
|
|
|
|
$.ajax({
|
|
url: `/${package_name}/ajax/${sub}/self_update`,
|
|
type: "POST",
|
|
dataType: "json",
|
|
success: function(ret) {
|
|
if (ret.ret == 'success') {
|
|
$.notify('<strong>업데이트 완료!</strong> 페이지를 새로고침합니다.', {type:'success'});
|
|
setTimeout(function() { location.reload(); }, 1500);
|
|
} else {
|
|
$.notify('<strong>업데이트 실패: ' + ret.msg + '</strong>', {type:'danger'});
|
|
}
|
|
},
|
|
error: function() {
|
|
$.notify('<strong>업데이트 중 오류 발생</strong>', {type:'danger'});
|
|
},
|
|
complete: function() {
|
|
btn.prop('disabled', false).html(originalText);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|