mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
quality preference saving
This commit is contained in:
parent
bc27a7ffa7
commit
3da2d616a2
4 changed files with 104 additions and 12 deletions
|
@ -1,5 +1,6 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
import { Toggle } from "@/components/buttons/Toggle";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Context } from "@/components/player/internals/ContextUtils";
|
||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||
|
@ -9,6 +10,7 @@ import {
|
|||
allQualities,
|
||||
qualityToString,
|
||||
} from "@/stores/player/utils/qualities";
|
||||
import { useQualityStore } from "@/stores/quality";
|
||||
|
||||
export function QualityOption(props: {
|
||||
children: React.ReactNode;
|
||||
|
@ -41,13 +43,18 @@ export function QualityView({ id }: { id: string }) {
|
|||
const availableQualities = usePlayerStore((s) => s.qualities);
|
||||
const currentQuality = usePlayerStore((s) => s.currentQuality);
|
||||
const switchQuality = usePlayerStore((s) => s.switchQuality);
|
||||
const setAutomaticQuality = useQualityStore((s) => s.setAutomaticQuality);
|
||||
const setLastChosenQuality = useQualityStore((s) => s.setLastChosenQuality);
|
||||
const autoQuality = useQualityStore((s) => s.quality.automaticQuality);
|
||||
|
||||
const change = useCallback(
|
||||
(q: SourceQuality) => {
|
||||
switchQuality(q);
|
||||
setLastChosenQuality(q);
|
||||
setAutomaticQuality(false);
|
||||
router.close();
|
||||
},
|
||||
[router, switchQuality]
|
||||
[router, switchQuality, setLastChosenQuality, setAutomaticQuality]
|
||||
);
|
||||
|
||||
const allVisibleQualities = allQualities.filter((t) => t !== "unknown");
|
||||
|
@ -73,7 +80,10 @@ export function QualityView({ id }: { id: string }) {
|
|||
<Context.Divider />
|
||||
<Context.Link>
|
||||
<Context.LinkTitle>Automatic quality</Context.LinkTitle>
|
||||
<span>Toggle</span>
|
||||
<Toggle
|
||||
onClick={() => setAutomaticQuality(!autoQuality)}
|
||||
enabled={autoQuality}
|
||||
/>
|
||||
</Context.Link>
|
||||
<Context.SmallText>
|
||||
You can try{" "}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
SourceSliceSource,
|
||||
selectQuality,
|
||||
} from "@/stores/player/utils/qualities";
|
||||
import { useQualityStore } from "@/stores/quality";
|
||||
import { ValuesOf } from "@/utils/typeguard";
|
||||
|
||||
export const playerStatus = {
|
||||
|
@ -118,7 +119,8 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
let qualities: string[] = [];
|
||||
if (stream.type === "file") qualities = Object.keys(stream.qualities);
|
||||
const store = get();
|
||||
const loadableStream = selectQuality(stream);
|
||||
const qualityPreferences = useQualityStore.getState();
|
||||
const loadableStream = selectQuality(stream, qualityPreferences.quality);
|
||||
|
||||
set((s) => {
|
||||
s.source = stream;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { QualityStore } from "@/stores/quality";
|
||||
|
||||
export type SourceQuality = "unknown" | "360" | "480" | "720" | "1080";
|
||||
|
||||
export type StreamType = "hls" | "mp4";
|
||||
|
@ -23,17 +25,56 @@ export type SourceSliceSource =
|
|||
};
|
||||
|
||||
const qualitySorting: Record<SourceQuality, number> = {
|
||||
"1080": 40,
|
||||
unknown: 0,
|
||||
"360": 10,
|
||||
"480": 20,
|
||||
"720": 30,
|
||||
unknown: 0,
|
||||
"1080": 40,
|
||||
};
|
||||
const sortedQualities: SourceQuality[] = Object.entries(qualitySorting)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map<SourceQuality>((v) => v[0] as SourceQuality);
|
||||
|
||||
export function selectQuality(source: SourceSliceSource): {
|
||||
function getPreferredQuality(
|
||||
availableQualites: SourceQuality[],
|
||||
qualityPreferences: QualityStore["quality"]
|
||||
) {
|
||||
if (
|
||||
qualityPreferences.automaticQuality ||
|
||||
qualityPreferences.lastChosenQuality === null ||
|
||||
qualityPreferences.lastChosenQuality === "unknown"
|
||||
)
|
||||
return sortedQualities.find((v) => availableQualites.includes(v));
|
||||
|
||||
// get preferred quality - not automatic or unknown
|
||||
const chosenQualityIndex = sortedQualities.indexOf(
|
||||
qualityPreferences.lastChosenQuality
|
||||
);
|
||||
let nearestChoseQuality: undefined | SourceQuality;
|
||||
|
||||
// check chosen quality or lower
|
||||
for (let i = chosenQualityIndex; i < sortedQualities.length; i += 1) {
|
||||
if (availableQualites.includes(sortedQualities[i])) {
|
||||
nearestChoseQuality = sortedQualities[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nearestChoseQuality) return nearestChoseQuality;
|
||||
|
||||
// chosen quality or lower doesn't exist, try higher
|
||||
for (let i = chosenQualityIndex; i >= 0; i -= 1) {
|
||||
if (availableQualites.includes(sortedQualities[i])) {
|
||||
nearestChoseQuality = sortedQualities[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nearestChoseQuality;
|
||||
}
|
||||
|
||||
export function selectQuality(
|
||||
source: SourceSliceSource,
|
||||
qualityPreferences: QualityStore["quality"]
|
||||
): {
|
||||
stream: LoadableSource;
|
||||
quality: null | SourceQuality;
|
||||
} {
|
||||
|
@ -43,13 +84,14 @@ export function selectQuality(source: SourceSliceSource): {
|
|||
quality: null,
|
||||
};
|
||||
if (source.type === "file") {
|
||||
const bestQuality = sortedQualities.find(
|
||||
(v) => source.qualities[v] && (source.qualities[v]?.url.length ?? 0) > 0
|
||||
);
|
||||
if (bestQuality) {
|
||||
const stream = source.qualities[bestQuality];
|
||||
const availableQualities = Object.entries(source.qualities)
|
||||
.filter((entry) => (entry[1].url.length ?? 0) > 0)
|
||||
.map((entry) => entry[0]) as SourceQuality[];
|
||||
const quality = getPreferredQuality(availableQualities, qualityPreferences);
|
||||
if (quality) {
|
||||
const stream = source.qualities[quality];
|
||||
if (stream) {
|
||||
return { stream, quality: bestQuality };
|
||||
return { stream, quality };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
38
src/stores/quality/index.ts
Normal file
38
src/stores/quality/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
import { SourceQuality } from "@/stores/player/utils/qualities";
|
||||
|
||||
export interface QualityStore {
|
||||
quality: {
|
||||
lastChosenQuality: SourceQuality | null;
|
||||
automaticQuality: boolean;
|
||||
};
|
||||
setLastChosenQuality(v: SourceQuality | null): void;
|
||||
setAutomaticQuality(v: boolean): void;
|
||||
}
|
||||
|
||||
export const useQualityStore = create(
|
||||
persist(
|
||||
immer<QualityStore>((set) => ({
|
||||
quality: {
|
||||
automaticQuality: true,
|
||||
lastChosenQuality: null,
|
||||
},
|
||||
setLastChosenQuality(v) {
|
||||
set((s) => {
|
||||
s.quality.lastChosenQuality = v;
|
||||
});
|
||||
},
|
||||
setAutomaticQuality(v) {
|
||||
set((s) => {
|
||||
s.quality.automaticQuality = v;
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: "__MW::quality",
|
||||
}
|
||||
)
|
||||
);
|
Loading…
Reference in a new issue