Completed
Push — master ( afd9a4...f094b6 )
by Carlos
02:31
created

Downloader::sortSeries()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
/**
3
 * Main cycle of the app
4
 */
5
namespace App;
6
7
use App\Exceptions\LoginException;
8
use App\Exceptions\SubscriptionNotActiveException;
9
use App\Http\Resolver;
10
use App\System\Controller;
11
use App\Utils\Utils;
12
use Cocur\Slugify\Slugify;
13
use GuzzleHttp\Client;
14
use League\Flysystem\Filesystem;
15
use Ubench;
16
17
/**
18
 * Class Downloader
19
 * @package App
20
 */
21
class Downloader
22
{
23
    /**
24
     * Http resolver object
25
     * @var Resolver
26
     */
27
    private $client;
28
29
    /**
30
     * System object
31
     * @var Controller
32
     */
33
    private $system;
34
35
    /**
36
     * Ubench lib
37
     * @var Ubench
38
     */
39
    private $bench;
40
41
    /**
42
     * Number of local lessons
43
     * @var int
44
     */
45
    public static $totalLocalLessons;
46
47
    /**
48
     * Current lesson number
49
     * @var int
50
     */
51
    public static $currentLessonNumber;
52
53
    private $wantSeries = [];
54
    private $wantLessons = [];
55
56
    /**
57
     * Receives dependencies
58
     *
59
     * @param Client $client
60
     * @param Filesystem $system
61
     * @param Ubench $bench
62
     * @param bool $retryDownload
63
     */
64
    public function __construct(Client $client, Filesystem $system, Ubench $bench, $retryDownload = false)
65
    {
66
        $this->client = new Resolver($client, $bench, $retryDownload);
67
        $this->system = new Controller($system);
68
        $this->bench = $bench;
69
    }
70
71
    /**
72
     * All the logic
73
     *
74
     * @param $options
75
     */
76
    public function start($options)
77
    {
78
        try {
79
            $counter = [
80
                'series'  => 1,
81
                'lessons' => 1,
82
                'failed_episode' => 0,
83
                'failed_lesson' => 0
84
            ];
85
86
            Utils::box('Authenticating');
87
88
            $this->doAuth($options);
89
90
            Utils::box('Starting Collecting the data');
91
92
            $this->bench->start();
93
94
            $localLessons = $this->system->getAllLessons();
95
            $allLessonsOnline = $this->client->getAllLessons();
96
97
            // Sort series episodes from low to high (download order)
98
            $this->sortSeries($allLessonsOnline);
99
100
            $this->bench->end();
101
102
103
            if($this->_haveOptions()) {  //filter all online lessons to the selected ones
104
                $allLessonsOnline = $this->onlyDownloadProvidedLessonsAndSeries($allLessonsOnline);
105
            }
106
107
            Utils::box('Downloading');
108
            //Magic to get what to download
109
            $diff = Utils::resolveFaultyLessons($allLessonsOnline, $localLessons);
110
111
            $new_lessons = Utils::countLessons($diff);
112
            $new_episodes = Utils::countEpisodes($diff);
113
114
            Utils::write(sprintf("%d new lessons and %d episodes. %s elapsed with %s of memory usage.",
115
                    $new_lessons,
116
                    $new_episodes,
117
                    $this->bench->getTime(),
118
                    $this->bench->getMemoryUsage())
119
            );
120
121
122
            //Download Lessons
123
            if ($new_lessons > 0) {
124
                $this->downloadLessons($diff, $counter, $new_lessons);
125
            }
126
127
            //Donwload Episodes
128
            if ($new_episodes > 0) {
129
                $this->downloadEpisodes($diff, $counter, $new_episodes);
130
            }
131
132
            Utils::writeln(sprintf("Finished! Downloaded %d new lessons and %d new episodes. Failed: %d",
133
                $new_lessons - $counter['failed_lesson'],
134
                $new_episodes - $counter['failed_episode'],
135
                $counter['failed_lesson'] + $counter['failed_episode']
136
            ));
137
        } catch (LoginException $e) {
138
            Utils::write("Your login details are wrong!");
139
        } catch (SubscriptionNotActiveException $e) {
140
            Utils::write('Your subscription is not active!');
141
        }
142
    }
143
144
    /**
145
     * Tries to login.
146
     *
147
     * @param $options
148
     *
149
     * @throws \Exception
150
     */
151
    public function doAuth($options)
152
    {
153
        if (!$this->client->doAuth($options['email'], $options['password'])) {
154
            throw new LoginException("Can't do the login..");
155
        }
156
        Utils::write("Successfull!");
157
    }
158
159
    /**
160
     * Download Lessons
161
     * @param $diff
162
     * @param $counter
163
     * @param $new_lessons
164
     */
165
    public function downloadLessons(&$diff, &$counter, $new_lessons)
166
    {
167
        $this->system->createFolderIfNotExists(LESSONS_FOLDER);
168
        Utils::box('Downloading Lessons');
169 View Code Duplication
        foreach ($diff['lessons'] as $lesson) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
170
171
            if($this->client->downloadLesson($lesson) === false) {
172
                $counter['failed_lesson']++;
173
            }
174
175
            Utils::write(sprintf("Current: %d of %d total. Left: %d",
176
                $counter['lessons']++,
177
                $new_lessons,
178
                $new_lessons - $counter['lessons'] + 1
179
            ));
180
        }
181
    }
182
183
    /**
184
     * Download Episodes
185
     * @param $diff
186
     * @param $counter
187
     * @param $new_episodes
188
     */
189
    public function downloadEpisodes(&$diff, &$counter, $new_episodes)
190
    {
191
        $this->system->createFolderIfNotExists(SERIES_FOLDER);
192
        Utils::box('Downloading Series');
193
        foreach ($diff['series'] as $serie => $episodes) {
194
            $this->system->createSerieFolderIfNotExists($serie);
195 View Code Duplication
            foreach ($episodes as $episode) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
196
197
                if($this->client->downloadSerieEpisode($serie, $episode) === false) {
198
                    $counter['failed_episode'] = $counter['failed_episode'] +1;
199
                }
200
201
                Utils::write(sprintf("Current: %d of %d total. Left: %d",
202
                    $counter['series']++,
203
                    $new_episodes,
204
                    $new_episodes - $counter['series'] + 1
205
                ));
206
            }
207
        }
208
    }
209
210
    protected function _haveOptions()
211
    {
212
        $found = false;
213
214
        $short_options = "s:";
215
        $short_options .= "l:";
216
217
        $long_options  = array(
218
            "series-name:",
219
            "lesson-name:"
220
        );
221
        $options = getopt($short_options, $long_options);
222
223
        Utils::box(sprintf("Checking for options %s", json_encode($options)));
224
225
        if(count($options) == 0) {
226
            Utils::write('No options provided');
227
            return false;
228
        }
229
230
        $slugify = new Slugify();
231
        $slugify->addRule("'", '');
232
233 View Code Duplication
        if(isset($options['s']) || isset($options['series-name'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
234
            $series = isset($options['s']) ? $options['s'] : $options['series-name'];
235
            if(!is_array($series))
236
                $series = [$series];
237
238
            Utils::write(sprintf("Series names provided: %s", json_encode($series)));
239
240
241
            $this->wantSeries = array_map(function ($serie) use ($slugify) {
242
                return $slugify->slugify($serie);
243
            }, $series);
244
245
            Utils::write(sprintf("Series names provided: %s", json_encode($this->wantSeries)));
246
247
            $found = true;
248
        }
249
250 View Code Duplication
        if(isset($options['l']) || isset($options['lesson-name'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
251
            $lessons = isset($options['l']) ? $options['l'] : $options['lesson-name'];
252
253
            if(!is_array($lessons))
254
                $lessons = [$lessons];
255
256
            Utils::write(sprintf("Lesson names provided: %s", json_encode($lessons)));
257
258
            $this->wantLessons = array_map(function($lesson) use ($slugify) {
259
                return $slugify->slugify($lesson); },$lessons
260
            );
261
262
            Utils::write(sprintf("Lesson names provided: %s", json_encode($this->wantLessons)));
263
264
            $found = true;
265
        }
266
267
        return $found;
268
    }
269
270
    /**
271
     * Download selected Series and lessons
272
     * @param $allLessonsOnline
273
     * @return array
274
     */
275
    public function onlyDownloadProvidedLessonsAndSeries($allLessonsOnline)
276
    {
277
        Utils::box('Checking if series and lessons exists');
278
279
        $selectedLessonsOnline = [
280
            'lessons' => [],
281
            'series' => []
282
        ];
283
284
        foreach($this->wantSeries as $series) {
285
            if(isset($allLessonsOnline['series'][$series])) {
286
                Utils::write('Series "'.$series.'" found!');
287
                $selectedLessonsOnline['series'][$series] = $allLessonsOnline['series'][$series];
288
            } else {
289
                Utils::write("Series '".$series."' not found!");
290
            }
291
        }
292
293
        foreach($this->wantLessons as $lesson) {
294
            if(in_array($lesson, $allLessonsOnline['lessons'])) {
295
                Utils::write('Lesson "'.$lesson.'" found');
296
                $selectedLessonsOnline['lessons'][] = $lesson;
297
            } else {
298
                Utils::write("Lesson '".$lesson."' not found!");
299
            }
300
        }
301
302
        return $selectedLessonsOnline;
303
    }
304
305
    public function sortSeries(&$allLessons) {
306
        foreach ($allLessons['series'] as $k => $v) {
307
308
            sort($allLessons['series'][$k], SORT_NUMERIC);
309
        }
310
    }
311
}
312