Skip to content

Commit

Permalink
Add --members-only option
Browse files Browse the repository at this point in the history
Allows downloading members streams only when monitoring a channel
  • Loading branch information
Kethsar committed Nov 19, 2023
1 parent 7faf1a6 commit 25df562
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 28 deletions.
1 change: 1 addition & 0 deletions Info.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ type DownloadInfo struct {
LiveURL bool
AudioOnly bool
VideoOnly bool
MembersOnly bool
InfoPrinted bool

Thumbnail string
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ Options:
Keep the final stream audio and video files after muxing them
instead of deleting them.
--members-only
Only download members-only streams. Can only be used with channel URLs
such as /live, /streams, etc, and requires cookies.
Useful when monitoring channels and you only want membership streams.
--merge
Automatically run the ffmpeg command for the downloaded streams
when manually cancelling the download. You will be prompted otherwise.
Expand All @@ -95,6 +100,10 @@ Options:
Be careful to monitor your disk usage when using this to avoid filling
your drive while away.
--newline
Print every message to a new line, instead of some messages reusing one
line.
--no-audio
Do not download the audio stream
Expand Down
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ Options:
Keep the final stream audio and video files after muxing them
instead of deleting them.
--members-only
Only download members-only streams. Can only be used with channel URLs
such as /live, /streams, etc, and requires cookies.
Useful when monitoring channels and you only want membership streams.
--merge
Automatically run the ffmpeg command for the downloaded streams
when manually cancelling the download. You will be prompted otherwise.
Expand Down Expand Up @@ -381,6 +386,7 @@ var (
monitorChannel bool
vp9 bool
h264 bool
membersOnly bool

cancelled = false
)
Expand Down Expand Up @@ -432,6 +438,7 @@ func init() {
cliFlags.BoolVar(&keepTSFiles, "keep-ts-files", false, "Keep the raw .ts files instead of deleting them after muxing.")
cliFlags.BoolVar(&separateAudio, "separate-audio", false, "Save a copy of the audio separately along with the muxed file.")
cliFlags.BoolVar(&monitorChannel, "monitor-channel", false, "Continually monitor a channel for streams.")
cliFlags.BoolVar(&membersOnly, "members-only", false, "Only download members-only streams when waiting on a channel URL such as /live.")
cliFlags.StringVar(&cookieFile, "c", "", "Cookies to be used when downloading.")
cliFlags.StringVar(&cookieFile, "cookies", "", "Cookies to be used when downloading.")
cliFlags.StringVar(&fnameFormat, "o", DefaultFilenameFormat, "Filename output format.")
Expand Down Expand Up @@ -508,6 +515,7 @@ func run() int {
info.H264 = h264
info.RetrySecs = retrySecs
info.FragMaxTries = fragMaxTries
info.MembersOnly = membersOnly

if doWait {
info.Wait = ActionDo
Expand Down
63 changes: 35 additions & 28 deletions player_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ type YtInitialData struct {
Richgridrenderer struct {
Contents []RichGridContent `json:"contents"`
} `json:"richGridRenderer"`
SectionListRenderer struct {
Contents []SectionListContent `json:"contents"`
} `json:"sectionListRenderer"`
} `json:"content"`
} `json:"tabRenderer"`
} `json:"tabs"`
Expand All @@ -155,26 +152,16 @@ type RichGridContent struct {
Style string `json:"style"`
} `json:"thumbnailOverlayTimeStatusRenderer"`
} `json:"thumbnailOverlays"`
Badges []struct {
Metadatabadgerenderer struct {
Style string `json:"style"`
} `json:"metadataBadgeRenderer"`
} `json:"badges"`
} `json:"videoRenderer"`
} `json:"content"`
} `json:"richItemRenderer"`
}

type SectionListContent struct {
ItemSectionRenderer struct {
Contents []struct {
Videorenderer struct {
Videoid string `json:"videoId"`
Thumbnailoverlays []struct {
Thumbnailoverlaytimestatusrenderer struct {
Style string `json:"style"`
} `json:"thumbnailOverlayTimeStatusRenderer"`
} `json:"thumbnailOverlays"`
} `json:"videoRenderer"`
} `json:"contents"`
} `json:"itemSectionRenderer"`
}

// Search the given HTML for the player response object
func GetJsonFromHtml(htmlData []byte, jsonDecl []byte) []byte {
var objData []byte
Expand Down Expand Up @@ -217,13 +204,19 @@ func GetJsonFromHtml(htmlData []byte, jsonDecl []byte) []byte {
}
}

func GetNewestStreamFromStreams(liveUrl string) string {
func (di *DownloadInfo) GetNewestStreamFromStreams() string {
// Surely there won't be more than 5 simultaneous streams when looking for membership streams, right?
const MAX_STREAM_ITEM_CHECK = 5
streamUrl := ""
if !di.LiveURL {
return streamUrl
}

initialData := &YtInitialData{}
var contents []RichGridContent
streamsUrl := strings.Replace(liveUrl, "/live", "/streams", 1)
streamsUrl := strings.Replace(di.URL, "/live", "/streams", 1)
streamsHtml := DownloadData(streamsUrl)
ytInitialData := GetJsonFromHtml(streamsHtml, ytInitialDataDecl)
streamUrl := ""

err := json.Unmarshal(ytInitialData, initialData)
if err != nil {
Expand All @@ -236,8 +229,25 @@ func GetNewestStreamFromStreams(liveUrl string) string {
}
}

for _, content := range contents {
for i, content := range contents {
if i >= MAX_STREAM_ITEM_CHECK {
break
}

videoRenderer := content.Richitemrenderer.Content.Videorenderer
if di.MembersOnly {
mengen := false
for _, badge := range videoRenderer.Badges {
if badge.Metadatabadgerenderer.Style == "BADGE_STYLE_TYPE_MEMBERS_ONLY" {
mengen = true
break
}
}

if !mengen {
continue
}
}

for _, thumbnailRenderer := range videoRenderer.Thumbnailoverlays {
if thumbnailRenderer.Thumbnailoverlaytimestatusrenderer.Style == "LIVE" {
Expand Down Expand Up @@ -315,18 +325,14 @@ func (di *DownloadInfo) GetVideoHtml() []byte {
var videoHtml []byte

if di.LiveURL {
streamUrl := ""

if len(streamUrl) == 0 {
streamUrl = GetNewestStreamFromStreams(di.URL)
}
streamUrl := di.GetNewestStreamFromStreams()

if len(streamUrl) > 0 {
videoHtml = DownloadData(streamUrl)
}
}

if len(videoHtml) == 0 {
if len(videoHtml) == 0 && !di.MembersOnly {
videoHtml = DownloadData(di.URL)
}

Expand Down Expand Up @@ -581,6 +587,7 @@ func (di *DownloadInfo) GetPlayablePlayerResponse() (retrieved int, pr *PlayerRe
// player response returned from /live does not include full information
if isLiveURL {
di.URL = fmt.Sprintf("https://www.youtube.com/watch?v=%s", di.VideoID)
di.MembersOnly = false
isLiveURL = false
continue
}
Expand Down

0 comments on commit 25df562

Please sign in to comment.