mirror of
https://github.com/glomatico/gamdl.git
synced 2026-06-13 12:15:18 +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(
|
index_js_uri_match = re.search(
|
||||||
r"/(assets/index-legacy[~-][^/\"]+\.js)",
|
r"/(assets/index[~-][^/\"]+\.js)",
|
||||||
home_page,
|
home_page,
|
||||||
)
|
)
|
||||||
if not index_js_uri_match:
|
if not index_js_uri_match:
|
||||||
@@ -116,7 +116,7 @@ class AppleMusicApi:
|
|||||||
status_code=response.status_code if response is not None else None,
|
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:
|
if not token_match:
|
||||||
raise GamdlApiResponseError("Error finding token in index.js page")
|
raise GamdlApiResponseError("Error finding token in index.js page")
|
||||||
token = token_match.group(1)
|
token = token_match.group(1)
|
||||||
|
|||||||
+15
-3
@@ -1,3 +1,6 @@
|
|||||||
|
import json
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from ..utils import GamdlError
|
from ..utils import GamdlError
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +12,7 @@ class GamdlApiResponseError(GamdlApiError):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
message: str,
|
message: str,
|
||||||
content: str | None = None,
|
content: Any | None = None,
|
||||||
status_code: int | None = None,
|
status_code: int | None = None,
|
||||||
):
|
):
|
||||||
self.message = message
|
self.message = message
|
||||||
@@ -19,7 +22,16 @@ class GamdlApiResponseError(GamdlApiError):
|
|||||||
if status_code is not None:
|
if status_code is not None:
|
||||||
message = f"{message} (Status code: {status_code})"
|
message = f"{message} (Status code: {status_code})"
|
||||||
|
|
||||||
if content:
|
if content is not None:
|
||||||
message += f": {content}"
|
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)
|
super().__init__(message)
|
||||||
|
|||||||
@@ -205,8 +205,8 @@ class AppleMusicBaseInterface:
|
|||||||
async def get_cover_bytes(self, cover_url: str) -> bytes | None:
|
async def get_cover_bytes(self, cover_url: str) -> bytes | None:
|
||||||
log = logger.bind(action="get_cover_bytes", cover_url=cover_url)
|
log = logger.bind(action="get_cover_bytes", cover_url=cover_url)
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
response = await client.get(cover_url)
|
response = await client.get(cover_url, follow_redirects=True)
|
||||||
|
|
||||||
if response.status_code == 404:
|
if response.status_code == 404:
|
||||||
log.debug("cover_not_found")
|
log.debug("cover_not_found")
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ class AppleMusicMusicVideoInterface:
|
|||||||
genre_id=int(itunes_page_metadata["genres"][0]["genreId"]),
|
genre_id=int(itunes_page_metadata["genres"][0]["genreId"]),
|
||||||
media_type=MediaType.MUSIC_VIDEO,
|
media_type=MediaType.MUSIC_VIDEO,
|
||||||
storefront=self.base.itunes_api.storefront_id,
|
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"]),
|
title_id=int(metadata["id"]),
|
||||||
rating=rating,
|
rating=rating,
|
||||||
)
|
)
|
||||||
@@ -139,7 +140,8 @@ class AppleMusicMusicVideoInterface:
|
|||||||
if not album:
|
if not album:
|
||||||
return tags
|
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_artist = lookup_metadata[1]["artistName"]
|
||||||
tags.album_id = int(itunes_page_metadata["collectionId"])
|
tags.album_id = int(itunes_page_metadata["collectionId"])
|
||||||
tags.disc = lookup_metadata[0]["discNumber"]
|
tags.disc = lookup_metadata[0]["discNumber"]
|
||||||
@@ -441,7 +443,7 @@ class AppleMusicMusicVideoInterface:
|
|||||||
)
|
)
|
||||||
)["data"][0]
|
)["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(
|
catalog_metadata = self.base.get_catalog_metadata_from_library(
|
||||||
media.media_metadata
|
media.media_metadata
|
||||||
)
|
)
|
||||||
|
|||||||
+29
-12
@@ -191,18 +191,24 @@ class AppleMusicSongInterface:
|
|||||||
|
|
||||||
return f"[{timestamp.strftime('%M:%S.%f')[:-4]}]{text}"
|
return f"[{timestamp.strftime('%M:%S.%f')[:-4]}]{text}"
|
||||||
|
|
||||||
def _get_m3u8_from_playback(self, playback: dict) -> str | None:
|
def _switch_m3u8_master_url_to_default(self, m3u8_master_url: str) -> str:
|
||||||
return playback["songList"][0].get("hls-playlist-url")
|
return re.sub(
|
||||||
|
r"(P\d+)_[^/]+(\.m3u8)",
|
||||||
|
r"\1_default\2",
|
||||||
|
m3u8_master_url,
|
||||||
|
)
|
||||||
|
|
||||||
async def get_m3u8_master_url(
|
def _get_m3u8_from_playback(self, playback: dict) -> str | None:
|
||||||
self,
|
log = logger.bind(action="get_m3u8_master_url_from_playback")
|
||||||
playback: dict | None,
|
|
||||||
song_metadata: dict | None,
|
m3u8_master_url = playback["songList"][0].get("hls-playlist-url")
|
||||||
) -> str | None:
|
|
||||||
if playback:
|
if m3u8_master_url:
|
||||||
return self._get_m3u8_from_playback(playback)
|
m3u8_master_url = self._switch_m3u8_master_url_to_default(m3u8_master_url)
|
||||||
else:
|
log.debug("success", m3u8_master_url=m3u8_master_url)
|
||||||
return await self._get_m3u8_master_url_from_metadata(song_metadata)
|
return m3u8_master_url
|
||||||
|
|
||||||
|
log.debug("no_m3u8_master_url")
|
||||||
|
|
||||||
async def _get_m3u8_master_url_from_metadata(
|
async def _get_m3u8_master_url_from_metadata(
|
||||||
self,
|
self,
|
||||||
@@ -227,6 +233,7 @@ class AppleMusicSongInterface:
|
|||||||
enhanced = song_metadata["attributes"]["extendedAssetUrls"].get("enhancedHls")
|
enhanced = song_metadata["attributes"]["extendedAssetUrls"].get("enhancedHls")
|
||||||
|
|
||||||
if enhanced:
|
if enhanced:
|
||||||
|
enhanced = self._switch_m3u8_master_url_to_default(enhanced)
|
||||||
log.debug("success", m3u8_master_url=enhanced)
|
log.debug("success", m3u8_master_url=enhanced)
|
||||||
return enhanced
|
return enhanced
|
||||||
|
|
||||||
@@ -234,6 +241,16 @@ class AppleMusicSongInterface:
|
|||||||
|
|
||||||
return None
|
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(
|
async def get_stream_info(
|
||||||
self,
|
self,
|
||||||
media_id: str,
|
media_id: str,
|
||||||
@@ -506,7 +523,7 @@ class AppleMusicSongInterface:
|
|||||||
)
|
)
|
||||||
)["data"][0]
|
)["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(
|
catalog_metadata = self.base.get_catalog_metadata_from_library(
|
||||||
media.media_metadata
|
media.media_metadata
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "gamdl"
|
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."
|
description = "A command-line app for downloading Apple Music songs, music videos and post videos."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
Reference in New Issue
Block a user