using RSecurityBackend.Models.Generic; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace RMuseum.Services.Implementation { /// /// British Library Tile Downloader And Mixer /// public class BLTileMixer { /// /// Constructor /// public BLTileMixer() { } private Dictionary<(int x, int y), Image> _tiles = null; public async Task> DownloadMix(string pageUrl, int order) { using (var client = new HttpClient()) { client.Timeout = TimeSpan.FromMinutes(5); _tiles = new Dictionary<(int x, int y), Image>(); int max_x = -1; for (int x = 0; ; x++) { string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/{x}_0.jpg"; var imageResult = await client.GetAsync(imageUrl); int _ImportRetryCount = 5; int _ImportRetryInitialSleep = 500; int retryCount = 0; while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable) { imageResult.Dispose(); Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1)); imageResult = await client.GetAsync(imageUrl); retryCount++; } if (imageResult.IsSuccessStatusCode) { using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync()) { imageStream.Position = 0; try { Image tile = Image.FromStream(imageStream); _tiles.Add((x, 0), tile); max_x = x; } catch (ArgumentException) { break; } } } else { imageResult.Dispose(); return new RServiceResult(null, $"Http result is not ok ({imageResult.StatusCode}) for page {order}, url {imageUrl}"); } } int max_y = -1; for (int y = 1; ; y++) { string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/0_{y}.jpg"; var imageResult = await client.GetAsync(imageUrl); int _ImportRetryCount = 5; int _ImportRetryInitialSleep = 500; int retryCount = 0; while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable) { imageResult.Dispose(); Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1)); imageResult = await client.GetAsync(imageUrl); retryCount++; } if (imageResult.IsSuccessStatusCode) { using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync()) { imageStream.Position = 0; try { Image tile = Image.FromStream(imageStream); _tiles.Add((0, y), tile); max_y = y; } catch (ArgumentException) { break; } } } else { imageResult.Dispose(); return new RServiceResult(null, $"Http result is not ok ({imageResult.StatusCode}) for page {order}, url {imageUrl}"); } } List threadsList = new List(); List<(int x, int y)> unprocessedtiles = new List<(int x, int y)>(); for (int x = 0; x <= max_x; x++) for (int y = 0; y <= max_y; y++) if (_tiles.TryGetValue((x, y), out Image tmp) == false) { unprocessedtiles.Add((x, y)); } foreach ((int x, int y) unprocessedtile in unprocessedtiles) { Thread th = new Thread ( new ThreadStart ( async () => { await _ProcessTile(pageUrl, unprocessedtile.x, unprocessedtile.y); } ) ); th.Start(); Thread.Sleep(100); threadsList.Add(th); } foreach (Thread th in threadsList) th.Join(); int tileWidth = _tiles[(0, 0)].Width; int tileHeight = _tiles[(0, 0)].Height; int imageWidth = tileWidth * (max_x + 1); int imageHeight = tileHeight * (max_y + 1); using (Image image = new Bitmap(imageWidth, imageHeight)) { using (Graphics g = Graphics.FromImage(image)) { for (int x = 0; x <= max_x; x++) for (int y = 0; y < max_y; y++) { g.DrawImage(_tiles[(x, y)], new Point(x * tileWidth, y * tileHeight)); _tiles[(x, y)].Dispose(); } Stream imageStream = new MemoryStream(); ImageCodecInfo jpgEncoder = _GetEncoder(ImageFormat.Jpeg); Encoder myEncoder = Encoder.Quality; EncoderParameters jpegParameters = new EncoderParameters(1); EncoderParameter qualityParameter = new EncoderParameter(myEncoder, 92L); jpegParameters.Param[0] = qualityParameter; image.Save(imageStream, jpgEncoder, jpegParameters); imageStream.Position = 0; return new RServiceResult(imageStream); } } } } private async Task _ProcessTile(string pageUrl, int x, int y) { using (var client = new HttpClient()) { client.Timeout = TimeSpan.FromMinutes(5); string imageUrl = $"http://www.bl.uk/manuscripts/Proxy.ashx?view={pageUrl}_files/13/{x}_{y}.jpg"; var imageResult = await client.GetAsync(imageUrl); int _ImportRetryCount = 5; int _ImportRetryInitialSleep = 500; int retryCount = 0; while (retryCount < _ImportRetryCount && !imageResult.IsSuccessStatusCode && imageResult.StatusCode == HttpStatusCode.ServiceUnavailable) { imageResult.Dispose(); Thread.Sleep(_ImportRetryInitialSleep * (retryCount + 1)); imageResult = await client.GetAsync(imageUrl); retryCount++; } if (imageResult.IsSuccessStatusCode) { using (Stream imageStream = await imageResult.Content.ReadAsStreamAsync()) { if (imageStream.Length == 0) return; imageStream.Position = 0; _tiles.Add((x, y), Image.FromStream(imageStream)); } } else { imageResult.Dispose(); } } } /// /// find image encodder /// /// /// private ImageCodecInfo _GetEncoder(ImageFormat format) { ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); foreach (ImageCodecInfo codec in codecs) { if (codec.FormatID == format.Guid) { return codec; } } return null; } } }