-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3a0cdd8
Showing
6 changed files
with
372 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
youtube_credentials.txt | ||
spotify_credentials.txt | ||
Spotify_automation.py | ||
Mp3downloader.py | ||
final.csv | ||
.cache | ||
backend.cpython-311.pyc | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
from fastapi import FastAPI, Request, Form | ||
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse | ||
from fastapi.staticfiles import StaticFiles | ||
from fastapi.templating import Jinja2Templates | ||
from pytube import YouTube | ||
import csv | ||
from googleapiclient.discovery import build | ||
import os | ||
import spotipy | ||
from spotipy.oauth2 import SpotifyClientCredentials | ||
from typing import List | ||
import zipfile | ||
|
||
app = FastAPI() | ||
|
||
app.mount("/static", StaticFiles(directory="static"), name="static") | ||
templates = Jinja2Templates(directory="templates") | ||
|
||
# Load YouTube API key from file | ||
with open("youtube_credentials.txt") as f: | ||
API_KEY = f.read().strip() | ||
|
||
#extracting client id and client secret code in a secure way | ||
with open("spotify_credentials.txt") as f: | ||
[SPOTIPY_CLIENT_ID, SPOTIFY_CLIENT_SECRET] = f.read().split("\n") | ||
f.close() | ||
|
||
#Connecting with Spotify API | ||
auth_manager = SpotifyClientCredentials(client_id=SPOTIPY_CLIENT_ID, client_secret=SPOTIFY_CLIENT_SECRET) | ||
sp = spotipy.Spotify(auth_manager=auth_manager) | ||
|
||
|
||
def generate_playlist_csv(playlist_link, user_id): | ||
playlist_dict = sp.playlist(playlist_link) | ||
|
||
no_of_songs = playlist_dict["tracks"]["total"] | ||
|
||
album_list = [] | ||
song_list = [] | ||
release_date_list = [] | ||
artists_list = [] | ||
|
||
tracks = playlist_dict["tracks"] | ||
items = tracks["items"] | ||
offset = 0 | ||
i = 0 | ||
|
||
while i < no_of_songs: | ||
song = items[i-offset]["track"]["name"] | ||
album = items[i-offset]["track"]["album"]["name"] | ||
release_date = items[i-offset]["track"]["album"]["release_date"] | ||
artists = [k["name"] for k in items[i-offset]["track"]["artists"]] | ||
artists = ','.join(artists) | ||
album_list.append(album) | ||
song_list.append(song) | ||
release_date_list.append(release_date) | ||
artists_list.append(artists) | ||
|
||
if (i+1)%100 == 0: | ||
tracks = sp.next(tracks) | ||
items = tracks["items"] | ||
offset = i+1 | ||
|
||
i+=1 | ||
|
||
final_data = list(zip(song_list,artists_list,album_list,release_date_list)) | ||
|
||
#Creating CSV File | ||
csv_file_name = f"{user_id}_final.csv" | ||
csv_file_path = os.path.join("csv_file", csv_file_name) | ||
|
||
|
||
with open(csv_file_path, 'w', newline='') as f: | ||
writer = csv.writer(f) | ||
writer.writerow(["Name", "Artists", "Album", "Release Date"]) | ||
writer.writerows(final_data) | ||
|
||
return csv_file_path | ||
|
||
|
||
|
||
# Searching for a song on Youtube using YouTube Data API | ||
def search_youtube(song_name, artist): | ||
youtube = build('youtube', 'v3', developerKey=API_KEY) | ||
search_query = song_name + ' ' + artist + ' audio' | ||
request = youtube.search().list( | ||
q=search_query, | ||
part='id', | ||
type='video', | ||
maxResults=1 | ||
) | ||
response = request.execute() | ||
if 'items' in response: | ||
if len(response['items']) > 0: | ||
video_id = response['items'][0]['id']['videoId'] | ||
video_url = 'https://www.youtube.com/watch?v=' + video_id | ||
return video_url | ||
print(f"No search results found for '{song_name}' by {artist}.") | ||
return None | ||
|
||
# Download MP3 from YouTube using pytube | ||
def download_mp3(youtube_url, user_folder): | ||
try: | ||
yt = YouTube(youtube_url) | ||
stream = yt.streams.filter(only_audio=True).first() | ||
if stream: | ||
print("Downloading MP3...") | ||
|
||
os.makedirs(user_folder, exist_ok=True) | ||
file_name = f"{yt.title}.mp4" | ||
# song_path = os.path.join(user_folder, f"{yt.title}.mp4") | ||
stream.download(output_path=user_folder) | ||
print("Download complete.\n") | ||
return file_name | ||
else: | ||
print("No audio stream available for this video.") | ||
except Exception as e: | ||
print(f"Error downloading MP3: {e}") | ||
|
||
|
||
#Loading song details from CSV | ||
def load_song_details(csv_file): | ||
song_details = [] | ||
with open(csv_file, 'r', newline='') as f: | ||
reader = csv.reader(f) | ||
next(reader) #skipping header | ||
for row in reader: | ||
song_details.append(row[:2]) | ||
|
||
return song_details | ||
|
||
|
||
@app.get("/", response_class=HTMLResponse) | ||
async def read_item(request: Request): | ||
# Render the HTML template | ||
return templates.TemplateResponse("index.html", {"request": request}) | ||
|
||
|
||
@app.post("/download_songs", response_class=HTMLResponse) | ||
async def download_songs(request: Request, playlist_link: str = Form(...), userid: str = Form(...)): | ||
|
||
print(userid) | ||
print(playlist_link) | ||
#Generating CSV file for the playlist | ||
csv_file_path = generate_playlist_csv(playlist_link, userid) | ||
print("CSV file is created") | ||
|
||
user_folder = os.path.join("downloaded_songs", userid) | ||
os.makedirs(user_folder, exist_ok=True) | ||
|
||
song_details = load_song_details(csv_file_path) | ||
downloaded_songs = [] | ||
|
||
|
||
for song_name, artist in song_details: | ||
youtube_url = search_youtube(song_name, artist) | ||
if youtube_url: | ||
song_file = download_mp3(youtube_url, user_folder) | ||
if song_file: | ||
#encoded_file_name = urllib.parse.quote(song_file, safe="") | ||
downloaded_songs.append(song_file) | ||
|
||
response_data = { | ||
"message": "Songs downloaded successfully!", | ||
"user_folder": user_folder, | ||
"songs": downloaded_songs | ||
} | ||
|
||
return JSONResponse(content=response_data) | ||
|
||
@app.get("/download/{user_id}/{file_name}") | ||
async def download_song(user_id: str, file_name: str): | ||
user_folder = os.path.join("downloaded_songs", user_id) | ||
file_path = os.path.join(user_folder, file_name) | ||
return FileResponse(file_path, media_type='audio/mp3', filename=file_name) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
document.addEventListener("DOMContentLoaded", function(){ | ||
const form = document.getElementById("playlist-form"); | ||
form.addEventListener("submit", function(event){ | ||
event.preventDefault(); | ||
const formData = new FormData(form); | ||
const playlistLink = formData.get("playlist_link"); | ||
const userid = generateUserId(); | ||
fetch("/download_songs", { | ||
method: "POST", | ||
body: new URLSearchParams({ playlist_link: playlistLink, userid: userid }), | ||
headers:{ | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
}, | ||
}) | ||
.then(response => response.json()) | ||
.then(data => { | ||
const userFolder = data.user_folder; | ||
const songs = data.songs; | ||
const downloadContainer = document.getElementById("downloads"); | ||
if(downloadContainer){ | ||
songs.forEach(song => { | ||
const downloadLink = document.createElement("a"); | ||
downloadLink.href = `/download/${userid}/${song}`; | ||
downloadLink.textContent = `Download ${song}`; | ||
downloadContainer.appendChild(downloadLink); | ||
downloadContainer.appendChild(document.createElement("br")); | ||
}); | ||
} else { | ||
console.error("Download container not found."); | ||
} | ||
}) | ||
.catch(error => { | ||
console.error("Error: ", error); | ||
}); | ||
}); | ||
|
||
function generateUserId(){ | ||
return Math.random().toString(36).substring(2,9); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
body { | ||
font-family: Arial, sans-serif; | ||
background-color: #f8f8f8; | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
h1 { | ||
text-align: center; | ||
color: #333; | ||
margin-top: 20px; | ||
} | ||
|
||
form { | ||
margin: 0 auto; | ||
width: 300px; | ||
background-color: #fff; | ||
padding: 20px; | ||
border-radius: 8px; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
} | ||
|
||
form label { | ||
display: block; | ||
margin-bottom: 10px; | ||
color: #333; | ||
} | ||
|
||
form input[type="text"] { | ||
width: calc(100% - 20px); | ||
padding: 10px; | ||
margin-bottom: 20px; | ||
border: 1px solid #ccc; | ||
border-radius: 4px; | ||
} | ||
|
||
form button[type="submit"] { | ||
width: 100%; | ||
padding: 10px; | ||
background-color: #007bff; | ||
color: #fff; | ||
border: none; | ||
border-radius: 4px; | ||
cursor: pointer; | ||
transition: background-color 0.3s ease; | ||
} | ||
|
||
form button[type="submit"]:hover { | ||
background-color: #0056b3; | ||
} | ||
|
||
#progress { | ||
margin-top: 20px; | ||
text-align: center; | ||
} | ||
|
||
.container { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 200px; | ||
background-color: #fff; | ||
border-radius: 8px; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
overflow: hidden; | ||
} | ||
|
||
.img { | ||
max-width: 100%; | ||
max-height: 100%; | ||
object-fit: cover; | ||
} | ||
|
||
section#description { | ||
background-color: #333; | ||
padding: 20px; | ||
border-radius: 8px; | ||
margin: 20px auto; | ||
max-width: 800px; | ||
color: #fff; | ||
} | ||
|
||
section#description h2 { | ||
color: #fff; | ||
text-align: center; | ||
margin-bottom: 20px; | ||
} | ||
|
||
section#description p { | ||
line-height: 1.6; | ||
margin-bottom: 10px; | ||
} | ||
|
||
section#description ol { | ||
margin-left: 20px; | ||
} | ||
|
||
section#description ol li { | ||
margin-bottom: 10px; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Spotify2Mp3</title> | ||
<link rel="stylesheet" href="{{ url_for('static', path='style.css') }}"> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<img src="/static/image.png" class="img"> | ||
</div> | ||
<h1>Spotify2Mp3</h1> | ||
|
||
<!-- Description Section --> | ||
<section id="description"> | ||
<h2>How Spotify2Mp3 Works: </h2> | ||
<p>Spotify2Mp3 is a tool that allows you to convert Spotify playlists into MP3 files for offline listening. Here's how it works:</p> | ||
<ol> | ||
<li>Enter the link to your Spotify playlist in the form provided.</li> | ||
<li>Click the "Submit" button to initiate the conversion process.</li> | ||
<li>Spotify2Mp3 will extract the song details from your playlist and search for them on YouTube.</li> | ||
<li>Once found, it downloads the audio tracks from YouTube in MP3 format.</li> | ||
<li>Then on the bottom-left links of the songs will appear, you can simply click on it to store it on your device.</li> | ||
</ol> | ||
<p>Enjoy your favorite Spotify playlists offline with Spotify2Mp3!</p> | ||
</section> | ||
|
||
<!-- Playlist Form --> | ||
<form id="playlist-form"> | ||
<label for="playlist-link">Enter Playlist Link:</label><br> | ||
<input type="text" id="playlist-link" name="playlist_link"><br><br> | ||
<button type="submit">Submit</button> | ||
</form> | ||
|
||
<!-- Progress Section --> | ||
<div id="progress"> | ||
<!-- Progress will be shown here --> | ||
</div> | ||
|
||
<!-- Downloads Section --> | ||
<div id="downloads"> | ||
<!-- Download links will be appended here --> | ||
</div> | ||
|
||
<script src="{{ url_for('static', path='script.js') }}"></script> | ||
</body> | ||
</html> |