package main import ( "bytes" "fmt" "io/fs" "log" "os/exec" "path/filepath" "sort" "strconv" "strings" ) // Category 电影分类结构 type Category struct { Name string `json:"name"` Movies []Movie `json:"movies"` } // Movie 电影结构 type Movie struct { Name string `json:"filename"` Image string `json:"image"` Duration int `json:"duration"` } // var movies []Movie var categories []Category var IsRemakePNG = false func initMovie() { // 需要改进 如果存在这个文件的略缩图, 就不存进movieDict里 var movieDict map[string]*Movie = make(map[string]*Movie) matches, err := filepath.Glob("movie/*") if err != nil { log.Println(err) } for _, filename := range matches { base := filepath.Base(filename) // ext := filepath.Ext(base) base = base[:strings.IndexByte(base, '.')] if _, ok := movieDict[base]; ok { if !IsRemakePNG { delete(movieDict, base) } } else { movieDict[base] = &Movie{Name: filename} } } // 初始化分类 categories = []Category{ {Name: "15min", Movies: []Movie{}}, {Name: "30min", Movies: []Movie{}}, {Name: "60min", Movies: []Movie{}}, {Name: "大于60min", Movies: []Movie{}}, } filepath.Walk("./movie", func(path string, info fs.FileInfo, err error) error { if !info.IsDir() && filepath.Ext(info.Name()) != ".png" { base := info.Name() cmd := exec.Command("ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=nw=1:nk=1", "./movie/"+info.Name()) durationOutput, err := cmd.Output() if err != nil { log.Printf("Error getting duration for %s: %v", info.Name(), err) return err } duration, err := strconv.ParseFloat(strings.Trim(string(durationOutput), "\n "), 64) if err != nil { log.Printf("Error parsing duration for %s: %v", info.Name(), err) return err } // log.Println(path, info.Name()) movie := Movie{ Name: info.Name(), Image: base[:strings.IndexByte(base, '.')] + ".png", Duration: int(duration / 60.0), } if m, ok := movieDict[base[:strings.IndexByte(base, '.')]]; ok { m.Duration = movie.Duration } if movie.Duration <= 15 { categories[0].Movies = append(categories[0].Movies, movie) } else if movie.Duration <= 30 { categories[1].Movies = append(categories[1].Movies, movie) } else if movie.Duration <= 60 { categories[2].Movies = append(categories[2].Movies, movie) } else { categories[3].Movies = append(categories[3].Movies, movie) } } return nil }) for _, category := range categories { var movies = category.Movies sort.Slice(movies, func(i, j int) bool { return movies[i].Duration < movies[j].Duration }) } for key, movie := range movieDict { // width := 160 // height := 120 // if movie.Duration <= 40 { // continue // } log.Println(movie.Name, "时长:", movie.Duration) var filter string if movie.Duration <= 2 { filter = "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,5)',scale=320:180,tile=3x3" } else if movie.Duration <= 5 { filter = "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,10)',scale=320:180,tile=3x3" } else if movie.Duration <= 30 { filter = "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,20)',scale=320:180,tile=3x3" } else if movie.Duration <= 60 { filter = "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,35)',scale=320:180,tile=3x3" } else { filter = "select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,60)',scale=320:180,tile=3x3" } cmd := exec.Command("ffmpeg", "-i", movie.Name, "-vf", filter, "-frames:v", "1", "-y", "movie/"+key+".png", ) var buffer bytes.Buffer cmd.Stdout = &buffer if cmd.Run() != nil { log.Println(buffer.String()) panic(fmt.Errorf("could not generate frame %s %d", movie.Name, movie.Duration)) } } // log.Printf("%##v", categories) }