Skip to content

Commit

Permalink
feat(player): improve api.video url parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibaultBee committed Jun 16, 2023
1 parent ceb550e commit 1aae897
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,4 @@ fun String.toVideoType() = VideoType.fromString(this)
*
* @return corresponding [VideoOptions] or an exception
*/
fun String.parseAsVideoOptions(
vodDomainURL: String = VideoType.VOD.baseUrl,
liveDomainURL: String = VideoType.LIVE.baseUrl
) = Utils.parseMediaUrl(this, vodDomainURL, liveDomainURL)
fun String.parseAsVideoOptions() = Utils.parseMediaUrl(this)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package video.api.player.extensions

import video.api.player.models.VideoOptions
import video.api.player.models.VideoType
import video.api.player.utils.Utils
import java.net.URL

Expand All @@ -10,7 +9,4 @@ import java.net.URL
*
* @return corresponding [VideoOptions] or an exception
*/
fun URL.parseAsVideoOptions(
vodDomainURL: URL = URL(VideoType.VOD.baseUrl),
liveDomainURL: URL = URL(VideoType.LIVE.baseUrl)
) = Utils.parseMediaUrl(this, vodDomainURL, liveDomainURL)
fun URL.parseAsVideoOptions() = Utils.parseMediaUrl(this)
12 changes: 4 additions & 8 deletions player/src/main/java/video/api/player/models/VideoOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,16 @@ data class VideoOptions(
* @param url the URL of the video to play
*/
fun fromUrl(
url: String,
vodDomainURL: String = VideoType.VOD.baseUrl,
liveDomainURL: String = VideoType.LIVE.baseUrl
) = url.parseAsVideoOptions(vodDomainURL, liveDomainURL)
url: String
) = url.parseAsVideoOptions()

/**
* Creates a [VideoOptions] from an api.video URL.
*
* @param url the URL of the video to play
*/
fun fromUrl(
url: URL,
vodDomainURL: URL = URL(VideoType.VOD.baseUrl),
liveDomainURL: URL = URL(VideoType.LIVE.baseUrl)
) = url.parseAsVideoOptions(vodDomainURL, liveDomainURL)
url: URL
) = url.parseAsVideoOptions()
}
}
33 changes: 14 additions & 19 deletions player/src/main/java/video/api/player/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package video.api.player.utils

import video.api.player.extensions.toVideoType
import video.api.player.models.VideoOptions
import video.api.player.models.VideoType
import java.io.IOException
Expand All @@ -11,37 +12,31 @@ object Utils {
private const val LIVE_TOKEN_DELIMITER = "private"

fun parseMediaUrl(
mediaUrl: String,
vodDomainURL: String = VideoType.VOD.baseUrl,
liveDomainURL: String = VideoType.LIVE.baseUrl
) = parseMediaUrl(URL(mediaUrl), URL(vodDomainURL), URL(liveDomainURL))
mediaUrl: String
) = parseMediaUrl(URL(mediaUrl))

fun parseMediaUrl(
mediaUrl: URL,
vodDomainURL: URL = URL(VideoType.VOD.baseUrl),
liveDomainURL: URL = URL(VideoType.LIVE.baseUrl)
mediaUrl: URL
): VideoOptions {
val regex =
"https:/.*[/].*/(?<id>(vi|li)[^/^.]*)[/.].*"
val regex = "https://[^/]+/(?>(?<type>vod|live)/)?(?>.*/)?(?<id>(vi|li)[^/^.]*).*"
val pattern = Pattern.compile(regex)
val matcher = pattern.matcher(mediaUrl.toString())

if (matcher.groupCount() < 2) {
if (matcher.groupCount() < 3) {
throw IOException("The media url doesn't look like an api.video URL.")
}

try {
matcher.find()
// Group naming is not supported before Android API 26
val videoType =
if (mediaUrl.toString().startsWith(vodDomainURL.toString())) {
VideoType.VOD
} else if (mediaUrl.toString().startsWith(liveDomainURL.toString())) {
VideoType.LIVE
} else {
throw IOException("The media url must start with $vodDomainURL or $liveDomainURL")
}
val videoId = matcher.group(1) ?: throw IOException("Failed to get videoId")
val videoId = matcher.group(2) ?: throw IOException("Failed to get videoId")

// For live, we might not have a type for now because there isn't any `/live/` in the URL.
val firstGroup = matcher.group(1)
val videoType = firstGroup?.toVideoType()
?: if (videoId.startsWith("li")) VideoType.LIVE else throw IOException(
"Failed to get videoType"
)

val tokenDelimiter =
if (videoType == VideoType.VOD) VOD_TOKEN_DELIMITER else LIVE_TOKEN_DELIMITER
Expand Down
54 changes: 37 additions & 17 deletions player/src/test/java/video/api/player/models/VideoOptionsTest.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package video.api.player.models

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

class VideoOptionsTest {
@Test
fun `test embed vod url`() {
val videoOptions = VideoOptions("vi5oNqxkifcXkT4auGNsvgZB", VideoType.VOD)
assertEquals(
"https://vod.api.video/vod/vi5oNqxkifcXkT4auGNsvgZB/hls/manifest.m3u8",
videoOptions.hlsManifestUrl
)
}

@Test
fun `test vod url`() {
val videoOptions = VideoOptions("vi5oNqxkifcXkT4auGNsvgZB", VideoType.VOD)
Expand Down Expand Up @@ -58,13 +68,22 @@ class VideoOptionsTest {
)
}

@Test
fun `test parse embed vod url`() {
val videoOptions =
VideoOptions.fromUrl("https://embed.api.video/vod/vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoId, "vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoType, VideoType.VOD)
assertNull(videoOptions.token)
}

@Test
fun `test parse vod url`() {
val videoOptions =
VideoOptions.fromUrl("https://vod.api.video/vod/vi5oNqxkifcXkT4auGNsvgZB/hls/manifest.m3u8")
assertEquals(videoOptions.videoId, "vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoType, VideoType.VOD)
assertEquals(videoOptions.token, null)
assertNull(videoOptions.token)
}

@Test
Expand All @@ -82,7 +101,7 @@ class VideoOptionsTest {
VideoOptions.fromUrl("https://vod.api.video/vod/vi5oNqxkifcXkT4auGNsvgZB/mp4/source.mp4")
assertEquals(videoOptions.videoId, "vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoType, VideoType.VOD)
assertEquals(videoOptions.token, null)
assertNull(videoOptions.token)
}

@Test
Expand All @@ -94,13 +113,22 @@ class VideoOptionsTest {
assertEquals(videoOptions.token, "PRIVATE_TOKEN")
}

@Test
fun `test parse embed live url`() {
val videoOptions =
VideoOptions.fromUrl("https://embed.api.video/live/li77ACbZjzEJgmr8d0tm4xFt")
assertEquals(videoOptions.videoId, "li77ACbZjzEJgmr8d0tm4xFt")
assertEquals(videoOptions.videoType, VideoType.LIVE)
assertNull(videoOptions.token)
}

@Test
fun `test parse live url`() {
val videoOptions =
VideoOptions.fromUrl("https://live.api.video/li77ACbZjzEJgmr8d0tm4xFt.m3u8")
assertEquals(videoOptions.videoId, "li77ACbZjzEJgmr8d0tm4xFt")
assertEquals(videoOptions.videoType, VideoType.LIVE)
assertEquals(videoOptions.token, null)
assertNull(videoOptions.token)
}

@Test
Expand All @@ -116,22 +144,18 @@ class VideoOptionsTest {
fun `test parse vod url with custom domain`() {
val videoOptions =
VideoOptions.fromUrl(
"https://mycustom.vod.domain/vod/vi5oNqxkifcXkT4auGNsvgZB/hls/manifest.m3u8",
"https://mycustom.vod.domain",
"https://mycustom.live.domain"
"https://mycustom.vod.domain/vod/vi5oNqxkifcXkT4auGNsvgZB/hls/manifest.m3u8"
)
assertEquals(videoOptions.videoId, "vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoType, VideoType.VOD)
assertEquals(videoOptions.token, null)
assertNull(videoOptions.token)
}

@Test
fun `test parse private vod url with custom domain`() {
val videoOptions =
VideoOptions.fromUrl(
"https://mycustom.vod.domain/vod/vi5oNqxkifcXkT4auGNsvgZB/token/PRIVATE_TOKEN/hls/manifest.m3u8",
"https://mycustom.vod.domain",
"https://mycustom.live.domain"
"https://mycustom.vod.domain/vod/vi5oNqxkifcXkT4auGNsvgZB/token/PRIVATE_TOKEN/hls/manifest.m3u8"
)
assertEquals(videoOptions.videoId, "vi5oNqxkifcXkT4auGNsvgZB")
assertEquals(videoOptions.videoType, VideoType.VOD)
Expand All @@ -141,22 +165,18 @@ class VideoOptionsTest {
@Test
fun `test parse live url with custom domain`() {
val videoOptions = VideoOptions.fromUrl(
"https://mycustom.live.domain/li77ACbZjzEJgmr8d0tm4xFt.m3u8",
"https://mycustom.vod.domain",
"https://mycustom.live.domain"
"https://mycustom.live.domain/li77ACbZjzEJgmr8d0tm4xFt.m3u8"
)
assertEquals(videoOptions.videoId, "li77ACbZjzEJgmr8d0tm4xFt")
assertEquals(videoOptions.videoType, VideoType.LIVE)
assertEquals(videoOptions.token, null)
assertNull(videoOptions.token)
}

@Test
fun `test parse private live url with custom domain`() {
val videoOptions =
VideoOptions.fromUrl(
"https://mycustom.live.domain/private/PRIVATE_TOKEN/li77ACbZjzEJgmr8d0tm4xFt.m3u8",
"https://mycustom.vod.domain",
"https://mycustom.live.domain"
"https://mycustom.live.domain/private/PRIVATE_TOKEN/li77ACbZjzEJgmr8d0tm4xFt.m3u8"
)
assertEquals(videoOptions.videoId, "li77ACbZjzEJgmr8d0tm4xFt")
assertEquals(videoOptions.videoType, VideoType.LIVE)
Expand Down

0 comments on commit 1aae897

Please sign in to comment.