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.

update subtitle delay slider with helper text

Pas 29687ff6 f03fbdfc

+171 -3
+3 -1
src/assets/locales/en.json
··· 711 711 "empty": "There are no provided subtitles for this.", 712 712 "notFound": "None of the available options match your query", 713 713 "useNativeSubtitles": "Use native video subtitles", 714 - "useNativeSubtitlesDescription": "May fix subtitles when casting and in PiP" 714 + "useNativeSubtitlesDescription": "May fix subtitles when casting and in PiP", 715 + "delayLate": "Heard audio", 716 + "delayEarly": "Saw caption" 715 717 }, 716 718 "watchparty": { 717 719 "watchpartyItem": "Watch Party",
+168 -2
src/components/player/atoms/settings/CaptionSettingsView.tsx
··· 39 39 ); 40 40 } 41 41 42 + export function CaptionDelay(props: { 43 + textTransformer?: (s: string) => string; 44 + value: number; 45 + onChange?: (val: number) => void; 46 + max: number; 47 + label: string; 48 + min: number; 49 + decimalsAllowed?: number; 50 + }) { 51 + const { t } = useTranslation(); 52 + 53 + const inputRef = useRef<HTMLInputElement | null>(null); 54 + const ref = useRef<HTMLDivElement>(null); 55 + 56 + const currentPercentage = (props.value - props.min) / (props.max - props.min); 57 + const commit = useCallback( 58 + (percentage: number) => { 59 + const range = props.max - props.min; 60 + const newPercentage = Math.min(Math.max(percentage, 0), 1); 61 + props.onChange?.(props.min + range * newPercentage); 62 + }, 63 + [props], 64 + ); 65 + 66 + const { dragging, dragPercentage, dragMouseDown } = useProgressBar( 67 + ref, 68 + commit, 69 + true, 70 + ); 71 + 72 + const [isFocused, setIsFocused] = useState(false); 73 + const [inputValue, setInputValue] = useState(""); 74 + 75 + useEffect(() => { 76 + function listener(e: KeyboardEvent) { 77 + if (e.key === "Enter" && isFocused) { 78 + inputRef.current?.blur(); 79 + } 80 + } 81 + window.addEventListener("keydown", listener); 82 + return () => { 83 + window.removeEventListener("keydown", listener); 84 + }; 85 + }, [isFocused]); 86 + 87 + const inputClasses = 88 + "tabbable py-1 bg-video-context-inputBg rounded text-white cursor-text text-center px-4 w-24 h-12"; 89 + const textTransformer = props.textTransformer ?? ((s) => s); 90 + 91 + return ( 92 + <div> 93 + <Menu.FieldTitle>{props.label}</Menu.FieldTitle> 94 + <div className="space-y-3"> 95 + {/* Slider */} 96 + <div ref={ref}> 97 + <div 98 + className="group/progress w-full h-8 flex items-center cursor-pointer" 99 + onMouseDown={dragMouseDown} 100 + onTouchStart={dragMouseDown} 101 + > 102 + <div 103 + dir="ltr" 104 + className={[ 105 + "relative w-full h-1 bg-video-context-slider bg-opacity-25 rounded-full transition-[height] duration-100 group-hover/progress:h-1.5", 106 + dragging ? "!h-1.5" : "", 107 + ].join(" ")} 108 + > 109 + {/* Actual progress bar */} 110 + <div 111 + className="absolute top-0 left-0 h-full rounded-full bg-video-context-sliderFilled flex justify-end items-center" 112 + style={{ 113 + width: `${ 114 + Math.max( 115 + 0, 116 + Math.min( 117 + 1, 118 + dragging ? dragPercentage / 100 : currentPercentage, 119 + ), 120 + ) * 100 121 + }%`, 122 + }} 123 + > 124 + <div 125 + className={[ 126 + "w-[1rem] min-w-[1rem] h-[1rem] border-[4px] border-video-context-sliderFilled rounded-full transform translate-x-1/2 bg-white transition-[transform] duration-100", 127 + ].join(" ")} 128 + /> 129 + </div> 130 + </div> 131 + </div> 132 + </div> 133 + 134 + {/* Control buttons and value display */} 135 + <div className="flex items-center gap-2"> 136 + {/* Left arrow button - full width */} 137 + <button 138 + type="button" 139 + onClick={() => 140 + props.onChange?.( 141 + props.value - 1 / 10 ** (props.decimalsAllowed ?? 0), 142 + ) 143 + } 144 + className="flex-1 flex-col tabbable py-2 h-12 hover:text-white transition-colors duration-100 flex justify-center items-center hover:bg-video-context-buttonOverInputHover rounded bg-video-context-inputBg" 145 + > 146 + <Icon icon={Icons.CHEVRON_LEFT} /> 147 + <span className="text-xs"> 148 + {t("player.menus.subtitles.delayLate")} 149 + </span> 150 + </button> 151 + 152 + {/* Value display/input */} 153 + {isFocused ? ( 154 + <input 155 + className={inputClasses} 156 + value={inputValue} 157 + autoFocus 158 + onFocus={(e) => { 159 + (e.target as HTMLInputElement).select(); 160 + }} 161 + onBlur={(e) => { 162 + setIsFocused(false); 163 + const num = Number((e.target as HTMLInputElement).value); 164 + if (!Number.isNaN(num)) 165 + props.onChange?.( 166 + (props.decimalsAllowed ?? 0) === 0 ? Math.round(num) : num, 167 + ); 168 + }} 169 + ref={inputRef} 170 + onChange={(e) => 171 + setInputValue((e.target as HTMLInputElement).value) 172 + } 173 + /> 174 + ) : ( 175 + <button 176 + className={inputClasses} 177 + type="button" 178 + tabIndex={0} 179 + onClick={() => { 180 + setInputValue(props.value.toFixed(props.decimalsAllowed ?? 0)); 181 + setIsFocused(true); 182 + }} 183 + > 184 + {textTransformer(props.value.toFixed(props.decimalsAllowed ?? 0))} 185 + </button> 186 + )} 187 + 188 + {/* Right arrow button - full width */} 189 + <button 190 + type="button" 191 + onClick={() => 192 + props.onChange?.( 193 + props.value + 1 / 10 ** (props.decimalsAllowed ?? 0), 194 + ) 195 + } 196 + className="flex-1 flex-col tabbable py-2 h-12 hover:text-white transition-colors duration-100 flex justify-center items-center hover:bg-video-context-buttonOverInputHover rounded bg-video-context-inputBg" 197 + > 198 + <Icon icon={Icons.CHEVRON_RIGHT} /> 199 + <span className="text-xs"> 200 + {t("player.menus.subtitles.delayEarly")} 201 + </span> 202 + </button> 203 + </div> 204 + </div> 205 + </div> 206 + ); 207 + } 208 + 42 209 export function CaptionSetting(props: { 43 210 textTransformer?: (s: string) => string; 44 211 value: number; ··· 294 461 <span className="text-xs text-type-secondary"> 295 462 {t("player.menus.subtitles.useNativeSubtitlesDescription")} 296 463 </span> 297 - <CaptionSetting 464 + <CaptionDelay 298 465 label={t("player.menus.subtitles.settings.delay")} 299 466 max={20} 300 467 min={-20} ··· 302 469 value={delay} 303 470 textTransformer={(s) => `${s}s`} 304 471 decimalsAllowed={1} 305 - controlButtons 306 472 /> 307 473 <div className="flex justify-between items-center"> 308 474 <Menu.FieldTitle>