public static async Task Run( [QueueTrigger("%AzureStorageConversionQueueName%", Connection = "AzureWebJobsStorage")] string message, [SignalR(HubName = "broadcast")] IAsyncCollector <SignalRMessage> signalRMessages, ExecutionContext context, ILogger log) { var sw = new Stopwatch(); sw.Start(); log.LogInformation("Downloader triggered"); var payload = JsonConvert.DeserializeObject <QueueMessagePayload>(message); AppInsightsClient.SetOperation(context.InvocationId.ToString(), "downloader").SetSessionId(payload.ClientId); var video = payload.Video; video.DownloaderInvocationId = context.InvocationId; var cts = new CancellationTokenSource(); string videoTempPath = null; string audioTempPath = null; int? previousPercentage = null; long? cliDuration = null; bool Publish(string s) { if (Cancellation.Tokens.ContainsKey(video.DownloaderInvocationId)) { video.Error = "Download cancelled"; if (!cts.IsCancellationRequested) { cts.Cancel(); } return(false); } video.Message = s; signalRMessages.Publish("inprogress", video); //payload.ClientId return(true); } void ProgressNotifier(string s) { if (Cancellation.Tokens.ContainsKey(video.DownloaderInvocationId)) { cts.Cancel(); return; } var m = FfmpegStatus.Match(s); if (m.Success && TimeSpan.TryParse(m.Groups["time"].Value, CultureInfo.InvariantCulture, out var time)) { var p = (int)Math.Floor(time.TotalMilliseconds * 100 / video.Duration.TotalMilliseconds); if (previousPercentage == p) { return; } Publish($"{CONVERTING} ({p}%)"); previousPercentage = p; } } try { MediaStreamInfo streamInfo = null; if (Publish("Processing...")) { streamInfo = await ProcessVideo(video, log); } if (Publish("Downloading...")) { videoTempPath = await DownloadVideo(streamInfo, video, log, cts.Token); } if (Publish(CONVERTING)) { var result = await ConvertToAudio(video, videoTempPath, log, ProgressNotifier, cts.Token); cliDuration = result.Item1.RunTime.Ticks; audioTempPath = result.Item2; WriteId3Tag(video, audioTempPath, log); } if (Publish("Storing...")) { await UploadAudio(video, audioTempPath, log, cts.Token); } } catch (TaskCanceledException ex) { log.LogInformation("Download cancelled"); video.Error = ex.Message; AppInsightsClient.TrackException(ex, video.Properties); } catch (Exception ex) { log.LogError(ex, ex.Message.EscapeCurlyBraces()); video.Error = ex.Message; AppInsightsClient.TrackException(ex, video.Properties); } finally { Cancellation.Tokens.TryRemove(video.DownloaderInvocationId, out var value); cts.Dispose(); if (videoTempPath != null && File.Exists(videoTempPath)) { File.Delete(videoTempPath); } if (audioTempPath != null && File.Exists(audioTempPath)) { File.Delete(audioTempPath); } Publish(string.Empty); AppInsightsClient.TrackEvent("Download", video.Properties, new Dictionary <string, double> { { "Video duration", video.Duration.Ticks }, { "Function duration", sw.Elapsed.Ticks }, { "Conversion duration", cliDuration ?? 0 }, { "Cancelled", cts.IsCancellationRequested ? 1 : 0 } }); } }