mirror of
https://github.com/WonderAliceUwU/OmniArr.git
synced 2026-11-11 04:01:01 +00:00
fix: adjust free space
This commit is contained in:
1 parent
5013d16b35
commit
7bd8b0600f
6 files changed
+47
-34
No files matched your search
@@ -13,8 +13,6 @@ services:
|
||||
environment:
|
||||
PORT: "8787"
|
||||
OMNIARR_DATA_DIR: /app/data
|
||||
OMNIARR_STORAGE_PATH: /media
|
||||
TMDB_BEARER_TOKEN: ""
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./media:/media:ro
|
||||
+3
-23
@@ -1,5 +1,5 @@
|
||||
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
|
||||
import { readFile, statfs } from 'node:fs/promises';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
import {
|
||||
defaultConfig,
|
||||
@@ -49,8 +49,6 @@ const SESSION_COOKIE = 'omniarr_session';
|
||||
const SESSION_TTL_MS = 1000 * 60 * 60 * 12;
|
||||
const sessions = new Map<string, SessionRecord>();
|
||||
const failedAttempts = new Map<string, { count: number; lastAttempt: number }>();
|
||||
const DATA_DIR = resolve(process.env.OMNIARR_DATA_DIR || resolve(process.cwd(), 'data'));
|
||||
const STORAGE_DIR = resolve(process.env.OMNIARR_STORAGE_PATH || DATA_DIR);
|
||||
|
||||
const server = createServer(async (request, response) => {
|
||||
try {
|
||||
@@ -412,26 +410,8 @@ function createSession(encryptionKey: Buffer): string {
|
||||
return token;
|
||||
}
|
||||
|
||||
async function getDiskSpaceStatus(config: AppConfig): Promise<DiskSpaceStatus> {
|
||||
if (process.env.OMNIARR_STORAGE_PATH) {
|
||||
return getFilesystemDiskSpace(STORAGE_DIR);
|
||||
}
|
||||
|
||||
const mediaDiskSpace = await getMediaDiskSpace(config);
|
||||
if (mediaDiskSpace) {
|
||||
return mediaDiskSpace;
|
||||
}
|
||||
|
||||
return getFilesystemDiskSpace(DATA_DIR);
|
||||
}
|
||||
|
||||
async function getFilesystemDiskSpace(path: string): Promise<DiskSpaceStatus> {
|
||||
const stats = await statfs(path);
|
||||
return {
|
||||
totalBytes: Number(stats.bsize) * Number(stats.blocks),
|
||||
availableBytes: Number(stats.bsize) * Number(stats.bavail),
|
||||
path
|
||||
};
|
||||
async function getDiskSpaceStatus(config: AppConfig): Promise<DiskSpaceStatus | null> {
|
||||
return getMediaDiskSpace(config);
|
||||
}
|
||||
|
||||
function getSession(request: IncomingMessage): SessionRecord | null {
|
||||
|
||||
+40
-7
@@ -443,8 +443,8 @@ export async function getServiceOptions(config: AppConfig): Promise<ServiceOptio
|
||||
|
||||
export async function getMediaDiskSpace(config: AppConfig): Promise<DiskSpaceStatus | null> {
|
||||
const services = [
|
||||
{ baseUrl: config.radarr.baseUrl, apiKey: config.radarr.apiKey, rootFolderPath: config.radarr.rootFolderPath },
|
||||
{ baseUrl: config.sonarr.baseUrl, apiKey: config.sonarr.apiKey, rootFolderPath: config.sonarr.rootFolderPath }
|
||||
{ source: 'radarr' as const, baseUrl: config.radarr.baseUrl, apiKey: config.radarr.apiKey, rootFolderPath: config.radarr.rootFolderPath },
|
||||
{ source: 'sonarr' as const, baseUrl: config.sonarr.baseUrl, apiKey: config.sonarr.apiKey, rootFolderPath: config.sonarr.rootFolderPath }
|
||||
];
|
||||
|
||||
for (const service of services) {
|
||||
@@ -453,18 +453,25 @@ export async function getMediaDiskSpace(config: AppConfig): Promise<DiskSpaceSta
|
||||
}
|
||||
|
||||
try {
|
||||
const folders = await fetchJson<Record<string, unknown>[]>(`${service.baseUrl}/api/v3/rootFolder`, {
|
||||
const disks = await fetchJson<Record<string, unknown>[]>(`${service.baseUrl}/api/v3/diskspace`, {
|
||||
headers: { 'X-Api-Key': service.apiKey }
|
||||
});
|
||||
const folder = folders.find((entry) => String(entry.path || '') === service.rootFolderPath) || folders[0];
|
||||
const totalBytes = Number(folder?.totalSpace);
|
||||
const availableBytes = Number(folder?.freeSpace);
|
||||
const disk = selectDiskSpaceForRootFolder(disks, service.rootFolderPath);
|
||||
if (!disk) {
|
||||
console.warn(
|
||||
`[Storage] ${service.source} did not report disk space for ${service.rootFolderPath}. Reported paths: ${disks.map((entry) => String(entry.path || '')).join(', ') || 'none'}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const totalBytes = Number(disk?.totalSpace);
|
||||
const availableBytes = Number(disk?.freeSpace);
|
||||
|
||||
if (Number.isFinite(totalBytes) && totalBytes > 0 && Number.isFinite(availableBytes) && availableBytes >= 0) {
|
||||
return {
|
||||
totalBytes,
|
||||
availableBytes,
|
||||
path: String(folder?.path || service.rootFolderPath)
|
||||
path: String(disk?.path || service.rootFolderPath),
|
||||
source: service.source
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
@@ -475,6 +482,32 @@ export async function getMediaDiskSpace(config: AppConfig): Promise<DiskSpaceSta
|
||||
return null;
|
||||
}
|
||||
|
||||
function selectDiskSpaceForRootFolder(
|
||||
disks: Record<string, unknown>[],
|
||||
rootFolderPath: string
|
||||
): Record<string, unknown> | undefined {
|
||||
const normalizedRoot = rootFolderPath.replace(/\/+$/, '');
|
||||
const matchingDisks = disks
|
||||
.filter((disk) => {
|
||||
const diskPath = String(disk.path || '').replace(/\/+$/, '');
|
||||
return diskPath && (normalizedRoot === diskPath || normalizedRoot.startsWith(`${diskPath}/`));
|
||||
})
|
||||
.sort((left, right) => String(right.path || '').length - String(left.path || '').length);
|
||||
|
||||
if (matchingDisks[0]) {
|
||||
return matchingDisks[0];
|
||||
}
|
||||
|
||||
if (!normalizedRoot) {
|
||||
return disks.find((disk) => {
|
||||
const path = String(disk.path || '').replace(/\/+$/, '');
|
||||
return path && path !== '/config';
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function loadJellyfinUsers(baseUrl: string, apiKey: string): Promise<SelectOption[]> {
|
||||
if (!baseUrl.trim() || !apiKey.trim()) {
|
||||
return [];
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface DiskSpaceStatus {
|
||||
totalBytes: number;
|
||||
availableBytes: number;
|
||||
path?: string;
|
||||
source: 'radarr' | 'sonarr';
|
||||
}
|
||||
|
||||
export interface MediaCard {
|
||||
|
||||
+2
-1
@@ -679,7 +679,8 @@ function formatBytes(bytes: number): string {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
const unitIndex = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1)
|
||||
const value = bytes / (1024 ** unitIndex)
|
||||
return `${value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`
|
||||
const formattedValue = unitIndex === 0 || Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)
|
||||
return `${formattedValue} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
function LoadingScreen() {
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ export const api = {
|
||||
body: JSON.stringify(payload),
|
||||
}),
|
||||
getDashboard: () => request<DashboardResponse>('/api/dashboard'),
|
||||
getDiskSpace: () => request<DiskSpaceStatus>('/api/storage'),
|
||||
getDiskSpace: () => request<DiskSpaceStatus | null>('/api/storage'),
|
||||
getCalendar: (start: Date, end: Date) => request<CalendarResponse>(`/api/calendar?start=${start.toISOString()}&end=${end.toISOString()}`),
|
||||
getHub: (page: HubPageResponse['page']) => request<HubPageResponse>(`/api/hubs/${page}`),
|
||||
searchTitles: (query: string) => request<SearchResponse>(`/api/search?query=${encodeURIComponent(query)}`),
|
||||
|
||||
Reference in new issue
Block a user