source_api_baserequest.bs

' Functions for making requests to the API
function buildParams(params = {} as object) as string
  ' Take an object of parameters and construct the URL query

  param_array = []
  for each field in params.items()
    item = ""
    if type(field.value) = "String" or type(field.value) = "roString"
      item = field.key + "=" + field.value.trim().EncodeUriComponent()
    else if type(field.value) = "roInteger" or type(field.value) = "roInt"
      item = field.key + "=" + stri(field.value).trim()
      'item = field.key + "=" + str(field.value).trim()
    else if type(field.value) = "roFloat"
      item = field.key + "=" + stri(int(field.value)).trim()
    else if type(field.value) = "LongInteger"
      item = field.key + "=" + field.value.toStr().trim()
    else if type(field.value) = "roArray"
      ' TODO handle array params
    else if type(field.value) = "roBoolean"
      if field.value
        item = field.key + "=true"
      else
        item = field.key + "=false"
      end if
    else if field.value = invalid
      item = field.key + "=null"
    else if field <> invalid
      print "Unhandled param type: " + type(field.value)
      item = field.key + "=" + field.value.EncodeUriComponent()
    end if

    if item <> "" then param_array.push(item)
  end for

  return param_array.join("&")
end function

function buildURL(path as string, params = {} as object) as dynamic
  serverURL = get_url()
  if serverURL = invalid then return invalid

  ' Add intial '/' if path does not start with one
  if path.Left(1) = "/"
    full_url = serverURL + path
  else
    full_url = serverURL + "/" + path
  end if

  if params.count() > 0
    full_url = full_url + "?" + buildParams(params)
  end if

  return full_url
end function

function APIRequest(url as string, params = {} as object) as dynamic
  full_url = buildURL(url, params)
  if full_url = invalid then return invalid

  serverURL = m.global.session.server.url
  if serverURL = invalid then return invalid

  req = createObject("roUrlTransfer")
  req.setUrl(full_url)
  req = authRequest(req)
  ' SSL cert
  if serverURL.left(8) = "https://"
    setCertificateAuthority(req)
  end if

  return req
end function

function getJson(req)
  'req.retainBodyOnError(True)
  data = req.GetToString()
  if data = invalid or data = ""
    return invalid
  end if
  json = ParseJson(data)
  return json
end function

function postVoid(req, data = "" as string) as boolean
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.AsyncPostFromString(data)
  resp = wait(30000, req.GetMessagePort())
  if type(resp) <> "roUrlEvent"
    return false
  end if

  if resp.GetResponseCode() = 200
    return true
  end if

  return false
end function

function headVoid(req) as boolean
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.AsyncHead()
  resp = wait(30000, req.GetMessagePort())
  if type(resp) <> "roUrlEvent"
    return false
  end if

  if resp.GetResponseCode() = 200
    return true
  end if

  return false
end function

function getVoid(req) as boolean
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.AsyncGetToString()
  resp = wait(30000, req.GetMessagePort())

  if type(resp) <> "roUrlEvent"
    return false
  end if

  if resp.GetResponseCode() = 200
    return true
  end if

  return false
end function

function postJson(req, data = "" as string)
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.AsyncPostFromString(data)
  resp = wait(30000, req.GetMessagePort())
  if type(resp) <> "roUrlEvent"
    return invalid
  end if

  if resp.getString() = ""
    return invalid
  end if

  json = ParseJson(resp.GetString())

  return json
end function

function deleteVoid(req)
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.SetRequest("DELETE")
  req.GetToString()

  return true
end function

function get_url()
  serverURL = m.global.session.server.url
  if serverURL <> invalid
    if serverURL.right(1) = "/"
      serverURL = serverURL.left(serverURL.len() - 1)
    end if

    ' append http:// to the start if not specified
    if serverURL.left(7) <> "http://" and serverURL.left(8) <> "https://"
      serverURL = "http://" + serverURL
    end if
  end if
  return serverURL
end function

function getString(req)
  data = req.GetToString()
  return data
end function

function postString(req, data = "" as string)
  req.setMessagePort(CreateObject("roMessagePort"))
  req.AddHeader("Content-Type", "application/json")
  req.AsyncPostFromString(data)
  resp = wait(30000, req.GetMessagePort())
  if type(resp) <> "roUrlEvent"
    return invalid
  end if

  return resp.getString()
end function

' sets the certificate authority by file path on the passed node
sub setCertificateAuthority(request as object) as void
  request.setCertificatesFile("common:/certs/ca-bundle.crt")
end sub

' Takes and returns a roUrlTransfer object after adding a Jellyfin "Authorization" header
function authRequest(req as object) as object
  req.AddHeader("Authorization", buildAuthHeader())
  return req
end function

' Returns a string containing the "Authorization" header payload
function buildAuthHeader() as string
  QUOTE = Chr(34)
  auth = "MediaBrowser" + " Client=" + QUOTE + "JellyRock" + QUOTE
  auth = auth + ", Device=" + QUOTE + m.global.device.name + " (" + m.global.device.model + ")" + QUOTE
  auth = auth + ", Version=" + QUOTE + m.global.app.version + QUOTE

  if m.global.session.user.id <> invalid
    auth = auth + ", UserId=" + QUOTE + m.global.session.user.id + QUOTE
  end if

  auth = auth + ", DeviceId=" + QUOTE + m.global.device.serverDeviceName + QUOTE

  if m.global.session.user.authToken <> invalid
    auth = auth + ", Token=" + QUOTE + m.global.session.user.authToken + QUOTE
  end if

  return auth
end function