docs: async def download_file(
              self, input_location, file=None, *, part_size_kb=None,
              file_size=None, progress_callback=None, dc_id=None):
          """
          Downloads the given input location to a file.
  
          Args:
              input_location (:tl:`InputFileLocation`):
                  The file location from which the file will be downloaded.
                  See `telethon.utils.get_input_location` source for a complete
                  list of supported types.
  
              file (`str` | `file`, optional):
                  The output file path, directory, or stream-like object.
                  If the path exists and is a file, it will be overwritten.
  
                  If the file path is ``None`` or ``bytes``, then the result
                  will be saved in memory and returned as `bytes`.
  
              part_size_kb (`int`, optional):
                  Chunk size when downloading files. The larger, the less
                  requests will be made (up to 512KB maximum).
  
              file_size (`int`, optional):
                  The file size that is about to be downloaded, if known.
                  Only used if ``progress_callback`` is specified.
  
              progress_callback (`callable`, optional):
                  A callback function accepting two parameters:
                  ``(downloaded bytes, total)``. Note that the
                  ``total`` is the provided ``file_size``.
  
              dc_id (`int`, optional):
                  The data center the library should connect to in order
                  to download the file. You shouldn't worry about this.
          """
          if not part_size_kb:
              if not file_size:
                  part_size_kb = 64  # Reasonable default
              else:
                  part_size_kb = utils.get_appropriated_part_size(file_size)
  
          part_size = int(part_size_kb * 1024)
          # https://core.telegram.org/api/files says:
          # > part_size % 1024 = 0 (divisible by 1KB)
          #
          # But https://core.telegram.org/cdn (more recent) says:
          # > limit must be divisible by 4096 bytes
          # So we just stick to the 4096 limit.
          if part_size % 4096 != 0:
              raise ValueError(
                  'The part size must be evenly divisible by 4096.')
  
          in_memory = file is None or file is bytes
          if in_memory:
              f = io.BytesIO()
          elif isinstance(file, str):
              # Ensure that we'll be able to download the media
              helpers.ensure_parent_dir_exists(file)
              f = open(file, 'wb')
          else:
              f = file
  
          old_dc = dc_id
          dc_id, input_location = utils.get_input_location(input_location)
          if dc_id is None:
              dc_id = old_dc
  
          exported = dc_id and self.session.dc_id != dc_id
          if exported:
              try:
                  sender = await self._borrow_exported_sender(dc_id)
              except errors.DcIdInvalidError:
                  # Can't export a sender for the ID we are currently in
                  config = await self(functions.help.GetConfigRequest())
                  for option in config.dc_options:
                      if option.ip_address == self.session.server_address:
                          self.session.set_dc(
                              option.id, option.ip_address, option.port)
                          self.session.save()
                          break
  
                  # TODO Figure out why the session may have the wrong DC ID
                  sender = self._sender
                  exported = False
          else:
              # The used sender will also change if ``FileMigrateError`` occurs
              sender = self._sender
  
          self._log[__name__].info('Downloading file in chunks of %d bytes',
                                   part_size)
          try:
              offset = 0
              while True:
                  try:
                      result = await sender.send(functions.upload.GetFileRequest(
                          input_location, offset, part_size
                      ))
                      if isinstance(result, types.upload.FileCdnRedirect):
                          # TODO Implement
                          raise NotImplementedError
                  except errors.FileMigrateError as e:
                      self._log[__name__].info('File lives in another DC')
                      sender = await self._borrow_exported_sender(e.new_dc)
                      exported = True
                      continue
  
                  offset += part_size
                  if not result.bytes:
                      if in_memory:
                          f.flush()
                          return f.getvalue()
                      else:
                          return getattr(result, 'type', '')
  
                  self._log[__name__].debug('Saving %d more bytes',
                                            len(result.bytes))
                  f.write(result.bytes)
                  if progress_callback:
                      progress_callback(f.tell(), file_size)
          finally:
              if exported:
                  await self._return_exported_sender(sender)
              elif sender != self._sender:
                  await sender.disconnect()
              if isinstance(file, str) or in_memory:
                  f.close()