Commit 16527ed5 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(analytics): add OP3 analytics service option + update episode audio url

parent 7fbbd08d
Loading
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -194,6 +194,14 @@ $routes->group('@(:podcastHandle)', static function ($routes): void {
    $routes->get('feed', 'FeedController/$1');
});

// audio routes
$routes->head('audio/@(:podcastHandle)/(:slug)', 'EpisodeController::audio/$1/$2', [
    'as' => 'episode-audio',
],);
$routes->get('audio/@(:podcastHandle)/(:slug)', 'EpisodeController::audio/$1/$2', [
    'as' => 'episode-audio',
],);

// Other pages
$routes->get('/credits', 'CreditsController', [
    'as' => 'credits',
+80 −0
Original line number Diff line number Diff line
@@ -19,12 +19,14 @@ use App\Models\PodcastModel;
use App\Models\PostModel;
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\Response;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;
use Modules\Analytics\AnalyticsTrait;
use Modules\Fediverse\Objects\OrderedCollectionObject;
use Modules\Fediverse\Objects\OrderedCollectionPage;
use Modules\PremiumPodcasts\Models\SubscriptionModel;
use SimpleXMLElement;

class EpisodeController extends BaseController
@@ -329,4 +331,82 @@ class EpisodeController extends BaseController
            ->setHeader('Access-Control-Allow-Origin', '*')
            ->setBody($collection->toJSON());
    }

    public function audio(): RedirectResponse | ResponseInterface
    {
        // check if episode is premium?
        $subscription = null;

        // check if podcast is already unlocked before any token validation
        if ($this->episode->is_premium && ($subscription = service('premium_podcasts')->subscription(
            $this->episode->podcast->handle
        )) === null) {
            // look for token as GET parameter
            if (($token = $this->request->getGet('token')) === null) {
                return $this->response->setStatusCode(401)
                    ->setJSON([
                        'errors' => [
                            'status' => 401,
                            'title' => 'Unauthorized',
                            'detail' => 'Episode is premium, you must provide a token to unlock it.',
                        ],
                    ]);
            }

            // check if there's a valid subscription for the provided token
            if (($subscription = (new SubscriptionModel())->validateSubscription(
                $this->episode->podcast->handle,
                $token
            )) === null) {
                return $this->response->setStatusCode(401, 'Invalid token!')
                    ->setJSON([
                        'errors' => [
                            'status' => 401,
                            'title' => 'Unauthorized',
                            'detail' => 'Invalid token!',
                        ],
                    ]);
            }
        }

        $session = Services::session();
        $session->start();

        $serviceName = '';
        if ($this->request->getGet('_from')) {
            $serviceName = $this->request->getGet('_from');
        } elseif ($session->get('embed_domain') !== null) {
            $serviceName = $session->get('embed_domain');
        } elseif ($session->get('referer') !== null && $session->get('referer') !== '- Direct -') {
            $serviceName = parse_url((string) $session->get('referer'), PHP_URL_HOST);
        }

        $audioFileSize = $this->episode->audio->file_size;
        $audioFileHeaderSize = $this->episode->audio->header_size;
        $audioDuration = $this->episode->audio->duration;

        // bytes_threshold: number of bytes that must be downloaded for an episode to be counted in download analytics
        // - if audio is less than or equal to 60s, then take the audio file_size
        // - if audio is more than 60s, then take the audio file_header_size + 60s
        $bytesThreshold = $audioDuration <= 60
            ? $audioFileSize
            : $audioFileHeaderSize +
                (int) floor((($audioFileSize - $audioFileHeaderSize) / $audioDuration) * 60);

        helper('analytics');
        podcast_hit(
            $this->episode->podcast_id,
            $this->episode->id,
            $bytesThreshold,
            $audioFileSize,
            $audioDuration,
            $this->episode->published_at->getTimestamp(),
            $serviceName,
            $subscription !== null ? $subscription->id : null
        );

        $analyticsConfig = config('Analytics');

        return redirect()->to($analyticsConfig->getAudioUrl($this->episode, $this->request->getGet()));
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -3,14 +3,13 @@
declare(strict_types=1);

/**
 * @copyright  2020 Ad Aures
 * @copyright  2022 Ad Aures
 * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
 * @link       https://castopod.org/
 */

namespace App\Controllers;

use App\Entities\Podcast;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\Controller;
+6 −23
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ use RuntimeException;
 * @property string $title
 * @property int $audio_id
 * @property Audio $audio
 * @property string $audio_analytics_url
 * @property string $audio_url
 * @property string $audio_web_url
 * @property string $audio_opengraph_url
 * @property string|null $description Holds text only description, striped of any markdown or html special characters
@@ -93,7 +93,7 @@ class Episode extends Entity

    protected ?Audio $audio = null;

    protected string $audio_analytics_url;
    protected string $audio_url;

    protected string $audio_web_url;

@@ -335,36 +335,19 @@ class Episode extends Entity
        return $this->chapters;
    }

    public function getAudioAnalyticsUrl(): string
    public function getAudioUrl(): string
    {
        helper('analytics');

        return generate_episode_analytics_url(
            $this->podcast_id,
            $this->id,
            $this->getPodcast()
                ->handle,
            $this->attributes['slug'],
            $this->getAudio()
                ->file_extension,
            $this->getAudio()
                ->duration,
            $this->getAudio()
                ->file_size,
            $this->getAudio()
                ->header_size,
            $this->published_at,
        );
        return url_to('episode-audio', $this->getPodcast()->handle, $this->slug);
    }

    public function getAudioWebUrl(): string
    {
        return $this->getAudioAnalyticsUrl() . '?_from=-+Website+-';
        return $this->getAudioUrl() . '?_from=-+Website+-';
    }

    public function getAudioOpengraphUrl(): string
    {
        return $this->getAudioAnalyticsUrl() . '?_from=-+Open+Graph+-';
        return $this->getAudioUrl() . '?_from=-+Open+Graph+-';
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -286,7 +286,7 @@ if (! function_exists('get_rss_feed')) {

            $enclosure->addAttribute(
                'url',
                $episode->audio_analytics_url . ($enclosureParams === '' ? '' : '?' . $enclosureParams),
                $episode->audio_url . ($enclosureParams === '' ? '' : '?' . $enclosureParams),
            );
            $enclosure->addAttribute('length', (string) $episode->audio->file_size);
            $enclosure->addAttribute('type', $episode->audio->file_mimetype);
Loading