Skip to content

Commit 6c1a0fe

Browse files
committed
feat(camera): add intelligent OEM fallback stream detection
1 parent 41a92bb commit 6c1a0fe

17 files changed

+100
-17
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"author": "Parallel-7",
7272
"license": "MIT",
7373
"dependencies": {
74-
"@ghosttypes/ff-api": "^1.2.0",
74+
"@ghosttypes/ff-api": "^1.3.0",
7575
"@parallel-7/slicer-meta": "1.1.0-20251121155836",
7676
"axios": "^1.8.4",
7777
"express": "^5.1.0",

src/printer-backends/AD5XBackend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class AD5XBackend extends DualAPIBackend {
4848
return {
4949
camera: {
5050
oemStreamUrl: '',
51+
fallbackStreamUrl: '',
5152
customUrl: null,
5253
customEnabled: false,
5354
},

src/printer-backends/Adventurer5MBackend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class Adventurer5MBackend extends DualAPIBackend {
3434
return {
3535
camera: {
3636
oemStreamUrl: '',
37+
fallbackStreamUrl: '',
3738
customUrl: null,
3839
customEnabled: false,
3940
},

src/printer-backends/Adventurer5MProBackend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class Adventurer5MProBackend extends DualAPIBackend {
3434
return {
3535
camera: {
3636
oemStreamUrl: '',
37+
fallbackStreamUrl: '',
3738
customUrl: null,
3839
customEnabled: false,
3940
},

src/printer-backends/BasePrinterBackend.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export abstract class BasePrinterBackend extends EventEmitter {
8181
protected customLedsEnabled: boolean;
8282
protected forceLegacyMode: boolean;
8383
protected oemCameraStreamUrl: string = '';
84+
protected fallbackCameraStreamUrl: string = '';
8485

8586
constructor(options: BackendInitOptions) {
8687
super();
@@ -165,6 +166,18 @@ export abstract class BasePrinterBackend extends EventEmitter {
165166
return true;
166167
}
167168

169+
protected updateFallbackCameraStreamUrl(streamUrl: string | null | undefined): boolean {
170+
const nextFallbackCameraStreamUrl = typeof streamUrl === 'string' ? streamUrl.trim() : '';
171+
172+
if (this.fallbackCameraStreamUrl === nextFallbackCameraStreamUrl) {
173+
return false;
174+
}
175+
176+
this.fallbackCameraStreamUrl = nextFallbackCameraStreamUrl;
177+
this.rebuildFeatureSet(true, ['fallbackCameraStreamUrl']);
178+
return true;
179+
}
180+
168181
/**
169182
* Emit backend event
170183
*/
@@ -252,6 +265,7 @@ export abstract class BasePrinterBackend extends EventEmitter {
252265
return {
253266
camera: {
254267
oemStreamUrl: baseFeatures.camera.oemStreamUrl || this.oemCameraStreamUrl,
268+
fallbackStreamUrl: baseFeatures.camera.fallbackStreamUrl || this.fallbackCameraStreamUrl,
255269
customUrl: settingsOverrides.customCameraEnabled
256270
? String(settingsOverrides.customCameraUrl)
257271
: null,
@@ -329,6 +343,7 @@ export abstract class BasePrinterBackend extends EventEmitter {
329343
case 'camera':
330344
return (
331345
this.features.camera.oemStreamUrl.trim() !== '' ||
346+
this.features.camera.fallbackStreamUrl.trim() !== '' ||
332347
(this.features.camera.customEnabled &&
333348
this.features.camera.customUrl !== null &&
334349
this.features.camera.customUrl.trim() !== '')

src/printer-backends/DualAPIBackend.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ import { BasePrinterBackend } from './BasePrinterBackend';
3939
* Provides common implementation for printers using both FiveMClient and FlashForgeClient
4040
*/
4141
export abstract class DualAPIBackend extends BasePrinterBackend {
42+
private static readonly FALLBACK_CAMERA_RETRY_MS = 60_000;
43+
4244
protected fiveMClient!: FiveMClient;
4345
protected legacyClient!: FlashForgeClient;
4446
protected productInfo: Product | null = null;
47+
private lastFallbackCameraProbeAt = 0;
4548

4649
/**
4750
* Cache for last known filament usage data while printing
@@ -861,6 +864,25 @@ export abstract class DualAPIBackend extends BasePrinterBackend {
861864
const info = _machineInfo as { CameraStreamUrl?: unknown } | null;
862865
const cameraStreamUrl = typeof info?.CameraStreamUrl === 'string' ? info.CameraStreamUrl : '';
863866
this.updateOEMCameraStreamUrl(cameraStreamUrl);
867+
868+
if (cameraStreamUrl.trim() !== '') {
869+
this.updateFallbackCameraStreamUrl('');
870+
this.lastFallbackCameraProbeAt = 0;
871+
return;
872+
}
873+
874+
if (this.fallbackCameraStreamUrl.trim() !== '') {
875+
return;
876+
}
877+
878+
const now = Date.now();
879+
if (now - this.lastFallbackCameraProbeAt < DualAPIBackend.FALLBACK_CAMERA_RETRY_MS) {
880+
return;
881+
}
882+
883+
this.lastFallbackCameraProbeAt = now;
884+
const detectedCameraStreamUrl = await this.fiveMClient.detectCameraStream();
885+
this.updateFallbackCameraStreamUrl(detectedCameraStreamUrl);
864886
}
865887

866888
/**
@@ -889,6 +911,7 @@ export abstract class DualAPIBackend extends BasePrinterBackend {
889911
public async dispose(): Promise<void> {
890912
// Clear filament usage cache on disconnect
891913
this.lastFilamentUsageCache = null;
914+
this.lastFallbackCameraProbeAt = 0;
892915

893916
// Call parent dispose to clean up clients
894917
await super.dispose();

src/printer-backends/GenericLegacyBackend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export class GenericLegacyBackend extends BasePrinterBackend {
6565
return {
6666
camera: {
6767
oemStreamUrl: '',
68+
fallbackStreamUrl: '',
6869
customUrl: null,
6970
customEnabled: false,
7071
},

src/services/CameraStreamCoordinator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ export interface EnsuredCameraStream {
2121
readonly streamConfig: CameraStreamConfig;
2222
}
2323

24-
function isGo2rtcSourceType(sourceType: CameraSourceType): sourceType is 'oem' | 'custom' {
25-
return sourceType === 'oem' || sourceType === 'custom';
24+
function isGo2rtcSourceType(
25+
sourceType: CameraSourceType
26+
): sourceType is 'oem' | 'custom' | 'intelligent-fallback' {
27+
return sourceType === 'oem' || sourceType === 'custom' || sourceType === 'intelligent-fallback';
2628
}
2729

2830
export async function resolveAndEnsureCameraStream(

src/services/Go2rtcService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface ManagedStream {
1818
contextId: string;
1919
streamName: string;
2020
sourceUrl: string;
21-
sourceType: 'oem' | 'custom';
21+
sourceType: 'oem' | 'custom' | 'intelligent-fallback';
2222
streamType: 'mjpeg' | 'rtsp';
2323
addedAt: number;
2424
}
@@ -146,7 +146,7 @@ export class Go2rtcService extends EventEmitter<Go2rtcServiceEvents> {
146146
public hasMatchingStream(
147147
contextId: string,
148148
sourceUrl: string,
149-
sourceType: 'oem' | 'custom',
149+
sourceType: 'oem' | 'custom' | 'intelligent-fallback',
150150
streamType: 'mjpeg' | 'rtsp'
151151
): boolean {
152152
const stream = this.streams.get(contextId);
@@ -163,7 +163,7 @@ export class Go2rtcService extends EventEmitter<Go2rtcServiceEvents> {
163163
public async addStream(
164164
contextId: string,
165165
sourceUrl: string,
166-
sourceType: 'oem' | 'custom',
166+
sourceType: 'oem' | 'custom' | 'intelligent-fallback',
167167
streamType: 'mjpeg' | 'rtsp'
168168
): Promise<void> {
169169
if (!this.isRunning()) {

0 commit comments

Comments
 (0)