mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-05-09 07:32:41 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b3434f5fd | ||
|
|
85e2b2be50 | ||
|
|
b666378c00 | ||
|
|
28ed22dc1b | ||
|
|
911ce84f5a |
@@ -64,6 +64,7 @@ async def _refresh_favorite_item_info(
|
||||
process_nfo=process_nfo,
|
||||
process_upper=process_upper,
|
||||
process_subtitle=process_subtitle,
|
||||
refresh_mode=True,
|
||||
)
|
||||
for item in items
|
||||
],
|
||||
|
||||
@@ -65,10 +65,6 @@ class FavoriteItem(Model):
|
||||
class Meta:
|
||||
unique_together = (("bvid", "favorite_list_id"),)
|
||||
|
||||
@property
|
||||
def safe_name(self) -> str:
|
||||
return self.name.replace("/", "_")
|
||||
|
||||
@property
|
||||
def tmp_video_path(self) -> Path:
|
||||
return Path(settings.path_mapper[self.favorite_list_id]) / f"tmp_{self.bvid}_video"
|
||||
|
||||
25
nfo.py
25
nfo.py
@@ -15,6 +15,11 @@ class Base:
|
||||
def to_xml(self) -> str:
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def escape(s: str) -> str:
|
||||
"""转义 xml 特殊字符"""
|
||||
return s.translate(str.maketrans({"<": "<", ">": ">", "&": "&", "'": "'", '"': """}))
|
||||
|
||||
async def to_file(self, path: Path) -> None:
|
||||
"""把 xml 写入文件"""
|
||||
async with aopen(path, "w", encoding="utf-8") as f:
|
||||
@@ -39,7 +44,7 @@ class EpisodeInfo(Base):
|
||||
<episodedetails>
|
||||
<plot />
|
||||
<outline />
|
||||
<title>{self.title}</title>
|
||||
<title>{self.escape(self.title)}</title>
|
||||
<season>{self.season}</season>
|
||||
<episode>{self.episode}</episode>
|
||||
</episodedetails>
|
||||
@@ -59,7 +64,7 @@ class Actor(Base):
|
||||
return f"""
|
||||
<actor>
|
||||
<name>{self.name}</name>
|
||||
<role>{self.role}</role>
|
||||
<role>{self.escape(self.role)}</role>
|
||||
</actor>
|
||||
""".strip()
|
||||
|
||||
@@ -88,13 +93,15 @@ class MovieInfo(Base):
|
||||
|
||||
def to_xml(self) -> str:
|
||||
actor = "\n".join(_.to_xml() for _ in self.actor)
|
||||
tags = "\n".join(f" <genre>{_}</genre>" for _ in self.tags) if isinstance(self.tags, list) else ""
|
||||
tags = (
|
||||
"\n".join(f" <genre>{self.escape(_)}</genre>" for _ in self.tags) if isinstance(self.tags, list) else ""
|
||||
)
|
||||
return f"""
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<movie>
|
||||
<plot><![CDATA[{self.plot}]]></plot>
|
||||
<plot><![CDATA[{self.escape(self.plot)}]]></plot>
|
||||
<outline />
|
||||
<title>{self.title}</title>
|
||||
<title>{self.escape(self.title)}</title>
|
||||
{actor}
|
||||
<year>{self.aired.year}</year>
|
||||
{tags}
|
||||
@@ -126,13 +133,15 @@ class TVShowInfo(Base):
|
||||
|
||||
def to_xml(self) -> str:
|
||||
actor = "\n".join(_.to_xml() for _ in self.actor)
|
||||
tags = "\n".join(f" <genre>{_}</genre>" for _ in self.tags) if isinstance(self.tags, list) else ""
|
||||
tags = (
|
||||
"\n".join(f" <genre>{self.escape(_)}</genre>" for _ in self.tags) if isinstance(self.tags, list) else ""
|
||||
)
|
||||
return f"""
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<tvshow>
|
||||
<plot><![CDATA[{self.plot}]]></plot>
|
||||
<plot><![CDATA[{self.escape(self.plot)}]]></plot>
|
||||
<outline />
|
||||
<title>{self.title}</title>
|
||||
<title>{self.escape(self.title)}</title>
|
||||
{actor}
|
||||
<year>{self.aired.year}</year>
|
||||
{tags}
|
||||
|
||||
122
processor.py
122
processor.py
@@ -71,6 +71,18 @@ async def update_favorite_item(medias: list[dict], fav_list: FavoriteList) -> No
|
||||
)
|
||||
|
||||
|
||||
async def update_favorite_item_page(pages: list[dict], item: FavoriteItem):
|
||||
pages = [
|
||||
FavoriteItemPage(
|
||||
favorite_item=item, cid=page["cid"], page=page["page"], name=page["part"], image=page.get("first_frame", "")
|
||||
)
|
||||
for page in pages
|
||||
]
|
||||
await FavoriteItemPage.bulk_create(
|
||||
pages, on_conflict=["favorite_item_id", "page"], update_fields=["cid", "name", "image"], batch_size=300
|
||||
)
|
||||
|
||||
|
||||
async def process() -> None:
|
||||
global anchor
|
||||
if (today := datetime.date.today()) > anchor:
|
||||
@@ -84,7 +96,10 @@ async def process() -> None:
|
||||
logger.exception("Failed to refresh credential.")
|
||||
return
|
||||
for favorite_id in settings.path_mapper:
|
||||
await process_favorite(favorite_id)
|
||||
try:
|
||||
await process_favorite(favorite_id)
|
||||
except asyncio.TimeoutError:
|
||||
logger.exception("Process favorite {} timeout.", favorite_id)
|
||||
|
||||
|
||||
async def process_favorite(favorite_id: int) -> None:
|
||||
@@ -131,6 +146,7 @@ async def process_favorite_item(
|
||||
process_nfo=True,
|
||||
process_upper=True,
|
||||
process_subtitle=True,
|
||||
refresh_mode=False,
|
||||
) -> None:
|
||||
logger.info("Start to process video {} {}.", fav_item.bvid, fav_item.name)
|
||||
if fav_item.type != MediaType.VIDEO:
|
||||
@@ -155,66 +171,50 @@ async def process_favorite_item(
|
||||
single_page = False
|
||||
if settings.paginated_video:
|
||||
pages = None
|
||||
try:
|
||||
pages = await v.get_pages()
|
||||
pages = [
|
||||
FavoriteItemPage(
|
||||
favorite_item=fav_item,
|
||||
cid=page["cid"],
|
||||
page=page["page"],
|
||||
name=page["part"],
|
||||
image=page["first_frame"],
|
||||
)
|
||||
for page in pages
|
||||
]
|
||||
except Exception:
|
||||
logger.exception("Failed to get pages of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
if pages:
|
||||
if len(pages) == 1:
|
||||
single_page = True
|
||||
else:
|
||||
# 如果有多个分 p,那么先创建记录
|
||||
await FavoriteItemPage.bulk_create(
|
||||
pages,
|
||||
on_conflict=["favorite_item_id", "page"],
|
||||
update_fields=["cid", "name", "image"],
|
||||
batch_size=300,
|
||||
)
|
||||
# 重新拉一下数据,不能用 bulk create 的返回值,因为 bulk_create 不会填充主键
|
||||
pages = await FavoriteItemPage.filter(favorite_item=fav_item).order_by("page")
|
||||
for page in pages:
|
||||
page.favorite_item = fav_item
|
||||
if process_nfo:
|
||||
try:
|
||||
await get_nfo(fav_item.tvshow_nfo_path, obj=fav_item, mode=NfoMode.TVSHOW)
|
||||
except FileExistsError:
|
||||
logger.info("Nfo of {} {} already exists, skipped.", fav_item.bvid, fav_item.name)
|
||||
except Exception:
|
||||
logger.exception("Failed to process nfo of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
if process_poster:
|
||||
try:
|
||||
await get_file(fav_item.cover, fav_item.tvshow_poster_path)
|
||||
except FileExistsError:
|
||||
logger.info("Poster of {} {} already exists, skipped.", fav_item.bvid, fav_item.name)
|
||||
except Exception:
|
||||
logger.exception("Failed to process poster of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
await asyncio.gather(
|
||||
*[
|
||||
process_favorite_item_page(
|
||||
page, v, process_poster, process_video, process_nfo, process_subtitle
|
||||
)
|
||||
for page in pages
|
||||
],
|
||||
return_exceptions=True,
|
||||
)
|
||||
fav_item.downloaded = all(page.downloaded for page in pages)
|
||||
page_status = {page.status for page in pages}
|
||||
if MediaStatus.INVISIBLE in page_status:
|
||||
fav_item.status = MediaStatus.INVISIBLE
|
||||
elif MediaStatus.DELETED in page_status:
|
||||
fav_item.status = MediaStatus.DELETED
|
||||
if not refresh_mode:
|
||||
# 非手动触发的情况下,会刷新一下 pages
|
||||
try:
|
||||
tmp_pages = await v.get_pages()
|
||||
if len(tmp_pages) <= 1:
|
||||
single_page = True
|
||||
else:
|
||||
fav_item.status = MediaStatus.NORMAL
|
||||
await update_favorite_item_page(tmp_pages, fav_item)
|
||||
except Exception:
|
||||
logger.exception("Failed to get pages of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
# 从表中查出 pages
|
||||
pages = await FavoriteItemPage.filter(favorite_item=fav_item).order_by("page")
|
||||
for page in pages:
|
||||
page.favorite_item = fav_item
|
||||
if pages and not single_page:
|
||||
if process_nfo:
|
||||
try:
|
||||
await get_nfo(fav_item.tvshow_nfo_path, obj=fav_item, mode=NfoMode.TVSHOW)
|
||||
except FileExistsError:
|
||||
logger.info("Nfo of {} {} already exists, skipped.", fav_item.bvid, fav_item.name)
|
||||
except Exception:
|
||||
logger.exception("Failed to process nfo of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
if process_poster:
|
||||
try:
|
||||
await get_file(fav_item.cover, fav_item.tvshow_poster_path)
|
||||
except FileExistsError:
|
||||
logger.info("Poster of {} {} already exists, skipped.", fav_item.bvid, fav_item.name)
|
||||
except Exception:
|
||||
logger.exception("Failed to process poster of video {} {}.", fav_item.bvid, fav_item.name)
|
||||
await asyncio.gather(
|
||||
*[
|
||||
process_favorite_item_page(page, v, process_poster, process_video, process_nfo, process_subtitle)
|
||||
for page in pages
|
||||
],
|
||||
return_exceptions=True,
|
||||
)
|
||||
fav_item.downloaded = all(page.downloaded for page in pages)
|
||||
page_status = {page.status for page in pages}
|
||||
if MediaStatus.INVISIBLE in page_status:
|
||||
fav_item.status = MediaStatus.INVISIBLE
|
||||
elif MediaStatus.DELETED in page_status:
|
||||
fav_item.status = MediaStatus.DELETED
|
||||
else:
|
||||
fav_item.status = MediaStatus.NORMAL
|
||||
if single_page or not settings.paginated_video:
|
||||
if process_nfo:
|
||||
try:
|
||||
@@ -291,7 +291,7 @@ async def process_favorite_item_page(
|
||||
)
|
||||
if process_poster:
|
||||
try:
|
||||
await get_file(fav_page.image, fav_page.poster_path)
|
||||
await get_file(fav_page.image or fav_page.favorite_item.cover, fav_page.poster_path)
|
||||
except FileExistsError:
|
||||
logger.info(
|
||||
"Poster of {} {} page {} already exists, skipped.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "bili-sync"
|
||||
version = "1.1.7"
|
||||
version = "1.1.9"
|
||||
description = ""
|
||||
authors = ["amtoaer <amtoaer@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
@@ -68,7 +68,7 @@ message = "chore: bump version from {current_version} to {new_version}"
|
||||
tag = true
|
||||
tag_name = "{new_version}"
|
||||
tag_message = ""
|
||||
current_version = "1.1.7"
|
||||
current_version = "1.1.9"
|
||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||
|
||||
[[tool.bumpversion.files]]
|
||||
@@ -82,6 +82,8 @@ filename = "pyproject.toml"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION = "1.1.7"
|
||||
VERSION = "1.1.9"
|
||||
|
||||
Reference in New Issue
Block a user