Created repo

This commit is contained in:
Matt McWilliams 2021-05-08 12:53:26 -04:00
commit bde2e319bd
48 changed files with 2435 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
videos
output
node/node_modules

222
README.md Normal file
View File

@ -0,0 +1,222 @@
---
header-includes:
- \hypersetup{colorlinks=true,
allbordercolors={0 0 1},
pdfborderstyle={/S/U/W 1}}
---
# UNIX FOR ARTISTS
### Fun with FFMPEG
## Requirements
The following applications and runtime environments are needed to run the scripts used in the workshop.
Installation instructions are available below, broken down by operating system.
* [Bash](https://www.gnu.org/software/bash/) - *You probably have it already!*
* [FFMPEG](https://www.ffmpeg.org/)
* [ImageMagick](https://imagemagick.org/index.php)
* [Youtube-dl](https://ytdl-org.github.io/youtube-dl/index.html)
* [Golang](https://golang.org/)
* \*[Python3](https://www.python.org/downloads/) (*optional*)
* \*[Node.js](https://nodejs.org/en/) (*optional*)
It's very helpful to have a text editor that you can comfortably read and edit scripts in.
This selection of text editors ranges from free and open source to "free to use" and "free to try".
You can also use TextEdit or Notepad or any text editing application you prefer as long as it's capable of editing in plaintext.
* [Atom](https://atom.io/) - Free. *macOS, Windows and Linux.*
* [GNU Emacs](https://www.gnu.org/software/emacs/download.html) - Free. *Linux, macOS and Windows.*
* [Visual Studio Code](https://code.visualstudio.com/download) - Free. *macOS, Windows and Linux.*
* [Sublime Text 3](https://www.sublimetext.com/3) - Free to evaluate. *macOS, Windows and Linux.*
* [Notepad++](https://notepad-plus-plus.org/downloads/) - Free to use. *Windows only.*
* [BBEdit](http://www.barebones.com/products/bbedit/download.html) - Freemium. *macOS only.*
-------
## macOS
![The macOS Terminal](img/mac_terminal.png "The macOS Terminal")
Open the Terminal app located in your Applications folder.
Bash 3.2 is available by default on macOS.
##### Install Homebrew
Go to [https://brew.sh](https://brew.sh) and run the command under the "Install Homebrew" heading after pasting it into your Terminal.
You will be asked to confirm a few things while Homebrew installs.
Once the installation process is complete you can install the applications we will be using in this workshop by running the following commands:
```bash
brew install ffmpeg imagemagick youtube-dl
```
Optionally, you can install Node.js for a single example that we will be covering in the workshop.
```bash
brew install node
```
Make sure that you have a compatible version of Python installed by running the following command:
```bash
python3 --version
```
It should print out a value similar to `Python 3.8.2`.
Anything higher than 3.7.0 will work.
##### Install Golang on macOS
Go to the [Golang download site](https://golang.org/doc/install).
Select the "Mac" tab under "2. Go install." if not already selected and download the .pkg installer using the "Download Go for Mac" button.
Open the installer and follow the instructions.
Confirm that Go is installed by running the following command in Terminal:
```bash
go version
```
If Golang has been installed correctly you can now install `primitive`.
```bash
go get -u github.com/fogleman/primitive
```
-------
## Windows 10
Windows has its own command-line interpreter, but in order to keep code and examples consistent across different operating systems you will need to install the Windows Subsystem for Linux (WSL).
##### Enable WSL
Open the "Turn Windows Features on or off" dialog by searching in your command bar.
![Turn Windows features on or off](img/windows_10_turn_windows_features.png "Turn Windows features on or off")
Enable "Virtual Machine Platform" and "Windows Subsystem for Linux" by selecting the checkboxes next to the items and hit "OK".
![Enable WSL and Virtual Machine Platform](img/windows_10_enable.png "Enable WSL and Virtual Machine Platform")
Restart your computer for changes to apply.
##### Install Ubuntu 20.04 LTS
Open the Microsoft Store and search for "Ubuntu".
For the latest LTS version, install `Ubuntu 20.04`.
![Install Ubuntu 20.04](img/windows_10_install.png "Install Ubuntu 20.04 through the Microsoft Store")
Other versions of Ubuntu or openSUSE may work, but will be subject to differences in behavior.
![Select the Ubuntu shell](img/windows_10_ubuntu.png "Select the Ubuntu shell")
Once you've installed Ubuntu, you can open the shell and begin running commands using Bash.
##### Install Requirements on Windows 10
![The Ubuntu shell on Windows 10](img/windows_10_terminal.png "The Ubuntu shell on Windows 10")
You can now run the following commands to install the applications for this workshop:
```bash
sudo apt update
sudo apt install -y ffmpeg imagemagick youtube-dl
```
Optionally install Node.js.
```bash
sudo apt install -y nodejs npm
```
##### Install Golang on Windows 10
Go to the [Golang download site](https://golang.org/doc/install).
Select the "Windows" tab under "2. Go install." if not already selected and download the .msi installer by using the "Download Go for Windows" button.
Confirm that Go is installed by running the following command in your shell:
```bash
go version
```
If Golang has been installed correctly you can now install `primitive`.
```bash
go get -u github.com/fogleman/primitive
```
-------
Additional tutorials for installing Bash on Windows 10.
* [How to Install Linux Bash Shell on Windows 10](https://itsfoss.com/install-bash-on-windows/)
* [Install Bash on Windows 10](https://bayton.org/docs/linux/ubuntu/install-bash-on-windows-10-build-14316/)
-------
## Linux
Open up your preferred terminal application and install the required applications on a Debian-based system.
```bash
sudo apt install -y ffmpeg imagemagick youtube-dl
```
Or, if your distribution uses the `yum` package manager.
```bash
yum install ffmpeg imagemagick youtube-dl
```
Optionally install Node.js.
```bash
sudo apt install -y nodejs npm
```
Or using yum.
```
yum install nodejs
```
##### Install Golang on Linux
Go to the [Golang download site](https://golang.org/doc/install).
Select the "Linux" tab under "2. Go install." if not already selected and download the .tar.gz archive using the "Download Go for Linux" button.
Navigate to the folder you downloaded the archive into.
For the following commands, `go1.16.3.linux-amd64.tar.gz` can be replaced by the filename of the version that you just downloaded.
```bash
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.16.3.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
```
Either restart your terminal or run the command `source $HOME/.profile` to apply the changes just made to your system.
Confirm that Go is installed by running the following command:
```bash
go version
```
If Golang has been correctly installed you can now install `primitive`.
```bash
go get -u github.com/fogleman/primitive
```

BIN
audio/spring.mp3 Normal file

Binary file not shown.

22
bash/arguments.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/bash -e
#####################################################
#
# Arguments are provided to commands and scripts usually
# separated by a space. They can be read and used within
# bash scripts by referencing them with the "$#" notation.
#
# Usage: bash bash/arguments.sh <first> <second> <third> <etc>
#
#####################################################
echo "You entered \"$1\" as the first argument"
# Using a slightly different notation
if [ "${2}" != "" ]; then
echo "You entered \"${2}\" as the second argument"
fi
if [ "${3}" != "" ]; then
echo "All of the arguments you entered \"${@}\""
fi

28
bash/cat.sh Normal file
View File

@ -0,0 +1,28 @@
#!/bin/bash -e
#####################################################
#
# "cat" is an application that prints text files
# into your terminal and allows you to concatinate
# multiple files together.
#
# Usage: bash bash/cat.sh
#
#####################################################
cat "text/cat1.txt"
sleep 2
# Concatinate multiple files and print the result
cat "text/cat2.txt" "text/cat3.txt"
sleep 2
# Concatinate all files into a new file using the ">" redirect operator
cat "text/cat1.txt" "text/cat2.txt" "text/cat3.txt" > "text/all.txt"
sleep 2
# Print the contents of this file
cat bash/cat.sh

26
bash/functions.sh Normal file
View File

@ -0,0 +1,26 @@
#!/bin/bash -e
#####################################################
#
# Functions can be defined using the "name () {}"
# notation where the code is contained within the
# brackets. Functions receive arguments the same way
# a script does, using the "$#" notation.
#
#####################################################
myFunction () {
if [ "${1}" != "" ]; then
echo "Your first argument is \"${1}\""
else
echo "Called myFunction with no arguments"
fi
}
# Invoke your function as if it were any other command
# or application located in your $PATH.
myFunction
sleep 2
myFunction "First argument"

72
bash/ifelse.sh Normal file
View File

@ -0,0 +1,72 @@
#!/bin/bash -e
#####################################################
#
# If/else statements allow you to run select commands
# if conditions are met or if they are not.
#
# Usage: bash bash/ifelse.sh
#
#####################################################
NUMBER=3
COMPARE="Compare this string"
SUBSTRING="not"
SUBSTRING2="this"
# Evaluate whether two numbers are equal with the "-eq" operator
if [ ${NUMBER} -eq 3 ]; then
echo "Variable \$NUMBER is equal to 3"
else
echo "Variable \$NUMBER is NOT equal to 3"
fi
sleep 2
# Evaluate whether a string contains substrings. Uses the
# "elif" operator to check if a second condition is met if
# the first one fails.
if [[ "${COMPARE}" == *"${SUBSTRING}"* ]]; then
echo "String \$COMPARE contains substring \"${SUBSTRING}\""
elif [[ "${COMPARE}" == *"${SUBSTRING2}"* ]]; then
echo "String \$COMPARE contains substring \"${SUBSTRING2}\""
else
echo "String \$COMPARE contains none of the substrings"
fi
sleep 2
# Evaluate whether a file exists with the "-f" operator
if [ -f "text/cat1.txt" ]; then
echo "File text/cat1.txt exists. Here are the contents:"
cat text/cat1.txt
else
echo "File text/cat1.txt does NOT exist"
fi
sleep 2
# Evaluate whether a directory exists with the "-d" operator
if [ -d "not_here" ]; then
echo "Directory not_here exists. Here are the contents:"
ls not_here
else
echo "Directory not_here does NOT exist"
fi
# Evaluate whether or not one condition OR another
# is met using the "||" operators.
# The "-gt" operator determines if a number is
# greater than a compared number and "-lt" for
# less than.
if [ $NUMBER -gt 1 ] || [ $NUMBER -lt 5]; then
echo "Variable \$NUMBER is greater than 1 or less than 5"
fi
# Evaluate whether both conditions are met using
# the AND operator "&&"
if [[ "${COMPARE}" == *"${SUBSTRING}"* ]] && [[ "${COMPARE}" == *"${SUBSTRING2}"* ]]; then
echo "String \$COMPARE contains both substrings"
else
echo "String \$COMPARE does not contain both substrings"
fi

50
bash/paths.sh Normal file
View File

@ -0,0 +1,50 @@
#!/bin/bash -e
#####################################################
#
# The commands "cd",
#
# Usage: bash bash/paths.sh
#
#####################################################
THIS_DIRECTORY=`pwd`
echo "${THIS_DIRECTORY}"
sleep 2
# Go to your "home" directory for your user
cd ~/
echo "You are now in:"
pwd
sleep 2
# Go to the root of your filesystem
cd /
echo "You are now in:"
pwd
sleep 2
# List all files and directories in "long" and "readable" format
ls -lh
sleep 10
# Return to the directory you started in
cd "${THIS_DIRECTORY}"
echo "You are now back in:"
pwd
sleep 2
# Go into the "bash" sub-directory
cd bash
echo "You are now in:"
pwd
# Return to the parent directory, the one you started in,
# by using the double period ".." notation
cd ../
echo "You are now back in:"
pwd

33
bash/variables.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/bash -e
#####################################################
#
# Variables are stored values that can be set locally
# in the script or inherited from your environment.
#
# Usage: bash bash/variables.sh
#
#####################################################
# Set this variable to run the script
VARIABLE=""
NUMBER=3
# Save output of the command "pwd" to a variable
# by running it in a subshell
CURRENT_DIR=`pwd`
# A different notation for subshells, this time
# saving the contents of a text file to a variable
FILE=$(cat text/cat1.txt)
if [ "${VARIABLE}" != "" ]; then
echo "Your variable: ${VARIABLE}"
echo "Your number: ${NUMBER}"
echo "Your current dir: ${CURRENT_DIR}"
# $PATH is an environment variable set by your system
echo "Your username: ${USER}"
echo "Your system \$PATH: ${PATH}"
else
echo "Please create within the quotes on line 13"
fi

23
download.sh Normal file
View File

@ -0,0 +1,23 @@
#!/bin/bash -e
VIDEOS=(
bear.mkv
bird.mkv
flower.mkv
ice.mkv
metamorphosis.mkv
negative.mkv
net.mkv
pig.mkv
stream.mkv
surface.mkv
water.mkv
)
mkdir -p videos
mkdir -p output
for video in ${VIDEOS[@]}; do
curl "https://sixteenmillimeter.com/unix4artists/${video}" -o "videos/${video}"
sleep 2
done

View File

@ -0,0 +1,28 @@
#!/bin/bash -e
#####################################################
#
# This script will combine the audio from one file and the
# video from another file.
#
# Usage: bash ffmpeg/audio/audiovideo.sh <input audio> <input video> <output video>
#
#####################################################
AUDIO="${1}"
VIDEO="${2}"
OUTPUT="${3}"
# Check if output file extension is .mkv
if [[ "${OUTPUT}" != *".mkv" ]]; then
echo "Please use an .mkv extension on your output file argument"
exit 1
fi
ffmpeg -i "${VIDEO}" \
-i "${AUDIO}" \
-c copy \
-map 0:v:0 \
-map 1:a:0 \
-shortest \
"${OUTPUT}"

21
ffmpeg/audio/extract.sh Normal file
View File

@ -0,0 +1,21 @@
#!/bin/bash -e
#####################################################
#
# This script will extract the audio from a video
# losslessly into a .wav file.
#
# Usage: bash ffmpeg/audio/extract.sh <input video> <output audio>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .wav
if [[ "${OUTPUT}" != *".wav" ]]; then
echo "Please use an .wav extension on your output file argument"
exit 1
fi
ffmpeg -i "${INPUT}" -vn "${OUTPUT}"

View File

@ -0,0 +1,35 @@
#!/bin/bash -e
#####################################################
#
# This script will take an audio file and an image and
# combine them to create a video.
#
# Usage: bash ffmpeg/audio/stillaudio.sh <input audio> <input image> <output video>
#
#####################################################
AUDIO="${1}"
IMAGE="${2}"
OUTPUT="${3}"
# Check if file extension is .mkv or .MKV
if [[ "${OUTPUT}" != *".mkv" ]]; then
echo "Please use an .mkv extension on your output file argument"
exit 1
fi
ffmpeg -loop 1 \
-framerate 2 \
-i "${IMAGE}" \
-i "${AUDIO}" \
-c:v libx264 \
-preset slow \
-tune stillimage \
-pix_fmt yuv420p \
-crf 12 \
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1:color=black" \
-c:a aac \
-b:a 192k \
-shortest \
"${OUTPUT}"

23
ffmpeg/basic/info.sh Normal file
View File

@ -0,0 +1,23 @@
#!/bin/bash -e
#####################################################
#
# "ffprobe" is an application that comes bundled with
# the ffmpeg installation. It will print file
# information about the streams within video and audio
# files.
#
# Usage: bash ffmpeg/basic/info.sh <file>
#
#####################################################
if [ "${1}" == "" ]; then
echo "Please include a path to a file as the first argument"
exit 1
fi
if [ "${2}" != "json" ]; then
ffprobe -v quiet -show_format -show_streams "${1}"
else
ffprobe -v quiet -print_format json -show_format -show_streams "${1}"
fi

55
ffmpeg/basic/prores.sh Normal file
View File

@ -0,0 +1,55 @@
#!/bin/bash -e
#####################################################
#
# This command will re-encode a video into an MOV
# container with a ProRes HQ (422) video stream and
# audio preserved in its original format.
#
# -i "${INPUT}"
# Sets the first argument, saved as variable "$INPUT"
# as the input file that ffmpeg will re-encode. Must
# go at the beginning of your command.
#
# -c:v prores_ks
# Sets the video codec of the output video to be ProRes
# using the "prores_kostya" library.
#
# -profile:v 3
# Sets the ProRes profile to 3 or "HQ" use on the output
# video stream. The profiles available: 0 = proxy (PR),
# 1 = light (LT), 2 = standard (ST), 3 = high quality (HQ),
# 4 = 4444, 5 = 4444 (XQ)
#
# -c:a copy
# Copies the audio stream and preserves the format
# and codec from the original file.
#
# "${OUTPUT}"
# The output video file, which should be the last
# argument you provide.
#
# More info: https://avpres.net/FFmpeg/im_ProRes.html
# https://codecs.multimedia.cx/2012/03/a-few-words-about-my-prores-encoder/
#
# Usage: bash ffmpeg/basic/prores.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if input and output arguments are supplied
if [ "${INPUT}" == "" ] || [ "${OUTPUT}" == "" ]; then
echo "Please provide input and output files as your first and second arguments"
exit 1
fi
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 2
fi
# Command to re-encode the input video into ProRes
ffmpeg -i "${INPUT}" -c:v prores_ks -profile:v 3 -c:a copy "${OUTPUT}"

61
ffmpeg/basic/reencode.sh Normal file
View File

@ -0,0 +1,61 @@
#!/bin/bash -e
#####################################################
#
# This command will re-encode a video into an MP4
# container with H264 video and AAC audio encoded to
# 96K. Each of the arguments are explained as follows:
#
# -i "${INPUT}"
# Sets the first argument, saved as variable "$INPUT"
# as the input file that ffmpeg will re-encode. Must
# go at the beginning of your command.
#
# -codec:video libx264
# Sets the video codec to libx264, which is the library
# ffmpeg uses to encode H264 video streams.
#
# -preset slow
# Sets the preset of the H264 encoding process to run
# slow, which can yield higher quality imagery at the
# expense of speed. Available options:
# ultrafast, superfast, veryfast, faster, medium (default),
# slow, slower, veryslow, placebo
#
# -crf 22
# Sets the Constant Rate Factor of the video stream,
# which determines the data rate. The range of -crf
# values range from 0 to 51, with 0 being visually
# lossless and 51 being extremely compressed.
#
# -codec:audio aac
# Sets the codec of the output audio stream to AAC.
#
# -b:a
# Sets the data rate of the audio to 192kbit/s.
#
# "${OUTPUT}"
# The output video file, which should be the last
# argument you provide.
#
# Usage: bash ffmpeg/basic/reencode.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if input and output arguments are supplied
if [ "${INPUT}" == "" ] || [ "${OUTPUT}" == "" ]; then
echo "Please provide input and output files as your first and second arguments"
exit 1
fi
# Check if file extension is .mp4
if [[ "${OUTPUT}" != *".mp4" ]]; then
echo "Please use an .mp4 extension on your output file argument"
exit 2
fi
# Command to re-encode the input video into H264/AAC
ffmpeg -i "${INPUT}" -codec:video libx264 -preset slow -crf 22 -codec:audio aac -bitrate:audio 192k "${OUTPUT}"

74
ffmpeg/basic/social.sh Normal file
View File

@ -0,0 +1,74 @@
#!/bin/bash -e
#####################################################
#
# Youtube: https://trac.ffmpeg.org/wiki/Encode/YouTube
# Vimeo: https://vimeo.zendesk.com/hc/en-us/articles/360056550451-Video-and-audio-compression-guidelines
#
# Usage: bash ffmpeg/basic/social.sh <input video>
#
#####################################################
INPUT=`realpath "${1}"`
FILENAME=`basename "${INPUT}"`
FILE="${FILENAME%.*}"
INPUTDIR=`dirname "${INPUT}"`
if [ "${INPUT}" == "" ]; then
echo "Please provide an input video as your first argument"
exit 1
fi
YOUTUBE="${INPUTDIR}/${FILE}-youtube.mkv"
VIMEO="${INPUTDIR}/${FILE}-vimeo.mkv"
TWITTER="${INPUTDIR}/${FILE}-twitter.mp4"
INSTAGRAM="${INPUTDIR}/${FILE}-instagram.mp4"
# Encode a video for YouTube
ffmpeg -i "${INPUT}" \
-vcodec libx264 \
-preset slow \
-vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" \
-pix_fmt yuv420p \
-crf 12 \
-c:a aac \
-b:a 192k \
"${YOUTUBE}"
# Encode a video for Vimeo using the
# recommended settings for 720p video
ffmpeg -i "${INPUT}" \
-vcodec libx264 \
-preset slow \
-pix_fmt yuv420p \
-profile:v main \
-b:v 7500k \
-minrate 5000k \
-maxrate 10000k \
-c:a aac \
-ar 48000 \
"${VIMEO}"
# Encode video for Twitter
ffmpeg -i "${INPUT}" \
-pix_fmt yuv420p \
-vcodec libx264 \
-acodec aac \
-vb 2048k\
-minrate 1024k \
-maxrate 4096k \
-bufsize 1024k \
-ar 44100 \
-ac 2 \
-strict experimental \
"${TWITTER}"
# Crop and encode for Instagram
ffmpeg -i "${INPUT}" \
-vf "crop=w='min(iw\,ih)':h='min(iw\,ih)',setsar=1" \
-framerate 30 \
-vcodec mpeg4 \
-vb 8000k \
-strict experimental \
-q:v 0 \
"${INSTAGRAM}"

24
ffmpeg/basic/support.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash -e
#####################################################
#
# These commands print out the formats and codecs that
# your installation of ffmpeg supports. You can customize
# ffmpeg to support additional formats, codecs and features
# by using installation options or flags when compiling
# from source.
#
# Mac (Homebrew): https://gist.github.com/Piasy/b5dfd5c048eb69d1b91719988c0325d8
# Linux & Windows: https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu
#
# Usage: bash ffmpeg/format/support.sh
#
#####################################################
echo "Formats your FFMPEG install supports:"
ffmpeg -formats
sleep 10
echo "Codecs your FFMPEG install supports:"
ffmpeg -codecs

24
ffmpeg/basic/trim.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash -e
#####################################################
#
# This script will trim your video to a 5 second length
# starting 2 seconds into it and write the output as a
# ProRes video file.
#
# Usage: bash ffmpeg/basic/trim.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 2
fi
# Trim the video using the "-t" argument with the value 5 (seconds)
# and starting at 00:00:02 (h:m:s) using the "-ss" seeking argument
ffmpeg -ss 00:00:02 -i "${INPUT}" -t 5 -c:v prores_ks -profile:v 3 -c:a copy "${OUTPUT}"

41
ffmpeg/filter/bipack.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/bash -e
#####################################################
#
# Bipack.sh will simulate the optical printing effect
# of superimposing video A over video B with a third
# video (the matte) as the alpha layer.
#
# Usage: bash ffmpeg/filter/bipack.sh <input video A> <input video B> <input video matte> <output video>
#
#####################################################
A=${1}
B=${2}
MATTE=${3}
OUTPUT_FILE=${4}
CONTRAST=100
W=1280
H=720
RATE=24
echo "Running bipack on image sources ${A} and ${B} with ${MATTE} as the matte layer..."
time ffmpeg -y -i $A -i $B -i $MATTE \
-filter_complex "
color=0x000000:size=${W}x${H}, format=rgb24[bla];
[0] fps=${RATE},scale=${W}:${H}:force_original_aspect_ratio=decrease,format=rgb24 [a];
[1] fps=${RATE},scale=${W}:${H}:force_original_aspect_ratio=decrease,format=rgb24 [b];
[2] fps=${RATE},scale=${W}:${H}:force_original_aspect_ratio=decrease,format=gray,
smartblur=1, eq=contrast=$CONTRAST, format=rgb24 [maska];
[2] fps=${RATE},scale=${W}:${H}:force_original_aspect_ratio=decrease,format=gray,
smartblur=1, eq=contrast=$CONTRAST, negate, format=rgb24 [maskb];
[bla][a][maska] maskedmerge, format=rgb24 [pass1];
[pass1][b][maskb] maskedmerge, format=rgb24
" \
-r $RATE \
-c:v prores_ks \
-profile:v 3 \
-shortest \
$OUTPUT_FILE

View File

@ -0,0 +1,58 @@
#!/bin/bash
set -e
W=1280
H=720
A=${1}
B=${2}
MATTE=${3}
OUTPUT=${4}
MATTE1=matte1.mov
MATTE2=matte2.mov
PASS1=pass1.mov
PASS2=pass2.mov
echo "Generating mattes from $MATTE..."
ffmpeg -y -i "$MATTE" -vf eq=saturation=0:contrast=100,smartblur=1 -c:v prores_ks -profile:v 4 "$MATTE1"
ffmpeg -y -i "$MATTE1" -vf negate -c:v prores_ks -profile:v 4 "$MATTE2"
echo "Applying matte to $A..."
ffmpeg -y -i "$A" -i "$MATTE1" \
-filter_complex 'color=0x000000:size=1280x720,format=rgb24[bla];[bla][0][1]maskedmerge' \
-c:v prores_ks \
-profile:v 4 \
-shortest \
"$PASS1"
echo "Applying matte to $B..."
ffmpeg -y -i "$B" -i "$MATTE2" \
-filter_complex 'color=0x000000:size=1280x720,format=rgb24[bla];[bla][0][1]maskedmerge' \
-c:v prores_ks \
-profile:v 4 \
-shortest \
"$PASS2"
echo "Cleaning up tmp matte files..."
rm "$MATTE1"
rm "$MATTE2"
echo "Combining matted layers together into $OUTPUT_FILE..."
ffmpeg -y -i "$PASS1" -i "$PASS2" \
-filter_complex "[0][1]blend=all_mode='lighten'" \
-c:v prores_ks \
-profile:v 3 \
-shortest \
"$OUTPUT"
echo "Cleaning up temp files..."
rm "$PASS1"
rm "$PASS2"

View File

@ -0,0 +1,27 @@
#!/bin/bash -e
#####################################################
#
# This script will increase the contrast of an input
# video by 2 (acceptable values -1000 to 1000) and
# increases the brightness by 0.4 (-1 to 1)
#
# Usage: bash ffmpeg/filter/brightness.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
ffmpeg -i "${INPUT}" \
-vf eq=contrast=2:brightness=0.4 \
-c:v prores_ks \
-profile:v 3 \
"${OUTPUT}"

View File

@ -0,0 +1,25 @@
#!/bin/bash -e
#####################################################
#
# This script will convert a color video to black &
# white by reducing color saturation to 0.
#
# Usage: bash ffmpeg/filter/desaturate.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
ffmpeg -i "${INPUT}" \
-vf hue=s=0 \
-c:v prores_ks \
-profile:v 3 \
"${OUTPUT}"

View File

@ -0,0 +1,25 @@
#!/bin/bash -e
#####################################################
#
# This script will convert a color video to black &
# white by converting to grayscale format.
#
# Usage: bash ffmpeg/filter/grayscale.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
ffmpeg -i "${INPUT}" \
-vf format=gray \
-c:v prores_ks \
-profile:v 3 \
"${OUTPUT}"

25
ffmpeg/filter/negative.sh Normal file
View File

@ -0,0 +1,25 @@
#!/bin/bash -e
#####################################################
#
# This script will invert all colors of a video and
# save the output in a ProRes video.
#
# Usage: bash ffmpeg/filter/negative.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
ffmpeg -i "${INPUT}" \
-vf negate \
-c:v prores_ks \
-profile:v 3 \
"${OUTPUT}"

98
ffmpeg/filter/title.sh Normal file
View File

@ -0,0 +1,98 @@
#!/bin/bash -e
#####################################################
#
# This script will create a 10 second title and
# overlay it on a video with a blend mode:
#
# addition
# grainmerge
# and
# average
# darken
# difference
# grainextract
# divide
# dodge
# freeze
# exlusion
# extremity
# glow
# hardlight
# hardmix
# heat
# lighten
# linearlight
# multiply
# multiply128
# negation
# normal
# or
# overlay
# pheonix
# pinlight
# reflect
# screen
# softlight
# subtract
# vividlight
# xor
#
# Usage: bash ffmpeg/filter/title.sh <title text> <input video> <output video> <blendmode> "invert" (optional)
#
#####################################################
W=720
H=480
TEXT="${1}"
INPUT="${2}"
OUTPUT="${3}"
BLENDMODE="${4}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
if [ "${BLENDMODE}" == "" ]; then
BLENDMODE="multiply"
fi
if [ "${5}" == "invert" ]; then
convert -size ${W}x${H} \
-background white \
-fill black \
-gravity Center \
-weight 700 \
-pointsize 100 \
label:"${TEXT}" \
title.png
else
convert -size ${W}x${H} \
-background black \
-fill white \
-gravity Center \
-weight 700 \
-pointsize 100 \
label:"${TEXT}" \
title.png
fi
ffmpeg -y -loop 1 \
-framerate 24 \
-f image2 \
-i title.png \
-s ${W}x${H} \
-c:v prores_ks \
-profile:v 3 \
-t 10 \
"title.mov"
ffmpeg -i "${INPUT}" -i "title.mov" -filter_complex "[0][1]blend=${BLENDMODE}" "${OUTPUT}"
# Clean up by removing the image and title video
rm title.png
rm title.mov

30
ffmpeg/frames/basic.sh Normal file
View File

@ -0,0 +1,30 @@
#!/bin/bash -e
#####################################################
#
# This script splits a video into an image sequence
# and then stitches the image sequence back into a
# ProRes video.
#
# Usage: bash ffmpeg/frames/basic.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
mkdir -p frames
ffmpeg -i "${INPUT}" frames/frame-%06d.png
sleep 20
ffmpeg -f image2 -i frames/frame-%06d.png -c:v prores_ks -profile:v 3 "${OUTPUT}"
rm -rf frames

View File

@ -0,0 +1,41 @@
#!/bin/bash -e
#####################################################
#
# Frameloom is a script that exports all frames from
# two videos and alternates them in
#
# Usage: bash ffmpeg/frames/frameloom.sh <input video1> <input video2> <output video>
#
#####################################################
TMPDIR=`mktemp -d`
i=0
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] ;
then
echo "Not enough arguments supplied"
fi
# Check if output file extension is .mov
if [[ "${3}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
mkdir -p $TMPDIR
ffmpeg -i "$1" -compression_algo raw -pix_fmt rgb24 "${TMPDIR}export-%08d_a.tif"
ffmpeg -i "$2" -compression_algo raw -pix_fmt rgb24 "${TMPDIR}export-%08d_b.tif"
#rm -r $TMP
for filename in ${TMPDIR}*.tif; do
value=`printf %08d $i`
mv "$filename" "${TMPDIR}render_${value}.tif"
i=`expr $i + 1`
done
ffmpeg -r 24 -f image2 -s 1280x720 -i "${TMPDIR}render_%08d.tif" -c:v prores_ks -profile:v 3 -y "$3"
rm -r "$TMPDIR"

39
ffmpeg/frames/loop.sh Normal file
View File

@ -0,0 +1,39 @@
#!/bin/bash -e
#####################################################
#
# This script exports all frames of your video into
# a directory named "frames" and then runs a convert
# command which does edge detection on each frame.
# Then all frames are stitched back into a ProRes
# video.
#
# Usage: bash ffmpeg/frames/loop.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
mkdir -p frames
ffmpeg -i "${INPUT}" frames/frame-%06d.png
FRAMES=frames/*.png
for frame in ${FRAMES}; do
echo "Running edge detection on $frame..."
convert "${frame}" -edge 1 "${frame}"
done
sleep 10
ffmpeg -f image2 -i frames/frame-%06d.png -c:v prores_ks -profile:v 3 "${OUTPUT}"
rm -rf frames

View File

@ -0,0 +1,53 @@
#!/bin/bash -e
#####################################################
#
# This script will export your video to frames and
# use the application "primitive" to reproduce the
# image with polygonal primitive shapes. Then the
# converted frames are p
#
# The modes available for the -m argument are:
# 0=combo
# 1=triangle
# 2=rect
# 3=ellipse
# 4=circle
# 5=rotatedrect
# 6=beziers
# 7=rotatedellipse
# 8=polygon
#
# primitive repo : https://github.com/fogleman/primitive
#
# Usage: bash ffmpeg/frames/primitive.sh <input video> <output video>
#
#####################################################
INPUT="${1}"
OUTPUT="${2}"
MODE=1
NUMBER=100
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
mkdir -p frames
ffmpeg -i "${INPUT}" frames/frame-%06d.png
FRAMES=frames/*.png
for frame in ${FRAMES}; do
echo "Processing $frame with primitive..."
# Run the "primitive" application on every frame
primitive -i "${frame}" -o "${frame}" -n 200 -m 4
done
ffmpeg -f image2 -i frames/frame-%06d.png -c:v prores_ks -profile:v 3 "${OUTPUT}"
rm -rf frames

42
im/title.sh Normal file
View File

@ -0,0 +1,42 @@
#!/bin/bash -e
#####################################################
#
# This script will create a 15 second title with a
# 5 second fade in and a 5 second fade out
#
# Usage: bash im/title.sh <title text> <output video>
#
#####################################################
TEXT="${1}"
OUTPUT="${2}"
# Check if output file extension is .mov
if [[ "${OUTPUT}" != *".mov" ]]; then
echo "Please use an .mov extension on your output file argument"
exit 1
fi
convert -size 1280x720 \
-background black \
-fill white \
-gravity Center \
-weight 700 \
-pointsize 200 \
label:"${TEXT}" \
title.png
ffmpeg -loop 1 \
-framerate 24 \
-f image2 \
-i title.png \
-s 1280x720 \
-vf "fade=t=in:st=0:d=5,fade=t=out:st=10:d=5" \
-c:v prores_ks \
-profile:v 3 \
-t 15 \
"${OUTPUT}"
# Clean up by removing the image
rm title.png

BIN
img/mac_terminal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

BIN
img/sonicseasonings.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
img/windows_10_admin.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

BIN
img/windows_10_enable.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
img/windows_10_install.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

BIN
img/windows_10_terminal.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 KiB

BIN
img/windows_10_ubuntu.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

BIN
img/windows_10_wsl.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

84
node/index.js Normal file
View File

@ -0,0 +1,84 @@
'use strict';
const PORT = typeof process.env.PORT !== 'undefined' ? parseInt(process.env.PORT) : 3000;
const express = require('express');
const bodyParser = require('body-parser');
const { execSync } = require('child_process');
const app = express();
const HOME = `<html>
<head><title>FFMPEG demo</title></head>
<body>
<form method="POST">
<input name="title" />
<input type="submit" />
</form>
</body>
</html>`;
const VIDEO = `<html>
<head><title>FFMPEG demo</title></head>
<body>
<video controls autoplay>
<source src="/title.mp4" type="video/mp4" />
</video>
</body>
</html>`
function generateTitle (text) {
const title = `convert \
-size 720x480 \
-background black \
-fill white \
-gravity Center \
-weight 700 \
-pointsize 100 \
label:"${text}" \
title.png`;
const ffmpeg = `ffmpeg -y \
-loop 1 \
-framerate 24 \
-f image2 \
-i title.png \
-s 720x480 \
-vf "fade=t=in:st=0:d=5,fade=t=out:st=10:d=5" \
-c:v libx264 \
-preset slow \
-crf 22 \
-f mp4 \
-strict -2 \
-pix_fmt yuv420p \
-t 15 \
-an \
title.mp4`;
const cleanup = `rm title.png`;
execSync(title);
execSync(ffmpeg);
execSync(cleanup);
}
app.use(express.static('./'));
app.use(bodyParser.urlencoded({ extended : true }));
app.get('/', (req, res, next) => {
res.end(HOME);
return next();
});
app.post('/', (req, res, next) => {
generateTitle(req.body.title);
res.end(VIDEO);
return next();
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
execSync('rm -f title.mp4');
});

860
node/package-lock.json generated Normal file
View File

@ -0,0 +1,860 @@
{
"name": "ffmpeg-demo",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ffmpeg-demo",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1"
}
},
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dependencies": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dependencies": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dependencies": {
"safe-buffer": "5.1.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.47.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
"dependencies": {
"mime-db": "1.47.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"node_modules/proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dependencies": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dependencies": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dependencies": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"engines": {
"node": ">= 0.8"
}
}
},
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.47.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw=="
},
"mime-types": {
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
"requires": {
"mime-db": "1.47.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

15
node/package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "ffmpeg-demo",
"version": "0.0.1",
"description": "Demo of ffmpeg used within node.js",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "mmcwilliams",
"license": "MIT",
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1"
}
}

111
stream/capture.sh Normal file
View File

@ -0,0 +1,111 @@
#!/bin/bash
#################################
#
# From https://gist.github.com/sixteenmillimeter/d6443c6b18a7d143b3695dd9d79c3c22
#
# Instructions
#
# First, install ffmpeg and youtube-dl
#
# https://ffmpeg.org/download.html
# https://ytdl-org.github.io/youtube-dl/download.html
#
# To capture a stream at https://yoururl and save to a dated, named TS file:
#
# bash capture.sh https://yoururl streamfilename
#
# Will create file streamfile_STARTDATE.ts and streamfilename.txt containing stream metadata.
#
# To capture stream and convert to an MKV file after:
#
# bash capture.sh https://streamurl streamfilename "convert"
#
# Will create TS file and streamfile_STARTDATE_to_FINISHDATE.mkv after capture complete.
#
# Note: Since this is capturing from a M3U8 stream you may end up retrieving more of earlier
# parts of the stream than the metadata will indicate. This will depend on the service
# you are capturing from.
#
#################################
THREADS="1" #set number of threads for process or comment out to utilize 100% of CPU
QUALITY=-1 #-2 for second best, -1 for best
INPUT="${1}"
OUTPUT="${2}" # filepath without extension
if [ "${OUTPUT}" == "" ]; then
OUTPUT=`basename "${INPUT}"`
fi
if [ "${THREADS}" == "" ]; then
THREADS_ARG=""
else
THREADS_ARG="-threads ${THREADS}"
fi
METADATA="${OUTPUT}.txt"
FORMATS=`mktemp`
# Show the available streams using youtube-dl
youtube-dl --list-formats "${INPUT}" > "${FORMATS}"
FORMAT_FULL=`tail ${QUALITY} "${FORMATS}" | head -1`
FORMAT=`echo "${FORMAT_FULL}" | awk -F" " '{print $1}'`
M3U8=`youtube-dl -f "${FORMAT}" -g "${INPUT}"`
STARTED=`date "+%F-%T-%Z"`
OUTPUT_TS="${OUTPUT}_${STARTED}.ts"
echo "Stream: ${INPUT}"
echo "Format: ${FORMAT_FULL}"
echo "Output: ${OUTPUT_TS}"
if [ -f "${METADATA}" ]; then
echo " " >> "${METADATA}"
else
echo "==========================" > "${METADATA}"
echo " " >> "${METADATA}"
fi
echo "Stream : ${INPUT}" >> "${METADATA}"
echo "M3U8 : ${M3U8}" >> "${METADATA}"
echo "Format : ${FORMAT_FULL}" >> "${METADATA}"
echo "Output : ${OUTPUT_TS}" >> "${METADATA}"
echo "Started : ${STARTED}" >> "${METADATA}"
echo "Starting capture, press 'q' to finish"
# capture with no console output
ffmpeg -i "${M3U8}" \
-loglevel warning \
-hide_banner ${THREADS_ARG} \
-c copy \
"${OUTPUT_TS}"
FINISHED=`date "+%F-%T-%Z"`
echo "Finished capturing at ${FINISHED}"
echo "Finished : ${FINISHED}" >> "${METADATA}"
if [ "${3}" == "convert" ]; then
OUTPUT_MKV="${OUTPUT}_${STARTED}_to_${FINISHED}.mkv"
echo "Creating MKV file ${OUTPUT_MKV}..."
# put video in an MKV wrapper as is (not really a conversion)
ffmpeg -i "${OUTPUT_TS}" \
-loglevel warning \
-hide_banner ${THREADS_ARG} \
-map 0 \
-c copy \
"${OUTPUT_MKV}"
echo "Created ${OUTPUT_MKV}"
echo "MKV : ${OUTPUT_MKV}" >> "${METADATA}"
fi
echo " " >> "${METADATA}"
echo "==========================" >> "${METADATA}"
echo "Cleaning up..."
# cleanup
rm -f "${FORMATS}"
echo "Completed capturing ${INPUT}"

20
text/all.txt Normal file
View File

@ -0,0 +1,20 @@
Welcome to Unix for Artists!
_______ __ __ __ _ _ _ ___ _______ __ __
| || | | || | | | | | _ | || | | || | | |
| ___|| | | || |_| | | || || || | |_ _|| |_| |
| |___ | |_| || | | || | | | | |
| ___|| || _ | | || | | | | |
| | | || | | | | _ || | | | | _ |
|___| |_______||_| |__| |__| |__||___| |___| |__| |__|
________ ________ __ __ _______ ________ ______
| \| \| \ / \| \ | \ / \
| $$$$$$$$| $$$$$$$$| $$\ / $$| $$$$$$$\| $$$$$$$$| $$$$$$\
| $$__ | $$__ | $$$\ / $$$| $$__/ $$| $$__ | $$ __\$$
| $$ \ | $$ \ | $$$$\ $$$$| $$ $$| $$ \ | $$| \
| $$$$$ | $$$$$ | $$\$$ $$ $$| $$$$$$$ | $$$$$ | $$ \$$$$
| $$ | $$ | $$ \$$$| $$| $$ | $$_____ | $$__| $$
| $$ | $$ | $$ \$ | $$| $$ | $$ \ \$$ $$
\$$ \$$ \$$ \$$ \$$ \$$$$$$$$ \$$$$$$

1
text/cat1.txt Normal file
View File

@ -0,0 +1 @@
Welcome to Unix for Artists!

7
text/cat2.txt Normal file
View File

@ -0,0 +1,7 @@
_______ __ __ __ _ _ _ ___ _______ __ __
| || | | || | | | | | _ | || | | || | | |
| ___|| | | || |_| | | || || || | |_ _|| |_| |
| |___ | |_| || | | || | | | | |
| ___|| || _ | | || | | | | |
| | | || | | | | _ || | | | | _ |
|___| |_______||_| |__| |__| |__||___| |___| |__| |__|

9
text/cat3.txt Normal file
View File

@ -0,0 +1,9 @@
________ ________ __ __ _______ ________ ______
| \| \| \ / \| \ | \ / \
| $$$$$$$$| $$$$$$$$| $$\ / $$| $$$$$$$\| $$$$$$$$| $$$$$$\
| $$__ | $$__ | $$$\ / $$$| $$__/ $$| $$__ | $$ __\$$
| $$ \ | $$ \ | $$$$\ $$$$| $$ $$| $$ \ | $$| \
| $$$$$ | $$$$$ | $$\$$ $$ $$| $$$$$$$ | $$$$$ | $$ \$$$$
| $$ | $$ | $$ \$$$| $$| $$ | $$_____ | $$__| $$
| $$ | $$ | $$ \$ | $$| $$ | $$ \ \$$ $$
\$$ \$$ \$$ \$$ \$$ \$$$$$$$$ \$$$$$$