lyrics-transcriber 0.58.0__py3-none-any.whl → 0.60.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ import logging
2
2
  from typing import Optional, Dict, Any
3
3
  import syrics.api
4
4
  import time
5
+ import requests
5
6
 
6
7
  from lyrics_transcriber.types import LyricsData, LyricsMetadata, LyricsSegment, Word
7
8
  from lyrics_transcriber.lyrics.base_lyrics_provider import BaseLyricsProvider, LyricsProviderConfig
@@ -14,9 +15,11 @@ class SpotifyProvider(BaseLyricsProvider):
14
15
  def __init__(self, config: LyricsProviderConfig, logger: Optional[logging.Logger] = None):
15
16
  super().__init__(config, logger)
16
17
  self.cookie = config.spotify_cookie
18
+ self.rapidapi_key = config.rapidapi_key
17
19
  self.client = None
18
20
 
19
- if self.cookie:
21
+ # Only initialize syrics client if rapidapi_key is not set
22
+ if self.cookie and not self.rapidapi_key:
20
23
  max_retries = 5
21
24
  retry_delay = 5 # seconds
22
25
 
@@ -32,9 +35,17 @@ class SpotifyProvider(BaseLyricsProvider):
32
35
  time.sleep(retry_delay)
33
36
 
34
37
  def _fetch_data_from_source(self, artist: str, title: str) -> Optional[Dict[str, Any]]:
35
- """Fetch raw data from Spotify APIs using syrics library."""
38
+ """Fetch raw data from Spotify APIs using RapidAPI or syrics library."""
39
+ # Try RapidAPI first if available
40
+ if self.rapidapi_key:
41
+ self.logger.info(f"Trying RapidAPI for {artist} - {title}")
42
+ result = self._fetch_from_rapidapi(artist, title)
43
+ if result:
44
+ return result
45
+
46
+ # Fall back to syrics library
36
47
  if not self.client:
37
- self.logger.warning("No Spotify cookie provided")
48
+ self.logger.warning("No Spotify cookie provided and RapidAPI failed")
38
49
  return None
39
50
 
40
51
  try:
@@ -57,8 +68,82 @@ class SpotifyProvider(BaseLyricsProvider):
57
68
  self.logger.error(f"Error fetching from Spotify: {str(e)}")
58
69
  return None
59
70
 
71
+ def _fetch_from_rapidapi(self, artist: str, title: str) -> Optional[Dict[str, Any]]:
72
+ """Fetch song data using RapidAPI."""
73
+ try:
74
+ # Step 1: Search for the track
75
+ search_url = "https://spotify-scraper.p.rapidapi.com/v1/track/search"
76
+ search_params = {
77
+ "name": f"{title} {artist}"
78
+ }
79
+
80
+ headers = {
81
+ "x-rapidapi-key": self.rapidapi_key,
82
+ "x-rapidapi-host": "spotify-scraper.p.rapidapi.com"
83
+ }
84
+
85
+ self.logger.debug(f"Making RapidAPI search request for '{artist} {title}'")
86
+ search_response = requests.get(search_url, headers=headers, params=search_params, timeout=10)
87
+ search_response.raise_for_status()
88
+
89
+ search_data = search_response.json()
90
+
91
+ # Check if search was successful
92
+ if not search_data.get("status") or search_data.get("errorId") != "Success":
93
+ self.logger.warning("RapidAPI search failed")
94
+ return None
95
+
96
+ track_id = search_data.get("id")
97
+ if not track_id:
98
+ self.logger.warning("No track ID found in RapidAPI search results")
99
+ return None
100
+
101
+ self.logger.debug(f"Found track ID: {track_id}")
102
+
103
+ # Step 2: Fetch lyrics using the track ID
104
+ lyrics_url = "https://spotify-scraper.p.rapidapi.com/v1/track/lyrics"
105
+ lyrics_params = {
106
+ "trackId": track_id,
107
+ "format": "json",
108
+ "removeNote": "true"
109
+ }
110
+
111
+ self.logger.debug(f"Making RapidAPI lyrics request for track ID {track_id}")
112
+ lyrics_response = requests.get(lyrics_url, headers=headers, params=lyrics_params, timeout=10)
113
+ lyrics_response.raise_for_status()
114
+
115
+ lyrics_data = lyrics_response.json()
116
+
117
+ # Create a clean RapidAPI response structure
118
+ rapidapi_response = {
119
+ "track_data": search_data,
120
+ "lyrics_data": lyrics_data,
121
+ # Mark this as RapidAPI source
122
+ "_rapidapi_source": True
123
+ }
124
+
125
+ self.logger.info("Successfully fetched lyrics from RapidAPI")
126
+ return rapidapi_response
127
+
128
+ except requests.exceptions.RequestException as e:
129
+ self.logger.error(f"RapidAPI request failed: {str(e)}")
130
+ return None
131
+ except Exception as e:
132
+ self.logger.error(f"Error fetching from RapidAPI: {str(e)}")
133
+ return None
134
+
60
135
  def _convert_result_format(self, raw_data: Dict[str, Any]) -> LyricsData:
61
136
  """Convert Spotify's raw API response to standardized format."""
137
+ # Use our explicit source marker for detection
138
+ is_rapidapi = raw_data.get("_rapidapi_source", False)
139
+
140
+ if is_rapidapi:
141
+ return self._convert_rapidapi_format(raw_data)
142
+ else:
143
+ return self._convert_syrics_format(raw_data)
144
+
145
+ def _convert_syrics_format(self, raw_data: Dict[str, Any]) -> LyricsData:
146
+ """Convert syrics format to standardized format."""
62
147
  track_data = raw_data["track_data"]
63
148
  lyrics_data = raw_data["lyrics_data"]["lyrics"]
64
149
 
@@ -79,6 +164,7 @@ class SpotifyProvider(BaseLyricsProvider):
79
164
  "preview_url": track_data.get("preview_url"),
80
165
  "external_urls": track_data.get("external_urls"),
81
166
  "sync_type": lyrics_data.get("syncType"),
167
+ "api_source": "syrics",
82
168
  },
83
169
  )
84
170
 
@@ -122,6 +208,80 @@ class SpotifyProvider(BaseLyricsProvider):
122
208
 
123
209
  return LyricsData(source="spotify", segments=segments, metadata=metadata)
124
210
 
211
+ def _convert_rapidapi_format(self, raw_data: Dict[str, Any]) -> LyricsData:
212
+ """Convert RapidAPI format to standardized format."""
213
+ track_data = raw_data["track_data"]
214
+ lyrics_data = raw_data["lyrics_data"]
215
+
216
+ # Extract artist names from RapidAPI format
217
+ artist_names = []
218
+ if "artists" in track_data:
219
+ artist_names = [artist.get("name", "") for artist in track_data["artists"]]
220
+
221
+ # Create metadata object
222
+ metadata = LyricsMetadata(
223
+ source="spotify",
224
+ track_name=track_data.get("name"),
225
+ artist_names=", ".join(artist_names),
226
+ album_name=track_data.get("album", {}).get("name"),
227
+ duration_ms=track_data.get("durationMs"),
228
+ explicit=track_data.get("explicit"),
229
+ is_synced=True, # RapidAPI format includes timing information
230
+ lyrics_provider="spotify",
231
+ lyrics_provider_id=track_data.get("id"),
232
+ provider_metadata={
233
+ "spotify_id": track_data.get("id"),
234
+ "share_url": track_data.get("shareUrl"),
235
+ "duration_text": track_data.get("durationText"),
236
+ "album_cover": track_data.get("album", {}).get("cover"),
237
+ "api_source": "rapidapi",
238
+ },
239
+ )
240
+
241
+ # Create segments with timing information from RapidAPI format
242
+ segments = []
243
+ for line in lyrics_data:
244
+ if not line.get("text"):
245
+ continue
246
+
247
+ # Skip lines that are just musical notes
248
+ if not self._clean_lyrics(line["text"]):
249
+ continue
250
+
251
+ # Split line into words
252
+ word_texts = line["text"].strip().split()
253
+ if not word_texts:
254
+ continue
255
+
256
+ # Calculate timing for each word
257
+ start_time = float(line["startMs"]) / 1000 if line.get("startMs") else 0.0
258
+ duration = float(line["durMs"]) / 1000 if line.get("durMs") else 0.0
259
+ end_time = start_time + duration
260
+ word_duration = duration / len(word_texts)
261
+
262
+ words = []
263
+ for i, word_text in enumerate(word_texts):
264
+ word = Word(
265
+ id=WordUtils.generate_id(),
266
+ text=word_text,
267
+ start_time=start_time + (i * word_duration),
268
+ end_time=start_time + ((i + 1) * word_duration),
269
+ confidence=1.0,
270
+ created_during_correction=False,
271
+ )
272
+ words.append(word)
273
+
274
+ segment = LyricsSegment(
275
+ id=WordUtils.generate_id(),
276
+ text=line["text"].strip(),
277
+ words=words,
278
+ start_time=start_time,
279
+ end_time=end_time
280
+ )
281
+ segments.append(segment)
282
+
283
+ return LyricsData(source="spotify", segments=segments, metadata=metadata)
284
+
125
285
  def _clean_lyrics(self, lyrics: str) -> str:
126
286
  """Clean and process lyrics from Spotify to remove unwanted content."""
127
287
  # Remove lines that contain only musical note symbols
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lyrics-transcriber
3
- Version: 0.58.0
3
+ Version: 0.60.0
4
4
  Summary: Automatically create synchronised lyrics files in ASS and MidiCo LRC formats with word-level timestamps, using Whisper and lyrics from Genius and Spotify
5
5
  License: MIT
6
6
  Author: Andrew Beveridge
@@ -1,9 +1,9 @@
1
1
  lyrics_transcriber/__init__.py,sha256=g9ZbJg9U1qo7XzrC25J3bTKcNzzwUJWDVdi_7-hjcM4,412
2
2
  lyrics_transcriber/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- lyrics_transcriber/cli/cli_main.py,sha256=kMWoV_89KRD2XAU39Brs2rdkbQmG6OxrEn7SAh2zCTM,10648
3
+ lyrics_transcriber/cli/cli_main.py,sha256=Tk_PtZyAogsPSrmAD8KNQsPMWFW_patX2XM0EZGaVis,10752
4
4
  lyrics_transcriber/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- lyrics_transcriber/core/config.py,sha256=euwOOtuNbXy4-a1xs8QKdjcf5jXZQle0zf6X1Wthurw,1229
6
- lyrics_transcriber/core/controller.py,sha256=66qwIv-2jEW94wU5RVFRIcfrTyszC-aC_Fcx5dCjG7k,20255
5
+ lyrics_transcriber/core/config.py,sha256=G4Z5kEBMDXRscEiRKbnofAPuFTDB5h1fLJvwxaHaT4I,1268
6
+ lyrics_transcriber/core/controller.py,sha256=hBSkvHtcdmnLKRx82Szya-UMKil_QwnmcpmO8-H2Sm0,20700
7
7
  lyrics_transcriber/correction/anchor_sequence.py,sha256=Bz08zB8yS8orz73aA5dDyNUgBBU87KtQM6yOZGNDoFI,32228
8
8
  lyrics_transcriber/correction/corrector.py,sha256=wwSLHat4SGKEJffFQVcmSfMN_I8Drv-jpeTkO8ndLu0,20930
9
9
  lyrics_transcriber/correction/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,7 +29,7 @@ lyrics_transcriber/frontend/README.md,sha256=-D6CAfKTT7Y0V3EjlZ2fMy7fyctFQ4x2TJ9
29
29
  lyrics_transcriber/frontend/__init__.py,sha256=nW8acRSWTjXoRwGqcTU4w-__X7tMAE0iXL0uihBN3CU,836
30
30
  lyrics_transcriber/frontend/eslint.config.js,sha256=3ADH23ANA4NNBKFy6nCVk65e8bx1DrVd_FIaYNnhuqA,734
31
31
  lyrics_transcriber/frontend/index.html,sha256=KfqJVONzpUyPIwV73nZRiCWlwLnFWeB3z0vzxDPNudU,376
32
- lyrics_transcriber/frontend/package.json,sha256=MMCglMWJSeiXc8AmFTCZHAiAMGKTYRa7oAQMCJrMY_4,1182
32
+ lyrics_transcriber/frontend/package.json,sha256=CvGu-kAvDW1zqGICBQqzpeaNtweFj8r8Ig_Kgl6H9TQ,1182
33
33
  lyrics_transcriber/frontend/public/vite.svg,sha256=SnSK_UQ5GLsWWRyDTEAdrjPoeGGrXbrQgRw6O0qSFPs,1497
34
34
  lyrics_transcriber/frontend/src/App.tsx,sha256=f1-dp-MU8vap18eAXacwVDO5P4eE2iG9zSvjau-7NJs,6533
35
35
  lyrics_transcriber/frontend/src/api.ts,sha256=UgqPc1jo8DEVgxh3_9Lyf9GBsHYpqMAqsPEE5BzTV4w,6640
@@ -83,15 +83,16 @@ lyrics_transcriber/frontend/update_version.js,sha256=PxkqCnsucXnXiIqutsanVcx00Gq
83
83
  lyrics_transcriber/frontend/vite.config.d.ts,sha256=S5bdGf0pSdKM6A6RNBKwAm3EIeW_bDHYfHtesRtXU7Q,76
84
84
  lyrics_transcriber/frontend/vite.config.js,sha256=P4GuPgRZzwEWPQZpyujUe7eA3mjPoFAe2CgE5sQAXg8,232
85
85
  lyrics_transcriber/frontend/vite.config.ts,sha256=8FdW0dN8zDFqfhQSxX5h7sIu72X2piLYlp_TZYRQvBQ,216
86
- lyrics_transcriber/frontend/web_assets/assets/index-fO30CduZ.js,sha256=mweQzzHIH-rLHty92kWFaie7V8sCOz8xAg21ugSLiBM,1257959
87
- lyrics_transcriber/frontend/web_assets/assets/index-fO30CduZ.js.map,sha256=qgdhzBuYAqsfLFflCxfy8rDePS6KoCMeAUDhrm85K2k,2678465
88
- lyrics_transcriber/frontend/web_assets/index.html,sha256=gacSk2b2_RFh_x2aGiiidBzukUldKMEQtlRhFnUarso,400
86
+ lyrics_transcriber/frontend/web_assets/assets/index-Bktwnsnn.js,sha256=zH60jX1R-f07xK0LTeeXJe9xX2DO3vIOSI4Z-pwMko8,1257959
87
+ lyrics_transcriber/frontend/web_assets/assets/index-Bktwnsnn.js.map,sha256=CkhEETBBDcAouzUG4qr5FsMgNnOS5-E5pq2Lf0ojrcI,2678465
88
+ lyrics_transcriber/frontend/web_assets/index.html,sha256=dzIvCdIPwwrs6ADqTHJxh_phyBvKplrYDkaDD3I3klg,400
89
89
  lyrics_transcriber/frontend/web_assets/vite.svg,sha256=SnSK_UQ5GLsWWRyDTEAdrjPoeGGrXbrQgRw6O0qSFPs,1497
90
90
  lyrics_transcriber/frontend/yarn.lock,sha256=wtImLsCO1P1Lpkhc1jAN6IiHQ0As4xn39n0cwKoh4LM,131996
91
- lyrics_transcriber/lyrics/base_lyrics_provider.py,sha256=mqlqssKG2AofvqEU48nCwLnz0FhO9Ee6MNixF6GBnYY,9133
91
+ lyrics_transcriber/lyrics/base_lyrics_provider.py,sha256=LCzmwpBFgSfC6VVY5AtJHmE0OvgjAQyjPnL-dYLLwg4,9172
92
92
  lyrics_transcriber/lyrics/file_provider.py,sha256=WNd6mHMV2FhrnHiWBvxUxPkdVi47mbLE4hXaTYqStTM,4290
93
- lyrics_transcriber/lyrics/genius.py,sha256=SIMFEmD_QbXUB8hpDhRU7AAyVrJbRvKyTWsShA9jecE,5693
94
- lyrics_transcriber/lyrics/spotify.py,sha256=K7aL_OHdQjhI8ydnHUq8-PUvkyDu2s-et7njiLIBVgY,5457
93
+ lyrics_transcriber/lyrics/genius.py,sha256=XTKLAjwzopYIOvvn0na_Ym5yM1JZjp8a-f1Ebr4cuN0,17121
94
+ lyrics_transcriber/lyrics/musixmatch.py,sha256=mlTuRm7XxeXy1NAHqqLUhilf90IY0JiGdX9nAmiZ2M0,7368
95
+ lyrics_transcriber/lyrics/spotify.py,sha256=O8rzDRjM-wdF2KntLqPe414-1jRTmVGfRCfMwSovHVg,12044
95
96
  lyrics_transcriber/lyrics/user_input_provider.py,sha256=oNzwjk2bOQYyUXvVqPcbrF8vJU7LLtwTvJTXxtPaQto,1798
96
97
  lyrics_transcriber/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
98
  lyrics_transcriber/output/ass/__init__.py,sha256=EYQ45gI7_-vclVgzISL0ML8VgxCdB0odqEyPyiPCIw0,578
@@ -152,8 +153,8 @@ lyrics_transcriber/transcribers/base_transcriber.py,sha256=T3m4ZCwZ9Bpv6Jvb2hNcn
152
153
  lyrics_transcriber/transcribers/whisper.py,sha256=YcCB1ic9H6zL1GS0jD0emu8-qlcH0QVEjjjYB4aLlIQ,13260
153
154
  lyrics_transcriber/types.py,sha256=wqFrTKhb8qAUB48zH-51_EEGCGrxm0Ji-ETfQumtSKc,27666
154
155
  lyrics_transcriber/utils/word_utils.py,sha256=-cMGpj9UV4F6IsoDKAV2i1aiqSO8eI91HMAm_igtVMk,958
155
- lyrics_transcriber-0.58.0.dist-info/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
156
- lyrics_transcriber-0.58.0.dist-info/METADATA,sha256=0voXGbhwVigZppC-My5uCpqvdX2Z-dBAr5TpQAZ7Upw,6637
157
- lyrics_transcriber-0.58.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
158
- lyrics_transcriber-0.58.0.dist-info/entry_points.txt,sha256=kcp-bSFkCACAEA0t166Kek0HpaJUXRo5SlF5tVrqNBU,216
159
- lyrics_transcriber-0.58.0.dist-info/RECORD,,
156
+ lyrics_transcriber-0.60.0.dist-info/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
157
+ lyrics_transcriber-0.60.0.dist-info/METADATA,sha256=029j3LSsxZ7bKfCfKOHcX8J6UzFEJmqCHMmXdQZR3jI,6637
158
+ lyrics_transcriber-0.60.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
159
+ lyrics_transcriber-0.60.0.dist-info/entry_points.txt,sha256=kcp-bSFkCACAEA0t166Kek0HpaJUXRo5SlF5tVrqNBU,216
160
+ lyrics_transcriber-0.60.0.dist-info/RECORD,,