viddl 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ module Viddl
2
+
3
+ module Video
4
+
5
+ class Clip
6
+
7
+ module Crop
8
+
9
+ extend self
10
+
11
+ # Crop options formatted for ffmpeg
12
+ # @param [Hash] options
13
+ # @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
14
+ # @return [Hash]
15
+ def options_formatted(options = {})
16
+ result = {}
17
+ unless options[:crop].nil?
18
+ crop = {}
19
+ [:x, :y, :width, :height].each do |property|
20
+ if options[:crop][property].nil?
21
+ raise "Crop is missing required #{property} property"
22
+ else
23
+ crop[property] = options[:crop][property].to_i
24
+ end
25
+ end
26
+ result[:crop] = crop
27
+ end
28
+ result
29
+ end
30
+
31
+ # Command line options for cropping
32
+ # @param [Hash] options
33
+ # @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
34
+ # @return [String]
35
+ def filter_args(options = {})
36
+ unless options[:crop].nil?
37
+ crop = options[:crop]
38
+ "crop=#{crop[:width]}:#{crop[:height]}:#{crop[:x]}:#{crop[:y]}"
39
+ end
40
+ end
41
+
42
+ # Token added to clip filename for crop args
43
+ # @param [Hash] options
44
+ # @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
45
+ # @return [String, nil]
46
+ def filename_token(options = {})
47
+ unless options[:crop].nil?
48
+ crop = options[:crop]
49
+ "cx#{crop[:x]}cy#{crop[:y]}cw#{crop[:width]}ch#{crop[:height]}"
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,83 @@
1
+ module Viddl
2
+
3
+ module Video
4
+
5
+ class Clip
6
+
7
+ module Cut
8
+
9
+ extend self
10
+
11
+ # Cut options formatted for ffmpeg
12
+ # @param [Hash] options
13
+ # @option options [Numeric] :start Time in the source file where the clip starts
14
+ # @option options [Numeric] :duration Duration of the clip
15
+ # @option options [Numeric] :end Time in the source file where the clip ends
16
+ # @return [Hash]
17
+ def options_formatted(options = {})
18
+ result = {}
19
+ result[:start] = options[:start]
20
+ result[:duration] = duration(options)
21
+ result
22
+ end
23
+
24
+ # Command line options for the given cut constraints
25
+ # @param [Hash] options
26
+ # @option options [Numeric] :start Time in the source file where the clip starts
27
+ # @option options [Numeric] :duration Duration of the clip
28
+ # @return [String, nil]
29
+ def args(options = {})
30
+ args = []
31
+ unless options[:start].nil?
32
+ args << "-ss #{options[:start]}"
33
+ end
34
+ unless options[:duration].nil?
35
+ args << "-t #{options[:duration]}"
36
+ end
37
+ args.join(" ") unless args.empty?
38
+ end
39
+
40
+ # Token added to clip filename for cut args
41
+ # @param [Hash] options
42
+ # @option options [Numeric] :start Time in the source file where the clip starts
43
+ # @option options [Numeric] :duration Duration of the clip
44
+ # @return [String, nil]
45
+ def filename_token(options = {})
46
+ if !options[:start].nil? || !options[:duration].nil?
47
+ args = ""
48
+ if !options[:start].nil?
49
+ args += "s#{options[:start]}"
50
+ end
51
+ if !options[:duration].nil?
52
+ args += "d#{options[:duration]}"
53
+ end
54
+ args
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # Numeric duration for the given options
61
+ # @param [Hash] options
62
+ # @option options [Numeric] :duration Duration of the clip
63
+ # @option options [Numeric] :end Time in the source file where the clip ends
64
+ # @return [Numeric]
65
+ def duration(options = {})
66
+ duration = nil
67
+ if !options[:duration].nil? && !options[:end].nil?
68
+ raise "Can not use both end time and duration"
69
+ elsif !options[:duration].nil? && options[:end].nil?
70
+ duration = options[:duration]
71
+ elsif options[:duration].nil? && !options[:end].nil?
72
+ duration = options[:end] - (options[:start] || 0)
73
+ end
74
+ duration
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,66 @@
1
+ module Viddl
2
+
3
+ module Video
4
+
5
+ class Clip
6
+
7
+ module Resize
8
+
9
+ extend self
10
+
11
+ # Resize options formatted for ffmpeg
12
+ # @param [Hash] options
13
+ # @option options [Integer, String] :width The desired width to resize to
14
+ # @option options [Integer, String] :height The desired height to resize to
15
+ # @return [Hash]
16
+ def options_formatted(options = {})
17
+ result = {}
18
+ [:width, :height].each do |property|
19
+ result[property] = options[property].to_i unless options[property].nil?
20
+ end
21
+ result
22
+ end
23
+
24
+ # Command line options for resize
25
+ # @param [Hash] options
26
+ # @option options [Integer] :width The desired width to resize to
27
+ # @option options [Integer] :height The desired height to resize to
28
+ # @return [String, nil]
29
+ def filter_args(options = {})
30
+ scale = if options[:width].nil? && !options[:height].nil?
31
+ "-1:#{options[:height]}"
32
+ elsif !options[:width].nil? && options[:height].nil?
33
+ "#{options[:width]}:-1"
34
+ elsif !options[:width].nil? && !options[:height].nil?
35
+ "#{options[:width]}:#{options[:height]}"
36
+ end
37
+ unless scale.nil?
38
+ "scale=#{scale}"
39
+ end
40
+ end
41
+
42
+ # Token added to clip filename for resize args
43
+ # @param [Hash] options
44
+ # @option options [Integer] :width The desired width to resize to
45
+ # @option options [Integer] :height The desired height to resize to
46
+ # @return [String, nil]
47
+ def filename_token(options = {})
48
+ if !options[:width].nil? || !options[:height].nil?
49
+ args = ""
50
+ if !options[:width].nil?
51
+ args += "w#{options[:width]}"
52
+ end
53
+ if !options[:height].nil?
54
+ args += "h#{options[:height]}"
55
+ end
56
+ args
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,49 @@
1
+ module Viddl
2
+
3
+ module Video
4
+
5
+ class Download
6
+
7
+ # download is stored to /tmp before processing
8
+ TEMPDIR = "/tmp"
9
+ # download format is forced to mp4 to optimize for quickness
10
+ FORMAT_ARG = "-f 'best[ext=mp4]'"
11
+
12
+ # Download the given video
13
+ # @param [Video::Instance] video
14
+ # @param [Hash] options
15
+ # @return [Download]
16
+ def self.process(video, options = {})
17
+ download = new(video)
18
+ download.process(options)
19
+ download
20
+ end
21
+
22
+ # @param [Video::Instance] video
23
+ def initialize(video)
24
+ @video = video
25
+ @video.download = self if @video.download.nil?
26
+ end
27
+
28
+ # Download the video file
29
+ # @param [Hash] options
30
+ # @return [Boolean]
31
+ def process(options = {})
32
+ result = Kernel.system(command_line)
33
+ raise(result.to_s) unless result
34
+ true
35
+ end
36
+
37
+ private
38
+
39
+ # Command line to download the video file
40
+ # @return [String]
41
+ def command_line
42
+ "youtube-dl #{@video.source_url} #{FORMAT_ARG} -o '#{TEMPDIR}/#{@video.id}s.%(ext)s'"
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,62 @@
1
+ module Viddl
2
+
3
+ module Video
4
+
5
+ class Instance
6
+
7
+ attr_accessor :download
8
+ attr_reader :id, :source_url
9
+
10
+ # @param [String] url The url of the video source
11
+ def initialize(url)
12
+ @source_url = url
13
+
14
+ populate_id
15
+ end
16
+
17
+ # Cut the video source using the given options
18
+ # @param [Hash] options
19
+ # @option options [Boolean] :audio Whether to include audio
20
+ # @option options [Numeric] :start Time in the source file where the clip starts
21
+ # @option options [Numeric] :duration Duration of the clip
22
+ # @option options [Numeric] :end Time in the source file where the clip ends
23
+ # @option options [Integer, String] :width The desired width to resize to
24
+ # @option options [Integer, String] :height The desired height to resize to
25
+ # @option options [Hash] :crop The desired crop parameters (:x, :y, :width, :height)
26
+ # @return [Array<Clip>]
27
+ def create_clip(options = {})
28
+ source_filenames.map do |filename|
29
+ Clip.process(filename, options)
30
+ end
31
+ end
32
+
33
+ # Download the video source
34
+ # @param [Hash] options
35
+ # @return [Download]
36
+ def process_download(options = {})
37
+ @download = Download.process(self, options)
38
+ end
39
+
40
+ # The downloaded source filenames
41
+ # @return [Array<String>]
42
+ def source_filenames
43
+ if @download.nil?
44
+ raise "File must be downloaded"
45
+ else
46
+ @source_filenames = Dir["#{Download::TEMPDIR}/#{@id}*"]
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # The video instance id
53
+ # @return [String]
54
+ def populate_id
55
+ @id = @source_url.scan(/youtube.com\/watch\?v\=(\S*)&?/).flatten.first
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.join("..", "lib"))
2
+
3
+ require "rspec"
4
+ require "viddl"
5
+
6
+ #RSpec.configure do |c|
7
+ # c.mock_with :rspec
8
+ #end
@@ -0,0 +1,77 @@
1
+ require "helper"
2
+
3
+ describe Viddl::System do
4
+
5
+ context ".validate" do
6
+
7
+ context "passes" do
8
+
9
+ it "execs command line" do
10
+ expect(Kernel).to(receive(:system).twice.and_return(true))
11
+ @result = Viddl::System.send(:validate)
12
+ end
13
+
14
+ end
15
+
16
+ context "fails" do
17
+
18
+ it "raises" do
19
+ expect(Kernel).to(receive(:system).and_return(nil))
20
+ expect {
21
+ Viddl::System.send(:validate)
22
+ }.to(raise_error(RuntimeError))
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ context ".validate_ffmpeg" do
30
+
31
+ context "passes" do
32
+
33
+ it "execs command line" do
34
+ expect(Kernel).to(receive(:system).and_return(true))
35
+ @result = Viddl::System.send(:validate_ffmpeg)
36
+ end
37
+
38
+ end
39
+
40
+ context "fails" do
41
+
42
+ it "raises" do
43
+ expect(Kernel).to(receive(:system).and_return(nil))
44
+ expect {
45
+ Viddl::System.send(:validate_ffmpeg)
46
+ }.to(raise_error(RuntimeError))
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ context ".validate_youtube_dl" do
54
+
55
+ context "passes" do
56
+
57
+ it "execs command line" do
58
+ expect(Kernel).to(receive(:system).and_return(true))
59
+ @result = Viddl::System.send(:validate_youtube_dl)
60
+ end
61
+
62
+ end
63
+
64
+ context "fails" do
65
+
66
+ it "raises" do
67
+ expect(Kernel).to(receive(:system).and_return(nil))
68
+ expect {
69
+ Viddl::System.send(:validate_youtube_dl)
70
+ }.to(raise_error(RuntimeError))
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,81 @@
1
+ require "helper"
2
+
3
+ describe Viddl::Video::Clip::Audio do
4
+
5
+ context ".options_formatted" do
6
+
7
+ context "with no options" do
8
+
9
+ it "returns audio = true" do
10
+ options = {}
11
+ opts = Viddl::Video::Clip::Audio.send(:options_formatted, options)
12
+ expect(opts).to(include(audio: true))
13
+ end
14
+
15
+ end
16
+
17
+ context "with audio = false" do
18
+
19
+ it "returns audio = false" do
20
+ options = {
21
+ audio: false
22
+ }
23
+ opts = Viddl::Video::Clip::Audio.send(:options_formatted, options)
24
+ expect(opts).to(include(audio: false))
25
+ end
26
+
27
+ end
28
+
29
+ context "with audio = true" do
30
+
31
+ it "returns audio = true" do
32
+ options = {
33
+ audio: true
34
+ }
35
+ opts = Viddl::Video::Clip::Audio.send(:options_formatted, options)
36
+ expect(opts).to(include(audio: true))
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ context ".args" do
44
+
45
+ context "with no options" do
46
+
47
+ it "return blank string" do
48
+ options = {}
49
+ args = Viddl::Video::Clip::Audio.send(:args, options)
50
+ expect(args).to(be_nil)
51
+ end
52
+
53
+ end
54
+
55
+ context "with audio=false" do
56
+
57
+ it "turns off audio" do
58
+ options = {
59
+ audio: false
60
+ }
61
+ args = Viddl::Video::Clip::Audio.send(:args, options)
62
+ expect(args).to(eq("-an"))
63
+ end
64
+
65
+ end
66
+
67
+ context "with audio=true" do
68
+
69
+ it "returns blank string" do
70
+ options = {
71
+ audio: true
72
+ }
73
+ args = Viddl::Video::Clip::Audio.send(:args, options)
74
+ expect(args).to(be_nil)
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end