public R <PlayResource> GetResourceById(AudioResource resource) { var result = ValidateUri(resource.ResourceId); if (!result) { return(result.Error); } else { var resData = result.Value; AudioResource finalResource; if (resource.ResourceTitle != null) { finalResource = resource; } else if (!string.IsNullOrWhiteSpace(resData.Title)) { finalResource = resource.WithName(resData.Title); } else { finalResource = resource.WithName(resource.ResourceId); } return(new PlayResource(resData.FullUri, finalResource)); } }
private R <PlayResource> YoutubeDlWrapped(AudioResource resource) { string title = null; string url = null; Log.Write(Log.Level.Debug, "YT Ruined!"); var result = YoutubeDlHelper.FindAndRunYoutubeDl(resource.ResourceId); if (!result.Ok) { return(result.Message); } var response = result.Value; title = response.Item1; var urlOptions = response.Item2; if (urlOptions.Count == 1) { url = urlOptions[0]; } else if (urlOptions.Count >= 1) { Uri[] uriList = urlOptions.Select(s => new Uri(s)).ToArray(); Uri bestMatch = uriList .FirstOrDefault(u => HttpUtility.ParseQueryString(u.Query) .GetValues("mime") .Any(x => x.StartsWith("audio", StringComparison.OrdinalIgnoreCase))); url = (bestMatch ?? uriList[0]).OriginalString; } if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(url)) { return("No youtube-dl response"); } Log.Write(Log.Level.Debug, "YT Saved!"); return(new PlayResource(url, resource.WithName(title))); }
private static R <PlayResource> YoutubeDlWrapped(AudioResource resource) { Log.Debug("Falling back to youtube-dl!"); var result = YoutubeDlHelper.FindAndRunYoutubeDl(resource.ResourceId); if (!result.Ok) { return(result.Error); } var response = result.Value; var title = response.title; var urlOptions = response.links; string url = null; if (urlOptions.Count == 1) { url = urlOptions[0]; } else if (urlOptions.Count >= 1) { Uri[] uriList = urlOptions.Select(s => new Uri(s)).ToArray(); Uri bestMatch = uriList .FirstOrDefault(u => HttpUtility.ParseQueryString(u.Query) .GetValues("mime")? .Any(x => x.StartsWith("audio", StringComparison.OrdinalIgnoreCase)) ?? false); url = (bestMatch ?? uriList[0]).OriginalString; } if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(url)) { return("No youtube-dl response"); } Log.Debug("youtube-dl succeeded!"); return(new PlayResource(url, resource.WithName(title))); }
public R <PlayResource> GetResourceById(AudioResource resource) { if (!WebWrapper.DownloadString(out string resulthtml, new Uri($"http://www.youtube.com/get_video_info?video_id={resource.ResourceId}&el=info"))) { return(RResultCode.NoConnection.ToString()); } var videoTypes = new List <VideoData>(); NameValueCollection dataParse = HttpUtility.ParseQueryString(resulthtml); string videoDataUnsplit = dataParse["url_encoded_fmt_stream_map"]; if (videoDataUnsplit != null) { string[] videoData = videoDataUnsplit.Split(','); foreach (string vdat in videoData) { NameValueCollection videoparse = HttpUtility.ParseQueryString(vdat); string vLink = videoparse["url"]; if (vLink == null) { continue; } string vType = videoparse["type"]; if (vType == null) { continue; } string vQuality = videoparse["quality"]; if (vQuality == null) { continue; } var vt = new VideoData() { Link = vLink, Codec = GetCodec(vType), Qualitydesciption = vQuality }; videoTypes.Add(vt); } } videoDataUnsplit = dataParse["adaptive_fmts"]; if (videoDataUnsplit != null) { string[] videoData = videoDataUnsplit.Split(','); foreach (string vdat in videoData) { NameValueCollection videoparse = HttpUtility.ParseQueryString(vdat); string vType = videoparse["type"]; if (vType == null) { continue; } bool audioOnly = false; if (vType.StartsWith("video/", StringComparison.Ordinal)) { continue; } else if (vType.StartsWith("audio/", StringComparison.Ordinal)) { audioOnly = true; } string vLink = videoparse["url"]; if (vLink == null) { continue; } var vt = new VideoData() { Codec = GetCodec(vType), Qualitydesciption = vType, Link = vLink }; if (audioOnly) { vt.AudioOnly = true; } else { vt.VideoOnly = true; } videoTypes.Add(vt); } } // Validation Process if (videoTypes.Count <= 0) { return(RResultCode.YtNoVideosExtracted.ToString()); } int codec = SelectStream(videoTypes); if (codec < 0) { return("No playable codec found"); } var result = ValidateMedia(videoTypes[codec]); if (!result) { if (string.IsNullOrWhiteSpace(data.YoutubedlPath)) { return(result.Message); } return(YoutubeDlWrapped(resource)); } return(new PlayResource(videoTypes[codec].Link, resource.ResourceTitle != null ? resource : resource.WithName(dataParse["title"] ?? $"<YT - no title : {resource.ResourceTitle}>"))); }
public R <PlayResource> GetResourceById(AudioResource resource) { var channel = resource.ResourceId; // request api token string jsonResponse; if (!WebWrapper.DownloadString(out jsonResponse, new Uri($"http://api.twitch.tv/api/channels/{channel}/access_token"), new Tuple <string, string>("Client-ID", twitchClientId))) { return(RResultCode.NoConnection.ToString()); } var jsonDict = (Dictionary <string, object>)Util.Serializer.DeserializeObject(jsonResponse); // request m3u8 file var token = Uri.EscapeUriString(jsonDict["token"].ToString()); var sig = jsonDict["sig"]; // guaranteed to be random, chosen by fair dice roll. var random = 4; string m3u8; if (!WebWrapper.DownloadString(out m3u8, new Uri($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}"))) { return(RResultCode.NoConnection.ToString()); } // parse m3u8 file var dataList = new List <StreamData>(); using (var reader = new System.IO.StringReader(m3u8)) { var header = reader.ReadLine(); if (string.IsNullOrEmpty(header) || header != "#EXTM3U") { return(RResultCode.TwitchMalformedM3u8File.ToString()); } while (true) { var blockInfo = reader.ReadLine(); if (string.IsNullOrEmpty(blockInfo)) { break; } var match = M3U8ExtMatch.Match(blockInfo); if (!match.Success) { continue; } switch (match.Groups[1].Value) { case "EXT-X-TWITCH-INFO": break; // Ignore twitch info line case "EXT-X-MEDIA": string streamInfo = reader.ReadLine(); Match infoMatch; if (string.IsNullOrEmpty(streamInfo) || !(infoMatch = M3U8ExtMatch.Match(streamInfo)).Success || infoMatch.Groups[1].Value != "EXT-X-STREAM-INF") { return(RResultCode.TwitchMalformedM3u8File.ToString()); } var streamData = new StreamData(); // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only" for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++) { string key = infoMatch.Groups[4].Captures[i].Value.ToUpper(CultureInfo.InvariantCulture); string value = infoMatch.Groups[5].Captures[i].Value; switch (key) { case "BANDWIDTH": streamData.Bandwidth = int.Parse(value, CultureInfo.InvariantCulture); break; case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break; case "VIDEO": StreamQuality quality; if (Enum.TryParse(TextUtil.StripQuotes(value), out quality)) { streamData.QualityType = quality; } else { streamData.QualityType = StreamQuality.unknown; } break; } } streamData.Url = reader.ReadLine(); dataList.Add(streamData); break; default: break; } } } // Validation Process if (dataList.Count <= 0) { return(RResultCode.TwitchNoStreamsExtracted.ToString()); } int codec = SelectStream(dataList); if (codec < 0) { return("The stream has no audio_only version."); } return(new PlayResource(dataList[codec].Url, resource.ResourceTitle != null ? resource : resource.WithName($"Twitch channel: {channel}"))); }
public R <PlayResource, LocalStr> GetResourceById(AudioResource resource) { var channel = resource.ResourceId; // request api token if (!WebWrapper.DownloadString(out string jsonResponse, new Uri($"https://api.twitch.tv/api/channels/{channel}/access_token"), ("Client-ID", TwitchClientId))) { return(new LocalStr(strings.error_net_no_connection)); } var jObj = JObject.Parse(jsonResponse); // request m3u8 file var tokenResult = jObj.TryCast <string>("token"); var sigResult = jObj.TryCast <string>("sig"); if (!tokenResult.Ok || !sigResult.Ok) { return(new LocalStr(strings.error_media_internal_invalid + " (tokenResult|sigResult)")); } var token = Uri.EscapeUriString(tokenResult.Value); var sig = sigResult.Value; // guaranteed to be random, chosen by fair dice roll. const int random = 4; if (!WebWrapper.DownloadString(out string m3u8, new Uri($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}"))) { return(new LocalStr(strings.error_net_no_connection)); } // parse m3u8 file var dataList = new List <StreamData>(); using (var reader = new System.IO.StringReader(m3u8)) { var header = reader.ReadLine(); if (string.IsNullOrEmpty(header) || header != "#EXTM3U") { return(new LocalStr(strings.error_media_internal_missing + " (m3uHeader)")); } while (true) { var blockInfo = reader.ReadLine(); if (string.IsNullOrEmpty(blockInfo)) { break; } var match = M3U8ExtMatch.Match(blockInfo); if (!match.Success) { continue; } switch (match.Groups[1].Value) { case "EXT-X-TWITCH-INFO": break; // Ignore twitch info line case "EXT-X-MEDIA": string streamInfo = reader.ReadLine(); Match infoMatch; if (string.IsNullOrEmpty(streamInfo) || !(infoMatch = M3U8ExtMatch.Match(streamInfo)).Success || infoMatch.Groups[1].Value != "EXT-X-STREAM-INF") { return(new LocalStr(strings.error_media_internal_missing + " (m3uStream)")); } var streamData = new StreamData(); // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only" for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++) { string key = infoMatch.Groups[4].Captures[i].Value.ToUpper(CultureInfo.InvariantCulture); string value = infoMatch.Groups[5].Captures[i].Value; switch (key) { case "BANDWIDTH": streamData.Bandwidth = int.Parse(value, CultureInfo.InvariantCulture); break; case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break; case "VIDEO": streamData.QualityType = Enum.TryParse(TextUtil.StripQuotes(value), out StreamQuality quality) ? quality : StreamQuality.unknown; break; } } streamData.Url = reader.ReadLine(); dataList.Add(streamData); break; } } } // Validation Process if (dataList.Count <= 0) { return(new LocalStr(strings.error_media_no_stream_extracted)); } int codec = SelectStream(dataList); if (codec < 0) { return(new LocalStr(strings.error_media_no_stream_extracted)); } return(new PlayResource(dataList[codec].Url, resource.ResourceTitle != null ? resource : resource.WithName($"Twitch channel: {channel}"))); }
private R <PlayResource, LocalStr> ResolveResourceInternal(AudioResource resource) { if (!WebWrapper.DownloadString(out string resulthtml, new Uri($"https://www.youtube.com/get_video_info?video_id={resource.ResourceId}"))) { return(new LocalStr(strings.error_net_no_connection)); } var videoTypes = new List <VideoData>(); var dataParse = ParseQueryString(resulthtml); if (dataParse.TryGetValue("url_encoded_fmt_stream_map", out var videoDataUnsplit)) { string[] videoData = videoDataUnsplit[0].Split(','); foreach (string vdat in videoData) { var videoparse = ParseQueryString(vdat); if (!videoparse.TryGetValue("url", out var vLink)) { continue; } if (!videoparse.TryGetValue("type", out var vType)) { continue; } if (!videoparse.TryGetValue("quality", out var vQuality)) { continue; } var vt = new VideoData() { Link = vLink[0], Codec = GetCodec(vType[0]), Qualitydesciption = vQuality[0] }; videoTypes.Add(vt); } } if (dataParse.TryGetValue("adaptive_fmts", out videoDataUnsplit)) { string[] videoData = videoDataUnsplit[0].Split(','); foreach (string vdat in videoData) { var videoparse = ParseQueryString(vdat); if (!videoparse.TryGetValue("type", out var vTypeArr)) { continue; } var vType = vTypeArr[0]; bool audioOnly = false; if (vType.StartsWith("video/", StringComparison.Ordinal)) { continue; } else if (vType.StartsWith("audio/", StringComparison.Ordinal)) { audioOnly = true; } if (!videoparse.TryGetValue("url", out var vLink)) { continue; } var vt = new VideoData() { Codec = GetCodec(vType), Qualitydesciption = vType, Link = vLink[0] }; if (audioOnly) { vt.AudioOnly = true; } else { vt.VideoOnly = true; } videoTypes.Add(vt); } } // Validation Process if (videoTypes.Count <= 0) { return(new LocalStr(strings.error_media_no_stream_extracted)); } int codec = SelectStream(videoTypes); if (codec < 0) { return(new LocalStr(strings.error_media_no_stream_extracted)); } var result = ValidateMedia(videoTypes[codec]); if (!result.Ok) { return(result.Error); } var title = dataParse.TryGetValue("title", out var titleArr) ? titleArr[0] : $"<YT - no title : {resource.ResourceTitle}>"; return(new PlayResource(videoTypes[codec].Link, resource.ResourceTitle != null ? resource : resource.WithName(title))); }