private static R <PlayResource, LocalStr> ParseLiveData(AudioResource resource, JsonPlayerResponse parsed) { var webListResponse = WebWrapper.GetResponse(new Uri(parsed.streamingData.hlsManifestUrl), response => { return(AudioTags.M3uReader.TryGetData(response.GetResponseStream()).OkOr(null)); }); if (webListResponse.Ok) { const string AacHe = "mp4a.40.5"; const string AacLc = "mp4a.40.2"; var webList = webListResponse.Value; var streamPref = from item in webList let codecs = item.StreamMeta != null?StreamCodecMatch.Match(item.StreamMeta).Groups[1].Value : "" let codecPref = codecs.Contains(AacLc) ? 0 : codecs.Contains(AacHe) ? 1 : 2 let bitrate = item.StreamMeta != null?int.Parse(StreamBitrateMatch.Match(item.StreamMeta).Groups[1].Value) : int.MaxValue orderby codecPref, bitrate ascending select item; var streamSelect = streamPref.FirstOrDefault(); if (streamSelect != null) { if (resource.ResourceTitle == null) { resource = resource.WithTitle(StringNormalize.Normalize(parsed.videoDetails.title)); } return(new PlayResource(streamSelect.TrackUrl, resource)); } } return(new LocalStr(strings.error_media_no_stream_extracted)); }
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("player_response", out var playerData)) { var parsed = JsonConvert.DeserializeObject <JsonPlayerResponse>(playerData[0]); Log.Debug("Extracted data: {@playerData}", parsed); if (parsed?.videoDetails != null) { if (resource.ResourceTitle == null) { resource = resource.WithTitle(StringNormalize.Normalize(parsed.videoDetails.title)); } bool isLive = parsed.videoDetails.isLive ?? false; if (isLive && parsed.streamingData?.hlsManifestUrl != null) { return(ParseLiveData(resource, parsed)); } else if (isLive) { Log.Warn("Live stream without hls stream data"); } ParsePlayerData(parsed, videoTypes); } } if (dataParse.TryGetValue("url_encoded_fmt_stream_map", out var videoDataUnsplit)) { ParseEncodedFmt(videoDataUnsplit, videoTypes); } if (dataParse.TryGetValue("adaptive_fmts", out videoDataUnsplit)) { ParseAdaptiveFmt(videoDataUnsplit, videoTypes); } // 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); } if (resource.ResourceTitle == null) { resource = resource.WithTitle($"<YT - no title : {resource.ResourceId}>"); } return(new PlayResource(videoTypes[codec].Link, resource)); }
private static R <PlayResource, LocalStr> YoutubeDlWrapped(AudioResource resource) { R <JsonYtdlDump, LocalStr> GetResourceInfo(string id) { var result = YoutubeDlHelper.GetSingleVideo(id); if (!result.Ok) { return(result.Error); } return(result.Value); } Log.Debug("Falling back to youtube-dl!"); // Get first try information var resourceInfo = GetResourceInfo(resource.ResourceId); if (!resourceInfo.Ok) { return(resourceInfo.Error); } var response = resourceInfo.Value; var formats = YoutubeDlHelper.SortBest(response.formats); if (formats.Count == 0) { return(new LocalStr(strings.error_ytdl_empty_response)); } string url = formats[0]?.url; Log.Trace("Found the following audio codec possibilities:"); foreach (var f in formats) { if (f.acodec != "none") { Log.Trace("Result: abr={0} acodec={1} vcodec={2} url={3}", f.abr, f.acodec, f.vcodec, f.url); } } Log.Trace("Using version with {0}kbit/s", formats[0]?.abr); // Resource is broken if (string.IsNullOrEmpty(url)) { return(new LocalStr(strings.error_ytdl_empty_response)); } // Check if URL actually works. If not, return an error. var resp = new HttpClient().GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result; if (!resp.IsSuccessStatusCode) { Log.Info("Download URL generated by youtube-dl responds with non-200 status code: " + resp.StatusCode); return(new LocalStr("Youtube-URL for song is broken.")); } if (resource.ResourceTitle == null) { resource = resource.WithTitle(StringNormalize.Normalize(response.title) ?? $"Youtube-{resource.ResourceId}"); } Log.Debug("youtube-dl succeeded!"); return(new PlayResource(url, resource)); }