drop oauth plan (copy config)

This commit is contained in:
2026-08-08 07:10:59 -04:00
parent 13a31aabdc
commit 3ef19f80b8
20 changed files with 530 additions and 1592 deletions

View File

@@ -8,8 +8,8 @@
import { createSignal } from "solid-js";
import type { Progress } from "../types/episode";
import {
loadProgressFromFile,
saveProgressToFile,
loadProgressFromFile,
saveProgressToFile,
} from "../utils/app-persistence";
/** Threshold (fraction 0-1) at which an episode is considered completed */
@@ -21,146 +21,146 @@ const MIN_POSITION_TO_SAVE = 5;
// --- Singleton store ---
const [progressMap, setProgressMap] = createSignal<Record<string, Progress>>(
{},
{},
);
/** Persist current progress map to file (fire-and-forget) */
function persist(): void {
saveProgressToFile(progressMap()).catch(() => {});
saveProgressToFile(progressMap());
}
/** Parse raw progress entries from file, reviving Date objects */
function parseProgressEntries(
raw: Record<string, unknown>,
raw: Record<string, unknown>,
): Record<string, Progress> {
const result: Record<string, Progress> = {};
for (const [key, value] of Object.entries(raw)) {
const p = value as Record<string, unknown>;
result[key] = {
episodeId: p.episodeId as string,
position: p.position as number,
duration: p.duration as number,
timestamp: new Date(p.timestamp as string),
playbackSpeed: p.playbackSpeed as number | undefined,
};
}
return result;
const result: Record<string, Progress> = {};
for (const [key, value] of Object.entries(raw)) {
const p = value as Record<string, unknown>;
result[key] = {
episodeId: p.episodeId as string,
position: p.position as number,
duration: p.duration as number,
timestamp: new Date(p.timestamp as string),
playbackSpeed: p.playbackSpeed as number | undefined,
};
}
return result;
}
async function initProgress(): Promise<void> {
const raw = await loadProgressFromFile();
const parsed = parseProgressEntries(raw as Record<string, unknown>);
setProgressMap(parsed);
const raw = await loadProgressFromFile();
const parsed = parseProgressEntries(raw as Record<string, unknown>);
setProgressMap(parsed);
}
// Fire-and-forget init
initProgress();
function createProgressStore() {
return {
/**
* Get progress for a specific episode.
*/
get(episodeId: string): Progress | undefined {
return progressMap()[episodeId];
},
return {
/**
* Get progress for a specific episode.
*/
get(episodeId: string): Progress | undefined {
return progressMap()[episodeId];
},
/**
* Get all progress entries.
*/
all(): Record<string, Progress> {
return progressMap();
},
/**
* Get all progress entries.
*/
all(): Record<string, Progress> {
return progressMap();
},
/**
* Update progress for an episode. Only persists if position is meaningful.
*/
update(
episodeId: string,
position: number,
duration: number,
playbackSpeed?: number,
): void {
if (position < MIN_POSITION_TO_SAVE && duration > 0) return;
/**
* Update progress for an episode. Only persists if position is meaningful.
*/
update(
episodeId: string,
position: number,
duration: number,
playbackSpeed?: number,
): void {
if (position < MIN_POSITION_TO_SAVE && duration > 0) return;
setProgressMap((prev) => ({
...prev,
[episodeId]: {
episodeId,
position,
duration,
timestamp: new Date(),
playbackSpeed,
},
}));
persist();
},
setProgressMap((prev) => ({
...prev,
[episodeId]: {
episodeId,
position,
duration,
timestamp: new Date(),
playbackSpeed,
},
}));
persist();
},
/**
* Check if an episode is completed.
*/
isCompleted(episodeId: string): boolean {
const p = progressMap()[episodeId];
if (!p || p.duration <= 0) return false;
return p.position / p.duration >= COMPLETION_THRESHOLD;
},
/**
* Check if an episode is completed.
*/
isCompleted(episodeId: string): boolean {
const p = progressMap()[episodeId];
if (!p || p.duration <= 0) return false;
return p.position / p.duration >= COMPLETION_THRESHOLD;
},
/**
* Get progress percentage (0-100) for an episode.
*/
getPercent(episodeId: string): number {
const p = progressMap()[episodeId];
if (!p || p.duration <= 0) return 0;
return Math.min(100, Math.round((p.position / p.duration) * 100));
},
/**
* Get progress percentage (0-100) for an episode.
*/
getPercent(episodeId: string): number {
const p = progressMap()[episodeId];
if (!p || p.duration <= 0) return 0;
return Math.min(100, Math.round((p.position / p.duration) * 100));
},
/**
* Mark an episode as completed (set position to duration).
*/
markCompleted(episodeId: string): void {
const p = progressMap()[episodeId];
const duration = p?.duration ?? 0;
setProgressMap((prev) => ({
...prev,
[episodeId]: {
episodeId,
position: duration,
duration,
timestamp: new Date(),
playbackSpeed: p?.playbackSpeed,
},
}));
persist();
},
/**
* Mark an episode as completed (set position to duration).
*/
markCompleted(episodeId: string): void {
const p = progressMap()[episodeId];
const duration = p?.duration ?? 0;
setProgressMap((prev) => ({
...prev,
[episodeId]: {
episodeId,
position: duration,
duration,
timestamp: new Date(),
playbackSpeed: p?.playbackSpeed,
},
}));
persist();
},
/**
* Remove progress for an episode (e.g. "mark as new").
*/
remove(episodeId: string): void {
setProgressMap((prev) => {
const next = { ...prev };
delete next[episodeId];
return next;
});
persist();
},
/**
* Remove progress for an episode (e.g. "mark as new").
*/
remove(episodeId: string): void {
setProgressMap((prev) => {
const next = { ...prev };
delete next[episodeId];
return next;
});
persist();
},
/**
* Clear all progress data.
*/
clear(): void {
setProgressMap({});
persist();
},
};
/**
* Clear all progress data.
*/
clear(): void {
setProgressMap({});
persist();
},
};
}
// Singleton instance
let instance: ReturnType<typeof createProgressStore> | null = null;
export function useProgressStore() {
if (!instance) {
instance = createProgressStore();
}
return instance;
if (!instance) {
instance = createProgressStore();
}
return instance;
}