royw-tmdb 0.1.0 → 0.1.1

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.
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 0
2
+ :patch: 1
3
3
  :major: 0
4
4
  :minor: 1
@@ -8,6 +8,14 @@ class String
8
8
  end
9
9
  end
10
10
 
11
+ # this handles unicode characters by converting each byte to "%XX"
12
+ # where XX is the hex value
13
+ my_extension("escape_unicode") do
14
+ def escape_unicode
15
+ self.each_byte.collect{|c| c.to_i > 127 ? "%#{c.to_i.to_s(16)}" : c.chr}.join('')
16
+ end
17
+ end
18
+
11
19
  my_extension("strip_tags") do
12
20
  def strip_tags
13
21
  gsub(/<\/?[^>]*>/, "")
@@ -8,6 +8,7 @@ require 'xmlsimple'
8
8
 
9
9
  require 'tmdb/optional_logger'
10
10
  require 'tmdb/tmdb_movie'
11
+ require 'tmdb/tmdb_image'
11
12
  require 'tmdb/tmdb_profile'
12
13
  require 'module_extensions'
13
14
  require 'string_extensions'
@@ -9,23 +9,35 @@ class OptionalLogger
9
9
  end
10
10
 
11
11
  # debug {...}
12
- def debug(&blk)
13
- @logger.debug(blk.call) unless @logger.nil?
12
+ def debug(msg=nil, &blk)
13
+ unless @logger.nil?
14
+ @logger.debug(msg) unless msg.nil?
15
+ @logger.debug(blk.call) unless blk.nil?
16
+ end
14
17
  end
15
18
 
16
19
  # info {...}
17
- def info(&blk)
18
- @logger.info(blk.call) unless @logger.nil?
20
+ def info(msg=nil, &blk)
21
+ unless @logger.nil?
22
+ @logger.info(msg) unless msg.nil?
23
+ @logger.info(blk.call) unless blk.nil?
24
+ end
19
25
  end
20
26
 
21
27
  # warn {...}
22
- def warn(&blk)
23
- @logger.warn(blk.call) unless @logger.nil?
28
+ def warn(msg=nil, &blk)
29
+ unless @logger.nil?
30
+ @logger.warn(msg) unless msg.nil?
31
+ @logger.warn(blk.call) unless blk.nil?
32
+ end
24
33
  end
25
34
 
26
35
  # error {...}
27
- def error(&blk)
28
- @logger.error(blk.call) unless @logger.nil?
36
+ def error(msg=nil, &blk)
37
+ unless @logger.nil?
38
+ @logger.error(msg) unless msg.nil?
39
+ @logger.error(blk.call) unless blk.nil?
40
+ end
29
41
  end
30
42
  end
31
43
 
@@ -0,0 +1,155 @@
1
+ class TmdbImage
2
+ attr_reader :imdb_id
3
+
4
+ # imdb_id => String IMDB ID either with or without the 'tt' prefix
5
+ # api_key => String containing the themovieDb.com API key
6
+ # logger => nil or logger instance
7
+ def initialize(imdb_id, api_key, logger)
8
+ @imdb_id = imdb_id
9
+ @api_key = api_key
10
+ @logger = OptionalLogger.new(logger)
11
+ end
12
+
13
+ # return an Array of fanart sizes as Strings
14
+ def fanart_sizes
15
+ ['original', 'mid', 'thumb']
16
+ end
17
+
18
+ # return an Array of poster sizes as Strings
19
+ def poster_sizes
20
+ ['original', 'mid', 'thumb', 'cover']
21
+ end
22
+
23
+ # return nil or the source url to the given size (must be in fanart_sizes) fanart
24
+ # optionally save the image to dest_filespec unless dest_filespec is nil
25
+ def fanart(size, dest_filespec=nil)
26
+ src_url = nil
27
+ if fanart_sizes.include?(size)
28
+ src_url = image_url(@imdb_id, 'fanarts', size)
29
+ copy_image(src_url, dest_filespec) unless dest_filespec.nil?
30
+ end
31
+ src_url
32
+ end
33
+
34
+ # return nil or the source url to the given size (must be in poster_sizes) poster
35
+ # optionally save the image to dest_filespec unless dest_filespec is nil
36
+ def poster(size, dest_filespec=nil)
37
+ src_url = nil
38
+ if poster_sizes.include?(size)
39
+ src_url = image_url(@imdb_id, 'posters', size)
40
+ copy_image(src_url, dest_filespec) unless dest_filespec.nil?
41
+ end
42
+ src_url
43
+ end
44
+
45
+ # return nil or the source url to the 'original' size fanart
46
+ # optionally save the image to dest_filespec unless dest_filespec is nil
47
+ def fanart_original(dest_filespec=nil)
48
+ fanart('original', dest_filespec)
49
+ end
50
+
51
+ # return nil or the source url to the 'mid' size fanart
52
+ # optionally save the image to dest_filespec unless dest_filespec is nil
53
+ def fanart_mid(dest_filespec=nil)
54
+ fanart('mid', dest_filespec)
55
+ end
56
+
57
+ # return nil or the source url to the 'thumb' size fanart
58
+ # optionally save the image to dest_filespec unless dest_filespec is nil
59
+ def fanart_thumb(dest_filespec=nil)
60
+ fanart('thumb', dest_filespec)
61
+ end
62
+
63
+ # return nil or the source url to the 'original' size poster
64
+ # optionally save the image to dest_filespec unless dest_filespec is nil
65
+ def poster_original(dest_filespec=nil)
66
+ poster('original', dest_filespec)
67
+ end
68
+
69
+ # return nil or the source url to the 'mid' size poster
70
+ # optionally save the image to dest_filespec unless dest_filespec is nil
71
+ def poster_mid(dest_filespec=nil)
72
+ poster('original', dest_filespec)
73
+ end
74
+
75
+ # return nil or the source url to the 'thumb' size poster
76
+ # optionally save the image to dest_filespec unless dest_filespec is nil
77
+ def poster_thumb(dest_filespec=nil)
78
+ poster('original', dest_filespec)
79
+ end
80
+
81
+ # return nil or the source url to the 'cover' size poster
82
+ # optionally save the image to dest_filespec unless dest_filespec is nil
83
+ def poster_cover(dest_filespec=nil)
84
+ poster('original', dest_filespec)
85
+ end
86
+
87
+ protected
88
+
89
+ # find the URL to the image
90
+ # imdb_id => IMDB ID with or without 'tt' prefix
91
+ # type => either 'fanart' or 'poster' String
92
+ # size => member of either fanart_sizes or poster_sizes
93
+ def image_url(imdb_id, type, size)
94
+ src_url = nil
95
+ profile = TmdbProfile.first(:imdb_id => imdb_id, :api_key => @api_key, :filespec => nil, :logger => @logger)
96
+ indexes = {}
97
+ unless profile.nil? || profile.movie.blank?
98
+ movie = profile.movie
99
+ unless movie[type].blank?
100
+ images = movie[type]
101
+ images.each do |image|
102
+ if image['size'] == size
103
+ @logger.debug { "#{image.inspect}" }
104
+ src_url = image['content']
105
+ end
106
+ end
107
+ end
108
+ end
109
+ src_url
110
+ end
111
+
112
+ # download the fanart
113
+ def copy_image(src_url, dest_filespec)
114
+ begin
115
+ extension = File.extname(src_url)
116
+ unless extension.blank?
117
+ dest_filespec += extension
118
+ end
119
+ data = fetch(src_url.escape_unicode)
120
+ File.open(dest_filespec, 'w') do |file|
121
+ file.print(data)
122
+ end
123
+ rescue Exception => e
124
+ @logger.error { "Error fetching image.\n src_url => #{src_url},\n dest_filespec => #{dest_filespec}\n #{e.to_s}" }
125
+ end
126
+ end
127
+
128
+ MAX_ATTEMPTS = 3
129
+ SECONDS_BETWEEN_RETRIES = 1.0
130
+
131
+ # fetch the page retrying on error up to MAX_ATTEMPTS with a pause
132
+ # of SECONDS_BETWEEN_RETRIES seconds between retries
133
+ def fetch(page)
134
+ doc = nil
135
+ attempts = 0
136
+ begin
137
+ doc = read_page(page)
138
+ rescue Exception => e
139
+ attempts += 1
140
+ if attempts > MAX_ATTEMPTS
141
+ raise
142
+ else
143
+ sleep SECONDS_BETWEEN_RETRIES
144
+ retry
145
+ end
146
+ end
147
+ doc
148
+ end
149
+
150
+ # makes reading from cache during specs possible
151
+ def read_page(src_url)
152
+ open(src_url).read
153
+ end
154
+
155
+ end
@@ -2,9 +2,15 @@ class TmdbMovie
2
2
 
3
3
  attr_reader :query, :document
4
4
 
5
- def initialize(ident, key)
5
+ def initialize(ident, key, logger)
6
6
  @imdb_id = 'tt' + ident.gsub(/^tt/, '') unless ident.blank?
7
+ @api_key = key
7
8
  @query = "http://api.themoviedb.org/2.0/Movie.imdbLookup?imdb_id=#{@imdb_id}&api_key=#{key}"
9
+ @logger = OptionalLogger.new(logger)
10
+ end
11
+
12
+ def image
13
+ TmdbImage.new(@imdb_id, @api_key, @logger)
8
14
  end
9
15
 
10
16
  def fanarts
@@ -100,7 +106,7 @@ class TmdbMovie
100
106
  # Fetch the document with retry to handle the occasional glitches
101
107
  def document
102
108
  if @document.nil?
103
- html = fetch(self.query)
109
+ html = fetch(self.query.escape_unicode)
104
110
  @document = XmlSimple.xml_in(html)
105
111
  @document = nil if @document['totalResults'] == ['0']
106
112
  end
@@ -49,6 +49,8 @@ class TmdbProfile
49
49
 
50
50
  attr_reader :imdb_id, :movie
51
51
 
52
+ # convert to xml
53
+ # returns String (either empty or containing the xml)
52
54
  def to_xml
53
55
  xml = ''
54
56
  unless @movie.blank?
@@ -58,6 +60,11 @@ class TmdbProfile
58
60
  xml
59
61
  end
60
62
 
63
+ # return the TmdbImage for this profile
64
+ def image
65
+ TmdbImage.new(@imdb_id.gsub(/^tt/, ''), @api_key, @logger)
66
+ end
67
+
61
68
  protected
62
69
 
63
70
  def load
@@ -67,7 +74,7 @@ class TmdbProfile
67
74
  @movie = from_xml(open(@filespec).read)
68
75
  elsif !@imdb_id.blank?
69
76
  @logger.debug { "loading movie from tmdb.com, filespec=> #{@filespec.inspect}" }
70
- @movie = TmdbMovie.new(@imdb_id.gsub(/^tt/, ''), @api_key).to_hash
77
+ @movie = TmdbMovie.new(@imdb_id.gsub(/^tt/, ''), @api_key, @logger).to_hash
71
78
  save(@filespec) unless @filespec.blank?
72
79
  end
73
80
  unless @movie.blank?
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ require 'ruby-debug'
5
+
6
+ # Time to add your specs!
7
+ # http://rspec.info/
8
+
9
+ TMDB_API_KEY = '7a2f6eb9b6aa01651000f0a9324db835'
10
+
11
+ describe "TmdbImage" do
12
+
13
+ before(:all) do
14
+ @logger = nil
15
+ File.mkdirs(TMPDIR)
16
+ end
17
+
18
+ before(:each) do
19
+ # tt0465234 => National Treasure: Book of Secrets
20
+ @image = TmdbImage.new('tt0465234', TMDB_API_KEY, @logger)
21
+ end
22
+
23
+ after(:each) do
24
+ Dir.glob(File.join(TMPDIR,'tmdb_image_spec*')).each { |filename| File.delete(filename) }
25
+ end
26
+
27
+ it "should find by imdb_id" do
28
+ @image.should_not == nil
29
+ end
30
+
31
+ it "should find image via profile" do
32
+ profile = TmdbProfile.first(:imdb_id => 'tt0465234', :api_key => TMDB_API_KEY, :logger => @logger)
33
+ profile.image.imdb_id.should == '0465234'
34
+ end
35
+
36
+ it "should find image via movie" do
37
+ profile = TmdbMovie.new('tt0465234', TMDB_API_KEY, @logger)
38
+ profile.image.imdb_id.should == 'tt0465234'
39
+ end
40
+
41
+ it "should find original size fanart" do
42
+ src_url = nil
43
+ src_url = @image.fanart_original unless @image.nil?
44
+ src_url.should_not be_nil
45
+ end
46
+
47
+ it "should find mid size fanart" do
48
+ src_url = nil
49
+ src_url = @image.fanart_mid unless @image.nil?
50
+ src_url.should_not be_nil
51
+ end
52
+
53
+ it "should find thumb size fanart" do
54
+ src_url = nil
55
+ src_url = @image.fanart_thumb unless @image.nil?
56
+ src_url.should_not be_nil
57
+ end
58
+
59
+ it "should find original size poster" do
60
+ src_url = nil
61
+ src_url = @image.poster_original unless @image.nil?
62
+ src_url.should_not be_nil
63
+ end
64
+
65
+ it "should find mid size poster" do
66
+ src_url = nil
67
+ src_url = @image.poster_mid unless @image.nil?
68
+ src_url.should_not be_nil
69
+ end
70
+
71
+ it "should find thumb size poster" do
72
+ src_url = nil
73
+ src_url = @image.poster_thumb unless @image.nil?
74
+ src_url.should_not be_nil
75
+ end
76
+
77
+ it "should find cover size poster" do
78
+ src_url = nil
79
+ src_url = @image.poster_cover unless @image.nil?
80
+ src_url.should_not be_nil
81
+ end
82
+
83
+ it "should find all fanarts" do
84
+ buf = []
85
+ @image.fanart_sizes.each do |size|
86
+ buf << "fanart(#{size}) returned nil" if @image.fanart(size).nil?
87
+ end
88
+ puts buf.join("\n") unless buf.empty?
89
+ buf.empty?.should be_true
90
+ end
91
+
92
+ it "should find all posters" do
93
+ buf = []
94
+ @image.poster_sizes.each do |size|
95
+ buf << "poster(#{size}) returned nil" if @image.poster(size).nil?
96
+ end
97
+ puts buf.join("\n") unless buf.empty?
98
+ buf.empty?.should be_true
99
+ end
100
+
101
+ # the same routine is used by all the getters to fetch the image so only
102
+ # need to test one case
103
+ it "should fetch fanart" do
104
+ filespec = get_temp_filename
105
+ src_url = @image.fanart_original(filespec)
106
+ filespec += File.extname(src_url)
107
+ (File.exist?(filespec).should be_true) && (File.size(filespec).should > 0)
108
+ end
109
+
110
+ def get_temp_filename
111
+ outfile = Tempfile.new('tmdb_image_spec', TMPDIR)
112
+ filespec = outfile.path
113
+ outfile.unlink
114
+ filespec
115
+ end
116
+
117
+ end
118
+
@@ -8,11 +8,12 @@ TMDB_API_KEY = '7a2f6eb9b6aa01651000f0a9324db835'
8
8
  describe "TmdbMovie" do
9
9
 
10
10
  before(:all) do
11
+ @logger = nil
11
12
  File.mkdirs(TMPDIR)
12
13
  end
13
14
 
14
15
  before(:each) do
15
- @profile = TmdbMovie.new('tt0465234', TMDB_API_KEY)
16
+ @profile = TmdbMovie.new('tt0465234', TMDB_API_KEY, @logger)
16
17
  end
17
18
 
18
19
  after(:each) do
@@ -68,7 +69,7 @@ describe "TmdbMovie" do
68
69
  end
69
70
 
70
71
  it "should handle The Sand Pebble" do
71
- profile = TmdbMovie.new('tt0060934', TMDB_API_KEY)
72
+ profile = TmdbMovie.new('tt0060934', TMDB_API_KEY, @logger)
72
73
  profile.idents.should be_nil
73
74
  end
74
75
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: royw-tmdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roy Wright
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-17 00:00:00 -07:00
12
+ date: 2009-04-18 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -33,10 +33,12 @@ files:
33
33
  - lib/string_extensions.rb
34
34
  - lib/tmdb.rb
35
35
  - lib/tmdb/optional_logger.rb
36
+ - lib/tmdb/tmdb_image.rb
36
37
  - lib/tmdb/tmdb_movie.rb
37
38
  - lib/tmdb/tmdb_profile.rb
38
39
  - spec/cache_extensions.rb
39
40
  - spec/spec_helper.rb
41
+ - spec/tmdb_image_spec.rb
40
42
  - spec/tmdb_movie_spec.rb
41
43
  - spec/tmdb_profile_spec.rb
42
44
  has_rdoc: true
@@ -70,3 +72,4 @@ test_files:
70
72
  - spec/tmdb_profile_spec.rb
71
73
  - spec/spec_helper.rb
72
74
  - spec/tmdb_movie_spec.rb
75
+ - spec/tmdb_image_spec.rb