feat(vip): add default icons and remove download speed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 17:48:10 +08:00
co-authored by Claude Fable 5
parent 77d54b3b9d
commit 18da39788f
15 changed files with 105 additions and 74 deletions
@@ -21,11 +21,8 @@ export const vipSchema = {
pay_user_vip_desc: '',
pay_user_vip_more: '',
pay_download_limit: 0,
pay_download_speed: 0,
pay_download_limit_vip_1: 0,
pay_download_speed_vip_1: 0,
pay_download_limit_vip_2: 0,
pay_download_speed_vip_2: 0,
pay_vip_pass_charge_s: false,
pay_vip_pass_charge_desc: '',
pay_vip_pass_charge_only_password: true,
@@ -51,12 +48,12 @@ export const vipSchema = {
{ key: 'pay_user_vip_1_name', label: '一级会员名称', type: 'input', default: '一级会员', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
{ key: 'pay_user_vip_1_desc', label: '一级会员一句话简介', type: 'input', default: '尊享一级会员特权', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
{ key: 'pay_user_vip_1_equity', label: '一级会员权益 HTML', type: 'textarea', default: '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>', dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '建议使用 <li> 列表项' },
{ key: 'vip_1_img_icon', label: '一级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
{ key: 'vip_1_img_icon', label: '一级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '留空使用内置 vip-1.svg 图标' },
{ key: 'pay_user_vip_2_s', label: '启用二级会员', type: 'switch', default: true },
{ key: 'pay_user_vip_2_name', label: '二级会员名称', type: 'input', default: '二级会员', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'pay_user_vip_2_desc', label: '二级会员一句话简介', type: 'input', default: '尊享二级会员特权', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'pay_user_vip_2_equity', label: '二级会员权益 HTML', type: 'textarea', default: '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'vip_2_img_icon', label: '二级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'vip_2_img_icon', label: '二级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_2_s', value: true }, help: '留空使用内置 vip-2.svg 图标' },
{ key: 'pay_user_vip_desc', label: '弹窗顶部会员说明', type: 'textarea', default: '' },
{ key: 'pay_user_vip_more', label: '弹窗底部补充说明 HTML', type: 'textarea', default: '' },
],
@@ -109,11 +106,8 @@ export const vipSchema = {
icon: 'fas fa-gift',
fields: [
{ key: 'pay_download_limit', label: '普通用户下载限制', type: 'number', default: 0, min: 0, max: 9999, help: '0为不限制' },
{ key: 'pay_download_speed', label: '普通用户下载速度(KB/s)', type: 'number', default: 0, min: 0, max: 99999, help: '0为不限制' },
{ key: 'pay_download_limit_vip_1', label: '一级会员下载限制', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_1_s', value: true } },
{ key: 'pay_download_speed_vip_1', label: '一级会员下载速度(KB/s)', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'pay_user_vip_1_s', value: true } },
{ key: 'pay_download_limit_vip_2', label: '二级会员下载限制', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'pay_download_speed_vip_2', label: '二级会员下载速度(KB/s)', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'pay_user_vip_2_s', value: true } },
{ key: 'pay_vip_pass_charge_s', label: '会员卡密兑换', type: 'switch', default: false },
{ key: 'pay_vip_pass_charge_desc', label: '卡密兑换说明', type: 'textarea', default: '', dependsOn: { key: 'pay_vip_pass_charge_s', value: true } },
{ key: 'pay_vip_pass_charge_only_password', label: '仅密码兑换', type: 'switch', default: true, dependsOn: { key: 'pay_vip_pass_charge_s', value: true }, help: '开启后只需输入卡密;关闭则需卡号+卡密。' },
+16
View File
@@ -361,6 +361,22 @@
vertical-align: middle;
}
.aksrt-vip-level-icon {
display: inline-block;
width: 1.4em;
height: 1.4em;
object-fit: contain;
vertical-align: middle;
}
.aksrt-vip-badge__icon {
display: inline-block;
width: 1.1em;
height: 1.1em;
object-fit: contain;
vertical-align: -.15em;
}
.aksrt-vip-badge i {
font-size: 10px;
}
+13
View File
@@ -691,6 +691,19 @@ body.uc-modal-open {
padding: 22px;
color: #fff;
}
.aksrt-vip-level-icon {
display: inline-block;
width: 1.4em;
height: 1.4em;
object-fit: contain;
vertical-align: middle;
}
.uc-vip-card__icon .aksrt-vip-level-icon {
width: 28px;
height: 28px;
}
.uc-vip-card__header {
display: flex;
align-items: center;
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024"><defs><linearGradient id="a"><stop offset="0" stop-color="#fcdba2"/><stop offset="1" stop-color="#fdf6d8"/></linearGradient><linearGradient id="e"><stop offset="0" stop-color="#f8b446"/><stop offset="1" stop-color="#fdf4cf"/></linearGradient><linearGradient xlink:href="#a" id="j" x1="523.292" x2="521.856" y1="345.301" y2="627.851" gradientTransform="translate(8.2904476 3.9483139) scale(.96795)" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#b" id="f" x1="143.09692" x2="898.33069" y1="164.29646" y2="810.88257" gradientTransform="translate(-.00191063 -.00196486) scale(1.0005)" gradientUnits="userSpaceOnUse"/><linearGradient id="b"><stop offset="0" stop-color="#fbe2c9"/><stop offset="1" stop-color="#eea36b"/></linearGradient><linearGradient xlink:href="#c" id="g" x1="1274.6226" x2="1902.6591" y1="-291.49371" y2="204.04561" gradientTransform="translate(-1063.8611 567.31936) scale(1.0005)" gradientUnits="userSpaceOnUse"/><linearGradient id="c"><stop offset="0" stop-color="#e2a576"/><stop offset="1" stop-color="#f9e7d6"/></linearGradient><linearGradient xlink:href="#d" id="h" x1="-285.41812" x2="285.38971" y1="-257.87775" y2="255.19093" gradientTransform="translate(512.02424 518.02725) scale(1.0005)" gradientUnits="userSpaceOnUse"/><linearGradient id="d"><stop offset="0" stop-color="#facda1"/><stop offset="1" stop-color="#e3814e"/></linearGradient><radialGradient xlink:href="#e" id="i" cx="521.652" cy="617.926" r="257.943" fx="521.652" fy="617.926" gradientTransform="matrix(.9934 .00447 -.0041 .91186 -2.4439201 11.161921)" gradientUnits="userSpaceOnUse"/></defs><circle cx="512" cy="512" r="508" fill="url(#f)"/><circle cx="513.64362" cy="515.61957" r="425.46204" fill="url(#g)"/><circle cx="512.02417" cy="518.02716" r="386.19312" fill="url(#h)"/><path fill="url(#i)" d="M260.93578 362.19446v273.13658c0 31.70185 13.987 57.984 47.47974 57.984h404.12609c23.37369 0 47.64683-21.11456 47.74888-58.93648 0-32.14907-.27014-278.28715-.27014-278.28715l-246.59831 255.5878z"/><path fill="url(#j)" d="M517.23194 291.90631 362.70967 569.71123c-37.86393 67.97299 24.27614 70.95448 24.27614 70.95448h241.41372s66.13307 1.85993 33.10956-68.66634z"/><circle cx="516.09039" cy="286.75574" r="39.223614" fill="#fdf9e1"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

+8
View File
@@ -297,6 +297,14 @@
var row = node("div", "pay-vip-form-row");
var info = node("div", "pay-vip-info level-" + level);
var levelInfo = levels[String(level)] || {};
if (levelInfo.img_icon) {
var levelIcon = node("img", "aksrt-vip-level-icon");
levelIcon.src = text(levelInfo.img_icon);
levelIcon.alt = "";
levelIcon.loading = "lazy";
levelIcon.decoding = "async";
append(info, levelIcon);
}
append(info, node("div", "v-name font-bold", group.name || levelInfo.name || "VIP" + level));
if (levelInfo.desc) append(info, node("div", "muted-2-color em09 mb6", levelInfo.desc));
renderEquity(info, levelInfo.equity);
+10 -26
View File
@@ -230,31 +230,6 @@ if (!function_exists('aksrt_get_user_free_download_limit')) {
}
}
/**
* Download speed cap in KB/s for a user (0 = unlimited).
*/
if (!function_exists('aksrt_get_user_download_speed')) {
function aksrt_get_user_download_speed(int $user_id = 0): int {
if ($user_id <= 0) {
$user_id = get_current_user_id();
}
if ($user_id <= 0 || !class_exists('\AKSRT\Settings\Settings')) {
return 0;
}
$vip = \AKSRT\Settings\Settings::get_payment_vip_settings();
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
$benefit = is_array($vip['benefit'] ?? null) ? $vip['benefit'] : [];
if ($level >= 1 && !empty($levels[$level]['enabled'])) {
return max(0, (int) ($levels[$level]['download_speed'] ?? 0));
}
return max(0, (int) ($benefit['download_speed'] ?? 0));
}
}
/**
* Count free downloads used today for a user (zibll pay_down_number).
*/
@@ -383,7 +358,16 @@ function aksrt_render_vip_badge(int $user_id = 0, string $size = 'sm'): string {
$size_class = 'aksrt-vip-badge--' . $size;
$level_class = $level >= 2 ? 'aksrt-vip-badge--vip2' : 'aksrt-vip-badge--vip1';
return '<span class="aksrt-vip-badge aksrt-vip-badge--gold ' . esc_attr($size_class . ' ' . $level_class) . '"><i class="fas fa-crown"></i> ' . esc_html($badge_text) . '</span>';
$icon_url = '';
if (class_exists('\AKSRT\Settings\Settings')) {
$vip_settings = \AKSRT\Settings\Settings::get_payment_vip_settings();
$icon_url = esc_url((string) ($vip_settings['levels'][$level]['img_icon'] ?? ''));
}
$icon = $icon_url !== ''
? '<img class="aksrt-vip-badge__icon" src="' . $icon_url . '" alt="" loading="lazy" decoding="async" />'
: '<i class="fas fa-crown" aria-hidden="true"></i>';
return '<span class="aksrt-vip-badge aksrt-vip-badge--gold ' . esc_attr($size_class . ' ' . $level_class) . '">' . $icon . ' ' . esc_html($badge_text) . '</span>';
}
/**
@@ -50,7 +50,10 @@ if ($vip_level >= 1 && !empty($vip_settings['levels'][$vip_level]['name'])) {
$level_name = (string) $vip_settings['levels'][$vip_level]['name'];
}
$cash_symbol = '¥';
$level_icon_url = esc_url((string) ($vip_settings['levels'][$vip_level]['img_icon'] ?? ''));
$level_icon = $level_icon_url !== ''
? '<img class="aksrt-vip-level-icon" src="' . $level_icon_url . '" alt="" loading="lazy" decoding="async">'
: '<i class="fas fa-crown" aria-hidden="true"></i>';
if (class_exists('\AKSRT\Settings\Settings')) {
$paid_display = \AKSRT\Settings\Settings::get_paid_content_settings();
$cash_symbol = (string) ($paid_display['display']['currency_symbol'] ?? '¥');
@@ -79,7 +82,7 @@ if (class_exists('\AKSRT\Payment\ChargeCard')) {
<div class="uc-vip-card__content">
<div class="uc-vip-card__header">
<div class="uc-vip-card__icon">
<i class="fas fa-crown" aria-hidden="true"></i>
<?php echo $level_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="uc-vip-card__info">
<span class="uc-vip-card__label"><?php echo esc_html($level_name); ?></span>
@@ -30,7 +30,7 @@ if ($status === '') {
if ($vip_level > 0) {
$title = (string) ($vip_levels[$vip_level]['name'] ?? ('VIP' . $vip_level));
if ($expires <= 0 && $vip_state_source !== 'order') {
if ($expires <= 0 && ($vip_state['source'] ?? 'none') !== 'order') {
$status = __('永久会员', 'aksrt');
} else {
$remaining_days = max(1, (int) ceil(($expires - time()) / DAY_IN_SECONDS));
@@ -44,9 +44,15 @@ if ($vip_level > 0) {
$title = (string) ($vip_levels[$expired_level]['name'] ?? ('VIP' . $expired_level));
$status = __('已过期', 'aksrt');
}
$sidebar_level = $vip_level > 0 ? $vip_level : $expired_level;
$sidebar_icon_url = esc_url((string) ($vip_levels[$sidebar_level]['img_icon'] ?? ''));
$sidebar_icon = $sidebar_icon_url !== ''
? '<img class="aksrt-vip-level-icon" src="' . $sidebar_icon_url . '" alt="" loading="lazy" decoding="async">'
: '<i class="fas fa-crown" aria-hidden="true"></i>';
?>
<a class="uc-sidebar-vip" href="<?php echo esc_url($vip_url); ?>">
<span class="uc-sidebar-vip__icon"><i class="fas fa-crown" aria-hidden="true"></i></span>
<span class="uc-sidebar-vip__icon"><?php echo $sidebar_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="uc-sidebar-vip__content">
<strong><?php echo esc_html($title); ?></strong>
<small><?php echo esc_html($status); ?></small>
+10 -2
View File
@@ -90,13 +90,17 @@ if ($level > 0) {
: __('到期时间:已过期', 'aksrt');
}
$status_icon_url = esc_url((string) ($levels[$vip_level_number]['img_icon'] ?? ''));
$status_icon = $status_icon_url !== ''
? '<img class="aksrt-vip-level-icon" src="' . $status_icon_url . '" alt="" loading="lazy" decoding="async">'
: '<i class="fas fa-crown"></i>';
$status_html = '<div class="uc-vip-status" role="status">'
. '<div class="uc-vip-status__heading">'
. '<h2 class="uc-vip-status__label">' . esc_html__('我的会员', 'aksrt') . '</h2>'
. '<span class="uc-vip-status__state">' . esc_html($vip_state_label) . '</span>'
. '</div>'
. '<div class="uc-vip-status__main uc-vip-status__main--level-' . (int) $vip_level_number . '">'
. '<span class="uc-vip-status__icon" aria-hidden="true"><i class="fas fa-crown"></i></span>'
. '<span class="uc-vip-status__icon" aria-hidden="true">' . $status_icon . '</span>'
. '<span class="uc-vip-status__level">' . esc_html($vip_level_name) . '</span>'
. '</div>'
. '<div class="uc-vip-status__desc">' . esc_html($vip_description) . '</div>'
@@ -118,6 +122,10 @@ foreach ([1, 2] as $lv) {
: '';
$cta = sprintf(__('开通%s', 'aksrt'), $lv_name);
$vip_attr = $lv;
$lv_icon_url = esc_url((string) ($levels[$lv]['img_icon'] ?? ''));
$lv_icon = $lv_icon_url !== ''
? '<img class="aksrt-vip-level-icon" src="' . $lv_icon_url . '" alt="" loading="lazy" decoding="async">'
: '<i class="fas fa-crown" aria-hidden="true"></i>';
if (str_starts_with($btn_type, 'renew_')) {
$renew_lv = (int) substr($btn_type, 6);
if ($lv === $renew_lv) {
@@ -144,7 +152,7 @@ foreach ([1, 2] as $lv) {
$cards .= '<div class="vip-baicon" aria-hidden="true"><i class="fas fa-gem"></i></div>';
$cards .= '<div class="relative uc-vip-level-card__inner">';
$cards .= '<div class="v-iconbox mb10 flex jc ac">';
$cards .= '<div class="v-icon flex jc ac"><i class="fas fa-crown" aria-hidden="true"></i></div>';
$cards .= '<div class="v-icon flex jc ac">' . $lv_icon . '</div>';
$cards .= '<div class="v-info text-left"><div class="v-name font-bold">' . esc_html($lv_name) . '</div>';
if ($lv_desc !== '') {
$cards .= '<div class="v-desc em09 muted-2-color">' . esc_html($lv_desc) . '</div>';
+2 -5
View File
@@ -112,12 +112,12 @@ class SchemaPayment {
['key' => 'pay_user_vip_1_name', 'label' => '一级会员名称', 'type' => 'input', 'default' => '一级会员', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_user_vip_1_desc', 'label' => '一级会员一句话简介', 'type' => 'input', 'default' => '尊享一级会员特权', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_user_vip_1_equity', 'label' => '一级会员权益 HTML', 'type' => 'textarea', 'default' => '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'vip_1_img_icon', 'label' => '一级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'vip_1_img_icon', 'label' => '一级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true], 'help' => '留空使用内置 vip-1.svg 图标'],
['key' => 'pay_user_vip_2_s', 'label' => '启用二级会员', 'type' => 'switch', 'default' => true],
['key' => 'pay_user_vip_2_name', 'label' => '二级会员名称', 'type' => 'input', 'default' => '二级会员', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_user_vip_2_desc', 'label' => '二级会员一句话简介', 'type' => 'input', 'default' => '尊享二级会员特权', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_user_vip_2_equity', 'label' => '二级会员权益 HTML', 'type' => 'textarea', 'default' => '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'vip_2_img_icon', 'label' => '二级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'vip_2_img_icon', 'label' => '二级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true], 'help' => '留空使用内置 vip-2.svg 图标'],
['key' => 'pay_user_vip_desc', 'label' => '弹窗顶部会员说明', 'type' => 'textarea', 'default' => ''],
['key' => 'pay_user_vip_more', 'label' => '弹窗底部补充说明 HTML', 'type' => 'textarea', 'default' => ''],
]],
@@ -134,11 +134,8 @@ class SchemaPayment {
]],
['key' => 'benefit', 'title' => '下载权益', 'fields' => [
['key' => 'pay_download_limit', 'label' => '普通用户下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 9999],
['key' => 'pay_download_speed', 'label' => '普通用户下载速度(KB/s)', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 99999],
['key' => 'pay_download_limit_vip_1', 'label' => '一级会员下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 9999, 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_download_speed_vip_1', 'label' => '一级会员下载速度(KB/s)', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 99999, 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_download_limit_vip_2', 'label' => '二级会员下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 9999, 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_download_speed_vip_2', 'label' => '二级会员下载速度(KB/s)', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 99999, 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_vip_pass_charge_s', 'label' => '会员卡密兑换', 'type' => 'switch', 'default' => false],
['key' => 'pay_vip_pass_charge_desc', 'label' => '卡密兑换说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'pay_vip_pass_charge_s', 'value' => true]],
['key' => 'pay_vip_pass_charge_only_password', 'label' => '仅密码兑换', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'pay_vip_pass_charge_s', 'value' => true]],
-1
View File
@@ -127,7 +127,6 @@ class SchemaPermissions {
['key' => 'download_credits_cost', 'label' => '默认积分价参考', 'type' => 'number', 'default' => 1, 'min' => 0, 'max' => 100, 'help' => '仅运营参考/兼容;实际价格以文章付费设置为准。'],
['key' => 'download_daily_limit', 'label' => '每日下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'help' => '0为不限制'],
['key' => 'download_link_expire', 'label' => '下载链接有效期(分钟)', 'type' => 'number', 'default' => 30, 'min' => 5, 'max' => 1440],
['key' => 'download_speed_limit', 'label' => '下载速度限制(KB/s)', 'type' => 'number', 'default' => 0, 'min' => 0, 'help' => '0为不限制'],
]],
['key' => 'vip', 'title' => 'VIP 特权', 'fields' => [
['key' => 'download_vip_free', 'label' => 'VIP 免费下载', 'type' => 'switch', 'default' => false],
+5 -22
View File
@@ -295,16 +295,11 @@ class Download {
}
$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 attachment / uploads file: stream without artificial throttling.
$local_path = $this->resolve_local_download_path($target_url);
if ($local_path !== '' && is_readable($local_path)) {
$this->stream_local_file($local_path, $speed_kb);
$this->stream_local_file($local_path);
exit;
}
@@ -347,9 +342,9 @@ class Download {
}
/**
* Stream a local file with optional KB/s throttle.
* Stream a local file.
*/
private function stream_local_file(string $path, int $speed_kb = 0): void {
private function stream_local_file(string $path): void {
$filename = basename($path);
$size = filesize($path);
if ($size === false) {
@@ -364,21 +359,12 @@ class Download {
header('Content-Length: ' . (string) $size);
$chunk = 1024 * 64; // 64KB
$sleep_us = 0;
if ($speed_kb > 0) {
// Emit ~speed_kb KB per second using fixed 64KB chunks when possible.
$bytes_per_sec = max(1024, $speed_kb * 1024);
$chunk = min(1024 * 64, $bytes_per_sec);
$chunk = max(1024, $chunk);
// Sleep so chunk_bytes / second ≈ bytes_per_sec.
$sleep_us = (int) max(0, (int) round(($chunk / $bytes_per_sec) * 1000000) - 1000);
}
$fh = fopen($path, 'rb');
if (!$fh) {
$this->fail_410();
}
// Disable output compression for accurate Content-Length / throttle.
// Disable output compression so Content-Length remains accurate.
if (function_exists('apache_setenv')) {
@apache_setenv('no-gzip', '1');
}
@@ -392,9 +378,6 @@ class Download {
if (function_exists('flush')) {
flush();
}
if ($sleep_us > 0) {
usleep($sleep_us);
}
}
fclose($fh);
}
+5 -1
View File
@@ -41,6 +41,7 @@ trait VipServiceModal {
'name' => (string) ($levels[$level]['name'] ?? ('VIP' . $level)),
'desc' => (string) ($levels[$level]['desc'] ?? ''),
'equity' => $equity,
'img_icon' => esc_url_raw((string) ($levels[$level]['img_icon'] ?? '')),
];
}
@@ -161,7 +162,10 @@ trait VipServiceModal {
? aksrt_format_vip_equity_html($equity, 'mb10 relative')
: '';
$icon = '<i class="fas fa-gem" aria-hidden="true"></i>';
$icon_url = esc_url((string) ($levels[$level]['img_icon'] ?? ''));
$icon = $icon_url !== ''
? '<img class="aksrt-vip-level-icon" src="' . $icon_url . '" alt="" loading="lazy" decoding="async" />'
: '<i class="fas fa-gem" aria-hidden="true"></i>';
$html = '<div class="vip-card pointer level-' . $level . ' vip-theme' . $level . $cls . '" vip-level="' . $level . '">';
$html .= '<div class="vip-baicon" aria-hidden="true">' . $icon . '</div>';
$html .= '<div class="vip-img abs-right">' . $icon . '</div>';
+19 -5
View File
@@ -122,6 +122,23 @@ class PaymentRuntime {
];
}
/**
* Resolve a configured VIP icon, falling back to the bundled level asset.
*/
private static function get_vip_icon_url(int $level, string $configured): string {
$configured = esc_url_raw($configured);
if ($configured !== '') {
return $configured;
}
$filename = 'vip-' . $level . '.svg';
if (defined('AKSRT_DIR') && is_file(AKSRT_DIR . '/assets/img/user/vip/' . $filename)) {
return defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/user/vip/' . $filename : '';
}
return '';
}
/**
* Dual-level VIP settings (Zibll pay_user_vip_1 / pay_user_vip_2).
*
@@ -190,27 +207,24 @@ class PaymentRuntime {
'name' => $name1,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_1_desc'] ?? __('尊享一级会员特权', 'aksrt'))),
'equity' => wp_kses((string) (($settings['pay_user_vip_1_equity'] ?? '') !== '' ? $settings['pay_user_vip_1_equity'] : '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>'), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_1_img_icon'] ?? '')),
'img_icon' => self::get_vip_icon_url(1, (string) ($settings['vip_1_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']),
'products_raw' => $products_1,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_1'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_1'] ?? 0))),
],
2 => [
'enabled' => $vip2_enabled,
'name' => $name2,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_2_desc'] ?? __('尊享二级会员特权', 'aksrt'))),
'equity' => wp_kses((string) (($settings['pay_user_vip_2_equity'] ?? '') !== '' ? $settings['pay_user_vip_2_equity'] : '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>'), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_2_img_icon'] ?? '')),
'img_icon' => self::get_vip_icon_url(2, (string) ($settings['vip_2_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']),
'products_raw' => $products_2,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_2'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_2'] ?? 0))),
],
],
'benefit' => [
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed'] ?? 0))),
'pass_charge' => !empty($settings['pay_vip_pass_charge_s']),
'pass_charge_desc' => sanitize_textarea_field((string) ($settings['pay_vip_pass_charge_desc'] ?? '')),
'pass_charge_only_password' => !isset($settings['pay_vip_pass_charge_only_password']) || !empty($settings['pay_vip_pass_charge_only_password']),