Compare commits
No commits in common. 'develop' and 'master' have entirely different histories.
65 changed files with 232 additions and 869 deletions
@ -1,4 +1,4 @@ |
|||||||
package tv.anypoint.proxy.model.adb |
package tv.anypoint.domain.adb |
||||||
|
|
||||||
enum class ChangeCommand { |
enum class ChangeCommand { |
||||||
CHANGE_API_ENDPOINT, |
CHANGE_API_ENDPOINT, |
||||||
@ -1,8 +1,8 @@ |
|||||||
package tv.anypoint.proxy.model.adb |
package tv.anypoint.domain.adb |
||||||
|
|
||||||
enum class ExtraKey(val string: String) { |
enum class ExtraKey(val string: String) { |
||||||
EXTRA_JSON("EXTRA_JSON"), |
EXTRA_JSON("EXTRA_JSON"), |
||||||
CHANGE_COMMAND("change.command"), |
CHANGE_COMMAND("change.command"), |
||||||
API_ENDPOINT("api.endpoint"), |
API_ENDPOINT("api.endpoint"), |
||||||
TEST_DEVICE("test.device") |
TEST_DEVICE("test.device") |
||||||
} |
} |
||||||
@ -1,5 +1,5 @@ |
|||||||
package tv.anypoint.proxy.model.adb |
package tv.anypoint.domain.adb |
||||||
|
|
||||||
enum class IntentAction(val string: String) { |
enum class IntentAction(val string: String) { |
||||||
CHANGE_TEST_PROPERTY("tv.anypoint.agent.app.CHANGE_TEST_PROPERTY") |
CHANGE_TEST_PROPERTY("tv.anypoint.agent.app.CHANGE_TEST_PROPERTY") |
||||||
} |
} |
||||||
@ -1,8 +1,5 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class AuthRequest( |
data class AuthRequest( |
||||||
var fingerPrint: String = "", |
var fingerPrint: String = "", |
||||||
val soId: Int = 0, |
val soId: Int = 0, |
||||||
@ -1,4 +1,4 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
data class Cue( |
data class Cue( |
||||||
var id: Long = 0, |
var id: Long = 0, |
||||||
@ -1,4 +1,4 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
enum class CueOwner { |
enum class CueOwner { |
||||||
PP, |
PP, |
||||||
@ -1,7 +1,7 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
enum class PlayType { |
enum class PlayType { |
||||||
DNP, |
DNP, |
||||||
LAZY_DNP, |
LAZY_DNP, |
||||||
STREAMING |
STREAMING |
||||||
} |
} |
||||||
@ -1,9 +1,6 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class PlayerStateCheckParam( |
data class PlayerStateCheckParam( |
||||||
val playerStateCheckDuration: Int = 1500, |
val playerStateCheckDuration: Int = 1500, |
||||||
val maxPlayerInvalidateInterval: Int = 300 |
val maxPlayerInvalidateInterval: Int = 300 |
||||||
) |
) |
||||||
@ -1,8 +1,5 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class ProgramProviderChannel( |
data class ProgramProviderChannel( |
||||||
val id: Int, |
val id: Int, |
||||||
val delay: Int = 0, |
val delay: Int = 0, |
||||||
@ -1,4 +1,4 @@ |
|||||||
package tv.anypoint.proxy.model.agent |
package tv.anypoint.domain.agent |
||||||
|
|
||||||
data class StateChangeLog( |
data class StateChangeLog( |
||||||
val soId: Int = 0, |
val soId: Int = 0, |
||||||
@ -1,8 +1,5 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class AdsResponse( |
data class AdsResponse( |
||||||
val status: AdListResponseStatus = AdListResponseStatus.OK, |
val status: AdListResponseStatus = AdListResponseStatus.OK, |
||||||
val ads: List<Ad> = listOf(), |
val ads: List<Ad> = listOf(), |
||||||
@ -1,4 +1,4 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
data class AdsSyncRequest( |
data class AdsSyncRequest( |
||||||
val deviceId: Long, |
val deviceId: Long, |
||||||
@ -1,9 +1,7 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
import tv.anypoint.domain.agent.PlayType |
||||||
import tv.anypoint.proxy.model.agent.PlayType |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class Asset( |
data class Asset( |
||||||
var assetId: Long = 0L, |
var assetId: Long = 0L, |
||||||
var crc: String = "0", |
var crc: String = "0", |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
|
data class AssetConvertResponse(val id: Long) |
||||||
@ -1,8 +1,5 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class Click( |
data class Click( |
||||||
val targetType: String, |
val targetType: String, |
||||||
val targetValue: String, |
val targetValue: String, |
||||||
@ -1,9 +1,6 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class TargetAd( |
data class TargetAd( |
||||||
val hours: String? = null, |
val hours: String? = null, |
||||||
val days: String? = null, |
val days: String? = null, |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
package tv.anypoint.domain.agent.ad |
||||||
|
|
||||||
|
data class VastResponse(val id: Long) |
||||||
@ -1,13 +0,0 @@ |
|||||||
package tv.anypoint.dsl |
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean |
|
||||||
import org.springframework.context.annotation.Configuration |
|
||||||
import org.springframework.context.annotation.Scope |
|
||||||
import tv.anypoint.dsl.model.TcContext |
|
||||||
|
|
||||||
@Configuration |
|
||||||
class TestCaseConfig { |
|
||||||
@Bean |
|
||||||
@Scope("singleton") |
|
||||||
fun tcContext(): TcContext = TcContext() |
|
||||||
} |
|
||||||
@ -0,0 +1,8 @@ |
|||||||
|
package tv.anypoint.dsl.model |
||||||
|
|
||||||
|
class LogInfo( |
||||||
|
val absoluteFilePath: String |
||||||
|
) { |
||||||
|
var cursor: Long = 0 |
||||||
|
var lastLine: Long = 0 |
||||||
|
} |
||||||
@ -1,12 +0,0 @@ |
|||||||
package tv.anypoint.dsl.model |
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
class Logcat( |
|
||||||
val logcatFilePath: String |
|
||||||
) { |
|
||||||
var startLine: Int = 0 |
|
||||||
var cursor: Int = 0 |
|
||||||
var lastLine: Int = 0 |
|
||||||
} |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
package tv.anypoint.dsl.model |
|
||||||
|
|
||||||
class TcContext { |
|
||||||
var tc: Tc = Tc() |
|
||||||
} |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
package tv.anypoint.dsl.model |
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable |
|
||||||
|
|
||||||
@Serializable |
|
||||||
data class TcFailEvent( |
|
||||||
val tcName: String, |
|
||||||
val errorMessage: String |
|
||||||
) |
|
||||||
@ -0,0 +1,11 @@ |
|||||||
|
package tv.anypoint.dsl.service |
||||||
|
|
||||||
|
import org.springframework.stereotype.Service |
||||||
|
import tv.anypoint.dsl.Adb |
||||||
|
|
||||||
|
@Service |
||||||
|
class AdbTransmitter { |
||||||
|
fun transmit(req: Adb) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -1,41 +0,0 @@ |
|||||||
package tv.anypoint.proxy.adapter |
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json |
|
||||||
import okhttp3.MediaType.Companion.toMediaType |
|
||||||
import org.springframework.context.annotation.Bean |
|
||||||
import org.springframework.context.annotation.Configuration |
|
||||||
import retrofit2.Call |
|
||||||
import retrofit2.Retrofit |
|
||||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory |
|
||||||
import retrofit2.http.Body |
|
||||||
import retrofit2.http.POST |
|
||||||
import retrofit2.http.Query |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import tv.anypoint.proxy.model.agent.ad.AdsResponse |
|
||||||
import tv.anypoint.proxy.model.agent.ad.AdsSyncRequest |
|
||||||
|
|
||||||
interface AssignAdapter { |
|
||||||
@POST("/v3/device/ads") |
|
||||||
fun ads( |
|
||||||
@Query("deviceId") deviceId: Long, |
|
||||||
@Query("freeStorage") freeStorage: Long = 0, |
|
||||||
@Query("usedStorage") usedStorage: Long = 0 |
|
||||||
): Call<AdsResponse> |
|
||||||
|
|
||||||
@POST("/v3/device/sync/ads") |
|
||||||
fun syncAds( |
|
||||||
@Body request: AdsSyncRequest |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
@Configuration |
|
||||||
class AssignAdapterConfiguration { |
|
||||||
@Bean |
|
||||||
fun assignAdapter(json: Json, applicationProperties: ApplicationProperties): AssignAdapter { |
|
||||||
val retrofit = Retrofit.Builder() |
|
||||||
.baseUrl(applicationProperties.endpoints.assign) |
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType())) |
|
||||||
.build() |
|
||||||
return retrofit.create(AssignAdapter::class.java) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,34 +0,0 @@ |
|||||||
package tv.anypoint.proxy.adapter |
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json |
|
||||||
import okhttp3.MediaType.Companion.toMediaType |
|
||||||
import org.springframework.context.annotation.Bean |
|
||||||
import org.springframework.context.annotation.Configuration |
|
||||||
import retrofit2.Call |
|
||||||
import retrofit2.Retrofit |
|
||||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory |
|
||||||
import retrofit2.http.Body |
|
||||||
import retrofit2.http.POST |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import tv.anypoint.proxy.model.agent.AuthRequest |
|
||||||
import tv.anypoint.proxy.model.agent.AuthResponse |
|
||||||
|
|
||||||
|
|
||||||
interface AuthAdapter { |
|
||||||
@POST("/v3/device/auth") |
|
||||||
fun auth( |
|
||||||
@Body request: AuthRequest |
|
||||||
): Call<AuthResponse> |
|
||||||
} |
|
||||||
|
|
||||||
@Configuration |
|
||||||
class AuthAdapterConfiguration { |
|
||||||
@Bean |
|
||||||
fun authAdapter(json: Json, applicationProperties: ApplicationProperties): AuthAdapter { |
|
||||||
val retrofit = Retrofit.Builder() |
|
||||||
.baseUrl(applicationProperties.endpoints.auth) |
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType())) |
|
||||||
.build() |
|
||||||
return retrofit.create(AuthAdapter::class.java) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,30 @@ |
|||||||
|
package tv.anypoint.proxy.adapter |
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient |
||||||
|
import org.springframework.web.bind.annotation.PostMapping |
||||||
|
import org.springframework.web.bind.annotation.RequestBody |
||||||
|
import org.springframework.web.bind.annotation.RequestParam |
||||||
|
import tv.anypoint.domain.agent.AuthRequest |
||||||
|
import tv.anypoint.domain.agent.AuthResponse |
||||||
|
import tv.anypoint.domain.agent.ad.AdsResponse |
||||||
|
import tv.anypoint.domain.agent.ad.AdsSyncRequest |
||||||
|
|
||||||
|
|
||||||
|
@FeignClient( |
||||||
|
name = "DeviceV3Adapter", |
||||||
|
path = "/v3/device" |
||||||
|
) |
||||||
|
interface DeviceV3Adapter { |
||||||
|
@PostMapping("/auth") |
||||||
|
fun auth(@RequestBody body: AuthRequest): AuthResponse |
||||||
|
|
||||||
|
@PostMapping("/ads") |
||||||
|
fun ads( |
||||||
|
@RequestParam(required = true) deviceId: Long, |
||||||
|
@RequestParam("freeStorage", required = true, defaultValue = "0") freeStorage: Long = 0, |
||||||
|
@RequestParam("usedStorage", required = true, defaultValue = "0") usedStorage: Long = 0 |
||||||
|
): AdsResponse |
||||||
|
|
||||||
|
@PostMapping("/sync/ads") |
||||||
|
fun syncAds(@RequestBody request: AdsSyncRequest) |
||||||
|
} |
||||||
@ -1,32 +0,0 @@ |
|||||||
package tv.anypoint.proxy.config |
|
||||||
|
|
||||||
import kotlinx.coroutines.launch |
|
||||||
import kotlinx.coroutines.runBlocking |
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.context.annotation.Bean |
|
||||||
import org.springframework.context.annotation.Configuration |
|
||||||
import se.vidstige.jadb.JadbDevice |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import tv.anypoint.proxy.util.JadbDeviceSerialUtil |
|
||||||
import tv.anypoint.proxy.util.NetworkUtil |
|
||||||
import tv.anypoint.proxy.util.getDevice |
|
||||||
import java.io.File |
|
||||||
|
|
||||||
@Configuration |
|
||||||
class ProxyApplicationConfig( |
|
||||||
private val applicationProperties: ApplicationProperties |
|
||||||
) { |
|
||||||
@Bean |
|
||||||
fun jadbDevice(): JadbDevice = getDevice( |
|
||||||
stbIp = applicationProperties.stb.ip, |
|
||||||
stbPort = applicationProperties.stb.port |
|
||||||
) |
|
||||||
|
|
||||||
@Bean |
|
||||||
fun serverIp(jadbDevice: JadbDevice): String = NetworkUtil.getPrivateIpV4( |
|
||||||
JadbDeviceSerialUtil.serialToAddress(jadbDevice.serial) |
|
||||||
?: throw RuntimeException("Can't get server ip address") |
|
||||||
) |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
|
||||||
|
|
||||||
data class AssetConvertResponse(val id: Long) |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
package tv.anypoint.proxy.model.agent.ad |
|
||||||
|
|
||||||
data class VastResponse(val id: Long) |
|
||||||
@ -1,6 +0,0 @@ |
|||||||
package tv.anypoint.proxy.model.push |
|
||||||
|
|
||||||
enum class PushCommandType(val string: String) { |
|
||||||
UPDATE_ASSET_COMMAND("UpdateAssetCommand"), |
|
||||||
UPDATE_SYSTEM_INFO("UpdateSystemInfo") |
|
||||||
} |
|
||||||
@ -1,42 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import kotlinx.coroutines.GlobalScope |
|
||||||
import kotlinx.coroutines.launch |
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.stereotype.Service |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import java.io.File |
|
||||||
import java.util.concurrent.TimeUnit |
|
||||||
|
|
||||||
@Service |
|
||||||
class LogcatService( |
|
||||||
private val applicationProperties: ApplicationProperties |
|
||||||
) { |
|
||||||
private var dumpProcess: Process? = null |
|
||||||
|
|
||||||
fun startDump() { |
|
||||||
val filePath = "${applicationProperties.fileRoot}/all.log" |
|
||||||
GlobalScope.launch { |
|
||||||
logger.info("start to save logcat. path: $filePath") |
|
||||||
launch { |
|
||||||
File(filePath).delete() |
|
||||||
ProcessBuilder("/bin/sh", "-c", "adb logcat -c").start() |
|
||||||
val processBuilder = ProcessBuilder("/bin/sh", "-c", "adb logcat -s AnypointAD") |
|
||||||
|
|
||||||
processBuilder.redirectOutput(File(filePath)) |
|
||||||
val process = processBuilder.start() |
|
||||||
process.onExit().thenAcceptAsync { |
|
||||||
logger.info("finished to save logcat. exitValue: ${it.exitValue()}, path: $filePath") |
|
||||||
} |
|
||||||
dumpProcess = process |
|
||||||
Thread.sleep(TimeUnit.HOURS.toMillis(2)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fun stopDump() { |
|
||||||
dumpProcess?.destroy() |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,98 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import kotlinx.serialization.encodeToString |
|
||||||
import kotlinx.serialization.json.Json |
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties |
|
||||||
import org.springframework.stereotype.Service |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import tv.anypoint.dsl.exception.httpValidationError |
|
||||||
import tv.anypoint.dsl.model.TcContext |
|
||||||
import tv.anypoint.proxy.adapter.AssignAdapter |
|
||||||
import tv.anypoint.proxy.adapter.AuthAdapter |
|
||||||
import tv.anypoint.proxy.model.agent.AuthRequest |
|
||||||
import tv.anypoint.proxy.model.agent.AuthResponse |
|
||||||
import tv.anypoint.proxy.model.agent.Endpoints |
|
||||||
import tv.anypoint.proxy.model.agent.ad.AdsResponse |
|
||||||
import tv.anypoint.proxy.model.agent.ad.AdsSyncRequest |
|
||||||
|
|
||||||
@Service |
|
||||||
class ProxyDeviceService( |
|
||||||
private val json: Json, |
|
||||||
private val serverIp: String, |
|
||||||
private val serverProperties: ServerProperties, |
|
||||||
private val applicationProperties: ApplicationProperties, |
|
||||||
private val authAdapter: AuthAdapter, |
|
||||||
private val assignAdapter: AssignAdapter, |
|
||||||
private val tcContext: TcContext, |
|
||||||
private val logcatService: LogcatService |
|
||||||
) { |
|
||||||
fun auth(request: AuthRequest): AuthResponse { |
|
||||||
return try { |
|
||||||
val authResponse = authAdapter.auth( |
|
||||||
request = request |
|
||||||
).execute().body()!! |
|
||||||
authResponse.endpoints.redirectToProxyServer( |
|
||||||
serverIp = serverIp, |
|
||||||
serverPort = serverProperties.port, |
|
||||||
pushServerPort = applicationProperties.pushServerPort |
|
||||||
) |
|
||||||
if (!tcContext.tc.auth.validate(authResponse)) { |
|
||||||
throw httpValidationError(authResponse) |
|
||||||
} |
|
||||||
val response = tcContext.tc.auth.convert(authResponse) |
|
||||||
tcContext.tc.authResponse = response |
|
||||||
logger.info("auth response: ${json.encodeToString(response)}") |
|
||||||
|
|
||||||
if (tcContext.tc.reboot) { |
|
||||||
logcatService.startDump() |
|
||||||
// TODO: logcat 에서 `auth response body` 를 기다린 다음에 adb ADS_SYNC 호출 |
|
||||||
} else { |
|
||||||
// TODO: adb ADS_SYNC 호출 |
|
||||||
} |
|
||||||
response |
|
||||||
} catch(e: Exception) { |
|
||||||
logger.error("failed to auth.", e) |
|
||||||
throw RuntimeException("failed to auth.") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private fun Endpoints.redirectToProxyServer( |
|
||||||
serverIp: String, |
|
||||||
serverPort: Int, |
|
||||||
pushServerPort: Int |
|
||||||
) { |
|
||||||
val serverEndpoint = "http://$serverIp:$serverPort" |
|
||||||
val pushServerEndpoint = "$serverIp:$pushServerPort" |
|
||||||
this.auth = serverEndpoint |
|
||||||
this.requestAds = serverEndpoint |
|
||||||
this.adSyncResult = serverEndpoint |
|
||||||
this.appLog = serverEndpoint |
|
||||||
this.event = serverEndpoint |
|
||||||
// this.pushServers = listOf(pushServerEndpoint) |
|
||||||
this.ntpServers = emptyList() |
|
||||||
this.stateLog = serverEndpoint |
|
||||||
this.impressionLog = serverEndpoint |
|
||||||
this.assetRequest = serverEndpoint |
|
||||||
this.proxyAdLog = serverEndpoint |
|
||||||
} |
|
||||||
|
|
||||||
fun ads(deviceId: Long, freeStorage: Long?, usedStorage: Long?): AdsResponse { |
|
||||||
val adsResponse = assignAdapter.ads( |
|
||||||
deviceId = deviceId, |
|
||||||
freeStorage = freeStorage ?: 0, |
|
||||||
usedStorage = usedStorage ?: 0 |
|
||||||
).execute().body()!! |
|
||||||
val response = tcContext.tc.ads.convert(adsResponse) |
|
||||||
tcContext.tc.adsResponse = response |
|
||||||
logger.info("ads response: ${json.encodeToString(response)}") |
|
||||||
|
|
||||||
return response |
|
||||||
} |
|
||||||
|
|
||||||
fun adSync(request: AdsSyncRequest) { |
|
||||||
// nothing to do. |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,10 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import tv.anypoint.proxy.model.agent.Cue |
|
||||||
import tv.anypoint.proxy.model.push.PushCommandType |
|
||||||
|
|
||||||
interface PushServerInterface { |
|
||||||
fun sendCommand(commandType: PushCommandType): Boolean |
|
||||||
|
|
||||||
fun sendCue(cue: Cue): Boolean |
|
||||||
} |
|
||||||
@ -1,21 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import org.springframework.stereotype.Service |
|
||||||
import tv.anypoint.ApplicationProperties |
|
||||||
import tv.anypoint.proxy.model.agent.Cue |
|
||||||
import tv.anypoint.proxy.model.push.PushCommandType |
|
||||||
|
|
||||||
@Service |
|
||||||
class PushServerService( |
|
||||||
private val applicationProperties: ApplicationProperties |
|
||||||
) : PushServerInterface { |
|
||||||
override fun sendCommand(commandType: PushCommandType): Boolean { |
|
||||||
applicationProperties.pushServerPort |
|
||||||
TODO("Not Implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun sendCue(cue: Cue): Boolean { |
|
||||||
applicationProperties.pushServerPort |
|
||||||
TODO("Not Implemented") |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,54 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties |
|
||||||
import org.springframework.stereotype.Service |
|
||||||
import se.vidstige.jadb.JadbDevice |
|
||||||
import tv.anypoint.dsl.model.Tc |
|
||||||
import tv.anypoint.dsl.model.TcContext |
|
||||||
import tv.anypoint.proxy.model.push.PushCommandType |
|
||||||
import tv.anypoint.proxy.shell.ShellController |
|
||||||
import tv.anypoint.proxy.util.changeAuthEndpoint |
|
||||||
import tv.anypoint.proxy.util.reboot |
|
||||||
import tv.anypoint.proxy.util.sendNumberInput |
|
||||||
|
|
||||||
@Service |
|
||||||
class StbService( |
|
||||||
private val serverIp: String, |
|
||||||
private val jadbDevice: JadbDevice, |
|
||||||
private val tcContext: TcContext, |
|
||||||
private val serverProperties: ServerProperties, |
|
||||||
private val logcatService: LogcatService, |
|
||||||
private val pushServerService: PushServerService |
|
||||||
) { |
|
||||||
fun start( |
|
||||||
restartStb: Boolean, |
|
||||||
serverIp: String = this.serverIp, |
|
||||||
serverPort: Int = serverProperties.port |
|
||||||
) { |
|
||||||
logcatService.startDump() |
|
||||||
tcContext.tc = Tc().apply { |
|
||||||
this.number = "MANUAL" |
|
||||||
this.reboot = restartStb |
|
||||||
} |
|
||||||
changeAuthEndpoint(serverIp = serverIp, serverPort = serverPort) |
|
||||||
if (restartStb) { |
|
||||||
jadbDevice.reboot() |
|
||||||
} else { |
|
||||||
pushServerService.sendCommand(PushCommandType.UPDATE_SYSTEM_INFO) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private fun changeAuthEndpoint( |
|
||||||
serverIp: String, |
|
||||||
serverPort: Int |
|
||||||
) { |
|
||||||
jadbDevice.changeAuthEndpoint(serverIp = serverIp, serverPort = serverPort) |
|
||||||
} |
|
||||||
|
|
||||||
fun changeChannelNumber(channelNumber: String) { |
|
||||||
channelNumber.forEach { c -> |
|
||||||
jadbDevice.sendNumberInput(c) |
|
||||||
} |
|
||||||
ShellController.logger.info("changed ch number: $channelNumber") |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,40 +0,0 @@ |
|||||||
package tv.anypoint.proxy.shell |
|
||||||
|
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.shell.standard.ShellComponent |
|
||||||
import org.springframework.shell.standard.ShellMethod |
|
||||||
import tv.anypoint.proxy.service.StbService |
|
||||||
|
|
||||||
@ShellComponent |
|
||||||
class ShellController( |
|
||||||
private val stbService: StbService |
|
||||||
) { |
|
||||||
|
|
||||||
/** |
|
||||||
* 채널 변경 |
|
||||||
*/ |
|
||||||
@ShellMethod |
|
||||||
fun ch(channelNumber: String) { |
|
||||||
stbService.changeChannelNumber(channelNumber) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* STB 에 접속 |
|
||||||
* ip, port 지정하지 않을 경우 설정된 STB 에 접속 |
|
||||||
*/ |
|
||||||
@ShellMethod |
|
||||||
fun start( |
|
||||||
restart: Boolean = false, |
|
||||||
ip: String? = null, |
|
||||||
port: Int? = null |
|
||||||
) { |
|
||||||
when { |
|
||||||
ip == null && port == null -> stbService.start(restartStb = restart) |
|
||||||
ip != null && port != null -> stbService.start(restartStb = restart, serverIp = ip, serverPort = port) |
|
||||||
ip != null && port == null -> stbService.start(restartStb = restart, serverIp = ip) |
|
||||||
ip == null && port != null -> stbService.start(restartStb = restart, serverPort = port) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,22 +0,0 @@ |
|||||||
package tv.anypoint.proxy.util |
|
||||||
|
|
||||||
import java.net.Inet4Address |
|
||||||
import java.net.InetSocketAddress |
|
||||||
|
|
||||||
object JadbDeviceSerialUtil { |
|
||||||
|
|
||||||
private val serialRegex = Regex("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})") |
|
||||||
|
|
||||||
fun addressToId(hostAddress: String, port: Int) = "$hostAddress:$port" |
|
||||||
fun addressToId(socketAddress: InetSocketAddress) = addressToId(socketAddress.address.hostAddress, socketAddress.port) |
|
||||||
|
|
||||||
fun serialToSocketAddress(serial: String) : InetSocketAddress? { |
|
||||||
val matchResult = serialRegex.matchEntire(serial) ?: return null |
|
||||||
val address = Inet4Address.getByName(matchResult.groupValues[1]) |
|
||||||
val port = Integer.parseInt(matchResult.groupValues[2]) |
|
||||||
return InetSocketAddress(address, port) |
|
||||||
} |
|
||||||
|
|
||||||
fun serialToAddress(serial: String) = serialToSocketAddress(serial)?.address |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,88 +0,0 @@ |
|||||||
package tv.anypoint.proxy.util |
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils |
|
||||||
import org.slf4j.Logger |
|
||||||
import org.slf4j.LoggerFactory |
|
||||||
import se.vidstige.jadb.JadbConnection |
|
||||||
import se.vidstige.jadb.JadbDevice |
|
||||||
import tv.anypoint.proxy.model.adb.ChangeCommand |
|
||||||
import tv.anypoint.proxy.model.adb.ExtraKey |
|
||||||
import tv.anypoint.proxy.model.adb.IntentAction |
|
||||||
import java.net.InetSocketAddress |
|
||||||
import java.nio.charset.StandardCharsets |
|
||||||
|
|
||||||
private val logger: Logger = LoggerFactory.getLogger("tv.anypoint.proxy.util.StbUtilKt") |
|
||||||
|
|
||||||
fun getDevice(stbIp: String, stbPort: Int): JadbDevice { |
|
||||||
try { |
|
||||||
val socketAddress = InetSocketAddress(stbIp, stbPort) |
|
||||||
val jadbc = JadbConnection() |
|
||||||
try { |
|
||||||
jadbc.connectToTcpDevice(socketAddress) |
|
||||||
} catch (e: Exception) { |
|
||||||
logger.error("Can't connect adb to $socketAddress, Reason -> ${e.message}") |
|
||||||
} |
|
||||||
return jadbc.devices.firstOrNull { it.serial in JadbDeviceSerialUtil.addressToId(socketAddress) } |
|
||||||
?: throw RuntimeException("adb server not running!") |
|
||||||
} catch (e: Exception) { |
|
||||||
throw RuntimeException("adb server not running!", e) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sends broadcast command to the device to change the auth endpoint to the server. |
|
||||||
*/ |
|
||||||
fun JadbDevice.changeAuthEndpoint( |
|
||||||
serverIp: String, |
|
||||||
serverPort: Int |
|
||||||
) { |
|
||||||
val proxyEndpoint = "http://$serverIp:$serverPort" |
|
||||||
executeShellDebug( |
|
||||||
a = tv.anypoint.proxy.model.adb.IntentAction.CHANGE_TEST_PROPERTY, |
|
||||||
es = mapOf( |
|
||||||
tv.anypoint.proxy.model.adb.ExtraKey.CHANGE_COMMAND to tv.anypoint.proxy.model.adb.ChangeCommand.CHANGE_API_ENDPOINT.name, |
|
||||||
tv.anypoint.proxy.model.adb.ExtraKey.API_ENDPOINT to proxyEndpoint |
|
||||||
) |
|
||||||
) |
|
||||||
logger.info("changed auth endpoint to proxy endpoint $proxyEndpoint") |
|
||||||
} |
|
||||||
|
|
||||||
fun JadbDevice.executeShellDebug(a: tv.anypoint.proxy.model.adb.IntentAction, es: Map<tv.anypoint.proxy.model.adb.ExtraKey, String>) { |
|
||||||
val shellContentBuilder = StringBuilder() |
|
||||||
shellContentBuilder.append("am broadcast ") |
|
||||||
shellContentBuilder.append("-a ${a.string} ") |
|
||||||
es.forEach { (k, v) -> |
|
||||||
shellContentBuilder.append("--es ${k.string} $v ") |
|
||||||
} |
|
||||||
this.executeShellDebug(shellContentBuilder.toString()) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sends number as an input to the device, just like |
|
||||||
* pressing a number button in the remote controller. |
|
||||||
*/ |
|
||||||
fun JadbDevice.sendNumberInput(numberButton: Char) { |
|
||||||
if (numberButton < '0' || numberButton > '9') return |
|
||||||
|
|
||||||
val shellContent = "input keyevent KEYCODE_$numberButton" |
|
||||||
this.executeShellDebug(shellContent) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sends shell to the device |
|
||||||
*/ |
|
||||||
fun JadbDevice.executeShellDebug(content: String) { |
|
||||||
logger.info("[[ cmd --> {} ]] {}", this.serial, content) |
|
||||||
val inputStream = this.executeShell(content) |
|
||||||
val commandResult = IOUtils.toString(inputStream, StandardCharsets.UTF_8) |
|
||||||
for (line in commandResult.split('\n')) { |
|
||||||
logger.info("[[ cmd <-- {} ]] {}", this.serial, line.removeNewLines()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sends reboot command to the device. |
|
||||||
*/ |
|
||||||
fun JadbDevice.reboot() { |
|
||||||
executeShellDebug("reboot") |
|
||||||
} |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
package tv.anypoint.proxy.util |
|
||||||
|
|
||||||
fun String.removeNewLines() = this.replace("\n", "") |
|
||||||
@ -1,18 +0,0 @@ |
|||||||
package tv.anypoint.proxy.web |
|
||||||
|
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.web.bind.annotation.GetMapping |
|
||||||
import org.springframework.web.bind.annotation.RequestMapping |
|
||||||
import org.springframework.web.bind.annotation.RestController |
|
||||||
|
|
||||||
@RequestMapping("/test") |
|
||||||
@RestController |
|
||||||
class TestController { |
|
||||||
@GetMapping |
|
||||||
fun testGet(): Boolean { |
|
||||||
logger.info("GET /test") |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,15 +0,0 @@ |
|||||||
import java.io.BufferedReader |
|
||||||
import java.io.FileReader |
|
||||||
import java.io.IOException |
|
||||||
import java.io.LineNumberReader |
|
||||||
|
|
||||||
class Test { |
|
||||||
@Throws(IOException::class) |
|
||||||
fun test() { |
|
||||||
val lnr = LineNumberReader(BufferedReader(FileReader("all.log"))) |
|
||||||
var line: String? |
|
||||||
while ((lnr.readLine().also { line = it }) != null) { |
|
||||||
println(line) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,46 +0,0 @@ |
|||||||
package tv.anypoint.dsl |
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec |
|
||||||
import io.kotest.matchers.longs.shouldBeLessThanOrEqual |
|
||||||
import io.kotest.matchers.shouldBe |
|
||||||
import mu.KLogging |
|
||||||
import tv.anypoint.dsl.model.Logcat |
|
||||||
import java.io.File |
|
||||||
import java.time.Duration |
|
||||||
import java.time.LocalDateTime |
|
||||||
|
|
||||||
class DslKtTest : FunSpec() { |
|
||||||
init { |
|
||||||
test("Log.expected()") { |
|
||||||
// given |
|
||||||
val file = File("./test.log") |
|
||||||
val writer = file.writer() |
|
||||||
val startedAt = LocalDateTime.now() |
|
||||||
val test5 = Logcat("./test.log") |
|
||||||
val test7 = Logcat("./test.log") |
|
||||||
|
|
||||||
object : Thread() { |
|
||||||
override fun run() { |
|
||||||
(1..20).forEach { |
|
||||||
logger.info("print test $it") |
|
||||||
writer.write("test $it${System.lineSeparator()}") |
|
||||||
writer.flush() |
|
||||||
sleep(500) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.start() |
|
||||||
|
|
||||||
// when |
|
||||||
test5.expected("test 5") |
|
||||||
test7.expected("test 7") |
|
||||||
|
|
||||||
// then |
|
||||||
test5.cursor shouldBe 4 |
|
||||||
test7.cursor shouldBe 6 |
|
||||||
Duration.between(startedAt, LocalDateTime.now()).seconds shouldBeLessThanOrEqual 10_000 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,44 +0,0 @@ |
|||||||
package tv.anypoint.proxy.adapter |
|
||||||
|
|
||||||
import io.kotest.core.spec.style.BehaviorSpec |
|
||||||
import io.kotest.core.spec.style.FunSpec |
|
||||||
import io.kotest.matchers.shouldBe |
|
||||||
import mu.KLogging |
|
||||||
import org.springframework.boot.test.context.SpringBootTest |
|
||||||
import tv.anypoint.proxy.model.agent.AuthRequest |
|
||||||
|
|
||||||
@SpringBootTest |
|
||||||
class AuthAdapterTest( |
|
||||||
private val authAdapter: AuthAdapter |
|
||||||
) : BehaviorSpec() { |
|
||||||
init { |
|
||||||
Given("request") { |
|
||||||
val request = AuthRequest( |
|
||||||
fingerPrint = "", |
|
||||||
soId = 1, |
|
||||||
uuid = "43dd41a2-111b-48c8-b840-629978714f0b", |
|
||||||
freeStorage = 2519707648, |
|
||||||
usedStorage = 294611792, |
|
||||||
cachedStorage = 203884308, |
|
||||||
modelName = "BID-AI100", |
|
||||||
firmwareBuildDate = "2024.04.02", |
|
||||||
firmwareVer = "16.540.22", |
|
||||||
fullFirmwareVer = "16.540.22-0000", |
|
||||||
appVersion = "3.9.4-RC59_20230712", |
|
||||||
platformAdId = "63c33b8c-f5d5-47b3-9e7d-f06342fb65fe", |
|
||||||
sdkVersion = "2.0.1-RC12" |
|
||||||
) |
|
||||||
|
|
||||||
When("call auth API") { |
|
||||||
val actual = authAdapter.auth(request) |
|
||||||
.execute().body() |
|
||||||
logger.info("actual: $actual") |
|
||||||
Then("actual is not null") { |
|
||||||
1 shouldBe 1 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
companion object : KLogging() |
|
||||||
} |
|
||||||
@ -1,15 +0,0 @@ |
|||||||
package tv.anypoint.proxy.service |
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec |
|
||||||
import org.springframework.boot.test.context.SpringBootTest |
|
||||||
|
|
||||||
@SpringBootTest |
|
||||||
class LogcatServiceTest( |
|
||||||
logcatService: LogcatService |
|
||||||
) : FunSpec({ |
|
||||||
test("start") { |
|
||||||
logcatService.startDump() |
|
||||||
Thread.sleep(5_000) |
|
||||||
logcatService.stopDump() |
|
||||||
} |
|
||||||
}) |
|
||||||
Loading…
Reference in new issue