diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 63fce469..4d1b14bf 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -42,11 +42,16 @@ class UserResource extends JsonResource 'bonus_human' => number_format($this->seedbonus, 1), 'seed_points' => floatval($this->seed_points), 'seed_points_human' => number_format($this->seed_points, 1), + 'seed_points_per_hour' => floatval($this->seed_points_per_hour), + 'seed_points_per_hour_human' => number_format($this->seed_points_per_hour, 1), + 'seed_bonus_per_hour' => floatval($this->seed_bonus_per_hour), + 'seed_bonus_per_hour_human' => number_format($this->seed_bonus_per_hour, 1), 'seedtime' => $this->seedtime, 'seedtime_text' => mkprettytime($this->seedtime), 'leechtime' => $this->leechtime, 'leechtime_text' => mkprettytime($this->leechtime), 'share_ratio' => get_ratio($this->id), + 'seeding_leeching_data' => $this->whenHas('seeding_leeching_data'), 'inviter' => new UserResource($this->whenLoaded('inviter')), 'valid_medals' => MedalResource::collection($this->whenLoaded('valid_medals')), ]; diff --git a/app/Jobs/CalculateUserSeedBonus.php b/app/Jobs/CalculateUserSeedBonus.php index afdf998b..69ba369f 100644 --- a/app/Jobs/CalculateUserSeedBonus.php +++ b/app/Jobs/CalculateUserSeedBonus.php @@ -96,7 +96,7 @@ class CalculateUserSeedBonus implements ShouldQueue $logFile = getLogFile("seed-bonus-points"); do_log("$logPrefix, [GET_UID_REAL], count: " . count($results) . ", logFile: $logFile"); $fd = fopen($logFile, 'a'); - $seedPointsUpdates = $seedPointsPerHourUpdates = $seedBonusUpdates = []; + $seedPointsUpdates = $seedPointsPerHourUpdates = $seedBonusPerHourUpdates = $seedBonusUpdates = []; $seedingTorrentCountUpdates = $seedingTorrentSizeUpdates = []; $logStr = ""; $bonusLogInsert = []; @@ -159,6 +159,7 @@ class CalculateUserSeedBonus implements ShouldQueue // NexusDB::statement($sql); $seedPointsUpdates[] = sprintf("when %d then ifnull(seed_points, 0) + %f", $uid, $seed_points); $seedPointsPerHourUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['seed_points']); + $seedBonusPerHourUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['seed_bonus']); $seedingTorrentCountUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['torrent_peer_count']); $seedingTorrentSizeUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['size']); $seedBonusUpdates[] = sprintf("when %d then seedbonus + %f", $uid, $all_bonus); @@ -177,8 +178,8 @@ class CalculateUserSeedBonus implements ShouldQueue } $nowStr = now()->toDateTimeString(); $sql = sprintf( - "update users set seed_points = case id %s end, seed_points_per_hour = case id %s end, seedbonus = case id %s end, seeding_torrent_count = case id %s end, seeding_torrent_size = case id %s end, seed_points_updated_at = '%s' where id in (%s)", - implode(" ", $seedPointsUpdates), implode(" ", $seedPointsPerHourUpdates), implode(" ", $seedBonusUpdates), implode(" ", $seedingTorrentCountUpdates), implode(" ", $seedingTorrentSizeUpdates), $nowStr, $idStr + "update users set seed_points = case id %s end, seed_points_per_hour = case id %s end, seed_bonus_per_hour = case id %s end, seedbonus = case id %s end, seeding_torrent_count = case id %s end, seeding_torrent_size = case id %s end, seed_points_updated_at = '%s' where id in (%s)", + implode(" ", $seedPointsUpdates), implode(" ", $seedPointsPerHourUpdates), implode(" ", $seedBonusPerHourUpdates), implode(" ", $seedBonusUpdates), implode(" ", $seedingTorrentCountUpdates), implode(" ", $seedingTorrentSizeUpdates), $nowStr, $idStr ); $result = NexusDB::statement($sql); if ($delIdRedisKey) { diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 9e320c15..a3b2dfb4 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -36,6 +36,9 @@ use Nexus\Database\NexusDB; class UserRepository extends BaseRepository { + private static array $allowIncludes = ['inviter', 'valid_medals']; + private static array $allowIncludeFields = ['seeding_leeching_data']; + private static array $allowIncludeCounts = []; public function getList(array $params) { $query = User::query(); @@ -66,28 +69,34 @@ class UserRepository extends BaseRepository { //query this info default $query = User::query()->with([]); - $allowIncludes = ['inviter', 'valid_medals']; - $allowIncludeCounts = []; - $allowIncludeFields = []; $apiQueryBuilder = ApiQueryBuilder::for(UserResource::NAME, $query) - ->allowIncludes($allowIncludes) - ->allowIncludeCounts($allowIncludeCounts) - ->allowIncludeFields($allowIncludeFields) + ->allowIncludes(self::$allowIncludes) + ->allowIncludeCounts(self::$allowIncludeCounts) + ->allowIncludeFields(self::$allowIncludeFields) ; $query = $apiQueryBuilder->build(); $user = $query->findOrFail($id); Gate::authorize('view', $user); - return $this->appendIncludeFields($apiQueryBuilder, $currentUser, $user); + $userList = $this->appendIncludeFields($apiQueryBuilder, $currentUser, [$user]); + return $userList[0]; } - private function appendIncludeFields(ApiQueryBuilder $apiQueryBuilder, Authenticatable $currentUser, User $user): User + private function appendIncludeFields(ApiQueryBuilder $apiQueryBuilder, Authenticatable $currentUser, $userList) { -// $id = $torrent->id; -// if ($apiQueryBuilder->hasIncludeField('has_bookmarked')) { -// $torrent->has_bookmarked = (int)$user->bookmarks()->where('torrentid', $id)->exists();; -// } - - return $user; + $idArr = []; + foreach ($userList as $user) { + $idArr[] = $user->id; + } + if ($hasFieldSeedingData = $apiQueryBuilder->hasIncludeField('seeding_leeching_data')) { + $seedingData = $this->listUserSeedingLeechingData($idArr); + } + foreach ($userList as $user) { + $id = $user->id; + if ($hasFieldSeedingData && isset($seedingData[$id])) { + $user->seeding_leeching_data = $seedingData[$id]; + } + } + return $userList; } /** @@ -259,7 +268,7 @@ class UserRepository extends BaseRepository private function setEnableLatelyCache(int $userId): void { - NexusDB::cache_put(User::getUserEnableLatelyCacheKey($userId), now()->toDateTimeString()); + NexusDB::cache_put(User::getUserEnableLatelyCacheKey($userId), now()->toDateTimeString(), 86400); } public function getInviteInfo($id) @@ -814,4 +823,38 @@ class UserRepository extends BaseRepository return $loginLog; } + /** + * get user seeding/leeching count and size + * + * @see calculate_seed_bonus() + * @param array $userIdArr + * @return array + */ + private function listUserSeedingLeechingData(array $userIdArr) + { + $minSize = get_setting('bonus.min_size', 0); + $idStr = implode(",", $userIdArr); + $sql = "select peers.userid, peers.seeder, torrents.size from torrents LEFT JOIN peers ON peers.torrent = torrents.id WHERE peers.userid in ($idStr) and torrents.size > $minSize group by peers.torrent, peers.peer_id, peers.userid, peers.seeder"; + $data = NexusDB::select($sql); + $result = []; + foreach ($data as $row) { + if (!isset($result[$row['userid']])) { + $result[$row['userid']] = [ + 'seeding_count' => 0, + 'seeding_size' => 0, + 'leeching_count' => 0, + 'leeching_size' => 0, + ]; + } + if ($row['seeder'] == 'yes') { + $result[$row['userid']]['seeding_count'] += 1; + $result[$row['userid']]['seeding_size'] += $row['size']; + } else { + $result[$row['userid']]['leeching_count'] += 1; + $result[$row['userid']]['leeching_size'] += $row['size']; + } + } + return $result; + } + } diff --git a/database/migrations/2025_11_19_033120_add_seeding_bonus_per_hour_to_users_table.php b/database/migrations/2025_11_19_033120_add_seeding_bonus_per_hour_to_users_table.php new file mode 100644 index 00000000..f5850946 --- /dev/null +++ b/database/migrations/2025_11_19_033120_add_seeding_bonus_per_hour_to_users_table.php @@ -0,0 +1,28 @@ +decimal('seed_bonus_per_hour', 20, 1)->default(0)->after('seed_points_per_hour'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('seed_bonus_per_hour'); + }); + } +}; diff --git a/include/cleanup.php b/include/cleanup.php index 5de5c51c..3a88daf6 100644 --- a/include/cleanup.php +++ b/include/cleanup.php @@ -341,7 +341,7 @@ function docleanup($forceAll = 0, $printProgress = false) { //rest seed_points_per_hour $seedPointsUpdatedAtMin = $carbonNow->subSeconds(2*intval($autoclean_interval_one))->toDateTimeString(); - sql_query("update users set seed_points_per_hour = 0 where seed_points_updated_at < " . sqlesc($seedPointsUpdatedAtMin)); + sql_query("update users set seed_points_per_hour = 0, seed_bonus_per_hour = 0, seeding_torrent_count = 0, seeding_torrent_size = 0 where seed_points_updated_at < " . sqlesc($seedPointsUpdatedAtMin)); \App\Repositories\CleanupRepository::runBatchJobCalculateUserSeedBonus($requestId); diff --git a/include/constants.php b/include/constants.php index b8122a79..9649eb04 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ \"D\""); + $actions[] = "\"D\""; } - print("
\"E\"\n"); + $actions[] = "\"E\""; + echo sprintf("%s", implode("
", $actions)); } print("\n"); $counter++; diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 57037c84..a222877a 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -1668,6 +1668,14 @@ JS; \Nexus\Nexus::js($js, 'footer', false); } +function nexus_escape($data): array|string +{ + if (is_array($data)) { + return array_map('nexus_escape', $data); + } + return htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); +} + function is_fpm_mode(): bool { return php_sapi_name() === 'fpm-fcgi'; diff --git a/nexus/PTGen/PTGen.php b/nexus/PTGen/PTGen.php index 8c459456..4a875e21 100644 --- a/nexus/PTGen/PTGen.php +++ b/nexus/PTGen/PTGen.php @@ -84,14 +84,15 @@ class PTGen public function generate(string $url, bool $withoutCache = false): array { - $parsed = $this->parse($url); +// $parsed = $this->parse($url); $targetUrl = trim($this->apiPoint, '/'); if (Str::contains($targetUrl, '?')) { $targetUrl .= "&"; } else { $targetUrl .= "?"; } - $targetUrl .= sprintf('site=%s&sid=%s&url=%s', $parsed['site'] , $parsed['id'], urlencode($parsed['url'])); +// $targetUrl .= sprintf('site=%s&sid=%s&url=%s', $parsed['site'] , $parsed['id'], urlencode($parsed['url'])); + $targetUrl .= "url=" . urlencode($url); return $this->request($targetUrl, $withoutCache); } @@ -177,7 +178,7 @@ HTML; } do_log("$logPrefix, going to send request..."); $http = new Client(); - $response = $http->get($url, ['timeout' => 10]); + $response = $http->post($url, ['timeout' => 10]); $statusCode = $response->getStatusCode(); if ($statusCode != 200) { $msg = "api point response http status code: $statusCode"; diff --git a/public/details.php b/public/details.php index 0a49e836..7084b49c 100644 --- a/public/details.php +++ b/public/details.php @@ -29,7 +29,6 @@ $row = mysql_fetch_array($res); if (user_can('torrentmanage') || $CURUSER["id"] == $row["owner"]) $owned = 1; else $owned = 0; - $settingMain = get_setting('main'); if (!$row) { stderr($lang_details['std_error'], $lang_details['std_no_torrent_id']); @@ -308,20 +307,20 @@ JS; //technical info if ($settingMain['enable_technical_info'] == 'yes') { - $technicalData = $row['technical_info'] ?? ''; - + $technicalData = nexus_escape($row['technical_info'] ?? ''); + // 判断是否为BDINFO格式 $isBdInfo = false; if (!empty($technicalData)) { $firstLine = strtok($technicalData, "\n"); - if (strpos($firstLine, 'DISC INFO') !== false + if (strpos($firstLine, 'DISC INFO') !== false || strpos($firstLine, 'Disc Title') !== false || strpos($firstLine, 'Disc Label') !== false ) { $isBdInfo = true; } } - + if ($isBdInfo) { // 使用BdInfoExtra处理BDINFO格式 $technicalInfo = new \Nexus\Torrent\BdInfoExtra($technicalData); @@ -329,7 +328,7 @@ JS; // 使用TechnicalInformation处理MediaInfo格式 $technicalInfo = new \Nexus\Torrent\TechnicalInformation($technicalData); } - + $technicalInfoResult = $technicalInfo->renderOnDetailsPage(); if (!empty($technicalInfoResult)) { tr($lang_functions['text_technical_info'], $technicalInfoResult, 1); diff --git a/public/takeconfirm.php b/public/takeconfirm.php index 173244d1..c0871df8 100644 --- a/public/takeconfirm.php +++ b/public/takeconfirm.php @@ -12,6 +12,7 @@ if(!empty($_POST['conusr'])) { // sql_query("UPDATE users SET status = 'confirmed', editsecret = '' WHERE id IN (" . implode(", ", $_POST['conusr']) . ") AND status='pending'"); $userList = \App\Models\User::query()->whereIn('id', $_POST['conusr']) ->where('status', 'pending') + ->where('invited_by', $id) ->get(\App\Models\User::$commonFields) ; if ($userList->isNotEmpty()) { @@ -21,6 +22,9 @@ if(!empty($_POST['conusr'])) { fire_event(\App\Enums\ModelEventEnum::USER_UPDATED, $user); } \App\Models\User::query()->whereIn('id', $uidArr)->update(['status' => 'confirmed', 'editsecret' => '']); + } else { + stderr($lang_takeconfirm['std_sorry'],$lang_takeconfirm['std_no_buddy_to_confirm']. + "".$lang_takeconfirm['std_here_to_go_back'],false); } } else { stderr($lang_takeconfirm['std_sorry'],$lang_takeconfirm['std_no_buddy_to_confirm'].