search feature

This commit is contained in:
ju09279
2024-08-18 02:20:46 +02:00
parent b4ac10c006
commit 97521f19e5
13 changed files with 783 additions and 88 deletions

View File

@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using shitweb;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
@@ -39,12 +40,12 @@ app.MapGet("/api/v1/songs/recent", (int? limit, int? offset) =>
var limitValue = limit ?? 100; // default to 10 if not provided
var offsetValue = offset ?? 0; // default to 0 if not provided
return Results.Json(Osudb.Instance.GetRecent(limitValue, offsetValue));
return Results.Json(SqliteDB.GetByRecent(limitValue, offsetValue));
});
app.MapGet("/api/v1/songs/favorite", (int? limit, int? offset) =>
{
var limitValue = limit ?? 10; // default to 10 if not provided
var limitValue = limit ?? 100; // default to 10 if not provided
var offsetValue = offset ?? 0; // default to 0 if not provided
return Results.Ok(new { Limit = limitValue, Offset = offsetValue, Message = "List of favorite songs" });
@@ -56,13 +57,17 @@ app.MapGet("/api/v1/songs/{hash}", (string hash) =>
});
app.MapGet("/api/v1/collections/", async (int? limit, int? offset, [FromServices] IMemoryCache cache) =>
{
const string cacheKey = "collections";
{
var limitValue = limit ?? 100; // default to 10 if not provided
var offsetValue = offset ?? 0;
string cacheKey = $"collections_{offsetValue}_{limitValue}";
if (!cache.TryGetValue(cacheKey, out var collections))
{
collections = Osudb.Instance.GetCollections();
collections = Osudb.Instance.GetCollections(limit: limitValue, offset: offsetValue);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromDays(1))
@@ -105,6 +110,28 @@ app.MapGet("/api/v1/audio/{*fileName}", async (string fileName, HttpContext cont
return Results.Stream(fileStream, contentType, enableRangeProcessing: true);
});
app.MapGet("/api/v1/search/active", async (string? q) =>
{
return Results.Ok(SqliteDB.activeSearch(q));
});
app.MapGet("/api/v1/search/artist", async (string? q, int? limit, int? offset) =>
{
var limitv = limit ?? 100;
var offsetv = offset ?? 0;
return Results.Ok(SqliteDB.GetArtistSearch(q, limitv, offsetv));
});
app.MapGet("/api/v1/search/songs", async (string? q, int? limit, int? offset) =>
{
return Results.Ok();
});
app.MapGet("/api/v1/search/collections", async (string? q, int? limit, int? offset) =>
{
return Results.Ok();
});
app.MapGet("/api/v1/images/{*filename}", async (string filename, int? h, int? w) =>
{
@@ -195,4 +222,5 @@ static ImageFormat GetImageFormat(string extension)
};
}
app.Run();
Osudb.Instance.ToString();
app.Run();

444
backend/SqliteDB.cs Normal file
View File

@@ -0,0 +1,444 @@
using OsuParsers.Beatmaps;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SQLite;
using System.Reflection.PortableExecutable;
namespace shitweb
{
public class SqliteDB
{
private static SqliteDB instance;
private const string filename = "Beatmaps.db";
private const string dburl = $"Data Source={filename};Version=3;";
static SqliteDB() { }
public static SqliteDB Instance()
{
if (instance == null)
{
instance = new SqliteDB();
}
return instance;
}
public void setup(OsuParsers.Database.OsuDatabase osuDatabase)
{
int count = 0;
using (var connection = new SQLiteConnection(dburl))
{
connection.Open();
string createBeatmaps = @"
CREATE TABLE IF NOT EXISTS Beatmap (
BeatmapId INTEGER DEFAULT 0,
Artist TEXT DEFAULT '?????',
ArtistUnicode TEXT DEFAULT '?????',
Title TEXT DEFAULT '???????',
TitleUnicode TEXT DEFAULT '???????',
Creator TEXT DEFAULT '?????',
Difficulty TEXT DEFAULT '1',
AudioFileName TEXT DEFAULT 'unknown.mp3',
MD5Hash TEXT DEFAULT '00000000000000000000000000000000',
FileName TEXT DEFAULT 'unknown.osu',
RankedStatus TEXT DEFAULT Unknown,
LastModifiedTime DATETIME DEFAULT '0001-01-01 00:00:00',
TotalTime INTEGER DEFAULT 0,
AudioPreviewTime INTEGER DEFAULT 0,
BeatmapSetId INTEGER DEFAULT -1,
Source TEXT DEFAULT '',
Tags TEXT DEFAULT '',
LastPlayed DATETIME DEFAULT '0001-01-01 00:00:00',
FolderName TEXT DEFAULT 'Unknown Folder',
UNIQUE (Artist, Title, MD5Hash)
);";
using (var command = new SQLiteCommand(createBeatmaps, connection))
{
command.ExecuteNonQuery();
}
string activeSearch = @"CREATE VIRTUAL TABLE IF NOT EXISTS BeatmapFTS USING fts4(
Title,
Artist,
);";
using (var command = new SQLiteCommand(activeSearch, connection))
{
command.ExecuteNonQuery();
}
string triggerSearchupdate = @"CREATE TRIGGER IF NOT EXISTS Beatmap_Insert_Trigger
AFTER INSERT ON Beatmap
BEGIN
INSERT INTO BeatmapFTS (Title, Artist)
VALUES (NEW.Title, NEW.Artist);
END;";
using (var command = new SQLiteCommand(triggerSearchupdate, connection))
{
command.ExecuteNonQuery();
}
string query = @"SELECT COUNT(rowid) as count FROM Beatmap";
using (var command = new SQLiteCommand(query, connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
count = reader.GetInt32(reader.GetOrdinal("count"));
}
}
}
}
if (count < osuDatabase.BeatmapCount)
{
DateTime? LastMapInsert = null;
using (var connection = new SQLiteConnection(dburl))
{
connection.Open();
string query = @"SELECT MAX(LastModifiedTime) as Time FROM Beatmap"; ;
using (var command = new SQLiteCommand(query, connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
try
{
LastMapInsert = reader.GetDateTime(reader.GetOrdinal("Time"));
}
catch (Exception e)
{
LastMapInsert = null;
}
}
}
}
int i = 0;
int size = osuDatabase.BeatmapCount;
Console.CursorVisible = false;
foreach (var item in osuDatabase.Beatmaps)
{
if (LastMapInsert == null || item.LastModifiedTime > LastMapInsert)
{
insertBeatmap(item);
i++;
Console.CursorTop -= 1;
Console.Write($"Inserted {i}/{size}");
}
}
}
}
}
public static List<Song> GetByRecent(int limit, int offset)
{
var songs = new List<Song>();
using (var connection = new SQLiteConnection(dburl))
{
connection.Open();
string query = @"
SELECT
MD5Hash,
Title,
Artist,
TotalTime,
Creator,
FileName,
FolderName,
AudioFileName
FROM
Beatmap
GROUP BY
BeatmapSetId
ORDER BY
LastModifiedTime DESC
LIMIT @Limit
OFFSET @Offset
";
using (var command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@Limit", limit);
command.Parameters.AddWithValue("@Offset", offset);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
string file = reader.GetString(reader.GetOrdinal("FileName"));
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
var img = Osudb.getBG(folder, file);
Song song = new Song(
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
name: reader.GetString(reader.GetOrdinal("Title")),
artist: reader.GetString(reader.GetOrdinal("Artist")),
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
url: $"{folder}/{audio}",
previewimage: img,
mapper: reader.GetString(reader.GetOrdinal("Creator"))
);
songs.Add(song);
}
}
}
}
return songs;
}
public void insertBeatmap(OsuParsers.Database.Objects.DbBeatmap beatmap)
{
using (var connection = new SQLiteConnection(dburl))
{
connection.Open();
string insertBeatmap = @"
INSERT INTO Beatmap (
BeatmapId, Artist, ArtistUnicode, Title, TitleUnicode, Creator, Difficulty,
AudioFileName, MD5Hash, FileName, RankedStatus, LastModifiedTime, TotalTime,
AudioPreviewTime, BeatmapSetId, Source, Tags, LastPlayed, FolderName
) VALUES (
@BeatmapId, @Artist, @ArtistUnicode, @Title, @TitleUnicode, @Creator, @Difficulty,
@AudioFileName, @MD5Hash, @FileName, @RankedStatus, @LastModifiedTime, @TotalTime,
@AudioPreviewTime, @BeatmapSetId, @Source, @Tags, @LastPlayed, @FolderName
);";
using (var command = new SQLiteCommand(insertBeatmap, connection))
{
command.Parameters.AddWithValue("@BeatmapSetId", beatmap.BeatmapSetId);
command.Parameters.AddWithValue("@BeatmapId", beatmap.BeatmapId);
command.Parameters.AddWithValue("@Artist", beatmap.Artist);
command.Parameters.AddWithValue("@ArtistUnicode", beatmap.ArtistUnicode);
command.Parameters.AddWithValue("@Title", beatmap.Title);
command.Parameters.AddWithValue("@TitleUnicode", beatmap.TitleUnicode);
command.Parameters.AddWithValue("@Creator", beatmap.Creator);
command.Parameters.AddWithValue("@Difficulty", beatmap.Difficulty);
command.Parameters.AddWithValue("@AudioFileName", beatmap.AudioFileName);
command.Parameters.AddWithValue("@MD5Hash", beatmap.MD5Hash);
command.Parameters.AddWithValue("@FileName", beatmap.FileName);
command.Parameters.AddWithValue("@RankedStatus", beatmap.RankedStatus);
command.Parameters.AddWithValue("@LastModifiedTime", beatmap.LastModifiedTime);
command.Parameters.AddWithValue("@TotalTime", beatmap.TotalTime);
command.Parameters.AddWithValue("@AudioPreviewTime", beatmap.AudioPreviewTime);
command.Parameters.AddWithValue("@Source", beatmap.Source);
command.Parameters.AddWithValue("@Tags", beatmap.Tags);
command.Parameters.AddWithValue("@LastPlayed", beatmap.LastPlayed);
command.Parameters.AddWithValue("@FolderName", beatmap.FolderName);
int rows = command.ExecuteNonQuery();
Console.WriteLine(rows);
}
}
}
public static Song GetSongByHash(string hash)
{
using (var connection = new SQLiteConnection(dburl))
{
connection.Open();
string query = @"
SELECT
MD5Hash,
Title,
Artist,
TotalTime,
Creator,
FileName,
FolderName,
AudioFileName
FROM Beatmap
WHERE MD5Hash = @Hash;
";
using (var command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@Hash", hash);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
string file = reader.GetString(reader.GetOrdinal("FileName"));
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
var img = Osudb.getBG(folder, file);
Song song = new Song(
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
name: reader.GetString(reader.GetOrdinal("Title")),
artist: reader.GetString(reader.GetOrdinal("Artist")),
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
url: $"{folder}/{audio}",
previewimage: img,
mapper: reader.GetString(reader.GetOrdinal("Creator"))
);
return song;
}
}
}
}
return null;
}
public static ActiveSearch activeSearch(string query) {
ActiveSearch search = new ActiveSearch();
using (var connection = new SQLiteConnection(dburl))
{
string q = @"SELECT
MD5Hash,
Title,
Artist,
TotalTime,
Creator,
FileName,
FolderName,
AudioFileName
FROM Beatmap
WHERE Title LIKE @query
OR Artist LIKE @query
OR Tags LIKE @query
Group By Title
LIMIT 15";
connection.Open();
using (var command = new SQLiteCommand(q, connection))
{
command.Parameters.AddWithValue("@query", "%" + query + "%");
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
string file = reader.GetString(reader.GetOrdinal("FileName"));
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
var img = Osudb.getBG(folder, file);
Song song = new Song(
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
name: reader.GetString(reader.GetOrdinal("Title")),
artist: reader.GetString(reader.GetOrdinal("Artist")),
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
url: $"{folder}/{audio}",
previewimage: img,
mapper: reader.GetString(reader.GetOrdinal("Creator"))
);
search.Songs.Add(song);
}
}
}
string q2 = @"SELECT
Artist
FROM Beatmap
WHERE Artist LIKE @query
Group By Artist
LIMIT 5";
using (var command = new SQLiteCommand(q2, connection))
{
command.Parameters.AddWithValue("@query", query + "%");
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
search.Artist.Add(reader.GetString(reader.GetOrdinal("Artist")));
}
}
}
}
return search;
}
public static List<Song> GetArtistSearch(string query, int limit, int offset) {
List<Song> songs = new List<Song>();
query = $"%{query}%";
using (var connection = new SQLiteConnection(dburl))
{
string q = @"SELECT
MD5Hash,
Title,
Artist,
TotalTime,
Creator,
FileName,
FolderName,
AudioFileName
FROM Beatmap
WHERE Artist LIKE @query
Group By Title
LIMIT @Limit
OFFSET @Offset";
connection.Open();
using (var command = new SQLiteCommand(q, connection))
{
command.Parameters.AddWithValue("@query", query);
command.Parameters.AddWithValue("@Limit", limit);
command.Parameters.AddWithValue("@Offset", offset);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
string file = reader.GetString(reader.GetOrdinal("FileName"));
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
var img = Osudb.getBG(folder, file);
Song song = new Song(
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
name: reader.GetString(reader.GetOrdinal("Title")),
artist: reader.GetString(reader.GetOrdinal("Artist")),
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
url: $"{folder}/{audio}",
previewimage: img,
mapper: reader.GetString(reader.GetOrdinal("Creator"))
);
songs.Add(song);
}
}
}
}
return songs;
}
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.Win32;
using OsuParsers.Beatmaps;
using OsuParsers.Database;
using OsuParsers.Database.Objects;
using shitweb;
using System.Collections;
using System.Net;
using System.Text.RegularExpressions;
@@ -13,7 +14,6 @@ public class Osudb
private static Osudb instance = null;
private static readonly object padlock = new object();
public static string osufolder { get; private set; }
public static OsuDatabase osuDatabase { get; private set; }
public static CollectionDatabase CollectionDb { get; private set; }
static Osudb()
@@ -37,6 +37,7 @@ public class Osudb
static void Parse(string filepath)
{
OsuDatabase osuDatabase = null;
string file = "/osu!.db";
if (File.Exists(filepath + file))
{
@@ -45,6 +46,8 @@ public class Osudb
Console.WriteLine($"Parsing {file}");
osuDatabase = OsuParsers.Decoders.DatabaseDecoder.DecodeOsu($"{filepath}{file}");
Console.WriteLine($"Parsed {file}");
fileStream.Close();
}
}
@@ -58,6 +61,10 @@ public class Osudb
Console.WriteLine($"Parsed {file}");
}
}
SqliteDB.Instance().setup(osuDatabase);
osuDatabase = null;
GC.Collect();
}
public static Osudb Instance
@@ -75,15 +82,6 @@ public class Osudb
}
}
public DbBeatmap GetBeatmapbyHash(string Hash)
{
if (Hash == null || osuDatabase == null)
{
return null;
}
return osuDatabase.Beatmaps.FirstOrDefault(beatmap => beatmap.MD5Hash == Hash);
}
public static OsuParsers.Database.Objects.Collection GetCollectionbyName(string name) {
return CollectionDb.Collections.FirstOrDefault(collection => collection.Name == name);
@@ -101,87 +99,37 @@ public class Osudb
if (collection == null) { return null; }
List<Song> songs = new List<Song>();
var activeId = 0;
var activeId = "";
collection.MD5Hashes.ForEach(hash =>
{
var beatmap = GetBeatmapbyHash(hash);
var beatmap = SqliteDB.GetSongByHash(hash);
if (beatmap == null) { return; }
if (activeId == beatmap.BeatmapSetId) { return; }
activeId = beatmap.BeatmapSetId;
//todo
string img = getBG(beatmap.FolderName, beatmap.FileName);
songs.Add(new Song(hash: beatmap.MD5Hash, name: beatmap.Title, artist: beatmap.Artist, length: beatmap.TotalTime, url: $"{beatmap.FolderName}/{beatmap.AudioFileName}" , previewimage: img, mapper: beatmap.Creator));
songs.Add(beatmap);
});
return new Collection(collection.Name, songs.Count, songs);
}
public List<CollectionPreview> GetCollections()
public List<CollectionPreview> GetCollections(int limit, int offset)
{
List<CollectionPreview> collections = new List<CollectionPreview>();
for (int i = 0; i < CollectionDb.Collections.Count; i++) {
for (int i = offset; i < CollectionDb.Collections.Count - 1 && i < offset + limit; i++) {
var collection = CollectionDb.Collections[i];
var beatmap = GetBeatmapbyHash(collection.MD5Hashes.FirstOrDefault());
var beatmap = SqliteDB.GetSongByHash(collection.MD5Hashes.FirstOrDefault());
//todo
string img = getBG(beatmap.FolderName, beatmap.FileName);
collections.Add(new CollectionPreview(index: i, name: collection.Name, previewimage: img, length: collection.Count));
collections.Add(new CollectionPreview(index: i, name: collection.Name, previewimage: beatmap.previewimage, length: collection.Count));
};
return collections;
}
public List<Song> GetRecent(int limit, int offset)
{
var recent = new List<Song>();
if(limit > 100 && limit < 0) {
limit = 100;
}
var size = osuDatabase.Beatmaps.Count -1;
for (int i = size - offset; i > size - offset - limit; i--)
{
var beatmap = osuDatabase.Beatmaps.ElementAt(i);
if (beatmap == null) {
continue;
}
string img = getBG(beatmap.FolderName, beatmap.FileName);
recent.Add(new Song(
name: beatmap.FileName,
hash: beatmap.MD5Hash,
artist: beatmap.Artist,
length: beatmap.TotalTime,
url: $"{beatmap.FolderName}/{beatmap.AudioFileName}",
previewimage: img,
mapper: beatmap.Creator));
}
return recent;
}
public List<Song> GetFavorites()
{
var recent = new List<Song>();
/*
osuDatabase.Beatmaps.ForEach(beatmap =>
{
Console.WriteLine(beatmap.LastModifiedTime);
});
*/
return null;
}
private static string getBG(string songfolder, string diff)
public static string getBG(string songfolder, string diff)
{
string folderpath = Path.Combine(songfolder, diff);
string filepath = Path.Combine(osufolder, "Songs", folderpath);

View File

@@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="OsuParsers" Version="1.7.1" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
</ItemGroup>

View File

@@ -1,3 +1,7 @@
using OsuParsers.Database.Objects;
using OsuParsers.Enums.Database;
using OsuParsers.Enums;
public class Song{
public string hash {get; set;}
public string name {get; set;}
@@ -37,4 +41,46 @@ public class Collection{
this.name = name; this.length = length; this.songs = songs;
}
}
public class ActiveSearch{
public List<string> Artist { get; set; } = new List<string>();
public List<Song> Songs { get; set; } = new List<Song>();
}
public class Beatmap
{
public string Artist { get; set; }
public string ArtistUnicode { get; set; }
public string Title { get; set; }
public string TitleUnicode { get; set; }
public string Creator { get; set; }
public string Difficulty { get; set; }
public string AudioFileName { get; set; }
public string MD5Hash { get; set; }
public string FileName { get; set; }
public RankedStatus RankedStatus { get; set; }
public DateTime LastModifiedTime { get; set; }
public int TotalTime { get; set; }
public int AudioPreviewTime { get; set; }
public int BeatmapId { get; set; }
public int BeatmapSetId { get; set; }
public string Source { get; set; }
public string Tags { get; set; }
public DateTime LastPlayed { get; set; }
public string FolderName { get; set; }
}
public class BeatmapSet {
public int BeatmapSetId { get; set; }
public string FolderName { get; set; }
public string Creator { get; set; }
public DateTime LastModifiedTime { get; set; }
public List<Beatmap> Beatmaps { get; private set; } = new List<Beatmap>();
public void AddBeatmap(Beatmap beatmap)
{
beatmap.BeatmapSetId = this.BeatmapSetId;
Beatmaps.Add(beatmap);
}
}