-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change Twitter download to a more abstracted video download that hand…
…les more websites(#14) * Add instaloader to requirments * Add a `Downloader` interphase and `VideoFile` data type. This interphase allows for easier implementations of other methods to be added. Currently planned are: - Twitter (as that already exists in main right now) - Instagram (Might require using an account for reliable downloading) - Youtube (Already have most ready, just needs an additional function) * Implement the downloader in twitter.py * create downloading_system this will also support Instagram and Youtube soon * update the download command to use the VideoDownloader abstraction BREAKING: change its name to "video-indir" as it will support more platforms not just twitter WARNING: IF WANTED TO REVERT, ALSO REVERT THE LAST 2 COMMITS * add Youtube to `video-indir` command * add Instagram to `video-indir` command INFO: tries to log in to a instagram account from env variables: `INSTAGRAM_USERNAME` and `INSTAGRAM_PASSWORD` WARNING: writes to "/jsons/instagram_session.json" when instagram logs in BE CAREFULL TO KEEP IT A SECRET LIKE A TOKEN ALSO_UPDATES: gitignore and dockerignore * add mutli video support to `instagram.py` I was finally able to find an example of a "GraphSidecar"... Its insta naming this not me. * update all subclasses to use a spesific return type This will make future refactors like adding a error-type easier. * add better error handling and multiple video support to `twitter.py` Now it finally works when there are multiple videos on a tweet. It is also a lot more reliable then before, WOHOO! * move video download command to a separate file This will be used for the context menu feature that I will add * remove old context menus and add new video ones Old ones were not used anymore so away they go, and instead now we add 2 extra context menu abilities so that people can download videos without notifying the other person using ephemeral messages * try to extract the url if match failed in `get_downloader` * edit documentation * move converting to discord files into try-catch this should result in less errors causing a infinite load error * add an s if video count is more than one in download command text * compile url_find regex before use * improve the error message shown to user
- Loading branch information
Showing
11 changed files
with
366 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import logging | ||
import discord | ||
|
||
from src.Helpers.twitter_helpers import convert_paths_to_discord_files | ||
from src.downloading_system import get_downloader | ||
|
||
|
||
async def download_video_command(interaction: discord.Interaction, url: str, is_ephemeral: bool = False): | ||
#TODO: add better error handling then just catching all exceptions | ||
downloader = get_downloader(url) | ||
if downloader is None: | ||
await interaction.response.send_message("Bu link desteklenmiyor", ephemeral=True) | ||
logging.info("Found an unsupported link: %s", url) | ||
return | ||
|
||
await interaction.response.defer(ephemeral=is_ephemeral) | ||
|
||
try: | ||
attachments = downloader.download_video_from_link(url) | ||
file_paths = [attachment.path for attachment in attachments] | ||
discord_files = convert_paths_to_discord_files(file_paths) | ||
except Exception as e: | ||
await interaction.followup.send("Bir şey ters gitti... lütfen tekrar deneyin", ephemeral=True) | ||
raise e # re-raise the exception so we can see what went wrong | ||
if len(attachments) == 0: | ||
await interaction.followup.send("Videoyu Bulamadım, lütfen daha sonra tekrar deneyin ya da hatayı bildirin", ephemeral=True) | ||
return | ||
content = " + ".join(filter(None, [attachment.caption for attachment in attachments])) or f"Video{'s' if len(attachments) > 1 else ''} Downloaded" | ||
await interaction.followup.send(content, files=discord_files, ephemeral=is_ephemeral) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import logging | ||
from abc import ABC, abstractmethod | ||
|
||
_NONE_STRING = "Doesn't exist" | ||
|
||
|
||
class VideoFile: | ||
""" | ||
Video object that contains the title, and its file path. | ||
""" | ||
def __init__(self, file_path: str, title: str | None = None) -> None: | ||
self._title = title | ||
self._file_path = file_path | ||
|
||
def __str__(self) -> str: | ||
return f"Title: {self._title}, File Path: {self._file_path}" | ||
|
||
def __repr__(self) -> str: | ||
return f'Title: {self._title or _NONE_STRING}, File Path: {self._file_path or _NONE_STRING}' | ||
|
||
def __hash__(self) -> int: | ||
return hash((self._title, self._file_path)) | ||
|
||
def __eq__(self, other: object) -> bool: | ||
if not isinstance(other, VideoFile): | ||
return False | ||
return self._title == other._title and self._file_path == other._file_path | ||
|
||
@property | ||
def caption(self) -> str | None: | ||
return self._title | ||
|
||
@property | ||
def path(self) -> str: | ||
return self._file_path | ||
|
||
|
||
|
||
VIDEO_RETURN_TYPE = list[VideoFile] | ||
|
||
class VideoDownloader(ABC): | ||
""" | ||
INTERPHASE FOR DOWNLOADING CONTENT FROM A WEBSITE | ||
""" | ||
|
||
@staticmethod | ||
@abstractmethod | ||
def download_video_from_link(url: str, path: str | None = None) -> VIDEO_RETURN_TYPE: | ||
""" | ||
Downloads Videos from a url | ||
if path is None, the default path is downloads/{website_name} | ||
if the download fails, it returns an empty list | ||
""" | ||
logging.error( | ||
"VideoDownloader download_url interface was directly called, this should not happen! url was: %s for path: %s", | ||
url, | ||
path, | ||
) | ||
return [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import re | ||
from typing import Type | ||
|
||
from src.Youtube import YoutubeDownloader | ||
from src.downloader import VideoDownloader | ||
from src.instagram import InstagramDownloader | ||
from src.twitter import TwitterDownloader | ||
|
||
_URL_PARSE_REGEX = re.compile(r"\b((?:https?://)?(?:(?:www\.)?(?:[\da-z\.-]+)\.(?:[a-z]{2,6})|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])))(?::[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?(?:/[\w\.-]*)*/?)\b") # NOSONAR | ||
|
||
_TWITTER_REGEX = re.compile(r"\b(?:https?:\/\/)?(?:www\.)?(?:twitter\.com\/|t\.co\/|x\.com\/)\S*") | ||
_INSTAGRAM_REGEX = re.compile(r"\b(?:https?:\/\/)?(?:www\.)?(?:instagram\.com\/|instagr\.am\/)\S*") | ||
_YOUTUBE_REGEX = re.compile(r"\b(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/|youtu\.be\/)\S*") | ||
|
||
|
||
def get_downloader(url: str) -> Type[VideoDownloader] | None: | ||
""" | ||
Returns the correct downloader for the given url if it can't find it | ||
it tries to extract the url incase there is extra text in the url string | ||
if it still can't find a downloader, it returns None | ||
""" | ||
|
||
if re.match(_TWITTER_REGEX, url): | ||
return TwitterDownloader | ||
if re.match(_INSTAGRAM_REGEX, url): | ||
return InstagramDownloader | ||
if re.match(_YOUTUBE_REGEX, url): | ||
return YoutubeDownloader | ||
# try to extract the url from the text incase there is extra text | ||
if (result := re.search(_URL_PARSE_REGEX, url)) and result.group(0) != url: | ||
return get_downloader(result.group(0)) | ||
return None |
Oops, something went wrong.