36896-vm/api.php
Flatlogic Bot 4e986c09da v3
2026-01-14 14:14:17 +00:00

347 lines
15 KiB
PHP

<?php
header('Content-Type: application/json');
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/db/config.php';
$response = [];
$action = $_GET['action'] ?? '';
use Dropbox\Client;
use Dropbox\Exception;
switch ($action) {
case 'save':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$url = $data['url'] ?? null;
$filename = $data['filename'] ?? null;
$token = $data['token'] ?? null;
if (empty($url) || empty($filename)) {
http_response_code(400);
$response['error'] = 'URL do stream e nome do arquivo são obrigatórios.';
} elseif (!filter_var($url, FILTER_VALIDATE_URL)) {
http_response_code(400);
$response['error'] = 'URL do stream inválida.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare(
"INSERT INTO streams (url, filename, dropbox_token, status) VALUES (:url, :filename, :token, 'pending')"
);
$stmt->execute([
':url' => $url,
':filename' => $filename,
':token' => $token
]);
$newId = $pdo->lastInsertId();
http_response_code(201);
$response['success'] = true;
$response['message'] = 'Stream salvo com sucesso! Pronto para conversão.';
$response['job_id'] = $newId;
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
}
}
} else {
http_response_code(405);
$response['error'] = 'Método não permitido para esta ação.';
}
break;
case 'get_streams':
try {
$pdo = db();
$stmt = $pdo->query("SELECT id, url, filename, status, created_at, progress FROM streams ORDER BY created_at DESC");
$streams = $stmt->fetchAll(PDO::FETCH_ASSOC);
$response['success'] = true;
$response['streams'] = $streams;
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
}
break;
case 'get_stream':
try {
$id = $_GET['id'] ?? null;
if (empty($id)) {
http_response_code(400);
$response['error'] = 'O ID do stream é obrigatório.';
} else {
$pdo = db();
$stmt = $pdo->prepare("SELECT id, url, filename, status, created_at, progress FROM streams WHERE id = :id");
$stmt->execute([':id' => $id]);
$stream = $stmt->fetch(PDO::FETCH_ASSOC);
$response['success'] = true;
$response['stream'] = $stream;
}
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
}
break;
case 'convert_to_mp4':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$id = $data['id'] ?? null;
if (empty($id)) {
http_response_code(400);
$response['error'] = 'O ID do stream é obrigatório.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT url, filename FROM streams WHERE id = :id");
$stmt->execute([':id' => $id]);
$stream = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stream) {
http_response_code(404);
$response['error'] = 'Stream não encontrado.';
} else {
// Get video duration using ffprobe
$ffprobe_command = [
'ffprobe',
'-v', 'error',
'-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1',
$stream['url']
];
$duration_process = new \Symfony\Component\Process\Process($ffprobe_command);
$duration_process->run();
$duration = (float)$duration_process->getOutput();
$output_filename = pathinfo($stream['filename'], PATHINFO_FILENAME) . '_' . time() . '.mp4';
$output_path = __DIR__ . '/videos/' . $output_filename;
$pdo->prepare("UPDATE streams SET status = 'converting', duration = :duration, output_filename = :output_filename WHERE id = :id")->execute([
':duration' => $duration,
':output_filename' => $output_filename,
':id' => $id
]);
$progress_log_path = sys_get_temp_dir() . '/' . $output_filename . '.log';
if (!is_dir(__DIR__ . '/videos')) {
mkdir(__DIR__ . '/videos', 0775, true);
}
$command = [
'ffmpeg',
'-y',
'-v', 'quiet',
'-progress', $progress_log_path,
'-i', $stream['url'],
'-c:v', 'libx264',
'-preset', 'veryfast',
'-crf', '23',
'-c:a', 'aac',
'-b:a', '128k',
$output_path
];
$process = new \Symfony\Component\Process\Process($command);
$process->setTimeout(3600);
$process->start();
$response['success'] = true;
$response['message'] = 'A conversão do vídeo foi iniciada.';
}
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
} catch (\Exception $e) {
http_response_code(500);
$response['error'] = 'Erro na conversão: ' . $e->getMessage();
}
}
} else {
http_response_code(405);
$response['error'] = 'Método não permitido para esta ação.';
}
break;
case 'get_conversion_progress':
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$id = $_GET['id'] ?? null;
if (empty($id)) {
http_response_code(400);
$response['error'] = 'O ID do stream é obrigatório.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT duration, converted_path, output_filename FROM streams WHERE id = :id");
$stmt->execute([':id' => $id]);
$stream = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stream) {
http_response_code(404);
$response['error'] = 'Stream não encontrado.';
} else {
$output_filename = $stream['output_filename'];
if(empty($output_filename)) {
$response['progress'] = 0;
$response['success'] = true;
} else {
$progress_log_path = sys_get_temp_dir() . '/' . $output_filename . '.log';
if (!file_exists($progress_log_path)) {
$response['progress'] = 0;
} else {
$log_content = file_get_contents($progress_log_path);
preg_match_all("/out_time_ms=(\d+)/", $log_content, $matches);
$last_match = end($matches[1]);
if ($last_match) {
$processed_ms = (float)$last_match / 1000000; // a unidade está em microsegundos
$duration = (float)$stream['duration'];
$progress = $duration > 0 ? round(($processed_ms / $duration) * 100) : 0;
$progress = min(100, $progress); // Garante que o progresso não passe de 100
$pdo->prepare("UPDATE streams SET progress = :progress WHERE id = :id")->execute([':progress' => $progress, ':id' => $id]);
if ($progress == 100) {
$pdo->prepare("UPDATE streams SET status = 'completed', converted_path = :converted_path WHERE id = :id")->execute([':converted_path' => $output_filename, ':id' => $id]);
unlink($progress_log_path); // Limpa o arquivo de log
}
$response['progress'] = $progress;
} else {
$response['progress'] = 0;
}
}
$response['success'] = true;
}
}
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
} catch (\Exception $e) {
http_response_code(500);
$response['error'] = 'Erro ao obter progresso: ' . $e->getMessage();
}
}
} else {
http_response_code(405);
$response['error'] = 'Método não permitido para esta ação.';
}
break;
case 'send_to_dropbox':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$id = $data['id'] ?? null;
$token = $data['token'] ?? null;
if (empty($id) || empty($token)) {
http_response_code(400);
$response['error'] = 'O ID do stream e o token do Dropbox são obrigatórios.';
break;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT converted_path FROM streams WHERE id = :id AND status = 'completed'");
$stmt->execute([':id' => $id]);
$stream = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stream || empty($stream['converted_path'])) {
http_response_code(404);
$response['error'] = 'Arquivo convertido não encontrado ou o stream não está completo.';
break;
}
$filePath = __DIR__ . '/videos/' . $stream['converted_path'];
if (!file_exists($filePath)) {
http_response_code(404);
$response['error'] = 'Arquivo físico não encontrado no servidor.';
break;
}
$app = new \Dropbox\App("", "", $token);
$dropbox = new \Dropbox\Client($app);
$dropboxFileName = '/' . basename($filePath);
$file = $dropbox->upload($filePath, $dropboxFileName, ['autorename' => true]);
$response['success'] = true;
$response['message'] = 'Arquivo enviado com sucesso para o Dropbox!';
$response['file_name'] = $file->getName();
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
} catch (\Dropbox\Exception $e) {
http_response_code(500);
$response['error'] = 'Erro no Dropbox: ' . $e->getMessage();
} catch (Exception $e) {
http_response_code(500);
$response['error'] = 'Ocorreu um erro inesperado: ' . $e->getMessage();
}
} else {
http_response_code(405);
$response['error'] = 'Método não permitido para esta ação.';
}
break;
case 'delete':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$id = $data['id'] ?? null;
if (empty($id)) {
http_response_code(400);
$response['error'] = 'O ID do stream é obrigatório.';
break;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT converted_path FROM streams WHERE id = :id");
$stmt->execute([':id' => $id]);
$stream = $stmt->fetch(PDO::FETCH_ASSOC);
if ($stream && !empty($stream['converted_path'])) {
$filePath = __DIR__ . '/videos/' . $stream['converted_path'];
if (file_exists($filePath)) {
unlink($filePath);
}
}
$stmt = $pdo->prepare("DELETE FROM streams WHERE id = :id");
$stmt->execute([':id' => $id]);
if ($stmt->rowCount() > 0) {
$response['success'] = true;
$response['message'] = 'Vídeo e registro apagados com sucesso!';
} else {
http_response_code(404);
$response['error'] = 'Nenhum vídeo encontrado com este ID.';
}
} catch (PDOException $e) {
http_response_code(500);
$response['error'] = 'Erro no banco de dados: ' . $e->getMessage();
} catch (\Exception $e) {
http_response_code(500);
$response['error'] = 'Ocorreu um erro inesperado: ' . $e->getMessage();
}
} else {
http_response_code(405);
$response['error'] = 'Método não permitido para esta ação.';
}
break;
default:
http_response_code(400);
$response['error'] = 'Ação não especificada ou inválida.';
break;
}
echo json_encode($response);