Skip to content

Instantly share code, notes, and snippets.

@gdkchan
Last active April 28, 2017 03:30
Show Gist options
  • Save gdkchan/fed0d38eeef241e7d3d243c9e5b788e3 to your computer and use it in GitHub Desktop.
Save gdkchan/fed0d38eeef241e7d3d243c9e5b788e3 to your computer and use it in GitHub Desktop.
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace UnBili
{
class Program
{
const string UserAgent = "Mozilla / 5.0(Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1";
const string AppKey = "6f90a59ac58a4123";
const string BiliKey = "0bfd84cc3940035173f35e6777508326";
static void Main(string[] args)
{
Console.WriteLine("Bilibili Stream URL generation test by gdkchan");
Console.WriteLine("Enter a video URL to extract the Stream:");
string URL = Console.ReadLine();
Console.WriteLine(string.Empty);
Console.WriteLine("Trying to download the webpage...");
string HTML = HttpGetGZip(URL);
string CId = Regex.Match(HTML, "cid=([0-9]*)").Groups[1].Value;
//Here we create the JSON URL
//We need to generate a signature based on some parameters (see GenSign function for details)
string[] Params = new string[6];
Params[0] = "cid=" + CId;
Params[1] = "appkey=" + AppKey;
Params[2] = "otype=json";
Params[3] = "type=mp4";
Params[4] = "quality=2";
Params[5] = "sign=" + GenSign(CId);
string JSONURL = "http://interface.bilibili.com/playurl?" + string.Join("&", Params);
Console.WriteLine("Trying to download JSON from \"" + JSONURL + "\"...");
Console.WriteLine(string.Empty);
string JSON = HttpGet(JSONURL);
//Extract all URLs from the JSON
MatchCollection URLs = Regex.Matches(JSON, "https?://[^\"]*");
if (URLs.Count > 0)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("URLs:");
Console.ResetColor();
foreach (Match StreamURL in URLs)
{
Console.WriteLine(StreamURL.Value);
}
}
else
{
//If we couldn't extract URLs then something is broken, show the returned message
Console.WriteLine("Can't extract, see JSON below:" + Environment.NewLine + JSON);
}
Console.WriteLine(string.Empty);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
static string GenSign(string ContentId)
{
//Generates the Signature from a given Video CId
/*
* Example of a signature before being hashed:
* appkey=6f90a59ac58a4123&cid=9388980&otype=json&quality=2&type=mp40bfd84cc3940035173f35e6777508326
* Both keys are constant for a given Player version. When the Player version changes, the base values
* for generating the keys or the primitives themselves may change.
*/
string[] Params = new string[5];
Params[0] = "appkey=" + AppKey;
Params[1] = "cid=" + ContentId;
Params[2] = "otype=json";
Params[3] = "quality=2";
Params[4] = "type=mp4";
string BaseSign = string.Join("&", Params) + BiliKey;
string Signature = GetMD5(BaseSign);
return Signature;
}
#region "Hash Utils"
static string GetMD5(string Input)
{
//Calculates the MD5 of a Input string
using (MD5 Hash = MD5.Create())
{
//Compute the MD5 Hash of the String on a Byte Array
byte[] Hashed = Hash.ComputeHash(Encoding.ASCII.GetBytes(Input));
//Convert Byte Array to a Hex String and returns it
StringBuilder Output = new StringBuilder();
foreach (byte HB in Hashed) Output.Append(HB.ToString("X2"));
return Output.ToString().ToLowerInvariant();
}
}
#endregion
#region "HTTP Utils"
private static string HttpGetGZip(string URL)
{
//This just downloads a Webpage using HTTP Get and the Firefox User Agent
WebRequest Request = WebRequest.Create(URL);
((HttpWebRequest)Request).UserAgent = UserAgent;
//Note: The page is GZip compressed, so we need to decompress it first
WebResponse Response = Request.GetResponse();
using (GZipStream Reader = new GZipStream(Response.GetResponseStream(), CompressionMode.Decompress))
{
using (MemoryStream Output = new MemoryStream())
{
byte[] Buffer = new byte[0x1000]; //4KiB Buffer
int Length = 0;
while ((Length = Reader.Read(Buffer, 0, Buffer.Length)) > 0)
{
Output.Write(Buffer, 0, Length);
}
return Encoding.UTF8.GetString(Output.ToArray());
}
}
}
private static string HttpGet(string URL)
{
//Same thing of the function above, but without the GZip compression
WebRequest Request = WebRequest.Create(URL);
((HttpWebRequest)Request).UserAgent = UserAgent;
WebResponse Response = Request.GetResponse();
StreamReader Reader = new StreamReader(Response.GetResponseStream());
return Reader.ReadToEnd();
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment