Newer
Older
Twilight / src / main / kotlin / net / taehui / twilight / system / TVSystem.kt
package net.taehui.twilight.system

import net.taehui.twilight.Logger
import org.openqa.selenium.By
import org.openqa.selenium.TimeoutException
import org.openqa.selenium.WebDriver
import org.openqa.selenium.edge.EdgeDriver
import org.openqa.selenium.edge.EdgeOptions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

object TVSystem : Logger {
    class TVItem(val href: String, val title: String, val text: String) {
        override fun hashCode(): Int {
            return href.hashCode()
        }

        override fun equals(other: Any?): Boolean {
            return href == (other as TVItem).href
        }

        override fun toString(): String {
            return text
        }
    }

    private val ses: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor {
        Executors.defaultThreadFactory().newThread(it).apply {
            isDaemon = true
        }
    }
    private var tv: WebDriver? = null
    private var future: ScheduledFuture<*>? = null

    fun handleSystem() {
        if (tv != null) {
            logInfo("TV is already running")
        } else if (Configure.mode.tv) {
            try {
                tv = EdgeDriver(EdgeOptions().apply {
                    addArguments("--headless", "--no-sandbox")
                })
                val pendingElements = mutableMapOf<TVItem, Int>()
                var lastElements = emptySet<TVItem>()

                future = ses.scheduleWithFixedDelay({
                    tv?.let { it ->
                        it.get("https://www.twitch.tv/directory/game/Qwilight")
                        val test0 = By.cssSelector("[data-test-selector=TitleAndChannel]")
                        val elements0 = try {
                            WebDriverWait(
                                it,
                                Duration.ofSeconds(1)
                            ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(test0))
                            it.findElements(test0) ?: throw TimeoutException()
                        } catch (e: TimeoutException) {
                            emptyList()
                        }.map { element ->
                            TVItem(
                                element.getAttribute("href"),
                                element.findElement(By.tagName("h3")).text,
                                element.findElement(By.tagName("p")).text
                            )
                        }.toSet()

                        it.get("https://chzzk.naver.com/category/GAME/Qwilight")
                        val test1 =
                            By.cssSelector("#layout-body > div > section > div > ul > li > div > div > div:nth-child(2)")
                        val elements1 = try {
                            WebDriverWait(
                                it,
                                Duration.ofSeconds(1)
                            ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(test1))
                            it.findElements(test1)
                        } catch (e: TimeoutException) {
                            emptyList()
                        }.map {
                            val titleElement = it.findElement(By.cssSelector("a"))
                            TVItem(
                                titleElement.getAttribute("href"),
                                titleElement.text.split("\n")[0],
                                it.findElement(By.cssSelector("div > a > span > span")).text
                            )
                        }.toSet()

                        val elements = elements0.plus(elements1)
                        elements.subtract(lastElements).filter {
                            pendingElements.remove(it) == null
                        }.forEach {
                            SiteHandler.putSiteYell(it)
                        }
                        lastElements.subtract(elements).forEach {
                            pendingElements[it] = 0
                        }
                        pendingElements.toMap().forEach {
                            if (it.value < 5) {
                                pendingElements[it.key] = it.value + 1
                            } else {
                                pendingElements.remove(it.key)
                            }
                        }
                        lastElements = elements
                    }
                }, 0L, 1L, TimeUnit.MINUTES)
            } catch (e: Exception) {
                logFault(e)
            }
        } else {
            logInfo("TV is disabled")
        }
    }

    val tvStatus: String
        get() = "{Title: ${tv?.title}, Page: ${tv?.pageSource}, URL: ${tv?.currentUrl}, Handles: [${tv?.windowHandles?.joinToString()}]}"

    fun dispose() {
        future?.cancel(false)
        tv?.quit()
        tv = null
    }
}