diff --git a/src/database.rs b/src/database.rs
index feb7ca5..efd16c5 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -36,7 +36,7 @@ impl Database {
     pub async fn _insert_sheet(&self, sheet: Sheet) -> sqlx::Result<()> {
         sqlx::query(
             "
-                INSERT INTO sheets (name, composer_id, path, file_size, file_hash, last_modified)
+                INSERT INTO sheets (name, composer_id, path, file_size, file_hash, last_opened)
                 VALUES ($1, $2, $3, $4, $5, $6)
             ",
         )
@@ -66,20 +66,20 @@ impl Database {
             .await
     }
 
-    pub async fn insert_orphan_file(&self, file: OrphanFile) -> sqlx::Result<()> {
+    pub async fn insert_orphan_file(&self, file: &OrphanFile) -> sqlx::Result<i64> {
         sqlx::query(
             "
-                INSERT INTO orphan_files (path, file_size, file_hash, last_modified)
+                INSERT INTO orphan_files (path, file_size, file_hash, last_opened)
                 VALUES ($1, $2, $3, $4)
             ",
         )
         .bind(file.path.to_str().unwrap().to_string())
         .bind(file.file_size as i32)
-        .bind(file.file_hash)
+        .bind(file.file_hash.clone())
         .bind(file.last_opened.timestamp())
         .execute(&self.connection)
         .await
-        .map(|_| ())
+        .map(|result| result.last_insert_rowid())
     }
 
     pub async fn update_orphan_file_path(&self, file: &OrphanFile) -> sqlx::Result<()> {
diff --git a/src/main.rs b/src/main.rs
index 45a4fb2..99890aa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@ use database::Database;
 use env_logger::Env;
 use log::{debug, error};
 use relm4::RelmApp;
-use sheet::Sheet;
+use sheet::{OrphanFile, Sheet};
 use walkdir::WalkDir;
 
 use crate::ui::app::AppModel;
@@ -35,17 +35,41 @@ async fn main() {
     let database = Database::setup("./testdb.sqlite").await.unwrap();
     // database.insert_sheet(Sheet::new_debug()).await.unwrap();
     let sheets = database.fetch_all_sheets().await.unwrap();
+    let orphan_files = database.fetch_all_orphan_files().await.unwrap();
 
     debug!("Validating sheets from database...");
-    let validation_result = validate_sheet_files(sheets, &cli.directory);
-    debug!("{}", validation_result.get_stats());
+    let mut validation_result = validate_sheet_files(sheets, orphan_files, &cli.directory);
+    debug!("{}", validation_result.get_stats()); // TODO: handle invalidated files
     for updated in validation_result.updated_sheets.iter() {
         database.update_sheet_path(updated).await.unwrap();
     }
+    for updated in validation_result.updated_orphan_files.iter() {
+        database.update_orphan_file_path(updated).await.unwrap();
+    }
+
+    let mut orphans = validation_result.validated_orphan_files;
+    debug!("Inserting unassigned files into orphan table...");
+    for unassigned in validation_result.unassigned_files {
+        let mut orphan = OrphanFile::try_from(unassigned).unwrap();
+        let id = database.insert_orphan_file(&orphan).await.unwrap();
+        orphan.id = id;
+        orphans.push(orphan);
+    }
+
+    let mut sheets = validation_result.validated_sheets;
+    sheets.append(&mut validation_result.updated_sheets);
+
+    let sheets_and_orphans = SheetsAndOrphans { sheets, orphans };
 
     let app = RelmApp::new("de.frajul.sheet-organizer");
     // Pass empty command line args to allow my own parsing
-    app.with_args(Vec::new()).run::<AppModel>(validation_result);
+    app.with_args(Vec::new())
+        .run::<AppModel>(sheets_and_orphans);
+}
+
+pub struct SheetsAndOrphans {
+    sheets: Vec<Sheet>,
+    orphans: Vec<OrphanFile>,
 }
 
 pub struct FileValidationResult {
@@ -53,21 +77,39 @@ pub struct FileValidationResult {
     invalidated_sheets: Vec<Sheet>,
     updated_sheets: Vec<Sheet>,
 
+    validated_orphan_files: Vec<OrphanFile>,
+    invalidated_orphan_files: Vec<OrphanFile>,
+    updated_orphan_files: Vec<OrphanFile>,
+
     unassigned_files: Vec<PathBuf>,
 }
 
 impl FileValidationResult {
     fn get_stats(&self) -> String {
-        format!("Validated sheets: {}\nInvalidated sheets: {}\nUpdated sheets: {}\nUnassigned files: {}", self.validated_sheets.len(), self.invalidated_sheets.len(), self.updated_sheets.len(), self.unassigned_files.len())
+        format!("Validated sheets: {}\nInvalidated sheets: {}\nUpdated sheets: {}\nValidated orphan_files: {}\nInvalidated orphan_files: {}\nUpdated orphan_files: {}\nUnassigned files: {}",
+                self.validated_sheets.len(), self.invalidated_sheets.len(), self.updated_sheets.len(),
+                self.validated_orphan_files.len(), self.invalidated_orphan_files.len(), self.updated_orphan_files.len(), self.unassigned_files.len())
     }
 }
 
-fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValidationResult {
+fn validate_sheet_files(
+    sheets: Vec<Sheet>,
+    orphan_files: Vec<OrphanFile>,
+    dir: impl AsRef<Path>,
+) -> FileValidationResult {
+    // TODO: fix duplication
     let (validated_sheets, mut invalidated_sheets): (Vec<_>, Vec<_>) = sheets
         .into_iter()
         .partition(|sheet| sheet.validate_path(&sheet.path).unwrap_or(false));
+    let (validated_orphan_files, mut invalidated_orphan_files): (Vec<_>, Vec<_>) =
+        orphan_files.into_iter().partition(|orphan_file| {
+            orphan_file
+                .validate_path(&orphan_file.path)
+                .unwrap_or(false)
+        });
 
     let mut updated_sheets = Vec::new();
+    let mut updated_orphan_files = Vec::new();
     let mut unassigned_files = Vec::new();
 
     for pdf_file in WalkDir::new(dir)
@@ -89,7 +131,19 @@ fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValida
             let mut sheet = invalidated_sheets.remove(i);
             sheet.path = pdf_file;
             updated_sheets.push(sheet);
-        } else {
+        } else if let Some((i, _)) = invalidated_orphan_files
+            .iter()
+            .enumerate()
+            .find(|(_, orphan_file)| orphan_file.validate_path(&pdf_file).unwrap_or(false))
+        {
+            let mut orphan_file = invalidated_orphan_files.remove(i);
+            orphan_file.path = pdf_file;
+            updated_orphan_files.push(orphan_file);
+        } else if !validated_sheets.iter().any(|sheet| sheet.path == pdf_file)
+            && !validated_orphan_files
+                .iter()
+                .any(|orphan| orphan.path == pdf_file)
+        {
             unassigned_files.push(pdf_file);
         }
     }
@@ -98,6 +152,9 @@ fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValida
         validated_sheets,
         invalidated_sheets,
         updated_sheets,
+        validated_orphan_files,
+        invalidated_orphan_files,
+        updated_orphan_files,
         unassigned_files,
     }
 }
diff --git a/src/sheet.rs b/src/sheet.rs
index 578e6a5..b533213 100644
--- a/src/sheet.rs
+++ b/src/sheet.rs
@@ -9,9 +9,9 @@ use chrono::{DateTime, NaiveDateTime, Utc};
 
 #[derive(Debug, Clone)]
 pub struct Sheet {
-    pub id: i32,
+    pub id: i64,
     pub name: String,
-    pub composer_id: i32,
+    pub composer_id: i64,
     pub path: PathBuf,
     pub file_size: u64,
     pub file_hash: String,
@@ -25,7 +25,7 @@ impl FromRow<'_, SqliteRow> for Sheet {
             name: row.try_get("name")?,
             composer_id: row.try_get("composer_id")?,
             path: row.try_get::<&str, _>("path")?.into(),
-            file_size: row.try_get::<i32, _>("file_size")? as u64,
+            file_size: row.try_get::<i64, _>("file_size")? as u64,
             file_hash: row.try_get("file_hash")?,
             last_opened: NaiveDateTime::from_timestamp_opt(
                 row.try_get::<i64, _>("last_opened")?,
@@ -39,7 +39,7 @@ impl FromRow<'_, SqliteRow> for Sheet {
 
 #[derive(sqlx::FromRow)]
 pub struct Composer {
-    pub id: i32,
+    pub id: i64,
     pub name: String,
 }
 
@@ -59,9 +59,44 @@ impl Sheet {
     }
 }
 
+impl OrphanFile {
+    // TODO: fix duplication
+    pub fn validate_path(&self, path: impl AsRef<Path>) -> std::io::Result<bool> {
+        // First compare file size since it is faster than hashing
+        let file_size = fs::metadata(path.as_ref())?.len();
+        if file_size == self.file_size {
+            let file_content = fs::read(path.as_ref())?;
+            let file_hash = blake3::hash(&file_content);
+            if file_hash.to_string() == self.file_hash {
+                return Ok(true);
+            }
+        }
+
+        Ok(false)
+    }
+}
+
+impl TryFrom<PathBuf> for OrphanFile {
+    type Error = std::io::Error;
+
+    fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
+        let file_size = fs::metadata(path.as_path())?.len();
+        let file_content = fs::read(path.as_path())?;
+        let file_hash = blake3::hash(&file_content).to_string();
+
+        Ok(OrphanFile {
+            id: -1,
+            path,
+            file_size,
+            file_hash,
+            last_opened: DateTime::default(),
+        })
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct OrphanFile {
-    pub id: i32,
+    pub id: i64,
     pub path: PathBuf,
     pub file_size: u64,
     pub file_hash: String,
@@ -73,7 +108,7 @@ impl FromRow<'_, SqliteRow> for OrphanFile {
         Ok(Self {
             id: row.try_get("id")?,
             path: row.try_get::<&str, _>("path")?.into(),
-            file_size: row.try_get::<i32, _>("file_size")? as u64,
+            file_size: row.try_get::<i64, _>("file_size")? as u64,
             file_hash: row.try_get("file_hash")?,
             last_opened: NaiveDateTime::from_timestamp_opt(
                 row.try_get::<i64, _>("last_opened")?,
diff --git a/src/ui/app.rs b/src/ui/app.rs
index f732921..13333ba 100644
--- a/src/ui/app.rs
+++ b/src/ui/app.rs
@@ -1,11 +1,9 @@
-
-
 use gtk::prelude::*;
-use relm4::{prelude::*};
+use relm4::prelude::*;
 
 use crate::{
     ui::{mcdu::McduOutput, sheet_model::SheetModelType},
-    FileValidationResult,
+    FileValidationResult, SheetsAndOrphans,
 };
 
 use super::{
@@ -30,7 +28,7 @@ pub enum AppInput {
 impl SimpleComponent for AppModel {
     type Input = AppInput;
     type Output = ();
-    type Init = FileValidationResult;
+    type Init = SheetsAndOrphans;
 
     view! {
         #[root]
@@ -69,7 +67,7 @@ impl SimpleComponent for AppModel {
     }
 
     fn init(
-        file_validation_result: Self::Init,
+        sheets_and_orphans: Self::Init,
         window: &Self::Root,
         sender: ComponentSender<Self>,
     ) -> relm4::ComponentParts<Self> {
@@ -81,22 +79,22 @@ impl SimpleComponent for AppModel {
                 McduOutput::SearchStarted(query) => AppInput::SearchStarted(query),
             });
 
-        let new_files: Vec<SheetModelType> = file_validation_result
-            .unassigned_files
+        let new_files: Vec<SheetModelType> = sheets_and_orphans
+            .orphans
             .iter()
-            .map(|file| SheetModelType::Pdf {
-                path: file.to_path_buf(),
+            .map(|orphan| SheetModelType::Orphan {
+                orphan: orphan.clone(),
             })
             .collect();
-        let new_files_clone_iter = file_validation_result
-            .unassigned_files
-            .into_iter()
-            .map(|file| SheetModelType::Pdf { path: file });
 
-        let sheets_and_files: Vec<SheetModelType> = file_validation_result
-            .validated_sheets
+        let new_files_clone_iter = sheets_and_orphans
+            .orphans
+            .into_iter()
+            .map(|orphan| SheetModelType::Orphan { orphan });
+
+        let sheets_and_files: Vec<SheetModelType> = sheets_and_orphans
+            .sheets
             .into_iter()
-            .chain(file_validation_result.updated_sheets)
             .map(|sheet| SheetModelType::Sheet { sheet })
             .chain(new_files_clone_iter)
             .collect();
diff --git a/src/ui/sheet_model.rs b/src/ui/sheet_model.rs
index 66680f8..c2ca768 100644
--- a/src/ui/sheet_model.rs
+++ b/src/ui/sheet_model.rs
@@ -3,21 +3,21 @@ use std::path::{Path, PathBuf};
 use gtk::prelude::*;
 use relm4::prelude::*;
 
-use crate::sheet::Sheet;
+use crate::sheet::{OrphanFile, Sheet};
 
 use super::sheet_listing::SheetListingInput;
 
 #[derive(Debug, Clone)]
 pub enum SheetModelType {
     Sheet { sheet: Sheet },
-    Pdf { path: PathBuf },
+    Orphan { orphan: OrphanFile },
 }
 
 impl SheetModelType {
     pub fn get_path(&self) -> &Path {
         match self {
             SheetModelType::Sheet { sheet } => &sheet.path.as_path(),
-            SheetModelType::Pdf { path } => path.as_path(),
+            SheetModelType::Orphan { orphan } => &orphan.path.as_path(),
         }
     }
 }
@@ -64,7 +64,13 @@ impl FactoryComponent for SheetModel {
     fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
         let label = match &value {
             SheetModelType::Sheet { sheet } => sheet.name.to_string(),
-            SheetModelType::Pdf { path } => path.file_name().unwrap().to_str().unwrap().to_string(),
+            SheetModelType::Orphan { orphan } => orphan
+                .path
+                .file_name()
+                .unwrap()
+                .to_str()
+                .unwrap()
+                .to_string(),
         };
 
         SheetModel {