source_utils_nodeHelpers.bs

' Node utility functions for creating and copying SGNodes
namespace nodeHelpers

  ' createQueueItem: Create a queue item AA from a source item.
  '
  ' The queue item is a thin AA containing only what VideoPlayerView and QueueManager need.
  ' LoadVideoContentTask re-fetches all metadata server-side via ItemMetaData(), so the
  ' queue item does NOT need to carry full data — eliminating the deep-copy perf problem.
  '
  ' Accepts either a JellyfinBaseItem node (typed camelCase fields) or a raw Jellyfin API
  ' response AA (PascalCase fields). BrightScript AA lookup is case-insensitive, so field
  ' access works identically for both — e.g. sourceNode.backdropImageTags finds
  ' BackdropImageTags on a raw API AA and backdropImageTags on a typed node.
  '
  ' @param sourceNode - JellyfinBaseItem node or raw Jellyfin API response AA
  ' @returns Associative array with guaranteed field contract, or invalid if sourceNode is invalid
  '
  ' Fields always present in the returned AA (guaranteed contract):
  '   - backdropImageTags: Array of backdrop image tags (empty array = no backdrop)
  '   - parentBackdropImageTags: Array of parent backdrop tags (empty array = none)
  '   - parentBackdropItemId: Parent item ID for parent backdrops (empty string = none)
  '
  ' Fields present only when non-empty / non-zero:
  '   - id: Item identifier (required for playback)
  '   - type: Media type (required for queue routing)
  '   - title: Display title
  '   - mediaSourceId: Media source hint
  '   - runTimeTicks: Runtime duration
  '   - seriesId: Series ID for episode navigation in resume dialog
  '   - seasonId: Season ID for episode navigation in resume dialog
  '   - playbackPositionTicks: Saved resume position
  '
  ' Callers set startingPoint and selectedAudioStreamIndex on the returned AA after creation.
  '
  function createQueueItem(sourceNode as object) as object
    if not isValid(sourceNode)
      print "[nodeHelpers.createQueueItem] ERROR: sourceNode is invalid"
      return invalid
    end if

    ' Thin AA — only fields the queue infrastructure actually reads
    queueItem = {}

    if isValidAndNotEmpty(sourceNode.id) then queueItem.id = sourceNode.id
    if isValidAndNotEmpty(sourceNode.type) then queueItem.type = sourceNode.type
    if isValidAndNotEmpty(sourceNode.title) then queueItem.title = sourceNode.title
    if isValidAndNotEmpty(sourceNode.mediaSourceId) then queueItem.mediaSourceId = sourceNode.mediaSourceId
    if isValid(sourceNode.runTimeTicks) and sourceNode.runTimeTicks > 0
      queueItem.runTimeTicks = sourceNode.runTimeTicks
    end if

    ' Episode navigation fields used by the resume dialog handler
    if isValidAndNotEmpty(sourceNode.seriesId) then queueItem.seriesId = sourceNode.seriesId
    if isValidAndNotEmpty(sourceNode.seasonId) then queueItem.seasonId = sourceNode.seasonId

    ' Resume position — stored so the resume dialog can read it without the original node.
    ' Typed nodes: transformer populates playbackPositionTicks from UserData.
    ' Raw API AAs: fall back to UserData.PlaybackPositionTicks (nested).
    positionTicks = 0
    if isValid(sourceNode.playbackPositionTicks) and sourceNode.playbackPositionTicks > 0
      positionTicks = sourceNode.playbackPositionTicks
    else if isValid(sourceNode.UserData) and isValid(sourceNode.UserData.PlaybackPositionTicks)
      positionTicks = sourceNode.UserData.PlaybackPositionTicks
    end if
    if positionTicks > 0 then queueItem.playbackPositionTicks = positionTicks

    ' Backdrop fields — always set to guarantee callers can check .Count() without isValid().
    ' Empty array/string = no backdrop available for this item.
    queueItem.backdropImageTags = isValid(sourceNode.backdropImageTags) ? sourceNode.backdropImageTags : []
    queueItem.parentBackdropImageTags = isValid(sourceNode.parentBackdropImageTags) ? sourceNode.parentBackdropImageTags : []
    queueItem.parentBackdropItemId = isValidAndNotEmpty(sourceNode.parentBackdropItemId) ? sourceNode.parentBackdropItemId : ""

    return queueItem
  end function

end namespace