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.

make native subtitles a proper setting

Pas 0edb4ece 30f0d4ac

+252 -181
+2
src/backend/accounts/settings.ts
··· 23 23 enableSourceOrder?: boolean; 24 24 proxyTmdb?: boolean; 25 25 enableLowPerformanceMode?: boolean; 26 + enableNativeSubtitles?: boolean; 26 27 } 27 28 28 29 export interface SettingsResponse { ··· 44 45 enableSourceOrder?: boolean; 45 46 proxyTmdb?: boolean; 46 47 enableLowPerformanceMode?: boolean; 48 + enableNativeSubtitles?: boolean; 47 49 } 48 50 49 51 export function updateSettings(
+21 -6
src/components/player/atoms/settings/CaptionSettingsView.tsx
··· 10 10 import { useOverlayRouter } from "@/hooks/useOverlayRouter"; 11 11 import { useProgressBar } from "@/hooks/useProgressBar"; 12 12 import { usePlayerStore } from "@/stores/player/store"; 13 + import { usePreferencesStore } from "@/stores/preferences"; 13 14 import { SubtitleStyling, useSubtitleStore } from "@/stores/subtitles"; 14 15 15 16 export function ColorOption(props: { ··· 229 230 const { t } = useTranslation(); 230 231 const router = useOverlayRouter(id); 231 232 const subtitleStore = useSubtitleStore(); 233 + const preferencesStore = usePreferencesStore(); 232 234 const styling = subtitleStore.styling; 233 235 const overrideCasing = subtitleStore.overrideCasing; 234 236 const delay = subtitleStore.delay; ··· 236 238 const setDelay = subtitleStore.setDelay; 237 239 const updateStyling = subtitleStore.updateStyling; 238 240 const setCaptionAsTrack = usePlayerStore((s) => s.setCaptionAsTrack); 239 - const captionAsTrack = usePlayerStore((s) => s.caption.asTrack); 241 + const enableNativeSubtitles = preferencesStore.enableNativeSubtitles; 240 242 241 243 useEffect(() => { 242 244 subtitleStore.updateStyling(styling); 243 245 }, [styling, subtitleStore]); 244 246 247 + // Sync preferences with player store 248 + useEffect(() => { 249 + setCaptionAsTrack(enableNativeSubtitles); 250 + }, [enableNativeSubtitles, setCaptionAsTrack]); 251 + 245 252 const handleStylingChange = (newStyling: SubtitleStyling) => { 246 253 updateStyling(newStyling); 247 254 }; ··· 267 274 {t("player.menus.subtitles.settings.backlink")} 268 275 </Menu.BackLink> 269 276 <Menu.Section className="space-y-6 pb-5"> 270 - {!captionAsTrack ? ( 277 + {!enableNativeSubtitles ? ( 271 278 <> 272 279 <div className="flex justify-between items-center"> 273 280 <Menu.FieldTitle> ··· 275 282 </Menu.FieldTitle> 276 283 <div className="flex justify-center items-center"> 277 284 <Toggle 278 - enabled={captionAsTrack} 279 - onClick={() => setCaptionAsTrack(!captionAsTrack)} 285 + enabled={enableNativeSubtitles} 286 + onClick={() => 287 + preferencesStore.setEnableNativeSubtitles( 288 + !enableNativeSubtitles, 289 + ) 290 + } 280 291 /> 281 292 </div> 282 293 </div> ··· 478 489 </Menu.FieldTitle> 479 490 <div className="flex justify-center items-center"> 480 491 <Toggle 481 - enabled={captionAsTrack} 482 - onClick={() => setCaptionAsTrack(!captionAsTrack)} 492 + enabled={enableNativeSubtitles} 493 + onClick={() => 494 + preferencesStore.setEnableNativeSubtitles( 495 + !enableNativeSubtitles, 496 + ) 497 + } 483 498 /> 484 499 </div> 485 500 </div>
+8
src/hooks/auth/useAuthData.ts
··· 57 57 const setEnableLowPerformanceMode = usePreferencesStore( 58 58 (s) => s.setEnableLowPerformanceMode, 59 59 ); 60 + const setEnableNativeSubtitles = usePreferencesStore( 61 + (s) => s.setEnableNativeSubtitles, 62 + ); 60 63 61 64 const login = useCallback( 62 65 async ( ··· 164 167 if (settings.enableLowPerformanceMode !== undefined) { 165 168 setEnableLowPerformanceMode(settings.enableLowPerformanceMode); 166 169 } 170 + 171 + if (settings.enableNativeSubtitles !== undefined) { 172 + setEnableNativeSubtitles(settings.enableNativeSubtitles); 173 + } 167 174 }, 168 175 [ 169 176 replaceBookmarks, ··· 185 192 setProxyTmdb, 186 193 setFebboxKey, 187 194 setEnableLowPerformanceMode, 195 + setEnableNativeSubtitles, 188 196 ], 189 197 ); 190 198
+2
src/pages/parts/auth/VerifyPassphrasePart.tsx
··· 54 54 enableSourceOrder: store.enableSourceOrder, 55 55 proxyTmdb: store.proxyTmdb, 56 56 febboxKey: store.febboxKey, 57 + enableLowPerformanceMode: store.enableLowPerformanceMode, 58 + enableNativeSubtitles: store.enableNativeSubtitles, 57 59 })); 58 60 59 61 const backendUrl = useBackendUrl();
+211 -175
src/pages/parts/settings/CaptionsPart.tsx
··· 16 16 import { CaptionCue } from "@/components/player/Player"; 17 17 import { Heading1 } from "@/components/utils/Text"; 18 18 import { Transition } from "@/components/utils/Transition"; 19 + import { usePlayerStore } from "@/stores/player/store"; 20 + import { usePreferencesStore } from "@/stores/preferences"; 19 21 import { SubtitleStyling, useSubtitleStore } from "@/stores/subtitles"; 20 22 21 23 export function CaptionPreview(props: { ··· 86 88 const [fullscreenPreview, setFullscreenPreview] = useState(false); 87 89 88 90 const subtitleStore = useSubtitleStore(); 91 + const preferencesStore = usePreferencesStore(); 92 + const setCaptionAsTrack = usePlayerStore((s) => s.setCaptionAsTrack); 93 + const enableNativeSubtitles = preferencesStore.enableNativeSubtitles; 89 94 90 95 useEffect(() => { 91 96 subtitleStore.updateStyling(props.styling); 92 97 }, [props.styling, subtitleStore, subtitleStore.updateStyling]); 98 + 99 + // Sync preferences with player store 100 + useEffect(() => { 101 + setCaptionAsTrack(enableNativeSubtitles); 102 + }, [enableNativeSubtitles, setCaptionAsTrack]); 93 103 94 104 const handleStylingChange = (newStyling: SubtitleStyling) => { 95 105 props.setStyling(newStyling); ··· 114 124 <Heading1 border>{t("settings.subtitles.title")}</Heading1> 115 125 <div className="grid md:grid-cols-[1fr,356px] gap-8"> 116 126 <div className="space-y-6"> 117 - <CaptionSetting 118 - label={t("settings.subtitles.backgroundLabel")} 119 - max={100} 120 - min={0} 121 - onChange={(v) => 122 - handleStylingChange({ 123 - ...props.styling, 124 - backgroundOpacity: v / 100, 125 - }) 126 - } 127 - value={props.styling.backgroundOpacity * 100} 128 - textTransformer={(s) => `${s}%`} 129 - /> 130 - <CaptionSetting 131 - label={t("settings.subtitles.backgroundBlurLabel")} 132 - max={100} 133 - min={0} 134 - onChange={(v) => 135 - handleStylingChange({ 136 - ...props.styling, 137 - backgroundBlur: v / 100, 138 - }) 139 - } 140 - value={props.styling.backgroundBlur * 100} 141 - textTransformer={(s) => `${s}%`} 142 - /> 143 - <CaptionSetting 144 - label={t("settings.subtitles.textSizeLabel")} 145 - max={200} 146 - min={1} 147 - textTransformer={(s) => `${s}%`} 148 - onChange={(v) => 149 - handleStylingChange({ 150 - ...props.styling, 151 - size: v / 100, 152 - }) 153 - } 154 - value={props.styling.size * 100} 155 - /> 156 127 <div className="flex justify-between items-center"> 157 128 <Menu.FieldTitle> 158 - {t("settings.subtitles.textStyle.title")} 129 + {t("player.menus.subtitles.useNativeSubtitles")} 159 130 </Menu.FieldTitle> 160 - <div className="w-30"> 161 - <Dropdown 162 - options={[ 163 - { 164 - id: "default", 165 - name: t("settings.subtitles.textStyle.default"), 166 - }, 167 - { 168 - id: "raised", 169 - name: t("settings.subtitles.textStyle.raised"), 170 - }, 171 - { 172 - id: "depressed", 173 - name: t("settings.subtitles.textStyle.depressed"), 174 - }, 175 - { 176 - id: "uniform", 177 - name: t("settings.subtitles.textStyle.uniform"), 178 - }, 179 - { 180 - id: "dropShadow", 181 - name: t("settings.subtitles.textStyle.dropShadow"), 182 - }, 183 - ]} 184 - selectedItem={{ 185 - id: props.styling.fontStyle, 186 - name: 187 - t( 188 - `settings.subtitles.textStyle.${props.styling.fontStyle}`, 189 - ) || props.styling.fontStyle, 190 - }} 191 - setSelectedItem={(item) => 192 - handleStylingChange({ 193 - ...props.styling, 194 - fontStyle: item.id, 195 - }) 131 + <div className="flex justify-center items-center"> 132 + <Toggle 133 + enabled={enableNativeSubtitles} 134 + onClick={() => 135 + preferencesStore.setEnableNativeSubtitles( 136 + !enableNativeSubtitles, 137 + ) 196 138 } 197 139 /> 198 140 </div> 199 141 </div> 200 - <div className="flex justify-between items-center"> 201 - <Menu.FieldTitle> 202 - {t("settings.subtitles.textBoldLabel")} 203 - </Menu.FieldTitle> 204 - <div className="flex justify-center items-center"> 205 - <Toggle 206 - enabled={props.styling.bold} 207 - onClick={() => 142 + <span className="text-xs text-type-secondary"> 143 + {t("player.menus.subtitles.useNativeSubtitlesDescription")} 144 + </span> 145 + {!enableNativeSubtitles && ( 146 + <> 147 + <CaptionSetting 148 + label={t("settings.subtitles.backgroundLabel")} 149 + max={100} 150 + min={0} 151 + onChange={(v) => 208 152 handleStylingChange({ 209 153 ...props.styling, 210 - bold: !props.styling.bold, 154 + backgroundOpacity: v / 100, 211 155 }) 212 156 } 157 + value={props.styling.backgroundOpacity * 100} 158 + textTransformer={(s) => `${s}%`} 213 159 /> 214 - </div> 215 - </div> 216 - <div className="flex justify-between items-center"> 217 - <Menu.FieldTitle> 218 - {t("settings.subtitles.colorLabel")} 219 - </Menu.FieldTitle> 220 - <div className="flex justify-center items-center space-x-2"> 221 - {colors.map((v) => ( 222 - <ColorOption 223 - onClick={() => 224 - handleStylingChange({ 225 - ...props.styling, 226 - color: v, 227 - }) 228 - } 229 - color={v} 230 - active={props.styling.color === v} 231 - key={v} 232 - /> 233 - ))} 234 - <div className="relative"> 235 - <input 236 - type="color" 237 - value={props.styling.color} 238 - onChange={(e) => { 239 - const color = e.target.value; 240 - handleStylingChange({ ...props.styling, color }); 241 - subtitleStore.updateStyling({ 242 - ...props.styling, 243 - color, 244 - }); 245 - }} 246 - className="absolute opacity-0 cursor-pointer w-8 h-8" 247 - /> 248 - <div style={{ color: props.styling.color }}> 249 - <Icon icon={Icons.BRUSH} className="text-2xl" /> 250 - </div> 251 - </div> 252 - </div> 253 - </div> 254 - <div className="flex justify-between items-center"> 255 - <Menu.FieldTitle> 256 - {t("settings.subtitles.verticalPositionLabel")} 257 - </Menu.FieldTitle> 258 - <div className="flex justify-center items-center space-x-2"> 259 - <button 260 - type="button" 261 - className={classNames( 262 - "px-3 py-1 rounded transition-colors duration-100", 263 - props.styling.verticalPosition === 3 264 - ? "bg-video-context-buttonFocus" 265 - : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 266 - )} 267 - onClick={() => 160 + <CaptionSetting 161 + label={t("settings.subtitles.backgroundBlurLabel")} 162 + max={100} 163 + min={0} 164 + onChange={(v) => 268 165 handleStylingChange({ 269 166 ...props.styling, 270 - verticalPosition: 3, 167 + backgroundBlur: v / 100, 271 168 }) 272 169 } 273 - > 274 - {t("settings.subtitles.default")} 275 - </button> 276 - <button 277 - type="button" 278 - className={classNames( 279 - "px-3 py-1 rounded transition-colors duration-100", 280 - props.styling.verticalPosition === 1 281 - ? "bg-video-context-buttonFocus" 282 - : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 283 - )} 284 - onClick={() => 170 + value={props.styling.backgroundBlur * 100} 171 + textTransformer={(s) => `${s}%`} 172 + /> 173 + <CaptionSetting 174 + label={t("settings.subtitles.textSizeLabel")} 175 + max={200} 176 + min={1} 177 + textTransformer={(s) => `${s}%`} 178 + onChange={(v) => 285 179 handleStylingChange({ 286 180 ...props.styling, 287 - verticalPosition: 1, 181 + size: v / 100, 288 182 }) 289 183 } 184 + value={props.styling.size * 100} 185 + /> 186 + <div className="flex justify-between items-center"> 187 + <Menu.FieldTitle> 188 + {t("settings.subtitles.textStyle.title")} 189 + </Menu.FieldTitle> 190 + <div className="w-30"> 191 + <Dropdown 192 + options={[ 193 + { 194 + id: "default", 195 + name: t("settings.subtitles.textStyle.default"), 196 + }, 197 + { 198 + id: "raised", 199 + name: t("settings.subtitles.textStyle.raised"), 200 + }, 201 + { 202 + id: "depressed", 203 + name: t("settings.subtitles.textStyle.depressed"), 204 + }, 205 + { 206 + id: "uniform", 207 + name: t("settings.subtitles.textStyle.uniform"), 208 + }, 209 + { 210 + id: "dropShadow", 211 + name: t("settings.subtitles.textStyle.dropShadow"), 212 + }, 213 + ]} 214 + selectedItem={{ 215 + id: props.styling.fontStyle, 216 + name: 217 + t( 218 + `settings.subtitles.textStyle.${props.styling.fontStyle}`, 219 + ) || props.styling.fontStyle, 220 + }} 221 + setSelectedItem={(item) => 222 + handleStylingChange({ 223 + ...props.styling, 224 + fontStyle: item.id, 225 + }) 226 + } 227 + /> 228 + </div> 229 + </div> 230 + <div className="flex justify-between items-center"> 231 + <Menu.FieldTitle> 232 + {t("settings.subtitles.textBoldLabel")} 233 + </Menu.FieldTitle> 234 + <div className="flex justify-center items-center"> 235 + <Toggle 236 + enabled={props.styling.bold} 237 + onClick={() => 238 + handleStylingChange({ 239 + ...props.styling, 240 + bold: !props.styling.bold, 241 + }) 242 + } 243 + /> 244 + </div> 245 + </div> 246 + <div className="flex justify-between items-center"> 247 + <Menu.FieldTitle> 248 + {t("settings.subtitles.colorLabel")} 249 + </Menu.FieldTitle> 250 + <div className="flex justify-center items-center space-x-2"> 251 + {colors.map((v) => ( 252 + <ColorOption 253 + onClick={() => 254 + handleStylingChange({ 255 + ...props.styling, 256 + color: v, 257 + }) 258 + } 259 + color={v} 260 + active={props.styling.color === v} 261 + key={v} 262 + /> 263 + ))} 264 + <div className="relative"> 265 + <input 266 + type="color" 267 + value={props.styling.color} 268 + onChange={(e) => { 269 + const color = e.target.value; 270 + handleStylingChange({ ...props.styling, color }); 271 + subtitleStore.updateStyling({ 272 + ...props.styling, 273 + color, 274 + }); 275 + }} 276 + className="absolute opacity-0 cursor-pointer w-8 h-8" 277 + /> 278 + <div style={{ color: props.styling.color }}> 279 + <Icon icon={Icons.BRUSH} className="text-2xl" /> 280 + </div> 281 + </div> 282 + </div> 283 + </div> 284 + <div className="flex justify-between items-center"> 285 + <Menu.FieldTitle> 286 + {t("settings.subtitles.verticalPositionLabel")} 287 + </Menu.FieldTitle> 288 + <div className="flex justify-center items-center space-x-2"> 289 + <button 290 + type="button" 291 + className={classNames( 292 + "px-3 py-1 rounded transition-colors duration-100", 293 + props.styling.verticalPosition === 3 294 + ? "bg-video-context-buttonFocus" 295 + : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 296 + )} 297 + onClick={() => 298 + handleStylingChange({ 299 + ...props.styling, 300 + verticalPosition: 3, 301 + }) 302 + } 303 + > 304 + {t("settings.subtitles.default")} 305 + </button> 306 + <button 307 + type="button" 308 + className={classNames( 309 + "px-3 py-1 rounded transition-colors duration-100", 310 + props.styling.verticalPosition === 1 311 + ? "bg-video-context-buttonFocus" 312 + : "bg-video-context-buttonFocus bg-opacity-0 hover:bg-opacity-50", 313 + )} 314 + onClick={() => 315 + handleStylingChange({ 316 + ...props.styling, 317 + verticalPosition: 1, 318 + }) 319 + } 320 + > 321 + {t("settings.subtitles.low")} 322 + </button> 323 + </div> 324 + </div> 325 + <Button 326 + className="w-full md:w-auto" 327 + theme="secondary" 328 + onClick={resetSubStyling} 290 329 > 291 - {t("settings.subtitles.low")} 292 - </button> 293 - </div> 294 - </div> 330 + {t("settings.reset")} 331 + </Button> 332 + </> 333 + )} 295 334 </div> 296 - <CaptionPreview 297 - show 298 - styling={props.styling} 299 - onToggle={() => setFullscreenPreview((s) => !s)} 300 - /> 301 - <CaptionPreview 302 - show={fullscreenPreview} 303 - fullscreen 304 - styling={props.styling} 305 - onToggle={() => setFullscreenPreview((s) => !s)} 306 - /> 307 - <Button 308 - className="w-full md:w-auto" 309 - theme="secondary" 310 - onClick={resetSubStyling} 311 - > 312 - {t("settings.reset")} 313 - </Button> 335 + {!enableNativeSubtitles && ( 336 + <> 337 + <CaptionPreview 338 + show 339 + styling={props.styling} 340 + onToggle={() => setFullscreenPreview((s) => !s)} 341 + /> 342 + <CaptionPreview 343 + show={fullscreenPreview} 344 + fullscreen 345 + styling={props.styling} 346 + onToggle={() => setFullscreenPreview((s) => !s)} 347 + /> 348 + </> 349 + )} 314 350 </div> 315 351 </div> 316 352 );
+8
src/stores/preferences/index.tsx
··· 18 18 febboxKey: string | null; 19 19 realDebridKey: string | null; 20 20 enableLowPerformanceMode: boolean; 21 + enableNativeSubtitles: boolean; 21 22 22 23 setEnableThumbnails(v: boolean): void; 23 24 setEnableAutoplay(v: boolean): void; ··· 34 35 setFebboxKey(v: string | null): void; 35 36 setRealDebridKey(v: string | null): void; 36 37 setEnableLowPerformanceMode(v: boolean): void; 38 + setEnableNativeSubtitles(v: boolean): void; 37 39 } 38 40 39 41 export const usePreferencesStore = create( ··· 54 56 febboxKey: null, 55 57 realDebridKey: null, 56 58 enableLowPerformanceMode: false, 59 + enableNativeSubtitles: false, 57 60 setEnableThumbnails(v) { 58 61 set((s) => { 59 62 s.enableThumbnails = v; ··· 127 130 setEnableLowPerformanceMode(v) { 128 131 set((s) => { 129 132 s.enableLowPerformanceMode = v; 133 + }); 134 + }, 135 + setEnableNativeSubtitles(v) { 136 + set((s) => { 137 + s.enableNativeSubtitles = v; 130 138 }); 131 139 }, 132 140 })),