All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mmauro94.mkvtoolnix_wrapper.MkvToolnixCommandResult.kt Maven / Gradle / Ivy

Go to download

An easy to use light kotlin-jvm wrapper for most common mkvmerge and mkvpropedit CLI commands

The newest version!
package com.github.mmauro94.mkvtoolnix_wrapper

import com.github.mmauro94.mkvtoolnix_wrapper.MkvToolnixCommandResult.*
import com.github.mmauro94.mkvtoolnix_wrapper.MkvToolnixCommandResult.Line.Type
import com.github.mmauro94.mkvtoolnix_wrapper.utils.CachedSequence
import java.io.BufferedReader

/**
 * Abstract class that represents a result given by a MKV Toolnix binary.
 * There are two "versions" of this class:
 * * [Lazy]: its output can be iterated while the program is executing
 * * [Sync]: all output is parsed beforehand
 *
 * Both implementations contain the following information:
 * * `output`: a sequence of [Line]s that contain the output given by the execution
 * * `exitCode`: the numerical exit code of the process
 * * `success`: true if the exit code is `0` and the output contains no error or warnings
 */
sealed class MkvToolnixCommandResult>(val command: COMMAND) {

    /**
     * A line outputted by the program execution
     * The type can be [Type.ERROR], [Type.WARNING] or, for all other lines, [Type.INFO]
     */
    data class Line(
        val message: String,
        val type: Type
    ) {
        enum class Type {
            INFO, WARNING, ERROR
        }

        val progress by lazy {
            if (type == Type.INFO) {
                val m = PROGRESS_REGEX.matchEntire(message)
                if (m != null) {
                    m.groupValues[1].toInt() / 100f
                } else null
            } else null
        }

        val isProgressLine by lazy { progress != null }

        override fun toString(): String {
            return "${type.name}: $message";
        }

        companion object {
            private val PROGRESS_REGEX = ".+?(100|\\d{1,2})%$".toRegex()
        }
    }

    /** The exit code of the process. `0` means everything executed successfully */
    abstract val exitCode: Int
    /**
     * [Sequence] of [Line]s. In the [Lazy] implementation it can be iterated while the program is still executing.
     * It can be iterated multiple times.
     */
    abstract val output: Sequence

    /**
     * true if the exit code is `0` and the output contains no error or warnings
     *
     * WARNING: Evaluating this variable in the [Lazy] implementation causes the method to halt until the process finishes
     */
    val success by lazy {
        exitCode == 0 && !(output.hasErrors() || output.hasWarnings())
    }

    private fun outputLines(printCommand: Boolean, printOutput: Boolean, printExitCode: Boolean): Sequence {
        return sequence {
            if (printCommand) {
                yield(command.toString())
                yield("")
            }
            if (printOutput) {
                yieldAll(output.map { it.toString() })
                yield("")
            }
            if (printExitCode) {
                yield("Exit code: $exitCode")
            }
        }.asSequence()
    }

    /**
     * @param printCommand whether to include the command used at the start of the string. Default: `false`
     * @param printOutput whether to include the output of the program. Default: `true`
     * @param printExitCode whether to include the exit code of the program. Default: `true`
     * @return a string representation of this program execution
     */
    fun toString(printCommand: Boolean = false, printOutput: Boolean = true, printExitCode: Boolean = true) = StringBuilder().apply {
        outputLines(printCommand, printOutput, printExitCode).forEach {
            appendLine(it)
        }
    }.toString()

    /**
     * Prints a string representation of this program.
     *
     * Please notice that this method, if called in an [Lazy] implementation, prints each output line as soon as it's generated by the process.
     *
     * @param printCommand whether to include the command used at the start of the string. Default: `false`
     * @param printOutput whether to include the output of the program. Default: `true`
     * @param printExitCode whether to include the exit code of the program. Default: `true`
     */
    fun print(printCommand: Boolean = false, printOutput: Boolean = true, printExitCode: Boolean = true) {
        outputLines(printCommand, printOutput, printExitCode).forEach {
            println(it)
        }
    }

    /**
     * @return a string representation of this program execution with:
     * * command: no
     * * output: yes
     * * exitCode: yes
     * @see toString
     */
    override fun toString(): String {
        return toString(false)
    }

    /**
     * Lazy implementation.
     * See [MkvToolnixCommandResult] for details
     */
    class Lazy> internal constructor(
        command: COMMAND,
        private val reader: BufferedReader,
        exitCodeEvaluator: () -> Int,
        opt: CachedSequence
    ) : MkvToolnixCommandResult(command), AutoCloseable {

        override val output: Sequence = opt
        override val exitCode by lazy(exitCodeEvaluator)

        internal fun waitForCompletion(exceptionInitializer: ExceptionInitializer): Sync {
            if (!success) {
                throw exceptionInitializer(this, "Errors/warnings have been produced", null)
            }
            return toSync()
        }

        /**
         * Returns a sync version of this object. Takes care of closing the input stream. Halts until program execution terminates.
         */
        fun toSync() = use {
            Sync(command, exitCode, output.toList())
        }

        override fun close() {
            reader.close()
        }
    }

    /**
     * Sync implementation.
     * See [MkvToolnixCommandResult] for details
     *
     * @param outputList the list of lines as a list. Contains the same elements present in the [output] sequence
     */
    class Sync> internal constructor(
        command: COMMAND,
        override val exitCode: Int,
        val outputList: List
    ) : MkvToolnixCommandResult(command) {
        override val output = outputList.asSequence()
    }
}

fun Sequence.hasWarnings() = any { it.type == MkvToolnixCommandResult.Line.Type.WARNING }
fun Sequence.hasErrors() = any { it.type == MkvToolnixCommandResult.Line.Type.ERROR }
fun Sequence.hasInfo() = any { it.type == MkvToolnixCommandResult.Line.Type.INFO }

fun Sequence.warnings() = filter { it.type == MkvToolnixCommandResult.Line.Type.WARNING }
fun Sequence.errors() = filter { it.type == MkvToolnixCommandResult.Line.Type.ERROR }
fun Sequence.info() = filter { it.type == MkvToolnixCommandResult.Line.Type.INFO }

@Suppress("ReplaceSingleLineLet")
fun Sequence.progress() = this.let {
    sequence {
        yield(0f)
        yieldAll(it
            .map { it.progress }
            .filterNotNull()
        )
        yield(1f)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy