royw-dvdprofiler2xbmc 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,12 +8,19 @@
8
8
  class DvdProfiler2Xbmc
9
9
  include Singleton
10
10
 
11
+ attr_accessor :multiple_profiles
12
+
11
13
  @interrupted = false
14
+ @interrupt_message = "control-C detected, finishing current task"
15
+
16
+ class << self
17
+ attr_accessor :interrupt_message
18
+ end
12
19
 
13
20
  # A trap("INT") in the Runner calls this to indicate that a ^C has been detected.
14
21
  # Note, once set, it is never cleared
15
22
  def self.interrupt
16
- AppConfig[:logger].error { "control-C detected, finishing current task" }
23
+ AppConfig[:logger].error { @interrupt_message }
17
24
  @interrupted = true
18
25
  end
19
26
 
@@ -25,6 +32,7 @@ class DvdProfiler2Xbmc
25
32
 
26
33
  def initialize
27
34
  @media_files = nil
35
+ @multiple_profiles = []
28
36
  end
29
37
 
30
38
  # the application's main execution loop
@@ -32,13 +40,15 @@ class DvdProfiler2Xbmc
32
40
  AppConfig[:logger].info { "Media Directories:\n #{AppConfig[:directories].join("\n ")}" }
33
41
 
34
42
  @media_files = MediaFiles.new(AppConfig[:directories])
35
- @media_files.titles.each do |title, medias|
36
- break if DvdProfiler2Xbmc.interrupted?
37
- medias.each do |media|
38
- # note, NfoController update must be first as it sets isbn and imdb_id for media
39
- NfoController.update(media)
40
- ThumbnailController.update(media)
41
- FanartController.update(media)
43
+ if AppConfig[:do_update]
44
+ @media_files.titles.each do |title, medias|
45
+ break if DvdProfiler2Xbmc.interrupted?
46
+ medias.each do |media|
47
+ # note, NfoController update must be first as it sets isbn and imdb_id for media
48
+ NfoController.update(media)
49
+ ThumbnailController.update(media)
50
+ FanartController.update(media)
51
+ end
42
52
  end
43
53
  end
44
54
 
@@ -57,6 +67,7 @@ class DvdProfiler2Xbmc
57
67
  buf += gen_report('missing_isbns', 'Missing ISBNs')
58
68
  buf += gen_report('missing_imdb_ids', 'Missing IMDB IDs')
59
69
  buf += gen_report('missing_thumbnails', 'Missing Thumbnails')
70
+ buf += gen_report('multiple_profiles', 'Multiple Profiles Found For Single Titles')
60
71
  end
61
72
  end
62
73
  buf
@@ -77,6 +88,58 @@ class DvdProfiler2Xbmc
77
88
  buf
78
89
  end
79
90
 
91
+ def self.save_to_file(filespec, data)
92
+ new_filespec = filespec + AppConfig[:extensions][:new]
93
+ File.open(new_filespec, "w") do |file|
94
+ file.puts(data)
95
+ end
96
+ backup_filespec = filespec + AppConfig[:extensions][:backup]
97
+ File.delete(backup_filespec) if File.exist?(backup_filespec)
98
+ File.rename(filespec, backup_filespec) if File.exist?(filespec)
99
+ File.rename(new_filespec, filespec)
100
+ File.delete(new_filespec) if File.exist?(new_filespec)
101
+ end
102
+
103
+ # options hash may have the following:
104
+ # :extension - an extension to append to the generated filespec
105
+ # :year - the production year
106
+ # :resolution - the video resolution
107
+ def self.generate_filespec(media_pathspec, type, options={})
108
+ filespec = nil
109
+ begin
110
+ basespec = File.basename(media_pathspec, ".*").gsub(AppConfig[:part_regex], '')
111
+ dirname = File.dirname(media_pathspec)
112
+ part = :no_part
113
+ if media_pathspec =~ AppConfig[:part_regex]
114
+ part = :part
115
+ end
116
+
117
+ extension = AppConfig[:extensions][type]
118
+ year = options[:year] || ''
119
+ resolution = options[:resolution] || ''
120
+
121
+ if AppConfig[:naming][type].nil?
122
+ filespec = File.join(dirname, basespec)
123
+ unless extension.blank?
124
+ filespec += extension
125
+ end
126
+ else
127
+ format_str = AppConfig[:naming][type][part]
128
+ unless format_str.blank?
129
+ unless extension.blank?
130
+ filespec = File.join(dirname, format_str.gsub(/%t/, basespec).gsub(/%e/, extension).gsub(/%r/, resolution).gsub(/%y/, year))
131
+ end
132
+ end
133
+ end
134
+ unless options[:extension].blank?
135
+ filespec += options[:extension]
136
+ end
137
+ rescue Exception => e
138
+ AppConfig[:logger].error { "Error in generate_filespec(#{media_pathspec}, #{type}, #{options.inspect}) - #{e.to_s}" }
139
+ end
140
+ filespec
141
+ end
142
+
80
143
  protected
81
144
 
82
145
  # set the directory and file permissions for all files and directories under
@@ -85,9 +148,9 @@ class DvdProfiler2Xbmc
85
148
  Dir.glob(File.join(dir, '**/*')).each do |f|
86
149
  begin
87
150
  if File.directory?(f)
88
- File.chmod(AppConfig[:dir_permissions], f) unless AppConfig[:dir_permissions].nil?
151
+ File.chmod(AppConfig[:dir_permissions].to_i(8), f) unless AppConfig[:dir_permissions].nil?
89
152
  else
90
- File.chmod(AppConfig[:file_permissions], f) unless AppConfig[:file_permissions].nil?
153
+ File.chmod(AppConfig[:file_permissions].to_i(8), f) unless AppConfig[:file_permissions].nil?
91
154
  end
92
155
  rescue Exception => e
93
156
  AppConfig[:logger].error {e.to_s}
@@ -120,7 +183,7 @@ class DvdProfiler2Xbmc
120
183
  if medias[0].isbn.nil?
121
184
  paths = []
122
185
  medias.each do |media|
123
- unless File.exist? media.path_to(:no_isbn_extension)
186
+ unless File.exist? media.path_to(:no_isbn)
124
187
  paths << " #{media.media_path}"
125
188
  end
126
189
  end
@@ -157,7 +220,7 @@ class DvdProfiler2Xbmc
157
220
  buf << "No media for #{title}"
158
221
  else
159
222
  medias.each do |media|
160
- thumbnail = media.path_to(:thumbnail_extension)
223
+ thumbnail = media.path_to(:thumbnail)
161
224
  unless File.exist?(thumbnail)
162
225
  buf << " #{thumbnail} #{media.imdb_id.nil? ? '' : media.imdb_id}"
163
226
  end
@@ -167,5 +230,9 @@ class DvdProfiler2Xbmc
167
230
  buf
168
231
  end
169
232
 
233
+ def multiple_profiles_report
234
+ @multiple_profiles
235
+ end
236
+
170
237
  end
171
238
 
@@ -23,41 +23,63 @@ class FanartController
23
23
  unless @media.imdb_id.blank?
24
24
  if @media.fanart_files.empty?
25
25
  fetch_fanart(@media.imdb_id)
26
+ link_fanart(@media.path_to(:fanart))
26
27
  end
27
28
  end
28
29
  result
29
30
  end
30
31
 
31
- protected
32
+ def link_fanart(dest_filespec)
33
+ ['original', 'mid', 'cover', 'thumb'].each do |size|
34
+ files = Dir.glob("#{dest_filespec}.#{size}.*")
35
+ unless files.blank?
36
+ filespec = files.sort.first
37
+ extension = File.extname(filespec)
38
+ link_filespec = dest_filespec + extension
39
+ unless File.exist?(link_filespec)
40
+ File.link(filespec, link_filespec)
41
+ end
42
+ break
43
+ end
44
+ end
45
+ end
32
46
 
33
- DISC_NUMBER_REGEX = /\.(cd|part|disk|disc)\d+/i
47
+ protected
34
48
 
35
- # TODO this only fetches the first fanart. Probably should fetch
36
- # all fanart but will need a naming scheme. Need to research what
37
- # the other xbmc utilities are doing.
38
49
  def fetch_fanart(imdb_id)
39
- profile = TmdbProfile.new(imdb_id, @media.path_to(:tmdb_xml_extension))
50
+ profile = TmdbProfile.new(imdb_id, @media.path_to(:tmdb_xml))
51
+ indexes = {}
40
52
  unless profile.nil? || profile.movie.blank?
41
53
  movie = profile.movie
42
54
  unless movie['fanarts'].blank?
43
- fanart = movie['fanarts'].first
44
- AppConfig[:logger].debug { "#{fanart.inspect}" }
45
- src_url = fanart['content']
46
- unless src_url.blank?
47
- fanart_filename = File.basename(@media.media_path, ".*").gsub(DISC_NUMBER_REGEX, '')
48
- fanart_filename += AppConfig[:fanart_extension]
49
- fanart_filename += File.extname(src_url)
50
- dest_filespec = File.join(File.dirname(@media.media_path), fanart_filename)
51
- unless File.exist?(dest_filespec)
52
- AppConfig[:logger].info { "src_url => #{src_url}" }
53
- AppConfig[:logger].info { "dest_fanart_filespec => #{dest_filespec}" }
54
- copy_fanart(src_url, dest_filespec)
55
+ fanarts = movie['fanarts']
56
+ fanarts.each do |fanart|
57
+ AppConfig[:logger].debug { "#{fanart.inspect}" }
58
+ src_url = fanart['content']
59
+ unless src_url.blank?
60
+ dest_filespec = FanartController.get_destination_filespec(@media.media_path, fanart, indexes)
61
+ unless File.exist?(dest_filespec)
62
+ AppConfig[:logger].info { "src_url => #{src_url}" }
63
+ AppConfig[:logger].info { "dest_fanart_filespec => #{dest_filespec}" }
64
+ copy_fanart(src_url, dest_filespec)
65
+ end
55
66
  end
56
67
  end
57
68
  end
58
69
  end
59
70
  end
60
71
 
72
+ def FanartController.get_destination_filespec(media_path, fanart, indexes)
73
+ extension = File.extname(fanart['content'])
74
+ size = fanart['size']
75
+ unless size.blank?
76
+ indexes[size] ||= -1
77
+ indexes[size] += 1
78
+ extension = ".#{size}.#{indexes[size]}#{extension}"
79
+ end
80
+ fanart_filename = DvdProfiler2Xbmc.generate_filespec(media_path, :fanart, :extension => extension)
81
+ end
82
+
61
83
  # download the fanart
62
84
  def copy_fanart(src_url, dest_filespec)
63
85
  begin
@@ -20,7 +20,7 @@ class NfoController
20
20
  def initialize(media)
21
21
  @media = media
22
22
  @info = Hash.new
23
- @xbmc_info = XbmcInfo.new(@media.path_to(:nfo_extension))
23
+ @xbmc_info = XbmcInfo.new(@media.path_to(:nfo))
24
24
  self.isbn = @xbmc_info.movie['isbn']
25
25
  self.imdb_id = @xbmc_info.movie['id']
26
26
  end
@@ -41,10 +41,16 @@ class NfoController
41
41
  @info.merge!(imdb_hash_to_info(imdb_hash))
42
42
  @info.merge!(dvd_hash_to_info(dvd_hash))
43
43
 
44
+ genres = @info['genre']
45
+ genres ||= []
46
+ genres += @media.media_subdirs.split('/') if AppConfig[:subdirs_as_genres]
47
+ new_genres = map_genres(genres.uniq).uniq.compact
48
+ @info['genre'] = new_genres unless new_genres.blank?
49
+
44
50
  save
45
51
  result = true
46
52
  rescue Exception => e
47
- AppConfig[:logger].error { "Error updating \"#{@media.path_to(:nfo_extension)}\" - " + e.to_s + "\n" + e.backtrace.join("\n") }
53
+ AppConfig[:logger].error { "Error updating \"#{@media.path_to(:nfo)}\" - " + e.to_s + "\n" + e.backtrace.join("\n") }
48
54
  raise e
49
55
  end
50
56
  result
@@ -108,12 +114,20 @@ class NfoController
108
114
  def load_dvdprofiler
109
115
  dvd_hash = Hash.new
110
116
  # find ISBN for each title and assign to the media
111
- profile = DvdprofilerProfile.first(:isbn => self.isbn, :title => @media.title)
112
- unless profile.nil?
113
- self.isbn ||= profile.isbn
114
- AppConfig[:logger].info { "ISBN => #{self.isbn}" } unless self.isbn.nil?
115
- profile.save(@media.path_to(:dvdprofiler_xml_extension))
116
- dvd_hash = profile.dvd_hash
117
+ profiles = DvdprofilerProfile.all(:isbn => self.isbn, :title => @media.title, :year => @media.year)
118
+ if profiles.length > 1
119
+ title = "#{@media.title}#{@media.year.blank? ? '' : ' (' + @media.year + ')'}"
120
+ Dvdprofiler2Xbmc.multiple_profiles << "#{title} #{profiles.collect{|prof| prof.isbn}.join(", ")}"
121
+ AppConfig[:logger].warn { "Multiple profiles found for #{title}" }
122
+ else
123
+ profile = profiles.first
124
+ unless profile.nil?
125
+ self.isbn ||= profile.isbn
126
+ AppConfig[:logger].info { "ISBN => #{self.isbn}" } unless self.isbn.nil?
127
+ profile.save(@media.path_to(:dvdprofiler_xml))
128
+ dvd_hash = profile.dvd_hash
129
+ @media.year = [dvd_hash[:productionyear]].flatten.sort.first if @media.year.blank? && !dvd_hash[:productionyear].blank?
130
+ end
117
131
  end
118
132
  dvd_hash
119
133
  end
@@ -122,13 +136,13 @@ class NfoController
122
136
  # return movie hash
123
137
  def load_imdb(dvd_hash)
124
138
  imdb_hash = Hash.new
125
- unless File.exist?(@media.path_to(:no_imdb_extension))
139
+ unless File.exist?(@media.path_to(:no_imdb_lookup))
126
140
  profile = ImdbProfile.first(:imdb_id => self.imdb_id,
127
- :titles => self.get_imdb_titles,
141
+ :titles => self.get_imdb_titles(dvd_hash),
128
142
  :media_years => [@media.year.to_i],
129
143
  :production_years => dvd_hash[:productionyear],
130
144
  :released_years => dvd_hash[:released],
131
- :filespec => @media.path_to(:imdb_xml_extension)
145
+ :filespec => @media.path_to(:imdb_xml)
132
146
  )
133
147
  unless profile.nil?
134
148
  self.imdb_id ||= profile.imdb_id
@@ -143,9 +157,9 @@ class NfoController
143
157
  # return movie hash
144
158
  def load_tmdb
145
159
  tmdb_hash = Hash.new
146
- unless File.exist?(@media.path_to(:no_tmdb_extension))
160
+ unless File.exist?(@media.path_to(:no_tmdb_lookup))
147
161
  profile = TmdbProfile.first(:imdb_id => self.imdb_id,
148
- :filespec => @media.path_to(:tmdb_xml_extension))
162
+ :filespec => @media.path_to(:tmdb_xml))
149
163
  unless profile.nil?
150
164
  tmdb_hash = profile.movie
151
165
  end
@@ -153,13 +167,35 @@ class NfoController
153
167
  tmdb_hash
154
168
  end
155
169
 
156
- def get_imdb_titles
170
+ def get_imdb_titles(dvd_hash)
157
171
  titles = []
158
172
  titles << @info['title'] unless @info['title'].blank?
159
173
  titles << @media.title unless @media.title.blank?
174
+ titles += get_parent_titles(dvd_hash)
160
175
  titles.uniq.compact
161
176
  end
162
177
 
178
+ # try to find box set parent's title
179
+ def get_parent_titles(dvd_hash)
180
+ titles = []
181
+ unless dvd_hash[:boxset].blank?
182
+ begin
183
+ AppConfig[:logger].info { "Need to find box set parent's title" }
184
+ parent_isbn = dvd_hash[:boxset].first['parent'].first
185
+ unless parent_isbn.blank?
186
+ parent_profile = DvdprofilerProfile.first(:isbn => parent_isbn)
187
+ unless parent_profile.blank?
188
+ titles << parent_profile.title
189
+ titles += get_parent_titles(parent_profile.dvd_hash)
190
+ end
191
+ end
192
+ rescue
193
+ end
194
+ end
195
+ AppConfig[:logger].info { "parent titles => #{titles.pretty_inspect}" } unless titles.empty?
196
+ titles
197
+ end
198
+
163
199
  DVD_HASH_TO_INFO_MAP = {
164
200
  :rating => 'mpaa',
165
201
  :plot => 'outline',
@@ -183,8 +219,7 @@ class NfoController
183
219
  info = Hash.new
184
220
  unless dvd_hash.nil?
185
221
  dvd_hash[:genres] ||= []
186
- genres = map_genres((dvd_hash[:genres] + @media.media_subdirs.split('/')).uniq)
187
- info['genre'] = genres unless genres.blank?
222
+ info['genre'] = dvd_hash[:genres] unless dvd_hash[:genres].blank?
188
223
  info['title'] = dvd_hash[:title]
189
224
  info['year'] = [dvd_hash[:productionyear], dvd_hash[:released]].flatten.uniq.collect{|s| ((s =~ /(\d{4})/) ? $1 : nil)}.uniq.compact.first
190
225
  DVD_HASH_TO_INFO_MAP.each do |key, value|
@@ -39,7 +39,7 @@ class ThumbnailController
39
39
  def fetch_imdb_thumbnail(imdb_id)
40
40
  imdb_movie = ImdbMovie.new(imdb_id.gsub(/^tt/, ''))
41
41
  source_uri = imdb_movie.poster.image
42
- dest_image_filespec = @media.path_to(:thumbnail_extension)
42
+ dest_image_filespec = @media.path_to(:thumbnail)
43
43
  puts "fetch_imdb_thumbnail(#{imdb_id}) => #{source_uri}"
44
44
  begin
45
45
  File.open(dest_image_filespec, "wb") {|f| f.write(open(source_uri).read)}
@@ -52,7 +52,7 @@ class ThumbnailController
52
52
  def copy_thumbnail(isbn)
53
53
  src_image_filespec = File.join(AppConfig[:images_dir], "#{isbn}f.jpg")
54
54
  if File.exist?(src_image_filespec)
55
- dest_image_filespec = @media.path_to(:thumbnail_extension)
55
+ dest_image_filespec = @media.path_to(:thumbnail)
56
56
  do_copy = true
57
57
  if File.exist?(dest_image_filespec)
58
58
  if File.mtime(src_image_filespec) <= File.mtime(dest_image_filespec)
@@ -118,6 +118,9 @@ class Collection
118
118
  dvd_hash[:lastedited] = dvd[:lastedited][0] unless dvd[:lastedited].blank?
119
119
  directors = find_directors(dvd[:credits])
120
120
  dvd_hash[:directors] = directors unless directors.blank?
121
+ dvd_hash[:boxset] = dvd[:boxset] unless dvd[:boxset].blank?
122
+ dvd_hash[:mediatypes] = dvd[:mediatypes] unless dvd[:mediatypes].blank?
123
+ dvd_hash[:format] = dvd[:format] unless dvd[:format].blank?
121
124
  @isbn_dvd_hash[isbn] = dvd_hash
122
125
  end
123
126
  end
@@ -13,7 +13,7 @@
13
13
  # puts profile.to_xml
14
14
  # puts profile.isbn
15
15
  # puts profile.title
16
- # profile.save(media.path_to(:dvdprofiler_xml_extension))
16
+ # profile.save(media.path_to(:dvdprofiler_xml))
17
17
  #
18
18
  class DvdprofilerProfile
19
19
 
@@ -40,7 +40,13 @@ class DvdprofilerProfile
40
40
  isbns.each do |isbn|
41
41
  dvd_hash = collection.isbn_dvd_hash[isbn]
42
42
  unless dvd_hash.blank?
43
- result << DvdprofilerProfile.new(dvd_hash, isbn, options[:title])
43
+ unless options[:year].blank?
44
+ if dvd_hash[:productionyear].include? options[:year]
45
+ result << DvdprofilerProfile.new(dvd_hash, isbn, options[:title])
46
+ end
47
+ else
48
+ result << DvdprofilerProfile.new(dvd_hash, isbn, options[:title])
49
+ end
44
50
  end
45
51
  end
46
52
  end
@@ -103,25 +109,11 @@ class DvdprofilerProfile
103
109
  xml = self.to_xml
104
110
  unless xml.blank?
105
111
  AppConfig[:logger].debug { "saving #{filespec}" }
106
- save_to_file(filespec, xml)
112
+ DvdProfiler2Xbmc.save_to_file(filespec, xml)
107
113
  end
108
114
  rescue Exception => e
109
115
  AppConfig[:logger].error { "Unable to save dvdprofiler profile to #{filespec} - #{e.to_s}" }
110
116
  end
111
117
  end
112
118
 
113
- protected
114
-
115
- def save_to_file(filespec, data)
116
- new_filespec = filespec + AppConfig[:new_extension]
117
- File.open(new_filespec, "w") do |file|
118
- file.puts(data)
119
- end
120
- backup_filespec = filespec + AppConfig[:backup_extension]
121
- File.delete(backup_filespec) if File.exist?(backup_filespec)
122
- File.rename(filespec, backup_filespec) if File.exist?(filespec)
123
- File.rename(new_filespec, filespec)
124
- File.delete(new_filespec) if File.exist?(new_filespec)
125
- end
126
-
127
119
  end
@@ -12,7 +12,7 @@
12
12
  # :media_years => ['2000'],
13
13
  # :production_years => ['1999'],
14
14
  # :released_years => ['2002', '2008']
15
- # :filespec => media.path_to(:imdb_xml_extension))
15
+ # :filespec => media.path_to(:imdb_xml))
16
16
  # puts profile.movie['key'].first
17
17
  # puts profile.to_xml
18
18
  # puts profile.imdb_id
@@ -117,25 +117,13 @@ class ImdbProfile
117
117
  xml = self.to_xml
118
118
  unless xml.blank?
119
119
  AppConfig[:logger].debug { "saving #{filespec}" }
120
- save_to_file(filespec, xml)
120
+ DvdProfiler2Xbmc.save_to_file(filespec, xml)
121
121
  end
122
122
  rescue Exception => e
123
123
  AppConfig[:logger].error "Unable to save imdb profile to #{filespec} - #{e.to_s}"
124
124
  end
125
125
  end
126
126
 
127
- def save_to_file(filespec, data)
128
- new_filespec = filespec + AppConfig[:new_extension]
129
- File.open(new_filespec, "w") do |file|
130
- file.puts(data)
131
- end
132
- backup_filespec = filespec + AppConfig[:backup_extension]
133
- File.delete(backup_filespec) if File.exist?(backup_filespec)
134
- File.rename(filespec, backup_filespec) if File.exist?(filespec)
135
- File.rename(new_filespec, filespec)
136
- File.delete(new_filespec) if File.exist?(new_filespec)
137
- end
138
-
139
127
  # lookup IMDB title using years as the secondary search key
140
128
  # the titles should behave as an Array, the intent here is to be
141
129
  # able to try to find the exact title from DVD Profiler and if that