Refactor io.bartek.ttsserver.sonos package

This commit is contained in:
2020-05-16 10:54:11 +02:00
parent 6eaf27359d
commit 763e386bec
10 changed files with 127 additions and 114 deletions

View File

@@ -7,7 +7,7 @@ import androidx.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import io.bartek.ttsserver.service.ForegroundNotificationFactory
import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.sonos.queue.SonosQueue
import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.tts.status.TTSStatusHolder
import io.bartek.ttsserver.util.NetworkUtil

View File

@@ -1,66 +0,0 @@
package io.bartek.ttsserver.sonos
import android.content.SharedPreferences
import com.vmichalak.sonoscontroller.SonosDiscovery
import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.service.ForegroundService
import io.bartek.ttsserver.service.ServiceState
import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.util.NetworkUtil
import io.bartek.ttsserver.web.dto.BaseDTO
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
private class Consumer(
private val tts: TTSEngine,
private val host: String,
private val port: Int,
private val queue: BlockingQueue<BaseDTO>
) : Runnable {
override fun run() = try {
while (ForegroundService.state == ServiceState.RUNNING) {
consume(queue.take())
}
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
}
private fun consume(data: BaseDTO) =
SonosDiscovery.discover().firstOrNull { it.zoneGroupState.name == data.zone }?.let {
val file = tts.createTTSFile(data.text, data.language)
val filename = file.name
val url = "http://$host:$port/sonos/$filename"
val currentVolume = it.volume
it.volume = data.volume
it.clip(url, "")
it.volume = currentVolume
}
}
class SonosQueue(
private val tts: TTSEngine,
private val networkUtil: NetworkUtil,
private val preferences: SharedPreferences
) {
private val queue: BlockingQueue<BaseDTO> = LinkedBlockingQueue()
private val host: String
get() = networkUtil.getIpAddress()
private val port: Int
get() = preferences.getInt(PreferenceKey.PORT, 8080)
private var consumer: Thread? = null
fun run() {
consumer?.interrupt()
consumer = Thread(Consumer(tts, host, port, queue)).also { it.name = "SonosQueue" }
consumer?.start()
}
fun stop() {
consumer?.interrupt()
consumer = null
}
fun push(data: BaseDTO) = queue.add(data)
}

View File

@@ -1,2 +0,0 @@
package io.bartek.ttsserver.sonos

View File

@@ -0,0 +1,43 @@
package io.bartek.ttsserver.sonos.queue
import android.content.SharedPreferences
import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.sonos.worker.SonosWorker
import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.util.NetworkUtil
import io.bartek.ttsserver.web.dto.SonosDTO
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
class SonosQueue(
private val tts: TTSEngine,
private val networkUtil: NetworkUtil,
private val preferences: SharedPreferences
) {
private val queue: BlockingQueue<SonosDTO> = LinkedBlockingQueue()
private var consumer: Thread? = null
private val host: String
get() = networkUtil.getIpAddress()
private val port: Int
get() = preferences.getInt(PreferenceKey.PORT, 8080)
fun run() {
consumer?.interrupt()
consumer = Thread(
SonosWorker(
tts,
host,
port,
queue
)
).also { it.name = "SonosQueue" }
consumer?.start()
}
fun stop() {
consumer?.interrupt()
consumer = null
}
fun push(data: SonosDTO) = queue.add(data)
}

View File

@@ -0,0 +1,49 @@
package io.bartek.ttsserver.sonos.worker
import com.vmichalak.sonoscontroller.SonosDevice
import com.vmichalak.sonoscontroller.SonosDiscovery
import com.vmichalak.sonoscontroller.model.PlayState
import io.bartek.ttsserver.service.ForegroundService
import io.bartek.ttsserver.service.ServiceState
import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.web.dto.SonosDTO
import java.util.concurrent.BlockingQueue
class SonosWorker(
private val tts: TTSEngine,
private val host: String,
private val port: Int,
private val queue: BlockingQueue<SonosDTO>
) : Runnable {
override fun run() = try {
while (ForegroundService.state == ServiceState.RUNNING) {
consume(queue.take())
}
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
}
private fun consume(data: SonosDTO) =
SonosDiscovery.discover().firstOrNull { it.zoneGroupState.name == data.zone }?.let {
val file = tts.createTTSFile(data.text, data.language)
val filename = file.name
val url = "http://$host:$port/sonos/$filename"
it.announce(url, data.volume)
}
private fun SonosDevice.announce(url: String, volume: Int) {
val currentPlayState = this.playState
val currentVolume = this.volume
this.stop()
this.volume = volume
this.clip(url, "")
this.volume = currentVolume
when(currentPlayState) {
PlayState.PLAYING -> this.play()
else -> this.stop()
}
}
}

View File

@@ -1,35 +1,15 @@
package io.bartek.ttsserver.web.dto
import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.ResponseException
import org.json.JSONObject
import java.util.*
data class BaseDTO(val text: String, val language: Locale, val zone: String, val volume: Int) {
data class BaseDTO(val text: String, val language: Locale) : DTO() {
companion object {
fun fromJSON(json: String): BaseDTO {
val root = JSONObject(json)
fun fromJSON(json: String) = JSONObject(json).let {root ->
val language = root.nullableString("language") ?.let { Locale(it) } ?: Locale.US
val text = root.requiredString("text")
val language = root.optString("language")
.takeIf { it.isNotBlank() }
?.let { Locale(it) }
?: Locale.US
val text = root.optString("text") ?: throw ResponseException(
NanoHTTPD.Response.Status.BAD_REQUEST,
""
)
val zone = root.optString("zone") ?: throw ResponseException(
NanoHTTPD.Response.Status.BAD_REQUEST,
""
)
val volume = root.optInt("volume", 50)
return BaseDTO(
text,
language,
zone,
volume
)
BaseDTO(text, language)
}
}
}

View File

@@ -0,0 +1,16 @@
package io.bartek.ttsserver.web.dto
import fi.iki.elonen.NanoHTTPD.Response
import fi.iki.elonen.NanoHTTPD.ResponseException
import org.json.JSONObject
abstract class DTO {
companion object {
fun JSONObject.requiredString(key: String) = this.nullableString(key)
?: throw ResponseException(Response.Status.BAD_REQUEST, "")
fun JSONObject.nullableString(key: String) = this.optString(key)
.takeIf { it.isNotBlank() }
}
}

View File

@@ -1,25 +1,18 @@
package io.bartek.ttsserver.web.dto
import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.ResponseException
import org.json.JSONObject
import java.util.*
data class SonosDTO(val text: String, val language: Locale) {
data class SonosDTO(val text: String, val language: Locale, val zone: String, val volume: Int) :
DTO() {
companion object {
fun fromJSON(json: String): SonosDTO {
val root = JSONObject(json)
fun fromJSON(json: String) = JSONObject(json).let { root ->
val language = root.nullableString("language") ?.let { Locale(it) } ?: Locale.US
val text = root.requiredString("text")
val zone = root.requiredString("zone")
val volume = root.optInt("volume", 50)
val language = root.optString("language")
.takeIf { it.isNotBlank() }
?.let { Locale(it) }
?: Locale.US
val text = root.optString("text") ?: throw ResponseException(
NanoHTTPD.Response.Status.BAD_REQUEST,
""
)
return SonosDTO(text, language)
SonosDTO(text, language, zone, volume)
}
}
}

View File

@@ -10,7 +10,7 @@ import fi.iki.elonen.NanoHTTPD.Response.Status.*
import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.service.ForegroundService
import io.bartek.ttsserver.service.ServiceState
import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.sonos.queue.SonosQueue
import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.tts.status.TTSStatus
import io.bartek.ttsserver.web.dto.BaseDTO
@@ -73,7 +73,7 @@ class WebServer(
}
val (text, language) = extractBody(session) {
SonosDTO.fromJSON(
BaseDTO.fromJSON(
it
)
}
@@ -103,7 +103,7 @@ class WebServer(
}
val (text, language) = extractBody(session) {
SonosDTO.fromJSON(
BaseDTO.fromJSON(
it
)
}
@@ -127,7 +127,7 @@ class WebServer(
}
val data = extractBody(session) {
BaseDTO.fromJSON(
SonosDTO.fromJSON(
it
)
}

View File

@@ -3,7 +3,7 @@ package io.bartek.ttsserver.web.server
import android.content.Context
import android.content.SharedPreferences
import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.sonos.queue.SonosQueue
import io.bartek.ttsserver.tts.engine.TTSEngine
class WebServerFactory(