Merge origin/dev into feat/widget-only-integration-refactor
Resolve integration.ts conflict (keep widget-only documentationUrl: null, take defaultUrl from dev) and regenerate pnpm-lock.yaml.
This commit is contained in:
84 files changed
+4379
-2098
No files matched your search
@@ -35,7 +35,7 @@ export class GlancesIntegration extends Integration implements ISystemHealthMoni
|
||||
|
||||
if (session == null) throw new Error("Session was unexpectitly null");
|
||||
|
||||
const stats = await this.getAllStatsAsync();
|
||||
const [stats, cpuTemp] = await Promise.all([this.getAllStatsAsync(), this.getCpuTempAsync()]);
|
||||
|
||||
return {
|
||||
cpuUtilization: stats.cpu.total,
|
||||
@@ -58,7 +58,7 @@ export class GlancesIntegration extends Integration implements ISystemHealthMoni
|
||||
cpuModelName: stats.quicklook?.cpu_name ?? "Unknown",
|
||||
loadAverage: null,
|
||||
smart: [],
|
||||
cpuTemp: undefined,
|
||||
cpuTemp,
|
||||
gpu: stats.gpu.map((gpu) => ({
|
||||
gpuId: gpu.gpu_id,
|
||||
name: gpu.name,
|
||||
@@ -80,6 +80,23 @@ export class GlancesIntegration extends Integration implements ISystemHealthMoni
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
private async getCpuTempAsync(): Promise<number | undefined> {
|
||||
// CPU temperature is optional data, so a transport error or payload drift on
|
||||
// /api/4/sensors must not fail the whole getSystemInfoAsync() call.
|
||||
try {
|
||||
const response = await fetchWithTrustedCertificatesAsync(this.url("/api/4/sensors"));
|
||||
|
||||
if (!response.ok) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sensors = await sensorsSchema.parseAsync(await response.json());
|
||||
return parseGlancesCpuTempFromSensors(sensors);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async getAllStatsAsync() {
|
||||
const response = await fetchWithTrustedCertificatesAsync(this.url("/api/4/all"));
|
||||
|
||||
@@ -173,3 +190,33 @@ const allSchema = z.object({
|
||||
)
|
||||
.default([]),
|
||||
});
|
||||
|
||||
const sensorsSchema = z.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
value: z.number(),
|
||||
type: z.string().optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
const cpuTempLabelPriority = ["CPU", "Package id 0"] as const;
|
||||
|
||||
export const parseGlancesCpuTempFromSensors = (sensors: z.infer<typeof sensorsSchema>): number | undefined => {
|
||||
const temperatureSensors = sensors.filter(
|
||||
(sensor) => sensor.type === "temperature_core" || sensor.type === "temperature",
|
||||
);
|
||||
|
||||
for (const label of cpuTempLabelPriority) {
|
||||
const match = temperatureSensors.find((sensor) => sensor.label === label);
|
||||
if (match) {
|
||||
return match.value;
|
||||
}
|
||||
}
|
||||
|
||||
const firstCore = temperatureSensors[0];
|
||||
if (firstCore) {
|
||||
return firstCore.value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
@@ -69,36 +69,24 @@ export class LidarrIntegration extends Integration implements ICalendarIntegrati
|
||||
private getLinksForLidarrCalendarEvent = (event: z.infer<typeof lidarrCalendarEventSchema>) => {
|
||||
const links: CalendarLink[] = [];
|
||||
|
||||
// titleSlug is only present when Lidarr returns it; guard so a missing value
|
||||
// neither breaks the link nor drops the event.
|
||||
if (event.artist.titleSlug) {
|
||||
links.push({
|
||||
href: this.externalUrl(`/artist/${event.artist.titleSlug}`).toString(),
|
||||
name: "Lidarr",
|
||||
logo: "/images/apps/lidarr.svg",
|
||||
color: undefined,
|
||||
isDark: true,
|
||||
});
|
||||
}
|
||||
|
||||
for (const link of event.artist.links) {
|
||||
switch (link.name) {
|
||||
case "vgmdb":
|
||||
links.push({
|
||||
href: link.url,
|
||||
name: "VgmDB",
|
||||
color: "#f5c518",
|
||||
isDark: false,
|
||||
logo: "/images/apps/vgmdb.svg",
|
||||
});
|
||||
break;
|
||||
case "imdb":
|
||||
links.push({
|
||||
href: link.url,
|
||||
name: "IMDb",
|
||||
color: "#f5c518",
|
||||
isDark: false,
|
||||
logo: "/images/apps/imdb.png",
|
||||
});
|
||||
break;
|
||||
case "last":
|
||||
links.push({
|
||||
href: link.url,
|
||||
name: "LastFM",
|
||||
color: "#cf222a",
|
||||
isDark: false,
|
||||
logo: "/images/apps/lastfm.svg",
|
||||
});
|
||||
break;
|
||||
const externalLink = lidarrExternalCalendarLinks[link.name];
|
||||
if (!externalLink) {
|
||||
continue;
|
||||
}
|
||||
links.push({ ...externalLink, href: link.url });
|
||||
}
|
||||
|
||||
return links;
|
||||
@@ -109,7 +97,7 @@ export class LidarrIntegration extends Integration implements ICalendarIntegrati
|
||||
): z.infer<typeof lidarrCalendarEventSchema>["images"][number] | undefined => {
|
||||
const flatImages = [...event.images];
|
||||
|
||||
const sortedImages = flatImages.sort(
|
||||
const sortedImages = flatImages.toSorted(
|
||||
(imageA, imageB) =>
|
||||
mediaOrganizerPriorities.indexOf(imageA.coverType) - mediaOrganizerPriorities.indexOf(imageB.coverType),
|
||||
);
|
||||
@@ -149,6 +137,16 @@ const lidarrCalendarEventSchema = z.object({
|
||||
title: z.string(),
|
||||
overview: z.string().optional(),
|
||||
images: lidarrCalendarEventImageSchema,
|
||||
artist: z.object({ links: z.array(z.object({ url: z.string().url(), name: z.string() })), artistName: z.string() }),
|
||||
artist: z.object({
|
||||
links: z.array(z.object({ url: z.string().url(), name: z.string() })),
|
||||
artistName: z.string(),
|
||||
titleSlug: z.string().optional(),
|
||||
}),
|
||||
releaseDate: z.string().transform((value) => new Date(value)),
|
||||
});
|
||||
|
||||
const lidarrExternalCalendarLinks: Record<string, Omit<CalendarLink, "href">> = {
|
||||
vgmdb: { name: "VgmDB", color: "#f5c518", isDark: false, logo: "/images/apps/vgmdb.svg" },
|
||||
imdb: { name: "IMDb", color: "#f5c518", isDark: false, logo: "/images/apps/imdb.png" },
|
||||
last: { name: "LastFM", color: "#cf222a", isDark: false, logo: "/images/apps/lastfm.svg" },
|
||||
};
|
||||
Reference in new issue
Block a user