muvy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86d7b6a8c11d0ce8195f6d35bf9400028771c85a
4
+ data.tar.gz: a8c38340f7e011b1821f89f0e6ce7f040382dfd9
5
+ SHA512:
6
+ metadata.gz: 209b168a3b90bd569a48504295a4a745adbaf4e08c46d742d73b9027d12624fa6327dc9369fe738fbaf8a65f27f4d8668b048279eefb7723c4bd930397677122
7
+ data.tar.gz: 9bbcaa4a1ff2a929d2d1983d493e387b3f9124c104a9e74e6c9b5b84c6670e6266eed611b69fad1097873c4d009b473a1c99214df8cdae231f8a11f6d62859d4
@@ -0,0 +1,54 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ Gemfile.lock
47
+ # .ruby-version
48
+ # .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
52
+
53
+ # rspec failure tracking
54
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Aaron Agarunov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,172 @@
1
+ ![muvy-header](https://i.imgur.com/Akc3Fh9.png)
2
+
3
+ **muvy** is a simple Ruby movie barcode generator.
4
+ You can feed it a youtube video, phone gallery, or any locally stored video files. It pulls most of the frames out, moves around the colors, and throws them back together in a neat montage.
5
+
6
+ ------
7
+ * [Install](#install)
8
+ * [Notes](#notes)
9
+ * [Getting Started](#getting-started)
10
+ * [Usage](#usage)
11
+ * [Basics](#basics)
12
+ * [Options](#options)
13
+ * [Features](#features)
14
+ * [Examples](#examples)
15
+ * [Links](#links)
16
+ ------
17
+
18
+ ## Install
19
+
20
+ ### Notes
21
+
22
+ * Currently version 0.2.0, with many [features still planned](#features).
23
+
24
+ ### Getting Started
25
+
26
+ #### macOS
27
+ If you don't already have FFmpeg, ImageMagick, or youtube-dl installed, you can download all of them with [Homebrew](https://brew.sh/). You can use `ffmpeg -v`, `convert -v`, or `youtube-dl --version` at the terminal to check if you've already got them.
28
+
29
+ With Homewbrew, just bring up a terminal session and type:
30
+ ```sh
31
+ $ brew install ffmpeg
32
+ $ brew install imagemagick
33
+ $ brew install youtube-dl
34
+ $ gem install muvy
35
+ ```
36
+
37
+ #### Windows
38
+ 1. You can [download Ruby here](https://rubyinstaller.org/).
39
+ 2. Then you can grab [FFmpeg here](http://ffmpeg.zeranoe.com/builds/).
40
+ 3. ..and then download [ImageMagick here](https://www.imagemagick.org/script/download.php#windows), **noting**:
41
+ * On the nth installation window, you need to check 2 boxes:
42
+ * [x] Add folder to your path variable
43
+ * [x] Download legacy binaries
44
+ 4. And finally get [youtube-dl here](https://rg3.github.io/youtube-dl/download.html).
45
+ 5. Then, you can install any gem like so:
46
+ ```sh
47
+ $ gem install muvy
48
+ ```
49
+
50
+
51
+ ## Usage
52
+
53
+ ### Basics
54
+
55
+ | Type | Command: `muvy [Type] [Options]` | Support |
56
+ |--------|--------------------------------------------|-----------------------------------------------------------------------------------------------|
57
+ | URL | `muvy https://someVideoSite.com/someVidID` | [youtube-dl supported sites](https://rg3.github.io/youtube-dl/supportedsites.html) |
58
+ | Local | `muvy /Documents/Fave-Films/movie.mp4` | [FFmpeg supported formats](https://www.ffmpeg.org/general.html#File-Formats) |
59
+ | Folder | `muvy /Downloads/Phone-Backup-1/Photos` | [ImageMagick supported formats](https://www.imagemagick.org/script/formats.php) |
60
+
61
+ ### Options
62
+
63
+ #### `-p, --path`
64
+
65
+ Optionally specify the path where your final image will be saved.
66
+ **Default**: your present working directory
67
+
68
+ #### `-s, --style`
69
+ Optionally specify currently supported styles: [solid](link) or [stretch](link).
70
+ **Default**: solid
71
+
72
+ #### `-g, --gradient`
73
+ Optionally add a gradient on top of the final image.
74
+
75
+ Choose one:
76
+ ```
77
+ black:heavy black:medium black:light
78
+ white:heavy white:medium white:light
79
+ ```
80
+ **Default**: none
81
+
82
+ [See examples](link)
83
+
84
+ #### `--arc`
85
+ Pass `--arc` to wrap all of the lines around a center point.
86
+
87
+ [See examples](link)
88
+
89
+ #### `--rotate`
90
+ Pass `--rotate` to rotate the final image 90 degrees, i.e. to draw horizontal lines,
91
+ where the top is the 'start' of your media file.
92
+ TODO - check if the last sentence is true
93
+
94
+ #### `-h, --height`
95
+ Optionally specify a custom height for the output image.
96
+
97
+ #### `--format`
98
+ Optionally force the download quality for `youtube-dl`.
99
+ This determines the height of your image when using `-s stretch` only if you didn't specify --height.
100
+ **Default**: 135 *(854x480 DASH at 24fps)*
101
+
102
+ See [youtube-dl docs on format selection](https://github.com/rg3/youtube-dl/blob/master/README.md#format-selection).
103
+
104
+ #### `--frame_rate`
105
+ Optionally specify the amount of frames to extract per second from the media.
106
+ This determines the width of the image.
107
+
108
+ You should run `muvy [..]` without this option once and check the stats printout
109
+ to get an idea of a better number.
110
+ For example, if the stats printout used "1.6 fps," passing `--frame_rate 3.2`
111
+ would double the amount of frames, lines, and subsequently the width.
112
+
113
+ > Setting this to an unreasonable number might cause hundreds of thousands
114
+ of files to be temporarily created in your system's temp files.
115
+
116
+ #### `--start` and `--end`
117
+ Optionally specify starting and ending times for processing videos.
118
+ If you only specify one of them, the other will default to the start/end.
119
+
120
+ [Examples](link)
121
+
122
+ ### Features
123
+ - [x] Accepting image galleries, local videos, and online videos
124
+ - [x] Specifying start & end times for frame extraction
125
+ - [x] Vertical lines
126
+ - [x] Horizontal lines
127
+ - [x] Stretched output (average of each line of pixels)
128
+ - [x] Arc distortion
129
+ - [ ] Spotmap output ('QR' code)
130
+ - [ ] Slit scan output
131
+ - [ ] 'Bedforms' output
132
+ - [ ] Dominant color algorithms
133
+ - [ ] via ImageMagick histograms
134
+ - [ ] via k-means clustering
135
+ - [ ] Fade to black or white on edges
136
+ - [ ] Pixel thickness control
137
+ - [ ] Colorspace adjustments
138
+ - [ ] Accept music files
139
+ - [ ] Generate audio waveforms
140
+ - [ ] Randomize waveform colors
141
+ - [ ] Presets
142
+
143
+ ## Examples
144
+
145
+ ## Troubleshooting
146
+
147
+ Make sure you can access `ffmpeg -v`, `magick -v`, and `youtube-dl --version` on the command line. If you can't, you likely have to update your existing PATH environment variable [like this](https://video.stackexchange.com/questions/20495/how-do-i-set-up-and-use-ffmpeg-in-windows).
148
+
149
+ You might also want to update all three binaries.
150
+
151
+ If it's not working out, [I've linked more generators](#links) and methods that you can try out, most of them depending on some combination of `ffmpeg` and `ImageMagick`.
152
+
153
+ ## Links
154
+ * Binaries · Gems
155
+ * [FFmpeg](https://www.ffmpeg.org/documentation.html) · [Streamio FFmpeg](https://github.com/streamio/streamio-ffmpeg)
156
+ * [ImageMagick](https://www.imagemagick.org/script/command-line-options.php) · [MiniMagick](https://github.com/minimagick/minimagick)
157
+ * [youtube-dl](https://github.com/rg3/youtube-dl) · [youtube-dl.rb](https://github.com/layer8x/youtube-dl.rb)
158
+ * Other Works & Inspirations
159
+ * [Zach Whalen's Barcoder](http://zachwhalen.net/pg/barcoder/)
160
+ * [arcanesanctum generator](http://arcanesanctum.net/movie-barcode-generator/)
161
+ * [moviebarcode on tumblr](http://moviebarcode.tumblr.com/)
162
+ * [/u/etherealpenguin on reddit](https://www.reddit.com/r/dataisbeautiful/comments/3rb8zi/the_average_color_of_every_frame_of_a_given_movie/)
163
+ * [Colors of Motion](http://thecolorsofmotion.com/films)
164
+ * Slit scanning
165
+ * [informal catalogue of research on this topic by Levin](http://www.flong.com/texts/lists/slit_scan/)
166
+ * K-means clustering as dominant color algorithms
167
+ * [k-means clustering on wikipedia](link)
168
+ * ImageMagick Histograms
169
+ * [Sparse color docs](http://www.imagemagick.org/Usage/canvas/#sparse-color)
170
+ * [Stackoverflow discussion (1)](https://stackoverflow.com/questions/40381273/apply-gradient-mask-on-image-that-already-has-transparency-with-imagemagick)
171
+ * ImageMagick support
172
+ * tremendous thank you to [fmw](http://www.fmwconcepts.com/imagemagick/index.php) & [snibgo](http://im.snibgo.com/index.htm)
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "muvy"
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(__FILE__)
@@ -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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'muvy'
4
+
5
+ Muvy::CLI.new.start
@@ -0,0 +1,5 @@
1
+ require "muvy/version"
2
+ require "muvy/cli"
3
+
4
+ module Muvy
5
+ end
@@ -0,0 +1,119 @@
1
+ require 'muvy/media'
2
+ require 'muvy/errors'
3
+ require 'slop'
4
+
5
+ module Muvy
6
+ class CLI
7
+ attr_reader :media, :options
8
+
9
+ def start
10
+ parse
11
+ handle_media
12
+ handle_path
13
+ convert_options
14
+ read_media
15
+ end
16
+
17
+ def parse
18
+ @options = Slop.parse do |o|
19
+ o.banner = "Usage: muvy [media link, file, or file path] [options]"
20
+
21
+ o.separator ""
22
+ o.separator "Optional adjustments:"
23
+ o.string "-p", "--path", "Directory to save final images, " +
24
+ "\n\t\t\s\s\s\sDefault: your pwd → #{Dir.pwd}",
25
+ default: Dir.pwd
26
+ o.string "-s", "--style", "Choose image style: solid, stretch " +
27
+ "\n\t\t\s\s\s\sDefault: solid",
28
+ default: "solid"
29
+ o.string "-g", "--gradient", "Add a gradient over the final image" +
30
+ "\n\t\t\s\s\s\sChoose one from: black:heavy black:medium black:light" +
31
+ "\n\t\t\s\s\s\s\t\t\s\s\s\s\swhite:heavy white:medium white:light" +
32
+ "\n\t\t\s\s\s\sDefault: none"
33
+ o.boolean "--arc", "Wrap all of the lines around a center point"
34
+ o.boolean "-r", "--rotate", "Image will have horizontal lines"
35
+ o.integer "-h", "--height", "Custom height of the final image"
36
+ o.string "--start", "Custom video start time"
37
+ o.string "--end", "Custom video end time"
38
+ o.string "--format", "Force youtube-dl to use a specific video quality"
39
+ o.string "--frame_rate", <<~FPS
40
+ Set a custom frame rate. Be careful!
41
+ \t\t\s\s\s\sSetting this to a high number might cause hundreds of
42
+ \t\t\s\s\s\sthousands of images to be generated in your sytem's temp dir.
43
+ \t\t\s\s\s\sThe specific frame_rate used by default for your file is printed
44
+ \t\t\s\s\s\safter generation. You can use that number to make a reasonable change.
45
+ FPS
46
+
47
+ o.separator "More:"
48
+ o.on "--help", "Shows this usage page" do
49
+ abort o.to_s
50
+ end
51
+
52
+ o.on "-v", "--version", "Displays the version" do
53
+ abort "muvy version #{VERSION}"
54
+ end
55
+ end
56
+ rescue Slop::Error => e
57
+ abort <<~ERROR
58
+ #{e}.
59
+ Type `muvy --help` to see options, or visit the
60
+ github repo for extensive usage examples.
61
+ ERROR
62
+ end
63
+
64
+ private
65
+
66
+ def handle_media
67
+ @media = options.arguments.shift
68
+ raise Muvy::Errors::NoMediaInput if media.nil?
69
+ rescue => e
70
+ abort input_error(e)
71
+ end
72
+
73
+ # if -path was specified but is invalid, raise an error
74
+ # if -path was not specified, it was set to the pwd by Slop defaults
75
+ def handle_path
76
+ raise Muvy::Errors::InvalidPathOption unless File.directory?(options[:path])
77
+ rescue => e
78
+ abort "#{e}: You specified a non-existent path '#{options[:path]}'"
79
+ end
80
+
81
+ # FileUtils.remove_entry_secure is called before ::mktmpdir returns
82
+ def read_media
83
+ Dir.mktmpdir do |tmp_dir|
84
+ options[:tmp_dir] = tmp_dir
85
+ puts "Using #{tmp_dir} to store jobs locally..."
86
+
87
+ Media.new(media, options).run
88
+ end
89
+ rescue => e
90
+ abort media_error(e)
91
+ ensure
92
+ puts "#{options[:tmp_dir]} was erased." if options[:tmp_dir]
93
+ end
94
+
95
+ def convert_options
96
+ @options = options.to_hash # finalize
97
+ end
98
+
99
+ def input_error(e)
100
+ <<~INPUT_ERROR
101
+ #{e}
102
+ You forgot to enter a URL, file, or folder with images.
103
+
104
+ #{options}
105
+ INPUT_ERROR
106
+ end
107
+
108
+ def media_error(e)
109
+ <<~MEDIA_ERROR
110
+ #{e}
111
+ Media is unrecognized.
112
+ The input was not a valid URL, file, or folder with images.
113
+
114
+ #{options}
115
+ MEDIA_ERROR
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'youtube-dl.rb'
4
+
5
+ module Muvy
6
+ class Download
7
+ attr_reader :media, :options, :settings
8
+
9
+ def initialize(media, options = {})
10
+ @media = media
11
+ @options = options
12
+ end
13
+
14
+ def run
15
+ @settings = merge_settings
16
+
17
+ download_video
18
+ send_video
19
+ end
20
+
21
+ private
22
+
23
+ def download_video
24
+ vid = YoutubeDL.download(media, settings)
25
+ add_options(vid)
26
+
27
+ puts <<~DOWNLOADED
28
+ Download complete.
29
+ Video title: #{vid.information[:title]}
30
+ Video URL: #{vid.information[:webpage_url]}
31
+ Video format: #{vid.information[:format]} saved as #{vid.information[:ext]}
32
+ as #{vid.information[:_filename]}
33
+ File size: #{(vid.information[:filesize] / 1.024e6).round(2)} MB.
34
+ DOWNLOADED
35
+ end
36
+
37
+ # Add important settings to @options hash for use by FFmpeg
38
+ def add_options(vid)
39
+ options[:fps] = vid.information[:fps]
40
+ options[:media_length] = vid.information[:duration]
41
+ end
42
+
43
+ def send_video
44
+ Video.new(settings[:output], options).run if File.exists?(settings[:output])
45
+ rescue => e
46
+ puts e
47
+ end
48
+
49
+ # defaults holds default values
50
+ # options holds command-line arguments
51
+ # settings merges defaults with options where appropriate
52
+ def merge_settings
53
+ defaults = {
54
+ continue: false,
55
+ format: 135,
56
+ output: "#{options[:tmp_dir]}/" +
57
+ Time.now.strftime("%d-%m-%Y-%H%M%S") +
58
+ ".mp4"
59
+ }
60
+
61
+ @settings = defaults.merge!(options.select { |k, v| defaults.key?(k) && v })
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,9 @@
1
+ module Muvy
2
+ module Errors
3
+ class NoMediaInput < ArgumentError; end
4
+
5
+ class InvalidMediaInput < ArgumentError; end
6
+
7
+ class InvalidPathOption < ArgumentError; end
8
+ end
9
+ end
@@ -0,0 +1,125 @@
1
+ require "mini_magick"
2
+
3
+ module Muvy
4
+ class Image
5
+ attr_reader :media, :options
6
+
7
+ def initialize(media, options)
8
+ @media = media
9
+ @options = options
10
+ end
11
+
12
+ def run
13
+ montage
14
+ modification
15
+ printout
16
+ end
17
+
18
+ private
19
+
20
+ def montage
21
+ MiniMagick::Tool::Montage.new do |montage|
22
+ montage << "#{media}/thumb*.png"
23
+ montage.mode("Concatenate")
24
+ montage.tile("x1")
25
+ options[:img] =
26
+ File.absolute_path(options[:path]) +
27
+ "/muvy-" +
28
+ Time.now.strftime('%d-%m-%H%M%S') +
29
+ ".png"
30
+ montage << options[:img]
31
+ end
32
+ end
33
+
34
+ def modification
35
+ image = MiniMagick::Image.new(options[:img])
36
+
37
+ # TODO: Programatically trigger the methods based on options `nil` status
38
+ resize(image)
39
+ gradient(image)
40
+ rotate(image)
41
+ arc
42
+ end
43
+
44
+ # Arbitrary default height 720
45
+ def resize(image)
46
+ if options[:style] != "stretch"
47
+ image.resize "#{image.width}x#{options[:height] ||= 720}!"
48
+ elsif options[:height] # and style is stretch
49
+ image.resize "#{image.width}x#{options[:height]}!"
50
+ end
51
+ end
52
+
53
+ def gradient(image)
54
+ if options[:gradient]
55
+ choice = options[:gradient].split(":")
56
+ choice_path = "#{options[:tmp_dir]}/muvy-gradient.png"
57
+
58
+ weights = {
59
+ "heavy" => 1.6,
60
+ "medium" => 0.9,
61
+ "light" => 0.5
62
+ }
63
+
64
+ MiniMagick::Tool::Convert.new do |cmd|
65
+ cmd.size("#{image.width}x#{image.height}")
66
+ cmd << "gradient:"
67
+ cmd << "-function" << "Polynomial" << "-4,4,.1"
68
+ cmd << "-evaluate" << "Pow" << weights[choice[1]]
69
+ cmd.negate unless choice[0] == "white"
70
+ cmd.stack do |stack|
71
+ stack.merge! ["+clone", "-fill", "Black", "-colorize", "100"]
72
+ end
73
+ cmd << "+swap"
74
+ cmd << "-alpha" << "Off"
75
+ cmd.compose("CopyOpacity")
76
+ cmd << "-composite"
77
+ cmd.negate unless choice[0] == "black"
78
+ cmd << choice_path
79
+ end
80
+
81
+ gradient_image = MiniMagick::Image.new(choice_path)
82
+ apply_gradient = image.composite(gradient_image) do |composite|
83
+ composite.compose "Over"
84
+ end
85
+
86
+ apply_gradient.write(options[:img])
87
+ end
88
+ end
89
+
90
+ def rotate(image)
91
+ image.rotate(90) if options[:rotate]
92
+ end
93
+
94
+ def arc
95
+ if options[:arc]
96
+ MiniMagick::Tool::Convert.new do |cmd|
97
+ cmd << options[:img]
98
+ cmd << "-gravity" << "center"
99
+ cmd << "+repage"
100
+ cmd << "-virtual-pixel" << "Transparent"
101
+ cmd << "-distort" << "Arc" << "366"
102
+ cmd << options[:img]
103
+ end
104
+ end
105
+ end
106
+
107
+ def printout
108
+ image = MiniMagick::Image.open(options[:img])
109
+ puts <<~PRINTOUT_IMG
110
+ Saved to #{options[:img]}.
111
+ Width: #{image.dimensions[0]}, Height: #{image.dimensions[1]}
112
+ Type: #{image.type}
113
+ Colorspace: #{image.colorspace}
114
+ PRINTOUT_IMG
115
+
116
+ puts <<~PRINTOUT_VIDEO if options[:fps]
117
+
118
+ Thumbnails made at #{options[:frame_rate] ? options[:frame_rate] : (options[:fps] / (options[:media_length]**(1 / 1.99))).round(4) } frames per second.
119
+ Original video duration was #{options[:media_length]}.
120
+ #{'Start time: ' + options[:start].to_s if options[:start]}
121
+ #{'End time: ' + options[:end].to_s if options[:end]}
122
+ PRINTOUT_VIDEO
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,56 @@
1
+ require "mini_magick"
2
+
3
+ module Muvy
4
+ class ImageFolder
5
+ attr_reader :media, :options, :photos
6
+
7
+ def initialize(media, options)
8
+ @media = media
9
+ @options = options
10
+ @photos = add_photos
11
+ end
12
+
13
+ def run
14
+ average
15
+ send_thumbs
16
+ end
17
+
18
+ private
19
+
20
+ def add_photos
21
+ Dir.glob("#{media}/*")
22
+ end
23
+
24
+ def average
25
+ # Store the average photos here
26
+ Dir.mkdir("#{options[:tmp_dir]}/muvy-img-folder/")
27
+
28
+ photos.each_with_index do |photo, i|
29
+ MiniMagick::Tool::Convert.new do |convert|
30
+ convert << photo
31
+ convert.resize("1x#{height}!")
32
+ convert << "#{options[:tmp_dir]}/muvy-img-folder/thumb#{i.to_s.rjust(6, '0')}.png"
33
+ end
34
+ end
35
+ end
36
+
37
+ def height
38
+ if options[:style] == "stretch"
39
+ options[:height] ? options[:height] : (abort <<~NO_HEIGHT)
40
+ You specified an image folder and 'stretch' with the --style flag.
41
+ You should also specify a uniform height with the --height option.
42
+ NO_HEIGHT
43
+ else
44
+ # Arbitrary default height 720
45
+ options[:height] ||= 720
46
+
47
+ # 1x1 to get average colors ('solid' style)
48
+ 1
49
+ end
50
+ end
51
+
52
+ def send_thumbs
53
+ Image.new("#{options[:tmp_dir]}/muvy-img-folder", options).run
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ require 'muvy/download'
2
+ require 'muvy/video'
3
+ require 'muvy/imagefolder'
4
+ require 'muvy/errors'
5
+ require 'uri'
6
+
7
+ module Muvy
8
+ class Media
9
+ attr_reader :media, :options, :type
10
+
11
+ def initialize(media, options)
12
+ @media = media
13
+ @options = options
14
+ end
15
+
16
+ def run
17
+ get_type
18
+ send_type
19
+ end
20
+
21
+ private
22
+
23
+ def send_type
24
+ Muvy.const_get(type.to_s).new(media, options).run
25
+ end
26
+
27
+ # Checks the first argument (store in :media, access via getter).
28
+ # Determines if it should be read by Download (type - online URL),
29
+ # by Video (type - local media), or by Image (type - local image files).
30
+ # Unrecognized inputs invoke the usage heredocs and banners at `CLI`
31
+ def get_type
32
+ if valid_url?(media)
33
+ @type = :Download
34
+ elsif file_exists?(media)
35
+ @type = :Video
36
+ elsif path_exists?(media)
37
+ @type = :ImageFolder
38
+ else
39
+ raise Muvy::Errors::InvalidMediaInput
40
+ end
41
+ end
42
+
43
+ # Accepts a string that behaves like a URL.
44
+ # The URL must have a valid URI scheme (e.g. http) to differentiate
45
+ # it from file paths. URI module doesn't recognize #host without it.
46
+ def valid_url?(url)
47
+ encoded_url = URI.escape(url)
48
+ parsed_url = URI.parse(encoded_url)
49
+ !parsed_url.host.nil?
50
+ end
51
+
52
+ def file_exists?(file)
53
+ file_path = File.absolute_path(file)
54
+ File.file?(file_path)
55
+ end
56
+
57
+ def path_exists?(path)
58
+ full_path = File.absolute_path(path)
59
+ File.directory?(full_path)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module Muvy
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,60 @@
1
+ require 'streamio-ffmpeg'
2
+ require 'muvy/image'
3
+
4
+ module Muvy
5
+ class Video
6
+ attr_reader :media, :options, :settings, :vid
7
+
8
+ def initialize(media, options = {})
9
+ @media = media
10
+ @options = options
11
+ end
12
+
13
+ def run
14
+ @vid = FFMPEG::Movie.new(media)
15
+ @settings = merge_settings
16
+
17
+ thumbs
18
+ send_thumbs
19
+ end
20
+
21
+ private
22
+
23
+ # Add important settings to @options hash for use by FFmpeg
24
+ def add_options
25
+ options[:fps] = vid.frame_rate
26
+ options[:media_length] = vid.duration
27
+ end
28
+
29
+ # defaults holds default values
30
+ # options holds command-line arguments
31
+ # settings merges defaults with options where appropriate
32
+ def merge_settings
33
+ add_options
34
+
35
+ defaults = {
36
+ vframes: (options[:fps] * options[:media_length]).floor,
37
+ frame_rate: options[:fps] / (options[:media_length]**(1 / 1.99)),
38
+ custom: %W{
39
+ -vf scale=1:#{options[:style] == 'stretch' ? vid.height : 1}
40
+ -ss #{options[:start] ? options[:start] : 0}
41
+ -to #{options[:end] ? options[:end] : options[:media_length]}
42
+ }
43
+ }
44
+
45
+ @settings = defaults.merge!(options.select { |k, v| defaults.key?(k) && v })
46
+ end
47
+
48
+ def thumbs
49
+ vid.screenshot(
50
+ "#{options[:tmp_dir]}/thumb%06d.png",
51
+ settings,
52
+ validate: false
53
+ )
54
+ end
55
+
56
+ def send_thumbs
57
+ Image.new(options[:tmp_dir], options).run
58
+ end
59
+ end
60
+ 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 "muvy/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "muvy"
8
+ spec.version = Muvy::VERSION
9
+ spec.authors = ["agarun"]
10
+ spec.email = ["19801205+agarun@users.noreply.github.com"]
11
+
12
+ spec.summary = "Ruby movie barcode generator."
13
+ spec.homepage = "https://github.com/agarun/ruby-muvy"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_runtime_dependency "youtube-dl.rb", "~> 0"
24
+ spec.add_runtime_dependency "streamio-ffmpeg", "~> 3.0"
25
+ spec.add_runtime_dependency "mini_magick", "~> 4.8"
26
+ spec.add_runtime_dependency "slop", "~> 4.6"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.15"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: muvy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - agarun
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-10-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: youtube-dl.rb
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: streamio-ffmpeg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mini_magick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: slop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.15'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.15'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ description:
112
+ email:
113
+ - 19801205+agarun@users.noreply.github.com
114
+ executables:
115
+ - muvy
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rspec"
121
+ - ".travis.yml"
122
+ - Gemfile
123
+ - LICENSE
124
+ - README.md
125
+ - Rakefile
126
+ - bin/console
127
+ - bin/setup
128
+ - exe/muvy
129
+ - lib/muvy.rb
130
+ - lib/muvy/cli.rb
131
+ - lib/muvy/download.rb
132
+ - lib/muvy/errors.rb
133
+ - lib/muvy/image.rb
134
+ - lib/muvy/imagefolder.rb
135
+ - lib/muvy/media.rb
136
+ - lib/muvy/version.rb
137
+ - lib/muvy/video.rb
138
+ - muvy.gemspec
139
+ homepage: https://github.com/agarun/ruby-muvy
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.5.1
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Ruby movie barcode generator.
163
+ test_files: []