private async Task ThumbnailAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); if (args.Length > 3) { var rect = JsonConvert.DeserializeObject <Rect>(args[2]); await ImageHelper.CropAsync(file, temp, rect, 90); } else { await ImageHelper.ScaleJpegAsync(file, temp, 90, 0.77); } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task CompressAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); if (args.Length > 3) { var rect = JsonConvert.DeserializeObject <Rect>(args[2]); await ImageHelper.CropAsync(file, temp, rect); } else { await ImageHelper.ScaleJpegAsync(file, temp, 1280, 0.77); } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); if (IsTemporary(file)) { await file.DeleteAsync(); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, ex.ToString()))); } }
private async Task ThumbnailTranscodeAsync(UpdateFileGenerationStart update, string[] args) { try { var conversion = JsonConvert.DeserializeObject<VideoConversion>(args[2]); //if (conversion.Transcode) { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); var props = await file.Properties.GetVideoPropertiesAsync(); double originalWidth = props.GetWidth(); double originalHeight = props.GetHeight(); //if (!conversion.CropRectangle.IsEmpty()) //{ // file = await ImageHelper.CropAsync(file, temp, conversion.CropRectangle); // originalWidth = conversion.CropRectangle.Width; // originalHeight = conversion.CropRectangle.Height; //} using (var fileStream = await ImageHelper.OpenReadAsync(file)) using (var outputStream = await temp.OpenAsync(FileAccessMode.ReadWrite)) { var decoder = await BitmapDecoder.CreateAsync(fileStream); double ratioX = (double)90 / originalWidth; double ratioY = (double)90 / originalHeight; double ratio = Math.Min(ratioX, ratioY); uint width = (uint)(originalWidth * ratio); uint height = (uint)(originalHeight * ratio); var transform = new BitmapTransform(); transform.ScaledWidth = width; transform.ScaledHeight = height; transform.InterpolationMode = BitmapInterpolationMode.Linear; transform.Flip = conversion.Mirror == MediaMirroringOptions.Horizontal ? BitmapFlip.Horizontal : BitmapFlip.None; var pixelData = await decoder.GetSoftwareBitmapAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.DoNotColorManage); var propertySet = new BitmapPropertySet(); var qualityValue = new BitmapTypedValue(0.77, PropertyType.Single); propertySet.Add("ImageQuality", qualityValue); var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, outputStream); encoder.SetSoftwareBitmap(pixelData); await encoder.FlushAsync(); _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task DownloadAsync(UpdateFileGenerationStart update) { try { if (!Uri.TryCreate(update.OriginalPath, UriKind.Absolute, out Uri result)) { return; } var client = new HttpClient(); client.DefaultRequestHeaders.ExpectContinue = false; var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); var request = new HttpRequestMessage(HttpMethod.Get, result); var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); var length = int.Parse(response.Content.Headers.GetValues("Content-Length").FirstOrDefault()); _protoService.Send(new SetFileGenerationProgress(update.GenerationId, length, 0)); using (var fs = await temp.OpenAsync(FileAccessMode.ReadWrite)) { using (var stream = await response.Content.ReadAsStreamAsync()) { var inputStream = stream.AsInputStream(); int totalBytesRead = 0; while (true) { // Read from the web. IBuffer buffer = new Windows.Storage.Streams.Buffer(1024); buffer = await inputStream.ReadAsync( buffer, buffer.Capacity, InputStreamOptions.None); if (buffer.Length == 0) { // There is nothing else to read. break; } // Report progress. totalBytesRead += (int)buffer.Length; _protoService.Send(new SetFileGenerationProgress(update.GenerationId, length, totalBytesRead)); // Write to file. await fs.WriteAsync(buffer); } inputStream.Dispose(); fs.Dispose(); } } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
public async void Handle(UpdateFileGenerationStart update) { var args = update.Conversion.Split('#'); if (args.Length < 2) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "Invalid generation arguments"))); return; } if (Enum.TryParse(args[1], true, out ConversionType conversion)) { if (conversion == ConversionType.Copy) { await CopyAsync(update, args); } else if (conversion == ConversionType.Compress) { await CompressAsync(update, args); } else if (conversion == ConversionType.Transcode) { await TranscodeAsync(update, args); } else if (conversion == ConversionType.TranscodeThumbnail) { await ThumbnailTranscodeAsync(update, args); } else if (conversion == ConversionType.DocumentThumbnail) { await ThumbnailDocumentAsync(update, args); } else if (conversion == ConversionType.ChatPhoto) { await ChatPhotoAsync(update, args); } // TDLib else if (conversion == ConversionType.Url) { await DownloadAsync(update); } } else { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID Unknown conversion type"))); } }
private async Task ThumbnailDocumentAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); var mode = ThumbnailMode.DocumentsView; if (file.ContentType.StartsWith("audio/")) { mode = ThumbnailMode.MusicView; } using (var thumbnail = await file.GetThumbnailAsync(mode, 90)) { if (thumbnail != null && thumbnail.Type == ThumbnailType.Image) { using (var reader = new DataReader(thumbnail)) { await reader.LoadAsync((uint)thumbnail.Size); var buffer = new byte[(int)thumbnail.Size]; reader.ReadBytes(buffer); await FileIO.WriteBytesAsync(temp, buffer); } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } else { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID No thumbnail found"))); } } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task CopyAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); await file.CopyAndReplaceAsync(temp); _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); if (IsTemporary(file)) { await file.DeleteAsync(); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, ex.ToString()))); } }
private async Task CompressAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); if (args.Length > 3) { var editState = JsonConvert.DeserializeObject <BitmapEditState>(args[2]); var rectangle = editState.Rectangle; await ImageHelper.CropAsync(file, temp, rectangle, rotation : editState.Rotation, flip : editState.Flip); var drawing = editState.Strokes; if (drawing != null && drawing.Count > 0) { await ImageHelper.DrawStrokesAsync(temp, drawing, rectangle, editState.Rotation, editState.Flip); } } else { await ImageHelper.ScaleJpegAsync(file, temp, 1280, 0.77); } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); if (IsTemporary(file)) { await file.DeleteAsync(); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task CopyAsync(UpdateFileGenerationStart update, string[] args) { try { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await _protoService.GetFileAsync(update.DestinationPath); if (IsTemporary(file)) { await file.MoveAndReplaceAsync(temp); } else { await file.CopyAndReplaceAsync(temp); } _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task TranscodeAsync(UpdateFileGenerationStart update, string[] args) { try { var conversion = JsonConvert.DeserializeObject <VideoConversion>(args[2]); if (conversion.Mute) { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); var profile = await MediaEncodingProfile.CreateFromFileAsync(file); if (profile.Audio == null && conversion.Mute) { await CopyAsync(update, args); return; } //profile.Video.Width = conversion.Width; //profile.Video.Height = conversion.Height; //profile.Video.Bitrate = conversion.Bitrate; if (conversion.Mute) { profile.Audio = null; } var transcoder = new MediaTranscoder(); if (conversion.Transform) { var transform = new VideoTransformEffectDefinition(); transform.Rotation = conversion.Rotation; transform.OutputSize = conversion.OutputSize; transform.Mirror = conversion.Mirror; transform.CropRectangle = conversion.CropRectangle.IsEmpty() ? Rect.Empty : conversion.CropRectangle; transcoder.AddVideoEffect(transform.ActivatableClassId, true, transform.Properties); } var prepare = await transcoder.PrepareFileTranscodeAsync(file, temp, profile); if (prepare.CanTranscode) { var progress = prepare.TranscodeAsync(); progress.Progress = (result, delta) => { _protoService.Send(new SetFileGenerationProgress(update.GenerationId, 100, (int)delta)); }; progress.Completed = (result, delta) => { _protoService.Send(new FinishFileGeneration(update.GenerationId, prepare.FailureReason == TranscodeFailureReason.None ? null : new Error(500, prepare.FailureReason.ToString()))); }; } else { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, prepare.FailureReason.ToString()))); } } else { await CopyAsync(update, args); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task ChatPhotoAsync(UpdateFileGenerationStart update, string[] args) { try { var conversion = JsonConvert.DeserializeObject <ChatPhotoConversion>(args[2]); var sticker = await _protoService.SendAsync(new GetFile(conversion.StickerFileId)) as Telegram.Td.Api.File; if (sticker == null || !sticker.Local.IsFileExisting()) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID No sticker found"))); return; } Background background = null; var backgroundLink = await _protoService.SendAsync(new GetInternalLinkType(conversion.BackgroundUrl ?? string.Empty)) as InternalLinkTypeBackground; if (backgroundLink != null) { background = await _protoService.SendAsync(new SearchBackground(backgroundLink.BackgroundName)) as Background; } else { var freeform = new[] { 0xDBDDBB, 0x6BA587, 0xD5D88D, 0x88B884 }; background = new Background(0, true, false, string.Empty, new Document(string.Empty, "application/x-tgwallpattern", null, null, TdExtensions.GetLocalFile("Assets\\Background.tgv", "Background")), new BackgroundTypePattern(new BackgroundFillFreeformGradient(freeform), 50, false, false)); } if (background == null || (background.Document != null && !background.Document.DocumentValue.Local.IsFileExisting())) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID No background found"))); return; } var device = CanvasDevice.GetSharedDevice(); var bitmaps = new List <CanvasBitmap>(); var sfondo = new CanvasRenderTarget(device, 640, 640, 96, DirectXPixelFormat.B8G8R8A8UIntNormalized, CanvasAlphaMode.Premultiplied); using (var session = sfondo.CreateDrawingSession()) { if (background.Type is BackgroundTypePattern pattern) { if (pattern.Fill is BackgroundFillFreeformGradient freeform) { var colors = freeform.GetColors(); var positions = new Vector2[] { new Vector2(0.80f, 0.10f), new Vector2(0.35f, 0.25f), new Vector2(0.20f, 0.90f), new Vector2(0.65f, 0.75f), }; using (var gradient = CanvasBitmap.CreateFromBytes(device, ChatBackgroundFreeform.GenerateGradientData(50, 50, colors, positions), 50, 50, DirectXPixelFormat.B8G8R8A8UIntNormalized)) using (var cache = await PlaceholderHelper.GetPatternBitmapAsync(device, null, background.Document.DocumentValue)) { using (var scale = new ScaleEffect { Source = gradient, BorderMode = EffectBorderMode.Hard, Scale = new Vector2(640f / 50f, 640f / 50f) }) using (var colorize = new TintEffect { Source = cache, Color = Color.FromArgb(0x76, 00, 00, 00) }) using (var tile = new BorderEffect { Source = colorize, ExtendX = CanvasEdgeBehavior.Wrap, ExtendY = CanvasEdgeBehavior.Wrap }) using (var effect = new BlendEffect { Foreground = tile, Background = scale, Mode = BlendEffectMode.Overlay }) { session.DrawImage(effect, new Rect(0, 0, 640, 640), new Rect(0, 0, 640, 640)); } } } } } bitmaps.Add(sfondo); var width = (int)(512d * conversion.Scale); var height = (int)(512d * conversion.Scale); var animation = await Task.Run(() => LottieAnimation.LoadFromFile(sticker.Local.Path, new Windows.Graphics.SizeInt32 { Width = width, Height = height }, false, null)); if (animation == null) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID Can't load Lottie animation"))); return; } var composition = new MediaComposition(); var layer = new MediaOverlayLayer(); var buffer = ArrayPool <byte> .Shared.Rent(width *height * 4); var framesPerUpdate = animation.FrameRate < 60 ? 1 : 2; var duration = TimeSpan.Zero; for (int i = 0; i < animation.TotalFrame; i += framesPerUpdate) { var bitmap = CanvasBitmap.CreateFromBytes(device, buffer, width, height, DirectXPixelFormat.B8G8R8A8UIntNormalized); animation.RenderSync(bitmap, i); var clip = MediaClip.CreateFromSurface(bitmap, TimeSpan.FromMilliseconds(1000d / 30d)); var overlay = new MediaOverlay(clip, new Rect(320 - (width / 2d), 320 - (height / 2d), width, height), 1); overlay.Delay = duration; layer.Overlays.Add(overlay); duration += clip.OriginalDuration; bitmaps.Add(bitmap); } composition.OverlayLayers.Add(layer); composition.Clips.Add(MediaClip.CreateFromSurface(sfondo, duration)); var temp = await _protoService.GetFileAsync(update.DestinationPath); var profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto); profile.Audio = null; profile.Video.Bitrate = 1800000; profile.Video.Width = 640; profile.Video.Height = 640; profile.Video.FrameRate.Numerator = 30; profile.Video.FrameRate.Denominator = 1; var progress = composition.RenderToFileAsync(temp, MediaTrimmingPreference.Precise, profile); progress.Progress = (result, delta) => { _protoService.Send(new SetFileGenerationProgress(update.GenerationId, 100, (int)delta)); }; var result = await progress; if (result == TranscodeFailureReason.None) { _protoService.Send(new FinishFileGeneration(update.GenerationId, null)); } else { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, result.ToString()))); } ArrayPool <byte> .Shared.Return(buffer); foreach (var bitmap in bitmaps) { bitmap.Dispose(); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }
private async Task TranscodeAsync(UpdateFileGenerationStart update, string[] args) { try { var conversion = JsonConvert.DeserializeObject <VideoConversion>(args[2]); if (conversion.Transcode) // <==> conversion.Mute (currently, see: MessageFactory) { var file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(args[0]); var temp = await StorageFile.GetFileFromPathAsync(update.DestinationPath); var profile = await MediaEncodingProfile.CreateFromFileAsync(file); if ((profile.Video.Width != conversion.Width || profile.Video.Height != conversion.Height) && conversion.Width > 0 && conversion.Height > 0 && conversion.Bitrate > 0) // All zero for video profile { profile.Video.Width = conversion.Width; //Note: OutputSize tells the video effect how to crop the video, and encoding profile tells the encoder how to encode the video profile.Video.Height = conversion.Height; profile.Video.Bitrate = conversion.Bitrate; } else if (profile.Video.Width == conversion.Width && profile.Video.Height == conversion.Height && Math.Abs(profile.Video.Bitrate - conversion.Bitrate) < 10000) { // Do not transcode if bitrate is very similar await CopyAsync(update, args); return; } else if (profile.Audio == null && conversion.Mute && conversion.TrimStartTime == null && conversion.TrimStopTime == null) { await CopyAsync(update, args); return; } //profile.Video.Width = conversion.Width; //profile.Video.Height = conversion.Height; //profile.Video.Bitrate = conversion.Bitrate; if (conversion.Mute) { profile.Audio = null; } var transcoder = new MediaTranscoder(); if (conversion.TrimStartTime is TimeSpan trimStart) { transcoder.TrimStartTime = trimStart; } if (conversion.TrimStopTime is TimeSpan trimStop) { transcoder.TrimStopTime = trimStop; } if (conversion.Transform) { var transform = new VideoTransformEffectDefinition(); transform.Rotation = conversion.Rotation; transform.OutputSize = conversion.OutputSize; transform.Mirror = conversion.Mirror; transform.CropRectangle = conversion.CropRectangle.IsEmpty() ? Rect.Empty : conversion.CropRectangle; profile.Video.Width = (uint)conversion.OutputSize.Width; profile.Video.Height = (uint)conversion.OutputSize.Height; transcoder.AddVideoEffect(transform.ActivatableClassId, true, transform.Properties); } var prepare = await transcoder.PrepareFileTranscodeAsync(file, temp, profile); if (prepare.CanTranscode) { var progress = prepare.TranscodeAsync(); progress.Progress = (result, delta) => { _protoService.Send(new SetFileGenerationProgress(update.GenerationId, 100, (int)delta)); }; progress.Completed = (result, delta) => { _protoService.Send(new FinishFileGeneration(update.GenerationId, prepare.FailureReason == TranscodeFailureReason.None ? null : new Error(500, prepare.FailureReason.ToString()))); }; } else { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, prepare.FailureReason.ToString()))); } } else { await CopyAsync(update, args); } } catch (Exception ex) { _protoService.Send(new FinishFileGeneration(update.GenerationId, new Error(500, "FILE_GENERATE_LOCATION_INVALID " + ex.ToString()))); } }