Skip to content

Commit 2dbc443

Browse files
committed
Cleaned up storing analysis and audio files
1 parent d3a9bda commit 2dbc443

File tree

13 files changed

+325
-585
lines changed

13 files changed

+325
-585
lines changed

src/main/kotlin/org/abimon/eternalJukebox/MediaWrapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object MediaWrapper {
2727
return ffmpegProcess.exitValue() == 0
2828
}
2929

30-
ffmpegProcess.destroyForcibly()
30+
ffmpegProcess.destroyForcibly().waitFor()
3131
return false
3232
}
3333
}

src/main/kotlin/org/abimon/eternalJukebox/VertxExtensions.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.abimon.eternalJukebox
22

3-
import io.vertx.core.buffer.Buffer
43
import io.vertx.core.http.HttpServerResponse
54
import io.vertx.core.json.JsonArray
65
import io.vertx.core.json.JsonObject
@@ -11,8 +10,6 @@ import io.vertx.kotlin.coroutines.dispatcher
1110
import kotlinx.coroutines.launch
1211
import org.abimon.eternalJukebox.objects.ClientInfo
1312
import org.abimon.eternalJukebox.objects.ConstantValues
14-
import org.abimon.visi.io.DataSource
15-
import org.abimon.visi.io.readChunked
1613

1714
fun HttpServerResponse.end(json: JsonArray) = putHeader("Content-Type", "application/json").end(json.toString())
1815
fun HttpServerResponse.end(json: JsonObject) = putHeader("Content-Type", "application/json").end(json.toString())
@@ -27,13 +24,6 @@ fun RoutingContext.endWithStatusCode(statusCode: Int, init: JsonObject.() -> Uni
2724
.end(json.toString())
2825
}
2926

30-
fun HttpServerResponse.end(data: DataSource, contentType: String = "application/octet-stream") {
31-
putHeader("Content-Type", contentType)
32-
putHeader("Content-Length", "${data.size}")
33-
data.use { stream -> stream.readChunked { chunk -> write(Buffer.buffer(chunk)) } }
34-
end()
35-
}
36-
3727
fun HttpServerResponse.redirect(url: String): Unit = putHeader("Location", url).setStatusCode(307).end()
3828

3929
val RoutingContext.clientInfo: ClientInfo
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
package org.abimon.eternalJukebox.data
22

33
import com.github.kittinunf.fuel.Fuel
4-
import org.abimon.visi.io.DataSource
5-
import org.abimon.visi.io.HTTPDataSource
6-
import java.net.URL
4+
import io.vertx.ext.web.RoutingContext
5+
import org.abimon.eternalJukebox.redirect
76
import java.util.*
87

98
abstract class NodeSource {
109
abstract val nodeHosts: Array<String>
1110

1211
private val rng: Random = Random()
1312

14-
fun provide(path: String): DataSource? {
13+
fun provide(path: String, context: RoutingContext): Boolean {
1514
val starting = rng.nextInt(nodeHosts.size)
1615

1716
for (i in nodeHosts.indices) {
1817
val host = nodeHosts[(starting + i) % nodeHosts.size]
1918
val (_, healthy) = Fuel.get("$host/api/node/healthy").timeout(5 * 1000).response()
20-
if (healthy.statusCode == 200)
21-
return HTTPDataSource(URL("$host/api/node/$path"))
19+
if (healthy.statusCode == 200) {
20+
context.response().redirect("$host/api/node/$path")
21+
return true
22+
}
2223
}
2324

24-
return null
25+
return false
2526
}
2627
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
package org.abimon.eternalJukebox.data.audio
22

3+
import io.vertx.ext.web.RoutingContext
34
import org.abimon.eternalJukebox.EternalJukebox
45
import org.abimon.eternalJukebox.objects.ClientInfo
56
import org.abimon.eternalJukebox.objects.JukeboxInfo
6-
import org.abimon.visi.io.DataSource
77
import java.net.URL
88

99
@FunctionalInterface
1010
interface IAudioSource {
1111
val audioSourceOptions
1212
get() = EternalJukebox.config.audioSourceOptions
1313
/**
14-
* Provide the audio data for a required song
15-
* Returns a data source pointing to a **valid audio file**, or null if none can be obtained
14+
* Provide the audio data for a required song to the routing context.
15+
* Returns true if handled; false otherwise.
1616
*/
17-
suspend fun provide(info: JukeboxInfo, clientInfo: ClientInfo?): DataSource?
17+
suspend fun provide(info: JukeboxInfo, context: RoutingContext): Boolean
1818

1919
/**
2020
* Provide a location for a required song
2121
* The provided location may not be a direct download link, and may not contain valid audio data.
2222
* The provided location, however, should be a link to said song where possible, or return null if nothing could be found.
2323
*/
2424
suspend fun provideLocation(info: JukeboxInfo, clientInfo: ClientInfo?): URL? = null
25-
}
25+
}

src/main/kotlin/org/abimon/eternalJukebox/data/audio/NodeAudioSource.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package org.abimon.eternalJukebox.data.audio
22

3+
import io.vertx.ext.web.RoutingContext
4+
import org.abimon.eternalJukebox.clientInfo
35
import org.abimon.eternalJukebox.data.NodeSource
4-
import org.abimon.eternalJukebox.objects.ClientInfo
56
import org.abimon.eternalJukebox.objects.JukeboxInfo
6-
import org.abimon.visi.io.DataSource
77

88
@Suppress("UNCHECKED_CAST")
99
object NodeAudioSource: NodeSource(), IAudioSource {
1010
@Suppress("JoinDeclarationAndAssignment")
1111
override val nodeHosts: Array<String>
1212

13-
override suspend fun provide(info: JukeboxInfo, clientInfo: ClientInfo?): DataSource? = provide("audio/${info.id}?user_uid=${clientInfo?.userUID}")
13+
override suspend fun provide(info: JukeboxInfo, context: RoutingContext): Boolean = provide("audio/${info.id}?user_uid=${context.clientInfo.userUID}", context)
1414

1515
init {
1616
nodeHosts = if (audioSourceOptions.containsKey("NODE_HOST"))

src/main/kotlin/org/abimon/eternalJukebox/data/audio/YoutubeAudioSource.kt

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package org.abimon.eternalJukebox.data.audio
22

33
import com.github.kittinunf.fuel.Fuel
4+
import io.vertx.ext.web.RoutingContext
45
import kotlinx.coroutines.*
5-
import org.abimon.eternalJukebox.EternalJukebox
6-
import org.abimon.eternalJukebox.MediaWrapper
7-
import org.abimon.eternalJukebox.guaranteeDelete
6+
import org.abimon.eternalJukebox.*
87
import org.abimon.eternalJukebox.objects.*
9-
import org.abimon.eternalJukebox.useThenDelete
10-
import org.abimon.visi.io.DataSource
118
import org.abimon.visi.io.FileDataSource
129
import org.schabi.newpipe.extractor.*
1310
import org.schabi.newpipe.extractor.search.SearchInfo
@@ -56,8 +53,8 @@ object YoutubeAudioSource : IAudioSource, CoroutineScope {
5653
private val hitQuota = AtomicLong(-1)
5754
private val QUOTA_TIMEOUT = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES)
5855

59-
override suspend fun provide(info: JukeboxInfo, clientInfo: ClientInfo?): DataSource? {
60-
logger.trace("[{}] Attempting to provide audio for {}", clientInfo?.userUID, info.id)
56+
override suspend fun provide(info: JukeboxInfo, context: RoutingContext): Boolean {
57+
logger.trace("[{}] Attempting to provide audio for {}", context.clientInfo.userUID, info.id)
6158

6259
var youTubeUrl: String? = null
6360
val queryText = "${info.artist} - ${info.title}"
@@ -74,21 +71,21 @@ object YoutubeAudioSource : IAudioSource, CoroutineScope {
7471

7572
videoDetails.minByOrNull { abs(info.duration - it.contentDetails.duration.toMillis()) }
7673
?.let { youTubeUrl = VIDEO_LINK_PREFIX + it.id } ?: run {
77-
logger.warn("[${clientInfo?.userUID}] Searches for \"$queryText\" using YouTube Data API v3 turned up nothing")
74+
logger.warn("[${context.clientInfo.userUID}] Searches for \"$queryText\" using YouTube Data API v3 turned up nothing")
7875
}
7976
}
8077
if (youTubeUrl == null) {
8178
val infoItems = getInfoItemsFromNewPipeSearch(queryText)
8279

8380
infoItems.minByOrNull { abs(info.duration - TimeUnit.SECONDS.toMillis(it.duration)) }
8481
?.let { youTubeUrl = it.url } ?: run {
85-
logger.error("[${clientInfo?.userUID}] Searches for \"$queryText\" using NewPipeExtractor turned up nothing")
82+
logger.error("[${context.clientInfo.userUID}] Searches for \"$queryText\" using NewPipeExtractor turned up nothing")
8683
}
8784
}
8885

89-
if (youTubeUrl == null) return null
86+
if (youTubeUrl == null) return false
9087
logger.trace(
91-
"[{}] Settled on {}", clientInfo?.userUID, youTubeUrl
88+
"[{}] Settled on {}", context.clientInfo.userUID, youTubeUrl
9289
)
9390

9491
val tmpFile = File("$uuid.tmp")
@@ -110,33 +107,33 @@ object YoutubeAudioSource : IAudioSource, CoroutineScope {
110107
if (!downloadProcess.waitFor(90, TimeUnit.SECONDS)) {
111108
downloadProcess.destroyForcibly().waitFor()
112109
logger.error(
113-
"[{}] Forcibly destroyed the download process for {}", clientInfo?.userUID, youTubeUrl
110+
"[{}] Forcibly destroyed the download process for {}", context.clientInfo.userUID, youTubeUrl
114111
)
115112
}
116113
}
117114

118115
if (!endGoalTmp.exists()) {
119116
logger.warn(
120-
"[{}] {} does not exist, attempting to convert with ffmpeg", clientInfo?.userUID, endGoalTmp
117+
"[{}] {} does not exist, attempting to convert with ffmpeg", context.clientInfo.userUID, endGoalTmp
121118
)
122119

123120
if (!tmpFile.exists()) {
124-
logger.error("[{}] {} does not exist, what happened?", clientInfo?.userUID, tmpFile)
125-
return null
121+
logger.error("[{}] {} does not exist, what happened?", context.clientInfo.userUID, tmpFile)
122+
return false
126123
}
127124

128125
if (MediaWrapper.ffmpeg.installed) {
129126
if (withContext(Dispatchers.IO) { !MediaWrapper.ffmpeg.convert(tmpFile, endGoalTmp, ffmpegLog) }) {
130-
logger.error("[{}] Failed to convert {} to {}. Check {}", clientInfo?.userUID, tmpFile, endGoalTmp, ffmpegLog.name)
131-
return null
127+
logger.error("[{}] Failed to convert {} to {}. Check {}", context.clientInfo.userUID, tmpFile, endGoalTmp, ffmpegLog.name)
128+
return false
132129
}
133130

134131
if (!endGoalTmp.exists()) {
135-
logger.error("[{}] {} does not exist, check {}", clientInfo?.userUID, endGoalTmp, ffmpegLog.name)
136-
return null
132+
logger.error("[{}] {} does not exist, check {}", context.clientInfo.userUID, endGoalTmp, ffmpegLog.name)
133+
return false
137134
}
138135
} else {
139-
logger.debug("[{}] ffmpeg not installed, nothing we can do", clientInfo?.userUID)
136+
logger.debug("[{}] ffmpeg not installed, nothing we can do", context.clientInfo.userUID)
140137
}
141138
}
142139

@@ -151,32 +148,32 @@ object YoutubeAudioSource : IAudioSource, CoroutineScope {
151148
}
152149
if (videoId != null) {
153150
logger.debug("Storing Location from yt-dlp")
154-
EternalJukebox.database.storeAudioLocation(info.id, VIDEO_LINK_PREFIX + videoId, clientInfo)
151+
EternalJukebox.database.storeAudioLocation(info.id, VIDEO_LINK_PREFIX + videoId, context.clientInfo)
155152
}
156153
endGoalTmp.useThenDelete {
157154
EternalJukebox.storage.store(
158155
"${info.id}.$format",
159156
EnumStorageType.AUDIO,
160157
FileDataSource(it),
161158
mimes[format] ?: "audio/mpeg",
162-
clientInfo
159+
context.clientInfo
163160
)
164161
}
165162
}
166163

167-
return EternalJukebox.storage.provide("${info.id}.$format", EnumStorageType.AUDIO, clientInfo)
164+
return EternalJukebox.storage.safeProvide("${info.id}.$format", EnumStorageType.AUDIO, context)
168165
} finally {
169166
tmpFile.guaranteeDelete()
170167
File(tmpFile.absolutePath + ".part").guaranteeDelete()
171168
withContext(Dispatchers.IO) {
172169
tmpLog.useThenDelete {
173170
EternalJukebox.storage.store(
174-
it.name, EnumStorageType.LOG, FileDataSource(it), "text/plain", clientInfo
171+
it.name, EnumStorageType.LOG, FileDataSource(it), "text/plain", context.clientInfo
175172
)
176173
}
177174
ffmpegLog.useThenDelete {
178175
EternalJukebox.storage.store(
179-
it.name, EnumStorageType.LOG, FileDataSource(it), "text/plain", clientInfo
176+
it.name, EnumStorageType.LOG, FileDataSource(it), "text/plain", context.clientInfo
180177
)
181178
}
182179
endGoalTmp.useThenDelete {
@@ -185,7 +182,7 @@ object YoutubeAudioSource : IAudioSource, CoroutineScope {
185182
EnumStorageType.AUDIO,
186183
FileDataSource(it),
187184
mimes[format] ?: "audio/mpeg",
188-
clientInfo
185+
context.clientInfo
189186
)
190187
}
191188
}

0 commit comments

Comments
 (0)