node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
# db
Should only import `models` internal package.
### 🚫 Do not
- Do not define **models** here.

View File

@@ -0,0 +1,59 @@
package db
import (
"errors"
"seanime/internal/database/models"
"gorm.io/gorm/clause"
)
var accountCache *models.Account
func (db *Database) UpsertAccount(acc *models.Account) (*models.Account, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(acc).Error
if err != nil {
db.Logger.Error().Err(err).Msg("Failed to save account in the database")
return nil, err
}
if acc.Username != "" {
accountCache = acc
} else {
accountCache = nil
}
return acc, nil
}
func (db *Database) GetAccount() (*models.Account, error) {
if accountCache != nil {
return accountCache, nil
}
var acc models.Account
err := db.gormdb.Last(&acc).Error
if err != nil {
return nil, err
}
if acc.Username == "" || acc.Token == "" || acc.Viewer == nil {
return nil, errors.New("account not found")
}
accountCache = &acc
return &acc, err
}
// GetAnilistToken retrieves the AniList token from the account or returns an empty string
func (db *Database) GetAnilistToken() string {
acc, err := db.GetAccount()
if err != nil {
return ""
}
return acc.Token
}

View File

@@ -0,0 +1,57 @@
package db
import (
"seanime/internal/database/models"
)
func (db *Database) GetAutoDownloaderItems() ([]*models.AutoDownloaderItem, error) {
var res []*models.AutoDownloaderItem
err := db.gormdb.Find(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
func (db *Database) GetAutoDownloaderItem(id uint) (*models.AutoDownloaderItem, error) {
var res models.AutoDownloaderItem
err := db.gormdb.First(&res, id).Error
if err != nil {
return nil, err
}
return &res, nil
}
func (db *Database) GetAutoDownloaderItemByMediaId(mId int) ([]*models.AutoDownloaderItem, error) {
var res []*models.AutoDownloaderItem
err := db.gormdb.Where("media_id = ?", mId).Find(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
func (db *Database) InsertAutoDownloaderItem(item *models.AutoDownloaderItem) error {
err := db.gormdb.Create(item).Error
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteAutoDownloaderItem(id uint) error {
return db.gormdb.Delete(&models.AutoDownloaderItem{}, id).Error
}
// DeleteDownloadedAutoDownloaderItems will delete all the downloaded queued items from the database.
func (db *Database) DeleteDownloadedAutoDownloaderItems() error {
return db.gormdb.Where("downloaded = ?", true).Delete(&models.AutoDownloaderItem{}).Error
}
func (db *Database) UpdateAutoDownloaderItem(id uint, item *models.AutoDownloaderItem) error {
// Save the data
return db.gormdb.Model(&models.AutoDownloaderItem{}).Where("id = ?", id).Updates(item).Error
}

View File

@@ -0,0 +1,135 @@
package db
import (
"errors"
"gorm.io/gorm"
"seanime/internal/database/models"
)
func (db *Database) GetChapterDownloadQueue() ([]*models.ChapterDownloadQueueItem, error) {
var res []*models.ChapterDownloadQueueItem
err := db.gormdb.Find(&res).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to get chapter download queue")
return nil, err
}
return res, nil
}
func (db *Database) GetNextChapterDownloadQueueItem() (*models.ChapterDownloadQueueItem, error) {
var res models.ChapterDownloadQueueItem
err := db.gormdb.Where("status = ?", "not_started").First(&res).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
db.Logger.Error().Err(err).Msg("db: Failed to get next chapter download queue item")
}
return nil, nil
}
return &res, nil
}
func (db *Database) DequeueChapterDownloadQueueItem() (*models.ChapterDownloadQueueItem, error) {
// Pop the first item from the queue
var res models.ChapterDownloadQueueItem
err := db.gormdb.Where("status = ?", "downloading").First(&res).Error
if err != nil {
return nil, err
}
err = db.gormdb.Delete(&res).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to delete chapter download queue item")
return nil, err
}
return &res, nil
}
func (db *Database) InsertChapterDownloadQueueItem(item *models.ChapterDownloadQueueItem) error {
// Check if the item already exists
var existingItem models.ChapterDownloadQueueItem
err := db.gormdb.Where("provider = ? AND media_id = ? AND chapter_id = ?", item.Provider, item.MediaID, item.ChapterID).First(&existingItem).Error
if err == nil {
db.Logger.Debug().Msg("db: Chapter download queue item already exists")
return errors.New("chapter is already in the download queue")
}
if item.ChapterID == "" {
return errors.New("chapter ID is empty")
}
if item.Provider == "" {
return errors.New("provider is empty")
}
if item.MediaID == 0 {
return errors.New("media ID is empty")
}
if item.ChapterNumber == "" {
return errors.New("chapter number is empty")
}
err = db.gormdb.Create(item).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to insert chapter download queue item")
return err
}
return nil
}
func (db *Database) UpdateChapterDownloadQueueItemStatus(provider string, mId int, chapterId string, status string) error {
err := db.gormdb.Model(&models.ChapterDownloadQueueItem{}).
Where("provider = ? AND media_id = ? AND chapter_id = ?", provider, mId, chapterId).
Update("status", status).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to update chapter download queue item status")
return err
}
return nil
}
func (db *Database) GetMediaQueuedChapters(mediaId int) ([]*models.ChapterDownloadQueueItem, error) {
var res []*models.ChapterDownloadQueueItem
err := db.gormdb.Where("media_id = ?", mediaId).Find(&res).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to get media queued chapters")
return nil, err
}
return res, nil
}
func (db *Database) ClearAllChapterDownloadQueueItems() error {
err := db.gormdb.
Where("status = ? OR status = ? OR status = ?", "not_started", "downloading", "errored").
Delete(&models.ChapterDownloadQueueItem{}).
Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to clear all chapter download queue items")
return err
}
return nil
}
func (db *Database) ResetErroredChapterDownloadQueueItems() error {
err := db.gormdb.Model(&models.ChapterDownloadQueueItem{}).
Where("status = ?", "errored").
Update("status", "not_started").Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to reset errored chapter download queue items")
return err
}
return nil
}
func (db *Database) ResetDownloadingChapterDownloadQueueItems() error {
err := db.gormdb.Model(&models.ChapterDownloadQueueItem{}).
Where("status = ?", "downloading").
Update("status", "not_started").Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to reset downloading chapter download queue items")
return err
}
return nil
}

View File

@@ -0,0 +1,102 @@
package db
import (
"fmt"
"log"
"os"
"path/filepath"
"seanime/internal/database/models"
"time"
"github.com/glebarez/sqlite"
"github.com/rs/zerolog"
"github.com/samber/mo"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
)
type Database struct {
gormdb *gorm.DB
Logger *zerolog.Logger
CurrMediaFillers mo.Option[map[int]*MediaFillerItem]
}
func (db *Database) Gorm() *gorm.DB {
return db.gormdb
}
func NewDatabase(appDataDir, dbName string, logger *zerolog.Logger) (*Database, error) {
// Set the SQLite database path
var sqlitePath string
if os.Getenv("TEST_ENV") == "true" {
sqlitePath = ":memory:"
} else {
sqlitePath = filepath.Join(appDataDir, dbName+".db")
}
// Connect to the SQLite database
db, err := gorm.Open(sqlite.Open(sqlitePath), &gorm.Config{
Logger: gormlogger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
gormlogger.Config{
SlowThreshold: time.Second,
LogLevel: gormlogger.Error,
IgnoreRecordNotFoundError: true,
ParameterizedQueries: false,
Colorful: true,
},
),
})
if err != nil {
return nil, err
}
// Migrate tables
err = migrateTables(db)
if err != nil {
logger.Fatal().Err(err).Msg("db: Failed to perform auto migration")
return nil, err
}
logger.Info().Str("name", fmt.Sprintf("%s.db", dbName)).Msg("db: Database instantiated")
return &Database{
gormdb: db,
Logger: logger,
CurrMediaFillers: mo.None[map[int]*MediaFillerItem](),
}, nil
}
// MigrateTables performs auto migration on the database
func migrateTables(db *gorm.DB) error {
err := db.AutoMigrate(
&models.LocalFiles{},
&models.Settings{},
&models.Account{},
&models.Mal{},
&models.ScanSummary{},
&models.AutoDownloaderRule{},
&models.AutoDownloaderItem{},
&models.SilencedMediaEntry{},
&models.Theme{},
&models.PlaylistEntry{},
&models.ChapterDownloadQueueItem{},
&models.TorrentstreamSettings{},
&models.TorrentstreamHistory{},
&models.MediastreamSettings{},
&models.MediaFiller{},
&models.MangaMapping{},
&models.OnlinestreamMapping{},
&models.DebridSettings{},
&models.DebridTorrentItem{},
&models.PluginData{},
//&models.MangaChapterContainer{},
)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,56 @@
package db
import (
"seanime/internal/database/models"
)
func (db *Database) GetDebridTorrentItems() ([]*models.DebridTorrentItem, error) {
var res []*models.DebridTorrentItem
err := db.gormdb.Find(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
func (db *Database) GetDebridTorrentItemByDbId(dbId uint) (*models.DebridTorrentItem, error) {
var res models.DebridTorrentItem
err := db.gormdb.First(&res, dbId).Error
if err != nil {
return nil, err
}
return &res, nil
}
func (db *Database) GetDebridTorrentItemByTorrentItemId(tId string) (*models.DebridTorrentItem, error) {
var res *models.DebridTorrentItem
err := db.gormdb.Where("torrent_item_id = ?", tId).First(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
func (db *Database) InsertDebridTorrentItem(item *models.DebridTorrentItem) error {
err := db.gormdb.Create(item).Error
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteDebridTorrentItemByDbId(dbId uint) error {
return db.gormdb.Delete(&models.DebridTorrentItem{}, dbId).Error
}
func (db *Database) DeleteDebridTorrentItemByTorrentItemId(tId string) error {
return db.gormdb.Where("torrent_item_id = ?", tId).Delete(&models.DebridTorrentItem{}).Error
}
func (db *Database) UpdateDebridTorrentItemByDbId(dbId uint, item *models.DebridTorrentItem) error {
// Save the data
return db.gormdb.Model(&models.DebridTorrentItem{}).Where("id = ?", dbId).Updates(item).Error
}

View File

@@ -0,0 +1,51 @@
package db
import (
"seanime/internal/database/models"
"gorm.io/gorm/clause"
)
// TrimLocalFileEntries will trim the local file entries if there are more than 10 entries.
// This is run in a goroutine.
func (db *Database) TrimLocalFileEntries() {
go func() {
var count int64
err := db.gormdb.Model(&models.LocalFiles{}).Count(&count).Error
if err != nil {
db.Logger.Error().Err(err).Msg("database: Failed to count local file entries")
return
}
if count > 10 {
// Leave 5 entries
err = db.gormdb.Delete(&models.LocalFiles{}, "id IN (SELECT id FROM local_files ORDER BY id ASC LIMIT ?)", count-5).Error
if err != nil {
db.Logger.Error().Err(err).Msg("database: Failed to delete old local file entries")
return
}
}
}()
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (db *Database) UpsertLocalFiles(lfs *models.LocalFiles) (*models.LocalFiles, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(lfs).Error
if err != nil {
return nil, err
}
return lfs, nil
}
func (db *Database) InsertLocalFiles(lfs *models.LocalFiles) (*models.LocalFiles, error) {
err := db.gormdb.Create(lfs).Error
if err != nil {
return nil, err
}
return lfs, nil
}

View File

@@ -0,0 +1,50 @@
package db
import (
"errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"seanime/internal/database/models"
)
func (db *Database) GetMalInfo() (*models.Mal, error) {
// Get the first entry
var res models.Mal
err := db.gormdb.First(&res, 1).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("MAL not connected")
} else if err != nil {
return nil, err
}
return &res, nil
}
func (db *Database) UpsertMalInfo(info *models.Mal) (*models.Mal, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(info).Error
if err != nil {
return nil, err
}
return info, nil
}
func (db *Database) InsertMalInfo(info *models.Mal) (*models.Mal, error) {
err := db.gormdb.Create(info).Error
if err != nil {
return nil, err
}
return info, nil
}
func (db *Database) DeleteMalInfo() error {
err := db.gormdb.Delete(&models.Mal{}, 1).Error
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,102 @@
package db
import (
"fmt"
"seanime/internal/database/models"
"seanime/internal/util/result"
)
var mangaMappingCache = result.NewResultMap[string, *models.MangaMapping]()
func formatMangaMappingCacheKey(provider string, mediaId int) string {
return fmt.Sprintf("%s$%d", provider, mediaId)
}
func (db *Database) GetMangaMapping(provider string, mediaId int) (*models.MangaMapping, bool) {
if res, ok := mangaMappingCache.Get(formatMangaMappingCacheKey(provider, mediaId)); ok {
return res, true
}
var res models.MangaMapping
err := db.gormdb.Where("provider = ? AND media_id = ?", provider, mediaId).First(&res).Error
if err != nil {
return nil, false
}
mangaMappingCache.Set(formatMangaMappingCacheKey(provider, mediaId), &res)
return &res, true
}
func (db *Database) InsertMangaMapping(provider string, mediaId int, mangaId string) error {
mapping := models.MangaMapping{
Provider: provider,
MediaID: mediaId,
MangaID: mangaId,
}
mangaMappingCache.Set(formatMangaMappingCacheKey(provider, mediaId), &mapping)
return db.gormdb.Save(&mapping).Error
}
func (db *Database) DeleteMangaMapping(provider string, mediaId int) error {
err := db.gormdb.Where("provider = ? AND media_id = ?", provider, mediaId).Delete(&models.MangaMapping{}).Error
if err != nil {
return err
}
mangaMappingCache.Delete(formatMangaMappingCacheKey(provider, mediaId))
return nil
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var mangaChapterContainerCache = result.NewResultMap[string, *models.MangaChapterContainer]()
func formatMangaChapterContainerCacheKey(provider string, mediaId int, chapterId string) string {
return fmt.Sprintf("%s$%d$%s", provider, mediaId, chapterId)
}
func (db *Database) GetMangaChapterContainer(provider string, mediaId int, chapterId string) (*models.MangaChapterContainer, bool) {
if res, ok := mangaChapterContainerCache.Get(formatMangaChapterContainerCacheKey(provider, mediaId, chapterId)); ok {
return res, true
}
var res models.MangaChapterContainer
err := db.gormdb.Where("provider = ? AND media_id = ? AND chapter_id = ?", provider, mediaId, chapterId).First(&res).Error
if err != nil {
return nil, false
}
mangaChapterContainerCache.Set(formatMangaChapterContainerCacheKey(provider, mediaId, chapterId), &res)
return &res, true
}
func (db *Database) InsertMangaChapterContainer(provider string, mediaId int, chapterId string, chapterContainer []byte) error {
container := models.MangaChapterContainer{
Provider: provider,
MediaID: mediaId,
ChapterID: chapterId,
Data: chapterContainer,
}
mangaChapterContainerCache.Set(formatMangaChapterContainerCacheKey(provider, mediaId, chapterId), &container)
return db.gormdb.Save(&container).Error
}
func (db *Database) DeleteMangaChapterContainer(provider string, mediaId int, chapterId string) error {
err := db.gormdb.Where("provider = ? AND media_id = ? AND chapter_id = ?", provider, mediaId, chapterId).Delete(&models.MangaChapterContainer{}).Error
if err != nil {
return err
}
mangaChapterContainerCache.Delete(formatMangaChapterContainerCacheKey(provider, mediaId, chapterId))
return nil
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,182 @@
package db
import (
"github.com/goccy/go-json"
"github.com/samber/mo"
"seanime/internal/api/filler"
"seanime/internal/database/models"
"time"
)
type MediaFillerItem struct {
DbId uint `json:"dbId"`
Provider string `json:"provider"`
Slug string `json:"slug"`
MediaId int `json:"mediaId"`
LastFetchedAt time.Time `json:"lastFetchedAt"`
FillerEpisodes []string `json:"fillerEpisodes"`
}
// GetCachedMediaFillers will return all the media fillers (cache-first).
// If the cache is empty, it will fetch the media fillers from the database.
func (db *Database) GetCachedMediaFillers() (map[int]*MediaFillerItem, error) {
if db.CurrMediaFillers.IsPresent() {
return db.CurrMediaFillers.MustGet(), nil
}
var res []*models.MediaFiller
err := db.gormdb.Find(&res).Error
if err != nil {
return nil, err
}
// Unmarshal the media fillers
mediaFillers := make(map[int]*MediaFillerItem)
for _, mf := range res {
var fillerData filler.Data
if err := json.Unmarshal(mf.Data, &fillerData); err != nil {
return nil, err
}
// Get the filler episodes
var fillerEpisodes []string
if fillerData.FillerEpisodes != nil || len(fillerData.FillerEpisodes) > 0 {
fillerEpisodes = fillerData.FillerEpisodes
}
mediaFillers[mf.MediaID] = &MediaFillerItem{
DbId: mf.ID,
Provider: mf.Provider,
MediaId: mf.MediaID,
Slug: mf.Slug,
LastFetchedAt: mf.LastFetchedAt,
FillerEpisodes: fillerEpisodes,
}
}
// Cache the media fillers
db.CurrMediaFillers = mo.Some(mediaFillers)
return db.CurrMediaFillers.MustGet(), nil
}
func (db *Database) GetMediaFillerItem(mediaId int) (*MediaFillerItem, bool) {
mediaFillers, err := db.GetCachedMediaFillers()
if err != nil {
return nil, false
}
item, ok := mediaFillers[mediaId]
return item, ok
}
func (db *Database) InsertMediaFiller(
provider string,
mediaId int,
slug string,
lastFetchedAt time.Time,
fillerEpisodes []string,
) error {
// Marshal the filler data
fillerData := filler.Data{
FillerEpisodes: fillerEpisodes,
}
fillerDataBytes, err := json.Marshal(fillerData)
if err != nil {
return err
}
// Delete the existing media filler
_ = db.DeleteMediaFiller(mediaId)
// Save the media filler
err = db.gormdb.Create(&models.MediaFiller{
Provider: provider,
MediaID: mediaId,
Slug: slug,
LastFetchedAt: lastFetchedAt,
Data: fillerDataBytes,
}).Error
if err != nil {
return err
}
// Update the cache
db.CurrMediaFillers = mo.None[map[int]*MediaFillerItem]()
return nil
}
// SaveCachedMediaFillerItems will save the cached media filler items in the database.
// Call this function after editing the cached media filler items.
func (db *Database) SaveCachedMediaFillerItems() error {
if db.CurrMediaFillers.IsAbsent() {
return nil
}
mediaFillers, err := db.GetCachedMediaFillers()
if err != nil {
return err
}
for _, mf := range mediaFillers {
if len(mf.FillerEpisodes) == 0 {
continue
}
// Marshal the filler data
fillerData := filler.Data{
FillerEpisodes: mf.FillerEpisodes,
}
fillerDataBytes, err := json.Marshal(fillerData)
if err != nil {
return err
}
// Save the media filler
err = db.gormdb.Model(&models.MediaFiller{}).
Where("id = ?", mf.DbId).
Updates(map[string]interface{}{
"last_fetched_at": mf.LastFetchedAt,
"data": fillerDataBytes,
}).Error
if err != nil {
return err
}
}
// Update the cache
db.CurrMediaFillers = mo.None[map[int]*MediaFillerItem]()
return nil
}
func (db *Database) DeleteMediaFiller(mediaId int) error {
mediaFillers, err := db.GetCachedMediaFillers()
if err != nil {
return err
}
item, ok := mediaFillers[mediaId]
if !ok {
return nil
}
err = db.gormdb.Delete(&models.MediaFiller{}, item.DbId).Error
if err != nil {
return err
}
// Update the cache
db.CurrMediaFillers = mo.None[map[int]*MediaFillerItem]()
return nil
}

View File

@@ -0,0 +1,24 @@
package db
import (
"seanime/internal/database/models"
)
func (db *Database) UpsertNakamaSettings(nakamaSettings *models.NakamaSettings) (*models.NakamaSettings, error) {
// Get current settings
currentSettings, err := db.GetSettings()
if err != nil {
return nil, err
}
// Update the settings
*(currentSettings.Nakama) = *nakamaSettings
_, err = db.UpsertSettings(currentSettings)
if err != nil {
return nil, err
}
return nakamaSettings, nil
}

View File

@@ -0,0 +1,52 @@
package db
import (
"fmt"
"seanime/internal/database/models"
"seanime/internal/util/result"
)
var onlinestreamMappingCache = result.NewResultMap[string, *models.OnlinestreamMapping]()
func formatOnlinestreamMappingCacheKey(provider string, mediaId int) string {
return fmt.Sprintf("%s$%d", provider, mediaId)
}
func (db *Database) GetOnlinestreamMapping(provider string, mediaId int) (*models.OnlinestreamMapping, bool) {
if res, ok := onlinestreamMappingCache.Get(formatOnlinestreamMappingCacheKey(provider, mediaId)); ok {
return res, true
}
var res models.OnlinestreamMapping
err := db.gormdb.Where("provider = ? AND media_id = ?", provider, mediaId).First(&res).Error
if err != nil {
return nil, false
}
onlinestreamMappingCache.Set(formatOnlinestreamMappingCacheKey(provider, mediaId), &res)
return &res, true
}
func (db *Database) InsertOnlinestreamMapping(provider string, mediaId int, animeId string) error {
mapping := models.OnlinestreamMapping{
Provider: provider,
MediaID: mediaId,
AnimeID: animeId,
}
onlinestreamMappingCache.Set(formatOnlinestreamMappingCacheKey(provider, mediaId), &mapping)
return db.gormdb.Save(&mapping).Error
}
func (db *Database) DeleteOnlinestreamMapping(provider string, mediaId int) error {
err := db.gormdb.Where("provider = ? AND media_id = ?", provider, mediaId).Delete(&models.OnlinestreamMapping{}).Error
if err != nil {
return err
}
onlinestreamMappingCache.Delete(formatOnlinestreamMappingCacheKey(provider, mediaId))
return nil
}

View File

@@ -0,0 +1,24 @@
package db
import (
"seanime/internal/database/models"
)
func (db *Database) TrimScanSummaryEntries() {
go func() {
var count int64
err := db.gormdb.Model(&models.ScanSummary{}).Count(&count).Error
if err != nil {
db.Logger.Error().Err(err).Msg("Failed to count scan summary entries")
return
}
if count > 10 {
// Leave 5 entries
err = db.gormdb.Delete(&models.ScanSummary{}, "id IN (SELECT id FROM scan_summaries ORDER BY id ASC LIMIT ?)", count-5).Error
if err != nil {
db.Logger.Error().Err(err).Msg("Failed to delete old scan summary entries")
return
}
}
}()
}

View File

@@ -0,0 +1,200 @@
package db
import (
"seanime/internal/database/models"
"gorm.io/gorm/clause"
)
var CurrSettings *models.Settings
func (db *Database) UpsertSettings(settings *models.Settings) (*models.Settings, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(settings).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to save settings in the database")
return nil, err
}
CurrSettings = settings
db.Logger.Debug().Msg("db: Settings saved")
return settings, nil
}
func (db *Database) GetSettings() (*models.Settings, error) {
if CurrSettings != nil {
return CurrSettings, nil
}
var settings models.Settings
err := db.gormdb.Where("id = ?", 1).Find(&settings).Error
if err != nil {
return nil, err
}
return &settings, nil
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (db *Database) GetLibraryPathFromSettings() (string, error) {
settings, err := db.GetSettings()
if err != nil {
return "", err
}
return settings.Library.LibraryPath, nil
}
func (db *Database) GetAdditionalLibraryPathsFromSettings() ([]string, error) {
settings, err := db.GetSettings()
if err != nil {
return []string{}, err
}
return settings.Library.LibraryPaths, nil
}
func (db *Database) GetAllLibraryPathsFromSettings() ([]string, error) {
settings, err := db.GetSettings()
if err != nil {
return []string{}, err
}
if settings.Library == nil {
return []string{}, nil
}
return append([]string{settings.Library.LibraryPath}, settings.Library.LibraryPaths...), nil
}
func (db *Database) AllLibraryPathsFromSettings(settings *models.Settings) *[]string {
if settings.Library == nil {
return &[]string{}
}
r := append([]string{settings.Library.LibraryPath}, settings.Library.LibraryPaths...)
return &r
}
func (db *Database) AutoUpdateProgressIsEnabled() (bool, error) {
settings, err := db.GetSettings()
if err != nil {
return false, err
}
return settings.Library.AutoUpdateProgress, nil
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var CurrMediastreamSettings *models.MediastreamSettings
func (db *Database) UpsertMediastreamSettings(settings *models.MediastreamSettings) (*models.MediastreamSettings, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(settings).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to save media streaming settings in the database")
return nil, err
}
CurrMediastreamSettings = settings
db.Logger.Debug().Msg("db: Media streaming settings saved")
return settings, nil
}
func (db *Database) GetMediastreamSettings() (*models.MediastreamSettings, bool) {
if CurrMediastreamSettings != nil {
return CurrMediastreamSettings, true
}
var settings models.MediastreamSettings
err := db.gormdb.Where("id = ?", 1).First(&settings).Error
if err != nil {
return nil, false
}
return &settings, true
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var CurrTorrentstreamSettings *models.TorrentstreamSettings
func (db *Database) UpsertTorrentstreamSettings(settings *models.TorrentstreamSettings) (*models.TorrentstreamSettings, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(settings).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to save torrent streaming settings in the database")
return nil, err
}
CurrTorrentstreamSettings = settings
db.Logger.Debug().Msg("db: Torrent streaming settings saved")
return settings, nil
}
func (db *Database) GetTorrentstreamSettings() (*models.TorrentstreamSettings, bool) {
if CurrTorrentstreamSettings != nil {
return CurrTorrentstreamSettings, true
}
var settings models.TorrentstreamSettings
err := db.gormdb.Where("id = ?", 1).First(&settings).Error
if err != nil {
return nil, false
}
return &settings, true
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var CurrentDebridSettings *models.DebridSettings
func (db *Database) UpsertDebridSettings(settings *models.DebridSettings) (*models.DebridSettings, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(settings).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to save debrid settings in the database")
return nil, err
}
CurrentDebridSettings = settings
db.Logger.Debug().Msg("db: Debrid settings saved")
return settings, nil
}
func (db *Database) GetDebridSettings() (*models.DebridSettings, bool) {
if CurrentDebridSettings != nil {
return CurrentDebridSettings, true
}
var settings models.DebridSettings
err := db.gormdb.Where("id = ?", 1).First(&settings).Error
if err != nil {
return nil, false
}
return &settings, true
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,70 @@
package db
import (
"gorm.io/gorm/clause"
"seanime/internal/database/models"
)
func (db *Database) GetSilencedMediaEntries() ([]*models.SilencedMediaEntry, error) {
var res []*models.SilencedMediaEntry
err := db.gormdb.Find(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
// GetSilencedMediaEntryIds returns the ids of all silenced media entries.
// It returns an empty slice if there is an error.
func (db *Database) GetSilencedMediaEntryIds() ([]int, error) {
var res []*models.SilencedMediaEntry
err := db.gormdb.Find(&res).Error
if err != nil {
return make([]int, 0), err
}
if len(res) == 0 {
return make([]int, 0), nil
}
mIds := make([]int, len(res))
for i, v := range res {
mIds[i] = int(v.ID)
}
return mIds, nil
}
func (db *Database) GetSilencedMediaEntry(mId uint) (*models.SilencedMediaEntry, error) {
var res models.SilencedMediaEntry
err := db.gormdb.First(&res, mId).Error
if err != nil {
return nil, err
}
return &res, nil
}
func (db *Database) InsertSilencedMediaEntry(mId uint) error {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(&models.SilencedMediaEntry{
BaseModel: models.BaseModel{
ID: mId,
},
}).Error
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteSilencedMediaEntry(id uint) error {
err := db.gormdb.Delete(&models.SilencedMediaEntry{}, id).Error
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,47 @@
package db
import (
"gorm.io/gorm/clause"
"seanime/internal/database/models"
)
var themeCache *models.Theme
func (db *Database) GetTheme() (*models.Theme, error) {
if themeCache != nil {
return themeCache, nil
}
var theme models.Theme
err := db.gormdb.Where("id = ?", 1).Find(&theme).Error
if err != nil {
return nil, err
}
themeCache = &theme
return &theme, nil
}
// UpsertTheme updates the theme settings.
func (db *Database) UpsertTheme(settings *models.Theme) (*models.Theme, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(settings).Error
if err != nil {
db.Logger.Error().Err(err).Msg("db: Failed to save theme in the database")
return nil, err
}
db.Logger.Debug().Msg("db: Theme saved")
themeCache = settings
return settings, nil
}

View File

@@ -0,0 +1,21 @@
package db
import (
"gorm.io/gorm/clause"
"seanime/internal/database/models"
)
func (db *Database) UpsertToken(token *models.Token) (*models.Token, error) {
err := db.gormdb.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"value", "updated_at"}),
}).Create(token).Error
if err != nil {
db.Logger.Error().Err(err).Msg("Failed to save token in the database")
return nil, err
}
return token, nil
}

View File

@@ -0,0 +1,24 @@
package db
import (
"seanime/internal/database/models"
)
func (db *Database) TrimTorrentstreamHistory() {
go func() {
var count int64
err := db.gormdb.Model(&models.TorrentstreamHistory{}).Count(&count).Error
if err != nil {
db.Logger.Error().Err(err).Msg("database: Failed to count torrent stream history entries")
return
}
if count > 50 {
// Leave 40 entries
err = db.gormdb.Delete(&models.TorrentstreamHistory{}, "id IN (SELECT id FROM torrentstream_histories ORDER BY updated_at ASC LIMIT ?)", 10).Error
if err != nil {
db.Logger.Error().Err(err).Msg("database: Failed to delete old torrent stream history entries")
return
}
}
}()
}