mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-16 02:44:51 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8b33aee3 | ||
|
|
645b226359 | ||
|
|
61ee6238d5 | ||
|
|
a7717dfcb9 | ||
|
|
337f338224 | ||
|
|
72328aacf6 |
@@ -43,16 +43,12 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install npm dependencies (bun)
|
- name: Install/Typecheck/Build
|
||||||
|
# We use a `prepare` script that run builds to support git installs.
|
||||||
run: bun install
|
run: bun install
|
||||||
|
|
||||||
- name: Build lib
|
|
||||||
run: bun run build
|
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
# No NPM_TOKEN: auth happens via OIDC trusted publishing (id-token: write above).
|
|
||||||
# Provenance is generated automatically, so NPM_CONFIG_PROVENANCE is not needed.
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GIT_AUTHOR_NAME: ${{ github.actor }}
|
GIT_AUTHOR_NAME: ${{ github.actor }}
|
||||||
GIT_AUTHOR_EMAIL: '${{ github.actor }}@users.noreply.github.com'
|
GIT_AUTHOR_EMAIL: '${{ github.actor }}@users.noreply.github.com'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# omni
|
# react-native-omni
|
||||||
|
|
||||||
A library to have real players on android and web. It uses VLC on android and
|
A library to have real players on android and web. It uses VLC on android and
|
||||||
videojs v10 on the web (ios not implemented yet, PR welcome)
|
videojs v10 on the web (ios not implemented yet, PR welcome)
|
||||||
@@ -53,14 +53,6 @@ subtitles rendering:
|
|||||||
|
|
||||||
<summary>ass rendering</summary>
|
<summary>ass rendering</summary>
|
||||||
|
|
||||||
#### Fonts
|
|
||||||
|
|
||||||
JASSUB only renders glyphs for fonts it has, and does **not** ship a usable
|
|
||||||
default font. Provide the fonts a subtitle references through
|
|
||||||
`source.fonts` (an array of font-file URLs). Optionally set
|
|
||||||
`subtitleAssets.jassub.fontUrl` as a fallback for styles whose font isn't
|
|
||||||
listed. If no matching font is available, that text simply won't appear.
|
|
||||||
|
|
||||||
#### Cross-origin isolation
|
#### Cross-origin isolation
|
||||||
|
|
||||||
jassub's wasm is multi-threaded and relies on `SharedArrayBuffer`, which
|
jassub's wasm is multi-threaded and relies on `SharedArrayBuffer`, which
|
||||||
|
|||||||
@@ -23,9 +23,9 @@
|
|||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "19.2.7",
|
"react": ">=19.0.0",
|
||||||
"react-native": "0.86.0",
|
"react-native": ">=0.86.0",
|
||||||
"react-native-nitro-modules": "0.36.1",
|
"react-native-nitro-modules": ">=0.36.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"example": {
|
"example": {
|
||||||
|
|||||||
+8
-4
@@ -14,6 +14,7 @@
|
|||||||
"typecheck": "tsc --noEmit && tsc -p plugin/tsconfig.json --noEmit",
|
"typecheck": "tsc --noEmit && tsc -p plugin/tsconfig.json --noEmit",
|
||||||
"build:plugin": "tsc -p plugin/tsconfig.json",
|
"build:plugin": "tsc -p plugin/tsconfig.json",
|
||||||
"build": "bun run typecheck && bob build && bun run build:plugin",
|
"build": "bun run typecheck && bob build && bun run build:plugin",
|
||||||
|
"prepare": "bun run build",
|
||||||
"codegen": "nitrogen --logLevel=\"debug\" && node post-script.js",
|
"codegen": "nitrogen --logLevel=\"debug\" && node post-script.js",
|
||||||
"release": "semantic-release"
|
"release": "semantic-release"
|
||||||
},
|
},
|
||||||
@@ -48,7 +49,10 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"example"
|
"example"
|
||||||
],
|
],
|
||||||
"repository": "https://github.com/zoriya/react-native-omni.git",
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/zoriya/react-native-omni.git"
|
||||||
|
},
|
||||||
"author": "Zoe Roux",
|
"author": "Zoe Roux",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": "https://github.com/zoriya/react-native-omni/issues",
|
"bugs": "https://github.com/zoriya/react-native-omni/issues",
|
||||||
@@ -76,9 +80,9 @@
|
|||||||
"typescript": "^7.0.2"
|
"typescript": "^7.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "19.2.7",
|
"react": ">=19.0.0",
|
||||||
"react-native": "0.86.0",
|
"react-native": ">=0.86.0",
|
||||||
"react-native-nitro-modules": "0.36.1"
|
"react-native-nitro-modules": ">=0.36.0"
|
||||||
},
|
},
|
||||||
"react-native-builder-bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
|
|||||||
+2
-4
@@ -108,18 +108,16 @@ export const useEvent = <Event extends keyof OmniEvents>(
|
|||||||
prevRef.current = value;
|
prevRef.current = value;
|
||||||
}, [value, config]);
|
}, [value, config]);
|
||||||
|
|
||||||
// prev/next are triggered by the app (or the media session) rather than the
|
|
||||||
// media element
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// don't use callbackRef.current directly to keep future callback updates
|
||||||
|
const cb = () => (callbackRef.current as () => void)();
|
||||||
if (event === "prev") {
|
if (event === "prev") {
|
||||||
const cb = callbackRef.current as () => void;
|
|
||||||
player.onPrev.add(cb);
|
player.onPrev.add(cb);
|
||||||
return () => {
|
return () => {
|
||||||
player.onPrev.delete(cb);
|
player.onPrev.delete(cb);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (event === "next") {
|
if (event === "next") {
|
||||||
const cb = callbackRef.current as () => void;
|
|
||||||
player.onNext.add(cb);
|
player.onNext.add(cb);
|
||||||
return () => {
|
return () => {
|
||||||
player.onNext.delete(cb);
|
player.onNext.delete(cb);
|
||||||
|
|||||||
+13
-2
@@ -1,6 +1,12 @@
|
|||||||
import { createPlayer } from "@videojs/react";
|
import { createPlayer } from "@videojs/react";
|
||||||
import { videoFeatures } from "@videojs/react/video";
|
import { videoFeatures } from "@videojs/react/video";
|
||||||
import { createContext, type ReactNode, useContext, useEffect } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
type ReactNode,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from "react";
|
||||||
import { WebOmniPlayer } from "./player.web";
|
import { WebOmniPlayer } from "./player.web";
|
||||||
import type { OmniPlayer } from "./types/player";
|
import type { OmniPlayer } from "./types/player";
|
||||||
import type { Source } from "./types/source";
|
import type { Source } from "./types/source";
|
||||||
@@ -39,10 +45,15 @@ const PlayerInitializer = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const store = VideoPlayer.usePlayer();
|
const store = VideoPlayer.usePlayer();
|
||||||
const player = useLazyRef(() => new WebOmniPlayer(store));
|
const player = useLazyRef(() => new WebOmniPlayer(store));
|
||||||
|
const seekedForSrc = useRef<string | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
player.source = source;
|
player.source = source;
|
||||||
if (source.startTime) store.seek(source.startTime).catch(() => {});
|
const uri = source.src[0]?.uri;
|
||||||
|
if (uri !== seekedForSrc.current) {
|
||||||
|
seekedForSrc.current = uri;
|
||||||
|
if (source.startTime) store.seek(source.startTime)
|
||||||
|
}
|
||||||
}, [source, store]);
|
}, [source, store]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
+17
-8
@@ -108,21 +108,29 @@ export const OmniView = ({
|
|||||||
const ref = useRef<HTMLVideoElement>(undefined!);
|
const ref = useRef<HTMLVideoElement>(undefined!);
|
||||||
|
|
||||||
const src = player.source?.src[0];
|
const src = player.source?.src[0];
|
||||||
const Tech = src?.uri.endsWith("m3u8") ? HlsJsVideo : Video;
|
const isHls =
|
||||||
|
src?.mimeType?.toLowerCase().includes("mpegurl") ||
|
||||||
|
src?.uri.split(/[?#]/)[0]?.toLowerCase().endsWith(".m3u8") ||
|
||||||
|
false;
|
||||||
|
const Tech = isHls ? HlsJsVideo : Video;
|
||||||
|
|
||||||
const config = useMemo<HlsMediaConfig | undefined>(() => {
|
const headersRef = useRef(src?.headers);
|
||||||
const headers = src?.headers;
|
headersRef.current = src?.headers;
|
||||||
if (!headers || Object.keys(headers).length === 0) return undefined;
|
|
||||||
return {
|
const config = useMemo<HlsMediaConfig>(
|
||||||
|
() => ({
|
||||||
hlsJs: {
|
hlsJs: {
|
||||||
xhrSetup: (xhr: XMLHttpRequest) => {
|
xhrSetup: (xhr: XMLHttpRequest) => {
|
||||||
|
const headers = headersRef.current;
|
||||||
|
if (!headers) return;
|
||||||
for (const [key, value] of Object.entries(headers)) {
|
for (const [key, value] of Object.entries(headers)) {
|
||||||
if (value) xhr.setRequestHeader(key, value);
|
if (value) xhr.setRequestHeader(key, value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
}, [src?.headers]);
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VideoPlayer.Container
|
<VideoPlayer.Container
|
||||||
@@ -133,7 +141,7 @@ export const OmniView = ({
|
|||||||
<Tech
|
<Tech
|
||||||
ref={ref}
|
ref={ref}
|
||||||
src={src.uri}
|
src={src.uri}
|
||||||
config={config}
|
config={isHls ? config : undefined}
|
||||||
autoPlay={autoplay}
|
autoPlay={autoplay}
|
||||||
playsInline
|
playsInline
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
@@ -144,6 +152,7 @@ export const OmniView = ({
|
|||||||
.map((subtitle) => (
|
.map((subtitle) => (
|
||||||
<track
|
<track
|
||||||
key={subtitle.id}
|
key={subtitle.id}
|
||||||
|
id={subtitle.id}
|
||||||
kind="subtitles"
|
kind="subtitles"
|
||||||
src={subtitle.link}
|
src={subtitle.link}
|
||||||
srcLang={subtitle.language}
|
srcLang={subtitle.language}
|
||||||
|
|||||||
Reference in New Issue
Block a user