Database based caching via diesel & SQLite added.
This commit is contained in:
parent
e493d37860
commit
3eb6e425f5
3
.gitignore
vendored
3
.gitignore
vendored
@ -169,3 +169,6 @@ dist
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# SQLite
|
||||
*.sqlite3
|
||||
*.db
|
||||
|
24
Cargo.toml
24
Cargo.toml
@ -1,15 +1,25 @@
|
||||
[package]
|
||||
name = "curators"
|
||||
version = "0.1.0"
|
||||
authors = ["Micha Glave <mig@xilab.net>"]
|
||||
authors = ["Micha Glave <coding@migmedia.de>"]
|
||||
description = "An image gallery, defined by plain files."
|
||||
edition = "2018"
|
||||
exclude = [".travis.yml", ".gitignore", "test-data/**"]
|
||||
description = "Image Gallery. Defined by plain files."
|
||||
name = "curators"
|
||||
readme = "README.md"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
toml = "0.5"
|
||||
actix-rt = "1.0"
|
||||
actix-web = "2.0"
|
||||
diesel = { version ="1.4", features = ["sqlite", "r2d2", "uuid", "chrono"] }
|
||||
uuid = { version="0.7", features = ["v5"] }
|
||||
dotenv = "0.15"
|
||||
env_logger = "0.7"
|
||||
futures = "0.3"
|
||||
kamadak-exif = "0.5"
|
||||
listenfd = "0.3"
|
||||
structopt = "0.3"
|
||||
#exempi = "2.5.0"
|
||||
strum = "0.18.0"
|
||||
strum_macros = "0.18.0"
|
||||
kamadak-exif = "0.5"
|
||||
toml = "0.5"
|
||||
chrono = "0.4"
|
||||
anyhow = "1"
|
||||
|
23
README.md
23
README.md
@ -1,10 +1,27 @@
|
||||
# curators
|
||||
# curators - Picture data long-term archive
|
||||
|
||||
Image Gallery. Defined by plain files.
|
||||
|
||||
## Problem definition:
|
||||
|
||||
Managing many images from different sources is a challenge. Either there is a quantity limit
|
||||
by storage space or program (iPhoto), vendor lock or security issues throughout the environment.
|
||||
|
||||
|
||||
## Goal:
|
||||
Image management for multiple (competing) users. Purely file-based, non-destructive, based on standards (as much as possible). Using
|
||||
Image management for multiple (competing) users. Purely file-based, non-destructive, based on standards (as much as
|
||||
possible). Using
|
||||
|
||||
* [Exif](https://de.wikipedia.org/wiki/Exchangeable_Image_File_Format)
|
||||
* [IPTC](https://de.wikipedia.org/wiki/IPTC-IIM-Standard)
|
||||
* [XMP](https://de.wikipedia.org/wiki/Extensible_Metadata_Platform)
|
||||
* [XMP](https://de.wikipedia.org/wiki/Extensible_Metadata_Platform)
|
||||
|
||||
|
||||
|
||||
## Principles:
|
||||
|
||||
* Original file is not changed.
|
||||
* Meta-data are next to the image file
|
||||
* description of contents: people / faces, place, tags
|
||||
* Authorization
|
||||
* Reshaping (crop, color correction)
|
||||
|
5
diesel.toml
Normal file
5
diesel.toml
Normal file
@ -0,0 +1,5 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
0
migrations/.gitkeep
Normal file
0
migrations/.gitkeep
Normal file
1
migrations/2020-06-15-200823_images/down.sql
Normal file
1
migrations/2020-06-15-200823_images/down.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS images;
|
12
migrations/2020-06-15-200823_images/up.sql
Normal file
12
migrations/2020-06-15-200823_images/up.sql
Normal file
@ -0,0 +1,12 @@
|
||||
CREATE TABLE images (
|
||||
id VARCHAR(40) PRIMARY KEY NOT NULL,
|
||||
path VARCHAR(500) NOT NULL,
|
||||
title VARCHAR(200),
|
||||
last_changed TIMESTAMP NOT NULL,
|
||||
published BOOLEAN NOT NULL DEFAULT 'f'
|
||||
);
|
||||
|
||||
INSERT INTO images (id, path, title, last_changed, published) VALUES
|
||||
('8e153cab-7dc9-46c7-9a72-91e56ac174ca', './BlueSquare.jpg', 'Blaues Quadrat', '2020-05-27 13:33:56.747473', false );
|
||||
INSERT INTO images (id, path, title, last_changed, published) VALUES
|
||||
('ea01f5cd-a532-4232-810c-bae977cc4336', './Canon_40D.jpg', 'Canon 40d', '2020-05-27 13:33:56.747473', false );
|
18
src/bin/listdb.rs
Normal file
18
src/bin/listdb.rs
Normal file
@ -0,0 +1,18 @@
|
||||
extern crate curators;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use curators::database::*;
|
||||
use curators::models::Image;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let results = load_images();
|
||||
println!("Displaying {} images", results.len());
|
||||
for img in results {
|
||||
println!("{:?} ", img);
|
||||
}
|
||||
let p = Path::new(r"./BlueSquare.jpg").canonicalize().unwrap();
|
||||
println!("{:?}", Image::try_from(&p));
|
||||
let p = Path::new(r"./Canon_40D.jpg").canonicalize().unwrap();
|
||||
println!("{:?}", Image::try_from(&p));
|
||||
}
|
26
src/database.rs
Normal file
26
src/database.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
|
||||
use crate::models::Image;
|
||||
use dotenv::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub fn establish_connection() -> SqliteConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
SqliteConnection::establish(&database_url)
|
||||
.expect(&format!("Error connecting to {}", database_url))
|
||||
}
|
||||
|
||||
pub fn load_images() -> Vec<Image> {
|
||||
use crate::schema::images::dsl::*;
|
||||
|
||||
let connection = establish_connection();
|
||||
images
|
||||
.filter(published.eq(false))
|
||||
.limit(50)
|
||||
.load::<Image>(&connection)
|
||||
.expect("Error loading images")
|
||||
}
|
24
src/handling.rs
Normal file
24
src/handling.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use snafu::{ensure, Backtrace, ErrorCompat, ResultExt, Snafu};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum Error {
|
||||
#[snafu(display("Could not open config from {}: {}", filename.display(), source))]
|
||||
OpenConfig {
|
||||
filename: PathBuf,
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[snafu(display("Could not save config to {}: {}", filename.display(), source))]
|
||||
SaveConfig {
|
||||
filename: PathBuf,
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[snafu(display("Could not read file {}: {}", filename.display(), source))]
|
||||
OpenFile {
|
||||
filename: PathBuf,
|
||||
source: std::io::Error,
|
||||
},
|
||||
}
|
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
extern crate anyhow;
|
||||
extern crate dotenv;
|
||||
|
||||
pub mod database;
|
||||
pub mod models;
|
||||
pub mod schema;
|
53
src/models.rs
Normal file
53
src/models.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::schema::images;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::NaiveDateTime;
|
||||
use std::ffi::OsString;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Queryable, Insertable, Identifiable)]
|
||||
pub struct Image {
|
||||
pub id: String,
|
||||
pub path: String,
|
||||
pub title: Option<String>,
|
||||
pub last_changed: NaiveDateTime,
|
||||
pub published: bool,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn try_from(path: &Path) -> anyhow::Result<Self> {
|
||||
let abs_path = path.canonicalize().context("Path not valid!")?;
|
||||
let os_path = abs_path.clone().into_os_string();
|
||||
let path_str = os_path.into_string().expect(&format!(
|
||||
"Converting path-name problem! {:?}",
|
||||
path.display()
|
||||
));
|
||||
let uuid = Uuid::new_v5(&Uuid::NAMESPACE_URL, &path_str.as_bytes());
|
||||
let metadata = abs_path
|
||||
.metadata()
|
||||
.context(format!("Unable to load Metadata for: {}", path.display()))?;
|
||||
let time = match metadata.modified() {
|
||||
Ok(t) => t,
|
||||
Err(_) => metadata.created().context(format!(
|
||||
"Unable to read created-timestamp of: {}",
|
||||
path.display()
|
||||
))?,
|
||||
};
|
||||
let mtime = time.duration_since(UNIX_EPOCH).unwrap();
|
||||
Ok(Image {
|
||||
id: uuid.to_string(),
|
||||
path: path_str.to_string(),
|
||||
title: None,
|
||||
last_changed: NaiveDateTime::from_timestamp(
|
||||
mtime.as_secs() as i64,
|
||||
mtime.subsec_nanos() as u32,
|
||||
),
|
||||
published: false,
|
||||
})
|
||||
}
|
||||
}
|
9
src/schema.rs
Normal file
9
src/schema.rs
Normal file
@ -0,0 +1,9 @@
|
||||
table! {
|
||||
images (id) {
|
||||
id -> Text,
|
||||
path -> Text,
|
||||
title -> Nullable<Text>,
|
||||
last_changed -> Timestamp,
|
||||
published -> Bool,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user