commit 06/11/2025

This commit is contained in:
2025-11-06 11:31:02 +07:00
parent 72c509feec
commit b24c153615
34 changed files with 719 additions and 174 deletions

View File

@@ -401,7 +401,7 @@ class MainExtension01 {
"[ETAD]" -> {
val values = variables["ETAD"].orEmpty().split(":").map { it.trim() }.filter { IsNumber(it) }
println("ETAD values: $values")
//println("ETAD values: $values")
if (values.size == 2) {
if (IsNumber(values[0]) && IsNumber(values[1])) {
val _h = values[0].toInt()

View File

@@ -16,9 +16,12 @@ import contentCache
import org.tinylog.Logger
@Suppress("unused")
class AudioPlayer (var samplingrate: Int) {
class AudioPlayer (var samplingrate: Int = 44100) {
val bass: Bass = Bass.Instance
val bassenc : BassEnc = BassEnc.Instance
val bassencmp3: BassEncMP3 = BassEncMP3.Instance
val bassencopus: BassEncOpus = BassEncOpus.Instance
val bassencogg : BassEncOGG = BassEncOGG.Instance
var initedDevice = -1
@@ -28,6 +31,9 @@ class AudioPlayer (var samplingrate: Int) {
if (samplingrate<1) samplingrate = 44100 // Default sampling rate
Logger.info {"Bass version ${Integer.toHexString(bass.BASS_GetVersion())}"}
Logger.info { "BassEnc version ${Integer.toHexString(bassenc.BASS_Encode_GetVersion())}" }
Logger.info { "BassEncMP3 version ${Integer.toHexString(bassencmp3.BASS_Encode_MP3_GetVersion())}" }
Logger.info { "BassEncOpus version ${Integer.toHexString(bassencopus.BASS_Encode_OPUS_GetVersion())}" }
Logger.info {" BassEncOGG version ${Integer.toHexString(bassencogg.BASS_Encode_OGG_GetVersion())}"}
InitAudio(0) // Audio 0 is No Sound, use for reading and writing wav silently
}

View File

@@ -88,7 +88,7 @@ public interface BassEnc extends Library {
* @param length number of bytes
* @param user the user pointer passed to BASS_Encode_Start
*/
void ENCODEPROC(int encoderhandle, int channelhandle, Memory encodedData, int length, Pointer user);
void ENCODEPROC(int encoderhandle, int channelhandle, Pointer encodedData, int length, Pointer user);
}
interface ENCODEPROCEX extends Callback {
@@ -101,7 +101,7 @@ public interface BassEnc extends Library {
* @param offset file offset of the data
* @param user the user pointer passed to BASS_Encode_Start
*/
void ENCODEPROCEX(int handle, int channel, Memory buffer, int length, long offset, Object user);
void ENCODEPROCEX(int handle, int channel, Pointer buffer, int length, long offset, Pointer user);
}
interface ENCODERPROC extends Callback {
@@ -115,7 +115,7 @@ public interface BassEnc extends Library {
* @param user the user pointer passed to BASS_Encode_Start
* @return the amount of encoded data (-1 = stop)
*/
int ENCODERPROC(int encoderHandle, int channelHandle, Memory encodedData, int length, int maxOut, Pointer user);
int ENCODERPROC(int encoderHandle, int channelHandle, Pointer encodedData, int length, int maxOut, Pointer user);
}

13
src/audio/BassEncMP3.java Normal file
View File

@@ -0,0 +1,13 @@
package audio;
import com.sun.jna.Library;
@SuppressWarnings("unused")
public interface BassEncMP3 extends Library {
BassEncMP3 Instance = (BassEncMP3) com.sun.jna.Native.load("bassenc_mp3", BassEncMP3.class);
int BASS_Encode_MP3_GetVersion();
int BASS_Encode_MP3_Start(int handle, String options, int flags, BassEnc.ENCODEPROCEX proc, Object user);
int BASS_Encode_MP3_StartFile(int handle, String options, int flags, String filename);
}

15
src/audio/BassEncOGG.java Normal file
View File

@@ -0,0 +1,15 @@
package audio;
import com.sun.jna.Library;
@SuppressWarnings("unused")
public interface BassEncOGG extends Library {
BassEncOGG Instance = (BassEncOGG) com.sun.jna.Native.load("bassenc_ogg", BassEncOGG.class);
int BASS_ENCODE_OGG_RESET = 0x1000000;
int BASS_Encode_OGG_GetVersion();
int BASS_Encode_OGG_Start(int handle, String options, int flags, BassEnc.ENCODEPROC proc, Object user);
int BASS_Encode_OGG_StartFile(int handle, String options, int flags, String filename);
boolean BASS_Encode_OGG_NewStream(int handle, String options, int flags);
}

View File

@@ -0,0 +1,18 @@
package audio;
import com.sun.jna.Library;
@SuppressWarnings("unused")
public interface BassEncOpus extends Library {
BassEncOpus Instance = (BassEncOpus) com.sun.jna.Native.load("bassenc_opus", BassEncOpus.class);
int BASS_ENCODE_OPUS_RESET = 0x1000000;
int BASS_ENCODE_OPUS_CTLONLY = 0x2000000;
int BASS_Encode_OPUS_GetVersion();
int BASS_Encode_OPUS_Start(int handle, String options, int flags, BassEnc.ENCODEPROC proc, Object user);
int BASS_Encode_OPUS_StartFile(int handle, String options, int flags, String filename);
boolean BASS_Encode_OPUS_NewStream(int handle, String options, int flags);
}

126
src/audio/Mp3Encoder.kt Normal file
View File

@@ -0,0 +1,126 @@
package audio
import audioPlayer
import com.sun.jna.Memory
import com.sun.jna.Pointer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.tinylog.Logger
import java.util.function.Consumer
@Suppress("unused")
class Mp3Encoder(val samplingrate: Int=44100, val channels: Int=1) {
var push_handle: Int = 0
var mp3_handle: Int = 0
private val bass = audioPlayer.bass
private val bassencmp3 = audioPlayer.bassencmp3
var callback : Consumer<ByteArray>? = null
/**
* Check if the encoder is started
* @return true if started, false otherwise
*/
fun isStarted() : Boolean {
return push_handle!=0 && mp3_handle!=0
}
val proc = BassEnc.ENCODEPROCEX{
enchandle, sourcehandle, buffer, length, offset, user ->
if (enchandle == mp3_handle) {
if (sourcehandle == push_handle) {
val data = ByteArray(length)
buffer.read(0, data, 0, length)
callback?.accept(data)
} else Logger.error { "MP3 Encoder callback called with unknown source handle: $sourcehandle" }
} else Logger.error { "MP3 Encoder callback called with unknown encoder handle: $enchandle" }
}
/**
* Start the MP3 encoder
* @param cb Function to receive encoded MP3 data
*/
fun Start(cb: Consumer<ByteArray>){
callback = null
push_handle = bass.BASS_StreamCreate(samplingrate, channels, Bass.BASS_STREAM_DECODE, Pointer(-1), null)
if (push_handle!=0){
Logger.info{"MP3 Encoder initialized with sampling rate $samplingrate Hz and $channels channel(s)" }
mp3_handle = bassencmp3.BASS_Encode_MP3_Start(push_handle, null, BassEnc.BASS_ENCODE_AUTOFREE,proc, null)
if (mp3_handle!=0){
callback = cb
CoroutineScope(Dispatchers.Default).launch {
val readsize = 4*1024 // 4 K buffer
Logger.info{"MP3 Encoder started successfully." }
while(isActive && push_handle!=0){
val p = Memory(readsize.toLong())
val read = bass.BASS_ChannelGetData(push_handle, p, readsize)
if (read==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to read data from MP3 Encoder stream. BASS error code: $err" }
break
}
delay(2)
}
Logger.info{"MP3 Encoder finished successfully." }
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to start MP3 Encoder. BASS error code: $err"}
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to initialize MP3 Encoder. BASS error code: $err" }
}
}
/**
* Push PCM data to be encoded
* @param data PCM data in ByteArray
* @return Number of bytes written, or 0 if failed
*/
fun PushData(data: ByteArray): Int {
if (push_handle==0){
Logger.error{"MP3 Encoder is not started." }
return 0
}
val mem = Memory(data.size.toLong())
mem.write(0, data, 0, data.size)
val written = bass.BASS_StreamPutData(push_handle, mem, data.size)
if (written==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to push data to MP3 Encoder. BASS error code: $err" }
return 0
}
//println("MP3 Encoder: Pushed $written bytes of PCM data.")
return written
}
/**
* Stop the MP3 encoder and free resources
*/
fun Stop(){
if (push_handle!=0){
val res = bass.BASS_StreamFree(push_handle)
if (res){
Logger.info{"MP3 Encoder stream freed successfully." }
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to free MP3 Encoder stream. BASS error code: $err"}
}
push_handle = 0
}
// auto close by BASS_ENCODE_AUTOFREE
mp3_handle = 0
callback = null
println("MP3 Encoder: Stopped.")
}
}

127
src/audio/OpusEncoder.kt Normal file
View File

@@ -0,0 +1,127 @@
package audio
import audioPlayer
import com.sun.jna.Memory
import com.sun.jna.Pointer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.tinylog.Logger
import java.util.function.Consumer
@Suppress("unused")
class OpusEncoder(val samplingrate: Int=44100, val channels: Int=1) {
var push_handle: Int = 0
var opus_handle: Int = 0
private val bass = audioPlayer.bass
private val bassencopus = audioPlayer.bassencopus
var callback : Consumer<ByteArray>? = null
/**
* Check if the encoder is started
* @return true if started, false otherwise
*/
fun isStarted() : Boolean {
return push_handle!=0 && opus_handle!=0
}
val proc = BassEnc.ENCODEPROC{
enchandle, sourcehandle, buffer, length, user ->
if (enchandle == opus_handle) {
if (sourcehandle == push_handle) {
val data = ByteArray(length)
buffer.read(0, data, 0, length)
callback?.accept(data)
//println("MP3 Encoder callback: Sent $length bytes")
} else Logger.error { "Opus Encoder callback called with unknown source handle: $sourcehandle" }
} else Logger.error { "Opus Encoder callback called with unknown encoder handle: $enchandle" }
}
/**
* Start the Opus encoder
* @param cb Function to receive encoded MP3 data
*/
fun Start(cb: Consumer<ByteArray>){
callback = null
push_handle = bass.BASS_StreamCreate(samplingrate, channels, Bass.BASS_STREAM_DECODE, Pointer(-1), null)
if (push_handle!=0){
Logger.info{"Opus Encoder initialized with sampling rate $samplingrate Hz and $channels channel(s)" }
opus_handle = bassencopus.BASS_Encode_OPUS_Start(push_handle, null, BassEnc.BASS_ENCODE_AUTOFREE, proc, null)
if (opus_handle!=0){
callback = cb
CoroutineScope(Dispatchers.Default).launch {
val readsize = 8 * 1024 * 1024 // 8 MB buffer
Logger.info{"Opus Encoder started successfully." }
while(isActive && push_handle!=0){
delay(2)
val p = Memory(readsize.toLong())
val read = bass.BASS_ChannelGetData(push_handle, p, readsize)
if (read==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to read data from Opus Encoder stream. BASS error code: $err" }
break
}
}
Logger.info{"Opus Encoder finished successfully." }
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to start Opus Encoder. BASS error code: $err"}
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to initialize Opus Encoder. BASS error code: $err" }
}
}
/**
* Push PCM data to be encoded
* @param data PCM data in ByteArray
* @return Number of bytes written, or 0 if failed
*/
fun PushData(data: ByteArray): Int {
if (push_handle==0){
Logger.error{"Opus Encoder is not started." }
return 0
}
val mem = Memory(data.size.toLong())
mem.write(0, data, 0, data.size)
val written = bass.BASS_StreamPutData(push_handle, mem, data.size)
if (written==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to push data to Opus Encoder. BASS error code: $err" }
return 0
}
//println("Opus Encoder: Pushed $written bytes of PCM data.")
return written
}
/**
* Stop the Opus encoder and free resources
*/
fun Stop(){
if (push_handle!=0){
val res = bass.BASS_StreamFree(push_handle)
if (res){
Logger.info{"Opus Encoder stream freed successfully." }
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to free Opus Encoder stream. BASS error code: $err"}
}
push_handle = 0
}
// auto close by BASS_ENCODE_AUTOFREE
opus_handle = 0
callback = null
println("Opus Encoder: Stopped.")
}
}

View File

@@ -1,5 +1,7 @@
package barix
import audio.Mp3Encoder
import audio.OpusEncoder
import codes.Somecodes
import com.fasterxml.jackson.databind.JsonNode
import kotlinx.coroutines.CoroutineScope
@@ -24,37 +26,37 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
private val inet = InetSocketAddress(ipaddress, port)
private val maxUDPsize = 1000
private var _tcp: Socket? = null
private val PipeOuts = mutableMapOf<String,PipedOutputStream>()
private val pipeOuts = mutableMapOf<String,PipedOutputStream>()
private val mp3encoder = Mp3Encoder()
fun AddPipeOut(key: String, pipeOut: PipedOutputStream) {
RemovePipeOut(key)
PipeOuts[key] = pipeOut
pipeOuts[key] = pipeOut
println("Added pipeOut $key to BarixConnection $channel ($ipaddress)")
}
fun RemovePipeOut(key: String) {
println("Removing pipeOut $key")
if (PipeOuts.contains(key)){
val pipe = PipeOuts[key]
if (pipeOuts.contains(key)){
val pipe = pipeOuts[key]
try {
pipe?.close()
} catch (e: Exception) {
// ignore
}
println("Removed pipeOut $key from BarixConnection $channel ($ipaddress)")
PipeOuts.remove(key)
pipeOuts.remove(key)
}
}
fun ClearPipeOuts() {
PipeOuts.values.forEach { piped ->
pipeOuts.values.forEach { piped ->
try {
piped.close()
} catch (e: Exception) {
// ignore
}
}
PipeOuts.clear()
pipeOuts.clear()
}
/**
* Buffer remain in bytes
@@ -137,34 +139,40 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
CoroutineScope(Dispatchers.IO).launch {
DatagramSocket().use{ udp ->
val bb = ByteBuffer.wrap(data)
if (!mp3encoder.isStarted()) {
mp3encoder.Start { data ->
pipeOuts.keys.forEach { kk ->
val pp = pipeOuts[kk]
try {
pp?.write(data)
pp?.flush()
println("Written ${data.size} bytes to pipeOut $kk")
} catch (e: Exception) {
Logger.error { "Failed to write to pipeOut $kk, message: ${e.message}" }
pp?.close()
pipeOuts.remove(kk)
}
}
}
}
while(bb.hasRemaining()){
try {
val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining())
bb.get(chunk)
//println("Buffer remain: $bufferRemain, sending chunk size: ${chunk.size}")
while(bufferRemain<chunk.size){
delay(10)
}
udp.send(DatagramPacket(chunk, chunk.size, inet))
mp3encoder.PushData(chunk)
delay(2)
PipeOuts.keys.forEach { kk ->
val pp = PipeOuts[kk]
try {
pp?.write(chunk)
pp?.flush()
println("Written ${chunk.size} bytes to pipeOut $kk")
} catch (e: Exception) {
Logger.error { "Failed to write to pipeOut $kk, message: ${e.message}" }
pp?.close()
PipeOuts.remove(kk)
}
}
} catch (e: Exception) {
cbFail.accept("SendData to $ipaddress failed, message: ${e.message}")
return@launch
}
}
mp3encoder.Stop()
cbOK.accept("SendData to $channel ($ipaddress) succeeded, ${data.size} bytes sent")
}
}

View File

@@ -49,13 +49,14 @@ import java.io.File
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.nio.file.Path
import java.util.UUID
@Suppress("unused")
class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val _config: configFile) {
lateinit var app: Javalin
lateinit var semiauto : Javalin
lateinit var semiauto: Javalin
val objectmapper = jacksonObjectMapper()
private fun SendReply(context: WsMessageContext, command: String, value: String) {
@@ -73,7 +74,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
}
private fun Start_WebServer(){
private fun Start_WebServer() {
Logger.info { "Starting Web Application on port $listenPort" }
app = Javalin.create { config ->
config.useVirtualThreads = true
@@ -253,15 +254,26 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
if (param.isNotEmpty()) {
val bc = Get_Barix_Connection_by_ZoneName(param)
if (bc != null) {
val key = ctx.req().remoteAddr + ":" + ctx.req().remotePort
val key = ctx.cookie("client-id") ?: UUID.randomUUID().toString()
.also { ctx.cookie("client-id", it) }
val pipeIN = PipedInputStream(8192)
val pipeOUT = PipedOutputStream(pipeIN)
// write WAV Header 44 bytes contains samplingrate 44100 Hz, 16 bit, mono
pipeOUT.write(Generate_WAV_Header())
// pipeOUT.write(Generate_WAV_Header())
bc.AddPipeOut(key, pipeOUT)
ctx.contentType("audio/wav")
ctx.header("Cache-Control", "no-cache")
ctx.contentType("audio/mpeg")
ctx.header("Cache-Control", "no-cache, no-store, must-revalidate")
ctx.header("Pragma", "no-cache")
ctx.header("Expires", "0")
// 🔥 This one is critical — tells Jetty to send data in HTTP chunks as it becomes available:
ctx.header("Transfer-Encoding", "chunked")
// Keeps the TCP socket open so the browser doesnt close after initial data
ctx.header("Connection", "keep-alive")
ctx.result(pipeIN)
println("LiveAudio Open for zone $param SUCCESS")
} else ctx.status(400)
@@ -273,17 +285,23 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
get("Close/{broadcastzone}") { ctx ->
val param = ctx.pathParam("broadcastzone")
println("LiveAudio Close for zone $param")
if (param.isNotEmpty()) {
val bc = Get_Barix_Connection_by_ZoneName(param)
if (bc != null) {
val key = ctx.req().remoteAddr + ":" + ctx.req().remotePort
bc.RemovePipeOut(key)
ctx.result(objectmapper.writeValueAsString(resultMessage("OK")))
println("LiveAudio Close for zone $param SUCCESS")
val key = ctx.cookie("client-id")
if (key != null && key.isNotEmpty()) {
if (param.isNotEmpty()) {
val bc = Get_Barix_Connection_by_ZoneName(param)
if (bc != null) {
bc.RemovePipeOut(key)
ctx.result(objectmapper.writeValueAsString(resultMessage("OK")))
println("LiveAudio Close for zone $param SUCCESS")
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Broadcastzone not found")))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Broadcastzone not found")))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid broadcastzone")))
.result(objectmapper.writeValueAsString(resultMessage("Invalid broadcastzone")))
} else {
ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("No client-id cookie found")))
}
}
}
path("VoiceType") {
@@ -2043,55 +2061,55 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
}.start(listenPort)
}
private fun Start_SemiAutoServer(){
Logger.info {"Starting SemiAuto web server at port ${listenPort+1}"}
semiauto = Javalin.create{ config ->
private fun Start_SemiAutoServer() {
Logger.info { "Starting SemiAuto web server at port ${listenPort + 1}" }
semiauto = Javalin.create { config ->
config.useVirtualThreads = true
config.staticFiles.add ("/semiauto")
config.staticFiles.add("/semiauto")
config.jsonMapper(JavalinJackson(jacksonObjectMapper()))
config.router.apiBuilder {
path("/"){
path("/") {
get {
it.cookie("semiauto-user","")
it.cookie("semiauto-user", "")
it.redirect("login.html")
}
}
path("logout"){
path("logout") {
get {
it.cookie("semiauto-user","")
it.cookie("semiauto-user", "")
it.redirect("login.html")
}
}
path("login.html"){
path("login.html") {
post {
val formuser = it.formParam("username") ?: ""
val formpass = it.formParam("password") ?: ""
if (formuser.isNotEmpty() && formpass.isNotEmpty()){
val user = db.userDB.List.find { u -> u.username==formuser && u.password==formpass }
if (user!=null){
it.cookie("semiauto-user",user.username)
if (formuser.isNotEmpty() && formpass.isNotEmpty()) {
val user = db.userDB.List.find { u -> u.username == formuser && u.password == formpass }
if (user != null) {
it.cookie("semiauto-user", user.username)
it.redirect("index.html")
} else ResultMessageString(it, 400, "Invalid username or password")
} else ResultMessageString(it, 400, "Username or password cannot be empty")
}
}
path("index.html"){
path("index.html") {
before { CheckSemiAutoUsers(it) }
}
path("log.html"){
path("log.html") {
before { CheckSemiAutoUsers(it) }
}
path("api"){
path("Initialize"){
path("api") {
path("Initialize") {
get { ctx ->
val username = ctx.cookie("semiauto-user") ?: ""
if (username.isNotEmpty()){
val user = db.userDB.List.find { u -> u.username==username }
if (user!=null){
if (username.isNotEmpty()) {
val user = db.userDB.List.find { u -> u.username == username }
if (user != null) {
val result = SemiAutoInitData(username)
// messages
String_To_List(user.messagebank_ann_id).forEach { msg ->
db.messageDB.List.filter{it.ANN_ID==msg.toUInt()}.forEach {xx ->
db.messageDB.List.filter { it.ANN_ID == msg.toUInt() }.forEach { xx ->
result.messages.add("${xx.ANN_ID};${xx.Description};${xx.Language};${xx.Message_Detail}")
}
}
@@ -2100,47 +2118,55 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
// cities
String_To_List(user.city_tags).forEach { ct ->
db.soundDB.List.firstOrNull { it.TAG == ct && it.Category == Category.City.name }
?.let{
result.cities.add("${it.TAG};${it.Description}")
}
?.let {
result.cities.add("${it.TAG};${it.Description}")
}
}
// airplane names
String_To_List(user.airline_tags).forEach { at ->
db.soundDB.List.firstOrNull { it.TAG == at && it.Category == Category.Airplane_Name.name }
?.let{
result.airlines.add("${it.TAG};${it.Description}")
}
?.let {
result.airlines.add("${it.TAG};${it.Description}")
}
}
// places
db.soundDB.List.filter{it.Category==Category.Places.name}.distinctBy { it.TAG }.forEach { xx ->
result.places.add("${xx.TAG};${xx.Description}")
}
db.soundDB.List.filter { it.Category == Category.Places.name }.distinctBy { it.TAG }
.forEach { xx ->
result.places.add("${xx.TAG};${xx.Description}")
}
// shalat
db.soundDB.List.filter{it.Category==Category.Shalat.name}.distinctBy { it.TAG }.forEach { xx ->
result.shalat.add("${xx.TAG};${xx.Description}")
}
db.soundDB.List.filter { it.Category == Category.Shalat.name }.distinctBy { it.TAG }
.forEach { xx ->
result.shalat.add("${xx.TAG};${xx.Description}")
}
// sequences
db.soundDB.List.filter{it.Category==Category.Sequence.name}.distinctBy { it.TAG }.forEach { xx ->
db.soundDB.List.filter { it.Category == Category.Sequence.name }
.distinctBy { it.TAG }.forEach { xx ->
result.sequences.add("${xx.TAG};${xx.Description}")
}
// reasons
db.soundDB.List.filter{it.Category==Category.Reason.name}.distinctBy { it.TAG }.forEach { xx ->
result.reasons.add("${xx.TAG};${xx.Description}")
}
db.soundDB.List.filter { it.Category == Category.Reason.name }.distinctBy { it.TAG }
.forEach { xx ->
result.reasons.add("${xx.TAG};${xx.Description}")
}
// procedures
db.soundDB.List.filter{it.Category==Category.Procedure.name}.distinctBy { it.TAG }.forEach { xx ->
db.soundDB.List.filter { it.Category == Category.Procedure.name }
.distinctBy { it.TAG }.forEach { xx ->
result.procedures.add("${xx.TAG};${xx.Description}")
}
// gates
db.soundDB.List.filter{it.Category==Category.Gate.name}.distinctBy { it.TAG }.forEach { xx ->
result.gates.add("${xx.TAG};${xx.Description}")
}
db.soundDB.List.filter { it.Category == Category.Gate.name }.distinctBy { it.TAG }
.forEach { xx ->
result.gates.add("${xx.TAG};${xx.Description}")
}
// compensation
db.soundDB.List.filter{it.Category==Category.Compensation.name}.distinctBy { it.TAG }.forEach { xx ->
db.soundDB.List.filter { it.Category == Category.Compensation.name }
.distinctBy { it.TAG }.forEach { xx ->
result.compensation.add("${xx.TAG};${xx.Description}")
}
// greetings
db.soundDB.List.filter{it.Category==Category.Greeting.name}.distinctBy { it.TAG }.forEach { xx ->
db.soundDB.List.filter { it.Category == Category.Greeting.name }
.distinctBy { it.TAG }.forEach { xx ->
result.greetings.add("${xx.TAG};${xx.Description}")
}
@@ -2149,18 +2175,18 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
} else ResultMessageString(ctx, 400, "Username is empty")
}
}
path("SemiAuto"){
path("SemiAuto") {
post { ctx ->
val json : JsonNode = objectmapper.readTree(ctx.body())
val json: JsonNode = objectmapper.readTree(ctx.body())
// butuh description, languages, tags, dan broadcastzones
val description = json.get("description").asText("")
val languages = json.get("languages").asText("")
val tags = json.get("tags").asText("")
val broadcastzones = json.get("broadcastzones").asText("")
if (description.isNotEmpty()){
if (languages.isNotEmpty()){
if (tags.isNotEmpty()){
if (broadcastzones.isNotEmpty()){
if (description.isNotEmpty()) {
if (languages.isNotEmpty()) {
if (tags.isNotEmpty()) {
if (broadcastzones.isNotEmpty()) {
val qt = QueueTable(
0u,
LocalDateTime.now().format(datetimeformat1),
@@ -2172,10 +2198,10 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
1u,
languages
)
if (db.queuetableDB.Add(qt)){
if (db.queuetableDB.Add(qt)) {
db.queuetableDB.Resort()
Logger.info{"SemiAutoWeb added to queue table: $qt" }
println("SemiAuto added to queue table: ${objectmapper.writeValueAsString(qt)}")
Logger.info { "SemiAutoWeb added to queue table: $qt" }
//println("SemiAuto added to queue table: ${objectmapper.writeValueAsString(qt)}")
ctx.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else ResultMessageString(ctx, 500, "Failed to add to queue table")
} else ResultMessageString(ctx, 400, "Broadcast zones cannot be empty")
@@ -2185,13 +2211,12 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
}
}
path("Log"){
get("/{datelog}"){ ctx ->
path("Log") {
get("/{datelog}") { ctx ->
val datelog = ctx.pathParam("datelog")
if (ValidDate(datelog)){
if (ValidDate(datelog)) {
println("SemiAuto Get Log for date $datelog")
db.GetLogForHtml(datelog){
loghtml ->
db.GetLogForHtml(datelog) { loghtml ->
val resultstring = objectmapper.writeValueAsString(loghtml)
println("Log HTML for date $datelog: $resultstring")
ctx.result(resultstring)
@@ -2202,18 +2227,19 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val
}
}
}
}.start(listenPort+1)
}.start(listenPort + 1)
}
fun ResultMessageString(ctx: Context, code: Int, message: String) {
ctx.status(code).result(objectmapper.writeValueAsString(resultMessage(message)))
}
fun CheckSemiAutoUsers(ctx: Context){
fun CheckSemiAutoUsers(ctx: Context) {
val user = ctx.cookie("semiauto-user")
if (user == null) {
ctx.redirect("login.html")
}
val foundUser = db.userDB.List.find { u -> u.username==user }
val foundUser = db.userDB.List.find { u -> u.username == user }
if (foundUser == null) {
ctx.redirect("login.html")
}