wabbajack/Wabbajack.Lib/Downloaders/ModDBDownloader.cs

126 lines
4.3 KiB
C#
Raw Normal View History

using System;
using System.Linq;
using System.Net.Http;
2019-10-12 22:31:07 +00:00
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using HtmlAgilityPack;
2020-03-26 23:02:15 +00:00
using MessagePack;
2019-10-12 22:31:07 +00:00
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
2019-10-12 22:31:07 +00:00
namespace Wabbajack.Lib.Downloaders
2019-10-12 22:31:07 +00:00
{
public class ModDBDownloader : IDownloader, IUrlDownloader
2019-10-12 22:31:07 +00:00
{
2020-04-03 03:57:59 +00:00
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
2019-10-12 22:31:07 +00:00
{
2019-11-21 15:51:57 +00:00
var url = archiveINI?.General?.directURL;
return GetDownloaderState(url);
}
2019-10-12 22:31:07 +00:00
public AbstractDownloadState GetDownloaderState(string url)
{
2019-10-12 22:31:07 +00:00
if (url != null && url.StartsWith("https://www.moddb.com/downloads/start"))
{
return new State
{
Url = url
2019-10-12 22:31:07 +00:00
};
}
return null;
}
2019-12-07 02:45:13 +00:00
public async Task Prepare()
2019-10-12 22:31:07 +00:00
{
}
2020-03-26 23:02:15 +00:00
[MessagePackObject]
2019-10-12 22:31:07 +00:00
public class State : AbstractDownloadState
{
2020-03-26 23:02:15 +00:00
[Key(0)]
public string Url { get; set; }
2020-03-26 23:02:15 +00:00
[IgnoreMember]
public override object[] PrimaryKey { get => new object[]{Url}; }
2019-10-12 22:31:07 +00:00
public override bool IsWhitelisted(ServerWhitelist whitelist)
{
// Everything from Moddb is whitelisted
return true;
}
2020-03-25 22:30:43 +00:00
public override async Task<bool> Download(Archive a, AbsolutePath destination)
2019-10-12 22:31:07 +00:00
{
var urls = await GetDownloadUrls();
Utils.Log($"Found {urls.Length} ModDB mirrors for {a.Name}");
foreach (var (url, idx) in urls.Zip(Enumerable.Range(0, urls.Length), (s, i) => (s, i)))
{
try
{
await new HTTPDownloader.State {Url = url}.Download(a, destination);
return true;
}
catch (Exception)
{
if (idx == urls.Length - 1)
throw;
Utils.Log($"Download from {url} failed, trying next mirror");
}
}
return false;
2019-10-12 22:31:07 +00:00
}
private async Task<string[]> GetDownloadUrls()
2019-10-12 22:31:07 +00:00
{
var uri = new Uri(Url);
var modId = uri.AbsolutePath.Split('/').Reverse().First(f => int.TryParse(f, out int _));
var mirrorUrl = $"https://www.moddb.com/downloads/start/{modId}/all";
var doc = await new HtmlWeb().LoadFromWebAsync($"https://www.moddb.com/downloads/start/{modId}/all");
var mirrors = doc.DocumentNode.Descendants().Where(d => d.NodeType == HtmlNodeType.Element && d.HasClass("row"))
.Select(d => new
{
Link = "https://www.moddb.com"+
d.Descendants().Where(s => s.Id == "downloadon")
.Select(i => i.GetAttributeValue("href", ""))
.FirstOrDefault(),
Load = d.Descendants().Where(s => s.HasClass("subheading"))
.Select(i => i.InnerHtml.Split(',')
.Last()
.Split('%')
.Select(v => double.TryParse(v, out var dr) ? dr : double.MaxValue)
.First())
.FirstOrDefault()
})
.OrderBy(d => d.Load)
.ToList();
return mirrors.Select(d => d.Link).ToArray();
2019-10-12 22:31:07 +00:00
}
public override async Task<bool> Verify(Archive a)
2019-10-12 22:31:07 +00:00
{
await GetDownloadUrls();
return true;
2019-10-12 22:31:07 +00:00
}
public override IDownloader GetDownloader()
{
return DownloadDispatcher.GetInstance<ModDBDownloader>();
}
public override string GetManifestURL(Archive a)
{
return Url;
}
public override string[] GetMetaIni()
{
return new[] {"[General]", $"directURL={Url}"};
}
2019-10-12 22:31:07 +00:00
}
}
}