From b35e4d4d01e559553196a317fc75e11677e97397 Mon Sep 17 00:00:00 2001 From: chylex <contact@chylex.com> Date: Fri, 21 Jul 2017 12:14:15 +0200 Subject: [PATCH] Add "Save all images as..." context menu option for tweets with multiple images --- Core/Bridge/TweetDeckBridge.cs | 5 ++++- Core/Handling/ContextMenuBase.cs | 25 +++++++++++++++++------ Core/Utils/TwitterUtils.cs | 35 +++++++++++++++++++++++++------- Resources/Scripts/code.js | 14 +++++++------ 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/Core/Bridge/TweetDeckBridge.cs b/Core/Bridge/TweetDeckBridge.cs index 13cd422d..5903f0ea 100644 --- a/Core/Bridge/TweetDeckBridge.cs +++ b/Core/Bridge/TweetDeckBridge.cs @@ -10,9 +10,11 @@ sealed class TweetDeckBridge{ public static string LastRightClickedImage = string.Empty; public static string LastHighlightedTweet = string.Empty; public static string LastHighlightedQuotedTweet = string.Empty; + public static string[] LastHighlightedTweetImages = new string[0]; public static void ResetStaticProperties(){ LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty; + LastHighlightedTweetImages = new string[0]; } private readonly FormBrowser form; @@ -43,10 +45,11 @@ public void SetLastRightClickedImage(string link){ form.InvokeAsyncSafe(() => LastRightClickedImage = link); } - public void SetLastHighlightedTweet(string link, string quotedLink){ + public void SetLastHighlightedTweet(string link, string quotedLink, string imageList){ form.InvokeAsyncSafe(() => { LastHighlightedTweet = link; LastHighlightedQuotedTweet = quotedLink; + LastHighlightedTweetImages = imageList.Split(';'); }); } diff --git a/Core/Handling/ContextMenuBase.cs b/Core/Handling/ContextMenuBase.cs index aa174b36..edeb0d0e 100644 --- a/Core/Handling/ContextMenuBase.cs +++ b/Core/Handling/ContextMenuBase.cs @@ -25,12 +25,15 @@ private static string GetImage(IContextMenuParams parameters){ private const int MenuOpenLinkUrl = 26500; private const int MenuCopyLinkUrl = 26501; private const int MenuCopyUsername = 26502; - private const int MenuOpenImage = 26503; - private const int MenuSaveImage = 26504; - private const int MenuCopyImageUrl = 26505; + private const int MenuOpenImageUrl = 26503; + private const int MenuCopyImageUrl = 26504; + private const int MenuSaveImage = 26505; + private const int MenuSaveAllImages = 26506; private const int MenuOpenDevTools = 26599; private readonly Form form; + + private string[] lastHighlightedTweetImageList; protected ContextMenuBase(Form form){ this.form = form; @@ -38,6 +41,7 @@ protected ContextMenuBase(Form form){ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage); + lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages; if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage){ if (RegexTwitterAccount.Value.IsMatch(parameters.UnfilteredLinkUrl)){ @@ -54,9 +58,14 @@ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser bro } if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){ - model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser"); - model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as..."); + model.AddItem((CefMenuCommand)MenuOpenImageUrl, "Open image in browser"); model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address"); + model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as..."); + + if (lastHighlightedTweetImageList.Length > 1){ + model.AddItem((CefMenuCommand)MenuSaveAllImages, "Save all images as..."); + } + model.AddSeparator(); } } @@ -71,7 +80,7 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br SetClipboardText(GetLink(parameters)); break; - case MenuOpenImage: + case MenuOpenImageUrl: BrowserUtils.OpenExternalBrowser(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality)); break; @@ -79,6 +88,10 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br TwitterUtils.DownloadImage(GetImage(parameters), ImageQuality); break; + case MenuSaveAllImages: + TwitterUtils.DownloadImages(lastHighlightedTweetImageList, ImageQuality); + break; + case MenuCopyImageUrl: SetClipboardText(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality)); break; diff --git a/Core/Utils/TwitterUtils.cs b/Core/Utils/TwitterUtils.cs index d3391b1f..5a469ef4 100644 --- a/Core/Utils/TwitterUtils.cs +++ b/Core/Utils/TwitterUtils.cs @@ -1,4 +1,5 @@ -using CefSharp; +using System; +using CefSharp; using System.Drawing; using System.IO; using System.Windows.Forms; @@ -46,22 +47,42 @@ public static string GetImageLink(string url, ImageQuality quality){ return url; } } - + public static void DownloadImage(string url, ImageQuality quality){ - string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(url)); + DownloadImages(new string[]{ url }, quality); + } + + public static void DownloadImages(string[] urls, ImageQuality quality){ + if (urls.Length == 0){ + return; + } + + string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(urls[0])); string ext = Path.GetExtension(file); // includes dot using(SaveFileDialog dialog = new SaveFileDialog{ AutoUpgradeEnabled = true, - OverwritePrompt = true, + OverwritePrompt = urls.Length == 1, Title = "Save image", FileName = file, - Filter = string.IsNullOrEmpty(ext) ? "Image (unknown)|*.*" : $"Image (*{ext})|*{ext}" + Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") }){ if (dialog.ShowDialog() == DialogResult.OK){ - BrowserUtils.DownloadFileAsync(GetImageLink(url, quality), dialog.FileName, null, ex => { + void OnFailure(Exception ex){ FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); - }); + } + + if (urls.Length == 1){ + BrowserUtils.DownloadFileAsync(GetImageLink(urls[0], quality), dialog.FileName, null, OnFailure); + } + else{ + string pathBase = Path.ChangeExtension(dialog.FileName, null); + string pathExt = Path.GetExtension(dialog.FileName); + + for(int index = 0; index < urls.Length; index++){ + BrowserUtils.DownloadFileAsync(GetImageLink(urls[index], quality), pathBase+(index+1)+pathExt, null, OnFailure); + } + } } } } diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js index bf0e2e9d..635c162a 100644 --- a/Resources/Scripts/code.js +++ b/Resources/Scripts/code.js @@ -363,12 +363,12 @@ return !!highlightedColumnObj; }; - var updateHighlightedTweet = function(ele, obj, link, embeddedLink){ + var updateHighlightedTweet = function(ele, obj, link, embeddedLink, imageList){ highlightedTweetEle = ele; highlightedTweetObj = obj; if (lastTweet !== link){ - $TD.setLastHighlightedTweet(link, embeddedLink); + $TD.setLastHighlightedTweet(link, embeddedLink, imageList); lastTweet = link; } }; @@ -398,16 +398,18 @@ if (tweet.chirpType === TD.services.ChirpBase.TWEET){ var link = tweet.getChirpURL(); var embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : ""; - - updateHighlightedTweet(me, tweet, link || "", embedded || ""); + var images = tweet.hasImage() ? tweet.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";") : ""; + // TODO maybe handle embedded images too? + + updateHighlightedTweet(me, tweet, link || "", embedded || "", images); } else{ - updateHighlightedTweet(me, tweet, "", ""); + updateHighlightedTweet(me, tweet, "", "", ""); } } } else if (e.type === "mouseleave"){ - updateHighlightedTweet(null, null, "", ""); + updateHighlightedTweet(null, null, "", "", ""); } }); })();