mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-03 18:38:27 +01:00
Add Videos to Remarkup
Summary: Ref T6916. Added video to remarkup using D7156 as reference. Test Plan: - Viewed video files (MP4, Ogg) in Safari, Chrome, Firefox (some don't work, e.g., OGG in Safari, but nothing we can really do about that). - Used `alt`. - Used `autoplay`. - Used `loop`. - Used `media=audio`. - Viewed file detail page. Reviewers: nateguchi2, chad, #blessed_reviewers Reviewed By: chad, #blessed_reviewers Subscribers: asherkin, ivo, joshuaspence, Korvin, epriestley Tags: #remarkup Maniphest Tasks: T6916 Differential Revision: https://secure.phabricator.com/D11297
This commit is contained in:
parent
f0eb6f4fe0
commit
411cf13457
7 changed files with 173 additions and 30 deletions
|
@ -34,9 +34,16 @@ final class PhabricatorFilesConfigOptions
|
|||
'image/x-icon' => 'image/x-icon',
|
||||
'image/vnd.microsoft.icon' => 'image/x-icon',
|
||||
|
||||
'audio/x-wav' => 'audio/x-wav',
|
||||
// This is a generic type for both OGG video and OGG audio.
|
||||
'application/ogg' => 'application/ogg',
|
||||
|
||||
'audio/x-wav' => 'audio/x-wav',
|
||||
'audio/mpeg' => 'audio/mpeg',
|
||||
'audio/ogg' => 'audio/ogg',
|
||||
|
||||
'video/mp4' => 'video/mp4',
|
||||
'video/ogg' => 'video/ogg',
|
||||
'video/webm' => 'video/webm',
|
||||
);
|
||||
|
||||
$image_default = array(
|
||||
|
@ -49,10 +56,29 @@ final class PhabricatorFilesConfigOptions
|
|||
'image/vnd.microsoft.icon' => true,
|
||||
);
|
||||
|
||||
|
||||
// The "application/ogg" type is listed as both an audio and video type,
|
||||
// because it may contain either type of content.
|
||||
|
||||
$audio_default = array(
|
||||
'audio/x-wav' => true,
|
||||
'application/ogg' => true,
|
||||
'audio/mpeg' => true,
|
||||
'audio/ogg' => true,
|
||||
|
||||
// These are video or ambiguous types, but can be forced to render as
|
||||
// audio with `media=audio`, which seems to work properly in browsers.
|
||||
// (For example, you can embed a music video as audio if you just want
|
||||
// to set the mood for your task without distracting viewers.)
|
||||
'video/mp4' => true,
|
||||
'video/ogg' => true,
|
||||
'application/ogg' => true,
|
||||
);
|
||||
|
||||
$video_default = array(
|
||||
'video/mp4' => true,
|
||||
'video/ogg' => true,
|
||||
'video/webm' => true,
|
||||
'application/ogg' => true,
|
||||
);
|
||||
|
||||
// largely lifted from http://en.wikipedia.org/wiki/Internet_media_type
|
||||
|
@ -70,6 +96,7 @@ final class PhabricatorFilesConfigOptions
|
|||
// movie file icon
|
||||
'video/mpeg' => 'fa-file-movie-o',
|
||||
'video/mp4' => 'fa-file-movie-o',
|
||||
'application/ogg' => 'fa-file-movie-o',
|
||||
'video/ogg' => 'fa-file-movie-o',
|
||||
'video/quicktime' => 'fa-file-movie-o',
|
||||
'video/webm' => 'fa-file-movie-o',
|
||||
|
@ -122,8 +149,14 @@ final class PhabricatorFilesConfigOptions
|
|||
->setSummary(pht('Configure which MIME types are audio.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'List of MIME types which can be used to render an `%s` tag.',
|
||||
'List of MIME types which can be rendered with an `%s` tag.',
|
||||
'<audio />')),
|
||||
$this->newOption('files.video-mime-types', 'set', $video_default)
|
||||
->setSummary(pht('Configure which MIME types are video.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'List of MIME types which can be rendered with a `%s` tag.',
|
||||
'<video />')),
|
||||
$this->newOption('files.icon-mime-types', 'wild', $icon_default)
|
||||
->setLocked(true)
|
||||
->setSummary(pht('Configure which MIME types map to which icons.'))
|
||||
|
|
|
@ -230,23 +230,34 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
$cache_string = pht('Not Applicable');
|
||||
}
|
||||
|
||||
$finfo->addProperty(pht('Viewable Image'), $image_string);
|
||||
$finfo->addProperty(pht('Cacheable'), $cache_string);
|
||||
|
||||
$builtin = $file->getBuiltinName();
|
||||
if ($builtin === null) {
|
||||
$builtin_string = pht('No');
|
||||
} else {
|
||||
$builtin_string = $builtin;
|
||||
$types = array();
|
||||
if ($file->isViewableImage()) {
|
||||
$types[] = pht('Image');
|
||||
}
|
||||
|
||||
$finfo->addProperty(pht('Builtin'), $builtin_string);
|
||||
if ($file->isVideo()) {
|
||||
$types[] = pht('Video');
|
||||
}
|
||||
|
||||
$is_profile = $file->getIsProfileImage()
|
||||
? pht('Yes')
|
||||
: pht('No');
|
||||
if ($file->isAudio()) {
|
||||
$types[] = pht('Audio');
|
||||
}
|
||||
|
||||
$finfo->addProperty(pht('Profile'), $is_profile);
|
||||
if ($file->getCanCDN()) {
|
||||
$types[] = pht('Can CDN');
|
||||
}
|
||||
|
||||
$builtin = $file->getBuiltinName();
|
||||
if ($builtin !== null) {
|
||||
$types[] = pht('Builtin ("%s")', $builtin);
|
||||
}
|
||||
|
||||
if ($file->getIsProfileImage()) {
|
||||
$types[] = pht('Profile');
|
||||
}
|
||||
|
||||
$types = implode(', ', $types);
|
||||
$finfo->addProperty(pht('Attributes'), $types);
|
||||
|
||||
$storage_properties = new PHUIPropertyListView();
|
||||
$box->addPropertyList($storage_properties, pht('Storage'));
|
||||
|
@ -292,6 +303,23 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
$media = id(new PHUIPropertyListView())
|
||||
->addImageContent($linked_image);
|
||||
|
||||
$box->addPropertyList($media);
|
||||
} else if ($file->isVideo()) {
|
||||
$video = phutil_tag(
|
||||
'video',
|
||||
array(
|
||||
'controls' => 'controls',
|
||||
'class' => 'phui-property-list-video',
|
||||
),
|
||||
phutil_tag(
|
||||
'source',
|
||||
array(
|
||||
'src' => $file->getViewURI(),
|
||||
'type' => $file->getMimeType(),
|
||||
)));
|
||||
$media = id(new PHUIPropertyListView())
|
||||
->addImageContent($video);
|
||||
|
||||
$box->addPropertyList($media);
|
||||
} else if ($file->isAudio()) {
|
||||
$audio = phutil_tag(
|
||||
|
|
|
@ -43,12 +43,27 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
|
||||
$is_viewable_image = $object->isViewableImage();
|
||||
$is_audio = $object->isAudio();
|
||||
$is_video = $object->isVideo();
|
||||
$force_link = ($options['layout'] == 'link');
|
||||
|
||||
$options['viewable'] = ($is_viewable_image || $is_audio);
|
||||
// If a file is both audio and video, as with "application/ogg" by default,
|
||||
// render it as video but allow the user to specify `media=audio` if they
|
||||
// want to force it to render as audio.
|
||||
if ($is_audio && $is_video) {
|
||||
$media = $options['media'];
|
||||
if ($media == 'audio') {
|
||||
$is_video = false;
|
||||
} else {
|
||||
$is_audio = false;
|
||||
}
|
||||
}
|
||||
|
||||
$options['viewable'] = ($is_viewable_image || $is_audio || $is_video);
|
||||
|
||||
if ($is_viewable_image && !$force_link) {
|
||||
return $this->renderImageFile($object, $handle, $options);
|
||||
} else if ($is_video && !$force_link) {
|
||||
return $this->renderVideoFile($object, $handle, $options);
|
||||
} else if ($is_audio && !$force_link) {
|
||||
return $this->renderAudioFile($object, $handle, $options);
|
||||
} else {
|
||||
|
@ -64,6 +79,9 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
'width' => null,
|
||||
'height' => null,
|
||||
'alt' => null,
|
||||
'media' => null,
|
||||
'autoplay' => null,
|
||||
'loop' => null,
|
||||
);
|
||||
|
||||
if ($option_string) {
|
||||
|
@ -201,22 +219,47 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
return $this->renderMediaFile('audio', $file, $handle, $options);
|
||||
}
|
||||
|
||||
private function renderVideoFile(
|
||||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
return $this->renderMediaFile('video', $file, $handle, $options);
|
||||
}
|
||||
|
||||
private function renderMediaFile(
|
||||
$tag,
|
||||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
|
||||
$is_video = ($tag == 'video');
|
||||
|
||||
if (idx($options, 'autoplay')) {
|
||||
$preload = 'auto';
|
||||
$autoplay = 'autoplay';
|
||||
} else {
|
||||
// If we don't preload video, the user can't see the first frame and
|
||||
// has no clue what they're looking at, so always preload.
|
||||
if ($is_video) {
|
||||
$preload = 'auto';
|
||||
} else {
|
||||
$preload = 'none';
|
||||
}
|
||||
$autoplay = null;
|
||||
}
|
||||
|
||||
return $this->newTag(
|
||||
'audio',
|
||||
$tag,
|
||||
array(
|
||||
'controls' => 'controls',
|
||||
'preload' => $preload,
|
||||
'autoplay' => $autoplay,
|
||||
'loop' => idx($options, 'loop') ? 'loop' : null,
|
||||
'alt' => $options['alt'],
|
||||
'class' => 'phabricator-media',
|
||||
),
|
||||
$this->newTag(
|
||||
'source',
|
||||
|
|
|
@ -802,6 +802,16 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return idx($mime_map, $mime_type);
|
||||
}
|
||||
|
||||
public function isVideo() {
|
||||
if (!$this->isViewableInBrowser()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mime_map = PhabricatorEnv::getEnvConfig('files.video-mime-types');
|
||||
$mime_type = $this->getMimeType();
|
||||
return idx($mime_map, $mime_type);
|
||||
}
|
||||
|
||||
public function isTransformableImage() {
|
||||
// NOTE: The way the 'gd' extension works in PHP is that you can install it
|
||||
// with support for only some file types, so it might be able to handle
|
||||
|
|
|
@ -410,18 +410,29 @@ You can set file display options like this:
|
|||
|
||||
{F123, layout=left, float, size=full, alt="a duckling"}
|
||||
|
||||
Valid options are:
|
||||
Valid options for all files are:
|
||||
|
||||
- **layout** left (default), center, right, inline, link (render a link
|
||||
instead of a thumbnail for images)
|
||||
- **name** with `layout=link` or for non-images, use this name for the link
|
||||
text
|
||||
- **alt** Provide alternate text for assistive technologies.
|
||||
|
||||
Image files support these options:
|
||||
|
||||
- **float** If layout is set to left or right, the image will be floated so
|
||||
text wraps around it.
|
||||
- **size** thumb (default), full
|
||||
- **name** with `layout=link` or for non-images, use this name for the link
|
||||
text
|
||||
- **width** Scale image to a specific width.
|
||||
- **height** Scale image to a specific height.
|
||||
- **alt** Provide alternate text for assistive technologies.
|
||||
|
||||
Audio and video files support these options:
|
||||
|
||||
- **media**: Specify the media type as `audio` or `video`. This allows you
|
||||
to disambiguate how file format which may contain either audio or video
|
||||
should be rendered.
|
||||
- **loop**: Loop this media.
|
||||
- **autoplay**: Automatically begin playing this media.
|
||||
|
||||
== Embedding Countdowns
|
||||
|
||||
|
|
|
@ -218,6 +218,17 @@
|
|||
width: 50%;
|
||||
}
|
||||
|
||||
video.phabricator-media {
|
||||
background: {$greybackground};
|
||||
}
|
||||
|
||||
.phabricator-remarkup video {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
min-width: 240px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-mention-exists {
|
||||
font-weight: bold;
|
||||
background: #e6f3ff;
|
||||
|
|
|
@ -160,6 +160,13 @@
|
|||
min-width: 240px;
|
||||
}
|
||||
|
||||
.phui-property-list-video {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 90%;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
/* When tags appear in property lists, give them a little more vertical
|
||||
spacing. */
|
||||
.phui-property-list-view .phui-tag-view {
|
||||
|
|
Loading…
Add table
Reference in a new issue