Newer
Older
Twilight / src / main / kotlin / net / taehui / twilight / system / TVSystem.kt
@Taehui Taehui on 25 Feb 5 KB v1.0-SNAPSHOT
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/search?query=Qwilight")

                        val test1 =
                            By.cssSelector("#layout-body > div > section > section")
                        val elements1 = try {
                            WebDriverWait(
                                it,
                                Duration.ofSeconds(1)
                            ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(test1))
                            it.findElements(test1).filter {
                                it.findElement(By.cssSelector("strong")).text == "라이브"
                            }.map {
                                it.findElements(By.cssSelector("div > ul > li"))
                            }
                        } catch (e: TimeoutException) {
                            emptyList()
                        }.flatten().map {
                            val titleElement = it.findElement(By.cssSelector("div > div > div > a"))
                            TVItem(
                                titleElement.getAttribute("href"),
                                titleElement.text.split("\n")[0],
                                it.findElement(By.cssSelector("div > div > div > div")).text.split("\n")[0]
                            )
                        }.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: Throwable) {
                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
    }
}