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.

add subtitle track option

Pas dd1af01c 3428abdb

+227 -176
+3 -1
src/assets/locales/en.json
··· 528 528 "dropSubtitleFile": "Drop subtitle file here! >_<", 529 529 "scrapeButton": "Scrape subtitles", 530 530 "empty": "There are no provided subtitles for this.", 531 - "notFound": "None of the available options match your query" 531 + "notFound": "None of the available options match your query", 532 + "useNativeSubtitles": "Use native video subtitles", 533 + "useNativeSubtitlesDescription": "May fix subtitles when casting and in PiP" 532 534 }, 533 535 "watchparty": { 534 536 "watchpartyItem": "Watch Party",
+218 -175
src/components/player/atoms/settings/CaptionSettingsView.tsx
··· 9 9 import { Menu } from "@/components/player/internals/ContextMenu"; 10 10 import { useOverlayRouter } from "@/hooks/useOverlayRouter"; 11 11 import { useProgressBar } from "@/hooks/useProgressBar"; 12 + import { usePlayerStore } from "@/stores/player/store"; 12 13 import { SubtitleStyling, useSubtitleStore } from "@/stores/subtitles"; 13 14 14 15 export function ColorOption(props: { ··· 234 235 const setOverrideCasing = subtitleStore.setOverrideCasing; 235 236 const setDelay = subtitleStore.setDelay; 236 237 const updateStyling = subtitleStore.updateStyling; 238 + const setCaptionAsTrack = usePlayerStore((s) => s.setCaptionAsTrack); 239 + const captionAsTrack = usePlayerStore((s) => s.caption.asTrack); 237 240 238 241 useEffect(() => { 239 242 subtitleStore.updateStyling(styling); ··· 264 267 {t("player.menus.subtitles.settings.backlink")} 265 268 </Menu.BackLink> 266 269 <Menu.Section className="space-y-6 pb-5"> 267 - <CaptionSetting 268 - label={t("player.menus.subtitles.settings.delay")} 269 - max={20} 270 - min={-20} 271 - onChange={(v) => setDelay(v)} 272 - value={delay} 273 - textTransformer={(s) => `${s}s`} 274 - decimalsAllowed={1} 275 - controlButtons 276 - /> 277 - <div className="flex justify-between items-center"> 278 - <Menu.FieldTitle> 279 - {t("player.menus.subtitles.settings.fixCapitals")} 280 - </Menu.FieldTitle> 281 - <div className="flex justify-center items-center"> 282 - <Toggle 283 - enabled={overrideCasing} 284 - onClick={() => setOverrideCasing(!overrideCasing)} 270 + {!captionAsTrack ? ( 271 + <> 272 + <div className="flex justify-between items-center"> 273 + <Menu.FieldTitle> 274 + {t("player.menus.subtitles.useNativeSubtitles")} 275 + </Menu.FieldTitle> 276 + <div className="flex justify-center items-center"> 277 + <Toggle 278 + enabled={captionAsTrack} 279 + onClick={() => setCaptionAsTrack(!captionAsTrack)} 280 + /> 281 + </div> 282 + </div> 283 + <span className="text-xs text-type-secondary"> 284 + {t("player.menus.subtitles.useNativeSubtitlesDescription")} 285 + </span> 286 + <CaptionSetting 287 + label={t("player.menus.subtitles.settings.delay")} 288 + max={20} 289 + min={-20} 290 + onChange={(v) => setDelay(v)} 291 + value={delay} 292 + textTransformer={(s) => `${s}s`} 293 + decimalsAllowed={1} 294 + controlButtons 285 295 /> 286 - </div> 287 - </div> 288 - <Menu.Divider /> 289 - <CaptionSetting 290 - label={t("settings.subtitles.backgroundLabel")} 291 - max={100} 292 - min={0} 293 - onChange={(v) => 294 - handleStylingChange({ ...styling, backgroundOpacity: v / 100 }) 295 - } 296 - value={styling.backgroundOpacity * 100} 297 - textTransformer={(s) => `${s}%`} 298 - /> 299 - <CaptionSetting 300 - label={t("settings.subtitles.backgroundBlurLabel")} 301 - max={100} 302 - min={0} 303 - onChange={(v) => 304 - handleStylingChange({ ...styling, backgroundBlur: v / 100 }) 305 - } 306 - value={styling.backgroundBlur * 100} 307 - textTransformer={(s) => `${s}%`} 308 - /> 309 - <CaptionSetting 310 - label={t("settings.subtitles.textSizeLabel")} 311 - max={200} 312 - min={1} 313 - textTransformer={(s) => `${s}%`} 314 - onChange={(v) => handleStylingChange({ ...styling, size: v / 100 })} 315 - value={styling.size * 100} 316 - /> 317 - <div className="flex justify-between items-center"> 318 - <Menu.FieldTitle> 319 - {t("settings.subtitles.textStyle.title") || "Font Style"} 320 - </Menu.FieldTitle> 321 - <div className="w-64"> 322 - <Dropdown 323 - options={[ 324 - { 325 - id: "default", 326 - name: t("settings.subtitles.textStyle.default"), 327 - }, 328 - { 329 - id: "raised", 330 - name: t("settings.subtitles.textStyle.raised"), 331 - }, 332 - { 333 - id: "depressed", 334 - name: t("settings.subtitles.textStyle.depressed"), 335 - }, 336 - { 337 - id: "uniform", 338 - name: t("settings.subtitles.textStyle.uniform"), 339 - }, 340 - { 341 - id: "dropShadow", 342 - name: t("settings.subtitles.textStyle.dropShadow"), 343 - }, 344 - ]} 345 - selectedItem={{ 346 - id: styling.fontStyle, 347 - name: 348 - t(`settings.subtitles.textStyle.${styling.fontStyle}`) || 349 - styling.fontStyle, 350 - }} 351 - setSelectedItem={(item) => 352 - handleStylingChange({ 353 - ...styling, 354 - fontStyle: item.id, 355 - }) 296 + <div className="flex justify-between items-center"> 297 + <Menu.FieldTitle> 298 + {t("player.menus.subtitles.settings.fixCapitals")} 299 + </Menu.FieldTitle> 300 + <div className="flex justify-center items-center"> 301 + <Toggle 302 + enabled={overrideCasing} 303 + onClick={() => setOverrideCasing(!overrideCasing)} 304 + /> 305 + </div> 306 + </div> 307 + <Menu.Divider /> 308 + <CaptionSetting 309 + label={t("settings.subtitles.backgroundLabel")} 310 + max={100} 311 + min={0} 312 + onChange={(v) => 313 + handleStylingChange({ ...styling, backgroundOpacity: v / 100 }) 356 314 } 315 + value={styling.backgroundOpacity * 100} 316 + textTransformer={(s) => `${s}%`} 357 317 /> 358 - </div> 359 - </div> 360 - <div className="flex justify-between items-center"> 361 - <Menu.FieldTitle> 362 - {t("settings.subtitles.textBoldLabel")} 363 - </Menu.FieldTitle> 364 - <div className="flex justify-center items-center"> 365 - <Toggle 366 - enabled={styling.bold} 367 - onClick={() => 368 - handleStylingChange({ ...styling, bold: !styling.bold }) 318 + <CaptionSetting 319 + label={t("settings.subtitles.backgroundBlurLabel")} 320 + max={100} 321 + min={0} 322 + onChange={(v) => 323 + handleStylingChange({ ...styling, backgroundBlur: v / 100 }) 369 324 } 325 + value={styling.backgroundBlur * 100} 326 + textTransformer={(s) => `${s}%`} 370 327 /> 371 - </div> 372 - </div> 373 - <div className="flex justify-between items-center"> 374 - <Menu.FieldTitle> 375 - {t("settings.subtitles.colorLabel")} 376 - </Menu.FieldTitle> 377 - <div className="flex justify-center items-center space-x-2"> 378 - {colors.map((color) => ( 379 - <ColorOption 380 - key={color} 381 - color={color} 382 - active={styling.color === color} 383 - onClick={() => handleStylingChange({ ...styling, color })} 384 - /> 385 - ))} 386 - <div className="relative inline-block"> 387 - <input 388 - type="color" 389 - value={styling.color} 390 - onChange={(e) => { 391 - const color = e.target.value; 392 - handleStylingChange({ ...styling, color }); 393 - }} 394 - className="absolute opacity-0 cursor-pointer w-10 h-10" 395 - /> 396 - <div style={{ color: styling.color }}> 397 - <Icon icon={Icons.BRUSH} className="text-2xl" /> 328 + <CaptionSetting 329 + label={t("settings.subtitles.textSizeLabel")} 330 + max={200} 331 + min={1} 332 + textTransformer={(s) => `${s}%`} 333 + onChange={(v) => 334 + handleStylingChange({ ...styling, size: v / 100 }) 335 + } 336 + value={styling.size * 100} 337 + /> 338 + <div className="flex justify-between items-center"> 339 + <Menu.FieldTitle> 340 + {t("settings.subtitles.textStyle.title") || "Font Style"} 341 + </Menu.FieldTitle> 342 + <div className="w-64"> 343 + <Dropdown 344 + options={[ 345 + { 346 + id: "default", 347 + name: t("settings.subtitles.textStyle.default"), 348 + }, 349 + { 350 + id: "raised", 351 + name: t("settings.subtitles.textStyle.raised"), 352 + }, 353 + { 354 + id: "depressed", 355 + name: t("settings.subtitles.textStyle.depressed"), 356 + }, 357 + { 358 + id: "uniform", 359 + name: t("settings.subtitles.textStyle.uniform"), 360 + }, 361 + { 362 + id: "dropShadow", 363 + name: t("settings.subtitles.textStyle.dropShadow"), 364 + }, 365 + ]} 366 + selectedItem={{ 367 + id: styling.fontStyle, 368 + name: 369 + t(`settings.subtitles.textStyle.${styling.fontStyle}`) || 370 + styling.fontStyle, 371 + }} 372 + setSelectedItem={(item) => 373 + handleStylingChange({ 374 + ...styling, 375 + fontStyle: item.id, 376 + }) 377 + } 378 + /> 398 379 </div> 399 380 </div> 400 - </div> 401 - </div> 402 - <div className="flex justify-between items-center"> 403 - <Menu.FieldTitle> 404 - {t("settings.subtitles.verticalPositionLabel")} 405 - </Menu.FieldTitle> 406 - <div className="flex justify-center items-center space-x-2"> 407 - <button 408 - type="button" 409 - className={classNames( 410 - "px-3 py-1 rounded transition-colors duration-100", 411 - styling.verticalPosition === 3 412 - ? "bg-video-context-buttonFocus" 413 - : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 414 - )} 415 - onClick={() => 416 - handleStylingChange({ 417 - ...styling, 418 - verticalPosition: 3, 419 - }) 420 - } 421 - > 422 - {t("settings.subtitles.default")} 423 - </button> 424 - <button 425 - type="button" 426 - className={classNames( 427 - "px-3 py-1 rounded transition-colors duration-100", 428 - styling.verticalPosition === 1 429 - ? "bg-video-context-buttonFocus" 430 - : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 431 - )} 432 - onClick={() => 433 - handleStylingChange({ 434 - ...styling, 435 - verticalPosition: 1, 436 - }) 437 - } 381 + <div className="flex justify-between items-center"> 382 + <Menu.FieldTitle> 383 + {t("settings.subtitles.textBoldLabel")} 384 + </Menu.FieldTitle> 385 + <div className="flex justify-center items-center"> 386 + <Toggle 387 + enabled={styling.bold} 388 + onClick={() => 389 + handleStylingChange({ ...styling, bold: !styling.bold }) 390 + } 391 + /> 392 + </div> 393 + </div> 394 + <div className="flex justify-between items-center"> 395 + <Menu.FieldTitle> 396 + {t("settings.subtitles.colorLabel")} 397 + </Menu.FieldTitle> 398 + <div className="flex justify-center items-center space-x-2"> 399 + {colors.map((color) => ( 400 + <ColorOption 401 + key={color} 402 + color={color} 403 + active={styling.color === color} 404 + onClick={() => handleStylingChange({ ...styling, color })} 405 + /> 406 + ))} 407 + <div className="relative inline-block"> 408 + <input 409 + type="color" 410 + value={styling.color} 411 + onChange={(e) => { 412 + const color = e.target.value; 413 + handleStylingChange({ ...styling, color }); 414 + }} 415 + className="absolute opacity-0 cursor-pointer w-10 h-10" 416 + /> 417 + <div style={{ color: styling.color }}> 418 + <Icon icon={Icons.BRUSH} className="text-2xl" /> 419 + </div> 420 + </div> 421 + </div> 422 + </div> 423 + <div className="flex justify-between items-center"> 424 + <Menu.FieldTitle> 425 + {t("settings.subtitles.verticalPositionLabel")} 426 + </Menu.FieldTitle> 427 + <div className="flex justify-center items-center space-x-2"> 428 + <button 429 + type="button" 430 + className={classNames( 431 + "px-3 py-1 rounded transition-colors duration-100", 432 + styling.verticalPosition === 3 433 + ? "bg-video-context-buttonFocus" 434 + : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 435 + )} 436 + onClick={() => 437 + handleStylingChange({ 438 + ...styling, 439 + verticalPosition: 3, 440 + }) 441 + } 442 + > 443 + {t("settings.subtitles.default")} 444 + </button> 445 + <button 446 + type="button" 447 + className={classNames( 448 + "px-3 py-1 rounded transition-colors duration-100", 449 + styling.verticalPosition === 1 450 + ? "bg-video-context-buttonFocus" 451 + : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 452 + )} 453 + onClick={() => 454 + handleStylingChange({ 455 + ...styling, 456 + verticalPosition: 1, 457 + }) 458 + } 459 + > 460 + {t("settings.subtitles.low")} 461 + </button> 462 + </div> 463 + </div> 464 + <Button 465 + className="w-full md:w-auto" 466 + theme="secondary" 467 + onClick={resetSubStyling} 438 468 > 439 - {t("settings.subtitles.low")} 440 - </button> 441 - </div> 442 - </div> 443 - <Button 444 - className="w-full md:w-auto" 445 - theme="secondary" 446 - onClick={resetSubStyling} 447 - > 448 - {t("settings.reset")} 449 - </Button> 469 + {t("settings.reset")} 470 + </Button> 471 + </> 472 + ) : ( 473 + <> 474 + <div className="flex justify-between items-center"> 475 + <Menu.FieldTitle> 476 + {t( 477 + "player.menus.subtitles.settings.useNativeSubtitles", 478 + "Use native video subtitles", 479 + )} 480 + </Menu.FieldTitle> 481 + <div className="flex justify-center items-center"> 482 + <Toggle 483 + enabled={captionAsTrack} 484 + onClick={() => setCaptionAsTrack(!captionAsTrack)} 485 + /> 486 + </div> 487 + </div> 488 + <span className="text-xs text-type-secondary"> 489 + {t("player.menus.subtitles.useNativeSubtitlesDescription")} 490 + </span> 491 + </> 492 + )} 450 493 </Menu.Section> 451 494 </> 452 495 );
+6
src/stores/player/slices/source.ts
··· 90 90 setSourceId(id: string | null): void; 91 91 enableAutomaticQuality(): void; 92 92 redisplaySource(startAt: number): void; 93 + setCaptionAsTrack(asTrack: boolean): void; 93 94 } 94 95 95 96 export function metaToScrapeMedia(meta: PlayerMeta): ScrapeMedia { ··· 221 222 enableAutomaticQuality() { 222 223 const store = get(); 223 224 store.display?.changeQuality(true, null); 225 + }, 226 + setCaptionAsTrack(asTrack: boolean) { 227 + set((s) => { 228 + s.caption.asTrack = asTrack; 229 + }); 224 230 }, 225 231 });