コード例 #1
0
        private void _OpenStory(WInteractiveStory story)
        {
            _story = story;

            // Update the Story info textbox
            // Update the Story Info textbox with the story's info
            StringBuilder sb = new StringBuilder();

            sb.AppendLine($"Name: {story.Name}");
            sb.AppendLine();
            if (!string.IsNullOrWhiteSpace(story.ShortDescription))
            {
                sb.AppendLine(story.ShortDescription);
                sb.AppendLine();
            }
            sb.AppendLine("Description:");
            sb.AppendLine(story.Description);

            UpdateStoryInfo(sb.ToString());

            // Change the form
            tbStoryUrl.Text     = story.Url;
            tbStoryUrl.ReadOnly = true;
            EnableSaveButton(true);
            EnableFetchStoryButton(true);

            log.InfoFormat("Story opened: {0}", story.UrlID);
        }
        public static void SaveInteractiveStory(string filePath, WInteractiveStory storyToSave)
        {
            log.DebugFormat("Saving interactive story {0}", filePath);
            string serializedStory = SerializeInteractiveStoryToString(storyToSave);

            File.WriteAllText(filePath, serializedStory);
        }
コード例 #3
0
 public static string SerializeInteractiveStoryToString(WInteractiveStory storyToSerialize)
 {
     using (StringWriter sw = new StringWriter())
     {
         storySerializer.Serialize(sw, storyToSerialize);
         return(sw.ToString());
     }
 }
コード例 #4
0
        private void ResetForm()
        {
            log.Debug("Resetting form");

            // Cancel an export, if one is running
            StoryExporter?.Cancel();

            tbStoryUrl.ReadOnly = false;
            EnableCancelButton(false);
            EnableSaveButton(false);
            EnableFetchStoryButton(false);
            EnableOpenStoryButton(true);
            EnableCredentialFields(true);

            UpdateStatusProgress(0);
            UpdateStatusMessage("Open a story to get started");

            tbStoryInfo.Clear();

            _story = null;
        }
コード例 #5
0
        public void ExportStory(WInteractiveStory story)
        {
            UpdateStatusMessage("Exporting story to HTML");

            try
            {
                var sfd = new SaveFileDialog();
                sfd.Filter           = STORY_EXPORT_DIALOG_FILTER;
                sfd.InitialDirectory = LastFileDir;
                sfd.FileName         = $"{story.UrlID}.{STORY_EXPORT_DIALOG_SUFFIX}";
                sfd.ValidateNames    = true;
                sfd.OverwritePrompt  = true;
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    var htmlPayload = new WStoryToHtml().ConvertStory(story);
                    File.WriteAllText(sfd.FileName, htmlPayload);

                    if (MessageBox.Show(
                            "The story has been exported.\n\nWould you like to see it?",
                            "Story exported",
                            MessageBoxButtons.YesNo,
                            MessageBoxIcon.Information
                            ) == DialogResult.Yes)
                    {
                        string args = $"/select,\"{sfd.FileName}\"";
                        Process.Start("explorer", args);
                    }

                    LastFileDir = Path.GetDirectoryName(sfd.FileName);
                    UpdateStatusMessage("Story exported to HTML");
                    log.Info("Story exported to HTML");
                }
            }
            catch (Exception ex)
            {
                log.Error("Error while exporting story to HTML", ex);
                UpdateStatusMessage("Failed to export story to HTML");
            }
        }
コード例 #6
0
        public async Task <WInteractiveStory> GetInteractive(Uri interactiveUrl)
        {
            WInteractiveStory newInteractive = new WInteractiveStory();

            log.DebugFormat("Getting interactive story: {0}", interactiveUrl);

            string interactiveHtml = await HttpGetAsyncAsString(interactiveUrl);

            // DEBUG
            //string interactiveHtml = System.IO.File.ReadAllText("test-interactive-cover.html");
            if (IsInteractivesUnavailablePage(interactiveHtml))
            {
                throw new InteractivesTemporarilyUnavailableException();
            }

            // Detect "Login required" or "access restricted"
            if (IsLoginPage(interactiveHtml))
            {
                // We need to log in, and get the chapter again
                log.Debug("Login required while trying to get an interactive's details");
                await LoginAsync();

                // Get it again
                interactiveHtml = await HttpGetAsyncAsString(interactiveUrl);

                // Check if it's a login again. If it is, login failed
                if (IsLoginPage(interactiveHtml))
                {
                    throw new Exception("Failed to login to retrieve interactive details");
                }
            }


            // Get the interactive story's title
            // This method grabs it from within the <title> element, not sure if it gets truncated or not.
            Regex interactiveTitleRegex = new Regex("(?<=<title>).+?(?= - Writing\\.Com<\\/title>)", RegexOptions.IgnoreCase);
            Match interactiveTitleMatch = interactiveTitleRegex.Match(interactiveHtml);

            if (!interactiveTitleMatch.Success)
            {
                throw new WritingClientHtmlParseException($"Couldn't find the title for interactive story '{interactiveUrl.ToString()}'", interactiveHtml);
            }
            string interactiveTitle = HttpUtility.HtmlDecode(interactiveTitleMatch.Value);

            // Get the interactive's tagline or short description
            // Previously this has been difficult o pin-point
            // However I found this on 11/01/2019, they've got it in a META tag at the top of the HTML
            // E.g. <META NAME="description" content="How will young James fare alone with his mature, womanly neighbors? ">
            Regex interactiveShortDescRegex = new Regex("(?<=<META NAME =\"description\" content=\").+?(?=\">)", RegexOptions.IgnoreCase);
            Match interactiveShortDescMatch = interactiveShortDescRegex.Match(interactiveHtml);

            if (!interactiveShortDescMatch.Success)
            {
                log.Warn($"Couldn't find the short description for interactive story '{interactiveUrl.ToString()}'"); // Just a warning, don't throw an exception over it
            }
            string interactiveShortDesc = HttpUtility.HtmlDecode(interactiveShortDescMatch.Value);


            // Get the interactive story's description
            Regex interactiveDescRegex = new Regex("(?<=<td align=left class=\"norm\">).+?(?=<\\/td>)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Match interactiveDescMatch = interactiveDescRegex.Match(interactiveHtml);

            if (!interactiveDescMatch.Success)
            {
                throw new WritingClientHtmlParseException($"Couldn't find the description for interactive story '{interactiveUrl.ToString()}'", interactiveHtml);
            }
            string interactiveDesc = HttpUtility.HtmlDecode(interactiveDescMatch.Value);

            // TODO get author, looks like it'll be a pain in the ass telling the chapter author apart from the story author

            newInteractive.Url              = interactiveUrl.ToString();
            newInteractive.UrlID            = interactiveUrl.Segments[interactiveUrl.Segments.Length - 1].TrimEnd('/'); // Did I just break this?
            newInteractive.Name             = interactiveTitle;
            newInteractive.Description      = interactiveDesc;
            newInteractive.ShortDescription = interactiveShortDesc;

            return(newInteractive);
        }
コード例 #7
0
 private void Init()
 {
     _cancelTokenSource = new CancellationTokenSource();
     _story             = null;
     IsExporting        = false;
 }
コード例 #8
0
        public async Task <WInteractiveStory> ExportStory(WInteractiveStory story, WStoryExporterExportSettings exportSettings)
        {
            var cancelToken = _cancelTokenSource.Token;

            _story = story;

            if (IsExporting)
            {
                throw new Exception("Cannot export a story while already exporting a story");
            }

            IsExporting = true;

            try
            {
                DoStateUpdate("Exporting story", 0, 1);

                // Get the story map
                await UpdateStory();

                // Cancellation support
                if (cancelToken.IsCancellationRequested)
                {
                    return(null);
                }

                // Update all the stories
                //foreach (var chapter in _story.Chapters)
                _progress             = new WStoryExporterProgress();
                _progress.ProgressMax = _story.Chapters.Count;
                for (var i = 0; i < _story.Chapters.Count; i++)
                {
                    // Cancellation support
                    if (cancelToken.IsCancellationRequested)
                    {
                        return(null);
                    }

                    _progress.ProgressIndex = i;
                    var chapter = _story.Chapters[i];

                    if (string.IsNullOrEmpty(chapter.Content) || exportSettings.UpdateExisting == true)
                    {
                        DoStateUpdate($"Exporting chapter: {chapter.Path}", i, _story.Chapters.Count);

                        await UpdateChapter(chapter);
                    }
                }

                // Done
                _log.Info("Story export complete");
                _progress = null;
                DoStateUpdate("Finished exporting story, ready to save", 1, 1);
                return(_story);
            }
            finally
            {
                IsExporting = false;
                _story      = null;
            }
        }
 public static string SerializeInteractiveStoryToString(WInteractiveStory storyToSerialize)
 {
     return(JsonConvert.SerializeObject(storyToSerialize));
 }
        public static void SerializeInteractiveStory(TextWriter stream, WInteractiveStory storyToSerialize)
        {
            JsonSerializer serializer = new JsonSerializer();

            serializer.Serialize(stream, storyToSerialize);
        }
コード例 #11
0
        public static void SerializeInteractiveStory(Stream stream, WInteractiveStory storyToSerialize)
        {
            storySerializer.Serialize(stream, storyToSerialize);

            //return JsonConvert.SerializeObject(storyToSerialize);
        }
コード例 #12
0
        public string ConvertStory(WInteractiveStory story)
        {
            var output = string.Empty;

            // Make a copy of the chapter list, so we can do some work on it
            //var chapterList = new List<WInteractiveChapter>(story.Chapters);
            var chapterList = story.Chapters.OrderBy(c => c.Path).ToList();

            // Do the story summary content block
            log.Debug("Building the story summary block");
            var storySummaryContent = Templates["StorySummary"]
                                      .Replace("{StoryTitle}", story.Name)
                                                                                                  //.Replace("{StoryAuthor}", story.Author.Name) // Author isn't currently supported
                                      .Replace("{StoryShortDescription}", story.ShortDescription) // The tagline / short description isn't currentl supported
                                      .Replace("{StoryDescription}", story.Description);

            // Do the story chapter outline content block
            log.Debug("Building the story outline content block");
            var chapterOutlineLines = new List <string>();

            foreach (var chapter in chapterList)
            {
                chapterOutlineLines.Add(
                    Templates["StoryOutlineItem"]
                    .Replace("{ChapterPathDisplay}", StoryOutlineChapterPath(chapter.Path))
                    .Replace("{ChapterPath}", chapter.Path)
                    .Replace("{ChapterName}", chapter.Title)
                    );
            }

            var chapterOutlineContent = Templates["StoryOutline"]
                                        .Replace("{Chapters}", string.Join("\n", chapterOutlineLines));

            // Build the chapter content block
            var sbChapters = new StringBuilder();

            foreach (var chapter in chapterList)
            {
                log.DebugFormat("Adding chapter {0}", chapter.Path);

                //DEBUG
                if (chapter.Path == "1131222")
                {
                    var lolwut = "test";
                }

                string choiceListBlock = "";

                if (chapter.IsEnd)
                {
                    choiceListBlock = Templates["ChapterChoiceListEnd"];
                }
                else
                {
                    var sbChapterChoices = new StringBuilder();
                    foreach (var choice in chapter.Choices)
                    {
                        bool isChoiceValid = story.Chapters.SingleOrDefault(c => c.Path == choice.MapLink) != null;
                        // Choice links to a chapter that exists, choice is valid
                        var choiceTemplate = isChoiceValid ? "ChapterChoiceValid" : "ChapterChoiceInvalid";
                        sbChapterChoices.AppendLine(Templates[choiceTemplate])
                        .Replace("{ChoicePath}", choice.MapLink)
                        .Replace("{ChoiceName}", choice.Name);
                    }

                    choiceListBlock = Templates["ChapterChoiceList"]
                                      .Replace("{Choices}", sbChapterChoices.ToString());
                }

                string sourceChoiceLink = String.Empty;
                if (chapter.Path != "1")
                {
                    sourceChoiceLink = string.Format("<a href='#{0}'>Go back</a>", chapter.Path.Substring(0, chapter.Path.Length - 1));
                }

                string chapterContentElementsClosed;
                string chapterContent = CloseUnclosedElements(chapter.Content, out chapterContentElementsClosed);
                if (!string.IsNullOrEmpty(chapterContentElementsClosed))
                {
                    log.Debug($"Chapter {chapter.Path} had unclosed HTML elements: {chapterContentElementsClosed}");
                }

                sbChapters.AppendLine(Templates["Chapter"]
                                      .Replace("{Path}", chapter.Path)
                                      .Replace("{PathDisplay}", StoryOutlineChapterPath(chapter.Path))
                                      .Replace("{Title}", chapter.Title)
                                      .Replace("{SourceChoice}", chapter.Path == "1" ? "(empty)" : chapter.SourceChoiceTitle)
                                      .Replace("{SourceChapterLink}", sourceChoiceLink)
                                      .Replace("{AuthorName}", chapter.Author.Name)
                                      .Replace("{Content}", chapterContent)
                                      .Replace("{Choices}", choiceListBlock)
                                      );
            }


            // Final put together
            output = Templates["Page"]
                     .Replace("{StorySummary}", storySummaryContent)
                     .Replace("{StoryTitle}", story.Name)
                     .Replace("{StoryOutline}", chapterOutlineContent)
                     .Replace("{Chapters}", sbChapters.ToString());


            return(output);
        }