Википедия:Песочница

Материал из Википедии — свободной энциклопедии
Это старая версия этой страницы, сохранённая Vort (обсуждение | вклад) в 08:22, 4 ноября 2021. Она может серьёзно отличаться от текущей версии.
Перейти к навигации Перейти к поиску
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using System.Security.Authentication;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using System.Threading;

namespace GettyImagesDownloader
{
    class Downloader
    {
        WebClient wc;

        Action<string> updateStatus;

        string imageId;

        string downloadType;

        string compUrl;
        int compSize;

        string customerId;
        string sharedKey;
        string authHeader;

        byte[] sfi;

        int width;
        int height;

        int tileSize;

        string key;

        Image imageH;
        Image imageV;
        Image imageT;


        Bitmap bitmap;
        Graphics graphics;

        string ReadString(BinaryReader br)
        {
            var sb = new StringBuilder();
            for (;;)
            {
                var b = br.ReadByte();
                if (b == 0x00)
                    return sb.ToString();
                sb.Append(Encoding.ASCII.GetString(new[] { b }));
            }
        }

        void DownloadSfi()
        {
            updateStatus("Downloading image data... ");
            wc.Headers.Add("Accept", authHeader);
            sfi = wc.DownloadData(
                $"https://images.smartframe.net/sfis/{customerId}/{imageId}/2560w.sfi");
        }

        void ParseSfi()
        {
            updateStatus("Processing image data... ");
            var br = new BinaryReader(new MemoryStream(sfi));

            var s1 = ReadString(br);
            if (!s1.StartsWith("PBS2"))
                throw new Exception();
            var s1s = s1.Substring(4).Split(';');

            width = int.Parse(s1s[0]);
            height = int.Parse(s1s[1]);
            int jpegHsize = int.Parse(s1s[2]);
            if (jpegHsize != 0)
                imageH = Image.FromStream(new MemoryStream(br.ReadBytes(jpegHsize)), true);

            int jpegVsize = int.Parse(ReadString(br));
            if (jpegVsize != 0)
                imageV = Image.FromStream(new MemoryStream(br.ReadBytes(jpegVsize)), true);

            ReadString(br);
            tileSize = int.Parse(ReadString(br));
            ReadString(br);
            var key1 = ReadString(br);
            int jpegTsize = int.Parse(ReadString(br));

            imageT = Image.FromStream(new MemoryStream(br.ReadBytes(jpegTsize)), true);

            ReadString(br);
            if (br.BaseStream.Position != br.BaseStream.Length)
                throw new Exception();

            key = string.Concat(sharedKey.ToCharArray().Zip(
                key1.ToCharArray(), (f, s) => new string(new char[] { f, s })));
        }

        void RenderSfi()
        {
            bitmap = new Bitmap(width, height,
                PixelFormat.Format24bppRgb);
            graphics = Graphics.FromImage(bitmap);

            if (imageH != null)
                graphics.DrawImage(imageH, 0, height - imageH.Height);
            if (imageV != null)
                graphics.DrawImage(imageV, width - imageV.Width, 0);

            int tileCountX = width / tileSize;
            int tileCountY = height / tileSize;

            var Y = new int[tileCountY];
            var T = new int[tileCountY, tileCountX];
            for (var y = 0; y < tileCountY; y++)
            {
                Y[y] = tileCountX;
                for (var x = 0; x < tileCountX; x++)
                    T[y, x] = -1;
            }

            var y3 = tileCountY;
            for (var y1 = 0; y1 < tileCountY; y1++)
            {
                for (var x1 = 0; x1 < tileCountX; x1++)
                {
                    var i = y1 * tileCountX + x1;
                    var s1 = key[i % key.Length] % y3;
                    var s1c = 0;
                    for (var y2 = 0; y2 < tileCountY; y2++)
                    {
                        if (Y[y2] != 0 && s1 == s1c++)
                        {
                            var s2c = 0;
                            var s2 = key[x1 % key.Length] % Y[y2];
                            for (var x2 = 0; x2 < tileCountX; x2++)
                            {
                                if (T[y2, x2] == -1 && s2 == s2c++)
                                {
                                    T[y2, x2] = i;
                                    Y[y2]--;
                                    if (Y[y2] == 0)
                                        y3--;
                                    graphics.DrawImage(
                                        imageT,
                                        x1 * tileSize,
                                        y1 * tileSize,
                                        new Rectangle(
                                            x2 * (tileSize + 8),
                                            y2 * (tileSize + 8),
                                            tileSize,
                                            tileSize),
                                        GraphicsUnit.Pixel);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            updateStatus("Saving image data... ");
            string filename = $"{imageId}_sfi.png";
            bitmap.Save(filename);
            updateStatus($"File is saved: {filename}");
        }

        bool ReadId(string input)
        {
            var m = Regex.Match(input, "(/|^)([0-9]+)(\\?|$)");
            if (!m.Success)
            {
                updateStatus("Wrong input format");
                return false;
            }
            imageId = m.Groups[2].Value;
            return true;
        }

        void SelectDownloadType()
        {
            updateStatus("Selecting download type... ");
            wc = new WebClient();

            try
            {
                wc.DownloadString(
                    $"https://embed.gettyimages.com/oembed?url=http://gty.im/{imageId}");
                downloadType = "sfi";
            }
            catch (WebException e)
            {
                HttpWebResponse r = e.Response as HttpWebResponse;
                if (r != null)
                    if (r.StatusCode == HttpStatusCode.NotFound)
                        downloadType = "comp";
            }
            if (downloadType == null)
                throw new Exception();
        }

        void GetSfiProperties()
        {
            updateStatus("Obtaining sfi properties... ");

            var wb = wc.DownloadString(
                "https://static.smartframe.net/getty-bridge/widgets-bridge.js");
            var m = Regex.Match(wb,
                "https://embed\\.smartframe\\.net/([0-9a-f]+)\\.js");
            customerId = m.Groups[1].Value;

            var sf = wc.DownloadString(
                $"https://static.smartframe.net/sf/{customerId}/smartframe.js");
            m = Regex.Match(sf, "sharedKey:'([0-9a-f]+)'");
            sharedKey = m.Groups[1].Value;

            m = Regex.Match(sf, "downloadImageAcceptHeader:'([^']+)'");
            authHeader = m.Groups[1].Value;

            wc.Headers.Add("Accept", authHeader);

            // Trigger image generation
            wc.DownloadData($"https://sfm.smartframe.net/sfm/{customerId}/{imageId}.sfm");
        }

        void GetCompUrl()
        {
            updateStatus("Obtaining comp URL... ");

            var gid = wc.DownloadString($"https://www.gettyimages.com/detail/{imageId}");
            var m = Regex.Match(gid,
                "<script>window\\.currentAsset *= *(\\{[^<]+)window\\.currentPreferences *= *\\{\\}");
            if (!m.Success)
                throw new Exception();

            var jsonString = m.Groups[1].Value;
            var jss = new JavaScriptSerializer();
            var json = jss.DeserializeObject(jsonString) as Dictionary<string, object>;

            if (json.ContainsKey("compUrl"))
            {
                compUrl = json["compUrl"] as string;
                compSize = 594;
            }
            if (json.ContainsKey("galleryComp1024Url"))
            {
                compUrl = json["galleryComp1024Url"] as string;
                compSize = 1024;
            }
            if (json.ContainsKey("galleryHighResCompUrl"))
            {
                compUrl = json["galleryHighResCompUrl"] as string;
                compSize = 2048;
            }
            if (compUrl == null)
                throw new Exception();
        }

        void DownloadAndSaveComp()
        {
            updateStatus("Downloading image data... ");
            var jpgData = wc.DownloadData(compUrl);

            updateStatus("Saving image data... ");
            string filename = $"{imageId}_comp_{compSize}.jpg";
            File.WriteAllBytes(filename, jpgData);
            updateStatus($"File is saved: {filename}");
        }

        public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
        public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;

        public void Download(string input, Action<string> updateStatus)
        {
            // www.gettyimages.com refuses to work with TLS 1.1
            ServicePointManager.SecurityProtocol = Tls12;

            this.updateStatus = updateStatus;

            if (!ReadId(input))
                return;
            SelectDownloadType();
            if (downloadType == "sfi")
            {
                GetSfiProperties();
                DownloadSfi();
                ParseSfi();
                RenderSfi();
            }
            else if (downloadType == "comp")
            {
                GetCompUrl();
                DownloadAndSaveComp();
            }
        }
    }

    public class MainForm : Form
    {
        TableLayoutPanel tableLayoutPanel;
        Label inputLabel;
        TextBox inputTextBox;
        Button downloadButton;
        StatusStrip statusStrip;
        ToolStripStatusLabel statusLabel;

        void UpdateStatus(string status)
        {
            Invoke(new Action(() => statusLabel.Text = status));
        }

        private void InputTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.A)
            {
                inputTextBox.SelectAll();
                e.SuppressKeyPress = true;
            }
        }

        private void DownloadButton_Click(object sender, EventArgs e)
        {
            inputTextBox.Enabled = false;
            downloadButton.Enabled = false;

            new Thread(() =>
            {
                try
                {
                    new Downloader().Download(
                        inputTextBox.Text, UpdateStatus);
                }
                catch (Exception ex)
                {
                    Invoke(new Action(() =>
                    {
                        statusLabel.Text = "Failed";
                        MessageBox.Show(
                            ex.ToString(),
                            "Download failed",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Exclamation);
                    }));
                }
                Invoke(new Action(() =>
                {
                    inputTextBox.Enabled = true;
                    downloadButton.Enabled = true;
                    inputTextBox.Focus();
                }));
            })
            {
                IsBackground = true
            }.Start();
        }

        public MainForm()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            tableLayoutPanel = new TableLayoutPanel();
            inputLabel = new Label();
            inputTextBox = new TextBox();
            downloadButton = new Button();
            statusStrip = new StatusStrip();
            statusLabel = new ToolStripStatusLabel();
            tableLayoutPanel.SuspendLayout();
            statusStrip.SuspendLayout();
            SuspendLayout();

            tableLayoutPanel.ColumnCount = 1;
            tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
            tableLayoutPanel.Controls.Add(inputLabel, 0, 0);
            tableLayoutPanel.Controls.Add(inputTextBox, 0, 1);
            tableLayoutPanel.Controls.Add(downloadButton, 0, 2);
            tableLayoutPanel.Dock = DockStyle.Fill;
            tableLayoutPanel.Padding = new Padding(4);
            tableLayoutPanel.RowCount = 3;
            tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 16f));
            tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
            tableLayoutPanel.RowStyles.Add(new RowStyle());
            tableLayoutPanel.TabIndex = 0;

            inputLabel.Dock = DockStyle.Fill;
            inputLabel.Text = "Enter image identifier or URL:";

            inputTextBox.Dock = DockStyle.Fill;
            inputTextBox.Multiline = true;
            inputTextBox.TabIndex = 1;
            inputTextBox.KeyDown += InputTextBox_KeyDown;

            downloadButton.Anchor = AnchorStyles.None;
            downloadButton.Size = new Size(100, 26);
            downloadButton.TabIndex = 2;
            downloadButton.Text = "Download";
            downloadButton.Click += DownloadButton_Click;

            statusStrip.Items.AddRange(new ToolStripItem[] { statusLabel});
            statusStrip.RenderMode = ToolStripRenderMode.Professional;
            statusStrip.SizingGrip = false;

            statusLabel.ForeColor = SystemColors.GrayText;
            statusLabel.Text = "Idle";

            AcceptButton = downloadButton;
            AutoScaleDimensions = new SizeF(6f, 13f);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(304, 142);
            Controls.Add(tableLayoutPanel);
            Controls.Add(statusStrip);
            MinimumSize = new Size(190, 140);
            ShowIcon = false;
            StartPosition = FormStartPosition.CenterScreen;
            Text = "GettyImagesDownloader";

            tableLayoutPanel.ResumeLayout(false);
            tableLayoutPanel.PerformLayout();
            statusStrip.ResumeLayout(false);
            statusStrip.PerformLayout();
            ResumeLayout(false);
            PerformLayout();
        }

        [STAThread]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MainForm());
        }
    }
}