fix(trakt lists): adds support for official trakt lists

correct validation and helper text to support 'official' trakt lists

fix #49
This commit is contained in:
Tom Wheeler
2025-09-03 19:55:53 +12:00
parent edb93fecdc
commit d71862c637
7 changed files with 38 additions and 25 deletions
+3 -3
View File
@@ -338,7 +338,7 @@ components:
nullable: true
traktCustomListUrl:
type: string
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name)"
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)"
example: "https://trakt.tv/users/username/lists/my-list"
nullable: true
tmdbCustomListUrl:
@@ -638,7 +638,7 @@ components:
nullable: true
traktCustomListUrl:
type: string
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name)"
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)"
example: "https://trakt.tv/users/username/lists/my-list"
nullable: true
tmdbCustomListUrl:
@@ -906,7 +906,7 @@ components:
nullable: true
traktCustomListUrl:
type: string
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name)"
description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)"
example: "https://trakt.tv/users/username/lists/my-list"
nullable: true
tmdbCustomListUrl:
+22 -13
View File
@@ -350,26 +350,35 @@ class TraktAPI {
limit = 1000
): Promise<TraktListResponse[]> {
try {
// Parse the URL to extract username and list slug
// Expected format: https://trakt.tv/users/{username}/lists/{list-slug}
const urlMatch = listUrl.match(
// Parse the URL to extract username and list slug or official list slug
// Expected formats:
// - https://trakt.tv/users/{username}/lists/{list-slug}
// - https://trakt.tv/lists/official/{collection-name}
const userListMatch = listUrl.match(
/trakt\.tv\/users\/([^/]+)\/lists\/([^/?]+)/
);
if (!urlMatch) {
const officialListMatch = listUrl.match(
/trakt\.tv\/lists\/official\/([^/?]+)/
);
let apiPath: string;
if (userListMatch) {
const [, username, listSlug] = userListMatch;
apiPath = `/users/${username}/lists/${listSlug}/items`;
} else if (officialListMatch) {
const [, collectionSlug] = officialListMatch;
apiPath = `/lists/official/${collectionSlug}/items`;
} else {
throw new Error(
'Invalid Trakt list URL format. Expected: https://trakt.tv/users/{username}/lists/{list-name}'
'Invalid Trakt list URL format. Expected: https://trakt.tv/users/{username}/lists/{list-name} or https://trakt.tv/lists/official/{collection-name}'
);
}
const [, username, listSlug] = urlMatch;
return await this.retryRequest(async () => {
const response = await this.axios.get<TraktListResponse[]>(
`/users/${username}/lists/${listSlug}/items`,
{
params: { limit },
}
);
const response = await this.axios.get<TraktListResponse[]>(apiPath, {
params: { limit },
});
return response.data;
});
} catch (e) {
+1 -1
View File
@@ -76,7 +76,7 @@ export interface CollectionConfig {
readonly seasonsPerShowLimit?: number; // Limit each TV show to only the first X seasons (0 = all seasons)
readonly maxPositionToProcess?: number; // Only process items in positions 1-X of the list (0 = no limit)
// Trakt custom list fields
readonly traktCustomListUrl?: string; // Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name)
readonly traktCustomListUrl?: string; // Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)
// TMDb custom list fields
readonly tmdbCustomListUrl?: string; // Custom TMDb list/collection URL (e.g., https://www.themoviedb.org/list/123456)
// IMDb custom list fields
+5 -2
View File
@@ -96,11 +96,14 @@ function validateExternalUrl(
// Validate URL patterns for each service
switch (type) {
case 'trakt':
if (!urlObj.pathname.match(/^\/users\/[^/]+\/lists\/[^/?]+\/?$/)) {
if (
!urlObj.pathname.match(/^\/users\/[^/]+\/lists\/[^/?]+\/?$/) &&
!urlObj.pathname.match(/^\/lists\/official\/[^/?]+\/?$/)
) {
return {
isValid: false,
error:
'Invalid Trakt list URL format. Expected: https://trakt.tv/users/username/lists/listname',
'Invalid Trakt list URL format. Expected: https://trakt.tv/users/username/lists/listname or https://trakt.tv/lists/official/collection-name',
};
}
break;
@@ -98,7 +98,7 @@ const CustomUrlSection = ({
type="url"
id="traktCustomListUrl"
name="traktCustomListUrl"
placeholder="https://trakt.tv/users/username/lists/listname"
placeholder="https://trakt.tv/users/username/lists/listname or https://trakt.tv/lists/official/collection-name"
className="flex-1 rounded-md border border-gray-600 bg-gray-700 px-3 py-2 text-white placeholder-gray-400 focus:border-orange-500 focus:outline-none focus:ring-2 focus:ring-orange-500"
/>
{fetchTraktTitle && (
@@ -120,7 +120,8 @@ const CustomUrlSection = ({
className="mt-1 text-sm text-red-500"
/>
<p className="mt-1 text-xs text-gray-400">
Example: https://trakt.tv/users/username/lists/listname
Examples: https://trakt.tv/users/username/lists/listname or
https://trakt.tv/lists/official/jurassic-park-collection
</p>
</div>
);
@@ -178,8 +178,8 @@ const CollectionFormConfigForm = ({
schema
.required('Trakt list URL is required')
.matches(
/trakt\.tv\/users\/[^/]+\/lists\/[^/?]+/,
'Please enter a valid Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name)'
/trakt\.tv\/(users\/[^/]+\/lists\/[^/?]+|lists\/official\/[^/?]+)/,
'Please enter a valid Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)'
),
otherwise: (schema) => schema,
}),
+2 -2
View File
@@ -75,8 +75,8 @@ const customUrlValidations = {
schema
.required('Trakt list URL is required')
.matches(
/trakt\.tv\/users\/[^/]+\/lists\/[^/?]+/,
'Please enter a valid Trakt list URL (e.g., https://trakt.tv/users/username/lists/listname)'
/trakt\.tv\/(users\/[^/]+\/lists\/[^/?]+|lists\/official\/[^/?]+)/,
'Please enter a valid Trakt list URL (e.g., https://trakt.tv/users/username/lists/listname or https://trakt.tv/lists/official/collection-name)'
),
otherwise: (schema) => schema,
}),