diff --git a/app.json b/app.json
index 1aa8150..2683ddd 100644
--- a/app.json
+++ b/app.json
@@ -36,7 +36,8 @@
"favicon": "./assets/favicon.png"
},
"plugins": [
- "expo-router"
+ "expo-router",
+ "react-native-video"
],
"experiments": {
"typedRoutes": true
diff --git a/components/NativeVideoPlayer.tsx b/components/NativeVideoPlayer.tsx
index 811988b..01d8220 100644
--- a/components/NativeVideoPlayer.tsx
+++ b/components/NativeVideoPlayer.tsx
@@ -1,16 +1,21 @@
-import React, { useState } from 'react';
+import React, { useState, useRef, useEffect } from 'react';
import {
View, StyleSheet, Dimensions, TouchableOpacity,
- Text, Modal, FlatList,
+ Text, Modal,
} from 'react-native';
-import { WebView } from 'react-native-webview';
+import Video, { VideoRef } from 'react-native-video';
import { Ionicons } from '@expo/vector-icons';
-import { buildMpd } from '../utils/buildMpd';
import type { PlayUrlResponse } from '../services/types';
+import { buildDashDataUri } from '../utils/dash';
const { width } = Dimensions.get('window');
const VIDEO_HEIGHT = width * 0.5625;
+const BILIBILI_HEADERS = {
+ Referer: 'https://www.bilibili.com',
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+};
+
interface Props {
playData: PlayUrlResponse | null;
qualities: { qn: number; desc: string }[];
@@ -19,76 +24,46 @@ interface Props {
onFullscreen: () => void;
onMiniPlayer?: () => void;
style?: object;
-}
-
-function buildMp4Html(url: string): string {
- return `
-
-
-
-
-
-
-
-
-
-`;
-}
-
-function buildDashHtml(mpdStr: string): string {
- const mpdBase64 = `data:application/dash+xml;base64,${btoa(unescape(encodeURIComponent(mpdStr)))}`;
- return `
-
-
-
-
-
-
-
-
-
-
-`;
-}
-
-function getHtml(playData: PlayUrlResponse | null): string {
- if (!playData) return '';
- if (playData.dash) {
- const v = playData.dash.video[0];
- const a = playData.dash.audio[0];
- if (v && a) {
- const mpd = buildMpd(v.baseUrl, v.codecs, v.bandwidth, a.baseUrl, a.codecs, a.bandwidth);
- return buildDashHtml(mpd);
- }
- }
- const url = playData.durl?.[0]?.url;
- if (url) return buildMp4Html(url);
- return '';
+ onProgress?: (currentTime: number, duration: number) => void;
+ seekTo?: { t: number; v: number };
}
export function NativeVideoPlayer({
playData, qualities, currentQn, onQualityChange, onFullscreen, onMiniPlayer, style,
+ onProgress, seekTo,
}: Props) {
const [showQuality, setShowQuality] = useState(false);
+ const videoRef = useRef(null);
const currentDesc = qualities.find(q => q.qn === currentQn)?.desc ?? (currentQn ? String(currentQn) : 'HD');
- const html = getHtml(playData);
+ const isDash = !!playData?.dash;
+ const url = isDash
+ ? buildDashDataUri(playData!, currentQn)
+ : playData?.durl?.[0]?.url;
+
+ useEffect(() => {
+ if (seekTo !== undefined) videoRef.current?.seek(seekTo.t);
+ }, [seekTo]);
return (
-
+ {url ? (
+