public async Task DownloadProductFileAsync(long id, string title, string sourceUri, string destination, IStatus status)
        {
            if (string.IsNullOrEmpty(sourceUri))
            {
                return;
            }

            var sourceUriSansSession = formatUriRemoveSessionDelegate.Format(sourceUri);
            var destinationUri       = formatValidationFileDelegate.Format(sourceUriSansSession);

            // return early if validation is not expected for this file
            if (!confirmValidationExpectedDelegate.Confirm(sourceUriSansSession))
            {
                return;
            }

            if (fileController.Exists(destinationUri))
            {
                await statusController.InformAsync(status, "Validation file already exists, will not be redownloading");

                return;
            }

            var validationSourceUri = formatValidationUriDelegate.Format(sourceUri);

            var downloadValidationFileTask = await statusController.CreateAsync(status, "Download validation file");

            await downloadFromUriAsyncDelegate.DownloadFromUriAsync(
                validationSourceUri,
                validationDirectoryDelegate.GetDirectory(string.Empty),
                downloadValidationFileTask);

            await statusController.CompleteAsync(downloadValidationFileTask);
        }
Пример #2
0
        public string Format(string sourceUri)
        {
            var sourceUriSansSession = formatUriRemoveSessionDelegate.Format(sourceUri);
            var sourceFilename       = Path.GetFileName(sourceUriSansSession);
            var validationFilename   = getValidationFilenameDelegate.GetFilename(sourceFilename);

            return(sourceUri.Replace(sourceFilename, validationFilename));
        }
        public async Task <IDictionary <long, IList <string> > > GetDownloadSourcesAsync(IStatus status)
        {
            var processUpdatesTask = await statusController.CreateAsync(status, "Process screenshots updates");

            var screenshotsSources = new Dictionary <long, IList <string> >();
            var current            = 0;
            var total = await screenshotsDataController.CountAsync(processUpdatesTask);

            var processProductsScreenshotsTask = await statusController.CreateAsync(processUpdatesTask, "Process product screenshots");

            foreach (var id in await screenshotsDataController.ItemizeAllAsync(processProductsScreenshotsTask))
            {
                var productScreenshots = await screenshotsDataController.GetByIdAsync(id, processProductsScreenshotsTask);

                if (productScreenshots == null)
                {
                    await statusController.WarnAsync(processProductsScreenshotsTask, $"Product {id} doesn't have screenshots");

                    continue;
                }

                await statusController.UpdateProgressAsync(
                    processUpdatesTask,
                    ++current,
                    total,
                    productScreenshots.Title);

                var currentProductScreenshotSources = new List <string>();

                foreach (var uri in productScreenshots.Uris)
                {
                    var sourceUri      = formatScreenshotsUriDelegate.Format(uri);
                    var destinationUri = Path.Combine(
                        screenshotsDirectoryDelegate.GetDirectory(string.Empty),
                        Path.GetFileName(sourceUri));

                    if (fileController.Exists(destinationUri))
                    {
                        continue;
                    }

                    currentProductScreenshotSources.Add(sourceUri);
                }

                if (currentProductScreenshotSources.Any())
                {
                    screenshotsSources.Add(id, currentProductScreenshotSources);
                }
            }

            await statusController.CompleteAsync(processProductsScreenshotsTask);

            await statusController.CompleteAsync(processUpdatesTask);

            return(screenshotsSources);
        }
Пример #4
0
        public override async Task ProcessActivityAsync(IStatus status)
        {
            var cleanupTask = await statusController.CreateAsync(status, $"Cleanup {context}");

            var expectedItems = await itemizeAllExpectedItemsAsyncDelegate.ItemizeAllAsync(status);

            var actualItems = await itemizeAllActualItemsAsyncDelegate.ItemizeAllAsync(status);

            var unexpectedItems = actualItems.Except(expectedItems);
            var cleanupItems    = new List <string>();

            foreach (var unexpectedItem in unexpectedItems)
            {
                foreach (var detailedItem in itemizeDetailsDelegate.Itemize(unexpectedItem))
                {
                    cleanupItems.Add(detailedItem);
                    cleanupItems.Add(formatSupplementaryItemDelegate.Format(detailedItem));
                }
            }

            var moveToRecycleBinTask = await statusController.CreateAsync(status, "Move unexpected items to recycle bin");

            var current = 0;

            foreach (var item in cleanupItems)
            {
                await statusController.UpdateProgressAsync(
                    moveToRecycleBinTask,
                    ++current,
                    cleanupItems.Count,
                    item);

                recycleDelegate.Recycle(item);
            }

            // check if any of the directories are left empty and delete
            var emptyDirectories = new List <string>();

            foreach (var item in cleanupItems)
            {
                var directory = Path.GetDirectoryName(item);
                if (!emptyDirectories.Contains(directory) &&
                    !directoryController.EnumerateFiles(directory).Any() &&
                    !directoryController.EnumerateDirectories(directory).Any())
                {
                    emptyDirectories.Add(directory);
                }
            }

            foreach (var directory in emptyDirectories)
            {
                directoryController.Delete(directory);
            }

            await statusController.CompleteAsync(moveToRecycleBinTask);
        }
Пример #5
0
        public async Task <IDictionary <long, IList <string> > > GetDownloadSourcesAsync(IStatus status)
        {
            var getDownloadSourcesStatus = await statusController.CreateAsync(status, "Get download sources");

            var productImageSources = new Dictionary <long, IList <string> >();
            var productIds          = await itemizeAllProductsAsyncDelegate.ItemizeAllAsync(getDownloadSourcesStatus);

            var current = 0;

            foreach (var id in productIds)
            {
                await statusController.UpdateProgressAsync(
                    getDownloadSourcesStatus,
                    ++current,
                    productIds.Count(),
                    id.ToString());

                var productCore = await dataController.GetByIdAsync(id, getDownloadSourcesStatus);

                // not all updated products can be found with all dataControllers
                if (productCore == null)
                {
                    continue;
                }

                var imageSources = new List <string> {
                    formatImagesUriDelegate.Format(
                        getImageUriDelegate.GetImageUri(productCore))
                };

                if (!productImageSources.ContainsKey(id))
                {
                    productImageSources.Add(id, new List <string>());
                }

                foreach (var source in imageSources)
                {
                    productImageSources[id].Add(source);
                }
            }

            await statusController.CompleteAsync(getDownloadSourcesStatus);

            return(productImageSources);
        }
Пример #6
0
        public async Task ConstrainAsync(int delaySeconds, IStatus status)
        {
            var throttleTask = await statusController.CreateAsync(
                status,
                $"Sleeping {formatSecondsDelegate.Format(delaySeconds)} before next operation");

            for (var ii = 0; ii < delaySeconds; ii++)
            {
                await Task.Delay(1000);

                await statusController.UpdateProgressAsync(
                    throttleTask,
                    ii + 1,
                    delaySeconds,
                    "Countdown",
                    TimeUnits.Seconds);
            }

            await statusController.CompleteAsync(throttleTask);
        }
Пример #7
0
        public async Task OutputContinuousAsync(IStatus status, params string[] data)
        {
            // Clear frame buffer
            fragmentBuffer.Clear();
            // Break the lines with new line separator, also wrap lines given the available console width
            var fragmentWrappedLines = formatTextToFitConsoleWindowDelegate.Format(data);

            // To build the buffer, we'll pad each line with spaces.
            // That takes care of previous frame lines that could have been longer
            foreach (var line in fragmentWrappedLines)
            {
                fragmentBuffer.Append(line.PadRight(consoleController.WindowWidth));
            }
            // Write the current frame buffer, no need to worry about line breaks or new lines:
            // padded content length takes care of both
            consoleController.Write(fragmentBuffer.ToString());
            // For consoles that have zero cursor width make sure to forcefully break the line
            if (consoleController.CursorLeft > 0)
            {
                consoleController.WriteLine(string.Empty);
            }
            // Clear remaining lines from the previous frame, if any
            await ClearContinuousLinesAsync(previousFrameLines - fragmentWrappedLines.Count());
        }
Пример #8
0
        public IDictionary <string, string> GetViewModel(IStatus status)
        {
            if (status.Complete)
            {
                return(null);
            }

            // viewmodel schemas
            var viewModel = new Dictionary <string, string>
            {
                { StatusAppViewModelSchema.Title, "" },
                { StatusAppViewModelSchema.ContainsProgress, "" },
                { StatusAppViewModelSchema.ProgressTarget, "" },
                { StatusAppViewModelSchema.ProgressPercent, "" },
                { StatusAppViewModelSchema.ProgressCurrent, "" },
                { StatusAppViewModelSchema.ProgressTotal, "" },
                { StatusAppViewModelSchema.ContainsETA, "" },
                { StatusAppViewModelSchema.RemainingTime, "" },
                { StatusAppViewModelSchema.AverageUnitsPerSecond, "" },
                { StatusAppViewModelSchema.ContainsFailures, "" },
                { StatusAppViewModelSchema.FailuresCount, "" },
                { StatusAppViewModelSchema.ContainsWarnings, "" },
                { StatusAppViewModelSchema.WarningsCount, "" }
            };

            viewModel[StatusAppViewModelSchema.Title] = status.Title;

            if (status.Progress != null)
            {
                var current = status.Progress.Current;
                var total   = status.Progress.Total;

                viewModel[StatusAppViewModelSchema.ContainsProgress] = "true";
                viewModel[StatusAppViewModelSchema.ProgressTarget]   = status.Progress.Target;
                viewModel[StatusAppViewModelSchema.ProgressPercent]  = string.Format("{0:P1}", (double)current / total);

                var currentFormatted = current.ToString();
                var totalFormatted   = total.ToString();

                if (status.Progress.Unit == DataUnits.Bytes)
                {
                    viewModel[StatusAppViewModelSchema.ContainsETA] = "true";

                    currentFormatted = formatBytesDelegate.Format(current);
                    totalFormatted   = formatBytesDelegate.Format(total);

                    var estimatedTimeAvailable = formatRemainingTimeAtSpeedDelegate.Format(status);

                    var remainingTime = formatSecondsDelegate.Format(estimatedTimeAvailable.Item1);
                    var speed         = formatBytesDelegate.Format((long)estimatedTimeAvailable.Item2);

                    viewModel[StatusAppViewModelSchema.RemainingTime]         = remainingTime;
                    viewModel[StatusAppViewModelSchema.AverageUnitsPerSecond] = speed;
                }

                viewModel[StatusAppViewModelSchema.ProgressCurrent] = currentFormatted;
                viewModel[StatusAppViewModelSchema.ProgressTotal]   = totalFormatted;
            }

            if (status.Failures != null && status.Failures.Any())
            {
                viewModel[StatusAppViewModelSchema.ContainsFailures] = "true";
                viewModel[StatusAppViewModelSchema.FailuresCount]    = status.Failures.Count.ToString();
            }

            if (status.Warnings != null && status.Warnings.Any())
            {
                viewModel[StatusAppViewModelSchema.ContainsWarnings] = "true";
                viewModel[StatusAppViewModelSchema.WarningsCount]    = status.Warnings.Count.ToString();
            }

            return(viewModel);
        }
        public override async Task ProcessActivityAsync(IStatus status)
        {
            var validateProductsStatus = await statusController.CreateAsync(status, "Validate products");

            var current = 0;

            var validateProductsList = await itemizeAllProductsAsyncDelegate.ItemizeAllAsync(validateProductsStatus);

            var validateProductsCount = validateProductsList.Count();

            foreach (var id in validateProductsList)
            {
                var gameDetails = await gameDetailsDataController.GetByIdAsync(id, validateProductsStatus);

                var validationResults = await validationResultsDataController.GetByIdAsync(id, validateProductsStatus);

                if (validationResults == null)
                {
                    validationResults = new ValidationResult
                    {
                        Id    = id,
                        Title = gameDetails.Title
                    }
                }
                ;

                await statusController.UpdateProgressAsync(validateProductsStatus,
                                                           ++current,
                                                           validateProductsCount,
                                                           gameDetails.Title);

                var localFiles = new List <string>();

                var getLocalFilesTask = await statusController.CreateAsync(validateProductsStatus, "Enumerate local product files");

                foreach (var manualUrl in
                         await itemizeGameDetailsManualUrlsAsyncDelegate.ItemizeAsync(gameDetails, getLocalFilesTask))
                {
                    var resolvedUri = await routingController.TraceRouteAsync(id, manualUrl, getLocalFilesTask);

                    // use directory from source and file from resolved URI
                    var localFile = Path.Combine(
                        productFileDirectoryDelegate.GetDirectory(manualUrl),
                        productFileFilenameDelegate.GetFilename(resolvedUri));

                    localFiles.Add(localFile);
                }
                await statusController.CompleteAsync(getLocalFilesTask);


                // check if current validation results allow us to skip validating current product
                // otherwise - validate all the files again

                // ...

                var fileValidationResults = new List <IFileValidationResult>(localFiles.Count);

                var validateFilesTask = await statusController.CreateAsync(
                    validateProductsStatus,
                    "Validate product files");

                var currentFile = 0;

                foreach (var localFile in localFiles)
                {
                    await statusController.UpdateProgressAsync(validateFilesTask,
                                                               ++currentFile,
                                                               localFiles.Count,
                                                               localFile);

                    var validationFile = formatValidationFileDelegate.Format(localFile);

                    try
                    {
                        fileValidationResults.Add(await fileValidationController.ValidateFileAsync(
                                                      localFile,
                                                      validationFile,
                                                      validateFilesTask));
                    }
                    catch (Exception ex)
                    {
                        await statusController.FailAsync(validateProductsStatus,
                                                         $"{localFile}: {ex.Message}");
                    }
                }

                await statusController.CompleteAsync(validateFilesTask);

                validationResults.Files = fileValidationResults.ToArray();

                var updateValidationResultsTask = await statusController.CreateAsync(
                    validateProductsStatus,
                    "Update validation results");

                await validationResultsDataController.UpdateAsync(validationResults, validateProductsStatus);

                await statusController.CompleteAsync(updateValidationResultsTask);
            }

            await statusController.CompleteAsync(validateProductsStatus);
        }
    }
Пример #10
0
        public async Task DownloadProductFileAsync(long id, string title, string sourceUri, string destination, IStatus status)
        {
            var downloadTask = await statusController.CreateAsync(status, "Download game details manual url");

            HttpResponseMessage response;

            try
            {
                response = await networkController.RequestResponseAsync(downloadTask, HttpMethod.Get, sourceUri);
            }
            catch (HttpRequestException ex)
            {
                await statusController.FailAsync(
                    downloadTask,
                    $"Failed to get successful response for {sourceUri} for " +
                    $"product {id}: {title}, message: {ex.Message}");

                await statusController.CompleteAsync(downloadTask);

                return;
            }

            using (response)
            {
                var resolvedUri = response.RequestMessage.RequestUri.ToString();

                // GOG.com quirk
                // When resolving ManualUrl from GameDetails we get CDN Uri with the session key.
                // Storing this key is pointless - it expries after some time and needs to be updated.
                // So here we filter our this session key and store direct file Uri

                var uriSansSession = formatUriRemoveSessionDelegate.Format(resolvedUri);

                await routingController.UpdateRouteAsync(
                    id,
                    title,
                    sourceUri,
                    uriSansSession,
                    downloadTask);

                try
                {
                    await downloadFromResponseAsyncDelegate.DownloadFromResponseAsync(
                        response,
                        destination,
                        downloadTask);
                }
                catch (Exception ex)
                {
                    await statusController.FailAsync(
                        downloadTask,
                        $"Couldn't download {sourceUri}, resolved as {resolvedUri} to {destination} " +
                        $"for product {id}: {title}, error message: {ex.Message}");
                }

                // GOG.com quirk
                // Supplementary download is a secondary download to a primary driven by download scheduling
                // The example is validation file - while we can use the same pipeline, we would be
                // largerly duplicating all the work to establish the session, compute the name etc.
                // While the only difference validation files have - is additional extension.
                // So instead we'll do a supplementary download using primary download information

                await downloadValidationFileAsyncDelegate?.DownloadProductFileAsync(
                    id,
                    title,
                    resolvedUri,
                    destination,
                    downloadTask);
            }

            await statusController.CompleteAsync(downloadTask);
        }
        public IDictionary <string, string> GetViewModel(IStatus status)
        {
            // viewmodel schemas
            var viewModel = new Dictionary <string, string>
            {
                { StatusReportViewModelSchema.Title, "" },
                { StatusReportViewModelSchema.Complete, "" },
                { StatusReportViewModelSchema.Started, "" },
                { StatusReportViewModelSchema.Duration, "" },
                { StatusReportViewModelSchema.Result, "" },
                { StatusReportViewModelSchema.ContainsProgress, "" },
                { StatusReportViewModelSchema.ProgressTarget, "" },
                { StatusReportViewModelSchema.ProgressCurrent, "" },
                { StatusReportViewModelSchema.ProgressTotal, "" },
                { StatusReportViewModelSchema.ContainsFailures, "" },
                { StatusReportViewModelSchema.Failures, "" },
                { StatusReportViewModelSchema.ContainsWarnings, "" },
                { StatusReportViewModelSchema.Warnings, "" },
                { StatusReportViewModelSchema.ContainsInformation, "" },
                { StatusReportViewModelSchema.Information, "" }
            };

            viewModel[StatusReportViewModelSchema.Title]    = status.Title;
            viewModel[StatusReportViewModelSchema.Complete] = status.Complete ? "true" : "";
            viewModel[StatusReportViewModelSchema.Started]  = status.Started.ToLocalTime().ToString();
            viewModel[StatusReportViewModelSchema.Duration] =
                status.Complete ?
                formatSecondsDelegate.Format((status.Completed - status.Started).Seconds) :
                "";

            var results = new List <string>();

            if (status.Failures != null && status.Failures.Any())
            {
                results.Add("Failure(s)");
            }
            if (status.Warnings != null && status.Warnings.Any())
            {
                results.Add("Warning(s)");
            }
            var result = string.Join(",", results);

            if (string.IsNullOrEmpty(result))
            {
                result = "Success";
            }
            viewModel[StatusReportViewModelSchema.Result] = result;

            if (status.Progress != null)
            {
                var current = status.Progress.Current;
                var total   = status.Progress.Total;

                viewModel[StatusReportViewModelSchema.ContainsProgress] = "true";
                viewModel[StatusReportViewModelSchema.ProgressTarget]   = status.Progress.Target;

                var currentFormatted = current.ToString();
                var totalFormatted   = total.ToString();

                if (status.Progress.Unit == DataUnits.Bytes)
                {
                    currentFormatted = formatBytesDelegate.Format(current);
                    totalFormatted   = formatBytesDelegate.Format(total);
                }

                viewModel[StatusReportViewModelSchema.ProgressCurrent] = currentFormatted;
                viewModel[StatusReportViewModelSchema.ProgressTotal]   = totalFormatted;
            }

            if (status.Failures != null && status.Failures.Any())
            {
                viewModel[StatusReportViewModelSchema.ContainsFailures] = "true";
                viewModel[StatusReportViewModelSchema.Failures]         = string.Join("; ", status.Failures);
            }

            if (status.Warnings != null && status.Warnings.Any())
            {
                viewModel[StatusReportViewModelSchema.ContainsWarnings] = "true";
                viewModel[StatusReportViewModelSchema.Warnings]         = string.Join("; ", status.Warnings);
            }

            if (status.Information != null && status.Information.Any())
            {
                viewModel[StatusReportViewModelSchema.ContainsInformation] = "true";
                viewModel[StatusReportViewModelSchema.Information]         = string.Join("; ", status.Information);
            }

            return(viewModel);
        }
Пример #12
0
        public async Task <GameDetails> GetDeserializedAsync(IStatus status, string uri, IDictionary <string, string> parameters = null)
        {
            // GOG.com quirk
            // GameDetails as sent by GOG.com servers have an intersting data structure for downloads:
            // it's represented as an array, where first element is a string with the language,
            // followed by actual download details, something like:
            // [ "English", { // download1 }, { // download2 } ]
            // Which of course is not a problem for JavaScript, but is a problem for
            // deserializing into strongly typed C# data structures.
            // To work around this we wrapped encapsulated usual network requests,
            // data transformation and desearialization in a sinlge package.
            // To process downloads we do the following:
            // - if response contains language downloads, signified by [[
            // - extract actual language information and remove it from the string
            // - deserialize downloads into OperatingSystemsDownloads collection
            // - assign languages, since we know we should have as many downloads array as languages

            var data = await getResourceAsyncDelegate.GetResourceAsync(status, uri, parameters);

            var gameDetails = serializationController.Deserialize <GameDetails>(data);

            if (gameDetails == null)
            {
                return(null);
            }

            var gameDetailsLanguageDownloads = new List <OperatingSystemsDownloads>();

            if (!confirmStringContainsLanguageDownloadsDelegate.Confirm(data))
            {
                return(gameDetails);
            }
            var downloadStrings = itemizeGameDetailsDownloadsDelegate.Itemize(data);

            foreach (var downloadString in downloadStrings)
            {
                var downloadLanguages = itemizeDownloadLanguagesDelegate.Itemize(downloadString);
                if (downloadLanguages == null)
                {
                    throw new InvalidOperationException("Cannot find any download languages or download language format changed.");
                }

                // ... and remove download lanugage strings from downloads
                var downloadsStringSansLanguages = replaceMultipleStringsDelegate.ReplaceMultiple(
                    downloadString,
                    string.Empty,
                    downloadLanguages.ToArray());

                // now it should be safe to deserialize langugage downloads
                var downloads =
                    serializationController.Deserialize <OperatingSystemsDownloads[][]>(
                        downloadsStringSansLanguages);

                // and convert GOG two-dimensional array of downloads to single-dimensional array
                var languageDownloads = convert2DArrayToArrayDelegate.Convert(downloads);

                if (languageDownloads.Count() != downloadLanguages.Count())
                {
                    throw new InvalidOperationException("Number of extracted language downloads doesn't match number of languages.");
                }

                // map language downloads with the language code we extracted earlier
                var languageDownloadIndex = 0;

                collectionController.Map(downloadLanguages, language =>
                {
                    var formattedLanguage = formatDownloadLanguageDelegate.Format(language);
                    var languageCode      = languageController.GetLanguageCode(formattedLanguage);

                    languageDownloads[languageDownloadIndex++].Language = languageCode;
                });

                gameDetailsLanguageDownloads.AddRange(languageDownloads);
            }

            gameDetails.LanguageDownloads = gameDetailsLanguageDownloads.ToArray();

            return(gameDetails);
        }