ytptube/ui/app/utils/media.ts
2026-05-17 20:21:58 +03:00

89 lines
2 KiB
TypeScript

import { useLocalCache } from '~/utils/cache';
const KEY = 'video:';
let cache: ReturnType<typeof useLocalCache> | null = null;
const getCache = (): ReturnType<typeof useLocalCache> => {
if (!cache) {
cache = useLocalCache();
}
return cache;
};
const read = (id: string | null | undefined): number => {
if (!id) {
return 0;
}
try {
const time = Number(getCache().get<number>(`${KEY}${id}`));
return Number.isFinite(time) && time > 0 ? time : 0;
} catch {
return 0;
}
};
const save = (id: string | null | undefined, time: number): void => {
if (!id || !Number.isFinite(time) || time <= 0) {
return;
}
getCache().set(`${KEY}${id}`, time, 3600 * 24);
};
const clear = (id: string | null | undefined): void => {
if (!id) {
return;
}
getCache().remove(`${KEY}${id}`);
};
const nearEnd = (
target: Pick<HTMLMediaElement, 'currentTime' | 'duration'> | null,
pad: number = 5,
): boolean => {
if (!target) {
return false;
}
const duration = target.duration;
if (!Number.isFinite(duration) || duration <= 0) {
return false;
}
const time =
Number.isFinite(target.currentTime) && target.currentTime > 0 ? target.currentTime : 0;
return duration - time <= pad;
};
const clampResumeTime = (
target: Pick<HTMLMediaElement, 'duration' | 'seekable'>,
time: number,
): number => {
if (!Number.isFinite(time) || time <= 0) {
return 0;
}
const seekable = target.seekable;
if (seekable && seekable.length > 0) {
const last = seekable.length - 1;
const start = seekable.start(last);
const end = seekable.end(last);
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) {
return 0;
}
return Math.min(Math.max(time, start), end);
}
const duration = target.duration;
if (Number.isFinite(duration) && duration > 0) {
return Math.min(time, Math.max(duration - 0.25, 0));
}
return time;
};
export { clampResumeTime, clear, nearEnd, read, save };