private val elementCache: Cache<String, LinkedElement> = CacheBuilder.newBuilder().build()
private val callback = object: FreenetInterface.BackgroundFetchCallback {
override fun shouldCancel(uri: FreenetURI, mimeType: String, size: Long): Boolean {
+ if (mimeType.startsWith("audio/")) {
+ elementCache.get(uri.toString().decode().normalize()) {
+ LinkedElement(uri.toString(), properties = mapOf(
+ "type" to "audio", "size" to size, "sizeHuman" to size.human
+ ))
+ }
+ }
return (size > 2097152) || (!mimeType.startsWith("image/") && !mimeType.startsWith("text/html"))
}
.firstOrNull { !it.first.tagName().startsWith("h", ignoreCase = true) }
?.second
- private val Int.human get() = when (this) {
+ private val Long.human get() = when (this) {
in 0..1023 -> "$this B"
in 1024..1048575 -> "${this / 1024} KiB"
in 1048576..1073741823 -> "${this / 1048576} MiB"
else -> "${this / 1073741824} GiB"
}
+ private val Int.human get() = toLong().human
+
override fun failed(uri: FreenetURI) {
failureCache.put(uri.toString().decode().normalize(), true)
removeLoadingLink(uri)
data is LinkedElement && data.loading -> renderNotLoadedLinkedElement(data)
data is LinkedElement && data.properties["type"] == "image" -> renderLinkedImage(data)
data is LinkedElement && data.properties["type"] == "html" -> renderHtmlPage(data)
+ data is LinkedElement && data.properties["type"] == "audio" -> renderAudioPlayer(data)
else -> null
}
private fun renderLinkedImage(linkedElement: LinkedElement) =
- StringWriter().use {
- val templateContext = templateContextFactory.createTemplateContext()
+ renderTemplate(loadedImageTemplate) { templateContext ->
templateContext["link"] = linkedElement.link
- it.also { loadedImageTemplate.render(templateContext, it) }
- }.toString()
+ }
private fun renderHtmlPage(linkedElement: LinkedElement) =
- StringWriter().use {
- val templateContext = templateContextFactory.createTemplateContext()
+ renderTemplate(loadedHtmlPageTemplate) { templateContext ->
templateContext["link"] = linkedElement.link
templateContext["title"] = linkedElement.properties["title"] ?: "No title"
templateContext["description"] = linkedElement.properties["description"] ?: "No description"
- it.also { loadedHtmlPageTemplate.render(templateContext, it) }
- }.toString()
+ }
+
+ private fun renderAudioPlayer(linkedElement: LinkedElement) =
+ renderTemplate(loadedAudioTemplate) { templateContext ->
+ templateContext["link"] = linkedElement.link
+ }
private fun renderNotLoadedLinkedElement(linkedElement: LinkedElement) =
- StringWriter().use {
- val templateContext = templateContextFactory.createTemplateContext()
+ renderTemplate(notLoadedImageTemplate) { templateContext ->
templateContext["link"] = linkedElement.link
- it.also { notLoadedImageTemplate.render(templateContext, it) }
+ }
+
+ private fun renderTemplate(template: Template, fillTemplateContext: (TemplateContext) -> Unit) =
+ StringWriter().use { stringWriter ->
+ stringWriter.also {
+ templateContextFactory.createTemplateContext().also { templateContext ->
+ fillTemplateContext(templateContext)
+ template.render(templateContext, stringWriter)
+ }
+ }
}.toString()
}
private val loadedImageTemplate = """<%include linked/image.html>""".asTemplate()
private val loadedHtmlPageTemplate = """<%include linked/html-page.html>""".asTemplate()
+private val loadedAudioTemplate = """<%include linked/audio.html>""".asTemplate()
private val notLoadedImageTemplate = """<%include linked/notLoaded.html>""".asTemplate()
--- /dev/null
+<span class="linked-element loaded" title="<%link|html>"><audio controls src="/<% link|html>"></audio></span>
import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
-import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import java.io.ByteArrayOutputStream
import java.util.concurrent.TimeUnit
+import kotlin.test.Test
/**
* Unit test for [DefaultElementLoaderTest].
}
@Test
+ fun `image loader returns element for audio data`() {
+ elementLoader.loadElement(audioKey)
+ verify(freenetInterface).startFetch(eq(FreenetURI(audioKey)), callback.capture())
+ callback.value.shouldCancel(FreenetURI(audioKey), "audio/mpeg", 123)
+ val linkedElement = elementLoader.loadElement(audioKey)
+ assertThat(linkedElement, equalTo(LinkedElement(audioKey, properties = mapOf(
+ "type" to "audio", "size" to 123L, "sizeHuman" to "123 B"
+ ))))
+ }
+
+ @Test
fun `element loader can extract description from description header`() {
elementLoader.loadElement(textKey)
verify(freenetInterface).startFetch(eq(FreenetURI(textKey)), callback.capture())
private const val IMAGE_ID = "KSK@gpl.png"
private val freenetURI = FreenetURI(IMAGE_ID)
private const val decomposedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/fru%CC%88hstu%CC%88ck.jpg"
+private const val audioKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/audio.mp3"
private const val normalizedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/frühstück.jpg"
private const val textKey = "KSK@gpl.html"
private const val sizeOkay = 2097152L
import org.hamcrest.Matchers.*
import org.jsoup.*
import org.jsoup.nodes.*
-import org.junit.*
+import kotlin.test.Test
/**
* Unit test for [LinkedElementRenderFilter].
}
@Test
+ fun `filter can render audio player`() {
+ val html = filter.format(null, LinkedElement("KSK@gpl.mp3", properties = mapOf("type" to "audio")), emptyMap()) as String
+ val outerSpanNode = Jsoup.parseBodyFragment(html).body().child(0)
+ assertThat(outerSpanNode.nodeName(), equalTo("span"))
+ assertThat(outerSpanNode.attr("class"), equalTo("linked-element loaded"))
+ assertThat(outerSpanNode.attr("title"), equalTo("KSK@gpl.mp3"))
+ val linkNode = outerSpanNode.child(0)
+ assertThat(linkNode.nodeName(), equalTo("audio"))
+ assertThat(linkNode.attr("controls"), equalTo(""))
+ assertThat(linkNode.attr("src"), equalTo("/KSK@gpl.mp3"))
+ }
+
+ @Test
fun `render filter can be created by guice`() {
val injector = Guice.createInjector(TemplateContextFactory::class.isProvidedByMock())
assertThat(injector.getInstance<LinkedElementRenderFilter>(), notNullValue())