@startuml class-data !theme plain title sofarr — Data Model Diagram skinparam classAttributeIconSize 0 package "External API Responses" { class "SABnzbd Queue Slot" as sabq { + filename : string + nzbname : string + percentage : string + mb : string + mbmissing : string + size : string + timeleft : string + status : string + storage : string } class "SABnzbd History Slot" as sabh { + name : string + nzb_name : string + nzbname : string + status : string + size : string + completed_time : string + storage : string } class "Sonarr Queue Record" as sqr { + id : number + seriesId : number + series : SonarrSeries + title : string + sourceTitle : string + trackedDownloadStatus : string + trackedDownloadState : string + statusMessages : StatusMessage[] + errorMessage : string } class "Sonarr History Record" as shr { + id : number + seriesId : number + title : string + sourceTitle : string + eventType : string } class "SonarrSeries" as ss { + id : number + title : string + titleSlug : string + path : string + tags : number[] + images : Image[] + _instanceUrl : string } class "Radarr Queue Record" as rqr { + id : number + movieId : number + movie : RadarrMovie + title : string + sourceTitle : string + trackedDownloadStatus : string + trackedDownloadState : string + statusMessages : StatusMessage[] + errorMessage : string } class "Radarr History Record" as rhr { + id : number + movieId : number + title : string + sourceTitle : string + eventType : string } class "RadarrMovie" as rm { + id : number + title : string + titleSlug : string + path : string + tags : number[] + images : Image[] + _instanceUrl : string } class "Tag" as tag { + id : number + label : string } class "Image" as img { + coverType : string + remoteUrl : string + url : string } class "StatusMessage" as sm { + title : string + messages : string[] } class "qBittorrent Torrent" as qbt { + name : string + hash : string + size : number + completed : number + progress : number (0-1) + state : string + dlspeed : number + eta : number + num_seeds : number + num_leechs : number + availability : number + category : string + tags : string + save_path : string + content_path : string + instanceId : string + instanceName : string } class "Emby User" as eu { + Id : string + Name : string + Policy : { IsAdministrator: boolean } } sqr *-- ss : embedded\n(includeSeries) rqr *-- rm : embedded\n(includeMovie) sqr *-- sm rqr *-- sm ss *-- img rm *-- img } package "sofarr Internal Models" { class "Download Object" as dl { + type : 'series' | 'movie' | 'torrent' + title : string + coverArt : string | null + status : string + progress : string + mb : string + mbmissing : string + size : string + speed : string + eta : string + seriesName : string | null + movieName : string | null + episodeInfo : object | null + movieInfo : object | null + userTag : string + importIssues : string[] | null + downloadPath : string | null + targetPath : string | null + arrLink : string | null + qbittorrent : boolean + seeds : number + peers : number + availability : string + rawSize : number + rawSpeed : number + rawEta : number + hash : string + category : string + completedAt : string } class "API Response\n/user-downloads" as apir { + user : string + isAdmin : boolean + downloads : Download[] } class "Status Response\n/status" as statr { + server : ServerInfo + polling : PollingInfo + cache : CacheStats + clients : ClientInfo[] } class "ServerInfo" as si { + uptimeSeconds : number + nodeVersion : string + memoryUsageMB : number + heapUsedMB : number + heapTotalMB : number } class "PollingInfo" as pi { + enabled : boolean + intervalMs : number + lastPoll : PollTimings } class "Session Cookie\nemby_user" as cookie { + id : string + name : string + isAdmin : boolean + token : string } apir *-- dl statr *-- si statr *-- pi } ' Data flow connections sabq ..> dl : matched &\ntransformed sabh ..> dl : matched &\ntransformed qbt ..> dl : mapTorrentToDownload() ss ..> dl : coverArt, seriesName,\npath, tags rm ..> dl : coverArt, movieName,\npath, tags tag ..> dl : userTag resolution eu ..> cookie : login creates @enduml