feat: Add NFO configuration settings (Task 7)

- Added NFOConfig model with TMDB API key, auto-create, media downloads, image size settings
- Created NFO settings section in UI with form fields and validation
- Implemented nfo-config.js module for loading, saving, and testing TMDB connection
- Added TMDB API key validation endpoint (POST /api/config/tmdb/validate)
- Integrated NFO config into AppConfig and ConfigUpdate models
- Added 5 unit tests for NFO config model validation
- Added API test for TMDB validation endpoint
- All 16 config model tests passing, all 10 config API tests passing
- Documented in docs/task7_status.md (100% complete)
This commit is contained in:
2026-01-16 19:33:23 +01:00
parent ecfa8d3c10
commit 120b26b9f7
8 changed files with 635 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
/**
* AniWorld - NFO Config Module
*
* Handles NFO metadata configuration settings.
*
* Dependencies: constants.js, api-client.js, ui-utils.js
*/
var AniWorld = window.AniWorld || {};
AniWorld.NFOConfig = (function() {
'use strict';
const API = AniWorld.Constants.API;
/**
* Load NFO configuration from server
*/
async function load() {
try {
const config = await AniWorld.ApiClient.request(API.CONFIG);
if (config && config.nfo) {
const nfo = config.nfo;
// TMDB API Key
const tmdbKey = document.getElementById('tmdb-api-key');
if (tmdbKey && nfo.tmdb_api_key) {
tmdbKey.value = nfo.tmdb_api_key;
}
// Auto-create NFO
const autoCreate = document.getElementById('nfo-auto-create');
if (autoCreate) {
autoCreate.checked = nfo.auto_create || false;
}
// Update on scan
const updateOnScan = document.getElementById('nfo-update-on-scan');
if (updateOnScan) {
updateOnScan.checked = nfo.update_on_scan || false;
}
// Download options
const downloadPoster = document.getElementById('nfo-download-poster');
if (downloadPoster) {
downloadPoster.checked = nfo.download_poster !== false;
}
const downloadLogo = document.getElementById('nfo-download-logo');
if (downloadLogo) {
downloadLogo.checked = nfo.download_logo !== false;
}
const downloadFanart = document.getElementById('nfo-download-fanart');
if (downloadFanart) {
downloadFanart.checked = nfo.download_fanart !== false;
}
// Image size
const imageSize = document.getElementById('nfo-image-size');
if (imageSize && nfo.image_size) {
imageSize.value = nfo.image_size;
}
}
} catch (error) {
console.error('Error loading NFO config:', error);
AniWorld.UI.showToast('Failed to load NFO configuration', 'error');
}
}
/**
* Save NFO configuration
*/
async function save() {
try {
AniWorld.UI.showLoading('Saving NFO configuration...');
// Get form values
const tmdbKey = document.getElementById('tmdb-api-key');
const autoCreate = document.getElementById('nfo-auto-create');
const updateOnScan = document.getElementById('nfo-update-on-scan');
const downloadPoster = document.getElementById('nfo-download-poster');
const downloadLogo = document.getElementById('nfo-download-logo');
const downloadFanart = document.getElementById('nfo-download-fanart');
const imageSize = document.getElementById('nfo-image-size');
// Validate TMDB API key is provided if auto-create is enabled
if (autoCreate && autoCreate.checked && (!tmdbKey || !tmdbKey.value.trim())) {
AniWorld.UI.hideLoading();
AniWorld.UI.showToast('TMDB API key is required when auto-create is enabled', 'error');
return;
}
const nfoConfig = {
tmdb_api_key: tmdbKey ? tmdbKey.value.trim() : null,
auto_create: autoCreate ? autoCreate.checked : false,
update_on_scan: updateOnScan ? updateOnScan.checked : false,
download_poster: downloadPoster ? downloadPoster.checked : true,
download_logo: downloadLogo ? downloadLogo.checked : true,
download_fanart: downloadFanart ? downloadFanart.checked : true,
image_size: imageSize ? imageSize.value : 'original'
};
// Save configuration
const response = await AniWorld.ApiClient.request(
API.CONFIG,
{
method: 'PUT',
body: JSON.stringify({ nfo: nfoConfig })
}
);
if (response) {
AniWorld.UI.showToast('NFO configuration saved successfully', 'success');
} else {
throw new Error('Failed to save configuration');
}
} catch (error) {
console.error('Error saving NFO config:', error);
AniWorld.UI.showToast('Failed to save NFO configuration: ' + error.message, 'error');
} finally {
AniWorld.UI.hideLoading();
}
}
/**
* Test TMDB API connection
*/
async function testTMDBConnection() {
try {
const tmdbKey = document.getElementById('tmdb-api-key');
if (!tmdbKey || !tmdbKey.value.trim()) {
AniWorld.UI.showToast('Please enter a TMDB API key first', 'warning');
return;
}
AniWorld.UI.showLoading('Testing TMDB connection...');
const response = await AniWorld.ApiClient.request(
'/api/config/tmdb/validate',
{
method: 'POST',
body: JSON.stringify({ api_key: tmdbKey.value.trim() })
}
);
if (response && response.valid) {
AniWorld.UI.showToast('TMDB API key is valid!', 'success');
} else {
const message = response && response.message ? response.message : 'Invalid API key';
AniWorld.UI.showToast('TMDB validation failed: ' + message, 'error');
}
} catch (error) {
console.error('Error testing TMDB connection:', error);
AniWorld.UI.showToast('Failed to test TMDB connection: ' + error.message, 'error');
} finally {
AniWorld.UI.hideLoading();
}
}
// Public API
return {
load: load,
save: save,
testTMDBConnection: testTMDBConnection
};
})();