Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #69 from readium/fixes/webpub
Browse files Browse the repository at this point in the history
Add support for remote Web Publication
  • Loading branch information
aferditamuriqi authored Jul 11, 2019
2 parents ab696e4 + 7bc7a98 commit 585687f
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 9 deletions.
8 changes: 8 additions & 0 deletions r2-shared-swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
CA2AE322221C1DCB008BD18F /* Loggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2AE31F221C1DCB008BD18F /* Loggable.swift */; };
CA40C07A21FF25F80069A50E /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA40C07921FF25F80069A50E /* JSON.swift */; };
CA50B86E22B2A1CF003AFF24 /* R2LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA50B86D22B2A1CF003AFF24 /* R2LocalizedString.swift */; };
CA94291622BCD08C00305CDB /* ResourcesServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA94291522BCD08B00305CDB /* ResourcesServer.swift */; };
CA9A40D1221B0AA200531EA1 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9A40D0221B0AA200531EA1 /* Either.swift */; };
CA9E6BA12239823300ECF6E4 /* WP+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9E6BA02239823300ECF6E4 /* WP+Deprecated.swift */; };
CA9E6BA4223A657900ECF6E4 /* JSONEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9E6BA3223A657900ECF6E4 /* JSONEquatable.swift */; };
Expand Down Expand Up @@ -67,6 +68,7 @@
CACD75F32236B0A5004F20CA /* PublicationCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CACD75F22236B0A5004F20CA /* PublicationCollection.swift */; };
CACD75F52236B2AF004F20CA /* PublicationCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CACD75F42236B2AF004F20CA /* PublicationCollectionTests.swift */; };
CAD178AD22B3A75C004E6812 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CAD178AF22B3A75C004E6812 /* Localizable.strings */; };
CADD69E222C3B17500A4CADF /* DocumentTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADD69E122C3B17500A4CADF /* DocumentTypes.swift */; };
CAF4EAE42237ABC700A17DA1 /* MediaOverlayNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E7D4041F4EC69100DF166D /* MediaOverlayNode.swift */; };
CAF4EAE52237ABC700A17DA1 /* MediaOverlays.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E7D4051F4EC69100DF166D /* MediaOverlays.swift */; };
CAF4EAE72237AD6000A17DA1 /* UserProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEF39DF220E3895200A560F3 /* UserProperties.swift */; };
Expand Down Expand Up @@ -134,6 +136,7 @@
CA50B86D22B2A1CF003AFF24 /* R2LocalizedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = R2LocalizedString.swift; sourceTree = "<group>"; };
CA6161EA21FB257700D2CFE3 /* r2-shared-swiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "r2-shared-swiftTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
CA6161EE21FB257700D2CFE3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CA94291522BCD08B00305CDB /* ResourcesServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcesServer.swift; sourceTree = "<group>"; };
CA9A40D0221B0AA200531EA1 /* Either.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = "<group>"; };
CA9E6BA02239823300ECF6E4 /* WP+Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WP+Deprecated.swift"; sourceTree = "<group>"; };
CA9E6BA3223A657900ECF6E4 /* JSONEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONEquatable.swift; sourceTree = "<group>"; };
Expand All @@ -158,6 +161,7 @@
CACD75F22236B0A5004F20CA /* PublicationCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicationCollection.swift; sourceTree = "<group>"; };
CACD75F42236B2AF004F20CA /* PublicationCollectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicationCollectionTests.swift; sourceTree = "<group>"; };
CAD178AE22B3A75C004E6812 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
CADD69E122C3B17500A4CADF /* DocumentTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTypes.swift; sourceTree = "<group>"; };
F3B1879F1FA33D4D00BB46BF /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
F3B187A11FA33DFA00BB46BF /* Facet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Facet.swift; sourceTree = "<group>"; };
F3B187A31FA33E1D00BB46BF /* OpdsMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpdsMetadata.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -200,6 +204,8 @@
57470F7D20ED0D1A000CDCA3 /* DownloadSession.swift */,
CA9E6BA3223A657900ECF6E4 /* JSONEquatable.swift */,
CA50B86D22B2A1CF003AFF24 /* R2LocalizedString.swift */,
CA94291522BCD08B00305CDB /* ResourcesServer.swift */,
CADD69E122C3B17500A4CADF /* DocumentTypes.swift */,
);
path = Toolkit;
sourceTree = "<group>";
Expand Down Expand Up @@ -611,6 +617,7 @@
CABBB2F82237C99E004EB039 /* OPDSAcquisition.swift in Sources */,
CAF4EAE72237AD6000A17DA1 /* UserProperties.swift in Sources */,
CA176387223A898B00959FEB /* EPUBProperties.swift in Sources */,
CA94291622BCD08C00305CDB /* ResourcesServer.swift in Sources */,
CA16A8702232F7CD00E66255 /* Link.swift in Sources */,
CA176389223A8D0300959FEB /* EPUBMetadata.swift in Sources */,
F3E7D4191F4EC69100DF166D /* RootFile.swift in Sources */,
Expand Down Expand Up @@ -643,6 +650,7 @@
CAC34F0A221C61BD002C452E /* DRM.swift in Sources */,
CABBB2FC22394450004EB039 /* ContentLayout.swift in Sources */,
CABBB2F62237BF61004EB039 /* OPDSPrice.swift in Sources */,
CADD69E222C3B17500A4CADF /* DocumentTypes.swift in Sources */,
CA16A8762233065F00E66255 /* Publication+JSON.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
31 changes: 22 additions & 9 deletions r2-shared-swift/Publication/Publication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,25 @@ public class Publication: WebPublication, Loggable {

/// Generates an URL to a publication's `Link`.
public func url(to link: Link?) -> URL? {
guard let link = link, let baseURL = self.baseURL else {
guard let link = link else {
return nil
}

// Remove trailing "/" before appending the href (href are absolute, hence starting with a "/", but relative to the publication).
let trimmedBaseURLString = baseURL.absoluteString.trimmingCharacters(in: ["/"])

return URL(string: trimmedBaseURLString)?
.appendingPathComponent(link.href)
if let url = URL(string: link.href), url.scheme != nil {
return url
} else {
var href = link.href
if href.hasPrefix("/") {
href = String(href.dropFirst())
}
return baseURL.map { $0.appendingPathComponent(href) }
}
}


public enum Format: Equatable, Hashable {
/// Formats natively supported by Readium.
case cbz, epub, pdf
case cbz, epub, pdf, webpub
/// Default value when the format is not specified.
case unknown

Expand All @@ -149,7 +153,7 @@ public class Publication: WebPublication, Loggable {
}
self.init(mimetypes: [mimetype])
}

/// Finds the format from a list of possible mimetypes or fallback on a file extension.
public init(mimetypes: [String] = [], fileExtension: String? = nil) {
self = {
Expand All @@ -161,6 +165,8 @@ public class Publication: WebPublication, Loggable {
return .cbz
case "application/pdf", "application/pdf+lcp":
return .pdf
case "application/webpub+json", "application/audiobook+json":
return .webpub
default:
break
}
Expand All @@ -173,12 +179,14 @@ public class Publication: WebPublication, Loggable {
return .cbz
case "pdf", "lcpdf":
return .pdf
case "json":
return .webpub
default:
return .unknown
}
}()
}

/// Finds the format of the publication at the given url.
/// Uses the format declared as exported UTIs in the app's Info.plist, or fallbacks on the file extension.
///
Expand Down Expand Up @@ -207,6 +215,11 @@ public class Publication: WebPublication, Loggable {
fileExtension: file.pathExtension
)
}

@available(*, deprecated, renamed: "init(file:)")
public init(url: URL) {
self.init(file: url)
}

}

Expand Down
56 changes: 56 additions & 0 deletions r2-shared-swift/Toolkit/DocumentTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// DocumentTypes.swift
// r2-shared-swift
//
// Created by Mickaël Menu on 26.06.19.
//
// Copyright 2019 Readium Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style license which is detailed
// in the LICENSE file present in the project repository where this source code is maintained.
//

import CoreServices
import Foundation

public final class DocumentTypes {

// Extracts supported Document Types from the main bundle's Info.plist.
private static let types = (Bundle.main.object(forInfoDictionaryKey: "CFBundleDocumentTypes") as? [[String: Any]] ?? [])

/// Supported UTI.
public static let utis: [String] = types
.flatMap { $0["LSItemContentTypes"] as? [String] ?? [] }

/// Supported document content types.
public static let contentTypes: [String] = utis
.compactMap { UTTypeCopyPreferredTagWithClass($0 as CFString, kUTTagClassMIMEType)?.takeRetainedValue() as String? }

/// Supported document extensions.
public static let extensions: [String] = types
.flatMap { $0["CFBundleTypeExtensions"] as? [String] ?? [] }
.map { $0.lowercased() }

/// Returns the content type for the given URL.
public static func contentType(for url: URL?) -> String? {
return contentType(forExtension: url?.pathExtension)
}

/// Returns the content type for the given document extension.
public static func contentType(forExtension ext: String?) -> String? {
guard let ext = ext else {
return nil
}
return (UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext as CFString, nil)?.takeUnretainedValue())
.flatMap { UTTypeCopyPreferredTagWithClass($0, kUTTagClassMIMEType)?.takeRetainedValue() as String? }
}

/// Returns the document extension for given content type.
public static func `extension`(forContentType contentType: String?) -> String? {
guard let contentType = contentType else {
return nil
}
return (UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, contentType as CFString, nil)?.takeUnretainedValue())
.flatMap { UTTypeCopyPreferredTagWithClass($0, kUTTagClassFilenameExtension)?.takeRetainedValue() as String? }
}

}
30 changes: 30 additions & 0 deletions r2-shared-swift/Toolkit/ResourcesServer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ResourcesServer.swift
// r2-shared-swift
//
// Created by Mickaël Menu on 21.06.19.
//
// Copyright 2019 Readium Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style license which is detailed
// in the LICENSE file present in the project repository where this source code is maintained.
//

import Foundation

public enum ResourcesServerError: Error {
case fileNotFound
case invalidPath
case serverFailure
}

public protocol ResourcesServer {

/// Serves the local file URL at the given absolute path on the server.
/// If the given URL is a directory, then all the files in the directory are served.
/// Subsequent calls with the same served path overwrite each other.
///
/// Returns: The URL to access the file on the server.
@discardableResult
func serve(_ url: URL, at path: String) throws -> URL

}

0 comments on commit 585687f

Please sign in to comment.