ytmsc / app.py
Chrunos's picture
Update app.py
0f09a72 verified
raw
history blame
7.93 kB
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)