refactor: move some impl-specific constants out from config.py
This commit is contained in:
@@ -12,12 +12,9 @@ from loguru import logger
|
|||||||
|
|
||||||
from .base import BaseAuthenticator
|
from .base import BaseAuthenticator
|
||||||
from ..cache import CacheEngine
|
from ..cache import CacheEngine
|
||||||
from ..config import (
|
from ..config import HTTP_TIMEOUT, MUSIXMATCH_COOLDOWN_MS, credentials
|
||||||
HTTP_TIMEOUT,
|
|
||||||
MUSIXMATCH_TOKEN_URL,
|
_MUSIXMATCH_TOKEN_URL = "https://apic-desktop.musixmatch.com/ws/1.1/token.get"
|
||||||
MUSIXMATCH_COOLDOWN_MS,
|
|
||||||
credentials,
|
|
||||||
)
|
|
||||||
|
|
||||||
_MXM_HEADERS = {"Cookie": "x-mxm-token-guid="}
|
_MXM_HEADERS = {"Cookie": "x-mxm-token-guid="}
|
||||||
_MXM_BASE_PARAMS = {
|
_MXM_BASE_PARAMS = {
|
||||||
@@ -61,7 +58,7 @@ class MusixmatchAuthenticator(BaseAuthenticator):
|
|||||||
{"until_ms": until_ms},
|
{"until_ms": until_ms},
|
||||||
expires_at_ms=until_ms,
|
expires_at_ms=until_ms,
|
||||||
)
|
)
|
||||||
logger.warning("Musixmatch: token unavailable, entering cooldown for 1 hour")
|
logger.warning("Musixmatch: token unavailable, entering cooldown")
|
||||||
|
|
||||||
def _invalidate_token(self) -> None:
|
def _invalidate_token(self) -> None:
|
||||||
"""Discard the current token from memory and DB."""
|
"""Discard the current token from memory and DB."""
|
||||||
@@ -76,7 +73,7 @@ class MusixmatchAuthenticator(BaseAuthenticator):
|
|||||||
"user_language": "en",
|
"user_language": "en",
|
||||||
"t": str(int(time.time() * 1000)),
|
"t": str(int(time.time() * 1000)),
|
||||||
}
|
}
|
||||||
url = f"{MUSIXMATCH_TOKEN_URL}?{urlencode(params)}"
|
url = f"{_MUSIXMATCH_TOKEN_URL}?{urlencode(params)}"
|
||||||
logger.debug("Musixmatch: fetching anonymous token")
|
logger.debug("Musixmatch: fetching anonymous token")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ from loguru import logger
|
|||||||
|
|
||||||
from .base import BaseAuthenticator
|
from .base import BaseAuthenticator
|
||||||
from ..cache import CacheEngine
|
from ..cache import CacheEngine
|
||||||
from ..config import (
|
from ..config import HTTP_TIMEOUT, UA_BROWSER, credentials
|
||||||
HTTP_TIMEOUT,
|
|
||||||
SPOTIFY_SERVER_TIME_URL,
|
|
||||||
SPOTIFY_SECRET_URL,
|
|
||||||
SPOTIFY_TOKEN_URL,
|
|
||||||
UA_BROWSER,
|
|
||||||
credentials,
|
|
||||||
)
|
|
||||||
|
|
||||||
_SPOTIFY_BASE_HEADERS = {
|
_SPOTIFY_TOKEN_URL = "https://open.spotify.com/api/token"
|
||||||
|
_SPOTIFY_SERVER_TIME_URL = "https://open.spotify.com/api/server-time"
|
||||||
|
_SPOTIFY_SECRET_URL = (
|
||||||
|
"https://raw.githubusercontent.com/xyloflake/spot-secrets-go"
|
||||||
|
"/refs/heads/main/secrets/secrets.json"
|
||||||
|
)
|
||||||
|
SPOTIFY_BASE_HEADERS = {
|
||||||
|
"User-Agent": UA_BROWSER,
|
||||||
"Referer": "https://open.spotify.com/",
|
"Referer": "https://open.spotify.com/",
|
||||||
"Origin": "https://open.spotify.com",
|
"Origin": "https://open.spotify.com",
|
||||||
"App-Platform": "WebPlayer",
|
"App-Platform": "WebPlayer",
|
||||||
@@ -82,7 +82,7 @@ class SpotifyAuthenticator(BaseAuthenticator):
|
|||||||
|
|
||||||
async def _get_server_time(self, client: httpx.AsyncClient) -> Optional[int]:
|
async def _get_server_time(self, client: httpx.AsyncClient) -> Optional[int]:
|
||||||
try:
|
try:
|
||||||
res = await client.get(SPOTIFY_SERVER_TIME_URL, timeout=HTTP_TIMEOUT)
|
res = await client.get(_SPOTIFY_SERVER_TIME_URL, timeout=HTTP_TIMEOUT)
|
||||||
res.raise_for_status()
|
res.raise_for_status()
|
||||||
data = res.json()
|
data = res.json()
|
||||||
if not isinstance(data, dict) or "serverTime" not in data:
|
if not isinstance(data, dict) or "serverTime" not in data:
|
||||||
@@ -100,7 +100,7 @@ class SpotifyAuthenticator(BaseAuthenticator):
|
|||||||
logger.debug("Spotify: using cached TOTP secret")
|
logger.debug("Spotify: using cached TOTP secret")
|
||||||
return self._cached_secret
|
return self._cached_secret
|
||||||
try:
|
try:
|
||||||
res = await client.get(SPOTIFY_SECRET_URL, timeout=HTTP_TIMEOUT)
|
res = await client.get(_SPOTIFY_SECRET_URL, timeout=HTTP_TIMEOUT)
|
||||||
res.raise_for_status()
|
res.raise_for_status()
|
||||||
data = res.json()
|
data = res.json()
|
||||||
if not isinstance(data, list) or len(data) == 0:
|
if not isinstance(data, list) or len(data) == 0:
|
||||||
@@ -140,10 +140,9 @@ class SpotifyAuthenticator(BaseAuthenticator):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": UA_BROWSER,
|
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"Cookie": f"sp_dc={credentials.SPOTIFY_SP_DC}",
|
"Cookie": f"sp_dc={credentials.SPOTIFY_SP_DC}",
|
||||||
**_SPOTIFY_BASE_HEADERS,
|
**SPOTIFY_BASE_HEADERS,
|
||||||
}
|
}
|
||||||
|
|
||||||
async with httpx.AsyncClient(headers=headers) as client:
|
async with httpx.AsyncClient(headers=headers) as client:
|
||||||
@@ -169,7 +168,7 @@ class SpotifyAuthenticator(BaseAuthenticator):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
res = await client.get(
|
res = await client.get(
|
||||||
SPOTIFY_TOKEN_URL, params=params, timeout=HTTP_TIMEOUT
|
_SPOTIFY_TOKEN_URL, params=params, timeout=HTTP_TIMEOUT
|
||||||
)
|
)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
logger.error(f"Spotify: token request returned {res.status_code}")
|
logger.error(f"Spotify: token request returned {res.status_code}")
|
||||||
|
|||||||
+3
-31
@@ -57,34 +57,10 @@ MULTI_CANDIDATE_DELAY_S = 0.2 # delay between sequential lyric fetches
|
|||||||
LEGACY_CONFIDENCE_SYNCED = 50.0
|
LEGACY_CONFIDENCE_SYNCED = 50.0
|
||||||
LEGACY_CONFIDENCE_UNSYNCED = 40.0
|
LEGACY_CONFIDENCE_UNSYNCED = 40.0
|
||||||
|
|
||||||
# Spotify related
|
# User-Agents
|
||||||
SPOTIFY_TOKEN_URL = "https://open.spotify.com/api/token"
|
UA_BROWSER = "Mozilla/5.0 (X11; Linux x86_64; rv:149.0) Gecko/20100101 Firefox/149.0"
|
||||||
SPOTIFY_LYRICS_URL = "https://spclient.wg.spotify.com/color-lyrics/v2/track/"
|
UA_LRX = f"LRX-CLI {APP_VERSION} (https://github.com/Uyanide/lrx-cli)"
|
||||||
SPOTIFY_SERVER_TIME_URL = "https://open.spotify.com/api/server-time"
|
|
||||||
SPOTIFY_SECRET_URL = (
|
|
||||||
"https://raw.githubusercontent.com/xyloflake/spot-secrets-go"
|
|
||||||
"/refs/heads/main/secrets/secrets.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Netease api
|
|
||||||
NETEASE_SEARCH_URL = "https://music.163.com/api/cloudsearch/pc"
|
|
||||||
NETEASE_LYRIC_URL = "https://interface3.music.163.com/api/song/lyric"
|
|
||||||
|
|
||||||
# QQ api endpoints
|
|
||||||
QQ_MUSIC_API_SEARCH_ENDPOINT = "/api/search"
|
|
||||||
QQ_MUSIC_API_LYRIC_ENDPOINT = "/api/lyric"
|
|
||||||
|
|
||||||
# LRCLIB api
|
|
||||||
LRCLIB_API_URL = "https://lrclib.net/api/get"
|
|
||||||
LRCLIB_SEARCH_URL = "https://lrclib.net/api/search"
|
|
||||||
|
|
||||||
# Musixmatch desktop API
|
|
||||||
MUSIXMATCH_TOKEN_URL = "https://apic-desktop.musixmatch.com/ws/1.1/token.get"
|
|
||||||
MUSIXMATCH_SEARCH_URL = "https://apic-desktop.musixmatch.com/ws/1.1/track.search"
|
|
||||||
MUSIXMATCH_MACRO_URL = "https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get"
|
|
||||||
MUSIXMATCH_TRACK_MATCH_URL = (
|
|
||||||
"https://apic-desktop.musixmatch.com/ws/1.1/matcher.track.get"
|
|
||||||
)
|
|
||||||
MUSIXMATCH_COOLDOWN_MS = 600_000 # 10 minutes
|
MUSIXMATCH_COOLDOWN_MS = 600_000 # 10 minutes
|
||||||
|
|
||||||
# Player preference (used when multiple MPRIS players are active)
|
# Player preference (used when multiple MPRIS players are active)
|
||||||
@@ -114,10 +90,6 @@ class _Credentials:
|
|||||||
|
|
||||||
credentials = _Credentials()
|
credentials = _Credentials()
|
||||||
|
|
||||||
# User-Agents
|
|
||||||
UA_BROWSER = "Mozilla/5.0 (X11; Linux x86_64; rv:149.0) Gecko/20100101 Firefox/149.0"
|
|
||||||
UA_LRX = f"LRX-CLI {APP_VERSION} (https://github.com/Uyanide/lrx-cli)"
|
|
||||||
|
|
||||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||||
|
|
||||||
# Logger
|
# Logger
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ from loguru import logger
|
|||||||
from .base import BaseEnricher
|
from .base import BaseEnricher
|
||||||
from ..authenticators.musixmatch import MusixmatchAuthenticator
|
from ..authenticators.musixmatch import MusixmatchAuthenticator
|
||||||
from ..models import TrackMeta
|
from ..models import TrackMeta
|
||||||
from ..config import MUSIXMATCH_TRACK_MATCH_URL
|
|
||||||
|
_MUSIXMATCH_TRACK_MATCH_URL = (
|
||||||
|
"https://apic-desktop.musixmatch.com/ws/1.1/matcher.track.get"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MusixmatchSpotifyEnricher(BaseEnricher):
|
class MusixmatchSpotifyEnricher(BaseEnricher):
|
||||||
@@ -36,7 +39,7 @@ class MusixmatchSpotifyEnricher(BaseEnricher):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
data = await self.auth.get_json(
|
data = await self.auth.get_json(
|
||||||
MUSIXMATCH_TRACK_MATCH_URL,
|
_MUSIXMATCH_TRACK_MATCH_URL,
|
||||||
{"track_spotify_id": track.trackid},
|
{"track_spotify_id": track.trackid},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -21,10 +21,11 @@ from ..config import (
|
|||||||
TTL_UNSYNCED,
|
TTL_UNSYNCED,
|
||||||
TTL_NOT_FOUND,
|
TTL_NOT_FOUND,
|
||||||
TTL_NETWORK_ERROR,
|
TTL_NETWORK_ERROR,
|
||||||
LRCLIB_API_URL,
|
|
||||||
UA_LRX,
|
UA_LRX,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_LRCLIB_API_URL = "https://lrclib.net/api/get"
|
||||||
|
|
||||||
|
|
||||||
class LrclibFetcher(BaseFetcher):
|
class LrclibFetcher(BaseFetcher):
|
||||||
@property
|
@property
|
||||||
@@ -48,7 +49,7 @@ class LrclibFetcher(BaseFetcher):
|
|||||||
"album_name": track.album,
|
"album_name": track.album,
|
||||||
"duration": track.length / 1000.0 if track.length else 0,
|
"duration": track.length / 1000.0 if track.length else 0,
|
||||||
}
|
}
|
||||||
url = f"{LRCLIB_API_URL}?{urlencode(params)}"
|
url = f"{_LRCLIB_API_URL}?{urlencode(params)}"
|
||||||
logger.info(f"LRCLIB: fetching lyrics for {track.display_name()}")
|
logger.info(f"LRCLIB: fetching lyrics for {track.display_name()}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ from ..config import (
|
|||||||
TTL_UNSYNCED,
|
TTL_UNSYNCED,
|
||||||
TTL_NOT_FOUND,
|
TTL_NOT_FOUND,
|
||||||
TTL_NETWORK_ERROR,
|
TTL_NETWORK_ERROR,
|
||||||
LRCLIB_SEARCH_URL,
|
|
||||||
UA_LRX,
|
UA_LRX,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_LRCLIB_SEARCH_URL = "https://lrclib.net/api/search"
|
||||||
|
|
||||||
|
|
||||||
class LrclibSearchFetcher(BaseFetcher):
|
class LrclibSearchFetcher(BaseFetcher):
|
||||||
@property
|
@property
|
||||||
@@ -83,7 +84,7 @@ class LrclibSearchFetcher(BaseFetcher):
|
|||||||
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
||||||
|
|
||||||
async def _query(params: dict[str, str]) -> tuple[list[dict], bool]:
|
async def _query(params: dict[str, str]) -> tuple[list[dict], bool]:
|
||||||
url = f"{LRCLIB_SEARCH_URL}?{urlencode(params)}"
|
url = f"{_LRCLIB_SEARCH_URL}?{urlencode(params)}"
|
||||||
logger.debug(f"LRCLIB-search: query {params}")
|
logger.debug(f"LRCLIB-search: query {params}")
|
||||||
try:
|
try:
|
||||||
resp = await client.get(url, headers={"User-Agent": UA_LRX})
|
resp = await client.get(url, headers={"User-Agent": UA_LRX})
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ from .selection import SearchCandidate, select_best
|
|||||||
from ..authenticators.musixmatch import MusixmatchAuthenticator
|
from ..authenticators.musixmatch import MusixmatchAuthenticator
|
||||||
from ..lrc import LRCData
|
from ..lrc import LRCData
|
||||||
from ..models import CacheStatus, LyricResult, TrackMeta
|
from ..models import CacheStatus, LyricResult, TrackMeta
|
||||||
from ..config import (
|
from ..config import TTL_NETWORK_ERROR, TTL_NOT_FOUND
|
||||||
MUSIXMATCH_MACRO_URL,
|
|
||||||
MUSIXMATCH_SEARCH_URL,
|
_MUSIXMATCH_MACRO_URL = (
|
||||||
TTL_NETWORK_ERROR,
|
"https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get"
|
||||||
TTL_NOT_FOUND,
|
|
||||||
)
|
)
|
||||||
|
_MUSIXMATCH_SEARCH_URL = "https://apic-desktop.musixmatch.com/ws/1.1/track.search"
|
||||||
|
|
||||||
# Macro-specific params (format/app_id injected by authenticator)
|
# Macro-specific params (format/app_id injected by authenticator)
|
||||||
_MXM_MACRO_PARAMS = {
|
_MXM_MACRO_PARAMS = {
|
||||||
@@ -97,7 +97,7 @@ async def _fetch_macro(
|
|||||||
lyrics are found. Raises on HTTP/network errors.
|
lyrics are found. Raises on HTTP/network errors.
|
||||||
"""
|
"""
|
||||||
logger.debug(f"Musixmatch: macro call with {list(params.keys())}")
|
logger.debug(f"Musixmatch: macro call with {list(params.keys())}")
|
||||||
data = await auth.get_json(MUSIXMATCH_MACRO_URL, {**_MXM_MACRO_PARAMS, **params})
|
data = await auth.get_json(_MUSIXMATCH_MACRO_URL, {**_MXM_MACRO_PARAMS, **params})
|
||||||
if data is None:
|
if data is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ class MusixmatchFetcher(BaseFetcher):
|
|||||||
params["q_album"] = track.album
|
params["q_album"] = track.album
|
||||||
|
|
||||||
logger.debug(f"Musixmatch: searching for '{track.display_name()}'")
|
logger.debug(f"Musixmatch: searching for '{track.display_name()}'")
|
||||||
data = await self.auth.get_json(MUSIXMATCH_SEARCH_URL, params)
|
data = await self.auth.get_json(_MUSIXMATCH_SEARCH_URL, params)
|
||||||
if data is None:
|
if data is None:
|
||||||
return None, 0.0
|
return None, 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,15 @@ from ..config import (
|
|||||||
TTL_NOT_FOUND,
|
TTL_NOT_FOUND,
|
||||||
TTL_NETWORK_ERROR,
|
TTL_NETWORK_ERROR,
|
||||||
MULTI_CANDIDATE_DELAY_S,
|
MULTI_CANDIDATE_DELAY_S,
|
||||||
NETEASE_SEARCH_URL,
|
|
||||||
NETEASE_LYRIC_URL,
|
|
||||||
UA_BROWSER,
|
UA_BROWSER,
|
||||||
)
|
)
|
||||||
|
|
||||||
_HEADERS = {
|
_NETEASE_SEARCH_URL = "https://music.163.com/api/cloudsearch/pc"
|
||||||
|
_NETEASE_LYRIC_URL = "https://interface3.music.163.com/api/song/lyric"
|
||||||
|
_NETEASE_BASE_HEADERS = {
|
||||||
"User-Agent": UA_BROWSER,
|
"User-Agent": UA_BROWSER,
|
||||||
"Referer": "https://music.163.com/",
|
"Referer": "https://music.163.com/",
|
||||||
|
"Origin": "https://music.163.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -57,8 +58,8 @@ class NeteaseFetcher(BaseFetcher):
|
|||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
||||||
resp = await client.post(
|
resp = await client.post(
|
||||||
NETEASE_SEARCH_URL,
|
_NETEASE_SEARCH_URL,
|
||||||
headers=_HEADERS,
|
headers=_NETEASE_BASE_HEADERS,
|
||||||
data={"s": query, "type": "1", "limit": str(limit), "offset": "0"},
|
data={"s": query, "type": "1", "limit": str(limit), "offset": "0"},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
@@ -124,8 +125,8 @@ class NeteaseFetcher(BaseFetcher):
|
|||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
||||||
resp = await client.post(
|
resp = await client.post(
|
||||||
NETEASE_LYRIC_URL,
|
_NETEASE_LYRIC_URL,
|
||||||
headers=_HEADERS,
|
headers=_NETEASE_BASE_HEADERS,
|
||||||
data={
|
data={
|
||||||
"id": str(song_id),
|
"id": str(song_id),
|
||||||
"cp": "false",
|
"cp": "false",
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ from ..config import (
|
|||||||
TTL_NOT_FOUND,
|
TTL_NOT_FOUND,
|
||||||
TTL_NETWORK_ERROR,
|
TTL_NETWORK_ERROR,
|
||||||
MULTI_CANDIDATE_DELAY_S,
|
MULTI_CANDIDATE_DELAY_S,
|
||||||
QQ_MUSIC_API_LYRIC_ENDPOINT,
|
|
||||||
QQ_MUSIC_API_SEARCH_ENDPOINT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_QQ_MUSIC_API_SEARCH_ENDPOINT = "/api/search"
|
||||||
|
_QQ_MUSIC_API_LYRIC_ENDPOINT = "/api/lyric"
|
||||||
from ..authenticators import QQMusicAuthenticator
|
from ..authenticators import QQMusicAuthenticator
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ class QQMusicFetcher(BaseFetcher):
|
|||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
f"{await self.auth.authenticate()}{QQ_MUSIC_API_SEARCH_ENDPOINT}",
|
f"{await self.auth.authenticate()}{_QQ_MUSIC_API_SEARCH_ENDPOINT}",
|
||||||
params={"keyword": query, "type": "song", "num": limit},
|
params={"keyword": query, "type": "song", "num": limit},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
@@ -113,7 +114,7 @@ class QQMusicFetcher(BaseFetcher):
|
|||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
f"{await self.auth.authenticate()}{QQ_MUSIC_API_LYRIC_ENDPOINT}",
|
f"{await self.auth.authenticate()}{_QQ_MUSIC_API_LYRIC_ENDPOINT}",
|
||||||
params={"mid": mid},
|
params={"mid": mid},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|||||||
@@ -9,23 +9,12 @@ from typing import Optional
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from .base import BaseFetcher
|
from .base import BaseFetcher
|
||||||
from ..authenticators.spotify import SpotifyAuthenticator
|
from ..authenticators.spotify import SpotifyAuthenticator, SPOTIFY_BASE_HEADERS
|
||||||
from ..models import TrackMeta, LyricResult, CacheStatus
|
from ..models import TrackMeta, LyricResult, CacheStatus
|
||||||
from ..lrc import LRCData
|
from ..lrc import LRCData
|
||||||
from ..config import (
|
from ..config import HTTP_TIMEOUT, TTL_NOT_FOUND, TTL_NETWORK_ERROR
|
||||||
HTTP_TIMEOUT,
|
|
||||||
TTL_NOT_FOUND,
|
|
||||||
TTL_NETWORK_ERROR,
|
|
||||||
SPOTIFY_LYRICS_URL,
|
|
||||||
UA_BROWSER,
|
|
||||||
)
|
|
||||||
|
|
||||||
_SPOTIFY_BASE_HEADERS = {
|
_SPOTIFY_LYRICS_URL = "https://spclient.wg.spotify.com/color-lyrics/v2/track/"
|
||||||
"Referer": "https://open.spotify.com/",
|
|
||||||
"Origin": "https://open.spotify.com",
|
|
||||||
"App-Platform": "WebPlayer",
|
|
||||||
"Spotify-App-Version": "1.2.88.21.g8e037c8f",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SpotifyFetcher(BaseFetcher):
|
class SpotifyFetcher(BaseFetcher):
|
||||||
@@ -71,12 +60,11 @@ class SpotifyFetcher(BaseFetcher):
|
|||||||
logger.error("Spotify: cannot fetch lyrics without a token")
|
logger.error("Spotify: cannot fetch lyrics without a token")
|
||||||
return LyricResult(status=CacheStatus.NETWORK_ERROR, ttl=TTL_NETWORK_ERROR)
|
return LyricResult(status=CacheStatus.NETWORK_ERROR, ttl=TTL_NETWORK_ERROR)
|
||||||
|
|
||||||
url = f"{SPOTIFY_LYRICS_URL}{track.trackid}?format=json&vocalRemoval=false&market=from_token"
|
url = f"{_SPOTIFY_LYRICS_URL}{track.trackid}?format=json&vocalRemoval=false&market=from_token"
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": UA_BROWSER,
|
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": f"Bearer {token}",
|
"Authorization": f"Bearer {token}",
|
||||||
**_SPOTIFY_BASE_HEADERS,
|
**SPOTIFY_BASE_HEADERS,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "lrx-cli"
|
name = "lrx-cli"
|
||||||
version = "0.5.4"
|
version = "0.5.5"
|
||||||
description = "Fetch line-synced lyrics for your music player."
|
description = "Fetch line-synced lyrics for your music player."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
|
|||||||
Reference in New Issue
Block a user