muvy 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.
@@ -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: []