em-sofa 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.
Files changed (63) hide show
  1. data/.document +5 -0
  2. data/.gitignore +22 -0
  3. data/.yardopts +6 -0
  4. data/LICENSE +20 -0
  5. data/README.md +110 -0
  6. data/Rakefile +75 -0
  7. data/doc/Em-sofa/Mapping.html +168 -0
  8. data/doc/Em-sofa/Mapping/ClassMethods.html +314 -0
  9. data/doc/Em-sofa/Mapping/InstanceMethods.html +358 -0
  10. data/doc/Em-sofa/TVRage.html +85 -0
  11. data/doc/Em-sofa/TVRage/Episode.html +358 -0
  12. data/doc/Em-sofa/TVRage/Schedule.html +207 -0
  13. data/doc/Em-sofa/TVRage/Season.html +236 -0
  14. data/doc/Em-sofa/TVRage/Show.html +904 -0
  15. data/doc/Em-sofa/TVRage/Show/ShowNotFound.html +92 -0
  16. data/doc/Em-sofa/Version.html +100 -0
  17. data/doc/Sofa.html +85 -0
  18. data/doc/_index.html +233 -0
  19. data/doc/class_list.html +36 -0
  20. data/doc/css/common.css +1 -0
  21. data/doc/css/full_list.css +50 -0
  22. data/doc/css/style.css +277 -0
  23. data/doc/file.README.html +150 -0
  24. data/doc/file_list.html +38 -0
  25. data/doc/index.html +150 -0
  26. data/doc/js/app.js +138 -0
  27. data/doc/js/full_list.js +117 -0
  28. data/doc/js/jquery.js +19 -0
  29. data/doc/method_list.html +195 -0
  30. data/doc/top-level-namespace.html +88 -0
  31. data/lib/em-sofa.rb +8 -0
  32. data/lib/em-sofa/mapping.rb +108 -0
  33. data/lib/em-sofa/tvrage.rb +14 -0
  34. data/lib/em-sofa/tvrage/episode.rb +41 -0
  35. data/lib/em-sofa/tvrage/schedule.rb +28 -0
  36. data/lib/em-sofa/tvrage/season.rb +38 -0
  37. data/lib/em-sofa/tvrage/show.rb +193 -0
  38. data/lib/em-sofa/version.rb +10 -0
  39. data/spec/fixtures/tvrage/cases/castle.xml +228 -0
  40. data/spec/fixtures/tvrage/cases/community.xml +109 -0
  41. data/spec/fixtures/tvrage/cases/live_with_regis_and_kelly.xml +56237 -0
  42. data/spec/fixtures/tvrage/episode_list.xml +1183 -0
  43. data/spec/fixtures/tvrage/episode_list_one_season.xml +17 -0
  44. data/spec/fixtures/tvrage/episode_list_two_episodes.xml +25 -0
  45. data/spec/fixtures/tvrage/full_schedule.xml +4731 -0
  46. data/spec/fixtures/tvrage/full_show_info.xml +1291 -0
  47. data/spec/fixtures/tvrage/quickinfo.html +17 -0
  48. data/spec/fixtures/tvrage/quickinfo_missing.html +1 -0
  49. data/spec/fixtures/tvrage/search.xml +151 -0
  50. data/spec/fixtures/tvrage/show_info.xml +42 -0
  51. data/spec/fixtures/tvrage/show_info_blank.xml +3 -0
  52. data/spec/fixtures/tvrage/single_episode.xml +8 -0
  53. data/spec/sofa/mapping_spec.rb +82 -0
  54. data/spec/sofa/tvrage/cases_spec.rb +29 -0
  55. data/spec/sofa/tvrage/episode_spec.rb +45 -0
  56. data/spec/sofa/tvrage/schedule_spec.rb +17 -0
  57. data/spec/sofa/tvrage/season_spec.rb +61 -0
  58. data/spec/sofa/tvrage/show_spec.rb +201 -0
  59. data/spec/sofa/version_spec.rb +11 -0
  60. data/spec/sofa_spec.rb +5 -0
  61. data/spec/spec.opts +1 -0
  62. data/spec/spec_helper.rb +20 -0
  63. metadata +165 -0
@@ -0,0 +1,28 @@
1
+ module EventMachine
2
+ module Sofa
3
+ module TVRage
4
+ # This class will hold the full/quick schedule information as per the TVRage API.
5
+ #
6
+ # @see http://services.tvrage.com/index.php?page=public&go=fullschedule TVRage API : Full Schedule
7
+ # @see http://services.tvrage.com/index.php?page=public&go=quickschedule TV Rage API : Quick Schedule
8
+ class Schedule
9
+ Base_Uri = 'services.tvrage.com/feeds'
10
+
11
+ extend TVRage
12
+
13
+ class << self
14
+ # Gets the full schedule for country
15
+ #
16
+ # @param country [String] The country to query in (US, UK, NL)
17
+ # @param &block [Block] Called back with result unless fibered
18
+ # @return [Hash] The parsed XML of schedule information
19
+ # @see http://services.tvrage.com/feeds/fullschedule.php?country=US US's Full Schedule
20
+ def full(country, &block)
21
+ return Request.fibered(method(__method__), country) if defined? Fiber and Fiber.respond_to? :current and not block
22
+ Request.new(Base_Uri, '/fullschedule.php', block, :return_element => 'schedule', :country => country)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ module EventMachine
2
+ module Sofa
3
+ module TVRage
4
+ # This class holds the XML information of a single season as per the TVRage API.
5
+ #
6
+ # @see http://services.tvrage.com/index.php?page=public TVRage API : Episode List
7
+ class Season
8
+ include Mapping
9
+
10
+ # Maps :no to itself
11
+ # @see EM:Sofa::Mapping
12
+ maps(:no => nil)
13
+
14
+ # Maps :episode to :episodes
15
+ # @see EM:Sofa::Mapping
16
+ # @yieldparam value [Hash, Array] A Hash of info if there's only one. An Array of info if there's multiple
17
+ # @yieldreturn [Array] A list of episodes initialized with value
18
+ maps(:episode => :episodes) do |value|
19
+ case value
20
+ when Hash
21
+ [Episode.new(value)]
22
+ when Array
23
+ value.map { |info| Episode.new(info) }
24
+ end
25
+ end
26
+
27
+ # Returns a new instance of Season, mapping info from the TVRage API
28
+ #
29
+ # @param info [Hash<Symbol, Object>] Info to initialize with
30
+ # @option info [String] :no The season number
31
+ # @option info [String] :episode The collection of episodes
32
+ def initialize(info)
33
+ update_with_mapping(info)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,193 @@
1
+ module EventMachine
2
+ module Sofa
3
+ module TVRage
4
+ # This class holds the XML information of a single Show as per the TVRage API.
5
+ # It's also the root point for communicating with the API.
6
+ #
7
+ # @see http://services.tvrage.com/index.php?page=public TVRage API
8
+ class Show
9
+ Base_Uri = 'services.tvrage.com'
10
+
11
+ extend TVRage
12
+
13
+ class << self
14
+ # Gets the info for a Show.
15
+ #
16
+ # @param sid [String] The show's id
17
+ # @param &block [Block] Called back with the parsed XML when successful unless fibered
18
+ # @return [EM:TVRage::Request] TVRage request object or result hash if fibered
19
+ # @see http://services.tvrage.com/feeds/showinfo.php?sid=15614 Chuck's Show Info
20
+ def info(sid, &block)
21
+ return fibered_async(__method__, sid, options) if defined? Fiber and Fiber.respond_to? :current and not block
22
+ raise ArgumentError, "No block given for completion callback" unless block
23
+ Request.new(Base_Uri, '/feeds/showinfo.php', block, :return_element => 'Showinfo', :sid => sid)
24
+ end
25
+
26
+ # Gets the full show info (info + season list + episode list) for a Show.
27
+ #
28
+ # @param sid [String] The show's id
29
+ # @param &block [Block] Called back with the parsed XML when successful unless fibered
30
+ # @return [EM:TVRage::Request] TVRage request object or result hash if fibered
31
+ # @see http://services.tvrage.com/feeds/full_show_info.php?sid=15614 Chuck's Full Show Info
32
+ def full_info(sid, &block)
33
+ return fibered_async(__method__, sid, options) if defined? Fiber and Fiber.respond_to? :current and not block
34
+ raise ArgumentError, "No block given for completion callback" unless block
35
+ Request.new(Base_Uri, '/feeds/full_show_info.php', block, :return_element => 'Show', :sid => sid)
36
+ end
37
+
38
+ # Gets the episode list for a Show.
39
+ #
40
+ # @param sid [String] The show's id
41
+ # @param &block [Block] Called back with the parsed XML when successful unless fibered
42
+ # @return [EM:TVRage::Request] TVRage request object or result hash if fibered
43
+ # @see http://services.tvrage.com/feeds/episode_list.php?sid=15614 Chuck's Episode List
44
+ def episode_list(sid, &block)
45
+ return Request.fibered(method(__method__), sid) if defined? Fiber and Fiber.respond_to? :current and not block
46
+ raise ArgumentError, "No block given for completion callback" unless block
47
+ Request.new(Base_Uri, '/feeds/episode_list.php', block, :return_element => 'Show', :sid => sid)
48
+ end
49
+
50
+ # Finds the Show by name using TVRage's Quickinfo API.
51
+ #
52
+ # @param name [String] The name of the show to search for
53
+ # @option options [Boolean] :greedy Whether or not to eager load the Season and Episode info
54
+ # @param &block [Block] Called back with the show with id parsed from the Quickinfo search unless fibered
55
+ # @return [EM:TVRage::Request] TVRage request object or result Show object if fibered
56
+ # @see http://services.tvrage.com/index.php?page=public&go=quickinfo TVRage Quickinfo API
57
+ # @see http://services.tvrage.com/tools/quickinfo.php?show=Chuck Chuck's Quickinfo
58
+ def by_name(name, options = {}, &block)
59
+ return Request.fibered(method(__method__), name, options) if defined? Fiber and Fiber.respond_to? :current and not block
60
+ raise ArgumentError, "No block given for completion callback" unless block
61
+ Request.new(Base_Uri, '/tools/quickinfo.php', block, :parse_element => 'pre', :show => name) do |request, xml|
62
+ options[:info] = parsed_quickinfo(xml)
63
+ Show.new(options[:info]['showid'], options) {|show| request.succeed show }
64
+ end
65
+ end
66
+
67
+ def parsed_quickinfo(raw_info)
68
+ info = Hash[raw_info.split(/\n/).collect {|line| k, v = line.split /@/; [k.gsub(' ', '').downcase, v] }]
69
+ info['showlink'] = info.delete('showurl')
70
+ info.each_key do |key|
71
+ case key.to_sym
72
+ when :latestepisode, :nextepisode
73
+ info[key] = Hash[[:number, :name, :date].zip(info[key].split /\^/)]
74
+ info[key][:date] = Date.parse(info[key][:date])
75
+ when :genres
76
+ info[key] = info[key].split ' | '
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ include Mapping
83
+
84
+ # @see EM:Sofa::Mapping
85
+ maps(
86
+ :ended => nil,
87
+ :showid => :show_id,
88
+ :showname => :name,
89
+ :name => nil,
90
+ :showlink => :show_link,
91
+ :seasons => nil,
92
+ :started => nil,
93
+ :startdate => :start_date,
94
+ :ended => nil,
95
+ :origin_country => nil,
96
+ :status => nil,
97
+ :classification => nil,
98
+ :runtime => :run_time,
99
+ :network => nil,
100
+ :airtime => :air_time,
101
+ :airday => :air_day,
102
+ :timezone => :time_zone,
103
+ :latestepisode => :latest_episode,
104
+ :nextepisode => :next_episode
105
+ )
106
+ maps(:genres => nil) #{ |value| value["genre"] }
107
+ maps(:akas => nil) { |value| value["aka"] }
108
+
109
+ # Maps :Episodelist to :season_list
110
+ # @see EM:Sofa::Mapping
111
+ # @yieldparam value [Hash, Array] A Hash of info if there's only one. An Array of info if there's multiple
112
+ # @yieldreturn [Array] A list of seasons initialized with value
113
+ maps(:Episodelist => :season_list) do |value|
114
+ case seasons = value["Season"]
115
+ when Hash
116
+ [Season.new(seasons)]
117
+ when Array
118
+ seasons.map { |info| Season.new(info) }
119
+ end
120
+ end
121
+
122
+ # Stores all the info that was greedy-loaded
123
+ #
124
+ # @return [Hash] The full show info (including seasons and episodes)
125
+ # @see Show.full_info
126
+ attr_accessor :greedy
127
+
128
+ # Returns a new instance of Show, loading and then mapping info from the TVRage API.
129
+ #
130
+ # @param id [String] The show_id as per the TVRage API
131
+ # @option options [Boolean] :greedy Whether or not to eager load the Season and Episode info
132
+ # @param &block [Block] Called back with the show with parsed info unless fibered
133
+ def initialize(id, options = {}, &block)
134
+ raise RuntimeError.new("id is required") unless (@show_id = id)
135
+ return TVRage::Request.fibered(method(__method__), id, options) if defined? Fiber and Fiber.respond_to? :current and not block
136
+ raise ArgumentError, "No block given for completion callback" unless block
137
+ klass = self.class
138
+ if options[:greedy]
139
+ return block.call(self) if @greedy
140
+ klass.full_info(@show_id) do |full_info|
141
+ next block.call(nil) unless full_info
142
+ update_with_mapping(@greedy = full_info)
143
+ block.call(self)
144
+ end
145
+ elsif options[:info]
146
+ update_with_mapping(options[:info])
147
+ block.call(self)
148
+ else
149
+ klass.info(@show_id) do |info|
150
+ next block.call(nil) unless info
151
+ update_with_mapping(info)
152
+ block.call(self)
153
+ end
154
+ end
155
+ end
156
+
157
+ # @param &block [Block] Called back with the list of seasons
158
+ def season_list(&block)
159
+ if defined? Fiber and Fiber.respond_to? :current and not block
160
+ f = Fiber.current
161
+ method(__method__).call {|data| f.resume(data) }
162
+ return Fiber.yield
163
+ end
164
+ raise ArgumentError, "No block given for completion callback" unless block
165
+ puts "@season_list = #@season_list" if @season_list
166
+ return block.call(@season_list) if @season_list
167
+ self.class.episode_list(@show_id) do |episode_list|
168
+ update_with_mapping(episode_list)
169
+ block.call(@season_list)
170
+ end
171
+ true
172
+ end
173
+
174
+ # @param &block [Block] Called back with the list of episodes
175
+ def episode_list(&block)
176
+ if defined? Fiber and Fiber.respond_to? :current and not block
177
+ f = Fiber.current
178
+ method(__method__).call {|data| f.resume(data) }
179
+ return Fiber.yield
180
+ end
181
+ raise ArgumentError, "No block given for completion callback" unless block
182
+ season_list do |seasons|
183
+ block.call(seasons.collect { |season| season.episodes }.flatten)
184
+ end
185
+ true
186
+ end
187
+
188
+ class NotFound < RuntimeError
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,10 @@
1
+ module EventMachine
2
+ module Sofa
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 0
7
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,228 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Show>
3
+ <name>Castle (2009)</name>
4
+ <totalseasons>2</totalseasons>
5
+ <showid>19267</showid>
6
+ <showlink>http://tvrage.com/Castle</showlink>
7
+ <started>Mar/09/2009</started>
8
+ <ended/>
9
+ <image>http://images.tvrage.com/shows/20/19267.jpg</image>
10
+ <origin_country>US</origin_country>
11
+ <status>Returning Series</status>
12
+ <classification>Scripted</classification>
13
+ <genres>
14
+ <genre>Crime</genre>
15
+ <genre>Drama</genre>
16
+ <genre>Mystery</genre>
17
+ </genres>
18
+ <runtime>60</runtime>
19
+ <network country="US">ABC</network>
20
+ <airtime>22:00</airtime>
21
+ <airday>Monday</airday>
22
+ <timezone>GMT-5 -DST</timezone>
23
+ <Episodelist>
24
+ <Season no="1">
25
+ <episode>
26
+ <epnum>1</epnum>
27
+ <seasonnum>01</seasonnum>
28
+ <prodnum>101</prodnum>
29
+ <airdate>2009-03-09</airdate>
30
+ <link>http://www.tvrage.com/Castle/episodes/703445</link>
31
+ <title>Flowers for Your Grave</title>
32
+ <screencap>http://images.tvrage.com/screencaps/97/19267/703445.jpg</screencap>
33
+ </episode>
34
+ <episode>
35
+ <epnum>2</epnum>
36
+ <seasonnum>02</seasonnum>
37
+ <prodnum>102</prodnum>
38
+ <airdate>2009-03-16</airdate>
39
+ <link>http://www.tvrage.com/Castle/episodes/781859</link>
40
+ <title>Nanny McDead</title>
41
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781859.jpg</screencap>
42
+ </episode>
43
+ <episode>
44
+ <epnum>3</epnum>
45
+ <seasonnum>03</seasonnum>
46
+ <prodnum>103</prodnum>
47
+ <airdate>2009-03-23</airdate>
48
+ <link>http://www.tvrage.com/Castle/episodes/781860</link>
49
+ <title>Hedge Fund Homeboys</title>
50
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781860.jpg</screencap>
51
+ </episode>
52
+ <episode>
53
+ <epnum>4</epnum>
54
+ <seasonnum>04</seasonnum>
55
+ <prodnum>104</prodnum>
56
+ <airdate>2009-03-30</airdate>
57
+ <link>http://www.tvrage.com/Castle/episodes/781861</link>
58
+ <title>Hell Hath No Fury</title>
59
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781861.jpg</screencap>
60
+ </episode>
61
+ <episode>
62
+ <epnum>5</epnum>
63
+ <seasonnum>05</seasonnum>
64
+ <prodnum>105</prodnum>
65
+ <airdate>2009-04-06</airdate>
66
+ <link>http://www.tvrage.com/Castle/episodes/781862</link>
67
+ <title>A Chill Goes Through Her Veins</title>
68
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781862.jpg</screencap>
69
+ </episode>
70
+ <episode>
71
+ <epnum>6</epnum>
72
+ <seasonnum>06</seasonnum>
73
+ <prodnum>106</prodnum>
74
+ <airdate>2009-04-13</airdate>
75
+ <link>http://www.tvrage.com/Castle/episodes/781864</link>
76
+ <title>Always Buy Retail</title>
77
+ </episode>
78
+ <episode>
79
+ <epnum>7</epnum>
80
+ <seasonnum>07</seasonnum>
81
+ <prodnum>107</prodnum>
82
+ <airdate>2009-04-20</airdate>
83
+ <link>http://www.tvrage.com/Castle/episodes/781863</link>
84
+ <title>Home Is Where the Heart Stops</title>
85
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781863.jpg</screencap>
86
+ </episode>
87
+ <episode>
88
+ <epnum>8</epnum>
89
+ <seasonnum>08</seasonnum>
90
+ <prodnum>108</prodnum>
91
+ <airdate>2009-04-27</airdate>
92
+ <link>http://www.tvrage.com/Castle/episodes/781865</link>
93
+ <title>Ghosts</title>
94
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781865.jpg</screencap>
95
+ </episode>
96
+ <episode>
97
+ <epnum>9</epnum>
98
+ <seasonnum>09</seasonnum>
99
+ <prodnum>109</prodnum>
100
+ <airdate>2009-05-04</airdate>
101
+ <link>http://www.tvrage.com/Castle/episodes/781866</link>
102
+ <title>Little Girl Lost</title>
103
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781866.jpg</screencap>
104
+ </episode>
105
+ <episode>
106
+ <epnum>10</epnum>
107
+ <seasonnum>10</seasonnum>
108
+ <prodnum>110</prodnum>
109
+ <airdate>2009-05-11</airdate>
110
+ <link>http://www.tvrage.com/Castle/episodes/781867</link>
111
+ <title>A Death in the Family</title>
112
+ <screencap>http://images.tvrage.com/screencaps/97/19267/781867.jpg</screencap>
113
+ </episode>
114
+ </Season>
115
+ <Season no="2">
116
+ <episode>
117
+ <epnum>11</epnum>
118
+ <seasonnum>01</seasonnum>
119
+ <prodnum>201</prodnum>
120
+ <airdate>2009-09-21</airdate>
121
+ <link>http://www.tvrage.com/Castle/episodes/1064812069</link>
122
+ <title>Deep in Death</title>
123
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064812069.jpg</screencap>
124
+ </episode>
125
+ <episode>
126
+ <epnum>12</epnum>
127
+ <seasonnum>02</seasonnum>
128
+ <prodnum>202</prodnum>
129
+ <airdate>2009-09-28</airdate>
130
+ <link>http://www.tvrage.com/Castle/episodes/1064824888</link>
131
+ <title>The Double Down</title>
132
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064824888.jpg</screencap>
133
+ </episode>
134
+ <episode>
135
+ <epnum>13</epnum>
136
+ <seasonnum>03</seasonnum>
137
+ <prodnum>203</prodnum>
138
+ <airdate>2009-10-05</airdate>
139
+ <link>http://www.tvrage.com/Castle/episodes/1064824889</link>
140
+ <title>Inventing the Girl</title>
141
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064824889.jpg</screencap>
142
+ </episode>
143
+ <episode>
144
+ <epnum>14</epnum>
145
+ <seasonnum>04</seasonnum>
146
+ <prodnum>204</prodnum>
147
+ <airdate>2009-10-12</airdate>
148
+ <link>http://www.tvrage.com/Castle/episodes/1064824890</link>
149
+ <title>Fool Me Once</title>
150
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064824890.jpg</screencap>
151
+ </episode>
152
+ <episode>
153
+ <epnum>15</epnum>
154
+ <seasonnum>05</seasonnum>
155
+ <prodnum>205</prodnum>
156
+ <airdate>2009-10-19</airdate>
157
+ <link>http://www.tvrage.com/Castle/episodes/1064842856</link>
158
+ <title>When the Bough Breaks</title>
159
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064842856.jpg</screencap>
160
+ </episode>
161
+ <episode>
162
+ <epnum>16</epnum>
163
+ <seasonnum>06</seasonnum>
164
+ <prodnum>206</prodnum>
165
+ <airdate>2009-10-26</airdate>
166
+ <link>http://www.tvrage.com/Castle/episodes/1064842857</link>
167
+ <title>Vampire Weekend</title>
168
+ <screencap>http://images.tvrage.com/screencaps/97/19267/1064842857.jpg</screencap>
169
+ </episode>
170
+ <episode>
171
+ <epnum>17</epnum>
172
+ <seasonnum>07</seasonnum>
173
+ <prodnum>207</prodnum>
174
+ <airdate>2009-11-02</airdate>
175
+ <link>http://www.tvrage.com/Castle/episodes/1064842858</link>
176
+ <title>Famous Last Words</title>
177
+ </episode>
178
+ <episode>
179
+ <epnum>18</epnum>
180
+ <seasonnum>08</seasonnum>
181
+ <prodnum>208</prodnum>
182
+ <airdate>2009-11-09</airdate>
183
+ <link>http://www.tvrage.com/Castle/episodes/1064842859</link>
184
+ <title>Kill the Messenger</title>
185
+ </episode>
186
+ <episode>
187
+ <epnum>19</epnum>
188
+ <seasonnum>09</seasonnum>
189
+ <prodnum>209</prodnum>
190
+ <airdate>2009-11-16</airdate>
191
+ <link>http://www.tvrage.com/Castle/episodes/1064842860</link>
192
+ <title>Love Me Dead</title>
193
+ </episode>
194
+ <episode>
195
+ <epnum>20</epnum>
196
+ <seasonnum>10</seasonnum>
197
+ <prodnum>210</prodnum>
198
+ <airdate>2009-11-23</airdate>
199
+ <link>http://www.tvrage.com/Castle/episodes/1064842861</link>
200
+ <title>One Man's Treasure</title>
201
+ </episode>
202
+ <episode>
203
+ <epnum>21</epnum>
204
+ <seasonnum>11</seasonnum>
205
+ <prodnum>211</prodnum>
206
+ <airdate>2009-11-30</airdate>
207
+ <link>http://www.tvrage.com/Castle/episodes/1064842862</link>
208
+ <title>The Fifth Bullet</title>
209
+ </episode>
210
+ <episode>
211
+ <epnum>22</epnum>
212
+ <seasonnum>12</seasonnum>
213
+ <prodnum>212</prodnum>
214
+ <airdate>0000-00-00</airdate>
215
+ <link>http://www.tvrage.com/Castle/episodes/1064842863</link>
216
+ <title>Season 2, Episode 12</title>
217
+ </episode>
218
+ <episode>
219
+ <epnum>23</epnum>
220
+ <seasonnum>13</seasonnum>
221
+ <prodnum>213</prodnum>
222
+ <airdate>0000-00-00</airdate>
223
+ <link>http://www.tvrage.com/Castle/episodes/1064842864</link>
224
+ <title>Season 2, Episode 13</title>
225
+ </episode>
226
+ </Season>
227
+ </Episodelist>
228
+ </Show>