feat: add album and miscellaneous controllers with album mapping
All checks were successful
Update changelog / changelog (push) Successful in 26s

This commit is contained in:
2026-01-28 05:18:03 +01:00
parent 6736ce8c13
commit a1c9e3807b
4 changed files with 367 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using Shadow.Data;
using Shadow.DTOs;
using Shadow.Entities;
using Shadow.Mapping;
using Shadow.Tools;
namespace Shadow.Controllers;
[ApiController]
[Route("rest")]
[Produces("application/json")]
public class AlbumController : ControllerBase
{
private readonly ApplicationDbContext db;
private readonly GeneralUseHelpers guhf;
public AlbumController(ApplicationDbContext _db, GeneralUseHelpers _guhf)
{
db = _db;
guhf = _guhf;
}
[HttpGet("getCoverArt")]
[HttpGet("getCoverArt.view")]
[HttpPost("getCoverArt.view")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> GetCoverArt()
{
string thumbnailPath = db.Globals
.First(g => g.Key == "musicThumbnailPath").Value!;
IFileInfo fileInfo = new PhysicalFileProvider(thumbnailPath)
.GetFileInfo("default.png");
if (!fileInfo.Exists) return NotFound();
return PhysicalFile(fileInfo.PhysicalPath!, "image/png");
}
[HttpGet("getAlbumList2")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetAlbumList2()
{
// First, check if user is authorized
User? user = await guhf.GetUserFromParams(Request);
if (user == null)
{
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
Status = "failed",
error = new ErrorDTO()
}
});
}
List<AlbumViewShortDTO> albumsDTO = [];
List<Album> albums = db.Albums
.Where(a => a.State != 1)
.Include(a => a.Artist)
.Include(a => a.Songs)
.ToList();
foreach (Album al in albums)
{
albumsDTO.Add(
AlbumMapping.ToAlbumViewShort(al)
);
}
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
albumList2 = new AlbumList2DTO
{
album = albumsDTO
}
}
});
}
[HttpPost("getAlbumList2.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetAlbumList2Form()
{
// First, check if user is authorized
User? user = await guhf.GetUserFromForm(Request);
if (user == null)
{
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
Status = "failed",
error = new ErrorDTO()
}
});
}
List<AlbumViewShortDTO> albumsDTO = [];
List<Album> albums = db.Albums
.Where(a => a.State != 1)
.Include(a => a.Artist)
.Include(a => a.Songs)
.ToList();
foreach (Album al in albums)
{
albumsDTO.Add(
AlbumMapping.ToAlbumViewShort(al)
);
}
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
albumList2 = new AlbumList2DTO
{
album = albumsDTO
}
}
});
}
}

View File

@@ -0,0 +1,153 @@
using Microsoft.AspNetCore.Mvc;
using Shadow.Data;
using Shadow.DTOs;
using Shadow.Entities;
using Shadow.Tools;
namespace Shadow.Controllers;
[ApiController]
[Route("rest")]
[Produces("application/json")]
public class MiscController : ControllerBase
{
private readonly ApplicationDbContext db;
private readonly GeneralUseHelpers guhf;
public MiscController(ApplicationDbContext _db, GeneralUseHelpers _guhf)
{
db = _db;
guhf = _guhf;
}
[HttpGet("ping")]
[ProducesResponseType(200)]
public async Task<IActionResult> Ping()
{
User? user = await guhf.GetUserFromParams(Request);
// Wrong credentials?
if (user == null)
{
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
Status = "failed",
error = new ErrorDTO
{
code = 40,
message = "Wrong username or password."
}
}
});
}
return Ok(new ResponseWrapper {
SubsonicResponse = new SubsonicResponseDTO()
});
}
[HttpPost("ping.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> PingForm()
{
User? user = await guhf.GetUserFromForm(Request);
// Wrong credentials?
if (user == null) {
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO {
Status = "failed",
error = new ErrorDTO {
code = 40,
message = "Wrong username or password."
}
}
});
}
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO()
});
}
[HttpGet("getUser")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetUser()
{
User? user = await guhf.GetUserFromParams(Request);
// Wrong credentials?
if (user == null)
{
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
Status = "failed",
error = new ErrorDTO
{
code = 40,
message = "Wrong username or password."
}
}
});
}
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
userPing = new UserPingDTO
{
username = user.Name,
adminRole = user.IsAdmin()
}
}
});
}
[HttpPost("getUser.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetUserForm()
{
User? user = await guhf.GetUserFromForm(Request);
// Wrong credentials?
if (user == null)
{
// Craft an error
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
Status = "failed",
error = new ErrorDTO
{
code = 40,
message = "Wrong username or password."
}
}
});
}
return Ok(new ResponseWrapper
{
SubsonicResponse = new SubsonicResponseDTO
{
userPing = new UserPingDTO
{
username = user.Name,
adminRole = user.IsAdmin()
}
}
});
}
}

27
Mapping/AlbumMapping.cs Normal file
View File

@@ -0,0 +1,27 @@
using Shadow.DTOs;
using Shadow.Entities;
namespace Shadow.Mapping;
public static class AlbumMapping
{
public static AlbumViewShortDTO ToAlbumViewShort(Album album)
{
AlbumViewShortDTO dto = new AlbumViewShortDTO
{
id = $"{album.Id}",
name = album.Name,
artist = album.Artist?.Name ?? "[Unknown Artist]",
artistId = $"{album.ArtistId}",
coverArt = "default.png",
songCount = album.Songs.Count,
duration = album.Songs.Sum(s => s.Duration),
playCount = 0,
year = 0,
genre = "unknown",
// played = "never"
};
return dto;
}
}

View File

@@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
using Shadow.Data; using Shadow.Data;
using Shadow.Entities; using Shadow.Entities;
using System.Text; using System.Text;
using System.Xml.Serialization;
namespace Shadow.Tools; namespace Shadow.Tools;
@@ -46,4 +47,55 @@ public class GeneralUseHelpers(ApplicationDbContext? db = null, IConfiguration?
return "{" + resultJson[..^2] + "}"; return "{" + resultJson[..^2] + "}";
} }
public async Task<User?> GetUserFromForm(HttpRequest request)
{
User? user = null;
try
{
string username = request.Form["u"]!;
string saltedPassword = request.Form["t"]!;
string sesame = request.Form["s"]!;
User? foundUser = _db?.Users
.FirstOrDefault(u => u.NormalizedName == username.ToLower());
if (foundUser == null)
return user;
string resaltedPassword = MetadataExtractor.GetStringMD5($"{foundUser.Password}{sesame}");
if (resaltedPassword == saltedPassword)
user = foundUser;
}
catch
{
user = null;
}
return user;
}
public async Task<User?> GetUserFromParams(HttpRequest request)
{
User? user = null;
try
{
string username = request.Query["u"]!;
string saltedPassword = request.Query["t"]!;
string sesame = request.Query["s"]!;
User foundUser = _db!.Users
.FirstOrDefault(u => u.NormalizedName == username.ToLower())!;
string resaltedPassword = MetadataExtractor.GetStringMD5($"{foundUser.Password}{sesame}");
if (resaltedPassword == saltedPassword)
user = foundUser;
}
catch
{
user = null;
}
return user;
}
} }