// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.30.0 // source: queries.sql package database import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const AddBookToCollection = `-- name: AddBookToCollection :one INSERT INTO collection_items (collection_id, media_item_id, added_by_user_id) VALUES ($1, $2, $3) ON CONFLICT (collection_id, media_item_id) DO NOTHING RETURNING id, collection_id, media_item_id, added_at, added_by_user_id, excluded ` type AddBookToCollectionParams struct { CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` AddedByUserID pgtype.UUID `db:"added_by_user_id" json:"added_by_user_id"` } // COLLECTION ITEMS QUERIES // Add book to collection func (q *Queries) AddBookToCollection(ctx context.Context, arg AddBookToCollectionParams) (CollectionItems, error) { row := q.db.QueryRow(ctx, AddBookToCollection, arg.CollectionID, arg.MediaItemID, arg.AddedByUserID) var i CollectionItems err := row.Scan( &i.ID, &i.CollectionID, &i.MediaItemID, &i.AddedAt, &i.AddedByUserID, &i.Excluded, ) return i, err } const AddBookToKoboShelf = `-- name: AddBookToKoboShelf :one INSERT INTO kobo_shelves (device_id, media_item_id, shelf_name, shelf_position) VALUES ($1, $2, $3, $4) ON CONFLICT (device_id, media_item_id) DO UPDATE SET shelf_name = EXCLUDED.shelf_name, shelf_position = EXCLUDED.shelf_position, last_synced_at = NOW() RETURNING id, device_id, media_item_id, shelf_name, shelf_position, added_at, last_synced_at, collection_id, position_in_collection ` type AddBookToKoboShelfParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` ShelfPosition pgtype.Int4 `db:"shelf_position" json:"shelf_position"` } // ============================================ // KOBO SHELF MANAGEMENT QUERIES // ============================================ func (q *Queries) AddBookToKoboShelf(ctx context.Context, arg AddBookToKoboShelfParams) (KoboShelves, error) { row := q.db.QueryRow(ctx, AddBookToKoboShelf, arg.DeviceID, arg.MediaItemID, arg.ShelfName, arg.ShelfPosition, ) var i KoboShelves err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.ShelfName, &i.ShelfPosition, &i.AddedAt, &i.LastSyncedAt, &i.CollectionID, &i.PositionInCollection, ) return i, err } const AddLibraryFolder = `-- name: AddLibraryFolder :one INSERT INTO library_folders (library_id, folder_path) VALUES ($1, $2) RETURNING id, library_id, folder_path, created_at ` type AddLibraryFolderParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FolderPath string `db:"folder_path" json:"folder_path"` } // Library Folders queries func (q *Queries) AddLibraryFolder(ctx context.Context, arg AddLibraryFolderParams) (LibraryFolders, error) { row := q.db.QueryRow(ctx, AddLibraryFolder, arg.LibraryID, arg.FolderPath) var i LibraryFolders err := row.Scan( &i.ID, &i.LibraryID, &i.FolderPath, &i.CreatedAt, ) return i, err } const BulkUpdateFormatGroups = `-- name: BulkUpdateFormatGroups :exec UPDATE media_items m SET format_group = detect_format_group(m.mime_type, m.file_path), format_mimetype = m.mime_type, is_reflowable = (detect_format_group(m.mime_type, m.file_path) = 'reflowable'), has_fixed_layout = (detect_format_group(m.mime_type, m.file_path) IN ('fixed_layout', 'comic_archive')), updated_at = NOW() WHERE m.format_group IS NULL OR m.format_group = 'unknown' ` // Bulk update format group for all media items func (q *Queries) BulkUpdateFormatGroups(ctx context.Context) error { _, err := q.db.Exec(ctx, BulkUpdateFormatGroups) return err } const BulkUpdateProgressFromSync = `-- name: BulkUpdateProgressFromSync :many SELECT bulk_update_progress_from_koreader FROM bulk_update_progress_from_koreader($1::uuid, $2::jsonb) ` type BulkUpdateProgressFromSyncParams struct { Column1 pgtype.UUID `db:"column_1" json:"column_1"` Column2 []byte `db:"column_2" json:"column_2"` } func (q *Queries) BulkUpdateProgressFromSync(ctx context.Context, arg BulkUpdateProgressFromSyncParams) ([]interface{}, error) { rows, err := q.db.Query(ctx, BulkUpdateProgressFromSync, arg.Column1, arg.Column2) if err != nil { return nil, err } defer rows.Close() items := []interface{}{} for rows.Next() { var bulk_update_progress_from_koreader interface{} if err := rows.Scan(&bulk_update_progress_from_koreader); err != nil { return nil, err } items = append(items, bulk_update_progress_from_koreader) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const CheckForProgressConflicts = `-- name: CheckForProgressConflicts :one SELECT COUNT(*) as conflict_count FROM reading_progress WHERE media_item_id = $1 AND user_id = $2 AND last_sync_timestamp > NOW() - INTERVAL '5 minutes' AND last_sync_source != $3 ` type CheckForProgressConflictsParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LastSyncSource pgtype.Text `db:"last_sync_source" json:"last_sync_source"` } func (q *Queries) CheckForProgressConflicts(ctx context.Context, arg CheckForProgressConflictsParams) (int64, error) { row := q.db.QueryRow(ctx, CheckForProgressConflicts, arg.MediaItemID, arg.UserID, arg.LastSyncSource) var conflict_count int64 err := row.Scan(&conflict_count) return conflict_count, err } const CleanupExpiredOpdsTokens = `-- name: CleanupExpiredOpdsTokens :exec DELETE FROM opds_tokens WHERE expires_at < NOW() ` // Cleanup expired OPDS tokens func (q *Queries) CleanupExpiredOpdsTokens(ctx context.Context) error { _, err := q.db.Exec(ctx, CleanupExpiredOpdsTokens) return err } const CleanupExpiredRefreshTokens = `-- name: CleanupExpiredRefreshTokens :exec DELETE FROM refresh_tokens WHERE expires_at < NOW() OR (revoked_at IS NOT NULL AND revoked_at < NOW() - make_interval(secs => $1::double precision)) ` func (q *Queries) CleanupExpiredRefreshTokens(ctx context.Context, dollar_1 float64) error { _, err := q.db.Exec(ctx, CleanupExpiredRefreshTokens, dollar_1) return err } const ClearDeviceSyncQueue = `-- name: ClearDeviceSyncQueue :exec DELETE FROM sync_queue WHERE device_id = $1 ` func (q *Queries) ClearDeviceSyncQueue(ctx context.Context, deviceID pgtype.UUID) error { _, err := q.db.Exec(ctx, ClearDeviceSyncQueue, deviceID) return err } const ClearKoboShelf = `-- name: ClearKoboShelf :exec DELETE FROM kobo_shelves WHERE device_id = $1 ` func (q *Queries) ClearKoboShelf(ctx context.Context, deviceID pgtype.UUID) error { _, err := q.db.Exec(ctx, ClearKoboShelf, deviceID) return err } const ClearKoboShelfByName = `-- name: ClearKoboShelfByName :exec DELETE FROM kobo_shelves WHERE device_id = $1 AND shelf_name = $2 ` type ClearKoboShelfByNameParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` } func (q *Queries) ClearKoboShelfByName(ctx context.Context, arg ClearKoboShelfByNameParams) error { _, err := q.db.Exec(ctx, ClearKoboShelfByName, arg.DeviceID, arg.ShelfName) return err } const CountAdmins = `-- name: CountAdmins :one SELECT COUNT(*) FROM users WHERE role = 'admin' ` func (q *Queries) CountAdmins(ctx context.Context) (int64, error) { row := q.db.QueryRow(ctx, CountAdmins) var count int64 err := row.Scan(&count) return count, err } const CountUnlinkedBooks = `-- name: CountUnlinkedBooks :one SELECT COUNT(*) as count FROM unlinked_books WHERE device_id = $1 AND resolved = false ` // Count unlinked books for a device func (q *Queries) CountUnlinkedBooks(ctx context.Context, deviceID pgtype.UUID) (int64, error) { row := q.db.QueryRow(ctx, CountUnlinkedBooks, deviceID) var count int64 err := row.Scan(&count) return count, err } const CountUserDevices = `-- name: CountUserDevices :one SELECT COUNT(*) FROM devices WHERE user_id = $1 ` func (q *Queries) CountUserDevices(ctx context.Context, userID pgtype.UUID) (int64, error) { row := q.db.QueryRow(ctx, CountUserDevices, userID) var count int64 err := row.Scan(&count) return count, err } const CreateAutoResolvedSyncConflict = `-- name: CreateAutoResolvedSyncConflict :one INSERT INTO sync_conflicts (media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_at) VALUES ($1, $2, $3, $4, 'auto_resolved', $5, NOW()) RETURNING id, media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_by, resolved_at, created_at ` type CreateAutoResolvedSyncConflictParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` ConflictType string `db:"conflict_type" json:"conflict_type"` ConflictData []byte `db:"conflict_data" json:"conflict_data"` ResolutionData []byte `db:"resolution_data" json:"resolution_data"` } func (q *Queries) CreateAutoResolvedSyncConflict(ctx context.Context, arg CreateAutoResolvedSyncConflictParams) (SyncConflicts, error) { row := q.db.QueryRow(ctx, CreateAutoResolvedSyncConflict, arg.MediaItemID, arg.UserID, arg.ConflictType, arg.ConflictData, arg.ResolutionData, ) var i SyncConflicts err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const CreateCollection = `-- name: CreateCollection :one INSERT INTO collections (user_id, name, description, color, icon, auto_assign_rules, view_settings) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at ` type CreateCollectionParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` Color pgtype.Text `db:"color" json:"color"` Icon pgtype.Text `db:"icon" json:"icon"` AutoAssignRules []byte `db:"auto_assign_rules" json:"auto_assign_rules"` ViewSettings []byte `db:"view_settings" json:"view_settings"` } // COLLECTIONS QUERIES // Create collection func (q *Queries) CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collections, error) { row := q.db.QueryRow(ctx, CreateCollection, arg.UserID, arg.Name, arg.Description, arg.Color, arg.Icon, arg.AutoAssignRules, arg.ViewSettings, ) var i Collections err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ) return i, err } const CreateDevice = `-- name: CreateDevice :one INSERT INTO devices (user_id, device_name, device_type, device_identifier, auth_token, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` type CreateDeviceParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` DeviceIdentifier string `db:"device_identifier" json:"device_identifier"` AuthToken string `db:"auth_token" json:"auth_token"` SyncEnabled pgtype.Bool `db:"sync_enabled" json:"sync_enabled"` AutoSync pgtype.Bool `db:"auto_sync" json:"auto_sync"` SyncFrequencyMinutes pgtype.Int4 `db:"sync_frequency_minutes" json:"sync_frequency_minutes"` DeviceMetadata []byte `db:"device_metadata" json:"device_metadata"` } // ============================================ // DEVICE MANAGEMENT & AUTH // ============================================ // Device Registration & Management func (q *Queries) CreateDevice(ctx context.Context, arg CreateDeviceParams) (Devices, error) { row := q.db.QueryRow(ctx, CreateDevice, arg.UserID, arg.DeviceName, arg.DeviceType, arg.DeviceIdentifier, arg.AuthToken, arg.SyncEnabled, arg.AutoSync, arg.SyncFrequencyMinutes, arg.DeviceMetadata, ) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const CreateDeviceCatalog = `-- name: CreateDeviceCatalog :one INSERT INTO device_catalogs (device_id, media_item_id, bookhoard_uuid, kobo_content_id, content_id_type, available, delivery_date, delivery_method) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (device_id, kobo_content_id) DO UPDATE SET available = EXCLUDED.available, delivery_date = COALESCE(EXCLUDED.delivery_date, device_catalogs.delivery_date), delivery_method = EXCLUDED.delivery_method RETURNING id, device_id, media_item_id, bookhoard_uuid, kobo_content_id, content_id_type, available, delivery_date, delivery_method ` type CreateDeviceCatalogParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` BookhoardUuid pgtype.UUID `db:"bookhoard_uuid" json:"bookhoard_uuid"` KoboContentID string `db:"kobo_content_id" json:"kobo_content_id"` ContentIDType pgtype.Text `db:"content_id_type" json:"content_id_type"` Available pgtype.Bool `db:"available" json:"available"` DeliveryDate pgtype.Timestamptz `db:"delivery_date" json:"delivery_date"` DeliveryMethod pgtype.Text `db:"delivery_method" json:"delivery_method"` } // DEVICE CATALOGS QUERIES // Create device catalog entry func (q *Queries) CreateDeviceCatalog(ctx context.Context, arg CreateDeviceCatalogParams) (DeviceCatalogs, error) { row := q.db.QueryRow(ctx, CreateDeviceCatalog, arg.DeviceID, arg.MediaItemID, arg.BookhoardUuid, arg.KoboContentID, arg.ContentIDType, arg.Available, arg.DeliveryDate, arg.DeliveryMethod, ) var i DeviceCatalogs err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.BookhoardUuid, &i.KoboContentID, &i.ContentIDType, &i.Available, &i.DeliveryDate, &i.DeliveryMethod, ) return i, err } const CreateDeviceFileAlias = `-- name: CreateDeviceFileAlias :one INSERT INTO device_file_aliases (media_item_id, device_id, file_path, file_sha256, confidence_score) VALUES ($1, $2, $3, $4, $5) RETURNING id, media_item_id, device_id, file_path, file_sha256, confidence_score, last_seen_at ` type CreateDeviceFileAliasParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` FilePath string `db:"file_path" json:"file_path"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` } // DEVICE FILE ALIASES QUERIES // Create device file alias func (q *Queries) CreateDeviceFileAlias(ctx context.Context, arg CreateDeviceFileAliasParams) (DeviceFileAliases, error) { row := q.db.QueryRow(ctx, CreateDeviceFileAlias, arg.MediaItemID, arg.DeviceID, arg.FilePath, arg.FileSha256, arg.ConfidenceScore, ) var i DeviceFileAliases err := row.Scan( &i.ID, &i.MediaItemID, &i.DeviceID, &i.FilePath, &i.FileSha256, &i.ConfidenceScore, &i.LastSeenAt, ) return i, err } const CreateDeviceShelfMapping = `-- name: CreateDeviceShelfMapping :one INSERT INTO device_shelf_mappings (collection_id, device_id, device_shelf_name, sync_direction) VALUES ($1, $2, $3, $4) ON CONFLICT (collection_id, device_id) DO UPDATE SET device_shelf_name = EXCLUDED.device_shelf_name, sync_direction = EXCLUDED.sync_direction RETURNING id, collection_id, device_id, device_shelf_name, sync_direction, created_at ` type CreateDeviceShelfMappingParams struct { CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` DeviceShelfName pgtype.Text `db:"device_shelf_name" json:"device_shelf_name"` SyncDirection pgtype.Text `db:"sync_direction" json:"sync_direction"` } // DEVICE SHELF MAPPINGS QUERIES // Create device shelf mapping func (q *Queries) CreateDeviceShelfMapping(ctx context.Context, arg CreateDeviceShelfMappingParams) (DeviceShelfMappings, error) { row := q.db.QueryRow(ctx, CreateDeviceShelfMapping, arg.CollectionID, arg.DeviceID, arg.DeviceShelfName, arg.SyncDirection, ) var i DeviceShelfMappings err := row.Scan( &i.ID, &i.CollectionID, &i.DeviceID, &i.DeviceShelfName, &i.SyncDirection, &i.CreatedAt, ) return i, err } const CreateDictionaryEntry = `-- name: CreateDictionaryEntry :one INSERT INTO dictionary_cache (word, definition, part_of_speech, example, etymology, accessed_at) VALUES ($1, $2, $3, $4, $5, NOW()) RETURNING id, word, definition, part_of_speech, example, etymology, created_at, accessed_at ` type CreateDictionaryEntryParams struct { Word string `db:"word" json:"word"` Definition string `db:"definition" json:"definition"` PartOfSpeech pgtype.Text `db:"part_of_speech" json:"part_of_speech"` Example pgtype.Text `db:"example" json:"example"` Etymology pgtype.Text `db:"etymology" json:"etymology"` } func (q *Queries) CreateDictionaryEntry(ctx context.Context, arg CreateDictionaryEntryParams) (DictionaryCache, error) { row := q.db.QueryRow(ctx, CreateDictionaryEntry, arg.Word, arg.Definition, arg.PartOfSpeech, arg.Example, arg.Etymology, ) var i DictionaryCache err := row.Scan( &i.ID, &i.Word, &i.Definition, &i.PartOfSpeech, &i.Example, &i.Etymology, &i.CreatedAt, &i.AccessedAt, ) return i, err } const CreateHashConflict = `-- name: CreateHashConflict :exec INSERT INTO hash_conflicts (library_id, file_sha256) VALUES ($1, $2) ON CONFLICT (library_id, file_sha256) DO NOTHING ` type CreateHashConflictParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FileSha256 string `db:"file_sha256" json:"file_sha256"` } // HASH CONFLICTS QUERIES // Record a pending hash conflict (no-op if the group is already tracked, so // resolved groups stay resolved and are never re-flagged) func (q *Queries) CreateHashConflict(ctx context.Context, arg CreateHashConflictParams) error { _, err := q.db.Exec(ctx, CreateHashConflict, arg.LibraryID, arg.FileSha256) return err } const CreateLibrary = `-- name: CreateLibrary :one INSERT INTO libraries (name, description, library_type_id, created_by_admin_id) VALUES ($1, $2, $3, $4) RETURNING id, name, description, library_type_id, created_by_admin_id, created_at, updated_at ` type CreateLibraryParams struct { 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"` } // Libraries queries func (q *Queries) CreateLibrary(ctx context.Context, arg CreateLibraryParams) (Libraries, error) { row := q.db.QueryRow(ctx, CreateLibrary, arg.Name, arg.Description, arg.LibraryTypeID, arg.CreatedByAdminID, ) var i Libraries err := row.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const CreateMediaBookmark = `-- name: CreateMediaBookmark :one INSERT INTO media_bookmarks (media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at ` type CreateMediaBookmarkParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` PageNumber pgtype.Int4 `db:"page_number" json:"page_number"` ChapterNumber pgtype.Int4 `db:"chapter_number" json:"chapter_number"` CfiPosition pgtype.Text `db:"cfi_position" json:"cfi_position"` Title string `db:"title" json:"title"` Position pgtype.Text `db:"position" json:"position"` Notes pgtype.Text `db:"notes" json:"notes"` } func (q *Queries) CreateMediaBookmark(ctx context.Context, arg CreateMediaBookmarkParams) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, CreateMediaBookmark, arg.MediaItemID, arg.UserID, arg.PageNumber, arg.ChapterNumber, arg.CfiPosition, arg.Title, arg.Position, arg.Notes, ) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaBookmarkFull = `-- name: CreateMediaBookmarkFull :one INSERT INTO media_bookmarks ( media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, percentage_location, epubcfi_location, chapter_reference, dedup_key, last_modified_at, last_modified_source, device_sync_data ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 ) RETURNING id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at ` type CreateMediaBookmarkFullParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` PageNumber pgtype.Int4 `db:"page_number" json:"page_number"` ChapterNumber pgtype.Int4 `db:"chapter_number" json:"chapter_number"` CfiPosition pgtype.Text `db:"cfi_position" json:"cfi_position"` Title string `db:"title" json:"title"` Position pgtype.Text `db:"position" json:"position"` Notes pgtype.Text `db:"notes" json:"notes"` PercentageLocation pgtype.Float8 `db:"percentage_location" json:"percentage_location"` EpubcfiLocation pgtype.Text `db:"epubcfi_location" json:"epubcfi_location"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) CreateMediaBookmarkFull(ctx context.Context, arg CreateMediaBookmarkFullParams) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, CreateMediaBookmarkFull, arg.MediaItemID, arg.UserID, arg.PageNumber, arg.ChapterNumber, arg.CfiPosition, arg.Title, arg.Position, arg.Notes, arg.PercentageLocation, arg.EpubcfiLocation, arg.ChapterReference, arg.DedupKey, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaHighlight = `-- name: CreateMediaHighlight :one INSERT INTO media_highlights (media_item_id, user_id, selection_text, start_position, end_position, color, note_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at ` type CreateMediaHighlightParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` NoteID pgtype.UUID `db:"note_id" json:"note_id"` } // Media Highlights queries func (q *Queries) CreateMediaHighlight(ctx context.Context, arg CreateMediaHighlightParams) (MediaHighlights, error) { row := q.db.QueryRow(ctx, CreateMediaHighlight, arg.MediaItemID, arg.UserID, arg.SelectionText, arg.StartPosition, arg.EndPosition, arg.Color, arg.NoteID, ) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaHighlightFull = `-- name: CreateMediaHighlightFull :one INSERT INTO media_highlights ( media_item_id, user_id, selection_text, start_position, end_position, color, note_text, percentage_start, percentage_end, epubcfi_start, epubcfi_end, chapter_reference, dedup_key, last_modified_at, last_modified_source, device_sync_data ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 ) RETURNING id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at ` type CreateMediaHighlightFullParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` NoteText pgtype.Text `db:"note_text" json:"note_text"` PercentageStart pgtype.Float8 `db:"percentage_start" json:"percentage_start"` PercentageEnd pgtype.Float8 `db:"percentage_end" json:"percentage_end"` EpubcfiStart pgtype.Text `db:"epubcfi_start" json:"epubcfi_start"` EpubcfiEnd pgtype.Text `db:"epubcfi_end" json:"epubcfi_end"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) CreateMediaHighlightFull(ctx context.Context, arg CreateMediaHighlightFullParams) (MediaHighlights, error) { row := q.db.QueryRow(ctx, CreateMediaHighlightFull, arg.MediaItemID, arg.UserID, arg.SelectionText, arg.StartPosition, arg.EndPosition, arg.Color, arg.NoteText, arg.PercentageStart, arg.PercentageEnd, arg.EpubcfiStart, arg.EpubcfiEnd, arg.ChapterReference, arg.DedupKey, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaItem = `-- 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, tags_search, asin, date_published, publisher, contributors, contributors_search, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, library_type_name) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44) ON CONFLICT (library_id, file_path) DO UPDATE SET updated_at = NOW() RETURNING id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence ` type CreateMediaItemParams struct { 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 []string `db:"tags" json:"tags"` TagsSearch []string `db:"tags_search" json:"tags_search"` Asin pgtype.Text `db:"asin" json:"asin"` DatePublished pgtype.Date `db:"date_published" json:"date_published"` Publisher pgtype.Text `db:"publisher" json:"publisher"` Contributors []string `db:"contributors" json:"contributors"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` } // Media Items queries func (q *Queries) CreateMediaItem(ctx context.Context, arg CreateMediaItemParams) (MediaItems, error) { row := q.db.QueryRow(ctx, CreateMediaItem, arg.LibraryID, arg.Title, arg.Author, arg.Isbn, arg.Description, arg.FilePath, arg.FileSize, arg.MimeType, arg.CoverImagePath, arg.Series, arg.SeriesNumber, arg.Tags, arg.TagsSearch, arg.Asin, arg.DatePublished, arg.Publisher, arg.Contributors, arg.ContributorsSearch, arg.Language, arg.Edition, arg.PageCount, arg.Genre, arg.CopyrightYear, arg.GoodreadsID, arg.OpenlibraryID, arg.GoogleBooksID, arg.AddedByAdminID, arg.CreatedAt, arg.ImportedAt, arg.MangaType, arg.ReadingDirection, arg.SeriesCount, arg.Volume, arg.Imprint, arg.AgeRating, arg.WebUrl, arg.StoryArc, arg.IsBlackAndWhite, arg.MetadataNotes, arg.CommunityRating, arg.AlternateInfo, arg.ScanInformation, arg.Summary, arg.LibraryTypeName, ) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const CreateMediaItemFormat = `-- name: CreateMediaItemFormat :one INSERT INTO media_item_formats (media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, converted_from_format_id) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (media_item_id, format_type) DO UPDATE SET file_path = EXCLUDED.file_path, file_sha256 = EXCLUDED.file_sha256, file_size_bytes = EXCLUDED.file_size_bytes, mime_type = EXCLUDED.mime_type RETURNING id, media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, created_at, converted_from_format_id ` type CreateMediaItemFormatParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` FormatType string `db:"format_type" json:"format_type"` FilePath pgtype.Text `db:"file_path" json:"file_path"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` FileSizeBytes pgtype.Int8 `db:"file_size_bytes" json:"file_size_bytes"` MimeType pgtype.Text `db:"mime_type" json:"mime_type"` ConvertedFromFormatID pgtype.UUID `db:"converted_from_format_id" json:"converted_from_format_id"` } // MEDIA ITEM FORMATS QUERIES // Create media item format func (q *Queries) CreateMediaItemFormat(ctx context.Context, arg CreateMediaItemFormatParams) (MediaItemFormats, error) { row := q.db.QueryRow(ctx, CreateMediaItemFormat, arg.MediaItemID, arg.FormatType, arg.FilePath, arg.FileSha256, arg.FileSizeBytes, arg.MimeType, arg.ConvertedFromFormatID, ) var i MediaItemFormats err := row.Scan( &i.ID, &i.MediaItemID, &i.FormatType, &i.FilePath, &i.FileSha256, &i.FileSizeBytes, &i.MimeType, &i.CreatedAt, &i.ConvertedFromFormatID, ) return i, err } const CreateMediaNote = `-- name: CreateMediaNote :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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at ` type CreateMediaNoteParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Content string `db:"content" json:"content"` Position pgtype.Text `db:"position" json:"position"` } // Media Notes queries func (q *Queries) CreateMediaNote(ctx context.Context, arg CreateMediaNoteParams) (MediaNotes, error) { row := q.db.QueryRow(ctx, CreateMediaNote, arg.MediaItemID, arg.UserID, arg.Content, arg.Position, ) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaNoteFull = `-- name: CreateMediaNoteFull :one INSERT INTO media_notes ( media_item_id, user_id, content, position, percentage_location, character_start, character_end, epubcfi_location, chapter_reference, paragraph_reference, dedup_key, last_modified_at, last_modified_source, device_sync_data ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14 ) 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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at ` type CreateMediaNoteFullParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Content string `db:"content" json:"content"` Position pgtype.Text `db:"position" json:"position"` PercentageLocation pgtype.Float8 `db:"percentage_location" json:"percentage_location"` CharacterStart pgtype.Int4 `db:"character_start" json:"character_start"` CharacterEnd pgtype.Int4 `db:"character_end" json:"character_end"` EpubcfiLocation pgtype.Text `db:"epubcfi_location" json:"epubcfi_location"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` ParagraphReference pgtype.Int4 `db:"paragraph_reference" json:"paragraph_reference"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) CreateMediaNoteFull(ctx context.Context, arg CreateMediaNoteFullParams) (MediaNotes, error) { row := q.db.QueryRow(ctx, CreateMediaNoteFull, arg.MediaItemID, arg.UserID, arg.Content, arg.Position, arg.PercentageLocation, arg.CharacterStart, arg.CharacterEnd, arg.EpubcfiLocation, arg.ChapterReference, arg.ParagraphReference, arg.DedupKey, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const CreateMediaRating = `-- name: CreateMediaRating :one INSERT INTO media_ratings (media_item_id, user_id, rating) VALUES ($1, $2, $3) ON CONFLICT (media_item_id, user_id) DO UPDATE SET rating = EXCLUDED.rating, updated_at = NOW() RETURNING id, media_item_id, user_id, rating, created_at, updated_at ` type CreateMediaRatingParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Rating int32 `db:"rating" json:"rating"` } func (q *Queries) CreateMediaRating(ctx context.Context, arg CreateMediaRatingParams) (MediaRatings, error) { row := q.db.QueryRow(ctx, CreateMediaRating, arg.MediaItemID, arg.UserID, arg.Rating) var i MediaRatings err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Rating, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const CreateOpdsToken = `-- name: CreateOpdsToken :one INSERT INTO opds_tokens (device_id, token, token_type, expires_at) VALUES ($1, $2, $3, $4) RETURNING id, device_id, token, token_type, expires_at, created_at ` type CreateOpdsTokenParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` Token string `db:"token" json:"token"` TokenType pgtype.Text `db:"token_type" json:"token_type"` ExpiresAt pgtype.Timestamptz `db:"expires_at" json:"expires_at"` } // OPDS TOKENS QUERIES // Create OPDS token func (q *Queries) CreateOpdsToken(ctx context.Context, arg CreateOpdsTokenParams) (OpdsTokens, error) { row := q.db.QueryRow(ctx, CreateOpdsToken, arg.DeviceID, arg.Token, arg.TokenType, arg.ExpiresAt, ) var i OpdsTokens err := row.Scan( &i.ID, &i.DeviceID, &i.Token, &i.TokenType, &i.ExpiresAt, &i.CreatedAt, ) return i, err } const CreateOrUpdateKoboEntitlement = `-- name: CreateOrUpdateKoboEntitlement :one INSERT INTO kobo_entitlements (device_id, media_item_id, entitlement_id, content_id, revision_number, purchase_date, kobo_metadata) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (device_id, entitlement_id) DO UPDATE SET content_id = EXCLUDED.content_id, revision_number = EXCLUDED.revision_number, purchase_date = COALESCE(EXCLUDED.purchase_date, kobo_entitlements.purchase_date), book_status = 'installed', sync_status = 'synced', kobo_metadata = EXCLUDED.kobo_metadata, updated_at = NOW() RETURNING id, device_id, media_item_id, entitlement_id, content_id, revision_number, purchase_date, accession_date, book_status, sync_status, kobo_metadata, created_at, updated_at ` type CreateOrUpdateKoboEntitlementParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` ContentID string `db:"content_id" json:"content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` PurchaseDate pgtype.Timestamptz `db:"purchase_date" json:"purchase_date"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` } // ============================================ // KOBO ENTITLEMENT QUERIES // ============================================ func (q *Queries) CreateOrUpdateKoboEntitlement(ctx context.Context, arg CreateOrUpdateKoboEntitlementParams) (KoboEntitlements, error) { row := q.db.QueryRow(ctx, CreateOrUpdateKoboEntitlement, arg.DeviceID, arg.MediaItemID, arg.EntitlementID, arg.ContentID, arg.RevisionNumber, arg.PurchaseDate, arg.KoboMetadata, ) var i KoboEntitlements err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.EntitlementID, &i.ContentID, &i.RevisionNumber, &i.PurchaseDate, &i.AccessionDate, &i.BookStatus, &i.SyncStatus, &i.KoboMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const CreateProcessingIssue = `-- name: CreateProcessingIssue :one INSERT INTO processing_issues (media_item_id, library_id, issue_type, issue_description, severity) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (media_item_id, issue_type) DO UPDATE SET issue_description = EXCLUDED.issue_description, severity = EXCLUDED.severity, resolved = false, resolved_at = NULL RETURNING id, media_item_id, library_id, issue_type, issue_description, severity, resolved, resolved_at, created_at ` type CreateProcessingIssueParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` IssueType string `db:"issue_type" json:"issue_type"` IssueDescription string `db:"issue_description" json:"issue_description"` Severity string `db:"severity" json:"severity"` } // ============================================================================ // PROCESSING ISSUES QUERIES // ============================================================================ func (q *Queries) CreateProcessingIssue(ctx context.Context, arg CreateProcessingIssueParams) (ProcessingIssues, error) { row := q.db.QueryRow(ctx, CreateProcessingIssue, arg.MediaItemID, arg.LibraryID, arg.IssueType, arg.IssueDescription, arg.Severity, ) var i ProcessingIssues err := row.Scan( &i.ID, &i.MediaItemID, &i.LibraryID, &i.IssueType, &i.IssueDescription, &i.Severity, &i.Resolved, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const CreateReadingHistory = `-- name: CreateReadingHistory :one INSERT INTO reading_history ( user_id, media_item_id, device_id, progress_percentage, reading_session_start, reading_session_end, pages_read, time_spent_seconds, device_metadata ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, user_id, media_item_id, device_id, progress_percentage, reading_session_start, reading_session_end, pages_read, time_spent_seconds, device_metadata, created_at ` type CreateReadingHistoryParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ProgressPercentage pgtype.Float8 `db:"progress_percentage" json:"progress_percentage"` ReadingSessionStart pgtype.Timestamptz `db:"reading_session_start" json:"reading_session_start"` ReadingSessionEnd pgtype.Timestamptz `db:"reading_session_end" json:"reading_session_end"` PagesRead pgtype.Int4 `db:"pages_read" json:"pages_read"` TimeSpentSeconds pgtype.Int4 `db:"time_spent_seconds" json:"time_spent_seconds"` DeviceMetadata []byte `db:"device_metadata" json:"device_metadata"` } // Create reading history entry func (q *Queries) CreateReadingHistory(ctx context.Context, arg CreateReadingHistoryParams) (ReadingHistory, error) { row := q.db.QueryRow(ctx, CreateReadingHistory, arg.UserID, arg.MediaItemID, arg.DeviceID, arg.ProgressPercentage, arg.ReadingSessionStart, arg.ReadingSessionEnd, arg.PagesRead, arg.TimeSpentSeconds, arg.DeviceMetadata, ) var i ReadingHistory err := row.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.DeviceID, &i.ProgressPercentage, &i.ReadingSessionStart, &i.ReadingSessionEnd, &i.PagesRead, &i.TimeSpentSeconds, &i.DeviceMetadata, &i.CreatedAt, ) return i, err } const CreateReadingSpeed = `-- name: CreateReadingSpeed :one INSERT INTO reading_speed (user_id, media_item_id, pages_per_minute, pages_read, total_reading_minutes, last_read_at) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, user_id, media_item_id, words_per_minute, pages_per_minute, pages_read, total_reading_minutes, last_read_at, updated_at ` type CreateReadingSpeedParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` PagesPerMinute pgtype.Float4 `db:"pages_per_minute" json:"pages_per_minute"` PagesRead pgtype.Int4 `db:"pages_read" json:"pages_read"` TotalReadingMinutes pgtype.Float4 `db:"total_reading_minutes" json:"total_reading_minutes"` LastReadAt pgtype.Timestamptz `db:"last_read_at" json:"last_read_at"` } func (q *Queries) CreateReadingSpeed(ctx context.Context, arg CreateReadingSpeedParams) (ReadingSpeed, error) { row := q.db.QueryRow(ctx, CreateReadingSpeed, arg.UserID, arg.MediaItemID, arg.PagesPerMinute, arg.PagesRead, arg.TotalReadingMinutes, arg.LastReadAt, ) var i ReadingSpeed err := row.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.WordsPerMinute, &i.PagesPerMinute, &i.PagesRead, &i.TotalReadingMinutes, &i.LastReadAt, &i.UpdatedAt, ) return i, err } const CreateRefreshToken = `-- name: CreateRefreshToken :one INSERT INTO refresh_tokens (user_id, token, expires_at) VALUES ($1, $2, $3) RETURNING id, user_id, token, expires_at, created_at, revoked_at ` type CreateRefreshTokenParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Token pgtype.UUID `db:"token" json:"token"` ExpiresAt pgtype.Timestamptz `db:"expires_at" json:"expires_at"` } // Refresh Tokens queries func (q *Queries) CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshTokens, error) { row := q.db.QueryRow(ctx, CreateRefreshToken, arg.UserID, arg.Token, arg.ExpiresAt) var i RefreshTokens err := row.Scan( &i.ID, &i.UserID, &i.Token, &i.ExpiresAt, &i.CreatedAt, &i.RevokedAt, ) return i, err } const CreateSavedFilter = `-- name: CreateSavedFilter :one INSERT INTO saved_filters (user_id, name, resource_type, filters) VALUES ($1, $2, $3, $4) RETURNING id, user_id, name, resource_type, filters, created_at, updated_at ` type CreateSavedFilterParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` ResourceType string `db:"resource_type" json:"resource_type"` Filters []byte `db:"filters" json:"filters"` } func (q *Queries) CreateSavedFilter(ctx context.Context, arg CreateSavedFilterParams) (SavedFilters, error) { row := q.db.QueryRow(ctx, CreateSavedFilter, arg.UserID, arg.Name, arg.ResourceType, arg.Filters, ) var i SavedFilters err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.ResourceType, &i.Filters, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const CreateSyncConflict = `-- name: CreateSyncConflict :one INSERT INTO sync_conflicts (media_item_id, user_id, conflict_type, conflict_data) VALUES ($1, $2, $3, $4) RETURNING id, media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_by, resolved_at, created_at ` type CreateSyncConflictParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` ConflictType string `db:"conflict_type" json:"conflict_type"` ConflictData []byte `db:"conflict_data" json:"conflict_data"` } // Conflict Resolution func (q *Queries) CreateSyncConflict(ctx context.Context, arg CreateSyncConflictParams) (SyncConflicts, error) { row := q.db.QueryRow(ctx, CreateSyncConflict, arg.MediaItemID, arg.UserID, arg.ConflictType, arg.ConflictData, ) var i SyncConflicts err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const CreateSyncHistoryEntry = `-- name: CreateSyncHistoryEntry :one INSERT INTO sync_queue (device_id, media_item_id, sync_type, sync_data, priority, status) VALUES ($1, $2, 'koreader_progress', $3, 5, 'completed') RETURNING id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at ` type CreateSyncHistoryEntryParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` SyncData []byte `db:"sync_data" json:"sync_data"` } func (q *Queries) CreateSyncHistoryEntry(ctx context.Context, arg CreateSyncHistoryEntryParams) (SyncQueue, error) { row := q.db.QueryRow(ctx, CreateSyncHistoryEntry, arg.DeviceID, arg.MediaItemID, arg.SyncData) var i SyncQueue err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ) return i, err } const CreateSyncQueueItem = `-- name: CreateSyncQueueItem :one INSERT INTO sync_queue (device_id, media_item_id, sync_type, sync_data, priority, max_attempts, status) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at ` type CreateSyncQueueItemParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` SyncType string `db:"sync_type" json:"sync_type"` SyncData []byte `db:"sync_data" json:"sync_data"` Priority pgtype.Int4 `db:"priority" json:"priority"` MaxAttempts pgtype.Int4 `db:"max_attempts" json:"max_attempts"` Status pgtype.Text `db:"status" json:"status"` } // Sync Queue Management func (q *Queries) CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueItemParams) (SyncQueue, error) { row := q.db.QueryRow(ctx, CreateSyncQueueItem, arg.DeviceID, arg.MediaItemID, arg.SyncType, arg.SyncData, arg.Priority, arg.MaxAttempts, arg.Status, ) var i SyncQueue err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ) return i, err } const CreateSystemCollection = `-- name: CreateSystemCollection :one INSERT INTO collections (user_id, name, description, icon, color, show_on_dashboard, query_type, priority, is_system_collection, auto_assign_rules) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true, 'null'::jsonb) RETURNING id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at ` type CreateSystemCollectionParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` Icon pgtype.Text `db:"icon" json:"icon"` Color pgtype.Text `db:"color" json:"color"` ShowOnDashboard pgtype.Bool `db:"show_on_dashboard" json:"show_on_dashboard"` QueryType pgtype.Text `db:"query_type" json:"query_type"` Priority pgtype.Int4 `db:"priority" json:"priority"` } func (q *Queries) CreateSystemCollection(ctx context.Context, arg CreateSystemCollectionParams) (Collections, error) { row := q.db.QueryRow(ctx, CreateSystemCollection, arg.UserID, arg.Name, arg.Description, arg.Icon, arg.Color, arg.ShowOnDashboard, arg.QueryType, arg.Priority, ) var i Collections err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ) return i, err } const CreateUnlinkedBook = `-- name: CreateUnlinkedBook :one INSERT INTO unlinked_books (device_id, content_id, file_path, title, author, confidence_score) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, device_id, content_id, file_path, title, author, confidence_score, resolved, media_item_id, resolved_at, resolution_method, last_seen_at, created_at ` type CreateUnlinkedBookParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ContentID string `db:"content_id" json:"content_id"` FilePath pgtype.Text `db:"file_path" json:"file_path"` Title pgtype.Text `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` } // ============================================ // ENHANCED KOBO SYNC // ============================================ // Create unlinked book entry func (q *Queries) CreateUnlinkedBook(ctx context.Context, arg CreateUnlinkedBookParams) (UnlinkedBooks, error) { row := q.db.QueryRow(ctx, CreateUnlinkedBook, arg.DeviceID, arg.ContentID, arg.FilePath, arg.Title, arg.Author, arg.ConfidenceScore, ) var i UnlinkedBooks err := row.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, ) return i, err } const CreateUser = `-- name: CreateUser :one INSERT INTO users (email, username, password_hash, first_name, last_name, theme, role) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, email, username, theme, first_name, last_name, role, created_at, updated_at ` type CreateUserParams struct { Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` PasswordHash string `db:"password_hash" json:"password_hash"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Theme pgtype.Text `db:"theme" json:"theme"` Role string `db:"role" json:"role"` } type CreateUserRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) { row := q.db.QueryRow(ctx, CreateUser, arg.Email, arg.Username, arg.PasswordHash, arg.FirstName, arg.LastName, arg.Theme, arg.Role, ) var i CreateUserRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const DeleteCollection = `-- name: DeleteCollection :exec DELETE FROM collections WHERE id = $1 ` // Delete collection func (q *Queries) DeleteCollection(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteCollection, id) return err } const DeleteDevice = `-- name: DeleteDevice :exec DELETE FROM devices WHERE id = $1 ` func (q *Queries) DeleteDevice(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteDevice, id) return err } const DeleteDeviceByToken = `-- name: DeleteDeviceByToken :exec DELETE FROM devices WHERE auth_token = $1 ` func (q *Queries) DeleteDeviceByToken(ctx context.Context, authToken string) error { _, err := q.db.Exec(ctx, DeleteDeviceByToken, authToken) return err } const DeleteDeviceCatalog = `-- name: DeleteDeviceCatalog :exec DELETE FROM device_catalogs WHERE id = $1 ` // Delete device catalog entry func (q *Queries) DeleteDeviceCatalog(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteDeviceCatalog, id) return err } const DeleteDeviceFileAlias = `-- name: DeleteDeviceFileAlias :exec DELETE FROM device_file_aliases WHERE id = $1 ` // Delete device file alias func (q *Queries) DeleteDeviceFileAlias(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteDeviceFileAlias, id) return err } const DeleteDeviceShelfMapping = `-- name: DeleteDeviceShelfMapping :exec DELETE FROM device_shelf_mappings WHERE id = $1 ` // Delete device shelf mapping func (q *Queries) DeleteDeviceShelfMapping(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteDeviceShelfMapping, id) return err } const DeleteKoboEntitlement = `-- name: DeleteKoboEntitlement :exec DELETE FROM kobo_entitlements WHERE device_id = $1 AND entitlement_id = $2 ` type DeleteKoboEntitlementParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` } func (q *Queries) DeleteKoboEntitlement(ctx context.Context, arg DeleteKoboEntitlementParams) error { _, err := q.db.Exec(ctx, DeleteKoboEntitlement, arg.DeviceID, arg.EntitlementID) return err } const DeleteLibrary = `-- name: DeleteLibrary :exec DELETE FROM libraries WHERE id = $1 ` func (q *Queries) DeleteLibrary(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteLibrary, id) return err } const DeleteLibraryFolder = `-- name: DeleteLibraryFolder :one DELETE FROM library_folders WHERE library_id = $1 AND folder_path = $2 RETURNING id, library_id, folder_path, created_at ` type DeleteLibraryFolderParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FolderPath string `db:"folder_path" json:"folder_path"` } func (q *Queries) DeleteLibraryFolder(ctx context.Context, arg DeleteLibraryFolderParams) (LibraryFolders, error) { row := q.db.QueryRow(ctx, DeleteLibraryFolder, arg.LibraryID, arg.FolderPath) var i LibraryFolders err := row.Scan( &i.ID, &i.LibraryID, &i.FolderPath, &i.CreatedAt, ) return i, err } const DeleteMediaBookmark = `-- name: DeleteMediaBookmark :exec DELETE FROM media_bookmarks WHERE id = $1 ` func (q *Queries) DeleteMediaBookmark(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteMediaBookmark, id) return err } const DeleteMediaHighlight = `-- name: DeleteMediaHighlight :exec DELETE FROM media_highlights WHERE id = $1 ` func (q *Queries) DeleteMediaHighlight(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteMediaHighlight, id) return err } const DeleteMediaItem = `-- name: DeleteMediaItem :exec DELETE FROM media_items WHERE id = $1 ` func (q *Queries) DeleteMediaItem(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteMediaItem, id) return err } const DeleteMediaItemFormat = `-- name: DeleteMediaItemFormat :exec DELETE FROM media_item_formats WHERE id = $1 ` // Delete media item format func (q *Queries) DeleteMediaItemFormat(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteMediaItemFormat, id) return err } const DeleteMediaNote = `-- name: DeleteMediaNote :exec DELETE FROM media_notes WHERE id = $1 ` func (q *Queries) DeleteMediaNote(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteMediaNote, id) return err } const DeleteMediaRating = `-- name: DeleteMediaRating :exec DELETE FROM media_ratings WHERE media_item_id = $1 AND user_id = $2 ` type DeleteMediaRatingParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) DeleteMediaRating(ctx context.Context, arg DeleteMediaRatingParams) error { _, err := q.db.Exec(ctx, DeleteMediaRating, arg.MediaItemID, arg.UserID) return err } const DeleteProcessingIssue = `-- name: DeleteProcessingIssue :one DELETE FROM processing_issues WHERE id = $1 RETURNING id, media_item_id, library_id, issue_type, issue_description, severity, resolved, resolved_at, created_at ` func (q *Queries) DeleteProcessingIssue(ctx context.Context, id pgtype.UUID) (ProcessingIssues, error) { row := q.db.QueryRow(ctx, DeleteProcessingIssue, id) var i ProcessingIssues err := row.Scan( &i.ID, &i.MediaItemID, &i.LibraryID, &i.IssueType, &i.IssueDescription, &i.Severity, &i.Resolved, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const DeleteReadingProgress = `-- name: DeleteReadingProgress :exec DELETE FROM reading_progress WHERE media_item_id = $1 AND user_id = $2 ` type DeleteReadingProgressParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) DeleteReadingProgress(ctx context.Context, arg DeleteReadingProgressParams) error { _, err := q.db.Exec(ctx, DeleteReadingProgress, arg.MediaItemID, arg.UserID) return err } const DeleteSavedFilter = `-- name: DeleteSavedFilter :one DELETE FROM saved_filters WHERE id = $1 AND user_id = $2 RETURNING id, user_id, name, resource_type, filters, created_at, updated_at ` type DeleteSavedFilterParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) DeleteSavedFilter(ctx context.Context, arg DeleteSavedFilterParams) (SavedFilters, error) { row := q.db.QueryRow(ctx, DeleteSavedFilter, arg.ID, arg.UserID) var i SavedFilters err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.ResourceType, &i.Filters, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const DeleteSyncConflict = `-- name: DeleteSyncConflict :exec DELETE FROM sync_conflicts WHERE id = $1 ` func (q *Queries) DeleteSyncConflict(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteSyncConflict, id) return err } const DeleteSyncQueueItem = `-- name: DeleteSyncQueueItem :exec DELETE FROM sync_queue WHERE id = $1 ` func (q *Queries) DeleteSyncQueueItem(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteSyncQueueItem, id) return err } const DeleteSystemConfig = `-- name: DeleteSystemConfig :exec DELETE FROM system_config WHERE key = $1 ` // Delete system config func (q *Queries) DeleteSystemConfig(ctx context.Context, key string) error { _, err := q.db.Exec(ctx, DeleteSystemConfig, key) return err } const DeleteUnlinkedBook = `-- name: DeleteUnlinkedBook :exec DELETE FROM unlinked_books WHERE id = $1 ` // Delete unlinked book func (q *Queries) DeleteUnlinkedBook(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteUnlinkedBook, id) return err } const DeleteUser = `-- name: DeleteUser :exec DELETE FROM users WHERE id = $1 ` func (q *Queries) DeleteUser(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, DeleteUser, id) return err } const DeleteUserSystemCollection = `-- name: DeleteUserSystemCollection :exec DELETE FROM collections WHERE user_id = $1 AND name = $2 AND is_system_collection = true ` type DeleteUserSystemCollectionParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` } func (q *Queries) DeleteUserSystemCollection(ctx context.Context, arg DeleteUserSystemCollectionParams) error { _, err := q.db.Exec(ctx, DeleteUserSystemCollection, arg.UserID, arg.Name) return err } const FindHashConflictGroups = `-- name: FindHashConflictGroups :many SELECT library_id, file_sha256, COUNT(*) AS dup_count FROM media_items WHERE file_sha256 IS NOT NULL GROUP BY library_id, file_sha256 HAVING COUNT(*) > 1 ` type FindHashConflictGroupsRow struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` DupCount int64 `db:"dup_count" json:"dup_count"` } // Find content-duplicate groups (same library + SHA-256, more than one row) func (q *Queries) FindHashConflictGroups(ctx context.Context) ([]FindHashConflictGroupsRow, error) { rows, err := q.db.Query(ctx, FindHashConflictGroups) if err != nil { return nil, err } defer rows.Close() items := []FindHashConflictGroupsRow{} for rows.Next() { var i FindHashConflictGroupsRow if err := rows.Scan(&i.LibraryID, &i.FileSha256, &i.DupCount); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GenerateKoboEntitlementId = `-- name: GenerateKoboEntitlementId :one SELECT 'kobo_' || uuid_generate_v4()::TEXT as entitlement_id ` func (q *Queries) GenerateKoboEntitlementId(ctx context.Context) (interface{}, error) { row := q.db.QueryRow(ctx, GenerateKoboEntitlementId) var entitlement_id interface{} err := row.Scan(&entitlement_id) return entitlement_id, err } const GetActiveAnnotationsForBook = `-- name: GetActiveAnnotationsForBook :many SELECT mh.id, mh.selection_text, mh.start_position, mh.end_position, mh.color, mh.created_at, mh.updated_at, 'highlight' as annotation_type, mh.percentage_start, mh.percentage_end, mh.epubcfi_start, mh.epubcfi_end, mh.note_text, mh.dedup_key, mh.last_modified_at, mh.last_modified_source FROM media_highlights mh WHERE mh.media_item_id = $1 AND mh.user_id = $2 AND mh.deleted = FALSE UNION ALL SELECT mn.id, mn.content, mn.position, NULL as end_position, NULL as color, mn.created_at, mn.updated_at, 'note' as annotation_type, mn.percentage_location as percentage_start, NULL as percentage_end, mn.epubcfi_location as epubcfi_start, NULL as epubcfi_end, NULL as note_text, mn.dedup_key, mn.last_modified_at, mn.last_modified_source FROM media_notes mn WHERE mn.media_item_id = $1 AND mn.user_id = $2 AND mn.deleted = FALSE ORDER BY created_at DESC ` type GetActiveAnnotationsForBookParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } type GetActiveAnnotationsForBookRow struct { ID pgtype.UUID `db:"id" json:"id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` AnnotationType string `db:"annotation_type" json:"annotation_type"` PercentageStart pgtype.Float8 `db:"percentage_start" json:"percentage_start"` PercentageEnd pgtype.Float8 `db:"percentage_end" json:"percentage_end"` EpubcfiStart pgtype.Text `db:"epubcfi_start" json:"epubcfi_start"` EpubcfiEnd pgtype.Text `db:"epubcfi_end" json:"epubcfi_end"` NoteText pgtype.Text `db:"note_text" json:"note_text"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` } // ============================================ // ANNOTATION SERVE QUERIES // ============================================ func (q *Queries) GetActiveAnnotationsForBook(ctx context.Context, arg GetActiveAnnotationsForBookParams) ([]GetActiveAnnotationsForBookRow, error) { rows, err := q.db.Query(ctx, GetActiveAnnotationsForBook, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []GetActiveAnnotationsForBookRow{} for rows.Next() { var i GetActiveAnnotationsForBookRow if err := rows.Scan( &i.ID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.CreatedAt, &i.UpdatedAt, &i.AnnotationType, &i.PercentageStart, &i.PercentageEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.NoteText, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetAllSystemConfig = `-- name: GetAllSystemConfig :many SELECT key, value, updated_at, updated_by FROM system_config ORDER BY key ` // Get all system config func (q *Queries) GetAllSystemConfig(ctx context.Context) ([]SystemConfig, error) { rows, err := q.db.Query(ctx, GetAllSystemConfig) if err != nil { return nil, err } defer rows.Close() items := []SystemConfig{} for rows.Next() { var i SystemConfig if err := rows.Scan( &i.Key, &i.Value, &i.UpdatedAt, &i.UpdatedBy, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetAllSystemSettings = `-- name: GetAllSystemSettings :many SELECT setting_key, setting_value, description FROM system_settings ORDER BY setting_key ` type GetAllSystemSettingsRow struct { SettingKey string `db:"setting_key" json:"setting_key"` SettingValue string `db:"setting_value" json:"setting_value"` Description pgtype.Text `db:"description" json:"description"` } func (q *Queries) GetAllSystemSettings(ctx context.Context) ([]GetAllSystemSettingsRow, error) { rows, err := q.db.Query(ctx, GetAllSystemSettings) if err != nil { return nil, err } defer rows.Close() items := []GetAllSystemSettingsRow{} for rows.Next() { var i GetAllSystemSettingsRow if err := rows.Scan(&i.SettingKey, &i.SettingValue, &i.Description); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetAllSystemSettingsFull = `-- name: GetAllSystemSettingsFull :many SELECT id, setting_key, setting_value, description, updated_at, setting_type, min_value, max_value, requires_restart, category FROM system_settings ORDER BY category, setting_key ` func (q *Queries) GetAllSystemSettingsFull(ctx context.Context) ([]SystemSettings, error) { rows, err := q.db.Query(ctx, GetAllSystemSettingsFull) if err != nil { return nil, err } defer rows.Close() items := []SystemSettings{} for rows.Next() { var i SystemSettings if err := rows.Scan( &i.ID, &i.SettingKey, &i.SettingValue, &i.Description, &i.UpdatedAt, &i.SettingType, &i.MinValue, &i.MaxValue, &i.RequiresRestart, &i.Category, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetAnnotationsForBook = `-- name: GetAnnotationsForBook :many SELECT mh.id, mh.selection_text, mh.start_position, mh.end_position, mh.color, mh.created_at, mh.updated_at, 'highlight' as annotation_type, mh.percentage_start, mh.percentage_end, mh.epubcfi_start, mh.epubcfi_end FROM media_highlights mh WHERE mh.media_item_id = $1 AND mh.user_id = $2 AND COALESCE(mh.deleted, FALSE) = FALSE UNION ALL SELECT mn.id, mn.content, mn.position, NULL as end_position, NULL as color, mn.created_at, mn.updated_at, 'note' as annotation_type, mn.percentage_location as percentage_start, NULL as percentage_end, mn.epubcfi_location as epubcfi_start, NULL as epubcfi_end FROM media_notes mn WHERE mn.media_item_id = $1 AND mn.user_id = $2 AND COALESCE(mn.deleted, FALSE) = FALSE ORDER BY created_at DESC ` type GetAnnotationsForBookParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } type GetAnnotationsForBookRow struct { ID pgtype.UUID `db:"id" json:"id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` AnnotationType string `db:"annotation_type" json:"annotation_type"` PercentageStart pgtype.Float8 `db:"percentage_start" json:"percentage_start"` PercentageEnd pgtype.Float8 `db:"percentage_end" json:"percentage_end"` EpubcfiStart pgtype.Text `db:"epubcfi_start" json:"epubcfi_start"` EpubcfiEnd pgtype.Text `db:"epubcfi_end" json:"epubcfi_end"` } func (q *Queries) GetAnnotationsForBook(ctx context.Context, arg GetAnnotationsForBookParams) ([]GetAnnotationsForBookRow, error) { rows, err := q.db.Query(ctx, GetAnnotationsForBook, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []GetAnnotationsForBookRow{} for rows.Next() { var i GetAnnotationsForBookRow if err := rows.Scan( &i.ID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.CreatedAt, &i.UpdatedAt, &i.AnnotationType, &i.PercentageStart, &i.PercentageEnd, &i.EpubcfiStart, &i.EpubcfiEnd, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetBooksByTag = `-- name: GetBooksByTag :many SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE library_id = $1 AND tags @> ARRAY[$2::text] ORDER BY title ASC ` type GetBooksByTagParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Column2 string `db:"column_2" json:"column_2"` } func (q *Queries) GetBooksByTag(ctx context.Context, arg GetBooksByTagParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetBooksByTag, arg.LibraryID, arg.Column2) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetCollection = `-- name: GetCollection :one SELECT id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at FROM collections WHERE id = $1 ` // Get collection func (q *Queries) GetCollection(ctx context.Context, id pgtype.UUID) (Collections, error) { row := q.db.QueryRow(ctx, GetCollection, id) var i Collections err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ) return i, err } const GetCollectionItems = `-- name: GetCollectionItems :many SELECT ci.id, ci.collection_id, ci.media_item_id, ci.added_at, ci.added_by_user_id, ci.excluded, mi.title, mi.author, mi.cover_image_path, mi.library_id FROM collection_items ci JOIN media_items mi ON ci.media_item_id = mi.id WHERE ci.collection_id = $1 ORDER BY ci.added_at DESC ` type GetCollectionItemsRow struct { ID pgtype.UUID `db:"id" json:"id"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` AddedAt pgtype.Timestamptz `db:"added_at" json:"added_at"` AddedByUserID pgtype.UUID `db:"added_by_user_id" json:"added_by_user_id"` Excluded pgtype.Bool `db:"excluded" json:"excluded"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` CoverImagePath pgtype.Text `db:"cover_image_path" json:"cover_image_path"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } // Get collection items func (q *Queries) GetCollectionItems(ctx context.Context, collectionID pgtype.UUID) ([]GetCollectionItemsRow, error) { rows, err := q.db.Query(ctx, GetCollectionItems, collectionID) if err != nil { return nil, err } defer rows.Close() items := []GetCollectionItemsRow{} for rows.Next() { var i GetCollectionItemsRow if err := rows.Scan( &i.ID, &i.CollectionID, &i.MediaItemID, &i.AddedAt, &i.AddedByUserID, &i.Excluded, &i.Title, &i.Author, &i.CoverImagePath, &i.LibraryID, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetCollectionItemsForDashboard = `-- name: GetCollectionItemsForDashboard :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, ci.excluded FROM media_items mi INNER JOIN collection_items ci ON ci.media_item_id = mi.id WHERE ci.collection_id = $1 AND ($2::uuid IS NULL OR mi.library_id = $2::uuid) ORDER BY ci.added_at DESC LIMIT $3 ` type GetCollectionItemsForDashboardParams struct { CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type GetCollectionItemsForDashboardRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` Excluded pgtype.Bool `db:"excluded" json:"excluded"` } func (q *Queries) GetCollectionItemsForDashboard(ctx context.Context, arg GetCollectionItemsForDashboardParams) ([]GetCollectionItemsForDashboardRow, error) { rows, err := q.db.Query(ctx, GetCollectionItemsForDashboard, arg.CollectionID, arg.LibraryID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []GetCollectionItemsForDashboardRow{} for rows.Next() { var i GetCollectionItemsForDashboardRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.Excluded, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetCollectionWithBookCount = `-- name: GetCollectionWithBookCount :one SELECT c.id, c.user_id, c.name, c.description, c.color, c.icon, c.auto_assign_rules, c.view_settings, c.show_on_dashboard, c.query_type, c.priority, c.is_system_collection, c.created_at, COUNT(ci.id) as book_count FROM collections c LEFT JOIN collection_items ci ON c.id = ci.collection_id WHERE c.id = $1 GROUP BY c.id ` type GetCollectionWithBookCountRow struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` Color pgtype.Text `db:"color" json:"color"` Icon pgtype.Text `db:"icon" json:"icon"` AutoAssignRules []byte `db:"auto_assign_rules" json:"auto_assign_rules"` ViewSettings []byte `db:"view_settings" json:"view_settings"` ShowOnDashboard pgtype.Bool `db:"show_on_dashboard" json:"show_on_dashboard"` QueryType pgtype.Text `db:"query_type" json:"query_type"` Priority pgtype.Int4 `db:"priority" json:"priority"` IsSystemCollection pgtype.Bool `db:"is_system_collection" json:"is_system_collection"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` BookCount int64 `db:"book_count" json:"book_count"` } // Get collection with book count func (q *Queries) GetCollectionWithBookCount(ctx context.Context, id pgtype.UUID) (GetCollectionWithBookCountRow, error) { row := q.db.QueryRow(ctx, GetCollectionWithBookCount, id) var i GetCollectionWithBookCountRow err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, &i.BookCount, ) return i, err } const GetCollectionsByUser = `-- name: GetCollectionsByUser :many SELECT id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at FROM collections WHERE user_id = $1 ORDER BY created_at DESC ` // Get collections by user func (q *Queries) GetCollectionsByUser(ctx context.Context, userID pgtype.UUID) ([]Collections, error) { rows, err := q.db.Query(ctx, GetCollectionsByUser, userID) if err != nil { return nil, err } defer rows.Close() items := []Collections{} for rows.Next() { var i Collections if err := rows.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetCollectionsForBook = `-- name: GetCollectionsForBook :many SELECT c.id, c.user_id, c.name, c.description, c.color, c.icon, c.auto_assign_rules, c.view_settings, c.show_on_dashboard, c.query_type, c.priority, c.is_system_collection, c.created_at FROM collections c JOIN collection_items ci ON c.id = ci.collection_id WHERE ci.media_item_id = $1 ` // Get collections for book func (q *Queries) GetCollectionsForBook(ctx context.Context, mediaItemID pgtype.UUID) ([]Collections, error) { rows, err := q.db.Query(ctx, GetCollectionsForBook, mediaItemID) if err != nil { return nil, err } defer rows.Close() items := []Collections{} for rows.Next() { var i Collections if err := rows.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetContinueReadingItems = `-- name: GetContinueReadingItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi INNER JOIN ( SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at FROM reading_progress WHERE user_id = $1 AND percentage > 0 AND percentage < 1 ORDER BY media_item_id, last_read_at DESC ) rp ON rp.media_item_id = mi.id WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid) ORDER BY rp.last_read_at DESC LIMIT $3 ` type GetContinueReadingItemsParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Limit pgtype.Int4 `db:"limit" json:"limit"` } // Smart section queries (for system collections) func (q *Queries) GetContinueReadingItems(ctx context.Context, arg GetContinueReadingItemsParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetContinueReadingItems, arg.UserID, arg.LibraryID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetContinueSeriesItems = `-- name: GetContinueSeriesItems :many WITH user_series_progress AS ( SELECT mi.series, MAX(mi.series_number) as max_read_number, MAX(rp.last_read_at) as last_read_at FROM reading_progress rp JOIN media_items mi ON mi.id = rp.media_item_id WHERE rp.user_id = $2 AND rp.percentage > 0 AND mi.series IS NOT NULL AND mi.series != '' AND ($3::uuid IS NULL OR mi.library_id = $3::uuid) GROUP BY mi.series ), next_books AS ( SELECT DISTINCT ON (mi.series) mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, usp.last_read_at FROM media_items mi JOIN user_series_progress usp ON mi.series = usp.series WHERE ($3::uuid IS NULL OR mi.library_id = $3::uuid) AND (mi.series_number > usp.max_read_number OR usp.max_read_number IS NULL) ORDER BY mi.series, mi.series_number ASC NULLS LAST ) SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence, last_read_at FROM next_books ORDER BY last_read_at DESC NULLS LAST LIMIT $1 ` type GetContinueSeriesItemsParams struct { Limit pgtype.Int4 `db:"limit" json:"limit"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } type GetContinueSeriesItemsRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LastReadAt interface{} `db:"last_read_at" json:"last_read_at"` } func (q *Queries) GetContinueSeriesItems(ctx context.Context, arg GetContinueSeriesItemsParams) ([]GetContinueSeriesItemsRow, error) { rows, err := q.db.Query(ctx, GetContinueSeriesItems, arg.Limit, arg.UserID, arg.LibraryID) if err != nil { return nil, err } defer rows.Close() items := []GetContinueSeriesItemsRow{} for rows.Next() { var i GetContinueSeriesItemsRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LastReadAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetDashboardPreferences = `-- name: GetDashboardPreferences :one SELECT id, user_id, library_id, hidden_collections, collection_order, items_per_section, created_at, updated_at FROM user_dashboard_preferences WHERE user_id = $1 AND ($2::uuid IS NULL OR library_id = $2::uuid) ` type GetDashboardPreferencesParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } // ============================================ // CAROUSEL-STYLE DASHBOARD // ============================================ // Dashboard preferences queries func (q *Queries) GetDashboardPreferences(ctx context.Context, arg GetDashboardPreferencesParams) (UserDashboardPreferences, error) { row := q.db.QueryRow(ctx, GetDashboardPreferences, arg.UserID, arg.LibraryID) var i UserDashboardPreferences err := row.Scan( &i.ID, &i.UserID, &i.LibraryID, &i.HiddenCollections, &i.CollectionOrder, &i.ItemsPerSection, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetDevice = `-- name: GetDevice :one SELECT id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at FROM devices WHERE id = $1 ` func (q *Queries) GetDevice(ctx context.Context, id pgtype.UUID) (Devices, error) { row := q.db.QueryRow(ctx, GetDevice, id) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetDeviceByAuthToken = `-- name: GetDeviceByAuthToken :one SELECT id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at FROM devices WHERE auth_token = $1 ` func (q *Queries) GetDeviceByAuthToken(ctx context.Context, authToken string) (Devices, error) { row := q.db.QueryRow(ctx, GetDeviceByAuthToken, authToken) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetDeviceByIdentifier = `-- name: GetDeviceByIdentifier :one SELECT id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at FROM devices WHERE device_identifier = $1 ` func (q *Queries) GetDeviceByIdentifier(ctx context.Context, deviceIdentifier string) (Devices, error) { row := q.db.QueryRow(ctx, GetDeviceByIdentifier, deviceIdentifier) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetDeviceCatalogByBookhoardUUID = `-- name: GetDeviceCatalogByBookhoardUUID :one SELECT id, device_id, media_item_id, bookhoard_uuid, kobo_content_id, content_id_type, available, delivery_date, delivery_method FROM device_catalogs WHERE device_id = $1 AND bookhoard_uuid = $2 ` type GetDeviceCatalogByBookhoardUUIDParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` BookhoardUuid pgtype.UUID `db:"bookhoard_uuid" json:"bookhoard_uuid"` } // Get device catalog by Bookhoard UUID func (q *Queries) GetDeviceCatalogByBookhoardUUID(ctx context.Context, arg GetDeviceCatalogByBookhoardUUIDParams) (DeviceCatalogs, error) { row := q.db.QueryRow(ctx, GetDeviceCatalogByBookhoardUUID, arg.DeviceID, arg.BookhoardUuid) var i DeviceCatalogs err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.BookhoardUuid, &i.KoboContentID, &i.ContentIDType, &i.Available, &i.DeliveryDate, &i.DeliveryMethod, ) return i, err } const GetDeviceCatalogByKoboContentId = `-- name: GetDeviceCatalogByKoboContentId :one SELECT id, device_id, media_item_id, bookhoard_uuid, kobo_content_id, content_id_type, available, delivery_date, delivery_method FROM device_catalogs WHERE kobo_content_id = $1 ` // Get device catalog by Kobo ContentId func (q *Queries) GetDeviceCatalogByKoboContentId(ctx context.Context, koboContentID string) (DeviceCatalogs, error) { row := q.db.QueryRow(ctx, GetDeviceCatalogByKoboContentId, koboContentID) var i DeviceCatalogs err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.BookhoardUuid, &i.KoboContentID, &i.ContentIDType, &i.Available, &i.DeliveryDate, &i.DeliveryMethod, ) return i, err } const GetDeviceCatalogEntries = `-- name: GetDeviceCatalogEntries :many SELECT dc.id, dc.device_id, dc.media_item_id, dc.bookhoard_uuid, dc.kobo_content_id, dc.content_id_type, dc.available, dc.delivery_date, dc.delivery_method, mi.title, mi.author FROM device_catalogs dc JOIN media_items mi ON dc.media_item_id = mi.id WHERE dc.device_id = $1 AND dc.available = true ORDER BY dc.delivery_date DESC ` type GetDeviceCatalogEntriesRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` BookhoardUuid pgtype.UUID `db:"bookhoard_uuid" json:"bookhoard_uuid"` KoboContentID string `db:"kobo_content_id" json:"kobo_content_id"` ContentIDType pgtype.Text `db:"content_id_type" json:"content_id_type"` Available pgtype.Bool `db:"available" json:"available"` DeliveryDate pgtype.Timestamptz `db:"delivery_date" json:"delivery_date"` DeliveryMethod pgtype.Text `db:"delivery_method" json:"delivery_method"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } // Get device catalog entries func (q *Queries) GetDeviceCatalogEntries(ctx context.Context, deviceID pgtype.UUID) ([]GetDeviceCatalogEntriesRow, error) { rows, err := q.db.Query(ctx, GetDeviceCatalogEntries, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetDeviceCatalogEntriesRow{} for rows.Next() { var i GetDeviceCatalogEntriesRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.BookhoardUuid, &i.KoboContentID, &i.ContentIDType, &i.Available, &i.DeliveryDate, &i.DeliveryMethod, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetDeviceFileAlias = `-- name: GetDeviceFileAlias :one SELECT id, media_item_id, device_id, file_path, file_sha256, confidence_score, last_seen_at FROM device_file_aliases WHERE device_id = $1 AND file_path = $2 ` type GetDeviceFileAliasParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` FilePath string `db:"file_path" json:"file_path"` } // Get device file alias func (q *Queries) GetDeviceFileAlias(ctx context.Context, arg GetDeviceFileAliasParams) (DeviceFileAliases, error) { row := q.db.QueryRow(ctx, GetDeviceFileAlias, arg.DeviceID, arg.FilePath) var i DeviceFileAliases err := row.Scan( &i.ID, &i.MediaItemID, &i.DeviceID, &i.FilePath, &i.FileSha256, &i.ConfidenceScore, &i.LastSeenAt, ) return i, err } const GetDeviceFileAliasBySHA256 = `-- name: GetDeviceFileAliasBySHA256 :one SELECT dfa.id, dfa.media_item_id, dfa.device_id, dfa.file_path, dfa.file_sha256, dfa.confidence_score, dfa.last_seen_at, mi.title, mi.author FROM device_file_aliases dfa JOIN media_items mi ON dfa.media_item_id = mi.id WHERE dfa.file_sha256 = $1 ` type GetDeviceFileAliasBySHA256Row struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` FilePath string `db:"file_path" json:"file_path"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` LastSeenAt pgtype.Timestamptz `db:"last_seen_at" json:"last_seen_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } // Get device file alias by SHA-256 func (q *Queries) GetDeviceFileAliasBySHA256(ctx context.Context, fileSha256 pgtype.Text) (GetDeviceFileAliasBySHA256Row, error) { row := q.db.QueryRow(ctx, GetDeviceFileAliasBySHA256, fileSha256) var i GetDeviceFileAliasBySHA256Row err := row.Scan( &i.ID, &i.MediaItemID, &i.DeviceID, &i.FilePath, &i.FileSha256, &i.ConfidenceScore, &i.LastSeenAt, &i.Title, &i.Author, ) return i, err } const GetDeviceFileAliasesByDevice = `-- name: GetDeviceFileAliasesByDevice :many SELECT dfa.id, dfa.media_item_id, dfa.device_id, dfa.file_path, dfa.file_sha256, dfa.confidence_score, dfa.last_seen_at, mi.title, mi.author FROM device_file_aliases dfa JOIN media_items mi ON dfa.media_item_id = mi.id WHERE dfa.device_id = $1 ORDER BY dfa.last_seen_at DESC ` type GetDeviceFileAliasesByDeviceRow struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` FilePath string `db:"file_path" json:"file_path"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` LastSeenAt pgtype.Timestamptz `db:"last_seen_at" json:"last_seen_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } // Get device file aliases by device func (q *Queries) GetDeviceFileAliasesByDevice(ctx context.Context, deviceID pgtype.UUID) ([]GetDeviceFileAliasesByDeviceRow, error) { rows, err := q.db.Query(ctx, GetDeviceFileAliasesByDevice, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetDeviceFileAliasesByDeviceRow{} for rows.Next() { var i GetDeviceFileAliasesByDeviceRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.DeviceID, &i.FilePath, &i.FileSha256, &i.ConfidenceScore, &i.LastSeenAt, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetDeviceShelfMapping = `-- name: GetDeviceShelfMapping :one SELECT id, collection_id, device_id, device_shelf_name, sync_direction, created_at FROM device_shelf_mappings WHERE device_id = $1 AND collection_id = $2 ` type GetDeviceShelfMappingParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` } // Get device shelf mapping func (q *Queries) GetDeviceShelfMapping(ctx context.Context, arg GetDeviceShelfMappingParams) (DeviceShelfMappings, error) { row := q.db.QueryRow(ctx, GetDeviceShelfMapping, arg.DeviceID, arg.CollectionID) var i DeviceShelfMappings err := row.Scan( &i.ID, &i.CollectionID, &i.DeviceID, &i.DeviceShelfName, &i.SyncDirection, &i.CreatedAt, ) return i, err } const GetDeviceShelfMappings = `-- name: GetDeviceShelfMappings :many SELECT dsm.id, dsm.collection_id, dsm.device_id, dsm.device_shelf_name, dsm.sync_direction, dsm.created_at, c.name as collection_name, c.icon as collection_icon FROM device_shelf_mappings dsm JOIN collections c ON dsm.collection_id = c.id WHERE dsm.device_id = $1 ` type GetDeviceShelfMappingsRow struct { ID pgtype.UUID `db:"id" json:"id"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` DeviceShelfName pgtype.Text `db:"device_shelf_name" json:"device_shelf_name"` SyncDirection pgtype.Text `db:"sync_direction" json:"sync_direction"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` CollectionName string `db:"collection_name" json:"collection_name"` CollectionIcon pgtype.Text `db:"collection_icon" json:"collection_icon"` } // Get device shelf mappings func (q *Queries) GetDeviceShelfMappings(ctx context.Context, deviceID pgtype.UUID) ([]GetDeviceShelfMappingsRow, error) { rows, err := q.db.Query(ctx, GetDeviceShelfMappings, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetDeviceShelfMappingsRow{} for rows.Next() { var i GetDeviceShelfMappingsRow if err := rows.Scan( &i.ID, &i.CollectionID, &i.DeviceID, &i.DeviceShelfName, &i.SyncDirection, &i.CreatedAt, &i.CollectionName, &i.CollectionIcon, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetDictionaryEntry = `-- name: GetDictionaryEntry :one SELECT id, word, definition, part_of_speech, example, etymology, created_at, accessed_at FROM dictionary_cache WHERE word = $1 ` func (q *Queries) GetDictionaryEntry(ctx context.Context, word string) (DictionaryCache, error) { row := q.db.QueryRow(ctx, GetDictionaryEntry, word) var i DictionaryCache err := row.Scan( &i.ID, &i.Word, &i.Definition, &i.PartOfSpeech, &i.Example, &i.Etymology, &i.CreatedAt, &i.AccessedAt, ) return i, err } const GetDistinctSeries = `-- name: GetDistinctSeries :many SELECT series, COUNT(*) as book_count, MAX(series_count) as total_in_series, MAX(created_at) as last_entry_at FROM media_items WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series IS NOT NULL AND series != '' GROUP BY series ORDER BY MAX(created_at) DESC LIMIT $3 OFFSET $2 ` type GetDistinctSeriesParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type GetDistinctSeriesRow struct { Series pgtype.Text `db:"series" json:"series"` BookCount int64 `db:"book_count" json:"book_count"` TotalInSeries interface{} `db:"total_in_series" json:"total_in_series"` LastEntryAt interface{} `db:"last_entry_at" json:"last_entry_at"` } func (q *Queries) GetDistinctSeries(ctx context.Context, arg GetDistinctSeriesParams) ([]GetDistinctSeriesRow, error) { rows, err := q.db.Query(ctx, GetDistinctSeries, arg.LibraryID, arg.Offset, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []GetDistinctSeriesRow{} for rows.Next() { var i GetDistinctSeriesRow if err := rows.Scan( &i.Series, &i.BookCount, &i.TotalInSeries, &i.LastEntryAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetDistinctSeriesCount = `-- name: GetDistinctSeriesCount :one SELECT COUNT(DISTINCT series)::int FROM media_items WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series IS NOT NULL AND series != '' ` func (q *Queries) GetDistinctSeriesCount(ctx context.Context, libraryID pgtype.UUID) (int32, error) { row := q.db.QueryRow(ctx, GetDistinctSeriesCount, libraryID) var column_1 int32 err := row.Scan(&column_1) return column_1, err } const GetFailedSyncQueueItems = `-- name: GetFailedSyncQueueItems :many SELECT id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at FROM sync_queue WHERE status = 'failed' AND attempts < max_attempts ORDER BY priority ASC, created_at ASC LIMIT $1 ` func (q *Queries) GetFailedSyncQueueItems(ctx context.Context, limit int32) ([]SyncQueue, error) { rows, err := q.db.Query(ctx, GetFailedSyncQueueItems, limit) if err != nil { return nil, err } defer rows.Close() items := []SyncQueue{} for rows.Next() { var i SyncQueue if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetFirstAdmin = `-- name: GetFirstAdmin :one SELECT id FROM users WHERE role = 'admin' ORDER BY created_at ASC LIMIT 1 ` func (q *Queries) GetFirstAdmin(ctx context.Context) (pgtype.UUID, error) { row := q.db.QueryRow(ctx, GetFirstAdmin) var id pgtype.UUID err := row.Scan(&id) return id, err } const GetFirstAdminExclude = `-- name: GetFirstAdminExclude :one SELECT id, email, username, theme, first_name, last_name, role, max_devices, created_at, updated_at FROM users WHERE role = 'admin' AND id != $1 ORDER BY created_at ASC LIMIT 1 ` type GetFirstAdminExcludeRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) GetFirstAdminExclude(ctx context.Context, id pgtype.UUID) (GetFirstAdminExcludeRow, error) { row := q.db.QueryRow(ctx, GetFirstAdminExclude, id) var i GetFirstAdminExcludeRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.MaxDevices, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetHashConflict = `-- name: GetHashConflict :one SELECT id, library_id, file_sha256, status, resolution, resolved_by, created_at, resolved_at FROM hash_conflicts WHERE id = $1 ` func (q *Queries) GetHashConflict(ctx context.Context, id pgtype.UUID) (HashConflicts, error) { row := q.db.QueryRow(ctx, GetHashConflict, id) var i HashConflicts err := row.Scan( &i.ID, &i.LibraryID, &i.FileSha256, &i.Status, &i.Resolution, &i.ResolvedBy, &i.CreatedAt, &i.ResolvedAt, ) return i, err } const GetKoboEntitlementByContentId = `-- name: GetKoboEntitlementByContentId :one SELECT ke.id, ke.device_id, ke.media_item_id, ke.entitlement_id, ke.content_id, ke.revision_number, ke.purchase_date, ke.accession_date, ke.book_status, ke.sync_status, ke.kobo_metadata, ke.created_at, ke.updated_at, mi.title, mi.author, mi.file_path FROM kobo_entitlements ke JOIN media_items mi ON ke.media_item_id = mi.id WHERE ke.device_id = $1 AND ke.content_id = $2 ` type GetKoboEntitlementByContentIdParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ContentID string `db:"content_id" json:"content_id"` } type GetKoboEntitlementByContentIdRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` ContentID string `db:"content_id" json:"content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` PurchaseDate pgtype.Timestamptz `db:"purchase_date" json:"purchase_date"` AccessionDate pgtype.Timestamptz `db:"accession_date" json:"accession_date"` BookStatus pgtype.Text `db:"book_status" json:"book_status"` SyncStatus pgtype.Text `db:"sync_status" json:"sync_status"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` } func (q *Queries) GetKoboEntitlementByContentId(ctx context.Context, arg GetKoboEntitlementByContentIdParams) (GetKoboEntitlementByContentIdRow, error) { row := q.db.QueryRow(ctx, GetKoboEntitlementByContentId, arg.DeviceID, arg.ContentID) var i GetKoboEntitlementByContentIdRow err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.EntitlementID, &i.ContentID, &i.RevisionNumber, &i.PurchaseDate, &i.AccessionDate, &i.BookStatus, &i.SyncStatus, &i.KoboMetadata, &i.CreatedAt, &i.UpdatedAt, &i.Title, &i.Author, &i.FilePath, ) return i, err } const GetKoboEntitlementByEntitlementId = `-- name: GetKoboEntitlementByEntitlementId :one SELECT ke.id, ke.device_id, ke.media_item_id, ke.entitlement_id, ke.content_id, ke.revision_number, ke.purchase_date, ke.accession_date, ke.book_status, ke.sync_status, ke.kobo_metadata, ke.created_at, ke.updated_at, mi.title, mi.author, mi.file_path FROM kobo_entitlements ke JOIN media_items mi ON ke.media_item_id = mi.id WHERE ke.device_id = $1 AND ke.entitlement_id = $2 ` type GetKoboEntitlementByEntitlementIdParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` } type GetKoboEntitlementByEntitlementIdRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` ContentID string `db:"content_id" json:"content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` PurchaseDate pgtype.Timestamptz `db:"purchase_date" json:"purchase_date"` AccessionDate pgtype.Timestamptz `db:"accession_date" json:"accession_date"` BookStatus pgtype.Text `db:"book_status" json:"book_status"` SyncStatus pgtype.Text `db:"sync_status" json:"sync_status"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` } func (q *Queries) GetKoboEntitlementByEntitlementId(ctx context.Context, arg GetKoboEntitlementByEntitlementIdParams) (GetKoboEntitlementByEntitlementIdRow, error) { row := q.db.QueryRow(ctx, GetKoboEntitlementByEntitlementId, arg.DeviceID, arg.EntitlementID) var i GetKoboEntitlementByEntitlementIdRow err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.EntitlementID, &i.ContentID, &i.RevisionNumber, &i.PurchaseDate, &i.AccessionDate, &i.BookStatus, &i.SyncStatus, &i.KoboMetadata, &i.CreatedAt, &i.UpdatedAt, &i.Title, &i.Author, &i.FilePath, ) return i, err } const GetKoboEntitlementsForDevice = `-- name: GetKoboEntitlementsForDevice :many SELECT ke.id, ke.device_id, ke.media_item_id, ke.entitlement_id, ke.content_id, ke.revision_number, ke.purchase_date, ke.accession_date, ke.book_status, ke.sync_status, ke.kobo_metadata, ke.created_at, ke.updated_at, mi.title, mi.author, mi.file_path FROM kobo_entitlements ke JOIN media_items mi ON ke.media_item_id = mi.id WHERE ke.device_id = $1 ORDER BY ke.accession_date DESC ` type GetKoboEntitlementsForDeviceRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` ContentID string `db:"content_id" json:"content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` PurchaseDate pgtype.Timestamptz `db:"purchase_date" json:"purchase_date"` AccessionDate pgtype.Timestamptz `db:"accession_date" json:"accession_date"` BookStatus pgtype.Text `db:"book_status" json:"book_status"` SyncStatus pgtype.Text `db:"sync_status" json:"sync_status"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` } func (q *Queries) GetKoboEntitlementsForDevice(ctx context.Context, deviceID pgtype.UUID) ([]GetKoboEntitlementsForDeviceRow, error) { rows, err := q.db.Query(ctx, GetKoboEntitlementsForDevice, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetKoboEntitlementsForDeviceRow{} for rows.Next() { var i GetKoboEntitlementsForDeviceRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.EntitlementID, &i.ContentID, &i.RevisionNumber, &i.PurchaseDate, &i.AccessionDate, &i.BookStatus, &i.SyncStatus, &i.KoboMetadata, &i.CreatedAt, &i.UpdatedAt, &i.Title, &i.Author, &i.FilePath, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetKoboShelfBookCount = `-- name: GetKoboShelfBookCount :one SELECT COUNT(*) as book_count FROM kobo_shelves WHERE device_id = $1 ` func (q *Queries) GetKoboShelfBookCount(ctx context.Context, deviceID pgtype.UUID) (int64, error) { row := q.db.QueryRow(ctx, GetKoboShelfBookCount, deviceID) var book_count int64 err := row.Scan(&book_count) return book_count, err } const GetKoboShelfBooks = `-- name: GetKoboShelfBooks :many SELECT ks.id, ks.device_id, ks.media_item_id, ks.shelf_name, ks.shelf_position, ks.added_at, ks.last_synced_at, ks.collection_id, ks.position_in_collection, mi.title, mi.author, mi.file_path, mi.mime_type, mi.entitlement_id, mi.kobo_content_id, mi.revision_number FROM kobo_shelves ks JOIN media_items mi ON ks.media_item_id = mi.id WHERE ks.device_id = $1 ORDER BY ks.shelf_position ASC, ks.added_at ASC ` type GetKoboShelfBooksRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` ShelfPosition pgtype.Int4 `db:"shelf_position" json:"shelf_position"` AddedAt pgtype.Timestamptz `db:"added_at" json:"added_at"` LastSyncedAt pgtype.Timestamptz `db:"last_synced_at" json:"last_synced_at"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` PositionInCollection pgtype.Int4 `db:"position_in_collection" json:"position_in_collection"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` MimeType pgtype.Text `db:"mime_type" json:"mime_type"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` } func (q *Queries) GetKoboShelfBooks(ctx context.Context, deviceID pgtype.UUID) ([]GetKoboShelfBooksRow, error) { rows, err := q.db.Query(ctx, GetKoboShelfBooks, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetKoboShelfBooksRow{} for rows.Next() { var i GetKoboShelfBooksRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.ShelfName, &i.ShelfPosition, &i.AddedAt, &i.LastSyncedAt, &i.CollectionID, &i.PositionInCollection, &i.Title, &i.Author, &i.FilePath, &i.MimeType, &i.EntitlementID, &i.KoboContentID, &i.RevisionNumber, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetKoboShelfBooksByShelfName = `-- name: GetKoboShelfBooksByShelfName :many SELECT ks.id, ks.device_id, ks.media_item_id, ks.shelf_name, ks.shelf_position, ks.added_at, ks.last_synced_at, ks.collection_id, ks.position_in_collection, mi.title, mi.author, mi.file_path, mi.mime_type, mi.entitlement_id, mi.kobo_content_id, mi.revision_number FROM kobo_shelves ks JOIN media_items mi ON ks.media_item_id = mi.id WHERE ks.device_id = $1 AND ks.shelf_name = $2 ORDER BY ks.shelf_position ASC, ks.added_at ASC ` type GetKoboShelfBooksByShelfNameParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` } type GetKoboShelfBooksByShelfNameRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` ShelfPosition pgtype.Int4 `db:"shelf_position" json:"shelf_position"` AddedAt pgtype.Timestamptz `db:"added_at" json:"added_at"` LastSyncedAt pgtype.Timestamptz `db:"last_synced_at" json:"last_synced_at"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` PositionInCollection pgtype.Int4 `db:"position_in_collection" json:"position_in_collection"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` MimeType pgtype.Text `db:"mime_type" json:"mime_type"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` } func (q *Queries) GetKoboShelfBooksByShelfName(ctx context.Context, arg GetKoboShelfBooksByShelfNameParams) ([]GetKoboShelfBooksByShelfNameRow, error) { rows, err := q.db.Query(ctx, GetKoboShelfBooksByShelfName, arg.DeviceID, arg.ShelfName) if err != nil { return nil, err } defer rows.Close() items := []GetKoboShelfBooksByShelfNameRow{} for rows.Next() { var i GetKoboShelfBooksByShelfNameRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.ShelfName, &i.ShelfPosition, &i.AddedAt, &i.LastSyncedAt, &i.CollectionID, &i.PositionInCollection, &i.Title, &i.Author, &i.FilePath, &i.MimeType, &i.EntitlementID, &i.KoboContentID, &i.RevisionNumber, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetKoboShelvesByCollection = `-- name: GetKoboShelvesByCollection :many SELECT ks.id, ks.device_id, ks.media_item_id, ks.shelf_name, ks.shelf_position, ks.added_at, ks.last_synced_at, ks.collection_id, ks.position_in_collection, mi.title, mi.author FROM kobo_shelves ks JOIN media_items mi ON ks.media_item_id = mi.id WHERE ks.device_id = $1 AND ks.collection_id = $2 ORDER BY ks.position_in_collection ASC, ks.shelf_position ASC ` type GetKoboShelvesByCollectionParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` } type GetKoboShelvesByCollectionRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ShelfName pgtype.Text `db:"shelf_name" json:"shelf_name"` ShelfPosition pgtype.Int4 `db:"shelf_position" json:"shelf_position"` AddedAt pgtype.Timestamptz `db:"added_at" json:"added_at"` LastSyncedAt pgtype.Timestamptz `db:"last_synced_at" json:"last_synced_at"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` PositionInCollection pgtype.Int4 `db:"position_in_collection" json:"position_in_collection"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } // Get Kobo shelves by collection func (q *Queries) GetKoboShelvesByCollection(ctx context.Context, arg GetKoboShelvesByCollectionParams) ([]GetKoboShelvesByCollectionRow, error) { rows, err := q.db.Query(ctx, GetKoboShelvesByCollection, arg.DeviceID, arg.CollectionID) if err != nil { return nil, err } defer rows.Close() items := []GetKoboShelvesByCollectionRow{} for rows.Next() { var i GetKoboShelvesByCollectionRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.ShelfName, &i.ShelfPosition, &i.AddedAt, &i.LastSyncedAt, &i.CollectionID, &i.PositionInCollection, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetLibrary = `-- name: GetLibrary :one SELECT l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at, 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 ` type GetLibraryRow 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"` TypeName string `db:"type_name" json:"type_name"` TypeDescription pgtype.Text `db:"type_description" json:"type_description"` } func (q *Queries) GetLibrary(ctx context.Context, id pgtype.UUID) (GetLibraryRow, error) { row := q.db.QueryRow(ctx, GetLibrary, id) var i GetLibraryRow err := row.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, &i.TypeName, &i.TypeDescription, ) return i, err } const GetLibraryByFolder = `-- name: GetLibraryByFolder :one SELECT lf.library_id, l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at FROM library_folders lf JOIN libraries l ON lf.library_id = l.id WHERE lf.folder_path = $1 ` type GetLibraryByFolderRow struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` 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"` } func (q *Queries) GetLibraryByFolder(ctx context.Context, folderPath string) (GetLibraryByFolderRow, error) { row := q.db.QueryRow(ctx, GetLibraryByFolder, folderPath) var i GetLibraryByFolderRow err := row.Scan( &i.LibraryID, &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetLibraryByFolderPathPrefix = `-- name: GetLibraryByFolderPathPrefix :one SELECT lf.library_id, lf.folder_path, l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at FROM library_folders lf JOIN libraries l ON lf.library_id = l.id WHERE $1 LIKE lf.folder_path || '%' ORDER BY LENGTH(lf.folder_path) DESC LIMIT 1 ` type GetLibraryByFolderPathPrefixRow struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FolderPath string `db:"folder_path" json:"folder_path"` 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"` } func (q *Queries) GetLibraryByFolderPathPrefix(ctx context.Context, folderPath string) (GetLibraryByFolderPathPrefixRow, error) { row := q.db.QueryRow(ctx, GetLibraryByFolderPathPrefix, folderPath) var i GetLibraryByFolderPathPrefixRow err := row.Scan( &i.LibraryID, &i.FolderPath, &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetLibraryFolders = `-- name: GetLibraryFolders :many SELECT id, library_id, folder_path, created_at FROM library_folders WHERE library_id = $1 ORDER BY created_at ` func (q *Queries) GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID) ([]LibraryFolders, error) { rows, err := q.db.Query(ctx, GetLibraryFolders, libraryID) if err != nil { return nil, err } defer rows.Close() items := []LibraryFolders{} for rows.Next() { var i LibraryFolders if err := rows.Scan( &i.ID, &i.LibraryID, &i.FolderPath, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetLibraryItems = `-- name: GetLibraryItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid) ORDER BY mi.created_at DESC ` func (q *Queries) GetLibraryItems(ctx context.Context, libraryID pgtype.UUID) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetLibraryItems, libraryID) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetLibraryType = `-- name: GetLibraryType :one SELECT id, name, description, allowed_extensions, created_at FROM library_types WHERE id = $1 ` func (q *Queries) GetLibraryType(ctx context.Context, id pgtype.UUID) (LibraryTypes, error) { row := q.db.QueryRow(ctx, GetLibraryType, id) var i LibraryTypes err := row.Scan( &i.ID, &i.Name, &i.Description, &i.AllowedExtensions, &i.CreatedAt, ) return i, err } const GetLibraryTypeByName = `-- name: GetLibraryTypeByName :one SELECT id, name, description, allowed_extensions, created_at FROM library_types WHERE name = $1 ` func (q *Queries) GetLibraryTypeByName(ctx context.Context, name string) (LibraryTypes, error) { row := q.db.QueryRow(ctx, GetLibraryTypeByName, name) var i LibraryTypes err := row.Scan( &i.ID, &i.Name, &i.Description, &i.AllowedExtensions, &i.CreatedAt, ) return i, err } const GetLibraryTypes = `-- name: GetLibraryTypes :many SELECT id, name, description, allowed_extensions, created_at FROM library_types ORDER BY name ` // Library Types queries func (q *Queries) GetLibraryTypes(ctx context.Context) ([]LibraryTypes, error) { rows, err := q.db.Query(ctx, GetLibraryTypes) if err != nil { return nil, err } defer rows.Close() items := []LibraryTypes{} for rows.Next() { var i LibraryTypes if err := rows.Scan( &i.ID, &i.Name, &i.Description, &i.AllowedExtensions, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetLibraryVisibility = `-- name: GetLibraryVisibility :one SELECT id, user_id, library_id, is_visible, created_at, updated_at FROM library_visibility WHERE user_id = $1 AND library_id = $2 ` type GetLibraryVisibilityParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } func (q *Queries) GetLibraryVisibility(ctx context.Context, arg GetLibraryVisibilityParams) (LibraryVisibility, error) { row := q.db.QueryRow(ctx, GetLibraryVisibility, arg.UserID, arg.LibraryID) var i LibraryVisibility err := row.Scan( &i.ID, &i.UserID, &i.LibraryID, &i.IsVisible, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetLibraryWithType = `-- name: GetLibraryWithType :one SELECT l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at, lt.name as type_name, lt.description as type_description, lt.allowed_extensions FROM libraries l JOIN library_types lt ON l.library_type_id = lt.id WHERE l.id = $1 ` type GetLibraryWithTypeRow 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"` TypeName string `db:"type_name" json:"type_name"` TypeDescription pgtype.Text `db:"type_description" json:"type_description"` AllowedExtensions []string `db:"allowed_extensions" json:"allowed_extensions"` } // ============================================================================ // LIBRARY WITH TYPE INFO QUERIES // ============================================================================ func (q *Queries) GetLibraryWithType(ctx context.Context, id pgtype.UUID) (GetLibraryWithTypeRow, error) { row := q.db.QueryRow(ctx, GetLibraryWithType, id) var i GetLibraryWithTypeRow err := row.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, &i.TypeName, &i.TypeDescription, &i.AllowedExtensions, ) return i, err } const GetMediaBookmark = `-- name: GetMediaBookmark :one SELECT id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at FROM media_bookmarks WHERE id = $1 ` func (q *Queries) GetMediaBookmark(ctx context.Context, id pgtype.UUID) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, GetMediaBookmark, id) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaBookmarkByDedupKey = `-- name: GetMediaBookmarkByDedupKey :one SELECT id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at FROM media_bookmarks WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 ORDER BY deleted ASC, deleted_at DESC NULLS LAST LIMIT 1 ` type GetMediaBookmarkByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } // ============================================ // ANNOTATION SYNC QUERIES (bookmarks) // ============================================ func (q *Queries) GetMediaBookmarkByDedupKey(ctx context.Context, arg GetMediaBookmarkByDedupKeyParams) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, GetMediaBookmarkByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaBookmarks = `-- name: GetMediaBookmarks :many SELECT id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at FROM media_bookmarks WHERE media_item_id = $1 AND user_id = $2 AND COALESCE(deleted, FALSE) = FALSE ORDER BY created_at DESC ` type GetMediaBookmarksParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetMediaBookmarks(ctx context.Context, arg GetMediaBookmarksParams) ([]MediaBookmarks, error) { rows, err := q.db.Query(ctx, GetMediaBookmarks, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []MediaBookmarks{} for rows.Next() { var i MediaBookmarks if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetMediaHighlight = `-- name: GetMediaHighlight :one SELECT id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at FROM media_highlights WHERE id = $1 ` func (q *Queries) GetMediaHighlight(ctx context.Context, id pgtype.UUID) (MediaHighlights, error) { row := q.db.QueryRow(ctx, GetMediaHighlight, id) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaHighlightByDedupKey = `-- name: GetMediaHighlightByDedupKey :one SELECT id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at FROM media_highlights WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 ORDER BY deleted ASC, deleted_at DESC NULLS LAST LIMIT 1 ` type GetMediaHighlightByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } // ============================================ // ANNOTATION SYNC QUERIES (highlights) // ============================================ func (q *Queries) GetMediaHighlightByDedupKey(ctx context.Context, arg GetMediaHighlightByDedupKeyParams) (MediaHighlights, error) { row := q.db.QueryRow(ctx, GetMediaHighlightByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaHighlights = `-- name: GetMediaHighlights :many SELECT id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at FROM media_highlights WHERE media_item_id = $1 AND user_id = $2 AND COALESCE(deleted, FALSE) = FALSE ORDER BY created_at DESC ` type GetMediaHighlightsParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetMediaHighlights(ctx context.Context, arg GetMediaHighlightsParams) ([]MediaHighlights, error) { rows, err := q.db.Query(ctx, GetMediaHighlights, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []MediaHighlights{} for rows.Next() { var i MediaHighlights if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetMediaItem = `-- name: GetMediaItem :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE id = $1 ` func (q *Queries) GetMediaItem(ctx context.Context, id pgtype.UUID) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItem, id) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByFilePath = `-- name: GetMediaItemByFilePath :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_path = $1 AND library_id = $2 ` type GetMediaItemByFilePathParams struct { FilePath string `db:"file_path" json:"file_path"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } func (q *Queries) GetMediaItemByFilePath(ctx context.Context, arg GetMediaItemByFilePathParams) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByFilePath, arg.FilePath, arg.LibraryID) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByFilePathAnyLibrary = `-- name: GetMediaItemByFilePathAnyLibrary :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_path = $1 LIMIT 1 ` func (q *Queries) GetMediaItemByFilePathAnyLibrary(ctx context.Context, filePath string) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByFilePathAnyLibrary, filePath) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByFilePathForSync = `-- name: GetMediaItemByFilePathForSync :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_path = $1 ` // ============================================ // KOREADER SYNC PROTOCOL // ============================================ func (q *Queries) GetMediaItemByFilePathForSync(ctx context.Context, filePath string) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByFilePathForSync, filePath) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByKoboContentId = `-- name: GetMediaItemByKoboContentId :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE kobo_content_id = $1 ` func (q *Queries) GetMediaItemByKoboContentId(ctx context.Context, koboContentID pgtype.Text) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByKoboContentId, koboContentID) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByOPFIdentifier = `-- name: GetMediaItemByOPFIdentifier :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE opf_identifier = $1 ` // Get media item by OPF identifier func (q *Queries) GetMediaItemByOPFIdentifier(ctx context.Context, opfIdentifier pgtype.Text) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByOPFIdentifier, opfIdentifier) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemByOPFUUID = `-- name: GetMediaItemByOPFUUID :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE opf_uuid = $1 ` // Get media item by OPF UUID func (q *Queries) GetMediaItemByOPFUUID(ctx context.Context, opfUuid pgtype.Text) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemByOPFUUID, opfUuid) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemBySHA256 = `-- name: GetMediaItemBySHA256 :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_sha256 = $1 ` // Get media item by SHA-256 hash func (q *Queries) GetMediaItemBySHA256(ctx context.Context, fileSha256 pgtype.Text) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemBySHA256, fileSha256) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemBySHA256AndLibrary = `-- name: GetMediaItemBySHA256AndLibrary :one SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_sha256 = $1 AND library_id = $2 ` type GetMediaItemBySHA256AndLibraryParams struct { FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } // Get media item by SHA-256 hash within a specific library (content dedup) func (q *Queries) GetMediaItemBySHA256AndLibrary(ctx context.Context, arg GetMediaItemBySHA256AndLibraryParams) (MediaItems, error) { row := q.db.QueryRow(ctx, GetMediaItemBySHA256AndLibrary, arg.FileSha256, arg.LibraryID) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const GetMediaItemFormatBySHA256 = `-- name: GetMediaItemFormatBySHA256 :one SELECT id, media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, created_at, converted_from_format_id FROM media_item_formats WHERE file_sha256 = $1 ` // Get media item format by SHA-256 func (q *Queries) GetMediaItemFormatBySHA256(ctx context.Context, fileSha256 pgtype.Text) (MediaItemFormats, error) { row := q.db.QueryRow(ctx, GetMediaItemFormatBySHA256, fileSha256) var i MediaItemFormats err := row.Scan( &i.ID, &i.MediaItemID, &i.FormatType, &i.FilePath, &i.FileSha256, &i.FileSizeBytes, &i.MimeType, &i.CreatedAt, &i.ConvertedFromFormatID, ) return i, err } const GetMediaItemFormatByType = `-- name: GetMediaItemFormatByType :one SELECT id, media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, created_at, converted_from_format_id FROM media_item_formats WHERE media_item_id = $1 AND format_type = $2 ` type GetMediaItemFormatByTypeParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` FormatType string `db:"format_type" json:"format_type"` } // Get media item format by type func (q *Queries) GetMediaItemFormatByType(ctx context.Context, arg GetMediaItemFormatByTypeParams) (MediaItemFormats, error) { row := q.db.QueryRow(ctx, GetMediaItemFormatByType, arg.MediaItemID, arg.FormatType) var i MediaItemFormats err := row.Scan( &i.ID, &i.MediaItemID, &i.FormatType, &i.FilePath, &i.FileSha256, &i.FileSizeBytes, &i.MimeType, &i.CreatedAt, &i.ConvertedFromFormatID, ) return i, err } const GetMediaItemFormats = `-- name: GetMediaItemFormats :many SELECT id, media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, created_at, converted_from_format_id FROM media_item_formats WHERE media_item_id = $1 ` // Get media item formats func (q *Queries) GetMediaItemFormats(ctx context.Context, mediaItemID pgtype.UUID) ([]MediaItemFormats, error) { rows, err := q.db.Query(ctx, GetMediaItemFormats, mediaItemID) if err != nil { return nil, err } defer rows.Close() items := []MediaItemFormats{} for rows.Next() { var i MediaItemFormats if err := rows.Scan( &i.ID, &i.MediaItemID, &i.FormatType, &i.FilePath, &i.FileSha256, &i.FileSizeBytes, &i.MimeType, &i.CreatedAt, &i.ConvertedFromFormatID, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetMediaItemUsageCounts = `-- name: GetMediaItemUsageCounts :one SELECT (SELECT COUNT(*) FROM reading_progress rp WHERE rp.media_item_id = $1) AS progress_count, (SELECT COUNT(*) FROM media_highlights mh WHERE mh.media_item_id = $1) AS highlights_count, (SELECT COUNT(*) FROM media_bookmarks mb WHERE mb.media_item_id = $1) AS bookmarks_count, (SELECT COUNT(*) FROM media_notes mn WHERE mn.media_item_id = $1) AS notes_count, (SELECT COUNT(*) FROM collection_items ci WHERE ci.media_item_id = $1) AS collections_count ` type GetMediaItemUsageCountsRow struct { ProgressCount int64 `db:"progress_count" json:"progress_count"` HighlightsCount int64 `db:"highlights_count" json:"highlights_count"` BookmarksCount int64 `db:"bookmarks_count" json:"bookmarks_count"` NotesCount int64 `db:"notes_count" json:"notes_count"` CollectionsCount int64 `db:"collections_count" json:"collections_count"` } // Per-item user-data counts, used when choosing which duplicate copy to keep func (q *Queries) GetMediaItemUsageCounts(ctx context.Context, mediaItemID pgtype.UUID) (GetMediaItemUsageCountsRow, error) { row := q.db.QueryRow(ctx, GetMediaItemUsageCounts, mediaItemID) var i GetMediaItemUsageCountsRow err := row.Scan( &i.ProgressCount, &i.HighlightsCount, &i.BookmarksCount, &i.NotesCount, &i.CollectionsCount, ) return i, err } const GetMediaNote = `-- name: GetMediaNote :one SELECT 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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at FROM media_notes WHERE id = $1 ` func (q *Queries) GetMediaNote(ctx context.Context, id pgtype.UUID) (MediaNotes, error) { row := q.db.QueryRow(ctx, GetMediaNote, id) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaNoteByDedupKey = `-- name: GetMediaNoteByDedupKey :one SELECT 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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at FROM media_notes WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 ORDER BY deleted ASC, deleted_at DESC NULLS LAST LIMIT 1 ` type GetMediaNoteByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } // ============================================ // ANNOTATION SYNC QUERIES (notes) // ============================================ func (q *Queries) GetMediaNoteByDedupKey(ctx context.Context, arg GetMediaNoteByDedupKeyParams) (MediaNotes, error) { row := q.db.QueryRow(ctx, GetMediaNoteByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const GetMediaNotes = `-- name: GetMediaNotes :many SELECT 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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at FROM media_notes WHERE media_item_id = $1 AND user_id = $2 AND COALESCE(deleted, FALSE) = FALSE ORDER BY created_at DESC ` type GetMediaNotesParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetMediaNotes(ctx context.Context, arg GetMediaNotesParams) ([]MediaNotes, error) { rows, err := q.db.Query(ctx, GetMediaNotes, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []MediaNotes{} for rows.Next() { var i MediaNotes if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetMediaRating = `-- name: GetMediaRating :one SELECT id, media_item_id, user_id, rating, created_at, updated_at FROM media_ratings WHERE media_item_id = $1 AND user_id = $2 ` type GetMediaRatingParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetMediaRating(ctx context.Context, arg GetMediaRatingParams) (MediaRatings, error) { row := q.db.QueryRow(ctx, GetMediaRating, arg.MediaItemID, arg.UserID) var i MediaRatings err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Rating, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetMediaRatings = `-- name: GetMediaRatings :many SELECT mr.id, mr.media_item_id, mr.user_id, mr.rating, mr.created_at, mr.updated_at, 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 ` type GetMediaRatingsRow 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 int32 `db:"rating" json:"rating"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` Username string `db:"username" json:"username"` } func (q *Queries) GetMediaRatings(ctx context.Context, mediaItemID pgtype.UUID) ([]GetMediaRatingsRow, error) { rows, err := q.db.Query(ctx, GetMediaRatings, mediaItemID) if err != nil { return nil, err } defer rows.Close() items := []GetMediaRatingsRow{} for rows.Next() { var i GetMediaRatingsRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Rating, &i.CreatedAt, &i.UpdatedAt, &i.Username, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetNextRetryTime = `-- name: GetNextRetryTime :one SELECT CASE WHEN attempts = 0 THEN NOW() WHEN attempts = 1 THEN NOW() + INTERVAL '1 minute' WHEN attempts = 2 THEN NOW() + INTERVAL '5 minutes' WHEN attempts = 3 THEN NOW() + INTERVAL '15 minutes' WHEN attempts = 4 THEN NOW() + INTERVAL '1 hour' ELSE NOW() + INTERVAL '24 hours' END as next_retry_time ` func (q *Queries) GetNextRetryTime(ctx context.Context) (interface{}, error) { row := q.db.QueryRow(ctx, GetNextRetryTime) var next_retry_time interface{} err := row.Scan(&next_retry_time) return next_retry_time, err } const GetNotStartedItems = `-- name: GetNotStartedItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid) AND NOT EXISTS ( SELECT 1 FROM reading_progress rp WHERE rp.media_item_id = mi.id AND rp.user_id = $2 AND rp.percentage > 0 ) ORDER BY mi.created_at DESC LIMIT $3 ` type GetNotStartedItemsParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Limit pgtype.Int4 `db:"limit" json:"limit"` } func (q *Queries) GetNotStartedItems(ctx context.Context, arg GetNotStartedItemsParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetNotStartedItems, arg.LibraryID, arg.UserID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetOpdsToken = `-- name: GetOpdsToken :one SELECT ot.id, ot.device_id, ot.token, ot.token_type, ot.expires_at, ot.created_at, d.device_name, d.device_type FROM opds_tokens ot JOIN devices d ON ot.device_id = d.id WHERE ot.token = $1 AND ot.expires_at > NOW() ` type GetOpdsTokenRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` Token string `db:"token" json:"token"` TokenType pgtype.Text `db:"token_type" json:"token_type"` ExpiresAt pgtype.Timestamptz `db:"expires_at" json:"expires_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` } // Get OPDS token func (q *Queries) GetOpdsToken(ctx context.Context, token string) (GetOpdsTokenRow, error) { row := q.db.QueryRow(ctx, GetOpdsToken, token) var i GetOpdsTokenRow err := row.Scan( &i.ID, &i.DeviceID, &i.Token, &i.TokenType, &i.ExpiresAt, &i.CreatedAt, &i.DeviceName, &i.DeviceType, ) return i, err } const GetOpdsTokensByDevice = `-- name: GetOpdsTokensByDevice :many SELECT id, device_id, token, token_type, expires_at, created_at FROM opds_tokens WHERE device_id = $1 AND expires_at > NOW() ` // Get OPDS tokens by device func (q *Queries) GetOpdsTokensByDevice(ctx context.Context, deviceID pgtype.UUID) ([]OpdsTokens, error) { rows, err := q.db.Query(ctx, GetOpdsTokensByDevice, deviceID) if err != nil { return nil, err } defer rows.Close() items := []OpdsTokens{} for rows.Next() { var i OpdsTokens if err := rows.Scan( &i.ID, &i.DeviceID, &i.Token, &i.TokenType, &i.ExpiresAt, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetPanelData = `-- name: GetPanelData :one SELECT id, media_item_id, page_number, detection_method, panels, created_at, updated_at FROM panel_data WHERE media_item_id = $1 AND page_number = $2 ` type GetPanelDataParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` PageNumber int32 `db:"page_number" json:"page_number"` } func (q *Queries) GetPanelData(ctx context.Context, arg GetPanelDataParams) (PanelData, error) { row := q.db.QueryRow(ctx, GetPanelData, arg.MediaItemID, arg.PageNumber) var i PanelData err := row.Scan( &i.ID, &i.MediaItemID, &i.PageNumber, &i.DetectionMethod, &i.Panels, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetPopularBooks = `-- name: GetPopularBooks :many SELECT mi.id, mi.title, mi.author, COUNT(*) as read_count, AVG(rh.progress_percentage) as avg_completion, MAX(rh.created_at) as last_read FROM media_items mi JOIN reading_history rh ON rh.media_item_id = mi.id WHERE rh.user_id = $1 GROUP BY mi.id, mi.title, mi.author ORDER BY read_count DESC LIMIT $2 ` type GetPopularBooksParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Limit int32 `db:"limit" json:"limit"` } type GetPopularBooksRow struct { ID pgtype.UUID `db:"id" json:"id"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` ReadCount int64 `db:"read_count" json:"read_count"` AvgCompletion float64 `db:"avg_completion" json:"avg_completion"` LastRead interface{} `db:"last_read" json:"last_read"` } // Get most popular books for a user func (q *Queries) GetPopularBooks(ctx context.Context, arg GetPopularBooksParams) ([]GetPopularBooksRow, error) { rows, err := q.db.Query(ctx, GetPopularBooks, arg.UserID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []GetPopularBooksRow{} for rows.Next() { var i GetPopularBooksRow if err := rows.Scan( &i.ID, &i.Title, &i.Author, &i.ReadCount, &i.AvgCompletion, &i.LastRead, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetProcessingIssueStats = `-- name: GetProcessingIssueStats :one SELECT COUNT(*) FILTER (WHERE severity = 'error' AND resolved = false) as error_count, COUNT(*) FILTER (WHERE severity = 'warning' AND resolved = false) as warning_count, COUNT(*) FILTER (WHERE severity = 'info' AND resolved = false) as info_count FROM processing_issues WHERE library_id = $1 ` type GetProcessingIssueStatsRow struct { ErrorCount int64 `db:"error_count" json:"error_count"` WarningCount int64 `db:"warning_count" json:"warning_count"` InfoCount int64 `db:"info_count" json:"info_count"` } func (q *Queries) GetProcessingIssueStats(ctx context.Context, libraryID pgtype.UUID) (GetProcessingIssueStatsRow, error) { row := q.db.QueryRow(ctx, GetProcessingIssueStats, libraryID) var i GetProcessingIssueStatsRow err := row.Scan(&i.ErrorCount, &i.WarningCount, &i.InfoCount) return i, err } const GetReaderSettings = `-- name: GetReaderSettings :one SELECT setting_value FROM reader_settings WHERE user_id = $1 AND setting_key = 'reader_settings' ` func (q *Queries) GetReaderSettings(ctx context.Context, userID pgtype.UUID) ([]byte, error) { row := q.db.QueryRow(ctx, GetReaderSettings, userID) var setting_value []byte err := row.Scan(&setting_value) return setting_value, err } const GetReadingHistory = `-- name: GetReadingHistory :many SELECT id, user_id, media_item_id, device_id, progress_percentage, reading_session_start, reading_session_end, pages_read, time_spent_seconds, device_metadata, created_at FROM reading_history WHERE user_id = $1 AND media_item_id = $2 ORDER BY created_at DESC LIMIT $3 ` type GetReadingHistoryParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` Limit int32 `db:"limit" json:"limit"` } // Get reading history for a user and book func (q *Queries) GetReadingHistory(ctx context.Context, arg GetReadingHistoryParams) ([]ReadingHistory, error) { rows, err := q.db.Query(ctx, GetReadingHistory, arg.UserID, arg.MediaItemID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []ReadingHistory{} for rows.Next() { var i ReadingHistory if err := rows.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.DeviceID, &i.ProgressPercentage, &i.ReadingSessionStart, &i.ReadingSessionEnd, &i.PagesRead, &i.TimeSpentSeconds, &i.DeviceMetadata, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetReadingProgress = `-- name: GetReadingProgress :one SELECT id, media_item_id, user_id, current_page, total_pages, last_read_at, percentage, character_offset, epubcfi, context_text, chapter, chapter_progress, viewport_x, viewport_y, zoom_level, scroll_position_x, scroll_position_y, panel_number, reading_mode, last_sync_device, last_sync_source, last_sync_timestamp, conflict_detected, conflict_resolved FROM reading_progress WHERE media_item_id = $1 AND user_id = $2 ` type GetReadingProgressParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetReadingProgress(ctx context.Context, arg GetReadingProgressParams) (ReadingProgress, error) { row := q.db.QueryRow(ctx, GetReadingProgress, arg.MediaItemID, arg.UserID) var i ReadingProgress err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.CurrentPage, &i.TotalPages, &i.LastReadAt, &i.Percentage, &i.CharacterOffset, &i.Epubcfi, &i.ContextText, &i.Chapter, &i.ChapterProgress, &i.ViewportX, &i.ViewportY, &i.ZoomLevel, &i.ScrollPositionX, &i.ScrollPositionY, &i.PanelNumber, &i.ReadingMode, &i.LastSyncDevice, &i.LastSyncSource, &i.LastSyncTimestamp, &i.ConflictDetected, &i.ConflictResolved, ) return i, err } const GetReadingSpeed = `-- name: GetReadingSpeed :one SELECT id, user_id, media_item_id, words_per_minute, pages_per_minute, pages_read, total_reading_minutes, last_read_at, updated_at FROM reading_speed WHERE user_id = $1 AND media_item_id = $2 ` type GetReadingSpeedParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) GetReadingSpeed(ctx context.Context, arg GetReadingSpeedParams) (ReadingSpeed, error) { row := q.db.QueryRow(ctx, GetReadingSpeed, arg.UserID, arg.MediaItemID) var i ReadingSpeed err := row.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.WordsPerMinute, &i.PagesPerMinute, &i.PagesRead, &i.TotalReadingMinutes, &i.LastReadAt, &i.UpdatedAt, ) return i, err } const GetRecentlyAddedItems = `-- name: GetRecentlyAddedItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid) ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC LIMIT $2 ` type GetRecentlyAddedItemsParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Limit pgtype.Int4 `db:"limit" json:"limit"` } func (q *Queries) GetRecentlyAddedItems(ctx context.Context, arg GetRecentlyAddedItemsParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetRecentlyAddedItems, arg.LibraryID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetRecentlyReadItems = `-- name: GetRecentlyReadItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi INNER JOIN ( SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at FROM reading_progress WHERE user_id = $1 AND percentage >= 1 ORDER BY media_item_id, last_read_at DESC ) rp ON rp.media_item_id = mi.id WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid) ORDER BY rp.last_read_at DESC LIMIT $3 ` type GetRecentlyReadItemsParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Limit pgtype.Int4 `db:"limit" json:"limit"` } func (q *Queries) GetRecentlyReadItems(ctx context.Context, arg GetRecentlyReadItemsParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetRecentlyReadItems, arg.UserID, arg.LibraryID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetRefreshToken = `-- name: GetRefreshToken :one SELECT rt.id, rt.user_id, rt.token, rt.expires_at, rt.created_at, rt.revoked_at, u.email, u.username, u.role FROM refresh_tokens rt JOIN users u ON rt.user_id = u.id WHERE rt.token = $1 AND rt.revoked_at IS NULL AND rt.expires_at > NOW() ` type GetRefreshTokenRow struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Token pgtype.UUID `db:"token" json:"token"` ExpiresAt pgtype.Timestamptz `db:"expires_at" json:"expires_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` RevokedAt pgtype.Timestamptz `db:"revoked_at" json:"revoked_at"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Role string `db:"role" json:"role"` } func (q *Queries) GetRefreshToken(ctx context.Context, token pgtype.UUID) (GetRefreshTokenRow, error) { row := q.db.QueryRow(ctx, GetRefreshToken, token) var i GetRefreshTokenRow err := row.Scan( &i.ID, &i.UserID, &i.Token, &i.ExpiresAt, &i.CreatedAt, &i.RevokedAt, &i.Email, &i.Username, &i.Role, ) return i, err } const GetSavedFilterByID = `-- name: GetSavedFilterByID :one SELECT id, user_id, name, resource_type, filters, created_at, updated_at FROM saved_filters WHERE id = $1 AND user_id = $2 ` type GetSavedFilterByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) GetSavedFilterByID(ctx context.Context, arg GetSavedFilterByIDParams) (SavedFilters, error) { row := q.db.QueryRow(ctx, GetSavedFilterByID, arg.ID, arg.UserID) var i SavedFilters err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.ResourceType, &i.Filters, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetSavedFilters = `-- name: GetSavedFilters :many SELECT id, user_id, name, resource_type, filters, created_at, updated_at FROM saved_filters WHERE user_id = $1 AND resource_type = $2 ORDER BY created_at DESC ` type GetSavedFiltersParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` ResourceType string `db:"resource_type" json:"resource_type"` } func (q *Queries) GetSavedFilters(ctx context.Context, arg GetSavedFiltersParams) ([]SavedFilters, error) { rows, err := q.db.Query(ctx, GetSavedFilters, arg.UserID, arg.ResourceType) if err != nil { return nil, err } defer rows.Close() items := []SavedFilters{} for rows.Next() { var i SavedFilters if err := rows.Scan( &i.ID, &i.UserID, &i.Name, &i.ResourceType, &i.Filters, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetSeriesBooks = `-- name: GetSeriesBooks :many SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE series = $1 ORDER BY series_number ASC NULLS LAST ` func (q *Queries) GetSeriesBooks(ctx context.Context, series pgtype.Text) ([]MediaItems, error) { rows, err := q.db.Query(ctx, GetSeriesBooks, series) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetSeriesCovers = `-- name: GetSeriesCovers :many SELECT cover_image_path, library_id FROM media_items WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series = $2 AND cover_image_path IS NOT NULL AND cover_image_path != '' ORDER BY series_number ASC NULLS LAST LIMIT $3 ` type GetSeriesCoversParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Series pgtype.Text `db:"series" json:"series"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type GetSeriesCoversRow struct { CoverImagePath pgtype.Text `db:"cover_image_path" json:"cover_image_path"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } func (q *Queries) GetSeriesCovers(ctx context.Context, arg GetSeriesCoversParams) ([]GetSeriesCoversRow, error) { rows, err := q.db.Query(ctx, GetSeriesCovers, arg.LibraryID, arg.Series, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []GetSeriesCoversRow{} for rows.Next() { var i GetSeriesCoversRow if err := rows.Scan(&i.CoverImagePath, &i.LibraryID); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetStuckSyncQueueItems = `-- name: GetStuckSyncQueueItems :many SELECT id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at FROM sync_queue WHERE status = 'processing' AND created_at < NOW() - INTERVAL '1 hour' ORDER BY priority ASC, created_at ASC ` func (q *Queries) GetStuckSyncQueueItems(ctx context.Context) ([]SyncQueue, error) { rows, err := q.db.Query(ctx, GetStuckSyncQueueItems) if err != nil { return nil, err } defer rows.Close() items := []SyncQueue{} for rows.Next() { var i SyncQueue if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetSyncConflict = `-- name: GetSyncConflict :one SELECT id, media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_by, resolved_at, created_at FROM sync_conflicts WHERE id = $1 ` func (q *Queries) GetSyncConflict(ctx context.Context, id pgtype.UUID) (SyncConflicts, error) { row := q.db.QueryRow(ctx, GetSyncConflict, id) var i SyncConflicts err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const GetSyncQueueItem = `-- name: GetSyncQueueItem :one SELECT id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at FROM sync_queue WHERE id = $1 ` func (q *Queries) GetSyncQueueItem(ctx context.Context, id pgtype.UUID) (SyncQueue, error) { row := q.db.QueryRow(ctx, GetSyncQueueItem, id) var i SyncQueue err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ) return i, err } const GetSyncQueueStats = `-- name: GetSyncQueueStats :one SELECT COUNT(*) FILTER (WHERE status = 'pending') as pending_count, COUNT(*) FILTER (WHERE status = 'processing') as processing_count, COUNT(*) FILTER (WHERE status = 'failed') as failed_count, COUNT(*) FILTER (WHERE status = 'completed') as completed_count, COUNT(*) as total_count FROM sync_queue WHERE device_id = $1 ` type GetSyncQueueStatsRow struct { PendingCount int64 `db:"pending_count" json:"pending_count"` ProcessingCount int64 `db:"processing_count" json:"processing_count"` FailedCount int64 `db:"failed_count" json:"failed_count"` CompletedCount int64 `db:"completed_count" json:"completed_count"` TotalCount int64 `db:"total_count" json:"total_count"` } func (q *Queries) GetSyncQueueStats(ctx context.Context, deviceID pgtype.UUID) (GetSyncQueueStatsRow, error) { row := q.db.QueryRow(ctx, GetSyncQueueStats, deviceID) var i GetSyncQueueStatsRow err := row.Scan( &i.PendingCount, &i.ProcessingCount, &i.FailedCount, &i.CompletedCount, &i.TotalCount, ) return i, err } const GetSystemCollectionsForDashboard = `-- name: GetSystemCollectionsForDashboard :many SELECT id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at FROM collections WHERE user_id = $1 AND is_system_collection = true AND show_on_dashboard = true ORDER BY priority ASC ` // Dashboard collections queries func (q *Queries) GetSystemCollectionsForDashboard(ctx context.Context, userID pgtype.UUID) ([]Collections, error) { rows, err := q.db.Query(ctx, GetSystemCollectionsForDashboard, userID) if err != nil { return nil, err } defer rows.Close() items := []Collections{} for rows.Next() { var i Collections if err := rows.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetSystemConfig = `-- name: GetSystemConfig :one SELECT key, value, updated_at, updated_by FROM system_config WHERE key = $1 ` // SYSTEM CONFIG QUERIES // Get system config func (q *Queries) GetSystemConfig(ctx context.Context, key string) (SystemConfig, error) { row := q.db.QueryRow(ctx, GetSystemConfig, key) var i SystemConfig err := row.Scan( &i.Key, &i.Value, &i.UpdatedAt, &i.UpdatedBy, ) return i, err } const GetSystemSetting = `-- name: GetSystemSetting :one SELECT setting_value FROM system_settings WHERE setting_key = $1 ` // System Settings queries func (q *Queries) GetSystemSetting(ctx context.Context, settingKey string) (string, error) { row := q.db.QueryRow(ctx, GetSystemSetting, settingKey) var setting_value string err := row.Scan(&setting_value) return setting_value, err } const GetSystemSettingFull = `-- name: GetSystemSettingFull :one SELECT id, setting_key, setting_value, description, updated_at, setting_type, min_value, max_value, requires_restart, category FROM system_settings WHERE setting_key = $1 ` func (q *Queries) GetSystemSettingFull(ctx context.Context, settingKey string) (SystemSettings, error) { row := q.db.QueryRow(ctx, GetSystemSettingFull, settingKey) var i SystemSettings err := row.Scan( &i.ID, &i.SettingKey, &i.SettingValue, &i.Description, &i.UpdatedAt, &i.SettingType, &i.MinValue, &i.MaxValue, &i.RequiresRestart, &i.Category, ) return i, err } const GetSystemTimezone = `-- name: GetSystemTimezone :one SELECT setting_value FROM system_settings WHERE setting_key = 'default_timezone' ` func (q *Queries) GetSystemTimezone(ctx context.Context) (string, error) { row := q.db.QueryRow(ctx, GetSystemTimezone) var setting_value string err := row.Scan(&setting_value) return setting_value, err } const GetTombstonedAnnotationsForBook = `-- name: GetTombstonedAnnotationsForBook :many SELECT mh.id, mh.dedup_key, 'highlight' as annotation_type, mh.device_sync_data, mh.deleted_at, mh.start_position, mh.end_position, mh.epubcfi_start, mh.epubcfi_end FROM media_highlights mh WHERE mh.media_item_id = $1 AND mh.user_id = $2 AND mh.deleted = TRUE AND mh.deleted_at > $3 UNION ALL SELECT mn.id, mn.dedup_key, 'note' as annotation_type, mn.device_sync_data, mn.deleted_at, mn.position as start_position, NULL as end_position, mn.epubcfi_location as epubcfi_start, NULL as epubcfi_end FROM media_notes mn WHERE mn.media_item_id = $1 AND mn.user_id = $2 AND mn.deleted = TRUE AND mn.deleted_at > $3 UNION ALL SELECT mb.id, mb.dedup_key, 'bookmark' as annotation_type, mb.device_sync_data, mb.deleted_at, mb.position as start_position, NULL as end_position, mb.cfi_position as epubcfi_start, NULL as epubcfi_end FROM media_bookmarks mb WHERE mb.media_item_id = $1 AND mb.user_id = $2 AND mb.deleted = TRUE AND mb.deleted_at > $3 ORDER BY deleted_at DESC ` type GetTombstonedAnnotationsForBookParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` DeletedAt pgtype.Timestamptz `db:"deleted_at" json:"deleted_at"` } type GetTombstonedAnnotationsForBookRow struct { ID pgtype.UUID `db:"id" json:"id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` AnnotationType string `db:"annotation_type" json:"annotation_type"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` DeletedAt pgtype.Timestamptz `db:"deleted_at" json:"deleted_at"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` EpubcfiStart pgtype.Text `db:"epubcfi_start" json:"epubcfi_start"` EpubcfiEnd pgtype.Text `db:"epubcfi_end" json:"epubcfi_end"` } func (q *Queries) GetTombstonedAnnotationsForBook(ctx context.Context, arg GetTombstonedAnnotationsForBookParams) ([]GetTombstonedAnnotationsForBookRow, error) { rows, err := q.db.Query(ctx, GetTombstonedAnnotationsForBook, arg.MediaItemID, arg.UserID, arg.DeletedAt) if err != nil { return nil, err } defer rows.Close() items := []GetTombstonedAnnotationsForBookRow{} for rows.Next() { var i GetTombstonedAnnotationsForBookRow if err := rows.Scan( &i.ID, &i.DedupKey, &i.AnnotationType, &i.DeviceSyncData, &i.DeletedAt, &i.StartPosition, &i.EndPosition, &i.EpubcfiStart, &i.EpubcfiEnd, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUniversalProgress = `-- name: GetUniversalProgress :one SELECT rp.id, rp.media_item_id, rp.user_id, rp.current_page, rp.total_pages, rp.last_read_at, rp.percentage, rp.character_offset, rp.epubcfi, rp.context_text, rp.chapter, rp.chapter_progress, rp.viewport_x, rp.viewport_y, rp.zoom_level, rp.scroll_position_x, rp.scroll_position_y, rp.panel_number, rp.reading_mode, rp.last_sync_device, rp.last_sync_source, rp.last_sync_timestamp, rp.conflict_detected, rp.conflict_resolved, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count FROM reading_progress rp JOIN media_items mi ON rp.media_item_id = mi.id WHERE rp.media_item_id = $1 AND rp.user_id = $2 ` type GetUniversalProgressParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } type GetUniversalProgressRow 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"` Percentage pgtype.Float8 `db:"percentage" json:"percentage"` CharacterOffset pgtype.Int8 `db:"character_offset" json:"character_offset"` Epubcfi pgtype.Text `db:"epubcfi" json:"epubcfi"` ContextText pgtype.Text `db:"context_text" json:"context_text"` Chapter pgtype.Int4 `db:"chapter" json:"chapter"` ChapterProgress pgtype.Float8 `db:"chapter_progress" json:"chapter_progress"` ViewportX pgtype.Float8 `db:"viewport_x" json:"viewport_x"` ViewportY pgtype.Float8 `db:"viewport_y" json:"viewport_y"` ZoomLevel pgtype.Float8 `db:"zoom_level" json:"zoom_level"` ScrollPositionX pgtype.Float8 `db:"scroll_position_x" json:"scroll_position_x"` ScrollPositionY pgtype.Float8 `db:"scroll_position_y" json:"scroll_position_y"` PanelNumber pgtype.Int4 `db:"panel_number" json:"panel_number"` ReadingMode pgtype.Text `db:"reading_mode" json:"reading_mode"` LastSyncDevice pgtype.Text `db:"last_sync_device" json:"last_sync_device"` LastSyncSource pgtype.Text `db:"last_sync_source" json:"last_sync_source"` LastSyncTimestamp pgtype.Timestamptz `db:"last_sync_timestamp" json:"last_sync_timestamp"` ConflictDetected pgtype.Bool `db:"conflict_detected" json:"conflict_detected"` ConflictResolved pgtype.Bool `db:"conflict_resolved" json:"conflict_resolved"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` } // Get universal progress for a book func (q *Queries) GetUniversalProgress(ctx context.Context, arg GetUniversalProgressParams) (GetUniversalProgressRow, error) { row := q.db.QueryRow(ctx, GetUniversalProgress, arg.MediaItemID, arg.UserID) var i GetUniversalProgressRow err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.CurrentPage, &i.TotalPages, &i.LastReadAt, &i.Percentage, &i.CharacterOffset, &i.Epubcfi, &i.ContextText, &i.Chapter, &i.ChapterProgress, &i.ViewportX, &i.ViewportY, &i.ZoomLevel, &i.ScrollPositionX, &i.ScrollPositionY, &i.PanelNumber, &i.ReadingMode, &i.LastSyncDevice, &i.LastSyncSource, &i.LastSyncTimestamp, &i.ConflictDetected, &i.ConflictResolved, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, ) return i, err } const GetUnlinkedBookByContentId = `-- name: GetUnlinkedBookByContentId :one SELECT id, device_id, content_id, file_path, title, author, confidence_score, resolved, media_item_id, resolved_at, resolution_method, last_seen_at, created_at FROM unlinked_books WHERE device_id = $1 AND content_id = $2 ` type GetUnlinkedBookByContentIdParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ContentID string `db:"content_id" json:"content_id"` } // Get unlinked book by ContentId func (q *Queries) GetUnlinkedBookByContentId(ctx context.Context, arg GetUnlinkedBookByContentIdParams) (UnlinkedBooks, error) { row := q.db.QueryRow(ctx, GetUnlinkedBookByContentId, arg.DeviceID, arg.ContentID) var i UnlinkedBooks err := row.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, ) return i, err } const GetUnlinkedBookByID = `-- name: GetUnlinkedBookByID :one SELECT id, device_id, content_id, file_path, title, author, confidence_score, resolved, media_item_id, resolved_at, resolution_method, last_seen_at, created_at FROM unlinked_books WHERE id = $1 ` // Get unlinked book by ID func (q *Queries) GetUnlinkedBookByID(ctx context.Context, id pgtype.UUID) (UnlinkedBooks, error) { row := q.db.QueryRow(ctx, GetUnlinkedBookByID, id) var i UnlinkedBooks err := row.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, ) return i, err } const GetUnlinkedBooksByDevice = `-- name: GetUnlinkedBooksByDevice :many SELECT ub.id, ub.device_id, ub.content_id, ub.file_path, ub.title, ub.author, ub.confidence_score, ub.resolved, ub.media_item_id, ub.resolved_at, ub.resolution_method, ub.last_seen_at, ub.created_at, d.device_name, d.device_type FROM unlinked_books ub JOIN devices d ON ub.device_id = d.id WHERE ub.device_id = $1 AND ub.resolved = false ORDER BY ub.last_seen_at DESC ` type GetUnlinkedBooksByDeviceRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ContentID string `db:"content_id" json:"content_id"` FilePath pgtype.Text `db:"file_path" json:"file_path"` Title pgtype.Text `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` Resolved pgtype.Bool `db:"resolved" json:"resolved"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` ResolutionMethod pgtype.Text `db:"resolution_method" json:"resolution_method"` LastSeenAt pgtype.Timestamptz `db:"last_seen_at" json:"last_seen_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` } // Get unlinked books for a device func (q *Queries) GetUnlinkedBooksByDevice(ctx context.Context, deviceID pgtype.UUID) ([]GetUnlinkedBooksByDeviceRow, error) { rows, err := q.db.Query(ctx, GetUnlinkedBooksByDevice, deviceID) if err != nil { return nil, err } defer rows.Close() items := []GetUnlinkedBooksByDeviceRow{} for rows.Next() { var i GetUnlinkedBooksByDeviceRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, &i.DeviceName, &i.DeviceType, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUser = `-- name: GetUser :one SELECT u.id, u.email, u.username, u.theme, u.first_name, u.last_name, u.role, u.max_devices, u.created_at, u.updated_at, u.timezone, (SELECT COUNT(*) FROM devices WHERE user_id = u.id) as device_count FROM users u WHERE u.id = $1 ` type GetUserRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` Timezone pgtype.Text `db:"timezone" json:"timezone"` DeviceCount int64 `db:"device_count" json:"device_count"` } func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error) { row := q.db.QueryRow(ctx, GetUser, id) var i GetUserRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.MaxDevices, &i.CreatedAt, &i.UpdatedAt, &i.Timezone, &i.DeviceCount, ) return i, err } const GetUserByEmail = `-- name: GetUserByEmail :one SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1 ` type GetUserByEmailRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error) { row := q.db.QueryRow(ctx, GetUserByEmail, email) var i GetUserByEmailRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1 OR username = $1 ` type GetUserByEmailOrUsernameRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) GetUserByEmailOrUsername(ctx context.Context, email string) (GetUserByEmailOrUsernameRow, error) { row := q.db.QueryRow(ctx, GetUserByEmailOrUsername, email) var i GetUserByEmailOrUsernameRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetUserByUsername = `-- name: GetUserByUsername :one SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE username = $1 ` type GetUserByUsernameRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) GetUserByUsername(ctx context.Context, username string) (GetUserByUsernameRow, error) { row := q.db.QueryRow(ctx, GetUserByUsername, username) var i GetUserByUsernameRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetUserCollectionsForDashboard = `-- name: GetUserCollectionsForDashboard :many SELECT c.id, c.user_id, c.name, c.description, c.color, c.icon, c.auto_assign_rules, c.view_settings, c.show_on_dashboard, c.query_type, c.priority, c.is_system_collection, c.created_at FROM collections c WHERE c.user_id = $1 AND c.show_on_dashboard = true AND c.is_system_collection = false ORDER BY priority ASC ` func (q *Queries) GetUserCollectionsForDashboard(ctx context.Context, userID pgtype.UUID) ([]Collections, error) { rows, err := q.db.Query(ctx, GetUserCollectionsForDashboard, userID) if err != nil { return nil, err } defer rows.Close() items := []Collections{} for rows.Next() { var i Collections if err := rows.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUserDeviceUsage = `-- name: GetUserDeviceUsage :many SELECT d.id, d.device_name, d.device_type, COUNT(*) as sync_count, MAX(rh.created_at) as last_sync, SUM(rh.time_spent_seconds) as total_time_seconds FROM devices d JOIN reading_history rh ON rh.device_id = d.id WHERE d.user_id = $1 GROUP BY d.id, d.device_name, d.device_type ORDER BY sync_count DESC ` type GetUserDeviceUsageRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` SyncCount int64 `db:"sync_count" json:"sync_count"` LastSync interface{} `db:"last_sync" json:"last_sync"` TotalTimeSeconds int64 `db:"total_time_seconds" json:"total_time_seconds"` } // Get user device usage statistics func (q *Queries) GetUserDeviceUsage(ctx context.Context, userID pgtype.UUID) ([]GetUserDeviceUsageRow, error) { rows, err := q.db.Query(ctx, GetUserDeviceUsage, userID) if err != nil { return nil, err } defer rows.Close() items := []GetUserDeviceUsageRow{} for rows.Next() { var i GetUserDeviceUsageRow if err := rows.Scan( &i.ID, &i.DeviceName, &i.DeviceType, &i.SyncCount, &i.LastSync, &i.TotalTimeSeconds, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUserForLogin = `-- name: GetUserForLogin :one SELECT id, email, username, password_hash, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1 OR username = $1 ` type GetUserForLoginRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` PasswordHash string `db:"password_hash" json:"password_hash"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` } func (q *Queries) GetUserForLogin(ctx context.Context, email string) (GetUserForLoginRow, error) { row := q.db.QueryRow(ctx, GetUserForLogin, email) var i GetUserForLoginRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.PasswordHash, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const GetUserMediaItemsForSync = `-- name: GetUserMediaItemsForSync :many SELECT mi.id, mi.title, mi.author, mi.file_path, mi.mime_type, mi.page_count, mi.format_group, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.file_size, mi.file_sha256 FROM media_items mi JOIN library_visibility lv ON mi.library_id = lv.library_id WHERE lv.user_id = $1 AND lv.is_visible = true ORDER BY mi.title ASC LIMIT 1000 ` type GetUserMediaItemsForSyncRow struct { ID pgtype.UUID `db:"id" json:"id"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` MimeType pgtype.Text `db:"mime_type" json:"mime_type"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` FormatGroup string `db:"format_group" json:"format_group"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` FileSize pgtype.Int8 `db:"file_size" json:"file_size"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` } func (q *Queries) GetUserMediaItemsForSync(ctx context.Context, userID pgtype.UUID) ([]GetUserMediaItemsForSyncRow, error) { rows, err := q.db.Query(ctx, GetUserMediaItemsForSync, userID) if err != nil { return nil, err } defer rows.Close() items := []GetUserMediaItemsForSyncRow{} for rows.Next() { var i GetUserMediaItemsForSyncRow if err := rows.Scan( &i.ID, &i.Title, &i.Author, &i.FilePath, &i.MimeType, &i.PageCount, &i.FormatGroup, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.FileSize, &i.FileSha256, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUserPasswordHash = `-- name: GetUserPasswordHash :one SELECT password_hash FROM users WHERE id = $1 ` func (q *Queries) GetUserPasswordHash(ctx context.Context, id pgtype.UUID) (string, error) { row := q.db.QueryRow(ctx, GetUserPasswordHash, id) var password_hash string err := row.Scan(&password_hash) return password_hash, err } const GetUserProgressForBooks = `-- name: GetUserProgressForBooks :many SELECT rp.media_item_id, rp.user_id, rp.percentage, rp.character_offset, rp.epubcfi, rp.chapter, rp.chapter_progress, rp.current_page, rp.total_pages, rp.last_read_at, rp.last_sync_device, rp.last_sync_source, mi.title, mi.author, mi.file_path FROM reading_progress rp JOIN media_items mi ON rp.media_item_id = mi.id WHERE rp.user_id = $1 AND rp.media_item_id = ANY($2::uuid[]) ORDER BY rp.last_read_at DESC ` type GetUserProgressForBooksParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Column2 []pgtype.UUID `db:"column_2" json:"column_2"` } type GetUserProgressForBooksRow struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Percentage pgtype.Float8 `db:"percentage" json:"percentage"` CharacterOffset pgtype.Int8 `db:"character_offset" json:"character_offset"` Epubcfi pgtype.Text `db:"epubcfi" json:"epubcfi"` Chapter pgtype.Int4 `db:"chapter" json:"chapter"` ChapterProgress pgtype.Float8 `db:"chapter_progress" json:"chapter_progress"` 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"` LastSyncDevice pgtype.Text `db:"last_sync_device" json:"last_sync_device"` LastSyncSource pgtype.Text `db:"last_sync_source" json:"last_sync_source"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FilePath string `db:"file_path" json:"file_path"` } func (q *Queries) GetUserProgressForBooks(ctx context.Context, arg GetUserProgressForBooksParams) ([]GetUserProgressForBooksRow, error) { rows, err := q.db.Query(ctx, GetUserProgressForBooks, arg.UserID, arg.Column2) if err != nil { return nil, err } defer rows.Close() items := []GetUserProgressForBooksRow{} for rows.Next() { var i GetUserProgressForBooksRow if err := rows.Scan( &i.MediaItemID, &i.UserID, &i.Percentage, &i.CharacterOffset, &i.Epubcfi, &i.Chapter, &i.ChapterProgress, &i.CurrentPage, &i.TotalPages, &i.LastReadAt, &i.LastSyncDevice, &i.LastSyncSource, &i.Title, &i.Author, &i.FilePath, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUserReadingHistory = `-- name: GetUserReadingHistory :many SELECT rh.id, rh.user_id, rh.media_item_id, rh.device_id, rh.progress_percentage, rh.reading_session_start, rh.reading_session_end, rh.pages_read, rh.time_spent_seconds, rh.device_metadata, rh.created_at, mi.title, mi.author, d.device_name, d.device_type FROM reading_history rh JOIN media_items mi ON rh.media_item_id = mi.id LEFT JOIN devices d ON rh.device_id = d.id WHERE rh.user_id = $1 AND rh.created_at >= $2 AND rh.created_at <= $3 ORDER BY rh.created_at DESC ` type GetUserReadingHistoryParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` CreatedAt_2 pgtype.Timestamptz `db:"created_at_2" json:"created_at_2"` } type GetUserReadingHistoryRow struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ProgressPercentage pgtype.Float8 `db:"progress_percentage" json:"progress_percentage"` ReadingSessionStart pgtype.Timestamptz `db:"reading_session_start" json:"reading_session_start"` ReadingSessionEnd pgtype.Timestamptz `db:"reading_session_end" json:"reading_session_end"` PagesRead pgtype.Int4 `db:"pages_read" json:"pages_read"` TimeSpentSeconds pgtype.Int4 `db:"time_spent_seconds" json:"time_spent_seconds"` DeviceMetadata []byte `db:"device_metadata" json:"device_metadata"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` DeviceName pgtype.Text `db:"device_name" json:"device_name"` DeviceType pgtype.Text `db:"device_type" json:"device_type"` } // Analytics queries // Get user reading history for analytics func (q *Queries) GetUserReadingHistory(ctx context.Context, arg GetUserReadingHistoryParams) ([]GetUserReadingHistoryRow, error) { rows, err := q.db.Query(ctx, GetUserReadingHistory, arg.UserID, arg.CreatedAt, arg.CreatedAt_2) if err != nil { return nil, err } defer rows.Close() items := []GetUserReadingHistoryRow{} for rows.Next() { var i GetUserReadingHistoryRow if err := rows.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.DeviceID, &i.ProgressPercentage, &i.ReadingSessionStart, &i.ReadingSessionEnd, &i.PagesRead, &i.TimeSpentSeconds, &i.DeviceMetadata, &i.CreatedAt, &i.Title, &i.Author, &i.DeviceName, &i.DeviceType, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetUserVisibleLibraries = `-- name: GetUserVisibleLibraries :many SELECT l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at, 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 ASC ` type GetUserVisibleLibrariesRow 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"` TypeName string `db:"type_name" json:"type_name"` TypeDescription pgtype.Text `db:"type_description" json:"type_description"` IsVisible bool `db:"is_visible" json:"is_visible"` } func (q *Queries) GetUserVisibleLibraries(ctx context.Context, userID pgtype.UUID) ([]GetUserVisibleLibrariesRow, error) { rows, err := q.db.Query(ctx, GetUserVisibleLibraries, userID) if err != nil { return nil, err } defer rows.Close() items := []GetUserVisibleLibrariesRow{} for rows.Next() { var i GetUserVisibleLibrariesRow if err := rows.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, &i.TypeName, &i.TypeDescription, &i.IsVisible, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const GetVisibleLibraryMediaCounts = `-- name: GetVisibleLibraryMediaCounts :many SELECT l.id, COUNT(mi.id) as media_count FROM libraries l LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1 LEFT JOIN media_items mi ON mi.library_id = l.id WHERE COALESCE(lv.is_visible, true) = true GROUP BY l.id ` type GetVisibleLibraryMediaCountsRow struct { ID pgtype.UUID `db:"id" json:"id"` MediaCount int64 `db:"media_count" json:"media_count"` } func (q *Queries) GetVisibleLibraryMediaCounts(ctx context.Context, userID pgtype.UUID) ([]GetVisibleLibraryMediaCountsRow, error) { rows, err := q.db.Query(ctx, GetVisibleLibraryMediaCounts, userID) if err != nil { return nil, err } defer rows.Close() items := []GetVisibleLibraryMediaCountsRow{} for rows.Next() { var i GetVisibleLibraryMediaCountsRow if err := rows.Scan(&i.ID, &i.MediaCount); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const HasRecentConflictResolution = `-- name: HasRecentConflictResolution :one SELECT EXISTS( SELECT 1 FROM sync_conflicts WHERE media_item_id = $1 AND user_id = $2 AND resolution_status != 'unresolved' AND resolved_at > NOW() - INTERVAL '10 minutes' ) ` type HasRecentConflictResolutionParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) HasRecentConflictResolution(ctx context.Context, arg HasRecentConflictResolutionParams) (bool, error) { row := q.db.QueryRow(ctx, HasRecentConflictResolution, arg.MediaItemID, arg.UserID) var exists bool err := row.Scan(&exists) return exists, err } const IncrementSyncQueueAttempts = `-- name: IncrementSyncQueueAttempts :one UPDATE sync_queue SET attempts = attempts + 1, status = CASE WHEN attempts + 1 >= max_attempts THEN 'failed' ELSE 'pending' END, error_message = $2 WHERE id = $1 RETURNING id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at ` type IncrementSyncQueueAttemptsParams struct { ID pgtype.UUID `db:"id" json:"id"` ErrorMessage pgtype.Text `db:"error_message" json:"error_message"` } func (q *Queries) IncrementSyncQueueAttempts(ctx context.Context, arg IncrementSyncQueueAttemptsParams) (SyncQueue, error) { row := q.db.QueryRow(ctx, IncrementSyncQueueAttempts, arg.ID, arg.ErrorMessage) var i SyncQueue err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ) return i, err } const IsBookInCollection = `-- name: IsBookInCollection :one SELECT EXISTS( SELECT 1 FROM collection_items WHERE collection_id = $1 AND media_item_id = $2 ) as in_collection ` type IsBookInCollectionParams struct { CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } // Check if book is in collection func (q *Queries) IsBookInCollection(ctx context.Context, arg IsBookInCollectionParams) (bool, error) { row := q.db.QueryRow(ctx, IsBookInCollection, arg.CollectionID, arg.MediaItemID) var in_collection bool err := row.Scan(&in_collection) return in_collection, err } const IsBookOnKoboShelf = `-- name: IsBookOnKoboShelf :one SELECT EXISTS( SELECT 1 FROM kobo_shelves WHERE device_id = $1 AND media_item_id = $2 ) as on_shelf ` type IsBookOnKoboShelfParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) IsBookOnKoboShelf(ctx context.Context, arg IsBookOnKoboShelfParams) (bool, error) { row := q.db.QueryRow(ctx, IsBookOnKoboShelf, arg.DeviceID, arg.MediaItemID) var on_shelf bool err := row.Scan(&on_shelf) return on_shelf, err } const LinkUnlinkedBook = `-- name: LinkUnlinkedBook :one UPDATE unlinked_books SET media_item_id = $2, confidence_score = $3, resolved = true, resolved_at = NOW(), resolution_method = 'manual_link' WHERE id = $1 RETURNING id, device_id, content_id, file_path, title, author, confidence_score, resolved, media_item_id, resolved_at, resolution_method, last_seen_at, created_at ` type LinkUnlinkedBookParams struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` } // Link unlinked book to media item func (q *Queries) LinkUnlinkedBook(ctx context.Context, arg LinkUnlinkedBookParams) (UnlinkedBooks, error) { row := q.db.QueryRow(ctx, LinkUnlinkedBook, arg.ID, arg.MediaItemID, arg.ConfidenceScore) var i UnlinkedBooks err := row.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, ) return i, err } const ListAllConflictsByUserAndStatus = `-- name: ListAllConflictsByUserAndStatus :many SELECT sc.id, sc.media_item_id, sc.user_id, sc.conflict_type, sc.conflict_data, sc.resolution_status, sc.resolution_data, sc.resolved_by, sc.resolved_at, sc.created_at, mi.title, mi.author FROM sync_conflicts sc JOIN media_items mi ON sc.media_item_id = mi.id WHERE sc.user_id = $1 AND sc.resolution_status = $2 ORDER BY sc.created_at DESC ` type ListAllConflictsByUserAndStatusParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` ResolutionStatus pgtype.Text `db:"resolution_status" json:"resolution_status"` } type ListAllConflictsByUserAndStatusRow 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"` ConflictType string `db:"conflict_type" json:"conflict_type"` ConflictData []byte `db:"conflict_data" json:"conflict_data"` ResolutionStatus pgtype.Text `db:"resolution_status" json:"resolution_status"` ResolutionData []byte `db:"resolution_data" json:"resolution_data"` ResolvedBy pgtype.UUID `db:"resolved_by" json:"resolved_by"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } func (q *Queries) ListAllConflictsByUserAndStatus(ctx context.Context, arg ListAllConflictsByUserAndStatusParams) ([]ListAllConflictsByUserAndStatusRow, error) { rows, err := q.db.Query(ctx, ListAllConflictsByUserAndStatus, arg.UserID, arg.ResolutionStatus) if err != nil { return nil, err } defer rows.Close() items := []ListAllConflictsByUserAndStatusRow{} for rows.Next() { var i ListAllConflictsByUserAndStatusRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListAllSyncQueueItems = `-- name: ListAllSyncQueueItems :many SELECT sq.id, sq.device_id, sq.media_item_id, sq.sync_type, sq.sync_data, sq.priority, sq.attempts, sq.max_attempts, sq.status, sq.error_message, sq.created_at, sq.processed_at, d.device_name, d.device_type, u.email as user_email, mi.title as media_title FROM sync_queue sq JOIN devices d ON sq.device_id = d.id JOIN users u ON d.user_id = u.id LEFT JOIN media_items mi ON sq.media_item_id = mi.id ORDER BY sq.priority ASC, sq.created_at DESC LIMIT $1 OFFSET $2 ` type ListAllSyncQueueItemsParams struct { Limit int32 `db:"limit" json:"limit"` Offset int32 `db:"offset" json:"offset"` } type ListAllSyncQueueItemsRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` SyncType string `db:"sync_type" json:"sync_type"` SyncData []byte `db:"sync_data" json:"sync_data"` Priority pgtype.Int4 `db:"priority" json:"priority"` Attempts pgtype.Int4 `db:"attempts" json:"attempts"` MaxAttempts pgtype.Int4 `db:"max_attempts" json:"max_attempts"` Status pgtype.Text `db:"status" json:"status"` ErrorMessage pgtype.Text `db:"error_message" json:"error_message"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ProcessedAt pgtype.Timestamptz `db:"processed_at" json:"processed_at"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` UserEmail string `db:"user_email" json:"user_email"` MediaTitle pgtype.Text `db:"media_title" json:"media_title"` } func (q *Queries) ListAllSyncQueueItems(ctx context.Context, arg ListAllSyncQueueItemsParams) ([]ListAllSyncQueueItemsRow, error) { rows, err := q.db.Query(ctx, ListAllSyncQueueItems, arg.Limit, arg.Offset) if err != nil { return nil, err } defer rows.Close() items := []ListAllSyncQueueItemsRow{} for rows.Next() { var i ListAllSyncQueueItemsRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, &i.DeviceName, &i.DeviceType, &i.UserEmail, &i.MediaTitle, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListConflictsByUser = `-- name: ListConflictsByUser :many SELECT sc.id, sc.media_item_id, sc.user_id, sc.conflict_type, sc.conflict_data, sc.resolution_status, sc.resolution_data, sc.resolved_by, sc.resolved_at, sc.created_at, mi.title, mi.author FROM sync_conflicts sc JOIN media_items mi ON sc.media_item_id = mi.id WHERE sc.user_id = $1 ORDER BY sc.created_at DESC ` type ListConflictsByUserRow 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"` ConflictType string `db:"conflict_type" json:"conflict_type"` ConflictData []byte `db:"conflict_data" json:"conflict_data"` ResolutionStatus pgtype.Text `db:"resolution_status" json:"resolution_status"` ResolutionData []byte `db:"resolution_data" json:"resolution_data"` ResolvedBy pgtype.UUID `db:"resolved_by" json:"resolved_by"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } func (q *Queries) ListConflictsByUser(ctx context.Context, userID pgtype.UUID) ([]ListConflictsByUserRow, error) { rows, err := q.db.Query(ctx, ListConflictsByUser, userID) if err != nil { return nil, err } defer rows.Close() items := []ListConflictsByUserRow{} for rows.Next() { var i ListConflictsByUserRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListDeletedAnnotationsForBook = `-- name: ListDeletedAnnotationsForBook :many SELECT mh.id, mh.dedup_key, 'highlight' as annotation_type, mh.selection_text as display_text, mh.note_text as secondary_text, mh.color, mh.deleted_at, mh.created_at FROM media_highlights mh WHERE mh.media_item_id = $1 AND mh.user_id = $2 AND mh.deleted = TRUE UNION ALL SELECT mn.id, mn.dedup_key, 'note' as annotation_type, mn.content as display_text, NULL::text as secondary_text, NULL::text as color, mn.deleted_at, mn.created_at FROM media_notes mn WHERE mn.media_item_id = $1 AND mn.user_id = $2 AND mn.deleted = TRUE UNION ALL SELECT mb.id, mb.dedup_key, 'bookmark' as annotation_type, mb.title as display_text, mb.notes as secondary_text, NULL::text as color, mb.deleted_at, mb.created_at FROM media_bookmarks mb WHERE mb.media_item_id = $1 AND mb.user_id = $2 AND mb.deleted = TRUE ORDER BY deleted_at DESC ` type ListDeletedAnnotationsForBookParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } type ListDeletedAnnotationsForBookRow struct { ID pgtype.UUID `db:"id" json:"id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` AnnotationType string `db:"annotation_type" json:"annotation_type"` DisplayText string `db:"display_text" json:"display_text"` SecondaryText pgtype.Text `db:"secondary_text" json:"secondary_text"` Color pgtype.Text `db:"color" json:"color"` DeletedAt pgtype.Timestamptz `db:"deleted_at" json:"deleted_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` } // ============================================ // ANNOTATION HISTORY (deleted-annotation archive) // ============================================ // Lists every currently-tombstoned annotation for a book regardless of the // tombstone TTL: this backs the book page's "recently deleted" history where // users can restore or permanently remove entries. Rows whose tombstones have // been purged by the daily maintenance sweep no longer exist at all. func (q *Queries) ListDeletedAnnotationsForBook(ctx context.Context, arg ListDeletedAnnotationsForBookParams) ([]ListDeletedAnnotationsForBookRow, error) { rows, err := q.db.Query(ctx, ListDeletedAnnotationsForBook, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []ListDeletedAnnotationsForBookRow{} for rows.Next() { var i ListDeletedAnnotationsForBookRow if err := rows.Scan( &i.ID, &i.DedupKey, &i.AnnotationType, &i.DisplayText, &i.SecondaryText, &i.Color, &i.DeletedAt, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListDevicesByType = `-- name: ListDevicesByType :many SELECT id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at FROM devices WHERE device_type = $1 ORDER BY created_at DESC ` func (q *Queries) ListDevicesByType(ctx context.Context, deviceType string) ([]Devices, error) { rows, err := q.db.Query(ctx, ListDevicesByType, deviceType) if err != nil { return nil, err } defer rows.Close() items := []Devices{} for rows.Next() { var i Devices if err := rows.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListDevicesByUser = `-- name: ListDevicesByUser :many SELECT id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at FROM devices WHERE user_id = $1 ORDER BY created_at DESC ` func (q *Queries) ListDevicesByUser(ctx context.Context, userID pgtype.UUID) ([]Devices, error) { rows, err := q.db.Query(ctx, ListDevicesByUser, userID) if err != nil { return nil, err } defer rows.Close() items := []Devices{} for rows.Next() { var i Devices if err := rows.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListLibraries = `-- name: ListLibraries :many SELECT l.id, l.name, l.description, l.library_type_id, l.created_by_admin_id, l.created_at, l.updated_at, 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 ` type ListLibrariesRow 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"` TypeName string `db:"type_name" json:"type_name"` TypeDescription pgtype.Text `db:"type_description" json:"type_description"` } func (q *Queries) ListLibraries(ctx context.Context) ([]ListLibrariesRow, error) { rows, err := q.db.Query(ctx, ListLibraries) if err != nil { return nil, err } defer rows.Close() items := []ListLibrariesRow{} for rows.Next() { var i ListLibrariesRow if err := rows.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, &i.TypeName, &i.TypeDescription, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListMediaItems = `-- name: ListMediaItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, 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 ` type ListMediaItemsParams struct { Limit int32 `db:"limit" json:"limit"` Offset int32 `db:"offset" json:"offset"` } type ListMediaItemsRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LibraryName string `db:"library_name" json:"library_name"` LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"` } func (q *Queries) ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error) { rows, err := q.db.Query(ctx, ListMediaItems, arg.Limit, arg.Offset) if err != nil { return nil, err } defer rows.Close() items := []ListMediaItemsRow{} for rows.Next() { var i ListMediaItemsRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LibraryName, &i.LibraryTypeName_2, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListMediaItemsByLibrary = `-- name: ListMediaItemsByLibrary :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, 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 ` type ListMediaItemsByLibraryRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LibraryName string `db:"library_name" json:"library_name"` LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"` } func (q *Queries) ListMediaItemsByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryRow, error) { rows, err := q.db.Query(ctx, ListMediaItemsByLibrary, libraryID) if err != nil { return nil, err } defer rows.Close() items := []ListMediaItemsByLibraryRow{} for rows.Next() { var i ListMediaItemsByLibraryRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LibraryName, &i.LibraryTypeName_2, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListMediaItemsBySHA256AndLibrary = `-- name: ListMediaItemsBySHA256AndLibrary :many SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_sha256 = $1 AND library_id = $2 ORDER BY file_path ` type ListMediaItemsBySHA256AndLibraryParams struct { FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } // List all media items sharing a SHA-256 hash within a library (hash conflict group) func (q *Queries) ListMediaItemsBySHA256AndLibrary(ctx context.Context, arg ListMediaItemsBySHA256AndLibraryParams) ([]MediaItems, error) { rows, err := q.db.Query(ctx, ListMediaItemsBySHA256AndLibrary, arg.FileSha256, arg.LibraryID) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListMediaItemsMissingHash = `-- name: ListMediaItemsMissingHash :many SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items WHERE file_sha256 IS NULL ORDER BY created_at ` // List media items that have no stored SHA-256 (imported before hashing existed) func (q *Queries) ListMediaItemsMissingHash(ctx context.Context) ([]MediaItems, error) { rows, err := q.db.Query(ctx, ListMediaItemsMissingHash) if err != nil { return nil, err } defer rows.Close() items := []MediaItems{} for rows.Next() { var i MediaItems if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListMediaItemsSorted = `-- name: ListMediaItemsSorted :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, 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 CASE WHEN $2 = 'title ASC' THEN mi.title ELSE '' END ASC, CASE WHEN $2 = 'title DESC' THEN mi.title ELSE '' END DESC, CASE WHEN $2 = 'author ASC' THEN COALESCE(mi.author, '') ELSE '' END ASC, CASE WHEN $2 = 'author DESC' THEN COALESCE(mi.author, '') ELSE '' END DESC, CASE WHEN $2 = 'created_at ASC' THEN mi.created_at ELSE '1970-01-01'::timestamp END ASC, CASE WHEN $2 = 'created_at DESC' THEN mi.created_at ELSE '1970-01-01'::timestamp END DESC, CASE WHEN $2 = 'series ASC' THEN COALESCE(mi.series, '') ELSE '' END ASC, CASE WHEN $2 = 'series DESC' THEN COALESCE(mi.series, '') ELSE '' END DESC, CASE WHEN $2 = 'date_published ASC' THEN COALESCE(mi.date_published::text, '') ELSE '' END ASC, CASE WHEN $2 = 'date_published DESC' THEN COALESCE(mi.date_published::text, '') ELSE '' END DESC, CASE WHEN $2 = 'copyright_year ASC' THEN COALESCE(mi.copyright_year::text, '0') ELSE '' END ASC, CASE WHEN $2 = 'copyright_year DESC' THEN COALESCE(mi.copyright_year::text, '0') ELSE '' END DESC, CASE WHEN $2 = 'page_count ASC' THEN COALESCE(mi.page_count::text, '0') ELSE '' END ASC, CASE WHEN $2 = 'page_count DESC' THEN COALESCE(mi.page_count::text, '0') ELSE '' END DESC, CASE WHEN $2 = 'genre ASC' THEN COALESCE(mi.genre, '') ELSE '' END ASC, CASE WHEN $2 = 'genre DESC' THEN COALESCE(mi.genre, '') ELSE '' END DESC, mi.created_at DESC LIMIT $4 OFFSET $3 ` type ListMediaItemsSortedParams struct { LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Sort interface{} `db:"sort" json:"sort"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type ListMediaItemsSortedRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LibraryName string `db:"library_name" json:"library_name"` LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"` } func (q *Queries) ListMediaItemsSorted(ctx context.Context, arg ListMediaItemsSortedParams) ([]ListMediaItemsSortedRow, error) { rows, err := q.db.Query(ctx, ListMediaItemsSorted, arg.LibraryID, arg.Sort, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []ListMediaItemsSortedRow{} for rows.Next() { var i ListMediaItemsSortedRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LibraryName, &i.LibraryTypeName_2, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListPendingHashConflicts = `-- name: ListPendingHashConflicts :many SELECT hc.id, hc.library_id, hc.file_sha256, hc.created_at, l.name AS library_name, COUNT(mi.id) AS item_count FROM hash_conflicts hc JOIN libraries l ON l.id = hc.library_id LEFT JOIN media_items mi ON mi.library_id = hc.library_id AND mi.file_sha256 = hc.file_sha256 WHERE hc.status = 'pending' GROUP BY hc.id, hc.library_id, hc.file_sha256, hc.created_at, l.name ORDER BY hc.created_at ` type ListPendingHashConflictsRow struct { ID pgtype.UUID `db:"id" json:"id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` FileSha256 string `db:"file_sha256" json:"file_sha256"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` LibraryName string `db:"library_name" json:"library_name"` ItemCount int64 `db:"item_count" json:"item_count"` } func (q *Queries) ListPendingHashConflicts(ctx context.Context) ([]ListPendingHashConflictsRow, error) { rows, err := q.db.Query(ctx, ListPendingHashConflicts) if err != nil { return nil, err } defer rows.Close() items := []ListPendingHashConflictsRow{} for rows.Next() { var i ListPendingHashConflictsRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.FileSha256, &i.CreatedAt, &i.LibraryName, &i.ItemCount, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListPendingSyncQueueItems = `-- name: ListPendingSyncQueueItems :many SELECT id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at FROM sync_queue WHERE device_id = $1 AND status = 'pending' ORDER BY priority ASC, created_at ASC LIMIT $2 ` type ListPendingSyncQueueItemsParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` Limit int32 `db:"limit" json:"limit"` } func (q *Queries) ListPendingSyncQueueItems(ctx context.Context, arg ListPendingSyncQueueItemsParams) ([]SyncQueue, error) { rows, err := q.db.Query(ctx, ListPendingSyncQueueItems, arg.DeviceID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []SyncQueue{} for rows.Next() { var i SyncQueue if err := rows.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListProcessingIssuesByLibrary = `-- name: ListProcessingIssuesByLibrary :many SELECT pi.id, pi.media_item_id, pi.issue_type, pi.issue_description, pi.severity, pi.resolved, pi.resolved_at, pi.created_at, mi.title, mi.file_path, mi.format_group, lt.name as library_type_name FROM processing_issues pi JOIN media_items mi ON pi.media_item_id = mi.id JOIN libraries l ON pi.library_id = l.id JOIN library_types lt ON l.library_type_id = lt.id WHERE pi.library_id = $1 AND pi.resolved = false ORDER BY CASE pi.severity WHEN 'error' THEN 1 WHEN 'warning' THEN 2 WHEN 'info' THEN 3 END, pi.created_at DESC ` type ListProcessingIssuesByLibraryRow struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` IssueType string `db:"issue_type" json:"issue_type"` IssueDescription string `db:"issue_description" json:"issue_description"` Severity string `db:"severity" json:"severity"` Resolved pgtype.Bool `db:"resolved" json:"resolved"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` Title string `db:"title" json:"title"` FilePath string `db:"file_path" json:"file_path"` FormatGroup string `db:"format_group" json:"format_group"` LibraryTypeName string `db:"library_type_name" json:"library_type_name"` } func (q *Queries) ListProcessingIssuesByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListProcessingIssuesByLibraryRow, error) { rows, err := q.db.Query(ctx, ListProcessingIssuesByLibrary, libraryID) if err != nil { return nil, err } defer rows.Close() items := []ListProcessingIssuesByLibraryRow{} for rows.Next() { var i ListProcessingIssuesByLibraryRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.IssueType, &i.IssueDescription, &i.Severity, &i.Resolved, &i.ResolvedAt, &i.CreatedAt, &i.Title, &i.FilePath, &i.FormatGroup, &i.LibraryTypeName, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListSyncConflictsByMediaItem = `-- name: ListSyncConflictsByMediaItem :many SELECT id, media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_by, resolved_at, created_at FROM sync_conflicts WHERE media_item_id = $1 AND user_id = $2 ORDER BY created_at DESC ` type ListSyncConflictsByMediaItemParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) ListSyncConflictsByMediaItem(ctx context.Context, arg ListSyncConflictsByMediaItemParams) ([]SyncConflicts, error) { rows, err := q.db.Query(ctx, ListSyncConflictsByMediaItem, arg.MediaItemID, arg.UserID) if err != nil { return nil, err } defer rows.Close() items := []SyncConflicts{} for rows.Next() { var i SyncConflicts if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListSyncConflictsByUser = `-- name: ListSyncConflictsByUser :many SELECT sc.id, sc.media_item_id, sc.user_id, sc.conflict_type, sc.conflict_data, sc.resolution_status, sc.resolution_data, sc.resolved_by, sc.resolved_at, sc.created_at, mi.title, mi.author FROM sync_conflicts sc JOIN media_items mi ON sc.media_item_id = mi.id WHERE sc.user_id = $1 AND sc.resolution_status = 'unresolved' ORDER BY sc.created_at DESC ` type ListSyncConflictsByUserRow 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"` ConflictType string `db:"conflict_type" json:"conflict_type"` ConflictData []byte `db:"conflict_data" json:"conflict_data"` ResolutionStatus pgtype.Text `db:"resolution_status" json:"resolution_status"` ResolutionData []byte `db:"resolution_data" json:"resolution_data"` ResolvedBy pgtype.UUID `db:"resolved_by" json:"resolved_by"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` } func (q *Queries) ListSyncConflictsByUser(ctx context.Context, userID pgtype.UUID) ([]ListSyncConflictsByUserRow, error) { rows, err := q.db.Query(ctx, ListSyncConflictsByUser, userID) if err != nil { return nil, err } defer rows.Close() items := []ListSyncConflictsByUserRow{} for rows.Next() { var i ListSyncConflictsByUserRow if err := rows.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, &i.Title, &i.Author, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListUnresolvedUnlinkedBooks = `-- name: ListUnresolvedUnlinkedBooks :many SELECT ub.id, ub.device_id, ub.content_id, ub.file_path, ub.title, ub.author, ub.confidence_score, ub.resolved, ub.media_item_id, ub.resolved_at, ub.resolution_method, ub.last_seen_at, ub.created_at, d.device_name, d.device_type FROM unlinked_books ub JOIN devices d ON ub.device_id = d.id WHERE ub.resolved = false ORDER BY ub.last_seen_at DESC LIMIT $1 OFFSET $2 ` type ListUnresolvedUnlinkedBooksParams struct { Limit int32 `db:"limit" json:"limit"` Offset int32 `db:"offset" json:"offset"` } type ListUnresolvedUnlinkedBooksRow struct { ID pgtype.UUID `db:"id" json:"id"` DeviceID pgtype.UUID `db:"device_id" json:"device_id"` ContentID string `db:"content_id" json:"content_id"` FilePath pgtype.Text `db:"file_path" json:"file_path"` Title pgtype.Text `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` Resolved pgtype.Bool `db:"resolved" json:"resolved"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ResolvedAt pgtype.Timestamptz `db:"resolved_at" json:"resolved_at"` ResolutionMethod pgtype.Text `db:"resolution_method" json:"resolution_method"` LastSeenAt pgtype.Timestamptz `db:"last_seen_at" json:"last_seen_at"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` DeviceName string `db:"device_name" json:"device_name"` DeviceType string `db:"device_type" json:"device_type"` } // List unresolved unlinked books with pagination func (q *Queries) ListUnresolvedUnlinkedBooks(ctx context.Context, arg ListUnresolvedUnlinkedBooksParams) ([]ListUnresolvedUnlinkedBooksRow, error) { rows, err := q.db.Query(ctx, ListUnresolvedUnlinkedBooks, arg.Limit, arg.Offset) if err != nil { return nil, err } defer rows.Close() items := []ListUnresolvedUnlinkedBooksRow{} for rows.Next() { var i ListUnresolvedUnlinkedBooksRow if err := rows.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, &i.DeviceName, &i.DeviceType, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ListUsers = `-- name: ListUsers :many SELECT u.id, u.email, u.username, u.theme, u.first_name, u.last_name, u.role, u.max_devices, u.created_at, u.updated_at, (SELECT COUNT(*) FROM devices WHERE user_id = u.id) as device_count FROM users u ORDER BY u.created_at DESC ` type ListUsersRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Theme pgtype.Text `db:"theme" json:"theme"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` Role string `db:"role" json:"role"` MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` DeviceCount int64 `db:"device_count" json:"device_count"` } func (q *Queries) ListUsers(ctx context.Context) ([]ListUsersRow, error) { rows, err := q.db.Query(ctx, ListUsers) if err != nil { return nil, err } defer rows.Close() items := []ListUsersRow{} for rows.Next() { var i ListUsersRow if err := rows.Scan( &i.ID, &i.Email, &i.Username, &i.Theme, &i.FirstName, &i.LastName, &i.Role, &i.MaxDevices, &i.CreatedAt, &i.UpdatedAt, &i.DeviceCount, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const PurgeExpiredBookmarkTombstones = `-- name: PurgeExpiredBookmarkTombstones :exec DELETE FROM media_bookmarks WHERE deleted = TRUE AND deleted_at < $1 ` func (q *Queries) PurgeExpiredBookmarkTombstones(ctx context.Context, deletedAt pgtype.Timestamptz) error { _, err := q.db.Exec(ctx, PurgeExpiredBookmarkTombstones, deletedAt) return err } const PurgeExpiredHighlightTombstones = `-- name: PurgeExpiredHighlightTombstones :exec DELETE FROM media_highlights WHERE deleted = TRUE AND deleted_at < $1 ` func (q *Queries) PurgeExpiredHighlightTombstones(ctx context.Context, deletedAt pgtype.Timestamptz) error { _, err := q.db.Exec(ctx, PurgeExpiredHighlightTombstones, deletedAt) return err } const PurgeExpiredNoteTombstones = `-- name: PurgeExpiredNoteTombstones :exec DELETE FROM media_notes WHERE deleted = TRUE AND deleted_at < $1 ` func (q *Queries) PurgeExpiredNoteTombstones(ctx context.Context, deletedAt pgtype.Timestamptz) error { _, err := q.db.Exec(ctx, PurgeExpiredNoteTombstones, deletedAt) return err } const PurgeMediaBookmarkByID = `-- name: PurgeMediaBookmarkByID :execrows DELETE FROM media_bookmarks WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type PurgeMediaBookmarkByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) PurgeMediaBookmarkByID(ctx context.Context, arg PurgeMediaBookmarkByIDParams) (int64, error) { result, err := q.db.Exec(ctx, PurgeMediaBookmarkByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const PurgeMediaHighlightByID = `-- name: PurgeMediaHighlightByID :execrows DELETE FROM media_highlights WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type PurgeMediaHighlightByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } // Permanent removal from the history (distinct from the TTL-driven purge, // which is maintenance). Scoped to the owning user and book. func (q *Queries) PurgeMediaHighlightByID(ctx context.Context, arg PurgeMediaHighlightByIDParams) (int64, error) { result, err := q.db.Exec(ctx, PurgeMediaHighlightByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const PurgeMediaNoteByID = `-- name: PurgeMediaNoteByID :execrows DELETE FROM media_notes WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type PurgeMediaNoteByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) PurgeMediaNoteByID(ctx context.Context, arg PurgeMediaNoteByIDParams) (int64, error) { result, err := q.db.Exec(ctx, PurgeMediaNoteByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const QueryMediaItemsByIdentifiers = `-- name: QueryMediaItemsByIdentifiers :many SELECT mi.id, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.isbn, mi.asin, mi.title, mi.author, mi.file_size, mi.hash_confidence, CASE WHEN $1::uuid IS NOT NULL AND mi.id = $1::uuid THEN 1.0 WHEN mi.opf_uuid IS NOT NULL AND mi.opf_uuid = $2::text THEN 0.95 WHEN mi.file_sha256 IS NOT NULL AND mi.file_sha256 = $3::text THEN 0.9 WHEN mi.opf_identifier IS NOT NULL AND mi.opf_identifier = $4::text THEN 0.85 WHEN mi.isbn IS NOT NULL AND mi.isbn = $5::text THEN 0.8 WHEN mi.asin IS NOT NULL AND mi.asin = $6::text THEN 0.8 ELSE 0.5 END as confidence_score FROM media_items mi WHERE ($1::uuid IS NULL OR mi.id = $1::uuid) OR ($2::text IS NULL OR mi.opf_uuid = $2::text) OR ($3::text IS NULL OR mi.file_sha256 = $3::text) OR ($4::text IS NULL OR mi.opf_identifier = $4::text) OR ($5::text IS NULL OR mi.isbn = $5::text) OR ($6::text IS NULL OR mi.asin = $6::text) OR ($7::text IS NULL OR mi.title ILIKE '%' || $7::text || '%') ORDER BY confidence_score DESC ` type QueryMediaItemsByIdentifiersParams struct { Column1 pgtype.UUID `db:"column_1" json:"column_1"` Column2 string `db:"column_2" json:"column_2"` Column3 string `db:"column_3" json:"column_3"` Column4 string `db:"column_4" json:"column_4"` Column5 string `db:"column_5" json:"column_5"` Column6 string `db:"column_6" json:"column_6"` Column7 string `db:"column_7" json:"column_7"` } type QueryMediaItemsByIdentifiersRow struct { ID pgtype.UUID `db:"id" json:"id"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` Isbn pgtype.Text `db:"isbn" json:"isbn"` Asin pgtype.Text `db:"asin" json:"asin"` Title string `db:"title" json:"title"` Author pgtype.Text `db:"author" json:"author"` FileSize pgtype.Int8 `db:"file_size" json:"file_size"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` ConfidenceScore float64 `db:"confidence_score" json:"confidence_score"` } // Query media items by multiple identifiers with confidence scoring func (q *Queries) QueryMediaItemsByIdentifiers(ctx context.Context, arg QueryMediaItemsByIdentifiersParams) ([]QueryMediaItemsByIdentifiersRow, error) { rows, err := q.db.Query(ctx, QueryMediaItemsByIdentifiers, arg.Column1, arg.Column2, arg.Column3, arg.Column4, arg.Column5, arg.Column6, arg.Column7, ) if err != nil { return nil, err } defer rows.Close() items := []QueryMediaItemsByIdentifiersRow{} for rows.Next() { var i QueryMediaItemsByIdentifiersRow if err := rows.Scan( &i.ID, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.Isbn, &i.Asin, &i.Title, &i.Author, &i.FileSize, &i.HashConfidence, &i.ConfidenceScore, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const ReassignLibraries = `-- name: ReassignLibraries :exec UPDATE libraries SET created_by_admin_id = $2, updated_at = NOW() WHERE created_by_admin_id = $1 ` type ReassignLibrariesParams struct { CreatedByAdminID pgtype.UUID `db:"created_by_admin_id" json:"created_by_admin_id"` CreatedByAdminID_2 pgtype.UUID `db:"created_by_admin_id_2" json:"created_by_admin_id_2"` } func (q *Queries) ReassignLibraries(ctx context.Context, arg ReassignLibrariesParams) error { _, err := q.db.Exec(ctx, ReassignLibraries, arg.CreatedByAdminID, arg.CreatedByAdminID_2) return err } const ReassignMediaItems = `-- name: ReassignMediaItems :exec UPDATE media_items SET added_by_admin_id = $2 WHERE added_by_admin_id = $1 ` type ReassignMediaItemsParams struct { AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` AddedByAdminID_2 pgtype.UUID `db:"added_by_admin_id_2" json:"added_by_admin_id_2"` } func (q *Queries) ReassignMediaItems(ctx context.Context, arg ReassignMediaItemsParams) error { _, err := q.db.Exec(ctx, ReassignMediaItems, arg.AddedByAdminID, arg.AddedByAdminID_2) return err } const RemoveBookFromCollection = `-- name: RemoveBookFromCollection :exec DELETE FROM collection_items WHERE collection_id = $1 AND media_item_id = $2 ` type RemoveBookFromCollectionParams struct { CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } // Remove book from collection func (q *Queries) RemoveBookFromCollection(ctx context.Context, arg RemoveBookFromCollectionParams) error { _, err := q.db.Exec(ctx, RemoveBookFromCollection, arg.CollectionID, arg.MediaItemID) return err } const RemoveBookFromKoboShelf = `-- name: RemoveBookFromKoboShelf :exec DELETE FROM kobo_shelves WHERE device_id = $1 AND media_item_id = $2 ` type RemoveBookFromKoboShelfParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) RemoveBookFromKoboShelf(ctx context.Context, arg RemoveBookFromKoboShelfParams) error { _, err := q.db.Exec(ctx, RemoveBookFromKoboShelf, arg.DeviceID, arg.MediaItemID) return err } const ReparentMediaItemChildren = `-- name: ReparentMediaItemChildren :exec SELECT reparent_media_item_children($1::uuid, $2::uuid) ` type ReparentMediaItemChildrenParams struct { Column1 pgtype.UUID `db:"column_1" json:"column_1"` Column2 pgtype.UUID `db:"column_2" json:"column_2"` } // Re-parent all child rows of p_source onto p_target (defined in schema.sql) func (q *Queries) ReparentMediaItemChildren(ctx context.Context, arg ReparentMediaItemChildrenParams) error { _, err := q.db.Exec(ctx, ReparentMediaItemChildren, arg.Column1, arg.Column2) return err } const ResetSystemCollectionMetadata = `-- name: ResetSystemCollectionMetadata :exec UPDATE collections SET description = $3, icon = $4, color = $5, priority = $6 WHERE user_id = $1 AND name = $2 AND is_system_collection = true ` type ResetSystemCollectionMetadataParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` Icon pgtype.Text `db:"icon" json:"icon"` Color pgtype.Text `db:"color" json:"color"` Priority pgtype.Int4 `db:"priority" json:"priority"` } func (q *Queries) ResetSystemCollectionMetadata(ctx context.Context, arg ResetSystemCollectionMetadataParams) error { _, err := q.db.Exec(ctx, ResetSystemCollectionMetadata, arg.UserID, arg.Name, arg.Description, arg.Icon, arg.Color, arg.Priority, ) return err } const ResolveHashConflict = `-- name: ResolveHashConflict :exec UPDATE hash_conflicts SET status = 'resolved', resolution = $2, resolved_by = $3, resolved_at = NOW() WHERE id = $1 ` type ResolveHashConflictParams struct { ID pgtype.UUID `db:"id" json:"id"` Resolution pgtype.Text `db:"resolution" json:"resolution"` ResolvedBy pgtype.UUID `db:"resolved_by" json:"resolved_by"` } func (q *Queries) ResolveHashConflict(ctx context.Context, arg ResolveHashConflictParams) error { _, err := q.db.Exec(ctx, ResolveHashConflict, arg.ID, arg.Resolution, arg.ResolvedBy) return err } const ResolveProcessingIssue = `-- name: ResolveProcessingIssue :one UPDATE processing_issues SET resolved = true, resolved_at = NOW() WHERE id = $1 AND media_item_id = $2 RETURNING id, media_item_id, library_id, issue_type, issue_description, severity, resolved, resolved_at, created_at ` type ResolveProcessingIssueParams struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) ResolveProcessingIssue(ctx context.Context, arg ResolveProcessingIssueParams) (ProcessingIssues, error) { row := q.db.QueryRow(ctx, ResolveProcessingIssue, arg.ID, arg.MediaItemID) var i ProcessingIssues err := row.Scan( &i.ID, &i.MediaItemID, &i.LibraryID, &i.IssueType, &i.IssueDescription, &i.Severity, &i.Resolved, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const ResolveSyncConflict = `-- name: ResolveSyncConflict :one UPDATE sync_conflicts SET resolution_status = $2, resolution_data = $3, resolved_by = $4, resolved_at = NOW() WHERE id = $1 RETURNING id, media_item_id, user_id, conflict_type, conflict_data, resolution_status, resolution_data, resolved_by, resolved_at, created_at ` type ResolveSyncConflictParams struct { ID pgtype.UUID `db:"id" json:"id"` ResolutionStatus pgtype.Text `db:"resolution_status" json:"resolution_status"` ResolutionData []byte `db:"resolution_data" json:"resolution_data"` ResolvedBy pgtype.UUID `db:"resolved_by" json:"resolved_by"` } func (q *Queries) ResolveSyncConflict(ctx context.Context, arg ResolveSyncConflictParams) (SyncConflicts, error) { row := q.db.QueryRow(ctx, ResolveSyncConflict, arg.ID, arg.ResolutionStatus, arg.ResolutionData, arg.ResolvedBy, ) var i SyncConflicts err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.ConflictType, &i.ConflictData, &i.ResolutionStatus, &i.ResolutionData, &i.ResolvedBy, &i.ResolvedAt, &i.CreatedAt, ) return i, err } const ResolveUnlinkedBook = `-- name: ResolveUnlinkedBook :one UPDATE unlinked_books SET resolved = true, media_item_id = $2, resolved_at = NOW(), resolution_method = $3 WHERE id = $1 RETURNING id, device_id, content_id, file_path, title, author, confidence_score, resolved, media_item_id, resolved_at, resolution_method, last_seen_at, created_at ` type ResolveUnlinkedBookParams struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ResolutionMethod pgtype.Text `db:"resolution_method" json:"resolution_method"` } // Resolve unlinked book func (q *Queries) ResolveUnlinkedBook(ctx context.Context, arg ResolveUnlinkedBookParams) (UnlinkedBooks, error) { row := q.db.QueryRow(ctx, ResolveUnlinkedBook, arg.ID, arg.MediaItemID, arg.ResolutionMethod) var i UnlinkedBooks err := row.Scan( &i.ID, &i.DeviceID, &i.ContentID, &i.FilePath, &i.Title, &i.Author, &i.ConfidenceScore, &i.Resolved, &i.MediaItemID, &i.ResolvedAt, &i.ResolutionMethod, &i.LastSeenAt, &i.CreatedAt, ) return i, err } const RestoreMediaBookmarkByID = `-- name: RestoreMediaBookmarkByID :execrows UPDATE media_bookmarks SET deleted = FALSE, deleted_at = NULL, last_modified_at = NOW() WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type RestoreMediaBookmarkByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) RestoreMediaBookmarkByID(ctx context.Context, arg RestoreMediaBookmarkByIDParams) (int64, error) { result, err := q.db.Exec(ctx, RestoreMediaBookmarkByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const RestoreMediaHighlightByID = `-- name: RestoreMediaHighlightByID :execrows UPDATE media_highlights SET deleted = FALSE, deleted_at = NULL, last_modified_at = NOW() WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type RestoreMediaHighlightByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) RestoreMediaHighlightByID(ctx context.Context, arg RestoreMediaHighlightByIDParams) (int64, error) { result, err := q.db.Exec(ctx, RestoreMediaHighlightByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const RestoreMediaNoteByID = `-- name: RestoreMediaNoteByID :execrows UPDATE media_notes SET deleted = FALSE, deleted_at = NULL, last_modified_at = NOW() WHERE id = $1 AND user_id = $2 AND media_item_id = $3 AND deleted = TRUE ` type RestoreMediaNoteByIDParams struct { ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` } func (q *Queries) RestoreMediaNoteByID(ctx context.Context, arg RestoreMediaNoteByIDParams) (int64, error) { result, err := q.db.Exec(ctx, RestoreMediaNoteByID, arg.ID, arg.UserID, arg.MediaItemID) if err != nil { return 0, err } return result.RowsAffected(), nil } const RevokeAllUserRefreshTokens = `-- name: RevokeAllUserRefreshTokens :exec UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = $1 AND revoked_at IS NULL ` func (q *Queries) RevokeAllUserRefreshTokens(ctx context.Context, userID pgtype.UUID) error { _, err := q.db.Exec(ctx, RevokeAllUserRefreshTokens, userID) return err } const RevokeDevice = `-- name: RevokeDevice :exec UPDATE devices SET auth_token = NULL, sync_enabled = false, updated_at = NOW() WHERE id = $1 ` func (q *Queries) RevokeDevice(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, RevokeDevice, id) return err } const RevokeOpdsToken = `-- name: RevokeOpdsToken :exec DELETE FROM opds_tokens WHERE token = $1 ` // Revoke OPDS token func (q *Queries) RevokeOpdsToken(ctx context.Context, token string) error { _, err := q.db.Exec(ctx, RevokeOpdsToken, token) return err } const RevokeRefreshToken = `-- name: RevokeRefreshToken :exec UPDATE refresh_tokens SET revoked_at = NOW() WHERE token = $1 ` func (q *Queries) RevokeRefreshToken(ctx context.Context, token pgtype.UUID) error { _, err := q.db.Exec(ctx, RevokeRefreshToken, token) return err } const SearchAuthorValues = `-- name: SearchAuthorValues :many SELECT mi.author as value, COUNT(*) as count, word_similarity($1, COALESCE(mi.author, ''))::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 WHERE COALESCE(lv.is_visible, true) = true AND mi.library_id = $3 AND word_similarity($1, COALESCE(mi.author, '')) > 0.3 AND mi.author IS NOT NULL AND mi.author != '' GROUP BY mi.author, word_similarity($1, COALESCE(mi.author, ''))::float8 ORDER BY score DESC, count DESC LIMIT $5 OFFSET $4 ` type SearchAuthorValuesParams struct { SearchQuery pgtype.Text `db:"search_query" json:"search_query"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchAuthorValuesRow struct { Value pgtype.Text `db:"value" json:"value"` Count int64 `db:"count" json:"count"` Score float64 `db:"score" json:"score"` } func (q *Queries) SearchAuthorValues(ctx context.Context, arg SearchAuthorValuesParams) ([]SearchAuthorValuesRow, error) { rows, err := q.db.Query(ctx, SearchAuthorValues, arg.SearchQuery, arg.UserID, arg.LibraryID, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchAuthorValuesRow{} for rows.Next() { var i SearchAuthorValuesRow if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchGenreValues = `-- name: SearchGenreValues :many SELECT mi.genre as value, COUNT(*) as count, word_similarity($1, COALESCE(mi.genre, ''))::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 WHERE COALESCE(lv.is_visible, true) = true AND mi.library_id = $3 AND word_similarity($1, COALESCE(mi.genre, '')) > 0.3 AND mi.genre IS NOT NULL AND mi.genre != '' GROUP BY mi.genre, word_similarity($1, COALESCE(mi.genre, ''))::float8 ORDER BY score DESC, count DESC LIMIT $5 OFFSET $4 ` type SearchGenreValuesParams struct { SearchQuery pgtype.Text `db:"search_query" json:"search_query"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchGenreValuesRow struct { Value pgtype.Text `db:"value" json:"value"` Count int64 `db:"count" json:"count"` Score float64 `db:"score" json:"score"` } func (q *Queries) SearchGenreValues(ctx context.Context, arg SearchGenreValuesParams) ([]SearchGenreValuesRow, error) { rows, err := q.db.Query(ctx, SearchGenreValues, arg.SearchQuery, arg.UserID, arg.LibraryID, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchGenreValuesRow{} for rows.Next() { var i SearchGenreValuesRow if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchLanguageValues = `-- name: SearchLanguageValues :many SELECT mi.language as value, COUNT(*) as count, word_similarity($1, COALESCE(mi.language, ''))::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 WHERE COALESCE(lv.is_visible, true) = true AND mi.library_id = $3 AND word_similarity($1, COALESCE(mi.language, '')) > 0.3 AND mi.language IS NOT NULL AND mi.language != '' GROUP BY mi.language, word_similarity($1, COALESCE(mi.language, ''))::float8 ORDER BY score DESC, count DESC LIMIT $5 OFFSET $4 ` type SearchLanguageValuesParams struct { SearchQuery pgtype.Text `db:"search_query" json:"search_query"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchLanguageValuesRow struct { Value pgtype.Text `db:"value" json:"value"` Count int64 `db:"count" json:"count"` Score float64 `db:"score" json:"score"` } func (q *Queries) SearchLanguageValues(ctx context.Context, arg SearchLanguageValuesParams) ([]SearchLanguageValuesRow, error) { rows, err := q.db.Query(ctx, SearchLanguageValues, arg.SearchQuery, arg.UserID, arg.LibraryID, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchLanguageValuesRow{} for rows.Next() { var i SearchLanguageValuesRow if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchMediaItems = `-- name: SearchMediaItems :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, 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 LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1 WHERE COALESCE(lv.is_visible, true) = true AND ($2::uuid IS NULL OR mi.library_id = $2::uuid) AND ( mi.title ILIKE $3 OR mi.author ILIKE $3 OR mi.series ILIKE $3 OR $3 = ANY(mi.tags_search) OR $3 = ANY(mi.contributors_search) ) ORDER BY CASE WHEN mi.title ILIKE $3 THEN 1 WHEN mi.author ILIKE $3 THEN 2 WHEN mi.series ILIKE $3 THEN 3 WHEN $3 = ANY(mi.tags_search) THEN 4 ELSE 5 END, mi.title ASC LIMIT $5 OFFSET $4 ` type SearchMediaItemsParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` SearchPattern pgtype.Text `db:"search_pattern" json:"search_pattern"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchMediaItemsRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"` CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"` ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"` UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LibraryName string `db:"library_name" json:"library_name"` LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"` } // Search Media Items queries func (q *Queries) SearchMediaItems(ctx context.Context, arg SearchMediaItemsParams) ([]SearchMediaItemsRow, error) { rows, err := q.db.Query(ctx, SearchMediaItems, arg.UserID, arg.LibraryID, arg.SearchPattern, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchMediaItemsRow{} for rows.Next() { var i SearchMediaItemsRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LibraryName, &i.LibraryTypeName_2, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchMediaItemsUnified = `-- name: SearchMediaItemsUnified :many SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, 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 LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1 WHERE COALESCE(lv.is_visible, true) = true AND ($2::uuid IS NULL OR mi.library_id = $2::uuid) -- Fuzzy author filter AND ($3 = '' OR word_similarity($3, COALESCE(mi.author, '')) > 0.3) -- Fuzzy series filter AND ($4 = '' OR word_similarity($4, COALESCE(mi.series, '')) > 0.3) -- Fuzzy genre filter AND ($5 = '' OR word_similarity($5, COALESCE(mi.genre, '')) > 0.3) -- Fuzzy language filter AND ($6 = '' OR word_similarity($6, COALESCE(mi.language, '')) > 0.3) -- Tags filter (NEW - fuzzy match against tags array) AND ($7 = '' OR EXISTS ( SELECT 1 FROM unnest(mi.tags_search) AS tag WHERE word_similarity($7, tag) > 0.3 )) -- Year range (exact) - prioritize date_published, fallback to copyright_year AND ( $8 = 0 OR EXTRACT(YEAR FROM mi.date_published) >= $8 OR (mi.date_published IS NULL AND mi.copyright_year >= $8) ) AND ( $9 = 0 OR EXTRACT(YEAR FROM mi.date_published) <= $9 OR (mi.date_published IS NULL AND mi.copyright_year <= $9) ) -- Boolean (exact) AND ( $10::bool IS NULL OR -- Not specified = show all ($10::bool IS TRUE AND mi.cover_image_path IS NOT NULL) OR ($10::bool IS FALSE AND mi.cover_image_path IS NULL) ) -- Search query (fuzzy or exact based on quotes) AND ( $11 = '' OR -- Fuzzy search (default) $12 = false AND ( word_similarity($11, mi.title) > 0.3 OR word_similarity($11, COALESCE(mi.author, '')) > 0.3 OR word_similarity($11, COALESCE(mi.series, '')) > 0.3 OR EXISTS ( SELECT 1 FROM unnest(mi.tags_search) AS tag WHERE word_similarity($11, tag) > 0.3 LIMIT 1 ) OR EXISTS ( SELECT 1 FROM unnest(mi.contributors_search) AS contributor WHERE word_similarity($11, contributor) > 0.3 LIMIT 1 ) ) OR -- Exact search (with quotes) -- Keeping old pattern commented out in case we want wildcard exact back. See search.go line 62 -- sqlc.narg('is_exact_search') = true AND ( -- mi.title ILIKE sqlc.narg('search_pattern') OR -- mi.author ILIKE sqlc.narg('search_pattern') OR -- mi.series ILIKE sqlc.narg('search_pattern') OR -- sqlc.narg('search_pattern') = ANY(mi.tags_search) OR -- sqlc.narg('search_pattern') = ANY(mi.contributors_search) -- ) -- Exact search (with quotes) - true exact match, not substring $12 = true AND ( mi.title = $11 OR COALESCE(mi.author, '') = $11 OR COALESCE(mi.series, '') = $11 OR $11 = ANY(mi.tags_search) OR $11 = ANY(mi.contributors_search) ) ) ORDER BY -- Primary sort: relevance score when searching CASE WHEN $11 != '' THEN GREATEST( CASE WHEN $12 = false THEN word_similarity($11, mi.title) ELSE 0 END, CASE WHEN $12 = false THEN word_similarity($11, COALESCE(mi.author, '')) ELSE 0 END, word_similarity($3, COALESCE(mi.author, '')), word_similarity($5, COALESCE(mi.genre, '')), (SELECT MAX(word_similarity($7, tag)) FROM unnest(mi.tags_search) AS tag) ) ELSE 0 END DESC, -- Secondary sort: user-specified sort parameter CASE WHEN $13 = 'title ASC' THEN mi.title ELSE '' END ASC, CASE WHEN $13 = 'title DESC' THEN mi.title ELSE '' END DESC, CASE WHEN $13 = 'author ASC' THEN COALESCE(mi.author, '') ELSE '' END ASC, CASE WHEN $13 = 'author DESC' THEN COALESCE(mi.author, '') ELSE '' END DESC, CASE WHEN $13 = 'created_at ASC' THEN mi.created_at ELSE '1970-01-01'::timestamp END ASC, CASE WHEN $13 = 'created_at DESC' THEN mi.created_at ELSE '1970-01-01'::timestamp END DESC, CASE WHEN $13 = 'page_count ASC' THEN COALESCE(mi.page_count::text, '0') ELSE '' END ASC, CASE WHEN $13 = 'page_count DESC' THEN COALESCE(mi.page_count::text, '0') ELSE '' END DESC, -- Tertiary sort: title (default fallback) mi.title ASC LIMIT $15 OFFSET $14 ` type SearchMediaItemsUnifiedParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` AuthorFilter interface{} `db:"author_filter" json:"author_filter"` SeriesFilter interface{} `db:"series_filter" json:"series_filter"` GenreFilter interface{} `db:"genre_filter" json:"genre_filter"` LanguageFilter interface{} `db:"language_filter" json:"language_filter"` TagsFilter interface{} `db:"tags_filter" json:"tags_filter"` YearMin interface{} `db:"year_min" json:"year_min"` YearMax interface{} `db:"year_max" json:"year_max"` HasCover pgtype.Bool `db:"has_cover" json:"has_cover"` SearchQuery interface{} `db:"search_query" json:"search_query"` IsExactSearch interface{} `db:"is_exact_search" json:"is_exact_search"` Sort interface{} `db:"sort" json:"sort"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchMediaItemsUnifiedRow 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 []string `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 []string `db:"contributors" json:"contributors"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` 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"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` TagsSearch []string `db:"tags_search" json:"tags_search"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` LibraryName string `db:"library_name" json:"library_name"` LibraryTypeName string `db:"library_type_name" json:"library_type_name"` } func (q *Queries) SearchMediaItemsUnified(ctx context.Context, arg SearchMediaItemsUnifiedParams) ([]SearchMediaItemsUnifiedRow, error) { rows, err := q.db.Query(ctx, SearchMediaItemsUnified, arg.UserID, arg.LibraryID, arg.AuthorFilter, arg.SeriesFilter, arg.GenreFilter, arg.LanguageFilter, arg.TagsFilter, arg.YearMin, arg.YearMax, arg.HasCover, arg.SearchQuery, arg.IsExactSearch, arg.Sort, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchMediaItemsUnifiedRow{} for rows.Next() { var i SearchMediaItemsUnifiedRow if err := rows.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, &i.LibraryName, &i.LibraryTypeName, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchSeriesValues = `-- name: SearchSeriesValues :many SELECT mi.series as value, COUNT(*) as count, word_similarity($1, COALESCE(mi.series, ''))::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 WHERE COALESCE(lv.is_visible, true) = true AND mi.library_id = $3 AND word_similarity($1, COALESCE(mi.series, '')) > 0.3 AND mi.series IS NOT NULL AND mi.series != '' GROUP BY mi.series, word_similarity($1, COALESCE(mi.series, ''))::float8 ORDER BY score DESC, count DESC LIMIT $5 OFFSET $4 ` type SearchSeriesValuesParams struct { SearchQuery pgtype.Text `db:"search_query" json:"search_query"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchSeriesValuesRow struct { Value pgtype.Text `db:"value" json:"value"` Count int64 `db:"count" json:"count"` Score float64 `db:"score" json:"score"` } func (q *Queries) SearchSeriesValues(ctx context.Context, arg SearchSeriesValuesParams) ([]SearchSeriesValuesRow, error) { rows, err := q.db.Query(ctx, SearchSeriesValues, arg.SearchQuery, arg.UserID, arg.LibraryID, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchSeriesValuesRow{} for rows.Next() { var i SearchSeriesValuesRow if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SearchTagsValues = `-- name: SearchTagsValues :many SELECT tag::TEXT as value, COUNT(*) as count, word_similarity($1, tag)::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 CROSS JOIN LATERAL unnest(mi.tags_search) AS tag WHERE COALESCE(lv.is_visible, true) = true AND ($3::uuid IS NULL OR mi.library_id = $3::uuid) AND tag IS NOT NULL AND word_similarity($1, tag) > 0.3 GROUP BY tag, word_similarity($1, tag)::float8 ORDER BY word_similarity($1, tag)::float8 DESC, COUNT DESC LIMIT $5 OFFSET $4 ` type SearchTagsValuesParams struct { SearchQuery pgtype.Text `db:"search_query" json:"search_query"` UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` Offset pgtype.Int4 `db:"offset" json:"offset"` Limit pgtype.Int4 `db:"limit" json:"limit"` } type SearchTagsValuesRow struct { Value string `db:"value" json:"value"` Count int64 `db:"count" json:"count"` Score float64 `db:"score" json:"score"` } func (q *Queries) SearchTagsValues(ctx context.Context, arg SearchTagsValuesParams) ([]SearchTagsValuesRow, error) { rows, err := q.db.Query(ctx, SearchTagsValues, arg.SearchQuery, arg.UserID, arg.LibraryID, arg.Offset, arg.Limit, ) if err != nil { return nil, err } defer rows.Close() items := []SearchTagsValuesRow{} for rows.Next() { var i SearchTagsValuesRow if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const SetLibraryVisibility = `-- 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 id, user_id, library_id, is_visible, created_at, updated_at ` type SetLibraryVisibilityParams struct { 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"` } // Library Visibility queries func (q *Queries) SetLibraryVisibility(ctx context.Context, arg SetLibraryVisibilityParams) (LibraryVisibility, error) { row := q.db.QueryRow(ctx, SetLibraryVisibility, arg.UserID, arg.LibraryID, arg.IsVisible) var i LibraryVisibility err := row.Scan( &i.ID, &i.UserID, &i.LibraryID, &i.IsVisible, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const SetSystemConfig = `-- name: SetSystemConfig :one INSERT INTO system_config (key, value, updated_by) VALUES ($1, $2, $3) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_by = EXCLUDED.updated_by, updated_at = NOW() RETURNING key, value, updated_at, updated_by ` type SetSystemConfigParams struct { Key string `db:"key" json:"key"` Value string `db:"value" json:"value"` UpdatedBy pgtype.UUID `db:"updated_by" json:"updated_by"` } // Set system config func (q *Queries) SetSystemConfig(ctx context.Context, arg SetSystemConfigParams) (SystemConfig, error) { row := q.db.QueryRow(ctx, SetSystemConfig, arg.Key, arg.Value, arg.UpdatedBy) var i SystemConfig err := row.Scan( &i.Key, &i.Value, &i.UpdatedAt, &i.UpdatedBy, ) return i, err } const SyncLibraryTypeExtensions = `-- name: SyncLibraryTypeExtensions :exec UPDATE library_types SET allowed_extensions = $2 WHERE name = $1 ` type SyncLibraryTypeExtensionsParams struct { Name string `db:"name" json:"name"` AllowedExtensions []string `db:"allowed_extensions" json:"allowed_extensions"` } func (q *Queries) SyncLibraryTypeExtensions(ctx context.Context, arg SyncLibraryTypeExtensionsParams) error { _, err := q.db.Exec(ctx, SyncLibraryTypeExtensions, arg.Name, arg.AllowedExtensions) return err } const TombstoneMediaBookmarkByDedupKey = `-- name: TombstoneMediaBookmarkByDedupKey :exec UPDATE media_bookmarks SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 AND deleted = FALSE ` type TombstoneMediaBookmarkByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } func (q *Queries) TombstoneMediaBookmarkByDedupKey(ctx context.Context, arg TombstoneMediaBookmarkByDedupKeyParams) error { _, err := q.db.Exec(ctx, TombstoneMediaBookmarkByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) return err } const TombstoneMediaBookmarkByID = `-- name: TombstoneMediaBookmarkByID :exec UPDATE media_bookmarks SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE id = $1 ` func (q *Queries) TombstoneMediaBookmarkByID(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, TombstoneMediaBookmarkByID, id) return err } const TombstoneMediaHighlightByDedupKey = `-- name: TombstoneMediaHighlightByDedupKey :exec UPDATE media_highlights SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 AND deleted = FALSE ` type TombstoneMediaHighlightByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } func (q *Queries) TombstoneMediaHighlightByDedupKey(ctx context.Context, arg TombstoneMediaHighlightByDedupKeyParams) error { _, err := q.db.Exec(ctx, TombstoneMediaHighlightByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) return err } const TombstoneMediaHighlightByID = `-- name: TombstoneMediaHighlightByID :exec UPDATE media_highlights SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE id = $1 ` func (q *Queries) TombstoneMediaHighlightByID(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, TombstoneMediaHighlightByID, id) return err } const TombstoneMediaNoteByDedupKey = `-- name: TombstoneMediaNoteByDedupKey :exec UPDATE media_notes SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE user_id = $1 AND media_item_id = $2 AND dedup_key = $3 AND deleted = FALSE ` type TombstoneMediaNoteByDedupKeyParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` DedupKey pgtype.Text `db:"dedup_key" json:"dedup_key"` } func (q *Queries) TombstoneMediaNoteByDedupKey(ctx context.Context, arg TombstoneMediaNoteByDedupKeyParams) error { _, err := q.db.Exec(ctx, TombstoneMediaNoteByDedupKey, arg.UserID, arg.MediaItemID, arg.DedupKey) return err } const TombstoneMediaNoteByID = `-- name: TombstoneMediaNoteByID :exec UPDATE media_notes SET deleted = TRUE, deleted_at = NOW(), last_modified_at = NOW() WHERE id = $1 ` func (q *Queries) TombstoneMediaNoteByID(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, TombstoneMediaNoteByID, id) return err } const UpdateCollection = `-- name: UpdateCollection :one UPDATE collections SET name = $2, description = $3, color = $4, icon = $5, auto_assign_rules = $6, view_settings = $7 WHERE id = $1 RETURNING id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at ` type UpdateCollectionParams struct { ID pgtype.UUID `db:"id" json:"id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` Color pgtype.Text `db:"color" json:"color"` Icon pgtype.Text `db:"icon" json:"icon"` AutoAssignRules []byte `db:"auto_assign_rules" json:"auto_assign_rules"` ViewSettings []byte `db:"view_settings" json:"view_settings"` } // Update collection func (q *Queries) UpdateCollection(ctx context.Context, arg UpdateCollectionParams) (Collections, error) { row := q.db.QueryRow(ctx, UpdateCollection, arg.ID, arg.Name, arg.Description, arg.Color, arg.Icon, arg.AutoAssignRules, arg.ViewSettings, ) var i Collections err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.Description, &i.Color, &i.Icon, &i.AutoAssignRules, &i.ViewSettings, &i.ShowOnDashboard, &i.QueryType, &i.Priority, &i.IsSystemCollection, &i.CreatedAt, ) return i, err } const UpdateDashboardPreferences = `-- name: UpdateDashboardPreferences :one UPDATE user_dashboard_preferences SET hidden_collections = $2, collection_order = $3, items_per_section = $4, updated_at = NOW() WHERE user_id = $1 AND library_id = $5 RETURNING id, user_id, library_id, hidden_collections, collection_order, items_per_section, created_at, updated_at ` type UpdateDashboardPreferencesParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` HiddenCollections []string `db:"hidden_collections" json:"hidden_collections"` CollectionOrder []string `db:"collection_order" json:"collection_order"` ItemsPerSection pgtype.Int4 `db:"items_per_section" json:"items_per_section"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` } func (q *Queries) UpdateDashboardPreferences(ctx context.Context, arg UpdateDashboardPreferencesParams) (UserDashboardPreferences, error) { row := q.db.QueryRow(ctx, UpdateDashboardPreferences, arg.UserID, arg.HiddenCollections, arg.CollectionOrder, arg.ItemsPerSection, arg.LibraryID, ) var i UserDashboardPreferences err := row.Scan( &i.ID, &i.UserID, &i.LibraryID, &i.HiddenCollections, &i.CollectionOrder, &i.ItemsPerSection, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDevice = `-- name: UpdateDevice :one UPDATE devices SET device_name = $2, sync_enabled = $3, auto_sync = $4, sync_frequency_minutes = $5, device_metadata = $6, updated_at = NOW() WHERE id = $1 RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` type UpdateDeviceParams struct { ID pgtype.UUID `db:"id" json:"id"` DeviceName string `db:"device_name" json:"device_name"` SyncEnabled pgtype.Bool `db:"sync_enabled" json:"sync_enabled"` AutoSync pgtype.Bool `db:"auto_sync" json:"auto_sync"` SyncFrequencyMinutes pgtype.Int4 `db:"sync_frequency_minutes" json:"sync_frequency_minutes"` DeviceMetadata []byte `db:"device_metadata" json:"device_metadata"` } func (q *Queries) UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (Devices, error) { row := q.db.QueryRow(ctx, UpdateDevice, arg.ID, arg.DeviceName, arg.SyncEnabled, arg.AutoSync, arg.SyncFrequencyMinutes, arg.DeviceMetadata, ) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDeviceAuthToken = `-- name: UpdateDeviceAuthToken :one UPDATE devices SET auth_token = $2, updated_at = NOW() WHERE id = $1 RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` type UpdateDeviceAuthTokenParams struct { ID pgtype.UUID `db:"id" json:"id"` AuthToken string `db:"auth_token" json:"auth_token"` } func (q *Queries) UpdateDeviceAuthToken(ctx context.Context, arg UpdateDeviceAuthTokenParams) (Devices, error) { row := q.db.QueryRow(ctx, UpdateDeviceAuthToken, arg.ID, arg.AuthToken) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDeviceCatalogAvailability = `-- name: UpdateDeviceCatalogAvailability :exec UPDATE device_catalogs SET available = $2 WHERE id = $1 ` type UpdateDeviceCatalogAvailabilityParams struct { ID pgtype.UUID `db:"id" json:"id"` Available pgtype.Bool `db:"available" json:"available"` } // Update device catalog availability func (q *Queries) UpdateDeviceCatalogAvailability(ctx context.Context, arg UpdateDeviceCatalogAvailabilityParams) error { _, err := q.db.Exec(ctx, UpdateDeviceCatalogAvailability, arg.ID, arg.Available) return err } const UpdateDeviceFileAlias = `-- name: UpdateDeviceFileAlias :one UPDATE device_file_aliases SET media_item_id = $2, file_sha256 = $3, confidence_score = $4, last_seen_at = NOW() WHERE id = $1 RETURNING id, media_item_id, device_id, file_path, file_sha256, confidence_score, last_seen_at ` type UpdateDeviceFileAliasParams struct { ID pgtype.UUID `db:"id" json:"id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` ConfidenceScore pgtype.Float8 `db:"confidence_score" json:"confidence_score"` } // Update device file alias func (q *Queries) UpdateDeviceFileAlias(ctx context.Context, arg UpdateDeviceFileAliasParams) (DeviceFileAliases, error) { row := q.db.QueryRow(ctx, UpdateDeviceFileAlias, arg.ID, arg.MediaItemID, arg.FileSha256, arg.ConfidenceScore, ) var i DeviceFileAliases err := row.Scan( &i.ID, &i.MediaItemID, &i.DeviceID, &i.FilePath, &i.FileSha256, &i.ConfidenceScore, &i.LastSeenAt, ) return i, err } const UpdateDeviceLastSeen = `-- name: UpdateDeviceLastSeen :one UPDATE devices SET last_seen = NOW(), updated_at = NOW() WHERE id = $1 RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` func (q *Queries) UpdateDeviceLastSeen(ctx context.Context, id pgtype.UUID) (Devices, error) { row := q.db.QueryRow(ctx, UpdateDeviceLastSeen, id) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDeviceLastSync = `-- name: UpdateDeviceLastSync :one UPDATE devices SET last_sync = NOW(), last_seen = NOW(), updated_at = NOW() WHERE id = $1 RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` func (q *Queries) UpdateDeviceLastSync(ctx context.Context, id pgtype.UUID) (Devices, error) { row := q.db.QueryRow(ctx, UpdateDeviceLastSync, id) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDeviceShelfMapping = `-- name: UpdateDeviceShelfMapping :one UPDATE device_shelf_mappings SET device_shelf_name = $2, sync_direction = $3 WHERE id = $1 RETURNING id, collection_id, device_id, device_shelf_name, sync_direction, created_at ` type UpdateDeviceShelfMappingParams struct { ID pgtype.UUID `db:"id" json:"id"` DeviceShelfName pgtype.Text `db:"device_shelf_name" json:"device_shelf_name"` SyncDirection pgtype.Text `db:"sync_direction" json:"sync_direction"` } // Update device shelf mapping func (q *Queries) UpdateDeviceShelfMapping(ctx context.Context, arg UpdateDeviceShelfMappingParams) (DeviceShelfMappings, error) { row := q.db.QueryRow(ctx, UpdateDeviceShelfMapping, arg.ID, arg.DeviceShelfName, arg.SyncDirection) var i DeviceShelfMappings err := row.Scan( &i.ID, &i.CollectionID, &i.DeviceID, &i.DeviceShelfName, &i.SyncDirection, &i.CreatedAt, ) return i, err } const UpdateDeviceSyncTimestamp = `-- name: UpdateDeviceSyncTimestamp :one UPDATE devices SET last_sync = NOW(), last_seen = NOW(), updated_at = NOW() WHERE id = $1 RETURNING id, user_id, device_name, device_type, device_identifier, auth_token, last_sync, last_seen, sync_enabled, auto_sync, sync_frequency_minutes, device_metadata, created_at, updated_at ` func (q *Queries) UpdateDeviceSyncTimestamp(ctx context.Context, id pgtype.UUID) (Devices, error) { row := q.db.QueryRow(ctx, UpdateDeviceSyncTimestamp, id) var i Devices err := row.Scan( &i.ID, &i.UserID, &i.DeviceName, &i.DeviceType, &i.DeviceIdentifier, &i.AuthToken, &i.LastSync, &i.LastSeen, &i.SyncEnabled, &i.AutoSync, &i.SyncFrequencyMinutes, &i.DeviceMetadata, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateDictionaryAccessed = `-- name: UpdateDictionaryAccessed :one UPDATE dictionary_cache SET accessed_at = NOW() WHERE word = $1 RETURNING id, word, definition, part_of_speech, example, etymology, created_at, accessed_at ` func (q *Queries) UpdateDictionaryAccessed(ctx context.Context, word string) (DictionaryCache, error) { row := q.db.QueryRow(ctx, UpdateDictionaryAccessed, word) var i DictionaryCache err := row.Scan( &i.ID, &i.Word, &i.Definition, &i.PartOfSpeech, &i.Example, &i.Etymology, &i.CreatedAt, &i.AccessedAt, ) return i, err } const UpdateEmail = `-- name: UpdateEmail :exec UPDATE users SET email = $2, updated_at = NOW() WHERE id = $1 ` type UpdateEmailParams struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` } func (q *Queries) UpdateEmail(ctx context.Context, arg UpdateEmailParams) error { _, err := q.db.Exec(ctx, UpdateEmail, arg.ID, arg.Email) return err } const UpdateKoboEntitlementRevision = `-- name: UpdateKoboEntitlementRevision :exec UPDATE kobo_entitlements SET revision_number = $3, updated_at = NOW() WHERE device_id = $1 AND entitlement_id = $2 ` type UpdateKoboEntitlementRevisionParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` } func (q *Queries) UpdateKoboEntitlementRevision(ctx context.Context, arg UpdateKoboEntitlementRevisionParams) error { _, err := q.db.Exec(ctx, UpdateKoboEntitlementRevision, arg.DeviceID, arg.EntitlementID, arg.RevisionNumber) return err } const UpdateKoboEntitlementStatus = `-- name: UpdateKoboEntitlementStatus :exec UPDATE kobo_entitlements SET book_status = $3, sync_status = 'synced', updated_at = NOW() WHERE device_id = $1 AND entitlement_id = $2 ` type UpdateKoboEntitlementStatusParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` EntitlementID string `db:"entitlement_id" json:"entitlement_id"` BookStatus pgtype.Text `db:"book_status" json:"book_status"` } func (q *Queries) UpdateKoboEntitlementStatus(ctx context.Context, arg UpdateKoboEntitlementStatusParams) error { _, err := q.db.Exec(ctx, UpdateKoboEntitlementStatus, arg.DeviceID, arg.EntitlementID, arg.BookStatus) return err } const UpdateKoboShelfBookPosition = `-- name: UpdateKoboShelfBookPosition :exec UPDATE kobo_shelves SET shelf_position = $3, last_synced_at = NOW() WHERE device_id = $1 AND media_item_id = $2 ` type UpdateKoboShelfBookPositionParams struct { DeviceID pgtype.UUID `db:"device_id" json:"device_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` ShelfPosition pgtype.Int4 `db:"shelf_position" json:"shelf_position"` } func (q *Queries) UpdateKoboShelfBookPosition(ctx context.Context, arg UpdateKoboShelfBookPositionParams) error { _, err := q.db.Exec(ctx, UpdateKoboShelfBookPosition, arg.DeviceID, arg.MediaItemID, arg.ShelfPosition) return err } const UpdateKoboShelfCollection = `-- name: UpdateKoboShelfCollection :one UPDATE kobo_shelves SET collection_id = $2, position_in_collection = $3, last_synced_at = NOW() WHERE id = $1 RETURNING id, device_id, media_item_id, shelf_name, shelf_position, added_at, last_synced_at, collection_id, position_in_collection ` type UpdateKoboShelfCollectionParams struct { ID pgtype.UUID `db:"id" json:"id"` CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"` PositionInCollection pgtype.Int4 `db:"position_in_collection" json:"position_in_collection"` } // Update Kobo shelves to support collections func (q *Queries) UpdateKoboShelfCollection(ctx context.Context, arg UpdateKoboShelfCollectionParams) (KoboShelves, error) { row := q.db.QueryRow(ctx, UpdateKoboShelfCollection, arg.ID, arg.CollectionID, arg.PositionInCollection) var i KoboShelves err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.ShelfName, &i.ShelfPosition, &i.AddedAt, &i.LastSyncedAt, &i.CollectionID, &i.PositionInCollection, ) return i, err } const UpdateLibrary = `-- name: UpdateLibrary :one UPDATE libraries SET name = $2, description = $3, updated_at = NOW() WHERE id = $1 RETURNING id, name, description, library_type_id, created_by_admin_id, created_at, updated_at ` type UpdateLibraryParams struct { ID pgtype.UUID `db:"id" json:"id"` Name string `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` } func (q *Queries) UpdateLibrary(ctx context.Context, arg UpdateLibraryParams) (Libraries, error) { row := q.db.QueryRow(ctx, UpdateLibrary, arg.ID, arg.Name, arg.Description) var i Libraries err := row.Scan( &i.ID, &i.Name, &i.Description, &i.LibraryTypeID, &i.CreatedByAdminID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateMediaBookmark = `-- name: UpdateMediaBookmark :one UPDATE media_bookmarks SET title = $2, notes = $3, position = $4, last_modified_at = NOW() WHERE id = $1 AND user_id = $5 RETURNING id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at ` type UpdateMediaBookmarkParams struct { ID pgtype.UUID `db:"id" json:"id"` Title string `db:"title" json:"title"` Notes pgtype.Text `db:"notes" json:"notes"` Position pgtype.Text `db:"position" json:"position"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) UpdateMediaBookmark(ctx context.Context, arg UpdateMediaBookmarkParams) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, UpdateMediaBookmark, arg.ID, arg.Title, arg.Notes, arg.Position, arg.UserID, ) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaBookmarkForSync = `-- name: UpdateMediaBookmarkForSync :one UPDATE media_bookmarks SET page_number = $2, chapter_number = $3, cfi_position = $4, title = $5, position = $6, notes = $7, percentage_location = $8, epubcfi_location = $9, chapter_reference = $10, last_modified_at = $11, last_modified_source = $12, device_sync_data = $13, created_at = created_at, deleted = FALSE, deleted_at = NULL WHERE id = $1 RETURNING id, media_item_id, user_id, page_number, chapter_number, cfi_position, title, position, notes, created_at, dedup_key, last_modified_at, last_modified_source, device_sync_data, percentage_location, epubcfi_location, chapter_reference, deleted, deleted_at ` type UpdateMediaBookmarkForSyncParams struct { ID pgtype.UUID `db:"id" json:"id"` PageNumber pgtype.Int4 `db:"page_number" json:"page_number"` ChapterNumber pgtype.Int4 `db:"chapter_number" json:"chapter_number"` CfiPosition pgtype.Text `db:"cfi_position" json:"cfi_position"` Title string `db:"title" json:"title"` Position pgtype.Text `db:"position" json:"position"` Notes pgtype.Text `db:"notes" json:"notes"` PercentageLocation pgtype.Float8 `db:"percentage_location" json:"percentage_location"` EpubcfiLocation pgtype.Text `db:"epubcfi_location" json:"epubcfi_location"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) UpdateMediaBookmarkForSync(ctx context.Context, arg UpdateMediaBookmarkForSyncParams) (MediaBookmarks, error) { row := q.db.QueryRow(ctx, UpdateMediaBookmarkForSync, arg.ID, arg.PageNumber, arg.ChapterNumber, arg.CfiPosition, arg.Title, arg.Position, arg.Notes, arg.PercentageLocation, arg.EpubcfiLocation, arg.ChapterReference, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaBookmarks err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.PageNumber, &i.ChapterNumber, &i.CfiPosition, &i.Title, &i.Position, &i.Notes, &i.CreatedAt, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.DeviceSyncData, &i.PercentageLocation, &i.EpubcfiLocation, &i.ChapterReference, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaHighlight = `-- name: UpdateMediaHighlight :one UPDATE media_highlights SET selection_text = $2, start_position = $3, end_position = $4, color = $5, note_id = $6, updated_at = NOW() WHERE id = $1 RETURNING id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at ` type UpdateMediaHighlightParams struct { ID pgtype.UUID `db:"id" json:"id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` NoteID pgtype.UUID `db:"note_id" json:"note_id"` } func (q *Queries) UpdateMediaHighlight(ctx context.Context, arg UpdateMediaHighlightParams) (MediaHighlights, error) { row := q.db.QueryRow(ctx, UpdateMediaHighlight, arg.ID, arg.SelectionText, arg.StartPosition, arg.EndPosition, arg.Color, arg.NoteID, ) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaHighlightForSync = `-- name: UpdateMediaHighlightForSync :one UPDATE media_highlights SET selection_text = $2, start_position = $3, end_position = $4, color = $5, note_text = $6, percentage_start = $7, percentage_end = $8, epubcfi_start = $9, epubcfi_end = $10, chapter_reference = $11, last_modified_at = $12, last_modified_source = $13, device_sync_data = $14, updated_at = NOW(), deleted = FALSE, deleted_at = NULL WHERE id = $1 RETURNING id, media_item_id, user_id, selection_text, start_position, end_position, color, note_id, created_at, updated_at, percentage_start, percentage_end, character_start, character_end, epubcfi_start, epubcfi_end, chapter_reference, paragraph_start, paragraph_end, panel_number, device_sync_data, dedup_key, last_modified_at, last_modified_source, note_text, deleted, deleted_at ` type UpdateMediaHighlightForSyncParams struct { ID pgtype.UUID `db:"id" json:"id"` SelectionText string `db:"selection_text" json:"selection_text"` StartPosition pgtype.Text `db:"start_position" json:"start_position"` EndPosition pgtype.Text `db:"end_position" json:"end_position"` Color pgtype.Text `db:"color" json:"color"` NoteText pgtype.Text `db:"note_text" json:"note_text"` PercentageStart pgtype.Float8 `db:"percentage_start" json:"percentage_start"` PercentageEnd pgtype.Float8 `db:"percentage_end" json:"percentage_end"` EpubcfiStart pgtype.Text `db:"epubcfi_start" json:"epubcfi_start"` EpubcfiEnd pgtype.Text `db:"epubcfi_end" json:"epubcfi_end"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) UpdateMediaHighlightForSync(ctx context.Context, arg UpdateMediaHighlightForSyncParams) (MediaHighlights, error) { row := q.db.QueryRow(ctx, UpdateMediaHighlightForSync, arg.ID, arg.SelectionText, arg.StartPosition, arg.EndPosition, arg.Color, arg.NoteText, arg.PercentageStart, arg.PercentageEnd, arg.EpubcfiStart, arg.EpubcfiEnd, arg.ChapterReference, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaHighlights err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.SelectionText, &i.StartPosition, &i.EndPosition, &i.Color, &i.NoteID, &i.CreatedAt, &i.UpdatedAt, &i.PercentageStart, &i.PercentageEnd, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiStart, &i.EpubcfiEnd, &i.ChapterReference, &i.ParagraphStart, &i.ParagraphEnd, &i.PanelNumber, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.NoteText, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaItem = `-- 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, tags_search = $10, asin = $11, date_published = $12, publisher = $13, contributors = $14, contributors_search = $15, language = $16, edition = $17, page_count = $18, genre = $19, copyright_year = $20, goodreads_id = $21, openlibrary_id = $22, google_books_id = $23, manga_type = $24, reading_direction = $25, series_count = $26, volume = $27, imprint = $28, age_rating = $29, web_url = $30, metadata_notes = $31, community_rating = $32, story_arc = $33, is_black_and_white = $34, alternate_info = $35, scan_information = $36, summary = $37, updated_at = NOW() WHERE id = $1 RETURNING id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence ` type UpdateMediaItemParams struct { ID pgtype.UUID `db:"id" json:"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"` 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 []string `db:"tags" json:"tags"` TagsSearch []string `db:"tags_search" json:"tags_search"` Asin pgtype.Text `db:"asin" json:"asin"` DatePublished pgtype.Date `db:"date_published" json:"date_published"` Publisher pgtype.Text `db:"publisher" json:"publisher"` Contributors []string `db:"contributors" json:"contributors"` ContributorsSearch []string `db:"contributors_search" json:"contributors_search"` Language pgtype.Text `db:"language" json:"language"` Edition pgtype.Text `db:"edition" json:"edition"` PageCount pgtype.Int4 `db:"page_count" json:"page_count"` Genre pgtype.Text `db:"genre" json:"genre"` CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"` GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"` OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"` GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"` MangaType pgtype.Text `db:"manga_type" json:"manga_type"` ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"` SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"` Volume pgtype.Int4 `db:"volume" json:"volume"` Imprint pgtype.Text `db:"imprint" json:"imprint"` AgeRating pgtype.Text `db:"age_rating" json:"age_rating"` WebUrl pgtype.Text `db:"web_url" json:"web_url"` MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"` CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"` StoryArc pgtype.Text `db:"story_arc" json:"story_arc"` IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"` AlternateInfo []byte `db:"alternate_info" json:"alternate_info"` ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"` Summary pgtype.Text `db:"summary" json:"summary"` } func (q *Queries) UpdateMediaItem(ctx context.Context, arg UpdateMediaItemParams) (MediaItems, error) { row := q.db.QueryRow(ctx, UpdateMediaItem, arg.ID, arg.Title, arg.Author, arg.Isbn, arg.Description, arg.CoverImagePath, arg.Series, arg.SeriesNumber, arg.Tags, arg.TagsSearch, arg.Asin, arg.DatePublished, arg.Publisher, arg.Contributors, arg.ContributorsSearch, arg.Language, arg.Edition, arg.PageCount, arg.Genre, arg.CopyrightYear, arg.GoodreadsID, arg.OpenlibraryID, arg.GoogleBooksID, arg.MangaType, arg.ReadingDirection, arg.SeriesCount, arg.Volume, arg.Imprint, arg.AgeRating, arg.WebUrl, arg.MetadataNotes, arg.CommunityRating, arg.StoryArc, arg.IsBlackAndWhite, arg.AlternateInfo, arg.ScanInformation, arg.Summary, ) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const UpdateMediaItemChapterMetadata = `-- name: UpdateMediaItemChapterMetadata :one UPDATE media_items SET chapter_metadata = $2, updated_at = NOW() WHERE id = $1 RETURNING id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence ` type UpdateMediaItemChapterMetadataParams struct { ID pgtype.UUID `db:"id" json:"id"` ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"` } func (q *Queries) UpdateMediaItemChapterMetadata(ctx context.Context, arg UpdateMediaItemChapterMetadataParams) (MediaItems, error) { row := q.db.QueryRow(ctx, UpdateMediaItemChapterMetadata, arg.ID, arg.ChapterMetadata) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const UpdateMediaItemFormat = `-- name: UpdateMediaItemFormat :one UPDATE media_item_formats SET file_path = $2, file_sha256 = $3, file_size_bytes = $4, mime_type = $5 WHERE id = $1 RETURNING id, media_item_id, format_type, file_path, file_sha256, file_size_bytes, mime_type, created_at, converted_from_format_id ` type UpdateMediaItemFormatParams struct { ID pgtype.UUID `db:"id" json:"id"` FilePath pgtype.Text `db:"file_path" json:"file_path"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` FileSizeBytes pgtype.Int8 `db:"file_size_bytes" json:"file_size_bytes"` MimeType pgtype.Text `db:"mime_type" json:"mime_type"` } // Update media item format func (q *Queries) UpdateMediaItemFormat(ctx context.Context, arg UpdateMediaItemFormatParams) (MediaItemFormats, error) { row := q.db.QueryRow(ctx, UpdateMediaItemFormat, arg.ID, arg.FilePath, arg.FileSha256, arg.FileSizeBytes, arg.MimeType, ) var i MediaItemFormats err := row.Scan( &i.ID, &i.MediaItemID, &i.FormatType, &i.FilePath, &i.FileSha256, &i.FileSizeBytes, &i.MimeType, &i.CreatedAt, &i.ConvertedFromFormatID, ) return i, err } const UpdateMediaItemFormatGroup = `-- name: UpdateMediaItemFormatGroup :exec UPDATE media_items SET format_group = $2, format_mimetype = $3, is_reflowable = $4, has_fixed_layout = $5, total_characters = $6, chapter_count = $7, updated_at = NOW() WHERE id = $1 ` type UpdateMediaItemFormatGroupParams struct { ID pgtype.UUID `db:"id" json:"id"` FormatGroup string `db:"format_group" json:"format_group"` FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"` IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"` HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"` TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"` ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"` } // ============================================ // FORMAT DETECTION & PROGRESS // ============================================ // Update media item format group information func (q *Queries) UpdateMediaItemFormatGroup(ctx context.Context, arg UpdateMediaItemFormatGroupParams) error { _, err := q.db.Exec(ctx, UpdateMediaItemFormatGroup, arg.ID, arg.FormatGroup, arg.FormatMimetype, arg.IsReflowable, arg.HasFixedLayout, arg.TotalCharacters, arg.ChapterCount, ) return err } const UpdateMediaItemIdentifiers = `-- name: UpdateMediaItemIdentifiers :one UPDATE media_items SET file_sha256 = $2, opf_identifier = $3, opf_uuid = $4, hash_confidence = $5, updated_at = NOW() WHERE id = $1 RETURNING id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence ` type UpdateMediaItemIdentifiersParams struct { ID pgtype.UUID `db:"id" json:"id"` FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"` OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"` OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"` HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"` } // Media Items Admin Operations // ============================================ // UNIVERSAL BOOK IDENTIFIERS // ============================================ // Update media item with universal identifiers func (q *Queries) UpdateMediaItemIdentifiers(ctx context.Context, arg UpdateMediaItemIdentifiersParams) (MediaItems, error) { row := q.db.QueryRow(ctx, UpdateMediaItemIdentifiers, arg.ID, arg.FileSha256, arg.OpfIdentifier, arg.OpfUuid, arg.HashConfidence, ) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const UpdateMediaItemKoboMetadata = `-- name: UpdateMediaItemKoboMetadata :one UPDATE media_items SET entitlement_id = $2, kobo_content_id = $3, revision_number = COALESCE($4, revision_number) + 1, kobo_metadata = $5, updated_at = NOW() WHERE id = $1 RETURNING id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence ` type UpdateMediaItemKoboMetadataParams struct { ID pgtype.UUID `db:"id" json:"id"` EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"` KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"` RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"` KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"` } func (q *Queries) UpdateMediaItemKoboMetadata(ctx context.Context, arg UpdateMediaItemKoboMetadataParams) (MediaItems, error) { row := q.db.QueryRow(ctx, UpdateMediaItemKoboMetadata, arg.ID, arg.EntitlementID, arg.KoboContentID, arg.RevisionNumber, arg.KoboMetadata, ) var i MediaItems err := row.Scan( &i.ID, &i.LibraryID, &i.Title, &i.Author, &i.Isbn, &i.Description, &i.FilePath, &i.FileSize, &i.MimeType, &i.CoverImagePath, &i.Series, &i.SeriesNumber, &i.Tags, &i.Asin, &i.DatePublished, &i.Publisher, &i.Contributors, &i.Language, &i.Edition, &i.PageCount, &i.Genre, &i.CopyrightYear, &i.GoodreadsID, &i.OpenlibraryID, &i.GoogleBooksID, &i.AddedByAdminID, &i.CreatedAt, &i.ImportedAt, &i.UpdatedAt, &i.FormatGroup, &i.FormatMimetype, &i.IsReflowable, &i.HasFixedLayout, &i.TotalCharacters, &i.ChapterCount, &i.EntitlementID, &i.RevisionNumber, &i.KoboContentID, &i.KoboMetadata, &i.MangaType, &i.ReadingDirection, &i.SeriesCount, &i.Volume, &i.Imprint, &i.AgeRating, &i.WebUrl, &i.StoryArc, &i.IsBlackAndWhite, &i.MetadataNotes, &i.CommunityRating, &i.AlternateInfo, &i.ScanInformation, &i.Summary, &i.ChapterMetadata, &i.LibraryTypeName, &i.TagsSearch, &i.ContributorsSearch, &i.FileSha256, &i.OpfIdentifier, &i.OpfUuid, &i.HashConfidence, ) return i, err } const UpdateMediaNote = `-- name: UpdateMediaNote :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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at ` type UpdateMediaNoteParams struct { ID pgtype.UUID `db:"id" json:"id"` Content string `db:"content" json:"content"` Position pgtype.Text `db:"position" json:"position"` } func (q *Queries) UpdateMediaNote(ctx context.Context, arg UpdateMediaNoteParams) (MediaNotes, error) { row := q.db.QueryRow(ctx, UpdateMediaNote, arg.ID, arg.Content, arg.Position) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaNoteForSync = `-- name: UpdateMediaNoteForSync :one UPDATE media_notes SET content = $2, position = $3, percentage_location = $4, character_start = $5, character_end = $6, epubcfi_location = $7, chapter_reference = $8, paragraph_reference = $9, last_modified_at = $10, last_modified_source = $11, device_sync_data = $12, updated_at = NOW(), deleted = FALSE, deleted_at = NULL 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, dedup_key, last_modified_at, last_modified_source, deleted, deleted_at ` type UpdateMediaNoteForSyncParams struct { ID pgtype.UUID `db:"id" json:"id"` Content string `db:"content" json:"content"` Position pgtype.Text `db:"position" json:"position"` PercentageLocation pgtype.Float8 `db:"percentage_location" json:"percentage_location"` CharacterStart pgtype.Int4 `db:"character_start" json:"character_start"` CharacterEnd pgtype.Int4 `db:"character_end" json:"character_end"` EpubcfiLocation pgtype.Text `db:"epubcfi_location" json:"epubcfi_location"` ChapterReference pgtype.Int4 `db:"chapter_reference" json:"chapter_reference"` ParagraphReference pgtype.Int4 `db:"paragraph_reference" json:"paragraph_reference"` LastModifiedAt pgtype.Timestamptz `db:"last_modified_at" json:"last_modified_at"` LastModifiedSource pgtype.Text `db:"last_modified_source" json:"last_modified_source"` DeviceSyncData []byte `db:"device_sync_data" json:"device_sync_data"` } func (q *Queries) UpdateMediaNoteForSync(ctx context.Context, arg UpdateMediaNoteForSyncParams) (MediaNotes, error) { row := q.db.QueryRow(ctx, UpdateMediaNoteForSync, arg.ID, arg.Content, arg.Position, arg.PercentageLocation, arg.CharacterStart, arg.CharacterEnd, arg.EpubcfiLocation, arg.ChapterReference, arg.ParagraphReference, arg.LastModifiedAt, arg.LastModifiedSource, arg.DeviceSyncData, ) var i MediaNotes err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Content, &i.Position, &i.CreatedAt, &i.UpdatedAt, &i.PercentageLocation, &i.CharacterStart, &i.CharacterEnd, &i.EpubcfiLocation, &i.ChapterReference, &i.ParagraphReference, &i.DeviceSyncData, &i.DedupKey, &i.LastModifiedAt, &i.LastModifiedSource, &i.Deleted, &i.DeletedAt, ) return i, err } const UpdateMediaRating = `-- name: UpdateMediaRating :one UPDATE media_ratings SET rating = $3, updated_at = NOW() WHERE media_item_id = $1 AND user_id = $2 RETURNING id, media_item_id, user_id, rating, created_at, updated_at ` type UpdateMediaRatingParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Rating int32 `db:"rating" json:"rating"` } func (q *Queries) UpdateMediaRating(ctx context.Context, arg UpdateMediaRatingParams) (MediaRatings, error) { row := q.db.QueryRow(ctx, UpdateMediaRating, arg.MediaItemID, arg.UserID, arg.Rating) var i MediaRatings err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.Rating, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdatePassword = `-- name: UpdatePassword :exec UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1 ` type UpdatePasswordParams struct { ID pgtype.UUID `db:"id" json:"id"` PasswordHash string `db:"password_hash" json:"password_hash"` } func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error { _, err := q.db.Exec(ctx, UpdatePassword, arg.ID, arg.PasswordHash) return err } const UpdateReadingProgress = `-- name: UpdateReadingProgress :one INSERT INTO reading_progress (media_item_id, user_id, current_page, total_pages, last_read_at) VALUES ($1, $2, $3, $4, NOW()) ON CONFLICT (media_item_id, user_id) DO UPDATE SET current_page = EXCLUDED.current_page, total_pages = EXCLUDED.total_pages, last_read_at = NOW() RETURNING id, media_item_id, user_id, current_page, total_pages, last_read_at, percentage, character_offset, epubcfi, context_text, chapter, chapter_progress, viewport_x, viewport_y, zoom_level, scroll_position_x, scroll_position_y, panel_number, reading_mode, last_sync_device, last_sync_source, last_sync_timestamp, conflict_detected, conflict_resolved ` type UpdateReadingProgressParams struct { 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"` } func (q *Queries) UpdateReadingProgress(ctx context.Context, arg UpdateReadingProgressParams) (ReadingProgress, error) { row := q.db.QueryRow(ctx, UpdateReadingProgress, arg.MediaItemID, arg.UserID, arg.CurrentPage, arg.TotalPages, ) var i ReadingProgress err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.CurrentPage, &i.TotalPages, &i.LastReadAt, &i.Percentage, &i.CharacterOffset, &i.Epubcfi, &i.ContextText, &i.Chapter, &i.ChapterProgress, &i.ViewportX, &i.ViewportY, &i.ZoomLevel, &i.ScrollPositionX, &i.ScrollPositionY, &i.PanelNumber, &i.ReadingMode, &i.LastSyncDevice, &i.LastSyncSource, &i.LastSyncTimestamp, &i.ConflictDetected, &i.ConflictResolved, ) return i, err } const UpdateReadingSpeed = `-- name: UpdateReadingSpeed :one UPDATE reading_speed SET pages_per_minute = $3, pages_read = pages_read + $4, total_reading_minutes = total_reading_minutes + $5, last_read_at = $6, updated_at = NOW() WHERE user_id = $1 AND media_item_id = $2 RETURNING id, user_id, media_item_id, words_per_minute, pages_per_minute, pages_read, total_reading_minutes, last_read_at, updated_at ` type UpdateReadingSpeedParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` PagesPerMinute pgtype.Float4 `db:"pages_per_minute" json:"pages_per_minute"` PagesRead pgtype.Int4 `db:"pages_read" json:"pages_read"` TotalReadingMinutes pgtype.Float4 `db:"total_reading_minutes" json:"total_reading_minutes"` LastReadAt pgtype.Timestamptz `db:"last_read_at" json:"last_read_at"` } func (q *Queries) UpdateReadingSpeed(ctx context.Context, arg UpdateReadingSpeedParams) (ReadingSpeed, error) { row := q.db.QueryRow(ctx, UpdateReadingSpeed, arg.UserID, arg.MediaItemID, arg.PagesPerMinute, arg.PagesRead, arg.TotalReadingMinutes, arg.LastReadAt, ) var i ReadingSpeed err := row.Scan( &i.ID, &i.UserID, &i.MediaItemID, &i.WordsPerMinute, &i.PagesPerMinute, &i.PagesRead, &i.TotalReadingMinutes, &i.LastReadAt, &i.UpdatedAt, ) return i, err } const UpdateSavedFilter = `-- name: UpdateSavedFilter :one UPDATE saved_filters SET name = $1, filters = $2, updated_at = NOW() WHERE id = $3 AND user_id = $4 RETURNING id, user_id, name, resource_type, filters, created_at, updated_at ` type UpdateSavedFilterParams struct { Name string `db:"name" json:"name"` Filters []byte `db:"filters" json:"filters"` ID pgtype.UUID `db:"id" json:"id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` } func (q *Queries) UpdateSavedFilter(ctx context.Context, arg UpdateSavedFilterParams) (SavedFilters, error) { row := q.db.QueryRow(ctx, UpdateSavedFilter, arg.Name, arg.Filters, arg.ID, arg.UserID, ) var i SavedFilters err := row.Scan( &i.ID, &i.UserID, &i.Name, &i.ResourceType, &i.Filters, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpdateSyncQueueItemStatus = `-- name: UpdateSyncQueueItemStatus :one UPDATE sync_queue SET status = $2, attempts = attempts + 1, error_message = $3, processed_at = CASE WHEN $2::varchar = 'completed' THEN NOW() ELSE NULL END WHERE id = $1 RETURNING id, device_id, media_item_id, sync_type, sync_data, priority, attempts, max_attempts, status, error_message, created_at, processed_at ` type UpdateSyncQueueItemStatusParams struct { ID pgtype.UUID `db:"id" json:"id"` Status pgtype.Text `db:"status" json:"status"` ErrorMessage pgtype.Text `db:"error_message" json:"error_message"` } func (q *Queries) UpdateSyncQueueItemStatus(ctx context.Context, arg UpdateSyncQueueItemStatusParams) (SyncQueue, error) { row := q.db.QueryRow(ctx, UpdateSyncQueueItemStatus, arg.ID, arg.Status, arg.ErrorMessage) var i SyncQueue err := row.Scan( &i.ID, &i.DeviceID, &i.MediaItemID, &i.SyncType, &i.SyncData, &i.Priority, &i.Attempts, &i.MaxAttempts, &i.Status, &i.ErrorMessage, &i.CreatedAt, &i.ProcessedAt, ) return i, err } const UpdateSystemSetting = `-- name: UpdateSystemSetting :exec UPDATE system_settings SET setting_value = $2, updated_at = NOW() WHERE setting_key = $1 ` type UpdateSystemSettingParams struct { SettingKey string `db:"setting_key" json:"setting_key"` SettingValue string `db:"setting_value" json:"setting_value"` } func (q *Queries) UpdateSystemSetting(ctx context.Context, arg UpdateSystemSettingParams) error { _, err := q.db.Exec(ctx, UpdateSystemSetting, arg.SettingKey, arg.SettingValue) return err } const UpdateUniversalProgress = `-- name: UpdateUniversalProgress :one INSERT INTO reading_progress ( media_item_id, user_id, percentage, character_offset, epubcfi, context_text, chapter, chapter_progress, viewport_x, viewport_y, zoom_level, scroll_position_x, scroll_position_y, panel_number, reading_mode, last_sync_device, last_sync_source, last_sync_timestamp, current_page, total_pages, last_read_at ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, NOW(), $18, $19, NOW() ) ON CONFLICT (media_item_id, user_id) DO UPDATE SET percentage = EXCLUDED.percentage, character_offset = EXCLUDED.character_offset, epubcfi = EXCLUDED.epubcfi, context_text = EXCLUDED.context_text, chapter = EXCLUDED.chapter, chapter_progress = EXCLUDED.chapter_progress, viewport_x = EXCLUDED.viewport_x, viewport_y = EXCLUDED.viewport_y, zoom_level = EXCLUDED.zoom_level, scroll_position_x = EXCLUDED.scroll_position_x, scroll_position_y = EXCLUDED.scroll_position_y, panel_number = EXCLUDED.panel_number, reading_mode = EXCLUDED.reading_mode, last_sync_device = EXCLUDED.last_sync_device, last_sync_source = EXCLUDED.last_sync_source, last_sync_timestamp = EXCLUDED.last_sync_timestamp, current_page = EXCLUDED.current_page, total_pages = EXCLUDED.total_pages, last_read_at = NOW() RETURNING id, media_item_id, user_id, current_page, total_pages, last_read_at, percentage, character_offset, epubcfi, context_text, chapter, chapter_progress, viewport_x, viewport_y, zoom_level, scroll_position_x, scroll_position_y, panel_number, reading_mode, last_sync_device, last_sync_source, last_sync_timestamp, conflict_detected, conflict_resolved ` type UpdateUniversalProgressParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` UserID pgtype.UUID `db:"user_id" json:"user_id"` Percentage pgtype.Float8 `db:"percentage" json:"percentage"` CharacterOffset pgtype.Int8 `db:"character_offset" json:"character_offset"` Epubcfi pgtype.Text `db:"epubcfi" json:"epubcfi"` ContextText pgtype.Text `db:"context_text" json:"context_text"` Chapter pgtype.Int4 `db:"chapter" json:"chapter"` ChapterProgress pgtype.Float8 `db:"chapter_progress" json:"chapter_progress"` ViewportX pgtype.Float8 `db:"viewport_x" json:"viewport_x"` ViewportY pgtype.Float8 `db:"viewport_y" json:"viewport_y"` ZoomLevel pgtype.Float8 `db:"zoom_level" json:"zoom_level"` ScrollPositionX pgtype.Float8 `db:"scroll_position_x" json:"scroll_position_x"` ScrollPositionY pgtype.Float8 `db:"scroll_position_y" json:"scroll_position_y"` PanelNumber pgtype.Int4 `db:"panel_number" json:"panel_number"` ReadingMode pgtype.Text `db:"reading_mode" json:"reading_mode"` LastSyncDevice pgtype.Text `db:"last_sync_device" json:"last_sync_device"` LastSyncSource pgtype.Text `db:"last_sync_source" json:"last_sync_source"` CurrentPage pgtype.Int4 `db:"current_page" json:"current_page"` TotalPages pgtype.Int4 `db:"total_pages" json:"total_pages"` } // Update universal progress func (q *Queries) UpdateUniversalProgress(ctx context.Context, arg UpdateUniversalProgressParams) (ReadingProgress, error) { row := q.db.QueryRow(ctx, UpdateUniversalProgress, arg.MediaItemID, arg.UserID, arg.Percentage, arg.CharacterOffset, arg.Epubcfi, arg.ContextText, arg.Chapter, arg.ChapterProgress, arg.ViewportX, arg.ViewportY, arg.ZoomLevel, arg.ScrollPositionX, arg.ScrollPositionY, arg.PanelNumber, arg.ReadingMode, arg.LastSyncDevice, arg.LastSyncSource, arg.CurrentPage, arg.TotalPages, ) var i ReadingProgress err := row.Scan( &i.ID, &i.MediaItemID, &i.UserID, &i.CurrentPage, &i.TotalPages, &i.LastReadAt, &i.Percentage, &i.CharacterOffset, &i.Epubcfi, &i.ContextText, &i.Chapter, &i.ChapterProgress, &i.ViewportX, &i.ViewportY, &i.ZoomLevel, &i.ScrollPositionX, &i.ScrollPositionY, &i.PanelNumber, &i.ReadingMode, &i.LastSyncDevice, &i.LastSyncSource, &i.LastSyncTimestamp, &i.ConflictDetected, &i.ConflictResolved, ) return i, err } const UpdateUserMaxDevices = `-- name: UpdateUserMaxDevices :one UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1 RETURNING id, email, username, password_hash, first_name, last_name, role, theme, max_devices, created_at, timezone, updated_at ` type UpdateUserMaxDevicesParams struct { ID pgtype.UUID `db:"id" json:"id"` MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"` } func (q *Queries) UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDevicesParams) (Users, error) { row := q.db.QueryRow(ctx, UpdateUserMaxDevices, arg.ID, arg.MaxDevices) var i Users err := row.Scan( &i.ID, &i.Email, &i.Username, &i.PasswordHash, &i.FirstName, &i.LastName, &i.Role, &i.Theme, &i.MaxDevices, &i.CreatedAt, &i.Timezone, &i.UpdatedAt, ) return i, err } const UpdateUserProfile = `-- name: UpdateUserProfile :exec UPDATE users SET first_name = $2, last_name = $3, updated_at = NOW() WHERE id = $1 ` type UpdateUserProfileParams struct { ID pgtype.UUID `db:"id" json:"id"` FirstName pgtype.Text `db:"first_name" json:"first_name"` LastName pgtype.Text `db:"last_name" json:"last_name"` } func (q *Queries) UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) error { _, err := q.db.Exec(ctx, UpdateUserProfile, arg.ID, arg.FirstName, arg.LastName) return err } const UpdateUserRole = `-- name: UpdateUserRole :one UPDATE users SET role = $2, updated_at = NOW() WHERE id = $1 RETURNING id, email, username, role ` type UpdateUserRoleParams struct { ID pgtype.UUID `db:"id" json:"id"` Role string `db:"role" json:"role"` } type UpdateUserRoleRow struct { ID pgtype.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` Username string `db:"username" json:"username"` Role string `db:"role" json:"role"` } func (q *Queries) UpdateUserRole(ctx context.Context, arg UpdateUserRoleParams) (UpdateUserRoleRow, error) { row := q.db.QueryRow(ctx, UpdateUserRole, arg.ID, arg.Role) var i UpdateUserRoleRow err := row.Scan( &i.ID, &i.Email, &i.Username, &i.Role, ) return i, err } const UpdateUserTheme = `-- name: UpdateUserTheme :exec UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1 ` type UpdateUserThemeParams struct { ID pgtype.UUID `db:"id" json:"id"` Theme pgtype.Text `db:"theme" json:"theme"` } func (q *Queries) UpdateUserTheme(ctx context.Context, arg UpdateUserThemeParams) error { _, err := q.db.Exec(ctx, UpdateUserTheme, arg.ID, arg.Theme) return err } const UpdateUserTimezone = `-- name: UpdateUserTimezone :exec UPDATE users SET timezone = $2, updated_at = NOW() WHERE id = $1 ` type UpdateUserTimezoneParams struct { ID pgtype.UUID `db:"id" json:"id"` Timezone pgtype.Text `db:"timezone" json:"timezone"` } func (q *Queries) UpdateUserTimezone(ctx context.Context, arg UpdateUserTimezoneParams) error { _, err := q.db.Exec(ctx, UpdateUserTimezone, arg.ID, arg.Timezone) return err } const UpdateUsername = `-- name: UpdateUsername :exec UPDATE users SET username = $2, updated_at = NOW() WHERE id = $1 ` type UpdateUsernameParams struct { ID pgtype.UUID `db:"id" json:"id"` Username string `db:"username" json:"username"` } func (q *Queries) UpdateUsername(ctx context.Context, arg UpdateUsernameParams) error { _, err := q.db.Exec(ctx, UpdateUsername, arg.ID, arg.Username) return err } const UpsertDashboardPreferences = `-- name: UpsertDashboardPreferences :one INSERT INTO user_dashboard_preferences (user_id, library_id, hidden_collections, collection_order, items_per_section) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (user_id, library_id) DO UPDATE SET hidden_collections = EXCLUDED.hidden_collections, collection_order = EXCLUDED.collection_order, items_per_section = EXCLUDED.items_per_section, updated_at = NOW() RETURNING id, user_id, library_id, hidden_collections, collection_order, items_per_section, created_at, updated_at ` type UpsertDashboardPreferencesParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` LibraryID pgtype.UUID `db:"library_id" json:"library_id"` HiddenCollections []string `db:"hidden_collections" json:"hidden_collections"` CollectionOrder []string `db:"collection_order" json:"collection_order"` ItemsPerSection pgtype.Int4 `db:"items_per_section" json:"items_per_section"` } func (q *Queries) UpsertDashboardPreferences(ctx context.Context, arg UpsertDashboardPreferencesParams) (UserDashboardPreferences, error) { row := q.db.QueryRow(ctx, UpsertDashboardPreferences, arg.UserID, arg.LibraryID, arg.HiddenCollections, arg.CollectionOrder, arg.ItemsPerSection, ) var i UserDashboardPreferences err := row.Scan( &i.ID, &i.UserID, &i.LibraryID, &i.HiddenCollections, &i.CollectionOrder, &i.ItemsPerSection, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpsertPanelData = `-- name: UpsertPanelData :one INSERT INTO panel_data (media_item_id, page_number, detection_method, panels) VALUES ($1, $2, $3, $4) ON CONFLICT (media_item_id, page_number) DO UPDATE SET detection_method = EXCLUDED.detection_method, panels = EXCLUDED.panels, updated_at = NOW() RETURNING id, media_item_id, page_number, detection_method, panels, created_at, updated_at ` type UpsertPanelDataParams struct { MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"` PageNumber int32 `db:"page_number" json:"page_number"` DetectionMethod string `db:"detection_method" json:"detection_method"` Panels []byte `db:"panels" json:"panels"` } func (q *Queries) UpsertPanelData(ctx context.Context, arg UpsertPanelDataParams) (PanelData, error) { row := q.db.QueryRow(ctx, UpsertPanelData, arg.MediaItemID, arg.PageNumber, arg.DetectionMethod, arg.Panels, ) var i PanelData err := row.Scan( &i.ID, &i.MediaItemID, &i.PageNumber, &i.DetectionMethod, &i.Panels, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const UpsertReaderSettings = `-- name: UpsertReaderSettings :one INSERT INTO reader_settings (user_id, setting_key, setting_value) VALUES ($1, 'reader_settings', $2) ON CONFLICT (user_id, setting_key) DO UPDATE SET setting_value = EXCLUDED.setting_value, updated_at = NOW() RETURNING id, user_id, setting_key, setting_value, updated_at ` type UpsertReaderSettingsParams struct { UserID pgtype.UUID `db:"user_id" json:"user_id"` SettingValue []byte `db:"setting_value" json:"setting_value"` } func (q *Queries) UpsertReaderSettings(ctx context.Context, arg UpsertReaderSettingsParams) (ReaderSettings, error) { row := q.db.QueryRow(ctx, UpsertReaderSettings, arg.UserID, arg.SettingValue) var i ReaderSettings err := row.Scan( &i.ID, &i.UserID, &i.SettingKey, &i.SettingValue, &i.UpdatedAt, ) return i, err } const UpsertSystemSetting = `-- name: UpsertSystemSetting :one INSERT INTO system_settings (setting_key, setting_value, description, setting_type, min_value, max_value, requires_restart, category) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (setting_key) DO UPDATE SET setting_value = EXCLUDED.setting_value, description = EXCLUDED.description, setting_type = EXCLUDED.setting_type, min_value = EXCLUDED.min_value, max_value = EXCLUDED.max_value, requires_restart = EXCLUDED.requires_restart, category = EXCLUDED.category, updated_at = NOW() RETURNING id, setting_key, setting_value, description, updated_at, setting_type, min_value, max_value, requires_restart, category ` type UpsertSystemSettingParams struct { SettingKey string `db:"setting_key" json:"setting_key"` SettingValue string `db:"setting_value" json:"setting_value"` Description pgtype.Text `db:"description" json:"description"` SettingType pgtype.Text `db:"setting_type" json:"setting_type"` MinValue pgtype.Text `db:"min_value" json:"min_value"` MaxValue pgtype.Text `db:"max_value" json:"max_value"` RequiresRestart pgtype.Bool `db:"requires_restart" json:"requires_restart"` Category pgtype.Text `db:"category" json:"category"` } func (q *Queries) UpsertSystemSetting(ctx context.Context, arg UpsertSystemSettingParams) (SystemSettings, error) { row := q.db.QueryRow(ctx, UpsertSystemSetting, arg.SettingKey, arg.SettingValue, arg.Description, arg.SettingType, arg.MinValue, arg.MaxValue, arg.RequiresRestart, arg.Category, ) var i SystemSettings err := row.Scan( &i.ID, &i.SettingKey, &i.SettingValue, &i.Description, &i.UpdatedAt, &i.SettingType, &i.MinValue, &i.MaxValue, &i.RequiresRestart, &i.Category, ) return i, err }