Skip to content

Commit 09e338b

Browse files
authored
Merge AssetOpener and AssetSniffer public APIs into AssetRetriever (#434)
1 parent 1ba1223 commit 09e338b

File tree

33 files changed

+463
-336
lines changed

33 files changed

+463
-336
lines changed

docs/guides/open-publication.md

+14-13
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44

55
Readium requires you to instantiate a few components before you can actually open a publication.
66

7-
## Constructing an `AssetOpener`
7+
## Constructing an `AssetRetriever`
88

99
First, you need to instantiate an `HttpClient` to provide the toolkit the ability to do HTTP requests.
1010
You can use the Readium `DefaultHttpClient` or a custom implementation. In the former case, its callback will
1111
enable you to perform authentication when required.
12-
Then, you can create an `AssetOpener` which will enable you to read content through different schemes and guessing its format.
13-
In addition to an `HttpClient`, the `AssetOpener` constructor takes a `ContentResolver` to support data access through the `content` scheme.
12+
Then, you can create an `AssetRetriever` which will enable you to read content through different schemes and guess its format.
13+
In addition to an `HttpClient`, the `AssetRetriever` constructor takes a `ContentResolver` to support data access through the `content` scheme.
1414

1515
```kotlin
1616
val httpClient = DefaultHttpClient()
1717

18-
val assetOpener = AssetOpener(context.contentResolver, httpClient)
18+
val assetRetriever = AssetRetriever(context.contentResolver, httpClient)
1919
```
2020

2121
## Constructing a `PublicationOpener`
2222

23-
The component which can parse an `Asset` giving access to a publication to build a proper `Publication`
23+
The component which can parse an `Asset` giving access to a publication to build a `Publication`
2424
object is the `PublicationOpener`. Its constructor requires you to pass in:
2525

2626
* a `PublicationParser` it delegates the parsing work to.
@@ -32,38 +32,39 @@ The easiest way to get a `PublicationParser` is to use the `DefaultPublicationPa
3232
```kotlin
3333
val contentProtections = listOf(lcpService.contentProtection(authentication))
3434

35-
val publicationParser = DefaultPublicationParser(context, httpClient, assetOpener, pdfFactory)
35+
val publicationParser = DefaultPublicationParser(context, httpClient, assetRetriever, pdfFactory)
3636

3737
val publicationOpener = PublicationOpener(publicationParser, contentProtections)
3838
```
3939

4040
## Bringing the pieces together
4141

42-
Once you have got an `AssetOpener` and a `PublicationOpener`, you can eventually open a publication as follows:
42+
Once you have got an `AssetRetriever` and a `PublicationOpener`, you can eventually open a publication as follows:
4343
```kotlin
44-
val asset = assetOpener.open(url, mediaType)
44+
val asset = assetRetriever.open(url, mediaType)
4545
.getOrElse { return error }
4646

4747
val publication = publicationOpener.open(asset)
4848
.getOrElse { return error }
4949
```
5050

51-
Persisting the asset media type on the device can significantly improve performance as it is valuable hint
51+
Persisting the asset media type on the device can significantly improve performance as it is strong hint
5252
for the content format, especially in case of remote publications.
5353

54-
## Extensibility`
54+
## Supporting additional formats or URL schemes
5555

5656
`DefaultPublicationParser` accepts additional parsers. You can also use your own parser list
5757
with `CompositePublicationParser` or implement [PublicationParser] in the way you like.
5858

59-
`AssetOpener` offers an alternative constructor providing better extensibility in a similar way.
59+
`AssetRetriever` offers an alternative constructor providing better extensibility in a similar way.
6060
This constructor takes several parameters with different responsibilities.
6161

6262
* `ResourceFactory` determines which schemes you will be able to access content through.
63-
* `ArchiveOpener` which kinds of archives your `AssetOpener` will be able to open.
64-
* `FormatSniffer` which file formats your `AssetOpener` will be able to identify.
63+
* `ArchiveOpener` which kinds of archives your `AssetRetriever` will be able to open.
64+
* `FormatSniffer` which file formats your `AssetRetriever` will be able to identify.
6565

6666
For each of these components, you can either use the default implementations or implement yours
6767
with the composite pattern. `CompositeResourceFactory`, `CompositeArchiveOpener` and `CompositeFormatSniffer`
6868
provide simple implementations trying every item of a list in turns.
6969

70+

readium/lcp/src/main/java/org/readium/r2/lcp/LcpContentProtection.kt

+8-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import org.readium.r2.shared.util.ThrowableError
1919
import org.readium.r2.shared.util.Try
2020
import org.readium.r2.shared.util.Url
2121
import org.readium.r2.shared.util.asset.Asset
22-
import org.readium.r2.shared.util.asset.AssetOpener
22+
import org.readium.r2.shared.util.asset.AssetRetriever
2323
import org.readium.r2.shared.util.asset.ContainerAsset
2424
import org.readium.r2.shared.util.asset.ResourceAsset
2525
import org.readium.r2.shared.util.data.Container
@@ -38,7 +38,7 @@ import org.readium.r2.shared.util.resource.TransformingContainer
3838
internal class LcpContentProtection(
3939
private val lcpService: LcpService,
4040
private val authentication: LcpAuthenticating,
41-
private val assetOpener: AssetOpener
41+
private val assetRetriever: AssetRetriever
4242
) : ContentProtection {
4343

4444
override suspend fun open(
@@ -191,14 +191,14 @@ internal class LcpContentProtection(
191191

192192
val asset =
193193
if (link.mediaType != null) {
194-
assetOpener.open(
194+
assetRetriever.retrieve(
195195
url,
196196
mediaType = link.mediaType
197197
)
198198
.map { it as ContainerAsset }
199199
.mapFailure { it.wrap() }
200200
} else {
201-
assetOpener.open(url)
201+
assetRetriever.retrieve(url)
202202
.mapFailure { it.wrap() }
203203
.flatMap {
204204
if (it is ContainerAsset) {
@@ -218,13 +218,13 @@ internal class LcpContentProtection(
218218
return asset.flatMap { createResultAsset(it, license) }
219219
}
220220

221-
private fun AssetOpener.OpenError.wrap(): ContentProtection.OpenError =
221+
private fun AssetRetriever.RetrieveUrlError.wrap(): ContentProtection.OpenError =
222222
when (this) {
223-
is AssetOpener.OpenError.FormatNotSupported ->
223+
is AssetRetriever.RetrieveUrlError.FormatNotSupported ->
224224
ContentProtection.OpenError.AssetNotSupported(this)
225-
is AssetOpener.OpenError.Reading ->
225+
is AssetRetriever.RetrieveUrlError.Reading ->
226226
ContentProtection.OpenError.Reading(cause)
227-
is AssetOpener.OpenError.SchemeNotSupported ->
227+
is AssetRetriever.RetrieveUrlError.SchemeNotSupported ->
228228
ContentProtection.OpenError.AssetNotSupported(this)
229229
}
230230
}

readium/lcp/src/main/java/org/readium/r2/lcp/LcpPublicationRetriever.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import org.readium.r2.shared.extensions.tryOrLog
1616
import org.readium.r2.shared.util.AbsoluteUrl
1717
import org.readium.r2.shared.util.ErrorException
1818
import org.readium.r2.shared.util.FileExtension
19-
import org.readium.r2.shared.util.asset.AssetSniffer
19+
import org.readium.r2.shared.util.asset.AssetRetriever
2020
import org.readium.r2.shared.util.downloads.DownloadManager
2121
import org.readium.r2.shared.util.format.EpubSpecification
2222
import org.readium.r2.shared.util.format.Format
@@ -33,7 +33,7 @@ import org.readium.r2.shared.util.mediatype.MediaType
3333
public class LcpPublicationRetriever(
3434
context: Context,
3535
private val downloadManager: DownloadManager,
36-
private val assetSniffer: AssetSniffer
36+
private val assetRetriever: AssetRetriever
3737
) {
3838

3939
@JvmInline
@@ -197,7 +197,7 @@ public class LcpPublicationRetriever(
197197
downloadsRepository.removeDownload(requestId.value)
198198

199199
val format =
200-
assetSniffer.sniff(
200+
assetRetriever.sniffFormat(
201201
download.file,
202202
FormatHints(
203203
mediaTypes = listOfNotNull(

readium/lcp/src/main/java/org/readium/r2/lcp/LcpService.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import org.readium.r2.lcp.service.PassphrasesService
2929
import org.readium.r2.shared.publication.protection.ContentProtection
3030
import org.readium.r2.shared.util.Try
3131
import org.readium.r2.shared.util.asset.Asset
32-
import org.readium.r2.shared.util.asset.AssetOpener
32+
import org.readium.r2.shared.util.asset.AssetRetriever
3333
import org.readium.r2.shared.util.downloads.DownloadManager
3434
import org.readium.r2.shared.util.format.Format
3535

@@ -174,7 +174,7 @@ public interface LcpService {
174174
*/
175175
public operator fun invoke(
176176
context: Context,
177-
assetOpener: AssetOpener,
177+
assetRetriever: AssetRetriever,
178178
downloadManager: DownloadManager
179179
): LcpService? {
180180
if (!LcpClient.isAvailable()) {
@@ -200,7 +200,7 @@ public interface LcpService {
200200
network = network,
201201
passphrases = passphrases,
202202
context = context,
203-
assetOpener = assetOpener,
203+
assetRetriever = assetRetriever,
204204
downloadManager = downloadManager
205205
)
206206
}

readium/lcp/src/main/java/org/readium/r2/lcp/service/LicensesService.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import org.readium.r2.lcp.license.model.LicenseDocument
3333
import org.readium.r2.shared.publication.protection.ContentProtection
3434
import org.readium.r2.shared.util.Try
3535
import org.readium.r2.shared.util.asset.Asset
36-
import org.readium.r2.shared.util.asset.AssetOpener
36+
import org.readium.r2.shared.util.asset.AssetRetriever
3737
import org.readium.r2.shared.util.downloads.DownloadManager
3838
import timber.log.Timber
3939

@@ -44,20 +44,20 @@ internal class LicensesService(
4444
private val network: NetworkService,
4545
private val passphrases: PassphrasesService,
4646
private val context: Context,
47-
private val assetOpener: AssetOpener,
47+
private val assetRetriever: AssetRetriever,
4848
private val downloadManager: DownloadManager
4949
) : LcpService, CoroutineScope by MainScope() {
5050

5151
override fun contentProtection(
5252
authentication: LcpAuthenticating
5353
): ContentProtection =
54-
LcpContentProtection(this, authentication, assetOpener)
54+
LcpContentProtection(this, authentication, assetRetriever)
5555

5656
override fun publicationRetriever(): LcpPublicationRetriever {
5757
return LcpPublicationRetriever(
5858
context,
5959
downloadManager,
60-
assetOpener.assetSniffer
60+
assetRetriever
6161
)
6262
}
6363

readium/shared/src/main/java/org/readium/r2/shared/util/asset/ArchiveOpener.kt renamed to readium/shared/src/main/java/org/readium/r2/shared/util/archive/ArchiveOpener.kt

+26-9
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
* available in the top-level LICENSE file of the project.
55
*/
66

7-
package org.readium.r2.shared.util.asset
7+
package org.readium.r2.shared.util.archive
88

9+
import org.readium.r2.shared.util.Error
910
import org.readium.r2.shared.util.Try
11+
import org.readium.r2.shared.util.asset.ContainerAsset
1012
import org.readium.r2.shared.util.data.Container
13+
import org.readium.r2.shared.util.data.ReadError
1114
import org.readium.r2.shared.util.data.Readable
1215
import org.readium.r2.shared.util.format.Format
1316
import org.readium.r2.shared.util.getOrElse
@@ -20,19 +23,31 @@ public interface ArchiveOpener {
2023

2124
public sealed class OpenError(
2225
override val message: String,
23-
override val cause: org.readium.r2.shared.util.Error?
24-
) : org.readium.r2.shared.util.Error {
26+
override val cause: Error?
27+
) : Error {
2528

2629
public class FormatNotSupported(
2730
public val format: Format,
28-
cause: org.readium.r2.shared.util.Error? = null
31+
cause: Error? = null
2932
) : OpenError("Format not supported.", cause)
3033

3134
public class Reading(
32-
override val cause: org.readium.r2.shared.util.data.ReadError
35+
override val cause: ReadError
3336
) : OpenError("An error occurred while attempting to read the resource.", cause)
3437
}
3538

39+
public sealed class SniffOpenError(
40+
override val message: String,
41+
override val cause: Error?
42+
) : Error {
43+
44+
public data object NotRecognized :
45+
SniffOpenError("Format of resource could not be inferred.", null)
46+
47+
public data class Reading(override val cause: ReadError) :
48+
SniffOpenError("An error occurred while trying to read content.", cause)
49+
}
50+
3651
/**
3752
* Creates a new [Container] to access the entries of an archive with a known format.
3853
*/
@@ -46,7 +61,7 @@ public interface ArchiveOpener {
4661
*/
4762
public suspend fun sniffOpen(
4863
source: Readable
49-
): Try<ContainerAsset, SniffError>
64+
): Try<ContainerAsset, SniffOpenError>
5065
}
5166

5267
/**
@@ -78,18 +93,20 @@ public class CompositeArchiveOpener(
7893
return Try.failure(ArchiveOpener.OpenError.FormatNotSupported(format))
7994
}
8095

81-
override suspend fun sniffOpen(source: Readable): Try<ContainerAsset, SniffError> {
96+
override suspend fun sniffOpen(
97+
source: Readable
98+
): Try<ContainerAsset, ArchiveOpener.SniffOpenError> {
8299
for (factory in openers) {
83100
factory.sniffOpen(source)
84101
.getOrElse { error ->
85102
when (error) {
86-
is SniffError.NotRecognized -> null
103+
is ArchiveOpener.SniffOpenError.NotRecognized -> null
87104
else -> return Try.failure(error)
88105
}
89106
}
90107
?.let { return Try.success(it) }
91108
}
92109

93-
return Try.failure(SniffError.NotRecognized)
110+
return Try.failure(ArchiveOpener.SniffOpenError.NotRecognized)
94111
}
95112
}

readium/shared/src/main/java/org/readium/r2/shared/util/resource/ArchiveProperties.kt renamed to readium/shared/src/main/java/org/readium/r2/shared/util/archive/ArchiveProperties.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
* available in the top-level LICENSE file of the project.
55
*/
66

7-
package org.readium.r2.shared.util.resource
7+
package org.readium.r2.shared.util.archive
88

99
import org.json.JSONObject
1010
import org.readium.r2.shared.JSONable
1111
import org.readium.r2.shared.extensions.optNullableBoolean
1212
import org.readium.r2.shared.extensions.optNullableLong
1313
import org.readium.r2.shared.extensions.toMap
14+
import org.readium.r2.shared.util.resource.Resource
1415

1516
/**
1617
* Holds information about how the resource is stored in the archive.

readium/shared/src/main/java/org/readium/r2/shared/util/asset/Asset.kt

+2-6
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,20 @@
66

77
package org.readium.r2.shared.util.asset
88

9+
import org.readium.r2.shared.util.SuspendingCloseable
910
import org.readium.r2.shared.util.data.Container
1011
import org.readium.r2.shared.util.format.Format
1112
import org.readium.r2.shared.util.resource.Resource
1213

1314
/**
1415
* An asset which is either a single resource or a container that holds multiple resources.
1516
*/
16-
public sealed class Asset {
17+
public sealed class Asset : SuspendingCloseable {
1718

1819
/**
1920
* Format of the asset.
2021
*/
2122
public abstract val format: Format
22-
23-
/**
24-
* Releases in-memory resources related to this asset.
25-
*/
26-
public abstract suspend fun close()
2723
}
2824

2925
/**

0 commit comments

Comments
 (0)