diff --git a/bdfr/site_downloaders/download_factory.py b/bdfr/site_downloaders/download_factory.py index 49dba5f..96e9a42 100644 --- a/bdfr/site_downloaders/download_factory.py +++ b/bdfr/site_downloaders/download_factory.py @@ -17,6 +17,7 @@ from bdfr.site_downloaders.pornhub import PornHub from bdfr.site_downloaders.redgifs import Redgifs from bdfr.site_downloaders.self_post import SelfPost from bdfr.site_downloaders.vidble import Vidble +from bdfr.site_downloaders.vreddit import VReddit from bdfr.site_downloaders.youtube import Youtube @@ -47,6 +48,8 @@ class DownloadFactory: return Youtube elif re.match(r'i\.redd\.it.*', sanitised_url): return Direct + elif re.match(r'v\.redd\.it.*', sanitised_url): + return VReddit elif re.match(r'pornhub\.com.*', sanitised_url): return PornHub elif re.match(r'vidble\.com', sanitised_url): diff --git a/bdfr/site_downloaders/vreddit.py b/bdfr/site_downloaders/vreddit.py new file mode 100644 index 0000000..ad526b4 --- /dev/null +++ b/bdfr/site_downloaders/vreddit.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import logging +import tempfile +from pathlib import Path +from typing import Callable, Optional + +import yt_dlp +from praw.models import Submission + +from bdfr.exceptions import NotADownloadableLinkError, SiteDownloaderError +from bdfr.resource import Resource +from bdfr.site_authenticator import SiteAuthenticator +from bdfr.site_downloaders.youtube import Youtube + +logger = logging.getLogger(__name__) + + +class VReddit(Youtube): + def __init__(self, post: Submission): + super().__init__(post) + + def find_resources(self, authenticator: Optional[SiteAuthenticator] = None) -> list[Resource]: + ytdl_options = { + 'playlistend': 1, + 'nooverwrites': True, + } + download_function = self._download_video(ytdl_options) + extension = self.get_video_attributes(self.post.url)['ext'] + res = Resource(self.post, self.post.url, download_function, extension) + return [res] + + @staticmethod + def get_video_attributes(url: str) -> dict: + result = VReddit.get_video_data(url) + if 'ext' in result: + return result + else: + try: + result = result["entries"][0] + return result + except Exception as e: + logger.exception(e) + raise NotADownloadableLinkError(f'Video info extraction failed for {url}') diff --git a/bdfr/site_downloaders/youtube.py b/bdfr/site_downloaders/youtube.py index f18f405..70c35ae 100644 --- a/bdfr/site_downloaders/youtube.py +++ b/bdfr/site_downloaders/youtube.py @@ -58,7 +58,7 @@ class Youtube(BaseDownloader): return download @staticmethod - def get_video_attributes(url: str) -> dict: + def get_video_data(url: str) -> dict: yt_logger = logging.getLogger('youtube-dl') yt_logger.setLevel(logging.CRITICAL) with yt_dlp.YoutubeDL({'logger': yt_logger, }) as ydl: @@ -67,6 +67,11 @@ class Youtube(BaseDownloader): except Exception as e: logger.exception(e) raise NotADownloadableLinkError(f'Video info extraction failed for {url}') + return result + + @staticmethod + def get_video_attributes(url: str) -> dict: + result = Youtube.get_video_data(url) if 'ext' in result: return result else: diff --git a/tests/site_downloaders/fallback_downloaders/test_ytdlp_fallback.py b/tests/site_downloaders/fallback_downloaders/test_ytdlp_fallback.py index 23c95f9..9aeca98 100644 --- a/tests/site_downloaders/fallback_downloaders/test_ytdlp_fallback.py +++ b/tests/site_downloaders/fallback_downloaders/test_ytdlp_fallback.py @@ -15,7 +15,7 @@ from bdfr.site_downloaders.fallback_downloaders.ytdlp_fallback import YtdlpFallb ('https://www.youtube.com/watch?v=P19nvJOmqCc', True), ('https://www.example.com/test', False), ('https://milesmatrix.bandcamp.com/album/la-boum/', False), - ('https://v.redd.it/54i8fvzev3u81', True), + ('https://v.redd.it/54i8fvzev3u81', False), )) def test_can_handle_link(test_url: str, expected: bool): result = YtdlpFallback.can_handle_link(test_url) diff --git a/tests/site_downloaders/test_download_factory.py b/tests/site_downloaders/test_download_factory.py index 134396c..dcb5303 100644 --- a/tests/site_downloaders/test_download_factory.py +++ b/tests/site_downloaders/test_download_factory.py @@ -16,6 +16,7 @@ from bdfr.site_downloaders.imgur import Imgur from bdfr.site_downloaders.pornhub import PornHub from bdfr.site_downloaders.redgifs import Redgifs from bdfr.site_downloaders.self_post import SelfPost +from bdfr.site_downloaders.vreddit import VReddit from bdfr.site_downloaders.youtube import Youtube @@ -42,12 +43,13 @@ from bdfr.site_downloaders.youtube import Youtube ('https://i.imgur.com/3SKrQfK.jpg?1', Direct), ('https://dynasty-scans.com/system/images_images/000/017/819/original/80215103_p0.png?1612232781', Direct), ('https://m.imgur.com/a/py3RW0j', Imgur), - ('https://v.redd.it/9z1dnk3xr5k61', YtdlpFallback), + ('https://v.redd.it/9z1dnk3xr5k61', VReddit), ('https://streamable.com/dt46y', YtdlpFallback), ('https://vimeo.com/channels/31259/53576664', YtdlpFallback), ('http://video.pbs.org/viralplayer/2365173446/', YtdlpFallback), ('https://www.pornhub.com/view_video.php?viewkey=ph5a2ee0461a8d0', PornHub), ('https://www.patreon.com/posts/minecart-track-59346560', Gallery), + ('https://v.redd.it/9z1dnk3xr5k61', VReddit) )) def test_factory_lever_good(test_submission_url: str, expected_class: BaseDownloader, reddit_instance: praw.Reddit): result = DownloadFactory.pull_lever(test_submission_url) diff --git a/tests/site_downloaders/test_vreddit.py b/tests/site_downloaders/test_vreddit.py new file mode 100644 index 0000000..f1f8219 --- /dev/null +++ b/tests/site_downloaders/test_vreddit.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# coding=utf-8 + +from unittest.mock import MagicMock + +import pytest + +from bdfr.exceptions import NotADownloadableLinkError +from bdfr.resource import Resource +from bdfr.site_downloaders.vreddit import VReddit + + +@pytest.mark.online +@pytest.mark.slow +@pytest.mark.parametrize(('test_url', 'expected_hash'), ( + ('https://www.reddit.com/user/Xomb_Forever/comments/u5p2kj/hold_up/', '690cffe27a7884196437926c22897216'), +)) +def test_find_resources_good(test_url: str, expected_hash: str): + test_submission = MagicMock() + test_submission.url = test_url + downloader = VReddit(test_submission) + resources = downloader.find_resources() + assert len(resources) == 1 + assert isinstance(resources[0], Resource) + resources[0].download() + assert resources[0].hash.hexdigest() == expected_hash + + +@pytest.mark.online +@pytest.mark.parametrize('test_url', ( + 'https://www.polygon.com/disney-plus/2020/5/14/21249881/gargoyles-animated-series-disney-plus-greg-weisman' + '-interview-oj-simpson-goliath-chronicles', +)) +def test_find_resources_bad(test_url: str): + test_submission = MagicMock() + test_submission.url = test_url + downloader = VReddit(test_submission) + with pytest.raises(NotADownloadableLinkError): + downloader.find_resources()