Completed
Pull Request — master (#39)
by
unknown
01:35
created

Resolver::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 5
nc 1
nop 3
1
<?php
2
/**
3
 * Http functions
4
 */
5
6
namespace App\Http;
7
8
use App\Downloader;
9
use App\Exceptions\NoDownloadLinkException;
10
use App\Exceptions\SubscriptionNotActiveException;
11
use App\Html\Parser;
12
use App\Utils\Utils;
13
use GuzzleHttp\Client;
14
use GuzzleHttp\Cookie\CookieJar;
15
use GuzzleHttp\Event\ProgressEvent;
16
use Ubench;
17
18
/**
19
 * Class Resolver
20
 * @package App\Http
21
 */
22
class Resolver
23
{
24
    /**
25
     * Guzzle client
26
     * @var Client
27
     */
28
    private $client;
29
30
    /**
31
     * Guzzle cookie
32
     * @var CookieJar
33
     */
34
    private $cookie;
35
36
    /**
37
     * Ubench lib
38
     * @var Ubench
39
     */
40
    private $bench;
41
42
    /**
43
     * Retry download on connection fail
44
     * @var int
45
     */
46
    private $retryDownload = false;
47
48
    /**
49
     * Receives dependencies
50
     *
51
     * @param Client $client
52
     * @param Ubench $bench
53
     * @param bool $retryDownload
54
     */
55
    public function __construct(Client $client, Ubench $bench, $retryDownload = false)
56
    {
57
        $this->client = $client;
58
        $this->cookie = new CookieJar();
59
        $this->bench = $bench;
60
        $this->retryDownload = $retryDownload;
0 ignored issues
show
Documentation Bug introduced by
The property $retryDownload was declared of type integer, but $retryDownload is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
61
    }
62
63
    /**
64
     * Grabs all lessons & series from the website.
65
     */
66
    public function getAllLessons()
67
    {
68
        $array = [];
69
        $html = $this->getAllPage();
70
        Parser::getAllLessons($html, $array);
71
72
        while ($nextPage = Parser::hasNextPage($html)) {
73
            $html = $this->client->get($nextPage, ['verify' => false])->getBody()->getContents();
74
            Parser::getAllLessons($html, $array);
75
        }
76
77
        Downloader::$currentLessonNumber = count($array['lessons']);
78
79
        return $array;
80
    }
81
82
    /**
83
     * Gets the latest lessons only.
84
     *
85
     * @return array
86
     *
87
     * @throws \Exception
88
     */
89
    public function getLatestLessons()
90
    {
91
        $array = [];
92
93
        $html = $this->getAllPage();
94
        Parser::getAllLessons($html, $array);
95
96
        return $array;
97
    }
98
99
    /**
100
     * Gets the html from the all page.
101
     *
102
     * @return string
103
     *
104
     * @throws \Exception
105
     */
106
    private function getAllPage()
107
    {
108
        $response = $this->client->get(LARACASTS_ALL_PATH, ['verify' => false]);
109
110
        return $response->getBody()->getContents();
111
    }
112
113
    /**
114
     * Tries to auth.
115
     *
116
     * @param $email
117
     * @param $password
118
     *
119
     * @return bool
120
     * @throws SubscriptionNotActiveException
121
     */
122
    public function doAuth($email, $password)
123
    {
124
        $response = $this->client->get(LARACASTS_LOGIN_PATH, [
125
            'cookies' => $this->cookie,
126
            'verify' => false
127
        ]);
128
129
        $token = Parser::getToken($response->getBody()->getContents());
130
131
        $response = $this->client->post(LARACASTS_POST_LOGIN_PATH, [
132
            'cookies' => $this->cookie,
133
            'body'    => [
134
                'email'    => $email,
135
                'password' => $password,
136
                '_token'   => $token,
137
                'remember' => 1,
138
            ],
139
            'verify' => false
140
        ]);
141
142
        $html = $response->getBody()->getContents();
143
144
        if (strpos($html, "Reactivate") !== FALSE) {
145
            throw new SubscriptionNotActiveException();
146
        }
147
148
        if(strpos($html, "The email must be a valid email address.") !== FALSE) {
149
            return false;
150
        }
151
152
        return strpos($html, "verify your credentials.") === FALSE;
153
    }
154
155
    /**
156
     * Download the episode of the serie.
157
     *
158
     * @param $serie
159
     * @param $episode
160
     * @return bool
161
     */
162
    public function downloadSerieEpisode($serie, $episode)
163
    {
164
        $path = LARACASTS_SERIES_PATH . '/' . $serie . '/episodes/' . $episode;
165
        $episodePage = $this->getPage($path);
166
        $name = $this->getNameOfEpisode($episodePage, $path);
167
        $number = sprintf("%02d", $episode);
168
        $saveTo = BASE_FOLDER . '/' . SERIES_FOLDER . '/' . $serie . '/' . $number . '-' . $name . '.mp4';
169
        Utils::writeln(sprintf("Download started: %s . . . . Saving on " . SERIES_FOLDER . '/' . $serie . ' folder.',
170
            $number . ' - ' . $name
171
        ));
172
173
        return $this->downloadLessonFromPath($episodePage, $saveTo);
174
    }
175
176
    /**
177
     * Downloads the lesson.
178
     *
179
     * @param $lesson
180
     * @return bool
181
     */
182
    public function downloadLesson($lesson)
183
    {
184
        $path = LARACASTS_LESSONS_PATH . '/' . $lesson;
185
        $number = sprintf("%04d", Downloader::$totalLocalLessons + Downloader::$currentLessonNumber--);
186
        $saveTo = BASE_FOLDER . '/' . LESSONS_FOLDER . '/' . $number . '-' . $lesson . '.mp4';
187
188
        Utils::writeln(sprintf("Download started: %s . . . . Saving on " . LESSONS_FOLDER . ' folder.',
189
            $lesson
190
        ));
191
        $html = $this->getPage($path);
192
        return $this->downloadLessonFromPath($html, $saveTo);
193
    }
194
195
    /**
196
     * Helper function to get html of a page
197
     * @param $path
198
     * @return string
199
     */
200
    private function getPage($path) {
201
        return $this->client
202
            ->get($path, ['cookies' => $this->cookie, 'verify' => false])
203
            ->getBody()
204
            ->getContents();
205
    }
206
207
    /**
208
     * Helper to get the Location header.
209
     *
210
     * @param $url
211
     *
212
     * @return string
213
     */
214
    private function getRedirectUrl($url)
215
    {
216
        $response = $this->client->get($url, [
217
            'cookies'         => $this->cookie,
218
            'allow_redirects' => FALSE,
219
            'verify' => false
220
        ]);
221
222
        return $response->getHeader('Location');
223
    }
224
225
    /**
226
     * Gets the name of the serie episode.
227
     *
228
     * @param $html
229
     *
230
     * @param $path
231
     * @return string
232
     */
233
    private function getNameOfEpisode($html, $path)
234
    {
235
        $name = Parser::getNameOfEpisode($html, $path);
236
237
        return Utils::parseEpisodeName($name);
238
    }
239
240
    /**
241
     * Helper to download the video.
242
     *
243
     * @param $html
244
     * @param $saveTo
245
     * @return bool
246
     */
247
    private function downloadLessonFromPath($html, $saveTo)
248
    {
249
        try {
250
            $downloadUrl = Parser::getDownloadLink($html);
251
            $viemoUrl = $this->getRedirectUrl($downloadUrl);
252
            $finalUrl = $this->getRedirectUrl($viemoUrl);
253
        } catch(NoDownloadLinkException $e) {
254
            Utils::write(sprintf("Can't download this lesson! :( No download button"));
255
256
            try {
257
                Utils::write(sprintf("Tring to find a Wistia.net video"));
258
                $Wistia = new Wistia($html,$this->bench);
259
                $finalUrl = $Wistia->getDownloadUrl();
260
            } catch(NoDownloadLinkException $e) {
261
                return false;
262
            }
263
264
        }
265
266
        $this->bench->start();
267
268
        $retries = 0;
269
        while (true) {
270
            try {
271
                $downloadedBytes = file_exists($saveTo) ? filesize($saveTo) : 0;
272
                $req = $this->client->createRequest('GET', $finalUrl, [
273
                    'save_to' => fopen($saveTo, 'a'),
274
                    'verify' => false,
275
                    'headers' => [
276
                        'Range' => 'bytes=' . $downloadedBytes . '-'
277
                    ]
278
                ]);
279
280
                if (php_sapi_name() == "cli") { //on cli show progress
281
                    $req->getEmitter()->on('progress', function (ProgressEvent $e) use ($downloadedBytes) {
282
                        printf("> Total: %d%% Downloaded: %s of %s     \r",
283
                            Utils::getPercentage($e->downloaded + $downloadedBytes, $e->downloadSize),
284
                            Utils::formatBytes($e->downloaded + $downloadedBytes),
285
                            Utils::formatBytes($e->downloadSize));
286
                    });
287
                }
288
289
                $this->client->send($req); 
290
                break;  
291
            } catch (\Exception $e) {
292
                if (!$this->retryDownload || ($this->retryDownload && $retries >= 3)) {
293
                    throw $e;
294
                }
295
                ++$retries;
296
                Utils::write(sprintf("Retry download after connection fail!     "));
297
                continue;
298
            }
299
        }
300
301
        $this->bench->end();
302
303
        Utils::write(sprintf("Elapsed time: %s, Memory: %s         ",
304
            $this->bench->getTime(),
305
            $this->bench->getMemoryUsage()
306
        ));
307
308
        return true;
309
    }
310
}
311