# Video Metadata Guide for VideoTools ## Overview This guide covers adding custom metadata fields to video files, NFO generation, and integration with VideoTools modules. --- ## πŸ“¦ Container Format Metadata Capabilities ### MP4 / MOV (MPEG-4) **Metadata storage:** Atoms in `moov` container **Standard iTunes-compatible tags:** ``` Β©nam - Title Β©ART - Artist Β©alb - Album Β©day - Year Β©gen - Genre Β©cmt - Comment desc - Description Β©too - Encoding tool Β©enc - Encoded by cprt - Copyright ``` **Custom tags (with proper keys):** ``` ----:com.apple.iTunes:DIRECTOR - Director ----:com.apple.iTunes:PERFORMERS - Performers ----:com.apple.iTunes:STUDIO - Studio/Production ----:com.apple.iTunes:SERIES - Series name ----:com.apple.iTunes:SCENE - Scene number ----:com.apple.iTunes:CATEGORIES - Categories/Tags ``` **Setting metadata with FFmpeg:** ```bash ffmpeg -i input.mp4 -c copy \ -metadata title="Scene Title" \ -metadata artist="Performer Name" \ -metadata album="Series Name" \ -metadata date="2025" \ -metadata genre="Category" \ -metadata comment="Scene description" \ -metadata description="Full scene info" \ output.mp4 ``` **Custom fields:** ```bash ffmpeg -i input.mp4 -c copy \ -metadata:s:v:0 custom_field="Custom Value" \ output.mp4 ``` --- ### MKV (Matroska) **Metadata storage:** Tags element (XML-based) **Built-in tag support:** ```xml TITLE Scene Title ARTIST Performer Name DIRECTOR Director Name STUDIO Production Studio PERFORMERS Performer 1, Performer 2 SCENE_NUMBER EP042 CATEGORIES Cat1, Cat2, Cat3 ``` **Setting metadata with FFmpeg:** ```bash ffmpeg -i input.mkv -c copy \ -metadata title="Scene Title" \ -metadata artist="Performer Name" \ -metadata director="Director" \ -metadata studio="Studio Name" \ output.mkv ``` **Advantages of MKV:** - Unlimited custom tags (any key-value pairs) - Can attach files (NFO, images, scripts) - Hierarchical metadata structure - Best for archival/preservation --- ### MOV (QuickTime) Same as MP4 (both use MPEG-4 structure), but QuickTime supports additional proprietary tags. --- ## πŸ“„ NFO File Format NFO (Info) files are plain text/XML files that contain detailed metadata. Common in media libraries (Kodi, Plex, etc.). ### NFO Format for Movies: ```xml Scene Title Original Title Sort Title 2025 2025-12-04 Scene description and plot summary 45 Production Studio Director Name Performer 1 Role 1 path/to/performer1.jpg Performer 2 Role 2 Category 1 Category 2 Tag1 Tag2 8.5 9.0 Series Name 42 EP042 ``` ### NFO Format for TV Episodes: ```xml Episode Title Series Name 1 5 2025-12-04 Episode description 30 Director Name Performer 1 Character Production Studio 8.0 ``` --- ## πŸ› οΈ VideoTools Integration Plan ### Module: **Metadata Editor** (New Module) **Purpose:** Edit video metadata and generate NFO files **Features:** 1. **Load video** β†’ Extract existing metadata 2. **Edit fields** β†’ Standard + custom fields 3. **NFO generation** β†’ Auto-generate from metadata 4. **Embed metadata** β†’ Write back to video file (lossless remux) 5. **Batch metadata** β†’ Apply same metadata to multiple files 6. **Templates** β†’ Save/load metadata templates **UI Layout:** ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ < METADATA β”‚ ← Purple header β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ File: scene_042.mp4 β”‚ β”‚ β”‚ β”‚ β”Œβ”€ Basic Info ──────────────────────────────┐ β”‚ β”‚ β”‚ Title: [________________] β”‚ β”‚ β”‚ β”‚ Studio: [________________] β”‚ β”‚ β”‚ β”‚ Series: [________________] β”‚ β”‚ β”‚ β”‚ Scene #: [____] β”‚ β”‚ β”‚ β”‚ Date: [2025-12-04] β”‚ β”‚ β”‚ β”‚ Duration: 45:23 (auto) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€ Performers ────────────────────────────────┐ β”‚ β”‚ β”‚ Performer 1: [________________] [X] β”‚ β”‚ β”‚ β”‚ Performer 2: [________________] [X] β”‚ β”‚ β”‚ β”‚ [+ Add Performer] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€ Categories/Tags ──────────────────────────┐ β”‚ β”‚ β”‚ [Tag1] [Tag2] [Tag3] [+ Add] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€ Description ────────────────────────────────┐ β”‚ β”‚ β”‚ [Multiline text area for plot/description] β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€ Custom Fields ────────────────────────────┐ β”‚ β”‚ β”‚ Director: [________________] β”‚ β”‚ β”‚ β”‚ IMDB ID: [________________] β”‚ β”‚ β”‚ β”‚ Custom 1: [________________] β”‚ β”‚ β”‚ β”‚ [+ Add Field] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ [Generate NFO] [Embed in Video] [Save Template]β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ”§ Implementation Details ### 1. Reading Metadata **Using FFprobe:** ```bash ffprobe -v quiet -print_format json -show_format input.mp4 # Output includes: { "format": { "filename": "input.mp4", "tags": { "title": "Scene Title", "artist": "Performer Name", "album": "Series Name", "date": "2025", "genre": "Category", "comment": "Description" } } } ``` **Go implementation:** ```go type VideoMetadata struct { Title string Studio string Series string SceneNumber string Date string Performers []string Director string Categories []string Description string CustomFields map[string]string } func probeMetadata(path string) (*VideoMetadata, error) { cmd := exec.Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", path, ) output, err := cmd.Output() if err != nil { return nil, err } var result struct { Format struct { Tags map[string]string `json:"tags"` } `json:"format"` } json.Unmarshal(output, &result) metadata := &VideoMetadata{ Title: result.Format.Tags["title"], Studio: result.Format.Tags["studio"], Series: result.Format.Tags["album"], Date: result.Format.Tags["date"], Categories: strings.Split(result.Format.Tags["genre"], ", "), Description: result.Format.Tags["comment"], CustomFields: make(map[string]string), } return metadata, nil } ``` --- ### 2. Writing Metadata **Using FFmpeg (lossless remux):** ```go func embedMetadata(inputPath string, metadata *VideoMetadata, outputPath string) error { args := []string{ "-i", inputPath, "-c", "copy", // Lossless copy } // Add standard tags if metadata.Title != "" { args = append(args, "-metadata", fmt.Sprintf("title=%s", metadata.Title)) } if metadata.Studio != "" { args = append(args, "-metadata", fmt.Sprintf("studio=%s", metadata.Studio)) } if metadata.Series != "" { args = append(args, "-metadata", fmt.Sprintf("album=%s", metadata.Series)) } if metadata.Date != "" { args = append(args, "-metadata", fmt.Sprintf("date=%s", metadata.Date)) } if len(metadata.Categories) > 0 { args = append(args, "-metadata", fmt.Sprintf("genre=%s", strings.Join(metadata.Categories, ", "))) } if metadata.Description != "" { args = append(args, "-metadata", fmt.Sprintf("comment=%s", metadata.Description)) } // Add custom fields for key, value := range metadata.CustomFields { args = append(args, "-metadata", fmt.Sprintf("%s=%s", key, value)) } args = append(args, outputPath) cmd := exec.Command("ffmpeg", args...) return cmd.Run() } ``` --- ### 3. Generating NFO ```go func generateNFO(metadata *VideoMetadata, videoPath string) (string, error) { nfo := ` ` + escapeXML(metadata.Title) + ` ` + escapeXML(metadata.Studio) + ` ` + escapeXML(metadata.Series) + ` ` + metadata.Date + ` ` + escapeXML(metadata.Description) + ` ` // Add performers for _, performer := range metadata.Performers { nfo += ` ` + escapeXML(performer) + ` ` } // Add categories/genres for _, category := range metadata.Categories { nfo += ` ` + escapeXML(category) + ` ` } // Add custom fields for key, value := range metadata.CustomFields { nfo += ` <` + key + `>` + escapeXML(value) + ` ` } nfo += `` // Save to file (same name as video + .nfo extension) nfoPath := strings.TrimSuffix(videoPath, filepath.Ext(videoPath)) + ".nfo" return nfoPath, os.WriteFile(nfoPath, []byte(nfo), 0644) } func escapeXML(s string) string { s = strings.ReplaceAll(s, "&", "&") s = strings.ReplaceAll(s, "<", "<") s = strings.ReplaceAll(s, ">", ">") s = strings.ReplaceAll(s, "\"", """) s = strings.ReplaceAll(s, "'", "'") return s } ``` --- ### 4. Attaching NFO to MKV MKV supports embedded attachments (like NFO files): ```bash # Attach NFO file to MKV mkvpropedit video.mkv --add-attachment scene_info.nfo --attachment-mime-type text/plain --attachment-name "scene_info.nfo" # Or with FFmpeg (re-mux required) ffmpeg -i input.mkv -i scene_info.nfo -c copy \ -attach scene_info.nfo -metadata:s:t:0 mimetype=text/plain \ output.mkv ``` **Go implementation:** ```go func attachNFOtoMKV(mkvPath string, nfoPath string) error { cmd := exec.Command("mkvpropedit", mkvPath, "--add-attachment", nfoPath, "--attachment-mime-type", "text/plain", "--attachment-name", filepath.Base(nfoPath), ) return cmd.Run() } ``` --- ## πŸ“‹ Metadata Templates Allow users to save metadata templates for batch processing. **Template JSON:** ```json { "name": "Studio XYZ Default Template", "fields": { "studio": "Studio XYZ", "series": "Series Name", "categories": ["Category1", "Category2"], "custom_fields": { "director": "John Doe", "producer": "Jane Smith" } } } ``` **Usage:** 1. User creates template with common studio/series info 2. Load template when editing new video 3. Only fill in unique fields (title, performers, date, scene #) 4. Batch apply template to multiple files --- ## 🎯 Use Cases ### 1. Adult Content Library ``` Title: "Scene Title" Studio: "Production Studio" Series: "Series Name - Season 2" Scene Number: "EP042" Performers: ["Performer A", "Performer B"] Director: "Director Name" Categories: ["Category1", "Category2", "Category3"] Date: "2025-12-04" Description: "Full scene description and plot" ``` ### 2. Personal Video Archive ``` Title: "Birthday Party 2025" Event: "John's 30th Birthday" Location: "Los Angeles, CA" People: ["John", "Sarah", "Mike", "Emily"] Date: "2025-06-15" Description: "John's surprise birthday party" ``` ### 3. Movie Collection ``` Title: "Movie Title" Original Title: "原鑌" Director: "Christopher Nolan" Year: "2024" IMDB ID: "tt1234567" Actors: ["Actor 1", "Actor 2"] Genre: ["Sci-Fi", "Thriller"] Rating: "8.5/10" ``` --- ## πŸ”Œ Integration with Existing Modules ### Convert Module - **Checkbox**: "Preserve metadata" (default: on) - **Checkbox**: "Copy metadata from source" (default: on) - Allow adding/editing metadata before conversion ### Inspect Module - **Add tab**: "Metadata" to view/edit metadata - Show both standard and custom fields - Quick edit without re-encoding ### Compare Module - **Add**: "Compare Metadata" button - Show metadata diff between two files - Highlight differences --- ## πŸš€ Implementation Roadmap ### Phase 1: Basic Metadata Support (Week 1) - Read metadata with ffprobe - Display in Inspect module - Edit basic fields (title, artist, date, comment) - Write metadata with FFmpeg (lossless) ### Phase 2: NFO Generation (Week 2) - NFO file generation - Save alongside video file - Load NFO and populate fields - Template system ### Phase 3: Advanced Metadata (Week 3) - Custom fields support - Performers list - Categories/tags - Metadata Editor module UI ### Phase 4: Batch & Templates (Week 4) - Metadata templates - Batch apply to multiple files - MKV attachment support (embed NFO) --- ## πŸ“š References ### FFmpeg Metadata Documentation - https://ffmpeg.org/ffmpeg-formats.html#Metadata - https://wiki.multimedia.cx/index.php/FFmpeg_Metadata ### NFO Format Standards - Kodi NFO: https://kodi.wiki/view/NFO_files - Plex Agents: https://support.plex.tv/articles/ ### Matroska Tags - https://www.matroska.org/technical/specs/tagging/index.html --- ## βœ… Summary **Yes, you can absolutely store custom metadata in video files!** **Best format for rich metadata:** MKV (unlimited custom tags + file attachments) **Most compatible:** MP4/MOV (iTunes tags work in QuickTime, VLC, etc.) **Recommended approach for VideoTools:** 1. Support both embedded metadata (in video file) AND sidecar NFO files 2. MKV: Embed NFO as attachment + metadata tags 3. MP4: Metadata tags + separate .nfo file 4. Allow users to choose what metadata to embed 5. Generate NFO for media center compatibility (Kodi, Plex, Jellyfin) **Next steps:** 1. Add basic metadata reading to `probeVideo()` function 2. Add metadata display to Inspect module 3. Create Metadata Editor module 4. Implement NFO generation 5. Add metadata templates This would be a killer feature for VideoTools! πŸš€