protected function downloadFile($url, $file, callable $onProgress, callable $onFinish)
	{
		$videoFile = fopen($file, 'a');
		$options = array(
			'sink' => $videoFile,
			'verify' => false,
			'timeout' => 0,
			'connect_timeout' => 50,
			'progress' => $onProgress,
			'cookies' => new CookieJar(false, [['url' => 'https://www.youtube.com/watch?v=' . $this->videoId]])
		);

		$request = new Request('get', $url);
		$promise = $this->webClient->sendAsync($request, $options);
		$promise->then(
			function (ResponseInterface $response) use ($videoFile, $file, $onFinish) {
				fclose($videoFile);

				$size = filesize($file);
				$remained = intval((string)$response->getHeader('Content-Length')[0]);

				$onFinish($size, $remained);
			},
			function (GuzzleException $e) {
				if ($e instanceof ClientException && $e->hasResponse())
					throw new YoutubeException($e->getResponse()->getReasonPhrase(), 4);
				else
					throw new YoutubeException($e->getMessage(), 4);
			}
		);

		try {
			$promise->wait();
		} catch (ClientException $e) {
			throw new YoutubeException('An error occurred when downloading video', 20, $e);
		} catch (\RuntimeException $e) {
			throw new YoutubeException('An error occurred when downloading video', 20, $e);
		}
	}