Database based caching via diesel & SQLite added.

This commit is contained in:
Micha Glave 2020-06-28 16:31:56 +02:00
parent e493d37860
commit 3eb6e425f5
13 changed files with 196 additions and 10 deletions

3
.gitignore vendored
View File

@ -169,3 +169,6 @@ dist
# Stores VSCode versions used for testing VSCode extensions # Stores VSCode versions used for testing VSCode extensions
.vscode-test .vscode-test
# SQLite
*.sqlite3
*.db

View File

@ -1,15 +1,25 @@
[package] [package]
name = "curators" authors = ["Micha Glave <coding@migmedia.de>"]
version = "0.1.0" description = "An image gallery, defined by plain files."
authors = ["Micha Glave <mig@xilab.net>"]
edition = "2018" edition = "2018"
exclude = [".travis.yml", ".gitignore", "test-data/**"] exclude = [".travis.yml", ".gitignore", "test-data/**"]
description = "Image Gallery. Defined by plain files." name = "curators"
readme = "README.md"
version = "0.1.0"
[dependencies] [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" structopt = "0.3"
#exempi = "2.5.0"
strum = "0.18.0" strum = "0.18.0"
strum_macros = "0.18.0" strum_macros = "0.18.0"
kamadak-exif = "0.5" toml = "0.5"
chrono = "0.4"
anyhow = "1"

View File

@ -1,10 +1,27 @@
# curators # curators - Picture data long-term archive
Image Gallery. Defined by plain files. 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: ## 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) * [Exif](https://de.wikipedia.org/wiki/Exchangeable_Image_File_Format)
* [IPTC](https://de.wikipedia.org/wiki/IPTC-IIM-Standard) * [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
View 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
View File

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS images;

View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,9 @@
table! {
images (id) {
id -> Text,
path -> Text,
title -> Nullable<Text>,
last_changed -> Timestamp,
published -> Bool,
}
}