snapgit 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.editorconfig +17 -0
- data/.gitattributes +1 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +57 -0
- data/.travis.yml +42 -0
- data/Gemfile +4 -0
- data/LICENSE +165 -0
- data/README.md +28 -0
- data/Rakefile +79 -0
- data/bin/lolcommits +335 -0
- data/bin/snapgit +14 -0
- data/config/cucumber.yml +2 -0
- data/features/bugs.feature +63 -0
- data/features/lolcommits.feature +286 -0
- data/features/plugins.feature +20 -0
- data/features/step_definitions/lolcommits_steps.rb +142 -0
- data/features/support/env.rb +61 -0
- data/features/support/path_helpers.rb +39 -0
- data/lib/core_ext/class.rb +8 -0
- data/lib/core_ext/mini_magick/utilities.rb +15 -0
- data/lib/lolcommits.rb +34 -0
- data/lib/lolcommits/capturer.rb +24 -0
- data/lib/lolcommits/capturer/capture_cygwin.rb +22 -0
- data/lib/lolcommits/capturer/capture_fake.rb +9 -0
- data/lib/lolcommits/capturer/capture_linux.rb +45 -0
- data/lib/lolcommits/capturer/capture_linux_animated.rb +72 -0
- data/lib/lolcommits/capturer/capture_mac.rb +23 -0
- data/lib/lolcommits/capturer/capture_mac_animated.rb +73 -0
- data/lib/lolcommits/capturer/capture_windows.rb +22 -0
- data/lib/lolcommits/cli/fatals.rb +77 -0
- data/lib/lolcommits/cli/launcher.rb +29 -0
- data/lib/lolcommits/cli/process_runner.rb +48 -0
- data/lib/lolcommits/cli/timelapse_gif.rb +45 -0
- data/lib/lolcommits/configuration.rb +138 -0
- data/lib/lolcommits/git_info.rb +82 -0
- data/lib/lolcommits/init.rb +121 -0
- data/lib/lolcommits/installation.rb +128 -0
- data/lib/lolcommits/platform.rb +127 -0
- data/lib/lolcommits/plugin.rb +130 -0
- data/lib/lolcommits/plugins/dot_com.rb +50 -0
- data/lib/lolcommits/plugins/lol_protonet.rb +68 -0
- data/lib/lolcommits/plugins/lol_slack.rb +68 -0
- data/lib/lolcommits/plugins/lol_tumblr.rb +127 -0
- data/lib/lolcommits/plugins/lol_twitter.rb +155 -0
- data/lib/lolcommits/plugins/lol_yammer.rb +85 -0
- data/lib/lolcommits/plugins/lolsrv.rb +59 -0
- data/lib/lolcommits/plugins/loltext.rb +147 -0
- data/lib/lolcommits/plugins/snapgit.rb +167 -0
- data/lib/lolcommits/plugins/tranzlate.rb +115 -0
- data/lib/lolcommits/plugins/uploldz.rb +65 -0
- data/lib/lolcommits/runner.rb +134 -0
- data/lib/lolcommits/version.rb +5 -0
- data/snapgit.gemspec +63 -0
- data/test/images/test_image.jpg +0 -0
- data/test/lolcommits_test.rb +35 -0
- data/test/plugins_test.rb +52 -0
- data/vendor/ext/CommandCam/COPYING +674 -0
- data/vendor/ext/CommandCam/CommandCam.exe +0 -0
- data/vendor/ext/CommandCam/LICENSE +16 -0
- data/vendor/ext/imagesnap/ReadMeOrDont.rtf +117 -0
- data/vendor/ext/imagesnap/imagesnap +0 -0
- data/vendor/ext/videosnap/videosnap +0 -0
- data/vendor/fonts/Lato.ttf +0 -0
- data/vendor/logos/Snapgit.png +0 -0
- metadata +509 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'aruba/cucumber'
|
|
3
|
+
require 'methadone/cucumber'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'ffaker'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
|
|
8
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'path_helpers')
|
|
9
|
+
include Lolcommits
|
|
10
|
+
|
|
11
|
+
World(PathHelpers)
|
|
12
|
+
|
|
13
|
+
Before do
|
|
14
|
+
# Using "announce" causes massive warnings on 1.9.2
|
|
15
|
+
@puts = true
|
|
16
|
+
@aruba_timeout_seconds = 20
|
|
17
|
+
|
|
18
|
+
# prevent launchy from opening gifs in tests
|
|
19
|
+
set_env 'LAUNCHY_DRY_RUN', 'true'
|
|
20
|
+
set_env 'LOLCOMMITS_CAPTURER', 'CaptureFake'
|
|
21
|
+
|
|
22
|
+
author_name = 'Testy McTesterson'
|
|
23
|
+
author_email = 'testy@tester.com'
|
|
24
|
+
|
|
25
|
+
set_env 'GIT_AUTHOR_NAME', author_name
|
|
26
|
+
set_env 'GIT_COMMITTER_NAME', author_name
|
|
27
|
+
set_env 'GIT_AUTHOR_EMAIL', author_email
|
|
28
|
+
set_env 'GIT_COMMITTER_EMAIL', author_email
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# for tasks that may take an insanely long time (e.g. network related)
|
|
32
|
+
# we should strive to not have any of these in our scenarios, naturally.
|
|
33
|
+
Before('@slow_process') do
|
|
34
|
+
@aruba_io_wait_seconds = 5
|
|
35
|
+
@aruba_timeout_seconds = 60
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# in order to fake an interactive rebase, we replace the editor with a script
|
|
39
|
+
# to simply squash a few random commits. in this case, using lines 3-5.
|
|
40
|
+
Before('@fake-interactive-rebase') do
|
|
41
|
+
set_env 'GIT_EDITOR', "sed -i -e '3,5 s/pick/squash/g'"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# adjust the path so tests dont see a global imagemagick install
|
|
45
|
+
Before('@fake-no-imagemagick') do
|
|
46
|
+
reject_paths_with_cmd('mogrify')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# adjust the path so tests dont see a global ffmpeg install
|
|
50
|
+
Before('@fake-no-ffmpeg') do
|
|
51
|
+
reject_paths_with_cmd('ffmpeg')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# do test in temporary directory so our own git repo-ness doesn't affect it
|
|
55
|
+
Before('@in-tempdir') do
|
|
56
|
+
@dirs = [Dir.mktmpdir]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
After('@in-tempdir') do
|
|
60
|
+
FileUtils.rm_rf(@dirs.first)
|
|
61
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'aruba/api'
|
|
4
|
+
require 'lolcommits/platform'
|
|
5
|
+
|
|
6
|
+
module PathHelpers
|
|
7
|
+
def reject_paths_with_cmd(cmd)
|
|
8
|
+
# make a new subdir that still contains cmds
|
|
9
|
+
tmpbindir = File.expand_path(File.join(@dirs, 'bin'))
|
|
10
|
+
FileUtils.mkdir_p tmpbindir
|
|
11
|
+
|
|
12
|
+
preseve_cmds_in_path(%w(git mplayer), tmpbindir)
|
|
13
|
+
|
|
14
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
|
15
|
+
newpaths = ENV['PATH'].split(File::PATH_SEPARATOR).reject do |path|
|
|
16
|
+
found_cmd = false
|
|
17
|
+
exts.each do |ext|
|
|
18
|
+
exe = "#{path}/#{cmd}#{ext}"
|
|
19
|
+
found_cmd = true if File.executable? exe
|
|
20
|
+
end
|
|
21
|
+
found_cmd
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# add the temporary directory with git in it back into the path
|
|
25
|
+
newpaths << tmpbindir
|
|
26
|
+
|
|
27
|
+
# use aruba/api set_env to set PATH, which will be automaticaly restored
|
|
28
|
+
set_env 'PATH', newpaths.join(File::PATH_SEPARATOR)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def preseve_cmds_in_path(cmds, tmpbindir)
|
|
32
|
+
cmds.each do |cmd|
|
|
33
|
+
whichcmd = Lolcommits::Platform.command_which(cmd)
|
|
34
|
+
unless whichcmd.nil?
|
|
35
|
+
FileUtils.ln_s whichcmd, File.join(tmpbindir, File.basename(whichcmd))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
# To maintain MiniMagick compatibility on Windows for v3.8.1 we need this patch
|
|
3
|
+
# If/when we upgrade MiniMagick to 4.2+ this patch can be removed
|
|
4
|
+
# We are locked at v3.8.1 since MiniMagick 4+ dropped support for Ruby 1.8.7
|
|
5
|
+
module MiniMagick
|
|
6
|
+
module Utilities
|
|
7
|
+
class << self
|
|
8
|
+
# fixes issue introduced in this commit
|
|
9
|
+
# https://github.com/minimagick/minimagick/commit/65b6427395cbfe6
|
|
10
|
+
def windows_escape(cmdline)
|
|
11
|
+
'"' + cmdline.gsub(/\\(?=\\*\")/, '\\\\\\').gsub(/\"/, '\\"').gsub(/\\$/, '\\\\\\').gsub('%', '%%') + '"'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/lolcommits.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path('.')
|
|
3
|
+
|
|
4
|
+
require 'core_ext/class'
|
|
5
|
+
require 'mini_magick'
|
|
6
|
+
require 'core_ext/mini_magick/utilities'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'git'
|
|
9
|
+
require 'open3'
|
|
10
|
+
require 'methadone'
|
|
11
|
+
require 'date'
|
|
12
|
+
|
|
13
|
+
require 'lolcommits/version'
|
|
14
|
+
require 'lolcommits/configuration'
|
|
15
|
+
require 'lolcommits/capturer'
|
|
16
|
+
require 'lolcommits/git_info'
|
|
17
|
+
require 'lolcommits/installation'
|
|
18
|
+
require 'lolcommits/plugin'
|
|
19
|
+
require 'lolcommits/platform'
|
|
20
|
+
|
|
21
|
+
require 'lolcommits/plugins/loltext'
|
|
22
|
+
require 'lolcommits/plugins/dot_com'
|
|
23
|
+
require 'lolcommits/plugins/tranzlate'
|
|
24
|
+
require 'lolcommits/plugins/lol_twitter'
|
|
25
|
+
require 'lolcommits/plugins/uploldz'
|
|
26
|
+
require 'lolcommits/plugins/lolsrv'
|
|
27
|
+
require 'lolcommits/plugins/lol_yammer'
|
|
28
|
+
require 'lolcommits/plugins/lol_protonet'
|
|
29
|
+
require 'lolcommits/plugins/lol_tumblr'
|
|
30
|
+
require 'lolcommits/plugins/lol_slack'
|
|
31
|
+
require 'lolcommits/plugins/snapgit'
|
|
32
|
+
|
|
33
|
+
# require runner after all the plugins have been required
|
|
34
|
+
require 'lolcommits/runner'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class Capturer
|
|
4
|
+
include Methadone::CLILogging
|
|
5
|
+
|
|
6
|
+
attr_accessor :capture_device, :capture_delay, :snapshot_location,
|
|
7
|
+
:video_location, :frames_location, :animated_duration
|
|
8
|
+
|
|
9
|
+
def initialize(attributes = {})
|
|
10
|
+
attributes.each do |attr, val|
|
|
11
|
+
send("#{attr}=", val)
|
|
12
|
+
end
|
|
13
|
+
debug 'Capturer: initializing new instance ' + to_s
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
require 'lolcommits/capturer/capture_mac'
|
|
19
|
+
require 'lolcommits/capturer/capture_mac_animated'
|
|
20
|
+
require 'lolcommits/capturer/capture_linux'
|
|
21
|
+
require 'lolcommits/capturer/capture_linux_animated'
|
|
22
|
+
require 'lolcommits/capturer/capture_windows'
|
|
23
|
+
require 'lolcommits/capturer/capture_cygwin'
|
|
24
|
+
require 'lolcommits/capturer/capture_fake'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureCygwin < Capturer
|
|
4
|
+
def capture
|
|
5
|
+
# DirectShow takes a while to show... at least for me anyway
|
|
6
|
+
delaycmd = ' /delay 3000'
|
|
7
|
+
if capture_delay > 0
|
|
8
|
+
# CommandCam delay is in milliseconds
|
|
9
|
+
delaycmd = " /delay #{capture_delay * 1000}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
_stdin, stdout, _stderr = Open3.popen3("#{executable_path} /filename `cygpath -w #{snapshot_location}`#{delaycmd}")
|
|
13
|
+
|
|
14
|
+
# looks like we still need to read the output for something to happen
|
|
15
|
+
stdout.read
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def executable_path
|
|
19
|
+
File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', 'CommandCam', 'CommandCam.exe')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureLinux < Capturer
|
|
4
|
+
MPLAYER_FPS = 25
|
|
5
|
+
|
|
6
|
+
def capture_device_string
|
|
7
|
+
@capture_device.nil? ? nil : "-tv device=\"#{@capture_device}\""
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def capture
|
|
11
|
+
debug 'LinuxCapturer: making tmp directory'
|
|
12
|
+
tmpdir = Dir.mktmpdir
|
|
13
|
+
|
|
14
|
+
# Default delay is 1s
|
|
15
|
+
delay = capture_delay != 0 ? capture_delay : 1
|
|
16
|
+
|
|
17
|
+
# There's no way to give a capture delay in mplayer, but a number of frame
|
|
18
|
+
# mplayer's "delay" is actually a number of frames at 25 fps
|
|
19
|
+
# multiply the set value (in seconds) by 25
|
|
20
|
+
frames = delay.to_i * MPLAYER_FPS
|
|
21
|
+
|
|
22
|
+
debug 'LinuxCapturer: calling out to mplayer to capture image'
|
|
23
|
+
# mplayer's output is ugly and useless, let's throw it away
|
|
24
|
+
_stdin, stdout, _stderr = Open3.popen3("mplayer -vo jpeg:outdir=#{tmpdir} #{capture_device_string} -frames #{frames} -fps #{MPLAYER_FPS} tv://")
|
|
25
|
+
# looks like we still need to read the output for something to happen
|
|
26
|
+
stdout.read
|
|
27
|
+
|
|
28
|
+
debug 'LinuxCapturer: calling out to mplayer to capture image'
|
|
29
|
+
|
|
30
|
+
# get last frame from tmpdir (regardless of fps)
|
|
31
|
+
all_frames = Dir.glob("#{tmpdir}/*.jpg").sort_by do |f|
|
|
32
|
+
File.mtime(f)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if all_frames.empty?
|
|
36
|
+
debug 'LinuxCapturer: failed to capture any image'
|
|
37
|
+
else
|
|
38
|
+
FileUtils.mv(all_frames.last, snapshot_location)
|
|
39
|
+
debug 'LinuxCapturer: cleaning up'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
FileUtils.rm_rf(tmpdir)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureLinuxAnimated < Capturer
|
|
4
|
+
def capture
|
|
5
|
+
# make a fresh frames directory
|
|
6
|
+
FileUtils.rm_rf(frames_location)
|
|
7
|
+
FileUtils.mkdir_p(frames_location)
|
|
8
|
+
|
|
9
|
+
# capture the raw video with ffmpeg video4linux2
|
|
10
|
+
system_call "ffmpeg -v quiet -y -f video4linux2 -video_size 320x240 -i #{capture_device_string} -t #{capture_duration} \"#{video_location}\" > /dev/null"
|
|
11
|
+
return unless File.exist?(video_location)
|
|
12
|
+
# convert raw video to png frames with ffmpeg
|
|
13
|
+
system_call "ffmpeg #{capture_delay_string} -v quiet -i \"#{video_location}\" -t #{animated_duration} \"#{frames_location}/%09d.png\" > /dev/null"
|
|
14
|
+
|
|
15
|
+
# use fps to set delay and number of frames to skip (for lower filesized gifs)
|
|
16
|
+
fps = video_fps(video_location)
|
|
17
|
+
skip = frame_skip(fps)
|
|
18
|
+
delay = frame_delay(fps, skip)
|
|
19
|
+
debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
|
|
20
|
+
|
|
21
|
+
# create the looping animated gif from frames (picks nth frame with seq,
|
|
22
|
+
# quotes output and concats to a single line with tr)
|
|
23
|
+
seq_command = "seq -f \"\\\"#{frames_location}/%09g.png\\\"\" 1 #{skip} #{Dir["#{frames_location}/*"].length} | tr '\\n' ' '"
|
|
24
|
+
seq_frame_files = system_call(seq_command, true)
|
|
25
|
+
# convert to animated gif with delay and gif optimisation
|
|
26
|
+
system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 #{seq_frame_files} -coalesce \"#{snapshot_location}\""
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def system_call(call_str, capture_output = false)
|
|
32
|
+
debug "Capturer: making system call for \n #{call_str}"
|
|
33
|
+
capture_output ? `#{call_str}` : system(call_str)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def frame_delay(fps, skip)
|
|
37
|
+
# calculate frame delay
|
|
38
|
+
delay = ((100.0 * skip) / fps.to_f).to_i
|
|
39
|
+
delay < 6 ? 6 : delay # hard limit for IE browsers
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def video_fps(file)
|
|
43
|
+
# inspect fps of the captured video file (default to 29.97)
|
|
44
|
+
fps = system_call("ffmpeg -i \"#{file}\" 2>&1 | sed -n \"s/.*, \\(.*\\) fp.*/\\1/p\"", true)
|
|
45
|
+
fps.to_i < 1 ? 29.97 : fps.to_f
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def frame_skip(fps)
|
|
49
|
+
# of frames to skip depends on movie fps
|
|
50
|
+
case fps
|
|
51
|
+
when 0..15
|
|
52
|
+
2
|
|
53
|
+
when 16..28
|
|
54
|
+
3
|
|
55
|
+
else
|
|
56
|
+
4
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def capture_device_string
|
|
61
|
+
capture_device || '/dev/video0'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def capture_delay_string
|
|
65
|
+
" -ss #{capture_delay}" if capture_delay.to_i > 0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def capture_duration
|
|
69
|
+
animated_duration.to_i + capture_delay.to_i
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureMac < Capturer
|
|
4
|
+
def capture_device_string
|
|
5
|
+
@capture_device.nil? ? nil : "-d \"#{@capture_device}\""
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def capture
|
|
9
|
+
# TODO: check we have a webcam we can capture from first. See issue #219
|
|
10
|
+
# operating laptop in clamshell (lid closed) from 2nd desktop screen,
|
|
11
|
+
# needs to better handle the capturer (imagesnap, videosnap
|
|
12
|
+
# CommandCam, mplayer) return code or check with an option before
|
|
13
|
+
# attempting capture. Alt solution is puttin in prompt mode option :(
|
|
14
|
+
call_str = "#{executable_path} -q \"#{snapshot_location}\" -w #{capture_delay} #{capture_device_string}"
|
|
15
|
+
debug "Capturer: making system call for #{call_str}"
|
|
16
|
+
system(call_str)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def executable_path
|
|
20
|
+
File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', 'imagesnap', 'imagesnap')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureMacAnimated < Capturer
|
|
4
|
+
def capture
|
|
5
|
+
# make a fresh frames directory
|
|
6
|
+
FileUtils.rm_rf(frames_location)
|
|
7
|
+
FileUtils.mkdir_p(frames_location)
|
|
8
|
+
|
|
9
|
+
# capture the raw video with videosnap
|
|
10
|
+
system_call "#{executable_path} -s 240 #{capture_device_string}#{capture_delay_string}-t #{animated_duration} --no-audio \"#{video_location}\" > /dev/null"
|
|
11
|
+
return unless File.exist?(video_location)
|
|
12
|
+
# get fps for ffmpeg output stream configuration
|
|
13
|
+
fps = video_fps(video_location)
|
|
14
|
+
# convert raw video to png frames with ffmpeg
|
|
15
|
+
system_call "ffmpeg -v quiet -i \"#{video_location}\" -t #{animated_duration} -r #{fps} \"#{frames_location}/%09d.png\""
|
|
16
|
+
|
|
17
|
+
# use fps to set delay and number of frames to skip (for lower filesized gifs)
|
|
18
|
+
skip = frame_skip(fps)
|
|
19
|
+
delay = frame_delay(fps, skip)
|
|
20
|
+
debug "Capturer: animated gif choosing every #{skip} frames with a frame delay of #{delay}"
|
|
21
|
+
|
|
22
|
+
# create the looping animated gif from frames (picks nth frame with seq,
|
|
23
|
+
# quotes output and concats to a single line with tr)
|
|
24
|
+
seq_command = "seq -f \"\\\"#{frames_location}/%09g.png\\\"\" 1 #{skip} #{Dir["#{frames_location}/*"].length} | tr '\\n' ' '"
|
|
25
|
+
seq_frame_files = system_call(seq_command, true)
|
|
26
|
+
# convert to animated gif with delay and gif optimisation
|
|
27
|
+
system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 #{seq_frame_files} -coalesce \"#{snapshot_location}\" > /dev/null"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def executable_path
|
|
31
|
+
File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', 'videosnap', 'videosnap')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def system_call(call_str, capture_output = false)
|
|
37
|
+
debug "Capturer: making system call for \n #{call_str}"
|
|
38
|
+
capture_output ? `#{call_str}` : system(call_str)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def frame_delay(fps, skip)
|
|
42
|
+
# calculate frame delay
|
|
43
|
+
delay = ((100.0 * skip) / fps.to_f).to_i
|
|
44
|
+
delay < 6 ? 6 : delay # hard limit for IE browsers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def video_fps(file)
|
|
48
|
+
# inspect fps of the captured video file (default to 29.97)
|
|
49
|
+
fps = system_call("ffmpeg -i \"#{file}\" 2>&1 | sed -n \"s/.*, \\(.*\\) fp.*/\\1/p\"", true)
|
|
50
|
+
fps.to_i < 1 ? 29.97 : fps.to_f
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def frame_skip(fps)
|
|
54
|
+
# of frames to skip depends on movie fps
|
|
55
|
+
case fps
|
|
56
|
+
when 0..15
|
|
57
|
+
2
|
|
58
|
+
when 16..28
|
|
59
|
+
3
|
|
60
|
+
else
|
|
61
|
+
4
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def capture_device_string
|
|
66
|
+
"-d \"#{capture_device}\" " if capture_device
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def capture_delay_string
|
|
70
|
+
"-w \"#{capture_delay}\" " if capture_delay.to_i > 0
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
module Lolcommits
|
|
3
|
+
class CaptureWindows < Capturer
|
|
4
|
+
def capture
|
|
5
|
+
# DirectShow takes a while to show... at least for me anyway
|
|
6
|
+
delaycmd = ' /delay 3000'
|
|
7
|
+
if capture_delay > 0
|
|
8
|
+
# CommandCam delay is in milliseconds
|
|
9
|
+
delaycmd = " /delay #{capture_delay * 1000}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
_stdin, stdout, _stderr = Open3.popen3("#{executable_path} /filename #{snapshot_location}#{delaycmd}")
|
|
13
|
+
|
|
14
|
+
# looks like we still need to read the output for something to happen
|
|
15
|
+
stdout.read
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def executable_path
|
|
19
|
+
File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', 'CommandCam', 'CommandCam.exe')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|