"""
Utilities for interacting with Google Cloud Storage.
API Reference
=============
"""
from io import BytesIO
from pathlib import Path
from typing import Union
from flask import current_app
from google.cloud import storage
import uuid
def _get_storage_client():
return storage.Client(
project=current_app.config['PROJECT_ID']
)
def _get_bucket() -> storage.Bucket:
client = _get_storage_client()
image_store = current_app.config['STORAGE_BUCKET']
return client.bucket(image_store)
def _get_image_prefix() -> str:
return current_app.config['IMAGE_PREFIX']
def _get_video_prefix() -> str:
return current_app.config['VIDEO_PREFIX']
def _get_transcoding_prefix() -> str:
return current_app.config['TRANSCODING_PREFIX']
[docs]def generate_image_path(post_id: str, ext: str) -> Path:
"""
Generates the full path to be passed to :py:func:`.upload_data`.
:param post_id:
Output filename, usually gathered from an upload request.
:param ext:
Output extension, usually gathered from an upload request.
:return:
Full path to be passed to :py:func:`.upload_data`.
"""
image_store = _get_image_prefix()
path = "{}/{}.{}".format(image_store, post_id, ext)
return Path(path)
[docs]def generate_video_path(output_name: str, ext: str) -> Path:
"""
Generates the full path to be passed to :py:func:`upload_data`.
:param output_name:
Output filename, usually gathered from an upload request.
:param ext:
Output extension, usually gathered from an upload request.
:return:
Full path to be passed to :py:func:`.upload_data`.
"""
video_store = _get_video_prefix()
path = "{}/{}.{}".format(video_store, output_name, ext)
return Path(path)
[docs]def generate_transcoding_path() -> Path:
"""
Generates a directory path that can be used for publishing transcode events.
:return:
"""
prefix = _get_transcoding_prefix()
return Path(prefix, str(uuid.uuid4()))
[docs]def upload_data(data: BytesIO, content_type: str, full_path: Path) -> str:
"""
Uploads binary data to the given path on Google Cloud Storage.
:param data:
Binary data (for now just photos/videos).
:param content_type:
Correct content type for the data we're uploading.
:param full_path:
Full path including bucket.
:raises ValueError:
If :code:`full_path` begins with a leading slash.
:return:
Public URL to access file.
"""
if full_path.is_absolute():
raise ValueError("full_path may not start with a leading slash.")
bucket = _get_bucket()
blob = bucket.blob(str(full_path))
blob.upload_from_string(data.read(), content_type=content_type)
blob.make_public()
return blob.public_url
[docs]def get_public_url(filename: str) -> Union[str, None]:
"""
Get's a files public url based on the filename. Could be a video or image.
:param filename:
:return:
Public URL
"""
url = get_image_url(filename)
if url is None:
url = get_video_url(filename)
return url
[docs]def get_image_url(filename: str) -> Union[str, None]:
"""
Gets an image's public url based on the filename without extension.
:param filename:
:return:
Image's public URL.
"""
bucket = _get_bucket()
img_prefix = _get_image_prefix()
for blob in bucket.list_blobs(prefix=img_prefix):
path = Path(blob.name)
if path.stem == filename:
return blob.public_url
[docs]def get_video_url(filename: str) -> Union[str, None]:
"""
Gets a video's public url based on the filename without extension.
:param filename:
:return:
Video's public URL
"""
bucket = _get_bucket()
vid_prefix = _get_video_prefix()
for blob in bucket.list_blobs(prefix=vid_prefix):
path = Path(blob.name)
if path.stem == filename:
return blob.public_url