wgif 0.2.0 → 0.3.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.
@@ -1,27 +1,10 @@
1
1
  module WGif
2
-
3
- class InvalidUrlException < Exception
4
- end
5
-
6
- class InvalidTimestampException < Exception
7
- end
8
-
9
- class InvalidDurationException < Exception
10
- end
11
-
12
- class InvalidFramesException < Exception
13
- end
14
-
15
- class MissingOutputFileException < Exception
16
- end
17
-
18
- class VideoNotFoundException < Exception
19
- end
20
-
21
- class ClipEncodingException < Exception
22
- end
23
-
24
- class ImgurException < Exception
25
- end
26
-
2
+ InvalidUrlException = Class.new(Exception)
3
+ InvalidTimestampException = Class.new(Exception)
4
+ InvalidDurationException = Class.new(Exception)
5
+ InvalidFramesException = Class.new(Exception)
6
+ MissingOutputFileException = Class.new(Exception)
7
+ VideoNotFoundException = Class.new(Exception)
8
+ ClipEncodingException = Class.new(Exception)
9
+ ImgurException = Class.new(Exception)
27
10
  end
@@ -12,7 +12,9 @@ module WGif
12
12
 
13
13
  def resize(image, dimensions)
14
14
  image.each do |frame|
15
- frame.change_geometry(dimensions) { |cols, rows, img| img.resize!(cols, rows) }
15
+ frame.change_geometry(dimensions) do |cols, rows, img|
16
+ img.resize!(cols, rows)
17
+ end
16
18
  end
17
19
  end
18
20
  end
@@ -6,7 +6,7 @@ module WGif
6
6
 
7
7
  def run
8
8
  if dependencies_installed?
9
- puts "All dependencies are installed. Go make a GIF."
9
+ puts 'All dependencies are installed. Go make a gif.'
10
10
  Kernel.exit 0
11
11
  end
12
12
  if homebrew_installed?
@@ -21,7 +21,7 @@ module WGif
21
21
  end
22
22
 
23
23
  def dependencies_installed?
24
- DEPENDENCIES.map {|_, binary| installed?(binary)}.inject(:&)
24
+ DEPENDENCIES.map { |_, binary| installed?(binary) }.inject(:&)
25
25
  end
26
26
 
27
27
  def homebrew_installed?
@@ -39,6 +39,5 @@ module WGif
39
39
  def installed?(binary)
40
40
  Kernel.system "which #{binary} > /dev/null"
41
41
  end
42
-
43
42
  end
44
43
  end
data/lib/wgif/uploader.rb CHANGED
@@ -14,10 +14,13 @@ module WGif
14
14
  def upload(filename)
15
15
  File.open(filename, 'r') do |file|
16
16
  response = Typhoeus.post(UPLOAD_ENDPOINT,
17
- body: {image: file},
17
+ body: { image: file },
18
18
  headers: auth_header)
19
- raise WGif::ImgurException, error_message(response) unless response.success?
20
- image_url(response)
19
+ if response.success?
20
+ image_url(response)
21
+ else
22
+ fail WGif::ImgurException, error_message(response)
23
+ end
21
24
  end
22
25
  end
23
26
 
@@ -32,7 +35,7 @@ module WGif
32
35
  end
33
36
 
34
37
  def auth_header
35
- {Authorization: "Client-ID #{@client_id}"}
38
+ { Authorization: "Client-ID #{@client_id}" }
36
39
  end
37
40
  end
38
41
  end
data/lib/wgif/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module WGif
2
- VERSION = "0.2.0"
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/wgif/video.rb CHANGED
@@ -5,25 +5,25 @@ module WGif
5
5
  class Video
6
6
  attr_accessor :name, :clip, :logger
7
7
 
8
- def initialize name, filepath
8
+ def initialize(name, filepath)
9
9
  @name = name
10
10
  @clip = FFMPEG::Movie.new(filepath)
11
- FileUtils.mkdir_p "/tmp/wgif/"
11
+ FileUtils.mkdir_p '/tmp/wgif/'
12
12
  @logger = Logger.new("/tmp/wgif/#{name}.log")
13
13
  FFMPEG.logger = @logger
14
14
  end
15
15
 
16
- def trim start_timestamp, duration
16
+ def trim(start_timestamp, duration)
17
17
  options = {
18
- audio_codec: "copy",
19
- video_codec: "copy",
18
+ audio_codec: 'copy',
19
+ video_codec: 'copy',
20
20
  custom: "-ss #{start_timestamp} -t 00:00:#{'%06.3f' % duration}"
21
21
  }
22
- trimmed = transcode(@clip, "/tmp/wgif/#{@name}-clip.mov", options)
22
+ transcode(@clip, "/tmp/wgif/#{@name}-clip.mov", options)
23
23
  WGif::Video.new "#{@name}-clip", "/tmp/wgif/#{@name}-clip.mov"
24
24
  end
25
25
 
26
- def to_frames(options={})
26
+ def to_frames(options = {})
27
27
  make_frame_dir
28
28
  if options[:frames]
29
29
  framerate = options[:frames] / @clip.duration
@@ -37,22 +37,23 @@ module WGif
37
37
  private
38
38
 
39
39
  def make_frame_dir
40
- FileUtils.rm Dir.glob("/tmp/wgif/frames/*.png")
41
- FileUtils.mkdir_p "/tmp/wgif/frames"
40
+ FileUtils.rm Dir.glob('/tmp/wgif/frames/*.png')
41
+ FileUtils.mkdir_p '/tmp/wgif/frames'
42
42
  end
43
43
 
44
44
  def open_frame_dir
45
- Dir.glob("/tmp/wgif/frames/*.png")
45
+ Dir.glob('/tmp/wgif/frames/*.png')
46
46
  end
47
47
 
48
48
  def transcode(clip, file, options)
49
- begin
50
- clip.transcode(file, options)
51
- rescue FFMPEG::Error => error
52
- raise WGif::ClipEncodingException unless error.message.include? "no output file created"
53
- raise WGif::ClipEncodingException if error.message.include? "Invalid data found when processing input"
49
+ clip.transcode(file, options)
50
+ rescue FFMPEG::Error => error
51
+ unless error.message.include? 'no output file created'
52
+ raise WGif::ClipEncodingException
53
+ end
54
+ if error.message.include? 'Invalid data found when processing input'
55
+ raise WGif::ClipEncodingException
54
56
  end
55
57
  end
56
-
57
58
  end
58
59
  end
@@ -9,10 +9,7 @@ module WGif
9
9
 
10
10
  def get(video_id)
11
11
  path = "/tmp/wgif/#{video_id}"
12
- if Pathname.new(path).exist?
13
- WGif::Video.new(video_id, path)
14
- end
12
+ WGif::Video.new(video_id, path) if Pathname.new(path).exist?
15
13
  end
16
-
17
14
  end
18
15
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'wgif/cli'
3
+
4
+ describe 'empty image list bug', integration: true do
5
+ it 'throws an empty image list error' do
6
+ args = ['https://www.youtube.com/watch?v=deFDlB8RiNg',
7
+ 'fish_grease.gif',
8
+ '--start',
9
+ '00:00:13',
10
+ '-d',
11
+ '3',
12
+ '-f',
13
+ '20',
14
+ '--width',
15
+ '350']
16
+ WGif::CLI.new.make_gif(args)
17
+ end
18
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'rspec'
2
2
  require 'simplecov'
3
+ require 'codeclimate-test-reporter'
4
+
5
+ CodeClimate::TestReporter.start
3
6
 
4
7
  if ENV['COVERAGE'] == 'true'
5
8
  SimpleCov.start do
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+ require 'wgif/argument_parser'
3
+
4
+ describe WGif::ArgumentParser do
5
+ let(:parser) { described_class.new }
6
+
7
+ it 'parses a URL from command line args' do
8
+ args = parser.parse_args ['http://example.com']
9
+ args[:url].should eq('http://example.com')
10
+ end
11
+
12
+ it 'starts at 0s by default' do
13
+ args = parser.parse_args ['http://example.com']
14
+ args[:trim_from].should eq('00:00:00')
15
+ end
16
+
17
+ it 'trims parserps to 1s by default' do
18
+ args = parser.parse_args ['http://example.com']
19
+ args[:duration].should eq(1)
20
+ end
21
+
22
+ it 'parses the short frame count option' do
23
+ options = parser.parse_options ['-f', '40']
24
+ options[:frames].should eq(40)
25
+ end
26
+
27
+ it 'parses the long frame count option' do
28
+ options = parser.parse_options ['--frames', '40']
29
+ options[:frames].should eq(40)
30
+ end
31
+
32
+ it 'parses the short start time option' do
33
+ options = parser.parse_options ['-s', '00:00:05']
34
+ options[:trim_from].should eq('00:00:05')
35
+ end
36
+
37
+ it 'parses the long start time option' do
38
+ options = parser.parse_options ['--start', '00:00:05']
39
+ options[:trim_from].should eq('00:00:05')
40
+ end
41
+
42
+ it 'parses the short duration option' do
43
+ options = parser.parse_options ['-d', '1.43']
44
+ options[:duration].should eq(1.43)
45
+ end
46
+
47
+ it 'parses the long duration option' do
48
+ options = parser.parse_options ['--duration', '5.3']
49
+ options[:duration].should eq(5.3)
50
+ end
51
+
52
+ it 'parses the short dimensions option' do
53
+ options = parser.parse_options ['-w', '400']
54
+ expect(options[:dimensions]).to eq('400')
55
+ end
56
+
57
+ it 'parses the long dimensions option' do
58
+ options = parser.parse_options ['--width', '300']
59
+ expect(options[:dimensions]).to eq('300')
60
+ end
61
+
62
+ it 'parses the short upload option' do
63
+ options = parser.parse_options ['-u']
64
+ expect(options[:upload]).to eq(true)
65
+ end
66
+
67
+ it 'parses the long upload option' do
68
+ options = parser.parse_options ['--upload']
69
+ expect(options[:upload]).to eq(true)
70
+ end
71
+
72
+ it 'parses the short preview option' do
73
+ options = parser.parse_options ['-p']
74
+ expect(options[:preview]).to eq(true)
75
+ end
76
+
77
+ it 'parses the long preview option' do
78
+ options = parser.parse_options ['--preview']
79
+ expect(options[:preview]).to eq(true)
80
+ end
81
+
82
+ it 'handles args in wacky order' do
83
+ args = parser.parse_args([
84
+ '-d',
85
+ '1.5',
86
+ '--upload',
87
+ 'http://example.com',
88
+ '--frames',
89
+ '60',
90
+ '-p',
91
+ 'my-great-gif.gif',
92
+ '-s',
93
+ '00:00:05'
94
+ ])
95
+
96
+ expect(args).to eq(url: 'http://example.com',
97
+ trim_from: '00:00:05',
98
+ duration: 1.5,
99
+ frames: 60,
100
+ output: 'my-great-gif.gif',
101
+ dimensions: '480',
102
+ upload: true,
103
+ preview: true)
104
+ end
105
+
106
+ context 'validating args' do
107
+
108
+ it 'checks for a missing output file' do
109
+ args = parser.parse_args(['http://example.com'])
110
+ expect { parser.validate_args args }
111
+ .to raise_error(WGif::MissingOutputFileException)
112
+ end
113
+
114
+ it 'checks for an invalid URL' do
115
+ args = parser.parse_args(['crazy nonsense', 'output.gif'])
116
+ expect { parser.validate_args args }
117
+ .to raise_error(WGif::InvalidUrlException)
118
+ end
119
+
120
+ it 'checks for an invalid timestamp' do
121
+ args = parser.parse_args([
122
+ 'http://lol.wut',
123
+ 'output.gif',
124
+ '-s',
125
+ 'rofl'
126
+ ])
127
+ expect { parser.validate_args args }
128
+ .to raise_error(WGif::InvalidTimestampException)
129
+ end
130
+
131
+ it 'returns true when args are OK' do
132
+ args = parser.parse_args([
133
+ 'https://crazynonsense.info',
134
+ 'output.gif'
135
+ ])
136
+ expect { parser.validate_args args }.not_to raise_error
137
+ end
138
+ end
139
+
140
+ it 'parses and validates' do
141
+ expect { parser.parse(['http://lol.wut']) }
142
+ .to raise_error(WGif::MissingOutputFileException)
143
+ end
144
+
145
+ it 'returns parsed arguments' do
146
+ args = parser.parse(['http://lol.wut', 'out.gif'])
147
+ expect(args).to eq({dimensions: '480',
148
+ duration: 1.0,
149
+ output: 'out.gif',
150
+ trim_from: '00:00:00',
151
+ url: 'http://lol.wut'})
152
+ end
153
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'wgif/cli'
3
+
4
+ describe WGif::CLI do
5
+ let(:cli) { described_class.new }
6
+
7
+ context 'error handling' do
8
+
9
+ before do
10
+ @mock_stdout = StringIO.new
11
+ @real_stdout, $stdout = $stdout, @mock_stdout
12
+ end
13
+
14
+ after do
15
+ $stdout = @real_stdout
16
+ end
17
+
18
+ def expect_help_with_message(out, message)
19
+ expect(out).to include(message)
20
+ expect(out)
21
+ .to include('Usage: wgif [YouTube URL] [output file] [options]')
22
+ cli.argument_parser.argument_summary.each do |help_info|
23
+ expect(out).to include(help_info)
24
+ end
25
+ expect(out).to include('Example:')
26
+ end
27
+
28
+ it 'catches invalid URLs' do
29
+ WGif::ArgumentParser.any_instance.stub(:parse)
30
+ .and_raise(WGif::InvalidUrlException)
31
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
32
+ message = 'That looks like an invalid URL. Check the syntax.'
33
+ expect_help_with_message(@mock_stdout.string, message)
34
+ end
35
+
36
+ it 'catches invalid timestamps' do
37
+ WGif::ArgumentParser.any_instance.stub(:parse)
38
+ .and_raise(WGif::InvalidTimestampException)
39
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
40
+ message = 'That looks like an invalid timestamp. Check the syntax.'
41
+ expect_help_with_message(@mock_stdout.string, message)
42
+ end
43
+
44
+ it 'catches missing output args' do
45
+ WGif::ArgumentParser.any_instance.stub(:parse)
46
+ .and_raise(WGif::MissingOutputFileException)
47
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
48
+ message = 'Please specify an output file.'
49
+ expect_help_with_message(@mock_stdout.string, message)
50
+ end
51
+
52
+ it 'catches missing videos' do
53
+ WGif::ArgumentParser.any_instance.stub(:parse)
54
+ .and_raise(WGif::VideoNotFoundException)
55
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
56
+ message = "WGif can't find a valid YouTube video at that URL."
57
+ expect_help_with_message(@mock_stdout.string, message)
58
+ end
59
+
60
+ it 'catches encoding exceptions' do
61
+ WGif::ArgumentParser.any_instance.stub(:parse)
62
+ .and_raise(WGif::ClipEncodingException)
63
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
64
+ message = 'WGif encountered an error transcoding the video.'
65
+ expect_help_with_message(@mock_stdout.string, message)
66
+ end
67
+
68
+ it 'catches upload errors' do
69
+ WGif::ArgumentParser.any_instance.stub(:parse)
70
+ .and_raise(WGif::ImgurException, 'Imgur error')
71
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
72
+ expect_help_with_message(@mock_stdout.string, 'Imgur error')
73
+ end
74
+
75
+ it 'raises SystemExit when thrown' do
76
+ WGif::ArgumentParser.any_instance.stub(:parse).and_raise(SystemExit)
77
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
78
+ end
79
+
80
+ it 'Prints the backtrace for all other exceptions' do
81
+ exception = StandardError.new 'crazy error'
82
+ WGif::ArgumentParser.any_instance.stub(:parse).and_raise(exception)
83
+ expect { cli.make_gif([]) }.to raise_error(SystemExit)
84
+ message = 'Something went wrong creating your GIF. The details:'
85
+ expect_help_with_message(@mock_stdout.string, message)
86
+ expect(@mock_stdout.string).to include('Please open an issue')
87
+ expect(@mock_stdout.string).to include("#{exception}")
88
+ expect(@mock_stdout.string).to include(exception.backtrace.join("\n"))
89
+ end
90
+
91
+ it 'prints help information' do
92
+ expect { cli.make_gif(['-h']) }.to raise_error(SystemExit)
93
+ cli.argument_parser.argument_summary.each do |help_info|
94
+ expect(@mock_stdout.string).to include(help_info)
95
+ end
96
+ end
97
+ end
98
+ end