/// <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); }
/// <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 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); }