Enable gong for /sonos endpoint

This commit is contained in:
2020-05-31 16:39:19 +02:00
parent 2621978ddd
commit ad07a25210
5 changed files with 62 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
package com.bartlomiejpluta.ttsserver.core.sonos.queue package com.bartlomiejpluta.ttsserver.core.sonos.queue
import android.content.SharedPreferences
import com.bartlomiejpluta.ttsserver.core.sonos.worker.SonosWorker import com.bartlomiejpluta.ttsserver.core.sonos.worker.SonosWorker
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.util.NetworkUtil import com.bartlomiejpluta.ttsserver.core.util.NetworkUtil
@@ -7,17 +8,25 @@ import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import java.util.concurrent.BlockingQueue import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
class SonosQueue(private val tts: TTSEngine, private val networkUtil: NetworkUtil) { class SonosQueue(
private val tts: TTSEngine,
private val preferences: SharedPreferences,
private val networkUtil: NetworkUtil
) {
private val queue: BlockingQueue<SonosDTO> = LinkedBlockingQueue() private val queue: BlockingQueue<SonosDTO> = LinkedBlockingQueue()
private var consumer: Thread? = null private var consumer: Thread? = null
fun run() { fun run() {
consumer?.interrupt() consumer?.interrupt()
consumer = Thread(SonosWorker(tts, networkUtil.serverAddress, queue)) consumer = createWorkerThread()
.also { it.name = "SonosQueue" }
consumer?.start() consumer?.start()
} }
private fun createWorkerThread(): Thread {
val worker = SonosWorker(tts, networkUtil.serverAddress, preferences, queue)
return Thread(worker).also { it.name = "SonosQueue" }
}
fun stop() { fun stop() {
consumer?.interrupt() consumer?.interrupt()
consumer = null consumer = null

View File

@@ -1,9 +1,12 @@
package com.bartlomiejpluta.ttsserver.core.sonos.worker package com.bartlomiejpluta.ttsserver.core.sonos.worker
import android.content.SharedPreferences
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey
import com.vmichalak.sonoscontroller.Snapshot import com.vmichalak.sonoscontroller.Snapshot
import com.vmichalak.sonoscontroller.SonosDevice import com.vmichalak.sonoscontroller.SonosDevice
import com.vmichalak.sonoscontroller.SonosDiscovery import com.vmichalak.sonoscontroller.SonosDiscovery
@@ -13,8 +16,11 @@ import java.util.concurrent.BlockingQueue
class SonosWorker( class SonosWorker(
private val tts: TTSEngine, private val tts: TTSEngine,
private val address: String, private val address: String,
private val preferences: SharedPreferences,
private val queue: BlockingQueue<SonosDTO> private val queue: BlockingQueue<SonosDTO>
) : Runnable { ) : Runnable {
private val gongUrl: String get() = address + Endpoint.GONG.trimmedUri
private val announcementUrl: String get() = address + Endpoint.SONOS_CACHE.trimmedUri
private var snapshot: Snapshot? = null private var snapshot: Snapshot? = null
override fun run() = try { override fun run() = try {
@@ -27,21 +33,30 @@ class SonosWorker(
private fun consume(data: SonosDTO) = private fun consume(data: SonosDTO) =
SonosDiscovery.discover().firstOrNull { it.zoneGroupState.name == data.zone }?.let { SonosDiscovery.discover().firstOrNull { it.zoneGroupState.name == data.zone }?.let {
updateSnapshotIfFirst(it)
val url = prepareTTSFile(data) val url = prepareTTSFile(data)
updateSnapshotIfFirst(it)
announce(it, data, url) announce(it, data, url)
restoreSnapshotIfLast() restoreSnapshotIfLast()
} }
private fun prepareTTSFile(data: SonosDTO): String { private fun prepareTTSFile(data: SonosDTO): String {
val filename = tts.createTTSFile(data.text, data.language).name val filename = tts.createTTSFile(data.text, data.language).name
return "$address/sonos/$filename" return "$announcementUrl$filename"
} }
private fun announce(device: SonosDevice, data: SonosDTO, url: String) { private fun announce(device: SonosDevice, data: SonosDTO, url: String) {
device.stop() device.stop()
device.volume = data.volume device.volume = data.volume
device.playUri(url, "")
if (preferences.getBoolean(PreferenceKey.ENABLE_GONG, false)) {
playUri(device, gongUrl)
}
playUri(device, url)
}
private fun playUri(device: SonosDevice, uri: String) {
device.playUri(uri, "")
while (device.playState != PlayState.STOPPED) { while (device.playState != PlayState.STOPPED) {
Thread.sleep(500) Thread.sleep(500)
} }

View File

@@ -5,7 +5,11 @@ enum class Endpoint(val uri: String, val id: Int) {
SAY("/say", 2), SAY("/say", 2),
WAVE("/wave", 3), WAVE("/wave", 3),
SONOS("/sonos", 4), SONOS("/sonos", 4),
SONOS_CACHE("/sonos/*", 5); SONOS_CACHE("/sonos/*", 5),
GONG("/gong.wav", 6);
val trimmedUri: String
get() = uri.replace("*", "")
companion object { companion object {
fun of(id: Int) = values().firstOrNull { it.id == id } ?: UNKNOWN fun of(id: Int) = values().firstOrNull { it.id == id } ?: UNKNOWN

View File

@@ -7,6 +7,7 @@ import android.net.Uri
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.tts.exception.TTSException
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatus import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatus
import com.bartlomiejpluta.ttsserver.core.web.dto.BaseDTO import com.bartlomiejpluta.ttsserver.core.web.dto.BaseDTO
import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
@@ -59,6 +60,7 @@ class WebServer(
Endpoint.WAVE -> wave(it) Endpoint.WAVE -> wave(it)
Endpoint.SONOS -> sonos(it) Endpoint.SONOS -> sonos(it)
Endpoint.SONOS_CACHE -> sonosCache(it) Endpoint.SONOS_CACHE -> sonosCache(it)
Endpoint.GONG -> gong(it)
Endpoint.UNKNOWN -> throw WebException(NOT_FOUND) Endpoint.UNKNOWN -> throw WebException(NOT_FOUND)
} }
} }
@@ -161,6 +163,29 @@ class WebServer(
return newFixedLengthResponse(OK, MIME_WAVE, stream, size) return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
} }
private fun gong(session: IHTTPSession): Response {
if (!preferences.getBoolean(PreferenceKey.ENABLE_GONG, false)) {
throw WebException(NOT_FOUND)
}
if (session.method != Method.GET) {
throw WebException(METHOD_NOT_ALLOWED, "Only GET methods are allowed")
}
val uri = Uri.parse(
preferences.getString(PreferenceKey.GONG, null) ?: throw TTSException()
)
val size = context.contentResolver.openFileDescriptor(uri, "r")?.statSize
?: throw TTSException()
val stream = BufferedInputStream(
context.contentResolver.openInputStream(uri) ?: throw TTSException()
)
return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
}
override fun start() { override fun start() {
super.start() super.start()
sonos.run() sonos.run()

View File

@@ -60,7 +60,8 @@ class TTSModule {
@Provides @Provides
@Singleton @Singleton
fun sonosQueue(tts: TTSEngine, networkUtil: NetworkUtil) = SonosQueue(tts, networkUtil) fun sonosQueue(tts: TTSEngine, preferences: SharedPreferences, networkUtil: NetworkUtil) =
SonosQueue(tts, preferences, networkUtil)
@Provides @Provides
@Singleton @Singleton