tamar-file-hub-client 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- file_hub_client/client.py +24 -4
- file_hub_client/rpc/async_client.py +31 -4
- file_hub_client/rpc/gen/file_service_pb2.py +23 -5
- file_hub_client/rpc/gen/file_service_pb2_grpc.py +173 -0
- file_hub_client/rpc/protos/file_service.proto +66 -0
- file_hub_client/rpc/sync_client.py +31 -4
- file_hub_client/schemas/__init__.py +10 -0
- file_hub_client/schemas/context.py +171 -160
- file_hub_client/schemas/file.py +44 -0
- file_hub_client/services/file/async_blob_service.py +259 -8
- file_hub_client/services/file/async_file_service.py +217 -0
- file_hub_client/services/file/sync_blob_service.py +260 -8
- file_hub_client/services/file/sync_file_service.py +217 -0
- file_hub_client/utils/__init__.py +14 -0
- file_hub_client/utils/file_utils.py +186 -153
- file_hub_client/utils/ip_detector.py +226 -0
- {tamar_file_hub_client-0.1.3.dist-info → tamar_file_hub_client-0.1.4.dist-info}/METADATA +162 -1
- {tamar_file_hub_client-0.1.3.dist-info → tamar_file_hub_client-0.1.4.dist-info}/RECORD +20 -19
- {tamar_file_hub_client-0.1.3.dist-info → tamar_file_hub_client-0.1.4.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.1.3.dist-info → tamar_file_hub_client-0.1.4.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ from .base_file_service import BaseFileService
|
|
11
11
|
from ...enums import UploadMode
|
12
12
|
from ...errors import ValidationError
|
13
13
|
from ...rpc import AsyncGrpcClient
|
14
|
-
from ...schemas import FileUploadResponse, UploadUrlResponse, BatchDownloadUrlResponse, DownloadUrlInfo, GcsUrlInfo, GetGcsUrlResponse, BatchGcsUrlResponse
|
14
|
+
from ...schemas import FileUploadResponse, UploadUrlResponse, BatchDownloadUrlResponse, DownloadUrlInfo, GcsUrlInfo, GetGcsUrlResponse, BatchGcsUrlResponse, CompressionStatusResponse, GetVariantsResponse, RecompressionResponse, VariantDownloadUrlResponse, CompressedVariant
|
15
15
|
from ...utils import AsyncHttpUploader, AsyncHttpDownloader, retry_with_backoff, get_file_mime_type
|
16
16
|
|
17
17
|
|
@@ -121,7 +121,7 @@ class AsyncBlobService(BaseFileService):
|
|
121
121
|
request_id: Optional[str] = None,
|
122
122
|
**metadata
|
123
123
|
) -> FileUploadResponse:
|
124
|
-
"""
|
124
|
+
"""流式上传(GCS 直传)"""
|
125
125
|
|
126
126
|
# 获取上传URL,以及对应的文件和上传文件信息
|
127
127
|
upload_url_resp = await self.generate_upload_url(
|
@@ -145,11 +145,17 @@ class AsyncBlobService(BaseFileService):
|
|
145
145
|
upload_file=upload_url_resp.upload_file
|
146
146
|
)
|
147
147
|
|
148
|
+
# 构建HTTP头,包含Content-Type和固定的Cache-Control
|
149
|
+
headers = {
|
150
|
+
"Content-Type": mime_type,
|
151
|
+
"Cache-Control": "public, max-age=86400" # 24小时公共缓存
|
152
|
+
}
|
153
|
+
|
148
154
|
# 上传文件到对象存储
|
149
155
|
await self.http_uploader.upload(
|
150
156
|
url=upload_url_resp.upload_url,
|
151
157
|
content=content,
|
152
|
-
headers=
|
158
|
+
headers=headers,
|
153
159
|
total_size=file_size,
|
154
160
|
)
|
155
161
|
|
@@ -181,7 +187,8 @@ class AsyncBlobService(BaseFileService):
|
|
181
187
|
request_id: Optional[str] = None,
|
182
188
|
**metadata
|
183
189
|
) -> FileUploadResponse:
|
184
|
-
"""
|
190
|
+
"""断点续传实现(GCS 直传)"""
|
191
|
+
|
185
192
|
# 获取断点续传URL,以及对应的文件和上传文件信息
|
186
193
|
upload_url_resp = await self.generate_resumable_upload_url(
|
187
194
|
file_name=file_name,
|
@@ -204,6 +211,12 @@ class AsyncBlobService(BaseFileService):
|
|
204
211
|
upload_file=upload_url_resp.upload_file
|
205
212
|
)
|
206
213
|
|
214
|
+
# 构建HTTP头,包含Content-Type和固定的Cache-Control
|
215
|
+
headers = {
|
216
|
+
"Content-Type": mime_type,
|
217
|
+
"Cache-Control": "public, max-age=86400" # 24小时公共缓存
|
218
|
+
}
|
219
|
+
|
207
220
|
# 开启断点续传
|
208
221
|
upload_url = await self.http_uploader.start_resumable_session(
|
209
222
|
url=upload_url_resp.upload_url,
|
@@ -215,7 +228,7 @@ class AsyncBlobService(BaseFileService):
|
|
215
228
|
await self.http_uploader.upload(
|
216
229
|
url=upload_url,
|
217
230
|
content=content,
|
218
|
-
headers=
|
231
|
+
headers=headers,
|
219
232
|
total_size=file_size,
|
220
233
|
)
|
221
234
|
|
@@ -413,6 +426,8 @@ class AsyncBlobService(BaseFileService):
|
|
413
426
|
|
414
427
|
Note:
|
415
428
|
必须提供 file 或 url 参数之一
|
429
|
+
|
430
|
+
Cache-Control 头在 GCS 直传模式(STREAM/RESUMABLE)下自动设置为 "public, max-age=86400"
|
416
431
|
"""
|
417
432
|
# 参数验证:必须提供 file 或 url 之一
|
418
433
|
if file is None and not url:
|
@@ -468,7 +483,7 @@ class AsyncBlobService(BaseFileService):
|
|
468
483
|
|
469
484
|
# 根据上传模式执行不同的上传逻辑
|
470
485
|
if mode == UploadMode.NORMAL:
|
471
|
-
# 普通上传(通过gRPC
|
486
|
+
# 普通上传(通过gRPC)- 不需要 Cache-Control
|
472
487
|
return await self._upload_file(
|
473
488
|
file_name=extracted_file_name,
|
474
489
|
content=content,
|
@@ -483,7 +498,7 @@ class AsyncBlobService(BaseFileService):
|
|
483
498
|
)
|
484
499
|
|
485
500
|
elif mode == UploadMode.STREAM:
|
486
|
-
#
|
501
|
+
# 流式上传(目前使用直传实现)- 需要 Cache-Control
|
487
502
|
return await self._upload_stream(
|
488
503
|
file_name=extracted_file_name,
|
489
504
|
content=content,
|
@@ -500,7 +515,7 @@ class AsyncBlobService(BaseFileService):
|
|
500
515
|
)
|
501
516
|
|
502
517
|
elif mode == UploadMode.RESUMABLE:
|
503
|
-
# 断点续传
|
518
|
+
# 断点续传 - 需要 Cache-Control
|
504
519
|
return await self._upload_resumable(
|
505
520
|
file_name=extracted_file_name,
|
506
521
|
content=content,
|
@@ -588,6 +603,30 @@ class AsyncBlobService(BaseFileService):
|
|
588
603
|
chunk_size=chunk_size,
|
589
604
|
)
|
590
605
|
|
606
|
+
async def download_to_bytes(
|
607
|
+
self,
|
608
|
+
file_id: str,
|
609
|
+
*,
|
610
|
+
request_id: Optional[str] = None,
|
611
|
+
**metadata
|
612
|
+
) -> bytes:
|
613
|
+
"""
|
614
|
+
下载文件并返回字节数据
|
615
|
+
|
616
|
+
Args:
|
617
|
+
file_id: 文件ID
|
618
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
619
|
+
**metadata: 额外的元数据
|
620
|
+
|
621
|
+
Returns:
|
622
|
+
文件的字节数据
|
623
|
+
"""
|
624
|
+
# 获取下载URL
|
625
|
+
download_url = await self.generate_download_url(file_id, request_id=request_id, **metadata)
|
626
|
+
|
627
|
+
# 下载到内存并返回字节数据
|
628
|
+
return await self.http_downloader.download(url=download_url, save_path=None)
|
629
|
+
|
591
630
|
async def batch_generate_download_url(
|
592
631
|
self,
|
593
632
|
file_ids: List[str],
|
@@ -710,3 +749,215 @@ class AsyncBlobService(BaseFileService):
|
|
710
749
|
))
|
711
750
|
|
712
751
|
return BatchGcsUrlResponse(gcs_urls=gcs_urls)
|
752
|
+
|
753
|
+
async def get_compression_status(
|
754
|
+
self,
|
755
|
+
file_id: str,
|
756
|
+
*,
|
757
|
+
request_id: Optional[str] = None,
|
758
|
+
**metadata
|
759
|
+
) -> CompressionStatusResponse:
|
760
|
+
"""
|
761
|
+
获取文件压缩状态
|
762
|
+
|
763
|
+
Args:
|
764
|
+
file_id: 文件ID
|
765
|
+
request_id: 请求ID,用于追踪
|
766
|
+
**metadata: 额外的gRPC元数据
|
767
|
+
|
768
|
+
Returns:
|
769
|
+
CompressionStatusResponse: 压缩状态响应
|
770
|
+
"""
|
771
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
772
|
+
|
773
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
774
|
+
|
775
|
+
request = file_service_pb2.CompressionStatusRequest(file_id=file_id)
|
776
|
+
|
777
|
+
# 构建元数据
|
778
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
779
|
+
|
780
|
+
response = await stub.GetCompressionStatus(request, metadata=grpc_metadata)
|
781
|
+
|
782
|
+
# 转换压缩变体
|
783
|
+
variants = []
|
784
|
+
for variant in response.variants:
|
785
|
+
variants.append(CompressedVariant(
|
786
|
+
variant_name=variant.variant_name,
|
787
|
+
variant_type=variant.variant_type,
|
788
|
+
media_type=variant.media_type,
|
789
|
+
width=variant.width,
|
790
|
+
height=variant.height,
|
791
|
+
file_size=variant.file_size,
|
792
|
+
format=variant.format,
|
793
|
+
quality=variant.quality if variant.quality else None,
|
794
|
+
duration=variant.duration if variant.duration else None,
|
795
|
+
bitrate=variant.bitrate if variant.bitrate else None,
|
796
|
+
fps=variant.fps if variant.fps else None,
|
797
|
+
compression_ratio=variant.compression_ratio,
|
798
|
+
stored_path=variant.stored_path
|
799
|
+
))
|
800
|
+
|
801
|
+
return CompressionStatusResponse(
|
802
|
+
status=response.status,
|
803
|
+
error_message=response.error_message if response.error_message else None,
|
804
|
+
variants=variants
|
805
|
+
)
|
806
|
+
|
807
|
+
async def get_compressed_variants(
|
808
|
+
self,
|
809
|
+
file_id: str,
|
810
|
+
*,
|
811
|
+
variant_type: Optional[str] = None,
|
812
|
+
request_id: Optional[str] = None,
|
813
|
+
**metadata
|
814
|
+
) -> GetVariantsResponse:
|
815
|
+
"""
|
816
|
+
获取文件的压缩变体
|
817
|
+
|
818
|
+
Args:
|
819
|
+
file_id: 文件ID
|
820
|
+
variant_type: 变体类型(image, video, thumbnail)
|
821
|
+
request_id: 请求ID,用于追踪
|
822
|
+
**metadata: 额外的gRPC元数据
|
823
|
+
|
824
|
+
Returns:
|
825
|
+
GetVariantsResponse: 压缩变体响应
|
826
|
+
"""
|
827
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
828
|
+
|
829
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
830
|
+
|
831
|
+
request = file_service_pb2.GetVariantsRequest(file_id=file_id)
|
832
|
+
if variant_type:
|
833
|
+
request.variant_type = variant_type
|
834
|
+
|
835
|
+
# 构建元数据
|
836
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
837
|
+
|
838
|
+
response = await stub.GetCompressedVariants(request, metadata=grpc_metadata)
|
839
|
+
|
840
|
+
# 转换压缩变体
|
841
|
+
variants = []
|
842
|
+
for variant in response.variants:
|
843
|
+
variants.append(CompressedVariant(
|
844
|
+
variant_name=variant.variant_name,
|
845
|
+
variant_type=variant.variant_type,
|
846
|
+
media_type=variant.media_type,
|
847
|
+
width=variant.width,
|
848
|
+
height=variant.height,
|
849
|
+
file_size=variant.file_size,
|
850
|
+
format=variant.format,
|
851
|
+
quality=variant.quality if variant.quality else None,
|
852
|
+
duration=variant.duration if variant.duration else None,
|
853
|
+
bitrate=variant.bitrate if variant.bitrate else None,
|
854
|
+
fps=variant.fps if variant.fps else None,
|
855
|
+
compression_ratio=variant.compression_ratio,
|
856
|
+
stored_path=variant.stored_path
|
857
|
+
))
|
858
|
+
|
859
|
+
return GetVariantsResponse(variants=variants)
|
860
|
+
|
861
|
+
async def trigger_recompression(
|
862
|
+
self,
|
863
|
+
file_id: str,
|
864
|
+
*,
|
865
|
+
force_reprocess: bool = False,
|
866
|
+
request_id: Optional[str] = None,
|
867
|
+
**metadata
|
868
|
+
) -> RecompressionResponse:
|
869
|
+
"""
|
870
|
+
触发文件重新压缩
|
871
|
+
|
872
|
+
Args:
|
873
|
+
file_id: 文件ID
|
874
|
+
force_reprocess: 是否强制重新处理
|
875
|
+
request_id: 请求ID,用于追踪
|
876
|
+
**metadata: 额外的gRPC元数据
|
877
|
+
|
878
|
+
Returns:
|
879
|
+
RecompressionResponse: 重新压缩响应
|
880
|
+
"""
|
881
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
882
|
+
|
883
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
884
|
+
|
885
|
+
request = file_service_pb2.RecompressionRequest(
|
886
|
+
file_id=file_id,
|
887
|
+
force_reprocess=force_reprocess
|
888
|
+
)
|
889
|
+
|
890
|
+
# 构建元数据
|
891
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
892
|
+
|
893
|
+
response = await stub.TriggerRecompression(request, metadata=grpc_metadata)
|
894
|
+
|
895
|
+
return RecompressionResponse(
|
896
|
+
task_id=response.task_id,
|
897
|
+
status=response.status
|
898
|
+
)
|
899
|
+
|
900
|
+
async def generate_variant_download_url(
|
901
|
+
self,
|
902
|
+
file_id: str,
|
903
|
+
variant_name: str,
|
904
|
+
*,
|
905
|
+
expire_seconds: int = 3600,
|
906
|
+
is_cdn: bool = False,
|
907
|
+
request_id: Optional[str] = None,
|
908
|
+
**metadata
|
909
|
+
) -> VariantDownloadUrlResponse:
|
910
|
+
"""
|
911
|
+
生成变体下载URL
|
912
|
+
|
913
|
+
Args:
|
914
|
+
file_id: 文件ID
|
915
|
+
variant_name: 变体名称(large/medium/small/thumbnail)
|
916
|
+
expire_seconds: 过期时间(秒)
|
917
|
+
is_cdn: 是否使用CDN
|
918
|
+
request_id: 请求ID,用于追踪
|
919
|
+
**metadata: 额外的gRPC元数据
|
920
|
+
|
921
|
+
Returns:
|
922
|
+
VariantDownloadUrlResponse: 变体下载URL响应
|
923
|
+
"""
|
924
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
925
|
+
|
926
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
927
|
+
|
928
|
+
request = file_service_pb2.VariantDownloadUrlRequest(
|
929
|
+
file_id=file_id,
|
930
|
+
variant_name=variant_name,
|
931
|
+
expire_seconds=expire_seconds,
|
932
|
+
is_cdn=is_cdn
|
933
|
+
)
|
934
|
+
|
935
|
+
# 构建元数据
|
936
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
937
|
+
|
938
|
+
response = await stub.GenerateVariantDownloadUrl(request, metadata=grpc_metadata)
|
939
|
+
|
940
|
+
# 转换变体信息
|
941
|
+
variant_info = None
|
942
|
+
if response.variant_info:
|
943
|
+
variant_info = CompressedVariant(
|
944
|
+
variant_name=response.variant_info.variant_name,
|
945
|
+
variant_type=response.variant_info.variant_type,
|
946
|
+
media_type=response.variant_info.media_type,
|
947
|
+
width=response.variant_info.width,
|
948
|
+
height=response.variant_info.height,
|
949
|
+
file_size=response.variant_info.file_size,
|
950
|
+
format=response.variant_info.format,
|
951
|
+
quality=response.variant_info.quality if response.variant_info.quality else None,
|
952
|
+
duration=response.variant_info.duration if response.variant_info.duration else None,
|
953
|
+
bitrate=response.variant_info.bitrate if response.variant_info.bitrate else None,
|
954
|
+
fps=response.variant_info.fps if response.variant_info.fps else None,
|
955
|
+
compression_ratio=response.variant_info.compression_ratio,
|
956
|
+
stored_path=response.variant_info.stored_path
|
957
|
+
)
|
958
|
+
|
959
|
+
return VariantDownloadUrlResponse(
|
960
|
+
url=response.url,
|
961
|
+
error=response.error if response.error else None,
|
962
|
+
variant_info=variant_info
|
963
|
+
)
|
@@ -12,6 +12,11 @@ from ...schemas import (
|
|
12
12
|
File,
|
13
13
|
FileListResponse,
|
14
14
|
GetFileResponse,
|
15
|
+
CompressionStatusResponse,
|
16
|
+
GetVariantsResponse,
|
17
|
+
RecompressionResponse,
|
18
|
+
VariantDownloadUrlResponse,
|
19
|
+
CompressedVariant,
|
15
20
|
)
|
16
21
|
from ...errors import FileNotFoundError
|
17
22
|
|
@@ -275,3 +280,215 @@ class AsyncFileService(BaseFileService):
|
|
275
280
|
files = [self._convert_file_info(f) for f in response.files]
|
276
281
|
|
277
282
|
return FileListResponse(files=files)
|
283
|
+
|
284
|
+
async def get_compression_status(
|
285
|
+
self,
|
286
|
+
file_id: str,
|
287
|
+
*,
|
288
|
+
request_id: Optional[str] = None,
|
289
|
+
**metadata
|
290
|
+
) -> CompressionStatusResponse:
|
291
|
+
"""
|
292
|
+
获取文件压缩状态
|
293
|
+
|
294
|
+
Args:
|
295
|
+
file_id: 文件ID
|
296
|
+
request_id: 请求ID,用于追踪
|
297
|
+
**metadata: 额外的gRPC元数据
|
298
|
+
|
299
|
+
Returns:
|
300
|
+
CompressionStatusResponse: 压缩状态响应
|
301
|
+
"""
|
302
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
303
|
+
|
304
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
305
|
+
|
306
|
+
request = file_service_pb2.CompressionStatusRequest(file_id=file_id)
|
307
|
+
|
308
|
+
# 构建元数据
|
309
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
310
|
+
|
311
|
+
response = await stub.GetCompressionStatus(request, metadata=grpc_metadata)
|
312
|
+
|
313
|
+
# 转换压缩变体
|
314
|
+
variants = []
|
315
|
+
for variant in response.variants:
|
316
|
+
variants.append(CompressedVariant(
|
317
|
+
variant_name=variant.variant_name,
|
318
|
+
variant_type=variant.variant_type,
|
319
|
+
media_type=variant.media_type,
|
320
|
+
width=variant.width,
|
321
|
+
height=variant.height,
|
322
|
+
file_size=variant.file_size,
|
323
|
+
format=variant.format,
|
324
|
+
quality=variant.quality if variant.quality else None,
|
325
|
+
duration=variant.duration if variant.duration else None,
|
326
|
+
bitrate=variant.bitrate if variant.bitrate else None,
|
327
|
+
fps=variant.fps if variant.fps else None,
|
328
|
+
compression_ratio=variant.compression_ratio,
|
329
|
+
stored_path=variant.stored_path
|
330
|
+
))
|
331
|
+
|
332
|
+
return CompressionStatusResponse(
|
333
|
+
status=response.status,
|
334
|
+
error_message=response.error_message if response.error_message else None,
|
335
|
+
variants=variants
|
336
|
+
)
|
337
|
+
|
338
|
+
async def get_compressed_variants(
|
339
|
+
self,
|
340
|
+
file_id: str,
|
341
|
+
*,
|
342
|
+
variant_type: Optional[str] = None,
|
343
|
+
request_id: Optional[str] = None,
|
344
|
+
**metadata
|
345
|
+
) -> GetVariantsResponse:
|
346
|
+
"""
|
347
|
+
获取文件的压缩变体
|
348
|
+
|
349
|
+
Args:
|
350
|
+
file_id: 文件ID
|
351
|
+
variant_type: 变体类型(image, video, thumbnail)
|
352
|
+
request_id: 请求ID,用于追踪
|
353
|
+
**metadata: 额外的gRPC元数据
|
354
|
+
|
355
|
+
Returns:
|
356
|
+
GetVariantsResponse: 压缩变体响应
|
357
|
+
"""
|
358
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
359
|
+
|
360
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
361
|
+
|
362
|
+
request = file_service_pb2.GetVariantsRequest(file_id=file_id)
|
363
|
+
if variant_type:
|
364
|
+
request.variant_type = variant_type
|
365
|
+
|
366
|
+
# 构建元数据
|
367
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
368
|
+
|
369
|
+
response = await stub.GetCompressedVariants(request, metadata=grpc_metadata)
|
370
|
+
|
371
|
+
# 转换压缩变体
|
372
|
+
variants = []
|
373
|
+
for variant in response.variants:
|
374
|
+
variants.append(CompressedVariant(
|
375
|
+
variant_name=variant.variant_name,
|
376
|
+
variant_type=variant.variant_type,
|
377
|
+
media_type=variant.media_type,
|
378
|
+
width=variant.width,
|
379
|
+
height=variant.height,
|
380
|
+
file_size=variant.file_size,
|
381
|
+
format=variant.format,
|
382
|
+
quality=variant.quality if variant.quality else None,
|
383
|
+
duration=variant.duration if variant.duration else None,
|
384
|
+
bitrate=variant.bitrate if variant.bitrate else None,
|
385
|
+
fps=variant.fps if variant.fps else None,
|
386
|
+
compression_ratio=variant.compression_ratio,
|
387
|
+
stored_path=variant.stored_path
|
388
|
+
))
|
389
|
+
|
390
|
+
return GetVariantsResponse(variants=variants)
|
391
|
+
|
392
|
+
async def trigger_recompression(
|
393
|
+
self,
|
394
|
+
file_id: str,
|
395
|
+
*,
|
396
|
+
force_reprocess: bool = False,
|
397
|
+
request_id: Optional[str] = None,
|
398
|
+
**metadata
|
399
|
+
) -> RecompressionResponse:
|
400
|
+
"""
|
401
|
+
触发文件重新压缩
|
402
|
+
|
403
|
+
Args:
|
404
|
+
file_id: 文件ID
|
405
|
+
force_reprocess: 是否强制重新处理
|
406
|
+
request_id: 请求ID,用于追踪
|
407
|
+
**metadata: 额外的gRPC元数据
|
408
|
+
|
409
|
+
Returns:
|
410
|
+
RecompressionResponse: 重新压缩响应
|
411
|
+
"""
|
412
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
413
|
+
|
414
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
415
|
+
|
416
|
+
request = file_service_pb2.RecompressionRequest(
|
417
|
+
file_id=file_id,
|
418
|
+
force_reprocess=force_reprocess
|
419
|
+
)
|
420
|
+
|
421
|
+
# 构建元数据
|
422
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
423
|
+
|
424
|
+
response = await stub.TriggerRecompression(request, metadata=grpc_metadata)
|
425
|
+
|
426
|
+
return RecompressionResponse(
|
427
|
+
task_id=response.task_id,
|
428
|
+
status=response.status
|
429
|
+
)
|
430
|
+
|
431
|
+
async def generate_variant_download_url(
|
432
|
+
self,
|
433
|
+
file_id: str,
|
434
|
+
variant_name: str,
|
435
|
+
*,
|
436
|
+
expire_seconds: int = 3600,
|
437
|
+
is_cdn: bool = False,
|
438
|
+
request_id: Optional[str] = None,
|
439
|
+
**metadata
|
440
|
+
) -> VariantDownloadUrlResponse:
|
441
|
+
"""
|
442
|
+
生成变体下载URL
|
443
|
+
|
444
|
+
Args:
|
445
|
+
file_id: 文件ID
|
446
|
+
variant_name: 变体名称(large/medium/small/thumbnail)
|
447
|
+
expire_seconds: 过期时间(秒)
|
448
|
+
is_cdn: 是否使用CDN
|
449
|
+
request_id: 请求ID,用于追踪
|
450
|
+
**metadata: 额外的gRPC元数据
|
451
|
+
|
452
|
+
Returns:
|
453
|
+
VariantDownloadUrlResponse: 变体下载URL响应
|
454
|
+
"""
|
455
|
+
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
456
|
+
|
457
|
+
stub = await self.client.get_stub(file_service_pb2_grpc.FileServiceStub)
|
458
|
+
|
459
|
+
request = file_service_pb2.VariantDownloadUrlRequest(
|
460
|
+
file_id=file_id,
|
461
|
+
variant_name=variant_name,
|
462
|
+
expire_seconds=expire_seconds,
|
463
|
+
is_cdn=is_cdn
|
464
|
+
)
|
465
|
+
|
466
|
+
# 构建元数据
|
467
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
468
|
+
|
469
|
+
response = await stub.GenerateVariantDownloadUrl(request, metadata=grpc_metadata)
|
470
|
+
|
471
|
+
# 转换变体信息
|
472
|
+
variant_info = None
|
473
|
+
if response.variant_info:
|
474
|
+
variant_info = CompressedVariant(
|
475
|
+
variant_name=response.variant_info.variant_name,
|
476
|
+
variant_type=response.variant_info.variant_type,
|
477
|
+
media_type=response.variant_info.media_type,
|
478
|
+
width=response.variant_info.width,
|
479
|
+
height=response.variant_info.height,
|
480
|
+
file_size=response.variant_info.file_size,
|
481
|
+
format=response.variant_info.format,
|
482
|
+
quality=response.variant_info.quality if response.variant_info.quality else None,
|
483
|
+
duration=response.variant_info.duration if response.variant_info.duration else None,
|
484
|
+
bitrate=response.variant_info.bitrate if response.variant_info.bitrate else None,
|
485
|
+
fps=response.variant_info.fps if response.variant_info.fps else None,
|
486
|
+
compression_ratio=response.variant_info.compression_ratio,
|
487
|
+
stored_path=response.variant_info.stored_path
|
488
|
+
)
|
489
|
+
|
490
|
+
return VariantDownloadUrlResponse(
|
491
|
+
url=response.url,
|
492
|
+
error=response.error if response.error else None,
|
493
|
+
variant_info=variant_info
|
494
|
+
)
|