viddl 0.0.2
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/LICENSE +13 -0
- data/README.md +100 -0
- data/bin/viddl +80 -0
- data/lib/viddl.rb +19 -0
- data/lib/viddl/system.rb +38 -0
- data/lib/viddl/video.rb +23 -0
- data/lib/viddl/video/clip.rb +129 -0
- data/lib/viddl/video/clip/audio.rb +43 -0
- data/lib/viddl/video/clip/crop.rb +59 -0
- data/lib/viddl/video/clip/cut.rb +83 -0
- data/lib/viddl/video/clip/resize.rb +66 -0
- data/lib/viddl/video/download.rb +49 -0
- data/lib/viddl/video/instance.rb +62 -0
- data/spec/helper.rb +8 -0
- data/spec/system_spec.rb +77 -0
- data/spec/video/clip/audio_spec.rb +81 -0
- data/spec/video/clip/crop_spec.rb +113 -0
- data/spec/video/clip/cut_spec.rb +246 -0
- data/spec/video/clip/resize_spec.rb +136 -0
- data/spec/video/clip_spec.rb +443 -0
- data/spec/video/download_spec.rb +42 -0
- data/spec/video/instance_spec.rb +121 -0
- data/spec/video_spec.rb +24 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 754721cccbe03abc4baaaeeb3cc7b4a466ff792b
|
4
|
+
data.tar.gz: 4aaddeaa7ff0dcd5bdedd75e81168671a640e528
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d0ac5e272ff2b62a0db0e2de646c85450ad802a1b9248216966f1421a3fdcaa555ca4938c867629bc09756945915f787e59976195147852941bc0823cc1ac5c8
|
7
|
+
data.tar.gz: 81f1c6314ab103b92c6bead41880835dc5eb4afebe9362d09f6881d35cc9fb4122b8b82604745b426364dfe7ec8d410ce14fa1f725d965e696451fa4d143fd8f
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2017 Ari Russo
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Viddl
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Use Viddl to quickly download, cut, crop and resize videos
|
6
|
+
|
7
|
+
Viddl can be used at the [command line](#command-line) or in [Ruby](#ruby)
|
8
|
+
|
9
|
+
It requires that both [youtube-dl](https://github.com/rg3/youtube-dl) and [ffmpeg](https://ffmpeg.org) are installed before using.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Running Viddl generates video clip files in the current directory
|
14
|
+
|
15
|
+
### Command Line
|
16
|
+
|
17
|
+
The command line usage and options are as follows:
|
18
|
+
|
19
|
+
#### Download
|
20
|
+
|
21
|
+
With no options, Viddl will download the original video
|
22
|
+
|
23
|
+
```sh
|
24
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU
|
25
|
+
```
|
26
|
+
|
27
|
+
#### Cut
|
28
|
+
|
29
|
+
This will start the clip at 10 seconds into the original video and run for five seconds
|
30
|
+
|
31
|
+
```sh
|
32
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU -s 10 -d 5
|
33
|
+
```
|
34
|
+
|
35
|
+
Alternately, this will start the clip at 15 seconds in the original video and stop at 22 seconds
|
36
|
+
|
37
|
+
```sh
|
38
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU -s 15 -e 22
|
39
|
+
```
|
40
|
+
|
41
|
+
#### Resize
|
42
|
+
|
43
|
+
This will resize to 640 x 480:
|
44
|
+
|
45
|
+
```sh
|
46
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU -w 640 -h 480
|
47
|
+
```
|
48
|
+
|
49
|
+
#### Crop
|
50
|
+
|
51
|
+
This will crop a 40 x 40 pixel box at position 20 x 20:
|
52
|
+
|
53
|
+
```sh
|
54
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU --cx 20 --cy 20 --cw 40 --ch 40
|
55
|
+
```
|
56
|
+
|
57
|
+
#### Strip Audio
|
58
|
+
|
59
|
+
Audio can be left out of the clip:
|
60
|
+
|
61
|
+
```sh
|
62
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU --no-audio
|
63
|
+
```
|
64
|
+
|
65
|
+
#### Combine
|
66
|
+
|
67
|
+
Any or all of these options can be used together:
|
68
|
+
|
69
|
+
```sh
|
70
|
+
viddl https://www.youtube.com/watch?v=6g4dkBF5anU -s 15 -e 22 --no-audio --cx 20 --cy 20 --cw 40 --ch 40 -w 640 -h 480
|
71
|
+
```
|
72
|
+
|
73
|
+
### Ruby
|
74
|
+
|
75
|
+
Similar to the command line, Ruby usage and options are as follows:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
options = {
|
79
|
+
start: 15,
|
80
|
+
end: 22,
|
81
|
+
audio: false,
|
82
|
+
crop: {
|
83
|
+
x: 20,
|
84
|
+
y: 20,
|
85
|
+
width: 40,
|
86
|
+
height: 40
|
87
|
+
},
|
88
|
+
width: 640,
|
89
|
+
height: 480
|
90
|
+
}
|
91
|
+
|
92
|
+
video = Viddl::Video.download("https://www.youtube.com/watch?v=6g4dkBF5anU")
|
93
|
+
video.create_clip(options)
|
94
|
+
```
|
95
|
+
|
96
|
+
## License
|
97
|
+
|
98
|
+
Licensed under Apache 2.0, See the file LICENSE
|
99
|
+
|
100
|
+
Copyright (c) 2017 [Ari Russo](http://arirusso.com)
|
data/bin/viddl
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require "viddl"
|
6
|
+
|
7
|
+
def help(opts)
|
8
|
+
puts(opts)
|
9
|
+
exit
|
10
|
+
end
|
11
|
+
|
12
|
+
options = {}
|
13
|
+
opts = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: viddl [url] [options]"
|
15
|
+
|
16
|
+
## cut
|
17
|
+
|
18
|
+
opts.on("-dDURATION", "--duration=DURATION", "Duration") do |duration|
|
19
|
+
options[:duration] = duration.to_f
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-eEND", "--end=END", "End time") do |en|
|
23
|
+
options[:end] = en.to_f
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("-sSTART", "--start=START", "Start time") do |start|
|
27
|
+
options[:start] = start.to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
# resize
|
31
|
+
|
32
|
+
opts.on("-wWIDTH", "--width=WIDTH", "Width") do |width|
|
33
|
+
options[:width] = width.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-hHEIGHT", "--height=HEIGHT", "Height") do |height|
|
37
|
+
options[:height] = height.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
# crop
|
41
|
+
|
42
|
+
opts.on("--cx=CX", "Crop X") do |cx|
|
43
|
+
options[:crop] ||= {}
|
44
|
+
options[:crop][:x] = cx.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--cy=CY", "Crop Y") do |cy|
|
48
|
+
options[:crop] ||= {}
|
49
|
+
options[:crop][:y] = cy.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("--cw=CWIDTH", "Crop Width") do |cwidth|
|
53
|
+
options[:crop] ||= {}
|
54
|
+
options[:crop][:width] = cwidth.to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--ch=CHEIGHT", "Crop Height") do |cheight|
|
58
|
+
options[:crop] ||= {}
|
59
|
+
options[:crop][:height] = cheight.to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
# audio
|
63
|
+
|
64
|
+
opts.on("--no-audio", "No audio") do
|
65
|
+
options[:audio] = false
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on_tail("--help", "Show this message") { help(opts) }
|
69
|
+
|
70
|
+
end
|
71
|
+
opts.parse!
|
72
|
+
|
73
|
+
url = ARGV[0]
|
74
|
+
|
75
|
+
if url.nil?
|
76
|
+
help(opts) and exit
|
77
|
+
end
|
78
|
+
|
79
|
+
video = Viddl::Video.download(url)
|
80
|
+
video.create_clip(options)
|
data/lib/viddl.rb
ADDED
data/lib/viddl/system.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Viddl
|
2
|
+
|
3
|
+
module System
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Validate that the system has all of its dependencies
|
8
|
+
# @return [Boolean]
|
9
|
+
def validate
|
10
|
+
validate_ffmpeg
|
11
|
+
validate_youtube_dl
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Validate that ffmpeg is installed
|
18
|
+
# @return [Boolean]
|
19
|
+
def validate_ffmpeg
|
20
|
+
result = Kernel.system("ffmpeg")
|
21
|
+
if result.nil?
|
22
|
+
raise("Viddl requires that ffmpeg be installed")
|
23
|
+
end
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Validate that youtube-dl is installed
|
28
|
+
# @return [Boolean]
|
29
|
+
def validate_youtube_dl
|
30
|
+
result = Kernel.system("youtube-dl")
|
31
|
+
if result.nil?
|
32
|
+
raise("Viddl requires that youtube-dl be installed https://github.com/rg3/youtube-dl")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/viddl/video.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "viddl/video/clip"
|
2
|
+
require "viddl/video/download"
|
3
|
+
require "viddl/video/instance"
|
4
|
+
|
5
|
+
module Viddl
|
6
|
+
|
7
|
+
module Video
|
8
|
+
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Download a video using the given url
|
12
|
+
# @param [String] url
|
13
|
+
# @param [Hash] options
|
14
|
+
# @return [Video::Instance]
|
15
|
+
def download(url, options = {})
|
16
|
+
video = Instance.new(url)
|
17
|
+
video.process_download(options)
|
18
|
+
video
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "viddl/video/clip/audio"
|
2
|
+
require "viddl/video/clip/crop"
|
3
|
+
require "viddl/video/clip/cut"
|
4
|
+
require "viddl/video/clip/resize"
|
5
|
+
|
6
|
+
module Viddl
|
7
|
+
|
8
|
+
module Video
|
9
|
+
|
10
|
+
class Clip
|
11
|
+
|
12
|
+
MODULES = [Audio, Crop, Cut, Resize]
|
13
|
+
|
14
|
+
# Create a clip using the given video source file path and options
|
15
|
+
# @param [String] source_path Path to video file to create clip from
|
16
|
+
# @param [Hash] options
|
17
|
+
# @option options [Boolean] :audio Whether to include audio
|
18
|
+
# @option options [Numeric] :start Time in the source file where the clip starts
|
19
|
+
# @option options [Numeric] :duration Duration of the clip
|
20
|
+
# @option options [Numeric] :end Time in the source file where the clip ends
|
21
|
+
# @option options [Integer, String] :width The desired width to resize to
|
22
|
+
# @option options [Integer, String] :height The desired height to resize to
|
23
|
+
# @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
|
24
|
+
# @return [Clip]
|
25
|
+
def self.process(path, options = {})
|
26
|
+
clip = new(path)
|
27
|
+
clip.process(options)
|
28
|
+
clip
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [String] source_path Path to video file to create clip from
|
32
|
+
def initialize(source_path)
|
33
|
+
@source_path = source_path
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a clip using the given options
|
37
|
+
# @param [Hash] options
|
38
|
+
# @option options [Boolean] :audio Whether to include audio
|
39
|
+
# @option options [Numeric] :start Time in the source file where the clip starts
|
40
|
+
# @option options [Numeric] :duration Duration of the clip
|
41
|
+
# @option options [Numeric] :end Time in the source file where the clip ends
|
42
|
+
# @option options [Integer, String] :width The desired width to resize to
|
43
|
+
# @option options [Integer, String] :height The desired height to resize to
|
44
|
+
# @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
|
45
|
+
# @return [Boolean]
|
46
|
+
def process(options = {})
|
47
|
+
command = command_line(options)
|
48
|
+
Kernel.system(command)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Command line to create a clip from the source file and given options
|
54
|
+
# @param [Hash] options
|
55
|
+
# @option options [Boolean] :audio Whether to include audio
|
56
|
+
# @option options [Numeric] :start Time in the source file where the clip starts
|
57
|
+
# @option options [Numeric] :duration Duration of the clip
|
58
|
+
# @option options [Numeric] :end Time in the source file where the clip ends
|
59
|
+
# @option options [Integer, String] :width The desired width to resize to
|
60
|
+
# @option options [Integer, String] :height The desired height to resize to
|
61
|
+
# @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
|
62
|
+
# @return [String]
|
63
|
+
def command_line(options = {})
|
64
|
+
if options.values.compact.empty?
|
65
|
+
# when there are no clip options, the source file can just be copied
|
66
|
+
# over to the output file location without using ffmpeg
|
67
|
+
"cp #{@source_path} #{output_path}"
|
68
|
+
else
|
69
|
+
formatted_opts = options_formatted(options)
|
70
|
+
|
71
|
+
modules_with_args = MODULES.select { |mod| mod.respond_to?(:args) }
|
72
|
+
modules_with_filters = MODULES.select { |mod| mod.respond_to?(:filter_args) }
|
73
|
+
|
74
|
+
module_args = modules_with_args.map { |mod| mod.args(formatted_opts) }.compact
|
75
|
+
module_filters = modules_with_filters.map { |mod| mod.filter_args(formatted_opts) }.compact
|
76
|
+
|
77
|
+
module_arg_string = module_args.join(" ")
|
78
|
+
unless module_filters.empty?
|
79
|
+
module_arg_string += " -vf '#{module_filters.join(",")}'"
|
80
|
+
end
|
81
|
+
|
82
|
+
"ffmpeg -i #{@source_path} #{module_arg_string} #{output_path(formatted_opts)}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Options formatted for ffmpeg
|
87
|
+
# @param [Hash] options
|
88
|
+
# @option options [Boolean] :audio Whether to include audio (default: true)
|
89
|
+
# @option options [Numeric] :start Time in the source file where the clip starts
|
90
|
+
# @option options [Numeric] :duration Duration of the clip
|
91
|
+
# @option options [Numeric] :end Time in the source file where the clip ends
|
92
|
+
# @option options [Integer, String] :width The desired width to resize to
|
93
|
+
# @option options [Integer, String] :height The desired height to resize to
|
94
|
+
# @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
|
95
|
+
# @return [Hash]
|
96
|
+
def options_formatted(options = {})
|
97
|
+
mod_options = MODULES.map { |mod| mod.options_formatted(options) }
|
98
|
+
mod_options.inject(:merge)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Path of the created clip
|
102
|
+
# @param [Hash] options
|
103
|
+
# @option options [Boolean] :audio Whether to include audio
|
104
|
+
# @option options [Numeric] :start Time in the source file where the clip starts
|
105
|
+
# @option options [Numeric] :duration Duration of the clip
|
106
|
+
# @option options [Integer, String] :width The desired width to resize to
|
107
|
+
# @option options [Integer, String] :height The desired height to resize to
|
108
|
+
# @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
|
109
|
+
# @return [String]
|
110
|
+
def output_path(options = {})
|
111
|
+
base = @source_path.scan(/#{Download::TEMPDIR}\/(.*)/).flatten.first
|
112
|
+
result = base
|
113
|
+
if !options.values.flatten.compact.empty?
|
114
|
+
name, ext = *base.split(".")
|
115
|
+
tokens = ""
|
116
|
+
MODULES.each do |mod|
|
117
|
+
token = mod.filename_token(options)
|
118
|
+
tokens += "-#{token}" unless token.nil?
|
119
|
+
end
|
120
|
+
result = "#{name}#{tokens}.#{ext}"
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Viddl
|
2
|
+
|
3
|
+
module Video
|
4
|
+
|
5
|
+
class Clip
|
6
|
+
|
7
|
+
module Audio
|
8
|
+
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Audio options formatted for ffmpeg
|
12
|
+
# @param [Hash] options
|
13
|
+
# @option options [Boolean] :audio Whether to include audio (default: true)
|
14
|
+
# @return [Hash]
|
15
|
+
def options_formatted(options = {})
|
16
|
+
result = {}
|
17
|
+
result[:audio] = !options[:audio].eql?(false)
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
# Command line options for audio
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [Boolean] :audio Whether to include audio (default: false)
|
24
|
+
# @return [String, nil]
|
25
|
+
def args(options = {})
|
26
|
+
"-an" if options[:audio].eql?(false)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Token added to clip filename for audio args
|
30
|
+
# @param [Hash] options
|
31
|
+
# @option options [Boolean] :audio Whether to include audio in the clip (default: false)
|
32
|
+
# @return [String, nil]
|
33
|
+
def filename_token(options = {})
|
34
|
+
"noaudio" if options[:audio].eql?(false)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|