Music Player using Django
In this article, we’ll build a Music Player using Django. To get started, the first thing we’ll do is create an account, just like with other music players. After creating the account, we’ll log in. Once successfully logged in, we’ll be redirected to the home page. On the home page, we can search for songs based on our preferences we used an API to fetch songs. Additionally, we’ll have the option to add songs, and the corresponding pictures related to those songs will be displayed on the home page. We can easily play the songs and manage the login system through the admin panel.
Music Player using Django
Below is the step-by-step guide to playing music player
To install Django follow these steps.
Starting the Project Folder
To start the project use this command
django-admin startproject music_player_project
cd core
we create two apps for creating the app use this command
python manage.py startapp api
python manage.py startapp music_player_app
Now add this app to the ‘settings.py’
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"music_player_app",
"api",
"rest_framework"
]
File Structure
Setting Necessary Files
api/models.py :Below,code defines a Django model named “Song” with fields for id, title, category, artist, audio file, and audio image, along with their respective properties, and a string representation method returning the song title.
Python3
# api/models.py from django.db import models class Song(models.Model): id = models.AutoField(primary_key = True ) title = models.CharField(max_length = 100 ) categorie = models.CharField(max_length = 100 , null = True , default = None ) artist = models.CharField(max_length = 100 ) audio_file = models.FileField(upload_to = 'audio/' ) audio_img = models.FileField(upload_to = 'audio_img/' ) def __str__( self ): return self .title |
music_player_app/models.py : below code defines a custom Django user model named “CustomUser” that extends the AbstractUser model, adding fields for groups and user permissions, allowing users to be associated with multiple groups and permissions.
Python3
# music_player_app/models.py from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): groups = models.ManyToManyField( 'auth.Group' , related_name = 'customuser_set' , related_query_name = 'user' ) user_permissions = models.ManyToManyField( 'auth.Permission' , related_name = 'customuser_set' , related_query_name = 'user' ) |
api/views.py : below code defines two Django Rest Framework views, “SongListCreateView” for handling the listing and creation of songs, and “SongDetailView” for handling the retrieval, update, and deletion of individual songs, using the “Song” model and its corresponding serializer “SongSerializer.”
Python3
# api/views.py from rest_framework import generics from .models import Song from .serializers import SongSerializer class SongListCreateView(generics.ListCreateAPIView): queryset = Song.objects. all () serializer_class = SongSerializer class SongDetailView(generics.RetrieveUpdateDestroyAPIView): queryset = Song.objects. all () serializer_class = SongSerializer |
music_player_app/views.py : below code defines Django views for user registration, login, and authenticated user actions such as viewing the home page, adding a song, and updating a song. It uses forms for registration and login, and requires authentication for certain actions.
Python3
# music_player_app/views.py from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from django.contrib.auth import login from .forms import RegistrationForm, LoginForm def register(request): if request.method = = 'POST' : form = RegistrationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect( 'home' ) else : form = RegistrationForm() return render(request, 'music_player_app/register.html' , { 'form' : form}) def user_login(request): if request.method = = 'POST' : form = LoginForm(request, request.POST) if form.is_valid(): user = form.get_user() login(request, user) return redirect( 'home' ) else : form = LoginForm() return render(request, 'music_player_app/login.html' , { 'form' : form}) @login_required def index(request): return render(request, 'music_player_app/index.html' ) @login_required def AddSong(request): return render(request, 'music_player_app/AddSong.html' ) @login_required def UpdateSong(request, pk): return render(request, 'music_player_app/UpdateSong.html' ) |
api/serializers.py : below code defines a Django Rest Framework serializer, “SongSerializer,” using the model “Song” and including all fields for serialization.
Python3
# api/serializers.py from rest_framework import serializers from .models import Song class SongSerializer(serializers.ModelSerializer): class Meta: model = Song fields = '__all__' |
music_palyer_app/forms.py : below code defines a registration form (“RegistrationForm”) and a login form (“LoginForm”) for a custom user model, extending default Django forms for user creation and authentication.
Python3
# music_player_app/forms.py from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from .models import CustomUser class RegistrationForm(UserCreationForm): class Meta: model = CustomUser fields = [ 'username' , 'password1' , 'password2' ] class LoginForm(AuthenticationForm): class Meta: model = CustomUser fields = [ 'username' , 'password' ] |
api/urls.py : below code sets up Django URL patterns for song listing/creation and song detail using “SongListCreateView” and “SongDetailView” views, respectively.
Python3
# api/urls.py from django.urls import path from .views import SongListCreateView, SongDetailView urlpatterns = [ path( ' ' , SongListCreateView.as_view(), name = 'song-list-create' ), path( 'songs/<int:pk>/' , SongDetailView.as_view(), name = 'song-detail' ), ] |
music_player_app/urls.py : below code defines Django URL patterns for user authentication, registration, login, logout, and views for the home page, updating a song, and adding a song in the music player app. It includes paths for default authentication views, custom registration and login views, and specific URLs for app features.
Python3
# music_player_app/urls.py from django.contrib import admin from django.urls import path, include from django.contrib.auth.views import LogoutView from music_player_app.views import register, user_login from music_player_app.views import index, UpdateSong, AddSong urlpatterns = [ path( 'accounts/' , include( 'django.contrib.auth.urls' )), path( 'register/' , register, name = 'register' ), path( 'login/' , user_login, name = 'login' ), path( 'logout/' , LogoutView.as_view(next_page = '/login/' ), name = 'logout' ), path( 'home' , index, name = 'home' ), path( 'UpdateSong/<int:pk>' , UpdateSong, name = 'UpdateSong' ), path( 'AddSong' , AddSong, name = 'AddSong' ), ] |
Creating GUI
templates/music_player_app/html Files
AddSong.html : The HTML document presents a form for adding a song to a music player app, styled with Bootstrap. JavaScript functions validate the form, collect input data, and send a POST request to a Django API endpoint for song addition, with CSRF token handling.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" /> < title >Add Song</ title > <!-- Bootstrap CSS --> < link rel = "stylesheet" href = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity = "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin = "anonymous" /> </ head > < body > < div class = "container mt-5" > < h2 >Add Song</ h2 > < form id = "songForm" > < div class = "form-group" > < label for = "title" >Title:</ label > < input type = "text" class = "form-control" id = "title" name = "title" required /> < div class = "invalid-feedback" >Title is required.</ div > </ div > < div class = "form-group" > < label for = "artist" >Artist:</ label > < input type = "text" class = "form-control" id = "artist" name = "artist" required /> < div class = "invalid-feedback" >Artist is required.</ div > </ div > < div class = "form-group" > < label for = "categorie" >Categorie:</ label > < input type = "text" class = "form-control" id = "categorie" name = "categorie" required /> < div class = "invalid-feedback" >Categorie is required.</ div > </ div > < div class = "form-group" > < label for = "audio_file" >Audio File:</ label > < input type = "file" class = "form-control-file" id = "audio_file" name = "audio_file" accept = "audio/*" required /> < div class = "invalid-feedback" >Audio file is required.</ div > </ div > < div class = "form-group" > < label for = "audio_img" >Audio Img:</ label > < input type = "file" class = "form-control-file" id = "audio_img" name = "audio_img" required /> < div class = "invalid-feedback" >Audio Img is required.</ div > </ div > < button type = "button" class = "btn btn-primary" onclick = "postData()" >Submit</ button > </ form > </ div > <!-- Bootstrap JS and dependencies --> < script src = "https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin = "anonymous" ></ script > < script src = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin = "anonymous" ></ script > < script src = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin = "anonymous" ></ script > < script > function postData() { // Validate the form if (validateForm()) { let title = document.getElementById('title').value let artist = document.getElementById('artist').value let categorie = document.getElementById('categorie').value let audio_file = document.getElementById('audio_file').files[0] let audio_img = document.getElementById('audio_img').files[0] const formData = new FormData() formData.append('title', title) formData.append('artist', artist) formData.append('categorie', categorie) formData.append('audio_file', audio_file) formData.append('audio_img', audio_img) fetch('http://127.0.0.1:8000/api/songs/', { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken') }, body: formData }) .then((response) => response.json()) .then((result) => { console.log('Success:', result) // Refresh the page after successful submission location.reload() }) .catch((error) => { // Handle errors as needed console.error('Error:', error) }) } } function validateForm() { const title = document.getElementById('title').value const artist = document.getElementById('artist').value const categorie = document.getElementById('categorie').value const audio_file = document.getElementById('audio_file').files[0] const audio_img = document.getElementById('audio_img').files[0] const invalidFeedbacks = document.querySelectorAll('.invalid-feedback') invalidFeedbacks.forEach((element) => { element.style.display = 'none' }) let isValid = true if (!title.trim()) { document.getElementById('title').nextElementSibling.style.display = 'block' isValid = false } if (!categorie.trim()) { document.getElementById('categorie').nextElementSibling.style.display = 'block' isValid = false } if (!artist.trim()) { document.getElementById('artist').nextElementSibling.style.display = 'block' isValid = false } if (!audio_file) { document.getElementById('audio_file').nextElementSibling.style.display = 'block' isValid = false } if (!audio_img) { document.getElementById('audio_img').nextElementSibling.style.display = 'block' isValid = false } return isValid } // Function to get CSRF token from cookies function getCookie(name) { var cookieValue = null if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';') for (var i = 0; i < cookies.length ; i++) { var cookie = cookies [i].trim() if (cookie.substring(0, name.length + 1) === name + '=') { cookieValue = decodeURIComponent (cookie.substring(name.length + 1)) break } } } return cookieValue } </script> </ body > </ html > |
index.html : The HTML document implements a music player interface using Bootstrap, featuring an audio player, playlist, search bar, and buttons for song management. JavaScript functions handle song retrieval, playback, deletion, and redirection to add/update songs. A search function interacts with the Saavn API to display results, and there’s a logout function.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" /> < title >Music Player</ title > < link rel = "stylesheet" href = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" /> < style > body { font-family: 'Arial', sans-serif; text-align: center; margin: 50px; } #audioPlayer { width: 100%; max-width: 400px; margin: 20px auto; } #playlist { list-style: none; padding: 0; } #playlist li { margin: 5px; cursor: pointer; transition: transform 0.3s ease-in-out; display: flex; justify-content: space-between; align-items: center; } #playlist li:hover { transform: scale(1.1); } </ style > </ head > < body > < div class = "container" > < div class = "player" > < img style = "width:250px; height:250px;" src = "https://th.bing.com/th/id/OIP.bLCU8HwL546JIVk9vLV3NAHaHa?rs=1&pid=ImgDetMain" alt id = "audioPlayerimg" /> < br /> < audio id = "audioPlayer" class = "w-100" controls>Your browser does not support the audio element.</ audio > </ div > < label for = "Search" >Search Song:</ label > < input type = "text" id = "Search" placeholder = "Enter song name" /> < button onclick = "SearchSongs()" >Search</ button > < ul id = "playlist" class = "list-group" ></ ul > < button type = "button" class = "btn btn-primary mt-3" data-toggle = "modal" data-target = "#addSongModal" onclick = "AddSong()" >Add Song</ button > < button type = "button" class = "btnlogout btn btn-danger mt-3" data-toggle = "modal" data-target = "#addSongModal" onclick = "Logout()" >Logout</ button > </ div > < script > document.addEventListener('DOMContentLoaded', function () { fetchSongs(); }); const audioPlayer = document.getElementById('audioPlayer'); const audioPlayerimg = document.getElementById('audioPlayerimg'); const playlist = document.getElementById('playlist'); function fetchSongs() { fetch('/api/songs/') .then(response => response.json()) .then(songs => { songs.forEach(song => { playlist.innerHTML += `< li class = "list-group-item" onclick = "playSong('${song.audio_file}','${song.audio_img}')" > < span >${song.title} - ${song.artist}-${song.categorie}</ span > < div > < button class = "btn btn-info btn-sm" onclick = "UpdateSong(${song.id})" >Update</ button > < button class = "btn btn-danger btn-sm" onclick = "deleteSong(${song.id})" >Delete</ button > </ div ></ li >`; }); }) .catch(error => console.error('Error fetching data:', error)); } function playSong(songSrc, songimg) { console.log(songSrc); console.log(songimg); document.querySelectorAll('#playlist li').forEach((item) => { item.style.transform = 'scale(1)'; }); event.target.style.transform = 'scale(1.2)'; audioPlayer.src = songSrc; console.log(audioPlayerimg); audioPlayerimg.src = songimg; audioPlayer.play(); } function deleteSong(songId) { if (confirm('Are you sure you want to delete this song?')) { fetch(`/api/songs/${songId}/`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}', }, }) .then(response => { if (response.ok) { location.reload(); } else { alert('Failed to delete the song. Please try again.'); } }) .catch(error => { console.error('Error:', error); }); } } function AddSong() { window.location.href = `/AddSong`; } function UpdateSong(itemId) { window.location.href = `/UpdateSong/${itemId}`; } function Logout(itemId) { window.location.href = `/logout/`; } function SearchSongs() { const SearchSong = document.getElementById('Search').value.toLowerCase(); // Saavn API endpoint for searching songs const saavnSearchUrl = 'https://saavn.me/search/songs'; // Query parameters for the search const params = { query: SearchSong, }; // Request headers const headers = { 'Content-Type': 'application/json', }; // Make the GET request to search for songs fetch(`${saavnSearchUrl}?${new URLSearchParams(params)}`, { method: 'GET', headers: headers, }) .then(response => response.json()) .then(songData => { playlist.innerHTML = ''; for (const song of songData.data.results) { const songName = song.name; const artistName = song.primaryArtists; const highestQualityDownloadUrl = song.downloadUrl.find(downloadUrl => downloadUrl.quality === '320kbps'); const image150x150 = song.image.find(image => image.quality === '150x150'); const lowestQualityImage = song.image.find(image => image.quality === '50x50'); playlist.innerHTML += `< li class = "list-group-item" onclick = "playSong('${highestQualityDownloadUrl.link}','${image150x150.link}')" >< span > < img src = "${lowestQualityImage.link}" > ${songName} by ${artistName}</ span > </ li >`; } }) .catch(error => console.error('Error:', error)); } </ script > </ body > </ html > |
login.html : The HTML document presents a login form using Bootstrap styling, with a title, input fields for username and password, and a login button. It includes a link to register for an account if the user doesn’t have one. The form is submitted via POST request and includes a CSRF token for security.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title >Login</ title > < link rel = "stylesheet" href = "https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" > </ head > < body > < div class = "container" > < div class = "row" > < div class = "col-md-6 offset-md-3" > < h2 class = "mt-4 mb-4" >Login</ h2 > < form method = "post" class = "mb-4" > {% csrf_token %} {{ form.as_p }} < button type = "submit" class = "btn btn-primary" >Login</ button > </ form > < p >Don't have an account? < a href = "{% url 'register' %}" class = "btn btn-link" >Register here</ a >.</ p > </ div > </ div > </ div > </ body > </ html > |
register.html : The HTML document displays a registration form using Bootstrap styling, including fields for username, password, and password confirmation. The form is submitted via POST request and contains a CSRF token for security. Additionally, there’s a link to the login page for users who already have an account.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" /> < title >Register</ title > < link rel = "stylesheet" href = "https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" /> </ head > < body > < div class = "container" > < div class = "row" > < div class = "col-md-6 offset-md-3" > < h2 class = "mt-4 mb-4" >Register</ h2 > < form method = "post" class = "mb-4" > {% csrf_token %} {{ form.as_p }} < button type = "submit" class = "btn btn-primary" >Register</ button > </ form > < p >Already have an account? < a href = "{% url 'login' %}" class = "btn btn-link" >Login here</ a >.</ p > </ div > </ div > </ div > </ body > </ html > |
UpdateSong.html : The HTML document is an update form for a song in a music player app, utilizing Bootstrap. JavaScript fetches existing song data, populates the form fields, and handles form submission with an API call for updating. CSRF token is included, and upon success, the user is redirected to the home page.
HTML
<!-- music_player_app/templates/music_player_app/update_form.html --> <!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" /> < title >Update Song</ title > < link rel = "stylesheet" href = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" /> </ head > < body > < div class = "container mt-5" > < h2 >Update Song</ h2 > < form id = "updateForm" > < div class = "form-group" > < label for = "title" >Title</ label > < input type = "text" class = "form-control" id = "title" name = "title" required /> < label for = "artist" >Artist</ label > < input type = "text" class = "form-control" id = "artist" name = "artist" required /> < label for = "categorie" >Categorie</ label > < input type = "text" class = "form-control" id = "categorie" name = "categorie" required /> < label for = "audio_file" >Audio File</ label > < input type = "file" class = "form-control-file" id = "audio_file" name = "audio_file" accept = "audio/*" required /> < label for = "audio_img" >Audio Img</ label > < input type = "file" class = "form-control-file" id = "audio_img" name = "audio_img" required /> </ div > <!-- Add other form fields as needed --> < button type = "submit" class = "btn btn-primary" >Update</ button > </ form > </ div > < script > document.addEventListener('DOMContentLoaded', function () { // Fetch song data and fill the form fields const url = window.location.pathname.split('/') let songId = url[2] fetch(`/api/songs/${songId}/`) .then((response) => response.json()) .then((data) => { document.getElementById('title').value = data.title document.getElementById('artist').value = data.artist document.getElementById('categorie').value = data.categorie document.getElementById('audio_file').value = '' document.getElementById('audio_img').value = '' }) .catch((error) => console.error('Error fetching song data:', error)) // Submit form with API call document.getElementById('updateForm').addEventListener('submit', function (event) { event.preventDefault() // Get form data const formData = new FormData(this) // Make API call to update song fetch(`/api/songs/${songId}/`, { method: 'PUT', headers: { 'X-CSRFToken': getCookie('csrftoken') // Ensure to include CSRF token }, body: formData }) .then((response) => response.json()) .then((data) => { alert('Song updated successfully!') window.location.href = '/home' // Redirect to the song list page }) .catch((error) => console.error('Error updating song:', error)) }) }) // Function to get CSRF token from cookies function getCookie(name) { var cookieValue = null if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';') for (var i = 0; i < cookies.length ; i++) { var cookie = cookies [i].trim() if (cookie.substring(0, name.length + 1) === name + '=') { cookieValue = decodeURIComponent (cookie.substring(name.length + 1)) break } } } return cookieValue } </script> </ body > </ html > |
api/admin.py : here we register the models of api app.
Python3
from django.contrib import admin from .models import Song class SongAdmin(admin.ModelAdmin): list_display = ( 'id' , 'title' , 'categorie' , 'artist' , ) search_fields = ( 'title' , 'artist' ) admin.site.register(Song, SongAdmin) |
music_player_app/admin.py : here , we register the app of music_player_app app.
Python3
# music_player_app/admin.py from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import CustomUser admin.site.register(CustomUser, UserAdmin) |
Deployement of the Project
Run these commands to apply the migrations:
python3 manage.py makemigrations
python3 manage.py migrate
Run the server with the help of following command:
python3 manage.py runserver
Output