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 '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 { $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; $duration = (float)$stream['duration']; $progress = $duration > 0 ? round(($processed_ms / $duration) * 100) : 0; $progress = min(100, $progress); $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]); if (file_exists($progress_log_path)) unlink($progress_log_path); } $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 DropboxApp("", "", $token); $dropbox = new Dropbox($app); $dropboxFileName = '/' . basename($filePath); $dropboxFile = new DropboxFile($filePath); $file = $dropbox->upload($dropboxFile, $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 (DropboxClientException $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);