diff --git a/back/src/Kyoo.Abstractions/Models/Font.cs b/back/src/Kyoo.Abstractions/Models/Font.cs
index eafb3bdd..ca9c4680 100644
--- a/back/src/Kyoo.Abstractions/Models/Font.cs
+++ b/back/src/Kyoo.Abstractions/Models/Font.cs
@@ -25,7 +25,7 @@ namespace Kyoo.Abstractions.Models
///
/// A font of an .
///
- public class Font
+ public class Font : ILink
{
///
/// A human-readable identifier, used in the URL.
@@ -47,6 +47,9 @@ namespace Kyoo.Abstractions.Models
///
[SerializeIgnore] public string Path { get; set; }
+ ///
+ public object Link { get; init; }
+
///
/// Create a new empty .
///
@@ -56,12 +59,14 @@ namespace Kyoo.Abstractions.Models
/// Create a new from a path.
///
/// The path of the font.
- public Font(string path)
+ /// The slug of the episode that contains this font.
+ public Font(string path, string episodeSlug)
{
Slug = Utility.ToSlug(PathIO.GetFileNameWithoutExtension(path));
Path = path;
File = PathIO.GetFileName(path);
Format = PathIO.GetExtension(path).Replace(".", string.Empty);
+ Link = $"/watch/{episodeSlug}/font/{Slug}.{Format}";
}
}
}
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/ILink.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/ILink.cs
new file mode 100644
index 00000000..5c146786
--- /dev/null
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/ILink.cs
@@ -0,0 +1,31 @@
+// 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 .
+
+namespace Kyoo.Abstractions.Models
+{
+ ///
+ /// An interface to represent resources that should have a link field in their return values (like videos).
+ ///
+ public interface ILink
+ {
+ ///
+ /// The link to return, in most cases this should be a string.
+ ///
+ public object Link { get; }
+ }
+}
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Track.cs b/back/src/Kyoo.Abstractions/Models/Resources/Track.cs
index 59b3746f..d243c0eb 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Track.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Track.cs
@@ -55,7 +55,7 @@ namespace Kyoo.Abstractions.Models
///
/// A video, audio or subtitle track for an episode.
///
- public class Track : IResource
+ public class Track : IResource, ILink
{
///
public int ID { get; set; }
@@ -66,9 +66,9 @@ namespace Kyoo.Abstractions.Models
{
get
{
- string type = Type.ToString().ToLower();
+ string type = Type.ToString().ToLowerInvariant();
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
- string episode = _episodeSlug ?? Episode?.Slug ?? EpisodeID.ToString();
+ string episode = _episodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(CultureInfo.InvariantCulture);
return $"{episode}.{Language ?? "und"}{index}{(IsForced ? ".forced" : string.Empty)}.{type}";
}
@@ -92,7 +92,7 @@ namespace Kyoo.Abstractions.Models
Language = match.Groups["lang"].Value;
if (Language == "und")
Language = null;
- TrackIndex = match.Groups["index"].Success ? int.Parse(match.Groups["index"].Value) : 0;
+ TrackIndex = match.Groups["index"].Success ? int.Parse(match.Groups["index"].Value, CultureInfo.InvariantCulture) : 0;
IsForced = match.Groups["forced"].Success;
Type = Enum.Parse(match.Groups["type"].Value, true);
}
@@ -198,6 +198,9 @@ namespace Kyoo.Abstractions.Models
///
[SerializeIgnore] private Episode _episode;
+ ///
+ public object Link => Type == StreamType.Subtitle ? $"/subtitle/{Slug}" : null;
+
// Converting mkv track language to c# system language tag.
private static string _GetLanguage(string mkvLanguage)
{
diff --git a/back/src/Kyoo.Abstractions/Models/WatchItem.cs b/back/src/Kyoo.Abstractions/Models/WatchItem.cs
index 70e61836..e737aa74 100644
--- a/back/src/Kyoo.Abstractions/Models/WatchItem.cs
+++ b/back/src/Kyoo.Abstractions/Models/WatchItem.cs
@@ -33,7 +33,7 @@ namespace Kyoo.Abstractions.Models
/// Information about tracks and display information that could be used by the player.
/// This contains mostly data from an with another form.
///
- public class WatchItem : CustomTypeDescriptor, IThumbnails
+ public class WatchItem : CustomTypeDescriptor, IThumbnails, ILink
{
///
/// The ID of the episode associated with this item.
@@ -136,6 +136,13 @@ namespace Kyoo.Abstractions.Models
///
public ICollection Chapters { get; set; }
+ ///
+ public object Link => new
+ {
+ Direct = $"/video/direct/{Slug}",
+ Transmux = $"/video/transmux/{Slug}/master.m3u8",
+ };
+
///
/// Create a from an .
///
diff --git a/back/src/Kyoo.Core/Controllers/Transcoder.cs b/back/src/Kyoo.Core/Controllers/Transcoder.cs
index b684271d..04bb3414 100644
--- a/back/src/Kyoo.Core/Controllers/Transcoder.cs
+++ b/back/src/Kyoo.Core/Controllers/Transcoder.cs
@@ -233,7 +233,7 @@ namespace Kyoo.Core.Controllers
{
string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments");
return (await _files.ListFiles(path))
- .Select(x => new Font(x))
+ .Select(x => new Font(x, episode.Slug))
.ToArray();
}
@@ -245,7 +245,7 @@ namespace Kyoo.Core.Controllers
.FirstOrDefault(x => Utility.ToSlug(Path.GetFileNameWithoutExtension(x)) == slug);
if (font == null)
return null;
- return new Font(font);
+ return new Font(font, episode.Slug);
}
///
diff --git a/front/src/models/resources/watch-item.ts b/front/src/models/resources/watch-item.ts
index 023353d1..8131a47d 100644
--- a/front/src/models/resources/watch-item.ts
+++ b/front/src/models/resources/watch-item.ts
@@ -20,7 +20,7 @@
import { z } from "zod";
import { zdate } from "~/utils/zod";
-import { ResourceP, ImagesP } from "../traits";
+import { ResourceP, ImagesP, imageFn } from "../traits";
import { EpisodeP } from "./episode";
/**
@@ -59,6 +59,10 @@ export const TrackP = ResourceP.extend({
* A user-friendly name for this track. It does not include the track type.
*/
displayName: z.string(),
+ /*
+ * The url of this track (only if this is a subtitle)..
+ */
+ link: z.string().transform(imageFn).nullable(),
});
export type Track = z.infer;
@@ -78,7 +82,7 @@ export const FontP = z.object({
/*
* The url of the font.
*/
- link: z.string(),
+ link: z.string().transform(imageFn),
});
export type Font = z.infer;
@@ -105,13 +109,6 @@ const WatchMovieP = z.preprocess(
if (!x) return x;
x.name = x.title;
- x.link = {
- direct: `/api/video/${x.slug}`,
- };
- x.fonts = x.fonts?.map((y: Font) => {
- y.link = `/api/watch/${x.slug}/font/${y.slug}.${y.format}`;
- return y;
- })
return x;
},
ImagesP.extend({
@@ -155,7 +152,8 @@ const WatchMovieP = z.preprocess(
* The links to the videos of this watch item.
*/
link: z.object({
- direct: z.string(),
+ direct: z.string().transform(imageFn),
+ transmux: z.string().transform(imageFn),
}),
}),
);
diff --git a/front/src/models/traits/images.ts b/front/src/models/traits/images.ts
index de3e0a25..a1273237 100644
--- a/front/src/models/traits/images.ts
+++ b/front/src/models/traits/images.ts
@@ -20,7 +20,7 @@
import { z } from "zod";
-const imageFn = (url: string) => (url.startsWith("/api") ? url : `/api${url}`);
+export const imageFn = (url: string) => (url.startsWith("/api") ? url : `/api${url}`);
export const ImagesP = z.object({
/**
diff --git a/front/src/player/state.tsx b/front/src/player/state.tsx
index 9db11d02..99e634e9 100644
--- a/front/src/player/state.tsx
+++ b/front/src/player/state.tsx
@@ -21,7 +21,7 @@
import { BoxProps } from "@mui/material";
import { atom, useSetAtom } from "jotai";
import { useRouter } from "next/router";
-import { RefObject, useCallback, useEffect, useRef, useState } from "react";
+import { RefObject, useEffect, useRef } from "react";
import { Font, Track } from "~/models/resources/watch-item";
import { bakedAtom } from "~/utils/jotai-utils";
// @ts-ignore
@@ -169,7 +169,7 @@ export const [_subtitleAtom, subtitleAtom] = bakedAtom