diff --git a/frontend/src/api.js b/frontend/src/api.js
index d050e4a7..97d20d65 100644
--- a/frontend/src/api.js
+++ b/frontend/src/api.js
@@ -1471,6 +1471,22 @@ export default class API {
}
}
+ static async deleteSetting({ id, key }) {
+ if (!id) return;
+ try {
+ await request(`${host}/api/core/settings/${id}/`, {
+ method: 'DELETE',
+ });
+ if (key) {
+ useSettingsStore.getState().removeSetting(key);
+ }
+ return true;
+ } catch (e) {
+ errorNotification('Failed to delete setting', e);
+ throw e;
+ }
+ }
+
static async getChannelStats(uuid = null) {
try {
const response = await request(`${host}/proxy/ts/status`);
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index ec449074..4fdd1561 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -615,61 +615,73 @@ const SettingsPage = () => {
});
}, [tmdbSetting?.value, validateTmdbKeyValue]);
- const handleSaveTmdbKey = async () => {
- const trimmedKey = tmdbKey.trim();
+ const persistTmdbKey = async (rawValue) => {
+ const trimmedKey = (rawValue || '').trim();
+ setTmdbKey(trimmedKey);
setSavingTmdbKey(true);
try {
- let validationResult = null;
- if (trimmedKey) {
- validationResult = await validateTmdbKeyValue(trimmedKey);
- if (!validationResult?.overall_valid) {
- notifications.show({
- title: 'Invalid TMDB key',
- message:
- validationResult?.message ||
- 'Metadata providers are unavailable. TMDB rejected the API key and Movie-DB is unreachable.',
- color: 'red',
- });
- return;
- }
- } else {
- validationResult = await validateTmdbKeyValue('');
+ const validationResult = await validateTmdbKeyValue(trimmedKey);
+
+ if (trimmedKey && !validationResult?.overall_valid) {
+ notifications.show({
+ title: 'Invalid TMDB key',
+ message:
+ validationResult?.message ||
+ 'Metadata providers are unavailable. TMDB rejected the API key and Movie-DB is unreachable.',
+ color: 'red',
+ });
+ return;
}
- if (tmdbSetting && tmdbSetting.id) {
- await API.updateSetting({
- ...tmdbSetting,
- value: trimmedKey,
- });
- } else {
- await API.createSetting({
- key: 'tmdb-api-key',
- name: 'TMDB API Key',
- value: trimmedKey,
- });
+ if (trimmedKey) {
+ if (tmdbSetting && tmdbSetting.id) {
+ await API.updateSetting({
+ ...tmdbSetting,
+ value: trimmedKey,
+ });
+ } else {
+ await API.createSetting({
+ key: 'tmdb-api-key',
+ name: 'TMDB API Key',
+ value: trimmedKey,
+ });
+ }
+ } else if (tmdbSetting && tmdbSetting.id) {
+ await API.deleteSetting(tmdbSetting);
}
const provider = validationResult?.provider || activeMetadataSource;
const usingFallback = provider === 'movie-db';
+ const title = trimmedKey
+ ? provider === 'tmdb'
+ ? 'TMDB key saved'
+ : 'Saved with fallback'
+ : usingFallback
+ ? 'TMDB key removed'
+ : 'Metadata unavailable';
+ const message = trimmedKey
+ ? provider === 'tmdb'
+ ? 'TMDB API key saved and verified.'
+ : usingFallback
+ ? 'Movie-DB fallback will be used for metadata until TMDB becomes available.'
+ : 'Metadata providers are currently unavailable. Libraries may fail to scan.'
+ : usingFallback
+ ? 'TMDB API key removed. Movie-DB fallback will be used for metadata.'
+ : 'TMDB API key removed, but no metadata providers are currently available.';
+ const color = trimmedKey
+ ? provider === 'tmdb'
+ ? 'green'
+ : usingFallback
+ ? 'blue'
+ : 'red'
+ : usingFallback
+ ? 'blue'
+ : 'red';
+
notifications.show({
- title:
- provider === 'tmdb'
- ? 'TMDB key saved'
- : trimmedKey
- ? 'Saved with fallback'
- : 'Using fallback metadata',
- message:
- provider === 'tmdb'
- ? 'TMDB API key saved and verified.'
- : usingFallback
- ? 'Movie-DB fallback will be used for metadata until TMDB becomes available.'
- : 'Metadata providers are currently unavailable. Libraries may fail to scan.',
- color:
- provider === 'tmdb'
- ? 'green'
- : usingFallback
- ? 'blue'
- : 'red',
+ title,
+ message,
+ color,
});
} catch (error) {
console.error('Failed to save TMDB key', error);
@@ -683,6 +695,24 @@ const SettingsPage = () => {
}
};
+ const handleSaveTmdbKey = () => persistTmdbKey(tmdbKey);
+
+ const handleDeleteTmdbKey = async () => {
+ if (!tmdbKey) return;
+ const confirmed =
+ typeof window === 'undefined'
+ ? true
+ : window.confirm(
+ 'Remove the TMDB API key? Metadata will fall back to Movie-DB when possible.'
+ );
+ if (!confirmed) return;
+ try {
+ await persistTmdbKey('');
+ } catch (error) {
+ console.error('Failed to remove TMDB key', error);
+ }
+ };
+
const libraryActionsDisabled = tmdbValidating || !metadataSourcesAvailable;
const tmdbMessageColor =
tmdbValidationState === 'valid'
@@ -990,6 +1020,23 @@ const SettingsPage = () => {
placeholder="Enter TMDB API key"
value={tmdbKey}
onChange={(event) => setTmdbKey(event.currentTarget.value)}
+ rightSection={
+ tmdbKey && tmdbKey.trim().length > 0 ? (
+
+
+
+
+
+ ) : null
+ }
+ rightSectionPointerEvents="auto"
description="Used for metadata and artwork lookups."
/>
diff --git a/frontend/src/store/settings.jsx b/frontend/src/store/settings.jsx
index 99390320..61f03f2e 100644
--- a/frontend/src/store/settings.jsx
+++ b/frontend/src/store/settings.jsx
@@ -40,6 +40,13 @@ const useSettingsStore = create((set) => ({
set((state) => ({
settings: { ...state.settings, [setting.key]: setting },
})),
+ removeSetting: (key) =>
+ set((state) => {
+ if (!key) return state;
+ const next = { ...state.settings };
+ delete next[key];
+ return { ...state, settings: next };
+ }),
}));
export default useSettingsStore;