feat: enhance plugin system with async support and lifecycle hooks & reverted PR #79 due to Node.js v24 stream compatibility issues

This commit is contained in:
Technomancer702
2026-02-05 01:42:40 -08:00
parent 6eafa871b5
commit 72e14dca57
6 changed files with 1177 additions and 63 deletions
+147 -7
View File
@@ -6,18 +6,158 @@ This directory allows you to extend the functionality of **NodeCast TV** without
## 🛠️ How It Works
The plugin loader in `server/index.js` scans this directory and expects each file to export a **initialization function**.
The plugin loader in `server/index.js` scans this directory at startup and loads plugins in **alphabetical order** (sorted by filename). Each plugin file should export either:
- A function (sync or async) that will be called during initialization
- An object with `init()` and optionally `shutdown()` methods for lifecycle management
When the server starts, it calls this function and passes the internal Express instance and the loaded services.
When the server starts, it calls your plugin's initialization code and passes:
- `app` - The Express application instance
- `services` - A **frozen** (read-only) object containing all internal services
### Plugin Signature
Each plugin must follow this structure:
---
## 📝 Plugin Patterns
### Pattern 1: Simple Function Export
The simplest plugin pattern - just export a function:
```javascript
/**
* @param {Object} app - The Express application instance
* @param {Object} services - Object containing all internal services (db, syncService, etc.)
* @param {Object} services - Frozen object containing all internal services
*/
module.exports = function(app, services) {
// Your logic here
};
// Register routes, middleware, etc.
app.get('/api/my-route', (req, res) => {
res.json({ message: 'Hello from plugin!' });
});
};
```
### Pattern 2: Async Function Export
For plugins that need to perform async initialization (database connections, API calls, etc.):
```javascript
module.exports = async function(app, services) {
// Async initialization
await someAsyncSetup();
// Access services
if (services.syncService) {
console.log('Sync service available');
}
// Register routes
app.get('/api/my-route', (req, res) => {
res.json({ status: 'ready' });
});
};
```
### Pattern 3: Lifecycle Hooks (Advanced)
For plugins that need cleanup on shutdown:
```javascript
module.exports = {
/**
* Called during server startup
*/
init: async (app, services) => {
// Setup code
this.interval = setInterval(() => {
console.log('Background task running...');
}, 60000);
app.get('/api/status', (req, res) => {
res.json({ uptime: process.uptime() });
});
},
/**
* Called on SIGTERM (graceful shutdown)
*/
shutdown: async () => {
// Cleanup code
if (this.interval) {
clearInterval(this.interval);
}
console.log('Plugin cleaned up');
}
};
```
---
## 🔒 Security & Permissions
> [!WARNING]
> Plugins run with **full Node.js permissions** and have access to the Express app and all services. Only install plugins from trusted sources.
**Important notes:**
- The `services` object is **frozen** - you cannot modify or delete services
- Plugins can still mutate properties of service objects (e.g., `services.cache.set()`)
- Plugins can register routes, middleware, and access the file system
- This is appropriate for self-hosted applications where you control what plugins are installed
---
## 📦 Available Services
The `services` object contains all modules from `server/services/`:
| Service | Description |
|---------|-------------|
| `cache` | Caching utilities |
| `epgParser` | EPG/XMLTV parsing |
| `hwDetect` | Hardware acceleration detection |
| `m3uParser` | M3U playlist parsing |
| `m3uXtreamAdapter` | Xtream API adapter |
| `syncService` | Channel/EPG synchronization |
| `transcodeSession` | Transcoding session management |
| `xtreamApi` | Xtream API client |
**Example:**
```javascript
module.exports = async function(app, services) {
// Use the EPG parser service
const epgData = await services.epgParser.fetchAndParse('http://example.com/epg.xml');
console.log(`Loaded ${epgData.programmes.length} programmes`);
};
```
---
## 🔄 Load Order
Plugins are loaded in **alphabetical order** by filename. If you need specific ordering:
```
plugins/
01-database.js # Loads first
02-api-client.js # Loads second
99-cleanup.js # Loads last
```
---
## 🧪 Testing Your Plugin
1. Place your `.js` file in `server/plugins/`
2. Restart the NodeCast TV server
3. Check the console for plugin loading messages:
- `✓ Loaded plugin: your-plugin.js` - Success
- `⚠ Plugin your-plugin.js does not export...` - Wrong export format
- `✗ Failed to load plugin your-plugin.js` - Error during initialization
---
## 💡 Example Use Cases
- **Custom scrapers** - Add support for new streaming sources
- **Notification systems** - Send alerts when new content is available
- **Analytics** - Track viewing patterns
- **Custom APIs** - Expose additional endpoints for integrations
- **Middleware** - Add authentication, logging, or rate limiting
+21 -4
View File
@@ -1,8 +1,25 @@
module.exports = function(app, services) {
console.log("🚀 Plugin 'Hello' activé !");
/**
* Example plugin demonstrating async initialization and service access
* This plugin registers a test route at /api/hello
*/
module.exports = async function (app, services) {
console.log("Plugin 'Hello' activated!");
//route de test accessible sur http://localhost:3000/api/hello
// Example: Access loaded services
if (services.syncService) {
console.log(" - syncService is available");
}
// Simulate async initialization (e.g., database connection, API setup)
await new Promise(resolve => setTimeout(resolve, 100));
// Register a test route accessible at http://localhost:3000/api/hello
app.get('/api/hello', (req, res) => {
res.json({ message: "Le système de plugin fonctionne !" });
res.json({
message: "The plugin system is working!",
availableServices: Object.keys(services)
});
});
console.log(" - Registered route: GET /api/hello");
};