Skip to content

Commit

Permalink
Add the ReadiumAdapterMinizip library (#548)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu authored Jan 28, 2025
1 parent 8d9844f commit d805e4e
Show file tree
Hide file tree
Showing 18 changed files with 688 additions and 108 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ All notable changes to this project will be documented in this file. Take a look
#### Shared

* Support for streaming ZIP packages over HTTP. This lets you open a remote EPUB, audiobook, or any other ZIP-based publication without needing to download it first.
* A new `ReadiumAdapterMinizip` library ships the old `ArchiveOpener` using Minizip. Compared to the newer default `ZIPArchiveOpener`, it has the following differences:
* It does not support HTTP streaming of ZIP packages.
* It offers better performance for LCP-protected publications containing large resources that are `deflated` instead of `stored` in the archive, which is not recommended.

### Deprecated

Expand Down
19 changes: 19 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ let package = Package(
// Adapters to third-party dependencies.
.library(name: "ReadiumAdapterGCDWebServer", targets: ["ReadiumAdapterGCDWebServer"]),
.library(name: "ReadiumAdapterLCPSQLite", targets: ["ReadiumAdapterLCPSQLite"]),
.library(name: "ReadiumAdapterMinizip", targets: ["ReadiumAdapterMinizip"]),
],
dependencies: [
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.0"),
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.0"),
.package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.3.0"),
.package(url: "https://github.com/readium/Fuzi.git", from: "4.0.0"),
.package(url: "https://github.com/readium/GCDWebServer.git", from: "4.0.0"),
Expand Down Expand Up @@ -157,6 +159,23 @@ let package = Package(
path: "Sources/Adapters/LCPSQLite"
),

.target(
name: "ReadiumAdapterMinizip",
dependencies: [
"ReadiumShared",
"Zip",
],
path: "Sources/Adapters/Minizip"
),
.testTarget(
name: "ReadiumAdapterMinizipTests",
dependencies: ["ReadiumAdapterMinizip"],
path: "Tests/Adapters/MinizipTests",
resources: [
.copy("Fixtures"),
]
),

.target(
name: "ReadiumInternal",
path: "Sources/Internal"
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,18 @@ Note that Carthage will build all Readium modules and their dependencies, but yo

Refer to the following table to know which dependencies are required for each Readium library.

| | `ReadiumShared` | `ReadiumStreamer` | `ReadiumNavigator` | `ReadiumOPDS` | `ReadiumLCP` | `ReadiumAdapterGCDWebServer` | `ReadiumAdapterLCPSQLite` |
|------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|------------------------------|---------------------------|
| **`ReadiumShared`** | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| **`ReadiumInternal`** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
| `CryptoSwift` | | :heavy_check_mark: | | | :heavy_check_mark: | | |
| `DifferenceKit` | | | :heavy_check_mark: | | | | |
| `ReadiumFuzi` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
| `ReadiumGCDWebServer` | | | | | | :heavy_check_mark: | |
| `ReadiumZIPFoundation` | :heavy_check_mark: | | | | :heavy_check_mark: | | |
| `SQLite.swift` | | | | | | | :heavy_check_mark: |
| `SwiftSoup` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
| | `ReadiumShared` | `ReadiumStreamer` | `ReadiumNavigator` | `ReadiumOPDS` | `ReadiumLCP` | `ReadiumAdapterGCDWebServer` | `ReadiumAdapterLCPSQLite` | `ReadiumAdapterMinizip` |
|------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|------------------------------|---------------------------|-------------------------|
| **`ReadiumShared`** | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| **`ReadiumInternal`** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |
| `CryptoSwift` | | :heavy_check_mark: | | | :heavy_check_mark: | | | |
| `DifferenceKit` | | | :heavy_check_mark: | | | | | |
| `ReadiumFuzi` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |
| `ReadiumGCDWebServer` | | | | | | :heavy_check_mark: | | |
| `ReadiumZIPFoundation` | :heavy_check_mark: | | | | :heavy_check_mark: | | | |
| `Minizip` | | | | | | | | :heavy_check_mark: |
| `SQLite.swift` | | | | | | | :heavy_check_mark: | |
| `SwiftSoup` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |

### CocoaPods

Expand Down
66 changes: 66 additions & 0 deletions Sources/Adapters/Minizip/MinizipArchiveOpener.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// Copyright 2025 Readium Foundation. All rights reserved.
// Use of this source code is governed by the BSD-style license
// available in the top-level LICENSE file of the project.
//

import Foundation
import ReadiumShared

/// An ``ArchiveOpener`` able to open ZIP archives using Minizip.
///
/// Compared to the default ZIPFoundation implementation shipped with
/// ReadiumShared, the ``MinizipArchiveOpener``:
///
/// - Does not support HTTP streaming of ZIP archives.
/// - Has better performance when reading an LCP-protected package containing
/// large deflated ZIP entries (instead of stored).
public final class MinizipArchiveOpener: ArchiveOpener {
public init() {}

public func open(resource: any Resource, format: Format) async -> Result<ContainerAsset, ArchiveOpenError> {
guard
format.conformsTo(.zip),
let file = resource.sourceURL?.fileURL
else {
return .failure(.formatNotSupported(format))
}

return await MinizipContainer.make(file: file)
.mapError {
switch $0 {
case .notAZIP:
return .formatNotSupported(format)
case let .reading(error):
return .reading(error)
}
}
.map { ContainerAsset(container: $0, format: format) }
}

public func sniffOpen(resource: any Resource) async -> Result<ContainerAsset, ArchiveSniffOpenError> {
guard let file = resource.sourceURL?.fileURL else {
return .failure(.formatNotRecognized)
}

return await MinizipContainer.make(file: file)
.mapError {
switch $0 {
case .notAZIP:
return .formatNotRecognized
case let .reading(error):
return .reading(error)
}
}
.map {
ContainerAsset(
container: $0,
format: Format(
specifications: .zip,
mediaType: .zip,
fileExtension: "zip"
)
)
}
}
}
Loading

0 comments on commit d805e4e

Please sign in to comment.