Mux export integration

Create command summit:presentation-materials-mux-assets {summit_id} {mounting_folder?} {event_id?}

Change-Id: If1ac9b315ea7ede64109e21d0c9dd0468b6c7e2c
Signed-off-by: smarcet <smarcet@gmail.com>
This commit is contained in:
smarcet 2020-10-09 18:37:59 -03:00
parent 9fb62d7dcc
commit 8b7e68d3bd
20 changed files with 576 additions and 10 deletions

View File

@ -167,4 +167,7 @@ RABBITMQ_SSL_LOCALCERT=/certs/rabbit/client-cert-osf.pem
RABBITMQ_SSL_LOCALKEY=/certs/rabbit/client-key-osf.pem
RABBITMQ_SSL_VERIFY_PEER=false
DROPBOX_ACCESS_TOKEN=
DROPBOX_ACCESS_TOKEN=
MUX_TOKEN_ID=
MUX_TOKEN_SECRET=

View File

@ -27,5 +27,5 @@ interface ITransactionService
*
* @throws \Exception
*/
public function transaction(Closure $callback, int $isolationLevel);
public function transaction(Closure $callback, int $isolationLevel = 2);
}

View File

@ -0,0 +1,72 @@
<?php namespace App\Console\Commands;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Services\Model\IPresentationVideoMediaUploadProcessor;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
/**
* Class PresentationMaterialsCreateMUXAssetsCommand
* @package App\Console\Commands
*/
final class PresentationMaterialsCreateMUXAssetsCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'summit:presentation-materials-mux-assets';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'summit:presentation-materials-mux-assets {summit_id} {mounting_folder?} {event_id?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Process Presentation Videos and ingest on MUX';
/**
* @param IPresentationVideoMediaUploadProcessor $service
*/
public function handle(IPresentationVideoMediaUploadProcessor $service)
{
$summit_id = $this->argument('summit_id');
$event_id = $this->argument('event_id');
if(empty($summit_id))
throw new \InvalidArgumentException("summit_id is required");
$mountingFolder = $this->argument('mounting_folder');
if(empty($mountingFolder))
$mountingFolder = Config::get('mediaupload.mounting_folder');
Log::debug(sprintf("starting to process published presentations for summit id %s mountingFolder %s event id %s", $summit_id, $mountingFolder, $event_id));
$this->info(sprintf("starting to process published presentations for summit id %s mountingFolder %s event id %s", $summit_id, $mountingFolder, $event_id));
if(empty($event_id)) {
$service->processPublishedPresentationFor(intval($summit_id), $mountingFolder);
return;
}
$service->processEvent(intval($event_id), $mountingFolder);
}
}

View File

@ -11,6 +11,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Console\Commands\PresentationMaterialsCreateMUXAssetsCommand;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\App;
@ -41,6 +43,7 @@ class Kernel extends ConsoleKernel
\App\Console\Commands\SummitForwardXDays::class,
\App\Console\Commands\SummitEmailFlowEventSeederCommand::class,
\App\Console\Commands\SummitEmailFlowTypeSeederCommand::class,
\App\Console\Commands\PresentationMaterialsCreateMUXAssetsCommand::class,
];
/**

View File

@ -43,7 +43,7 @@ final class AdminPresentationCSVSerializer extends AdminPresentationSerializer
$values['video'] = '';
$values['public_video'] = '';
foreach ($presentation->getMediaUploads() as $mediaUpload) {
if(str_contains(strtolower($mediaUpload->getMediaUploadType()->getType()->getName()), "video")) {
if($mediaUpload->getMediaUploadType()->isVideo()) {
$media_upload_csv = SerializerRegistry::getInstance()->getSerializer($mediaUpload, $serializerType)->serialize(AbstractSerializer::filterExpandByPrefix($expand, 'media_uploads'));;
if(!isset($media_upload_csv['private_url']) || !isset($media_upload_csv['filename'])){
Log::warning(sprintf("AdminPresentationCSVSerializer::serialize can not process media upload %s", json_encode($media_upload_csv)));

View File

@ -15,6 +15,7 @@
use App\Models\Utils\IStorageTypesConstants;
use Doctrine\ORM\Mapping AS ORM;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
/**
* @ORM\Entity
@ -107,18 +108,24 @@ class PresentationMediaUpload extends PresentationMaterial
/**
* @param string $storageType
* @param string|null $mountingFolder
* @return string
*/
public function getRelativePath(string $storageType = IStorageTypesConstants::PublicType):string {
return sprintf('%s/%s', $this->getPath($storageType), $this->getFilename());
public function getRelativePath(string $storageType = IStorageTypesConstants::PublicType, ?string $mountingFolder = null):string {
return sprintf('%s/%s', $this->getPath($storageType, $mountingFolder), $this->getFilename());
}
/**
* @param string $storageType
* @param string|null $mountingFolder
* @return string
*/
public function getPath(string $storageType = IStorageTypesConstants::PublicType): string {
$mountingFolder = Config::get('mediaupload.mounting_folder');
public function getPath(string $storageType = IStorageTypesConstants::PublicType, ?string $mountingFolder = null): string {
if(empty($mountingFolder))
$mountingFolder = Config::get('mediaupload.mounting_folder');
Log::debug(sprintf("PresentationMediaUpload::getPath storageType %s mountingFolder %s", $storageType, $mountingFolder));
$summit = $this->getPresentation()->getSummit();
$presentation = $this->getPresentation();
$format = $storageType == IStorageTypesConstants::PublicType ? '%s/%s/%s': '%s/'.IStorageTypesConstants::PrivateType.'/%s/%s';

View File

@ -217,6 +217,18 @@ class SummitEvent extends SilverstripeBaseModel
*/
protected $streaming_url;
/**
* @ORM\Column(name="MuxAssetID", type="string")
* @var string
*/
protected $mux_asset_id;
/**
* @ORM\Column(name="MuxPlaybackID", type="string")
* @var string
*/
protected $mux_playback_id;
/**
* @ORM\Column(name="EtherpadLink", type="string")
* @var string
@ -1281,4 +1293,37 @@ class SummitEvent extends SilverstripeBaseModel
}
return null;
}
/**
* @return string
*/
public function getMuxAssetId(): ?string
{
return $this->mux_asset_id;
}
/**
* @param string $mux_asset_id
*/
public function setMuxAssetId(string $mux_asset_id): void
{
$this->mux_asset_id = $mux_asset_id;
}
/**
* @return string
*/
public function getMuxPlaybackId(): ?string
{
return $this->mux_playback_id;
}
/**
* @param string $mux_playback_id
*/
public function setMuxPlaybackId(string $mux_playback_id): void
{
$this->mux_playback_id = $mux_playback_id;
}
}

View File

@ -272,4 +272,8 @@ class SummitMediaUploadType extends SilverstripeBaseModel
return ($this->private_storage_type != IStorageTypesConstants::None || $this->public_storage_type != IStorageTypesConstants::None);
}
public function isVideo():bool{
return str_contains(strtolower($this->getType()->getName()), "video");
}
}

View File

@ -65,4 +65,10 @@ interface ISummitEventRepository extends IBaseRepository
* @return mixed
*/
public function getPublishedEventsBySummitNotInExternalIds(Summit $summit, array $external_ids);
/**
* @param int $summit_id,
* @return array
*/
public function getPublishedEventsIdsBySummit(int $summit_id):array;
}

View File

@ -369,6 +369,24 @@ final class DoctrineSummitEventRepository
return $query->getQuery()->getResult();
}
/**
* @param int $summit_id,
* @return array
*/
public function getPublishedEventsIdsBySummit(int $summit_id):array
{
$query = $this->getEntityManager()
->createQueryBuilder()
->select("e.id")
->from($this->getBaseEntity(), "e")
->join('e.summit', 's', Join::WITH, " s.id = :summit_id")
->where('e.published = 1')
->setParameter('summit_id', $summit_id);
$res = $query->getQuery()->getArrayResult();
return array_column($res, 'id');
}
/**
* @param PagingInfo $paging_info
* @param Filter|null $filter

View File

@ -0,0 +1,52 @@
<?php namespace App\Services\FileSystem\Dropbox;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Illuminate\Support\Facades\Log;
use Spatie\FlysystemDropbox\DropboxAdapter as BaseDropboxAdapter;
use Spatie\Dropbox\Exceptions\BadRequest as BadRequestException;
use Exception;
/**
* Class DropboxAdapter
* @package App\Services\FileSystem\Dropbox
*/
final class DropboxAdapter extends BaseDropboxAdapter
{
public function getUrl(string $path): string
{
$client = $this->client;
try {
// default visibility is RequestedVisibility.public.
$res = $client->createSharedLinkWithSettings($path);
return $res['url'];
}
catch (BadRequestException $ex){
if($ex->dropboxCode === 'shared_link_already_exists')
{
try {
$res = $client->listSharedLinks($path);
foreach ($res as $entry) {
if($entry['path_lower'] === strtolower($path) )
return $entry['url'];
}
}
catch (Exception $ex){
Log::warning($ex);
}
}
}
catch (Exception $ex){
Log::warning($ex);
}
return '#';
}
}

View File

@ -15,7 +15,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use App\Services\FileSystem\Dropbox\DropboxAdapter;
/**
* Class DropboxServiceProvider
* @package App\Services\FileSystem\Dropbox

View File

@ -0,0 +1,35 @@
<?php namespace App\Services\Model;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* Interface IPresentationVideoMediaUploadProcessor
* @package App\Services\Model
*/
interface IPresentationVideoMediaUploadProcessor
{
/**
* @param int $summit_id
* @param string|null $mountingFolder
* @return int
*/
public function processPublishedPresentationFor(int $summit_id, ?string $mountingFolder = null):int;
/**
* @param int $event_id
* @param string|null $mountingFolder
* @return bool
*/
public function processEvent(int $event_id, ?string $mountingFolder):bool;
}

View File

@ -0,0 +1,181 @@
<?php namespace App\Services\Model\Imp;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Utils\IStorageTypesConstants;
use App\Services\Filesystem\FileDownloadStrategyFactory;
use App\Services\Model\AbstractService;
use App\Services\Model\IPresentationVideoMediaUploadProcessor;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use libs\utils\ITransactionService;
use models\summit\ISummitEventRepository;
use models\summit\ISummitRepository;
use models\summit\Presentation;
use MuxPhp\Configuration as MuxConfig;
use MuxPhp\Api\AssetsApi as MuxAssetApi;
use GuzzleHttp\Client as GuzzleHttpClient;
use MuxPhp\Models\InputSettings as MuxInputSettings;
use MuxPhp\Models\CreateAssetRequest as MuxCreateAssetRequest;
use MuxPhp\Models\PlaybackPolicy as MuxPlaybackPolicy;
/**
* Class PresentationVideoMediaUploadProcessor
* @package App\Services\Model\Imp
*/
final class PresentationVideoMediaUploadProcessor
extends AbstractService
implements IPresentationVideoMediaUploadProcessor
{
/**
* @var ISummitRepository
*/
private $summit_repository;
/**
* @var ISummitEventRepository
*/
private $event_repository;
/**
* @var MuxAssetApi
*/
private $assets_api;
public function __construct
(
ISummitRepository $summit_repository,
ISummitEventRepository $event_repository,
ITransactionService $tx_service
)
{
parent::__construct($tx_service);
$this->summit_repository = $summit_repository;
$this->event_repository = $event_repository;
$mux_user = Config::get("mux.user", null);
$mux_password = Config::get("mux.password", null);
if(empty($mux_user)){
throw new \InvalidArgumentException("missing setting mux.user");
}
if(empty($mux_password)){
throw new \InvalidArgumentException("missing setting mux.password");
}
// Authentication Setup
$config = MuxConfig::getDefaultConfiguration()
->setUsername($mux_user)
->setPassword($mux_password);
// API Client Initialization
$this->assets_api = new MuxAssetApi(
new GuzzleHttpClient,
$config
);
}
/**
* @param int $summit_id
* @param string|null $mountingFolder
* @return int
* @throws \Exception
*/
public function processPublishedPresentationFor(int $summit_id, ?string $mountingFolder = null): int
{
Log::debug(sprintf("PresentationVideoMediaUploadProcessor::processPublishedPresentationFor summit id %s mountingFolder %s", $summit_id, $mountingFolder));
$event_ids = $this->tx_service->transaction(function() use($summit_id){
return $this->event_repository->getPublishedEventsIdsBySummit($summit_id);
});
foreach($event_ids as $event_id){
Log::warning(sprintf("PresentationVideoMediaUploadProcessor::processPublishedPresentationFor processing event %s", $event_id));
$this->processEvent(intval($event_id), $mountingFolder);
}
return count($event_ids);
}
/**
* @param int $event_id
* @param string|null $mountingFolder
* @return bool
*/
public function processEvent(int $event_id, ?string $mountingFolder):bool{
try {
return $this->tx_service->transaction(function () use ($event_id, $mountingFolder) {
try {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
if (is_null($event) || !$event instanceof Presentation) {
Log::warning(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s not found", $event_id));
return false;
}
if(!$event->isPublished()){
Log::warning(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s not published", $event_id));
return false;
}
Log::debug(sprintf("PresentationVideoMediaUploadProcessor::processEvent processing event %s (%s)", $event->getTitle(), $event_id));
if(!empty($event->getMuxAssetId())){
Log::warning(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s already has assigned an asset id %s", $event_id, $event->getMuxAssetId()));
return false;
}
$has_video = false;
foreach($event->getMediaUploads() as $mediaUpload){
if($mediaUpload->getMediaUploadType()->isVideo()){
if($has_video){
Log::warning(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s processing media upload %s (%s) already has a video processed!.", $event_id, $mediaUpload->getId(), $mediaUpload->getFilename()));
continue;
}
Log::debug(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s processing media upload %s", $event_id, $mediaUpload->getId()));
$has_video = true;
$strategy = FileDownloadStrategyFactory::build($mediaUpload->getMediaUploadType()->getPrivateStorageType());
if (!is_null($strategy)) {
$assetUrl = $strategy->getUrl($mediaUpload->getRelativePath(IStorageTypesConstants::PrivateType, $mountingFolder));
Log::debug(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s processing media upload %s got asset url %s", $event_id, $mediaUpload->getId(), $assetUrl));
// Create Asset Request
$input = new MuxInputSettings(["url" => $assetUrl]);
$createAssetRequest = new MuxCreateAssetRequest(["input" => $input, "playback_policy" => [MuxPlaybackPolicy::PUBLIC_PLAYBACK_POLICY] ]);
// Ingest
$result = $this->assets_api->createAsset($createAssetRequest);
// Print URL
$playback_id = $result->getData()->getPlaybackIds()[0]->getId();
$streaming_url = sprintf("https://stream.mux.com/%s.m3u8", $playback_id);
$asset_id = $result->getData()->getId();
Log::debug(sprintf("PresentationVideoMediaUploadProcessor::processEvent event %s Playback URL: %s assset id %s", $event_id, $streaming_url, $asset_id));
$event->setStreamingUrl($streaming_url);
$event->setMuxAssetId($asset_id);
$event->setMuxPlaybackId($playback_id);
}
}
}
} catch (\Exception $ex) {
Log::warning($ex);
throw $ex;
}
return true;
});
}
catch (\Exception $ex) {
Log::error($ex);
return false;
}
}
}

View File

@ -111,6 +111,8 @@ use App\Services\Model\ICalendarSyncWorkRequestPreProcessor;
use App\Services\Model\IMemberActionsCalendarSyncProcessingService;
use App\Services\Model\AdminActionsCalendarSyncProcessingService;
use App\Services\Model\IAdminActionsCalendarSyncProcessingService;
use App\Services\Model\IPresentationVideoMediaUploadProcessor;
use App\Services\Model\Imp\PresentationVideoMediaUploadProcessor;
/***
* Class ModelServicesProvider
* @package services
@ -377,6 +379,12 @@ final class ModelServicesProvider extends ServiceProvider
ISummitMediaUploadTypeService::class,
SummitMediaUploadTypeService::class
);
App::singleton
(
IPresentationVideoMediaUploadProcessor::class,
PresentationVideoMediaUploadProcessor::class
);
}
/**
@ -436,6 +444,7 @@ final class ModelServicesProvider extends ServiceProvider
ISummitAdministratorPermissionGroupService::class,
ISummitMediaFileTypeService::class,
ISummitMediaUploadTypeService::class,
IPresentationVideoMediaUploadProcessor::class
];
}
}

View File

@ -35,6 +35,7 @@
"laravel/tinker": "^1.0",
"league/csv": "^9.6",
"league/oauth2-client": "^2.4",
"muxinc/mux-php": "^0.5.0",
"php-amqplib/php-amqplib": "^2.11",
"php-opencloud/openstack": "dev-master",
"predis/predis": "1.0.*",

57
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "a441f3fcf95ec732067dc7b61c451a5b",
"content-hash": "2d35492694cc690aadba8344d827c267",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -2876,6 +2876,60 @@
],
"time": "2020-05-22T07:31:27+00:00"
},
{
"name": "muxinc/mux-php",
"version": "0.5.0",
"source": {
"type": "git",
"url": "https://github.com/muxinc/mux-php.git",
"reference": "b9894ee7003f2caf2e74366c4dfb19736832077f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/muxinc/mux-php/zipball/b9894ee7003f2caf2e74366c4dfb19736832077f",
"reference": "b9894ee7003f2caf2e74366c4dfb19736832077f",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/guzzle": "^6.2",
"php": ">=7.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.12",
"squizlabs/php_codesniffer": "~2.6"
},
"type": "library",
"autoload": {
"psr-4": {
"MuxPhp\\": "MuxPhp/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mux SDK team",
"email": "sdks@mux.com",
"homepage": "https://mux.com"
}
],
"description": "Official Mux API wrapper for PHP projects, supporting both Mux Data and Mux Video. Not familiar with Mux? Check out https://mux.com/ for more information.",
"homepage": "https://mux.com",
"keywords": [
"api",
"php",
"rest",
"sdk",
"streaming",
"video"
],
"time": "2020-09-10T18:53:57+00:00"
},
{
"name": "nesbot/carbon",
"version": "1.26.6",
@ -7320,6 +7374,7 @@
"keywords": [
"tokenizer"
],
"abandoned": true,
"time": "2019-09-17T06:23:10+00:00"
},
{

19
config/mux.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
return [
'user' => env('MUX_TOKEN_ID', null),
'password' => env('MUX_TOKEN_SECRET', null),
];

View File

@ -39,6 +39,11 @@ class Version20200928132323 extends AbstractMigration
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
if($schema->hasTable("PresentationMediaUpload") && $builder->hasColumn("PresentationMediaUpload","LegacyPathFormat") ) {
$builder->table('PresentationMediaUpload', function (Table $table) {
$table->dropColumn('LegacyPathFormat');
});
}
}
}

View File

@ -0,0 +1,51 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Builder;
use LaravelDoctrine\Migrations\Schema\Table;
/**
* Class Version20201008203936
* @package Database\Migrations\Model
*/
final class Version20201008203936 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if($schema->hasTable("SummitEvent") && !$builder->hasColumn("SummitEvent","MuxPlaybackID") ) {
$builder->table('SummitEvent', function (Table $table) {
$table->text('MuxPlaybackID')->setDefault(null)->setNotnull(false);
$table->text('MuxAssetID')->setDefault(null)->setNotnull(false);
});
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
if($schema->hasTable("SummitEvent") && $builder->hasColumn("SummitEvent","MuxPlaybackID") ) {
$builder->table('SummitEvent', function (Table $table) {
$table->dropColumn('MuxPlaybackID');
$table->dropColumn('MuxAssetID');
});
}
}
}