Automatically download/extract info on API creation (dirtyfix) (#161

* Download thumbnails automatically

* Automatically extract tracks
This commit is contained in:
Zoe Roux 2023-04-03 20:02:12 +09:00
parent d0db4815f1
commit 592c92785f
15 changed files with 73 additions and 24 deletions

View File

@ -20,7 +20,7 @@ COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
RUN dotnet restore RUN dotnet restore
COPY --from=transcoder /transcoder/libtranscoder.so /app COPY --from=transcoder /transcoder/libtranscoder.so /app/out/bin/Kyoo.Host/Debug/net6.0/libtranscoder.so
WORKDIR /kyoo WORKDIR /kyoo
EXPOSE 5000 EXPOSE 5000

View File

@ -69,6 +69,7 @@ namespace Kyoo.Core.Controllers
try try
{ {
_logger.LogInformation("Downloading image {What}", what);
AsyncRef<string> mime = new(); AsyncRef<string> mime = new();
await using Stream reader = await _files.GetReader(url, mime); await using Stream reader = await _files.GetReader(url, mime);
string extension = new FileExtensionContentTypeProvider() string extension = new FileExtensionContentTypeProvider()

View File

@ -138,7 +138,7 @@ namespace Kyoo.Core.Api
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
[ProducesResponseType(StatusCodes.Status409Conflict, Type = typeof(ActionResult<>))] [ProducesResponseType(StatusCodes.Status409Conflict, Type = typeof(ActionResult<>))]
public async Task<ActionResult<T>> Create([FromBody] T resource) public virtual async Task<ActionResult<T>> Create([FromBody] T resource)
{ {
return await Repository.Create(resource); return await Repository.Create(resource);
} }

View File

@ -153,5 +153,14 @@ namespace Kyoo.Core.Api
{ {
return GetImage(identifier, Images.Thumbnail); return GetImage(identifier, Images.Thumbnail);
} }
/// <inheritdoc/>
public override async Task<ActionResult<T>> Create([FromBody] T resource)
{
// TODO: Remove this method and use a websocket API to do that.
ActionResult<T> ret = await base.Create(resource);
await _thumbs.DownloadImages(ret.Value);
return ret;
}
} }
} }

View File

@ -24,6 +24,7 @@ using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils; using Kyoo.Abstractions.Models.Utils;
using Kyoo.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
@ -76,6 +77,15 @@ namespace Kyoo.Core.Api
_files = files; _files = files;
} }
/// <inheritdoc/>
public override async Task<ActionResult<Episode>> Create([FromBody] Episode resource)
{
// TODO: Remove this method and use a websocket API to do that.
resource.Tracks = await _transcoder.ExtractInfos(resource, false);
ActionResult<Episode> ret = await base.Create(resource);
return ret;
}
/// <summary> /// <summary>
/// Get episode's show /// Get episode's show
/// </summary> /// </summary>

View File

@ -48,6 +48,7 @@ namespace Kyoo.Core.Api
/// The library manager used to modify or retrieve information in the data store. /// The library manager used to modify or retrieve information in the data store.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITranscoder _transcoder;
/// <summary> /// <summary>
/// Create a new <see cref="ShowApi"/>. /// Create a new <see cref="ShowApi"/>.
@ -57,12 +58,34 @@ namespace Kyoo.Core.Api
/// </param> /// </param>
/// <param name="files">The file manager used to send images and fonts.</param> /// <param name="files">The file manager used to send images and fonts.</param>
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param> /// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
/// <param name="transcoder">TODO: Remove this.</param>
public ShowApi(ILibraryManager libraryManager, public ShowApi(ILibraryManager libraryManager,
IFileSystem files, IFileSystem files,
IThumbnailsManager thumbs) IThumbnailsManager thumbs,
ITranscoder transcoder)
: base(libraryManager.ShowRepository, files, thumbs) : base(libraryManager.ShowRepository, files, thumbs)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_transcoder = transcoder;
}
/// <inheritdoc/>
public override async Task<ActionResult<Show>> Create([FromBody] Show resource)
{
ActionResult<Show> ret = await base.Create(resource);
if (ret.Value.IsMovie)
{
Episode episode = new()
{
Show = ret.Value,
Title = ret.Value.Title,
Path = ret.Value.Path
};
episode.Tracks = await _transcoder.ExtractInfos(episode, false);
await _libraryManager.Create(episode);
}
return ret;
} }
/// <summary> /// <summary>

View File

@ -24,7 +24,6 @@ using System.Threading.Tasks;
using Autofac; using Autofac;
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using Kyoo.Postgresql; using Kyoo.Postgresql;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -176,6 +175,7 @@ namespace Kyoo.Host
.MinimumLevel.Warning() .MinimumLevel.Warning()
.MinimumLevel.Override("Kyoo", LogEventLevel.Verbose) .MinimumLevel.Override("Kyoo", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal)
.WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code)) .WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code))
.Enrich.WithThreadId() .Enrich.WithThreadId()
.Enrich.FromLogContext(); .Enrich.FromLogContext();

View File

@ -342,8 +342,8 @@ class TheMovieDatabase(Provider):
release_date=datetime.strptime(episode["air_date"], "%Y-%m-%d").date() release_date=datetime.strptime(episode["air_date"], "%Y-%m-%d").date()
if episode["air_date"] if episode["air_date"]
else None, else None,
thumbnail=f"https://image.tmdb.org/t/p/original{episode['poster_path']}" thumbnail=f"https://image.tmdb.org/t/p/original{episode['still_path']}"
if "poster_path" in episode if "still_path" in episode and episode["still_path"] is not None
else None, else None,
external_ids={ external_ids={
self.name: MetadataID( self.name: MetadataID(

View File

@ -41,6 +41,9 @@ class Episode:
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(self.translations[default_language]),
"title": self.translations[default_language].name, "title": self.translations[default_language].name,
"images": {
"1": self.thumbnail,
},
# TODO: The back has bad external id support, we disable it for now # TODO: The back has bad external id support, we disable it for now
"external_ids": None, "external_ids": None,
"show": None, "show": None,

View File

@ -22,4 +22,4 @@ class Genre(str, Enum):
WESTERN = "Western" WESTERN = "Western"
def to_kyoo(self): def to_kyoo(self):
return {"name": f"{self}"} return {"name": self.value}

View File

@ -50,12 +50,12 @@ class Movie:
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(self.translations[default_language]),
"poster": next(iter(self.translations[default_language].posters), None), "images": {
"thumbnail": next( "0": next(iter(self.translations[default_language].posters), None),
iter(self.translations[default_language].thumbnails), None "1": next(iter(self.translations[default_language].thumbnails), None),
), "2": next(iter(self.translations[default_language].logos), None),
"logo": next(iter(self.translations[default_language].logos), None), "3": next(iter(self.translations[default_language].trailers), None),
"trailer": next(iter(self.translations[default_language].trailers), None), },
"studio": next((x.to_kyoo() for x in self.studios), None), "studio": next((x.to_kyoo() for x in self.studios), None),
"release_date": None, "release_date": None,
"startAir": format_date(self.release_date), "startAir": format_date(self.release_date),

View File

@ -30,10 +30,10 @@ class Season:
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(self.translations[default_language]),
"poster": next(iter(self.translations[default_language].posters), None), "images": {
"thumbnail": next( "0": next(iter(self.translations[default_language].posters), None),
iter(self.translations[default_language].thumbnails), None "1": next(iter(self.translations[default_language].thumbnails), None),
), },
"title": self.translations[default_language].name, "title": self.translations[default_language].name,
# TODO: The back has bad external id support, we disable it for now # TODO: The back has bad external id support, we disable it for now
"external_ids": None, "external_ids": None,

View File

@ -52,12 +52,12 @@ class Show:
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(self.translations[default_language]),
"poster": next(iter(self.translations[default_language].posters), None), "images": {
"thumbnail": next( "0": next(iter(self.translations[default_language].posters), None),
iter(self.translations[default_language].thumbnails), None "1": next(iter(self.translations[default_language].thumbnails), None),
), "2": next(iter(self.translations[default_language].logos), None),
"logo": next(iter(self.translations[default_language].logos), None), "3": next(iter(self.translations[default_language].trailers), None),
"trailer": next(iter(self.translations[default_language].trailers), None), },
"studio": next((x.to_kyoo() for x in self.studios), None), "studio": next((x.to_kyoo() for x in self.studios), None),
"title": self.translations[default_language].name, "title": self.translations[default_language].name,
"genres": [x.to_kyoo() for x in self.genres], "genres": [x.to_kyoo() for x in self.genres],

View File

@ -12,6 +12,9 @@ class Studio:
def to_kyoo(self): def to_kyoo(self):
return { return {
**asdict(self), **asdict(self),
"images": {
"2": next(iter(self.logos), None),
},
# TODO: The back has bad external id support, we disable it for now # TODO: The back has bad external id support, we disable it for now
"external_ids": None, "external_ids": None,
} }

View File

@ -38,7 +38,7 @@ def provider_cache(*args):
if "event" in ic: if "event" in ic:
await ic["event"].wait() await ic["event"].wait()
if not ic["ret"]: if "ret" not in ic:
raise ProviderError("Cache miss. Another error should exist") raise ProviderError("Cache miss. Another error should exist")
return ic["ret"] return ic["ret"]
ic["event"] = asyncio.Event() ic["event"] = asyncio.Event()