source_data_SessionDataTransformer.bs
import "pkg:/source/utils/misc.bs"
' Transforms session data (server, user, app, device) to content nodes
class SessionDataTransformer
sub new()
end sub
' Transform server info to JellyfinServer content node
function transformServerInfo(serverData as object) as object
if not isValid(serverData)
return invalid
end if
try
serverNode = CreateObject("roSGNode", "JellyfinServer")
serverNode.id = serverData.id ?? ""
serverNode.name = serverData.name ?? ""
serverNode.version = serverData.version ?? ""
serverNode.productName = serverData.productName ?? ""
serverNode.operatingSystem = serverData.operatingSystem ?? ""
serverNode.serverUrl = serverData.serverUrl ?? ""
serverNode.localURL = serverData.localURL ?? ""
serverNode.isHTTPS = serverData.isHTTPS ?? false
serverNode.isLocalHTTPS = serverData.isLocalHTTPS ?? false
serverNode.startupWizardCompleted = serverData.startupWizardCompleted ?? false
' Set loading state
serverNode.isLoaded = true
serverNode.isConnected = true
serverNode.lastConnected = CreateObject("roDateTime").AsSeconds()
#if debug
serverNode.rawServerInfo = serverData
#end if
return serverNode
catch error
return invalid
end try
end function
' Transform user info to JellyfinUser content node (flattened structure)
function transformUserInfo(userData as object) as object
if not isValid(userData)
return invalid
end if
try
userNode = CreateObject("roSGNode", "JellyfinUser")
' User identity
userNode.id = userData.id ?? ""
userNode.name = userData.name ?? ""
userNode.friendlyName = userData.friendlyName ?? ""
userNode.authToken = userData.authToken ?? ""
userNode.primaryImageTag = userData.primaryImageTag ?? ""
userNode.lastRunVersion = userData.lastRunVersion ?? ""
userNode.fontScaleFactor = userData.fontScaleFactor ?? 1.0
' Transform configuration to nested content node
if isValid(userData.Configuration)
userNode.config = m.transformUserConfiguration(userData.Configuration)
else
' Create empty config node with defaults
userNode.config = CreateObject("roSGNode", "JellyfinUserConfiguration")
end if
' Transform policy to nested content node
if isValid(userData.Policy)
userNode.policy = m.transformUserPolicy(userData.Policy)
else
' Create empty policy node with defaults
userNode.policy = CreateObject("roSGNode", "JellyfinUserPolicy")
end if
' Transform user settings (nested node)
if isValid(userData.settings)
userNode.settings = m.transformUserSettings(userData.settings)
else
' Create empty settings node
userNode.settings = CreateObject("roSGNode", "JellyfinUserSettings")
end if
' Set loading state
userNode.isLoaded = true
userNode.loadedAt = CreateObject("roDateTime").AsSeconds()
#if debug
userNode.rawUserData = userData
#end if
return userNode
catch error
return invalid
end try
end function
' Transform user settings (from registry and API)
function transformUserSettings(settingsData as object) as object
settingsNode = CreateObject("roSGNode", "JellyfinUserSettings")
if not isValid(settingsData)
return settingsNode
end if
' Playback Settings
settingsNode.playbackBitrateMaxLimited = toBoolean(settingsData["playbackBitrateMaxLimited"])
if settingsData.DoesExist("playbackBitrateLimit")
settingsNode.playbackBitrateLimit = Val(settingsData["playbackBitrateLimit"])
end if
settingsNode.playbackCinemaMode = toBoolean(settingsData["playbackCinemaMode"])
settingsNode.playbackDecodeMultichannelAudio = toBoolean(settingsData["playbackDecodeMultichannelAudio"])
settingsNode.playbackSubsCustom = toBoolean(settingsData["playbackSubsCustom"])
settingsNode.playbackResolutionMax = settingsData["playbackResolutionMax"] ?? ""
if settingsData.DoesExist("playbackNextUpButtonSeconds")
settingsNode.playbackNextUpButtonSeconds = Val(settingsData["playbackNextUpButtonSeconds"])
end if
settingsNode.playbackPlayNextEpisode = settingsData["playbackPlayNextEpisode"] ?? ""
settingsNode.playbackPlayDefaultAudioTrack = settingsData["playbackPlayDefaultAudioTrack"] ?? ""
settingsNode.playbackPreferredMultichannelCodec = settingsData["playbackPreferredMultichannelCodec"] ?? ""
settingsNode.playbackSubsOnlyText = toBoolean(settingsData["playbackSubsOnlyText"])
settingsNode.playbackMpeg2 = toBoolean(settingsData["playbackMpeg2"])
settingsNode.playbackMpeg4 = toBoolean(settingsData["playbackMpeg4"])
settingsNode.playbackTryDirectH264ProfileLevel = toBoolean(settingsData["playbackTryDirectH264ProfileLevel"])
settingsNode.playbackTryDirectHevcProfileLevel = toBoolean(settingsData["playbackTryDirectHevcProfileLevel"])
' UI Settings
settingsNode.uiGeneralEpisodeImages = settingsData["uiGeneralEpisodeImages"] ?? ""
settingsNode.uiFontFallback = toBoolean(settingsData["uiFontFallback"])
settingsNode.uiDesignHideClock = toBoolean(settingsData["uiDesignHideClock"])
if settingsData.DoesExist("uiDetailsMaxDaysNextUp")
settingsNode.uiDetailsMaxDaysNextUp = Val(settingsData["uiDetailsMaxDaysNextUp"])
end if
settingsNode.uiDetailsEnableRewatchingNextUp = toBoolean(settingsData["uiDetailsEnableRewatchingNextUp"])
settingsNode.uiRowLayout = settingsData["uiRowLayout"] ?? ""
settingsNode.uiHomeSplashBackground = toBoolean(settingsData["uiHomeSplashBackground"])
settingsNode.uiHomeUseWebArrangement = toBoolean(settingsData["uiHomeUseWebArrangement"])
settingsNode.uiDetailsHideTagline = toBoolean(settingsData["uiDetailsHideTagline"])
settingsNode.uiMoviesShowRatings = toBoolean(settingsData["uiMoviesShowRatings"])
settingsNode.uiTvShowsBlurUnwatched = toBoolean(settingsData["uiTvShowsBlurUnwatched"])
settingsNode.uiTvShowsDisableCommunityRating = toBoolean(settingsData["uiTvShowsDisableCommunityRating"])
settingsNode.uiTvShowsDisableUnwatchedCount = toBoolean(settingsData["uiTvShowsDisableUnwatchedCount"])
settingsNode.uiTvShowsGoStraightToEpisodes = toBoolean(settingsData["uiTvShowsGoStraightToEpisodes"])
' Item Grid Settings
settingsNode.itemGridShowItemCount = toBoolean(settingsData["itemGridShowItemCount"])
settingsNode.itemGridTitles = settingsData["itemGridTitles"] ?? ""
settingsNode.itemGridReset = toBoolean(settingsData["itemGridReset"])
settingsNode.itemGridMovieDefaultView = settingsData["itemGridMovieDefaultView"] ?? ""
' Home sections
settingsNode.homeSection0 = settingsData["homeSection0"] ?? ""
settingsNode.homeSection1 = settingsData["homeSection1"] ?? ""
settingsNode.homeSection2 = settingsData["homeSection2"] ?? ""
settingsNode.homeSection3 = settingsData["homeSection3"] ?? ""
settingsNode.homeSection4 = settingsData["homeSection4"] ?? ""
settingsNode.homeSection5 = settingsData["homeSection5"] ?? ""
settingsNode.homeSection6 = settingsData["homeSection6"] ?? ""
' Display settings
settingsNode.displayLiveTvLanding = settingsData["displayLiveTvLanding"] ?? ""
' Transform display settings into nested structure
' Build local AA first, then assign to node field
displaySettings = {}
for each key in settingsData.Keys()
if key.StartsWith("display.")
' Parse "display.libraryId.settingKey" → nested object
parts = key.Split(".")
if parts.Count() = 3 and isValidAndNotEmpty(parts[1]) and isValidAndNotEmpty(parts[2])
libraryId = parts[1]
settingKey = parts[2]
' Create library object if doesn't exist
if not displaySettings.DoesExist(libraryId)
displaySettings[libraryId] = {}
end if
' Store setting value with proper type conversion
rawValue = settingsData[key]
' Convert boolean fields from string to actual boolean
if settingKey = "sortAscending"
displaySettings[libraryId][settingKey] = toBoolean(rawValue)
else
' Other fields stored as-is (strings, etc.)
displaySettings[libraryId][settingKey] = rawValue
end if
end if
end if
end for
' Assign to content node field
settingsNode.displaySettings = displaySettings
' Global settings are NOT loaded here - they come from "JellyRock" registry section
' and are loaded separately via user.settings.LoadGlobals()
' Set loading state
settingsNode.isLoaded = true
settingsNode.loadedAt = CreateObject("roDateTime").AsSeconds()
' Debug-only: Store raw settings for debugging
#if debug
settingsNode.rawSettings = settingsData
#end if
return settingsNode
end function
' Transform app info to AppInfo content node
function transformAppInfo(appData as object) as object
if not isValid(appData)
return invalid
end if
app = CreateObject("roSGNode", "AppInfo")
app.appId = appData.id ?? ""
app.isDev = appData.isDev ?? false
app.version = appData.version ?? ""
app.lastRunVersion = appData.lastRunVersion ?? ""
return app
end function
' Transform device info to DeviceInfo content node
function transformDeviceInfo(deviceData as object) as object
if not isValid(deviceData)
return invalid
end if
device = CreateObject("roSGNode", "DeviceInfo")
device.id = deviceData.id ?? ""
device.name = deviceData.name ?? ""
device.model = deviceData.model ?? ""
device.serial = deviceData.serial ?? ""
device.friendlyName = deviceData.friendlyName ?? ""
device.serverDeviceName = deviceData.serverDeviceName ?? ""
device.modelType = deviceData.modelType ?? ""
device.videoHeight = deviceData.videoHeight ?? 1080
device.videoWidth = deviceData.videoWidth ?? 1920
device.maxVideoHeight = deviceData.maxVideoHeight ?? 1080
device.maxVideoWidth = deviceData.maxVideoWidth ?? 1920
device.canPlay4K = deviceData.canPlay4K ?? false
if isValid(deviceData.videoCodecs)
device.videoCodecs = deviceData.videoCodecs
end if
if isValid(deviceData.audioCodecs)
device.audioCodecs = deviceData.audioCodecs
end if
device.memoryLevel = deviceData.memoryLevel ?? "normal"
device.memoryTotal = deviceData.memoryTotal ?? 0
device.modelDetails = deviceData.modelDetails ?? {}
return device
end function
' Transform user configuration to JellyfinUserConfiguration content node
' Configuration is SERVER-AUTHORITATIVE and should never be loaded from registry
function transformUserConfiguration(configData as object) as object
config = CreateObject("roSGNode", "JellyfinUserConfiguration")
if not isValid(configData)
return config
end if
' Audio Settings
config.audioLanguagePreference = configData.AudioLanguagePreference ?? ""
config.playDefaultAudioTrack = configData.PlayDefaultAudioTrack ?? true
config.rememberAudioSelections = configData.RememberAudioSelections ?? false
' Subtitle Settings
config.subtitleMode = configData.SubtitleMode ?? "Default"
config.subtitleLanguagePreference = configData.SubtitleLanguagePreference ?? ""
config.rememberSubtitleSelections = configData.RememberSubtitleSelections ?? false
' Display Settings
config.displayMissingEpisodes = configData.DisplayMissingEpisodes ?? false
config.displayCollectionsView = configData.DisplayCollectionsView ?? false
config.hidePlayedInLatest = configData.HidePlayedInLatest ?? true
config.useEpisodeImagesInNextUpAndResume = configData.useEpisodeImagesInNextUpAndResume ?? false
' Playback Settings
config.enableNextEpisodeAutoPlay = configData.EnableNextEpisodeAutoPlay ?? false
' View Organization
config.orderedViews = configData.OrderedViews ?? []
config.latestItemsExcludes = configData.LatestItemsExcludes ?? []
config.myMediaExcludes = configData.MyMediaExcludes ?? []
config.groupedFolders = configData.GroupedFolders ?? []
' Other Settings
config.castReceiverId = configData.CastReceiverId ?? ""
config.enableLocalPassword = configData.EnableLocalPassword ?? false
#if debug
config.rawConfigData = configData
#end if
return config
end function
' Transform user policy to JellyfinUserPolicy content node
' Policy is SERVER-AUTHORITATIVE and should never be loaded from registry
function transformUserPolicy(policyData as object) as object
policy = CreateObject("roSGNode", "JellyfinUserPolicy")
if not isValid(policyData)
return policy
end if
' User Status
policy.isAdministrator = policyData.IsAdministrator ?? false
policy.isDisabled = policyData.IsDisabled ?? false
policy.isHidden = policyData.IsHidden ?? false
' Authentication Settings
policy.authenticationProviderId = policyData.AuthenticationProviderId ?? ""
policy.passwordResetProviderId = policyData.PasswordResetProviderId ?? ""
policy.invalidLoginAttemptCount = policyData.InvalidLoginAttemptCount ?? 0
policy.loginAttemptsBeforeLockout = policyData.LoginAttemptsBeforeLockout ?? - 1
' Media Playback Permissions
policy.enableMediaPlayback = policyData.EnableMediaPlayback ?? true
policy.enableAudioPlaybackTranscoding = policyData.EnableAudioPlaybackTranscoding ?? true
policy.enableVideoPlaybackTranscoding = policyData.EnableVideoPlaybackTranscoding ?? true
policy.enablePlaybackRemuxing = policyData.EnablePlaybackRemuxing ?? true
policy.forceRemoteSourceTranscoding = policyData.ForceRemoteSourceTranscoding ?? false
' Content Access Permissions
policy.enableAllFolders = policyData.EnableAllFolders ?? false
policy.enabledFolders = policyData.EnabledFolders ?? []
policy.enableContentDownloading = policyData.EnableContentDownloading ?? true
policy.enableContentDeletion = policyData.EnableContentDeletion ?? false
policy.enableContentDeletionFromFolders = policyData.EnableContentDeletionFromFolders ?? []
' Live TV Permissions
policy.enableLiveTvAccess = policyData.EnableLiveTvAccess ?? true
policy.enableLiveTvManagement = policyData.EnableLiveTvManagement ?? false
' Channel Permissions
policy.enableAllChannels = policyData.EnableAllChannels ?? false
policy.enabledChannels = policyData.EnabledChannels ?? []
policy.blockedChannels = policyData.BlockedChannels ?? []
' Device Permissions
policy.enableAllDevices = policyData.EnableAllDevices ?? true
policy.enabledDevices = policyData.EnabledDevices ?? []
policy.maxActiveSessions = policyData.MaxActiveSessions ?? 0
' Remote Access Permissions
policy.enableRemoteAccess = policyData.EnableRemoteAccess ?? true
policy.enableRemoteControlOfOtherUsers = policyData.EnableRemoteControlOfOtherUsers ?? false
policy.enableSharedDeviceControl = policyData.EnableSharedDeviceControl ?? true
policy.remoteClientBitrateLimit = policyData.RemoteClientBitrateLimit ?? 0
' Content Management Permissions
policy.enableCollectionManagement = policyData.EnableCollectionManagement ?? false
policy.enableSubtitleManagement = policyData.EnableSubtitleManagement ?? false
policy.enableLyricManagement = policyData.EnableLyricManagement ?? false
' Sharing and Sync Permissions
policy.enablePublicSharing = policyData.EnablePublicSharing ?? true
policy.enableSyncTranscoding = policyData.EnableSyncTranscoding ?? true
policy.enableMediaConversion = policyData.EnableMediaConversion ?? true
' User Preferences
policy.enableUserPreferenceAccess = policyData.EnableUserPreferenceAccess ?? true
' Content Filtering
policy.blockedMediaFolders = policyData.BlockedMediaFolders ?? []
policy.blockedTags = policyData.BlockedTags ?? []
policy.allowedTags = policyData.AllowedTags ?? []
policy.blockUnratedItems = policyData.BlockUnratedItems ?? []
' Access Schedules
policy.accessSchedules = policyData.AccessSchedules ?? []
' Sync Play
policy.syncPlayAccess = policyData.SyncPlayAccess ?? "CreateAndJoinGroups"
#if debug
policy.rawPolicyData = policyData
#end if
return policy
end function
end class