streamer-node 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.
- package/README.md +11 -0
- package/dist/public/client.png +0 -0
- package/dist/public/ds.png +0 -0
- package/dist/src/application/interfaces/Logger.d.ts +7 -0
- package/dist/src/application/interfaces/Logger.d.ts.map +1 -0
- package/dist/src/application/interfaces/Logger.js +3 -0
- package/dist/src/application/interfaces/Logger.js.map +1 -0
- package/dist/src/application/interfaces/StartStreamUseCase.types.d.ts +36 -0
- package/dist/src/application/interfaces/StartStreamUseCase.types.d.ts.map +1 -0
- package/dist/src/application/interfaces/StartStreamUseCase.types.js +12 -0
- package/dist/src/application/interfaces/StartStreamUseCase.types.js.map +1 -0
- package/dist/src/application/services/HttpClient.d.ts +5 -0
- package/dist/src/application/services/HttpClient.d.ts.map +1 -0
- package/dist/src/application/services/HttpClient.js +30 -0
- package/dist/src/application/services/HttpClient.js.map +1 -0
- package/dist/src/application/services/StreamManagerService.d.ts +38 -0
- package/dist/src/application/services/StreamManagerService.d.ts.map +1 -0
- package/dist/src/application/services/StreamManagerService.js +298 -0
- package/dist/src/application/services/StreamManagerService.js.map +1 -0
- package/dist/src/application/use-cases/StartStreamUseCase.d.ts +27 -0
- package/dist/src/application/use-cases/StartStreamUseCase.d.ts.map +1 -0
- package/dist/src/application/use-cases/StartStreamUseCase.js +195 -0
- package/dist/src/application/use-cases/StartStreamUseCase.js.map +1 -0
- package/dist/src/application/use-cases/StopStreamUseCase.d.ts +18 -0
- package/dist/src/application/use-cases/StopStreamUseCase.d.ts.map +1 -0
- package/dist/src/application/use-cases/StopStreamUseCase.js +96 -0
- package/dist/src/application/use-cases/StopStreamUseCase.js.map +1 -0
- package/dist/src/domain/entities/Stream.d.ts +40 -0
- package/dist/src/domain/entities/Stream.d.ts.map +1 -0
- package/dist/src/domain/entities/Stream.js +115 -0
- package/dist/src/domain/entities/Stream.js.map +1 -0
- package/dist/src/domain/events/StreamEvent.d.ts +48 -0
- package/dist/src/domain/events/StreamEvent.d.ts.map +1 -0
- package/dist/src/domain/events/StreamEvent.js +3 -0
- package/dist/src/domain/events/StreamEvent.js.map +1 -0
- package/dist/src/domain/repositories/StreamRepository.d.ts +41 -0
- package/dist/src/domain/repositories/StreamRepository.d.ts.map +1 -0
- package/dist/src/domain/repositories/StreamRepository.js +3 -0
- package/dist/src/domain/repositories/StreamRepository.js.map +1 -0
- package/dist/src/domain/services/FFmpegService.d.ts +42 -0
- package/dist/src/domain/services/FFmpegService.d.ts.map +1 -0
- package/dist/src/domain/services/FFmpegService.js +3 -0
- package/dist/src/domain/services/FFmpegService.js.map +1 -0
- package/dist/src/domain/services/SSEService.d.ts +42 -0
- package/dist/src/domain/services/SSEService.d.ts.map +1 -0
- package/dist/src/domain/services/SSEService.js +3 -0
- package/dist/src/domain/services/SSEService.js.map +1 -0
- package/dist/src/domain/value-objects/StreamId.d.ts +10 -0
- package/dist/src/domain/value-objects/StreamId.d.ts.map +1 -0
- package/dist/src/domain/value-objects/StreamId.js +31 -0
- package/dist/src/domain/value-objects/StreamId.js.map +1 -0
- package/dist/src/domain/value-objects/StreamState.d.ts +12 -0
- package/dist/src/domain/value-objects/StreamState.d.ts.map +1 -0
- package/dist/src/domain/value-objects/StreamState.js +35 -0
- package/dist/src/domain/value-objects/StreamState.js.map +1 -0
- package/dist/src/domain/value-objects/StreamUrl.d.ts +12 -0
- package/dist/src/domain/value-objects/StreamUrl.d.ts.map +1 -0
- package/dist/src/domain/value-objects/StreamUrl.js +48 -0
- package/dist/src/domain/value-objects/StreamUrl.js.map +1 -0
- package/dist/src/infrastructure/config/Config.d.ts +42 -0
- package/dist/src/infrastructure/config/Config.d.ts.map +1 -0
- package/dist/src/infrastructure/config/Config.js +113 -0
- package/dist/src/infrastructure/config/Config.js.map +1 -0
- package/dist/src/infrastructure/logging/ConsoleLogger.d.ts +12 -0
- package/dist/src/infrastructure/logging/ConsoleLogger.d.ts.map +1 -0
- package/dist/src/infrastructure/logging/ConsoleLogger.js +41 -0
- package/dist/src/infrastructure/logging/ConsoleLogger.js.map +1 -0
- package/dist/src/infrastructure/logging/LogBuffer.d.ts +20 -0
- package/dist/src/infrastructure/logging/LogBuffer.d.ts.map +1 -0
- package/dist/src/infrastructure/logging/LogBuffer.js +95 -0
- package/dist/src/infrastructure/logging/LogBuffer.js.map +1 -0
- package/dist/src/infrastructure/logging/LogShippingService.d.ts +23 -0
- package/dist/src/infrastructure/logging/LogShippingService.d.ts.map +1 -0
- package/dist/src/infrastructure/logging/LogShippingService.js +128 -0
- package/dist/src/infrastructure/logging/LogShippingService.js.map +1 -0
- package/dist/src/infrastructure/logging/RemoteLogger.d.ts +37 -0
- package/dist/src/infrastructure/logging/RemoteLogger.d.ts.map +1 -0
- package/dist/src/infrastructure/logging/RemoteLogger.js +120 -0
- package/dist/src/infrastructure/logging/RemoteLogger.js.map +1 -0
- package/dist/src/infrastructure/logging/types/LogTypes.d.ts +27 -0
- package/dist/src/infrastructure/logging/types/LogTypes.d.ts.map +1 -0
- package/dist/src/infrastructure/logging/types/LogTypes.js +3 -0
- package/dist/src/infrastructure/logging/types/LogTypes.js.map +1 -0
- package/dist/src/infrastructure/repositories/FileSystemStreamRepository.d.ts +22 -0
- package/dist/src/infrastructure/repositories/FileSystemStreamRepository.d.ts.map +1 -0
- package/dist/src/infrastructure/repositories/FileSystemStreamRepository.js +212 -0
- package/dist/src/infrastructure/repositories/FileSystemStreamRepository.js.map +1 -0
- package/dist/src/infrastructure/services/NodeFFmpegService.d.ts +17 -0
- package/dist/src/infrastructure/services/NodeFFmpegService.d.ts.map +1 -0
- package/dist/src/infrastructure/services/NodeFFmpegService.js +306 -0
- package/dist/src/infrastructure/services/NodeFFmpegService.js.map +1 -0
- package/dist/src/infrastructure/services/NodeSSEService.d.ts +30 -0
- package/dist/src/infrastructure/services/NodeSSEService.d.ts.map +1 -0
- package/dist/src/infrastructure/services/NodeSSEService.js +268 -0
- package/dist/src/infrastructure/services/NodeSSEService.js.map +1 -0
- package/dist/src/main.d.ts +3 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +87 -0
- package/dist/src/main.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StartStreamUseCase = void 0;
|
|
4
|
+
const Stream_1 = require("../../domain/entities/Stream");
|
|
5
|
+
const StreamId_1 = require("../../domain/value-objects/StreamId");
|
|
6
|
+
const StreamUrl_1 = require("../../domain/value-objects/StreamUrl");
|
|
7
|
+
const Config_1 = require("../../infrastructure/config/Config");
|
|
8
|
+
const StreamState_1 = require("../../domain/value-objects/StreamState");
|
|
9
|
+
const StartStreamUseCase_types_1 = require("../interfaces/StartStreamUseCase.types");
|
|
10
|
+
class StartStreamUseCase {
|
|
11
|
+
constructor(streamRepository, ffmpegService, logger, httpClient) {
|
|
12
|
+
this.streamRepository = streamRepository;
|
|
13
|
+
this.ffmpegService = ffmpegService;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.httpClient = httpClient;
|
|
16
|
+
this.config = Config_1.Config.getInstance().get();
|
|
17
|
+
}
|
|
18
|
+
async shouldStartNewStream(event, stopStream) {
|
|
19
|
+
// Find stream by camera URL and stream key
|
|
20
|
+
const streams = await this.streamRepository.findAll();
|
|
21
|
+
const runningStreams = streams.filter((stream) => stream.courtId === event.courtId && stream.state === StreamState_1.StreamState.RUNNING);
|
|
22
|
+
const action = await this.validateStreamEvent(runningStreams, event);
|
|
23
|
+
if (action.isValid)
|
|
24
|
+
return { isValid: true }; // return true for happy case
|
|
25
|
+
// handle other event types
|
|
26
|
+
const streamEvent = action.data;
|
|
27
|
+
switch (streamEvent.action) {
|
|
28
|
+
// stop all running streams, and run against the only one event received
|
|
29
|
+
case StartStreamUseCase_types_1.StreamAction.MULTIPLE_STREAMS_RUNNING:
|
|
30
|
+
await Promise.all(streamEvent.streamList.map((stream) => stopStream({
|
|
31
|
+
streamId: stream.id.toString(),
|
|
32
|
+
})));
|
|
33
|
+
return { isValid: true };
|
|
34
|
+
// update the file with the failed state against process
|
|
35
|
+
case StartStreamUseCase_types_1.StreamAction.STREAM_RUNNING_WITHOUT_PID:
|
|
36
|
+
case StartStreamUseCase_types_1.StreamAction.DEAD_PROCESS_DETECTED:
|
|
37
|
+
streamEvent.stream.markAsFailed();
|
|
38
|
+
await this.streamRepository.save(streamEvent.stream);
|
|
39
|
+
return { isValid: true };
|
|
40
|
+
// ignore the event is received for a stream that is already running
|
|
41
|
+
case StartStreamUseCase_types_1.StreamAction.DUPLICATE_EVENT:
|
|
42
|
+
return {
|
|
43
|
+
isValid: false,
|
|
44
|
+
data: {
|
|
45
|
+
streamId: streamEvent.stream.id.toString(),
|
|
46
|
+
hasAudio: streamEvent.stream.hasAudio,
|
|
47
|
+
processId: streamEvent.stream.processId,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
// restart the stream since the youtube stream key is no longer valid
|
|
51
|
+
case StartStreamUseCase_types_1.StreamAction.INVALID_YOUTUBE_STREAM_KEY:
|
|
52
|
+
await stopStream({
|
|
53
|
+
streamId: streamEvent.stream.id.toString(),
|
|
54
|
+
});
|
|
55
|
+
return { isValid: true };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validates if a new stream can be started by checking:
|
|
60
|
+
* 1. No existing streams (valid to start)
|
|
61
|
+
* 2. Multiple streams on same court (invalid)
|
|
62
|
+
* 3. Stream exists but missing process ID (invalid)
|
|
63
|
+
* 4. Different stream key than existing (invalid)
|
|
64
|
+
* 5. Process already running (duplicate event)
|
|
65
|
+
* 6. Process dead but stream marked as running (stale state)
|
|
66
|
+
*/
|
|
67
|
+
async validateStreamEvent(runningStreams, event) {
|
|
68
|
+
const targetStream = runningStreams.length > 0 ? runningStreams[0] : null;
|
|
69
|
+
if (!targetStream) {
|
|
70
|
+
// there is no running stream. Safe to say stream can be started
|
|
71
|
+
return {
|
|
72
|
+
isValid: true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// check if there are multiple streams on a single court - ideally this should never be the case
|
|
76
|
+
if (runningStreams.length > 1) {
|
|
77
|
+
return {
|
|
78
|
+
isValid: false,
|
|
79
|
+
data: {
|
|
80
|
+
action: StartStreamUseCase_types_1.StreamAction.MULTIPLE_STREAMS_RUNNING,
|
|
81
|
+
streamList: runningStreams,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// stream file info update issue - stream was saved without pid - ideally this should never be the case
|
|
86
|
+
if (!targetStream.processId) {
|
|
87
|
+
return {
|
|
88
|
+
isValid: false,
|
|
89
|
+
data: {
|
|
90
|
+
action: StartStreamUseCase_types_1.StreamAction.STREAM_RUNNING_WITHOUT_PID,
|
|
91
|
+
stream: targetStream,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// check if the incoming event is not a duplicate
|
|
96
|
+
if (targetStream.streamKey !== event.streamKey) {
|
|
97
|
+
return {
|
|
98
|
+
isValid: false,
|
|
99
|
+
data: {
|
|
100
|
+
action: StartStreamUseCase_types_1.StreamAction.INVALID_YOUTUBE_STREAM_KEY,
|
|
101
|
+
stream: targetStream,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const ffmpegProcess = await this.ffmpegService.isProcessRunning(targetStream.processId);
|
|
106
|
+
// check if the process is already running - this means the event is duplicate
|
|
107
|
+
if (ffmpegProcess) {
|
|
108
|
+
return {
|
|
109
|
+
isValid: false,
|
|
110
|
+
data: {
|
|
111
|
+
action: StartStreamUseCase_types_1.StreamAction.DUPLICATE_EVENT,
|
|
112
|
+
stream: targetStream,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
isValid: false,
|
|
118
|
+
data: {
|
|
119
|
+
action: StartStreamUseCase_types_1.StreamAction.DEAD_PROCESS_DETECTED,
|
|
120
|
+
stream: targetStream,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async execute(request, stopProcess) {
|
|
125
|
+
// check if the stream should be started
|
|
126
|
+
const handleResponse = await this.shouldStartNewStream(request, stopProcess);
|
|
127
|
+
if (!handleResponse.isValid)
|
|
128
|
+
return handleResponse.data;
|
|
129
|
+
this.logger.info("Starting stream", {
|
|
130
|
+
cameraUrl: request.cameraUrl,
|
|
131
|
+
streamKey: request.streamKey,
|
|
132
|
+
courtId: request.courtId,
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
// Create value objects
|
|
136
|
+
const streamId = StreamId_1.StreamId.create();
|
|
137
|
+
const cameraUrl = StreamUrl_1.StreamUrl.create(request.cameraUrl);
|
|
138
|
+
// Detect audio if requested
|
|
139
|
+
let hasAudio = false;
|
|
140
|
+
if (request.detectAudio) {
|
|
141
|
+
this.logger.info("Detecting audio for stream", {
|
|
142
|
+
streamId: streamId.value,
|
|
143
|
+
});
|
|
144
|
+
hasAudio = await this.ffmpegService.detectAudio(cameraUrl);
|
|
145
|
+
this.logger.info("Audio detection result", {
|
|
146
|
+
streamId: streamId.value,
|
|
147
|
+
hasAudio,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
// Create stream entity
|
|
151
|
+
const stream = Stream_1.Stream.create(streamId, cameraUrl, request.streamKey, request.courtId, hasAudio);
|
|
152
|
+
// Start FFmpeg process
|
|
153
|
+
this.logger.info("Starting FFmpeg process", { streamId: streamId.value });
|
|
154
|
+
// Create PID update callback for retries
|
|
155
|
+
const onPidUpdate = async (newPid) => {
|
|
156
|
+
this.logger.info("Updating stream PID after retry", {
|
|
157
|
+
streamId: streamId.value,
|
|
158
|
+
oldPid: stream.processId,
|
|
159
|
+
newPid,
|
|
160
|
+
});
|
|
161
|
+
stream.updateProcessId(newPid);
|
|
162
|
+
await this.streamRepository.save(stream);
|
|
163
|
+
};
|
|
164
|
+
const ffmpegProcess = await this.ffmpegService.startStream(cameraUrl, request.streamKey, hasAudio, 5, // maxRetries
|
|
165
|
+
5000, // retryDelayMs
|
|
166
|
+
onPidUpdate);
|
|
167
|
+
// Update stream with process ID
|
|
168
|
+
stream.start(ffmpegProcess.pid);
|
|
169
|
+
// Save stream state
|
|
170
|
+
await this.streamRepository.save(stream);
|
|
171
|
+
this.logger.info("Stream started successfully", {
|
|
172
|
+
streamId: streamId.value,
|
|
173
|
+
processId: ffmpegProcess.pid,
|
|
174
|
+
hasAudio,
|
|
175
|
+
});
|
|
176
|
+
// notify server that the stream has started to go live on YouTube
|
|
177
|
+
await this.httpClient.goLiveYouTube(this.config.groundInfo.groundId, stream.courtId, stream.streamKey);
|
|
178
|
+
return {
|
|
179
|
+
streamId: streamId.value,
|
|
180
|
+
processId: ffmpegProcess.pid,
|
|
181
|
+
hasAudio,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
this.logger.error("Failed to start stream", {
|
|
186
|
+
error: error instanceof Error ? error.message : String(error),
|
|
187
|
+
cameraUrl: request.cameraUrl,
|
|
188
|
+
streamKey: request.streamKey,
|
|
189
|
+
});
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.StartStreamUseCase = StartStreamUseCase;
|
|
195
|
+
//# sourceMappingURL=StartStreamUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StartStreamUseCase.js","sourceRoot":"","sources":["../../../../src/application/use-cases/StartStreamUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StreamRepository } from "../../domain/repositories/StreamRepository";
|
|
2
|
+
import { FFmpegService } from "../../domain/services/FFmpegService";
|
|
3
|
+
import { Logger } from "../interfaces/Logger";
|
|
4
|
+
export interface StopStreamRequest {
|
|
5
|
+
streamId: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StopStreamResponse {
|
|
8
|
+
streamId: string;
|
|
9
|
+
stopped: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class StopStreamUseCase {
|
|
12
|
+
private readonly streamRepository;
|
|
13
|
+
private readonly ffmpegService;
|
|
14
|
+
private readonly logger;
|
|
15
|
+
constructor(streamRepository: StreamRepository, ffmpegService: FFmpegService, logger: Logger);
|
|
16
|
+
execute(request: StopStreamRequest): Promise<StopStreamResponse>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=StopStreamUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StopStreamUseCase.d.ts","sourceRoot":"","sources":["../../../../src/application/use-cases/StopStreamUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,gBAAgB,EAAE,gBAAgB,EAClC,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM;IAGpB,OAAO,CAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,kBAAkB,CAAC;CAuG/B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StopStreamUseCase = void 0;
|
|
4
|
+
const StreamId_1 = require("../../domain/value-objects/StreamId");
|
|
5
|
+
class StopStreamUseCase {
|
|
6
|
+
constructor(streamRepository, ffmpegService, logger) {
|
|
7
|
+
this.streamRepository = streamRepository;
|
|
8
|
+
this.ffmpegService = ffmpegService;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
}
|
|
11
|
+
async execute(request) {
|
|
12
|
+
this.logger.info("Stopping stream", { streamId: request.streamId });
|
|
13
|
+
try {
|
|
14
|
+
const streamId = StreamId_1.StreamId.fromString(request.streamId);
|
|
15
|
+
// Find the stream
|
|
16
|
+
const stream = await this.streamRepository.findById(streamId);
|
|
17
|
+
if (!stream) {
|
|
18
|
+
this.logger.warn("Stream not found", { streamId: request.streamId });
|
|
19
|
+
return { streamId: request.streamId, stopped: false };
|
|
20
|
+
}
|
|
21
|
+
// Stop FFmpeg process if it has a PID and the process is actually running
|
|
22
|
+
if (stream.processId) {
|
|
23
|
+
const isProcessRunning = await this.ffmpegService.isProcessRunning(stream.processId);
|
|
24
|
+
this.logger.info("Checking FFmpeg process status", {
|
|
25
|
+
streamId: request.streamId,
|
|
26
|
+
processId: stream.processId,
|
|
27
|
+
isProcessRunning,
|
|
28
|
+
streamState: stream.state,
|
|
29
|
+
});
|
|
30
|
+
if (isProcessRunning) {
|
|
31
|
+
this.logger.info("Stopping running FFmpeg process", {
|
|
32
|
+
streamId: request.streamId,
|
|
33
|
+
processId: stream.processId,
|
|
34
|
+
});
|
|
35
|
+
await this.ffmpegService.stopStream(stream.processId);
|
|
36
|
+
// Clear the process ID after successfully stopping the process
|
|
37
|
+
stream.clearProcessId();
|
|
38
|
+
this.logger.info("FFmpeg process stopped and process ID cleared", {
|
|
39
|
+
streamId: request.streamId,
|
|
40
|
+
processId: stream.processId,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.logger.info("FFmpeg process not running, skipping process termination", {
|
|
45
|
+
streamId: request.streamId,
|
|
46
|
+
processId: stream.processId,
|
|
47
|
+
});
|
|
48
|
+
// Clear the process ID since the process is not running anyway
|
|
49
|
+
stream.clearProcessId();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
this.logger.info("No process ID found for stream, skipping process termination", {
|
|
54
|
+
streamId: request.streamId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// Update stream state - handle different states gracefully
|
|
58
|
+
if (stream.isRunning()) {
|
|
59
|
+
stream.stop();
|
|
60
|
+
}
|
|
61
|
+
else if (stream.isFailed()) {
|
|
62
|
+
// For failed streams, we've already cleaned up the process above
|
|
63
|
+
// The stream remains in failed state, but the process is properly terminated
|
|
64
|
+
this.logger.info("Stream was in failed state, process cleanup completed", {
|
|
65
|
+
streamId: request.streamId,
|
|
66
|
+
currentState: stream.state,
|
|
67
|
+
});
|
|
68
|
+
// Don't change the state - failed streams should remain failed until explicitly restarted
|
|
69
|
+
// The important part is that we've cleaned up any running processes
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.logger.info("Stream already in stopped/pending state", {
|
|
73
|
+
streamId: request.streamId,
|
|
74
|
+
currentState: stream.state,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
await this.streamRepository.save(stream);
|
|
78
|
+
this.logger.info("Stream stopped successfully", {
|
|
79
|
+
streamId: request.streamId,
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
streamId: request.streamId,
|
|
83
|
+
stopped: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.logger.error("Failed to stop stream", {
|
|
88
|
+
error: error instanceof Error ? error.message : String(error),
|
|
89
|
+
streamId: request.streamId,
|
|
90
|
+
});
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.StopStreamUseCase = StopStreamUseCase;
|
|
96
|
+
//# sourceMappingURL=StopStreamUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StopStreamUseCase.js","sourceRoot":"","sources":["../../../../src/application/use-cases/StopStreamUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { StreamId } from "../value-objects/StreamId";
|
|
2
|
+
import { StreamUrl } from "../value-objects/StreamUrl";
|
|
3
|
+
import { StreamState } from "../value-objects/StreamState";
|
|
4
|
+
export interface StreamProps {
|
|
5
|
+
id: StreamId;
|
|
6
|
+
cameraUrl: StreamUrl;
|
|
7
|
+
streamKey: string;
|
|
8
|
+
courtId: string;
|
|
9
|
+
state: StreamState;
|
|
10
|
+
hasAudio: boolean;
|
|
11
|
+
processId?: number;
|
|
12
|
+
createdAt: Date;
|
|
13
|
+
updatedAt: Date;
|
|
14
|
+
}
|
|
15
|
+
export declare class Stream {
|
|
16
|
+
private props;
|
|
17
|
+
private constructor();
|
|
18
|
+
static create(id: StreamId, cameraUrl: StreamUrl, streamKey: string, courtId: string, hasAudio?: boolean): Stream;
|
|
19
|
+
static fromPersistence(props: StreamProps): Stream;
|
|
20
|
+
get id(): StreamId;
|
|
21
|
+
get cameraUrl(): StreamUrl;
|
|
22
|
+
get streamKey(): string;
|
|
23
|
+
get courtId(): string;
|
|
24
|
+
get state(): StreamState;
|
|
25
|
+
get hasAudio(): boolean;
|
|
26
|
+
get processId(): number | undefined;
|
|
27
|
+
get createdAt(): Date;
|
|
28
|
+
get updatedAt(): Date;
|
|
29
|
+
start(processId: number): void;
|
|
30
|
+
stop(): void;
|
|
31
|
+
markAsFailed(error?: string): void;
|
|
32
|
+
updateAudioDetection(hasAudio: boolean): void;
|
|
33
|
+
clearProcessId(): void;
|
|
34
|
+
updateProcessId(processId: number): void;
|
|
35
|
+
isRunning(): boolean;
|
|
36
|
+
isStopped(): boolean;
|
|
37
|
+
isFailed(): boolean;
|
|
38
|
+
toJSON(): any;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=Stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stream.d.ts","sourceRoot":"","sources":["../../../../src/domain/entities/Stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,QAAQ,CAAC;IACb,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,MAAM;IACG,OAAO,CAAC,KAAK;IAAjC,OAAO;WAEO,MAAM,CAClB,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,OAAe,GACxB,MAAM;WAcK,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM;IAKzD,IAAW,EAAE,IAAI,QAAQ,CAExB;IAED,IAAW,SAAS,IAAI,SAAS,CAEhC;IAED,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,OAAO,IAAI,MAAM,CAE3B;IAED,IAAW,KAAK,IAAI,WAAW,CAE9B;IAED,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,IAAW,SAAS,IAAI,MAAM,GAAG,SAAS,CAEzC;IAED,IAAW,SAAS,IAAI,IAAI,CAE3B;IAED,IAAW,SAAS,IAAI,IAAI,CAE3B;IAGM,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAa9B,IAAI,IAAI,IAAI;IAUZ,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAMlC,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAK7C,cAAc,IAAI,IAAI;IAKtB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IASxC,SAAS,IAAI,OAAO;IAIpB,SAAS,IAAI,OAAO;IAIpB,QAAQ,IAAI,OAAO;IAInB,MAAM,IAAI,GAAG;CAarB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Stream = void 0;
|
|
4
|
+
const StreamState_1 = require("../value-objects/StreamState");
|
|
5
|
+
class Stream {
|
|
6
|
+
constructor(props) {
|
|
7
|
+
this.props = props;
|
|
8
|
+
}
|
|
9
|
+
static create(id, cameraUrl, streamKey, courtId, hasAudio = false) {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
return new Stream({
|
|
12
|
+
id,
|
|
13
|
+
cameraUrl,
|
|
14
|
+
streamKey,
|
|
15
|
+
courtId,
|
|
16
|
+
state: StreamState_1.StreamState.PENDING,
|
|
17
|
+
hasAudio,
|
|
18
|
+
createdAt: now,
|
|
19
|
+
updatedAt: now,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
static fromPersistence(props) {
|
|
23
|
+
return new Stream(props);
|
|
24
|
+
}
|
|
25
|
+
// Getters
|
|
26
|
+
get id() {
|
|
27
|
+
return this.props.id;
|
|
28
|
+
}
|
|
29
|
+
get cameraUrl() {
|
|
30
|
+
return this.props.cameraUrl;
|
|
31
|
+
}
|
|
32
|
+
get streamKey() {
|
|
33
|
+
return this.props.streamKey;
|
|
34
|
+
}
|
|
35
|
+
get courtId() {
|
|
36
|
+
return this.props.courtId;
|
|
37
|
+
}
|
|
38
|
+
get state() {
|
|
39
|
+
return this.props.state;
|
|
40
|
+
}
|
|
41
|
+
get hasAudio() {
|
|
42
|
+
return this.props.hasAudio;
|
|
43
|
+
}
|
|
44
|
+
get processId() {
|
|
45
|
+
return this.props.processId;
|
|
46
|
+
}
|
|
47
|
+
get createdAt() {
|
|
48
|
+
return this.props.createdAt;
|
|
49
|
+
}
|
|
50
|
+
get updatedAt() {
|
|
51
|
+
return this.props.updatedAt;
|
|
52
|
+
}
|
|
53
|
+
// Business methods
|
|
54
|
+
start(processId) {
|
|
55
|
+
if (this.props.state !== StreamState_1.StreamState.PENDING &&
|
|
56
|
+
this.props.state !== StreamState_1.StreamState.STOPPED) {
|
|
57
|
+
throw new Error(`Cannot start stream in ${this.props.state} state`);
|
|
58
|
+
}
|
|
59
|
+
this.props.state = StreamState_1.StreamState.RUNNING;
|
|
60
|
+
this.props.processId = processId;
|
|
61
|
+
this.props.updatedAt = new Date();
|
|
62
|
+
}
|
|
63
|
+
stop() {
|
|
64
|
+
if (this.props.state !== StreamState_1.StreamState.RUNNING) {
|
|
65
|
+
throw new Error(`Cannot stop stream in ${this.props.state} state`);
|
|
66
|
+
}
|
|
67
|
+
this.props.state = StreamState_1.StreamState.STOPPED;
|
|
68
|
+
this.props.processId = undefined;
|
|
69
|
+
this.props.updatedAt = new Date();
|
|
70
|
+
}
|
|
71
|
+
markAsFailed(error) {
|
|
72
|
+
this.props.state = StreamState_1.StreamState.FAILED;
|
|
73
|
+
// Keep processId so we can still terminate the process later
|
|
74
|
+
this.props.updatedAt = new Date();
|
|
75
|
+
}
|
|
76
|
+
updateAudioDetection(hasAudio) {
|
|
77
|
+
this.props.hasAudio = hasAudio;
|
|
78
|
+
this.props.updatedAt = new Date();
|
|
79
|
+
}
|
|
80
|
+
clearProcessId() {
|
|
81
|
+
this.props.processId = undefined;
|
|
82
|
+
this.props.updatedAt = new Date();
|
|
83
|
+
}
|
|
84
|
+
updateProcessId(processId) {
|
|
85
|
+
if (this.props.state !== StreamState_1.StreamState.RUNNING) {
|
|
86
|
+
throw new Error(`Cannot update processId for stream in ${this.props.state} state`);
|
|
87
|
+
}
|
|
88
|
+
this.props.processId = processId;
|
|
89
|
+
this.props.updatedAt = new Date();
|
|
90
|
+
}
|
|
91
|
+
isRunning() {
|
|
92
|
+
return this.props.state === StreamState_1.StreamState.RUNNING;
|
|
93
|
+
}
|
|
94
|
+
isStopped() {
|
|
95
|
+
return this.props.state === StreamState_1.StreamState.STOPPED;
|
|
96
|
+
}
|
|
97
|
+
isFailed() {
|
|
98
|
+
return this.props.state === StreamState_1.StreamState.FAILED;
|
|
99
|
+
}
|
|
100
|
+
toJSON() {
|
|
101
|
+
return {
|
|
102
|
+
id: this.props.id.value,
|
|
103
|
+
cameraUrl: this.props.cameraUrl.value,
|
|
104
|
+
streamKey: this.props.streamKey,
|
|
105
|
+
courtId: this.props.courtId,
|
|
106
|
+
state: this.props.state,
|
|
107
|
+
hasAudio: this.props.hasAudio,
|
|
108
|
+
processId: this.props.processId,
|
|
109
|
+
createdAt: this.props.createdAt.toISOString(),
|
|
110
|
+
updatedAt: this.props.updatedAt.toISOString(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.Stream = Stream;
|
|
115
|
+
//# sourceMappingURL=Stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stream.js","sourceRoot":"","sources":["../../../../src/domain/entities/Stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { StreamId } from "../value-objects/StreamId";
|
|
2
|
+
export interface DomainEvent {
|
|
3
|
+
readonly eventId: string;
|
|
4
|
+
readonly occurredOn: Date;
|
|
5
|
+
readonly eventType: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StreamStartedEvent extends DomainEvent {
|
|
8
|
+
readonly eventType: "StreamStarted";
|
|
9
|
+
readonly streamId: StreamId;
|
|
10
|
+
readonly processId: number;
|
|
11
|
+
readonly cameraUrl: string;
|
|
12
|
+
readonly streamKey: string;
|
|
13
|
+
readonly courtId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface StreamStoppedEvent extends DomainEvent {
|
|
16
|
+
readonly eventType: "StreamStopped";
|
|
17
|
+
readonly streamId: StreamId;
|
|
18
|
+
readonly courtId: string;
|
|
19
|
+
readonly reason?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface StreamFailedEvent extends DomainEvent {
|
|
22
|
+
readonly eventType: "StreamFailed";
|
|
23
|
+
readonly streamId: StreamId;
|
|
24
|
+
readonly courtId: string;
|
|
25
|
+
readonly error: string;
|
|
26
|
+
readonly processId?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface AudioDetectedEvent extends DomainEvent {
|
|
29
|
+
readonly eventType: "AudioDetected";
|
|
30
|
+
readonly streamId: StreamId;
|
|
31
|
+
readonly courtId: string;
|
|
32
|
+
readonly hasAudio: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface SSEConnectionEvent extends DomainEvent {
|
|
35
|
+
readonly eventType: "SSEConnectionEvent";
|
|
36
|
+
readonly status: "connected" | "disconnected" | "reconnecting";
|
|
37
|
+
readonly retryCount?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface SSEStreamEvent extends DomainEvent {
|
|
40
|
+
readonly eventType: "SSEStreamEvent";
|
|
41
|
+
readonly courtId: string;
|
|
42
|
+
readonly action: "start" | "stop";
|
|
43
|
+
readonly cameraUrl: string;
|
|
44
|
+
readonly streamKey: string;
|
|
45
|
+
readonly reconciliationMode?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export type StreamDomainEvent = StreamStartedEvent | StreamStoppedEvent | StreamFailedEvent | AudioDetectedEvent | SSEConnectionEvent | SSEStreamEvent;
|
|
48
|
+
//# sourceMappingURL=StreamEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamEvent.d.ts","sourceRoot":"","sources":["../../../../src/domain/events/StreamEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,kBAAkB,GAClB,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamEvent.js","sourceRoot":"","sources":["../../../../src/domain/events/StreamEvent.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Stream } from '../entities/Stream';
|
|
2
|
+
import { StreamId } from '../value-objects/StreamId';
|
|
3
|
+
export interface StreamRepository {
|
|
4
|
+
/**
|
|
5
|
+
* Save a stream to persistent storage
|
|
6
|
+
*/
|
|
7
|
+
save(stream: Stream): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Find a stream by its ID
|
|
10
|
+
*/
|
|
11
|
+
findById(id: StreamId): Promise<Stream | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Find all streams
|
|
14
|
+
*/
|
|
15
|
+
findAll(): Promise<Stream[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Find all running streams
|
|
18
|
+
*/
|
|
19
|
+
findRunning(): Promise<Stream[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Find streams by state
|
|
22
|
+
*/
|
|
23
|
+
findByState(state: string): Promise<Stream[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Delete a stream
|
|
26
|
+
*/
|
|
27
|
+
delete(id: StreamId): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a stream exists
|
|
30
|
+
*/
|
|
31
|
+
exists(id: StreamId): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Get all stream IDs
|
|
34
|
+
*/
|
|
35
|
+
getAllIds(): Promise<StreamId[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all streams (for testing/cleanup)
|
|
38
|
+
*/
|
|
39
|
+
clear(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=StreamRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/repositories/StreamRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE/C;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7B;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjC;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9C;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEjC;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamRepository.js","sourceRoot":"","sources":["../../../../src/domain/repositories/StreamRepository.ts"],"names":[],"mappings":""}
|