Skip to content

Instantly share code, notes, and snippets.

@paxbun
Created May 17, 2022 08:58
Show Gist options
  • Save paxbun/226ecc1ed8640e04c2c419350142d399 to your computer and use it in GitHub Desktop.
Save paxbun/226ecc1ed8640e04c2c419350142d399 to your computer and use it in GitHub Desktop.
using System.Collections.Concurrent;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.RegularExpressions;
using Microsoft.Azure.Management.Media.Models;
using Microsoft.IdentityModel.Tokens;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.AllowAnyOrigin();
});
});
Regex videoPattern = new(@"^/(.*)\.ism/(.*)$");
ConcurrentDictionary<string, (string contentType, byte[] bytes)> cache = new();
const string originalVideoPathPrefix = "https://mediaservicesresourcename-region.streaming.media.azure.net/.../....ism/";
const string playReadyVerificationKey = /* base64 */ ;
const string widevineVerificationKey = /* base64 */ ;
Guid streamingLocatorId = Guid.Parse(/* streaming locator ID here */);
string GetTokenFromVerificationKey(string verificationKey)
{
const string issuer = "issuer";
const string audience = "audience";
SymmetricSecurityKey key = new(Convert.FromBase64String(verificationKey));
SigningCredentials cred = new(key, SecurityAlgorithms.HmacSha256, SecurityAlgorithms.Sha256Digest);
Claim[] claims =
{
new(ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim.ClaimType, streamingLocatorId.ToString())
};
JwtSecurityToken token = new(
issuer: issuer,
audience: audience,
claims: claims,
notBefore: DateTime.Now.AddMinutes(-5),
expires: DateTime.Now.AddMinutes(60),
signingCredentials: cred);
JwtSecurityTokenHandler handler = new();
return handler.WriteToken(token);
}
Console.WriteLine("PlayReady: Bearer={0}\n", GetTokenFromVerificationKey(playReadyVerificationKey));
Console.WriteLine("Widevine: Bearer={0}\n", GetTokenFromVerificationKey(widevineVerificationKey));
async Task<(string contentType, byte[] bytes)> SendRequestToOriginal(string postfix)
{
if (cache.TryGetValue(postfix, out (string, byte[]) result))
return result;
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(new Uri(originalVideoPathPrefix + postfix));
Stream stream = response.Content.ReadAsStream();
long numBytes = response.Content.Headers.ContentLength ?? 0;
byte[] bytes = new byte[numBytes];
long numBytesRead = 0;
while (numBytesRead < numBytes)
{
numBytesRead += await stream.ReadAsync(bytes, (int) numBytesRead, (int) (numBytes - numBytesRead));
}
string contentType = response.Content.Headers.ContentType?.ToString() ?? "";
cache[postfix] = (contentType, bytes);
return (contentType, bytes);
}
WebApplication app = builder.Build();
app.UseCors();
app.Run(async context =>
{
Match match = videoPattern.Match(context.Request.Path);
if (match.Success)
{
string postfix = match.Groups[2].Value;
var (contentType, bytes) = await SendRequestToOriginal(postfix);
Console.WriteLine("Processed: {0} {1}: {2}, {3} bytes", context.Request.Method, postfix, contentType,
bytes.Length);
context.Response.ContentType = contentType;
context.Response.ContentLength = bytes.Length;
await context.Response.BodyWriter.WriteAsync(bytes);
}
else
{
Console.WriteLine("Ignored: {0} {1}", context.Request.Method, context.Request.Path);
}
});
app.Run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment