1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Blacklight; |
4
|
|
|
|
5
|
|
|
use ApaiIO\ApaiIO; |
6
|
|
|
use Blacklight\db\DB; |
7
|
|
|
use GuzzleHttp\Client; |
8
|
|
|
use App\Models\Release; |
9
|
|
|
use App\Models\BookInfo; |
10
|
|
|
use App\Models\Category; |
11
|
|
|
use App\Models\Settings; |
12
|
|
|
use ApaiIO\Operations\Search; |
13
|
|
|
use Illuminate\Support\Carbon; |
14
|
|
|
use ApaiIO\Configuration\Country; |
15
|
|
|
use ApaiIO\Request\GuzzleRequest; |
16
|
|
|
use Illuminate\Support\Facades\Cache; |
17
|
|
|
use ApaiIO\Configuration\GenericConfiguration; |
18
|
|
|
use ApaiIO\ResponseTransformer\XmlToSimpleXmlObject; |
19
|
|
|
|
20
|
|
|
class Books |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* @var \Blacklight\db\DB |
24
|
|
|
*/ |
25
|
|
|
public $pdo; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var bool |
29
|
|
|
*/ |
30
|
|
|
public $echooutput; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var null|string |
34
|
|
|
*/ |
35
|
|
|
public $pubkey; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var null|string |
39
|
|
|
*/ |
40
|
|
|
public $privkey; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var null|string |
44
|
|
|
*/ |
45
|
|
|
public $asstag; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var int|null|string |
49
|
|
|
*/ |
50
|
|
|
public $bookqty; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var int|null|string |
54
|
|
|
*/ |
55
|
|
|
public $sleeptime; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var string |
59
|
|
|
*/ |
60
|
|
|
public $imgSavePath; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var null|string |
64
|
|
|
*/ |
65
|
|
|
public $bookreqids; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var string |
69
|
|
|
*/ |
70
|
|
|
public $renamed; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @var array |
74
|
|
|
*/ |
75
|
|
|
public $failCache; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @param array $options Class instances / Echo to cli. |
79
|
|
|
* |
80
|
|
|
* @throws \Exception |
81
|
|
|
*/ |
82
|
|
|
public function __construct(array $options = []) |
83
|
|
|
{ |
84
|
|
|
$defaults = [ |
85
|
|
|
'Echo' => false, |
86
|
|
|
'Settings' => null, |
87
|
|
|
]; |
88
|
|
|
$options += $defaults; |
89
|
|
|
|
90
|
|
|
$this->echooutput = ($options['Echo'] && config('nntmux.echocli')); |
91
|
|
|
$this->pdo = ($options['Settings'] instanceof DB ? $options['Settings'] : new DB()); |
92
|
|
|
|
93
|
|
|
$this->pubkey = Settings::settingValue('APIs..amazonpubkey'); |
|
|
|
|
94
|
|
|
$this->privkey = Settings::settingValue('APIs..amazonprivkey'); |
95
|
|
|
$this->asstag = Settings::settingValue('APIs..amazonassociatetag'); |
96
|
|
|
$this->bookqty = Settings::settingValue('..maxbooksprocessed') !== '' ? (int) Settings::settingValue('..maxbooksprocessed') : 300; |
97
|
|
|
$this->sleeptime = Settings::settingValue('..amazonsleep') !== '' ? (int) Settings::settingValue('..amazonsleep') : 1000; |
98
|
|
|
$this->imgSavePath = NN_COVERS.'book'.DS; |
|
|
|
|
99
|
|
|
$result = Settings::settingValue('..book_reqids'); |
100
|
|
|
$this->bookreqids = $result ?? Category::BOOKS_EBOOK; |
101
|
|
|
$this->renamed = (int) Settings::settingValue('..lookupbooks') === 2 ? 'AND isrenamed = 1' : ''; |
102
|
|
|
|
103
|
|
|
$this->failCache = []; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param $id |
108
|
|
|
* |
109
|
|
|
* @return \Illuminate\Database\Eloquent\Model|null|static |
110
|
|
|
*/ |
111
|
|
|
public function getBookInfo($id) |
112
|
|
|
{ |
113
|
|
|
return BookInfo::query()->where('id', $id)->first(); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param $title |
118
|
|
|
* |
119
|
|
|
* @return \Illuminate\Database\Eloquent\Model |
120
|
|
|
*/ |
121
|
|
|
public function getBookInfoByName($title) |
122
|
|
|
{ |
123
|
|
|
|
124
|
|
|
//only used to get a count of words |
125
|
|
|
$searchWords = $searchsql = ''; |
|
|
|
|
126
|
|
|
$title = preg_replace('/( - | -|\(.+\)|\(|\))/', ' ', $title); |
127
|
|
|
$title = preg_replace('/[^\w ]+/', '', $title); |
128
|
|
|
$title = trim(preg_replace('/\s\s+/i', ' ', $title)); |
129
|
|
|
$title = trim($title); |
130
|
|
|
$words = explode(' ', $title); |
131
|
|
|
|
132
|
|
|
foreach ($words as $word) { |
133
|
|
|
$word = trim(rtrim(trim($word), '-')); |
134
|
|
|
if ($word !== '' && $word !== '-') { |
135
|
|
|
$word = '+'.$word; |
136
|
|
|
$searchWords .= sprintf('%s ', $word); |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
$searchWords = trim($searchWords); |
140
|
|
|
|
141
|
|
|
return BookInfo::search($searchWords)->first(); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @param $cat |
146
|
|
|
* @param $start |
147
|
|
|
* @param $num |
148
|
|
|
* @param $orderby |
149
|
|
|
* @param array $excludedcats |
150
|
|
|
* @return array |
151
|
|
|
* @throws \Exception |
152
|
|
|
*/ |
153
|
|
|
public function getBookRange($cat, $start, $num, $orderby, array $excludedcats = []): array |
154
|
|
|
{ |
155
|
|
|
$browseby = $this->getBrowseBy(); |
156
|
|
|
|
157
|
|
|
$catsrch = ''; |
158
|
|
|
if (\count($cat) > 0 && $cat[0] !== -1) { |
159
|
|
|
$catsrch = Category::getCategorySearch($cat); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$exccatlist = ''; |
163
|
|
|
if (\count($excludedcats) > 0) { |
164
|
|
|
$exccatlist = ' AND r.categories_id NOT IN ('.implode(',', $excludedcats).')'; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$order = $this->getBookOrder($orderby); |
168
|
|
|
|
169
|
|
|
$booksql = sprintf( |
170
|
|
|
" |
171
|
|
|
SELECT SQL_CALC_FOUND_ROWS boo.id, |
172
|
|
|
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id |
173
|
|
|
FROM bookinfo boo |
174
|
|
|
LEFT JOIN releases r ON boo.id = r.bookinfo_id |
175
|
|
|
WHERE r.nzbstatus = 1 |
176
|
|
|
AND boo.cover = 1 |
177
|
|
|
AND boo.title != '' |
178
|
|
|
AND r.passwordstatus %s |
179
|
|
|
%s %s %s |
180
|
|
|
GROUP BY boo.id |
181
|
|
|
ORDER BY %s %s %s", |
182
|
|
|
Releases::showPasswords(), |
183
|
|
|
$browseby, |
184
|
|
|
$catsrch, |
185
|
|
|
$exccatlist, |
186
|
|
|
$order[0], |
187
|
|
|
$order[1], |
188
|
|
|
($start === false ? '' : ' LIMIT '.$num.' OFFSET '.$start) |
189
|
|
|
); |
190
|
|
|
$expiresAt = Carbon::now()->addSeconds(config('nntmux.cache_expiry_medium')); |
191
|
|
|
$bookscache = Cache::get(md5($booksql)); |
192
|
|
|
if ($bookscache !== null) { |
193
|
|
|
$books = $bookscache; |
194
|
|
|
} else { |
195
|
|
|
$books = $this->pdo->queryCalc($booksql); |
196
|
|
|
Cache::put(md5($booksql), $books, $expiresAt); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
$bookIDs = $releaseIDs = false; |
200
|
|
|
|
201
|
|
|
if (\is_array($books['result'])) { |
202
|
|
|
foreach ($books['result'] as $book => $id) { |
203
|
|
|
$bookIDs[] = $id['id']; |
204
|
|
|
$releaseIDs[] = $id['grp_release_id']; |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
$sql = sprintf( |
209
|
|
|
" |
210
|
|
|
SELECT |
211
|
|
|
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id, |
212
|
|
|
GROUP_CONCAT(r.rarinnerfilecount ORDER BY r.postdate DESC SEPARATOR ',') as grp_rarinnerfilecount, |
213
|
|
|
GROUP_CONCAT(r.haspreview ORDER BY r.postdate DESC SEPARATOR ',') AS grp_haspreview, |
214
|
|
|
GROUP_CONCAT(r.passwordstatus ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_password, |
215
|
|
|
GROUP_CONCAT(r.guid ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_guid, |
216
|
|
|
GROUP_CONCAT(rn.releases_id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_nfoid, |
217
|
|
|
GROUP_CONCAT(g.name ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grpname, |
218
|
|
|
GROUP_CONCAT(r.searchname ORDER BY r.postdate DESC SEPARATOR '#') AS grp_release_name, |
219
|
|
|
GROUP_CONCAT(r.postdate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_postdate, |
220
|
|
|
GROUP_CONCAT(r.size ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_size, |
221
|
|
|
GROUP_CONCAT(r.totalpart ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_totalparts, |
222
|
|
|
GROUP_CONCAT(r.comments ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_comments, |
223
|
|
|
GROUP_CONCAT(r.grabs ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grabs, |
224
|
|
|
GROUP_CONCAT(df.failed ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_failed, |
225
|
|
|
boo.*, |
226
|
|
|
r.bookinfo_id, |
227
|
|
|
g.name AS group_name, |
228
|
|
|
rn.releases_id AS nfoid |
229
|
|
|
FROM releases r |
230
|
|
|
LEFT OUTER JOIN groups g ON g.id = r.groups_id |
231
|
|
|
LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id |
232
|
|
|
LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id |
233
|
|
|
INNER JOIN bookinfo boo ON boo.id = r.bookinfo_id |
234
|
|
|
WHERE boo.id IN (%s) |
235
|
|
|
AND r.id IN (%s) |
236
|
|
|
%s |
237
|
|
|
GROUP BY boo.id |
238
|
|
|
ORDER BY %s %s", |
239
|
|
|
(\is_array($bookIDs) ? implode(',', $bookIDs) : -1), |
|
|
|
|
240
|
|
|
(\is_array($releaseIDs) ? implode(',', $releaseIDs) : -1), |
|
|
|
|
241
|
|
|
$catsrch, |
242
|
|
|
$order[0], |
243
|
|
|
$order[1] |
244
|
|
|
); |
245
|
|
|
$return = Cache::get(md5($sql)); |
246
|
|
|
if ($return !== null) { |
247
|
|
|
return $return; |
|
|
|
|
248
|
|
|
} |
249
|
|
|
$return = $this->pdo->query($sql); |
250
|
|
|
if (! empty($return)) { |
251
|
|
|
$return[0]['_totalcount'] = $books['total'] ?? 0; |
252
|
|
|
} |
253
|
|
|
Cache::put(md5($sql), $return, $expiresAt); |
254
|
|
|
|
255
|
|
|
return $return; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @param $orderby |
260
|
|
|
* |
261
|
|
|
* @return array |
262
|
|
|
*/ |
263
|
|
|
public function getBookOrder($orderby): array |
264
|
|
|
{ |
265
|
|
|
$order = ($orderby === '') ? 'r.postdate' : $orderby; |
266
|
|
|
$orderArr = explode('_', $order); |
267
|
|
|
switch ($orderArr[0]) { |
268
|
|
|
case 'title': |
|
|
|
|
269
|
|
|
$orderfield = 'boo.title'; |
270
|
|
|
break; |
271
|
|
|
case 'author': |
|
|
|
|
272
|
|
|
$orderfield = 'boo.author'; |
273
|
|
|
break; |
274
|
|
|
case 'publishdate': |
275
|
|
|
$orderfield = 'boo.publishdate'; |
276
|
|
|
break; |
277
|
|
|
case 'size': |
278
|
|
|
$orderfield = 'r.size'; |
279
|
|
|
break; |
280
|
|
|
case 'files': |
281
|
|
|
$orderfield = 'r.totalpart'; |
282
|
|
|
break; |
283
|
|
|
case 'stats': |
284
|
|
|
$orderfield = 'r.grabs'; |
285
|
|
|
break; |
286
|
|
|
case 'posted': |
287
|
|
|
default: |
288
|
|
|
$orderfield = 'r.postdate'; |
289
|
|
|
break; |
290
|
|
|
} |
291
|
|
|
$ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc'; |
292
|
|
|
|
293
|
|
|
return [$orderfield, $ordersort]; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* @return array |
298
|
|
|
*/ |
299
|
|
|
public function getBookOrdering(): array |
300
|
|
|
{ |
301
|
|
|
return [ |
302
|
|
|
'title_asc', |
303
|
|
|
'title_desc', |
304
|
|
|
'posted_asc', |
305
|
|
|
'posted_desc', |
306
|
|
|
'size_asc', |
307
|
|
|
'size_desc', |
308
|
|
|
'files_asc', |
309
|
|
|
'files_desc', |
310
|
|
|
'stats_asc', |
311
|
|
|
'stats_desc', |
312
|
|
|
'releasedate_asc', |
313
|
|
|
'releasedate_desc', |
314
|
|
|
'author_asc', |
315
|
|
|
'author_desc', |
316
|
|
|
]; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @return array |
321
|
|
|
*/ |
322
|
|
|
public function getBrowseByOptions(): array |
323
|
|
|
{ |
324
|
|
|
return ['author' => 'author', 'title' => 'title']; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* @return string |
329
|
|
|
*/ |
330
|
|
|
public function getBrowseBy(): string |
331
|
|
|
{ |
332
|
|
|
$browseby = ' '; |
333
|
|
|
$browsebyArr = $this->getBrowseByOptions(); |
334
|
|
|
foreach ($browsebyArr as $bbk => $bbv) { |
335
|
|
|
if (isset($_REQUEST[$bbk]) && ! empty($_REQUEST[$bbk])) { |
336
|
|
|
$bbs = stripslashes($_REQUEST[$bbk]); |
337
|
|
|
$browseby .= 'AND boo.'.$bbv.' '.$this->pdo->likeString($bbs, true, true); |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return $browseby; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* @param $title |
346
|
|
|
* |
347
|
|
|
* @return bool|mixed |
348
|
|
|
* @throws \Exception |
349
|
|
|
*/ |
350
|
|
|
public function fetchAmazonProperties($title) |
351
|
|
|
{ |
352
|
|
|
$conf = new GenericConfiguration(); |
353
|
|
|
$client = new Client(); |
354
|
|
|
$request = new GuzzleRequest($client); |
355
|
|
|
|
356
|
|
|
try { |
357
|
|
|
$conf |
358
|
|
|
->setCountry(Country::INTERNATIONAL) |
359
|
|
|
->setAccessKey($this->pubkey) |
360
|
|
|
->setSecretKey($this->privkey) |
361
|
|
|
->setAssociateTag($this->asstag) |
362
|
|
|
->setRequest($request) |
363
|
|
|
->setResponseTransformer(new XmlToSimpleXmlObject()); |
364
|
|
|
} catch (\Exception $e) { |
365
|
|
|
echo $e->getMessage(); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
$search = new Search(); |
369
|
|
|
$search->setCategory('Books'); |
370
|
|
|
$search->setKeywords($title); |
371
|
|
|
$search->setResponseGroup(['Large']); |
372
|
|
|
|
373
|
|
|
$apaiIo = new ApaiIO($conf); |
374
|
|
|
|
375
|
|
|
$response = $apaiIo->runOperation($search); |
376
|
|
|
if ($response === false) { |
377
|
|
|
throw new \RuntimeException('Could not connect to Amazon'); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
if (isset($response->Items->Item->ItemAttributes->Title)) { |
381
|
|
|
ColorCLI::doEcho(ColorCLI::info('Found matching title: '.$response->Items->Item->ItemAttributes->Title), true); |
382
|
|
|
|
383
|
|
|
return $response; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
ColorCLI::doEcho(ColorCLI::notice('Could not find a match on Amazon!'), true); |
387
|
|
|
|
388
|
|
|
return false; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Process book releases, 1 category at a time. |
393
|
|
|
* |
394
|
|
|
* @throws \Exception |
395
|
|
|
*/ |
396
|
|
|
public function processBookReleases(): void |
397
|
|
|
{ |
398
|
|
|
$bookids = []; |
399
|
|
|
if (ctype_digit((string) $this->bookreqids)) { |
400
|
|
|
$bookids[] = $this->bookreqids; |
401
|
|
|
} else { |
402
|
|
|
$bookids = explode(', ', $this->bookreqids); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
$total = \count($bookids); |
406
|
|
|
if ($total > 0) { |
407
|
|
|
foreach ($bookids as $i => $iValue) { |
408
|
|
|
$this->processBookReleasesHelper( |
409
|
|
|
Release::query()->where('nzbstatus', '=', NZB::NZB_ADDED) |
410
|
|
|
->whereNull('bookinfo_id') |
411
|
|
|
->whereIn('categories_id', [$bookids[$i]]) |
412
|
|
|
->orderBy('postdate', 'desc') |
413
|
|
|
->limit($this->bookqty) |
|
|
|
|
414
|
|
|
->get(['searchname', 'id', 'categories_id']), |
415
|
|
|
$bookids[$i] |
416
|
|
|
); |
417
|
|
|
} |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* @param $res |
423
|
|
|
* @param $categoryID |
424
|
|
|
* |
425
|
|
|
* @throws \Exception |
426
|
|
|
*/ |
427
|
|
|
protected function processBookReleasesHelper($res, $categoryID): void |
428
|
|
|
{ |
429
|
|
|
if ($res->count() > 0) { |
430
|
|
|
if ($this->echooutput) { |
431
|
|
|
ColorCLI::doEcho(ColorCLI::header("\nProcessing ".$res->count().' book release(s) for categories id '.$categoryID), true); |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
$bookId = -2; |
435
|
|
|
foreach ($res as $arr) { |
436
|
|
|
$startTime = microtime(true); |
437
|
|
|
$usedAmazon = false; |
438
|
|
|
// audiobooks are also books and should be handled in an identical manor, even though it falls under a music category |
439
|
|
|
if ($arr['categories_id'] === '3030') { |
440
|
|
|
// audiobook |
441
|
|
|
$bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'audiobook'); |
442
|
|
|
} else { |
443
|
|
|
// ebook |
444
|
|
|
$bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'ebook'); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
if ($bookInfo !== false) { |
448
|
|
|
if ($this->echooutput) { |
449
|
|
|
ColorCLI::doEcho(ColorCLI::headerOver('Looking up: ').ColorCLI::primary($bookInfo), true); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
// Do a local lookup first |
453
|
|
|
$bookCheck = $this->getBookInfoByName($bookInfo); |
454
|
|
|
|
455
|
|
|
if ($bookCheck === null && \in_array($bookInfo, $this->failCache, false)) { |
456
|
|
|
// Lookup recently failed, no point trying again |
457
|
|
|
if ($this->echooutput) { |
458
|
|
|
ColorCLI::doEcho(ColorCLI::headerOver('Cached previous failure. Skipping.'), true); |
459
|
|
|
} |
460
|
|
|
$bookId = -2; |
461
|
|
|
} elseif ($bookCheck === null) { |
462
|
|
|
$bookId = $this->updateBookInfo($bookInfo); |
463
|
|
|
$usedAmazon = true; |
464
|
|
|
if ($bookId === -2) { |
465
|
|
|
$this->failCache[] = $bookInfo; |
466
|
|
|
} |
467
|
|
|
} else { |
468
|
|
|
if ($bookCheck !== null) { |
469
|
|
|
$bookId = $bookCheck['id']; |
470
|
|
|
} |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
// Update release. |
474
|
|
|
Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]); |
475
|
|
|
} else { // Could not parse release title. |
476
|
|
|
Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]); |
477
|
|
|
if ($this->echooutput) { |
478
|
|
|
echo '.'; |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
// Sleep to not flood amazon. |
482
|
|
|
$diff = floor((microtime(true) - $startTime) * 1000000); |
483
|
|
|
if ($this->sleeptime * 1000 - $diff > 0 && $usedAmazon === true) { |
484
|
|
|
usleep($this->sleeptime * 1000 - $diff); |
|
|
|
|
485
|
|
|
} |
486
|
|
|
} |
487
|
|
|
} elseif ($this->echooutput) { |
488
|
|
|
ColorCLI::doEcho(ColorCLI::header('No book releases to process for categories id '.$categoryID), true); |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* @param $release_name |
494
|
|
|
* @param $releaseID |
495
|
|
|
* @param $releasetype |
496
|
|
|
* |
497
|
|
|
* @return bool|string |
498
|
|
|
*/ |
499
|
|
|
public function parseTitle($release_name, $releaseID, $releasetype) |
500
|
|
|
{ |
501
|
|
|
$a = preg_replace('/\d{1,2} \d{1,2} \d{2,4}|(19|20)\d\d|anybody got .+?[a-z]\? |[-._ ](Novel|TIA)([-._ ]|$)|( |\.)HQ(-|\.| )|[\(\)\.\-_ ](AVI|AZW3?|DOC|EPUB|LIT|MOBI|NFO|RETAIL|(si)?PDF|RTF|TXT)[\)\]\.\-_ ](?![a-z0-9])|compleet|DAGSTiDNiNGEN|DiRFiX|\+ extra|r?e ?Books?([\.\-_ ]English|ers)?|azw3?|ePu(b|p)s?|html|mobi|^NEW[\.\-_ ]|PDF([\.\-_ ]English)?|Please post more|Post description|Proper|Repack(fix)?|[\.\-_ ](Chinese|English|French|German|Italian|Retail|Scan|Swedish)|^R4 |Repost|Skytwohigh|TIA!+|TruePDF|V413HAV|(would someone )?please (re)?post.+? "|with the authors name right/i', '', $release_name); |
502
|
|
|
$b = preg_replace('/^(As Req |conversion |eq |Das neue Abenteuer \d+|Fixed version( ignore previous post)?|Full |Per Req As Found|(\s+)?R4 |REQ |revised |version |\d+(\s+)?$)|(COMPLETE|INTERNAL|RELOADED| (AZW3|eB|docx|ENG?|exe|FR|Fix|gnv64|MU|NIV|R\d\s+\d{1,2} \d{1,2}|R\d|Req|TTL|UC|v(\s+)?\d))(\s+)?$/i', '', $a); |
503
|
|
|
|
504
|
|
|
//remove book series from title as this gets more matches on amazon |
505
|
|
|
$c = preg_replace('/ - \[.+\]|\[.+\]/', '', $b); |
506
|
|
|
|
507
|
|
|
//remove any brackets left behind |
508
|
|
|
$d = preg_replace('/(\(\)|\[\])/', '', $c); |
509
|
|
|
$releasename = trim(preg_replace('/\s\s+/i', ' ', $d)); |
510
|
|
|
|
511
|
|
|
// the default existing type was ebook, this handles that in the same manor as before |
512
|
|
|
if ($releasetype === 'ebook') { |
513
|
|
|
if (preg_match('/^([a-z0-9] )+$|ArtofUsenet|ekiosk|(ebook|mobi).+collection|erotica|Full Video|ImwithJamie|linkoff org|Mega.+pack|^[a-z0-9]+ (?!((January|February|March|April|May|June|July|August|September|O(c|k)tober|November|De(c|z)ember)))[a-z]+( (ebooks?|The))?$|NY Times|(Book|Massive) Dump|Sexual/i', $releasename)) { |
514
|
|
|
if ($this->echooutput) { |
515
|
|
|
ColorCLI::doEcho( |
516
|
|
|
ColorCLI::headerOver('Changing category to misc books: ').ColorCLI::primary($releasename), true |
517
|
|
|
); |
518
|
|
|
} |
519
|
|
|
Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_UNKNOWN]); |
520
|
|
|
|
521
|
|
|
return false; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
if (preg_match('/^([a-z0-9ü!]+ ){1,2}(N|Vol)?\d{1,4}(a|b|c)?$|^([a-z0-9]+ ){1,2}(Jan( |unar|$)|Feb( |ruary|$)|Mar( |ch|$)|Apr( |il|$)|May(?![a-z0-9])|Jun( |e|$)|Jul( |y|$)|Aug( |ust|$)|Sep( |tember|$)|O(c|k)t( |ober|$)|Nov( |ember|$)|De(c|z)( |ember|$))/ui', $releasename) && ! preg_match('/Part \d+/i', $releasename)) { |
525
|
|
|
if ($this->echooutput) { |
526
|
|
|
ColorCLI::doEcho( |
527
|
|
|
ColorCLI::headerOver('Changing category to magazines: ').ColorCLI::primary($releasename), true |
528
|
|
|
); |
529
|
|
|
} |
530
|
|
|
Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_MAGAZINES]); |
531
|
|
|
|
532
|
|
|
return false; |
533
|
|
|
} |
534
|
|
|
if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) { |
535
|
|
|
return $releasename; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
return false; |
539
|
|
|
} |
540
|
|
|
if ($releasetype === 'audiobook') { |
541
|
|
|
if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) { |
542
|
|
|
// we can skip category for audiobooks, since we already know it, so as long as the release name is valid return it so that it is postprocessed by amazon. In the future, determining the type of audiobook could be added (Lecture or book), since we can skip lookups on lectures, but for now handle them all the same way |
543
|
|
|
return $releasename; |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
return false; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
return false; |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* @param string $bookInfo |
554
|
|
|
* @param null $amazdata |
|
|
|
|
555
|
|
|
* |
556
|
|
|
* @return false|int|string |
557
|
|
|
* @throws \Exception |
558
|
|
|
*/ |
559
|
|
|
public function updateBookInfo($bookInfo = '', $amazdata = null) |
560
|
|
|
{ |
561
|
|
|
$ri = new ReleaseImage(); |
562
|
|
|
|
563
|
|
|
$book = []; |
564
|
|
|
$bookId = -2; |
565
|
|
|
|
566
|
|
|
$amaz = false; |
567
|
|
|
if ($bookInfo !== '') { |
568
|
|
|
ColorCLI::doEcho(ColorCLI::info('Fetching data from Amazon for '.$bookInfo), true); |
569
|
|
|
|
570
|
|
|
$amaz = $this->fetchAmazonProperties($bookInfo); |
571
|
|
|
} elseif ($amazdata !== null) { |
572
|
|
|
$amaz = $amazdata; |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
if (! $amaz) { |
576
|
|
|
return false; |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
$book['title'] = (string) $amaz->Items->Item->ItemAttributes->Title; |
580
|
|
|
$book['author'] = (string) $amaz->Items->Item->ItemAttributes->Author; |
581
|
|
|
$book['asin'] = (string) $amaz->Items->Item->ASIN; |
582
|
|
|
$book['isbn'] = (string) $amaz->Items->Item->ItemAttributes->ISBN; |
583
|
|
|
if ($book['isbn'] === '') { |
584
|
|
|
$book['isbn'] = 'null'; |
585
|
|
|
} |
586
|
|
|
|
587
|
|
|
$book['ean'] = (string) $amaz->Items->Item->ItemAttributes->EAN; |
588
|
|
|
if ($book['ean'] === '') { |
589
|
|
|
$book['ean'] = 'null'; |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
$book['url'] = (string) $amaz->Items->Item->DetailPageURL; |
593
|
|
|
$book['url'] = str_replace('%26tag%3Dws', '%26tag%3Dopensourceins%2D21', $book['url']); |
594
|
|
|
|
595
|
|
|
$book['salesrank'] = (string) $amaz->Items->Item->SalesRank; |
596
|
|
|
if ($book['salesrank'] === '') { |
597
|
|
|
$book['salesrank'] = 'null'; |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
$book['publisher'] = (string) $amaz->Items->Item->ItemAttributes->Publisher; |
601
|
|
|
if ($book['publisher'] === '') { |
602
|
|
|
$book['publisher'] = 'null'; |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
$book['publishdate'] = date('Y-m-d', strtotime((string) $amaz->Items->Item->ItemAttributes->PublicationDate)); |
606
|
|
|
if ($book['publishdate'] === '') { |
607
|
|
|
$book['publishdate'] = 'null'; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
$book['pages'] = (string) $amaz->Items->Item->ItemAttributes->NumberOfPages; |
611
|
|
|
if ($book['pages'] === '') { |
612
|
|
|
$book['pages'] = 'null'; |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
if (isset($amaz->Items->Item->EditorialReviews->EditorialReview->Content)) { |
616
|
|
|
$book['overview'] = strip_tags((string) $amaz->Items->Item->EditorialReviews->EditorialReview->Content); |
617
|
|
|
if ($book['overview'] === '') { |
618
|
|
|
$book['overview'] = 'null'; |
619
|
|
|
} |
620
|
|
|
} else { |
621
|
|
|
$book['overview'] = 'null'; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
if (isset($amaz->Items->Item->BrowseNodes->BrowseNode->Name)) { |
625
|
|
|
$book['genre'] = (string) $amaz->Items->Item->BrowseNodes->BrowseNode->Name; |
626
|
|
|
if ($book['genre'] === '') { |
627
|
|
|
$book['genre'] = 'null'; |
628
|
|
|
} |
629
|
|
|
} else { |
630
|
|
|
$book['genre'] = 'null'; |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
$book['coverurl'] = (string) $amaz->Items->Item->LargeImage->URL; |
634
|
|
|
if ($book['coverurl'] !== '') { |
635
|
|
|
$book['cover'] = 1; |
636
|
|
|
} else { |
637
|
|
|
$book['cover'] = 0; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
$check = BookInfo::query()->where('asin', $book['asin'])->first(); |
641
|
|
|
if ($check === null) { |
642
|
|
|
$bookId = BookInfo::query()->insertGetId( |
643
|
|
|
[ |
644
|
|
|
'title' => $book['title'], |
645
|
|
|
'author' => $book['author'], |
646
|
|
|
'asin' => $book['asin'], |
647
|
|
|
'isbn' => $book['isbn'], |
648
|
|
|
'ean' => $book['ean'], |
649
|
|
|
'url' => $book['url'], |
650
|
|
|
'salesrank' => $book['salesrank'], |
651
|
|
|
'publisher' => $book['publisher'], |
652
|
|
|
'publishdate' => $book['publishdate'], |
653
|
|
|
'pages' => $book['pages'], |
654
|
|
|
'overview' =>$book['overview'], |
655
|
|
|
'genre' => $book['genre'], |
656
|
|
|
'cover' => $book['cover'], |
657
|
|
|
'created_at' => Carbon::now(), |
658
|
|
|
'updated_at' => Carbon::now(), |
659
|
|
|
] |
660
|
|
|
); |
661
|
|
|
} else { |
662
|
|
|
if ($check !== null) { |
663
|
|
|
$bookId = $check['id']; |
664
|
|
|
} |
665
|
|
|
BookInfo::query()->where('id', $bookId)->update( |
666
|
|
|
[ |
667
|
|
|
'title' => $book['title'], |
668
|
|
|
'author' => $book['author'], |
669
|
|
|
'asin' => $book['asin'], |
670
|
|
|
'isbn' => $book['isbn'], |
671
|
|
|
'ean' => $book['ean'], |
672
|
|
|
'url' => $book['url'], |
673
|
|
|
'salesrank' => $book['salesrank'], |
674
|
|
|
'publisher' => $book['publisher'], |
675
|
|
|
'publishdate' => $book['publishdate'], |
676
|
|
|
'pages' => $book['pages'], |
677
|
|
|
'overview' => $book['overview'], |
678
|
|
|
'genre' => $book['genre'], |
679
|
|
|
'cover' => $book['cover'], |
680
|
|
|
] |
681
|
|
|
); |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
if ($bookId && $bookId !== -2) { |
685
|
|
|
if ($this->echooutput) { |
686
|
|
|
ColorCLI::doEcho(ColorCLI::header('Added/updated book: '), true); |
687
|
|
|
if ($book['author'] !== '') { |
688
|
|
|
ColorCLI::doEcho(ColorCLI::alternateOver(' Author: ').ColorCLI::primary($book['author']), true); |
689
|
|
|
} |
690
|
|
|
echo ColorCLI::alternateOver(' Title: ').ColorCLI::primary(' '.$book['title']); |
691
|
|
|
if ($book['genre'] !== 'null') { |
692
|
|
|
ColorCLI::doEcho(ColorCLI::alternateOver(' Genre: ').ColorCLI::primary(' '.$book['genre']), true); |
693
|
|
|
} |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
$book['cover'] = $ri->saveImage($bookId, $book['coverurl'], $this->imgSavePath, 250, 250); |
697
|
|
|
} else { |
698
|
|
|
if ($this->echooutput) { |
699
|
|
|
ColorCLI::doEcho( |
700
|
|
|
ColorCLI::header('Nothing to update: '). |
701
|
|
|
ColorCLI::header($book['author']. |
702
|
|
|
' - '. |
703
|
|
|
$book['title']), true |
704
|
|
|
); |
705
|
|
|
} |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
return $bookId; |
709
|
|
|
} |
710
|
|
|
} |
711
|
|
|
|