fix(ohli24): refresh queue progress without manual reload
- fix the queue socket namespace and framework event name for ohli24 - listen to GDM download_status updates and patch rendered rows in place - re-render silently when polling detects progress-only changes - bump patch version to 0.7.22
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
# 2026-03-26 작업 이력
|
||||||
|
|
||||||
|
## ohli24 큐 탭 초기화 버튼 오류 수정
|
||||||
|
|
||||||
|
- `ohli24` 큐 탭의 `초기화` 버튼 클릭 시 확인 모달 뒤에 `{ret: 'error'}`가 떨어지고 실제 초기화가 진행되지 않는 문제를 점검했다.
|
||||||
|
- 원인은 [mod_ohli24.py](/Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/mod_ohli24.py) 의 `queue_command` 처리에서 `req.form["entity_id"]`를 무조건 읽는 방식이었다.
|
||||||
|
- 큐 템플릿은 `reset`, `delete_completed` 요청 시 `entity_id` 없이 `globalSendCommand('reset', null, ...)` 형태로 보내므로, 서버가 `reset` 분기까지 내려가기 전에 실패할 수 있었다.
|
||||||
|
- `queue_command`를 [mod_anilife.py](/Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/mod_anilife.py) 와 같은 안전 패턴으로 정리해 `command`, `entity_id`를 `req.form.get(...)`로 읽도록 수정했다.
|
||||||
|
- `reset` 처리 시 GDM 다운로드 취소만 하지 않고, [ffmpeg_queue_v1.py](/Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/lib/ffmpeg_queue_v1.py) 의 `reset` 경로를 통해 `self.queue` 런타임 큐와 `entity_list`도 함께 비우도록 보강했다.
|
||||||
|
- 일반 큐 명령 fallback도 `entity_id`가 비어 있을 수 있는 경우를 고려해 `int(entity_id) if digit else 0` 패턴으로 안전화했다.
|
||||||
|
|
||||||
|
## 2026-03-26 검증
|
||||||
|
|
||||||
|
- `python -m py_compile /Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/mod_ohli24.py`: success
|
||||||
|
- 수정 후 [mod_ohli24.py](/Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/mod_ohli24.py) 의 `queue_command` 분기에서 `reset` 요청이 `entity_id` 없이도 처리 가능한지 코드 경로를 재확인했다.
|
||||||
|
|
||||||
|
## ohli24 큐 진행률 실시간 반영 보강
|
||||||
|
|
||||||
|
- [anime_downloader_ohli24_queue.html](/Volumes/WD/Users/Work/python/ff_dev_plugins/anime_downloader/templates/anime_downloader_ohli24_queue.html) 에서 큐 페이지 Socket.IO 네임스페이스가 `ohli24`가 아닌 `linkkf`로 하드코딩되어 있어, 모듈 전용 큐 이벤트를 정상 수신하지 못하는 문제를 확인했다.
|
||||||
|
- 같은 템플릿이 GDM 이벤트를 `status`로 수신하고 있었는데, 실제 [gommi_downloader_manager/mod_queue.py](/Volumes/WD/Users/Work/python/ff_dev_plugins/gommi_downloader_manager/mod_queue.py) 는 `/gommi_downloader_manager` 네임스페이스에 `download_status` 이벤트를 emit하고 있었다.
|
||||||
|
- 추가로 `autoRefreshList()`가 항목 개수 변화가 있을 때만 `renderList()`를 호출하고 있어, 진행률/속도만 변하는 구간에서는 폴링 fallback이 있어도 화면이 갱신되지 않았다.
|
||||||
|
- 템플릿을 `PACKAGE_NAME`, `MODULE_NAME` 기반 동적 네임스페이스로 바꾸고, framework 이벤트명도 `MODULE_NAME + '_status'`를 쓰도록 정리했다.
|
||||||
|
- GDM 소켓은 `download_status`를 수신하도록 맞췄고, 이미 렌더된 항목은 전체 새로고침 대신 progress/status/button 영역만 즉시 갱신하게 보강했다.
|
||||||
|
- 소켓 이벤트를 놓치더라도 현재 DOM과 서버 목록을 비교해 진행률 차이가 있으면 조용히 다시 렌더하도록 fallback을 추가했다.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
title: "애니 다운로더"
|
title: "애니 다운로더"
|
||||||
version: 0.7.21
|
version: 0.7.22
|
||||||
package_name: "anime_downloader"
|
package_name: "anime_downloader"
|
||||||
developer: "projectdx"
|
developer: "projectdx"
|
||||||
description: "anime downloader"
|
description: "anime downloader"
|
||||||
|
|||||||
@@ -150,11 +150,12 @@ $(document).ready(function(){
|
|||||||
|
|
||||||
var protocol = location.protocol;
|
var protocol = location.protocol;
|
||||||
var socketUrl = protocol + "//" + document.domain + ":" + location.port;
|
var socketUrl = protocol + "//" + document.domain + ":" + location.port;
|
||||||
|
var frameworkStatusEvent = MODULE_NAME + '_status';
|
||||||
|
|
||||||
// Queue 전용 소켓 시도
|
// Queue 전용 소켓 시도
|
||||||
var queueSocket = null;
|
var queueSocket = null;
|
||||||
try {
|
try {
|
||||||
queueSocket = io.connect(socketUrl + '/anime_downloader/linkkf/queue');
|
queueSocket = io.connect(socketUrl + '/' + PACKAGE_NAME + '/' + MODULE_NAME + '/queue');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Queue socket error:', e);
|
console.error('Queue socket error:', e);
|
||||||
}
|
}
|
||||||
@@ -162,7 +163,7 @@ $(document).ready(function(){
|
|||||||
var frameworkSocket = null;
|
var frameworkSocket = null;
|
||||||
try {
|
try {
|
||||||
frameworkSocket = io.connect(socketUrl + '/framework');
|
frameworkSocket = io.connect(socketUrl + '/framework');
|
||||||
frameworkSocket.on('linkkf_status', function(data) {
|
frameworkSocket.on(frameworkStatusEvent, function(data) {
|
||||||
status_html(data);
|
status_html(data);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -180,12 +181,16 @@ $(document).ready(function(){
|
|||||||
var gdmSocket = null;
|
var gdmSocket = null;
|
||||||
try {
|
try {
|
||||||
gdmSocket = io.connect(socketUrl + '/gommi_downloader_manager');
|
gdmSocket = io.connect(socketUrl + '/gommi_downloader_manager');
|
||||||
gdmSocket.on('status', function(data) {
|
gdmSocket.on('download_status', function(data) {
|
||||||
// 이 모듈과 관련된 작업만 처리
|
// 이 모듈과 관련된 작업만 처리
|
||||||
if (data.caller_plugin === PACKAGE_NAME + '_' + MODULE_NAME) {
|
if (data.caller_plugin === PACKAGE_NAME + '_' + MODULE_NAME) {
|
||||||
// GDM 데이터를 큐 형식으로 변환하여 UI 업데이트
|
if (document.getElementById("progress_" + data.callback_id)) {
|
||||||
// 수동 새로고침 없이 실시간 반영을 위해 renderList 호출 주기를 짧게 하거나 직접 UI 갱신
|
status_html(convertGdmStatus(data));
|
||||||
|
button_html(convertGdmStatus(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
silentFetchList(function(newList) {
|
silentFetchList(function(newList) {
|
||||||
|
current_list_length = newList.length;
|
||||||
renderList(newList);
|
renderList(newList);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -214,8 +219,9 @@ function silentFetchList(callback) {
|
|||||||
|
|
||||||
function autoRefreshList() {
|
function autoRefreshList() {
|
||||||
silentFetchList(function(data) {
|
silentFetchList(function(data) {
|
||||||
if (data.length !== current_list_length) {
|
var shouldRender = data.length !== current_list_length || hasRenderableChanges(data);
|
||||||
current_list_length = data.length;
|
current_list_length = data.length;
|
||||||
|
if (shouldRender) {
|
||||||
renderList(data);
|
renderList(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +252,34 @@ function on_start() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertGdmStatus(data) {
|
||||||
|
return {
|
||||||
|
idx: data.callback_id,
|
||||||
|
percent: data.progress,
|
||||||
|
status_str: data.status_str,
|
||||||
|
status_kor: data.status_kor,
|
||||||
|
current_pf_count: data.current_pf_count || 0,
|
||||||
|
current_speed: data.speed || '',
|
||||||
|
download_time: data.eta || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasRenderableChanges(data) {
|
||||||
|
if (!data || data.length === 0) return false;
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var item = data[i];
|
||||||
|
var progressEl = document.getElementById("progress_" + item.idx);
|
||||||
|
if (!progressEl) return true;
|
||||||
|
var currentPercent = parseInt(progressEl.style.width || progressEl.textContent || '0', 10);
|
||||||
|
if (currentPercent !== parseInt(item.percent || 0, 10)) return true;
|
||||||
|
var speedEl = document.getElementById("current_speed_" + item.idx);
|
||||||
|
if (speedEl && speedEl.textContent !== String(item.current_speed || '')) return true;
|
||||||
|
var statusEl = document.getElementById("status_" + item.idx);
|
||||||
|
if (statusEl && statusEl.textContent.indexOf(item.status_kor) === -1) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function renderList(data) {
|
function renderList(data) {
|
||||||
$("#list").html('');
|
$("#list").html('');
|
||||||
if (!data || data.length == 0) {
|
if (!data || data.length == 0) {
|
||||||
@@ -408,4 +442,3 @@ function status_html(data) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user