Skip to content
Commits on Source (19)
......@@ -18,11 +18,12 @@ class Transcode extends Cli\Controller implements Interfaces\CliControllerInterf
{
$this->out('TBD');
}
public function exec()
{
$transcoder = new Core\Media\Services\FFMpeg;
$transcoder->setKey($this->getOpt('guid'));
$transcoder->transcode();
$transcoder->setFullHD($this->getOpt('full_hd') ?? false);
$transcoder->onQueue();
}
}
......@@ -36,6 +36,13 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
return Factory::response(['status' => 'error']);
}
if (!in_array($this->getType($entity), ['object:video', 'object:image'], true)) {
return Factory::response([
'status' => 'error',
'message' => 'Entity is not a media entity',
]);
}
switch ($entity->subtype) {
case "video":
// Helpers\Counters::increment($pages[0], 'plays');
......@@ -43,10 +50,12 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
if (isset($pages[1]) && $pages[1] == 'play') {
http_response_code(302);
$res = !empty($_GET['res']) && in_array($_GET['res'], ['360', '720', '1080'], true) ?$_GET['res'] : '360';
if ($entity->subtype == 'audio') {
\forward($entity->getSourceUrl('128.mp3'));
} else {
\forward($entity->getSourceUrl('360.mp4'));
\forward($entity->getSourceUrl("{$res}.mp4"));
}
exit;
......@@ -58,8 +67,11 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$response = $entities[0];
$response['transcodes'] = [
'360.mp4' => $entity->getSourceUrl('360.mp4'),
'720.mp4' => $entity->getSourceUrl('720.mp4')
'720.mp4' => $entity->getSourceUrl('720.mp4'),
];
if ($entity->getFlag('full_hd')) {
$response['transcodes']['1080.mp4'] = $entity->getSourceUrl('1080.mp4');
}
}
if (method_exists($entity, 'getWireThreshold')) {
......@@ -158,6 +170,7 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$body = $req['body'];
fwrite($fp, $body);
$video->access_id = 0;
$video->patch(['full_hd', Core\Session::getLoggedinUser()->isPro()]);
$video->upload($tmpFilename);
$guid = $video->save();
fclose($fp);
......@@ -258,7 +271,8 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
'access_id' => 0,
'owner_guid' => $user->guid,
'hidden' => $container_guid !== null,
'container_guid' => $container_guid
'container_guid' => $container_guid,
'full_hd' => $user->isPro(),
]);
$assets = Core\Media\AssetsFactory::build($entity);
......@@ -267,7 +281,7 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$entity->setAssets($assets->upload($media, $data));
// Save initial entity
$success = $save
->setEntity($entity)
->save(true);
......@@ -356,4 +370,9 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
return $response;
}
private function getType($entity): string
{
return $entity->subtype ? "{$entity->type}:{$entity->subtype}" : $entity->type;
}
}
......@@ -358,7 +358,8 @@ class newsfeed implements Interfaces\Api
->setCustom('video', [
'thumbnail_src' => $embeded->getIconUrl(),
'guid' => $embeded->guid,
'mature' => $embeded instanceof Flaggable ? $embeded->getFlag('mature') : false
'mature' => $embeded instanceof Flaggable ? $embeded->getFlag('mature') : false,
'full_hd' => $embeded->getFlag('full_hd') ?? false,
])
->setTitle($embeded->title)
->setBlurb($embeded->description)
......@@ -525,7 +526,7 @@ class newsfeed implements Interfaces\Api
]);
}
}
$save->setEntity($activity)
->save();
......
......@@ -10,6 +10,8 @@ namespace Minds\Controllers\api\v2\media;
use Minds\Api\Factory;
use Minds\Core\Di\Di;
use Minds\Core\Media\ClientUpload\Manager;
use Minds\Core\Session;
use Minds\Interfaces;
use Minds\Core\Media\ClientUpload\ClientUploadLease;
......@@ -38,6 +40,7 @@ class upload implements Interfaces\Api
*/
public function put($pages)
{
/** @var Manager $manager */
$manager = Di::_()->get("Media\ClientUpload\Manager");
switch ($pages[0]) {
case 'prepare':
......@@ -55,7 +58,9 @@ class upload implements Interfaces\Api
$lease->setGuid($guid)
->setMediaType($mediaType);
$manager->complete($lease);
$manager
->setFullHD(Session::getLoggedinUser()->isPro())
->complete($lease);
break;
}
return Factory::response([]);
......
......@@ -8,6 +8,7 @@ namespace Minds\Controllers\api\v2\pro;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain as ProDomain;
use Minds\Core\Pro\Manager;
use Minds\Core\Session;
use Minds\Entities\User;
......@@ -81,6 +82,18 @@ class settings implements Interfaces\Api
]);
}
if (isset($_POST['domain'])) {
/** @var ProDomain $proDomain */
$proDomain = Di::_()->get('Pro\Domain');
if (!$proDomain->isAvailable($_POST['domain'], (string) $user->guid)) {
return Factory::response([
'status' => 'error',
'message' => 'This domain is taken',
]);
}
}
try {
$success = $manager->set($_POST);
......
<?php
/**
* settings assets
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro\settings;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Manager;
use Minds\Core\Pro\Assets\Manager as AssetsManager;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
use Zend\Diactoros\ServerRequest;
class assets implements Interfaces\Api
{
/** @var ServerRequest */
public $request;
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
*/
public function get($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
* @throws Exception
*/
public function post($pages)
{
$type = $pages[0] ?? null;
// Check and validate user
$user = Session::getLoggedinUser();
if (isset($pages[1]) && $pages[1]) {
if (!Session::isAdmin()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not authorized',
]);
}
$user = new User($pages[1]);
}
// Check uploaded file
/** @var \Zend\Diactoros\UploadedFile[] $files */
$files = $this->request->getUploadedFiles();
if (!$files || !isset($files['file'])) {
return Factory::response([
'status' => 'error',
'message' => 'Missing file',
]);
}
$file = $files['file'];
if ($file->getError()) {
return Factory::response([
'status' => 'error',
'message' => sprintf('Error %s when uploading file', $files['file']->getError()),
]);
}
// Get Pro managers
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser($user)
->setActor(Session::getLoggedinUser());
if (!$manager->isActive()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not Pro',
]);
}
/** @var AssetsManager $assetsManager */
$assetsManager = Di::_()->get('Pro\Assets\Manager');
$assetsManager
->setType($type)
->setUser($user)
->setActor(Session::getLoggedinUser());
try {
$success = $assetsManager
->set($file);
if (!$success) {
throw new Exception(sprintf("Cannot save Pro %s asset", $type));
}
} catch (\Exception $e) {
return Factory::response([
'status' => 'error',
'message' => $e->getMessage(),
]);
}
return Factory::response([]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public function delete($pages)
{
return Factory::response([]);
}
}
<?php
/**
* domain
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro\settings;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain as ProDomain;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
class domain implements Interfaces\Api
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws Exception
*/
public function get($pages)
{
$user = Session::getLoggedinUser();
if (isset($pages[0]) && $pages[0]) {
if (!Session::isAdmin()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not authorized',
]);
}
$user = new User($pages[0]);
}
/** @var ProDomain $proDomain */
$proDomain = Di::_()->get('Pro\Domain');
return Factory::response([
'isValid' => $proDomain->isAvailable($_GET['domain'], (string) $user->guid)
]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
*/
public function post($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public function delete($pages)
{
return Factory::response([]);
}
}
<?php
namespace Minds\Controllers\Api\v2\settings;
namespace Minds\Controllers\api\v2\settings;
use Minds\Api\Factory;
use Minds\Core;
......
<?php
namespace Minds\Controllers\Api\v2\settings;
namespace Minds\Controllers\api\v2\settings;
use Minds\Api\Factory;
use Minds\Core;
......
<?php
/**
* pro
* @author edgebal
*/
namespace Minds\Controllers\fs\v1;
use Minds\Core\Pro\Assets\Asset;
use Minds\Interfaces;
class pro implements Interfaces\FS
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws \IOException
* @throws \InvalidParameterException
* @throws \Exception
*/
public function get($pages)
{
$asset = new Asset();
$asset
->setType($pages[1] ?? null)
->setUserGuid($pages[0] ?? null);
$file = $asset->getFile();
$file->open('read');
$contents = $file->read();
header(sprintf("Content-Type: %s", $asset->getMimeType()));
header(sprintf("Expires: %s", date('r', time() + 864000)));
header('Pragma: public');
header('Cache-Control: public');
echo $contents;
exit;
}
}
<?php
/**
* Engagement Dashboard
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method EngagementDashboard setTimespanId(string $timespanId)
* @method EngagementDashboard setFilterIds(array $filtersIds)
* @method EngagementDashboard setUser(User $user)
*/
class EngagementDashboard implements DashboardInterface
{
use MagicAttributes;
/** @var string */
private $timespanId = '30d';
/** @var string[] */
private $filterIds = [ 'platform::browser' ];
/** @var string */
private $metricId = 'votes_up';
/** @var Timespans\TimespansCollection */
private $timespansCollection;
/** @var Metrics\MetricsCollection */
private $metricsCollection;
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var User */
private $user;
public function __construct(
$timespansCollection = null,
$metricsCollection = null,
$filtersCollection = null
) {
$this->timespansCollection = $timespansCollection ?? new Timespans\TimespansCollection();
$this->metricsCollection = $metricsCollection ?? new Metrics\MetricsCollection();
$this->filtersCollection = $filtersCollection ?? new Filters\FiltersCollection();
}
/**
* Build the dashboard
* @return self
*/
public function build(): self
{
$this->timespansCollection
->setSelectedId($this->timespanId)
->addTimespans(
new Timespans\TodayTimespan(),
new Timespans\_30dTimespan(),
new Timespans\_1yTimespan(),
new Timespans\MtdTimespan(),
new Timespans\YtdTimespan()
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->setUser($this->user)
->addFilters(
new Filters\ChannelFilter()
);
$this->metricsCollection
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\Engagement\VotesUpMetric(),
new Metrics\Engagement\CommentsMetric(),
new Metrics\Engagement\RemindsMetric(),
new Metrics\Engagement\SubscribersMetric()
)
->build();
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$this->build();
return [
'category' => 'engagement',
'label' => 'Engagement',
'description' => '',
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
'metrics' => $this->metricsCollection->export(),
'filter' => $this->filtersCollection->getSelectedIds(),
'filters' => $this->filtersCollection->export(),
];
}
}
......@@ -7,6 +7,7 @@ class Manager
'traffic' => TrafficDashboard::class,
'trending' => TrendingDashboard::class,
'earnings' => EarningsDashboard::class,
'engagement' => EngagementDashboard::class,
];
/**
......
......@@ -39,7 +39,7 @@ class ActiveUsersMetric extends AbstractMetric
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$comparisonTsMs = strtotime("midnight -{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs();
// Field name to use for the aggregation
......@@ -76,10 +76,14 @@ class ActiveUsersMetric extends AbstractMetric
],
];
$must[]['exists'] = [
'field' => 'active::total',
];
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......@@ -145,6 +149,10 @@ class ActiveUsersMetric extends AbstractMetric
'entity_urn' => 'urn:metric:global'
];
$must[]['exists'] = [
'field' => 'active::total',
];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
......
......@@ -54,7 +54,7 @@ abstract class AbstractEarningsMetric extends AbstractMetric
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......@@ -66,6 +66,12 @@ abstract class AbstractEarningsMetric extends AbstractMetric
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
......@@ -127,6 +133,12 @@ abstract class AbstractEarningsMetric extends AbstractMetric
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
......
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
abstract class AbstractEngagementMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = '';
/** @var string */
protected $label = '';
/** @var string */
protected $description = '';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $unit = 'number';
/** @var string */
protected $aggField = '';
public function __construct($es = null)
{
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* Build the metrics
* @return self
*/
public function buildSummary(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$comparisonTsMs = strtotime("midnight -{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs();
$values = [];
foreach ([ 'value' => $currentTsMs, 'comparison' => $comparisonTsMs ] as $key => $tsMs) {
$must = [];
$maxTs = strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000);
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lt' => $maxTs * 1000,
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
$indexes = implode(',', [
'minds-entitycentric-' . date('m-Y', $tsMs / 1000),
'minds-entitycentric-' . date('m-Y', $maxTs),
]);
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'sum' => [
'field' => $this->aggField,
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$values[$key] = $response['aggregations']['1']['value'];
}
$this->summary = new MetricSummary();
$this->summary
->setValue($values['value'])
->setComparisonValue($values['comparison'])
->setComparisonInterval($timespan->getComparisonInterval())
->setComparisonPositivity(true);
return $this;
}
/**
* Build a visualisation for the metric
* @return self
*/
public function buildVisualisation(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $timespan->getFromTsMs(),
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getInterval(),
'min_doc_count' => 0,
'extended_bounds' => [
'min' => $timespan->getFromTsMs(),
'max' => time() * 1000,
],
],
'aggs' => [
'2' => [
'sum' => [
'field' => $this->aggField,
],
],
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$buckets[] = [
'key' => $bucket['key'],
'date' => date('c', $bucket['key'] / 1000),
'value' => $bucket['2']['value']
];
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXLabel('Date')
->setYLabel('Count')
->setBuckets($buckets);
return $this;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class CommentsMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'comments';
/** @var string */
protected $label = 'Comments';
/** @var string */
protected $description = "Number of comments you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'comment::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class RemindsMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'reminds';
/** @var string */
protected $label = 'Reminds';
/** @var string */
protected $description = "Number of reminds you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'remind::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class SubscribersMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'subscribers';
/** @var string */
protected $label = 'Subscribes';
/** @var string */
protected $description = "Number of subscribers your channel has gained";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'subscribe::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class VotesUpMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'votes_up';
/** @var string */
protected $label = 'Votes up';
/** @var string */
protected $description = "Number of votes up you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'vote:up::total';
}
......@@ -54,7 +54,7 @@ class SignupsMetric extends AbstractMetric
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......