mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-09-06 07:57:12 +08:00
fix: 修复新的配置项没有写入配置文件的问题,扩充单行字符限制 (#33)
This commit is contained in:
@@ -48,9 +48,7 @@ async def entry() -> None:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 确保 docker 退出时正确触发资源释放
|
# 确保 docker 退出时正确触发资源释放
|
||||||
signal.signal(
|
signal.signal(signal.SIGTERM, lambda *_: os.kill(os.getpid(), signal.SIGINT))
|
||||||
signal.SIGTERM, lambda *_: os.kill(os.getpid(), signal.SIGINT)
|
|
||||||
)
|
|
||||||
with asyncio.Runner() as runner:
|
with asyncio.Runner() as runner:
|
||||||
try:
|
try:
|
||||||
runner.run(entry())
|
runner.run(entry())
|
||||||
|
|||||||
@@ -41,15 +41,11 @@ class Upper(Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def thumb_path(self) -> Path:
|
def thumb_path(self) -> Path:
|
||||||
return (
|
return DEFAULT_THUMB_PATH / str(self.mid)[0] / f"{self.mid}" / "folder.jpg"
|
||||||
DEFAULT_THUMB_PATH / str(self.mid)[0] / f"{self.mid}" / "folder.jpg"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def meta_path(self) -> Path:
|
def meta_path(self) -> Path:
|
||||||
return (
|
return DEFAULT_THUMB_PATH / str(self.mid)[0] / f"{self.mid}" / "person.nfo"
|
||||||
DEFAULT_THUMB_PATH / str(self.mid)[0] / f"{self.mid}" / "person.nfo"
|
|
||||||
)
|
|
||||||
|
|
||||||
async def save_metadata(self):
|
async def save_metadata(self):
|
||||||
async with aopen(self.meta_path, "w") as f:
|
async with aopen(self.meta_path, "w") as f:
|
||||||
@@ -74,16 +70,12 @@ class FavoriteItem(Model):
|
|||||||
id = fields.IntField(pk=True)
|
id = fields.IntField(pk=True)
|
||||||
name = fields.CharField(max_length=255)
|
name = fields.CharField(max_length=255)
|
||||||
type = fields.IntEnumField(enum_type=MediaType)
|
type = fields.IntEnumField(enum_type=MediaType)
|
||||||
status = fields.IntEnumField(
|
status = fields.IntEnumField(enum_type=MediaStatus, default=MediaStatus.NORMAL)
|
||||||
enum_type=MediaStatus, default=MediaStatus.NORMAL
|
|
||||||
)
|
|
||||||
bvid = fields.CharField(max_length=255)
|
bvid = fields.CharField(max_length=255)
|
||||||
desc = fields.TextField()
|
desc = fields.TextField()
|
||||||
cover = fields.TextField()
|
cover = fields.TextField()
|
||||||
tags = fields.JSONField(null=True)
|
tags = fields.JSONField(null=True)
|
||||||
favorite_list = fields.ForeignKeyField(
|
favorite_list = fields.ForeignKeyField("models.FavoriteList", related_name="items")
|
||||||
"models.FavoriteList", related_name="items"
|
|
||||||
)
|
|
||||||
upper = fields.ForeignKeyField("models.Upper", related_name="uploads")
|
upper = fields.ForeignKeyField("models.Upper", related_name="uploads")
|
||||||
ctime = fields.DatetimeField()
|
ctime = fields.DatetimeField()
|
||||||
pubtime = fields.DatetimeField()
|
pubtime = fields.DatetimeField()
|
||||||
@@ -101,38 +93,23 @@ class FavoriteItem(Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tmp_video_path(self) -> Path:
|
def tmp_video_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"tmp_{self.bvid}_video"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"tmp_{self.bvid}_video"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tmp_audio_path(self) -> Path:
|
def tmp_audio_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"tmp_{self.bvid}_audio"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"tmp_{self.bvid}_audio"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def video_path(self) -> Path:
|
def video_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"{self.bvid}.mp4"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"{self.bvid}.mp4"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nfo_path(self) -> Path:
|
def nfo_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"{self.bvid}.nfo"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"{self.bvid}.nfo"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def poster_path(self) -> Path:
|
def poster_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"{self.bvid}-poster.jpg"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"{self.bvid}-poster.jpg"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def upper_path(self) -> list[Path]:
|
def upper_path(self) -> list[Path]:
|
||||||
@@ -143,10 +120,7 @@ class FavoriteItem(Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitle_path(self) -> Path:
|
def subtitle_path(self) -> Path:
|
||||||
return (
|
return Path(settings.path_mapper[self.favorite_list_id]) / f"{self.bvid}.zh-CN.default.ass"
|
||||||
Path(settings.path_mapper[self.favorite_list_id])
|
|
||||||
/ f"{self.bvid}.zh-CN.default.ass"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Program(Model):
|
class Program(Model):
|
||||||
|
|||||||
+10
-28
@@ -45,9 +45,7 @@ async def manage_model(medias: list[dict], fav_list: FavoriteList) -> None:
|
|||||||
)
|
)
|
||||||
for media in medias
|
for media in medias
|
||||||
]
|
]
|
||||||
await Upper.bulk_create(
|
await Upper.bulk_create(uppers, on_conflict=["mid"], update_fields=["name", "thumb"])
|
||||||
uppers, on_conflict=["mid"], update_fields=["name", "thumb"]
|
|
||||||
)
|
|
||||||
items = [
|
items = [
|
||||||
FavoriteItem(
|
FavoriteItem(
|
||||||
name=media["title"],
|
name=media["title"],
|
||||||
@@ -114,10 +112,8 @@ async def process_favorite(favorite_id: int) -> None:
|
|||||||
while True:
|
while True:
|
||||||
page += 1
|
page += 1
|
||||||
if page > 1:
|
if page > 1:
|
||||||
favorite_video_list = (
|
favorite_video_list = await favorite_list.get_video_favorite_list_content(
|
||||||
await favorite_list.get_video_favorite_list_content(
|
favorite_id, page=page, credential=credential
|
||||||
favorite_id, page=page, credential=credential
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
# 先看看对应 bvid 的记录是否存在
|
# 先看看对应 bvid 的记录是否存在
|
||||||
existed_items = await FavoriteItem.filter(
|
existed_items = await FavoriteItem.filter(
|
||||||
@@ -125,14 +121,10 @@ async def process_favorite(favorite_id: int) -> None:
|
|||||||
bvid__in=[media["bvid"] for media in favorite_video_list["medias"]],
|
bvid__in=[media["bvid"] for media in favorite_video_list["medias"]],
|
||||||
)
|
)
|
||||||
# 记录一下获得的列表中的 bvid 和 fav_time
|
# 记录一下获得的列表中的 bvid 和 fav_time
|
||||||
media_info = {
|
media_info = {(media["bvid"], media["fav_time"]) for media in favorite_video_list["medias"]}
|
||||||
(media["bvid"], media["fav_time"])
|
|
||||||
for media in favorite_video_list["medias"]
|
|
||||||
}
|
|
||||||
# 如果有 bvid 和 fav_time 都相同的记录,说明已经到达了上次处理到的位置
|
# 如果有 bvid 和 fav_time 都相同的记录,说明已经到达了上次处理到的位置
|
||||||
continue_flag = not media_info & {
|
continue_flag = not media_info & {
|
||||||
(item.bvid, int(item.fav_time.timestamp()))
|
(item.bvid, int(item.fav_time.timestamp())) for item in existed_items
|
||||||
for item in existed_items
|
|
||||||
}
|
}
|
||||||
await manage_model(favorite_video_list["medias"], fav_list)
|
await manage_model(favorite_video_list["medias"], fav_list)
|
||||||
if not (continue_flag and favorite_video_list["has_more"]):
|
if not (continue_flag and favorite_video_list["has_more"]):
|
||||||
@@ -186,9 +178,7 @@ async def process_favorite_item(
|
|||||||
await amakedirs(fav_item.upper.thumb_path.parent, exist_ok=True)
|
await amakedirs(fav_item.upper.thumb_path.parent, exist_ok=True)
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
fav_item.upper.save_metadata(),
|
fav_item.upper.save_metadata(),
|
||||||
download_content(
|
download_content(fav_item.upper.thumb, fav_item.upper.thumb_path),
|
||||||
fav_item.upper.thumb, fav_item.upper.thumb_path
|
|
||||||
),
|
|
||||||
return_exceptions=True,
|
return_exceptions=True,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -299,9 +289,7 @@ async def process_favorite_item(
|
|||||||
)
|
)
|
||||||
streams = detector.detect_best_streams(codecs=settings.codec)
|
streams = detector.detect_best_streams(codecs=settings.codec)
|
||||||
if detector.check_flv_stream():
|
if detector.check_flv_stream():
|
||||||
await download_content(
|
await download_content(streams[0].url, fav_item.tmp_video_path)
|
||||||
streams[0].url, fav_item.tmp_video_path
|
|
||||||
)
|
|
||||||
process = await create_subprocess_exec(
|
process = await create_subprocess_exec(
|
||||||
FFMPEG_COMMAND,
|
FFMPEG_COMMAND,
|
||||||
"-i",
|
"-i",
|
||||||
@@ -314,12 +302,8 @@ async def process_favorite_item(
|
|||||||
fav_item.tmp_video_path.unlink()
|
fav_item.tmp_video_path.unlink()
|
||||||
else:
|
else:
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
download_content(
|
download_content(streams[0].url, fav_item.tmp_video_path),
|
||||||
streams[0].url, fav_item.tmp_video_path
|
download_content(streams[1].url, fav_item.tmp_audio_path),
|
||||||
),
|
|
||||||
download_content(
|
|
||||||
streams[1].url, fav_item.tmp_audio_path
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
process = await create_subprocess_exec(
|
process = await create_subprocess_exec(
|
||||||
FFMPEG_COMMAND,
|
FFMPEG_COMMAND,
|
||||||
@@ -358,9 +342,7 @@ async def process_favorite_item(
|
|||||||
fav_item.status.text,
|
fav_item.status.text,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception("Failed to process video {} {}", fav_item.bvid, fav_item.name)
|
||||||
"Failed to process video {} {}", fav_item.bvid, fav_item.name
|
|
||||||
)
|
|
||||||
await fav_item.save()
|
await fav_item.save()
|
||||||
logger.info(
|
logger.info(
|
||||||
"{} {} is processed successfully.",
|
"{} {} is processed successfully.",
|
||||||
|
|||||||
+2
-2
@@ -24,10 +24,10 @@ ipython = "8.17.2"
|
|||||||
ruff = "0.1.6"
|
ruff = "0.1.6"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 80
|
line-length = 100
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 80
|
line-length = 100
|
||||||
select = [
|
select = [
|
||||||
"F", # https://beta.ruff.rs/docs/rules/#pyflakes-f
|
"F", # https://beta.ruff.rs/docs/rules/#pyflakes-f
|
||||||
"E",
|
"E",
|
||||||
|
|||||||
+5
-4
@@ -1,5 +1,4 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Self
|
|
||||||
|
|
||||||
from bilibili_api.video import VideoCodecs
|
from bilibili_api.video import VideoCodecs
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
@@ -42,7 +41,7 @@ class Config(BaseModel):
|
|||||||
return codecs
|
return codecs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(path: Path | None = None) -> Self:
|
def load(path: Path | None = None) -> "Config":
|
||||||
if not path:
|
if not path:
|
||||||
path = DEFAULT_CONFIG_PATH
|
path = DEFAULT_CONFIG_PATH
|
||||||
try:
|
try:
|
||||||
@@ -51,7 +50,7 @@ class Config(BaseModel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Failed to load config file: {path}") from e
|
raise RuntimeError(f"Failed to load config file: {path}") from e
|
||||||
|
|
||||||
def save(self, path: Path | None = None) -> Self:
|
def save(self, path: Path | None = None) -> "Config":
|
||||||
if not path:
|
if not path:
|
||||||
path = DEFAULT_CONFIG_PATH
|
path = DEFAULT_CONFIG_PATH
|
||||||
try:
|
try:
|
||||||
@@ -65,8 +64,10 @@ class Config(BaseModel):
|
|||||||
|
|
||||||
def init_settings() -> Config:
|
def init_settings() -> Config:
|
||||||
if not DEFAULT_CONFIG_PATH.exists():
|
if not DEFAULT_CONFIG_PATH.exists():
|
||||||
|
# 配置文件不存在的情况下,写入空的默认值
|
||||||
Config().save(DEFAULT_CONFIG_PATH)
|
Config().save(DEFAULT_CONFIG_PATH)
|
||||||
return Config.load(DEFAULT_CONFIG_PATH)
|
# 读取配置文件,校验出错会抛出异常,校验通过则重新保存一下配置文件(写入新配置项的默认值)
|
||||||
|
return Config.load(DEFAULT_CONFIG_PATH).save()
|
||||||
|
|
||||||
|
|
||||||
settings = init_settings()
|
settings = init_settings()
|
||||||
|
|||||||
Reference in New Issue
Block a user