Generate a preview video of a larger video

Original Article:
davidwalsh.name/video-preview

Displaying a preview image before loading a video is common practice with web media these days; oftentimes that image preview is the video’s first frame or an important frame from the video.  If you’ve visited an adult site recently (ahem), you will have noticed that not only do you get a preview image but hovering over said image shows a small sample video with snippets at different points of the movie.  Naturally I was interested in how all of this worked and concluded the following:

  • They create the preview image using a tool like ffmpeg
  • Upon hover of image, the preview video is loaded asynchronously in the background
  • Once the preview image has loaded, the video is displayed over the image and autoplayed
  • When the mouse leaves, the video is hidden (in case the user hovers over the image again)

Achieving the <image> and <video> element swap is simple but I wanted to know how PornHub, the largest adult site network, generated their preview videos.  The idea behind the preview video is simple, of course, but I wanted to teach myself more about ffmpeg and bash scripting.  Here’s the proof:

Video preview generated from a local download of Kung Fury.

Let’s deconstruct my code to learn how this (very basic) preview video can be created!

Running the Script

Our script, preview.sh, will be called as follows:

# ./preview.sh {input.format} {output.format}
$ ./preview.sh kung-fury.mp4 kung-fury-preview.mp4

In a perfect world the script would accept loads more arguments, like desired video size or any of the settings we’ll assume within the script, but I wanted to focus on getting the meat of the script working; adding command line arguments would be a routine exercise when everything else was a go.

The Script: preview.sh

The script starts with some very basic validation:

sourcefile=$1
destfile=$2

# Overly simple validation
if [ ! -e "$sourcefile" ]; then
  echo 'Please provide an existing input file.'
  exit
fi

if [ "$destfile" == "" ]; then
  echo 'Please provide an output preview file name.'
  exit
fi

Next we have a series of variables relating to video length, desired points in the video to cut previews, the video size, etc.:

# Detect destination file extension
extension=${destfile#*.}

# Get video length in seconds
length=$(ffprobe $sourcefile  -show_format 2>&1 | sed -n 's/duration=//p' | awk '{print int($0)}')

# Start 20 seconds into the video to avoid opening credits (arbitrary)
starttimeseconds=20

# Mini-snippets will be 2 seconds in length
snippetlengthinseconds=2

# We'll aim for 5 snippets spread throughout the video
desiredsnippets=5

# Ensure the video is long enough to even bother previewing
minlength=$(($snippetlengthinseconds*$desiredsnippets))

# Video dimensions (these could probably be command line arguments)
dimensions=640:-1

# Temporary directory and text file where we'll store snippets
# These will be cleaned up and removed when the preview image is generated
tempdir=snippets
listfile=list.txt

If the video is too short to generate a preview from, simply bail:

# Display and check video length
echo 'Video length: ' $length
if [ "$length" -lt "$minlength" ]
then
  echo 'Video is too short.  Exiting.'
  exit
fi

Next we generate a series of “snippet” videos at calculated times within the video, saving these snippet videos to a temporary directory:

# Loop and generate video snippets
mkdir $tempdir
interval=$(($length/$desiredsnippets-$starttimeseconds))
for i in $(seq 1 $desiredsnippets)
  do
    # Format the second marks into hh:mm:ss format
    start=$(($(($i*$interval))+$starttimeseconds))
    formattedstart=$(printf "%02d:%02d:%02d\n" $(($start/3600)) $(($start%3600/60)) $(($start%60)))
    echo 'Generating preview part ' $i $formattedstart
    # Generate the snippet at calculated time
    ffmpeg -i $sourcefile -vf scale=$dimensions -preset fast -qmin 1 -qmax 1 -ss $formattedstart -t $snippetlengthinseconds $tempdir/$i.$extension &>/dev/null
done

Note: copying and slicing videos with ffmpeg is difficult if you aren’t a codec and media expert, which I certainly am not.  These basic settings do allow the job to get done but maybe not in the most optimal way.  Please comment below if you know a way to improve performance or video quality.

The last step is concatenating the snippet videos in to the final preview video:

# Concat videos
echo 'Generating final preview file'

# Generate a text file with one snippet video location per line
# (https://trac.ffmpeg.org/wiki/Concatenate)
for f in $tempdir/*; do echo "file '$f'" >> $listfile; done

# Concatenate the files based on the generated list
ffmpeg -f concat -safe 0 -i $listfile -c copy $destfile &>/dev/null

echo 'Done!  Check ' $destfile '!'

# Cleanup
rm -rf $tempdir $listfile

Video concatenation is easy in this case because we’re concatenating snippets from the same original video.  Concatenation would be much more difficult if we were using videos of different frame rates, sizes, etc.

The final script in all its glory:

sourcefile=$1
destfile=$2

# Overly simple validation
if [ ! -e "$sourcefile" ]; then
  echo 'Please provide an existing input file.'
  exit
fi

if [ "$destfile" == "" ]; then
  echo 'Please provide an output preview file name.'
  exit
fi

# Detect destination file extension
extension=${destfile#*.}

# Get video length in seconds
length=$(ffprobe $sourcefile  -show_format 2>&1 | sed -n 's/duration=//p' | awk '{print int($0)}')

# Start 20 seconds into the video to avoid opening credits (arbitrary)
starttimeseconds=20

# Mini-snippets will be 2 seconds in length
snippetlengthinseconds=2

# We'll aim for 5 snippets spread throughout the video
desiredsnippets=5

# Ensure the video is long enough to even bother previewing
minlength=$(($snippetlengthinseconds*$desiredsnippets))

# Video dimensions (these could probably be command line arguments)
dimensions=640:-1

# Temporary directory and text file where we'll store snippets
# These will be cleaned up and removed when the preview image is generated
tempdir=snippets
listfile=list.txt

# Display and check video length
echo 'Video length: ' $length
if [ "$length" -lt "$minlength" ]
then
  echo 'Video is too short.  Exiting.'
  exit
fi

# Loop and generate video snippets
mkdir $tempdir
interval=$(($length/$desiredsnippets-$starttimeseconds))
for i in $(seq 1 $desiredsnippets)
  do
    # Format the second marks into hh:mm:ss format
    start=$(($(($i*$interval))+$starttimeseconds))
    formattedstart=$(printf "%02d:%02d:%02d\n" $(($start/3600)) $(($start%3600/60)) $(($start%60)))
    echo 'Generating preview part ' $i $formattedstart
    # Generate the snippet at calculated time
    ffmpeg -i $sourcefile -vf scale=$dimensions -preset fast -qmin 1 -qmax 1 -ss $formattedstart -t $snippetlengthinseconds $tempdir/$i.$extension &>/dev/null
done

# Concat videos
echo 'Generating final preview file'

# Generate a text file with one snippet video location per line
# (https://trac.ffmpeg.org/wiki/Concatenate)
for f in $tempdir/*; do echo "file '$f'" >> $listfile; done

# Concatenate the files based on the generated list
ffmpeg -f concat -safe 0 -i $listfile -c copy $destfile &>/dev/null

echo 'Done!  Check ' $destfile '!'

# Cleanup
rm -rf $tempdir $listfile

Karabiner Catalina not working

Update: 23 February, 2021

A new method is available – check below.

  • At Settings -> Privacy -> Input Monitoring
  • Add /Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber to the list
  • Restart the keygrabber using the terminal
  • sudo killall karabiner_grabber

Workaround: add /Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber to the list.

Oh, and sudo killall karabiner_grabber

https://github.com/pqrs-org/Karabiner-Elements/issues/1867 – however, this solution doesn’t work anymore 😦

Update: 23 February, 2021

Install an older version for Catalina 10.15.7, i.e. version Karabiner v12.10.0

This is where you can download, https://github.com/pqrs-org/Karabiner-Elements/releases/tag/v12.10.0

Taken from: https://github.com/pqrs-org/Karabiner-Elements/issues/2473

Stop Mac from suggesting applications outside boot drive

8

I have two hard drives on my machine (Catalina). One contains the regular copy of Catalina that I use everyday, the one has a slightly older copy of the Sierra that I keep as a backup (just in case).

Whenever I right-click on a file and go to “Open With”, it starts suggesting applications from Sierra.

https://superuser.com/questions/348930/how-to-prevent-applications-on-an-external-hard-drive-from-showing-up-in-the-op

/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -r -all local,system,user

Hit Enter on TextBox & goto next TextBox

Very helpful if you are using a barcode reader, or you want to trigger something when someone presses Enter.

Maybe hop on to the next text-box? I need to figure out how to do that bit- move to next on Return (just like they do with TAB).

Type some text into the TextBox and press the Enter key.

The Code Behind on C#

private void OnKeyDownHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
textBlock1.Text = "You Entered: " + textBox1.Text;
}
}

All the above generously borrowed from:

http://msdn.microsoft.com/en-us/library/ms752054.aspx

Winamp 2019

I still use Winamp (v. 5.622) in what is 2 months from the year 2020.

******* Keyboard Shortcuts (these can be used in most Winamp windows) *******

Key Action
—————————————————————————–
F1 Open Help
Ctrl+F1 About Box
Ctrl+A Toggle Always on Top (N/A in playlist editor and media library)
Ctrl+Alt+A Toggle always on top (playlist editor)
Ctrl+W Toggle Windowshade mode (main window, unless in playlist editor)
Ctrl+D Toggle Doublesize Mode
Ctrl+E Toggle Easymove (only applicable in classic skins)
Ctrl+T Toggle Time Display Mode
Alt+W Toggle Main Window
Alt+E Toggle Playlist Editor
Alt+G Toggle Graphical Equalizer
Alt+V Toggle Video Window
Alt+L Toggle Media Library
Ctrl+Tab Cycle through different Winamp windows
Alt+S Go to Skin selection
Ctrl+P Go to Preferences
Alt+F Open Main Menu
Alt+K Configure current visualization plug-in
Ctrl+Sh+K Start/stop current visualization plug-In
Ctrl+K Open visualization plug-in section of preferences
Ctrl+J Jump to time in current track
J or Keypad . Open jump-to-file box
Ctrl+Alt+N Spawn new Winamp instance
Ctrl+Alt+B Add current to bookmarks (requires ML and ml_bookmarks)
Alt+M Minimize Winamp
Ctrl+H Show recently played files/streams (History 🙂

******* Main Window Keyboard Shortcuts *******

Key Action
—————————————————————————–
(options/toggles)
R Toggle Repeat
S Toggle Shuffle

Alt+3 Current file info box/tag editor

(playback controls)
Z Previous Track
X Play/Restart/Unpause
C Pause/Unpause
V Stop
Shift+V Stop with Fadeout
Ctrl+V Stop after current track
B Next Track
L Open/Play File
Ctrl+L Open/Play location
Shift+L Open/Play Directory

Left Arrow Rewind 5 seconds
Right Arrow Fast-forward 5 seconds
Up Arrow Turn Volume Up
Down Arrow Turn Volume Down

Keypad 1 Jump Ten Songs Back
Keypad 6 Next Track
Keypad 5 Play/Restart/Unpause
Keypad 4 Previous Track
Keypad 3 Jump Ten Songs Forward
Keypad 7 Rewind 5 seconds
Keypad 9 Fast-forward 5 seconds
Keypad 8 Turn Volume Up
Keypad 2 Turn Volume Down
Keypad 0 Open/Play File
Ctrl+Keypad 0 Open/Play location
Insert Open/Play Directory

******* Playlist Window Keyboard Shortcuts *******

Key Action
—————————————————————————–
R Toggle Repeat
S Toggle Shuffle
Ctrl+Z Go to start of list
Ctrl+B Go to end of list
Alt+I Add current to bookmarks (requires ML and ml_bookmarks)

(file io)
L Add File
Ctrl+L Add Location
Shift+L Add Directory
Ctrl+N New (Clear) Playlist
Ctrl+O Open (Load) Playlist
Ctrl+S Save Playlist
Alt+3 View/Edit Track Info for selected track(s)
Ctrl+E Edit Selected Track Filename(s)
Ctrl+Keypad 0 Add Location
Insert Add Directory

(playlist manipulation)
Ctrl+A Select All
Ctrl+I Invert Selection
Delete Remove Selected Files from Playlist
Ctrl+Delete Crop Playlist
Ctrl+Sh+Del Clear Playlist (same as Ctrl+N)

Alt+Down Arrow Move Selected Files Down
Alt+Up Arrow Move Selected Files Up

Down Arrow Move Cursor Down
Up Arrow Move Cursor Up
Enter Play Selected File
End Jump to End of List
Home Jump to Start of List
Page Up Move up by a fifth of a page
Page Down Move down by a fifth of a page

Alt+Delete Remove missing files from playlist

(playlist sorting)
Ctrl+Sh+1 Sort Playlist by Title
Ctrl+Sh+2 Sort Playlist by File Name
Ctrl+Sh+3 Sort Playlist by File Path and Name
Ctrl+R Reverse Playlist
Ctrl+Sh+R Randomize Playlist

Most main window playback controls also work in the playlist editor.

******* Equalizer Keyboard Shortcuts (Classic skins only) *******

Key Action
—————————————————————————–
1 – 0 Increase EQ bands 1-10
Q – P Decrease EQ bands 1-10
` Increase EQ Preamp
TAB Decrease EQ Preamp
N Toggle EQ Enabled
A Toggle EQ Auto-Loading
S Open Presets Menu
Ctrl+Alt+S Load Preset
P

PHPStorm Activator Killed 9

Step 1: Install Homebrew

https://www.howtogeek.com/211541/homebrew-for-os-x-easily-installs-desktop-apps-and-terminal-utilities/

xcode-select --install
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Check installation with:

brew doctor

Step 2: Running 386 code instead of amd64

You need “upx” so,

brew install upx

using upx, do

upx -d IntelliJIDEALicenseServer_darwin_386

But that doesn’t work.

Rich Text Editor with HTML Support

https://quilljs.com/docs/quickstart/

Quill instantiates with a div. Need to get that div’s inner HTML, save it to an invisible textbox and trigger form submit.


$("#methodForm").submit(function(e){
e.preventDefault();
var form = this;
checkIndex('upload/segments.gen').done(function() {
form.submit(); // submit bypassing the jQuery bound event
}).fail(function () {
alert("No index present!");
});
});


function quillGetHTML(inputDelta) {
var tempCont = document.createElement("div");
(new Quill(tempCont)).setContents(inputDelta);
return tempCont.getElementsByClassName("ql-editor")[0].innerHTML;
}

Try using htmlspecialchars() on the string to put into the DB, and then, when pulling it back out, use htmlspecialchars_decode(). Might make a difference.