274 lines
10 KiB
Python
274 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
import json
|
|
import os
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import desc, or_
|
|
|
|
from .setup import *
|
|
|
|
|
|
ModelSetting = P.ModelSetting
|
|
|
|
|
|
class ModelLinkkfProgram(ModelBase):
|
|
P = P
|
|
__tablename__ = "linkkf_program"
|
|
__bind_key__ = P.package_name
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
contents_json = db.Column(db.JSON)
|
|
created_time = db.Column(db.DateTime)
|
|
programcode = db.Column(db.String)
|
|
save_folder = db.Column(db.String)
|
|
season = db.Column(db.Integer)
|
|
|
|
def __init__(self, data):
|
|
self.created_time = datetime.now()
|
|
self.programcode = data["code"]
|
|
self.save_folder = data["title"]
|
|
self.season = data["season"]
|
|
|
|
def set_info(self, data):
|
|
self.contents_json = data
|
|
self.programcode = data["code"]
|
|
self.save_folder = data["save_folder"]
|
|
self.season = data["season"]
|
|
|
|
|
|
class ModelLinkkf(ModelBase):
|
|
P = P
|
|
__tablename__ = "linkkf_auto_episode"
|
|
__bind_key__ = P.package_name
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
contents_json = db.Column(db.JSON)
|
|
created_time = db.Column(db.DateTime)
|
|
completed_time = db.Column(db.DateTime)
|
|
|
|
programcode = db.Column(db.String)
|
|
episodecode = db.Column(db.String)
|
|
filename = db.Column(db.String)
|
|
duration = db.Column(db.Integer)
|
|
start_time = db.Column(db.DateTime)
|
|
end_time = db.Column(db.DateTime)
|
|
download_time = db.Column(db.Integer)
|
|
completed = db.Column(db.Boolean)
|
|
user_abort = db.Column(db.Boolean)
|
|
pf_abort = db.Column(db.Boolean)
|
|
etc_abort = db.Column(db.Integer)
|
|
ffmpeg_status = db.Column(db.Integer)
|
|
temp_path = db.Column(db.String)
|
|
save_path = db.Column(db.String)
|
|
pf = db.Column(db.Integer)
|
|
retry = db.Column(db.Integer)
|
|
filesize = db.Column(db.Integer)
|
|
filesize_str = db.Column(db.String)
|
|
download_speed = db.Column(db.String)
|
|
call = db.Column(db.String)
|
|
status = db.Column(db.String)
|
|
linkkf_info = db.Column(db.JSON)
|
|
|
|
def __init__(self, call, info):
|
|
self.created_time = datetime.now()
|
|
self.completed = False
|
|
self.start_time = datetime.now()
|
|
self.user_abort = False
|
|
self.pf_abort = False
|
|
self.etc_abort = 0
|
|
self.ffmpeg_status = -1
|
|
self.pf = 0
|
|
self.retry = 0
|
|
self.call = call
|
|
self.set_info(info)
|
|
|
|
def as_dict(self):
|
|
ret = super().as_dict()
|
|
if ret.get("status") in [None, ""]:
|
|
if self.completed is True:
|
|
ret["status"] = "completed"
|
|
elif self.user_abort is True:
|
|
ret["status"] = "canceled"
|
|
elif self.pf_abort is True or (self.etc_abort is not None and int(self.etc_abort) > 0):
|
|
ret["status"] = "error"
|
|
elif self.ffmpeg_status in [0, 5]:
|
|
ret["status"] = "downloading"
|
|
ret["created_time"] = self.created_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
ret["completed_time"] = (
|
|
self.completed_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
if self.completed_time is not None
|
|
else None
|
|
)
|
|
return ret
|
|
|
|
def set_info(self, data):
|
|
self.contents_json = data
|
|
self.programcode = data["program_code"]
|
|
self.episodecode = data["code"]
|
|
self.filename = data.get("filename", self.filename)
|
|
self.linkkf_info = data
|
|
if self.status in [None, ""]:
|
|
self.status = "waiting"
|
|
|
|
@staticmethod
|
|
def _normalize_json_data(data):
|
|
if isinstance(data, dict):
|
|
return data
|
|
if isinstance(data, str) and data.strip() != "":
|
|
try:
|
|
return json.loads(data)
|
|
except Exception:
|
|
return {}
|
|
return {}
|
|
|
|
@classmethod
|
|
def sync_completed_from_filesystem(cls):
|
|
with F.app.app_context():
|
|
changed = 0
|
|
rows = F.db.session.query(cls).filter(
|
|
or_(cls.status != "completed", cls.status.is_(None), cls.completed.is_(False))
|
|
).all()
|
|
for row in rows:
|
|
info = cls._normalize_json_data(row.linkkf_info) or cls._normalize_json_data(row.contents_json)
|
|
save_path = row.save_path or info.get("save_path")
|
|
filename = row.filename or info.get("filename")
|
|
if not save_path or not filename:
|
|
continue
|
|
fullpath = os.path.join(save_path, filename)
|
|
if os.path.exists(fullpath) is False:
|
|
continue
|
|
row.completed = True
|
|
row.user_abort = False
|
|
row.pf_abort = False
|
|
row.etc_abort = 0
|
|
row.ffmpeg_status = 7 if row.ffmpeg_status in [None, -1, 0, 5] else row.ffmpeg_status
|
|
row.status = "completed"
|
|
file_time = datetime.fromtimestamp(os.path.getmtime(fullpath))
|
|
if row.end_time is None:
|
|
row.end_time = file_time
|
|
if row.completed_time is None:
|
|
row.completed_time = file_time
|
|
changed += 1
|
|
if changed > 0:
|
|
F.db.session.commit()
|
|
return changed
|
|
|
|
@classmethod
|
|
def migrate_existing_rows(cls):
|
|
with F.app.app_context():
|
|
changed = 0
|
|
rows = F.db.session.query(cls).all()
|
|
for row in rows:
|
|
info = cls._normalize_json_data(row.contents_json) or cls._normalize_json_data(row.linkkf_info)
|
|
updated = False
|
|
if row.programcode in [None, ""] and info.get("program_code"):
|
|
row.programcode = info.get("program_code")
|
|
updated = True
|
|
if row.episodecode in [None, ""] and info.get("code"):
|
|
row.episodecode = info.get("code")
|
|
updated = True
|
|
if row.filename in [None, ""] and info.get("filename"):
|
|
row.filename = info.get("filename")
|
|
updated = True
|
|
if row.linkkf_info in [None, {}] and info:
|
|
row.linkkf_info = info
|
|
updated = True
|
|
if row.status in [None, ""]:
|
|
if row.completed is True:
|
|
row.status = "completed"
|
|
elif row.user_abort is True:
|
|
row.status = "canceled"
|
|
elif row.pf_abort is True or (row.etc_abort is not None and int(row.etc_abort) > 0):
|
|
row.status = "error"
|
|
elif row.ffmpeg_status in [0, 5]:
|
|
row.status = "downloading"
|
|
else:
|
|
row.status = "waiting"
|
|
updated = True
|
|
if updated:
|
|
changed += 1
|
|
if changed > 0:
|
|
F.db.session.commit()
|
|
changed += cls.sync_completed_from_filesystem()
|
|
return changed
|
|
|
|
@classmethod
|
|
def web_list(cls, req):
|
|
with F.app.app_context():
|
|
ret = {}
|
|
cls.sync_completed_from_filesystem()
|
|
page = int(req.form["page"]) if "page" in req.form else 1
|
|
page_size = 30
|
|
search = req.form["search_word"] if "search_word" in req.form else req.form.get("keyword", "")
|
|
option = req.form["option"] if "option" in req.form else req.form.get("option1", "finished")
|
|
order = req.form["order"] if "order" in req.form else "desc"
|
|
|
|
query = cls.make_query(search=search, order=order, option=option)
|
|
count = query.count()
|
|
query = query.limit(page_size).offset((page - 1) * page_size)
|
|
lists = query.all()
|
|
ret["list"] = [item.as_dict() for item in lists]
|
|
ret["paging"] = cls.get_paging_info(count, page, page_size)
|
|
return ret
|
|
|
|
@classmethod
|
|
def get_by_linkkf_id(cls, linkkf_id):
|
|
with F.app.app_context():
|
|
return F.db.session.query(cls).filter_by(episodecode=linkkf_id).first()
|
|
|
|
@classmethod
|
|
def make_query(cls, search="", order="desc", option="all"):
|
|
query = F.db.session.query(cls)
|
|
if search is not None and search != "":
|
|
if "|" in search:
|
|
conditions = []
|
|
for token in [x.strip() for x in search.split("|") if x.strip()]:
|
|
conditions.append(cls.filename.like(f"%{token}%"))
|
|
conditions.append(cls.programcode.like(f"%{token}%"))
|
|
if conditions:
|
|
query = query.filter(or_(*conditions))
|
|
elif "," in search:
|
|
for token in [x.strip() for x in search.split(",") if x.strip()]:
|
|
query = query.filter(
|
|
or_(
|
|
cls.filename.like(f"%{token}%"),
|
|
cls.programcode.like(f"%{token}%"),
|
|
)
|
|
)
|
|
else:
|
|
query = query.filter(
|
|
or_(
|
|
cls.filename.like(f"%{search}%"),
|
|
cls.programcode.like(f"%{search}%"),
|
|
)
|
|
)
|
|
if option == "completed":
|
|
query = query.filter(or_(cls.status == "completed", cls.completed.is_(True)))
|
|
elif option == "canceled":
|
|
query = query.filter(or_(cls.status == "canceled", cls.user_abort.is_(True)))
|
|
elif option == "error":
|
|
query = query.filter(
|
|
or_(
|
|
cls.status == "error",
|
|
cls.pf_abort.is_(True),
|
|
cls.etc_abort > 0,
|
|
)
|
|
)
|
|
elif option == "finished":
|
|
query = query.filter(
|
|
or_(
|
|
cls.status.in_(["completed", "error", "canceled"]),
|
|
cls.completed.is_(True),
|
|
cls.user_abort.is_(True),
|
|
cls.pf_abort.is_(True),
|
|
cls.etc_abort > 0,
|
|
)
|
|
)
|
|
elif option == "downloading":
|
|
query = query.filter(cls.status == "downloading")
|
|
if order == "desc":
|
|
query = query.order_by(desc(cls.id))
|
|
else:
|
|
query = query.order_by(cls.id)
|
|
return query
|