Skip to content

Commit

Permalink
feat: multi-threading
Browse files Browse the repository at this point in the history
doc: update documentation
chore: bump to 0.1.3
  • Loading branch information
LynMoe committed Nov 11, 2023
1 parent 85b47e1 commit 179bb85
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 122 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
.vscode
/tmp
nya-exif.log
24 changes: 1 addition & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nya-exif"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
authors = ["Lyn <[email protected]>"]
description = "A cross-platform tool for embedding GPS data into photographs"
Expand All @@ -11,7 +11,6 @@ readme = "README.md"
chrono = "0.4.31"
clap = { version = "4.4.7", features = ["derive"] }
csv = "1.3.0"
ctrlc = "3.4.1"
dirs = "5.0.1"
indicatif = "0.17.7"
os_info = "3.7.0"
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [x] 全平台支持
- [x] 支持国策局 GCJ-02 和 WGS-84 坐标系 (解决国内坐标漂移问题)
- [x] 自动检测国内外位置, 自动转换为对应坐标系
- [x] 支持多线程处理

## DEMO

Expand All @@ -22,7 +23,9 @@
2023-11-11 12:46:08.337698000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4100.JPG] Location updated, lat: 34.7737885, lon: 131.9007701
2023-11-11 12:46:08.394225000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4114.JPG] Location updated, lat: 34.67844170666667, lon: 131.83647663733333
2023-11-11 12:46:08.434180000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4128.JPG] Location updated, lat: 34.68192337279844, lon: 131.8327970596869
⠂ [00:00:04] [###########################>-----------------------------------------------] 93/233 (6.7s)
⠉ [00:00:03] [###########################>-----------------------------------------------] 54/77 (1.8s)
⠉ [00:00:03] [###########################>-----------------------------------------------] 55/77 (1.6s)
⠉ [00:00:03] [#############################>---------------------------------------------] 60/79 (1.4s)
```
## 使用
Expand Down Expand Up @@ -71,6 +74,13 @@ Options:
-r, --recursive
Turn on recursive mode

-x, --threads <THREADS>
Threads

Number of threads to use.

[default: 3]

-w, --writer-type <WRITER_TYPE>
Exif writer type

Expand Down
15 changes: 13 additions & 2 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- [x] Supports JPEG and PNG as well as mainstream RAW formats from major camera manufacturers
- [x] Multi-platform support
- [x] Supports GCJ-02 and WGS-84 coordinate systems (defaults to GCJ-02)
- [x] Automatically detects location data and converts it to the corresponding coordinate system
- [x] Multi-threaded processing

## DEMO

Expand All @@ -21,7 +23,9 @@
2023-11-11 12:46:08.337698000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4100.JPG] Location updated, lat: 34.7737885, lon: 131.9007701
2023-11-11 12:46:08.394225000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4114.JPG] Location updated, lat: 34.67844170666667, lon: 131.83647663733333
2023-11-11 12:46:08.434180000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4128.JPG] Location updated, lat: 34.68192337279844, lon: 131.8327970596869
⠂ [00:00:04] [###########################>-----------------------------------------------] 93/233 (6.7s)
⠉ [00:00:03] [###########################>-----------------------------------------------] 54/77 (1.8s)
⠉ [00:00:03] [###########################>-----------------------------------------------] 55/77 (1.6s)
⠉ [00:00:03] [#############################>---------------------------------------------] 60/79 (1.4s)
```
## Usage
Expand All @@ -38,7 +42,7 @@ nya-exif -f /path/to/life-path/data /path/to/images
# If the ExifTool installation path is not in PATH, manually specify the executable file location.
nya-exif -b /path/to/exiftool /path/to/images

# Specify the target coordinate system, default is China's GCJ-02 coordinate system. Needs to be specified as WGS-84 coordinate system if the photo is taken overseas.
# Specify the target coordinate system, default is China's GCJ-02 coordinate system.
nya-exif -c wgs84 /path/to/images
```
Expand Down Expand Up @@ -70,6 +74,13 @@ Options:
-r, --recursive
Turn on recursive mode

-x, --threads <THREADS>
Threads

Number of threads to use.

[default: 3]

-w, --writer-type <WRITER_TYPE>
Exif writer type

Expand Down
193 changes: 110 additions & 83 deletions src/core/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use crate::exif_writer::{exiftool::ExifWriterExifTool, ExifWriterBase, ExifWrite
use crate::location_reader::{life_path, LocationReaderBase, LocationReaderParam};
use crate::util::{file, location as lc};
use clap::ValueEnum;
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use indicatif::{MultiProgress, ProgressBar, ProgressState, ProgressStyle};
use simple_log::log::{info, warn};
use std::fmt::Write;
use std::path::{Path, PathBuf};
use std::thread;
use undrift_gps::wgs_to_gcj;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
Expand All @@ -28,10 +29,11 @@ pub enum LocationGpsCoordinateTarget {
GCJ02,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct AppParams {
pub operate_dir: Vec<PathBuf>,
pub recursive: bool,
pub thread_count: u32,
pub writer_type: ExifWriterType,
pub writer_bin_path: Option<PathBuf>,
pub location_reader_type: LocationReaderType,
Expand All @@ -43,104 +45,129 @@ pub struct AppParams {
}

pub fn run(params: AppParams) {
let exif_param = ExifWriterParam {
binary_path: params.writer_bin_path.clone(),
};

let mut exiftool: Box<dyn ExifWriterBase>;
match params.writer_type {
ExifWriterType::Exiftool => {
exiftool = Box::new(ExifWriterExifTool::new(exif_param));
}
}

let location_param = LocationReaderParam {
data_path: params.location_file_path.clone(),
time_offset: params.time_offset.clone(),
max_interval: params.location_max_interval.clone(),
};

let mut location_reader: Box<dyn LocationReaderBase>;
match params.location_reader_type {
LocationReaderType::LifePath => {
location_reader = Box::new(life_path::LocationReaderLiftPath::new(location_param));
}
}

let mut fi = vec![];
for dir in params.operate_dir {
for dir in params.operate_dir.clone() {
let fi_ = file::read_dir_files(dir.as_ref(), true, true).unwrap();
fi.extend(fi_);
}

let pb = ProgressBar::new(fi.len() as u64);
pb.set_style(
ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>7}/{len:7} ({eta})",
)
.unwrap()
.with_key("eta", |state: &ProgressState, w: &mut dyn Write| {
write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()
})
.progress_chars("#>-"),
);

// split fi in params.thread_count parts
let mut fi_parts = vec![];
let mut now_state = 0;
let mut now_part = 0;
let part_size = fi.len() / params.thread_count as usize;
for file in fi {
now_state += 1;
if now_state == 0 {
fi_parts.push(vec![]);
}

let filename = file.as_str();
let time = exiftool.read_timestamp(filename);
let mut location = location_reader.get_location(time as i32);
fi_parts[now_part].push(file);

if location.is_some() {
if params.location_gps_coordinate_target.is_some() {
match params.location_gps_coordinate_target.unwrap() {
LocationGpsCoordinateTarget::GCJ02 => {
let mut lo = location.unwrap();
let (lat, lon) = wgs_to_gcj(lo.lat, lo.lon);
lo.lat = lat;
lo.lon = lon;
now_state += 1;
if now_state == part_size && now_part + 1 < params.thread_count as usize {
now_state = 0;
now_part += 1;
}
}

location = Some(lo);
}
_ => {}
let m = MultiProgress::new();
let sty = ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>7}/{len:7} ({eta})",
)
.unwrap()
.with_key("eta", |state: &ProgressState, w: &mut dyn Write| {
write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()
})
.progress_chars("#>-");

let mut threads = vec![];
for fi in fi_parts {
let count = fi.len();
let pb = m.add(ProgressBar::new(count as u64));
pb.set_style(sty.clone());

let params = params.clone();
threads.push(thread::spawn(move || {
let exif_param = ExifWriterParam {
binary_path: params.writer_bin_path.clone(),
};

let mut exiftool: Box<dyn ExifWriterBase>;
match params.writer_type {
ExifWriterType::Exiftool => {
exiftool = Box::new(ExifWriterExifTool::new(exif_param));
}
} else {
let llo = location.clone().unwrap();
let result = lc::is_point_in_gcj_region((llo.lat, llo.lon));
}

if result {
let mut lo = location.unwrap();
let (lat, lon) = wgs_to_gcj(lo.lat, lo.lon);
lo.lat = lat;
lo.lon = lon;
let location_param = LocationReaderParam {
data_path: params.location_file_path.clone(),
time_offset: params.time_offset.clone(),
max_interval: params.location_max_interval.clone(),
};

location = Some(lo);
let mut location_reader: Box<dyn LocationReaderBase>;
match params.location_reader_type {
LocationReaderType::LifePath => {
location_reader = Box::new(life_path::LocationReaderLiftPath::new(location_param));
}
}
}

if location.is_some() {
let location = location.unwrap();
exiftool.write_location(filename, location.lat, location.lon, location.alt);

pb.suspend(|| {
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap();
info!(
"[{}] Location updated, lat: {}, lon: {}",
filename, location.lat, location.lon
);
});
} else {
pb.suspend(|| {
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap();
warn!("[{}] No matching location, timestamp {}", filename, time);
});
}
let mut now_state = 0;
for file in fi {
now_state += 1;

let filename = file.as_str();
let time = exiftool.read_timestamp(filename);
let location = location_reader.get_location(time as i32);

if location.is_some() {
let mut location = location.unwrap();

if params.location_gps_coordinate_target.is_some() {
match params.location_gps_coordinate_target.unwrap() {
LocationGpsCoordinateTarget::GCJ02 => {
let (lat, lon) = wgs_to_gcj(location.lat, location.lon);
location.lat = lat;
location.lon = lon;
}
_ => {}
}
} else {
let result = lc::is_point_in_gcj_region((location.lat, location.lon));

if result {
let (lat, lon) = wgs_to_gcj(location.lat, location.lon);
location.lat = lat;
location.lon = lon;
}
}

exiftool.write_location(filename, location.lat, location.lon, location.alt);

pb.suspend(|| {
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap();
info!(
"[{}] Location updated, lat: {}, lon: {}",
filename, location.lat, location.lon
);
});
} else {
pb.suspend(|| {
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap();
warn!("[{}] No matching location, timestamp {}", filename, time);
});
}

pb.set_position(now_state);
}
}));
}

pb.set_position(now_state);
for thread in threads {
let _ = thread.join();
}
m.clear().unwrap();
info!("Finished");

pb.finish_with_message("Finished");
// pb.finish_with_message("Finished");
}
Loading

0 comments on commit 179bb85

Please sign in to comment.