chore(format): fix black/isort after rebase to develop
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 34s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m50s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m14s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 34s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m50s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m14s
This commit is contained in:
@@ -48,9 +48,7 @@ class FeatureFlagUpdateRequest(BaseModel):
|
||||
|
||||
enabled: bool = Field(..., description="是否启用")
|
||||
percentage: int = Field(0, ge=0, le=100, description="灰度百分比 (0-100)")
|
||||
whitelist: list[str] = Field(
|
||||
default_factory=list, description="白名单列表(如 user_id)"
|
||||
)
|
||||
whitelist: list[str] = Field(default_factory=list, description="白名单列表(如 user_id)")
|
||||
|
||||
|
||||
class FeatureFlagResponse(BaseModel):
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
from . import dedup_helpers, ffmpeg_utils, oss_helpers
|
||||
from .processor import VideoProcessor, VideoResult
|
||||
from .render_adapter import RenderAdapter, RenderAdapterResult
|
||||
from .unified_render_service import RenderResult, UnifiedRenderService
|
||||
from .render_engine_resolver import RenderEngineResolver, get_render_engine_resolver
|
||||
from .unified_render_service import RenderResult, UnifiedRenderService
|
||||
|
||||
__all__ = [
|
||||
"VideoProcessor",
|
||||
|
||||
@@ -61,9 +61,7 @@ class RenderEngineResolver:
|
||||
refresh_interval: Redis flag 配置刷新间隔(秒)
|
||||
store: 直接传入 store 实例(测试用,优先级高于 redis_url)
|
||||
"""
|
||||
self._default_engine = (
|
||||
default_engine.lower() if default_engine else ENGINE_LEGACY
|
||||
)
|
||||
self._default_engine = default_engine.lower() if default_engine else ENGINE_LEGACY
|
||||
if self._default_engine not in VALID_ENGINES:
|
||||
logger.warning(
|
||||
"Invalid default engine '%s', fallback to '%s'",
|
||||
@@ -188,9 +186,7 @@ def get_render_engine_resolver() -> RenderEngineResolver:
|
||||
from worker_app.core.config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
redis_url = getattr(settings, "redis_url", None) or getattr(
|
||||
settings, "broker_url", None
|
||||
)
|
||||
redis_url = getattr(settings, "redis_url", None) or getattr(settings, "broker_url", None)
|
||||
default = getattr(settings, "render_engine", ENGINE_LEGACY)
|
||||
_resolver = RenderEngineResolver(
|
||||
default_engine=default,
|
||||
|
||||
@@ -109,9 +109,7 @@ def _compose_with_legacy_engine(task, job_service, job, plan_id: str, db) -> dic
|
||||
|
||||
# 构建合成命令
|
||||
job_service.update_progress(job_id, progress=30.0, current_stage="构建 FFmpeg 命令")
|
||||
_output_dir = os.environ.get(
|
||||
"VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output")
|
||||
)
|
||||
_output_dir = os.environ.get("VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output"))
|
||||
output_path = os.path.join(_output_dir, f"{job_id}.mp4")
|
||||
compose_cmd = compose_svc.build_compose_command(plan_id, output_path)
|
||||
|
||||
@@ -161,9 +159,7 @@ def _compose_with_unified_engine(task, job_service, job, plan_id: str, db) -> di
|
||||
job_id = job.id
|
||||
|
||||
# 标记为 running
|
||||
job_service.update_progress(
|
||||
job_id, progress=10.0, current_stage="初始化统一渲染引擎"
|
||||
)
|
||||
job_service.update_progress(job_id, progress=10.0, current_stage="初始化统一渲染引擎")
|
||||
|
||||
from video_processing.render_adapter import RenderAdapter
|
||||
|
||||
@@ -225,9 +221,7 @@ def _compose_with_unified_engine(task, job_service, job, plan_id: str, db) -> di
|
||||
def _cleanup_output(job_id: str) -> None:
|
||||
"""清理临时输出文件。"""
|
||||
try:
|
||||
_output_dir = os.environ.get(
|
||||
"VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output")
|
||||
)
|
||||
_output_dir = os.environ.get("VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output"))
|
||||
output_path = os.path.join(_output_dir, f"{job_id}.mp4")
|
||||
if Path(output_path).exists():
|
||||
Path(output_path).unlink()
|
||||
|
||||
@@ -84,9 +84,7 @@ class FeatureFlagConfig:
|
||||
|
||||
# 基于 identifier 做哈希取模,确保同一用户始终落在同一侧
|
||||
if identifier:
|
||||
hash_val = int(
|
||||
hashlib.md5(f"{self.name}:{identifier}".encode("utf-8")).hexdigest(), 16
|
||||
)
|
||||
hash_val = int(hashlib.md5(f"{self.name}:{identifier}".encode("utf-8")).hexdigest(), 16)
|
||||
return (hash_val % 100) < self.percentage
|
||||
|
||||
# 无 identifier 且百分比在 0-100 之间 → 按比例随机(不保证一致性)
|
||||
@@ -158,9 +156,7 @@ class RedisFeatureFlagStore(FeatureFlagStore):
|
||||
Fields: enabled, percentage, whitelist(JSON array)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, redis_url: str, key_prefix: str = FEATURE_FLAG_REDIS_PREFIX
|
||||
) -> None:
|
||||
def __init__(self, redis_url: str, key_prefix: str = FEATURE_FLAG_REDIS_PREFIX) -> None:
|
||||
import redis as redis_lib
|
||||
|
||||
self._redis = redis_lib.from_url(redis_url, decode_responses=True)
|
||||
@@ -242,9 +238,7 @@ class RedisFeatureFlagStore(FeatureFlagStore):
|
||||
try:
|
||||
cursor = 0
|
||||
while True:
|
||||
cursor, keys = self._redis.scan(
|
||||
cursor=cursor, match=pattern, count=100
|
||||
)
|
||||
cursor, keys = self._redis.scan(cursor=cursor, match=pattern, count=100)
|
||||
for key in keys:
|
||||
name = key[len(self._key_prefix) :]
|
||||
result[name] = self.get(name)
|
||||
|
||||
@@ -146,9 +146,7 @@ class TestInMemoryFeatureFlagStore:
|
||||
def test_set_and_get(self):
|
||||
"""设置后可以读取。"""
|
||||
store = InMemoryFeatureFlagStore()
|
||||
config = FeatureFlagConfig(
|
||||
name="test", enabled=True, percentage=50, whitelist={"u1"}
|
||||
)
|
||||
config = FeatureFlagConfig(name="test", enabled=True, percentage=50, whitelist={"u1"})
|
||||
store.set(config)
|
||||
|
||||
got = store.get("test")
|
||||
@@ -183,11 +181,7 @@ class TestInMemoryFeatureFlagStore:
|
||||
def test_is_active_convenience(self):
|
||||
"""is_active 便捷方法。"""
|
||||
store = InMemoryFeatureFlagStore()
|
||||
store.set(
|
||||
FeatureFlagConfig(
|
||||
name="render", enabled=True, percentage=0, whitelist={"vip_user"}
|
||||
)
|
||||
)
|
||||
store.set(FeatureFlagConfig(name="render", enabled=True, percentage=0, whitelist={"vip_user"}))
|
||||
assert store.is_active("render", "vip_user") is True
|
||||
assert store.is_active("render", "normal_user") is False
|
||||
assert store.is_active("nonexistent") is False
|
||||
@@ -283,9 +277,7 @@ class TestRenderEngineResolver:
|
||||
assert resolver.get_engine("user1") == "legacy"
|
||||
|
||||
# 开启 100%
|
||||
resolver.set_flag(
|
||||
FeatureFlagConfig(name="render_engine", enabled=True, percentage=100)
|
||||
)
|
||||
resolver.set_flag(FeatureFlagConfig(name="render_engine", enabled=True, percentage=100))
|
||||
assert resolver.get_engine("user1") == "unified"
|
||||
|
||||
# 关闭
|
||||
|
||||
Reference in New Issue
Block a user