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); }
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()); } } }
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; }
internal void EnqueueSong(BeatSaverSharp.Beatmap song, Texture2D cover) { DownloadQueueItem queuedSong = new DownloadQueueItem(song, cover); queueItems.Add(queuedSong); _downloadList?.tableView?.ReloadData(); UpdateDownloadingState(queuedSong); }
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; } }
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); } } }
//////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /// <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); }
internal void Initialize(BeatSaverSharp.Beatmap song) { songDescription.SetText(song.Description); }
public SongControl(BeatSaverSharp.Beatmap song, AudioSource audioSource) { this.song = song; this.audioSource = audioSource; }