Apply special formatting to A/FX Sony cameras

This commit is contained in:
Sam Becker 2025-03-20 21:28:58 -05:00
parent a16f992d1a
commit 7ab259a09e
6 changed files with 122 additions and 5 deletions

View File

@ -27,7 +27,9 @@
"headlessui",
"hgetall",
"hset",
"ILCE",
"ILIKE",
"ILME",
"jpgs",
"Lightbox",
"Makernote",

View File

@ -1,4 +1,5 @@
import { Camera, formatCameraText } from '@/camera';
import { MAKE_SONY } from '@/platforms/sony';
const APPLE : Camera = { make: 'Apple', model: 'iPhone 11 Pro' };
const APPLE_01 : Camera = { make: 'Apple', model: 'iPhone 11' };
@ -11,6 +12,44 @@ const RICOH : Camera = {
model: 'RICOH GR III',
};
export const SONY_CAMERAS = {
'SONY ILCE-1M2': 'Sony A1 II',
'SONY ILCE-1': 'Sony A1',
'SONY ILCE-9M3': 'Sony A9 III',
'SONY ILCE-9M2': 'Sony A9 II',
'SONY ILCE-9': 'Sony A9',
'SONY ILCE-7RM5': 'Sony A7R V',
'SONY ILCE-7RM4': 'Sony A7R IV',
'SONY ILCE-7RM3': 'Sony A7R III',
'SONY ILCE-7RM2': 'Sony A7R II',
'SONY ILCE-7R': 'Sony A7R',
'SONY ILCE-7SM3': 'Sony A7S III',
'SONY ILCE-7SM2': 'Sony A7S II',
'SONY ILCE-7S': 'Sony A7S',
'SONY ILCE-7M4': 'Sony A7 IV',
'SONY ILCE-7M3': 'Sony A7 III',
'SONY ILCE-7M2': 'Sony A7 II',
'SONY ILCE-7': 'Sony A7',
'SONY ILCE-7CR': 'Sony A7CR',
'SONY ILCE-7CM2': 'Sony A7C II',
'SONY ILCE-7C': 'Sony A7C',
'SONY ILCE-6700': 'Sony A6700',
'SONY ILCE-6600': 'Sony A6600',
'SONY ILCE-6500': 'Sony A6500',
'SONY ILCE-6400': 'Sony A6400',
'SONY ILCE-6300': 'Sony A6300',
'SONY ILCE-6100': 'Sony A6100',
'SONY ILCE-6000': 'Sony A6000',
'SONY ILCE-5100': 'Sony A5100',
'SONY ILCE-5000': 'Sony A5000',
'SONY ILCE-3500': 'Sony A3500',
'SONY ILCE-3000': 'Sony A3000',
'SONY ILME-FX3': 'Sony FX3',
'SONY ILME-FX6V': 'Sony FX6',
'SONY ILME-FX6VK': 'Sony FX6',
'SONY ILCE-QX1': 'Sony AQX1',
};
describe('Camera', () => {
it('labels full text correctly', () => {
expect(formatCameraText(APPLE)).toBe('iPhone 11 Pro');
@ -33,5 +72,11 @@ describe('Camera', () => {
expect(formatCameraText(RICOH, 'short')).toBe('GR III');
expect(formatCameraText(NIKON, 'short')).toBe('D7000');
});
it('formats Sony cameras', () => {
Object.entries(SONY_CAMERAS).forEach(([model, expected]) => {
const camera = { make: MAKE_SONY, model };
expect(formatCameraText(camera, 'medium'))
.toBe(expected.toLocaleUpperCase());
});
});
});

View File

@ -1,6 +1,22 @@
import { roundToString, roundToNumber } from '@/utility/number';
import {
convertNumberToRomanNumeral,
roundToString,
roundToNumber,
} from '@/utility/number';
describe('number', () => {
it('converts to roman numerals', () => {
expect(convertNumberToRomanNumeral(1)).toBe('I');
expect(convertNumberToRomanNumeral(2)).toBe('II');
expect(convertNumberToRomanNumeral(3)).toBe('III');
expect(convertNumberToRomanNumeral(4)).toBe('IV');
expect(convertNumberToRomanNumeral(5)).toBe('V');
expect(convertNumberToRomanNumeral(6)).toBe('VI');
expect(convertNumberToRomanNumeral(7)).toBe('VII');
expect(convertNumberToRomanNumeral(8)).toBe('VIII');
expect(convertNumberToRomanNumeral(9)).toBe('IX');
expect(convertNumberToRomanNumeral(10)).toBe('X');
});
describe('rounds to a', () => {
it('string', () => {
expect(roundToString(1.2345, 1)).toBe('1.2');

View File

@ -1,5 +1,6 @@
import type { Photo } from '@/photo';
import { isCameraMakeApple } from '@/platforms/apple';
import { formatSonyModel, isMakeSony } from '@/platforms/sony';
import { parameterize } from '@/utility/string';
const CAMERA_PLACEHOLDER: Camera = { make: 'Camera', model: 'Model' };
@ -58,7 +59,7 @@ export const cameraFromPhoto = (
: fallback ?? CAMERA_PLACEHOLDER;
export const formatCameraText = (
{ make, model: modelRaw }: Camera,
{ make, model: _model }: Camera,
length:
'long' | // Unmodified make and model
'medium' | // Make and model, with modifiers removed
@ -67,11 +68,11 @@ export const formatCameraText = (
) => {
// Capture simple make without modifiers like 'Corporation' or 'Company'
const makeSimple = make.match(/^(\S+)/)?.[1];
let model = isMakeSony(make) ? formatSonyModel(_model) : _model;
const doesModelStartWithMake = (
makeSimple &&
modelRaw.toLocaleLowerCase().startsWith(makeSimple.toLocaleLowerCase())
model.toLocaleLowerCase().startsWith(makeSimple.toLocaleLowerCase())
);
let model = modelRaw;
switch (length) {
case 'long':
return `${make} ${model}`;

26
src/platforms/sony.ts Normal file
View File

@ -0,0 +1,26 @@
import { convertNumberToRomanNumeral } from '@/utility/number';
export const MAKE_SONY = 'SONY';
export const isMakeSony = (make: string) =>
make === MAKE_SONY;
export const formatSonyModel = (model: string) => {
const [
_,
type,
series,
letter,
_version,
] = /^SONY (ILCE|ILME)-([0-9]*)([a-ln-z]*)M*([0-9]*)/gi.exec(model) ?? [];
const version = parseInt(_version ?? '0');
const versionRomanNumber = version > 1 && version < 10
? ` ${convertNumberToRomanNumeral(version)}`
: undefined;
if (type === 'ILCE' || type === 'ILME') {
return type === 'ILCE'
? `A${series}${letter}${versionRomanNumber || version || ''}`
: `FX${series}${_version}`;
}
return model;
};

View File

@ -91,3 +91,30 @@ export const formatBytesToMB = (
precision = 1,
) =>
`${(bytes / byteSize / byteSize).toFixed(precision)}MB`;
export const convertNumberToRomanNumeral = (number: number) => {
const romanNumerals = [
{ value: 1000, numeral: 'M' },
{ value: 900, numeral: 'CM' },
{ value: 500, numeral: 'D' },
{ value: 400, numeral: 'CD' },
{ value: 100, numeral: 'C' },
{ value: 90, numeral: 'XC' },
{ value: 50, numeral: 'L' },
{ value: 40, numeral: 'XL' },
{ value: 10, numeral: 'X' },
{ value: 9, numeral: 'IX' },
{ value: 5, numeral: 'V' },
{ value: 4, numeral: 'IV' },
{ value: 1, numeral: 'I' },
];
let result = '';
for (const romanNumeral of romanNumerals) {
while (number >= romanNumeral.value) {
result += romanNumeral.numeral;
number -= romanNumeral.value;
}
}
return result;
};