diff --git a/Cargo.lock b/Cargo.lock
index 49a533e669af4d801be6601f9fba174090536faa..9b025daa4181e1510a78ee6f9d266fd1fb3eec4a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -540,9 +540,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "0.4.5"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "jobserver"
@@ -625,7 +625,7 @@ dependencies = [
 
 [[package]]
 name = "media-ingestion"
-version = "0.2.0"
+version = "1.0.0"
 dependencies = [
  "anyhow",
  "ffmpeg_api",
@@ -1110,11 +1110,12 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.57"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
@@ -1163,9 +1164,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "structopt"
-version = "0.3.18"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82"
+checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
 dependencies = [
  "clap",
  "lazy_static",
@@ -1174,9 +1175,9 @@ dependencies = [
 
 [[package]]
 name = "structopt-derive"
-version = "0.4.11"
+version = "0.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9"
+checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
 dependencies = [
  "heck 0.3.1",
  "proc-macro-error",
diff --git a/Cargo.toml b/Cargo.toml
index d1df18c8417ae2a2fb76efd9b7deeb501bd1473f..7ca921229f74727f08c441614f0e50e80fa51716 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,17 +12,17 @@ repository = "https://github.com/justjanne/media-ingestion"
 readme = "README.md"
 license = "MPL-2.0"
 
-edition = "2018"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.32"
+anyhow = "1.0.98"
 fraction = "0.15.3"
 image = "0.25.6"
-serde_json = "1.0.57"
-serde = { version = "1.0.116", features = ["derive"] }
-structopt = "0.3.18"
+serde_json = "1.0.140"
+serde = { version = "1.0.219", features = ["derive"] }
+structopt = "0.3.26"
 
 ffmpeg_api = { path = "lib/ffmpeg_api" }
 media_time = { path = "lib/media_time" }
diff --git a/lib/ffmpeg_api/Cargo.toml b/lib/ffmpeg_api/Cargo.toml
index 407e9caab7441d469ba151b78f133e63d58ba7b1..4b7ae65238ef3fe68106621c7f858378e6f00baa 100644
--- a/lib/ffmpeg_api/Cargo.toml
+++ b/lib/ffmpeg_api/Cargo.toml
@@ -2,7 +2,7 @@
 name = "ffmpeg_api"
 version = "0.1.0"
 authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
-edition = "2018"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/lib/media_time/Cargo.toml b/lib/media_time/Cargo.toml
index 6420efc39143cffdef20eddd8a7f02939615f727..224647b892c7fe1fcb3daf4c43efc6d1bc57003c 100644
--- a/lib/media_time/Cargo.toml
+++ b/lib/media_time/Cargo.toml
@@ -2,7 +2,7 @@
 name = "media_time"
 version = "0.1.0"
 authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
-edition = "2018"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/lib/webvtt/Cargo.toml b/lib/webvtt/Cargo.toml
index 178a5cca295eebefdd64ef1c9ded38e281a541b6..20de307eb23f6175c8023ae72437eb96842ff76f 100644
--- a/lib/webvtt/Cargo.toml
+++ b/lib/webvtt/Cargo.toml
@@ -2,7 +2,7 @@
 name = "webvtt"
 version = "0.1.0"
 authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
-edition = "2018"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/src/lib.rs b/src/lib.rs
index c806a501416782690a19b93fba88901759b65340..09c48ece0dfbd4f03fd1cc64c3c367a19ab47c82 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,22 +1,20 @@
 #![allow(dead_code)]
 
 pub mod spritesheet;
+mod options;
 
 use std::path::Path;
 
 use anyhow::format_err;
 use ffmpeg_api::api::*;
 use ffmpeg_api::enums::*;
-use image::ImageFormat as ImageOutputFormat;
+pub use options::ExtractOptions;
 
+#[allow(clippy::too_many_arguments)]
 pub fn extract(
-    max_size: u32,
-    num_horizontal: u32,
-    num_vertical: u32,
-    frame_interval: media_time::MediaTime,
     input_file: &Path,
     output_folder: &Path,
-    format: ImageOutputFormat,
+    options: ExtractOptions,
     scaler: SwsScaler,
     flags: SwsFlags,
 ) -> anyhow::Result<()> {
@@ -24,15 +22,11 @@ pub fn extract(
     avformat_context.open_input(input_file)?;
     let duration = avformat_context.duration()?;
 
-    std::fs::create_dir_all(&output_folder)?;
+    std::fs::create_dir_all(output_folder)?;
     let mut spritesheet_manager = spritesheet::SpritesheetManager::new(
-        max_size,
-        num_horizontal,
-        num_vertical,
-        frame_interval,
+        options,
         output_folder,
         "preview",
-        format,
     );
 
     let mut stream: AVStream = avformat_context
diff --git a/src/main.rs b/src/main.rs
index 3882818df2afacffc71a5af01c9aa3536d919728..07a5327b0c9d216c0523616a6d93bffdc2cf4420 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ use ffmpeg_api::enums::{SwsFlags, SwsScaler};
 use image::ImageFormat as ImageOutputFormat;
 use media_time::MediaTime;
 use structopt::StructOpt;
+use media_ingestion::ExtractOptions;
 
 fn parse_scaler(src: &str) -> Result<SwsScaler, String> {
     match src {
@@ -62,17 +63,19 @@ fn main() -> anyhow::Result<()> {
     }
 
     if let Err(err) = media_ingestion::extract(
-        options.max_size,
-        options.num_horizontal,
-        options.num_vertical,
-        MediaTime::from_seconds(options.frame_interval),
         Path::new(&options.input),
         Path::new(&options.output),
-        match options.format.as_str() {
-            "jpeg" | "jpg" => ImageOutputFormat::Jpeg,
-            "png" => ImageOutputFormat::Png,
-            "bmp" => ImageOutputFormat::Bmp,
-            _ => panic!("Unsupported image format: {}", options.format),
+        ExtractOptions {
+            max_size: options.max_size,
+            num_horizontal: options.num_horizontal,
+            num_vertical: options.num_vertical,
+            frame_interval: MediaTime::from_seconds(options.frame_interval),
+            format: match options.format.as_str() {
+                "jpeg" | "jpg" => ImageOutputFormat::Jpeg,
+                "png" => ImageOutputFormat::Png,
+                "bmp" => ImageOutputFormat::Bmp,
+                _ => panic!("Unsupported image format: {}", options.format),
+            }
         },
         options.scaler,
         flags,
diff --git a/src/options.rs b/src/options.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0f1362bccb2e27c485e8b1f142bb217e2ea1e1e2
--- /dev/null
+++ b/src/options.rs
@@ -0,0 +1,9 @@
+use image::ImageFormat as ImageOutputFormat;
+
+pub struct ExtractOptions {
+    pub max_size: u32,
+    pub num_horizontal: u32,
+    pub num_vertical: u32,
+    pub frame_interval: media_time::MediaTime,
+    pub format: ImageOutputFormat,
+}
diff --git a/src/spritesheet.rs b/src/spritesheet.rs
index 1168e094b864644d808dd7e53fc397988de13c09..2350d8db3392715e7de1e8de9efb27b41686f108 100644
--- a/src/spritesheet.rs
+++ b/src/spritesheet.rs
@@ -6,6 +6,7 @@ use anyhow::{bail, Error, format_err};
 use image::{DynamicImage, ImageFormat as ImageOutputFormat, RgbImage};
 use media_time::MediaTime;
 use webvtt::{WebVTTCue, WebVTTFile};
+use crate::options::ExtractOptions;
 
 pub enum ImageFormat {
     Jpeg(i32),
@@ -31,28 +32,24 @@ pub struct SpritesheetManager {
 
 impl SpritesheetManager {
     pub fn new(
-        max_side: u32,
-        num_horizontal: u32,
-        num_vertical: u32,
-        frame_interval: MediaTime,
+        options: ExtractOptions,
         output_path: impl Into<PathBuf>,
         name: impl AsRef<str>,
-        format: ImageOutputFormat,
     ) -> SpritesheetManager {
         SpritesheetManager {
-            num_horizontal,
-            num_vertical,
-            max_side,
+            num_horizontal: options.num_horizontal,
+            num_vertical: options.num_vertical,
+            max_side: options.max_size,
             sprite_width: 0,
             sprite_height: 0,
             spritesheet: RgbImage::new(0, 0),
             current_image: 0,
             last_timestamp: MediaTime::from_millis(0),
-            frame_interval,
+            frame_interval: options.frame_interval,
             metadata: WebVTTFile::new(),
             output_path: output_path.into(),
             name: String::from(name.as_ref()),
-            format,
+            format: options.format,
             initialized: false,
         }
     }
@@ -184,7 +181,7 @@ impl SpritesheetManager {
 
         let new_buffer = self.reinit_buffer();
         DynamicImage::ImageRgb8(std::mem::replace(&mut self.spritesheet, new_buffer))
-            .write_to(&mut BufWriter::new(file), self.format.clone())
+            .write_to(&mut BufWriter::new(file), self.format)
             .map_err(|err| format_err!("Could not write spritesheet {}: {}", &name, err))?;
 
         Ok(())