/// <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> /// Returns the best format from the list in this order of availability: WebM, Mp4 or Flash. /// Mp4 will be chosen if WebM is over 35% smaller. /// </summary> /// <param name="list">The list of videos to chose from.</param> /// <returns>The best format available.</returns> public static BestFormatInfo SelectBestFormat(IEnumerable<VideoInfo> list) { var MaxResolutionList = (from v in list where (Settings.SavedFile.MaxDownloadQuality == 0 || v.Resolution <= Settings.SavedFile.MaxDownloadQuality) && v.AdaptiveType != AdaptiveType.Audio orderby v.Resolution descending select v).ToList(); MaxResolutionList = MaxResolutionList.Where(v => v.Resolution == MaxResolutionList.First().Resolution).ToList(); // If for the maximum resolution, only some formats have a file size, the other ones must be queried. if (MaxResolutionList.Any(v => v.FileSize == 0) && MaxResolutionList.Any(v => v.FileSize > 0)) { foreach (VideoInfo item in MaxResolutionList.Where(v => v.FileSize == 0)) { DownloadUrlResolver.QueryStreamSize(item); } } VideoInfo BestVideo = (from v in MaxResolutionList // WebM VP9 encodes ~35% better. non-DASH is VP8 and isn't better than MP4. let Preference = (int)((v.VideoType == VideoType.WebM && v.AdaptiveType == AdaptiveType.Video) ? v.FileSize * 1.35 : v.FileSize) where v.Resolution == MaxResolutionList.First().Resolution orderby Preference descending select v).FirstOrDefault(); if (BestVideo != null) { BestFormatInfo Result = new BestFormatInfo(); Result.BestVideo = BestVideo; // Even for non-DASH videos, we still want to know what audio is available even though it may not be downloaded. Result.BestAudio = SelectBestAudio(from v in list where (v.CanExtractAudio || v.AdaptiveType == AdaptiveType.Audio) orderby v.AudioBitrate descending select v); return Result; } else return null; }