aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2026-03-03 14:16:45 +0300
committerA Farzat <a@farzat.xyz>2026-03-03 14:16:45 +0300
commit6782269bb52a349e9568a28ed6c1f43438b7bb24 (patch)
tree4f2cc973c6d160c475790dccf38aebeb4c5d4a69 /src
parent9a95cf1684fcd8bfc81efcac7676532413d35328 (diff)
downloadoreilly-epub-6782269bb52a349e9568a28ed6c1f43438b7bb24.tar.gz
oreilly-epub-6782269bb52a349e9568a28ed6c1f43438b7bb24.zip
Add container.xml to zip
Instead of writing it to file and then reading it again, we write container.xml straight to zip, just like we did with mimetype.
Diffstat (limited to 'src')
-rw-r--r--src/epub.rs35
-rw-r--r--src/main.rs9
2 files changed, 22 insertions, 22 deletions
diff --git a/src/epub.rs b/src/epub.rs
index e71e3ca..e993c31 100644
--- a/src/epub.rs
+++ b/src/epub.rs
@@ -1,5 +1,5 @@
use crate::models::FileEntry;
-use anyhow::Result;
+use anyhow::{Context, Result};
use reqwest::Client;
use std::{io::Write, path::Path};
use tokio::{
@@ -9,15 +9,7 @@ use tokio::{
use zip::{CompressionMethod, ZipWriter, write::FileOptions};
/// Creates and writes container.xml.
-pub async fn write_container_xml(dest_root: &Path, opf_full_path: &str) -> Result<()> {
- // Create destination directory.
- let dest_dir = dest_root.join("META-INF");
- fs::create_dir_all(&dest_dir).await?;
-
- // Create distination file.
- let dest_path = dest_dir.join("container.xml");
- let mut file = File::create(dest_path).await?;
-
+fn write_container_xml_to_zip(zip: &mut ZipWriter<std::fs::File>, opf_full_path: &str) -> Result<()> {
// Prepare file contents.
let contents = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
@@ -30,7 +22,10 @@ pub async fn write_container_xml(dest_root: &Path, opf_full_path: &str) -> Resul
);
// Write down the file.
- file.write_all(contents.as_bytes()).await?;
+ let options: FileOptions<()> =
+ FileOptions::default().compression_method(CompressionMethod::Deflated);
+ zip.start_file("META-INF/container.xml", options)?;
+ zip.write_all(contents.as_bytes())?;
Ok(())
}
@@ -61,14 +56,26 @@ pub async fn download_all_files(
}
/// Creates the EPUB archive (creates zip and includes all files in it).
-pub fn create_epub_archive(epub_root: &Path, output_epub: &Path) -> Result<()> {
+pub fn create_epub_archive(
+ epub_root: &Path,
+ output_epub: &Path,
+ file_entries: &[FileEntry],
+) -> Result<()> {
let out_file = std::fs::File::create(output_epub)?;
let mut zip = ZipWriter::new(out_file);
- let mimetype_options: FileOptions<()> =
+ // Write mimetype to zip first. It must be uncompressed.
+ let options: FileOptions<()> =
FileOptions::default().compression_method(CompressionMethod::Stored);
- zip.start_file("mimetype", mimetype_options)?;
+ zip.start_file("mimetype", options)?;
zip.write_all(b"application/epub+zip")?;
+ // Find the OPF file entry to reference it in container.xml
+ let opf_entry = file_entries
+ .iter()
+ .find(|f| f.filename_ext == ".opf" && f.media_type == "application/oebps-package+xml")
+ .context("No OPF file with the correct MIME type was found.")?;
+ write_container_xml_to_zip(&mut zip, &opf_entry.full_path)?;
+
Ok(())
}
diff --git a/src/main.rs b/src/main.rs
index db1fdd4..8c5c712 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,7 @@ mod models;
use std::collections::HashMap;
use std::path::Path;
-use crate::epub::{download_all_files, write_container_xml};
+use crate::epub::{download_all_files,};
use crate::http_client::build_authenticated_client;
use crate::models::{Chapter, EpubResponse, FileEntry, Paginated, SpineItem, TocNode};
use anyhow::{Context, Result, ensure};
@@ -112,16 +112,9 @@ async fn main() -> Result<()> {
let spine_items: Vec<SpineItem> = fetch_all_pages(&client, epub_data.spine.clone()).await?;
let toc_vec: Vec<TocNode> = fetch_direct_array(&client, &epub_data.table_of_contents).await?;
- // Find the OPF file entry to reference it in container.xml
- let opf_entry = file_entries
- .iter()
- .find(|f| f.filename_ext == ".opf" && f.media_type == "application/oebps-package+xml")
- .context("No OPF file with the correct MIME type was found.")?;
-
let dest_root = format!("Books/{}/epub_root", args.bookid);
let dest_root = Path::new(&dest_root);
download_all_files(&client, &file_entries, dest_root).await?;
- write_container_xml(dest_root, &opf_entry.full_path).await?;
// Sanity check: Every entry in spine exists in chapters.
let chapters: HashMap<String, Chapter> =