wallzilla 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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +1 -0
- data/bin/console +17 -0
- data/bin/setup +8 -0
- data/exe/wz +80 -0
- data/images/output.jpg +0 -0
- data/lib/wallzilla.rb +8 -0
- data/lib/wallzilla/fetcher.rb +75 -0
- data/lib/wallzilla/montage.rb +15 -0
- data/lib/wallzilla/progressbar.rb +237 -0
- data/lib/wallzilla/runner.rb +117 -0
- data/lib/wallzilla/version.rb +3 -0
- data/lib/wallzilla/words.rb +27 -0
- data/wallzilla.gemspec +26 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 82be8d70f275ad3b47b168a1fe9cb279c541d1c2
|
4
|
+
data.tar.gz: f3e379fd5291352e95d6dd86e791d823583dd293
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e2321f9bb4be9373dbff9207386855f3da863ae9c872fbdc53120999cefe116089ace10624deff2c7698ff0c8c519ecb7e3f6ee1e165e2ca275c9483030780f5
|
7
|
+
data.tar.gz: 4ea4016317fd6d494210f389fc87354dbc730c74a87733e18c52bca1ed9e7874ee078e076b9fbdba3d3ea081810ee530ba2a3092eba82ca73a52d5de73957fb5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Yury Batenko
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# Wallzilla
|
2
|
+
|
3
|
+
**Wallzilla** very simple ruby gem with CLI for generating mosaic images from Flickr.
|
4
|
+
|
5
|
+
It provides:
|
6
|
+
|
7
|
+
* accepts a list of search keywords as arguments
|
8
|
+
* queries the Flickr API for the top-rated (term: interestingness) image for each keyword
|
9
|
+
* downloads the results
|
10
|
+
* assembles a collage grid from ten images and
|
11
|
+
* writes the result to a user-supplied filenameds
|
12
|
+
|
13
|
+
If given less than ten keywords, or if any keyword fails to
|
14
|
+
result in a match, gem retrieve random words from a dictionary
|
15
|
+
source such as `/usr/share/dict/words` or provided one.
|
16
|
+
|
17
|
+
I like the idea to keep library as small as I can, so it has no external dependancies (except `imagemagick`)
|
18
|
+
|
19
|
+
## Example
|
20
|
+
|
21
|
+
$ wz --key YOUR_FLICKR_KEY winter boy rocket racoon fear mars
|
22
|
+
|
23
|
+
<img src="https://raw.github.com/svenyurgensson/wallzilla/master/images/output.jpg" alt="Example resulting mosaic"/>
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
### Prerequisites:
|
28
|
+
|
29
|
+
This gem assumes that you have `imagemagick` installed in your system.
|
30
|
+
|
31
|
+
You can install it from [official site](http://www.imagemagick.org/script/download.php)
|
32
|
+
|
33
|
+
### CLI installation:
|
34
|
+
|
35
|
+
$ gem install wallzilla
|
36
|
+
|
37
|
+
### Programm interface
|
38
|
+
|
39
|
+
Add this line to your application's Gemfile:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
gem 'wallzilla'
|
43
|
+
```
|
44
|
+
And then execute:
|
45
|
+
|
46
|
+
$ bundle
|
47
|
+
|
48
|
+
## CLI Usage
|
49
|
+
|
50
|
+
```shell
|
51
|
+
USAGE: wz [options] kw1 kw2 kw3 ... kw10
|
52
|
+
-o, --output FILE Write result to dir/to/filename.extname
|
53
|
+
-k, --key key_string Flickr API key string
|
54
|
+
-w, --words [FILE] Read words from dir/to/words file
|
55
|
+
-t, --tile [5x2 | 4x3] Images positioning columns x rows (4x3)
|
56
|
+
-b, --background color Fill background color (black)
|
57
|
+
-v, --version Show version
|
58
|
+
```
|
59
|
+
### Options:
|
60
|
+
|
61
|
+
`-o, --output FILE`
|
62
|
+
Save resulting mosaic to file. Default is `output.jpg` in current directory.
|
63
|
+
|
64
|
+
`-k, --key key_string`
|
65
|
+
Flickr API key, you could get one [here](https://www.flickr.com/services/apps/create/apply)
|
66
|
+
You also could set environment variable `export FLICKR_KEY=YOUR_KEY` somewhere in your profile scripts or in command line:
|
67
|
+
|
68
|
+
`$ FLICKR_KEY=YOUR_KEY wz winter boy rocket racoon fear mars`
|
69
|
+
|
70
|
+
`-b, --background color`
|
71
|
+
You could find color names [here](http://www.imagemagick.org/script/color.php), default is `black`
|
72
|
+
|
73
|
+
`-w, --words [FILE]`
|
74
|
+
Just plain text file filled with words you like, line by line. Default words sources: `/usr/share/dict/words`, `/usr/share/words`.
|
75
|
+
|
76
|
+
`-t, --tile [5x2 | 4x3]`
|
77
|
+
You could choose how images will be placed in resulting mosaic `columns X rows`, default is `4x3`
|
78
|
+
|
79
|
+
## Library usage
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
Wallzilla::Runner.build(kw: %w(winter boy rocket racoon fear mars),
|
83
|
+
result: "output.jpg",
|
84
|
+
key: YOUR_FLICKR_KEY,
|
85
|
+
words: nil, # optional
|
86
|
+
tiles: "4x3", # optional
|
87
|
+
bg: "black") # optional
|
88
|
+
```
|
89
|
+
As result you have file `output.jpg` in working directory.
|
90
|
+
|
91
|
+
## Development
|
92
|
+
|
93
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
94
|
+
|
95
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Svenyurgensson/wallzilla.
|
100
|
+
|
101
|
+
## License
|
102
|
+
|
103
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
104
|
+
|
105
|
+
## Progressbar
|
106
|
+
|
107
|
+
Progressbar code borrowed from:
|
108
|
+
|
109
|
+
# Ruby/ProgressBar - a text progress bar library
|
110
|
+
#
|
111
|
+
# Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
|
112
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "wallzilla"
|
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
|
+
|
11
|
+
begin
|
12
|
+
require "pry"
|
13
|
+
Pry.start
|
14
|
+
rescue LoadError
|
15
|
+
require "irb"
|
16
|
+
IRB.start(__FILE__)
|
17
|
+
end
|
data/bin/setup
ADDED
data/exe/wz
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$: << File.expand_path("../../lib/", __FILE__)
|
5
|
+
|
6
|
+
require "wallzilla"
|
7
|
+
require "optparse"
|
8
|
+
|
9
|
+
`#{Wallzilla::Montage.montage} --version`
|
10
|
+
if $? != 0
|
11
|
+
puts "You should have imagemagick installed!"
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
help_usage = "USAGE: #{$PROGRAM_NAME} [options] kw1 kw2 kw3 ... kw10"
|
16
|
+
|
17
|
+
OptionParser.new do |o|
|
18
|
+
o.banner = help_usage
|
19
|
+
|
20
|
+
o.on_tail("-v", "--version", "Show version") { puts "Wallzilla version: #{Wallzilla::VERSION}"; exit 0 }
|
21
|
+
|
22
|
+
o.on("-o", "--output FILE", "Write result to dir/to/filename.extname") do |f|
|
23
|
+
ENV["result_file"] = f
|
24
|
+
end
|
25
|
+
|
26
|
+
o.on("-w", "--words [FILE]", "Read words from dir/to/words file") do |f|
|
27
|
+
ENV["words_file"] = f
|
28
|
+
end
|
29
|
+
|
30
|
+
o.on("-t", "--tile [5x2 | 4x3]", "Images positioning columns x rows (4x3)") do |f|
|
31
|
+
ENV["tiles"] = "4x3"
|
32
|
+
end
|
33
|
+
|
34
|
+
o.on("-b", "--background color", "Fill background color (black)") do |f|
|
35
|
+
ENV["background_color"] = f || "black"
|
36
|
+
end
|
37
|
+
|
38
|
+
o.on("-k", "--key key_string", "Flickr API key string") do |f|
|
39
|
+
ENV["FLICKR_KEY"] ||= f
|
40
|
+
end
|
41
|
+
|
42
|
+
end.parse!
|
43
|
+
|
44
|
+
require "wallzilla/progressbar"
|
45
|
+
# Progress function.
|
46
|
+
# Expects :started with file, :progress with current line and :finished or :interrupted when done.
|
47
|
+
# <tt>message</tt> Current state (:started, :finished, :interupted or :progress).
|
48
|
+
# <tt>value</tt> File or current line.
|
49
|
+
def handle_progress(message, value = nil)
|
50
|
+
case message
|
51
|
+
when :started
|
52
|
+
@progress_bar =
|
53
|
+
Wallzilla::ProgressBar.new("Wallzilla", Wallzilla::Runner::KEYWORDS_SIZE, STDERR)
|
54
|
+
when :finished
|
55
|
+
@progress_bar.finish
|
56
|
+
@progress_bar = nil
|
57
|
+
when :interrupted
|
58
|
+
if @progress_bar
|
59
|
+
@progress_bar.halt
|
60
|
+
@progress_bar = nil
|
61
|
+
end
|
62
|
+
when :progress
|
63
|
+
@progress_bar.set(value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
Wallzilla::Runner.run!(ARGV, progressbar: method(:handle_progress))
|
69
|
+
|
70
|
+
puts
|
71
|
+
puts "Search Flickr for best photos (#{Wallzilla::Runner::KEYWORDS_SIZE}) with text keywords: #{Wallzilla::Runner.keywords}"
|
72
|
+
puts "Result mozaic image saved to: #{Wallzilla::Runner.output_file}"
|
73
|
+
puts Wallzilla::Runner::COPYRIGHT
|
74
|
+
puts
|
75
|
+
rescue => e
|
76
|
+
puts
|
77
|
+
puts e.message
|
78
|
+
puts
|
79
|
+
exit 1
|
80
|
+
end
|
data/images/output.jpg
ADDED
Binary file
|
data/lib/wallzilla.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "tempfile"
|
3
|
+
require "uri"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Wallzilla
|
7
|
+
module Fetcher
|
8
|
+
extend Fetcher
|
9
|
+
WRONG_API_KEY = "Your Flicker API key "
|
10
|
+
|
11
|
+
# Downoads image to tempfile for the given keyword
|
12
|
+
# returns:
|
13
|
+
# file handler if image found and downloaded
|
14
|
+
# nil if no image found
|
15
|
+
def fetch(keyword)
|
16
|
+
if image = search_image(keyword)
|
17
|
+
download_photo(image)
|
18
|
+
else
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def search_image(keyword)
|
24
|
+
url = flickr_search_url(keyword)
|
25
|
+
uri = URI.parse(url)
|
26
|
+
response = Net::HTTP.get_response(uri)
|
27
|
+
|
28
|
+
result = JSON.parse(response.body)
|
29
|
+
|
30
|
+
if (100...117).cover?(result["code"])
|
31
|
+
fail(result["message"])
|
32
|
+
end
|
33
|
+
|
34
|
+
photos = result["photos"]["photo"]
|
35
|
+
return nil if photos.empty?
|
36
|
+
|
37
|
+
photos.first[photo_size_code]
|
38
|
+
end
|
39
|
+
|
40
|
+
def download_photo(url)
|
41
|
+
uri = URI.parse(url)
|
42
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
|
43
|
+
resp = http.get(uri.path)
|
44
|
+
file = Tempfile.new(["flickr", ".jpg"], encoding: "binary")
|
45
|
+
file.binmode
|
46
|
+
file.write(resp.body)
|
47
|
+
file.flush
|
48
|
+
file
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def flickr_search_url(keyword, key = flickr_api_key)
|
55
|
+
"https://api.flickr.com/services/rest/?method=flickr.photos.search" +
|
56
|
+
"&api_key=#{key}" +
|
57
|
+
"&text=#{keyword}" +
|
58
|
+
"&content_type=1" + # Only photos
|
59
|
+
"&sort=interestingness-desc" +
|
60
|
+
"&extras=#{photo_size_code}" +
|
61
|
+
"&media=photos" +
|
62
|
+
"&per_page=1" +
|
63
|
+
"&format=json" +
|
64
|
+
"&nojsoncallback=1"
|
65
|
+
end
|
66
|
+
|
67
|
+
def photo_size_code
|
68
|
+
"url_c"
|
69
|
+
end
|
70
|
+
|
71
|
+
def flickr_api_key
|
72
|
+
Wallzilla::Runner.flickr_api_key
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Wallzilla
|
2
|
+
module Montage
|
3
|
+
extend Montage
|
4
|
+
|
5
|
+
def process!(file_handlers, output, fill)
|
6
|
+
files = file_handlers.map(&:path).join(" ")
|
7
|
+
cmd = "#{montage} -mode concatenate -background #{fill} -tile 5x2 #{files} #{output}"
|
8
|
+
system(cmd)
|
9
|
+
end
|
10
|
+
|
11
|
+
def montage
|
12
|
+
Gem.win_platform? ? "montage.exe" : "montage"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#
|
2
|
+
# Ruby/ProgressBar - a text progress bar library
|
3
|
+
#
|
4
|
+
# Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
|
5
|
+
# All rights reserved.
|
6
|
+
# This is free software with ABSOLUTELY NO WARRANTY.
|
7
|
+
#
|
8
|
+
# You can redistribute it and/or modify it under the terms
|
9
|
+
# of Ruby's license.
|
10
|
+
|
11
|
+
module Wallzilla
|
12
|
+
class ProgressBar
|
13
|
+
VERSION = '0.9'
|
14
|
+
|
15
|
+
def initialize(title, total, out = STDERR)
|
16
|
+
@title = title
|
17
|
+
@total = total
|
18
|
+
@out = out
|
19
|
+
@terminal_width = 80
|
20
|
+
@bar_mark = '='
|
21
|
+
@current = 0
|
22
|
+
@previous = 0
|
23
|
+
@finished_p = false
|
24
|
+
@start_time = Time.now
|
25
|
+
@previous_time = @start_time
|
26
|
+
@title_width = 24
|
27
|
+
@format = "%-#{@title_width}s %3d%% %s %s"
|
28
|
+
@format_arguments = [:title, :percentage, :bar, :stat]
|
29
|
+
clear
|
30
|
+
show
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :title
|
34
|
+
attr_reader :current
|
35
|
+
attr_reader :total
|
36
|
+
attr_accessor :start_time
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def fmt_bar
|
41
|
+
bar_width = do_percentage * @terminal_width / 100
|
42
|
+
sprintf('[%s%s]',
|
43
|
+
@bar_mark * bar_width,
|
44
|
+
' ' * (@terminal_width - bar_width))
|
45
|
+
end
|
46
|
+
|
47
|
+
def fmt_percentage
|
48
|
+
do_percentage
|
49
|
+
end
|
50
|
+
|
51
|
+
def fmt_stat
|
52
|
+
if @finished_p then elapsed else eta end
|
53
|
+
end
|
54
|
+
|
55
|
+
def fmt_stat_for_file_transfer
|
56
|
+
if @finished_p then
|
57
|
+
sprintf('%s %s %s', bytes, transfer_rate, elapsed)
|
58
|
+
else
|
59
|
+
sprintf('%s %s %s', bytes, transfer_rate, eta)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def fmt_title
|
64
|
+
@title[0, (@title_width - 1)] + ':'
|
65
|
+
end
|
66
|
+
|
67
|
+
def convert_bytes(bytes)
|
68
|
+
if bytes < 1024
|
69
|
+
sprintf('%6dB', bytes)
|
70
|
+
elsif bytes < 1024 * 1000 # 1000kb
|
71
|
+
sprintf('%5.1fKB', bytes.to_f / 1024)
|
72
|
+
elsif bytes < 1024 * 1024 * 1000 # 1000mb
|
73
|
+
sprintf('%5.1fMB', bytes.to_f / 1024 / 1024)
|
74
|
+
else
|
75
|
+
sprintf('%5.1fGB', bytes.to_f / 1024 / 1024 / 1024)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def transfer_rate
|
80
|
+
bytes_per_second = @current.to_f / (Time.now - @start_time)
|
81
|
+
sprintf('%s/s', convert_bytes(bytes_per_second))
|
82
|
+
end
|
83
|
+
|
84
|
+
def bytes
|
85
|
+
convert_bytes(@current)
|
86
|
+
end
|
87
|
+
|
88
|
+
def format_time(t)
|
89
|
+
t = t.to_i
|
90
|
+
sec = t % 60
|
91
|
+
min = (t / 60) % 60
|
92
|
+
hour = t / 3600
|
93
|
+
sprintf('%02d:%02d:%02d', hour, min, sec)
|
94
|
+
end
|
95
|
+
|
96
|
+
# ETA stands for Estimated Time of Arrival.
|
97
|
+
def eta
|
98
|
+
if @current == 0
|
99
|
+
'ETA: --:--:--'
|
100
|
+
else
|
101
|
+
elapsed = Time.now - @start_time
|
102
|
+
eta = elapsed * @total / @current - elapsed
|
103
|
+
sprintf('ETA: %s', format_time(eta))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def elapsed
|
108
|
+
elapsed = Time.now - @start_time
|
109
|
+
sprintf('Time: %s', format_time(elapsed))
|
110
|
+
end
|
111
|
+
|
112
|
+
def eol
|
113
|
+
if @finished_p then "\n" else "\r" end
|
114
|
+
end
|
115
|
+
|
116
|
+
def do_percentage
|
117
|
+
if @total.zero?
|
118
|
+
100
|
119
|
+
else
|
120
|
+
@current * 100 / @total
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def show
|
125
|
+
arguments = @format_arguments.map do |method|
|
126
|
+
method = sprintf('fmt_%s', method)
|
127
|
+
send(method)
|
128
|
+
end
|
129
|
+
line = sprintf(@format, *arguments)
|
130
|
+
|
131
|
+
width = terminal_width(80, @out)
|
132
|
+
if line.length == width - 1
|
133
|
+
@out.print(line + eol)
|
134
|
+
@out.flush
|
135
|
+
elsif line.length >= width
|
136
|
+
@terminal_width = [@terminal_width - (line.length - width + 1), 0].max
|
137
|
+
if @terminal_width == 0 then @out.print(line + eol) else show end
|
138
|
+
else # line.length < width - 1
|
139
|
+
@terminal_width += width - line.length + 1
|
140
|
+
show
|
141
|
+
end
|
142
|
+
@previous_time = Time.now
|
143
|
+
end
|
144
|
+
|
145
|
+
def show_if_needed
|
146
|
+
if @total.zero?
|
147
|
+
cur_percentage = 100
|
148
|
+
prev_percentage = 0
|
149
|
+
else
|
150
|
+
cur_percentage = (@current * 100 / @total).to_i
|
151
|
+
prev_percentage = (@previous * 100 / @total).to_i
|
152
|
+
end
|
153
|
+
|
154
|
+
# Use "!=" instead of ">" to support negative changes
|
155
|
+
if cur_percentage != prev_percentage ||
|
156
|
+
Time.now - @previous_time >= 1 || @finished_p
|
157
|
+
show
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
public
|
162
|
+
|
163
|
+
def clear
|
164
|
+
@out.print "\r"
|
165
|
+
@out.print(' ' * (terminal_width(80) - 1))
|
166
|
+
@out.print "\r"
|
167
|
+
end
|
168
|
+
|
169
|
+
def finish
|
170
|
+
@current = @total
|
171
|
+
@finished_p = true
|
172
|
+
show
|
173
|
+
end
|
174
|
+
|
175
|
+
def finished?
|
176
|
+
@finished_p
|
177
|
+
end
|
178
|
+
|
179
|
+
def file_transfer_mode
|
180
|
+
@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
181
|
+
end
|
182
|
+
|
183
|
+
attr_writer :format
|
184
|
+
|
185
|
+
attr_writer :format_arguments
|
186
|
+
|
187
|
+
def halt
|
188
|
+
@finished_p = true
|
189
|
+
show
|
190
|
+
end
|
191
|
+
|
192
|
+
def inc(step = 1)
|
193
|
+
@current += step
|
194
|
+
@current = @total if @current > @total
|
195
|
+
show_if_needed
|
196
|
+
@previous = @current
|
197
|
+
end
|
198
|
+
|
199
|
+
def set(count)
|
200
|
+
count = 0 if count < 0
|
201
|
+
count = @total if count > @total
|
202
|
+
|
203
|
+
@current = count
|
204
|
+
show_if_needed
|
205
|
+
@previous = @current
|
206
|
+
end
|
207
|
+
|
208
|
+
def inspect
|
209
|
+
"#<ProgressBar:#{@current}/#{@total}>"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Try to determine the terminal with.
|
213
|
+
# If it is not possible to to so, it returns the default_width.
|
214
|
+
# <tt>default_width</tt> Defaults to 81
|
215
|
+
def terminal_width(default_width = 81, out = STDOUT)
|
216
|
+
tiocgwinsz = 0x5413
|
217
|
+
data = [0, 0, 0, 0].pack('SSSS')
|
218
|
+
if !RUBY_PLATFORM.include?('java') && out.ioctl(tiocgwinsz, data) >= 0 # JRuby crashes on ioctl
|
219
|
+
_, cols, _, _ = data.unpack('SSSS')
|
220
|
+
fail unless cols > 0
|
221
|
+
cols
|
222
|
+
else
|
223
|
+
fail
|
224
|
+
end
|
225
|
+
rescue
|
226
|
+
begin
|
227
|
+
IO.popen('stty -a 2>&1') do |pipe|
|
228
|
+
column_line = pipe.find { |line| /(\d+) columns/ =~ line }
|
229
|
+
fail unless column_line
|
230
|
+
Regexp.last_match[1].to_i
|
231
|
+
end
|
232
|
+
rescue
|
233
|
+
default_width
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Wallzilla
|
2
|
+
module Runner
|
3
|
+
extend Runner
|
4
|
+
|
5
|
+
KEYWORDS_SIZE = 10
|
6
|
+
KW_LEN_RANGE = 4...10
|
7
|
+
DEFAULT_FILENAME = "output.jpg"
|
8
|
+
NO_WORDS_FILE_FOUND = "Sorry, but no one words list file found!"
|
9
|
+
COPYRIGHT = "© 2017 Yury Batenko. Done with fun"
|
10
|
+
|
11
|
+
# Entry point for external calling Wallzilla image generation
|
12
|
+
#
|
13
|
+
def build(kw:, result:, key:, words: nil, tiles: "4x3", bg: "black")
|
14
|
+
@_output_file = result
|
15
|
+
@_key = key
|
16
|
+
@wordlist = words
|
17
|
+
@tiles = tiles
|
18
|
+
@_fill = bg
|
19
|
+
|
20
|
+
run!(kw)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run!(kw, progressbar: nil)
|
24
|
+
@progressbar = progressbar
|
25
|
+
maybe_show_progress(:started)
|
26
|
+
|
27
|
+
@keywords = normalize(kw)
|
28
|
+
|
29
|
+
photos = process_photos(keywords)
|
30
|
+
|
31
|
+
Wallzilla::Montage.process!(photos, output_file, fill)
|
32
|
+
|
33
|
+
maybe_show_progress(:finished)
|
34
|
+
rescue => e
|
35
|
+
maybe_show_progress(:interrupted)
|
36
|
+
raise e
|
37
|
+
ensure
|
38
|
+
clean(photos) if photos
|
39
|
+
end
|
40
|
+
|
41
|
+
def process_photos(kw)
|
42
|
+
kw.map.with_index(1) do |word, idx|
|
43
|
+
fetched_photo = loop do
|
44
|
+
url = fetch(word)
|
45
|
+
break url if url
|
46
|
+
word = random_word
|
47
|
+
end
|
48
|
+
maybe_show_progress(:progress, idx)
|
49
|
+
fetched_photo
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def clean(photos)
|
54
|
+
photos.map { |x| x.close; x.unlink }
|
55
|
+
end
|
56
|
+
|
57
|
+
def fetch(kw)
|
58
|
+
Wallzilla::Fetcher.fetch(kw)
|
59
|
+
end
|
60
|
+
|
61
|
+
def keywords
|
62
|
+
@keywords
|
63
|
+
end
|
64
|
+
|
65
|
+
def fill
|
66
|
+
return @_fill if defined?(@_fill)
|
67
|
+
|
68
|
+
@_fill =
|
69
|
+
ENV["background_color"] || "black"
|
70
|
+
end
|
71
|
+
|
72
|
+
def output_file
|
73
|
+
return @_output_file if defined?(@_output_file)
|
74
|
+
|
75
|
+
@_output_file =
|
76
|
+
ENV["result_file"] || File.join(Dir.pwd, DEFAULT_FILENAME)
|
77
|
+
end
|
78
|
+
|
79
|
+
def flickr_api_key
|
80
|
+
return @_key if defined?(@_key)
|
81
|
+
|
82
|
+
@_key = ENV.fetch("FLICKR_KEY") do |e|
|
83
|
+
fail("You should provide Flickr API application key! (--key key_string)")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def dict_file
|
88
|
+
return @_dict if defined?(@_dict)
|
89
|
+
|
90
|
+
@_dict = maybe_dictionaries.
|
91
|
+
uniq.
|
92
|
+
compact.
|
93
|
+
select { |f| FileTest.readable?(f) }.first || fail(NO_WORDS_FILE_FOUND)
|
94
|
+
end
|
95
|
+
|
96
|
+
def maybe_dictionaries
|
97
|
+
Array(@wordlist) +
|
98
|
+
Array(ENV["words_file"]) +
|
99
|
+
["/usr/share/dict/words", "/usr/share/words"]
|
100
|
+
end
|
101
|
+
|
102
|
+
def normalize(kw)
|
103
|
+
Array.new(KEYWORDS_SIZE) do |i|
|
104
|
+
kw[i] || random_word
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def random_word
|
109
|
+
Wallzilla::Words.random_word(KW_LEN_RANGE)
|
110
|
+
end
|
111
|
+
|
112
|
+
def maybe_show_progress(msg, value = nil)
|
113
|
+
return unless @progressbar
|
114
|
+
@progressbar.call(msg, value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Wallzilla
|
2
|
+
module Words
|
3
|
+
extend Words
|
4
|
+
|
5
|
+
# @params [size_range] # Example: 3...9
|
6
|
+
#
|
7
|
+
# returns:
|
8
|
+
# random word from dictionary word list file
|
9
|
+
def random_word(size_range, window = 4)
|
10
|
+
f = File.open(Wallzilla::Runner.dict_file, 'r')
|
11
|
+
fsize = f.size
|
12
|
+
loop do
|
13
|
+
f.seek(rand(fsize))
|
14
|
+
next if f.eof?
|
15
|
+
f.readline
|
16
|
+
window.times do
|
17
|
+
break if f.eof
|
18
|
+
w = f.readline.strip
|
19
|
+
if size_range.cover? w.size
|
20
|
+
f.close
|
21
|
+
return w
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/wallzilla.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "wallzilla/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "wallzilla"
|
8
|
+
spec.version = Wallzilla::VERSION
|
9
|
+
spec.authors = ["Yury Batenko"]
|
10
|
+
spec.email = ["jurbat@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Flickr best images wall generator based on provided tags}
|
13
|
+
spec.description = %q{Flickr best images wall generator based on provided tags}
|
14
|
+
spec.homepage = "https://github.com/svenyurgensson/wallzilla"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wallzilla
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yury Batenko
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Flickr best images wall generator based on provided tags
|
42
|
+
email:
|
43
|
+
- jurbat@gmail.com
|
44
|
+
executables:
|
45
|
+
- wz
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".rspec"
|
51
|
+
- ".travis.yml"
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE.txt
|
54
|
+
- README.md
|
55
|
+
- Rakefile
|
56
|
+
- bin/console
|
57
|
+
- bin/setup
|
58
|
+
- exe/wz
|
59
|
+
- images/output.jpg
|
60
|
+
- lib/wallzilla.rb
|
61
|
+
- lib/wallzilla/fetcher.rb
|
62
|
+
- lib/wallzilla/montage.rb
|
63
|
+
- lib/wallzilla/progressbar.rb
|
64
|
+
- lib/wallzilla/runner.rb
|
65
|
+
- lib/wallzilla/version.rb
|
66
|
+
- lib/wallzilla/words.rb
|
67
|
+
- wallzilla.gemspec
|
68
|
+
homepage: https://github.com/svenyurgensson/wallzilla
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.6.12
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Flickr best images wall generator based on provided tags
|
92
|
+
test_files: []
|