start method

Future<void> start()

Starts or resumes downloading.

Implementation

Future<void> start() async {
  try {
    _setStatus(TaskStatus.working);
    logger?.info('task: Starting connection...');
    var head = await _worker.connect(originalURL);
    logger?.info('task: Remote head: ${head.url}:${head.size}');

    // Setup dumper.
    var dumper = await Dumper.loadOrCreate(destFile, head, logger);
    _dumper = dumper;
    logger?.info(
        'task: Dumper created with state:\n${dumper.currentState.toJSON()}\n');
    var state = dumper.currentState;
    if (head.size == 0) {
      // Empty remote file, complete the task immediately.
      await _complete();
      return;
    }
    if (head.size < 0) {
      logger?.info('task: Head.size unknown');
      // Remote size unknown, discard local data, and start from zero.
      await _resetData(state, dumper);
    }

    var canResume = await _worker.canResume(head);
    logger?.info('task: Can resume? $canResume');
    if (canResume) {
      // Set dumper position to last downloaded position.
      await dumper.seek(state.transferred);
      logger?.info('task: Dumper position set to: ${state.transferred}');
    } else {
      // If remove size is unknown, the dumper has been truncated to 0 here.
      // Otherwise, reset dumper position to 0.
      if (head.size > 0) {
        await _resetData(state, dumper);
      }
    }

    logger?.info('task: Preparing...');
    var stateToBeUpdated = await _worker.prepare(state);
    if (stateToBeUpdated != null) {
      logger?.info(
          'task: [prepare] returned state ${stateToBeUpdated.toJSON()}');
      state = stateToBeUpdated;
      await dumper.writeState(state);
    }

    onBeforeDownload?.call(dumper.currentState, dumper.position);
    logger?.info('task: Downloading...');
    var dataStream = await _worker.start(state);

    await for (var body in dataStream) {
      logger?.verbose(
          'task: Body received: ${body.data.length}, poz: ${body.position}');
      var poz = body.position;
      if (poz != null) {
        logger?.verbose('task: Seek: $poz');
        await dumper.seek(poz);
      }
      await dumper.writeData(body.data);

      // Update state.
      state.transferred += body.data.length;
      onProgress?.call(TaskProgress(state.transferred, head.size));
      logger?.verbose('task: Progress: ${state.transferred}/${head.size}');

      if (head.size >= 0 && state.transferred > head.size) {
        throw Exception(
            'task: Remote file overflow (${state.transferred}/${head.size}).');
      }
      if (state.transferred == head.size) {
        await _complete();
        return;
      } else {
        await dumper.writeState(state);
      }
    }
    await _worker.transferCompleted();
    await _closeWorker();

    logger?.info('task: Data transfer done');
    // Complete the task if remote size is unknown.
    if (head.size == -1) {
      await _complete();
    } else {
      await close();
    }
  } catch (ex) {
    _setStatus(TaskStatus.error);
    logger?.error('task: FATAL: $ex');
    await close();
    rethrow;
  }
}