Last active
March 4, 2021 14:38
-
-
Save chakany/b90d14ed482ff34ef4c8c6ecbc3e5f5c to your computer and use it in GitHub Desktop.
Files for XBackBone Embeds
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="{{ current_lang }}"> | |
<head> | |
<title>{% block title %}Default{% endblock %} | {{ config.app_name }}</title> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('/static/images/apple-touch-icon.png') }}"> | |
<link rel="icon" type="image/png" sizes="32x32" href="{{ asset('/static/images/favicon-32x32.png') }}"> | |
<link rel="icon" type="image/png" sizes="16x16" href="{{ asset('/static/images/favicon-16x16.png') }}"> | |
<link rel="manifest" href="{{ asset('/static/images/site.webmanifest') }}"> | |
<link rel="mask-icon" href="{{ asset('/static/images/safari-pinned-tab.svg') }}" color="#5bbad5"> | |
<link rel="shortcut icon" href="{{ asset('/static/images/favicon.ico') }}"> | |
<meta name="msapplication-TileColor" content="#603cba"> | |
<meta name="msapplication-config" content="{{ asset('/static/images/browserconfig.xml') }}"> | |
<meta name="theme-color" content="#ff3232"> | |
<link rel="preload" href="{{ asset('/static/bootstrap/css/bootstrap.min.css') }}" as="style"> | |
<link rel="preload" href="{{ asset('/static/fontawesome/css/all.min.css') }}" as="style"> | |
<link rel="preload" href="{{ asset('/static/app/app.css') }}" as="style"> | |
<link rel="preload" href="{{ asset('/static/jquery/jquery.min.js') }}" as="script"> | |
<link rel="preload" href="{{ asset('/static/bootstrap/js/bootstrap.bundle.min.js') }}" as="script"> | |
<link rel="preload" href="{{ asset('/static/app/app.js') }}" as="script"> | |
<link href="{{ asset('/static/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/fontawesome/css/all.min.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/highlightjs/styles/monokai.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/plyr/plyr.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/dropzone/dropzone.min.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/bootstrap/css/bootstrap4-toggle.min.css') }}" rel="stylesheet"> | |
<link href="{{ asset('/static/app/app.css') }}" rel="stylesheet"> | |
<script> | |
window.AppConfig = { | |
'base_url': '{{ urlFor() }}', | |
'max_upload_size': {{ maxUploadSize }}, | |
'lang': {'publish': '{{ lang('publish') }}', 'hide': '{{ lang('hide') }}', 'dropzone': '{{ lang('drop_to_upload') }}'} | |
} | |
</script> | |
<meta id="embed-title" property="og:site_name" content="{{ config.app_name }}"> | |
<meta id="embed-url" property="og:url" content="{{ url }}"> | |
<!-- This will only display an image, but that's the most important for now --> | |
{% if type %} | |
<meta name="twitter:card" content="summary_large_image"> | |
<meta property="og:type" content="website" /> | |
<meta id="embed-title" property="og:title" content="{{ media.filename }} ({{ media.size }})"> | |
<meta id="embed-image" property="og:image" content="{{ url }}/raw"> | |
<meta id="embed-desc" property="og:description" content="Uploaded at {{ media.timestamp }} by {{ media.username }}"> | |
<meta id="discord" name="twitter:image" content="{{ url }}/raw"> | |
<meta id="image-src" name="twitter:image:src" content="{{ url }}/raw"> | |
{% else %} | |
<meta id="embed-title" property="og:title" content="{{ block('title') }}"> | |
<meta id="discord" name="twitter:image" content="/static/images/favicon-32x32.png"> | |
<meta id="image-src" name="twitter:image:src" content="/static/images/favicon-32x32.png"> | |
<meta id="embed-image" property="og:image" content="/static/images/favicon-32x32.png"> | |
{% endif %} | |
{% block head %}{% endblock %} | |
{{ customHead|raw }} | |
</head> | |
<body class="bg-light"> | |
{% block content %}{% endblock %} | |
{% block footer %} | |
<div class="container-fluid footer" style="display: none; font-size: 0.8rem"> | |
<div class="text-muted">Proudly powered by <a href="https://sergix44.github.io/XBackBone/">XBackBone{% if session.get('admin') %} v{{ PLATFORM_VERSION }}{% endif %}</a> — <i class="fas fa-fw fa-balance-scale"></i> AGPL v3.0</div> | |
</div> | |
{% endblock %} | |
<script src="{{ asset('/static/jquery/jquery.min.js') }}"></script> | |
<script src="{{ asset('/static/bootstrap/js/bootstrap.bundle.min.js') }}"></script> | |
<script src="{{ asset('/static/highlightjs/highlight.pack.min.js') }}"></script> | |
<script src="{{ asset('/static/highlightjs/highlightjs-line-numbers.min.js') }}"></script> | |
<script src="{{ asset('/static/clipboardjs/clipboard.min.js') }}"></script> | |
<script src="{{ asset('/static/plyr/plyr.min.js') }}"></script> | |
<script src="{{ asset('/static/dropzone/dropzone.min.js') }}"></script> | |
<script src="{{ asset('/static/bootstrap/js/bootstrap4-toggle.min.js') }}"></script> | |
<script src="{{ asset('/static/app/app.js') }}"></script> | |
<script> | |
hljs.initHighlightingOnLoad(); | |
hljs.initLineNumbersOnLoad({ | |
singleLine: true | |
}); | |
</script> | |
{% block js %}{% endblock %} | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Controllers; | |
use App\Database\Queries\UserQuery; | |
use GuzzleHttp\Psr7\Stream; | |
use Intervention\Image\Constraint; | |
use Intervention\Image\ImageManagerStatic as Image; | |
use League\Flysystem\FileNotFoundException; | |
use League\Flysystem\Filesystem; | |
use Psr\Http\Message\ResponseInterface as Response; | |
use Psr\Http\Message\ServerRequestInterface as Request; | |
use Slim\Exception\HttpBadRequestException; | |
use Slim\Exception\HttpNotFoundException; | |
use Slim\Exception\HttpUnauthorizedException; | |
class MediaController extends Controller | |
{ | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param string $userCode | |
* @param string $mediaCode | |
* @param string|null $token | |
* | |
* @return Response | |
* @throws HttpNotFoundException | |
* @throws \Twig\Error\LoaderError | |
* @throws \Twig\Error\RuntimeError | |
* @throws \Twig\Error\SyntaxError | |
* @throws FileNotFoundException | |
* | |
*/ | |
public function show(Request $request, Response $response, string $userCode, string $mediaCode, string $token = null): Response | |
{ | |
$media = $this->getMedia($userCode, $mediaCode, true); | |
if (!$media || (!$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false))) { | |
throw new HttpNotFoundException($request); | |
} | |
$filesystem = $this->storage; | |
try { | |
$media->mimetype = $filesystem->getMimetype($media->storage_path); | |
$size = $filesystem->getSize($media->storage_path); | |
$type = explode('/', $media->mimetype)[0]; | |
if ($type === 'image' && !isDisplayableImage($media->mimetype)) { | |
$type = 'application'; | |
$media->mimetype = 'application/octet-stream'; | |
} | |
if ($type === 'text') { | |
if ($size <= (200 * 1024)) { // less than 200 KB | |
$media->text = $filesystem->read($media->storage_path); | |
} else { | |
$type = 'application'; | |
$media->mimetype = 'application/octet-stream'; | |
} | |
} | |
$media->size = humanFileSize($size); | |
} catch (FileNotFoundException $e) { | |
throw new HttpNotFoundException($request); | |
} | |
return view()->render($response, 'upload/public.twig', [ | |
'delete_token' => $token, | |
'media' => $media, | |
'type' => $type, | |
'url' => urlFor("/{$userCode}/{$mediaCode}"), | |
'copy_url_behavior' => $this->getSetting('copy_url_behavior', 'off'), | |
]); | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param int $id | |
* | |
* @return Response | |
* @throws HttpNotFoundException | |
* | |
* @throws FileNotFoundException | |
*/ | |
public function getRawById(Request $request, Response $response, int $id): Response | |
{ | |
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch(); | |
if (!$media) { | |
throw new HttpNotFoundException($request); | |
} | |
return $this->streamMedia($request, $response, $this->storage, $media); | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param string $userCode | |
* @param string $mediaCode | |
* @param string|null $ext | |
* | |
* @return Response | |
* @throws HttpBadRequestException | |
* @throws HttpNotFoundException | |
* | |
* @throws FileNotFoundException | |
*/ | |
public function getRaw(Request $request, Response $response, string $userCode, string $mediaCode, ?string $ext = null): Response | |
{ | |
$media = $this->getMedia($userCode, $mediaCode, false); | |
if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) { | |
throw new HttpNotFoundException($request); | |
} | |
if ($ext !== null && pathinfo($media->filename, PATHINFO_EXTENSION) !== $ext) { | |
throw new HttpBadRequestException($request); | |
} | |
// If contains html, return it as text/plain | |
if (strpos($this->storage->getMimetype($media->storage_path), 'text/htm') !== false) { | |
$response = $this->streamMedia($request, $response, $this->storage, $media); | |
return $response->withHeader('Content-Type', 'text/plain'); | |
} | |
return $this->streamMedia($request, $response, $this->storage, $media); | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param string $userCode | |
* @param string $mediaCode | |
* | |
* @return Response | |
* @throws HttpNotFoundException | |
* | |
* @throws FileNotFoundException | |
*/ | |
public function download(Request $request, Response $response, string $userCode, string $mediaCode): Response | |
{ | |
$media = $this->getMedia($userCode, $mediaCode, false); | |
if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) { | |
throw new HttpNotFoundException($request); | |
} | |
return $this->streamMedia($request, $response, $this->storage, $media, 'attachment'); | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param int $id | |
* | |
* @return Response | |
* @throws HttpNotFoundException | |
* | |
*/ | |
public function togglePublish(Request $request, Response $response, int $id): Response | |
{ | |
if ($this->session->get('admin')) { | |
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch(); | |
} else { | |
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? AND `user_id` = ? LIMIT 1', [$id, $this->session->get('user_id')])->fetch(); | |
} | |
if (!$media) { | |
throw new HttpNotFoundException($request); | |
} | |
$this->database->query('UPDATE `uploads` SET `published`=? WHERE `id`=?', [$media->published ? 0 : 1, $media->id]); | |
return $response; | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param int $id | |
* | |
* @return Response | |
* @throws HttpNotFoundException | |
* @throws HttpUnauthorizedException | |
*/ | |
public function delete(Request $request, Response $response, int $id): Response | |
{ | |
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch(); | |
if (!$media) { | |
throw new HttpNotFoundException($request); | |
} | |
if ($this->session->get('admin', false) || $media->user_id === $this->session->get('user_id')) { | |
$this->deleteMedia($request, $media->storage_path, $id, $media->user_id); | |
$this->logger->info('User '.$this->session->get('username').' deleted a media.', [$id]); | |
if ($media->user_id === $this->session->get('user_id')) { | |
$user = make(UserQuery::class)->get($request, $media->user_id, true); | |
$this->setSessionQuotaInfo($user->current_disk_quota, $user->max_disk_quota); | |
} | |
} else { | |
throw new HttpUnauthorizedException($request); | |
} | |
if ($request->getMethod() === 'GET') { | |
return redirect($response, route('home')); | |
} | |
return $response; | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param string $userCode | |
* @param string $mediaCode | |
* @param string $token | |
* | |
* @return Response | |
* @throws HttpUnauthorizedException | |
* | |
* @throws HttpNotFoundException | |
*/ | |
public function deleteByToken(Request $request, Response $response, string $userCode, string $mediaCode, string $token): Response | |
{ | |
$media = $this->getMedia($userCode, $mediaCode, false); | |
if (!$media) { | |
throw new HttpNotFoundException($request); | |
} | |
$user = $this->database->query('SELECT `id`, `active` FROM `users` WHERE `token` = ? LIMIT 1', $token)->fetch(); | |
if (!$user) { | |
$this->session->alert(lang('token_not_found'), 'danger'); | |
return redirect($response, $request->getHeaderLine('Referer')); | |
} | |
if (!$user->active) { | |
$this->session->alert(lang('account_disabled'), 'danger'); | |
return redirect($response, $request->getHeaderLine('Referer')); | |
} | |
if ($this->session->get('admin', false) || $user->id === $media->user_id) { | |
$this->deleteMedia($request, $media->storage_path, $media->mediaId, $user->id); | |
$this->logger->info('User '.$user->username.' deleted a media via token.', [$media->mediaId]); | |
} else { | |
throw new HttpUnauthorizedException($request); | |
} | |
return redirect($response, route('home')); | |
} | |
/** | |
* @param Request $request | |
* @param string $storagePath | |
* @param int $id | |
* | |
* @param int $userId | |
* @return void | |
* @throws HttpNotFoundException | |
*/ | |
protected function deleteMedia(Request $request, string $storagePath, int $id, int $userId) | |
{ | |
try { | |
$size = $this->storage->getSize($storagePath); | |
$this->storage->delete($storagePath); | |
$this->updateUserQuota($request, $userId, $size, true); | |
} catch (FileNotFoundException $e) { | |
throw new HttpNotFoundException($request); | |
} finally { | |
$this->database->query('DELETE FROM `uploads` WHERE `id` = ?', $id); | |
$this->database->query('DELETE FROM `tags` WHERE `tags`.`id` NOT IN (SELECT `uploads_tags`.`tag_id` FROM `uploads_tags`)'); | |
} | |
} | |
/** | |
* @param $userCode | |
* @param $mediaCode | |
* | |
* @param bool $withTags | |
* @return mixed | |
*/ | |
protected function getMedia($userCode, $mediaCode, $withTags = false) | |
{ | |
$mediaCode = pathinfo($mediaCode)['filename']; | |
$media = $this->database->query('SELECT `uploads`.*, `users`.*, `users`.`id` AS `userId`, `uploads`.`id` AS `mediaId` FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_code` = ? AND `uploads`.`code` = ? LIMIT 1', [ | |
$userCode, | |
$mediaCode, | |
])->fetch(); | |
if (!$withTags || !$media) { | |
return $media; | |
} | |
$media->tags = []; | |
foreach ($this->database->query('SELECT `tags`.`id`, `tags`.`name` FROM `uploads_tags` INNER JOIN `tags` ON `uploads_tags`.`tag_id` = `tags`.`id` WHERE `uploads_tags`.`upload_id` = ?', $media->mediaId) as $tag) { | |
$media->tags[$tag->id] = $tag->name; | |
} | |
return $media; | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param Filesystem $storage | |
* @param $media | |
* @param string $disposition | |
* | |
* @return Response | |
* @throws FileNotFoundException | |
* | |
*/ | |
protected function streamMedia(Request $request, Response $response, Filesystem $storage, $media, string $disposition = 'inline'): Response | |
{ | |
set_time_limit(0); | |
$this->session->close(); | |
$mime = $storage->getMimetype($media->storage_path); | |
if ((param($request, 'width') !== null || param($request, 'height') !== null) && explode('/', $mime)[0] === 'image') { | |
return $this->makeThumbnail($storage, $media, param($request, 'width'), param($request, 'height'), $disposition); | |
} else { | |
$stream = new Stream($storage->readStream($media->storage_path)); | |
if (!in_array(explode('/', $mime)[0], ['image', 'video', 'audio']) || $disposition === 'attachment') { | |
return $response->withHeader('Content-Type', $mime) | |
->withHeader('Content-Disposition', $disposition.'; filename="'.$media->filename.'"') | |
->withHeader('Content-Length', $stream->getSize()) | |
->withBody($stream); | |
} | |
if (isset($request->getServerParams()['HTTP_RANGE'])) { | |
return $this->handlePartialRequest($response, $stream, $request->getServerParams()['HTTP_RANGE'], $disposition, $media, $mime); | |
} | |
return $response->withHeader('Content-Type', $mime) | |
->withHeader('Content-Length', $stream->getSize()) | |
->withHeader('Accept-Ranges', 'bytes') | |
->withBody($stream); | |
} | |
} | |
/** | |
* @param Filesystem $storage | |
* @param $media | |
* @param null $width | |
* @param null $height | |
* @param string $disposition | |
* | |
* @return Response | |
* @throws FileNotFoundException | |
* | |
*/ | |
protected function makeThumbnail(Filesystem $storage, $media, $width = null, $height = null, string $disposition = 'inline') | |
{ | |
return Image::make($storage->readStream($media->storage_path)) | |
->resize($width, $height, function (Constraint $constraint) { | |
$constraint->aspectRatio(); | |
}) | |
->resizeCanvas($width, $height, 'center') | |
->psrResponse('png') | |
->withHeader('Content-Disposition', $disposition.';filename="scaled-'.pathinfo($media->filename, PATHINFO_FILENAME).'.png"'); | |
} | |
/** | |
* @param Response $response | |
* @param Stream $stream | |
* @param string $range | |
* @param string $disposition | |
* @param $media | |
* @param $mime | |
* | |
* @return Response | |
*/ | |
protected function handlePartialRequest(Response $response, Stream $stream, string $range, string $disposition, $media, $mime) | |
{ | |
$end = $stream->getSize() - 1; | |
[, $range] = explode('=', $range, 2); | |
if (strpos($range, ',') !== false) { | |
return $response->withHeader('Content-Type', $mime) | |
->withHeader('Content-Disposition', $disposition.'; filename="'.$media->filename.'"') | |
->withHeader('Content-Length', $stream->getSize()) | |
->withHeader('Accept-Ranges', 'bytes') | |
->withHeader('Content-Range', "0,{$stream->getSize()}") | |
->withStatus(416) | |
->withBody($stream); | |
} | |
if ($range === '-') { | |
$start = $stream->getSize() - (int) substr($range, 1); | |
} else { | |
$range = explode('-', $range); | |
$start = (int) $range[0]; | |
$end = (isset($range[1]) && is_numeric($range[1])) ? (int) $range[1] : $stream->getSize(); | |
} | |
$end = ($end > $stream->getSize() - 1) ? $stream->getSize() - 1 : $end; | |
$stream->seek($start); | |
header("Content-Type: $mime"); | |
header('Content-Length: '.($end - $start + 1)); | |
header('Accept-Ranges: bytes'); | |
header("Content-Range: bytes $start-$end/{$stream->getSize()}"); | |
http_response_code(206); | |
ob_end_clean(); | |
$buffer = 16348; | |
$readed = $start; | |
while ($readed < $end) { | |
if ($readed + $buffer > $end) { | |
$buffer = $end - $readed + 1; | |
} | |
echo $stream->read($buffer); | |
$readed += $buffer; | |
} | |
exit(0); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends 'base.twig' %} | |
{% block title %}{{ media.filename }}{% endblock %} | |
{% block content %} | |
<nav class="navbar navbar-dark bg-primary navbar-expand-md mb-4"> | |
<div class="container-fluid"> | |
<a class="navbar-brand" href="{{ route('root') }}">{{ config.app_name }}</a> | |
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> | |
<span class="navbar-toggler-icon"></span> | |
</button> | |
<div class="collapse navbar-collapse" id="navbarCollapse"> | |
<div class="ml-auto"> | |
<a href="javascript:void(0)" class="btn btn-success my-2 my-sm-0 btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ url }}{{ copy_url_behavior == 'raw' ? '/raw' }}"><i class="fas fa-link fa-lg fa-fw"></i></a> | |
<a href="{{ url }}/raw" class="btn btn-secondary my-2 my-sm-0" data-toggle="tooltip" title="{{ lang('raw') }}"><i class="fas fa-file-alt fa-lg fa-fw"></i></a> | |
<a href="{{ url }}/download" class="btn btn-warning my-2 my-sm-0" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt fa-lg fa-fw"></i></a> | |
{% if session.get('logged') %} | |
<a href="javascript:void(0)" class="btn btn-danger my-2 my-sm-0 public-delete" data-link="{{ route('upload.delete', {'id': media.mediaId}) }}" data-toggle="tooltip" title="{{ lang('delete') }}"><i class="fas fa-trash fa-lg fa-fw"></i></a> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
</nav> | |
<div class="container-fluid"> | |
{% include 'comp/alert.twig' %} | |
<div class="row ml-auto mr-auto"> | |
<div class="col-md-12 justify-content-center"> | |
{% if delete_token is not null %} | |
<form method="post" action="{{ url }}/delete/{{ delete_token }}"> | |
<div class="text-center mb-4"> | |
<p>{{ lang('public.delete_text') }}</p> | |
<div class="btn-group"> | |
<button type="submit" class="btn btn-danger"><i class="fas fa-trash"></i> {{ lang('yes') }}</button> | |
<a href="{{ url }}" class="btn btn-secondary">{{ lang('no') }}</a> | |
</div> | |
</div> | |
</form> | |
{% endif %} | |
{% set typeMatched = false %} | |
{% if type is same as ('image') %} | |
{% set typeMatched = true %} | |
<div class="row mb-2"> | |
<div class="col-md-12"> | |
<img src="{{ url }}/raw" class="img-thumbnail rounded mx-auto d-block" alt="{{ media.filename }}"> | |
</div> | |
</div> | |
{% elseif type is same as ('text') %} | |
{% set typeMatched = true %} | |
<div class="row mb-2"> | |
<div class="col-md-12"> | |
<pre><code>{{ media.text }}</code></pre> | |
</div> | |
</div> | |
{% elseif type is same as ('audio') %} | |
{% set typeMatched = true %} | |
<div class="media-player media-audio"> | |
<audio id="player" autoplay controls loop preload="auto"> | |
<source src="{{ url }}/raw" type="{{ media.mimetype }}"> | |
Your browser does not support HTML5 audio. | |
<a href="{{ url }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a> | |
</audio> | |
</div> | |
{% elseif type is same as ('video') %} | |
{% set typeMatched = true %} | |
<div class="media-player"> | |
<video id="player" autoplay controls loop preload="auto"> | |
<source src="{{ url }}/raw" type="{{ media.mimetype }}"> | |
Your browser does not support HTML5 video. | |
<a href="{{ url }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a> | |
</video> | |
</div> | |
{% elseif media.mimetype is same as ('application/pdf') %} | |
{% set typeMatched = true %} | |
<object type="{{ media.mimetype }}" data="{{ url }}/raw" class="pdf-viewer"> | |
Your browser does not support PDF previews. | |
<a href="{{ url }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a> | |
</object> | |
{% endif %} | |
{% if not typeMatched %} | |
<div class="text-center"> | |
<div class="row mb-3"> | |
<div class="col-md-12"> | |
<i class="far {{ mime2font(media.mimetype) }} fa-10x"></i> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-12"> | |
<b>{{ media.filename }}</b> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-12"> | |
{{ media.size }} | |
</div> | |
</div> | |
{% if media.tags is not empty %} | |
<div class="row mt-1 mb-2"> | |
<div class="col-md-12 text-center"> | |
{% for tag_id, tag_name in media.tags %} | |
<span class="badge badge-pill badge-primary shadow-sm mr-1" title="{{ tag_name }}">{{ tag_name }}</span> | |
{% endfor %} | |
</div> | |
</div> | |
{% endif %} | |
<div class="row mt-3"> | |
<div class="col-md-12"> | |
<a href="{{ url }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a> | |
</div> | |
</div> | |
</div> | |
{% else %} | |
<div class="row mt-1"> | |
<div class="col-md-12 text-center"> | |
{{ media.filename }} | |
</div> | |
</div> | |
{% if media.tags is not empty %} | |
<div class="row mt-2"> | |
<div class="col-md-12 text-center"> | |
{% for tag_id, tag_name in media.tags %} | |
<span class="badge badge-pill badge-primary shadow-sm mr-1" title="{{ tag_name }}">{{ tag_name }}</span> | |
{% endfor %} | |
</div> | |
</div> | |
{% endif %} | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
{% include 'comp/modal_delete.twig' %} | |
{% endblock %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Paths are:
resources/templates/base.twig
resources/templates/upload/public.twig
app/Controllers/MediaController.php