From aab7a0ae31e1e455b0d06cc2ae2463d91c65ef31 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 8 Feb 2026 14:05:20 -0500 Subject: [PATCH] docs: add comprehensive media scanner cleanup plan - Add detailed line-by-line plan for renaming EbookScanner to MediaScanner - Include database cleanup (remove unused backward compatibility functions) - Cover all test files, handlers, routers, and documentation - 9 phases with specific file/line references for safe implementation - Includes verification steps and rollback plan --- COMPLETE_MEDIA_CLEANUP_PLAN.md | 1181 ++++++++++++++++++++++++++++++++ 1 file changed, 1181 insertions(+) create mode 100644 COMPLETE_MEDIA_CLEANUP_PLAN.md diff --git a/COMPLETE_MEDIA_CLEANUP_PLAN.md b/COMPLETE_MEDIA_CLEANUP_PLAN.md new file mode 100644 index 0000000..558ee13 --- /dev/null +++ b/COMPLETE_MEDIA_CLEANUP_PLAN.md @@ -0,0 +1,1181 @@ +# Complete Media Scanner Renaming & Legacy Cleanup Plan + +## Overview + +This comprehensive plan renames all legacy "ebook" naming to "media" terminology and removes unused backward compatibility code. Since the app has never been deployed, we can safely remove all legacy artifacts without migration concerns. + +**Scope:** +- **File Renames:** 5 files (handlers, services, tests) +- **Type Renames:** 2 types (EbookScanner, EbookMetadata) +- **Method Renames:** 5 methods + all receivers +- **Code Removal:** 1 unused method + 3 unused DB functions +- **Variable Renames:** 2 variables +- **Test Helper Rename:** 1 function +- **Documentation Updates:** All references +- **Bruno Updates:** Environment variables and descriptions + +**Critical Constraints (from PROJECT_GUIDELINES.md):** +- Never break existing functionality +- Post-edit verification mandatory (compile after each file) +- Use cascading fix-up pattern prohibition +- Multiple logical git commits +- Verify no functionality loss + +--- + +## Pre-Implementation Safety Steps + +### Step 1: Create Safety Backup +```bash +# Create a backup branch before any changes +git branch backup-before-cleanup-$(date +%Y%m%d) + +# Verify clean working tree +git status + +# Run tests to establish baseline +make test + +# Build to ensure everything compiles +go build ./cmd/server +``` + +### Step 2: Document Current State +```bash +# List all files to be modified +git status --short > /tmp/pre-cleanup-status.txt + +# Count current test coverage +go test ./... -cover 2>&1 | grep -E "coverage|ok|FAIL" > /tmp/pre-coverage.txt +``` + +--- + +## Phase 1: Remove Unused Database Functions + +### Phase 1.1: Remove from queries.sql + +**File:** `internal/database/queries/queries.sql` + +**Lines 473-487** - Remove entire EbookNote query section: + +```sql +-- BEFORE (Lines 473-487): +-- Backward compatibility - Ebook Notes queries (using views) +-- name: CreateEbookNote :one +INSERT INTO media_notes (media_item_id, user_id, content, position) +VALUES ($1, $2, $3, $4) +RETURNING id, media_item_id, user_id, content, position, created_at, updated_at, percentage_location, character_start, character_end, epubcfi_location, chapter_reference, paragraph_reference, device_sync_data; + +-- name: UpdateEbookNote :one +UPDATE media_notes +SET content = $2, position = $3, updated_at = NOW() +WHERE id = $1 +RETURNING id, media_item_id, user_id, content, position, created_at, updated_at, percentage_location, character_start, character_end, epubcfi_location, chapter_reference, paragraph_reference, device_sync_data; + +-- name: DeleteEbookNote :exec +DELETE FROM media_notes WHERE id = $1; + +-- AFTER: +-- (Remove all of the above - use CreateMediaNote, UpdateMediaNote, DeleteMediaNote instead) +``` + +### Phase 1.2: Regenerate SQLC Code + +```bash +# After editing queries.sql, regenerate the Go code +sqlc generate + +# Or if using Makefile: +make generate +``` + +### Phase 1.3: Verify Removal + +```bash +# Check that old functions are gone +grep -n "CreateEbookNote\|UpdateEbookNote\|DeleteEbookNote" internal/database/queries.sql.go +# Expected: No results (or only in historical comments) + +# Check that new functions still exist +grep -n "CreateMediaNote\|UpdateMediaNote\|DeleteMediaNote" internal/database/queries.sql.go +# Expected: Should find the Media versions + +# Compile to verify +go build ./internal/database +``` + +### Phase 1.4: Remove from Querier Interface + +**File:** `internal/database/querier.go` + +**Lines 52-53, 98, 286** - Remove interface methods: + +```go +// BEFORE (Line 52-53): +// Backward compatibility - Ebook Notes queries (using views) +CreateEbookNote(ctx context.Context, arg CreateEbookNoteParams) (MediaNotes, error) + +// BEFORE (Line 98): +DeleteEbookNote(ctx context.Context, id pgtype.UUID) error + +// BEFORE (Line 286): +UpdateEbookNote(ctx context.Context, arg UpdateEbookNoteParams) (MediaNotes, error) + +// AFTER: Remove these lines completely +``` + +### Phase 1.5: Verification + +```bash +# Ensure database package compiles +go build ./internal/database + +# Run database tests +go test ./internal/database/... -v + +# Full build verification +go build ./cmd/server +``` + +--- + +## Phase 2: Rename Service Layer + +### Phase 2.1: Rename ebook_scanner.go to media_scanner.go + +**File:** `internal/services/ebook_scanner.go` → `internal/services/media_scanner.go` + +```bash +git mv internal/services/ebook_scanner.go internal/services/media_scanner.go +``` + +### Phase 2.2: Update Type Definitions + +**Line 36-52** - Rename EbookMetadata to MediaMetadata: + +```go +// BEFORE: +type EbookMetadata struct { + Title string + Author string + Description string + Series string + SeriesNumber int32 + Publisher string + PublishDate time.Time + Contributors []string + CoverPath string + ISBN string + ASIN string + Tags []string + + Phase1HashInfo *HashInfo + Phase1FormatFormats []*FormatInfo +} + +// AFTER: +// MediaMetadata contains extracted metadata for media files (ebooks, comics, manga) +type MediaMetadata struct { + Title string + Author string + Description string + Series string + SeriesNumber int32 + Publisher string + PublishDate time.Time + Contributors []string + CoverPath string + ISBN string + ASIN string + Tags []string + + Phase1HashInfo *HashInfo + Phase1FormatFormats []*FormatInfo +} +``` + +**Line 69-76** - Rename EbookScanner to MediaScanner: + +```go +// BEFORE: +type EbookScanner struct { + db *database.Queries + watcher *fsnotify.Watcher + folders []string + adminID pgtype.UUID + defaultLibraryID pgtype.UUID + libraryTypes map[string][]string +} + +// AFTER: +// MediaScanner scans library folders for media files (ebooks, comics, manga) +type MediaScanner struct { + db *database.Queries + watcher *fsnotify.Watcher + folders []string + adminID pgtype.UUID + defaultLibraryID pgtype.UUID + libraryTypes map[string][]string +} +``` + +### Phase 2.3: Update Constructor + +**Line 78** - Rename NewEbookScanner: + +```go +// BEFORE: +func NewEbookScanner(db *database.Queries) *EbookScanner { + +// AFTER: +// NewMediaScanner creates a new media scanner instance +func NewMediaScanner(db *database.Queries) *MediaScanner { +``` + +**Line 84** - Update return statement: + +```go +// BEFORE: +return &EbookScanner{ + +// AFTER: +return &MediaScanner{ +``` + +### Phase 2.4: Update All Method Receivers + +Change all method receivers from `(s *EbookScanner)` to `(s *MediaScanner)`: + +**Line 94:** `SetAdminID` +**Line 98:** `SetFolders` +**Line 148:** `ScanFolders` +**Line 205:** `isEbookFile` (being removed) +**Line 215:** `isScannableFile` +**Line 251:** `extractFolderStructureMetadata` +**Line 311:** `processEbookFile` → `processMediaFile` +**Line 521:** `extractMetadata` +**Line 537:** `extractEPUBMetadata` +**Line 623:** `extractPDFMetadata` +**Line 944:** `updateEbook` → `updateMediaItem` +**Line 949:** `getEbookByFilePath` → `getMediaItemByFilePath` +**Line 953:** `getMimeType` +**Line 981:** `WatchChanges` +**Line 1023:** `Close` +**Line 1035:** `calculateFileSHA256` +**Line 1066:** `extractOPFIdentifiers` +**Line 1121:** `extractISBNFromIdentifier` +**Line 1141:** `determineHashConfidence` +**Line 1152:** `detectFormatType` +**Line 1185:** `extractHashInfo` + +### Phase 2.5: Remove Unused Method + +**Line 205-213** - Remove isEbookFile method: + +```go +// BEFORE (Lines 205-213): +func (s *EbookScanner) isEbookFile(path string) bool { + ext := strings.ToLower(filepath.Ext(path)) + switch ext { + case ".epub", ".pdf", ".mobi", ".azw3", ".fb2", ".txt": + return true + default: + return false + } +} + +// AFTER: Remove this entire method +``` + +### Phase 2.6: Rename Methods + +**Line 251** - Update extractFolderStructureMetadata return type: + +```go +// BEFORE: +func (s *EbookScanner) extractFolderStructureMetadata(path, rootFolder string) *EbookMetadata { + metadata := &EbookMetadata{} + +// AFTER: +func (s *MediaScanner) extractFolderStructureMetadata(path, rootFolder string) *MediaMetadata { + metadata := &MediaMetadata{} +``` + +**Line 311** - Rename processEbookFile to processMediaFile: + +```go +// BEFORE: +func (s *EbookScanner) processEbookFile(ctx context.Context, path string) error { + fmt.Printf("Processing ebook file: %s\n", path) + +// AFTER: +func (s *MediaScanner) processMediaFile(ctx context.Context, path string) error { + fmt.Printf("Processing media file: %s\n", path) +``` + +**Lines 323-339** - Update variable names and print statements: + +```go +// BEFORE: +// Check if ebook already exists in database +existingEbook, err := s.getEbookByFilePath(ctx, path) +if err == nil { + fmt.Printf("Ebook already exists in database: %s (size: %d vs %d)\n", path, existingEbook.FileSize.Int64, info.Size()) + // Ebook exists, check if file has changed (by size) + if existingEbook.FileSize.Int64 != info.Size() { + fmt.Printf("File size changed, updating ebook: %s\n", path) + return s.updateEbook(ctx, existingEbook.ID, path, info) + } + fmt.Printf("Ebook already exists with same size, skipping: %s\n", path) + +// AFTER: +// Check if media item already exists in database +existingItem, err := s.getMediaItemByFilePath(ctx, path) +if err == nil { + fmt.Printf("Media item already exists in database: %s (size: %d vs %d)\n", path, existingItem.FileSize.Int64, info.Size()) + // Media item exists, check if file has changed (by size) + if existingItem.FileSize.Int64 != info.Size() { + fmt.Printf("File size changed, updating media item: %s\n", path) + return s.updateMediaItem(ctx, existingItem.ID, path, info) + } + fmt.Printf("Media item already exists with same size, skipping: %s\n", path) +``` + +**Line 345** - Update metadata initialization: + +```go +// BEFORE: +metadata = &EbookMetadata{} + +// AFTER: +metadata = &MediaMetadata{} +``` + +**Line 521** - Update extractMetadata return type: + +```go +// BEFORE: +func (s *EbookScanner) extractMetadata(path string) (*EbookMetadata, error) { + +// AFTER: +func (s *MediaScanner) extractMetadata(path string) (*MediaMetadata, error) { +``` + +**Line 531** - Update extractMetadata default return: + +```go +// BEFORE: +return &EbookMetadata{ + +// AFTER: +return &MediaMetadata{ +``` + +**Line 537** - Update extractEPUBMetadata: + +```go +// BEFORE: +func (s *EbookScanner) extractEPUBMetadata(path string) (*EbookMetadata, error) { + metadata := &EbookMetadata{} + +// AFTER: +func (s *MediaScanner) extractEPUBMetadata(path string) (*MediaMetadata, error) { + metadata := &MediaMetadata{} +``` + +**Line 623** - Update extractPDFMetadata: + +```go +// BEFORE: +func (s *EbookScanner) extractPDFMetadata(path string) (*EbookMetadata, error) { + return &EbookMetadata{ + +// AFTER: +func (s *MediaScanner) extractPDFMetadata(path string) (*MediaMetadata, error) { + return &MediaMetadata{ +``` + +**Line 944** - Rename updateEbook to updateMediaItem: + +```go +// BEFORE: +func (s *EbookScanner) updateEbook(ctx context.Context, ebookID pgtype.UUID, filePath string, info os.FileInfo) error { + +// AFTER: +func (s *MediaScanner) updateMediaItem(ctx context.Context, mediaItemID pgtype.UUID, filePath string, info os.FileInfo) error { +``` + +**Line 949** - Rename getEbookByFilePath to getMediaItemByFilePath: + +```go +// BEFORE: +func (s *EbookScanner) getEbookByFilePath(ctx context.Context, filePath string) (database.MediaItems, error) { + +// AFTER: +func (s *MediaScanner) getMediaItemByFilePath(ctx context.Context, filePath string) (database.MediaItems, error) { +``` + +### Phase 2.7: Update Print Statements in ScanFolders + +**Line 156** - Rename counter variable: + +```go +// BEFORE: +ebookFiles := 0 + +// AFTER: +mediaFiles := 0 +``` + +**Lines 185-201** - Update print statements: + +```go +// BEFORE (Line 185-186): +ebookFiles++ +fmt.Printf("Found ebook file: %s\n", path) + +// AFTER: +mediaFiles++ +fmt.Printf("Found media file: %s\n", path) + +// BEFORE (Line 187-190): +if err := s.processEbookFile(ctx, path); err != nil { + fmt.Printf("Error processing ebook %s: %v\n", path, err) +} else { + fmt.Printf("Successfully processed ebook: %s\n", path) +} + +// AFTER: +if err := s.processMediaFile(ctx, path); err != nil { + fmt.Printf("Error processing media file %s: %v\n", path, err) +} else { + fmt.Printf("Successfully processed media file: %s\n", path) +} + +// BEFORE (Line 201): +fmt.Printf("Scan completed: %d total files scanned, %d ebook files found\n", totalFiles, ebookFiles) + +// AFTER: +fmt.Printf("Scan completed: %d total files scanned, %d media files found\n", totalFiles, mediaFiles) +``` + +### Phase 2.8: Update WatchChanges Method + +**Line 1005-1007** - Update print statements: + +```go +// BEFORE: +fmt.Printf("New/modified ebook detected: %s\n", event.Name) +if err := s.processEbookFile(ctx, event.Name); err != nil { + fmt.Printf("Error processing modified ebook %s: %v\n", event.Name, err) + +// AFTER: +fmt.Printf("New/modified media file detected: %s\n", event.Name) +if err := s.processMediaFile(ctx, event.Name); err != nil { + fmt.Printf("Error processing modified media file %s: %v\n", event.Name, err) +``` + +### Phase 2.9: Verification + +```bash +# Compile services package +go build ./internal/services + +# Run service tests +go test ./internal/services/... -v + +# Check no Ebook references remain +grep -n "EbookScanner\|EbookMetadata" internal/services/media_scanner.go +# Expected: No results +``` + +--- + +## Phase 3: Rename Test Files + +### Phase 3.1: Rename Test Files + +```bash +# Rename all test files +git mv internal/services/ebook_scanner_library_type_test.go internal/services/media_scanner_library_type_test.go +git mv internal/services/ebook_scanner_comic_test.go internal/services/media_scanner_comic_test.go +git mv internal/services/ebook_scanner_hash_test.go internal/services/media_scanner_hash_test.go +``` + +### Phase 3.2: Update Test Function Names + +**File:** `internal/services/media_scanner_library_type_test.go` + +**Line 9-10:** +```go +// BEFORE: +// TestEbookScanner_LibraryTypeAwareScanning tests Phase 2: library-type-aware scanning +func TestEbookScanner_LibraryTypeAwareScanning(t *testing.T) { + +// AFTER: +// TestMediaScanner_LibraryTypeAwareScanning tests Phase 2: library-type-aware scanning +func TestMediaScanner_LibraryTypeAwareScanning(t *testing.T) { +``` + +**Line 124-125:** +```go +// BEFORE: +// TestEbookScanner_SetFolders_BuildsLibraryTypeCache tests Phase 2: SetFolders builds cache +func TestEbookScanner_SetFolders_BuildsLibraryTypeCache(t *testing.T) { + +// AFTER: +// TestMediaScanner_SetFolders_BuildsLibraryTypeCache tests Phase 2: SetFolders builds cache +func TestMediaScanner_SetFolders_BuildsLibraryTypeCache(t *testing.T) { +``` + +**Line 153-154:** +```go +// BEFORE: +// TestEbookScanner_LibraryTypeCrossContamination tests Phase 2: prevents cross-contamination +func TestEbookScanner_LibraryTypeCrossContamination(t *testing.T) { + +// AFTER: +// TestMediaScanner_LibraryTypeCrossContamination tests Phase 2: prevents cross-contamination +func TestMediaScanner_LibraryTypeCrossContamination(t *testing.T) { +``` + +**Line 188-189:** +```go +// BEFORE: +// TestEbookScanner_MultipleLibraryTypes tests Phase 2: multiple libraries with different types +func TestEbookScanner_MultipleLibraryTypes(t *testing.T) { + +// AFTER: +// TestMediaScanner_MultipleLibraryTypes tests Phase 2: multiple libraries with different types +func TestMediaScanner_MultipleLibraryTypes(t *testing.T) { +``` + +**File:** `internal/services/media_scanner_hash_test.go` + +**Line 425:** +```go +// BEFORE: +func ExampleEbookScanner_calculateFileSHA256() { + +// AFTER: +func ExampleMediaScanner_calculateFileSHA256() { +``` + +### Phase 3.3: Verification + +```bash +# Run all service tests +go test ./internal/services/... -v + +# Verify no old test function names +grep -r "TestEbookScanner" internal/services/ +# Expected: No results +``` + +--- + +## Phase 4: Rename Handler Layer + +### Phase 4.1: Rename Handler File + +```bash +git mv internal/handlers/ebook.go internal/handlers/scanner.go +``` + +### Phase 4.2: Update Handler Struct + +**File:** `internal/handlers/scanner.go` + +**Line 24** - Update scanner field type: + +```go +// BEFORE: +scanner *services.EbookScanner + +// AFTER: +scanner *services.MediaScanner +``` + +### Phase 4.3: Update Constructor + +**Line 45** - Update scanner initialization: + +```go +// BEFORE: +scanner: services.NewEbookScanner(db), + +// AFTER: +scanner: services.NewMediaScanner(db), +``` + +### Phase 4.4: Update StartScanner Method + +**Line 180** - Update comment: + +```go +// BEFORE: +// Set the admin ID for ebook association + +// AFTER: +// Set the admin ID for media item association +``` + +**Line 242** - Update scanner initialization: + +```go +// BEFORE: +scanner := services.NewEbookScanner(h.db) + +// AFTER: +scanner := services.NewMediaScanner(h.db) +``` + +### Phase 4.5: Verification + +```bash +# Compile handlers package +go build ./internal/handlers + +# Run handler tests +go test ./internal/handlers/... -v +``` + +--- + +## Phase 5: Update Router Layer + +### Phase 5.1: Update Router Comments + +**File:** `internal/router/router.go` + +**Line 135** - Update comment: + +```go +// BEFORE: +// Create ebook handler for scanner routes and progress routes + +// AFTER: +// Create scanner handler for scanner routes and progress routes +``` + +### Phase 5.2: Verification + +```bash +# Compile router package +go build ./internal/router +``` + +--- + +## Phase 6: Update Test Helpers + +### Phase 6.1: Rename Test Helper Function + +**File:** `cmd/server/tests/test_helpers.go` + +**Line 272-273** - Rename function: + +```go +// BEFORE: +// createTestEbookID creates a test ebook and returns its ID +func createTestEbookID(t *testing.T, ts *httptest.Server, token string) string { + +// AFTER: +// createTestMediaItemID creates a test media item and returns its ID +func createTestMediaItemID(t *testing.T, ts *httptest.Server, token string) string { +``` + +**Line 277** - Update comment: + +```go +// BEFORE: +"description": "A test library for ebooks", + +// AFTER: +"description": "A test library for media items", +``` + +**Line 298-301** - Update comment and request: + +```go +// BEFORE: +// Create a test ebook +ebookReq := map[string]interface{}{ + "library_id": libData, + "title": "Test Ebook", + +// AFTER: +// Create a test media item +mediaItemReq := map[string]interface{}{ + "library_id": libData, + "title": "Test Media Item", +``` + +**Line 307** - Update variable name: + +```go +// BEFORE: +ebookBody, _ := json.Marshal(ebookReq) + +// AFTER: +mediaItemBody, _ := json.Marshal(mediaItemReq) +``` + +**Line 309** - Update request: + +```go +// BEFORE: +req2, _ := http.NewRequest("POST", ts.URL+"/api/media-items", bytes.NewBuffer(ebookBody)) + +// AFTER: +req2, _ := http.NewRequest("POST", ts.URL+"/api/media-items", bytes.NewBuffer(mediaItemBody)) +``` + +### Phase 6.2: Update All Test File References + +Update all test files that call `createTestEbookID`: + +**Files to update:** +- `cmd/server/tests/analytics_test.go` +- `cmd/server/tests/book_matching_test.go` +- `cmd/server/tests/collections_bulk_test.go` +- `cmd/server/tests/kobo_test.go` +- `cmd/server/tests/media_bulk_test.go` + +**Example change (in each file):** +```go +// BEFORE: +bookID := createTestEbookID(t, ts, token) + +// AFTER: +bookID := createTestMediaItemID(t, ts, token) +``` + +### Phase 6.3: Verification + +```bash +# Compile test package +go build ./cmd/server/tests + +# Run integration tests (if available) +go test ./cmd/server/tests/... -v +``` + +--- + +## Phase 7: Update Bruno Files + +### Phase 7.1: Update Environment Variable + +**File:** `bruno/environments/Bookmann.bru` + +**Line 3** - Rename variable: + +``` +# BEFORE: +ebook_id: 02a535a4-19f8-43fa-b81b-89a226d19dd9 + +# AFTER: +media_item_id: 02a535a4-19f8-43fa-b81b-89a226d19dd9 +``` + +### Phase 7.2: Update Scanner Files + +**File:** `bruno/scanner/Scan Media Items.bru` + +**Line 20** - Update example path: + +``` +# BEFORE: +"folder_paths": ["/path/to/ebooks"] + +# AFTER: +"folder_paths": ["/path/to/media"] +``` + +**File:** `bruno/scanner/Start Watch Mode.bru` + +**Line 44** - Update documentation: + +``` +# BEFORE: +- Automatically detects new ebook files (CREATE events) + +# AFTER: +- Automatically detects new media files (CREATE events) +``` + +### Phase 7.3: Update Library Files + +**File:** `bruno/library/Update Scan Settings.bru` + +**Line 32** - Update description: + +``` +# BEFORE: +Updates the user's ebook scanning settings. + +# AFTER: +Updates the user's media scanning settings. +``` + +### Phase 7.4: Update Media Items Files + +**File:** `bruno/media-items/Get Media Item.bru` and `List Media Items.bru` + +Update media_type examples if they use "ebook" as a generic term: + +``` +# BEFORE: +- `media_type` (string): Type of media (e.g., "ebook", "audiobook") + +# AFTER (keep examples but make clear it's a type value): +- `media_type` (string): Type of media (e.g., "epub", "pdf", "cbz") +``` + +**Note:** Keep library type references like `"type": "ebooks"` - these are correct library type values. + +### Phase 7.5: Update Collection Documentation + +**File:** `bruno/collection.bru` + +**Lines 175-176** - Update references: + +``` +# BEFORE: +- **Media Items vs Books**: The API uses "media items" (supports ebooks, comics, manga) + +# AFTER: +- **Media Items**: The API uses "media items" (supports ebooks, comics, manga) +``` + +--- + +## Phase 8: Update Documentation + +### Phase 8.1: Update Contributing Documentation + +**File:** `docs/contributing/development.md` + +**Line 35** - Update file reference: + +```markdown +# BEFORE: +- `ebook.go` - Media item operations + +# AFTER: +- `scanner.go` - Media scanning operations +``` + +**Line 66** - Update file reference: + +```markdown +# BEFORE: +- `ebook_scanner.go` - File scanning & metadata extraction + +# AFTER: +- `media_scanner.go` - File scanning & metadata extraction +``` + +### Phase 8.2: Update API Documentation + +**Files:** +- `docs/developer/api/scanner/scan_library.md` +- `docs/developer/api/scanner/start_scanner.md` +- `docs/developer/api/scanner/start_watch_mode.md` +- `docs/developer/api/scanner/overview.md` + +**Changes needed:** +- Update any "ebook scanning" → "media scanning" +- Keep specific format mentions ("EPUB files", "comic files") where accurate +- Keep library type "ebooks" where it's a type value + +### Phase 8.3: Update API Reference + +**File:** `docs/developer/api/api-reference.md` + +**Line 12** - Update reference: + +```markdown +# BEFORE: +- [Media Items](media-items/) - Book/ebook operations + +# AFTER: +- [Media Items](media-items/) - Media item operations +``` + +### Phase 8.4: Remove Historical Comments from Schema + +**File:** `database/schema/schema.sql` + +Find and remove historical migration comments: + +```sql +-- BEFORE: +-- Create media_items table (replaces ebooks table for broader media support) + +-- AFTER: +-- Create media_items table +``` + +```sql +-- BEFORE: +-- Create media_ratings table (replaces ebook_ratings) + +-- AFTER: +-- Create media_ratings table +``` + +```sql +-- BEFORE: +-- NOTE: All backward compatibility views (ebooks, ebook_ratings, ebook_reading_progress, ebook_notes, ebook_highlights) have been removed + +-- AFTER (remove this line entirely - no longer relevant): +``` + +### Phase 8.5: Remove Comments from Queries + +**File:** `internal/database/queries/queries.sql` + +**Line 354** - Remove or update: + +```sql +-- BEFORE: +-- Note: User ebook folders replaced by library folders system + +-- AFTER (remove - this is historical): +``` + +--- + +## Phase 9: Final Verification + +### Phase 9.1: Global Search for Remaining References + +```bash +# Search for any remaining problematic references +echo "=== Checking for EbookScanner ===" +grep -r "EbookScanner" --include="*.go" . | grep -v ".git" + +echo "=== Checking for EbookMetadata ===" +grep -r "EbookMetadata" --include="*.go" . | grep -v ".git" + +echo "=== Checking for ebook_scanner ===" +grep -r "ebook_scanner" --include="*.go" . | grep -v ".git" + +echo "=== Checking for isEbookFile ===" +grep -r "isEbookFile" --include="*.go" . | grep -v ".git" + +echo "=== Checking for processEbookFile ===" +grep -r "processEbookFile" --include="*.go" . | grep -v ".git" + +echo "=== Checking for createTestEbookID ===" +grep -r "createTestEbookID" --include="*.go" . | grep -v ".git" + +# These should return nothing after completion +``` + +### Phase 9.2: Full Build Verification + +```bash +# Clean build +go clean -cache +go build ./cmd/server + +# Run all tests +make test + +# Run specific package tests +go test ./internal/services/... -v +go test ./internal/handlers/... -v +go test ./cmd/server/tests/... -v +``` + +### Phase 9.3: Integration Test + +```bash +# Start the application +make build + +# Verify it runs +# (Manual check: Application should start without errors) +``` + +--- + +## Git Commit Strategy + +### Commit 1: Remove Unused Database Functions +```bash +git add internal/database/queries/queries.sql +git add internal/database/querier.go +git add internal/database/queries.sql.go # regenerated +git commit -m "chore(database): remove unused EbookNote backward compatibility functions + +- Remove CreateEbookNote, UpdateEbookNote, DeleteEbookNote queries +- These were marked as backward compatibility but never used +- API uses CreateMediaNote, UpdateMediaNote, DeleteMediaNote instead +- Remove misleading backward compatibility comments +- Regenerate sqlc code" +``` + +### Commit 2: Rename Service Layer +```bash +git add internal/services/media_scanner.go +git add internal/services/ebook_scanner.go # deletion +git commit -m "refactor(services): rename EbookScanner to MediaScanner + +- Rename EbookScanner struct to MediaScanner +- Rename EbookMetadata struct to MediaMetadata +- Rename NewEbookScanner to NewMediaScanner +- Rename processEbookFile to processMediaFile +- Rename updateEbook to updateMediaItem +- Rename getEbookByFilePath to getMediaItemByFilePath +- Remove unused isEbookFile method +- Update all method receivers +- Update variable names (ebookFiles → mediaFiles, existingEbook → existingItem) +- Update print statements to use 'media' terminology +- File renamed: ebook_scanner.go -> media_scanner.go" +``` + +### Commit 3: Rename Service Test Files +```bash +git add internal/services/media_scanner_*_test.go +git add internal/services/ebook_scanner_*_test.go # deletions +git commit -m "test(services): rename test files for MediaScanner + +- Rename ebook_scanner_library_type_test.go -> media_scanner_library_type_test.go +- Rename ebook_scanner_comic_test.go -> media_scanner_comic_test.go +- Rename ebook_scanner_hash_test.go -> media_scanner_hash_test.go +- Update test function names (TestEbookScanner* -> TestMediaScanner*)" +``` + +### Commit 4: Rename Handler Layer +```bash +git add internal/handlers/scanner.go +git add internal/handlers/ebook.go # deletion +git commit -m "refactor(handlers): rename ebook.go to scanner.go + +- Rename file: ebook.go -> scanner.go +- Update scanner field type to *services.MediaScanner +- Update NewMediaScanner call +- Update comments to use 'media' terminology +- File renamed: ebook.go -> scanner.go" +``` + +### Commit 5: Update Router and Test Helpers +```bash +git add internal/router/router.go +git add cmd/server/tests/test_helpers.go +git add cmd/server/tests/*_test.go +git commit -m "refactor(tests): update test helpers and router comments + +- Rename createTestEbookID to createTestMediaItemID +- Update all test file references to use new function name +- Update test helper comments and variable names +- Update router comments (ebook handler -> scanner handler)" +``` + +### Commit 6: Update Bruno Files +```bash +git add bruno/ +git commit -m "docs(bruno): update API test files for media terminology + +- Rename ebook_id environment variable to media_item_id +- Update scanner endpoint documentation +- Update media scanning descriptions +- Keep library type 'ebooks' where appropriate (valid type value)" +``` + +### Commit 7: Update Documentation +```bash +git add docs/ +git add database/schema/schema.sql +git commit -m "docs: update documentation for media scanner naming + +- Update development.md with new file names +- Update API documentation to use 'media' terminology +- Remove historical migration comments from schema +- Update scanner documentation references" +``` + +### Commit 8: Final Cleanup (if needed) +```bash +git add -A +git commit -m "chore: final cleanup after media scanner rename + +- Remove any remaining legacy references +- Verify all tests pass +- Clean up comments" +``` + +--- + +## Rollback Plan + +If anything goes wrong, rollback immediately: + +```bash +# If you created a backup branch +git checkout backup-before-cleanup-$(date +%Y%m%d) + +# Or reset to before changes +git reset --hard HEAD~N # where N is number of commits to undo + +# Or restore specific files +git checkout HEAD -- internal/services/ebook_scanner.go +``` + +--- + +## Success Criteria + +✅ All files renamed correctly +✅ All types renamed (EbookScanner→MediaScanner, EbookMetadata→MediaMetadata) +✅ All methods renamed with updated receivers +✅ Unused code removed (isEbookFile, CreateEbookNote, etc.) +✅ All variables renamed (ebookFiles→mediaFiles, etc.) +✅ Test helper renamed (createTestEbookID→createTestMediaItemID) +✅ All test references updated +✅ Bruno files updated +✅ Documentation updated +✅ No legacy "ebook" references in code (except library type 'ebooks') +✅ Application builds successfully +✅ All tests pass +✅ Clean git history with logical commits + +--- + +## Notes for AI Implementation + +1. **Verify compilation after EVERY file edit** +2. **Use specific, narrow matches** - avoid broad regex +3. **Read context (20 lines before/after)** before editing +4. **If compilation fails, STOP and analyze** with `git diff` +5. **Commit frequently** - after each logical unit of work +6. **Test files are critical** - don't skip them +7. **This is a pure refactor** - no behavior changes +8. **Library type 'ebooks' is correct** - don't change it +9. **MIME types are correct** - don't change them +10. **When in doubt, preserve functionality** + +--- + +## Post-Implementation Verification Checklist + +- [ ] All 5 files renamed +- [ ] 2 types renamed +- [ ] 5+ methods renamed +- [ ] 1 unused method removed +- [ ] 3 unused DB functions removed +- [ ] 2+ variables renamed +- [ ] Test helper renamed +- [ ] Bruno files updated +- [ ] Documentation updated +- [ ] Build succeeds: `go build ./cmd/server` +- [ ] Tests pass: `make test` +- [ ] No EbookScanner references remain +- [ ] No EbookMetadata references remain +- [ ] Git commits are clean and logical +- [ ] Working tree is clean