Files
anime_downloader/model_base.py

177 lines
7.4 KiB
Python

from .lib.ffmpeg_queue_v1 import FfmpegQueueEntity
from .lib.downloader_factory import DownloaderFactory
from framework import db
import os, shutil, re, logging
from datetime import datetime
logger = logging.getLogger(__name__)
class AnimeQueueEntity(FfmpegQueueEntity):
def __init__(self, P, module_logic, info):
super(AnimeQueueEntity, self).__init__(P, module_logic, info)
self.P = P
def get_downloader(self, video_url, output_file, callback=None, **kwargs):
"""Returns the appropriate downloader using the factory."""
method = self.P.ModelSetting.get(f"{self.module_logic.name}_download_method")
threads = self.P.ModelSetting.get_int(f"{self.module_logic.name}_download_threads")
if threads is None:
threads = 16
# Prepare headers and proxy
headers = self.headers
if headers is None:
headers = getattr(self.module_logic, 'headers', None)
proxy = getattr(self, 'proxy', None)
if proxy is None:
proxy = getattr(self.module_logic, 'proxy', None)
# Build downloader arguments
args = {
'cookies_file': getattr(self, 'cookies_file', None),
'iframe_src': getattr(self, 'iframe_src', None),
'callback_id': self.entity_id,
'callback_function': kwargs.get('callback_function') or getattr(self, 'ffmpeg_listener', None)
}
# Site specific referer defaults
if self.module_logic.name == 'ohli24':
args['referer_url'] = "https://ani.ohli24.com/"
elif self.module_logic.name == 'anilife':
args['referer_url'] = self.P.ModelSetting.get("anilife_url", "https://anilife.live")
args.update(kwargs)
return DownloaderFactory.get_downloader(
method=method,
video_url=video_url,
output_file=output_file,
headers=headers,
callback=callback,
proxy=proxy,
threads=threads,
**args
)
def prepare_extra(self):
"""
[Lazy Extraction]
다운로드 직전에 호출되는 무거운 분석 로직 (URL 추출 등).
자식 클래스에서 오버라이드하여 구현합니다.
"""
pass
def refresh_status(self):
"""Common status refresh logic"""
if self.ffmpeg_status == -1:
self.ffmpeg_status_kor = "대기"
elif self.ffmpeg_status == 0:
self.ffmpeg_status_kor = "대기" # Waiting in queue
elif self.ffmpeg_status == 1:
self.ffmpeg_status_kor = "분석 중"
elif self.ffmpeg_status == 2:
self.ffmpeg_status_kor = "다운로드 중"
elif self.ffmpeg_status == 3:
self.ffmpeg_status_kor = "변환 중" # post-processing
elif self.ffmpeg_status == 4:
self.ffmpeg_status_kor = "실패"
elif self.ffmpeg_status == 5:
self.ffmpeg_status_kor = "다운로드 중" # downloading
elif self.ffmpeg_status == 6:
self.ffmpeg_status_kor = "취소"
elif self.ffmpeg_status == 7:
self.ffmpeg_status_kor = "완료"
elif self.ffmpeg_status == 8:
self.ffmpeg_status_kor = "완료(이미 있음)"
elif self.ffmpeg_status == 9:
self.ffmpeg_status_kor = "실패(파일 없음)"
def download_completed(self):
"""Common file move logic"""
try:
# LogicCommon to move file
# Specific implementation might vary but usually:
# 1. Check self.savepath
# 2. Check self.filename
# 3. Move self.filepath to dest
if not self.savepath or not self.filename:
return
if not os.path.exists(self.savepath):
os.makedirs(self.savepath)
# Clean filename
# self.filename = Util.change_text_for_use_filename(self.filename)
# (Assuming Util available or do basic replace)
self.filename = re.sub(r'[\\/:*?"<>|]', '', self.filename)
dest_path = os.path.join(self.savepath, self.filename)
# If already at destination, just return
if self.filepath == dest_path:
self.ffmpeg_status = 7
self.ffmpeg_status_kor = "완료"
self.end_time = datetime.now()
self._update_db_status()
return
if self.filepath and os.path.exists(self.filepath):
if os.path.exists(dest_path):
self.P.logger.info(f"Destination file exists, removing to overwrite: {dest_path}")
os.remove(dest_path)
shutil.move(self.filepath, dest_path)
self.filepath = dest_path # Update filepath to new location
self.ffmpeg_status = 7
self.ffmpeg_status_kor = "완료"
self.end_time = datetime.now()
self._update_db_status()
except Exception as e:
self.P.logger.error(f"Download completed error: {e}")
self.ffmpeg_status = 4
self.ffmpeg_status_kor = "이동 실패"
def _update_db_status(self):
"""Update DB status to completed - generic method for all sites."""
try:
from framework import app
with app.app_context():
# Get the web_list_model from module_logic
model_class = getattr(self.module_logic, 'web_list_model', None)
if model_class is None:
return
# Try to find the DB entity
db_entity = None
info = getattr(self, 'info', {})
# Anilife uses _id
if hasattr(model_class, 'get_by_anilife_id') and info.get('_id'):
db_entity = model_class.get_by_anilife_id(info['_id'])
# Ohli24 uses _id (via get_by_ohli24_id)
elif hasattr(model_class, 'get_by_ohli24_id') and info.get('_id'):
db_entity = model_class.get_by_ohli24_id(info['_id'])
# Linkkf uses _id (via get_by_linkkf_id)
elif hasattr(model_class, 'get_by_linkkf_id') and info.get('_id'):
db_entity = model_class.get_by_linkkf_id(info['_id'])
# Other modules might use get_by_id with db_id
elif hasattr(model_class, 'get_by_id') and info.get('db_id'):
db_entity = model_class.get_by_id(info['db_id'])
elif hasattr(model_class, 'get_by_content_code') and info.get('content_code'):
db_entity = model_class.query.filter_by(content_code=info['content_code'], episode_no=info.get('episode_no')).first()
if db_entity is not None:
db_entity.status = "completed"
db_entity.completed_time = datetime.now()
db_entity.filename = getattr(self, 'filename', None)
db_entity.save()
logger.info(f"[{self.module_logic.name}] DB status updated to 'completed': {info.get('title', 'Unknown')}")
except Exception as e:
logger.error(f"Failed to update DB status: {e}")
def info_dict(self, tmp):
"""Default valid implementation"""
return tmp