Merge branch 'master' into laravel-upgrades

This commit is contained in:
Adam Lavin 2017-12-03 04:41:18 +00:00 committed by GitHub
commit 081202e7e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 166 additions and 38 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ _ide_helper.php
resources/views/emails/html resources/views/emails/html
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
/composer.phar

View file

@ -145,7 +145,9 @@ class UploadTrackCommand extends CommandBase
$input = Request::all(); $input = Request::all();
$input['track'] = $trackFile; $input['track'] = $trackFile;
if (!$this->_isReplacingTrack) {
// Prevent the setting of the cover index for validation
if (!$this->_isReplacingTrack && isset($coverFile)) {
$input['cover'] = $coverFile; $input['cover'] = $coverFile;
} }
@ -159,7 +161,7 @@ class UploadTrackCommand extends CommandBase
. 'audio_channels:1,2', . 'audio_channels:1,2',
]; ];
if (!$this->_isReplacingTrack) { if (!$this->_isReplacingTrack) {
array_push($rules, [ array_merge($rules, [
'cover' => 'image|mimes:png,jpeg|min_width:350|min_height:350', 'cover' => 'image|mimes:png,jpeg|min_width:350|min_height:350',
'auto_publish' => 'boolean', 'auto_publish' => 'boolean',
'title' => 'string', 'title' => 'string',
@ -198,6 +200,12 @@ class UploadTrackCommand extends CommandBase
$this->_track->source = $this->_customTrackSource ?? $source; $this->_track->source = $this->_customTrackSource ?? $source;
$this->_track->save(); $this->_track->save();
// If the cover was null, and not included, add it back in as null so that
// other commands do not encounter a undefined index.
if (! isset($input['cover'])) {
$input['cover'] = null;
}
if (!$this->_isReplacingTrack) { if (!$this->_isReplacingTrack) {
// Parse any tags in the uploaded files. // Parse any tags in the uploaded files.
$parseTagsCommand = new ParseTrackTagsCommand($this->_track, $trackFile, $input); $parseTagsCommand = new ParseTrackTagsCommand($this->_track, $trackFile, $input);

View file

@ -0,0 +1,78 @@
<?php
/**
* Pony.fm - A community for pony fan music.
* Copyright (C) 2017 Isaac Avram
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Poniverse\Ponyfm\Console\Commands;
use Illuminate\Console\Command;
use Poniverse\Ponyfm\Models\Image;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File;
class RebuildImages extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rebuild:images';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Resizes all images to fit the specifications in Models/Image';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->info("Regenerating Images");
$progressBar = $this->output->createProgressBar(Image::count());
Image::chunk(1000, function($images) use ($progressBar) {
foreach ($images as $image) {
try {
$image->buildCovers();
} catch (FileNotFoundException $e) {
$name = $image->filename;
$id = $image->id;
$this->error("Unable to process image $name (id: $id): ".$e->getMessage());
}
$progressBar->advance();
}
});
}
}

View file

@ -24,6 +24,8 @@ use External;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Config; use Config;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
/** /**
@ -57,12 +59,14 @@ class Image extends Model
const SMALL = 4; const SMALL = 4;
public static $ImageTypes = [ public static $ImageTypes = [
self::NORMAL => ['id' => self::NORMAL, 'name' => 'normal', 'width' => 350, 'height' => 350], self::NORMAL => ['id' => self::NORMAL, 'name' => 'normal', 'width' => 350, 'height' => 350, 'geometry' => '350'],
self::ORIGINAL => ['id' => self::ORIGINAL, 'name' => 'original', 'width' => null, 'height' => null], self::ORIGINAL => ['id' => self::ORIGINAL, 'name' => 'original', 'width' => null, 'height' => null, 'geometry' => null],
self::SMALL => ['id' => self::SMALL, 'name' => 'small', 'width' => 100, 'height' => 100], self::SMALL => ['id' => self::SMALL, 'name' => 'small', 'width' => 100, 'height' => 100, 'geometry' => '100x100^'],
self::THUMBNAIL => ['id' => self::THUMBNAIL, 'name' => 'thumbnail', 'width' => 50, 'height' => 50] self::THUMBNAIL => ['id' => self::THUMBNAIL, 'name' => 'thumbnail', 'width' => 50, 'height' => 50, 'geometry' => '50x50^']
]; ];
const MIME_JPEG = 'image/jpeg';
public static function getImageTypeFromName($name) public static function getImageTypeFromName($name)
{ {
foreach (self::$ImageTypes as $cover) { foreach (self::$ImageTypes as $cover) {
@ -95,17 +99,7 @@ class Image extends Model
if ($image) { if ($image) {
if ($forceReupload) { if ($forceReupload) {
// delete existing versions of the image $image->clearExisting(true);
$filenames = scandir($image->getDirectory());
$imagePrefix = $image->id.'_';
$filenames = array_filter($filenames, function (string $filename) use ($imagePrefix) {
return Str::startsWith($filename, $imagePrefix);
});
foreach ($filenames as $filename) {
unlink($image->getDirectory().'/'.$filename);
}
} else { } else {
return $image; return $image;
} }
@ -124,27 +118,7 @@ class Image extends Model
$image->ensureDirectoryExists(); $image->ensureDirectoryExists();
foreach (self::$ImageTypes as $coverType) { foreach (self::$ImageTypes as $coverType) {
if ($coverType['id'] === self::ORIGINAL && $image->mime === 'image/jpeg') { self::processFile($file, $image->getFile($coverType['id']), $coverType);
$command = 'cp "'.$file->getPathname().'" '.$image->getFile($coverType['id']);
} else {
// ImageMagick options reference: http://www.imagemagick.org/script/command-line-options.php
$command = 'convert 2>&1 "'.$file->getPathname().'" -background white -alpha remove -alpha off -strip';
if ($image->mime === 'image/jpeg') {
$command .= ' -quality 100 -format jpeg';
} else {
$command .= ' -quality 95 -format png';
}
if (isset($coverType['width']) && isset($coverType['height'])) {
$command .= " -thumbnail ${coverType['width']}x${coverType['height']}^ -gravity center -extent ${coverType['width']}x${coverType['height']}";
}
$command .= ' "'.$image->getFile($coverType['id']).'"';
}
External::execute($command);
chmod($image->getFile($coverType['id']), 0644);
} }
return $image; return $image;
@ -154,6 +128,37 @@ class Image extends Model
} }
} }
/**
* Converts the image into the specified cover type to the specified path.
*
* @param File $image The image file to be processed
* @param string $path The path to save the processed image file
* @param array $coverType The type to process the image to
*/
private static function processFile(File $image, string $path, $coverType) {
if ($coverType['id'] === self::ORIGINAL && $image->getMimeType() === self::MIME_JPEG) {
$command = 'cp "'.$image->getPathname().'" '.$path;
} else {
// ImageMagick options reference: http://www.imagemagick.org/script/command-line-options.php
$command = 'convert 2>&1 "'.$image->getPathname().'" -background white -alpha remove -alpha off -strip';
if ($image->getMimeType() === self::MIME_JPEG) {
$command .= ' -quality 100 -format jpeg';
} else {
$command .= ' -quality 95 -format png';
}
if (isset($coverType['geometry'])) {
$command .= " -gravity center -thumbnail ${coverType['geometry']} -extent ${coverType['geometry']}";
}
$command .= ' "'.$path.'"';
}
External::execute($command);
chmod($path, 0644);
}
protected $table = 'images'; protected $table = 'images';
public function getUrl($type = self::NORMAL) public function getUrl($type = self::NORMAL)
@ -191,4 +196,40 @@ class Image extends Model
mkdir($destination, 0777, true); mkdir($destination, 0777, true);
} }
} }
/**
* Deletes any generated files if they exist
* @param bool $includeOriginal Set to true if the original image should be deleted as well.
*/
public function clearExisting(bool $includeOriginal = false) {
$files = scandir($this->getDirectory());
$filePrefix = $this->id.'_';
$originalName = $filePrefix.Image::$ImageTypes[Image::ORIGINAL]['name'];
$files = array_filter($files, function($file) use ($originalName, $includeOriginal, $filePrefix) {
if (Str::startsWith($file,$originalName) && !$includeOriginal) {
return false;
}
else {
return (Str::startsWith($file, $filePrefix));
}
});
foreach ($files as $file) {
unlink($this->getDirectory().'/'.$file);
}
}
/**
* Builds the cover images for the image, overwriting if needed.
*
* @throws FileNotFoundException If the original file cannot be found.
*/
public function buildCovers() {
$originalFile = new File($this->getFile(self::ORIGINAL));
foreach (self::$ImageTypes as $imageType) {
self::processFile($originalFile, $this->getFile($imageType['id']), $imageType);
}
}
} }