# API Reference ## Overview meta-fuse exposes a REST API for FUSE operations, VFS management, and service discovery. The API server is built with Fastify and listens on port 3000 by default. **Base URL**: `http://localhost:3000` --- ## Health & Status ### GET /health Health check endpoint. **Response**: ```json { "status": "ok", "timestamp": "2024-01-15T10:30:00.000Z", "service": "meta-fuse" } ``` ### GET /api/health Alias for `/health`. ### GET /api/fuse/health Alias for `/health`. ### GET /api/fuse/stats Get VFS statistics and state information. **Response**: ```json { "fileCount": 5123, "directoryCount": 847, "totalSize": 15847392847362, "lastRefresh": "2024-01-15T10:30:00.000Z", "categories": { "Movies": 1523, "TV": 2847, "Anime": 753 }, "kvConnection": "connected", "fuseMount": "mounted" } ``` --- ## FUSE Operations ### POST /api/fuse/readdir List contents of a virtual directory. **Request Body**: ```json { "path": "/Movies/Action" } ``` **Response** (200 OK): ```json { "entries": [ "Die Hard (1988)", "Mad Max Fury Road (2015)", "The Dark Knight (2008)" ] } ``` **Response** (404 Not Found): ```json { "error": "Directory not found" } ``` ### POST /api/fuse/getattr Get file or directory attributes. **Request Body**: ```json { "path": "/Movies/Action/Die Hard (1988)/Die Hard.mkv" } ``` **Response** (200 OK) - File: ```json { "size": 4831838208, "mode": "file", "permissions": 644, "uid": 1000, "gid": 1000, "atime": "2024-01-15T10:30:00Z", "mtime": "2024-01-15T10:30:00Z", "ctime": "2024-01-15T10:30:00Z", "sourcePath": "/files/watch/downloads/Die.Hard.1988.1080p.mkv" } ``` **Response** (200 OK) - Directory: ```json { "mode": "directory", "permissions": 755, "uid": 1000, "gid": 1000 } ``` **Response** (404 Not Found): ```json { "error": "Path not found" } ``` ### POST /api/fuse/exists Check if a path exists in the VFS. **Request Body**: ```json { "path": "/Movies/Action/Die Hard (1988)" } ``` **Response**: ```json { "exists": true } ``` ### POST /api/fuse/read Get information for reading a file. Returns source path and optionally WebDAV URL. **Request Body**: ```json { "path": "/Movies/Action/Die Hard (1988)/Die Hard.mkv" } ``` **Response** (200 OK) - Regular file: ```json { "sourcePath": "/files/watch/downloads/Die.Hard.1988.1080p.mkv", "size": 4831838208 } ``` **Response** (200 OK) - File with WebDAV access: ```json { "sourcePath": "/files/watch/downloads/Die.Hard.1988.1080p.mkv", "size": 4831838208, "webdavUrl": "http://meta-core/webdav/watch/downloads/Die.Hard.1988.1080p.mkv" } ``` **Response** (200 OK) - Virtual file with content: ```json { "sourcePath": null, "size": 256, "content": "VGhpcyBpcyBhIHZpcnR1YWwgZmlsZQ==", "contentEncoding": "base64" } ``` **Response** (404 Not Found): ```json { "error": "File not found" } ``` ### POST /api/fuse/metadata Get full metadata for a virtual path. **Request Body**: ```json { "path": "/Movies/Action/Die Hard (1988)/Die Hard.mkv" } ``` **Response** (200 OK): ```json { "hashId": "bafkreiabc123...", "filePath": "watch/downloads/Die.Hard.1988.1080p.mkv", "title": "Die Hard", "year": "1988", "fileType": "video", "size": "4831838208", "extension": "mkv", "video/codec": "h265", "video/resolution": "1920x1080", "audio/codec": "aac", "audio/channels": "6" } ``` **Response** (404 Not Found): ```json { "error": "Metadata not found" } ``` ### GET /api/fuse/files List all virtual files in the VFS. **Response**: ```json { "files": [ "/Movies/Action/Die Hard (1988)/Die Hard.mkv", "/Movies/Action/Mad Max Fury Road (2015)/Mad Max Fury Road.mkv", "/TV/Breaking Bad/Season 01/S01E01 - Pilot.mkv" ] } ``` ### GET /api/fuse/directories List all virtual directories in the VFS. **Response**: ```json { "directories": [ "/Movies", "/Movies/Action", "/Movies/Action/Die Hard (1988)", "/TV", "/TV/Breaking Bad", "/TV/Breaking Bad/Season 01" ] } ``` ### POST /api/fuse/refresh Trigger a VFS refresh. Re-reads metadata and rebuilds virtual paths. **Response**: ```json { "status": "ok" } ``` --- ## Renaming Rules ### GET /api/fuse/rules Get the current renaming rules configuration. **Response**: ```json { "config": { "rules": [ { "name": "Movies", "enabled": true, "priority": 100, "template": "/Movies/{genre}/{title} ({year})/{fileName}", "conditions": { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" }, { "field": "mediaType", "operator": "equals", "value": "movie" } ] } }, { "name": "TV Shows", "enabled": true, "priority": 90, "template": "/TV/{title}/Season {season:pad2}/{title} - S{season:pad2}E{episode:pad2}.{extension}", "conditions": { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" }, { "field": "mediaType", "operator": "equals", "value": "tv" } ] } } ], "defaultRule": { "name": "Default", "template": "/Other/{fileName}", "conditions": { "operator": "and", "conditions": [] } }, "lastModified": "2024-01-15T10:30:00Z" }, "lastModified": "2024-01-15T10:30:00Z" } ``` ### PUT /api/fuse/rules Update the renaming rules configuration. Automatically triggers VFS refresh. **Request Body**: ```json { "config": { "rules": [ { "name": "Movies", "enabled": true, "priority": 100, "template": "/Movies/{title} ({year})/{fileName}", "conditions": { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" } ] } } ] } } ``` **Response** (200 OK): ```json { "success": true, "refreshed": true } ``` **Response** (400 Bad Request) - Validation errors: ```json { "success": false, "errors": [ "Rule \"Movies\": Unknown variable 'unknownVar' in template", "Rule \"TV Shows\": Invalid condition operator 'matches'" ] } ``` ### POST /api/fuse/rules/preview Preview how files would be renamed with the given rules. **Request Body**: ```json { "rules": [ { "name": "Movies", "template": "/New Movies/{title} ({year})/{fileName}", "conditions": { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" } ] } } ], "limit": 10 } ``` **Response**: ```json { "previews": [ { "hashId": "bafkreiabc123...", "currentPath": "/Movies/Action/Die Hard (1988)/Die Hard.mkv", "newPath": "/New Movies/Die Hard (1988)/Die Hard.mkv", "ruleName": "Movies" }, { "hashId": "bafkreixyz789...", "currentPath": "/TV/Breaking Bad/Season 01/S01E01.mkv", "newPath": null, "ruleName": null, "reason": "No matching rule" } ], "total": 5123, "sampled": 10 } ``` ### POST /api/fuse/rules/validate Validate a single renaming rule. **Request Body**: ```json { "rule": { "name": "Test Rule", "template": "/Test/{title|originalTitle} ({year})/{fileName}", "conditions": { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" } ] } }, "sampleMetadata": { "title": "Inception", "originalTitle": "Inception", "year": "2010", "fileName": "Inception.mkv" } } ``` **Response** (valid rule): ```json { "valid": true, "errors": [], "warnings": [], "sampleOutput": "/Test/Inception (2010)/Inception.mkv" } ``` **Response** (invalid rule): ```json { "valid": false, "errors": [ "Unknown variable 'unknownField' in template" ], "warnings": [ "Variable 'director' is rarely available in metadata" ], "sampleOutput": null } ``` ### GET /api/fuse/rules/variables Get list of available template variables. **Response**: ```json { "variables": [ { "name": "title", "description": "Media title", "example": "Inception" }, { "name": "originalTitle", "description": "Original language title", "example": "Inception" }, { "name": "year", "description": "Release year", "example": "2010" }, { "name": "season", "description": "Season number (TV shows)", "example": "1", "modifiers": ["pad2", "pad3"] }, { "name": "episode", "description": "Episode number (TV shows)", "example": "5", "modifiers": ["pad2", "pad3"] }, { "name": "fileName", "description": "Original filename", "example": "Movie.2024.1080p.mkv" }, { "name": "extension", "description": "File extension", "example": "mkv" }, { "name": "fileType", "description": "Type of file", "example": "video" }, { "name": "genre", "description": "Primary genre", "example": "Action" } ] } ``` --- ## Service Discovery ### GET /api/services List all discovered MetaMesh services. **Response**: ```json { "services": [ { "name": "meta-sort", "url": "http://metasort-app", "api": "http://metasort-app/api", "status": "healthy", "role": "writer" }, { "name": "meta-fuse", "url": "http://metafuse-app", "api": "http://metafuse-app/api", "status": "healthy", "role": "reader" }, { "name": "meta-stremio", "url": "http://metastremio-app", "api": "http://metastremio-app/api", "status": "healthy", "role": "reader" } ], "current": "meta-fuse", "leader": { "hostname": "metacore-app", "baseUrl": "http://localhost:8083", "apiUrl": "http://meta-core:9000", "redisUrl": "redis://meta-core:6379", "webdavUrl": "http://localhost:8083/webdav" } } ``` --- ## Error Responses All endpoints may return these error responses: ### 400 Bad Request Missing or invalid parameters. ```json { "error": "Missing or invalid \"path\" parameter" } ``` ### 404 Not Found Resource not found. ```json { "error": "File not found" } ``` ### 500 Internal Server Error Server-side error. ```json { "error": "Failed to process request" } ``` --- ## Template Syntax ### Basic Variables ``` {variableName} → Replaced with metadata value {title} → "Inception" {year} → "2010" ``` ### Variable with Fallback ``` {title|originalTitle} → Uses title, falls back to originalTitle {genre|"Unknown"} → Uses genre, falls back to literal "Unknown" ``` ### Variable Modifiers ``` {season:pad2} → "01" (zero-padded to 2 digits) {episode:pad3} → "001" (zero-padded to 3 digits) {title:lower} → "inception" (lowercase) {title:upper} → "INCEPTION" (uppercase) ``` ### Condition Operators | Operator | Description | Example | |----------|-------------|---------| | `equals` | Exact match | `{ "field": "fileType", "operator": "equals", "value": "video" }` | | `notEquals` | Not equal | `{ "field": "fileType", "operator": "notEquals", "value": "subtitle" }` | | `contains` | Contains substring | `{ "field": "title", "operator": "contains", "value": "Star" }` | | `startsWith` | Starts with | `{ "field": "fileName", "operator": "startsWith", "value": "[" }` | | `endsWith` | Ends with | `{ "field": "extension", "operator": "endsWith", "value": "mkv" }` | | `exists` | Field exists | `{ "field": "season", "operator": "exists" }` | | `notExists` | Field missing | `{ "field": "season", "operator": "notExists" }` | | `greaterThan` | Numeric > | `{ "field": "year", "operator": "greaterThan", "value": "2000" }` | | `lessThan` | Numeric < | `{ "field": "season", "operator": "lessThan", "value": "10" }` | ### Condition Groups ```json { "operator": "and", "conditions": [ { "field": "fileType", "operator": "equals", "value": "video" }, { "operator": "or", "conditions": [ { "field": "mediaType", "operator": "equals", "value": "movie" }, { "field": "mediaType", "operator": "equals", "value": "tv" } ] } ] } ``` --- ## Related Documentation - [README](../README.md) - Service overview - [VFS Rebuild Architecture](vfs-rebuild-architecture.md) - State management - [Streaming Architecture](streaming-architecture.md) - Event processing