Skip to content

Commit fd3c08b

Browse files
authored
fix: async and many bugs... (#3)
1 parent e83eb6c commit fd3c08b

File tree

7 files changed

+107
-44
lines changed

7 files changed

+107
-44
lines changed

animepipeline/encode/finalrip.py

+76-29
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import asyncio
2+
import gc
13
import mimetypes
24
import time
35
from pathlib import Path
46
from typing import Union
57

6-
import aiofiles
8+
import httpx
79
from httpx import AsyncClient
810
from loguru import logger
911
from tenacity import retry, stop_after_attempt, wait_random
@@ -17,6 +19,8 @@
1719
OSSPresignedURLRequest,
1820
OSSPresignedURLResponse,
1921
PingResponse,
22+
RetryMergeRequest,
23+
RetryMergeResponse,
2024
StartTaskRequest,
2125
StartTaskResponse,
2226
TaskNotCompletedError,
@@ -67,6 +71,14 @@ async def _get_oss_presigned_url(self, data: OSSPresignedURLRequest) -> OSSPresi
6771
logger.error(f"Error getting presigned URL: {e}, {data}")
6872
raise e
6973

74+
async def _retry_merge(self, data: RetryMergeRequest) -> RetryMergeResponse:
75+
try:
76+
response = await self.client.post("/api/v1/task/retry/merge", params=data.model_dump())
77+
return RetryMergeResponse(**response.json())
78+
except Exception as e:
79+
logger.error(f"Error retrying merge: {e}, {data}")
80+
raise e
81+
7082
async def check_task_exist(self, video_key: str) -> bool:
7183
try:
7284
get_task_progress_response = await self._get_task_progress(GetTaskProgressRequest(video_key=video_key))
@@ -85,6 +97,27 @@ async def check_task_completed(self, video_key: str) -> bool:
8597
logger.error(f"Error checking task completed: {e}, video_key: {video_key}")
8698
return False
8799

100+
async def check_task_all_clips_done(self, video_key: str) -> bool:
101+
try:
102+
get_task_progress_response = await self._get_task_progress(GetTaskProgressRequest(video_key=video_key))
103+
if not get_task_progress_response.success:
104+
logger.error(f"Error getting task progress: {get_task_progress_response.error.message}") # type: ignore
105+
return False
106+
107+
for clip in get_task_progress_response.data.progress: # type: ignore
108+
if not clip.completed:
109+
return False
110+
111+
return True
112+
except Exception as e:
113+
logger.error(f"Error checking task all clips done: {e}, video_key: {video_key}")
114+
return False
115+
116+
async def retry_merge(self, video_key: str) -> None:
117+
retry_merge_response = await self._retry_merge(RetryMergeRequest(video_key=video_key))
118+
if not retry_merge_response.success:
119+
logger.error(f"Error retrying merge: {retry_merge_response.error.message}") # type: ignore
120+
88121
@retry(wait=wait_random(min=3, max=5), stop=stop_after_attempt(5))
89122
async def upload_and_new_task(self, video_path: Union[str, Path]) -> None:
90123
"""
@@ -102,36 +135,50 @@ async def upload_and_new_task(self, video_path: Union[str, Path]) -> None:
102135

103136
# gen oss presigned url
104137
video_key = Path(video_path).name
105-
oss_presigned_url_response = await self._get_oss_presigned_url(OSSPresignedURLRequest(video_key=video_key))
106-
if not oss_presigned_url_response.success:
107-
logger.error(f"Error getting presigned URL: {oss_presigned_url_response.error.message}") # type: ignore
108-
raise ValueError(f"Error getting presigned URL: {oss_presigned_url_response.error.message}") # type: ignore
109-
110138
try:
111-
content_type = mimetypes.guess_type(video_path)[0]
112-
except Exception:
113-
content_type = "application/octet-stream"
114-
115-
# upload file
116-
try:
117-
logger.info(f"Uploading file: {video_path}")
118-
t0 = time.time()
119-
async with aiofiles.open(video_path, mode="rb") as v:
120-
video_content = await v.read()
121-
122-
response = await self.client.put(
123-
url=oss_presigned_url_response.data.url, # type: ignore
124-
content=video_content,
125-
headers={"Content-Type": content_type},
126-
timeout=60 * 60,
127-
)
128-
if response.status_code != 200:
129-
raise IOError(f"Error uploading file: {response.text}")
130-
logger.info(f"Upload file Successfully! path: {video_path} time: {time.time() - t0:.2f}s")
139+
oss_presigned_url_response = await self._get_oss_presigned_url(OSSPresignedURLRequest(video_key=video_key))
140+
if not oss_presigned_url_response.success:
141+
logger.error(f"Error getting presigned URL: {oss_presigned_url_response.error.message}") # type: ignore
142+
raise ValueError(f"Error getting presigned URL: {oss_presigned_url_response.error.message}") # type: ignore
131143
except Exception as e:
132-
logger.error(f"Error in uploading file: {video_path}: {e}")
144+
logger.error(f"Error getting presigned URL: {e}")
133145
raise e
134146

147+
if not oss_presigned_url_response.data.exist: # type: ignore
148+
try:
149+
content_type = mimetypes.guess_type(video_path)[0]
150+
except Exception:
151+
content_type = "application/octet-stream"
152+
153+
# upload file
154+
try:
155+
logger.info(f"Uploading file: {video_path}")
156+
t0 = time.time()
157+
158+
# 这里不要用异步,会内存泄漏
159+
def _upload_file() -> None:
160+
with open(video_path, mode="rb") as v:
161+
video_content = v.read()
162+
logger.info(f"Read file Successfully! path: {video_path} time: {time.time() - t0:.2f}s")
163+
response = httpx.put(
164+
url=oss_presigned_url_response.data.url, # type: ignore
165+
content=video_content,
166+
headers={"Content-Type": content_type},
167+
timeout=60 * 60,
168+
)
169+
if response.status_code != 200:
170+
raise IOError(f"Error uploading file: {response.text}")
171+
172+
_upload_file()
173+
del _upload_file
174+
gc.collect()
175+
logger.info(f"Upload file Successfully! path: {video_path} time: {time.time() - t0:.2f}s")
176+
except Exception as e:
177+
logger.error(f"Error in uploading file: {video_path}: {e}")
178+
raise e
179+
180+
await asyncio.sleep(2)
181+
135182
# new task
136183
new_task_response = await self._new_task(NewTaskRequest(video_key=video_key))
137184
if not new_task_response.success:
@@ -168,5 +215,5 @@ async def download_completed_task(self, video_key: str, save_path: Union[str, Pa
168215
if response.status_code != 200:
169216
raise IOError(f"Error downloading file: {response.text}")
170217

171-
async with aiofiles.open(save_path, mode="wb") as v:
172-
await v.write(response.content)
218+
with open(save_path, mode="wb") as v:
219+
v.write(response.content)

animepipeline/encode/type.py

+9
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,12 @@ class Data(BaseModel):
8585
data: Optional[Data] = None
8686
error: Optional[Error] = None
8787
success: bool
88+
89+
90+
class RetryMergeRequest(BaseModel):
91+
video_key: str
92+
93+
94+
class RetryMergeResponse(BaseModel):
95+
error: Optional[Error] = None
96+
success: bool

animepipeline/loop.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ async def pipeline_finalrip(self, task_info: TaskInfo) -> None:
173173
while not await self.finalrip_client.check_task_exist(bt_downloaded_path.name):
174174
try:
175175
await self.finalrip_client.upload_and_new_task(bt_downloaded_path)
176+
logger.info(f'FinalRip Task Created for "{task_info.name}" EP {task_info.episode}')
176177
except Exception as e:
177178
logger.error(f"Failed to upload and new finalrip task: {e}")
178-
raise e
179179
await asyncio.sleep(10)
180180

181181
try:
@@ -184,6 +184,7 @@ async def pipeline_finalrip(self, task_info: TaskInfo) -> None:
184184
encode_param=task_info.param,
185185
script=task_info.script,
186186
)
187+
logger.info(f'FinalRip Task Started for "{task_info.name}" EP {task_info.episode}')
187188
except Exception as e:
188189
logger.error(f"Failed to start finalrip task: {e}")
189190

@@ -192,6 +193,20 @@ async def pipeline_finalrip(self, task_info: TaskInfo) -> None:
192193

193194
# check task progress
194195
while not await self.finalrip_client.check_task_completed(bt_downloaded_path.name):
196+
# retry merge if all clips are done but merge failed?
197+
if await self.finalrip_client.check_task_all_clips_done(bt_downloaded_path.name):
198+
# wait 30s before retry merge
199+
await asyncio.sleep(30)
200+
# check again
201+
if await self.finalrip_client.check_task_completed(bt_downloaded_path.name):
202+
break
203+
204+
try:
205+
await self.finalrip_client.retry_merge(bt_downloaded_path.name)
206+
logger.info(f'Retry Merge Clips for "{task_info.name}" EP {task_info.episode}')
207+
except Exception as e:
208+
logger.error(f'Failed to retry merge clips for "{task_info.name}" EP {task_info.episode}: {e}')
209+
195210
await asyncio.sleep(10)
196211

197212
# download temp file to bt_downloaded_path's parent directory

animepipeline/post/tg.py

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ async def send_video(self, video_path: Union[Path, str], caption: Optional[str]
5454
caption=caption,
5555
read_timeout=6000,
5656
write_timeout=6000,
57+
pool_timeout=6000,
5758
)
5859
except telegram.error.NetworkError as e:
5960
logger.error(f"Network error: {e}, video path: {video_path}, video_caption: {caption}")

poetry.lock

+1-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,13 @@ license = "MIT"
3939
name = "animepipeline"
4040
readme = "README.md"
4141
repository = "https://github.com/TensoRaws/AnimePipeline"
42-
version = "0.0.2"
42+
version = "0.0.3"
4343

4444
# Requirements
4545
[tool.poetry.dependencies]
4646
python = "^3.9"
4747

4848
[tool.poetry.group.dev.dependencies]
49-
aiofiles = "^24.1.0"
5049
feedparser = "^6.0.11"
5150
httpx = "^0.27.2"
5251
loguru = "^0.7.2"

tests/test_encode.py

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ async def test_task_progress(self) -> None:
5252
task_progress = await self.finalrip._get_task_progress(GetTaskProgressRequest(video_key=video_key))
5353
print(task_progress)
5454

55+
async def test_retry_merge(self) -> None:
56+
await self.finalrip.retry_merge(video_key)
57+
5558
async def test_download_completed_task(self) -> None:
5659
while True:
5760
try:

0 commit comments

Comments
 (0)