mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 13:18:19 -04:00
feat(server,web,mobile): Use binary prefixes for data sizes (#1009)
This commit is contained in:
parent
df0a059a02
commit
976d347623
@ -6,6 +6,7 @@ import 'package:immich_mobile/shared/models/asset.dart';
|
|||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:immich_mobile/utils/bytes_units.dart';
|
||||||
|
|
||||||
class ExifBottomSheet extends ConsumerWidget {
|
class ExifBottomSheet extends ConsumerWidget {
|
||||||
final Asset assetDetail;
|
final Asset assetDetail;
|
||||||
@ -162,7 +163,7 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
subtitle: exifInfo.exifImageHeight != null
|
subtitle: exifInfo.exifImageHeight != null
|
||||||
? Text(
|
? Text(
|
||||||
"${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth} ${exifInfo.fileSizeInByte!}B ",
|
"${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth} ${formatBytes(exifInfo.fileSizeInByte!)} ",
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
@ -178,7 +179,7 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"ƒ/${exifInfo.fNumber} 1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)} ${exifInfo.focalLength}mm ISO${exifInfo.iso} ",
|
"ƒ/${exifInfo.fNumber} 1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
|
||||||
String formatBytes(int bytes) {
|
String formatBytes(int bytes) {
|
||||||
if (bytes < 1000) {
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
|
||||||
return "$bytes B";
|
|
||||||
} else if (bytes < 1000000) {
|
int magnitude = 0;
|
||||||
final kb = (bytes / 1000).toStringAsFixed(1);
|
double remainder = bytes.toDouble();
|
||||||
return "$kb kB";
|
while (remainder >= 1024) {
|
||||||
} else if (bytes < 1000000000) {
|
if (magnitude + 1 < units.length) {
|
||||||
final mb = (bytes / 1000000).toStringAsFixed(1);
|
magnitude++;
|
||||||
return "$mb MB";
|
remainder /= 1024;
|
||||||
} else {
|
|
||||||
final gb = (bytes / 1000000000).toStringAsFixed(1);
|
|
||||||
return "$gb GB";
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "${remainder.toStringAsFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}";
|
||||||
}
|
}
|
@ -35,7 +35,7 @@ export class DownloadService {
|
|||||||
fileCount++;
|
fileCount++;
|
||||||
|
|
||||||
// for easier testing, can be changed before merging.
|
// for easier testing, can be changed before merging.
|
||||||
if (totalSize > HumanReadableSize.GB * 20) {
|
if (totalSize > HumanReadableSize.GiB * 20) {
|
||||||
complete = false;
|
complete = false;
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Archive size exceeded after ${fileCount} files, capping at ${totalSize} bytes (${asHumanReadable(
|
`Archive size exceeded after ${fileCount} files, capping at ${totalSize} bytes (${asHumanReadable(
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
const KB = 1000;
|
const KiB = Math.pow(1024, 1);
|
||||||
const MB = KB * 1000;
|
const MiB = Math.pow(1024, 2);
|
||||||
const GB = MB * 1000;
|
const GiB = Math.pow(1024, 3);
|
||||||
const TB = GB * 1000;
|
const TiB = Math.pow(1024, 4);
|
||||||
const PB = TB * 1000;
|
const PiB = Math.pow(1024, 5);
|
||||||
|
|
||||||
export const HumanReadableSize = { KB, MB, GB, TB, PB };
|
export const HumanReadableSize = { KiB, MiB, GiB, TiB, PiB };
|
||||||
|
|
||||||
export function asHumanReadable(bytes: number, precision = 1) {
|
export function asHumanReadable(bytes: number, precision = 1): string {
|
||||||
if (bytes >= PB) {
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
|
||||||
return `${(bytes / PB).toFixed(precision)}PB`;
|
|
||||||
|
let magnitude = 0;
|
||||||
|
let remainder = bytes;
|
||||||
|
while (remainder >= 1024) {
|
||||||
|
if (magnitude + 1 < units.length) {
|
||||||
|
magnitude++;
|
||||||
|
remainder /= 1024;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes >= TB) {
|
return `${remainder.toFixed( magnitude == 0 ? 0 : precision )} ${units[magnitude]}`;
|
||||||
return `${(bytes / TB).toFixed(precision)}TB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes >= GB) {
|
|
||||||
return `${(bytes / GB).toFixed(precision)}GB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes >= MB) {
|
|
||||||
return `${(bytes / MB).toFixed(precision)}MB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes >= KB) {
|
|
||||||
return `${(bytes / KB).toFixed(precision)}KB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${bytes}B`;
|
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
$: spaceUnit = stats.usage.slice(stats.usage.length - 2, stats.usage.length);
|
$: spaceUnit = stats.usage.split(' ')[1];
|
||||||
$: spaceUsage = stats.usage.slice(0, stats.usage.length - 2);
|
$: spaceUsage = stats.usage.split(' ')[0];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { AssetResponseDto, AlbumResponseDto } from '@api';
|
import { AssetResponseDto, AlbumResponseDto } from '@api';
|
||||||
|
import { getHumanReadableBytes } from '../../utils/byte-units';
|
||||||
|
|
||||||
type Leaflet = typeof import('leaflet');
|
type Leaflet = typeof import('leaflet');
|
||||||
type LeafletMap = import('leaflet').Map;
|
type LeafletMap = import('leaflet').Map;
|
||||||
@ -59,32 +60,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const getHumanReadableString = (sizeInByte: number) => {
|
|
||||||
const pepibyte = 1.126 * Math.pow(10, 15);
|
|
||||||
const tebibyte = 1.1 * Math.pow(10, 12);
|
|
||||||
const gibibyte = 1.074 * Math.pow(10, 9);
|
|
||||||
const mebibyte = 1.049 * Math.pow(10, 6);
|
|
||||||
const kibibyte = 1024;
|
|
||||||
// Pebibyte
|
|
||||||
if (sizeInByte >= pepibyte) {
|
|
||||||
// Pe
|
|
||||||
return `${(sizeInByte / pepibyte).toFixed(1)}PB`;
|
|
||||||
} else if (tebibyte <= sizeInByte && sizeInByte < pepibyte) {
|
|
||||||
// Te
|
|
||||||
return `${(sizeInByte / tebibyte).toFixed(1)}TB`;
|
|
||||||
} else if (gibibyte <= sizeInByte && sizeInByte < tebibyte) {
|
|
||||||
// Gi
|
|
||||||
return `${(sizeInByte / gibibyte).toFixed(1)}GB`;
|
|
||||||
} else if (mebibyte <= sizeInByte && sizeInByte < gibibyte) {
|
|
||||||
// Mega
|
|
||||||
return `${(sizeInByte / mebibyte).toFixed(1)}MB`;
|
|
||||||
} else if (kibibyte <= sizeInByte && sizeInByte < mebibyte) {
|
|
||||||
// Kibi
|
|
||||||
return `${(sizeInByte / kibibyte).toFixed(1)}KB`;
|
|
||||||
} else {
|
|
||||||
return `${sizeInByte}B`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMegapixel = (width: number, height: number): number | undefined => {
|
const getMegapixel = (width: number, height: number): number | undefined => {
|
||||||
const megapixel = Math.round((height * width) / 1_000_000);
|
const megapixel = Math.round((height * width) / 1_000_000);
|
||||||
@ -143,13 +118,13 @@
|
|||||||
{#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
|
{#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
|
||||||
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
|
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
|
||||||
<p>
|
<p>
|
||||||
{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}MP
|
{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)} MP
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<p>{asset.exifInfo.exifImageHeight} x {asset.exifInfo.exifImageWidth}</p>
|
<p>{asset.exifInfo.exifImageHeight} x {asset.exifInfo.exifImageWidth}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<p>{getHumanReadableString(asset.exifInfo.fileSizeInByte)}</p>
|
<p>{getHumanReadableBytes(asset.exifInfo.fileSizeInByte)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -162,7 +137,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<p>{asset.exifInfo.make || ''} {asset.exifInfo.model || ''}</p>
|
<p>{asset.exifInfo.make || ''} {asset.exifInfo.model || ''}</p>
|
||||||
<div class="flex text-sm gap-2">
|
<div class="flex text-sm gap-2">
|
||||||
<p>{`f/${asset.exifInfo.fNumber}` || ''}</p>
|
<p>{`ƒ/${asset.exifInfo.fNumber}` || ''}</p>
|
||||||
|
|
||||||
{#if asset.exifInfo.exposureTime}
|
{#if asset.exifInfo.exposureTime}
|
||||||
<p>{`1/${Math.floor(1 / asset.exifInfo.exposureTime)}`}</p>
|
<p>{`1/${Math.floor(1 / asset.exifInfo.exposureTime)}`}</p>
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte';
|
import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte';
|
||||||
import type { UploadAsset } from '$lib/models/upload-asset';
|
import type { UploadAsset } from '$lib/models/upload-asset';
|
||||||
import { notificationController, NotificationType } from './notification/notification';
|
import { notificationController, NotificationType } from './notification/notification';
|
||||||
|
import { getHumanReadableBytes } from '../../utils/byte-units';
|
||||||
|
|
||||||
let showDetail = true;
|
let showDetail = true;
|
||||||
|
|
||||||
let uploadLength = 0;
|
let uploadLength = 0;
|
||||||
@ -30,33 +32,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function getSizeInHumanReadableFormat(sizeInByte: number) {
|
|
||||||
const pepibyte = 1.126 * Math.pow(10, 15);
|
|
||||||
const tebibyte = 1.1 * Math.pow(10, 12);
|
|
||||||
const gibibyte = 1.074 * Math.pow(10, 9);
|
|
||||||
const mebibyte = 1.049 * Math.pow(10, 6);
|
|
||||||
const kibibyte = 1024;
|
|
||||||
// Pebibyte
|
|
||||||
if (sizeInByte >= pepibyte) {
|
|
||||||
// Pe
|
|
||||||
return `${(sizeInByte / pepibyte).toFixed(1)}PB`;
|
|
||||||
} else if (tebibyte <= sizeInByte && sizeInByte < pepibyte) {
|
|
||||||
// Te
|
|
||||||
return `${(sizeInByte / tebibyte).toFixed(1)}TB`;
|
|
||||||
} else if (gibibyte <= sizeInByte && sizeInByte < tebibyte) {
|
|
||||||
// Gi
|
|
||||||
return `${(sizeInByte / gibibyte).toFixed(1)}GB`;
|
|
||||||
} else if (mebibyte <= sizeInByte && sizeInByte < gibibyte) {
|
|
||||||
// Mega
|
|
||||||
return `${(sizeInByte / mebibyte).toFixed(1)}MB`;
|
|
||||||
} else if (kibibyte <= sizeInByte && sizeInByte < mebibyte) {
|
|
||||||
// Kibi
|
|
||||||
return `${(sizeInByte / kibibyte).toFixed(1)}KB`;
|
|
||||||
} else {
|
|
||||||
return `${sizeInByte}B`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reactive action to get thumbnail image of upload asset whenever there is a new one added to the list
|
// Reactive action to get thumbnail image of upload asset whenever there is a new one added to the list
|
||||||
$: {
|
$: {
|
||||||
if ($uploadAssetsStore.length != uploadLength) {
|
if ($uploadAssetsStore.length != uploadLength) {
|
||||||
@ -140,7 +115,7 @@
|
|||||||
<input
|
<input
|
||||||
disabled
|
disabled
|
||||||
class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2"
|
class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2"
|
||||||
value={`[${getSizeInHumanReadableFormat(uploadAsset.file.size)}] ${
|
value={`[${getHumanReadableBytes(uploadAsset.file.size)}] ${
|
||||||
uploadAsset.file.name
|
uploadAsset.file.name
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
16
web/src/lib/utils/byte-units.ts
Normal file
16
web/src/lib/utils/byte-units.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export function getHumanReadableBytes(bytes: number): string {
|
||||||
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
|
||||||
|
|
||||||
|
let magnitude = 0;
|
||||||
|
let remainder = bytes;
|
||||||
|
while (remainder >= 1024) {
|
||||||
|
if (magnitude + 1 < units.length) {
|
||||||
|
magnitude++;
|
||||||
|
remainder /= 1024;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${remainder.toFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}`;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user