Skip to content

Commit a1b502a

Browse files
committed
feat: Don't show image, when nothing happen + show how much time waiting
1 parent 57b85a7 commit a1b502a

2 files changed

Lines changed: 59 additions & 15 deletions

File tree

src/lib/server/nina.ts

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sharp from 'sharp';
12
import { NINA_BASE_URL, UPDATE_THRESHOLD_COUNT } from './variables';
23

34
interface NinaResponse<T> {
@@ -27,13 +28,16 @@ interface SequenceItem {
2728
Items?: SequenceItem[];
2829
Name?: string;
2930
Triggers?: SequenceItem[];
31+
TargetTime?: string;
32+
Delay?: number;
3033
}
3134

3235
export type LiveStatus = {
3336
active: boolean;
3437
imageInfo?: ImageHistoryItem;
3538
mountInfo?: MountInfo;
3639
currentAction?: string;
40+
showImage?: boolean;
3741
};
3842

3943
function isNetworkError(e: unknown, code: string): boolean {
@@ -77,7 +81,8 @@ export class NinaClient {
7781
}
7882
}
7983

80-
private mapRunningAction(name: string): string {
84+
private mapRunningAction(item: SequenceItem): string {
85+
const name = item.Name || '';
8186
const map: Record<string, string> = {
8287
'Meridian Flip_Trigger': 'Meridian flip',
8388
'Smart Exposure': 'Exposing',
@@ -88,31 +93,44 @@ export class NinaClient {
8893
'AF After HFR Increase_Trigger': 'Focusing',
8994
'Cool Camera': 'Cooling Camera',
9095
'Wait for Time': 'Waiting',
91-
'Wait for Time Span': 'Waiting'
96+
'Wait for Time Span': 'Waiting',
97+
'Warm Camera': 'Warming Camera'
9298
};
9399

94-
return map[name] || name;
100+
let action = map[name] || name;
101+
102+
if (name === 'Wait for Time' && item.TargetTime) {
103+
const target = new Date(item.TargetTime).getTime();
104+
const diff = Math.ceil((target - Date.now()) / 1000);
105+
if (diff > 0) {
106+
action += ` (${diff}s)`;
107+
}
108+
} else if (name === 'Wait for Time Span' && item.Delay) {
109+
action += ` for ${item.Delay}s`;
110+
}
111+
112+
return action;
95113
}
96114

97115
// Helper to actually implement the recursive search properly aligned with logic
98-
private getDeepestRunningName(items: SequenceItem[]): string | undefined {
116+
private getDeepestRunningItem(items: SequenceItem[]): SequenceItem | undefined {
99117
for (const item of items) {
100118
// Check if this item is RUNNING
101119
if (item.Status === 'RUNNING') {
102120
// 1. Check Triggers for this item
103121
if (item.Triggers && Array.isArray(item.Triggers)) {
104122
const runningTrigger = item.Triggers.find((t) => t.Status === 'RUNNING');
105-
if (runningTrigger) return runningTrigger.Name;
123+
if (runningTrigger) return runningTrigger;
106124
}
107125

108126
// 2. Check Children Items
109127
if (item.Items && item.Items.length > 0) {
110-
const deepName = this.getDeepestRunningName(item.Items);
111-
if (deepName) return deepName;
128+
const deepItem = this.getDeepestRunningItem(item.Items);
129+
if (deepItem) return deepItem;
112130
}
113131

114132
// 3. If no children/triggers running, this is the deepest running item
115-
return item.Name;
133+
return item;
116134
}
117135
}
118136
return undefined;
@@ -136,11 +154,11 @@ export class NinaClient {
136154
const sequenceData =
137155
await this.fetch<NinaResponse<SequenceItem[]>>('api/sequence/json');
138156

139-
const deepestName =
157+
const deepestItem =
140158
sequenceData?.Success && sequenceData.Response
141-
? this.getDeepestRunningName(sequenceData.Response)
159+
? this.getDeepestRunningItem(sequenceData.Response)
142160
: undefined;
143-
const isRunning = !!deepestName;
161+
const isRunning = !!deepestItem;
144162

145163
if (!isRunning) {
146164
this.cachedLiveStatus = { active: false };
@@ -177,11 +195,31 @@ export class NinaClient {
177195
this.lastUpdate = now;
178196
}
179197

198+
const currentAction = this.mapRunningAction(deepestItem!);
199+
const hiddenActions = [
200+
'Meridian flip',
201+
'Waiting',
202+
'Cooling Camera',
203+
'Warming Camera'
204+
];
205+
206+
let showImage = !hiddenActions.some((hidden) => currentAction.startsWith(hidden));
207+
208+
// Check if the last image is older than 10 minutes (600,000 ms)
209+
if (imageInfo?.Date && Date.now() - new Date(imageInfo.Date).getTime() > 600000) {
210+
showImage = false;
211+
}
212+
213+
if (!this.cachedLiveImage) {
214+
showImage = false;
215+
}
216+
180217
this.cachedLiveStatus = {
181218
active: true,
182219
mountInfo: mountData?.Response,
183220
imageInfo,
184-
currentAction: this.mapRunningAction(deepestName!)
221+
currentAction,
222+
showImage
185223
};
186224

187225
return this.cachedLiveStatus;
@@ -191,7 +229,11 @@ export class NinaClient {
191229
try {
192230
const res = await fetch(`${this.baseUrl}/v2/api/prepared-image`);
193231
if (!res.ok) return;
194-
this.cachedLiveImage = Buffer.from(await res.arrayBuffer());
232+
const buffer = Buffer.from(await res.arrayBuffer());
233+
234+
this.cachedLiveImage = await sharp(buffer)
235+
.jpeg({ quality: 80, mozjpeg: true })
236+
.toBuffer();
195237
} catch (e) {
196238
if (isNetworkError(e, 'ECONNREFUSED') || isNetworkError(e, 'EHOSTUNREACH')) {
197239
// Host unreachable, likely Nina is offline => don't spam errors

src/routes/[[lang=lang]]/live-photo/+page.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
<div class="mt-8 grid grid-cols-1 gap-6 px-2 lg:grid-cols-3">
4848
<div class="lg:col-span-2">
49-
{#if liveData?.active}
49+
{#if liveData?.active && liveData?.showImage}
5050
<img
5151
src={`/api/live-image?t=${now}`}
5252
alt="Live view"
@@ -56,7 +56,9 @@
5656
<div
5757
class="flex aspect-video w-full items-center justify-center rounded-lg bg-black text-xl text-white shadow-lg"
5858
>
59-
{appState.lang.live_photo.inactive}
59+
{liveData?.active
60+
? liveData?.currentAction || appState.lang.live_photo.inactive
61+
: appState.lang.live_photo.inactive}
6062
</div>
6163
{/if}
6264
</div>

0 commit comments

Comments
 (0)