jackwener/OpenCLI · error · ArgumentError

不支持的视频格式: ${ext}(支持 mp4/mov/avi/webm)

Error message

不支持的视频格式: ${ext}(支持 mp4/mov/avi/webm)

What it means

The douyin draft command validates the uploaded video's file extension against an allowlist (.mp4, .mov, .avi, .webm) before opening the upload page. If path.extname returns anything else, an ArgumentError is thrown naming the offending extension. This is a client-side guard so the browser automation never submits a file Douyin's uploader will reject.

Source

Thrown at clis/douyin/draft.js:313

    domain: 'creator.douyin.com',
    strategy: Strategy.COOKIE,
    navigateBefore: false,
    args: [
        { name: 'video', required: true, positional: true, help: '视频文件路径' },
        { name: 'title', required: true, help: '视频标题(≤30字)' },
        { name: 'caption', default: '', help: '正文内容(≤1000字,支持 #话题)' },
        { name: 'cover', default: '', help: '封面图片路径' },
        { name: 'visibility', default: 'public', choices: ['public', 'friends', 'private'] },
    ],
    columns: ['status', 'draft_id'],
    func: async (page, kwargs) => {
        const videoPath = path.resolve(kwargs.video);
        if (!fs.existsSync(videoPath)) {
            throw new ArgumentError(`视频文件不存在: ${videoPath}`);
        }
        const ext = path.extname(videoPath).toLowerCase();
        if (!['.mp4', '.mov', '.avi', '.webm'].includes(ext)) {
            throw new ArgumentError(`不支持的视频格式: ${ext}(支持 mp4/mov/avi/webm)`);
        }
        const title = kwargs.title;
        if (title.length > 30) {
            throw new ArgumentError('标题不能超过 30 字');
        }
        const caption = kwargs.caption || '';
        if (caption.length > 1000) {
            throw new ArgumentError('正文不能超过 1000 字');
        }
        const coverPath = kwargs.cover;
        if (coverPath) {
            if (!fs.existsSync(path.resolve(coverPath))) {
                throw new ArgumentError(`封面文件不存在: ${path.resolve(coverPath)}`);
            }
        }
        if (!page.setFileInput) {
            throw new CommandExecutionError('当前浏览器适配器不支持文件注入', '请使用 Browser Bridge 或支持 setFileInput 的浏览器模式');
        }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Convert the video to MP4 (e.g. `ffmpeg -i input.mkv -c copy output.mp4`) and pass the new file
  2. Rename the file with a supported extension ONLY if the actual container matches (renaming .mkv to .mp4 will fail at upload)
  3. Check the extension: `node -e "console.log(require('path').extname(process.argv[1]))" yourfile` to confirm what the CLI sees

Example fix

// before
await cli.draft({ video: 'clip.mkv' });
// after
// convert first: ffmpeg -i clip.mkv clip.mp4
await cli.draft({ video: 'clip.mp4' });
Defensive patterns

Strategy: validation

Validate before calling

const ok = ['.mp4', '.mov', '.avi', '.webm'].includes(path.extname(video).toLowerCase());
if (!ok) throw new Error(`Convert ${video} to mp4/mov/avi/webm first`);

Type guard

const isSupportedVideo = (p) => ['.mp4', '.mov', '.avi', '.webm'].includes(require('path').extname(String(p)).toLowerCase());

Try / catch

try {
  await cli.draft({ video, title });
} catch (e) {
  if (e instanceof ArgumentError && e.message.includes('不支持的视频格式')) {
    console.error(`Re-encode with: ffmpeg -i ${video} ${video.replace(/\.\w+$/, '')}.mp4`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `opencli douyin draft --video <file>` where the file exists but its extension (case-insensitive) is not .mp4/.mov/.avi/.webm — e.g. .mkv, .flv, .wmv, .m4v, .ts, or a file with no extension.

Common situations: Screen recordings saved as .mkv, GoPro/OBS footage in exotic codecs, renamed files without extensions, or passing a directory path that happens to resolve.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/5b223eb023f509ef. Report an issue: GitHub.