Add linkkf backend files

This commit is contained in:
2026-04-01 11:34:29 +09:00
parent bb93190624
commit 3dd08888d0
+273
View File
@@ -0,0 +1,273 @@
# -*- 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