File size: 7,928 Bytes
7e58923
 
 
4bc7387
510a8f2
4bc7387
 
63e3048
6146a19
4bc7387
7e58923
 
6a45bf5
 
 
 
5ce8d0d
7e58923
 
 
 
 
 
 
 
 
 
 
f94628b
ded5ac7
 
0f09a72
f94628b
6b74354
f94628b
 
c07416b
0f09a72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c07416b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ddfd898
c07416b
6146a19
 
c07416b
 
 
 
 
 
4bc7387
 
ddfd898
6146a19
 
 
 
 
 
2dbf28a
 
 
c07416b
 
 
 
 
 
 
 
 
 
 
 
 
 
4bc7387
 
5ce8d0d
c07416b
 
4bc7387
c07416b
 
e6bda95
5ce8d0d
ef388a2
6a3de95
c07416b
 
 
 
 
 
 
 
 
5ce8d0d
4bc7387
 
7e58923
 
 
 
 
 
 
 
 
 
 
 
74078ba
 
 
 
 
 
7e58923
c07416b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
from flask import Flask, render_template, jsonify, request
from ytmusicapi import YTMusic
import os
import logging
import requests
from datetime import datetime, timedelta
import time
import asyncio
import cloudscraper

app = Flask(__name__)
ytmusic = YTMusic()


# Configure logging 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/search', methods=['POST'])
def search():
    query = request.json.get('query', '')
    search_results = ytmusic.search(query, filter="songs")
    return jsonify(search_results)

@app.route('/searcht', methods=['POST'])
def searcht(): 
    query = request.json.get('query', '')
    logger.info(f"serch query: {query}")
    search_results = ytmusic.search(query, filter="songs")
    first_song = next((song for song in search_results if 'videoId' in song and song['videoId']), {}) if search_results else {}
    return jsonify(first_song)


# Function to extract track ID from Amazon Music URL
def extract_amazon_track_id(url: str):
    if "music.amazon.com" in url:
        # Case 1: URL contains trackAsin (e.g., https://music.amazon.com/albums/B01N48U32A?trackAsin=B01NAE38YO&do=play)
        parsed_url = urlparse(url)
        query_params = parse_qs(parsed_url.query)
        if "trackAsin" in query_params:
            return query_params["trackAsin"][0]
        
        # Case 2: URL is a direct track link (e.g., https://music.amazon.com/tracks/B0DNTPYT5S)
        if "/tracks/" in url:
            return url.split("/tracks/")[-1].split("?")[0]
    
    return None
    

# Function to get track info from Song.link API
def get_song_link_info(url: str):
     # Check if the URL is from Amazon Music
    if "music.amazon.com" in url:
        track_id = extract_amazon_track_id(url)
        if track_id:
            # Use the working format for Amazon Music tracks
            api_url = f"https://api.song.link/v1-alpha.1/links?type=song&platform=amazonMusic&id={track_id}&userCountry=US"
        else:
            # If no track ID is found, use the original URL
            api_url = f"https://api.song.link/v1-alpha.1/links?url={url}&userCountry=US"
    else:
        # For non-Amazon Music URLs, use the standard format
        api_url = f"https://api.song.link/v1-alpha.1/links?url={url}&userCountry=US"
    
    # Make the API call
    response = requests.get(api_url)
    if response.status_code == 200:
        return response.json()
    else:
        return None

# Function to extract Tidal or YouTube URL
def extract_url(links_by_platform: dict, platform: str):
    if platform in links_by_platform:
        return links_by_platform[platform]["url"]
    return None


# Function to extract track title and artist from entities
def extract_track_info(entities_by_unique_id: dict, platform: str):
    for entity in entities_by_unique_id.values():
        if entity["apiProvider"] == platform:
            return entity["title"], entity["artistName"]
    return None, None

class SpotTrackRequest(BaseModel):
    url: str

@app.post("/match")
async def match(track_request: SpotTrackRequest):
    track_url = track_request.url

    if not track_url:
        raise HTTPException(status_code=400, detail="No URL provided")

    track_info = get_song_link_info(track_url)
    if not track_info:
        raise HTTPException(status_code=404, detail="Could not fetch track info")

    youtube_url = extract_url(track_info["linksByPlatform"], "youtube")
    if youtube_url:
        title, artist = extract_track_info(track_info["entitiesByUniqueId"], "youtube")
        if title and artist:
            filename = f"{title} - {artist}"
            return {"url": youtube_url, "filename": filename}
        else:
            return {"url": youtube_url, "filename": "Unknown Track - Unknown Artist"}
    else:
        entityUniqueId = track_info[0]."entityUniqueId"
        logger.info(f"songlink info: {entityUniqueId}")
        title = track_info[0]."entitiesByUniqueId".entityUniqueId.title
        search_results = ytmusic.search(title, filter="songs")
        first_song = next((song for song in search_results if 'videoId' in song and song['videoId']), {}) if search_results else {}
        return jsonify(first_song)

    # If no URLs found, return an error
    raise HTTPException(status_code=404, detail="No matching URL found")

class ApiRotator:
    def __init__(self, apis):
        self.apis = apis
        self.last_successful_index = None

    def get_prioritized_apis(self):
        if self.last_successful_index is not None:
            # Move the last successful API to the front
            rotated_apis = (
                [self.apis[self.last_successful_index]] + 
                self.apis[:self.last_successful_index] + 
                self.apis[self.last_successful_index+1:]
            )
            return rotated_apis
        return self.apis

    def update_last_successful(self, index):
        self.last_successful_index = index

# In your function:
api_rotator = ApiRotator([
    "https://cobalt-api.ayo.tf",
    "https://cobalt-api.kwiatekmiki.com",
    "http://34.107.254.11",
    "https://dwnld.nichind.dev",
    "https://yt.edd1e.xyz/"
    
])



async def get_track_download_url(track_id: str) -> str:
    apis = api_rotator.get_prioritized_apis()
    session = cloudscraper.create_scraper()  # Requires cloudscraper package
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    }

    for i, api_url in enumerate(apis):
        try:
            logger.info(f"Attempting to get download URL from: {api_url}")
            y_url = f"https://youtu.be/{track_id}"
            response = session.post(
                api_url,
                timeout=20,
                json={"url": y_url, "audioFormat": "mp3", "downloadMode": "audio"},
                headers=headers
            )
            logger.info(f"Response status: {response.status_code}")
            logger.info(f"Response content: {response.content}")

            if response.headers.get('content-type', '').startswith('application/json'):
                json_response = response.json()
                error_code = json_response.get("error", {}).get("code", "")
                
                if error_code == "error.api.content.video.unavailable":
                    logger.warning(f"Video unavailable error from {api_url}")
                    break  # Only break for specific error
                
                if "url" in json_response:
                    api_rotator.update_last_successful(i)
                    return json_response["url"]
                
        except Exception as e:
            logger.error(f"Failed with {api_url}: {str(e)}")
            continue
    
    logger.error(f"No download URL found")
    return {"error": "Download URL not found"}



    
@app.route('/track_dl', methods=['POST'])
async def track_dl():
    track_id = request.json.get('track_id')
    dl_url = await get_track_download_url(track_id)
    if dl_url and "http" in dl_url:
        result = {"url": dl_url}
        return jsonify(result)
    else:
        return {
                "error": "Failed to Fetch the Track."
            }
    
    



@app.route('/get_artist', methods=['GET'])
def get_artist():
    artist_id = request.args.get('id')
    artist_info = ytmusic.get_artist(artist_id)
    return jsonify(artist_info)

@app.route('/get_album', methods=['GET'])
def get_album():
    album_id = request.args.get('id')
    album_info = ytmusic.get_album(album_id)
    return jsonify(album_info)

@app.route('/get_song', methods=['GET'])
def get_song():
    song_id = request.args.get('id')
    song_info = ytmusic.get_song(song_id)
    return jsonify(song_info)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=7860)