feat(pay): zibll-style pay_modo/points/pay_limit, VIP quota, and local speed stream
Expand post pay meta with cash/points modes and member purchase limits, enforce free-download daily quotas, and stream local files with VIP speed caps while keeping remote links on redirect.
This commit is contained in:
1 parent
8b50687e9d
commit
1c8e4d3919
6 files changed
+311
-33
No files matched your search
@@ -284,11 +284,101 @@ class AKSRT_Download {
|
||||
update_post_meta((int) $row['post_id'], '_aksrt_res_download_count', $count + 1);
|
||||
}
|
||||
|
||||
$target_url = esc_url_raw((string) $meta['downloads'][$idx]['url']);
|
||||
$speed_kb = 0;
|
||||
$uid_speed = (int) ($row['user_id'] ?? 0);
|
||||
if ($uid_speed > 0 && function_exists('aksrt_get_user_download_speed')) {
|
||||
$speed_kb = aksrt_get_user_download_speed($uid_speed);
|
||||
}
|
||||
|
||||
// Local attachment / uploads file: optional throttled stream (Zibll speed limit).
|
||||
$local_path = $this->resolve_local_download_path($target_url);
|
||||
if ($local_path !== '' && is_readable($local_path)) {
|
||||
$this->stream_local_file($local_path, $speed_kb);
|
||||
exit;
|
||||
}
|
||||
|
||||
nocache_headers();
|
||||
wp_redirect(esc_url_raw((string) $meta['downloads'][$idx]['url']), 302);
|
||||
wp_redirect($target_url, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a site-local URL to a filesystem path under uploads (or ABSPATH).
|
||||
*/
|
||||
private function resolve_local_download_path(string $url): string {
|
||||
if ($url === '') {
|
||||
return '';
|
||||
}
|
||||
$uploads = wp_upload_dir();
|
||||
$baseurl = trailingslashit((string) ($uploads['baseurl'] ?? ''));
|
||||
$basedir = trailingslashit((string) ($uploads['basedir'] ?? ''));
|
||||
if ($baseurl !== '' && str_starts_with($url, $baseurl)) {
|
||||
$rel = substr($url, strlen($baseurl));
|
||||
$path = $basedir . ltrim(rawurldecode($rel), '/\\');
|
||||
$real = realpath($path);
|
||||
$root = realpath($basedir);
|
||||
if ($real && $root && str_starts_with($real, $root) && is_file($real)) {
|
||||
return $real;
|
||||
}
|
||||
}
|
||||
// Same-host path under ABSPATH.
|
||||
$home = trailingslashit(home_url('/'));
|
||||
if (str_starts_with($url, $home)) {
|
||||
$rel = substr($url, strlen($home));
|
||||
$path = ABSPATH . ltrim(rawurldecode($rel), '/\\');
|
||||
$real = realpath($path);
|
||||
$root = realpath(ABSPATH);
|
||||
if ($real && $root && str_starts_with($real, $root) && is_file($real)) {
|
||||
return $real;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a local file with optional KB/s throttle.
|
||||
*/
|
||||
private function stream_local_file(string $path, int $speed_kb = 0): void {
|
||||
$filename = basename($path);
|
||||
$size = filesize($path);
|
||||
if ($size === false) {
|
||||
$this->fail_410();
|
||||
}
|
||||
|
||||
nocache_headers();
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Length: ' . (string) $size);
|
||||
|
||||
$chunk = 1024 * 64; // 64KB
|
||||
if ($speed_kb > 0) {
|
||||
$chunk = max(1024, min($chunk, $speed_kb * 1024));
|
||||
}
|
||||
|
||||
$fh = fopen($path, 'rb');
|
||||
if (!$fh) {
|
||||
$this->fail_410();
|
||||
}
|
||||
while (!feof($fh)) {
|
||||
$buf = fread($fh, $chunk);
|
||||
if ($buf === false) {
|
||||
break;
|
||||
}
|
||||
echo $buf;
|
||||
if (function_exists('flush')) {
|
||||
flush();
|
||||
}
|
||||
if ($speed_kb > 0) {
|
||||
// Approximate throttle: sleep 1s per speed_kb window when chunk ~= speed_kb KB.
|
||||
usleep(200000); // 0.2s per chunk for smoother throttle
|
||||
}
|
||||
}
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
private function fail_410(): void {
|
||||
status_header(410);
|
||||
nocache_headers();
|
||||
|
||||
Reference in new issue
Block a user