refactor: switch from Fetch API to Axios
This commit is contained in:
@@ -17,8 +17,8 @@ class CoverArtArchive extends ExternalAPI {
|
||||
{
|
||||
nodeCache: cacheManager.getCache('covertartarchive').data,
|
||||
rateLimit: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 50,
|
||||
id: 'covertartarchive',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -16,8 +16,8 @@ class ListenBrainzAPI extends ExternalAPI {
|
||||
{
|
||||
nodeCache: cacheManager.getCache('listenbrainz').data,
|
||||
rateLimit: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 25,
|
||||
id: 'listenbrainz',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -25,44 +25,38 @@ class ListenBrainzAPI extends ExternalAPI {
|
||||
|
||||
public async getAlbum(mbid: string): Promise<LbAlbumDetails> {
|
||||
try {
|
||||
return await this.getRolling<LbAlbumDetails>(
|
||||
return await this.post<LbAlbumDetails>(
|
||||
`/album/${mbid}`,
|
||||
{},
|
||||
43200,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
baseURL: 'https://listenbrainz.org',
|
||||
},
|
||||
'https://listenbrainz.org'
|
||||
43200
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[ListenBrainz] Failed to fetch album details: ${e.message}`
|
||||
`[ListenBrainz] Failed to fetch album details: ${
|
||||
e instanceof Error ? e.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async getArtist(mbid: string): Promise<LbArtistDetails> {
|
||||
try {
|
||||
return await this.getRolling<LbArtistDetails>(
|
||||
return await this.post<LbArtistDetails>(
|
||||
`/artist/${mbid}`,
|
||||
{},
|
||||
43200,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
baseURL: 'https://listenbrainz.org',
|
||||
},
|
||||
'https://listenbrainz.org'
|
||||
43200
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[ListenBrainz] Failed to fetch artist details: ${e.message}`
|
||||
`[ListenBrainz] Failed to fetch artist details: ${
|
||||
e instanceof Error ? e.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -79,9 +73,11 @@ class ListenBrainzAPI extends ExternalAPI {
|
||||
return this.get<LbTopAlbumsResponse>(
|
||||
'/stats/sitewide/release-groups',
|
||||
{
|
||||
offset: offset.toString(),
|
||||
range,
|
||||
count: count.toString(),
|
||||
params: {
|
||||
offset: offset.toString(),
|
||||
range,
|
||||
count: count.toString(),
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
@@ -99,9 +95,11 @@ class ListenBrainzAPI extends ExternalAPI {
|
||||
return this.get<LbTopArtistsResponse>(
|
||||
'/stats/sitewide/artists',
|
||||
{
|
||||
offset: offset.toString(),
|
||||
range,
|
||||
count: count.toString(),
|
||||
params: {
|
||||
offset: offset.toString(),
|
||||
range,
|
||||
count: count.toString(),
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
@@ -121,10 +119,12 @@ class ListenBrainzAPI extends ExternalAPI {
|
||||
return this.get<LbFreshReleasesResponse>(
|
||||
'/explore/fresh-releases',
|
||||
{
|
||||
days: days.toString(),
|
||||
sort,
|
||||
offset: offset.toString(),
|
||||
count: count.toString(),
|
||||
params: {
|
||||
days: days.toString(),
|
||||
sort,
|
||||
offset: offset.toString(),
|
||||
count: count.toString(),
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ExternalAPI from '@server/api/externalapi';
|
||||
import cacheManager from '@server/lib/cache';
|
||||
import axios from 'axios';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import type { MbAlbumDetails, MbArtistDetails } from './interfaces';
|
||||
@@ -20,8 +21,8 @@ class MusicBrainz extends ExternalAPI {
|
||||
},
|
||||
nodeCache: cacheManager.getCache('musicbrainz').data,
|
||||
rateLimit: {
|
||||
maxRequests: 1,
|
||||
maxRPS: 1,
|
||||
id: 'musicbrainz',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -45,17 +46,23 @@ class MusicBrainz extends ExternalAPI {
|
||||
}>(
|
||||
'/release-group',
|
||||
{
|
||||
query,
|
||||
fmt: 'json',
|
||||
limit: limit.toString(),
|
||||
offset: offset.toString(),
|
||||
params: {
|
||||
query,
|
||||
fmt: 'json',
|
||||
limit: limit.toString(),
|
||||
offset: offset.toString(),
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
|
||||
return data['release-groups'];
|
||||
} catch (e) {
|
||||
throw new Error(`[MusicBrainz] Failed to search albums: ${e.message}`);
|
||||
throw new Error(
|
||||
`[MusicBrainz] Failed to search albums: ${
|
||||
e instanceof Error ? e.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,17 +84,23 @@ class MusicBrainz extends ExternalAPI {
|
||||
}>(
|
||||
'/artist',
|
||||
{
|
||||
query,
|
||||
fmt: 'json',
|
||||
limit: limit.toString(),
|
||||
offset: offset.toString(),
|
||||
params: {
|
||||
query,
|
||||
fmt: 'json',
|
||||
limit: limit.toString(),
|
||||
offset: offset.toString(),
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
|
||||
return data.artists;
|
||||
} catch (e) {
|
||||
throw new Error(`[MusicBrainz] Failed to search artists: ${e.message}`);
|
||||
throw new Error(
|
||||
`[MusicBrainz] Failed to search artists: ${
|
||||
e instanceof Error ? e.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +124,7 @@ class MusicBrainz extends ExternalAPI {
|
||||
try {
|
||||
const safeUrl = `https://musicbrainz.org/artist/${artistMbid}/wikipedia-extract`;
|
||||
|
||||
const response = await fetch(safeUrl, {
|
||||
const response = await axios.get(safeUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Language': language,
|
||||
@@ -120,11 +133,7 @@ class MusicBrainz extends ExternalAPI {
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = response.data;
|
||||
if (!data.wikipediaExtract || !data.wikipediaExtract.content) {
|
||||
return null;
|
||||
}
|
||||
@@ -161,8 +170,10 @@ class MusicBrainz extends ExternalAPI {
|
||||
}>(
|
||||
`/release/${releaseId}`,
|
||||
{
|
||||
inc: 'release-groups',
|
||||
fmt: 'json',
|
||||
params: {
|
||||
inc: 'release-groups',
|
||||
fmt: 'json',
|
||||
},
|
||||
},
|
||||
43200
|
||||
);
|
||||
@@ -170,7 +181,9 @@ class MusicBrainz extends ExternalAPI {
|
||||
return data['release-group']?.id ?? null;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[MusicBrainz] Failed to fetch release group: ${e.message}`
|
||||
`[MusicBrainz] Failed to fetch release group: ${
|
||||
e instanceof Error ? e.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +120,11 @@ export interface LidarrAlbumDetails {
|
||||
status: string;
|
||||
duration: number;
|
||||
trackCount: number;
|
||||
media: any[];
|
||||
media: unknown[];
|
||||
mediumCount: number;
|
||||
disambiguation: string;
|
||||
country: any[];
|
||||
label: any[];
|
||||
country: unknown[];
|
||||
label: unknown[];
|
||||
format: string;
|
||||
monitored: boolean;
|
||||
}[];
|
||||
@@ -136,8 +136,8 @@ export interface LidarrAlbumDetails {
|
||||
}[];
|
||||
artist: LidarrArtistDetails & {
|
||||
artistName: string;
|
||||
nextAlbum: any | null;
|
||||
lastAlbum: any | null;
|
||||
nextAlbum: unknown | null;
|
||||
lastAlbum: unknown | null;
|
||||
};
|
||||
images: LidarrImage[];
|
||||
links: {
|
||||
@@ -202,9 +202,9 @@ export interface LidarrAlbumOptions {
|
||||
mediumCount?: number;
|
||||
ratings?: LidarrRating;
|
||||
releaseDate?: string;
|
||||
releases: any[];
|
||||
releases: unknown[];
|
||||
genres: string[];
|
||||
media: any[];
|
||||
media: unknown[];
|
||||
artist: {
|
||||
status: string;
|
||||
ended: boolean;
|
||||
@@ -310,9 +310,11 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
|
||||
public async removeAlbum(albumId: number): Promise<void> {
|
||||
try {
|
||||
await this.delete(`/album/${albumId}`, {
|
||||
deleteFiles: 'true',
|
||||
addImportExclusion: 'false',
|
||||
await this.axios.delete(`/album/${albumId}`, {
|
||||
params: {
|
||||
deleteFiles: 'true',
|
||||
addImportExclusion: 'false',
|
||||
},
|
||||
});
|
||||
logger.info(`[Lidarr] Removed album ${albumId}`);
|
||||
} catch (e) {
|
||||
@@ -322,8 +324,10 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
|
||||
public async searchAlbum(mbid: string): Promise<LidarrAlbumResult[]> {
|
||||
try {
|
||||
const data = await this.get<LidarrAlbumResult[]>(`/search`, {
|
||||
term: `lidarr:${mbid}`,
|
||||
const data = await this.get<LidarrAlbumResult[]>('/search', {
|
||||
params: {
|
||||
term: `lidarr:${mbid}`,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
@@ -334,8 +338,10 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
public async addAlbum(options: LidarrAlbumOptions): Promise<LidarrAlbum> {
|
||||
try {
|
||||
const existingAlbums = await this.get<LidarrAlbum[]>('/album', {
|
||||
foreignAlbumId: options.foreignAlbumId,
|
||||
includeAllArtistAlbums: 'false',
|
||||
params: {
|
||||
foreignAlbumId: options.foreignAlbumId,
|
||||
includeAllArtistAlbums: 'false',
|
||||
},
|
||||
});
|
||||
|
||||
if (existingAlbums.length > 0 && existingAlbums[0].monitored) {
|
||||
@@ -358,7 +364,7 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
}
|
||||
);
|
||||
|
||||
const updatedAlbum = await this.put<LidarrAlbum>(
|
||||
const updatedAlbum = await this.axios.put<LidarrAlbum>(
|
||||
`/album/${existingAlbums[0].id}`,
|
||||
{
|
||||
...existingAlbums[0],
|
||||
@@ -368,10 +374,10 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
|
||||
await this.post('/command', {
|
||||
name: 'AlbumSearch',
|
||||
albumIds: [updatedAlbum.id],
|
||||
albumIds: [updatedAlbum.data.id],
|
||||
});
|
||||
|
||||
return updatedAlbum;
|
||||
return updatedAlbum.data;
|
||||
}
|
||||
|
||||
const data = await this.post<LidarrAlbum>('/album', {
|
||||
@@ -389,7 +395,9 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
|
||||
): Promise<LidarrAlbumResult[]> {
|
||||
try {
|
||||
const data = await this.get<LidarrAlbumResult[]>('/search', {
|
||||
term: `lidarr:${mbid}`,
|
||||
params: {
|
||||
term: `lidarr:${mbid}`,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
|
||||
@@ -18,8 +18,8 @@ class TheAudioDb extends ExternalAPI {
|
||||
{
|
||||
nodeCache: cacheManager.getCache('tadb').data,
|
||||
rateLimit: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 25,
|
||||
id: 'tadb',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -103,7 +103,7 @@ class TheAudioDb extends ExternalAPI {
|
||||
try {
|
||||
const data = await this.get<TadbArtistResponse>(
|
||||
`/${this.apiKey}/artist-mb.php`,
|
||||
{ i: id },
|
||||
{ params: { i: id } },
|
||||
this.CACHE_TTL
|
||||
);
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ class TmdbPersonMapper extends ExternalAPI {
|
||||
{
|
||||
nodeCache: cacheManager.getCache('tmdb').data,
|
||||
rateLimit: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 50,
|
||||
id: 'tmdb',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -135,10 +135,12 @@ class TmdbPersonMapper extends ExternalAPI {
|
||||
const searchResults = await this.get<TmdbSearchPersonResponse>(
|
||||
'/search/person',
|
||||
{
|
||||
query: cleanArtistName,
|
||||
page: '1',
|
||||
include_adult: 'false',
|
||||
language: 'en',
|
||||
params: {
|
||||
query: cleanArtistName,
|
||||
page: '1',
|
||||
include_adult: 'false',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
this.CACHE_TTL
|
||||
);
|
||||
@@ -316,10 +318,12 @@ class TmdbPersonMapper extends ExternalAPI {
|
||||
return await this.get<TmdbSearchPersonResponse>(
|
||||
'/search/person',
|
||||
{
|
||||
query: options.query,
|
||||
page: options.page?.toString() ?? '1',
|
||||
include_adult: options.includeAdult ? 'true' : 'false',
|
||||
language: options.language ?? 'en',
|
||||
params: {
|
||||
query: options.query,
|
||||
page: options.page?.toString() ?? '1',
|
||||
include_adult: options.includeAdult ? 'true' : 'false',
|
||||
language: options.language ?? 'en',
|
||||
},
|
||||
},
|
||||
this.CACHE_TTL
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Router } from 'express';
|
||||
const router = Router();
|
||||
const caaImageProxy = new ImageProxy('caa', 'https://archive.org/download', {
|
||||
rateLimitOptions: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 50,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Router } from 'express';
|
||||
const router = Router();
|
||||
const tmdbImageProxy = new ImageProxy('tmdb', 'https://image.tmdb.org', {
|
||||
rateLimitOptions: {
|
||||
maxRequests: 20,
|
||||
maxRPS: 50,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import Error from '@app/pages/_error';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import { ArrowRightCircleIcon, XCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { MediaStatus } from '@server/constants/media';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
@@ -394,14 +395,15 @@ const ArtistDetails = () => {
|
||||
|
||||
try {
|
||||
const pageSize = Math.min(data?.typeCounts?.[albumType] || 100, 1000);
|
||||
const response = await fetch(
|
||||
`/api/v1/artist/${artistId}?albumType=${encodeURIComponent(
|
||||
albumType
|
||||
)}&pageSize=${pageSize}`
|
||||
);
|
||||
const response = await axios.get(`/api/v1/artist/${artistId}`, {
|
||||
params: {
|
||||
albumType,
|
||||
pageSize,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
if (response.status === 200) {
|
||||
const responseData = response.data;
|
||||
const validAlbums = responseData.releaseGroups
|
||||
.filter((album: Album) => album && album.id)
|
||||
.map((album: Album) => ({
|
||||
|
||||
@@ -57,7 +57,9 @@ const BlacklistModal = ({
|
||||
if (!show) return;
|
||||
try {
|
||||
setError(null);
|
||||
const response = await axios.get(`/api/v1/${type}/${type === 'music' ? mbId : tmdbId}`);
|
||||
const response = await axios.get(
|
||||
`/api/v1/${type}/${type === 'music' ? mbId : tmdbId}`
|
||||
);
|
||||
setData(response.data);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
|
||||
@@ -33,6 +33,7 @@ import { MediaStatus, MediaType } from '@server/constants/media';
|
||||
import { MediaServerType } from '@server/constants/server';
|
||||
import type { MusicDetails as MusicDetailsType } from '@server/models/Music';
|
||||
import type { AlbumResult, ArtistResult } from '@server/models/Search';
|
||||
import axios from 'axios';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -217,56 +218,41 @@ const MusicDetails = ({ music }: MusicDetailsProps) => {
|
||||
const onClickWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
|
||||
const res = await fetch('/api/v1/watchlist', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
try {
|
||||
const { data } = await axios.post('/api/v1/watchlist', {
|
||||
mbId: music?.id,
|
||||
mediaType: MediaType.MUSIC,
|
||||
title: music?.title,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
if (data) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(messages.watchlistSuccess, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(messages.watchlistError), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
return;
|
||||
setToggleWatchlist((prevState) => !prevState);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(messages.watchlistSuccess, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
|
||||
setIsUpdating(false);
|
||||
setToggleWatchlist((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const onClickDeleteWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const res = await fetch(`/api/v1/watchlist/${music?.id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
const response = await axios.delete(`/api/v1/watchlist/${music?.id}`);
|
||||
|
||||
if (res.status === 204) {
|
||||
if (response.status === 204) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(messages.watchlistDeleted, {
|
||||
@@ -277,7 +263,7 @@ const MusicDetails = ({ music }: MusicDetailsProps) => {
|
||||
{ appearance: 'info', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(messages.watchlistError), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
@@ -291,51 +277,46 @@ const MusicDetails = ({ music }: MusicDetailsProps) => {
|
||||
const onClickHideItemBtn = async (): Promise<void> => {
|
||||
setIsBlacklistUpdating(true);
|
||||
|
||||
const res = await fetch('/api/v1/blacklist', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
try {
|
||||
const response = await axios.post('/api/v1/blacklist', {
|
||||
mbId: music?.id,
|
||||
mediaType: 'music',
|
||||
title: music?.title,
|
||||
user: user?.id,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
if (res.status === 201) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(globalMessages.blacklistSuccess, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
);
|
||||
if (response.status === 201) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(globalMessages.blacklistSuccess, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
);
|
||||
|
||||
revalidate();
|
||||
} else if (res.status === 412) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(globalMessages.blacklistDuplicateError, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'info', autoDismiss: true }
|
||||
);
|
||||
} else {
|
||||
revalidate();
|
||||
} else if (response.status === 412) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(globalMessages.blacklistDuplicateError, {
|
||||
title: music?.title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'info', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(globalMessages.blacklistError), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
} finally {
|
||||
setIsBlacklistUpdating(false);
|
||||
closeBlacklistModal();
|
||||
}
|
||||
|
||||
setIsBlacklistUpdating(false);
|
||||
closeBlacklistModal();
|
||||
};
|
||||
|
||||
const showHideButton = hasPermission([Permission.MANAGE_BLACKLIST], {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ArrowRightCircleIcon, XCircleIcon } from '@heroicons/react/24/outline';
|
||||
import type { MediaStatus } from '@server/constants/media';
|
||||
import type { PersonCombinedCreditsResponse } from '@server/interfaces/api/personInterfaces';
|
||||
import type { PersonDetails as PersonDetailsType } from '@server/models/Person';
|
||||
import axios from 'axios';
|
||||
import { groupBy, orderBy } from 'lodash';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -430,40 +431,32 @@ const PersonDetails = () => {
|
||||
data?.artist?.typeCounts?.[albumType] || 100,
|
||||
1000
|
||||
);
|
||||
const response = await fetch(
|
||||
`/api/v1/person/${parsedPersonId}?albumType=${encodeURIComponent(
|
||||
albumType
|
||||
)}&pageSize=${pageSize}`
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
const validAlbums =
|
||||
responseData.artist?.releaseGroups
|
||||
?.filter((album: Album) => album && album.id)
|
||||
.map((album: Album) => ({
|
||||
...album,
|
||||
needsCoverArt: !album.posterPath,
|
||||
})) || [];
|
||||
const response = await axios.get(`/api/v1/person/${parsedPersonId}`, {
|
||||
params: {
|
||||
albumType: albumType,
|
||||
pageSize: pageSize,
|
||||
},
|
||||
});
|
||||
|
||||
setAlbumTypes((prev) => ({
|
||||
...prev,
|
||||
[albumType]: {
|
||||
...prev[albumType],
|
||||
albums: validAlbums,
|
||||
isExpanded: true,
|
||||
isLoading: false,
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
setAlbumTypes((prev) => ({
|
||||
...prev,
|
||||
[albumType]: {
|
||||
...prev[albumType],
|
||||
isLoading: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
const responseData = response.data;
|
||||
const validAlbums =
|
||||
responseData.artist?.releaseGroups
|
||||
?.filter((album: Album) => album && album.id)
|
||||
.map((album: Album) => ({
|
||||
...album,
|
||||
needsCoverArt: !album.posterPath,
|
||||
})) || [];
|
||||
|
||||
setAlbumTypes((prev) => ({
|
||||
...prev,
|
||||
[albumType]: {
|
||||
...prev[albumType],
|
||||
albums: validAlbums,
|
||||
isExpanded: true,
|
||||
isLoading: false,
|
||||
},
|
||||
}));
|
||||
} catch (error) {
|
||||
setAlbumTypes((prev) => ({
|
||||
...prev,
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { NonFunctionProperties } from '@server/interfaces/api/common';
|
||||
import type { QuotaResponse } from '@server/interfaces/api/userInterfaces';
|
||||
import { Permission } from '@server/lib/permissions';
|
||||
import type { MusicDetails } from '@server/models/Music';
|
||||
import axios from 'axios';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -85,23 +86,17 @@ const MusicRequestModal = ({
|
||||
tags: requestOverrides.tags,
|
||||
};
|
||||
}
|
||||
const res = await fetch('/api/v1/request', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
mediaId: data?.mbId,
|
||||
mediaType: 'music',
|
||||
...overrideParams,
|
||||
}),
|
||||
|
||||
const response = await axios.post<MediaRequest>('/api/v1/request', {
|
||||
mediaId: data?.mbId,
|
||||
mediaType: 'music',
|
||||
...overrideParams,
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
const mediaRequest: MediaRequest = await res.json();
|
||||
|
||||
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
|
||||
mutate('/api/v1/request/count');
|
||||
|
||||
if (mediaRequest) {
|
||||
if (response.data) {
|
||||
if (onComplete) {
|
||||
onComplete(
|
||||
hasPermission(Permission.AUTO_APPROVE)
|
||||
@@ -133,15 +128,12 @@ const MusicRequestModal = ({
|
||||
setIsUpdating(true);
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/v1/request/${editRequest?.id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
const response = await axios.delete(`/api/v1/request/${editRequest?.id}`);
|
||||
|
||||
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
|
||||
mutate('/api/v1/request/count');
|
||||
|
||||
if (res.status === 204) {
|
||||
if (response.status === 204) {
|
||||
if (onComplete) {
|
||||
onComplete(MediaStatus.UNKNOWN);
|
||||
}
|
||||
@@ -164,28 +156,19 @@ const MusicRequestModal = ({
|
||||
setIsUpdating(true);
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/v1/request/${editRequest?.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
mediaType: 'music',
|
||||
serverId: requestOverrides?.server,
|
||||
profileId: requestOverrides?.profile,
|
||||
rootFolder: requestOverrides?.folder,
|
||||
userId: requestOverrides?.user?.id,
|
||||
tags: requestOverrides?.tags,
|
||||
}),
|
||||
await axios.put(`/api/v1/request/${editRequest?.id}`, {
|
||||
mediaType: 'music',
|
||||
serverId: requestOverrides?.server,
|
||||
profileId: requestOverrides?.profile,
|
||||
rootFolder: requestOverrides?.folder,
|
||||
userId: requestOverrides?.user?.id,
|
||||
tags: requestOverrides?.tags,
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
|
||||
if (alsoApproveRequest) {
|
||||
const res = await fetch(`/api/v1/request/${editRequest?.id}/approve`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
await axios.post(`/api/v1/request/${editRequest?.id}/approve`);
|
||||
}
|
||||
|
||||
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
|
||||
mutate('/api/v1/request/count');
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import globalMessages from '@app/i18n/globalMessages';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import { Transition } from '@headlessui/react';
|
||||
import type { LidarrSettings } from '@server/lib/settings';
|
||||
import axios from 'axios';
|
||||
import { Field, Formik } from 'formik';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
@@ -165,28 +166,19 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
|
||||
}) => {
|
||||
setIsTesting(true);
|
||||
try {
|
||||
const response = await fetch('/api/v1/settings/lidarr/test', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
const response = await axios.post<TestResponse>(
|
||||
'/api/v1/settings/lidarr/test',
|
||||
{
|
||||
hostname,
|
||||
apiKey,
|
||||
port: Number(port),
|
||||
baseUrl,
|
||||
useSsl,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data: TestResponse = await response.json();
|
||||
}
|
||||
);
|
||||
|
||||
setIsValidated(true);
|
||||
setTestResponse(data);
|
||||
setTestResponse(response.data);
|
||||
if (initialLoad.current) {
|
||||
addToast(intl.formatMessage(messages.toastLidarrTestSuccess), {
|
||||
appearance: 'success',
|
||||
@@ -281,21 +273,13 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
|
||||
)?.name,
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
!lidarr
|
||||
? '/api/v1/settings/lidarr'
|
||||
: `/api/v1/settings/lidarr/${lidarr.id}`,
|
||||
{
|
||||
method: !lidarr ? 'POST' : 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(submission),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
if (!lidarr) {
|
||||
await axios.post('/api/v1/settings/lidarr', submission);
|
||||
} else {
|
||||
await axios.put(
|
||||
`/api/v1/settings/lidarr/${lidarr.id}`,
|
||||
submission
|
||||
);
|
||||
}
|
||||
|
||||
onSave();
|
||||
|
||||
@@ -131,7 +131,10 @@ const TitleCard = ({
|
||||
...(mediaType === 'album' ? { mbId: id } : { tmdbId: Number(id) }),
|
||||
};
|
||||
|
||||
const response = await axios.post<Watchlist>('/api/v1/watchlist', requestBody);
|
||||
const response = await axios.post<Watchlist>(
|
||||
'/api/v1/watchlist',
|
||||
requestBody
|
||||
);
|
||||
mutate('/api/v1/discover/watchlist');
|
||||
if (response.data) {
|
||||
addToast(
|
||||
|
||||
Reference in New Issue
Block a user