flickollage 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0bf1524178358a9af7ccece83dff0511d631733f
4
+ data.tar.gz: 7b52460521ecd5ec1633f780b15c525ec9651afe
5
+ SHA512:
6
+ metadata.gz: 2acf6edde06c4bc7a5326ba2401949c1f564d9fada5236cc6f0d90ac05c963606788d564fba590bd83dd0af853a9116586747ab6796ba42187277140e40186b8
7
+ data.tar.gz: 05fdea90eb41ebd282ab9c1ee32c355de1baceeab78dd93b138a35b0516cb76f62c9919de0cc9a63137aa2ada5f8fed2fd1c7eac9ad3db5d7417268c69a151a2
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ .env
3
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format doc
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.2
3
+
4
+ Style/Documentation:
5
+ Enabled: false
6
+
7
+ Metrics/LineLength:
8
+ Max: 100
9
+
10
+ Metrics/BlockLength:
11
+ Exclude:
12
+ - spec/**/*
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+
8
+ before_script:
9
+ - sudo apt-get update
10
+ - sudo apt-get install imagemagick libmagickcore-dev libmagickwand-dev
11
+ - sudo apt-get install -qq graphicsmagick
12
+
13
+ script:
14
+ - rspec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Flickollage [![Gem Version](https://badge.fury.io/rb/flickollage.svg)](https://badge.fury.io/rb/flickollage) [![Build Status](https://travis-ci.org/alexandrz/flickollage.svg?branch=master)](https://travis-ci.org/alexandrz/flickollage)
2
+
3
+ A simple command-line tool that accepts a list of words as arguments and generates
4
+ a collage grid from ten top-rated images found on Flickr using the provided keywords.
5
+
6
+ ## Installation
7
+
8
+ You can install Flickollage from rubygems. It requires Ruby version >= 2.0 and
9
+ ImageMagick or GraphicsMagick command-line tool.
10
+
11
+ $ gem install flickollage
12
+
13
+ ## Flickr access key
14
+
15
+ You need to provide Flickr access key and shared secret. It can be done using environment
16
+ variables `FLICKR_API_KEY` and `FLICKR_SHARED_SECRET` or command-line options
17
+ `--flickr-api-key` and `--flickr-shared-key`.
18
+
19
+ ## Usage
20
+
21
+ $ flickollage generate -n 2 --rows=1 --cols=2 --width=300 --height=200 Berlin 'New York'
22
+
23
+ ![Cities Collage](spec/fixtures/cities.png)
24
+
25
+ $ flickollage generate --dict=spec/fixtures/mountains --output mountains.png
26
+
27
+ ![Mountains Collage](spec/fixtures/mountains.png)
data/bin/flickollage ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'flickollage'
5
+
6
+ Dotenv.load
7
+
8
+ Flickollage::CLI.start(ARGV)
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'flickollage'
3
+ s.version = '0.0.3'
4
+ s.date = '2017-03-05'
5
+ s.summary = 'Flickollage Collage Generator'
6
+ s.description = 'Flickollage is a simple command line tool for generating collages'
7
+ s.authors = ['Aleksandr Zykov']
8
+ s.email = 'alexandrz@gmail.com'
9
+ s.homepage = 'https://github.com/alexandrz/flickollage'
10
+ s.license = 'MIT'
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- spec/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
15
+ s.require_paths = ['lib']
16
+
17
+ s.add_dependency 'thor', '~> 0.19'
18
+ s.add_dependency 'flickraw', '~> 0.9'
19
+ s.add_dependency 'mini_magick', '~> 4.6'
20
+
21
+ s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'dotenv'
23
+ s.add_development_dependency 'vcr'
24
+ s.add_development_dependency 'webmock'
25
+ s.add_development_dependency 'rubocop', '~> 0.47.1'
26
+ end
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+ require 'dotenv'
3
+ require 'logger'
4
+
5
+ # Flickollage Command Line Interface
6
+ module Flickollage
7
+ class CLI < ::Thor
8
+ include Logger
9
+
10
+ desc 'generate [LIST OF WORDS]', 'Generate collage from the list of words'
11
+ option :dict,
12
+ type: :string, aliases: '-d', default: Dictionary.default_dict_path,
13
+ desc: 'Path of the dictionatry file'
14
+ option :output,
15
+ type: :string, aliases: '-o', default: 'collage.png',
16
+ desc: 'Output image file name'
17
+ option :flickr_api_key,
18
+ type: :string,
19
+ desc: 'Flickr API Key, can also be set using environment variable'
20
+ option :flickr_shared_secret,
21
+ type: :string,
22
+ desc: 'Flickr Shared Secret, can also be set using environment variable'
23
+ option :number, type: :numeric, aliases: '-n', default: 10
24
+ option :rows, type: :numeric, aliases: '-r', default: 5
25
+ option :cols, type: :numeric, aliases: '-c', default: 2
26
+ option :width, type: :numeric, aliases: '-w', default: 200
27
+ option :height, type: :numeric, aliases: '-h', default: 150
28
+ option :verbose, type: :boolean, aliases: '-v'
29
+ long_desc <<-LONGDESC
30
+ `flickollage generate dolomites annapurna` will generate a photo collage.
31
+
32
+ You can provide flickr api key and shared secret using options described before
33
+ or environment variables `FLICKR_API_KEY` and `FLICKR_SHARED_SECRET`.
34
+ LONGDESC
35
+ def generate(*words)
36
+ Flickollage.init_logger(options)
37
+ return unless Flickollage.configure_flickraw(options)
38
+ Flickollage::Collage.new(words, options).generate_collage
39
+ rescue Flickollage::Error => e
40
+ logger.error(e.message)
41
+ logger.debug(e.inspect)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,80 @@
1
+ module Flickollage
2
+ class Collage
3
+ include Logger
4
+ class Error < ::Flickollage::Error; end
5
+
6
+ attr_reader :dictionary
7
+ attr_reader :words
8
+ attr_reader :options
9
+ attr_reader :images
10
+
11
+ RETRY_LIMIT = 10
12
+
13
+ def initialize(words, options)
14
+ @words = words
15
+ @options = options
16
+
17
+ init_dictionary
18
+ load_images
19
+ download_images
20
+ crop_images(options[:width], options[:height])
21
+ end
22
+
23
+ def generate_collage(path = nil)
24
+ path ||= options[:output]
25
+ MiniMagick::Tool::Montage.new do |montage|
26
+ images.each { |image| montage << image.image.path }
27
+
28
+ montage.geometry "#{options[:width]}x#{options[:height]}+0+0"
29
+ montage.tile "#{options[:cols]}x#{options[:rows]}"
30
+ montage.background 'black'
31
+
32
+ montage << path
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def init_dictionary
39
+ @dictionary = Dictionary.new(options[:dict])
40
+ @dictionary.append(words)
41
+ rescue Flickollage::Dictionary::Error
42
+ @dictionary = Dictionary.new(words)
43
+ end
44
+
45
+ def download_images
46
+ images.each(&:download)
47
+ end
48
+
49
+ def crop_images(width, height)
50
+ images.each do |image|
51
+ image.crop(width, height)
52
+ end
53
+ end
54
+
55
+ def load_images
56
+ @images = []
57
+ options[:number].times do
58
+ @images << load_image
59
+ end
60
+ end
61
+
62
+ def load_image(attempt = 1)
63
+ word = dictionary.pop
64
+ not_enought_words unless word
65
+ Image.new(word)
66
+ rescue Flickollage::Image::Error
67
+ too_many_attempts if attempt == RETRY_LIMIT
68
+ load_image(attempt + 1)
69
+ end
70
+
71
+ def not_enought_words
72
+ raise Flickollage::Error,
73
+ 'Not enought words. Please, specify more words or provide a bigger dictionary.'
74
+ end
75
+
76
+ def too_many_attempts
77
+ raise Flickollage::Error, 'Could not load images. Please, try later.'
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,47 @@
1
+ module Flickollage
2
+ class Dictionary
3
+ class Error < ::Flickollage::Error; end
4
+
5
+ MAX_DICT_LENGTH = 1_500_000
6
+
7
+ COMMON_DICT_PATHS = %w(
8
+ /usr/share/dict/words
9
+ /usr/dict/words
10
+ ).freeze
11
+
12
+ attr_reader :words
13
+
14
+ def initialize(path_or_words)
15
+ @words = []
16
+ load_from_file(path_or_words) if path_or_words.is_a?(String)
17
+ @words = path_or_words.reverse if path_or_words.is_a?(Array)
18
+ end
19
+
20
+ def pop
21
+ @words.pop
22
+ end
23
+
24
+ def append(words)
25
+ @words += words.reverse
26
+ end
27
+
28
+ private
29
+
30
+ def load_from_file(path)
31
+ File.foreach(path).with_index do |line, i|
32
+ break if i >= MAX_DICT_LENGTH
33
+ line = line.chop
34
+ @words << line unless line.empty?
35
+ end
36
+ @words.shuffle!
37
+ rescue Errno::ENOENT
38
+ raise Error, 'Dictionary file not found'
39
+ end
40
+
41
+ class << self
42
+ def default_dict_path
43
+ COMMON_DICT_PATHS.find { |path| File.exist?(path) } || COMMON_DICT_PATHS.first
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ module Flickollage
2
+ class Image
3
+ include Logger
4
+ class Error < ::Flickollage::Error; end
5
+
6
+ attr_reader :word
7
+ attr_reader :url
8
+ attr_accessor :image
9
+
10
+ def initialize(word)
11
+ @word = word
12
+ search_on_flickr(word)
13
+ end
14
+
15
+ def download
16
+ logger.debug("Downloading an image: #{url}")
17
+ @image = MiniMagick::Image.open(url)
18
+ file_not_found unless @image
19
+ @image
20
+ end
21
+
22
+ def crop(width, height)
23
+ image.combine_options do |b|
24
+ b.resize "#{width}x#{height}^"
25
+ b.gravity 'Center'
26
+ b.extent "#{width}x#{height}"
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def file_not_found
33
+ raise Error, "Failed to load an image for keyword '#{word}'."
34
+ end
35
+
36
+ def search_on_flickr(word)
37
+ photo = flickr.photos.search(tags: word, sort: 'interestingness-desc', per_page: 1)[0]
38
+ raise Error, 'Could not find an image using this keyword' unless photo
39
+ @url = FlickRaw.url_b(photo)
40
+ logger.debug("Found an image for keyword '#{word}': #{@url}")
41
+ rescue FlickRaw::FailedResponse
42
+ raise ::Flickollage::Error, 'Invalid Flickr API key'
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ module Flickollage
2
+ module Logger
3
+ def logger
4
+ Flickollage.logger
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ require 'flickraw'
2
+ require 'mini_magick'
3
+
4
+ module Flickollage
5
+ class Error < StandardError; end
6
+ class << self
7
+ attr_accessor :logger
8
+
9
+ def init_logger(options = {})
10
+ Flickollage.logger = ::Logger.new(STDOUT).tap do |logger|
11
+ logger.level = options[:verbose] ? ::Logger::DEBUG : ::Logger::INFO
12
+ logger.formatter = proc do |_severity, _datetime, _progname, msg|
13
+ "#{msg}\n"
14
+ end
15
+ end
16
+ end
17
+
18
+ def configure_flickraw(options)
19
+ FlickRaw.api_key = ENV['FLICKR_API_KEY'] || options[:flickr_api_key]
20
+ FlickRaw.shared_secret = ENV['FLICKR_SHARED_SECRET'] || options[:flickr_shared_secret]
21
+ return true if FlickRaw.api_key && FlickRaw.shared_secret
22
+ logger.error 'Flickr configuration is not provided.'
23
+ false
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'flickollage/logger.rb'
29
+ require 'flickollage/dictionary.rb'
30
+ require 'flickollage/image.rb'
31
+ require 'flickollage/collage.rb'
32
+ require 'flickollage/cli.rb'
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ Mont Blanc
2
+ Monte Rosa
3
+ Liskamm
4
+ Weisshorn
5
+ Matterhorn
6
+ Dent Blanche
7
+ Grand Combin
8
+ Finsteraarhorn
9
+ Zinalrothorn
10
+ Grandes Jorasses
11
+
12
+
13
+ Alphubel
14
+ Rimpfischhorn
15
+ Aletschhorn
16
+ Strahlhorn
17
+ Dent d'Hérens
18
+ Breithorn
19
+ Jungfrau
20
+ Aiguille Verte
21
+ Mönch
22
+ Barre des Écrins
23
+ Schreckhorn
24
+ Ober Gabelhorn
Binary file
Binary file
@@ -0,0 +1,112 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.flickr.com/services/rest/
6
+ body:
7
+ encoding: US-ASCII
8
+ string: method=flickr.reflection.getMethods&format=json&nojsoncallback=1
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - FlickRaw/0.9.9
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Sun, 05 Mar 2017 11:41:25 GMT
25
+ Content-Type:
26
+ - application/json
27
+ Content-Length:
28
+ - '1438'
29
+ P3p:
30
+ - policyref="https://policies.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM
31
+ DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND
32
+ PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
33
+ X-Content-Type-Options:
34
+ - nosniff
35
+ Cache-Control:
36
+ - private
37
+ X-Served-By:
38
+ - www245.flickr.bf1.yahoo.com
39
+ X-Frame-Options:
40
+ - SAMEORIGIN
41
+ Vary:
42
+ - Accept-Encoding
43
+ Age:
44
+ - '0'
45
+ Via:
46
+ - http/1.1 fts101.flickr.bf1.yahoo.com (ApacheTrafficServer [cMsSf ]), http/1.1
47
+ e20.ycpi.lob.yahoo.com (ApacheTrafficServer [cMsSf ])
48
+ Server:
49
+ - ATS
50
+ Connection:
51
+ - keep-alive
52
+ body:
53
+ encoding: ASCII-8BIT
54
+ string: '{"methods":{"method":[{"_content":"flickr.activity.userComments"},{"_content":"flickr.activity.userPhotos"},{"_content":"flickr.auth.checkToken"},{"_content":"flickr.auth.getFrob"},{"_content":"flickr.auth.getFullToken"},{"_content":"flickr.auth.getToken"},{"_content":"flickr.auth.oauth.checkToken"},{"_content":"flickr.auth.oauth.getAccessToken"},{"_content":"flickr.blogs.getList"},{"_content":"flickr.blogs.getServices"},{"_content":"flickr.blogs.postPhoto"},{"_content":"flickr.cameras.getBrandModels"},{"_content":"flickr.cameras.getBrands"},{"_content":"flickr.collections.getInfo"},{"_content":"flickr.collections.getTree"},{"_content":"flickr.commons.getInstitutions"},{"_content":"flickr.contacts.getList"},{"_content":"flickr.contacts.getListRecentlyUploaded"},{"_content":"flickr.contacts.getPublicList"},{"_content":"flickr.contacts.getTaggingSuggestions"},{"_content":"flickr.favorites.add"},{"_content":"flickr.favorites.getContext"},{"_content":"flickr.favorites.getList"},{"_content":"flickr.favorites.getPublicList"},{"_content":"flickr.favorites.remove"},{"_content":"flickr.galleries.addPhoto"},{"_content":"flickr.galleries.create"},{"_content":"flickr.galleries.editMeta"},{"_content":"flickr.galleries.editPhoto"},{"_content":"flickr.galleries.editPhotos"},{"_content":"flickr.galleries.getInfo"},{"_content":"flickr.galleries.getList"},{"_content":"flickr.galleries.getListForPhoto"},{"_content":"flickr.galleries.getPhotos"},{"_content":"flickr.groups.browse"},{"_content":"flickr.groups.discuss.replies.add"},{"_content":"flickr.groups.discuss.replies.delete"},{"_content":"flickr.groups.discuss.replies.edit"},{"_content":"flickr.groups.discuss.replies.getInfo"},{"_content":"flickr.groups.discuss.replies.getList"},{"_content":"flickr.groups.discuss.topics.add"},{"_content":"flickr.groups.discuss.topics.getInfo"},{"_content":"flickr.groups.discuss.topics.getList"},{"_content":"flickr.groups.getInfo"},{"_content":"flickr.groups.join"},{"_content":"flickr.groups.joinRequest"},{"_content":"flickr.groups.leave"},{"_content":"flickr.groups.members.getList"},{"_content":"flickr.groups.pools.add"},{"_content":"flickr.groups.pools.getContext"},{"_content":"flickr.groups.pools.getGroups"},{"_content":"flickr.groups.pools.getPhotos"},{"_content":"flickr.groups.pools.remove"},{"_content":"flickr.groups.search"},{"_content":"flickr.interestingness.getList"},{"_content":"flickr.machinetags.getNamespaces"},{"_content":"flickr.machinetags.getPairs"},{"_content":"flickr.machinetags.getPredicates"},{"_content":"flickr.machinetags.getRecentValues"},{"_content":"flickr.machinetags.getValues"},{"_content":"flickr.panda.getList"},{"_content":"flickr.panda.getPhotos"},{"_content":"flickr.people.findByEmail"},{"_content":"flickr.people.findByUsername"},{"_content":"flickr.people.getGroups"},{"_content":"flickr.people.getInfo"},{"_content":"flickr.people.getLimits"},{"_content":"flickr.people.getPhotos"},{"_content":"flickr.people.getPhotosOf"},{"_content":"flickr.people.getPublicGroups"},{"_content":"flickr.people.getPublicPhotos"},{"_content":"flickr.people.getUploadStatus"},{"_content":"flickr.photos.addTags"},{"_content":"flickr.photos.comments.addComment"},{"_content":"flickr.photos.comments.deleteComment"},{"_content":"flickr.photos.comments.editComment"},{"_content":"flickr.photos.comments.getList"},{"_content":"flickr.photos.comments.getRecentForContacts"},{"_content":"flickr.photos.delete"},{"_content":"flickr.photos.geo.batchCorrectLocation"},{"_content":"flickr.photos.geo.correctLocation"},{"_content":"flickr.photos.geo.getLocation"},{"_content":"flickr.photos.geo.getPerms"},{"_content":"flickr.photos.geo.photosForLocation"},{"_content":"flickr.photos.geo.removeLocation"},{"_content":"flickr.photos.geo.setContext"},{"_content":"flickr.photos.geo.setLocation"},{"_content":"flickr.photos.geo.setPerms"},{"_content":"flickr.photos.getAllContexts"},{"_content":"flickr.photos.getContactsPhotos"},{"_content":"flickr.photos.getContactsPublicPhotos"},{"_content":"flickr.photos.getContext"},{"_content":"flickr.photos.getCounts"},{"_content":"flickr.photos.getExif"},{"_content":"flickr.photos.getFavorites"},{"_content":"flickr.photos.getInfo"},{"_content":"flickr.photos.getNotInSet"},{"_content":"flickr.photos.getPerms"},{"_content":"flickr.photos.getPopular"},{"_content":"flickr.photos.getRecent"},{"_content":"flickr.photos.getSizes"},{"_content":"flickr.photos.getUntagged"},{"_content":"flickr.photos.getWithGeoData"},{"_content":"flickr.photos.getWithoutGeoData"},{"_content":"flickr.photos.licenses.getInfo"},{"_content":"flickr.photos.licenses.setLicense"},{"_content":"flickr.photos.notes.add"},{"_content":"flickr.photos.notes.delete"},{"_content":"flickr.photos.notes.edit"},{"_content":"flickr.photos.people.add"},{"_content":"flickr.photos.people.delete"},{"_content":"flickr.photos.people.deleteCoords"},{"_content":"flickr.photos.people.editCoords"},{"_content":"flickr.photos.people.getList"},{"_content":"flickr.photos.recentlyUpdated"},{"_content":"flickr.photos.removeTag"},{"_content":"flickr.photos.search"},{"_content":"flickr.photos.setContentType"},{"_content":"flickr.photos.setDates"},{"_content":"flickr.photos.setMeta"},{"_content":"flickr.photos.setPerms"},{"_content":"flickr.photos.setSafetyLevel"},{"_content":"flickr.photos.setTags"},{"_content":"flickr.photos.suggestions.approveSuggestion"},{"_content":"flickr.photos.suggestions.getList"},{"_content":"flickr.photos.suggestions.rejectSuggestion"},{"_content":"flickr.photos.suggestions.removeSuggestion"},{"_content":"flickr.photos.suggestions.suggestLocation"},{"_content":"flickr.photos.transform.rotate"},{"_content":"flickr.photos.upload.checkTickets"},{"_content":"flickr.photosets.addPhoto"},{"_content":"flickr.photosets.comments.addComment"},{"_content":"flickr.photosets.comments.deleteComment"},{"_content":"flickr.photosets.comments.editComment"},{"_content":"flickr.photosets.comments.getList"},{"_content":"flickr.photosets.create"},{"_content":"flickr.photosets.delete"},{"_content":"flickr.photosets.editMeta"},{"_content":"flickr.photosets.editPhotos"},{"_content":"flickr.photosets.getContext"},{"_content":"flickr.photosets.getInfo"},{"_content":"flickr.photosets.getList"},{"_content":"flickr.photosets.getPhotos"},{"_content":"flickr.photosets.orderSets"},{"_content":"flickr.photosets.removePhoto"},{"_content":"flickr.photosets.removePhotos"},{"_content":"flickr.photosets.reorderPhotos"},{"_content":"flickr.photosets.setPrimaryPhoto"},{"_content":"flickr.places.find"},{"_content":"flickr.places.findByLatLon"},{"_content":"flickr.places.getChildrenWithPhotosPublic"},{"_content":"flickr.places.getInfo"},{"_content":"flickr.places.getInfoByUrl"},{"_content":"flickr.places.getPlaceTypes"},{"_content":"flickr.places.getShapeHistory"},{"_content":"flickr.places.getTopPlacesList"},{"_content":"flickr.places.placesForBoundingBox"},{"_content":"flickr.places.placesForContacts"},{"_content":"flickr.places.placesForTags"},{"_content":"flickr.places.placesForUser"},{"_content":"flickr.places.resolvePlaceId"},{"_content":"flickr.places.resolvePlaceURL"},{"_content":"flickr.places.tagsForPlace"},{"_content":"flickr.prefs.getContentType"},{"_content":"flickr.prefs.getGeoPerms"},{"_content":"flickr.prefs.getHidden"},{"_content":"flickr.prefs.getPrivacy"},{"_content":"flickr.prefs.getSafetyLevel"},{"_content":"flickr.profile.getProfile"},{"_content":"flickr.push.getSubscriptions"},{"_content":"flickr.push.getTopics"},{"_content":"flickr.push.subscribe"},{"_content":"flickr.push.unsubscribe"},{"_content":"flickr.reflection.getMethodInfo"},{"_content":"flickr.reflection.getMethods"},{"_content":"flickr.stats.getCollectionDomains"},{"_content":"flickr.stats.getCollectionReferrers"},{"_content":"flickr.stats.getCollectionStats"},{"_content":"flickr.stats.getCSVFiles"},{"_content":"flickr.stats.getPhotoDomains"},{"_content":"flickr.stats.getPhotoReferrers"},{"_content":"flickr.stats.getPhotosetDomains"},{"_content":"flickr.stats.getPhotosetReferrers"},{"_content":"flickr.stats.getPhotosetStats"},{"_content":"flickr.stats.getPhotoStats"},{"_content":"flickr.stats.getPhotostreamDomains"},{"_content":"flickr.stats.getPhotostreamReferrers"},{"_content":"flickr.stats.getPhotostreamStats"},{"_content":"flickr.stats.getPopularPhotos"},{"_content":"flickr.stats.getTotalViews"},{"_content":"flickr.tags.getClusterPhotos"},{"_content":"flickr.tags.getClusters"},{"_content":"flickr.tags.getHotList"},{"_content":"flickr.tags.getListPhoto"},{"_content":"flickr.tags.getListUser"},{"_content":"flickr.tags.getListUserPopular"},{"_content":"flickr.tags.getListUserRaw"},{"_content":"flickr.tags.getMostFrequentlyUsed"},{"_content":"flickr.tags.getRelated"},{"_content":"flickr.test.echo"},{"_content":"flickr.test.login"},{"_content":"flickr.test.null"},{"_content":"flickr.urls.getGroup"},{"_content":"flickr.urls.getUserPhotos"},{"_content":"flickr.urls.getUserProfile"},{"_content":"flickr.urls.lookupGallery"},{"_content":"flickr.urls.lookupGroup"},{"_content":"flickr.urls.lookupUser"}]},"stat":"ok"}'
55
+ http_version:
56
+ recorded_at: Sun, 05 Mar 2017 11:41:25 GMT
57
+ - request:
58
+ method: post
59
+ uri: https://api.flickr.com/services/rest/
60
+ body:
61
+ encoding: US-ASCII
62
+ string: tags=dolomites&sort=interestingness-desc&per_page=1&method=flickr.photos.search&format=json&nojsoncallback=1
63
+ headers:
64
+ Accept-Encoding:
65
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
66
+ Accept:
67
+ - "*/*"
68
+ User-Agent:
69
+ - FlickRaw/0.9.9
70
+ Content-Type:
71
+ - application/x-www-form-urlencoded
72
+ response:
73
+ status:
74
+ code: 200
75
+ message: OK
76
+ headers:
77
+ Date:
78
+ - Sun, 05 Mar 2017 11:41:25 GMT
79
+ Content-Type:
80
+ - application/json
81
+ Content-Length:
82
+ - '196'
83
+ P3p:
84
+ - policyref="https://policies.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM
85
+ DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND
86
+ PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
87
+ X-Content-Type-Options:
88
+ - nosniff
89
+ Cache-Control:
90
+ - private
91
+ X-Served-By:
92
+ - bm-www930.flickr.bf1.yahoo.com
93
+ X-Frame-Options:
94
+ - SAMEORIGIN
95
+ Vary:
96
+ - Accept-Encoding
97
+ Age:
98
+ - '0'
99
+ Via:
100
+ - http/1.1 fts110.flickr.bf1.yahoo.com (ApacheTrafficServer [cMsSf ]), http/1.1
101
+ e22.ycpi.lob.yahoo.com (ApacheTrafficServer [cMsSf ])
102
+ Server:
103
+ - ATS
104
+ Connection:
105
+ - keep-alive
106
+ body:
107
+ encoding: ASCII-8BIT
108
+ string: '{"photos":{"page":1,"pages":159876,"perpage":1,"total":"159876","photo":[{"id":"33209118996","owner":"48059305@N03","secret":"1e1d0a66ee","server":"758","farm":1,"title":"Magic
109
+ dawn at Lusia alp","ispublic":1,"isfriend":0,"isfamily":0}]},"stat":"ok"}'
110
+ http_version:
111
+ recorded_at: Sun, 05 Mar 2017 11:41:25 GMT
112
+ recorded_with: VCR 2.9.2