mes-engine 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.mocharc.json +7 -0
  2. package/README.md +94 -85
  3. package/dist/index.js +243 -24
  4. package/dist/index.js.map +1 -1
  5. package/dist/{types → src}/core/VideoEngine.d.ts +2 -1
  6. package/dist/{types → src}/core/types.d.ts +12 -0
  7. package/dist/src/engines/FFmpegEngine.d.ts +7 -0
  8. package/dist/{types/engines/FFmpegEngine.d.ts → src/engines/GStreamerEngine.d.ts} +2 -1
  9. package/dist/src/index.d.ts +12 -0
  10. package/dist/{types → src}/processor.d.ts +5 -5
  11. package/dist/{types → src}/storage/FileSystemStorage.d.ts +1 -1
  12. package/dist/{types → src}/streaming/StreamManager.d.ts +1 -1
  13. package/dist/tests/video-processor.test.d.ts +1 -0
  14. package/docs/API.md +109 -0
  15. package/docs/HLS.md +54 -0
  16. package/docs/README.md +172 -169
  17. package/docs/caching.md +62 -0
  18. package/docs/engines.md +62 -58
  19. package/docs/storage.md +57 -0
  20. package/examples/full-demo/backend/.env +6 -0
  21. package/examples/full-demo/backend/package-lock.json +1783 -0
  22. package/examples/full-demo/backend/package.json +22 -0
  23. package/examples/full-demo/backend/src/routes/video.js +92 -0
  24. package/examples/full-demo/backend/src/server.js +43 -0
  25. package/examples/full-demo/backend/src/services/videoProcessor.js +85 -0
  26. package/examples/full-demo/frontend/index.html +13 -0
  27. package/examples/full-demo/frontend/package-lock.json +5791 -0
  28. package/examples/full-demo/frontend/package.json +32 -0
  29. package/examples/full-demo/frontend/postcss.config.js +6 -0
  30. package/examples/full-demo/frontend/src/App.jsx +113 -0
  31. package/examples/full-demo/frontend/src/components/ProcessingStatus.jsx +71 -0
  32. package/examples/full-demo/frontend/src/components/VideoPlayer.jsx +87 -0
  33. package/examples/full-demo/frontend/src/components/VideoUploader.jsx +62 -0
  34. package/examples/full-demo/frontend/src/index.css +3 -0
  35. package/examples/full-demo/frontend/src/main.jsx +10 -0
  36. package/examples/full-demo/frontend/src/services/api.js +30 -0
  37. package/examples/full-demo/frontend/tailwind.config.js +10 -0
  38. package/examples/full-demo/frontend/vite.config.js +16 -0
  39. package/examples/simple-usage/README.md +31 -0
  40. package/examples/simple-usage/index.ts +68 -0
  41. package/examples/simple-usage/package-lock.json +592 -0
  42. package/examples/simple-usage/package.json +15 -0
  43. package/package.json +69 -48
  44. package/rollup.config.js +3 -1
  45. package/src/bandwidth.ts +1 -1
  46. package/src/core/VideoEngine.ts +29 -4
  47. package/src/core/events.ts +9 -1
  48. package/src/core/types.ts +38 -3
  49. package/src/engines/FFmpegEngine.ts +173 -32
  50. package/src/engines/GStreamerEngine.ts +24 -1
  51. package/src/index.ts +13 -13
  52. package/src/processor.ts +119 -35
  53. package/src/storage/FileSystemStorage.ts +2 -4
  54. package/src/storage/StorageProvider.ts +7 -2
  55. package/src/streaming/StreamManager.ts +4 -5
  56. package/tests/video-processor.test.ts +32 -12
  57. package/tsconfig.json +19 -5
  58. package/tsconfig.test.json +17 -4
  59. package/dist/types/index.d.ts +0 -10
  60. package/dist/{types → src}/bandwidth.d.ts +0 -0
  61. package/dist/{types → src}/cache/ExternalCache.d.ts +0 -0
  62. package/dist/{types → src}/cache/LRU.d.ts +2 -2
  63. /package/dist/{types → src}/cache/cacheStrategy.d.ts +0 -0
  64. /package/dist/{types → src}/cache/internalCache.d.ts +0 -0
  65. /package/dist/{types → src}/core/events.d.ts +0 -0
  66. /package/dist/{types → src}/storage/StorageProvider.d.ts +0 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "mes-engine-demo-backend",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "node src/server.js",
7
+ "dev": "nodemon src/server.js"
8
+ },
9
+ "dependencies": {
10
+ "express": "^4.18.2",
11
+ "mes-engine": "file:../../..",
12
+ "ffmpeg-static": "^5.2.0",
13
+ "ffprobe-static": "^3.1.0",
14
+ "multer": "^2.0.2",
15
+ "cors": "^2.8.5",
16
+ "dotenv": "^16.3.1",
17
+ "socket.io": "^4.6.1"
18
+ },
19
+ "devDependencies": {
20
+ "nodemon": "^3.0.1"
21
+ }
22
+ }
@@ -0,0 +1,92 @@
1
+ import express from 'express';
2
+ import multer from 'multer';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import videoProcessorService from '../services/videoProcessor.js';
6
+ import { dirname } from 'path';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const router = express.Router();
10
+
11
+ // Configure multer for file upload
12
+ const storage = multer.diskStorage({
13
+ destination: (req, file, cb) => {
14
+ cb(null, path.join(__dirname, '../../uploads'));
15
+ },
16
+ filename: (req, file, cb) => {
17
+ const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
18
+ cb(null, uniqueSuffix + path.extname(file.originalname));
19
+ }
20
+ });
21
+
22
+ const upload = multer({
23
+ storage,
24
+ limits: {
25
+ fileSize: parseInt(process.env.MAX_FILE_SIZE) || 500 * 1024 * 1024
26
+ },
27
+ fileFilter: (req, file, cb) => {
28
+ const allowedTypes = /mp4|mov|avi|mkv|webm/;
29
+ const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
30
+ const mimetype = allowedTypes.test(file.mimetype);
31
+
32
+ if (extname && mimetype) {
33
+ return cb(null, true);
34
+ }
35
+ cb(new Error('Only video files are allowed!'));
36
+ }
37
+ });
38
+
39
+ // Upload and process video
40
+ router.post('/upload', upload.single('video'), async (req, res) => {
41
+ try {
42
+ if (!req.file) {
43
+ return res.status(400).json({ error: 'No video file uploaded' });
44
+ }
45
+
46
+ const socketId = req.body.socketId;
47
+ const io = req.app.get('io');
48
+
49
+ // Start processing asynchronously
50
+ videoProcessorService.processVideo(req.file.path, socketId, io)
51
+ .catch(error => {
52
+ console.error('Processing error:', error);
53
+ });
54
+
55
+ res.json({
56
+ message: 'Video uploaded successfully. Processing started.',
57
+ filename: req.file.filename,
58
+ size: req.file.size
59
+ });
60
+ } catch (error) {
61
+ res.status(500).json({ error: error.message });
62
+ }
63
+ });
64
+
65
+ // Get video manifest
66
+ router.get('/manifest/:videoId', async (req, res) => {
67
+ try {
68
+ const manifest = await videoProcessorService.getManifest(req.params.videoId);
69
+ res.json(manifest);
70
+ } catch (error) {
71
+ res.status(404).json({ error: 'Manifest not found' });
72
+ }
73
+ });
74
+
75
+ // Stream video chunk
76
+ router.get('/stream/:videoId/:quality/:chunk', async (req, res) => {
77
+ try {
78
+ const { videoId, quality, chunk } = req.params;
79
+ const stream = await videoProcessorService.streamChunk(
80
+ videoId,
81
+ parseInt(quality),
82
+ parseInt(chunk)
83
+ );
84
+
85
+ res.setHeader('Content-Type', 'video/mp4');
86
+ stream.pipe(res);
87
+ } catch (error) {
88
+ res.status(404).json({ error: 'Chunk not found' });
89
+ }
90
+ });
91
+
92
+ export default router;
@@ -0,0 +1,43 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { createServer } from 'http';
4
+ import { Server } from 'socket.io';
5
+ import dotenv from 'dotenv';
6
+ import videoRoutes from './routes/video.js';
7
+
8
+ dotenv.config();
9
+
10
+ const app = express();
11
+ const httpServer = createServer(app);
12
+ const io = new Server(httpServer, {
13
+ cors: {
14
+ origin: "http://localhost:5173",
15
+ methods: ["GET", "POST"]
16
+ }
17
+ });
18
+
19
+ // Middleware
20
+ app.use(cors());
21
+ app.use(express.json());
22
+ app.use(express.static('processed'));
23
+
24
+ // Make io available to routes
25
+ app.set('io', io);
26
+
27
+ // Routes
28
+ app.use('/api/video', videoRoutes);
29
+
30
+ // Socket.io connection
31
+ io.on('connection', (socket) => {
32
+ console.log('Client connected:', socket.id);
33
+
34
+ socket.on('disconnect', () => {
35
+ console.log('Client disconnected:', socket.id);
36
+ });
37
+ });
38
+
39
+ const PORT = process.env.PORT || 3001;
40
+
41
+ httpServer.listen(PORT, () => {
42
+ console.log(`Server running on port ${PORT}`);
43
+ });
@@ -0,0 +1,85 @@
1
+ import {
2
+ VideoProcessor,
3
+ FFmpegEngine,
4
+ FileSystemStorage,
5
+ InternalCache
6
+ } from 'mes-engine';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+
12
+ export class VideoProcessorService {
13
+ constructor() {
14
+ this.processor = new VideoProcessor(
15
+ new FFmpegEngine(),
16
+ new FileSystemStorage(path.join(__dirname, '../../processed')),
17
+ {
18
+ chunkSize: 10, // 10 seconds
19
+ cacheDir: path.join(__dirname, '../../processed'),
20
+ maxCacheSize: parseInt(process.env.CACHE_SIZE) || 100 * 1024 * 1024, // 100MB
21
+ defaultQualities: [
22
+ { height: 1080, bitrate: '5000k' },
23
+ { height: 720, bitrate: '2500k' },
24
+ { height: 480, bitrate: '1000k' },
25
+ { height: 360, bitrate: '500k' }
26
+ ]
27
+ }
28
+ );
29
+
30
+ this.setupEventListeners();
31
+ }
32
+
33
+ setupEventListeners() {
34
+ // The current VideoProcessor doesn't emit 'progress',
35
+ // it emits CHUNK_PROCESSED, QUALITY_PROCESSED, etc.
36
+ this.processor.on('chunkProcessed', (data) => {
37
+ console.log('Chunk processed:', data);
38
+ });
39
+
40
+ this.processor.on('processingComplete', (data) => {
41
+ console.log('Processing complete:', data);
42
+ });
43
+
44
+ this.processor.on('error', (error) => {
45
+ console.error('Processing error:', error);
46
+ });
47
+ }
48
+
49
+ async processVideo(filePath, socketId, io) {
50
+ try {
51
+ // Emit processing started
52
+ io.to(socketId).emit('processing:started', { filePath });
53
+
54
+ // Process video
55
+ // Note: VideoProcessor.processVideo(inputPath, options)
56
+ const manifest = await this.processor.processVideo(filePath, {
57
+ title: path.basename(filePath)
58
+ });
59
+
60
+ // Emit processing complete
61
+ io.to(socketId).emit('processing:complete', { manifest });
62
+
63
+ return manifest;
64
+ } catch (error) {
65
+ io.to(socketId).emit('processing:error', { error: error.message });
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ async streamChunk(videoId, quality, chunkNumber) {
71
+ return await this.processor.streamChunk(videoId, quality, chunkNumber);
72
+ }
73
+
74
+ async getManifest(videoId) {
75
+ // In actual implementation, we'd read from disk or memory.
76
+ // For the demo, let's assume we can fetch it.
77
+ // The VideoProcessor doesn't have a getManifest method.
78
+ const manifestPath = path.join(__dirname, '../../processed', videoId, 'manifest.json');
79
+ const data = await fs.promises.readFile(manifestPath, 'utf8');
80
+ return JSON.parse(data);
81
+ }
82
+ }
83
+
84
+ import fs from 'fs';
85
+ export default new VideoProcessorService();
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>MES-Engine Demo</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>