247 lines
7.8 KiB
PHP
247 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace App\AlbumSources;
|
|
|
|
use App\BackblazeB2FileIdCache;
|
|
use App\Photo;
|
|
use App\Services\BackblazeB2Service;
|
|
use App\Storage;
|
|
use GuzzleHttp\Psr7\Stream;
|
|
use Illuminate\Support\Facades\Log;
|
|
use function GuzzleHttp\Psr7\stream_for;
|
|
|
|
class BackblazeB2Source extends AlbumSourceBase implements IAlbumSource
|
|
{
|
|
const BUCKET_TYPE_AUTO = 0;
|
|
const BUCKET_TYPE_PRIVATE = 1;
|
|
const BUCKET_TYPE_PUBLIC = 2;
|
|
|
|
/**
|
|
* @var BackblazeB2Service
|
|
*/
|
|
private $backblaze;
|
|
|
|
/**
|
|
* Type of bucket which determines what type of URLs to generate to images.
|
|
* @var integer
|
|
*/
|
|
private $bucketType;
|
|
|
|
/**
|
|
* Token used to download files from a private bucket.
|
|
* @var string
|
|
*/
|
|
private $downloadToken;
|
|
|
|
/**
|
|
* Deletes an entire album's media contents.
|
|
* @return void
|
|
*/
|
|
public function deleteAlbumContents()
|
|
{
|
|
// No need to do anything for the album container - once the files are gone, the virtual folder is also gone
|
|
}
|
|
|
|
/**
|
|
* Deletes a thumbnail file for a photo.
|
|
* @param Photo $photo Photo to delete the thumbnail from.
|
|
* @param string $thumbnail Thumbnail to delete (or null to delete the original.)
|
|
* @return void
|
|
*/
|
|
public function deleteThumbnail(Photo $photo, $thumbnail = null)
|
|
{
|
|
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
// Create or update our cache record
|
|
|
|
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
if (is_null($b2Cache))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->getClient()->deleteFile($b2Cache->b2_file_id, $pathOnStorage);
|
|
$b2Cache->delete();
|
|
}
|
|
|
|
/**
|
|
* Fetches the contents of a thumbnail for a photo.
|
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
|
* @return Stream
|
|
*/
|
|
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
|
{
|
|
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
// First we need the file ID
|
|
|
|
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
if (is_null($b2Cache))
|
|
{
|
|
return stream_for('');
|
|
}
|
|
|
|
return stream_for($this->getClient()->downloadFile($b2Cache->b2_file_id));
|
|
}
|
|
|
|
/**
|
|
* Gets the name of this album source.
|
|
* @return string
|
|
*/
|
|
public function getName()
|
|
{
|
|
return 'global.album_sources.backblaze_b2';
|
|
}
|
|
|
|
/**
|
|
* Gets the absolute URL to the given photo file.
|
|
* @param Photo $photo Photo to get the URL to.
|
|
* @param string $thumbnail Thumbnail to get the image to.
|
|
* @return string
|
|
*/
|
|
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
|
{
|
|
$client = $this->getClient();
|
|
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
switch ($this->bucketType)
|
|
{
|
|
case self::BUCKET_TYPE_PRIVATE:
|
|
if (is_null($this->downloadToken))
|
|
{
|
|
$this->downloadToken = $client->getDownloadAuthToken();
|
|
}
|
|
|
|
// Once I sort out the issue with b2_download_file_by_id, this line can be removed
|
|
return sprintf('%s/file/%s/%s?Authorization=%s', $client->getDownloadUrl(), $this->configuration->container_name, $pathOnStorage, $this->downloadToken);
|
|
|
|
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
if (is_null($b2Cache))
|
|
{
|
|
return '';
|
|
}
|
|
|
|
return sprintf('%s/b2api/v2/b2_download_file_by_id?fileId=%s&Authorization=%s', $client->getDownloadUrl(), urlencode($b2Cache->b2_file_id), urlencode($this->downloadToken));
|
|
|
|
case self::BUCKET_TYPE_PUBLIC:
|
|
/*
|
|
* From https://www.backblaze.com/b2/docs/b2_download_file_by_name.html:
|
|
* The base URL to use comes from the b2_authorize_account call, and looks something like
|
|
* https://f345.backblazeb2.com. The "f" in the URL stands for "file", and the number is the cluster
|
|
* number containing your account. To this base, you add "file/", your bucket name, a "/", and then the
|
|
* name of the file. The file name may itself include more "/" characters.
|
|
*/
|
|
return sprintf('%s/file/%s/%s', $client->getDownloadUrl(), $this->configuration->container_name, $pathOnStorage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves a generated thumbnail to its permanent location.
|
|
* @param Photo $photo Photo the image relates to.
|
|
* @param string $tempFilename Filename containing the image.
|
|
* @param string $thumbnail Name of the thumbnail (or null for the original.)
|
|
* @return mixed
|
|
*/
|
|
public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail = null)
|
|
{
|
|
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
|
|
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
if (!is_null($b2Cache))
|
|
{
|
|
// Delete the current file version if we're replacing a file that already exists
|
|
$this->getClient()->deleteFile($b2Cache->b2_file_id, $pathOnStorage);
|
|
}
|
|
|
|
// Upload the file to B2
|
|
$b2FileID = $this->getClient()->uploadFile($tempFilename, $pathOnStorage);
|
|
|
|
// Create or update our cache record
|
|
if (is_null($b2Cache))
|
|
{
|
|
$b2Cache = new BackblazeB2FileIdCache([
|
|
'photo_id' => $photo->id,
|
|
'storage_path' => $pathOnStorage,
|
|
'b2_file_id' => $b2FileID
|
|
]);
|
|
}
|
|
else
|
|
{
|
|
$b2Cache->b2_file_id = $b2FileID;
|
|
}
|
|
|
|
$b2Cache->save();
|
|
}
|
|
|
|
public function setConfiguration(Storage $configuration)
|
|
{
|
|
parent::setConfiguration($configuration);
|
|
}
|
|
|
|
/**
|
|
* @param $pathOnStorage
|
|
* @return BackblazeB2FileIdCache|null
|
|
*/
|
|
private function getB2FileFromCache($pathOnStorage)
|
|
{
|
|
$b2Cache = BackblazeB2FileIdCache::where('storage_path', $pathOnStorage)->first();
|
|
if (is_null($b2Cache))
|
|
{
|
|
// TODO: lookup the file on B2 to get the file ID
|
|
Log::warning(sprintf('B2 file ID not found in cache: %s', $pathOnStorage));
|
|
return null;
|
|
}
|
|
|
|
return $b2Cache;
|
|
}
|
|
|
|
private function getClient()
|
|
{
|
|
if (is_null($this->backblaze))
|
|
{
|
|
$this->backblaze = new BackblazeB2Service();
|
|
$this->backblaze->setCredentials(decrypt($this->configuration->access_key), decrypt($this->configuration->secret_key));
|
|
$this->backblaze->authorizeAccount();
|
|
$this->backblaze->setBucketName($this->configuration->container_name);
|
|
|
|
if (intval($this->configuration->b2_bucket_type) == self::BUCKET_TYPE_AUTO)
|
|
{
|
|
/* Auto-detect the type of bucket in use on B2 */
|
|
|
|
switch ($this->backblaze->getBucketType())
|
|
{
|
|
case 'allPrivate':
|
|
$this->configuration->b2_bucket_type = self::BUCKET_TYPE_PRIVATE;
|
|
break;
|
|
|
|
case 'allPublic':
|
|
$this->configuration->b2_bucket_type = self::BUCKET_TYPE_PUBLIC;
|
|
break;
|
|
}
|
|
|
|
$this->configuration->save();
|
|
}
|
|
|
|
// Set the bucket type
|
|
$this->bucketType = $this->configuration->b2_bucket_type;
|
|
}
|
|
|
|
return $this->backblaze;
|
|
}
|
|
|
|
private function getOriginalsFolder()
|
|
{
|
|
return '_originals';
|
|
}
|
|
|
|
private function getPathToPhoto(Photo $photo, $thumbnail = null)
|
|
{
|
|
return sprintf(
|
|
'%s/%s/%s',
|
|
$this->album->url_alias,
|
|
is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail,
|
|
$photo->storage_file_name
|
|
);
|
|
}
|
|
} |