Fix tests, move helpers to media package
This commit is contained in:
parent
3ce3736770
commit
8c30a2581a
|
@ -79,7 +79,7 @@ func (s *FetchMediaSetService) Fetch(ctx context.Context, id string) (*MediaSet,
|
||||||
|
|
||||||
// grab an audio stream from youtube
|
// grab an audio stream from youtube
|
||||||
// TODO: avoid possible panic
|
// TODO: avoid possible panic
|
||||||
format := sortAudio(video.Formats)[0]
|
format := SortYoutubeAudio(video.Formats)[0]
|
||||||
|
|
||||||
sampleRate, err := strconv.Atoi(format.AudioSampleRate)
|
sampleRate, err := strconv.Atoi(format.AudioSampleRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -109,10 +109,7 @@ func (s *FetchMediaSetService) Fetch(ctx context.Context, id string) (*MediaSet,
|
||||||
return &mediaSet, nil
|
return &mediaSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchAudio fetches the audio stream from Youtube, pipes it through FFMPEG to
|
// FetchAudio fetches the audio part of a MediaSet.
|
||||||
// extract the raw audio samples, and uploads them to S3. It
|
|
||||||
// returns a FetchAudioProgressReader. This reader must be read until
|
|
||||||
// completion - it will return any error which occurs during the fetch process.
|
|
||||||
func (s *FetchMediaSetService) FetchAudio(ctx context.Context, id string) (FetchAudioProgressReader, error) {
|
func (s *FetchMediaSetService) FetchAudio(ctx context.Context, id string) (FetchAudioProgressReader, error) {
|
||||||
mediaSet := NewMediaSet(id)
|
mediaSet := NewMediaSet(id)
|
||||||
if !mediaSet.Exists() {
|
if !mediaSet.Exists() {
|
||||||
|
@ -134,7 +131,7 @@ func (s *FetchMediaSetService) FetchAudio(ctx context.Context, id string) (Fetch
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: avoid possible panic
|
// TODO: avoid possible panic
|
||||||
format := sortAudio(video.Formats)[0]
|
format := SortYoutubeAudio(video.Formats)[0]
|
||||||
|
|
||||||
stream, _, err := s.youtube.GetStreamContext(ctx, video, &format)
|
stream, _, err := s.youtube.GetStreamContext(ctx, video, &format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -184,8 +181,6 @@ type fetchAudioState struct {
|
||||||
uploader *multipartUploadWriter
|
uploader *multipartUploadWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
// run copies the audio data from ffmpeg, waits for termination and then cleans
|
|
||||||
// up appropriately.
|
|
||||||
func (s *fetchAudioState) run(ctx context.Context) {
|
func (s *fetchAudioState) run(ctx context.Context) {
|
||||||
mw := io.MultiWriter(s, s.uploader)
|
mw := io.MultiWriter(s, s.uploader)
|
||||||
done := make(chan error)
|
done := make(chan error)
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
youtubev2 "github.com/kkdai/youtube/v2"
|
youtubev2 "github.com/kkdai/youtube/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sortAudio returns the provided formats ordered in descending preferred
|
// SortYoutubeAudio returns the provided formats ordered in descending preferred
|
||||||
// order. The ideal candidate is opus-encoded stereo audio in a webm container,
|
// order. The ideal candidate is opus-encoded stereo audio in a webm container,
|
||||||
// with the lowest available bitrate.
|
// with the lowest available bitrate.
|
||||||
func sortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
|
func SortYoutubeAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
|
||||||
var formats youtubev2.FormatList
|
var formats youtubev2.FormatList
|
||||||
for _, format := range inFormats {
|
for _, format := range inFormats {
|
||||||
if format.FPS == 0 && format.AudioChannels > 0 {
|
if format.FPS == 0 && format.AudioChannels > 0 {
|
||||||
|
@ -32,3 +32,25 @@ func sortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
|
||||||
})
|
})
|
||||||
return formats
|
return formats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SortYoutubeVideo returns the provided formats ordered in descending preferred
|
||||||
|
// order. The ideal candidate is video in an mp4 container with a low
|
||||||
|
// bitrate, with audio channels (needed to allow synced playback on the
|
||||||
|
// website).
|
||||||
|
func SortYoutubeVideo(inFormats youtubev2.FormatList) youtubev2.FormatList {
|
||||||
|
var formats youtubev2.FormatList
|
||||||
|
for _, format := range inFormats {
|
||||||
|
if format.FPS > 0 && format.ContentLength > 0 {
|
||||||
|
formats = append(formats, format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.SliceStable(formats, func(i, j int) bool {
|
||||||
|
isMP4I := strings.Contains(formats[i].MimeType, "mp4")
|
||||||
|
isMP4J := strings.Contains(formats[j].MimeType, "mp4")
|
||||||
|
if isMP4I && isMP4J {
|
||||||
|
return formats[i].ContentLength < formats[j].ContentLength
|
||||||
|
}
|
||||||
|
return isMP4I
|
||||||
|
})
|
||||||
|
return formats
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package youtube_test
|
package media_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.netflux.io/rob/clipper/youtube"
|
"git.netflux.io/rob/clipper/media"
|
||||||
youtubev2 "github.com/kkdai/youtube/v2"
|
youtubev2 "github.com/kkdai/youtube/v2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -12,70 +12,77 @@ func TestSortAudio(t *testing.T) {
|
||||||
formats := []youtubev2.Format{
|
formats := []youtubev2.Format{
|
||||||
{
|
{
|
||||||
MimeType: `audio/webm; codecs="opus"`,
|
MimeType: `audio/webm; codecs="opus"`,
|
||||||
Bitrate: 350_000,
|
ContentLength: 38573,
|
||||||
AudioChannels: 2,
|
AudioChannels: 1,
|
||||||
AudioSampleRate: "16000",
|
AudioSampleRate: "16000",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MimeType: `audio/webm; codecs="opus"`,
|
MimeType: `audio/webm; codecs="opus"`,
|
||||||
Bitrate: 350_000,
|
ContentLength: 39458,
|
||||||
AudioChannels: 2,
|
AudioChannels: 2,
|
||||||
AudioSampleRate: "48000",
|
AudioSampleRate: "16000",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MimeType: `audio/mp4; codecs="mp4a.40.2"`,
|
MimeType: `audio/mp4; codecs="mp4a.40.2"`,
|
||||||
Bitrate: 250_000,
|
ContentLength: 118394,
|
||||||
|
AudioChannels: 1,
|
||||||
|
AudioSampleRate: "48000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MimeType: `audio/webm; codecs="opus"`,
|
||||||
|
ContentLength: 127393,
|
||||||
AudioChannels: 2,
|
AudioChannels: 2,
|
||||||
AudioSampleRate: "48000",
|
AudioSampleRate: "48000",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MimeType: `audio/webm; codecs="opus"`,
|
MimeType: `audio/webm; codecs="opus"`,
|
||||||
Bitrate: 125_000,
|
ContentLength: 123245,
|
||||||
AudioChannels: 2,
|
AudioChannels: 2,
|
||||||
AudioSampleRate: "48000",
|
AudioSampleRate: "48000",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedFormats := youtube.SortAudio(formats)
|
sortedFormats := media.SortYoutubeAudio(formats)
|
||||||
|
|
||||||
assert.Equal(t, formats[1], sortedFormats[0])
|
assert.Equal(t, formats[1], sortedFormats[0])
|
||||||
assert.Equal(t, formats[3], sortedFormats[1])
|
assert.Equal(t, formats[4], sortedFormats[1])
|
||||||
assert.Equal(t, formats[0], sortedFormats[2])
|
assert.Equal(t, formats[3], sortedFormats[2])
|
||||||
assert.Equal(t, formats[2], sortedFormats[3])
|
assert.Equal(t, formats[0], sortedFormats[3])
|
||||||
|
assert.Equal(t, formats[2], sortedFormats[4])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSortVideo(t *testing.T) {
|
func TestSortVideo(t *testing.T) {
|
||||||
formats := []youtubev2.Format{
|
formats := []youtubev2.Format{
|
||||||
|
{
|
||||||
|
MimeType: `audio/webm; codecs="opus"`,
|
||||||
|
QualityLabel: "120p",
|
||||||
|
FPS: 30,
|
||||||
|
ContentLength: 39402,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
||||||
QualityLabel: "240p",
|
QualityLabel: "240p",
|
||||||
FPS: 30,
|
FPS: 30,
|
||||||
AudioChannels: 2,
|
ContentLength: 40353,
|
||||||
},
|
|
||||||
{
|
|
||||||
MimeType: `audio/webm; codecs="opus"`,
|
|
||||||
QualityLabel: "",
|
|
||||||
FPS: 0,
|
|
||||||
AudioChannels: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
||||||
QualityLabel: "720p",
|
QualityLabel: "720p",
|
||||||
FPS: 30,
|
FPS: 30,
|
||||||
AudioChannels: 2,
|
ContentLength: 393103,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
MimeType: `video/mp4; codecs="avc1.42001E, mp4a.40.2"`,
|
||||||
QualityLabel: "360p",
|
QualityLabel: "360p",
|
||||||
FPS: 30,
|
FPS: 0,
|
||||||
AudioChannels: 2,
|
ContentLength: 20403,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedFormats := youtube.SortVideo(formats)
|
sortedFormats := media.SortYoutubeVideo(formats)
|
||||||
|
|
||||||
assert.Len(t, sortedFormats, 3)
|
assert.Len(t, sortedFormats, 3)
|
||||||
assert.Equal(t, formats[3], sortedFormats[0])
|
assert.Equal(t, formats[1], sortedFormats[0])
|
||||||
assert.Equal(t, formats[0], sortedFormats[1])
|
assert.Equal(t, formats[2], sortedFormats[1])
|
||||||
assert.Equal(t, formats[2], sortedFormats[2])
|
assert.Equal(t, formats[0], sortedFormats[2])
|
||||||
}
|
}
|
Loading…
Reference in New Issue