From 805926ebdb521a79439dd4c321101a2d97c20858 Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Thu, 13 Apr 2017 09:49:25 +0200 Subject: [PATCH 1/7] removed flash and updated readme I have removed the audio download stuff which depended on the flash format of the youtube video's. This also contains PR 245 with the updated decipherer. --- .../YoutubeExtractor/AacAudioExtractor.cs | 104 ----------- .../YoutubeExtractor/AudioDownloader.cs | 113 ------------ .../AudioExtractionException.cs | 35 ---- .../YoutubeExtractor/Decipherer.cs | 171 ++++++++---------- .../YoutubeExtractor/DownloadUrlResolver.cs | 1 + YoutubeExtractor/YoutubeExtractor/FlvFile.cs | 16 +- .../YoutubeExtractor/YoutubeExtractor.csproj | 9 +- readme.md | 14 +- 8 files changed, 98 insertions(+), 365 deletions(-) delete mode 100644 YoutubeExtractor/YoutubeExtractor/AacAudioExtractor.cs delete mode 100644 YoutubeExtractor/YoutubeExtractor/AudioDownloader.cs delete mode 100644 YoutubeExtractor/YoutubeExtractor/AudioExtractionException.cs diff --git a/YoutubeExtractor/YoutubeExtractor/AacAudioExtractor.cs b/YoutubeExtractor/YoutubeExtractor/AacAudioExtractor.cs deleted file mode 100644 index 39bea27..0000000 --- a/YoutubeExtractor/YoutubeExtractor/AacAudioExtractor.cs +++ /dev/null @@ -1,104 +0,0 @@ -// **************************************************************************** -// -// FLV Extract -// Copyright (C) 2006-2012 J.D. Purcell (moitah@yahoo.com) -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// **************************************************************************** - -using System.IO; - -namespace YoutubeExtractor -{ - internal class AacAudioExtractor : IAudioExtractor - { - private readonly FileStream fileStream; - private int aacProfile; - private int channelConfig; - private int sampleRateIndex; - - public AacAudioExtractor(string path) - { - this.VideoPath = path; - fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 64 * 1024); - } - - public string VideoPath { get; private set; } - - public void Dispose() - { - this.fileStream.Dispose(); - } - - public void WriteChunk(byte[] chunk, uint timeStamp) - { - if (chunk.Length < 1) - { - return; - } - - if (chunk[0] == 0) - { - // Header - if (chunk.Length < 3) - { - return; - } - - ulong bits = (ulong)BigEndianBitConverter.ToUInt16(chunk, 1) << 48; - - aacProfile = BitHelper.Read(ref bits, 5) - 1; - sampleRateIndex = BitHelper.Read(ref bits, 4); - channelConfig = BitHelper.Read(ref bits, 4); - - if (aacProfile < 0 || aacProfile > 3) - throw new AudioExtractionException("Unsupported AAC profile."); - if (sampleRateIndex > 12) - throw new AudioExtractionException("Invalid AAC sample rate index."); - if (channelConfig > 6) - throw new AudioExtractionException("Invalid AAC channel configuration."); - } - - else - { - // Audio data - int dataSize = chunk.Length - 1; - ulong bits = 0; - - // Reference: WriteADTSHeader from FAAC's bitstream.c - - BitHelper.Write(ref bits, 12, 0xFFF); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 2, 0); - BitHelper.Write(ref bits, 1, 1); - BitHelper.Write(ref bits, 2, aacProfile); - BitHelper.Write(ref bits, 4, sampleRateIndex); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 3, channelConfig); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 1, 0); - BitHelper.Write(ref bits, 13, 7 + dataSize); - BitHelper.Write(ref bits, 11, 0x7FF); - BitHelper.Write(ref bits, 2, 0); - - fileStream.Write(BigEndianBitConverter.GetBytes(bits), 1, 7); - fileStream.Write(chunk, 1, dataSize); - } - } - } -} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/AudioDownloader.cs b/YoutubeExtractor/YoutubeExtractor/AudioDownloader.cs deleted file mode 100644 index d359e8d..0000000 --- a/YoutubeExtractor/YoutubeExtractor/AudioDownloader.cs +++ /dev/null @@ -1,113 +0,0 @@ -// **************************************************************************** -// -// FLV Extract -// Copyright (C) 2013-2014 Dennis Daume (daume.dennis@gmail.com) -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// **************************************************************************** - -using System; -using System.IO; -using System.Net; - -namespace YoutubeExtractor -{ - /// - /// Provides a method to download a video and extract its audio track. - /// - public class AudioDownloader : Downloader - { - private bool isCanceled; - - /// - /// Initializes a new instance of the class. - /// - /// The video to convert. - /// The path to save the audio. - /// /// An optional value to limit the number of bytes to download. - /// or is null. - public AudioDownloader(VideoInfo video, string savePath, int? bytesToDownload = null) - : base(video, savePath, bytesToDownload) - { } - - /// - /// Occurs when the progress of the audio extraction has changed. - /// - public event EventHandler AudioExtractionProgressChanged; - - /// - /// Occurs when the download progress of the video file has changed. - /// - public event EventHandler DownloadProgressChanged; - - /// - /// Downloads the video from YouTube and then extracts the audio track out if it. - /// - /// - /// The temporary video file could not be created. - /// - or - - /// The audio file could not be created. - /// - /// An error occured during audio extraction. - /// An error occured while downloading the video. - public override void Execute() - { - string tempPath = Path.GetTempFileName(); - - this.DownloadVideo(tempPath); - - if (!this.isCanceled) - { - this.ExtractAudio(tempPath); - } - - this.OnDownloadFinished(EventArgs.Empty); - } - - private void DownloadVideo(string path) - { - var videoDownloader = new VideoDownloader(this.Video, path, this.BytesToDownload); - - videoDownloader.DownloadProgressChanged += (sender, args) => - { - if (this.DownloadProgressChanged != null) - { - this.DownloadProgressChanged(this, args); - - this.isCanceled = args.Cancel; - } - }; - - videoDownloader.Execute(); - } - - private void ExtractAudio(string path) - { - using (var flvFile = new FlvFile(path, this.SavePath)) - { - flvFile.ConversionProgressChanged += (sender, args) => - { - if (this.AudioExtractionProgressChanged != null) - { - this.AudioExtractionProgressChanged(this, new ProgressEventArgs(args.ProgressPercentage)); - } - }; - - flvFile.ExtractStreams(); - } - } - } -} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/AudioExtractionException.cs b/YoutubeExtractor/YoutubeExtractor/AudioExtractionException.cs deleted file mode 100644 index e546478..0000000 --- a/YoutubeExtractor/YoutubeExtractor/AudioExtractionException.cs +++ /dev/null @@ -1,35 +0,0 @@ -// **************************************************************************** -// -// FLV Extract -// Copyright (C) 2013-2014 Dennis Daume (daume.dennis@gmail.com) -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// **************************************************************************** - -using System; - -namespace YoutubeExtractor -{ - /// - /// The exception that is thrown when an error occurs durin audio extraction. - /// - public class AudioExtractionException : Exception - { - public AudioExtractionException(string message) - : base(message) - { } - } -} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs index e5ac261..78c8ae2 100644 --- a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs +++ b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -12,132 +13,116 @@ public static string DecipherWithVersion(string cipher, string cipherVersion) string jsUrl = string.Format("http://s.ytimg.com/yts/jsbin/player-{0}.js", cipherVersion); string js = HttpHelper.DownloadString(jsUrl); - //Find "C" in this: var A = B.sig||C (B.s) - string functNamePattern = @"\""signature"",\s?([a-zA-Z0-9\$]+)\("; //Regex Formed To Find Word or DollarSign + var decodeArray = FindSignatureCode(js); - var funcName = Regex.Match(js, functNamePattern).Groups[1].Value; - - if (funcName.Contains("$")) - { - funcName = "\\" + funcName; //Due To Dollar Sign Introduction, Need To Escape - } + return DecryptSignature(cipher, decodeArray); + } - string funcPattern = @"(?!h\.)" + @funcName + @"=function\(\w+\)\{.*?\}"; //Escape funcName string - var funcBody = Regex.Match(js, funcPattern, RegexOptions.Singleline).Value; //Entire sig function - var lines = funcBody.Split(';'); //Each line in sig function + private static List FindSignatureCode(string sourceCode) + { + var signatureFunctionName = FindMatch(sourceCode, @"\.set\s*\(""signature""\s*,\s*([a-zA-Z0-9_$][\w$]*)\("); - string idReverse = "", idSlice = "", idCharSwap = ""; //Hold name for each cipher method - string functionIdentifier = ""; - string operations = ""; + //Optimization of Gantt's technique - functionName not needed + var regExp = @"\s*\([\w$]*\)\s*{[\w$]*=[\w$]*\.split\(""""\);\n*(.+);return [\w$]*\.join"; - foreach (var line in lines.Skip(1).Take(lines.Length - 2)) //Matches the funcBody with each cipher method. Only runs till all three are defined. - { - if (!string.IsNullOrEmpty(idReverse) && !string.IsNullOrEmpty(idSlice) && - !string.IsNullOrEmpty(idCharSwap)) - { - break; //Break loop if all three cipher methods are defined - } + var reverseFunctionName = FindMatch(sourceCode, @"([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.reverse\s*\(\s*\)\s*}"); + var sliceFunctionName = FindMatch(sourceCode, @"([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*,\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.(?:slice|splice)\(.+\)\s*}"); - functionIdentifier = GetFunctionFromLine(line); - string reReverse = string.Format(@"{0}:\bfunction\b\(\w+\)", functionIdentifier); //Regex for reverse (one parameter) - string reSlice = string.Format(@"{0}:\bfunction\b\([a],b\).(\breturn\b)?.?\w+\.", functionIdentifier); //Regex for slice (return or not) - string reSwap = string.Format(@"{0}:\bfunction\b\(\w+\,\w\).\bvar\b.\bc=a\b", functionIdentifier); //Regex for the char swap. + var functionCode = FindMatch(sourceCode, regExp); + functionCode = functionCode.Replace(reverseFunctionName, "reverse"); + functionCode = functionCode.Replace(sliceFunctionName, "slice"); + var functionCodePieces = functionCode.Split(';'); - if (Regex.Match(js, reReverse).Success) - { - idReverse = functionIdentifier; //If def matched the regex for reverse then the current function is a defined as the reverse - } + List decodeArray = new List(); - if (Regex.Match(js, reSlice).Success) - { - idSlice = functionIdentifier; //If def matched the regex for slice then the current function is defined as the slice. - } + var regSlice = new Regex("slice\\s*\\(\\s*.+([0-9]+)\\s*\\)"); + string regSwap = "\\w+\\s*\\(\\s*\\w+\\s*,\\s*([0-9]+)\\s*\\)"; + string regInline = "\\w+\\[0\\]\\s*=\\s*\\w+\\[([0-9]+)\\s*%\\s*\\w+\\.length\\]"; - if (Regex.Match(js, reSwap).Success) - { - idCharSwap = functionIdentifier; //If def matched the regex for charSwap then the current function is defined as swap. - } - } - - foreach (var line in lines.Skip(1).Take(lines.Length - 2)) + for (var i = 0; i < functionCodePieces.Length; i++) { - Match m; - functionIdentifier = GetFunctionFromLine(line); - if ((m = Regex.Match(line, @"\(\w+,(?\d+)\)")).Success && functionIdentifier == idCharSwap) - { - operations += "w" + m.Groups["index"].Value + " "; //operation is a swap (w) - } + functionCodePieces[i] = functionCodePieces[i].Trim(); - if ((m = Regex.Match(line, @"\(\w+,(?\d+)\)")).Success && functionIdentifier == idSlice) - { - operations += "s" + m.Groups["index"].Value + " "; //operation is a slice - } + var codeLine = functionCodePieces[i]; - if (functionIdentifier == idReverse) //No regex required for reverse (reverse method has no parameters) + if (codeLine.Length > 0) { - operations += "r "; //operation is a reverse - } - } - operations = operations.Trim(); + var arrSlice = regSlice.Match(codeLine); - return DecipherWithOperations(cipher, operations); - } - - private static string ApplyOperation(string cipher, string op) - { - switch (op[0]) - { - case 'r': - return new string(cipher.ToCharArray().Reverse().ToArray()); - - case 'w': + if (arrSlice.Success && arrSlice.Length >= 2) { - int index = GetOpIndex(op); - return SwapFirstChar(cipher, index); + var slice = int.Parse(arrSlice.Groups[1].Value); + decodeArray.Add(-slice); } - - case 's': + else if (functionCodePieces[i].IndexOf("reverse") >= 0) { - int index = GetOpIndex(op); - return cipher.Substring(index); + decodeArray.Add(0); + } + else if (codeLine.IndexOf("[0]") >= 0) + { // inline swap + + if (i + 2 < functionCodePieces.Length && functionCodePieces[i + 1].IndexOf(".length") >= 0 && functionCodePieces[i + 1].IndexOf("[0]") >= 0) + { + + var inline = FindMatch(functionCodePieces[i + 1], regInline); + decodeArray.Add(int.Parse(inline)); + + i += 2; + + } } + else if (codeLine.IndexOf(',') >= 0) + { // swap + var swap = FindMatch(codeLine, regSwap); + int swapVal = int.Parse(swap); - default: - throw new NotImplementedException("Couldn't find cipher operation."); + if (swapVal > 0) + { + decodeArray.Add(swapVal); + } + + } + } } + return decodeArray; } - private static string DecipherWithOperations(string cipher, string operations) + private static string DecryptSignature(string sig, List arr) { - return operations.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries) - .Aggregate(cipher, ApplyOperation); - } + var sigA = sig; - private static string GetFunctionFromLine(string currentLine) - { - Regex matchFunctionReg = new Regex(@"\w+\.(?\w+)\("); //lc.ac(b,c) want the ac part. - Match rgMatch = matchFunctionReg.Match(currentLine); - string matchedFunction = rgMatch.Groups["functionID"].Value; - return matchedFunction; //return 'ac' + for (var i = 0; i < arr.Count; i++) + { + var act = arr[i]; + sigA = (act > 0) ? Swap(sigA.ToCharArray(), act) : ((act == 0) ? Reverse(sigA) : sigA.Substring(-act)); + } + + return sigA; } - private static int GetOpIndex(string op) + private static string Swap(char[] a, int b) { - string parsed = new Regex(@".(\d+)").Match(op).Result("$1"); - int index = Int32.Parse(parsed); + var c = a[0]; + a[0] = a[b % a.Length]; + a[b] = c; - return index; + return new string(a); } - private static string SwapFirstChar(string cipher, int index) + private static string Reverse(string s) + { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + private static string FindMatch(string text, string regexp) { - var builder = new StringBuilder(cipher); - builder[0] = cipher[index]; - builder[index] = cipher[0]; + Regex rgx = new Regex(regexp); + var matches = rgx.Matches(text); - return builder.ToString(); + return matches[0].Groups[1].Value; } } } \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs index 6896edf..8ef9667 100644 --- a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs +++ b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; +using System.Xml.Linq; using Newtonsoft.Json.Linq; namespace YoutubeExtractor diff --git a/YoutubeExtractor/YoutubeExtractor/FlvFile.cs b/YoutubeExtractor/YoutubeExtractor/FlvFile.cs index 6b4692b..676a4d3 100644 --- a/YoutubeExtractor/YoutubeExtractor/FlvFile.cs +++ b/YoutubeExtractor/YoutubeExtractor/FlvFile.cs @@ -62,11 +62,11 @@ public void ExtractStreams() { this.Seek(0); - if (this.ReadUInt32() != 0x464C5601) - { - // not a FLV file - throw new AudioExtractionException("Invalid input file. Impossible to extract audio track."); - } + //if (this.ReadUInt32() != 0x464C5601) + //{ + // // not a FLV file + // throw new AudioExtractionException("Invalid input file. Impossible to extract audio track."); + //} this.ReadUInt8(); uint dataOffset = this.ReadUInt32(); @@ -142,8 +142,8 @@ private IAudioExtractor GetAudioWriter(uint mediaInfo) case 2: return new Mp3AudioExtractor(this.outputPath); - case 10: - return new AacAudioExtractor(this.outputPath); + //case 10: + // return new AacAudioExtractor(this.outputPath); } string typeStr; @@ -165,7 +165,7 @@ private IAudioExtractor GetAudioWriter(uint mediaInfo) break; } - throw new AudioExtractionException("Unable to extract audio (" + typeStr + " is unsupported)."); + throw new Exception("Unable to extract audio (" + typeStr + " is unsupported)."); } private byte[] ReadBytes(int length) diff --git a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj index 9cb7519..7f22de9 100644 --- a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj +++ b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj @@ -36,17 +36,18 @@ - ..\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll + D:\Code\DownloadYouTube\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll + + False + ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll + - - - diff --git a/readme.md b/readme.md index b43bf24..e2435bc 100644 --- a/readme.md +++ b/readme.md @@ -101,10 +101,8 @@ videoDownloader.Execute(); /* * We want the first extractable video with the highest audio quality. */ -VideoInfo video = videoInfos - .Where(info => info.CanExtractAudio) - .OrderByDescending(info => info.AudioBitrate) - .First(); +VideoInfo video = videoInfos.Where(i => i.VideoType == VideoType.Mp4 && i.Resolution == 0) + .OrderByDescending(q => q.AudioBitrate).First(); /* * If the video has a decrypted signature, decipher it @@ -119,17 +117,17 @@ if (video.RequiresDecryption) * The first argument is the video where the audio should be extracted from. * The second argument is the path to save the audio file. */ -var audioDownloader = new AudioDownloader(video, Path.Combine("D:/Downloads", video.Title + video.AudioExtension)); +var videoDownloader = new VideoDownloader(video, Path.Combine("D:/Downloads", video.Title + ".mp3")); // Register the progress events. We treat the download progress as 85% of the progress and the extraction progress only as 15% of the progress, // because the download will take much longer than the audio extraction. -audioDownloader.DownloadProgressChanged += (sender, args) => Console.WriteLine(args.ProgressPercentage * 0.85); -audioDownloader.AudioExtractionProgressChanged += (sender, args) => Console.WriteLine(85 + args.ProgressPercentage * 0.15); +videoDownloader.DownloadProgressChanged += (sender, args) => Console.WriteLine(args.ProgressPercentage * 0.85); +videoDownloader.AudioExtractionProgressChanged += (sender, args) => Console.WriteLine(85 + args.ProgressPercentage * 0.15); /* * Execute the audio downloader. * For GUI applications note, that this method runs synchronously. */ -audioDownloader.Execute(); +videoDownloader.Execute(); ``` From 7b101404e1732ab40cdf8175bad2d3ae42f9ff70 Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Thu, 13 Apr 2017 16:15:18 +0200 Subject: [PATCH 2/7] Added Thumbnail Url So you can use it in order to add to the ID3 tag of the audio. --- .../YoutubeExtractor/DownloadUrlResolver.cs | 26 ++++++++++++++++--- .../YoutubeExtractor/VideoInfo.cs | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs index 8ef9667..de31361 100644 --- a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs +++ b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs @@ -86,13 +86,14 @@ public static IEnumerable GetDownloadUrls(string videoUrl, bool decry try { + string strThumbnail = LocateThumbnail(videoUrl); var json = LoadJson(videoUrl); string videoTitle = GetVideoTitle(json); IEnumerable downloadUrls = ExtractDownloadUrls(json); - IEnumerable infos = GetVideoInfos(downloadUrls, videoTitle).ToList(); + IEnumerable infos = GetVideoInfos(downloadUrls, videoTitle, strThumbnail).ToList(); string htmlPlayerVersion = GetHtml5PlayerVersion(json); @@ -122,6 +123,24 @@ public static IEnumerable GetDownloadUrls(string videoUrl, bool decry return null; // Will never happen, but the compiler requires it } + private static string LocateThumbnail(string videoUrl) + { + string pageSource = HttpHelper.DownloadString(videoUrl); + int start = pageSource.IndexOf("og:image"); + if (start > 0) + { + string t = pageSource.Substring(start + 10); + int eind = t.IndexOf(">"); + t = t.Substring(0, eind); + t = t.Trim().Replace("content=\"", "").Replace("\"", ""); + return t; + // + } + else { + return ""; + } + } + #if PORTABLE public static System.Threading.Tasks.Task> GetDownloadUrlsAsync(string videoUrl, bool decryptSignature = true) @@ -252,7 +271,7 @@ private static string GetStreamMap(JObject json) return streamMapString; } - private static IEnumerable GetVideoInfos(IEnumerable extractionInfos, string videoTitle) + private static IEnumerable GetVideoInfos(IEnumerable extractionInfos, string videoTitle, string thumbnail) { var downLoadInfos = new List(); @@ -270,7 +289,8 @@ private static IEnumerable GetVideoInfos(IEnumerable { DownloadUrl = extractionInfo.Uri.ToString(), Title = videoTitle, - RequiresDecryption = extractionInfo.RequiresDecryption + RequiresDecryption = extractionInfo.RequiresDecryption, + ThumbnailUrl = thumbnail }; } diff --git a/YoutubeExtractor/YoutubeExtractor/VideoInfo.cs b/YoutubeExtractor/YoutubeExtractor/VideoInfo.cs index e52db40..a9fa359 100644 --- a/YoutubeExtractor/YoutubeExtractor/VideoInfo.cs +++ b/YoutubeExtractor/YoutubeExtractor/VideoInfo.cs @@ -195,6 +195,7 @@ public string VideoExtension /// Gets the video type (container). /// public VideoType VideoType { get; private set; } + public string ThumbnailUrl { get; internal set; } /// /// We use this in the method to From a2e133a337da64df77b84ebfd7203e9a4241d37a Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Fri, 21 Apr 2017 10:30:00 +0200 Subject: [PATCH 3/7] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e2435bc..c569747 100644 --- a/readme.md +++ b/readme.md @@ -117,7 +117,7 @@ if (video.RequiresDecryption) * The first argument is the video where the audio should be extracted from. * The second argument is the path to save the audio file. */ -var videoDownloader = new VideoDownloader(video, Path.Combine("D:/Downloads", video.Title + ".mp3")); +var videoDownloader = new VideoDownloader(video, Path.Combine("D:/Downloads", video.Title + ".m4a")); // Register the progress events. We treat the download progress as 85% of the progress and the extraction progress only as 15% of the progress, // because the download will take much longer than the audio extraction. From 96258edd6e2d8ca88f716a40691f945fe1f90c2f Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Fri, 21 Apr 2017 10:31:08 +0200 Subject: [PATCH 4/7] Update YoutubeExtractor.csproj --- YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj index 7f22de9..555cc08 100644 --- a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj +++ b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj @@ -36,7 +36,7 @@ - D:\Code\DownloadYouTube\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll @@ -77,4 +77,4 @@ --> - \ No newline at end of file + From 6de1a945cd701cd7aff47bdafd03c692bd73d0c1 Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Fri, 21 Apr 2017 10:32:08 +0200 Subject: [PATCH 5/7] Update FlvFile.cs --- YoutubeExtractor/YoutubeExtractor/FlvFile.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/FlvFile.cs b/YoutubeExtractor/YoutubeExtractor/FlvFile.cs index 676a4d3..7b7d68c 100644 --- a/YoutubeExtractor/YoutubeExtractor/FlvFile.cs +++ b/YoutubeExtractor/YoutubeExtractor/FlvFile.cs @@ -62,12 +62,6 @@ public void ExtractStreams() { this.Seek(0); - //if (this.ReadUInt32() != 0x464C5601) - //{ - // // not a FLV file - // throw new AudioExtractionException("Invalid input file. Impossible to extract audio track."); - //} - this.ReadUInt8(); uint dataOffset = this.ReadUInt32(); @@ -141,9 +135,6 @@ private IAudioExtractor GetAudioWriter(uint mediaInfo) case 14: case 2: return new Mp3AudioExtractor(this.outputPath); - - //case 10: - // return new AacAudioExtractor(this.outputPath); } string typeStr; @@ -253,4 +244,4 @@ private void Seek(long offset) this.fileOffset = offset; } } -} \ No newline at end of file +} From f19f3d8f83a08ed8c724d2d944246868eb7a6a64 Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Fri, 16 Mar 2018 11:16:06 +0100 Subject: [PATCH 6/7] Can d/l all yt urls again Have a wonderful weekend! --- YoutubeExtractor/YoutubeExtractor/Decipherer.cs | 6 +++++- .../YoutubeExtractor/DownloadUrlResolver.cs | 2 +- .../YoutubeExtractor/YoutubeExtractor.csproj | 13 ++++++++----- .../packages.YoutubeExtractor.config | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs index 78c8ae2..9193e8c 100644 --- a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs +++ b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs @@ -117,12 +117,16 @@ private static string Reverse(string s) Array.Reverse(charArray); return new string(charArray); } + private static string FindMatch(string text, string regexp) { Regex rgx = new Regex(regexp); var matches = rgx.Matches(text); - return matches[0].Groups[1].Value; + if (matches.Count > 0) + return matches[0].Groups[1].Value; + else + return string.Empty; } } } \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs index de31361..89def70 100644 --- a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs +++ b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs @@ -317,7 +317,7 @@ private static string GetVideoTitle(JObject json) private static bool IsVideoUnavailable(string pageSource) { - const string unavailableContainer = "
"; + const string unavailableContainer = "
"; return pageSource.Contains(unavailableContainer); } diff --git a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj index 7f22de9..6641588 100644 --- a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj +++ b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -10,8 +10,9 @@ Properties YoutubeExtractor YoutubeExtractor - v3.5 + v4.7.1 512 + true @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -33,17 +35,18 @@ 4 bin\Release\Full\YoutubeExtractor.xml true + false - - D:\Code\DownloadYouTube\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll + + ..\..\..\..\..\Source\Workspaces\DownloadYouTube\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll False - ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll + ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Xml.Linq.dll diff --git a/YoutubeExtractor/YoutubeExtractor/packages.YoutubeExtractor.config b/YoutubeExtractor/YoutubeExtractor/packages.YoutubeExtractor.config index 9520f36..71b8fed 100644 --- a/YoutubeExtractor/YoutubeExtractor/packages.YoutubeExtractor.config +++ b/YoutubeExtractor/YoutubeExtractor/packages.YoutubeExtractor.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From 4cc64b0bd048f1aaeb08171a6b07c2ee405f8950 Mon Sep 17 00:00:00 2001 From: JP Hellemons Date: Fri, 16 Mar 2018 11:25:15 +0100 Subject: [PATCH 7/7] pulling changes down corrupted my csproj fixed again --- YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj index e0366b5..ea5f897 100644 --- a/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj +++ b/YoutubeExtractor/YoutubeExtractor/YoutubeExtractor.csproj @@ -40,8 +40,6 @@ ..\..\..\..\..\Source\Workspaces\DownloadYouTube\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll - - ..\packages\Newtonsoft.Json.5.0.8\lib\net35\Newtonsoft.Json.dll