mirror of
https://github.com/wallabag/wallabag.git
synced 2024-11-27 11:31:05 +00:00
Add a command to clean downloaded images
There were a bug in versions prior to 2.4.0 where images weren't properly removed (mostly when coming from the API). With that command, we'll be able to remove images which aren't associated to any entries. Like other command you can pass a username to only clean one user.
This commit is contained in:
parent
9aeac727df
commit
9743058f7d
2 changed files with 151 additions and 22 deletions
123
src/Wallabag/CoreBundle/Command/CleanDownloadedImagesCommand.php
Normal file
123
src/Wallabag/CoreBundle/Command/CleanDownloadedImagesCommand.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\CoreBundle\Command;
|
||||
|
||||
use Doctrine\ORM\NoResultException;
|
||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Wallabag\UserBundle\Entity\User;
|
||||
|
||||
class CleanDownloadedImagesCommand extends ContainerAwareCommand
|
||||
{
|
||||
/** @var SymfonyStyle */
|
||||
protected $io;
|
||||
|
||||
protected $deleted = 0;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('wallabag:clean-downloaded-images')
|
||||
->setDescription('Cleans downloaded images which are no more associated to an entry')
|
||||
->addArgument(
|
||||
'username',
|
||||
InputArgument::OPTIONAL,
|
||||
'User to clean'
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
|
||||
$username = $input->getArgument('username');
|
||||
|
||||
if ($username) {
|
||||
try {
|
||||
$user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
|
||||
$this->clean($user);
|
||||
} catch (NoResultException $e) {
|
||||
$this->io->error(sprintf('User "%s" not found.', $username));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->io->success('Finished cleaning.');
|
||||
} else {
|
||||
$users = $this->getContainer()->get('wallabag_user.user_repository')->findAll();
|
||||
|
||||
$this->io->text(sprintf('Cleaning through <info>%d</info> user accounts', \count($users)));
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->clean($user);
|
||||
}
|
||||
$this->io->success(sprintf('Finished cleaning. %d deleted images', $this->deleted));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function clean(User $user)
|
||||
{
|
||||
$this->io->text(sprintf('Processing user <info>%s</info>', $user->getUsername()));
|
||||
|
||||
$repo = $this->getContainer()->get('wallabag_core.entry_repository');
|
||||
$downloadImages = $this->getContainer()->get('wallabag_core.entry.download_images');
|
||||
$baseFolder = $downloadImages->getBaseFolder();
|
||||
|
||||
$entries = $repo->findAllEntriesIdByUserId($user->getId());
|
||||
|
||||
$deletedCount = 0;
|
||||
|
||||
// first retrieve _valid_ folders from existing entries
|
||||
$hashToId = [];
|
||||
$validPaths = [];
|
||||
foreach ($entries as $entry) {
|
||||
$path = $downloadImages->getRelativePath($entry['id']);
|
||||
|
||||
if (!file_exists($baseFolder . '/' . $path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only store the hash, not the full path
|
||||
$hash = explode('/', $path)[2];
|
||||
$validPaths[] = $hash;
|
||||
$hashToId[$hash] = $entry['id'];
|
||||
}
|
||||
|
||||
// then retrieve _existing_ folders in the image folder
|
||||
$finder = new Finder();
|
||||
$finder
|
||||
->directories()
|
||||
->ignoreDotFiles(true)
|
||||
->depth(2)
|
||||
->in($baseFolder);
|
||||
|
||||
$existingPaths = [];
|
||||
foreach ($finder as $file) {
|
||||
$existingPaths[] = $file->getFilename();
|
||||
}
|
||||
|
||||
// check if existing path are valid, if not, remove all images and the folder
|
||||
foreach ($existingPaths as $existingPath) {
|
||||
if (!\in_array($existingPath, $validPaths, true)) {
|
||||
$fullPath = $baseFolder . '/' . $existingPath[0] . '/' . $existingPath[1] . '/' . $existingPath;
|
||||
$res = array_map('unlink', glob($fullPath . '/*.*'));
|
||||
|
||||
rmdir($fullPath);
|
||||
|
||||
$deletedCount += \count($res);
|
||||
|
||||
$this->io->text(sprintf('Deleted images in <info>%s</info>: <info>%d</info>', $existingPath, \count($res)));
|
||||
}
|
||||
}
|
||||
|
||||
$this->deleted += $deletedCount;
|
||||
|
||||
$this->io->text(sprintf('Deleted <info>%d</info> images for user <info>%s</info>', $deletedCount, $user->getUserName()));
|
||||
}
|
||||
}
|
|
@ -37,6 +37,11 @@ class DownloadImages
|
|||
$this->setFolder();
|
||||
}
|
||||
|
||||
public function getBaseFolder()
|
||||
{
|
||||
return $this->baseFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the html and extract images URLs from it.
|
||||
*
|
||||
|
@ -210,6 +215,29 @@ class DownloadImages
|
|||
@rmdir($folderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the folder where we are going to save images based on the entry url.
|
||||
*
|
||||
* @param int $entryId ID of the entry
|
||||
* @param bool $createFolder Should we create the folder for the given id?
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativePath($entryId, $createFolder = true)
|
||||
{
|
||||
$hashId = hash('crc32', $entryId);
|
||||
$relativePath = $hashId[0] . '/' . $hashId[1] . '/' . $hashId;
|
||||
$folderPath = $this->baseFolder . '/' . $relativePath;
|
||||
|
||||
if (!file_exists($folderPath) && $createFolder) {
|
||||
mkdir($folderPath, 0777, true);
|
||||
}
|
||||
|
||||
$this->logger->debug('DownloadImages: Folder used for that Entry id', ['folder' => $folderPath, 'entryId' => $entryId]);
|
||||
|
||||
return $relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get images urls from the srcset image attribute.
|
||||
*
|
||||
|
@ -254,28 +282,6 @@ class DownloadImages
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the folder where we are going to save images based on the entry url.
|
||||
*
|
||||
* @param int $entryId ID of the entry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getRelativePath($entryId)
|
||||
{
|
||||
$hashId = hash('crc32', $entryId);
|
||||
$relativePath = $hashId[0] . '/' . $hashId[1] . '/' . $hashId;
|
||||
$folderPath = $this->baseFolder . '/' . $relativePath;
|
||||
|
||||
if (!file_exists($folderPath)) {
|
||||
mkdir($folderPath, 0777, true);
|
||||
}
|
||||
|
||||
$this->logger->debug('DownloadImages: Folder used for that Entry id', ['folder' => $folderPath, 'entryId' => $entryId]);
|
||||
|
||||
return $relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an $url absolute based on the $base.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue