Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature --live-from #198

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Changed --live-from positive value feature
Changed how --live-from works with positive time values. It now calculates the time from the start of the stream.
  • Loading branch information
keredau committed Jul 16, 2024
commit 606081b5fdb87d6091843b07494d5c1a4941f2b3
57 changes: 38 additions & 19 deletions Info.go
Original file line number Diff line number Diff line change
@@ -497,6 +497,7 @@ func (di *DownloadInfo) ParseLiveFromStrVal() error {
// --live-from now
// Seek to current sequence number
di.LiveFromSq = di.LastSq
LogGeneral("--live-from: Starting from now...")
} else {
durationVal := strings.TrimPrefix(di.LiveFromVal, "-") // Removes negative symbol from start of duration string

@@ -506,8 +507,8 @@ func (di *DownloadInfo) ParseLiveFromStrVal() error {
// Try to parse the value as a HH:MM:SS string
duration, err = hhmmss.Parse(durationVal)
if err != nil {
errStr := fmt.Errorf("unable to parse value as either a duration or a time: %v", err)
return errors.New(errStr.Error())
LogError("--live-from: Unable to parse value as either a duration or a time string: %v", err)
return err
}
}

@@ -522,34 +523,52 @@ func (di *DownloadInfo) ParseLiveFromStrVal() error {

// Invalid time specification (too short or too long)
if secondsTotal < 0 || secondsTotal > LiveMaximumSeekable {
errStr := fmt.Errorf("invalid duration specified %s. (maximum video seek time is %d days)", di.LiveFromVal, (LiveMaximumSeekable / 60 / 60 / 24))
return errors.New(errStr.Error())
LogError("--live-from: Invalid duration specified '%s'. (Maximum video seek time is %d days)", di.LiveFromVal, (LiveMaximumSeekable / 60 / 60 / 24))
return errors.New("invalid duration specified")
}
// If the stream hasn't been live long enough for the specified duration
if noOfFragsToJump > di.LastSq {
streamLength := di.LastSq * di.TargetDuration
curStreamDuration := time.Duration(streamLength * 1e9)
curStreamDuration := SecondsToDurationAndTimeStr(streamLength)

errStr := fmt.Errorf("invalid duration specified. the stream has not been live for that long (live for %s)", curStreamDuration)
return errors.New(errStr.Error())
LogError("--live-from: Invalid duration specified. The stream has not been live for that long [Live for %s].", curStreamDuration)
return errors.New("invalid duration specified")
}

di.LiveFromSq = di.LastSq - noOfFragsToJump
LogInfo("--live-from: Jumping back %d seconds (-%d frags). Will start from sequence %d [current sq right now is %d]", secondsRoundedToFragLength, noOfFragsToJump, di.LiveFromSq, di.LastSq)
LogGeneral("--live-from: Jumping back %d seconds from now, and starting to download from that time.", secondsRoundedToFragLength)
LogDebug("Jumping back -%d frags. Will start from sequence %d [current sq right now is %d].", noOfFragsToJump, di.LiveFromSq, di.LastSq)
} else {
// --live-from positive value
// Waits a specified timeframe and begins from a calculated future sequence number
if secondsTotal < 0 {
errStr := fmt.Errorf("invalid duration specified %s. (maximum video seek time is %d days)", di.LiveFromVal, (LiveMaximumSeekable / 60 / 60 / 24))
return errors.New(errStr.Error())
}
// Calculate the sequence number of the specified stream time to start from.
maxSq := di.LastSq
targetStartFrag := noOfFragsToJump

di.LiveFromSq = di.LastSq + noOfFragsToJump
LogGeneral("--live-from: Waiting %s (%d seconds) before starting...", di.LiveFromVal, secondsRoundedToFragLength)
LogInfo("--live-from: Waiting %s (%d seconds) before starting. Will start from sequence %d [current sq right now is %d]", di.LiveFromVal, secondsRoundedToFragLength, di.LiveFromSq, di.LastSq)
// Stream hasn't been live long enough
if di.LastSq < targetStartFrag {
streamLength := di.LastSq * di.TargetDuration
curStreamDuration := SecondsToDurationAndTimeStr(streamLength)

time.Sleep(time.Duration(secondsRoundedToFragLength+di.TargetDuration) * time.Second) // Waits for the specified length of time +1 frag interval (2 or 5 seconds etc).
info.GetVideoInfo() // And now that the duration has elapsed, re-grab the video information (to update the latest sq).
errStr := fmt.Errorf("invalid duration specified. the stream has not been live for that long [live for %s]", curStreamDuration)
return errors.New(errStr.Error())
} else {
// Make sure the Start Frag is within the 5 day limit.
if targetStartFrag < (di.LastSq - LiveMaximumSeekable) {
LogError("YT only retains the livestream 5 days past for seeking, your --live-from value of '%s' is not valid.", di.LiveFromVal)

// Calculate how long the stream has been live for
streamLiveTime := di.LastSq * di.TargetDuration
minSeekTime := streamLiveTime - LiveMaximumSeekable
LogError("You must specify a --live-from value between: %s and %s", SecondsToDurationAndTimeStr(minSeekTime), SecondsToDurationAndTimeStr(streamLiveTime))
return errors.New("value is not valid for stream duration")
}

di.LiveFromSq = targetStartFrag
startTimeStr := SecondsToDurationAndTimeStr(di.LiveFromSq * di.TargetDuration)
totalTimeToGrabStr := SecondsToDurationAndTimeStr((maxSq - di.LiveFromSq) * di.TargetDuration)
LogGeneral("--live-from: Starting from stream time '%s' and grabbing '%s' of content (and counting).", startTimeStr, totalTimeToGrabStr)
LogDebug("Starting from sequence %d [max sq right now is %d]", di.LiveFromSq, maxSq)
}
}
}

@@ -1124,7 +1143,7 @@ func (di *DownloadInfo) DownloadStream(dataType, dataFile string, progressChan c
// --live-from: Set start sequence.
curFrag = di.LiveFromSq
startFrag = curFrag
LogWarn("[--live-from] %s: Starting from sequence %d (latest is %d)", dataType, startFrag, di.LastSq)
LogDebug("[--live-from] %s: Starting from sequence %d (latest is %d)", dataType, startFrag, di.LastSq)
} else if curFrag > 0 {
// Stream that has been live for more than 5 days.
LogWarn("%s: YT only retains the livestream 5 days past for seeking, starting from sequence %d (latest is %d)", dataType, curFrag, di.LastSq)
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -39,6 +39,10 @@ Options:
--ipv6
Make all connections using IPv6.

--info-only
Print stream information such as Video title, Selected quality
Stream start time and duration and then exits.

--add-metadata
Write some basic metadata information to the final file.

@@ -268,14 +272,17 @@ Options:
--write-thumbnail
Write the thumbnail to a separate file.

--live-from [DURATION | TIMESTR] or 'now'
--live-from DURATION, TIMESTRING or NOW
Starts the download from the specified time in the future, the past or 'now'.
Use a negative value to skip back in time, positive value to wait to start.
Support time durations (e.g. 1d12h30m5s) or time strings (e.g. 12:30:05).
Examples: * '--live-from -01:10:00' will seek backwards 1 hour and 10 minutes and then
start downloading from that time.
* '--live-from 45m30s' will wait 45 minutes and 30 seconds from now and then
start downloading.
Use a negative time value to skip back in time from now.
Use a positive time value to specify the timestamp in the stream to start
capturing from (from the start of the stream).

Supports time durations (e.g. 1d8h30m5s) or time strings (e.g. 32:30:05).
Examples: * '--live-from -01:10:00' will seek backwards 1 hour and 10 minutes from now
and then start downloading from that time.
* '--live-from 1h10mm00s' will begin downloading from 1 hour 10 minutes
after the stream started.
* '--live-from now' will start recording from the current stream time.

Examples:
18 changes: 10 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
@@ -298,14 +298,17 @@ Options:
--write-thumbnail
Write the thumbnail to a separate file.

--live-from [DURATION | TIMESTR] or 'now'
--live-from DURATION, TIMESTRING or NOW
Starts the download from the specified time in the future, the past or 'now'.
Use a negative value to skip back in time, positive value to wait to start.
Support time durations (e.g. 1d12h30m5s) or time strings (e.g. 12:30:05).
Examples: * '--live-from -01:10:00' will seek backwards 1 hour and 10 minutes and then
start downloading from that time.
* '--live-from 45m30s' will wait 45 minutes and 30 seconds from now and then
start downloading.
Use a negative time value to skip back in time from now.
Use a positive time value to specify the timestamp in the stream to start
capturing from (from the start of the stream).

Supports time durations (e.g. 1d8h30m5s) or time strings (e.g. 32:30:05).
Examples: * '--live-from -01:10:00' will seek backwards 1 hour and 10 minutes from now
and then start downloading from that time.
* '--live-from 1h10mm00s' will begin downloading from 1 hour 10 minutes
after the stream started.
* '--live-from now' will start recording from the current stream time.

Examples:
@@ -676,7 +679,6 @@ func run() int {
if info.LiveFromVal != "" {
err = info.ParseLiveFromStrVal()
if err != nil {
LogError("--live-from: " + err.Error())
return 1
}
}
51 changes: 51 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -982,3 +982,54 @@ func GetFFmpegArgs(audioFile, videoFile, thumbnail, fileDir, fileName string, on
FileName: mergeFile,
}
}

func SecondsToDurationStr(seconds int) string {
days := seconds / (60 * 60 * 24)
seconds -= days * (60 * 60 * 24)

hours := seconds / (60 * 60)
seconds -= hours * (60 * 60)

minutes := seconds / 60
seconds -= minutes * 60

outputStr := ""
if days > 0 {
outputStr += fmt.Sprintf("%dd", days)
}
if hours > 0 {
outputStr += fmt.Sprintf("%dh", hours)
}
if minutes > 0 {
outputStr += fmt.Sprintf("%dm", minutes)
}
outputStr += fmt.Sprintf("%ds", seconds)

return outputStr
}

func SecondsToTimeStr(seconds int) string {
hours := seconds / (60 * 60)
seconds -= hours * (60 * 60)

minutes := seconds / 60
seconds -= minutes * 60

outputStr := ""
if hours > 0 {
outputStr += fmt.Sprintf("%0d:%02d:%02d", hours, minutes, seconds)
} else if minutes > 0 {
outputStr += fmt.Sprintf("%02d:%02d", minutes, seconds)
} else {
outputStr += fmt.Sprintf("%02d", seconds)
}

return outputStr
}

func SecondsToDurationAndTimeStr(seconds int) string {
durStr := SecondsToDurationStr(seconds)
timeStr := SecondsToTimeStr(seconds)

return durStr + " (" + timeStr + ")"
}