Add files via upload
ff_linkkf
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div id="linkkf_wrapper">
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
id="input_search"
|
||||
type="search"
|
||||
class="form-control rounded"
|
||||
placeholder="작품명 검색"
|
||||
aria-label="작품명 검색"
|
||||
aria-describedby="search-addon"
|
||||
/>
|
||||
<button id="btn_search" type="button" class="btn btn-primary">검색</button>
|
||||
</div>
|
||||
|
||||
<div id="anime_category" class="btn-group mb-3" role="group" aria-label="linkkf categories">
|
||||
<button id="ing" type="button" class="btn btn-success active">애니</button>
|
||||
<button id="movie" type="button" class="btn btn-primary">극장판</button>
|
||||
<button id="complete" type="button" class="btn btn-dark">성인</button>
|
||||
<button id="top_view" type="button" class="btn btn-warning">최신</button>
|
||||
</div>
|
||||
|
||||
<div id="screen_movie_list" class="container"></div>
|
||||
|
||||
<div class="spinner" id="spinner" style="display: none">
|
||||
<div class="double-bounce1"></div>
|
||||
<div class="double-bounce2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link
|
||||
href="{{ url_for('.static', filename='css/%s.css' % arg['template_name']) }}"
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script>
|
||||
"use strict";
|
||||
const package_name = '{{ arg["package_name"] }}';
|
||||
const linkkf_url = "{{ arg['linkkf_url'] }}";
|
||||
const request_path = "./request";
|
||||
const placeholder_image = "./static/img_loader_x200.svg";
|
||||
</script>
|
||||
<script src="{{ url_for('.static', filename='js/%s.js' % arg['template_name']) }}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,51 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div>
|
||||
<form id="form_search" class="mb-3">
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-md-4 mb-2">
|
||||
<select id="order" name="order" class="form-control form-control-sm">
|
||||
<option value="desc">최신순</option>
|
||||
<option value="asc">오래된순</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-2">
|
||||
<select id="option" name="option" class="form-control form-control-sm">
|
||||
<option value="finished" selected>완료/실패/취소</option>
|
||||
<option value="all">전체</option>
|
||||
<option value="completed">완료</option>
|
||||
<option value="error">실패</option>
|
||||
<option value="canceled">취소</option>
|
||||
<option value="downloading">진행중</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5 mb-2 d-flex">
|
||||
<input
|
||||
id="search_word"
|
||||
name="search_word"
|
||||
class="form-control form-control-sm mr-2"
|
||||
type="text"
|
||||
placeholder="작품명 검색"
|
||||
aria-label="Search"
|
||||
/>
|
||||
<button id="search" class="btn btn-sm btn-outline-success mr-2">검색</button>
|
||||
<button id="reset_btn" class="btn btn-sm btn-outline-secondary">리셋</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="page1"></div>
|
||||
<div id="list_div"></div>
|
||||
<div id="page2"></div>
|
||||
</div>
|
||||
|
||||
<link
|
||||
href="{{ url_for('.static', filename='css/%s.css' % arg['template_name']) }}"
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script type="text/javascript">
|
||||
const package_name = "{{arg['package_name']}}";
|
||||
</script>
|
||||
<script src="{{ url_for('.static', filename='js/%s.js' % arg['template_name']) }}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,161 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="mb-3 d-flex flex-wrap align-items-center">
|
||||
<button id="reset_btn" class="btn btn-sm btn-warning mr-2 mb-2">초기화</button>
|
||||
<button id="delete_completed_btn" class="btn btn-sm btn-secondary mr-2 mb-2">완료 항목 제거</button>
|
||||
<button id="go_ffmpeg_btn" class="btn btn-sm btn-info mb-2">Go FFMPEG</button>
|
||||
</div>
|
||||
|
||||
<div id="queue_empty" class="alert alert-secondary" style="display: none;">
|
||||
대기열이 비어 있습니다.
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>코드</th>
|
||||
<th>생성 시각</th>
|
||||
<th>파일명</th>
|
||||
<th>상태</th>
|
||||
<th>동작</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="download_list_div"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(() => {
|
||||
if (window.__linkkfQueueInitialized === true) {
|
||||
return;
|
||||
}
|
||||
window.__linkkfQueueInitialized = true;
|
||||
|
||||
const packageName = "{{ arg['package_name'] }}";
|
||||
const socketUrl = `${window.location.protocol}//${window.location.host}/${packageName}/main`;
|
||||
const socket = io(socketUrl);
|
||||
const listContainer = document.getElementById("download_list_div");
|
||||
const emptyBox = document.getElementById("queue_empty");
|
||||
|
||||
const escapeHtml = (value) =>
|
||||
String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
function makeProgress(id, percent, label) {
|
||||
const safePercent = Number(percent || 0);
|
||||
return `
|
||||
<div class="progress">
|
||||
<div
|
||||
id="progress_${escapeHtml(id)}"
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
style="width: ${safePercent}%"
|
||||
aria-valuenow="${safePercent}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
>
|
||||
<span id="progress_${escapeHtml(id)}_label">${escapeHtml(label)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderQueue(data) {
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
listContainer.innerHTML = "";
|
||||
emptyBox.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
emptyBox.style.display = "none";
|
||||
let html = "";
|
||||
for (const item of data) {
|
||||
const label = item.ffmpeg_percent && item.ffmpeg_percent !== 0
|
||||
? `${item.ffmpeg_status_kor} (${item.ffmpeg_percent}%)`
|
||||
: item.ffmpeg_status_kor;
|
||||
const fileName = `${escapeHtml(item.info.program_title || "")}<br>${escapeHtml(item.info.filename || "")}`;
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${escapeHtml(item.entity_id)}</td>
|
||||
<td>${escapeHtml(item.created_time)}</td>
|
||||
<td>${fileName}</td>
|
||||
<td>${makeProgress(item.entity_id, item.ffmpeg_percent, label)}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-danger mr-1 mb-1" data-command="cancel" data-id="${escapeHtml(item.entity_id)}">취소</button>
|
||||
<button class="btn btn-sm btn-outline-secondary mb-1" data-command="delete" data-id="${escapeHtml(item.entity_id)}">삭제</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
listContainer.innerHTML = html;
|
||||
}
|
||||
|
||||
function updateStatus(data) {
|
||||
const progress = document.getElementById(`progress_${data.plugin_id}`);
|
||||
const label = document.getElementById(`progress_${data.plugin_id}_label`);
|
||||
const percent = data.data && data.data.percent ? data.data.percent : 0;
|
||||
if (progress) {
|
||||
progress.style.width = `${percent}%`;
|
||||
progress.setAttribute("aria-valuenow", percent);
|
||||
}
|
||||
if (label) {
|
||||
const speed = data.data && data.data.current_speed ? ` x${data.data.current_speed}` : "";
|
||||
label.textContent = `${data.status} (${percent}%)${speed}`;
|
||||
}
|
||||
}
|
||||
|
||||
function programAutoCommand(payload) {
|
||||
$.ajax({
|
||||
url: "/" + packageName + "/ajax/program_auto_command",
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: payload,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.ret === "notify") {
|
||||
$.notify(`<strong>${ret.log}</strong>`, { type: "warning" });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
socket.on("on_connect", function (data) {
|
||||
renderQueue(data);
|
||||
});
|
||||
|
||||
socket.on("status", function (data) {
|
||||
updateStatus(data);
|
||||
});
|
||||
|
||||
socket.on("list_refresh", function (data) {
|
||||
renderQueue(data);
|
||||
});
|
||||
|
||||
$("body").on("click", "button[data-command='cancel']", function (e) {
|
||||
e.preventDefault();
|
||||
programAutoCommand({ command: "cancel", entity_id: $(this).data("id") });
|
||||
});
|
||||
|
||||
$("body").on("click", "#reset_btn", function (e) {
|
||||
e.preventDefault();
|
||||
programAutoCommand({ command: "reset", entity_id: -1 });
|
||||
});
|
||||
|
||||
$("body").on("click", "#delete_completed_btn", function (e) {
|
||||
e.preventDefault();
|
||||
programAutoCommand({ command: "delete_completed", entity_id: -1 });
|
||||
});
|
||||
|
||||
$("body").on("click", "#go_ffmpeg_btn", function (e) {
|
||||
e.preventDefault();
|
||||
window.location.href = "/ffmpeg";
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div id="request">
|
||||
<form id="program_list" style="padding-bottom: 1em;">
|
||||
<div class="card p-lg-5 p-md-3 py-2 border-light">
|
||||
{{
|
||||
macros.setting_input_text_and_buttons(
|
||||
'code',
|
||||
'작품 코드',
|
||||
[['analysis_btn', '분석'], ['go_linkkf_btn', 'LinkKF 열기'], ['go_modal_airing', '최신']],
|
||||
desc='예시: https://linkkf.tv/ani/405619/ 또는 405619'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="airing_list_form">
|
||||
<div id="airing_list"></div>
|
||||
</form>
|
||||
|
||||
<form id="program_auto_form">
|
||||
<div id="episode_list"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<link href="{{ url_for('.static', filename='css/%s.css' % arg['template_name']) }}" type="text/css" rel="stylesheet"/>
|
||||
<script type="text/javascript">
|
||||
const package_name = "{{ arg['package_name'] }}";
|
||||
const linkkf_url = "{{ arg['linkkf_url'] }}";
|
||||
const pageParams = new URLSearchParams(window.location.search);
|
||||
const initialCodeFromQuery = pageParams.get("code");
|
||||
|
||||
$(document).ready(function () {
|
||||
if ("{{ arg['current_code'] }}" !== "") {
|
||||
document.getElementById("code").value = "{{ arg['current_code'] }}";
|
||||
}
|
||||
|
||||
if (initialCodeFromQuery !== null && initialCodeFromQuery !== "") {
|
||||
document.getElementById("code").value = initialCodeFromQuery;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="{{ url_for('.static', filename='js/%s.js' % arg['template_name']) }}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,97 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div>
|
||||
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장']]) }}
|
||||
<form id="setting">
|
||||
{{ macros.setting_input_text_and_buttons('linkkf_url', 'Linkkf 주소', [['go_btn', '열기']], value=arg['linkkf_url']) }}
|
||||
{{ macros.setting_input_text('download_path', '다운로드 폴더', value=arg['download_path']) }}
|
||||
{{ macros.setting_checkbox('auto_make_folder', '작품 폴더 생성', value=arg['auto_make_folder']) }}
|
||||
{{ macros.setting_checkbox('linkkf_auto_make_season_folder', '시즌 폴더 생성', value=arg['linkkf_auto_make_season_folder']) }}
|
||||
{{ macros.setting_input_text('linkkf_finished_insert', '완결 표시', value=arg['linkkf_finished_insert'], col='3') }}
|
||||
{{ macros.setting_input_int('max_ffmpeg_process_count', '동시 다운로드 수', value=arg['max_ffmpeg_process_count']) }}
|
||||
{{ macros.setting_input_text('auto_interval', '자동 실행 주기', value=arg['auto_interval'], col='3', placeholder='*/20 * * * *') }}
|
||||
{{ macros.setting_checkbox('auto_start', '시작 시 자동 실행', value=arg['auto_start']) }}
|
||||
{{ macros.setting_input_textarea('whitelist_program', '자동 다운로드 코드', value=arg['whitelist_program']) }}
|
||||
{{ macros.global_setting_scheduler_button(arg['scheduler'], arg['is_running']) }}
|
||||
{{ macros.setting_buttons([['linkkf_one_execute_btn', '1회 실행']], left='수동 실행') }}
|
||||
{{ macros.setting_buttons([['linkkf_reset_db_btn', 'DB 초기화']], left='DB 정리') }}
|
||||
{{ macros.setting_buttons([['btn_airing_code', '최신 코드 가져오기']], left='보조 기능') }}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
const package_name = "{{ arg['package_name'] }}";
|
||||
|
||||
$(document).ready(function () {
|
||||
$("body").off("change", "#globalSchedulerSwitchBtn");
|
||||
});
|
||||
|
||||
$("body").on("click", "#go_btn", function (e) {
|
||||
e.preventDefault();
|
||||
window.open($("#linkkf_url").val(), "_blank");
|
||||
});
|
||||
|
||||
$("body").on("change", "#globalSchedulerSwitchBtn", function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: "/" + package_name + "/ajax/scheduler_toggle",
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: { scheduler: $(this).prop("checked") },
|
||||
dataType: "json",
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on("click", "#linkkf_one_execute_btn", function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: "/" + package_name + "/ajax/execute_once",
|
||||
type: "POST",
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.ret === "success") {
|
||||
$.notify("<strong>작업을 시작했습니다.</strong>", { type: "success" });
|
||||
} else {
|
||||
$.notify("<strong>작업 시작에 실패했습니다.</strong>", { type: "warning" });
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on("click", "#linkkf_reset_db_btn", function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: "/" + package_name + "/ajax/reset_db",
|
||||
type: "POST",
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.ret === "success") {
|
||||
$.notify("<strong>DB를 초기화했습니다.</strong>", { type: "success" });
|
||||
} else {
|
||||
$.notify("<strong>DB 초기화에 실패했습니다.</strong>", { type: "warning" });
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on("click", "#btn_airing_code", function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: "/" + package_name + "/ajax/get_airing_code",
|
||||
type: "GET",
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret.ret === "success" && Array.isArray(ret.data)) {
|
||||
$("#whitelist_program").val(ret.data.join(","));
|
||||
$.notify("<strong>최신 코드를 불러왔습니다.</strong>", { type: "success" });
|
||||
} else {
|
||||
$.notify("<strong>코드를 불러오지 못했습니다.</strong>", { type: "warning" });
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user