mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
feat(episodes): replace all audio file URL parameters with base64 encoded data
This commit is contained in:
parent
391c349daa
commit
e1f65cd3b5
@ -31,6 +31,7 @@ $routes->setAutoRoute(false);
|
||||
|
||||
$routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,191}');
|
||||
$routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}');
|
||||
$routes->addPlaceholder('base64', '[A-Za-z0-9\.\_]+\-{0,2}');
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------
|
||||
@ -59,14 +60,10 @@ $routes->group(config('App')->installGateway, function ($routes) {
|
||||
]);
|
||||
});
|
||||
|
||||
// Route for podcast audio file analytics (/audio/podcast_id/episode_id/bytes_threshold/filesize/duration/podcast_folder/filename.mp3)
|
||||
$routes->add(
|
||||
'audio/(:num)/(:num)/(:num)/(:num)/(:num)/(:any)',
|
||||
'Analytics::hit/$1/$2/$3/$4/$5/$6',
|
||||
[
|
||||
'as' => 'analytics_hit',
|
||||
]
|
||||
);
|
||||
// Route for podcast audio file analytics (/audio/pack(podcast_id,episode_id,bytes_threshold,filesize,duration,date)/podcast_folder/filename.mp3)
|
||||
$routes->add('audio/(:base64)/(:any)', 'Analytics::hit/$1/$2', [
|
||||
'as' => 'analytics_hit',
|
||||
]);
|
||||
|
||||
// Show the Unknown UserAgents
|
||||
$routes->get('.well-known/unknown-useragents', 'UnknownUserAgents');
|
||||
|
@ -46,24 +46,24 @@ class Analytics extends Controller
|
||||
}
|
||||
|
||||
// Add one hit to this episode:
|
||||
public function hit(
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
$bytesThreshold,
|
||||
$fileSize,
|
||||
$duration,
|
||||
...$filename
|
||||
) {
|
||||
helper('media');
|
||||
public function hit($base64EpisodeData, ...$filename)
|
||||
{
|
||||
helper('media', 'analytics');
|
||||
|
||||
$serviceName = isset($_GET['_from']) ? $_GET['_from'] : '';
|
||||
|
||||
$episodeData = unpack(
|
||||
'IpodcastId/IepisodeId/IbytesThreshold/IfileSize/Iduration/IpublicationDate',
|
||||
base64_url_decode($base64EpisodeData)
|
||||
);
|
||||
|
||||
podcast_hit(
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
$bytesThreshold,
|
||||
$fileSize,
|
||||
$duration,
|
||||
$episodeData['podcastId'],
|
||||
$episodeData['episodeId'],
|
||||
$episodeData['bytesThreshold'],
|
||||
$episodeData['fileSize'],
|
||||
$episodeData['duration'],
|
||||
$episodeData['publicationDate'],
|
||||
$serviceName
|
||||
);
|
||||
return redirect()->to(media_base_url($filename));
|
||||
|
@ -35,6 +35,7 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` (
|
||||
IN `p_bot` TINYINT(1) UNSIGNED,
|
||||
IN `p_filesize` INT UNSIGNED,
|
||||
IN `p_duration` INT UNSIGNED,
|
||||
IN `p_age` INT UNSIGNED,
|
||||
IN `p_new_listener` TINYINT(1) UNSIGNED
|
||||
) MODIFIES SQL DATA
|
||||
DETERMINISTIC
|
||||
@ -57,8 +58,8 @@ IF NOT `p_bot` THEN
|
||||
INSERT INTO `{$prefix}analytics_podcasts_by_hour`(`podcast_id`, `date`, `hour`)
|
||||
VALUES (p_podcast_id, @current_date, @current_hour)
|
||||
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
|
||||
INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`)
|
||||
SELECT p_podcast_id, p_episode_id, @current_date, datediff(@current_datetime,`published_at`) FROM `{$prefix}episodes` WHERE `id`= p_episode_id
|
||||
INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`)
|
||||
VALUES (p_podcast_id, p_episode_id, @current_date, p_age)
|
||||
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
|
||||
INSERT INTO `{$prefix}analytics_podcasts_by_country`(`podcast_id`, `country_code`, `date`)
|
||||
VALUES (p_podcast_id, p_country_code, @current_date)
|
||||
|
@ -179,25 +179,37 @@ class Episode extends Entity
|
||||
|
||||
public function getEnclosureUrl()
|
||||
{
|
||||
helper('analytics');
|
||||
|
||||
return base_url(
|
||||
route_to(
|
||||
'analytics_hit',
|
||||
$this->attributes['podcast_id'],
|
||||
$this->attributes['id'],
|
||||
// bytes_threshold: number of bytes that must be downloaded for an episode to be counted in download analytics
|
||||
// - if file is shorter than 60sec, then it's enclosure_filesize
|
||||
// - if file is longer than 60 seconds then it's enclosure_headersize + 60 seconds
|
||||
$this->attributes['enclosure_duration'] <= 60
|
||||
? $this->attributes['enclosure_filesize']
|
||||
: $this->attributes['enclosure_headersize'] +
|
||||
floor(
|
||||
(($this->attributes['enclosure_filesize'] -
|
||||
$this->attributes['enclosure_headersize']) /
|
||||
$this->attributes['enclosure_duration']) *
|
||||
60
|
||||
),
|
||||
$this->attributes['enclosure_filesize'],
|
||||
$this->attributes['enclosure_duration'],
|
||||
base64_url_encode(
|
||||
pack(
|
||||
'I*',
|
||||
$this->attributes['podcast_id'],
|
||||
$this->attributes['id'],
|
||||
// bytes_threshold: number of bytes that must be downloaded for an episode to be counted in download analytics
|
||||
// - if file is shorter than 60sec, then it's enclosure_filesize
|
||||
// - if file is longer than 60 seconds then it's enclosure_headersize + 60 seconds
|
||||
$this->attributes['enclosure_duration'] <= 60
|
||||
? $this->attributes['enclosure_filesize']
|
||||
: $this->attributes['enclosure_headersize'] +
|
||||
floor(
|
||||
(($this->attributes['enclosure_filesize'] -
|
||||
$this->attributes[
|
||||
'enclosure_headersize'
|
||||
]) /
|
||||
$this->attributes[
|
||||
'enclosure_duration'
|
||||
]) *
|
||||
60
|
||||
),
|
||||
$this->attributes['enclosure_filesize'],
|
||||
$this->attributes['enclosure_duration'],
|
||||
strtotime($this->attributes['published_at'])
|
||||
)
|
||||
),
|
||||
$this->attributes['enclosure_uri']
|
||||
)
|
||||
);
|
||||
|
@ -30,6 +30,22 @@ if (!function_exists('getallheaders')) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Base64 for URLs
|
||||
*/
|
||||
function base64_url_encode($input)
|
||||
{
|
||||
return strtr(base64_encode($input), '+/=', '._-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode Base64 from URL
|
||||
*/
|
||||
function base64_url_decode($input)
|
||||
{
|
||||
return base64_decode(strtr($input, '._-', '+/='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user country in session variable, for analytics purpose
|
||||
*/
|
||||
@ -245,6 +261,7 @@ function podcast_hit(
|
||||
$bytesThreshold,
|
||||
$fileSize,
|
||||
$duration,
|
||||
$publicationDate,
|
||||
$serviceName
|
||||
) {
|
||||
$session = \Config\Services::session();
|
||||
@ -311,6 +328,8 @@ function podcast_hit(
|
||||
$db = \Config\Database::connect();
|
||||
$procedureName = $db->prefixTable('analytics_podcasts');
|
||||
|
||||
$age = intdiv(time() - $publicationDate, 86400);
|
||||
|
||||
// We create a sha1 hash for this IP_Address+User_Agent+Podcast_ID (used to count unique listeners):
|
||||
$listenerHashId =
|
||||
'_IpUaPo_' .
|
||||
@ -337,7 +356,7 @@ function podcast_hit(
|
||||
cache()->save($listenerHashId, $downloadsByUser, $midnightTTL);
|
||||
|
||||
$db->query(
|
||||
"CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
|
||||
"CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
|
||||
[
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
@ -352,6 +371,7 @@ function podcast_hit(
|
||||
$session->get('player')['bot'],
|
||||
$fileSize,
|
||||
$duration,
|
||||
$age,
|
||||
$newListener,
|
||||
]
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user