Added command to update all courses and chapters, downloading videos and checking video size is split into two queues, chapters can be mark to sync individually, updated readme, check if subscription is active, playback rate controll,
This commit is contained in:
34
app/Console/Commands/Update.php
Normal file
34
app/Console/Commands/Update.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Http\SymfonyCastDl\SymfonyCastDlService;
|
||||
use App\Jobs\DownloadVideoFile;
|
||||
use App\Jobs\GetVideoFileSize;
|
||||
use App\Models\Chapter;
|
||||
use App\Models\Course;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Update extends Command
|
||||
{
|
||||
protected $signature = 'symfonycast:update';
|
||||
|
||||
protected $description = 'Updates information about courses and chapters';
|
||||
|
||||
public function handle(SymfonyCastDlService $symfonyCastDlService)
|
||||
{
|
||||
if (!$symfonyCastDlService->isSubscriptionActive()) {
|
||||
$this->error('To fully update courses and chapters you need active subscription');
|
||||
return self::FAILURE;
|
||||
}
|
||||
$this->info('Start updating courses');
|
||||
$courses = $symfonyCastDlService->updateCourses();
|
||||
$this->output->success(count($courses) . ' courses updated');
|
||||
$this->output->info('Start updating each course chapters');
|
||||
$this->withProgressBar($courses, function (Course $course) use ($symfonyCastDlService) {
|
||||
$symfonyCastDlService->updateChapters($course);
|
||||
});
|
||||
$this->output->success('Course chapters updated.');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
19
app/Http/Controllers/Chapter/Sync.php
Normal file
19
app/Http/Controllers/Chapter/Sync.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Chapter;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\DownloadVideoFile;
|
||||
use App\Models\Chapter;
|
||||
use App\Models\Course;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class Sync extends Controller
|
||||
{
|
||||
public function __invoke(Course $course, Chapter $chapter): RedirectResponse
|
||||
{
|
||||
$chapter->update(['sync_offline' => 1]);
|
||||
DownloadVideoFile::dispatch($chapter->id);
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,8 @@ class Index extends Controller
|
||||
},
|
||||
]);
|
||||
$request->whenHas('order', fn($order) => $courses->orderby($order, $request->dir));
|
||||
$request->whenMissing('order', fn() => $courses->orderby('name'));
|
||||
$request->whenMissing('order', fn() => $courses->orderby('published_at', 'desc'));
|
||||
$courses = $courses->get();
|
||||
return view('index', compact(['courses']));
|
||||
}
|
||||
|
||||
public function download(SymfonyCastDlService $symfonyCastDlService)
|
||||
{
|
||||
$symfonyCastDlService->getInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +67,12 @@ class HtmlParser
|
||||
|
||||
public function getVideoSource(Response $respose): ?string
|
||||
{
|
||||
$document = new Document($respose->getBody()->getContents());
|
||||
return $document->first('video source')?->getAttribute('src');
|
||||
return (new Document($respose->getBody()->getContents()))->first('video source')?->getAttribute('src');
|
||||
}
|
||||
|
||||
public function isSubscriptionActive(Response $respose): bool
|
||||
{
|
||||
return (new Document($respose->getBody()->getContents()))->has('.subscription-status .i-active');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ namespace App\Http\SymfonyCastDl;
|
||||
use App\Jobs\GetVideoFileSize;
|
||||
use App\Models\Chapter;
|
||||
use App\Models\Course;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\TransferStats;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Collection;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class SymfonyCastDlService
|
||||
@@ -33,26 +35,34 @@ class SymfonyCastDlService
|
||||
]);
|
||||
}
|
||||
|
||||
public function getInfo(): void
|
||||
/**
|
||||
* @return Collection
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function updateCourses(): Collection
|
||||
{
|
||||
$coursePage = $this->client->get('courses/filtering');
|
||||
$courses = $this->htmlParser->getCourses($coursePage);
|
||||
$courses->each->save();
|
||||
/** @var Course $course */
|
||||
foreach ($courses as $course) {
|
||||
$singleCoursePage = $this->client->get('/screencast/' . $course->link);
|
||||
$chapters = $this->htmlParser->getCourseDetails($singleCoursePage, $course->id);
|
||||
$chapters->each->save();
|
||||
$chapters->each(fn($chapter) => GetVideoFileSize::dispatch($chapter->id));
|
||||
}
|
||||
return $courses->each->save();
|
||||
}
|
||||
|
||||
public function updateCourse(Course $course): void
|
||||
/**
|
||||
* @param Course $course
|
||||
* @return Collection
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function updateChapters(Course $course): Collection
|
||||
{
|
||||
$singleCoursePage = $this->client->get('/screencast/' . $course->link);
|
||||
$chapters = $this->htmlParser->getCourseDetails($singleCoursePage, $course->id);
|
||||
$chapters->each->save();
|
||||
$chapters->each(fn($chapter) => GetVideoFileSize::dispatch($chapter->id));
|
||||
return $chapters;
|
||||
}
|
||||
|
||||
public function isSubscriptionActive(): bool
|
||||
{
|
||||
return $this->htmlParser->isSubscriptionActive($this->client->get('/profile/show'));
|
||||
}
|
||||
|
||||
public function getChapterInfo(Chapter $chapter): ?string
|
||||
@@ -89,5 +99,7 @@ class SymfonyCastDlService
|
||||
['sink' => $chapter->video_path],
|
||||
);
|
||||
}
|
||||
$chapter->video_size = filesize($chapter->video_path);
|
||||
$chapter->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,20 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class DownloadVideoFile implements ShouldQueue
|
||||
class DownloadVideoFile implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public const NAME = 'downloadVideoFile';
|
||||
|
||||
public function __construct(private int $chapterId)
|
||||
{
|
||||
$this->onQueue(self::NAME);
|
||||
}
|
||||
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->chapterId;
|
||||
}
|
||||
|
||||
public function handle(SymfonyCastDlService $symfonyCastDlService)
|
||||
|
||||
@@ -11,12 +11,20 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class GetVideoFileSize implements ShouldQueue
|
||||
class GetVideoFileSize implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public const NAME = 'getVideoFileSize';
|
||||
|
||||
public function __construct(private int $chapterId)
|
||||
{
|
||||
$this->onQueue(self::NAME);
|
||||
}
|
||||
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->chapterId;
|
||||
}
|
||||
|
||||
public function handle(SymfonyCastDlService $symfonyCastDlService)
|
||||
|
||||
Reference in New Issue
Block a user