flickr_collager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 723b3acc6258a9217c868acbc4117f68547e95d21983b5b4626662c0a9374656
4
+ data.tar.gz: 62383cf3bbe4e0c023e3d4a5c4b2e638b9a21bb6cad6ca82009ef47378a78995
5
+ SHA512:
6
+ metadata.gz: b2b24e7f2e8fcebe702e20830baa7b16b33335bb5164fb20c7c6435387c8c5fc70131b9917b1591006f8580b213e2191b3ee944a70c2b9faaa45ebd94729541e
7
+ data.tar.gz: ecbdb075230314daa83599e7b5b22d0f521119ac45c5d41c4475c60f82167341cf2694748df9b0081e562d5a723daa600a037b36bc749d1f53325da76c1e1dea
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ flickr_collager (0.0.1)
5
+ minitest (= 5.9.0)
6
+ rake (>= 12.3.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ minitest (5.9.0)
12
+ rake (13.0.1)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ flickr_collager!
19
+
20
+ BUNDLED WITH
21
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
@@ -0,0 +1,32 @@
1
+ #Flickr Collager
2
+
3
+ ## Flickr image search and Collage Creator.
4
+
5
+
6
+ ## To build
7
+ There is a rakefile for all the tests, the list of all Rake tasks available can be
8
+ seen by :
9
+
10
+ ```rake -T```
11
+
12
+ Also the project can be made into a gem
13
+
14
+ by doing a ```bundle``` and then building the gem by executing ```gem build flickr_collager.gemspec```
15
+
16
+ Once the gem in built it can be installed by :
17
+
18
+ ```gem install flickr_collager-0.0.1.gem```
19
+
20
+ ## Prerequisites :
21
+
22
+ Before Installing the gem make sure Imagemagick is installed.
23
+
24
+ ```brew install imagemagick```
25
+
26
+ Once the gem is installed into installs a executable called ```collage``` which can used as follows :
27
+
28
+ ## Usage :
29
+
30
+ ```collage -w pen,pencil,rose,women,brizil,everest,sunshine --out-file collage.png```
31
+
32
+ The project compensates for missing search terms if less than 10 from a dictionary.
@@ -0,0 +1,45 @@
1
+ namespace :app do
2
+ namespace :test do
3
+ task :collage do
4
+ desc 'Tests collage'
5
+ system 'ruby spec/test_collage.rb'
6
+ end
7
+
8
+ task :flickr do
9
+ desc 'Test Flickr Search API'
10
+ system 'ruby spec/test_flickr_search.rb'
11
+ end
12
+
13
+ task :magick do
14
+ desc 'Test ImageMagick interface API'
15
+ system 'ruby spec/test_magick_collage.rb'
16
+ end
17
+ end
18
+
19
+ namespace :gem do
20
+ task :build do
21
+ desc "Build The Flickr Collager 'gem'."
22
+ system 'gem build flickr_collager.gemspec'
23
+ end
24
+ end
25
+
26
+ task all: [
27
+ 'app:test:collage', 'app:test:flickr',
28
+ 'app:test:magick', 'app:gem:build'
29
+ ]
30
+ end
31
+
32
+ desc 'All Tasks'
33
+ task default: 'app:all'
34
+
35
+ desc 'Test Collage'
36
+ task 'test:collage' => 'app:test:collage'
37
+
38
+ desc 'Test Flickr Search API'
39
+ task 'test:flickr' => 'app:test:flickr'
40
+
41
+ desc 'Test ImageMagick inetrface API'
42
+ task 'test:magick' => 'app:test:magick'
43
+
44
+ desc "Build the Flickr Collager 'gem'"
45
+ task 'gem:build' => 'app:gem:build'
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require_relative '../loader'
4
+
5
+ class CollageParser
6
+ def self.parse(params)
7
+ args = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = 'Usage: collage -w word1,word2..wordN -o outfile.png'
10
+ opts.on('-w', '--words ', Array, 'Flickr Search Words') do |w|
11
+ args[:words] = w
12
+ end
13
+
14
+ opts.on('-o', '--out-file ', String, 'Output File') do |f|
15
+ args[:outfile] = f
16
+ end
17
+
18
+ opts.on_tail('-h', '--help', 'Show this message') do
19
+ puts opts
20
+ exit
21
+ end
22
+ end.parse! params
23
+ args
24
+ end
25
+ end
26
+
27
+ args = if ARGV.empty?
28
+ %w(--help)
29
+ else
30
+ ARGV
31
+ end
32
+
33
+ options = CollageParser.parse args
34
+ collage = Collage.new options[:words]
35
+ collage.download_images.build options[:outfile]
36
+
37
+ if RUBY_PLATFORM.downcase =~ /darwin/
38
+ fork { system "open #{options[:outfile]}" }
39
+ elsif RUBY_PLATFORM.downcase =~ /linux/
40
+ fork { system "eog #{options[:outfile]}" }
41
+ elsif RUBY_PLATFORM.downcase =~ /mswin/
42
+ puts 'Dude Seriously, Windows?'
43
+ end
@@ -0,0 +1,22 @@
1
+ #Collage Search app config options.
2
+
3
+ options:
4
+ MIN_WORD_COUNT: 10
5
+ DICT_PATH: '/usr/share/dict/words'
6
+
7
+ flickr:
8
+ API_KEY: '2ee12fb262a51e63f4e3c04f092bec55'
9
+ API_SECRET: '6002a6a1481c6fe0'
10
+ API_URL: 'https://api.flickr.com/services/rest/?'
11
+ API_FORMAT: 'json'
12
+ API_METHOD: 'flickr.photos.search'
13
+ API_SORT: 'relevance'
14
+ TEMP_DIR: '/tmp/collage'
15
+
16
+ imagemagick:
17
+ ROWS: 2
18
+ COLUMNS: 5
19
+ BG_COLOR: 'white'
20
+ BORDR_SIZE: '+5+5'
21
+ IMG_SIZE: 1000
22
+
@@ -0,0 +1,22 @@
1
+ require_relative 'lib/string_refinements'
2
+ require_relative 'lib/app_config'
3
+ require 'fileutils'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'flickr_collager'
7
+ gem.version = AppConfig::Version::STRING
8
+ gem.authors = ['Pankaj Doharey']
9
+ gem.email = ['pankajdoharey@gmail.com']
10
+ gem.licenses = ['WTFPL']
11
+ gem.description = 'Collage Builder'
12
+ gem.summary = 'Flickr based Collage builder'
13
+ gem.homepage = 'http://selfdotsend.com'
14
+
15
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+ gem.add_dependency 'rake', '>= 12.3.3'
20
+ gem.add_dependency 'minitest', '5.9.0'
21
+ gem.post_install_message = AppConfig::PostInstall.message
22
+ end
@@ -0,0 +1,28 @@
1
+ # Main Container module to namespace the application and its various parts
2
+ # are submodules inside this module.
3
+ module AppConfig
4
+ using Camelizer
5
+
6
+ def self.included(klass)
7
+ config = YAML.load_file(path_finder)
8
+
9
+ Dir.glob File.expand_path('../app_config/*.rb', __FILE__) do |file|
10
+ require file
11
+
12
+ # Set app configration from config.yaml
13
+ file_basename = File.basename(file, '.rb')
14
+ (klass.const_get file_basename.camelize).options config
15
+ end
16
+ end
17
+
18
+ def self.path_finder
19
+ if File.exist?('config.yaml')
20
+ 'config.yaml'
21
+ else
22
+ File.expand_path('~/.collage/config.yaml', __FILE__)
23
+ end
24
+ end
25
+ end
26
+
27
+ require_relative 'version'
28
+ require_relative 'post_install'
@@ -0,0 +1,74 @@
1
+ module AppConfig
2
+ module FlickrSearch
3
+ def self.options(config)
4
+ config['flickr'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+
9
+ def search_and_save_flickr_image(text)
10
+ uri = URI(search_flickr_for(text))
11
+ download_and_write_to_file uri
12
+ end
13
+
14
+ def search_flickr_for(search_term)
15
+ response_body = build_query_for(search_term).read_body
16
+ response_data = response_body.to_s.match(/\[([^\}]+)\}/)[0]
17
+ .sub(/^\[/, '')
18
+ json_response = JSON.parse response_data
19
+ construct_image_url_from json_response
20
+ rescue JSON::ParserError
21
+ puts "\nJSON Parser Error Detected. Retrying ..."
22
+ search_flickr_for(new_word)
23
+ end
24
+
25
+ def image_list
26
+ Dir[TEMP_DIR + '/*']
27
+ end
28
+
29
+ private
30
+
31
+ def download_and_write_to_file(uri)
32
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
33
+ filepath = File.join(TEMP_DIR, File.basename(uri.path))
34
+ open(filepath, 'wb') do |file|
35
+ file.write http.get(uri).read_body
36
+ file.close
37
+ end
38
+ end
39
+ rescue URI::InvalidURIError
40
+ puts 'URI::InvalidURIError Detected'
41
+ end
42
+
43
+ def build_query_for(word)
44
+ uri = uri_for word
45
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
46
+ request = Net::HTTP::Get.new uri
47
+ http.request request
48
+ end
49
+ end
50
+
51
+ def uri_for(search_text)
52
+ URI(
53
+ uri_builder(
54
+ url: API_URL, format: API_FORMAT, sort: API_SORT,
55
+ method: API_METHOD, text: search_text,
56
+ tag_mode: :all, api_key: API_KEY
57
+ )
58
+ )
59
+ end
60
+
61
+ def uri_builder(params = {})
62
+ url = params[:url]
63
+ params.reject! { |k, _| k == :url }
64
+ url + params.each_pair.map { |k, v| "#{k}=#{v}" }.join('&')
65
+ end
66
+
67
+ def construct_image_url_from(json)
68
+ <<-URL.gsub(/\s+/, '').strip
69
+ https://farm#{json['farm']}.staticflickr.com/
70
+ #{json['server']}/#{json['id']}_#{json['secret']}.jpg
71
+ URL
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ module AppConfig
2
+ module MagickCollage
3
+ def self.options(config)
4
+ config['imagemagick'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+
9
+ def build(file = 'collage.jpg')
10
+ crop_images
11
+ system <<-COMMAND.gsub(/\s+|\n/, ' ')
12
+ montage #{::AppConfig::FlickrSearch::TEMP_DIR}/cropped*.png
13
+ -geometry #{IMG_SIZE / COLUMNS}x#{IMG_SIZE / COLUMNS}#{BORDR_SIZE}
14
+ background #{BG_COLOR}
15
+ -tile #{COLUMNS}x#{ROWS} #{file}
16
+ 2>/dev/null
17
+ COMMAND
18
+ end
19
+
20
+ def crop_images
21
+ system <<-COMMAND.gsub(/\s+|\n/, ' ')
22
+ convert
23
+ #{::AppConfig::FlickrSearch::TEMP_DIR}/*.jpg -gamma .45455
24
+ -crop #{IMG_SIZE / COLUMNS}x#{IMG_SIZE / COLUMNS}+0+0 +repage
25
+ -gamma 2.2 #{::AppConfig::FlickrSearch::TEMP_DIR}/cropped.png
26
+ 2>/dev/null
27
+ COMMAND
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module AppConfig
2
+ module Options
3
+ def self.options(config)
4
+ config['options'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ # Collage is the main class that kicks off various code for searching,
2
+ # downloading and stitching together images to produce collage
3
+ class Collage
4
+ attr_reader :search_words
5
+
6
+ def initialize(words)
7
+ create_and_clean_temp_directory
8
+ @search_words = fill_missing words
9
+ end
10
+
11
+ def download_images
12
+ Thread.new { animate_cursor }.run
13
+ @search_words.each_slice(COLUMNS) do |slice|
14
+ slice.map do |word|
15
+ Thread.new do
16
+ search_and_save_flickr_image word
17
+ print "**"
18
+ end.run
19
+ end.each(&:join)
20
+ end
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ def create_and_clean_temp_directory
27
+ FileUtils.remove_dir(TEMP_DIR) if File.directory?(TEMP_DIR)
28
+ FileUtils.mkdir(TEMP_DIR)
29
+ end
30
+
31
+ def fill_missing(words)
32
+ if words.count < MIN_WORD_COUNT
33
+ (MIN_WORD_COUNT - words.count).times { words << new_word }
34
+ end
35
+ words
36
+ end
37
+
38
+ def new_word
39
+ word = DICTIONARY.sample
40
+ if ascii?(word)
41
+ word
42
+ else
43
+ puts "\nNon-Ascii Word Detected : #{word}. Retrying ..."
44
+ new_word
45
+ end
46
+ end
47
+
48
+ def ascii?(word)
49
+ word.force_encoding('UTF-8').ascii_only?
50
+ end
51
+
52
+ def animate_cursor
53
+ char = %w(| / - \\).cycle
54
+ loop do
55
+ print "\b"
56
+ print "#{char.next}"
57
+ sleep 0.05
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ module AppConfig
2
+ module Dictionary
3
+ def self.included(klass)
4
+ klass.const_set(:DICTIONARY, File.read(klass::DICT_PATH).split("\n"))
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module AppConfig
2
+ module PostInstall
3
+ def self.message
4
+ unless File.directory?(File.expand_path('~/.collage', __FILE__))
5
+ FileUtils.mkdir(File.expand_path('~/.collage/'))
6
+ FileUtils.cp('config.yaml', File.expand_path('~/.collage/config.yaml'))
7
+ puts <<-MESSAGE
8
+ A basic Yaml Configuration file has been copied to ~/.collage/config.yaml
9
+ you modify that to change some setting.
10
+ MESSAGE
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module Camelizer
2
+ refine String do
3
+ def camelize
4
+ split('_').map(&:capitalize).join
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module AppConfig
2
+ module Version
3
+ STRING = '0.0.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ ['../lib', '../lib/app_config'].each do |dir|
2
+ $LOAD_PATH << File.expand_path(dir, __FILE__)
3
+ end
4
+
5
+ require 'yaml'
6
+ require 'net/http'
7
+ require 'json'
8
+ require 'fileutils'
9
+ require 'string_refinements'
10
+ require 'app_config'
11
+ require 'dictionary'
12
+ require 'collage'
13
+
14
+ class Collage
15
+ include AppConfig
16
+ include Options
17
+ include Dictionary
18
+ include FlickrSearch
19
+ include MagickCollage
20
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../loader'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+ require 'minitest/pride' # Colorful tests
5
+
6
+ def sample_10_words
7
+ ARGV[0..9] = 'cat', 'dog', 'crow', 'cow', 'elephant',
8
+ 'tree_snake', 'women', 'red', 'tower', 'beach'
9
+ end
10
+
11
+ def sample_8_words
12
+ ARGV[0..7] = 'cat', 'dog', 'crow', 'cow', 'elephant',
13
+ 'tree_snake', 'women', 'red'
14
+ end
15
+
16
+ def sample_5_words
17
+ ARGV[0..5] = 'cat', 'dog', 'crow', 'cow', 'elephant'
18
+ end
19
+
20
+ def sample_2_words
21
+ ARGV[0..1] = 'cat', 'dog'
22
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Collage do
5
+ before do
6
+ @collage = Collage.new(sample_10_words)
7
+ end
8
+ it 'should be able to read the words as commandline options and store them' do
9
+ @collage.search_words.count.must_equal 10
10
+ end
11
+
12
+ it 'should have 10 exact words in ARGV' do
13
+ @collage.search_words.must_equal ARGV[0..9]
14
+ end
15
+
16
+ it 'should verify the search wordcount and add 2 more words if required' do
17
+ @collage = Collage.new(sample_8_words)
18
+ @collage.search_words.count.must_equal 10
19
+ end
20
+
21
+ it 'should verify the search wordcount and add 5 more words if required' do
22
+ @collage = Collage.new(sample_5_words)
23
+ @collage.search_words.count.must_equal 10
24
+ end
25
+
26
+ it 'should verify the search wordcount and add 8 more words if required' do
27
+ @collage = Collage.new(sample_2_words)
28
+ @collage.search_words.count.must_equal 10
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe ::AppConfig::FlickrSearch do
5
+ before do
6
+ @collage = Collage.new(sample_10_words)
7
+ @dict = File.read('/usr/share/dict/words').split("\n")
8
+ end
9
+
10
+ it 'should be able to search flickr api for a given search term' do
11
+ @collage.search_and_save_flickr_image(@dict.sample)
12
+ @collage.get_image_list.count.must_equal 1
13
+ end
14
+
15
+ it 'should search flickr api with sample words and store all images' do
16
+ @collage.download_images.get_image_list.count.must_equal 10
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe ::AppConfig::MagickCollage do
5
+ before do
6
+ @collage = Collage.new(sample_5_words)
7
+ end
8
+
9
+ it 'should crop the images' do
10
+ @collage.download_images
11
+ @collage.crop_images
12
+ @collage.get_image_list.select { |i| i =~ /cropped/ }.count.must_equal 10
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flickr_collager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pankaj Doharey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 12.3.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 12.3.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 5.9.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 5.9.0
41
+ description: Collage Builder
42
+ email:
43
+ - pankajdoharey@gmail.com
44
+ executables:
45
+ - collage
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - bin/collage
55
+ - config.yaml
56
+ - flickr_collager.gemspec
57
+ - lib/app_config.rb
58
+ - lib/app_config/flickr_search.rb
59
+ - lib/app_config/magick_collage.rb
60
+ - lib/app_config/options.rb
61
+ - lib/collage.rb
62
+ - lib/dictionary.rb
63
+ - lib/post_install.rb
64
+ - lib/string_refinements.rb
65
+ - lib/version.rb
66
+ - loader.rb
67
+ - spec/spec_helper.rb
68
+ - spec/test_collage.rb
69
+ - spec/test_flickr_search.rb
70
+ - spec/test_magick_collage.rb
71
+ homepage: http://selfdotsend.com
72
+ licenses:
73
+ - WTFPL
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.7.6
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Flickr based Collage builder
95
+ test_files:
96
+ - spec/spec_helper.rb
97
+ - spec/test_collage.rb
98
+ - spec/test_flickr_search.rb
99
+ - spec/test_magick_collage.rb