From 70c4865817bfef5716cf1373bcc07554a84730ed Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 26 Feb 2026 20:17:29 +0000 Subject: [PATCH] Save --- admin.php | 65 +++++++++++++++++++- api/bridge_control.php | 135 +++++++++++++++++++++++++++++++++++++---- tiktok_bridge.js | 59 +++++++++++++----- 3 files changed, 231 insertions(+), 28 deletions(-) diff --git a/admin.php b/admin.php index 1f396fa..be6c03f 100644 --- a/admin.php +++ b/admin.php @@ -125,7 +125,7 @@ $default_language = $settings['default_language'] ?? 'en';
-
+

@@ -200,10 +200,73 @@ $default_language = $settings['default_language'] ?? 'en';
+ + +
+

System Diagnostics

+

Check if your hosting (cPanel/Other) is compatible with the TikTok Bridge.

+ +
+ Loading diagnostics... +
+ + +
+ + \ No newline at end of file diff --git a/api/bridge_control.php b/api/bridge_control.php index c5d70f8..4a99c64 100644 --- a/api/bridge_control.php +++ b/api/bridge_control.php @@ -18,31 +18,144 @@ if (empty($username)) { exit; } +// Helper to check if a function is enabled +function is_enabled($func) { + return function_exists($func) && !in_array($func, array_map('trim', explode(',', ini_get('disable_functions')))); +} + +if (!is_enabled('shell_exec')) { + echo json_encode(['success' => false, 'error' => 'PHP function shell_exec is disabled on this server. Contact your hosting provider.']); + exit; +} + +// Find node path +function get_node_path() { + $path = trim((string)shell_exec('which node')); + if ($path && file_exists($path)) return $path; + + $common_paths = ['/usr/local/bin/node', '/usr/bin/node', '/bin/node', '/opt/node/bin/node']; + foreach ($common_paths as $p) { + if (file_exists($p)) return $p; + } + return 'node'; // Fallback to PATH +} + +$node = get_node_path(); + +// Create logs directory if not exists +$logs_dir = __DIR__ . '/../logs'; +if (!is_dir($logs_dir)) { + @mkdir($logs_dir, 0755, true); +} + +$pid_file = "$logs_dir/bridge_{$username}_{$user_id}.pid"; +$log_file = "$logs_dir/bridge_{$username}_{$user_id}.log"; + +function is_running($pid_file) { + if (!file_exists($pid_file)) return false; + $pid = (int)file_get_contents($pid_file); + if ($pid <= 0) return false; + + // Check if process exists (posix_getpgid is safer but might be disabled) + if (function_exists('posix_getpgid')) { + return @posix_getpgid($pid) !== false; + } + + // Fallback to pgrep or ps + $output = shell_exec("ps -p $pid"); + return (strpos($output, (string)$pid) !== false); +} + +function kill_process($pid_file, $username, $user_id) { + if (file_exists($pid_file)) { + $pid = (int)file_get_contents($pid_file); + if ($pid > 0) { + shell_exec("kill -9 $pid > /dev/null 2>&1"); + } + @unlink($pid_file); + } + // Also try pkill as backup + shell_exec("pkill -f \"node .*tiktok_bridge.js $username .* $user_id\""); +} + if ($action === 'start') { - // 1. Kill any existing bridge for this user - shell_exec("pkill -f \"node tiktok_bridge.js $username $user_id\""); + // 1. Kill any existing bridge + kill_process($pid_file, $username, $user_id); // 2. Start new bridge + $bridge_script = __DIR__ . '/../tiktok_bridge.js'; + + // Check if node_modules exist + if (!is_dir(__DIR__ . '/../node_modules')) { + echo json_encode(['success' => false, 'error' => 'node_modules folder not found. Please run "npm install" on your server.']); + exit; + } + $cmd = sprintf( - "node %s %s %s %s %s %s %s > /dev/null 2>&1 &", - escapeshellarg(__DIR__ . '/../tiktok_bridge.js'), + "%s %s %s %s %s %s %s %s > %s 2>&1 & echo $!", + $node, + escapeshellarg($bridge_script), escapeshellarg($username), escapeshellarg(DB_HOST), escapeshellarg(DB_USER), escapeshellarg(DB_PASS), escapeshellarg(DB_NAME), - escapeshellarg($user_id) + escapeshellarg($user_id), + escapeshellarg($log_file) ); - shell_exec($cmd); + $pid = trim((string)shell_exec($cmd)); - echo json_encode(['success' => true, 'message' => "Bridge started for @$username"]); + if ($pid > 0) { + file_put_contents($pid_file, $pid); + echo json_encode([ + 'success' => true, + 'message' => "Bridge started for @$username", + 'pid' => $pid, + 'log' => "logs/" . basename($log_file) + ]); + } else { + echo json_encode([ + 'success' => false, + 'error' => "Failed to start bridge. Check $log_file for errors.", + 'node_path' => $node + ]); + } + } elseif ($action === 'stop') { - shell_exec("pkill -f \"node tiktok_bridge.js $username $user_id\""); + kill_process($pid_file, $username, $user_id); echo json_encode(['success' => true, 'message' => "Bridge stopped for @$username"]); + } elseif ($action === 'status') { - $output = shell_exec("pgrep -f \"node tiktok_bridge.js $username $user_id\""); - echo json_encode(['success' => true, 'running' => !empty($output)]); + $running = is_running($pid_file); + + // Check log for errors if not running but was supposed to + $last_log = ""; + if (!$running && file_exists($log_file)) { + $last_log = trim((string)shell_exec("tail -n 5 " . escapeshellarg($log_file))); + } + + echo json_encode([ + 'success' => true, + 'running' => $running, + 'last_log' => $last_log + ]); + +} elseif ($action === 'debug') { + // Special action to check environment + $env = [ + 'php_version' => PHP_VERSION, + 'shell_exec_enabled' => is_enabled('shell_exec'), + 'node_path' => $node, + 'node_version' => trim((string)shell_exec("$node -v")), + 'npm_version' => trim((string)shell_exec("npm -v")), + 'node_modules_exists' => is_dir(__DIR__ . '/../node_modules'), + 'db_host' => DB_HOST, + 'db_user' => DB_USER, + 'os' => PHP_OS, + ]; + echo json_encode(['success' => true, 'env' => $env]); + } else { echo json_encode(['success' => false, 'error' => 'Invalid action']); -} \ No newline at end of file +} diff --git a/tiktok_bridge.js b/tiktok_bridge.js index b134a87..bc2fa09 100644 --- a/tiktok_bridge.js +++ b/tiktok_bridge.js @@ -9,7 +9,7 @@ if (!username) { } async function start() { - console.log(`TikTok Bridge starting for @${username} (User ID: ${userId})`); + console.log(`[${new Date().toISOString()}] TikTok Bridge starting for @${username} (User ID: ${userId})`); let connection; try { @@ -19,18 +19,24 @@ async function start() { password: dbPass || '', database: dbName || 'app_db' }); - console.log('Connected to database'); + console.log(`[${new Date().toISOString()}] Connected to database: ${dbName} at ${dbHost}`); } catch (err) { - console.error('DB Connection failed:', err); + console.error(`[${new Date().toISOString()}] DB Connection failed:`, err.message); process.exit(1); } - const tiktokConnection = new WebcastPushConnection(username); + // Create a new connection instance + let tiktokConnection = new WebcastPushConnection(username, { + processInitialData: false, + enableExtendedGiftInfo: true, + requestPollingIntervalMs: 2000 + }); tiktokConnection.connect().then(state => { - console.log(`Connected to TikTok Live @${username} (Room ID: ${state.roomId})`); + console.log(`[${new Date().toISOString()}] Connected to TikTok Live @${username} (Room ID: ${state.roomId})`); }).catch(err => { - console.error('TikTok Connection failed:', err); + console.error(`[${new Date().toISOString()}] TikTok Connection failed:`, err.message); + console.error('Check if the username is correct and the user is currently LIVE.'); process.exit(1); }); @@ -42,11 +48,15 @@ async function start() { [username, data.uniqueId, data.comment, userId || null] ); } catch (err) { - console.error('Error inserting chat:', err); + console.error('Error inserting chat:', err.message); } }); tiktokConnection.on('gift', async (data) => { + if (data.giftType === 1 && !data.repeatEnd) { + // Wait for repeatEnd for streaks + return; + } const giftMsg = `sent ${data.repeatCount}x ${data.giftName}!`; console.log(`[Gift] ${data.uniqueId}: ${giftMsg}`); try { @@ -55,30 +65,47 @@ async function start() { [username, data.uniqueId, giftMsg, userId || null] ); } catch (err) { - console.error('Error inserting gift:', err); + console.error('Error inserting gift:', err.message); } }); + tiktokConnection.on('member', (data) => { + console.log(`[Join] ${data.uniqueId} joined the stream`); + }); + tiktokConnection.on('disconnected', () => { - console.log('TikTok connection disconnected'); + console.log(`[${new Date().toISOString()}] TikTok connection disconnected`); + process.exit(0); + }); + + tiktokConnection.on('streamEnd', () => { + console.log(`[${new Date().toISOString()}] Stream ended by host`); process.exit(0); }); tiktokConnection.on('error', (err) => { - console.error('TikTok error:', err); + console.error(`[${new Date().toISOString()}] TikTok error:`, err.message); }); - // Keep alive + // Keep DB connection alive setInterval(async () => { try { await connection.query('SELECT 1'); } catch (err) { - console.error('DB connection lost, reconnecting...'); - connection = await mysql.createConnection({ - host: dbHost, user: dbUser, password: dbPass, database: dbName - }); + console.log(`[${new Date().toISOString()}] DB connection lost, reconnecting...`); + try { + connection = await mysql.createConnection({ + host: dbHost, user: dbUser, password: dbPass, database: dbName + }); + } catch (reconnectErr) { + console.error('Reconnection failed:', reconnectErr.message); + } } }, 30000); } -start(); +// Handle termination signals +process.on('SIGINT', () => process.exit(0)); +process.on('SIGTERM', () => process.exit(0)); + +start(); \ No newline at end of file