Exemple #1
0
 internal void HandleDidPressDownload(BeatSaverSharp.Beatmap song, Texture2D cover)
 {
     Plugin.log.Info("Download pressed for song: " + song.Metadata.SongName);
     //    Misc.SongDownloader.Instance.DownloadSong(song);
     _songDetailView.UpdateDownloadButtonStatus();
     _downloadQueueView.EnqueueSong(song, cover);
 }
Exemple #2
0
        public async Task DownloadSong(BeatSaverSharp.Beatmap song, System.Threading.CancellationToken token, IProgress <double> progress = null, bool direct = false)
        {
            try
            {
                string customSongsPath = CustomLevelPathHelper.customLevelsDirectoryPath;
                if (!Directory.Exists(customSongsPath))
                {
                    Directory.CreateDirectory(customSongsPath);
                }
                var zip = await song.DownloadZip(direct, token, progress).ConfigureAwait(false);

                Plugin.log.Info("Downloaded zip!");
                await ExtractZipAsync(song, zip, customSongsPath).ConfigureAwait(false);

                songDownloaded?.Invoke(song);
            }
            catch (Exception e)
            {
                if (e is TaskCanceledException)
                {
                    Plugin.log.Warn("Song Download Aborted.");
                }
                else
                {
                    Plugin.log.Critical("Failed to download Song!");
                }
                if (_alreadyDownloadedSongs.Contains(song.Hash.ToUpper()))
                {
                    _alreadyDownloadedSongs.Remove(song.Hash.ToUpper());
                }
            }
        }
Exemple #3
0
 public DownloadQueueItem(BeatSaverSharp.Beatmap song, Texture2D cover)
 {
     beatmap       = song;
     _songName     = song.Metadata.SongName;
     _coverTexture = cover;
     _authorName   = $"{song.Metadata.SongAuthorName} <size=80%>[{song.Metadata.LevelAuthorName}]";
 }
        ////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Set details
        /// </summary>
        /// <param name="p_Detail">p_Detail</param>
        internal void SetDetail(BeatSaverSharp.Beatmap p_Detail, bool p_SecondTime = false)
        {
            if (!CanBeUpdated)
            {
                m_PendingDetail = p_Detail;
                return;
            }

            string l_Description = "<line-height=125%>" + System.Net.WebUtility.HtmlEncode(p_Detail.Description.Replace("\r\n", "\n"));

            if (l_Description.Trim().Length == "<line-height=125%>".Length)
            {
                l_Description = "<align=\"center\"><alpha=#AA><i>No description...</i></align>";
            }

            l_Description += "\n\n\n\n\n\n\n\n\n\n ";

            m_DetailText.SetText(l_Description);
            m_DetailText.ScrollTo(0, true);

            float  l_Vote    = (float)Math.Round((double)p_Detail.Stats.Rating * 100f, 0);
            string l_SubText = $"Votes +{p_Detail.Stats.UpVotes}/-{p_Detail.Stats.DownVotes} ({l_Vote}%) {p_Detail.Stats.Downloads} downloads\n";

            l_SubText += "Uploaded on " + s_Months[p_Detail.Uploaded.Month - 1] + " " + p_Detail.Uploaded.Day + " of " + p_Detail.Uploaded.Year;

            m_SubDetailText.GetComponent <TextMeshProUGUI>().text = l_SubText;

            m_LastDetail    = p_Detail;
            m_PendingDetail = null;
        }
Exemple #5
0
        internal void EnqueueSong(BeatSaverSharp.Beatmap song, Texture2D cover)
        {
            DownloadQueueItem queuedSong = new DownloadQueueItem(song, cover);

            queueItems.Add(queuedSong);
            _downloadList?.tableView?.ReloadData();
            UpdateDownloadingState(queuedSong);
        }
Exemple #6
0
        internal void EnqueueSong(BeatSaverSharp.Beatmap song, Texture2D cover)
        {
            DownloadQueueItem queuedSong = new DownloadQueueItem(song, cover);

            queueItems.Add(queuedSong);
            Misc.SongDownloader.Instance.QueuedDownload(song.Hash.ToUpper());
            _downloadList?.tableView?.ReloadData();
            UpdateDownloadingState(queuedSong);
        }
        ////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Prefix
        /// </summary>
        /// <param name="__instance">BeatMap instance</param>
        /// <param name="__result">Output result</param>
        /// <param name="token">Cancellation token</param>
        /// <param name="progress">Progress reporter</param>
        /// <returns></returns>
        internal static bool Prefix(ref BeatSaverSharp.Beatmap __instance, ref Task <byte[]> __result, ref CancellationToken token, ref IProgress <double> progress)
        {
            if (!__instance.CoverURL.ToLower().StartsWith("http"))
            {
                return(true);
            }

            __result = FetchCoverImage(__instance, token, progress);
            return(false);
        }
        public SongControl GetControl(BeatSaverSharp.Beatmap song)
        {
            SongControl result = _controlls.Where(control => control.song == song).FirstOrDefault();

            if (result == null)
            {
                result = new SongControl(song, audioSource);
                _controlls.Add(result);
            }
            return(result);
        }
 internal void HandleDidSelectSong(BeatSaverSharp.Beatmap song, Texture2D cover = null)
 {
     _songDetailView.ClearData();
     _songDescriptionView.ClearData();
     if (!_songDetailView.isInViewControllerHierarchy)
     {
         PushViewControllerToNavigationController(_moreSongsNavigationcontroller, _songDetailView);
     }
     SetRightScreenViewController(_songDescriptionView);
     _songDescriptionView.Initialize(song);
     _songDetailView.Initialize(song, cover);
 }
        ////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Fetch cover image patch
        /// </summary>
        /// <param name="p_BeatMap">BeatMap instance</param>
        /// <param name="p_Token">Cancellation token</param>
        /// <param name="p_Progress">Progress reporter</param>
        /// <returns></returns>
        private static async Task <byte[]> FetchCoverImage(BeatSaverSharp.Beatmap p_BeatMap, CancellationToken p_Token, IProgress <double> p_Progress = null)
        {
            if (m_Beatmap_Client == null)
            {
                m_Beatmap_Client = typeof(BeatSaverSharp.Beatmap).GetProperty("Client", BindingFlags.Instance | BindingFlags.NonPublic);
            }
            if (m_BeatSaver_HttpInstance == null)
            {
                m_BeatSaver_HttpInstance = typeof(BeatSaverSharp.BeatSaver).GetProperty("HttpInstance", BindingFlags.Instance | BindingFlags.NonPublic);
            }
            if (m_Http_GetAsync == null)
            {
                m_Http_GetAsync = m_BeatSaver_HttpInstance.PropertyType.GetMethod("GetAsync", BindingFlags.Instance | BindingFlags.NonPublic);
            }

            var l_Client = m_Beatmap_Client.GetValue(p_BeatMap);

            if (l_Client != null)
            {
                var l_HTTPInstance = m_BeatSaver_HttpInstance.GetValue(l_Client);
                if (l_HTTPInstance != null && m_Http_GetAsync != null)
                {
                    var l_TaskRaw = m_Http_GetAsync.Invoke(l_HTTPInstance, new object[] { p_BeatMap.CoverURL, p_Token, p_Progress });
                    await(l_TaskRaw as Task);

                    if (m_TaskHttpResponse_GetAwaiter == null)
                    {
                        m_TaskHttpResponse_GetAwaiter = m_Http_GetAsync.ReturnType.GetMethod("GetAwaiter", BindingFlags.Instance | BindingFlags.Public);
                    }

                    var l_Awaiter = m_TaskHttpResponse_GetAwaiter.Invoke(l_TaskRaw, new object[] { });
                    if (l_Awaiter != null)
                    {
                        if (m_AwaiterHttpResponse_GetResult == null)
                        {
                            m_AwaiterHttpResponse_GetResult = l_Awaiter.GetType().GetMethod("GetResult", BindingFlags.Instance | BindingFlags.Public);
                        }

                        var l_Result = m_AwaiterHttpResponse_GetResult.Invoke(l_Awaiter, new object[] { });

                        if (m_HttpResponse_Bytes == null)
                        {
                            m_HttpResponse_Bytes = l_Result.GetType().GetMethod("Bytes", BindingFlags.Instance | BindingFlags.Public);
                        }

                        return(m_HttpResponse_Bytes.Invoke(l_Result, new object[] { }) as byte[]);
                    }
                }
            }

            return(await p_BeatMap.FetchCoverImage(p_Token, p_Progress).ConfigureAwait(false));
        }
        private async Task <string> ExtractZipAsync(BeatSaverSharp.Beatmap songInfo, byte[] zip, string customSongsPath, bool overwrite = false)
        {
            Stream zipStream = new MemoryStream(zip);

            try
            {
                Plugin.log.Info("Extracting...");
                _extractingZip = true;
                ZipArchive archive  = new ZipArchive(zipStream, ZipArchiveMode.Read);
                string     basePath = songInfo.Key + " (" + songInfo.Metadata.SongName + " - " + songInfo.Metadata.LevelAuthorName + ")";
                basePath = string.Join("", basePath.Split((Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).ToArray())));
                string path = customSongsPath + basePath;
                if (!overwrite && Directory.Exists(path))
                {
                    int pathNum = 1;
                    while (Directory.Exists(path + $" ({pathNum})"))
                    {
                        ++pathNum;
                    }
                    path += $" ({pathNum})";
                }
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                Plugin.log.Info(path);
                await Task.Run(() =>
                {
                    foreach (var entry in archive.Entries)
                    {
                        var entryPath = Path.Combine(path, entry.Name); // Name instead of FullName for better security and because song zips don't have nested directories anyway
                        if (overwrite || !File.Exists(entryPath))       // Either we're overwriting or there's no existing file
                        {
                            entry.ExtractToFile(entryPath, overwrite);
                        }
                    }
                }).ConfigureAwait(false);

                archive.Dispose();
                return(path);
            }
            catch (Exception e)
            {
                Plugin.log.Critical($"Unable to extract ZIP! Exception: {e}");
                _extractingZip = false;
                return(null);
            }
            zipStream.Close();
        }
        void SongDownloaded(BeatSaverSharp.Beatmap song, string path)
        {
            _path = path;
            // TODO: Add to BeatSaverSharp .songFilename.
            string file = Directory.GetFiles(path, "*.egg").FirstOrDefault();

            LoadClip("file://" + file, (clip) => {
                Plugin.log.Info("Preview loaded for song: " + song.Metadata.SongName);
                _clip = clip;
                if (_startPlayOnLoad)
                {
                    StartPlaying();
                }
            });
        }
        internal void HandleDidPressPreview(BeatSaverSharp.Beatmap song)
        {
            Plugin.log.Info("Preview pressed for song: " + song.Metadata.SongName);

            Misc.SongControl control = _songPlayer.GetControl(song);
            control.stateChanged += _songDetailView.SongStateChanged;

            if (control.state == Misc.SongControl.State.Playing)
            {
                control.Pause();
            }
            else
            {
                control.Play();
            }
        }
        ////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Set details
        /// </summary>
        /// <param name="p_Detail">p_Detail</param>
        internal void SetDetail(BeatSaverSharp.Beatmap p_Detail, bool p_SecondTime = false)
        {
            if (DetailText != null)
            {
                string l_Description = p_Detail.Description;

                if (l_Description.Trim().Length == 0)
                {
                    l_Description = "<align=\"center\"><alpha=#AA><i>No description...</i></align>";
                }

                l_Description += "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";

                DetailText.GetComponent <CurvedTextMeshPro>().text = l_Description;

                HMMainThreadDispatcher.instance.Enqueue(() =>
                {
                    var l_ScrollView = DetailBackground.transform.GetChild(0).GetComponent <ScrollView>();
                    var l_Child      = l_ScrollView.transform.GetChild(0);

                    l_ScrollView.SetContentHeight((l_Child.transform as RectTransform).rect.height);
                    l_ScrollView.SetDestinationPosY(0f);
                    l_ScrollView.ScrollTo(0f, false);
                    l_ScrollView.Update();
                    l_ScrollView.UpdateVerticalScrollIndicator(0f);
                    l_ScrollView.RefreshButtons();

                    if (!p_SecondTime)
                    {
                        SetDetail(p_Detail, true);
                    }
                });

                float  l_Vote    = (float)Math.Round((double)p_Detail.Stats.Rating * 100f, 0);
                string l_SubText = $"Votes +{p_Detail.Stats.UpVotes}/-{p_Detail.Stats.DownVotes} ({l_Vote}%) {p_Detail.Stats.Downloads} downloads";

                SubDetailText.GetComponent <CurvedTextMeshPro>().text = l_SubText;

                m_LastDetail    = p_Detail;
                m_PendingDetail = null;
            }
            else
            {
                m_PendingDetail = p_Detail;
            }
        }
Exemple #15
0
 internal async void EnqueueSongs(Tuple <BeatSaverSharp.Beatmap, Texture2D>[] songs, CancellationToken cancellationToken)
 {
     for (int i = 0; i < songs.Length; i++)
     {
         if (cancellationToken.IsCancellationRequested)
         {
             return;
         }
         bool downloaded = false;
         Tuple <BeatSaverSharp.Beatmap, Texture2D> pair = songs[i];
         BeatSaverSharp.Beatmap map = pair.Item1;
         if (map.Partial)
         {
             downloaded = SongDownloader.Instance.IsSongDownloaded(map.Hash);
             if (downloaded)
             {
                 continue;
             }
             try
             {
                 await map.Populate();
             }
             catch (BeatSaverSharp.Exceptions.InvalidPartialException ex)
             {
                 Plugin.log.Warn("Map not found on BeatSaver");
                 continue;
             }
         }
         bool inQueue = queueItems.Any(x => (x as DownloadQueueItem).beatmap == map);
         downloaded = SongDownloader.Instance.IsSongDownloaded(map.Hash);
         if (!inQueue & !downloaded)
         {
             EnqueueSong(map, pair.Item2);
         }
     }
 }
Exemple #16
0
        ////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Filter a beatmap
        /// </summary>
        /// <param name="p_BeatMap">Beatmap to filter</param>
        /// <param name="p_SenderName">Requester name</param>
        /// <param name="p_Reply">Output reply</param>
        /// <returns></returns>
        private bool FilterBeatMap(BeatSaverSharp.Beatmap p_BeatMap, string p_SenderName, out string p_Reply)
        {
            p_Reply = "";

            bool  l_FilterNPSMin = Config.ChatRequest.NPSMin;
            bool  l_FilterNPSMax = Config.ChatRequest.NPSMax;
            bool  l_FilterNJSMin = Config.ChatRequest.NJSMin;
            bool  l_FilterNJSMax = Config.ChatRequest.NJSMax;
            float l_Vote         = (float)Math.Round((double)p_BeatMap.Stats.Rating * 100f, 0);

            if (Config.ChatRequest.NoBeatSage && p_BeatMap.Metadata.Automapper != null && p_BeatMap.Metadata.Automapper != "")
            {
                p_Reply = $"@{p_SenderName} BeatSage map are not allowed!";
                return(false);
            }
            if (l_FilterNPSMin || l_FilterNPSMax)
            {
                int l_NPSMin = Config.ChatRequest.NPSMinV;
                int l_NPSMax = Config.ChatRequest.NPSMaxV;

                List <BeatSaverSharp.BeatmapCharacteristicDifficulty> l_Diffs = new List <BeatSaverSharp.BeatmapCharacteristicDifficulty>();
                foreach (var l_Chara in p_BeatMap.Metadata.Characteristics)
                {
                    l_Diffs.AddRange(l_Chara.Difficulties.Where(x => x.Value.HasValue).Select(x => x.Value.Value));
                }

                if (l_FilterNPSMin && !l_FilterNPSMax && l_Diffs.Count(x => ((float)x.Notes / (float)x.Length) >= l_NPSMin) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NPS of {l_NPSMin} minimum!";
                    return(false);
                }
                if (!l_FilterNPSMin && l_FilterNPSMax && l_Diffs.Count(x => ((float)x.Notes / (float)x.Length) <= l_NPSMax) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NPS of {l_NPSMax} maximum!";
                    return(false);
                }
                if (l_FilterNPSMin && l_FilterNPSMax && l_Diffs.Count(x => ((float)x.Notes / (float)x.Length) >= l_NPSMin && ((float)x.Notes / (float)x.Length) <= l_NPSMax) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NPS between {l_NPSMin} and {l_NPSMax}!";
                    return(false);
                }
            }
            if (l_FilterNJSMin || l_FilterNJSMax)
            {
                int l_NJSMin = Config.ChatRequest.NJSMinV;
                int l_NJSMax = Config.ChatRequest.NJSMaxV;

                List <BeatSaverSharp.BeatmapCharacteristicDifficulty> l_Diffs = new List <BeatSaverSharp.BeatmapCharacteristicDifficulty>();
                foreach (var l_Chara in p_BeatMap.Metadata.Characteristics)
                {
                    l_Diffs.AddRange(l_Chara.Difficulties.Where(x => x.Value.HasValue).Select(x => x.Value.Value));
                }

                if (l_FilterNJSMin && !l_FilterNJSMax && l_Diffs.Count(x => x.NoteJumpSpeed >= l_NJSMin) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NJS of {l_NJSMin} minimum!";
                    return(false);
                }
                if (!l_FilterNJSMin && l_FilterNJSMax && l_Diffs.Count(x => x.NoteJumpSpeed <= l_NJSMax) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NJS of {l_NJSMax} maximum!";
                    return(false);
                }
                if (l_FilterNJSMin && l_FilterNJSMax && l_Diffs.Count(x => x.NoteJumpSpeed >= l_NJSMin && x.NoteJumpSpeed <= l_NJSMax) == 0)
                {
                    p_Reply = $"@{p_SenderName} this song has no difficulty with a NJS between {l_NJSMin} and {l_NJSMax}!";
                    return(false);
                }
            }
            if (Config.ChatRequest.DurationMax && (int)p_BeatMap.Metadata.Duration > (Config.ChatRequest.DurationMaxV * 60))
            {
                p_Reply = $"@{p_SenderName} this song is too long ({Config.ChatRequest.DurationMaxV} minute(s) maximum)!";
                return(false);
            }
            if (Config.ChatRequest.VoteMin && l_Vote < Config.ChatRequest.VoteMinV)
            {
                p_Reply = $"@{p_SenderName} this song rating is too low ({Config.ChatRequest.VoteMinV}% minimum)!";
                return(false);
            }
            DateTime l_MinUploadDate = new DateTime(2018, 1, 1).AddMonths(Config.ChatRequest.DateMinV);

            if (Config.ChatRequest.DateMin && p_BeatMap.Uploaded < l_MinUploadDate)
            {
                string[] s_Months = new string[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
                p_Reply = $"@{p_SenderName} this song is too old ({s_Months[l_MinUploadDate.Month - 1]} {l_MinUploadDate.Year} minimum)!";
                return(false);
            }
            DateTime l_MaxUploadDate = new DateTime(2018, 1, 1).AddMonths(Config.ChatRequest.DateMaxV + 1);

            if (Config.ChatRequest.DateMax && p_BeatMap.Uploaded > l_MaxUploadDate)
            {
                string[] s_Months = new string[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
                p_Reply = $"@{p_SenderName} this song is too recent ({s_Months[l_MinUploadDate.Month - 1]} {l_MinUploadDate.Year} maximum)!";
                return(false);
            }

            return(true);
        }
Exemple #17
0
 internal void Initialize(BeatSaverSharp.Beatmap song)
 {
     songDescription.SetText(song.Description);
 }
 public SongControl(BeatSaverSharp.Beatmap song, AudioSource audioSource)
 {
     this.song        = song;
     this.audioSource = audioSource;
 }