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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +27 -10
- data/Rakefile +8 -3
- data/lib/wgif.rb +10 -10
- data/lib/wgif/argument_parser.rb +94 -0
- data/lib/wgif/cli.rb +31 -87
- data/lib/wgif/download_bar.rb +0 -1
- data/lib/wgif/downloader.rb +23 -27
- data/lib/wgif/exceptions.rb +8 -25
- data/lib/wgif/gif_maker.rb +3 -1
- data/lib/wgif/installer.rb +2 -3
- data/lib/wgif/uploader.rb +7 -4
- data/lib/wgif/version.rb +1 -1
- data/lib/wgif/video.rb +17 -16
- data/lib/wgif/video_cache.rb +1 -4
- data/spec/integration/empty_image_list_spec.rb +18 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/wgif/argument_parser_spec.rb +153 -0
- data/spec/unit/wgif/cli_spec.rb +98 -0
- data/spec/{wgif → unit/wgif}/download_bar_spec.rb +0 -0
- data/spec/{wgif → unit/wgif}/downloader_spec.rb +14 -9
- data/spec/{wgif → unit/wgif}/gif_maker_spec.rb +0 -0
- data/spec/{wgif → unit/wgif}/installer_spec.rb +45 -35
- data/spec/unit/wgif/uploader_spec.rb +48 -0
- data/spec/{wgif → unit/wgif}/video_cache_spec.rb +0 -0
- data/spec/{wgif → unit/wgif}/video_spec.rb +27 -22
- data/wgif.gemspec +22 -22
- metadata +37 -18
- data/spec/wgif/cli_spec.rb +0 -207
- data/spec/wgif/uploader_spec.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eda7edccc313c6f58f2bb806200b69011f1904c1
|
4
|
+
data.tar.gz: a2197e273c936b595a8b498e0c43deb43527ef23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e08a6c50358d79233771a2202991d0ed46eeffdfe723911b95dc97880a8583a407c62e7d3a24a15cb8aa53b9e7eb91d824e7c94e4b8717bf5c935f645525c92e
|
7
|
+
data.tar.gz: d8af1629380d458440a01bd496310bffc597892ec3f68b0ebdec8cc7af2712241b9ffb833ac4f93577a232a06a99382bf36c0f1b3d39de32cf3e527f3676ba33
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# WGif
|
2
2
|
[](https://travis-ci.org/ecmendenhall/wgif)
|
3
|
+
[](https://codeclimate.com/github/ecmendenhall/wgif)
|
3
4
|
|
4
5
|
WGif is a command line tool for creating animated GIFs from YouTube videos.
|
5
6
|
|
@@ -7,11 +8,13 @@ WGif is a command line tool for creating animated GIFs from YouTube videos.
|
|
7
8
|
```
|
8
9
|
Usage: wgif [YouTube URL] [output file] [options]
|
9
10
|
|
10
|
-
-f, --frames N
|
11
|
-
-s, --start HH:MM:SS
|
12
|
-
-d, --duration seconds
|
13
|
-
-
|
14
|
-
-
|
11
|
+
-f, --frames N Number of frames in the final gif. (Default 20)
|
12
|
+
-s, --start HH:MM:SS Start creating gif from input video at this timestamp. (Default 00:00:00)
|
13
|
+
-d, --duration seconds Number of seconds of input video to capture. (Default 1)
|
14
|
+
-w, --width pixels Width of the gif in pixels. (Default 480px)
|
15
|
+
-u, --upload Upload finished gif to Imgur
|
16
|
+
-p, --preview Preview finished gif with Quick Look
|
17
|
+
-h, --help Print help information.
|
15
18
|
|
16
19
|
Example:
|
17
20
|
|
@@ -34,7 +37,7 @@ $ gem build wgif.gemspec
|
|
34
37
|
and
|
35
38
|
|
36
39
|
```sh
|
37
|
-
$ gem install wgif-0.
|
40
|
+
$ gem install wgif-0.3.0.gem
|
38
41
|
```
|
39
42
|
|
40
43
|
to install the executable.
|
@@ -72,14 +75,24 @@ total number of frames in the finished GIF. This defaults to 20, so let's drop a
|
|
72
75
|
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif --start 00:03:30 -d 2 -f 18
|
73
76
|
```
|
74
77
|
|
75
|
-
|
76
|
-
|
78
|
+
To preview the output in a Quick Look window, add the `--preview` flag:
|
79
|
+
|
80
|
+
```sh
|
81
|
+
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif --start 00:03:30 -d 2 -f 18 --preview
|
82
|
+
```
|
83
|
+
|
84
|
+
You'll see a preview pop up in a Quick Look window like this one:
|
85
|
+
|
86
|
+

|
87
|
+
|
88
|
+
Dropping frames shrunk the file to 2.2 megabytes, but it's still not small enough to post on my Sugarcubes fan-Tumblr.
|
89
|
+
Let's scale it down a little with the `-w` or `--width` flag:
|
77
90
|
|
78
91
|
```sh
|
79
92
|
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif --start 00:03:30 -d 2 -f 18 --width 350
|
80
93
|
```
|
81
94
|
|
82
|
-
And finally, now that everything's completed add the `--upload` flag to automatically post it to Imgur:
|
95
|
+
And finally, now that everything's completed, let's add the `--upload` flag to automatically post it to Imgur:
|
83
96
|
|
84
97
|
```sh
|
85
98
|
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif --start 00:03:30 -d 2 -f 18 --width 350 --upload
|
@@ -88,12 +101,16 @@ Finished. GIF uploaded to Imgur at http://i.imgur.com/iA28DuR.gif
|
|
88
101
|
|
89
102
|
And here it is:
|
90
103
|
|
91
|
-

|
92
105
|
### "You shouldn't let poets lie to you."
|
93
106
|
|
94
107
|
## Changes
|
108
|
+
- v0.3.0, 2014/4/20: Add Quick Look preview with `--preview` flag.
|
95
109
|
- v0.2.0, 2014/4/11: Add automatic upload to Imgur with `--upload` flag.
|
96
110
|
|
111
|
+
## Contributors
|
112
|
+
Thanks to [arlandism](https://github.com/arlandism) and [ellie007](https://github.com/ellie007) for pairing on Imgur uploads.
|
113
|
+
|
97
114
|
## Contributions
|
98
115
|
Are welcome via pull request.
|
99
116
|
|
data/Rakefile
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
|
4
|
-
RSpec::Core::RakeTask.new(:spec)
|
5
|
-
|
4
|
+
RSpec::Core::RakeTask.new(:spec, :tag) do |t, task_args|
|
5
|
+
unless task_args[:tag] == 'integration'
|
6
|
+
t.rspec_opts = "--tag ~integration"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
task default: :spec
|
data/lib/wgif.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
1
|
+
require 'wgif/cli'
|
2
|
+
require 'wgif/download_bar'
|
3
|
+
require 'wgif/downloader'
|
4
|
+
require 'wgif/exceptions'
|
5
|
+
require 'wgif/gif_maker'
|
6
|
+
require 'wgif/installer'
|
7
|
+
require 'wgif/uploader'
|
8
|
+
require 'wgif/version'
|
9
|
+
require 'wgif/video'
|
10
|
+
require 'wgif/video_cache'
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'wgif/exceptions'
|
3
|
+
|
4
|
+
module WGif
|
5
|
+
class ArgumentParser
|
6
|
+
|
7
|
+
URL = %r{\Ahttps?://.*\z}
|
8
|
+
TIMESTAMP = /\A\d{1,2}(?::\d{2})+(?:\.\d*)?\z/
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@options = {}
|
12
|
+
@defaults = {
|
13
|
+
trim_from: '00:00:00',
|
14
|
+
duration: 1.0,
|
15
|
+
dimensions: '480'
|
16
|
+
}
|
17
|
+
@parser = OptionParser.new do |opts|
|
18
|
+
opts.on('-f N',
|
19
|
+
'--frames N',
|
20
|
+
'Number of frames in the final gif. (Default 20)') {
|
21
|
+
|n| @options[:frames] = n.to_i
|
22
|
+
}
|
23
|
+
opts.on('-s HH:MM:SS',
|
24
|
+
'--start HH:MM:SS',
|
25
|
+
'Start creating gif from input video at this timestamp. (Default 00:00:00)') {
|
26
|
+
|ts| @options[:trim_from] = ts
|
27
|
+
}
|
28
|
+
opts.on('-d seconds',
|
29
|
+
'--duration seconds',
|
30
|
+
'Number of seconds of input video to capture. (Default 1)') {
|
31
|
+
|d| @options[:duration] = d.to_f
|
32
|
+
}
|
33
|
+
opts.on('-w pixels',
|
34
|
+
'--width pixels',
|
35
|
+
'Width of the gif in pixels. (Default 480px)') {
|
36
|
+
|gs| @options[:dimensions] = gs
|
37
|
+
}
|
38
|
+
opts.on('-u',
|
39
|
+
'--upload',
|
40
|
+
'Upload finished gif to Imgur') {
|
41
|
+
|u| @options[:upload] = u
|
42
|
+
}
|
43
|
+
opts.on('-p',
|
44
|
+
'--preview',
|
45
|
+
'Preview finished gif with Quick Look') {
|
46
|
+
|p| @options[:preview] = p
|
47
|
+
}
|
48
|
+
opts.on_tail('-h',
|
49
|
+
'--help',
|
50
|
+
'Print help information.') {
|
51
|
+
print_help
|
52
|
+
exit
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse(args)
|
58
|
+
options = parse_args(args)
|
59
|
+
validate_args(options)
|
60
|
+
options
|
61
|
+
end
|
62
|
+
|
63
|
+
def argument_summary
|
64
|
+
@parser.summarize
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_args(args)
|
68
|
+
options = @defaults.merge(parse_options args)
|
69
|
+
options.merge(url: args[0], output: args[1])
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_options(args)
|
73
|
+
@parser.parse! args
|
74
|
+
@options
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate_args(args)
|
78
|
+
fail WGif::InvalidUrlException unless args[:url] =~ URL
|
79
|
+
fail WGif::InvalidTimestampException unless args[:trim_from] =~ TIMESTAMP
|
80
|
+
fail WGif::MissingOutputFileException unless args[:output]
|
81
|
+
end
|
82
|
+
|
83
|
+
def print_help
|
84
|
+
puts 'Usage: wgif [YouTube URL] [output file] [options]', "\n"
|
85
|
+
puts argument_summary, "\n"
|
86
|
+
puts <<-example
|
87
|
+
Example:
|
88
|
+
|
89
|
+
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif -s 00:03:30 -d 2 -w 400 --upload
|
90
|
+
|
91
|
+
example
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/wgif/cli.rb
CHANGED
@@ -1,114 +1,70 @@
|
|
1
|
-
require '
|
1
|
+
require 'wgif/argument_parser'
|
2
2
|
require 'wgif/exceptions'
|
3
3
|
require 'wgif/installer'
|
4
4
|
|
5
5
|
module WGif
|
6
6
|
class CLI
|
7
7
|
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :argument_parser
|
9
9
|
|
10
10
|
def initialize
|
11
|
-
@
|
12
|
-
|
13
|
-
trim_from: '00:00:00',
|
14
|
-
duration: 1.0,
|
15
|
-
dimensions: '480'
|
16
|
-
}
|
17
|
-
@parser = OptionParser.new do |opts|
|
18
|
-
opts.on('-f N',
|
19
|
-
'--frames N',
|
20
|
-
'Number of frames in the final gif. (Default 20)') {
|
21
|
-
|n| @options[:frames] = n.to_i
|
22
|
-
}
|
23
|
-
opts.on('-s HH:MM:SS',
|
24
|
-
'--start HH:MM:SS',
|
25
|
-
'Start creating gif from input video at this timestamp. (Default 00:00:00)') {
|
26
|
-
|ts| @options[:trim_from] = ts
|
27
|
-
}
|
28
|
-
opts.on('-d seconds',
|
29
|
-
'--duration seconds',
|
30
|
-
'Number of seconds of input video to capture. (Default 5)') {
|
31
|
-
|d| @options[:duration] = d.to_f
|
32
|
-
}
|
33
|
-
opts.on('-w pixels',
|
34
|
-
'--width pixels',
|
35
|
-
'Width of the gif in pixels. (Default 500px)') {
|
36
|
-
|gs| @options[:dimensions] = gs
|
37
|
-
}
|
38
|
-
opts.on('-u',
|
39
|
-
'--upload',
|
40
|
-
'Upload finished GIF to Imgur') {
|
41
|
-
|u| @options[:upload] = !!u
|
42
|
-
}
|
11
|
+
@argument_parser = WGif::ArgumentParser.new
|
12
|
+
end
|
43
13
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
14
|
+
def make_gif(cli_args)
|
15
|
+
WGif::Installer.new.run if cli_args[0] == 'install'
|
16
|
+
load_dependencies
|
17
|
+
rescue_errors do
|
18
|
+
args = @argument_parser.parse(cli_args)
|
19
|
+
frames = convert_video(args)
|
20
|
+
GifMaker.new.make_gif(frames, args[:output], args[:dimensions])
|
21
|
+
upload(args) if args[:upload]
|
22
|
+
preview(args) if args[:preview]
|
50
23
|
end
|
51
24
|
end
|
52
25
|
|
53
|
-
|
54
|
-
|
55
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def preview(args)
|
29
|
+
Kernel.system "qlmanage -p #{args[:output]} &>/dev/null"
|
56
30
|
end
|
57
31
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
32
|
+
def upload(args)
|
33
|
+
url = Uploader.new('d2321b02db7ba15').upload(args[:output])
|
34
|
+
puts "Finished. GIF uploaded to Imgur at #{url}"
|
61
35
|
end
|
62
36
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
37
|
+
def convert_video(args)
|
38
|
+
video = Downloader.new.get_video(args[:url])
|
39
|
+
clip = video.trim(args[:trim_from], args[:duration])
|
40
|
+
clip.to_frames(frames: args[:frames])
|
67
41
|
end
|
68
42
|
|
69
|
-
def
|
70
|
-
WGif::Installer.new.run if cli_args[0] == 'install'
|
43
|
+
def load_dependencies
|
71
44
|
require 'wgif/downloader'
|
72
45
|
require 'wgif/gif_maker'
|
73
46
|
require 'wgif/uploader'
|
74
|
-
rescue_errors do
|
75
|
-
args = parse_args cli_args
|
76
|
-
validate_args(args)
|
77
|
-
video = Downloader.new.get_video(args[:url])
|
78
|
-
clip = video.trim(args[:trim_from], args[:duration])
|
79
|
-
frames = clip.to_frames(frames: args[:frames])
|
80
|
-
GifMaker.new.make_gif(frames, args[:output], args[:dimensions])
|
81
|
-
if args[:upload]
|
82
|
-
url = Uploader.new('d2321b02db7ba15').upload(args[:output])
|
83
|
-
puts "Finished. GIF uploaded to Imgur at #{url}"
|
84
|
-
end
|
85
|
-
end
|
86
47
|
end
|
87
48
|
|
88
|
-
private
|
89
|
-
|
90
49
|
def rescue_errors
|
91
|
-
|
92
|
-
yield
|
50
|
+
yield
|
93
51
|
rescue WGif::InvalidUrlException
|
94
|
-
print_error
|
52
|
+
print_error 'That looks like an invalid URL. Check the syntax.'
|
95
53
|
rescue WGif::InvalidTimestampException
|
96
|
-
print_error
|
54
|
+
print_error 'That looks like an invalid timestamp. Check the syntax.'
|
97
55
|
rescue WGif::MissingOutputFileException
|
98
56
|
print_error 'Please specify an output file.'
|
99
57
|
rescue WGif::VideoNotFoundException
|
100
58
|
print_error "WGif can't find a valid YouTube video at that URL."
|
101
59
|
rescue WGif::ClipEncodingException
|
102
|
-
print_error
|
60
|
+
print_error 'WGif encountered an error transcoding the video.'
|
103
61
|
rescue WGif::ImgurException => e
|
104
62
|
print_error <<-error
|
105
63
|
WGif couldn't upload your GIF to Imgur. The Imgur error was:
|
106
64
|
|
107
65
|
#{e}
|
108
66
|
error
|
109
|
-
rescue
|
110
|
-
raise e
|
111
|
-
rescue Exception => e
|
67
|
+
rescue StandardError => e
|
112
68
|
print_error <<-error
|
113
69
|
Something went wrong creating your GIF. The details:
|
114
70
|
|
@@ -117,25 +73,13 @@ Something went wrong creating your GIF. The details:
|
|
117
73
|
|
118
74
|
Please open an issue at: https://github.com/ecmendenhall/wgif/issues/new
|
119
75
|
error
|
120
|
-
end
|
121
76
|
end
|
122
77
|
|
123
78
|
def print_error(message)
|
124
79
|
puts message, "\n"
|
125
|
-
print_help
|
80
|
+
@argument_parser.print_help
|
126
81
|
exit 1
|
127
82
|
end
|
128
83
|
|
129
|
-
def print_help
|
130
|
-
puts "Usage: wgif [YouTube URL] [output file] [options]", "\n"
|
131
|
-
puts @parser.summarize, "\n"
|
132
|
-
puts <<-example
|
133
|
-
Example:
|
134
|
-
|
135
|
-
$ wgif https://www.youtube.com/watch?v=1A78yTvIY1k bjork.gif -s 00:03:30 -d 2 -w 400
|
136
|
-
|
137
|
-
example
|
138
|
-
end
|
139
|
-
|
140
84
|
end
|
141
85
|
end
|
data/lib/wgif/download_bar.rb
CHANGED
data/lib/wgif/downloader.rb
CHANGED
@@ -14,29 +14,26 @@ module WGif
|
|
14
14
|
@cache = WGif::VideoCache.new
|
15
15
|
end
|
16
16
|
|
17
|
-
def video_url
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
raise WGif::VideoNotFoundException
|
23
|
-
end
|
17
|
+
def video_url(youtube_url)
|
18
|
+
urls = ViddlRb.get_urls(youtube_url)
|
19
|
+
urls.first
|
20
|
+
rescue
|
21
|
+
raise WGif::VideoNotFoundException
|
24
22
|
end
|
25
23
|
|
26
|
-
def video_id
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
raise WGif::InvalidUrlException
|
33
|
-
end
|
24
|
+
def video_id(youtube_url)
|
25
|
+
uri = URI(youtube_url)
|
26
|
+
params = CGI.parse(uri.query)
|
27
|
+
params['v'].first
|
28
|
+
rescue
|
29
|
+
raise WGif::InvalidUrlException
|
34
30
|
end
|
35
31
|
|
36
|
-
def get_video
|
32
|
+
def get_video(youtube_url)
|
37
33
|
id = video_id youtube_url
|
38
|
-
|
39
|
-
|
34
|
+
cached_clip = @cache.get(id)
|
35
|
+
if cached_clip
|
36
|
+
cached_clip
|
40
37
|
else
|
41
38
|
temp = load_clip(id, youtube_url)
|
42
39
|
video = WGif::Video.new(id, temp.path)
|
@@ -46,7 +43,7 @@ module WGif
|
|
46
43
|
|
47
44
|
private
|
48
45
|
|
49
|
-
def create_progress_bar
|
46
|
+
def create_progress_bar(request, output_file)
|
50
47
|
size = nil
|
51
48
|
download_bar = WGif::DownloadBar.new
|
52
49
|
|
@@ -56,29 +53,28 @@ module WGif
|
|
56
53
|
end
|
57
54
|
|
58
55
|
request.on_body do |chunk|
|
59
|
-
|
60
|
-
|
56
|
+
output_file.write(chunk)
|
57
|
+
download_bar.increment_progress(chunk.size)
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
64
|
-
def request_clip
|
65
|
-
clip_url =
|
61
|
+
def request_clip(youtube_url, output_file)
|
62
|
+
clip_url = video_url(youtube_url)
|
66
63
|
request = Typhoeus::Request.new clip_url
|
67
64
|
create_progress_bar(request, output_file)
|
68
65
|
request.run
|
69
66
|
end
|
70
67
|
|
71
|
-
def load_clip
|
72
|
-
FileUtils.mkdir_p
|
68
|
+
def load_clip(id, youtube_url)
|
69
|
+
FileUtils.mkdir_p '/tmp/wgif'
|
73
70
|
temp = File.open("/tmp/wgif/#{id}", 'wb')
|
74
71
|
begin
|
75
72
|
clip = request_clip(youtube_url, temp)
|
76
|
-
|
73
|
+
fail WGif::VideoNotFoundException unless clip.response_code == 200
|
77
74
|
ensure
|
78
75
|
temp.close
|
79
76
|
end
|
80
77
|
temp
|
81
78
|
end
|
82
|
-
|
83
79
|
end
|
84
80
|
end
|