
Alibabacloud Oss Media Process
- 97 installs
- 208 repo stars
- Updated August 4, 2026
- aliyun/alibabacloud-aiops-skills
alibabacloud-oss-media-process is a Claude skill that processes images, audio, and video stored in Alibaba Cloud OSS - resizing, watermarking, transcoding, and running IMM intelligent detection.
About
This skill processes images, audio, and video files stored in Alibaba Cloud OSS. A developer uses it to resize, crop, watermark, and convert images, run IMM intelligent features like face detection and blind watermarking, and transcode video, extract audio, take screenshots, and build sprite sheets or HLS streams. Results are returned as a signed URL, downloaded locally, or saved as a new OSS object, and it also supports plain file upload and download.
- Processes images, audio, and video stored in Alibaba Cloud OSS, with 14+ image operations
- Adds IMM intelligent features (blind watermark, face/body/car detection, QR recognition, labeling, scoring) and video tr
- Returns results as a signed URL, local download, or new OSS object via synchronous or asynchronous processing
Alibabacloud Oss Media Process by the numbers
- 97 all-time installs (skills.sh)
- Ranked #825 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
alibabacloud-oss-media-process capabilities & compatibility
- Works with
- aws
- Use cases
- image generation · video generation
What alibabacloud-oss-media-process says it does
Process images, audio, and video files stored in Alibaba Cloud OSS.
Supports 14+ image operations (resize, crop, rotate, watermark, blur, format conversion, etc.),
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills --skill alibabacloud-oss-media-processAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 97 |
|---|---|
| repo stars | ★ 208 |
| Last updated | August 4, 2026 |
| Repository | aliyun/alibabacloud-aiops-skills ↗ |
What it does
Process media in Alibaba Cloud OSS: resize/crop/watermark images, run IMM detection, and transcode audio and video.
Who is it for?
Developers transforming media stored in Alibaba Cloud OSS - thumbnails, watermarks, transcoding, and IMM detection.
Skip if: Media not stored in Alibaba Cloud OSS or non-media file operations beyond upload/download.
When should I use this skill?
The user needs to resize, crop, watermark, transcode, or detect faces in media files stored in OSS.
By the numbers
- 14+ image operations
- Synchronous via x-oss-process and asynchronous via x-oss-async-process
Files
Alibaba Cloud OSS Media Processing
Process images, audio, and video files stored in Alibaba Cloud OSS using native OSS media processing capabilities. Synchronous processing returns immediate results via x-oss-process; asynchronous processing handles long-running jobs via x-oss-async-process with polling.
Default language: 默认中文回复。Only use English when the user explicitly writes in English.
Quick Start
Working directory
All script commands run from the skill package root. Use full absolute paths to invoke scripts:
python /path/to/skill/scripts/process.py ...Do not cd into the directory and use relative paths. If a script fails with "No such file or directory", use Glob to find **/alibabacloud-oss-media-process/scripts/process.py and use its full path.
Setup workspace output directory (run once per session):
WORKSPACE_OUTPUT=$(pwd)/outputs && mkdir -p "$WORKSPACE_OUTPUT"All --output-path arguments MUST use $WORKSPACE_OUTPUT/<filename> — files saved inside the skill directory will NOT be renderable.
Credentials (Aliyun CLI)
This skill uses Aliyun CLI for credential management. Python scripts auto-discover credentials via the alibabacloud-credentials default chain (supporting ~/.aliyun/config.json, environment variables, ECS instance roles, etc.).
Security rules:
- Never read, echo, print,
cat, or dump~/.aliyun/config.json, credential files, or any raw command output that containsaccess_key_id,access_key_secret,sts_token,AccessKeyId,AccessKeySecret, orSecurityTokenvalues. - Never ask the user to input AK/SK directly in the conversation or command line
- Guide users to use
aliyun configureto set up credentials securely - Never write
AccessKeyId,AccessKeySecret, orSecurityTokeninto any temporary Python/Shell script, here-doc, env export, or intermediate file. All credentials must be discovered through Aliyun CLI or the SDK default credential chain. - For credential diagnostics, use
aliyun configure list,python scripts/load_env.py, or other non-secret checks. If you must inspect configuration structure, only inspect non-sensitive fields and do not print secret or token values to the transcript. - Treat full presigned URLs as sensitive whenever they contain signing parameters such as
OSSAccessKeyId,accessKeyId,x-oss-credential,Signature,x-oss-signature,security-token,SecurityToken, orsts_token. Do not print these full URLs into the conversation transcript, command echo, markdown summary, or ordinary log files. - When a signed URL is needed for user consumption, distinguish between delivery and display: it is acceptable to generate a usable signed URL, but unless the runtime provides a secure private-output channel that does not enter the transcript or logs, only display a redacted URL or an OSS path in normal user-facing text.
Prerequisites
| Step | Action | Command |
|---|---|---|
| 1 | Install Aliyun CLI (>=3.3.3) | `curl -fsSL https://aliyuncli.alicdn.com/setup.sh |
| 2 | Configure credentials | aliyun configure |
| 3 | Run blocking preflight check 1 | python scripts/load_env.py |
| 4 | Run blocking preflight check 2 | aliyun configure list |
| 5 | Enable plugins | aliyun configure set --auto-plugin-install true && aliyun plugin update |
| 6 | Install Python deps | pip install -r scripts/requirements.txt |
| 7 | Set bucket/region (choose one) | export ALIBABA_CLOUD_OSS_BUCKET=<b> ALIBABA_CLOUD_OSS_REGION=<r> (add to ~/.bashrc/~/.zshrc for persistence), or pass --bucket <b> --region <r> on every command |
Blocking preflight policy:
python scripts/load_env.pymay report missing SDKs, missing credentials, missing bucket/region, or RAM permission problems.aliyun configure listmust show a usable configured CLI profile.- Treat preflight results as stale after any environment or runtime change. If you install Python packages, run
aliyun configure, change env vars, edit shell profiles, switch users, or otherwise modify credential/runtime state, you must rerun bothpython scripts/load_env.pyandaliyun configure listbefore the nextpython scripts/process.py ...command. - If either command fails these checks, stop immediately.
- Do not run
python scripts/process.py .... - Do not retry media processing.
- Do not simulate a successful result.
- Return only configuration guidance until both checks pass.
AI-Mode
Enable at session start:
aliyun configure ai-mode enable
aliyun configure ai-mode set-user-agent --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-oss-media-process"Disable on every exit: success, failure, error, cancellation, or session end:
aliyun configure ai-mode disablePreflight then Execute
When the user requests a media operation (resize, detect faces, watermark, etc.), apply the blocking preflight policy above before running any python scripts/process.py ... command. process.py also performs a runtime dependency preflight and exits with pip install -r scripts/requirements.txt guidance if required SDKs are missing. If you change the environment after a failed attempt (for example by installing dependencies, editing env vars, or re-running aliyun configure), do not assume the earlier preflight still holds — rerun the full blocking preflight first.
First-time setup
Direct users to run aliyun configure to set up credentials, then verify with:
aliyun configure listPython scripts use the alibabacloud-credentials SDK to auto-discover credentials from the Aliyun CLI config. Bucket and region are read from the ALIBABA_CLOUD_OSS_BUCKET / ALIBABA_CLOUD_OSS_REGION environment variables, or from --bucket / --region CLI flags. load_env.py scans shell config files (~/.bashrc, ~/.zshrc) for these exports and loads them into os.environ.
Recommended Workflow
Follow this numbered workflow for every request:
1. Prepare Confirm the bucket and region are available through --bucket / --region or the ALIBABA_CLOUD_OSS_BUCKET / ALIBABA_CLOUD_OSS_REGION environment variables. Apply the blocking preflight policy before any media command. Create $WORKSPACE_OUTPUT once per session for all local downloads.
2. Choose the source Use --source for an existing OSS object key. Use --uri for a local file path or HTTP(S) URL that should be uploaded temporarily before processing.
3. Decide the execution path Use python scripts/process.py for all media processing and file operations. If the request involves video, audio, HLS, or image-intelligent features, run python scripts/imm_admin.py auto-setup --bucket <b> --region <r> first to ensure IMM bucket binding exists.
4. Execute Build exactly one valid operation chain. Prefer --output-mode download --output-path $WORKSPACE_OUTPUT/<name> for sync image outputs, --output-mode save --target-key <key> for async media outputs, and --output-mode url when the result is meant to be consumed remotely.
5. Verify Read the returned JSON. Check success, request_id, task_id (async only), target_key, local path, and any validation_warnings. If the command downloaded a local file, present the absolute path to the user. Only report a local output path after the file was actually written to $WORKSPACE_OUTPUT and the returned absolute path matches the real downloaded file. Do not claim that a file was saved to outputs/... or any other local path unless it truly exists there. If you need to record task_id, request_id, target_key, generated_keys, or similar fields in logs, notes, or output files, extract them directly from the process.py JSON response. Do not transcribe, rewrite, or manually retype these values. If the user or eval explicitly requires verification of any machine-verifiable output property (for example codec, bitrate, sample rate, channel count, duration, resolution, frame rate, width, height, or format), prefer running one additional read-only verification step against a persisted OSS output object before finalizing the summary. Use audio/info or video/info for audio/video outputs, and use a separate --operations info command for image outputs. Do not download the file locally just for this purpose. For image width, height, format, and file-size verification, treat OSS-side --operations info on the saved target object as the default and preferred verification path. info is a standalone read-only image metadata operation, not a follow-up segment that should be appended to a basic image processing chain. Do not switch to local image-library inspection when info can answer the question. If a read-only verification step was performed and its result differs from the requested value, report the actual verified output value. Do not substitute the request value, and do not claim the request was fully satisfied when the verification result shows otherwise. If no read-only verification step was performed, do not describe machine-verifiable output properties as independently confirmed. Do not assume local verification tools such as PIL/Pillow, ffprobe, or similar utilities are installed. If a tool is unavailable, do not claim that you performed the corresponding local pixel-level or media-property verification. For image-property verification in particular, do not introduce ad hoc local-library checks such as PIL/Pillow unless the workflow explicitly requires a local-file-only inspection and OSS-side info cannot provide the property. In normal skill usage and evals, prefer OSS-side verification and avoid emitting local PIL/Pillow commands entirely. If the workflow returns only a signed URL and does not persist a reusable OSS target object, do not claim that you performed a follow-up info check on the final output object unless such an object actually exists. In that case, either save the result to OSS first and verify the saved object, or state that only the immediate processing result was available and no persisted-object verification was performed. Before sending the final user-facing summary, follow the Language rule in Result Presentation.
6. Recover If the command fails, use the Error Recovery table below. Retry only after correcting the concrete cause, such as missing IMM binding, bad parameters, or insufficient RAM permissions.
---
Quick Decision Guide
All processing goes through process.py
Image, video, and audio operations MUST be executed via python scripts/process.py --operations "...". The agent must not write its own SDK or CLI calls to bypass process.py or imm_admin.py for video/audio/image processing. Underlying SDK or API requests triggered internally by these scripts (including IMM requests such as CreateMediaConvertTask) are expected implementation behavior and do not count as direct agent-side SDK usage. The only intentional script-level IMM entry points are imm_admin.py for project setup and blindwatermark-extract for async watermark extraction.
Never create your own Python scripts or wrappers to bypass process.py. When process.py doesn't support a feature, check SKILL.md and references/ documentation, use --dry-run to preview, and report to the user if it truly cannot be done.
IMM setup (before IMM-dependent ops)
Before running video, audio, HLS, or image-intelligent operations, first run imm_admin.py auto-setup to ensure the bucket is bound to an IMM project. Pass --imm-project <project_name> only for blindwatermark-extract, or if you intentionally want to override the optional ALIBABA_CLOUD_IMM_PROJECT fallback used by that operation.
Source selection
- OSS object →
--source object-key - Local file or URL →
--uri /path/to/file(auto-uploads, processes, cleans up)
Sync vs Async (auto-detected)
- Sync (
x-oss-process): image ops,video/snapshot,video/info,audio/info,hls/m3u8, AI detection - Async (
x-oss-async-process):video/convert,video/animation,video/snapshots,video/sprite,video/concat,audio/convert,audio/concat,blindwatermark-extract
The script auto-detects async-only operations and handles routing/polling automatically — no --async or --wait flags needed.
Output rules
| Operation type | Output mode | Command pattern |
|---|---|---|
| Sync (image) | download | --output-mode download --output-path $WORKSPACE_OUTPUT/<file> |
| Async (video/audio) | save then download | 1. --output-mode save --target-key output/<file> → 2. --operations download --output-path $WORKSPACE_OUTPUT/<file> |
video/snapshots | save with auto-download | --output-mode save --target-key output/frames/frame --output-path $WORKSPACE_OUTPUT/ — script auto-polls and downloads all frames |
hls/m3u8 | url | --output-mode url — returns signed URL for browser/player (not a downloadable file) |
All --output-path MUST use $WORKSPACE_OUTPUT/<filename> — files saved inside the skill directory will NOT be renderable.
No-local-download rule: if the user explicitly says not to download locally, only to save in OSS, or only to return a link/URL, do not pass --output-path and do not perform any follow-up download for verification. Use --output-mode url for sync results meant to be consumed remotely, and use --output-mode save --target-key ... for async media results that should remain in OSS. Never download to $WORKSPACE_OUTPUT, /tmp, or any local path just to verify success; rely on the process.py JSON response instead.
Ambiguous save wording rule: if the user says "保存", "保存下来", "存起来", or similar wording but does not explicitly say "下载到本地", "本地查看", "给我本地文件", or another clear local-destination phrase, default to saving the result back to OSS with --output-mode save --target-key .... Only use --output-mode download --output-path ... when the user explicitly asks for a local file. If the user only wants to inspect the result and does not require a persisted local copy, prefer --output-mode url for sync outputs and --output-mode save plus the OSS path for async outputs.
Signed-URL delivery rule: the purpose of --output-mode url is to make a remote result accessible, not to force the full signed query string into the transcript. In ordinary text responses, prefer an OSS path or a redacted URL. Only provide a full presigned URL when the runtime offers a secure private-output channel that keeps the raw URL out of transcript/log surfaces. If no such channel exists, explain the limitation briefly and avoid printing the full signed query parameters. A redacted URL should keep the path and any non-sensitive query parameters, while replacing sensitive signing values with ***, for example: https://bucket.oss-cn-hangzhou.aliyuncs.com/output/result.webp?OSSAccessKeyId=***&x-oss-credential=***&Signature=***&security-token=***&Expires=1700000000.
Unique suffix rule: when you need a unique OSS target key suffix for evals, retries, or parallel runs, prefer Python-generated UUIDs or a timestamp-plus-random suffix. Do not rely on uuidgen being available. If you must generate a suffix from shell commands, first verify the command exists; otherwise fall back to a timestamp plus random digits. Safe shell example: SUFFIX=$(python3 -c "import uuid; print(uuid.uuid4().hex[:8])" 2>/dev/null || date +%Y%m%d_%H%M%S_$RANDOM).
Chaining rules
See the dedicated Chaining Rules section below for full chaining guidelines.
---
Core Parameter Rules
1. Only pass parameters the user specifies — do not invent defaults. OSS uses official defaults for unspecified parameters (e.g., keep original width/height, original bitrate, original framerate). 2. Recipes are examples, not defaults — parameter values in recipe tables (e.g., w=800, vb=2000000) are for specific scenarios and should NOT be used as defaults. 3. video/convert — remux vs re-encode: omitting vcodec means OSS only does remux (stream copy without re-encoding). Parameters like videoslim, vb, crf, s, fps are silently ignored in remux mode. Always specify `vcodec` (default `h264`) when the user says "transcode", "compress", or "slim". Only omit vcodec for pure remux (e.g., AVI→MP4 container switch) or audio extraction. 4. video/concat — when input params differ: if input videos have different resolution, framerate, or codec, you must ask the user which video to align to (option A: first video, B: second video, C: custom params). Never auto-decide. 5. video/concat — validation scope: process.py always performs input compatibility checks before submitting the async task. Additional local ffprobe output validation only runs when the command also downloads the result via --output-path. If you use --output-mode save without a local download path, there is no post-download media validation step. 6. Snapshots vs snapshot: use video/snapshots (async) for multi-frame extraction. Never use multiple video/snapshot calls as a workaround. video/snapshots target-key must NOT have a file extension. 7. For full parameter specifications, see the corresponding reference files in references/.
---
Result Presentation
After every successful process.py execution, present results in this format:
Language rule: unless the user explicitly requested English, the final user-facing result summary in this section must be written in Chinese. Use a result template that matches the response language. For Chinese responses, use a Chinese lead-in such as 处理结果如下: and Chinese field labels such as 状态 / 请求 ID / 任务 ID / 源文件 / 输出 / 参数 / 文件大小 / OSS 路径. For English responses, use Result summary: and the corresponding English labels Status / RequestID / Task ID / Source / Output / Params / File Size / OSS Path.
1. File path: output the local absolute path in a code block (e.g., /path/to/outputs/snapshot.jpg). Never use open or Read tool to display files. Only include this section when the file was actually downloaded or written locally. Do not present an outputs/... path that was only planned, inferred, or mentioned in a transcript.
2. Result table:
| Item | Detail |
|---|---|
| Status | ✅ Completed |
| RequestID | <request_id> (or N/A) |
| Task ID | <task_id> (async only) |
| Source | source/input.mp4 |
| Output | output/result.mp4 |
| Params | Dynamic — from your command (e.g., MP4/H.264/2Mbps, or 800x600/JPEG) |
| File Size | From download output |
| OSS Path | oss://<bucket>/<target-key> (save mode only) |
Field sourcing rules: Status and Params must be quoted directly from the process.py JSON response. Status must come from the returned success field, and Params must come from the returned operations field. Never rewrite, estimate, normalize, or summarize numeric/media values by hand, including confidence scores, bitrate, resolution, dimensions, frame rate, or codec details.
If you need a textual summary, include the original command or process string in a fenced code block and describe it conservatively. Do not invent parameter values or restate them in free-form prose when they are not explicitly present in the process.py response.
Final summary constraints:
- Do not insert fixed English filler such as
Task Completed Successfully. - Numeric values such as sample rate, bitrate, resolution, duration, frame rate, and file count must be copied directly from
process.pyJSON fields or an explicitly performed read-only verification result. - If a value was not obtained directly from machine output, omit it instead of rewriting, estimating, rounding, or normalizing it by hand.
- If an explicitly performed read-only verification result differs from the requested value, report the actual verified output value and describe the request as only partially satisfied when necessary. Do not replace the verified value with the requested one.
- If no read-only verification result was obtained, do not claim that machine-verifiable output properties were independently confirmed.
If the user forbids local downloads, omit the File path row/section entirely and do not create temporary local files for validation. In that case, present only the JSON-backed metadata returned by process.py, such as success, request_id, task_id, target_key, generated_keys, or url.
If process.py returns a signed URL, treat the full query string as sensitive output. In normal visible summaries, prefer the OSS path, target key, or a redacted URL. Do not expand raw signing parameters into the final summary unless the runtime has a secure private-output channel for secret delivery.
If independent verification was requested but the workflow returned only a signed URL and did not create a persisted OSS target object, do not claim that a follow-up info check was performed on a final output object. Either save the result first and verify the saved object, or state clearly that no persisted-object verification was available.
For image outputs and visual effects such as watermarks, overlays, blur regions, or face redaction, distinguish between metadata verification and visual verification. If the output was not downloaded or rendered locally, do not claim that a visual element was independently confirmed by inspection; state that only the service-reported processing result was verified unless a local render or explicit inspection step was actually performed.
Rules:
- Do not run
video/info,audio/info, or image--operations infoafter processing for ordinary result reporting. However, if the user explicitly asks you to verify concrete machine-verifiable output properties such as codec, bitrate, sample rate, channel count, duration, resolution, frame rate, width, height, or format, or if the eval/acceptance criteria explicitly require an independent property check, prefer running one additional read-only verification step against a persisted OSS output object and report that verification separately from the mainprocess.pyresult. Useaudio/infoorvideo/infofor audio/video outputs, and use a separate--operations infocommand for image outputs. - Do not assume local verification libraries or binaries such as
PIL/Pillow,ffprobe, or similar tools are preinstalled. Use them only when they are actually available and the workflow genuinely requires a local-file check; otherwise rely onprocess.pyJSON output and permitted read-only OSS-side checks. - For image width/height/format verification, prefer OSS-side
--operations infoon the saved target object even if a local file is present. Do not usePIL/Pillowas the default verification method for evals or routine skill runs. - Requests to verify image width, height, format, or similar machine-verifiable properties do not by themselves authorize a local download. If the user did not explicitly request a local file, and a saved OSS target object can be verified with
info, do not switch to--output-mode downloadsolely for verification. - Do not use
head_objectas a substitute for media-property verification. - Avoid
sleep+ retry loops; the script handles async polling internally. - All media processing goes through
process.py; if unsupported, checkreferences/and report — do not write custom scripts.
---
Chaining Rules
Image Operations
- Basic operations can be freely chained with each other
blindwatermark-embedcan follow basic ops but must be the last operationblindwatermark-extractmust be used alone — no chaining- AI detection (
faces,bodies,cars,codes,labels,score) must be used alone
Video/Audio Operations
- Video/audio operations cannot be chained with image operations
- Only one video/audio operation per request (no chaining)
- For complex workflows, use multiple separate requests
---
Credential & Environment Setup
Credentials are managed by Aliyun CLI (~/.aliyun/config.json). Python scripts auto-discover them via the alibabacloud-credentials SDK default chain. See Prerequisites above for setup steps.
Diagnostic check:
python scripts/load_env.pyThis scans for legacy env vars and verifies RAM permissions. Use this if operations fail with access errors.
Runtime dependency preflight: process.py checks required Python packages before execution. Basic OSS/file operations require oss2 and alibabacloud-credentials; video/audio/HLS/IMM operations also require the IMM SDK packages from scripts/requirements.txt. If any dependency is missing, the command fails fast with an install hint instead of starting a partial execution.
IMM project — usually discovered by imm_admin.py auto-setup. process.py only consumes --imm-project / ALIBABA_CLOUD_IMM_PROJECT for blindwatermark-extract.
---
IMM Auto-Setup
Video/audio processing and image-intelligent features require an IMM project bound to the bucket. Follow this workflow for IMM-dependent operations:
Step 1 — Detect IMM project (before any processing command):
python scripts/imm_admin.py auto-setup --bucket <bucket> --region <region>This ensures the bucket is bound to a usable IMM project and prints the resolved project name.
Step 2 — Execute the media operation:
python scripts/process.py --source video.mp4 \
--operations "video/convert:f=mp4,vcodec=h264" \
--output-mode save --target-key output/video.mp4For blindwatermark-extract, append --imm-project <project_name> if you do not want to rely on the optional ALIBABA_CLOUD_IMM_PROJECT fallback.
Step 3 — Present results per Execution & Output Workflow above.
Operations that require IMM bucket setup: all video/audio/HLS ops, image-intelligent ops (faces, bodies, cars, codes, labels, score, blindwatermark-embed/extract), smart crop (crop:g=auto/crop:g=face), face blur (blur:g=face/blur:g=faces). Only blindwatermark-extract requires the project name as a direct process.py input.
---
Available Operations
Image Processing (Sync)
| Operation | Description | Reference |
|---|---|---|
resize, crop, indexcrop, rotate, flip | Basic transformations | references/image-basic-operations.md |
quality, format, interlace | Quality & format | references/image-basic-operations.md |
watermark, blur, sharpen, bright, contrast | Effects | references/image-basic-operations.md |
auto-orient, circle, rounded-corners | Utilities | references/image-basic-operations.md |
info, average-hue | Metadata (JSON) | references/image-basic-operations.md |
Image-Intelligent (IMM)
| Operation | Mode | Description | Reference |
|---|---|---|---|
blindwatermark-embed | Sync | Embed invisible watermark. Must be last in chain. | references/image-imm-operations.md |
blindwatermark-extract | Async | Extract watermark. Use alone. | references/image-imm-operations.md |
faces, bodies, cars | Sync | Detect faces/bodies/cars (JSON). | references/image-imm-operations.md |
codes, labels, score | Sync | QR/barcode recognition, labels, quality score (JSON). | references/image-imm-operations.md |
Video Processing
| Operation | Mode | Description | Reference |
|---|---|---|---|
video/convert | Async | Transcode video. Must specify `vcodec` for re-encode. | references/video-operations.md |
video/snapshot | Sync | Extract single frame. t (time ms) required. | references/video-operations.md |
video/info | Sync | Video metadata (JSON). | references/video-operations.md |
video/animation | Async | Video to GIF/WebP. | references/video-operations.md |
video/snapshots | Async | Multi-frame extraction. target-key must NOT have extension. | references/video-operations.md |
video/sprite | Async | Sprite sheet. Must specify num or inter. | references/video-operations.md |
video/concat | Async | Concatenate videos (max 11). Must verify input params match. | references/video-operations.md |
Audio Processing
| Operation | Mode | Description | Reference |
|---|---|---|---|
audio/convert | Async | Transcode audio. | references/audio-operations.md |
audio/concat | Async | Concatenate audio files. | references/audio-operations.md |
audio/info | Sync | Audio metadata (JSON). | references/audio-operations.md |
HLS Streaming
| Operation | Mode | Description | Reference |
|---|---|---|---|
hls/m3u8 | Sync | HLS playlist (returns a playlist, not a file — use --output-mode url). | references/video-operations.md |
File Operations
| Operation | Mode | Description |
|---|---|---|
upload | Sync | Upload local file/URL to OSS. Use with --uri and --target-key. |
download | Sync | Download OSS object. Use with --source and --output-path. |
---
Processing Modes
- Synchronous (
x-oss-process): image basic processing,video/snapshot,video/info,audio/info,hls/m3u8, AI detection — results returned immediately - Asynchronous (
x-oss-async-process): video/audio transcoding, animation, sprite, snapshots, concat, blindwatermark-extract — auto-detected, auto-polled until completion
---
Usage
python scripts/process.py \
[--bucket BUCKET_NAME] \
[--region REGION_ID] \
(--source OSS_OBJECT_KEY | --uri URI) \
--operations OPERATION [OPERATION ...] \
[--output-mode url|download|save] \
[--expires SECONDS] \
[--output-path LOCAL_PATH] \
[--target-key OSS_TARGET_KEY] \
[--endpoint CUSTOM_ENDPOINT] \
[--imm-project IMM_PROJECT_NAME] \
[--dry-run]--imm-project is only consumed by blindwatermark-extract; other operations rely on IMM bucket binding, not this flag.
--uri
Process a file from a local file path or URL (http/https) without pre-uploading. The script auto-uploads to a temp key, processes, and cleans up. --uri and --source are mutually exclusive.
--dry-run
Prints the generated process string and operation details as JSON to stdout, then exits without connecting to OSS.
Operation String Format
Each operation: name:key=value,key=value. No-param operations use just the name (e.g., info, video/info). Video/audio operations use slash notation: video/convert, audio/convert.
End-to-End Example
User request:
Resize `images/photo.jpg` in OSS to width 600px, add a bottom-right text watermark `Copyright 2026`, and download the result locally. The bucket is `my-media-bucket` in region `cn-shanghai`.Command:
python scripts/process.py --bucket my-media-bucket --region cn-shanghai \
--source images/photo.jpg \
--operations "resize:w=600" "watermark:text=Copyright 2026,g=se,opacity=60,size=30" \
--output-mode download \
--output-path "$WORKSPACE_OUTPUT/photo-watermarked.jpg"Expected result shape:
{
"success": true,
"mode": "download",
"path": "/absolute/path/to/outputs/photo-watermarked.jpg",
"size": 12345,
"request_id": "xxxxxx"
}Interpretation:
success: truemeans OSS processing completed successfully.pathis the local file path you should present to the user.request_idis the server-side request trace ID for troubleshooting.
Additional Examples
# HLS streaming (with IMM auto-setup)
python scripts/imm_admin.py auto-setup --bucket my-bucket --region cn-hangzhou
# → Capture project name from output
python scripts/process.py --bucket my-bucket --region cn-hangzhou \
--source videos/input.mp4 \
--operations "hls/m3u8:ss=15000,t=1800000,vcodec=h264,fps=25,s=1280x720,vb=2000000,acodec=aac,ab=128000" \
--output-mode url
# Upload a local file to OSS
python scripts/process.py --bucket my-bucket --region cn-hangzhou \
--uri /path/to/report.pdf --operations upload --target-key documents/report.pdf
# Download a file from OSS
python scripts/process.py --bucket my-bucket --region cn-hangzhou \
--source documents/report.pdf --operations download --output-path $WORKSPACE_OUTPUT/report.pdfEdge Cases
watermarkvalues that contain commas should be quoted. For example:preprocess="resize:w=200,text=demo,image/logo.png".video/snapshotstarget keys must not include a file extension. Useoutput/frames/frame, notoutput/frames/frame.jpg.video/concatalways performs input compatibility checks before task submission. Additional localffprobeoutput validation only runs when the result is also downloaded via--output-path.- Async media polling defaults to 600 seconds. Override with
--timeout-seconds <n>orALIBABA_CLOUD_ASYNC_TIMEOUT_SECONDS. blindwatermark-extractmust run alone.blindwatermark-embedcan follow basic image operations, but it must be the last operation in the chain.
---
Error Recovery
| Error | Cause | Recovery |
|---|---|---|
Repeated AccessDenied or InvalidArgument twice in a row | Configuration or authorization is still unresolved, and blind retries risk fabricated diagnosis | Stop immediately. Do not simulate output, do not fabricate logs, and do not keep retrying process.py. Run aliyun configure list to verify the active CLI profile, then check RAM permissions with python scripts/check_permissions.py or the relevant RAM policy setup. If you changed dependencies, env vars, or CLI configuration while recovering, rerun python scripts/load_env.py and aliyun configure list before any next process.py attempt. |
task_id: null | IMM project not bound to bucket, or blindwatermark-extract missing --imm-project / ALIBABA_CLOUD_IMM_PROJECT | Run python scripts/imm_admin.py auto-setup --bucket <b> --region <r> first; for blindwatermark-extract, also pass --imm-project <project> if needed |
NoSuchKey | Source file does not exist in OSS | Check --source path, or upload first with --uri and upload operation |
AccessDenied / 403 | RAM policy missing required permissions | Run python scripts/check_permissions.py for diagnosis |
InvalidArgument | Wrong parameter format or unsupported combination | Check parameter spelling; verify against references/ docs |
| Async timeout / polling exceeds limit | Job too large or queue backlog | Note the task_id, tell user to retry later; do NOT use sleep loops |
---
Quick References
- Parameter details:
references/image-basic-operations.md,references/image-imm-operations.md,references/video-operations.md,references/audio-operations.md - RAM Permissions:
references/ram-policies.md - Format Support & Limitations:
references/limitations.md - IMM Administration:
references/imm-admin.md
Audio Operations Parameter Reference
Detailed parameter specifications for audio processing operations. Audio operations use slash notation (e.g., audio/convert, audio/concat). Only one audio operation can be specified per request — no chaining with other video/audio/image operations.
Table of Contents
---
audio/convert — Audio Transcoding
Transcode audio to a different format, bitrate, or sample rate. Asynchronous ONLY — requires --async flag.
| Parameter | Type | Description |
|---|---|---|
ss | int | Start time in milliseconds |
t | int | Duration in milliseconds |
f | string | Output format (required): mp3, aac, flac, oga, ac3, opus, amr |
ar | int | Audio sample rate in Hz (e.g., 44100, 48000) |
ac | int | Audio channels (1-8) |
aq | int | Audio quality (0-100). Mutually exclusive with ab |
ab | int | Audio bitrate in bps (e.g., 128000, 192000). Mutually exclusive with aq |
abopt | int | Audio bitrate optimization: 0 = exact, 1 = lower, 2 = higher |
adepth | int | Audio bit depth: 16 or 24. FLAC output only |
Note:aqandabare mutually exclusive. Useaqfor quality-based encoding orabfor bitrate-based encoding.
Examples:
audio/convert:f=aac,ab=96000,ar=48000,ac=2
audio/convert:f=mp3,ab=192000,ar=44100,ac=2
audio/convert:f=mp3,aq=90,ar=44100,ac=2
audio/convert:f=flac,ar=48000,ac=2,adepth=24
audio/convert:f=opus,ab=64000,ar=48000,ac=1
audio/convert:f=aac,ss=5000,t=30000,ab=128000---
audio/concat — Audio Concatenation
Concatenate multiple audio files into one. Asynchronous ONLY — requires --async flag.
The first audio file is specified via --source. Additional audio files are appended using /pre (before main) and /sur (after main) segments with their parameters.
| Parameter | Type | Description |
|---|---|---|
f | string | Output format (required): mp3, aac, flac, oga, ac3, opus, amr |
ar | int | Audio sample rate in Hz |
ac | int | Audio channels (1-8) |
aq | int | Audio quality (0-100) |
ab | int | Audio bitrate in bps |
abopt | int | Audio bitrate optimization: 0/1/2 |
align | int | Alignment index |
adepth | int | Audio bit depth: 16 or 24. FLAC output only |
Segment syntax (for additional audio files):
/pre,o_<base64_key>,ss_<ms>,t_<ms>— prepend segment/sur,o_<base64_key>,ss_<ms>,t_<ms>— append segment
Where <base64_key> is the URL-safe Base64-encoded OSS object key.
Examples:
audio/concat:f=mp3,ab=192000,ar=44100,ac=2,align=0
audio/concat:f=aac,ab=128000,ar=48000,ac=2
audio/concat:f=flac,ar=48000,ac=2,adepth=24---
audio/info — Audio Metadata
Extract audio metadata including duration, format, bitrate, stream information. Returns JSON. Synchronous processing. No parameters required.
Note: Anonymous access is NOT supported for audio/info.
Example:
audio/infoSample response data:
{
"Format": {
"Duration": "245.3",
"Size": "3920000",
"FormatName": "mp3",
"BitRate": "128000"
},
"Streams": {
"AudioStream": [{
"CodecName": "mp3",
"SampleRate": "44100",
"Channels": "2",
"BitRate": "128000"
}]
}
}---
Parameter specifications in this document are derived from the Alibaba Cloud OSS Audio Processing documentation and are reproduced here for quick reference.
Basic Operations Parameter Reference
Detailed parameter specifications for basic image processing operations. The script accepts operations in the format name:key=value,key=value. Multiple operations can be passed as separate --operations arguments and will be chained in order.
Table of Contents
- resize — Image Scaling
- crop — Image Cropping
- rotate — Image Rotation
- flip — Image Flip
- quality — Quality Adjustment
- format — Format Conversion
- watermark — Text or Image Watermark
- blur — Blur Effect
- sharpen — Sharpen Effect
- bright — Brightness Adjustment
- contrast — Contrast Adjustment
- auto-orient — Auto Orientation
- circle — Inscribed Circle Crop
- rounded-corners — Rounded Corners
- interlace — Progressive Display
- info — Image Metadata
- average-hue — Dominant Color
- Chaining Operations
---
resize — Image Scaling
Scale the image by specifying width, height, percentage, or scaling mode.
| Parameter | Type | Description |
|---|---|---|
w | int | Target width in pixels |
h | int | Target height in pixels |
l | int | Specify the longer side in pixels |
s | int | Specify the shorter side in pixels |
p | int (1-1000) | Scale by percentage. 50 = half size, 200 = double size |
mode | string | Scaling mode (see below) |
limit | 0 or 1 | Whether to limit scaling to original size. Only `p` (percentage) can upscale without `limit`. For `w`, `h`, `l`, `s` upscaling, you MUST set `limit=0`. 1 = limit (default). 0 = allow upscaling. |
color | string | Padding color in hex (used with pad mode, e.g., FFFFFF) |
Scaling modes (`mode`):
| Value | Description |
|---|---|
lfit | (Default) Proportionally scale so the image fits within w x h |
mfit | Proportionally scale so the image covers w x h (may exceed) |
fill | Proportionally scale then center-crop to exactly w x h |
pad | Proportionally scale to fit within w x h, then pad with color |
fixed | Force resize to exactly w x h (may distort) |
Examples:
resize:w=400,h=300
resize:w=800,mode=lfit
resize:l=1024
resize:p=50
resize:p=200 # Proportional upscale, no limit needed
resize:w=800,h=600,limit=0 # Fixed-size upscale, limit=0 is required
resize:w=200,h=200,mode=pad,color=F5F5F5---
crop — Image Cropping
Crop a region from the image.
| Parameter | Type | Description |
|---|---|---|
w | int | Crop width in pixels |
h | int | Crop height in pixels |
x | int | Horizontal offset from the origin point |
y | int | Vertical offset from the origin point |
g | string | Gravity / anchor point (see below) |
p | int (1-200) | Zoom ratio for g=face mode only. 100 = original size. Only works with `g=face` |
Gravity values (`g`):
| Value | Position |
|---|---|
nw | Top-left (default) |
north | Top-center |
ne | Top-right |
west | Center-left |
center | Center |
east | Center-right |
sw | Bottom-left |
south | Bottom-center |
se | Bottom-right |
auto | Smart crop — AI-recommended crop region (ignores w/h/p) |
face | Face crop — center on largest face (supports p zoom) |
注意: OSS 官方 API 要求使用全称 (north,west,center,east,south)。process.py同时接受缩写 (n,w,c,e,s) 并自动转换为全称。
Smart crop (`g=auto`, `g=face`): Requires IMM project binding. Does not support anonymous access.
Examples:
crop:w=200,h=200,g=c
crop:w=300,h=300,x=50,y=50
crop:w=100,h=100,g=se
crop:g=auto # Smart AI-recommended crop
crop:g=face,w=200,h=200 # Face-centered crop with fixed size
crop:g=face,p=150 # Face-centered crop, 1.5x zoom---
indexcrop — Indexed Slice
Split the image along the x or y axis into equal-sized blocks, then return one block by index. x and y are mutually exclusive (if both specified, y takes precedence).
| Parameter | Type | Description |
|---|---|---|
x | int | Split along x-axis; size of each block in pixels (range: [1, image width]) |
y | int | Split along y-axis; size of each block in pixels (range: [1, image height]) |
i | int | Block index to return (0-based). If index exceeds block count, returns original image |
Examples:
indexcrop:x=100,i=0
indexcrop:y=200,i=1---
rotate — Image Rotation
Rotate the image clockwise by a specified angle.
| Parameter | Type | Description |
|---|---|---|
angle | int (0-360) | Rotation angle in degrees (clockwise) |
degree | int (0-360) | Alias for angle, same meaning |
Examples:
rotate:angle=90
rotate:angle=180
rotate:angle=45---
flip — Image Flip
Flip the image horizontally, vertically, or both.
| Parameter | Type | Description |
|---|---|---|
v | int (0-2) | Flip direction: 0 = vertical flip, 1 = horizontal flip, 2 = both directions |
Examples:
flip:v=0
flip:v=1
flip:v=2---
quality — Quality Adjustment
Adjust the compression quality. Applicable to JPG and WebP formats.
| Parameter | Type | Description |
|---|---|---|
q | int (1-100) | Relative quality percentage |
Examples:
quality:q=80
quality:q=50---
format — Format Conversion
Convert the image to a different format.
| Parameter | Type | Description |
|---|---|---|
target | string | Target format: jpg, jpeg, png, webp, bmp, gif, avif, tiff, heic |
Examples:
format:target=webp
format:target=png
format:target=avif---
watermark — Text or Image Watermark
Add a text or image watermark to the image. Use text for text watermarks, or image for image watermarks. Chain multiple watermark operations to overlay multiple watermarks.
Common Parameters
| Parameter | Type | Description |
|---|---|---|
opacity | int (0-100) | Transparency, 100 = fully opaque |
g | string | Position: nw, north, ne, west, center, east, sw, south, se (缩写 n/w/c/e/s 会自动转换) |
x | int | Horizontal offset from the anchor point |
y | int | Vertical offset from the anchor point |
tile | int (0-1) | Tiling mode: 0 = single watermark (default), 1 = tiled across entire image. Works for both text and image watermarks. |
Text Watermark Parameters
| Parameter | Type | Description |
|---|---|---|
text | string | Watermark text content (plain text, auto Base64-encoded) |
size | int | Font size in pixels |
color | string | Font color in hex without # (e.g., FFFFFF) |
type | string | Font name (Base64 encoded). If omitted, a default font (wqy-microhei) is used |
shadow | string | Text shadow effect (JSON string, auto Base64-encoded). Format: {"Enable":true,"Color":"#000000","Opacity":50,"Size":10,"Distance":5,"Angle":15} |
Image Watermark Parameters
| Parameter | Type | Description |
|---|---|---|
image | string | OSS object key of the watermark image (plain path, auto Base64-encoded) |
rw | int (1-1000) | Watermark image width scaling percentage (relative to original watermark size) |
rh | int (1-1000) | Watermark image height scaling percentage (relative to original watermark size) |
aw | int (>0) | Absolute watermark width in pixels (auto-scales for different source image sizes) |
ah | int (>0) | Absolute watermark height in pixels (auto-scales for different source image sizes) |
preprocess | string | Preprocess the watermark image before applying. Chain sub-operations with + (e.g., resize,P_30+rotate,90). Supported sub-ops: resize, crop, indexcrop, rounded-corners, rotate. The script automatically combines the path with preprocess instruction and Base64-encodes. |
Scaling vs Auto-size: Userw/rhfor percentage-based scaling. Useaw/ahfor absolute pixel dimensions that auto-adjust based on source image size.aw/ahtake priority overrw/rh.
>
Preprocess: Uses+to chain sub-operations. Each sub-op uses basic operation syntax (e.g.,resize,P_30,rotate,90).
>
Multi-watermark: Chain multiplewatermarkoperations to overlay text + image or multiple image watermarks (e.g.,"watermark:text=Copyright" "watermark:image=logo.png,opacity=50,g=se").
Examples:
watermark:text=Copyright 2024,size=24,color=FFFFFF,opacity=60,g=se,x=10,y=10
watermark:text=SAMPLE,size=48,color=FF0000,opacity=30,g=c
watermark:text=DRAFT,size=36,opacity=15,tile=1 # Tiled text watermark
watermark:image=assets/logo.png,opacity=50,g=se,x=10,y=10
watermark:image=watermarks/draft.png,opacity=20,tile=1 # Tiled image watermark
watermark:image=logo.png,rw=20,rh=20 # 20% of original size
watermark:image=logo.png,aw=200,ah=100 # Fixed 200x100 pixels
watermark:image=panda.png,preprocess=resize,P_30,opacity=90,g=se # 30% scale watermark
watermark:image=logo.png,preprocess=resize,w_100+rotate,90,opacity=80 # resize + rotate---
blur — Blur Effect
Apply a Gaussian blur to the image.
| Parameter | Type | Description |
|---|---|---|
r | int (1-50) | Blur radius |
s | int (1-50) | Standard deviation (sigma) |
g | string | Face blur mode: face (blur largest face), faces (blur all faces) |
p | int (1-200) | Zoom ratio for face blur. Only works with `g=face` or `g=faces` |
Face blur (`g=face`, `g=faces`): Requires IMM project binding. Does not support anonymous access.
Examples:
blur:r=5,s=3
blur:r=10,s=8
blur:g=face,r=25,s=50 # Blur largest face
blur:g=faces,r=25,s=50 # Blur all faces
blur:g=face,p=200,r=25,s=50 # Blur largest face, 2x zoom---
sharpen — Sharpen Effect
Sharpen the image.
| Parameter | Type | Description |
|---|---|---|
v | int (50-399) | Sharpening value |
Examples:
sharpen:v=100
sharpen:v=200---
bright — Brightness Adjustment
Adjust the image brightness.
| Parameter | Type | Description |
|---|---|---|
v | int (-100 to 100) | Brightness value. Positive = brighter, negative = darker |
Examples:
bright:v=20
bright:v=-30---
contrast — Contrast Adjustment
Adjust the image contrast.
| Parameter | Type | Description |
|---|---|---|
v | int (-100 to 100) | Contrast value. Positive = more contrast, negative = less |
Examples:
contrast:v=30
contrast:v=-20---
auto-orient — Auto Orientation
Automatically rotate the image based on EXIF orientation data.
| Parameter | Type | Description |
|---|---|---|
v | int | 0=keep original direction, 1=auto-rotate (default when omitted: 1) |
Example:
auto-orient
auto-orient:v=1
auto-orient:v=0---
circle — Inscribed Circle Crop
Crop the image to an inscribed circle. The output format must support transparency (e.g., PNG).
| Parameter | Type | Description |
|---|---|---|
r | int | Circle radius in pixels |
Example:
circle:r=100---
rounded-corners — Rounded Corners
Apply rounded corners to the image. The output format must support transparency (e.g., PNG).
| Parameter | Type | Description |
|---|---|---|
r | int | Corner radius in pixels |
Example:
rounded-corners:r=20
rounded-corners:r=50---
interlace — Progressive Display
Enable or disable progressive/interlaced rendering for JPEG images.
| Parameter | Type | Description |
|---|---|---|
mode | 0 or 1 | 0 = disable, 1 = enable progressive display |
Example:
interlace:mode=1---
info — Image Metadata
Retrieve image metadata (dimensions, format, file size, etc.). Returns a JSON object with the image properties. No parameters required.
Example:
infoSample response data:
{
"FileSize": {"value": "1024000"},
"Format": {"value": "jpg"},
"ImageHeight": {"value": "1200"},
"ImageWidth": {"value": "1600"}
}---
average-hue — Dominant Color
Retrieve the dominant (average) color of the image as a hex value. No parameters required.
Example:
average-hueSample response data:
{
"RGB": "0x5c783a"
}---
Chaining Operations
Multiple basic image operations are processed in order. Pass each basic operation as a separate --operations argument:
python scripts/process.py \
--bucket my-bucket --region cn-hangzhou \
--source photo.jpg \
--operations "resize:w=800" "quality:q=80" "format:target=webp"This generates the OSS process string: image/resize,w_800/quality,Q_80/format,webp
Basic operations are applied sequentially — order matters. For example, resize before crop produces different results than crop before resize.
Chaining Rules
- Basic operations can be freely chained with each other.
- `info` and `average-hue` are standalone read-only metadata operations and must be used alone.
- `blindwatermark-embed` can follow basic operations but must be the last operation in the chain (e.g.,
resize:w=800thenblindwatermark-embed:content=Test). - `blindwatermark-extract` must be used alone — no chaining.
- AI detection operations (
faces,bodies,cars,codes,labels,score) must be used alone — they cannot be chained with basic, metadata, or watermark operations.
---
Parameter specifications in this document are derived from the Alibaba Cloud OSS Image Processing documentation and are reproduced here for quick reference.
IMM Operations Parameter Reference
Detailed parameter specifications for image-intelligent (IMM) operations. These operations are powered by Alibaba Cloud IMM (Intelligent Media Management) and require IMM service activated with the OSS bucket bound to an IMM project.
Table of Contents
- blindwatermark-embed — Blind Watermark Embedding
- blindwatermark-extract — Blind Watermark Extraction
- faces — Face Detection
- bodies — Body Detection
- cars — Car Detection
- codes — QR/Barcode Recognition
- labels — Image Labeling
- score — Image Quality Score
- IMM Chaining Rules
---
blindwatermark-embed — Blind Watermark Embedding
Embed an invisible blind watermark into the image. The watermark text is not visible to the human eye but can be extracted later using blindwatermark-extract. This operation requires IMM service activated and bucket bound to an IMM project.
| Parameter | Type | Description |
|---|---|---|
content | string | Watermark text to embed (plain text, auto Base64-encoded) |
s | string | Watermark strength: low, medium, high (default: low) |
Higher strength makes the watermark more resistant to image transformations (crop, compression, etc.) but may slightly affect image quality.
blindwatermark-embed produces a new image and uses process_object with sys/saveas. It supports all three output modes (url, download, save). When chained with other operations, it must be the last operation.
Examples:
blindwatermark-embed:content=Copyright2024,s=high
blindwatermark-embed:content=MyBrand---
blindwatermark-extract — Blind Watermark Extraction
Extract blind watermark text from a previously watermarked image. This is an asynchronous operation that uses the IMM SDK to create a decode task and poll for results. Requires --imm-project or the ALIBABA_CLOUD_IMM_PROJECT environment variable.
| Parameter | Type | Description |
|---|---|---|
s | string | Watermark strength used during embedding: low, medium, high |
model | string | Watermark algorithm model: FFT, FFT_FULL (optional) |
This operation must be used alone — it cannot be chained with other operations. The result is returned as JSON containing the extracted watermark content and task ID.
Examples:
blindwatermark-extract:s=high
blindwatermark-extract:s=medium,model=FFT---
faces — Face Detection
Detect faces in the image and return their locations and attributes. Returns a JSON response. Requires IMM service. No parameters required.
Example:
faces---
bodies — Body Detection
Detect human bodies in the image and return their bounding boxes. Returns a JSON response. Requires IMM service. No parameters required.
Example:
bodies---
cars — Car Detection
Detect cars in the image and return their bounding boxes. Returns a JSON response. Requires IMM service. No parameters required.
Example:
cars---
codes — QR/Barcode Recognition
Recognize QR codes and barcodes in the image. Returns a JSON response with decoded content. Requires IMM service. No parameters required.
Example:
codes---
labels — Image Labeling
Generate descriptive labels/tags for the image content. Returns a JSON response with label names and confidence scores. Requires IMM service. No parameters required.
Example:
labels---
score — Image Quality Score
Evaluate the aesthetic/technical quality of the image. Returns a JSON response with a quality score. Requires IMM service. No parameters required.
Example:
score---
IMM Chaining Rules
- `blindwatermark-embed` can follow basic operations but must be the last operation in the chain (e.g.,
resize:w=800thenblindwatermark-embed:content=Test). - `blindwatermark-extract` must be used alone — no chaining.
- AI detection operations (
faces,bodies,cars,codes,labels,score) must be used alone — they cannot be chained with basic, metadata, or watermark operations.
---
Parameter specifications in this document are derived from the Alibaba Cloud IMM documentation and are reproduced here for quick reference.
IMM Administration
Use scripts/imm_admin.py to manage IMM (Intelligent Media Management) projects and bucket bindings. IMM projects are required for image-intelligent operations such as blind watermark extraction and AI detection.
IMM administration requires specific RAM permissions (imm:CreateProject, imm:GetProject, imm:ListProjects, imm:DeleteProject, imm:AttachOSSBucket, imm:DetachOSSBucket).
Commands
Create an IMM project
python scripts/imm_admin.py create-project --project PROJECT_NAME --region REGION_IDList all projects
python scripts/imm_admin.py list-projects --region REGION_IDGet project details
python scripts/imm_admin.py get-project --project PROJECT_NAME --region REGION_IDBind a bucket to a project
python scripts/imm_admin.py bind-bucket --project PROJECT_NAME --bucket BUCKET_NAME --region REGION_IDUnbind a bucket from a project
python scripts/imm_admin.py unbind-bucket --project PROJECT_NAME --bucket BUCKET_NAME --region REGION_IDDelete a project
python scripts/imm_admin.py delete-project --project PROJECT_NAME --region REGION_IDThe delete command includes a protective pre-check: it queries the project's dataset count and blocks deletion if any datasets (bound buckets) remain. Unbind all buckets before deleting.
Typical Setup Flow
# 1. Create an IMM project
python scripts/imm_admin.py create-project --project my-imm-project --region cn-hangzhou
# 2. Bind your OSS bucket to the project
python scripts/imm_admin.py bind-bucket --project my-imm-project --bucket my-bucket --region cn-hangzhou
# 3. Verify the setup
python scripts/imm_admin.py get-project --project my-imm-project --region cn-hangzhouAfter setup, you can use IMM operations in scripts/process.py with --imm-project my-imm-project.
Media Processing Limitations
Constraints and supported formats for Alibaba Cloud OSS media processing (image, video, audio).
Image Processing Constraints
Source Image Constraints
| Constraint | Limit |
|---|---|
| Maximum file size | 20 MB |
| Maximum single side length | 30,000 px |
| Maximum single side for rotation | 4,096 px |
| Maximum total pixels | 250,000,000 (2.5 billion) |
Supported Image Source Formats
- JPG / JPEG
- PNG
- BMP
- GIF (animated)
- WebP
- TIFF
- HEIC
- AVIF
Animated GIF Restrictions
When the source image is an animated GIF, only the following operations are supported:
resizecroprotatewatermark(image mode)
All other operations are not applicable to animated GIF images.
Image Output Format Notes
circleandrounded-cornersrequire an output format that supports transparency (e.g., PNG or WebP). If the source is JPG, chain aformat:target=pngoperation.interlaceonly applies to JPEG output.qualityonly applies to JPG and WebP output.
Blind Watermark Constraints
| Constraint | Limit |
|---|---|
| Minimum image dimension | 80 px (both width and height) |
| Maximum image dimension | 10,000 px (both width and height) |
| Supported source formats | JPG, PNG, BMP, WebP, TIFF |
| Pure black/white images | Not supported (watermark may not embed/extract correctly) |
The watermark strength used for extraction (s) must match the strength used during embedding. Using a different strength level will result in extraction failure or garbled content.
IMM Detection Constraints
- All AI detection operations (
faces,bodies,cars,codes,labels,score) require IMM service activated and the OSS bucket bound to an IMM project. - Detection operations are subject to IMM QPS (queries per second) limits. Default limits vary by region and service tier.
- Source images must be in a format supported by IMM (JPG, PNG, BMP, WebP, TIFF).
Video Processing Constraints
Source Video Constraints
| Constraint | Limit |
|---|---|
| Maximum file size (sync) | Depends on operation timeout |
| Resolution range | 64-4096 px per side (for output) |
| Frame rate range | 0-240 fps |
| Video bitrate range | 10,000-100,000,000 bps |
| Audio bitrate range | 1,000-10,000,000 bps |
Supported Video Input Formats
avi, mpeg, mpg, dat, divx, xvid, rm, rmvb, mov, qt, asf, wmv, vob, 3gp, mp4, flv, avs, mkv, ts, ogm, nsv, swf, webm
Supported Video Output Formats (Offline Transcoding)
mp4, mkv, mov, asf, avi, mxf, ts, flv, webm, mp3, aac, flac, oga, ac3, opus, amr
Video Operation Constraints
| Operation | Mode | Notes |
|---|---|---|
video/convert | Sync / Async | Use async for large files |
video/snapshot | Sync only | Single frame extraction |
video/info | Sync only | Metadata, no anonymous access |
video/animation | Async ONLY | GIF/WebP output, 32-4096 px |
video/snapshots | Async ONLY | Multi-frame extraction |
video/sprite | Async ONLY | Sprite sheet, max 100 tiles per row/column |
video/concat | Async ONLY | Max 11 videos |
Video Animation Constraints
- Output width and height: 32-4096 px
- Output format must be
giforwebp
Video Sprite Constraints
- Sub-image width and height: 32-4096 px
- Tiles per row: 1-100 (default 6)
- Tiles per column: 1-100 (default 6)
- Padding: 0-100 px (default 2)
- Margin: 0-100 px (default 2)
Video Concat Constraints
- Maximum number of input videos: 11
- All input videos must be accessible in the same OSS bucket
- Output format, video codec, and audio codec are required parameters
Audio Processing Constraints
Supported Audio Input Formats
mp3, wav, aac, flac, ogg, wma, m4a, ac3, opus, amr, and other mainstream formats
Supported Audio Output Formats
mp3, aac, flac, oga, ac3, opus, amr
Audio Operation Constraints
| Operation | Mode | Notes |
|---|---|---|
audio/convert | Async ONLY | Requires output format (f) |
audio/concat | Async ONLY | Requires output format (f) |
audio/info | Sync only | Metadata, no anonymous access |
Audio Parameter Constraints
- Audio channels: 1-8
aq(quality) andab(bitrate) are mutually exclusiveadepth(bit depth: 16/24) only applies to FLAC output
HLS Streaming Constraints
hls/m3u8is synchronous and returns an M3U8 playlist- Segment duration (
st) is specified in milliseconds - Only
h264andh265video codecs are supported
General Constraints
Processing Mode Rules
- Image operations can be freely chained with each other (except detection/watermark restrictions).
- Video/audio operations cannot be chained with image operations.
- Only one video/audio operation per request (no chaining between media operations).
- Async-only operations will auto-upgrade to async mode even without the
--asyncflag.
Async Processing
- Maximum polling timeout: 600 seconds (10 minutes)
- Polling interval: 5 seconds
- Async operations require
--target-keyto specify the output location
---
Technical specifications in this document are derived from the Alibaba Cloud OSS documentation and are reproduced here for quick reference.
RAM Permissions
Required RAM permissions for this Skill.
OSS Permissions (required for all operations)
oss:GetObject — Read objects from the OSS bucket (used for url/download/info modes)
oss:PutObject — Write objects to the OSS bucket (used for save mode and upload operations)
oss:ProcessObject — Execute media processing operations on objects (used for save mode and async operations)
oss:PostProcessTask — Submit asynchronous processing tasks (used for async video/audio operations via x-oss-async-process)
oss:DeleteObject — Delete temporary objects from the OSS bucket (used by blindwatermark-embed in download/url mode to clean up temp files, and by --uri auto-cleanup)
oss:SignUrl — Generate signed URLs for processed media (used for url output mode)
Check Permissions (required for check_permissions.py)
oss:GetBucketInfo — Verify bucket accessibility and permissions
IMM Permissions (required for image-intelligent operations)
imm:CreateDecodeBlindWatermarkTask — Create a blind watermark extraction task
imm:GetTask — Query task status during blind watermark extraction polling
imm:GetDecodeBlindWatermarkResult — Retrieve blind watermark extraction results
IMM Administration Permissions (required for imm_admin.py)
imm:CreateProject — Create an IMM project
imm:ListProjects — List all IMM projects
imm:GetProject — Get details of an IMM project
imm:DeleteProject — Delete an IMM project
imm:AttachOSSBucket — Bind an OSS bucket to an IMM project
imm:DetachOSSBucket — Unbind an OSS bucket from an IMM project
Recommended Policy
For basic media processing (image + video + audio):
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:GetObject",
"oss:PutObject",
"oss:ProcessObject",
"oss:PostProcessTask",
"oss:DeleteObject",
"oss:SignUrl",
"oss:GetBucketInfo"
],
"Resource": [
"acs:oss:*:*:<your-bucket>",
"acs:oss:*:*:<your-bucket>/*"
]
}
]
}For media processing + IMM operations:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:GetObject",
"oss:PutObject",
"oss:ProcessObject",
"oss:PostProcessTask",
"oss:DeleteObject",
"oss:SignUrl",
"oss:GetBucketInfo"
],
"Resource": [
"acs:oss:*:*:<your-bucket>",
"acs:oss:*:*:<your-bucket>/*"
]
},
{
"Effect": "Allow",
"Action": [
"imm:CreateDecodeBlindWatermarkTask",
"imm:GetTask",
"imm:GetDecodeBlindWatermarkResult",
"imm:GetProject",
"imm:CreateProject",
"imm:ListProjects",
"imm:DeleteProject",
"imm:AttachOSSBucket",
"imm:DetachOSSBucket"
],
"Resource": "*"
}
]
}Common Recipes
Quick-reference templates for frequent use cases. Copy and adjust parameters as needed.
| Use Case | Operations | Flags | Notes |
|---|---|---|---|
| Thumbnail (width 400, webp) | "resize:w=400" "format:target=webp" | --output-mode download --output-path $WORKSPACE_OUTPUT/thumb.webp | Output path is sufficient |
| Compress JPEG to 80% quality | "quality:q=80" | --output-mode download --output-path $WORKSPACE_OUTPUT/compressed.jpg | Output path is sufficient |
| Resize + compress + convert | "resize:w=800" "quality:q=80" "format:target=webp" | --output-mode download --output-path $WORKSPACE_OUTPUT/optimized.webp | Output path is sufficient |
| Text watermark bottom-right | "watermark:text=YourText,g=se,size=40,color=FFFFFF,t=50" | --output-mode download --output-path $WORKSPACE_OUTPUT/watermarked.jpg | Output path is sufficient |
| Resize + format + blind watermark | "resize:w=1200" "format:target=png" "blindwatermark-embed:content=YourMark,s=high" | --output-mode save --target-key output/marked.png | Download to $WORKSPACE_OUTPUT |
| Video first N sec to GIF | "video/animation:f=gif,t=5000" | --output-mode save --target-key output/anim.gif | Only add w/h/fps when user specifies |
| Video transcode to MP4 | "video/convert:f=mp4,vcodec=h264" | --output-mode save --target-key output/video.mp4 | Must specify vcodec when user says "transcode" (default h264). Without vcodec, only remux is performed. Use user-specified codec if provided |
| Video slim (lqhd) | "video/convert:f=mp4,vcodec=h264,videoslim=1" | --output-mode save --target-key output/video_slim.mp4 | videoslim requires vcodec (default h264) or it is silently ignored |
| Video compress / reduce bitrate | "video/convert:f=mp4,vcodec=h264,vb=2000000" | --output-mode save --target-key output/video_vbr.mp4 | vb requires vcodec (default h264) or it is silently ignored |
| Audio WAV to AAC | "audio/convert:f=aac" | --output-mode save --target-key output/audio.aac | Only add ar/ac/ab when user specifies |
| Local file upload + resize | "resize:w=600,h=600" "format:target=jpg" | --uri /path/to/file --output-mode download --output-path $WORKSPACE_OUTPUT/resized.jpg | Output path is sufficient |
| Multi-frame snapshot (every 1s) | "video/snapshots:f=jpg,inter=1000" | --output-mode save --target-key output/frames/frame --output-path $WORKSPACE_OUTPUT/ | Single command auto-submits + polls + downloads all frames. Never use snapshot loop instead. target-key must NOT have file extension |
| Face detection | "faces" | Returns JSON; use alone, no chaining | |
| Video sprite (5x5 sheet) | "video/sprite:f=jpg,sw=160,sh=120,tw=5,th=5,num=25" | --output-mode save --target-key output/sprite.jpg | num=tw*th ensures enough frames to fill grid. Missing num/inter causes black cells |
| Video single frame at 5s | "video/snapshot:t=5000,f=jpg" | --output-mode download --output-path $WORKSPACE_OUTPUT/frame.jpg | Keep original size (omit w/h). Output path is sufficient |
| Video concat | "video/concat:f=mp4,vcodec=h264,acodec=aac/sur,o_<base64_key>,ss_0,t_<ms>" | --output-mode save --target-key output/concatenated.mp4 | Main video via --source, append via /sur,o_<base64>. Must validate input params (resolution, framerate, codec) match. Output duration should ≈ sum of inputs |
| HLS playlist | "hls/m3u8:ss=0,t=10000" | --output-mode url | Must use url mode. Never download/save. Returns signed URL for browser/player |
⚠️ Parameters in recipes are examples, NOT defaults. Only pass parameters the user explicitly requests. OSS uses official defaults for unspecified parameters.
Common Mistakes & Fixes
| Mistake | What Happens | Fix |
|---|---|---|
| video/snapshots target-key has file extension (e.g. `output/frame.jpg`) | All frames overwrite each other; only the last frame remains → you get just 1 image | target-key MUST NOT have extension, e.g. output/frames/frame. OSS auto-generates numbered files like frame_0_1.jpg, frame_0_2.jpg |
| video/convert without vcodec → remux only | When user says "transcode" but vcodec is omitted, OSS only does remux (stream copy). videoslim, vb, crf, s, fps are all silently ignored. Output file is same size as source | When user says "transcode"/"compress"/"slim", always specify vcodec (default h264 if user doesn't specify). Only omit vcodec when user explicitly says "remux" or it's a pure format conversion (e.g. AVI→MP4). See Core Parameter Rules section 3 in SKILL.md |
| video/sprite without num or inter | tw and th only control grid layout, not frame count. Without num or inter, may only capture 1 frame — the remaining cells are all black | Always specify num=tw*th (e.g. tw=5,th=5 → num=25) or appropriate inter (ms) to ensure enough frames fill the grid |
| Multi-frame tasks using video/snapshot loop instead of video/snapshots | Workflow is verbose; starting from t=1000 misses the first frame; results are inconsistent | Use video/snapshots (async) for multi-frame extraction. Never use multiple video/snapshot calls as a workaround |
| video/snapshots: manual polling or one-by-one download | Agent gets stuck in infinite loop, head_object can't find files | Add --output-path $WORKSPACE_OUTPUT/ to let process.py auto-poll and auto-download. No manual list/head/download needed |
| Adding user-unspecified parameters (e.g. w=800, vb=2000000) | Output is inconsistent — file size and dimensions vary randomly | Only pass parameters the user explicitly requests. Let OSS use official defaults for unspecified parameters |
Running video/info or audio/info to check async task status or get output details | Crashes because output file doesn't exist yet during transcoding | Never run additional commands to populate the result table. Use command parameters (codec, format, etc.) and process.py JSON output (task_id). process.py handles async polling internally |
Running python scripts/... without cd to skill directory | No such file or directory error | Use full absolute paths: python /full/path/to/scripts/process.py ... |
Running video/audio/IMM ops without --imm-project | task_id: null, silent failure, or need to retry | Always run imm_admin.py auto-setup first to get project name, then include --imm-project <name> in the process.py command |
Manually adding --async or --wait flags | Unnecessary; may cause confusion | The script auto-detects async-only operations. Just run process.py — no extra flags needed |
| Chain AI detection with image ops | Wrong output category, unexpected results | Use AI detection (faces, bodies, etc.) alone |
blindwatermark-embed not last in chain | Processing error | Always put blindwatermark-embed as the last operation |
--source + --uri together | Error, mutually exclusive | Use one: --source for OSS objects, --uri for local files |
Missing --output-mode save --target-key for async ops | Result not saved to OSS | Add both flags; default target-key to output/<filename> |
| Downloading to skill directory instead of workspace | File exists but UI cannot render it — image not displayed, video card not shown | All --output-path MUST use $WORKSPACE_OUTPUT/<file> (the absolute workspace path). Files inside the skill directory are invisible to the UI |
| Skipping file path output | User doesn't know where the file is | Always output the absolute file path in the text response after download |
| Using Read tool to display images | Read tool does not support displaying images in the chat dialog. User sees nothing | Never use Read tool for image preview. Just output the absolute file path for the user to view |
| video/concat without verifying input parameters | Videos with different resolution/framerate/codec may silently fail or only keep the first video, yet process.py still reports "success" | Always check resolution, framerate, and codec of all input videos before concat. If they differ, ask the user which video to align to — never auto-decide. process.py will validate and report mismatches |
| Claiming async task success without verifying output | OSS may return task_id but actual processing failed; Agent pretends task is complete | After async operations (especially video/concat), verify output is reasonable (e.g. duration ≈ sum of inputs). process.py auto-validates and flags validation_error |
| Writing custom Python scripts to bypass process.py | Creates wrapper scripts like concat_videos.py, audio_concat_wrapper.py — violates skill design principle, code is unmaintainable | All media processing goes through python scripts/process.py. When process.py lacks a feature, check SKILL.md and references/, use --dry-run to preview. Report to user if truly impossible — don't write custom code |
| Asking user whether to download | Wastes time, poor UX | All tasks auto-download. Never ask "do you want to download?" — just download automatically |
| Omitting RequestID from result table | Incomplete result presentation | RequestID is mandatory in every table — sync and async. From process.py JSON output, or N/A if unavailable |
| Using `open` command | Pops up external application window | Never use open. Just output the file path |
| Using show_widget or other MCP tools | Unnecessary tooling, adds complexity | Just output the file path |
IMM operation fails with ResourceNotFound or "not found" | Bucket not bound to IMM project | Run python scripts/imm_admin.py auto-setup to auto-create and bind, then include --imm-project <project_name> in the retry |
Using IMM SDK directly for video/audio (e.g. CreateMediaConvertTask) | Wrong interface; fails or produces unexpected results | All video/audio processing MUST use process.py which calls OSS x-oss-process. Never import alibabacloud_imm20200930 for media processing |
| Using download or save mode for `hls/m3u8` | Downloaded m3u8 file is useless (internal signed URLs will expire), or save has no point | hls/m3u8 must use --output-mode url. Show the signed URL in the result table for direct playback in browser/player |
Video Operations Parameter Reference
Detailed parameter specifications for video processing operations. Video operations use slash notation (e.g., video/convert, video/snapshot). Only one video operation can be specified per request — no chaining with other video/audio/image operations.
Table of Contents
- video/convert — Video Transcoding
- video/snapshot — Single Frame Extraction
- video/info — Video Metadata
- video/animation — Animated Image Conversion
- video/snapshots — Multi-Frame Extraction
- video/sprite — Sprite Sheet Generation
- video/concat — Video Concatenation
- hls/m3u8 — HLS Streaming Playlist
---
video/convert — Video Transcoding
Transcode video to a different format, codec, resolution, or bitrate. Supports both synchronous and asynchronous processing. Use async mode (--async) for large files or complex transcoding.
| Parameter | Type | Description |
|---|---|---|
f | string | Output format: mp4, mkv, mov, asf, avi, mxf, ts, flv, webm, mp3, aac, flac, oga, ac3, opus, amr |
vcodec | string | Video codec: copy (passthrough), h264, h265, vp9 |
acodec | string | Audio codec: copy (passthrough), mp3, aac, flac, vorbis, ac3, opus, pcm, amr |
s | string | Resolution as WxH (e.g., 1920x1080). Width and height must be 64-4096, even numbers |
fps | int | Frame rate (0-240). 0 = use source frame rate |
vb | int | Video bitrate in bps (10000-100000000) |
ab | int | Audio bitrate in bps (1000-10000000) |
ss | int | Start time in milliseconds |
t | int | Duration in milliseconds |
vn | int | Disable video: 1 = remove video stream |
an | int | Disable audio: 1 = remove audio stream |
sn | int | Disable subtitles: 1 = remove subtitle stream |
scaletype | string | Scale type: crop, stretch, fill, fit |
videoslim | int | Lightweight HD compression: 1 = enable |
crf | int | Constant Rate Factor (0-51). Lower = better quality, larger file |
pixfmt | string | Pixel format (e.g., yuv420p) |
ar | int | Audio sample rate in Hz |
ac | int | Audio channels (1-8) |
fpsopt | int | Frame rate optimization: 0 = exact, 1 = lower, 2 = higher |
sopt | int | Resolution optimization: 0 = exact, 1 = lower, 2 = higher |
vbopt | int | Video bitrate optimization: 0 = exact, 1 = lower, 2 = higher |
abopt | int | Audio bitrate optimization: 0 = exact, 1 = lower, 2 = higher |
arotate | int | Auto-rotate: 0 = disable, 1 = enable (default) |
Examples:
video/convert:f=mp4,vcodec=h264,s=1920x1080,vb=2000000,fps=30,acodec=aac,ab=100000
video/convert:f=mp4,vcodec=h265,videoslim=1,s=1920x1080,vb=2000000,sn=1
video/convert:f=mp4,vcodec=h264,ss=5000,t=30000
video/convert:f=webm,vcodec=vp9,vb=1500000,acodec=vorbis,ab=128000
video/convert:f=mp3,vn=1,acodec=mp3,ab=192000---
video/snapshot — Single Frame Extraction
Extract a single frame from a video at a specified time. Supports synchronous processing.
| Parameter | Type | Description |
|---|---|---|
t | int | Time position in milliseconds. 0 = first frame |
w | int | Output width in pixels |
h | int | Output height in pixels |
f | string | Output format: jpg, png |
m | string | Mode: fast = nearest keyframe (faster but less precise) |
ar | string | Auto-rotate: auto (default), h (horizontal), w (vertical) |
Examples:
video/snapshot:t=17000,f=jpg,w=800,h=600
video/snapshot:t=0,f=png,w=1920,h=1080
video/snapshot:t=5000,f=jpg,m=fast---
video/info — Video Metadata
Extract video metadata including duration, resolution, codec, bitrate, stream information. Returns JSON. Synchronous processing. No parameters required.
Note: Anonymous access is NOT supported for video/info.
Example:
video/infoSample response data:
{
"Format": {
"Duration": "120.5",
"Size": "15728640",
"FormatName": "mov,mp4,m4a,3gp,3g2,mj2"
},
"Streams": {
"VideoStream": [{
"CodecName": "h264",
"Width": "1920",
"Height": "1080",
"FrameRate": "30.0",
"BitRate": "2000000"
}],
"AudioStream": [{
"CodecName": "aac",
"SampleRate": "44100",
"Channels": "2",
"BitRate": "128000"
}]
}
}---
video/animation — Animated Image Conversion
Convert a video segment to an animated GIF or WebP image. Asynchronous ONLY — requires --async flag.
| Parameter | Type | Description |
|---|---|---|
ss | int | Start time in milliseconds |
f | string | Output format: gif, webp (required) |
num | int | Number of frames to extract |
inter | int | Interval between frames in milliseconds |
fps | int | Frame rate (0-240) |
w | int | Output width (32-4096) |
h | int | Output height (32-4096) |
scaletype | string | Scale type: crop, stretch, fill, fit |
Examples:
video/animation:f=gif,w=480,h=320,inter=1000
video/animation:f=webp,w=320,h=240,fps=10,ss=5000
video/animation:f=gif,w=200,h=200,num=20,scaletype=crop---
video/snapshots — Multi-Frame Extraction
Extract multiple frames from a video at regular intervals or keyframes. Asynchronous ONLY — requires --async flag.
| Parameter | Type | Description |
|---|---|---|
ss | int | Start time in milliseconds |
f | string | Output format: jpg, png (required) |
m | string | Extraction mode: inter (interval), key (keyframes), avg (average), dhash (perceptual hash dedup) |
num | int | Number of frames to extract |
inter | int | Interval between frames in milliseconds |
w | int | Output width (32-4096) |
h | int | Output height (32-4096) |
pw | int | Width scaling percentage (0-200) |
ph | int | Height scaling percentage (0-200) |
scaletype | string | Scale type: crop, stretch, fill, fit |
thr | int | Threshold (0-100). Used with dhash mode for deduplication sensitivity |
Examples:
video/snapshots:f=jpg,w=640,h=360,scaletype=crop,inter=10000
video/snapshots:f=png,m=key,w=1920,h=1080
video/snapshots:f=jpg,m=dhash,thr=5,w=512,h=512,inter=5000
video/snapshots:f=jpg,num=10,w=320,h=240---
video/sprite — Sprite Sheet Generation
Generate a sprite sheet (contact sheet) from video frames. Asynchronous ONLY — requires --async flag.
| Parameter | Type | Description |
|---|---|---|
ss | int | Start time in milliseconds |
f | string | Output format: jpg, png (required) |
m | string | Extraction mode: inter (interval), key (keyframes), avg (average), dhash (dedup) |
thr | int | Threshold (0-100) for dhash mode |
num | int | Number of frames |
inter | int | Interval between frames in milliseconds |
sw | int | Sub-image width (32-4096) |
sh | int | Sub-image height (32-4096) |
psw | int | Sub-image width scaling percentage (0-200) |
psh | int | Sub-image height scaling percentage (0-200) |
scaletype | string | Scale type: crop, stretch, fill, fit |
tw | int | Tiles per row (1-100, default 6) |
th | int | Tiles per column (1-100, default 6) |
pad | int | Padding between tiles (0-100, default 2) |
margin | int | Margin around sprite (0-100, default 2) |
Examples:
video/sprite:f=jpg,sw=200,sh=150,inter=2000,tw=3,th=3,pad=0,margin=0
video/sprite:f=png,sw=320,sh=240,num=36,tw=6,th=6
video/sprite:f=jpg,sw=160,sh=120,m=key,tw=4,th=4,pad=2,margin=2---
video/concat — Video Concatenation
Concatenate multiple videos into one. Asynchronous ONLY — requires --async flag. Maximum 11 videos.
The first video is specified via --source. Additional videos are appended using /pre (before main) and /sur (after main) segments with their parameters.
| Parameter | Type | Description |
|---|---|---|
f | string | Output format (required): mp4, mkv, etc. |
vcodec | string | Video codec (required): h264, h265, vp9 |
acodec | string | Audio codec (required): aac, mp3, etc. |
fps | int | Frame rate |
vb | int | Video bitrate (bps) |
ab | int | Audio bitrate (bps) |
ar | int | Audio sample rate (Hz) |
ac | int | Audio channels |
s | string | Resolution (WxH) |
align | int | Alignment index (which video to use as baseline) |
Segment syntax (for additional videos):
/pre,o_<base64_key>,ss_<ms>,t_<ms>— prepend segment/sur,o_<base64_key>,ss_<ms>,t_<ms>— append segment
Where <base64_key> is the URL-safe Base64-encoded OSS object key.
Examples:
video/concat:f=mp4,vcodec=h264,fps=25,vb=1000000,acodec=aac,ab=96000,ar=48000,ac=2,align=1---
hls/m3u8 — HLS Streaming Playlist
Generate an HLS (HTTP Live Streaming) M3U8 playlist for transcode-while-play. Synchronous processing.
| Parameter | Type | Description |
|---|---|---|
ss | int | Start time in milliseconds |
t | int | Duration in milliseconds |
ta | int | Pre-cache segment count |
st | int | Segment duration in milliseconds |
initd | int | Initial buffering duration in milliseconds |
vcodec | string | Video codec: h264, h265 |
fps | int | Frame rate |
fpsopt | int | Frame rate optimization: 0/1/2 |
pixfmt | string | Pixel format |
s | string | Resolution (WxH) |
sopt | int | Resolution optimization: 0/1/2 |
scaletype | string | Scale type: stretch, crop, fill, fit |
arotate | int | Auto-rotate: 0/1 |
vb | int | Video bitrate (bps) |
vbopt | int | Video bitrate optimization: 0/1/2 |
crf | int | Constant Rate Factor |
maxrate | int | Maximum bitrate (bps) |
bufsize | int | Buffer size |
an | int | Disable audio: 0/1 |
acodec | string | Audio codec |
ar | int | Audio sample rate (Hz) |
ac | int | Audio channels |
aq | int | Audio quality (0-100) |
ab | int | Audio bitrate (bps) |
abopt | int | Audio bitrate optimization: 0/1/2 |
Examples:
hls/m3u8:ss=15000,t=1800000,vcodec=h264,fps=25,s=1280x720,vb=2000000,acodec=aac,ab=128000,st=10000,initd=30000
hls/m3u8:vcodec=h264,s=1920x1080,vb=4000000,acodec=aac,ab=256000---
Parameter specifications in this document are derived from the Alibaba Cloud OSS Video Processing documentation and are reproduced here for quick reference.
#!/usr/bin/env python3
"""
check_permissions.py — Verify RAM permissions for OSS and IMM operations
Checks whether the configured AccessKey has sufficient permissions to:
1. Access the specified OSS bucket (GetBucketInfo)
2. Access the specified IMM project (GetProject)
3. Perform image processing operations (SignURL test)
Usage:
python check_permissions.py
python check_permissions.py --verbose
"""
import json
import os
import sys
# Ensure sibling modules (load_env, etc.) are importable
# regardless of the working directory when invoked via absolute path.
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if _SCRIPT_DIR not in sys.path:
sys.path.insert(0, _SCRIPT_DIR)
from permission_checks import check_imm_permission, check_oss_permission
def _mask(value: str) -> str:
"""Mask a credential value for display."""
if len(value) > 8:
return value[:4] + "****" + value[-4:]
return "****"
def _check_oss_permission(bucket: str, region: str, verbose: bool = False) -> dict:
"""
Check OSS bucket access permission by calling HeadBucket.
Returns a dict with status and details.
"""
result = {
"service": "OSS",
"bucket": bucket,
"region": region,
"status": "fail",
"details": "",
}
ok, error = check_oss_permission(bucket, region)
if ok:
result["status"] = "pass"
result["details"] = f"Bucket '{bucket}' is accessible."
elif error == "oss2 SDK not installed":
result["details"] = "oss2 SDK not installed. Run: pip install oss2==2.19.1"
elif error == "AccessKey not configured":
result["details"] = "AccessKey not configured."
elif "does not exist in region" in error:
result["status"] = "fail"
result["details"] = f"{error}. Check the bucket name and region."
elif error == "Access denied to bucket (lacks OSS read permission)":
result["status"] = "fail"
result["details"] = (
f"Access denied to bucket '{bucket}'. "
f"The AccessKey lacks OSS bucket read permission."
)
result["hint"] = (
"Grant the 'AliyunOSSReadOnlyAccess' policy to your RAM user, "
"or attach a custom policy with 'oss:GetBucketInfo' permission:\n"
" https://ram.console.aliyun.com/policies"
)
elif error == "Invalid AccessKey ID":
result["status"] = "fail"
result["details"] = "Invalid AccessKey ID."
result["hint"] = (
"Verify your AccessKey at: "
"https://ram.console.aliyun.com/users"
)
else:
result["status"] = "fail"
result["details"] = error
return result
def _check_imm_permission(project: str, region: str, verbose: bool = False) -> dict:
"""
Check IMM project access permission by calling GetProject.
Returns a dict with status and details.
"""
result = {
"service": "IMM",
"project": project,
"region": region,
"status": "fail",
"details": "",
}
if not project:
result["status"] = "skip"
result["details"] = "IMM project not configured. Skipped."
return result
ok, error = check_imm_permission(project, region)
if ok is None:
result["status"] = "skip"
result["details"] = "IMM project not configured. Skipped."
return result
if error == "IMM SDK not installed":
result["details"] = (
"IMM SDK not installed. Run: "
"pip install alibabacloud_imm20200930==4.8.2 alibabacloud_tea_openapi==0.4.4"
)
return result
if error == "AccessKey not configured":
result["details"] = "AccessKey not configured."
return result
if ok:
result["status"] = "pass"
result["details"] = f"Project '{project}' is accessible."
elif error == "Access denied to IMM project (lacks IMM permission)":
result["status"] = "fail"
result["details"] = (
f"Access denied to IMM project '{project}'. "
f"The AccessKey lacks IMM permissions."
)
result["hint"] = (
"Grant the 'AliyunIMMFullAccess' policy to your RAM user, "
"or attach a custom policy with required IMM permissions:\n"
" https://ram.console.aliyun.com/policies"
)
elif "not found" in error.lower():
result["status"] = "fail"
result["details"] = f"Project '{project}' not found."
result["hint"] = (
f"Create a project: "
f"python imm_admin.py create-project --project {project} "
f"--bucket $ALIBABA_CLOUD_OSS_BUCKET --region {region}"
)
else:
result["status"] = "fail"
result["details"] = error
return result
def check_permissions(verbose: bool = False) -> dict:
"""
Check RAM permissions for both OSS and IMM.
Returns a summary dict with all check results.
"""
bucket = os.environ.get("ALIBABA_CLOUD_OSS_BUCKET", "")
region = os.environ.get("ALIBABA_CLOUD_OSS_REGION", "")
project = os.environ.get("ALIBABA_CLOUD_IMM_PROJECT", "")
import credential
credential_client = credential.get_credential_client(required=False)
masked_access_key = "not set"
if credential_client is not None:
try:
cred = credential_client.get_credential()
except Exception:
cred = None
if cred and cred.get_access_key_id():
masked_access_key = _mask(cred.get_access_key_id())
results = {
"access_key_id": masked_access_key,
"checks": [],
"summary": "",
}
if not bucket or not region:
results["checks"].append({
"service": "OSS",
"status": "skip",
"details": (
"Bucket or region not configured. Set ALIBABA_CLOUD_OSS_BUCKET "
"and ALIBABA_CLOUD_OSS_REGION, or use --bucket / --region."
),
})
else:
results["checks"].append(
_check_oss_permission(bucket, region, verbose)
)
results["checks"].append(
_check_imm_permission(project, region, verbose)
)
# Summary
pass_count = sum(1 for c in results["checks"] if c["status"] == "pass")
fail_count = sum(1 for c in results["checks"] if c["status"] == "fail")
total = len(results["checks"])
results["summary"] = f"{pass_count}/{total} checks passed"
results["all_pass"] = fail_count == 0
return results
def _print_results(results: dict) -> None:
"""Print check results in a human-readable format."""
print(f"AccessKey: {results['access_key_id']}")
print()
for check in results["checks"]:
service = check["service"]
status = check["status"].upper()
details = check["details"]
if status == "PASS":
print(f" [{status}] {service}: {details}")
elif status == "SKIP":
print(f" [{status}] {service}: {details}")
else:
print(f" [{status}] {service}: {details}")
if "hint" in check:
print(f" Hint: {check['hint']}")
print()
print(f"Summary: {results['summary']}")
if not results.get("all_pass", False):
print(
"\nSome checks failed. Review the hints above and fix "
"permissions before proceeding.",
file=sys.stderr,
)
# ─── Standalone mode ────────────────────────────────────────────────────────
if __name__ == "__main__":
import argparse
from load_env import ensure_env_loaded
parser = argparse.ArgumentParser(
description="Check RAM permissions for OSS and IMM operations."
)
parser.add_argument(
"--bucket", default=os.environ.get("ALIBABA_CLOUD_OSS_BUCKET"),
help="OSS bucket name (falls back to env var)."
)
parser.add_argument(
"--region", default=os.environ.get("ALIBABA_CLOUD_OSS_REGION"),
help="OSS region (falls back to env var)."
)
parser.add_argument(
"--project", default=os.environ.get("ALIBABA_CLOUD_IMM_PROJECT"),
help="IMM project name (falls back to env var)."
)
parser.add_argument(
"--verbose", "-v", action="store_true",
help="Show detailed output."
)
parser.add_argument(
"--json", action="store_true",
help="Output results as JSON."
)
args = parser.parse_args()
# Set env vars from CLI args so check_permissions() picks them up
if args.bucket:
os.environ["ALIBABA_CLOUD_OSS_BUCKET"] = args.bucket
if args.region:
os.environ["ALIBABA_CLOUD_OSS_REGION"] = args.region
if args.project:
os.environ["ALIBABA_CLOUD_IMM_PROJECT"] = args.project
# Load env vars from config files first
ensure_env_loaded(verbose=False)
results = check_permissions(verbose=args.verbose)
if args.json:
print(json.dumps(results, ensure_ascii=False, indent=2))
else:
_print_results(results)
sys.exit(0 if results.get("all_pass", False) else 1)
"""Shared credential helper for Alibaba Cloud OSS Media Processing Skill.
Provides a unified interface for obtaining Alibaba Cloud credentials via the
alibabacloud-credentials SDK default chain (~/.aliyun/config.json set by
`aliyun configure`, ECS instance metadata).
The credential client is cached and passed through provider-aware SDK hooks so
callers do not need to manually extract or manage raw AK/SK values.
"""
import json
import os
import sys
from errors import die as _die
_CRED_CLIENT = None
USER_AGENT = "AlibabaCloud-Agent-Skills/alibabacloud-oss-media-process"
def _credential_has_material(cred_client) -> bool:
"""Return True when a credential client yields usable access key material."""
try:
cred = cred_client.get_credential()
except Exception:
return False
if cred is None:
return False
access_key_id = getattr(cred, "access_key_id", None)
if access_key_id is None and hasattr(cred, "get_access_key_id"):
access_key_id = cred.get_access_key_id()
access_key_secret = getattr(cred, "access_key_secret", None)
if access_key_secret is None and hasattr(cred, "get_access_key_secret"):
access_key_secret = cred.get_access_key_secret()
return bool(access_key_id and access_key_secret)
def _try_credentials_sdk():
"""Try to get credentials from alibabacloud-credentials SDK.
Returns the credential client object or None.
"""
try:
from alibabacloud_credentials.client import Client as CredentialClient
except ImportError:
return None
try:
cred = CredentialClient()
if _credential_has_material(cred):
return cred
except Exception:
pass
return None
def ensure_credentials(required: bool = True) -> bool:
"""Load credentials via alibabacloud-credentials SDK and cache the client.
The SDK follows its default credential chain:
~/.aliyun/config.json (set by `aliyun configure`) →
ECS instance metadata
When required=True, prints error and exits if credentials cannot be obtained.
When required=False, returns False silently on failure.
Returns True if credentials were loaded successfully.
"""
global _CRED_CLIENT
# Already cached
if _CRED_CLIENT is not None:
return True
cred = _try_credentials_sdk()
if cred:
_CRED_CLIENT = cred
return True
# No credentials found
if required:
_die(
"Alibaba Cloud credentials not found.",
"Configure credentials with Aliyun CLI:\n"
" aliyun configure",
)
return False
def get_credential_client(required: bool = True):
"""Return the cached credential client from the default chain.
Returns None only when required=False and credentials are unavailable.
"""
if ensure_credentials(required=required):
return _CRED_CLIENT
return None
def get_oss_credentials_provider(required: bool = True):
"""Create an oss2 credentials provider backed by the default chain."""
client = get_credential_client(required=required)
if client is None:
return None
try:
import oss2.credentials as oss2_credentials
except ImportError:
if required:
_die("oss2 SDK not installed.", "Install with: pip install oss2==2.19.1")
return None
class _AliyunCredentialProvider(oss2_credentials.CredentialsProvider):
def __init__(self, cred_client):
self._cred_client = cred_client
def get_credentials(self):
cred = self._cred_client.get_credential()
return oss2_credentials.Credentials(
access_key_id=cred.get_access_key_id(),
access_key_secret=cred.get_access_key_secret(),
security_token=cred.get_security_token() or "",
)
return _AliyunCredentialProvider(client)
def has_credential_material() -> bool:
"""Return True when the default credential chain yields usable material."""
client = get_credential_client(required=False)
if client is None:
return False
return _credential_has_material(client)
def get_bucket(default: str = "") -> str:
"""Return bucket name from env var, or the given default."""
return os.environ.get("ALIBABA_CLOUD_OSS_BUCKET", default)
def get_region(default: str = "") -> str:
"""Return region from env var, or the given default."""
return os.environ.get("ALIBABA_CLOUD_OSS_REGION", default)
def get_imm_project(default: str = "") -> str:
"""Return IMM project name from env var, or the given default."""
return os.environ.get("ALIBABA_CLOUD_IMM_PROJECT", default)
"""Shared structured error helpers."""
import json
import sys
def die(message: str, hint: str = "") -> None:
"""Print a structured error to stderr and exit."""
err = {"success": False, "error": message}
if hint:
err["hint"] = hint
print(json.dumps(err), file=sys.stderr)
sys.exit(1)
#!/usr/bin/env python3
"""Alibaba Cloud IMM Administration Script.
Manages IMM (Intelligent Media Management) resources required by
image-intelligent operations such as blind watermark extraction and AI detection.
Subcommands:
auto-setup Auto-check/create IMM project and bind bucket (one command)
create-project Create an IMM project and auto-bind bucket
list-projects List IMM projects
get-project Get IMM project details
check-imm Check IMM project and bucket binding status
delete-project Delete an IMM project
bind-bucket Bind an OSS bucket to an IMM project
unbind-bucket Unbind an OSS bucket from an IMM project
Environment Variables / Aliyun CLI:
Credentials auto-discovered via alibabacloud-credentials SDK
(Supports ~/.aliyun/config.json, environment variables, ECS metadata).
ALIBABA_CLOUD_OSS_BUCKET Default OSS bucket name
ALIBABA_CLOUD_OSS_REGION Default OSS region (e.g., cn-hangzhou)
ALIBABA_CLOUD_IMM_PROJECT IMM project name
"""
import argparse
import json
import os
import sys
from errors import die as _die
# Ensure sibling modules (load_env, imm_client, etc.) are importable
# regardless of the working directory when invoked via absolute path.
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if _SCRIPT_DIR not in sys.path:
sys.path.insert(0, _SCRIPT_DIR)
def _get_imm_client(region: str):
"""Create and return an IMM client. Delegates to shared imm_client module."""
from imm_client import get_imm_client
return get_imm_client(region)
# ---------------------------------------------------------------------------
# Subcommand handlers
# ---------------------------------------------------------------------------
def cmd_create_project(args) -> None:
"""Create an IMM project and auto-bind bucket."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
request = models.CreateProjectRequest(project_name=args.project)
response = client.create_project(request)
result = {
"success": True,
"action": "create_project",
"project": args.project,
}
if hasattr(response.body, "project") and response.body.project:
proj = response.body.project
result["details"] = {
"project_name": getattr(proj, "project_name", None),
"create_time": getattr(proj, "create_time", None),
"service_role": getattr(proj, "service_role", None),
}
# Auto-bind bucket to the newly created project
bind_request = models.AttachOSSBucketRequest(
project_name=args.project,
ossbucket=args.bucket,
)
client.attach_ossbucket(bind_request)
result["auto_bind"] = {
"bucket": args.bucket,
"status": "bound",
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_list_projects(args) -> None:
"""List all IMM projects."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
request = models.ListProjectsRequest()
if args.max_results:
request.max_results = args.max_results
response = client.list_projects(request)
projects = []
if hasattr(response.body, "projects") and response.body.projects:
for proj in response.body.projects:
projects.append({
"project_name": getattr(proj, "project_name", None),
"create_time": getattr(proj, "create_time", None),
})
result = {
"success": True,
"action": "list_projects",
"count": len(projects),
"projects": projects,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_get_project(args) -> None:
"""Get details of an IMM project."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
request = models.GetProjectRequest(project_name=args.project)
response = client.get_project(request)
details = {}
if hasattr(response.body, "project") and response.body.project:
proj = response.body.project
details = {
"project_name": getattr(proj, "project_name", None),
"create_time": getattr(proj, "create_time", None),
"service_role": getattr(proj, "service_role", None),
"dataset_count": getattr(proj, "dataset_count", None),
}
result = {
"success": True,
"action": "get_project",
"project": args.project,
"details": details,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_check_imm(args) -> None:
"""Check IMM setup status: project existence and bucket binding."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
project_name = args.project
bucket = args.bucket
result = {
"success": True,
"action": "check_imm",
"region": args.region,
"project": project_name,
"bucket": bucket,
"checks": {},
}
# Check 1: Project exists
try:
get_request = models.GetProjectRequest(project_name=project_name)
get_response = client.get_project(get_request)
proj = getattr(get_response.body, "project", None)
if proj:
result["checks"]["project_exists"] = {
"status": "pass",
"project_name": getattr(proj, "project_name", project_name),
"create_time": getattr(proj, "create_time", None),
"service_role": getattr(proj, "service_role", None),
}
else:
result["checks"]["project_exists"] = {
"status": "fail",
"message": f"Project '{project_name}' not found.",
}
result["success"] = False
print(json.dumps(result, ensure_ascii=False, indent=2))
return
except Exception as exc:
result["checks"]["project_exists"] = {
"status": "fail",
"message": str(exc),
}
result["success"] = False
print(json.dumps(result, ensure_ascii=False, indent=2))
return
# Check 2: Bucket binding (query by bucket only, same approach as auto-setup)
try:
attach_request = models.GetOSSBucketAttachmentRequest()
attach_request.ossbucket = bucket
resp = client.get_ossbucket_attachment(attach_request)
bound_project = None
if resp.body:
body_map = resp.body.to_map() if hasattr(resp.body, "to_map") else {}
bound_project = (
body_map.get("ProjectName")
or body_map.get("project_name")
)
if bound_project:
bound_match = bound_project == project_name
result["checks"]["bucket_bound"] = {
"status": "pass" if bound_match else "warn",
"bucket": bucket,
"bound_to": bound_project,
}
if not bound_match:
result["checks"]["bucket_bound"]["message"] = (
f"Bucket is bound to '{bound_project}', not '{project_name}'."
)
else:
result["checks"]["bucket_bound"] = {
"status": "fail",
"bucket": bucket,
"message": f"Bucket '{bucket}' is not bound to any IMM project.",
"hint": f"Run: python imm_admin.py bind-bucket --project {project_name} --bucket {bucket} --region {args.region}",
}
result["success"] = False
except Exception as exc:
err_msg = str(exc)
if any(kw in err_msg for kw in ("NotAttached", "NotFound", "404")):
result["checks"]["bucket_bound"] = {
"status": "fail",
"bucket": bucket,
"message": f"Bucket '{bucket}' is not bound to any IMM project.",
"hint": f"Run: python imm_admin.py bind-bucket --project {project_name} --bucket {bucket} --region {args.region}",
}
else:
result["checks"]["bucket_bound"] = {
"status": "fail",
"bucket": bucket,
"message": f"Binding check error: {err_msg}",
}
result["success"] = False
# Summary
pass_count = sum(1 for c in result["checks"].values() if c["status"] == "pass")
total_count = len(result["checks"])
result["summary"] = f"{pass_count}/{total_count} checks passed"
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_delete_project(args) -> None:
"""Delete an IMM project with protective pre-check."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
# Protective pre-check: verify project exists and check for bound buckets
try:
get_request = models.GetProjectRequest(project_name=args.project)
get_response = client.get_project(get_request)
proj = getattr(get_response.body, "project", None)
if proj:
dataset_count = getattr(proj, "dataset_count", 0)
if dataset_count and dataset_count > 0:
_die(
f"Project '{args.project}' still has {dataset_count} "
f"dataset(s) bound. Unbind all buckets before deleting.",
"Use 'unbind-bucket' to remove bucket bindings first."
)
except Exception as exc:
print(
json.dumps({"status": "warning", "message":
f"Pre-check skipped: {exc}"}),
file=sys.stderr,
)
request = models.DeleteProjectRequest(project_name=args.project)
client.delete_project(request)
result = {
"success": True,
"action": "delete_project",
"project": args.project,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_bind_bucket(args) -> None:
"""Bind an OSS bucket to an IMM project."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
request = models.AttachOSSBucketRequest(
project_name=args.project,
ossbucket=args.bucket,
)
client.attach_ossbucket(request)
result = {
"success": True,
"action": "bind_bucket",
"project": args.project,
"bucket": args.bucket,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_unbind_bucket(args) -> None:
"""Unbind an OSS bucket from an IMM project."""
from alibabacloud_imm20200930 import models
client = _get_imm_client(args.region)
request = models.DetachOSSBucketRequest(
project_name=args.project,
ossbucket=args.bucket,
)
client.detach_ossbucket(request)
result = {
"success": True,
"action": "unbind_bucket",
"project": args.project,
"bucket": args.bucket,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_auto_setup(args) -> None:
"""Auto-setup IMM: check bucket, check/create project, bind bucket.
Workflow:
1. Verify OSS bucket exists and is accessible.
2. Query whether the bucket is already bound to an IMM project.
3. If bound, reuse the existing project.
4. If not bound, create a new project (name auto-generated) and bind it.
5. Output the resolved project name for downstream use.
"""
import datetime
import random
from alibabacloud_imm20200930 import models
try:
import oss2
except ImportError:
_die("oss2 SDK not installed.", "pip install oss2==2.19.1")
client = _get_imm_client(args.region)
bucket_name = args.bucket
region = args.region
result = {
"success": False,
"action": "auto_setup",
"region": region,
"bucket": bucket_name,
"steps": [],
}
# Step 1: Verify OSS bucket exists
try:
from credential import USER_AGENT, get_oss_credentials_provider
credentials_provider = get_oss_credentials_provider()
endpoint = "https://oss-{}.aliyuncs.com".format(region)
auth = oss2.ProviderAuth(credentials_provider)
bucket_obj = oss2.Bucket(
auth, endpoint, bucket_name,
connect_timeout=(30, 60),
app_name=USER_AGENT,
)
info = bucket_obj.get_bucket_info()
result["steps"].append({
"step": "check_bucket",
"status": "pass",
"message": "Bucket '{}' exists (created: {})".format(
bucket_name, info.creation_date),
})
except Exception as e:
result["steps"].append({
"step": "check_bucket",
"status": "fail",
"message": "Bucket '{}' not accessible: {}".format(bucket_name, e),
})
print(json.dumps(result, ensure_ascii=False, indent=2))
return
# Step 2: Query existing IMM binding for this bucket
existing_project = None
try:
req = models.GetOSSBucketAttachmentRequest()
req.ossbucket = bucket_name
resp = client.get_ossbucket_attachment(req)
if resp.body:
body_map = resp.body.to_map() if hasattr(resp.body, "to_map") else {}
existing_project = (
body_map.get("ProjectName")
or body_map.get("project_name")
)
except Exception as e:
err_msg = e.message if hasattr(e, "message") else str(e)
# Not attached is expected — continue to create
if not any(kw in err_msg for kw in ("NotAttached", "NotFound", "404")):
# Unexpected error — still try to continue
result["steps"].append({
"step": "query_binding",
"status": "warn",
"message": "Query binding returned unexpected error: {}".format(err_msg),
})
if existing_project:
result["steps"].append({
"step": "query_binding",
"status": "pass",
"message": "Bucket already bound to project '{}'".format(existing_project),
})
result["success"] = True
result["project_name"] = existing_project
print(json.dumps(result, ensure_ascii=False, indent=2))
return
result["steps"].append({
"step": "query_binding",
"status": "info",
"message": "Bucket is not bound to any IMM project. Will create one.",
})
# Step 3: Create a new IMM project
date_str = datetime.datetime.now().strftime("%Y%m%d")
safe_name = "".join(c for c in bucket_name if c.isalnum() or c == "-")[:20]
if not safe_name:
safe_name = "".join(
random.choice("abcdefghijklmnopqrstuvwxyz0123456789")
for _ in range(6)
)
new_project_name = "imm-auto-{}-{}".format(safe_name, date_str)
created_project = None
try:
create_req = models.CreateProjectRequest(project_name=new_project_name)
resp = client.create_project(create_req)
if resp.body:
body_map = resp.body.to_map() if hasattr(resp.body, "to_map") else {}
created_project = (
body_map.get("ProjectName")
or body_map.get("projectName")
or body_map.get("Name")
or new_project_name
)
else:
created_project = new_project_name
result["steps"].append({
"step": "create_project",
"status": "pass",
"message": "Project '{}' created".format(created_project),
})
except Exception as e:
err_msg = e.message if hasattr(e, "message") else str(e)
if "AlreadyExists" in err_msg:
# Retry with a random suffix
new_project_name += "-{}".format(random.randint(1000, 9999))
try:
create_req = models.CreateProjectRequest(project_name=new_project_name)
resp = client.create_project(create_req)
created_project = new_project_name
result["steps"].append({
"step": "create_project",
"status": "pass",
"message": "Project '{}' created (retry with suffix)".format(created_project),
})
except Exception as e2:
err2 = e2.message if hasattr(e2, "message") else str(e2)
result["steps"].append({
"step": "create_project",
"status": "fail",
"message": "Failed to create project: {}".format(err2),
})
print(json.dumps(result, ensure_ascii=False, indent=2))
return
else:
result["steps"].append({
"step": "create_project",
"status": "fail",
"message": "Failed to create project: {}".format(err_msg),
})
print(json.dumps(result, ensure_ascii=False, indent=2))
return
# Step 4: Bind bucket to the new project
try:
attach_req = models.AttachOSSBucketRequest(
project_name=created_project,
ossbucket=bucket_name,
)
client.attach_ossbucket(attach_req)
result["steps"].append({
"step": "bind_bucket",
"status": "pass",
"message": "Bucket '{}' bound to project '{}'".format(
bucket_name, created_project),
})
except Exception as e:
err_msg = e.message if hasattr(e, "message") else str(e)
if "already" in err_msg.lower() or "Attached" in err_msg:
result["steps"].append({
"step": "bind_bucket",
"status": "pass",
"message": "Binding already exists",
})
else:
result["steps"].append({
"step": "bind_bucket",
"status": "fail",
"message": "Failed to bind bucket: {}".format(err_msg),
})
print(json.dumps(result, ensure_ascii=False, indent=2))
return
result["success"] = True
result["project_name"] = created_project
print(json.dumps(result, ensure_ascii=False, indent=2))
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Manage Alibaba Cloud IMM resources for image-intelligent operations.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"Examples:\n"
" # Auto-setup: check/create IMM project and bind bucket\n"
" python imm_admin.py auto-setup --bucket my-bucket --region cn-hangzhou\n\n"
" # Create an IMM project (auto-binds bucket)\n"
" python imm_admin.py create-project --project my-project "
"--bucket my-bucket --region cn-hangzhou\n\n"
" # List all projects\n"
" python imm_admin.py list-projects --region cn-hangzhou\n\n"
" # Check IMM project and bucket binding status\n"
" python imm_admin.py check-imm\n\n"
" # Bind a bucket to a project\n"
" python imm_admin.py bind-bucket --project my-project "
"--bucket my-bucket --region cn-hangzhou\n\n"
" # Unbind a bucket\n"
" python imm_admin.py unbind-bucket --project my-project "
"--bucket my-bucket --region cn-hangzhou\n\n"
" # Delete a project\n"
" python imm_admin.py delete-project --project my-project --region cn-hangzhou\n"
),
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
subparsers.required = True
_region_help = (
"Region (e.g., cn-hangzhou). "
"Falls back to ALIBABA_CLOUD_OSS_REGION environment variable."
)
_region_default = os.environ.get("ALIBABA_CLOUD_OSS_REGION")
_bucket_help = (
"OSS bucket name. "
"Falls back to ALIBABA_CLOUD_OSS_BUCKET environment variable."
)
_bucket_default = os.environ.get("ALIBABA_CLOUD_OSS_BUCKET")
# create-project
p_create = subparsers.add_parser(
"create-project",
help="Create an IMM project and auto-bind bucket.",
)
p_create.add_argument("--project", required=True, help="IMM project name.")
p_create.add_argument(
"--bucket", default=_bucket_default, help=_bucket_help,
)
p_create.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_create.set_defaults(func=cmd_create_project)
# list-projects
p_list = subparsers.add_parser(
"list-projects", help="List all IMM projects.",
)
p_list.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_list.add_argument(
"--max-results", type=int, default=100,
help="Maximum number of results to return (default: 100).",
)
p_list.set_defaults(func=cmd_list_projects)
# get-project
p_get = subparsers.add_parser(
"get-project", help="Get details of an IMM project.",
)
p_get.add_argument("--project", required=True, help="IMM project name.")
p_get.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_get.set_defaults(func=cmd_get_project)
# check-imm
p_check = subparsers.add_parser(
"check-imm", help="Check IMM project and bucket binding status.",
)
p_check.add_argument(
"--project", default=os.environ.get("ALIBABA_CLOUD_IMM_PROJECT"),
help="IMM project name. Falls back to ALIBABA_CLOUD_IMM_PROJECT env var.",
)
p_check.add_argument(
"--bucket", default=_bucket_default, help=_bucket_help,
)
p_check.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_check.set_defaults(func=cmd_check_imm)
# delete-project
p_delete = subparsers.add_parser(
"delete-project", help="Delete an IMM project.",
)
p_delete.add_argument("--project", required=True, help="IMM project name.")
p_delete.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_delete.set_defaults(func=cmd_delete_project)
# bind-bucket
p_bind = subparsers.add_parser(
"bind-bucket", help="Bind an OSS bucket to an IMM project.",
)
p_bind.add_argument("--project", required=True, help="IMM project name.")
p_bind.add_argument(
"--bucket", default=_bucket_default, help=_bucket_help,
)
p_bind.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_bind.set_defaults(func=cmd_bind_bucket)
# unbind-bucket
p_unbind = subparsers.add_parser(
"unbind-bucket", help="Unbind an OSS bucket from an IMM project.",
)
p_unbind.add_argument("--project", required=True, help="IMM project name.")
p_unbind.add_argument(
"--bucket", default=_bucket_default, help=_bucket_help,
)
p_unbind.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_unbind.set_defaults(func=cmd_unbind_bucket)
# auto-setup
p_auto = subparsers.add_parser(
"auto-setup",
help="Auto-setup IMM: check bucket, check/create project, bind bucket.",
)
p_auto.add_argument(
"--bucket", default=_bucket_default, help=_bucket_help,
)
p_auto.add_argument(
"--region", default=_region_default, help=_region_help,
)
p_auto.set_defaults(func=cmd_auto_setup)
return parser
def main() -> None:
# Load environment variables from config files (does not override existing)
from load_env import ensure_env_loaded
ensure_env_loaded(verbose=False)
parser = build_parser()
args = parser.parse_args()
# Validate region (required for all subcommands)
if not args.region:
_die(
"Region is required. Use --region or set "
"ALIBABA_CLOUD_OSS_REGION environment variable.",
"Example: --region cn-hangzhou or "
"export ALIBABA_CLOUD_OSS_REGION='cn-hangzhou'",
)
# Validate bucket for subcommands that require it
if args.command in ("create-project", "bind-bucket", "unbind-bucket", "check-imm", "auto-setup"):
if not getattr(args, "bucket", None):
_die(
"Bucket name is required. Use --bucket or set "
"ALIBABA_CLOUD_OSS_BUCKET environment variable.",
"Example: --bucket my-bucket or "
"export ALIBABA_CLOUD_OSS_BUCKET='my-bucket'",
)
# Validate project for check-imm
if args.command == "check-imm":
if not getattr(args, "project", None):
_die(
"IMM project name is required. Use --project or set "
"ALIBABA_CLOUD_IMM_PROJECT environment variable.",
"Example: --project my-imm-project or "
"export ALIBABA_CLOUD_IMM_PROJECT='my-imm-project'",
)
args.func(args)
if __name__ == "__main__":
main()
Related skills
FAQ
What media does it process?
Images (14+ operations), audio, and video files stored in Alibaba Cloud OSS, plus IMM intelligent features.
How are results returned?
As a signed URL, downloaded locally, or saved as a new OSS object.