package models import ( "database/sql/driver" "errors" "strconv" "strings" "time" ) type BaseModel struct { ID uint `gorm:"primarykey" json:"id"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } type Token struct { BaseModel Value string `json:"value"` } type Account struct { BaseModel Username string `gorm:"column:username" json:"username"` Token string `gorm:"column:token" json:"token"` Viewer []byte `gorm:"column:viewer" json:"viewer"` } // +---------------------+ // | LocalFiles | // +---------------------+ type LocalFiles struct { BaseModel Value []byte `gorm:"column:value" json:"value"` } // +---------------------+ // | Settings | // +---------------------+ type Settings struct { BaseModel Library *LibrarySettings `gorm:"embedded" json:"library"` MediaPlayer *MediaPlayerSettings `gorm:"embedded" json:"mediaPlayer"` Torrent *TorrentSettings `gorm:"embedded" json:"torrent"` Manga *MangaSettings `gorm:"embedded" json:"manga"` Anilist *AnilistSettings `gorm:"embedded" json:"anilist"` ListSync *ListSyncSettings `gorm:"embedded" json:"listSync"` AutoDownloader *AutoDownloaderSettings `gorm:"embedded" json:"autoDownloader"` Discord *DiscordSettings `gorm:"embedded" json:"discord"` Notifications *NotificationSettings `gorm:"embedded" json:"notifications"` Nakama *NakamaSettings `gorm:"embedded;embeddedPrefix:nakama_" json:"nakama"` } type AnilistSettings struct { //AnilistClientId string `gorm:"column:anilist_client_id" json:"anilistClientId"` HideAudienceScore bool `gorm:"column:hide_audience_score" json:"hideAudienceScore"` EnableAdultContent bool `gorm:"column:enable_adult_content" json:"enableAdultContent"` BlurAdultContent bool `gorm:"column:blur_adult_content" json:"blurAdultContent"` } type LibrarySettings struct { LibraryPath string `gorm:"column:library_path" json:"libraryPath"` AutoUpdateProgress bool `gorm:"column:auto_update_progress" json:"autoUpdateProgress"` DisableUpdateCheck bool `gorm:"column:disable_update_check" json:"disableUpdateCheck"` TorrentProvider string `gorm:"column:torrent_provider" json:"torrentProvider"` AutoScan bool `gorm:"column:auto_scan" json:"autoScan"` EnableOnlinestream bool `gorm:"column:enable_onlinestream" json:"enableOnlinestream"` IncludeOnlineStreamingInLibrary bool `gorm:"column:include_online_streaming_in_library" json:"includeOnlineStreamingInLibrary"` DisableAnimeCardTrailers bool `gorm:"column:disable_anime_card_trailers" json:"disableAnimeCardTrailers"` EnableManga bool `gorm:"column:enable_manga" json:"enableManga"` DOHProvider string `gorm:"column:doh_provider" json:"dohProvider"` OpenTorrentClientOnStart bool `gorm:"column:open_torrent_client_on_start" json:"openTorrentClientOnStart"` OpenWebURLOnStart bool `gorm:"column:open_web_url_on_start" json:"openWebURLOnStart"` RefreshLibraryOnStart bool `gorm:"column:refresh_library_on_start" json:"refreshLibraryOnStart"` // v2.1+ AutoPlayNextEpisode bool `gorm:"column:auto_play_next_episode" json:"autoPlayNextEpisode"` // v2.2+ EnableWatchContinuity bool `gorm:"column:enable_watch_continuity" json:"enableWatchContinuity"` LibraryPaths LibraryPaths `gorm:"column:library_paths;type:text" json:"libraryPaths"` AutoSyncOfflineLocalData bool `gorm:"column:auto_sync_offline_local_data" json:"autoSyncOfflineLocalData"` // v2.6+ ScannerMatchingThreshold float64 `gorm:"column:scanner_matching_threshold" json:"scannerMatchingThreshold"` ScannerMatchingAlgorithm string `gorm:"column:scanner_matching_algorithm" json:"scannerMatchingAlgorithm"` // v2.9+ AutoSyncToLocalAccount bool `gorm:"column:auto_sync_to_local_account" json:"autoSyncToLocalAccount"` AutoSaveCurrentMediaOffline bool `gorm:"column:auto_save_current_media_offline" json:"autoSaveCurrentMediaOffline"` } func (o *LibrarySettings) GetLibraryPaths() (ret []string) { ret = make([]string, len(o.LibraryPaths)+1) ret[0] = o.LibraryPath if len(o.LibraryPaths) > 0 { copy(ret[1:], o.LibraryPaths) } return } type LibraryPaths []string func (o *LibraryPaths) Scan(src interface{}) error { str, ok := src.(string) if !ok { return errors.New("src value cannot cast to string") } *o = strings.Split(str, ",") return nil } func (o LibraryPaths) Value() (driver.Value, error) { if len(o) == 0 { return nil, nil } return strings.Join(o, ","), nil } type NakamaSettings struct { Enabled bool `gorm:"column:enabled" json:"enabled"` // Username is the name used to identify a peer or host. Username string `gorm:"column:username" json:"username"` // IsHost allows the server to act as a host for other clients. This requires a password to be set. IsHost bool `gorm:"column:is_host" json:"isHost"` HostPassword string `gorm:"column:host_password" json:"hostPassword"` RemoteServerURL string `gorm:"column:remote_server_url" json:"remoteServerURL"` RemoteServerPassword string `gorm:"column:remote_server_password" json:"remoteServerPassword"` // IncludeNakamaAnimeLibrary adds the local anime library of the host to the connected clients. IncludeNakamaAnimeLibrary bool `gorm:"column:include_nakama_anime_library" json:"includeNakamaAnimeLibrary"` // HostShareLocalAnimeLibrary shares the local anime library to connected clients HostShareLocalAnimeLibrary bool `gorm:"column:host_share_local_anime_library" json:"hostShareLocalAnimeLibrary"` // HostUnsharedAnimeIds is a list of anime IDs that should not be shared with connected clients. HostUnsharedAnimeIds IntSlice `gorm:"column:host_unshared_anime_ids;type:text" json:"hostUnsharedAnimeIds"` // HostEnablePortForwarding enables port forwarding. HostEnablePortForwarding bool `gorm:"column:host_enable_port_forwarding" json:"hostEnablePortForwarding"` } type IntSlice []int func (o *IntSlice) Scan(src interface{}) error { str, ok := src.(string) if !ok { return errors.New("src value cannot cast to string") } ids := strings.Split(str, ",") *o = make(IntSlice, len(ids)) for i, id := range ids { (*o)[i], _ = strconv.Atoi(id) } return nil } func (o IntSlice) Value() (driver.Value, error) { if len(o) == 0 { return nil, nil } strs := make([]string, len(o)) for i, id := range o { strs[i] = strconv.Itoa(id) } return strings.Join(strs, ","), nil } type MangaSettings struct { DefaultProvider string `gorm:"column:default_manga_provider" json:"defaultMangaProvider"` AutoUpdateProgress bool `gorm:"column:manga_auto_update_progress" json:"mangaAutoUpdateProgress"` LocalSourceDirectory string `gorm:"column:manga_local_source_directory" json:"mangaLocalSourceDirectory"` } type MediaPlayerSettings struct { Default string `gorm:"column:default_player" json:"defaultPlayer"` // "vlc" or "mpc-hc" Host string `gorm:"column:player_host" json:"host"` VlcUsername string `gorm:"column:vlc_username" json:"vlcUsername"` VlcPassword string `gorm:"column:vlc_password" json:"vlcPassword"` VlcPort int `gorm:"column:vlc_port" json:"vlcPort"` VlcPath string `gorm:"column:vlc_path" json:"vlcPath"` MpcPort int `gorm:"column:mpc_port" json:"mpcPort"` MpcPath string `gorm:"column:mpc_path" json:"mpcPath"` MpvSocket string `gorm:"column:mpv_socket" json:"mpvSocket"` MpvPath string `gorm:"column:mpv_path" json:"mpvPath"` MpvArgs string `gorm:"column:mpv_args" json:"mpvArgs"` IinaSocket string `gorm:"column:iina_socket" json:"iinaSocket"` IinaPath string `gorm:"column:iina_path" json:"iinaPath"` IinaArgs string `gorm:"column:iina_args" json:"iinaArgs"` } type TorrentSettings struct { Default string `gorm:"column:default_torrent_client" json:"defaultTorrentClient"` QBittorrentPath string `gorm:"column:qbittorrent_path" json:"qbittorrentPath"` QBittorrentHost string `gorm:"column:qbittorrent_host" json:"qbittorrentHost"` QBittorrentPort int `gorm:"column:qbittorrent_port" json:"qbittorrentPort"` QBittorrentUsername string `gorm:"column:qbittorrent_username" json:"qbittorrentUsername"` QBittorrentPassword string `gorm:"column:qbittorrent_password" json:"qbittorrentPassword"` QBittorrentTags string `gorm:"column:qbittorrent_tags" json:"qbittorrentTags"` TransmissionPath string `gorm:"column:transmission_path" json:"transmissionPath"` TransmissionHost string `gorm:"column:transmission_host" json:"transmissionHost"` TransmissionPort int `gorm:"column:transmission_port" json:"transmissionPort"` TransmissionUsername string `gorm:"column:transmission_username" json:"transmissionUsername"` TransmissionPassword string `gorm:"column:transmission_password" json:"transmissionPassword"` // v2.1+ ShowActiveTorrentCount bool `gorm:"column:show_active_torrent_count" json:"showActiveTorrentCount"` // v2.2+ HideTorrentList bool `gorm:"column:hide_torrent_list" json:"hideTorrentList"` } type ListSyncSettings struct { Automatic bool `gorm:"column:automatic_sync" json:"automatic"` Origin string `gorm:"column:sync_origin" json:"origin"` } type DiscordSettings struct { EnableRichPresence bool `gorm:"column:enable_rich_presence" json:"enableRichPresence"` EnableAnimeRichPresence bool `gorm:"column:enable_anime_rich_presence" json:"enableAnimeRichPresence"` EnableMangaRichPresence bool `gorm:"column:enable_manga_rich_presence" json:"enableMangaRichPresence"` RichPresenceHideSeanimeRepositoryButton bool `gorm:"column:rich_presence_hide_seanime_repository_button" json:"richPresenceHideSeanimeRepositoryButton"` RichPresenceShowAniListMediaButton bool `gorm:"column:rich_presence_show_anilist_media_button" json:"richPresenceShowAniListMediaButton"` RichPresenceShowAniListProfileButton bool `gorm:"column:rich_presence_show_anilist_profile_button" json:"richPresenceShowAniListProfileButton"` RichPresenceUseMediaTitleStatus bool `gorm:"column:rich_presence_use_media_title_status;default:true" json:"richPresenceUseMediaTitleStatus"` } type NotificationSettings struct { DisableNotifications bool `gorm:"column:disable_notifications" json:"disableNotifications"` DisableAutoDownloaderNotifications bool `gorm:"column:disable_auto_downloader_notifications" json:"disableAutoDownloaderNotifications"` DisableAutoScannerNotifications bool `gorm:"column:disable_auto_scanner_notifications" json:"disableAutoScannerNotifications"` } // +---------------------+ // | MAL | // +---------------------+ type Mal struct { BaseModel Username string `gorm:"column:username" json:"username"` AccessToken string `gorm:"column:access_token" json:"accessToken"` RefreshToken string `gorm:"column:refresh_token" json:"refreshToken"` TokenExpiresAt time.Time `gorm:"column:token_expires_at" json:"tokenExpiresAt"` } // +---------------------+ // | Scan Summary | // +---------------------+ type ScanSummary struct { BaseModel Value []byte `gorm:"column:value" json:"value"` } // +---------------------+ // | Auto downloader | // +---------------------+ type AutoDownloaderRule struct { BaseModel Value []byte `gorm:"column:value" json:"value"` } type AutoDownloaderItem struct { BaseModel RuleID uint `gorm:"column:rule_id" json:"ruleId"` MediaID int `gorm:"column:media_id" json:"mediaId"` Episode int `gorm:"column:episode" json:"episode"` Link string `gorm:"column:link" json:"link"` Hash string `gorm:"column:hash" json:"hash"` Magnet string `gorm:"column:magnet" json:"magnet"` TorrentName string `gorm:"column:torrent_name" json:"torrentName"` Downloaded bool `gorm:"column:downloaded" json:"downloaded"` } type AutoDownloaderSettings struct { Provider string `gorm:"column:auto_downloader_provider" json:"provider"` Interval int `gorm:"column:auto_downloader_interval" json:"interval"` Enabled bool `gorm:"column:auto_downloader_enabled" json:"enabled"` DownloadAutomatically bool `gorm:"column:auto_downloader_download_automatically" json:"downloadAutomatically"` EnableEnhancedQueries bool `gorm:"column:auto_downloader_enable_enhanced_queries" json:"enableEnhancedQueries"` EnableSeasonCheck bool `gorm:"column:auto_downloader_enable_season_check" json:"enableSeasonCheck"` UseDebrid bool `gorm:"column:auto_downloader_use_debrid" json:"useDebrid"` } // +---------------------+ // | Media Entry | // +---------------------+ type SilencedMediaEntry struct { BaseModel } // +---------------------+ // | Theme | // +---------------------+ type Theme struct { BaseModel // Main EnableColorSettings bool `gorm:"column:enable_color_settings" json:"enableColorSettings"` BackgroundColor string `gorm:"column:background_color" json:"backgroundColor"` AccentColor string `gorm:"column:accent_color" json:"accentColor"` SidebarBackgroundColor string `gorm:"column:sidebar_background_color" json:"sidebarBackgroundColor"` // DEPRECATED AnimeEntryScreenLayout string `gorm:"column:anime_entry_screen_layout" json:"animeEntryScreenLayout"` // DEPRECATED ExpandSidebarOnHover bool `gorm:"column:expand_sidebar_on_hover" json:"expandSidebarOnHover"` HideTopNavbar bool `gorm:"column:hide_top_navbar" json:"hideTopNavbar"` EnableMediaCardBlurredBackground bool `gorm:"column:enable_media_card_blurred_background" json:"enableMediaCardBlurredBackground"` // Note: These are named "libraryScreen" but are used on all pages LibraryScreenCustomBackgroundImage string `gorm:"column:library_screen_custom_background_image" json:"libraryScreenCustomBackgroundImage"` LibraryScreenCustomBackgroundOpacity int `gorm:"column:library_screen_custom_background_opacity" json:"libraryScreenCustomBackgroundOpacity"` // Anime SmallerEpisodeCarouselSize bool `gorm:"column:smaller_episode_carousel_size" json:"smallerEpisodeCarouselSize"` // Library Screen (Anime & Manga) // LibraryScreenBannerType: "dynamic", "custom" LibraryScreenBannerType string `gorm:"column:library_screen_banner_type" json:"libraryScreenBannerType"` LibraryScreenCustomBannerImage string `gorm:"column:library_screen_custom_banner_image" json:"libraryScreenCustomBannerImage"` LibraryScreenCustomBannerPosition string `gorm:"column:library_screen_custom_banner_position" json:"libraryScreenCustomBannerPosition"` LibraryScreenCustomBannerOpacity int `gorm:"column:library_screen_custom_banner_opacity" json:"libraryScreenCustomBannerOpacity"` DisableLibraryScreenGenreSelector bool `gorm:"column:disable_library_screen_genre_selector" json:"disableLibraryScreenGenreSelector"` LibraryScreenCustomBackgroundBlur string `gorm:"column:library_screen_custom_background_blur" json:"libraryScreenCustomBackgroundBlur"` EnableMediaPageBlurredBackground bool `gorm:"column:enable_media_page_blurred_background" json:"enableMediaPageBlurredBackground"` DisableSidebarTransparency bool `gorm:"column:disable_sidebar_transparency" json:"disableSidebarTransparency"` UseLegacyEpisodeCard bool `gorm:"column:use_legacy_episode_card" json:"useLegacyEpisodeCard"` // DEPRECATED DisableCarouselAutoScroll bool `gorm:"column:disable_carousel_auto_scroll" json:"disableCarouselAutoScroll"` // v2.6+ MediaPageBannerType string `gorm:"column:media_page_banner_type" json:"mediaPageBannerType"` MediaPageBannerSize string `gorm:"column:media_page_banner_size" json:"mediaPageBannerSize"` MediaPageBannerInfoBoxSize string `gorm:"column:media_page_banner_info_box_size" json:"mediaPageBannerInfoBoxSize"` // v2.7+ ShowEpisodeCardAnimeInfo bool `gorm:"column:show_episode_card_anime_info" json:"showEpisodeCardAnimeInfo"` ContinueWatchingDefaultSorting string `gorm:"column:continue_watching_default_sorting" json:"continueWatchingDefaultSorting"` AnimeLibraryCollectionDefaultSorting string `gorm:"column:anime_library_collection_default_sorting" json:"animeLibraryCollectionDefaultSorting"` MangaLibraryCollectionDefaultSorting string `gorm:"column:manga_library_collection_default_sorting" json:"mangaLibraryCollectionDefaultSorting"` ShowAnimeUnwatchedCount bool `gorm:"column:show_anime_unwatched_count" json:"showAnimeUnwatchedCount"` ShowMangaUnreadCount bool `gorm:"column:show_manga_unread_count" json:"showMangaUnreadCount"` // v2.8+ HideEpisodeCardDescription bool `gorm:"column:hide_episode_card_description" json:"hideEpisodeCardDescription"` HideDownloadedEpisodeCardFilename bool `gorm:"column:hide_downloaded_episode_card_filename" json:"hideDownloadedEpisodeCardFilename"` CustomCSS string `gorm:"column:custom_css" json:"customCSS"` MobileCustomCSS string `gorm:"column:mobile_custom_css" json:"mobileCustomCSS"` // v2.9+ UnpinnedMenuItems StringSlice `gorm:"column:unpinned_menu_items;type:text" json:"unpinnedMenuItems"` } // +---------------------+ // | Playlist | // +---------------------+ type PlaylistEntry struct { BaseModel Name string `gorm:"column:name" json:"name"` Value []byte `gorm:"column:value" json:"value"` } // +------------------------+ // | Chapter Download Queue | // +------------------------+ type ChapterDownloadQueueItem struct { BaseModel Provider string `gorm:"column:provider" json:"provider"` MediaID int `gorm:"column:media_id" json:"mediaId"` ChapterID string `gorm:"column:chapter_id" json:"chapterId"` ChapterNumber string `gorm:"column:chapter_number" json:"chapterNumber"` PageData []byte `gorm:"column:page_data" json:"pageData"` // Contains map of page index to page details Status string `gorm:"column:status" json:"status"` } // +---------------------+ // | MediaStream | // +---------------------+ type MediastreamSettings struct { BaseModel // DEVNOTE: Should really be "Enabled" TranscodeEnabled bool `gorm:"column:transcode_enabled" json:"transcodeEnabled"` TranscodeHwAccel string `gorm:"column:transcode_hw_accel" json:"transcodeHwAccel"` TranscodeThreads int `gorm:"column:transcode_threads" json:"transcodeThreads"` TranscodePreset string `gorm:"column:transcode_preset" json:"transcodePreset"` DisableAutoSwitchToDirectPlay bool `gorm:"column:disable_auto_switch_to_direct_play" json:"disableAutoSwitchToDirectPlay"` DirectPlayOnly bool `gorm:"column:direct_play_only" json:"directPlayOnly"` PreTranscodeEnabled bool `gorm:"column:pre_transcode_enabled" json:"preTranscodeEnabled"` PreTranscodeLibraryDir string `gorm:"column:pre_transcode_library_dir" json:"preTranscodeLibraryDir"` FfmpegPath string `gorm:"column:ffmpeg_path" json:"ffmpegPath"` FfprobePath string `gorm:"column:ffprobe_path" json:"ffprobePath"` // v2.2+ TranscodeHwAccelCustomSettings string `gorm:"column:transcode_hw_accel_custom_settings" json:"transcodeHwAccelCustomSettings"` //TranscodeTempDir string `gorm:"column:transcode_temp_dir" json:"transcodeTempDir"` // DEPRECATED } // +---------------------+ // | TorrentStream | // +---------------------+ type TorrentstreamSettings struct { BaseModel Enabled bool `gorm:"column:enabled" json:"enabled"` AutoSelect bool `gorm:"column:auto_select" json:"autoSelect"` PreferredResolution string `gorm:"column:preferred_resolution" json:"preferredResolution"` DisableIPV6 bool `gorm:"column:disable_ipv6" json:"disableIPV6"` DownloadDir string `gorm:"column:download_dir" json:"downloadDir"` AddToLibrary bool `gorm:"column:add_to_library" json:"addToLibrary"` TorrentClientHost string `gorm:"column:torrent_client_host" json:"torrentClientHost"` TorrentClientPort int `gorm:"column:torrent_client_port" json:"torrentClientPort"` StreamingServerHost string `gorm:"column:streaming_server_host" json:"streamingServerHost"` StreamingServerPort int `gorm:"column:streaming_server_port" json:"streamingServerPort"` //FallbackToTorrentStreamingView bool `gorm:"column:fallback_to_torrent_streaming_view" json:"fallbackToTorrentStreamingView"` // DEPRECATED IncludeInLibrary bool `gorm:"column:include_in_library" json:"includeInLibrary"` // v2.6+ StreamUrlAddress string `gorm:"column:stream_url_address" json:"streamUrlAddress"` // v2.7+ SlowSeeding bool `gorm:"column:slow_seeding" json:"slowSeeding"` } type TorrentstreamHistory struct { BaseModel MediaId int `gorm:"column:media_id" json:"mediaId"` Torrent []byte `gorm:"column:torrent" json:"torrent"` } // +---------------------+ // | Filler | // +---------------------+ type MediaFiller struct { BaseModel Provider string `gorm:"column:provider" json:"provider"` Slug string `gorm:"column:slug" json:"slug"` MediaID int `gorm:"column:media_id" json:"mediaId"` LastFetchedAt time.Time `gorm:"column:last_fetched_at" json:"lastFetchedAt"` Data []byte `gorm:"column:data" json:"data"` } // +---------------------+ // | Manga | // +---------------------+ type MangaMapping struct { BaseModel Provider string `gorm:"column:provider" json:"provider"` MediaID int `gorm:"column:media_id" json:"mediaId"` MangaID string `gorm:"column:manga_id" json:"mangaId"` // ID from search result, used to fetch chapters } type MangaChapterContainer struct { BaseModel Provider string `gorm:"column:provider" json:"provider"` MediaID int `gorm:"column:media_id" json:"mediaId"` ChapterID string `gorm:"column:chapter_id" json:"chapterId"` Data []byte `gorm:"column:data" json:"data"` } // +---------------------+ // | Online streaming | // +---------------------+ type OnlinestreamMapping struct { BaseModel Provider string `gorm:"column:provider" json:"provider"` MediaID int `gorm:"column:media_id" json:"mediaId"` AnimeID string `gorm:"column:anime_id" json:"anime_id"` // ID from search result, used to fetch episodes } // +---------------------+ // | Debrid | // +---------------------+ type DebridSettings struct { BaseModel Enabled bool `gorm:"column:enabled" json:"enabled"` Provider string `gorm:"column:provider" json:"provider"` ApiKey string `gorm:"column:api_key" json:"apiKey"` //FallbackToDebridStreamingView bool `gorm:"column:fallback_to_debrid_streaming_view" json:"fallbackToDebridStreamingView"` // DEPRECATED IncludeDebridStreamInLibrary bool `gorm:"column:include_debrid_stream_in_library" json:"includeDebridStreamInLibrary"` StreamAutoSelect bool `gorm:"column:stream_auto_select" json:"streamAutoSelect"` StreamPreferredResolution string `gorm:"column:stream_preferred_resolution" json:"streamPreferredResolution"` } type DebridTorrentItem struct { BaseModel TorrentItemID string `gorm:"column:torrent_item_id" json:"torrentItemId"` Destination string `gorm:"column:destination" json:"destination"` Provider string `gorm:"column:provider" json:"provider"` MediaId int `gorm:"column:media_id" json:"mediaId"` } // +---------------------+ // | Plugin | // +---------------------+ type PluginData struct { BaseModel PluginID string `gorm:"column:plugin_id;index" json:"pluginId"` Data []byte `gorm:"column:data" json:"data"` } /////////////////////////////////////////////////////////////////////////// type StringSlice []string func (o *StringSlice) Scan(src interface{}) error { str, ok := src.(string) if !ok { return errors.New("src value cannot cast to string") } *o = strings.Split(str, ",") return nil } func (o StringSlice) Value() (driver.Value, error) { if len(o) == 0 { return nil, nil } return strings.Join(o, ","), nil }