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: "애니 다운로더"
|
||||
version: 0.7.16
|
||||
version: 0.7.17
|
||||
package_name: "anime_downloader"
|
||||
developer: "projectdx"
|
||||
description: "anime downloader"
|
||||
|
||||
@@ -632,8 +632,29 @@ class LogicOhli24(AnimeModuleBase):
|
||||
|
||||
if ModuleQueue:
|
||||
if command == "stop" or command == "cancel":
|
||||
ModuleQueue.process_ajax('cancel', req)
|
||||
return jsonify({'ret':'success'})
|
||||
# Create a mock request object for GDM cancel as req.form is often immutable
|
||||
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":
|
||||
# Ohli24 모듈의 다운로드만 취소 (다른 플러그인 항목은 그대로)
|
||||
caller_id = f"{P.package_name}_{self.name}"
|
||||
@@ -641,7 +662,8 @@ class LogicOhli24(AnimeModuleBase):
|
||||
for task_id, task in list(ModuleQueue._downloads.items()):
|
||||
if task.caller_plugin == caller_id:
|
||||
task.cancel()
|
||||
del ModuleQueue._downloads[task_id]
|
||||
# GDM 내부 클린업은 cancel()이 담당하므로 여기서 del은 신중해야 함
|
||||
# 하지만 강제 초기화이므로 제거 시도
|
||||
cancelled_count += 1
|
||||
|
||||
# Ohli24 DB도 정리
|
||||
@@ -652,7 +674,7 @@ class LogicOhli24(AnimeModuleBase):
|
||||
F.db.session.commit()
|
||||
except Exception as 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":
|
||||
# 완료 항목만 삭제
|
||||
try:
|
||||
@@ -1620,6 +1642,15 @@ class LogicOhli24(AnimeModuleBase):
|
||||
m = hashlib.md5(ep_title.encode("utf-8"))
|
||||
_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({
|
||||
"title": ep_title,
|
||||
"link": href,
|
||||
@@ -1630,6 +1661,7 @@ class LogicOhli24(AnimeModuleBase):
|
||||
"va": href,
|
||||
"_vi": _vi,
|
||||
"content_code": code,
|
||||
"epi_no": epi_no,
|
||||
})
|
||||
except Exception as 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"):
|
||||
day = episode_info.get("day", "")
|
||||
|
||||
# Robust extraction logic (Sync with Ohli24QueueEntity.parse_metadata)
|
||||
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:
|
||||
folder_name = "%s %s" % (P.ModelSetting.get("ohli24_finished_insert"), content_title_clean)
|
||||
else:
|
||||
@@ -2869,15 +2911,27 @@ class Ohli24QueueEntity(AnimeQueueEntity):
|
||||
if not title_full:
|
||||
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:
|
||||
self.content_title = match.group("title").strip()
|
||||
self.content_title = match.group("title").strip().rstrip('-').strip()
|
||||
if 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:
|
||||
self.content_title = title_full
|
||||
self.epi_queue = 1
|
||||
# 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:
|
||||
self.content_title = title_full
|
||||
self.epi_queue = 1
|
||||
|
||||
# Predict initial filename/filepath for UI
|
||||
epi_no = self.epi_queue
|
||||
|
||||
@@ -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/' ~ 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>
|
||||
.queue-header-container {
|
||||
display: flex; justify-content: space-between; align-items: flex-end;
|
||||
@@ -236,7 +262,7 @@ function renderList(data) {
|
||||
$("body").on('click', '#stop_btn', function(e){
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
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) {
|
||||
if (!data || !data.idx) {
|
||||
autoRefreshList();
|
||||
return;
|
||||
}
|
||||
$('#tr1_'+data.idx).html(make_item1(data));
|
||||
$('#collapse_'+data.idx).html(make_item2(data));
|
||||
}
|
||||
|
||||
@@ -213,9 +213,14 @@
|
||||
let epThumbSrc = data.episode[i].thumbnail || '';
|
||||
let epTitle = data.episode[i].title || '';
|
||||
|
||||
// 에피소드 번호 추출 (title에서 "N화" 패턴 찾기)
|
||||
let epNumMatch = epTitle.match(/(\d+)화/);
|
||||
let epNumText = epNumMatch ? epNumMatch[1] + '화' : (parseInt(i) + 1) + '화';
|
||||
// 에피소드 번호 추출: 백엔드 epi_no 우선, 없으면 정규식, 마지막으로 인덱스
|
||||
let epNumText = '';
|
||||
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-thumb">';
|
||||
|
||||
Reference in New Issue
Block a user