From d1546505daa59d99efb3e4b57c5bbadd4687beff Mon Sep 17 00:00:00 2001 From: Martin Davy Date: Wed, 13 Jan 2016 18:19:58 -0500 Subject: [PATCH 1/5] Partial port of async UWA changes - does not build --- .../YoutubeExtractor/Decipherer.cs | 19 +++ .../YoutubeExtractor/DownloadUrlResolver.cs | 121 +++++++++++++++--- .../YoutubeExtractor/HttpHelper.cs | 29 +++++ .../YoutubeExtractor/project.json | 0 4 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 YoutubeExtractor/YoutubeExtractor/project.json diff --git a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs index 3f42263..1f467fe 100644 --- a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs +++ b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs @@ -3,14 +3,33 @@ using System.Text; using System.Text.RegularExpressions; +#if PORTABLE +using System.Threading.Tasks; +#endif + namespace YoutubeExtractor { internal static class Decipherer { + +#if PORTABLE + public static async Task DecipherWithVersionAsync(string cipher, string cipherVersion) + { + string jsUrl = string.Format("http://s.ytimg.com/yts/jsbin/player-{0}.js", cipherVersion); + string js = await HttpHelper.DownloadString(jsUrl); + return DecipherJson(js, cipher); + } +#endif + 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); + return DecipherJson(js, cipher); + } + + private static string DecipherJson(string js, string cipher) + { //Find "C" in this: var A = B.sig||C (B.s) string functNamePattern = @"\.sig\s*\|\|([a-zA-Z0-9\$]+)\("; //Regex Formed To Find Word or DollarSign diff --git a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs index 36b6b58..5978d17 100644 --- a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs +++ b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs @@ -5,6 +5,10 @@ using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; +#if PORTABLE +using System.Threading.Tasks; +#endif + namespace YoutubeExtractor { /// @@ -33,11 +37,38 @@ public static void DecryptDownloadUrl(VideoInfo videoInfo) { string encryptedSignature = queries[SignatureQuery]; - string decrypted; + string decrypted; try { decrypted = GetDecipheredSignature(videoInfo.HtmlPlayerVersion, encryptedSignature); + } + + catch (Exception ex) + { + throw new YoutubeParseException("Could not decipher signature", ex); +} + +videoInfo.DownloadUrl = HttpHelper.ReplaceQueryStringParameter(videoInfo.DownloadUrl, SignatureQuery, decrypted); + videoInfo.RequiresDecryption = false; + } + } + +#if PORTABLE + public static async Task DecryptDownloadUrlAsyn(VideoInfo videoInfo) + + { + IDictionary queries = HttpHelper.ParseQueryString(videoInfo.DownloadUrl); + + if (queries.ContainsKey(SignatureQuery)) + { + string encryptedSignature = queries[SignatureQuery]; + + string decrypted; + + try + { + decrypted = await GetDecipheredSignatureAync(videoInfo.HtmlPlayerVersion, encryptedSignature); } catch (Exception ex) @@ -49,6 +80,7 @@ public static void DecryptDownloadUrl(VideoInfo videoInfo) videoInfo.RequiresDecryption = false; } } +#endif /// /// Gets a list of s for the specified URL. @@ -72,21 +104,52 @@ public static void DecryptDownloadUrl(VideoInfo videoInfo) /// An error occurred while downloading the YouTube page html. /// /// The Youtube page could not be parsed. - public static IEnumerable GetDownloadUrls(string videoUrl, bool decryptSignature = true) + private static IEnumerable GetDownloadUrls(string videoUrl, bool decryptSignature = true) { - if (videoUrl == null) - throw new ArgumentNullException("videoUrl"); + try + { + var json = LoadJson(videoUrl); + + string videoTitle = GetVideoTitle(json); + + IEnumerable downloadUrls = ExtractDownloadUrls(json); + + IEnumerable infos = GetVideoInfos(downloadUrls, videoTitle).ToList(); + + string htmlPlayerVersion = GetHtml5PlayerVersion(json); + + foreach (VideoInfo info in infos) + { + info.HtmlPlayerVersion = htmlPlayerVersion; - bool isYoutubeUrl = TryNormalizeYoutubeUrl(videoUrl, out videoUrl); + if (decryptSignature && info.RequiresDecryption) + { + DecryptDownloadUrl(info); + } + } - if (!isYoutubeUrl) + return infos; + } + + catch (Exception ex) { - throw new ArgumentException("URL is not a valid youtube URL!"); + if (ex is WebException || ex is VideoNotAvailableException) + { + throw; + } + + ThrowYoutubeParseException(ex, videoUrl); } + return null; // Will never happen, but the compiler requires it + } + +#if PORTABLE + private static async Task> GetDownloadUrlsAsync(string videoUrl, bool decryptSignature = true) + { try { - var json = LoadJson(videoUrl); + var json = await LoadJsonAync(videoUrl); string videoTitle = GetVideoTitle(json); @@ -102,7 +165,7 @@ public static IEnumerable GetDownloadUrls(string videoUrl, bool decry if (decryptSignature && info.RequiresDecryption) { - DecryptDownloadUrl(info); + await DecryptDownloadUrlAync(info); } } @@ -122,15 +185,25 @@ public static IEnumerable GetDownloadUrls(string videoUrl, bool decry return null; // Will never happen, but the compiler requires it } -#if PORTABLE - - public static System.Threading.Tasks.Task> GetDownloadUrlsAsync(string videoUrl, bool decryptSignature = true) + public static async Task GetVideoUriAsync(string videoId) { - return System.Threading.Tasks.Task.Run(() => GetDownloadUrls(videoUrl, decryptSignature)); - } + var videoUrl = "http://www.youtube.com/watch?v=" + videoId; -#endif + var videoInfos = await GetDownloadUrlsAsync(videoUrl, false); + // TODO - parameterize video resolution + VideoInfo video = videoInfos + .First(info => info.VideoType == VideoType.Mp4 && info.Resolution == 360); + + // If the video has an encrypted signature, decipher it + if (video.RequiresDecryption) + { + await DownloadUrlResolver.DecryptDownloadUrlAsync(video); + } + + return new Uri(video.DownloadUrl); + } +#endif /// /// Normalizes the given YouTube URL to the format http://youtube.com/watch?v={youtube-id} /// and returns whether the normalization was successful or not. @@ -218,16 +291,17 @@ private static string GetAdaptiveStreamMap(JObject json) return streamMap.ToString(); } - private static string GetDecipheredSignature(string htmlPlayerVersion, string signature) +#if PORTABLE + private static async Task GetDecipheredSignatureAsync(string htmlPlayerVersion, string signature) { if (signature.Length == CorrectSignatureLength) { return signature; } - return Decipherer.DecipherWithVersion(signature, htmlPlayerVersion); + return await Decipherer.DecipherWithVersionAsync(signature, htmlPlayerVersion); } - +#endif private static string GetHtml5PlayerVersion(JObject json) { var regex = new Regex(@"player-(.+?).js"); @@ -301,10 +375,21 @@ private static bool IsVideoUnavailable(string pageSource) return pageSource.Contains(unavailableContainer); } +#if PORTABLE + private static async Task LoadJsonAsync(string url) + { + string pageSource = await HttpHelper.DownloadStringAsync(url); + return ParseJson(pageSource); + } +#endif private static JObject LoadJson(string url) { string pageSource = HttpHelper.DownloadString(url); + return ParseJson(pageSource); + } + private static JObject ParseJson(string pageSource) + { if (IsVideoUnavailable(pageSource)) { throw new VideoNotAvailableException(); diff --git a/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs b/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs index e25f23a..c0bf017 100644 --- a/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs +++ b/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs @@ -9,6 +9,21 @@ namespace YoutubeExtractor { internal static class HttpHelper { + +#if PORTABLE + public static async System.Threading.Tasks.Task DownloadStringAsync(string url) + { + var request = WebRequest.Create(url); + request.Method = "GET"; + + var response = await System.Threading.Tasks.Task.Factory.FromAsync( + request.BeginGetResponse, + asyncResult => request.EndGetResponse(asyncResult), + null); + + return await ReadStreamFromResponseAsync(response); +#endif + } public static string DownloadString(string url) { #if PORTABLE @@ -108,5 +123,19 @@ private static string ReadStreamFromResponse(WebResponse response) } } } + +#if PORTABLE + private static async System.Threading.Tasks.Task ReadStreamFromResponseAsync(WebResponse response) + { + using (Stream responseStream = response.GetResponseStream()) + { + using (var sr = new StreamReader(responseStream)) + { + return await sr.ReadToEndAsync(); + } + } + } } +#endif + } \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor/project.json b/YoutubeExtractor/YoutubeExtractor/project.json new file mode 100644 index 0000000..e69de29 From c18e85ff1d0483090b3c14bbb9147f7c9e3be3f3 Mon Sep 17 00:00:00 2001 From: Martin Davy Date: Thu, 14 Jan 2016 17:01:45 -0500 Subject: [PATCH 2/5] Fixed build errors --- .../YoutubeExtractor/Decipherer.cs | 2 +- .../YoutubeExtractor/DownloadUrlResolver.cs | 30 ++++++++++++------- .../YoutubeExtractor/HttpHelper.cs | 7 +++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs index 1f467fe..7cb165c 100644 --- a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs +++ b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs @@ -16,7 +16,7 @@ internal static class Decipherer public static async Task DecipherWithVersionAsync(string cipher, string cipherVersion) { string jsUrl = string.Format("http://s.ytimg.com/yts/jsbin/player-{0}.js", cipherVersion); - string js = await HttpHelper.DownloadString(jsUrl); + string js = await HttpHelper.DownloadStringAsync(jsUrl); return DecipherJson(js, cipher); } #endif diff --git a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs index 5978d17..6cd955b 100644 --- a/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs +++ b/YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs @@ -37,25 +37,25 @@ public static void DecryptDownloadUrl(VideoInfo videoInfo) { string encryptedSignature = queries[SignatureQuery]; - string decrypted; + string decrypted; try { decrypted = GetDecipheredSignature(videoInfo.HtmlPlayerVersion, encryptedSignature); - } + } catch (Exception ex) { throw new YoutubeParseException("Could not decipher signature", ex); -} + } -videoInfo.DownloadUrl = HttpHelper.ReplaceQueryStringParameter(videoInfo.DownloadUrl, SignatureQuery, decrypted); + videoInfo.DownloadUrl = HttpHelper.ReplaceQueryStringParameter(videoInfo.DownloadUrl, SignatureQuery, decrypted); videoInfo.RequiresDecryption = false; } } #if PORTABLE - public static async Task DecryptDownloadUrlAsyn(VideoInfo videoInfo) + public static async Task DecryptDownloadUrlAsync(VideoInfo videoInfo) { IDictionary queries = HttpHelper.ParseQueryString(videoInfo.DownloadUrl); @@ -68,7 +68,7 @@ public static async Task DecryptDownloadUrlAsyn(VideoInfo videoInfo) try { - decrypted = await GetDecipheredSignatureAync(videoInfo.HtmlPlayerVersion, encryptedSignature); + decrypted = await GetDecipheredSignatureAsync(videoInfo.HtmlPlayerVersion, encryptedSignature); } catch (Exception ex) @@ -104,7 +104,7 @@ public static async Task DecryptDownloadUrlAsyn(VideoInfo videoInfo) /// An error occurred while downloading the YouTube page html. /// /// The Youtube page could not be parsed. - private static IEnumerable GetDownloadUrls(string videoUrl, bool decryptSignature = true) + public static IEnumerable GetDownloadUrls(string videoUrl, bool decryptSignature = true) { try { @@ -145,11 +145,11 @@ private static IEnumerable GetDownloadUrls(string videoUrl, bool decr } #if PORTABLE - private static async Task> GetDownloadUrlsAsync(string videoUrl, bool decryptSignature = true) + public static async Task> GetDownloadUrlsAsync(string videoUrl, bool decryptSignature = true) { try { - var json = await LoadJsonAync(videoUrl); + var json = await LoadJsonAsync(videoUrl); string videoTitle = GetVideoTitle(json); @@ -165,7 +165,7 @@ private static async Task> GetDownloadUrlsAsync(string vi if (decryptSignature && info.RequiresDecryption) { - await DecryptDownloadUrlAync(info); + await DecryptDownloadUrlAsync(info); } } @@ -302,6 +302,16 @@ private static async Task GetDecipheredSignatureAsync(string htmlPlayerV return await Decipherer.DecipherWithVersionAsync(signature, htmlPlayerVersion); } #endif + private static string GetDecipheredSignature(string htmlPlayerVersion, string signature) + { + if (signature.Length == CorrectSignatureLength) + { + return signature; + } + + return Decipherer.DecipherWithVersion(signature, htmlPlayerVersion); + } + private static string GetHtml5PlayerVersion(JObject json) { var regex = new Regex(@"player-(.+?).js"); diff --git a/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs b/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs index c0bf017..9b89ca6 100644 --- a/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs +++ b/YoutubeExtractor/YoutubeExtractor/HttpHelper.cs @@ -22,8 +22,9 @@ public static async System.Threading.Tasks.Task DownloadStringAsync(stri null); return await ReadStreamFromResponseAsync(response); -#endif + } +#endif public static string DownloadString(string url) { #if PORTABLE @@ -135,7 +136,7 @@ private static async System.Threading.Tasks.Task ReadStreamFromResponseA } } } - } #endif + } +} -} \ No newline at end of file From 03bb5860bfa8265363e5aafab9681c333fdb6d9a Mon Sep 17 00:00:00 2001 From: mjdavy Date: Fri, 29 Jan 2016 16:25:58 -0500 Subject: [PATCH 3/5] Added Universal support --- .../ExampleApplication.Portable.csproj | 21 + .../ExampleApplication.Portable/Program.cs | 2 +- .../packages.config | 5 + .../ExampleApplication.csproj | 23 + .../ExampleApplication/Program.cs | 2 +- .../ExampleApplication/packages.config | 5 + .../ExampleApplication_Universal/App.xaml | 8 + .../ExampleApplication_Universal/App.xaml.cs | 108 +++++ .../ApplicationInsights.config | 3 + .../Assets/LockScreenLogo.scale-200.png | Bin 0 -> 1430 bytes .../Assets/SplashScreen.scale-200.png | Bin 0 -> 7700 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 2937 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 1647 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 1255 bytes .../Assets/StoreLogo.png | Bin 0 -> 1451 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 3204 bytes .../ExampleApplication_Universal.csproj | 150 ++++++ ...mpleApplication_Universal_TemporaryKey.pfx | Bin 0 -> 2452 bytes .../MainPage.xaml | 25 + .../MainPage.xaml.cs | 77 ++++ .../Package.appxmanifest | 49 ++ .../Properties/AssemblyInfo.cs | 29 ++ .../Properties/Default.rd.xml | 31 ++ .../ExampleApplication_Universal/project.json | 19 + .../YoutubeExtractor.Tests.csproj | 3 + YoutubeExtractor/YoutubeExtractor.sln | 110 ++++- .../YoutubeExtractor/Decipherer.cs | 2 +- .../YoutubeExtractor/DownloadUrlResolver.cs | 2 +- .../YoutubeExtractor/YoutubeExtractor.csproj | 20 + .../packages.YoutubeExtractor.config | 2 + .../packages.YoutubeExtractor_Portable.config | 6 +- .../AdaptiveType.cs | 9 + .../YoutubeExtractor_Universal/AudioType.cs | 14 + .../YoutubeExtractor_Universal/Decipherer.cs | 162 +++++++ .../DownloadUrlResolver.cs | 430 ++++++++++++++++++ .../YoutubeExtractor_Universal/HttpHelper.cs | 152 +++++++ .../Properties/AssemblyInfo.cs | 29 ++ .../YoutubeExtractor_Universal.rd.xml | 33 ++ .../YoutubeExtractor_Universal/VideoInfo.cs | 211 +++++++++ .../VideoNotAvailableException.cs | 18 + .../YoutubeExtractor_Universal/VideoType.cs | 22 + .../YoutubeExtractor_Universal.csproj | 139 ++++++ .../YoutubeParseException.cs | 18 + .../YoutubeExtractor_Universal/project.json | 17 + 44 files changed, 1948 insertions(+), 8 deletions(-) create mode 100644 YoutubeExtractor/ExampleApplication.Portable/packages.config create mode 100644 YoutubeExtractor/ExampleApplication/packages.config create mode 100644 YoutubeExtractor/ExampleApplication_Universal/App.xaml create mode 100644 YoutubeExtractor/ExampleApplication_Universal/App.xaml.cs create mode 100644 YoutubeExtractor/ExampleApplication_Universal/ApplicationInsights.config create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/LockScreenLogo.scale-200.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/SplashScreen.scale-200.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/Square150x150Logo.scale-200.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/Square44x44Logo.scale-200.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/StoreLogo.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Assets/Wide310x150Logo.scale-200.png create mode 100644 YoutubeExtractor/ExampleApplication_Universal/ExampleApplication_Universal.csproj create mode 100644 YoutubeExtractor/ExampleApplication_Universal/ExampleApplication_Universal_TemporaryKey.pfx create mode 100644 YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml create mode 100644 YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Package.appxmanifest create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Properties/AssemblyInfo.cs create mode 100644 YoutubeExtractor/ExampleApplication_Universal/Properties/Default.rd.xml create mode 100644 YoutubeExtractor/ExampleApplication_Universal/project.json create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/AdaptiveType.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/AudioType.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/Decipherer.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/DownloadUrlResolver.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/HttpHelper.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/Properties/AssemblyInfo.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/Properties/YoutubeExtractor_Universal.rd.xml create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/VideoInfo.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/VideoNotAvailableException.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/VideoType.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/YoutubeExtractor_Universal.csproj create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/YoutubeParseException.cs create mode 100644 YoutubeExtractor/YoutubeExtractor_Universal/project.json diff --git a/YoutubeExtractor/ExampleApplication.Portable/ExampleApplication.Portable.csproj b/YoutubeExtractor/ExampleApplication.Portable/ExampleApplication.Portable.csproj index a52208a..e43f42f 100644 --- a/YoutubeExtractor/ExampleApplication.Portable/ExampleApplication.Portable.csproj +++ b/YoutubeExtractor/ExampleApplication.Portable/ExampleApplication.Portable.csproj @@ -32,6 +32,26 @@ 4 + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll + True + + + ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll + True + @@ -41,6 +61,7 @@ + diff --git a/YoutubeExtractor/ExampleApplication.Portable/Program.cs b/YoutubeExtractor/ExampleApplication.Portable/Program.cs index 1b5383c..ce16423 100644 --- a/YoutubeExtractor/ExampleApplication.Portable/Program.cs +++ b/YoutubeExtractor/ExampleApplication.Portable/Program.cs @@ -15,7 +15,7 @@ private static void Main(string[] args) private static async void Run() { - IEnumerable videoInfos = await DownloadUrlResolver.GetDownloadUrlsAsync("http://www.youtube.com/watch?v=6bMmhKz6KXg"); + IEnumerable videoInfos = await DownloadUrlResolver.GetDownloadUrlsAsync("http://www.youtube.com/watch?v=fRh_vgS2dFE"); foreach (VideoInfo videoInfo in videoInfos) { diff --git a/YoutubeExtractor/ExampleApplication.Portable/packages.config b/YoutubeExtractor/ExampleApplication.Portable/packages.config new file mode 100644 index 0000000..abb6f27 --- /dev/null +++ b/YoutubeExtractor/ExampleApplication.Portable/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/YoutubeExtractor/ExampleApplication/ExampleApplication.csproj b/YoutubeExtractor/ExampleApplication/ExampleApplication.csproj index 2a54837..9c31c90 100644 --- a/YoutubeExtractor/ExampleApplication/ExampleApplication.csproj +++ b/YoutubeExtractor/ExampleApplication/ExampleApplication.csproj @@ -47,6 +47,26 @@ false + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll + True + + + ..\packages\NUnit.3.0.1\lib\net20\nunit.framework.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll + True + + + ..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll + True + @@ -60,6 +80,9 @@ YoutubeExtractor + + + + + PreserveNewest + + + + + + App.xaml + + + MainPage.xaml + + + + + + Designer + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + {ea7566f9-ac3b-4c81-b327-786821d5f96b} + YoutubeExtractor_Universal + + + + 14.0 + + + + \ No newline at end of file diff --git a/YoutubeExtractor/ExampleApplication_Universal/ExampleApplication_Universal_TemporaryKey.pfx b/YoutubeExtractor/ExampleApplication_Universal/ExampleApplication_Universal_TemporaryKey.pfx new file mode 100644 index 0000000000000000000000000000000000000000..194ea07c614ed73b37084e2d73c684e9510a7f45 GIT binary patch literal 2452 zcmZuz2{=^m8b4=djC~(j!XTzW>I^0`$W+LZipsP|*<~+ex%MMcNTg&ZO9&w?)@Ur* zvWFr|mV|_?75C`5x7&T5|2fb5o%i?qe(!s}=l#xk&w*gpFcgS}U{)tm}wZHKOokJW&ckU2OEe=gE1=*j9G+4v26dPk>p$$ zmaNwt{-8gRhC(rSU|>wg(|IEc5&<7XdXe*nx?Y^7q;M-xpKlLW*Qx3CDngpb-C*+f znU8Clvkk`MJYG}MaX3`fD_&vZg@HtS+^*uGNf1-fw>)~n;Jrk8QMJY>Z|sdbcb(5a ztzYac%;$`B<7wsigx|>=P$Dr!Q7*K4LH`!4)~D-{&2pXhV>zjaZ;zXrgJtVFSEv}oSu|(-3J(K6mj6x`DqtpH9nj5 zJMt%(j`fNqbXAr%9IA4BGw5?Q_i{IZmYd*g`8L2)CitP?jis;Geylifc1YjIEY8r2 z!Hmz3XkNE;)v;_H`@zWIK3ikZV&9rK)MfoFBspLQ9<6ZqF|o?cZ6%}~tGm=s_~jj^ zGt?Jaq`o%t%@ZebiEvgeWGJTNIGN5ZC!Edt`KoC{DP7NAn@>cnNNarPK$k&XT6pjM zR-e>-D0aH&_B9FanY=9DwX$-;@x%P9`}EP7M4Kl~T6LM7yQ`(bZDhrq@bmEF`weMc z)Q6hkKt8caq@(1Wqn7#Mb(Mijc#9KdAt_Rh!fKzW-g5@#ZL!vmMRktO1}vy*Mw+nk znS~lMldD}~f|9S4sRb9E6nuqlDg8JDzU!QLmsoqKc2qrMY_;<{6m+>TVS@_VtNFiuIqmyGOL58dJ{7kCQ91p6#;b zIt)Ftb%a}UqojzXk{$k={X#C6LmT@p@Fo2y(4yZmGWEjwo=M{Gv~Dx$O|2;$TMIF2 zx)Sn9RQuf4rTucs?rJp;8++~O^jd%Ap0z|f5%2IW?e$@~E8Q0hYsjQRX~VWhaXguB z{bZ7c_xqOajymI~76Mv1O%tT{?PJPL)tl;IN77 zqCMh7mpkrdxR=Q7f~KDLmfgv%mStSA!+mj6_;ChW!KQv|Z6@3-Iq=N9< zo+WZp5RZz?St1GrfP^S50SjP1f+&cBglH835@d>HmB)e@5LV#@NrvLbfn$IjB6I~@ z05`;J0CzwFIE9Scj5A=1AWg*g2Cg8S3xZvd#8-gJzoR4o!{0g&1X%&rfGY%}mHw=J z`-6a%#=vL^gce0i=b3AQ(0Kr{}ZoKmt&M7#P)$@I4R=R;Hm*APU8^J2ymc zr&v>@Hshp^(^-r!#QEnYAUhKP@shX^3T=!*^J9;hySm#sL*jy*r2P;{k*WwOQpqak z5vZ~aA;sVDpF&_z>hHuL6M%+6ZU9*a8wv(NAlo3C7S!!x)7?x-yx&vV0A?Lh?40!s z9w)fIQ=KF6@fxQ?$c3{kKmkBF2vEd)_ld^xr9AFlwW@R#xZ@)Dc8@! zWojU>xBOvtvlOajLaHt1@W-6$<^znqsCA=`iDI=mTI$k?l9PkK_}}`CeJ7|Nf} z;l5R>E7-M@1ru{rzbL3z=*ql5- zIvR=Qi%)K!ej@JM&Zv6Ala-!2Vzx_gWZk#3&ZbL@I-wQj@gm*bc08uzY5$#tu|b`N znThoo&*pirJS|d$*Ujsr8hKwdlmtHc6eL+3^;x0E$1|lBr%fk@%CPJ<@uLeg9u#AM z*JvKQZPaE*cv6j|SNNcARigu(ZpnD3(7u%OQOZw_&A$(kF+TBi=JX66pT40)l?k8W zOJG@hLywogL5mn`EpuO1&h-bB4oL84*Q(9Mn0BE_plDYNsbl`y2M3LZ3UFD zt(O8dA34O((j}9o_2-VrE18o7{`qxRgYXbDmPwuqjS|7&7Ve)y>EIP2=#A?kZ0J~_ m1@fXX2A5QC2be3Y4T^Y4)+WF6m;UVDIlTQ}gtUv_$G-uB+YEvL literal 0 HcmV?d00001 diff --git a/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml new file mode 100644 index 0000000..19dc3ca --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs new file mode 100644 index 0000000..8b03234 --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using YoutubeExtractor; + +// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace ExampleApplication_Universal +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainPage : Page + { + private IEnumerator videoEnumerator; + + public MainPage() + { + this.InitializeComponent(); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + GetVideoInfos(); + } + + private async void GetVideoInfos() + { + var videoInfos = await DownloadUrlResolver.GetDownloadUrlsAsync("http://www.youtube.com/watch?v=fRh_vgS2dFE"); + videoEnumerator = videoInfos.GetEnumerator(); + videoEnumerator.MoveNext(); + PlayVideo(); + } + + private void PlayVideo() + { + var currentVideo = videoEnumerator.Current; + this.info.Text = currentVideo.ToString(); + System.Diagnostics.Debug.WriteLine(currentVideo); + + if (currentVideo != null) + { + this.player.Source = new Uri(currentVideo.DownloadUrl); + } + } + + private void player_MediaEnded(object sender, RoutedEventArgs e) + { + System.Diagnostics.Debug.WriteLine("Media Ended"); + videoEnumerator.MoveNext(); + PlayVideo(); + } + + private void player_MediaFailed(object sender, ExceptionRoutedEventArgs e) + { + System.Diagnostics.Debug.WriteLine("Media failed"); + videoEnumerator.MoveNext(); + PlayVideo(); + } + + private void player_MediaOpened(object sender, RoutedEventArgs e) + { + System.Diagnostics.Debug.WriteLine("Media Opened"); + } + } +} diff --git a/YoutubeExtractor/ExampleApplication_Universal/Package.appxmanifest b/YoutubeExtractor/ExampleApplication_Universal/Package.appxmanifest new file mode 100644 index 0000000..3d84676 --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + + + ExampleApplication_Universal + Martin + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YoutubeExtractor/ExampleApplication_Universal/Properties/AssemblyInfo.cs b/YoutubeExtractor/ExampleApplication_Universal/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c318653 --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ExampleApplication_Universal")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ExampleApplication_Universal")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/YoutubeExtractor/ExampleApplication_Universal/Properties/Default.rd.xml b/YoutubeExtractor/ExampleApplication_Universal/Properties/Default.rd.xml new file mode 100644 index 0000000..80a960c --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/YoutubeExtractor/ExampleApplication_Universal/project.json b/YoutubeExtractor/ExampleApplication_Universal/project.json new file mode 100644 index 0000000..e3b2dba --- /dev/null +++ b/YoutubeExtractor/ExampleApplication_Universal/project.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "Microsoft.ApplicationInsights": "1.0.0", + "Microsoft.ApplicationInsights.PersistenceChannel": "1.0.0", + "Microsoft.ApplicationInsights.WindowsApps": "1.0.0", + "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" + }, + "frameworks": { + "uap10.0": {} + }, + "runtimes": { + "win10-arm": {}, + "win10-arm-aot": {}, + "win10-x86": {}, + "win10-x86-aot": {}, + "win10-x64": {}, + "win10-x64-aot": {} + } +} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor.Tests/YoutubeExtractor.Tests.csproj b/YoutubeExtractor/YoutubeExtractor.Tests/YoutubeExtractor.Tests.csproj index 9f50697..d01a686 100644 --- a/YoutubeExtractor/YoutubeExtractor.Tests/YoutubeExtractor.Tests.csproj +++ b/YoutubeExtractor/YoutubeExtractor.Tests/YoutubeExtractor.Tests.csproj @@ -55,6 +55,9 @@ YoutubeExtractor + + + + + + + + + + diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/VideoInfo.cs b/YoutubeExtractor/YoutubeExtractor_Universal/VideoInfo.cs new file mode 100644 index 0000000..e52db40 --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/VideoInfo.cs @@ -0,0 +1,211 @@ +using System.Collections.Generic; + +namespace YoutubeExtractor +{ + public class VideoInfo + { + internal static IEnumerable Defaults = new List + { + /* Non-adaptive */ + new VideoInfo(5, VideoType.Flash, 240, false, AudioType.Mp3, 64, AdaptiveType.None), + new VideoInfo(6, VideoType.Flash, 270, false, AudioType.Mp3, 64, AdaptiveType.None), + new VideoInfo(13, VideoType.Mobile, 0, false, AudioType.Aac, 0, AdaptiveType.None), + new VideoInfo(17, VideoType.Mobile, 144, false, AudioType.Aac, 24, AdaptiveType.None), + new VideoInfo(18, VideoType.Mp4, 360, false, AudioType.Aac, 96, AdaptiveType.None), + new VideoInfo(22, VideoType.Mp4, 720, false, AudioType.Aac, 192, AdaptiveType.None), + new VideoInfo(34, VideoType.Flash, 360, false, AudioType.Aac, 128, AdaptiveType.None), + new VideoInfo(35, VideoType.Flash, 480, false, AudioType.Aac, 128, AdaptiveType.None), + new VideoInfo(36, VideoType.Mobile, 240, false, AudioType.Aac, 38, AdaptiveType.None), + new VideoInfo(37, VideoType.Mp4, 1080, false, AudioType.Aac, 192, AdaptiveType.None), + new VideoInfo(38, VideoType.Mp4, 3072, false, AudioType.Aac, 192, AdaptiveType.None), + new VideoInfo(43, VideoType.WebM, 360, false, AudioType.Vorbis, 128, AdaptiveType.None), + new VideoInfo(44, VideoType.WebM, 480, false, AudioType.Vorbis, 128, AdaptiveType.None), + new VideoInfo(45, VideoType.WebM, 720, false, AudioType.Vorbis, 192, AdaptiveType.None), + new VideoInfo(46, VideoType.WebM, 1080, false, AudioType.Vorbis, 192, AdaptiveType.None), + + /* 3d */ + new VideoInfo(82, VideoType.Mp4, 360, true, AudioType.Aac, 96, AdaptiveType.None), + new VideoInfo(83, VideoType.Mp4, 240, true, AudioType.Aac, 96, AdaptiveType.None), + new VideoInfo(84, VideoType.Mp4, 720, true, AudioType.Aac, 152, AdaptiveType.None), + new VideoInfo(85, VideoType.Mp4, 520, true, AudioType.Aac, 152, AdaptiveType.None), + new VideoInfo(100, VideoType.WebM, 360, true, AudioType.Vorbis, 128, AdaptiveType.None), + new VideoInfo(101, VideoType.WebM, 360, true, AudioType.Vorbis, 192, AdaptiveType.None), + new VideoInfo(102, VideoType.WebM, 720, true, AudioType.Vorbis, 192, AdaptiveType.None), + + /* Adaptive (aka DASH) - Video */ + new VideoInfo(133, VideoType.Mp4, 240, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(134, VideoType.Mp4, 360, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(135, VideoType.Mp4, 480, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(136, VideoType.Mp4, 720, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(137, VideoType.Mp4, 1080, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(138, VideoType.Mp4, 2160, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(160, VideoType.Mp4, 144, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(242, VideoType.WebM, 240, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(243, VideoType.WebM, 360, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(244, VideoType.WebM, 480, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(247, VideoType.WebM, 720, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(248, VideoType.WebM, 1080, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(264, VideoType.Mp4, 1440, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(271, VideoType.WebM, 1440, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(272, VideoType.WebM, 2160, false, AudioType.Unknown, 0, AdaptiveType.Video), + new VideoInfo(278, VideoType.WebM, 144, false, AudioType.Unknown, 0, AdaptiveType.Video), + + /* Adaptive (aka DASH) - Audio */ + new VideoInfo(139, VideoType.Mp4, 0, false, AudioType.Aac, 48, AdaptiveType.Audio), + new VideoInfo(140, VideoType.Mp4, 0, false, AudioType.Aac, 128, AdaptiveType.Audio), + new VideoInfo(141, VideoType.Mp4, 0, false, AudioType.Aac, 256, AdaptiveType.Audio), + new VideoInfo(171, VideoType.WebM, 0, false, AudioType.Vorbis, 128, AdaptiveType.Audio), + new VideoInfo(172, VideoType.WebM, 0, false, AudioType.Vorbis, 192, AdaptiveType.Audio) + }; + + internal VideoInfo(int formatCode) + : this(formatCode, VideoType.Unknown, 0, false, AudioType.Unknown, 0, AdaptiveType.None) + { } + + internal VideoInfo(VideoInfo info) + : this(info.FormatCode, info.VideoType, info.Resolution, info.Is3D, info.AudioType, info.AudioBitrate, info.AdaptiveType) + { } + + private VideoInfo(int formatCode, VideoType videoType, int resolution, bool is3D, AudioType audioType, int audioBitrate, AdaptiveType adaptiveType) + { + this.FormatCode = formatCode; + this.VideoType = videoType; + this.Resolution = resolution; + this.Is3D = is3D; + this.AudioType = audioType; + this.AudioBitrate = audioBitrate; + this.AdaptiveType = adaptiveType; + } + + /// + /// Gets an enum indicating whether the format is adaptive or not. + /// + /// + /// AdaptiveType.Audio or AdaptiveType.Video if the format is adaptive; + /// otherwise, AdaptiveType.None. + /// + public AdaptiveType AdaptiveType { get; private set; } + + /// + /// The approximate audio bitrate in kbit/s. + /// + /// The approximate audio bitrate in kbit/s, or 0 if the bitrate is unknown. + public int AudioBitrate { get; private set; } + + /// + /// Gets the audio extension. + /// + /// The audio extension, or null if the audio extension is unknown. + public string AudioExtension + { + get + { + switch (this.AudioType) + { + case AudioType.Aac: + return ".aac"; + + case AudioType.Mp3: + return ".mp3"; + + case AudioType.Vorbis: + return ".ogg"; + } + + return null; + } + } + + /// + /// Gets the audio type (encoding). + /// + public AudioType AudioType { get; private set; } + + /// + /// Gets a value indicating whether the audio of this video can be extracted by YoutubeExtractor. + /// + /// + /// true if the audio of this video can be extracted by YoutubeExtractor; otherwise, false. + /// + public bool CanExtractAudio + { + get { return this.VideoType == VideoType.Flash; } + } + + /// + /// Gets the download URL. + /// + public string DownloadUrl { get; internal set; } + + /// + /// Gets the format code, that is used by YouTube internally to differentiate between + /// quality profiles. + /// + public int FormatCode { get; private set; } + + public bool Is3D { get; private set; } + + /// + /// Gets a value indicating whether this video info requires a signature decryption before + /// the download URL can be used. + /// + /// This can be achieved with the + /// + public bool RequiresDecryption { get; internal set; } + + /// + /// Gets the resolution of the video. + /// + /// The resolution of the video, or 0 if the resolution is unkown. + public int Resolution { get; private set; } + + /// + /// Gets the video title. + /// + public string Title { get; internal set; } + + /// + /// Gets the video extension. + /// + /// The video extension, or null if the video extension is unknown. + public string VideoExtension + { + get + { + switch (this.VideoType) + { + case VideoType.Mp4: + return ".mp4"; + + case VideoType.Mobile: + return ".3gp"; + + case VideoType.Flash: + return ".flv"; + + case VideoType.WebM: + return ".webm"; + } + + return null; + } + } + + /// + /// Gets the video type (container). + /// + public VideoType VideoType { get; private set; } + + /// + /// We use this in the method to + /// decrypt the signature + /// + /// + internal string HtmlPlayerVersion { get; set; } + + public override string ToString() + { + return string.Format("Full Title: {0}, Type: {1}, Resolution: {2}p", this.Title + this.VideoExtension, this.VideoType, this.Resolution); + } + } +} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/VideoNotAvailableException.cs b/YoutubeExtractor/YoutubeExtractor_Universal/VideoNotAvailableException.cs new file mode 100644 index 0000000..31b32a5 --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/VideoNotAvailableException.cs @@ -0,0 +1,18 @@ +using System; + +namespace YoutubeExtractor +{ + /// + /// The exception that is thrown when the video is not available for viewing. + /// This can happen when the uploader restricts the video to specific countries. + /// + public class VideoNotAvailableException : Exception + { + public VideoNotAvailableException() + { } + + public VideoNotAvailableException(string message) + : base(message) + { } + } +} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/VideoType.cs b/YoutubeExtractor/YoutubeExtractor_Universal/VideoType.cs new file mode 100644 index 0000000..7b8c2bf --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/VideoType.cs @@ -0,0 +1,22 @@ +namespace YoutubeExtractor +{ + /// + /// The video type. Also known as video container. + /// + public enum VideoType + { + /// + /// Video for mobile devices (3GP). + /// + Mobile, + + Flash, + Mp4, + WebM, + + /// + /// The video type is unknown. This can occur if YoutubeExtractor is not up-to-date. + /// + Unknown + } +} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeExtractor_Universal.csproj b/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeExtractor_Universal.csproj new file mode 100644 index 0000000..06add23 --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeExtractor_Universal.csproj @@ -0,0 +1,139 @@ + + + + + Debug + AnyCPU + {EA7566F9-AC3B-4C81-B327-786821D5F96B} + Library + Properties + YoutubeExtractor_Universal + YoutubeExtractor_Universal + en-US + UAP + 10.0.10240.0 + 10.0.10240.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + + + + + + + + + + + + + + + + + + + + 14.0 + + + + \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeParseException.cs b/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeParseException.cs new file mode 100644 index 0000000..a5db795 --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/YoutubeParseException.cs @@ -0,0 +1,18 @@ +using System; + +namespace YoutubeExtractor +{ + /// + /// + /// The exception that is thrown when the YouTube page could not be parsed. + /// This happens, when YouTube changes the structure of their page. + /// + /// Please report when this exception happens at www.github.com/flagbug/YoutubeExtractor/issues + /// + public class YoutubeParseException : Exception + { + public YoutubeParseException(string message, Exception innerException) + : base(message, innerException) + { } + } +} \ No newline at end of file diff --git a/YoutubeExtractor/YoutubeExtractor_Universal/project.json b/YoutubeExtractor/YoutubeExtractor_Universal/project.json new file mode 100644 index 0000000..2c3ee60 --- /dev/null +++ b/YoutubeExtractor/YoutubeExtractor_Universal/project.json @@ -0,0 +1,17 @@ +{ + "dependencies": { + "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0", + "Newtonsoft.Json": "8.0.2" + }, + "frameworks": { + "uap10.0": {} + }, + "runtimes": { + "win10-arm": {}, + "win10-arm-aot": {}, + "win10-x86": {}, + "win10-x86-aot": {}, + "win10-x64": {}, + "win10-x64-aot": {} + } +} \ No newline at end of file From 61714fe08d464e110c090dd390e36d40795565ca Mon Sep 17 00:00:00 2001 From: mjdavy Date: Tue, 2 Feb 2016 06:16:18 -0500 Subject: [PATCH 4/5] Bug fixes --- .../ExampleApplication_Universal/MainPage.xaml.cs | 6 +++--- YoutubeExtractor/YoutubeExtractor.sln | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs index 8b03234..9045d8c 100644 --- a/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs +++ b/YoutubeExtractor/ExampleApplication_Universal/MainPage.xaml.cs @@ -46,11 +46,11 @@ private async void GetVideoInfos() private void PlayVideo() { var currentVideo = videoEnumerator.Current; - this.info.Text = currentVideo.ToString(); - System.Diagnostics.Debug.WriteLine(currentVideo); - + if (currentVideo != null) { + this.info.Text = currentVideo.ToString(); + System.Diagnostics.Debug.WriteLine(currentVideo); this.player.Source = new Uri(currentVideo.DownloadUrl); } } diff --git a/YoutubeExtractor/YoutubeExtractor.sln b/YoutubeExtractor/YoutubeExtractor.sln index 72dd763..b9a5671 100644 --- a/YoutubeExtractor/YoutubeExtractor.sln +++ b/YoutubeExtractor/YoutubeExtractor.sln @@ -142,6 +142,7 @@ Global {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Debug|x86.Build.0 = Debug|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Debug|x86.Deploy.0 = Debug|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|Any CPU.ActiveCfg = Release|x86 + {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|Any CPU.Build.0 = Release|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.ActiveCfg = Release|ARM {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.Build.0 = Release|ARM {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.Deploy.0 = Release|ARM From 6fe7cf316513c5277cd57c208d6e4d3939105b89 Mon Sep 17 00:00:00 2001 From: mjdavy Date: Sun, 7 Feb 2016 10:01:41 -0500 Subject: [PATCH 5/5] updated release configuration --- YoutubeExtractor/YoutubeExtractor.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/YoutubeExtractor/YoutubeExtractor.sln b/YoutubeExtractor/YoutubeExtractor.sln index b9a5671..a65ad20 100644 --- a/YoutubeExtractor/YoutubeExtractor.sln +++ b/YoutubeExtractor/YoutubeExtractor.sln @@ -143,6 +143,7 @@ Global {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Debug|x86.Deploy.0 = Debug|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|Any CPU.ActiveCfg = Release|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|Any CPU.Build.0 = Release|x86 + {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|Any CPU.Deploy.0 = Release|x86 {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.ActiveCfg = Release|ARM {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.Build.0 = Release|ARM {E91C5DCD-BF46-4AE5-BBE5-011560E535A3}.Release|ARM.Deploy.0 = Release|ARM