pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

admin page add m3u8 workers

Pas 81b67dae 09f48bae

+114 -9
+114 -9
src/pages/parts/admin/M3U8TestPart.tsx
··· 6 6 import { Toggle } from "@/components/buttons/Toggle"; 7 7 import { Icon, Icons } from "@/components/Icon"; 8 8 import { Box } from "@/components/layout/Box"; 9 + import { AuthInputBox } from "@/components/text-inputs/AuthInputBox"; 9 10 import { Divider } from "@/components/utils/Divider"; 10 11 import { Heading2 } from "@/components/utils/Text"; 11 12 import { getM3U8ProxyUrls } from "@/utils/proxyUrls"; 13 + 14 + interface M3U8Proxy { 15 + id: string; 16 + url: string; 17 + } 12 18 13 19 export function M3U8ProxyItem(props: { 14 20 name: string; ··· 57 63 } 58 64 59 65 export function M3U8TestPart() { 60 - const m3u8ProxyList = useMemo(() => { 66 + const defaultProxyList = useMemo(() => { 61 67 return getM3U8ProxyUrls().map((v, ind) => ({ 62 68 id: ind.toString(), 63 69 url: v, 64 70 })); 65 71 }, []); 66 72 73 + // Load editable proxy list from localStorage 74 + const [m3u8ProxyList, setM3u8ProxyList] = useState<M3U8Proxy[]>(() => { 75 + const saved = localStorage.getItem("m3u8-proxy-list"); 76 + if (saved) { 77 + try { 78 + return JSON.parse(saved); 79 + } catch { 80 + return defaultProxyList; 81 + } 82 + } 83 + return defaultProxyList; 84 + }); 85 + 67 86 // Load enabled proxies from localStorage 68 87 const [enabledProxies, setEnabledProxies] = useState<Record<string, boolean>>( 69 88 () => { ··· 80 99 }, 81 100 ); 82 101 102 + // Save proxy list to localStorage 103 + useEffect(() => { 104 + localStorage.setItem("m3u8-proxy-list", JSON.stringify(m3u8ProxyList)); 105 + }, [m3u8ProxyList]); 106 + 83 107 // Save enabled proxies to localStorage 84 108 useEffect(() => { 85 109 localStorage.setItem("m3u8-proxy-enabled", JSON.stringify(enabledProxies)); ··· 106 130 setProxyState([]); 107 131 108 132 const activeProxies = m3u8ProxyList.filter( 109 - (proxy) => enabledProxies[proxy.id], 133 + (proxy: M3U8Proxy) => enabledProxies[proxy.id], 110 134 ); 111 - const proxyPromises = activeProxies.map(async (proxy) => { 135 + const proxyPromises = activeProxies.map(async (proxy: M3U8Proxy) => { 112 136 try { 113 137 if (proxy.url.endsWith("/")) { 114 138 updateProxy(proxy.id, { ··· 153 177 })); 154 178 }; 155 179 156 - const allEnabled = m3u8ProxyList.every((proxy) => enabledProxies[proxy.id]); 157 - const noneEnabled = m3u8ProxyList.every((proxy) => !enabledProxies[proxy.id]); 180 + const addProxy = () => { 181 + const newId = Date.now().toString(); 182 + setM3u8ProxyList((prev: M3U8Proxy[]) => [...prev, { id: newId, url: "" }]); 183 + setEnabledProxies((prev) => ({ ...prev, [newId]: true })); 184 + }; 185 + 186 + const changeProxy = (id: string, url: string) => { 187 + setM3u8ProxyList((prev: M3U8Proxy[]) => 188 + prev.map((proxy: M3U8Proxy) => 189 + proxy.id === id ? { ...proxy, url } : proxy, 190 + ), 191 + ); 192 + }; 193 + 194 + const removeProxy = (id: string) => { 195 + setM3u8ProxyList((prev: M3U8Proxy[]) => 196 + prev.filter((proxy: M3U8Proxy) => proxy.id !== id), 197 + ); 198 + setEnabledProxies((prev) => { 199 + const newEnabled = { ...prev }; 200 + delete newEnabled[id]; 201 + return newEnabled; 202 + }); 203 + }; 204 + 205 + const resetProxies = () => { 206 + setM3u8ProxyList(defaultProxyList); 207 + setEnabledProxies( 208 + Object.fromEntries( 209 + defaultProxyList.map((proxy: M3U8Proxy) => [proxy.id, true]), 210 + ), 211 + ); 212 + }; 213 + 214 + const allEnabled = m3u8ProxyList.every( 215 + (proxy: M3U8Proxy) => enabledProxies[proxy.id], 216 + ); 217 + const noneEnabled = m3u8ProxyList.every( 218 + (proxy: M3U8Proxy) => !enabledProxies[proxy.id], 219 + ); 158 220 159 221 const handleToggleAll = () => { 160 222 if (allEnabled) { 161 223 // Disable all 162 224 setEnabledProxies( 163 - Object.fromEntries(m3u8ProxyList.map((proxy) => [proxy.id, false])), 225 + Object.fromEntries( 226 + m3u8ProxyList.map((proxy: M3U8Proxy) => [proxy.id, false]), 227 + ), 164 228 ); 165 229 } else { 166 230 // Enable all 167 231 setEnabledProxies( 168 - Object.fromEntries(m3u8ProxyList.map((proxy) => [proxy.id, true])), 232 + Object.fromEntries( 233 + m3u8ProxyList.map((proxy: M3U8Proxy) => [proxy.id, true]), 234 + ), 169 235 ); 170 236 } 171 237 }; 172 238 173 239 const enabledCount = m3u8ProxyList.filter( 174 - (proxy) => enabledProxies[proxy.id], 240 + (proxy: M3U8Proxy) => enabledProxies[proxy.id], 175 241 ).length; 176 242 177 243 return ( 178 244 <> 245 + <Heading2 className="!mb-0 mt-12">M3U8 Proxy Configuration</Heading2> 246 + <Box> 247 + <p className="text-white font-bold mb-3">M3U8 Proxy URLs</p> 248 + </Box> 249 + 179 250 <Heading2 className="!mb-0 mt-12">M3U8 Proxy tests</Heading2> 180 251 <div className="flex items-center justify-between mb-8 mt-2"> 181 252 <p> ··· 191 262 </Button> 192 263 </div> 193 264 <Box> 194 - {m3u8ProxyList.map((v, i) => { 265 + {m3u8ProxyList.map((v: M3U8Proxy, i: number) => { 195 266 const s = proxyState.find((segment) => segment.id === v.id); 196 267 const name = `M3U8 Proxy ${i + 1}`; 197 268 const enabled = enabledProxies[v.id]; ··· 308 379 Test M3U8 proxies 309 380 </Button> 310 381 )} 382 + </div> 383 + <Divider /> 384 + <div className="my-6 space-y-2"> 385 + {m3u8ProxyList.length === 0 ? ( 386 + <p>No M3U8 proxies configured.</p> 387 + ) : ( 388 + m3u8ProxyList.map((proxy: M3U8Proxy) => ( 389 + <div 390 + key={proxy.id} 391 + className="grid grid-cols-[1fr,auto] items-center gap-2" 392 + > 393 + <AuthInputBox 394 + value={proxy.url} 395 + onChange={(url) => changeProxy(proxy.id, url)} 396 + placeholder="https://" 397 + /> 398 + <button 399 + type="button" 400 + onClick={() => removeProxy(proxy.id)} 401 + className="h-full scale-90 hover:scale-100 rounded-full aspect-square bg-authentication-inputBg hover:bg-authentication-inputBgHover flex justify-center items-center transition-transform duration-200 hover:text-white cursor-pointer" 402 + > 403 + <Icon className="text-xl" icon={Icons.X} /> 404 + </button> 405 + </div> 406 + )) 407 + )} 408 + </div> 409 + <div className="flex gap-2"> 410 + <Button theme="purple" onClick={addProxy}> 411 + Add Proxy 412 + </Button> 413 + <Button theme="secondary" onClick={resetProxies}> 414 + Reset to Default 415 + </Button> 311 416 </div> 312 417 </Box> 313 418 </>