Last active
October 22, 2019 07:02
-
-
Save gtk2k/4de29765b3f08bba21b8b39be7e9e980 to your computer and use it in GitHub Desktop.
Youtubeの動画URLを取得する(Live対応)
This file contains 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 Newtonsoft.Json.Linq; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
class YoutubeURL | |
{ | |
public static async Task<URLs> Get(string videoId) | |
{ | |
var raw = await GetWebStringAsync($"https://www.youtube.com/get_video_info?&video_id={videoId}"); | |
var html = await GetWebStringAsync($"https://www.youtube.com/watch?v={videoId}"); | |
var jsURL = new Regex(@"/yts/jsbin/player_ias-.+\.js").Match(html); | |
if (string.IsNullOrEmpty(raw) || jsURL == null) throw new Exception("Get error."); | |
var js = await GetWebStringAsync($"https://www.youtube.com{jsURL}"); | |
var data = parse(raw); | |
dynamic res = data["player_response"]; | |
dynamic streamingData = res["streamingData"]; | |
return new URLs | |
{ | |
Formats = data.ContainsKey("url_encoded_fmt_stream_map") ? parse2(js, data["url_encoded_fmt_stream_map"]) : null, | |
AdaptiveFormats = data.ContainsKey("adaptive_fmts") ? parse2(js, data["adaptive_fmts"]) : null, | |
DashURL = streamingData.dashManifestUrl, | |
HlsURL = streamingData.hlsManifestUrl | |
}; | |
} | |
private static Dictionary<string, object> parse(string s) | |
{ | |
return s.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) | |
.Select(x => Uri.UnescapeDataString(x).Split("=".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries)) | |
.Select(x => (k: x[0], v: x.Length < 2 ? null : x[1][0] == '{' ? JObject.Parse(x[1]) : float.TryParse(x[1], out float n) ? (object)n : x[1])) | |
.ToDictionary(x => x.k, x => x.v); | |
} | |
private static Dictionary<string, object>[] parse2(string js, object o) | |
{ | |
if (string.IsNullOrEmpty((string)o)) return null; | |
return ((string)o).Split(',').Select(x => execDecipher(js, parse(x))).ToArray(); | |
} | |
private static Dictionary<string, object> execDecipher(string js, Dictionary<string, object> x) | |
{ | |
if (x.ContainsKey("s") && x.ContainsKey("url")) x["url"] += $"&sig={Decipher(js, (string)x["s"])}"; // キー名をsignatureからsigに変更 | |
return x; | |
} | |
private static string Decipher(string js, string signature) | |
{ | |
var g = new Regex(@"\.split\(""""\);(.+?);return.+\.join\(""""\)").Match(js).Groups[1].Value; | |
var funcName = g.Split('.')[0]; | |
var ds = new Regex(funcName + @"=\{(.+?)\}\};", RegexOptions.Singleline).Match(js).ToString(); | |
var arr = signature.ToCharArray(); | |
foreach (var x in g.Split(new[] { funcName + "." }, StringSplitOptions.RemoveEmptyEntries)) | |
{ | |
var m = new Regex(@"(.+?)\(a,(\d+?)\)").Match(x); | |
var n = int.Parse(m.Groups[2].Value); | |
switch (new Regex(m.Groups[1].Value + @":function\(.+?\)\{(.+?)\}").Match(ds).Groups[1].Value) | |
{ | |
case "a.splice(0,b)": arr = arr.Skip(n).Take(arr.Length - n).ToArray(); break; | |
case "a.reverse()": arr = arr.Reverse().ToArray(); break; | |
case "var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c": | |
{ | |
var c = arr[0]; | |
arr[0] = arr[n % arr.Length]; | |
arr[n % arr.Length] = c; | |
break; | |
} | |
} | |
} | |
return new string(arr); | |
} | |
private static async Task<string> GetWebStringAsync(string url) | |
{ | |
var webReq = (HttpWebRequest)WebRequest.Create(url); | |
using (var response = await webReq.GetResponseAsync()) | |
{ | |
if (((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) return string.Empty; | |
using (var stream = response.GetResponseStream()) | |
{ | |
var reader = new StreamReader(stream, Encoding.UTF8); | |
return await reader.ReadToEndAsync(); | |
} | |
} | |
} | |
} | |
class URLs | |
{ | |
public bool IsLive { get { return DashURL != null || HlsURL != null; } } | |
public Dictionary<string, object>[] Formats; | |
public Dictionary<string, object>[] AdaptiveFormats; | |
public string DashURL; | |
public string HlsURL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment