public async Task StartScan(List<VideoListItem> selection, CancellationToken cancel) { await selection.ForEachAsync(5, cancel, async item => { MediaInfoReader InfoReader = new MediaInfoReader(); Media VideoData = PlayerAccess.GetVideoById(item.MediaId.Value); if (VideoData != null) { try { // Query the server for media info. SetStatus(item, VideoListItemStatusEnum.DownloadingInfo); var VTask = DownloadBusiness.GetDownloadUrlsAsync(VideoData.DownloadUrl); var VideoList = await VTask; if (VideoList != null) { // Get the highest resolution format. BestFormatInfo VideoFormat = DownloadBusiness.SelectBestFormat(VideoList); if (VideoFormat == null || VideoFormat.BestVideo == null) SetStatus(item, VideoListItemStatusEnum.Failed); else { SetStatus(item, await IsHigherQualityAvailable(Settings.NaturalGroundingFolder + VideoData.FileName, VideoFormat)); } if (VideoFormat != null && !string.IsNullOrEmpty(VideoFormat.StatusText)) SetStatus(item, item.Status, item.StatusText + string.Format(" ({0})", VideoFormat.StatusText)); } else SetStatus(item, VideoListItemStatusEnum.InvalidUrl); } catch { SetStatus(item, VideoListItemStatusEnum.Failed); } } }); }
/// <summary> /// Creates an AviSynth script that will auto-pitch the audio to 432hz. You then open this script file in the video player instead of directly opening the file. /// </summary> /// <param name="inputFile">The video to play.</param> /// <param name="infoReader">An object to read media information.</param> public static void CreateScript(string inputFile, MediaInfoReader infoReader) { bool AviSynthPlus = MpcConfigBusiness.GetAviSynthVersion() == AviSynthVersion.AviSynthPlus; AviSynthScriptBuilder Script = new AviSynthScriptBuilder(); Script.AddPluginPath(); if (AviSynthPlus) { //Script.AppendLine(@"SetFilterMTMode(""DEFAULT_MT_MODE"",2)"); //Script.AppendLine(@"SetFilterMTMode(""LWLibavVideoSource"",3)"); //Script.AppendLine(@"SetFilterMTMode(""LWLibavAudioSource"",3)"); Script.OpenDirect(inputFile, Settings.AutoPitchCache, !string.IsNullOrEmpty(infoReader.AudioFormat), 0); Script.AppendLine("Preroll(int(FrameRate*3))"); // This causes a slight audio delay in AviSynth 2.6 Script.LoadPluginDll("TimeStretch.dll"); Script.AppendLine("ResampleAudio(48000)"); Script.AppendLine("TimeStretchPlugin(pitch = 100.0 * 0.98181819915771484)"); //Script.AppendLine("Prefetch({0})", CPU); } else { int CPU = Environment.ProcessorCount / 2; Script.AppendLine("SetMTMode(3,{0})", CPU); Script.OpenDirect(inputFile, Settings.AutoPitchCache, !string.IsNullOrEmpty(infoReader.AudioFormat), CPU); Script.AppendLine("SetMTMode(2)"); Script.AppendLine("Preroll(int(FrameRate*3))"); //Script.AppendLine("Loop(int(FrameRate/2), 0, 0)"); //Script.LoadPluginAvsi("UUSize4.avsi"); //Script.AppendLine("UUSize4(mod=4)"); // This slightly slows down playback speed but audio stays in sync Script.AppendLine("V = AssumeFPS(432.0 / 440.0 * FrameRate)"); Script.AppendLine("A = AssumeSampleRate(int(432.0 / 440.0 * AudioRate))"); Script.AppendLine("AudioDub(V, A)"); } Script.Cleanup(); Script.WriteToFile(Settings.AutoPitchFile); File.Delete(Settings.AutoPitchCache); }
/// <summary> /// Applies 432hz auto-pitch if file PixelAspectRatio is 1 and FPS can be read. /// </summary> /// <param name="video">The video for which to create the auto-pitch script file.</param> /// <returns>Whether auto-pitch is enabled for this video.</returns> public static bool AppyAutoPitch(Media video) { using (MediaInfoReader InfoReader = new MediaInfoReader()) { InfoReader.LoadInfo(Settings.NaturalGroundingFolder + video.FileName); if (Settings.SavedFile.ChangeAudioPitch && InfoReader.PixelAspectRatio == 1 && !video.DisablePitch && (InfoReader.BitDepth ?? 8) == 8) { CreateScript(Settings.NaturalGroundingFolder + video.FileName, InfoReader); return true; } else return false; } }
/// <summary> /// Returns whether the local file should be replaced by the YouTube version. /// </summary> /// <param name="localFile">A path to the local file.</param> /// <param name="serverFile">The information of the available server file.</param> /// <returns>True if the local file should be replaced.</returns> public async Task<VideoListItemStatusEnum> IsHigherQualityAvailable(string localFile, BestFormatInfo serverFile) { // If there is no local file, it should be downloaded. if (!File.Exists(localFile)) return VideoListItemStatusEnum.HigherQualityAvailable; // If local file is FLV and there's another format available, it should be downloaded. string LocalFileExt = Path.GetExtension(localFile).ToLower(); if (LocalFileExt == ".flv" && serverFile.BestVideo.VideoType != VideoType.Flash) return VideoListItemStatusEnum.HigherQualityAvailable; // Original VCD files and files of unrecognized extensions should not be replaced. MediaInfoReader InfoReader = new MediaInfoReader(); await InfoReader.LoadInfoAsync(localFile); if (!DownloadBusiness.DownloadedExtensions.Contains(LocalFileExt) || InfoReader.VideoFormat == "MPEG Video") { serverFile.StatusText = "Not from YouTube"; return VideoListItemStatusEnum.OK; } // For server file size, estimate 10% extra for audio. Estimate 35% advantage for VP9 format. non-DASH WebM is VP8 and doesn't have that bonus. long ServerFileSize = (long)(serverFile.BestVideo.FileSize * 1.1); if (serverFile.BestVideo.VideoType == VideoType.WebM && serverFile.BestVideo.AdaptiveType == AdaptiveType.Video) ServerFileSize = (long)(ServerFileSize * 1.35); long LocalFileSize = new FileInfo(localFile).Length; if (InfoReader.VideoFormat == "VP9") LocalFileSize = (long)(LocalFileSize * 1.35); // If server resolution is better, download unless local file is bigger. int LocalFileHeight = InfoReader.Height ?? 0; if (serverFile.BestVideo.Resolution > LocalFileHeight) { if (ServerFileSize > LocalFileSize) return VideoListItemStatusEnum.HigherQualityAvailable; else if (ServerFileSize != 0) { // non-DASH videos have no file size specified, and we won't replace local video with non-DASH video. serverFile.StatusText = "Local file larger"; return VideoListItemStatusEnum.OK; } } // Is estimated server file size is at least 15% larger than local file (for same resolution), download. if (ServerFileSize > LocalFileSize * 1.15) return VideoListItemStatusEnum.HigherQualityAvailable; // download audio and merge with local video. (that didn't work, ffmpeg failed to merge back) int? LocalAudioBitRate = InfoReader.AudioBitRate; int ServerAudioBitRate = serverFile.BestAudio != null ? serverFile.BestAudio.AudioBitrate : serverFile.BestVideo.AudioBitrate; // Fix a bug where MediaInfo returns no bitrate for MKV containers with AAC audio. if (LocalAudioBitRate != null || LocalFileExt != ".mkv") { if ((LocalAudioBitRate == null || LocalAudioBitRate < ServerAudioBitRate * .8) && serverFile.BestVideo.Resolution == LocalFileHeight) { // Only redownload for audio if video file size is similar. Videos with AdaptiveType=None don't have file size. if (ServerFileSize > LocalFileSize * .9 && serverFile.BestVideo.AdaptiveType == AdaptiveType.Video) { serverFile.StatusText = "Audio"; return VideoListItemStatusEnum.HigherQualityAvailable; } else { serverFile.StatusText = ""; return VideoListItemStatusEnum.BetterAudioAvailable; } } } return VideoListItemStatusEnum.OK; }
/// <summary> /// Loops through the Media table to set the FileName, Length, Width and Height fields. /// </summary> /// <param name="progress">Reports the progress of the operation. First report is the amount of files to process, then subsequent reports represent the quantity done.</param> /// <returns>Whether some data was modified.</returns> public async Task<bool> LoadMediaInfoAsync(IProgress<int> progress) { // Calling this method when it is already running allows listening to the progress. if (progress != null) { loadingMediaInfoProgress = progress; // First progress report is total count. if (isLoadingMediaInfo) loadingMediaInfoProgress.Report(loadingMediaInfoCount); } if (isLoadingMediaInfo) return false; isLoadingMediaInfo = true; bool HasChanges = false; using (Entities context = new Entities()) { // Loop through all media items with missing Length, Width or Height. var Query = (from v in context.Media where v.FileName == null || v.Length == null || ((v.MediaTypeId == (int)MediaType.Video || v.MediaTypeId == (int)MediaType.Image) && v.Height == null) select v); // First progress report contains the total count. Subsequent reports contain the quantity completed. loadingMediaInfoCount = Query.Count(); if (loadingMediaInfoProgress != null) loadingMediaInfoProgress.Report(loadingMediaInfoCount); int ItemsCompleted = 0; string DefaultFileName; string FilePath; DefaultMediaPath PathCalc = new DefaultMediaPath(); PathCalc.LoadData(); MediaInfoReader MediaInfo = new MediaInfoReader(); foreach (Media item in Query) { // Try to auto-attach file if default file name exists. if (item.FileName == null) { DefaultFileName = PathCalc.GetDefaultFileName(item.Artist, item.Title, item.MediaCategoryId, item.MediaType); foreach (string ext in Settings.GetMediaTypeExtensions(item.MediaType)) { FilePath = Settings.NaturalGroundingFolder + DefaultFileName + ext; if (File.Exists(FilePath)) { item.FileName = DefaultFileName + ext; HasChanges = true; await Task.Delay(1); break; } } } // Load media file to set Length, Width and Height. if (item.FileName != null && await MediaInfo.LoadInfoAsync(item)) HasChanges = true; // Send update with the quantity of files completed. if (loadingMediaInfoProgress != null) loadingMediaInfoProgress.Report(++ItemsCompleted); } MediaInfo.Dispose(); if (HasChanges) context.SaveChanges(); } isLoadingMediaInfo = false; loadingMediaInfoCount = 0; loadingMediaInfoProgress = null; return HasChanges; }
private async Task DownloadCompletedAsync(DownloadItem downloadInfo) { // Separate file extension. string Destination = downloadInfo.Files[0].Destination; string DestinationExt = Path.GetExtension(Destination); Destination = Destination.Substring(0, Destination.Length - Path.GetExtension(Destination).Length); Media video = downloadInfo.Request; if (downloadInfo.Files.Count > 1) { VideoType File1Type = downloadInfo.Files[0].Source.VideoType; AudioType File2Type = downloadInfo.Files[1].Source.AudioType; string File1Ext = GetCodecExtension(File1Type); string File2Ext = GetAudioExtension(File2Type); DestinationExt = GetFinalExtension(File1Type, File2Type); // Make sure output file doesn't already exist. File.Delete(Destination + DestinationExt); // Merge audio and video files. await Task.Run(() => FfmpegBusiness.JoinAudioVideo(Destination + File1Ext, Destination + File2Ext, Destination + DestinationExt, true)); // Delete source files File.Delete(Destination + File1Ext); File.Delete(Destination + File2Ext); } else if (downloadInfo.UpgradeAudio) { // Get original video format. MediaInfoReader MediaReader = new MediaInfoReader(); //await MediaReader.LoadInfoAsync(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); string VideoDestExt = ".mkv"; //if (MediaReader.VideoFormat == "VP8" || MediaReader.VideoFormat == "VP9") // VideoDestExt = ".webm"; string VideoDest = downloadInfo.Destination + " (extract)" + VideoDestExt; // Keep existing video and upgrade audio. string AudioExt = GetAudioExtension(downloadInfo.Files[0].Source.AudioType); DestinationExt = GetFinalExtension(Path.GetExtension(downloadInfo.Request.FileName), AudioExt); // Merge audio and video files. await Task.Run(() => { FfmpegBusiness.ExtractVideo(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName, VideoDest, true); if (FileHasContent(VideoDest)) FfmpegBusiness.JoinAudioVideo(VideoDest, Destination + AudioExt, Destination + DestinationExt, true); }); // Delete source files File.Delete(VideoDest); File.Delete(Destination + AudioExt); if (FileHasContent(Destination + DestinationExt) && File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) FileOperationAPIWrapper.MoveToRecycleBin(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); } // Ensure download and merge succeeded. if (FileHasContent(Destination + DestinationExt)) { // Get final file name. DefaultMediaPath PathCalc = new DefaultMediaPath(); string NewFileName = PathCalc.GetDefaultFileName(video.Artist, video.Title, video.MediaCategoryId, (MediaType)video.MediaTypeId); Directory.CreateDirectory(Path.GetDirectoryName(Settings.NaturalGroundingFolder + NewFileName)); video.FileName = NewFileName + DestinationExt; // Move file and overwrite. if (downloadInfo.Request.FileName != null && File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) FileOperationAPIWrapper.MoveToRecycleBin(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); File.Move(Destination + DestinationExt, Settings.NaturalGroundingFolder + video.FileName); } else throw new FileNotFoundException("Audio and video streams could not be merged."); // Add to database EditVideoBusiness Business = new EditVideoBusiness(); Media ExistingData = Business.GetVideoById(video.MediaId); if (ExistingData != null) { // Edit video info. ExistingData.FileName = video.FileName; ExistingData.Length = null; ExistingData.Height = null; Business.Save(); } else { // Add new video info. Business.AddVideo(video); Business.Save(); } downloadInfo.Request.FileName = video.FileName; downloadInfo.Status = DownloadStatus.Done; }
private async void menuExtractAudio_Click(object sender, RoutedEventArgs e) { if (video.FileName != null && File.Exists(Settings.NaturalGroundingFolder + video.FileName)) { MediaInfoReader MInfo = new MediaInfoReader(); await MInfo.LoadInfoAsync(Settings.NaturalGroundingFolder + video.FileName); string Ext = null; if (MInfo.AudioFormat == "MPEG Audio") Ext = ".mp2"; else if (MInfo.AudioFormat == "PCM") Ext = ".wav"; else if (MInfo.AudioFormat == "Vorbis") Ext = ".ogg"; else if (MInfo.AudioFormat == "Opus") Ext = ".opus"; else Ext = ".aac"; SaveFileDialog SaveDlg = new SaveFileDialog(); SaveDlg.InitialDirectory = Settings.NaturalGroundingFolder + "Audios"; SaveDlg.OverwritePrompt = true; SaveDlg.DefaultExt = ".mp3"; SaveDlg.Filter = string.Format("Audio Files|*{0})", Ext); ; SaveDlg.FileName = Path.GetFileNameWithoutExtension(video.FileName) + Ext; if (SaveDlg.ShowDialog() == true) { FfmpegBusiness.ExtractAudio(Settings.NaturalGroundingFolder + video.FileName, SaveDlg.FileName); } } }
private async Task LoadMediaInfoAsync() { MediaInfoReader MediaInfo = new MediaInfoReader(); await MediaInfo.LoadInfoAsync(video); MediaInfo.Dispose(); DimensionText.GetBindingExpression(TextBox.TextProperty).UpdateTarget(); }
private async Task LoadMediaInfoAsync() { using (MediaInfoReader MediaInfo = new MediaInfoReader()) { await MediaInfo.LoadInfoAsync(video); DimensionText.GetBindingExpression(TextBox.TextProperty).UpdateTarget(); DisablePitchCheckBox.IsEnabled = MediaInfo.PixelAspectRatio == 1; if (!DisablePitchCheckBox.IsEnabled) video.DisablePitch = false; } }
/// <summary> /// Extracts the video stream from specified video file. /// </summary> /// <param name="source">The video file to extract from.</param> /// <param name="destination">The output file to create.</param> /// <param name="silent">If true, the FFMPEG window will be hidden.</param> /// <returns>Whether the operation was completed.</returns> public static bool ExtractVideo(string source, string destination, bool silent) { MediaInfoReader MediaReader = new MediaInfoReader(); File.Delete(destination); return Run(string.Format(@"-i ""{0}"" -vcodec copy -an ""{1}""", source, destination), silent); }
private async Task GetMediaInfo(string previewFile, MediaEncoderSettings settings) { MediaInfoReader InfoReader = new MediaInfoReader(); await InfoReader.LoadInfoAsync(previewFile); settings.SourceWidth = InfoReader.Width; settings.SourceHeight = InfoReader.Height; settings.SourceAspectRatio = InfoReader.PixelAspectRatio ?? 1; // Fix last track of VCDs that is widescreen. if (settings.SourceHeight == 288 && settings.SourceWidth == 352 && settings.SourceAspectRatio == 1.485f) settings.SourceAspectRatio = 1.092f; settings.SourceFrameRate = InfoReader.FrameRate; if (settings.ConvertToAvi) await InfoReader.LoadInfoAsync(Settings.NaturalGroundingFolder + settings.FileName); settings.AudioRequiresMkv = (InfoReader.AudioFormat != "AAC"); settings.Position = (InfoReader.Length ?? 0) / 2; }
private async Task GetMediaInfo(string previewFile, MediaEncoderSettings settings) { using (MediaInfoReader InfoReader = new MediaInfoReader()) { await InfoReader.LoadInfoAsync(previewFile); settings.SourceWidth = InfoReader.Width; settings.SourceHeight = InfoReader.Height; settings.SourceAspectRatio = InfoReader.PixelAspectRatio ?? 1; if (settings.SourceAspectRatio != 1) { // Get aspect ratio from FFMPEG which is more accurate. float? SAR = FfmpegBusiness.GetPixelAspectRatio(settings); if (SAR.HasValue) settings.SourceAspectRatio = SAR.Value; } // Fix last track of VCDs that is widescreen. if (settings.SourceHeight == 288 && settings.SourceWidth == 352 && settings.SourceAspectRatio == 1.485f) settings.SourceAspectRatio = 1.092f; settings.SourceFrameRate = InfoReader.FrameRate; if (settings.ConvertToAvi) await InfoReader.LoadInfoAsync(Settings.NaturalGroundingFolder + settings.FileName); settings.SourceAudioFormat = InfoReader.AudioFormat; settings.SourceVideoFormat = InfoReader.VideoFormat; settings.SourceColorMatrix = InfoReader.Height < 720 ? ColorMatrix.Rec601 : ColorMatrix.Rec709; if (!settings.CanCopyAudio) settings.EncodeFormat = VideoFormats.Mkv; settings.SourceAudioBitrate = InfoReader.AudioBitRate; settings.SourceBitDepth = InfoReader.BitDepth; settings.Position = (InfoReader.Length ?? 0) / 2; settings.CalculateSize(); } }
public async Task PreparePreviewFile(MediaEncoderSettings settings, bool overwrite) { if (string.IsNullOrEmpty(settings.FileName)) return; if (overwrite) { File.Delete(PreviewSourceFile); // Select default open method. if (settings.FileName.ToLower().EndsWith(".avi")) settings.ConvertToAvi = false; else { using (MediaInfoReader InfoReader = new MediaInfoReader()) { InfoReader.LoadInfo(Settings.NaturalGroundingFolder + settings.FileName); if (settings.ConvertToAvi && InfoReader.Height.HasValue && InfoReader.Height >= 720) settings.ConvertToAvi = false; } } } bool AviFileReady = File.Exists(PreviewSourceFile); if (!AviFileReady && settings.ConvertToAvi) AviFileReady = await Task.Run(() => FfmpegBusiness.ConvertToAVI(Settings.NaturalGroundingFolder + settings.FileName, PreviewSourceFile, false)); if (AviFileReady && settings.ConvertToAvi) await GetMediaInfo(PreviewSourceFile, settings); else { settings.ConvertToAvi = false; await GetMediaInfo(Settings.NaturalGroundingFolder + settings.FileName, settings); } // Auto-calculate crop settings. if (settings.CropLeft == 0 && settings.CropTop == 0 && settings.CropRight == 0 && settings.CropBottom == 0) { Rect AutoCrop = await Task.Run(() => FfmpegBusiness.GetAutoCropRect(settings, true)); if (settings.CropLeft == 0) settings.CropLeft = AutoCrop.Left; if (settings.CropTop == 0) settings.CropTop = AutoCrop.Top; if (settings.CropRight == 0) settings.CropRight = AutoCrop.Right; if (settings.CropBottom == 0) settings.CropBottom = AutoCrop.Bottom; } }