Skip to content

Commit

Permalink
Fixed download after major changes at laracasts
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio Torrella committed Nov 9, 2018
1 parent 9f64e3a commit 7835edb
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 131 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
EMAIL=test.com
PASSWORD=password
ALGOLIA_APP_ID=
ALGOLIA_API_KEY=
LOCAL_PATH=Downloads
LESSONS_FOLDER=lessons
SERIES_FOLDER=series
Expand Down
99 changes: 99 additions & 0 deletions App/Algolia/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
/**
* Algolia Controller
*/
namespace App\Algolia;

use AlgoliaSearch\Client;
use App\Downloader;
use App\Exceptions\AlgoliaException;

/**
* Class Controller
* @package App\Algolia
*/
class Controller
{
/**
* Client lib
* @var Client
*/
private $client;

/**
* Receives dependencies
*
* @param Client $client
*/
public function __construct(Client $client)
{
$this->client = $client;
}

/**
* Grabs all lessons & series from the algolia api.
*
* @return array
* @throws \AlgoliaSearch\AlgoliaException
*/
public function getAllLessons()
{
$index = $this->client->initIndex(ALGOLIA_INDEX_NAME);

$page = 0;
$params = [
'facetFilters' => [
[
'type:lesson',
'type:series',
],
],
'attributesToRetrieve' => [
'path',
'type',
'slug',
'episode_count',
],
'page' => $page,
];

$array = [
'lessons' => [],
'series' => [],
];

do {
try {
$res = $index->search('', $params);
} catch (\Exception $e) {
throw new AlgoliaException($e->getMessage(), $e->getCode(), $e);
}
$params['page'] = $res['page'] + 1;

foreach ($res['hits'] as $lessonInfo) {
switch ($lessonInfo['type']) {
case 'lesson':
$path = $lessonInfo['path'];
if (preg_match('/'.LARACASTS_LESSONS_PATH.'\/(.+)/', $path, $matches)) { // lesson
$array['lessons'][] = $matches[1];
}
break;
case 'series':
$serieSlug = $lessonInfo['slug'];
foreach (range(1, $lessonInfo['episode_count']) as $episode) {
$array['series'][$serieSlug][] = $episode;
}
break;
default:
break;
}
}

} while ($res['page'] <= $res['nbPages']);

Downloader::$currentLessonNumber = count($array['lessons']);

return $array;
}

}
36 changes: 18 additions & 18 deletions App/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
use App\Exceptions\LoginException;
use App\Exceptions\SubscriptionNotActiveException;
use App\Http\Resolver;
use App\System\Controller;
use App\System\Controller as SystemController;
use App\Utils\Utils;
use Cocur\Slugify\Slugify;
use GuzzleHttp\Client;
use GuzzleHttp\Client as HttpClient;
use AlgoliaSearch\Client as AlgoliaClient;
use App\Algolia\Controller as AlgoliaController;
use League\Flysystem\Filesystem;
use Ubench;

Expand All @@ -28,7 +30,7 @@ class Downloader

/**
* System object
* @var Controller
* @var SystemController
*/
private $system;

Expand All @@ -38,6 +40,12 @@ class Downloader
*/
private $bench;

/**
* Algolia object
* @var AlgoliaController
*/
private $algolia;

/**
* Number of local lessons
* @var int
Expand All @@ -56,16 +64,18 @@ class Downloader
/**
* Receives dependencies
*
* @param Client $client
* @param HttpClient $httpClient
* @param Filesystem $system
* @param Ubench $bench
* @param AlgoliaClient $algoliaClient
* @param bool $retryDownload
*/
public function __construct(Client $client, Filesystem $system, Ubench $bench, $retryDownload = false)
public function __construct(HttpClient $httpClient, Filesystem $system, Ubench $bench, AlgoliaClient $algoliaClient, $retryDownload = false)
{
$this->client = new Resolver($client, $bench, $retryDownload);
$this->system = new Controller($system);
$this->client = new Resolver($httpClient, $bench, $retryDownload);
$this->system = new SystemController($system);
$this->bench = $bench;
$this->algolia = new AlgoliaController($algoliaClient);
}

/**
Expand All @@ -92,10 +102,7 @@ public function start($options)
$this->bench->start();

$localLessons = $this->system->getAllLessons();
$allLessonsOnline = $this->client->getAllLessons();

// Sort series episodes from low to high (download order)
$this->sortSeries($allLessonsOnline);
$allLessonsOnline = $this->algolia->getAllLessons();

$this->bench->end();

Expand Down Expand Up @@ -301,11 +308,4 @@ public function onlyDownloadProvidedLessonsAndSeries($allLessonsOnline)

return $selectedLessonsOnline;
}

public function sortSeries(&$allLessons) {
foreach ($allLessons['series'] as $k => $v) {

sort($allLessons['series'][$k], SORT_NUMERIC);
}
}
}
27 changes: 27 additions & 0 deletions App/Exceptions/AlgoliaException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* Algolia Exception
*/
namespace App\Exceptions;

use Exception;
use Throwable;

/**
* Class AlgoliaException
* @package App\Exceptions
*/
class AlgoliaException extends Exception
{
/**
* AlgoliaException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
{
parent::__construct('Algolia error: ' . $message, $code, $previous);
}
}
45 changes: 1 addition & 44 deletions App/Html/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,6 @@
*/
class Parser
{
/**
* Parses the html and adds the lessons the the array.
*
* @param $html
* @param $array
*/
public static function getAllLessons($html, &$array)
{
$parser = new Crawler($html);

$parser->filter('h5.lesson-list-title')->each(function (Crawler $node) use (&$array) {
$link = $node->children()->attr('href');
if (preg_match('/'.LARACASTS_LESSONS_PATH.'\/(.+)/', $link, $matches)) { // lesson
$array['lessons'][] = $matches[1];
} else if ($node->children()->count() > 0) {
$link = $node->children()->eq(0)->attr('href');

if (preg_match('/'.LARACASTS_SERIES_PATH.'\/(.+)\/episodes\/(\d+)/', $link, $matches)) { // serie lesson
$array['series'][$matches[1]][] = (int) $matches[2];
}
}
});
}

/**
* Determines if there is next page, false if not or the link.
*
* @param $html
*
* @return bool|string
*/
public static function hasNextPage($html)
{
$parser = new Crawler($html);

$node = $parser->filter('[rel=next]');
if ($node->count() > 0) {
return $node->attr('href');
}

return false;
}

/**
* Gets the token input.
*
Expand Down Expand Up @@ -99,7 +56,7 @@ public static function getDownloadLink($html)
public static function getNameOfEpisode($html, $path)
{
$parser = new Crawler($html);
$t = $parser->filter("a[href='/".$path."']")->text();
$t = $parser->filter("a[href='/".$path."'] h6")->text();

return trim($t);
}
Expand Down
50 changes: 0 additions & 50 deletions App/Http/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,56 +60,6 @@ public function __construct(Client $client, Ubench $bench, $retryDownload = fals
$this->retryDownload = $retryDownload;
}

/**
* Grabs all lessons & series from the website.
*/
public function getAllLessons()
{
$array = [];
$html = $this->getAllPage();
Parser::getAllLessons($html, $array);

while ($nextPage = Parser::hasNextPage($html)) {
$html = $this->client->get($nextPage, ['verify' => false])->getBody()->getContents();
Parser::getAllLessons($html, $array);
}

Downloader::$currentLessonNumber = count($array['lessons']);

return $array;
}

/**
* Gets the latest lessons only.
*
* @return array
*
* @throws \Exception
*/
public function getLatestLessons()
{
$array = [];

$html = $this->getAllPage();
Parser::getAllLessons($html, $array);

return $array;
}

/**
* Gets the html from the all page.
*
* @return string
*
* @throws \Exception
*/
private function getAllPage()
{
$response = $this->client->get(LARACASTS_ALL_PATH, ['verify' => false]);

return $response->getBody()->getContents();
}

/**
* Tries to auth.
*
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Downloads new lessons and series from laracasts if there are updates. Or the whole catalogue.

**Working good at 14/09/2018**
**Working good at 10/11/2018**

## Description
Syncs your local folder with the laracasts website, when there are new lessons the app download it for you.
Expand All @@ -23,11 +23,15 @@ Even to download free lessons or series. The download option is only allowed to
- PHP >= 5.4
- php-cURL
- php-xml
- php-json
- Composer

## Installation
- Clone this repo to a folder in your machine
- Change your info in .env.example and rename it to .env
- Go to [laracasts.com --> Browse](https://laracasts.com/search), open Dev Tools --> Network,
find a request to algolia.net and save `x-algolia-api-key` and `x-algolia-application-id`
values to your .env
- `composer install`
- `php start.php` and you are done!

Expand Down
7 changes: 6 additions & 1 deletion bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@
$options['series_folder'] = getenv('SERIES_FOLDER');
//Flags
$options['retry_download'] = boolval(getenv('RETRY_DOWNLOAD'));
//Algolia
$options['algolia_app_id'] = getenv('ALGOLIA_APP_ID');
$options['algolia_api_key'] = getenv('ALGOLIA_API_KEY');

define('BASE_FOLDER', $options['local_path']);
define('LESSONS_FOLDER', $options['lessons_folder']);
define('SERIES_FOLDER', $options['series_folder']);
define('RETRY_DOWNLOAD', $options['retry_download']);
define('ALGOLIA_APP_ID', $options['algolia_app_id']);
define('ALGOLIA_API_KEY', $options['algolia_api_key']);
define('ALGOLIA_INDEX_NAME', 'lessons');

//laracasts
define('LARACASTS_BASE_URL', 'https://laracasts.com');
define('LARACASTS_ALL_PATH', 'lessons');
define('LARACASTS_LOGIN_PATH', 'login');
define('LARACASTS_POST_LOGIN_PATH', 'sessions');
define('LARACASTS_LESSONS_PATH', 'lessons');
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
"symfony/css-selector": "~3.0@dev",
"devster/ubench": "~1.1@dev",
"ext-curl": "*",
"ext-json": "*",
"ext-xml": "*",
"vlucas/phpdotenv": "^2.3",
"cocur/slugify": "^2.3"
"cocur/slugify": "^2.3",
"algolia/algoliasearch-client-php": "~1.28"
},
"require-dev": {
"digitalnature/php-ref": "dev-master"
Expand Down
Loading

0 comments on commit 7835edb

Please sign in to comment.