import { useMemo } from 'react' import { View, Text, ScrollView, TextInput, Pressable, StyleSheet, ActivityIndicator, } from 'react-native' import { Picker } from '@react-native-picker/picker' import { SafeAreaView } from 'react-native-safe-area-context' import { useBind } from '@/context/BindContext' import { useRecorder } from '@/hooks/useRecorder' import { theme } from '@/constants/theme' function ctrlStyle(action: string): object { switch (action) { case 'start': return styles.ctrl_start case 'stop': return styles.ctrl_stop case 'restart': return styles.ctrl_restart case 'status': return styles.ctrl_status default: return {} } } const variantColor: Record = { online: theme.success, stopped: theme.danger, error: theme.danger, warn: theme.warn, offline: theme.danger, loading: theme.muted, unknown: theme.accent, empty: theme.accent, } export default function RecordScreen() { const { bind } = useBind() const r = useRecorder() const statusColor = useMemo(() => variantColor[r.statusMeta.variant] ?? theme.muted, [r.statusMeta.variant]) if (r.bootError && bind) { return ( 无法连接:{r.bootError} ) } if (!bind) { return ( 请先在「配对」页扫码绑定 PC。 ) } return ( 当前录制 r.setCurrentProcess(String(v))} style={styles.picker} dropdownIconColor={theme.text} > {r.entries.map((e) => ( ))} {r.isYoutube && ( void r.togglePro(!r.multiChannelPro)} > Pro 转播 {r.multiChannelPro ? '开' : '关'} )} {r.statusMeta.line} {r.emptyHint ? ( {r.emptyHint} ) : ( <> {r.ent && ( 任务 {r.ent.label} {r.ent.script} )} {r.isYoutube && r.multiChannelPro && ( Pro 线路 {r.proChannelsError ? ( {r.proChannelsError} ) : ( r.setProChannelId(String(v))} style={styles.picker} dropdownIconColor={theme.text} > {r.proChannelsList.map((c) => ( ))} )} )} {r.isYoutube && r.multiChannelPro && !r.proChannelsError && r.proChannelsList.length > 0 && ( <> YouTube 密钥(当前线路) void r.saveProChannelKey()} > {r.saveProKeyLoading ? ( ) : ( 保存密钥 )} {r.youtubeStatus} 地址列表(当前线路) void r.saveProUrlConfig()} disabled={r.saveUrlLoading} > {r.saveUrlLoading ? '…' : '保存地址'} {r.urlStatus} )} {r.isYoutube && !r.multiChannelPro && ( youtube.ini void r.loadConfig('/get_config', r.setYoutubeText, r.setYoutubeStatus)} > 重新读取 { r.setSaveYoutubeLoading(true) void r.saveConfig('/save_config', r.youtubeText, r.setYoutubeStatus, () => r.setSaveYoutubeLoading(false), ) }} > 保存 {r.youtubeStatus} {!r.UI_SHOW_YOUTUBE_GOOGLE_OAUTH && ( Google 授权与拉取密钥请在 PC 端操作。 )} )} {((r.isYoutube && !r.multiChannelPro) || r.isTiktok) && ( 直播地址列表 void r.loadConfig('/get_url_config', r.setUrlText, r.setUrlStatus)} > 重新读取 { r.setSaveUrlLoading(true) void r.saveConfig('/save_url_config', r.urlText, r.setUrlStatus, () => r.setSaveUrlLoading(false), ) }} > 保存 {r.urlStatus} )} {!r.UI_SHOW_YOUTUBE_GOOGLE_OAUTH && r.isObs && ( OBS 推流 srt://live.local:10080/live/obs )} 录制开关 {( [ ['start', '开始录制'], ['stop', '停止录制'], ['restart', '重新开始'], ['status', '刷新状态'], ] as const ).map(([action, label]) => ( void r.runPm2Action(action)} > {label} ))} 运行记录 {r.logText || '—'} )} ) } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: theme.bg }, scroll: { padding: 16, paddingBottom: 32 }, toolbar: { marginBottom: 12 }, label: { fontSize: 12, color: theme.muted, marginBottom: 6 }, pickerWrap: { borderWidth: 1, borderColor: theme.border, borderRadius: 10, overflow: 'hidden', backgroundColor: theme.bgCard, }, picker: { color: theme.text }, proRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 12, paddingVertical: 10, paddingHorizontal: 12, backgroundColor: theme.bgCard, borderRadius: 10, borderWidth: 1, borderColor: theme.border, }, proLabel: { color: theme.text, fontWeight: '600' }, proVal: { color: theme.muted, fontWeight: '600' }, pill: { flexDirection: 'row', alignItems: 'center', alignSelf: 'flex-start', gap: 8, paddingVertical: 8, paddingHorizontal: 12, borderRadius: 999, borderWidth: 1, marginBottom: 14, }, dot: { width: 8, height: 8, borderRadius: 4 }, pillText: { fontSize: 13, fontWeight: '600' }, meta: { padding: 12, borderRadius: 10, backgroundColor: theme.bgElevated, borderWidth: 1, borderColor: theme.border, marginBottom: 12, }, metaLabel: { fontSize: 12, color: theme.muted }, metaName: { fontSize: 15, fontWeight: '600', color: theme.text, marginTop: 4 }, metaFile: { fontSize: 11, fontFamily: 'monospace', color: theme.muted, marginTop: 4 }, card: { padding: 14, borderRadius: 12, backgroundColor: theme.bgCard, borderWidth: 1, borderColor: theme.border, marginBottom: 12, }, cardAccent: { borderColor: 'rgba(91,157,255,0.35)', backgroundColor: 'rgba(91,157,255,0.06)', }, cardTitle: { fontSize: 15, fontWeight: '600', color: theme.text, marginBottom: 10 }, area: { minHeight: 100, borderWidth: 1, borderColor: theme.border, borderRadius: 10, padding: 10, color: theme.text, fontFamily: 'monospace', fontSize: 12, marginBottom: 10, backgroundColor: theme.bg, }, row: { flexDirection: 'row', gap: 10, flexWrap: 'wrap' }, btn: { backgroundColor: '#3d7eff', paddingVertical: 10, paddingHorizontal: 18, borderRadius: 10, minWidth: 100, alignItems: 'center', }, btnDisabled: { opacity: 0.55 }, btnText: { color: '#fff', fontWeight: '700' }, btnSec: { backgroundColor: theme.bgElevated, borderWidth: 1, borderColor: theme.border }, btnSecText: { color: theme.text, fontWeight: '600' }, hint: { color: theme.muted, fontSize: 14, lineHeight: 22 }, hintSmall: { color: theme.muted, fontSize: 12, marginTop: 8 }, err: { color: theme.danger, fontSize: 14 }, mono: { fontFamily: 'monospace', color: theme.accent, fontSize: 13 }, grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, ctrl: { flexGrow: 1, minWidth: '42%', paddingVertical: 14, borderRadius: 10, alignItems: 'center' }, ctrl_start: { backgroundColor: '#0e7a0d' }, ctrl_stop: { backgroundColor: '#d1342c' }, ctrl_restart: { backgroundColor: '#e68600' }, ctrl_status: { backgroundColor: theme.bgElevated, borderWidth: 1, borderColor: theme.border }, ctrlText: { color: '#fff', fontWeight: '700', fontSize: 14 }, logBox: { maxHeight: 280, backgroundColor: '#1e2128', borderRadius: 10, padding: 10 }, log: { fontFamily: 'monospace', fontSize: 11, color: '#d0d4dc', lineHeight: 18 }, })