diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 1bc15156..f0dd58b0 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -195,10 +195,10 @@ $routes->group('@(:podcastHandle)', static function ($routes): void { }); // audio routes -$routes->head('audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeController::audio/$1/$2', [ +$routes->head('audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController/$1/$2', [ 'as' => 'episode-audio', ], ); -$routes->get('audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeController::audio/$1/$2', [ +$routes->get('audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController/$1/$2', [ 'as' => 'episode-audio', ], ); diff --git a/app/Controllers/EpisodeAudioController.php b/app/Controllers/EpisodeAudioController.php new file mode 100644 index 00000000..8566c64a --- /dev/null +++ b/app/Controllers/EpisodeAudioController.php @@ -0,0 +1,163 @@ +analyticsConfig = config('Analytics'); + } + + public function _remap(string $method, string ...$params): mixed + { + if (count($params) < 2) { + throw PageNotFoundException::forPageNotFound(); + } + + if ( + ($podcast = (new PodcastModel())->getPodcastByHandle($params[0])) === null + ) { + throw PageNotFoundException::forPageNotFound(); + } + + $this->podcast = $podcast; + + if ( + ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) === null + ) { + throw PageNotFoundException::forPageNotFound(); + } + + $this->episode = $episode; + + unset($params[1]); + unset($params[0]); + + return $this->{$method}(...$params); + } + + public function index(): 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); + + podcast_hit( + $this->episode->podcast_id, + $this->episode->id, + $bytesThreshold, + $audioFileSize, + $audioDuration, + $this->episode->published_at->getTimestamp(), + $serviceName, + $subscription !== null ? $subscription->id : null + ); + + return redirect()->to($this->analyticsConfig->getAudioUrl($this->episode, $this->request->getGet())); + } +} diff --git a/app/Controllers/EpisodeController.php b/app/Controllers/EpisodeController.php index 03551e35..5b59cf55 100644 --- a/app/Controllers/EpisodeController.php +++ b/app/Controllers/EpisodeController.php @@ -19,14 +19,12 @@ 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 @@ -331,82 +329,4 @@ 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())); - } } diff --git a/modules/Analytics/Controllers/EpisodeAnalyticsController.php b/modules/Analytics/Controllers/EpisodeAnalyticsController.php index cd79668e..743ccd2a 100644 --- a/modules/Analytics/Controllers/EpisodeAnalyticsController.php +++ b/modules/Analytics/Controllers/EpisodeAnalyticsController.php @@ -15,45 +15,11 @@ use App\Models\EpisodeModel; use CodeIgniter\Controller; use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\RedirectResponse; -use CodeIgniter\HTTP\RequestInterface; -use CodeIgniter\HTTP\ResponseInterface; -use Modules\Analytics\Config\Analytics; -use Psr\Log\LoggerInterface; class EpisodeAnalyticsController extends Controller { - public mixed $config; - /** - * An array of helpers to be loaded automatically upon class instantiation. These helpers will be available to all - * other controllers that extend Analytics. - * - * @var string[] - */ - protected $helpers = ['analytics']; - - protected Analytics $analyticsConfig; - - /** - * Constructor. - */ - public function initController( - RequestInterface $request, - ResponseInterface $response, - LoggerInterface $logger - ): void { - // Do Not Edit This Line - parent::initController($request, $response, $logger); - - set_user_session_deny_list_ip(); - set_user_session_location(); - set_user_session_player(); - - $this->config = config('Analytics'); - } - - /** - * @deprecated Replaced by EpisodeController::audio method + * @deprecated Replaced by EpisodeAudioController::index method */ public function hit(string $base64EpisodeData, string ...$audioPath): RedirectResponse {