Compare commits

...

3 Commits

Author SHA1 Message Date
70ccb8f4fd Fix Transmission connection testing and API compatibility
- Updated TransmissionClient to use correct method names from transmission-promise
- Changed sessionGet to session() and sessionSet to sessionUpdate()
- Added robust error handling in connection test
- Improved logging for connection debugging
- Fixed error handling in TransmissionClient constructor

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-07 09:19:27 +00:00
301684886f Fix Transmission remote connection issues
- Prevent remote host from defaulting to localhost
- Preserve remote connection settings during config updates
- Handle empty values correctly to avoid overriding good config

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-07 09:16:28 +00:00
f28d49284e Fix module import issues on fresh installations
- Ensure server.js uses consistent .js extensions for module imports
- Create compatibility symlinks for different module naming styles
- Update file-creator-module.sh to handle module paths correctly
- Bump version to 2.0.8

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-07 09:13:02 +00:00
5 changed files with 142 additions and 52 deletions

View File

@ -1,9 +1,15 @@
# Transmission RSS Manager v2.0.7 # Transmission RSS Manager v2.0.8
A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration, intelligent media organization, and enhanced security features. Now with automatic updates and easy installation! A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration, intelligent media organization, and enhanced security features. Now with automatic updates and easy installation!
## Changelog ## Changelog
### v2.0.8 (2025-03-07)
- **Fixed**: Module import issues on fresh installations with proper file extension handling
- **Fixed**: Adding compatibility symlinks for different module naming styles
- **Improved**: Server.js now uses consistent module import paths with .js extensions
- **Improved**: More robust module file handling in the installer
### v2.0.7 (2025-03-07) ### v2.0.7 (2025-03-07)
- **Fixed**: Installation directory handling with prompt for choosing install path - **Fixed**: Installation directory handling with prompt for choosing install path
- **Fixed**: Bootstrap-installer now defaults to /opt/trans-install with user configuration option - **Fixed**: Bootstrap-installer now defaults to /opt/trans-install with user configuration option

View File

@ -136,8 +136,9 @@ const cors = require('cors');
const Transmission = require('transmission'); const Transmission = require('transmission');
// Import custom modules // Import custom modules
const PostProcessor = require('./modules/postProcessor'); const PostProcessor = require('./modules/post-processor.js');
const RssFeedManager = require('./modules/rssFeedManager'); const RssFeedManager = require('./modules/rss-feed-manager.js');
const TransmissionClient = require('./modules/transmission-client.js');
// Initialize Express app // Initialize Express app
const app = express(); const app = express();
@ -1823,6 +1824,20 @@ function copy_module_files() {
echo "Copying module: $module_name" echo "Copying module: $module_name"
cp "$js_file" "$INSTALL_DIR/modules/$module_name" cp "$js_file" "$INSTALL_DIR/modules/$module_name"
# Create symlinks for alternative module names that might be referenced
base_name=$(basename "$module_name" .js)
case "$base_name" in
"rss-feed-manager")
ln -sf "$INSTALL_DIR/modules/$module_name" "$INSTALL_DIR/modules/rssFeedManager.js"
;;
"post-processor")
ln -sf "$INSTALL_DIR/modules/$module_name" "$INSTALL_DIR/modules/postProcessor.js"
;;
"transmission-client")
ln -sf "$INSTALL_DIR/modules/$module_name" "$INSTALL_DIR/modules/transmissionClient.js"
;;
esac
# Set permissions # Set permissions
chown "$USER:$USER" "$INSTALL_DIR/modules/$module_name" chown "$USER:$USER" "$INSTALL_DIR/modules/$module_name"
chmod 644 "$INSTALL_DIR/modules/$module_name" chmod 644 "$INSTALL_DIR/modules/$module_name"

View File

@ -28,8 +28,14 @@ class TransmissionClient {
this.dirMappings = config.remoteConfig.directoryMapping; this.dirMappings = config.remoteConfig.directoryMapping;
} }
// Initialize the connection // Initialize the connection - but don't throw if it fails initially
// This allows the object to be created even if the connection fails
try {
this.initializeConnection(); this.initializeConnection();
} catch (error) {
console.error("Failed to initialize Transmission connection:", error.message);
// Don't throw - allow methods to handle connection retry logic
}
} }
/** /**
@ -39,8 +45,11 @@ class TransmissionClient {
const { host, port, username, password, path: rpcPath } = this.config.transmissionConfig; const { host, port, username, password, path: rpcPath } = this.config.transmissionConfig;
try { try {
// Only default to localhost if host is empty/null/undefined
const connectionHost = (host === undefined || host === null || host === '') ? 'localhost' : host;
this.client = new Transmission({ this.client = new Transmission({
host: host || 'localhost', host: connectionHost,
port: port || 9091, port: port || 9091,
username: username || '', username: username || '',
password: password || '', password: password || '',
@ -48,7 +57,7 @@ class TransmissionClient {
timeout: 30000 // 30 seconds timeout: 30000 // 30 seconds
}); });
console.log(`Initialized Transmission client connection to ${host}:${port}${rpcPath}`); console.log(`Initialized Transmission client connection to ${connectionHost}:${port}${rpcPath}`);
} catch (error) { } catch (error) {
console.error('Failed to initialize Transmission client:', error); console.error('Failed to initialize Transmission client:', error);
throw error; throw error;
@ -61,13 +70,17 @@ class TransmissionClient {
*/ */
async getStatus() { async getStatus() {
try { try {
// Use the session-stats method for basic connectivity check
const sessionInfo = await this.client.sessionStats(); const sessionInfo = await this.client.sessionStats();
const version = await this.client.sessionGet();
// Use the session-get method to get version info
// Note: In transmission-promise, this is 'session' not 'sessionGet'
const session = await this.client.session();
return { return {
connected: true, connected: true,
version: version.version, version: session.version || "Unknown",
rpcVersion: version['rpc-version'], rpcVersion: session['rpc-version'] || "Unknown",
downloadSpeed: sessionInfo.downloadSpeed, downloadSpeed: sessionInfo.downloadSpeed,
uploadSpeed: sessionInfo.uploadSpeed, uploadSpeed: sessionInfo.uploadSpeed,
torrentCount: sessionInfo.torrentCount, torrentCount: sessionInfo.torrentCount,
@ -443,7 +456,8 @@ class TransmissionClient {
*/ */
async setSessionParams(params) { async setSessionParams(params) {
try { try {
await this.client.sessionSet(params); // In transmission-promise, the method is sessionUpdate not sessionSet
await this.client.sessionUpdate(params);
return { return {
success: true, success: true,
message: 'Session parameters updated successfully' message: 'Session parameters updated successfully'

View File

@ -1,6 +1,6 @@
{ {
"name": "transmission-rss-manager", "name": "transmission-rss-manager",
"version": "2.0.7", "version": "2.0.8",
"description": "A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration and intelligent media organization", "description": "A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration and intelligent media organization",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {

View File

@ -18,37 +18,26 @@ const bcrypt = require('bcrypt');
// Try to import with .js extension first, then fallback to no extension for better compatibility // Try to import with .js extension first, then fallback to no extension for better compatibility
let RssFeedManager, TransmissionClient, PostProcessor; let RssFeedManager, TransmissionClient, PostProcessor;
// Always use explicit .js extension when importing our own modules
try { try {
RssFeedManager = require('./modules/rss-feed-manager.js'); RssFeedManager = require('./modules/rss-feed-manager.js');
} catch (e) { } catch (err) {
try {
RssFeedManager = require('./modules/rss-feed-manager');
} catch (err) {
console.error('Failed to load RssFeedManager module:', err); console.error('Failed to load RssFeedManager module:', err);
process.exit(1); process.exit(1);
}
} }
try { try {
TransmissionClient = require('./modules/transmission-client.js'); TransmissionClient = require('./modules/transmission-client.js');
} catch (e) { } catch (err) {
try {
TransmissionClient = require('./modules/transmission-client');
} catch (err) {
console.error('Failed to load TransmissionClient module:', err); console.error('Failed to load TransmissionClient module:', err);
process.exit(1); process.exit(1);
}
} }
try { try {
PostProcessor = require('./modules/post-processor.js'); PostProcessor = require('./modules/post-processor.js');
} catch (e) { } catch (err) {
try {
PostProcessor = require('./modules/post-processor');
} catch (err) {
console.error('Failed to load PostProcessor module:', err); console.error('Failed to load PostProcessor module:', err);
process.exit(1); process.exit(1);
}
} }
// Constants and configuration // Constants and configuration
@ -479,9 +468,45 @@ app.post('/api/config', authenticateJWT, async (req, res) => {
// Merge the new config with the existing one // Merge the new config with the existing one
const newConfig = { ...config, ...req.body }; const newConfig = { ...config, ...req.body };
// Keep passwords if they're not provided // Preserve existing Transmission configuration values when not explicitly provided
if (newConfig.transmissionConfig && !newConfig.transmissionConfig.password && config.transmissionConfig) { if (newConfig.transmissionConfig && config.transmissionConfig) {
newConfig.transmissionConfig.password = config.transmissionConfig.password; // First create a copy of the existing configuration
const preservedTransConfig = { ...config.transmissionConfig };
// Only update values that are explicitly provided and not empty
if (!req.body.transmissionConfig?.host) {
newConfig.transmissionConfig.host = preservedTransConfig.host;
}
if (!req.body.transmissionConfig?.port) {
newConfig.transmissionConfig.port = preservedTransConfig.port;
}
if (!req.body.transmissionConfig?.path) {
newConfig.transmissionConfig.path = preservedTransConfig.path;
}
if (!req.body.transmissionConfig?.username) {
newConfig.transmissionConfig.username = preservedTransConfig.username;
}
// Always preserve password if not provided
if (!newConfig.transmissionConfig.password) {
newConfig.transmissionConfig.password = preservedTransConfig.password;
}
}
// Preserve remote configuration settings if not explicitly provided
if (newConfig.remoteConfig && config.remoteConfig) {
// Make sure isRemote setting is preserved if not explicitly set
if (req.body.remoteConfig?.isRemote === undefined) {
newConfig.remoteConfig.isRemote = config.remoteConfig.isRemote;
}
// Preserve directory mappings if not provided
if (!req.body.remoteConfig?.directoryMapping && config.remoteConfig.directoryMapping) {
newConfig.remoteConfig.directoryMapping = { ...config.remoteConfig.directoryMapping };
}
} }
// Keep user passwords // Keep user passwords
@ -860,7 +885,15 @@ app.post('/api/transmission/remove', authenticateJWT, async (req, res) => {
app.post('/api/transmission/test', authenticateJWT, async (req, res) => { app.post('/api/transmission/test', authenticateJWT, async (req, res) => {
try { try {
const { host, port, username, password } = req.body; const { host, port, username, password, path: rpcPath } = req.body;
// Debug info
console.log('Testing Transmission connection with params:', {
host: host || 'from config',
port: port || 'from config',
username: username ? 'provided' : 'from config',
path: rpcPath || 'from config',
});
// Create a temporary client for testing // Create a temporary client for testing
const testConfig = { const testConfig = {
@ -869,10 +902,25 @@ app.post('/api/transmission/test', authenticateJWT, async (req, res) => {
port: port || config.transmissionConfig.port, port: port || config.transmissionConfig.port,
username: username || config.transmissionConfig.username, username: username || config.transmissionConfig.username,
password: password || config.transmissionConfig.password, password: password || config.transmissionConfig.password,
path: config.transmissionConfig.path path: rpcPath || config.transmissionConfig.path
},
// Also include remoteConfig to ensure proper remote handling
remoteConfig: {
// If host is provided and different from localhost, set isRemote to true
isRemote: host && host !== 'localhost' ? true : config.remoteConfig?.isRemote || false,
directoryMapping: config.remoteConfig?.directoryMapping || {}
} }
}; };
// Log the actual test config (without password)
console.log('Test configuration:', {
host: testConfig.transmissionConfig.host,
port: testConfig.transmissionConfig.port,
path: testConfig.transmissionConfig.path,
isRemote: testConfig.remoteConfig.isRemote
});
try {
const testClient = new TransmissionClient(testConfig); const testClient = new TransmissionClient(testConfig);
const status = await testClient.getStatus(); const status = await testClient.getStatus();
@ -891,6 +939,13 @@ app.post('/api/transmission/test', authenticateJWT, async (req, res) => {
message: `Failed to connect: ${status.error}` message: `Failed to connect: ${status.error}`
}); });
} }
} catch (error) {
console.error('Error testing Transmission connection:', error);
res.status(500).json({
success: false,
message: `Error testing connection: ${error.message}`
});
}
} catch (error) { } catch (error) {
res.status(500).json({ res.status(500).json({
success: false, success: false,