private static YTResult MakeYtResult(string trimmedLine)
        {
            JObject videoSearchJson = JsonConvert.DeserializeObject(trimmedLine) as JObject;
            // TimeSpan duration = TimeSpan.FromSeconds(double.Parse(videoSearchJson["duration"].ToString()));
            YTResult ytResult = new YTResult
            {
                author      = videoSearchJson["uploader_id"].ToString(),
                description = videoSearchJson["description"].ToString()
            };

            try
            {
                var duration = TimeSpan.FromSeconds(double.Parse(videoSearchJson["duration"].ToString()));
                ytResult.duration = duration.Hours > 0
                    ? $"{duration.Hours}:{duration.Minutes}:{duration.Seconds}"
                    : $"{duration.Minutes}:{duration.Seconds}";
            }
            catch (FormatException)
            {
                ytResult.duration = videoSearchJson["duration"].ToString();
            }
            ytResult.thumbnailURL = videoSearchJson["thumbnail"].ToString().Replace("/vi_webp/", "/vi/").Replace(".webp", ".jpg").Split('?')[0];
            ytResult.title        = videoSearchJson["title"].ToString();
            ytResult.URL          = videoSearchJson["webpage_url"].ToString().Replace("https://www.youtube.com", "");
            return(ytResult);
        }
        private void ListViewDownloadPressed(YTResult result)
        {
            // TODO
            // confirm dialogue if a videodata already exists for this song
            // delete pre-existing mp4 for overwriting

            if (selectedLevelVideo != null)
            {
                // present
                _simpleDialog.Init("Overwrite video?", $"Do you really want to delete \"{ selectedLevelVideo.title }\"\n and replace it with \"{result.title }\"", "Overwrite", "Cancel");
                _simpleDialog.didFinishEvent -= (SimpleDialogPromptViewController sender, bool delete) => { DismissViewController(_simpleDialog, null, false); if (delete)
                                                                                                            {
                                                                                                                StartDownload(result);
                                                                                                            }
                };
                _simpleDialog.didFinishEvent += (SimpleDialogPromptViewController sender, bool delete) => { DismissViewController(_simpleDialog, null, false); if (delete)
                                                                                                            {
                                                                                                                StartDownload(result);
                                                                                                            }
                };
                PresentViewController(_simpleDialog, null, false);
            }
            else
            {
                StartDownload(result);
            }
        }
Beispiel #3
0
 public VideoData(YTResult ytResult)
 {
     title        = ytResult.title;
     author       = ytResult.author;
     description  = ytResult.description;
     duration     = ytResult.duration;
     URL          = ytResult.URL;
     thumbnailURL = ytResult.thumbnailURL;
 }
Beispiel #4
0
 public VideoData(YTResult ytResult, IPreviewBeatmapLevel level)
 {
     title        = ytResult.title;
     author       = ytResult.author;
     description  = ytResult.description;
     duration     = ytResult.duration;
     URL          = ytResult.URL;
     thumbnailURL = ytResult.thumbnailURL;
     this.level   = level;
 }
 private void ListViewDownloadPressed(YTResult result)
 {
     if (selectedLevelVideo != null)
     {
         // present
         _simpleDialog.Init("Overwrite video?", $"Do you really want to delete \"{ selectedLevelVideo.title }\"\n and replace it with \"{result.title }\"", "Overwrite", "Cancel", delegate(int button) { if (button == 0)
                                                                                                                                                                                                          {
                                                                                                                                                                                                              QueueDownload(result);
                                                                                                                                                                                                          }
                            });
         PresentViewController(_simpleDialog, null, false);
     }
     else
     {
         QueueDownload(result);
     }
 }
        private void StartDownload(YTResult result)
        {
            // Delete existing
            if (selectedLevelVideo != null)
            {
                VideoLoader.Instance.RemoveVideo(selectedLevelVideo);

                switch (selectedLevelVideo.downloadState)
                {
                case DownloadState.Downloaded:
                    VideoLoader.Instance.DeleteVideo(selectedLevelVideo);
                    break;

                case DownloadState.Downloading:
                case DownloadState.Queued:
                    selectedLevelVideo.downloadState = DownloadState.Cancelled;     // stop download and dequeue
                    break;

                default:     // not downloaded, other
                    break;
                }
                selectedLevelVideo = null;
            }

            VideoData video = new VideoData(result);

            video.level        = selectedLevel;
            selectedLevelVideo = video;

            YouTubeDownloader.Instance.EnqueueVideo(video);
            VideoLoader.Instance.AddVideo(video);
            VideoLoader.SaveVideoToDisk(video);

            _videoDetailViewController.SetContent(video);
            _videoDetailViewController.UpdateContent();

            VideoDownloaderDownloadProgress(video);

            DismissViewController(_videoListViewController);
        }
        static IEnumerator SearchYoutubeCoroutine(string search, Action callback)
        {
            searchInProgress = true;
            searchResults    = new List <YTResult>();

            // get youtube results
            string url = "https://www.youtube.com/results?q=" + Uri.EscapeDataString(search);

            Plugin.logger.Info($"Searching with URL: {url}");
            UnityWebRequest request = UnityWebRequest.Get(url);

            yield return(request.SendWebRequest());

            if (request.error != null)
            {
                Plugin.logger.Warn("Search: An Error occured while searching. " + request.error);
                yield break;
            }

            Plugin.logger.Info(request.responseCode.ToString());

            MemoryStream stream = new MemoryStream(request.downloadHandler.data);

            HtmlDocument doc = new HtmlDocument();

            doc.Load(stream, System.Text.Encoding.UTF8);

            var videoNodes = doc.DocumentNode.SelectNodes("//*[contains(concat(' ', @class, ' '),'yt-lockup-video')]");

            Plugin.logger.Info(doc.Text);
            Dictionary <string, string> responseHeaders = request.GetResponseHeaders();

            foreach (KeyValuePair <string, string> keyValuePair in responseHeaders)
            {
                Plugin.logger.Info($"{keyValuePair.Key}: {keyValuePair.Value}");
            }

            if (videoNodes == null)
            {
                Plugin.logger.Info("Search: No results found matching: " + search);
            }
            else
            {
                for (int i = 0; i < Math.Min(MaxResults, videoNodes.Count); i++)
                {
                    var      node = HtmlNode.CreateNode(videoNodes[i].InnerHtml);
                    YTResult data = new YTResult();

                    // title
                    var titleNode = node.SelectSingleNode("//*[contains(concat(' ', @class, ' '),'yt-uix-tile-link')]");
                    if (titleNode == null)
                    {
                        continue;
                    }

                    data.title = HttpUtility.HtmlDecode(titleNode.InnerText);

                    // description
                    var descNode =
                        node.SelectSingleNode("//*[contains(concat(' ', @class, ' '),'yt-lockup-description')]");
                    if (descNode == null)
                    {
                        continue;
                    }

                    data.description = HttpUtility.HtmlDecode(descNode.InnerText);

                    // duration
                    var durationNode = node.SelectSingleNode("//*[contains(concat(' ', @class, ' '),'video-time')]");
                    if (durationNode == null)
                    {
                        // no duration means this is a live streamed video
                        continue;
                    }

                    data.duration = HttpUtility.HtmlDecode(durationNode.InnerText);

                    // author node
                    var authorNode =
                        node.SelectSingleNode("//*[contains(concat(' ', @class, ' '),'yt-lockup-byline')]");
                    if (authorNode == null)
                    {
                        continue;
                    }

                    data.author = HttpUtility.HtmlDecode(authorNode.InnerText);

                    // url
                    var urlNode = node.SelectSingleNode("//*[contains(concat(' ', @class, ' '),'yt-uix-tile-link')]");
                    if (urlNode == null)
                    {
                        continue;
                    }

                    data.URL = urlNode.Attributes["href"].Value;

                    var thumbnailNode = node.SelectSingleNode("//img");
                    if (thumbnailNode == null)
                    {
                        continue;
                    }

                    if (thumbnailNode.Attributes["data-thumb"] != null)
                    {
                        data.thumbnailURL = thumbnailNode.Attributes["data-thumb"].Value;
                    }
                    else
                    {
                        data.thumbnailURL = thumbnailNode.Attributes["src"].Value;
                    }

                    // append data to results
                    searchResults.Add(data);
                }
            }

            callback?.Invoke();
            searchInProgress = false;
        }
        //Make youtube process with my special parser to avoid stdout buffer overflows
        public static IEnumerator SearchYoutubeWithMyExeCoroutine(string query, Action callback, int resultsNumber = 5)
        {
            //If query is the same as the last one or the same as the default one I ran when the level was loaded, just wait for that to finish and use it
            if (searchResults != null && searchResults.query == query)
            {
                Plugin.logger.Debug("Waiting for other search");
                yield return(new WaitUntil(() => searchResults.isDone || searchResults.Count >= 5 || searchResults.query != query));

                Plugin.logger.Debug("Done Waiting for other search");
            }
            else
            {
                searchResults = new SearchResults(query);
                Plugin.logger.Debug("Starting Search");
                var searchProcess = new Process
                {
                    StartInfo =
                    {
                        // #define USEYTDLHELPER
#if USEYTDLHELPER
                        FileName  = Environment.CurrentDirectory + "/Youtube-dl/SelectJsonFromYoutubeDL.exe",
                        Arguments =
                            $"\"{Environment.CurrentDirectory + "/Youtube-dl/youtube-dl.exe"}\" \"{query}\" {resultsNumber}",
#else
                        FileName  = Environment.CurrentDirectory + "/Youtube-dl/youtube-dl.exe",
                        Arguments = $"\"ytsearch{resultsNumber}:{query}\" -j -i",
#endif
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                        UseShellExecute        = false,
                        CreateNoWindow         = true
                    },
                    EnableRaisingEvents = true,
                    //I think these are added only after Process Started
                    //PriorityClass = ProcessPriorityClass.RealTime,
                    PriorityBoostEnabled = true
                };
                Plugin.logger.Debug("Process Made");
                Plugin.logger.Info(
                    $"yt command: \"{searchProcess.StartInfo.FileName}\" {searchProcess.StartInfo.Arguments}");
                yield return(isReadingOutput = false);

                searchProcess.ErrorDataReceived += (sender, e) =>
                {
                    if (searchResults.query != query)
                    {
                        Plugin.logger.Debug("Killing Search Process");
                        try
                        {
                            searchProcess.Kill();
                        } catch { }

                        return;
                    }
                    if (e.Data == null)
                    {
                        return;
                    }

                    Plugin.logger.Error("Error With the process:");
                    Plugin.logger.Error(e.Data);
                };
                searchProcess.OutputDataReceived += (sender, e) =>
                {
                    if (searchResults.query != query)
                    {
                        Plugin.logger.Debug("Killing Search Process");
                        try
                        {
                            searchProcess.Kill();
                        } catch { }
                        return;
                    }

                    var output = e.Data.Trim();
                    if (string.IsNullOrWhiteSpace(output))
                    {
                        return;
                    }
                    else if (output.Contains("yt command exited"))
                    {
                        Plugin.logger.Debug("Done with Youtube Search, Processing...");
                        return;
                    }
                    else if (output.Contains("yt command"))
                    {
                        Plugin.logger.Debug($"Running with {output}");
                        return;
                    }
                    try
                    {
                        var      trimmedLine = output;
                        YTResult ytResult    = MakeYtResult(trimmedLine);
                        // Plugin.logger.Debug($"Adding: {ytResult.title}");
                        searchResults.Add(ytResult);
                        if (searchResults.Count >= resultsNumber)
                        {
                            try
                            {
                                ((Process)sender).Kill();
                            } catch {}
                        }
                    }
                    catch (Exception error)
                    {
                        Plugin.logger.Debug($"Invalid Response: {output}");
                        Plugin.logger.Error(error);
                    }
                };
                searchProcess.Exited += (sender, e) =>
                {
                    searchResults.isDone = true;
                    if (searchResults.Count != resultsNumber)
                    {
                        Plugin.logger.Warn($"Failed on {resultsNumber-searchResults.Count} queries with exitcode {((Process) sender).ExitCode}");
                    }
                    try
                    {
                        searchProcess.Kill();
                    } catch { }
                    try
                    {
                        searchProcess.Dispose();
                    } catch { }
                };
                // Plugin.logger.Debug("Error Set");
                yield return(searchProcess.Start());

                var fifteenSeconds = new TimeSpan(15 * TimeSpan.TicksPerSecond);
                var countdown      = YouTubeDownloader.Countdown(searchProcess, fifteenSeconds);
                SharedCoroutineStarter.instance.StartCoroutine(countdown);
                // Plugin.logger.Debug("started");
                searchProcess.BeginErrorReadLine();
                searchProcess.BeginOutputReadLine();
                // Plugin.logger.Debug("Error Reading");
                // var outputs = searchProcess.StandardOutput.ReadToEnd().Split('\n');
                yield return(new WaitUntil(() =>
                                           { try { return searchProcess.HasExited; } catch { return true; } }));

                SharedCoroutineStarter.instance.StopCoroutine(countdown);
                if (searchResults.query != query)
                {
                    yield break;
                }
                // foreach (var output in outputs)
                // {
                //     if (string.IsNullOrWhiteSpace(output))
                //     {
                //         continue;
                //     }
                //     else if (output.Contains("yt command exited"))
                //     {
                //         Plugin.logger.Debug("Done with Youtube Search, Processing...");
                //         continue;
                //     }
                //     else if (output.Contains("yt command"))
                //     {
                //         Plugin.logger.Debug($"Running with {output.Trim()}");
                //         continue;
                //     }
                //
                //     try
                //     {
                //         var trimmedLine = output.Trim();
                //         YTResult ytResult = MakeYtResult(trimmedLine);
                //         searchResults.Add(ytResult);
                //     }
                //     catch (Exception e)
                //     {
                //         Plugin.logger.Debug($"Invalid Response: {output.Trim()}");
                //         Plugin.logger.Error(e);
                //     }
                // }
                // Plugin.logger.Debug(searchResults.Count.ToString());
            }
            if (callback == null || query != searchResults.query)
            {
                yield break;
            }
            Plugin.logger.Debug($"Invoking Callback");
            callback?.Invoke();
            yield return(new WaitUntil((() => searchResults.isDone || searchResults.Count >= MaxResults)));

            callback?.Invoke();
            searchInProgress = false;
        }