feat: implement library system database schema
- Add library_types table with ebooks, comics, manga types - Add libraries table for multiple library support - Add library_folders table for multi-folder libraries - Add library_visibility table for user access control - Add media_items table replacing ebooks for broader media support - Create backward compatibility views for existing API - Implement library service with type validation and file extension handling - Support modular extension for future media types Manga type includes cbz/cbr archives as requested
This commit is contained in:
+118
-32
@@ -1,5 +1,51 @@
|
||||
-- Consolidated Bookmann Database Schema
|
||||
-- This file contains the complete current schema for the Bookmann ebook management system
|
||||
-- This file contains the complete current schema for the Bookmann media library management system
|
||||
|
||||
-- Create library types table
|
||||
CREATE TABLE library_types (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(50) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
allowed_extensions TEXT[] NOT NULL, -- Array of allowed file extensions for this type
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Insert default library types
|
||||
INSERT INTO library_types (name, description, allowed_extensions) VALUES
|
||||
('ebooks', 'Ebook files including EPUB, PDF, MOBI, etc.', ARRAY['.epub', '.pdf', '.mobi', '.azw', '.azw3', '.txt', '.rtf', '.doc', '.docx', '.lit', '.fb2', '.pdb']),
|
||||
('comics', 'Comic book archives and image formats', ARRAY['.cbz', '.cbr', '.cb7', '.cbt', '.pdf']),
|
||||
('manga', 'Manga files including archives and image folders', ARRAY['.cbz', '.cbr', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']);
|
||||
|
||||
-- Create libraries table
|
||||
CREATE TABLE libraries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
library_type_id UUID NOT NULL REFERENCES library_types(id) ON DELETE RESTRICT,
|
||||
created_by_admin_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create library_folders table for multiple folders per library
|
||||
CREATE TABLE library_folders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
|
||||
folder_path VARCHAR(500) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(library_id, folder_path)
|
||||
);
|
||||
|
||||
-- Create library_visibility table for user-specific library visibility
|
||||
CREATE TABLE library_visibility (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
|
||||
is_visible BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(user_id, library_id)
|
||||
);
|
||||
|
||||
-- Create users table
|
||||
CREATE TABLE users (
|
||||
@@ -17,12 +63,13 @@ CREATE TABLE users (
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create ebooks table
|
||||
CREATE TABLE ebooks (
|
||||
-- Create media_items table (replaces ebooks table for broader media support)
|
||||
CREATE TABLE media_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
author VARCHAR(255),
|
||||
isbn VARCHAR(13),
|
||||
isbn VARCHAR(13), -- Still relevant for ebooks
|
||||
description TEXT,
|
||||
file_path VARCHAR(500) NOT NULL,
|
||||
file_size BIGINT,
|
||||
@@ -31,7 +78,7 @@ CREATE TABLE ebooks (
|
||||
series VARCHAR(255),
|
||||
series_number INTEGER,
|
||||
tags TEXT,
|
||||
asin VARCHAR(20),
|
||||
asin VARCHAR(20), -- Still relevant for ebooks
|
||||
date_published DATE,
|
||||
publisher VARCHAR(255),
|
||||
contributors TEXT,
|
||||
@@ -40,57 +87,96 @@ CREATE TABLE ebooks (
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create ebooks view for backward compatibility
|
||||
CREATE VIEW ebooks AS
|
||||
SELECT mi.*
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
WHERE lt.name = 'ebooks';
|
||||
|
||||
-- Create reading_progress table
|
||||
CREATE TABLE reading_progress (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ebook_id UUID NOT NULL REFERENCES ebooks(id) ON DELETE CASCADE,
|
||||
media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
current_page INTEGER DEFAULT 0,
|
||||
total_pages INTEGER,
|
||||
last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(ebook_id, user_id)
|
||||
UNIQUE(media_item_id, user_id)
|
||||
);
|
||||
|
||||
-- Create ebook_ratings table
|
||||
CREATE TABLE ebook_ratings (
|
||||
-- Create reading_progress view for backward compatibility
|
||||
CREATE VIEW ebook_reading_progress AS
|
||||
SELECT rp.*,
|
||||
mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
|
||||
FROM reading_progress rp
|
||||
JOIN media_items mi ON rp.media_item_id = mi.id
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
WHERE lt.name = 'ebooks';
|
||||
|
||||
-- Create media_ratings table (replaces ebook_ratings)
|
||||
CREATE TABLE media_ratings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ebook_id UUID NOT NULL REFERENCES ebooks(id) ON DELETE CASCADE,
|
||||
media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 10), -- 10-point scale for half-star precision
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(ebook_id, user_id)
|
||||
UNIQUE(media_item_id, user_id)
|
||||
);
|
||||
|
||||
-- Create user_ebook_folders table for multiple folders per user
|
||||
CREATE TABLE user_ebook_folders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
folder_path VARCHAR(500) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(user_id, folder_path)
|
||||
-- Note: Admin-only access is enforced at application level in handlers/auth.go
|
||||
);
|
||||
-- Create ebook_ratings view for backward compatibility
|
||||
CREATE VIEW ebook_ratings AS
|
||||
SELECT mr.*,
|
||||
mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
|
||||
FROM media_ratings mr
|
||||
JOIN media_items mi ON mr.media_item_id = mi.id
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
WHERE lt.name = 'ebooks';
|
||||
|
||||
-- Note: user_ebook_folders table is replaced by library_folders table
|
||||
-- Libraries now handle folder management instead of individual users
|
||||
|
||||
-- Create indexes for better query performance
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_ebooks_title ON ebooks(title);
|
||||
CREATE INDEX idx_ebooks_author ON ebooks(author);
|
||||
CREATE INDEX idx_ebooks_added_by_admin_id ON ebooks(added_by_admin_id);
|
||||
CREATE INDEX idx_reading_progress_ebook_id ON reading_progress(ebook_id);
|
||||
CREATE INDEX idx_library_types_name ON library_types(name);
|
||||
|
||||
-- Library indexes
|
||||
CREATE INDEX idx_libraries_name ON libraries(name);
|
||||
CREATE INDEX idx_libraries_library_type_id ON libraries(library_type_id);
|
||||
CREATE INDEX idx_libraries_created_by_admin_id ON libraries(created_by_admin_id);
|
||||
CREATE INDEX idx_library_folders_library_id ON library_folders(library_id);
|
||||
CREATE INDEX idx_library_visibility_user_id ON library_visibility(user_id);
|
||||
CREATE INDEX idx_library_visibility_library_id ON library_visibility(library_id);
|
||||
|
||||
-- Media items indexes
|
||||
CREATE INDEX idx_media_items_title ON media_items(title);
|
||||
CREATE INDEX idx_media_items_author ON media_items(author);
|
||||
CREATE INDEX idx_media_items_library_id ON media_items(library_id);
|
||||
CREATE INDEX idx_media_items_added_by_admin_id ON media_items(added_by_admin_id);
|
||||
|
||||
-- Progress and ratings indexes
|
||||
CREATE INDEX idx_reading_progress_media_item_id ON reading_progress(media_item_id);
|
||||
CREATE INDEX idx_reading_progress_user_id ON reading_progress(user_id);
|
||||
CREATE INDEX idx_ebook_ratings_ebook_id ON ebook_ratings(ebook_id);
|
||||
CREATE INDEX idx_ebook_ratings_user_id ON ebook_ratings(user_id);
|
||||
CREATE INDEX idx_user_ebook_folders_user_id ON user_ebook_folders(user_id);
|
||||
CREATE INDEX idx_media_ratings_media_item_id ON media_ratings(media_item_id);
|
||||
CREATE INDEX idx_media_ratings_user_id ON media_ratings(user_id);
|
||||
|
||||
-- Add comment explaining the rating system
|
||||
COMMENT ON COLUMN ebook_ratings.rating IS 'Rating scale 1-10 (odd numbers = half-stars: 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars)';
|
||||
COMMENT ON COLUMN media_ratings.rating IS 'Rating scale 1-10 (odd numbers = half-stars: 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars)';
|
||||
|
||||
-- Library Type File Extensions Notes:
|
||||
-- - Ebooks: .epub, .pdf, .mobi, .azw, .azw3, .txt, .rtf, .doc, .docx, .lit, .fb2, .pdb
|
||||
-- - Comics: .cbz, .cbr, .cb7, .cbt, .pdf
|
||||
-- - Manga: .cbz, .cbr, .png, .jpg, .jpeg, .gif, .bmp, .webp (note: manga includes image folders)
|
||||
|
||||
-- Role System Notes:
|
||||
-- - All users default to 'user' role
|
||||
-- - Admin users can: add/edit/delete folders, scan ebooks, modify ebook metadata, delete ebooks
|
||||
-- - Regular users can: view all ebooks, rate ebooks, track reading progress, manage their profile
|
||||
-- - Admin users can: create/manage libraries and folders, scan media, modify media metadata, delete media, control library visibility
|
||||
-- - Regular users can: view visible libraries, rate media, track reading progress, manage their profile
|
||||
-- - To create first admin: UPDATE users SET role = 'admin' WHERE email = 'your-admin-email';
|
||||
-- - Only admins can manage folders and ebook metadata (enforced at application level)
|
||||
-- - user_ebook_folders table admin-only access is enforced by AdminMiddleware in handlers
|
||||
-- - Library visibility is controlled through library_visibility table - admins can hide/show libraries per user
|
||||
-- - Backward compatibility views (ebooks, ebook_ratings, ebook_reading_progress) maintain existing API contracts
|
||||
+81
-10
@@ -10,16 +10,27 @@ import (
|
||||
|
||||
type EbookRatings struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
EbookID pgtype.UUID `db:"ebook_id" json:"ebook_id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
// Rating scale 1-10 (odd numbers = half-stars: 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars)
|
||||
Rating int32 `db:"rating" json:"rating"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
EbookID pgtype.UUID `db:"ebook_id" json:"ebook_id"`
|
||||
}
|
||||
|
||||
type EbookReadingProgress struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
CurrentPage pgtype.Int4 `db:"current_page" json:"current_page"`
|
||||
TotalPages pgtype.Int4 `db:"total_pages" json:"total_pages"`
|
||||
LastReadAt pgtype.Timestamptz `db:"last_read_at" json:"last_read_at"`
|
||||
EbookID pgtype.UUID `db:"ebook_id" json:"ebook_id"`
|
||||
}
|
||||
|
||||
type Ebooks struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Title string `db:"title" json:"title"`
|
||||
Author pgtype.Text `db:"author" json:"author"`
|
||||
Isbn pgtype.Text `db:"isbn" json:"isbn"`
|
||||
@@ -40,22 +51,82 @@ type Ebooks struct {
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type Libraries struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Description pgtype.Text `db:"description" json:"description"`
|
||||
LibraryTypeID pgtype.UUID `db:"library_type_id" json:"library_type_id"`
|
||||
CreatedByAdminID pgtype.UUID `db:"created_by_admin_id" json:"created_by_admin_id"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type LibraryFolders struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
FolderPath string `db:"folder_path" json:"folder_path"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type LibraryTypes struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Description pgtype.Text `db:"description" json:"description"`
|
||||
AllowedExtensions []string `db:"allowed_extensions" json:"allowed_extensions"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type LibraryVisibility struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
IsVisible bool `db:"is_visible" json:"is_visible"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type MediaItems struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Title string `db:"title" json:"title"`
|
||||
Author pgtype.Text `db:"author" json:"author"`
|
||||
Isbn pgtype.Text `db:"isbn" json:"isbn"`
|
||||
Description pgtype.Text `db:"description" json:"description"`
|
||||
FilePath string `db:"file_path" json:"file_path"`
|
||||
FileSize pgtype.Int8 `db:"file_size" json:"file_size"`
|
||||
MimeType pgtype.Text `db:"mime_type" json:"mime_type"`
|
||||
CoverImagePath pgtype.Text `db:"cover_image_path" json:"cover_image_path"`
|
||||
Series pgtype.Text `db:"series" json:"series"`
|
||||
SeriesNumber pgtype.Int4 `db:"series_number" json:"series_number"`
|
||||
Tags pgtype.Text `db:"tags" json:"tags"`
|
||||
Asin pgtype.Text `db:"asin" json:"asin"`
|
||||
DatePublished pgtype.Date `db:"date_published" json:"date_published"`
|
||||
Publisher pgtype.Text `db:"publisher" json:"publisher"`
|
||||
Contributors pgtype.Text `db:"contributors" json:"contributors"`
|
||||
AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type MediaRatings struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
// Rating scale 1-10 (odd numbers = half-stars: 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars)
|
||||
Rating int32 `db:"rating" json:"rating"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type ReadingProgress struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
EbookID pgtype.UUID `db:"ebook_id" json:"ebook_id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
CurrentPage pgtype.Int4 `db:"current_page" json:"current_page"`
|
||||
TotalPages pgtype.Int4 `db:"total_pages" json:"total_pages"`
|
||||
LastReadAt pgtype.Timestamptz `db:"last_read_at" json:"last_read_at"`
|
||||
}
|
||||
|
||||
type UserEbookFolders struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
FolderPath string `db:"folder_path" json:"folder_path"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type Users struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Email string `db:"email" json:"email"`
|
||||
|
||||
@@ -11,33 +11,65 @@ import (
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
AddUserEbookFolder(ctx context.Context, arg AddUserEbookFolderParams) (UserEbookFolders, error)
|
||||
CreateEbook(ctx context.Context, arg CreateEbookParams) (Ebooks, error)
|
||||
CreateEbookRating(ctx context.Context, arg CreateEbookRatingParams) (EbookRatings, error)
|
||||
// Library Folders queries
|
||||
AddLibraryFolder(ctx context.Context, arg AddLibraryFolderParams) (LibraryFolders, error)
|
||||
CreateEbook(ctx context.Context, arg CreateEbookParams) (MediaItems, error)
|
||||
// Backward compatibility - Ebooks ratings (using views)
|
||||
CreateEbookRating(ctx context.Context, arg CreateEbookRatingParams) (MediaRatings, error)
|
||||
// Libraries queries
|
||||
CreateLibrary(ctx context.Context, arg CreateLibraryParams) (Libraries, error)
|
||||
// Media Items queries
|
||||
CreateMediaItem(ctx context.Context, arg CreateMediaItemParams) (MediaItems, error)
|
||||
CreateMediaRating(ctx context.Context, arg CreateMediaRatingParams) (MediaRatings, error)
|
||||
CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error)
|
||||
DeleteEbook(ctx context.Context, id pgtype.UUID) error
|
||||
DeleteEbookRating(ctx context.Context, arg DeleteEbookRatingParams) error
|
||||
DeleteLibrary(ctx context.Context, id pgtype.UUID) error
|
||||
DeleteLibraryFolder(ctx context.Context, arg DeleteLibraryFolderParams) (LibraryFolders, error)
|
||||
DeleteMediaItem(ctx context.Context, id pgtype.UUID) error
|
||||
DeleteMediaRating(ctx context.Context, arg DeleteMediaRatingParams) error
|
||||
DeleteReadingProgress(ctx context.Context, arg DeleteReadingProgressParams) error
|
||||
DeleteUser(ctx context.Context, id pgtype.UUID) error
|
||||
DeleteUserEbookFolder(ctx context.Context, arg DeleteUserEbookFolderParams) (UserEbookFolders, error)
|
||||
// Backward compatibility - Ebooks queries (using views)
|
||||
GetEbook(ctx context.Context, id pgtype.UUID) (Ebooks, error)
|
||||
// Note: User ebook folders replaced by library folders system
|
||||
// Legacy folder management is now handled through libraries
|
||||
GetEbookByFilePath(ctx context.Context, filePath string) (Ebooks, error)
|
||||
GetEbookRating(ctx context.Context, arg GetEbookRatingParams) (EbookRatings, error)
|
||||
GetEbookRatings(ctx context.Context, ebookID pgtype.UUID) ([]GetEbookRatingsRow, error)
|
||||
GetLibrary(ctx context.Context, id pgtype.UUID) (GetLibraryRow, error)
|
||||
GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID) ([]LibraryFolders, error)
|
||||
GetLibraryType(ctx context.Context, id pgtype.UUID) (LibraryTypes, error)
|
||||
GetLibraryTypeByName(ctx context.Context, name string) (LibraryTypes, error)
|
||||
// Library Types queries
|
||||
GetLibraryTypes(ctx context.Context) ([]LibraryTypes, error)
|
||||
GetLibraryVisibility(ctx context.Context, arg GetLibraryVisibilityParams) (LibraryVisibility, error)
|
||||
GetMediaItem(ctx context.Context, id pgtype.UUID) (MediaItems, error)
|
||||
GetMediaItemByFilePath(ctx context.Context, filePath string) (MediaItems, error)
|
||||
GetMediaRating(ctx context.Context, arg GetMediaRatingParams) (MediaRatings, error)
|
||||
GetMediaRatings(ctx context.Context, mediaItemID pgtype.UUID) ([]GetMediaRatingsRow, error)
|
||||
GetReadingProgress(ctx context.Context, arg GetReadingProgressParams) (ReadingProgress, error)
|
||||
GetScanSettings(ctx context.Context, id pgtype.UUID) (GetScanSettingsRow, error)
|
||||
GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error)
|
||||
GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error)
|
||||
GetUserByEmailOrUsername(ctx context.Context, email string) (GetUserByEmailOrUsernameRow, error)
|
||||
GetUserByUsername(ctx context.Context, username string) (GetUserByUsernameRow, error)
|
||||
GetUserEbookFolders(ctx context.Context, userID pgtype.UUID) ([]UserEbookFolders, error)
|
||||
GetUserForLogin(ctx context.Context, email string) (GetUserForLoginRow, error)
|
||||
GetUserPasswordHash(ctx context.Context, id pgtype.UUID) (string, error)
|
||||
GetUserVisibleLibraries(ctx context.Context, userID pgtype.UUID) ([]GetUserVisibleLibrariesRow, error)
|
||||
ListEbooks(ctx context.Context, arg ListEbooksParams) ([]Ebooks, error)
|
||||
ListLibraries(ctx context.Context) ([]ListLibrariesRow, error)
|
||||
ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error)
|
||||
ListMediaItemsByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryRow, error)
|
||||
ListUsers(ctx context.Context) ([]ListUsersRow, error)
|
||||
UpdateEbook(ctx context.Context, arg UpdateEbookParams) (Ebooks, error)
|
||||
UpdateEbookRating(ctx context.Context, arg UpdateEbookRatingParams) (EbookRatings, error)
|
||||
// Library Visibility queries
|
||||
SetLibraryVisibility(ctx context.Context, arg SetLibraryVisibilityParams) (LibraryVisibility, error)
|
||||
UpdateEbook(ctx context.Context, arg UpdateEbookParams) (MediaItems, error)
|
||||
UpdateEbookRating(ctx context.Context, arg UpdateEbookRatingParams) (MediaRatings, error)
|
||||
UpdateEmail(ctx context.Context, arg UpdateEmailParams) error
|
||||
UpdateLibrary(ctx context.Context, arg UpdateLibraryParams) (Libraries, error)
|
||||
UpdateMediaItem(ctx context.Context, arg UpdateMediaItemParams) (MediaItems, error)
|
||||
UpdateMediaRating(ctx context.Context, arg UpdateMediaRatingParams) (MediaRatings, error)
|
||||
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
|
||||
UpdateReadingProgress(ctx context.Context, arg UpdateReadingProgressParams) (ReadingProgress, error)
|
||||
UpdateScanSettings(ctx context.Context, arg UpdateScanSettingsParams) error
|
||||
|
||||
+976
-108
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,126 @@ SELECT password_hash FROM users WHERE id = $1;
|
||||
-- name: ListUsers :many
|
||||
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users ORDER BY created_at DESC;
|
||||
|
||||
-- Library Types queries
|
||||
-- name: GetLibraryTypes :many
|
||||
SELECT * FROM library_types ORDER BY name;
|
||||
|
||||
-- name: GetLibraryType :one
|
||||
SELECT * FROM library_types WHERE id = $1;
|
||||
|
||||
-- name: GetLibraryTypeByName :one
|
||||
SELECT * FROM library_types WHERE name = $1;
|
||||
|
||||
-- Libraries queries
|
||||
-- name: CreateLibrary :one
|
||||
INSERT INTO libraries (name, description, library_type_id, created_by_admin_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetLibrary :one
|
||||
SELECT l.*, lt.name as type_name, lt.description as type_description
|
||||
FROM libraries l
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
WHERE l.id = $1;
|
||||
|
||||
-- name: ListLibraries :many
|
||||
SELECT l.*, lt.name as type_name, lt.description as type_description
|
||||
FROM libraries l
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
ORDER BY l.created_at DESC;
|
||||
|
||||
-- name: UpdateLibrary :one
|
||||
UPDATE libraries SET
|
||||
name = $2,
|
||||
description = $3,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteLibrary :exec
|
||||
DELETE FROM libraries WHERE id = $1;
|
||||
|
||||
-- Library Folders queries
|
||||
-- name: AddLibraryFolder :one
|
||||
INSERT INTO library_folders (library_id, folder_path) VALUES ($1, $2) RETURNING *;
|
||||
|
||||
-- name: GetLibraryFolders :many
|
||||
SELECT * FROM library_folders WHERE library_id = $1 ORDER BY created_at;
|
||||
|
||||
-- name: DeleteLibraryFolder :one
|
||||
DELETE FROM library_folders WHERE library_id = $1 AND folder_path = $2 RETURNING *;
|
||||
|
||||
-- Library Visibility queries
|
||||
-- name: SetLibraryVisibility :one
|
||||
INSERT INTO library_visibility (user_id, library_id, is_visible)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (user_id, library_id)
|
||||
DO UPDATE SET
|
||||
is_visible = EXCLUDED.is_visible,
|
||||
updated_at = NOW()
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetLibraryVisibility :one
|
||||
SELECT * FROM library_visibility WHERE user_id = $1 AND library_id = $2;
|
||||
|
||||
-- name: GetUserVisibleLibraries :many
|
||||
SELECT l.*, lt.name as type_name, lt.description as type_description,
|
||||
COALESCE(lv.is_visible, true) as is_visible
|
||||
FROM libraries l
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||
WHERE COALESCE(lv.is_visible, true) = true
|
||||
ORDER BY l.created_at DESC;
|
||||
|
||||
-- Media Items queries
|
||||
-- name: CreateMediaItem :one
|
||||
INSERT INTO media_items (library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, added_by_admin_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetMediaItem :one
|
||||
SELECT * FROM media_items WHERE id = $1;
|
||||
|
||||
-- name: ListMediaItems :many
|
||||
SELECT mi.*, l.name as library_name, lt.name as library_type_name
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2;
|
||||
|
||||
-- name: ListMediaItemsByLibrary :many
|
||||
SELECT mi.*, l.name as library_name, lt.name as library_type_name
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
WHERE mi.library_id = $1
|
||||
ORDER BY mi.created_at DESC;
|
||||
|
||||
-- name: UpdateMediaItem :one
|
||||
UPDATE media_items SET
|
||||
title = $2,
|
||||
author = $3,
|
||||
isbn = $4,
|
||||
description = $5,
|
||||
cover_image_path = $6,
|
||||
series = $7,
|
||||
series_number = $8,
|
||||
tags = $9,
|
||||
asin = $10,
|
||||
date_published = $11,
|
||||
publisher = $12,
|
||||
contributors = $13,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteMediaItem :exec
|
||||
DELETE FROM media_items WHERE id = $1;
|
||||
|
||||
-- name: GetMediaItemByFilePath :one
|
||||
SELECT * FROM media_items WHERE file_path = $1;
|
||||
|
||||
-- Backward compatibility - Ebooks queries (using views)
|
||||
-- name: GetEbook :one
|
||||
SELECT * FROM ebooks WHERE id = $1;
|
||||
|
||||
@@ -31,12 +151,12 @@ SELECT * FROM ebooks WHERE id = $1;
|
||||
SELECT * FROM ebooks ORDER BY created_at DESC LIMIT $1 OFFSET $2;
|
||||
|
||||
-- name: CreateEbook :one
|
||||
INSERT INTO ebooks (title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, added_by_admin_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||
INSERT INTO media_items (library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, added_by_admin_id)
|
||||
VALUES ((SELECT id FROM libraries WHERE library_type_id = (SELECT id FROM library_types WHERE name = 'ebooks') LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateEbook :one
|
||||
UPDATE ebooks SET
|
||||
UPDATE media_items SET
|
||||
title = $2,
|
||||
author = $3,
|
||||
isbn = $4,
|
||||
@@ -54,15 +174,15 @@ WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteEbook :exec
|
||||
DELETE FROM ebooks WHERE id = $1;
|
||||
DELETE FROM media_items WHERE id = $1;
|
||||
|
||||
-- name: GetReadingProgress :one
|
||||
SELECT * FROM reading_progress WHERE ebook_id = $1 AND user_id = $2;
|
||||
SELECT * FROM reading_progress WHERE media_item_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: UpdateReadingProgress :one
|
||||
INSERT INTO reading_progress (ebook_id, user_id, current_page, total_pages, last_read_at)
|
||||
INSERT INTO reading_progress (media_item_id, user_id, current_page, total_pages, last_read_at)
|
||||
VALUES ($1, $2, $3, $4, NOW())
|
||||
ON CONFLICT (ebook_id, user_id)
|
||||
ON CONFLICT (media_item_id, user_id)
|
||||
DO UPDATE SET
|
||||
current_page = EXCLUDED.current_page,
|
||||
total_pages = EXCLUDED.total_pages,
|
||||
@@ -70,7 +190,7 @@ DO UPDATE SET
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteReadingProgress :exec
|
||||
DELETE FROM reading_progress WHERE ebook_id = $1 AND user_id = $2;
|
||||
DELETE FROM reading_progress WHERE media_item_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: UpdateUserTheme :exec
|
||||
UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1;
|
||||
@@ -96,10 +216,41 @@ UPDATE users SET scan_frequency_minutes = $2, auto_scan_enabled = $3, updated_at
|
||||
-- name: GetScanSettings :one
|
||||
SELECT scan_frequency_minutes, auto_scan_enabled FROM users WHERE id = $1;
|
||||
|
||||
-- name: CreateEbookRating :one
|
||||
INSERT INTO ebook_ratings (ebook_id, user_id, rating)
|
||||
-- name: CreateMediaRating :one
|
||||
INSERT INTO media_ratings (media_item_id, user_id, rating)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (ebook_id, user_id)
|
||||
ON CONFLICT (media_item_id, user_id)
|
||||
DO UPDATE SET
|
||||
rating = EXCLUDED.rating,
|
||||
updated_at = NOW()
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetMediaRating :one
|
||||
SELECT * FROM media_ratings WHERE media_item_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: GetMediaRatings :many
|
||||
SELECT mr.*, u.username
|
||||
FROM media_ratings mr
|
||||
JOIN users u ON mr.user_id = u.id
|
||||
WHERE mr.media_item_id = $1
|
||||
ORDER BY mr.created_at DESC;
|
||||
|
||||
-- name: UpdateMediaRating :one
|
||||
UPDATE media_ratings SET
|
||||
rating = $3,
|
||||
updated_at = NOW()
|
||||
WHERE media_item_id = $1 AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteMediaRating :exec
|
||||
DELETE FROM media_ratings WHERE media_item_id = $1 AND user_id = $2;
|
||||
|
||||
-- Backward compatibility - Ebooks ratings (using views)
|
||||
-- name: CreateEbookRating :one
|
||||
INSERT INTO media_ratings (media_item_id, user_id, rating)
|
||||
SELECT $1, $2, $3
|
||||
WHERE EXISTS (SELECT 1 FROM media_items WHERE id = $1)
|
||||
ON CONFLICT (media_item_id, user_id)
|
||||
DO UPDATE SET
|
||||
rating = EXCLUDED.rating,
|
||||
updated_at = NOW()
|
||||
@@ -116,23 +267,17 @@ WHERE er.ebook_id = $1
|
||||
ORDER BY er.created_at DESC;
|
||||
|
||||
-- name: UpdateEbookRating :one
|
||||
UPDATE ebook_ratings SET
|
||||
UPDATE media_ratings SET
|
||||
rating = $3,
|
||||
updated_at = NOW()
|
||||
WHERE ebook_id = $1 AND user_id = $2
|
||||
WHERE media_item_id = $1 AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteEbookRating :exec
|
||||
DELETE FROM ebook_ratings WHERE ebook_id = $1 AND user_id = $2;
|
||||
DELETE FROM media_ratings WHERE media_item_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: AddUserEbookFolder :one
|
||||
INSERT INTO user_ebook_folders (user_id, folder_path) VALUES ($1, $2) RETURNING *;
|
||||
|
||||
-- name: GetUserEbookFolders :many
|
||||
SELECT * FROM user_ebook_folders WHERE user_id = $1 ORDER BY created_at;
|
||||
|
||||
-- name: DeleteUserEbookFolder :one
|
||||
DELETE FROM user_ebook_folders WHERE user_id = $1 AND folder_path = $2 RETURNING *;
|
||||
-- Note: User ebook folders replaced by library folders system
|
||||
-- Legacy folder management is now handled through libraries
|
||||
|
||||
-- name: GetEbookByFilePath :one
|
||||
SELECT * FROM ebooks WHERE file_path = $1;
|
||||
@@ -0,0 +1,200 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user