示例#1
0
 private async Task<string> DownloadClientAsync([NotNull] CookieAwareWebClient client,
         [NotNull] FlexibleLoaderGetPreferredDestinationCallback getPreferredDestination,
         [CanBeNull] FlexibleLoaderReportDestinationCallback reportDestination, [CanBeNull] Func<bool> checkIfPaused,
         CancellationToken cancellation) {
     cancellation.Register(client.CancelAsync);
     var information = FlexibleLoaderMetaInformation.FromLoader(this);
     var destination = getPreferredDestination(Url, information);
     var filename = FileUtils.EnsureUnique(true, destination.Filename);
     reportDestination?.Invoke(filename);
     await client.DownloadFileTaskAsync(Url, filename).ConfigureAwait(false);
     return filename;
 }
示例#2
0
        public async Task <string> DownloadAsync(CookieAwareWebClient client,
                                                 FlexibleLoaderGetPreferredDestinationCallback getPreferredDestination,
                                                 FlexibleLoaderReportDestinationCallback reportDestination, Func <bool> checkIfPaused,
                                                 IProgress <long> progress, CancellationToken cancellation)
        {
            try {
                // TODO: Resume download?
                var d = getPreferredDestination(_uri.OriginalString, FlexibleLoaderMetaInformation.FromLoader(this));
                if (File.Exists(d.Filename))
                {
                    if (d.CanResumeDownload && new FileInfo(d.Filename).Length == TotalSize)
                    {
                        return(d.Filename);
                    }

                    File.Delete(d.Filename);
                }

                await _client.DownloadFileAsync(_uri, d.Filename, new Progress <double>(x => progress?.Report((long)((TotalSize ?? 0d) * x / 100))), cancellation);

                return(d.Filename);
            } catch (Exception e) when(IsBandwidthLimitExceeded(e))
            {
                throw new InformativeException("Bandwidth limit exceeded", "That’s Mega.nz for you.", e);
            } catch (ApiException e) {
                WindowsHelper.ViewInBrowser(_uri);
                throw new InformativeException("Unsupported link", "Please download it manually.", e);
            }

            bool IsBandwidthLimitExceeded(Exception e)
            {
                if (e is AggregateException ae)
                {
                    return(IsBandwidthLimitExceeded(ae.InnerException) || ae.InnerExceptions.Any(IsBandwidthLimitExceeded));
                }

                return(e is HttpRequestException && e.Message.Contains(@"509"));
            }
        }
示例#3
0
 public override string GetFootprint(FlexibleLoaderMetaInformation information, WebHeaderCollection headers)
 {
     return($"filename={information.FileName}, size={information.TotalSize}, checksum={headers?["X-Goog-Hash"]}".ToCutBase64());
 }
示例#4
0
        public static async Task <string> LoadAsyncTo(string argument,
                                                      FlexibleLoaderGetPreferredDestinationCallback getPreferredDestination, [CanBeNull] FlexibleLoaderReportDestinationCallback reportDestination,
                                                      Action <FlexibleLoaderMetaInformation> reportMetaInformation = null, Func <bool> checkIfPaused = null,
                                                      IProgress <AsyncProgressEntry> progress = null, CancellationToken cancellation = default)
        {
            progress?.Report(AsyncProgressEntry.FromStringIndetermitate("Finding fitting loader…"));
            var loader = await CreateLoaderAsync(argument, cancellation) ?? throw new OperationCanceledException();

            try {
                using (var order = KillerOrder.Create(new CookieAwareWebClient(), TimeSpan.FromMinutes(10))) {
                    var client = order.Victim;

                    if (_proxy != null)
                    {
                        client.Proxy = _proxy;
                    }

                    progress?.Report(AsyncProgressEntry.Indetermitate);

                    cancellation.ThrowIfCancellationRequested();
                    cancellation.Register(client.CancelAsync);

                    if (!await loader.PrepareAsync(client, cancellation))
                    {
                        throw new InformativeException("Can’t load file", "Loader preparation failed.");
                    }

                    cancellation.ThrowIfCancellationRequested();
                    reportMetaInformation?.Invoke(FlexibleLoaderMetaInformation.FromLoader(loader));

                    var initialProgressCallback = true;
                    var reportStopwatch         = Stopwatch.StartNew();
                    var progressStopwatch       = new AsyncProgressBytesStopwatch();

                    if (loader.UsesClientToDownload)
                    {
                        client.DownloadProgressChanged += (sender, args) => {
                            if (initialProgressCallback)
                            {
                                reportMetaInformation?.Invoke(FlexibleLoaderMetaInformation.FromLoader(loader));
                                initialProgressCallback = false;
                            }

                            if (reportStopwatch.Elapsed.TotalMilliseconds < 20)
                            {
                                return;
                            }
                            order.Delay();
                            reportStopwatch.Restart();
                            progress?.Report(AsyncProgressEntry.CreateDownloading(args.BytesReceived, args.TotalBytesToReceive == -1 &&
                                                                                  loader.TotalSize.HasValue ? Math.Max(loader.TotalSize.Value, args.BytesReceived) : args.TotalBytesToReceive,
                                                                                  progressStopwatch));
                        };
                    }

                    var loaded = await loader.DownloadAsync(client, getPreferredDestination, reportDestination, checkIfPaused,
                                                            loader.UsesClientToDownload?null : new Progress <long>(p => {
                        if (initialProgressCallback)
                        {
                            reportMetaInformation?.Invoke(FlexibleLoaderMetaInformation.FromLoader(loader));
                            initialProgressCallback = false;
                        }

                        if (reportStopwatch.Elapsed.TotalMilliseconds < 20)
                        {
                            return;
                        }
                        order.Delay();
                        reportStopwatch.Restart();
                        progress?.Report(loader.TotalSize.HasValue ? AsyncProgressEntry.CreateDownloading(p, loader.TotalSize.Value, progressStopwatch)
                                        : new AsyncProgressEntry(string.Format(UiStrings.Progress_Downloading, p.ToReadableSize(1)), null));
                    }), cancellation);

                    cancellation.ThrowIfCancellationRequested();
                    Logging.Write("Loaded: " + loaded);
                    return(loaded);
                }
            } catch (Exception e) when(cancellation.IsCancellationRequested || e.IsCancelled())
            {
                Logging.Warning("Cancelled");
                throw new OperationCanceledException();
            } catch (Exception e) {
                Logging.Warning(e);
                throw;
            }
        }
示例#5
0
        private async Task <string> DownloadResumeSupportAsync([NotNull] CookieAwareWebClient client,
                                                               [NotNull] FlexibleLoaderGetPreferredDestinationCallback getPreferredDestination,
                                                               [CanBeNull] FlexibleLoaderReportDestinationCallback reportDestination, [CanBeNull] Func <bool> checkIfPaused,
                                                               IProgress <long> progress, CancellationToken cancellation)
        {
            // Common variables
            string filename = null, selectedDestination = null, actualFootprint = null;
            Stream remoteData = null;

            var resumeSupported = ResumeSupported;

            try {
                // Read resume-related data and remove it to avoid conflicts
                var resumeDestination             = CacheStorage.Get <string>(_keyDestination);
                var resumePartiallyLoadedFilename = CacheStorage.Get <string>(_keyPartiallyLoadedFilename);
                var resumeLastWriteDate           = CacheStorage.Get <DateTime?>(_keyLastWriteDate);
                var resumePreviousFootprint       = CacheStorage.Get <string>(_keyFootprint);
                ClearResumeData();

                // Collect known information for destination callback
                var information = FlexibleLoaderMetaInformation.FromLoader(this);

                // Opening stream to read…
                var headRequest = HeadRequestSupported && resumeDestination != null;
                using (headRequest ? client.SetMethod("HEAD") : null) {
                    Logging.Warning($"Initial request: {(headRequest ? "HEAD" : "GET")}");
                    remoteData = await client.OpenReadTaskAsync(Url);
                }

                cancellation.ThrowIfCancellationRequested();

                // Maybe we’ll be lucky enough to load the most accurate data
                if (client.ResponseHeaders != null)
                {
                    if (long.TryParse(client.ResponseHeaders[HttpResponseHeader.ContentLength] ?? "",
                                      NumberStyles.Any, CultureInfo.InvariantCulture, out var length))
                    {
                        TotalSize = information.TotalSize = length;
                    }

                    if (TryGetFileName(client.ResponseHeaders, out var fileName))
                    {
                        FileName = information.FileName = fileName;
                    }

                    // For example, Google Drive responds with “none” and yet allows to download file partially,
                    // so this header will only be checked if value is not defined.
                    if (resumeSupported == null)
                    {
                        var accept = client.ResponseHeaders[HttpResponseHeader.AcceptRanges] ?? "";
                        if (accept.Contains("bytes"))
                        {
                            resumeSupported = true;
                        }
                        else if (accept.Contains("none"))
                        {
                            resumeSupported = false;
                        }
                    }

                    client.LogResponseHeaders();
                }

                // Was the file partially loaded before?
                var partiallyLoaded = ResumeSupported != false && resumePartiallyLoadedFilename != null
                        ? new FileInfo(FileUtils.EnsureFilenameIsValid(resumePartiallyLoadedFilename)) : null;
                if (partiallyLoaded != null)
                {
                    Logging.Warning("Not finished: " + partiallyLoaded);
                }

                // Does it still exist
                if (partiallyLoaded?.Exists != true)
                {
                    Logging.Warning($"Partially downloaded file “{partiallyLoaded?.FullName}” does not exist");
                    partiallyLoaded = null;
                }

                // If so, wasn’t it changed since the last time?
                if (partiallyLoaded?.LastWriteTime > resumeLastWriteDate + TimeSpan.FromMinutes(5))
                {
                    Logging.Warning($"Partially downloaded file is newer that it should be: {partiallyLoaded.LastWriteTime}, expected: {resumeLastWriteDate}");
                    partiallyLoaded = null;
                }

                // Looks like file is partially downloaded, but let’s ensure link still leads to the same content
                actualFootprint = GetFootprint(information, client.ResponseHeaders);
                if (partiallyLoaded != null && resumePreviousFootprint != actualFootprint)
                {
                    Logging.Warning($"Footprints don’t match: {resumePreviousFootprint}≠{actualFootprint}");
                    partiallyLoaded = null;
                }

                // Let’s check where to load data, which is potentially the most actual data at this point
                var destination = getPreferredDestination(Url, information);
                selectedDestination = destination.Filename;
                if (partiallyLoaded != null && (!destination.CanResumeDownload || !FileUtils.ArePathsEqual(selectedDestination, resumeDestination)))
                {
                    Logging.Warning($"Different destination chosen: {selectedDestination} (before: {resumeDestination})");
                    partiallyLoaded = null;
                }

                // TODO: Check that header?

                // Where to write?
                // ReSharper disable once MergeConditionalExpression
                filename = partiallyLoaded != null ? partiallyLoaded.FullName : FileUtils.EnsureUnique(true, destination.Filename);
                reportDestination?.Invoke(filename);

                // Set cancellation token
                cancellation.Register(o => client.CancelAsync(), null);

                // Open write stream
                if (partiallyLoaded != null)
                {
                    var rangeFrom = partiallyLoaded.Length;
                    using (client.SetRange(new Tuple <long, long>(rangeFrom, -1))) {
                        Logging.Warning($"Trying to resume download from {rangeFrom} bytes…");

                        remoteData.Dispose();
                        remoteData = await client.OpenReadTaskAsync(Url);

                        cancellation.ThrowIfCancellationRequested();
                        client.LogResponseHeaders();

                        // It’s unknown if resume is supported or not at this point
                        if (resumeSupported == null)
                        {
                            var bytes      = new byte[16];
                            var firstBytes = await remoteData.ReadAsync(bytes, 0, bytes.Length);

                            cancellation.ThrowIfCancellationRequested();

                            if (CouldBeBeginningOfAFile(bytes))
                            {
                                using (var file = File.Create(filename)) {
                                    Logging.Warning("File beginning found, restart download");
                                    file.Write(bytes, 0, firstBytes);
                                    await CopyToAsync(remoteData, file, checkIfPaused, progress, cancellation);

                                    cancellation.ThrowIfCancellationRequested();
                                }

                                Logging.Write("Download finished");
                                return(filename);
                            }

                            rangeFrom += firstBytes;
                        }

                        using (var file = new FileStream(filename, FileMode.Append, FileAccess.Write)) {
                            await CopyToAsync(remoteData, file, checkIfPaused, new Progress <long>(v => {
                                progress?.Report(v + rangeFrom);
                            }), cancellation);

                            cancellation.ThrowIfCancellationRequested();
                        }
                    }
                }
                else
                {
                    if (headRequest)
                    {
                        Logging.Warning("Re-open request to be GET");
                        remoteData.Dispose();
                        remoteData = await client.OpenReadTaskAsync(Url);
                    }

                    using (var file = File.Create(filename)) {
                        Logging.Debug("Downloading the whole file…");
                        await CopyToAsync(remoteData, file, checkIfPaused, progress, cancellation);

                        cancellation.ThrowIfCancellationRequested();
                    }
                }

                Logging.Write("Download finished");
                return(filename);
            } catch (Exception e) when(e is WebException || e.IsCancelled())
            {
                Logging.Write("Download is interrupted! Saving details to resume later…");
                var download = filename == null ? null : new FileInfo(filename);

                if (download?.Exists == true && filename.Length > 0)
                {
                    CacheStorage.Set(_keyDestination, selectedDestination);
                    CacheStorage.Set(_keyPartiallyLoadedFilename, filename);
                    CacheStorage.Set(_keyFootprint, actualFootprint);
                    CacheStorage.Set(_keyLastWriteDate, download.LastWriteTime);
                }
                else
                {
                    ClearResumeData();
                }

                throw;
            } finally {
                remoteData?.Dispose();
            }
        }
示例#6
0
 public virtual string GetFootprint(FlexibleLoaderMetaInformation information, [CanBeNull] WebHeaderCollection headers)
 {
     return($"filename={information.FileName}, size={information.TotalSize}".ToCutBase64());
 }