Files
video-create/.claude/skills/video-from-script/scripts/oss-upload.js
lc 1e110219ff feat: MiniMax TTS集成、口播批量剪辑流水线、执黑先行二号账号
- 新增 minimax-tts.js 和 minimax-voice-clone.js 脚本
- 新增口播批量剪辑流水线 (口播_assemble.js, 口播_pipeline.js)
- 更新 video-from-script 各阶段脚本 (kling, images, tts, videos)
- 新增执黑先行二号-风格延伸账号
- 更新执黑先行 account.json 配置
- 替换 ugc_product_seeding 参考图
- 更新 CLAUDE.md 和依赖配置

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:05:56 +08:00

177 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* OSS 文件上传工具
*
* 上传图片/视频到阿里云 OSS返回签名 URL。
* 支持单文件和批量上传。
*
* 用法:
* node oss-upload.js ./image.png
* node oss-upload.js ./video.mp4 --dir videos/
* node oss-upload.js batch ./manifest.json
*/
const OSS = require('ali-oss')
const path = require('path')
const fs = require('fs')
// ============================================================================
// 配置
// ============================================================================
function getConfig() {
const configPath = path.join(__dirname, '..', '..', 'config.json')
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
if (!config.ossRegion || !config.ossAccessKeyId || !config.ossAccessKeySecret || !config.ossBucket) {
console.error('config.json 需要填写 ossRegion, ossAccessKeyId, ossAccessKeySecret, ossBucket')
process.exit(1)
}
return config
}
function createClient(config) {
return new OSS({
region: config.ossRegion,
accessKeyId: config.ossAccessKeyId,
accessKeySecret: config.ossAccessKeySecret,
bucket: config.ossBucket,
secure: true,
})
}
// ============================================================================
// 上传
// ============================================================================
async function uploadFile(filePath, options = {}) {
const config = getConfig()
const client = createClient(config)
if (!fs.existsSync(filePath)) {
throw new Error(`文件不存在: ${filePath}`)
}
const folder = options.folder || config.ossFolder || 'tmp/'
const basename = options.name || path.basename(filePath)
const ossPath = `${folder}${basename}`
const stat = fs.statSync(filePath)
const opts = stat.size > 50 * 1024 * 1024
? { timeout: 600000 } // 10min for large files
: {}
const stream = fs.createReadStream(filePath)
await client.putStream(ossPath, stream, { ...opts, contentLength: stat.size })
const expires = config.ossExpires || 31536000
const url = client.signatureUrl(ossPath, { expires })
return { url, ossPath, size: stat.size }
}
async function uploadBuffer(buffer, options = {}) {
const config = getConfig()
const client = createClient(config)
const folder = options.folder || config.ossFolder || 'tmp/'
const basename = options.name || `${Date.now()}${options.ext || '.png'}`
const ossPath = `${folder}${basename}`
await client.put(ossPath, buffer)
const expires = config.ossExpires || 31536000
const url = client.signatureUrl(ossPath, { expires })
return { url, ossPath }
}
// ============================================================================
// 批量上传(读 manifest.json 中的 file 列表)
// ============================================================================
async function batchUpload(manifestPath, baseDir) {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
const dir = baseDir || path.dirname(manifestPath)
const results = {}
for (const item of manifest.items) {
const filePath = path.join(dir, item.file)
if (!fs.existsSync(filePath)) continue
const name = path.basename(item.file)
try {
const { url } = await uploadFile(filePath, { name })
results[item.file] = url
console.log(` OK: ${name}`)
} catch (err) {
console.error(` FAIL: ${name} - ${err.message}`)
}
}
return results
}
// ============================================================================
// CLI
// ============================================================================
function parseArgs(argv) {
const args = { _: [] }
for (let i = 0; i < argv.length; i++) {
if (argv[i].startsWith('--')) {
const key = argv[i].slice(2)
const val = argv[i + 1]
if (val && !val.startsWith('--')) { args[key] = val; i++ }
else args[key] = true
} else {
args._.push(argv[i])
}
}
return args
}
async function main() {
const args = parseArgs(process.argv.slice(2))
const cmd = args._[0]
if (!cmd) {
console.log('用法: node oss-upload.js <file> [--dir folder] [--name filename]')
console.log(' node oss-upload.js batch <manifest.json> [--dir <baseDir>]')
process.exit(0)
}
if (cmd === 'batch') {
const manifest = args._[1]
if (!manifest) { console.error('指定 manifest.json'); process.exit(1) }
console.log(`批量上传: ${manifest}`)
const results = await batchUpload(manifest, args.dir)
console.log(`\n完成: ${Object.keys(results).length} 个文件`)
// 写回 urls
const urlsPath = path.join(args.dir || path.dirname(manifest), 'urls.json')
const existing = fs.existsSync(urlsPath) ? JSON.parse(fs.readFileSync(urlsPath, 'utf-8')) : {}
Object.assign(existing, results)
fs.writeFileSync(urlsPath, JSON.stringify(existing, null, 2))
console.log(`URLs 已写入: ${urlsPath}`)
} else {
const filePath = path.resolve(cmd)
console.log(`上传: ${filePath}`)
const { url, ossPath, size } = await uploadFile(filePath, {
folder: args.dir,
name: args.name,
})
console.log(`\nOSS 路径: ${ossPath}`)
console.log(`签名 URL: ${url}`)
console.log(`文件大小: ${(size / 1024).toFixed(1)} KB`)
}
}
module.exports = { uploadFile, uploadBuffer, batchUpload }
if (require.main === module) {
main().catch(err => {
console.error(`错误: ${err.message}`)
process.exit(1)
})
}