flico 1.0.0 → 2.0.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/flico.gemspec +1 -1
- data/lib/flico/app.rb +35 -28
- data/lib/flico/collager.rb +13 -32
- data/lib/flico/dictionary.rb +20 -9
- data/lib/flico/flickr/base_api.rb +66 -0
- data/lib/flico/image_file.rb +29 -0
- data/lib/flico.rb +1 -53
- data/spec/base_api_spec.rb +33 -0
- data/spec/dictionary_spec.rb +21 -0
- metadata +6 -7
- data/lib/flico/cell.rb +0 -32
- data/lib/flico/flickr_command.rb +0 -55
- data/lib/flico/grid.rb +0 -83
- data/lib/flico/saver.rb +0 -39
- data/spec/flickr_command_spec.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473a26ece9ff8cf869ea977bdc1a96a5b800848ce82f4ee0282dad74bda96bdc
|
4
|
+
data.tar.gz: b4e83f8d45ea19e79e4d5884603f3941af769e279d8267dff9379f662f3acbbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55a33bfccc1180547b859c572ef4802f4bd6b6299298319e0b21519ae381b4f040b0620ffbde06e002c9d9216d6f00b559af148aa38b93d9968b10464351c918
|
7
|
+
data.tar.gz: c96edd9e612f9cb0dbc1711c0d0dedf5ade8be1b1e685316055cd4d48b3bbda9bbdc6c2b8aa8eea6073a9da88f10d8589b9f1c4bfad9db033d7d20664ec3ab7a
|
data/flico.gemspec
CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'flico'
|
8
|
-
spec.version = '
|
8
|
+
spec.version = '2.0.0'
|
9
9
|
spec.summary = 'A CLI tool to create collage from keywords using Flickr'
|
10
10
|
spec.homepage = 'https://github.com/amandeepbhamra/flico'
|
11
11
|
spec.license = 'MIT'
|
data/lib/flico/app.rb
CHANGED
@@ -1,44 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'flico/dictionary'
|
4
|
+
require 'flico/collager'
|
5
|
+
require 'flico/image_file'
|
6
|
+
|
3
7
|
module Flico
|
4
8
|
class ApplicationError < StandardError; end
|
5
|
-
class KeywordMissing < StandardError; end
|
6
|
-
class NoImage < StandardError; end
|
7
|
-
class FetchingError < StandardError; end
|
8
9
|
|
9
10
|
class App
|
10
|
-
|
11
|
+
KEYWORDS_COUNT = 10
|
12
|
+
|
13
|
+
attr_reader :keywords, :options
|
11
14
|
|
12
|
-
def initialize(
|
13
|
-
@
|
15
|
+
def initialize(keywords, options)
|
16
|
+
@keywords = add_missing_keywords(keywords)
|
17
|
+
@options = options
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
collage(image_urls)
|
20
|
+
def create
|
21
|
+
options[:file_name] = prepare_file_name(options)
|
22
|
+
|
23
|
+
Collager.new(prepare_images, options[:file_name]).save
|
24
|
+
|
25
|
+
puts "Flicollage saved at #{options[:file_name]}"
|
23
26
|
end
|
24
27
|
|
25
|
-
def
|
26
|
-
|
28
|
+
def prepare_images
|
29
|
+
keywords.map { |keyword| ImageFile.new(keyword).fetch_from_flickr }
|
27
30
|
end
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
32
|
+
private
|
33
|
+
|
34
|
+
def add_missing_keywords(words)
|
35
|
+
words_count = words.size
|
36
|
+
return words if words_count == KEYWORDS_COUNT
|
37
|
+
|
38
|
+
words += Dictionary.new.words(KEYWORDS_COUNT - words_count)
|
39
|
+
puts "KEYWORDS: #{words}"
|
40
|
+
words
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepare_file_name(options)
|
44
|
+
return options[:file_name] unless options[:file_name].nil?
|
45
|
+
|
46
|
+
puts 'Enter file name for collage (press ENTER to use default)'
|
47
|
+
file_name = $stdin.gets.strip
|
48
|
+
file_name.empty? ? "flicollage-#{Time.now.strftime('%Y%m%d%H%M%S')}.png" : file_name
|
42
49
|
end
|
43
50
|
end
|
44
51
|
end
|
data/lib/flico/collager.rb
CHANGED
@@ -1,44 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'flico/grid'
|
4
3
|
require 'mini_magick'
|
5
4
|
|
6
5
|
module Flico
|
7
6
|
class Collager
|
8
|
-
attr_reader :
|
7
|
+
attr_reader :images_url, :file_name
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@
|
9
|
+
def initialize(images_url, file_name)
|
10
|
+
@images_url = images_url
|
11
|
+
@file_name = file_name
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
image_urls.map.with_index do |path, idx|
|
24
|
-
image = MiniMagick::Image.open path.path
|
25
|
-
image.crop(grid.crop_rectangle(idx, image.width, image.height).to_mm)
|
26
|
-
image.resize(grid.resize_rectangle(idx, image.width, image.height).to_mm)
|
27
|
-
print_to_canvas(image, grid.cell_rectangle(idx), temp_file)
|
28
|
-
end
|
29
|
-
temp_file
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def print_to_canvas(image, rectangle, temp_file)
|
35
|
-
canvas = MiniMagick::Image.new temp_file.path
|
36
|
-
result = canvas.composite(image) do |c|
|
37
|
-
c.compose 'Over'
|
38
|
-
c.geometry rectangle.to_mm
|
39
|
-
end
|
40
|
-
result.write temp_file.path
|
41
|
-
temp_file.rewind
|
14
|
+
def save
|
15
|
+
montage = MiniMagick::Tool::Montage.new
|
16
|
+
montage.density '600'
|
17
|
+
montage.tile '5X2'
|
18
|
+
montage.geometry '+10+20'
|
19
|
+
montage.border '5'
|
20
|
+
images_url.each { |image_url| montage << image_url }
|
21
|
+
montage << file_name
|
22
|
+
montage.call
|
42
23
|
end
|
43
24
|
end
|
44
25
|
end
|
data/lib/flico/dictionary.rb
CHANGED
@@ -2,23 +2,34 @@
|
|
2
2
|
|
3
3
|
module Flico
|
4
4
|
class Dictionary
|
5
|
-
|
6
|
-
|
5
|
+
PATH = '/usr/share/dict/words'
|
6
|
+
ALPHABETS_LOWER_LIMIT = 3
|
7
|
+
ALPHABETS_UPPER_LIMIT = 10
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@dictionary_path = validate(PATH)
|
7
11
|
@words = []
|
8
12
|
end
|
9
13
|
|
10
|
-
def
|
11
|
-
@words
|
14
|
+
def words(count)
|
15
|
+
@words += pick_words(count)
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
18
|
+
private
|
19
|
+
|
20
|
+
def pick_words(count)
|
21
|
+
File.readlines(PATH).select do |word|
|
22
|
+
word.size > ALPHABETS_LOWER_LIMIT && word.size < ALPHABETS_UPPER_LIMIT
|
23
|
+
end.sample(count).map(&:strip)
|
16
24
|
end
|
17
25
|
|
18
|
-
|
26
|
+
def validate(path)
|
27
|
+
unless File.exist?(path)
|
28
|
+
warn "Exiting due to missing dictionary at path: #{path}"
|
29
|
+
exit 1
|
30
|
+
end
|
19
31
|
|
20
|
-
|
21
|
-
File.readlines("/usr/share/dict/words").select { |word| word.size > 3 && word.size < 10 }.sample.strip
|
32
|
+
path
|
22
33
|
end
|
23
34
|
end
|
24
35
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'flickraw'
|
4
|
+
|
5
|
+
module Flico
|
6
|
+
module Flickr
|
7
|
+
class BaseApi
|
8
|
+
attr_reader :key, :secret, :api
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@key = ENV['FLICKR_KEY']
|
12
|
+
@secret = ENV['FLICKR_SECRET']
|
13
|
+
@api = flickraw_api
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_photo(keyword)
|
17
|
+
valid_keyword?(keyword)
|
18
|
+
|
19
|
+
puts "Searching images for keyword: #{keyword}"
|
20
|
+
|
21
|
+
results = search_api(keyword)
|
22
|
+
|
23
|
+
results.count.zero? ? (warn "No Image for keyword: #{keyword}") : photo_url(results.first.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def flickraw_api
|
29
|
+
valid_secrets?
|
30
|
+
|
31
|
+
FlickRaw.api_key = key
|
32
|
+
FlickRaw.shared_secret = secret
|
33
|
+
FlickRaw::Flickr.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid_secrets?
|
37
|
+
if key.nil? || secret.nil?
|
38
|
+
warn 'Exiting due to missing required environment variables: FLICKR_KEY or FLICKR_SECRET'
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def search_api(keyword)
|
46
|
+
api.photos.search({ text: keyword, per_page: 10, sort: 'interestingness-desc' })
|
47
|
+
end
|
48
|
+
|
49
|
+
def info_api(image)
|
50
|
+
api.photos.getInfo(photo_id: image)
|
51
|
+
end
|
52
|
+
|
53
|
+
def size_api(id)
|
54
|
+
api.photos.getSizes(photo_id: id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def photo_url(id)
|
58
|
+
size_api(id).find { |v| v['label'] == 'Square' }['source']
|
59
|
+
end
|
60
|
+
|
61
|
+
def valid_keyword?(keyword)
|
62
|
+
warn "Missing Keywords (can't be nil)" if keyword.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'flico/flickr/base_api'
|
4
|
+
|
5
|
+
module Flico
|
6
|
+
class NoImage < StandardError; end
|
7
|
+
class FetchingError < StandardError; end
|
8
|
+
|
9
|
+
class ImageFile
|
10
|
+
attr_accessor :keyword
|
11
|
+
|
12
|
+
def initialize(keyword)
|
13
|
+
@keyword = keyword
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_from_flickr
|
17
|
+
Flickr::BaseApi.new.get_photo(keyword)
|
18
|
+
rescue NoImage => e
|
19
|
+
puts "Image not found for keyword '#{keyword}'. Message: #{e.message}. Retrying"
|
20
|
+
unless (tries -= 1).positive?
|
21
|
+
raise ApplicationError, "Failed getting image after retrying #{MAX_KEYWORD_RETRIES} times"
|
22
|
+
end
|
23
|
+
|
24
|
+
retry
|
25
|
+
rescue FetchingError => e
|
26
|
+
raise ApplicationError, e.message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/flico.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'flickraw'
|
4
3
|
require 'ostruct'
|
5
4
|
require 'optparse'
|
6
5
|
require 'flico/app'
|
7
|
-
require 'flico/flickr_command'
|
8
|
-
require 'flico/dictionary'
|
9
|
-
require 'flico/saver'
|
10
|
-
require 'flico/collager'
|
11
6
|
|
12
7
|
module Flico
|
13
8
|
class CommandLineInterface
|
@@ -24,54 +19,7 @@ module Flico
|
|
24
19
|
|
25
20
|
def self.start(args)
|
26
21
|
keywords, options = parse_args(args)
|
27
|
-
|
28
|
-
resources = validate_resources(keywords, options)
|
29
|
-
App.new(resources).create_collage
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.validate_resources(keywords, options = {})
|
33
|
-
dictionary = validate_dictionary
|
34
|
-
dictionary.append(keywords)
|
35
|
-
col = SaveCollage.new
|
36
|
-
col.output_file_name = options[:file_name]
|
37
|
-
|
38
|
-
Resource.new(flickr_api: validate_flickr_api, dictionary:, fetch_image: FetchImage.new,
|
39
|
-
collager: Collager.new, save_collage: col)
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.validate_flickr_api
|
43
|
-
key = ENV['FLICKR_KEY']
|
44
|
-
secret = ENV['FLICKR_SECRET']
|
45
|
-
if key.nil? && secret.nil?
|
46
|
-
warn 'Performing AutoExit due to missing required environment variables: FLICKR_KEY and FLICKR_SECRET'
|
47
|
-
exit 1
|
48
|
-
else
|
49
|
-
FlickRaw.api_key = key
|
50
|
-
FlickRaw.shared_secret = secret
|
51
|
-
FlickrCommand.setup(FlickRaw::Flickr.new)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.validate_dictionary
|
56
|
-
dictionary_path = '/usr/share/dict/words'
|
57
|
-
if File.exist?(dictionary_path)
|
58
|
-
Dictionary.new(dictionary_path)
|
59
|
-
else
|
60
|
-
warn "Performing AutoExit due to missing required dictionary at path: #{dictionary_path}"
|
61
|
-
exit 1
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class Resource
|
67
|
-
attr_reader :flickr_api, :dictionary, :fetch_image, :collager, :save_collage
|
68
|
-
|
69
|
-
def initialize(params = {})
|
70
|
-
@flickr_api = params.fetch(:flickr_api)
|
71
|
-
@dictionary = params.fetch(:dictionary)
|
72
|
-
@fetch_image = params.fetch(:fetch_image)
|
73
|
-
@collager = params.fetch(:collager)
|
74
|
-
@save_collage = params.fetch(:save_collage)
|
22
|
+
App.new(keywords, options).create
|
75
23
|
end
|
76
24
|
end
|
77
25
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'flico/flickr/base_api'
|
5
|
+
|
6
|
+
describe Flico::Flickr::BaseApi do
|
7
|
+
let(:photo_url) do
|
8
|
+
{
|
9
|
+
'label' => 'Square',
|
10
|
+
'width' => 75,
|
11
|
+
'height' => 75,
|
12
|
+
'source' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg',
|
13
|
+
'url' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/sq/',
|
14
|
+
'media' => 'photo'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:subject) { described_class.new }
|
19
|
+
|
20
|
+
context 'search success' do
|
21
|
+
before do
|
22
|
+
ENV['FLICKR_KEY'] = 'FOO'
|
23
|
+
ENV['FLICKR_SECRET'] = 'BAR'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return image url for search keyword' do
|
27
|
+
allow_any_instance_of(Flico::Flickr::BaseApi).to receive(:flickraw_api).and_return(double('flickraw_api'))
|
28
|
+
allow_any_instance_of(Flico::Flickr::BaseApi).to receive(:get_photo).and_return(photo_url)
|
29
|
+
|
30
|
+
expect(subject.get_photo('Volkswagen Golf in Luxembourg')['source']).to eq('https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'flico/dictionary'
|
5
|
+
|
6
|
+
describe Flico::Dictionary do
|
7
|
+
let(:subject) { described_class.new }
|
8
|
+
|
9
|
+
context '#words' do
|
10
|
+
let(:count) { 3 }
|
11
|
+
let(:words) { subject.words(count) }
|
12
|
+
|
13
|
+
it 'should not be empty' do
|
14
|
+
expect(words).to_not be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return 3 random words' do
|
18
|
+
expect(words.count).to eq count
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flico
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amandeep Singh Bhamra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flickraw
|
@@ -114,13 +114,12 @@ files:
|
|
114
114
|
- flico.gemspec
|
115
115
|
- lib/flico.rb
|
116
116
|
- lib/flico/app.rb
|
117
|
-
- lib/flico/cell.rb
|
118
117
|
- lib/flico/collager.rb
|
119
118
|
- lib/flico/dictionary.rb
|
120
|
-
- lib/flico/
|
121
|
-
- lib/flico/
|
122
|
-
-
|
123
|
-
- spec/
|
119
|
+
- lib/flico/flickr/base_api.rb
|
120
|
+
- lib/flico/image_file.rb
|
121
|
+
- spec/base_api_spec.rb
|
122
|
+
- spec/dictionary_spec.rb
|
124
123
|
- spec/spec_helper.rb
|
125
124
|
homepage: https://github.com/amandeepbhamra/flico
|
126
125
|
licenses:
|
data/lib/flico/cell.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Flico
|
4
|
-
class Cell
|
5
|
-
attr_reader :x, :y, :width, :height
|
6
|
-
|
7
|
-
def initialize(x = 0, y = 0, width, height)
|
8
|
-
@x = x
|
9
|
-
@y = y
|
10
|
-
@width = width
|
11
|
-
@height = height
|
12
|
-
end
|
13
|
-
|
14
|
-
def aspect_ratio
|
15
|
-
width.to_f / height
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_mm
|
19
|
-
"#{width}x#{height}+#{x}+#{y}"
|
20
|
-
end
|
21
|
-
|
22
|
-
def ==(other)
|
23
|
-
other.class == self.class && other.state == state
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
|
28
|
-
def state
|
29
|
-
[@x, @y, @width, @height]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/flico/flickr_command.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Flico
|
4
|
-
class FlickrCommand
|
5
|
-
def initialize(search_command:, sizes_command:)
|
6
|
-
@search_command = search_command
|
7
|
-
@sizes_command = sizes_command
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.setup(flickraw)
|
11
|
-
new(search_command: SearchCommand.new(flickraw), sizes_command: SizesCommand.new(flickraw))
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(keyword)
|
15
|
-
if keyword.nil?
|
16
|
-
warn "Missing Keywords (can't be nil)"
|
17
|
-
else
|
18
|
-
puts "Searching images for keyword: #{keyword}"
|
19
|
-
results = @search_command.call(keyword)
|
20
|
-
if results.count.zero?
|
21
|
-
warn "No Image for keyword: #{keyword}"
|
22
|
-
else
|
23
|
-
get_image_url(results.first['id'])
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def get_image_url(photo_id)
|
31
|
-
sizes = @sizes_command.call(photo_id)
|
32
|
-
sizes[sizes.count / 2]['source']
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class FlickrBase
|
37
|
-
attr_reader :api
|
38
|
-
|
39
|
-
def initialize(flickraw)
|
40
|
-
@api = flickraw
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class SearchCommand < FlickrBase
|
45
|
-
def call(keyword)
|
46
|
-
api.photos.search({ text: keyword, per_page: 10, sort: 'interestingness-desc' })
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class SizesCommand < FlickrBase
|
51
|
-
def call(photo_id)
|
52
|
-
api.photos.getSizes(photo_id:)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/flico/grid.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'flico/cell'
|
4
|
-
|
5
|
-
module Flico
|
6
|
-
class Grid
|
7
|
-
GRID = [[0, 1, 1, 2], [3, 4, 5, 6], [7, 8, 8, 9]].freeze
|
8
|
-
CANV_WIDTH = 1750
|
9
|
-
CANV_HEIGHT = 1250
|
10
|
-
|
11
|
-
def canv_width
|
12
|
-
CANV_WIDTH
|
13
|
-
end
|
14
|
-
|
15
|
-
def canv_height
|
16
|
-
CANV_HEIGHT
|
17
|
-
end
|
18
|
-
|
19
|
-
def columns
|
20
|
-
GRID.max_by(&:size).size
|
21
|
-
end
|
22
|
-
|
23
|
-
def column_cells
|
24
|
-
GRID.transpose
|
25
|
-
end
|
26
|
-
|
27
|
-
def rows
|
28
|
-
GRID.size
|
29
|
-
end
|
30
|
-
|
31
|
-
def row_cells
|
32
|
-
GRID
|
33
|
-
end
|
34
|
-
|
35
|
-
def cell_width(name)
|
36
|
-
cell_size(CANV_WIDTH, columns, size_multiplier(row_cells, name))
|
37
|
-
end
|
38
|
-
|
39
|
-
def cell_height(name)
|
40
|
-
cell_size(CANV_HEIGHT, rows, size_multiplier(column_cells, name))
|
41
|
-
end
|
42
|
-
|
43
|
-
def crop_rectangle(cell_name, image_width, image_height)
|
44
|
-
x = 0
|
45
|
-
y = 0
|
46
|
-
image = Cell.new(image_width, image_height)
|
47
|
-
cell = cell_rectangle(cell_name)
|
48
|
-
|
49
|
-
if image.aspect_ratio >= cell.aspect_ratio
|
50
|
-
final_width = image_width - (image.width - (image.height * cell.aspect_ratio))
|
51
|
-
final_height = image.height
|
52
|
-
else
|
53
|
-
final_width = image.width
|
54
|
-
final_height = image.height - (image.height - (image.width / cell.aspect_ratio))
|
55
|
-
end
|
56
|
-
|
57
|
-
Cell.new(x, y, final_width, final_height)
|
58
|
-
end
|
59
|
-
|
60
|
-
def resize_rectangle(cell_name, image_width, image_height)
|
61
|
-
Cell.new(image_width, image_height)
|
62
|
-
cell_rectangle(cell_name)
|
63
|
-
end
|
64
|
-
|
65
|
-
def cell_rectangle(name)
|
66
|
-
column = column_cells.find_index do |row|
|
67
|
-
row.include? name
|
68
|
-
end
|
69
|
-
row = row_cells.find_index { |row| row.include? name }
|
70
|
-
Cell.new(column * (CANV_WIDTH / columns), row * (CANV_HEIGHT / rows), cell_width(name), cell_height(name))
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def size_multiplier(table, name)
|
76
|
-
table.map { |row| row.count(name) }.max
|
77
|
-
end
|
78
|
-
|
79
|
-
def cell_size(total, divisions, multiplier)
|
80
|
-
multiplier.zero? ? 0 : ((total / divisions) * multiplier)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
data/lib/flico/saver.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'open-uri'
|
4
|
-
require 'tempfile'
|
5
|
-
require 'fileutils'
|
6
|
-
require 'date'
|
7
|
-
|
8
|
-
module Flico
|
9
|
-
class FetchingError < StandardError; end
|
10
|
-
|
11
|
-
class FetchImage
|
12
|
-
def call(url)
|
13
|
-
tempfile = Tempfile.new 'temp_image'
|
14
|
-
IO.copy_stream(URI.open(url, read_timeout: 5), tempfile)
|
15
|
-
tempfile.rewind
|
16
|
-
tempfile
|
17
|
-
rescue OpenURI::HTTPError => e
|
18
|
-
raise FetchingError, "Couldn't download from: #{url} due to #{e.message}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class SaveCollage
|
23
|
-
attr_accessor :output_file_name
|
24
|
-
|
25
|
-
def call(file_path)
|
26
|
-
file_name = output_file_name || validate_file_name
|
27
|
-
FileUtils.mv file_path, file_name
|
28
|
-
puts "Flicollage saved at #{file_name}"
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def validate_file_name
|
34
|
-
puts "Enter file name for collage (press ENTER to use '#{default}')"
|
35
|
-
file_name = $stdin.gets.strip
|
36
|
-
file_name.empty? ? "flicollage-#{Time.now.strftime('%Y%m%d%H%M%S')}.png" : file_name
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/spec/flickr_command_spec.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
require 'flico/flickr_command'
|
5
|
-
|
6
|
-
describe Flico::FlickrCommand do
|
7
|
-
let(:search_results) do
|
8
|
-
[
|
9
|
-
{
|
10
|
-
'id' => '33472607802',
|
11
|
-
'owner' => '78759190@N05',
|
12
|
-
'secret' => '42e011bcf5',
|
13
|
-
'server' => '2805',
|
14
|
-
'farm' => 3,
|
15
|
-
'title' => 'Volkswagen Golf in Luxembourg',
|
16
|
-
'ispublic' => 1,
|
17
|
-
'isfriend' => 0,
|
18
|
-
'isfamily' => 0
|
19
|
-
}
|
20
|
-
]
|
21
|
-
end
|
22
|
-
let(:sizes_results) do
|
23
|
-
[
|
24
|
-
{
|
25
|
-
'label' => 'Square',
|
26
|
-
'width' => 75,
|
27
|
-
'height' => 75,
|
28
|
-
'source' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg',
|
29
|
-
'url' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/sq/',
|
30
|
-
'media' => 'photo'
|
31
|
-
},
|
32
|
-
{
|
33
|
-
'label' => 'Large Square',
|
34
|
-
'width' => '150',
|
35
|
-
'height' => '150',
|
36
|
-
'source' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_q.jpg',
|
37
|
-
'url' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/q/',
|
38
|
-
'media' => 'photo'
|
39
|
-
},
|
40
|
-
|
41
|
-
{
|
42
|
-
'label' => 'Thumbnail',
|
43
|
-
'width' => '100',
|
44
|
-
'height' => '66',
|
45
|
-
'source' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_t.jpg',
|
46
|
-
'url' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/t/',
|
47
|
-
'media' => 'photo'
|
48
|
-
}
|
49
|
-
]
|
50
|
-
end
|
51
|
-
let(:subject) { described_class.new(search_command:, sizes_command:) }
|
52
|
-
let(:search_command) { double('search', call: search_results) }
|
53
|
-
let(:sizes_command) { double('sizes', call: sizes_results) }
|
54
|
-
|
55
|
-
context 'search success' do
|
56
|
-
it 'should return image url for search keyword' do
|
57
|
-
expect(subject.call('Volkswagen Golf in Luxembourg')).to eq('https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_q.jpg')
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|