/// <summary> /// Gets the stream identified by the specified metadata. /// </summary> public async ValueTask <Stream> GetAsync( IStreamInfo streamInfo, CancellationToken cancellationToken = default) { // For most streams, YouTube limits transfer speed to match the video playback rate. // This helps them avoid unnecessary bandwidth, but for us it's a hindrance because // we want to download the stream as fast as possible. // To solve this, we divide the logical stream up into multiple segments and download // them all separately. var isThrottled = !Regex.IsMatch(streamInfo.Url, "ratebypass[=/]yes"); var segmentSize = isThrottled ? 9_898_989 // breakpoint after which the throttling kicks in : (long?)null; // no segmentation for non-throttled streams var stream = new SegmentedHttpStream( _httpClient, streamInfo.Url, streamInfo.Size.Bytes, segmentSize ); // Pre-resolve inner stream eagerly await stream.PreloadAsync(cancellationToken); return(stream); }
private async Task <YoutubeMediaPacket> _fillGab(YoutubeRequestURL packetRequestURL, Range lostRange, YoutubeMediaPacketType type) { packetRequestURL.RequestPath.QueryString.SetValue("range", $"{lostRange.Start}-{lostRange.End}"); var requestableURL = packetRequestURL.ToRequestableURL(); // debugging YTrackLogger.Log("\nfilling broken range for type " + type + " : " + lostRange.ToString() + " from : " + requestableURL); long httpSegmentSize = packetRequestURL.RequestPath.QueryString.HasValue("ratebypass") && packetRequestURL.RequestPath.QueryString.GetValue("ratebypass") == "yes" ? (long)lostRange.Length : 9_898_989; YoutubeMediaPacket packet; using (SegmentedHttpStream segmentedHttpStream = new SegmentedHttpStream(_client, requestableURL, (long)lostRange.Length, httpSegmentSize)) { try { string tmpFileName = Path.GetTempFileName(); using (Stream outputStream = new FileStream(tmpFileName, FileMode.Append)) { IProgress <double> progressPercentage = new Progress <double>(b => packetDownloadProgressChanged(b)); await segmentedHttpStream.CopyToStreamAsync(outputStream, progressPercentage); } // no need to check whether the packetRequestURL is a valid requist URL // because we requested it anyway packet = new YoutubeMediaPacket(type, packetRequestURL, tmpFileName); OnPacketDownloadCompleted?.Invoke(this, packet); } catch (Exception e) { // two exception may be thrown // WebException if something went wrong when fetch the new packets // IOException if something went wrong when writing the packet file to disk // I'll raise PacketDownloadExceptionThrown and re-throw exception if (e is WebException) { _onGabPacketDownloadExceptionThrown?.Invoke(this, new PacketDownloadExceptionEventArgumanets() { Exception = e as WebException, FailedPacketRequestURL = packetRequestURL, Range = lostRange }); } // re-throw the error to the caller Helpers.YTrackLogger.Log("Failed to download Packet : " + e.Message + "\n\n" + e.StackTrace); throw; } } return(packet); }