Serializing images paths

This commit is contained in:
Zoe Roux 2021-03-13 00:49:12 +01:00
parent 356b8a5472
commit 779702f969
16 changed files with 100 additions and 124 deletions

View File

@ -7,4 +7,15 @@ namespace Kyoo.Models.Attributes
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DeserializeIgnoreAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class SerializeAsAttribute : Attribute
{
public string Format { get; }
public SerializeAsAttribute(string format)
{
Format = format;
}
}
}

View File

@ -8,7 +8,7 @@ namespace Kyoo.Models
public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Poster { get; set; }
[SerializeAs("{HOST}/api/library/{Slug}/poster")] public string Poster { get; set; }
public string Overview { get; set; }
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }

View File

@ -19,14 +19,14 @@ namespace Kyoo.Models
public int EpisodeNumber { get; set; } = -1;
public int AbsoluteNumber { get; set; } = -1;
[SerializeIgnore] public string Path { get; set; }
public string Thumb => $"/api/episodes/{Slug}/thumb";
[SerializeAs("{HOST}/api/episodes/{Slug}/thumb")] public string Thumb { get; set; }
public string Title { get; set; }
public string Overview { get; set; }
public DateTime? ReleaseDate { get; set; }
public int Runtime { get; set; } //This runtime variable should be in minutes
[SerializeIgnore] public string Poster { get; set; }
[LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
[LoadableRelation] public virtual ICollection<Track> Tracks { get; set; }
@ -41,7 +41,7 @@ namespace Kyoo.Models
string overview,
DateTime? releaseDate,
int runtime,
string poster,
string thumb,
IEnumerable<MetadataID> externalIDs)
{
SeasonNumber = seasonNumber;
@ -51,7 +51,7 @@ namespace Kyoo.Models
Overview = overview;
ReleaseDate = releaseDate;
Runtime = runtime;
Poster = poster;
Thumb = thumb;
ExternalIDs = externalIDs?.ToArray();
}

View File

@ -18,8 +18,7 @@ namespace Kyoo.Models
public string Overview { get; set; }
public int? Year { get; set; }
[SerializeIgnore] public string Poster { get; set; }
public string Thumb => $"/api/seasons/{Slug}/thumb";
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; }
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }

View File

@ -18,9 +18,9 @@ namespace Kyoo.Models
public int? StartYear { get; set; }
public int? EndYear { get; set; }
public string Poster { get; set; }
public string Logo { get; set; }
public string Backdrop { get; set; }
[SerializeAs("{HOST}/api/shows/{Slug}/poster")] public string Poster { get; set; }
[SerializeAs("{HOST}/api/shows/{Slug}/logo")] public string Logo { get; set; }
[SerializeAs("{HOST}/api/shows/{Slug}/backdrop")] public string Backdrop { get; set; }
public bool IsMovie { get; set; }

View File

@ -1,7 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Kyoo.Models;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
@ -13,7 +15,13 @@ namespace Kyoo.Controllers
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
{
private int _depth = -1;
private string _host;
public JsonPropertyIgnorer(string host)
{
_host = host;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
@ -38,6 +46,10 @@ namespace Kyoo.Controllers
property.ShouldSerialize = _ => false;
if (member?.GetCustomAttribute<DeserializeIgnoreAttribute>() != null)
property.ShouldDeserialize = _ => false;
SerializeAsAttribute serializeAs = member?.GetCustomAttribute<SerializeAsAttribute>();
if (serializeAs != null)
property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host);
return property;
}
@ -86,4 +98,40 @@ namespace Kyoo.Controllers
throw new NotImplementedException();
}
}
public class SerializeAsProvider : IValueProvider
{
private string _format;
private string _host;
public SerializeAsProvider(string format, string host)
{
_format = format;
_host = host.TrimEnd('/');
}
public object GetValue(object target)
{
return Regex.Replace(_format, @"(?<!{){(\w+)}", x =>
{
string value = x.Groups[1].Value;
if (value == "HOST")
return _host;
PropertyInfo properties = target.GetType().GetProperties()
.FirstOrDefault(y => y.Name == value);
if (properties == null)
return null;
if (properties.GetValue(target) is string ret)
return ret;
throw new ArgumentException($"Invalid serializer replacement {value}");
});
}
public void SetValue(object target, object value)
{
throw new NotImplementedException();
}
}
}

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Kyoo.CommonApi
{
@ -24,9 +25,15 @@ namespace Kyoo.CommonApi
where.Remove(key);
}
string[] fields = context.HttpContext.Request.Query["fields"]
List<string> fields = context.HttpContext.Request.Query["fields"]
.SelectMany(x => x.Split(','))
.ToArray();
.ToList();
if (fields.Contains("internal"))
{
fields.Remove("internal");
context.HttpContext.Items["internal"] = true;
// TODO disable SerializeAs attributes when this is true.
}
if (context.ActionDescriptor is ControllerActionDescriptor descriptor)
{
Type type = descriptor.MethodInfo.ReturnType;
@ -50,7 +57,7 @@ namespace Kyoo.CommonApi
});
return null;
})
.ToArray();
.ToList();
if (context.Result != null)
return;
}
@ -71,7 +78,7 @@ namespace Kyoo.CommonApi
return;
await using ILibraryManager library = context.HttpContext.RequestServices.GetService<ILibraryManager>();
string[] fields = (string[])context.HttpContext.Items["fields"];
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));

View File

@ -44,7 +44,7 @@ namespace Kyoo.Controllers
Library library,
string what)
{
List<T> ret = new List<T>();
List<T> ret = new();
IEnumerable<IMetadataProvider> providers = library?.Providers
.Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
@ -121,6 +121,7 @@ namespace Kyoo.Controllers
$"the season {seasonNumber} of {show.Title}");
season.Show = show;
season.ShowID = show.ID;
season.ShowSlug = show.Slug;
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
season.Title ??= $"Season {season.SeasonNumber}";
return season;
@ -139,6 +140,7 @@ namespace Kyoo.Controllers
"an episode");
episode.Show = show;
episode.ShowID = show.ID;
episode.ShowSlug = show.Slug;
episode.Path = episodePath;
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber;

View File

@ -96,11 +96,11 @@ namespace Kyoo.Controllers
if (episode?.Path == null)
return default;
if (episode.Poster != null)
if (episode.Thumb != null)
{
string localPath = Path.ChangeExtension(episode.Path, "jpg");
if (alwaysDownload || !File.Exists(localPath))
await DownloadImage(episode.Poster, localPath, $"The thumbnail of {episode.Show.Title}");
await DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Show.Title}");
}
return episode;
}

View File

@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Models.DatabaseMigrations.Internal
{
[DbContext(typeof(DatabaseContext))]
[Migration("20210306181259_Initial")]
[Migration("20210312234147_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -71,9 +71,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Path")
.HasColumnType("text");
b.Property<string>("Poster")
.HasColumnType("text");
b.Property<DateTime?>("ReleaseDate")
.HasColumnType("timestamp without time zone");
@ -89,6 +86,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Thumb")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");

View File

@ -318,11 +318,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
EpisodeNumber = table.Column<int>(type: "integer", nullable: false),
AbsoluteNumber = table.Column<int>(type: "integer", nullable: false),
Path = table.Column<string>(type: "text", nullable: true),
Thumb = table.Column<string>(type: "text", nullable: true),
Title = table.Column<string>(type: "text", nullable: true),
Overview = table.Column<string>(type: "text", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
Runtime = table.Column<int>(type: "integer", nullable: false),
Poster = table.Column<string>(type: "text", nullable: true)
Runtime = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{

View File

@ -69,9 +69,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Path")
.HasColumnType("text");
b.Property<string>("Poster")
.HasColumnType("text");
b.Property<DateTime?>("ReleaseDate")
.HasColumnType("timestamp without time zone");
@ -87,6 +84,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Thumb")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");

View File

@ -11,6 +11,7 @@ namespace Kyoo
{
public static async Task Main(string[] args)
{
if (args.Length > 0)
FileSystem.CurrentDirectory = args[0];
if (!File.Exists("./appsettings.json"))

View File

@ -37,6 +37,8 @@ namespace Kyoo
public void ConfigureServices(IServiceCollection services)
{
string publicUrl = _configuration.GetValue<string>("public_url");
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
@ -45,7 +47,7 @@ namespace Kyoo
services.AddControllers()
.AddNewtonsoftJson(x =>
{
x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer();
x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl);
x.SerializerSettings.Converters.Add(new PeopleRoleConverter());
});
services.AddHttpClient();
@ -63,7 +65,6 @@ namespace Kyoo
});
string assemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
string publicUrl = _configuration.GetValue<string>("public_url");
services.AddIdentityCore<User>(o =>
{

View File

@ -351,7 +351,8 @@ namespace Kyoo.Controllers
Title = show.Title,
Path = episodePath,
Show = show,
ShowID = show.ID
ShowID = show.ID,
ShowSlug = show.Slug
};
episode.Tracks = await GetTracks(episode);
return episode;

View File

@ -1,94 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authorization;
namespace Kyoo.Api
{
public class ThumbnailController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
private readonly string _peoplePath;
public ThumbnailController(ILibraryManager libraryManager, IConfiguration config)
{
_libraryManager = libraryManager;
_peoplePath = config.GetValue<string>("peoplePath");
}
[HttpGet("poster/{showSlug}")]
[Authorize(Policy="Read")]
public async Task<IActionResult> GetShowThumb(string showSlug)
{
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
string thumb = Path.Combine(path, "poster.jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
return NotFound();
}
[HttpGet("logo/{showSlug}")]
[Authorize(Policy="Read")]
public async Task<IActionResult> GetShowLogo(string showSlug)
{
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
string thumb = Path.Combine(path, "logo.png");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
return NotFound();
}
[HttpGet("backdrop/{showSlug}")]
[Authorize(Policy="Read")]
public async Task<IActionResult> GetShowBackdrop(string showSlug)
{
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
string thumb = Path.Combine(path, "backdrop.jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
return NotFound();
}
[HttpGet("peopleimg/{peopleSlug}")]
[Authorize(Policy="Read")]
public IActionResult GetPeopleIcon(string peopleSlug)
{
string thumbPath = Path.Combine(_peoplePath, peopleSlug + ".jpg");
if (!System.IO.File.Exists(thumbPath))
return NotFound();
return new PhysicalFileResult(Path.GetFullPath(thumbPath), "image/jpg");
}
[HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Read")]
public async Task<IActionResult> GetEpisodeThumb(string showSlug, int seasonNumber, int episodeNumber)
{
string path = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Path;
if (path == null)
return NotFound();
string thumb = Path.ChangeExtension(path, "jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(Path.GetFullPath(thumb), "image/jpg");
return NotFound();
}
}
}