feat: Implement directory browsing for ohli24 and linkkf, enhance path security with NFC/NFD normalization, and refine playlist generation logic in ohli24.
This commit is contained in:
@@ -23,7 +23,27 @@
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{{ macros.m_tab_content_start('normal', true) }}
|
||||
{{ macros.setting_input_text_and_buttons('anilife_url', '애니라이프 URL', [['go_btn', 'GO']], value=arg['anilife_url']) }}
|
||||
{{ macros.setting_input_text('anilife_download_path', '저장 폴더', value=arg['anilife_download_path'], desc='정상적으로 다운 완료 된 파일이 이동할 폴더 입니다. ') }}
|
||||
|
||||
<!-- 저장 폴더 (탐색 버튼 포함) -->
|
||||
<div class="row" style="padding-top: 10px; padding-bottom:10px; align-items: center;">
|
||||
<div class="col-sm-3 set-left">
|
||||
<strong>저장 폴더</strong>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group col-sm-9">
|
||||
<input type="text" class="form-control form-control-sm" id="anilife_download_path" name="anilife_download_path" value="{{arg['anilife_download_path']}}">
|
||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group" style="padding-left:5px; padding-top:0px">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="browse_folder_btn" title="폴더 탐색">
|
||||
<i class="bi bi-folder2-open"></i> 탐색
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding-left:20px; padding-top:5px;">
|
||||
<em>정상적으로 다운 완료 된 파일이 이동할 폴더 입니다.</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ macros.setting_input_int('anilife_max_ffmpeg_process_count', '동시 다운로드 수', value=arg['anilife_max_ffmpeg_process_count'], desc='동시에 다운로드 할 에피소드 갯수입니다.') }}
|
||||
{{ macros.setting_select('anilife_download_method', '다운로드 방법', [['ffmpeg', 'ffmpeg (기본)'], ['ytdlp', 'yt-dlp']], value=arg.get('anilife_download_method', 'ffmpeg'), desc='m3u8 다운로드에 사용할 도구를 선택합니다.') }}
|
||||
{{ macros.setting_checkbox('anilife_order_desc', '요청 화면 최신순 정렬', value=arg['anilife_order_desc'], desc='On : 최신화부터, Off : 1화부터') }}
|
||||
@@ -48,6 +68,44 @@
|
||||
</div>
|
||||
</div> <!--전체-->
|
||||
|
||||
<!-- 폴더 탐색 모달 -->
|
||||
<div class="modal fade" id="folderBrowserModal" tabindex="-1" role="dialog" aria-labelledby="folderBrowserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" style="background: #1e293b; border: 1px solid rgba(255,255,255,0.1);">
|
||||
<div class="modal-header" style="border-color: rgba(255,255,255,0.1);">
|
||||
<h5 class="modal-title text-white" id="folderBrowserModalLabel">
|
||||
<i class="bi bi-folder2-open mr-2"></i>폴더 선택
|
||||
</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary mr-2" id="folder_go_up" title="상위 폴더">
|
||||
<i class="bi bi-arrow-up"></i>
|
||||
</button>
|
||||
<div class="flex-grow-1 px-3 py-2 rounded" style="background: rgba(0,0,0,0.3); font-family: monospace; color: #94a3b8;">
|
||||
<span id="current_path_display">/</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="folder_list" style="min-height: 300px; max-height: 600px; overflow-y: auto; background: rgba(0,0,0,0.2); border-radius: 8px; padding: 4px;">
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="bi bi-arrow-repeat spin"></i> 로딩 중...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="border-color: rgba(255,255,255,0.1);">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>
|
||||
<button type="button" class="btn btn-primary" id="folder_select_btn">
|
||||
<i class="bi bi-check-lg mr-1"></i>선택
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css">
|
||||
|
||||
<style>
|
||||
@@ -252,6 +310,76 @@ $("body").on('click', '#go_btn', function(e){
|
||||
window.open(url, "_blank");
|
||||
});
|
||||
|
||||
// ======================================
|
||||
// 폴더 탐색 기능
|
||||
// ======================================
|
||||
var currentBrowsePath = '';
|
||||
var parentPath = null;
|
||||
|
||||
$('#browse_folder_btn').on('click', function() {
|
||||
var initialPath = $('#anilife_download_path').val() || '';
|
||||
loadFolderList(initialPath);
|
||||
$('#folderBrowserModal').modal('show');
|
||||
});
|
||||
|
||||
function loadFolderList(path) {
|
||||
$('#folder_list').html('<div class="text-center text-muted py-4"><i class="bi bi-arrow-repeat"></i> 로딩 중...</div>');
|
||||
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/' + sub + '/browse_dir',
|
||||
type: 'POST',
|
||||
data: { path: path },
|
||||
dataType: 'json',
|
||||
success: function(ret) {
|
||||
if (ret.ret === 'success') {
|
||||
currentBrowsePath = ret.current_path;
|
||||
parentPath = ret.parent_path;
|
||||
$('#current_path_display').text(currentBrowsePath);
|
||||
$('#folder_go_up').prop('disabled', !parentPath);
|
||||
|
||||
var html = '';
|
||||
if (parentPath) {
|
||||
html += '<div class="folder-item folder-parent d-flex align-items-center p-2 rounded" data-path="' + escapeHtml(parentPath) + '" style="cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.1);">';
|
||||
html += '<i class="bi bi-folder-symlink text-info mr-2"></i><span class="text-light">..</span><span class="text-muted ml-2">(상위 폴더)</span></div>';
|
||||
}
|
||||
html += '<div class="folder-item folder-current d-flex align-items-center p-2 rounded" data-path="' + escapeHtml(currentBrowsePath) + '" style="cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.05);">';
|
||||
html += '<i class="bi bi-folder-check text-success mr-2"></i><span class="text-light">.</span><span class="text-muted ml-2">(현재 폴더)</span></div>';
|
||||
|
||||
if (ret.directories.length === 0) {
|
||||
html += '<div class="text-center text-muted py-3"><small>하위 폴더 없음</small></div>';
|
||||
} else {
|
||||
for (var i = 0; i < ret.directories.length; i++) {
|
||||
var dir = ret.directories[i];
|
||||
html += '<div class="folder-item d-flex align-items-center p-2 rounded" data-path="' + escapeHtml(dir.path) + '" style="cursor: pointer;">';
|
||||
html += '<i class="bi bi-folder-fill text-warning mr-2"></i><span class="text-light">' + escapeHtml(dir.name) + '</span></div>';
|
||||
}
|
||||
}
|
||||
$('#folder_list').html(html);
|
||||
} else {
|
||||
$('#folder_list').html('<div class="text-center text-danger py-4">로드 실패: ' + (ret.error || '알 수 없는 오류') + '</div>');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$('#folder_list').html('<div class="text-center text-danger py-4">에러: ' + error + '</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#folder_list').on('dblclick', '.folder-item', function() { loadFolderList($(this).data('path')); });
|
||||
$('#folder_list').on('click', '.folder-item', function() {
|
||||
$('.folder-item').removeClass('selected').css('background', '');
|
||||
$(this).addClass('selected').css('background', 'rgba(59, 130, 246, 0.3)');
|
||||
currentBrowsePath = $(this).data('path');
|
||||
$('#current_path_display').text(currentBrowsePath);
|
||||
});
|
||||
$('#folder_go_up').on('click', function() { if (parentPath) loadFolderList(parentPath); });
|
||||
$('#folder_select_btn').on('click', function() {
|
||||
$('#anilife_download_path').val(currentBrowsePath);
|
||||
$('#folderBrowserModal').modal('hide');
|
||||
$.notify('저장 폴더가 설정되었습니다: ' + currentBrowsePath, {type: 'success'});
|
||||
});
|
||||
function escapeHtml(text) { var div = document.createElement('div'); div.appendChild(document.createTextNode(text)); return div.innerHTML; }
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user