|
#!/usr/bin/env php |
|
<?php |
|
|
|
declare(strict_types=1); |
|
|
|
require_once __DIR__.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php'; |
|
|
|
use Symfony\Component\Process\Process; |
|
use Symfony\Component\Process\ExecutableFinder; |
|
use Symfony\Component\Process\Exception\ProcessFailedException; |
|
use Symfony\Component\Console\Command\Command; |
|
use Symfony\Component\Console\Helper\ProgressBar; |
|
use Symfony\Component\Console\Input\InputInterface; |
|
use Symfony\Component\Console\Input\InputOption; |
|
use Symfony\Component\Console\Output\OutputInterface; |
|
use Symfony\Component\Console\SingleCommandApplication; |
|
use Symfony\Component\Console\Style\SymfonyStyle; |
|
|
|
(new SingleCommandApplication()) |
|
->setVersion('0.1.0') |
|
->addOption('watch-history-file', null, InputOption::VALUE_REQUIRED, 'Path to the watch-history.json file', '.'.DIRECTORY_SEPARATOR.'watch-history.json') |
|
->addOption('cookies-file', null, InputOption::VALUE_REQUIRED, 'Path to the youtube.com_cookies.txt file', '.'.DIRECTORY_SEPARATOR.'youtube.com_cookies.txt') |
|
->setCode(function (InputInterface $input, OutputInterface $output) { |
|
$io = new SymfonyStyle($input, $output); |
|
$io->title('YouTube Watch History Replay'); |
|
|
|
if (file_exists($input->getOption('watch-history-file').'.tmp')) { |
|
$ids = json_decode(file_get_contents($input->getOption('watch-history-file').'.tmp'), associative: true); |
|
} else { |
|
$ids = []; |
|
$history = array_reverse( |
|
json_decode( |
|
file_get_contents($input->getOption('watch-history-file')), |
|
associative: true |
|
) |
|
); |
|
|
|
foreach ($history as $entry) { |
|
if (isset($entry['titleUrl']) && strtolower($entry['header']) === 'youtube') { |
|
$qs = []; |
|
// Parses the URL to get the ID |
|
parse_str(parse_url($entry['titleUrl'], PHP_URL_QUERY), $qs); |
|
|
|
$ids[] = $qs['v'] ?? null; |
|
} |
|
} |
|
|
|
$ids = array_values(array_unique(array_filter($ids))); |
|
|
|
file_put_contents($input->getOption('watch-history-file').'.tmp', json_encode($ids)); |
|
} |
|
|
|
$count = \count($ids); |
|
if (false === $ids || 0 === $count) { |
|
$io->error('Empty or not found watch-history-file'); |
|
|
|
return Command::INVALID; |
|
} |
|
|
|
if (!file_exists($input->getOption('cookies-file'))) { |
|
$io->error('cookies-file not found'); |
|
|
|
return Command::INVALID; |
|
} |
|
|
|
$io->text('Will now replay '.$count.' URLs:'); |
|
|
|
$progressBar = new ProgressBar($output); |
|
$progressBar->setFormat('verbose'); |
|
$progressBar->start($count); |
|
|
|
$ytdlp = (new ExecutableFinder())->find('yt-dlp'); |
|
$cookiesFile = $input->getOption('cookies-file'); |
|
|
|
// This is the yt-dlp command, you can add/remove options as needed |
|
$cmdTemplate = <<<TPL |
|
%s |
|
--simulate |
|
--mark-watched |
|
--abort-on-error |
|
--no-playlist |
|
--ignore-config |
|
--no-warnings |
|
--no-progress |
|
--cookies "%s" |
|
"https://www.youtube.com/watch?v=%s" |
|
TPL; |
|
|
|
$errors = []; |
|
|
|
foreach ($ids as $id) { |
|
try { |
|
$cmd = str_replace( |
|
PHP_EOL, |
|
' ', |
|
sprintf( |
|
$cmdTemplate, |
|
$ytdlp, |
|
$cookiesFile, |
|
$id |
|
) |
|
); |
|
|
|
(Process::fromShellCommandline($cmd))->mustRun(); |
|
} catch (\Throwable $e) { |
|
$errors[$id] = $e->getMessage(); |
|
} finally { |
|
$progressBar->advance(); |
|
} |
|
} |
|
|
|
$progressBar->finish(); |
|
file_put_contents('errors.log.json', json_encode($errors)); |
|
|
|
$io->newLine(2); |
|
\count($errors) ? $io->warning('done with errors') : $io->success('done'); |
|
@unlink($input->getOption('watch-history-file').'.tmp'); |
|
|
|
return Command::SUCCESS; |
|
}) |
|
->run(); |