Created
May 17, 2022 08:58
-
-
Save paxbun/226ecc1ed8640e04c2c419350142d399 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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