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