123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- package ffmpeg
- import (
- "bytes"
- "context"
- "encoding/json"
- "io"
- "os/exec"
- "strings"
- "github.com/samber/lo"
- )
- type Result struct {
- Format *Format `json:"format"`
- Streams []*Stream `json:"streams"`
- }
- func (r *Result) HasAttachedPic() bool {
- return lo.ContainsBy(r.Streams, func(s *Stream) bool {
- return s.CodecType == "video"
- })
- }
- func (r *Result) getTagByKey(key string) string {
- for k, v := range r.Format.Tags {
- if key == strings.ToLower(k) {
- return v
- }
- }
- for _, stream := range r.Streams { // try to find in streams
- if stream.CodecType != "audio" {
- continue
- }
- for k, v := range stream.Tags {
- if key == strings.ToLower(k) {
- return v
- }
- }
- }
- return ""
- }
- func (r *Result) GetTitle() string {
- return r.getTagByKey("title")
- }
- func (r *Result) GetAlbum() string {
- return r.getTagByKey("album")
- }
- func (r *Result) GetArtists() []string {
- artists := strings.Split(r.getTagByKey("artist"), "/")
- for i := range artists {
- artists[i] = strings.TrimSpace(artists[i])
- }
- return artists
- }
- func (r *Result) HasMetadata() bool {
- return r.GetTitle() != "" || r.GetAlbum() != "" || len(r.GetArtists()) > 0
- }
- type Format struct {
- Filename string `json:"filename"`
- NbStreams int `json:"nb_streams"`
- NbPrograms int `json:"nb_programs"`
- FormatName string `json:"format_name"`
- FormatLongName string `json:"format_long_name"`
- StartTime string `json:"start_time"`
- Duration string `json:"duration"`
- BitRate string `json:"bit_rate"`
- ProbeScore int `json:"probe_score"`
- Tags map[string]string `json:"tags"`
- }
- type Stream struct {
- Index int `json:"index"`
- CodecName string `json:"codec_name"`
- CodecLongName string `json:"codec_long_name"`
- CodecType string `json:"codec_type"`
- CodecTagString string `json:"codec_tag_string"`
- CodecTag string `json:"codec_tag"`
- SampleFmt string `json:"sample_fmt"`
- SampleRate string `json:"sample_rate"`
- Channels int `json:"channels"`
- ChannelLayout string `json:"channel_layout"`
- BitsPerSample int `json:"bits_per_sample"`
- RFrameRate string `json:"r_frame_rate"`
- AvgFrameRate string `json:"avg_frame_rate"`
- TimeBase string `json:"time_base"`
- StartPts int `json:"start_pts"`
- StartTime string `json:"start_time"`
- BitRate string `json:"bit_rate"`
- Disposition *ProbeDisposition `json:"disposition"`
- Tags map[string]string `json:"tags"`
- }
- type ProbeDisposition struct {
- Default int `json:"default"`
- Dub int `json:"dub"`
- Original int `json:"original"`
- Comment int `json:"comment"`
- Lyrics int `json:"lyrics"`
- Karaoke int `json:"karaoke"`
- Forced int `json:"forced"`
- HearingImpaired int `json:"hearing_impaired"`
- VisualImpaired int `json:"visual_impaired"`
- CleanEffects int `json:"clean_effects"`
- AttachedPic int `json:"attached_pic"`
- TimedThumbnails int `json:"timed_thumbnails"`
- Captions int `json:"captions"`
- Descriptions int `json:"descriptions"`
- Metadata int `json:"metadata"`
- Dependent int `json:"dependent"`
- StillImage int `json:"still_image"`
- }
- func ProbeReader(ctx context.Context, rd io.Reader) (*Result, error) {
- cmd := exec.CommandContext(ctx, "ffprobe",
- "-v", "quiet", // disable logging
- "-print_format", "json", // use json format
- "-show_format", "-show_streams", "-show_error", // retrieve format and streams
- "pipe:0", // input from stdin
- )
- cmd.Stdin = rd
- stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
- cmd.Stdout, cmd.Stderr = stdout, stderr
- if err := cmd.Run(); err != nil {
- return nil, err
- }
- ret := new(Result)
- if err := json.Unmarshal(stdout.Bytes(), ret); err != nil {
- return nil, err
- }
- return ret, nil
- }
|