Skip to content

Commit

Permalink
Tweak formatting rules
Browse files Browse the repository at this point in the history
  • Loading branch information
arkon committed May 18, 2024
1 parent f1211b1 commit 25b9290
Show file tree
Hide file tree
Showing 35 changed files with 448 additions and 565 deletions.
18 changes: 16 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
[*.{kt,kts}]
max_line_length = 120
[*]
charset = utf-8
indent_size = 4
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
max_line_length = unset

[*.{kt,kts}]
max_line_length = 120
ktlint_code_style = intellij_idea
ktlint_standrd = enabled
ktlint_standard_annotation = disabled
ktlint_experimental = enabled
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = unset
105 changes: 48 additions & 57 deletions app/src/main/kotlin/com/livetl/android/data/feed/FeedService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,69 +16,60 @@ import kotlinx.serialization.json.Json
import timber.log.Timber
import javax.inject.Inject

class FeedService
@Inject
constructor(
private val client: HttpClient,
private val json: Json,
) {
suspend fun getFeed(
organization: String? = "Hololive",
status: StreamStatus,
): List<Stream> =
withContext(SupervisorJob() + Dispatchers.IO) {
val result =
client.get {
url {
baseUrl()
path("api", "v2", "videos")
parameter("status", status.apiValue)
parameter("lang", "all")
parameter("type", "stream")
parameter("include", "description,live_info")
parameter("org", organization)
parameter("sort", status.sortField)
parameter("order", if (status.sortAscending) "asc" else "desc")
parameter("limit", "50")
parameter("offset", "0")
parameter("paginated", "<empty>")
parameter("max_upcoming_hours", "48")
}
baseHeaders()
class FeedService @Inject constructor(private val client: HttpClient, private val json: Json) {
suspend fun getFeed(organization: String? = "Hololive", status: StreamStatus): List<Stream> =
withContext(SupervisorJob() + Dispatchers.IO) {
val result =
client.get {
url {
baseUrl()
path("api", "v2", "videos")
parameter("status", status.apiValue)
parameter("lang", "all")
parameter("type", "stream")
parameter("include", "description,live_info")
parameter("org", organization)
parameter("sort", status.sortField)
parameter("order", if (status.sortAscending) "asc" else "desc")
parameter("limit", "50")
parameter("offset", "0")
parameter("paginated", "<empty>")
parameter("max_upcoming_hours", "48")
}

try {
val response: HolodexVideosResponse = json.decodeFromString(result.bodyAsText())
response.items
} catch (e: Exception) {
Timber.e(e)
emptyList()
baseHeaders()
}
}

suspend fun getVideoInfo(videoId: String): Stream =
withContext(SupervisorJob() + Dispatchers.IO) {
val result =
client.get {
url {
baseUrl()
path("api", "v2", "videos", videoId)
}
baseHeaders()
}

json.decodeFromString(result.bodyAsText())
try {
val response: HolodexVideosResponse = json.decodeFromString(result.bodyAsText())
response.items
} catch (e: Exception) {
Timber.e(e)
emptyList()
}

private fun URLBuilder.baseUrl() {
protocol = URLProtocol.HTTPS
host = "holodex.net"
}

private fun HttpRequestBuilder.baseHeaders() {
headers {
set("X-APIKEY", "278935bd-d91d-4037-a2b8-95b781428af7")
set("Accept", "application/json")
suspend fun getVideoInfo(videoId: String): Stream = withContext(SupervisorJob() + Dispatchers.IO) {
val result =
client.get {
url {
baseUrl()
path("api", "v2", "videos", videoId)
}
baseHeaders()
}

json.decodeFromString(result.bodyAsText())
}

private fun URLBuilder.baseUrl() {
protocol = URLProtocol.HTTPS
host = "holodex.net"
}

private fun HttpRequestBuilder.baseHeaders() {
headers {
set("X-APIKEY", "278935bd-d91d-4037-a2b8-95b781428af7")
set("Accept", "application/json")
}
}
}
11 changes: 2 additions & 9 deletions app/src/main/kotlin/com/livetl/android/data/feed/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import kotlinx.serialization.Serializable

@Immutable
@Serializable
data class HolodexVideosResponse(
val total: Int,
val items: List<Stream>,
)
data class HolodexVideosResponse(val total: Int, val items: List<Stream>)

@Immutable
@Serializable
Expand Down Expand Up @@ -66,8 +63,4 @@ enum class StreamStatus(

@Immutable
@Serializable
data class Channel(
val name: String,
val photo: String,
val org: String? = null,
)
data class Channel(val name: String, val photo: String, val org: String? = null)
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,22 @@ import javax.inject.Inject
/**
* An in-memory cache of stream info to avoid unnecessary network calls where possible.
*/
class StreamRepository
@Inject
constructor(
private val videoIdParser: VideoIdParser,
private val feedService: FeedService,
) {
private val streams = mutableMapOf<String, Stream>()
class StreamRepository @Inject constructor(
private val videoIdParser: VideoIdParser,
private val feedService: FeedService,
) {
private val streams = mutableMapOf<String, Stream>()

suspend fun getStreams(
organization: String?,
status: StreamStatus,
): List<Stream> {
val feed = feedService.getFeed(organization, status)
feed.forEach {
streams[it.id] = it
}
return feed
suspend fun getStreams(organization: String?, status: StreamStatus): List<Stream> {
val feed = feedService.getFeed(organization, status)
feed.forEach {
streams[it.id] = it
}
return feed
}

suspend fun getStream(urlOrId: String): Stream {
val id = videoIdParser.getVideoId(urlOrId)
return streams.getOrPut(id) { feedService.getVideoInfo(id) }
}
suspend fun getStream(urlOrId: String): Stream {
val id = videoIdParser.getVideoId(urlOrId)
return streams.getOrPut(id) { feedService.getVideoInfo(id) }
}
}
94 changes: 46 additions & 48 deletions app/src/main/kotlin/com/livetl/android/data/stream/StreamService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,62 @@ import io.ktor.client.statement.bodyAsText
import timber.log.Timber
import javax.inject.Inject

class StreamService
@Inject
constructor(
private val streamRepository: StreamRepository,
private val videoIdParser: VideoIdParser,
private val client: HttpClient,
) {
suspend fun getStreamInfo(pageUrl: String): StreamInfo {
val videoId = videoIdParser.getVideoId(pageUrl)
class StreamService @Inject constructor(
private val streamRepository: StreamRepository,
private val videoIdParser: VideoIdParser,
private val client: HttpClient,
) {
suspend fun getStreamInfo(pageUrl: String): StreamInfo {
val videoId = videoIdParser.getVideoId(pageUrl)

return try {
Timber.d("Fetching stream: $videoId")
val stream = streamRepository.getStream(videoId)
return try {
Timber.d("Fetching stream: $videoId")
val stream = streamRepository.getStream(videoId)

val chatContinuation: String? =
when (stream.isLive) {
true -> null
false -> getChatContinuation(stream.id)
}
val chatContinuation: String? =
when (stream.isLive) {
true -> null
false -> getChatContinuation(stream.id)
}

StreamInfo(
videoId = stream.id,
title = stream.title,
author = stream.channel.name,
shortDescription = stream.description,
isLive = stream.isLive,
chatContinuation = chatContinuation,
)
} catch (e: ClientRequestException) {
Timber.e(e, "Error getting video info for $videoId from HoloDex")
StreamInfo(
videoId = stream.id,
title = stream.title,
author = stream.channel.name,
shortDescription = stream.description,
isLive = stream.isLive,
chatContinuation = chatContinuation,
)
} catch (e: ClientRequestException) {
Timber.e(e, "Error getting video info for $videoId from HoloDex")

StreamInfo(
videoId = videoId,
title = "",
author = "",
shortDescription = "",
isLive = false,
chatContinuation = getChatContinuation(videoId),
)
}
StreamInfo(
videoId = videoId,
title = "",
author = "",
shortDescription = "",
isLive = false,
chatContinuation = getChatContinuation(videoId),
)
}
}

private suspend fun getChatContinuation(videoId: String): String? {
val result =
client.get("https://www.youtube.com/watch?v=$videoId") {
headers {
set("User-Agent", USER_AGENT)
}
private suspend fun getChatContinuation(videoId: String): String? {
val result =
client.get("https://www.youtube.com/watch?v=$videoId") {
headers {
set("User-Agent", USER_AGENT)
}
val matches = CHAT_CONTINUATION_PATTERN.matcher(result.bodyAsText())
return if (matches.find()) {
matches.group(1)
} else {
Timber.w("Chat continuation found for $videoId")
null
}
val matches = CHAT_CONTINUATION_PATTERN.matcher(result.bodyAsText())
return if (matches.find()) {
matches.group(1)
} else {
Timber.w("Chat continuation found for $videoId")
null
}
}
}

const val USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44"
private val CHAT_CONTINUATION_PATTERN by lazy { """continuation":"(\w+)"""".toPattern() }
Loading

0 comments on commit 25b9290

Please sign in to comment.