Use guerrilla mail
This commit is contained in:
@@ -59,15 +59,15 @@ from configparser import ConfigParser
|
||||
from dataclasses import dataclass
|
||||
from http.cookiejar import MozillaCookieJar
|
||||
from typing import Dict, Optional, Set
|
||||
from urllib.parse import urljoin, urlsplit
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import mutagen
|
||||
import pyrfc6266
|
||||
import requests
|
||||
import secmail
|
||||
from bs4 import BeautifulSoup
|
||||
from docopt import docopt
|
||||
from tqdm import tqdm
|
||||
from guerrillamail import GuerrillaMailSession
|
||||
|
||||
from free_bandcamp_downloader import __version__, logger
|
||||
from free_bandcamp_downloader.bandcamp_http_adapter import BandcampHTTPAdapter
|
||||
@@ -75,20 +75,20 @@ from free_bandcamp_downloader.bandcamp_http_adapter import BandcampHTTPAdapter
|
||||
|
||||
@dataclass
|
||||
class BCFreeDownloaderOptions:
|
||||
country: str = None
|
||||
zipcode: str = None
|
||||
email: str = None
|
||||
format: str = None
|
||||
dir: str = None
|
||||
country: str = "United States"
|
||||
zipcode: str = "00000"
|
||||
email: str = "auto"
|
||||
format: str = "FLAC"
|
||||
dir: str = "."
|
||||
|
||||
|
||||
@dataclass
|
||||
class BCFreeDownloaderAlbumData:
|
||||
about: str = None
|
||||
credits: str = None
|
||||
tags: str = None
|
||||
id: str = None
|
||||
title: str = None
|
||||
about: Optional[str]
|
||||
credits: Optional[str]
|
||||
tags: Optional[str]
|
||||
id: str
|
||||
title: Optional[str]
|
||||
|
||||
|
||||
class BCFreeDownloadError(Exception):
|
||||
@@ -126,13 +126,12 @@ class BCFreeDownloader:
|
||||
self.mail_session = None
|
||||
self.mail_album_data: Dict[str, BCFreeDownloaderAlbumData] = {}
|
||||
self.unzip = unzip
|
||||
self.session = None
|
||||
self._init_downloaded()
|
||||
self._init_session(cookies_file, identity)
|
||||
|
||||
def _init_email(self):
|
||||
self.mail_session = secmail.Client(self.config_dir)
|
||||
self.options.email = self.mail_session.random_email(1, "1secmail.com")[0]
|
||||
self.mail_session = GuerrillaMailSession()
|
||||
self.options.email = self.mail_session.get_session_state()["email_address"]
|
||||
|
||||
def _init_downloaded(self):
|
||||
if self.download_history_file:
|
||||
@@ -240,7 +239,6 @@ class BCFreeDownloader:
|
||||
|
||||
@staticmethod
|
||||
def _get_album_data_from_soup(soup: BeautifulSoup) -> BCFreeDownloaderAlbumData:
|
||||
album_data = BCFreeDownloaderAlbumData()
|
||||
about = soup.find("div", class_="tralbum-about")
|
||||
credits = soup.find("div", class_="tralbum-credits")
|
||||
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']}"
|
||||
|
||||
album_data.about = about.get_text("\n") if about else None
|
||||
album_data.credits = credits.get_text("\n") if credits else None
|
||||
album_data.tags = ",".join(sorted(tags))
|
||||
album_data.id = id
|
||||
|
||||
return album_data
|
||||
return BCFreeDownloaderAlbumData(
|
||||
about=about.get_text("\n") if about else None,
|
||||
credits=credits.get_text("\n") if credits else None,
|
||||
tags=",".join(sorted(tags)),
|
||||
id=id,
|
||||
title=None,
|
||||
)
|
||||
|
||||
def _download_purchased_album(
|
||||
self, user_id: int, album_data: BCFreeDownloaderAlbumData
|
||||
@@ -330,7 +329,6 @@ class BCFreeDownloader:
|
||||
|
||||
if head_data["offers"]["price"] == 0.0:
|
||||
if tralbum_data["current"]["require_email"]:
|
||||
raise BCFreeDownloadError(f"{url} requires email. Skipping...")
|
||||
if not self.options.email or self.options.email == "auto":
|
||||
self._init_email()
|
||||
logger.info(f"{url} requires email")
|
||||
@@ -414,7 +412,7 @@ class BCFreeDownloader:
|
||||
|
||||
try:
|
||||
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.")
|
||||
|
||||
if url_type == "album" or url_type == "song":
|
||||
@@ -429,24 +427,27 @@ class BCFreeDownloader:
|
||||
while (expected_emails := len(self.mail_album_data)) > 0:
|
||||
logger.info(f"Waiting for {expected_emails} emails from Bandcamp...")
|
||||
time.sleep(5)
|
||||
for email in self.mail_session.get_inbox(self.options.email):
|
||||
if email.id not in checked_ids:
|
||||
checked_ids.add(email.id)
|
||||
for email in self.mail_session.get_email_list():
|
||||
email_id = email.guid
|
||||
if email_id not in checked_ids:
|
||||
checked_ids.add(email_id)
|
||||
if (
|
||||
email.from_address.endswith("@email.bandcamp.com")
|
||||
email.sender == "noreply@bandcamp.com"
|
||||
and "download" in email.subject
|
||||
):
|
||||
logger.info(f'Received email "{email.subject}"')
|
||||
email = self.mail_session.get_message(
|
||||
self.options.email, email.id
|
||||
)
|
||||
match = self.LINK_REGEX.search(email.html_body)
|
||||
content = self.mail_session.get_email(email_id).body
|
||||
match = self.LINK_REGEX.search(content)
|
||||
if match:
|
||||
download_url = match.group("url")
|
||||
album_id = self._download_file(
|
||||
download_url, self.options.format
|
||||
)
|
||||
self.mail_album_data.pop(album_id)
|
||||
else:
|
||||
logger.error(
|
||||
f"Could not find download URL in body: {content}"
|
||||
)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
[[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]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.12.3"
|
||||
@@ -189,76 +153,6 @@ files = [
|
||||
{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]]
|
||||
name = "idna"
|
||||
version = "3.8"
|
||||
@@ -309,6 +203,20 @@ files = [
|
||||
[package.dependencies]
|
||||
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]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
@@ -330,17 +238,6 @@ urllib3 = ">=1.21.1,<3"
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
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]]
|
||||
name = "soupsieve"
|
||||
version = "2.6"
|
||||
@@ -372,17 +269,6 @@ notebook = ["ipywidgets (>=6)"]
|
||||
slack = ["slack-sdk"]
|
||||
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]]
|
||||
name = "urllib3"
|
||||
version = "2.2.2"
|
||||
@@ -403,4 +289,4 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "b167eea04fb32fb22688a58b4a4cab72d2ad7bd468f82b19fb06bb5c20271dd9"
|
||||
content-hash = "85da27e91df1a1842bad13ae922fb9d9f63d82795c8665a3aeb91a7be43a0d8c"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
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"
|
||||
authors = ["7x11x13 <x7x11x13@gmail.com>"]
|
||||
license = "MIT"
|
||||
@@ -16,8 +16,8 @@ beautifulsoup4 = "^4.12.2"
|
||||
tqdm = "^4.66.1"
|
||||
pyrfc6266 = "^1.0.2"
|
||||
mutagen = "^1.47.0"
|
||||
1secmail = "^1.1.0"
|
||||
docopt-ng = "^0.9.0"
|
||||
python-guerrillamail = "^0.2.0"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
Reference in New Issue
Block a user