diff --git a/front/packages/models/src/login.ts b/front/packages/models/src/login.ts index 17cf5c9c..e9799a12 100644 --- a/front/packages/models/src/login.ts +++ b/front/packages/models/src/login.ts @@ -24,6 +24,7 @@ import { Account, Token, TokenP, getCurrentApiUrl } from "./accounts"; import { UserP } from "./resources"; import { addAccount, getCurrentAccount, removeAccounts, updateAccount } from "./account-internal"; import { Platform } from "react-native"; +import { useEffect, useRef, useState } from "react"; type Result = | { ok: true; value: A; error?: undefined } @@ -137,6 +138,36 @@ export const getTokenWJ = async ( export const getToken = async (): Promise => (await getTokenWJ())[0]; +export const getCurrentToken = () => { + const account = getCurrentAccount(); + return account ? `${account.token.token_type} ${account.token.access_token}` : null; +}; + +export const useToken = () => { + const account = getCurrentAccount(); + const refresher = useRef(null); + const [token, setToken] = useState( + account ? `${account.token.token_type} ${account.token.access_token}` : null, + ); + + useEffect(() => { + async function run() { + const nToken = await getTokenWJ(); + setToken(nToken[0]); + if (refresher.current) clearTimeout(refresher.current); + if (nToken[1]) + refresher.current = setTimeout(run, nToken[1].expire_at.getTime() - Date.now()); + } + run(); + return () => { + if (refresher.current) clearTimeout(refresher.current); + }; + }, [account]); + + if (!token) return null; + return token; +}; + export const logout = () => { removeAccounts((x) => x.selected); }; diff --git a/front/packages/primitives/src/image/image.tsx b/front/packages/primitives/src/image/image.tsx index 5c0c60d5..667b9de0 100644 --- a/front/packages/primitives/src/image/image.tsx +++ b/front/packages/primitives/src/image/image.tsx @@ -25,6 +25,7 @@ import { Blurhash } from "react-native-blurhash"; import { percent, useYoshiki } from "yoshiki/native"; import { Props, ImageLayout } from "./base-image"; import { Skeleton } from "../skeleton"; +import { getCurrentToken } from "@kyoo/models"; export const Image = ({ src, @@ -60,6 +61,7 @@ export const Image = ({ } quality ??= "high"; + const token = getCurrentToken(); return ( {state !== "finished" && ( @@ -72,6 +74,11 @@ export const Image = ({ = ({ useEffect(() => { if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); }, [apiUrl, router]); @@ -74,7 +74,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({ setError(error); if (error) return; router.replace("/", undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); }} {...css({ diff --git a/front/packages/ui/src/login/oidc.tsx b/front/packages/ui/src/login/oidc.tsx index 7de3fdce..43b38772 100644 --- a/front/packages/ui/src/login/oidc.tsx +++ b/front/packages/ui/src/login/oidc.tsx @@ -106,7 +106,7 @@ export const OidcCallbackPage: QueryPage<{ function onError(error: string) { router.replace({ pathname: "/login", query: { error, apiUrl } }, undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); } async function run() { @@ -114,7 +114,7 @@ export const OidcCallbackPage: QueryPage<{ if (loginError) onError(loginError); else { router.replace("/", undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); } } diff --git a/front/packages/ui/src/login/register.tsx b/front/packages/ui/src/login/register.tsx index 0ba1d287..81af64ee 100644 --- a/front/packages/ui/src/login/register.tsx +++ b/front/packages/ui/src/login/register.tsx @@ -45,7 +45,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => { useEffect(() => { if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); }, [apiUrl, router]); @@ -85,7 +85,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => { setError(error); if (error) return; router.replace("/", undefined, { - experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true }, + experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false }, }); }} {...css({ diff --git a/front/packages/ui/src/player/video.tsx b/front/packages/ui/src/player/video.tsx index 4bd497ff..c1b09476 100644 --- a/front/packages/ui/src/player/video.tsx +++ b/front/packages/ui/src/player/video.tsx @@ -33,7 +33,7 @@ declare module "react-native-video" { export * from "react-native-video"; -import { Audio, Subtitle, getToken } from "@kyoo/models"; +import { Audio, Subtitle, getToken, useToken } from "@kyoo/models"; import { IconButton, Menu } from "@kyoo/primitives"; import { ComponentProps, forwardRef, useEffect, useRef } from "react"; import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; @@ -67,20 +67,13 @@ const Video = forwardRef(function Video( ref, ) { const { css } = useYoshiki(); - const token = useRef(null); + const token = useToken(); const setInfo = useSetAtom(infoAtom); const [video, setVideo] = useAtom(videoAtom); const audio = useAtomValue(audioAtom); const subtitle = useAtomValue(subtitleAtom); const mode = useAtomValue(playModeAtom); - useEffect(() => { - async function run() { - token.current = await getToken(); - } - run(); - }, [source]); - useEffect(() => { if (mode === PlayMode.Hls) setVideo(-1); }, [mode, setVideo]); @@ -92,7 +85,7 @@ const Video = forwardRef(function Video( source={{ ...source, headers: { - Authorization: `Bearer: ${token.current}`, + ...(token ? { Authorization: token } : {}), "X-CLIENT-ID": clientId, }, }} @@ -181,17 +174,19 @@ export const QualitiesMenu = (props: CustomMenu) => { /> {/* TODO: Support video tracks when the play mode is not hls. */} {/* @ts-expect-error They forgot to type this. */} - {info?.videoTracks.map((x) => ( - { - setPlayMode(PlayMode.Hls); - setVideo(x.height); - }} - /> - ))} + {info?.videoTracks + .sort((a: any, b: any) => b.height - a.height) + .map((x: any, i: number) => ( + { + setPlayMode(PlayMode.Hls); + setVideo(x.height); + }} + /> + ))} ); }; diff --git a/front/packages/ui/src/player/video.web.tsx b/front/packages/ui/src/player/video.web.tsx index c3f1fd02..a0454ab3 100644 --- a/front/packages/ui/src/player/video.web.tsx +++ b/front/packages/ui/src/player/video.web.tsx @@ -70,7 +70,7 @@ const initHls = (): Hls => { hls = new Hls({ xhrSetup: async (xhr) => { const token = await getToken(); - if (token) xhr.setRequestHeader("Authorization", `Bearer: ${token}`); + if (token) xhr.setRequestHeader("Authorization", token); xhr.setRequestHeader("X-CLIENT-ID", client_id); }, autoStartLoad: false, diff --git a/transcoder/src/filestream.go b/transcoder/src/filestream.go index dd8c2a03..984cafb1 100644 --- a/transcoder/src/filestream.go +++ b/transcoder/src/filestream.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "os" + "strings" "sync" ) @@ -90,10 +91,12 @@ func (fs *FileStream) GetMaster() string { } aspectRatio := float32(fs.Info.Video.Width) / float32(fs.Info.Video.Height) - transmux_codec := "avc1.640028" + // codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow + transmux_prefix := "avc1.6400" + transmux_codec := transmux_prefix + "28" for _, quality := range Qualities { - same_codec := fs.Info.Video.MimeCodec != nil && *fs.Info.Video.MimeCodec == transmux_codec + same_codec := fs.Info.Video.MimeCodec != nil && strings.HasPrefix(*fs.Info.Video.MimeCodec, transmux_prefix) inc_lvl := quality.Height() < fs.Info.Video.Quality.Height() || (quality.Height() == fs.Info.Video.Quality.Height() && !same_codec)