video_sprites 0.1.0

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +76 -0
  7. data/Rakefile +10 -0
  8. data/Vagrantfile +76 -0
  9. data/ansible/development-playbook.yml +15 -0
  10. data/ansible/development.ini +2 -0
  11. data/ansible/roles/basic-setup/tasks/main.yml +35 -0
  12. data/ansible/roles/ffmpeg/defaults/main.yml +6 -0
  13. data/ansible/roles/ffmpeg/tasks/ff-libraries.yml +21 -0
  14. data/ansible/roles/ffmpeg/tasks/ffmpeg.yml +18 -0
  15. data/ansible/roles/ffmpeg/tasks/libfdk_aac.yml +19 -0
  16. data/ansible/roles/ffmpeg/tasks/libmp3lame.yml +20 -0
  17. data/ansible/roles/ffmpeg/tasks/libogg.yml +18 -0
  18. data/ansible/roles/ffmpeg/tasks/libopus.yml +20 -0
  19. data/ansible/roles/ffmpeg/tasks/libvorbis.yml +18 -0
  20. data/ansible/roles/ffmpeg/tasks/libvpx.yml +18 -0
  21. data/ansible/roles/ffmpeg/tasks/main.yml +80 -0
  22. data/ansible/roles/ffmpeg/tasks/x264.yml +18 -0
  23. data/ansible/roles/ffmpeg/tasks/x265.yml +14 -0
  24. data/ansible/roles/ffmpeg/tasks/yasm.yml +19 -0
  25. data/ansible/roles/rvm_io.rvm1-ruby/.gitignore +6 -0
  26. data/ansible/roles/rvm_io.rvm1-ruby/.travis.yml +52 -0
  27. data/ansible/roles/rvm_io.rvm1-ruby/LICENSE +22 -0
  28. data/ansible/roles/rvm_io.rvm1-ruby/README.md +142 -0
  29. data/ansible/roles/rvm_io.rvm1-ruby/defaults/main.yml +44 -0
  30. data/ansible/roles/rvm_io.rvm1-ruby/meta/main.yml +21 -0
  31. data/ansible/roles/rvm_io.rvm1-ruby/tasks/main.yml +4 -0
  32. data/ansible/roles/rvm_io.rvm1-ruby/tasks/rubies.yml +78 -0
  33. data/ansible/roles/rvm_io.rvm1-ruby/tasks/rvm.yml +55 -0
  34. data/ansible/roles/rvm_io.rvm1-ruby/tests/inventory +1 -0
  35. data/ansible/roles/rvm_io.rvm1-ruby/tests/test.yml +14 -0
  36. data/ansible/roles/rvm_io.rvm1-ruby/vars/main.yml +22 -0
  37. data/bin/console +14 -0
  38. data/bin/setup +8 -0
  39. data/exe/videosprites +6 -0
  40. data/lib/video_sprites.rb +7 -0
  41. data/lib/video_sprites/cli.rb +17 -0
  42. data/lib/video_sprites/processor.rb +167 -0
  43. data/lib/video_sprites/version.rb +3 -0
  44. data/video_sprites.gemspec +31 -0
  45. metadata +173 -0
@@ -0,0 +1,55 @@
1
+ ---
2
+
3
+ - name: Detect rvm binary
4
+ stat: path='{{ rvm1_rvm }}'
5
+ register: rvm_binary
6
+
7
+ - name: Detect rvm installer
8
+ stat: path='{{ rvm1_temp_download_path }}/rvm-installer.sh'
9
+ register: rvm_installer
10
+
11
+ - name: Detect current rvm version
12
+ command: '{{ rvm1_rvm}} version'
13
+ changed_when: False
14
+ register: rvm_current_version
15
+ when: rvm_binary.stat.exists
16
+
17
+ - name: Install rvm installer
18
+ get_url:
19
+ url: '{{ rvm1_rvm_latest_installer }}'
20
+ dest: '{{ rvm1_temp_download_path }}/rvm-installer.sh'
21
+ when: not rvm_installer.stat.exists
22
+
23
+ - name: Configure rvm installer
24
+ file:
25
+ path: '{{ rvm1_temp_download_path }}/rvm-installer.sh'
26
+ mode: 0755
27
+ when: not rvm_binary.stat.exists
28
+
29
+ - name: Import GPG keys
30
+ command: 'gpg --keyserver {{ rvm1_gpg_key_server }} --recv-keys {{ rvm1_gpg_keys }}'
31
+ changed_when: False
32
+ when: rvm1_gpg_keys != ''
33
+ become: yes
34
+ become_user: '{{ rvm1_user }}'
35
+
36
+ - name: Install rvm
37
+ command: >
38
+ {{ rvm1_temp_download_path }}/rvm-installer.sh {{ rvm1_rvm_version }}
39
+ --path {{ rvm1_install_path }} {{ rvm1_install_flags }}
40
+ when: not rvm_binary.stat.exists
41
+ become: yes
42
+ become_user: '{{ rvm1_user }}'
43
+
44
+ - name: Update rvm
45
+ shell: '{{ rvm1_rvm }} get {{ rvm1_rvm_version }} && {{ rvm1_rvm }} reload'
46
+ changed_when: False
47
+ when: rvm_binary.stat.exists and rvm1_rvm_check_for_updates
48
+ become: yes
49
+ become_user: '{{ rvm1_user }}'
50
+
51
+ - name: Configure rvm
52
+ command: '{{ rvm1_rvm }} autolibs {{ rvm1_autolib_mode }}'
53
+ when: not rvm_binary.stat.exists
54
+ become: yes
55
+ become_user: '{{ rvm1_user }}'
@@ -0,0 +1 @@
1
+ localhost
@@ -0,0 +1,14 @@
1
+ ---
2
+
3
+ - hosts: localhost
4
+ remote_user: travis
5
+
6
+ vars:
7
+ rvm1_rubies:
8
+ - 'ruby-2.1.0'
9
+ - 'ruby-2.1.2'
10
+ rvm1_install_path: '/home/travis/.rvm'
11
+ rvm1_install_flags: '--auto-dotfiles --user-install'
12
+
13
+ roles:
14
+ - rvm1-ansible
@@ -0,0 +1,22 @@
1
+ ---
2
+
3
+ rvm1_temp_download_path: '/tmp'
4
+
5
+ rvm1_default_ruby_version: '{{ rvm1_rubies | last if rvm1_rubies and rvm1_rubies is iterable else "" }}'
6
+
7
+ rvm1_rvm: '{{ rvm1_install_path }}/bin/rvm'
8
+
9
+ rvm1_symlink_binaries:
10
+ - 'bundle'
11
+ - 'bundler'
12
+ - 'erb'
13
+ - 'executable-hooks-uninstaller'
14
+ - 'gem'
15
+ - 'irb'
16
+ - 'rake'
17
+ - 'rdoc'
18
+ - 'ri'
19
+ - 'ruby'
20
+ - 'testrb'
21
+
22
+ rvm1_symlink_to: '/usr/local/bin'
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "video_sprites"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/videosprites ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'video_sprites'
4
+ require 'video_sprites/cli'
5
+ require 'byebug'
6
+ VideoSprites::CLI.start(ARGV)
@@ -0,0 +1,7 @@
1
+ require 'tmpdir'
2
+ require "video_sprites/version"
3
+ require "video_sprites/processor"
4
+
5
+ module VideoSprites
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,17 @@
1
+ require 'thor'
2
+ module VideoSprites
3
+ class CLI < Thor
4
+
5
+ desc "process <input_file> <output_directory>", "Create video thumbnail sprites"
6
+ option :seconds, default: 10, type: :numeric
7
+ option :width, default: 200, type: :numeric
8
+ option :columns, default: 5, type: :numeric
9
+ option :group, default: 20, type: :numeric
10
+ option :gif, default: false, type: :boolean
11
+ def process(input_file, output_directory)
12
+ processor = VideoSprites::Processor.new(input_file, output_directory, options)
13
+ processor.process
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,167 @@
1
+ module VideoSprites
2
+ class Processor
3
+ def initialize(input_file, output_directory, options=nil)
4
+ @input_file = input_file
5
+ @output_directory = output_directory
6
+ FileUtils.mkdir_p @output_directory unless File.exist? @output_directory
7
+ @options = options || default_options
8
+ end
9
+
10
+ def process
11
+ create_temporary_directory
12
+ create_images
13
+ create_sprites
14
+ create_webvtt
15
+ create_gif if @options[:gif]
16
+ clean_temporary_directory
17
+ end
18
+
19
+ def create_temporary_directory
20
+ @temporary_directory = Dir.mktmpdir
21
+ puts @temporary_directory
22
+ end
23
+
24
+ def create_images
25
+ # run ffmpeg command
26
+ `#{ffmpeg_cmd}`
27
+ end
28
+
29
+ def create_sprites
30
+ # determine how many images go in each sprite
31
+ all_images.each_slice(images_per_sprite).with_index do |(*sprite_slice), index|
32
+ cmd = montage_cmd(sprite_slice, index)
33
+ # puts cmd
34
+ `#{cmd}`
35
+ end
36
+ end
37
+
38
+ def create_webvtt
39
+ @webvtt = "WEBVTT\n\nNOTE This file was automatically generated by https://github.com/jronallo/video_sprites\n\n"
40
+ start = 0
41
+ total = 0
42
+ sprite_count.times do |sprite_index|
43
+ sprite_filename_base = File.basename sprite_filename(sprite_index)
44
+ puts sprite_filename_base
45
+ @options[:group].times do |group_index|
46
+ next if total >= all_images.length
47
+ cue_start = start
48
+ cue_end = start + @options[:seconds]
49
+ x = ((group_index % @options[:columns]) * @options[:width])
50
+ y = (group_index.to_f / @options[:columns].to_f).floor * processed_height
51
+
52
+ fractional_start = start == 0 ? "000" : "001"
53
+ cue_times = "#{formatted_time(cue_start)}.#{fractional_start} --> #{formatted_time(cue_end)}.000\n"
54
+ puts cue_times
55
+ cue_text = "#{sprite_filename_base}#xywh=#{x},#{y},#{@options[:width]},#{processed_height}\n\n"
56
+ @webvtt += cue_times
57
+ @webvtt += cue_text
58
+ start = cue_end
59
+ total += 1
60
+ end
61
+ end
62
+ File.open(webvtt_output_filename, 'w') do |fh|
63
+ fh.puts @webvtt
64
+ end
65
+ end
66
+
67
+ def create_gif
68
+ `convert -geometry #{@options[:width]}x -delay 20 -loop 0 #{all_images.join(' ')} #{gif_output_filename}`
69
+ end
70
+
71
+ def ffmpeg_cmd
72
+ %Q|ffmpeg -i "#{@input_file}" -vf fps=1/#{@options[:seconds]} #{thumbnail_image_path} |
73
+ end
74
+
75
+ def montage_cmd(sprite_slice, index)
76
+ image_files = sprite_slice.join(' ')
77
+ %Q|montage #{image_files} -tile #{@options[:columns]}x -geometry #{@options[:width]}x #{sprite_filename(index)}|
78
+ end
79
+
80
+ def thumbnail_image_path
81
+ File.join @temporary_directory, 'img-%05d.jpg'
82
+ end
83
+
84
+ def sprite_filename(index)
85
+ File.join @output_directory, "#{basename}-sprite-#{padded_index(index)}.jpg"
86
+ end
87
+
88
+ def webvtt_output_filename
89
+ File.join @output_directory, "#{basename}.vtt"
90
+ end
91
+
92
+ def gif_output_filename
93
+ File.join @output_directory, "#{basename}.gif"
94
+ end
95
+
96
+ def padded_index(index)
97
+ (index + 1).to_s.rjust(5, "0")
98
+ end
99
+
100
+ def default_options
101
+ {
102
+ seconds: 10,
103
+ width: 200,
104
+ columns: 5,
105
+ group: 20,
106
+ gif: false
107
+ }
108
+ end
109
+
110
+ def all_images
111
+ if @all_images
112
+ @all_images
113
+ else
114
+ images = Dir[all_images_glob]
115
+ images.pop
116
+ @all_images = images
117
+ end
118
+ end
119
+
120
+ def all_images_glob
121
+ File.join @temporary_directory, '*'
122
+ end
123
+
124
+ def first_jpeg
125
+ all_images.first
126
+ end
127
+
128
+ def sprite_count
129
+ (all_images.length.to_f / @options[:group]).ceil
130
+ end
131
+
132
+ def images_per_sprite
133
+ @options[:group]
134
+ end
135
+
136
+ # TODO: make basename configurable
137
+ def basename
138
+ "video"
139
+ end
140
+
141
+ def original_height
142
+ `identify -format "%h" -ping "#{first_jpeg}"`.to_f
143
+ end
144
+
145
+ def original_width
146
+ `identify -format "%w" -ping "#{first_jpeg}"`.to_f
147
+ end
148
+
149
+ def processed_height
150
+ (original_height.to_f / original_width.to_f * @options[:width]).to_i
151
+ end
152
+
153
+ def formatted_time(total_seconds)
154
+ seconds = total_seconds % 60
155
+ minutes = (total_seconds / 60) % 60
156
+ hours = total_seconds / (60 * 60)
157
+
158
+ # TODO: format start times to start at .0001
159
+ format("%02d:%02d:%02d", hours, minutes, seconds)
160
+ end
161
+
162
+ def clean_temporary_directory
163
+ FileUtils.rm_rf @temporary_directory
164
+ end
165
+
166
+ end
167
+ end
@@ -0,0 +1,3 @@
1
+ module VideoSprites
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'video_sprites/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "video_sprites"
8
+ spec.version = VideoSprites::VERSION
9
+ spec.authors = ["Jason Ronallo"]
10
+ spec.email = ["jronallo@gmail.com"]
11
+
12
+ spec.summary = %q{Create video thumbnail sprite sheets}
13
+ spec.description = %q{Create video thumbnail sprite sheets and WebVTT metadata files for showing thumbnails over video time rail.}
14
+ spec.homepage = "https://github.com/jronallo/video_sprites"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency "thor"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.13"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+ spec.add_development_dependency "colorize"
30
+ spec.add_development_dependency "bundler-audit"
31
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: video_sprites
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jason Ronallo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler-audit
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Create video thumbnail sprite sheets and WebVTT metadata files for showing
98
+ thumbnails over video time rail.
99
+ email:
100
+ - jronallo@gmail.com
101
+ executables:
102
+ - videosprites
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - Vagrantfile
113
+ - ansible/development-playbook.yml
114
+ - ansible/development.ini
115
+ - ansible/roles/basic-setup/tasks/main.yml
116
+ - ansible/roles/ffmpeg/defaults/main.yml
117
+ - ansible/roles/ffmpeg/tasks/ff-libraries.yml
118
+ - ansible/roles/ffmpeg/tasks/ffmpeg.yml
119
+ - ansible/roles/ffmpeg/tasks/libfdk_aac.yml
120
+ - ansible/roles/ffmpeg/tasks/libmp3lame.yml
121
+ - ansible/roles/ffmpeg/tasks/libogg.yml
122
+ - ansible/roles/ffmpeg/tasks/libopus.yml
123
+ - ansible/roles/ffmpeg/tasks/libvorbis.yml
124
+ - ansible/roles/ffmpeg/tasks/libvpx.yml
125
+ - ansible/roles/ffmpeg/tasks/main.yml
126
+ - ansible/roles/ffmpeg/tasks/x264.yml
127
+ - ansible/roles/ffmpeg/tasks/x265.yml
128
+ - ansible/roles/ffmpeg/tasks/yasm.yml
129
+ - ansible/roles/rvm_io.rvm1-ruby/.gitignore
130
+ - ansible/roles/rvm_io.rvm1-ruby/.travis.yml
131
+ - ansible/roles/rvm_io.rvm1-ruby/LICENSE
132
+ - ansible/roles/rvm_io.rvm1-ruby/README.md
133
+ - ansible/roles/rvm_io.rvm1-ruby/defaults/main.yml
134
+ - ansible/roles/rvm_io.rvm1-ruby/meta/main.yml
135
+ - ansible/roles/rvm_io.rvm1-ruby/tasks/main.yml
136
+ - ansible/roles/rvm_io.rvm1-ruby/tasks/rubies.yml
137
+ - ansible/roles/rvm_io.rvm1-ruby/tasks/rvm.yml
138
+ - ansible/roles/rvm_io.rvm1-ruby/tests/inventory
139
+ - ansible/roles/rvm_io.rvm1-ruby/tests/test.yml
140
+ - ansible/roles/rvm_io.rvm1-ruby/vars/main.yml
141
+ - bin/console
142
+ - bin/setup
143
+ - exe/videosprites
144
+ - lib/video_sprites.rb
145
+ - lib/video_sprites/cli.rb
146
+ - lib/video_sprites/processor.rb
147
+ - lib/video_sprites/version.rb
148
+ - video_sprites.gemspec
149
+ homepage: https://github.com/jronallo/video_sprites
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.5.2
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Create video thumbnail sprite sheets
173
+ test_files: []