JPEGify
// This silly little Fiddler extension converts all images passing through to JPEGs of a specified quality
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using Fiddler;
using System.Windows.Forms;
using System.Diagnostics;
[assembly: Fiddler.RequiredVersion("2.5.1.0")]
namespace MakeJPEG
{
public class JPEGify: IAutoTamper2
{
private bool bAutoConvert = false;
private long lngAutoJPEGQuality = 95; // 95% quality.
MenuItem oRulesMenuItem = new MenuItem("JPEGify I&mages...");
MenuItem oContextMenuItem = new MenuItem("&JPEGify...");
#region IAutoTamper Members
public void OnBeforeReturningError(Session oSession) { /* no-op */}
public void AutoTamperRequestAfter(Session oSession){/* no-op */}
public void AutoTamperRequestBefore(Session oSession){/* no-op */}
public void AutoTamperResponseAfter(Session oSession){/* no-op */}
public void OnPeekAtResponseHeaders(Session oSession)
{
if (!bAutoConvert) return;
if (!oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/")) return;
// Disable streaming because we want to JPEG stuff...
oSession.bBufferResponse = true;
}
public void AutoTamperResponseBefore(Session oSession)
{
if (!bAutoConvert) return;
ProcessSession(oSession, lngAutoJPEGQuality);
}
private void ProcessSession(Session oSession, long lngQuality)
{
if (oSession.responseCode == 200 && oSession.bHasResponse
&& (oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/gif") ||
oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/png") ||
oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/bmp") ||
oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/jp"))
)
{
try
{
int iOldSize = oSession.responseBodyBytes.Length;
MakeJPEGOfQuality(oSession, lngQuality);
oSession.oFlags.Add("Size-Before-JPEGify", iOldSize.ToString());
float flChange = 100 * (oSession.responseBodyBytes.Length / (float)iOldSize);
string sBloatInfo = String.Format("JPEGify to {0:N0} from {1:N0} bytes ({2:N1}%)", oSession.responseBodyBytes.Length, iOldSize, flChange);
FiddlerApplication.UI.SetStatusText(sBloatInfo);
}
catch (Exception eX)
{
// TODO: Log somewhere...
Trace.WriteLine(eX.ToString(), "JPEGify Failed");
}
}
}
private void MakeJPEGOfQuality(Session oSession, long lngQuality)
{
if ((lngQuality < 1) || (lngQuality > 100)) throw new ArgumentOutOfRangeException("lngQuality", lngQuality, "Quality must be between 1 and 100");
oSession.utilDecodeResponse();
using (MemoryStream oStream = new MemoryStream(oSession.responseBodyBytes))
{
Bitmap oBMP = new Bitmap(oStream);
using (MemoryStream oNewStream = new MemoryStream())
{
// TODO: Can we cache this?
ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);
// Create an Encoder object based on the GUID for the Quality parameter category.
Encoder myEncoder = Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, lngQuality); // value 0-100
myEncoderParameters.Param[0] = myEncoderParameter;
oBMP.Save(oNewStream, jgpEncoder, myEncoderParameters);
oSession.ResponseBody = oNewStream.ToArray();
oSession.oResponse.headers["Content-Type"] = "image/jpeg";
oSession.oResponse.headers["Cache-Control"] = "no-cache";
}
}
}
#endregion
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
#region IFiddlerExtension Members
public void OnBeforeUnload()
{
bAutoConvert = false;
oRulesMenuItem.Dispose();
oContextMenuItem.Dispose();
}
public void OnLoad()
{
oRulesMenuItem.Click += delegate(object s, EventArgs e)
{
bool bNewValue = !oRulesMenuItem.Checked;
if (bNewValue)
{
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers);
if (String.IsNullOrEmpty(sQuality))
{
bNewValue = false;
}
if (bNewValue)
{
long lng = 0;
if (long.TryParse(sQuality, out lng))
{
lngAutoJPEGQuality = lng;
}
else
{
bNewValue = false;
}
}
}
oRulesMenuItem.Checked = bAutoConvert = bNewValue;
};
oContextMenuItem.Click += delegate(object s, EventArgs e)
{
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers);
if (String.IsNullOrEmpty(sQuality)) return;
long lng = 0;
if (!long.TryParse(sQuality, out lng)) return;
foreach (Session oS in FiddlerApplication.UI.GetSelectedSessions())
{
ProcessSession(oS, lng);
oS.RefreshUI();
}
FiddlerApplication.UI.SetStatusText("JPEGify Complete");
};
FiddlerApplication.UI.mnuRules.MenuItems.Add(oRulesMenuItem);
FiddlerApplication.UI.lvSessions.ContextMenu.MenuItems.Add(0, oContextMenuItem );
}
#endregion
}
}