#include "media/plugin.hpp" #include #include #include #include #include #include namespace media { namespace plugins { /** * Example Custom Plugin * * This demonstrates how to create a custom plugin for the media daemon. * Plugins are loaded dynamically at runtime from shared libraries. * * Build with: * g++ -shared -fPIC -o my_custom_plugin.so my_custom_plugin.cpp -std=c++17 */ class MyCustomPlugin : public Plugin { public: MyCustomPlugin() = default; ~MyCustomPlugin() override = default; PluginInfo info() const override { return PluginInfo{ .id = "my_custom_plugin", .name = "My Custom Plugin", .version = "1.0.0", .author = "Your Name", .description = "Example custom plugin for media processing", .type = PluginType::PROCESSOR, .supported_formats = {"txt", "json", "xml"}, .capabilities = {"text_transform", "json_validate"}, .is_loaded = initialized_, .is_builtin = false }; } PluginCapabilities capabilities() const override { PluginCapabilities caps; caps.supports_video = false; caps.supports_audio = false; caps.supports_image = false; caps.supports_document = true; caps.supports_streaming = false; caps.supports_hardware_accel = false; caps.input_formats = {"txt", "json", "xml"}; caps.output_formats = {"txt", "json", "xml"}; return caps; } Result initialize(const std::string& config_path) override { std::cout << "[MyCustomPlugin] Initializing with config: " << config_path << std::endl; // Load configuration if provided if (!config_path.empty()) { std::ifstream config_file(config_path); if (config_file.is_open()) { // Parse config... config_file.close(); } } initialized_ = true; return Result::ok(); } void shutdown() override { std::cout << "[MyCustomPlugin] Shutting down" << std::endl; initialized_ = false; } bool is_healthy() const override { return initialized_; } bool can_handle(JobType type, const JobParams& params) const override { // This plugin only handles custom jobs if (type != JobType::CUSTOM) { return false; } // Check if params contain our supported operation if (auto* custom_params = std::get_if>(¶ms)) { auto it = custom_params->find("operation"); if (it != custom_params->end()) { return it->second == "text_transform" || it->second == "json_validate"; } } return false; } Result process( const JobRequest& request, JobProgressCallback progress_callback ) override { if (!initialized_) { return Result::error( ErrorCode::SERVICE_UNAVAILABLE, "Plugin not initialized" ); } // Extract custom params auto* custom_params = std::get_if>(&request.params); if (!custom_params) { return Result::error( ErrorCode::VALIDATION_ERROR, "Invalid parameters for custom plugin" ); } std::string operation = (*custom_params)["operation"]; std::string input_path = (*custom_params)["input_path"]; std::string output_path = (*custom_params)["output_path"]; // Report starting if (progress_callback) { progress_callback(request.id, JobProgress{ .percent = 0.0, .stage = "starting", .eta = "calculating..." }); } // Process based on operation Result result; if (operation == "text_transform") { result = process_text_transform(input_path, output_path, *custom_params, request.id, progress_callback); } else if (operation == "json_validate") { result = process_json_validate(input_path, output_path, *custom_params, request.id, progress_callback); } else { return Result::error( ErrorCode::VALIDATION_ERROR, "Unknown operation: " + operation ); } // Report completion if (progress_callback) { progress_callback(request.id, JobProgress{ .percent = 100.0, .stage = "completed" }); } return result; } Result cancel(const std::string& job_id) override { // Mark job as cancelled std::lock_guard lock(jobs_mutex_); auto it = active_jobs_.find(job_id); if (it != active_jobs_.end()) { it->second = true; // cancelled return Result::ok(); } return Result::error(ErrorCode::NOT_FOUND, "Job not found"); } private: Result process_text_transform( const std::string& input_path, const std::string& output_path, const std::map& params, const std::string& job_id, JobProgressCallback progress_callback ) { // Read input file std::ifstream input(input_path); if (!input.is_open()) { return Result::error( ErrorCode::NOT_FOUND, "Cannot open input file: " + input_path ); } std::stringstream buffer; buffer << input.rdbuf(); std::string content = buffer.str(); input.close(); if (progress_callback) { progress_callback(job_id, JobProgress{.percent = 30.0, .stage = "processing"}); } // Get transform type std::string transform = "uppercase"; auto it = params.find("transform"); if (it != params.end()) { transform = it->second; } // Apply transformation if (transform == "uppercase") { for (char& c : content) { c = std::toupper(c); } } else if (transform == "lowercase") { for (char& c : content) { c = std::tolower(c); } } else if (transform == "reverse") { std::reverse(content.begin(), content.end()); } if (progress_callback) { progress_callback(job_id, JobProgress{.percent = 70.0, .stage = "writing"}); } // Write output file std::ofstream output(output_path); if (!output.is_open()) { return Result::error( ErrorCode::STORAGE_ERROR, "Cannot create output file: " + output_path ); } output << content; output.close(); return Result::ok(output_path); } Result process_json_validate( const std::string& input_path, const std::string& output_path, const std::map& params, const std::string& job_id, JobProgressCallback progress_callback ) { // Read input file std::ifstream input(input_path); if (!input.is_open()) { return Result::error( ErrorCode::NOT_FOUND, "Cannot open input file: " + input_path ); } std::stringstream buffer; buffer << input.rdbuf(); std::string content = buffer.str(); input.close(); if (progress_callback) { progress_callback(job_id, JobProgress{.percent = 50.0, .stage = "validating"}); } // Simple JSON validation (check for balanced braces) int brace_count = 0; int bracket_count = 0; bool in_string = false; bool escaped = false; for (char c : content) { if (escaped) { escaped = false; continue; } if (c == '\\') { escaped = true; continue; } if (c == '"') { in_string = !in_string; continue; } if (!in_string) { if (c == '{') brace_count++; else if (c == '}') brace_count--; else if (c == '[') bracket_count++; else if (c == ']') bracket_count--; } } bool valid = (brace_count == 0 && bracket_count == 0 && !in_string); // Write validation result std::ofstream output(output_path); if (!output.is_open()) { return Result::error( ErrorCode::STORAGE_ERROR, "Cannot create output file: " + output_path ); } output << "{\n"; output << " \"valid\": " << (valid ? "true" : "false") << ",\n"; output << " \"input_file\": \"" << input_path << "\",\n"; output << " \"brace_balance\": " << brace_count << ",\n"; output << " \"bracket_balance\": " << bracket_count << "\n"; output << "}\n"; output.close(); if (!valid) { return Result::error( ErrorCode::VALIDATION_ERROR, "JSON validation failed" ); } return Result::ok(output_path); } bool initialized_ = false; std::mutex jobs_mutex_; std::map active_jobs_; // job_id -> cancelled }; } // namespace plugins } // namespace media // Export plugin functions MEDIA_PLUGIN_EXPORT(media::plugins::MyCustomPlugin)