mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-07 23:18:38 +08:00
1
This commit is contained in:
@@ -62,7 +62,6 @@ const LIVE_AREAS = [
|
|||||||
{ id: 9, name: "虚拟主播" },
|
{ id: 9, name: "虚拟主播" },
|
||||||
{ id: 10, name: "生活" },
|
{ id: 10, name: "生活" },
|
||||||
{ id: 11, name: "知识" },
|
{ id: 11, name: "知识" },
|
||||||
{ id: 13, name: "赛事" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default function LiveDetailScreen() {
|
|||||||
|
|
||||||
const isLive = room?.live_status === 1;
|
const isLive = room?.live_status === 1;
|
||||||
const hlsUrl = stream?.hlsUrl ?? "";
|
const hlsUrl = stream?.hlsUrl ?? "";
|
||||||
|
const flvUrl = stream?.flvUrl ?? "";
|
||||||
const qualities = stream?.qualities ?? [];
|
const qualities = stream?.qualities ?? [];
|
||||||
const currentQn = stream?.qn ?? 0;
|
const currentQn = stream?.qn ?? 0;
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ export default function LiveDetailScreen() {
|
|||||||
{/* Player */}
|
{/* Player */}
|
||||||
<LivePlayer
|
<LivePlayer
|
||||||
hlsUrl={hlsUrl}
|
hlsUrl={hlsUrl}
|
||||||
|
flvUrl={flvUrl}
|
||||||
isLive={isLive}
|
isLive={isLive}
|
||||||
qualities={qualities}
|
qualities={qualities}
|
||||||
currentQn={currentQn}
|
currentQn={currentQn}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Ionicons } from '@expo/vector-icons';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hlsUrl: string;
|
hlsUrl: string;
|
||||||
|
flvUrl?: string;
|
||||||
isLive: boolean;
|
isLive: boolean;
|
||||||
qualities?: { qn: number; desc: string }[];
|
qualities?: { qn: number; desc: string }[];
|
||||||
currentQn?: number;
|
currentQn?: number;
|
||||||
@@ -27,7 +28,7 @@ const HEADERS = {
|
|||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LivePlayer({ hlsUrl, isLive, qualities = [], currentQn = 0, onQualityChange }: Props) {
|
export function LivePlayer({ hlsUrl, flvUrl, isLive, qualities = [], currentQn = 0, onQualityChange }: Props) {
|
||||||
const { width: SCREEN_W, height: SCREEN_H } = useWindowDimensions();
|
const { width: SCREEN_W, height: SCREEN_H } = useWindowDimensions();
|
||||||
const VIDEO_H = SCREEN_W * 0.5625;
|
const VIDEO_H = SCREEN_W * 0.5625;
|
||||||
|
|
||||||
@@ -179,12 +180,6 @@ function NativeLivePlayer({
|
|||||||
|
|
||||||
{showControls && (
|
{showControls && (
|
||||||
<>
|
<>
|
||||||
{/* LIVE badge top-left */}
|
|
||||||
<View style={styles.liveBadge} pointerEvents="none">
|
|
||||||
<View style={styles.liveDot} />
|
|
||||||
<Text style={styles.liveText}>live</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Center play/pause */}
|
{/* Center play/pause */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.centerBtn}
|
style={styles.centerBtn}
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ export function useLiveDanmaku(roomId: number): DanmakuItem[] {
|
|||||||
const info = await getLiveDanmakuInfo(roomId);
|
const info = await getLiveDanmakuInfo(roomId);
|
||||||
token = info.token;
|
token = info.token;
|
||||||
host = info.host;
|
host = info.host;
|
||||||
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[danmaku] getDanmuInfo failed:', e);
|
console.warn('[danmaku] getDanmuInfo failed:', e);
|
||||||
}
|
}
|
||||||
@@ -144,19 +146,18 @@ export function useLiveDanmaku(roomId: number): DanmakuItem[] {
|
|||||||
wsRef.current = ws;
|
wsRef.current = ws;
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
|
||||||
const authBody = JSON.stringify({
|
const authBody = JSON.stringify({
|
||||||
uid: 0,
|
uid: 0,
|
||||||
roomid: roomId,
|
roomid: roomId,
|
||||||
protover: 2,
|
protover: 2,
|
||||||
platform: 'h5', // must match platform param used in getConf API call
|
platform: 'h5',
|
||||||
type: 2,
|
type: 2,
|
||||||
key: token,
|
key: token,
|
||||||
});
|
});
|
||||||
const authPkt = buildPacket(authBody, 7);
|
const authPkt = buildPacket(authBody, 7);
|
||||||
const hdr = Array.from(authPkt.slice(0, 16))
|
|
||||||
.map(b => b.toString(16).padStart(2, '0')).join(' ');
|
|
||||||
// Send as ArrayBuffer (more reliable than Uint8Array in some RN/Hermes versions)
|
|
||||||
const t0 = Date.now();
|
|
||||||
ws.send(authPkt.buffer as ArrayBuffer);
|
ws.send(authPkt.buffer as ArrayBuffer);
|
||||||
|
|
||||||
heartbeatRef.current = setInterval(() => {
|
heartbeatRef.current = setInterval(() => {
|
||||||
@@ -171,10 +172,8 @@ export function useLiveDanmaku(roomId: number): DanmakuItem[] {
|
|||||||
if (items.length > 0) setDanmakus(prev => [...prev, ...items]);
|
if (items.length > 0) setDanmakus(prev => [...prev, ...items]);
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (e: Event) => {
|
ws.onerror = () => {};
|
||||||
const ws2 = (e as any).currentTarget as WebSocket;
|
ws.onclose = () => {
|
||||||
};
|
|
||||||
ws.onclose = (e: CloseEvent) => {
|
|
||||||
if (heartbeatRef.current) clearInterval(heartbeatRef.current);
|
if (heartbeatRef.current) clearInterval(heartbeatRef.current);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ export async function getLiveAnchorInfo(roomId: number): Promise<LiveAnchorInfo>
|
|||||||
export async function getLiveStreamUrl(roomId: number, qn = 10000): Promise<LiveStreamInfo> {
|
export async function getLiveStreamUrl(roomId: number, qn = 10000): Promise<LiveStreamInfo> {
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`${LIVE_BASE}/xlive/web-room/v2/index/getRoomPlayInfo`, {
|
const res = await api.get(`${LIVE_BASE}/xlive/web-room/v2/index/getRoomPlayInfo`, {
|
||||||
params: { room_id: roomId, protocol: '0,1', format: '0,1,2', codec: '0', qn },
|
params: { room_id: roomId, protocol: '0,1', format: '0,1,2', codec: '0', qn, platform: 'android' },
|
||||||
});
|
});
|
||||||
const playurl = res.data?.data?.playurl_info?.playurl;
|
const playurl = res.data?.data?.playurl_info?.playurl;
|
||||||
const streams: any[] = playurl?.stream ?? [];
|
const streams: any[] = playurl?.stream ?? [];
|
||||||
@@ -338,11 +338,14 @@ export async function searchVideos(keyword: string, page = 1): Promise<VideoItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getLiveDanmakuInfo(roomId: number): Promise<{ token: string; host: string }> {
|
export async function getLiveDanmakuInfo(roomId: number): Promise<{ token: string; host: string }> {
|
||||||
const res = await api.get(`${LIVE_BASE}/room/v1/Danmu/getConf`, {
|
const { imgKey, subKey } = await getWbiKeys();
|
||||||
params: { room_id: roomId, platform: 'h5' },
|
const signed = signWbi({ id: roomId, type: 0 }, imgKey, subKey);
|
||||||
|
const res = await api.get(`${LIVE_BASE}/xlive/web-room/v1/index/getDanmuInfo`, {
|
||||||
|
params: signed,
|
||||||
});
|
});
|
||||||
const data = res.data.data;
|
const data = res.data.data;
|
||||||
const hostInfo = data?.host_server_list?.[0];
|
const hostList: any[] = data?.host_list ?? [];
|
||||||
|
const hostInfo = hostList.find(h => h.wss_port === 443) ?? hostList[0];
|
||||||
const host = hostInfo
|
const host = hostInfo
|
||||||
? `wss://${hostInfo.host}:${hostInfo.wss_port}/sub`
|
? `wss://${hostInfo.host}:${hostInfo.wss_port}/sub`
|
||||||
: 'wss://broadcastlv.chat.bilibili.com/sub';
|
: 'wss://broadcastlv.chat.bilibili.com/sub';
|
||||||
|
|||||||
Reference in New Issue
Block a user