├── songs
├── __init__.py
├── migrations
│ ├── __init__.py
│ ├── 0002_all_playlist_apple_music_all_playlist_spotify.py
│ └── 0001_initial.py
├── tests.py
├── .DS_Store
├── admin.py
├── apps.py
├── exceptions.py
├── client_secret.json
├── command.py
├── urls.py
├── models.py
├── views.py
├── apple_music.py
└── spotify.py
├── playlist_project
├── __init__.py
├── wsgi.py
├── urls.py
└── settings.py
├── .DS_Store
├── db.sqlite3
├── .github
├── dependabot.yml
└── workflows
│ ├── pylint.yml
│ └── codeql-analysis.yml
├── templates
├── registration
│ ├── login.html
│ └── change-password.html
├── new_apple_music.html
├── new_spotify.html
├── spotify.html
├── index.html
├── apple_music.html
├── all_spotify.html
├── all_apple_music.html
├── all_spotify_playlist-results-partial.html
├── all_apple_music_playlist-results-partial.html
├── all_spotify_playlist_search.html
├── all_apple_music_playlist_search.html
└── base.html
├── Pipfile
├── manage.py
├── LICENSE
├── CODE_OF_CONDUCT.md
├── README.md
└── Pipfile.lock
/songs/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/playlist_project/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/songs/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rohan-cod/Music_Playlist/HEAD/.DS_Store
--------------------------------------------------------------------------------
/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rohan-cod/Music_Playlist/HEAD/db.sqlite3
--------------------------------------------------------------------------------
/songs/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/songs/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rohan-cod/Music_Playlist/HEAD/songs/.DS_Store
--------------------------------------------------------------------------------
/songs/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/songs/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class SongsConfig(AppConfig):
5 | name = 'songs'
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "pip"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | open-pull-requests-limit: 10
8 |
--------------------------------------------------------------------------------
/songs/exceptions.py:
--------------------------------------------------------------------------------
1 | class ResponseException(Exception):
2 | def __init__(self, status_code, message=""):
3 | self.message = message
4 | self.status_code = status_code
5 |
6 | def __str__(self):
7 | return self.message + f"Response gave status code {self.status_code}"
--------------------------------------------------------------------------------
/songs/client_secret.json:
--------------------------------------------------------------------------------
1 | {
2 | "web": {
3 | "client_id": "",
4 | "client_secret": "",
5 | "redirect_uris": [""],
6 | "auth_uri": "https://accounts.google.com/o/oauth2/auth",
7 | "token_uri": "https://accounts.google.com/o/oauth2/token",
8 | "YOUR_API_KEY": ""
9 | }
10 | }
--------------------------------------------------------------------------------
/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %}Log In{% endblock title %}
4 |
5 | {% block content %}
6 |
7 |
Log In
8 |
9 |
14 |
15 | {% endblock content %}
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | django = "==3.2.5"
10 | google-auth-oauthlib = "*"
11 | google-api-python-client = "*"
12 | youtube-dl = "*"
13 | exception = "*"
14 | schedule = "*"
15 | gunicorn = "==20.1.0"
16 |
17 | [requires]
18 | python_version = "3.7"
19 |
--------------------------------------------------------------------------------
/templates/new_apple_music.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 | New Playlist for
5 |
11 | {% endblock content %}
--------------------------------------------------------------------------------
/templates/new_spotify.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 | New Playlist for
5 |
11 | {% endblock content %}
--------------------------------------------------------------------------------
/playlist_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for playlist_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'playlist_project.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/templates/registration/change-password.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %}Password Change{% endblock title %}
4 |
5 | {% block content %}
6 | Password change
7 | Please enter your old password, for security's sake, and then enter your
8 | new password twice so we can verify you typed it in correctly.
9 |
10 |
15 | {% endblock content %}
--------------------------------------------------------------------------------
/.github/workflows/pylint.yml:
--------------------------------------------------------------------------------
1 | name: Pylint
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Set up Python 3.9
13 | uses: actions/setup-python@v2
14 | with:
15 | python-version: 3.9
16 | - name: Install dependencies
17 | run: |
18 | python -m pip install --upgrade pip
19 | pip install pylint
20 | - name: Analysing the code with pylint
21 | run: |
22 | pylint `ls -R|grep .py$|xargs`
23 |
--------------------------------------------------------------------------------
/templates/spotify.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 | Add Songs to
5 |
10 |
11 | + Add Playlist | All Playlists
14 | {% endblock content %}
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %}Home{% endblock title %}
4 |
5 | {% block content %}
6 |
7 |
Music Playlist App
8 |
A Django app to convert YouTube liked videos to spotify | apple music playlist.
9 |
10 | |
13 |
14 |
15 | {% endblock content %}
--------------------------------------------------------------------------------
/templates/apple_music.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 | Add Songs to
5 |
11 |
12 | + Add Playlist | All Playlists
15 | {% endblock content %}
--------------------------------------------------------------------------------
/songs/command.py:
--------------------------------------------------------------------------------
1 | import schedule
2 | import time
3 | from .models import All_Playlist_Apple_Music, All_Playlist_Spotify
4 | from .spotify import ToPlaylist
5 | from .apple_music import A_ToPlaylist
6 |
7 | cp = ToPlaylist()
8 | cp_ = A_ToPlaylist()
9 |
10 |
11 | def comm():
12 | li = cp.get_all_playlist()
13 | for i in li:
14 | All_Playlist_Spotify.objects.update_or_create(
15 | pla_name = i
16 | )
17 |
18 | li_ = cp.get_all_playlist()
19 | for i_ in li_:
20 | All_Playlist_Apple_Music.objects.update_or_create(
21 | pla_name = i_
22 | )
23 |
24 | schedule.every(10).minutes.do(comm)
25 |
26 | while True:
27 | schedule.run_pending()
28 | time.sleep(1)
29 |
30 |
31 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'playlist_project.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 | schedule:
9 | - cron: '29 16 * * 3'
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | runs-on: ubuntu-latest
15 | permissions:
16 | actions: read
17 | contents: read
18 | security-events: write
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | language: [ 'python' ]
24 | steps:
25 | - name: Checkout repository
26 | uses: actions/checkout@v2
27 |
28 | - name: Initialize CodeQL
29 | uses: github/codeql-action/init@v1
30 | with:
31 | languages: ${{ matrix.language }}
32 | - name: Autobuild
33 | uses: github/codeql-action/autobuild@v1
34 |
35 |
36 | - name: Perform CodeQL Analysis
37 | uses: github/codeql-action/analyze@v1
38 |
--------------------------------------------------------------------------------
/songs/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from .views import HomePageView, Spotify, Apple_Music, New_Spotify, New_Apple_Music, search_spotify_view, search_apple_view, All_Spotify_Playlist, All_Apple_Music_Playlist
4 |
5 |
6 | urlpatterns = [
7 | path('', HomePageView.as_view(), name='home'),
8 | path('spotify/', Spotify.as_view(), name='spotify'),
9 | path('apple_music/', Apple_Music.as_view(), name='apple_music'),
10 | path('new_spotify/', New_Spotify.as_view(), name='new_spotify'),
11 | path('new_apple_music/', New_Apple_Music.as_view(), name='new_apple_music'),
12 | path('spotify_playlist/', All_Spotify_Playlist.as_view(), name='spotify_playlist'),
13 | path('apple_music_playlist/', All_Apple_Music_Playlist.as_view(), name='apple_music_playlist'),
14 | path('search_spotify/', search_spotify_view, name='search_spotify'),
15 | path('search_apple_music/', search_apple_view, name='search_apple_music'),
16 | ]
--------------------------------------------------------------------------------
/songs/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db import models
3 | from django.conf import settings
4 | from django.contrib.auth import get_user_model
5 | from django.urls import reverse
6 |
7 |
8 | class All_Playlist_Apple_Music(models.Model):
9 | pla_name = models.CharField(max_length=100)
10 |
11 | class All_Playlist_Spotify(models.Model):
12 | pla_name = models.CharField(max_length=100)
13 |
14 | class Playlist_Spotify(models.Model):
15 | playlist_name = models.CharField(max_length=100)
16 |
17 | class Playlist_Apple_Music(models.Model):
18 | playlist_name = models.CharField(max_length=100)
19 |
20 |
21 | class New_Playlist_Spotify(models.Model):
22 |
23 | description = models.CharField(max_length=200)
24 | playlist_name = models.CharField(max_length=100)
25 |
26 | class New_Playlist_Apple_Music(models.Model):
27 |
28 | description = models.CharField(max_length=200)
29 | playlist_name = models.CharField(max_length=100)
--------------------------------------------------------------------------------
/songs/migrations/0002_all_playlist_apple_music_all_playlist_spotify.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.10 on 2020-04-11 12:00
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('songs', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.CreateModel(
14 | name='All_Playlist_Apple_Music',
15 | fields=[
16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17 | ('pla_name', models.CharField(max_length=100)),
18 | ],
19 | ),
20 | migrations.CreateModel(
21 | name='All_Playlist_Spotify',
22 | fields=[
23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24 | ('pla_name', models.CharField(max_length=100)),
25 | ],
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Rohan Gupta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/templates/all_spotify.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %}All Playlists Spotify{% endblock title %}
3 |
4 | {% block content %}
5 | Music Playlist App
6 |
7 |
8 | All Playlists |
9 |
10 | {% for playlist in object_list %}
11 |
12 |
15 |
16 |
17 |
18 |
19 | {% endfor %}
20 |
21 | {% if is_paginated %}
22 |
23 |
32 |
33 | {% endif %}
34 |
35 | {% endblock content %}
--------------------------------------------------------------------------------
/templates/all_apple_music.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %}All Playlists Apple Music{% endblock title %}
3 |
4 | {% block content %}
5 | Music Playlist App
6 |
7 |
8 | All Playlists |
9 |
10 | {% for playlist in object_list %}
11 |
12 |
15 |
16 |
17 |
18 |
19 | {% endfor %}
20 |
21 | {% if is_paginated %}
22 |
23 |
32 |
33 | {% endif %}
34 |
35 | {% endblock content %}
--------------------------------------------------------------------------------
/playlist_project/urls.py:
--------------------------------------------------------------------------------
1 | """playlist_project URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import url, include
17 | from django.contrib import admin
18 | from django.conf import settings
19 | from django.urls import path, include, reverse_lazy
20 | from django.contrib.auth.views import LoginView
21 | from django.contrib.auth import views as auth_views
22 | from songs import views
23 |
24 | urlpatterns = [
25 | path(
26 | 'change-password/',
27 | auth_views.PasswordChangeView.as_view(
28 | template_name='registration/change-password.html',
29 | success_url = reverse_lazy('')
30 | ),
31 | name='change_password'
32 | ),
33 | path('admin/', admin.site.urls),
34 | path('users/', include('django.contrib.auth.urls')),
35 | url(r'^login/$',LoginView.as_view(), name='login'),
36 | path('', include('songs.urls')),
37 |
38 | ]
39 |
--------------------------------------------------------------------------------
/songs/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.10 on 2020-04-10 17:12
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='New_Playlist_Apple_Music',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('playlist_name', models.CharField(max_length=100)),
19 | ('description', models.CharField(max_length=200)),
20 | ],
21 | ),
22 | migrations.CreateModel(
23 | name='New_Playlist_Spotify',
24 | fields=[
25 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
26 | ('playlist_name', models.CharField(max_length=100)),
27 | ('description', models.CharField(max_length=200)),
28 | ],
29 | ),
30 | migrations.CreateModel(
31 | name='Playlist_Apple_Music',
32 | fields=[
33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
34 | ('playlist_name', models.CharField(max_length=100)),
35 | ],
36 | ),
37 | migrations.CreateModel(
38 | name='Playlist_Spotify',
39 | fields=[
40 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
41 | ('playlist_name', models.CharField(max_length=100)),
42 | ],
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/templates/all_spotify_playlist-results-partial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {% if all_spotify_playlist %}
7 | {% for playlist in object_list %}
8 |
9 |
12 |
13 |
14 |
15 |
16 | {% endfor %}
17 |
18 | {% else %}
19 |
20 |
23 |
24 | {% endif %}
25 |
26 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/templates/all_apple_music_playlist-results-partial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {% if all_apple_music_playlist %}
7 | {% for playlist in object_list %}
8 |
9 |
12 |
13 |
14 |
15 |
16 | {% endfor %}
17 |
18 | {% else %}
19 |
20 |
23 |
24 | {% endif %}
25 |
26 |
27 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/templates/all_spotify_playlist_search.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Playlists
5 |
6 |
7 |
8 | {# icon and search-box #}
9 |
10 |
11 |
12 |
13 |
14 | {# news-list section #}
15 |
16 | {% include 'all_spotify_playlist-results-partial.html' %}
17 |
18 |
19 |
20 |
21 |
36 | {% endblock %}
37 |
38 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/templates/all_apple_music_playlist_search.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Playlists
5 |
6 |
7 |
8 | {# icon and search-box #}
9 |
10 |
11 |
12 |
13 |
14 | {# news-list section #}
15 |
16 | {% include 'all_apple_music_playlist-results-partial.html' %}
17 |
18 |
19 |
20 |
21 |
36 | {% endblock %}
37 |
38 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at rohaninjmu@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/playlist_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for playlist_project project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.2.10.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.2/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '=qz4sr^9%n2u(yq)nc!gek$mz78r&ay%_r@$k%8@5+i_gfd&jg'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = False
27 |
28 | ALLOWED_HOSTS = ['*']
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'songs.apps.SongsConfig',
41 | ]
42 |
43 | MIDDLEWARE = [
44 | 'django.middleware.security.SecurityMiddleware',
45 | 'django.contrib.sessions.middleware.SessionMiddleware',
46 | 'django.middleware.common.CommonMiddleware',
47 | 'django.middleware.csrf.CsrfViewMiddleware',
48 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
49 | 'django.contrib.messages.middleware.MessageMiddleware',
50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51 | ]
52 |
53 | ROOT_URLCONF = 'playlist_project.urls'
54 |
55 | TEMPLATES = [
56 | {
57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58 | 'DIRS': [os.path.join(BASE_DIR, "templates")],
59 | 'APP_DIRS': True,
60 | 'OPTIONS': {
61 | 'context_processors': [
62 | 'django.template.context_processors.debug',
63 | 'django.template.context_processors.request',
64 | 'django.contrib.auth.context_processors.auth',
65 | 'django.contrib.messages.context_processors.messages',
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = 'playlist_project.wsgi.application'
72 |
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
76 |
77 | DATABASES = {
78 | 'default': {
79 | 'ENGINE': 'django.db.backends.sqlite3',
80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
81 | }
82 | }
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
91 | },
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
100 | },
101 | ]
102 |
103 |
104 | # Internationalization
105 | # https://docs.djangoproject.com/en/2.2/topics/i18n/
106 |
107 | LANGUAGE_CODE = 'en-us'
108 |
109 | TIME_ZONE = 'Asia/Kolkata'
110 |
111 | USE_I18N = True
112 |
113 | USE_L10N = True
114 |
115 | USE_TZ = True
116 |
117 |
118 | # Static files (CSS, JavaScript, Images)
119 | # https://docs.djangoproject.com/en/2.2/howto/static-files/
120 |
121 | STATIC_URL = '/static/'
122 | LOGIN_URL = 'login'
123 | LOGOUT_URL = 'logout'
124 | LOGIN_REDIRECT_URL = 'home'
125 | LOGOUT_REDIRECT_URL = 'home'
126 |
127 | CRISPY_TEMPLATE_PACK = 'bootstrap4'
128 |
--------------------------------------------------------------------------------
/songs/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
2 | from django.views.generic import ListView, TemplateView, DetailView
3 | from .models import Playlist_Spotify, Playlist_Apple_Music, New_Playlist_Spotify, New_Playlist_Apple_Music, All_Playlist_Spotify, All_Playlist_Apple_Music
4 | from django.views.generic.edit import CreateView, UpdateView, DeleteView
5 | from django.urls import reverse_lazy
6 | from django.template.loader import render_to_string
7 | from django.http import JsonResponse
8 | from django.shortcuts import render
9 | from .spotify import ToPlaylist
10 | from .apple_music import A_ToPlaylist
11 |
12 |
13 |
14 |
15 |
16 |
17 | class HomePageView(TemplateView):
18 | template_name = 'index.html'
19 |
20 | class Spotify(LoginRequiredMixin, CreateView):
21 | model = Playlist_Spotify
22 | template_name = 'spotify.html'
23 | login_url = 'login'
24 | fields = {'playlist_name',}
25 | def submit(request):
26 | if request.method == 'POST':
27 | pl_name = request.POST['playlist_name']
28 | cp = ToPlaylist()
29 | cp.add_song_to_playlist(pl_name)
30 |
31 | class Apple_Music(LoginRequiredMixin, CreateView):
32 | model = Playlist_Apple_Music
33 | template_name = 'apple_music.html'
34 | login_url = 'login'
35 | fields = {'playlist_name',}
36 | def submit(request):
37 | if request.method == 'POST':
38 | pl_name = request.POST['playlist_name']
39 | cp = A_ToPlaylist()
40 | cp.add_song_to_playlist(pl_name)
41 |
42 | class All_Spotify_Playlist(LoginRequiredMixin, ListView):
43 | template_name = 'all_spotify.html'
44 | model = All_Playlist_Spotify
45 | paginate_by = 5
46 | login_url = 'login'
47 |
48 | class All_Apple_Music_Playlist(LoginRequiredMixin, ListView):
49 | template_name = 'all_apple_music.html'
50 | model = All_Playlist_Apple_Music
51 | paginate_by = 5
52 | login_url = 'login'
53 |
54 | class New_Spotify(LoginRequiredMixin, CreateView):
55 | model = New_Playlist_Spotify
56 | template_name = 'new_spotify.html'
57 | login_url = 'login'
58 | fields = {'description', 'playlist_name',}
59 | def submit(request):
60 | if request.method == 'POST':
61 | pl_name = request.POST['playlist_name']
62 | des = request.POST['description']
63 | cp = ToPlaylist()
64 | cp.create_playlist(pl_name, des)
65 |
66 | class New_Apple_Music(LoginRequiredMixin, CreateView):
67 | model = New_Playlist_Apple_Music
68 | template_name = 'new_apple_music.html'
69 | login_url = 'login'
70 | fields = {'description', 'playlist_name',}
71 | def submit(request):
72 | if request.method == 'POST':
73 | pl_name=request.POST['playlist_name']
74 | des = request.POST['description']
75 | cp = A_ToPlaylist()
76 | cp.create_playlist(pl_name, des)
77 |
78 |
79 | def search_apple_view(request):
80 | ctx = {}
81 | url_parameter = request.GET.get("q")
82 |
83 | if url_parameter:
84 | all_apple_music_playlist = All_Playlist_Apple_Music.objects.filter(pla_name__icontains=url_parameter)
85 | else:
86 | all_apple_music_playlist = All_Playlist_Apple_Music.objects.all()
87 |
88 | ctx["all_apple_music_playlist"] = all_apple_music_playlist
89 |
90 | if request.is_ajax():
91 | html = render_to_string(
92 | template_name="all_apple_music_playlist-results-partial.html",
93 | context={"all_apple_music_playlist": all_apple_music_playlist}
94 | )
95 |
96 | data_dict = {"html_from_view": html}
97 |
98 | return JsonResponse(data=data_dict, safe=False)
99 |
100 | return render(request, "all_apple_music_playlist_search.html", context=ctx)
101 |
102 |
103 | def search_spotify_view(request):
104 | ctx = {}
105 | url_parameter = request.GET.get("q")
106 |
107 | if url_parameter:
108 | all_spotify_playlist = All_Playlist_Spotify.objects.filter(pla_name__icontains=url_parameter)
109 | else:
110 | all_spotify_playlist = All_Playlist_Spotify.objects.all()
111 |
112 | ctx["all_spotify_playlist"] = all_spotify_playlist
113 |
114 | if request.is_ajax():
115 | html = render_to_string(
116 | template_name="all_spotify_playlist-results-partial.html",
117 | context={"all_spotify_playlist": all_spotify_playlist}
118 | )
119 |
120 | data_dict = {"html_from_view": html}
121 |
122 | return JsonResponse(data=data_dict, safe=False)
123 |
124 | return render(request, "all_spotify_playlist_search.html", context=ctx)
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Music Playlist App
2 | A Web Application maade using the **Django** framework to automatically add songs liked by you on **YouTube** to a **Spotify** **|** **Apple Music** playlist. 🎧
3 |
4 | ## Configuration Required
5 |
6 | * Go to `songs/spotify.py`. Add your **Spotify Oauth Token** to the `spotify_token` variable and **Spotify user_id** to the `spotify_user_id` variable. To get **user_id** click [here](https://www.spotify.com/us/account/overview/). To get **Oauth Token** click [here](https://developer.spotify.com/console/post-playlists/).
7 | * Go to `songs/apple_music.py`. Add your **Apple music token** to the `apple_music_token` variable.
8 | * Go to `songs/client_secret.json`. Replace the complete contents of the file with the contents of your `client_secret.json` file that you can download after enabling **Oauth for YouTube** from [here](https://developers.google.com/youtube/v3/getting-started/).
9 |
10 | ## Steps to get the Oauth Token from Spotify
11 | * Login to your [Spotify account](https://www.spotify.com/us/account/overview/) and get your username.
12 | * The username will be your Spotify user_id.
13 | * Now, get the [Oauth token](https://developer.spotify.com/console/post-playlists/) by entering your user_id and then click Get Token.
14 | * In the scope options, select the required scopes.
15 | * It will generate a Spotify token and note that this **token expires** every once in a while. So, if you face any issue with accessing your Spotify Playlist, it may be because
16 | your token might have expired. In that case, create a new token by repeating the above process.
17 |
18 |
19 | ## Steps to get the Oauth for Youtube
20 | * Login to the [Google Cloud Platform](https://console.developers.google.com/)
21 | * Create a new project by entering your project name.
22 | * Now enable the the api (**YouTube Data API v3**) by selecting the "ENABLE APIS AND SERVICES" option.
23 | * After enabling the api, crete your credentials by clicking the "Credentials" in the sidebar.
24 | * Now press the "+ CREATE CREDENTIALS" option and then **select the OAuth Client ID**
25 | * Click on "CONFIGURE CONSENT SCREEN" and it redirect to OAuth consent screen.
26 | * Select the External option in the User type and then proceed.
27 | * Now, fill out the required fields and proceed forward. (Note: In the scopes section go through all the options and select the ones you think will be necessary)
28 | * In the Test Users section, add your email address and then save and continue.
29 | * Now, head back to the Credentials section in the sidebar and create the OAuth client ID.
30 | * Select the application type to be "**Web application**".
31 | * Now, in the "Authorized redirect URIs" add your local url of the django app. (It may look like "http://127.0.0.1:8000/")
32 | * After entering all the details, save it and then an OAuth client ID will be created.
33 | * Now download the **json file** by clicking on the download symbol next to your client ID.
34 | * Copy the contents of the json file and paste it inside the client_secret.json file in your projects folder.
35 |
36 | **Note that in the above method, it is mentioned that the app is a web application, so for it work the app needs to deployed first. So, use heroku to deploy the app in the web.**
37 |
38 | ## Requirements
39 |
40 | * Python 3.8
41 | * Django 2.2.10
42 | * [Youtube Data API v3](https://developers.google.com/youtube/v3)
43 | * [Spotify Web API](https://developer.spotify.com/documentation/web-api/)
44 | * [Apple Music API](https://developer.apple.com/documentation/applemusicapi)
45 | * [Requests Library v 2.22.0](https://requests.readthedocs.io/en/master/)
46 | * [Youtube_dl v 2020.01.24](https://github.com/ytdl-org/youtube-dl/)
47 | * Additional requirements are in **Pipfile**.
48 |
49 | ## Setting up the Project
50 |
51 | * Download and install Python 3.7
52 | * Download and install Git.
53 | * Fork the Repository.
54 | * Clone the repository to your local machine `$ git clone https://github.com//Music_Playlist.git`
55 | * Change directory to Music_Playlist `$ cd Music_Playlist`
56 | * Install pipenv `$ pip3 install pipenv`
57 | * Create a virtual environment and install all requirements from Pipfile `$ pipenv install`
58 | * Activate the env: `$ pipenv shell`
59 | * Make migrations `$ python manage.py makemigrations`
60 | * Migrate the changes to the database `$ python manage.py migrate`
61 | * Create superuser `$ python manage.py createsuperuser`
62 | * Run the server `$ python manage.py runserver`
63 |
64 | ## Contributing
65 |
66 | If you are new to open source you can first read about git by [clicking here](https://www.codecademy.com/learn/learn-git).
67 |
68 | ## Communtiy Slack Channel
69 |
70 | To get started, the first step is to meet the community. We use slack to communicate, and there the helpful community will guide you. Slack is an instant messaging service used by developers and users of GitHub. It uses chatrooms, where developers can join in and can talk about a particular topic. [Click here](https://join.slack.com/t/codingninjas-talk/shared_invite/zt-pnokq31t-yZX67OFljSq_TclAKsG_eg) to join our Slack Workspace.
71 | ## Code of Conduct
72 | Check code of conduct [here](https://github.com/shubhdeeprajput/Music_Playlist/blob/master/CODE_OF_CONDUCT.md)
73 |
74 |
75 | ## Communities for extenxive bussiness
76 | Artists
77 | Developers
78 | Advertising
79 | Investors
80 | Vendors
81 |
--------------------------------------------------------------------------------
/songs/apple_music.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 |
4 | import google_auth_oauthlib.flow
5 | import googleapiclient.discovery
6 | import googleapiclient.errors
7 | import requests
8 | import youtube_dl
9 |
10 | apple_music_token = ""
11 |
12 | class A_ToPlaylist():
13 | def __init__(self):
14 | self.youtube_client = self.get_youtube_client()
15 | self.all_song_info = {}
16 |
17 | def get_youtube_client(self):
18 | os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
19 | api_service_name = "youtube"
20 | api_version = "v3"
21 | client_secrets_file = "client_secret.json"
22 | scopes = ["https://www.googleapis.com/auth/youtube.readonly"]
23 | flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
24 | client_secrets_file, scopes)
25 | credentials = flow.run_console()
26 | youtube_client = googleapiclient.discovery.build(
27 | api_service_name, api_version, credentials=credentials)
28 |
29 | return youtube_client
30 |
31 | def get_liked_videos(self):
32 | request = self.youtube_client.videos().list(
33 | part="snippet,contentDetails,statistics",
34 | myRating="like"
35 | )
36 | response = request.execute()
37 | for item in response["items"]:
38 | video_title = item["snippet"]["title"]
39 | youtube_url = f'https://www.youtube.com/watch?v={item["id"]}'
40 | video = youtube_dl.YoutubeDL({}).extract_info(
41 | youtube_url, download=False)
42 | song_name = video["track"]
43 | artist = video["artist"]
44 |
45 | if song_name is not None and artist is not None:
46 | self.all_song_info[video_title] = {
47 | "youtube_url": youtube_url,
48 | "song_name": song_name,
49 | "artist": artist,
50 | "apple_music_id": self.get_apple_music_id(song_name)
51 |
52 | }
53 |
54 | def get_playlist(self, playlist_name):
55 | query = f'https://api.music.apple.com/v1/me/library/playlists'
56 | response = requests.get(
57 | query,
58 | headers={
59 | "Content-Type": "application/json",
60 | "Authorization": f'Bearer {apple_music_token}'
61 | }
62 | )
63 | response_json = response.json()
64 | for playlist in response_json["data"]:
65 | if playlist["attributes"]["name"] == playlist_name:
66 | result = playlist["id"]
67 |
68 | return result
69 |
70 | def create_playlist(self, playlist_name, description):
71 | request_body = json.dumps({
72 | "attributes":{
73 | "name": playlist_name,
74 | "description": description
75 | }
76 | })
77 |
78 | query = f'https://api.music.apple.com/v1/me/library/playlists'
79 | response = requests.post(
80 | query,
81 | data=request_body,
82 | headers={
83 | "Content-Type": "application/json",
84 | "Authorization": f'Bearer {apple_music_token}'
85 | }
86 | )
87 | response_json = response.json()
88 | return response_json["id"]
89 |
90 | def get_all_playlist(self):
91 | query = f'https://api.music.apple.com/v1/me/library/playlists'
92 | response = requests.get(
93 | query,
94 | headers={
95 | "Content-Type": "application/json",
96 | "Authorization": f'Bearer {apple_music_token}'
97 | }
98 | )
99 | all_playlists = []
100 | response_json = response.json()
101 | for playlist in response_json["data"]:
102 | all_playlists.append(playlist["attributes"]["name"])
103 |
104 | return all_playlists
105 |
106 | def get_apple_music_id(self, song_name):
107 | query = f'https://api.music.apple.com/v1/catalog/in/songs'
108 | response = requests.get(
109 | query,
110 | headers={
111 | "Content-Type": "application/json",
112 | "Authorization": f'Bearer {apple_music_token}'
113 | }
114 | )
115 | response_json = response.json()
116 | songs = response_json["data"]
117 | for song in songs:
118 | if song["attributes"]["name"] == song_name:
119 | id_ = song["attributes"]["playparams"]["id"]
120 |
121 | return id_
122 |
123 | def add_song_to_playlist(self, playlist_name):
124 | self.get_liked_videos()
125 | ids = [info["apple_music_id"]
126 | for song, info in self.all_song_info.items()]
127 | playlist_id = self.get_playlist(playlist_name)
128 | q = f'https://api.music.apple.com/v1/me/library/playlists/{playlist_id}'
129 | res = requests.get(
130 | q,
131 | headers={
132 | "Content-Type": "application/json",
133 | "Authorization": f'Bearer {apple_music_token}'
134 | })
135 | res_json = res.json()
136 | tracks = res_json["relationships"]["tracks"]
137 | for track in tracks:
138 | t = track["librarysong"]
139 | _id = t["attributes"]["playparams"]["id"]
140 | if _id in ids:
141 | ids.remove(_id)
142 |
143 |
144 | request_data = json.dumps(ids)
145 |
146 | query = f'https://api.music.apple.com/v1/me/library/playlists/{playlist_id}/tracks'
147 |
148 | response = requests.post(
149 | query,
150 | data=request_data,
151 | headers={
152 | "Content-Type": "application/json",
153 | "Authorization": f'Bearer {apple_music_token}'
154 | }
155 | )
156 |
157 | response_json = response.json()
158 | return response_json
159 |
160 |
--------------------------------------------------------------------------------
/songs/spotify.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 |
4 | import google_auth_oauthlib.flow
5 | import googleapiclient.discovery
6 | import googleapiclient.errors
7 | import requests
8 | import youtube_dl
9 | from .exceptions import ResponseException
10 |
11 | spotify_token = ""
12 | spotify_user_id = ""
13 |
14 | class ToPlaylist():
15 | def __init__(self):
16 | self.youtube_client = self.get_youtube_client()
17 | self.all_song_info = {}
18 |
19 | def get_youtube_client(self):
20 | os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
21 |
22 | api_service_name = "youtube"
23 | api_version = "v3"
24 | client_secrets_file = "client_secret.json"
25 | scopes = ["https://www.googleapis.com/auth/youtube.readonly"]
26 | flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
27 | client_secrets_file, scopes)
28 | credentials = flow.run_console()
29 | youtube_client = googleapiclient.discovery.build(
30 | api_service_name, api_version, credentials=credentials)
31 |
32 | return youtube_client
33 |
34 | def get_liked_videos(self):
35 | request = self.youtube_client.videos().list(
36 | part="snippet,contentDetails,statistics",
37 | myRating="like"
38 | )
39 | response = request.execute()
40 | for item in response["items"]:
41 | video_title = item["snippet"]["title"]
42 | youtube_url = f'https://www.youtube.com/watch?v={item["id"]}'
43 | video = youtube_dl.YoutubeDL({}).extract_info(
44 | youtube_url, download=False)
45 | song_name = video["track"]
46 | artist = video["artist"]
47 |
48 | if song_name is not None and artist is not None:
49 | self.all_song_info[video_title] = {
50 | "youtube_url": youtube_url,
51 | "song_name": song_name,
52 | "artist": artist,
53 | "spotify_uri": self.get_spotify_uri(song_name, artist)
54 |
55 | }
56 |
57 | def get_playlist(self, playlist_name):
58 | query = f'https://api.spotify.com/v1/users/{spotify_user_id}/playlists'
59 | response = requests.get(
60 | query,
61 | headers={
62 | "Content-Type": "application/json",
63 | "Authorization": f'Bearer {spotify_token}'
64 | }
65 | )
66 | response_json = response.json()
67 | for playlist in response_json["items"]:
68 | if playlist["name"] == playlist_name:
69 | result = playlist["id"]
70 |
71 | return result
72 |
73 | def get_all_playlist(self):
74 | query = f'https://api.spotify.com/v1/users/{spotify_user_id}/playlists'
75 | response = requests.get(
76 | query,
77 | headers={
78 | "Content-Type": "application/json",
79 | "Authorization": f'Bearer {spotify_token}'
80 | }
81 | )
82 | all_playlists = []
83 | response_json = response.json()
84 | for playlist in response_json["items"]:
85 | all_playlists.append(playlist["name"])
86 |
87 | return all_playlists
88 |
89 | def create_playlist(self, playlist_name, description):
90 | request_body = json.dumps({
91 | "name": playlist_name,
92 | "description": description,
93 | "public": True
94 | })
95 |
96 | query = f'https://api.spotify.com/v1/users/{spotify_user_id}/playlists'
97 | response = requests.post(
98 | query,
99 | data=request_body,
100 | headers={
101 | "Content-Type": "application/json",
102 | "Authorization": f'Bearer {spotify_token}'
103 | }
104 | )
105 | response_json = response.json()
106 | return response_json["id"]
107 |
108 | def get_spotify_uri(self, song_name, artist):
109 | query = f'https://api.spotify.com/v1/search?query=track%3A{song_name}+artist%3A{artist}&type=track&offset=0&limit=20'
110 | response = requests.get(
111 | query,
112 | headers={
113 | "Content-Type": "application/json",
114 | "Authorization": f'Bearer {spotify_token}'
115 | }
116 | )
117 | response_json = response.json()
118 | songs = response_json["tracks"]["items"]
119 | uri = songs[0]["uri"]
120 |
121 | return uri
122 |
123 | def add_song_to_playlist(self, playlist_name):
124 | self.get_liked_videos()
125 | p_name = playlist_name
126 | uris = [info["spotify_uri"]
127 | for song, info in self.all_song_info.items()]
128 | playlist_id = self.get_playlist(p_name)
129 | q = f'https://api.spotify.com/v1/users/{spotify_user_id}/playlists/{playlist_id}'
130 | res = requests.get(
131 | q,
132 | headers={
133 | "Content-Type": "application/json",
134 | "Authorization": f'Bearer {spotify_token}'
135 | })
136 | res_json = res.json()
137 | tracks = res_json["tracks"]
138 | for track in tracks:
139 | t = track["track"]
140 | urri = t["uri"]
141 | if urri in uris:
142 | uris.remove(urri)
143 |
144 | request_data = json.dumps(uris)
145 |
146 | query = f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks'
147 |
148 | response = requests.post(
149 | query,
150 | data=request_data,
151 | headers={
152 | "Content-Type": "application/json",
153 | "Authorization": f'Bearer {spotify_token}'
154 | }
155 | )
156 |
157 | if response.status_code != 200:
158 | raise ResponseException(response.status_code)
159 |
160 |
161 | response_json = response.json()
162 | return response_json
163 |
164 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | {% block title %}Music Playlist App{% endblock title %}
61 |
62 |
63 |
96 |
97 |
98 |
99 | {% block content %}
100 |
101 | {% endblock content %}
102 |
103 |
104 |
105 |
116 |
117 |
119 |
120 |
121 |
123 |
124 |
125 |
126 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "561b0935b30b67c79a6f5758d253fd39b0dd7c22eb03ea763e9b790ab6e98741"
5 | "sha256": "8dcc88141bb339f5efbd801ce7371c87a4f8150fb280e49db0dbef0d0dc10494"
6 | },
7 | "pipfile-spec": 6,
8 | "requires": {
9 | "python_version": "3.7"
10 | },
11 | "sources": [
12 | {
13 | "name": "pypi",
14 | "url": "https://pypi.org/simple",
15 | "verify_ssl": true
16 | }
17 | ]
18 | },
19 | "default": {
20 | "argparse": {
21 | "hashes": [
22 | "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4",
23 | "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"
24 | ],
25 | "version": "==1.4.0"
26 | },
27 | "asgiref": {
28 | "hashes": [
29 | "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
30 | "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
31 | ],
32 | "markers": "python_version >= '3.6'",
33 | "version": "==3.4.1"
34 | },
35 | "cachetools": {
36 | "hashes": [
37 | "sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
38 | "sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
39 | ],
40 | "markers": "python_version ~= '3.5'",
41 | "version": "==4.2.2"
42 | },
43 | "certifi": {
44 | "hashes": [
45 | "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
46 | "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
47 | ],
48 | "version": "==2021.5.30"
49 | },
50 | "chardet": {
51 | "hashes": [
52 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
53 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
54 | ],
55 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
56 | "version": "==4.0.0"
57 | },
58 | "django": {
59 | "hashes": [
60 | "sha256:3da05fea54fdec2315b54a563d5b59f3b4e2b1e69c3a5841dda35019c01855cd",
61 | "sha256:c58b5f19c5ae0afe6d75cbdd7df561e6eb929339985dbbda2565e1cabb19a62e"
62 | ],
63 | "index": "pypi",
64 | "version": "==3.2.5"
65 | },
66 | "exception": {
67 | "hashes": [
68 | "sha256:b11176dc5c4386a1649476d498b2efd3cd6eb1151ca6c2214f16b90a785c0a3b",
69 | "sha256:f8ce0ed8e75fafa9f948e37548290cb8d188a154a17426b1a9ca01f39647cf08"
70 | ],
71 | "index": "pypi",
72 | "version": "==0.1.0"
73 | },
74 | "google-api-core": {
75 | "hashes": [
76 | "sha256:7c8ba88e2b893ef4f67d67e229aade51a2db5053023b73b1394a5ee3dcdb561c",
77 | "sha256:a9f979b5c6a9cad31a4120ca6be712df76adc32336a7bf4bc2f4331a1b527fe7"
78 | ],
79 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
80 | "version": "==1.31.0"
81 | },
82 | "google-api-python-client": {
83 | "hashes": [
84 | "sha256:345bb7249ac9af93a6c75e4adb93a41139459945aee61e611a5c8e5b146cbef7",
85 | "sha256:b3874333cabc5ce46dcda2da48f1b258e227a9ae61688211e277e11270386307"
86 | ],
87 | "index": "pypi",
88 | "version": "==2.13.0"
89 | },
90 | "google-auth": {
91 | "hashes": [
92 | "sha256:9266252e11393943410354cf14a77bcca24dd2ccd9c4e1aef23034fe0fbae630",
93 | "sha256:c7c215c74348ef24faef2f7b62f6d8e6b38824fe08b1e7b7b09a02d397eda7b3"
94 | ],
95 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
96 | "version": "==1.32.1"
97 | },
98 | "google-auth-httplib2": {
99 | "hashes": [
100 | "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10",
101 | "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"
102 | ],
103 | "version": "==0.1.0"
104 | },
105 | "google-auth-oauthlib": {
106 | "hashes": [
107 | "sha256:09832c6e75032f93818edf1affe4746121d640c625a5bef9b5c96af676e98eee",
108 | "sha256:0e92aacacfb94978de3b7972cf4b0f204c3cd206f74ddd0dc0b31e91164e6317"
109 | ],
110 | "index": "pypi",
111 | "version": "==0.4.4"
112 | },
113 | "googleapis-common-protos": {
114 | "hashes": [
115 | "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4",
116 | "sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0"
117 | ],
118 | "markers": "python_version >= '3.6'",
119 | "version": "==1.53.0"
120 | },
121 | "gunicorn": {
122 | "hashes": [
123 | "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
124 | "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
125 | ],
126 | "index": "pypi",
127 | "version": "==20.1.0"
128 | },
129 | "httplib2": {
130 | "hashes": [
131 | "sha256:0b12617eeca7433d4c396a100eaecfa4b08ee99aa881e6df6e257a7aad5d533d",
132 | "sha256:2ad195faf9faf079723f6714926e9a9061f694d07724b846658ce08d40f522b4"
133 | ],
134 | "version": "==0.19.1"
135 | },
136 | "idna": {
137 | "hashes": [
138 | "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
139 | "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
140 | ],
141 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
142 | "version": "==2.10"
143 | },
144 | "oauthlib": {
145 | "hashes": [
146 | "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc",
147 | "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"
148 | ],
149 | "markers": "python_version >= '3.6'",
150 | "version": "==3.1.1"
151 | },
152 | "packaging": {
153 | "hashes": [
154 | "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
155 | "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
156 | ],
157 | "markers": "python_version >= '3.6'",
158 | "version": "==21.0"
159 | },
160 | "protobuf": {
161 | "hashes": [
162 | "sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637",
163 | "sha256:145ce0af55c4259ca74993ddab3479c78af064002ec8227beb3d944405123c71",
164 | "sha256:14c1c9377a7ffbeaccd4722ab0aa900091f52b516ad89c4b0c3bb0a4af903ba5",
165 | "sha256:1556a1049ccec58c7855a78d27e5c6e70e95103b32de9142bae0576e9200a1b0",
166 | "sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539",
167 | "sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87",
168 | "sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e",
169 | "sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde",
170 | "sha256:59e5cf6b737c3a376932fbfb869043415f7c16a0cf176ab30a5bbc419cd709c1",
171 | "sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d",
172 | "sha256:6ce4d8bf0321e7b2d4395e253f8002a1a5ffbcfd7bcc0a6ba46712c07d47d0b4",
173 | "sha256:6d847c59963c03fd7a0cd7c488cadfa10cda4fff34d8bc8cba92935a91b7a037",
174 | "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b",
175 | "sha256:7a4c97961e9e5b03a56f9a6c82742ed55375c4a25f2692b625d4087d02ed31b9",
176 | "sha256:85d6303e4adade2827e43c2b54114d9a6ea547b671cb63fafd5011dc47d0e13d",
177 | "sha256:8727ee027157516e2c311f218ebf2260a18088ffb2d29473e82add217d196b1c",
178 | "sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1",
179 | "sha256:9b7a5c1022e0fa0dbde7fd03682d07d14624ad870ae52054849d8960f04bc764",
180 | "sha256:a22b3a0dbac6544dacbafd4c5f6a29e389a50e3b193e2c70dae6bbf7930f651d",
181 | "sha256:a38bac25f51c93e4be4092c88b2568b9f407c27217d3dd23c7a57fa522a17554",
182 | "sha256:a981222367fb4210a10a929ad5983ae93bd5a050a0824fc35d6371c07b78caf6",
183 | "sha256:ab6bb0e270c6c58e7ff4345b3a803cc59dbee19ddf77a4719c5b635f1d547aa8",
184 | "sha256:c56c050a947186ba51de4f94ab441d7f04fcd44c56df6e922369cc2e1a92d683",
185 | "sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47",
186 | "sha256:ebcb546f10069b56dc2e3da35e003a02076aaa377caf8530fe9789570984a8d2",
187 | "sha256:f0e59430ee953184a703a324b8ec52f571c6c4259d496a19d1cabcdc19dabc62",
188 | "sha256:ffea251f5cd3c0b9b43c7a7a912777e0bc86263436a87c2555242a348817221b"
189 | ],
190 | "version": "==3.17.3"
191 | },
192 | "pyasn1": {
193 | "hashes": [
194 | "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
195 | "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
196 | "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
197 | "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
198 | "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
199 | "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
200 | "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
201 | "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
202 | "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
203 | "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
204 | "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
205 | "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
206 | "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
207 | ],
208 | "version": "==0.4.8"
209 | },
210 | "pyasn1-modules": {
211 | "hashes": [
212 | "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
213 | "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
214 | "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
215 | "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
216 | "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
217 | "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
218 | "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
219 | "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
220 | "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
221 | "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
222 | "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
223 | "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
224 | "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
225 | ],
226 | "version": "==0.2.8"
227 | },
228 | "pyparsing": {
229 | "hashes": [
230 | "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
231 | "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
232 | ],
233 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
234 | "version": "==2.4.7"
235 | },
236 | "pytz": {
237 | "hashes": [
238 | "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
239 | "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
240 | ],
241 | "version": "==2021.1"
242 | },
243 | "requests": {
244 | "hashes": [
245 | "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
246 | "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
247 | ],
248 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
249 | "version": "==2.25.1"
250 | },
251 | "requests-oauthlib": {
252 | "hashes": [
253 | "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
254 | "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
255 | "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
256 | ],
257 | "version": "==1.3.0"
258 | },
259 | "rsa": {
260 | "hashes": [
261 | "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2",
262 | "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"
263 | ],
264 | "markers": "python_version >= '3.6'",
265 | "version": "==4.7.2"
266 | },
267 | "schedule": {
268 | "hashes": [
269 | "sha256:617adce8b4bf38c360b781297d59918fbebfb2878f1671d189f4f4af5d0567a4",
270 | "sha256:e6ca13585e62c810e13a08682e0a6a8ad245372e376ba2b8679294f377dfc8e4"
271 | ],
272 | "index": "pypi",
273 | "version": "==1.1.0"
274 | },
275 | "six": {
276 | "hashes": [
277 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
278 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
279 | ],
280 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
281 | "version": "==1.16.0"
282 | },
283 | "sqlparse": {
284 | "hashes": [
285 | "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
286 | "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
287 | ],
288 | "markers": "python_version >= '3.5'",
289 | "version": "==0.4.1"
290 | },
291 | "typing-extensions": {
292 | "hashes": [
293 | "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
294 | "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
295 | "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
296 | ],
297 | "markers": "python_version < '3.8'",
298 | "version": "==3.10.0.0"
299 | },
300 | "uritemplate": {
301 | "hashes": [
302 | "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f",
303 | "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"
304 | ],
305 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
306 | "version": "==3.0.1"
307 | },
308 | "urllib3": {
309 | "hashes": [
310 | "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
311 | "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
312 | ],
313 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
314 | "version": "==1.26.6"
315 | },
316 | "youtube-dl": {
317 | "hashes": [
318 | "sha256:263e04d53fb8ba3dfbd246ad09b7d388e896c132a20cc770c26ee7684de050ac",
319 | "sha256:cb2d3ee002158ede783e97a82c95f3817594df54367ea6a77ce5ceea4772f0ab"
320 | ],
321 | "index": "pypi",
322 | "version": "==2021.6.6"
323 | }
324 | },
325 | "develop": {}
326 | }
327 |
--------------------------------------------------------------------------------