async def on_new_message(self, chat: Chat, message: Message):
     # If a message has text saying to rotate, and is a reply to a video, then cut it
     # `rotate left`, `rotate right`, `flip horizontal`?, `rotate 90`, `rotate 180`
     text_clean = message.text.strip().lower().replace("-", "")
     if text_clean.startswith("rotate"):
         transpose = self.get_rotate_direction(
             text_clean[len("rotate"):].strip())
     elif text_clean.startswith("flip"):
         transpose = self.get_flip_direction(
             text_clean[len("flip"):].strip())
     else:
         return
     video = find_video_for_message(chat, message)
     if video is None:
         await self.send_text_reply(
             chat, message,
             "Cannot work out which video you want to rotate/flip.")
     if transpose is None:
         return [
             await self.send_text_reply(
                 chat, message,
                 "I do not understand this rotate/flip command.")
         ]
     async with self.progress_message(chat, message,
                                      "Rotating or flipping video.."):
         output_path = random_sandbox_video_path()
         task = FfmpegTask(inputs={video.message_data.file_path: None},
                           outputs={output_path: f"-vf \"{transpose}\""})
         await self.worker.await_task(task)
         return [
             await self.send_video_reply(chat, message, output_path,
                                         video.tags(self.database))
         ]
Exemple #2
0
 async def on_new_message(self, chat: Chat,
                          message: Message) -> Optional[List[Message]]:
     text_clean = message.text.lower().strip()
     if not text_clean.startswith("video"):
         return
     video = find_video_for_message(chat, message)
     if video is None:
         return [
             await self.send_text_reply(
                 chat, message,
                 "I'm not sure which video you want to video.")
         ]
     # Parse arguments, if present
     args = text_clean[5:].strip().split()
     gif_settings = None
     if args:
         gif_settings = GifSettings.from_input(args)
         gif_settings.audio = True
     # Convert video
     output_path = random_sandbox_video_path()
     async with self.progress_message(chat, message,
                                      "Converting video into video"):
         if not await self.video_has_audio_track(video):
             task = add_audio_track_task(video.message_data.file_path,
                                         output_path)
             await self.worker.await_task(task)
         else:
             tasks = video_to_video(video.message_data.file_path,
                                    output_path, gif_settings)
             for task in tasks:
                 await self.worker.await_task(task)
         return [
             await self.send_video_reply(chat, message, output_path,
                                         video.tags(self.database))
         ]
 async def merge_messages(
         self, chat: Chat, cmd_message: Message,
         messages_to_merge: List[Message]) -> Optional[List[Message]]:
     if len(messages_to_merge) < 2:
         error_text = \
             "Merge commands require at least 2 videos to merge. " \
             "Please reply to a message, and provide telegram links to the other messages"
         return [await self.send_text_reply(chat, cmd_message, error_text)]
     num_files = len(messages_to_merge)
     filter_args = "".join([f"[{x}:v][{x}:a]" for x in range(num_files)
                            ]) + f" concat=n={num_files}:v=1:a=1 [v] [a]"
     output_args = f"-filter_complex \"{filter_args}\" -map \"[v]\" -map \"[a]\" -vsync 2"
     async with self.progress_message(chat, cmd_message, "Merging videos"):
         file_paths = await self.align_video_dimensions(
             [m.message_data.file_path for m in messages_to_merge])
         output_path = random_sandbox_video_path()
         task = FfmpegTask(
             inputs={file_path: None
                     for file_path in file_paths},
             outputs={output_path: output_args})
         await self.worker.await_task(task)
         tags = messages_to_merge[0].tags(self.database)
         tags.merge_all(
             [msg.tags(self.database) for msg in messages_to_merge[1:]])
         return [
             await self.send_video_reply(chat, cmd_message, output_path,
                                         tags)
         ]
 async def with_audio_track(self, file_path: str) -> str:
     audio_check_task = video_has_audio_track_task(file_path)
     if not await self.worker.await_task(audio_check_task):
         output_path = random_sandbox_video_path()
         await self.worker.await_task(
             add_audio_track_task(file_path, output_path))
         return output_path
     return file_path
 async def convert_file(self, video_path: str) -> str:
     if video_path.endswith(".gif"):
         return await self.convert_video_to_telegram_gif(video_path)
     else:
         processed_path = random_sandbox_video_path()
         task = FfmpegTask(inputs={video_path: None},
                           outputs={processed_path: "-qscale 0"})
         await self.worker.await_task(task)
         return processed_path
Exemple #6
0
 async def single_pass_convert(self, video_path: str,
                               gif_settings: GifSettings):
     first_pass_filename = random_sandbox_video_path()
     # first attempt
     ffmpeg_args = gif_settings.ffmpeg_options_one_pass
     task = FfmpegTask(inputs={video_path: None},
                       outputs={first_pass_filename: ffmpeg_args})
     await self.worker.await_task(task)
     return first_pass_filename
Exemple #7
0
 async def cut_out_video(self, video: Message, start: str, end: str) -> str:
     first_part_path = random_sandbox_video_path()
     second_part_path = random_sandbox_video_path()
     task1 = FfmpegTask(inputs={video.message_data.file_path: None},
                        outputs={first_part_path: f"-to {start}"})
     task2 = FfmpegTask(inputs={video.message_data.file_path: None},
                        outputs={second_part_path: f"-ss {end}"})
     await self.worker.await_tasks([task1, task2])
     inputs_file = random_sandbox_video_path("txt")
     with open(inputs_file, "w") as f:
         f.write(
             f"file '{first_part_path.split('/')[1]}'\nfile '{second_part_path.split('/')[1]}'"
         )
     output_path = random_sandbox_video_path()
     task_concat = FfmpegTask(inputs={inputs_file: "-safe 0 -f concat"},
                              outputs={output_path: "-c copy"})
     await self.worker.await_task(task_concat)
     return output_path
Exemple #8
0
 async def cut_video(self, video: Message, start: Optional[str],
                     end: Optional[str]) -> str:
     new_path = random_sandbox_video_path()
     out_string = (f"-ss {start}" if start is not None else
                   "") + " " + (f"-to {end}" if end is not None else "")
     task = FfmpegTask(inputs={video.message_data.file_path: None},
                       outputs={new_path: out_string})
     await self.worker.await_task(task)
     return new_path
Exemple #9
0
 async def convert_gif_link(self, chat: Chat, message: Message,
                            gif_link: str) -> Message:
     resp = requests.get(gif_link)
     gif_path = random_sandbox_video_path("gif")
     with open(gif_path, "wb") as f:
         f.write(resp.content)
     new_path = await self.convert_video_to_telegram_gif(gif_path)
     tags = VideoTags()
     tags.add_tag_value(VideoTags.source, gif_link)
     return await self.send_video_reply(chat, message, new_path, tags)
 async def send_imgur_video(self, chat: Chat, message: Message,
                            image: Dict[str,
                                        str], gallery_link: str) -> Message:
     file_url = image["mp4"]
     file_ext = file_url.split(".")[-1]
     resp = requests.get(file_url)
     file_path = random_sandbox_video_path(file_ext)
     with open(file_path, "wb") as f:
         f.write(resp.content)
     tags = VideoTags()
     tags.add_tag_value(VideoTags.source, gallery_link)
     return await self.send_video_reply(chat, message, file_path, tags)
 async def scale_and_pad_to_dimensions(self, file_path: str,
                                       dimensions: Tuple[int, int]) -> str:
     orig_dimensions = await self.get_video_dimensions(file_path)
     if orig_dimensions == dimensions:
         return file_path
     output_path = random_sandbox_video_path()
     x, y = dimensions
     args = f"-vf \"scale={x}:{y}:force_original_aspect_ratio=decrease,pad={x}:{y}:(ow-iw)/2:(oh-ih)/2,setsar=1\""
     task = FfmpegTask(inputs={file_path: None},
                       outputs={output_path: args})
     await self.worker.await_task(task)
     return output_path
Exemple #12
0
 async def two_pass_convert(self, video_path: str,
                            gif_settings: GifSettings):
     # If it's too big, do a 2 pass run
     two_pass_filename = random_sandbox_video_path()
     # First pass
     two_pass_args = gif_settings.ffmpeg_options_two_pass
     task1 = FfmpegTask(global_options=["-y"],
                        inputs={video_path: None},
                        outputs={os.devnull: two_pass_args[0]})
     await self.worker.await_task(task1)
     task2 = FfmpegTask(global_options=["-y"],
                        inputs={video_path: None},
                        outputs={two_pass_filename: two_pass_args[1]})
     await self.worker.await_task(task2)
     return two_pass_filename
Exemple #13
0
 async def on_new_message(self, chat: Chat, message: Message):
     # If a message has text saying to crop, some percentages maybe?
     # And is a reply to a video, then crop it
     text_clean = message.text.lower().strip()
     if not text_clean.startswith("crop"):
         return
     video = find_video_for_message(chat, message)
     if video is None:
         return [
             await self.send_text_reply(
                 chat, message,
                 "I'm not sure which video you would like to crop.")
         ]
     crop_args = text_clean[len("crop"):].strip()
     if crop_args.lower() == "auto":
         async with self.progress_message(chat, message,
                                          "Detecting auto crop settings"):
             crop_string = await self.detect_crop(
                 video.message_data.file_path)
         if crop_string is None:
             return [
                 await self.send_text_reply(
                     chat, message, "That video could not be auto cropped.")
             ]
     else:
         crop_string = self.parse_crop_input(crop_args)
     if crop_string is None:
         return [
             await self.send_text_reply(
                 chat, message, "I don't understand this crop command. "
                 "Please specify what percentage to cut off the left, right, top, bottom. "
                 "Alternatively specify the desired percentage for the width and height. "
                 "Use the format `crop left 20% right 20% top 10%`. "
                 "If the video has black bars you wish to crop, just use `crop auto`"
             )
         ]
     output_path = random_sandbox_video_path()
     async with self.progress_message(chat, message, "Cropping video"):
         task = FfmpegTask(inputs={video.message_data.file_path: None},
                           outputs={
                               output_path:
                               f"-filter:v \"{crop_string}\" -c:a copy"
                           })
         await self.worker.await_task(task)
         return [
             await self.send_video_reply(chat, message, output_path,
                                         video.tags(self.database))
         ]
 async def on_new_message(self, chat: Chat,
                          message: Message) -> Optional[List[Message]]:
     clean_text = message.text.strip().lower()
     if clean_text != "reverse":
         return None
     video = find_video_for_message(chat, message)
     if video is None:
         return [
             await self.send_text_reply(
                 chat, message,
                 "Please reply to the video you want to reverse")
         ]
     output_path = random_sandbox_video_path()
     reverse_task = FfmpegTask(
         inputs={video.message_data.file_path: None},
         outputs={output_path: "-vf reverse -af areverse"})
     async with self.progress_message(chat, message, "Reversing video"):
         await self.worker.await_task(reverse_task)
         return [
             await self.send_video_reply(chat, message, output_path,
                                         video.tags(self.database))
         ]
Exemple #15
0
 async def unzip(self, chat: Chat,
                 message: Message) -> Optional[List[Message]]:
     video_paths = []
     with zipfile.ZipFile(message.message_data.file_path, "r") as zip_ref:
         for filename in zip_ref.namelist():
             mime_type, _ = mimetypes.guess_type(filename)
             if mime_type_is_video(mime_type):
                 file_ext = filename.split(".")[-1]
                 video_path = random_sandbox_video_path(file_ext)
                 with zip_ref.open(filename) as zf, open(video_path,
                                                         "wb") as f:
                     shutil.copyfileobj(zf, f)
                 video_paths.append(video_path)
     # Convert to mp4s
     processed_paths = await asyncio.gather(*(self.convert_file(path)
                                              for path in video_paths))
     # Send them
     if processed_paths:
         return await asyncio.gather(
             *(self.send_video_reply(chat, message, path, VideoTags())
               for path in processed_paths))
     return None
Exemple #16
0
 async def on_new_message(self, chat: Chat,
                          message: Message) -> Optional[List[Message]]:
     text_clean = message.text.lower().strip()
     if text_clean not in [
             "stabilise", "stabilize", "stab", "deshake", "unshake"
     ]:
         return
     video = find_video_for_message(chat, message)
     if video is None:
         return [
             await self.send_text_reply(
                 chat, message,
                 "I'm not sure which video you would like to stabilise.")
         ]
     output_path = random_sandbox_video_path()
     async with self.progress_message(chat, message, "Stabilising video"):
         task = FfmpegTask(inputs={video.message_data.file_path: None},
                           outputs={output_path: "-vf deshake"})
         await self.worker.await_task(task)
         return [
             await self.send_video_reply(chat, message, output_path,
                                         video.tags(self.database))
         ]
 async def align_video_dimensions(self, file_paths: List[str]) -> List[str]:
     first = file_paths[0]
     the_rest = file_paths[1:]
     # Get dimensions of first video, scale the rest to match
     dimensions = await self.get_video_dimensions(first)
     rescaled = await asyncio.gather(*[
         self.scale_and_pad_to_dimensions(path, dimensions)
         for path in the_rest
     ])
     same_dimension_paths = [first] + rescaled
     # Add audio tracks if necessary
     with_audio = await asyncio.gather(
         *[self.with_audio_track(path) for path in same_dimension_paths])
     # Handle duplicate file paths. Copy them to sandbox files
     output_paths = []
     for path in with_audio:
         if path in output_paths:
             new_path = random_sandbox_video_path(path.split(".")[-1])
             shutil.copyfile(path, new_path)
             output_paths.append(new_path)
         else:
             output_paths.append(path)
     return output_paths
Exemple #18
0
 async def handle_post_link(self, chat: Chat, message: Message,
                            post_id: str):
     api_link = f"https://faexport.spangle.org.uk/submission/{post_id}.json"
     api_resp = requests.get(api_link,
                             headers={"User-Agent": "Gif pipeline"})
     api_data = api_resp.json()
     file_url = api_data["download"]
     file_ext = file_url.split(".")[-1]
     if file_ext not in ["gif"]:
         return await self.send_text_reply(
             chat, message, "That post doesn't seem to be a gif.")
     # Download file
     resp = requests.get(file_url)
     file_path = random_sandbox_video_path(file_ext)
     with open(file_path, "wb") as f:
         f.write(resp.content)
     # If gif, convert to telegram gif
     if file_ext == "gif":
         file_path = await self.convert_video_to_telegram_gif(file_path)
     tags = VideoTags()
     tags.add_tag_value(VideoTags.source,
                        f"https://furaffinity.net/view/{post_id}/")
     return await self.send_video_reply(chat, message, file_path, tags)
Exemple #19
0
 async def handle_post_link(self, chat: Chat, message: Message,
                            post_id: str):
     api_link = f"https://e621.net/posts/{post_id}.json"
     api_resp = requests.get(
         api_link,
         headers={"User-Agent": "Gif pipeline (my username is dr-spangle)"})
     api_data = api_resp.json()
     file_ext = api_data["post"]["file"]["ext"]
     if file_ext not in ["gif", "webm"]:
         return await self.send_text_reply(
             chat, message, "That post doesn't seem to be a gif or webm.")
     file_url = api_data["post"]["file"]["url"]
     # Download file
     resp = requests.get(file_url)
     file_path = random_sandbox_video_path(file_ext)
     with open(file_path, "wb") as f:
         f.write(resp.content)
     # If gif, convert to telegram gif
     if file_ext == "gif":
         file_path = await self.convert_video_to_telegram_gif(file_path)
     tags = VideoTags()
     tags.add_tag_value(VideoTags.source,
                        f"https://e621.net/posts/{post_id}")
     return await self.send_video_reply(chat, message, file_path, tags)
 async def download_link(self, link: str) -> str:
     output_path = random_sandbox_video_path("")
     task = YoutubeDLTask(link, output_path)
     return await self.worker.await_task(task)
Exemple #21
0
 def pass_log_file(self) -> str:
     if self._pass_log_file is None:
         self._pass_log_file = random_sandbox_video_path("")
     return self._pass_log_file