tvdb_client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.rubyversion +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +2 -0
- data/Guardfile +20 -0
- data/README.md +43 -0
- data/Rakefile +9 -0
- data/conf/settings.example.yml +7 -0
- data/lib/tvdb_client.rb +13 -0
- data/lib/tvdb_client/authorization.rb +46 -0
- data/lib/tvdb_client/client.rb +30 -0
- data/lib/tvdb_client/connection.rb +98 -0
- data/lib/tvdb_client/series.rb +54 -0
- data/lib/tvdb_client/series/base.rb +32 -0
- data/lib/tvdb_client/series/series_episodes.rb +18 -0
- data/lib/tvdb_client/series/series_filter.rb +21 -0
- data/lib/tvdb_client/series/series_images.rb +14 -0
- data/lib/tvdb_client/service/threading.rb +8 -0
- data/lib/tvdb_client/service/threading/threaded_request.rb +51 -0
- data/lib/tvdb_client/settings.rb +10 -0
- data/lib/tvdb_client/version.rb +3 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/fake_TVDB.rb +118 -0
- data/spec/support/fixtures/responses/login.json +3 -0
- data/spec/support/fixtures/responses/refresh_token.json +3 -0
- data/spec/support/fixtures/responses/series.json +33 -0
- data/spec/support/fixtures/responses/series_episodes_page_1.json +56 -0
- data/spec/support/fixtures/responses/series_episodes_page_2.json +56 -0
- data/spec/support/fixtures/responses/series_episodes_page_n.json +41 -0
- data/spec/support/fixtures/responses/series_episodes_query_airedSeason.json +41 -0
- data/spec/support/fixtures/responses/series_episodes_query_params.json +12 -0
- data/spec/support/fixtures/responses/series_episodes_summary.json +26 -0
- data/spec/support/fixtures/responses/series_filter.json +5 -0
- data/spec/support/fixtures/responses/series_filter_params.json +27 -0
- data/spec/support/fixtures/responses/series_images.json +9 -0
- data/spec/support/fixtures/responses/series_images_query.json +35 -0
- data/spec/support/fixtures/responses/series_images_query_params.json +60 -0
- data/spec/support/fixtures/settings.example.yml +6 -0
- data/spec/support/shared_contexts/authentication.rb +13 -0
- data/spec/support/shared_contexts/connection.rb +20 -0
- data/spec/support/shared_contexts/credentials.rb +20 -0
- data/spec/support/shared_contexts/series_subclass.rb +19 -0
- data/spec/tvdb_client/authorization_spec.rb +69 -0
- data/spec/tvdb_client/client_spec.rb +45 -0
- data/spec/tvdb_client/connection_spec.rb +159 -0
- data/spec/tvdb_client/series/base_spec.rb +13 -0
- data/spec/tvdb_client/series/series_episodes_spec.rb +55 -0
- data/spec/tvdb_client/series/series_filter_spec.rb +45 -0
- data/spec/tvdb_client/series/series_images_spec.rb +43 -0
- data/spec/tvdb_client/series_spec.rb +69 -0
- data/spec/tvdb_client/service/threading/threaded_request_spec.rb +54 -0
- data/spec/tvdb_client/version_spec.rb +12 -0
- data/tasks/version_bump.rake +102 -0
- data/templates/changelog_entry_template.md.erb +3 -0
- data/templates/version.rb.erb +3 -0
- data/tvdb_client.gemspec +41 -0
- metadata +412 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
module TVDB
|
2
|
+
class Series
|
3
|
+
class Images < TVDB::Series::Base
|
4
|
+
|
5
|
+
def initialize( options )
|
6
|
+
@connection = options.fetch( :connection )
|
7
|
+
@series_id = options.fetch( :series_id )
|
8
|
+
@parameters = options.fetch( :params ) { {} }
|
9
|
+
@route = "/series/#{series_id}/images"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TVDB
|
2
|
+
module Service
|
3
|
+
module Threading
|
4
|
+
class ThreadedRequest
|
5
|
+
|
6
|
+
attr_reader :connection, :pool_size
|
7
|
+
attr_accessor :threads
|
8
|
+
|
9
|
+
def initialize( options )
|
10
|
+
@connection = options.fetch( :connection )
|
11
|
+
@pool_size = options.fetch( :pool_size ) { 10 }
|
12
|
+
@threads = Array.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def make_request( route )
|
16
|
+
response = connection.get( route )
|
17
|
+
|
18
|
+
return response if response.code != 200
|
19
|
+
|
20
|
+
links = connection.get( route ).body["links"]
|
21
|
+
first_page = links["first"]
|
22
|
+
last_page = links["last"]
|
23
|
+
@results = Array.new
|
24
|
+
|
25
|
+
for page in first_page..last_page
|
26
|
+
manage_thread_pool
|
27
|
+
|
28
|
+
threads << Thread.new( page ) do |page_num|
|
29
|
+
params = { :params => { page: page_num } }
|
30
|
+
@results << connection.get( route, params ).body["data"]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
threads.each { |thread| thread.join }
|
35
|
+
|
36
|
+
return @results.flatten
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def manage_thread_pool
|
42
|
+
while threads.length > 10
|
43
|
+
threads.each { |thread| threads.delete( thread ) if thread.stop? }
|
44
|
+
sleep 0.01
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'settingslogic'
|
2
|
+
|
3
|
+
# Public: Reads in the config/settings.yml files and makes its entries
|
4
|
+
# available as Settings.whatever. You must require lib/settings.rb
|
5
|
+
# in classes where you wish to use this functionality. See manifest_scraper.rb
|
6
|
+
# for an example
|
7
|
+
#
|
8
|
+
class Settings < Settingslogic
|
9
|
+
source File.expand_path( "../../../conf/settings.example.yml", __FILE__ )
|
10
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'spork'
|
9
|
+
require 'rspec'
|
10
|
+
require 'pp'
|
11
|
+
require 'pry-byebug'
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
Spork.prefork do
|
15
|
+
require 'webmock/rspec'
|
16
|
+
require 'simplecov'
|
17
|
+
|
18
|
+
ENV['RSPEC'] = 'true'
|
19
|
+
|
20
|
+
SimpleCov.start do
|
21
|
+
add_filter "/bundle"
|
22
|
+
add_filter "/spec"
|
23
|
+
end
|
24
|
+
|
25
|
+
WebMock.disable_net_connect!( allow_localhost: true )
|
26
|
+
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.before(:each) do
|
29
|
+
stub_request( :any, /api.thetvdb.localhost.com/).to_rack( FakeTVDB )
|
30
|
+
end
|
31
|
+
|
32
|
+
config.run_all_when_everything_filtered = true
|
33
|
+
config.filter_run :focus
|
34
|
+
|
35
|
+
# Run specs in random order to surface order dependencies. If you find an
|
36
|
+
# order dependency and want to debug it, you can fix the order by providing
|
37
|
+
# the seed, which is printed after each run.
|
38
|
+
# --seed 1234
|
39
|
+
config.order = 'random'
|
40
|
+
config.fail_fast = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Spork.each_run do
|
45
|
+
# This code will be run each time you run your specs.
|
46
|
+
RSpec.configure do |config|
|
47
|
+
base_dir = File.expand_path("../../", __FILE__)
|
48
|
+
|
49
|
+
Dir["#{base_dir}/lib/tvdb_client/**/*.rb"].each {|f| require f}
|
50
|
+
Dir["#{base_dir}/spec/support/**/*.rb"].sort.each { |f| require f}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
class FakeTVDB < Sinatra::Base
|
4
|
+
post '/login' do
|
5
|
+
if JSON.parse( request.body.read )["username"] == "validuser"
|
6
|
+
json_response 200, 'responses/login.json'
|
7
|
+
else
|
8
|
+
unauthed_json_response
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
get '/refresh_token' do
|
13
|
+
if bad_auth_header?( request )
|
14
|
+
status 401
|
15
|
+
else
|
16
|
+
json_response 200, 'responses/refresh_token.json'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/series/:id' do
|
21
|
+
series_routes( request, params["id"], 'responses/series.json')
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/not/modified' do
|
25
|
+
not_modified_response
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/series/:id/episodes' do
|
29
|
+
page = params["page"]
|
30
|
+
|
31
|
+
if params["id"] == "76703"
|
32
|
+
if page == "1" || !page
|
33
|
+
series_routes( request, params["id"], 'responses/series_episodes_page_1.json')
|
34
|
+
elsif page == "2"
|
35
|
+
series_routes( request, params["id"], 'responses/series_episodes_page_2.json')
|
36
|
+
end
|
37
|
+
elsif params["id"] == "99999"
|
38
|
+
series_routes( request, params["id"], 'responses/series_episodes_page_n.json')
|
39
|
+
else
|
40
|
+
not_found_json_response( params["id"] )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/series/:id/episodes/query' do
|
45
|
+
aired_season = params["airedSeason"]
|
46
|
+
|
47
|
+
json_response 200, 'responses/series_episodes_query_airedSeason.json'
|
48
|
+
end
|
49
|
+
|
50
|
+
get '/series/:id/episodes/query/params' do
|
51
|
+
json_response 200, 'responses/series_episodes_query_params.json'
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/series/:id/episodes/summary' do
|
55
|
+
json_response 200, 'responses/series_episodes_summary.json'
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/series/:id/images' do
|
59
|
+
json_response 200, 'responses/series_images.json'
|
60
|
+
end
|
61
|
+
|
62
|
+
get '/series/:id/images/query' do
|
63
|
+
json_response 200, 'responses/series_images_query.json'
|
64
|
+
end
|
65
|
+
|
66
|
+
get '/series/:id/images/query/params' do
|
67
|
+
json_response 200, 'responses/series_images_query_params.json'
|
68
|
+
end
|
69
|
+
|
70
|
+
get '/series/:id/filter' do
|
71
|
+
return not_modified_response unless params.has_key?( "keys" )
|
72
|
+
json_response 200, 'responses/series_filter.json' if params["keys"] == "seriesName"
|
73
|
+
end
|
74
|
+
|
75
|
+
get '/series/:id/filter/params' do
|
76
|
+
json_response 200, 'responses/series_filter_params.json'
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def series_routes( request, id, json_response_string )
|
82
|
+
if bad_auth_header?( request )
|
83
|
+
unauthed_json_response
|
84
|
+
elsif id == '76703' || id == "99999"
|
85
|
+
json_response 200, json_response_string
|
86
|
+
else
|
87
|
+
not_found_json_response( id )
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def json_response(response_code, file_name)
|
92
|
+
content_type :json
|
93
|
+
status response_code
|
94
|
+
File.open(File.dirname(__FILE__) + '/fixtures/' + file_name, 'rb').read
|
95
|
+
end
|
96
|
+
|
97
|
+
def unauthed_json_response
|
98
|
+
content_type :json
|
99
|
+
status 401
|
100
|
+
'{"Error":"API Key Required"}'
|
101
|
+
end
|
102
|
+
|
103
|
+
def not_found_json_response( id )
|
104
|
+
content_type :json
|
105
|
+
status 404
|
106
|
+
"{\"Error\": \"ID: #{id} not found\"}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def not_modified_response
|
110
|
+
content_type :json
|
111
|
+
status 304
|
112
|
+
""
|
113
|
+
end
|
114
|
+
|
115
|
+
def bad_auth_header?( request )
|
116
|
+
request.env["HTTP_AUTHORIZATION"] == "Bearer" ? true : false
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,3 @@
|
|
1
|
+
{
|
2
|
+
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MzcyNDUzNTcsImlkIjoiTXkgb3duIHNjcmlwdHMiLCJvcmlnX2lhdCI6MTQzNzI0MTc1NywidXNlcmlkIjoyNDM5NDEsInVzZXJuYW1lIjoiZm9vIn0.TQCkOSmKht3imZ5RRu6VDtCcEfwgwccEdIKas_zxBdX9A7ILGbnS98CPn6ZLTVd76M-U6vUbinOMfRKWN99JCsE1FTKV84hBt8fd7UPIcH08e6n9eUlwJVAU4GjB6y4dwY3JPjAYKQicykm4k7Up_GmJ8pUJ0qpgDZg2z9bHdpX1wf8nj66RdVZsVLmdczHW3kCDrhiAHfec58j-j6jBARSmseVP4vJ8Rud77RxgQuZDgY4AjG-nHpfiooX_Xo_pS9M8I6boskXHgUUI9MGUqONpYDeo2DXyCT3BRxNpklWu-fDZ2QvNx2CqL5njwJ7gte9_6DeGmiPmZ7UYcK71sg"
|
3
|
+
}
|
@@ -0,0 +1,3 @@
|
|
1
|
+
{
|
2
|
+
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Mzc0MjE1MjMsImlkIjoiTXkgb3duIHNjcmlwdHMiLCJvcmlnX2lhdCI6MTQzNzQxNzg2OCwidXNlcmlkIjoyNDM5NDEsInVzZXJuYW1lIjoiZm9vIn0.kp9OFogBHG202F1NW_HX1SGoHijSwj4w8CWZa55xfmZRypSjTyqj93MOLfh6qSjSOZ2au4RuZqqUfRaWC3IgIrondGAk6dStq0dNs9dBkJ9kfcUO6U7_RCAjziv597kV7Q1dyRGr1g8g3j4l_dTdqjHlyQSk2jXiSl6zulKYCmfo9PHC0ID_jo__i2yh3he21_tc_5rTaJXz8q-CiIsl7juGWqV_egqLsab3Rj1RSWrWJGtPXzQIC2nn-u5-8fmcU8htMoribb2PGYr0it6UFNm-_iBMtJnNJjSGfCJNLhKpP7KqOzj_5C_yHH1QzJnS4AA_AjIjbKnp30jnzzPnwQ"
|
3
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"data": {
|
3
|
+
"id": 76703,
|
4
|
+
"seriesName": "Pokémon",
|
5
|
+
"aliases": [
|
6
|
+
"Pokemon",
|
7
|
+
"Diamond and Pearl",
|
8
|
+
"Pocket Monsters",
|
9
|
+
"Pokémon: Black & White"
|
10
|
+
],
|
11
|
+
"banner": "graphical/76703-g6.jpg",
|
12
|
+
"seriesId": "467",
|
13
|
+
"status": "Continuing",
|
14
|
+
"firstAired": "1997-04-01",
|
15
|
+
"network": "TV Tokyo",
|
16
|
+
"networkId": "",
|
17
|
+
"runtime": "30",
|
18
|
+
"genre": [
|
19
|
+
"Animation",
|
20
|
+
"Children"
|
21
|
+
],
|
22
|
+
"actors": [],
|
23
|
+
"overview": " A young boy named Ash Ketchum embarks on a journey to become a \"Pokemon Master\" with his first Pokemon, Pikachu. Joining him on his travels are Brock, a girl-obsessed Rock Pokemon Trainer, and Misty, a tomboyish Water Pokemon Trainer who may have a crush on him. Ash and Co. end up traveling through various regions, including Kanto, the Orange Islands, and Johto, and then enter the Pokemon League competitions there. Along the way, they run into many confrontations with Jessie, James, and Meowth, a trio of Pokemon thieves who are apart of an evil organization called \"Team Rocket\". But everytime Team Rocket try to do their evil deeds, they fail thanks to Ash and his Pokemon.",
|
24
|
+
"lastUpdated": 1437155768,
|
25
|
+
"airsDayOfWeek": "",
|
26
|
+
"airsTime": "",
|
27
|
+
"rating": "TV-Y",
|
28
|
+
"imdbId": "tt0176385",
|
29
|
+
"zap2itId": "",
|
30
|
+
"added": "",
|
31
|
+
"addedBy": null
|
32
|
+
}
|
33
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
{
|
2
|
+
"errors": null,
|
3
|
+
"links": {
|
4
|
+
"first": 1,
|
5
|
+
"last": 2,
|
6
|
+
"next": 2,
|
7
|
+
"prev": null
|
8
|
+
},
|
9
|
+
"data": [
|
10
|
+
{
|
11
|
+
"absoluteNumber": "1",
|
12
|
+
"airedEpisodeNumber": 1,
|
13
|
+
"airedSeason": "1",
|
14
|
+
"dvdEpisodeNumber": null,
|
15
|
+
"dvdSeason": null,
|
16
|
+
"episodeName": "Pokémon! I Choose You!",
|
17
|
+
"firstAired": "1997-04-01",
|
18
|
+
"id": 208318,
|
19
|
+
"language": {
|
20
|
+
"episodeName": "en",
|
21
|
+
"overview": "en"
|
22
|
+
},
|
23
|
+
"overview": "After beginning his Pokémon journey, Ash is supposed to meet Professor Oak - a Pokémon researcher who is giving Pokémon to all the 10-year-olds in Pallet Town. Arriving late, Ash finds that all the Pokémon are taken - all except for Pikachu. After a harrowing misadventure together, Pikachu and Ash form an unbreakable bond of friendship."
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"absoluteNumber": "83",
|
27
|
+
"airedEpisodeNumber": 1,
|
28
|
+
"airedSeason": "2",
|
29
|
+
"dvdEpisodeNumber": null,
|
30
|
+
"dvdSeason": null,
|
31
|
+
"episodeName": "Pallet Party Panic",
|
32
|
+
"firstAired": "1999-12-04",
|
33
|
+
"id": 208400,
|
34
|
+
"language": {
|
35
|
+
"episodeName": "en",
|
36
|
+
"overview": "en"
|
37
|
+
},
|
38
|
+
"overview": "Following the completion of the Pokémon League, Ash is back home in Pallet Town. Professor Oak and Delia throw him a party which of course Team Rocket crashes, but Ash is able to send them off with the help of Charizard, who proceeds to wreck the party. Later that day Professor Oak asks Ash to run a errand for him. He wants Ash to go to the Orange Islands and pick up the GS Ball from Professor Ivy. He agrees and leaves soon after. Along the way they become trapped ina forest where the same flock of Spearow from the beginning of Ash's journey is still causing trouble, along with their newly evolved Fearow leader. Now they must figure out a way to get out of the forest before the Spearow manage to get to them."
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"absoluteNumber": "119",
|
42
|
+
"airedEpisodeNumber": 1,
|
43
|
+
"airedSeason": "3",
|
44
|
+
"dvdEpisodeNumber": null,
|
45
|
+
"dvdSeason": null,
|
46
|
+
"episodeName": "Don't Touch That 'Dile",
|
47
|
+
"firstAired": "2000-10-14",
|
48
|
+
"id": 208436,
|
49
|
+
"language": {
|
50
|
+
"episodeName": "en",
|
51
|
+
"overview": "en"
|
52
|
+
},
|
53
|
+
"overview": "Ash begins his journey in Johto, a region largely unexplored and populated with Pokémon entirely new to him and his friends.\r\n"
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
{
|
2
|
+
"errors": null,
|
3
|
+
"links": {
|
4
|
+
"first": 1,
|
5
|
+
"last": 2,
|
6
|
+
"next": null,
|
7
|
+
"prev": 1
|
8
|
+
},
|
9
|
+
"data": [
|
10
|
+
{
|
11
|
+
"absoluteNumber": "749",
|
12
|
+
"airedEpisodeNumber": 6,
|
13
|
+
"airedSeason": "15",
|
14
|
+
"dvdEpisodeNumber": null,
|
15
|
+
"dvdSeason": null,
|
16
|
+
"episodeName": "Jostling for the Junior Cup!",
|
17
|
+
"firstAired": "2012-08-02",
|
18
|
+
"id": 4359831,
|
19
|
+
"language": {
|
20
|
+
"episodeName": "en",
|
21
|
+
"overview": "en"
|
22
|
+
},
|
23
|
+
"overview": "Ash and his friends arrive in Lacunosa Town for the Junior Cup. The Cup begins with an exhibition match between Cynthia and Caitlyn. Iris and Georgia face off against each other but Iris’ newly caught Dragonite refuses to listen to Iris’ commands."
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"absoluteNumber": null,
|
27
|
+
"airedEpisodeNumber": 6,
|
28
|
+
"airedSeason": "16",
|
29
|
+
"dvdEpisodeNumber": null,
|
30
|
+
"dvdSeason": null,
|
31
|
+
"episodeName": "Battling on thin ice!",
|
32
|
+
"firstAired": "2013-11-14",
|
33
|
+
"id": 4709580,
|
34
|
+
"language": {
|
35
|
+
"episodeName": "en",
|
36
|
+
"overview": "en"
|
37
|
+
},
|
38
|
+
"overview": "Ash challenges Viola to a rematch, now ready for Surskit's freezing strategy. "
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"absoluteNumber": "7",
|
42
|
+
"airedEpisodeNumber": 7,
|
43
|
+
"airedSeason": "1",
|
44
|
+
"dvdEpisodeNumber": null,
|
45
|
+
"dvdSeason": null,
|
46
|
+
"episodeName": "The Water Flowers of Cerulean City",
|
47
|
+
"firstAired": "1997-05-13",
|
48
|
+
"id": 208324,
|
49
|
+
"language": {
|
50
|
+
"episodeName": "en",
|
51
|
+
"overview": "en"
|
52
|
+
},
|
53
|
+
"overview": "In his search to collect Trainer Badges, Ash heads for Cerulean City, intending to confront the Cerulean Gym Leader. For some reason, Misty expresses a desire to avoid Cerulean City and disappears somewhere along the way. Without concern for Misty, Ash and Pikachu continue toward the Cerulean City Gym. When they arrive, the exterior of the Gym gives the strange appearance of an aquarium. Upon entering the building, Ash and Pikachu are entranced by a synchronized swimming show being performed by three beautiful sisters. Could this truly be the Cerulean Gym? And what could be the role of the three beautiful swimming sisters? More importantly, will Ash be able to defeat the strangely absent Gym Leader to earn his next badge?\\\r\\\n"
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"errors": null,
|
3
|
+
"links": {
|
4
|
+
"first": 1,
|
5
|
+
"last": 20,
|
6
|
+
"next": 2,
|
7
|
+
"prev": 1
|
8
|
+
},
|
9
|
+
"data": [
|
10
|
+
{
|
11
|
+
"absoluteNumber": "749",
|
12
|
+
"airedEpisodeNumber": 6,
|
13
|
+
"airedSeason": "15",
|
14
|
+
"dvdEpisodeNumber": null,
|
15
|
+
"dvdSeason": null,
|
16
|
+
"episodeName": "Jostling for the Junior Cup!",
|
17
|
+
"firstAired": "2012-08-02",
|
18
|
+
"id": 4359831,
|
19
|
+
"language": {
|
20
|
+
"episodeName": "en",
|
21
|
+
"overview": "en"
|
22
|
+
},
|
23
|
+
"overview": "Ash and his friends arrive in Lacunosa Town for the Junior Cup. The Cup begins with an exhibition match between Cynthia and Caitlyn. Iris and Georgia face off against each other but Iris’ newly caught Dragonite refuses to listen to Iris’ commands."
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"absoluteNumber": null,
|
27
|
+
"airedEpisodeNumber": 6,
|
28
|
+
"airedSeason": "16",
|
29
|
+
"dvdEpisodeNumber": null,
|
30
|
+
"dvdSeason": null,
|
31
|
+
"episodeName": "Battling on thin ice!",
|
32
|
+
"firstAired": "2013-11-14",
|
33
|
+
"id": 4709580,
|
34
|
+
"language": {
|
35
|
+
"episodeName": "en",
|
36
|
+
"overview": "en"
|
37
|
+
},
|
38
|
+
"overview": "Ash challenges Viola to a rematch, now ready for Surskit's freezing strategy. "
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|