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:
@@ -53,6 +53,9 @@ AniWorld.ConfigManager = (function() {
|
||||
// Main configuration
|
||||
bindMainEvents();
|
||||
|
||||
// NFO configuration
|
||||
bindNFOEvents();
|
||||
|
||||
// Status panel
|
||||
const closeStatus = document.getElementById('close-status');
|
||||
if (closeStatus) {
|
||||
@@ -115,6 +118,21 @@ AniWorld.ConfigManager = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind NFO config events
|
||||
*/
|
||||
function bindNFOEvents() {
|
||||
const saveNFO = document.getElementById('save-nfo-config');
|
||||
if (saveNFO) {
|
||||
saveNFO.addEventListener('click', AniWorld.NFOConfig.save);
|
||||
}
|
||||
|
||||
const testTMDB = document.getElementById('test-tmdb-connection');
|
||||
if (testTMDB) {
|
||||
testTMDB.addEventListener('click', AniWorld.NFOConfig.testTMDBConnection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind main configuration events
|
||||
*/
|
||||
@@ -197,6 +215,7 @@ AniWorld.ConfigManager = (function() {
|
||||
await AniWorld.SchedulerConfig.load();
|
||||
await AniWorld.LoggingConfig.load();
|
||||
await AniWorld.AdvancedConfig.load();
|
||||
await AniWorld.NFOConfig.load();
|
||||
|
||||
modal.classList.remove('hidden');
|
||||
} catch (error) {
|
||||
|
||||
169
src/server/web/static/js/index/nfo-config.js
Normal file
169
src/server/web/static/js/index/nfo-config.js
Normal 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
|
||||
};
|
||||
})();
|
||||
@@ -349,6 +349,85 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NFO Metadata Configuration -->
|
||||
<div class="config-section">
|
||||
<h4 data-text="nfo-config">NFO Metadata Settings</h4>
|
||||
|
||||
<div class="config-item">
|
||||
<label for="tmdb-api-key" data-text="tmdb-api-key">TMDB API Key:</label>
|
||||
<input type="text" id="tmdb-api-key" placeholder="Enter your TMDB API key" class="input-field">
|
||||
<small class="config-hint" data-text="tmdb-api-hint">
|
||||
Required for NFO metadata. Get your API key from <a href="https://www.themoviedb.org/settings/api" target="_blank">TMDB</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="config-item">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="nfo-auto-create">
|
||||
<span class="checkbox-custom"></span>
|
||||
<span data-text="nfo-auto-create">Auto-create NFO files</span>
|
||||
</label>
|
||||
<small class="config-hint" data-text="nfo-auto-create-hint">
|
||||
Automatically create NFO metadata when downloading new series
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="config-item">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="nfo-update-on-scan">
|
||||
<span class="checkbox-custom"></span>
|
||||
<span data-text="nfo-update-on-scan">Update NFO on rescan</span>
|
||||
</label>
|
||||
<small class="config-hint" data-text="nfo-update-hint">
|
||||
Refresh existing NFO files when rescanning library
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="config-item">
|
||||
<h5 data-text="media-downloads">Media File Downloads</h5>
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="nfo-download-poster" checked>
|
||||
<span class="checkbox-custom"></span>
|
||||
<span data-text="download-poster">Download poster.jpg</span>
|
||||
</label>
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="nfo-download-logo" checked>
|
||||
<span class="checkbox-custom"></span>
|
||||
<span data-text="download-logo">Download logo.png</span>
|
||||
</label>
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="nfo-download-fanart" checked>
|
||||
<span class="checkbox-custom"></span>
|
||||
<span data-text="download-fanart">Download fanart.jpg</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="config-item">
|
||||
<label for="nfo-image-size" data-text="image-size">Image Quality:</label>
|
||||
<select id="nfo-image-size" class="input-field">
|
||||
<option value="original">Original (Best Quality)</option>
|
||||
<option value="w500">Medium (w500)</option>
|
||||
</select>
|
||||
<small class="config-hint" data-text="image-size-hint">
|
||||
Original provides best quality but larger file sizes
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="config-actions">
|
||||
<button id="save-nfo-config" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
<span data-text="save-nfo-config">Save NFO Settings</span>
|
||||
</button>
|
||||
<button id="test-tmdb-connection" class="btn btn-secondary">
|
||||
<i class="fas fa-plug"></i>
|
||||
<span data-text="test-tmdb">Test TMDB Connection</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Management -->
|
||||
<div class="config-section">
|
||||
<h4 data-text="config-management">Configuration Management</h4>
|
||||
@@ -463,6 +542,7 @@
|
||||
<script src="/static/js/index/logging-config.js"></script>
|
||||
<script src="/static/js/index/advanced-config.js"></script>
|
||||
<script src="/static/js/index/main-config.js"></script>
|
||||
<script src="/static/js/index/nfo-config.js"></script>
|
||||
<script src="/static/js/index/config-manager.js"></script>
|
||||
<script src="/static/js/index/socket-handler.js"></script>
|
||||
<script src="/static/js/index/app-init.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user