screenshot_generator 0.0.1
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/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +12 -0
- data/README.md +32 -0
- data/Rakefile +5 -0
- data/lib/screenshot_generator.rb +77 -0
- data/lib/screenshot_generator/version.rb +3 -0
- data/screenshot_generator.gemspec +24 -0
- data/spec/features/image_generation_spec.rb +101 -0
- data/spec/fixtures/test-video.mov +0 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc45676b2096d9d1ac542d9e69c4a2491dae98ea
|
4
|
+
data.tar.gz: 90edf5253fffb6edaa668cc0824927dc68c29349
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 866854e0a4210f882315cdb4c6fa5f7fd92d73f771f7e5751b5d689dec888d4e01a0598dce3d0bb35edbcfdb3511fa185cc838e12369c10331ab9c527a040821
|
7
|
+
data.tar.gz: 59e420d241994def73a32944cda3dfc6178fe0c0ce534b2dfa99623480e826e9c05564bf64bf40bcd8ed2103e81b05516c79ef78a1a8365371b3f18ead7928f6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO + BEER/PIZZA PUBLIC LICENSE
|
2
|
+
Version 1, May 2011
|
3
|
+
|
4
|
+
Copyright (C) 2011 Tom Lea
|
5
|
+
|
6
|
+
|
7
|
+
DO WHAT THE FUCK YOU WANT TO + BEER/PIZZA PUBLIC LICENSE
|
8
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
9
|
+
|
10
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
|
+
1. If you make a substantial amount of money by exercising clause 0,
|
12
|
+
you should consider buying the author a beer or a pizza.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# ScreenshotGenerator
|
2
|
+
|
3
|
+
It makes pictures of your videos using ffmpg, and ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
```bash
|
7
|
+
gem install screenshot_generator
|
8
|
+
```
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
```ruby
|
12
|
+
# Capture that lovely view from 3 hours into your 6 hour holiday video.
|
13
|
+
ScreenshotGenerator.extract_frame("holiday.mp4", 60*60*3, "the-car-park.jpg")
|
14
|
+
|
15
|
+
# Take 20 frames spread evenly around your holiday video, so you can sample the
|
16
|
+
# majesty of the same beach, from 20 different angles.
|
17
|
+
ScreenshotGenerator.extract_multi("holiday.mp4", "pictures-for-firends/", 20)
|
18
|
+
|
19
|
+
# Capture the three interesting bits of your holiday video.
|
20
|
+
vid = ScreenshotGenerator.new("holiday.mp4")
|
21
|
+
vid.extract_frame(60*60*2+8, "base-jumping.jpg")
|
22
|
+
vid.extract_frame(vid.length - 30, "end-credits.jpg")
|
23
|
+
```
|
24
|
+
|
25
|
+
## Additional usage
|
26
|
+
Although not thoroughly tested, it is theoretically possible that this library
|
27
|
+
could be used to take screen shots of videos that are neither holiday related,
|
28
|
+
nor dull.
|
29
|
+
|
30
|
+
## Licence
|
31
|
+
WTFBPPL: http://tomlea.co.uk/WTFBPPL.txt
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require "screenshot_generator/version"
|
2
|
+
|
3
|
+
class ScreenshotGenerator
|
4
|
+
# Helper method for fetching a single frame
|
5
|
+
def self.extract_frame(video, *args)
|
6
|
+
new(video).extract_frame(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.extract_multi(video, *args)
|
10
|
+
new(video).extract_multi(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(video, size: '640x400')
|
14
|
+
@video = video
|
15
|
+
@size = size
|
16
|
+
raise ArgumentError, "Video does not exist" unless File.exist? @video
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :video, :size
|
20
|
+
|
21
|
+
def extract_frame(offset, path)
|
22
|
+
raise ArgumentError, "File already exists" if File.exist? path
|
23
|
+
offset = format_offset(offset)
|
24
|
+
ffmpeg(
|
25
|
+
"-loglevel", "error",
|
26
|
+
"-ss", offset,
|
27
|
+
"-i", video,
|
28
|
+
"-frames:v", 1,
|
29
|
+
"-s", size,
|
30
|
+
path
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def extract_multi(dir, count)
|
36
|
+
dir = Pathname.new(dir)
|
37
|
+
0.upto(count) do |index|
|
38
|
+
path = dir + "#{index}.jpg"
|
39
|
+
segment_size = length / (count + 2)
|
40
|
+
offset = (index + 1) * segment_size
|
41
|
+
extract_frame(offset, path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def length
|
46
|
+
@length ||= get_length
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def ffmpeg(*args)
|
52
|
+
options = [self.class.ffmpeg, *args].map(&:to_s)
|
53
|
+
options.push(:err=>[:child, :out])
|
54
|
+
IO.popen(options) {|io| return io.read }
|
55
|
+
raise "ffmpeg comamnd #{options.inspect} failed" unless $?.success?
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.ffmpeg
|
59
|
+
@ffmpeg ||= `which ffmpeg`.chomp
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_length
|
63
|
+
output = ffmpeg("-i", video, "-frames", 1)
|
64
|
+
if output =~ /Duration:\s*([0-9:.]+)/
|
65
|
+
h,m,s = $1.split(":").map(&:to_i)
|
66
|
+
h*60^2 + m*60 + s
|
67
|
+
else
|
68
|
+
60
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_offset(v)
|
73
|
+
m, s = v.divmod(60)
|
74
|
+
h, m = m.divmod(60)
|
75
|
+
"%02i:%02i:%02i" % [h, m, s]
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'screenshot_generator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "screenshot_generator"
|
8
|
+
spec.version = ScreenshotGenerator::VERSION
|
9
|
+
spec.authors = ["Tom Lea"]
|
10
|
+
spec.email = ["commit@tomlea.co.uk"]
|
11
|
+
spec.summary = %q{A simple wrapper around FFMpeg for extracing screen shots}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "WTFBPPL"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "chunky_png"
|
24
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require "screenshot_generator"
|
6
|
+
require "rspec"
|
7
|
+
|
8
|
+
require "chunky_png"
|
9
|
+
|
10
|
+
describe ScreenshotGenerator do
|
11
|
+
let(:test_video){ "spec/fixtures/test-video.mov" }
|
12
|
+
let(:tmpfile){ "tmp/test_frame.png" }
|
13
|
+
|
14
|
+
describe ".extract_frame" do
|
15
|
+
it "extracts a frame from the video" do
|
16
|
+
expect(color_for_frame(0)).to be :red
|
17
|
+
expect(color_for_frame(1)).to be :green
|
18
|
+
expect(color_for_frame(2)).to be :blue
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises an ArgumentError if the video does not exist" do
|
22
|
+
expect{
|
23
|
+
ScreenshotGenerator.extract_frame("/tmp/does-not-exist.mp4", 0, "/dev/null")
|
24
|
+
}.to raise_error ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
it "errors is the output file already exists" do
|
28
|
+
expect{
|
29
|
+
ScreenshotGenerator.extract_frame(test_video, 0, __FILE__)
|
30
|
+
}.to raise_error ArgumentError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#length" do
|
35
|
+
it "knows how long the video is" do
|
36
|
+
expect(ScreenshotGenerator.new(test_video).length).to eq 18
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".extract_multi" do
|
41
|
+
it "calls extract_frame for each needed frame" do
|
42
|
+
in_a_temp_dir do |dir|
|
43
|
+
ScreenshotGenerator.extract_multi(test_video, dir, 5)
|
44
|
+
0.upto(5) do |i|
|
45
|
+
%x{convert #{dir + "#{i}.jpg"} #{dir + "#{i}.png"}}
|
46
|
+
end
|
47
|
+
expect(color_for_image(dir + "0.png")).to be :blue
|
48
|
+
expect(color_for_image(dir + "1.png")).to be :black
|
49
|
+
expect(color_for_image(dir + "2.png")).to be :green
|
50
|
+
expect(color_for_image(dir + "3.png")).to be :white
|
51
|
+
expect(color_for_image(dir + "4.png")).to be :red
|
52
|
+
expect(color_for_image(dir + "5.png")).to be :blue
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def color_for_image(path)
|
58
|
+
rgb(ChunkyPNG::Image.from_file(path)[50,50]).classify
|
59
|
+
end
|
60
|
+
|
61
|
+
def color_for_frame(i)
|
62
|
+
ScreenshotGenerator.extract_frame(test_video, i, tmpfile)
|
63
|
+
color_for_image(tmpfile)
|
64
|
+
ensure
|
65
|
+
File.unlink tmpfile if File.exists?(tmpfile)
|
66
|
+
end
|
67
|
+
|
68
|
+
def in_a_temp_dir
|
69
|
+
dir = Pathname.new("tmp/working")
|
70
|
+
FileUtils.mkdir_p dir
|
71
|
+
yield dir
|
72
|
+
ensure
|
73
|
+
FileUtils.rm_r dir
|
74
|
+
end
|
75
|
+
|
76
|
+
Color = Struct.new(:r, :g, :b) do
|
77
|
+
def classify
|
78
|
+
if r > 250 and g > 250 and b > 250
|
79
|
+
:white
|
80
|
+
elsif r < 5 and g > 250 and b < 5
|
81
|
+
:green
|
82
|
+
elsif r > 250 and g < 5 and b < 5
|
83
|
+
:red
|
84
|
+
elsif r < 5 and g < 5 and b > 250
|
85
|
+
:blue
|
86
|
+
elsif r < 5 and g < 5 and b < 5
|
87
|
+
:black
|
88
|
+
else
|
89
|
+
raise "I don't know the color #{self.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def rgb(color)
|
95
|
+
Color.new(
|
96
|
+
ChunkyPNG::Color.r(color),
|
97
|
+
ChunkyPNG::Color.g(color),
|
98
|
+
ChunkyPNG::Color.b(color)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: screenshot_generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Lea
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: chunky_png
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- commit@tomlea.co.uk
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/screenshot_generator.rb
|
82
|
+
- lib/screenshot_generator/version.rb
|
83
|
+
- screenshot_generator.gemspec
|
84
|
+
- spec/features/image_generation_spec.rb
|
85
|
+
- spec/fixtures/test-video.mov
|
86
|
+
homepage: ''
|
87
|
+
licenses:
|
88
|
+
- WTFBPPL
|
89
|
+
metadata: {}
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.0.14
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: A simple wrapper around FFMpeg for extracing screen shots
|
110
|
+
test_files:
|
111
|
+
- spec/features/image_generation_spec.rb
|
112
|
+
- spec/fixtures/test-video.mov
|