first commit

This commit is contained in:
sanya
2025-09-01 14:20:39 +00:00
committed by ExternPointer
commit 490fc11f6a
4328 changed files with 1796224 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
#pragma once
#include <memory>
#include <utility>
#include <thread>
#include <atomic>
#include <chrono>
#include <httplib.h>
#include <mutex>
#include "AudioWriters/OggAudioWriter.h"
#include "ConfigService.h"
namespace snoop {
class AudioWriterService {
std::shared_ptr<ConfigService> m_configService;
std::shared_ptr<OggAudioWriter> m_oggWriter;
std::string m_destinationDirectoryPath;
std::string m_queueDirectoryPath;
std::thread m_writingThread;
std::thread m_uploadThread;
std::mutex m_fetchFilePathsMutex;
unsigned long long int m_currentRecordStartedAt = 0;
std::string m_currentRecordFilePath;
std::atomic<bool> m_isIntermission = false;
public:
explicit AudioWriterService( std::shared_ptr<ConfigService> configService, std::string destinationDirectoryPath ) :
m_configService( std::move( configService ) ),
m_destinationDirectoryPath( std::move( destinationDirectoryPath ) ),
m_oggWriter( std::make_shared<OggAudioWriter>( 48000, 1 ) ) {
if( !this->m_destinationDirectoryPath.empty() && !this->m_destinationDirectoryPath.ends_with( "/" ) ) {
this->m_destinationDirectoryPath.append( "/" );
}
this->m_queueDirectoryPath = this->m_destinationDirectoryPath + "queue/";
std::filesystem::create_directories( this->m_queueDirectoryPath );
this->MoveToQueueUncompletedRecords();
this->m_writingThread = std::thread( [this]() {
this->WritingThread();
} );
this->m_uploadThread = std::thread( [this]() {
this->UploadThread();
} );
spdlog::info( "AudioWriterService::AudioWriterService()" );
}
void WriteAudioData( const char* data, size_t size, size_t frames ) {
this->m_oggWriter->Write( data, size, frames );
}
~AudioWriterService() {
this->m_isIntermission = true;
this->m_writingThread.join();
this->m_uploadThread.join();
}
private:
void MoveToQueueUncompletedRecords() {
std::vector<std::filesystem::path> files;
for( const auto& entry: std::filesystem::directory_iterator( this->m_destinationDirectoryPath ) ) {
files.push_back( entry.path() );
}
for( const auto& file: files ) {
if( file.filename().string() != "queue" ) {
spdlog::info( "Move uncompleted record {} to queue", file.filename().string() );
this->MoveToUploadQueue( file.string() );
}
}
}
void WritingThread() {
auto now = std::chrono::system_clock::now();
this->m_currentRecordStartedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
this->m_currentRecordFilePath = this->m_destinationDirectoryPath + std::to_string( this->m_currentRecordStartedAt );
this->m_oggWriter->StartWriting( this->m_currentRecordFilePath );
while( !m_isIntermission ) {
now = std::chrono::system_clock::now();
auto currentRecordDuration = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count() -
this->m_currentRecordStartedAt;
if( currentRecordDuration >= this->m_configService->GetRecordingDuration() ) {
this->m_oggWriter->StopWriting();
this->MoveToUploadQueue( this->m_currentRecordFilePath );
this->m_currentRecordStartedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
this->m_currentRecordFilePath = this->m_destinationDirectoryPath + std::to_string( this->m_currentRecordStartedAt );
this->m_oggWriter->StartWriting( this->m_currentRecordFilePath );
}
std::this_thread::sleep_for( std::chrono::milliseconds( 1000 ) );
}
this->m_oggWriter->StopWriting();
this->MoveToUploadQueue( this->m_currentRecordFilePath );
// TODO: Move to upload queue
}
void MoveToUploadQueue( const std::string& filePath ) {
spdlog::info( "AudioWriterService::MoveToUploadQueue( {} )", filePath );
std::lock_guard lock( this->m_fetchFilePathsMutex );
auto now = std::chrono::system_clock::now();
auto recordStoppedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
if( std::filesystem::exists( filePath ) ) {
auto fileName = std::filesystem::path( filePath ).filename().string() + "-" + std::to_string( recordStoppedAt );
std::filesystem::rename( filePath, m_queueDirectoryPath + fileName );
}
}
void UploadThread() {
while (!m_isIntermission) {
std::vector<std::filesystem::path> files;
{
std::lock_guard l(this->m_fetchFilePathsMutex);
try {
for (const auto& entry : std::filesystem::directory_iterator(this->m_queueDirectoryPath)) {
files.push_back(entry.path());
}
} catch (const std::exception& e) {
spdlog::error("Error reading queue directory: {}", e.what());
}
}
for (const auto& filePath : files) {
auto fileName = filePath.filename().string();
spdlog::info("Processing file: {}", fileName);
size_t delimiterPos = fileName.find('-');
if (delimiterPos != std::string::npos) {
std::string startedAt = fileName.substr(0, delimiterPos);
std::string stoppedAt = fileName.substr(delimiterPos + 1);
try {
spdlog::info("Attempting to upload file...");
if (SendRecordedFile(filePath.string(), stoull(startedAt), stoull(stoppedAt))) {
spdlog::info("File uploaded, deleting: {}", filePath.string());
std::filesystem::remove(filePath);
} else {
spdlog::warn("Failed to upload file: {}", filePath.string());
}
} catch (const std::exception& e) {
spdlog::error("Exception during file upload: {}", e.what());
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
bool SendRecordedFile( const std::string& filepath, unsigned long long int startedAt, unsigned long long stoppedAt ) {
spdlog::info( "SendRecordedFile: {}", filepath );
httplib::Client client( this->m_configService->GetBaseUrl() );
std::ifstream ifs( filepath, std::ios::binary );
if( !ifs ) {
throw std::runtime_error( "Failed to open file" );
}
std::vector<char> buffer( ( std::istreambuf_iterator<char>( ifs ) ), ( std::istreambuf_iterator<char>() ) );
auto res = client.Post(
std::string( "/records/upload/" ),
httplib::MultipartFormDataItems{
{ "file", std::string( buffer.begin(), buffer.end() ), "file.ogg", "audio/ogg" },
{ "guid", this->m_configService->GetGuid() },
{ "startedAt", std::to_string( startedAt ) },
{ "stoppedAt", std::to_string( stoppedAt ) },
} );
if( res && res->status == 201 ) {
spdlog::info( "File uploaded successfully" );
return true;
}
spdlog::error( "Failed to upload file" );
return false;
}
};
}