mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-31 19:59:31 -05:00
Compare commits
7 Commits
preview-is
...
fix-librar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
044f9e667e | ||
|
|
a276c51ce7 | ||
|
|
7ab40cbbf3 | ||
|
|
6293dc2b70 | ||
|
|
016c50680c | ||
|
|
525537b4ad | ||
|
|
fa3e99a931 |
@@ -9,6 +9,9 @@ export interface JellyfinUserResponse {
|
|||||||
ServerId: string;
|
ServerId: string;
|
||||||
ServerName: string;
|
ServerName: string;
|
||||||
Id: string;
|
Id: string;
|
||||||
|
Configuration: {
|
||||||
|
GroupedFolders: string[];
|
||||||
|
};
|
||||||
Policy: {
|
Policy: {
|
||||||
IsAdministrator: boolean;
|
IsAdministrator: boolean;
|
||||||
};
|
};
|
||||||
@@ -24,6 +27,13 @@ export interface JellyfinUserListResponse {
|
|||||||
users: JellyfinUserResponse[];
|
users: JellyfinUserResponse[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface JellyfinMediaFolder {
|
||||||
|
Name: string;
|
||||||
|
Id: string;
|
||||||
|
Type: string;
|
||||||
|
CollectionType: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface JellyfinLibrary {
|
export interface JellyfinLibrary {
|
||||||
type: 'show' | 'movie';
|
type: 'show' | 'movie';
|
||||||
key: string;
|
key: string;
|
||||||
@@ -175,33 +185,18 @@ class JellyfinAPI {
|
|||||||
|
|
||||||
public async getLibraries(): Promise<JellyfinLibrary[]> {
|
public async getLibraries(): Promise<JellyfinLibrary[]> {
|
||||||
try {
|
try {
|
||||||
// TODO: Try to fix automatic grouping without fucking up LDAP users
|
const mediaFolders = await this.axios.get<any>(`/Library/MediaFolders`);
|
||||||
// const libraries = await this.axios.get<any>('/Library/VirtualFolders');
|
|
||||||
|
|
||||||
const account = await this.axios.get<any>(
|
return this.mapLibraries(mediaFolders.data.Items);
|
||||||
|
} catch (mediaFoldersError) {
|
||||||
|
// fallback to user views to get libraries
|
||||||
|
// this only affects LDAP users
|
||||||
|
try {
|
||||||
|
const mediaFolders = await this.axios.get<any>(
|
||||||
`/Users/${this.userId ?? 'Me'}/Views`
|
`/Users/${this.userId ?? 'Me'}/Views`
|
||||||
);
|
);
|
||||||
|
|
||||||
const response: JellyfinLibrary[] = account.data.Items.filter(
|
return this.mapLibraries(mediaFolders.data.Items);
|
||||||
(Item: any) => {
|
|
||||||
return (
|
|
||||||
Item.Type === 'CollectionFolder' &&
|
|
||||||
Item.CollectionType !== 'music' &&
|
|
||||||
Item.CollectionType !== 'books' &&
|
|
||||||
Item.CollectionType !== 'musicvideos' &&
|
|
||||||
Item.CollectionType !== 'homevideos'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
).map((Item: any) => {
|
|
||||||
return <JellyfinLibrary>{
|
|
||||||
key: Item.Id,
|
|
||||||
title: Item.Name,
|
|
||||||
type: Item.CollectionType === 'movies' ? 'movie' : 'show',
|
|
||||||
agent: 'jellyfin',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Something went wrong while getting libraries from the Jellyfin server: ${e.message}`,
|
`Something went wrong while getting libraries from the Jellyfin server: ${e.message}`,
|
||||||
@@ -210,11 +205,38 @@ class JellyfinAPI {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapLibraries(mediaFolders: JellyfinMediaFolder[]): JellyfinLibrary[] {
|
||||||
|
const excludedTypes = [
|
||||||
|
'music',
|
||||||
|
'books',
|
||||||
|
'musicvideos',
|
||||||
|
'homevideos',
|
||||||
|
'boxsets',
|
||||||
|
];
|
||||||
|
|
||||||
|
return mediaFolders
|
||||||
|
.filter((Item: JellyfinMediaFolder) => {
|
||||||
|
return (
|
||||||
|
Item.Type === 'CollectionFolder' &&
|
||||||
|
!excludedTypes.includes(Item.CollectionType)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((Item: JellyfinMediaFolder) => {
|
||||||
|
return <JellyfinLibrary>{
|
||||||
|
key: Item.Id,
|
||||||
|
title: Item.Name,
|
||||||
|
type: Item.CollectionType === 'movies' ? 'movie' : 'show',
|
||||||
|
agent: 'jellyfin',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getLibraryContents(id: string): Promise<JellyfinLibraryItem[]> {
|
public async getLibraryContents(id: string): Promise<JellyfinLibraryItem[]> {
|
||||||
try {
|
try {
|
||||||
const contents = await this.axios.get<any>(
|
const contents = await this.axios.get<any>(
|
||||||
`/Users/${this.userId}/Items?SortBy=SortName&SortOrder=Ascending&IncludeItemTypes=Series,Movie,Others&Recursive=true&StartIndex=0&ParentId=${id}`
|
`/Users/${this.userId}/Items?SortBy=SortName&SortOrder=Ascending&IncludeItemTypes=Series,Movie,Others&Recursive=true&StartIndex=0&ParentId=${id}&collapseBoxSetItems=false`
|
||||||
);
|
);
|
||||||
|
|
||||||
return contents.data.Items.filter(
|
return contents.data.Items.filter(
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ settingsRoutes.post('/jellyfin', (req, res) => {
|
|||||||
return res.status(200).json(settings.jellyfin);
|
return res.status(200).json(settings.jellyfin);
|
||||||
});
|
});
|
||||||
|
|
||||||
settingsRoutes.get('/jellyfin/library', async (req, res) => {
|
settingsRoutes.get('/jellyfin/library', async (req, res, next) => {
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
|
|
||||||
if (req.query.sync) {
|
if (req.query.sync) {
|
||||||
@@ -281,6 +281,19 @@ settingsRoutes.get('/jellyfin/library', async (req, res) => {
|
|||||||
|
|
||||||
const libraries = await jellyfinClient.getLibraries();
|
const libraries = await jellyfinClient.getLibraries();
|
||||||
|
|
||||||
|
if (libraries.length === 0) {
|
||||||
|
// Check if no libraries are found due to the fallback to user views
|
||||||
|
// This only affects LDAP users
|
||||||
|
const account = await jellyfinClient.getUser();
|
||||||
|
|
||||||
|
// Automatic Library grouping is not supported when user views are used to get library
|
||||||
|
if (account.Configuration.GroupedFolders.length > 0) {
|
||||||
|
return next({ status: 501, message: 'SYNC_ERROR_GROUPED_FOLDERS' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({ status: 404, message: 'SYNC_ERROR_NO_LIBRARIES' });
|
||||||
|
}
|
||||||
|
|
||||||
const newLibraries: Library[] = libraries.map((library) => {
|
const newLibraries: Library[] = libraries.map((library) => {
|
||||||
const existing = settings.jellyfin.libraries.find(
|
const existing = settings.jellyfin.libraries.find(
|
||||||
(l) => l.id === library.key && l.name === library.title
|
(l) => l.id === library.key && l.name === library.title
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ const messages = defineMessages({
|
|||||||
externalUrl: 'External URL',
|
externalUrl: 'External URL',
|
||||||
internalUrl: 'Internal URL',
|
internalUrl: 'Internal URL',
|
||||||
jellyfinForgotPasswordUrl: 'Forgot Password URL',
|
jellyfinForgotPasswordUrl: 'Forgot Password URL',
|
||||||
|
jellyfinSyncFailedNoLibrariesFound: 'No libraries were found',
|
||||||
|
jellyfinSyncFailedAutomaticGroupedFolders:
|
||||||
|
'Custom authentication with Automatic Library Grouping not supported',
|
||||||
|
jellyfinSyncFailedGenericError:
|
||||||
|
'Something went wrong while syncing libraries',
|
||||||
validationUrl: 'You must provide a valid URL',
|
validationUrl: 'You must provide a valid URL',
|
||||||
syncing: 'Syncing',
|
syncing: 'Syncing',
|
||||||
syncJellyfin: 'Sync Libraries',
|
syncJellyfin: 'Sync Libraries',
|
||||||
@@ -70,6 +75,7 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
showAdvancedSettings,
|
showAdvancedSettings,
|
||||||
}) => {
|
}) => {
|
||||||
const [isSyncing, setIsSyncing] = useState(false);
|
const [isSyncing, setIsSyncing] = useState(false);
|
||||||
|
const toasts = useToasts();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -117,11 +123,43 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
params.enable = activeLibraries.join(',');
|
params.enable = activeLibraries.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
await axios.get('/api/v1/settings/jellyfin/library', {
|
await axios.get('/api/v1/settings/jellyfin/library', {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
setIsSyncing(false);
|
setIsSyncing(false);
|
||||||
revalidate();
|
revalidate();
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response.data.message === 'SYNC_ERROR_GROUPED_FOLDERS') {
|
||||||
|
toasts.addToast(
|
||||||
|
intl.formatMessage(
|
||||||
|
messages.jellyfinSyncFailedAutomaticGroupedFolders
|
||||||
|
),
|
||||||
|
{
|
||||||
|
autoDismiss: true,
|
||||||
|
appearance: 'warning',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else if (e.response.data.message === 'SYNC_ERROR_NO_LIBRARIES') {
|
||||||
|
toasts.addToast(
|
||||||
|
intl.formatMessage(messages.jellyfinSyncFailedNoLibrariesFound),
|
||||||
|
{
|
||||||
|
autoDismiss: true,
|
||||||
|
appearance: 'error',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toasts.addToast(
|
||||||
|
intl.formatMessage(messages.jellyfinSyncFailedGenericError),
|
||||||
|
{
|
||||||
|
autoDismiss: true,
|
||||||
|
appearance: 'error',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setIsSyncing(false);
|
||||||
|
revalidate();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const startScan = async () => {
|
const startScan = async () => {
|
||||||
|
|||||||
@@ -946,6 +946,9 @@
|
|||||||
"components.Settings.jellyfinlibrariesDescription": "The libraries {mediaServerName} scans for titles. Click the button below if no libraries are listed.",
|
"components.Settings.jellyfinlibrariesDescription": "The libraries {mediaServerName} scans for titles. Click the button below if no libraries are listed.",
|
||||||
"components.Settings.jellyfinsettings": "{mediaServerName} Settings",
|
"components.Settings.jellyfinsettings": "{mediaServerName} Settings",
|
||||||
"components.Settings.jellyfinsettingsDescription": "Configure the settings for your {mediaServerName} server. {mediaServerName} scans your {mediaServerName} libraries to see what content is available.",
|
"components.Settings.jellyfinsettingsDescription": "Configure the settings for your {mediaServerName} server. {mediaServerName} scans your {mediaServerName} libraries to see what content is available.",
|
||||||
|
"components.Settings.jellyfinSyncFailedNoLibrariesFound": "No libraries were found",
|
||||||
|
"components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "Custom authentication with Automatic Library Grouping not supported",
|
||||||
|
"components.Settings.jellyfinSyncFailedGenericError": "Something went wrong while syncing libraries",
|
||||||
"components.Settings.librariesRemaining": "Libraries Remaining: {count}",
|
"components.Settings.librariesRemaining": "Libraries Remaining: {count}",
|
||||||
"components.Settings.manualscan": "Manual Library Scan",
|
"components.Settings.manualscan": "Manual Library Scan",
|
||||||
"components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
|
"components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
|
||||||
|
|||||||
Reference in New Issue
Block a user