diff --git a/.editorconfig b/.editorconfig
index 7b812979..db19b724 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,6 +14,12 @@ indent_style = space
indent_size = 2
[*.cs]
+csharp_prefer_braces = false
+dotnet_diagnostic.IDE0130.severity = none
+dotnet_diagnostic.IDE0058.severity = none
+dotnet_diagnostic.IDE0046.severity = none
+dotnet_diagnostic.CA1848.severity = none
+dotnet_diagnostic.CA2007.severity = none
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
csharp_using_directive_placement = outside_namespace:warning
diff --git a/.env.example b/.env.example
new file mode 100644
index 00000000..6cfd0e87
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,2 @@
+TVDB__APIKEY=
+THEMOVIEDB__APIKEY=
diff --git a/.gitignore b/.gitignore
index 694e9d6b..42235b8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,9 @@ libtranscoder.so
libtranscoder.dylib
transcoder.dll
+video
+.env
+
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
diff --git a/README.md b/README.md
index 9c1b965a..983e9f05 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,6 @@ services:
environment:
- KYOO_DATADIR=/var/lib/kyoo
- BASICS__PUBLICURL=https://demo.kyoo.moe
- - BASICS__MetadataInShow=false
- DATABASE__ENABLED=postgres
- DATABASE__CONFIGURATIONS__POSTGRES__SERVER=postgres
- DATABASE__CONFIGURATIONS__POSTGRES__USER ID=kyoo
diff --git a/deployment/changelog.json b/deployment/changelog.json
deleted file mode 100644
index 4e7c94ec..00000000
--- a/deployment/changelog.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "categories": [
- {
- "title": "## Features",
- "labels": ["enhancement"]
- },
- {
- "title": "## Fixes",
- "labels": ["bug"]
- },
- {
- "title": "## Web App",
- "labels": ["webapp"]
- }
- ],
- "template": "${{CHANGELOG}}\n\n\nOthers
\n\n${{UNCATEGORIZED}}\n ",
- "pr_template": "- ${{TITLE}} (PR: #${{NUMBER}})"
-}
diff --git a/deployment/kyoo.service b/deployment/kyoo.service
index 5727b7c5..b9cdcf0c 100644
--- a/deployment/kyoo.service
+++ b/deployment/kyoo.service
@@ -1,6 +1,5 @@
[Unit]
Description=Kyoo Media Server
-Requires=postgresql.service
After=network.target
[Service]
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..313392e8
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,35 @@
+version: "3.8"
+
+services:
+ kyoo:
+ build: .
+ restart: on-failure
+ environment:
+ - KYOO_DATADIR=/var/lib/kyoo
+ - BASICS__PUBLICURL=http://localhost:5000
+ - DATABASE__ENABLED=postgres
+ - DATABASE__CONFIGURATIONS__POSTGRES__SERVER=postgres
+ - DATABASE__CONFIGURATIONS__POSTGRES__USER ID=kyoo
+ - DATABASE__CONFIGURATIONS__POSTGRES__PASSWORD=kyooPassword
+ - TVDB__APIKEY=${TVDB__APIKEY}
+ - THEMOVIEDB__APIKEY=${THEMOVIEDB__APIKEY}
+ ports:
+ - "5000:5000"
+ depends_on:
+ - postgres
+ volumes:
+ - kyoo:/var/lib/kyoo
+ - ./video:/video
+ postgres:
+ image: "postgres"
+ restart: on-failure
+ environment:
+ - POSTGRES_USER=kyoo
+ - POSTGRES_PASSWORD=kyooPassword
+ volumes:
+ - db:/var/lib/postgresql/data
+
+volumes:
+ kyoo:
+ db:
+
diff --git a/docs/start/setting_up.md b/docs/start/setting_up.md
index 6a0b82ce..656bd460 100644
--- a/docs/start/setting_up.md
+++ b/docs/start/setting_up.md
@@ -27,22 +27,6 @@ We are going to take a look at the fields you might want to change to tailor Kyo
- ```pluginsPath```: The directory where the plugins are stored
- ```transmuxPath```: The directory where the transmux-ed video are stored (used as a cache)
- ```transcodePath```: The directory where the transcoded video are stored (used as a cache)
- - ```metadataInShow```: A boolean telling if the Movie/Show metadata (posters, extracted subtitles, Chapters) will be stored in the same directory as the video, in an ```Extra``` directory, or in the ```metadataPath```.
- For example, if ```metadataInShow``` is true, your file tree wil look something like this:
-
- ```bash
- /my-movies
- |
- -- My First Movie/
- |
- -- My First Movie.mp4
- -- Extra/
- |
- -- poster.jpe
- -- etc...
- ```
-
- **Warning** Therefore, if your shows are not in individual folders, it is recommended to set ```metadataInShow``` to ```false```. If you don't, all the shows will share the same metadata we are sure you don't want that ;)
- ```database```
- ```enabled```: Which database to use. Either ```sqlite``` (by default) or ```postgres```. SQLite is easier to use & manage if you don't have an SQL server on your machine. However, if you have a large amount of videos, we recommend using Postgres, which is more powerful to manage large databases
@@ -70,7 +54,7 @@ We are going to take a look at the fields you might want to change to tailor Kyo
- ```tvdb```
- ```apikey```: The API key that will be used to interact with the TVDB's API. See [there](https://thetvdb.com/api-information) to get one
-- ```the-moviedb```
+- ```themoviedb```
- ```apikey```: The API key that will be used to interact with TMDB's API. See [there](https://developers.themoviedb.org/3/getting-started/introduction) to get one
## Using a Container
diff --git a/front/projects/host/src/app/models/font.ts b/front/projects/host/src/app/models/font.ts
new file mode 100644
index 00000000..9f2e93e5
--- /dev/null
+++ b/front/projects/host/src/app/models/font.ts
@@ -0,0 +1,5 @@
+export interface Font {
+ slug: string;
+ file: string;
+ format: string;
+}
diff --git a/front/projects/host/src/app/pages/player/player.component.ts b/front/projects/host/src/app/pages/player/player.component.ts
index c53be96f..f41e3907 100644
--- a/front/projects/host/src/app/pages/player/player.component.ts
+++ b/front/projects/host/src/app/pages/player/player.component.ts
@@ -15,7 +15,7 @@ import { DomSanitizer, Title } from "@angular/platform-browser";
import { ActivatedRoute, Event, NavigationCancel, NavigationEnd, NavigationStart, Router } from "@angular/router";
import { OidcSecurityService } from "angular-auth-oidc-client";
import Hls from "hls.js";
-import { ShowService } from "../../services/api.service";
+import { EpisodeService, ShowService } from "../../services/api.service";
import { StartupService } from "../../services/startup.service";
import {
getWhatIsSupported,
@@ -24,6 +24,7 @@ import {
} from "./playbackMethodDetector";
import { AppComponent } from "../../app.component";
import { Track, WatchItem } from "../../models/watch-item";
+import { Font } from "../../models/font";
import SubtitlesOctopus from "libass-wasm/dist/js/subtitles-octopus.js";
import MouseMoveEvent = JQuery.MouseMoveEvent;
import TouchMoveEvent = JQuery.TouchMoveEvent;
@@ -161,13 +162,12 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
private hlsPlayer: Hls = new Hls();
private oidcSecurity: OidcSecurityService;
constructor(private route: ActivatedRoute,
- private sanitizer: DomSanitizer,
private snackBar: MatSnackBar,
private title: Title,
private router: Router,
private location: Location,
private injector: Injector,
- private shows: ShowService,
+ private episode: EpisodeService,
private startup: StartupService)
{ }
@@ -481,18 +481,13 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
if (subtitle.codec === "ass")
{
- if (!this.subtitlesManager)
- {
- const fonts: { [key: string]: string } = await this.shows.getFonts(this.item.showSlug).toPromise();
- this.subtitlesManager = new SubtitlesOctopus({
- video: this.player,
- subUrl: `subtitle/${subtitle.slug}`,
- fonts: Object.values(fonts),
- renderMode: "fast"
- });
- }
- else
- this.subtitlesManager.setTrackByUrl(`subtitle/${subtitle.slug}`);
+ const fonts: Font[] = await this.episode.getFonts(this.item.slug).toPromise();
+ this.subtitlesManager = new SubtitlesOctopus({
+ video: this.player,
+ subUrl: `subtitle/${subtitle.slug}`,
+ fonts: fonts.map(x => `/api/episode/${this.item.slug}/font/${x.slug}.${x.format}`),
+ renderMode: "fast"
+ });
}
else if (subtitle.codec === "subrip")
{
diff --git a/front/projects/host/src/app/services/api.service.ts b/front/projects/host/src/app/services/api.service.ts
index 33d18b53..a15aa042 100644
--- a/front/projects/host/src/app/services/api.service.ts
+++ b/front/projects/host/src/app/services/api.service.ts
@@ -9,6 +9,7 @@ import { LibraryItem } from "../models/resources/library-item";
import { map } from "rxjs/operators";
import { Season } from "../models/resources/season";
import { Episode } from "../models/resources/episode";
+import { Font } from "../models/font";
import { People } from "../models/resources/people";
import { Show } from "../models/resources/show";
import { Studio } from "../models/resources/studio";
@@ -126,6 +127,11 @@ export class EpisodeService extends CrudApi
return this.client.get(`/api/seasons/${show}-s${seasonNumber}/episodes${this.ArgsAsQuery(args)}`)
.pipe(map(x => Object.assign(new Page(), x)));
}
+
+ getFonts(id: string | number): Observable
+ {
+ return this.client.get(`/api/episodes/${id}/fonts`);
+ }
}
@Injectable({
@@ -177,11 +183,6 @@ export class ShowService extends CrudApi
return this.client.get>(`/api/collections/${collection}/shows${this.ArgsAsQuery(args)}`)
.pipe(map(x => Object.assign(new Page(), x)));
}
-
- getFonts(id: string | number): Observable<{[font: string]: string}>
- {
- return this.client.get(`/api/shows/${id}/fonts`);
- }
}
@Injectable({
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 823f045a..31fdbf88 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -28,10 +28,6 @@
true
true
-
-
-
-
true
@@ -48,8 +44,7 @@
- true
$(MSBuildThisFileDirectory)../Kyoo.ruleset
-
+
diff --git a/src/Kyoo.Abstractions/Controllers/ITranscoder.cs b/src/Kyoo.Abstractions/Controllers/ITranscoder.cs
new file mode 100644
index 00000000..b87a8d5d
--- /dev/null
+++ b/src/Kyoo.Abstractions/Controllers/ITranscoder.cs
@@ -0,0 +1,63 @@
+// Kyoo - A portable and vast media library solution.
+// Copyright (c) Kyoo.
+//
+// See AUTHORS.md and LICENSE file in the project root for full license information.
+//
+// Kyoo is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// Kyoo is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Kyoo. If not, see .
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Kyoo.Abstractions.Models;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Kyoo.Abstractions.Controllers
+{
+ ///
+ /// Transcoder responsible of handling low level video details.
+ ///
+ public interface ITranscoder
+ {
+ ///
+ /// Retrieve tracks for a specific episode.
+ /// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
+ ///
+ /// The episode to retrieve tracks for.
+ /// Should the cache be invalidated and subtitles and others be re-extracted?
+ /// The list of tracks available for this episode.
+ Task> ExtractInfos(Episode episode, bool reExtract);
+
+ ///
+ /// List fonts assosiated with this episode.
+ ///
+ /// Th episode to list fonts for.
+ /// The list of attachements for this epiosode.
+ Task> ListFonts(Episode episode);
+
+ ///
+ /// Get the specified font for this episode.
+ ///
+ /// The episode to list fonts for.
+ /// The slug of the specific font to retrive.
+ /// The with the given slug or null.
+ [ItemCanBeNull] Task GetFont(Episode episode, string slug);
+
+ ///
+ /// Transmux the selected episode to hls.
+ ///
+ /// The episode to transmux.
+ /// The master file (m3u8) of the transmuxed hls file.
+ IActionResult Transmux(Episode episode);
+ }
+}
diff --git a/src/Kyoo.Abstractions/Models/Attributes/EditableRelationAttribute.cs b/src/Kyoo.Abstractions/Models/Attributes/EditableRelationAttribute.cs
index 5b78b3f8..a94f401b 100644
--- a/src/Kyoo.Abstractions/Models/Attributes/EditableRelationAttribute.cs
+++ b/src/Kyoo.Abstractions/Models/Attributes/EditableRelationAttribute.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/Attributes/Serializer/DeserializeIgnoreAttribute.cs b/src/Kyoo.Abstractions/Models/Attributes/Serializer/DeserializeIgnoreAttribute.cs
index e1ab8bcc..8b10ee87 100644
--- a/src/Kyoo.Abstractions/Models/Attributes/Serializer/DeserializeIgnoreAttribute.cs
+++ b/src/Kyoo.Abstractions/Models/Attributes/Serializer/DeserializeIgnoreAttribute.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/Attributes/Serializer/SerializeIgnoreAttribute.cs b/src/Kyoo.Abstractions/Models/Attributes/Serializer/SerializeIgnoreAttribute.cs
index 9fc37750..4a87d3f9 100644
--- a/src/Kyoo.Abstractions/Models/Attributes/Serializer/SerializeIgnoreAttribute.cs
+++ b/src/Kyoo.Abstractions/Models/Attributes/Serializer/SerializeIgnoreAttribute.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs b/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs
new file mode 100644
index 00000000..6b4d86e2
--- /dev/null
+++ b/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs
@@ -0,0 +1,47 @@
+// Kyoo - A portable and vast media library solution.
+// Copyright (c) Kyoo.
+//
+// See AUTHORS.md and LICENSE file in the project root for full license information.
+//
+// Kyoo is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// Kyoo is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Kyoo. If not, see .
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Kyoo.Abstractions.Models.Exceptions
+{
+ ///
+ /// An exception thrown when a part of the app has a fatal issue.
+ ///
+ [Serializable]
+ public class HealthException : Exception
+ {
+ ///
+ /// Create a new with a custom message.
+ ///
+ /// The message to use.
+ public HealthException(string message)
+ : base(message)
+ { }
+
+ ///
+ /// The serialization constructor
+ ///
+ /// Serialization infos
+ /// The serialization context
+ protected HealthException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ { }
+ }
+}
diff --git a/src/Kyoo.Abstractions/Models/Font.cs b/src/Kyoo.Abstractions/Models/Font.cs
new file mode 100644
index 00000000..eafb3bdd
--- /dev/null
+++ b/src/Kyoo.Abstractions/Models/Font.cs
@@ -0,0 +1,67 @@
+// Kyoo - A portable and vast media library solution.
+// Copyright (c) Kyoo.
+//
+// See AUTHORS.md and LICENSE file in the project root for full license information.
+//
+// Kyoo is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// Kyoo is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Kyoo. If not, see .
+
+using Kyoo.Abstractions.Models.Attributes;
+using Kyoo.Utils;
+using PathIO = System.IO.Path;
+
+namespace Kyoo.Abstractions.Models
+{
+ ///
+ /// A font of an .
+ ///
+ public class Font
+ {
+ ///
+ /// A human-readable identifier, used in the URL.
+ ///
+ public string Slug { get; set; }
+
+ ///
+ /// The name of the font file (with the extension).
+ ///
+ public string File { get; set; }
+
+ ///
+ /// The format of this font (the extension).
+ ///
+ public string Format { get; set; }
+
+ ///
+ /// The path of the font.
+ ///
+ [SerializeIgnore] public string Path { get; set; }
+
+ ///
+ /// Create a new empty .
+ ///
+ public Font() { }
+
+ ///
+ /// Create a new from a path.
+ ///
+ /// The path of the font.
+ public Font(string path)
+ {
+ Slug = Utility.ToSlug(PathIO.GetFileNameWithoutExtension(path));
+ Path = path;
+ File = PathIO.GetFileName(path);
+ Format = PathIO.GetExtension(path).Replace(".", string.Empty);
+ }
+ }
+}
diff --git a/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/src/Kyoo.Abstractions/Models/Resources/Episode.cs
index 42e9ecee..10d5f5b6 100644
--- a/src/Kyoo.Abstractions/Models/Resources/Episode.cs
+++ b/src/Kyoo.Abstractions/Models/Resources/Episode.cs
@@ -34,7 +34,8 @@ namespace Kyoo.Abstractions.Models
public int ID { get; set; }
///
- [Computed] public string Slug
+ [Computed]
+ public string Slug
{
get
{
@@ -45,7 +46,9 @@ namespace Kyoo.Abstractions.Models
: null;
}
- [UsedImplicitly] [NotNull] private set
+ [UsedImplicitly]
+ [NotNull]
+ private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
diff --git a/src/Kyoo.Abstractions/Models/Resources/Season.cs b/src/Kyoo.Abstractions/Models/Resources/Season.cs
index 29d22d44..f07055e8 100644
--- a/src/Kyoo.Abstractions/Models/Resources/Season.cs
+++ b/src/Kyoo.Abstractions/Models/Resources/Season.cs
@@ -34,7 +34,8 @@ namespace Kyoo.Abstractions.Models
public int ID { get; set; }
///
- [Computed] public string Slug
+ [Computed]
+ public string Slug
{
get
{
@@ -43,7 +44,9 @@ namespace Kyoo.Abstractions.Models
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
}
- [UsedImplicitly] [NotNull] private set
+ [UsedImplicitly]
+ [NotNull]
+ private set
{
Match match = Regex.Match(value ?? string.Empty, @"(?.+)-s(?\d+)");
diff --git a/src/Kyoo.Abstractions/Models/Resources/Track.cs b/src/Kyoo.Abstractions/Models/Resources/Track.cs
index 8e41c7ee..59b3746f 100644
--- a/src/Kyoo.Abstractions/Models/Resources/Track.cs
+++ b/src/Kyoo.Abstractions/Models/Resources/Track.cs
@@ -50,12 +50,6 @@ namespace Kyoo.Abstractions.Models
/// The stream is a subtitle.
///
Subtitle = 3,
-
- ///
- /// The stream is an attachment (a font, an image or something else).
- /// Only fonts are handled by kyoo but they are not saved to the database.
- ///
- Attachment = 4
}
///
@@ -67,7 +61,8 @@ namespace Kyoo.Abstractions.Models
public int ID { get; set; }
///
- [Computed] public string Slug
+ [Computed]
+ public string Slug
{
get
{
@@ -77,7 +72,8 @@ namespace Kyoo.Abstractions.Models
return $"{episode}.{Language ?? "und"}{index}{(IsForced ? ".forced" : string.Empty)}.{type}";
}
- [UsedImplicitly] private set
+ [UsedImplicitly]
+ private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
@@ -86,8 +82,10 @@ namespace Kyoo.Abstractions.Models
if (!match.Success)
{
- throw new ArgumentException("Invalid track slug. " +
- "Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]");
+ throw new ArgumentException(
+ "Invalid track slug. " +
+ "Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]"
+ );
}
_episodeSlug = match.Groups["ep"].Value;
@@ -148,7 +146,8 @@ namespace Kyoo.Abstractions.Models
///
/// The episode that uses this track.
///
- [LoadableRelation(nameof(EpisodeID))] public Episode Episode
+ [LoadableRelation(nameof(EpisodeID))]
+ public Episode Episode
{
get => _episode;
set
diff --git a/src/Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs b/src/Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs
index 631c417f..66a11a3c 100644
--- a/src/Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs
+++ b/src/Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/Utils/Pagination.cs b/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
index e52bbf63..233b317d 100644
--- a/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
+++ b/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/Utils/Sort.cs b/src/Kyoo.Abstractions/Models/Utils/Sort.cs
index e68c8a8e..1aa88673 100644
--- a/src/Kyoo.Abstractions/Models/Utils/Sort.cs
+++ b/src/Kyoo.Abstractions/Models/Utils/Sort.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Abstractions/Models/WatchItem.cs b/src/Kyoo.Abstractions/Models/WatchItem.cs
index bedd5df5..7c7afc3b 100644
--- a/src/Kyoo.Abstractions/Models/WatchItem.cs
+++ b/src/Kyoo.Abstractions/Models/WatchItem.cs
@@ -200,7 +200,7 @@ namespace Kyoo.Abstractions.Models
ReleaseDate = ep.ReleaseDate,
Path = ep.Path,
Images = ep.Show.Images,
- Container = PathIO.GetExtension(ep.Path)![1..],
+ Container = PathIO.GetExtension(ep.Path).Replace(".", string.Empty),
Video = ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video),
Audios = ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(),
Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(),
diff --git a/src/Kyoo.Abstractions/Utility/Merger.cs b/src/Kyoo.Abstractions/Utility/Merger.cs
index f8bb8286..237eff06 100644
--- a/src/Kyoo.Abstractions/Utility/Merger.cs
+++ b/src/Kyoo.Abstractions/Utility/Merger.cs
@@ -170,7 +170,7 @@ namespace Kyoo.Utils
Type type = typeof(T);
IEnumerable properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite
- && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
+ && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
foreach (PropertyInfo property in properties)
{
@@ -221,7 +221,7 @@ namespace Kyoo.Utils
Type type = typeof(T);
IEnumerable properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite
- && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
+ && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
if (where != null)
properties = properties.Where(where);
@@ -293,7 +293,7 @@ namespace Kyoo.Utils
Type type = typeof(T);
IEnumerable properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite
- && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
+ && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
if (where != null)
properties = properties.Where(where);
@@ -325,7 +325,7 @@ namespace Kyoo.Utils
property.SetValue(first, newDictionary);
}
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)
- && property.PropertyType != typeof(string))
+ && property.PropertyType != typeof(string))
{
Type enumerableType = Utility.GetGenericDefinition(property.PropertyType, typeof(IEnumerable<>))
.GenericTypeArguments
diff --git a/src/Kyoo.Abstractions/Utility/Utility.cs b/src/Kyoo.Abstractions/Utility/Utility.cs
index 04a1f948..bce40f4e 100644
--- a/src/Kyoo.Abstractions/Utility/Utility.cs
+++ b/src/Kyoo.Abstractions/Utility/Utility.cs
@@ -43,8 +43,8 @@ namespace Kyoo.Utils
{
if (ex == null)
return false;
- return ex.Body is MemberExpression ||
- (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
+ return ex.Body is MemberExpression
+ || (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
}
///
@@ -242,9 +242,12 @@ namespace Kyoo.Utils
.Where(x => x.Name == name)
.Where(x => x.GetGenericArguments().Length == generics.Length)
.Where(x => x.GetParameters().Length == args.Length)
- .IfEmpty(() => throw new ArgumentException($"A method named {name} with " +
- $"{args.Length} arguments and {generics.Length} generic " +
- $"types could not be found on {type.Name}."))
+ .IfEmpty(() =>
+ {
+ throw new ArgumentException($"A method named {name} with " +
+ $"{args.Length} arguments and {generics.Length} generic " +
+ $"types could not be found on {type.Name}.");
+ })
// TODO this won't work but I don't know why.
// .Where(x =>
// {
diff --git a/src/Kyoo.Authentication/Controllers/PermissionValidator.cs b/src/Kyoo.Authentication/Controllers/PermissionValidator.cs
index 490c7649..4af4d963 100644
--- a/src/Kyoo.Authentication/Controllers/PermissionValidator.cs
+++ b/src/Kyoo.Authentication/Controllers/PermissionValidator.cs
@@ -154,12 +154,12 @@ namespace Kyoo.Authentication
return;
default:
throw new ArgumentException("Multiple non-matching partial permission attribute " +
- "are not supported.");
+ "are not supported.");
}
if (permission == null || kind == null)
{
throw new ArgumentException("The permission type or kind is still missing after two partial " +
- "permission attributes, this is unsupported.");
+ "permission attributes, this is unsupported.");
}
}
diff --git a/src/Kyoo.Core/Controllers/ConfigurationManager.cs b/src/Kyoo.Core/Controllers/ConfigurationManager.cs
index 10e0843b..ba3372ad 100644
--- a/src/Kyoo.Core/Controllers/ConfigurationManager.cs
+++ b/src/Kyoo.Core/Controllers/ConfigurationManager.cs
@@ -163,7 +163,7 @@ namespace Kyoo.Core.Controllers
if (typeof(T).IsAssignableFrom(type))
{
throw new InvalidCastException($"The type {typeof(T).Name} is not valid for " +
- $"a resource of type {type.Name}.");
+ $"a resource of type {type.Name}.");
}
return (T)GetValue(path);
}
diff --git a/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs b/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs
index 44fe5154..42d77f92 100644
--- a/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs
+++ b/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs
@@ -153,16 +153,12 @@ namespace Kyoo.Core.Controllers
///
public Task GetExtraDirectory(T resource)
{
- if (!_options.CurrentValue.MetadataInShow)
- return Task.FromResult(null);
- return Task.FromResult(resource switch
+ string path = resource switch
{
- Show show => Combine(show.Path, "Extra"),
- Season season => Combine(season.Show.Path, "Extra"),
- Episode episode => Combine(episode.Show.Path, "Extra"),
- Track track => Combine(track.Episode.Show.Path, "Extra"),
- _ => null
- });
+ IResource res => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLower(), res.Slug),
+ _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLower())
+ };
+ return CreateDirectory(path);
}
///
diff --git a/src/Kyoo.Core/Controllers/RegexIdentifier.cs b/src/Kyoo.Core/Controllers/RegexIdentifier.cs
index f4931338..ba9bc19a 100644
--- a/src/Kyoo.Core/Controllers/RegexIdentifier.cs
+++ b/src/Kyoo.Core/Controllers/RegexIdentifier.cs
@@ -25,8 +25,8 @@ using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
+using Kyoo.Core.Models;
using Kyoo.Core.Models.Options;
-using Kyoo.Core.Models.Watch;
using Kyoo.Utils;
using Microsoft.Extensions.Options;
diff --git a/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
index d54d251f..4a139a33 100644
--- a/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
+++ b/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
@@ -73,16 +73,16 @@ namespace Kyoo.Core.Controllers
public Task GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
- && x.SeasonNumber == seasonNumber
- && x.EpisodeNumber == episodeNumber);
+ && x.SeasonNumber == seasonNumber
+ && x.EpisodeNumber == episodeNumber);
}
///
public Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
- && x.SeasonNumber == seasonNumber
- && x.EpisodeNumber == episodeNumber);
+ && x.SeasonNumber == seasonNumber
+ && x.EpisodeNumber == episodeNumber);
}
///
@@ -107,14 +107,14 @@ namespace Kyoo.Core.Controllers
public Task GetAbsolute(int showID, int absoluteNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
- && x.AbsoluteNumber == absoluteNumber);
+ && x.AbsoluteNumber == absoluteNumber);
}
///
public Task GetAbsolute(string showSlug, int absoluteNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
- && x.AbsoluteNumber == absoluteNumber);
+ && x.AbsoluteNumber == absoluteNumber);
}
///
diff --git a/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
index 3a85e98a..db36be70 100644
--- a/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
+++ b/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
@@ -82,14 +82,14 @@ namespace Kyoo.Core.Controllers
public Task GetOrDefault(int showID, int seasonNumber)
{
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
- && x.SeasonNumber == seasonNumber);
+ && x.SeasonNumber == seasonNumber);
}
///
public Task GetOrDefault(string showSlug, int seasonNumber)
{
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
- && x.SeasonNumber == seasonNumber);
+ && x.SeasonNumber == seasonNumber);
}
///
diff --git a/src/Kyoo.Core/Controllers/ThumbnailsManager.cs b/src/Kyoo.Core/Controllers/ThumbnailsManager.cs
index 0d302ac5..cdde9452 100644
--- a/src/Kyoo.Core/Controllers/ThumbnailsManager.cs
+++ b/src/Kyoo.Core/Controllers/ThumbnailsManager.cs
@@ -129,18 +129,6 @@ namespace Kyoo.Core.Controllers
Images.Trailer => "trailer",
_ => $"{imageID}"
};
-
- switch (item)
- {
- case Season season:
- imageName = $"season-{season.SeasonNumber}-{imageName}";
- break;
- case Episode episode:
- directory = await _files.CreateDirectory(_files.Combine(directory, "Thumbnails"));
- imageName = $"{Path.GetFileNameWithoutExtension(episode.Path)}-{imageName}";
- break;
- }
-
return _files.Combine(directory, imageName);
}
diff --git a/src/Kyoo.Core/Controllers/Transcoder.cs b/src/Kyoo.Core/Controllers/Transcoder.cs
index e23896d9..bff0ddd1 100644
--- a/src/Kyoo.Core/Controllers/Transcoder.cs
+++ b/src/Kyoo.Core/Controllers/Transcoder.cs
@@ -19,11 +19,15 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
+using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Core.Models.Options;
+using Kyoo.Core.Models.Watch;
+using Kyoo.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -48,16 +52,16 @@ namespace Kyoo.Core.Controllers
private const string TranscoderPath = "transcoder";
///
- /// Initialize the C library, setup the logger and return the size of a .
+ /// Initialize the C library, setup the logger and return the size of a .
///
- /// The size of a
+ /// The size of a
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int init();
///
- /// Initialize the C library, setup the logger and return the size of a .
+ /// Initialize the C library, setup the logger and return the size of a .
///
- /// The size of a
+ /// The size of a
public static int Init() => init();
///
@@ -99,7 +103,7 @@ namespace Kyoo.Core.Controllers
/// The size of the returned array.
/// The number of tracks in the returned array.
/// Should the cache be invalidated and information re-extracted or not?
- /// A pointer to an array of
+ /// A pointer to an array of
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr extract_infos(string path,
@@ -109,7 +113,7 @@ namespace Kyoo.Core.Controllers
bool reExtract);
///
- /// An helper method to free an array of .
+ /// An helper method to free an array of .
///
/// A pointer to the first element of the array
/// The number of items in the array.
@@ -128,7 +132,7 @@ namespace Kyoo.Core.Controllers
path = path.Replace('\\', '/');
outPath = outPath.Replace('\\', '/');
- int size = Marshal.SizeOf();
+ int size = Marshal.SizeOf();
IntPtr ptr = extract_infos(path, outPath, out uint arrayLength, out uint trackCount, reExtract);
IntPtr streamsPtr = ptr;
Track[] tracks;
@@ -140,8 +144,8 @@ namespace Kyoo.Core.Controllers
int j = 0;
for (int i = 0; i < arrayLength; i++)
{
- Models.Watch.Stream stream = Marshal.PtrToStructure(streamsPtr);
- if (stream!.Type != StreamType.Unknown)
+ FTrack stream = Marshal.PtrToStructure(streamsPtr);
+ if (stream!.Type != FTrackType.Unknown && stream.Type != FTrackType.Attachment)
{
tracks[j] = stream.ToTrack();
j++;
@@ -174,6 +178,11 @@ namespace Kyoo.Core.Controllers
///
private readonly ILogger _logger;
+ ///
+ /// if the C library has been checked, otherwise.
+ ///
+ private bool _initialized;
+
///
/// Create a new .
///
@@ -187,14 +196,29 @@ namespace Kyoo.Core.Controllers
_files = files;
_options = options;
_logger = logger;
+ }
- if (TranscoderAPI.Init() != Marshal.SizeOf())
- _logger.LogCritical("The transcoder library could not be initialized correctly");
+ ///
+ /// Check if the C library can be used or if there is an issue with it.
+ ///
+ /// Should the healthcheck be abborted if the transcoder was already initialized?
+ /// If the transcoder is corrupted, this exception in thrown.
+ public void CheckHealth(bool fastStop = false)
+ {
+ if (fastStop && _initialized)
+ return;
+ if (TranscoderAPI.Init() == Marshal.SizeOf())
+ return;
+ _initialized = true;
+ _logger.LogCritical("The transcoder library could not be initialized correctly");
+ throw new HealthException("The transcoder library is corrupted or invalid.");
}
///
public async Task> ExtractInfos(Episode episode, bool reExtract)
{
+ CheckHealth(true);
+
string dir = await _files.GetExtraDirectory(episode);
if (dir == null)
throw new ArgumentException("Invalid path.");
@@ -204,9 +228,31 @@ namespace Kyoo.Core.Controllers
);
}
+ ///
+ public async Task> ListFonts(Episode episode)
+ {
+ string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments");
+ return (await _files.ListFiles(path))
+ .Select(x => new Font(x))
+ .ToArray();
+ }
+
+ ///
+ public async Task GetFont(Episode episode, string slug)
+ {
+ string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments");
+ string font = (await _files.ListFiles(path))
+ .FirstOrDefault(x => Utility.ToSlug(Path.GetFileNameWithoutExtension(x)) == slug);
+ if (font == null)
+ return null;
+ return new Font(font);
+ }
+
///
public IActionResult Transmux(Episode episode)
{
+ CheckHealth(true);
+
string folder = Path.Combine(_options.Value.TransmuxPath, episode.Slug);
string manifest = Path.GetFullPath(Path.Combine(folder, episode.Slug + ".m3u8"));
@@ -260,7 +306,7 @@ namespace Kyoo.Core.Controllers
_logger = logger;
}
-// We use threads so tasks are not always awaited.
+ // We use threads so tasks are not always awaited.
#pragma warning disable 4014
///
@@ -293,27 +339,4 @@ namespace Kyoo.Core.Controllers
#pragma warning restore 4014
}
}
-
- ///
- /// The transcoder used by the . This is on a different interface than the file system
- /// to offset the work.
- ///
- public interface ITranscoder
- {
- ///
- /// Retrieve tracks for a specific episode.
- /// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
- ///
- /// The episode to retrieve tracks for.
- /// Should the cache be invalidated and subtitles and others be re-extracted?
- /// The list of tracks available for this episode.
- Task> ExtractInfos(Episode episode, bool reExtract);
-
- ///
- /// Transmux the selected episode to hls.
- ///
- /// The episode to transmux.
- /// The master file (m3u8) of the transmuxed hls file.
- IActionResult Transmux(Episode episode);
- }
}
diff --git a/src/Kyoo.Core/Models/Stream.cs b/src/Kyoo.Core/Models/FTrack.cs
similarity index 67%
rename from src/Kyoo.Core/Models/Stream.cs
rename to src/Kyoo.Core/Models/FTrack.cs
index ff6979d6..cb779e23 100644
--- a/src/Kyoo.Core/Models/Stream.cs
+++ b/src/Kyoo.Core/Models/FTrack.cs
@@ -21,11 +21,45 @@ using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Models.Watch
{
+ ///
+ /// The list of available stream types.
+ /// Attachments are only used temporarily by the transcoder but are not stored in a database.
+ /// This is another enum used internally to communicate with ffmpeg.
+ ///
+ public enum FTrackType
+ {
+ ///
+ /// The type of the stream is not known.
+ ///
+ Unknown = StreamType.Unknown,
+
+ ///
+ /// The stream is a video.
+ ///
+ Video = StreamType.Video,
+
+ ///
+ /// The stream is an audio.
+ ///
+ Audio = StreamType.Audio,
+
+ ///
+ /// The stream is a subtitle.
+ ///
+ Subtitle = StreamType.Subtitle,
+
+ ///
+ /// The stream is an attachment (a font, an image or something else).
+ /// Only fonts are handled by kyoo but they are not saved to the database.
+ ///
+ Attachment
+ }
+
///
/// The unmanaged stream that the transcoder will return.
///
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- public struct Stream
+ public struct FTrack
{
///
/// The title of the stream.
@@ -60,7 +94,7 @@ namespace Kyoo.Core.Models.Watch
///
/// The type of this stream.
///
- public StreamType Type;
+ public FTrackType Type;
///
/// Create a track from this stream.
@@ -76,7 +110,7 @@ namespace Kyoo.Core.Models.Watch
IsDefault = IsDefault,
IsForced = IsForced,
Path = Path,
- Type = Type,
+ Type = Type < FTrackType.Attachment ? (StreamType)Type : StreamType.Unknown,
IsExternal = false
};
}
diff --git a/src/Kyoo.Core/Models/FileExtensions.cs b/src/Kyoo.Core/Models/FileExtensions.cs
index e28c9007..5db0b582 100644
--- a/src/Kyoo.Core/Models/FileExtensions.cs
+++ b/src/Kyoo.Core/Models/FileExtensions.cs
@@ -20,7 +20,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
-namespace Kyoo.Core.Models.Watch
+namespace Kyoo.Core.Models
{
///
/// A static class allowing one to identify files extensions.
diff --git a/src/Kyoo.Core/Models/Options/BasicOptions.cs b/src/Kyoo.Core/Models/Options/BasicOptions.cs
index 2fa00249..18633b4a 100644
--- a/src/Kyoo.Core/Models/Options/BasicOptions.cs
+++ b/src/Kyoo.Core/Models/Options/BasicOptions.cs
@@ -17,7 +17,6 @@
// along with Kyoo. If not, see .
using System;
-using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Models.Options
{
@@ -57,19 +56,7 @@ namespace Kyoo.Core.Models.Options
public string TranscodePath { get; set; } = "cached/transcode";
///
- /// true if the metadata of a show/season/episode should be stored in the same directory as video files,
- /// false to save them in a kyoo specific directory.
- ///
- ///
- /// Some file systems might discard this option to store them somewhere else.
- /// For example, readonly file systems will probably store them in a kyoo specific directory.
- ///
- public bool MetadataInShow { get; set; } = true;
-
- ///
- /// The path for metadata if they are not stored near show (see ).
- /// Some resources can't be stored near a show and they are stored in this directory
- /// (like ).
+ /// The path where metadata is stored.
///
public string MetadataPath { get; set; } = "metadata/";
}
diff --git a/src/Kyoo.Core/Tasks/Crawler.cs b/src/Kyoo.Core/Tasks/Crawler.cs
index 1a5d6e9e..791c280f 100644
--- a/src/Kyoo.Core/Tasks/Crawler.cs
+++ b/src/Kyoo.Core/Tasks/Crawler.cs
@@ -25,7 +25,7 @@ using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Attributes;
-using Kyoo.Core.Models.Watch;
+using Kyoo.Core.Models;
using Microsoft.Extensions.Logging;
namespace Kyoo.Core.Tasks
diff --git a/src/Kyoo.Core/Tasks/RegisterEpisode.cs b/src/Kyoo.Core/Tasks/RegisterEpisode.cs
index 0a4d6085..7a9641c8 100644
--- a/src/Kyoo.Core/Tasks/RegisterEpisode.cs
+++ b/src/Kyoo.Core/Tasks/RegisterEpisode.cs
@@ -150,9 +150,7 @@ namespace Kyoo.Core.Tasks
if (!show.IsMovie)
episode = await _metadataProvider.Get(episode);
progress.Report(70);
- episode.Tracks = (await _transcoder.ExtractInfos(episode, false))
- .Where(x => x.Type != StreamType.Attachment)
- .ToArray();
+ episode.Tracks = await _transcoder.ExtractInfos(episode, false);
await _thumbnailsManager.DownloadImages(episode);
progress.Report(90);
diff --git a/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs b/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs
index b1e58c1b..6ed67ac8 100644
--- a/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs
+++ b/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/src/Kyoo.Core/Views/Resources/EpisodeApi.cs
index ba11597a..cd568e91 100644
--- a/src/Kyoo.Core/Views/Resources/EpisodeApi.cs
+++ b/src/Kyoo.Core/Views/Resources/EpisodeApi.cs
@@ -47,20 +47,34 @@ namespace Kyoo.Core.Api
///
private readonly ILibraryManager _libraryManager;
+ ///
+ /// The transcoder used to retrive fonts.
+ ///
+ private readonly ITranscoder _transcoder;
+
+ ///
+ /// The file system used to send fonts.
+ ///
+ private readonly IFileSystem _files;
+
///
/// Create a new .
///
///
/// The library manager used to modify or retrieve information in the data store.
///
+ /// The transcoder used to retrive fonts
/// The file manager used to send images.
/// The thumbnail manager used to retrieve images paths.
public EpisodeApi(ILibraryManager libraryManager,
+ ITranscoder transcoder,
IFileSystem files,
IThumbnailsManager thumbnails)
: base(libraryManager.EpisodeRepository, files, thumbnails)
{
_libraryManager = libraryManager;
+ _transcoder = transcoder;
+ _files = files;
}
///
@@ -158,5 +172,60 @@ namespace Kyoo.Core.Api
return BadRequest(new RequestError(ex.Message));
}
}
+
+ ///
+ /// List fonts
+ ///
+ ///
+ /// List available fonts for this episode.
+ ///
+ /// The ID or slug of the .
+ /// An object containing the name of the font followed by the url to retrieve it.
+ [HttpGet("{identifier:id}/fonts")]
+ [HttpGet("{identifier:id}/font", Order = AlternativeRoute)]
+ [PartialPermission(Kind.Read)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task>> GetFonts(Identifier identifier)
+ {
+ Episode episode = await identifier.Match(
+ id => _libraryManager.GetOrDefault(id),
+ slug => _libraryManager.GetOrDefault(slug)
+ );
+ if (episode == null)
+ return NotFound();
+ return Ok(await _transcoder.ListFonts(episode));
+ }
+
+ ///
+ /// Get font
+ ///
+ ///
+ /// Get a font file that is used in subtitles of this episode.
+ ///
+ /// The ID or slug of the .
+ /// The slug of the font to retrieve.
+ /// A page of collections.
+ /// No show with the given ID/slug could be found or the font does not exist.
+ [HttpGet("{identifier:id}/fonts/{slug}")]
+ [HttpGet("{identifier:id}/font/{slug}", Order = AlternativeRoute)]
+ [PartialPermission(Kind.Read)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task GetFont(Identifier identifier, string slug)
+ {
+ Episode episode = await identifier.Match(
+ id => _libraryManager.GetOrDefault(id),
+ slug => _libraryManager.GetOrDefault(slug)
+ );
+ if (episode == null)
+ return NotFound();
+ if (slug.Contains('.'))
+ slug = slug[..slug.LastIndexOf('.')];
+ Font font = await _transcoder.GetFont(episode, slug);
+ if (font == null)
+ return NotFound();
+ return _files.FileResult(font.Path);
+ }
}
}
diff --git a/src/Kyoo.Core/Views/Resources/ShowApi.cs b/src/Kyoo.Core/Views/Resources/ShowApi.cs
index ccbf00e5..23261724 100644
--- a/src/Kyoo.Core/Views/Resources/ShowApi.cs
+++ b/src/Kyoo.Core/Views/Resources/ShowApi.cs
@@ -18,7 +18,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
@@ -28,10 +27,8 @@ using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils;
-using Kyoo.Core.Models.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Options;
using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api
@@ -53,17 +50,6 @@ namespace Kyoo.Core.Api
///
private readonly ILibraryManager _libraryManager;
- ///
- /// The file manager used to send images and fonts.
- ///
- private readonly IFileSystem _files;
-
- ///
- /// The base URL of Kyoo. This will be used to create links for images and
- /// .
- ///
- private readonly Uri _baseURL;
-
///
/// Create a new .
///
@@ -72,18 +58,12 @@ namespace Kyoo.Core.Api
///
/// The file manager used to send images and fonts.
/// The thumbnail manager used to retrieve images paths.
- ///
- /// Options used to retrieve the base URL of Kyoo.
- ///
public ShowApi(ILibraryManager libraryManager,
IFileSystem files,
- IThumbnailsManager thumbs,
- IOptions options)
+ IThumbnailsManager thumbs)
: base(libraryManager.ShowRepository, files, thumbs)
{
_libraryManager = libraryManager;
- _files = files;
- _baseURL = options.Value.PublicUrl;
}
///
@@ -375,65 +355,5 @@ namespace Kyoo.Core.Api
return BadRequest(new RequestError(ex.Message));
}
}
-
- ///
- /// List fonts
- ///
- ///
- /// List available fonts for this show.
- ///
- /// The ID or slug of the .
- /// An object containing the name of the font followed by the url to retrieve it.
- [HttpGet("{identifier:id}/fonts")]
- [HttpGet("{identifier:id}/font", Order = AlternativeRoute)]
- [PartialPermission(Kind.Read)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task>> GetFonts(Identifier identifier)
- {
- Show show = await identifier.Match(
- id => _libraryManager.GetOrDefault(id),
- slug => _libraryManager.GetOrDefault(slug)
- );
- if (show == null)
- return NotFound();
- string path = _files.Combine(await _files.GetExtraDirectory(show), "Attachments");
- return (await _files.ListFiles(path))
- .ToDictionary(
- Path.GetFileNameWithoutExtension,
- x => $"{_baseURL}api/shows/{identifier}/fonts/{Path.GetFileName(x)}"
- );
- }
-
- ///
- /// Get font
- ///
- ///
- /// Get a font file that is used in subtitles of this show.
- ///
- /// The ID or slug of the .
- /// The name of the font to retrieve (with it's file extension).
- /// A page of collections.
- /// The font name is invalid.
- /// No show with the given ID/slug could be found or the font does not exist.
- [HttpGet("{identifier:id}/fonts/{font}")]
- [HttpGet("{identifier:id}/font/{font}", Order = AlternativeRoute)]
- [PartialPermission(Kind.Read)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task GetFont(Identifier identifier, string font)
- {
- if (font.Contains('/') || font.Contains('\\'))
- return BadRequest(new RequestError("Invalid font name."));
- Show show = await identifier.Match(
- id => _libraryManager.GetOrDefault(id),
- slug => _libraryManager.GetOrDefault(slug)
- );
- if (show == null)
- return NotFound();
- string path = _files.Combine(await _files.GetExtraDirectory(show), "Attachments", font);
- return _files.FileResult(path);
- }
}
}
diff --git a/src/Kyoo.Host.Generic/Contollers/FileSystemComposite.cs b/src/Kyoo.Host.Generic/Contollers/FileSystemComposite.cs
index 0a9eec86..d2445d23 100644
--- a/src/Kyoo.Host.Generic/Contollers/FileSystemComposite.cs
+++ b/src/Kyoo.Host.Generic/Contollers/FileSystemComposite.cs
@@ -44,12 +44,6 @@ namespace Kyoo.Host.Generic.Controllers
///
private readonly ICollection, FileSystemMetadataAttribute>> _fileSystems;
- ///
- /// The library manager used to load shows to retrieve their path
- /// (only if the option is set to metadata in show)
- ///
- private readonly ILibraryManager _libraryManager;
-
///
/// Options to check if the metadata should be kept in the show directory or in a kyoo's directory.
///
@@ -60,14 +54,11 @@ namespace Kyoo.Host.Generic.Controllers
/// metadata.
///
/// The list of filesystem mapped to their metadata.
- /// The library manager used to load shows to retrieve their path.
/// The options to use.
public FileSystemComposite(ICollection, FileSystemMetadataAttribute>> fileSystems,
- ILibraryManager libraryManager,
IOptionsMonitor options)
{
_fileSystems = fileSystems;
- _libraryManager = libraryManager;
_options = options;
}
@@ -178,41 +169,10 @@ namespace Kyoo.Host.Generic.Controllers
}
///
- public async Task GetExtraDirectory(T resource)
+ public Task GetExtraDirectory(T resource)
{
- switch (resource)
- {
- case Season season:
- await _libraryManager.Load(season, x => x.Show);
- break;
- case Episode episode:
- await _libraryManager.Load(episode, x => x.Show);
- break;
- case Track track:
- await _libraryManager.Load(track, x => x.Episode);
- await _libraryManager.Load(track.Episode, x => x.Show);
- break;
- }
-
- IFileSystem fs = resource switch
- {
- Show show => _GetFileSystemForPath(show.Path, out string _),
- Season season => _GetFileSystemForPath(season.Show.Path, out string _),
- Episode episode => _GetFileSystemForPath(episode.Show.Path, out string _),
- Track track => _GetFileSystemForPath(track.Episode.Show.Path, out string _),
- _ => _GetFileSystemForPath(_options.CurrentValue.MetadataPath, out string _)
- };
- string path = await fs.GetExtraDirectory(resource)
- ?? resource switch
- {
- IResource res => Combine(
- _options.CurrentValue.MetadataPath,
- typeof(T).Name.ToLower(),
- res.Slug
- ),
- _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLower())
- };
- return await CreateDirectory(path);
+ IFileSystem fs = _GetFileSystemForPath(_options.CurrentValue.MetadataPath, out string path);
+ return fs.GetExtraDirectory(resource);
}
///
diff --git a/src/Kyoo.Host.Generic/settings.json b/src/Kyoo.Host.Generic/settings.json
index 6c472a6e..bf5e6a4e 100644
--- a/src/Kyoo.Host.Generic/settings.json
+++ b/src/Kyoo.Host.Generic/settings.json
@@ -76,7 +76,7 @@
"tvdb": {
"apiKey": ""
},
- "the-moviedb": {
+ "themoviedb": {
"apiKey": ""
}
}
diff --git a/src/Kyoo.SqLite/SqLiteContext.cs b/src/Kyoo.SqLite/SqLiteContext.cs
index a19151bf..3a0cf6b9 100644
--- a/src/Kyoo.SqLite/SqLiteContext.cs
+++ b/src/Kyoo.SqLite/SqLiteContext.cs
@@ -184,8 +184,9 @@ namespace Kyoo.SqLite
///
protected override bool IsDuplicateException(Exception ex)
{
- return ex.InnerException is SqliteException { SqliteExtendedErrorCode: 2067 /* SQLITE_CONSTRAINT_UNIQUE */ }
- or SqliteException { SqliteExtendedErrorCode: 1555 /* SQLITE_CONSTRAINT_PRIMARYKEY */ };
+ return ex.InnerException
+ is SqliteException { SqliteExtendedErrorCode: 2067 /* SQLITE_CONSTRAINT_UNIQUE */ }
+ or SqliteException { SqliteExtendedErrorCode: 1555 /* SQLITE_CONSTRAINT_PRIMARYKEY */ };
}
///
diff --git a/src/Kyoo.Swagger/SwaggerModule.cs b/src/Kyoo.Swagger/SwaggerModule.cs
index d5b51e71..fafb1281 100644
--- a/src/Kyoo.Swagger/SwaggerModule.cs
+++ b/src/Kyoo.Swagger/SwaggerModule.cs
@@ -1,4 +1,4 @@
-// Kyoo - A portable and vast media library solution.
+// Kyoo - A portable and vast media library solution.
// Copyright (c) Kyoo.
//
// See AUTHORS.md and LICENSE file in the project root for full license information.
diff --git a/src/Kyoo.TheMovieDb/PluginTmdb.cs b/src/Kyoo.TheMovieDb/PluginTmdb.cs
index 72d16f19..e20149b5 100644
--- a/src/Kyoo.TheMovieDb/PluginTmdb.cs
+++ b/src/Kyoo.TheMovieDb/PluginTmdb.cs
@@ -48,7 +48,7 @@ namespace Kyoo.TheMovieDb
if (!Enabled)
{
logger.LogWarning("No API key configured for TheMovieDB provider. " +
- "To enable TheMovieDB, specify one in the setting the-moviedb:APIKEY ");
+ "To enable TheMovieDB, specify one in the setting themoviedb:APIKEY");
}
}
@@ -62,7 +62,7 @@ namespace Kyoo.TheMovieDb
public string Description => "A metadata provider for TheMovieDB.";
///
- public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue("the-moviedb:apikey"));
+ public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue("themoviedb:apikey"));
///
public Dictionary Configuration => new()
diff --git a/src/Kyoo.TheMovieDb/TheMovieDbOptions.cs b/src/Kyoo.TheMovieDb/TheMovieDbOptions.cs
index 7167c902..a4ddcc2c 100644
--- a/src/Kyoo.TheMovieDb/TheMovieDbOptions.cs
+++ b/src/Kyoo.TheMovieDb/TheMovieDbOptions.cs
@@ -26,7 +26,7 @@ namespace Kyoo.TheMovieDb.Models
///
/// The path to get this option from the root configuration.
///
- public const string Path = "the-moviedb";
+ public const string Path = "themoviedb";
///
/// The api key of TheMovieDb.
diff --git a/src/Kyoo.Transcoder b/src/Kyoo.Transcoder
index 7bae8def..913c8e98 160000
--- a/src/Kyoo.Transcoder
+++ b/src/Kyoo.Transcoder
@@ -1 +1 @@
-Subproject commit 7bae8def39ace7bab481efea4825c4802e9e1f31
+Subproject commit 913c8e986e220ea48749b815593cdd10b2acb8de
diff --git a/tests/Kyoo.Tests/Database/TestSample.cs b/tests/Kyoo.Tests/Database/TestSample.cs
index a7327753..634c104a 100644
--- a/tests/Kyoo.Tests/Database/TestSample.cs
+++ b/tests/Kyoo.Tests/Database/TestSample.cs
@@ -180,8 +180,8 @@ namespace Kyoo.Tests
"We Still Don't Know the Name of the Flower We Saw That Day."
},
Overview = "When Yadomi Jinta was a child, he was a central piece in a group of close friends. " +
- "In time, however, these childhood friends drifted apart, and when they became high " +
- "school students, they had long ceased to think of each other as friends.",
+ "In time, however, these childhood friends drifted apart, and when they became high " +
+ "school students, they had long ceased to think of each other as friends.",
Status = Status.Finished,
StudioID = 1,
StartAir = new DateTime(2011, 1, 1),
@@ -243,7 +243,7 @@ namespace Kyoo.Tests
typeof(Track),
() =>
{
- Track ret = new()
+ Track ret = new()
{
ID = 1,
EpisodeID = 1,
diff --git a/tests/Kyoo.Tests/Transcoder/TranscoderTests.cs b/tests/Kyoo.Tests/Transcoder/TranscoderTests.cs
new file mode 100644
index 00000000..3d0a38d3
--- /dev/null
+++ b/tests/Kyoo.Tests/Transcoder/TranscoderTests.cs
@@ -0,0 +1,107 @@
+// Kyoo - A portable and vast media library solution.
+// Copyright (c) Kyoo.
+//
+// See AUTHORS.md and LICENSE file in the project root for full license information.
+//
+// Kyoo is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// Kyoo is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Kyoo. If not, see .
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Kyoo.Abstractions.Controllers;
+using Kyoo.Abstractions.Models;
+using Kyoo.Core.Models.Options;
+using Microsoft.Extensions.Options;
+using Moq;
+using Xunit;
+using Xunit.Abstractions;
+
+using KTranscoder = Kyoo.Core.Controllers.Transcoder;
+
+namespace Kyoo.Tests.Transcoder
+{
+ public class TranscoderTests
+ {
+ private readonly Mock _files;
+ private readonly ITranscoder _transcoder;
+
+ public TranscoderTests(ITestOutputHelper output)
+ {
+ _files = new Mock();
+ _transcoder = new KTranscoder(
+ _files.Object,
+ Options.Create(new BasicOptions()),
+ output.BuildLoggerFor()
+ );
+ }
+
+ [Fact]
+ public async Task ListFontsTest()
+ {
+ Episode episode = TestSample.Get();
+ _files.Setup(x => x.ListFiles(It.IsAny(), System.IO.SearchOption.TopDirectoryOnly))
+ .ReturnsAsync(new[] { "font.ttf", "font.TTF", "toto.ttf" });
+ ICollection fonts = await _transcoder.ListFonts(episode);
+ List fontsFiles = fonts.Select(x => x.File).ToList();
+ Assert.Equal(3, fonts.Count);
+ Assert.Contains("font.TTF", fontsFiles);
+ Assert.Contains("font.ttf", fontsFiles);
+ Assert.Contains("toto.ttf", fontsFiles);
+ }
+
+ [Fact]
+ public async Task GetNoFontTest()
+ {
+ Episode episode = TestSample.Get();
+ _files.Setup(x => x.GetExtraDirectory(It.IsAny()))
+ .ReturnsAsync("/path");
+ _files.Setup(x => x.ListFiles(It.IsAny(), System.IO.SearchOption.TopDirectoryOnly))
+ .ReturnsAsync(new[] { "font.ttf", "font.TTF", "toto.ttf" });
+ Font font = await _transcoder.GetFont(episode, "toto.ttf");
+ Assert.Null(font);
+ }
+
+ [Fact]
+ public async Task GetFontTest()
+ {
+ Episode episode = TestSample.Get();
+ _files.Setup(x => x.GetExtraDirectory(It.IsAny()))
+ .ReturnsAsync("/path");
+ _files.Setup(x => x.ListFiles(It.IsAny(), System.IO.SearchOption.TopDirectoryOnly))
+ .ReturnsAsync(new[] { "/path/font.ttf", "/path/font.TTF", "/path/toto.ttf" });
+ Font font = await _transcoder.GetFont(episode, "toto");
+ Assert.NotNull(font);
+ Assert.Equal("toto.ttf", font.File);
+ Assert.Equal("toto", font.Slug);
+ Assert.Equal("ttf", font.Format);
+ Assert.Equal("/path/toto.ttf", font.Path);
+ }
+
+ [Fact]
+ public async Task GetFontNoExtensionTest()
+ {
+ Episode episode = TestSample.Get();
+ _files.Setup(x => x.GetExtraDirectory(It.IsAny()))
+ .ReturnsAsync("/path");
+ _files.Setup(x => x.ListFiles(It.IsAny(), System.IO.SearchOption.TopDirectoryOnly))
+ .ReturnsAsync(new[] { "/path/font", "/path/toto.ttf" });
+ Font font = await _transcoder.GetFont(episode, "font");
+ Assert.NotNull(font);
+ Assert.Equal("font", font.File);
+ Assert.Equal("font", font.Slug);
+ Assert.Equal("", font.Format);
+ Assert.Equal("/path/font", font.Path);
+ }
+ }
+}