fix(promotion-import): 实例(一)(二)多备选源路径;目录内唯一 mov/jpg 自动匹配
Made-with: Cursor
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
将 `web/promotion/视频发布/` 下映射表中的文件复制到 **`{upload}/sites/{site_id}/promotion/social/`**,并在 **`site_assets`** 集合插入记录(与后台「保留原文件名」上传到 `promotion/social` 一致)。
|
||||
|
||||
对「操作与计算(一)(二)」等条目会**按顺序尝试多个源文件名**;若仍找不到且子目录内**恰好只有一个** `.mov` 或 `.jpg`,会自动选用(解决「有些视频有、有些 404」多为源文件名与映射不一致)。
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user