1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Hummingbird Anime Client |
4
|
|
|
* |
5
|
|
|
* An API client for Hummingbird to manage anime and manga watch lists |
6
|
|
|
* |
7
|
|
|
* @package HummingbirdAnimeClient |
8
|
|
|
* @author Timothy J. Warren |
9
|
|
|
* @copyright Copyright (c) 2015 - 2016 |
10
|
|
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient |
11
|
|
|
* @license MIT |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace Aviat\AnimeClient\Model; |
15
|
|
|
|
16
|
|
|
use GuzzleHttp\Cookie\Cookiejar; |
17
|
|
|
use GuzzleHttp\Cookie\SetCookie; |
18
|
|
|
|
19
|
|
|
use Aviat\Ion\Json; |
20
|
|
|
use Aviat\AnimeClient\Model\API; |
21
|
|
|
use Aviat\AnimeClient\Hummingbird\Transformer; |
22
|
|
|
use Aviat\AnimeClient\Hummingbird\Enum\MangaReadingStatus; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Model for handling requests dealing with the manga list |
26
|
|
|
*/ |
27
|
|
|
class Manga extends API { |
28
|
|
|
|
29
|
|
|
const READING = 'Reading'; |
30
|
|
|
const PLAN_TO_READ = 'Plan to Read'; |
31
|
|
|
const DROPPED = 'Dropped'; |
32
|
|
|
const ON_HOLD = 'On Hold'; |
33
|
|
|
const COMPLETED = 'Completed'; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Map API constants to display constants |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
protected $const_map = [ |
40
|
|
|
MangaReadingStatus::READING => self::READING, |
41
|
|
|
MangaReadingStatus::PLAN_TO_READ => self::PLAN_TO_READ, |
42
|
|
|
MangaReadingStatus::ON_HOLD => self::ON_HOLD, |
43
|
|
|
MangaReadingStatus::DROPPED => self::DROPPED, |
44
|
|
|
MangaReadingStatus::COMPLETED => self::COMPLETED |
45
|
|
|
]; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* The base url for api requests |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
protected $base_url = "https://hummingbird.me/"; |
52
|
|
|
|
53
|
|
|
protected function _manga_api_call($type, $url, $json=null) |
54
|
|
|
{ |
55
|
|
|
$token = $this->container->get('auth') |
56
|
|
|
->get_auth_token(); |
57
|
|
|
|
58
|
|
|
// Set the token cookie, with the authentication token |
59
|
|
|
// from the auth class. |
60
|
|
|
$cookieJar = $this->cookieJar; |
61
|
|
|
$cookie_data = new SetCookie([ |
62
|
|
|
'Name' => 'token', |
63
|
|
|
'Value' => $token, |
64
|
|
|
'Domain' => 'hummingbird.me' |
65
|
|
|
]); |
66
|
|
|
$cookieJar->setCookie($cookie_data); |
67
|
|
|
|
68
|
|
|
$config = [ |
69
|
|
|
'cookies' => $cookieJar |
70
|
|
|
]; |
71
|
|
|
|
72
|
|
|
if ( ! is_null($json)) |
73
|
|
|
{ |
74
|
|
|
$config['json'] = $json; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
$result = $this->client->request(strtoupper($type), $url, $config); |
78
|
|
|
|
79
|
|
|
return [ |
80
|
|
|
'statusCode' => $result->getStatusCode(), |
81
|
|
|
'body' => $result->getBody() |
82
|
|
|
]; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Add a manga to the list |
87
|
|
|
* |
88
|
|
|
* @param array $data |
89
|
|
|
*/ |
90
|
|
|
public function add($data) |
91
|
|
|
{ |
92
|
|
|
$object = [ |
93
|
|
|
'manga_library_entry' => [ |
94
|
|
|
'status' => $data['status'], |
95
|
|
|
'manga_id' => $data['id'] |
96
|
|
|
] |
97
|
|
|
]; |
98
|
|
|
|
99
|
|
|
return $this->_manga_api_call('post', 'manga_library_entries', $object); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Update the selected manga |
104
|
|
|
* |
105
|
|
|
* @param array $data |
106
|
|
|
* @return array |
107
|
|
|
*/ |
108
|
|
|
public function update($data) |
109
|
|
|
{ |
110
|
|
|
$id = $data['id']; |
111
|
|
|
|
112
|
|
|
return $this->_manga_api_call( |
113
|
|
|
'put', |
114
|
|
|
"manga_library_entries/{$id}", |
115
|
|
|
['manga_library_entry' => $data] |
116
|
|
|
); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Delete a manga entry |
121
|
|
|
* |
122
|
|
|
* @param array $data |
123
|
|
|
* @return array |
124
|
|
|
*/ |
125
|
|
|
public function delete($data) |
126
|
|
|
{ |
127
|
|
|
$id = $data['id']; |
128
|
|
|
|
129
|
|
|
return $this->_manga_api_call('delete', "manga_library_entries/{$id}"); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Search for manga by name |
134
|
|
|
* |
135
|
|
|
* @param string $name |
136
|
|
|
* @return array |
137
|
|
|
*/ |
138
|
|
|
public function search($name) |
139
|
|
|
{ |
140
|
|
|
$logger = $this->container->getLogger('default'); |
141
|
|
|
|
142
|
|
|
$config = [ |
143
|
|
|
'query' => [ |
144
|
|
|
'scope' => 'manga', |
145
|
|
|
'depth' => 'full', |
146
|
|
|
'query' => $name |
147
|
|
|
] |
148
|
|
|
]; |
149
|
|
|
|
150
|
|
|
$response = $this->get('search.json', $config); |
151
|
|
|
|
152
|
|
View Code Duplication |
if ($response->getStatusCode() != 200) |
|
|
|
|
153
|
|
|
{ |
154
|
|
|
$logger->warning("Non 200 response for search api call"); |
155
|
|
|
$logger->warning($response->getBody()); |
156
|
|
|
|
157
|
|
|
throw new RuntimeException($response->getEffectiveUrl()); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
return Json::decode($response->getBody(), TRUE); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Get the full set of anime lists |
165
|
|
|
* |
166
|
|
|
* @return array |
167
|
|
|
*/ |
168
|
|
|
public function get_all_lists() |
169
|
|
|
{ |
170
|
|
|
$data = $this->_get_list_from_api(); |
171
|
|
|
|
172
|
|
|
foreach ($data as &$val) |
173
|
|
|
{ |
174
|
|
|
$this->sort_by_name($val, 'manga'); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
return $data; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Get a category out of the full list |
182
|
|
|
* |
183
|
|
|
* @param string $status |
184
|
|
|
* @return array |
185
|
|
|
*/ |
186
|
|
|
public function get_list($status) |
187
|
|
|
{ |
188
|
|
|
$data = $this->_get_list_from_api($status); |
189
|
|
|
$this->sort_by_name($data, 'manga'); |
190
|
|
|
|
191
|
|
|
return $data; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Retrieve the list from the hummingbird api |
196
|
|
|
* |
197
|
|
|
* @param string $status |
198
|
|
|
* @return array |
199
|
|
|
*/ |
200
|
|
|
protected function _get_list_from_api($status = "All") |
201
|
|
|
{ |
202
|
|
|
$config = [ |
203
|
|
|
'query' => [ |
204
|
|
|
'user_id' => $this->config->get('hummingbird_username') |
205
|
|
|
], |
206
|
|
|
'allow_redirects' => FALSE |
207
|
|
|
]; |
208
|
|
|
|
209
|
|
|
$response = $this->get('manga_library_entries', $config); |
210
|
|
|
$data = $this->_check_cache($response); |
211
|
|
|
$output = $this->map_by_status($data); |
212
|
|
|
|
213
|
|
|
return (array_key_exists($status, $output)) |
214
|
|
|
? $output[$status] |
215
|
|
|
: $output; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Check the status of the cache and return the appropriate response |
220
|
|
|
* |
221
|
|
|
* @param \GuzzleHttp\Message\Response $response |
222
|
|
|
* @codeCoverageIgnore |
223
|
|
|
* @return array |
224
|
|
|
*/ |
225
|
|
|
private function _check_cache($response) |
226
|
|
|
{ |
227
|
|
|
// Bail out early if there isn't any manga data |
228
|
|
|
$api_data = Json::decode($response->getBody(), TRUE); |
229
|
|
|
if ( ! array_key_exists('manga', $api_data)) |
230
|
|
|
{ |
231
|
|
|
return []; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
$cache_file = _dir($this->config->get('data_cache_path'), 'manga.json'); |
235
|
|
|
$transformed_cache_file = _dir( |
236
|
|
|
$this->config->get('data_cache_path'), |
237
|
|
|
'manga-transformed.json' |
238
|
|
|
); |
239
|
|
|
|
240
|
|
|
$cached_data = file_exists($cache_file) |
241
|
|
|
? Json::decodeFile($cache_file) |
242
|
|
|
: []; |
243
|
|
|
|
244
|
|
View Code Duplication |
if ($cached_data === $api_data && file_exists($transformed_cache_file)) |
|
|
|
|
245
|
|
|
{ |
246
|
|
|
return Json::decodeFile($transformed_cache_file); |
247
|
|
|
} |
248
|
|
|
else |
249
|
|
|
{ |
250
|
|
|
Json::encodeFile($cache_file, $api_data); |
251
|
|
|
|
252
|
|
|
$zippered_data = $this->zipper_lists($api_data); |
253
|
|
|
$transformer = new Transformer\MangaListTransformer(); |
254
|
|
|
$transformed_data = $transformer->transform_collection($zippered_data); |
255
|
|
|
Json::encodeFile($transformed_cache_file, $transformed_data); |
256
|
|
|
return $transformed_data; |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Map transformed anime data to be organized by reading status |
262
|
|
|
* |
263
|
|
|
* @param array $data |
264
|
|
|
* @return array |
265
|
|
|
*/ |
266
|
|
|
private function map_by_status($data) |
267
|
|
|
{ |
268
|
|
|
$output = [ |
269
|
|
|
self::READING => [], |
270
|
|
|
self::PLAN_TO_READ => [], |
271
|
|
|
self::ON_HOLD => [], |
272
|
|
|
self::DROPPED => [], |
273
|
|
|
self::COMPLETED => [], |
274
|
|
|
]; |
275
|
|
|
|
276
|
|
|
foreach ($data as &$entry) |
277
|
|
|
{ |
278
|
|
|
$entry['manga']['image'] = $this->get_cached_image( |
279
|
|
|
$entry['manga']['image'], |
280
|
|
|
$entry['manga']['slug'], |
281
|
|
|
'manga' |
282
|
|
|
); |
283
|
|
|
$key = $this->const_map[$entry['reading_status']]; |
284
|
|
|
$output[$key][] = $entry; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return $output; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Combine the two manga lists into one |
292
|
|
|
* @param array $raw_data |
293
|
|
|
* @return array |
294
|
|
|
*/ |
295
|
|
|
private function zipper_lists($raw_data) |
296
|
|
|
{ |
297
|
|
|
return (new Transformer\MangaListsZipper($raw_data))->transform(); |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
// End of MangaModel.php |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.