Exemplo n.º 1
0
            protected override Storyboard GetStoryboard()
            {
                Storyboard storyboard;

                try
                {
                    using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
                    {
                        var decoder = Decoder.GetDecoder <Storyboard>(stream);

                        string storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;

                        // todo: support loading from both set-wide storyboard *and* beatmap specific.
                        if (string.IsNullOrEmpty(storyboardFilename))
                        {
                            storyboard = decoder.Decode(stream);
                        }
                        else
                        {
                            using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename))))
                                storyboard = decoder.Decode(stream, secondaryStream);
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e, "Storyboard failed to load");
                    storyboard = new Storyboard();
                }

                storyboard.BeatmapInfo = BeatmapInfo;

                return(storyboard);
            }
Exemplo n.º 2
0
 // todo: expose this when we need to do individual difficulty lookups.
 protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken)
 => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler);
 public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
 {
     LogForModel(beatmapSet, "Performing online lookups...");
     return(Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()));
 }
 // todo: expose this when we need to do individual difficulty lookups.
 protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken)
 => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
Exemplo n.º 5
0
 private void logForModel(BeatmapSetInfo set, string message) =>
 ArchiveModelManager <BeatmapSetInfo, BeatmapSetFileInfo> .LogForModel(set, $"[{nameof(BeatmapOnlineLookupQueue)}] {message}");
Exemplo n.º 6
0
 public WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, BeatmapDatabase database)
 {
     this.BeatmapInfo    = beatmapInfo;
     this.BeatmapSetInfo = beatmapSetInfo;
     this.database       = database;
 }
 public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
 {
     return(Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()));
 }
Exemplo n.º 8
0
 /// <summary>
 /// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
 /// </summary>
 /// <param name="beatmapSet">A stale instance.</param>
 /// <returns>A fresh instance.</returns>
 public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
Exemplo n.º 9
0
            protected override Storyboard GetStoryboard()
            {
                Storyboard storyboard;

                try
                {
                    using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
                    {
                        var decoder = Decoder.GetDecoder <Storyboard>(stream);

                        // todo: support loading from both set-wide storyboard *and* beatmap specific.
                        if (BeatmapSetInfo?.StoryboardFile == null)
                        {
                            storyboard = decoder.Decode(stream);
                        }
                        else
                        {
                            using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile))))
                                storyboard = decoder.Decode(stream, secondaryStream);
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e, "Storyboard failed to load");
                    storyboard = new Storyboard();
                }

                storyboard.BeatmapInfo = BeatmapInfo;

                return(storyboard);
            }
            private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap)
            {
                // download is in progress (or was, and failed).
                if (cacheDownloadRequest != null)
                {
                    return(false);
                }

                // database is unavailable.
                if (!storage.Exists(cache_database_name))
                {
                    return(false);
                }

                try
                {
                    using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online")))
                    {
                        db.Open();

                        using (var cmd = db.CreateCommand())
                        {
                            cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";

                            cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash));
                            cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value));
                            cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path));

                            using (var reader = cmd.ExecuteReader())
                            {
                                if (reader.Read())
                                {
                                    var status = (BeatmapSetOnlineStatus)reader.GetByte(2);

                                    beatmap.Status                        = status;
                                    beatmap.BeatmapSet.Status             = status;
                                    beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
                                    beatmap.OnlineBeatmapID               = reader.GetInt32(1);

                                    if (beatmap.Metadata != null)
                                    {
                                        beatmap.Metadata.AuthorID = reader.GetInt32(3);
                                    }

                                    if (beatmap.BeatmapSet.Metadata != null)
                                    {
                                        beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
                                    }

                                    LogForModel(set, $"Cached local retrieval for {beatmap}.");
                                    return(true);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}.");
                }

                return(false);
            }
Exemplo n.º 11
0
 protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
 {
     BeatmapInfo    = beatmapInfo;
     BeatmapSetInfo = beatmapSetInfo;
     WithStoryboard = withStoryboard;
 }
Exemplo n.º 12
0
 private void logForModel(BeatmapSetInfo set, string message) =>
 RealmArchiveModelImporter <BeatmapSetInfo> .LogForModel(set, $"[{nameof(BeatmapOnlineLookupQueue)}] {message}");
Exemplo n.º 13
0
        private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmapInfo)
        {
            // download is in progress (or was, and failed).
            if (cacheDownloadRequest != null)
            {
                return(false);
            }

            // database is unavailable.
            if (!storage.Exists(cache_database_name))
            {
                return(false);
            }

            if (string.IsNullOrEmpty(beatmapInfo.MD5Hash) &&
                string.IsNullOrEmpty(beatmapInfo.Path) &&
                beatmapInfo.OnlineID <= 0)
            {
                return(false);
            }

            try
            {
                using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage)))
                {
                    db.Open();

                    using (var cmd = db.CreateCommand())
                    {
                        cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineID OR filename = @Path";

                        cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash));
                        cmd.Parameters.Add(new SqliteParameter("@OnlineID", beatmapInfo.OnlineID));
                        cmd.Parameters.Add(new SqliteParameter("@Path", beatmapInfo.Path));

                        using (var reader = cmd.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                var status = (BeatmapOnlineStatus)reader.GetByte(2);

                                beatmapInfo.Status = status;

                                Debug.Assert(beatmapInfo.BeatmapSet != null);

                                beatmapInfo.BeatmapSet.Status   = status;
                                beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
                                beatmapInfo.OnlineID            = reader.GetInt32(1);

                                beatmapInfo.Metadata.Author.OnlineID = reader.GetInt32(3);

                                logForModel(set, $"Cached local retrieval for {beatmapInfo}.");
                                return(true);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logForModel(set, $"Cached local retrieval for {beatmapInfo} failed with {ex}.");
            }

            return(false);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Downloads a beatmap.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
        public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null)
            {
                return(existing);
            }

            if (api == null)
            {
                return(null);
            }

            ProgressNotification downloadNotification = new ProgressNotification
            {
                Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += data =>
            {
                downloadNotification.State = ProgressNotificationState.Completed;

                using (var stream = new MemoryStream(data))
                    using (var archive = new OszArchiveReader(stream))
                        Import(archive);

                currentDownloads.Remove(request);
            };

            request.Failure += data =>
            {
                downloadNotification.State = ProgressNotificationState.Completed;
                Logger.Error(data, "Failed to get beatmap download information");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Run(() => request.Perform(api));

            return(request);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Import a beamap into our local <see cref="FileStore"/> storage.
        /// If the beatmap is already imported, the existing instance will be returned.
        /// </summary>
        /// <param name="files">The store to import beatmap files to.</param>
        /// <param name="beatmaps">The store to import beatmaps to.</param>
        /// <param name="reader">The beatmap archive to be read.</param>
        /// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
        private BeatmapSetInfo importToStorage(FileStore files, BeatmapStore beatmaps, ArchiveReader reader)
        {
            // let's make sure there are actually .osu files to import.
            string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));

            if (string.IsNullOrEmpty(mapName))
            {
                throw new InvalidOperationException("No beatmap files found in the map folder.");
            }

            // for now, concatenate all .osu files in the set to create a unique hash.
            MemoryStream hashable = new MemoryStream();

            foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
            {
                using (Stream s = reader.GetStream(file))
                    s.CopyTo(hashable);
            }

            var hash = hashable.ComputeSHA2Hash();

            // check if this beatmap has already been imported and exit early if so.
            var beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash);

            if (beatmapSet != null)
            {
                undelete(beatmaps, files, beatmapSet);

                // ensure all files are present and accessible
                foreach (var f in beatmapSet.Files)
                {
                    if (!storage.Exists(f.FileInfo.StoragePath))
                    {
                        using (Stream s = reader.GetStream(f.Filename))
                            files.Add(s, false);
                    }
                }

                // todo: delete any files which shouldn't exist any more.

                return(beatmapSet);
            }

            List <BeatmapSetFileInfo> fileInfos = new List <BeatmapSetFileInfo>();

            // import files to manager
            foreach (string file in reader.Filenames)
            {
                using (Stream s = reader.GetStream(file))
                    fileInfos.Add(new BeatmapSetFileInfo
                    {
                        Filename = file,
                        FileInfo = files.Add(s)
                    });
            }

            BeatmapMetadata metadata;

            using (var stream = new StreamReader(reader.GetStream(mapName)))
                metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;

            // check if a set already exists with the same online id.
            if (metadata.OnlineBeatmapSetID != null)
            {
                beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID);
            }

            if (beatmapSet == null)
            {
                beatmapSet = new BeatmapSetInfo
                {
                    OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
                    Beatmaps           = new List <BeatmapInfo>(),
                    Hash     = hash,
                    Files    = fileInfos,
                    Metadata = metadata
                }
            }
            ;

            var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));

            foreach (var name in mapNames)
            {
                using (var raw = reader.GetStream(name))
                    using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
                        using (var sr = new StreamReader(ms))
                        {
                            raw.CopyTo(ms);
                            ms.Position = 0;

                            var     decoder = Decoder.GetDecoder(sr);
                            Beatmap beatmap = decoder.DecodeBeatmap(sr);

                            beatmap.BeatmapInfo.Path    = name;
                            beatmap.BeatmapInfo.Hash    = ms.ComputeSHA2Hash();
                            beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();

                            var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);

                            if (existing == null)
                            {
                                // Exclude beatmap-metadata if it's equal to beatmapset-metadata
                                if (metadata.Equals(beatmap.Metadata))
                                {
                                    beatmap.BeatmapInfo.Metadata = null;
                                }

                                RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);

                                // TODO: this should be done in a better place once we actually need to dynamically update it.
                                beatmap.BeatmapInfo.Ruleset        = ruleset;
                                beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;

                                beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
                            }
                        }
            }

            return(beatmapSet);
        }
Exemplo n.º 16
0
 /// <summary>
 /// Get an existing download request if it exists.
 /// </summary>
 /// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
 /// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
 public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
Exemplo n.º 17
0
        /// <summary>
        /// Downloads a beatmap.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
        public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null || api == null)
            {
                return;
            }

            if (!api.LocalUser.Value.IsSupporter)
            {
                PostNotification?.Invoke(new SimpleNotification
                {
                    Icon = FontAwesome.fa_superpowers,
                    Text = "You gotta be a supporter to download for now 'yo"
                });
                return;
            }

            ProgressNotification downloadNotification = new ProgressNotification
            {
                Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += data =>
            {
                downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";

                Task.Factory.StartNew(() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    using (var stream = new MemoryStream(data))
                        using (var archive = new OszArchiveReader(stream))
                            Import(archive);

                    downloadNotification.State = ProgressNotificationState.Completed;
                }, TaskCreationOptions.LongRunning);

                currentDownloads.Remove(request);
            };

            request.Failure += data =>
            {
                downloadNotification.State = ProgressNotificationState.Completed;
                Logger.Error(data, "Failed to get beatmap download information");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
            BeatmapDownloadBegan?.Invoke(request);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Import a beamap into our local <see cref="FileStore"/> storage.
        /// If the beatmap is already imported, the existing instance will be returned.
        /// </summary>
        /// <param name="reader">The beatmap archive to be read.</param>
        /// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
        private BeatmapSetInfo importToStorage(ArchiveReader reader)
        {
            // let's make sure there are actually .osu files to import.
            string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));

            if (string.IsNullOrEmpty(mapName))
            {
                throw new InvalidOperationException("No beatmap files found in the map folder.");
            }

            // for now, concatenate all .osu files in the set to create a unique hash.
            MemoryStream hashable = new MemoryStream();

            foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
            {
                using (Stream s = reader.GetStream(file))
                    s.CopyTo(hashable);
            }

            var hash = hashable.ComputeSHA2Hash();

            // check if this beatmap has already been imported and exit early if so.
            BeatmapSetInfo beatmapSet;

            lock (beatmaps)
                beatmapSet = beatmaps.QueryAndPopulate <BeatmapSetInfo>(b => b.Hash == hash).FirstOrDefault();

            if (beatmapSet != null)
            {
                Undelete(beatmapSet);
                return(beatmapSet);
            }

            List <BeatmapSetFileInfo> fileInfos = new List <BeatmapSetFileInfo>();

            // import files to manager
            foreach (string file in reader.Filenames)
            {
                using (Stream s = reader.GetStream(file))
                    fileInfos.Add(new BeatmapSetFileInfo
                    {
                        Filename = file,
                        FileInfo = files.Add(s)
                    });
            }

            BeatmapMetadata metadata;

            using (var stream = new StreamReader(reader.GetStream(mapName)))
                metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;

            beatmapSet = new BeatmapSetInfo
            {
                OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
                Beatmaps           = new List <BeatmapInfo>(),
                Hash     = hash,
                Files    = fileInfos,
                Metadata = metadata
            };

            var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));

            foreach (var name in mapNames)
            {
                using (var raw = reader.GetStream(name))
                    using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
                        using (var sr = new StreamReader(ms))
                        {
                            raw.CopyTo(ms);
                            ms.Position = 0;

                            var     decoder = BeatmapDecoder.GetDecoder(sr);
                            Beatmap beatmap = decoder.Decode(sr);

                            beatmap.BeatmapInfo.Path    = name;
                            beatmap.BeatmapInfo.Hash    = ms.ComputeSHA2Hash();
                            beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();

                            // TODO: Diff beatmap metadata with set metadata and leave it here if necessary
                            beatmap.BeatmapInfo.Metadata = null;

                            // TODO: this should be done in a better place once we actually need to dynamically update it.
                            beatmap.BeatmapInfo.Ruleset        = rulesets.Query <RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID);
                            beatmap.BeatmapInfo.StarDifficulty = rulesets.Query <RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap)
                                                                 .Calculate() ?? 0;

                            beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
                        }
            }

            return(beatmapSet);
        }
Exemplo n.º 19
0
 public WorkingBeatmap(Beatmap beatmap)
 {
     this.beatmap   = beatmap;
     BeatmapInfo    = beatmap.BeatmapInfo;
     BeatmapSetInfo = beatmap.BeatmapInfo.BeatmapSet;
 }