diff --git a/MediaThor-Commons/src/main/resources/logback.xml b/MediaThor-Commons/src/main/resources/logback.xml index 226c09a..4d13f53 100644 --- a/MediaThor-Commons/src/main/resources/logback.xml +++ b/MediaThor-Commons/src/main/resources/logback.xml @@ -6,11 +6,15 @@ - + + + + + diff --git a/MediaThor-Interceptor/.gitignore b/MediaThor-Interceptor/.gitignore new file mode 100644 index 0000000..916af1f --- /dev/null +++ b/MediaThor-Interceptor/.gitignore @@ -0,0 +1 @@ +/master_playlists.txt \ No newline at end of file diff --git a/MediaThor-Interceptor/README.md b/MediaThor-Interceptor/README.md new file mode 100644 index 0000000..0bdb32f --- /dev/null +++ b/MediaThor-Interceptor/README.md @@ -0,0 +1,9 @@ +# MediaThor-Interceptor + +A utility to intercept HLS stream requests and append them to a list + +## Limitations +* Master playlists won't be distinguished from media playlists + +## Requirements +* Chrome on Linux/Mac (Optional), or Chromium-based Edge on Windows \ No newline at end of file diff --git a/MediaThor-Interceptor/pom.xml b/MediaThor-Interceptor/pom.xml new file mode 100644 index 0000000..ba343b6 --- /dev/null +++ b/MediaThor-Interceptor/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + io.github.freya022 + mediathor + 1.0 + + + mediathor-interceptor + MediaThor-Interceptor + + + io.github.freya022.mediathor.interceptor.InterceptorMain + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + ${mainClass} + + + + false + ${project.name} + + + + + + + + io.github.freya022 + mediathor-commons + 1.0 + + + com.google.code.gson + gson + + + com.squareup.okhttp3 + okhttp + + + org.seleniumhq.selenium + selenium-java + + + org.seleniumhq.selenium + selenium-devtools-v85 + + + org.seleniumhq.selenium + selenium-devtools-v114 + + + org.seleniumhq.selenium + selenium-devtools-v115 + + + org.seleniumhq.selenium + selenium-firefox-driver + + + org.seleniumhq.selenium + selenium-ie-driver + + + + + \ No newline at end of file diff --git a/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/Interceptor.kt b/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/Interceptor.kt new file mode 100644 index 0000000..ebe3f22 --- /dev/null +++ b/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/Interceptor.kt @@ -0,0 +1,94 @@ +package io.github.freya022.mediathor.interceptor + +import mu.two.KotlinLogging +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.openqa.selenium.Platform +import org.openqa.selenium.chrome.ChromeDriver +import org.openqa.selenium.devtools.v116.network.Network +import org.openqa.selenium.devtools.v116.target.Target +import org.openqa.selenium.edge.EdgeDriver +import org.openqa.selenium.remote.DriverCommand +import org.openqa.selenium.remote.RemoteWebDriver +import java.nio.file.StandardOpenOption +import java.util.* +import java.util.logging.Level +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine +import kotlin.io.path.Path +import kotlin.io.path.bufferedWriter +import kotlin.jvm.optionals.getOrNull + +private val logger = KotlinLogging.logger { } + +class Interceptor private constructor() { + private val writer = outputPath.bufferedWriter(options = arrayOf(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) + + private lateinit var continuation: Continuation + + private val driver = when (Platform.getCurrent().family()) { + Platform.WINDOWS -> EdgeDriver() + else -> ChromeDriver() + } + + private val hook = Thread { + driver.maybeGetDevTools().getOrNull()?.clearListeners() + driver.quit() + close() + } + + init { + driver.setLogLevel(Level.INFO) + Runtime.getRuntime().addShutdownHook(hook) + + val devTools = driver.devTools + devTools.createSession() + devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty())) + + devTools.addListener(Network.requestWillBeSent()) { futureRequest -> + val url = futureRequest.request.url + if (!url.startsWith("http")) { + logger.trace { "Discarding $url as it is not an http url" } + return@addListener + } + + if (!url.toHttpUrl().pathSegments.last().endsWith(".m3u8")) { + logger.trace { "Discarding $url as it is not a '*.m3u8' url" } + return@addListener + } + + logger.info { "Added $url" } + writer.appendLine(url) + } + + devTools.addListener(Target.detachedFromTarget()) { + logger.info { "Exiting" } + Runtime.getRuntime().removeShutdownHook(hook) + continuation.resume(Unit) + } + } + + private suspend fun awaitTermination(): Unit = suspendCoroutine { + continuation = it + } + + // This method is only used when the browser is already closed. + private fun close() { + writer.close() + } + + companion object { + val outputPath = Path("master_playlists.txt") + + suspend fun intercept() { + val interceptor = Interceptor() + interceptor.awaitTermination() + // If the browser is closed, the driver is still running + // Using the QUIT command makes it exit + val executeMethod = RemoteWebDriver::class.java.getDeclaredMethod("execute", String::class.java) + executeMethod.isAccessible = true + executeMethod.invoke(interceptor.driver, DriverCommand.QUIT) + interceptor.close() + } + } +} \ No newline at end of file diff --git a/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/InterceptorMain.kt b/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/InterceptorMain.kt new file mode 100644 index 0000000..f18b045 --- /dev/null +++ b/MediaThor-Interceptor/src/main/kotlin/io/github/freya022/mediathor/interceptor/InterceptorMain.kt @@ -0,0 +1,18 @@ +package io.github.freya022.mediathor.interceptor + +import kotlinx.coroutines.runBlocking +import java.io.IOException +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists + +object InterceptorMain { + @JvmStatic + fun main(args: Array) { + if (Interceptor.outputPath.exists() && "-y" !in args) + throw IOException("${Interceptor.outputPath.absolutePathString()} already exists") + + runBlocking { + Interceptor.intercept() + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index ed5a2ef..40bc541 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,6 @@ These utilities are focused on specific use cases, the goal is NOT a swiss army ## Subprojects * [MediaThor-DL](MediaThor-DL): A downloader for HLS streams +* [MediaThor-Interceptor](MediaThor-Interceptor): Writes *.m3u8 files in a list from browser usage * [MediaThor-Volume](MediaThor-Volume): Helps you manage the volume of your music files * [MediaThor-Record-Helper](MediaThor-Record-Helper): Merges overlapping clips using a temporary ramdisk \ No newline at end of file diff --git a/pom.xml b/pom.xml index e5f9759..6534206 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ MediaThor-Volume MediaThor-Record-Helper MediaThor-Commons + MediaThor-Interceptor MediaThor @@ -210,6 +211,11 @@ jnr-winfsp c7be8901e8 + + org.seleniumhq.selenium + selenium-java + 4.12.0 +