/// <summary> /// Получить URI треда. /// </summary> /// <param name="link">Ссылка.</param> /// <param name="isReferer">Реферер.</param> /// <returns>URI.</returns> public Uri GetThreadUri(ThreadLink link, bool isReferer) { if (!isReferer) { return new Uri(BaseUri, string.Format("{0}/res/{1}.json", link.Board, link.Thread)); } return new Uri(BaseUri, string.Format("{0}/res/{1}.html", link.Board, link.Thread)); }
private async Task SpeedTest() { var sw = new Stopwatch(); sw.Start(); var storage = ServiceLocator.Current.GetServiceOrThrow<IStorageService>(); var link = new ThreadLink() { Engine = "makaba", Board = "mobi", Thread = 665542 }; for (var i = 0; i < 100; i++) { await storage.ThreadData.LoadThread(link); } sw.Stop(); await AppHelpers.ShowMessage($"{sw.Elapsed}"); }
/// <summary> /// Парсить частичные данные треда. /// </summary> /// <param name="data">Данные.</param> /// <param name="link">Ссылка.</param> /// <returns>Результат.</returns> public ThreadTreePartial ParseThreadPartial(BoardPost2[] data, ThreadPartLink link) { var threadLink = new ThreadLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, Thread = link.Thread }; var result = new ThreadTreePartial() { Link = threadLink, Posts = data.OrderBy(p => p.Number.TryParseWithDefault()).Select(p => Parse(p, threadLink, false)).ToList(), Extensions = new List<PostTreeCollectionExtension>(), ParentLink = new BoardLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, }, }; return result; }
/// <summary> /// Парсить данные треда. /// </summary> /// <param name="data">Данные.</param> /// <param name="link">Ссылка.</param> /// <returns>Результат.</returns> public ThreadTree ParseThread(BoardEntity2 data, ThreadLink link) { var entity = GetEntityTree(data); var result = new ThreadTree() { Link = link, ParentLink = new BoardLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, }, Extensions = new List<PostTreeCollectionExtension>(), Posts = data.Threads.SelectMany(p => p.Posts).OrderBy(p => p.Number.TryParseWithDefault()).Select(p => Parse(p, link, false)).ToList(), }; result.Extensions.Add(new MakabaCollectionExtension() { Entity = entity }); for (int i = 0; i < result.Posts.Count; i++) { result.Posts[i].Counter = i + 1; } return result; }
/// <summary> /// Разобрать HTML в посте. /// </summary> /// <param name="data">Данные.</param> /// <param name="link">Ссылка.</param> /// <param name="isPreview">Предварительный просмотр треда.</param> /// <returns>Результат разбора.</returns> private PostTree Parse(BoardPost2 data, ThreadLink link, bool isPreview) { var ipIdRegex = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(IpIdRegexText); var ipIdRegex2 = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(IpIdRegexText2); var colorRegex = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(ColorRegexText); PostFlags flags = 0; if (data.Banned != "0" && !string.IsNullOrWhiteSpace(data.Banned)) { flags |= PostFlags.Banned; } if (data.Closed != "0" && !string.IsNullOrWhiteSpace(data.Closed)) { flags |= PostFlags.Closed; } if (data.Sticky != "0" && !string.IsNullOrWhiteSpace(data.Sticky)) { flags |= PostFlags.Sticky; } if (isPreview) { flags |= PostFlags.ThreadPreview; } if (data.Op != "0" && !string.IsNullOrWhiteSpace(data.Op)) { flags |= PostFlags.Op; } if ("mailto:sage".Equals((data.Email ?? "").Trim(), StringComparison.OrdinalIgnoreCase)) { flags |= PostFlags.Sage; } if (data.Edited != "0" && !string.IsNullOrWhiteSpace(data.Edited)) { flags |= PostFlags.IsEdited; } string admName = null; if (data.Tripcode != null) { if (data.Tripcode.StartsWith("!!%") && data.Tripcode.EndsWith("%!!")) { if ("!!%mod%!!".Equals(data.Tripcode)) { admName = "## Mod ##"; } else if ("!!%adm%!!".Equals(data.Tripcode)) { admName = "## Abu ##"; } else if ("!!%Inquisitor%!!".Equals(data.Tripcode)) { admName = "## Applejack ##"; } else if ("!!%coder%!!".Equals(data.Tripcode)) { admName = "## Кодер ##"; } else admName = data.Tripcode.Replace("!!%", "## ").Replace("%!!", " ##"); flags |= PostFlags.AdminTrip; } } var number = data.Number.TryParseWithDefault(); var postNodes = Services.GetServiceOrThrow<IMakabaHtmlPostParseService>().GetPostNodes(data.Comment ?? ""); var name = admName != null && string.IsNullOrWhiteSpace(data.Name) ? admName : WebUtility.HtmlDecode(data.Name ?? string.Empty).Replace(" ", " "); var result = new PostTree() { Link = new PostLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, Thread = link.Thread, Post = number }, ParentLink = new ThreadLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, Thread = link.Thread }, Subject = WebUtility.HtmlDecode(data.Subject ?? string.Empty), BoardSpecificDate = data.Date, Date = Services.GetServiceOrThrow<IDateService>().FromUnixTime(data.Timestamp.TryParseWithDefault()), Flags = flags, Quotes = new List<BoardLinkBase>(), Comment = new PostNodes() { Nodes = postNodes.ToList() }, Hash = data.Md5, Extensions = new List<PostTreeExtension>(), Email = data.Email, Media = new List<PostMediaBase>(), }; result.GeneratePostStamp(); if (string.IsNullOrWhiteSpace(result.Subject) && number == link.Thread) { try { var lines = ToPlainText(result); if (lines.Count > 0) { var s = lines.Where(l => !string.IsNullOrWhiteSpace(l)).FirstOrDefault(); if (s != null) { if (s.Length >= 50) { s = s.Substring(0, 50 - 3) + "..."; } result.Subject = s; } } } catch { } } string nameColor = null; PostColor color = null; var match = ipIdRegex.Match(name); var match2 = ipIdRegex2.Match(name); if (match.Success) { name = match.Groups["id"].Captures[0].Value; } else if (match2.Success) { name = match2.Groups["id"].Captures[0].Value; nameColor = match2.Groups["style"].Captures[0].Value; var cmatch = colorRegex.Match(nameColor); if (cmatch.Success) { try { var r = byte.Parse(cmatch.Groups["r"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat); var g = byte.Parse(cmatch.Groups["g"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat); var b = byte.Parse(cmatch.Groups["b"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat); color = new PostColor() { R = r, G = g, B = b }; } catch (Exception) { color = null; } } } else if (name.StartsWith("Аноним ID:", StringComparison.OrdinalIgnoreCase)) { name = name.Remove(0, "Аноним ID:".Length).Trim(); } if (!string.IsNullOrEmpty(name) || !string.IsNullOrWhiteSpace(data.Tripcode)) { result.Extensions.Add(new PostTreePosterExtension() { Name = HtmlToText(name ?? ""), /* Windows.Data.Html.HtmlUtilities.ConvertToText работает очень медленно, потому что вызывает EDGE через COM, не годится для массовых обработок*/ //Name = Windows.Data.Html.HtmlUtilities.ConvertToText(name ?? ""), Tripcode = data.Tripcode, NameColorStr = nameColor, NameColor = color }); } var flagAndIcon = ParseFlagAndIcon(data.Icon); if (flagAndIcon.Icon != null) { result.Extensions.Add(flagAndIcon.Icon); } if (flagAndIcon.Country != null) { result.Extensions.Add(flagAndIcon.Country); } if (!string.IsNullOrWhiteSpace(data.Tags)) { result.Extensions.Add(new PostTreeTagsExtension() { TagString = data.Tags, Tags = new List<string>() { data.Tags } }); } if (data.Files != null) { foreach (var f in data.Files) { BoardLinkBase mediaLink, tnLink; if (IsBoardLink(f.Path, link.Board)) { mediaLink = new BoardMediaLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, RelativeUri = RemoveBoardFromLink(f.Path), KnownMediaType = f.Type == MakabaMediaTypes.Webm ? KnownMediaType.Webm : KnownMediaType.Default }; tnLink = new BoardMediaLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, RelativeUri = RemoveBoardFromLink(f.Thumbnail), KnownMediaType = KnownMediaType.Default }; } else { mediaLink = new MediaLink() { Engine = CoreConstants.Engine.Makaba, RelativeUri = f.Path, IsAbsolute = false, }; tnLink = new MediaLink() { Engine = CoreConstants.Engine.Makaba, RelativeUri = f.Thumbnail, IsAbsolute = false }; } var media = new PostImageWithThumbnail() { Link = mediaLink, ParentLink = result.Link, Size = f.Size * 1024, Height = f.Heigth, Width = f.Width, Name = f.Name, Thumbnail = new PostImage() { Link = tnLink, ParentLink = mediaLink, Height = f.TnHeight, Width = f.TnWidth, Size = null, Name = f.Name } }; result.Media.Add(media); } } return result; }
/// <summary> /// Парсить данные страницы борды. /// </summary> /// <param name="data">Данные.</param> /// <param name="link">Ссылка.</param> /// <returns>Результат.</returns> public BoardPageTree ParseBoardPage(BoardEntity2 data, BoardPageLink link) { var entity = GetEntityTree(data); var result = new BoardPageTree() { Extensions = new List<BoardPageTreeExtension>(), LastModified = null, Link = link, ParentLink = new RootLink() { Engine = CoreConstants.Engine.Makaba }, PageNumber = link.Page, Threads = new List<ThreadPreviewTree>(), MaxPage = entity.Pages != null ? entity.Pages.OrderByDescending(p => p).Select(p => (int?)p).FirstOrDefault() : null, }; result.Extensions.Add(new MakabaBoardPageExtension() { Entity = entity }); if (data.Threads != null) { foreach (var t in data.Threads) { var posts = t.Posts.OrderBy(p => p.Number.TryParseWithDefault()).ToArray(); var tnum = posts.First().Number.TryParseWithDefault(); var tlink = new ThreadLink() { Engine = CoreConstants.Engine.Makaba, Board = link.Board, Thread = tnum, }; var thread = new ThreadPreviewTree() { Extensions = new List<PostTreeCollectionExtension>(), Link = tlink, ParentLink = link, ImageCount = t.ImagesCount.TryParseWithDefault() + posts.Sum(p => (p.Files ?? new BoardPostFile2[0]).Length), Omit = t.PostsCount.TryParseWithDefault(), OmitImages = t.ImagesCount.TryParseWithDefault(), ReplyCount = Math.Max(t.PostsCount.TryParseWithDefault() + posts.Count(), 0), Posts = posts.Select(p => Parse(p, tlink, true)).ToList(), }; for (var i = 0; i < thread.Posts.Count; i++) { thread.Posts[i].Flags |= PostFlags.ThreadPreview; if (i == 0) { thread.Posts[i].Counter = 1; thread.Posts[i].Flags |= PostFlags.ThreadPreviewOpPost; } else { var ni = thread.Posts.Count - i; thread.Posts[i].Counter = thread.ReplyCount - ni; } } result.Threads.Add(thread); } } return result; }
/// <summary> /// Получить информацию о треде. /// </summary> /// <param name="link">Ссылка.</param> /// <returns>Информация.</returns> public Uri GetLastThreadInfoUri(ThreadLink link) { return new Uri(BaseUri, string.Format("makaba/mobile.fcgi?task=get_thread_last_info&board={0}&thread={1}", link.Board, link.Thread)); }