fix(promotion-import): 扫描实例(一)(二)目录、多mov取最大文件
Made-with: Cursor
This commit is contained in:
@@ -57,11 +57,12 @@ func mimeForExt(ext string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// importRule:按顺序尝试 SrcRels;若均不存在且 FallbackScanDir 非空,则在该子目录下「仅一个 .mov/.jpg 时」自动选用(兼容实际文件夹命名与文件名不一致)
|
// importRule:按顺序尝试 SrcRels;再 FallbackScanDir 下智能选文件;EpisodeScan 在非空时扫描 视频发布 下含「实例+一/二」的子目录(兼容半角括号、文件夹名略有差异)
|
||||||
type importRule struct {
|
type importRule struct {
|
||||||
SrcRels []string
|
SrcRels []string
|
||||||
Dst string
|
Dst string
|
||||||
FallbackScanDir string // 相对 视频发布/,仅当目标为视频时用 .mov;封面用 .jpg
|
FallbackScanDir string
|
||||||
|
EpisodeScan string // "一" 或 "二"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与 sync-video-assets-to-social.sh 对齐,并增加备选路径(线上常见「实例(一)」内不叫宣传片.mov 的情况)
|
// 与 sync-video-assets-to-social.sh 对齐,并增加备选路径(线上常见「实例(一)」内不叫宣传片.mov 的情况)
|
||||||
@@ -69,25 +70,155 @@ var mappings = []importRule{
|
|||||||
{[]string{
|
{[]string{
|
||||||
"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg",
|
"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg",
|
||||||
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg",
|
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg",
|
||||||
}, "video-calc-demo-1-cover.jpg", "宇恒一号操作计算软件实例(一)"},
|
"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg",
|
||||||
|
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg",
|
||||||
|
}, "video-calc-demo-1-cover.jpg", "宇恒一号操作计算软件实例(一)", "一"},
|
||||||
{[]string{
|
{[]string{
|
||||||
"宇恒一号操作计算软件实例(一)/宣传片.mov",
|
"宇恒一号操作计算软件实例(一)/宣传片.mov",
|
||||||
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov",
|
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov",
|
||||||
}, "video-calc-demo-1.mov", "宇恒一号操作计算软件实例(一)"},
|
"宇恒一号操作计算软件实例(一)/宣传片.mov",
|
||||||
|
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov",
|
||||||
|
}, "video-calc-demo-1.mov", "宇恒一号操作计算软件实例(一)", "一"},
|
||||||
{[]string{
|
{[]string{
|
||||||
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg",
|
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg",
|
||||||
"宇恒一号操作计算软件实例(二)/宣传片-封面.jpg",
|
"宇恒一号操作计算软件实例(二)/宣传片-封面.jpg",
|
||||||
}, "video-calc-demo-2-cover.jpg", "宇恒一号操作计算软件实例(二)"},
|
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg",
|
||||||
|
"宇恒一号操作计算软件实例(二)/宣传片-封面.jpg",
|
||||||
|
}, "video-calc-demo-2-cover.jpg", "宇恒一号操作计算软件实例(二)", "二"},
|
||||||
{[]string{
|
{[]string{
|
||||||
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov",
|
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov",
|
||||||
"宇恒一号操作计算软件实例(二)/宣传片.mov",
|
"宇恒一号操作计算软件实例(二)/宣传片.mov",
|
||||||
}, "video-calc-demo-2.mov", "宇恒一号操作计算软件实例(二)"},
|
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov",
|
||||||
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg"}, "video-aiword-cover.jpg", "宇恒一号AIWord简介"},
|
"宇恒一号操作计算软件实例(二)/宣传片.mov",
|
||||||
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介.mov"}, "video-aiword.mov", "宇恒一号AIWord简介"},
|
}, "video-calc-demo-2.mov", "宇恒一号操作计算软件实例(二)", "二"},
|
||||||
{[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg"}, "video-voice-office-cover.jpg", "宇恒一号语音办公实例"},
|
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg"}, "video-aiword-cover.jpg", "宇恒一号AIWord简介", ""},
|
||||||
{[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例.mov"}, "video-voice-office.mov", "宇恒一号语音办公实例"},
|
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介.mov"}, "video-aiword.mov", "宇恒一号AIWord简介", ""},
|
||||||
{[]string{"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票-封面.jpg"}, "video-invoice-ai-cover.jpg", "宇恒一号,AI 全自动办发票"},
|
{[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg"}, "video-voice-office-cover.jpg", "宇恒一号语音办公实例", ""},
|
||||||
{[]string{"宇恒一号,AI 全自动办发票/宇恒一号,AI 全自动办发票.mov"}, "video-invoice-ai.mov", "宇恒一号,AI 全自动办发票"},
|
{[]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 discoverEpisodeDir(videoPublish, episode string) (dirName string, ok bool) {
|
||||||
|
full := "(" + episode + ")"
|
||||||
|
half := "(" + episode + ")"
|
||||||
|
entries, err := os.ReadDir(videoPublish)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
var hits []string
|
||||||
|
for _, e := range entries {
|
||||||
|
if !e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n := e.Name()
|
||||||
|
if !strings.Contains(n, "实例") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
marked := strings.Contains(n, full) || strings.Contains(n, half)
|
||||||
|
if !marked {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if episode == "一" && (strings.Contains(n, "(二)") || strings.Contains(n, "(二)")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if episode == "二" && (strings.Contains(n, "(一)") || strings.Contains(n, "(一)")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hits = append(hits, n)
|
||||||
|
}
|
||||||
|
if len(hits) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if len(hits) == 1 {
|
||||||
|
return hits[0], true
|
||||||
|
}
|
||||||
|
for _, h := range hits {
|
||||||
|
if strings.Contains(h, "软件") {
|
||||||
|
return h, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hits[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func pickMediaInDir(videoPublish, dirName string, dstFile string) (absPath, relChosen string, ok bool) {
|
||||||
|
ext := strings.ToLower(filepath.Ext(dstFile))
|
||||||
|
if ext != ".mov" && ext != ".jpg" && ext != ".jpeg" {
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
dir := filepath.Join(videoPublish, filepath.FromSlash(dirName))
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
type cand struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
var movs, imgs []cand
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ne := strings.ToLower(filepath.Ext(e.Name()))
|
||||||
|
p := filepath.Join(dir, e.Name())
|
||||||
|
st, err := os.Stat(p)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sz := st.Size()
|
||||||
|
if ne == ".mov" {
|
||||||
|
movs = append(movs, cand{e.Name(), sz})
|
||||||
|
}
|
||||||
|
if ne == ".jpg" || ne == ".jpeg" {
|
||||||
|
imgs = append(imgs, cand{e.Name(), sz})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pickMov := func() (string, bool) {
|
||||||
|
if len(movs) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
best := movs[0]
|
||||||
|
for _, c := range movs[1:] {
|
||||||
|
if c.size > best.size {
|
||||||
|
best = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best.name, true
|
||||||
|
}
|
||||||
|
pickImg := func() (string, bool) {
|
||||||
|
if len(imgs) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
for _, c := range imgs {
|
||||||
|
if strings.Contains(c.name, "封面") {
|
||||||
|
return c.name, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
best := imgs[0]
|
||||||
|
for _, c := range imgs[1:] {
|
||||||
|
if c.size > best.size {
|
||||||
|
best = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best.name, true
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
var found bool
|
||||||
|
switch ext {
|
||||||
|
case ".mov":
|
||||||
|
name, found = pickMov()
|
||||||
|
case ".jpg", ".jpeg":
|
||||||
|
name, found = pickImg()
|
||||||
|
default:
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
rel := filepath.ToSlash(filepath.Join(dirName, name))
|
||||||
|
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) (absPath, relChosen string, ok bool) {
|
||||||
@@ -97,38 +228,32 @@ func resolveSourceFile(videoPublish string, rule importRule) (absPath, relChosen
|
|||||||
return p, rel, true
|
return p, rel, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rule.FallbackScanDir == "" {
|
tryDirs := []string{}
|
||||||
|
if rule.FallbackScanDir != "" {
|
||||||
|
tryDirs = append(tryDirs, rule.FallbackScanDir)
|
||||||
|
}
|
||||||
|
if rule.EpisodeScan != "" {
|
||||||
|
if d, ok := discoverEpisodeDir(videoPublish, rule.EpisodeScan); ok {
|
||||||
|
// 避免与固定目录重复
|
||||||
|
dup := false
|
||||||
|
for _, x := range tryDirs {
|
||||||
|
if x == d {
|
||||||
|
dup = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dup {
|
||||||
|
tryDirs = append(tryDirs, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, dirName := range tryDirs {
|
||||||
|
if abs, rel, ok := pickMediaInDir(videoPublish, dirName, rule.Dst); ok {
|
||||||
|
return abs, rel, true
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", "", false
|
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() {
|
func main() {
|
||||||
loadEnv()
|
loadEnv()
|
||||||
@@ -202,7 +327,7 @@ func main() {
|
|||||||
for _, m := range mappings {
|
for _, m := range mappings {
|
||||||
from, srcRelUsed, found := resolveSourceFile(videoPublish, m)
|
from, srcRelUsed, found := resolveSourceFile(videoPublish, m)
|
||||||
if !found {
|
if !found {
|
||||||
log.Printf("SKIP 源文件不存在(已试备选路径): dst=%s dir=%s", m.Dst, m.FallbackScanDir)
|
log.Printf("SKIP 源文件不存在(已试备选路径/扫描子目录): dst=%s episode=%s", m.Dst, m.EpisodeScan)
|
||||||
skip++
|
skip++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user