- 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
29 KiB
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
# 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
# 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:
-- 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
# After editing queries.sql, regenerate the Go code
sqlc generate
# Or if using Makefile:
make generate
Phase 1.3: Verify Removal
# 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:
// 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
# 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
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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// BEFORE:
metadata = &EbookMetadata{}
// AFTER:
metadata = &MediaMetadata{}
Line 521 - Update extractMetadata return type:
// BEFORE:
func (s *EbookScanner) extractMetadata(path string) (*EbookMetadata, error) {
// AFTER:
func (s *MediaScanner) extractMetadata(path string) (*MediaMetadata, error) {
Line 531 - Update extractMetadata default return:
// BEFORE:
return &EbookMetadata{
// AFTER:
return &MediaMetadata{
Line 537 - Update extractEPUBMetadata:
// 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:
// 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:
// 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:
// 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:
// BEFORE:
ebookFiles := 0
// AFTER:
mediaFiles := 0
Lines 185-201 - Update print statements:
// 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:
// 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
# 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
# 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:
// 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:
// 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:
// 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:
// 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:
// BEFORE:
func ExampleEbookScanner_calculateFileSHA256() {
// AFTER:
func ExampleMediaScanner_calculateFileSHA256() {
Phase 3.3: Verification
# 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
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:
// BEFORE:
scanner *services.EbookScanner
// AFTER:
scanner *services.MediaScanner
Phase 4.3: Update Constructor
Line 45 - Update scanner initialization:
// BEFORE:
scanner: services.NewEbookScanner(db),
// AFTER:
scanner: services.NewMediaScanner(db),
Phase 4.4: Update StartScanner Method
Line 180 - Update comment:
// BEFORE:
// Set the admin ID for ebook association
// AFTER:
// Set the admin ID for media item association
Line 242 - Update scanner initialization:
// BEFORE:
scanner := services.NewEbookScanner(h.db)
// AFTER:
scanner := services.NewMediaScanner(h.db)
Phase 4.5: Verification
# 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:
// BEFORE:
// Create ebook handler for scanner routes and progress routes
// AFTER:
// Create scanner handler for scanner routes and progress routes
Phase 5.2: Verification
# 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:
// 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:
// BEFORE:
"description": "A test library for ebooks",
// AFTER:
"description": "A test library for media items",
Line 298-301 - Update comment and request:
// 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:
// BEFORE:
ebookBody, _ := json.Marshal(ebookReq)
// AFTER:
mediaItemBody, _ := json.Marshal(mediaItemReq)
Line 309 - Update request:
// 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.gocmd/server/tests/book_matching_test.gocmd/server/tests/collections_bulk_test.gocmd/server/tests/kobo_test.gocmd/server/tests/media_bulk_test.go
Example change (in each file):
// BEFORE:
bookID := createTestEbookID(t, ts, token)
// AFTER:
bookID := createTestMediaItemID(t, ts, token)
Phase 6.3: Verification
# 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:
# BEFORE:
- `ebook.go` - Media item operations
# AFTER:
- `scanner.go` - Media scanning operations
Line 66 - Update file reference:
# 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.mddocs/developer/api/scanner/start_scanner.mddocs/developer/api/scanner/start_watch_mode.mddocs/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:
# 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:
-- BEFORE:
-- Create media_items table (replaces ebooks table for broader media support)
-- AFTER:
-- Create media_items table
-- BEFORE:
-- Create media_ratings table (replaces ebook_ratings)
-- AFTER:
-- Create media_ratings table
-- 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:
-- 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
# 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
# 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
# Start the application
make build
# Verify it runs
# (Manual check: Application should start without errors)
Git Commit Strategy
Commit 1: Remove Unused Database Functions
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
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
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
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
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
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
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)
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:
# 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
- Verify compilation after EVERY file edit
- Use specific, narrow matches - avoid broad regex
- Read context (20 lines before/after) before editing
- If compilation fails, STOP and analyze with
git diff - Commit frequently - after each logical unit of work
- Test files are critical - don't skip them
- This is a pure refactor - no behavior changes
- Library type 'ebooks' is correct - don't change it
- MIME types are correct - don't change them
- 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