mes-engine 0.0.1
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.
- package/CONTRIBUTING.md +199 -0
- package/README.md +86 -0
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -0
- package/dist/types/bandwidth.d.ts +8 -0
- package/dist/types/cache/ExternalCache.d.ts +11 -0
- package/dist/types/cache/LRU.d.ts +8 -0
- package/dist/types/cache/cacheStrategy.d.ts +12 -0
- package/dist/types/cache/internalCache.d.ts +13 -0
- package/dist/types/core/VideoEngine.d.ts +6 -0
- package/dist/types/core/events.d.ts +6 -0
- package/dist/types/core/types.d.ts +20 -0
- package/dist/types/engines/FFmpegEngine.d.ts +6 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/processor.d.ts +19 -0
- package/dist/types/storage/FileSystemStorage.d.ts +6 -0
- package/dist/types/storage/StorageProvider.d.ts +5 -0
- package/dist/types/streaming/StreamManager.d.ts +10 -0
- package/docs/README.md +170 -0
- package/docs/engines.md +58 -0
- package/package.json +48 -0
- package/rollup.config.js +24 -0
- package/src/bandwidth.ts +30 -0
- package/src/cache/ExternalCache.ts +49 -0
- package/src/cache/LRU.ts +35 -0
- package/src/cache/cacheStrategy.ts +15 -0
- package/src/cache/internalCache.ts +60 -0
- package/src/core/VideoEngine.ts +16 -0
- package/src/core/events.ts +7 -0
- package/src/core/types.ts +26 -0
- package/src/engines/FFmpegEngine.ts +51 -0
- package/src/engines/GStreamerEngine.ts +45 -0
- package/src/index.ts +13 -0
- package/src/processor.ts +90 -0
- package/src/storage/FileSystemStorage.ts +19 -0
- package/src/storage/StorageProvider.ts +7 -0
- package/src/streaming/StreamManager.ts +26 -0
- package/tests/video-processor.test.ts +247 -0
- package/tsconfig.json +16 -0
- package/tsconfig.test.json +16 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Contributing to mes-engine
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to mes-engine! This document provides guidelines and instructions for contributing to the project.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Code of Conduct](#code-of-conduct)
|
|
8
|
+
- [Getting Started](#getting-started)
|
|
9
|
+
- [Development Process](#development-process)
|
|
10
|
+
- [Pull Request Process](#pull-request-process)
|
|
11
|
+
- [Coding Standards](#coding-standards)
|
|
12
|
+
- [Testing Guidelines](#testing-guidelines)
|
|
13
|
+
- [Documentation](#documentation)
|
|
14
|
+
- [Release Process](#release-process)
|
|
15
|
+
|
|
16
|
+
## Code of Conduct
|
|
17
|
+
|
|
18
|
+
By participating in this project, you are expected to uphold our Code of Conduct:
|
|
19
|
+
|
|
20
|
+
- Use welcoming and inclusive language
|
|
21
|
+
- Be respectful of differing viewpoints and experiences
|
|
22
|
+
- Accept constructive criticism gracefully
|
|
23
|
+
- Focus on what is best for the community
|
|
24
|
+
- Show empathy towards other community members
|
|
25
|
+
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
1. Fork the repository
|
|
29
|
+
2. Clone your fork:
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/your-username/mes-engine.git
|
|
32
|
+
```
|
|
33
|
+
3. Add the upstream remote:
|
|
34
|
+
```bash
|
|
35
|
+
git remote add upstream https://github.com/original-owner/mes-engine.git
|
|
36
|
+
```
|
|
37
|
+
4. Install dependencies:
|
|
38
|
+
```bash
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development Process
|
|
43
|
+
|
|
44
|
+
1. Create a new branch for your feature/fix:
|
|
45
|
+
```bash
|
|
46
|
+
git checkout -b feature/your-feature-name
|
|
47
|
+
# or
|
|
48
|
+
git checkout -b fix/your-fix-name
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Make your changes, following our [coding standards](#coding-standards)
|
|
52
|
+
|
|
53
|
+
3. Run tests and ensure they pass:
|
|
54
|
+
```bash
|
|
55
|
+
npm test
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
4. Update documentation as needed
|
|
59
|
+
|
|
60
|
+
5. Commit your changes using conventional commits:
|
|
61
|
+
```bash
|
|
62
|
+
git commit -m "feat: add new video processing engine"
|
|
63
|
+
git commit -m "fix: resolve memory leak in cache system"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Pull Request Process
|
|
67
|
+
|
|
68
|
+
1. Update the README.md with details of changes if applicable
|
|
69
|
+
2. Update the docs/ with any necessary documentation
|
|
70
|
+
3. Add or update tests as needed
|
|
71
|
+
4. Ensure all tests pass and code coverage meets requirements
|
|
72
|
+
5. Submit a pull request to the `main` branch
|
|
73
|
+
6. Address any review comments
|
|
74
|
+
|
|
75
|
+
### PR Title Format
|
|
76
|
+
- feat: Add new feature
|
|
77
|
+
- fix: Fix an issue
|
|
78
|
+
- docs: Documentation changes
|
|
79
|
+
- style: Code style changes
|
|
80
|
+
- refactor: Code refactoring
|
|
81
|
+
- test: Add or update tests
|
|
82
|
+
- chore: Maintenance tasks
|
|
83
|
+
|
|
84
|
+
## Coding Standards
|
|
85
|
+
|
|
86
|
+
### TypeScript Guidelines
|
|
87
|
+
|
|
88
|
+
- Use TypeScript strict mode
|
|
89
|
+
- Prefer interfaces over types when possible
|
|
90
|
+
- Use explicit typing instead of inferring when the type isn't obvious
|
|
91
|
+
- Use meaningful variable and function names
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Good
|
|
95
|
+
interface VideoConfig {
|
|
96
|
+
quality: number;
|
|
97
|
+
format: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Avoid
|
|
101
|
+
type Config = {
|
|
102
|
+
q: number;
|
|
103
|
+
f: string;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Code Style
|
|
108
|
+
|
|
109
|
+
- Use 2 spaces for indentation
|
|
110
|
+
- Use semicolons
|
|
111
|
+
- Use single quotes for strings
|
|
112
|
+
- Add trailing commas in objects and arrays
|
|
113
|
+
- Keep lines under 100 characters
|
|
114
|
+
|
|
115
|
+
### Best Practices
|
|
116
|
+
|
|
117
|
+
- Write pure functions when possible
|
|
118
|
+
- Use early returns to avoid deep nesting
|
|
119
|
+
- Handle errors appropriately
|
|
120
|
+
- Document complex algorithms
|
|
121
|
+
- Write self-documenting code
|
|
122
|
+
|
|
123
|
+
## Testing Guidelines
|
|
124
|
+
|
|
125
|
+
### Test Structure
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
describe('Component', () => {
|
|
129
|
+
describe('method()', () => {
|
|
130
|
+
it('should handle normal case', () => {
|
|
131
|
+
// Test
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should handle error case', () => {
|
|
135
|
+
// Test
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Coverage Requirements
|
|
142
|
+
|
|
143
|
+
- Statements: 85%
|
|
144
|
+
- Branches: 80%
|
|
145
|
+
- Functions: 90%
|
|
146
|
+
- Lines: 85%
|
|
147
|
+
|
|
148
|
+
### Running Tests
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm test # Run all tests
|
|
152
|
+
npm run test:watch # Run tests in watch mode
|
|
153
|
+
npm run test:coverage # Generate coverage report
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Documentation
|
|
157
|
+
|
|
158
|
+
- Update API documentation for any new or modified functionality
|
|
159
|
+
- Include JSDoc comments for public APIs
|
|
160
|
+
- Add examples for new features
|
|
161
|
+
- Update README.md if adding new features
|
|
162
|
+
- Keep docs/ up to date with changes
|
|
163
|
+
|
|
164
|
+
### Documentation Style
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
/**
|
|
168
|
+
* Processes a video chunk with specified quality settings
|
|
169
|
+
* @param inputPath - Path to the input video file
|
|
170
|
+
* @param quality - Quality settings for processing
|
|
171
|
+
* @returns Promise resolving to processed chunk data
|
|
172
|
+
* @throws {ProcessingError} When processing fails
|
|
173
|
+
*/
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Release Process
|
|
177
|
+
|
|
178
|
+
1. Update version number in package.json
|
|
179
|
+
2. Update CHANGELOG.md
|
|
180
|
+
3. Create release notes
|
|
181
|
+
4. Create a tagged release
|
|
182
|
+
5. Publish to npm
|
|
183
|
+
|
|
184
|
+
### Version Numbers
|
|
185
|
+
|
|
186
|
+
Follow semantic versioning (MAJOR.MINOR.PATCH):
|
|
187
|
+
- MAJOR: Breaking changes
|
|
188
|
+
- MINOR: New features, no breaking changes
|
|
189
|
+
- PATCH: Bug fixes, no breaking changes
|
|
190
|
+
|
|
191
|
+
## Questions or Problems?
|
|
192
|
+
|
|
193
|
+
- File an issue in the GitHub issue tracker
|
|
194
|
+
- Tag issues appropriately (bug, enhancement, question, etc.)
|
|
195
|
+
- Provide as much relevant information as possible
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
By contributing, you agree that your contributions will be licensed under the same MIT License that covers the project.
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# mes-engine
|
|
2
|
+
|
|
3
|
+
A powerful and flexible video processing framework for Node.js with support for multiple processing engines, adaptive streaming, and intelligent caching.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/mes-engine)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🎥 Multiple video processing engines (FFmpeg, GStreamer)
|
|
11
|
+
- 🔄 Adaptive streaming with quality switching
|
|
12
|
+
- 📦 Chunk-based processing for efficient streaming
|
|
13
|
+
- 💾 Flexible storage providers
|
|
14
|
+
- 🚀 Built-in caching strategies (internal/external)
|
|
15
|
+
- 📡 Event-driven architecture
|
|
16
|
+
- 🔌 Extensible plugin system
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install mes-engine
|
|
24
|
+
|
|
25
|
+
# Install required engine
|
|
26
|
+
npm install ffmpeg-static # For FFmpeg engine
|
|
27
|
+
# or
|
|
28
|
+
npm install gstreamer # For GStreamer engine
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Basic Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import {
|
|
35
|
+
VideoProcessor,
|
|
36
|
+
FFmpegEngine,
|
|
37
|
+
FileSystemStorage,
|
|
38
|
+
InternalCache
|
|
39
|
+
} from 'mes-engine';
|
|
40
|
+
|
|
41
|
+
// Initialize processor
|
|
42
|
+
const processor = new VideoProcessor({
|
|
43
|
+
engine: new FFmpegEngine(),
|
|
44
|
+
storage: new FileSystemStorage(),
|
|
45
|
+
cache: new InternalCache({
|
|
46
|
+
maxSize: 1024 * 1024 * 100, // 100MB
|
|
47
|
+
ttl: 3600,
|
|
48
|
+
preloadNextChunk: true
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Process video
|
|
53
|
+
const manifest = await processor.processVideo('input.mp4');
|
|
54
|
+
|
|
55
|
+
// Stream video chunk
|
|
56
|
+
const stream = await processor.streamChunk(
|
|
57
|
+
manifest.videoId,
|
|
58
|
+
720, // quality
|
|
59
|
+
0 // chunk number
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
Full documentation is available in the [docs directory](./docs).
|
|
66
|
+
|
|
67
|
+
### Key Topics:
|
|
68
|
+
- [Getting Started](./docs/getting-started.md)
|
|
69
|
+
- [Video Engines](./docs/engines.md)
|
|
70
|
+
- [Storage Providers](./docs/storage.md)
|
|
71
|
+
- [Caching Strategies](./docs/caching.md)
|
|
72
|
+
- [API Reference](./docs/api.md)
|
|
73
|
+
|
|
74
|
+
## Supported Engines
|
|
75
|
+
|
|
76
|
+
- **FFmpegEngine**: Full-featured video processing using FFmpeg
|
|
77
|
+
- **GStreamerEngine**: High-performance processing using GStreamer
|
|
78
|
+
- **Custom Engines**: Create your own by extending `VideoEngine`
|
|
79
|
+
|
|
80
|
+
## Contributing
|
|
81
|
+
|
|
82
|
+
Contributions are welcome! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT © [Bumho Nisubire]
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { Readable } from 'stream';
|
|
4
|
+
import { promises } from 'fs';
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
var VideoEvent;
|
|
9
|
+
(function (VideoEvent) {
|
|
10
|
+
VideoEvent["CHUNK_PROCESSED"] = "chunkProcessed";
|
|
11
|
+
VideoEvent["QUALITY_PROCESSED"] = "qualityProcessed";
|
|
12
|
+
VideoEvent["PROCESSING_COMPLETE"] = "processingComplete";
|
|
13
|
+
VideoEvent["ERROR"] = "error";
|
|
14
|
+
})(VideoEvent || (VideoEvent = {}));
|
|
15
|
+
|
|
16
|
+
// core/VideoEngine.ts
|
|
17
|
+
class VideoEngine extends EventEmitter {
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// engines/FFmpegEngine.ts
|
|
21
|
+
class FFmpegEngine extends VideoEngine {
|
|
22
|
+
async processChunk(inputPath, outputPath, startTime, quality) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const ffmpeg = spawn('ffmpeg', [
|
|
25
|
+
'-i', inputPath,
|
|
26
|
+
'-ss', startTime.toString(),
|
|
27
|
+
'-t', '10',
|
|
28
|
+
'-vf', `scale=-1:${quality.height}`,
|
|
29
|
+
'-c:v', 'libx264',
|
|
30
|
+
'-b:v', quality.bitrate,
|
|
31
|
+
'-c:a', 'aac',
|
|
32
|
+
'-b:a', '128k',
|
|
33
|
+
'-preset', 'fast',
|
|
34
|
+
'-y',
|
|
35
|
+
outputPath
|
|
36
|
+
]);
|
|
37
|
+
ffmpeg.on('close', code => {
|
|
38
|
+
code === 0 ? resolve() : reject(new Error(`FFmpeg error: ${code}`));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async getDuration(inputPath) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const ffprobe = spawn('ffprobe', [
|
|
45
|
+
'-v', 'error',
|
|
46
|
+
'-show_entries', 'format=duration',
|
|
47
|
+
'-of', 'default=noprint_wrappers=1:nokey=1',
|
|
48
|
+
inputPath
|
|
49
|
+
]);
|
|
50
|
+
let output = '';
|
|
51
|
+
ffprobe.stdout.on('data', data => output += data);
|
|
52
|
+
ffprobe.on('close', code => {
|
|
53
|
+
code === 0 ? resolve(parseFloat(output)) : reject(new Error(`FFprobe error: ${code}`));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// streaming/StreamManager.ts
|
|
60
|
+
class StreamManager {
|
|
61
|
+
constructor(storage) {
|
|
62
|
+
this.storage = storage;
|
|
63
|
+
}
|
|
64
|
+
async createStream(chunkPath, range) {
|
|
65
|
+
const data = await this.storage.getChunk(chunkPath);
|
|
66
|
+
const stream = new Readable();
|
|
67
|
+
if (range) {
|
|
68
|
+
stream.push(data.slice(range.start, range.end + 1));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
stream.push(data);
|
|
72
|
+
}
|
|
73
|
+
stream.push(null);
|
|
74
|
+
return stream;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class GStreamerEngine extends VideoEngine {
|
|
79
|
+
async processChunk(inputPath, outputPath, startTime, quality) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const gst = spawn('gst-launch-1.0', [
|
|
82
|
+
'filesrc', `location=${inputPath}`,
|
|
83
|
+
'!', 'decodebin',
|
|
84
|
+
'!', 'videoconvert',
|
|
85
|
+
'!', 'videoscale',
|
|
86
|
+
'!', `video/x-raw,width=-1,height=${quality.height}`,
|
|
87
|
+
'!', 'x264enc', `bitrate=${quality.bitrate}`,
|
|
88
|
+
'!', 'mp4mux', '!', 'filesink', `location=${outputPath}`
|
|
89
|
+
]);
|
|
90
|
+
gst.on('close', code => {
|
|
91
|
+
code === 0 ? resolve() : reject(new Error(`GStreamer error: ${code}`));
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async getDuration(inputPath) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const gst = spawn('gst-launch-1.0', [
|
|
98
|
+
'filesrc', `location=${inputPath}`,
|
|
99
|
+
'!', 'decodebin',
|
|
100
|
+
'!', 'identity', '-debug', 'duration'
|
|
101
|
+
]);
|
|
102
|
+
let output = '';
|
|
103
|
+
gst.stdout.on('data', data => output += data);
|
|
104
|
+
gst.on('close', code => {
|
|
105
|
+
code === 0 ? resolve(parseFloat(output)) : reject(new Error(`GStreamer error: ${code}`));
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// storage/StorageProvider.ts
|
|
112
|
+
class StorageProvider {
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// storage/FileSystemStorage.ts
|
|
116
|
+
class FileSystemStorage extends StorageProvider {
|
|
117
|
+
async saveChunk(chunkPath, data) {
|
|
118
|
+
await promises.writeFile(chunkPath, data);
|
|
119
|
+
}
|
|
120
|
+
async getChunk(chunkPath) {
|
|
121
|
+
return promises.readFile(chunkPath);
|
|
122
|
+
}
|
|
123
|
+
async deleteChunk(chunkPath) {
|
|
124
|
+
await promises.unlink(chunkPath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
class CacheStrategy {
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// cache/LRU.ts
|
|
132
|
+
class LRU {
|
|
133
|
+
constructor(maxSize) {
|
|
134
|
+
this.maxSize = maxSize;
|
|
135
|
+
this.cache = new Map();
|
|
136
|
+
}
|
|
137
|
+
set(key, value) {
|
|
138
|
+
if (this.cache.has(key)) {
|
|
139
|
+
this.cache.delete(key);
|
|
140
|
+
}
|
|
141
|
+
else if (this.cache.size >= this.maxSize) {
|
|
142
|
+
const oldestKey = this.cache.keys().next().value;
|
|
143
|
+
if (oldestKey !== undefined) {
|
|
144
|
+
this.cache.delete(oldestKey);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
this.cache.set(key, value);
|
|
148
|
+
}
|
|
149
|
+
get(key) {
|
|
150
|
+
if (!this.cache.has(key))
|
|
151
|
+
return undefined;
|
|
152
|
+
const value = this.cache.get(key);
|
|
153
|
+
this.cache.delete(key);
|
|
154
|
+
this.cache.set(key, value);
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
clear() {
|
|
158
|
+
this.cache.clear();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// cache/internalCache.ts
|
|
163
|
+
class InternalCache extends CacheStrategy {
|
|
164
|
+
constructor(options, storage) {
|
|
165
|
+
super();
|
|
166
|
+
this.options = options;
|
|
167
|
+
this.cache = new LRU(options.maxSize);
|
|
168
|
+
this.storage = storage;
|
|
169
|
+
}
|
|
170
|
+
async set(key, value) {
|
|
171
|
+
this.cache.set(key, value);
|
|
172
|
+
}
|
|
173
|
+
async get(key) {
|
|
174
|
+
const cached = this.cache.get(key);
|
|
175
|
+
if (cached)
|
|
176
|
+
return cached;
|
|
177
|
+
try {
|
|
178
|
+
const data = await this.storage.getChunk(key);
|
|
179
|
+
await this.set(key, data);
|
|
180
|
+
return data;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async preload(key) {
|
|
187
|
+
if (this.options.preloadNextChunk) {
|
|
188
|
+
const nextChunkKey = this.getNextChunkKey(key);
|
|
189
|
+
if (!this.cache.get(nextChunkKey)) {
|
|
190
|
+
try {
|
|
191
|
+
const data = await this.storage.getChunk(nextChunkKey);
|
|
192
|
+
await this.set(nextChunkKey, data);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Ignore preload failures
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async clear() {
|
|
201
|
+
this.cache.clear();
|
|
202
|
+
}
|
|
203
|
+
getNextChunkKey(currentKey) {
|
|
204
|
+
const parts = currentKey.split('_');
|
|
205
|
+
const currentChunk = parseInt(parts[parts.length - 1]);
|
|
206
|
+
parts[parts.length - 1] = (currentChunk + 1).toString();
|
|
207
|
+
return parts.join('_');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// cache/ExternalCache.ts
|
|
212
|
+
class ExternalCache extends CacheStrategy {
|
|
213
|
+
constructor(options) {
|
|
214
|
+
super();
|
|
215
|
+
this.options = options;
|
|
216
|
+
this.baseUrl = options.externalCacheUrl;
|
|
217
|
+
}
|
|
218
|
+
async set(key, value) {
|
|
219
|
+
await fetch(`${this.baseUrl}/cache/${key}`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
body: value,
|
|
222
|
+
headers: {
|
|
223
|
+
'Content-Type': 'application/octet-stream',
|
|
224
|
+
'TTL': this.options.ttl.toString()
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async get(key) {
|
|
229
|
+
const response = await fetch(`${this.baseUrl}/cache/${key}`);
|
|
230
|
+
return response.ok ? Buffer.from(await response.arrayBuffer()) : null;
|
|
231
|
+
}
|
|
232
|
+
async preload(key) {
|
|
233
|
+
if (this.options.preloadNextChunk) {
|
|
234
|
+
const nextChunkKey = this.getNextChunkKey(key);
|
|
235
|
+
await fetch(`${this.baseUrl}/preload/${nextChunkKey}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async clear() {
|
|
239
|
+
await fetch(`${this.baseUrl}/cache`, { method: 'DELETE' });
|
|
240
|
+
}
|
|
241
|
+
getNextChunkKey(currentKey) {
|
|
242
|
+
const parts = currentKey.split('_');
|
|
243
|
+
const currentChunk = parseInt(parts[parts.length - 1]);
|
|
244
|
+
parts[parts.length - 1] = (currentChunk + 1).toString();
|
|
245
|
+
return parts.join('_');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
class VideoProcessor extends EventEmitter {
|
|
250
|
+
constructor(engine, storage, config) {
|
|
251
|
+
super();
|
|
252
|
+
this.engine = engine;
|
|
253
|
+
this.streamManager = new StreamManager(storage);
|
|
254
|
+
this.config = config;
|
|
255
|
+
}
|
|
256
|
+
async processVideo(inputPath) {
|
|
257
|
+
const videoId = await this.generateVideoId(inputPath);
|
|
258
|
+
const manifest = {
|
|
259
|
+
videoId,
|
|
260
|
+
qualities: this.config.defaultQualities,
|
|
261
|
+
chunks: []
|
|
262
|
+
};
|
|
263
|
+
const duration = await this.engine.getDuration(inputPath);
|
|
264
|
+
const chunks = Math.ceil(duration / this.config.chunkSize);
|
|
265
|
+
for (const quality of this.config.defaultQualities) {
|
|
266
|
+
for (let i = 0; i < chunks; i++) {
|
|
267
|
+
const chunkPath = this.getChunkPath(videoId, quality.height, i);
|
|
268
|
+
try {
|
|
269
|
+
await this.engine.processChunk(inputPath, chunkPath, i * this.config.chunkSize, quality);
|
|
270
|
+
manifest.chunks.push({
|
|
271
|
+
quality: quality.height,
|
|
272
|
+
number: i,
|
|
273
|
+
path: chunkPath
|
|
274
|
+
});
|
|
275
|
+
this.emit(VideoEvent.CHUNK_PROCESSED, { quality, chunkNumber: i });
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
this.emit(VideoEvent.ERROR, error);
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
this.emit(VideoEvent.QUALITY_PROCESSED, quality);
|
|
283
|
+
}
|
|
284
|
+
this.emit(VideoEvent.PROCESSING_COMPLETE, manifest);
|
|
285
|
+
return manifest;
|
|
286
|
+
}
|
|
287
|
+
async streamChunk(videoId, quality, chunkNumber, range) {
|
|
288
|
+
const chunkPath = this.getChunkPath(videoId, quality, chunkNumber);
|
|
289
|
+
return this.streamManager.createStream(chunkPath, range);
|
|
290
|
+
}
|
|
291
|
+
getChunkPath(videoId, quality, chunkNumber) {
|
|
292
|
+
return join(this.config.cacheDir, videoId, `${quality}p`, `chunk_${chunkNumber}.mp4`);
|
|
293
|
+
}
|
|
294
|
+
async generateVideoId(inputPath) {
|
|
295
|
+
const stats = await promises.stat(inputPath);
|
|
296
|
+
return `${inputPath.split('/').pop()?.split('.')[0]}_${stats.mtimeMs}`;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { CacheStrategy, ExternalCache, FFmpegEngine, FileSystemStorage, GStreamerEngine, InternalCache, StorageProvider, StreamManager, VideoEngine, VideoEvent, VideoProcessor };
|
|
301
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/core/events.ts","../../src/core/VideoEngine.ts","../../src/engines/FFmpegEngine.ts","../../src/streaming/StreamManager.ts","../../src/engines/GStreamerEngine.ts","../../src/storage/StorageProvider.ts","../../src/storage/FileSystemStorage.ts","../../src/cache/cacheStrategy.ts","../../src/cache/LRU.ts","../../src/cache/internalCache.ts","../../src/cache/ExternalCache.ts","../../src/processor.ts"],"sourcesContent":["\r\nexport enum VideoEvent {\r\n CHUNK_PROCESSED = 'chunkProcessed',\r\n QUALITY_PROCESSED = 'qualityProcessed',\r\n PROCESSING_COMPLETE = 'processingComplete',\r\n ERROR = 'error'\r\n}\r\n","\r\n// core/VideoEngine.ts\r\nimport { EventEmitter } from 'events';\r\nimport { VideoConfig, QualityLevel, VideoManifest } from './types';\r\nimport { VideoEvent } from './events';\r\n\r\nexport abstract class VideoEngine extends EventEmitter {\r\n abstract processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void>;\r\n\r\n abstract getDuration(inputPath: string): Promise<number>;\r\n}","\r\n// engines/FFmpegEngine.ts\r\nimport { spawn } from 'child_process';\r\nimport { VideoEngine } from '../core/VideoEngine';\r\nimport { QualityLevel } from '../core/types';\r\n\r\nexport class FFmpegEngine extends VideoEngine {\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const ffmpeg = spawn('ffmpeg', [\r\n '-i', inputPath,\r\n '-ss', startTime.toString(),\r\n '-t', '10',\r\n '-vf', `scale=-1:${quality.height}`,\r\n '-c:v', 'libx264',\r\n '-b:v', quality.bitrate,\r\n '-c:a', 'aac',\r\n '-b:a', '128k',\r\n '-preset', 'fast',\r\n '-y',\r\n outputPath\r\n ]);\r\n\r\n ffmpeg.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`FFmpeg error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n const ffprobe = spawn('ffprobe', [\r\n '-v', 'error',\r\n '-show_entries', 'format=duration',\r\n '-of', 'default=noprint_wrappers=1:nokey=1',\r\n inputPath\r\n ]);\r\n\r\n let output = '';\r\n ffprobe.stdout.on('data', data => output += data);\r\n ffprobe.on('close', code => {\r\n code === 0 ? resolve(parseFloat(output)) : reject(new Error(`FFprobe error: ${code}`));\r\n });\r\n });\r\n }\r\n}","\r\n// streaming/StreamManager.ts\r\nimport { Readable } from 'stream';\r\nimport { StorageProvider } from '../storage/StorageProvider';\r\n\r\nexport class StreamManager {\r\n private storage: StorageProvider;\r\n\r\n constructor(storage: StorageProvider) {\r\n this.storage = storage;\r\n }\r\n\r\n async createStream(chunkPath: string, range?: { start: number; end: number }): Promise<Readable> {\r\n const data = await this.storage.getChunk(chunkPath);\r\n const stream = new Readable();\r\n\r\n if (range) {\r\n stream.push(data.slice(range.start, range.end + 1));\r\n } else {\r\n stream.push(data);\r\n }\r\n\r\n stream.push(null);\r\n return stream;\r\n }\r\n}","import { spawn } from 'child_process';\r\nimport { VideoEngine } from '../core/VideoEngine';\r\nimport { QualityLevel } from '../core/types';\r\n\r\nexport class GStreamerEngine extends VideoEngine {\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'videoconvert',\r\n '!', 'videoscale',\r\n '!', `video/x-raw,width=-1,height=${quality.height}`,\r\n '!', 'x264enc', `bitrate=${quality.bitrate}`,\r\n '!', 'mp4mux', '!', 'filesink', `location=${outputPath}`\r\n ]);\r\n\r\n gst.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'identity', '-debug', 'duration'\r\n ]);\r\n\r\n let output = '';\r\n gst.stdout.on('data', data => output += data);\r\n gst.on('close', code => {\r\n code === 0 ? resolve(parseFloat(output)) : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n}\r\n","\r\n// storage/StorageProvider.ts\r\nexport abstract class StorageProvider {\r\n abstract saveChunk(chunkPath: string, data: Buffer): Promise<void>;\r\n abstract getChunk(chunkPath: string): Promise<Buffer>;\r\n abstract deleteChunk(chunkPath: string): Promise<void>;\r\n}","\r\n\r\n// storage/FileSystemStorage.ts\r\nimport { promises as fs } from 'fs';\r\nimport { StorageProvider } from './StorageProvider';\r\n\r\nexport class FileSystemStorage extends StorageProvider {\r\n async saveChunk(chunkPath: string, data: Buffer): Promise<void> {\r\n await fs.writeFile(chunkPath, data);\r\n }\r\n\r\n async getChunk(chunkPath: string): Promise<Buffer> {\r\n return fs.readFile(chunkPath);\r\n }\r\n\r\n async deleteChunk(chunkPath: string): Promise<void> {\r\n await fs.unlink(chunkPath);\r\n }\r\n}","\r\n// cache/CacheStrategy.ts\r\nexport interface CacheOptions {\r\n maxSize: number;\r\n ttl: number;\r\n preloadNextChunk: boolean;\r\n externalCacheUrl?: string;\r\n}\r\n\r\nexport abstract class CacheStrategy {\r\n abstract set(key: string, value: Buffer): Promise<void>;\r\n abstract get(key: string): Promise<Buffer | null>;\r\n abstract preload(key: string): Promise<void>;\r\n abstract clear(): Promise<void>;\r\n}","\r\n// cache/LRU.ts\r\nexport class LRU<T> {\r\n private maxSize: number;\r\n private cache: Map<string, T>;\r\n\r\n constructor(maxSize: number) {\r\n this.maxSize = maxSize;\r\n this.cache = new Map();\r\n }\r\n\r\n set(key: string, value: T): void {\r\n if (this.cache.has(key)) {\r\n this.cache.delete(key);\r\n } else if (this.cache.size >= this.maxSize) {\r\n const oldestKey = this.cache.keys().next().value;\r\n if (oldestKey !== undefined) {\r\n this.cache.delete(oldestKey);\r\n }\r\n }\r\n this.cache.set(key, value);\r\n }\r\n\r\n get(key: string): T | undefined {\r\n if (!this.cache.has(key)) return undefined;\r\n const value = this.cache.get(key)!;\r\n this.cache.delete(key);\r\n this.cache.set(key, value);\r\n return value;\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}","\r\n// cache/internalCache.ts\r\nimport { LRU } from './LRU';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\nimport { StorageProvider } from '../storage/StorageProvider';\r\n\r\nexport class InternalCache extends CacheStrategy {\r\n private cache: LRU<Buffer>;\r\n private options: CacheOptions;\r\n private storage: StorageProvider;\r\n\r\n constructor(options: CacheOptions, storage: StorageProvider) {\r\n super();\r\n this.options = options;\r\n this.cache = new LRU(options.maxSize);\r\n this.storage = storage;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n this.cache.set(key, value);\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const cached = this.cache.get(key);\r\n if (cached) return cached;\r\n\r\n try {\r\n const data = await this.storage.getChunk(key);\r\n await this.set(key, data);\r\n return data;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n if (!this.cache.get(nextChunkKey)) {\r\n try {\r\n const data = await this.storage.getChunk(nextChunkKey);\r\n await this.set(nextChunkKey, data);\r\n } catch {\r\n // Ignore preload failures\r\n }\r\n }\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n this.cache.clear();\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","\r\n// cache/ExternalCache.ts\r\nimport fetch from 'node-fetch';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\n\r\nexport class ExternalCache extends CacheStrategy {\r\n private baseUrl: string;\r\n private options: CacheOptions;\r\n\r\n constructor(options: CacheOptions) {\r\n super();\r\n this.options = options;\r\n this.baseUrl = options.externalCacheUrl!;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache/${key}`, {\r\n method: 'POST',\r\n body: value,\r\n headers: {\r\n 'Content-Type': 'application/octet-stream',\r\n 'TTL': this.options.ttl.toString()\r\n }\r\n });\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const response = await fetch(`${this.baseUrl}/cache/${key}`);\r\n return response.ok ? Buffer.from(await response.arrayBuffer()) : null;\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n await fetch(`${this.baseUrl}/preload/${nextChunkKey}`);\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache`, { method: 'DELETE' });\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","\r\n// processor.ts\r\nimport { VideoEngine } from './core/VideoEngine';\r\nimport { EventEmitter } from 'events';\r\nimport { StorageProvider } from './storage/StorageProvider';\r\nimport { StreamManager } from './streaming/StreamManager';\r\nimport { VideoConfig, VideoManifest } from './core/types';\r\nimport { join } from 'path';\r\nimport { promises as fs } from 'fs';\r\nimport { Readable } from 'stream';\r\nimport { VideoEvent } from './core/events';\r\n\r\nexport class VideoProcessor extends EventEmitter {\r\n private engine: VideoEngine;\r\n private streamManager: StreamManager;\r\n private config: VideoConfig;\r\n\r\n constructor(\r\n engine: VideoEngine,\r\n storage: StorageProvider,\r\n config: VideoConfig\r\n ) {\r\n super();\r\n this.engine = engine;\r\n this.streamManager = new StreamManager(storage);\r\n this.config = config;\r\n }\r\n\r\n async processVideo(inputPath: string): Promise<VideoManifest> {\r\n const videoId = await this.generateVideoId(inputPath);\r\n const manifest: VideoManifest = {\r\n videoId,\r\n qualities: this.config.defaultQualities,\r\n chunks: []\r\n };\r\n\r\n const duration = await this.engine.getDuration(inputPath);\r\n const chunks = Math.ceil(duration / this.config.chunkSize);\r\n\r\n for (const quality of this.config.defaultQualities) {\r\n for (let i = 0; i < chunks; i++) {\r\n const chunkPath = this.getChunkPath(videoId, quality.height, i);\r\n\r\n try {\r\n await this.engine.processChunk(\r\n inputPath,\r\n chunkPath,\r\n i * this.config.chunkSize,\r\n quality\r\n );\r\n\r\n manifest.chunks.push({\r\n quality: quality.height,\r\n number: i,\r\n path: chunkPath\r\n });\r\n\r\n this.emit(VideoEvent.CHUNK_PROCESSED, { quality, chunkNumber: i });\r\n } catch (error) {\r\n this.emit(VideoEvent.ERROR, error);\r\n throw error;\r\n }\r\n }\r\n\r\n this.emit(VideoEvent.QUALITY_PROCESSED, quality);\r\n }\r\n\r\n this.emit(VideoEvent.PROCESSING_COMPLETE, manifest);\r\n return manifest;\r\n }\r\n\r\n async streamChunk(\r\n videoId: string,\r\n quality: number,\r\n chunkNumber: number,\r\n range?: { start: number; end: number }\r\n ): Promise<Readable> {\r\n const chunkPath = this.getChunkPath(videoId, quality, chunkNumber);\r\n return this.streamManager.createStream(chunkPath, range);\r\n }\r\n\r\n private getChunkPath(videoId: string, quality: number, chunkNumber: number): string {\r\n return join(this.config.cacheDir, videoId, `${quality}p`, `chunk_${chunkNumber}.mp4`);\r\n }\r\n\r\n private async generateVideoId(inputPath: string): Promise<string> {\r\n const stats = await fs.stat(inputPath);\r\n return `${inputPath.split('/').pop()?.split('.')[0]}_${stats.mtimeMs}`;\r\n }\r\n}"],"names":["fs"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CacheStrategy, CacheOptions } from './cacheStrategy';
|
|
2
|
+
export declare class ExternalCache extends CacheStrategy {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private options;
|
|
5
|
+
constructor(options: CacheOptions);
|
|
6
|
+
set(key: string, value: Buffer): Promise<void>;
|
|
7
|
+
get(key: string): Promise<Buffer | null>;
|
|
8
|
+
preload(key: string): Promise<void>;
|
|
9
|
+
clear(): Promise<void>;
|
|
10
|
+
private getNextChunkKey;
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface CacheOptions {
|
|
2
|
+
maxSize: number;
|
|
3
|
+
ttl: number;
|
|
4
|
+
preloadNextChunk: boolean;
|
|
5
|
+
externalCacheUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare abstract class CacheStrategy {
|
|
8
|
+
abstract set(key: string, value: Buffer): Promise<void>;
|
|
9
|
+
abstract get(key: string): Promise<Buffer | null>;
|
|
10
|
+
abstract preload(key: string): Promise<void>;
|
|
11
|
+
abstract clear(): Promise<void>;
|
|
12
|
+
}
|