Skip to content

Commit

Permalink
Fix #16: Use DelegatingClient to decipher encrypted videos
Browse files Browse the repository at this point in the history
A few fixes have been made as well to use ConfigureAwait(false)
in async code. This prevents hanging on encrypted videos in UI apps.
  • Loading branch information
jamesqo committed Sep 22, 2015
1 parent 2275b9e commit 0246ce6
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 15 deletions.
10 changes: 8 additions & 2 deletions src/libvideo.compat/DownloadUrlResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ public static IEnumerable<VideoInfo> GetDownloadUrls(string videoUrl, bool decry
}

public async static Task<IEnumerable<VideoInfo>> GetDownloadUrlsAsync(
string videoUrl, bool decryptSignature = true) =>
(await Service.GetAllVideosAsync(videoUrl)).Select(v => new VideoInfo(v));
string videoUrl, bool decryptSignature = true)
{
var videos = await Service
.GetAllVideosAsync(videoUrl)
.ConfigureAwait(false);

return videos.Select(v => new VideoInfo(v));
}

public static bool TryNormalizeYoutubeUrl(string url, out string normalizedUrl)
{
Expand Down
112 changes: 112 additions & 0 deletions src/libvideo/DelegatingClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace VideoLibrary
{
public class DelegatingClient : IDisposable
{
private bool disposed = false;
private readonly HttpClient client;

public DelegatingClient()
{
this.client = MakeClient();
}

#region IDisposable

~DelegatingClient()
{
Dispose(false);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposed)
return;

disposed = true;

if (disposing)
{
if (client != null)
client.Dispose();
}
}

#endregion

#region MakeClient/MakeHandler

private HttpClient MakeClient() =>
MakeClient(MakeHandler());

protected virtual HttpMessageHandler MakeHandler()
{
var handler = new HttpClientHandler();

if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression =
DecompressionMethods.GZip |
DecompressionMethods.Deflate;
}

return handler;
}

protected virtual HttpClient MakeClient(HttpMessageHandler handler)
{
return new HttpClient(handler);
}

#endregion

#region Synchronous wrappers

public HttpResponseMessage Get(string uri) =>
GetAsync(uri).GetAwaiter().GetResult();

public byte[] GetByteArray(string uri) =>
GetByteArrayAsync(uri).GetAwaiter().GetResult();

public Stream GetStream(string uri) =>
GetStreamAsync(uri).GetAwaiter().GetResult();

public string GetString(string uri) =>
GetStringAsync(uri).GetAwaiter().GetResult();

#endregion

#region HttpClient wrappers

// TODO: Support other kinds of HTTP requests,
// such as PUT, POST, DELETE, etc.

public Task<HttpResponseMessage> GetAsync(string uri) =>
client.GetAsync(uri);

public Task<byte[]> GetByteArrayAsync(string uri) =>
client.GetByteArrayAsync(uri);

public Task<Stream> GetStreamAsync(string uri) =>
client.GetStreamAsync(uri);

public Task<string> GetStringAsync(string uri) =>
client.GetStringAsync(uri);

#endregion
}
}
10 changes: 8 additions & 2 deletions src/libvideo/VideoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ protected virtual HttpClient MakeClient(HttpMessageHandler handler)

public async Task<byte[]> GetBytesAsync(Video video)
{
string uri = await video.GetUriAsync();
string uri = await
video.GetUriAsync()
.ConfigureAwait(false);

return await client
.GetByteArrayAsync(uri)
.ConfigureAwait(false);
Expand All @@ -81,7 +84,10 @@ public async Task<byte[]> GetBytesAsync(Video video)

public async Task<Stream> StreamAsync(Video video)
{
string uri = await video.GetUriAsync();
string uri = await
video.GetUriAsync()
.ConfigureAwait(false);

return await client
.GetStreamAsync(uri)
.ConfigureAwait(false);
Expand Down
4 changes: 2 additions & 2 deletions src/libvideo/YouTube.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ private bool TryNormalize(string videoUri, out string normalized)
var queries = map.Split(',').Select(Unscramble);

foreach (var query in queries)
yield return new YouTubeVideo(title, query, jsPlayer, sourceFactory);
yield return new YouTubeVideo(title, query, jsPlayer);

string adaptiveMap = Json.GetKey("adaptive_fmts", source);

queries = adaptiveMap.Split(',').Select(Unscramble);

foreach (var query in queries)
yield return new YouTubeVideo(title, query, jsPlayer, sourceFactory);
yield return new YouTubeVideo(title, query, jsPlayer);
}

// TODO: Consider making this static...
Expand Down
8 changes: 4 additions & 4 deletions src/libvideo/YouTubeVideo.Decrypt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ public partial class YouTubeVideo
private const string SigTrig = ".sig";

private readonly string jsPlayer;
private readonly Func<string, Task<string>> sourceFactory;

private async Task<string> DecryptAsync(string uri)
private async Task<string> DecryptAsync(string uri, Func<DelegatingClient> makeClient)
{
var query = new Query(uri);

string signature;
if (!query.TryGetValue("signature", out signature))
return uri;

string js =
await sourceFactory(jsPlayer)
string js =
await makeClient()
.GetStringAsync(jsPlayer)
.ConfigureAwait(false);

query["signature"] = DecryptedSignature(signature, js);
Expand Down
16 changes: 11 additions & 5 deletions src/libvideo/YouTubeVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ public partial class YouTubeVideo : Video
private bool encrypted;

internal YouTubeVideo(string title,
UnscrambledQuery query, string jsPlayer,
Func<string, Task<string>> sourceFactory)
UnscrambledQuery query, string jsPlayer)
{
this.Title = title;
this.uri = query.Uri;
this.jsPlayer = jsPlayer;
this.encrypted = query.IsEncrypted;
this.sourceFactory = sourceFactory;
this.FormatCode = int.Parse(new Query(uri)["itag"]);
}

Expand All @@ -31,11 +29,19 @@ public partial class YouTubeVideo : Video
public override string Uri =>
GetUriAsync().GetAwaiter().GetResult();

public async override Task<string> GetUriAsync()
public string GetUri(Func<DelegatingClient> makeClient) =>
GetUriAsync(makeClient).GetAwaiter().GetResult();

public override Task<string> GetUriAsync() =>
GetUriAsync(() => new DelegatingClient());

public async Task<string> GetUriAsync(Func<DelegatingClient> makeClient)
{
if (encrypted)
{
uri = await DecryptAsync(uri);
uri = await
DecryptAsync(uri, makeClient)
.ConfigureAwait(false);
encrypted = false;
}

Expand Down
1 change: 1 addition & 0 deletions src/libvideo/libvideo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<Compile Include="ServiceBase.cs" />
<Compile Include="Client.cs" />
<Compile Include="Helpers\UnscrambledQuery.cs" />
<Compile Include="DelegatingClient.cs" />
<Compile Include="YouTubeVideo.cs" />
<Compile Include="YouTubeVideo.Format.cs" />
<Compile Include="Video.cs" />
Expand Down

0 comments on commit 0246ce6

Please sign in to comment.