2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-22 01:51:47 +00:00

Add PeerTube support to formatUriWithSeek function

This commit is contained in:
TPJ Schikhof 2025-02-16 21:43:14 +00:00 committed by Simon Redman
parent 03c2121d57
commit a9a99ea7bd
2 changed files with 43 additions and 7 deletions

View File

@ -13,6 +13,9 @@ object VideoUrlsHelper {
private const val MINUTES_IN_HOUR = 60
private const val SECONDS_IN_HOUR = SECONDS_IN_MINUTE * MINUTES_IN_HOUR
/** PeerTube uses a Flickr Base58 encoded short UUID (alphanumeric, but 0, O, I, and l are excluded) with a length of 22 characters **/
private val peerTubePathPattern = Regex("^/w/[1-9a-km-zA-HJ-NP-Z]{22}(\\?.+)?$")
@Throws(MalformedURLException::class)
fun formatUriWithSeek(address: String, position: Long): URL {
val positionSeconds = position / 1000 // milliseconds to seconds
@ -33,7 +36,10 @@ object VideoUrlsHelper {
url.editParameter("start", Regex("\\d+")) { "$positionSeconds" }
}
host.contains("twitch.tv") -> {
url.editParameter("t", Regex("(\\d+[hH])?(\\d+[mM])?\\d+[sS]")) { positionSeconds.formatTimestampHMS() }
url.editParameter("t", Regex("(\\d+[hH])?(\\d+[mM])?\\d+[sS]")) { formatTimestampHMS(positionSeconds) }
}
url.path.matches(peerTubePathPattern) -> {
url.editParameter("start", Regex("(\\d+[hH])?(\\d+[mM])?\\d+[sS]")) { formatTimestampHMS(positionSeconds) }
}
else -> url
}
@ -62,13 +68,16 @@ object VideoUrlsHelper {
return URL("${urlBase}?${modifiedUrlQuery}") // -> "https://www.youtube.com/watch?v=ovX5G0O5ZvA&t=14"
}
/** Formats timestamp to e.g. 01h02m34s */
private fun Long.formatTimestampHMS(): String {
if (this == 0L) return "0s"
/**
* @param timestamp in seconds
* @return timestamp formatted as e.g. "01h02m34s"
* */
private fun formatTimestampHMS(timestamp: Long): String {
if (timestamp == 0L) return "0s"
val seconds: Long = this % SECONDS_IN_MINUTE
val minutes: Long = (this / SECONDS_IN_MINUTE) % MINUTES_IN_HOUR
val hours: Long = this / SECONDS_IN_HOUR
val seconds: Long = timestamp % SECONDS_IN_MINUTE
val minutes: Long = (timestamp / SECONDS_IN_MINUTE) % MINUTES_IN_HOUR
val hours: Long = timestamp / SECONDS_IN_HOUR
fun pad(s: String) = s.padStart(3, '0')
val hoursText = if (hours > 0) pad("${hours}h") else ""

View File

@ -72,4 +72,31 @@ class VideoUrlsHelperTest {
val expected = "https://example.org/cool_video.mp4"
Assert.assertEquals(expected, formatted.toString())
}
@Test
fun checkPeerTubeURL() {
val validUrls = mapOf(
"https://video.blender.org/w/472h2s5srBFmAThiZVw96R?start=01m27s" to "https://video.blender.org/w/472h2s5srBFmAThiZVw96R?start=01m30s",
"https://video.blender.org/w/mDyZP2TrdjjjNRMoVUgPM2?start=01m27s" to "https://video.blender.org/w/mDyZP2TrdjjjNRMoVUgPM2?start=01m30s",
"https://video.blender.org/w/evhMcVhvK6VeAKJwCSuHSe?start=01m27s" to "https://video.blender.org/w/evhMcVhvK6VeAKJwCSuHSe?start=01m30s",
"https://video.blender.org/w/54tzKpEguEEu26Hi8Lcpna?start=01m27s" to "https://video.blender.org/w/54tzKpEguEEu26Hi8Lcpna?start=01m30s",
"https://video.blender.org/w/o5VtGNQaNpFNNHiJbLy4eM?start=01m27s" to "https://video.blender.org/w/o5VtGNQaNpFNNHiJbLy4eM?start=01m30s",
)
for ((from, to) in validUrls) {
val formatted = VideoUrlsHelper.formatUriWithSeek(from, 90_000L)
Assert.assertEquals(to, formatted.toString())
}
val invalidUrls = listOf(
"https://video.blender.org/w/472h2s5srBFmAOhiZVw96R?start=01m27s", // invalid character (O)
"https://video.blender.org/w/mDyZP2TrdjjjNIMoVUgPM2?start=01m27s", // invalid character (I)
"https://video.blender.org/w/evhMcVhvK6VeAlJwCSuHSe?start=01m27s", // invalid character (l)
"https://video.blender.org/w/54tzKpEguEEu20Hi8Lcpna?start=01m27s", // invalid character (0)
"https://video.blender.org/w/o5VtGNQaNpFNHiJbLy4eM?start=01m27s", // invalid length (21)
"https://video.blender.org/w/hb43bRmBzNpHd4sW74Y4cyAB?start=01m27s", // invalid length (23)
)
for (url in invalidUrls) {
val formatted = VideoUrlsHelper.formatUriWithSeek(url, 90_000L)
Assert.assertEquals(url, formatted.toString()) // should not modify the URL
}
}
}