mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-02-24 04:52:29 +00:00
184 lines
5.5 KiB
TypeScript
184 lines
5.5 KiB
TypeScript
import { storage } from "@/utils/mmkv";
|
|
import { Jellyfin } from "@jellyfin/sdk";
|
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
|
import { getItemsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
|
import * as Notifications from "expo-notifications";
|
|
import { Platform } from "react-native";
|
|
import { getDeviceName } from "react-native-device-info";
|
|
import { getOrSetDeviceId } from "./device";
|
|
|
|
export const RECENTLY_ADDED_SENT_NOTIFICATIONS_ITEM_IDS_KEY =
|
|
"notification_send_for_item_ids";
|
|
|
|
const acceptedIemTypes = ["Movie", "Episode", "Series"];
|
|
|
|
async function sendNewItemNotification(item: BaseItemDto) {
|
|
if (Platform.isTV) return;
|
|
if (!item.Type) return;
|
|
|
|
if (!acceptedIemTypes.includes(item.Type)) return;
|
|
|
|
if (item.Type === "Movie")
|
|
await Notifications.scheduleNotificationAsync({
|
|
content: {
|
|
title: `New Movie Added`,
|
|
body: `${item.Name} (${item.ProductionYear})`,
|
|
},
|
|
trigger: null,
|
|
});
|
|
else if (item.Type === "Episode")
|
|
await Notifications.scheduleNotificationAsync({
|
|
content: {
|
|
title: `New Episode Added`,
|
|
body: `${item.SeriesName} - ${item.Name}`,
|
|
},
|
|
trigger: null,
|
|
});
|
|
else if (item.Type === "Series")
|
|
await Notifications.scheduleNotificationAsync({
|
|
content: {
|
|
title: `New Series Added`,
|
|
body: `${item.Name} (${item.ProductionYear})`,
|
|
},
|
|
trigger: null,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetches recently added items from Jellyfin and sends notifications for new content.
|
|
*
|
|
* This function performs the following operations:
|
|
* 1. Retrieves previously notified item IDs from storage
|
|
* 2. Connects to Jellyfin server using provided credentials
|
|
* 3. Fetches 5 most recent episodes and 5 most recent movies
|
|
* 4. Checks for new items that haven't been notified before
|
|
* 5. Sends notifications for new items
|
|
* 6. Updates storage with new item IDs
|
|
*
|
|
* Note: On first run (when no previous notifications exist), it will store all
|
|
* current items without sending notifications to avoid mass-notifications.
|
|
*
|
|
* @param userId - The Jellyfin user ID to fetch items for
|
|
* @param basePath - The base URL of the Jellyfin server
|
|
* @param token - The authentication token for the Jellyfin server
|
|
*/
|
|
export async function fetchAndStoreRecentlyAdded(
|
|
userId: string,
|
|
basePath: string,
|
|
token: string
|
|
) {
|
|
try {
|
|
const deviceName = await getDeviceName();
|
|
const id = getOrSetDeviceId();
|
|
|
|
// Get stored items
|
|
const _alreadySentItemIds = storage.getString(
|
|
RECENTLY_ADDED_SENT_NOTIFICATIONS_ITEM_IDS_KEY
|
|
);
|
|
const alreadySentItemIds: string[] = _alreadySentItemIds
|
|
? JSON.parse(_alreadySentItemIds)
|
|
: [];
|
|
|
|
console.log(
|
|
"fetchAndStoreRecentlyAdded ~ notifications stored:",
|
|
alreadySentItemIds.length
|
|
);
|
|
|
|
const jellyfin = new Jellyfin({
|
|
clientInfo: { name: "Streamyfin", version: "0.27.0" },
|
|
deviceInfo: {
|
|
name: deviceName,
|
|
id,
|
|
},
|
|
});
|
|
|
|
const api = jellyfin?.createApi(basePath, token);
|
|
|
|
const response1 = await getItemsApi(api).getItems({
|
|
userId: userId,
|
|
limit: 10,
|
|
recursive: true,
|
|
includeItemTypes: ["Episode"],
|
|
sortOrder: ["Descending"],
|
|
sortBy: ["DateCreated"],
|
|
});
|
|
const response2 = await getItemsApi(api).getItems({
|
|
userId: userId,
|
|
limit: 10,
|
|
recursive: true,
|
|
includeItemTypes: ["Movie"],
|
|
sortOrder: ["Descending"],
|
|
sortBy: ["DateCreated"],
|
|
});
|
|
const response3 = await getItemsApi(api).getItems({
|
|
userId: userId,
|
|
limit: 10,
|
|
recursive: true,
|
|
includeItemTypes: ["Series"],
|
|
sortOrder: ["Descending"],
|
|
sortBy: ["DateCreated"],
|
|
});
|
|
|
|
const newEpisodes =
|
|
response1.data.Items?.map((item) => ({
|
|
Id: item.Id,
|
|
Name: item.Name,
|
|
DateCreated: item.DateCreated,
|
|
Type: item.Type,
|
|
})) ?? [];
|
|
|
|
const newMovies =
|
|
response2.data.Items?.map((item) => ({
|
|
Id: item.Id,
|
|
Name: item.Name,
|
|
DateCreated: item.DateCreated,
|
|
Type: item.Type,
|
|
})) ?? [];
|
|
|
|
const newSeries =
|
|
response3.data.Items?.map((item) => ({
|
|
Id: item.Id,
|
|
Name: item.Name,
|
|
DateCreated: item.DateCreated,
|
|
Type: item.Type,
|
|
})) ?? [];
|
|
|
|
const newIds: string[] = [];
|
|
const items = [...newEpisodes, ...newMovies, ...newSeries];
|
|
|
|
// Don't send initial mass-notifications if there are no previously sent notifications
|
|
if (alreadySentItemIds.length === 0) {
|
|
// Store all items as sent (since these items are already in the users library)
|
|
storage.set(
|
|
RECENTLY_ADDED_SENT_NOTIFICATIONS_ITEM_IDS_KEY,
|
|
JSON.stringify(items.map((item) => item.Id))
|
|
);
|
|
return;
|
|
} else {
|
|
// Only send notifications for items that haven't been sent yet
|
|
for (const newItem of items) {
|
|
const alreadySentNotificationFor = alreadySentItemIds.some(
|
|
(id) => id === newItem.Id
|
|
);
|
|
|
|
if (!alreadySentNotificationFor) {
|
|
const fullItem = await getUserLibraryApi(api).getItem({
|
|
itemId: newItem.Id!,
|
|
userId: userId,
|
|
});
|
|
|
|
await sendNewItemNotification(fullItem.data);
|
|
newIds.push(newItem.Id!);
|
|
}
|
|
}
|
|
// Store all new items as sent, so that we don't send notifications for them again
|
|
storage.set(
|
|
RECENTLY_ADDED_SENT_NOTIFICATIONS_ITEM_IDS_KEY,
|
|
JSON.stringify([...new Set([...alreadySentItemIds, ...newIds])])
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching recently added items:", error);
|
|
}
|
|
}
|