Compare commits

...

8 Commits

Author SHA1 Message Date
Rafael Moraes 2001b19d8f bump version 2025-02-23 03:50:23 -03:00
Rafael Moraes 14814dd2da small refactor on cookies user input 2025-02-23 03:16:03 -03:00
Rafael Moraes 6fad41467f add termcolor to dependencies 2025-02-23 03:14:31 -03:00
Rafael Moraes 0868f1c28c set timezone to utc on parse_datetime_obj_from_timestamp_ttml 2025-02-23 03:12:29 -03:00
Rafael Moraes a964011507 implement custom logging formatter with colored output 2025-02-23 03:11:23 -03:00
Rafael Moraes 3a943d0154 refactor media user token retrieval from cookies 2025-02-23 02:13:09 -03:00
Rafael Moraes 84bf0a3c2b prompt user for cookies file path if not found 2025-02-23 02:12:15 -03:00
Rafael Moraes 93dda6889c update Python version requirement to 3.9 2025-02-16 10:43:05 -03:00
8 changed files with 50 additions and 15 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ A Python CLI app for downloading Apple Music songs, music videos and post videos
* **Highly Customizable**: Extensive configuration options for advanced users.
## Prerequisites
* **Python 3.8 or higher** installed on your system.
* **Python 3.9 or higher** installed on your system.
* The **cookies file** of your Apple Music browser session in Netscape format (requires an active subscription).
* **Firefox**: Use the [Export Cookies](https://addons.mozilla.org/addon/export-cookies-txt) extension.
* **Chromium-based Browsers**: Use the [Open Cookies.txt](https://chromewebstore.google.com/detail/open-cookiestxt/gdocmgbfkjnnpapoeobnolbbkoibbcif) extension.
+1 -1
View File
@@ -1 +1 @@
__version__ = "2.3.9"
__version__ = "2.4"
+4 -3
View File
@@ -37,6 +37,9 @@ class AppleMusicApi:
cookies.load(ignore_discard=True, ignore_expires=True)
self.session.cookies.update(cookies)
self.storefront = self.session.cookies.get_dict()["itua"]
media_user_token = self.session.cookies.get_dict()["media-user-token"]
else:
media_user_token = ""
self.session.headers.update(
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0",
@@ -44,9 +47,7 @@ class AppleMusicApi:
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"content-type": "application/json",
"Media-User-Token": self.session.cookies.get_dict().get(
"media-user-token", ""
),
"Media-User-Token": media_user_token,
"x-apple-renewal": "true",
"DNT": "1",
"Connection": "keep-alive",
+18 -9
View File
@@ -7,10 +7,12 @@ from enum import Enum
from pathlib import Path
import click
from termcolor import colored
from . import __version__
from .apple_music_api import AppleMusicApi
from .constants import *
from .custom_formatter import CustomFormatter
from .downloader import Downloader
from .downloader_music_video import DownloaderMusicVideo
from .downloader_post import DownloaderPost
@@ -348,16 +350,20 @@ def main(
quality_post: PostQuality,
no_config_file: bool,
):
logging.basicConfig(
format="[%(levelname)-8s %(asctime)s] %(message)s",
datefmt="%H:%M:%S",
)
logger = logging.getLogger(__name__)
logger.setLevel(log_level)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(CustomFormatter())
logger.addHandler(stream_handler)
logger.info("Starting Gamdl")
if not cookies_path.exists():
logger.critical(X_NOT_FOUND_STRING.format("Cookies file", cookies_path))
return
while not cookies_path.exists():
cookies_path_str = click.prompt(
X_NOT_FOUND_STRING.format("Cookies file", cookies_path.absolute())
+ ". Move it to that location or drag and drop it here. Then, press enter to continue",
default=str(cookies_path),
show_default=False,
)
cookies_path = Path(cookies_path_str.strip('"'))
apple_music_api = AppleMusicApi(
cookies_path,
language=language,
@@ -460,7 +466,7 @@ def main(
_urls.extend(Path(url).read_text(encoding="utf-8").splitlines())
urls = _urls
for url_index, url in enumerate(urls, start=1):
url_progress = f"URL {url_index}/{len(urls)}"
url_progress = colored(f"URL {url_index}/{len(urls)}", "grey")
try:
logger.info(f'({url_progress}) Checking "{url}"')
url_info = downloader.get_url_info(url)
@@ -476,7 +482,10 @@ def main(
for download_index, track_metadata in enumerate(
download_queue_tracks_metadata, start=1
):
queue_progress = f"Track {download_index}/{len(download_queue_tracks_metadata)} from URL {url_index}/{len(urls)}"
queue_progress = colored(
f"Track {download_index}/{len(download_queue_tracks_metadata)} from URL {url_index}/{len(urls)}",
"grey",
)
try:
remuxed_path = None
if download_queue.playlist_attributes:
+20
View File
@@ -0,0 +1,20 @@
import logging
from termcolor import colored
class CustomFormatter(logging.Formatter):
basic_format = "[%(levelname)-8s %(asctime)s]"
formats = {
logging.DEBUG: colored(basic_format, "grey"),
logging.INFO: colored(basic_format, "green"),
logging.WARNING: colored(basic_format, "yellow"),
logging.ERROR: colored(basic_format, "red"),
logging.CRITICAL: colored(basic_format, "red"),
}
date_format = "%H:%M:%S"
def format(self, record: logging.LogRecord) -> str:
return logging.Formatter(
self.formats.get(record.levelno) + " %(message)s",
datefmt=self.date_format,
).format(record)
+4 -1
View File
@@ -176,7 +176,10 @@ class DownloaderSong:
secs = float(f"{mins_secs_ms[-2]}.{mins_secs_ms[-1]}")
if len(mins_secs_ms) > 2:
mins = int(mins_secs_ms[-3])
return datetime.datetime.fromtimestamp((mins * 60) + secs + (ms / 1000))
return datetime.datetime.fromtimestamp(
(mins * 60) + secs + (ms / 1000),
tz=datetime.timezone.utc,
)
def get_lyrics_synced_timestamp_lrc(self, timestamp_ttml: str) -> str:
datetime_obj = self.parse_datetime_obj_from_timestamp_ttml(timestamp_ttml)
+1
View File
@@ -11,6 +11,7 @@ dependencies = [
"pillow",
"pywidevine",
"pyyaml",
"termcolor",
"yt-dlp",
]
readme = "README.md"
+1
View File
@@ -5,4 +5,5 @@ mutagen
pillow
pywidevine
pyyaml
termcolor
yt-dlp