All checks were successful
- Remove instanceConfig parameter from all retriever methods (getTags, getQueue, getHistory) - Retriever instances now use this.url, this.apiKey, this.id instead of passed parameter - Convert ArrRetrieverRegistry from class with convenience functions to pure singleton object - Export singleton instance directly instead of class + convenience functions - Update poller.js and historyFetcher.js to call methods on singleton directly - All 261 tests pass with zero behavior changes
308 lines
10 KiB
JavaScript
308 lines
10 KiB
JavaScript
// Copyright (c) 2026 Gordon Bolton. MIT License.
|
|
const { logToFile } = require('./logger');
|
|
const {
|
|
getSonarrInstances,
|
|
getRadarrInstances
|
|
} = require('./config');
|
|
|
|
// Import retriever classes
|
|
const PollingSonarrRetriever = require('../clients/PollingSonarrRetriever');
|
|
const PollingRadarrRetriever = require('../clients/PollingRadarrRetriever');
|
|
|
|
// Retriever type mapping
|
|
const retrieverClasses = {
|
|
sonarr: PollingSonarrRetriever,
|
|
radarr: PollingRadarrRetriever
|
|
};
|
|
|
|
/**
|
|
* Singleton registry for *arr data retrievers
|
|
*/
|
|
const arrRetrieverRegistry = {
|
|
retrievers: new Map(),
|
|
initialized: false,
|
|
|
|
/**
|
|
* Initialize all configured *arr retrievers
|
|
*/
|
|
async initialize() {
|
|
if (this.initialized) {
|
|
return;
|
|
}
|
|
|
|
logToFile('[ArrRetrieverRegistry] Initializing *arr retrievers...');
|
|
|
|
// Get all instance configurations
|
|
const sonarrInstances = getSonarrInstances();
|
|
const radarrInstances = getRadarrInstances();
|
|
|
|
// Create retriever instances
|
|
const instanceConfigs = [
|
|
...sonarrInstances.map(inst => ({ ...inst, type: 'sonarr' })),
|
|
...radarrInstances.map(inst => ({ ...inst, type: 'radarr' }))
|
|
];
|
|
|
|
for (const config of instanceConfigs) {
|
|
try {
|
|
const RetrieverClass = retrieverClasses[config.type];
|
|
if (!RetrieverClass) {
|
|
logToFile(`[ArrRetrieverRegistry] Unknown retriever type: ${config.type}`);
|
|
continue;
|
|
}
|
|
|
|
const retriever = new RetrieverClass(config);
|
|
this.retrievers.set(config.id, retriever);
|
|
logToFile(`[ArrRetrieverRegistry] Created ${config.type} retriever: ${config.name} (${config.id})`);
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Failed to create retriever ${config.id}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
this.initialized = true;
|
|
logToFile(`[ArrRetrieverRegistry] Initialized ${this.retrievers.size} *arr retrievers`);
|
|
},
|
|
|
|
/**
|
|
* Get all registered retrievers
|
|
* @returns {Array<ArrRetriever>} Array of retriever instances
|
|
*/
|
|
getAllRetrievers() {
|
|
return Array.from(this.retrievers.values());
|
|
},
|
|
|
|
/**
|
|
* Get retriever by instance ID
|
|
* @param {string} instanceId - The instance ID
|
|
* @returns {ArrRetriever|null} Retriever instance or null if not found
|
|
*/
|
|
getRetriever(instanceId) {
|
|
return this.retrievers.get(instanceId) || null;
|
|
},
|
|
|
|
/**
|
|
* Get retrievers by type
|
|
* @param {string} type - Retriever type ('sonarr', 'radarr')
|
|
* @returns {Array<ArrRetriever>} Array of retriever instances
|
|
*/
|
|
getRetrieversByType(type) {
|
|
return this.getAllRetrievers().filter(retriever => retriever.getRetrieverType() === type);
|
|
},
|
|
|
|
/**
|
|
* Get tags from all retrievers
|
|
* @returns {Promise<Array<Object>>} Array of tag results with instance info
|
|
*/
|
|
async getAllTags() {
|
|
const retrievers = this.getAllRetrievers();
|
|
if (retrievers.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Fetch tags from all retrievers in parallel
|
|
const results = await Promise.allSettled(
|
|
retrievers.map(async (retriever) => {
|
|
try {
|
|
const tags = await retriever.getTags();
|
|
logToFile(`[ArrRetrieverRegistry] ${retriever.name}: ${tags.length} tags`);
|
|
return { instance: retriever.getInstanceId(), data: tags };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching tags from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: [] };
|
|
}
|
|
})
|
|
);
|
|
|
|
return results
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value);
|
|
},
|
|
|
|
/**
|
|
* Get queue from all retrievers
|
|
* @returns {Promise<Array<Object>>} Array of queue results with instance info
|
|
*/
|
|
async getAllQueues() {
|
|
const retrievers = this.getAllRetrievers();
|
|
if (retrievers.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Fetch queues from all retrievers in parallel
|
|
const results = await Promise.allSettled(
|
|
retrievers.map(async (retriever) => {
|
|
try {
|
|
const queue = await retriever.getQueue();
|
|
logToFile(`[ArrRetrieverRegistry] ${retriever.name}: ${(queue.records || []).length} queue items`);
|
|
return { instance: retriever.getInstanceId(), data: queue };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching queue from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
return results
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value);
|
|
},
|
|
|
|
/**
|
|
* Get history from all retrievers
|
|
* @param {Object} options - Optional parameters for history fetch
|
|
* @returns {Promise<Array<Object>>} Array of history results with instance info
|
|
*/
|
|
async getAllHistory(options = {}) {
|
|
const retrievers = this.getAllRetrievers();
|
|
if (retrievers.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Fetch history from all retrievers in parallel
|
|
const results = await Promise.allSettled(
|
|
retrievers.map(async (retriever) => {
|
|
try {
|
|
const history = await retriever.getHistory(options);
|
|
logToFile(`[ArrRetrieverRegistry] ${retriever.name}: ${(history.records || []).length} history records`);
|
|
return { instance: retriever.getInstanceId(), data: history };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching history from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
return results
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value);
|
|
},
|
|
|
|
/**
|
|
* Get tags grouped by retriever type
|
|
* @returns {Promise<Object>} Tags grouped by retriever type (array of { instance, data } objects)
|
|
*/
|
|
async getTagsByType() {
|
|
const sonarrRetrievers = this.getRetrieversByType('sonarr');
|
|
const radarrRetrievers = this.getRetrieversByType('radarr');
|
|
|
|
const sonarrTags = await Promise.allSettled(
|
|
sonarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const tags = await retriever.getTags();
|
|
return { instance: retriever.getInstanceId(), data: tags };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching tags from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: [] };
|
|
}
|
|
})
|
|
);
|
|
|
|
const radarrTags = await Promise.allSettled(
|
|
radarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const tags = await retriever.getTags();
|
|
return { instance: retriever.getInstanceId(), data: tags };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching tags from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: [] };
|
|
}
|
|
})
|
|
);
|
|
|
|
return {
|
|
sonarr: sonarrTags
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value),
|
|
radarr: radarrTags
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value)
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Get queue grouped by retriever type
|
|
* @returns {Promise<Object>} Queue grouped by retriever type
|
|
*/
|
|
async getQueuesByType() {
|
|
const sonarrRetrievers = this.getRetrieversByType('sonarr');
|
|
const radarrRetrievers = this.getRetrieversByType('radarr');
|
|
|
|
const sonarrQueues = await Promise.allSettled(
|
|
sonarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const queue = await retriever.getQueue();
|
|
return { instance: retriever.getInstanceId(), data: queue };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching queue from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
const radarrQueues = await Promise.allSettled(
|
|
radarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const queue = await retriever.getQueue();
|
|
return { instance: retriever.getInstanceId(), data: queue };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching queue from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
return {
|
|
sonarr: sonarrQueues
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value),
|
|
radarr: radarrQueues
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value)
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Get history grouped by retriever type
|
|
* @param {Object} options - Optional parameters for history fetch
|
|
* @returns {Promise<Object>} History grouped by retriever type
|
|
*/
|
|
async getHistoryByType(options = {}) {
|
|
const sonarrRetrievers = this.getRetrieversByType('sonarr');
|
|
const radarrRetrievers = this.getRetrieversByType('radarr');
|
|
|
|
const sonarrHistory = await Promise.allSettled(
|
|
sonarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const history = await retriever.getHistory(options);
|
|
return { instance: retriever.getInstanceId(), data: history };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching history from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
const radarrHistory = await Promise.allSettled(
|
|
radarrRetrievers.map(async (retriever) => {
|
|
try {
|
|
const history = await retriever.getHistory(options);
|
|
return { instance: retriever.getInstanceId(), data: history };
|
|
} catch (error) {
|
|
logToFile(`[ArrRetrieverRegistry] Error fetching history from ${retriever.name}: ${error.message}`);
|
|
return { instance: retriever.getInstanceId(), data: { records: [] } };
|
|
}
|
|
})
|
|
);
|
|
|
|
return {
|
|
sonarr: sonarrHistory
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value),
|
|
radarr: radarrHistory
|
|
.filter(result => result.status === 'fulfilled')
|
|
.map(result => result.value)
|
|
};
|
|
}
|
|
};
|
|
|
|
module.exports = arrRetrieverRegistry;
|