Use guerrilla mail
This commit is contained in:
@@ -59,15 +59,15 @@ from configparser import ConfigParser
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from http.cookiejar import MozillaCookieJar
|
from http.cookiejar import MozillaCookieJar
|
||||||
from typing import Dict, Optional, Set
|
from typing import Dict, Optional, Set
|
||||||
from urllib.parse import urljoin, urlsplit
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import mutagen
|
import mutagen
|
||||||
import pyrfc6266
|
import pyrfc6266
|
||||||
import requests
|
import requests
|
||||||
import secmail
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
from guerrillamail import GuerrillaMailSession
|
||||||
|
|
||||||
from free_bandcamp_downloader import __version__, logger
|
from free_bandcamp_downloader import __version__, logger
|
||||||
from free_bandcamp_downloader.bandcamp_http_adapter import BandcampHTTPAdapter
|
from free_bandcamp_downloader.bandcamp_http_adapter import BandcampHTTPAdapter
|
||||||
@@ -75,20 +75,20 @@ from free_bandcamp_downloader.bandcamp_http_adapter import BandcampHTTPAdapter
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BCFreeDownloaderOptions:
|
class BCFreeDownloaderOptions:
|
||||||
country: str = None
|
country: str = "United States"
|
||||||
zipcode: str = None
|
zipcode: str = "00000"
|
||||||
email: str = None
|
email: str = "auto"
|
||||||
format: str = None
|
format: str = "FLAC"
|
||||||
dir: str = None
|
dir: str = "."
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BCFreeDownloaderAlbumData:
|
class BCFreeDownloaderAlbumData:
|
||||||
about: str = None
|
about: Optional[str]
|
||||||
credits: str = None
|
credits: Optional[str]
|
||||||
tags: str = None
|
tags: Optional[str]
|
||||||
id: str = None
|
id: str
|
||||||
title: str = None
|
title: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class BCFreeDownloadError(Exception):
|
class BCFreeDownloadError(Exception):
|
||||||
@@ -126,13 +126,12 @@ class BCFreeDownloader:
|
|||||||
self.mail_session = None
|
self.mail_session = None
|
||||||
self.mail_album_data: Dict[str, BCFreeDownloaderAlbumData] = {}
|
self.mail_album_data: Dict[str, BCFreeDownloaderAlbumData] = {}
|
||||||
self.unzip = unzip
|
self.unzip = unzip
|
||||||
self.session = None
|
|
||||||
self._init_downloaded()
|
self._init_downloaded()
|
||||||
self._init_session(cookies_file, identity)
|
self._init_session(cookies_file, identity)
|
||||||
|
|
||||||
def _init_email(self):
|
def _init_email(self):
|
||||||
self.mail_session = secmail.Client(self.config_dir)
|
self.mail_session = GuerrillaMailSession()
|
||||||
self.options.email = self.mail_session.random_email(1, "1secmail.com")[0]
|
self.options.email = self.mail_session.get_session_state()["email_address"]
|
||||||
|
|
||||||
def _init_downloaded(self):
|
def _init_downloaded(self):
|
||||||
if self.download_history_file:
|
if self.download_history_file:
|
||||||
@@ -240,7 +239,6 @@ class BCFreeDownloader:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_album_data_from_soup(soup: BeautifulSoup) -> BCFreeDownloaderAlbumData:
|
def _get_album_data_from_soup(soup: BeautifulSoup) -> BCFreeDownloaderAlbumData:
|
||||||
album_data = BCFreeDownloaderAlbumData()
|
|
||||||
about = soup.find("div", class_="tralbum-about")
|
about = soup.find("div", class_="tralbum-about")
|
||||||
credits = soup.find("div", class_="tralbum-credits")
|
credits = soup.find("div", class_="tralbum-credits")
|
||||||
tags = [tag.get_text() for tag in soup.find_all("a", class_="tag")]
|
tags = [tag.get_text() for tag in soup.find_all("a", class_="tag")]
|
||||||
@@ -249,12 +247,13 @@ class BCFreeDownloader:
|
|||||||
)
|
)
|
||||||
id = f"{properties['item_type']}:{properties['item_id']}"
|
id = f"{properties['item_type']}:{properties['item_id']}"
|
||||||
|
|
||||||
album_data.about = about.get_text("\n") if about else None
|
return BCFreeDownloaderAlbumData(
|
||||||
album_data.credits = credits.get_text("\n") if credits else None
|
about=about.get_text("\n") if about else None,
|
||||||
album_data.tags = ",".join(sorted(tags))
|
credits=credits.get_text("\n") if credits else None,
|
||||||
album_data.id = id
|
tags=",".join(sorted(tags)),
|
||||||
|
id=id,
|
||||||
return album_data
|
title=None,
|
||||||
|
)
|
||||||
|
|
||||||
def _download_purchased_album(
|
def _download_purchased_album(
|
||||||
self, user_id: int, album_data: BCFreeDownloaderAlbumData
|
self, user_id: int, album_data: BCFreeDownloaderAlbumData
|
||||||
@@ -330,7 +329,6 @@ class BCFreeDownloader:
|
|||||||
|
|
||||||
if head_data["offers"]["price"] == 0.0:
|
if head_data["offers"]["price"] == 0.0:
|
||||||
if tralbum_data["current"]["require_email"]:
|
if tralbum_data["current"]["require_email"]:
|
||||||
raise BCFreeDownloadError(f"{url} requires email. Skipping...")
|
|
||||||
if not self.options.email or self.options.email == "auto":
|
if not self.options.email or self.options.email == "auto":
|
||||||
self._init_email()
|
self._init_email()
|
||||||
logger.info(f"{url} requires email")
|
logger.info(f"{url} requires email")
|
||||||
@@ -414,7 +412,7 @@ class BCFreeDownloader:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
url_type = soup.head.find("meta", attrs={"property": "og:type"})["content"]
|
url_type = soup.head.find("meta", attrs={"property": "og:type"})["content"]
|
||||||
except:
|
except Exception:
|
||||||
raise BCFreeDownloadError(f"{url} does not have an og:type property.")
|
raise BCFreeDownloadError(f"{url} does not have an og:type property.")
|
||||||
|
|
||||||
if url_type == "album" or url_type == "song":
|
if url_type == "album" or url_type == "song":
|
||||||
@@ -429,24 +427,27 @@ class BCFreeDownloader:
|
|||||||
while (expected_emails := len(self.mail_album_data)) > 0:
|
while (expected_emails := len(self.mail_album_data)) > 0:
|
||||||
logger.info(f"Waiting for {expected_emails} emails from Bandcamp...")
|
logger.info(f"Waiting for {expected_emails} emails from Bandcamp...")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
for email in self.mail_session.get_inbox(self.options.email):
|
for email in self.mail_session.get_email_list():
|
||||||
if email.id not in checked_ids:
|
email_id = email.guid
|
||||||
checked_ids.add(email.id)
|
if email_id not in checked_ids:
|
||||||
|
checked_ids.add(email_id)
|
||||||
if (
|
if (
|
||||||
email.from_address.endswith("@email.bandcamp.com")
|
email.sender == "noreply@bandcamp.com"
|
||||||
and "download" in email.subject
|
and "download" in email.subject
|
||||||
):
|
):
|
||||||
logger.info(f'Received email "{email.subject}"')
|
logger.info(f'Received email "{email.subject}"')
|
||||||
email = self.mail_session.get_message(
|
content = self.mail_session.get_email(email_id).body
|
||||||
self.options.email, email.id
|
match = self.LINK_REGEX.search(content)
|
||||||
)
|
|
||||||
match = self.LINK_REGEX.search(email.html_body)
|
|
||||||
if match:
|
if match:
|
||||||
download_url = match.group("url")
|
download_url = match.group("url")
|
||||||
album_id = self._download_file(
|
album_id = self._download_file(
|
||||||
download_url, self.options.format
|
download_url, self.options.format
|
||||||
)
|
)
|
||||||
self.mail_album_data.pop(album_id)
|
self.mail_album_data.pop(album_id)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"Could not find download URL in body: {content}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BCFreeDownloaderConfig:
|
class BCFreeDownloaderConfig:
|
||||||
|
|||||||
Generated
+15
-129
@@ -1,41 +1,5 @@
|
|||||||
# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "1secmail"
|
|
||||||
version = "1.2.0"
|
|
||||||
description = "📧 Simple and intuitive, yet full featured API wrapper for www.1secmail.com, supporting both synchronous and asynchronous operations."
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "1secMail-1.2.0-py3-none-any.whl", hash = "sha256:9e2e01fd8ed95abab90de2ec5dbf8f6e0687398ba90583d01b0d39c322aa008a"},
|
|
||||||
{file = "1secMail-1.2.0.tar.gz", hash = "sha256:169955e6239aa2b3e039388dae0a4f6d26ef4f0a86806562c0b21be79d78450c"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
httpx = ">=0.17.1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anyio"
|
|
||||||
version = "4.4.0"
|
|
||||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
|
|
||||||
{file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
|
|
||||||
idna = ">=2.8"
|
|
||||||
sniffio = ">=1.1"
|
|
||||||
typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
|
|
||||||
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
|
|
||||||
trio = ["trio (>=0.23)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beautifulsoup4"
|
name = "beautifulsoup4"
|
||||||
version = "4.12.3"
|
version = "4.12.3"
|
||||||
@@ -189,76 +153,6 @@ files = [
|
|||||||
{file = "docopt_ng-0.9.0.tar.gz", hash = "sha256:91c6da10b5bb6f2e9e25345829fb8278c78af019f6fc40887ad49b060483b1d7"},
|
{file = "docopt_ng-0.9.0.tar.gz", hash = "sha256:91c6da10b5bb6f2e9e25345829fb8278c78af019f6fc40887ad49b060483b1d7"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "exceptiongroup"
|
|
||||||
version = "1.2.2"
|
|
||||||
description = "Backport of PEP 654 (exception groups)"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
|
||||||
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
test = ["pytest (>=6)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "h11"
|
|
||||||
version = "0.14.0"
|
|
||||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
|
||||||
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httpcore"
|
|
||||||
version = "1.0.5"
|
|
||||||
description = "A minimal low-level HTTP client."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
|
|
||||||
{file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
certifi = "*"
|
|
||||||
h11 = ">=0.13,<0.15"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
asyncio = ["anyio (>=4.0,<5.0)"]
|
|
||||||
http2 = ["h2 (>=3,<5)"]
|
|
||||||
socks = ["socksio (==1.*)"]
|
|
||||||
trio = ["trio (>=0.22.0,<0.26.0)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httpx"
|
|
||||||
version = "0.27.0"
|
|
||||||
description = "The next generation HTTP client."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
|
|
||||||
{file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
anyio = "*"
|
|
||||||
certifi = "*"
|
|
||||||
httpcore = "==1.*"
|
|
||||||
idna = "*"
|
|
||||||
sniffio = "*"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
brotli = ["brotli", "brotlicffi"]
|
|
||||||
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
|
||||||
http2 = ["h2 (>=3,<5)"]
|
|
||||||
socks = ["socksio (==1.*)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.8"
|
version = "3.8"
|
||||||
@@ -309,6 +203,20 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pyparsing = ">=3.0.7,<3.1.0"
|
pyparsing = ">=3.0.7,<3.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-guerrillamail"
|
||||||
|
version = "0.2.0"
|
||||||
|
description = "Client for the Guerrillamail temporary email server"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "python-guerrillamail-0.2.0.tar.gz", hash = "sha256:f04c48c58ddc55f81c2fe63c43e5f8d806ea77695fc621fe8bdbc11413bf9e31"},
|
||||||
|
{file = "python_guerrillamail-0.2.0-py2.py3-none-any.whl", hash = "sha256:993934d09cdba4c0f8a70b00eedf3563acabdec3e24bb82335121a0e022afff5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
requests = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.3"
|
version = "2.32.3"
|
||||||
@@ -330,17 +238,6 @@ urllib3 = ">=1.21.1,<3"
|
|||||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sniffio"
|
|
||||||
version = "1.3.1"
|
|
||||||
description = "Sniff out which async library your code is running under"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
|
|
||||||
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "soupsieve"
|
name = "soupsieve"
|
||||||
version = "2.6"
|
version = "2.6"
|
||||||
@@ -372,17 +269,6 @@ notebook = ["ipywidgets (>=6)"]
|
|||||||
slack = ["slack-sdk"]
|
slack = ["slack-sdk"]
|
||||||
telegram = ["requests"]
|
telegram = ["requests"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typing-extensions"
|
|
||||||
version = "4.12.2"
|
|
||||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
|
||||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
@@ -403,4 +289,4 @@ zstd = ["zstandard (>=0.18.0)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "b167eea04fb32fb22688a58b4a4cab72d2ad7bd468f82b19fb06bb5c20271dd9"
|
content-hash = "85da27e91df1a1842bad13ae922fb9d9f63d82795c8665a3aeb91a7be43a0d8c"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "free-bandcamp-downloader"
|
name = "free-bandcamp-downloader"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
description = "Download free and name-your-price albums from Bandcamp in lossless quality"
|
description = "Download free and name-your-price albums from Bandcamp in lossless quality"
|
||||||
authors = ["7x11x13 <x7x11x13@gmail.com>"]
|
authors = ["7x11x13 <x7x11x13@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -16,8 +16,8 @@ beautifulsoup4 = "^4.12.2"
|
|||||||
tqdm = "^4.66.1"
|
tqdm = "^4.66.1"
|
||||||
pyrfc6266 = "^1.0.2"
|
pyrfc6266 = "^1.0.2"
|
||||||
mutagen = "^1.47.0"
|
mutagen = "^1.47.0"
|
||||||
1secmail = "^1.1.0"
|
|
||||||
docopt-ng = "^0.9.0"
|
docopt-ng = "^0.9.0"
|
||||||
|
python-guerrillamail = "^0.2.0"
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|||||||
Reference in New Issue
Block a user