Example #1
0
        public void DownloadEpisode(string URL)
        {
            string PageContent    = new WebClient().DownloadString(URL);
            string MediaID        = null;
            bool   MediaProtected = false;

            foreach (string video in Regex.Matches(PageContent, "\"Video:[^\"]*\":{([^}]*}[^}]*)}").Cast <Match>().Select(m => m.Groups[1].Value))
            {
                if (!video.Contains("\"slug\":\"" + URL.Substring(URL.LastIndexOf('/') + 1) + "\""))
                {
                    continue;
                }
                MediaProtected = !video.Contains("\"auth\":false,");
                if (!MediaProtected)
                {
                    MediaID = Regex.Match(video, "\"_id\":\"([^\"]*)").Groups[1].Value;
                }
                else
                {
                    MediaID = Regex.Match(video, "\"mediaID\":\"([^\"]*)").Groups[1].Value;
                }
                break;
            }
            if (MediaID == null)
            {
                Logger.Error("No MediaID obtained, aborting");
                return;
            }
            if (MediaProtected)
            {
                Logger.Info(URL + " is locked behind Adobe MSO/SP Auth, Getting it via Adobe shortAuthorize and authorize.json");

                // Get M3U8 URL
                AdobeAuthSP aasp = new AdobeAuthSP {
                    REQUESTERID    = "AdultSwim",
                    REQUESTINGPAGE = URL,
                    RESOURCEID     = "AdultSwim" //shouldnt this have some content data like disney and such? works but ehh
                };
                string    NGTVRes    = new WebClient().DownloadString("http://medium.ngtv.io/media/" + MediaID + "/tv");
                string    bulkAesURL = Regex.Match(NGTVRes, "bulkaes\":{.*?\"url\":\"([^\"]*)").Groups[1].Value;
                WebClient wc         = new WebClient();
                wc.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36");
                wc.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
                ServicePointManager.Expect100Continue = false;
                string M3U8Url = bulkAesURL + "?hdnea=" + Regex.Match(
                    wc.UploadString(
                        "http://token.vgtf.net/token/token_spe",
                        string.Join("&", new[] {
                    "path=" + WebUtility.UrlEncode(Regex.Match(bulkAesURL, "https?://[^/]+(.+)$").Groups[1].Value),
                    "videoId=" + MediaID,
                    "profile=tve",           //?
                    "accessTokenType=Adobe", //Adobe Auth
                    "accessToken=" + WebUtility.UrlEncode(aasp.ShortAuthorize())
                })
                        ),
                    "<token>(.*)?<\\/token>"
                    ).Groups[1].Value;

                // Chapters
                string bulkAesData = JToken.Parse(NGTVRes)["media"]["tv"]["bulkaes"].ToString();
                if (bulkAesData.Contains("\"start\""))
                {
                    Logger.Debug("Generating Chapters (based on json bulkaes data)");
                    PrepareDirectory(new[] { "temp" });
                    File.WriteAllText(
                        "temp/.chapters",
                        string.Join("\n",
                                    Regex.Matches(bulkAesData, "\"start\":([^,]*)").Cast <Match>().Select((x, i) => {
                        int num       = i + 1;
                        string numPad = num.ToString().PadLeft(2, '0');
                        return(string.Join("\n", new[] {
                            "CHAPTER" + numPad + "=" + TimeSpan.FromSeconds(double.Parse(x.Groups[1].Value)).ToString("hh\\:mm\\:ss\\.fff"),
                            "CHAPTER" + numPad + "NAME=Chapter " + num
                        }));
                    })
                                    )
                        );
                }

                // Download M3U8 File Contents and Get Resolutions
                string M3U8Content = new WebClient().DownloadString(M3U8Url);
                IOrderedEnumerable <Match> resolutions = Regex.Matches(M3U8Content, "#EXT-X-STREAM.*?BANDWIDTH=([^,]*),RESOLUTION=[^x]*x([^,]*).*\\s(.*)").Cast <Match>().OrderByDescending(f => int.Parse(f.Groups[1].Value)).OrderByDescending(f => int.Parse(f.Groups[2].Value));
                string Choice           = string.Empty;
                bool   CLIChoiceInvalid = Arguments.Quality != null && Arguments.Quality != "best" && !resolutions.Cast <Match>().Any(x => x.Groups[2].Value == Arguments.Quality);
                if (Arguments.Quality == null || CLIChoiceInvalid)
                {
                    if (CLIChoiceInvalid)
                    {
                        Logger.Error("--quality value \"" + Arguments.Quality + "\" is not valid for " + URL + "\n Please choose a new one below.");
                    }
                    VideoTracks(resolutions.Cast <Match>().Select((res, i) => res.Groups.Cast <Group>().Skip(1).Take(2).Select(x => x.Value).ToArray()).ToArray());
                    Choice = AskInput("Which resolution do you wish to download? (use # or 'best')").Trim('#');
                }
                else
                {
                    Choice = Arguments.Quality;
                }
                Match selected = Choice == "best" ? resolutions.First() : resolutions.ElementAt(int.Parse(Choice) - 1);
                Logger.Info("VIDEO: " + selected.Groups[2].Value + "p @ " + selected.Groups[1].Value + " bandwidth");

                string OutputFileName = "out";// Episode.Name_Serial + ".S" + Episode.TV_Season_Serial + "E" + Episode.TV_Episode_Serial + "." + Episode.TV_EpisodeTitle_Serial + "." + selected.Groups[1].Value + "p.WEB.[CODEC]";
                DownloadM3U8withMultiThreading(
                    bulkAesURL.Substring(0, bulkAesURL.LastIndexOf("/") + 1) + selected.Groups[3].Value,
                    OutputFileName
                    );

                // SHOULDNT NEED:

                //string text7 = new WebClient().DownloadString(chosenResM3u8Url);
                //File.WriteAllText("thing.m3u8", text7);
                //string text8 = Regex.Match(chosenResM3u8Url, "(http.*/)").Groups[1].Value + "seg.key";
                //string text9 = Regex.Match(text7, "(#EXT-X-KEY:METHOD=AES-128.*)").Groups[1].Value.Replace("seg.key", text8);
                //List<string> list3 = new List<string>();
                //int num2 = 0;
                //foreach (Match match3 in Regex.Matches(text7, "(#EXTINF[^,]+,.*?)#EXT-X-(DISCONTINUITY|ENDLIST)", RegexOptions.Singleline)) {
                //    string text11 = "output_seg" + num2.ToString();
                //    list3.Add(text11);
                //    using (StreamWriter streamWriter2 = new StreamWriter(text11 + ".m3u8")) {
                //        streamWriter2.Write("#EXTM3U\n#EXT-X-TARGETDURATION:6\n#EXT-X-VERSION:4\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-PLAYLIST-TYPE:VOD\n" + text9 + "\n" + Regex.Replace(match3.Groups[1].Value, "^([^#].*)$", Regex.Match(chosenResM3u8Url, "(http.*/)").Groups[1].Value + "$1", RegexOptions.Multiline) + Environment.NewLine + "#EXT-X-ENDLIST");
                //        streamWriter2.Flush();
                //    }
                //    num2++;
                //}
                //foreach (string text12 in list3) {
                //    if (RunEXE("ffmpeg.exe", "-protocol_whitelist file,http,https,tcp,tls,crypto -y -hide_banner -i \"" + text12 + ".m3u8\" -c copy \"output_seg" + list3.IndexOf(text12).ToString() + ".ts\"") != 0) {
                //        Console.WriteLine("[!ERROR]: ffmpeg closed with an error code :(");
                //    }
                //    RunEXE("ccextractor/ccextractorwin.exe", "--no_progress_bar \"output_seg" + list3.IndexOf(text12).ToString() + ".ts\"");
                //}

                //string text13 = "--output \"output.mkv\" ";
                //foreach (string str2 in list3) {
                //    text13 += "\"" + str2 + ".ts\" + ";
                //}
                //text13 = text13.Trim(new[] { ' ', '+' });
                //foreach (string text14 in list3) {
                //    if (File.Exists(text14 + ".srt")) {
                //        text13 += " --default-track 0:false --sub-charset 0:UTF-8 \"" + text14 + ".srt\" + ";
                //    } else {
                //        Console.WriteLine("[!ERROR]: Subtitle segment " + list3.IndexOf(text14).ToString() + " missing. Subtitles might be out of sync.");
                //    }
                //}
                //text13 = text13.Trim(new[] { ' ', '+' });
                //if (File.Exists("output_chapters.txt")) {
                //    text13 = text13 + " --chapters \"output_chapters.txt\"";
                //}
                //Console.WriteLine("Muxing...");
                //if (RunEXE("mkvmerge/mkvmerge.exe", text13) != 0) {
                //    Console.WriteLine("MKVMerge exited with error.");
                //    return false;
                //}
                //foreach (string str3 in list3) {
                //    File.Delete(str3 + ".ts");
                //    File.Delete(str3 + ".srt");
                //    File.Delete(str3 + ".m3u8");
                //}
                //File.Delete("output_chapters.txt");
            }
            else
            {
                Logger.Info(URL + " is FREE! Downloading directly via apiv1");

                JToken APIRes  = JToken.Parse(new WebClient().DownloadString("https://www.adultswim.com/api/shows/v1/videos/" + MediaID + "?fields=title%2Ccollection_title%2Cstream%2Csegments"))["data"]["video"];
                string PageRes = new WebClient().DownloadString(APIRes["url"].ToString());

                string chaptersdata = new WebClient().DownloadString(APIRes["stream"]["assets"].Where(asset => asset["url"].ToString().Contains("cue_points")).First()["url"].ToString());

                // Subtitles
                string vtt = APIRes["stream"]["assets"].Where(asset => asset["mime_type"].ToString() == "text/vtt").First()["url"].ToString();
                if (vtt != string.Empty)
                {
                    DownloadSRT(vtt);
                }

                // Chapters
                if (chaptersdata.Contains("<start time=\""))
                {
                    Logger.Debug("Generating Chapters (based on xml adcue data)");
                    PrepareDirectory(new[] { "temp" });
                    File.WriteAllText(
                        "temp/.chapters",
                        string.Join("\n",
                                    Regex.Matches(chaptersdata, "start time=\"([^:]*):([^:]*):([^:]*):([^\"]*)").Cast <Match>().Select((x, i) => {
                        int num       = i + 1;
                        string numPad = num.ToString().PadLeft(2, '0');
                        return(string.Join("\n", new[] {
                            "CHAPTER" + numPad + "=" + x.Groups[1].Value + ":" + x.Groups[2].Value + ":" + x.Groups[2].Value + "." + x.Groups[2].Value + "0",
                            "CHAPTER" + numPad + "NAME=Chapter " + num
                        }));
                    })
                                    )
                        );
                }

                string Title         = Regex.Replace(APIRes["collection_title"].ToString(), "[/\\*!?,.\'\"()<>:|]", string.Empty).Replace(" ", ".");
                string EpisodeName   = Regex.Replace(APIRes["title"].ToString(), "[/\\*!?,.\'\"()<>:|]", string.Empty).Replace(" ", ".");
                string SeasonNumber  = "0";
                Match  EpisodeFind   = Regex.Match(PageRes, "EP[^<]*?<!-- -->([^<]*)<!-- -->[^<]*?<\\/span><span>" + APIRes["title"].ToString() + "<\\/span>");
                string EpisodeNumber = EpisodeFind.Groups[1].Value;

                int[] Seasons = Regex.Matches(PageRes, "Season ([^<]*)<\\/h3>").Cast <Match>().Select(x => int.Parse(x.Groups[1].Value)).ToArray();
                for (int i = 0; i < Seasons.Length; i++)
                {
                    int Season           = Seasons[i];
                    int EpisodeTextIndex = PageRes.IndexOf(EpisodeFind.Value);
                    // If the Episode text is after the current season text
                    if (EpisodeTextIndex > PageRes.IndexOf("Season " + Season + "<\\/h3>"))
                    {
                        // and If its the last season number found, then it must be the one.
                        // or
                        // and if the episode is before the next season number it must be the one.
                        if (i == Seasons.Length - 1 || EpisodeTextIndex < PageRes.IndexOf("Season " + Seasons[i + 1] + "<\\/h3>"))
                        {
                            SeasonNumber = Season.ToString();
                        }
                    }
                }

                string OutputFilename = Title + ".S" + SeasonNumber.ToString().PadLeft(2, '0') + "E" + EpisodeNumber.PadLeft(2, '0') + "." + EpisodeName + ".[QUALITY]p.WEB.[CODEC]";
                Logger.Debug(OutputFilename);

                DownloadM3U8withMultiThreading(
                    ChooseResolutionFromM3U8(
                        Regex.Match(new WebClient().DownloadString("https://www.adultswim.com/api/shows/v1/media/" + MediaID + "/desktop"), "url\":\"([^\"]*)").Groups[1].Value
                        ),
                    OutputFilename
                    );
            }
        }
Example #2
0
        private static bool DownloadEpisodes(Title[] Episodes)
        {
            int CurrEpisode   = 0;
            int TotalEpisodes = Episodes.Length;

            foreach (Title Episode in Episodes)
            {
                CurrEpisode++;
                Logger.Info("Downloading Episode #" + CurrEpisode + "/" + TotalEpisodes + " - " + Episode.ID);
                JObject VideoData = (JObject)Fetch(
                    string.Join("/", new string[] {
                    "http://api.contents.watchabc.go.com/vp2/ws/s/contents/3000/videos",
                    BrandID,    //brand_id
                    "001",
                    "-1",
                    "-1",       //show_id?
                    "-1",
                    Episode.ID, //video_id
                    "-1",
                    "-1.json"
                }),
                    string.Empty,
                    "json"
                    );

                string M3U8Url = null;
                // No DRM, No Adobe Auth, 100% open
                if (Episode.AccessLevel == 0)
                {
                    Logger.Info("S" + Episode.TV_Season_Serial + "E" + Episode.TV_Episode_Serial + " is FREE! Getting it directly via authorize.json");
                    do
                    {
                        try {
                            JToken AuthorizeRes = JObject.Parse(new WebClient {
                                Encoding = Encoding.UTF8,
                                Headers  = { {
                                                 HttpRequestHeader.ContentType,
                                                 "application/x-www-form-urlencoded"
                                             }, {
                                                 HttpRequestHeader.Referer,
                                                 "http://cdn1.edgedatg.com/aws/apps/datg/web-player-unity/1.0.6.13/swf/player_vod.swf"
                                             } }
                            }.UploadString(
                                                                    "https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json",
                                                                    string.Join("&", new[] {
                                "device=" + DeviceID,
                                "video%5Ftype=lf",    //?
                                "video%5Fid=" + Episode.ID.Replace("_", "%5F"),
                                "brand=" + BrandID
                            })
                                                                    ));
                            bool error = AuthorizeRes.SelectToken("$.errors.count") != null;
                            M3U8Url = error ? null : VideoData.SelectToken("$.video[0].assets.asset[?(@.storagetype=='uplynk')].value").ToString() + "?" + AuthorizeRes.SelectToken("$.uplynkData.sessionKey").ToString() + "&ad.cping=1";
                            if (error)
                            {
                                Logger.Error("authorize.json call failed :( Attempting with Web Player Device ID Instead...");
                                DeviceID = "001";
                            }
                        } catch {
                            Logger.Error("authorize.json call failed :( Aborting...");
                            return(false);
                        }
                    } while (M3U8Url == null);
                }
                else
                {
                    Logger.Info("S" + Episode.TV_Season_Serial + "E" + Episode.TV_Episode_Serial + " is locked behind Adobe MSO/SP Auth, Getting it via Adobe shortAuthorize and authorize.json");
                    AdobeAuthSP aasp = new AdobeAuthSP {
                        REQUESTERID    = "DisneyChannels",
                        REQUESTINGPAGE = "https://disneynow.go.com" + Episode.URL,
                        RESOURCEID     = string.Concat(new[] {
                            "<rss version=\"2.0\" xmlns:media=\"http://search.yahoo.com/mrss/\"><channel><title>ABC</title><item><title>",
                            Episode.Name,
                            "</title><guid>",
                            Episode.ID,
                            "</guid><media:rating scheme=\"urn:v-chip\" /></item></channel></rss>"
                        })
                    };
                    M3U8Url = Regex.Match(
                        new WebClient {
                        Encoding = Encoding.UTF8,
                        Headers  = { {
                                         HttpRequestHeader.ContentType, "application/x-www-form-urlencoded"
                                     } }
                    }.UploadString(
                            "https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/playmanifest_secure.json",
                            string.Join("&", new[] {
                        "video_type=lf",               //?
                        "video_id=" + Episode.ID,
                        "adobe_requestor_id=DisneyXD", //Should this be "DisneyChannels" like with aasp?
                        "brand=" + BrandID,
                        "token_type=ap",               //ap = adobepass
                        "device=" + DeviceID,
                        "token=" + WebUtility.UrlEncode(aasp.ShortAuthorize())
                    })
                            ),
                        "\"([^\"]+m3u8[^\"]+)"
                        ).Groups[1].Value;
                }
                // Subtitles
                string ttml = (string)VideoData.SelectToken("$.video[0].closedcaption.src[?(@.type=='ttml')].value");
                if (ttml != string.Empty)
                {
                    DownloadSRT(ttml);
                }
                // Chapters
                if (VideoData.SelectTokens("$.video[0].cues.cue[*].value").Count() > 0)
                {
                    Logger.Debug("Generating Chapters (based on json cues)");
                    File.WriteAllText(
                        "temp/.chapters",
                        string.Join("\n",
                                    VideoData.SelectTokens("$.video[0].cues.cue[*].value").Select((x, i) => {
                        int num       = i + 1;
                        string numPad = num.ToString().PadLeft(2, '0');
                        return(string.Join("\n", new[] {
                            "CHAPTER" + numPad + "=" + TimeSpan.FromMilliseconds((double)x).ToString("hh\\:mm\\:ss\\.fff"),
                            "CHAPTER" + numPad + "NAME=Chapter " + num
                        }));
                    })
                                    )
                        );
                }
                // Download M3U8 File Contents and Get Resolutions
                string m3u8res = (string)Fetch(M3U8Url);
                IOrderedEnumerable <Match> resolutions = Regex.Matches(m3u8res, "#EXT-X-STREAM.*?RESOLUTION=[^x]*x([^,]*),BANDWIDTH=([^,]*).*\\s(.*)").Cast <Match>().OrderByDescending(f => int.Parse(f.Groups[2].Value)).OrderByDescending(f => int.Parse(f.Groups[1].Value));
                string Choice           = string.Empty;
                bool   CLIChoiceInvalid = Arguments.Quality != null && Arguments.Quality != "best" && !resolutions.Cast <Match>().Any(x => x.Groups[1].Value == Arguments.Quality);
                if (Arguments.Quality == null || CLIChoiceInvalid)
                {
                    if (CLIChoiceInvalid)
                    {
                        Logger.Error("--quality value \"" + Arguments.Quality + "\" is not valid for " + Episode.URL + "\n Please choose a new one below.");
                    }
                    VideoTracks(resolutions.Cast <Match>().Select((res, i) => res.Groups.Cast <Group>().Skip(1).Take(2).Select(x => x.Value).ToArray()).ToArray());
                    Choice = AskInput("Which resolution do you wish to download? (use # or 'best')").Trim('#');
                }
                else
                {
                    Choice = Arguments.Quality;
                }
                Match selected = Choice == "best" ? resolutions.First() : resolutions.ElementAt(int.Parse(Choice) - 1);
                Logger.Info("VIDEO: " + selected.Groups[1].Value + "p @ " + selected.Groups[2].Value + " bandwidth");
                string OutputFileName = Episode.Name_Serial + ".S" + Episode.TV_Season_Serial + "E" + Episode.TV_Episode_Serial + "." + Episode.TV_EpisodeTitle_Serial + ".[QUALITY]p.WEB.[CODEC]";
                DownloadM3U8withMultiThreading(
                    selected.Groups[3].Value,
                    OutputFileName
                    );
                Console.WriteLine("Done!");
            }
            return(true);
        }
Example #3
0
        public static void DownloadEpisode(string EpisodeID)
        {
            string VideoPage  = "http://www.simpsonsworld.com/video/" + EpisodeID.ToString();
            string UrlContent = new WebClient().DownloadString(VideoPage);

            string[] TrackTypes        = Regex.Matches(UrlContent, "var url_([^ ]*)").Cast <Match>().Select(t => t.Groups[1].Value).ToArray();
            string   SelectedTrackType = string.Empty;
            bool     CLIChoiceExists   = Arguments.TrackType == null || TrackTypes.Contains(Arguments.TrackType);

            if (Arguments.TrackType == null || !CLIChoiceExists)
            {
                if (!CLIChoiceExists)
                {
                    Logger.Error("--trackType value \"" + Arguments.TrackType + "\" is not valid for " + VideoPage + "\n Please choose a new one below.");
                }
                Logger.Info(
                    TrackTypes.Length + " Video Track Types:\n" +
                    " " + string.Join("\n ", TrackTypes.Select((t, i) => "#" + (i + 1) + " | " + t + " (" + t.Replace("x", ":").Replace("_commentary", " (+Commentary)") + " Version)"))
                    );
                SelectedTrackType = TrackTypes[int.Parse(AskInput("Which Track Type do you wish to download? (use #)").Trim('#')) - 1];
            }
            else
            {
                SelectedTrackType = Arguments.TrackType;
            }
            Logger.Info("VIDEOTYPE: " + SelectedTrackType.Replace("x", ":").Replace("_commentary", " (+Commentary)") + " Version (" + SelectedTrackType + ")");

            Match  ResourceData  = Regex.Match(UrlContent, "content_id:\"([^\"]*)\",content_title:\"([^\"]*)");
            string Resource_GUID = ResourceData.Groups[1].Value;

            AdobeAuthSP aasp = new AdobeAuthSP {
                REQUESTERID    = "fx",
                REQUESTINGPAGE = VideoPage,
                RESOURCEID     = "<rss version=\"2.0\" xmlns:media=\"http://search.yahoo.com/mrss/\"> <channel>     <title>fxx</title>     <item>         <title>" + ResourceData.Groups[2].Value + "</title>         <guid>" + Resource_GUID + "</guid>         <media:rating scheme=\"urn:v-chip\">" + Regex.Match(UrlContent, "data-guid=\"" + Resource_GUID + "\" data-rating=\"([^\"]*)").Groups[1].Value + "</media:rating>     </item> </channel></rss>"
            };

            string ShortToken = aasp.ShortAuthorize();

            if (ShortToken == null)
            {
                Console.WriteLine("[!ERROR]: Adobe Authorization FAILED. ABORTING!");
                return;
            }

            WebClient wc = new WebClient();

            wc.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36");
            wc.Headers.Add(HttpRequestHeader.Accept, "*/*");
            string postRes = wc.DownloadString(Regex.Match(UrlContent, SelectedTrackType + " = '([^']*)").Groups[1].Value + "&auto=true&sdk=PDK%205.8.6&auth=" + WebUtility.UrlEncode(ShortToken) + "&formats=m3u,mpeg4&format=SMIL&embedded=true&tracking=true");

            string videoUrl    = Regex.Match(postRes, "video src=\"([^\"]*)").Groups[1].Value;
            string SRTURL      = Regex.Match(postRes, "textstream src=\"([^\"]*.srt)").Groups[1].Value;
            string m3u8Content = new WebClient().DownloadString(videoUrl);

            MatchCollection resolutions      = Regex.Matches(m3u8Content, "#EXT-X-STREAM.*?BANDWIDTH=([^,]*),RESOLUTION=[^x]*x([^,]*).*\\s(.*)");
            string          Choice           = string.Empty;
            bool            CLIChoiceInvalid = Arguments.Quality != null && Arguments.Quality != "best" && !resolutions.Cast <Match>().Any(x => x.Groups[2].Value == Arguments.Quality);

            if (Arguments.Quality == null || CLIChoiceInvalid)
            {
                if (CLIChoiceInvalid)
                {
                    Logger.Error("--quality value \"" + Arguments.Quality + "\" is not valid for " + VideoPage + "\n Please choose a new one below.");
                }
                VideoTracks(resolutions.Cast <Match>().Select((res, i) => res.Groups.Cast <Group>().Reverse().Skip(1).Take(2).Select(x => x.Value).ToArray()).ToArray());
                Choice = AskInput("Which resolution do you wish to download? (use # or 'best')").Trim('#');
            }
            else
            {
                Choice = Arguments.Quality;
            }
            string chosenResM3u8Url = string.Empty;
            int    bestHeight       = 0;
            int    bestBandwidth    = 0;

            if (Choice == "best")
            {
                foreach (Match res in resolutions)
                {
                    int height    = int.Parse(res.Groups[2].Value);
                    int bandwidth = int.Parse(res.Groups[1].Value);
                    if (height > bestHeight && bandwidth > bestBandwidth)
                    {
                        bestHeight       = height;
                        bestBandwidth    = bandwidth;
                        chosenResM3u8Url = res.Groups[3].Value;
                        //QualityP = res.Groups[2].Value;
                    }
                }
            }
            else
            {
                Match res = resolutions[int.Parse(Choice) - 1];
                bestHeight       = int.Parse(res.Groups[2].Value);
                bestBandwidth    = int.Parse(res.Groups[1].Value);
                chosenResM3u8Url = res.Groups[3].Value;
                //QualityP = res.Groups[2].Value;
            }
            Logger.Info("VIDEO: " + bestHeight + "p @ " + bestBandwidth + " bandwidth");

            DownloadSRT(SRTURL);
            JToken TitleInfo = JToken.Parse(
                Regex.Match(
                    UrlContent.Replace("\"name\" : \"Matt \"Groening\" Nastuk\"", "\"name\" : \"Matt Nastuk\""),
                    "<script type='application\\/ld\\+json'>([\\s\\S]*?)<\\/script>"
                    ).Groups[1].Value
                );

            DownloadM3U8withMultiThreading(
                chosenResM3u8Url,
                TitleInfo["partOfSeries"]["name"].ToString().Replace(' ', '.') + ".S" + TitleInfo["partOfSeason"]["seasonNumber"].ToString().PadLeft(2, '0') + "E" + TitleInfo["episodeNumber"].ToString().PadLeft(2, '0') + "." + TitleInfo["name"].ToString().Replace(' ', '.') + "." + bestHeight + "p.WEB"
                );
            Console.WriteLine("Downloaded!\n");
        }