azihassan / youtube-d

A fast command-line Youtube downloader
MIT License
8 stars 0 forks source link

Add timestamp support #4

Open azihassan opened 1 year ago

azihassan commented 1 year ago

Some youtube links have a t parameter that makes the player jump to a given timestamp in the video. Not sure how this is implemented, but I'm guessing it might have something to do with the range parameter. It's worth looking into it.

azihassan commented 1 year ago

I compared a video URL at different timestamps, but only the range, rn and lsig fields changed. mt refers to the current timestamp, but I think range is the only relevant piece :

image

azihassan commented 1 year ago

The total video length in bytes is 38076857. It's 722 seconds long. Using that information, I tried to deduce the range for timestamp 5:25 (325 seconds) with the 38076857 * 325 / 722 formula, but it resulted in a different range (17139859.452908587)

azihassan commented 1 year ago

I added a break point to the mouse click and XHR events and eventually ended up in this part of the code :

  g.k.zB = function (a, b, c) {
    for (var d = [], e = a; e <= this.index.wd(); e++) {
      var f = GE(this.index.Vp(e), fva(this.index, e)),
        h = this.index.getStartTime(e),
        l = this.index.getDuration(e),
        m = Math.max(0, b - f.start),
        n = Math.min(f.end + 1, b + c) - (f.start + m);
      d.push(new IE(3, this, f, "getRequestInfoForRange", e, h, l, m, n));
      if (f.start + m + n >= b + c) break;
    }
    d.length || g.lB(new g.qx("b189619593", "" + a, "" + b, "" + c));
    return new RE(d);
  };

It looks like this is where offsets are calculated. Here's a screenshot of the debugger after I clicked on the 7:51 minute mark. I found a video URL in the network tab where the start field was mentioned in the range query parameter, but I forgot to copy it :

image

azihassan commented 1 year ago

Definition of the IE object :

  IE = function (a, b, c, d, e, f, h, l, m, n) {
    d = void 0 === d ? "" : d;
    this.type = a;
    this.j = b;
    this.range = c;
    this.source = d;
    this.J = [];
    this.D = "";
    this.Oa = -1;
    this.D = d;
    this.Oa = 0 <= e ? e : -1;
    this.startTime = f || 0;
    this.duration = h || 0;
    this.Qb = l || 0;
    this.u = 0 <= m ? m : this.range ? this.range.length : NaN;
    this.sf = this.range
      ? this.Qb + this.u === this.range.length
      : void 0 === n
      ? !!this.u
      : n;
    this.range
      ? ((this.B =
          this.startTime + (this.duration * this.Qb) / this.range.length),
        (this.I = (this.duration * this.u) / this.range.length),
        (this.C = this.B + this.I))
      : xua(this);
  };
azihassan commented 1 year ago

The total video length in bytes is 38076857. It's 722 seconds long. Using that information, I tried to deduce the range for timestamp 5:25 (325 seconds) with the 38076857 * 325 / 722 formula, but it resulted in a different range (17139859.452908587)

This doesn't take into account the bitrate. A more accurate range can be found by multiplying the bitrate and time offset :

range = bitrate * t * 1024 / 8

The bitrate is available in the formats and adaptiveFormats arrays. Not sure if it's a constant bitrate or if it's the average of the variable bitrates though.