Skip to content

Commit

Permalink
Refactor Format/Stream structs
Browse files Browse the repository at this point in the history
  • Loading branch information
corny committed Aug 13, 2020
1 parent ade0884 commit 140be57
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 117 deletions.
4 changes: 2 additions & 2 deletions downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func (dl *Downloader) DownloadWithHighQuality(ctx context.Context, outputFile st

switch quality {
case "hd1080":
videoStream = v.FindStreamByItag(137)
audioStream = v.FindStreamByItag(140)
videoStream = v.Streams.FindByItag(137)
audioStream = v.Streams.FindByItag(140)
default:
return fmt.Errorf("unknown quality: %s", quality)
}
Expand Down
39 changes: 36 additions & 3 deletions response_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ type PlayerResponseData struct {
ContextParams string `json:"contextParams"`
} `json:"playabilityStatus"`
StreamingData struct {
ExpiresInSeconds string `json:"expiresInSeconds"`
Formats []Format `json:"formats"`
AdaptiveFormats []AdaptiveFormat `json:"adaptiveFormats"`
ExpiresInSeconds string `json:"expiresInSeconds"`
Formats []Stream `json:"formats"`
AdaptiveFormats []Stream `json:"adaptiveFormats"`
} `json:"streamingData"`
Captions struct {
PlayerCaptionsRenderer struct {
Expand Down Expand Up @@ -121,3 +121,36 @@ type PlayerResponseData struct {
} `json:"playerMicroformatRenderer"`
} `json:"microformat"`
}

type Stream struct {
ItagNo int `json:"itag"`
URL string `json:"url"`
MimeType string `json:"mimeType"`
Quality string `json:"quality"`
Cipher string `json:"signatureCipher"`
Bitrate int `json:"bitrate"`
FPS int `json:"fps"`
Width int `json:"width"`
Height int `json:"height"`
LastModified string `json:"lastModified"`
ContentLength string `json:"contentLength"`
QualityLabel string `json:"qualityLabel"`
ProjectionType string `json:"projectionType"`
AverageBitrate int `json:"averageBitrate"`
AudioQuality string `json:"audioQuality"`
ApproxDurationMs string `json:"approxDurationMs"`
AudioSampleRate string `json:"audioSampleRate"`
AudioChannels int `json:"audioChannels"`

// InitRange is only available for adaptive streams
InitRange *struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"initRange"`

// IndexRange is only available for adaptive streams
IndexRange *struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"indexRange"`
}
21 changes: 21 additions & 0 deletions stream_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package youtube

type StreamList []Stream

func (list StreamList) FindByQuality(quality string) *Stream {
for i := range list {
if list[i].Quality == quality {
return &list[i]
}
}
return nil
}

func (list StreamList) FindByItag(itagNo int) *Stream {
for i := range list {
if list[i].ItagNo == itagNo {
return &list[i]
}
}
return nil
}
118 changes: 7 additions & 111 deletions video.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,17 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"strconv"
"time"
)

type Video struct {
ID string
Streams []Stream
Formats []Format
AdaptiveFormats []AdaptiveFormat
Title string
Author string
Duration time.Duration
}

type Stream struct {
ItagNo int `json:"itag"`
URL string `json:"url"`
MimeType string `json:"mimeType"`
Quality string `json:"quality"`
Cipher string `json:"signatureCipher"`
}

type Format struct {
Stream
Bitrate int `json:"bitrate"`
Width int `json:"width"`
Height int `json:"height"`
LastModified string `json:"lastModified"`
ContentLength string `json:"contentLength,omitempty"`
QualityLabel string `json:"qualityLabel"`
ProjectionType string `json:"projectionType"`
AverageBitrate int `json:"averageBitrate,omitempty"`
AudioQuality string `json:"audioQuality"`
ApproxDurationMs string `json:"approxDurationMs"`
AudioSampleRate string `json:"audioSampleRate"`
AudioChannels int `json:"audioChannels"`
}

type AdaptiveFormat struct {
Stream
Bitrate int `json:"bitrate"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
InitRange struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"initRange"`
IndexRange struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"indexRange"`
LastModified string `json:"lastModified"`
ContentLength string `json:"contentLength"`
Fps int `json:"fps,omitempty"`
QualityLabel string `json:"qualityLabel,omitempty"`
ProjectionType string `json:"projectionType"`
AverageBitrate int `json:"averageBitrate"`
ApproxDurationMs string `json:"approxDurationMs"`
ColorInfo struct {
Primaries string `json:"primaries"`
TransferCharacteristics string `json:"transferCharacteristics"`
MatrixCoefficients string `json:"matrixCoefficients"`
} `json:"colorInfo,omitempty"`
HighReplication bool `json:"highReplication,omitempty"`
AudioQuality string `json:"audioQuality,omitempty"`
AudioSampleRate string `json:"audioSampleRate,omitempty"`
AudioChannels int `json:"audioChannels,omitempty"`
}

func (v *Video) FindStreamByQuality(quality string) *Stream {
for i := range v.Streams {
if v.Streams[i].Quality == quality {
return &v.Streams[i]
}
}

return nil
}

func (v *Video) FindStreamByItag(itagNo int) *Stream {
for i := range v.Streams {
if v.Streams[i].ItagNo == itagNo {
return &v.Streams[i]
}
}
return nil
ID string
Streams StreamList
Title string
Author string
Duration time.Duration
}

func (v *Video) parseVideoInfo(info string) error {
Expand Down Expand Up @@ -134,39 +57,12 @@ func (v *Video) parseVideoInfo(info string) error {
}
}

// Get video download link
if err := v.parseStreams(prData); err != nil {
return err
}
// Collect Streams
v.Streams = append(prData.StreamingData.Formats, prData.StreamingData.AdaptiveFormats...)

if len(v.Streams) == 0 {
return errors.New("no Stream list found in the server's answer")
}

return nil
}

func (v *Video) parseStreams(prData PlayerResponseData) error {
v.Formats = prData.StreamingData.Formats
v.AdaptiveFormats = prData.StreamingData.AdaptiveFormats
size := len(v.Formats) + len(v.AdaptiveFormats)
streams := make([]Stream, 0, size)

filterFormat := func(stream Stream) {
if stream.MimeType == "" {
// FIXME logging
log.Printf("An error occurred while decoding one of the video's Stream's information: Stream %+v", stream)
return
}
streams = append(streams, stream)
}

for _, format := range v.Formats {
filterFormat(format.Stream)
}
for _, format := range v.AdaptiveFormats {
filterFormat(format.Stream)
}
v.Streams = streams
return nil
}
2 changes: 1 addition & 1 deletion video_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestDownload_Regular(t *testing.T) {

var stream *Stream
if tc.itagNo > 0 {
stream = video.FindStreamByItag(tc.itagNo)
stream = video.Streams.FindByItag(tc.itagNo)
require.NotNil(stream)
} else {
stream = &video.Streams[0]
Expand Down

0 comments on commit 140be57

Please sign in to comment.