private void CreateMediaJobs(MediaCreationQueue mediaQueue) { foreach (MediaCreationManagement mediaCreation in mediaQueue) { MediaJob job; job = new MediaJob(mediaCreation); job.Checked = __MediaObjectsState.SelectedMediaItems.Contains(mediaCreation); if (mediaCreation.ExecutionStatus.ExecutionStatus == ExecutionStatus.Creating && string.IsNullOrEmpty(mediaCreation.GetCreationPath( ))) { job.Comments = "Media creation was interrupted."; } __MediaJobs.Add(job); if (mediaCreation.ExecutionStatus.ExecutionStatus == ( ExecutionStatus )StatusComboBox.SelectedValue) { AddListViewItem(job); } } SetActiveMediaItem( ); }
public async Task <IActionResult> RemoveMedia([FromBody] MediaJob mediaJob, CancellationToken token) { if (mediaJob == null) { return(Ok(new { Success = false })); } await _mediatr.Send(new MediaPruneRequest(mediaJob), token); return(Ok(new { Success = true })); }
public JsonResult start(string[] assetIds, MediaJob mediaJob) { string directoryId = homeController.GetDirectoryId(this.Request); string authToken = homeController.GetAuthToken(this.Request, this.Response); MediaClient mediaClient = new MediaClient(authToken); MediaJobInput[] jobInputs = Workflow.GetJobInputs(mediaClient, assetIds); object jobOutput = Workflow.SubmitJob(directoryId, authToken, mediaClient, mediaJob, jobInputs); return(Json(jobOutput)); }
private void UpdateSelectedMediaItems(MediaJob job) { if (job.Checked && !__MediaObjectsState.SelectedMediaItems.Contains(job.MediaObject)) { __MediaObjectsState.SelectedMediaItems.Add(job.MediaObject); } else if (!job.Checked && __MediaObjectsState.SelectedMediaItems.Contains(job.MediaObject)) { __MediaObjectsState.SelectedMediaItems.Remove(job.MediaObject); } UpdateUIButtons( ); }
private static void SetCreatingMediaComment ( MediaCreationManagement mediaObject, MediaJob mediaJob ) { if (mediaObject.ExecutionStatus.ExecutionStatus == ExecutionStatus.Creating && string.IsNullOrEmpty(mediaObject.GetCreationPath( ))) { mediaJob.Comments = "Creating media..."; } else { mediaJob.Comments = string.Empty; } }
private void RefreshItem(ListViewItem item, MediaJob job) { MediaJobsListView.SuspendLayout( ); try { item.Text = job.MediaFileId; item.SubItems [Constants.ListViewColumns.PriorityIndex].Text = job.Priority.ToString( ); item.SubItems [Constants.ListViewColumns.NumberOfCopiesIndex].Text = job.NumberOfCopies.ToString( ); item.SubItems [Constants.ListViewColumns.CreationDateIndex].Text = job.CreationTime.ToLongDateString( ) + " " + job.CreationTime.ToLongTimeString( ); item.SubItems [Constants.ListViewColumns.CreationErrorIndex].Text = job.Comments; item.SubItems [Constants.ListViewColumns.MediaLocationIndex].Text = job.MediaLocation; } finally { MediaJobsListView.ResumeLayout(true); } }
private void AddListViewItem(MediaJob job) { ListViewItem item; item = new ListViewItem(job.MediaFileId); item.SubItems.Add(job.Priority.ToString( )); item.SubItems.Add(job.NumberOfCopies.ToString( )); item.SubItems.Add(job.CreationTime.ToLongDateString( ) + " " + job.CreationTime.ToLongTimeString( )); item.SubItems.Add(job.Comments).ForeColor = Color.Red; item.SubItems.Add(job.MediaLocation); item.Tag = job; item.Checked = job.Checked; item.UseItemStyleForSubItems = false; MediaJobsListView.Items.Add(item); UpdateSelectedMediaItems(job); }
public JsonResult start(MediaAssetInput[] inputAssets, MediaJob mediaJob) { string authToken = homeController.GetAuthToken(this.Request, this.Response); MediaClient mediaClient = new MediaClient(authToken); inputAssets = Workflow.MapInputAssets(mediaClient, inputAssets); Workflow.SetInputClips(mediaClient, inputAssets); using (DatabaseClient databaseClient = new DatabaseClient(false)) { foreach (MediaJobTask jobTask in mediaJob.Tasks) { if (!string.IsNullOrEmpty(jobTask.ProcessorDocumentId)) { JObject processorConfig = databaseClient.GetDocument(jobTask.ProcessorDocumentId); jobTask.ProcessorConfig = processorConfig.ToString(); } } } object result = Workflow.SubmitJob(authToken, mediaClient, null, inputAssets, mediaJob); return(Json(result)); }
public void OnMediaObjectAdded(MediaCreationManagement mediaObject) { try { MediaJob job; job = new MediaJob(mediaObject); SetCreatingMediaComment(mediaObject, job); __MediaJobs.Add(job); if (mediaObject.ExecutionStatus.ExecutionStatus == ( ExecutionStatus )StatusComboBox.SelectedValue) { AddListViewItem(job); } } catch (Exception exception) { Messager.ShowError(this, exception); } }
public JsonResult ingest(string storageAccount, bool storageEncryption, string inputAssetName, bool multipleFileAsset, string[] fileNames, MediaJob mediaJob) { string directoryId = homeController.GetDirectoryId(this.Request); string authToken = homeController.GetAuthToken(this.Request, this.Response); MediaClient mediaClient = new MediaClient(authToken); MediaJobInput[] jobInputs = Workflow.GetJobInputs(authToken, mediaClient, storageAccount, storageEncryption, inputAssetName, multipleFileAsset, fileNames); object jobOutput = Workflow.SubmitJob(directoryId, authToken, mediaClient, mediaJob, jobInputs); return(Json(jobOutput)); }
/// <summary> /// /// </summary> /// <param name="file"></param> /// <param name="progress"></param> /// <returns></returns> public async Task <MediaJob> Work(FileInfo file, ProgressWorker progress, bool thumb, bool icon) { MediaJob mediaJobData = new MediaJob(); // Load the image and get metadata using MagickImage image = new MagickImage(file.FullName); mediaJobData.File = file; mediaJobData.Updated = file.LastWriteTime; mediaJobData.Width = image.Width; mediaJobData.Height = image.Height; mediaJobData.Duration = 0; Dictionary <string, string> metaDic = metaDic = Media.GetMeta(file); mediaJobData.Title = metaDic.ContainsKey("Object Name") ? metaDic["Object Name"] : file.Name; mediaJobData.Caption = metaDic.ContainsKey("Caption/Abstract") ? metaDic["Caption/Abstract"] : mediaJobData.Caption; // Make the thumbnail if (thumb) { mediaJobData.Thumbnail = true; await Task.Run(() => { Media.Dimension imageCrop = null; if (Configuration.Get.UseFaceDetector) { // Check for faces Media.Dimension face = Media.GetFaceCrop(file, image.Width, image.Height); if (face != null) // If a face was detected, put it as the image crop { imageCrop = face; } } if (imageCrop == null) // If no image crop is defined, use the default crop { // Compute new dimensions imageCrop = Media.GetCroppedThumbnailDimensions(image.Width, image.Height, true); } // Crop image image.Crop(new MagickGeometry(imageCrop.CropLeft, imageCrop.CropTop, imageCrop.CropWidth, imageCrop.CropHeight)); // Resize image to thumbnail MagickGeometry thumbSize = new MagickGeometry(imageCrop.Width, imageCrop.Height); thumbSize.IgnoreAspectRatio = false; image.Resize(thumbSize); // Write the thumbnail to disk using (FileStream writeStream = file.GetAlternateDataStream(Media.GetThumbName(file), FileMode.Create).OpenWrite()) { image.Write(writeStream); writeStream.Close(); } progress.FinishedWork(Media.GetThumbName(file)); }); } if (icon) { mediaJobData.Icon = true; await Task.Run(() => { // Resize image to icon MagickGeometry iconSize = new MagickGeometry(Configuration.Get.Image.Icon.Width, Configuration.Get.Image.Icon.Height); iconSize.IgnoreAspectRatio = false; image.Resize(iconSize); // Write the icon to disk using (FileStream writeStream = file.GetAlternateDataStream(Media.GetIconName(file), FileMode.Create).OpenWrite()) { image.Write(writeStream); writeStream.Close(); } progress.FinishedWork(Media.GetIconName(file)); }); } return(mediaJobData); }
public async Task <Tuple <bool, MediaJob, MediaException> > EncodeMediaAsync( string title, string assetName, byte[] assetData, Action <string> progess, bool downloadAssets, CancellationToken token) { _logger.LogInformation($"EncodeMediaAsync: {assetName} with file: {assetData.Length}"); bool isSuccess = false; MediaJob result = null; MediaException exception = null; IAzureMediaServicesClient client = await GetClient(); // create unique name to prevent collisions with dup file names string uniqueness = Guid.NewGuid().ToString().Substring(0, 13); string jobName = $"job-{uniqueness}"; string locatorName = $"locator-{uniqueness}"; string inputAssetName = $"input-{assetName}{uniqueness}"; string outputAssetName = $"output-{assetName}{uniqueness}"; bool stopEndpoint = false; try { // Ensure that you have customized encoding Transform. This is really a one time setup operation. progess("Creating Transform..."); // var transform = await CreateCustomTransform(client, _settings.ResourceGroup, _settings.AccountName, CustomTransform); var transform = await CreateBuiltinTransform(client, _settings.ResourceGroup, _settings.AccountName, DefaultTransform); _logger.LogInformation($"Transform created...{transform.Description}"); // Create a new input Asset and upload the specified local video file into it. progess("Creating input Asset..."); var inputAsset = await CreateInputAssetAsync(client, _settings.ResourceGroup, _settings.AccountName, inputAssetName, assetName, assetData, token); _logger.LogInformation($"Input Asset created...{inputAsset.AssetId}"); // Output from the Job must be written to an Asset, so let's create one progess("Creating output Asset..."); var outputAsset = await CreateOutputAssetAsync(client, _settings.ResourceGroup, _settings.AccountName, outputAssetName, token); _logger.LogInformation($"Output Asset created...{outputAsset.AssetId}"); // create job progess("Creating and Submitting Job..."); var job = await SubmitJobAsync(client, _settings.ResourceGroup, _settings.AccountName, DefaultTransform, jobName, inputAsset.Name, outputAsset.Name, token); _logger.LogInformation($"Job created...{job.Name}"); DateTime startedTime = DateTime.Now; //TODO: event hub // Polling is not a recommended best practice for production applications because of the latency it introduces. // Overuse of this API may trigger throttling. Developers should instead use Event Grid and listen for the status events on the jobs _logger.LogInformation("Polling job status..."); progess("Polling job status..."); job = await WaitForJobToFinishAsync(client, _settings.ResourceGroup, _settings.AccountName, DefaultTransform, jobName, progess, token); TimeSpan elapsed = DateTime.Now - startedTime; if (job.State == JobState.Finished) { _logger.LogInformation($"Job finished: {elapsed}"); progess($"Job finished: {elapsed}"); var thumbnailList = await GenerateResults(client, _settings.ResourceGroup, _settings.AccountName, outputAsset.Name, OutputFolder, progess, downloadAssets, token); //Streaming progess("Creating Stream endpoints..."); var locator = await CreateStreamingLocatorAsync(client, _settings.ResourceGroup, _settings.AccountName, outputAssetName, locatorName, token); var streamingEndpoint = await client.StreamingEndpoints.GetAsync(_settings.ResourceGroup, _settings.AccountName, DefaultStreamingEndpointName, token); if (streamingEndpoint != null) { if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running) { _logger.LogInformation("Streaming Endpoint was Stopped, restarting now.."); progess("Streaming Endpoint was Stopped, restarting now.."); await client.StreamingEndpoints.StartAsync(_settings.ResourceGroup, _settings.AccountName, DefaultStreamingEndpointName, token); // Since we started the endpoint, we should stop it in cleanup. stopEndpoint = true; } } _logger.LogInformation("Getting the Streaming manifest URLs for HLS and DASH:"); progess("Getting the Streaming manifest URLs for HLS and DASH:"); var streamUrls = await GetStreamingUrlsAsync(client, _settings.ResourceGroup, _settings.AccountName, locator.Name, streamingEndpoint, token); _logger.LogInformation($"Retuning {streamUrls.Count} URLs"); isSuccess = true; result = new MediaJob(jobName, locatorName, inputAssetName, outputAssetName, stopEndpoint, streamUrls, thumbnailList.FirstOrDefault(), title); } else if (job.State == JobState.Error) { _logger.LogInformation($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}"); _logger.LogInformation($"ERROR: error details: {job.Outputs[0].Error.Details[0].Message}"); exception = new MediaException(job.Outputs[0].Error.Message); _logger.LogInformation("Cleaning up..."); await CleanUpAsync(client, _settings.ResourceGroup, _settings.AccountName, DefaultTransform, jobName, inputAssetName, outputAssetName, locatorName, stopEndpoint, DefaultStreamingEndpointName); } } catch (ApiErrorException e) { _logger.LogError(e, e.Message); exception = new MediaException(e.Message, e); _logger.LogInformation("Cleaning up..."); await CleanUpAsync(client, _settings.ResourceGroup, _settings.AccountName, DefaultTransform, jobName, inputAssetName, outputAssetName, locatorName, stopEndpoint, DefaultStreamingEndpointName); } return(new Tuple <bool, MediaJob, MediaException>(isSuccess, result, exception)); }
/// <summary> /// /// </summary> /// <param name="file"></param> /// <param name="progress"></param> /// <returns></returns> public async Task <MediaJob> Work(FileInfo file, ProgressWorker progress, bool thumb, bool icon) { MediaJob mediaJobData = new MediaJob(); Dictionary <string, string> metaData = new Dictionary <string, string>(); IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(file.FullName); IVideoStream videoStream = mediaInfo.VideoStreams.FirstOrDefault(); IVideoStream pictureStream = mediaInfo.VideoStreams.FirstOrDefault(); string thumbTempFile = Path.Combine(Configuration.Get.TempDir, Guid.NewGuid() + "_" + Media.GetThumbName(file)); string iconTempFile = Path.Combine(Configuration.Get.TempDir, Guid.NewGuid() + "_" + Media.GetIconName(file)); Func <string, string> iconTempFileFunc = (number) => { return("\"" + iconTempFile.Replace("\\", "\\\\") + "\""); }; // Fill out metadata Dictionary <string, string> metaDic = metaDic = Media.GetMeta(file); mediaJobData.Title = metaDic.ContainsKey("Object Name") ? metaDic["Object Name"] : file.Name; mediaJobData.Caption = metaDic.ContainsKey("Caption/Abstract") ? metaDic["Caption"] : mediaJobData.Caption; mediaJobData.File = file; mediaJobData.Updated = file.LastWriteTime; mediaJobData.Width = videoStream.Width; mediaJobData.Height = videoStream.Height; mediaJobData.Duration = videoStream.Duration.TotalSeconds; // Calculate the thumbnail clip start and length double clipStart = Configuration.Get.Video.ClipStart; double clipLength = Configuration.Get.Video.ClipLength; double duration = videoStream.Duration.TotalSeconds; if (clipStart + clipLength > duration) { clipStart = 0; // Requested clip exceeds video file's duration } if (clipStart + clipLength > duration) { clipLength = duration; // Extremely short video file } // Set up the video thumbnail conversion if (thumb) { Media.Dimension d = Media.GetCroppedThumbnailDimensions(videoStream.Width, videoStream.Height, false); mediaJobData.Thumbnail = true; IConversion thumbConversion = FFmpeg.Conversions.New() .AddStream(videoStream) .SetSeek(TimeSpan.FromSeconds(clipStart)) .SetOutputTime(TimeSpan.FromSeconds(clipLength)) .SetOutputFormat(Format.mp4) .SetVideoBitrate(60000) .AddParameter("-s " + ((int)d.Width).ToString() + "x" + ((int)d.Height).ToString()) .AddParameter("-vcodec libx264") .AddParameter("-pix_fmt yuv420p") .SetOutput(thumbTempFile); // Do the thumbnail conversion _ = thumbConversion.Start().ContinueWith((result) => { using (FileStream input = File.OpenRead(thumbTempFile)) using (FileStream output = file.GetAlternateDataStream(Media.GetThumbName(file), FileMode.Create).OpenWrite()) { int bufferLength = 1024; byte[] buffer = new byte[bufferLength]; int bytesRead = 0; do { bytesRead = input.Read(buffer, 0, bufferLength); output.Write(buffer, 0, bytesRead); }while (bytesRead != 0); } File.Delete(thumbTempFile); progress.FinishedWork(Media.GetThumbPath(file)); }); } if (icon) { mediaJobData.Icon = true; // Set up the icon conversion pictureStream.SetCodec(VideoCodec.png); IConversion iconConversion = FFmpeg.Conversions.New() .AddStream(pictureStream) .ExtractNthFrame(1, iconTempFileFunc) .AddParameter("-s " + (Configuration.Get.Image.Icon.Width).ToString() + "x" + ((int)(((double)Configuration.Get.Image.Icon.Width / (double)videoStream.Width) * (double)videoStream.Height)).ToString()); // Do the icon conversion _ = iconConversion.Start().ContinueWith((result) => { using (FileStream input = File.OpenRead(iconTempFile)) using (FileStream output = file.GetAlternateDataStream(Media.GetIconName(file), FileMode.Create).OpenWrite()) { int bufferLength = 1024; byte[] buffer = new byte[bufferLength]; int bytesRead = 0; do { bytesRead = input.Read(buffer, 0, bufferLength); output.Write(buffer, 0, bytesRead); }while (bytesRead != 0); } File.Delete(iconTempFile); progress.FinishedWork(Media.GetIconPath(file)); }); } // Done return(mediaJobData); }
public JsonResult upload(string[] fileNames, string storageAccount, bool storageEncryption, string inputAssetName, bool multipleFileAsset, bool publishInputAsset, MediaAssetInput[] inputAssets, MediaJob mediaJob) { string authToken = homeController.GetAuthToken(this.Request, this.Response); MediaClient mediaClient = new MediaClient(authToken); inputAssets = Workflow.CreateInputAssets(authToken, mediaClient, storageAccount, storageEncryption, inputAssetName, multipleFileAsset, publishInputAsset, fileNames); object result = Workflow.SubmitJob(authToken, mediaClient, storageAccount, inputAssets, mediaJob); return(Json(result)); }
public SortedDictionary <string, DirItem> Process(bool overwrite) { bool dirty = false; ProgressWorker progress = new ProgressWorker(workDirectory); List <Task <MediaJob> > mediaProcessingTasks = new List <Task <MediaJob> >(); SortedDictionary <string, DirItem> mediaList = new SortedDictionary <string, DirItem>(); // New metadata SortedDictionary <string, DirItem> mediaListOld = new SortedDictionary <string, DirItem>(); // Old cached metadata if (progress.GetProgress() > 0 && progress.GetProgress() < 100) { return(null); } progress.AddWork(workDirectory.FullName); // Retrieve existing media list if any if (File.Exists(Path.Combine(workDirectory.FullName, Configuration.Get.MetaFile))) { mediaListOld = JsonSerializer.Deserialize <SortedDictionary <string, DirItem> >(File.ReadAllText(Path.Combine(workDirectory.FullName, Configuration.Get.MetaFile))); } // List directories in the dir foreach (DirectoryInfo dir in workDirectory.GetDirectories()) { // If the dir starts with a $ then we ignore it if (dir.Name.StartsWith('$')) { continue; } // Try to identify the icon for a directory. It's basically whatever the first media file found is. FileInfo dirIcon = FindFirst(dir); if (dirIcon == null) { continue; } string dirName = dirIcon.FullName.Substring(Configuration.Get.GalleryPath.Length + 1).Replace('\\', '/'); if (!mediaListOld.ContainsKey(dirName) || // If the old media list doesn't contain this dir (because it's new), or overwrite) // We're instructed to re-generate all data { // Update the information about this dir String ext = dirIcon.Extension.ToLower().Trim('.'); // An image if (Configuration.Get.Image.Ext.Contains(ext)) { progress.AddWork(Media.GetIconPath(dirIcon)); ImageWorker worker = new ImageWorker(); mediaProcessingTasks.Add(worker.Work(dirIcon, progress, false, true)); } // A video if (Configuration.Get.Video.Ext.Contains(ext)) { progress.AddWork(Media.GetIconPath(dirIcon)); VideoWorker worker = new VideoWorker(); mediaProcessingTasks.Add(worker.Work(dirIcon, progress, false, true)); } mediaList.Add(dirName, new DirItem { Title = dir.Name, Type = "dir", Updated = DateTime.Now }); } else { // No updates, simply copy the old data to the new mediaList.Add(dirName, mediaListOld[dirName]); } } // List files in the dir foreach (FileInfo file in workDirectory.GetFiles()) { if (!mediaListOld.ContainsKey(file.Name) || // If the old media list doesn't contain this file (because it's new), or overwrite) // We're instructed to re-generate all data { // Update information about this file String ext = file.Extension.ToLower().Trim('.'); // An image if (Configuration.Get.Image.Ext.Contains(ext)) { progress.AddWork(Media.GetThumbPath(file)); ImageWorker worker = new ImageWorker(); mediaProcessingTasks.Add(worker.Work(file, progress, true, false)); } // A video if (Configuration.Get.Video.Ext.Contains(ext)) { progress.AddWork(Media.GetThumbPath(file)); VideoWorker worker = new VideoWorker(); mediaProcessingTasks.Add(worker.Work(file, progress, true, false)); } } else { // No updates, simply copy the old data to the new mediaList.Add(file.Name, mediaListOld[file.Name]); } } // Async job processing try { // For every media processing task initiated above, wait for all of them to complete. They spin off their own // encoding jobs that are tracked via ProgressWorker, but should return media metadata relatively quickly Task.WaitAll(mediaProcessingTasks.ToArray()); foreach (Task <MediaJob> task in mediaProcessingTasks) { dirty = true; MediaJob mediaJobData = task.Result; string ext = mediaJobData.File.Extension.ToLower().Trim('.'); string path = ""; string type = ""; if (!mediaJobData.Icon) { path = mediaJobData.File.Name; if (Configuration.Get.Image.Ext.Contains(ext)) { type = "image"; } else if (Configuration.Get.Video.Ext.Contains(ext)) { type = "video"; } else { type = "unknown"; } mediaList.Add(path, new DirItem { Title = mediaJobData.Title, Type = type, Updated = DateTime.Now }); } using (FileStream fs = mediaJobData.File.GetAlternateDataStream(Configuration.Get.MetaFile, FileMode.OpenOrCreate).OpenWrite()) { fs.SetLength(0); fs.Flush(); // delete the contents of the file first byte[] bytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new FileMeta() { Caption = mediaJobData.Caption })); fs.Write(bytes, 0, bytes.Length); } } } catch (AggregateException e) { // Error handling: Log all errors generated by the workers StringBuilder sb = new StringBuilder(); for (int j = 0; j < e.InnerExceptions.Count; j++) { progress.FinishedWork("Error"); sb.AppendLine("\n-------------------------------------------------\n" + e.InnerExceptions[j].ToString()); } LogManager.GetLogger("DirectoryWorker").Error(sb.ToString()); } // Save our dir list to a file if (dirty) { try { File.WriteAllText(Path.Combine(workDirectory.FullName, Configuration.Get.MetaFile), JsonSerializer.Serialize(mediaList)); } catch (IOException e) { LogManager.GetLogger("DirectoryWorker").Error(e.ToString()); } } progress.FinishedWork(workDirectory.FullName); return(mediaList); }
private ListViewItem FindMediaItem(MediaJob mediaJob) { return(MediaJobsListView.Items.OfType <ListViewItem> ( ).Where(n => (n.Tag == mediaJob)).FirstOrDefault( )); }