New Upstream Release - ffcvt
Ready changes
Summary
Merged new upstream version: 1.11.1 (was: 1.7.6).
Diff
diff --git a/.all-contributorsrc b/.all-contributorsrc
deleted file mode 100644
index 8309cb2..0000000
--- a/.all-contributorsrc
+++ /dev/null
@@ -1,80 +0,0 @@
-{
- "files": [
- "README.md",
- "README.beg.e.md",
- "README.end2.e.md"
- ],
- "imageSize": 100,
- "commit": false,
- "contributors": [
- {
- "login": "suntong",
- "name": "suntong",
- "avatar_url": "https://avatars.githubusercontent.com/u/422244?v=4",
- "profile": "https://github.com/suntong",
- "contributions": [
- "code",
- "ideas",
- "design",
- "data",
- "test",
- "bug",
- "doc",
- "blog",
- "example",
- "tutorial",
- "tool",
- "platform",
- "review",
- "question",
- "maintenance",
- "infra"
- ]
- },
- {
- "login": "sanjaymsh",
- "name": "sanjaymsh",
- "avatar_url": "https://avatars.githubusercontent.com/u/66668807?v=4",
- "profile": "https://github.com/sanjaymsh",
- "contributions": [
- "platform"
- ]
- },
- {
- "login": "bjwest",
- "name": "Billy West",
- "avatar_url": "https://avatars.githubusercontent.com/u/1934590?v=4",
- "profile": "https://github.com/bjwest",
- "contributions": [
- "bug",
- "userTesting"
- ]
- },
- {
- "login": "bdaroz",
- "name": "Brian Rozmierski",
- "avatar_url": "https://avatars.githubusercontent.com/u/9668896?v=4",
- "profile": "https://github.com/bdaroz",
- "contributions": [
- "bug",
- "userTesting"
- ]
- },
- {
- "login": "brainstorm",
- "name": "Roman Valls Guimera",
- "avatar_url": "https://avatars.githubusercontent.com/u/175587?v=4",
- "profile": "http://blogs.nopcode.org/brainstorm",
- "contributions": [
- "bug",
- "userTesting"
- ]
- }
- ],
- "contributorsPerLine": 7,
- "projectName": "ffcvt",
- "projectOwner": "suntong",
- "repoType": "github",
- "repoHost": "https://github.com",
- "skipCi": true
-}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index 56dff65..0000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ main ]
- paths-ignore:
- - '**/*.md'
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ main ]
- paths-ignore:
- - '**/*.md'
- schedule:
- # The default branch at 14:32 UTC on the 1st of every month
- - cron: '32 14 1 * *'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'go' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v2
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v1
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v1
-
- # βΉοΈ Command-line programs to run using the OS shell.
- # π https://git.io/JvXDl
-
- # βοΈ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/go-release-build.yml b/.github/workflows/go-release-build.yml
deleted file mode 100644
index 3a86022..0000000
--- a/.github/workflows/go-release-build.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-name: build
-
-on:
- push:
- branches:
- - 'master'
- tags:
- - 'v*'
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- -
- name: Set up Go
- uses: actions/setup-go@v2
- with:
- go-version: 1.15
-
- -
- name: Install Cloudsmith CLI
- run: pip install --upgrade cloudsmith-cli
- # Cloudsmith CLI tooling for pushing releases
- # See https://help.cloudsmith.io/docs/cli
-
- -
- name: Checkout
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- -
- name: Tests
- run: |
- export GOPATH=/home/runner/go
- mkdir -p $GOPATH/src/github.com/$GITHUB_ACTOR
- mv $GITHUB_WORKSPACE $GOPATH/src/github.com/$GITHUB_ACTOR
- ln -s $GOPATH/src/github.com/$GITHUB_REPOSITORY $GITHUB_WORKSPACE
- # go mod tidy
- go get -v ./...
- go test -v ./...
- go build -v .
- #pwd
- #ls -Al test/* test2/*
- ( cd test; ./test-all.sh; )
- ( cd test2; ./test-all.sh; )
-
- -
- name: Run GoReleaser
- uses: goreleaser/goreleaser-action@v2
- if: success() && startsWith(github.ref, 'refs/tags/')
- with:
- version: latest
- args: release --rm-dist
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- # Publish to cloudsmith repo
- -
- name: Publish package to cloudsmith
- if: success() && startsWith(github.ref, 'refs/tags/')
- env:
- CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
- run: |
- for filepath in dist/*; do
- echo "== Analyzing '$filepath' for publishing"
- filename=$(basename -- "$filepath")
- extension="${filename##*.}"
- filename="${filename%.*}"
- case "$extension" in
- 'apk')
- echo "Pushing '$filepath' to cloudsmith repo"
- cloudsmith push alpine suntong/repo/alpine/any-version $filepath
- ;;
- 'deb' | 'rpm')
- echo "Pushing '$filepath' to cloudsmith repo"
- cloudsmith push $extension suntong/repo/any-distro/any-version $filepath
- ;;
- *)
- echo "File .$extension skipped publishing"
- echo
- ;;
- esac
- done
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 9dd960d..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,27 +0,0 @@
-.all-contributorsrc
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
-*.so
-
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
-*.test
-*.prof
-ffcvt
-*~
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 725b555..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-image: golang:1.10
-
-stages:
- - build
- - test
-
-before_script:
- # Create a symbolic link under $GOPATH, this is needed for local build
- - cd $GOPATH/src
- - mkdir -p gitlab.com/$CI_PROJECT_NAMESPACE
- - cd gitlab.com/$CI_PROJECT_NAMESPACE
- - ln -s $CI_PROJECT_DIR
- - cd $CI_PROJECT_NAME
- - go get ./...
-
-build-test:
- script:
- # == build
- # - godep restore
- # - godep go build
- - go build -v -ldflags="-X main.date=`date -I$TIMESPEC`"; date -I$TIMESPEC
-
- # == test
- - $CI_PROJECT_NAME
- - $CI_PROJECT_NAME -version
- # - godep restore
- # - godep go test -v -cover ./...
- - go test -v -cover ./...
- - ( pwd; cd test; pwd; ls -l; ./test-all.sh; )
diff --git a/.goreleaser.yml b/.goreleaser.yml
deleted file mode 100644
index bc1998f..0000000
--- a/.goreleaser.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-project_name: ffcvt
-
-archives:
- - format: tar.gz
- wrap_in_directory: true
- format_overrides:
- - goos: windows
- format: zip
- # remove README and LICENSE
- files:
- - none*
-
-builds:
- - env: [CGO_ENABLED=0]
- goos:
- - linux
- - windows
- - darwin
- goarch:
- - amd64
- - arm64
-
-nfpms:
-- maintainer: Tong Sun <suntong@cpan.org>
- description: ffmpeg convert wrapper tool
- homepage: https://github.com/suntong/ffcvt
- license: MIT
- formats:
- - deb
- - rpm
- - apk
diff --git a/README.beg.e.md b/README.beg.e.md
deleted file mode 100644
index eb08897..0000000
--- a/README.beg.e.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# {{.Name}}
-<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
-[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
-<!-- ALL-CONTRIBUTORS-BADGE:END -->
-
-{{render "license/shields" . "License" "MIT"}}
-{{template "badge/godoc" .}}
-{{template "badge/goreport" .}}
-[![Build Status](https://github.com/{{.User}}/{{.Name}}/actions/workflows/go-release-build.yml/badge.svg?branch=master)](https://github.com/{{.User}}/{{.Name}}/actions/workflows/go-release-build.yml)
-[![PoweredBy WireFrame](https://github.com/go-easygen/wireframe/blob/master/PoweredBy-WireFrame-B.svg)](http://godoc.org/github.com/go-easygen/wireframe)
-
-{{pkgdoc}}
-
diff --git a/README.e.md b/README.e.md
deleted file mode 100644
index 48b47d2..0000000
--- a/README.e.md
+++ /dev/null
@@ -1,96 +0,0 @@
-## {{toc 5}}
-- [Install Debian/Ubuntu package](#install-debianubuntu-package)
-- [Download/install binaries](#downloadinstall-binaries)
- - [The binary executables](#the-binary-executables)
- - [Distro package](#distro-package)
- - [Debian package](#debian-package)
-- [Install Source](#install-source)
-- [Author](#author)
-- [Contributors](#contributors-)
-
-## {{.Name}} - ffmpeg convert wrapper tool
-
-### Latest Update(s)
-
-#### Release v1.7.5
-
-* Now able to speed up playback speed (`-Speed`). Details in [\#22](https://github.com/suntong/ffcvt/issues/22)
-* Also have added a `copy` target type that can speed up the `Seg` (split video) operation (v1.7.4). Details in [\#21](https://github.com/suntong/ffcvt/issues/21)
-
-#### Release v1.7.3
-
-* Now able to split video into multiple segments (`-S,Seg`) by the given time. Details in [\#16](https://github.com/suntong/ffcvt/issues/16)
-
-#### Release v1.7.2
-
-* Able to [choose streams by language, instead of streams index. ](https://github.com/suntong/ffcvt/commit/f649609356ef06d22d17d6dbe3f89b945cf18643)Details in [\#9](https://github.com/suntong/ffcvt/issues/9)
-* Fixed [\#8](https://github.com/suntong/ffcvt/issues/8). Now [force copy all subtitle streams. ](https://github.com/suntong/ffcvt/commit/46ce6725f9b036d373c6836d3bd66b429d5c4b2f)Details in [\#8](https://github.com/suntong/ffcvt/issues/8)
-* [Added option -sel](https://github.com/suntong/ffcvt/commit/defc5df5168216e279b944590f1d92523ecadc60), so now able to pick subtitle language(s). Details in [\#12](https://github.com/suntong/ffcvt/issues/12)
-
-#### Release v1.7.1
-
-Added option `-C,Cut` which allows cutting multiple segments.
-
-For further details, check out the wiki https://git.io/JuK0c,
-in which the source file of
-
-https://user-images.githubusercontent.com/422244/132961501-a2344db0-c48c-4a57-90fa-c3746bf3025f.mp4
-
-is cut-short into
-
-https://user-images.githubusercontent.com/422244/132961530-ea65cd03-19f8-4e7c-a871-40218f7289cc.mp4
-
-
-#### Release v1.7.0
-
-Added `wx` type for weixin.
-
-Convert to video that is recognizable and playable by weixin/wechat, by using the `-t wx` option as the convertion type. Here is a converted sample:
-
-https://user-images.githubusercontent.com/422244/132617136-e1371ef3-6a21-4f12-8324-6db003c12468.mp4
-
-(credit [here](https://www.youtube.com/watch?v=2-UzBitLmf8))
-
-For further details, check out the wiki https://git.io/JuK0q
-
-## Introduction
-
-- The next-generation codec like [High Efficiency Video codec (HEVC), H.265](https://goo.gl/IZrDH2) or [VP9](https://developers.google.com/media/vp9/) can produce videos visually comparable to H.264's result, but in [about half the file size](https://trac.ffmpeg.org/wiki/Encode/H.265).
-- Meanwhile the [Opus](https://goo.gl/BPUkTf) [audio codec](https://goo.gl/IZrDH2) is becoming the best thing ever for compressing audio -- A 64K Opus audio stream is comparable to mp3 files of 128K to 256K bandwidth.
-- Such fantastic high efficiency audio/video codec/encoding capability has long been available in `ffmpeg`, but fewer people know it or use it, partly because the `ffmpeg` command line is not that simple for every one.
-- The `ffcvt` is designed to take the burden from normal Joe -- All you need to do to encode a video is to give one parameter to `ffcvt`, i.e., the path and file name of the video to be encoded, and `ffcvt` will take care of the rest, using the recommended values for both audio/video encoding to properly encode it for you.
-- It can't be more simpler than that. However, beneath the simple surface, `ffcvt` is versatile and powerful enough to allow you to touch every corner of audio/video encoding. There is a huge list of environment variables (or command-line parameters) which will allow you tweak the encoding methods and parameters to exactly what you prefer instead.
-- Moreover, to encode a directory full of video files, including under its sub-directories, you need just to give `ffcvt` one single parameter, the directory location, and `ffcvt` will go ahead and encode all video files under that directory, including all its sub-directories as well.
-
-## Quick Usage
-
-There is a quick usage help that comes with `ffcvt`, produced when it is invoked without any parameters:
-
-### $ {{exec "ffcvt" | color "sh"}}
-
-
-## Environment Variables
-
-For each `ffcvt` command line parameter, there is a environment variable corresponding to it. For example you can use `export FFCVT_FFMPEG=avconv` to use `avconv` instead of `ffmpeg` (Don't, I use it for my [CommandLineArgs](https://github.com/suntong001/lang/blob/master/lang/Go/src/sys/CommandLineArgs.go) to develop/test `ffcvt` without invoking `ffmpeg` each time).
-
-## Encoding Help
-
-The detailed guide to choose/provide proper parameters to `ffcvt` have been moved to [wiki](https://github.com/suntong/ffcvt/wiki/). For example,
-
-- [HEVC vs VP9](https://github.com/suntong/ffcvt/wiki/KB:-WebM-(VP9)-Encoding#hevc-vs-vp9)
-- [HEVC Preset Method Comparison](https://github.com/suntong/ffcvt/wiki/KB:-HEVC-(x265)-Encoding#preset-method-comparison)
-- [The HEVC CRF Comparison](https://github.com/suntong/ffcvt/wiki/KB:-HEVC-(x265)-Encoding#the-crf-comparison)
-- [Example 1: YouTube Encoding](https://github.com/suntong/ffcvt/wiki/Example:-YouTube-Encoding)
-- [Example 2: Talk Encoding](https://github.com/suntong/ffcvt/wiki/Example:-Talk-Encoding)
-
-Please check them out in the [wiki](https://github.com/suntong/ffcvt/wiki/), and for other documents like "Most used ffmpeg options", "How to crop a video", etc.
-
-## Tools Choices
-
-As suggested before, don't use `avconv`, use `ffmpeg` instead (the `avconv` fork was more for political reasons. I personally believe `ffmpeg` is technically superior although might not be politically).
-
-As for video/movie play back, use [mpv](http://mpv.io/). It is a fork of mplayer2 and MPlayer, and is a true *modern* *all-in-one* movie player that can play ANYTHING, and one of the few movie players being actively developed all the time. Download link is in [mpv.io](http://mpv.io/), from which Ubuntu repo I get my Ubuntu `ffmpeg` package as well. If you are unsatisfied with mpv's simple user interface, check out https://wiki.archlinux.org/index.php/Mpv#Front_ends.
-
-### Install Debian/Ubuntu package
-
- apt install {{.Name}}
diff --git a/README.end.e.md b/README.end.e.md
deleted file mode 100644
index 1f34563..0000000
--- a/README.end.e.md
+++ /dev/null
@@ -1,71 +0,0 @@
-
-## Download/install binaries
-
-- The latest binary executables are available
-as the result of the Continuous-Integration (CI) process.
-- I.e., they are built automatically right from the source code at every git release by [GitHub Actions](https://docs.github.com/en/actions).
-- There are two ways to get/install such binary executables
- * Using the **binary executables** directly, or
- * Using **packages** for your distro
-
-### The binary executables
-
-- The latest binary executables are directly available under
-https://github.com/{{.User}}/{{.Name}}/releases/latest
-- Pick & choose the one that suits your OS and its architecture. E.g., for Linux, it would be the `{{.Name}}_verxx_linux_amd64.tar.gz` file.
-- Available OS for binary executables are
- * Linux
- * Mac OS (darwin)
- * Windows
-- If your OS and its architecture is not available in the download list, please let me know and I'll add it.
-- The manual installation is just to unpack it and move/copy the binary executable to somewhere in `PATH`. For example,
-
-``` sh
-tar -xvf {{.Name}}_*_linux_amd64.tar.gz
-sudo mv -v {{.Name}}_*_linux_amd64/{{.Name}} /usr/local/bin/
-rmdir -v {{.Name}}_*_linux_amd64
-```
-
-
-### Distro package
-
-- [Packages available for Linux distros](https://cloudsmith.io/~suntong/repos/repo/packages/) are
- * [Alpine Linux](https://cloudsmith.io/~suntong/repos/repo/setup/#formats-alpine)
- * [Debian](https://cloudsmith.io/~suntong/repos/repo/setup/#formats-deb)
- * [RedHat](https://cloudsmith.io/~suntong/repos/repo/setup/#formats-rpm)
-
-The repo setup instruction url has been given above.
-For example, for [Debian](https://cloudsmith.io/~suntong/repos/repo/setup/#formats-deb) --
-
-### Debian package
-
-
-```sh
-curl -1sLf \
- 'https://dl.cloudsmith.io/public/suntong/repo/setup.deb.sh' \
- | sudo -E bash
-
-# That's it. You then can do your normal operations, like
-
-sudo apt-get update
-apt-cache policy {{.Name}}
-
-sudo apt-get install -y {{.Name}}
-```
-
-## Install Source
-
-To install the source code instead:
-
-```
-go get -v -u {{.ProjectURL}}
-```
-
-## Author
-
-Tong SUN
-![suntong from cpan.org](https://img.shields.io/badge/suntong-%40cpan.org-lightgrey.svg "suntong from cpan.org")
-
-_Powered by_ [**WireFrame**](https://github.com/go-easygen/wireframe)
-[![PoweredBy WireFrame](https://github.com/go-easygen/wireframe/blob/master/PoweredBy-WireFrame-Y.svg)](http://godoc.org/github.com/go-easygen/wireframe)
-the _one-stop wire-framing solution_ for Go cli based projects, from _init_ to _deploy_.
diff --git a/README.end2.e.md b/README.end2.e.md
deleted file mode 100644
index 968e4f9..0000000
--- a/README.end2.e.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-## Contributors β¨
-
-Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
-
-<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
-<!-- prettier-ignore-start -->
-<!-- markdownlint-disable -->
-<table>
- <tr>
- <td align="center"><a href="https://github.com/suntong"><img src="https://avatars.githubusercontent.com/u/422244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>suntong</b></sub></a><br /><a href="https://github.com/suntong/ffcvt/commits?author=suntong" title="Code">π»</a> <a href="#ideas-suntong" title="Ideas, Planning, & Feedback">π€</a> <a href="#design-suntong" title="Design">π¨</a> <a href="#data-suntong" title="Data">π£</a> <a href="https://github.com/suntong/ffcvt/commits?author=suntong" title="Tests">β οΈ</a> <a href="https://github.com/suntong/ffcvt/issues?q=author%3Asuntong" title="Bug reports">π</a> <a href="https://github.com/suntong/ffcvt/commits?author=suntong" title="Documentation">π</a> <a href="#blog-suntong" title="Blogposts">π</a> <a href="#example-suntong" title="Examples">π‘</a> <a href="#tutorial-suntong" title="Tutorials">β
</a> <a href="#tool-suntong" title="Tools">π§</a> <a href="#platform-suntong" title="Packaging/porting to new platform">π¦</a> <a href="https://github.com/suntong/ffcvt/pulls?q=is%3Apr+reviewed-by%3Asuntong" title="Reviewed Pull Requests">π</a> <a href="#question-suntong" title="Answering Questions">π¬</a> <a href="#maintenance-suntong" title="Maintenance">π§</a> <a href="#infra-suntong" title="Infrastructure (Hosting, Build-Tools, etc)">π</a></td>
- <td align="center"><a href="https://github.com/sanjaymsh"><img src="https://avatars.githubusercontent.com/u/66668807?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sanjaymsh</b></sub></a><br /><a href="#platform-sanjaymsh" title="Packaging/porting to new platform">π¦</a></td>
- <td align="center"><a href="https://github.com/bjwest"><img src="https://avatars.githubusercontent.com/u/1934590?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Billy West</b></sub></a><br /><a href="https://github.com/suntong/ffcvt/issues?q=author%3Abjwest" title="Bug reports">π</a> <a href="#userTesting-bjwest" title="User Testing">π</a></td>
- <td align="center"><a href="https://github.com/bdaroz"><img src="https://avatars.githubusercontent.com/u/9668896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Rozmierski</b></sub></a><br /><a href="https://github.com/suntong/ffcvt/issues?q=author%3Abdaroz" title="Bug reports">π</a> <a href="#userTesting-bdaroz" title="User Testing">π</a></td>
- <td align="center"><a href="http://blogs.nopcode.org/brainstorm"><img src="https://avatars.githubusercontent.com/u/175587?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Roman Valls Guimera</b></sub></a><br /><a href="https://github.com/suntong/ffcvt/issues?q=author%3Abrainstorm" title="Bug reports">π</a> <a href="#userTesting-brainstorm" title="User Testing">π</a></td>
- </tr>
-</table>
-
-<!-- markdownlint-restore -->
-<!-- prettier-ignore-end -->
-
-<!-- ALL-CONTRIBUTORS-LIST:END -->
-
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
diff --git a/README.md b/README.md
index 2bf8d9a..eb71d11 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,15 @@
[![PoweredBy WireFrame](https://github.com/go-easygen/wireframe/blob/master/PoweredBy-WireFrame-B.svg)](http://godoc.org/github.com/go-easygen/wireframe)
+Transcodes all videos in the given directory and all of it's subdirectories
+using ffmpeg.
+
## TOC
- [ffcvt - ffmpeg convert wrapper tool](#ffcvt---ffmpeg-convert-wrapper-tool)
- [Latest Update(s)](#latest-update(s))
+ - [Release v1.10.0](#release-v1100)
+ - [Release v1.9.0](#release-v190)
+ - [Release v1.8.0](#release-v180)
- [Release v1.7.5](#release-v175)
- [Release v1.7.3](#release-v173)
- [Release v1.7.2](#release-v172)
@@ -20,11 +26,11 @@
- [Release v1.7.0](#release-v170)
- [Introduction](#introduction)
- [Quick Usage](#quick-usage)
+ - [$ ffcvt -version](#-ffcvt--version)
- [$ ffcvt](#-ffcvt)
- [Environment Variables](#environment-variables)
- [Encoding Help](#encoding-help)
- [Tools Choices](#tools-choices)
- - [Install Debian/Ubuntu package](#install-debianubuntu-package)
- [Install Debian/Ubuntu package](#install-debianubuntu-package)
- [Download/install binaries](#downloadinstall-binaries)
- [The binary executables](#the-binary-executables)
@@ -38,6 +44,29 @@
### Latest Update(s)
+#### Release v1.10.0
+
+Now able to
+
+* change the key signature of a song. Details in [\#30](https://github.com/suntong/ffcvt/issues/30).
+* add a karaoke audio track to the MTV videos. Details in [\#31](https://github.com/suntong/ffcvt/issues/31).
+
+
+#### Release v1.9.0
+
+- Release v1.9.0
+ * `ffcvt -version` now checks/outputs dependent program versions too
+ * now finished percentage are calculated from file size
+- Release v1.8.1, enable parallel execution
+
+#### Release v1.8.0
+
+* Now able to define your own defaults. Just make a copy of [ffcvt.json](ffcvt.json) and customize it to your heart's content, then use the `-cfg` option to point to it. Better yet, set `FFCVT_CFG` environment variable and forget all about it afterwards.
+ * This means that `ffcvt` is now not only limited to its own predefined transcoding sets, but you can also define your own transcoding rules and names and then fully enjoy its advanced addon assistants.
+ * BTW, If you have a good set, don't forget to send in a PR so that everybody can also benefit from it.
+* Now the subtitles, nfo, html or any files in the source directory will be duplicated into the output (work) directory, first by hard-link and if it fails due to cross storage devices, a copy will be used instead.
+* And when creating `par2` checksum/repair files, all files in the output (work) directory will be covered.
+
#### Release v1.7.5
* Now able to speed up playback speed (`-Speed`). Details in [\#22](https://github.com/suntong/ffcvt/issues/22)
@@ -92,6 +121,19 @@ For further details, check out the wiki https://git.io/JuK0q
There is a quick usage help that comes with `ffcvt`, produced when it is invoked without any parameters:
+### $ ffcvt -version
+
+```sh
+$ ffcvt -version
+ffcvt version 1.10.0 built on 2023-05-18
+
+ffmpeg version 4.3.6-0+deb11u1 Copyright (c) 2000-2023 the FFmpeg developers
+built with gcc 10 (Debian 10.2.1-6)
+
+ffprobe version 4.3.6-0+deb11u1 Copyright (c) 2007-2023 the FFmpeg developers
+built with gcc 10 (Debian 10.2.1-6)
+```
+
### $ ffcvt
```sh
Usage:
@@ -99,7 +141,8 @@ Usage:
Flags:
- -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy (FFCVT_T)
+ -cfg cfg file to define your own targets: webm/wx/youtube etc (FFCVT_CFG)
+ -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (FFCVT_T)
-ves video encoding method set (FFCVT_VES)
-aes audio encoding method set (FFCVT_AES)
-ses subtitle encoding method set (FFCVT_SES)
@@ -129,6 +172,9 @@ Flags:
strictly in the format of hh:mm:ss, and may repeat (FFCVT_C,CUT)
-S,Seg Split video into multiple segments (strictly in format: hh:mm:ss) (FFCVT_S,SEG)
-Speed Speed up/down video playback speed (e.g. 1.28) (FFCVT_SPEED)
+ -K,karaoke Add a karaoke audio track to .mp4 MTV (FFCVT_K,KARAOKE)
+ -tkf Transpose song's key from (e.g. C/C#/Db/D etc) (FFCVT_TKF)
+ -tkt Transpose song's key to (e.g. -tkf C -tkt Db) (FFCVT_TKT)
-lang language selection for audio stream extraction (FFCVT_LANG)
-sel subtitle encoding language (language picked for reencoded video) (FFCVT_SEL)
-o more options that will pass to ffmpeg program (FFCVT_O)
@@ -153,6 +199,7 @@ Details:
-Cut value
Cut segment(s) out to keep. Specify in the form of start-[end],
strictly in the format of hh:mm:ss, and may repeat
+ -K Add a karaoke audio track to .mp4 MTV
-S string
Split video into multiple segments (strictly in format: hh:mm:ss)
-Seg string
@@ -173,6 +220,8 @@ Details:
no audio, output video only
-ato-opus
audio encode to opus, using -abr
+ -cfg string
+ cfg file to define your own targets: webm/wx/youtube etc
-crf string
the CRF value: 0-51. Higher CRF gives lower quality
(28 for x265, ~ 23 for x264)
@@ -192,6 +241,8 @@ Details:
ffprobe program execution (default "ffprobe -print_format flat")
-force
overwrite any existing none-empty file
+ -karaoke
+ Add a karaoke audio track to .mp4 MTV
-lang string
language selection for audio stream extraction (default "eng")
-n no exec, dry run
@@ -211,7 +262,11 @@ Details:
-sym
symlinks will be processed as well
-t string
- target type: webm/x265-opus/x264-mp3/wx/youtube/copy (default "webm")
+ target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (default "webm")
+ -tkf string
+ Transpose song's key from (e.g. C/C#/Db/D etc)
+ -tkt string
+ Transpose song's key to (e.g. -tkf C -tkt Db)
-vc
copy video codec
-vea string
@@ -257,9 +312,9 @@ As suggested before, don't use `avconv`, use `ffmpeg` instead (the `avconv` fork
As for video/movie play back, use [mpv](http://mpv.io/). It is a fork of mplayer2 and MPlayer, and is a true *modern* *all-in-one* movie player that can play ANYTHING, and one of the few movie players being actively developed all the time. Download link is in [mpv.io](http://mpv.io/), from which Ubuntu repo I get my Ubuntu `ffmpeg` package as well. If you are unsatisfied with mpv's simple user interface, check out https://wiki.archlinux.org/index.php/Mpv#Front_ends.
-### Install Debian/Ubuntu package
+## Install Debian/Ubuntu package
- apt install ffcvt
+ sudo apt install -y ffcvt
## Download/install binaries
@@ -309,10 +364,10 @@ curl -1sLf \
# That's it. You then can do your normal operations, like
-sudo apt-get update
+sudo apt update
apt-cache policy ffcvt
-sudo apt-get install -y ffcvt
+sudo apt install -y ffcvt
```
## Install Source
@@ -320,7 +375,7 @@ sudo apt-get install -y ffcvt
To install the source code instead:
```
-go get -v -u github.com/suntong/ffcvt
+go install github.com/suntong/ffcvt@latest
```
## Author
diff --git a/VP9-CRF.png b/VP9-CRF.png
deleted file mode 100644
index 6ac3593..0000000
Binary files a/VP9-CRF.png and /dev/null differ
diff --git a/VP9-Speed.png b/VP9-Speed.png
deleted file mode 100644
index 319d3f6..0000000
Binary files a/VP9-Speed.png and /dev/null differ
diff --git a/config.go b/config.go
index f154631..9e4b976 100644
--- a/config.go
+++ b/config.go
@@ -8,6 +8,8 @@ import (
"flag"
"fmt"
"os"
+ "strconv"
+ "time"
)
////////////////////////////////////////////////////////////////////////////
@@ -17,35 +19,41 @@ const progname = "ffcvt" // os.Args[0]
// The Options struct defines the structure to hold the commandline values
type Options struct {
- Target string // target type: webm/x265-opus/x264-mp3/wx/youtube/copy
- Encoding // anonymous field to hold encoding values
- Directory string // directory that hold input files
- File string // input file name (either -d or -f must be specified)
- Links bool // symlinks will be processed as well
- Exts string // extension list for all the files to be queued
- Suffix string // suffix to the output file names
- WDirectory string // work directory that hold output files
- AC bool // copy audio codec
- VC bool // copy video codec
- AN bool // no audio, output video only
- VN bool // no video, output audio only
- VSS bool // video: same size
- Cut mFlags // Cut segment(s) out to keep. Specify in the form of start-[end],\n\tstrictly in the format of hh:mm:ss, and may repeat
- Seg string // Split video into multiple segments (strictly in format: hh:mm:ss)
- Speed string // Speed up/down video playback speed (e.g. 1.28)
- Lang string // language selection for audio stream extraction
- SEL mFlags // subtitle encoding language (language picked for reencoded video)
- OptExtra string // more options that will pass to ffmpeg program
- A2Opus bool // audio encode to opus, using -abr
- V2X265 bool // video video encode to x265, using -crf
- Par2C bool // par2create, create par2 files (in work directory)
- NoClobber bool // no clobber, do not queue those already been converted
- NoExec bool // no exec, dry run
- Force bool // overwrite any existing none-empty file
- Debug int // debugging level
- FFMpeg string // ffmpeg program executable name
- FFProbe string // ffprobe program execution
- PrintV bool // print version then exit
+ Cfg string // cfg file to define your own targets: webm/wx/youtube etc
+ Target string // target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty
+ Encoding // anonymous field to hold encoding values
+ Directory string // directory that hold input files
+ File string // input file name (either -d or -f must be specified)
+ Links bool // symlinks will be processed as well
+ Exts string // extension list for all the files to be queued
+ Suffix string // suffix to the output file names
+ WDirectory string // work directory that hold output files
+ AC bool // copy audio codec
+ VC bool // copy video codec
+ AN bool // no audio, output video only
+ VN bool // no video, output audio only
+ VSS bool // video: same size
+ Cut mFlags // Cut segment(s) out to keep. Specify in the form of start-[end],\n\tstrictly in the format of hh:mm:ss, and may repeat
+ Seg string // Split video into multiple segments (strictly in format: hh:mm:ss)
+ Speed string // Speed up/down video playback speed (e.g. 1.28)
+ Karaoke bool // Add a karaoke audio track to .mp4 MTV
+ TranspFrom string // Transpose song's key from (e.g. C/C#/Db/D etc)
+ TranspTo string // Transpose song's key to (e.g. -tkf C -tkt Db)
+ Lang string // language selection for audio stream extraction
+ SEL mFlags // subtitle encoding language (language picked for reencoded video)
+ OptExtra string // more options that will pass to ffmpeg program
+ A2Opus bool // audio encode to opus, using -abr
+ V2X265 bool // video video encode to x265, using -crf
+ Par2C bool // par2create, create par2 files (in work directory)
+ NoClobber bool // no clobber, do not queue those already been converted
+ BreathTime time.Duration // breath time, interval between conversion to take a breath
+ MaxC int // max conversion done each run (default no limit)
+ NoExec bool // no exec, dry run
+ Force bool // overwrite any existing none-empty file
+ Debug int // debugging level
+ FFMpeg string // ffmpeg program executable name
+ FFProbe string // ffprobe program execution
+ PrintV bool // print version then exit
}
////////////////////////////////////////////////////////////////////////////
@@ -60,8 +68,10 @@ var Opts Options
func initVars() {
// set default values for command line parameters
+ flag.StringVar(&Opts.Cfg, "cfg", "",
+ "cfg file to define your own targets: webm/wx/youtube etc")
flag.StringVar(&Opts.Target, "t", "webm",
- "target type: webm/x265-opus/x264-mp3/wx/youtube/copy")
+ "target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty")
flag.StringVar(&Opts.VES, "ves", "",
"video encoding method set")
flag.StringVar(&Opts.AES, "aes", "",
@@ -118,6 +128,14 @@ func initVars() {
"Split video into multiple segments (strictly in format: hh:mm:ss)")
flag.StringVar(&Opts.Speed, "Speed", "",
"Speed up/down video playback speed (e.g. 1.28)")
+ flag.BoolVar(&Opts.Karaoke, "K", false,
+ "Add a karaoke audio track to .mp4 MTV")
+ flag.BoolVar(&Opts.Karaoke, "karaoke", false,
+ "Add a karaoke audio track to .mp4 MTV")
+ flag.StringVar(&Opts.TranspFrom, "tkf", "",
+ "Transpose song's key from (e.g. C/C#/Db/D etc)")
+ flag.StringVar(&Opts.TranspTo, "tkt", "",
+ "Transpose song's key to (e.g. -tkf C -tkt Db)")
flag.StringVar(&Opts.Lang, "lang", "eng",
"language selection for audio stream extraction")
flag.Var(&Opts.SEL, "sel",
@@ -133,6 +151,10 @@ func initVars() {
"par2create, create par2 files (in work directory)")
flag.BoolVar(&Opts.NoClobber, "nc", false,
"no clobber, do not queue those already been converted")
+ flag.DurationVar(&Opts.BreathTime, "bt", 120*time.Second,
+ "breath time, interval between conversion to take a breath")
+ flag.IntVar(&Opts.MaxC, "maxc", 0,
+ "max conversion done each run (default no limit)")
flag.BoolVar(&Opts.NoExec, "n", false,
"no exec, dry run")
@@ -151,6 +173,10 @@ func initVars() {
func initVals() {
exists := false
// Now override those default values from environment variables
+ if len(Opts.Cfg) == 0 ||
+ len(os.Getenv("FFCVT_CFG")) != 0 {
+ Opts.Cfg = os.Getenv("FFCVT_CFG")
+ }
if len(Opts.Target) == 0 ||
len(os.Getenv("FFCVT_T")) != 0 {
Opts.Target = os.Getenv("FFCVT_T")
@@ -251,6 +277,17 @@ func initVals() {
len(os.Getenv("FFCVT_SPEED")) != 0 {
Opts.Speed = os.Getenv("FFCVT_SPEED")
}
+ if _, exists = os.LookupEnv("FFCVT_K,KARAOKE"); Opts.Karaoke || exists {
+ Opts.Karaoke = true
+ }
+ if len(Opts.TranspFrom) == 0 ||
+ len(os.Getenv("FFCVT_TKF")) != 0 {
+ Opts.TranspFrom = os.Getenv("FFCVT_TKF")
+ }
+ if len(Opts.TranspTo) == 0 ||
+ len(os.Getenv("FFCVT_TKT")) != 0 {
+ Opts.TranspTo = os.Getenv("FFCVT_TKT")
+ }
if len(Opts.Lang) == 0 ||
len(os.Getenv("FFCVT_LANG")) != 0 {
Opts.Lang = os.Getenv("FFCVT_LANG")
@@ -272,6 +309,12 @@ func initVals() {
if _, exists = os.LookupEnv("FFCVT_NC"); Opts.NoClobber || exists {
Opts.NoClobber = true
}
+ if Opts.MaxC == 0 ||
+ len(os.Getenv("FFCVT_MAXC")) != 0 {
+ if i, err := strconv.Atoi(os.Getenv("FFCVT_MAXC")); err == nil {
+ Opts.MaxC = i
+ }
+ }
if _, exists = os.LookupEnv("FFCVT_N"); Opts.NoExec || exists {
Opts.NoExec = true
}
@@ -279,6 +322,12 @@ func initVals() {
if _, exists = os.LookupEnv("FFCVT_FORCE"); Opts.Force || exists {
Opts.Force = true
}
+ if Opts.Debug == 0 ||
+ len(os.Getenv("FFCVT_DEBUG")) != 0 {
+ if i, err := strconv.Atoi(os.Getenv("FFCVT_DEBUG")); err == nil {
+ Opts.Debug = i
+ }
+ }
if len(Opts.FFMpeg) == 0 ||
len(os.Getenv("FFCVT_FFMPEG")) != 0 {
Opts.FFMpeg = os.Getenv("FFCVT_FFMPEG")
@@ -293,7 +342,7 @@ func initVals() {
}
-const usageSummary = " -t\ttarget type: webm/x265-opus/x264-mp3/wx/youtube/copy (FFCVT_T)\n -ves\tvideo encoding method set (FFCVT_VES)\n -aes\taudio encoding method set (FFCVT_AES)\n -ses\tsubtitle encoding method set (FFCVT_SES)\n -vep\tvideo encoding method prepend (FFCVT_VEP)\n -aep\taudio encoding method prepend (FFCVT_AEP)\n -sep\tsubtitle encoding method prepend (FFCVT_SEP)\n -vea\tvideo encoding method append (FFCVT_VEA)\n -aea\taudio encoding method append (FFCVT_AEA)\n -abr\taudio bitrate (64k for opus, 256k for mp3) (FFCVT_ABR)\n -crf\tthe CRF value: 0-51. Higher CRF gives lower quality\n\t (28 for x265, ~ 23 for x264) (FFCVT_CRF)\n\n -d\tdirectory that hold input files (FFCVT_D)\n -f\tinput file name (either -d or -f must be specified) (FFCVT_F)\n -sym\tsymlinks will be processed as well (FFCVT_SYM)\n -exts\textension list for all the files to be queued (FFCVT_EXTS)\n -suf\tsuffix to the output file names (FFCVT_SUF)\n -ext\textension for the output file (FFCVT_EXT)\n -w\twork directory that hold output files (FFCVT_W)\n\n -ac\tcopy audio codec (FFCVT_AC)\n -vc\tcopy video codec (FFCVT_VC)\n -an\tno audio, output video only (FFCVT_AN)\n -vn\tno video, output audio only (FFCVT_VN)\n -vss\tvideo: same size (FFCVT_VSS)\n -C,Cut\tCut segment(s) out to keep. Specify in the form of start-[end],\n\tstrictly in the format of hh:mm:ss, and may repeat (FFCVT_C,CUT)\n -S,Seg\tSplit video into multiple segments (strictly in format: hh:mm:ss) (FFCVT_S,SEG)\n -Speed\tSpeed up/down video playback speed (e.g. 1.28) (FFCVT_SPEED)\n -lang\tlanguage selection for audio stream extraction (FFCVT_LANG)\n -sel\tsubtitle encoding language (language picked for reencoded video) (FFCVT_SEL)\n -o\tmore options that will pass to ffmpeg program (FFCVT_O)\n -ato-opus\taudio encode to opus, using -abr (FFCVT_ATO_OPUS)\n -vto-x265\tvideo video encode to x265, using -crf (FFCVT_VTO_X265)\n\n -p\tpar2create, create par2 files (in work directory) (FFCVT_P)\n -nc\tno clobber, do not queue those already been converted (FFCVT_NC)\n -n\tno exec, dry run (FFCVT_N)\n\n -force\toverwrite any existing none-empty file (FFCVT_FORCE)\n -debug\tdebugging level (FFCVT_DEBUG)\n -ffmpeg\tffmpeg program executable name (FFCVT_FFMPEG)\n -ffprobe\tffprobe program execution (FFCVT_FFPROBE)\n -version\tprint version then exit (FFCVT_VERSION)\n\nDetails:\n\n"
+const usageSummary = " -cfg\tcfg file to define your own targets: webm/wx/youtube etc (FFCVT_CFG)\n -t\ttarget type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (FFCVT_T)\n -ves\tvideo encoding method set (FFCVT_VES)\n -aes\taudio encoding method set (FFCVT_AES)\n -ses\tsubtitle encoding method set (FFCVT_SES)\n -vep\tvideo encoding method prepend (FFCVT_VEP)\n -aep\taudio encoding method prepend (FFCVT_AEP)\n -sep\tsubtitle encoding method prepend (FFCVT_SEP)\n -vea\tvideo encoding method append (FFCVT_VEA)\n -aea\taudio encoding method append (FFCVT_AEA)\n -abr\taudio bitrate (64k for opus, 256k for mp3) (FFCVT_ABR)\n -crf\tthe CRF value: 0-51. Higher CRF gives lower quality\n\t (28 for x265, ~ 23 for x264) (FFCVT_CRF)\n\n -d\tdirectory that hold input files (FFCVT_D)\n -f\tinput file name (either -d or -f must be specified) (FFCVT_F)\n -sym\tsymlinks will be processed as well (FFCVT_SYM)\n -exts\textension list for all the files to be queued (FFCVT_EXTS)\n -suf\tsuffix to the output file names (FFCVT_SUF)\n -ext\textension for the output file (FFCVT_EXT)\n -w\twork directory that hold output files (FFCVT_W)\n\n -ac\tcopy audio codec (FFCVT_AC)\n -vc\tcopy video codec (FFCVT_VC)\n -an\tno audio, output video only (FFCVT_AN)\n -vn\tno video, output audio only (FFCVT_VN)\n -vss\tvideo: same size (FFCVT_VSS)\n -C,Cut\tCut segment(s) out to keep. Specify in the form of start-[end],\n\tstrictly in the format of hh:mm:ss, and may repeat (FFCVT_C,CUT)\n -S,Seg\tSplit video into multiple segments (strictly in format: hh:mm:ss) (FFCVT_S,SEG)\n -Speed\tSpeed up/down video playback speed (e.g. 1.28) (FFCVT_SPEED)\n -K,karaoke\tAdd a karaoke audio track to .mp4 MTV (FFCVT_K,KARAOKE)\n -tkf\tTranspose song's key from (e.g. C/C#/Db/D etc) (FFCVT_TKF)\n -tkt\tTranspose song's key to (e.g. -tkf C -tkt Db) (FFCVT_TKT)\n -lang\tlanguage selection for audio stream extraction (FFCVT_LANG)\n -sel\tsubtitle encoding language (language picked for reencoded video) (FFCVT_SEL)\n -o\tmore options that will pass to ffmpeg program (FFCVT_O)\n -ato-opus\taudio encode to opus, using -abr (FFCVT_ATO_OPUS)\n -vto-x265\tvideo video encode to x265, using -crf (FFCVT_VTO_X265)\n\n -p\tpar2create, create par2 files (in work directory) (FFCVT_P)\n -nc\tno clobber, do not queue those already been converted (FFCVT_NC)\n -bt\tbreath time, interval between conversion to take a breath (FFCVT_BT)\n -maxc\tmax conversion done each run (default no limit) (FFCVT_MAXC)\n -n\tno exec, dry run (FFCVT_N)\n\n -force\toverwrite any existing none-empty file (FFCVT_FORCE)\n -debug\tdebugging level (FFCVT_DEBUG)\n -ffmpeg\tffmpeg program executable name (FFCVT_FFMPEG)\n -ffprobe\tffprobe program execution (FFCVT_FFPROBE)\n -version\tprint version then exit (FFCVT_VERSION)\n\nDetails:\n\n"
// Usage function shows help on commandline usage
func Usage() {
diff --git a/debian/changelog b/debian/changelog
index 857691e..103dcb9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,10 @@
-ffcvt (1.7.6-2) UNRELEASED; urgency=medium
+ffcvt (1.11.1-1) UNRELEASED; urgency=medium
* Remove constraints unnecessary since buster:
+ Build-Depends: Drop versioned constraint on ronn.
+ * New upstream release.
- -- Debian Janitor <janitor@jelmer.uk> Thu, 03 Feb 2022 06:05:04 -0000
+ -- Debian Janitor <janitor@jelmer.uk> Wed, 14 Jun 2023 03:46:37 -0000
ffcvt (1.7.6-1) unstable; urgency=medium
diff --git a/ffcvt.go b/ffcvt.go
index ac01493..4b97b08 100644
--- a/ffcvt.go
+++ b/ffcvt.go
@@ -1,9 +1,3 @@
-////////////////////////////////////////////////////////////////////////////
-// Porgram: FfCvt
-// Purpose: ffmpeg convert wrapper tool
-// Authors: Tong Sun (c) 2015-2022, All rights reserved
-////////////////////////////////////////////////////////////////////////////
-
/*
Transcodes all videos in the given directory and all of it's subdirectories
@@ -11,13 +5,19 @@ using ffmpeg.
*/
+package main
+
+////////////////////////////////////////////////////////////////////////////
+// Porgram: FfCvt
+// Purpose: ffmpeg convert wrapper tool
+// Authors: Tong Sun (c) 2015-2023, All rights reserved
+////////////////////////////////////////////////////////////////////////////
+
//go:generate sh -x ffcvt_cli.sh
////////////////////////////////////////////////////////////////////////////
// Program start
-package main
-
import (
"bytes"
"flag"
@@ -37,33 +37,85 @@ import (
const _encodedExt = "_.mkv"
+// video encompass data around a single video
+type video struct {
+ name string
+ size, sum int64
+ pct int
+}
+
+// videoCol is the collection for the given set of videos
+type videoCol struct {
+ videos []video
+ sum int64
+}
+
////////////////////////////////////////////////////////////////////////////
// Global variables definitions
var (
- version = "1.7.5"
- date = "2022-01-02"
+ version = "1.11.1"
+ date = "2023-05-26"
encodedExt string = _encodedExt
totalOrg int64 = 1
totalNew int64 = 1
- videos []string
+ vidCol videoCol
workDirs []string
cutOps string = ""
+
+ transpFrom, transpTo string
)
+var pitch = map[string]string{
+ "C": "261.63",
+ "Db": "277.18",
+ "D": "293.66",
+ "Eb": "311.13",
+ "E": "329.63",
+ "F": "349.23",
+ "Gb": "369.99",
+ "G": "392.00",
+ "Ab": "415.30",
+ "A": "440.00",
+ "Bb": "466.16",
+ "B": "493.88",
+}
+
+func init() {
+ pitch["C#"] = pitch["Db"]
+ pitch["D#"] = pitch["Eb"]
+ pitch["F#"] = pitch["Gb"]
+ pitch["G#"] = pitch["Ab"]
+ pitch["A#"] = pitch["Bb"]
+}
+
////////////////////////////////////////////////////////////////////////////
// Main
func main() {
- flag.Usage = Usage
- flag.Parse()
+ // == Commandline handling
if Opts.PrintV {
- fmt.Fprintf(os.Stderr, "%s\nVersion %s built on %s\n", progname, version, date)
+ fmt.Printf("%s version %s built on %s\n\n", progname, version, date)
+
+ ver, err := getVersion(Opts.FFMpeg)
+ if err != nil {
+ log.Fatalf("%s: Version check error - %s\n", progname, err.Error())
+ }
+ fmt.Printf("%s\n\n", ver)
+
+ // ffprobe, only use the first word
+ ver, err = getVersion((strings.Fields(Opts.FFProbe))[0])
+ if err != nil {
+ log.Fatalf("%s: Version check error - %s\n", progname, err.Error())
+ }
+ fmt.Printf("%s\n", ver)
+
os.Exit(0)
}
+ //fmt.Printf("%+v\n", Opts)
if len(Opts.Seg) > 0 {
// sanity check
_, err := time.Parse("15:04:05", Opts.Seg)
@@ -127,9 +179,29 @@ cut_ok:
if len(Opts.Directory)+len(Opts.File) < 1 {
Usage()
}
- getDefault()
+ if len(Opts.TranspFrom) != 0 && len(Opts.TranspTo) == 0 ||
+ len(Opts.TranspFrom) == 0 && len(Opts.TranspTo) != 0 {
+ fmt.Fprintf(os.Stderr, "Signature transposition needs transpose keys for both from and to\n")
+ os.Exit(1)
+ }
+ if len(Opts.TranspFrom) != 0 {
+ var ok bool
+ if transpFrom, ok = pitch[Opts.TranspFrom]; !ok {
+ fmt.Fprintf(os.Stderr, "Unknown key signature to transpose from\n")
+ os.Exit(1)
+ }
+ if transpTo, ok = pitch[Opts.TranspTo]; !ok {
+ fmt.Fprintf(os.Stderr, "Unknown key signature to transpose to\n")
+ os.Exit(1)
+ }
+ fmt.Fprintf(os.Stderr, "Transposing from '%s' (%s) to '%s' (%s)\n",
+ Opts.TranspFrom, transpFrom, Opts.TranspTo, transpTo)
+ }
encodedExt = Opts.Ext
+ if Opts.Karaoke && encodedExt == _encodedExt {
+ encodedExt = "_karaoke.mp4"
+ }
// Sanity check
if Opts.WDirectory != "" {
// To error on the safe side -- when -d is not given but -f is,
@@ -165,6 +237,16 @@ cut_ok:
transcodeFile(Opts.File)
} else if Opts.Directory != "" {
filepath.Walk(Opts.Directory, visit)
+ // calculate (finished) percentage for each video
+ n := len(vidCol.videos)
+ if n > 0 {
+ totalSize := vidCol.sum
+ for i, _ := range vidCol.videos {
+ vidCol.videos[i].pct =
+ int((vidCol.videos[i].sum*1000/totalSize + 5) / 10)
+ }
+ }
+ //fmt.Printf("%v", vidCol)
transcodeVideos(startTime)
}
// par2 creating
@@ -181,22 +263,13 @@ cut_ok:
}
////////////////////////////////////////////////////////////////////////////
-// Function definitions
-
-//==========================================================================
-// Directory & files handling
-
-func visit(path string, f os.FileInfo, err error) error {
- if f.IsDir() {
- return nil
- }
-
- appendVideo(Opts.Directory + string(os.PathSeparator) + path)
- return nil
-}
+// Methods definitions
// Append the video file to the list, unless it's encoded already
-func appendVideo(fname string) {
+// and duplicate none-video files to dest dir as well
+func (vidCol *videoCol) append(path string, size int64) {
+ fname := Opts.Directory + string(os.PathSeparator) + path
+
if Opts.WDirectory == "" && fname[len(fname)-5:] == encodedExt {
debug("Already-encoded file ignored: "+fname, 1)
return
@@ -204,7 +277,16 @@ func appendVideo(fname string) {
fext := strings.ToUpper(fname[len(fname)-4:])
if strings.Index(Opts.Exts, fext) < 0 {
- debug("None-video file ignored: "+fname, 3)
+ // None-video files, dup to dest, hardlink 1st else copy
+ if Opts.WDirectory != "" {
+ src := Opts.Directory + "/" + fname
+ dst := Opts.WDirectory + "/" + fname
+ err := linkFile(src, dst)
+ if err != nil {
+ copyFile(src, dst)
+ }
+ }
+ debug("None-video file '"+fname+"' duplicated to dest dir.", 1)
return
}
@@ -218,9 +300,29 @@ func appendVideo(fname string) {
return
}
- videos = append(videos, fname)
+ vidCol.sum += size
+ vidCol.videos = append(vidCol.videos,
+ video{name: fname, size: size, sum: vidCol.sum})
+
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Function definitions
+
+//==========================================================================
+// Directory & files handling
+
+// visit will visit the source directory and queue videos found there to vidCol
+func visit(path string, f os.FileInfo, err error) error {
+ if f.IsDir() {
+ return nil
+ }
+
+ vidCol.append(path, f.Size())
+ return nil
}
+// visitWDir will visit the work dir and add all directories there to workDirs
func visitWDir(path string, f os.FileInfo, err error) error {
if !f.IsDir() {
return nil
@@ -231,6 +333,7 @@ func visitWDir(path string, f os.FileInfo, err error) error {
return nil
}
+// createPar2s will create par2s files for each dir in workDirs
func createPar2s(workDirs []string) {
fmt.Printf("\n== Creating par2 files\n\n")
for ii, dir := range workDirs {
@@ -241,7 +344,8 @@ func createPar2s(workDirs []string) {
os.Chdir(dir)
dirName := filepath.Base(dir)
- cmd := []string{"par2create", "-u", "zz_" + dirName + ".par2", "*" + encodedExt}
+ // create par2s for all files within dest dir
+ cmd := []string{"par2create", "-u", "zz_" + dirName + ".par2", "*"}
debug(strings.Join(cmd, " "), 1)
out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
@@ -257,17 +361,30 @@ func createPar2s(workDirs []string) {
// Transcode videos in the global videos array
func transcodeVideos(startTime time.Time) {
- videosTotal := len(videos)
- for i, inputName := range videos {
+ videosTotal := len(vidCol.videos)
+ for i, v := range vidCol.videos {
+ inputName := v.name
videoNdx := i + 1
- fmt.Printf("\n== Transcoding [%d/%d]: '%s'\n under %s\n",
- videoNdx, videosTotal, filepath.Base(inputName), filepath.Dir(inputName))
+
+ if Opts.NoClobber && fileExist(getOutputName(inputName)) {
+ debug("Encoded file exist for: "+inputName, 1)
+ continue
+ }
+
+ fmt.Printf("\n== Transcoding [%d/%d] (%d%%): '%s'\n under %s\n",
+ videoNdx, videosTotal, v.pct, filepath.Base(inputName), filepath.Dir(inputName))
transcodeFile(inputName)
fmt.Printf("Time taken so far %s\n", time.Since(startTime))
+ if Opts.MaxC != 0 && videoNdx >= Opts.MaxC {
+ debug("Max conversion count reached.", 1)
+ os.Exit(0)
+ }
+ debug("Taking a breath for: "+Opts.BreathTime.String(), 1)
+ time.Sleep(Opts.BreathTime)
fmt.Printf("Finishing the remaining %d%% in %s\n",
- (videosTotal-videoNdx)*100/videosTotal,
+ 100-v.pct,
time.Duration(int64(float32(time.Since(startTime))*
- float32(videosTotal-videoNdx)/float32(videoNdx))))
+ float32(100-v.pct)/float32(v.pct))))
}
}
@@ -292,7 +409,7 @@ func transcodeFile(inputName string) {
debug(fsinfo, 4)
/*
- Cases when `-map`s are necessary
+ Cases when `-map`s are necessary to deal with here
- more than one audio stream, and we pick eng stream only
- more than one subtitle stream, and
@@ -328,6 +445,22 @@ func transcodeFile(inputName string) {
args := []string{"-i", inputName}
args = append(args, strings.Fields(Opts.OptExtra)...)
args = encodeParametersS(encodeParametersA(encodeParametersV(args)))
+ if Opts.Karaoke {
+ args = append(args, "-filter_complex")
+ args = append(args, "[0:1]pan=mono|c0=c0-c1[a]")
+ args = append(args, "-map")
+ args = append(args, "0:0")
+ args = append(args, "-map")
+ args = append(args, "[a]")
+ args = append(args, "-map")
+ args = append(args, "0:1")
+ }
+ if len(transpFrom) != 0 {
+ args = append(args, "-af")
+ af := fmt.Sprintf("atempo=%s/%s,asetrate=44100*%[2]s/%[1]s",
+ transpFrom, transpTo)
+ args = append(args, af)
+ }
if Opts.Force {
args = append(args, "-y")
}
@@ -445,6 +578,21 @@ func probeFile(inputName string) (string, error) {
return string(out.Bytes()), err
}
+func getVersion(execName string) (string, error) {
+ out := &bytes.Buffer{}
+
+ cmd := exec.Command(execName, "-version")
+ cmd.Stdout = out
+ cmd.Stderr = out
+ err := cmd.Run()
+ if err != nil {
+ return "", err
+ }
+ // OK, then only return the first two lines
+ lines := strings.Split(string(out.Bytes()), "\n")
+ return strings.Join(lines[:2], "\n"), nil
+}
+
// Returns the encode parameters for Subtitle
func encodeParametersS(args []string) []string {
if Opts.SEP != "" {
@@ -472,6 +620,7 @@ func encodeParametersA(args []string) []string {
if Opts.A2Opus {
Opts.AES = "libopus"
}
+ //fmt.Printf("] %v + AE: '%s' '%s'\n", args, Opts.AES, Opts.ABR)
if Opts.AES != "" {
args = append(args, "-c:a", Opts.AES)
}
diff --git a/ffcvt.json b/ffcvt.json
new file mode 100644
index 0000000..98c87b8
--- /dev/null
+++ b/ffcvt.json
@@ -0,0 +1,53 @@
+{
+ "": {
+ "Ext": "_.mkv"
+ },
+ "copy": {
+ "AES": "copy",
+ "VES": "copy",
+ "SES": "-c:s copy",
+ "ABR": "64k",
+ "CRF": "42",
+ "Ext": "_.mkv"
+ },
+ "webm": {
+ "AES": "libopus",
+ "VES": "libvpx-vp9",
+ "SES": "-c:s copy",
+ "ABR": "64k",
+ "CRF": "42",
+ "Ext": "_.mkv"
+ },
+ "x265-opus": {
+ "AES": "libopus",
+ "VES": "libx265",
+ "ABR": "64k",
+ "CRF": "28",
+ "Ext": "_.mkv"
+ },
+ "wx": {
+ "AES": "aac",
+ "AEA": "-q:a 3",
+ "VES": "libx264",
+ "ABR": "48k",
+ "CRF": "33",
+ "Ext": "_.m4v"
+ },
+ "x264-mp3": {
+ "AES": "libmp3lame",
+ "AEA": "-q:a 3",
+ "VES": "libx264",
+ "ABR": "256k",
+ "CRF": "23",
+ "Ext": "_.mp4"
+ },
+ "youtube": {
+ "AES": "libvorbis",
+ "AEA": "-q:a 5",
+ "VES": "libx264",
+ "VEA": "-pix_fmt yuv420p",
+ "ABR": "",
+ "CRF": "20",
+ "Ext": "_.avi"
+ }
+}
diff --git a/ffcvt_cli.sh b/ffcvt_cli.sh
index 6b84a0b..85944a1 100755
--- a/ffcvt_cli.sh
+++ b/ffcvt_cli.sh
@@ -6,4 +6,4 @@ templateFile=$GOPATH/src/github.com/go-easygen/easygen/test/commandlineFlag
exit 1
}
-easygen $templateFile ffcvt_cli | tee /tmp/ffcvt_cli.go | sed '/\tVES\t\tstring/{ N; N; N; N; N; N; N; N; N; s|^.*$|\tEncoding\t// anonymous field to hold encoding values|; }; /\tExt\t\tstring/d; /flag.MFlagsVar/{s/flag.MFlagsVar/flag.Var/; s/, "",/,/; } ' | gofmt > config.go
+easygen $templateFile ffcvt_cli | tee /tmp/ffcvt_cli.go | sed '/\tVES\t\tstring/{ N; N; N; N; N; N; N; N; N; s|^.*$|\tEncoding\t// anonymous field to hold encoding values|; }; /\tExt\t\tstring/d; /flag.MFlagsVar/{s/flag.MFlagsVar/flag.Var/; s/, "",/,/; } ' | goimports > config.go
diff --git a/ffcvt_cli.yaml b/ffcvt_cli.yaml
index 7a07518..abebad2 100644
--- a/ffcvt_cli.yaml
+++ b/ffcvt_cli.yaml
@@ -29,12 +29,18 @@ StructVar: Opts
Options:
# Basic fields for encoding
-
+
+ - Name: Cfg
+ Type: string
+ Flag: cfg
+ Value: '""'
+ Usage: "cfg file to define your own targets: webm/wx/youtube etc"
+
- Name: Target
Type: string
Flag: t
Value: '"webm"'
- Usage: "target type: webm/x265-opus/x264-mp3/wx/youtube/copy"
+ Usage: "target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty"
- Name: VES
Type: string
@@ -198,6 +204,24 @@ Options:
Value: '""'
Usage: "Speed up/down video playback speed (e.g. 1.28)"
+ - Name: Karaoke
+ Type: bool
+ Flag: K,karaoke
+ Value: false
+ Usage: "Add a karaoke audio track to .mp4 MTV"
+
+ - Name: TranspFrom
+ Type: string
+ Flag: tkf
+ Value: '""'
+ Usage: "Transpose song's key from (e.g. C/C#/Db/D etc)"
+
+ - Name: TranspTo
+ Type: string
+ Flag: tkt
+ Value: '""'
+ Usage: "Transpose song's key to (e.g. -tkf C -tkt Db)"
+
- Name: Lang
Type: string
Flag: lang
@@ -242,6 +266,18 @@ Options:
Value: false
Usage: "no clobber, do not queue those already been converted"
+ - Name: BreathTime
+ Type: duration
+ Flag: bt
+ Value: '120*time.Second'
+ Usage: "breath time, interval between conversion to take a breath"
+
+ - Name: MaxC
+ Type: int
+ Flag: maxc
+ Value: 0
+ Usage: max conversion done each run (default no limit)
+
- Name: NoExec
Type: bool
Flag: "n"
diff --git a/ffcvt_proj.yaml b/ffcvt_proj.yaml
deleted file mode 100644
index 7aa9235..0000000
--- a/ffcvt_proj.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-Wireframe:
- Proj: ffcvt
- Desc: ffmpeg convert wrapper tool
- Lang: Go
- Vendor: suntong
- Author: Tong Sun <suntong@cpan.org>
- License: MIT
-
diff --git a/file_dup.go b/file_dup.go
new file mode 100644
index 0000000..14130eb
--- /dev/null
+++ b/file_dup.go
@@ -0,0 +1,39 @@
+// Copy or hardlink a file in Go
+
+package main
+
+import (
+ "io"
+ "os"
+)
+
+func mainSample() {
+ copyFile("test.txt", "test_copy.txt")
+ copyFile("test_copy.txt", "test_link.txt")
+}
+
+// hardlink a file
+func linkFile(src, dst string) error {
+ return os.Link(src, dst)
+}
+
+// Copy a file
+func copyFile(src, dst string) {
+ srcFile, err := os.Open(src)
+ check(err)
+ defer srcFile.Close()
+
+ destFile, err := os.Create(dst) // creates if file doesn't exist
+ check(err)
+ defer destFile.Close()
+
+ _, err = io.Copy(destFile, srcFile) // check first var for number of bytes copied
+ check(err)
+
+ err = destFile.Sync()
+ check(err)
+}
+
+func check(err error) {
+ checkError(err)
+}
diff --git a/preset-large.png b/preset-large.png
deleted file mode 100644
index 4735699..0000000
Binary files a/preset-large.png and /dev/null differ
diff --git a/preset-small.png b/preset-small.png
deleted file mode 100644
index 1720088..0000000
Binary files a/preset-small.png and /dev/null differ
diff --git a/structEnc.go b/structEnc.go
index db9a9be..ec703f4 100644
--- a/structEnc.go
+++ b/structEnc.go
@@ -1,6 +1,12 @@
package main
-import "log"
+import (
+ "embed"
+ "encoding/json"
+ "flag"
+ "io/ioutil"
+ "log"
+)
////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions
@@ -29,82 +35,48 @@ type Encoding struct {
var Defaults map[string]Encoding
func init() {
- initVars()
+ initVars() // define flag vars
debug(Opts.CRF, 3)
- initDefaults()
+
+ flag.Usage = Usage
+ flag.Parse()
+
+ initDefaults() // read & populate defaults from ffcvt.json
+ debug(Opts.CRF, 3)
+
+ getDefault() // re-read defaults based on cli -cfg & -t
+ //fmt.Fprintf(os.Stderr, "Defaults: '%+v'\n", Defaults)
debug(Opts.CRF, 3)
+ flag.Parse() // update Opts again from cli to overwrite defaults if necessary
+ initVals() // update as per cli settings
}
-func initDefaults() {
-
- Defaults = map[string]Encoding{
- "copy": {
- AES: "copy",
- VES: "copy",
- SES: "-c:s copy",
- ABR: "64k",
- CRF: "42",
- Ext: _encodedExt,
- },
- "webm": {
- AES: "libopus",
- VES: "libvpx-vp9",
- SES: "-c:s copy",
- ABR: "64k",
- CRF: "42",
- Ext: _encodedExt,
- },
- "x265-opus": {
- AES: "libopus",
- VES: "libx265",
- ABR: "64k",
- CRF: "28",
- Ext: _encodedExt,
- },
- "wx": {
- AES: "aac",
- AEA: "-q:a 3",
- VES: "libx264",
- ABR: "48k",
- CRF: "33",
- Ext: "_.m4v",
- },
- "x264-mp3": {
- AES: "libmp3lame",
- AEA: "-q:a 3",
- VES: "libx264",
- ABR: "256k",
- CRF: "23",
- Ext: "_.mp4",
- },
- "youtube": {
- // https://trac.ffmpeg.org/wiki/Encode/YouTube
- // https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio
- AES: "libvorbis",
- AEA: "-q:a 5",
- VES: "libx264",
- VEA: "-pix_fmt yuv420p",
- ABR: "",
- CRF: "20",
- Ext: "_.avi",
- },
- }
+//go:embed ffcvt.json
+// see https://pkg.go.dev/embed
+var f embed.FS
+func initDefaults() {
+ data, err := f.ReadFile("ffcvt.json")
+ checkError(err)
+ json.Unmarshal(data, &Defaults)
}
func getDefault() {
+ if Opts.Cfg != "" {
+ data, err := ioutil.ReadFile(Opts.Cfg)
+ checkError(err)
+ err = json.Unmarshal(data, &Defaults)
+ checkError(err)
+ }
if encDefault, ok := Defaults[Opts.Target]; ok {
// debug(encDefault.Ext, 2)
Opts.Encoding = encDefault
// debug(Opts.Encoding.Ext, 2)
// debug(Opts.Ext, 2)
} else {
- log.Fatal(progname + " Error: Wrong target option passed to -t.")
+ log.Fatalf("[%s] Error: Wrong target option passed to -t.", progname)
}
- initVals()
- debug(Opts.CRF, 3)
-
if Opts.Suffix != "" {
Opts.Suffix = "_" + Opts.Suffix
}
diff --git a/test/ffcvt_test.txt b/test/ffcvt_test.txt
index 051b7c6..1ea4628 100644
--- a/test/ffcvt_test.txt
+++ b/test/ffcvt_test.txt
@@ -4,7 +4,8 @@ Usage:
Flags:
- -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy (FFCVT_T)
+ -cfg cfg file to define your own targets: webm/wx/youtube etc (FFCVT_CFG)
+ -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (FFCVT_T)
-ves video encoding method set (FFCVT_VES)
-aes audio encoding method set (FFCVT_AES)
-ses subtitle encoding method set (FFCVT_SES)
@@ -34,6 +35,9 @@ Flags:
strictly in the format of hh:mm:ss, and may repeat (FFCVT_C,CUT)
-S,Seg Split video into multiple segments (strictly in format: hh:mm:ss) (FFCVT_S,SEG)
-Speed Speed up/down video playback speed (e.g. 1.28) (FFCVT_SPEED)
+ -K,karaoke Add a karaoke audio track to .mp4 MTV (FFCVT_K,KARAOKE)
+ -tkf Transpose song's key from (e.g. C/C#/Db/D etc) (FFCVT_TKF)
+ -tkt Transpose song's key to (e.g. -tkf C -tkt Db) (FFCVT_TKT)
-lang language selection for audio stream extraction (FFCVT_LANG)
-sel subtitle encoding language (language picked for reencoded video) (FFCVT_SEL)
-o more options that will pass to ffmpeg program (FFCVT_O)
@@ -42,6 +46,8 @@ Flags:
-p par2create, create par2 files (in work directory) (FFCVT_P)
-nc no clobber, do not queue those already been converted (FFCVT_NC)
+ -bt breath time, interval between conversion to take a breath (FFCVT_BT)
+ -maxc max conversion done each run (default no limit) (FFCVT_MAXC)
-n no exec, dry run (FFCVT_N)
-force overwrite any existing none-empty file (FFCVT_FORCE)
@@ -58,6 +64,7 @@ Details:
-Cut value
Cut segment(s) out to keep. Specify in the form of start-[end],
strictly in the format of hh:mm:ss, and may repeat
+ -K Add a karaoke audio track to .mp4 MTV
-S string
Split video into multiple segments (strictly in format: hh:mm:ss)
-Seg string
@@ -78,6 +85,10 @@ Details:
no audio, output video only
-ato-opus
audio encode to opus, using -abr
+ -bt duration
+ breath time, interval between conversion to take a breath (default 2m0s)
+ -cfg string
+ cfg file to define your own targets: webm/wx/youtube etc
-crf string
the CRF value: 0-51. Higher CRF gives lower quality
(28 for x265, ~ 23 for x264)
@@ -97,8 +108,12 @@ Details:
ffprobe program execution (default "ffprobe -print_format flat")
-force
overwrite any existing none-empty file
+ -karaoke
+ Add a karaoke audio track to .mp4 MTV
-lang string
language selection for audio stream extraction (default "eng")
+ -maxc int
+ max conversion done each run (default no limit)
-n no exec, dry run
-nc
no clobber, do not queue those already been converted
@@ -116,7 +131,11 @@ Details:
-sym
symlinks will be processed as well
-t string
- target type: webm/x265-opus/x264-mp3/wx/youtube/copy (default "webm")
+ target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (default "webm")
+ -tkf string
+ Transpose song's key from (e.g. C/C#/Db/D etc)
+ -tkt string
+ Transpose song's key to (e.g. -tkf C -tkt Db)
-vc
copy video codec
-vea string
@@ -137,122 +156,327 @@ Details:
work directory that hold output files
To reduce output, use `-debug 0`, e.g., `ffcvt -force -debug 0 -f testf.mp4 ...`
+# Test transcoding single file
== Transcoding: StreamSample.mkv
ffcvt: to execute -
ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+# Test transcoding different target types
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=23 -c:a libmp3lame -b:a 256k -q:a 3 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=23 -c:a libmp3lame -b:a 256k -q:a 3 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=33 -c:a aac -b:a 48k -q:a 3 /tmp/StreamSample.m4v
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=33 -c:a aac -b:a 48k -q:a 3 /tmp/StreamSample.m4v
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=20 -pix_fmt yuv420p -c:a libvorbis -q:a 5 /tmp/StreamSample.avi
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=20 -pix_fmt yuv420p -c:a libvorbis -q:a 5 /tmp/StreamSample.avi
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v copy -c:a copy -b:a 64k -c:s copy /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v copy -c:a copy -b:a 64k -c:s copy /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test adding karaoke audio track
+
+== Transcoding: test1.avi
+] ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_karaoke.mp4
+ffcvt: to execute -
+ ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_karaoke.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: test1.avi
+] ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_k.avi
+ffcvt: to execute -
+ ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_k.avi
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test transposing different keys
+Transposing from 'F' (349.23) to 'D' (293.66)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 256k -q:a 3 -af atempo=349.23/293.66,asetrate=44100*293.66/349.23 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 256k -q:a 3 -af atempo=349.23/293.66,asetrate=44100*293.66/349.23 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+Transposing from 'F#' (369.99) to 'Db' (277.18)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -q:a 3 -af atempo=369.99/277.18,asetrate=44100*277.18/369.99 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -q:a 3 -af atempo=369.99/277.18,asetrate=44100*277.18/369.99 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+Transposing from 'Gb' (369.99) to 'A#' (466.16)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -af atempo=369.99/466.16,asetrate=44100*466.16/369.99 StreamSample_.mp3
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -af atempo=369.99/466.16,asetrate=44100*466.16/369.99 StreamSample_.mp3
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test -sym control
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
] Skip symlink file: ./test1.avi
] Skip symlink file: ./test2.avi
] Skip symlink file: ./test3.webm
-== Transcoding [1/1]: 'StreamSample.mkv'
+== Transcoding [1/1] (100%): 'StreamSample.mkv'
under .
] ffmpeg -i ./StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./StreamSample_.mkv
ffcvt: to execute -
ffmpeg -i ./StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./StreamSample_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'StreamSample.mkv'
+== Transcoding [1/4] (100%): 'StreamSample.mkv'
under .
] ffmpeg -i ./StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./StreamSample_.mkv
ffcvt: to execute -
ffmpeg -i ./StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./StreamSample_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (100%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test1_.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (100%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test2_.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test2_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test3_.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test3_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
] Skip symlink file: ./test1.avi
] Skip symlink file: ./test2.avi
] Skip symlink file: ./test3.webm
-== Transcoding [1/1]: 'StreamSample.mkv'
+== Transcoding [1/1] (100%): 'StreamSample.mkv'
under .
] ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
ffcvt: to execute -
ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'StreamSample.mkv'
+== Transcoding [1/4] (100%): 'StreamSample.mkv'
under .
] ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
ffcvt: to execute -
ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (100%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (100%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test2_.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test2_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test3_.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test3_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
+
+== Transcoding [1/4] (100%): 'StreamSample.mkv'
+ under .
+] ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+
+== Transcoding [2/4] (100%): 'test1.avi'
+ under .
+] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+Time taken so far xxx ms
+] Max conversion count reached.
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
+
+== Transcoding [1/4] (100%): 'StreamSample.mkv'
+ under .
+] ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./StreamSample_.mkv
+Time taken so far xxx ms
+] Max conversion count reached.
] Transcoding to /tmp/test
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'StreamSample.mkv'
+== Transcoding [1/4] (100%): 'StreamSample.mkv'
under .
] ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/StreamSample.mkv
ffcvt: to execute -
ffmpeg -i ./StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/StreamSample.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (100%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test1.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test1.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (100%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test2.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test2.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test3.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test/test3.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
diff --git a/test/test-all.sh b/test/test-all.sh
index d0b0963..e160e5f 100755
--- a/test/test-all.sh
+++ b/test/test-all.sh
@@ -9,23 +9,43 @@ FFCVT=../ffcvt
$FFCVT -version
-echo '- Test (config.go) cli help output'
+echo
+echo '# Test (config.go) cli help output'
$FFCVT > /tmp/ffcvt_test.txt 2>&1
-echo - Test transcoding single file
+echo '# Test transcoding single file' | tee -a /tmp/ffcvt_test.txt
$FFCVT -n -debug 0 -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
-echo - Test -sym control
-$FFCVT -t x265-opus -n -d . >> /tmp/ffcvt_test.txt 2>&1
-$FFCVT -t x265-opus -n -d . -sym >> /tmp/ffcvt_test.txt 2>&1
+echo '# Test transcoding different target types' | tee -a /tmp/ffcvt_test.txt
+$FFCVT -t webm -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t x265-opus -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t x264-mp3 -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t wx -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t youtube -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t copy -n -f StreamSample.mkv -w /tmp >> /tmp/ffcvt_test.txt 2>&1
-$FFCVT -n -d . >> /tmp/ffcvt_test.txt 2>&1
-$FFCVT -n -d . -sym >> /tmp/ffcvt_test.txt 2>&1
+echo '# Test adding karaoke audio track' | tee -a /tmp/ffcvt_test.txt
+$FFCVT -n -f test1.avi -t '' -K >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -n -f test1.avi -t '' -karaoke -ext _k.avi >> /tmp/ffcvt_test.txt 2>&1
-$FFCVT -n -sym -debug 2 -d . -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+echo '# Test transposing different keys' | tee -a /tmp/ffcvt_test.txt
+$FFCVT -t x264-mp3 -n -f StreamSample.mkv -w /tmp -tkf F -tkt D -vn >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t x264-mp3 -n -f StreamSample.mkv -w /tmp -tkf 'F#' -tkt Db -vn -abr 72k >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t '' -n -f StreamSample.mkv -tkf Gb -tkt 'A#' -vn -abr 72k -aes libmp3lame -ext _.mp3 >> /tmp/ffcvt_test.txt 2>&1
-echo - Compare test results
-sed -i '/ [0-9.]*[mΒ΅]*s$/d' /tmp/ffcvt_test.txt
+echo '# Test -sym control' | tee -a /tmp/ffcvt_test.txt
+$FFCVT -t x265-opus -n -bt 0.1s -d . >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -t x265-opus -n -bt 0.1s -d . -sym >> /tmp/ffcvt_test.txt 2>&1
+
+$FFCVT -n -bt 0.1s -d . >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -n -bt 0.1s -d . -sym >> /tmp/ffcvt_test.txt 2>&1
+$FFCVT -n -bt 0.1s -d . -sym -maxc 2 >> /tmp/ffcvt_test.txt 2>&1
+FFCVT_MAXC=1 $FFCVT -n -bt 0.1s -d . -sym >> /tmp/ffcvt_test.txt 2>&1
+
+$FFCVT -n -sym -debug 2 -bt 0.1s -d . -w /tmp >> /tmp/ffcvt_test.txt 2>&1
+
+echo '# Compare test results, 0 means AOK:'
+sed -i '/ [0-9.]*[nmΒ΅]*s$/s// xxx ms/' /tmp/ffcvt_test.txt
diff -wU 1 ffcvt_test.txt /tmp/ffcvt_test.txt
ret=$?
diff --git a/test2/ffcvt_test.txt b/test2/ffcvt_test.txt
index d1424c2..aa5d659 100644
--- a/test2/ffcvt_test.txt
+++ b/test2/ffcvt_test.txt
@@ -4,7 +4,8 @@ Usage:
Flags:
- -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy (FFCVT_T)
+ -cfg cfg file to define your own targets: webm/wx/youtube etc (FFCVT_CFG)
+ -t target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (FFCVT_T)
-ves video encoding method set (FFCVT_VES)
-aes audio encoding method set (FFCVT_AES)
-ses subtitle encoding method set (FFCVT_SES)
@@ -34,6 +35,9 @@ Flags:
strictly in the format of hh:mm:ss, and may repeat (FFCVT_C,CUT)
-S,Seg Split video into multiple segments (strictly in format: hh:mm:ss) (FFCVT_S,SEG)
-Speed Speed up/down video playback speed (e.g. 1.28) (FFCVT_SPEED)
+ -K,karaoke Add a karaoke audio track to .mp4 MTV (FFCVT_K,KARAOKE)
+ -tkf Transpose song's key from (e.g. C/C#/Db/D etc) (FFCVT_TKF)
+ -tkt Transpose song's key to (e.g. -tkf C -tkt Db) (FFCVT_TKT)
-lang language selection for audio stream extraction (FFCVT_LANG)
-sel subtitle encoding language (language picked for reencoded video) (FFCVT_SEL)
-o more options that will pass to ffmpeg program (FFCVT_O)
@@ -42,6 +46,8 @@ Flags:
-p par2create, create par2 files (in work directory) (FFCVT_P)
-nc no clobber, do not queue those already been converted (FFCVT_NC)
+ -bt breath time, interval between conversion to take a breath (FFCVT_BT)
+ -maxc max conversion done each run (default no limit) (FFCVT_MAXC)
-n no exec, dry run (FFCVT_N)
-force overwrite any existing none-empty file (FFCVT_FORCE)
@@ -58,6 +64,7 @@ Details:
-Cut value
Cut segment(s) out to keep. Specify in the form of start-[end],
strictly in the format of hh:mm:ss, and may repeat
+ -K Add a karaoke audio track to .mp4 MTV
-S string
Split video into multiple segments (strictly in format: hh:mm:ss)
-Seg string
@@ -78,6 +85,10 @@ Details:
no audio, output video only
-ato-opus
audio encode to opus, using -abr
+ -bt duration
+ breath time, interval between conversion to take a breath (default 2m0s)
+ -cfg string
+ cfg file to define your own targets: webm/wx/youtube etc
-crf string
the CRF value: 0-51. Higher CRF gives lower quality
(28 for x265, ~ 23 for x264)
@@ -97,8 +108,12 @@ Details:
ffprobe program execution (default "ffprobe -print_format flat")
-force
overwrite any existing none-empty file
+ -karaoke
+ Add a karaoke audio track to .mp4 MTV
-lang string
language selection for audio stream extraction (default "eng")
+ -maxc int
+ max conversion done each run (default no limit)
-n no exec, dry run
-nc
no clobber, do not queue those already been converted
@@ -116,7 +131,11 @@ Details:
-sym
symlinks will be processed as well
-t string
- target type: webm/x265-opus/x264-mp3/wx/youtube/copy (default "webm")
+ target type: webm/x265-opus/x264-mp3/wx/youtube/copy, or empty (default "webm")
+ -tkf string
+ Transpose song's key from (e.g. C/C#/Db/D etc)
+ -tkt string
+ Transpose song's key to (e.g. -tkf C -tkt Db)
-vc
copy video codec
-vea string
@@ -137,112 +156,311 @@ Details:
work directory that hold output files
To reduce output, use `-debug 0`, e.g., `ffcvt -force -debug 0 -f testf.mp4 ...`
+# Test transcoding single file
== Transcoding: StreamSample.mkv
ffcvt: to execute -
ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+# Test transcoding different target types
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=23 -c:a libmp3lame -b:a 256k -q:a 3 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=23 -c:a libmp3lame -b:a 256k -q:a 3 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=33 -c:a aac -b:a 48k -q:a 3 /tmp/StreamSample.m4v
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=33 -c:a aac -b:a 48k -q:a 3 /tmp/StreamSample.m4v
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=20 -pix_fmt yuv420p -c:a libvorbis -q:a 5 /tmp/StreamSample.avi
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v libx264 -x264-params crf=20 -pix_fmt yuv420p -c:a libvorbis -q:a 5 /tmp/StreamSample.avi
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -c:v copy -c:a copy -b:a 64k -c:s copy /tmp/StreamSample.mkv
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -c:v copy -c:a copy -b:a 64k -c:s copy /tmp/StreamSample.mkv
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test adding karaoke audio track
+
+== Transcoding: test1.avi
+] ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_karaoke.mp4
+ffcvt: to execute -
+ ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_karaoke.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+
+== Transcoding: test1.avi
+] ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_k.avi
+ffcvt: to execute -
+ ffmpeg -i test1.avi -filter_complex [0:1]pan=mono|c0=c0-c1[a] -map 0:0 -map [a] -map 0:1 test1_k.avi
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test transposing different keys
+Transposing from 'F' (349.23) to 'D' (293.66)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 256k -q:a 3 -af atempo=349.23/293.66,asetrate=44100*293.66/349.23 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 256k -q:a 3 -af atempo=349.23/293.66,asetrate=44100*293.66/349.23 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+Transposing from 'F#' (369.99) to 'Db' (277.18)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -q:a 3 -af atempo=369.99/277.18,asetrate=44100*277.18/369.99 /tmp/StreamSample.mp4
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -q:a 3 -af atempo=369.99/277.18,asetrate=44100*277.18/369.99 /tmp/StreamSample.mp4
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+Transposing from 'Gb' (369.99) to 'A#' (466.16)
+
+== Transcoding: StreamSample.mkv
+] ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -af atempo=369.99/466.16,asetrate=44100*466.16/369.99 StreamSample_.mp3
+ffcvt: to execute -
+ ffmpeg -i StreamSample.mkv -vn -c:a libmp3lame -b:a 72k -af atempo=369.99/466.16,asetrate=44100*466.16/369.99 StreamSample_.mp3
+
+Transcoding completed in xxx ms
+Org Size: 0 MB
+New Size: 0 MB
+Saved: 0%
+# Test -sym control
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
] Skip symlink file: ./subdir/test_s1.avi
+] None-video file './test-all.sh' duplicated to dest dir.
] Skip symlink file: ./test1.avi
] Skip symlink file: ./test2.avi
] Skip symlink file: ./test3.webm
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'test_s1.avi'
+== Transcoding [1/4] (36%): 'test_s1.avi'
under subdir
] ffmpeg -i ./subdir/test_s1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./subdir/test_s1_.mkv
ffcvt: to execute -
ffmpeg -i ./subdir/test_s1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./subdir/test_s1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 64% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (57%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test1_.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 43% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (78%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test2_.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test2_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 22% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test3_.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libx265 -x265-params crf=28 -c:a libopus -b:a 64k ./test3_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
] Skip symlink file: ./subdir/test_s1.avi
+] None-video file './test-all.sh' duplicated to dest dir.
] Skip symlink file: ./test1.avi
] Skip symlink file: ./test2.avi
] Skip symlink file: ./test3.webm
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'test_s1.avi'
+== Transcoding [1/4] (36%): 'test_s1.avi'
under subdir
] ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
ffcvt: to execute -
ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 64% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (57%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 43% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (78%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test2_.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test2_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 22% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test3_.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test3_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
+
+== Transcoding [1/4] (36%): 'test_s1.avi'
+ under subdir
+] ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 64% in xxx ms
+
+== Transcoding [2/4] (57%): 'test1.avi'
+ under .
+] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./test1_.mkv
+Time taken so far xxx ms
+] Max conversion count reached.
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
+
+== Transcoding [1/4] (36%): 'test_s1.avi'
+ under subdir
+] ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
+ffcvt: to execute -
+ ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy ./subdir/test_s1_.mkv
+Time taken so far xxx ms
+] Max conversion count reached.
] Transcoding to /tmp/test2
+] None-video file './ffcvt_test.txt' duplicated to dest dir.
+] None-video file './test-all.sh' duplicated to dest dir.
-== Transcoding [1/4]: 'test_s1.avi'
+== Transcoding [1/4] (36%): 'test_s1.avi'
under subdir
] ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/subdir/test_s1.mkv
ffcvt: to execute -
ffmpeg -i ./subdir/test_s1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/subdir/test_s1.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 64% in xxx ms
-== Transcoding [2/4]: 'test1.avi'
+== Transcoding [2/4] (57%): 'test1.avi'
under .
] ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test1.mkv
ffcvt: to execute -
ffmpeg -i ./test1.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test1.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 43% in xxx ms
-== Transcoding [3/4]: 'test2.avi'
+== Transcoding [3/4] (78%): 'test2.avi'
under .
] ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test2.mkv
ffcvt: to execute -
ffmpeg -i ./test2.avi -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test2.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 22% in xxx ms
-== Transcoding [4/4]: 'test3.webm'
+== Transcoding [4/4] (100%): 'test3.webm'
under .
] ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test3.mkv
ffcvt: to execute -
ffmpeg -i ./test3.webm -c:v libvpx-vp9 -b:v 0 -crf 42 -c:a libopus -b:a 64k -c:s copy /tmp/test2/test3.mkv
+Time taken so far xxx ms
+] Taking a breath for: xxx ms
+Finishing the remaining 0% in xxx ms
+Transcoding completed in xxx ms
Org Size: 0 MB
New Size: 0 MB
Saved: 0%
diff --git a/video-crf.png b/video-crf.png
deleted file mode 100644
index 50e214f..0000000
Binary files a/video-crf.png and /dev/null differ