parent
c87232512b
commit
e5029a7922
|
|
@ -1,4 +1,4 @@
|
||||||
MODAL_ENVIRONMENT=dev
|
MODAL_ENVIRONMENT=test
|
||||||
modal_app_name=bowong-ai-video
|
modal_app_name=bowong-ai-video
|
||||||
S3_mount_dir=/mntS3
|
S3_mount_dir=/mntS3
|
||||||
S3_bucket_name=modal-media-cache
|
S3_bucket_name=modal-media-cache
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from typing import Union, Any
|
from typing import Union, Any
|
||||||
from pydantic import BaseModel, Field, computed_field, field_validator
|
from pydantic import BaseModel, Field, computed_field, field_validator, model_validator
|
||||||
from pydantic.json_schema import JsonSchemaValue
|
from pydantic.json_schema import JsonSchemaValue
|
||||||
from ..utils.TimeUtils import TimeDelta
|
from ..utils.TimeUtils import TimeDelta
|
||||||
|
|
||||||
|
|
@ -43,6 +43,12 @@ class FFMpegSliceSegment(BaseModel):
|
||||||
else:
|
else:
|
||||||
raise TypeError(v)
|
raise TypeError(v)
|
||||||
|
|
||||||
|
@model_validator(mode='after')
|
||||||
|
def validate_end_after_start(self) -> 'FFMpegSliceSegment':
|
||||||
|
if self.end <= self.start:
|
||||||
|
raise ValueError("end time must be greater than start time")
|
||||||
|
return self
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __get_pydantic_json_schema__(cls, core_schema: Any, handler: Any) -> JsonSchemaValue:
|
def __get_pydantic_json_schema__(cls, core_schema: Any, handler: Any) -> JsonSchemaValue:
|
||||||
# Override the schema to represent it as a string
|
# Override the schema to represent it as a string
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,14 @@ class MediaSources(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_inputs(cls, v: Union[str, MediaSource]) -> List[MediaSource]:
|
def parse_inputs(cls, v: Union[str, MediaSource]) -> List[MediaSource]:
|
||||||
if not v:
|
if not v:
|
||||||
raise ValidationError("inputs为空")
|
raise ValidationError([
|
||||||
|
{
|
||||||
|
'loc': ('inputs',),
|
||||||
|
'msg': "inputs为空",
|
||||||
|
'type': 'value_error',
|
||||||
|
'input': v
|
||||||
|
}
|
||||||
|
], MediaSources)
|
||||||
result = []
|
result = []
|
||||||
for item in v:
|
for item in v:
|
||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
|
|
@ -209,7 +216,14 @@ class MediaSources(BaseModel):
|
||||||
elif isinstance(item, MediaSource):
|
elif isinstance(item, MediaSource):
|
||||||
result.append(item)
|
result.append(item)
|
||||||
else:
|
else:
|
||||||
raise ValidationError("inputs元素类型错误: 必须是字符串")
|
raise ValidationError([
|
||||||
|
{
|
||||||
|
'loc': ('inputs',),
|
||||||
|
'msg': "inputs元素类型错误: 必须是字符串",
|
||||||
|
'type': 'value_error',
|
||||||
|
'input': v
|
||||||
|
}
|
||||||
|
], MediaSources)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
model_config = {
|
model_config = {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class SentryUtils:
|
||||||
op=op,
|
op=op,
|
||||||
name=name)
|
name=name)
|
||||||
else:
|
else:
|
||||||
transaction = sentry_sdk.start_transaction(op='modal.function', name='Modal Function直接调用')
|
transaction = sentry_sdk.start_transaction(op=op, name=name)
|
||||||
with transaction:
|
with transaction:
|
||||||
with transaction.start_child(op=op, name=name) as span:
|
with transaction.start_child(op=op, name=name) as span:
|
||||||
cpu_freq, cpu_count, mem = SentryUtils.capture_hardware_info()
|
cpu_freq, cpu_count, mem = SentryUtils.capture_hardware_info()
|
||||||
|
|
@ -43,23 +43,18 @@ class SentryUtils:
|
||||||
'x_trace.id': sentry_trace_id,
|
'x_trace.id': sentry_trace_id,
|
||||||
'x_trace.baggage': sentry_baggage,
|
'x_trace.baggage': sentry_baggage,
|
||||||
'cpu.count': cpu_count,
|
'cpu.count': cpu_count,
|
||||||
'cpu.frequency': cpu_freq.current / 1000,
|
'cpu.frequency': f"{cpu_freq.current / 1000:.2f}",
|
||||||
'cpu.frequency.format': f"{cpu_freq.current / 1000:.2f} GHz",
|
'cpu.frequency.format': f"{cpu_freq.current / 1000:.2f} GHz",
|
||||||
'memory.available': total_mb,
|
'memory.available': total_mb,
|
||||||
'memory.available.format': f"{total_mb} Mb",
|
'memory.available.format': f"{total_mb} Mb",
|
||||||
'cloud_resource': {
|
'modal.cloud.region': os.environ.get('MODAL_REGION', 'unknown'),
|
||||||
"cloud.provider": 'Modal',
|
'modal.cloud.provider': os.environ.get('MODAL_CLOUD_PROVIDER', 'unknown'),
|
||||||
"type": "cloud_resource"
|
'modal.task.id': os.environ.get('MODAL_TASK_ID', 'unknown'),
|
||||||
},
|
'modal.identity.token': os.environ.get('MODAL_IDENTITY_TOKEN', 'unknown'),
|
||||||
'modal': {
|
'modal.image.id': os.environ.get('MODAL_IMAGE_ID', 'unknown'),
|
||||||
'modal.region': os.environ.get('MODAL_REGION', 'unknown'),
|
|
||||||
'modal.provider': os.environ.get('MODAL_CLOUD_PROVIDER', 'unknown'),
|
|
||||||
'modal.task.id': os.environ.get('MODAL_TASK_ID', 'unknown'),
|
|
||||||
'modal.identity.token': os.environ.get('MODAL_IDENTITY_TOKEN', 'unknown'),
|
|
||||||
'modal.image.id': os.environ.get('MODAL_IMAGE_ID', 'unknown'),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
span.set_status('ok')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ with ffmpeg_worker_image.imports():
|
||||||
List[str], Optional[SentryTransactionInfo]]:
|
List[str], Optional[SentryTransactionInfo]]:
|
||||||
fn_id = current_function_call_id()
|
fn_id = current_function_call_id()
|
||||||
|
|
||||||
@SentryUtils.sentry_tracker(name="视频切割任务", op="ffmpeg.slice", fn_id=fn_id,
|
@SentryUtils.sentry_tracker(name="视频文件切割任务", op="ffmpeg.slice.media", fn_id=fn_id,
|
||||||
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
|
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
|
||||||
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
|
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
|
||||||
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)
|
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)
|
||||||
|
|
@ -164,15 +164,15 @@ with ffmpeg_worker_image.imports():
|
||||||
fn_id: str) -> List[FFMPEGResult]:
|
fn_id: str) -> List[FFMPEGResult]:
|
||||||
cache_filepath = f"{s3_mount}/{media_source.cache_filepath}"
|
cache_filepath = f"{s3_mount}/{media_source.cache_filepath}"
|
||||||
logger.info(f"从{media_source.urn}切割")
|
logger.info(f"从{media_source.urn}切割")
|
||||||
for marker in media_markers:
|
for i, marker in enumerate(media_markers):
|
||||||
logger.info(f"{marker.start.toFormatStr()} --> {marker.end.toFormatStr()}")
|
logger.info(f"[{i}] {marker.start.toFormatStr()} --> {marker.end.toFormatStr()}")
|
||||||
segments = await VideoUtils.ffmpeg_slice_media(media_path=cache_filepath,
|
segments = await VideoUtils.ffmpeg_slice_media(media_path=cache_filepath,
|
||||||
media_markers=media_markers,
|
media_markers=media_markers,
|
||||||
output_path=f"{output_path_prefix}/{config.modal_environment}/slice/outputs/{fn_id}/output.mp4")
|
output_path=f"{output_path_prefix}/{config.modal_environment}/slice/outputs/{fn_id}/output.mp4")
|
||||||
return [FFMPEGResult(urn=local_copy_to_s3([segment[0]])[0], metadata=segment[1],
|
return [FFMPEGResult(urn=local_copy_to_s3([segment[0]])[0], metadata=segment[1],
|
||||||
content_length=os.path.getsize(segment[0])) for segment in segments]
|
content_length=os.path.getsize(segment[0])) for segment in segments]
|
||||||
|
|
||||||
@SentryUtils.sentry_tracker(name="视频切割任务", op="ffmpeg.slice", fn_id=fn_id,
|
@SentryUtils.sentry_tracker(name="直播视频切割任务", op="ffmpeg.slice.stream", fn_id=fn_id,
|
||||||
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
|
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
|
||||||
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
|
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
|
||||||
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)
|
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue