ffprobe.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package ffmpeg
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "io"
  7. "os/exec"
  8. "strings"
  9. "github.com/samber/lo"
  10. )
  11. type Result struct {
  12. Format *Format `json:"format"`
  13. Streams []*Stream `json:"streams"`
  14. }
  15. func (r *Result) HasAttachedPic() bool {
  16. return lo.ContainsBy(r.Streams, func(s *Stream) bool {
  17. return s.CodecType == "video"
  18. })
  19. }
  20. func (r *Result) getTagByKey(key string) string {
  21. for k, v := range r.Format.Tags {
  22. if key == strings.ToLower(k) {
  23. return v
  24. }
  25. }
  26. for _, stream := range r.Streams { // try to find in streams
  27. if stream.CodecType != "audio" {
  28. continue
  29. }
  30. for k, v := range stream.Tags {
  31. if key == strings.ToLower(k) {
  32. return v
  33. }
  34. }
  35. }
  36. return ""
  37. }
  38. func (r *Result) GetTitle() string {
  39. return r.getTagByKey("title")
  40. }
  41. func (r *Result) GetAlbum() string {
  42. return r.getTagByKey("album")
  43. }
  44. func (r *Result) GetArtists() []string {
  45. artists := strings.Split(r.getTagByKey("artist"), "/")
  46. for i := range artists {
  47. artists[i] = strings.TrimSpace(artists[i])
  48. }
  49. return artists
  50. }
  51. func (r *Result) HasMetadata() bool {
  52. return r.GetTitle() != "" || r.GetAlbum() != "" || len(r.GetArtists()) > 0
  53. }
  54. type Format struct {
  55. Filename string `json:"filename"`
  56. NbStreams int `json:"nb_streams"`
  57. NbPrograms int `json:"nb_programs"`
  58. FormatName string `json:"format_name"`
  59. FormatLongName string `json:"format_long_name"`
  60. StartTime string `json:"start_time"`
  61. Duration string `json:"duration"`
  62. BitRate string `json:"bit_rate"`
  63. ProbeScore int `json:"probe_score"`
  64. Tags map[string]string `json:"tags"`
  65. }
  66. type Stream struct {
  67. Index int `json:"index"`
  68. CodecName string `json:"codec_name"`
  69. CodecLongName string `json:"codec_long_name"`
  70. CodecType string `json:"codec_type"`
  71. CodecTagString string `json:"codec_tag_string"`
  72. CodecTag string `json:"codec_tag"`
  73. SampleFmt string `json:"sample_fmt"`
  74. SampleRate string `json:"sample_rate"`
  75. Channels int `json:"channels"`
  76. ChannelLayout string `json:"channel_layout"`
  77. BitsPerSample int `json:"bits_per_sample"`
  78. RFrameRate string `json:"r_frame_rate"`
  79. AvgFrameRate string `json:"avg_frame_rate"`
  80. TimeBase string `json:"time_base"`
  81. StartPts int `json:"start_pts"`
  82. StartTime string `json:"start_time"`
  83. BitRate string `json:"bit_rate"`
  84. Disposition *ProbeDisposition `json:"disposition"`
  85. Tags map[string]string `json:"tags"`
  86. }
  87. type ProbeDisposition struct {
  88. Default int `json:"default"`
  89. Dub int `json:"dub"`
  90. Original int `json:"original"`
  91. Comment int `json:"comment"`
  92. Lyrics int `json:"lyrics"`
  93. Karaoke int `json:"karaoke"`
  94. Forced int `json:"forced"`
  95. HearingImpaired int `json:"hearing_impaired"`
  96. VisualImpaired int `json:"visual_impaired"`
  97. CleanEffects int `json:"clean_effects"`
  98. AttachedPic int `json:"attached_pic"`
  99. TimedThumbnails int `json:"timed_thumbnails"`
  100. Captions int `json:"captions"`
  101. Descriptions int `json:"descriptions"`
  102. Metadata int `json:"metadata"`
  103. Dependent int `json:"dependent"`
  104. StillImage int `json:"still_image"`
  105. }
  106. func ProbeReader(ctx context.Context, rd io.Reader) (*Result, error) {
  107. cmd := exec.CommandContext(ctx, "ffprobe",
  108. "-v", "quiet", // disable logging
  109. "-print_format", "json", // use json format
  110. "-show_format", "-show_streams", "-show_error", // retrieve format and streams
  111. "pipe:0", // input from stdin
  112. )
  113. cmd.Stdin = rd
  114. stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
  115. cmd.Stdout, cmd.Stderr = stdout, stderr
  116. if err := cmd.Run(); err != nil {
  117. return nil, err
  118. }
  119. ret := new(Result)
  120. if err := json.Unmarshal(stdout.Bytes(), ret); err != nil {
  121. return nil, err
  122. }
  123. return ret, nil
  124. }