fix(promotion-import): 双实例目录配对 demo-1/2;配对含仅封面目录
Made-with: Cursor
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
将 `web/promotion/视频发布/` 下映射表中的文件复制到 **`{upload}/sites/{site_id}/promotion/social/`**,并在 **`site_assets`** 集合插入记录(与后台「保留原文件名」上传到 `promotion/social` 一致)。
|
将 `web/promotion/视频发布/` 下映射表中的文件复制到 **`{upload}/sites/{site_id}/promotion/social/`**,并在 **`site_assets`** 集合插入记录(与后台「保留原文件名」上传到 `promotion/social` 一致)。
|
||||||
|
|
||||||
对「操作与计算(一)(二)」等条目会**按顺序尝试多个源文件名**;若仍找不到且子目录内**恰好只有一个** `.mov` 或 `.jpg`,会自动选用(解决「有些视频有、有些 404」多为源文件名与映射不一致)。
|
对「操作与计算(一)(二)」会尝试多组路径名、半角括号、子目录内**最大** `.mov`;若仍无法按「一/二」识别文件夹,会在 `视频发布` 下找出**恰好两个**含「实例」的兄弟目录(排除 AIWord/语音/发票),排序后**第一个 → demo-1、第二个 → demo-2**(文件夹名不含「一」也能配对)。
|
||||||
|
|
||||||
## 参数
|
## 参数
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -142,6 +143,107 @@ func discoverEpisodeDir(videoPublish, episode string) (dirName string, ok bool)
|
|||||||
return hits[0], true
|
return hits[0], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dirHasMov(dir string) bool {
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.EqualFold(filepath.Ext(e.Name()), ".mov") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirHasJpeg(dir string) bool {
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ext := strings.ToLower(filepath.Ext(e.Name()))
|
||||||
|
if ext == ".jpg" || ext == ".jpeg" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列出「操作与计算」类子目录:含「实例」、内有 .mov 或 .jpg(避免仅封面无 mov 的(一)被漏掉),排除 AIWord/语音/发票等
|
||||||
|
func listCalcInstanceDirsForPairing(videoPublish string) []string {
|
||||||
|
entries, err := os.ReadDir(videoPublish)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
skipSubstr := []string{"AIWord", "语音", "发票", "全自动"}
|
||||||
|
var out []string
|
||||||
|
outer:
|
||||||
|
for _, e := range entries {
|
||||||
|
if !e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n := e.Name()
|
||||||
|
if !strings.Contains(n, "实例") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, s := range skipSubstr {
|
||||||
|
if strings.Contains(n, s) {
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub := filepath.Join(videoPublish, n)
|
||||||
|
if !dirHasMov(sub) && !dirHasJpeg(sub) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, n)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将目录排序为 demo-1 在前、demo-2 在后(优先认全角/半角「一」「二」标记)
|
||||||
|
func orderCalcDirsForDemo12(dirs []string) []string {
|
||||||
|
if len(dirs) <= 1 {
|
||||||
|
return dirs
|
||||||
|
}
|
||||||
|
type scored struct {
|
||||||
|
name string
|
||||||
|
prio int
|
||||||
|
}
|
||||||
|
var xs []scored
|
||||||
|
for _, d := range dirs {
|
||||||
|
p := 100
|
||||||
|
switch {
|
||||||
|
case strings.Contains(d, "(一)") || strings.Contains(d, "(一)"):
|
||||||
|
p = 1
|
||||||
|
case strings.Contains(d, "(二)") || strings.Contains(d, "(二)"):
|
||||||
|
p = 2
|
||||||
|
case strings.Contains(d, "一") && !strings.Contains(d, "二"):
|
||||||
|
p = 5
|
||||||
|
case strings.Contains(d, "二"):
|
||||||
|
p = 6
|
||||||
|
}
|
||||||
|
xs = append(xs, scored{d, p})
|
||||||
|
}
|
||||||
|
sort.Slice(xs, func(i, j int) bool {
|
||||||
|
if xs[i].prio != xs[j].prio {
|
||||||
|
return xs[i].prio < xs[j].prio
|
||||||
|
}
|
||||||
|
return xs[i].name < xs[j].name
|
||||||
|
})
|
||||||
|
out := make([]string, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
out[i] = x.name
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func pickMediaInDir(videoPublish, dirName string, dstFile string) (absPath, relChosen string, ok bool) {
|
func pickMediaInDir(videoPublish, dirName string, dstFile string) (absPath, relChosen string, ok bool) {
|
||||||
ext := strings.ToLower(filepath.Ext(dstFile))
|
ext := strings.ToLower(filepath.Ext(dstFile))
|
||||||
if ext != ".mov" && ext != ".jpg" && ext != ".jpeg" {
|
if ext != ".mov" && ext != ".jpg" && ext != ".jpeg" {
|
||||||
@@ -221,7 +323,7 @@ func pickMediaInDir(videoPublish, dirName string, dstFile string) (absPath, relC
|
|||||||
return filepath.Join(videoPublish, filepath.FromSlash(rel)), rel, true
|
return filepath.Join(videoPublish, filepath.FromSlash(rel)), rel, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSourceFile(videoPublish string, rule importRule) (absPath, relChosen string, ok bool) {
|
func resolveSourceFile(videoPublish string, rule importRule, calcPair []string) (absPath, relChosen string, ok bool) {
|
||||||
for _, rel := range rule.SrcRels {
|
for _, rel := range rule.SrcRels {
|
||||||
p := filepath.Join(videoPublish, filepath.FromSlash(rel))
|
p := filepath.Join(videoPublish, filepath.FromSlash(rel))
|
||||||
if st, err := os.Stat(p); err == nil && !st.IsDir() {
|
if st, err := os.Stat(p); err == nil && !st.IsDir() {
|
||||||
@@ -252,6 +354,22 @@ func resolveSourceFile(videoPublish string, rule importRule) (absPath, relChosen
|
|||||||
return abs, rel, true
|
return abs, rel, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 恰好两个「实例」类目录且无法按名称命中时:排序后第 1 个 -> demo-1,第 2 个 -> demo-2
|
||||||
|
if rule.EpisodeScan == "一" && len(calcPair) >= 2 {
|
||||||
|
if abs, rel, ok := pickMediaInDir(videoPublish, calcPair[0], rule.Dst); ok {
|
||||||
|
return abs, rel, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rule.EpisodeScan == "一" && len(calcPair) == 1 {
|
||||||
|
if abs, rel, ok := pickMediaInDir(videoPublish, calcPair[0], rule.Dst); ok {
|
||||||
|
return abs, rel, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rule.EpisodeScan == "二" && len(calcPair) >= 2 {
|
||||||
|
if abs, rel, ok := pickMediaInDir(videoPublish, calcPair[1], rule.Dst); ok {
|
||||||
|
return abs, rel, true
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", "", false
|
return "", "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +399,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
videoPublish = filepath.Clean(videoPublish)
|
videoPublish = filepath.Clean(videoPublish)
|
||||||
|
|
||||||
|
calcPair := orderCalcDirsForDemo12(listCalcInstanceDirsForPairing(videoPublish))
|
||||||
|
if *dryRun && len(calcPair) > 0 {
|
||||||
|
log.Printf("[dry-run] 操作与计算类目录配对顺序(1<-[0], 2<-[1]): %v", calcPair)
|
||||||
|
}
|
||||||
|
|
||||||
uploadDir := *uploadRoot
|
uploadDir := *uploadRoot
|
||||||
if uploadDir == "" {
|
if uploadDir == "" {
|
||||||
uploadDir = os.Getenv("UPLOAD_DIR")
|
uploadDir = os.Getenv("UPLOAD_DIR")
|
||||||
@@ -325,7 +448,7 @@ func main() {
|
|||||||
|
|
||||||
ok, skip, fail := 0, 0, 0
|
ok, skip, fail := 0, 0, 0
|
||||||
for _, m := range mappings {
|
for _, m := range mappings {
|
||||||
from, srcRelUsed, found := resolveSourceFile(videoPublish, m)
|
from, srcRelUsed, found := resolveSourceFile(videoPublish, m, calcPair)
|
||||||
if !found {
|
if !found {
|
||||||
log.Printf("SKIP 源文件不存在(已试备选路径/扫描子目录): dst=%s episode=%s", m.Dst, m.EpisodeScan)
|
log.Printf("SKIP 源文件不存在(已试备选路径/扫描子目录): dst=%s episode=%s", m.Dst, m.EpisodeScan)
|
||||||
skip++
|
skip++
|
||||||
|
|||||||
Reference in New Issue
Block a user