oss_upload.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from __future__ import annotations
  2. import os
  3. from typing import Any, Callable, Mapping
  4. import httpx
  5. DEFAULT_OSS_UPLOAD_URL = "http://crawler-upload-v2.aiddit.com/crawler/oss/upload_stream"
  6. DEFAULT_OSS_TIMEOUT_SECONDS = 60 * 60.0
  7. def upload_video_from_url(
  8. src_url: str,
  9. *,
  10. project: str | None = None,
  11. referer: Mapping[str, str] | None = None,
  12. use_proxy: bool = True,
  13. endpoint: str = DEFAULT_OSS_UPLOAD_URL,
  14. timeout_seconds: float = DEFAULT_OSS_TIMEOUT_SECONDS,
  15. http_post: Callable[..., Any] = httpx.post,
  16. ) -> dict[str, Any]:
  17. if not src_url:
  18. return _failure("missing_src_url")
  19. payload: dict[str, Any] = {
  20. "src_url": src_url,
  21. "src_type": "video",
  22. "use_proxy": use_proxy,
  23. }
  24. payload_mode = "no_referer"
  25. if project:
  26. payload["project"] = project
  27. try:
  28. response = http_post(endpoint, json=payload, timeout=timeout_seconds)
  29. response.raise_for_status()
  30. body = response.json()
  31. except httpx.HTTPError as exc:
  32. return _failure(
  33. "oss_upload_http_error",
  34. exception_type=type(exc).__name__,
  35. error_message=str(exc),
  36. http_status_code=_http_status(exc),
  37. oss_payload_mode=payload_mode,
  38. )
  39. except Exception as exc:
  40. return _failure(
  41. "oss_upload_failed",
  42. exception_type=type(exc).__name__,
  43. error_message=str(exc),
  44. oss_payload_mode=payload_mode,
  45. )
  46. oss_object = body.get("oss_object") if isinstance(body, dict) else None
  47. if not isinstance(oss_object, dict) or not oss_object.get("cdn_url"):
  48. return _failure(
  49. "oss_upload_response_invalid",
  50. oss_payload_mode=payload_mode,
  51. oss_response_summary=_response_summary(body),
  52. )
  53. return {
  54. "status": "ok",
  55. "oss_url": oss_object.get("cdn_url"),
  56. "oss_object_key": oss_object.get("oss_object_key"),
  57. "save_oss_timestamp": oss_object.get("save_oss_timestamp"),
  58. "oss_payload_mode": payload_mode,
  59. "raw_payload": body,
  60. }
  61. def upload_video_from_env(
  62. src_url: str,
  63. *,
  64. referer: Mapping[str, str] | None = None,
  65. timeout_seconds: float | None = None,
  66. http_post: Callable[..., Any] = httpx.post,
  67. ) -> dict[str, Any]:
  68. return upload_video_from_url(
  69. src_url,
  70. project=os.environ.get("CONTENT_AGENT_OSS_PROJECT") or None,
  71. referer=referer,
  72. use_proxy=_env_bool("CONTENT_AGENT_OSS_USE_PROXY", default=True),
  73. endpoint=os.environ.get("CONTENT_AGENT_OSS_UPLOAD_URL") or DEFAULT_OSS_UPLOAD_URL,
  74. timeout_seconds=(
  75. timeout_seconds
  76. if timeout_seconds is not None
  77. else float(os.environ.get("CONTENT_AGENT_OSS_TIMEOUT_SECONDS") or DEFAULT_OSS_TIMEOUT_SECONDS)
  78. ),
  79. http_post=http_post,
  80. )
  81. def _failure(
  82. failure_type: str,
  83. *,
  84. exception_type: str | None = None,
  85. error_message: str | None = None,
  86. http_status_code: int | None = None,
  87. oss_payload_mode: str | None = None,
  88. oss_response_summary: dict[str, Any] | None = None,
  89. ) -> dict[str, Any]:
  90. result: dict[str, Any] = {"status": "failed", "failure_type": failure_type}
  91. if exception_type:
  92. result["exception_type"] = exception_type
  93. if error_message:
  94. result["error_message"] = error_message
  95. if http_status_code is not None:
  96. result["http_status_code"] = http_status_code
  97. if oss_payload_mode:
  98. result["oss_payload_mode"] = oss_payload_mode
  99. if oss_response_summary:
  100. result["oss_response_summary"] = oss_response_summary
  101. return result
  102. def _response_summary(body: Any) -> dict[str, Any]:
  103. if not isinstance(body, dict):
  104. return {"body_type": type(body).__name__}
  105. oss_object = body.get("oss_object")
  106. return {
  107. "status": body.get("status"),
  108. "msg": body.get("msg"),
  109. "oss_object_present": isinstance(oss_object, dict),
  110. "oss_object_has_cdn_url": isinstance(oss_object, dict) and bool(oss_object.get("cdn_url")),
  111. }
  112. def _http_status(exc: httpx.HTTPError) -> int | None:
  113. response = getattr(exc, "response", None)
  114. status_code = getattr(response, "status_code", None)
  115. return int(status_code) if isinstance(status_code, int) else None
  116. def _env_bool(key: str, *, default: bool) -> bool:
  117. value = os.environ.get(key)
  118. if value is None or value == "":
  119. return default
  120. return value.strip().lower() not in {"0", "false", "no", "off"}