mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Anotha one!
This commit is contained in:
+409
-108
@@ -1,97 +1,335 @@
|
||||
// KefinTweaks Studios Cache Manager
|
||||
// Handles fetching, processing, and caching of Popular TV Networks
|
||||
// KefinTweaks Movies Library Cache
|
||||
// Chunked per-user archive of movies (no UserData). Startup after login + incremental sync.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const LOG = (...args) => console.log('[KefinTweaks Movies Cache]', ...args);
|
||||
const WARN = (...args) => console.warn('[KefinTweaks Movies Cache]', ...args);
|
||||
const ERR = (...args) => console.error('[KefinTweaks Movies Cache]', ...args);
|
||||
const LOG = (...args) => console.log('[KefinTweaks Movies Cache]', ...args);
|
||||
const WARN = (...args) => console.warn('[KefinTweaks Movies Cache]', ...args);
|
||||
const ERR = (...args) => console.error('[KefinTweaks Movies Cache]', ...args);
|
||||
|
||||
// 1 week
|
||||
const CACHE_TTL = 7 * 24 * 60 * 60 * 1000;
|
||||
const IMDB_TOP_250_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours for ID list
|
||||
const CACHE_NAME = 'movies_library';
|
||||
const PARTIAL_CACHE_NAME = 'movies_library_partial';
|
||||
const LAST_FETCH_KEY_PREFIX = 'kefinTweaks_lastDateFetchedMovieCache_';
|
||||
const IMDB_TOP_250_CACHE_TTL = 24 * 60 * 60 * 1000;
|
||||
const MOVIE_FIELDS = [
|
||||
'ProviderIds', 'People', 'Studios', 'Taglines', 'Genres', 'Overview', 'PrimaryImageAspectRatio',
|
||||
'DateCreated'
|
||||
].join(',');
|
||||
|
||||
// State
|
||||
let movies = null;
|
||||
let isComplete = false;
|
||||
let imdbTop250Movies = null;
|
||||
let isInitializing = false;
|
||||
let fetchPromise = null;
|
||||
let providerIndex = null;
|
||||
|
||||
function utils() {
|
||||
return window.LibraryCacheUtils || {};
|
||||
}
|
||||
|
||||
function getTtl() {
|
||||
return utils().getLibraryCacheSettings?.().CACHE_TTL || (14 * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
function getChunkSize() {
|
||||
return utils().getLibraryCacheSettings?.().movieChunkSize || 1000;
|
||||
}
|
||||
|
||||
function lastFetchStorageKey() {
|
||||
const userId = window.ApiClient?.getCurrentUserId?.() || 'anonymous';
|
||||
return `${LAST_FETCH_KEY_PREFIX}${userId}`;
|
||||
}
|
||||
|
||||
function getLastDateFetched() {
|
||||
try {
|
||||
return localStorage.getItem(lastFetchStorageKey()) || null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setLastDateFetched(iso) {
|
||||
try {
|
||||
localStorage.setItem(lastFetchStorageKey(), iso || new Date().toISOString());
|
||||
} catch (e) {
|
||||
WARN('Failed to store lastDateFetchedMovieCache:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function getApiHelper() {
|
||||
return window.apiHelper || window.ApiHelper;
|
||||
}
|
||||
|
||||
function buildItemsUrl({ startIndex, limit, extraParams = {} }) {
|
||||
const server = window.ApiClient.serverAddress();
|
||||
const params = new URLSearchParams({
|
||||
IncludeItemTypes: 'Movie',
|
||||
Recursive: 'true',
|
||||
Fields: MOVIE_FIELDS,
|
||||
ExcludeLocationTypes: 'Virtual',
|
||||
EnableTotalRecordCount: 'true',
|
||||
SortBy: 'SortName',
|
||||
SortOrder: 'Ascending',
|
||||
StartIndex: String(startIndex || 0),
|
||||
Limit: String(limit)
|
||||
});
|
||||
Object.entries(extraParams).forEach(([k, v]) => {
|
||||
if (v !== undefined && v !== null && v !== '') params.set(k, String(v));
|
||||
});
|
||||
return `${server}/Items?${params.toString()}`;
|
||||
}
|
||||
|
||||
async function queryItems(url) {
|
||||
const api = getApiHelper();
|
||||
if (!api?.getQuery) throw new Error('apiHelper.getQuery unavailable');
|
||||
const result = await api.getQuery(url, { useCache: false });
|
||||
return result || { Items: [], TotalRecordCount: 0 };
|
||||
}
|
||||
|
||||
async function stripChunk(items) {
|
||||
if (utils().stripItemsAsync) {
|
||||
return utils().stripItemsAsync(items, 'Movie');
|
||||
}
|
||||
return (items || []).map((item) => utils().stripMovieForCache?.(item) || item).filter(Boolean);
|
||||
}
|
||||
|
||||
function rebuildProviderIndex(list) {
|
||||
providerIndex = utils().buildProviderIdIndex?.(list) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and caches all movies across all libraries.
|
||||
* You should not use this for anything related to UserData
|
||||
* The cache for this is long, and this is intended to simply be an "archive" of all movies across all libraries.
|
||||
* @returns {Promise<Array>} - Array of movie objects
|
||||
* Full crawl with partial resume. Commits to movies_library only when complete.
|
||||
*/
|
||||
async function fetchAndCacheMovies() {
|
||||
// IndexedDBCache
|
||||
const cacheName = 'movies_library';
|
||||
const cache = window.IndexedDBCache;
|
||||
|
||||
// Check cache first
|
||||
const cached = await cache.get(cacheName);
|
||||
if (cached) {
|
||||
LOG('Using cached Movies');
|
||||
return cached;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG('Fetching Movies...');
|
||||
|
||||
// We need to refactor this to pull the data in chunks of 100 items at a time in order to backfill the cache
|
||||
// We will populate 100 items at a time into the library cache.
|
||||
// Each group will contain the index of the last item in the group so we can resume from there
|
||||
// When resuming, we should first verify if the previous item based on the same sorting and startindex/limit is still the same
|
||||
// We just check the last item in the indexed db cache and make the same query starting at that index
|
||||
// If it's the same then we can resume from there, if not then we need to start from the beginning
|
||||
const allMoviesQuery = `${ApiClient.serverAddress()}/Items?IncludeItemTypes=Movie&Recursive=true&Fields=ProviderIds`;
|
||||
|
||||
const movieResult = await apiHelper.getQuery(allMoviesQuery);
|
||||
|
||||
// Resolve the data (cached or fresh)
|
||||
let allMovies = [];
|
||||
if (movieResult && movieResult.Items) {
|
||||
allMovies = movieResult.Items;
|
||||
}
|
||||
|
||||
LOG(`Fetched ${allMovies.length} Movies`);
|
||||
|
||||
// Cache the results
|
||||
const userId = ApiClient.getCurrentUserId();
|
||||
await cache.set(cacheName, allMovies, userId, CACHE_TTL);
|
||||
|
||||
return allMovies;
|
||||
} catch (err) {
|
||||
ERR('Error fetching Movies:', err);
|
||||
if (!cache) {
|
||||
ERR('IndexedDBCache unavailable');
|
||||
return [];
|
||||
}
|
||||
|
||||
const userId = window.ApiClient.getCurrentUserId();
|
||||
const complete = await cache.get(CACHE_NAME, userId);
|
||||
if (complete && Array.isArray(complete)) {
|
||||
movies = complete;
|
||||
isComplete = true;
|
||||
rebuildProviderIndex(movies);
|
||||
LOG(`Using complete cached movies (${movies.length})`);
|
||||
return movies;
|
||||
}
|
||||
|
||||
const chunkSize = getChunkSize();
|
||||
let startIndex = 0;
|
||||
let collected = [];
|
||||
|
||||
const partial = await cache.get(PARTIAL_CACHE_NAME, userId);
|
||||
if (partial && Array.isArray(partial.items) && partial.isComplete !== true) {
|
||||
collected = partial.items;
|
||||
startIndex = partial.startIndex || collected.length;
|
||||
LOG(`Resuming movie crawl from index ${startIndex} (${collected.length} items)`);
|
||||
}
|
||||
|
||||
try {
|
||||
let hasMore = true;
|
||||
while (hasMore) {
|
||||
const url = buildItemsUrl({ startIndex, limit: chunkSize });
|
||||
const data = await queryItems(url);
|
||||
const batch = data.Items || [];
|
||||
const stripped = await stripChunk(batch);
|
||||
collected = collected.concat(stripped);
|
||||
startIndex += batch.length;
|
||||
|
||||
await cache.set(PARTIAL_CACHE_NAME, {
|
||||
isComplete: false,
|
||||
startIndex,
|
||||
items: collected
|
||||
}, userId, getTtl());
|
||||
|
||||
LOG(`Movie crawl progress: ${collected.length}${data.TotalRecordCount != null ? ` / ${data.TotalRecordCount}` : ''}`);
|
||||
|
||||
if (batch.length < chunkSize) {
|
||||
hasMore = false;
|
||||
} else {
|
||||
await yieldToMain();
|
||||
}
|
||||
}
|
||||
|
||||
movies = collected;
|
||||
isComplete = true;
|
||||
rebuildProviderIndex(movies);
|
||||
await cache.set(CACHE_NAME, movies, userId, getTtl());
|
||||
await cache.clear(PARTIAL_CACHE_NAME, userId);
|
||||
setLastDateFetched(new Date().toISOString());
|
||||
LOG(`Movie crawl complete: ${movies.length} items`);
|
||||
return movies;
|
||||
} catch (err) {
|
||||
ERR('Error fetching movies:', err);
|
||||
return movies || collected || [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the cache
|
||||
* Incremental sync: upsert by MinDateLastSaved, remove orphans via TotalRecordCount + Id list.
|
||||
*/
|
||||
async function initialize() {
|
||||
if (isInitializing) return;
|
||||
|
||||
if (movies) return;
|
||||
|
||||
isInitializing = true;
|
||||
try {
|
||||
movies = await fetchAndCacheMovies();
|
||||
} finally {
|
||||
isInitializing = false;
|
||||
async function syncMoviesCache() {
|
||||
if (!isComplete || !movies) {
|
||||
LOG('Skip syncMoviesCache: complete cache not ready');
|
||||
return movies || [];
|
||||
}
|
||||
const lastFetched = getLastDateFetched();
|
||||
if (!lastFetched) {
|
||||
LOG('Skip syncMoviesCache: no lastDateFetched');
|
||||
return movies;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches IMDb Top 250 movies using client-side filtering
|
||||
* @returns {Promise<Array>} - List of matched Movie items
|
||||
*/
|
||||
const cache = window.IndexedDBCache;
|
||||
const userId = window.ApiClient.getCurrentUserId();
|
||||
const chunkSize = getChunkSize();
|
||||
|
||||
try {
|
||||
// Additions / metadata refreshes
|
||||
let startIndex = 0;
|
||||
let incoming = [];
|
||||
let hasMore = true;
|
||||
while (hasMore) {
|
||||
const url = buildItemsUrl({
|
||||
startIndex,
|
||||
limit: chunkSize,
|
||||
extraParams: {
|
||||
MinDateLastSaved: lastFetched,
|
||||
MinDateLastSavedForUser: lastFetched,
|
||||
SortBy: 'DateCreated',
|
||||
SortOrder: 'Descending'
|
||||
}
|
||||
});
|
||||
const data = await queryItems(url);
|
||||
const batch = data.Items || [];
|
||||
incoming = incoming.concat(await stripChunk(batch));
|
||||
startIndex += batch.length;
|
||||
if (batch.length < chunkSize) hasMore = false;
|
||||
}
|
||||
|
||||
const beforeIds = new Set((movies || []).map((m) => m.Id).filter(Boolean));
|
||||
let removedIds = [];
|
||||
|
||||
if (incoming.length) {
|
||||
movies = utils().upsertById?.(movies, incoming) || movies;
|
||||
LOG(`Synced ${incoming.length} movie upserts`);
|
||||
}
|
||||
|
||||
// Count check
|
||||
const countUrl = buildItemsUrl({
|
||||
startIndex: 0,
|
||||
limit: 1,
|
||||
extraParams: {
|
||||
Fields: '',
|
||||
EnableTotalRecordCount: 'true'
|
||||
}
|
||||
});
|
||||
// Rebuild without heavy Fields for count
|
||||
const server = window.ApiClient.serverAddress();
|
||||
const countParams = new URLSearchParams({
|
||||
IncludeItemTypes: 'Movie',
|
||||
Recursive: 'true',
|
||||
ExcludeLocationTypes: 'Virtual',
|
||||
EnableTotalRecordCount: 'true',
|
||||
Limit: '1',
|
||||
StartIndex: '0'
|
||||
});
|
||||
const countData = await queryItems(`${server}/Items?${countParams.toString()}`);
|
||||
const serverCount = countData.TotalRecordCount;
|
||||
if (typeof serverCount === 'number' && serverCount !== movies.length) {
|
||||
LOG(`Count mismatch cache=${movies.length} server=${serverCount}; reconciling ids`);
|
||||
const serverIds = new Set();
|
||||
let idx = 0;
|
||||
let more = true;
|
||||
while (more) {
|
||||
const idParams = new URLSearchParams({
|
||||
IncludeItemTypes: 'Movie',
|
||||
Recursive: 'true',
|
||||
ExcludeLocationTypes: 'Virtual',
|
||||
EnableTotalRecordCount: 'false',
|
||||
Limit: String(chunkSize),
|
||||
StartIndex: String(idx),
|
||||
Fields: ''
|
||||
});
|
||||
const idData = await queryItems(`${server}/Items?${idParams.toString()}`);
|
||||
const batch = idData.Items || [];
|
||||
batch.forEach((item) => {
|
||||
if (item?.Id) serverIds.add(item.Id);
|
||||
});
|
||||
idx += batch.length;
|
||||
if (batch.length < chunkSize) more = false;
|
||||
}
|
||||
const before = movies.length;
|
||||
removedIds = [...beforeIds].filter((id) => !serverIds.has(id));
|
||||
movies = movies.filter((m) => m?.Id && serverIds.has(m.Id));
|
||||
// Add any missing ids not in cache (light fetch would lack fields — full upsert via Id list)
|
||||
const cachedIds = new Set(movies.map((m) => m.Id));
|
||||
const missing = [...serverIds].filter((id) => !cachedIds.has(id));
|
||||
if (missing.length) {
|
||||
WARN(`${missing.length} movies on server missing from cache after reconcile; fetching by Ids`);
|
||||
for (let i = 0; i < missing.length; i += chunkSize) {
|
||||
const slice = missing.slice(i, i + chunkSize);
|
||||
const byIdParams = new URLSearchParams({
|
||||
Ids: slice.join(','),
|
||||
Fields: MOVIE_FIELDS,
|
||||
EnableTotalRecordCount: 'false'
|
||||
});
|
||||
const byIdData = await queryItems(`${server}/Items?${byIdParams.toString()}`);
|
||||
const stripped = await stripChunk(byIdData.Items || []);
|
||||
movies = utils().upsertById?.(movies, stripped) || movies;
|
||||
incoming = incoming.concat(stripped);
|
||||
}
|
||||
}
|
||||
LOG(`Reconcile complete: ${before} → ${movies.length}`);
|
||||
}
|
||||
|
||||
rebuildProviderIndex(movies);
|
||||
await cache.set(CACHE_NAME, movies, userId, getTtl());
|
||||
setLastDateFetched(new Date().toISOString());
|
||||
// Notify people cache of deltas when available
|
||||
if ((incoming.length || removedIds.length) && window.PeopleCache?.applyMovieDelta) {
|
||||
try {
|
||||
await window.PeopleCache.applyMovieDelta(incoming, removedIds);
|
||||
} catch (e) {
|
||||
WARN('PeopleCache.applyMovieDelta failed:', e);
|
||||
}
|
||||
}
|
||||
return movies;
|
||||
} catch (err) {
|
||||
ERR('syncMoviesCache failed:', err);
|
||||
return movies || [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getMovies() {
|
||||
if (movies && isComplete) return movies;
|
||||
if (fetchPromise) return fetchPromise;
|
||||
fetchPromise = (async () => {
|
||||
await fetchAndCacheMovies();
|
||||
return movies || [];
|
||||
})().finally(() => {
|
||||
fetchPromise = null;
|
||||
});
|
||||
return fetchPromise;
|
||||
}
|
||||
|
||||
function isCacheComplete() {
|
||||
return isComplete === true && Array.isArray(movies);
|
||||
}
|
||||
|
||||
async function getMoviesByProviderIds(ids) {
|
||||
const list = await getMovies();
|
||||
const idSet = new Set();
|
||||
(ids || []).forEach((id) => {
|
||||
(utils().normalizeProviderKeys?.(id) || []).forEach((k) => idSet.add(k));
|
||||
});
|
||||
if (!idSet.size) return [];
|
||||
return list.filter((item) => utils().itemMatchesProviderIds?.(item, idSet));
|
||||
}
|
||||
|
||||
async function fetchImdbTop250Data() {
|
||||
const CACHE_KEY_IDS = 'kefinTweaks_imdbTop250_ids';
|
||||
|
||||
let imdbIds = null;
|
||||
|
||||
// 1. Get IDs (Local Cache or Fetch GitHub)
|
||||
try {
|
||||
const cachedIds = localStorage.getItem(CACHE_KEY_IDS);
|
||||
if (cachedIds) {
|
||||
@@ -100,7 +338,9 @@
|
||||
imdbIds = parsed.ids;
|
||||
}
|
||||
}
|
||||
} catch (e) { WARN('Error reading IMDb Top 250 cache:', e); }
|
||||
} catch (e) {
|
||||
WARN('Error reading IMDb Top 250 cache:', e);
|
||||
}
|
||||
|
||||
if (!imdbIds) {
|
||||
LOG('Fetching IMDb Top 250 list from GitHub...');
|
||||
@@ -108,15 +348,10 @@
|
||||
const response = await fetch('https://raw.githubusercontent.com/theapache64/top250/master/top250_min.json');
|
||||
if (!response.ok) throw new Error('Failed to fetch Top 250 JSON');
|
||||
const data = await response.json();
|
||||
|
||||
// Extract IDs
|
||||
imdbIds = data.map(entry => {
|
||||
// entry.imdb_url e.g. "http://www.imdb.com/title/tt0111161/"
|
||||
imdbIds = data.map((entry) => {
|
||||
const match = entry.imdb_url && entry.imdb_url.match(/\/title\/(tt\d+)/);
|
||||
return match ? match[1] : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
// Cache
|
||||
localStorage.setItem(CACHE_KEY_IDS, JSON.stringify({
|
||||
timestamp: Date.now(),
|
||||
ids: imdbIds
|
||||
@@ -126,61 +361,127 @@
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!imdbIds || imdbIds.length === 0) return [];
|
||||
|
||||
return imdbIds;
|
||||
return imdbIds || [];
|
||||
}
|
||||
|
||||
async function getImdbTop250Movies() {
|
||||
const cacheName = 'imdbTop250Movies';
|
||||
const cache = window.IndexedDBCache;
|
||||
const cached = await cache.get(cacheName);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
if (cached) return cached;
|
||||
|
||||
if (!imdbTop250Movies) {
|
||||
if (!isCacheComplete()) {
|
||||
LOG('IMDb Top 250 waiting for movie cache…');
|
||||
}
|
||||
const imdbIds = await fetchImdbTop250Data();
|
||||
const allMovies = await getMovies();
|
||||
|
||||
// 3. Client-side Match
|
||||
const matchedMovies = [];
|
||||
const idsSet = new Set(imdbIds); // Faster lookup
|
||||
|
||||
for (const movie of allMovies) {
|
||||
if (movie.ProviderIds && movie.ProviderIds.Imdb && idsSet.has(movie.ProviderIds.Imdb)) {
|
||||
matchedMovies.push(movie);
|
||||
}
|
||||
}
|
||||
imdbTop250Movies = matchedMovies;
|
||||
const idsSet = new Set(imdbIds);
|
||||
imdbTop250Movies = allMovies.filter(
|
||||
(movie) => movie.ProviderIds?.Imdb && idsSet.has(movie.ProviderIds.Imdb)
|
||||
);
|
||||
}
|
||||
const userId = ApiClient.getCurrentUserId();
|
||||
const userId = window.ApiClient.getCurrentUserId();
|
||||
await cache.set(cacheName, imdbTop250Movies, userId, IMDB_TOP_250_CACHE_TTL);
|
||||
return imdbTop250Movies || [];
|
||||
}
|
||||
|
||||
async function getMovies() {
|
||||
if (!movies) {
|
||||
movies = await fetchAndCacheMovies();
|
||||
async function initialize() {
|
||||
await getMovies();
|
||||
if (isCacheComplete() && getLastDateFetched()) {
|
||||
await syncMoviesCache();
|
||||
}
|
||||
return movies || [];
|
||||
}
|
||||
|
||||
// Expose API
|
||||
async function waitForLoginReady(maxWaitMs = 60000) {
|
||||
const start = Date.now();
|
||||
while (!window.userHelper?.waitForLogin) {
|
||||
if (Date.now() - start >= maxWaitMs) {
|
||||
ERR('Timed out waiting for userHelper');
|
||||
return false;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
}
|
||||
return window.userHelper.waitForLogin(maxWaitMs);
|
||||
}
|
||||
|
||||
function yieldToMain() {
|
||||
return new Promise((resolve) => {
|
||||
if (typeof requestIdleCallback === 'function') {
|
||||
requestIdleCallback(() => resolve(), { timeout: 500 });
|
||||
} else {
|
||||
requestAnimationFrame(() => setTimeout(resolve, 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let bootstrapStarted = false;
|
||||
let bootstrapScheduled = false;
|
||||
|
||||
async function bootstrap() {
|
||||
if (bootstrapStarted) return;
|
||||
bootstrapStarted = true;
|
||||
try {
|
||||
await waitForLoginReady();
|
||||
await initialize();
|
||||
} catch (e) {
|
||||
ERR('Bootstrap failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Defer crawl until home has painted (or 3s after login) so home getQuery is not starved. */
|
||||
function scheduleBootstrap() {
|
||||
if (bootstrapScheduled) return;
|
||||
bootstrapScheduled = true;
|
||||
|
||||
const start = () => { bootstrap(); };
|
||||
|
||||
const onPainted = () => {
|
||||
document.removeEventListener('kefinTweaksHomePainted', onPainted);
|
||||
start();
|
||||
};
|
||||
document.addEventListener('kefinTweaksHomePainted', onPainted);
|
||||
|
||||
waitForLoginReady().then((ok) => {
|
||||
if (!ok) {
|
||||
document.removeEventListener('kefinTweaksHomePainted', onPainted);
|
||||
start();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
document.removeEventListener('kefinTweaksHomePainted', onPainted);
|
||||
start();
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
window.MoviesCache = {
|
||||
init: initialize,
|
||||
getMovies: getMovies,
|
||||
getImdbTop250Movies: getImdbTop250Movies,
|
||||
// Also expose the fetcher if we need to force refresh
|
||||
getMovies,
|
||||
getImdbTop250Movies,
|
||||
getMoviesByProviderIds,
|
||||
syncMoviesCache,
|
||||
fetchAndCacheMovies,
|
||||
isComplete: isCacheComplete,
|
||||
getLastDateFetched,
|
||||
bootstrap,
|
||||
scheduleBootstrap,
|
||||
refresh: async () => {
|
||||
const cache = window.IndexedDBCache;
|
||||
cache.delete('movies_library');
|
||||
movies = await fetchAndCacheMovies();
|
||||
return movies;
|
||||
const userId = window.ApiClient?.getCurrentUserId?.();
|
||||
if (cache) {
|
||||
await cache.clear(CACHE_NAME, userId);
|
||||
await cache.clear(PARTIAL_CACHE_NAME, userId);
|
||||
}
|
||||
movies = null;
|
||||
isComplete = false;
|
||||
providerIndex = null;
|
||||
imdbTop250Movies = null;
|
||||
return fetchAndCacheMovies();
|
||||
}
|
||||
};
|
||||
|
||||
scheduleBootstrap();
|
||||
LOG('Module loaded');
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user