Skip to content

Commit

Permalink
fix: 尝试修复 emby 头像路径,异步化所有文件操作
Browse files Browse the repository at this point in the history
  • Loading branch information
amtoaer committed Dec 1, 2023
1 parent bbde9d6 commit 012b3f9
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 43 deletions.
31 changes: 26 additions & 5 deletions commands.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import asyncio

from aiofiles.os import path
from loguru import logger

from constants import MediaStatus, MediaType
from models import FavoriteItem
from models import FavoriteItem, Upper
from processor import download_content
from utils import aexists, amakedirs


async def recheck():
Expand All @@ -14,9 +15,7 @@ async def recheck():
status=MediaStatus.NORMAL,
downloaded=True,
)
exists = await asyncio.gather(
*[path.exists(item.video_path) for item in items]
)
exists = await asyncio.gather(*[aexists(item.video_path) for item in items])
for item, exist in zip(items, exists):
if isinstance(exist, Exception):
logger.error(
Expand All @@ -36,3 +35,25 @@ async def recheck():
logger.info("Updating database...")
await FavoriteItem.bulk_update(items, fields=["downloaded"])
logger.info("Database updated.")


async def upper_thumb():
makedir_tasks = []
other_tasks = []
for upper in await Upper.all():
if not all(
await asyncio.gather(
aexists(upper.thumb_path), aexists(upper.meta_path)
)
):
makedir_tasks.append(
amakedirs(upper.thumb_path.parent, exist_ok=True)
)
other_tasks.extend(
[
upper.save_metadata(),
download_content(upper.thumb_url, upper.thumb_path),
]
)
await asyncio.gather(*makedir_tasks)
await asyncio.gather(*other_tasks)
21 changes: 10 additions & 11 deletions entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import uvloop
from loguru import logger

from commands import recheck
from commands import recheck, upper_thumb
from models import init_model
from processor import cleanup, process
from settings import settings
Expand All @@ -14,16 +14,15 @@

async def entry() -> None:
await init_model()
if any("once" in _ for _ in sys.argv):
# 单次运行
logger.info("Running once...")
await process()
return
if any("recheck" in _ for _ in sys.argv):
# 重新检查
logger.info("Rechecking...")
await recheck()
return
for command, func in [
("once", process),
("recheck", recheck),
("upper_thumb", upper_thumb),
]:
if any(command in _ for _ in sys.argv):
logger.info("Running {}...", command)
await func()
return
logger.info("Running daemon...")
while True:
await process()
Expand Down
27 changes: 26 additions & 1 deletion models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
MediaType,
)
from settings import settings
from utils import aopen


class FavoriteList(Model):
Expand All @@ -39,7 +40,31 @@ class Upper(Model):

@property
def thumb_path(self) -> Path:
return DEFAULT_THUMB_PATH / f"{self.mid}.jpg"
return (
DEFAULT_THUMB_PATH / f"{self.mid[0]}" / f"{self.mid}" / "folder.jpg"
)

@property
def meta_path(self) -> Path:
return (
DEFAULT_THUMB_PATH / f"{self.mid[0]}" / f"{self.mid}" / "person.nfo"
)

async def save_metadata(self):
async with aopen(self.meta_path, "w") as f:
await f.write(
f"""
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<person>
<plot />
<outline />
<lockdata>false</lockdata>
<dateadded>{self.created_at.strftime("%Y-%m-%d %H:%M:%S")}</dateadded>
<title>{self.mid}</title>
<sorttitle>{self.mid}</sorttitle>
</person>
""".strip()
)


class FavoriteItem(Model):
Expand Down
10 changes: 5 additions & 5 deletions nfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
from dataclasses import dataclass
from pathlib import Path

from utils import aopen


@dataclass
class Actor:
name: str
role: str
thumb: Path

def to_xml(self) -> str:
return f"""
<actor>
<name>{self.name}</name>
<role>{self.role}</role>
<thumb>{self.thumb.resolve()}</thumb>
</actor>
""".strip(
"\n"
Expand All @@ -29,9 +29,9 @@ class EpisodeInfo:
bvid: str
aired: datetime.datetime

def write_nfo(self, path: Path) -> None:
with path.open("w", encoding="utf-8") as f:
f.write(self.to_xml())
async def write_nfo(self, path: Path) -> None:
async with aopen(path, "w", encoding="utf-8") as f:
await f.write(self.to_xml())

def to_xml(self) -> str:
actor = "\n".join(_.to_xml() for _ in self.actor)
Expand Down
34 changes: 13 additions & 21 deletions processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
import datetime
from asyncio import Semaphore, create_subprocess_exec
from asyncio.subprocess import DEVNULL
from pathlib import Path

import aiofiles
import httpx
from bilibili_api import HEADERS, favorite_list, video
from bilibili_api import favorite_list, video
from bilibili_api.exceptions import ResponseCodeException
from loguru import logger
from tortoise import Tortoise
Expand All @@ -16,8 +13,7 @@
from models import FavoriteItem, FavoriteList, Upper
from nfo import Actor, EpisodeInfo
from settings import settings

client = httpx.AsyncClient(headers=HEADERS)
from utils import aexists, amakedirs, client, download_content

anchor = datetime.date.today()

Expand All @@ -40,16 +36,6 @@ async def wrapper(*args, **kwargs) -> any:
return decorator


async def download_content(url: str, path: Path) -> None:
async with client.stream("GET", url) as resp, aiofiles.open(
path, "wb"
) as f:
async for chunk in resp.aiter_bytes(40960):
if not chunk:
return
await f.write(chunk)


async def manage_model(medias: list[dict], fav_list: FavoriteList) -> None:
uppers = [
Upper(
Expand Down Expand Up @@ -174,27 +160,33 @@ async def process_video(fav_item: FavoriteItem) -> None:
logger.warning("Media {} is not a video, skipped.", fav_item.name)
return
try:
if fav_item.video_path.exists():
if await aexists(fav_item.video_path):
fav_item.downloaded = True
await fav_item.save()
logger.info(
"{} {} already exists, skipped.", fav_item.bvid, fav_item.name
)
return
# 写入 up 主头像
if not fav_item.upper.thumb_path.exists():
if not all(
await asyncio.gather(
aexists(fav_item.upper.thumb_path),
aexists(fav_item.upper.meta_path),
)
):
await amakedirs(fav_item.upper.thumb_path.parent, exist_ok=True)
await fav_item.upper.save_metadata()
await download_content(
fav_item.upper.thumb, fav_item.upper.thumb_path
fav_item.upper.thumb_url, fav_item.upper.thumb_path
)
# 写入 nfo
EpisodeInfo(
await EpisodeInfo(
title=fav_item.name,
plot=fav_item.desc,
actor=[
Actor(
name=fav_item.upper.mid,
role=fav_item.upper.name,
thumb=fav_item.upper.thumb_path,
)
],
bvid=fav_item.bvid,
Expand Down
33 changes: 33 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pathlib import Path

import aiofiles
import httpx
from aiofiles.base import AiofilesContextManager
from aiofiles.os import makedirs
from aiofiles.ospath import exists
from aiofiles.threadpool.text import AsyncTextIOWrapper
from bilibili_api import HEADERS

client = httpx.AsyncClient(headers=HEADERS)


async def download_content(url: str, path: Path) -> None:
async with client.stream("GET", url) as resp, aopen(path, "wb") as f:
async for chunk in resp.aiter_bytes(40960):
if not chunk:
return
await f.write(chunk)


async def aexists(path: Path) -> bool:
return await exists(path)


async def amakedirs(path: Path, exist_ok=False) -> None:
await makedirs(path, parents=True, exist_ok=exist_ok)


def aopen(
path: Path, mode: str = "r", **kwargs
) -> AiofilesContextManager[None, None, AsyncTextIOWrapper]:
return aiofiles.open(path, mode, **kwargs)

0 comments on commit 012b3f9

Please sign in to comment.