public ThrottledBufferedStream(Stream baseStream, long maximumBytesPerSecond, Action <YoutubeUploadStats> updateUploadProgress, YoutubeUploadStats stats, Upload upload) { if (ThrottledBufferedStream.historyForUploadInSeconds > ThrottledBufferedStream.keepHistoryForInSeconds) { throw new InvalidOperationException("History not long enough for upload throttling."); } if (ThrottledBufferedStream.historyForStatsInSeconds > ThrottledBufferedStream.keepHistoryForInSeconds) { throw new InvalidOperationException("History not long enough for stats."); } if (baseStream == null) { throw new ArgumentNullException("baseStream"); } if (maximumBytesPerSecond < 0) { throw new ArgumentOutOfRangeException("maximumBytesPerSecond", maximumBytesPerSecond, "The maximum number of bytes per second can't be negatie."); } this.updateUploadProgress = updateUploadProgress; this.stats = stats; this.upload = upload; this.lastStatsUpdate = DateTime.Now; this.baseStream = baseStream; this.maximumBytesPerSecondRead = maximumBytesPerSecond; this.position = this.baseStream.Position; }
public static async Task <UploadResult> Upload(Upload upload, long maxUploadInBytesPerSecond, Action <YoutubeUploadStats> updateUploadProgress, CancellationToken cancellationToken) { Tracer.Write($"YoutubeUploadService.Upload: Start with upload: {upload.FilePath}, maxUploadInBytesPerSecond: {maxUploadInBytesPerSecond}."); upload.UploadErrorMessage = string.Empty; UploadResult uploadResult = new UploadResult() { VideoResult = VideoResult.Failed, ThumbnailSuccessFull = false, PlaylistSuccessFull = false }; if (!File.Exists(upload.FilePath)) { Tracer.Write($"YoutubeUploadService.Upload: End, file doesn't exist."); upload.UploadErrorMessage = "File does not exist."; upload.UploadStatus = UplStatus.Failed; return(uploadResult); } StringBuilder errors = new StringBuilder(); try { Tracer.Write($"YoutubeUploadService.Upload: Initialize upload."); long uploadByteIndex = await YoutubeUploadService.initializeUpload(upload); JsonSerializationContent.JsonSerializer.SerializeAllUploads(); Tracer.Write($"YoutubeUploadService.Upload: Initial uploadByteIndex: {uploadByteIndex}."); upload.BytesSent = uploadByteIndex; long initialBytesSent = uploadByteIndex; YoutubeUploadStats stats = new YoutubeUploadStats(); using (FileStream fileStream = new FileStream(upload.FilePath, FileMode.Open)) using (ThrottledBufferedStream inputStream = new ThrottledBufferedStream(fileStream, maxUploadInBytesPerSecond, updateUploadProgress, stats, upload)) { inputStream.Position = uploadByteIndex; YoutubeUploadService.stream = inputStream; long totalBytesSentInSession = 0; long fileLength = upload.FileLength; HttpClient client = await HttpHelper.GetAuthenticatedUploadClient(); //on IOExceptions try 2 times more to upload the chunk. //no response from the server shall be requested on IOException. short uploadTry = 1; int package = 0; bool error = false; Tracer.Write($"YoutubeUploadService.Upload: fileLength: {fileLength}."); PartStream chunkStream; HttpResponseMessage message; StreamContent content; while (fileLength > totalBytesSentInSession + initialBytesSent) { if (error) { error = false; if (uploadTry > 3) { Tracer.Write($"YoutubeUploadService.Upload: End, Upload not successful after 3 tries for package {package}."); upload.UploadErrorMessage = $"YoutubeUploadService.Upload: Upload not successful after 3 tries for package {package}. Errors: {errors.ToString()}"; upload.UploadStatus = UplStatus.Failed; return(uploadResult); } //give a little time on IOException, e.g. to await router redial in on 24h disconnect await Task.Delay(TimeSpan.FromSeconds(2)); Tracer.Write($"YoutubeUploadService.Upload: Getting range due to upload retry."); uploadByteIndex = await YoutubeUploadService.getUploadByteIndex(upload); Tracer.Write($"YoutubeUploadService.Upload: Upload retry uploadByteIndex: {uploadByteIndex}."); inputStream.Position = uploadByteIndex; upload.BytesSent = uploadByteIndex; totalBytesSentInSession = uploadByteIndex - initialBytesSent; } else { package++; Tracer.Write($"YoutubeUploadService.Upload: Upload try: Package {package} Try {uploadTry}."); } chunkStream = new PartStream(inputStream, YoutubeUploadService.uploadChunkSizeInBytes); Tracer.Write($"YoutubeUploadService.Upload: Creating content."); using (content = HttpHelper.GetStreamContentResumableUpload(chunkStream, inputStream.Length, uploadByteIndex, YoutubeUploadService.uploadChunkSizeInBytes, MimeTypesMap.GetMimeType(upload.FilePath))) { try { Tracer.Write($"YoutubeUploadService.Upload: Start uploading."); message = await client.PutAsync(upload.ResumableSessionUri, content, cancellationToken); } catch (TaskCanceledException) { Tracer.Write($"YoutubeUploadService.Upload: End, Upload stopped by user."); upload.UploadStatus = UplStatus.Stopped; uploadResult.VideoResult = VideoResult.Stopped; return(uploadResult); } catch (Exception e) { Tracer.Write($"YoutubeUploadService.Upload: HttpClient.PutAsync Exception package {package} try {uploadTry}: {e.ToString()}."); errors.AppendLine($"YoutubeUploadService.Upload: HttpClient.PutAsync Exception package {package} try {uploadTry}: {e.GetType().ToString()}: {e.Message}."); error = true; uploadTry++; continue; } } using (message) { if ((int)message.StatusCode == 308) { //one upload package succeeded, reset upload counter. uploadTry = 1; Tracer.Write($"YoutubeUploadService.Upload: Package {package} finished."); } else { if (!message.IsSuccessStatusCode) { Tracer.Write($"YoutubeUploadService.Upload: HttpResponseMessage unexpected status code package {package} try {uploadTry}: {message.StatusCode} with message {message.ReasonPhrase}."); errors.AppendLine($"YoutubeUploadService.Upload: HttpResponseMessage unexpected status code package {package} try {uploadTry}: {message.StatusCode} with message {message.ReasonPhrase}."); error = true; uploadTry++; continue; } var definition = new { Id = "" }; var response = JsonConvert.DeserializeAnonymousType(await message.Content.ReadAsStringAsync(), definition); upload.VideoId = response.Id; //last stats update to reach 0 bytes and time left. stats.CurrentSpeedInBytesPerSecond = 1; upload.BytesSent = inputStream.Position; updateUploadProgress(stats); upload.UploadStatus = UplStatus.Finished; uploadResult.VideoResult = VideoResult.Finished; Tracer.Write($"YoutubeUploadService.Upload: Upload finished with {package}, video id {upload.VideoId}."); } } uploadByteIndex += YoutubeUploadService.uploadChunkSizeInBytes; totalBytesSentInSession += YoutubeUploadService.uploadChunkSizeInBytes; } } } catch (Exception e) { Tracer.Write($"YoutubeUploadService.Upload: End, Unexpected Exception: {e.ToString()}."); upload.UploadErrorMessage += $"YoutubeUploadService.Upload: Unexpected Exception: : {e.GetType().ToString()}: {e.Message}."; upload.UploadStatus = UplStatus.Failed; return(uploadResult); } uploadResult.ThumbnailSuccessFull = await YoutubeThumbnailService.AddThumbnail(upload); uploadResult.PlaylistSuccessFull = await YoutubePlaylistService.AddToPlaylist(upload); Tracer.Write($"YoutubeUploadService.Upload: End."); return(uploadResult); }