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:
2026-03-26 22:23:06 +09:00
parent 5a230e20f7
commit 9287889fdf
3 changed files with 66 additions and 9 deletions
+24
View File
@@ -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 -1
View File
@@ -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"
+40 -7
View File
@@ -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 %}