feat: add video and audio card support to MobiledocBuilder

- Add 8-param build() overload accepting videoUrl and audioUrl
- Video card: ["video",{"src":"url","loop":false}]
- Audio card: ["audio",{"src":"url"}]
- Card order: images -> video -> audio -> bookmark
- Add tests for video only, audio only, and all media types combined
This commit is contained in:
Paweł Orzech 2026-03-20 00:44:02 +01:00
parent 2410d05bd6
commit 96e2799787
2 changed files with 159 additions and 0 deletions

View file

@ -62,6 +62,24 @@ object MobiledocBuilder {
linkTitle: String?,
linkDescription: String?,
imageAlt: String?
): String {
return build(text, imageUrls, linkUrl, linkTitle, linkDescription, imageAlt, null, null)
}
/**
* Builds mobiledoc JSON with support for images, video, audio, and bookmark cards.
*
* Card order: images -> video -> audio -> bookmark
*/
fun build(
text: String,
imageUrls: List<String>,
linkUrl: String?,
linkTitle: String?,
linkDescription: String?,
imageAlt: String?,
videoUrl: String?,
audioUrl: String?
): String {
val escapedText = escapeForJson(text).replace("\n", "\\n")
@ -77,6 +95,20 @@ object MobiledocBuilder {
cardSections.add("[10,${cards.size - 1}]")
}
// Add video card if present
if (videoUrl != null) {
val escapedUrl = escapeForJson(videoUrl)
cards.add("""["video",{"src":"$escapedUrl","loop":false}]""")
cardSections.add("[10,${cards.size - 1}]")
}
// Add audio card if present
if (audioUrl != null) {
val escapedUrl = escapeForJson(audioUrl)
cards.add("""["audio",{"src":"$escapedUrl"}]""")
cardSections.add("[10,${cards.size - 1}]")
}
// Add bookmark card if link is present
if (linkUrl != null) {
val escapedUrl = escapeForJson(linkUrl)

View file

@ -597,4 +597,131 @@ class MobiledocBuilderTest {
val secondCard = cards.get(1).asJsonArray.get(1).asJsonObject
assertEquals("", secondCard.get("alt").asString)
}
// --- Video card tests ---
@Test
fun `build with video only produces valid JSON with video card`() {
val result = MobiledocBuilder.build(
"Check this video", emptyList(), null, null, null, null,
"https://example.com/video.mp4", null
)
val json = JsonParser.parseString(result).asJsonObject
assertNotNull(json)
val cards = json.getAsJsonArray("cards")
assertEquals(1, cards.size())
val card = cards.get(0).asJsonArray
assertEquals("video", card.get(0).asString)
val cardData = card.get(1).asJsonObject
assertEquals("https://example.com/video.mp4", cardData.get("src").asString)
assertFalse(cardData.get("loop").asBoolean)
}
@Test
fun `build with video has correct sections`() {
val result = MobiledocBuilder.build(
"Text", emptyList(), null, null, null, null,
"https://example.com/video.mp4", null
)
val json = JsonParser.parseString(result).asJsonObject
val sections = json.getAsJsonArray("sections")
assertEquals("Should have text section + video card section", 2, sections.size())
val videoSection = sections.get(1).asJsonArray
assertEquals(10, videoSection.get(0).asInt)
assertEquals(0, videoSection.get(1).asInt)
}
// --- Audio card tests ---
@Test
fun `build with audio only produces valid JSON with audio card`() {
val result = MobiledocBuilder.build(
"Listen to this", emptyList(), null, null, null, null,
null, "https://example.com/audio.mp3"
)
val json = JsonParser.parseString(result).asJsonObject
assertNotNull(json)
val cards = json.getAsJsonArray("cards")
assertEquals(1, cards.size())
val card = cards.get(0).asJsonArray
assertEquals("audio", card.get(0).asString)
val cardData = card.get(1).asJsonObject
assertEquals("https://example.com/audio.mp3", cardData.get("src").asString)
}
@Test
fun `build with audio has correct sections`() {
val result = MobiledocBuilder.build(
"Text", emptyList(), null, null, null, null,
null, "https://example.com/audio.mp3"
)
val json = JsonParser.parseString(result).asJsonObject
val sections = json.getAsJsonArray("sections")
assertEquals("Should have text section + audio card section", 2, sections.size())
}
// --- Combined: text + images + video + audio + bookmark ---
@Test
fun `build with all media types produces valid JSON with correct card order`() {
val images = listOf("https://example.com/img1.jpg", "https://example.com/img2.jpg")
val result = MobiledocBuilder.build(
"Full post", images,
"https://link.com", "Link Title", "Link Desc",
"Alt text",
"https://example.com/video.mp4",
"https://example.com/audio.mp3"
)
val json = JsonParser.parseString(result).asJsonObject
assertNotNull(json)
val cards = json.getAsJsonArray("cards")
// 2 images + 1 video + 1 audio + 1 bookmark = 5 cards
assertEquals(5, cards.size())
// Verify card order: image, image, video, audio, bookmark
assertEquals("image", cards.get(0).asJsonArray.get(0).asString)
assertEquals("image", cards.get(1).asJsonArray.get(0).asString)
assertEquals("video", cards.get(2).asJsonArray.get(0).asString)
assertEquals("audio", cards.get(3).asJsonArray.get(0).asString)
assertEquals("bookmark", cards.get(4).asJsonArray.get(0).asString)
// Verify sections: 1 text + 5 card sections = 6
val sections = json.getAsJsonArray("sections")
assertEquals(6, sections.size())
// Verify card section indices are sequential
for (i in 0 until 5) {
val cardSection = sections.get(i + 1).asJsonArray
assertEquals(10, cardSection.get(0).asInt)
assertEquals(i, cardSection.get(1).asInt)
}
}
@Test
fun `build with video and audio but no images produces correct cards`() {
val result = MobiledocBuilder.build(
"Media post", emptyList(), null, null, null, null,
"https://example.com/video.mp4",
"https://example.com/audio.mp3"
)
val json = JsonParser.parseString(result).asJsonObject
val cards = json.getAsJsonArray("cards")
assertEquals(2, cards.size())
assertEquals("video", cards.get(0).asJsonArray.get(0).asString)
assertEquals("audio", cards.get(1).asJsonArray.get(0).asString)
}
@Test
fun `build with video URL containing special chars produces valid JSON`() {
val result = MobiledocBuilder.build(
"Text", emptyList(), null, null, null, null,
"https://example.com/video?id=1&name=\"test\"", null
)
val json = JsonParser.parseString(result).asJsonObject
assertNotNull(json)
}
}