コード例 #1
0
        protected override async Task <ExitCode> Run()
        {
            var state = await DownloadDispatcher.Infer(Url);

            if (state == null)
            {
                return(CLIUtils.Exit($"Could not find download source for URL {Url}", ExitCode.Error));
            }

            await DownloadDispatcher.PrepareAll(new [] { state });

            using var queue = new WorkQueue();
            queue.Status
            .Where(s => s.ProgressPercent != Percent.Zero)
            .Debounce(TimeSpan.FromSeconds(1))
            .Subscribe(s => Console.WriteLine($"Downloading {s.ProgressPercent}"));

            new[] { state }
            .PMap(queue, async s =>
            {
                await s.Download(new Archive(state: null !)
                {
                    Name = Path.GetFileName(Output)
                }, (AbsolutePath)Output);
            }).Wait();
コード例 #2
0
        public async Task ServerGetsEdgeServerInfo()
        {
            var service = Fixture.GetService <CDNMirrorList>();

            Assert.True(await service.Execute() > 0);
            Assert.NotEmpty(service.Mirrors);
            Assert.True(DateTime.UtcNow - service.LastUpdate < TimeSpan.FromMinutes(1));

            var servers = await ClientAPI.GetCDNMirrorList();

            Assert.Equal(service.Mirrors, servers);

            var state = new WabbajackCDNDownloader.State(new Uri("https://wabbajack.b-cdn.net/this_file_doesn_t_exist"));
            await DownloadDispatcher.PrepareAll(new[] { state });

            await using var tmp = new TempFile();

            await Assert.ThrowsAsync <HttpException>(async() => await state.Download(new Archive(state)
            {
                Name = "test"
            }, tmp.Path));

            var downloader = DownloadDispatcher.GetInstance <WabbajackCDNDownloader>();

            Assert.Null(downloader.Mirrors); // Now works through a host remap
        }
コード例 #3
0
ファイル: ClientAPI.cs プロジェクト: TDarkShadow/wabbajack
        public static async Task <AbstractDownloadState?> InferDownloadState(Hash hash)
        {
            if (BuildServerStatus.IsBuildServerDown)
            {
                return(null);
            }

            var client = await GetClient();

            var results = await client.GetJsonAsync <Archive[]>(
                $"{Consts.WabbajackBuildServerUri}mod_files/by_hash/{hash.ToHex()}");

            await DownloadDispatcher.PrepareAll(results.Select(r => r.State));

            foreach (var result in results)
            {
                try
                {
                    if (await result.State.Verify(result))
                    {
                        return(result.State);
                    }
                }
                catch (Exception ex)
                {
                    Utils.Log($"Verification error for failed for inferenced archive {result.State.PrimaryKeyString}");
                    Utils.Log(ex.ToString());
                }
            }
            return(null);
        }
コード例 #4
0
ファイル: DownloadUrl.cs プロジェクト: bahaynes/wabbajack
        protected override async Task <int> Run()
        {
            var state = DownloadDispatcher.Infer(Url);

            if (state == null)
            {
                Console.WriteLine($"Could not find download source for URL {Url}");
                return(1);
            }

            DownloadDispatcher.PrepareAll(new [] { state });

            using var queue = new WorkQueue();
            queue.Status
            .Where(s => s.ProgressPercent != Percent.Zero)
            .Debounce(TimeSpan.FromSeconds(1))
            .Subscribe(s => Console.WriteLine($"Downloading {s.ProgressPercent}"));

            new[] { state }
            .PMap(queue, async s =>
            {
                await s.Download(new Archive {
                    Name = Path.GetFileName(Output)
                }, Output);
            }).Wait();

            File.WriteAllLines(Output + ".meta", state.GetMetaIni());
            return(0);
        }
コード例 #5
0
        public override async Task <int> Execute()
        {
            var archives = await _sql.GetNonNexusModlistArchives();

            _logger.Log(LogLevel.Information, $"Validating {archives.Count} non-Nexus archives");
            using var queue = new WorkQueue();
            await DownloadDispatcher.PrepareAll(archives.Select(a => a.State));

            var results = await archives.PMap(queue, async archive =>
            {
                try
                {
                    var token = new CancellationTokenSource();
                    token.CancelAfter(TimeSpan.FromMinutes(10));

                    ReportStarting(archive.State.PrimaryKeyString);
                    bool isValid = false;
                    switch (archive.State)
                    {
                    case WabbajackCDNDownloader.State _:
                    case GoogleDriveDownloader.State _:
                    case ManualDownloader.State _:
                    case ModDBDownloader.State _:
                    case HTTPDownloader.State h when h.Url.StartsWith("https://wabbajack"):
                        isValid = true;
                        break;

                    default:
                        isValid = await archive.State.Verify(archive, token.Token);
                        break;
                    }
                    return(Archive : archive, IsValid : isValid);
                }
                catch (Exception ex)
                {
                    _logger.Log(LogLevel.Warning, $"Error for {archive.Name} {archive.State.PrimaryKeyString} {ex}");
                    return(Archive : archive, IsValid : false);
                }
                finally
                {
                    ReportEnding(archive.State.PrimaryKeyString);
                }
            });

            await _sql.UpdateNonNexusModlistArchivesStatus(results);

            var failed = results.Count(r => !r.IsValid);
            var passed = results.Count() - failed;

            foreach (var(archive, _) in results.Where(f => !f.IsValid))
            {
                _logger.Log(LogLevel.Warning, $"Validation failed for {archive.Name} from {archive.State.PrimaryKeyString}");
            }

            _logger.Log(LogLevel.Information, $"Non-nexus validation completed {failed} out of {passed} failed");
            return(failed);
        }
コード例 #6
0
ファイル: EndToEndTests.cs プロジェクト: rhysmdnz/wabbajack
        private async Task <(AbsolutePath Download, AbsolutePath ModFolder)> DownloadAndInstall(string url, string filename, string modName = null)
        {
            if (modName != null)
            {
                await utils.AddMod(modName);
            }
            var src = _downloadFolder.Combine(filename);

            if (!src.Exists)
            {
                var state = DownloadDispatcher.ResolveArchive(url);
                await DownloadDispatcher.PrepareAll(new[] { state });

                await state.Download(new Archive(state : null !) { Name = "Unknown" }, src);
コード例 #7
0
        private async Task ValidateList(SqlService sql, ModlistMetadata list, WorkQueue queue, ValidateModlist whitelists)
        {
            var modlistPath = Consts.ModListDownloadFolder.Combine(list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlistPath))
            {
                modlistPath.Delete();

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlistPath);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded modlist");
            }


            Utils.Log($"Loading {modlistPath}");

            var installer = AInstaller.LoadFromFile(modlistPath);

            Utils.Log($"{installer.Archives.Count} archives to validate");

            await DownloadDispatcher.PrepareAll(installer.Archives.Select(a => a.State));


            var validated = (await installer.Archives
                             .PMap(queue, async archive =>
            {
                var isValid = await IsValid(sql, whitelists, archive);

                return(new DetailedStatusItem {
                    IsFailing = !isValid, Archive = archive
                });
            }))
                            .ToList();


            var status = new DetailedStatus
            {
                Name             = list.Title,
                Archives         = validated.OrderBy(v => v.Archive.Name).ToList(),
                DownloadMetaData = list.DownloadMetadata,
                HasFailures      = validated.Any(v => v.IsFailing)
            };

            var dto = new ModListStatus
            {
                Id      = list.Links.MachineURL,
                Summary = new ModListSummary
                {
                    Name       = status.Name,
                    MachineURL = list.Links?.MachineURL ?? status.Name,
                    Checked    = status.Checked,
                    Failed     = status.Archives.Count(a => a.IsFailing),
                    Passed     = status.Archives.Count(a => !a.IsFailing),
                },
                DetailedStatus = status,
                Metadata       = list
            };

            Utils.Log(
                $"Writing Update for {dto.Summary.Name} - {dto.Summary.Failed} failed - {dto.Summary.Passed} passed");
            await sql.UpdateModListStatus(dto);

            Utils.Log(
                $"Done updating {dto.Summary.Name}");
        }
コード例 #8
0
        public override async Task <int> Execute()
        {
            _nexusClient ??= await NexusApiClient.Get();

            int count = 0;

            while (true)
            {
                var(daily, hourly) = await _nexusClient.GetRemainingApiCalls();

                bool ignoreNexus = (daily < 100 && hourly < 10);
                //var ignoreNexus = true;
                if (ignoreNexus)
                {
                    _logger.LogWarning($"Ignoring Nexus Downloads due to low hourly api limit (Daily: {daily}, Hourly:{hourly})");
                }
                else
                {
                    _logger.LogInformation($"Looking for any download (Daily: {_nexusClient.DailyRemaining}, Hourly:{_nexusClient.HourlyRemaining})");
                }

                var nextDownload = await _sql.GetNextPendingDownload(ignoreNexus);

                if (nextDownload == null)
                {
                    break;
                }

                if (nextDownload.Archive.Hash != default && _archiveMaintainer.HaveArchive(nextDownload.Archive.Hash))
                {
                    await nextDownload.Finish(_sql);

                    continue;
                }

                if (nextDownload.Archive.State is ManualDownloader.State)
                {
                    await nextDownload.Finish(_sql);

                    continue;
                }

                try
                {
                    _logger.Log(LogLevel.Information, $"Downloading {nextDownload.Archive.State.PrimaryKeyString}");
                    await DownloadDispatcher.PrepareAll(new[] { nextDownload.Archive.State });

                    using var tempPath = new TempFile();
                    await nextDownload.Archive.State.Download(nextDownload.Archive, tempPath.Path);

                    var hash = await tempPath.Path.FileHashAsync();

                    if (nextDownload.Archive.Hash != default && hash != nextDownload.Archive.Hash)
                    {
                        await nextDownload.Fail(_sql, "Invalid Hash");

                        continue;
                    }

                    if (nextDownload.Archive.Size != default &&
                        tempPath.Path.Size != nextDownload.Archive.Size)
                    {
                        await nextDownload.Fail(_sql, "Invalid Size");

                        continue;
                    }
                    nextDownload.Archive.Hash = hash;
                    nextDownload.Archive.Size = tempPath.Path.Size;

                    _logger.Log(LogLevel.Information, $"Archiving {nextDownload.Archive.State.PrimaryKeyString}");
                    await _archiveMaintainer.Ingest(tempPath.Path);

                    _logger.Log(LogLevel.Information, $"Finished Archiving {nextDownload.Archive.State.PrimaryKeyString}");
                    await nextDownload.Finish(_sql);
                }
                catch (Exception ex)
                {
                    _logger.Log(LogLevel.Warning, $"Error downloading {nextDownload.Archive.State.PrimaryKeyString}");
                    await nextDownload.Fail(_sql, ex.ToString());
                }

                count++;
            }

            return(count);
        }
コード例 #9
0
        public override async Task <int> Execute()
        {
            _nexusClient ??= await NexusApiClient.Get();

            int count = 0;

            while (true)
            {
                var(daily, hourly) = await _nexusClient.GetRemainingApiCalls();

                bool ignoreNexus = (daily < 100 && hourly < 10);
                //var ignoreNexus = true;
                if (ignoreNexus)
                {
                    _logger.LogWarning($"Ignoring Nexus Downloads due to low hourly api limit (Daily: {daily}, Hourly:{hourly})");
                }
                else
                {
                    _logger.LogInformation($"Looking for any download (Daily: {_nexusClient.DailyRemaining}, Hourly:{_nexusClient.HourlyRemaining})");
                }

                var nextDownload = await _sql.GetNextPendingDownload(ignoreNexus);

                if (nextDownload == null)
                {
                    break;
                }

                _logger.LogInformation($"Checking for previously archived {nextDownload.Archive.Hash}");

                if (nextDownload.Archive.Hash != default && _archiveMaintainer.HaveArchive(nextDownload.Archive.Hash))
                {
                    await nextDownload.Finish(_sql);

                    continue;
                }

                if (nextDownload.Archive.State is ManualDownloader.State)
                {
                    await nextDownload.Finish(_sql);

                    continue;
                }

                try
                {
                    _logger.Log(LogLevel.Information, $"Downloading {nextDownload.Archive.State.PrimaryKeyString}");
                    if (!(nextDownload.Archive.State is GameFileSourceDownloader.State))
                    {
                        await _discord.Send(Channel.Spam, new DiscordMessage { Content = $"Downloading {nextDownload.Archive.State.PrimaryKeyString}" });
                    }
                    await DownloadDispatcher.PrepareAll(new[] { nextDownload.Archive.State });

                    await using var tempPath = new TempFile();
                    if (!await nextDownload.Archive.State.Download(nextDownload.Archive, tempPath.Path))
                    {
                        _logger.LogError($"Downloader returned false for {nextDownload.Archive.State.PrimaryKeyString}");
                        await nextDownload.Fail(_sql, "Downloader returned false");

                        continue;
                    }

                    var hash = await tempPath.Path.FileHashAsync();

                    if (nextDownload.Archive.Hash != default && hash != nextDownload.Archive.Hash)
                    {
                        _logger.Log(LogLevel.Warning, $"Downloaded archive hashes don't match for {nextDownload.Archive.State.PrimaryKeyString} {nextDownload.Archive.Hash} {nextDownload.Archive.Size} vs {hash} {tempPath.Path.Size}");
                        await nextDownload.Fail(_sql, "Invalid Hash");

                        continue;
                    }

                    if (nextDownload.Archive.Size != default &&
                        tempPath.Path.Size != nextDownload.Archive.Size)
                    {
                        await nextDownload.Fail(_sql, "Invalid Size");

                        continue;
                    }
                    nextDownload.Archive.Hash = hash;
                    nextDownload.Archive.Size = tempPath.Path.Size;

                    _logger.Log(LogLevel.Information, $"Archiving {nextDownload.Archive.State.PrimaryKeyString}");
                    await _archiveMaintainer.Ingest(tempPath.Path);

                    _logger.Log(LogLevel.Information, $"Finished Archiving {nextDownload.Archive.State.PrimaryKeyString}");
                    await nextDownload.Finish(_sql);

                    if (!(nextDownload.Archive.State is GameFileSourceDownloader.State))
                    {
                        await _discord.Send(Channel.Spam, new DiscordMessage { Content = $"Finished downloading {nextDownload.Archive.State.PrimaryKeyString}" });
                    }
                }
                catch (Exception ex)
                {
                    _logger.Log(LogLevel.Warning, $"Error downloading {nextDownload.Archive.State.PrimaryKeyString}");
                    await nextDownload.Fail(_sql, ex.ToString());

                    await _discord.Send(Channel.Spam, new DiscordMessage { Content = $"Error downloading {nextDownload.Archive.State.PrimaryKeyString}" });
                }

                count++;
            }

            if (count > 0)
            {
                // Wake the Patch builder up in case it needs to build a patch now
                await _quickSync.Notify <PatchBuilder>();
            }

            return(count);
        }
コード例 #10
0
        private static async Task ValidateList(DBContext db, ModlistMetadata list, WorkQueue queue, ValidateModlist whitelists)
        {
            var existing = await db.ModListStatus.FindOneAsync(l => l.Id == list.Links.MachineURL);

            var modlist_path = Path.Combine(Consts.ModListDownloadFolder, list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlist_path))
            {
                if (File.Exists(modlist_path))
                {
                    File.Delete(modlist_path);
                }

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlist_path);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded modlist");
            }


            Utils.Log($"Loading {modlist_path}");

            var installer = AInstaller.LoadFromFile(modlist_path);

            Utils.Log($"{installer.Archives.Count} archives to validate");

            DownloadDispatcher.PrepareAll(installer.Archives.Select(a => a.State));


            var validated = (await installer.Archives
                             .PMap(queue, async archive =>
            {
                bool is_failed;
                try
                {
                    is_failed = !(await archive.State.Verify(archive)) || !archive.State.IsWhitelisted(whitelists.ServerWhitelist);
                }
                catch (Exception)
                {
                    is_failed = false;
                }

                return(new DetailedStatusItem {
                    IsFailing = is_failed, Archive = archive
                });
            }))
                            .ToList();


            var status = new DetailedStatus
            {
                Name             = list.Title,
                Archives         = validated.OrderBy(v => v.Archive.Name).ToList(),
                DownloadMetaData = list.DownloadMetadata,
                HasFailures      = validated.Any(v => v.IsFailing)
            };

            var dto = new ModListStatus
            {
                Id      = list.Links.MachineURL,
                Summary = new ModlistSummary
                {
                    Name    = status.Name,
                    Checked = status.Checked,
                    Failed  = status.Archives.Count(a => a.IsFailing),
                    Passed  = status.Archives.Count(a => !a.IsFailing),
                },
                DetailedStatus = status,
                Metadata       = list
            };

            Utils.Log(
                $"Writing Update for {dto.Summary.Name} - {dto.Summary.Failed} failed - {dto.Summary.Passed} passed");
            await ModListStatus.Update(db, dto);

            Utils.Log(
                $"Done updating {dto.Summary.Name}");
        }
コード例 #11
0
        protected override async Task <ExitCode> Run()
        {
            var files = Input.EnumerateFiles()
                        .Where(f => f.WithExtension(Consts.MetaFileExtension).Exists)
                        .ToArray();

            Console.WriteLine($"Found {files.Length} files to verify");

            using var queue = new WorkQueue();

            var states = (await files.PMap(queue, async f =>
            {
                var ini = f.WithExtension(Consts.MetaFileExtension).LoadIniFile();
                var state = (AbstractDownloadState?)await DownloadDispatcher.ResolveArchive(ini, quickMode: true);
                if (state == default)
                {
                    Console.WriteLine($"[Skipping] {f.FileName} because no meta could be interpreted");
                }

                if (!string.IsNullOrWhiteSpace(StateType) && !state !.PrimaryKeyString.StartsWith(StateType + "|"))
                {
                    Console.WriteLine(
                        $"[Skipping] {f.FileName} because type {state.PrimaryKeyString[0]} does not match filter");
                    return(f, null);
                }

                return(f, state);
            })).Where(s => s.state != null)
                         .Select(s => (s.f, s.state !))
                         .ToArray();

            await DownloadDispatcher.PrepareAll(states.Select(s => s.Item2));

            Helpers.Init();

            Console.WriteLine($"Found {states.Length} states to verify");
            int timedOut = 0;

            await states.PMap(queue, async p =>
            {
                try
                {
                    var(f, state) = p;


                    try
                    {
                        var cts = new CancellationTokenSource();
                        cts.CancelAfter(TimeSpan.FromMinutes(10));
                        var result =
                            await state !.Verify(new Archive(state)
                        {
                            Name = f.FileName.ToString(), Size = f.Size
                        },
                                                 cts.Token);
                        Console.WriteLine($"[{(result ? "Failed" : "Passed")}] {f.FileName}");
                    }
                    catch (TaskCanceledException)
                    {
                        Console.WriteLine($"[Timed Out] {f.FileName} {state!.PrimaryKeyString}");
                        Interlocked.Increment(ref timedOut);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[Exception] {p.f.FileName} {ex.Message}");
                }
            });

            Console.WriteLine($"[Total TimedOut] {timedOut}");
            Console.WriteLine("[Done]");

            return(ExitCode.Ok);
        }