diff --git a/.env.example b/.env.example
index 123a0388..7dc4f780 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,6 @@
# Useful config options
LIBRARY_ROOT=/video
+CACHE_ROOT=/tmp/kyoo_cache
LIBRARY_LANGUAGES=en
# The following two values should be set to a random sequence of characters.
diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml
deleted file mode 100644
index 0b30aa3c..00000000
--- a/.github/workflows/analysis.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-name: Analysis
-on:
- push:
- branches:
- - master
- - next
- pull_request:
-
-
-jobs:
- analysis:
- name: Static Analysis
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
-
- - name: Cache SonarCloud packages
- uses: actions/cache@v1
- with:
- path: ~/sonar/cache
- key: ${{ runner.os }}-sonar
- restore-keys: ${{ runner.os }}-sonar
-
- - name: Cache SonarCloud scanner
- id: cache-sonar-scanner
- uses: actions/cache@v1
- with:
- path: ~/.sonar/scanner
- key: ${{ runner.os }}-sonar-scanner
- restore-keys: ${{ runner.os }}-sonar-scanner
-
- - name: Install SonarCloud scanner
- if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
- shell: bash
- run: |
- cd back
- mkdir -p ~/.sonar/scanner
- dotnet tool update dotnet-sonarscanner --tool-path ~/.sonar/scanner
-
- - name: Wait for tests to run (Push)
- uses: lewagon/wait-on-check-action@master
- if: github.event_name != 'pull_request'
- with:
- ref: ${{github.ref}}
- check-name: "Back tests"
- repo-token: ${{secrets.GITHUB_TOKEN}}
- running-workflow-name: analysis
- allowed-conclusions: success,skipped,cancelled,neutral,failure
- - name: Wait for tests to run (PR)
- uses: lewagon/wait-on-check-action@master
- if: github.event_name == 'pull_request'
- with:
- ref: ${{github.event.pull_request.head.sha}}
- check-name: "Back tests"
- repo-token: ${{secrets.GITHUB_TOKEN}}
- running-workflow-name: analysis
- allowed-conclusions: success,skipped,cancelled,neutral,failure
-
- - name: Download coverage report
- uses: dawidd6/action-download-artifact@v2
- with:
- commit: ${{env.COMMIT_SHA}}
- workflow: tests.yml
- github_token: ${{secrets.GITHUB_TOKEN}}
-
- - name: Build and analyze
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
- SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- shell: bash
- run: |
- cp -r results.xml/ coverage.xml/ back/
- cd back
- find . -name 'coverage.opencover.xml'
- dotnet build-server shutdown
-
- ~/.sonar/scanner/dotnet-sonarscanner begin \
- -k:"AnonymusRaccoon_Kyoo" \
- -o:"anonymus-raccoon" \
- -d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
- -d:sonar.host.url="https://sonarcloud.io" \
- -d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" \
- -d:sonar.cs.vstest.reportsPaths="**/TestOutputResults.xml"
-
- dotnet build --no-incremental '-p:SkipTranscoder=true'
-
- ~/.sonar/scanner/dotnet-sonarscanner end -d:sonar.login="${{ secrets.SONAR_TOKEN }}"
diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml
index e27da2f0..cc6bc419 100644
--- a/.github/workflows/coding-style.yml
+++ b/.github/workflows/coding-style.yml
@@ -41,6 +41,7 @@ jobs:
- name: Lint
run: yarn lint
+
scanner:
name: "Lint scanner"
runs-on: ubuntu-latest
@@ -54,3 +55,18 @@ jobs:
run: |
pip install black-with-tabs
black . --check
+
+ transcoder:
+ name: "Lint transcoder"
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./transcoder
+ steps:
+ - uses: actions/checkout@v1
+
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Run cargo fmt
+ run: |
+ cargo fmt --check
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 3da3af3b..32cbe682 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -24,6 +24,9 @@ jobs:
- context: ./scanner
label: scanner
image: zoriya/kyoo_scanner
+ - context: ./transcoder
+ label: transcoder
+ image: zoriya/kyoo_transcoder
name: Build ${{matrix.label}}
steps:
- uses: actions/checkout@v2
diff --git a/back/Dockerfile b/back/Dockerfile
index 1a3818d2..c4c5935a 100644
--- a/back/Dockerfile
+++ b/back/Dockerfile
@@ -1,4 +1,4 @@
-FROM gcc:latest as transcoder
+FROM mcr.microsoft.com/dotnet/sdk:6.0 as transcoder
RUN apt-get update && apt-get install -y cmake make libavutil-dev libavcodec-dev libavformat-dev
WORKDIR /transcoder
COPY src/Kyoo.Transcoder .
@@ -29,6 +29,6 @@ COPY --from=transcoder /transcoder/libtranscoder.so /app
WORKDIR /kyoo
EXPOSE 5000
-HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit
+HEALTHCHECK --interval=5s CMD curl --fail http://localhost:5000/health || exit
CMD /app/Kyoo.Host
diff --git a/back/Dockerfile.dev b/back/Dockerfile.dev
index 373dfa5b..34882c9f 100644
--- a/back/Dockerfile.dev
+++ b/back/Dockerfile.dev
@@ -1,5 +1,6 @@
-FROM gcc:latest as transcoder
-RUN apt-get update && apt-get install -y cmake make libavutil-dev libavcodec-dev libavformat-dev
+FROM mcr.microsoft.com/dotnet/sdk:6.0 as transcoder
+# Using the dotnet sdk as a base image to have the same versions of glibc/ffmpeg
+RUN apt-get update && apt-get install -y gcc cmake make libavutil-dev libavcodec-dev libavformat-dev
WORKDIR /transcoder
COPY src/Kyoo.Transcoder .
RUN cmake . && make -j
@@ -20,11 +21,12 @@ COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
RUN dotnet restore
-COPY --from=transcoder /transcoder/libtranscoder.so /app/out/bin/Kyoo.Host/Debug/net6.0/libtranscoder.so
+COPY --from=transcoder /transcoder/libtranscoder.so /lib/libtranscoder.so
WORKDIR /kyoo
EXPOSE 5000
ENV DOTNET_USE_POLLING_FILE_WATCHER 1
-HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit
+HEALTHCHECK --interval=5s CMD curl --fail http://localhost:5000/health || exit
+# ENV LD_DEBUG libs
CMD dotnet watch run --no-restore --project /app/src/Kyoo.Host
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
index bb60183f..ccf1f420 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
@@ -125,7 +125,7 @@ namespace Kyoo.Abstractions.Models
///
/// The path of the video file for this episode. Any format supported by a is allowed.
///
- [SerializeIgnore] public string Path { get; set; }
+ public string Path { get; set; }
///
public Dictionary Images { get; set; }
diff --git a/back/src/Kyoo.Abstractions/Models/WatchItem.cs b/back/src/Kyoo.Abstractions/Models/WatchItem.cs
index 15d7ddf5..13e61ae0 100644
--- a/back/src/Kyoo.Abstractions/Models/WatchItem.cs
+++ b/back/src/Kyoo.Abstractions/Models/WatchItem.cs
@@ -141,11 +141,14 @@ namespace Kyoo.Abstractions.Models
///
public ICollection Chapters { get; set; }
+ [SerializeIgnore]
+ private string _Type => IsMovie ? "movie" : "episode";
+
///
public object Link => new
{
- Direct = $"/video/direct/{Slug}",
- Transmux = $"/video/transmux/{Slug}/master.m3u8",
+ Direct = $"/video/{_Type}/{Slug}/direct",
+ Hls = $"/video/{_Type}/{Slug}/master.m3u8",
};
///
diff --git a/back/src/Kyoo.Core/CoreModule.cs b/back/src/Kyoo.Core/CoreModule.cs
index 5e808c9a..e1613c5c 100644
--- a/back/src/Kyoo.Core/CoreModule.cs
+++ b/back/src/Kyoo.Core/CoreModule.cs
@@ -16,9 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see .
-using System;
using System.Collections.Generic;
using System.Linq;
+using AspNetCore.Proxy;
using Autofac;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
@@ -113,6 +113,7 @@ namespace Kyoo.Core
x.EnableForHttps = true;
});
+ services.AddProxies();
services.AddHttpClient();
}
diff --git a/back/src/Kyoo.Core/Kyoo.Core.csproj b/back/src/Kyoo.Core/Kyoo.Core.csproj
index 8f113039..7342f457 100644
--- a/back/src/Kyoo.Core/Kyoo.Core.csproj
+++ b/back/src/Kyoo.Core/Kyoo.Core.csproj
@@ -12,6 +12,7 @@
+
diff --git a/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs b/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs
new file mode 100644
index 00000000..207700a1
--- /dev/null
+++ b/back/src/Kyoo.Core/Views/Watch/ProxyApi.cs
@@ -0,0 +1,48 @@
+// 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.Threading.Tasks;
+using AspNetCore.Proxy;
+using Kyoo.Abstractions.Models.Permissions;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Kyoo.Core.Api
+{
+ ///
+ /// Proxy to other services
+ ///
+ [ApiController]
+ public class ProxyApi : Controller
+ {
+ ///
+ /// Transcoder proxy
+ ///
+ ///
+ /// Simply proxy requests to the transcoder
+ ///
+ /// The path of the transcoder.
+ /// The return value of the transcoder.
+ [Route("video/{**rest}")]
+ [Permission("video", Kind.Read)]
+ public Task Proxy(string rest)
+ {
+ // TODO: Use an env var to configure transcoder:7666.
+ return this.HttpProxyAsync($"http://transcoder:7666/{rest}");
+ }
+ }
+}
diff --git a/back/src/Kyoo.Core/Views/Watch/VideoApi.cs b/back/src/Kyoo.Core/Views/Watch/VideoApi.cs
deleted file mode 100644
index 40f8c881..00000000
--- a/back/src/Kyoo.Core/Views/Watch/VideoApi.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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.IO;
-using System.Threading.Tasks;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Kyoo.Abstractions.Models.Attributes;
-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.AspNetCore.Mvc.Filters;
-using Microsoft.Extensions.Options;
-using static Kyoo.Abstractions.Models.Utils.Constants;
-
-namespace Kyoo.Core.Api
-{
- ///
- /// Get the video in a raw format or transcoded in the codec you want.
- ///
- [Route("videos")]
- [Route("video", Order = AlternativeRoute)]
- [ApiController]
- [ApiDefinition("Videos", Group = WatchGroup)]
- public class VideoApi : Controller
- {
- ///
- /// The library manager used to modify or retrieve information in the data store.
- ///
- private readonly ILibraryManager _libraryManager;
-
- ///
- /// The file system used to send video files.
- ///
- private readonly IFileSystem _files;
-
- ///
- /// Create a new .
- ///
- /// The library manager used to retrieve episodes.
- /// The file manager used to send video files.
- public VideoApi(ILibraryManager libraryManager,
- IFileSystem files)
- {
- _libraryManager = libraryManager;
- _files = files;
- }
-
- ///
- ///
- /// Disabling the cache prevent an issue on firefox that skip the last 30 seconds of HLS files
- ///
- public override void OnActionExecuted(ActionExecutedContext ctx)
- {
- base.OnActionExecuted(ctx);
- ctx.HttpContext.Response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
- ctx.HttpContext.Response.Headers.Add("Pragma", "no-cache");
- ctx.HttpContext.Response.Headers.Add("Expires", "0");
- }
-
- ///
- /// Direct video
- ///
- ///
- /// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or
- /// transmuxing is done.
- ///
- /// The identifier of the episode to retrieve.
- /// The raw video stream
- /// No episode exists for the given identifier.
- // TODO enable the following line, this is disabled since the web app can't use bearers. [Permission("video", Kind.Read)]
- [HttpGet("direct/{identifier:id}")]
- [HttpGet("{identifier:id}", Order = AlternativeRoute)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task Direct(Identifier identifier)
- {
- Episode episode = await identifier.Match(
- id => _libraryManager.GetOrDefault(id),
- slug => _libraryManager.GetOrDefault(slug)
- );
- return _files.FileResult(episode?.Path, true);
- }
-
- ///
- /// Transmux video
- ///
- ///
- /// Change the container of the video to hls but don't re-encode the video or audio. This doesn't require mutch
- /// resources from the server.
- ///
- /// The identifier of the episode to retrieve.
- /// The transmuxed video stream
- /// No episode exists for the given identifier.
- [HttpGet("transmux/{identifier:id}/master.m3u8")]
- [Permission("video", Kind.Read)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task Transmux(Identifier identifier)
- {
- Episode episode = await identifier.Match(
- id => _libraryManager.GetOrDefault(id),
- slug => _libraryManager.GetOrDefault(slug)
- );
- return _files.Transmux(episode);
- }
-
- ///
- /// Transmuxed chunk
- ///
- ///
- /// Retrieve a chunk of a transmuxed video.
- ///
- /// The identifier of the episode.
- /// The identifier of the chunk to retrieve.
- /// The options used to retrieve the path of the segments.
- /// A transmuxed video chunk.
- [HttpGet("transmux/{episodeLink}/segments/{chunk}", Order = AlternativeRoute)]
- [Permission("video", Kind.Read)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public IActionResult GetTransmuxedChunk(string episodeLink, string chunk,
- [FromServices] IOptions options)
- {
- string path = Path.GetFullPath(Path.Combine(options.Value.TransmuxPath, episodeLink));
- path = Path.Combine(path, "segments", chunk);
- return PhysicalFile(path, "video/MP2T");
- }
- }
-}
diff --git a/back/src/Kyoo.Transcoder/tests/test_main.c b/back/src/Kyoo.Transcoder/tests/test_main.c
index 653c3015..fba69376 100644
--- a/back/src/Kyoo.Transcoder/tests/test_main.c
+++ b/back/src/Kyoo.Transcoder/tests/test_main.c
@@ -69,4 +69,4 @@ int main(int argc, char **argv)
%s info video_path - Test info prober\n\
%s transmux video_path m3u8_output_file - Test transmuxing\n", argv[0], argv[0]);
return 0;
-}
\ No newline at end of file
+}
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 215bef95..b510fa5b 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -50,6 +50,20 @@ services:
volumes:
- ${LIBRARY_ROOT}:/video
+ transcoder:
+ build:
+ context: ./transcoder
+ dockerfile: Dockerfile.dev
+ ports:
+ - "7666:7666"
+ restart: on-failure
+ env_file:
+ - ./.env
+ volumes:
+ - ./transcoder:/app
+ - ${LIBRARY_ROOT}:/video
+ - ${CACHE_ROOT}:/cache
+
ingress:
image: nginx
restart: on-failure
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 861448f9..618a3454 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -11,7 +11,6 @@ services:
condition: service_healthy
volumes:
- kyoo:/kyoo
- - ./cache:/kyoo/cached
- ${LIBRARY_ROOT}:/video
front:
@@ -34,6 +33,15 @@ services:
volumes:
- ${LIBRARY_ROOT}:/video
+ transcoder:
+ image: zoriya/kyoo_transcoder:edge
+ restart: on-failure
+ env_file:
+ - ./.env
+ volumes:
+ - ${LIBRARY_ROOT}:/video
+ - ${CACHE_ROOT}:/cache
+
ingress:
image: nginx
restart: on-failure
diff --git a/docker-compose.yml b/docker-compose.yml
index 3402bfca..fc1b6db5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,6 @@ services:
condition: service_healthy
volumes:
- kyoo:/kyoo
- - ./cache:/kyoo/cached
- ${LIBRARY_ROOT}:/video
front:
@@ -34,6 +33,15 @@ services:
volumes:
- ${LIBRARY_ROOT}:/video
+ transcoder:
+ build: ./transcoder
+ restart: on-failure
+ env_file:
+ - ./.env
+ volumes:
+ - ${LIBRARY_ROOT}:/video
+ - ${CACHE_ROOT}:/cache
+
ingress:
image: nginx
restart: on-failure
diff --git a/front/Dockerfile.dev b/front/Dockerfile.dev
index b1e58e67..6c22cbbd 100644
--- a/front/Dockerfile.dev
+++ b/front/Dockerfile.dev
@@ -1,4 +1,4 @@
-FROM node:16-alpine AS builder
+FROM node:16-alpine
RUN apk add git bash
WORKDIR /app
COPY .yarn ./.yarn
@@ -14,4 +14,4 @@ RUN yarn --immutable
ENV NEXT_TELEMETRY_DISABLED 1
EXPOSE 3000
EXPOSE 19000
-CMD ["yarn", "dev"]
+CMD yarn dev
diff --git a/front/packages/models/src/login.ts b/front/packages/models/src/login.ts
index 513170c2..6ade564a 100644
--- a/front/packages/models/src/login.ts
+++ b/front/packages/models/src/login.ts
@@ -78,7 +78,6 @@ export const getTokenWJ = async (cookies?: string): Promise<[string, Token] | [n
export const getToken = async (cookies?: string): Promise =>
(await getTokenWJ(cookies))[0]
-
export const logout = async () =>{
deleteSecureItem("auth")
}
diff --git a/front/packages/models/src/resources/watch-item.ts b/front/packages/models/src/resources/watch-item.ts
index e063f749..76995234 100644
--- a/front/packages/models/src/resources/watch-item.ts
+++ b/front/packages/models/src/resources/watch-item.ts
@@ -129,7 +129,8 @@ const WatchMovieP = z.preprocess(
*/
releaseDate: zdate().nullable(),
/**
- * The container of the video file of this episode. Common containers are mp4, mkv, avi and so on.
+ * The container of the video file of this episode. Common containers are mp4, mkv, avi and so
+ * on.
*/
container: z.string(),
/**
@@ -157,7 +158,7 @@ const WatchMovieP = z.preprocess(
*/
link: z.object({
direct: z.string().transform(imageFn),
- transmux: z.string().transform(imageFn),
+ hls: z.string().transform(imageFn),
}),
}),
);
@@ -185,7 +186,8 @@ const WatchEpisodeP = WatchMovieP.and(
*/
episodeNumber: z.number().nullable(),
/**
- * The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
+ * The absolute number of this episode. It's an episode number that is not reset to 1 after a
+ * new season.
*/
absoluteNumber: z.number().nullable(),
/**
diff --git a/front/packages/primitives/src/menu.tsx b/front/packages/primitives/src/menu.tsx
index ffc52b86..a1c4b4ba 100644
--- a/front/packages/primitives/src/menu.tsx
+++ b/front/packages/primitives/src/menu.tsx
@@ -43,7 +43,7 @@ const Menu = ({
...props
}: {
Trigger: ComponentType;
- children: ReactNode | ReactNode[] | null;
+ children?: ReactNode | ReactNode[] | null;
onMenuOpen?: () => void;
onMenuClose?: () => void;
} & Omit) => {
diff --git a/front/packages/ui/src/player/components/hover.tsx b/front/packages/ui/src/player/components/hover.tsx
index e63e173b..3c92dba7 100644
--- a/front/packages/ui/src/player/components/hover.tsx
+++ b/front/packages/ui/src/player/components/hover.tsx
@@ -33,9 +33,9 @@ import {
tooltip,
ts,
} from "@kyoo/primitives";
-import { Chapter, Font, Track } from "@kyoo/models";
+import { Chapter, Font, Track, WatchItem } from "@kyoo/models";
import { useAtomValue, useSetAtom, useAtom } from "jotai";
-import { View, ViewProps } from "react-native";
+import { Platform, View, ViewProps } from "react-native";
import { useTranslation } from "react-i18next";
import { percent, rem, useYoshiki } from "yoshiki/native";
import { useRouter } from "solito/router";
@@ -51,6 +51,7 @@ export const Hover = ({
href,
poster,
chapters,
+ qualities,
subtitles,
fonts,
previousSlug,
@@ -66,6 +67,7 @@ export const Hover = ({
href?: string;
poster?: string | null;
chapters?: Chapter[];
+ qualities?: WatchItem["link"]
subtitles?: Track[];
fonts?: Font[];
previousSlug?: string | null;
@@ -85,7 +87,8 @@ export const Hover = ({
{...css(
[
{
- position: "absolute",
+ // Fixed is used because firefox android make the hover disapear under the navigation bar in absolute
+ position: Platform.OS === "web" ? "fixed" as any : "absolute",
bottom: 0,
left: 0,
right: 0,
@@ -104,6 +107,7 @@ export const Hover = ({
marginLeft: { xs: ts(0.5), sm: ts(3) },
flexDirection: "column",
flexGrow: 1,
+ maxWidth: percent(100),
})}
>
@@ -117,6 +121,7 @@ export const Hover = ({
diff --git a/front/packages/ui/src/player/components/right-buttons.tsx b/front/packages/ui/src/player/components/right-buttons.tsx
index 0e7f9869..b5fa5146 100644
--- a/front/packages/ui/src/player/components/right-buttons.tsx
+++ b/front/packages/ui/src/player/components/right-buttons.tsx
@@ -18,7 +18,7 @@
* along with Kyoo. If not, see .
*/
-import { Font, Track } from "@kyoo/models";
+import { Font, Track, WatchItem } from "@kyoo/models";
import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives";
import { useAtom, useSetAtom } from "jotai";
import { useEffect, useState } from "react";
@@ -27,21 +27,23 @@ import { useTranslation } from "react-i18next";
import ClosedCaption from "@material-symbols/svg-400/rounded/closed_caption-fill.svg";
import Fullscreen from "@material-symbols/svg-400/rounded/fullscreen-fill.svg";
import FullscreenExit from "@material-symbols/svg-400/rounded/fullscreen_exit-fill.svg";
+import SettingsIcon from "@material-symbols/svg-400/rounded/settings-fill.svg";
+import MusicNote from "@material-symbols/svg-400/rounded/music_note-fill.svg";
import { Stylable, useYoshiki } from "yoshiki/native";
-import { createParam } from "solito";
import { fullscreenAtom, subtitleAtom } from "../state";
-
-const { useParam } = createParam<{ subtitle?: string }>();
+import { AudiosMenu, QualitiesMenu } from "../video";
export const RightButtons = ({
subtitles,
fonts,
+ qualities,
onMenuOpen,
onMenuClose,
...props
}: {
subtitles?: Track[];
fonts?: Font[];
+ qualities?: WatchItem["link"];
onMenuOpen: () => void;
onMenuClose: () => void;
} & Stylable) => {
@@ -63,7 +65,7 @@ export const RightButtons = ({
return (
- {subtitles && (
+ {subtitles && subtitles.length > 0 && (
setHover(true)}
- // @ts-ignore Web only types
- onMouseLeave={() => setHover(false)}
+ onPointerEnter={(e) => { if (e.nativeEvent.pointerType === "mouse") setHover(true) }}
+ onPointerLeave={(e) => { if (e.nativeEvent.pointerType === "mouse") setHover(false) }}
+ onPointerDown={(e) => {
+ // also handle touch here because if we dont, the area where the hover should be will catch touches
+ // without openning the hover.
+ if (e.nativeEvent.pointerType !== "mouse")
+ displayControls ? setMouseMoved(false) : show();
+ }}
onMenuOpen={() => setMenuOpen(true)}
onMenuClose={() => {
// Disable hover since the menu overlay makes the mouseout unreliable.
@@ -215,7 +212,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
}}
show={displayControls}
/>
-
+
>
);
};
diff --git a/front/packages/ui/src/player/state.tsx b/front/packages/ui/src/player/state.tsx
index 2c049a07..993edca9 100644
--- a/front/packages/ui/src/player/state.tsx
+++ b/front/packages/ui/src/player/state.tsx
@@ -20,13 +20,20 @@
import { Track, WatchItem, Font } from "@kyoo/models";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
-import { memo, useEffect, useLayoutEffect, useRef } from "react";
+import { memo, useEffect, useLayoutEffect, useRef, useState } from "react";
import NativeVideo, { VideoProperties as VideoProps } from "./video";
import { Platform } from "react-native";
export const playAtom = atom(true);
export const loadAtom = atom(false);
+// TODO: Default to auto or pristine depending on the user settings.
+export enum PlayMode {
+ Direct,
+ Hls,
+}
+export const playModeAtom = atom(PlayMode.Direct);
+
export const bufferedAtom = atom(0);
export const durationAtom = atom(undefined);
@@ -56,15 +63,15 @@ export const fullscreenAtom = atom(
set(privateFullscreen, false);
screen.orientation.unlock();
}
- } catch {}
+ } catch(e) {
+ console.error(e);
+ }
},
);
const privateFullscreen = atom(false);
export const subtitleAtom = atom