Alexander 5 vuotta sitten
vanhempi
sitoutus
f6c0d1d90e

+ 4 - 2
modules/boonex/videos/classes/BxVideosConfig.php

@@ -187,12 +187,14 @@ class BxVideosConfig extends BxBaseModTextConfig
 
         $this->_aJsClasses = array(
         	'manage_tools' => 'BxVideosManageTools',
-            'categories' => 'BxDolCategories'
+            'categories' => 'BxDolCategories',
+            'embeds' => 'BxVideosEmbeds',
         );
 
         $this->_aJsObjects = array(
         	'manage_tools' => 'oBxVideosManageTools',
-            'categories' => 'oBxDolCategories'
+            'categories' => 'oBxDolCategories',
+            'embeds' => 'oBxVideosEmbeds',
         );
 
         $this->_aGridObjects = array(

+ 4 - 0
modules/boonex/videos/classes/BxVideosDb.php

@@ -29,6 +29,10 @@ class BxVideosDb extends BxBaseModTextDb
         $sSql = "UPDATE `" . $CNF['TABLE_ENTRIES'] . "` SET " . $this->arrayToSQL($aParamsSet) . " WHERE " . $this->arrayToSQL($aParamsWhere, " AND ");
         return $this->query($sSql);
     }
+
+    public function getEmbedProviders() {
+        return $this->getAll("SELECT * FROM `bx_videos_embeds_providers`");
+    }
 }
 
 /** @} */

+ 30 - 0
modules/boonex/videos/classes/BxVideosEmbedProviderVimeo.php

@@ -0,0 +1,30 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Videos Videos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Class for parsing vimeo links.
+ */
+class BxVideosEmbedProviderVimeo
+{
+    public static function parseLink($sLink) {
+        $aResponse = @json_decode(file_get_contents('https://vimeo.com/api/oembed.json?url='.urlencode($sLink)), true);
+        if ($aResponse) {
+            return [
+                'id' => $aResponse['video_id'],
+                'thumb' => $aResponse['thumbnail_url'],
+                'embed' => $aResponse['html'],
+            ];
+        }
+        return false;
+    }
+}
+
+/** @} */

+ 29 - 0
modules/boonex/videos/classes/BxVideosEmbedProviderYoutube.php

@@ -0,0 +1,29 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Videos Videos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Class for parsing youtube links.
+ */
+class BxVideosEmbedProviderYoutube
+{
+    public static function parseLink($sLink) {
+        if (preg_match("/^(?:http(?:s)?:\/\/)?(?:www\.)?(?:m\.)?(?:youtu\.be\/|youtube\.com\/(?:(?:watch)?\?(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)\/))([^\?&\"'>]+)/", $sLink, $matches)) {
+            return [
+                'id' => $matches[1],
+                'thumb' => 'https://img.youtube.com/vi/'.$matches[1].'/hqdefault.jpg',
+                'embed' => '<iframe src="https://www.youtube.com/embed/'.$matches[1].'" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
+            ];
+        }
+        return false;
+    }
+}
+
+/** @} */

+ 135 - 1
modules/boonex/videos/classes/BxVideosFormEntry.php

@@ -33,6 +33,7 @@ class BxVideosFormEntry extends BxBaseModTextFormEntry
             $this->aInputs[$CNF['FIELD_VIDEOS']]['content_id'] = 0;
             $this->aInputs[$CNF['FIELD_VIDEOS']]['ghost_template'] = '';
         }
+
     }
     
     public function initChecker ($aValues = array (), $aSpecificValues = array())
@@ -67,6 +68,12 @@ class BxVideosFormEntry extends BxBaseModTextFormEntry
                 $aValsToAdd[$CNF['FIELD_POSTER']] = $iFilePoster;
         }
 
+        if (bx_get('video_source') == 'embed') {
+            $aEmbedData = $this->_oModule->parseEmbedLink($this->getCleanValue('video_embed'));
+            if ($aEmbedData)
+                $aValsToAdd['video_embed_data'] = serialize($aEmbedData);
+        }
+
         $iContentId = parent::insert ($aValsToAdd, $isIgnore);
         if(!empty($iContentId))
             $this->processFiles($CNF['FIELD_VIDEOS'], $iContentId, true);
@@ -88,13 +95,58 @@ class BxVideosFormEntry extends BxBaseModTextFormEntry
                 $aValsToAdd[$CNF['FIELD_POSTER']] = $iFilePoster;
         }
 
-        $iRet = parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+        if (bx_get('video_source') == 'embed') {
+            $aValsToAdd[$CNF['FIELD_STATUS']] = 'active'; //no need to keep awaiting if we've switched to embed
+            $aEmbedData = $this->_oModule->parseEmbedLink($this->getCleanValue('video_embed'));
+            if ($aEmbedData)
+                $aValsToAdd['video_embed_data'] = serialize($aEmbedData);
+        } else {
+            $aValsToAdd['video_embed_data'] = '';
+            $aValsToAdd['video_embed'] = '';
+        }
 
+        $iRet = parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
         $this->processFiles($CNF['FIELD_VIDEOS'], $iContentId, false);
 
+
         return $iRet;
     }
 
+    public function processFiles ($sFieldFile, $iContentId = 0, $isAssociateWithContent = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if ($sFieldFile == $CNF['FIELD_VIDEOS'] && bx_get('video_source') == 'embed') {
+            // delete video files in case the embed link has been provided
+            if ($iContentId) {
+                if (!isset($this->aInputs[$sFieldFile]))
+                    return true;
+
+                $mixedFileIds = $this->getCleanValue($sFieldFile);
+                if (!$mixedFileIds)
+                    return true;
+
+                $oStorage = BxDolStorage::getObjectInstance($this->aInputs[$sFieldFile]['storage_object']);
+                if (!$oStorage)
+                    return false;
+
+                $iProfileId = $this->getContentOwnerProfileId($iContentId);
+
+                $aGhostFiles = $oStorage->getGhosts($iProfileId, $isAssociateWithContent ? 0 : $iContentId, true, $this->_isAdmin($iContentId));
+                if (!$aGhostFiles)
+                    return true;
+
+                foreach ($aGhostFiles as $aFile) {
+                    if (is_array($mixedFileIds) && !in_array($aFile['id'], $mixedFileIds))
+                        continue;
+                    $oStorage->deleteFile($aFile['id'], bx_get_logged_profile_id());
+                }
+            }
+        } else {
+            return parent::processFiles($sFieldFile, $iContentId, $isAssociateWithContent);
+        }
+    }
+
     function delete ($iContentId, $aContentInfo = array())
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
@@ -115,6 +167,7 @@ class BxVideosFormEntry extends BxBaseModTextFormEntry
 
     protected function genCustomViewRowValueDuration(&$aInput)
     {
+        if (!$aInput['value']) return null;
         return _t_format_duration($aInput['value']);
     }
 
@@ -163,6 +216,87 @@ class BxVideosFormEntry extends BxBaseModTextFormEntry
 			'content_id' => $this->aInputs[$CNF['FIELD_VIDEOS']]['content_id'],
 		);
     }
+
+    public function genCustomRowVideoEmbed(&$aInput) {
+        $sSource = isset($this->aInputs['video_source']) && $this->aInputs['video_source']['value'] == 'embed' ? 'embed' : 'upload';
+        if ($sSource != 'embed') $aInput['tr_attrs']['style'] = 'display:none';
+        return $this->genRowStandard($aInput);
+    }
+
+    public function genCustomRowVideos(&$aInput) {
+        $sSource = isset($this->aInputs['video_source']) && $this->aInputs['video_source']['value'] == 'embed' ? 'embed' : 'upload';
+        if ($sSource != 'upload') $aInput['tr_attrs']['style'] = 'display:none';
+        return $this->genRowCustom($aInput, 'genInputFiles');
+    }
+
+    public function genCustomInputVideoEmbed(&$aInput) {
+        $aEmbedInput = [
+            'type' => 'text',
+            'name' => $aInput['name'],
+            'value' => isset($aInput['value']) ? $aInput['value'] : '',
+            'attrs' => [
+                'onchange' => $this->_oModule->_oConfig->getJsObject('embeds').'.onNewEmbedCode(this.value)',
+            ],
+        ];
+
+        $sEmbedCode = '';
+        if (isset($aInput['value']) && !empty($aInput['value']) && $aEmbed = $this->_oModule->parseEmbedLink($aInput['value'])) {
+            $sEmbedCode = $aEmbed['embed'];
+        }
+
+        return $this->_oModule->_oTemplate->parseHtmlByName('form_field_embed.html', [
+            'input' => $this->genInputStandard($aEmbedInput),
+            'bx_if:noembed_code' => [
+                'condition' => empty($sEmbedCode),
+                'content' => [],
+            ],
+            'embed_code' => $sEmbedCode,
+        ]);
+    }
+
+    public function genInput(&$aInput) {
+        $sJsCode = '';
+
+        if ($aInput['name'] == 'video_source' && $aInput['type'] == 'radio_set') {
+            $aInput['attrs']['onchange'] = $this->_oModule->_oConfig->getJsObject('embeds').'.changeVideoSource(this.value);';
+            $sJsCode = $this->_oModule->_oTemplate->getJsCode('embeds');
+            if ($this->_bDynamicMode) {
+                $sJsCode .= $this->_oModule->_oTemplate->addJs('embeds.js', true);
+                $sJsCode .= $this->_oModule->_oTemplate->addCss('embeds.css', true);
+            } else {
+                $this->_oModule->_oTemplate->addJs('embeds.js', false);
+                $this->_oModule->_oTemplate->addCss('embeds.css', false);
+            }
+        }
+
+        return parent::genInput($aInput).$sJsCode;
+    }
+}
+
+class BxVideosFormCheckerHelper extends BxDolFormCheckerHelper {
+    static public function checkUploadVideoAvail ($s)
+    {
+        $sSource = bx_get('video_source');
+        if ($sSource != 'embed') $sSource = 'upload';
+
+        if ($sSource == 'embed') return true; //if we are embedding then skip uploads check
+
+        return parent::checkAvail($s);
+    }
+    static public function checkEmbedVideoAvail ($s)
+    {
+        $sSource = bx_get('video_source');
+        if ($sSource != 'embed') $sSource = 'upload';
+
+        if ($sSource == 'upload') return true; //if we are uploading then skip embed check
+
+        if (!parent::checkAvail($s)) return false;
+
+        $oModule = BxDolModule::getInstance('bx_videos');
+        if (!$oModule->parseEmbedLink($s)) return false;
+
+        return true;
+    }
 }
 
 /** @} */

+ 26 - 0
modules/boonex/videos/classes/BxVideosModule.php

@@ -19,6 +19,23 @@ class BxVideosModule extends BxBaseModTextModule
         parent::__construct($aModule);
     }
 
+    public function actionParseEmbedLink() {
+        $sCode = bx_get('code');
+        $aRes = $this->parseEmbedLink($sCode);
+        if ($aRes) echo $aRes['embed'];
+    }
+
+    public function parseEmbedLink($sLink) {
+        $aEmbedProviders = $this->_oDb->getEmbedProviders();
+        if ($aEmbedProviders) foreach ($aEmbedProviders as $aEmbedProvider) {
+            if ($aEmbedProvider['class_file']) require_once(BX_DIRECTORY_PATH_ROOT . $aEmbedProvider['class_file']);
+            $mResult = $aEmbedProvider['class_name']::parseLink($sLink);
+            if ($mResult) return $mResult;
+        }
+
+        return false;
+    }
+
     /**
      * @page service Service Calls
      * @section bx_videos Videos
@@ -75,6 +92,15 @@ class BxVideosModule extends BxBaseModTextModule
     {
         $aResult = parent::_getContentForTimelinePost($aEvent, $aContentInfo, $aBrowseParams);
 
+        if ($aContentInfo['video_source'] == 'embed' && !empty($aContentInfo['video_embed_data'])) {
+            if (!is_array($aContentInfo['video_embed_data'])) $aContentInfo['video_embed_data'] = unserialize($aContentInfo['video_embed_data']);
+            if (isset($aContentInfo['video_embed_data']['embed']) && !empty($aContentInfo['video_embed_data']['embed'])) {
+                $aResult['images'] = array();
+                $aResult['top_raw'] = $this->_oTemplate->getResponsiveEmbed($aContentInfo['video_embed_data']['embed'], isset($aBrowseParams['dynamic_mode']) ? $aBrowseParams['dynamic_mode'] : false);
+                return $aResult;
+            }
+        }
+
         if(!empty($aResult['videos']) && is_array($aResult['videos']))
             $aResult['images'] = array();
 

+ 1 - 1
modules/boonex/videos/classes/BxVideosSearchResult.php

@@ -21,7 +21,7 @@ class BxVideosSearchResult extends BxBaseModTextSearchResult
             'object_metatags' => 'bx_videos',
             'title' => _t('_bx_videos_page_title_browse'),
             'table' => 'bx_videos_entries',
-            'ownFields' => array('id', 'title', 'text', 'duration', 'thumb', 'poster', 'video', 'author', 'added', 'views'),
+            'ownFields' => array('id', 'title', 'text', 'duration', 'thumb', 'poster', 'video', 'author', 'added', 'views', 'video_source', 'video_embed_data'),
             'searchFields' => array(),
             'restriction' => array(
                 'author' => array('value' => '', 'field' => 'author', 'operator' => '='),

+ 36 - 3
modules/boonex/videos/classes/BxVideosTemplate.php

@@ -46,6 +46,13 @@ class BxVideosTemplate extends BxBaseModTextTemplate
             'video' => ''
         );
 
+        if ($aContentInfo['video_source'] == 'embed' && !empty($aContentInfo['video_embed_data'])) {
+            if (!is_array($aContentInfo['video_embed_data'])) $aContentInfo['video_embed_data'] = unserialize($aContentInfo['video_embed_data']);
+            if (isset($aContentInfo['video_embed_data']['embed']) && !empty($aContentInfo['video_embed_data']['embed'])) {
+                return $this->getResponsiveEmbed($aContentInfo['video_embed_data']['embed']);
+            }
+        }
+
         $mixedVideo = $this->getVideo($aContentInfo);
         if($mixedVideo !== false)
             $aTmplVars = array_merge($aTmplVars, array(
@@ -77,6 +84,16 @@ class BxVideosTemplate extends BxBaseModTextTemplate
     {
         $CNF = &$this->getModule()->_oConfig->CNF;
 
+        if ($aContentInfo['video_source'] == 'embed' && !empty($aContentInfo['video_embed_data'])) {
+            if (!is_array($aContentInfo['video_embed_data'])) $aContentInfo['video_embed_data'] = unserialize($aContentInfo['video_embed_data']);
+            if (isset($aContentInfo['video_embed_data']['thumb']) && !empty($aContentInfo['video_embed_data']['thumb']) && isset($aContentInfo['video_embed_data']['embed']) && !empty($aContentInfo['video_embed_data']['embed'])) {
+                return [
+                    'poster_url' => $aContentInfo['video_embed_data']['thumb'],
+                    'player' => $this->getResponsiveEmbed($aContentInfo['video_embed_data']['embed']),
+                ];
+            }
+        }
+
         $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE_VIDEOS']);
 
         $aTranscodersVideo = false;
@@ -155,7 +172,12 @@ class BxVideosTemplate extends BxBaseModTextTemplate
         $this->_checkDuration($aData);
 
         $aUnit = parent::getUnit($aData, $aParams);
-        $aUnit['bx_if:thumb']['content']['duration'] = _t_format_duration($aData[$CNF['FIELD_DURATION']]);
+        $aUnit['bx_if:thumb']['content']['bx_if:duration_set'] = [
+            'condition' => $aData[$CNF['FIELD_DURATION']] > 0,
+            'content' => [
+                'duration' => _t_format_duration($aData[$CNF['FIELD_DURATION']]),
+            ]
+        ];
 
         return $aUnit;
     }
@@ -183,13 +205,17 @@ class BxVideosTemplate extends BxBaseModTextTemplate
         $CNF = &$this->getModule()->_oConfig->CNF;
 
         $sImage = '';
-        if(empty($sField) || empty($aData[$sField])) 
-            return $sImage;
 
         $oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($sTranscoder);
         if($oImagesTranscoder)
             $sImage = $oImagesTranscoder->getFileUrl($aData[$sField]);
 
+        if (!$aData[$sField] && $aData['video_source'] == 'embed' && !empty($aData['video_embed_data'])) {
+            if (!is_array($aData['video_embed_data'])) $aData['video_embed_data'] = unserialize($aData['video_embed_data']);
+            if (isset($aData['video_embed_data']['thumb']) && !empty($aData['video_embed_data']['thumb']))
+                return $aData['video_embed_data']['thumb'];
+        }
+
         return $sImage;
     }
 
@@ -212,6 +238,13 @@ class BxVideosTemplate extends BxBaseModTextTemplate
         $this->_oDb->updateEntries(array($CNF['FIELD_DURATION'] => $iDuration), array($CNF['FIELD_ID'] => $aContentInfo[$CNF['FIELD_ID']]));
     }
 
+    public function getResponsiveEmbed($sEmbedCode, $bDynamic = false) {
+        $sCss = $this->addCss('embeds.css', $bDynamic);
+
+        return $this->parseHtmlByName('entry-video-embed.html', [
+            'video' => $sEmbedCode,
+        ]).($bDynamic ? $sCss : '');
+    }
 }
 
 /** @} */

+ 9 - 0
modules/boonex/videos/install/langs/en.xml

@@ -44,6 +44,11 @@
     <string name="_bx_videos_form_entry_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete the video]]></string>
     <string name="_bx_videos_form_entry_input_sys_pictures"><![CDATA[Pictures]]></string>
     <string name="_bx_videos_form_entry_input_pictures"><![CDATA[Pictures]]></string>
+    <string name="_bx_videos_form_entry_input_sys_video_source"><![CDATA[Video Source]]></string>
+    <string name="_bx_videos_form_entry_input_video_source"><![CDATA[Video Source]]></string>
+    <string name="_bx_videos_form_entry_input_sys_video_embed"><![CDATA[Video Embed Link]]></string>
+    <string name="_bx_videos_form_entry_input_video_embed"><![CDATA[Video Embed Link]]></string>
+    <string name="_bx_videos_form_entry_input_video_embed_error"><![CDATA[Please provide a valid embed link.]]></string>
     <string name="_bx_videos_form_entry_input_sys_videos"><![CDATA[Video]]></string>
     <string name="_bx_videos_form_entry_input_videos"><![CDATA[Video]]></string>
     <string name="_bx_videos_form_entry_input_videos_error"><![CDATA[Video is essential. Please upload a video file.]]></string>
@@ -246,6 +251,10 @@
     <string name="_bx_videos_cat_sports"><![CDATA[Sports]]></string>
     <string name="_bx_videos_cat_travel"><![CDATA[Travel]]></string>
 
+    <string name="_bx_videos_pre_lists_source"><![CDATA[Videos Source]]></string>
+    <string name="_bx_videos_source_upload"><![CDATA[Upload]]></string>
+    <string name="_bx_videos_source_embed"><![CDATA[Embed]]></string>
+
     <string name="_bx_videos_txt_all_entries_by"><![CDATA[{0} videos]]></string>
     <string name="_bx_videos_txt_all_entries_in"><![CDATA[<a href="{0}">All Videos in {1} ({2})</a>]]></string>
     <string name="_bx_videos_txt_sample_single"><![CDATA[video]]></string>

+ 39 - 14
modules/boonex/videos/install/sql/install.sql

@@ -6,7 +6,10 @@ CREATE TABLE IF NOT EXISTS `bx_videos_entries` (
   `changed` int(11) NOT NULL default '0',
   `thumb` int(11) NOT NULL default '0',
   `poster` int(11) NOT NULL default '0',
+  `video_source` enum('upload', 'embed') NOT NULL DEFAULT 'upload',
   `video` int(11) NOT NULL default '0',
+  `video_embed` TEXT,
+  `video_embed_data` TEXT,
   `title` varchar(255) NOT NULL,
   `cat` int(11) NOT NULL,
   `multicat` text NOT NULL,
@@ -277,6 +280,18 @@ CREATE TABLE IF NOT EXISTS `bx_videos_scores_track` (
   KEY `vote` (`object_id`, `author_nip`)
 );
 
+CREATE TABLE IF NOT EXISTS `bx_videos_embeds_providers` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object` varchar(64) NOT NULL,
+  `module` varchar(64) NOT NULL,
+  `class_name` varchar(255) NOT NULL,
+  `class_file` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+INSERT INTO `bx_videos_embeds_providers` (`object`, `module`, `class_name`, `class_file`) VALUES
+('youtube', 'bx_videos', 'BxVideosEmbedProviderYoutube', 'modules/boonex/videos/classes/BxVideosEmbedProviderYoutube.php'),
+('vimeo', 'bx_videos', 'BxVideosEmbedProviderVimeo', 'modules/boonex/videos/classes/BxVideosEmbedProviderVimeo.php');
+
 
 -- STORAGES & TRANSCODERS
 SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
@@ -314,7 +329,7 @@ INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_par
 
 -- FORMS
 INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
-('bx_videos', 'bx_videos', '_bx_videos_form_entry', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_videos_entries', 'id', '', '', 'a:2:{i:0;s:9:"do_submit";i:1;s:10:"do_publish";}', '', 0, 1, 'BxVideosFormEntry', 'modules/boonex/videos/classes/BxVideosFormEntry.php');
+('bx_videos', 'bx_videos', '_bx_videos_form_entry', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_videos_entries', 'id', '', '', 'a:2:{i:0;s:9:"do_submit";i:1;s:10:"do_publish";}', 'a:1:{s:14:"checker_helper";s:25:"BxVideosFormCheckerHelper";}', 0, 1, 'BxVideosFormEntry', 'modules/boonex/videos/classes/BxVideosFormEntry.php');
 
 INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
 ('bx_videos', 'bx_videos_entry_add', 'bx_videos', 0, '_bx_videos_form_entry_display_add'),
@@ -328,7 +343,9 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 ('bx_videos', 'bx_videos', 'do_publish', '_bx_videos_form_entry_input_do_publish', '', 0, 'submit', '_bx_videos_form_entry_input_sys_do_publish', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_videos', 'bx_videos', 'do_submit', '_bx_videos_form_entry_input_do_submit', '', 0, 'submit', '_bx_videos_form_entry_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_videos', 'bx_videos', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
-('bx_videos', 'bx_videos', 'videos', 'a:2:{i:0;s:15:"bx_videos_html5";i:1;s:22:"bx_videos_record_video";}', 'a:3:{s:16:"bx_videos_simple";s:26:"_sys_uploader_simple_title";s:15:"bx_videos_html5";s:25:"_sys_uploader_html5_title";s:22:"bx_videos_record_video";s:32:"_sys_uploader_record_video_title";}', 0, 'files', '_bx_videos_form_entry_input_sys_videos', '_bx_videos_form_entry_input_videos', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_videos_form_entry_input_videos_error', '', '', 1, 0),
+('bx_videos', 'bx_videos', 'video_source', 'upload', '#!bx_videos_source', 0, 'radio_set', '_bx_videos_form_entry_input_sys_video_source', '_bx_videos_form_entry_input_video_source', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_videos', 'bx_videos', 'video_embed', '', '', 0, 'custom', '_bx_videos_form_entry_input_sys_video_embed', '_bx_videos_form_entry_input_video_embed', '', 1, 0, 0, '', '', '', 'EmbedVideoAvail', '', '_bx_videos_form_entry_input_video_embed_error', 'Xss', '', 1, 0),
+('bx_videos', 'bx_videos', 'videos', 'a:2:{i:0;s:15:"bx_videos_html5";i:1;s:22:"bx_videos_record_video";}', 'a:3:{s:16:"bx_videos_simple";s:26:"_sys_uploader_simple_title";s:15:"bx_videos_html5";s:25:"_sys_uploader_html5_title";s:22:"bx_videos_record_video";s:32:"_sys_uploader_record_video_title";}', 0, 'files', '_bx_videos_form_entry_input_sys_videos', '_bx_videos_form_entry_input_videos', '', 1, 0, 0, '', '', '', 'UploadVideoAvail', '', '_bx_videos_form_entry_input_videos_error', '', '', 1, 0),
 ('bx_videos', 'bx_videos', 'pictures', 'a:1:{i:0;s:15:"bx_videos_html5";}', 'a:2:{s:16:"bx_videos_simple";s:26:"_sys_uploader_simple_title";s:15:"bx_videos_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_videos_form_entry_input_sys_pictures', '_bx_videos_form_entry_input_pictures', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_videos', 'bx_videos', 'text', '', '', 0, 'textarea', '_bx_videos_form_entry_input_sys_text', '_bx_videos_form_entry_input_text', '', 0, 0, 3, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
 ('bx_videos', 'bx_videos', 'title', '', '', 0, 'text', '_bx_videos_form_entry_input_sys_title', '_bx_videos_form_entry_input_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_videos_form_entry_input_title_err', 'Xss', '', 1, 0),
@@ -343,11 +360,13 @@ INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for
 ('bx_videos_entry_add', 'title', 2147483647, 1, 1),
 ('bx_videos_entry_add', 'cat', 2147483647, 1, 2),
 ('bx_videos_entry_add', 'text', 2147483647, 1, 3),
-('bx_videos_entry_add', 'videos', 2147483647, 1, 4),
-('bx_videos_entry_add', 'pictures', 2147483647, 1, 5),
-('bx_videos_entry_add', 'allow_view_to', 2147483647, 1, 6),
-('bx_videos_entry_add', 'location', 2147483647, 1, 7),
-('bx_videos_entry_add', 'do_publish', 2147483647, 1, 8),
+('bx_videos_entry_add', 'video_source', 2147483647, 1, 4),
+('bx_videos_entry_add', 'video_embed', 2147483647, 1, 5),
+('bx_videos_entry_add', 'videos', 2147483647, 1, 6),
+('bx_videos_entry_add', 'pictures', 2147483647, 1, 7),
+('bx_videos_entry_add', 'allow_view_to', 2147483647, 1, 8),
+('bx_videos_entry_add', 'location', 2147483647, 1, 9),
+('bx_videos_entry_add', 'do_publish', 2147483647, 1, 10),
 
 ('bx_videos_entry_delete', 'delete_confirm', 2147483647, 1, 1),
 ('bx_videos_entry_delete', 'do_submit', 2147483647, 1, 2),
@@ -355,11 +374,13 @@ INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for
 ('bx_videos_entry_edit', 'title', 2147483647, 1, 1),
 ('bx_videos_entry_edit', 'cat', 2147483647, 1, 2),
 ('bx_videos_entry_edit', 'text', 2147483647, 1, 3),
-('bx_videos_entry_edit', 'videos', 2147483647, 1, 4),
-('bx_videos_entry_edit', 'pictures', 2147483647, 1, 5),
-('bx_videos_entry_edit', 'allow_view_to', 2147483647, 1, 6),
-('bx_videos_entry_edit', 'location', 2147483647, 1, 7),
-('bx_videos_entry_edit', 'do_submit', 2147483647, 1, 8),
+('bx_videos_entry_edit', 'video_source', 2147483647, 1, 4),
+('bx_videos_entry_edit', 'video_embed', 2147483647, 1, 5),
+('bx_videos_entry_edit', 'videos', 2147483647, 1, 6),
+('bx_videos_entry_edit', 'pictures', 2147483647, 1, 7),
+('bx_videos_entry_edit', 'allow_view_to', 2147483647, 1, 8),
+('bx_videos_entry_edit', 'location', 2147483647, 1, 9),
+('bx_videos_entry_edit', 'do_submit', 2147483647, 1, 10),
 
 ('bx_videos_entry_view', 'duration', 2147483647, 1, 1),
 ('bx_videos_entry_view', 'cat', 2147483647, 1, 2),
@@ -369,7 +390,8 @@ INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for
 
 -- PRE-VALUES
 INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
-('bx_videos_cats', '_bx_videos_pre_lists_cats', 'bx_videos', '0');
+('bx_videos_cats', '_bx_videos_pre_lists_cats', 'bx_videos', '0'),
+('bx_videos_source', '_bx_videos_pre_lists_source', 'bx_videos', '0');
 
 INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
 ('bx_videos_cats', '', 0, '_sys_please_select', ''),
@@ -381,7 +403,10 @@ INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALU
 ('bx_videos_cats', '6', 6, '_bx_videos_cat_news', ''),
 ('bx_videos_cats', '7', 7, '_bx_videos_cat_people_and_blogs', ''),
 ('bx_videos_cats', '8', 8, '_bx_videos_cat_sports', ''),
-('bx_videos_cats', '9', 9, '_bx_videos_cat_travel', '');
+('bx_videos_cats', '9', 9, '_bx_videos_cat_travel', ''),
+
+('bx_videos_source', 'upload', 0, '_bx_videos_source_upload', ''),
+('bx_videos_source', 'embed', 1, '_bx_videos_source_embed', '');
 
 
 -- COMMENTS

+ 2 - 2
modules/boonex/videos/install/sql/uninstall.sql

@@ -1,6 +1,6 @@
 
 -- TABLES
-DROP TABLE IF EXISTS `bx_videos_entries`, `bx_videos_photos`, `bx_videos_videos`, `bx_videos_media_resized`, `bx_videos_cmts`, `bx_videos_cmts_notes`, `bx_videos_votes`, `bx_videos_votes_track`, `bx_videos_svotes`, `bx_videos_svotes_track`, `bx_videos_reactions`, `bx_videos_reactions_track`, `bx_videos_views_track`, `bx_videos_meta_keywords`, `bx_videos_meta_locations`, `bx_videos_meta_mentions`, `bx_videos_reports`, `bx_videos_reports_track`, `bx_videos_favorites_track`, `bx_videos_favorites_lists`, `bx_videos_scores`, `bx_videos_scores_track`;
+DROP TABLE IF EXISTS `bx_videos_entries`, `bx_videos_photos`, `bx_videos_videos`, `bx_videos_media_resized`, `bx_videos_cmts`, `bx_videos_cmts_notes`, `bx_videos_votes`, `bx_videos_votes_track`, `bx_videos_svotes`, `bx_videos_svotes_track`, `bx_videos_reactions`, `bx_videos_reactions_track`, `bx_videos_views_track`, `bx_videos_meta_keywords`, `bx_videos_meta_locations`, `bx_videos_meta_mentions`, `bx_videos_reports`, `bx_videos_reports_track`, `bx_videos_favorites_track`, `bx_videos_favorites_lists`, `bx_videos_scores`, `bx_videos_scores_track`, `bx_videos_embeds_providers`;
 
 
 -- STORAGES & TRANSCODERS
@@ -23,7 +23,7 @@ DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN ('bx_videos_entry_
 -- PRE-VALUES
 DELETE FROM `sys_form_pre_lists` WHERE `module` = 'bx_videos';
 
-DELETE FROM `sys_form_pre_values` WHERE `Key` IN('bx_videos_cats');
+DELETE FROM `sys_form_pre_values` WHERE `Key` IN('bx_videos_cats', 'bx_videos_source');
 
 
 -- COMMENTS

+ 45 - 0
modules/boonex/videos/js/embeds.js

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Videos Videos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxVideosEmbeds(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._sParamsDivider = oOptions.sParamsDivider == undefined ? '#-#' : oOptions.sParamsDivider;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxVideosEmbeds.prototype.changeVideoSource = function(sSource) {
+	if (sSource == 'upload') {
+        $('#bx-form-element-videos').show();
+        $('#bx-form-element-video_embed').hide();
+    } else {
+	    $('#bx-form-element-videos').hide();
+        $('#bx-form-element-video_embed').show();
+    }
+};
+
+BxVideosEmbeds.prototype.onNewEmbedCode = function(sCode) {
+	$.get(this._sActionsUrl + 'parse_embed_link/', {code: sCode}, function(sEmbed){
+	    if (!sEmbed.length) {
+            $('#bx-videos-input-embed-preview').fadeOut(function () {
+                $('#bx-videos-input-embed-preview > div.bx-videos-iframe-aspect-ratio').html('');
+            });
+        } else {
+            $('#bx-videos-input-embed-preview > div.bx-videos-iframe-aspect-ratio').html(sEmbed);
+            $('#bx-videos-input-embed-preview').fadeIn();
+        }
+    });
+};
+
+/** @} */

+ 19 - 0
modules/boonex/videos/template/css/embeds.css

@@ -0,0 +1,19 @@
+.bx-videos-iframe-wrapper {
+    max-width: 560px;
+}
+.bx-videos-iframe-aspect-ratio {
+    position: relative;
+    width: 100%;
+    height: 0;
+    padding-bottom: 56.25%;
+}
+.bx-videos-iframe-aspect-ratio iframe {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
+}
+.bx-media-tablet .bx-videos-iframe-aspect-ratio iframe {
+    max-width: unset!important;
+}

+ 3 - 0
modules/boonex/videos/template/entry-video-embed.html

@@ -0,0 +1,3 @@
+<div class="bx-def-margin-top bx-videos-iframe-aspect-ratio">
+    __video__
+</div>

+ 6 - 0
modules/boonex/videos/template/form_field_embed.html

@@ -0,0 +1,6 @@
+__input__
+<div class="bx-videos-iframe-wrapper" id="bx-videos-input-embed-preview" <bx_if:noembed_code>style="display:none;"</bx_if:noembed_code>>
+    <div class="bx-def-margin-top bx-videos-iframe-aspect-ratio">
+        __embed_code__
+    </div>
+</div>

+ 3 - 1
modules/boonex/videos/template/unit.html

@@ -2,7 +2,9 @@
     <bx_if:thumb>
         <div class="bx-base-text-unit-cover">
             <a href="__content_url__"><img class="bx-base-text-unit-cover bx-def-round-corners" alt="__title__" src="__thumb_url__" /></a>
-            <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+            <bx_if:duration_set>
+                <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+            </bx_if:duration_set>
         </div>
     </bx_if:thumb>
     <div class="bx-base-text-unit-text bx-def-padding bx-def-box-sizing">

+ 3 - 1
modules/boonex/videos/template/unit_full.html

@@ -2,7 +2,9 @@
     <bx_if:thumb>
         <div class="bx-base-text-unit-cover">
             <a href="__content_url__"><img class="bx-base-text-unit-cover bx-def-round-corners" alt="__title__" src="__thumb_url__" /></a>
-            <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+            <bx_if:duration_set>
+                <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+            </bx_if:duration_set>
         </div>
     </bx_if:thumb>
     <h1 class="bx-base-text-unit-title bx-def-padding-sec-top"><a href="__content_url__">__title__</a></h1>

+ 3 - 1
modules/boonex/videos/template/unit_gallery.html

@@ -4,7 +4,9 @@
             <a href="__content_url__" title="__summary_attr__">
                 <div class="bx-base-text-unit-thumb bx-def-gal-div-size bx-def-round-corners bx-def-font-middle" style="background-image:url(__gallery_url__)">
                     <div class="bx-base-text-unit-strecher">__strecher__</div>
-                    <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+                    <bx_if:duration_set>
+                        <div class="bx-video-unit-duration bx-def-padding-sec-leftright bx-def-padding-thd-topbottom bx-def-border bx-def-round-corners bx-def-font">__duration__</div>
+                    </bx_if:duration_set>
                 </div>
             </a>
         </bx_if:thumb>