1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-06 05:34:05 +02:00

Fix downloading images from DMs

This commit is contained in:
chylex 2022-01-19 02:19:04 +01:00
parent af5d785ff2
commit 38b1057a4c
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
9 changed files with 98 additions and 64 deletions

View File

@ -1,7 +1,9 @@
using System;
using System.IO;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Browser.Handling;
using TweetDuck.Management;
using TweetDuck.Utils;
using TweetLib.Browser.Base;
using TweetLib.Browser.Events;
@ -15,8 +17,7 @@ internal abstract class CefBrowserComponent : IBrowserComponent {
public bool Ready { get; private set; }
public string Url => browser.Address;
public IFileDownloader FileDownloader => TwitterFileDownloader.Instance;
public string CacheFolder => BrowserCache.CacheFolder;
public event EventHandler<BrowserLoadedEventArgs> BrowserLoaded;
public event EventHandler<PageLoadEventArgs> PageLoadStart;
@ -100,5 +101,25 @@ public void RunScript(string identifier, string script) {
using IFrame frame = browser.GetMainFrame();
frame.ExecuteJavaScriptAsync(script, identifier, 1);
}
public void DownloadFile(string url, string path, Action onSuccess, Action<Exception> onError) {
Cef.UIThreadTaskFactory.StartNew(() => {
try {
using IFrame frame = browser.GetMainFrame();
var request = frame.CreateRequest(false);
request.Method = "GET";
request.Url = url;
request.Flags = UrlRequestFlags.AllowStoredCredentials;
request.SetReferrer(Url, ReferrerPolicy.Default);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
var client = new DownloadRequestClient(fileStream, onSuccess, onError);
frame.CreateUrlRequest(request, client);
} catch (Exception e) {
onError?.Invoke(e);
}
});
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.IO;
using CefSharp;
namespace TweetDuck.Browser.Handling {
sealed class DownloadRequestClient : UrlRequestClient {
private readonly FileStream fileStream;
private readonly Action onSuccess;
private readonly Action<Exception> onError;
private bool hasFailed;
public DownloadRequestClient(FileStream fileStream, Action onSuccess, Action<Exception> onError) {
this.fileStream = fileStream;
this.onSuccess = onSuccess;
this.onError = onError;
}
protected override bool GetAuthCredentials(bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) {
onError?.Invoke(new Exception("This URL requires authentication."));
fileStream.Dispose();
hasFailed = true;
return false;
}
protected override void OnDownloadData(IUrlRequest request, Stream data) {
if (hasFailed) {
return;
}
try {
data.CopyTo(fileStream);
} catch (Exception e) {
fileStream.Dispose();
onError?.Invoke(e);
hasFailed = true;
}
}
protected override void OnRequestComplete(IUrlRequest request) {
if (hasFailed) {
return;
}
bool isEmpty = fileStream.Position == 0;
fileStream.Dispose();
var status = request.RequestStatus;
if (status == UrlRequestStatus.Failed) {
onError?.Invoke(new Exception("Unknown error."));
}
else if (status == UrlRequestStatus.Success) {
if (isEmpty) {
onError?.Invoke(new Exception("File is empty."));
return;
}
onSuccess?.Invoke();
}
}
}
}

View File

@ -106,6 +106,7 @@
<Compile Include="Browser\Handling\ContextMenuNotification.cs" />
<Compile Include="Browser\Handling\CustomKeyboardHandler.cs" />
<Compile Include="Browser\Handling\CustomLifeSpanHandler.cs" />
<Compile Include="Browser\Handling\DownloadRequestClient.cs" />
<Compile Include="Browser\Handling\DragHandlerBrowser.cs" />
<Compile Include="Browser\Handling\FileDialogHandler.cs" />
<Compile Include="Browser\Handling\JavaScriptDialogHandler.cs" />
@ -138,7 +139,6 @@
<Compile Include="Updates\UpdateInstaller.cs" />
<Compile Include="Utils\BrowserUtils.cs" />
<Compile Include="Utils\NativeMethods.cs" />
<Compile Include="Utils\TwitterFileDownloader.cs" />
<Compile Include="Utils\WindowsUtils.cs" />
<Compile Include="Version.cs" />
</ItemGroup>

View File

@ -1,41 +0,0 @@
using System;
using System.Net;
using System.Threading.Tasks;
using CefSharp;
using TweetDuck.Management;
using TweetLib.Browser.Interfaces;
using TweetLib.Utils.Static;
using Cookie = CefSharp.Cookie;
namespace TweetDuck.Utils {
sealed class TwitterFileDownloader : IFileDownloader {
public static TwitterFileDownloader Instance { get; } = new TwitterFileDownloader();
private TwitterFileDownloader() {}
public string CacheFolder => BrowserCache.CacheFolder;
public void DownloadFile(string url, string path, Action onSuccess, Action<Exception> onFailure) {
const string authCookieName = "auth_token";
using ICookieManager cookies = Cef.GetGlobalCookieManager();
cookies.VisitUrlCookiesAsync(url, true).ContinueWith(task => {
string cookieStr = null;
if (task.Status == TaskStatus.RanToCompletion) {
Cookie found = task.Result?.Find(cookie => cookie.Name == authCookieName); // the list may be null
if (found != null) {
cookieStr = $"{found.Name}={found.Value}";
}
}
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentChrome);
client.Headers[HttpRequestHeader.Cookie] = cookieStr;
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(path, onSuccess, onFailure);
client.DownloadFileAsync(new Uri(url), path);
});
}
}
}

View File

@ -5,8 +5,7 @@
namespace TweetLib.Browser.Interfaces {
public interface IBrowserComponent : IScriptExecutor {
string Url { get; }
IFileDownloader FileDownloader { get; }
string CacheFolder { get; }
event EventHandler<BrowserLoadedEventArgs> BrowserLoaded;
event EventHandler<PageLoadEventArgs> PageLoadStart;
@ -14,5 +13,6 @@ public interface IBrowserComponent : IScriptExecutor {
void Setup(BrowserSetup setup);
void AttachBridgeObject(string name, object bridge);
void DownloadFile(string url, string path, Action? onSuccess, Action<Exception>? onError);
}
}

View File

@ -1,8 +0,0 @@
using System;
namespace TweetLib.Browser.Interfaces {
public interface IFileDownloader {
string CacheFolder { get; }
void DownloadFile(string url, string path, Action? onSuccess, Action<Exception>? onError);
}
}

View File

@ -11,7 +11,7 @@ internal class BaseContextMenu : IContextMenuHandler {
public BaseContextMenu(IBrowserComponent browser) {
this.browser = browser;
this.fileDownloadManager = new FileDownloadManager(browser.FileDownloader);
this.fileDownloadManager = new FileDownloadManager(browser);
}
public virtual void Show(IContextMenuBuilder menu, Context context) {

View File

@ -14,15 +14,15 @@ public sealed class FileDownloadManager {
public bool SupportsCopyingImage => App.SystemHandler.CopyImageFromFile != null;
public bool SupportsFileSaving => App.FileDialogs != null;
private readonly IFileDownloader fileDownloader;
private readonly IBrowserComponent browserComponent;
internal FileDownloadManager(IFileDownloader fileDownloader) {
this.fileDownloader = fileDownloader;
internal FileDownloadManager(IBrowserComponent browserComponent) {
this.browserComponent = browserComponent;
}
private void DownloadTempImage(string url, Action<string> process) {
string? staticFileName = TwitterUrls.GetImageFileName(url);
string file = Path.Combine(fileDownloader.CacheFolder, staticFileName ?? Path.GetRandomFileName());
string file = Path.Combine(browserComponent.CacheFolder, staticFileName ?? Path.GetRandomFileName());
if (staticFileName != null && FileUtils.FileExistsAndNotEmpty(file)) {
process(file);
@ -36,7 +36,7 @@ static void OnFailure(Exception ex) {
App.MessageDialogs.Error("Image Download", "An error occurred while downloading the image: " + ex.Message);
}
fileDownloader.DownloadFile(url, file, OnSuccess, OnFailure);
browserComponent.DownloadFile(url, file, OnSuccess, OnFailure);
}
}
@ -94,14 +94,14 @@ static void OnFailure(Exception ex) {
}
if (oneImage) {
fileDownloader.DownloadFile(firstImageLink, path, null, OnFailure);
browserComponent.DownloadFile(firstImageLink, path, null, OnFailure);
}
else {
string pathBase = Path.ChangeExtension(path, null);
string pathExt = Path.GetExtension(path);
for (int index = 0; index < urls.Length; index++) {
fileDownloader.DownloadFile(urls[index], $"{pathBase} {index + 1}{pathExt}", null, OnFailure);
browserComponent.DownloadFile(urls[index], $"{pathBase} {index + 1}{pathExt}", null, OnFailure);
}
}
});
@ -129,7 +129,7 @@ static void OnError(Exception ex) {
App.MessageDialogs.Error("Video Download", "An error occurred while downloading the video: " + ex.Message);
}
fileDownloader.DownloadFile(url, path, null, OnError);
browserComponent.DownloadFile(url, path, null, OnError);
});
}
}

View File

@ -20,7 +20,7 @@ public sealed class TweetDeckBrowser : BaseBrowser<TweetDeckBrowser> {
private const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
public TweetDeckFunctions Functions { get; }
public FileDownloadManager FileDownloadManager => new (browserComponent.FileDownloader);
public FileDownloadManager FileDownloadManager => new (browserComponent);
private readonly ISoundNotificationHandler soundNotificationHandler;
private readonly PluginManager pluginManager;