package services import ( "bookmann/internal/database" "context" "fmt" "path/filepath" "strings" "github.com/jackc/pgx/v5/pgtype" ) type LibraryService struct { db *database.Queries } func NewLibraryService(db *database.Queries) *LibraryService { return &LibraryService{ db: db, } } // Library type definitions and file extensions const ( LibraryTypeEbooks = "ebooks" LibraryTypeComics = "comics" LibraryTypeManga = "manga" ) var AllowedExtensions = map[string][]string{ LibraryTypeEbooks: {".epub", ".pdf", ".mobi", ".azw", ".azw3", ".txt", ".rtf", ".doc", ".docx", ".lit", ".fb2", ".pdb"}, LibraryTypeComics: {".cbz", ".cbr", ".cb7", ".cbt", ".pdf"}, LibraryTypeManga: {".cbz", ".cbr", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"}, } // GetLibraryTypes retrieves all available library types func (s *LibraryService) GetLibraryTypes(ctx context.Context) ([]database.LibraryTypes, error) { return s.db.GetLibraryTypes(ctx) } // CreateLibrary creates a new library with the given parameters func (s *LibraryService) CreateLibrary(ctx context.Context, name, description, libraryType string, adminID pgtype.UUID) (*database.Libraries, error) { // Get library type ID libType, err := s.db.GetLibraryTypeByName(ctx, libraryType) if err != nil { return nil, fmt.Errorf("invalid library type: %w", err) } // Create library library, err := s.db.CreateLibrary(ctx, database.CreateLibraryParams{ Name: name, Description: pgtype.Text{String: description, Valid: true}, LibraryTypeID: libType.ID, CreatedByAdminID: adminID, }) if err != nil { return nil, fmt.Errorf("failed to create library: %w", err) } return &library, nil } // GetLibrary retrieves a library by ID with type information func (s *LibraryService) GetLibrary(ctx context.Context, libraryID pgtype.UUID) (*database.GetLibraryRow, error) { library, err := s.db.GetLibrary(ctx, libraryID) if err != nil { return nil, err } return &library, nil } // ListLibraries retrieves all libraries func (s *LibraryService) ListLibraries(ctx context.Context) ([]database.ListLibrariesRow, error) { return s.db.ListLibraries(ctx) } // UpdateLibrary updates an existing library func (s *LibraryService) UpdateLibrary(ctx context.Context, libraryID pgtype.UUID, name, description string) (*database.Libraries, error) { library, err := s.db.UpdateLibrary(ctx, database.UpdateLibraryParams{ ID: libraryID, Name: name, Description: pgtype.Text{String: description, Valid: true}, }) if err != nil { return nil, err } return &library, nil } // DeleteLibrary deletes a library and all its associated data func (s *LibraryService) DeleteLibrary(ctx context.Context, libraryID pgtype.UUID) error { return s.db.DeleteLibrary(ctx, libraryID) } // AddLibraryFolder adds a folder to a library func (s *LibraryService) AddLibraryFolder(ctx context.Context, libraryID pgtype.UUID, folderPath string) (*database.LibraryFolders, error) { folder, err := s.db.AddLibraryFolder(ctx, database.AddLibraryFolderParams{ LibraryID: libraryID, FolderPath: folderPath, }) if err != nil { return nil, err } return &folder, nil } // GetLibraryFolders retrieves all folders for a library func (s *LibraryService) GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID) ([]database.LibraryFolders, error) { return s.db.GetLibraryFolders(ctx, libraryID) } // DeleteLibraryFolder removes a folder from a library func (s *LibraryService) DeleteLibraryFolder(ctx context.Context, libraryID pgtype.UUID, folderPath string) error { _, err := s.db.DeleteLibraryFolder(ctx, database.DeleteLibraryFolderParams{ LibraryID: libraryID, FolderPath: folderPath, }) return err } // SetLibraryVisibility sets library visibility for a user func (s *LibraryService) SetLibraryVisibility(ctx context.Context, userID, libraryID pgtype.UUID, isVisible bool) (*database.LibraryVisibility, error) { visibility, err := s.db.SetLibraryVisibility(ctx, database.SetLibraryVisibilityParams{ UserID: userID, LibraryID: libraryID, IsVisible: isVisible, }) if err != nil { return nil, err } return &visibility, nil } // GetUserVisibleLibraries retrieves all libraries visible to a user func (s *LibraryService) GetUserVisibleLibraries(ctx context.Context, userID pgtype.UUID) ([]database.GetUserVisibleLibrariesRow, error) { return s.db.GetUserVisibleLibraries(ctx, userID) } // IsFileExtensionAllowed checks if a file extension is allowed for a library type func (s *LibraryService) IsFileExtensionAllowed(libraryType, extension string) bool { extensions, exists := AllowedExtensions[libraryType] if !exists { return false } for _, ext := range extensions { if strings.EqualFold(ext, extension) { return true } } return false } // GetLibraryFileExtensions returns all allowed file extensions for a library type func (s *LibraryService) GetLibraryFileExtensions(libraryType string) []string { extensions, exists := AllowedExtensions[libraryType] if !exists { return []string{} } return extensions } // GetLibraryTypeFromFileExtension determines the library type based on file extension func (s *LibraryService) GetLibraryTypeFromFileExtension(filename string) string { ext := strings.ToLower(filepath.Ext(filename)) for libType, extensions := range AllowedExtensions { for _, allowedExt := range extensions { if ext == allowedExt { return libType } } } return "" } // ValidateLibraryPath checks if a path is valid for the given library type func (s *LibraryService) ValidateLibraryPath(libraryType, folderPath string) error { // You could add more validation here like: // - Check if path exists // - Check if path is readable // - Validate path format for specific library types // - Check for appropriate file structures (e.g., for manga with image folders) return nil } // GetLibraryStats returns statistics for a library (media count, etc.) func (s *LibraryService) GetLibraryStats(ctx context.Context, libraryID pgtype.UUID) (map[string]interface{}, error) { // For now, return basic info. This can be expanded with more detailed stats mediaItems, err := s.db.ListMediaItemsByLibrary(ctx, libraryID) if err != nil { return nil, err } return map[string]interface{}{ "media_count": len(mediaItems), }, nil }