chore: refactor for device support

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-10-21 15:55:00 +03:00
parent 7e8599fa61
commit 4532fb1775
11 changed files with 754 additions and 663 deletions

View File

@@ -24,7 +24,7 @@ fn handle_export(device_id: Option<&str>, output: &Output) -> anyhow::Result<()>
let mut camera = usb::get_camera(device_id)?;
let mut writer = output.get_writer()?;
let backup = camera.export_backup()?;
let backup = camera.backup_export()?;
writer.write_all(&backup)?;
Ok(())
@@ -36,7 +36,7 @@ fn handle_import(device_id: Option<&str>, input: &Input) -> anyhow::Result<()> {
let mut reader = input.get_reader()?;
let mut backup = Vec::new();
reader.read_to_end(&mut backup)?;
camera.import_backup(&backup)?;
camera.backup_import(&backup)?;
Ok(())
}

View File

@@ -1,12 +1,6 @@
use std::fmt;
use clap::Subcommand;
use serde::Serialize;
use crate::{
camera::{Camera, ptp::hex::UsbMode},
usb,
};
use crate::{camera::devices::CameraInfoListItem, usb};
#[derive(Subcommand, Debug, Clone, Copy)]
pub enum DeviceCmd {
@@ -19,38 +13,8 @@ pub enum DeviceCmd {
Info,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CameraItemRepr {
pub name: &'static str,
pub usb_id: String,
pub vendor_id: String,
pub product_id: String,
}
impl From<&Camera> for CameraItemRepr {
fn from(camera: &Camera) -> Self {
Self {
name: camera.name(),
usb_id: camera.connected_usb_id(),
vendor_id: format!("0x{:04x}", camera.vendor_id()),
product_id: format!("0x{:04x}", camera.product_id()),
}
}
}
impl fmt::Display for CameraItemRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} ({}:{}) (USB ID: {})",
self.name, self.vendor_id, self.product_id, self.usb_id
)
}
}
fn handle_list(json: bool) -> anyhow::Result<()> {
let cameras: Vec<CameraItemRepr> = usb::get_connected_cameras()?
let cameras: Vec<CameraInfoListItem> = usb::get_connected_cameras()?
.iter()
.map(std::convert::Into::into)
.collect();
@@ -65,7 +29,6 @@ fn handle_list(json: bool) -> anyhow::Result<()> {
return Ok(());
}
println!("Connected Cameras:");
for d in cameras {
println!("- {d}");
}
@@ -73,54 +36,10 @@ fn handle_list(json: bool) -> anyhow::Result<()> {
Ok(())
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CameraRepr {
#[serde(flatten)]
pub device: CameraItemRepr,
pub manufacturer: String,
pub model: String,
pub device_version: String,
pub serial_number: String,
pub mode: UsbMode,
pub battery: u32,
}
impl fmt::Display for CameraRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Name: {}", self.device.name)?;
writeln!(f, "USB ID: {}", self.device.usb_id)?;
writeln!(
f,
"Vendor ID: {}, Product ID: {}",
self.device.vendor_id, self.device.product_id
)?;
writeln!(f, "Manufacturer: {}", self.manufacturer)?;
writeln!(f, "Model: {}", self.model)?;
writeln!(f, "Version: {}", self.device_version)?;
writeln!(f, "Serial Number: {}", self.serial_number)?;
writeln!(f, "Mode: {}", self.mode)?;
write!(f, "Battery: {}%", self.battery)
}
}
fn handle_info(json: bool, device_id: Option<&str>) -> anyhow::Result<()> {
let mut camera = usb::get_camera(device_id)?;
let info = camera.get_info()?;
let mode = camera.get_usb_mode()?;
let battery = camera.get_battery_info()?;
let repr = CameraRepr {
device: (&camera).into(),
manufacturer: info.manufacturer.clone(),
model: info.model.clone(),
device_version: info.device_version.clone(),
serial_number: info.serial_number,
mode,
battery,
};
let repr = camera.info_get()?;
if json {
println!("{}", serde_json::to_string_pretty(&repr)?);

View File

@@ -1,6 +1,5 @@
mod common;
pub mod backup;
pub mod common;
pub mod device;
pub mod render;
pub mod simulation;

View File

@@ -1,14 +1,5 @@
use std::fmt;
use crate::{
camera::ptp::hex::{
FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiColorSpace,
FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority,
FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality,
FujiImageSize, FujiLensModulationOptimizer, FujiMonochromaticColorTemperature,
FujiMonochromaticColorTint, FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect,
FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature,
},
camera::ptp::hex::{FujiCustomSetting, FujiCustomSettingName},
usb,
};
@@ -17,9 +8,6 @@ use super::common::{
film::FilmSimulationOptions,
};
use clap::{Args, Subcommand};
use log::warn;
use serde::Serialize;
use strum::IntoEnumIterator;
#[derive(Subcommand, Debug)]
pub enum SimulationCmd {
@@ -72,173 +60,27 @@ pub enum SimulationCmd {
pub struct SetFilmSimulationOptions {
/// The name of the slot
#[clap(long)]
name: Option<FujiCustomSettingName>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomSettingRepr {
pub slot: FujiCustomSetting,
pub name: FujiCustomSettingName,
pub name: Option<FujiCustomSettingName>,
}
fn handle_list(json: bool, device_id: Option<&str>) -> anyhow::Result<()> {
let mut camera = usb::get_camera(device_id)?;
let mut slots = Vec::new();
for slot in FujiCustomSetting::iter() {
camera.set_active_custom_setting(&slot)?;
let name = camera.get_custom_setting_name()?;
slots.push(CustomSettingRepr { slot, name });
}
let slots = camera.simulation_list()?;
if json {
println!("{}", serde_json::to_string_pretty(&slots)?);
} else {
println!("Film Simulations:");
for slot in slots {
println!("- {}: {}", slot.slot, slot.name);
for repr in slots {
println!("- {repr}");
}
}
Ok(())
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FilmSimulationRepr {
pub name: FujiCustomSettingName,
pub size: FujiImageSize,
pub quality: FujiImageQuality,
pub simulation: FujiFilmSimulation,
pub monochromatic_color_temperature: FujiMonochromaticColorTemperature,
pub monochromatic_color_tint: FujiMonochromaticColorTint,
pub highlight: FujiHighlightTone,
pub shadow: FujiShadowTone,
pub color: FujiColor,
pub sharpness: FujiSharpness,
pub clarity: FujiClarity,
pub noise_reduction: FujiHighISONR,
pub grain: FujiGrainEffect,
pub color_chrome_effect: FujiColorChromeEffect,
pub color_chrome_fx_blue: FujiColorChromeFXBlue,
pub smooth_skin_effect: FujiSmoothSkinEffect,
pub white_balance: FujiWhiteBalance,
pub white_balance_shift_red: FujiWhiteBalanceShift,
pub white_balance_shift_blue: FujiWhiteBalanceShift,
pub white_balance_temperature: FujiWhiteBalanceTemperature,
pub dynamic_range: FujiDynamicRange,
pub dynamic_range_priority: FujiDynamicRangePriority,
pub lens_modulation_optimizer: FujiLensModulationOptimizer,
pub color_space: FujiColorSpace,
}
impl fmt::Display for FilmSimulationRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Name: {}", self.name)?;
writeln!(f, "Size: {}", self.size)?;
writeln!(f, "Quality: {}", self.quality)?;
writeln!(f, "Simulation: {}", self.simulation)?;
match self.simulation {
FujiFilmSimulation::Monochrome
| FujiFilmSimulation::MonochromeYe
| FujiFilmSimulation::MonochromeR
| FujiFilmSimulation::MonochromeG
| FujiFilmSimulation::AcrosSTD
| FujiFilmSimulation::AcrosYe
| FujiFilmSimulation::AcrosR
| FujiFilmSimulation::AcrosG => {
writeln!(
f,
"Monochromatic Color Temperature: {}",
self.monochromatic_color_temperature
)?;
writeln!(
f,
"Monochromatic Color Tint: {}",
self.monochromatic_color_tint
)?;
}
_ => {}
}
if self.dynamic_range_priority == FujiDynamicRangePriority::Off {
writeln!(f, "Highlights: {}", self.highlight)?;
writeln!(f, "Shadows: {}", self.shadow)?;
}
writeln!(f, "Color: {}", self.color)?;
writeln!(f, "Sharpness: {}", self.sharpness)?;
writeln!(f, "Clarity: {}", self.clarity)?;
writeln!(f, "Noise Reduction: {}", self.noise_reduction)?;
writeln!(f, "Grain: {}", self.grain)?;
writeln!(f, "Color Chrome Effect: {}", self.color_chrome_effect)?;
writeln!(f, "Color Chrome FX Blue: {}", self.color_chrome_fx_blue)?;
writeln!(f, "Smooth Skin Effect: {}", self.smooth_skin_effect)?;
writeln!(f, "White Balance: {}", self.white_balance)?;
writeln!(
f,
"White Balance Shift (R/B): {} / {}",
self.white_balance_shift_red, self.white_balance_shift_blue
)?;
if self.white_balance == FujiWhiteBalance::Temperature {
writeln!(
f,
"White Balance Temperature: {}K",
self.white_balance_temperature
)?;
}
if self.dynamic_range_priority == FujiDynamicRangePriority::Off {
writeln!(f, "Dynamic Range: {}", self.dynamic_range)?;
}
writeln!(f, "Dynamic Range Priority: {}", self.dynamic_range_priority)?;
writeln!(
f,
"Lens Modulation Optimizer: {}",
self.lens_modulation_optimizer
)?;
writeln!(f, "Color Space: {}", self.color_space)
}
}
fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> anyhow::Result<()> {
let mut camera = usb::get_camera(device_id)?;
camera.set_active_custom_setting(&slot)?;
let repr = FilmSimulationRepr {
name: camera.get_custom_setting_name()?,
size: camera.get_image_size()?,
quality: camera.get_image_quality()?,
simulation: camera.get_film_simulation()?,
monochromatic_color_temperature: camera.get_monochromatic_color_temperature()?,
monochromatic_color_tint: camera.get_monochromatic_color_tint()?,
highlight: camera.get_highlight_tone()?,
shadow: camera.get_shadow_tone()?,
color: camera.get_color()?,
sharpness: camera.get_sharpness()?,
clarity: camera.get_clarity()?,
noise_reduction: camera.get_high_iso_nr()?,
grain: camera.get_grain_effect()?,
color_chrome_effect: camera.get_color_chrome_effect()?,
color_chrome_fx_blue: camera.get_color_chrome_fx_blue()?,
smooth_skin_effect: camera.get_smooth_skin_effect()?,
white_balance: camera.get_white_balance()?,
white_balance_shift_red: camera.get_white_balance_shift_red()?,
white_balance_shift_blue: camera.get_white_balance_shift_blue()?,
white_balance_temperature: camera.get_white_balance_temperature()?,
dynamic_range: camera.get_dynamic_range()?,
dynamic_range_priority: camera.get_dynamic_range_priority()?,
lens_modulation_optimizer: camera.get_lens_modulation_optimizer()?,
color_space: camera.get_color_space()?,
};
let repr = camera.simulation_get(slot)?;
if json {
println!("{}", serde_json::to_string_pretty(&repr)?);
@@ -258,176 +100,7 @@ fn handle_set(
options: &FilmSimulationOptions,
) -> anyhow::Result<()> {
let mut camera = usb::get_camera(device_id)?;
camera.set_active_custom_setting(&slot)?;
// General
if let Some(name) = &set_options.name {
camera.set_custom_setting_name(name)?;
}
if let Some(size) = &options.size {
camera.set_image_size(size)?;
}
if let Some(quality) = &options.quality {
camera.set_image_quality(quality)?;
}
// Style
if let Some(simulation) = &options.simulation {
camera.set_film_simulation(simulation)?;
}
if options.monochromatic_color_temperature.is_some()
|| options.monochromatic_color_tint.is_some()
{
let simulation = if let Some(simulation) = &options.simulation {
simulation
} else {
&camera.get_film_simulation()?
};
let is_bnw = matches!(
*simulation,
FujiFilmSimulation::Monochrome
| FujiFilmSimulation::MonochromeYe
| FujiFilmSimulation::MonochromeR
| FujiFilmSimulation::MonochromeG
| FujiFilmSimulation::AcrosSTD
| FujiFilmSimulation::AcrosYe
| FujiFilmSimulation::AcrosR
| FujiFilmSimulation::AcrosG
);
if let Some(monochromatic_color_temperature) = &options.monochromatic_color_temperature {
if is_bnw {
camera.set_monochromatic_color_temperature(monochromatic_color_temperature)?;
} else {
warn!(
"A B&W film simulation is not selected, refusing to set monochromatic color temperature"
);
}
}
if let Some(monochromatic_color_tint) = &options.monochromatic_color_tint {
if is_bnw {
camera.set_monochromatic_color_tint(monochromatic_color_tint)?;
} else {
warn!(
"A B&W film simulation is not selected, refusing to set monochromatic color tint"
);
}
}
}
if let Some(color) = &options.color {
camera.set_color(color)?;
}
if let Some(sharpness) = &options.sharpness {
camera.set_sharpness(sharpness)?;
}
if let Some(clarity) = &options.clarity {
camera.set_clarity(clarity)?;
}
if let Some(noise_reduction) = &options.noise_reduction {
camera.set_high_iso_nr(noise_reduction)?;
}
if let Some(grain) = &options.grain {
camera.set_grain_effect(grain)?;
}
if let Some(color_chrome_effect) = &options.color_chrome_effect {
camera.set_color_chrome_effect(color_chrome_effect)?;
}
if let Some(color_chrome_fx_blue) = &options.color_chrome_fx_blue {
camera.set_color_chrome_fx_blue(color_chrome_fx_blue)?;
}
if let Some(smooth_skin_effect) = &options.smooth_skin_effect {
camera.set_smooth_skin_effect(smooth_skin_effect)?;
}
// White Balance
if let Some(white_balance) = &options.white_balance {
camera.set_white_balance(white_balance)?;
}
if let Some(temperature) = &options.white_balance_temperature {
let white_balance = if let Some(white_balance) = &options.white_balance {
white_balance
} else {
&camera.get_white_balance()?
};
if *white_balance == FujiWhiteBalance::Temperature {
camera.set_white_balance_temperature(temperature)?;
} else {
warn!("White Balance mode is not set to 'Temperature', refusing to set temperature");
}
}
if let Some(shift_red) = &options.white_balance_shift_red {
camera.set_white_balance_shift_red(shift_red)?;
}
if let Some(shift_blue) = &options.white_balance_shift_blue {
camera.set_white_balance_shift_blue(shift_blue)?;
}
// Exposure
if let Some(dynamic_range_priority) = &options.dynamic_range_priority {
camera.set_dynamic_range_priority(dynamic_range_priority)?;
}
if options.dynamic_range.is_some() || options.highlight.is_some() || options.shadow.is_some() {
let dynamic_range_priority =
if let Some(dynamic_range_priority) = &options.dynamic_range_priority {
dynamic_range_priority
} else {
&camera.get_dynamic_range_priority()?
};
let is_drp_off = *dynamic_range_priority == FujiDynamicRangePriority::Off;
if let Some(dynamic_range) = &options.dynamic_range {
if is_drp_off {
camera.set_dynamic_range(dynamic_range)?;
} else {
warn!("Dynamic Range Priority is enabled, refusing to set dynamic range");
}
}
if let Some(highlights) = &options.highlight {
if is_drp_off {
camera.set_highlight_tone(highlights)?;
} else {
warn!("Dynamic Range Priority is enabled, refusing to set highlight tone");
}
}
if let Some(shadows) = &options.shadow {
if is_drp_off {
camera.set_shadow_tone(shadows)?;
} else {
warn!("Dynamic Range Priority is enabled, refusing to set shadow tone");
}
}
}
// Extras
if let Some(lens_modulation_optimizer) = &options.lens_modulation_optimizer {
camera.set_lens_modulation_optimizer(lens_modulation_optimizer)?;
}
if let Some(color_space) = &options.color_space {
camera.set_color_space(color_space)?;
}
camera.simulation_set(slot, set_options, options)?;
Ok(())
}