mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-23 15:30:34 -04:00
Merge pull request #104 from AnonymusRaccoon/fix/fonts
This commit is contained in:
commit
a84a9fed85
@ -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
|
||||
|
2
.env.example
Normal file
2
.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
TVDB__APIKEY=
|
||||
THEMOVIEDB__APIKEY=
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -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.
|
||||
##
|
||||
|
@ -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
|
||||
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"title": "## Features",
|
||||
"labels": ["enhancement"]
|
||||
},
|
||||
{
|
||||
"title": "## Fixes",
|
||||
"labels": ["bug"]
|
||||
},
|
||||
{
|
||||
"title": "## Web App",
|
||||
"labels": ["webapp"]
|
||||
}
|
||||
],
|
||||
"template": "${{CHANGELOG}}\n\n<details>\n<summary>Others</summary>\n\n${{UNCATEGORIZED}}\n</details>",
|
||||
"pr_template": "- ${{TITLE}} (PR: #${{NUMBER}})"
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
[Unit]
|
||||
Description=Kyoo Media Server
|
||||
Requires=postgresql.service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
|
35
docker-compose.yml
Normal file
35
docker-compose.yml
Normal file
@ -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:
|
||||
|
@ -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
|
||||
|
5
front/projects/host/src/app/models/font.ts
Normal file
5
front/projects/host/src/app/models/font.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Font {
|
||||
slug: string;
|
||||
file: string;
|
||||
format: string;
|
||||
}
|
@ -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")
|
||||
{
|
||||
|
@ -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<Episode>
|
||||
return this.client.get(`/api/seasons/${show}-s${seasonNumber}/episodes${this.ArgsAsQuery(args)}`)
|
||||
.pipe(map(x => Object.assign(new Page<Episode>(), x)));
|
||||
}
|
||||
|
||||
getFonts(id: string | number): Observable<Font[]>
|
||||
{
|
||||
return this.client.get<Font[]>(`/api/episodes/${id}/fonts`);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
@ -177,11 +183,6 @@ export class ShowService extends CrudApi<Show>
|
||||
return this.client.get<Page<Show>>(`/api/collections/${collection}/shows${this.ArgsAsQuery(args)}`)
|
||||
.pipe(map(x => Object.assign(new Page<Show>(), x)));
|
||||
}
|
||||
|
||||
getFonts(id: string | number): Observable<{[font: string]: string}>
|
||||
{
|
||||
return this.client.get<any>(`/api/shows/${id}/fonts`);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
|
@ -28,10 +28,6 @@
|
||||
<IsOSX Condition="$([MSBuild]::IsOSPlatform('OSX'))">true</IsOSX>
|
||||
<IsLinux Condition="$([MSBuild]::IsOSPlatform('Linux'))">true</IsLinux>
|
||||
|
||||
|
||||
<!-- TODO the next thing does not work on rider, enabling coding style check by default. -->
|
||||
<!--<CheckCodingStyle Condition="$(BuildingInsideVisualStudio) == true">true</CheckCodingStyle>-->
|
||||
<!--<CheckCodingStyle Condition="$(BuildingInsideReSharper) == true">true</CheckCodingStyle>-->
|
||||
<CheckCodingStyle Condition="$(CheckCodingStyle) == ''">true</CheckCodingStyle>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -48,8 +44,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="$(CheckCodingStyle) == true">
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../Kyoo.ruleset</CodeAnalysisRuleSet>
|
||||
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->
|
||||
<!-- <AnalysisMode>All</AnalysisMode> -->
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
63
src/Kyoo.Abstractions/Controllers/ITranscoder.cs
Normal file
63
src/Kyoo.Abstractions/Controllers/ITranscoder.cs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Transcoder responsible of handling low level video details.
|
||||
/// </summary>
|
||||
public interface ITranscoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve tracks for a specific episode.
|
||||
/// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to retrieve tracks for.</param>
|
||||
/// <param name="reExtract">Should the cache be invalidated and subtitles and others be re-extracted?</param>
|
||||
/// <returns>The list of tracks available for this episode.</returns>
|
||||
Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract);
|
||||
|
||||
/// <summary>
|
||||
/// List fonts assosiated with this episode.
|
||||
/// </summary>
|
||||
/// <param name="episode">Th episode to list fonts for.</param>
|
||||
/// <returns>The list of attachements for this epiosode.</returns>
|
||||
Task<ICollection<Font>> ListFonts(Episode episode);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified font for this episode.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to list fonts for.</param>
|
||||
/// <param name="slug">The slug of the specific font to retrive.</param>
|
||||
/// <returns>The <see cref="Font"/> with the given slug or null.</returns>
|
||||
[ItemCanBeNull] Task<Font> GetFont(Episode episode, string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Transmux the selected episode to hls.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to transmux.</param>
|
||||
/// <returns>The master file (m3u8) of the transmuxed hls file.</returns>
|
||||
IActionResult Transmux(Episode episode);
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
47
src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs
Normal file
47
src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception thrown when a part of the app has a fatal issue.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class HealthException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HealthException"/> with a custom message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to use.</param>
|
||||
public HealthException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor
|
||||
/// </summary>
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected HealthException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
67
src/Kyoo.Abstractions/Models/Font.cs
Normal file
67
src/Kyoo.Abstractions/Models/Font.cs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using PathIO = System.IO.Path;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A font of an <see cref="Episode"/>.
|
||||
/// </summary>
|
||||
public class Font
|
||||
{
|
||||
/// <summary>
|
||||
/// A human-readable identifier, used in the URL.
|
||||
/// </summary>
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the font file (with the extension).
|
||||
/// </summary>
|
||||
public string File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The format of this font (the extension).
|
||||
/// </summary>
|
||||
public string Format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the font.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new empty <see cref="Font"/>.
|
||||
/// </summary>
|
||||
public Font() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Font"/> from a path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the font.</param>
|
||||
public Font(string path)
|
||||
{
|
||||
Slug = Utility.ToSlug(PathIO.GetFileNameWithoutExtension(path));
|
||||
Path = path;
|
||||
File = PathIO.GetFileName(path);
|
||||
Format = PathIO.GetExtension(path).Replace(".", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,8 @@ namespace Kyoo.Abstractions.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[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));
|
||||
|
@ -34,7 +34,8 @@ namespace Kyoo.Abstractions.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[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, @"(?<show>.+)-s(?<season>\d+)");
|
||||
|
||||
|
@ -50,12 +50,6 @@ namespace Kyoo.Abstractions.Models
|
||||
/// The stream is a subtitle.
|
||||
/// </summary>
|
||||
Subtitle = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
Attachment = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -67,7 +61,8 @@ namespace Kyoo.Abstractions.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[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
|
||||
/// <summary>
|
||||
/// The episode that uses this track.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(EpisodeID))] public Episode Episode
|
||||
[LoadableRelation(nameof(EpisodeID))]
|
||||
public Episode Episode
|
||||
{
|
||||
get => _episode;
|
||||
set
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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(),
|
||||
|
@ -170,7 +170,7 @@ namespace Kyoo.Utils
|
||||
Type type = typeof(T);
|
||||
IEnumerable<PropertyInfo> 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<PropertyInfo> 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<PropertyInfo> 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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 =>
|
||||
// {
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -153,16 +153,12 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetExtraDirectory<T>(T resource)
|
||||
{
|
||||
if (!_options.CurrentValue.MetadataInShow)
|
||||
return Task.FromResult<string>(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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -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;
|
||||
|
||||
|
@ -73,16 +73,16 @@ namespace Kyoo.Core.Controllers
|
||||
public Task<Episode> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -107,14 +107,14 @@ namespace Kyoo.Core.Controllers
|
||||
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -82,14 +82,14 @@ namespace Kyoo.Core.Controllers
|
||||
public Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Season> GetOrDefault(string showSlug, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the C library, setup the logger and return the size of a <see cref="Models.Watch.Stream"/>.
|
||||
/// Initialize the C library, setup the logger and return the size of a <see cref="FTrack"/>.
|
||||
/// </summary>
|
||||
/// <returns>The size of a <see cref="Models.Watch.Stream"/></returns>
|
||||
/// <returns>The size of a <see cref="FTrack"/></returns>
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int init();
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the C library, setup the logger and return the size of a <see cref="Models.Watch.Stream"/>.
|
||||
/// Initialize the C library, setup the logger and return the size of a <see cref="FTrack"/>.
|
||||
/// </summary>
|
||||
/// <returns>The size of a <see cref="Models.Watch.Stream"/></returns>
|
||||
/// <returns>The size of a <see cref="FTrack"/></returns>
|
||||
public static int Init() => init();
|
||||
|
||||
/// <summary>
|
||||
@ -99,7 +103,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <param name="length">The size of the returned array.</param>
|
||||
/// <param name="trackCount">The number of tracks in the returned array.</param>
|
||||
/// <param name="reExtract">Should the cache be invalidated and information re-extracted or not?</param>
|
||||
/// <returns>A pointer to an array of <see cref="Models.Watch.Stream"/></returns>
|
||||
/// <returns>A pointer to an array of <see cref="FTrack"/></returns>
|
||||
[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);
|
||||
|
||||
/// <summary>
|
||||
/// An helper method to free an array of <see cref="Models.Watch.Stream"/>.
|
||||
/// An helper method to free an array of <see cref="FTrack"/>.
|
||||
/// </summary>
|
||||
/// <param name="streams">A pointer to the first element of the array</param>
|
||||
/// <param name="count">The number of items in the array.</param>
|
||||
@ -128,7 +132,7 @@ namespace Kyoo.Core.Controllers
|
||||
path = path.Replace('\\', '/');
|
||||
outPath = outPath.Replace('\\', '/');
|
||||
|
||||
int size = Marshal.SizeOf<Models.Watch.Stream>();
|
||||
int size = Marshal.SizeOf<FTrack>();
|
||||
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<Models.Watch.Stream>(streamsPtr);
|
||||
if (stream!.Type != StreamType.Unknown)
|
||||
FTrack stream = Marshal.PtrToStructure<FTrack>(streamsPtr);
|
||||
if (stream!.Type != FTrackType.Unknown && stream.Type != FTrackType.Attachment)
|
||||
{
|
||||
tracks[j] = stream.ToTrack();
|
||||
j++;
|
||||
@ -174,6 +178,11 @@ namespace Kyoo.Core.Controllers
|
||||
/// </summary>
|
||||
private readonly ILogger<Transcoder> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the C library has been checked, <see langword="false"/> otherwise.
|
||||
/// </summary>
|
||||
private bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Transcoder"/>.
|
||||
/// </summary>
|
||||
@ -187,14 +196,29 @@ namespace Kyoo.Core.Controllers
|
||||
_files = files;
|
||||
_options = options;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
if (TranscoderAPI.Init() != Marshal.SizeOf<Models.Watch.Stream>())
|
||||
_logger.LogCritical("The transcoder library could not be initialized correctly");
|
||||
/// <summary>
|
||||
/// Check if the C library can be used or if there is an issue with it.
|
||||
/// </summary>
|
||||
/// <param name="fastStop">Should the healthcheck be abborted if the transcoder was already initialized?</param>
|
||||
/// <exception cref="HealthException">If the transcoder is corrupted, this exception in thrown.</exception>
|
||||
public void CheckHealth(bool fastStop = false)
|
||||
{
|
||||
if (fastStop && _initialized)
|
||||
return;
|
||||
if (TranscoderAPI.Init() == Marshal.SizeOf<FTrack>())
|
||||
return;
|
||||
_initialized = true;
|
||||
_logger.LogCritical("The transcoder library could not be initialized correctly");
|
||||
throw new HealthException("The transcoder library is corrupted or invalid.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<Track>> 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
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<ICollection<Font>> ListFonts(Episode episode)
|
||||
{
|
||||
string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments");
|
||||
return (await _files.ListFiles(path))
|
||||
.Select(x => new Font(x))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Font> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -293,27 +339,4 @@ namespace Kyoo.Core.Controllers
|
||||
#pragma warning restore 4014
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The transcoder used by the <see cref="LocalFileSystem"/>. This is on a different interface than the file system
|
||||
/// to offset the work.
|
||||
/// </summary>
|
||||
public interface ITranscoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve tracks for a specific episode.
|
||||
/// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to retrieve tracks for.</param>
|
||||
/// <param name="reExtract">Should the cache be invalidated and subtitles and others be re-extracted?</param>
|
||||
/// <returns>The list of tracks available for this episode.</returns>
|
||||
Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract);
|
||||
|
||||
/// <summary>
|
||||
/// Transmux the selected episode to hls.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to transmux.</param>
|
||||
/// <returns>The master file (m3u8) of the transmuxed hls file.</returns>
|
||||
IActionResult Transmux(Episode episode);
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,45 @@ using Kyoo.Abstractions.Models;
|
||||
|
||||
namespace Kyoo.Core.Models.Watch
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of available stream types.
|
||||
/// Attachments are only used temporarily by the transcoder but are not stored in a database.
|
||||
/// This is another <see cref="StreamType"/> enum used internally to communicate with ffmpeg.
|
||||
/// </summary>
|
||||
public enum FTrackType
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the stream is not known.
|
||||
/// </summary>
|
||||
Unknown = StreamType.Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is a video.
|
||||
/// </summary>
|
||||
Video = StreamType.Video,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is an audio.
|
||||
/// </summary>
|
||||
Audio = StreamType.Audio,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is a subtitle.
|
||||
/// </summary>
|
||||
Subtitle = StreamType.Subtitle,
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
Attachment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unmanaged stream that the transcoder will return.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public struct Stream
|
||||
public struct FTrack
|
||||
{
|
||||
/// <summary>
|
||||
/// The title of the stream.
|
||||
@ -60,7 +94,7 @@ namespace Kyoo.Core.Models.Watch
|
||||
/// <summary>
|
||||
/// The type of this stream.
|
||||
/// </summary>
|
||||
public StreamType Type;
|
||||
public FTrackType Type;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
};
|
||||
}
|
@ -20,7 +20,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
|
||||
namespace Kyoo.Core.Models.Watch
|
||||
namespace Kyoo.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class allowing one to identify files extensions.
|
||||
|
@ -17,7 +17,6 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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";
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the metadata of a show/season/episode should be stored in the same directory as video files,
|
||||
/// <c>false</c> to save them in a kyoo specific directory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool MetadataInShow { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The path for metadata if they are not stored near show (see <see cref="MetadataInShow"/>).
|
||||
/// Some resources can't be stored near a show and they are stored in this directory
|
||||
/// (like <see cref="Provider"/>).
|
||||
/// The path where metadata is stored.
|
||||
/// </summary>
|
||||
public string MetadataPath { get; set; } = "metadata/";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -47,20 +47,34 @@ namespace Kyoo.Core.Api
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// The transcoder used to retrive fonts.
|
||||
/// </summary>
|
||||
private readonly ITranscoder _transcoder;
|
||||
|
||||
/// <summary>
|
||||
/// The file system used to send fonts.
|
||||
/// </summary>
|
||||
private readonly IFileSystem _files;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="EpisodeApi"/>.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">
|
||||
/// The library manager used to modify or retrieve information in the data store.
|
||||
/// </param>
|
||||
/// <param name="transcoder">The transcoder used to retrive fonts</param>
|
||||
/// <param name="files">The file manager used to send images.</param>
|
||||
/// <param name="thumbnails">The thumbnail manager used to retrieve images paths.</param>
|
||||
public EpisodeApi(ILibraryManager libraryManager,
|
||||
ITranscoder transcoder,
|
||||
IFileSystem files,
|
||||
IThumbnailsManager thumbnails)
|
||||
: base(libraryManager.EpisodeRepository, files, thumbnails)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_transcoder = transcoder;
|
||||
_files = files;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -158,5 +172,60 @@ namespace Kyoo.Core.Api
|
||||
return BadRequest(new RequestError(ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List fonts
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// List available fonts for this episode.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Episode"/>.</param>
|
||||
/// <returns>An object containing the name of the font followed by the url to retrieve it.</returns>
|
||||
[HttpGet("{identifier:id}/fonts")]
|
||||
[HttpGet("{identifier:id}/font", Order = AlternativeRoute)]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<ICollection<Font>>> GetFonts(Identifier identifier)
|
||||
{
|
||||
Episode episode = await identifier.Match(
|
||||
id => _libraryManager.GetOrDefault<Episode>(id),
|
||||
slug => _libraryManager.GetOrDefault<Episode>(slug)
|
||||
);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
return Ok(await _transcoder.ListFonts(episode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get font
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get a font file that is used in subtitles of this episode.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Episode"/>.</param>
|
||||
/// <param name="slug">The slug of the font to retrieve.</param>
|
||||
/// <returns>A page of collections.</returns>
|
||||
/// <response code="404">No show with the given ID/slug could be found or the font does not exist.</response>
|
||||
[HttpGet("{identifier:id}/fonts/{slug}")]
|
||||
[HttpGet("{identifier:id}/font/{slug}", Order = AlternativeRoute)]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetFont(Identifier identifier, string slug)
|
||||
{
|
||||
Episode episode = await identifier.Match(
|
||||
id => _libraryManager.GetOrDefault<Episode>(id),
|
||||
slug => _libraryManager.GetOrDefault<Episode>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// The file manager used to send images and fonts.
|
||||
/// </summary>
|
||||
private readonly IFileSystem _files;
|
||||
|
||||
/// <summary>
|
||||
/// The base URL of Kyoo. This will be used to create links for images and
|
||||
/// <see cref="Abstractions.Models.Page{T}"/>.
|
||||
/// </summary>
|
||||
private readonly Uri _baseURL;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ShowApi"/>.
|
||||
/// </summary>
|
||||
@ -72,18 +58,12 @@ namespace Kyoo.Core.Api
|
||||
/// </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="options">
|
||||
/// Options used to retrieve the base URL of Kyoo.
|
||||
/// </param>
|
||||
public ShowApi(ILibraryManager libraryManager,
|
||||
IFileSystem files,
|
||||
IThumbnailsManager thumbs,
|
||||
IOptions<BasicOptions> options)
|
||||
IThumbnailsManager thumbs)
|
||||
: base(libraryManager.ShowRepository, files, thumbs)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_files = files;
|
||||
_baseURL = options.Value.PublicUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -375,65 +355,5 @@ namespace Kyoo.Core.Api
|
||||
return BadRequest(new RequestError(ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List fonts
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// List available fonts for this show.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <returns>An object containing the name of the font followed by the url to retrieve it.</returns>
|
||||
[HttpGet("{identifier:id}/fonts")]
|
||||
[HttpGet("{identifier:id}/font", Order = AlternativeRoute)]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Dictionary<string, string>>> GetFonts(Identifier identifier)
|
||||
{
|
||||
Show show = await identifier.Match(
|
||||
id => _libraryManager.GetOrDefault<Show>(id),
|
||||
slug => _libraryManager.GetOrDefault<Show>(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)}"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get font
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get a font file that is used in subtitles of this show.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <param name="font">The name of the font to retrieve (with it's file extension).</param>
|
||||
/// <returns>A page of collections.</returns>
|
||||
/// <response code="400">The font name is invalid.</response>
|
||||
/// <response code="404">No show with the given ID/slug could be found or the font does not exist.</response>
|
||||
[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<IActionResult> 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<Show>(id),
|
||||
slug => _libraryManager.GetOrDefault<Show>(slug)
|
||||
);
|
||||
if (show == null)
|
||||
return NotFound();
|
||||
string path = _files.Combine(await _files.GetExtraDirectory(show), "Attachments", font);
|
||||
return _files.FileResult(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,12 +44,6 @@ namespace Kyoo.Host.Generic.Controllers
|
||||
/// </summary>
|
||||
private readonly ICollection<Meta<Func<IFileSystem>, FileSystemMetadataAttribute>> _fileSystems;
|
||||
|
||||
/// <summary>
|
||||
/// The library manager used to load shows to retrieve their path
|
||||
/// (only if the option is set to metadata in show)
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Options to check if the metadata should be kept in the show directory or in a kyoo's directory.
|
||||
/// </summary>
|
||||
@ -60,14 +54,11 @@ namespace Kyoo.Host.Generic.Controllers
|
||||
/// metadata.
|
||||
/// </summary>
|
||||
/// <param name="fileSystems">The list of filesystem mapped to their metadata.</param>
|
||||
/// <param name="libraryManager">The library manager used to load shows to retrieve their path.</param>
|
||||
/// <param name="options">The options to use.</param>
|
||||
public FileSystemComposite(ICollection<Meta<Func<IFileSystem>, FileSystemMetadataAttribute>> fileSystems,
|
||||
ILibraryManager libraryManager,
|
||||
IOptionsMonitor<BasicOptions> options)
|
||||
{
|
||||
_fileSystems = fileSystems;
|
||||
_libraryManager = libraryManager;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
@ -178,41 +169,10 @@ namespace Kyoo.Host.Generic.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetExtraDirectory<T>(T resource)
|
||||
public Task<string> GetExtraDirectory<T>(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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -76,7 +76,7 @@
|
||||
"tvdb": {
|
||||
"apiKey": ""
|
||||
},
|
||||
"the-moviedb": {
|
||||
"themoviedb": {
|
||||
"apiKey": ""
|
||||
}
|
||||
}
|
||||
|
@ -184,8 +184,9 @@ namespace Kyoo.SqLite
|
||||
/// <inheritdoc />
|
||||
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 */ };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -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.
|
||||
|
@ -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.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue<string>("the-moviedb:apikey"));
|
||||
public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue<string>("themoviedb:apikey"));
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, Type> Configuration => new()
|
||||
|
@ -26,7 +26,7 @@ namespace Kyoo.TheMovieDb.Models
|
||||
/// <summary>
|
||||
/// The path to get this option from the root configuration.
|
||||
/// </summary>
|
||||
public const string Path = "the-moviedb";
|
||||
public const string Path = "themoviedb";
|
||||
|
||||
/// <summary>
|
||||
/// The api key of TheMovieDb.
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7bae8def39ace7bab481efea4825c4802e9e1f31
|
||||
Subproject commit 913c8e986e220ea48749b815593cdd10b2acb8de
|
@ -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,
|
||||
|
107
tests/Kyoo.Tests/Transcoder/TranscoderTests.cs
Normal file
107
tests/Kyoo.Tests/Transcoder/TranscoderTests.cs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<IFileSystem> _files;
|
||||
private readonly ITranscoder _transcoder;
|
||||
|
||||
public TranscoderTests(ITestOutputHelper output)
|
||||
{
|
||||
_files = new Mock<IFileSystem>();
|
||||
_transcoder = new KTranscoder(
|
||||
_files.Object,
|
||||
Options.Create(new BasicOptions()),
|
||||
output.BuildLoggerFor<KTranscoder>()
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListFontsTest()
|
||||
{
|
||||
Episode episode = TestSample.Get<Episode>();
|
||||
_files.Setup(x => x.ListFiles(It.IsAny<string>(), System.IO.SearchOption.TopDirectoryOnly))
|
||||
.ReturnsAsync(new[] { "font.ttf", "font.TTF", "toto.ttf" });
|
||||
ICollection<Font> fonts = await _transcoder.ListFonts(episode);
|
||||
List<string> 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<Episode>();
|
||||
_files.Setup(x => x.GetExtraDirectory(It.IsAny<Episode>()))
|
||||
.ReturnsAsync("/path");
|
||||
_files.Setup(x => x.ListFiles(It.IsAny<string>(), 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<Episode>();
|
||||
_files.Setup(x => x.GetExtraDirectory(It.IsAny<Episode>()))
|
||||
.ReturnsAsync("/path");
|
||||
_files.Setup(x => x.ListFiles(It.IsAny<string>(), 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<Episode>();
|
||||
_files.Setup(x => x.GetExtraDirectory(It.IsAny<Episode>()))
|
||||
.ReturnsAsync("/path");
|
||||
_files.Setup(x => x.ListFiles(It.IsAny<string>(), 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user