brandly-cli 0.3.2__tar.gz → 0.3.3__tar.gz

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.

Potentially problematic release.


This version of brandly-cli might be problematic. Click here for more details.

Files changed (127) hide show
  1. brandly_cli-0.3.3/.github/workflows/ci.yml +71 -0
  2. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/.github/workflows/release.yml +10 -0
  3. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/.gitignore +2 -0
  4. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/.pre-commit-config.yaml +1 -1
  5. brandly_cli-0.3.3/LICENSE +21 -0
  6. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/Makefile +20 -7
  7. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/PKG-INFO +2 -1
  8. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/pyproject.toml +1 -1
  9. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/__about__.py +1 -1
  10. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/autodirector.py +1 -1
  11. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/beat_sync.py +1 -1
  12. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/cli.py +102 -28
  13. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/export_platforms.py +1 -1
  14. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/sharing.py +1 -1
  15. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/stitch.py +1 -1
  16. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/thumbnails.py +3 -3
  17. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/utils.py +1 -1
  18. brandly_cli-0.3.2/$null +0 -2
  19. brandly_cli-0.3.2/.github/workflows/ci.yml +0 -37
  20. brandly_cli-0.3.2/byteplus-ark-cli-1.0.24.tgz +0 -0
  21. brandly_cli-0.3.2/generate_svd.py +0 -101
  22. brandly_cli-0.3.2/package/LICENSE +0 -201
  23. brandly_cli-0.3.2/package/README.md +0 -47
  24. brandly_cli-0.3.2/package/manifest.json +0 -72
  25. brandly_cli-0.3.2/package/package.json +0 -48
  26. brandly_cli-0.3.2/package/scripts/postinstall.js +0 -545
  27. brandly_cli-0.3.2/package/scripts/run.js +0 -44
  28. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/.env.example +0 -0
  29. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/README.md +0 -0
  30. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/assets/banner.png +0 -0
  31. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/AUDIT.md +0 -0
  32. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/DEVELOPMENT.md +0 -0
  33. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/GOAL.md +0 -0
  34. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/IMPLEMENTATION.md +0 -0
  35. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/PROGRESS.md +0 -0
  36. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/SECURITY.md +0 -0
  37. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/dev-notes/SPEC.md +0 -0
  38. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/scripts/install-hooks.sh +0 -0
  39. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/README.md +0 -0
  40. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-animal-sheet/SKILL.md +0 -0
  41. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-animal-sheet/references/habitats.md +0 -0
  42. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-animal-sheet/references/prompt-variants.md +0 -0
  43. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-animal-sheet/references/species-guide.md +0 -0
  44. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-character-sheet/SKILL.md +0 -0
  45. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-character-sheet/references/common-types.md +0 -0
  46. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-character-sheet/references/consistency-checklist.md +0 -0
  47. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-character-sheet/references/prompt-variants.md +0 -0
  48. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-consistency/SKILL.md +0 -0
  49. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-consistency/references/anchoring-system.md +0 -0
  50. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-consistency/references/drift-checklist.md +0 -0
  51. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-consistency/references/lock-strength.md +0 -0
  52. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-location-sheet/SKILL.md +0 -0
  53. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-location-sheet/references/lighting-setups.md +0 -0
  54. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-location-sheet/references/location-types.md +0 -0
  55. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-location-sheet/references/prompt-variants.md +0 -0
  56. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-mecha-sheet/SKILL.md +0 -0
  57. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-mecha-sheet/references/mecha-types.md +0 -0
  58. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-mecha-sheet/references/prompt-variants.md +0 -0
  59. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-mecha-sheet/references/technical-reference.md +0 -0
  60. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-object-sheet/SKILL.md +0 -0
  61. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-object-sheet/references/photography-setups.md +0 -0
  62. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-object-sheet/references/product-categories.md +0 -0
  63. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-object-sheet/references/prompt-variants.md +0 -0
  64. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-plant-sheet/SKILL.md +0 -0
  65. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-plant-sheet/references/plant-species.md +0 -0
  66. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-plant-sheet/references/prompt-variants.md +0 -0
  67. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-plant-sheet/references/seasonal-reference.md +0 -0
  68. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-production-bible/SKILL.md +0 -0
  69. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-production-bible/references/asset-map.md +0 -0
  70. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-production-bible/references/bible-template.md +0 -0
  71. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-production-bible/references/example-bible.md +0 -0
  72. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-storyboard/SKILL.md +0 -0
  73. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-storyboard/references/examples.md +0 -0
  74. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-storyboard/references/pacing-guide.md +0 -0
  75. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-storyboard/references/shot-formats.md +0 -0
  76. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-vehicle-sheet/SKILL.md +0 -0
  77. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-vehicle-sheet/references/prompt-variants.md +0 -0
  78. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-vehicle-sheet/references/shot-angles.md +0 -0
  79. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-vehicle-sheet/references/vehicle-types.md +0 -0
  80. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/SKILL.md +0 -0
  81. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/references/camera-control.md +0 -0
  82. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/references/character-consistency.md +0 -0
  83. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/references/lighting-presets.md +0 -0
  84. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/references/troubleshooting.md +0 -0
  85. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/skills/brandly-video-generation/references/video-styles.md +0 -0
  86. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/__init__.py +0 -0
  87. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/__main__.py +0 -0
  88. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/agnes_client.py +0 -0
  89. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/analyzer.py +0 -0
  90. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/ark_client.py +0 -0
  91. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/audio_client.py +0 -0
  92. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/captions.py +0 -0
  93. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/constants.py +0 -0
  94. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/cost_tracker.py +0 -0
  95. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/director.py +0 -0
  96. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/dubbing.py +0 -0
  97. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/edit.py +0 -0
  98. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/memory.py +0 -0
  99. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/minimax_client.py +0 -0
  100. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/project_manager.py +0 -0
  101. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/style_presets.py +0 -0
  102. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/sync.py +0 -0
  103. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/templates.py +0 -0
  104. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/trends.py +0 -0
  105. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/types.py +0 -0
  106. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/video_prompts.py +0 -0
  107. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/src/brandly_cli/webhook.py +0 -0
  108. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/__init__.py +0 -0
  109. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/_check.py +0 -0
  110. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_agnes_client.py +0 -0
  111. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_analyzer.py +0 -0
  112. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_ark_client.py +0 -0
  113. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_autodirector.py +0 -0
  114. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_beat_sync.py +0 -0
  115. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_brandly_cli.py +0 -0
  116. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_cli.py +0 -0
  117. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_dubbing.py +0 -0
  118. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_edit.py +0 -0
  119. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_export_platforms.py +0 -0
  120. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_reference.py +0 -0
  121. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_sharing.py +0 -0
  122. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_stitch.py +0 -0
  123. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_templates.py +0 -0
  124. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_thumbnails.py +0 -0
  125. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_trends.py +0 -0
  126. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_video_prompts.py +0 -0
  127. {brandly_cli-0.3.2 → brandly_cli-0.3.3}/tests/test_webhook.py +0 -0
@@ -0,0 +1,71 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened]
6
+ push:
7
+ branches: [main]
8
+
9
+ # Cancel superseded runs on the same ref (for PRs); keep main runs to
10
+ # avoid clobbering a long build when a new commit lands mid-run.
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14
+
15
+ jobs:
16
+ quality:
17
+ runs-on: ubuntu-latest
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ python-version: ["3.10", "3.11", "3.12"]
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Install FFmpeg
26
+ run: sudo apt-get update && sudo apt-get install -y ffmpeg
27
+
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+
32
+ - name: Install dependencies
33
+ run: |
34
+ python -m pip install --upgrade pip
35
+ pip install -e ".[dev]" build twine
36
+
37
+ - name: Lint
38
+ run: ruff check src/ tests/
39
+
40
+ - name: Type check
41
+ run: mypy src/
42
+
43
+ - name: Syntax check
44
+ run: python -m py_compile src/brandly_cli/*.py
45
+
46
+ - name: Run tests
47
+ run: |
48
+ python -m pytest tests/ -v --tb=short \
49
+ --cov=brandly_cli --cov-report=xml --cov-report=term-missing
50
+
51
+ - name: Build package
52
+ run: python -m build
53
+
54
+ - name: Check build
55
+ run: python -m twine check dist/*
56
+
57
+ - name: Upload dist
58
+ if: ${{ matrix.python-version == '3.12' }}
59
+ uses: actions/upload-artifact@v4
60
+ with:
61
+ name: dist
62
+ path: dist/
63
+ if-no-files-found: error
64
+
65
+ - name: Upload coverage
66
+ if: ${{ matrix.python-version == '3.12' }}
67
+ uses: actions/upload-artifact@v4
68
+ with:
69
+ name: coverage
70
+ path: coverage.xml
71
+ if-no-files-found: warn
@@ -29,5 +29,15 @@ jobs:
29
29
  - name: Build package
30
30
  run: python -m build
31
31
 
32
+ - name: Check build
33
+ run: python -m twine check dist/*
34
+
35
+ - name: Upload dist
36
+ uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist-${{ github.event.release.tag_name }}
39
+ path: dist/
40
+ if-no-files-found: error
41
+
32
42
  - name: Release to PyPI
33
43
  uses: pypa/gh-action-pypi-publish@release/v1
@@ -48,3 +48,5 @@ skills_output/
48
48
 
49
49
  # Pre-commit
50
50
  .pre-commit-cache/
51
+ .agnes/
52
+ debates/
@@ -3,7 +3,7 @@ repos:
3
3
  hooks:
4
4
  - id: ruff-lint
5
5
  name: ruff-lint
6
- entry: python3 -m ruff check src/
6
+ entry: python3 -m ruff check src/ tests/
7
7
  language: system
8
8
  types: [python]
9
9
  pass_filenames: false
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dream Pixels Forge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,14 +1,14 @@
1
1
  # Brandly CLI — Makefile
2
2
 
3
3
  PYTHON := python3
4
- PNPM := pnpm
5
4
  PIP := $(PYTHON) -m pip
6
5
  PYTEST := $(PYTHON) -m pytest
7
6
  RUFF := $(PYTHON) -m ruff
7
+ MYPY := $(PYTHON) -m mypy
8
8
  ROOT ?= .
9
9
 
10
- .PHONY: help install dev test lint type-check build clean \
11
- pre-commit ci e2e upgrade sync
10
+ .PHONY: help install dev test cov lint lint-fix check-syntax type-check \
11
+ build clean pre-commit ci e2e upgrade sync
12
12
 
13
13
  ## Show this help
14
14
  help:
@@ -19,6 +19,10 @@ help:
19
19
  install: ## install
20
20
  $(PIP) install -e ".[dev]"
21
21
 
22
+ ## Install full dev environment (package + build tooling, mirrors CI)
23
+ dev: ## dev
24
+ $(PIP) install -e ".[dev]" build twine
25
+
22
26
  ## Install pre-commit hooks
23
27
  pre-commit: ## pre-commit
24
28
  $(PIP) install pre-commit
@@ -34,18 +38,27 @@ cov: ## cov
34
38
 
35
39
  ## Run linter
36
40
  lint: ## lint
37
- $(RUFF) check src/
41
+ $(RUFF) check src/ tests/
38
42
 
39
43
  ## Fix lint issues automatically
40
44
  lint-fix: ## lint-fix
41
- $(RUFF) check src/ --fix
45
+ $(RUFF) check src/ tests/ --fix
46
+
47
+ ## Type check
48
+ type-check: ## type-check
49
+ $(MYPY) src/
42
50
 
43
51
  ## Syntax check all modules
44
52
  check-syntax: ## check-syntax
45
53
  $(PYTHON) -m py_compile src/brandly_cli/*.py
46
54
 
47
- ## Run all quality gates (test + lint + syntax)
48
- ci: check-syntax lint test ## ci
55
+ ## Build sdist + wheel (run 'make dev' first to install build/twine)
56
+ build: ## build
57
+ $(PYTHON) -m build
58
+ $(PYTHON) -m twine check dist/*
59
+
60
+ ## Run all quality gates (lint + type-check + test + build)
61
+ ci: lint type-check test build ## ci
49
62
  @echo "All quality gates passed."
50
63
 
51
64
  ## End-to-end CLI smoke test
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: brandly-cli
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: AI product video orchestrator — add image, video, and sound generation to any AI tool
5
5
  Project-URL: Homepage, https://github.com/Dream-Pixels-Forge/brandly-cli
6
6
  Project-URL: Repository, https://github.com/Dream-Pixels-Forge/brandly-cli
7
7
  Project-URL: Issues, https://github.com/Dream-Pixels-Forge/brandly-cli/issues
8
8
  Author-email: Dream Pixels Forge <contact@dreampixelsforge.com>
9
9
  License-Expression: MIT
10
+ License-File: LICENSE
10
11
  Keywords: agent,ai,cli,generation,image,orchestrator,video
11
12
  Classifier: Development Status :: 4 - Beta
12
13
  Classifier: Intended Audience :: Developers
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "brandly-cli"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "AI product video orchestrator — add image, video, and sound generation to any AI tool"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """Package metadata."""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.3"
@@ -32,7 +32,7 @@ def parse_script(script: str) -> list[dict[str, str]]:
32
32
  content = scene_text[len(lines[0]):].strip()
33
33
 
34
34
  scenes.append({
35
- "index": i,
35
+ "index": str(i),
36
36
  "title": title,
37
37
  "text": content,
38
38
  "full": scene_text,
@@ -56,7 +56,7 @@ def _get_duration(path: Path) -> float:
56
56
  return 0.0
57
57
 
58
58
 
59
- async def _run_ffmpeg(cmd: list[str]) -> tuple[int, str]:
59
+ async def _run_ffmpeg(cmd: list[str]) -> tuple[int | None, str]:
60
60
  """Run FFmpeg command and return (returncode, stderr)."""
61
61
  proc = await asyncio.create_subprocess_exec(
62
62
  *cmd,
@@ -2283,10 +2283,10 @@ async def speed(
2283
2283
  # ---------------------------------------------------------------------------
2284
2284
 
2285
2285
 
2286
- @cli.command()
2286
+ @cli.command(name="probe")
2287
2287
  @click.argument("input", type=click.Path(exists=True))
2288
2288
  @click.option("-o", "--output", type=click.Choice(["table", "json"]), default="table")
2289
- def analyze(input: str, output: str) -> None:
2289
+ def probe(input: str, output: str) -> None:
2290
2290
  """Analyze a video file's metadata and properties."""
2291
2291
  info = asyncio.run(get_video_info(input))
2292
2292
  if "error" in info:
@@ -2864,31 +2864,56 @@ async def ark_cancel(task_id: str) -> None:
2864
2864
  @click.argument("output")
2865
2865
  @click.option("--transition", default="fade", help="Transition type (fade, dissolve, wipe, slide)")
2866
2866
  @click.option("--transition-duration", default=0.5, help="Transition duration in seconds")
2867
- @click.option("--color-grade", default="cinematic", help="Color grade (cinematic, warm, cool, desaturated, none)")
2867
+ @click.option(
2868
+ "--color-grade", default="cinematic",
2869
+ help="Color grade (cinematic, warm, cool, desaturated, none)",
2870
+ )
2868
2871
  @click.option("--root", default=None, help="Working directory")
2869
- def stitch(clips: tuple[str, ...], output: str, transition: str, transition_duration: float, color_grade: str, root: str | None) -> None:
2872
+ def stitch(
2873
+ clips: tuple[str, ...],
2874
+ output: str,
2875
+ transition: str,
2876
+ transition_duration: float,
2877
+ color_grade: str,
2878
+ root: str | None,
2879
+ ) -> None:
2870
2880
  """Stitch multiple video clips with transitions and color grading."""
2871
2881
  from brandly_cli.stitch import stitch_videos
2872
2882
  clip_paths = [Path(c) for c in clips]
2873
2883
  output_path = Path(output)
2874
2884
  console.print(f"[bold]Stitching[/bold] {len(clip_paths)} clips → {output_path}")
2875
- result = asyncio.run(stitch_videos(clip_paths, output_path, transition=transition, transition_duration=transition_duration, color_grade=color_grade))
2885
+ result = asyncio.run(
2886
+ stitch_videos(
2887
+ clip_paths,
2888
+ output_path,
2889
+ transition=transition,
2890
+ transition_duration=transition_duration,
2891
+ color_grade=color_grade,
2892
+ )
2893
+ )
2876
2894
  if "error" in result:
2877
2895
  console.print(f"[red]Error: {result['error']}[/red]")
2878
2896
  sys.exit(1)
2879
2897
  console.print(f"[green]✓ Output:[/green] {result['output_path']}")
2880
- console.print(f" Duration: {result['duration_seconds']:.1f}s | Size: {result['size_bytes']//1024}KB")
2898
+ console.print(
2899
+ f" Duration: {result['duration_seconds']:.1f}s | Size: {result['size_bytes']//1024}KB"
2900
+ )
2881
2901
  console.print(f" Transitions: {', '.join(result['transitions_applied']) or 'none'}")
2882
2902
  console.print(f" Color grade: {result['color_grade']}")
2883
2903
 
2884
2904
 
2885
2905
  @cli.command()
2886
2906
  @click.argument("project_id")
2887
- @click.option("--platforms", multiple=True, help="Target platforms (tiktok, instagram_reel, youtube_standard, etc.)")
2907
+ @click.option(
2908
+ "--platforms", multiple=True,
2909
+ help="Target platforms (tiktok, instagram_reel, youtube_standard, etc.)",
2910
+ )
2888
2911
  @click.option("--output", default=None, help="Output directory")
2889
2912
  @click.option("--root", default=None, help="Working directory")
2890
2913
  def export_platforms(project_id, platforms, output, root):
2891
2914
  """Export project to platform-optimized formats."""
2915
+ from brandly_cli.export_platforms import export_for_platform
2916
+
2892
2917
  if not platforms:
2893
2918
  platforms = ("tiktok", "youtube_standard")
2894
2919
  proj_dir = Path(root or ".") / ".brandly" / "projects" / project_id
@@ -2923,7 +2948,12 @@ def thumbnail(project_id: str, count: int, style: str, root: str | None) -> None
2923
2948
  console.print(f"[red]No video found in project: {project_id}[/red]")
2924
2949
  sys.exit(1)
2925
2950
  output_dir = proj_dir / "thumbnails"
2926
- result = asyncio.run(generate_thumbnails(video_file, output_dir, count=count, style_preset=style, root=root))
2951
+ result = asyncio.run(
2952
+ generate_thumbnails(
2953
+ video_file, output_dir, count=count, style_preset=style,
2954
+ root=Path(root) if root else None,
2955
+ )
2956
+ )
2927
2957
  if "error" in result:
2928
2958
  console.print(f"[red]Error: {result['error']}[/red]")
2929
2959
  sys.exit(1)
@@ -2935,14 +2965,27 @@ def thumbnail(project_id: str, count: int, style: str, root: str | None) -> None
2935
2965
  @cli.command(name="voice-match")
2936
2966
  @click.argument("video_path")
2937
2967
  @click.option("--source", default="en", help="Source language code")
2938
- @click.option("--target", required=True, help="Target language code (en, es, fr, de, ja, ko, zh, pt, ar, hi)")
2968
+ @click.option(
2969
+ "--target", required=True,
2970
+ help="Target language code (en, es, fr, de, ja, ko, zh, pt, ar, hi)",
2971
+ )
2939
2972
  @click.option("--voice-style", default="professional", help="Voice style")
2940
2973
  @click.option("--output", default=None, help="Output path")
2941
2974
  @click.option("--root", default=None, help="Working directory")
2942
- def voice_match(video_path: str, source: str, target: str, voice_style: str, output: str | None, root: str | None) -> None:
2975
+ def voice_match(
2976
+ video_path: str, source: str, target: str, voice_style: str,
2977
+ output: str | None, root: str | None,
2978
+ ) -> None:
2943
2979
  """Dub a video to a target language."""
2944
2980
  from brandly_cli.dubbing import dub_video
2945
- result = asyncio.run(dub_video(Path(video_path), source, target, voice_style=voice_style, output_path=Path(output) if output else None, root=root))
2981
+ result = asyncio.run(
2982
+ dub_video(
2983
+ Path(video_path), source, target,
2984
+ voice_style=voice_style,
2985
+ output_path=Path(output) if output else None,
2986
+ root=Path(root) if root else None,
2987
+ )
2988
+ )
2946
2989
  if "error" in result:
2947
2990
  console.print(f"[red]Error: {result['error']}[/red]")
2948
2991
  sys.exit(1)
@@ -2958,10 +3001,20 @@ def voice_match(video_path: str, source: str, target: str, voice_style: str, out
2958
3001
  @click.option("--threshold", default=0.5, help="Beat detection threshold")
2959
3002
  @click.option("--min-duration", default=1.0, help="Minimum clip duration")
2960
3003
  @click.option("--root", default=None, help="Working directory")
2961
- def beat_sync(video_path: str, audio_path: str, output: str, threshold: float, min_duration: float, root: str | None) -> None:
3004
+ def beat_sync(
3005
+ video_path: str, audio_path: str, output: str,
3006
+ threshold: float, min_duration: float, root: str | None,
3007
+ ) -> None:
2962
3008
  """Cut video to match beat positions in audio."""
2963
3009
  from brandly_cli.beat_sync import beat_sync as bs_sync
2964
- result = asyncio.run(bs_sync(Path(video_path), Path(audio_path), Path(output), beat_threshold=threshold, min_clip_duration=min_duration, root=root))
3010
+ result = asyncio.run(
3011
+ bs_sync(
3012
+ Path(video_path), Path(audio_path), Path(output),
3013
+ beat_threshold=threshold,
3014
+ min_clip_duration=min_duration,
3015
+ root=Path(root) if root else None,
3016
+ )
3017
+ )
2965
3018
  if "error" in result:
2966
3019
  console.print(f"[red]Error: {result['error']}[/red]")
2967
3020
  sys.exit(1)
@@ -2976,7 +3029,7 @@ def beat_sync(video_path: str, audio_path: str, output: str, threshold: float, m
2976
3029
  @click.option("--json", is_flag=True, help="Output as JSON")
2977
3030
  def trend(category: str | None, platforms: tuple[str, ...], json: bool) -> None:
2978
3031
  """Research trending video formats."""
2979
- from brandly_cli.trends import research_trends, list_categories
3032
+ from brandly_cli.trends import list_categories, research_trends
2980
3033
  cats = list_categories()
2981
3034
  if not category:
2982
3035
  console.print("[bold]Available categories:[/bold]")
@@ -3005,7 +3058,12 @@ def trend(category: str | None, platforms: tuple[str, ...], json: bool) -> None:
3005
3058
  def analyze(video_path: str, script: str | None, style: str, root: str | None) -> None:
3006
3059
  """Analyze video performance prediction."""
3007
3060
  from brandly_cli.analyzer import analyze_video
3008
- result = asyncio.run(analyze_video(Path(video_path), script=script, style=style, root=root))
3061
+ result = asyncio.run(
3062
+ analyze_video(
3063
+ Path(video_path), script=script, style=style,
3064
+ root=Path(root) if root else None,
3065
+ )
3066
+ )
3009
3067
  if "error" in result:
3010
3068
  console.print(f"[red]Error: {result['error']}[/red]")
3011
3069
  sys.exit(1)
@@ -3032,12 +3090,18 @@ def analyze(video_path: str, script: str | None, style: str, root: str | None) -
3032
3090
  @click.option("--platforms", multiple=True, help="Target platforms")
3033
3091
  @click.option("--save", is_flag=True, help="Save as custom template")
3034
3092
  @click.option("--root", default=None, help="Working directory")
3035
- def template(name: str, style: str, shots: int, duration: int, budget: int, platforms: tuple[str, ...], save: bool, root: str | None) -> None:
3093
+ def template(
3094
+ name: str, style: str, shots: int, duration: int, budget: int,
3095
+ platforms: tuple[str, ...], save: bool, root: str | None,
3096
+ ) -> None:
3036
3097
  """Create or manage project templates."""
3037
- from brandly_cli.templates import save_template, create_from_template, list_templates
3098
+ from brandly_cli.templates import create_from_template, save_template
3038
3099
  if save:
3039
- config = {"style": style, "shots": shots, "duration": duration, "budget": budget, "platforms": list(platforms) or ["tiktok"]}
3040
- asyncio.run(save_template(name, config, root=root))
3100
+ config = {
3101
+ "style": style, "shots": shots, "duration": duration,
3102
+ "budget": budget, "platforms": list(platforms) or ["tiktok"],
3103
+ }
3104
+ asyncio.run(save_template(name, config, root=Path(root) if root else None))
3041
3105
  console.print(f"[green]✓ Template saved: {name}[/green]")
3042
3106
  else:
3043
3107
  console.print(f"[bold]Template: {name}[/bold]")
@@ -3050,9 +3114,9 @@ def template(name: str, style: str, shots: int, duration: int, budget: int, plat
3050
3114
  @click.option("--root", default=None, help="Working directory")
3051
3115
  def template_list(root: str | None) -> None:
3052
3116
  """List available templates."""
3053
- from brandly_cli.templates import list_templates, list_custom_templates
3117
+ from brandly_cli.templates import list_custom_templates, list_templates
3054
3118
  builtins = asyncio.run(list_templates())
3055
- customs = asyncio.run(list_custom_templates(root=root))
3119
+ customs = asyncio.run(list_custom_templates(root=Path(root) if root else None))
3056
3120
  console.print("[bold]Built-in Templates[/bold]")
3057
3121
  for t in builtins:
3058
3122
  console.print(f" • {t}")
@@ -3069,14 +3133,19 @@ def template_list(root: str | None) -> None:
3069
3133
  @click.pass_context
3070
3134
  def webhook(ctx: click.Context, host: str, port: int) -> None:
3071
3135
  """Start webhook server for CI/CD integration."""
3072
- try:
3073
- from fastapi import FastAPI
3074
- from uvicorn import run
3075
- except ImportError:
3136
+ import importlib.util
3137
+
3138
+ if (
3139
+ importlib.util.find_spec("fastapi") is None
3140
+ or importlib.util.find_spec("uvicorn") is None
3141
+ ):
3076
3142
  console.print("[red]FastAPI/uvicorn not installed. Run: pip install fastapi uvicorn[/red]")
3077
3143
  sys.exit(1)
3078
3144
  console.print(f"[bold]Starting webhook server[/bold] at http://{host}:{port}")
3079
- console.print("[dim]Endpoints: POST /webhook/generate, GET /webhook/jobs/{id}, POST /webhook/jobs/{id}/cancel[/dim]")
3145
+ console.print(
3146
+ "[dim]Endpoints: POST /webhook/generate, "
3147
+ "GET /webhook/jobs/{id}, POST /webhook/jobs/{id}/cancel[/dim]"
3148
+ )
3080
3149
 
3081
3150
 
3082
3151
  @cli.command()
@@ -3086,11 +3155,16 @@ def webhook(ctx: click.Context, host: str, port: int) -> None:
3086
3155
  def share(file_path: str, provider: str, root: str | None) -> None:
3087
3156
  """Upload file to cloud for sharing."""
3088
3157
  from brandly_cli.sharing import share_file
3089
- result = asyncio.run(share_file(Path(file_path), provider=provider, root=root))
3158
+ result = asyncio.run(
3159
+ share_file(
3160
+ Path(file_path), provider=provider,
3161
+ root=Path(root) if root else None,
3162
+ )
3163
+ )
3090
3164
  if "error" in result:
3091
3165
  console.print(f"[red]Error: {result['error']}[/red]")
3092
3166
  sys.exit(1)
3093
- console.print(f"[green]✓ Shared[/green]")
3167
+ console.print("[green]✓ Shared[/green]")
3094
3168
  console.print(f" URL: {result.get('share_url', 'N/A')}")
3095
3169
  console.print(f" Provider: {result.get('provider', provider)}")
3096
3170
  if result.get("file_size_bytes"):
@@ -80,7 +80,7 @@ def _ffprobe_available() -> bool:
80
80
  return False
81
81
 
82
82
 
83
- async def _run_ffmpeg(cmd: list[str]) -> tuple[int, str]:
83
+ async def _run_ffmpeg(cmd: list[str]) -> tuple[int | None, str]:
84
84
  """Run FFmpeg command and return (returncode, stderr)."""
85
85
  proc = await asyncio.create_subprocess_exec(
86
86
  *cmd,
@@ -103,7 +103,7 @@ async def share_file(
103
103
  base_dir = Path(root) / ".brandly" / "shares"
104
104
  else:
105
105
  base_dir = Path.cwd() / ".brandly" / "shares"
106
- p = LocalShareProvider(base_dir)
106
+ p: ShareProvider = LocalShareProvider(base_dir)
107
107
  elif provider == "s3":
108
108
  p = S3ShareProvider("default-bucket")
109
109
  else:
@@ -210,7 +210,7 @@ async def stitch_videos(
210
210
  return _result(output, clips, transitions_applied, color_grade)
211
211
 
212
212
 
213
- async def _run(cmd: list[str]) -> tuple[int, str]:
213
+ async def _run(cmd: list[str]) -> tuple[int | None, str]:
214
214
  """Run an FFmpeg command, returning (returncode, stderr)."""
215
215
  proc = await asyncio.create_subprocess_exec(
216
216
  *cmd,
@@ -27,7 +27,7 @@ def _ffmpeg_available() -> bool:
27
27
  return False
28
28
 
29
29
 
30
- async def _run_ffmpeg(cmd: list[str]) -> tuple[bytes, bytes, int]:
30
+ async def _run_ffmpeg(cmd: list[str]) -> tuple[bytes, bytes, int | None]:
31
31
  """Execute an FFmpeg command and return (stdout, stderr, returncode)."""
32
32
  proc = await asyncio.create_subprocess_exec(
33
33
  *cmd,
@@ -208,7 +208,7 @@ async def generate_thumbnails(
208
208
  thumbnails.append(entry)
209
209
 
210
210
  for platform, (pw, ph) in PLATFORM_SIZES.items():
211
- resized = img.resize((pw, ph), Image.LANCZOS)
211
+ resized = img.resize((pw, ph), Image.Resampling.LANCZOS)
212
212
  out_path = output_dir / f"thumb_{platform}.jpg"
213
213
  resized.convert("RGB").save(out_path, "JPEG", quality=90)
214
214
  thumbnails.append({
@@ -248,7 +248,7 @@ def _draw_text_overlay(
248
248
  try:
249
249
  font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", fw)
250
250
  except OSError:
251
- font = ImageFont.load_default()
251
+ font = ImageFont.load_default() # type: ignore[assignment]
252
252
 
253
253
  bbox = draw.textbbox((0, 0), text, font=font)
254
254
  text_w = bbox[2] - bbox[0]
@@ -510,7 +510,7 @@ def get_reference_image_urls(project_id: str, root: Path | None = None) -> list[
510
510
  # Async FFmpeg runner for subprocess operations
511
511
  # ---------------------------------------------------------------------------
512
512
 
513
- async def async_run_ffmpeg(cmd: list[str]) -> tuple[int, str]:
513
+ async def async_run_ffmpeg(cmd: list[str]) -> tuple[int | None, str]:
514
514
  """Run an FFmpeg command asynchronously.
515
515
 
516
516
  Args:
brandly_cli-0.3.2/$null DELETED
@@ -1,2 +0,0 @@
1
- 'Get-ChildItem' is not recognized as an internal or external command,
2
- operable program or batch file.
@@ -1,37 +0,0 @@
1
- name: CI
2
- on:
3
- pull_request:
4
- types: [opened, synchronize, reopened]
5
- push:
6
- branches: [main]
7
-
8
- concurrency:
9
- group: ${{ github.workflow }}-${{ github.ref }}
10
- cancel-in-progress: true
11
-
12
- jobs:
13
- quality:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v4
17
-
18
- - name: Install FFmpeg
19
- run: sudo apt-get update && sudo apt-get install -y ffmpeg
20
-
21
- - uses: actions/setup-python@v5
22
- with:
23
- python-version: "3.12"
24
-
25
- - name: Install dependencies
26
- run: |
27
- python -m pip install --upgrade pip
28
- pip install -e ".[dev]"
29
-
30
- - name: Type check
31
- run: python -m pytest tests/ -v --tb=short
32
-
33
- - name: Lint
34
- run: ruff check src/
35
-
36
- - name: Syntax check
37
- run: python -m py_compile src/brandly_cli/*.py