diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..86d6f8d0 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,25 @@ +name: .NET + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 5.0.x + - name: Restore dependencies + run: dotnet restore Platforms/Anf.sln + - name: Build + run: dotnet build Platforms/Anf.sln --no-restore + - name: Test + run: dotnet test Platforms/Anf.sln --no-build --verbosity normal diff --git a/Platforms/Anf.Avalon/Anf.Avalon.csproj b/Platforms/Anf.Avalon/Anf.Avalon.csproj index 5622df18..3c2d405b 100644 --- a/Platforms/Anf.Avalon/Anf.Avalon.csproj +++ b/Platforms/Anf.Avalon/Anf.Avalon.csproj @@ -10,7 +10,7 @@ false - net472;net6.0 + net472;net5.0 net472 diff --git a/Platforms/Anf.Avalon/App.axaml.cs b/Platforms/Anf.Avalon/App.axaml.cs index 0e1e9448..0a6d8824 100644 --- a/Platforms/Anf.Avalon/App.axaml.cs +++ b/Platforms/Anf.Avalon/App.axaml.cs @@ -51,7 +51,7 @@ private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionE private void InitServices() { AppEngine.Reset(); - AppEngine.AddServices(NetworkAdapterTypes.WebRequest); + AppEngine.AddServices(NetworkAdapterTypes.HttpClient); var store = FileStoreService.FromMd5Default(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, XComicConst.CacheFolderName)); var hp = new Lazy(() => new HomePage()); var cv = new Lazy(() => new ComicView()); @@ -90,8 +90,8 @@ public override void OnFrameworkInitializationCompleted() var nav = AppEngine.GetRequiredService(); var mainWin = AppEngine.GetRequiredService(); desktop.MainWindow =mainWin; - nav.Navigate(new VisitingView()); - //nav.Navigate(); + //nav.Navigate(new VisitingView()); + nav.Navigate(); AppEngine.GetRequiredService().Bind(mainWin); mainWin.KeyDown += OnMainWinKeyDown; diff --git a/Platforms/Anf.Avalon/MainWindow.axaml b/Platforms/Anf.Avalon/MainWindow.axaml index 178f5528..9f9efe72 100644 --- a/Platforms/Anf.Avalon/MainWindow.axaml +++ b/Platforms/Anf.Avalon/MainWindow.axaml @@ -9,33 +9,35 @@ x:Class="Anf.Avalon.MainWindow" Title="Anf"> - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + IsVisible="{Binding HasException}" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + Padding="16"> @@ -65,6 +67,5 @@ - diff --git a/Platforms/Anf.Avalon/ViewModels/AvalonVisitingViewModel.cs b/Platforms/Anf.Avalon/ViewModels/AvalonVisitingViewModel.cs index 8bf66c3a..33b7332d 100644 --- a/Platforms/Anf.Avalon/ViewModels/AvalonVisitingViewModel.cs +++ b/Platforms/Anf.Avalon/ViewModels/AvalonVisitingViewModel.cs @@ -211,5 +211,10 @@ protected async override void OnCurrentChaterCursorChanged(IDataCursor - + - + diff --git a/Platforms/Anf.Web/Anf.Web.csproj b/Platforms/Anf.Web/Anf.Web.csproj index 8d994a0a..32ccc88d 100644 --- a/Platforms/Anf.Web/Anf.Web.csproj +++ b/Platforms/Anf.Web/Anf.Web.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net5.0 8.0 $(NoWarn);CS1591 false diff --git a/Platforms/Engines/Anf.KnowEngines/BilibiliComicOperator.cs b/Platforms/Engines/Anf.KnowEngines/BilibiliComicOperator.cs index 62b1eba1..67bf4239 100644 --- a/Platforms/Engines/Anf.KnowEngines/BilibiliComicOperator.cs +++ b/Platforms/Engines/Anf.KnowEngines/BilibiliComicOperator.cs @@ -1,4 +1,7 @@ using Anf.Networks; +using HtmlAgilityPack; +using JavaScriptEngineSwitcher.Core; +using Jint.Native.Array; #if !NETSTANDARD1_3 using Microsoft.IO; #endif @@ -9,6 +12,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Anf.KnowEngines diff --git a/Platforms/Engines/Anf.KnowEngines/KnowEnginesExtensions.cs b/Platforms/Engines/Anf.KnowEngines/KnowEnginesExtensions.cs index 1c724267..2adf1e00 100644 --- a/Platforms/Engines/Anf.KnowEngines/KnowEnginesExtensions.cs +++ b/Platforms/Engines/Anf.KnowEngines/KnowEnginesExtensions.cs @@ -18,6 +18,8 @@ public static void AddKnowEngines(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } public static void UseKnowEngines(this IServiceProvider provider) { @@ -29,6 +31,8 @@ public static void UseKnowEngines(this IServiceProvider provider) eng.Add(new TencentComicSourceCondition()); eng.Add(new BilibiliComicSourceCondition()); eng.Add(new QimianComicSourceCondition()); + eng.Add(new MangabzComicCondition()); + eng.Add(new XmanhuaComicCondition()); var searchEng = provider.GetRequiredService(); searchEng.Add(typeof(SomanSearchProvider)); } diff --git a/Platforms/Engines/Anf.KnowEngines/MangabzComicCondition.cs b/Platforms/Engines/Anf.KnowEngines/MangabzComicCondition.cs new file mode 100644 index 00000000..f156f0fe --- /dev/null +++ b/Platforms/Engines/Anf.KnowEngines/MangabzComicCondition.cs @@ -0,0 +1,16 @@ +using System; + +namespace Anf.KnowEngines +{ + public class MangabzComicCondition : ComicSourceConditionBase + { + public override string EnginName => "Mangabz"; + + public override Uri Address { get; } = new Uri("http://www.mangabz.com/"); + + public override bool Condition(ComicSourceContext context) + { + return context.Uri.Host == Address.Host; + } + } +} diff --git a/Platforms/Engines/Anf.KnowEngines/MangabzComicOperator.cs b/Platforms/Engines/Anf.KnowEngines/MangabzComicOperator.cs new file mode 100644 index 00000000..ced93c22 --- /dev/null +++ b/Platforms/Engines/Anf.KnowEngines/MangabzComicOperator.cs @@ -0,0 +1,164 @@ +using Anf.Networks; +using HtmlAgilityPack; +using JavaScriptEngineSwitcher.Core; +using Jint.Native.Array; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Anf.KnowEngines +{ + public class MangabzComicOperator : IComicSourceProvider + { + private static readonly Regex cidRegex = new Regex(@"var MANGABZ_CID=(.*)?;", RegexOptions.Compiled); + private static readonly Regex dtRegex = new Regex(@"var MANGABZ_VIEWSIGN_DT=""(.*)?"";", RegexOptions.Compiled); + private static readonly Regex midRegex = new Regex(@"var MANGABZ_MID=(.*)?;", RegexOptions.Compiled); + private static readonly Regex viewSignRegex = new Regex(@"var MANGABZ_VIEWSIGN=(.*)?;", RegexOptions.Compiled); + private static readonly Regex imageCountRegex = new Regex(@"var MANGABZ_IMAGE_COUNT=(.*)?;", RegexOptions.Compiled); + + private readonly INetworkAdapter networkAdapter; + private readonly IJsEngine jsEngine; + private static readonly Dictionary headers = new Dictionary + { + ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4437.0 Safari/537.36 Edg/91.0.831.1", + }; + + public MangabzComicOperator(INetworkAdapter networkAdapter, IJsEngine jsEngine) + { + this.networkAdapter = networkAdapter; + this.jsEngine = jsEngine; + } + + protected virtual Task GetStreamAsync(string address) + { + return networkAdapter.GetStreamAsync(new RequestSettings + { + Address = address, + Host = new Uri(address).Host, + Referrer = GetBaseAddress(), + Headers = headers + }); + } + protected virtual string GetBaseAddress() + { + return "http://www.mangabz.com/"; + } + public async Task GetChaptersAsync(string targetUrl) + { + var str = string.Empty; + using (var sr = new StreamReader(await GetStreamAsync(targetUrl))) + { + str = sr.ReadToEnd(); + } + var html = new HtmlDocument(); + html.LoadHtml(str); + var img = html.DocumentNode.SelectSingleNode("//div[@class='detail-info']/img[@class='detail-info-cover']"); + var title = html.DocumentNode.SelectSingleNode("//div[@class='detail-info']/p[@class='detail-info-title']"); + var desc = html.DocumentNode.SelectSingleNode("//div[@class='detail-info']/div[@class='detail-info-content']"); + + var chpsBox = html.DocumentNode.SelectNodes("//div[@id='chapterlistload']/a"); + var chps = new List(); + foreach (var item in chpsBox) + { + var name = item.InnerText.Replace(" ", string.Empty); + var addr = item.Attributes["href"].Value; + var chp = new ComicChapter + { + Title = name, + TargetUrl = "http://www.mangabz.com" + addr + }; + chps.Add(chp); + } + var entity = new ComicEntity + { + ComicUrl = targetUrl, + Descript = desc?.InnerText, + ImageUrl = img?.Attributes["src"].Value, + Name = title?.InnerText, + Chapters = chps.ToArray() + }; + return entity; + } + + public Task GetImageStreamAsync(string targetUrl) + { + return GetStreamAsync(targetUrl); + } + + public async Task GetPagesAsync(string targetUrl) + { + var str = string.Empty; + using (var sr = new StreamReader(await GetStreamAsync(targetUrl))) + { + str = sr.ReadToEnd(); + } + var cidRgx = cidRegex.Match(str).Groups[0].Value; + var dtRgx = dtRegex.Match(str).Groups[0].Value; + var midRgx = midRegex.Match(str).Groups[0].Value; + var viewSignRgx = viewSignRegex.Match(str).Groups[0].Value; + var imgCountRgx = imageCountRegex.Match(str).Groups[0].Value; + + var cid = cidRgx.Substring(0, cidRgx.IndexOf(';')).Split('=').Last(); + var dt = dtRgx.Substring(0, dtRgx.IndexOf(';')).Split('=').Last().Trim('\"'); + var mid = midRgx.Substring(0, midRgx.IndexOf(';')).Split('=').Last(); + var viewSign = viewSignRgx.Substring(0, viewSignRgx.IndexOf(';')).Split('=').Last().Trim('\"'); + var imgCount = imgCountRgx.Substring(0, imgCountRgx.IndexOf(';')).Split('=').Last().Trim('\"'); + var val = int.Parse(imgCount); + + var refAddr = new Uri(targetUrl).Segments.Last(); + + var part = $"{GetBaseAddress()}/{refAddr}/chapterimage.ashx?cid={cid}&page={{0}}&key=&_cid={cid}&_mid={mid}&_dt={DateTime.Now}&_sign={viewSign}"; + + async Task RunBlockAsync(int index) + { + var pgs = new List(); + var partBlock = string.Format(part, index + 1); + + string partEncod = null; + using (var sr = new StreamReader(await GetStreamAsync(partBlock))) + { + partEncod = sr.ReadToEnd(); + } + if (!string.IsNullOrEmpty(partEncod)) + { + var ret = (ArrayInstance)jsEngine.Evaluate(partEncod); + var length = ret.GetLength(); + for (int i = 0; i < length; i++) + { + pgs.Add(new ComicPage + { + Name = (index + 1).ToString(), + TargetUrl = ret.GetProperty(i.ToString()).Value.ToString() + }); + } + } + return pgs.ToArray(); + } + var datas = new List(); + for (int i = 0; i < val; i++) + { + var j = i; + datas.Add(await RunBlockAsync(j)); + } + var containPages = new HashSet(); + var pages = new List(datas.Count); + for (int i = 0; i < datas.Count; i++) + { + var res = datas[i]; + for (int q = 0; q < res.Length; q++) + { + var r = res[q]; + if (containPages.Add(r.TargetUrl)) + { + pages.Add(r); + } + } + } + return pages.ToArray(); + } + } +} diff --git a/Platforms/Engines/Anf.KnowEngines/XmanhuaComicCondition.cs b/Platforms/Engines/Anf.KnowEngines/XmanhuaComicCondition.cs new file mode 100644 index 00000000..30f16d1e --- /dev/null +++ b/Platforms/Engines/Anf.KnowEngines/XmanhuaComicCondition.cs @@ -0,0 +1,16 @@ +using System; + +namespace Anf.KnowEngines +{ + public class XmanhuaComicCondition : ComicSourceConditionBase + { + public override string EnginName => "Xmanhua"; + + public override Uri Address { get; } = new Uri("http://www.xmanhua.com/"); + + public override bool Condition(ComicSourceContext context) + { + return context.Uri.Host == Address.Host; + } + } +} diff --git a/Platforms/Engines/Anf.KnowEngines/XmanhuaComicOperator.cs b/Platforms/Engines/Anf.KnowEngines/XmanhuaComicOperator.cs new file mode 100644 index 00000000..aaa9d1fd --- /dev/null +++ b/Platforms/Engines/Anf.KnowEngines/XmanhuaComicOperator.cs @@ -0,0 +1,17 @@ +using Anf.Networks; +using JavaScriptEngineSwitcher.Core; + +namespace Anf.KnowEngines +{ + public class XmanhuaComicOperator : MangabzComicOperator + { + public XmanhuaComicOperator(INetworkAdapter networkAdapter, IJsEngine jsEngine) : base(networkAdapter, jsEngine) + { + } + + protected override string GetBaseAddress() + { + return "http://www.xmanhua.com"; + } + } +} diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..3123672c --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,54 @@ +# .NET Desktop +# Build and run tests for .NET Desktop or Windows classic desktop solutions. +# Add steps that publish symbols, save build artifacts, and more: +# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net + +trigger: +- dev + +pool: + vmImage: 'windows-latest' + +variables: + solution: '**/*.sln' + buildPlatform: 'Any CPU' + buildConfiguration: 'Release' + +steps: +- task: NuGetToolInstaller@1 + +- task: NuGetCommand@2 + inputs: + restoreSolution: '$(solution)' + +- task: VSBuild@1 + inputs: + solution: '$(solution)' + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)' + +- task: VSTest@2 + inputs: + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: 'dotnet test' + inputs: + command: test + projects: '**/*.Test.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "XPlat Code coverage" -- RunConfiguration.DisableAppDomain=true' + testRunTitle: MyProject.UnitTests + +- script: 'dotnet tool install --global dotnet-reportgenerator-globaltool --version 4.5.8' + displayName: 'Install ReportGenerator tool' + +- script: 'reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/coverlet/reports -reporttypes:"Cobertura"' + displayName: 'Create reports' + +- task: PublishCodeCoverageResults@1 + displayName: 'Publish code coverage' + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '$(Build.SourcesDirectory)/coverlet/reports/Cobertura.xml' + diff --git a/test/Anf.Easy.Test/Anf.Easy.Test.csproj b/test/Anf.Easy.Test/Anf.Easy.Test.csproj index 38afeb6b..311620a1 100644 --- a/test/Anf.Easy.Test/Anf.Easy.Test.csproj +++ b/test/Anf.Easy.Test/Anf.Easy.Test.csproj @@ -6,6 +6,7 @@ + diff --git a/test/Anf.Easy.Test/ComicHostExtensionsTest.cs b/test/Anf.Easy.Test/ComicHostExtensionsTest.cs index 0c092832..417d0911 100644 --- a/test/Anf.Easy.Test/ComicHostExtensionsTest.cs +++ b/test/Anf.Easy.Test/ComicHostExtensionsTest.cs @@ -30,7 +30,7 @@ public async Task GetVisitingAndLoadAsync_MustReturnLoadedVisitor() [typeof(IComicVisiting)]=()=>ComicVisitingHelper.CreateResrouceVisitor() } }; - var visitor=await ComicHostExtensions.GetVisitingAndLoadAsync(provider, "http://localhost:8765"); + var visitor=await ComicHostExtensions.GetVisitingAndLoadAsync(provider, "http://localhost:8887"); Assert.IsNotNull(visitor); Assert.IsNotNull(visitor.Entity); } @@ -44,7 +44,7 @@ public async Task GetVisitingAndLoadAsync_FailToLoad_MustReturnNull() [typeof(IComicVisiting)] = () => new NullComicVisiting { LoadSucceed=false} } }; - var visitor = await ComicHostExtensions.GetVisitingAndLoadAsync(provider, "http://localhost:8765"); + var visitor = await ComicHostExtensions.GetVisitingAndLoadAsync(provider, "http://localhost:8889"); Assert.IsNull(visitor); } [TestMethod] diff --git a/test/Anf.Easy.Test/Downloading/DownloadManagersTest.cs b/test/Anf.Easy.Test/Downloading/DownloadManagersTest.cs index 566a6e97..34102822 100644 --- a/test/Anf.Easy.Test/Downloading/DownloadManagersTest.cs +++ b/test/Anf.Easy.Test/Downloading/DownloadManagersTest.cs @@ -22,7 +22,7 @@ private async Task Run(AsyncDownloadManager mgr,int taskCount,int preCount) } mgr.Start(); var tks = new CancellationTokenSource(); - var timeOutTask = Task.Delay(10 * 1000).ContinueWith(_ => tks.Cancel()); + var timeOutTask = Task.Delay(TimeSpan.FromMinutes(30)).ContinueWith(_ => tks.Cancel()); while (!tks.IsCancellationRequested) { if (mgr.Count == 0) diff --git a/test/Anf.Easy.Test/Visiting/BlockSlotsTest.cs b/test/Anf.Easy.Test/Visiting/BlockSlotsTest.cs index de0c869d..bdd7d41f 100644 --- a/test/Anf.Easy.Test/Visiting/BlockSlotsTest.cs +++ b/test/Anf.Easy.Test/Visiting/BlockSlotsTest.cs @@ -39,9 +39,10 @@ public async Task GivenDisposeableObject_Dispose_AllDisposed() await slot.GetAsync(i); } slot.Dispose(); - for (int i = 0; i < slot.Size; i++) + var objs = slot.GetCreatedValues().OfType().ToArray(); + for (int i = 0; i < objs.Length; i++) { - var obj = (DispoableObject)slot[i]; + var obj = objs[i]; Assert.IsTrue(obj.IsDisposed); } } diff --git a/test/Anf.Easy.Test/Visiting/ComicVisitingHelper.cs b/test/Anf.Easy.Test/Visiting/ComicVisitingHelper.cs index 4c1010e0..bbfc5823 100644 --- a/test/Anf.Easy.Test/Visiting/ComicVisitingHelper.cs +++ b/test/Anf.Easy.Test/Visiting/ComicVisitingHelper.cs @@ -9,7 +9,7 @@ namespace Anf.Easy.Test.Visiting { internal static class ComicVisitingHelper { - public static readonly Uri AnyUri = new Uri("http://localhost:8765/"); + public static readonly Uri AnyUri = new Uri("http://localhost:8886/"); public static ComicVisiting CreateResrouceVisitor() { var creator = new StreamResourceFactoryCreator();