Skip to content

Commit 4e2d4a0

Browse files
authored
Merge pull request #507 from JetBrains/backend-video-urls
Add video URL data to backend
2 parents d970830 + db471b9 commit 4e2d4a0

File tree

10 files changed

+201
-19
lines changed

10 files changed

+201
-19
lines changed

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/di/ServiceModule.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package org.jetbrains.kotlinconf.backend.di
33
import org.jetbrains.kotlinconf.backend.services.NewsService
44
import org.jetbrains.kotlinconf.backend.services.SessionizeService
55
import org.jetbrains.kotlinconf.backend.services.TimeService
6+
import org.jetbrains.kotlinconf.backend.services.VideoUrlService
67
import org.koin.core.module.dsl.singleOf
78
import org.koin.dsl.module
89

910
val serviceModule = module {
1011
singleOf(::TimeService)
1112
singleOf(::NewsService)
13+
singleOf(::VideoUrlService)
1214
singleOf(::SessionizeService)
13-
}
15+
}

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/services/NewsService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import org.slf4j.LoggerFactory
1717

1818
class NewsService(private val client: HttpClient, config: ConferenceConfig): Closeable {
1919
private val log = LoggerFactory.getLogger("NewsService")
20-
private val repo: String = config.newsRepo
21-
private val branch: String = config.newsBranch
20+
private val repo: String = config.dataRepo
21+
private val branch: String = config.dataBranch
2222
private val folder: String = config.newsFolder
2323
private val updateInterval = config.sessionizeInterval
2424

@@ -132,4 +132,4 @@ class NewsService(private val client: HttpClient, config: ConferenceConfig): Clo
132132
override fun close() {
133133
syncJob.cancel()
134134
}
135-
}
135+
}

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/services/SessionizeService.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import kotlinx.datetime.toLocalDateTime
1414
import org.jetbrains.kotlinconf.Conference
1515
import org.jetbrains.kotlinconf.EVENT_TIME_ZONE
1616
import org.jetbrains.kotlinconf.Session
17+
import org.jetbrains.kotlinconf.SessionId
1718
import org.jetbrains.kotlinconf.Speaker
1819
import org.jetbrains.kotlinconf.backend.model.CategoryItemData
1920
import org.jetbrains.kotlinconf.backend.model.SessionizeData
@@ -23,6 +24,7 @@ import java.util.concurrent.TimeUnit
2324

2425
class SessionizeService(
2526
private val client: HttpClient,
27+
private val videoUrlService: VideoUrlService,
2628
config: ConferenceConfig
2729
): Closeable {
2830
private val conference = MutableSharedFlow<Conference>(replay = 1)
@@ -50,9 +52,10 @@ class SessionizeService(
5052
suspend fun synchronizeWithSessionize(
5153
sessionizeUrl: String,
5254
) {
53-
val updatedValue = client.get(sessionizeUrl)
54-
.body<SessionizeData>()
55-
.toConference()
55+
val sessionizeData = client.get(sessionizeUrl).body<SessionizeData>()
56+
val videoUrls = videoUrlService.getVideoUrls()
57+
58+
val updatedValue = sessionizeData.toConference(videoUrls)
5659

5760
conference.emit(updatedValue)
5861
}
@@ -66,7 +69,7 @@ class SessionizeService(
6669

6770
suspend fun getConferenceData(): Conference = conference.first()
6871

69-
private fun SessionizeData.toConference(): Conference {
72+
private fun SessionizeData.toConference(videoUrls: Map<SessionId, String>): Conference {
7073
val tags: Map<Int, CategoryItemData> = categories
7174
.flatMap { it.items }
7275
.associateBy { it.id }
@@ -88,7 +91,8 @@ class SessionizeService(
8891
(it.roomId?.let<Int, String> { findRoom(it) } ?: "unknown").removeSuffix(" (Lightning talks)"),
8992
startsAt.toLocalDateTime(EVENT_TIME_ZONE),
9093
endsAt.toLocalDateTime(EVENT_TIME_ZONE),
91-
tags
94+
tags,
95+
videoUrls[it.id]
9296
)
9397
}.mergeWorkshops()
9498

@@ -124,7 +128,8 @@ class SessionizeService(
124128
first.location,
125129
startTime,
126130
endTime,
127-
first.tags
131+
first.tags,
132+
first.videoUrl
128133
)
129134
}
130135

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.jetbrains.kotlinconf.backend.services
2+
3+
import io.ktor.client.HttpClient
4+
import io.ktor.client.call.body
5+
import io.ktor.client.request.get
6+
import io.ktor.client.request.headers
7+
import io.ktor.utils.io.core.Closeable
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.GlobalScope
10+
import kotlinx.coroutines.delay
11+
import kotlinx.coroutines.flow.MutableSharedFlow
12+
import kotlinx.coroutines.flow.first
13+
import kotlinx.coroutines.launch
14+
import org.jetbrains.kotlinconf.SessionId
15+
import org.jetbrains.kotlinconf.backend.model.GitHubItem
16+
import org.jetbrains.kotlinconf.backend.utils.ConferenceConfig
17+
import org.slf4j.LoggerFactory
18+
19+
class VideoUrlService(private val client: HttpClient, config: ConferenceConfig) : Closeable {
20+
private val log = LoggerFactory.getLogger("VideoUrlService")
21+
private val repo: String = config.dataRepo
22+
private val branch: String = config.dataBranch
23+
private val folder: String = config.videosFolder
24+
private val updateInterval = config.sessionizeInterval
25+
26+
private val videoUrls = MutableSharedFlow<Map<SessionId, String>>(replay = 1)
27+
28+
val syncJob = GlobalScope.launch(Dispatchers.IO) {
29+
while (true) {
30+
runCatching { updateVideoUrls() }.onFailure {
31+
log.error("Failed to update video URLs", it)
32+
}
33+
34+
delay(updateInterval * 1000)
35+
}
36+
}
37+
38+
suspend fun getVideoUrls(): Map<SessionId, String> = videoUrls.first()
39+
40+
suspend fun updateVideoUrls() {
41+
videoUrls.emit(downloadVideoUrls())
42+
}
43+
44+
private suspend fun downloadVideoUrls(): Map<SessionId, String> {
45+
val url = "https://api.github.com/repos/$repo/contents/$folder?ref=$branch"
46+
47+
val contents: List<GitHubItem> = try {
48+
client.get(url) {
49+
headers {
50+
append("Accept", "application/vnd.github.v3+json")
51+
}
52+
}.body()
53+
} catch (cause: Throwable) {
54+
log.warn("Error fetching repository contents: ${cause.message}")
55+
return emptyMap()
56+
}
57+
58+
val urlsFile = contents
59+
.find { it.type == "file" && it.name == "urls.csv" }
60+
?.download_url ?: run {
61+
log.warn("urls.csv file not found in $folder")
62+
return emptyMap()
63+
}
64+
65+
return try {
66+
val content = client.get(urlsFile).body<String>()
67+
parseVideoUrls(content)
68+
} catch (cause: Throwable) {
69+
log.warn("Error downloading $urlsFile: ${cause.message}")
70+
emptyMap()
71+
}
72+
}
73+
74+
internal fun parseVideoUrls(csvContent: String): Map<SessionId, String> {
75+
val result = mutableMapOf<SessionId, String>()
76+
77+
csvContent.lineSequence()
78+
.filter { it.isNotBlank() }
79+
.forEach { line ->
80+
val parts = line.split(";", limit = 2)
81+
if (parts.size == 2) {
82+
val sessionId = parts[0].trim()
83+
val videoUrl = parts[1].trim()
84+
if (sessionId.isNotEmpty() && videoUrl.isNotEmpty()) {
85+
result[SessionId(sessionId)] = videoUrl
86+
}
87+
}
88+
}
89+
90+
return result
91+
}
92+
93+
override fun close() {
94+
syncJob.cancel()
95+
}
96+
}

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/utils/ConferenceConfig.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class ConferenceConfig(config: ApplicationConfig) {
99

1010
val adminSecret: String = config.property("service.secret").getString()
1111

12-
val newsRepo: String = config.property("news.repo").getString()
13-
val newsBranch: String = config.property("news.branch").getString()
14-
val newsFolder: String = config.property("news.folder").getString()
15-
}
12+
val dataRepo: String = config.property("data.repo").getString()
13+
val dataBranch: String = config.property("data.branch").getString()
14+
val newsFolder: String = config.property("data.newsFolder").getString()
15+
val videosFolder: String = config.property("data.videosFolder").getString()
16+
}

backend/src/main/resources/application.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ sessionize:
1414
imagesUrl: "https://sessionize.com/image/"
1515
interval: 60
1616

17-
news:
17+
data:
1818
repo: "JetBrains/kotlinconf-app"
1919
branch: "main"
20-
folder: "news"
20+
newsFolder: "data/news"
21+
videosFolder: "data/videos"
2122

2223
database:
2324
host: "$DB0_HOST:"

backend/src/test/resources/test-application.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ sessionize:
1414
imagesUrl: "https://sessionize.com/image/"
1515
interval: 60
1616

17-
news:
17+
data:
1818
repo: "JetBrains/kotlinconf-app"
19-
branch: "e5l/news"
20-
folder: "news"
19+
branch: "backend-video-urls"
20+
newsFolder: "data/news"
21+
videosFolder: "data/videos"
2122

2223
database:
2324
host: "$DB0_HOST:"
File renamed without changes.
File renamed without changes.

data/videos/urls.csv

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
857088;https://www.youtube.com/watch?v=F5NaqGF9oT4
2+
857092;https://www.youtube.com/watch?v=E4f5PyNmGLo
3+
814508;https://www.youtube.com/watch?v=NcAW-FZtpzk
4+
814689;https://www.youtube.com/watch?v=WTWW7OOHQIw
5+
793515;https://www.youtube.com/watch?v=5lkZj4v4-ks
6+
793888;https://www.youtube.com/watch?v=mQhtLCMiUMA
7+
795861;https://www.youtube.com/watch?v=bkd6EAPIVe0
8+
786515;https://www.youtube.com/watch?v=kIEBQ_czdxs
9+
762779;https://www.youtube.com/watch?v=IUrA3mDSWZQ
10+
794504;https://www.youtube.com/watch?v=Hy5TOyDDics
11+
795179;https://www.youtube.com/watch?v=QExksqeNWbY
12+
800713;https://www.youtube.com/watch?v=9KdP2idt6LE
13+
867231;https://www.youtube.com/watch?v=D3hCBrxJHLo
14+
812350;https://www.youtube.com/watch?v=434WFSiYj9k
15+
792910;https://www.youtube.com/watch?v=zBgb0z1pQkM
16+
795318;https://www.youtube.com/watch?v=ufQvrZmQw_I
17+
793244;https://www.youtube.com/watch?v=qpM3_ymNkP8
18+
779171;https://www.youtube.com/watch?v=JKLqQiYh8GQ
19+
793302;https://www.youtube.com/watch?v=z-u99yZFn5o
20+
796310;https://www.youtube.com/watch?v=M9Nni0Qywyo
21+
800297;https://www.youtube.com/watch?v=YKTlW8Qkj0w
22+
774874;https://www.youtube.com/watch?v=XcxqhmG_bdg
23+
795195;https://www.youtube.com/watch?v=bKwdgMKyFOY
24+
816767;https://www.youtube.com/watch?v=a8uMQk4R6jQ
25+
779829;https://www.youtube.com/watch?v=Q4oj9fkaDCs
26+
836755;https://www.youtube.com/watch?v=Zw7eJFciwXg
27+
856501;https://www.youtube.com/watch?v=O8WQCrdza8E
28+
795897;https://www.youtube.com/watch?v=PW_qc6EUBQE
29+
794336;https://www.youtube.com/watch?v=fv-vw1huJ-4
30+
783886;https://www.youtube.com/watch?v=UebhKUdO2sQ
31+
793235;https://www.youtube.com/watch?v=3vaAVtnrzAM
32+
885501;https://www.youtube.com/watch?v=JxTIZAEos8Y
33+
793709;https://www.youtube.com/watch?v=vewgb-vyJME
34+
795839;https://www.youtube.com/watch?v=vMNCAryfJys
35+
800068;https://www.youtube.com/watch?v=57Ed70ROmm4
36+
795976;https://www.youtube.com/watch?v=hxEM5J6QfLY
37+
812314;https://www.youtube.com/watch?v=2Vp2QeBZkfo
38+
812209;https://www.youtube.com/watch?v=ojuBhKRzyL8
39+
796169;https://www.youtube.com/watch?v=FXGT6HbBXNw
40+
779345;https://www.youtube.com/watch?v=4A6aLK2KznU
41+
858141;https://www.youtube.com/watch?v=5Sc3Qdb0XoQ
42+
781372;https://www.youtube.com/watch?v=9P7qUGi5_gc
43+
797367;https://www.youtube.com/watch?v=dcgwPpVT74g
44+
795941;https://www.youtube.com/watch?v=sDA28kH6AIc
45+
795620;https://www.youtube.com/watch?v=n0LpCCv3VEY
46+
788999;https://www.youtube.com/watch?v=qJB5iX2cOu0
47+
799261;https://www.youtube.com/watch?v=QTX5_JV4TVU
48+
779451;https://www.youtube.com/watch?v=HSIhkB5bGJs
49+
759131;https://www.youtube.com/watch?v=OuX5325yq_I
50+
788419;https://www.youtube.com/watch?v=P0mo8Tzrapo
51+
774286;https://www.youtube.com/watch?v=vWIDRH6aQfI
52+
812400;https://www.youtube.com/watch?v=RJtiFt5pbfs
53+
794830;https://www.youtube.com/watch?v=xgfeqj8UyVA
54+
795546;https://www.youtube.com/watch?v=QSoG8OaCSgw
55+
857571;https://www.youtube.com/watch?v=bC_grxuSO08
56+
811578;https://www.youtube.com/watch?v=clDGqPfaIto
57+
795023;https://www.youtube.com/watch?v=lcqtRQ5Fou8
58+
787545;https://www.youtube.com/watch?v=OyEfB6Q4Y0s
59+
795778;https://www.youtube.com/watch?v=2Eyq1VhBxUg
60+
856531;https://www.youtube.com/watch?v=-CznWwKD-WE
61+
793518;https://www.youtube.com/watch?v=z_IO2_XeW2Q
62+
811915;https://www.youtube.com/watch?v=K2PN03AepC0
63+
796359;https://www.youtube.com/watch?v=n6egwuOnGuk
64+
786540;https://www.youtube.com/watch?v=AlGWsTXnWsY
65+
798313;https://www.youtube.com/watch?v=KEsVNrzPf24
66+
792693;https://www.youtube.com/watch?v=l7alNC819MU
67+
800400;https://www.youtube.com/watch?v=rKbM3e0OidI
68+
784566;https://www.youtube.com/watch?v=O0BqoLcRuJI
69+
796464;https://www.youtube.com/watch?v=eryPIdJjBgk
70+
811421;https://www.youtube.com/watch?v=jDz_yNZkEzk
71+
774210;https://www.youtube.com/watch?v=6jZa4B-If-I
72+
904371;https://www.youtube.com/watch?v=Jyj4kdK8a6o
73+
791121;https://www.youtube.com/watch?v=SwNTpgp262o
74+
787342;https://www.youtube.com/watch?v=uGgK0F3cLEI
75+
792742;https://www.youtube.com/watch?v=PY7eOmnOK3s
76+
782203;https://www.youtube.com/watch?v=oRKytkr2FOY

0 commit comments

Comments
 (0)