(information gathering)
(resources)
 
(15 intermediate revisions by the same user not shown)
Line 25: Line 25:
 
=== information gathering ===
 
=== information gathering ===
  
to retrieve the file information, i used '''ffprobe''', it dumps a lot of info
+
to retrieve the video information, i used '''ffprobe''', it dumps a lot of info
  
 
  Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'carnages_creamy_patterns.mov':
 
  Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'carnages_creamy_patterns.mov':
Line 44: Line 44:
  
 
=== scripting ===
 
=== scripting ===
 +
 
here comes the interesting part!  
 
here comes the interesting part!  
  
the process requires a minimum of text editing: just create a '''media_*.py''' file containing the required vars (with correct types)
+
the process relies on 3 parts:
 +
 
 +
* a data file, containing the media information - '''media_*.py'''
 +
* an animation script, creating the animation in terminal - '''credits_generator.py'''
 +
* and a bash file that controls the whole recording process via ffmpeg - '''credits.sh'''
 +
 
 +
They are detailed here below.
  
 
==== media info ====
 
==== media info ====
 +
 +
the process requires a minimum of text editing: just create a '''media_[name].py''' file containing the required vars (with correct types)
  
 
example of media info file
 
example of media info file
 +
 +
'''WARNING''': mediawiki removes the "'" chars, go to source to copy/paste! (append '''&action=edit''' to current url)
  
 
  from styler import *
 
  from styler import *
Line 76: Line 87:
  
 
the imported script comes from [https://github.com/frankiezafe/TerminalStyler TerminalStyler]
 
the imported script comes from [https://github.com/frankiezafe/TerminalStyler TerminalStyler]
 +
 +
==== animation ====
 +
 +
the animation is done via another python script, '''credits_generator.py''':
 +
 +
import os
 +
import sys
 +
import time
 +
from styler import *
 +
 +
mediainfo = __import__( str( sys.argv[1] )[:-3], fromlist=["*"] )
 +
 +
############# config #############
 +
margin = (4,10)
 +
logo = '''
 +
                                         
 +
  __  __,  ,_  _  __,  __  _  , 
 +
_(_,_(_/(__/ (__/ (_(_/(__(_/__(/__/_)_
 +
                          _/_         
 +
                          (/           
 +
                                         
 +
'''
 +
ret = style("_n")
 +
t_mtop = style("_n" + str( margin[0] ) )
 +
t_mlef = style("_s" + str( margin[1] ) )
 +
logo_margined_y = style("_n" + str( margin[0] ) + "|cyan" ) + margin_left( logo, margin[1] ) + style("")
 +
logo_margined_w = style("_n" + str( margin[0] ) ) + margin_left( logo, margin[1] ) + style("")
 +
credits_margined = margin_left( mediainfo.t_credits, margin[1] )
 +
stream_margined = margin_left( mediainfo.t_stream, margin[1] )
 +
input_margined = margin_left( mediainfo.t_input, margin[1] )
 +
 +
############# frames #############
 +
 +
frames = []
 +
frames.append( [t_mtop, 4] )
 +
for i in range( 0, 5 ):
 +
if i % 2 == 0:
 +
frames.append( [logo_margined_w, 0.1] )
 +
else:
 +
frames.append( [logo_margined_y, 0.15] )
 +
frames.append( [style("_n" + str( margin[0] + 8 )), 0.1] )
 +
frames.append( [logo_margined_y, 0.5] )
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret + t_mlef,
 +
1] )
 +
 +
authinfo = ret + t_mlef + mediainfo.t_date + ret + t_mlef + mediainfo.t_size + ret + t_mlef + mediainfo.t_duration + ret + t_mlef + mediainfo.t_author
 +
for i in range( linecount(stream_margined) ):
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret +
 +
ret + lines( authinfo, 0, i ),
 +
0.05] )
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret +
 +
ret + authinfo + ret,
 +
5] )
 +
 +
techinfo = stream_margined + ret + input_margined
 +
for i in range( linecount(stream_margined) ):
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret +
 +
ret + lines( techinfo, 0, i ),
 +
0.05] )
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret +
 +
ret + techinfo,
 +
3] )
 +
frames.append( [
 +
logo_margined_y +
 +
ret + t_mlef + mediainfo.filename +
 +
ret +
 +
ret + techinfo +
 +
ret + credits_margined,
 +
2] )
 +
 +
############# animation #############
 +
 +
for i in frames:
 +
os.system('clear')
 +
print( i[0] )
 +
sys.stdout.write( t_mlef )
 +
sys.stdout.flush()
 +
time.sleep( i[1] )
 +
 +
 +
* __import__( str( sys.argv[1] )[:-3], fromlist=["*"] ) is super useful, as it allows to pass the media_[name].py path via an argument, without changing anything to this script
 +
* '''margin''' represents the top and left margin of the text, expressed in returns and tabs
 +
* '''frames''' is a list of the animation frame, with text at position 0 and duration at 1
 +
* functions '''linecount''' and '''lines''' (from styler.py) allows to make the text animations easier: '''lines''' function truncates the block of text by lines (from, to)
 +
 +
Once all frames are packed, we just have to loop over them and print the text. Lines ''sys.stdout'' allows to offset the cursor by the left margin.
 +
 +
To test, just launch the script in terminal,
 +
 +
python credits_generator.py media_[name].py
 +
 +
==== recording ====
 +
 +
once everything's ready on python level, we still have to record the execution of the script and save it as a video
 +
 +
bash is there to help:
 +
 +
#!/bin/bash
 +
 +
#open gnome-terminal in fullscreen, with menu bar enabled
 +
 +
if [ -f "tmp.mov" ]
 +
then
 +
    rm tmp.mov
 +
fi
 +
if [ -f "credits.mkv" ]
 +
then
 +
    rm credits.mkv
 +
fi
 +
ffmpeg -loglevel quiet -nostats -video_size 1920x1080 -framerate 30 -f x11grab -i :0.0 -q:v 1 -vcodec qtrle tmp.mov > /dev/null &
 +
 +
python credits_generator.py $1
 +
 +
killall ffmpeg
 +
 +
sleep 1
 +
 +
ffmpeg -ss 00:00:01 -i tmp.mov -video_size 1920x1080 -vcodec mjpeg -q:v 0 -vf crop=740:700:0:25,pad=1920:1080:iw-740:ih-700 credits.mkv
 +
 +
first thing to do is to remove old video files: as we launch ffmpeg recording in silent mode, we will not be able to confirm overwrite!
 +
 +
second line is just a standard line to grab screen
 +
 +
the last ffmpeg command is a bit special: the 1 second removal is due to glitches appearing when ffmpeg synchronises with display, and the cropping and padding removes the menu bar of the fullscreen terminal
 +
 +
To run the script:
 +
 +
credits.sh media_[name].py
 +
 +
The full process outputs a credits.mkv in full HD, ready to be used!
  
 
== resources ==
 
== resources ==
Line 84: Line 240:
 
* [https://github.com/blueset/figlet-fonts figlet-fonts] - collection of ascii art fonts for figlet or toilet
 
* [https://github.com/blueset/figlet-fonts figlet-fonts] - collection of ascii art fonts for figlet or toilet
 
* [https://asciinema.org/ asciinema] - record and share your terminal sessions, the right way.
 
* [https://asciinema.org/ asciinema] - record and share your terminal sessions, the right way.
 +
 +
[[category:programmation]]
 +
[[category:generative]]
 +
[[category:porn]]
 +
[[category:carnages]]
 +
[[category:python]]
 +
[[category:bash]]
 +
[[category:video]]

Latest revision as of 20:40, 20 October 2017

Result of the scripts

watch this at full resolution (full HD), or you will not see anything :)

how does it works

notes for me and whoever might find this interesting

the logo is using the 'ascii' font JS Cursive, slightly modified on the 'n'

  __  __,   ,_   _   __,   __   _   ,  
_(_,_(_/(__/ (__/ (_(_/(__(_/__(/__/_)_
                          _/_          
                         (/

that's a nice way to display other fonts in the terminal; see resources for links

information gathering

to retrieve the video information, i used ffprobe, it dumps a lot of info

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'carnages_creamy_patterns.mov':
 Metadata:
   major_brand     : qt  
   minor_version   : 512
   compatible_brands: qt  
   encoder         : Lavf56.40.101
 Duration: 01:39:01.03, start: 0.000000, bitrate: 200411 kb/s
   Stream #0:0(eng): Video: qtrle (rle  / 0x20656C72), rgb24, 1920x1080, 200408 kb/s, 30 fps, 30 tbr, 15360 tbn, 15360 tbc (default)
   Metadata:
     handler_name    : DataHandler
     encoder         : Lavc56.60.100 qtrle

for the credits, i just used the Stream #0:0, reformated as a tabbed list

other info comes from the file browser (nemo in my case)

scripting

here comes the interesting part!

the process relies on 3 parts:

  • a data file, containing the media information - media_*.py
  • an animation script, creating the animation in terminal - credits_generator.py
  • and a bash file that controls the whole recording process via ffmpeg - credits.sh

They are detailed here below.

media info

the process requires a minimum of text editing: just create a media_[name].py file containing the required vars (with correct types)

example of media info file

WARNING: mediawiki removes the "'" chars, go to source to copy/paste! (append &action=edit to current url)

from styler import *
filename = style("b|yellow") + "carnages_teeny_black.mkv [#6]" + style("")
t_date= 		"date: " + style("b") + "20171019" + style("")
t_size= 		"size: " + style("b") + "2.507.349.390 bytes" + style("")
t_duration= 	"duration: " + style("b") + "00:08:16.8" + style("")
t_author = 		"author:" + style("b") + "françois zajéga" + style("")
t_credits = title font: JS Cursive By Joan Stark
t_stream = stream #0:0(eng): video: 
   mjpeg
   yuvj444p(pc, bt470bg/unknown/unknown)
   1920x1080
   SAR 1:1 DAR 16:9
   25 fps
   25 tbr
   1k tbn 
   1k tbc (default)
encoder: Lavc56.60.100 libx264
t_input= source:
   width: 1024
   height: 544

content of the video here above

the imported script comes from TerminalStyler

animation

the animation is done via another python script, credits_generator.py:

import os
import sys
import time
from styler import *

mediainfo = __import__( str( sys.argv[1] )[:-3], fromlist=["*"] )

############# config #############
margin = (4,10)
logo = 
                                         
  __  __,   ,_   _   __,   __   _   ,  
_(_,_(_/(__/ (__/ (_(_/(__(_/__(/__/_)_
                          _/_          
                         (/            
                                         

ret = style("_n")
t_mtop = style("_n" + str( margin[0] ) )
t_mlef = style("_s" + str( margin[1] ) )
logo_margined_y = style("_n" + str( margin[0] ) + "|cyan" ) + margin_left( logo, margin[1] ) + style("")
logo_margined_w = style("_n" + str( margin[0] ) ) + margin_left( logo, margin[1] ) + style("")
credits_margined = margin_left( mediainfo.t_credits, margin[1] )
stream_margined = margin_left( mediainfo.t_stream, margin[1] )
input_margined = margin_left( mediainfo.t_input, margin[1] )

############# frames #############

frames = []
frames.append( [t_mtop, 4] )
for i in range( 0, 5 ):
	if i % 2 == 0:
		frames.append( [logo_margined_w, 0.1] )
	else:
		frames.append( [logo_margined_y, 0.15] )
	frames.append( [style("_n" + str( margin[0] + 8 )), 0.1] )
frames.append( [logo_margined_y, 0.5] )
frames.append( [
	logo_margined_y + 
	ret + t_mlef + mediainfo.filename + 
	ret + t_mlef, 
	1] )

authinfo = ret + t_mlef + mediainfo.t_date + ret + t_mlef + mediainfo.t_size + ret + t_mlef + mediainfo.t_duration + ret + t_mlef + mediainfo.t_author
for i in range( linecount(stream_margined) ):
	frames.append( [
		logo_margined_y + 
		ret + t_mlef + mediainfo.filename +
		ret +
		ret + lines( authinfo, 0, i ), 
		0.05] )
frames.append( [
	logo_margined_y + 
	ret + t_mlef + mediainfo.filename +
	ret +
	ret + authinfo + ret, 
	5] )

techinfo = stream_margined + ret + input_margined
for i in range( linecount(stream_margined) ):
	frames.append( [
		logo_margined_y + 
		ret + t_mlef + mediainfo.filename +
		ret +
		ret + lines( techinfo, 0, i ), 
		0.05] )
frames.append( [
		logo_margined_y + 
		ret + t_mlef + mediainfo.filename +
		ret +
		ret + techinfo, 
		3] )
frames.append( [
		logo_margined_y + 
		ret + t_mlef + mediainfo.filename +
		ret +
		ret + techinfo +
		ret + credits_margined, 
		2] )

############# animation #############

for i in frames:
	os.system('clear')
	print( i[0] )
	sys.stdout.write( t_mlef )
	sys.stdout.flush()
	time.sleep( i[1] )


  • __import__( str( sys.argv[1] )[:-3], fromlist=["*"] ) is super useful, as it allows to pass the media_[name].py path via an argument, without changing anything to this script
  • margin represents the top and left margin of the text, expressed in returns and tabs
  • frames is a list of the animation frame, with text at position 0 and duration at 1
  • functions linecount and lines (from styler.py) allows to make the text animations easier: lines function truncates the block of text by lines (from, to)

Once all frames are packed, we just have to loop over them and print the text. Lines sys.stdout allows to offset the cursor by the left margin.

To test, just launch the script in terminal,

python credits_generator.py media_[name].py

recording

once everything's ready on python level, we still have to record the execution of the script and save it as a video

bash is there to help:

#!/bin/bash

#open gnome-terminal in fullscreen, with menu bar enabled

if [ -f "tmp.mov" ]
then
    rm tmp.mov 
fi
if [ -f "credits.mkv" ]
then
    rm credits.mkv
fi
ffmpeg -loglevel quiet -nostats -video_size 1920x1080 -framerate 30 -f x11grab -i :0.0 -q:v 1 -vcodec qtrle tmp.mov > /dev/null &

python credits_generator.py $1

killall ffmpeg 

sleep 1

ffmpeg -ss 00:00:01 -i tmp.mov -video_size 1920x1080 -vcodec mjpeg -q:v 0 -vf crop=740:700:0:25,pad=1920:1080:iw-740:ih-700 credits.mkv

first thing to do is to remove old video files: as we launch ffmpeg recording in silent mode, we will not be able to confirm overwrite!

second line is just a standard line to grab screen

the last ffmpeg command is a bit special: the 1 second removal is due to glitches appearing when ffmpeg synchronises with display, and the cropping and padding removes the menu bar of the fullscreen terminal

To run the script:

credits.sh media_[name].py

The full process outputs a credits.mkv in full HD, ready to be used!

resources

  • TerminalStyler - script to style the console output
  • taag - online interface to use 'ascii' fonts
  • FIGlet - a program for making large letters out of ordinary text
  • figlet-fonts - collection of ascii art fonts for figlet or toilet
  • asciinema - record and share your terminal sessions, the right way.

online identity ∋ [ social ∋ [mastodon♥, twitter®, facebook®, diaspora, linkedin®] ∥ repos ∋ [github®, gitlab♥, bitbucket®, sourceforge] ∥ media ∋ [itch.io®, vimeo®, peertube♥, twitch.tv®, tumblr®] ∥ communities ∋ [godotengine♥, openprocessing, stackoverflow, threejs]]