diff --git a/app/build.gradle b/app/build.gradle
index 7a99dd6..68e3258 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -38,6 +38,7 @@ dependencies {
implementation 'com.google.dagger:dagger-android:2.15'
implementation 'com.google.dagger:dagger-android-support:2.15'
implementation 'com.github.adrielcafe:AndroidAudioConverter:0.0.8'
+ implementation 'org.luaj:luaj-jse:3.0.1'
kapt 'com.google.dagger:dagger-android-processor:2.15'
kapt 'com.google.dagger:dagger-compiler:2.15'
testImplementation 'junit:junit:4.12'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 795f887..63ba4a5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@
+
{
+ val scripts = context.getExternalFilesDir("Endpoints")?.listFiles() ?: emptyArray()
+
+ return scripts
+ .map { sandboxFactory.createSandbox().loadfile(it.absolutePath).call() }
+ .map { it as? LuaTable ?: throw IllegalArgumentException("Expected single table to be returned") }
+ .map { createEndpoint(it) }
+ }
+
+ private fun createEndpoint(luaTable: LuaTable) = Endpoint(
+ uri = parseUri(luaTable),
+ method = parseMethod(luaTable),
+ type = parseType(luaTable),
+ accepts = parseAccepts(luaTable),
+ consumer = parseConsumer(luaTable)
+ )
+
+ private fun parseUri(luaTable: LuaTable) = luaTable.get("uri")
+ .takeIf { it !is LuaNil }
+ ?.let { it as? LuaString ?: throw IllegalArgumentException("'uri' must be of string type'") }
+ ?.tojstring()
+ ?.let { UriTemplate.parse(it) }
+ ?: throw IllegalArgumentException("'uri' field is required")
+
+ private fun parseConsumer(luaTable: LuaTable) = luaTable.get("consumer")
+ .takeIf { it !is LuaNil }
+ ?.let { it as? LuaClosure ?: throw IllegalArgumentException("'consumer' must be a function'") }
+ ?: throw IllegalArgumentException("'consumer' field is required")
+
+ private fun parseAccepts(luaTable: LuaTable) = luaTable.get("accepts")
+ .takeIf { it !is LuaNil }
+ ?.let { it as? LuaString ?: throw IllegalArgumentException("'accepts' must be of string type'") }
+ ?.tojstring()
+ ?: "text/plain"
+
+ private fun parseType(luaTable: LuaTable) = luaTable.get("type")
+ .takeIf { it !is LuaNil }
+ ?.let { it as? LuaString ?: throw IllegalArgumentException("'type' must be of string type'") }
+ ?.let { EndpointType.valueOf(it.tojstring()) }
+ ?: EndpointType.DEFAULT
+
+ private fun parseMethod(luaTable: LuaTable) = luaTable.get("method")
+ .takeIf { it !is LuaNil }
+ ?.let { it as? LuaString ?: throw IllegalArgumentException("'method' must be of string type'") }
+ ?.let { Method.valueOf(it.tojstring()) }
+ ?: Method.GET
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/lua/sandbox/SandboxFactory.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/lua/sandbox/SandboxFactory.kt
new file mode 100644
index 0000000..690cd1d
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/lua/sandbox/SandboxFactory.kt
@@ -0,0 +1,31 @@
+package com.bartlomiejpluta.ttsserver.core.lua.sandbox
+
+import com.bartlomiejpluta.ttsserver.core.lua.lib.HTTPLibrary
+import com.bartlomiejpluta.ttsserver.core.lua.lib.TTSLibrary
+import org.luaj.vm2.Globals
+import org.luaj.vm2.LoadState
+import org.luaj.vm2.compiler.LuaC
+import org.luaj.vm2.lib.PackageLib
+import org.luaj.vm2.lib.StringLib
+import org.luaj.vm2.lib.TableLib
+import org.luaj.vm2.lib.jse.JseBaseLib
+import org.luaj.vm2.lib.jse.JseMathLib
+import org.luaj.vm2.lib.jse.JseOsLib
+
+class SandboxFactory(
+ private val httpLibrary: HTTPLibrary,
+ private val ttsLibrary: TTSLibrary
+) {
+ fun createSandbox() = Globals().also {
+ it.load(JseBaseLib())
+ it.load(PackageLib())
+ it.load(TableLib())
+ it.load(StringLib())
+ it.load(JseMathLib())
+ it.load(JseOsLib())
+ it.load(httpLibrary)
+ it.load(ttsLibrary)
+ LoadState.install(it)
+ LuaC.install(it)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
index b0cef44..115e9da 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
@@ -4,7 +4,7 @@ import android.content.SharedPreferences
import cafe.adriel.androidaudioconverter.model.AudioFormat
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
-import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
+import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpointx
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
@@ -20,8 +20,8 @@ class SonosWorker(
private val preferences: SharedPreferences,
private val queue: BlockingQueue
) : Runnable {
- private val gongUrl: String get() = address + Endpoint.GONG.trimmedUri
- private val announcementUrl: String get() = address + Endpoint.SONOS_CACHE.trimmedUri
+ private val gongUrl: String get() = address + Endpointx.GONG.trimmedUri
+ private val announcementUrl: String get() = address + Endpointx.SONOS_CACHE.trimmedUri
private var snapshot: Snapshot? = null
override fun run() = try {
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
index b10cb45..c3ec146 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
@@ -1,22 +1,92 @@
package com.bartlomiejpluta.ttsserver.core.web.endpoint
-enum class Endpoint(val uri: String, val id: Int) {
- UNKNOWN("/", 1),
- SAY("/say", 2),
- WAVE("/wave", 3),
- AAC("/aac", 4),
- MP3("/mp3", 5),
- M4A("/m4a", 6),
- WMA("/wma", 7),
- FLAC("/flac", 8),
- SONOS("/sonos", 9),
- SONOS_CACHE("/sonos/*", 10),
- GONG("/gong.wav", 11);
+import com.bartlomiejpluta.ttsserver.core.web.uri.UriTemplate
+import fi.iki.elonen.NanoHTTPD.*
+import org.luaj.vm2.LuaClosure
+import org.luaj.vm2.LuaTable
+import org.luaj.vm2.LuaValue
+import java.io.BufferedInputStream
+import java.io.File
+import java.io.FileInputStream
- val trimmedUri: String
- get() = uri.replace("*", "")
+class Endpoint(
+ private val uri: UriTemplate,
+ private val type: EndpointType,
+ private val accepts: String,
+ private val method: Method,
+ private val consumer: LuaClosure
+) {
- companion object {
- fun of(ordinal: Int) = values().firstOrNull { it.ordinal == ordinal } ?: UNKNOWN
+ fun hit(session: IHTTPSession): Response? {
+ if (session.method != method) {
+ return null
+ }
+
+ val matchingResult = uri.match(session.uri)
+ if (!matchingResult.matched) {
+ return null
+ }
+
+ val params = LuaValue.tableOf().also { params ->
+ matchingResult.variables
+ .map { LuaValue.valueOf(it.key) to LuaValue.valueOf(it.value) }
+ .forEach { params.set(it.first, it.second) }
+ }
+
+
+ val response = consumer.call(LuaValue.valueOf(extractBody(session)), params).checktable()
+ return parseResponse(response)
+ }
+
+ private fun parseResponse(response: LuaValue) = response
+ .let {
+ it as? LuaTable
+ ?: throw IllegalArgumentException("Invalid type for response - expected table")
+ }
+ .let { provideResponse(it) }
+
+
+ private fun provideResponse(response: LuaTable) =
+ when (response.get("type").checkjstring()) {
+ ResponseType.TEXT.name -> getTextResponse(response)
+ ResponseType.FILE.name -> getFileResponse(response)
+ else -> throw IllegalArgumentException("Unknown value for type in response")
+ }
+
+ private fun getTextResponse(response: LuaTable) = newFixedLengthResponse(
+ getStatus(response),
+ getMimeType(response),
+ getData(response)
+ )
+
+ private fun getFileResponse(response: LuaTable): Response? {
+ val file = File(response.get("file").checkstring().tojstring())
+ val stream = BufferedInputStream(FileInputStream(file))
+ val length = file.length()
+ return newFixedLengthResponse(
+ getStatus(response),
+ getMimeType(response),
+ stream,
+ length
+ )
+ }
+
+ private fun getStatus(response: LuaTable): Response.Status {
+ val status = response.get("status").checkint()
+ return Response.Status
+ .values()
+ .firstOrNull { it.requestStatus == status }
+ ?: throw IllegalArgumentException("Unsupported status: $status")
+ }
+
+ private fun getMimeType(response: LuaTable) = response.get("mime").checkstring().tojstring()
+
+ private fun getData(response: LuaTable) = response.get("data").checkstring().tojstring()
+
+ private fun extractBody(session: IHTTPSession): String {
+ return mutableMapOf().let {
+ session.parseBody(it)
+ it["postData"] ?: ""
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
index 1eb1626..8f2e3e1 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
@@ -7,11 +7,11 @@ object EndpointMatcher {
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
init {
- Endpoint.values().forEach {
+ Endpointx.values().forEach {
uriMatcher.addURI("", it.uri, it.ordinal)
}
}
fun match(uri: String) =
- Endpoint.of(uriMatcher.match(Uri.parse("content://$uri")))
+ Endpointx.of(uriMatcher.match(Uri.parse("content://$uri")))
}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointType.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointType.kt
new file mode 100644
index 0000000..1708406
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointType.kt
@@ -0,0 +1,6 @@
+package com.bartlomiejpluta.ttsserver.core.web.endpoint
+
+enum class EndpointType {
+ DEFAULT,
+ QUEUE
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpointx.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpointx.kt
new file mode 100644
index 0000000..e98398a
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpointx.kt
@@ -0,0 +1,22 @@
+package com.bartlomiejpluta.ttsserver.core.web.endpoint
+
+enum class Endpointx(val uri: String, val id: Int) {
+ UNKNOWN("/", 1),
+ SAY("/say", 2),
+ WAVE("/wave", 3),
+ AAC("/aac", 4),
+ MP3("/mp3", 5),
+ M4A("/m4a", 6),
+ WMA("/wma", 7),
+ FLAC("/flac", 8),
+ SONOS("/sonos", 9),
+ SONOS_CACHE("/sonos/*", 10),
+ GONG("/gong.wav", 11);
+
+ val trimmedUri: String
+ get() = uri.replace("*", "")
+
+ companion object {
+ fun of(ordinal: Int) = values().firstOrNull { it.ordinal == ordinal } ?: UNKNOWN
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/ResponseType.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/ResponseType.kt
new file mode 100644
index 0000000..ba98028
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/ResponseType.kt
@@ -0,0 +1,6 @@
+package com.bartlomiejpluta.ttsserver.core.web.endpoint
+
+enum class ResponseType {
+ TEXT,
+ FILE
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
index 56d1b02..6e384b7 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
@@ -3,19 +3,14 @@ package com.bartlomiejpluta.ttsserver.core.web.server
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
-import android.net.Uri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import cafe.adriel.androidaudioconverter.model.AudioFormat
+import com.bartlomiejpluta.ttsserver.core.lua.loader.EndpointLoader
+import com.bartlomiejpluta.ttsserver.core.lua.sandbox.SandboxFactory
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
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.web.dto.BaseDTO
-import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
-import com.bartlomiejpluta.ttsserver.core.web.endpoint.EndpointMatcher
import com.bartlomiejpluta.ttsserver.core.web.exception.WebException
-import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
@@ -23,10 +18,6 @@ import com.bartlomiejpluta.ttsserver.ui.preference.model.TimeRange
import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.Response.Status.*
import org.json.JSONObject
-import java.io.BufferedInputStream
-import java.io.File
-import java.io.FileInputStream
-import java.util.*
class WebServer(
@@ -34,7 +25,8 @@ class WebServer(
private val context: Context,
private val preferences: SharedPreferences,
private val tts: TTSEngine,
- private val sonos: SonosQueue
+ private val sonos: SonosQueue,
+ private val endpoints: List
) : NanoHTTPD(port) {
private val speakersSilenceSchedulerEnabled: Boolean
get() = preferences.getBoolean(PreferenceKey.ENABLE_SPEAKERS_SILENCE_SCHEDULER, false)
@@ -72,20 +64,12 @@ class WebServer(
}
}
- private fun dispatch(it: IHTTPSession): Response {
- return when (EndpointMatcher.match(it.uri)) {
- Endpoint.SAY -> say(it)
- Endpoint.WAVE -> file(it, AudioFormat.WAV)
- Endpoint.AAC -> file(it, AudioFormat.AAC)
- Endpoint.MP3 -> file(it, AudioFormat.MP3)
- Endpoint.M4A -> file(it, AudioFormat.M4A)
- Endpoint.WMA -> file(it, AudioFormat.WMA)
- Endpoint.FLAC -> file(it, AudioFormat.FLAC)
- Endpoint.SONOS -> sonos(it)
- Endpoint.SONOS_CACHE -> sonosCache(it)
- Endpoint.GONG -> gong(it)
- Endpoint.UNKNOWN -> throw WebException(NOT_FOUND)
+ private fun dispatch(session: IHTTPSession): Response {
+ for (endpoint in endpoints) {
+ endpoint.hit(session)?.let { return it }
}
+
+ throw WebException(NOT_FOUND)
}
private fun handleWebException(e: WebException) =
@@ -100,28 +84,28 @@ class WebServer(
return newFixedLengthResponse(INTERNAL_ERROR, MIME_PLAINTEXT, stacktrace)
}
- private fun say(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_SAY_ENDPOINT, true)) {
- throw WebException(NOT_FOUND)
- }
-
- if (session.method != Method.POST) {
- throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
- }
-
- if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
- throw WebException(BAD_REQUEST, "Only JSON data is accepted")
- }
-
- if (speakersSilenceSchedulerEnabled && speakersSilenceSchedule.inRange(Calendar.getInstance())) {
- return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
- }
-
- val dto = extractBody(session) { BaseDTO(it) }
-
- tts.performTTS(dto.text, dto.language)
- return newFixedLengthResponse(OK, MIME_JSON, SUCCESS_RESPONSE)
- }
+// private fun say(session: IHTTPSession): Response {
+// if (!preferences.getBoolean(PreferenceKey.ENABLE_SAY_ENDPOINT, true)) {
+// throw WebException(NOT_FOUND)
+// }
+//
+// if (session.method != Method.POST) {
+// throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
+// }
+//
+// if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
+// throw WebException(BAD_REQUEST, "Only JSON data is accepted")
+// }
+//
+// if (speakersSilenceSchedulerEnabled && speakersSilenceSchedule.inRange(Calendar.getInstance())) {
+// return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
+// }
+//
+// val dto = extractBody(session) { BaseDTO(it) }
+//
+// tts.performTTS(dto.text, dto.language)
+// return newFixedLengthResponse(OK, MIME_JSON, SUCCESS_RESPONSE)
+// }
private fun extractBody(session: IHTTPSession, provider: (String) -> T): T {
return mutableMapOf().let {
@@ -130,92 +114,92 @@ class WebServer(
}
}
- private fun file(session: IHTTPSession, audioFormat: AudioFormat): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_FILE_ENDPOINTS, true)) {
- throw WebException(NOT_FOUND)
- }
-
- if (session.method != Method.POST) {
- throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
- }
-
- if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
- throw WebException(BAD_REQUEST, "Only JSON data is accepted")
- }
-
- val dto = extractBody(session) { BaseDTO(it) }
-
- val (stream, size) = tts.fetchTTSStream(dto.text, dto.language, audioFormat)
- return newFixedLengthResponse(OK, MimeType.forAudioFormat(audioFormat).mimeType, stream, size)
- }
-
- private fun sonos(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_SONOS_ENDPOINT, true)) {
- throw WebException(NOT_FOUND)
- }
-
- if (session.method != Method.POST) {
- throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
- }
-
- if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
- throw WebException(BAD_REQUEST, "Only JSON data is accepted")
- }
-
- if (sonosSilenceSchedulerEnabled && sonosSilenceSchedule.inRange(Calendar.getInstance())) {
- return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
- }
-
- val dto = extractBody(session) { SonosDTO(it) }
-
- sonos.push(dto)
-
- return newFixedLengthResponse(ACCEPTED, MIME_JSON, QUEUED_RESPONSE)
- }
-
- private fun sonosCache(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_SONOS_ENDPOINT, true)) {
- throw WebException(NOT_FOUND)
- }
-
- if (session.method != Method.GET) {
- throw WebException(METHOD_NOT_ALLOWED, "Only GET methods are allowed")
- }
-
- val filename = Uri.parse(session.uri).lastPathSegment ?: throw WebException(BAD_REQUEST)
- val file = File(context.cacheDir, filename)
-
- if (!file.exists()) {
- throw WebException(NOT_FOUND)
- }
-
- val stream = BufferedInputStream(FileInputStream(file))
- val size = file.length()
- return newFixedLengthResponse(OK, MimeType.forFile(file).mimeType, 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, MimeType.WAV.mimeType, stream, size)
- }
+// private fun file(session: IHTTPSession, audioFormat: AudioFormat): Response {
+// if (!preferences.getBoolean(PreferenceKey.ENABLE_FILE_ENDPOINTS, true)) {
+// throw WebException(NOT_FOUND)
+// }
+//
+// if (session.method != Method.POST) {
+// throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
+// }
+//
+// if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
+// throw WebException(BAD_REQUEST, "Only JSON data is accepted")
+// }
+//
+// val dto = extractBody(session) { BaseDTO(it) }
+//
+// val (stream, size) = tts.fetchTTSStream(dto.text, dto.language, audioFormat)
+// return newFixedLengthResponse(OK, MimeType.forAudioFormat(audioFormat).mimeType, stream, size)
+// }
+//
+// private fun sonos(session: IHTTPSession): Response {
+// if (!preferences.getBoolean(PreferenceKey.ENABLE_SONOS_ENDPOINT, true)) {
+// throw WebException(NOT_FOUND)
+// }
+//
+// if (session.method != Method.POST) {
+// throw WebException(METHOD_NOT_ALLOWED, "Only POST methods are allowed")
+// }
+//
+// if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
+// throw WebException(BAD_REQUEST, "Only JSON data is accepted")
+// }
+//
+// if (sonosSilenceSchedulerEnabled && sonosSilenceSchedule.inRange(Calendar.getInstance())) {
+// return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
+// }
+//
+// val dto = extractBody(session) { SonosDTO(it) }
+//
+// sonos.push(dto)
+//
+// return newFixedLengthResponse(ACCEPTED, MIME_JSON, QUEUED_RESPONSE)
+// }
+//
+// private fun sonosCache(session: IHTTPSession): Response {
+// if (!preferences.getBoolean(PreferenceKey.ENABLE_SONOS_ENDPOINT, true)) {
+// throw WebException(NOT_FOUND)
+// }
+//
+// if (session.method != Method.GET) {
+// throw WebException(METHOD_NOT_ALLOWED, "Only GET methods are allowed")
+// }
+//
+// val filename = Uri.parse(session.uri).lastPathSegment ?: throw WebException(BAD_REQUEST)
+// val file = File(context.cacheDir, filename)
+//
+// if (!file.exists()) {
+// throw WebException(NOT_FOUND)
+// }
+//
+// val stream = BufferedInputStream(FileInputStream(file))
+// val size = file.length()
+// return newFixedLengthResponse(OK, MimeType.forFile(file).mimeType, 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, MimeType.WAV.mimeType, stream, size)
+// }
override fun start() {
super.start()
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServerFactory.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServerFactory.kt
index 055cb3b..43c9cfb 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServerFactory.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServerFactory.kt
@@ -2,6 +2,7 @@ package com.bartlomiejpluta.ttsserver.core.web.server
import android.content.Context
import android.content.SharedPreferences
+import com.bartlomiejpluta.ttsserver.core.lua.loader.EndpointLoader
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
@@ -10,13 +11,15 @@ class WebServerFactory(
private val preferences: SharedPreferences,
private val context: Context,
private val tts: TTSEngine,
- private val sonos: SonosQueue
+ private val sonos: SonosQueue,
+ private val endpointLoader: EndpointLoader
) {
- fun createWebServer() =
- WebServer(
- preferences.getInt(
- PreferenceKey.PORT,
- 8080
- ), context, preferences, tts, sonos
- )
+ fun createWebServer() = WebServer(
+ preferences.getInt(PreferenceKey.PORT, 8080),
+ context,
+ preferences,
+ tts,
+ sonos,
+ endpointLoader.loadEndpoints()
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/uri/UriTemplate.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/uri/UriTemplate.kt
index 074d907..4d7de12 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/uri/UriTemplate.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/uri/UriTemplate.kt
@@ -28,10 +28,9 @@ class UriTemplate private constructor(uri: String) {
variables.add(variableBuilder.toString())
}
- isVariable -> char.takeIf { it.isLetter() } ?: error(
- "Only letters are allowed as template",
- index + 1
- ).let { variableBuilder?.append(it) }
+ isVariable -> char.takeIf { it.isLetter() }
+ ?.let { variableBuilder?.append(it) }
+ ?: error("Only letters are allowed as template", index + 1)
else -> patternBuilder.append(char)
}
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/component/AppComponent.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/component/AppComponent.kt
index b0156bb..ce09872 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/component/AppComponent.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/component/AppComponent.kt
@@ -3,6 +3,7 @@ package com.bartlomiejpluta.ttsserver.di.component
import android.content.Context
import com.bartlomiejpluta.ttsserver.TTSApplication
import com.bartlomiejpluta.ttsserver.di.module.AndroidModule
+import com.bartlomiejpluta.ttsserver.di.module.LuaModule
import com.bartlomiejpluta.ttsserver.di.module.TTSModule
import dagger.BindsInstance
import dagger.Component
@@ -11,7 +12,7 @@ import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton
-@Component(modules = [AndroidSupportInjectionModule::class, AndroidModule::class, TTSModule::class])
+@Component(modules = [AndroidSupportInjectionModule::class, AndroidModule::class, TTSModule::class, LuaModule::class])
interface AppComponent : AndroidInjector {
@Component.Builder
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/LuaModule.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/LuaModule.kt
new file mode 100644
index 0000000..2824599
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/LuaModule.kt
@@ -0,0 +1,33 @@
+package com.bartlomiejpluta.ttsserver.di.module
+
+import android.content.Context
+import com.bartlomiejpluta.ttsserver.core.lua.lib.HTTPLibrary
+import com.bartlomiejpluta.ttsserver.core.lua.lib.TTSLibrary
+import com.bartlomiejpluta.ttsserver.core.lua.loader.EndpointLoader
+import com.bartlomiejpluta.ttsserver.core.lua.sandbox.SandboxFactory
+import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
+import dagger.Module
+import dagger.Provides
+import javax.inject.Singleton
+
+@Module
+class LuaModule {
+
+ @Provides
+ @Singleton
+ fun endpointLoader(context: Context, sandboxFactory: SandboxFactory) =
+ EndpointLoader(context, sandboxFactory)
+
+ @Provides
+ @Singleton
+ fun sandboxFactory(httpLibrary: HTTPLibrary, ttsLibrary: TTSLibrary) =
+ SandboxFactory(httpLibrary, ttsLibrary)
+
+ @Provides
+ @Singleton
+ fun httpLibrary() = HTTPLibrary()
+
+ @Provides
+ @Singleton
+ fun ttsLibrary(ttsEngine: TTSEngine) = TTSLibrary(ttsEngine)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
index 4a4f9b4..23d4549 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.content.SharedPreferences
import android.speech.tts.TextToSpeech
import androidx.preference.PreferenceManager
+import com.bartlomiejpluta.ttsserver.core.lua.loader.EndpointLoader
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatusHolder
@@ -43,12 +44,14 @@ class TTSModule {
preferences: SharedPreferences,
context: Context,
tts: TTSEngine,
- sonos: SonosQueue
+ sonos: SonosQueue,
+ endpointLoader: EndpointLoader
) = WebServerFactory(
preferences,
context,
tts,
- sonos
+ sonos,
+ endpointLoader
)
@Provides