apple-tv-converter 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/Gemfile.lock +12 -6
- data/README.md +2 -1
- data/VERSION +1 -1
- data/lib/apple_tv_converter.rb +7 -1
- data/lib/apple_tv_converter/command_line.rb +14 -4
- data/lib/apple_tv_converter/media.rb +38 -5
- data/lib/apple_tv_converter/media_converter.rb +7 -2
- data/lib/apple_tv_converter/media_converter_adapter.rb +3 -96
- data/lib/apple_tv_converter/media_converter_mac_adapter.rb +19 -62
- data/lib/apple_tv_converter/metadata/imdb.rb +77 -0
- data/lib/apple_tv_converter/metadata/info.rb +23 -0
- data/lib/apple_tv_converter/metadata/movie_db.rb +104 -0
- data/lib/apple_tv_converter/metadata/tv_db.rb +303 -0
- data/lib/apple_tv_converter/subtitles_fetcher/opensubtitles.rb +3 -3
- metadata +28 -3
- data/lib/apple_tv_converter/tv_db_fetcher.rb +0 -264
@@ -0,0 +1,77 @@
|
|
1
|
+
module AppleTvConverter
|
2
|
+
module Metadata
|
3
|
+
class Imdb
|
4
|
+
def self.get_metadata(media, interactive = true, language = 'en')
|
5
|
+
printf "* Getting info from IMDB" if interactive
|
6
|
+
|
7
|
+
metadata_id = media.get_metadata_id(:imdb, :show)
|
8
|
+
|
9
|
+
if !metadata_id
|
10
|
+
search = ::Imdb::Search.new(media.show)
|
11
|
+
|
12
|
+
search.movies.delete_if do |item|
|
13
|
+
item.title.strip =~ /(?:(?:\(TV\s*(?:Movie|(?:Mini.?)?Series|Episode))|(?:Video(?:\s*Game)?))/i
|
14
|
+
end
|
15
|
+
|
16
|
+
metadata_id = if search.movies.length > 1 && interactive
|
17
|
+
choice = 0
|
18
|
+
puts "\n *"
|
19
|
+
while true
|
20
|
+
puts %Q[ | Several movies found, choose the intended one#{" (showing only the first 20 of #{search.movies.length} results)" if search.movies.length > 20}:]
|
21
|
+
|
22
|
+
search.movies[0...20].each_with_index do |item, index|
|
23
|
+
puts " | #{(index + 1).to_s.rjust(search.movies.length.to_s.length)} - #{item.title.strip} (id: #{item.id})"
|
24
|
+
if item.also_known_as.any?
|
25
|
+
akas = item.also_known_as[0...5].each do |aka|
|
26
|
+
puts " | #{' '.rjust(search.movies.length.to_s.length)} AKA: #{(aka.is_a?(Hash) ? aka[:title] : aka).strip}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
printf " |\n *- What's your choice (1..#{[search.movies.length, 20].min})? "
|
32
|
+
choice = STDIN.gets.chomp.to_i
|
33
|
+
|
34
|
+
break if choice.between?(1, [search.movies.length, 20].min)
|
35
|
+
|
36
|
+
puts " | Invalid choice!"
|
37
|
+
puts " |"
|
38
|
+
end
|
39
|
+
|
40
|
+
printf " * Getting info from IMDB"
|
41
|
+
search.movies[choice - 1].id
|
42
|
+
else
|
43
|
+
search.movies.first.id rescue nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# begin
|
48
|
+
if metadata_id
|
49
|
+
imdb_movie = ::Imdb::Movie.new(metadata_id)
|
50
|
+
|
51
|
+
media.metadata.name = imdb_movie.title.gsub(/"/, '"')
|
52
|
+
media.metadata.genre = imdb_movie.genres.first.gsub(/"/, '"') if imdb_movie.genres.any?
|
53
|
+
media.metadata.description = imdb_movie.plot.gsub(/"/, '"') if imdb_movie.plot
|
54
|
+
media.release_date = imdb_movie.year if imdb_movie.year
|
55
|
+
media.metadata.director = (imdb_movie.director.first || '').gsub(/"/, '"') if imdb_movie.director.any?
|
56
|
+
media.metadata.codirector = imdb_movie.director[1].gsub(/"/, '"') if imdb_movie.director.length > 1
|
57
|
+
media.metadata.artwork = imdb_movie.poster if imdb_movie.poster
|
58
|
+
|
59
|
+
media.set_metadata_id :imdb, :show, metadata_id
|
60
|
+
|
61
|
+
puts " [DONE]" if interactive
|
62
|
+
end
|
63
|
+
# rescue OpenURI::HTTPError => e
|
64
|
+
# media.set_metadata_id :imdb, :show, nil
|
65
|
+
# media.imdb_movie = nil
|
66
|
+
# puts (e.message =~ /404/ ? " [NOT FOUND]" : " [ERROR]") if interactive
|
67
|
+
# rescue
|
68
|
+
# if media.get_metadata_id(:imdb, :show).nil?
|
69
|
+
# puts " [NOT FOUND]" if interactive
|
70
|
+
# else
|
71
|
+
# raise e
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AppleTvConverter
|
2
|
+
module Metadata
|
3
|
+
class Info
|
4
|
+
attr_accessor :name, :genre, :description, :release_date
|
5
|
+
attr_accessor :tv_show, :tv_show_season, :tv_show_episode, :tv_network
|
6
|
+
attr_accessor :screenwriters, :director, :codirector
|
7
|
+
attr_accessor :artwork_filename
|
8
|
+
|
9
|
+
def initialize(media)
|
10
|
+
@media = media
|
11
|
+
end
|
12
|
+
|
13
|
+
def artwork ; @media.artwork_filename ; end
|
14
|
+
def artwork=(value) ; AppleTvConverter.copy value, @media.artwork_filename ; end
|
15
|
+
|
16
|
+
def sort_name ; return @media.is_tv_show_episode? ? "#{tv_show} S#{tv_show_season.to_s.rjust(2, '0')}E#{tv_show_episode.to_s.rjust(2, '0')}" : name ; end
|
17
|
+
def sort_album ; return tv_show ; end
|
18
|
+
def sort_album_artist ; return tv_show ; end
|
19
|
+
def sort_composer ; return tv_show ; end
|
20
|
+
def sort_show ; return "#{tv_show} Season #{tv_show_season.to_s.rjust(2, '0')}" ; end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module AppleTvConverter
|
2
|
+
module Metadata
|
3
|
+
class MovieDb
|
4
|
+
def self.get_metadata(media, interactive = true, language = 'en')
|
5
|
+
show_id = nil
|
6
|
+
|
7
|
+
if media.get_metadata_id(:tmdb, :show)
|
8
|
+
# We have an id, assume it for the search
|
9
|
+
show_id = media.get_metadata_id(:tmdb, :show)
|
10
|
+
else
|
11
|
+
printf "* Searching TheMovieDb.org "
|
12
|
+
|
13
|
+
# Query the data
|
14
|
+
results = search(media)
|
15
|
+
if results
|
16
|
+
puts "[DONE]"
|
17
|
+
|
18
|
+
if results[:total_results] > 0
|
19
|
+
if results[:total_results] == 1 || !interactive
|
20
|
+
# Only 1 result, or non-interactive, use the first result
|
21
|
+
show_id = results[:results].first[:id]
|
22
|
+
else
|
23
|
+
# More than one result, ask the user
|
24
|
+
choice = 0
|
25
|
+
puts "\n *"
|
26
|
+
|
27
|
+
while true
|
28
|
+
puts %Q[ | Several shows found, choose the intended one:]
|
29
|
+
|
30
|
+
results[:results].each_with_index do |item, index|
|
31
|
+
puts " | #{(index + 1).to_s.rjust(results[:total_results].to_s.length)} - #{item[:title]} (#{Date.parse(item[:release_date]).year}) (id: #{item[:id]})"
|
32
|
+
end
|
33
|
+
|
34
|
+
printf " |\n *- What's your choice (1..#{results[:results].length})? "
|
35
|
+
choice = STDIN.gets.chomp.to_i
|
36
|
+
|
37
|
+
break if choice.between?(1, results[:results].length)
|
38
|
+
|
39
|
+
puts " | Invalid choice!"
|
40
|
+
puts " |"
|
41
|
+
end
|
42
|
+
|
43
|
+
show_id = results[:results][choice - 1][:id]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# It's not found, return false to continue with other services
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
else
|
50
|
+
puts "[ERROR]"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if show_id.to_i > 0
|
55
|
+
printf "* Fetching metadata from TheMovieDb.org "
|
56
|
+
|
57
|
+
# Fetch the detailed data
|
58
|
+
data = get(show_id)
|
59
|
+
|
60
|
+
if data
|
61
|
+
puts "[DONE]"
|
62
|
+
media.metadata.name = data[:title]
|
63
|
+
media.metadata.genre = data[:genres].first[:name]
|
64
|
+
media.metadata.description = data[:overview]
|
65
|
+
media.metadata.release_date = data[:release_date]
|
66
|
+
# media.metadata.screenwriters = data[:release_date]
|
67
|
+
# media.metadata.director = data[:release_date]
|
68
|
+
# media.metadata.codirector = data[:release_date]
|
69
|
+
media.metadata.artwork = poster_url(data[:poster_path])
|
70
|
+
|
71
|
+
media.release_date = Date.parse(media.metadata.release_date).year
|
72
|
+
media.set_metadata_id :tmdb, :show, show_id
|
73
|
+
|
74
|
+
return true
|
75
|
+
else
|
76
|
+
puts "[ERROR]"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def self.api_key; return '5ebb3f1009ddd14d244cbe1645b616a0' ; end
|
84
|
+
def self.base_url; return 'http://api.themoviedb.org/3/'; end
|
85
|
+
def self.build_url(path, params = {}) ; return "#{base_url}#{path}" ; end
|
86
|
+
def self.build_params(params = {}) ; return params.merge(:api_key => api_key) ; end
|
87
|
+
def self.poster_url(extra) ; return "#{configuration[:images][:base_url]}original#{extra}" ; end
|
88
|
+
|
89
|
+
def self.search(media) ; request 'search/movie', :query => media.show ; end
|
90
|
+
def self.get(id) ; request("movie/#{id}", :append_to_response => 'credits') ; end
|
91
|
+
def self.configuration ; @configuration ||= request('configuration') ; end
|
92
|
+
|
93
|
+
def self.request(url, params = {})
|
94
|
+
data = { :params => build_params(params), :accept => 'application/json', :block_response => true }
|
95
|
+
begin
|
96
|
+
return JSON.parse(RestClient.get(build_url(url), data), :symbolize_names => true, :symbolize_keys => true)
|
97
|
+
rescue => e
|
98
|
+
return nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
@@ -0,0 +1,303 @@
|
|
1
|
+
module AppleTvConverter
|
2
|
+
module Metadata
|
3
|
+
class TvDb
|
4
|
+
require 'httparty'
|
5
|
+
require 'yaml'
|
6
|
+
require 'net/http'
|
7
|
+
require 'zip/zip'
|
8
|
+
require 'xml'
|
9
|
+
|
10
|
+
include HTTParty
|
11
|
+
|
12
|
+
base_uri 'thetvdb.com/api'
|
13
|
+
|
14
|
+
def self.search(media, interactive = true, language = 'en')
|
15
|
+
get_updates_from_server
|
16
|
+
|
17
|
+
if media.get_metadata_id(:tvdb, :show)
|
18
|
+
show_id = media.get_metadata_id(:tvdb, :show)
|
19
|
+
else
|
20
|
+
data = load_config_file('show_ids') || {}
|
21
|
+
|
22
|
+
# http://thetvdb.com/api/GetSeries.php?seriesname=
|
23
|
+
unless data.has_key?(media.show)
|
24
|
+
printf "* Searching TheTVDB "
|
25
|
+
|
26
|
+
show_ids = get_and_parse_data_from_server('show_ids', '/GetSeries.php', { :query => { :seriesname => media.show } }, ['Data', 'Series']) do |loaded_data|
|
27
|
+
puts "[DONE]"
|
28
|
+
loaded_data = [loaded_data].flatten
|
29
|
+
|
30
|
+
data[media.show] = if loaded_data.length > 1 && interactive
|
31
|
+
choice = 0
|
32
|
+
puts "\n *"
|
33
|
+
|
34
|
+
while true
|
35
|
+
puts %Q[ | Several shows found, choose the intended one:]
|
36
|
+
|
37
|
+
loaded_data.each_with_index do |item, index|
|
38
|
+
puts " | #{(index + 1).to_s.rjust(loaded_data.length.to_s.length)} - #{item['SeriesName']} (id: #{item['seriesid']})"
|
39
|
+
puts " | #{' '.rjust(loaded_data.length.to_s.length)} AKA: #{item['AliasNames']}" if item['AliasNames']
|
40
|
+
end
|
41
|
+
|
42
|
+
printf " |\n *- What's your choice (1..#{loaded_data.length})? "
|
43
|
+
choice = STDIN.gets.chomp.to_i
|
44
|
+
|
45
|
+
break if choice.between?(1, loaded_data.length)
|
46
|
+
|
47
|
+
puts " | Invalid choice!"
|
48
|
+
puts " |"
|
49
|
+
end
|
50
|
+
|
51
|
+
loaded_data[choice - 1]['seriesid']
|
52
|
+
else
|
53
|
+
loaded_data.first['seriesid']
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return the new list sorted by show name
|
57
|
+
Hash[data.sort]
|
58
|
+
end
|
59
|
+
|
60
|
+
puts "[NOT FOUND]" if show_ids.empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
show_id = data[media.show]
|
64
|
+
end
|
65
|
+
|
66
|
+
if show_id.to_i > 0
|
67
|
+
printf "* Getting info from TheTVDB "
|
68
|
+
|
69
|
+
# <mirrorpath_zip>/api/<apikey>/series/<seriesid>/all/<language>.zip
|
70
|
+
show_data = get_data(show_id, "/#{api_key}/series/#{show_id}/all/#{language}.zip", { :zip => true }) do |data|
|
71
|
+
show_data = xml_document_to_hash(XML::Document.string(data[language.to_s].gsub(/>\s*</im, '><')))
|
72
|
+
banners = xml_document_to_hash(XML::Document.string(data['banners'].gsub(/>\s*</im, '><'))) rescue { 'Banner' => [] }
|
73
|
+
actors = xml_document_to_hash(XML::Document.string(data['actors'].gsub(/>\s*</im, '><'))) rescue { 'Actor' => [] }
|
74
|
+
|
75
|
+
{
|
76
|
+
:series => show_data['Series'],
|
77
|
+
:episodes => [show_data['Episode']].flatten,
|
78
|
+
:banners => [banners['Banner']].flatten,
|
79
|
+
:actors => [actors['Actor']].flatten
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
data = {
|
84
|
+
:episode => show_data[:episodes].detect do |ep|
|
85
|
+
# For season 1, check the absolute number first (for cartoons, etc.), and then check the usual season/episode combo
|
86
|
+
(media.season.to_i == 1 && ep['absolute_number'].to_i == media.number.to_i) || (ep['SeasonNumber'].to_i == media.season.to_i && ep['EpisodeNumber'].to_i == media.number.to_i)
|
87
|
+
end,
|
88
|
+
:show => show_data
|
89
|
+
}
|
90
|
+
|
91
|
+
if data[:episode]
|
92
|
+
media.metadata.tv_show = data[:show][:series]['SeriesName'] || media.show
|
93
|
+
media.metadata.tv_show_season = media.use_absolute_episode_numbering ? 1 : data[:episode]['SeasonNumber']
|
94
|
+
media.metadata.tv_show_episode = data[:episode][media.use_absolute_episode_numbering ? 'absolute_number' : 'EpisodeNumber']
|
95
|
+
media.metadata.tv_network = data[:show][:series]['Network']
|
96
|
+
media.metadata.name = data[:episode]['EpisodeName'] || "#{media.metadata.tv_show} S#{media.metadata.tv_show_season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}"
|
97
|
+
media.metadata.genre = data[:show][:series]['Genre'].gsub(/(?:^\|)|(?:\|$)/, '').split('|').first rescue nil
|
98
|
+
media.metadata.description = data[:episode]['Overview']
|
99
|
+
media.metadata.release_date = data[:episode]['FirstAired']
|
100
|
+
media.metadata.screenwriters = data[:episode]['Writer'].gsub(/(?:^\|)|(?:\|$)/, '').split('|').join(', ') rescue nil
|
101
|
+
media.metadata.director = data[:episode]['Director']
|
102
|
+
media.metadata.artwork = get_poster(media, data)
|
103
|
+
|
104
|
+
# Update some data in the media
|
105
|
+
set_metadata_id_if_not_set(:imdb, :show, data[:show][:series]['IMDB_ID']) rescue nil
|
106
|
+
media.release_date = media.metadata.release_date
|
107
|
+
|
108
|
+
# Update the episode name, if available
|
109
|
+
media.episode_title = data[:episode]['EpisodeName']
|
110
|
+
media.set_metadata_id :tvdb, :show, data[:episode]['seriesid']
|
111
|
+
media.set_metadata_id :tvdb, :season, data[:episode]['seasonid']
|
112
|
+
media.set_metadata_id :tvdb, :episode, data[:episode]['id']
|
113
|
+
media.set_metadata_id :imdb, :episode, data[:episode]['IMDB_ID']
|
114
|
+
|
115
|
+
puts "[DONE]"
|
116
|
+
|
117
|
+
return true
|
118
|
+
else
|
119
|
+
puts "[NOT FOUND]"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.get_poster(media, data = nil)
|
127
|
+
local_file = File.join(AppleTvConverter.data_path, 'cache', 'tvdb', "#{media.tvdb_id}.jpg")
|
128
|
+
|
129
|
+
unless File.exists?(local_file)
|
130
|
+
artwork_filename = data[:show][:series]['poster'] || ''
|
131
|
+
artwork_filename = data[:episode]['filename'] || '' if artwork_filename.blank?
|
132
|
+
artwork_filename = "http://thetvdb.com/banners/#{artwork_filename}" if !artwork_filename.blank?
|
133
|
+
|
134
|
+
AppleTvConverter.copy artwork_filename, local_file unless artwork_filename.blank?
|
135
|
+
end
|
136
|
+
|
137
|
+
local_file
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def self.api_key ; return '67FBF9F0670DBDF2' ; end
|
143
|
+
def self.local_cache_base_path
|
144
|
+
return File.expand_path(File.join(AppleTvConverter.data_path, 'cache', 'tvdb'))
|
145
|
+
end
|
146
|
+
def self.server_update_timestamp
|
147
|
+
@server_update_timestamp ||= load_config_file('update')
|
148
|
+
|
149
|
+
unless @server_update_timestamp
|
150
|
+
# http://thetvdb.com/api//Updates.php?type=none
|
151
|
+
@server_update_timestamp = get_data_from_server('/Updates.php', { :query => { :type => 'none' }})["Items"]["Time"] rescue nil
|
152
|
+
@server_update_timestamp = @server_update_timestamp.to_i unless @server_update_timestamp.nil?
|
153
|
+
save_config_file 'update', @server_update_timestamp
|
154
|
+
end
|
155
|
+
|
156
|
+
@server_update_timestamp
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.load_config_file(filename)
|
160
|
+
full_filename = File.join(local_cache_base_path, filename =~ /\.yml$/ ? filename : "#{filename}.yml")
|
161
|
+
File.exists?(full_filename) ? YAML.load_file(full_filename) : nil
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.save_config_file(filename, data)
|
165
|
+
full_filename = File.join(local_cache_base_path, filename =~ /\.yml$/ ? filename : "#{filename}.yml")
|
166
|
+
File.open(full_filename, 'w') { |f| f.write data.to_yaml }
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.delete_config_file(filename)
|
170
|
+
full_filename = File.join(local_cache_base_path, filename =~ /\.yml$/ ? filename : "#{filename}.yml")
|
171
|
+
File.delete(full_filename) if File.exists?(full_filename)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def self.get_data_from_server(url, options = {})
|
176
|
+
AppleTvConverter.logger.debug " -> Getting from server: #{url}"
|
177
|
+
cache = options.delete(:cache) || true
|
178
|
+
zip = options.delete(:zip) || false
|
179
|
+
response = self.get(url, options).parsed_response
|
180
|
+
|
181
|
+
if zip
|
182
|
+
filename = File.join(local_cache_base_path, 'zip_file.zip')
|
183
|
+
|
184
|
+
begin
|
185
|
+
File.open(filename, 'wb') { |f| f.write response }
|
186
|
+
response = {}
|
187
|
+
|
188
|
+
Zip::ZipFile.open(filename) do |zipfile|
|
189
|
+
zipfile.each do |entry|
|
190
|
+
unless entry.name.downcase["__macosx"]
|
191
|
+
zip_data = zipfile.read(entry)
|
192
|
+
response[entry.name.to_s.gsub(/\.xml$/i, '')] = zip_data
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
rescue => e
|
197
|
+
ap [e, e.backtrace]
|
198
|
+
|
199
|
+
ensure
|
200
|
+
FileUtils.rm_f filename if File.exists?(filename)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
return response
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.get_data(filename, url, url_options, response_indexes = [])
|
208
|
+
AppleTvConverter.logger.debug "-> Getting data: #{filename}"
|
209
|
+
data = load_config_file(filename)
|
210
|
+
|
211
|
+
unless data
|
212
|
+
data = get_data_from_server(url, url_options)
|
213
|
+
|
214
|
+
if data
|
215
|
+
begin
|
216
|
+
response_indexes.each { |idx| data = data[idx] }
|
217
|
+
|
218
|
+
data = yield(data) if block_given?
|
219
|
+
|
220
|
+
save_config_file filename, data
|
221
|
+
rescue
|
222
|
+
data = nil
|
223
|
+
end
|
224
|
+
end
|
225
|
+
else
|
226
|
+
# ap ['found on cache', filename, data]
|
227
|
+
end
|
228
|
+
|
229
|
+
return data
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.get_and_parse_data_from_server(filename, url, url_options, response_indexes = [])
|
233
|
+
cache = url_options.delete(:cache) || true
|
234
|
+
data = get_data_from_server(url, url_options)
|
235
|
+
|
236
|
+
if data
|
237
|
+
begin
|
238
|
+
response_indexes.each { |idx| data = data[idx] }
|
239
|
+
|
240
|
+
data = yield(data) if block_given?
|
241
|
+
|
242
|
+
save_config_file filename, data if cache
|
243
|
+
rescue
|
244
|
+
data = nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def self.get_updates_from_server(options = {})
|
250
|
+
get_and_parse_data_from_server('updates', '/Updates.php', { :query => { :type => 'all', :time => load_config_file('update')}, :cache => false }, ['Items']) do |data|
|
251
|
+
if data
|
252
|
+
# Delete each show's cached data
|
253
|
+
data['Series'].each do |show_id|
|
254
|
+
delete_config_file show_id
|
255
|
+
delete_config_file "#{show_id}.jpg"
|
256
|
+
end
|
257
|
+
|
258
|
+
# Save the new timestamp
|
259
|
+
save_config_file 'update', data['Time']
|
260
|
+
@server_update_timestamp = data['Time']
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.xml_document_to_hash(document)
|
266
|
+
def self.xml_node_to_hash(xml)
|
267
|
+
return nil if xml.children.empty?
|
268
|
+
return xml.children.first.to_s if xml.children.count == 1 && xml.children.first.text?
|
269
|
+
|
270
|
+
# Append a sequential number to the name to prevent replacing items that should be in an array
|
271
|
+
child_number = 0
|
272
|
+
Hash[*(xml.children.map { |child| child_number += 1 ; ["#{child.name}::#{child_number}", xml_node_to_hash(child)] }.compact.flatten(1))]
|
273
|
+
end
|
274
|
+
|
275
|
+
intermediate_hash = xml_node_to_hash(document.root)
|
276
|
+
|
277
|
+
return Hash[*(intermediate_hash.group_by do |obj|
|
278
|
+
obj.first.gsub(/::\d+$/, '')
|
279
|
+
end.map do |key, value|
|
280
|
+
# Remove the 'key' entries
|
281
|
+
value = value.flatten(1).delete_if { |v| v.to_s =~ /#{key}::\d+/ }
|
282
|
+
|
283
|
+
# Remove the sequential number from the keys
|
284
|
+
value.map! do |element|
|
285
|
+
Hash[*(element.map do |ikey, ivalue|
|
286
|
+
[ikey.gsub(/::\d+$/, ''), ivalue]
|
287
|
+
end.flatten(1))]
|
288
|
+
end
|
289
|
+
|
290
|
+
# If there's only one entry, remove the array
|
291
|
+
value = value.first if value.count == 1
|
292
|
+
|
293
|
+
[key, value]
|
294
|
+
end.flatten(1))]
|
295
|
+
end
|
296
|
+
|
297
|
+
FileUtils.mkdir_p local_cache_base_path
|
298
|
+
|
299
|
+
# Load the server timestamp on startup
|
300
|
+
server_update_timestamp
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|