Bump version to 0.7.17: Fix Ohli24 naming, queue controls, and analysis badge mismatch
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
title: "애니 다운로더"
|
title: "애니 다운로더"
|
||||||
version: 0.7.16
|
version: 0.7.17
|
||||||
package_name: "anime_downloader"
|
package_name: "anime_downloader"
|
||||||
developer: "projectdx"
|
developer: "projectdx"
|
||||||
description: "anime downloader"
|
description: "anime downloader"
|
||||||
|
|||||||
@@ -632,8 +632,29 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
|
|
||||||
if ModuleQueue:
|
if ModuleQueue:
|
||||||
if command == "stop" or command == "cancel":
|
if command == "stop" or command == "cancel":
|
||||||
ModuleQueue.process_ajax('cancel', req)
|
# Create a mock request object for GDM cancel as req.form is often immutable
|
||||||
return jsonify({'ret':'success'})
|
class MockRequest:
|
||||||
|
def __init__(self, form_data):
|
||||||
|
self.form = form_data
|
||||||
|
|
||||||
|
mock_req = MockRequest({"id": entity_id})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to call process_ajax on what we have
|
||||||
|
ret = ModuleQueue.process_ajax('cancel', mock_req)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delegate cancel to ModuleQueue: {e}")
|
||||||
|
# Fallback: Find the instance via P if available
|
||||||
|
try:
|
||||||
|
from gommi_downloader_manager.setup import P as GDM_P
|
||||||
|
if GDM_P and hasattr(GDM_P, 'module_list'):
|
||||||
|
for m in GDM_P.module_list:
|
||||||
|
if m.name == 'queue':
|
||||||
|
ret = m.process_ajax('cancel', mock_req)
|
||||||
|
break
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
return jsonify({'ret':'success', 'data': {'idx': entity_id, 'status_str': 'STOP', 'status_kor': '중지'}})
|
||||||
elif command == "reset":
|
elif command == "reset":
|
||||||
# Ohli24 모듈의 다운로드만 취소 (다른 플러그인 항목은 그대로)
|
# Ohli24 모듈의 다운로드만 취소 (다른 플러그인 항목은 그대로)
|
||||||
caller_id = f"{P.package_name}_{self.name}"
|
caller_id = f"{P.package_name}_{self.name}"
|
||||||
@@ -641,7 +662,8 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
for task_id, task in list(ModuleQueue._downloads.items()):
|
for task_id, task in list(ModuleQueue._downloads.items()):
|
||||||
if task.caller_plugin == caller_id:
|
if task.caller_plugin == caller_id:
|
||||||
task.cancel()
|
task.cancel()
|
||||||
del ModuleQueue._downloads[task_id]
|
# GDM 내부 클린업은 cancel()이 담당하므로 여기서 del은 신중해야 함
|
||||||
|
# 하지만 강제 초기화이므로 제거 시도
|
||||||
cancelled_count += 1
|
cancelled_count += 1
|
||||||
|
|
||||||
# Ohli24 DB도 정리
|
# Ohli24 DB도 정리
|
||||||
@@ -652,7 +674,7 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
F.db.session.commit()
|
F.db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to clear Ohli24 DB: {e}")
|
logger.error(f"Failed to clear Ohli24 DB: {e}")
|
||||||
return jsonify({'ret':'notify', 'log':f'{cancelled_count}개 Ohli24 항목이 초기화되었습니다.'})
|
return jsonify({'ret':'success', 'log':f'{cancelled_count}개 Ohli24 항목이 초기화되었습니다.'})
|
||||||
elif command == "delete_completed":
|
elif command == "delete_completed":
|
||||||
# 완료 항목만 삭제
|
# 완료 항목만 삭제
|
||||||
try:
|
try:
|
||||||
@@ -1620,6 +1642,15 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
m = hashlib.md5(ep_title.encode("utf-8"))
|
m = hashlib.md5(ep_title.encode("utf-8"))
|
||||||
_vi = m.hexdigest()
|
_vi = m.hexdigest()
|
||||||
|
|
||||||
|
# Parse episode number for UI badge
|
||||||
|
epi_no = None
|
||||||
|
ep_match = re.search(r"(\d+(?:\.\d+)?)[\s\.\…화회]*$", ep_title)
|
||||||
|
if ep_match:
|
||||||
|
try:
|
||||||
|
epi_val = float(ep_match.group(1))
|
||||||
|
epi_no = int(epi_val) if epi_val.is_integer() else epi_val
|
||||||
|
except: pass
|
||||||
|
|
||||||
episodes.append({
|
episodes.append({
|
||||||
"title": ep_title,
|
"title": ep_title,
|
||||||
"link": href,
|
"link": href,
|
||||||
@@ -1630,6 +1661,7 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
"va": href,
|
"va": href,
|
||||||
"_vi": _vi,
|
"_vi": _vi,
|
||||||
"content_code": code,
|
"content_code": code,
|
||||||
|
"epi_no": epi_no,
|
||||||
})
|
})
|
||||||
except Exception as ep_err:
|
except Exception as ep_err:
|
||||||
logger.warning(f"Episode parse error: {ep_err}")
|
logger.warning(f"Episode parse error: {ep_err}")
|
||||||
@@ -2496,7 +2528,17 @@ class LogicOhli24(AnimeModuleBase):
|
|||||||
|
|
||||||
if P.ModelSetting.get_bool("ohli24_auto_make_folder"):
|
if P.ModelSetting.get_bool("ohli24_auto_make_folder"):
|
||||||
day = episode_info.get("day", "")
|
day = episode_info.get("day", "")
|
||||||
|
|
||||||
|
# Robust extraction logic (Sync with Ohli24QueueEntity.parse_metadata)
|
||||||
content_title_clean = match.group("title").strip() if match else title
|
content_title_clean = match.group("title").strip() if match else title
|
||||||
|
if not match:
|
||||||
|
# Fallback for truncated titles (e.g. "Long Title 6…")
|
||||||
|
fallback_match = re.search(r"(?P<title>.*?)\s*(?P<epi_no>\d+(?:\.\d+)?)(?:\.+|…)?\s*[^\d]*$", title.strip())
|
||||||
|
if fallback_match:
|
||||||
|
content_title_clean = fallback_match.group("title").strip().rstrip('-').strip()
|
||||||
|
else:
|
||||||
|
content_title_clean = title
|
||||||
|
|
||||||
if "완결" in day:
|
if "완결" in day:
|
||||||
folder_name = "%s %s" % (P.ModelSetting.get("ohli24_finished_insert"), content_title_clean)
|
folder_name = "%s %s" % (P.ModelSetting.get("ohli24_finished_insert"), content_title_clean)
|
||||||
else:
|
else:
|
||||||
@@ -2869,12 +2911,24 @@ class Ohli24QueueEntity(AnimeQueueEntity):
|
|||||||
if not title_full:
|
if not title_full:
|
||||||
return
|
return
|
||||||
|
|
||||||
match = re.compile(r"(?P<title>.*?)\s*((?P<season>\d+)기)?\s*((?P<epi_no>\d+)화)").search(title_full)
|
# Improved Regex: Handle optional [-(, optional season, and various episode suffixes
|
||||||
|
regex_main = re.compile(r"(?P<title>.*?)\s*(?:[\-\(\[])?\s*((?P<season>\d+)기)?\s*(?P<epi_no>\d+(?:\.\d+)?)\s*(?:화|회|part|part\s*\d+)?\s*(?:\(完\))?\s*(?:[\)\]])?$")
|
||||||
|
match = regex_main.search(title_full.strip())
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
self.content_title = match.group("title").strip()
|
self.content_title = match.group("title").strip().rstrip('-').strip()
|
||||||
if match.group("season"):
|
if match.group("season"):
|
||||||
self.season = int(match.group("season"))
|
self.season = int(match.group("season"))
|
||||||
self.epi_queue = int(match.group("epi_no"))
|
self.epi_queue = float(match.group("epi_no"))
|
||||||
|
if self.epi_queue.is_integer():
|
||||||
|
self.epi_queue = int(self.epi_queue)
|
||||||
|
else:
|
||||||
|
# Fallback for truncated titles or unusual suffixes (e.g. "Title 6…")
|
||||||
|
fallback_match = re.search(r"(?P<title>.*?)\s*(?P<epi_no>\d+(?:\.\d+)?)(?:\.+|…)?\s*[^\d]*$", title_full.strip())
|
||||||
|
if fallback_match:
|
||||||
|
self.content_title = fallback_match.group("title").strip().rstrip('-').strip()
|
||||||
|
epi_val = float(fallback_match.group("epi_no"))
|
||||||
|
self.epi_queue = int(epi_val) if epi_val.is_integer() else epi_val
|
||||||
else:
|
else:
|
||||||
self.content_title = title_full
|
self.content_title = title_full
|
||||||
self.epi_queue = 1
|
self.epi_queue = 1
|
||||||
|
|||||||
@@ -3,6 +3,32 @@
|
|||||||
<link rel="stylesheet" href="{{ url_for('.static', filename='css/mobile_custom.css') }}"/>
|
<link rel="stylesheet" href="{{ url_for('.static', filename='css/mobile_custom.css') }}"/>
|
||||||
<link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/>
|
<link rel="stylesheet" href="{{ url_for('.static', filename='css/' ~ arg['sub'] ~ '.css') }}"/>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function globalConfirmModal(title, body, func) {
|
||||||
|
$("#confirm_title").html(title);
|
||||||
|
$("#confirm_body").html(body);
|
||||||
|
|
||||||
|
// Remove previous handlers to prevent accumulation
|
||||||
|
$("body").off('click', '#confirm_button').on('click', '#confirm_button', function(e){
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (typeof func === 'function') {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
$("body").off('click', '#confirm_button');
|
||||||
|
$("#confirm_modal").modal('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up listener when modal is closed (any way)
|
||||||
|
$("#confirm_modal").one('hidden.bs.modal', function () {
|
||||||
|
$("body").off('click', '#confirm_button');
|
||||||
|
$('#confirm_button').removeAttr('onclick');
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#confirm_modal").modal();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.queue-header-container {
|
.queue-header-container {
|
||||||
display: flex; justify-content: space-between; align-items: flex-end;
|
display: flex; justify-content: space-between; align-items: flex-end;
|
||||||
@@ -236,7 +262,7 @@ function renderList(data) {
|
|||||||
$("body").on('click', '#stop_btn', function(e){
|
$("body").on('click', '#stop_btn', function(e){
|
||||||
e.stopPropagation(); e.preventDefault();
|
e.stopPropagation(); e.preventDefault();
|
||||||
globalSendCommand('stop', $(this).data('idx'), null, null, function(ret){
|
globalSendCommand('stop', $(this).data('idx'), null, null, function(ret){
|
||||||
refresh_item(ret.data);
|
autoRefreshList();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -265,6 +291,10 @@ $("body").on('click', '#delete_btn', function(e){
|
|||||||
});
|
});
|
||||||
|
|
||||||
function refresh_item(data) {
|
function refresh_item(data) {
|
||||||
|
if (!data || !data.idx) {
|
||||||
|
autoRefreshList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
$('#tr1_'+data.idx).html(make_item1(data));
|
$('#tr1_'+data.idx).html(make_item1(data));
|
||||||
$('#collapse_'+data.idx).html(make_item2(data));
|
$('#collapse_'+data.idx).html(make_item2(data));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,9 +213,14 @@
|
|||||||
let epThumbSrc = data.episode[i].thumbnail || '';
|
let epThumbSrc = data.episode[i].thumbnail || '';
|
||||||
let epTitle = data.episode[i].title || '';
|
let epTitle = data.episode[i].title || '';
|
||||||
|
|
||||||
// 에피소드 번호 추출 (title에서 "N화" 패턴 찾기)
|
// 에피소드 번호 추출: 백엔드 epi_no 우선, 없으면 정규식, 마지막으로 인덱스
|
||||||
let epNumMatch = epTitle.match(/(\d+)화/);
|
let epNumText = '';
|
||||||
let epNumText = epNumMatch ? epNumMatch[1] + '화' : (parseInt(i) + 1) + '화';
|
if (data.episode[i].epi_no !== undefined && data.episode[i].epi_no !== null) {
|
||||||
|
epNumText = data.episode[i].epi_no + '화';
|
||||||
|
} else {
|
||||||
|
let epNumMatch = epTitle.match(/(\d+(?:\.\d+)?)[\s\.\…화회]*$/);
|
||||||
|
epNumText = epNumMatch ? epNumMatch[1] + '화' : (parseInt(i) + 1) + '화';
|
||||||
|
}
|
||||||
|
|
||||||
str += '<div class="episode-card">';
|
str += '<div class="episode-card">';
|
||||||
str += '<div class="episode-thumb">';
|
str += '<div class="episode-thumb">';
|
||||||
|
|||||||
Reference in New Issue
Block a user