disable collection/group syncing

This commit is contained in:
Pas 2026-02-25 11:08:08 -07:00
parent 6e5184501f
commit 387982d2f9

View file

@ -4,42 +4,42 @@ import { useInterval } from "react-use";
import { getPosterForMedia } from "@/backend/metadata/tmdb";
import { useBookmarkStore } from "@/stores/bookmarks";
import { useTraktAuthStore } from "@/stores/trakt/store";
import { modifyBookmarks } from "@/utils/bookmarkModifications";
import { traktService } from "@/utils/trakt";
import { TraktContentData, TraktList } from "@/utils/traktTypes";
import { TraktContentData } from "@/utils/traktTypes";
const TRAKT_SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 min
const INITIAL_SYNC_DELAY_MS = 2000; // Re-sync after backend restore
function listId(list: TraktList): string {
return list.ids.slug ?? String(list.ids.trakt);
}
async function findListByName(
username: string,
groupName: string,
): Promise<TraktList | null> {
const lists = await traktService.getLists(username);
return lists.find((l) => l.name === groupName) ?? null;
}
async function ensureListExists(
username: string,
groupName: string,
): Promise<TraktList | null> {
const existing = await findListByName(username, groupName);
if (existing) return existing;
try {
return await traktService.createList(username, groupName);
} catch {
return null;
}
}
// Collections/groups sync disabled for now - bookmarks only sync to watchlist
// import { modifyBookmarks } from "@/utils/bookmarkModifications";
// import { TraktList } from "@/utils/traktTypes";
// function listId(list: TraktList): string {
// return list.ids.slug ?? String(list.ids.trakt);
// }
// async function findListByName(
// username: string,
// groupName: string,
// ): Promise<TraktList | null> {
// const lists = await traktService.getLists(username);
// return lists.find((l) => l.name === groupName) ?? null;
// }
// async function ensureListExists(
// username: string,
// groupName: string,
// ): Promise<TraktList | null> {
// const existing = await findListByName(username, groupName);
// if (existing) return existing;
// try {
// return await traktService.createList(username, groupName);
// } catch {
// return null;
// }
// }
export function TraktBookmarkSyncer() {
const { traktUpdateQueue, removeTraktUpdateItem, replaceBookmarks } =
useBookmarkStore();
const { accessToken, user } = useTraktAuthStore();
const { accessToken } = useTraktAuthStore();
const isSyncingRef = useRef(false);
const [hydrated, setHydrated] = useState(false);
@ -51,9 +51,6 @@ export function TraktBookmarkSyncer() {
const queue = [...traktUpdateQueue];
if (queue.length === 0) return;
const slug = user?.ids?.slug;
const hasLists = Boolean(slug);
for (const item of queue) {
removeTraktUpdateItem(item.id);
@ -70,48 +67,50 @@ export function TraktBookmarkSyncer() {
if (item.action === "add") {
await traktService.addToWatchlist(contentData);
if (hasLists) {
const newGroups = item.group ?? [];
const prevGroups = item.previousGroup ?? [];
// Collections sync disabled - bookmarks only sync to watchlist
// if (hasLists) {
// const newGroups = item.group ?? [];
// const prevGroups = item.previousGroup ?? [];
// Remove from Trakt lists that the bookmark no longer belongs to
const groupsToRemove = prevGroups.filter(
(g) => !newGroups.includes(g),
);
for (const groupName of groupsToRemove) {
const list = await findListByName(slug!, groupName);
if (list) {
await traktService.removeFromList(slug!, listId(list), [
contentData,
]);
}
}
// // Remove from Trakt lists that the bookmark no longer belongs to
// const groupsToRemove = prevGroups.filter(
// (g) => !newGroups.includes(g),
// );
// for (const groupName of groupsToRemove) {
// const list = await findListByName(slug!, groupName);
// if (list) {
// await traktService.removeFromList(slug!, listId(list), [
// contentData,
// ]);
// }
// }
// Add to Trakt lists that are new
const groupsToAdd = newGroups.filter(
(g) => !prevGroups.includes(g),
);
for (const groupName of groupsToAdd) {
const list = await ensureListExists(slug!, groupName);
if (list) {
await traktService.addToList(slug!, listId(list), [
contentData,
]);
}
}
}
// // Add to Trakt lists that are new
// const groupsToAdd = newGroups.filter(
// (g) => !prevGroups.includes(g),
// );
// for (const groupName of groupsToAdd) {
// const list = await ensureListExists(slug!, groupName);
// if (list) {
// await traktService.addToList(slug!, listId(list), [
// contentData,
// ]);
// }
// }
// }
} else if (item.action === "delete") {
await traktService.removeFromWatchlist(contentData);
if (hasLists && item.group?.length) {
for (const groupName of item.group) {
const list = await findListByName(slug!, groupName);
if (list) {
await traktService.removeFromList(slug!, listId(list), [
contentData,
]);
}
}
}
// Collections sync disabled - bookmarks only sync to watchlist
// if (hasLists && item.group?.length) {
// for (const groupName of item.group) {
// const list = await findListByName(slug!, groupName);
// if (list) {
// await traktService.removeFromList(slug!, listId(list), [
// contentData,
// ]);
// }
// }
// }
}
} catch (error) {
console.error("Failed to sync bookmark to Trakt", error);
@ -120,19 +119,20 @@ export function TraktBookmarkSyncer() {
};
processQueue();
}, [accessToken, user?.ids?.slug, traktUpdateQueue, removeTraktUpdateItem]);
}, [accessToken, traktUpdateQueue, removeTraktUpdateItem]);
// Push local bookmarks to Trakt (watchlist + groups)
// Push local bookmarks to Trakt watchlist (TODO implement collections/groups sync)
const syncBookmarksToTrakt = useCallback(async () => {
if (!accessToken || isSyncingRef.current) return;
const slug = useTraktAuthStore.getState().user?.ids?.slug;
if (!slug) return;
// const slug = useTraktAuthStore.getState().user?.ids?.slug;
// if (!slug) return;
isSyncingRef.current = true;
try {
if (!useTraktAuthStore.getState().user) {
await traktService.getUserProfile();
}
const bookmarks = useBookmarkStore.getState().bookmarks;
for (const [tmdbId, b] of Object.entries(bookmarks)) {
try {
const contentData: TraktContentData = {
@ -142,14 +142,15 @@ export function TraktBookmarkSyncer() {
type: b.type === "movie" ? "movie" : "show",
};
await traktService.addToWatchlist(contentData);
if (b.group?.length) {
for (const groupName of b.group) {
const list = await ensureListExists(slug, groupName);
if (list) {
await traktService.addToList(slug, listId(list), [contentData]);
}
}
}
// Collections sync disabled - bookmarks only sync to watchlist
// if (b.group?.length) {
// for (const groupName of b.group) {
// const list = await ensureListExists(slug, groupName);
// if (list) {
// await traktService.addToList(slug, listId(list), [contentData]);
// }
// }
// }
} catch (err) {
console.warn("Failed to push bookmark to Trakt:", tmdbId, err);
}
@ -192,63 +193,64 @@ export function TraktBookmarkSyncer() {
replaceBookmarks(merged);
const slug = useTraktAuthStore.getState().user?.ids?.slug;
if (slug) {
try {
const lists = await traktService.getLists(slug);
const currentBookmarks = useBookmarkStore.getState().bookmarks;
let modifiedBookmarks = { ...currentBookmarks };
// Collections sync disabled - only watchlist is synced, no Trakt lists
// const slug = useTraktAuthStore.getState().user?.ids?.slug;
// if (slug) {
// try {
// const lists = await traktService.getLists(slug);
// const currentBookmarks = useBookmarkStore.getState().bookmarks;
// let modifiedBookmarks = { ...currentBookmarks };
for (const list of lists) {
const listTitle = list.name;
const items = await traktService.getListItems(slug, listId(list));
for (const li of items) {
const media = li.movie || li.show;
if (!media?.ids?.tmdb) continue;
// for (const list of lists) {
// const listTitle = list.name;
// const items = await traktService.getListItems(slug, listId(list));
// for (const li of items) {
// const media = li.movie || li.show;
// if (!media?.ids?.tmdb) continue;
const tmdbId = media.ids.tmdb.toString();
const type = li.movie ? "movie" : "show";
const bookmark = modifiedBookmarks[tmdbId];
// const tmdbId = media.ids.tmdb.toString();
// const type = li.movie ? "movie" : "show";
// const bookmark = modifiedBookmarks[tmdbId];
if (!bookmark) {
const poster = await getPosterForMedia(tmdbId, type);
modifiedBookmarks[tmdbId] = {
type: type as "movie" | "show",
title: media.title,
year: media.year,
poster,
updatedAt: Date.now(),
group: [listTitle],
};
} else {
const groups = bookmark.group ?? [];
if (!groups.includes(listTitle)) {
const { modifiedBookmarks: next } = modifyBookmarks(
modifiedBookmarks,
[tmdbId],
{ addGroups: [listTitle] },
);
modifiedBookmarks = next;
}
}
}
}
// if (!bookmark) {
// const poster = await getPosterForMedia(tmdbId, type);
// modifiedBookmarks[tmdbId] = {
// type: type as "movie" | "show",
// title: media.title,
// year: media.year,
// poster,
// updatedAt: Date.now(),
// group: [listTitle],
// };
// } else {
// const groups = bookmark.group ?? [];
// if (!groups.includes(listTitle)) {
// const { modifiedBookmarks: next } = modifyBookmarks(
// modifiedBookmarks,
// [tmdbId],
// { addGroups: [listTitle] },
// );
// modifiedBookmarks = next;
// }
// }
// }
// }
const hasNewBookmarks =
Object.keys(modifiedBookmarks).length !==
Object.keys(currentBookmarks).length;
const hasGroupChanges = Object.keys(modifiedBookmarks).some(
(id) =>
JSON.stringify(modifiedBookmarks[id]?.group ?? []) !==
JSON.stringify(currentBookmarks[id]?.group ?? []),
);
if (hasNewBookmarks || hasGroupChanges) {
replaceBookmarks(modifiedBookmarks);
}
} catch (listError) {
console.warn("Failed to sync Trakt lists (groups)", listError);
}
}
// const hasNewBookmarks =
// Object.keys(modifiedBookmarks).length !==
// Object.keys(currentBookmarks).length;
// const hasGroupChanges = Object.keys(modifiedBookmarks).some(
// (id) =>
// JSON.stringify(modifiedBookmarks[id]?.group ?? []) !==
// JSON.stringify(currentBookmarks[id]?.group ?? []),
// );
// if (hasNewBookmarks || hasGroupChanges) {
// replaceBookmarks(modifiedBookmarks);
// }
// } catch (listError) {
// console.warn("Failed to sync Trakt lists (groups)", listError);
// }
// }
} catch (error) {
console.error("Failed to sync Trakt watchlist to local", error);
} finally {
@ -257,8 +259,9 @@ export function TraktBookmarkSyncer() {
}, [accessToken, replaceBookmarks]);
const fullSync = useCallback(async () => {
await syncWatchlistFromTrakt(); // Pull Trakt → local, merge
await syncBookmarksToTrakt(); // Push local → Trakt
// Push local → Trakt first so our changes reach Trakt before we pull
await syncBookmarksToTrakt();
await syncWatchlistFromTrakt(); // Then pull Trakt → local, merge
}, [syncWatchlistFromTrakt, syncBookmarksToTrakt]);
// Wait for Trakt auth store to rehydrate from persist (accessToken may be null on first render)
@ -281,13 +284,12 @@ export function TraktBookmarkSyncer() {
};
}, []);
// On mount (after hydration): pull immediately (Trakt → local)
// On mount (after hydration): full sync (push then pull)
useEffect(() => {
if (!hydrated || !accessToken) return;
syncWatchlistFromTrakt();
const t = setTimeout(fullSync, INITIAL_SYNC_DELAY_MS);
return () => clearTimeout(t);
}, [hydrated, accessToken, syncWatchlistFromTrakt, fullSync]);
}, [hydrated, accessToken, fullSync]);
// Periodic full sync (pull + push)
useInterval(fullSync, TRAKT_SYNC_INTERVAL_MS);