public void CreateAlbum() { Thread t = new Thread(() => { using (WebClient client = new WebClient()) { client.Headers.Add("Authorization", "Client-ID " + _clientId); NameValueCollection parameters = new NameValueCollection() { { "privacy", "public" } }; byte[] response = client.UploadValues(_baseAlbumCreationUrl, parameters); string json = Encoding.UTF8.GetString(response); ImgurUploadResponse serializedResponse = JsonUtility.FromJson <ImgurUploadResponse>(json); StreamWriter writer = File.CreateText(_albumDelethashFileLocation); writer.Write(serializedResponse.data.deletehash); writer.Close(); writer = File.CreateText(_albumIdFileLocation); writer.Write(serializedResponse.data.id); writer.Close(); } }) { IsBackground = true }; t.Start(); }
public OnImageUploadedEventArgs(ImgurUploadResponse response) { this.response = response; }
private async Task ProcessRequests(List <RedditThing> requests) { // Get parent of each request. Dictionary <RedditThing, RedditThing> requestsWithParents = new Dictionary <RedditThing, RedditThing>(); foreach (RedditThing request in requests) { RedditThing immediateParent = await redditClient.GetInfoOfCommentOrLink(request.Subreddit, request.ParentId); // Determine whether to use the root link or the request's immediate parent. RedditThing parentPost; List <string> splitRequest = request.GetCommandTextFromMention(redditClient.Username).Split().ToList(); if (immediateParent.Kind == "t3" || (splitRequest.Count > 1 && splitRequest[1].ToLower() == "!immediate")) { parentPost = immediateParent; } else { parentPost = await redditClient.GetInfoOfCommentOrLink(request.Subreddit, immediateParent.LinkId); } requestsWithParents.Add(request, parentPost); } // Process each request. Dictionary <string, List <RedditThing> > processedRequestsByUser = new Dictionary <string, List <RedditThing> >(); foreach (var kvp in requestsWithParents) { try { RedditThing mentionComment = kvp.Key; RedditThing parentPost = kvp.Value; // If we have already processed a request from this user in this batch of requests:... if (processedRequestsByUser.ContainsKey(mentionComment.Author)) { // If this is a duplicate request: discard it. if (processedRequestsByUser[mentionComment.Author].Any(rt => rt.ParentId == mentionComment.ParentId && rt.Body == mentionComment.Body)) { Log.Information($"Skipping {mentionComment.Name}: Post is a duplicate. ({mentionComment.Author}: '{mentionComment.Body}')"); await redditClient.MarkMessagesAsRead(new List <string> { mentionComment.Name }); continue; } // If we have already processed enough requests from this user in this batch: temporarily skip it. else if (processedRequestsByUser[mentionComment.Author].Count >= 2) { Log.Information($"Temporarily skipping {mentionComment.Name}: Too many recent requests. ({mentionComment.Author}: '{mentionComment.Body}')"); continue; } processedRequestsByUser[mentionComment.Author].Add(mentionComment); } else { processedRequestsByUser.Add(mentionComment.Author, new List <RedditThing> { mentionComment }); } bool requestorIsAdmin = settings.Administrators.Contains(mentionComment.Author); // Verify that the media post is old enough. if (!requestorIsAdmin && parentPost.CreatedUtc.Value.UnixTimeToDateTime() > DateTime.Now.ToUniversalTime().AddMinutes(-settings.FilterSettings.MinimumPostAgeInMinutes)) { Log.Information($"Temporarily skipping {mentionComment.Name}: Post is too recent. ({mentionComment.Author}: '{mentionComment.Body}')"); continue; } await redditClient.MarkMessagesAsRead(new List <string> { mentionComment.Name }); // Verify that the requestor isn't blacklisted. if (databaseAccessor.GetBlacklistedUser(mentionComment.Author) != null) { Log.Information($"Skipping {mentionComment.Name}: Requestor is blacklisted. ({mentionComment.Author}: '{mentionComment.Body}')"); continue; } Func <string, Task> onFailedToProcessPost = async(reason) => { Log.Information($"Skipping {mentionComment.Name}: {reason} ({mentionComment.Author}: '{mentionComment.Body}')"); await PostReplyToFallbackThread($"/u/{mentionComment.Author} I was unable to process your [request](https://reddit.com{mentionComment.Context}). \nReason: {reason}"); }; // Verify that the post is safe to process. Tuple <bool, string> postSafetyInfo = await PostIsSafeToProcess(parentPost, true); if (!requestorIsAdmin && !postSafetyInfo.Item1) { await onFailedToProcessPost($"{postSafetyInfo.Item2} See [here](https://www.reddit.com/r/IVAEbot/wiki/index#wiki_limitations) for more information."); continue; } // Get the commands from the mention comment. List <IVAECommand> commands; try { commands = IVAECommandFactory.CreateCommands(mentionComment.GetCommandTextFromMention(redditClient.Username)); IVAECommand speedupCommand = commands.FirstOrDefault(c => c.GetType() == typeof(AdjustSpeedCommand) && ((AdjustSpeedCommand)c).FrameRate > 1); if (speedupCommand != null) { commands.Remove(speedupCommand); commands.Insert(0, speedupCommand); } IVAECommand trimCommand = commands.FirstOrDefault(c => c.GetType() == typeof(TrimCommand)); if (trimCommand != null) { commands.Remove(trimCommand); commands.Insert(0, trimCommand); } } catch (ArgumentException ex) { await onFailedToProcessPost($"{ex.Message} \nSee [here](https://www.reddit.com/r/IVAEbot/wiki/index#wiki_commands) for a list of valid commands."); continue; } catch (Exception ex) { Log.Warning(ex.ToString()); await onFailedToProcessPost($"An error occurred while trying to parse commands. \nSee [here](https://www.reddit.com/r/IVAEbot/wiki/index#wiki_commands) for a list of valid commands."); continue; } if (commands != null && commands.Any(command => commands.Count(cmd => cmd.GetType() == command.GetType()) > 1)) // This is O(n^2), consider something more efficient. { await onFailedToProcessPost("Multiple commands of same type."); continue; } // Get url to media file. Uri mediaUrl = GetMediaUrlFromPost(parentPost); if (mediaUrl == null || string.IsNullOrWhiteSpace(mediaUrl.AbsoluteUri)) { await onFailedToProcessPost("Invalid media URL."); continue; } // Ensure that the download directory exists. if (!System.IO.Directory.Exists(DOWNLOAD_DIR)) { System.IO.Directory.CreateDirectory(DOWNLOAD_DIR); } string mediaFilePath = null; try { // Download the media file. System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); string fileNameWithoutExtension = Guid.NewGuid().ToString(); string filePathWithoutExtension = System.IO.Path.Combine(DOWNLOAD_DIR, fileNameWithoutExtension); string mediaUrlFileExtension = System.IO.Path.GetExtension(mediaUrl.AbsoluteUri).ToLower(); if (mediaUrlFileExtension == ".jpg" || mediaUrlFileExtension == ".png") { // Verify that the file is not too large. if (!TryGetMediaFileSize(mediaUrl.AbsoluteUri, out long fileSize)) { await onFailedToProcessPost("Failed to get media file size."); continue; } else if (fileSize > settings.FilterSettings.MaximumDownloadFileSizeInMB * 10000000) { await onFailedToProcessPost("Media file too large."); continue; } mediaFilePath = $"{filePathWithoutExtension}{mediaUrlFileExtension}"; using (System.Net.WebClient client = new System.Net.WebClient()) { client.DownloadFile(mediaUrl, mediaFilePath); } } else if (mediaUrl.Host == "v.redd.it") { // Temporary override for v.redd.it links until youtube-dl is fixed. mediaFilePath = $"{filePathWithoutExtension}.mp4"; using (System.Net.WebClient client = new System.Net.WebClient()) { client.DownloadFile(mediaUrl, mediaFilePath); try { string audioUrl = $"{mediaUrl.AbsoluteUri.Substring(0, mediaUrl.AbsoluteUri.LastIndexOf("/"))}/audio"; string audioFilePath = System.IO.Path.Combine(DOWNLOAD_DIR, $"{System.IO.Path.GetFileNameWithoutExtension(mediaFilePath)}_audio"); string combinedFilePath = System.IO.Path.Combine(DOWNLOAD_DIR, "combined.mp4"); client.DownloadFile(audioUrl, audioFilePath); new MediaManipulation.FFmpegProcessRunner().Run($"-i {mediaFilePath} -i {audioFilePath} -acodec copy -vcodec copy \"{combinedFilePath}\""); System.IO.File.Delete(audioFilePath); System.IO.File.Delete(mediaFilePath); System.IO.File.Move(combinedFilePath, mediaFilePath); } catch (Exception) { } } } else { YoutubedlProcessRunner youtubedlProcessRunner = new YoutubedlProcessRunner(); List <string> downloadOutput = youtubedlProcessRunner.Run($"\"{mediaUrl.AbsoluteUri}\" --max-filesize {settings.FilterSettings.MaximumDownloadFileSizeInMB}m -o \"{filePathWithoutExtension}.%(ext)s\" -f mp4"); mediaFilePath = System.IO.Directory.GetFiles(DOWNLOAD_DIR, $"{fileNameWithoutExtension}*").SingleOrDefault(); } if (mediaFilePath == null) { await onFailedToProcessPost("Failed to download media file. (The file may have been too big.)"); continue; } // Execute all commands on the media file. long origFileSize = new System.IO.FileInfo(mediaFilePath).Length; foreach (IVAECommand command in commands) { string path = command.Execute(mediaFilePath); System.IO.File.Delete(mediaFilePath); if (System.IO.File.Exists(path)) { mediaFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(mediaFilePath), $"{System.IO.Path.GetFileNameWithoutExtension(mediaFilePath)}{System.IO.Path.GetExtension(path)}"); System.IO.File.Move(path, mediaFilePath); } } if (!System.IO.File.Exists(mediaFilePath)) { await onFailedToProcessPost($"Failed to create output file."); continue; } double transformedFileSizeInMB = ((double)new System.IO.FileInfo(mediaFilePath).Length) / 1000000; MediaManipulation.MediaFileInfo transformedMFI = new MediaManipulation.MediaFileInfo(mediaFilePath); if (!transformedMFI.IsValidMediaFile) { await onFailedToProcessPost($"Output file was broken."); continue; } if (transformedFileSizeInMB > settings.FilterSettings.MaximumUploadFileSizeInMB) { await onFailedToProcessPost($"Output file ({transformedFileSizeInMB.ToString("N2")}MB) can not be larger than {settings.FilterSettings.MaximumUploadFileSizeInMB}MB."); continue; } else if (transformedMFI.Duration > settings.FilterSettings.MaximumUploadFileDurationInSeconds) { await onFailedToProcessPost($"Output file ({transformedMFI.Duration.Value.ToString("N2")}s) can not be longer than {settings.FilterSettings.MaximumUploadFileDurationInSeconds} seconds."); continue; } // Upload transformed media file. byte[] mediaFileBytes = System.IO.File.ReadAllBytes(mediaFilePath); string deleteKey, uploadDestination, uploadPath, uploadLink; if (!transformedMFI.HasVideo || transformedMFI.Duration <= 30) { uploadDestination = "imgur"; string videoFormat = null; if (System.IO.Path.GetExtension(mediaFilePath) == ".mp4") { videoFormat = "mp4"; } ImgurUploadResponse imgurUploadResponse = await imgurClient.Upload(mediaFileBytes, videoFormat); if (imgurUploadResponse == null) { await onFailedToProcessPost("Failed to upload transformed file."); continue; } deleteKey = imgurUploadResponse.DeleteHash; uploadLink = imgurUploadResponse.Link; uploadPath = imgurUploadResponse.Name; } else { uploadDestination = "gfycat"; string gfyname = await gfycatClient.Upload(mediaFileBytes); if (gfyname == null) { await onFailedToProcessPost("Failed to upload transformed file."); continue; } deleteKey = gfyname; uploadLink = $"https://giant.gfycat.com/{gfyname}.mp4"; uploadPath = gfyname; } // Respond with link. stopwatch.Stop(); Guid uploadId = Guid.NewGuid(); string responseText = $"[Direct File Link]({uploadLink})\n\n" + $"***\n" + $"Finished in {(stopwatch.Elapsed.TotalMinutes >= 1 ? $"{stopwatch.Elapsed.ToString("mm")} minutes " : "" )}{stopwatch.Elapsed.ToString("ss")} seconds. {((double)origFileSize / 1000000).ToString("N2")}MB -> {transformedFileSizeInMB.ToString("N2")}MB. \n" + $"[How To Use](https://www.reddit.com/r/IVAEbot/wiki/index) | [Submit Feedback](https://www.reddit.com/message/compose/?to=TheTollski&subject=IVAEbot%20Feedback) | [Delete](https://www.reddit.com/message/compose/?to=IVAEbot&subject=Command&message=delete%20{uploadId.ToString()}) (Requestor Only) \n" + $"^^I ^^am ^^a ^^bot ^^in ^^beta ^^testing ^^and ^^need ^^more ^^[testers](https://www.reddit.com/r/IVAEbot/comments/bp3aha/testers_needed/). ^^Feel ^^free ^^to ^^learn ^^what ^^I ^^can ^^do ^^and ^^summon ^^me."; string replyCommentName = await redditClient.PostComment(mentionComment.Name, responseText); if (replyCommentName == null) { replyCommentName = await PostReplyToFallbackThread($"/u/{mentionComment.Author} I was unable to repond directly to your [request]({mentionComment.Permalink}) so I have posted my response here.\n\n{responseText}"); } databaseAccessor.InsertUploadLog(new UploadLog { Id = uploadId, PostFullname = parentPost.Name, ReplyDeleted = false, ReplyFullname = replyCommentName, RequestorUsername = mentionComment.Author, UploadDatetime = DateTime.UtcNow, UploadDeleted = false, UploadDeleteKey = deleteKey, UploadDestination = uploadDestination, UploadPath = uploadPath }); } catch (Exception) { if (!string.IsNullOrWhiteSpace(mediaFilePath) && System.IO.File.Exists(mediaFilePath)) { System.IO.File.Delete(mediaFilePath); } throw; } } catch (Exception ex) { Log.Error(ex, "Exception occurred while processing a request."); } } }