diff --git a/scripts/sync-video-assets-to-social.ps1 b/scripts/sync-video-assets-to-social.ps1 index 16310bc..111e715 100644 --- a/scripts/sync-video-assets-to-social.ps1 +++ b/scripts/sync-video-assets-to-social.ps1 @@ -6,26 +6,40 @@ $Src = Join-Path $Root "web\promotion\视频发布" $Dst = Join-Path $Root "web\promotion\social" New-Item -ItemType Directory -Force -Path $Dst | Out-Null -function Copy-IfExists($fromRel, $toName) { - $from = Join-Path $Src $fromRel +function Copy-First($toName, [string[]]$fromRels) { $to = Join-Path $Dst $toName - if (Test-Path -LiteralPath $from) { - Copy-Item -LiteralPath $from -Destination $to -Force - Write-Host "OK $toName" - } else { - Write-Warning "SKIP (缺失): $from" + foreach ($rel in $fromRels) { + $from = Join-Path $Src $rel + if (Test-Path -LiteralPath $from) { + Copy-Item -LiteralPath $from -Destination $to -Force + Write-Host "OK $toName <= $rel" + return + } } + Write-Warning "SKIP (均未找到): -> $toName" } -Copy-IfExists "宇恒一号操作计算软件实例(一)\宣传片-封面.jpg" "video-calc-demo-1-cover.jpg" -Copy-IfExists "宇恒一号操作计算软件实例(一)\宣传片.mov" "video-calc-demo-1.mov" -Copy-IfExists "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二)-封面.jpg" "video-calc-demo-2-cover.jpg" -Copy-IfExists "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二).mov" "video-calc-demo-2.mov" -Copy-IfExists "宇恒一号AIWord简介\宇恒一号AIWord简介-封面.jpg" "video-aiword-cover.jpg" -Copy-IfExists "宇恒一号AIWord简介\宇恒一号AIWord简介.mov" "video-aiword.mov" -Copy-IfExists "宇恒一号语音办公实例\宇恒一号语音办公实例-封面.jpg" "video-voice-office-cover.jpg" -Copy-IfExists "宇恒一号语音办公实例\宇恒一号语音办公实例.mov" "video-voice-office.mov" -Copy-IfExists "宇恒一号,AI 全自动办发票\宇恒一号,AI 全自动办发票-封面.jpg" "video-invoice-ai-cover.jpg" -Copy-IfExists "宇恒一号,AI 全自动办发票\宇恒一号,AI 全自动办发票.mov" "video-invoice-ai.mov" +Copy-First "video-calc-demo-1-cover.jpg" @( + "宇恒一号操作计算软件实例(一)\宣传片-封面.jpg", + "宇恒一号操作计算软件实例(一)\宇恒一号操作计算软件实例(一)-封面.jpg" +) +Copy-First "video-calc-demo-1.mov" @( + "宇恒一号操作计算软件实例(一)\宣传片.mov", + "宇恒一号操作计算软件实例(一)\宇恒一号操作计算软件实例(一).mov" +) +Copy-First "video-calc-demo-2-cover.jpg" @( + "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二)-封面.jpg", + "宇恒一号操作计算软件实例(二)\宣传片-封面.jpg" +) +Copy-First "video-calc-demo-2.mov" @( + "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二).mov", + "宇恒一号操作计算软件实例(二)\宣传片.mov" +) +Copy-First "video-aiword-cover.jpg" @("宇恒一号AIWord简介\宇恒一号AIWord简介-封面.jpg") +Copy-First "video-aiword.mov" @("宇恒一号AIWord简介\宇恒一号AIWord简介.mov") +Copy-First "video-voice-office-cover.jpg" @("宇恒一号语音办公实例\宇恒一号语音办公实例-封面.jpg") +Copy-First "video-voice-office.mov" @("宇恒一号语音办公实例\宇恒一号语音办公实例.mov") +Copy-First "video-invoice-ai-cover.jpg" @("宇恒一号,AI 全自动办发票\宇恒一号,AI 全自动办发票-封面.jpg") +Copy-First "video-invoice-ai.mov" @("宇恒一号,AI 全自动办发票\宇恒一号,AI 全自动办发票.mov") Write-Host "完成。Linux 服务器上建议在 social 目录执行: chmod -R a+rX ." diff --git a/scripts/sync-video-assets-to-social.sh b/scripts/sync-video-assets-to-social.sh index e867368..2411271 100644 --- a/scripts/sync-video-assets-to-social.sh +++ b/scripts/sync-video-assets-to-social.sh @@ -8,35 +8,48 @@ SRC="$ROOT/web/promotion/视频发布" DST="$ROOT/web/promotion/social" mkdir -p "$DST" -copy_if () { - local from="$1" to="$2" - if [[ -f "$from" ]]; then - cp -f "$from" "$to" - echo "OK $to" - else - echo "SKIP (缺失): $from" >&2 - fi +# 按顺序使用第一个存在的源文件 +copy_first() { + local dest="$1" + shift + for from in "$@"; do + if [[ -f "$from" ]]; then + cp -f "$from" "$dest" + echo "OK $(basename "$dest") <= $from" + return 0 + fi + done + echo "SKIP (均未找到): -> $dest" >&2 + return 1 } # 操作与计算软件实例(一) -copy_if "$SRC/宇恒一号操作计算软件实例(一)/宣传片-封面.jpg" "$DST/video-calc-demo-1-cover.jpg" -copy_if "$SRC/宇恒一号操作计算软件实例(一)/宣传片.mov" "$DST/video-calc-demo-1.mov" +copy_first "$DST/video-calc-demo-1-cover.jpg" \ + "$SRC/宇恒一号操作计算软件实例(一)/宣传片-封面.jpg" \ + "$SRC/宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg" || true +copy_first "$DST/video-calc-demo-1.mov" \ + "$SRC/宇恒一号操作计算软件实例(一)/宣传片.mov" \ + "$SRC/宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov" || true # 操作与计算软件实例(二) -copy_if "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg" "$DST/video-calc-demo-2-cover.jpg" -copy_if "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov" "$DST/video-calc-demo-2.mov" +copy_first "$DST/video-calc-demo-2-cover.jpg" \ + "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg" \ + "$SRC/宇恒一号操作计算软件实例(二)/宣传片-封面.jpg" || true +copy_first "$DST/video-calc-demo-2.mov" \ + "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov" \ + "$SRC/宇恒一号操作计算软件实例(二)/宣传片.mov" || true # AI Word -copy_if "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg" "$DST/video-aiword-cover.jpg" -copy_if "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介.mov" "$DST/video-aiword.mov" +copy_first "$DST/video-aiword-cover.jpg" "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg" || true +copy_first "$DST/video-aiword.mov" "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介.mov" || true # 语音办公 -copy_if "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg" "$DST/video-voice-office-cover.jpg" -copy_if "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例.mov" "$DST/video-voice-office.mov" +copy_first "$DST/video-voice-office-cover.jpg" "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg" || true +copy_first "$DST/video-voice-office.mov" "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例.mov" || true # 办发票(目录名含全角逗号) -copy_if "$SRC/宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票-封面.jpg" "$DST/video-invoice-ai-cover.jpg" -copy_if "$SRC/宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票.mov" "$DST/video-invoice-ai.mov" +copy_first "$DST/video-invoice-ai-cover.jpg" "$SRC/宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票-封面.jpg" || true +copy_first "$DST/video-invoice-ai.mov" "$SRC/宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票.mov" || true if command -v chmod >/dev/null 2>&1; then chmod -R a+rX "$DST" 2>/dev/null || true diff --git a/server/cmd/promotion-import/README.md b/server/cmd/promotion-import/README.md index 505872d..00ac8d2 100644 --- a/server/cmd/promotion-import/README.md +++ b/server/cmd/promotion-import/README.md @@ -2,6 +2,8 @@ 将 `web/promotion/视频发布/` 下映射表中的文件复制到 **`{upload}/sites/{site_id}/promotion/social/`**,并在 **`site_assets`** 集合插入记录(与后台「保留原文件名」上传到 `promotion/social` 一致)。 +对「操作与计算(一)(二)」等条目会**按顺序尝试多个源文件名**;若仍找不到且子目录内**恰好只有一个** `.mov` 或 `.jpg`,会自动选用(解决「有些视频有、有些 404」多为源文件名与映射不一致)。 + ## 参数 | 参数 | 说明 | diff --git a/server/cmd/promotion-import/main.go b/server/cmd/promotion-import/main.go index 6a78307..7c2b348 100644 --- a/server/cmd/promotion-import/main.go +++ b/server/cmd/promotion-import/main.go @@ -57,21 +57,77 @@ func mimeForExt(ext string) string { } } -// 与 scripts/sync-video-assets-to-social.sh 一致:源相对「视频发布」目录,目标为 promotion/social 下英文名 -var mappings = []struct { - SrcRel string // 相对 视频发布/ - Dst string // 仅文件名,落在 promotion/social/ -}{ - {"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg", "video-calc-demo-1-cover.jpg"}, - {"宇恒一号操作计算软件实例(一)/宣传片.mov", "video-calc-demo-1.mov"}, - {"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg", "video-calc-demo-2-cover.jpg"}, - {"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov", "video-calc-demo-2.mov"}, - {"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg", "video-aiword-cover.jpg"}, - {"宇恒一号AIWord简介/宇恒一号AIWord简介.mov", "video-aiword.mov"}, - {"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg", "video-voice-office-cover.jpg"}, - {"宇恒一号语音办公实例/宇恒一号语音办公实例.mov", "video-voice-office.mov"}, - {"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票-封面.jpg", "video-invoice-ai-cover.jpg"}, - {"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票.mov", "video-invoice-ai.mov"}, +// importRule:按顺序尝试 SrcRels;若均不存在且 FallbackScanDir 非空,则在该子目录下「仅一个 .mov/.jpg 时」自动选用(兼容实际文件夹命名与文件名不一致) +type importRule struct { + SrcRels []string + Dst string + FallbackScanDir string // 相对 视频发布/,仅当目标为视频时用 .mov;封面用 .jpg +} + +// 与 sync-video-assets-to-social.sh 对齐,并增加备选路径(线上常见「实例(一)」内不叫宣传片.mov 的情况) +var mappings = []importRule{ + {[]string{ + "宇恒一号操作计算软件实例(一)/宣传片-封面.jpg", + "宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg", + }, "video-calc-demo-1-cover.jpg", "宇恒一号操作计算软件实例(一)"}, + {[]string{ + "宇恒一号操作计算软件实例(一)/宣传片.mov", + "宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov", + }, "video-calc-demo-1.mov", "宇恒一号操作计算软件实例(一)"}, + {[]string{ + "宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg", + "宇恒一号操作计算软件实例(二)/宣传片-封面.jpg", + }, "video-calc-demo-2-cover.jpg", "宇恒一号操作计算软件实例(二)"}, + {[]string{ + "宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov", + "宇恒一号操作计算软件实例(二)/宣传片.mov", + }, "video-calc-demo-2.mov", "宇恒一号操作计算软件实例(二)"}, + {[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg"}, "video-aiword-cover.jpg", "宇恒一号AIWord简介"}, + {[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介.mov"}, "video-aiword.mov", "宇恒一号AIWord简介"}, + {[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg"}, "video-voice-office-cover.jpg", "宇恒一号语音办公实例"}, + {[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例.mov"}, "video-voice-office.mov", "宇恒一号语音办公实例"}, + {[]string{"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票-封面.jpg"}, "video-invoice-ai-cover.jpg", "宇恒一号,AI 全自动办发票"}, + {[]string{"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票.mov"}, "video-invoice-ai.mov", "宇恒一号,AI 全自动办发票"}, +} + +func resolveSourceFile(videoPublish string, rule importRule) (absPath, relChosen string, ok bool) { + for _, rel := range rule.SrcRels { + p := filepath.Join(videoPublish, filepath.FromSlash(rel)) + if st, err := os.Stat(p); err == nil && !st.IsDir() { + return p, rel, true + } + } + if rule.FallbackScanDir == "" { + return "", "", false + } + ext := strings.ToLower(filepath.Ext(rule.Dst)) + if ext != ".mov" && ext != ".jpg" && ext != ".jpeg" { + return "", "", false + } + dir := filepath.Join(videoPublish, filepath.FromSlash(rule.FallbackScanDir)) + entries, err := os.ReadDir(dir) + if err != nil { + return "", "", false + } + var matches []string + for _, e := range entries { + if e.IsDir() { + continue + } + ne := strings.ToLower(filepath.Ext(e.Name())) + if ext == ".mov" && ne == ".mov" { + matches = append(matches, e.Name()) + } + if (ext == ".jpg" || ext == ".jpeg") && (ne == ".jpg" || ne == ".jpeg") { + matches = append(matches, e.Name()) + } + } + if len(matches) != 1 { + return "", "", false + } + rel := filepath.ToSlash(filepath.Join(rule.FallbackScanDir, matches[0])) + p := filepath.Join(videoPublish, filepath.FromSlash(rel)) + return p, rel, true } func main() { @@ -144,9 +200,9 @@ func main() { ok, skip, fail := 0, 0, 0 for _, m := range mappings { - from := filepath.Join(videoPublish, filepath.FromSlash(m.SrcRel)) - if _, err := os.Stat(from); err != nil { - log.Printf("SKIP 源文件不存在: %s", from) + from, srcRelUsed, found := resolveSourceFile(videoPublish, m) + if !found { + log.Printf("SKIP 源文件不存在(已试备选路径): dst=%s dir=%s", m.Dst, m.FallbackScanDir) skip++ continue } @@ -168,7 +224,7 @@ func main() { } if err := copyFile(from, destPath); err != nil { - log.Printf("FAIL 复制 %s: %v", m.SrcRel, err) + log.Printf("FAIL 复制 %s: %v", srcRelUsed, err) fail++ continue } @@ -193,7 +249,7 @@ func main() { "downloadable": false, "created_at": time.Now().Format(time.RFC3339), "import_source": "video_publish_legacy", - "source_relpath": m.SrcRel, + "source_relpath": srcRelUsed, "promotion_alias": filepath.ToSlash(filepath.Join("promotion", "social", m.Dst)), } if _, err := coll.InsertOne(ctx, doc); err != nil { @@ -201,7 +257,7 @@ func main() { fail++ continue } - log.Printf("OK %s -> %s", m.SrcRel, relPath) + log.Printf("OK %s -> %s", srcRelUsed, relPath) ok++ }