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.

fix uploading captions

Pas eb7659ca 2c9015dd

+81 -23
+74 -23
src/components/player/atoms/settings/CaptionsView.tsx
··· 203 203 const setCaption = usePlayerStore((s) => s.setCaption); 204 204 const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); 205 205 const fileInput = useRef<HTMLInputElement>(null); 206 + const [error, setError] = useState<string | null>(null); 207 + 208 + const handleFileSelect = (file: File) => { 209 + setError(null); 210 + const reader = new FileReader(); 211 + 212 + reader.addEventListener("load", (event) => { 213 + if (!event.target || typeof event.target.result !== "string") { 214 + setError("Failed to read file"); 215 + return; 216 + } 217 + 218 + try { 219 + const converted = convert(event.target.result, "srt"); 220 + setCaption({ 221 + language: "custom", 222 + srtData: converted, 223 + id: "custom-caption", 224 + }); 225 + setCustomSubs(); 226 + } catch (err) { 227 + setError( 228 + err instanceof Error 229 + ? err.message 230 + : "Failed to convert subtitle file", 231 + ); 232 + } 233 + }); 234 + 235 + reader.addEventListener("error", () => { 236 + setError("Failed to read file"); 237 + }); 238 + 239 + reader.readAsText(file, "utf-8"); 240 + }; 206 241 207 242 return ( 208 243 <CaptionOption 209 244 selected={lang === "custom"} 245 + error={error} 210 246 onClick={() => fileInput.current?.click()} 211 247 > 212 248 {t("player.menus.subtitles.customChoice")} ··· 216 252 accept={subtitleTypeList.join(",")} 217 253 type="file" 218 254 onChange={(e) => { 219 - if (!e.target.files) return; 220 - const reader = new FileReader(); 221 - reader.addEventListener("load", (event) => { 222 - if (!event.target || typeof event.target.result !== "string") 223 - return; 224 - const converted = convert(event.target.result, "srt"); 225 - setCaption({ 226 - language: "custom", 227 - srtData: converted, 228 - id: "custom-caption", 229 - }); 230 - setCustomSubs(); 231 - }); 232 - reader.readAsText(e.target.files[0], "utf-8"); 255 + const files = e.target.files; 256 + if (!files || files.length === 0) return; 257 + 258 + const file = files[0]; 259 + const fileExtension = `.${file.name.split(".").pop()?.toLowerCase()}`; 260 + 261 + if (!subtitleTypeList.includes(fileExtension)) { 262 + setError( 263 + `Unsupported file type. Supported: ${subtitleTypeList.join(", ")}`, 264 + ); 265 + e.target.value = ""; // Reset input 266 + return; 267 + } 268 + 269 + handleFileSelect(file); 270 + e.target.value = ""; // Reset input so same file can be selected again 233 271 }} 234 272 /> 235 273 </CaptionOption> ··· 344 382 ); 345 383 const delay = useSubtitleStore((s) => s.delay); 346 384 const appLanguage = useLanguageStore((s) => s.language); 385 + const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); 347 386 348 387 // Get combined caption list 349 388 const captions = useMemo( ··· 418 457 }, [srtData, selectedLanguage, delay, videoTime, selectedCaptionId]); 419 458 420 459 function onDrop(event: DragEvent<HTMLDivElement>) { 460 + event.preventDefault(); 421 461 const files = event.dataTransfer.files; 422 462 const firstFile = files[0]; 423 463 if (!files || !firstFile) return; 424 464 425 - const fileExtension = `.${firstFile.name.split(".").pop()}`; 465 + const fileExtension = `.${firstFile.name.split(".").pop()?.toLowerCase()}`; 426 466 if (!fileExtension || !subtitleTypeList.includes(fileExtension)) { 427 467 return; 428 468 } 429 469 430 470 const reader = new FileReader(); 431 471 reader.addEventListener("load", (e) => { 432 - if (!e.target || typeof e.target.result !== "string") return; 472 + if (!e.target || typeof e.target.result !== "string") { 473 + return; 474 + } 433 475 434 - const converted = convert(e.target.result, "srt"); 476 + try { 477 + const converted = convert(e.target.result, "srt"); 435 478 436 - setCaption({ 437 - language: "custom", 438 - srtData: converted, 439 - id: "custom-caption", 440 - }); 479 + setCaption({ 480 + language: "custom", 481 + srtData: converted, 482 + id: "custom-caption", 483 + }); 484 + setCustomSubs(); 485 + } catch (err) { 486 + // Silently fail on drop - user can use the upload button for better error feedback 487 + } 441 488 }); 442 489 443 - reader.readAsText(firstFile); 490 + reader.addEventListener("error", () => { 491 + // Silently fail on drop - user can use the upload button for better error feedback 492 + }); 493 + 494 + reader.readAsText(firstFile, "utf-8"); 444 495 } 445 496 446 497 return (
+7
src/components/player/hooks/useCaptions.ts
··· 180 180 useEffect(() => { 181 181 if (!selectedCaption) return; 182 182 183 + // Skip validation for custom/pasted captions that aren't in the caption list 184 + const isCustomCaption = 185 + selectedCaption.id === "custom-caption" || 186 + selectedCaption.id === "pasted-caption"; 187 + 188 + if (isCustomCaption) return; 189 + 183 190 const isSelectedCaptionStillAvailable = captions.some( 184 191 (caption) => caption.id === selectedCaption.id, 185 192 );