zhuker
5/20/2019 - 6:35 AM

ffmpeg from dir with images

given a folder with numbered files

./R1_235_CC
./R1_235_CC/M99_R1.086208.tif
./R1_235_CC/M99_R1.086209.tif
./R1_235_CC/M99_R1.086210.tif
./R1_235_CC/M99_R1.086211.tif

produces ffmpeg input command

ffmpeg -f image2 -start_number 086208 -i './R1_235_CC/M99_R1.%06d.tif'
import java.io.File
import java.io.FileFilter
import java.text.ParseException

val MEDIASEQ_UNKNOWN = -1

fun getMediaSequence(f: File) = getMediaSequence(f.name)

fun getMediaSequence(s: String): Int {
    if (s.startsWith(".")) return MEDIASEQ_UNKNOWN
    val basename = getBaseName(s)
    val split = basename.split("[^0-9]+".toRegex())
    return split.lastOrNull()?.toIntOrNull() ?: MEDIASEQ_UNKNOWN
}

fun getDirMediaSequence(dir: File, filter: FileFilter? = null): Int {
    return dir.listFiles(filter).orEmpty().map { getMediaSequence(it) }.max() ?: MEDIASEQ_UNKNOWN
}

val MEDIASEQ_CMP = Comparator { o1: File, o2: File -> getMediaSequence(o1) - getMediaSequence(o2) }

val MEDIASEQ_CMPString = Comparator { o1: String, o2: String -> getMediaSequence(o1) - getMediaSequence(o2) }

fun getBaseName(filename: String) =  filename.substringAfterLast("/").substringBeforeLast(".")

fun dpxInfoFromFile(f: File): DpxFilename {
    // simple - 0177670.dpx
    // vdms - Scrubs_Ep101_102_CR_B019_1641738.dpx
    // cbs - BX505_SR7378_01.00089437.dpx

    val bn = getBaseName(f.name)
    var number = ""
    var reel: String? = ""
    for (i in bn.length - 1 downTo 0) {
        val c = bn[i]
        if (Character.isDigit(c)) {
            number = c + number
        } else {
            reel = bn.substring(0, i)
            break
        }
    }

    if (number.isNotBlank()) {
        val pf = f.parentFile
        var clip: String? = pf?.name
        if ((clip ?: "").matches("[0-9]+x[0-9]+".toRegex())) {
            //CBS weird corner case: /$CLIP/$videoRes/*.dpx
            clip = f.parentFile.parentFile.name
        }
        reel = if (reel.isNullOrBlank()) null else reel
        val filenamesPattern = f.name.replace(number, "%0${number.length}d")
        return DpxFilename(clip, reel, number, filenamesPattern)
    }
    throw ParseException("Can't parse filename ${f.absolutePath}", 0)
}

fun dpxInfoFromFiles(url: String, dpxList: List<File>): DpxFilename {
    val dpxls = dpxList.sortedWith(MEDIASEQ_CMP)
    val firstDpx = dpxls.firstOrNull() ?: throw IllegalStateException("No dpx files found at URL $url")
    val lastDpx = dpxls.lastOrNull() ?: throw IllegalStateException("No dpx files found at URL $url")

    val dpxinfo1 = dpxInfoFromFile(firstDpx)
    val dpxinfo2 = dpxInfoFromFile(lastDpx)

    if (dpxinfo1.filenamesPattern == dpxinfo2.filenamesPattern) {
        return dpxinfo1
    }
    if (dpxinfo1.number.startsWith("0") || dpxinfo2.number.startsWith("0")) {
        throw IllegalStateException("cant create dpx number pattern ${firstDpx.name} vs ${lastDpx.name}")
    }

    val filenamesPattern = firstDpx.name.replace("${Integer.parseInt(dpxinfo1.number)}", "%d")
    return DpxFilename(dpxinfo1.clipName, dpxinfo1.reel, dpxinfo1.number, filenamesPattern)
}

data class DpxFilename(val clipName: String? // Usually it is DPX folder
                       , val reel: String? // Usually is it part of the dpx filename
                       , val number: String, val filenamesPattern: String)


args.forEach {
    val f = File(it)
    when {
        f.isDirectory -> {
            val files = f.listFiles().orEmpty()
            val jpgs = files.filter { ff -> ff.name.toLowerCase().endsWith("jpg") }
            val pngs = files.filter { ff -> ff.name.toLowerCase().endsWith("png") }
            val dpxs = files.filter { ff -> ff.name.toLowerCase().endsWith("dpx") }
            val tifs = files.filter { ff -> ff.name.toLowerCase().endsWith("tif") }

            val ls = arrayOf(jpgs, pngs, dpxs, tifs).maxBy { ff -> ff.size } ?: emptyList()
            val info = dpxInfoFromFiles(it, ls)
            println("ffmpeg -f image2 -start_number ${info.number} -i '$it/${info.filenamesPattern}'")
        }
        f.isFile -> {
            println(dpxInfoFromFile(f))
        }
    }
}