Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
476a3e6
Refactor media types
mickael-menu Aug 18, 2023
2cfc2e7
Implement the media type in resources thanks to the format registry
mickael-menu Aug 18, 2023
de0a9e1
Optimization
mickael-menu Aug 18, 2023
14d8e02
Remove legacy code
mickael-menu Aug 18, 2023
c27c939
Fix reading remote web publication
mickael-menu Aug 20, 2023
bf320f3
Fix database naming convention in the test app
mickael-menu Aug 20, 2023
a67f52d
Move multi rounds sniffing strategy to `OptimizedRoundsMediaTypeSniffer`
mickael-menu Aug 21, 2023
1fdf5d3
Remove Format from Asset
mickael-menu Aug 21, 2023
af68130
Restore default HTTP client in the OPDS parsers
mickael-menu Aug 21, 2023
3a54463
Simplify `Format`
mickael-menu Aug 21, 2023
d065398
Make `Resource.mediaType` non nullable
mickael-menu Aug 21, 2023
05a7017
`MediaTypeSnifferContext` is not closeable
mickael-menu Aug 21, 2023
595add0
Fix tests
mickael-menu Aug 21, 2023
05a09bd
Make system and content URI sniffing generic
mickael-menu Aug 21, 2023
26eec03
Fix lint
mickael-menu Aug 21, 2023
49f6cbc
Simplify `FormatRegistry`
mickael-menu Aug 21, 2023
25bc2d3
Make sure we always return canonical media types, and separate light …
mickael-menu Aug 21, 2023
1c8d360
Address review comments
mickael-menu Aug 21, 2023
866b93b
Fix `ParserAssetFactory`
mickael-menu Aug 21, 2023
b516c69
Reintroduce `MediaTypeRetriever`
mickael-menu Aug 22, 2023
9afa5ef
Remove `ZipContainer`
mickael-menu Aug 22, 2023
0c2fce1
Fix tests
mickael-menu Aug 22, 2023
be26f9f
Fix `AssetRetriever`
mickael-menu Aug 22, 2023
801266d
Add documentation
mickael-menu Aug 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ lint/reports/
docs/readium
docs/index.md
docs/package-list
site/
site/
androidTestResultsUserPreferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ package org.readium.r2.lcp
import org.readium.r2.lcp.auth.LcpPassphraseAuthentication
import org.readium.r2.lcp.license.model.LicenseDocument
import org.readium.r2.shared.asset.Asset
import org.readium.r2.shared.asset.AssetRetriever
import org.readium.r2.shared.asset.AssetType
import org.readium.r2.shared.error.ThrowableError
import org.readium.r2.shared.error.Try
import org.readium.r2.shared.error.flatMap
import org.readium.r2.shared.error.getOrElse
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.encryption.encryption
Expand All @@ -27,8 +30,7 @@ import org.readium.r2.shared.util.toFile
internal class LcpContentProtection(
private val lcpService: LcpService,
private val authentication: LcpAuthenticating,
private val resourceFactory: ResourceFactory,
private val archiveFactory: ArchiveFactory
private val assetRetriever: AssetRetriever
) : ContentProtection {

override val scheme: ContentProtection.Scheme =
Expand Down Expand Up @@ -152,15 +154,13 @@ internal class LcpContentProtection(
)
)

val resource = resourceFactory.create(url)
.getOrElse { return Try.failure(it.wrap()) }

val container = archiveFactory.create(resource, password = null)
.getOrElse { return Try.failure(it.wrap()) }

val publicationAsset = Asset.Container(link.mediaType, exploded = false, container)

return createResultAsset(publicationAsset, license)
return assetRetriever.retrieve(
url,
mediaType = link.mediaType,
assetType = AssetType.Archive
)
.mapFailure { Publication.OpeningException.ParsingFailed(it) }
.flatMap { createResultAsset(it as Asset.Container, license) }
}

private fun ResourceFactory.Error.wrap(): Publication.OpeningException =
Expand Down
26 changes: 11 additions & 15 deletions readium/lcp/src/main/java/org/readium/r2/lcp/LcpService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ import org.readium.r2.lcp.service.NetworkService
import org.readium.r2.lcp.service.PassphrasesRepository
import org.readium.r2.lcp.service.PassphrasesService
import org.readium.r2.shared.asset.Asset
import org.readium.r2.shared.asset.AssetRetriever
import org.readium.r2.shared.error.Try
import org.readium.r2.shared.publication.protection.ContentProtection
import org.readium.r2.shared.resource.ArchiveFactory
import org.readium.r2.shared.resource.DefaultArchiveFactory
import org.readium.r2.shared.resource.FileResourceFactory
import org.readium.r2.shared.resource.ResourceFactory
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.mediatype.MediaTypeRetriever
import org.readium.r2.shared.util.mediatype.MediaTypeSniffer

/**
* Service used to acquire and open publications protected with LCP.
Expand Down Expand Up @@ -159,9 +156,8 @@ public interface LcpService {
*/
public operator fun invoke(
context: Context,
mediaTypeRetriever: MediaTypeRetriever = MediaTypeRetriever(),
resourceFactory: ResourceFactory = FileResourceFactory(),
archiveFactory: ArchiveFactory = DefaultArchiveFactory()
assetRetriever: AssetRetriever,
mediaTypeSniffer: MediaTypeSniffer
): LcpService? {
if (!LcpClient.isAvailable()) {
return null
Expand All @@ -171,7 +167,7 @@ public interface LcpService {
val deviceRepository = DeviceRepository(db)
val passphraseRepository = PassphrasesRepository(db)
val licenseRepository = LicensesRepository(db)
val network = NetworkService(mediaTypeRetriever = mediaTypeRetriever)
val network = NetworkService(mediaTypeSniffer)
val device = DeviceService(
repository = deviceRepository,
network = network,
Expand All @@ -186,18 +182,17 @@ public interface LcpService {
network = network,
passphrases = passphrases,
context = context,
mediaTypeRetriever = mediaTypeRetriever,
resourceFactory = resourceFactory,
archiveFactory = archiveFactory
assetRetriever = assetRetriever
)
}

@Suppress("UNUSED_PARAMETER")
@Deprecated(
"Use `LcpService()` instead",
ReplaceWith("LcpService(context)"),
ReplaceWith("LcpService(context, AssetRetriever(), DefaultMediaTypeSniffer())"),
level = DeprecationLevel.ERROR
)
public fun create(context: Context): LcpService? = invoke(context)
public fun create(context: Context): LcpService? = throw NotImplementedError()
}

@Deprecated(
Expand Down Expand Up @@ -235,13 +230,14 @@ public interface LcpService {
}
}

@Suppress("UNUSED_PARAMETER")
@Deprecated(
"Renamed to `LcpService()`",
replaceWith = ReplaceWith("LcpService(context)"),
level = DeprecationLevel.ERROR
)
public fun R2MakeLCPService(context: Context): LcpService =
LcpService(context) ?: throw Exception("liblcp is missing on the classpath")
throw NotImplementedError()

@Deprecated(
"Renamed to `LcpService.AcquiredPublication`",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public data class Link(val json: JSONObject) {
get() = url(parameters = emptyMap())

val mediaType: MediaType
get() = type?.let { MediaType.parse(it) } ?: MediaType.BINARY
get() = type?.let { MediaType(it) } ?: MediaType.BINARY

/**
* List of URI template parameter keys, if the [Link] is templated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,6 @@ public typealias LCPError = LcpException
ReplaceWith("LcpService()"),
level = DeprecationLevel.ERROR
)
@Suppress("UNUSED_PARAMETER")
public fun R2MakeLCPService(context: Context): LcpService? =
LcpService(context)
throw NotImplementedError()
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ import org.readium.r2.lcp.license.container.LicenseContainer
import org.readium.r2.lcp.license.container.createLicenseContainer
import org.readium.r2.lcp.license.model.LicenseDocument
import org.readium.r2.shared.asset.Asset
import org.readium.r2.shared.asset.AssetRetriever
import org.readium.r2.shared.error.Try
import org.readium.r2.shared.extensions.tryOr
import org.readium.r2.shared.publication.protection.ContentProtection
import org.readium.r2.shared.resource.ArchiveFactory
import org.readium.r2.shared.resource.ResourceFactory
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.mediatype.MediaTypeRetriever
import timber.log.Timber

internal class LicensesService(
Expand All @@ -46,17 +44,13 @@ internal class LicensesService(
private val network: NetworkService,
private val passphrases: PassphrasesService,
private val context: Context,
private val mediaTypeRetriever: MediaTypeRetriever,
private val resourceFactory: ResourceFactory,
private val archiveFactory: ArchiveFactory
private val assetRetriever: AssetRetriever
) : LcpService, CoroutineScope by MainScope() {

override suspend fun isLcpProtected(file: File): Boolean =
tryOr(false) {
val mediaType = mediaTypeRetriever.retrieve(file) ?: return false
createLicenseContainer(file, mediaType).read()
true
}
override suspend fun isLcpProtected(file: File): Boolean {
val asset = assetRetriever.retrieve(file) ?: return false
return isLcpProtected(asset)
}

override suspend fun isLcpProtected(asset: Asset): Boolean =
tryOr(false) {
Expand All @@ -73,7 +67,7 @@ internal class LicensesService(
override fun contentProtection(
authentication: LcpAuthenticating
): ContentProtection =
LcpContentProtection(this, authentication, resourceFactory, archiveFactory)
LcpContentProtection(this, authentication, assetRetriever)

override suspend fun acquirePublication(lcpl: ByteArray, onProgress: (Double) -> Unit): Try<LcpService.AcquiredPublication, LcpException> =
try {
Expand Down Expand Up @@ -251,9 +245,7 @@ internal class LicensesService(
destination,
mediaType = link.type,
onProgress = onProgress
)
?: mediaTypeRetriever.retrieve(mediaType = link.type)
?: MediaType.EPUB
) ?: link.mediaType

// Saves the License Document into the downloaded publication
val container = createLicenseContainer(destination, mediaType)
Expand All @@ -266,4 +258,14 @@ internal class LicensesService(
licenseDocument = license
)
}

private val MediaType.fileExtension: String get() =
when {
matches(MediaType.DIVINA) -> "divina"
matches(MediaType.EPUB) -> "epub"
matches(MediaType.LCP_PROTECTED_PDF) -> "pdf"
matches(MediaType.READIUM_AUDIOBOOK) -> "audiobook"
matches(MediaType.READIUM_WEBPUB) -> "webpub"
else -> "epub"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.readium.r2.lcp.LcpException
import org.readium.r2.shared.error.Try
import org.readium.r2.shared.util.http.retrieve
import org.readium.r2.shared.format.FormatHints
import org.readium.r2.shared.util.http.invoke
import org.readium.r2.shared.util.mediatype.HintMediaTypeSnifferContext
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.mediatype.MediaTypeRetriever
import org.readium.r2.shared.util.mediatype.MediaTypeSniffer
import timber.log.Timber

internal typealias URLParameters = Map<String, String?>
Expand All @@ -34,7 +36,7 @@ internal class NetworkException(val status: Int?, cause: Throwable? = null) : Ex
)

internal class NetworkService(
private val mediaTypeRetriever: MediaTypeRetriever
private val mediaTypeSniffer: MediaTypeSniffer
) {
enum class Method(val value: String) {
GET("GET"), POST("POST"), PUT("PUT");
Expand Down Expand Up @@ -138,9 +140,10 @@ internal class NetworkService(
}
}

mediaTypeRetriever.retrieve(
connection = connection,
mediaType = mediaType
mediaTypeSniffer.sniff(
HintMediaTypeSnifferContext(
hints = FormatHints(connection, mediaType = mediaType)
)
)
} catch (e: Exception) {
Timber.e(e)
Expand Down
9 changes: 4 additions & 5 deletions readium/opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import org.readium.r2.shared.parser.xml.XmlParser
import org.readium.r2.shared.publication.*
import org.readium.r2.shared.toJSON
import org.readium.r2.shared.util.Href
import org.readium.r2.shared.util.http.DefaultHttpClient
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.http.HttpRequest
import org.readium.r2.shared.util.http.fetchWithDecoder
Expand All @@ -46,15 +45,15 @@ public object Namespaces {
public class OPDS1Parser {
public companion object {

public suspend fun parseUrlString(url: String, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
public suspend fun parseUrlString(url: String, client: HttpClient): Try<ParseData, Exception> {
return client.fetchWithDecoder(HttpRequest(url)) {
this.parse(it.body, URL(url))
}
}

public suspend fun parseRequest(
request: HttpRequest,
client: HttpClient = DefaultHttpClient()
client: HttpClient
): Try<ParseData, Exception> {
return client.fetchWithDecoder(request) {
this.parse(it.body, URL(request.url))
Expand Down Expand Up @@ -186,7 +185,7 @@ public class OPDS1Parser {
}

@Suppress("unused")
public suspend fun retrieveOpenSearchTemplate(feed: Feed): Try<String?, Exception> {
public suspend fun retrieveOpenSearchTemplate(feed: Feed, client: HttpClient): Try<String?, Exception> {
var openSearchURL: URL? = null
var selfMimeType: String? = null

Expand All @@ -204,7 +203,7 @@ public class OPDS1Parser {
return@let it
}

return DefaultHttpClient().fetchWithDecoder(HttpRequest(unwrappedURL.toString())) {
return client.fetchWithDecoder(HttpRequest(unwrappedURL.toString())) {
val document = XmlParser().parse(it.body.inputStream())

val urls = document.get("Url", Namespaces.Search)
Expand Down
5 changes: 2 additions & 3 deletions readium/opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.Manifest
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.Href
import org.readium.r2.shared.util.http.DefaultHttpClient
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.http.HttpRequest
import org.readium.r2.shared.util.http.fetchWithDecoder
Expand All @@ -39,15 +38,15 @@ public class OPDS2Parser {

private lateinit var feed: Feed

public suspend fun parseUrlString(url: String, client: HttpClient = DefaultHttpClient()): Try<ParseData, Exception> {
public suspend fun parseUrlString(url: String, client: HttpClient): Try<ParseData, Exception> {
return client.fetchWithDecoder(HttpRequest(url)) {
this.parse(it.body, URL(url))
}
}

public suspend fun parseRequest(
request: HttpRequest,
client: HttpClient = DefaultHttpClient()
client: HttpClient
): Try<ParseData, Exception> {
return client.fetchWithDecoder(request) {
this.parse(it.body, URL(request.url))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package org.readium.r2.shared.asset

import org.readium.r2.shared.resource.Resource as SharedResource
import org.readium.r2.shared.util.mediatype.MediaType

/**
Expand All @@ -14,14 +15,14 @@ import org.readium.r2.shared.util.mediatype.MediaType
public sealed class Asset {

/**
* Media type of the asset.
* Type of the asset source.
*/
public abstract val mediaType: MediaType
public abstract val assetType: AssetType

/**
* Type of the asset source.
* Media type of the asset.
*/
public abstract val assetType: AssetType
public abstract val mediaType: MediaType

/**
* Releases in-memory resources related to this asset.
Expand All @@ -36,7 +37,7 @@ public sealed class Asset {
*/
public class Resource(
override val mediaType: MediaType,
public val resource: org.readium.r2.shared.resource.Resource
public val resource: SharedResource
) : Asset() {

override val assetType: AssetType =
Expand Down
Loading