mirror of
https://github.com/glomatico/gamdl.git
synced 2026-06-13 04:05:14 +03:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0c5335767 | |||
| 69c2a8a063 | |||
| fb143ad1b4 | |||
| b66c06a9cb | |||
| a9e75384f0 | |||
| d88dbe5bb6 | |||
| 8398d9c65f | |||
| c6bce4b2c1 | |||
| f54ab12408 | |||
| 817479d807 |
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "3.7.1"
|
||||
__version__ = "3.7.4"
|
||||
|
||||
@@ -93,7 +93,7 @@ class AppleMusicApi:
|
||||
)
|
||||
|
||||
index_js_uri_match = re.search(
|
||||
r"/(assets/index-legacy[~-][^/\"]+\.js)",
|
||||
r"/(assets/index[~-][^/\"]+\.js)",
|
||||
home_page,
|
||||
)
|
||||
if not index_js_uri_match:
|
||||
@@ -116,7 +116,7 @@ class AppleMusicApi:
|
||||
status_code=response.status_code if response is not None else None,
|
||||
)
|
||||
|
||||
token_match = re.search('(?=eyJh)(.*?)(?=")', index_js_page)
|
||||
token_match = re.search(r'"(eyJ[A-Za-z0-9\-_]+\.eyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+)"', index_js_page)
|
||||
if not token_match:
|
||||
raise GamdlApiResponseError("Error finding token in index.js page")
|
||||
token = token_match.group(1)
|
||||
|
||||
+15
-3
@@ -1,3 +1,6 @@
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from ..utils import GamdlError
|
||||
|
||||
|
||||
@@ -9,7 +12,7 @@ class GamdlApiResponseError(GamdlApiError):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
content: str | None = None,
|
||||
content: Any | None = None,
|
||||
status_code: int | None = None,
|
||||
):
|
||||
self.message = message
|
||||
@@ -19,7 +22,16 @@ class GamdlApiResponseError(GamdlApiError):
|
||||
if status_code is not None:
|
||||
message = f"{message} (Status code: {status_code})"
|
||||
|
||||
if content:
|
||||
message += f": {content}"
|
||||
if content is not None:
|
||||
if isinstance(content, str):
|
||||
content_text = content
|
||||
else:
|
||||
try:
|
||||
content_text = json.dumps(content)
|
||||
except TypeError:
|
||||
content_text = str(content)
|
||||
|
||||
if content_text:
|
||||
message += f": {content_text}"
|
||||
|
||||
super().__init__(message)
|
||||
|
||||
@@ -205,8 +205,8 @@ class AppleMusicBaseInterface:
|
||||
async def get_cover_bytes(self, cover_url: str) -> bytes | None:
|
||||
log = logger.bind(action="get_cover_bytes", cover_url=cover_url)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(cover_url)
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.get(cover_url, follow_redirects=True)
|
||||
|
||||
if response.status_code == 404:
|
||||
log.debug("cover_not_found")
|
||||
|
||||
@@ -127,7 +127,8 @@ class AppleMusicMusicVideoInterface:
|
||||
genre_id=int(itunes_page_metadata["genres"][0]["genreId"]),
|
||||
media_type=MediaType.MUSIC_VIDEO,
|
||||
storefront=self.base.itunes_api.storefront_id,
|
||||
title=lookup_metadata[0]["trackCensoredName"],
|
||||
title=lookup_metadata[0]["trackName"],
|
||||
title_sort=lookup_metadata[0]["trackCensoredName"],
|
||||
title_id=int(metadata["id"]),
|
||||
rating=rating,
|
||||
)
|
||||
@@ -139,7 +140,8 @@ class AppleMusicMusicVideoInterface:
|
||||
if not album:
|
||||
return tags
|
||||
|
||||
tags.album = lookup_metadata[1]["collectionCensoredName"]
|
||||
tags.album = lookup_metadata[1]["collectionName"]
|
||||
tags.album_sort = lookup_metadata[1]["collectionCensoredName"]
|
||||
tags.album_artist = lookup_metadata[1]["artistName"]
|
||||
tags.album_id = int(itunes_page_metadata["collectionId"])
|
||||
tags.disc = lookup_metadata[0]["discNumber"]
|
||||
@@ -441,7 +443,7 @@ class AppleMusicMusicVideoInterface:
|
||||
)
|
||||
)["data"][0]
|
||||
|
||||
if media.media_metadata["attributes"]["playParams"].get("isLibrary"):
|
||||
if media.media_metadata["attributes"].get("playParams", {}).get("isLibrary"):
|
||||
catalog_metadata = self.base.get_catalog_metadata_from_library(
|
||||
media.media_metadata
|
||||
)
|
||||
|
||||
+29
-12
@@ -191,18 +191,24 @@ class AppleMusicSongInterface:
|
||||
|
||||
return f"[{timestamp.strftime('%M:%S.%f')[:-4]}]{text}"
|
||||
|
||||
def _get_m3u8_from_playback(self, playback: dict) -> str | None:
|
||||
return playback["songList"][0].get("hls-playlist-url")
|
||||
def _switch_m3u8_master_url_to_default(self, m3u8_master_url: str) -> str:
|
||||
return re.sub(
|
||||
r"(P\d+)_[^/]+(\.m3u8)",
|
||||
r"\1_default\2",
|
||||
m3u8_master_url,
|
||||
)
|
||||
|
||||
async def get_m3u8_master_url(
|
||||
self,
|
||||
playback: dict | None,
|
||||
song_metadata: dict | None,
|
||||
) -> str | None:
|
||||
if playback:
|
||||
return self._get_m3u8_from_playback(playback)
|
||||
else:
|
||||
return await self._get_m3u8_master_url_from_metadata(song_metadata)
|
||||
def _get_m3u8_from_playback(self, playback: dict) -> str | None:
|
||||
log = logger.bind(action="get_m3u8_master_url_from_playback")
|
||||
|
||||
m3u8_master_url = playback["songList"][0].get("hls-playlist-url")
|
||||
|
||||
if m3u8_master_url:
|
||||
m3u8_master_url = self._switch_m3u8_master_url_to_default(m3u8_master_url)
|
||||
log.debug("success", m3u8_master_url=m3u8_master_url)
|
||||
return m3u8_master_url
|
||||
|
||||
log.debug("no_m3u8_master_url")
|
||||
|
||||
async def _get_m3u8_master_url_from_metadata(
|
||||
self,
|
||||
@@ -227,6 +233,7 @@ class AppleMusicSongInterface:
|
||||
enhanced = song_metadata["attributes"]["extendedAssetUrls"].get("enhancedHls")
|
||||
|
||||
if enhanced:
|
||||
enhanced = self._switch_m3u8_master_url_to_default(enhanced)
|
||||
log.debug("success", m3u8_master_url=enhanced)
|
||||
return enhanced
|
||||
|
||||
@@ -234,6 +241,16 @@ class AppleMusicSongInterface:
|
||||
|
||||
return None
|
||||
|
||||
async def get_m3u8_master_url(
|
||||
self,
|
||||
playback: dict | None,
|
||||
song_metadata: dict | None,
|
||||
) -> str | None:
|
||||
if playback:
|
||||
return self._get_m3u8_from_playback(playback)
|
||||
else:
|
||||
return await self._get_m3u8_master_url_from_metadata(song_metadata)
|
||||
|
||||
async def get_stream_info(
|
||||
self,
|
||||
media_id: str,
|
||||
@@ -506,7 +523,7 @@ class AppleMusicSongInterface:
|
||||
)
|
||||
)["data"][0]
|
||||
|
||||
if media.media_metadata["attributes"]["playParams"].get("isLibrary"):
|
||||
if media.media_metadata["attributes"].get("playParams", {}).get("isLibrary"):
|
||||
catalog_metadata = self.base.get_catalog_metadata_from_library(
|
||||
media.media_metadata
|
||||
)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "gamdl"
|
||||
version = "3.7.1"
|
||||
version = "3.7.4"
|
||||
description = "A command-line app for downloading Apple Music songs, music videos and post videos."
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
Reference in New Issue
Block a user