bitwize-music-studio/cloud-uploader
Uploads promo videos and content to Cloudflare R2 or AWS S3. Use when the user wants to host promo content for social media or distribution.
npx skills add https://github.com/bitwize-music-studio/claude-ai-music-skills --skill cloud-uploader
Upload promo videos and other album content to cloud storage (Cloudflare R2 or AWS S3).
After generating promo videos with /bitwize-music:promo-director, upload them to cloud storage for:
Generate → Master → Promo Videos → **[Cloud Upload]** → Release
Optional step after promo-director, before release-director.
Cloud credentials must be configured in ~/.bitwize-music/config.yaml:
cloud:
enabled: true
provider: "r2" # or "s3"
# For Cloudflare R2
r2:
account_id: "your-account-id"
access_key_id: "your-access-key"
secret_access_key: "your-secret-key"
bucket: "promo-videos"
# For AWS S3
s3:
region: "us-west-2"
access_key_id: "your-access-key"
secret_access_key: "your-secret-key"
bucket: "promo-videos"
See ${CLAUDE_PLUGIN_ROOT}/reference/cloud/setup-guide.md for detailed setup instructions.
/bitwize-music:promo-director first){audio_root}/artists/{artist}/albums/{genre}/{album}/promo_videos/{audio_root}/artists/{artist}/albums/{genre}/{album}/album_sampler.mp4# If using the shared venv (recommended)
~/.bitwize-music/venv/bin/pip install -r ${CLAUDE_PLUGIN_ROOT}/requirements.txt
# Or install separately
pip install boto3
The upload script uses ~/.bitwize-music/venv if available, otherwise falls back to system Python.
Check config:
cat ~/.bitwize-music/config.yaml | grep -A 20 "cloud:"
Verify:
cloud.enabled: trueCheck promo videos exist:
ls {audio_root}/artists/{artist}/albums/{genre}/{album}/promo_videos/
ls {audio_root}/artists/{artist}/albums/{genre}/{album}/album_sampler.mp4
If missing:
Error: Promo videos not found.
Generate with: /bitwize-music:promo-director {album}
Call get_python_command() first to get the venv Python path and plugin root. Use these for all bash invocations below.
PYTHON="{python from get_python_command}"
PLUGIN_DIR="{plugin_root from get_python_command}"
Preview first:
$PYTHON "$PLUGIN_DIR/tools/cloud/upload_to_cloud.py" {album} --dry-run
Output shows:
Upload all (promos + sampler):
$PYTHON "$PLUGIN_DIR/tools/cloud/upload_to_cloud.py" {album}
Upload only track promos:
$PYTHON "$PLUGIN_DIR/tools/cloud/upload_to_cloud.py" {album} --type promos
Upload only album sampler:
$PYTHON "$PLUGIN_DIR/tools/cloud/upload_to_cloud.py" {album} --type sampler
Upload with public access:
$PYTHON "$PLUGIN_DIR/tools/cloud/upload_to_cloud.py" {album} --public
For R2:
{artist}/{album}/For S3:
aws s3 ls s3://{bucket}/{artist}/{album}/## Cloud Upload Complete
**Provider:** R2 (or S3)
**Bucket:** {bucket}
**Album:** {album}
**Uploaded Files:**
- {artist}/{album}/promos/01-track_promo.mp4
- {artist}/{album}/promos/02-track_promo.mp4
- ...
- {artist}/{album}/promos/album_sampler.mp4
**Total:** 11 files, 125.4 MB
**Next Steps:**
1. Verify files in cloud dashboard
2. If public: Test URLs work
3. Continue to release: /bitwize-music:release-director {album}
IMPORTANT: Cloud paths are FLAT - no genre folder.
The cloud path structure is different from the local content structure:
| Location | Path Structure |
|----------|----------------|
| Local content | {content_root}/artists/{artist}/albums/{genre}/{album}/ |
| Local audio | {audio_root}/artists/{artist}/albums/{genre}/{album}/ |
| Cloud | {artist}/{album}/ (no genre!) |
Files are organized in the bucket as:
{bucket}/
└── {artist}/
└── {album}/
└── promos/
├── 01-track_promo.mp4
├── 02-track_promo.mp4
├── ...
└── album_sampler.mp4
Example for album "my-album" by "bitwize" in rock genre:
~/music/artists/bitwize/albums/rock/my-album/bitwize/my-album/promos/ (NOT bitwize/albums/rock/my-album/)| Option | Description |
|--------|-------------|
| --type promos | Upload only track promo videos |
| --type sampler | Upload only album sampler |
| --type all | Upload both (default) |
| --dry-run | Preview without uploading |
| --public | Set files as public-read |
| --audio-root PATH | Override audio_root from config |
Basic upload:
/bitwize-music:cloud-uploader my-album
Preview only:
/bitwize-music:cloud-uploader my-album --dry-run
Upload promos only:
/bitwize-music:cloud-uploader my-album --type promos
Upload with public access:
/bitwize-music:cloud-uploader my-album --public
"Cloud uploads not enabled"
cloud.enabled: true to config${CLAUDE_PLUGIN_ROOT}/reference/cloud/setup-guide.md"Credentials not configured"
"Album not found"
{audio_root}/artists/{artist}/albums/{genre}/{album}/"No files found to upload"
/bitwize-music:promo-director {album}"Access Denied"
"Bucket not found"
--public flag only for files intended for public accesspromo-director:
After promo generation:
Promo videos generated successfully.
**Optional:** Upload to cloud storage: /bitwize-music:cloud-uploader {album}
release-director:
After cloud upload:
Cloud upload complete.
Ready for release workflow: /bitwize-music:release-director {album}
${CLAUDE_PLUGIN_ROOT}/reference/cloud/setup-guide.md - Detailed setup instructions${CLAUDE_PLUGIN_ROOT}/skills/promo-director/SKILL.md - Generate promo videos${CLAUDE_PLUGIN_ROOT}/skills/release-director/SKILL.md - Release workflowSonnet 4.5 - This skill runs scripts and coordinates workflow. No creative output from LLM.
Take bitwize-music-studio/cloud-uploader from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip.
Without those the skill loads but fails at the first command.