Skip to content

Commit

Permalink
Begin implementing automatic sync
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelgomesxyz committed May 3, 2021
1 parent 6027bb7 commit b1eecd5
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 33 deletions.
2 changes: 0 additions & 2 deletions src/api/TmdbApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { RequestException, Requests } from '../common/Requests';
import { Item } from '../models/Item';
import { TraktItem } from '../models/TraktItem';
import { secrets } from '../secrets';
import { getSyncStore } from '../streaming-services/common/common';
import { StreamingServiceId } from '../streaming-services/streaming-services';

export interface TmdbConfigResponse {
Expand Down Expand Up @@ -205,7 +204,6 @@ class _TmdbApi {
for (const item of missingItems) {
item.imageUrl = item.imageUrl ?? this.PLACEHOLDER_IMAGE;
}
await getSyncStore(serviceId).update();
};

loadItemImage = async (item: Item): Promise<Item> => {
Expand Down
2 changes: 0 additions & 2 deletions src/api/WrongItemApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { CacheValues } from '../common/Cache';
import { Messaging } from '../common/Messaging';
import { Requests } from '../common/Requests';
import { CorrectionSuggestion, Item } from '../models/Item';
import { getSyncStore } from '../streaming-services/common/common';
import { StreamingServiceId } from '../streaming-services/streaming-services';

class _WrongItemApi {
Expand Down Expand Up @@ -74,7 +73,6 @@ class _WrongItemApi {
for (const item of missingItems) {
item.correctionSuggestions = item.correctionSuggestions ?? null;
}
await getSyncStore(serviceId).update();
};

loadItemSuggestions = async (item: Item): Promise<Item> => {
Expand Down
3 changes: 3 additions & 0 deletions src/common/BrowserStorage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type StreamingServiceValue = {
autoSync: boolean;
autoSyncDays: number;
lastSync: number;
lastSyncId: string;
};

export type ThemeValue = 'light' | 'dark' | 'system';
Expand Down Expand Up @@ -178,6 +179,7 @@ class _BrowserStorage {
autoSync: false,
autoSyncDays: 7,
lastSync: 0,
lastSyncId: '',
},
])
) as Record<StreamingServiceId, StreamingServiceValue>,
Expand Down Expand Up @@ -275,6 +277,7 @@ class _BrowserStorage {
autoSync: false,
autoSyncDays: 7,
lastSync: 0,
lastSyncId: '',
},
])
) as Record<StreamingServiceId, StreamingServiceValue>;
Expand Down
22 changes: 20 additions & 2 deletions src/modules/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { RequestDetails, Requests } from '../../common/Requests';
import { Shared } from '../../common/Shared';
import { Item } from '../../models/Item';
import { TraktItem } from '../../models/TraktItem';
import { AutoSync } from '../../streaming-services/common/AutoSync';
import { StreamingServiceId, streamingServices } from '../../streaming-services/streaming-services';

export type MessageRequest =
Expand Down Expand Up @@ -130,10 +131,27 @@ const init = async () => {
browser.tabs.onRemoved.addListener((tabId) => void onTabRemoved(tabId));
browser.storage.onChanged.addListener(onStorageChanged);
if (storage.options?.streamingServices) {
const scrobblerEnabled = (Object.entries(storage.options.streamingServices) as [
const streamingServiceEntries = Object.entries(storage.options.streamingServices) as [
StreamingServiceId,
StreamingServiceValue
][]).some(
][];

const now = Math.trunc(Date.now() / 1e3);
const servicesToSync = streamingServiceEntries.filter(
([streamingServiceId, value]) =>
streamingServices[streamingServiceId].hasSync &&
streamingServices[streamingServiceId].hasAutoSync &&
value.sync &&
value.autoSync &&
value.autoSyncDays > 0 &&
value.lastSync > 0 &&
now - value.lastSync >= value.autoSyncDays * 86400
);
if (servicesToSync.length > 0) {
void AutoSync.sync(servicesToSync, now);
}

const scrobblerEnabled = streamingServiceEntries.some(
([streamingServiceId, value]) =>
streamingServices[streamingServiceId].hasScrobbler && value.scrobble
);
Expand Down
5 changes: 4 additions & 1 deletion src/modules/options/OptionsApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ export const OptionsApp: React.FC = () => {
if (streamingServiceValue.sync && !service.hasSync) {
streamingServiceValue.sync = false;
}
if (streamingServiceValue.autoSync && (!service.hasSync || !streamingServiceValue.sync)) {
if (
streamingServiceValue.autoSync &&
(!service.hasSync || !service.hasAutoSync || !streamingServiceValue.sync)
) {
streamingServiceValue.autoSync = false;
}
if (streamingServiceValue.scrobble || streamingServiceValue.sync) {
Expand Down
4 changes: 2 additions & 2 deletions src/modules/options/components/StreamingServiceOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ export const StreamingServiceOption: React.FC<StreamingServiceOptionProps> = (
<Switch
checked={value.autoSync}
color="primary"
disabled={!service.hasSync || !value.sync}
disabled={!service.hasSync || !service.hasAutoSync || !value.sync}
edge="start"
onChange={onAutoSyncChange}
/>
<TextField
className="options-grid-item--text-field"
disabled={!service.hasSync || !value.sync || !value.autoSync}
disabled={!service.hasSync || !service.hasAutoSync || !value.sync || !value.autoSync}
label={I18N.translate('days')}
size="small"
title={I18N.translate('autoSyncDescription', autoSyncDays.toString())}
Expand Down
4 changes: 1 addition & 3 deletions src/streaming-services/common/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { RequestException } from '../../common/Requests';
import { Item } from '../../models/Item';
import { TraktItem, TraktItemBase } from '../../models/TraktItem';
import { StreamingServiceId } from '../streaming-services';
import { getSyncStore } from './common';

export abstract class Api {
id: StreamingServiceId;
Expand All @@ -16,7 +15,7 @@ export abstract class Api {
this.id = id;
}

abstract loadHistory(itemsToLoad: number): Promise<void>;
abstract loadHistory(itemsToLoad: number, lastSync?: number, lastSyncId?: string): Promise<void>;

loadTraktHistory = async (items: Item[]) => {
const missingItems = items.filter((item) => typeof item.trakt === 'undefined');
Expand All @@ -38,7 +37,6 @@ export abstract class Api {
}
await Promise.all(promises);
await BrowserStorage.set({ traktCache }, false);
await getSyncStore(this.id).update();
} catch (err) {
if (!(err as RequestException).canceled) {
Errors.error('Failed to load Trakt history.', err);
Expand Down
66 changes: 66 additions & 0 deletions src/streaming-services/common/AutoSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { TraktSync } from '../../api/TraktSync';
import {
BrowserStorage,
Option,
StorageValuesOptions,
StreamingServiceValue,
} from '../../common/BrowserStorage';
import '../pages';
import { StreamingServiceId } from '../streaming-services';
import { getApi, getSyncStore } from './common';

const addOptionToSave = <K extends keyof StorageValuesOptions>(
optionsToSave: StorageValuesOptions,
option: Option<K>
) => {
optionsToSave[option.id] = option.value;
};

class _AutoSync {
sync = async (serviceEntries: [StreamingServiceId, StreamingServiceValue][], now: number) => {
const optionsToSave = {} as StorageValuesOptions;
const options = await BrowserStorage.getOptions();
const syncOptions = await BrowserStorage.getSyncOptions();

for (const [serviceId, serviceValue] of serviceEntries) {
try {
const api = getApi(serviceId);
const store = getSyncStore(serviceId);

await api.loadHistory(0, serviceValue.lastSync, serviceValue.lastSyncId);
options.streamingServices.value[serviceId].lastSync = now;

let items = store.data.items.filter(
(item) => !item.percentageWatched || item.percentageWatched >= 10.0
);

if (items.length === 0) {
continue;
}

await api.loadTraktHistory(items);

items = items
.filter((item) => item.trakt && !item.trakt.watchedAt)
.map((item) => ({ ...item, isSelected: true }));

if (items.length === 0) {
continue;
}

await TraktSync.sync(items, syncOptions.addWithReleaseDate.value);

options.streamingServices.value[serviceId].lastSyncId = items[0].id;
} catch (err) {
// Do nothing
}
}

for (const option of Object.values(options) as Option<keyof StorageValuesOptions>[]) {
addOptionToSave(optionsToSave, option);
}
await BrowserStorage.set({ options: optionsToSave }, true);
};
}

export const AutoSync = new _AutoSync();
26 changes: 21 additions & 5 deletions src/streaming-services/common/SyncPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { TraktSync } from '../../api/TraktSync';
import { WrongItemApi } from '../../api/WrongItemApi';
import {
BrowserStorage,
Option,
Options,
StorageValuesOptions,
StorageValuesSyncOptions,
SyncOptions,
} from '../../common/BrowserStorage';
Expand All @@ -31,7 +33,6 @@ import { HistoryList } from '../../modules/history/components/HistoryList';
import { HistoryOptionsList } from '../../modules/history/components/HistoryOptionsList';
import { StreamingServiceId } from '../streaming-services';
import { Api } from './Api';
import { getApi, getSyncStore } from './common';
import { SyncStore } from './SyncStore';

interface PageProps {
Expand Down Expand Up @@ -60,6 +61,13 @@ interface Content {
hasReachedEnd: boolean;
}

const addOptionToSave = <K extends keyof StorageValuesOptions>(
optionsToSave: StorageValuesOptions,
option: Option<K>
) => {
optionsToSave[option.id] = option.value;
};

export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
const { serviceId, serviceName, store, api } = props;

Expand Down Expand Up @@ -125,6 +133,13 @@ export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
isLoading: true,
}));
await TraktSync.sync(content.visibleItems, syncOptionsContent.options.addWithReleaseDate.value);
const optionsToSave = {} as StorageValuesOptions;
const options = await BrowserStorage.getOptions();
options.streamingServices.value[serviceId].lastSync = Math.trunc(Date.now() / 1e3);
for (const option of Object.values(options) as Option<keyof StorageValuesOptions>[]) {
addOptionToSave(optionsToSave, option);
}
await BrowserStorage.set({ options: optionsToSave }, true);
setContent((prevContent) => ({
...prevContent,
isLoading: false,
Expand Down Expand Up @@ -188,7 +203,7 @@ export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
if (!traktCache) {
traktCache = {};
}
await getApi(serviceId).loadTraktItemHistory(data.item, traktCache, {
await api.loadTraktItemHistory(data.item, traktCache, {
type: data.type,
traktId: data.traktId,
url: data.url,
Expand All @@ -205,7 +220,7 @@ export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
}
}
await BrowserStorage.set({ traktCache }, false);
await getSyncStore(serviceId).update();
await store.update();
};

const onMissingWatchedDateAdded = async (data: MissingWatchedDateAddedData): Promise<void> => {
Expand All @@ -227,7 +242,7 @@ export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
break;
// no-default
}
await getSyncStore(serviceId).update();
await store.update();
};

const onHistorySyncSuccess = async (data: HistorySyncSuccessData) => {
Expand Down Expand Up @@ -361,11 +376,12 @@ export const SyncPage: React.FC<PageProps> = (props: PageProps) => {
useEffect(() => {
const loadData = async () => {
try {
await getApi(serviceId).loadTraktHistory(content.visibleItems);
await api.loadTraktHistory(content.visibleItems);
await Promise.all([
WrongItemApi.loadSuggestions(serviceId, content.visibleItems),
TmdbApi.loadImages(serviceId, content.visibleItems),
]);
await store.update();
} catch (err) {
// Do nothing
}
Expand Down
27 changes: 23 additions & 4 deletions src/streaming-services/hbo-go/HboGoApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ class _HboGoApi extends Api {
.replace(/{ageRating}/i, '0');
};

loadHistory = async (itemsToLoad: number): Promise<void> => {
loadHistory = async (
itemsToLoad: number,
lastSync?: number,
lastSyncId?: string
): Promise<void> => {
try {
if (!this.isActivated) {
await this.activate();
Expand All @@ -221,16 +225,31 @@ class _HboGoApi extends Api {
if (responseJson) {
const responseItems = responseJson.Container[0]?.Contents.Items;
if (responseItems && responseItems.length > 0) {
itemsToLoad -= responseItems.length;
historyItems.push(...responseItems);
let filteredItems = [];
if (lastSyncId) {
for (const responseItem of responseItems) {
if (responseItem.Id && responseItem.Id !== lastSyncId) {
filteredItems.push(responseItem);
} else {
break;
}
}
if (filteredItems.length !== responseItems.length) {
hasReachedEnd = true;
}
} else {
filteredItems = responseItems;
}
itemsToLoad -= filteredItems.length;
historyItems.push(...filteredItems);
} else {
hasReachedEnd = true;
}
} else {
hasReachedEnd = true;
}
nextPage += 1;
} while (!hasReachedEnd && itemsToLoad > 0);
} while (!hasReachedEnd && (itemsToLoad > 0 || lastSyncId));
if (historyItems.length > 0) {
items = historyItems.map(this.parseHistoryItem);
}
Expand Down
24 changes: 20 additions & 4 deletions src/streaming-services/netflix/NetflixApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class _NetflixApi extends Api {
);
};

loadHistory = async (itemsToLoad: number) => {
loadHistory = async (itemsToLoad: number, lastSync?: number) => {
try {
if (!this.isActivated) {
await this.activate();
Expand All @@ -220,13 +220,28 @@ class _NetflixApi extends Api {
});
const responseJson = JSON.parse(responseText) as NetflixHistoryResponse;
if (responseJson && responseJson.viewedItems.length > 0) {
itemsToLoad -= responseJson.viewedItems.length;
historyItems.push(...responseJson.viewedItems);
let filteredItems = [];
if (lastSync) {
for (const viewedItem of responseJson.viewedItems) {
if (viewedItem.date && Math.trunc(viewedItem.date / 1e3) > lastSync) {
filteredItems.push(viewedItem);
} else {
break;
}
}
if (filteredItems.length !== responseJson.viewedItems.length) {
hasReachedEnd = true;
}
} else {
filteredItems = responseJson.viewedItems;
}
itemsToLoad -= filteredItems.length;
historyItems.push(...filteredItems);
} else {
hasReachedEnd = true;
}
nextPage += 1;
} while (!hasReachedEnd && itemsToLoad > 0);
} while (!hasReachedEnd && (itemsToLoad > 0 || lastSync));
if (historyItems.length > 0) {
const historyItemsWithMetadata = await this.getHistoryMetadata(historyItems);
items = historyItemsWithMetadata.map(this.parseHistoryItem);
Expand Down Expand Up @@ -292,6 +307,7 @@ class _NetflixApi extends Api {
let episode;
const isCollection = !historyItem.seasonDescriptor.includes('Season');
if (!isCollection) {
// TODO: Some items don't have a summary response (see Friends pilot).
season = historyItem.summary.season;
episode = historyItem.summary.episode;
}
Expand Down
Loading

0 comments on commit b1eecd5

Please sign in to comment.