├── .github
├── .workflows
│ └── build.yml
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── STATIC
├── 1.png
├── 2.png
├── 3.png
├── header.png
├── rezonance-api.png
├── rezonance-logo-blue-sq.png
└── rezonance-logo-witching-sq.png
├── requirements.txt
├── src
├── database
│ └── .gitignore
├── main.py
└── scripts
│ ├── fetch.py
│ ├── recommend.py
│ ├── search.py
│ └── trending.py
└── tests
└── test_main.py
/.github/.workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | python-version: [3.8, 3.9]
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Test using ${{ matrix.python-version }}
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: ${{ matrix.python-version }}
18 | - name: Install Dependencies
19 | run: |
20 | python -m pip install pip --upgrade pip
21 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
22 | curl -L -o src/database/database.zip https://www.dropbox.com/s/co34arck1nbgfzb/database.zip?dl=0
23 | unzip -o src/database/database.zip
24 | rm src/database/database.zip
25 |
26 | - name:
27 | run:
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | python-version: [3.8, 3.9]
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Test using ${{ matrix.python-version }}
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: ${{ matrix.python-version }}
18 |
19 | - name: Start Redis
20 | uses: supercharge/redis-github-action@1.2.0
21 | with:
22 | redis-version: 6
23 |
24 | - name: Install Dependencies
25 | run: |
26 | python -m pip install pip --upgrade pip
27 | sudo apt-get install libsqlite3-dev
28 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
29 | cd src/database/
30 | curl -L -o database.zip ${{ secrets.DB_URL }}
31 | unzip -o database.zip
32 | rm database.zip
33 | cd ../..
34 | cd src/scripts/
35 | curl -L -o secret.py ${{ secrets.SECRET_URL }}
36 | cd ../..
37 | echo $(pwd)
38 | echo $(ls src/database/)
39 |
40 | - name: run unit tests [pytest]
41 | run: |
42 | export PYTHONPATH=src
43 | pytest
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store/
2 | venv/
3 | __pycache__/
4 | .vscode/
5 | .pytest_cache/
6 |
7 | # Database files
8 | *.db
9 | *.pyc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
2 |
3 | [](https://forthebadge.com)
4 | [](https://forthebadge.com)
5 | [](https://forthebadge.com)
6 | [](https://forthebadge.com)
7 |
8 |
9 |
14 |
15 |
20 |
13 |
16 |
17 |
18 | rezonance
19 |
21 |
22 | Rezonance API
23 |
24 | Visit the website »
25 |
26 |
27 | View Demo
28 | ·
29 | Report Bug
30 | ·
31 | Request Feature
32 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
195 |
196 |
197 | ## Product Screenshots
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | ## Roadmap
209 |
210 | See the [open issues](https://github.com/rezonance-india/engine-api/issues) for a list of proposed features (and known issues).
211 |
212 |
213 |
214 | ## Contributing
215 |
216 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
217 |
218 | 1. Fork the Project
219 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
220 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
221 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
222 | 5. Open a Pull Request
223 |
224 |
225 |
226 |
227 |
228 |
229 | ## Contact
230 |
231 | Arijit Roy - [GitHub](https://github.com/radioactive11) - roy.arijit2001@gmail.com
232 |
233 | Kartik Goel - [GitHub](https://github.com/kg-kartik) - goel.kartik39@gmail.com
234 |
235 |
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/STATIC/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/1.png
--------------------------------------------------------------------------------
/STATIC/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/2.png
--------------------------------------------------------------------------------
/STATIC/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/3.png
--------------------------------------------------------------------------------
/STATIC/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/header.png
--------------------------------------------------------------------------------
/STATIC/rezonance-api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/rezonance-api.png
--------------------------------------------------------------------------------
/STATIC/rezonance-logo-blue-sq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/rezonance-logo-blue-sq.png
--------------------------------------------------------------------------------
/STATIC/rezonance-logo-witching-sq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rezonance-india/engine-api/3d00df9f4c02da0dd73265db2b09e7551afef3bf/STATIC/rezonance-logo-witching-sq.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | asgiref==3.4.0
2 | attrs==21.2.0
3 | certifi==2021.5.30
4 | chardet==4.0.0
5 | click==8.0.1
6 | fastapi==0.65.2
7 | h11==0.12.0
8 | idna==2.10
9 | iniconfig==1.1.1
10 | numpy==1.21.0
11 | packaging==20.9
12 | pluggy==0.13.1
13 | py==1.10.0
14 | pydantic==1.8.2
15 | pyparsing==2.4.7
16 | pytest==6.2.4
17 | redis==3.5.3
18 | requests==2.25.1
19 | sqlite-spellfix==1.0
20 | starlette==0.14.2
21 | toml==0.10.2
22 | typing-extensions==3.10.0.0
23 | urllib3==1.26.6
24 | uvicorn==0.14.0
25 |
--------------------------------------------------------------------------------
/src/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.db
2 | *.npy
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | from logging import debug
2 | from typing import Optional
3 | from fastapi import FastAPI, Header, Request, HTTPException
4 | from fastapi.middleware.cors import CORSMiddleware
5 | from pydantic import BaseModel
6 |
7 | import re
8 |
9 | from scripts import search, recommend, fetch, trending
10 |
11 |
12 | app = FastAPI()
13 |
14 |
15 | origins = [
16 | "http://localhost:8080",
17 | ]
18 |
19 | app.add_middleware(
20 | CORSMiddleware,
21 | allow_origins=origins,
22 | allow_credentials=True,
23 | allow_methods=["*"],
24 | allow_headers=["*"],
25 | )
26 |
27 |
28 | class SearchResource(BaseModel):
29 | query: str
30 |
31 | @app.post('/search/tracks')
32 | def _search_tracks(request_body: SearchResource, request: Request) -> list:
33 | """search for tracks in database
34 |
35 | Args:
36 | request_body (Search): Schema of Request Body
37 | """
38 | data = request_body.query
39 | data = re.sub(r'[^A-Za-z0-9 ]+', '', data)
40 | result = search.search_tracks(data)
41 |
42 | return result
43 |
44 |
45 | @app.post('/search/albums')
46 | def _search_albums(request_body: SearchResource) -> list:
47 | data = request_body.query
48 | data = re.sub(r'[^A-Za-z0-9 ]+', '', data)
49 | result = search.search_albums(data)
50 |
51 | return result
52 |
53 |
54 | @app.post('/search/artists')
55 | def _search_artists(request_body: SearchResource) -> list:
56 | data = request_body.query
57 | data = re.sub(r'[^A-Za-z0-9 ]+', '', data)
58 | result = search.search_artists(data)
59 |
60 | return result
61 |
62 |
63 | class RecommendResource(BaseModel):
64 | ref_id: str
65 |
66 | @app.post("/recommend")
67 | def _recommendaton(request_body: RecommendResource) -> list:
68 | ref_id = request_body.ref_id
69 | genre = ref_id[:3]
70 | valid_genres = {"pop", "roc", "edm", "rap", "ind"}
71 | if genre not in valid_genres:
72 | raise HTTPException(status_code=406, detail="invalid ref_id")
73 | results = recommend.get_recoms(ref_id)
74 |
75 | return results
76 |
77 |
78 | class FetchTracks(BaseModel):
79 | album_id: str
80 |
81 | @app.post('/fetch/tracks')
82 | def _fetch_tracks(request_body: FetchTracks):
83 | album_id = request_body.album_id
84 | results = fetch.albums_tracks(album_id)
85 |
86 | return results
87 |
88 |
89 | class FetchAlbums(BaseModel):
90 | artist_id: str
91 |
92 | @app.post("/fetch/albums")
93 | def _fetch_albums(request_body: FetchAlbums):
94 | artist_id = request_body.artist_id
95 | results = fetch.artist_albums(artist_id)
96 |
97 | return results
98 |
99 |
100 | @app.get('/trending/tracks')
101 | def _fetch_trending():
102 | trending_tracks = trending.fetch_trending()
103 | return trending_tracks
104 |
--------------------------------------------------------------------------------
/src/scripts/fetch.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import os
3 | from . import secret
4 | import secrets
5 |
6 |
7 | def track_url_encoder(url_id: str, url_pin: str) -> str:
8 | cdn_list = secret.cdn_list
9 | cdn = secrets.choice(cdn_list)
10 | BASE_URL = secret.BASE_URL
11 | BITRATE = "96"
12 | track_url = f"https://{cdn}.{BASE_URL}/{url_pin}/{url_id}_{BITRATE}.mp4"
13 |
14 | return track_url
15 |
16 |
17 | def artist_albums(artist_id: str):
18 | BASE_DIR = os.getcwd()
19 | DB_DIR = "src/database/rezo.db"
20 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
21 | conn = sqlite3.connect(DB_PATH)
22 | cur = conn.cursor()
23 |
24 | SQL = """
25 | SELECT * FROM albums
26 | WHERE artist_id = ?
27 | ORDER by album_popularity DESC
28 | LIMIT 50
29 | """
30 | cur.execute(SQL, (artist_id, ))
31 |
32 | res = cur.fetchall()
33 | dict_list = []
34 | for album in res:
35 | album_dict = {
36 | "artist_name": album[0],
37 | "album_name": album[1],
38 | "album_id": album[2],
39 | "artist_id": album[3],
40 | "album_img": album[4],
41 | "album_popularity": album[5]
42 | }
43 | dict_list.append(album_dict)
44 |
45 | cur.close()
46 | conn.close()
47 | return dict_list
48 |
49 |
50 |
51 | def albums_tracks(album_id: str):
52 | BASE_DIR = os.getcwd()
53 | DB_DIR = "src/database/rezo.db"
54 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
55 | conn = sqlite3.connect(DB_PATH)
56 | cur = conn.cursor()
57 |
58 | SQL = """
59 | SELECT * from tracks_fts4
60 | WHERE album_id=?
61 | LIMIT 50
62 | """
63 | cur.execute(SQL, (album_id, ))
64 |
65 | tracks = cur.fetchall()
66 |
67 | tracks_list = []
68 | for track in tracks:
69 | url_id = track[5]
70 | url_pin = track[6]
71 | track_url = track_url_encoder(url_id, url_pin)
72 | track_dict ={
73 | "ref_id": track[0],
74 | "track_name": track[3],
75 | "track_id": track[2],
76 | "track_url": track_url
77 | }
78 | tracks_list.append(track_dict)
79 |
80 | cur.close()
81 | conn.close()
82 | return tracks_list
83 |
84 |
--------------------------------------------------------------------------------
/src/scripts/recommend.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import numpy as np
3 | import random
4 | import os
5 | from . import secret
6 | import secrets
7 |
8 |
9 | def track_url_encoder(url_id: str, url_pin: str) -> str:
10 | cdn_list = secret.cdn_list
11 | cdn = secrets.choice(cdn_list)
12 | BASE_URL = secret.BASE_URL
13 | BITRATE = "96"
14 | track_url = f"https://{cdn}.{BASE_URL}/{url_pin}/{url_id}_{BITRATE}.mp4"
15 |
16 | return track_url
17 |
18 |
19 | def get_recoms(query):
20 | genre = query[: 3]
21 | idx = int(query[3:])
22 | result = []
23 | if genre == 'pop':
24 | result = recommend_pop(idx)
25 |
26 | elif genre == 'rap':
27 | result = recommend_rap(idx)
28 |
29 | elif genre == 'roc':
30 | result = recommend_rock(idx)
31 |
32 | elif genre == 'edm':
33 | result = recommend_edm(idx)
34 |
35 | elif genre == 'ind':
36 | result = recommend_ind(idx)
37 |
38 | return result
39 |
40 |
41 | def recommend_pop(query):
42 | BASE_DIR = os.getcwd()
43 | DB_DIR = "src/database/rezo.db"
44 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
45 | conn = sqlite3.connect(DB_PATH)
46 | cur = conn.cursor()
47 | item_list = []
48 |
49 | ref_id = "pop" + str(query)
50 | cur.execute("SELECT album_id FROM tracks_fts4 WHERE ref_id = ?", (ref_id, ))
51 | album_id = cur.fetchone()[0]
52 | cur.execute(
53 | """
54 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
55 | FROM tracks_fts4 t
56 | INNER JOIN albums a
57 | ON t.album_id = a.album_id
58 | INNER JOIN artists art
59 | ON a.artist_id = art.artist_id
60 | WHERE t.album_id = ?
61 | ORDER BY art.artist_popularity DESC
62 | LIMIT 10
63 | """, (album_id, ))
64 | album_tracks = cur.fetchall()
65 | if len(album_tracks) >= 4:
66 | rand_idx = random.sample(range(0, len(album_tracks)), 4)
67 | for idx in rand_idx:
68 | if album_tracks[idx][0] == ref_id:
69 | continue
70 |
71 | else:
72 | url_id = album_tracks[idx][5]
73 | url_pin = album_tracks[idx][6]
74 | track_url = track_url_encoder(url_id, url_pin)
75 | track_dict = {
76 | "track_id": album_tracks[idx][0],
77 | "track_name": album_tracks[idx][1],
78 | "album_name": album_tracks[idx][2],
79 | "artist_name": album_tracks[idx][3],
80 | "album_image": album_tracks[idx][4],
81 | "track_url": track_url
82 | }
83 | item_list.append(track_dict)
84 |
85 | if len(item_list) == 4:
86 | item_list = item_list[: 3]
87 |
88 | sim = np.load("src/database/pop-light.npy")
89 | recoms_list = sim[query, :]
90 | print(recoms_list)
91 | for i in recoms_list:
92 | print(i)
93 | idx = 'pop' + str(int(i))
94 | cur.execute("""
95 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
96 | FROM tracks_fts4 t
97 | INNER JOIN albums a
98 | ON t.album_id = a.album_id
99 | INNER JOIN artists art
100 | ON a.artist_id = art.artist_id
101 | WHERE t.ref_id = ?
102 | ORDER BY art.artist_popularity DESC """, (idx, ))
103 | track = cur.fetchone()
104 | url_id = track[5]
105 | url_pin = track[6]
106 | track_url = track_url_encoder(url_id, url_pin)
107 | track_dict = {
108 | "track_id": track[0],
109 | "track_name": track[1],
110 | "album_name": track[2],
111 | "artist_name": track[3],
112 | "album_image": track[4],
113 | "track_url": track_url
114 | }
115 | item_list.append(track_dict)
116 | conn.close()
117 | return item_list
118 |
119 |
120 | def recommend_rap(query):
121 | BASE_DIR = os.getcwd()
122 | DB_DIR = "src/database/rezo.db"
123 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
124 | conn = sqlite3.connect(DB_PATH)
125 | cur = conn.cursor()
126 | item_list = []
127 |
128 | ref_id = "rap" + str(query)
129 | cur.execute("SELECT album_id FROM tracks_fts4 WHERE ref_id = ?", (ref_id, ))
130 | album_id = cur.fetchone()[0]
131 | cur.execute(
132 | """
133 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
134 | FROM tracks_fts4 t
135 | INNER JOIN albums a
136 | ON t.album_id = a.album_id
137 | INNER JOIN artists art
138 | ON a.artist_id = art.artist_id
139 | WHERE t.album_id = ?
140 | ORDER BY art.artist_popularity DESC
141 | LIMIT 10
142 | """, (album_id, ))
143 | album_tracks = cur.fetchall()
144 | if len(album_tracks) >= 4:
145 | rand_idx = random.sample(range(0, len(album_tracks)), 4)
146 | for idx in rand_idx:
147 | if album_tracks[idx][0] == ref_id:
148 | continue
149 |
150 | else:
151 | url_id = album_tracks[idx][5]
152 | url_pin = album_tracks[idx][6]
153 | track_url = track_url_encoder(url_id, url_pin)
154 | track_dict = {
155 | "track_id": album_tracks[idx][0],
156 | "track_name": album_tracks[idx][1],
157 | "album_name": album_tracks[idx][2],
158 | "artist_name": album_tracks[idx][3],
159 | "album_image": album_tracks[idx][4],
160 | "track_url": track_url
161 | }
162 | item_list.append(track_dict)
163 |
164 | if len(item_list) == 4:
165 | item_list = item_list[: 3]
166 |
167 | sim = np.load("src/database/rap-light.npy")
168 | recoms_list = sim[query, :]
169 | for i in recoms_list:
170 | idx = 'rap' + str(int(i))
171 | cur.execute("""
172 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
173 | FROM tracks_fts4 t
174 | INNER JOIN albums a
175 | ON t.album_id = a.album_id
176 | INNER JOIN artists art
177 | ON a.artist_id = art.artist_id
178 | WHERE t.ref_id = ?
179 | ORDER BY art.artist_popularity DESC """, (idx, ))
180 | track = cur.fetchone()
181 | url_id = track[5]
182 | url_pin = track[6]
183 | track_url = track_url_encoder(url_id, url_pin)
184 | track_dict = {
185 | "track_id": track[0],
186 | "track_name": track[1],
187 | "album_name": track[2],
188 | "artist_name": track[3],
189 | "album_image": track[4],
190 | "track_url": track_url
191 | }
192 | item_list.append(track_dict)
193 | conn.close()
194 | return item_list
195 |
196 |
197 | def recommend_edm(query):
198 | BASE_DIR = os.getcwd()
199 | DB_DIR = "src/database/rezo.db"
200 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
201 | conn = sqlite3.connect(DB_PATH)
202 | cur = conn.cursor()
203 | item_list = []
204 |
205 | ref_id = "edm" + str(query)
206 | cur.execute("SELECT album_id FROM tracks_fts4 WHERE ref_id = ?", (ref_id, ))
207 | album_id = cur.fetchone()[0]
208 | cur.execute(
209 | """
210 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
211 | FROM tracks_fts4 t
212 | INNER JOIN albums a
213 | ON t.album_id = a.album_id
214 | INNER JOIN artists art
215 | ON a.artist_id = art.artist_id
216 | WHERE t.album_id = ?
217 | ORDER BY art.artist_popularity DESC
218 | LIMIT 10
219 | """, (album_id, ))
220 | album_tracks = cur.fetchall()
221 | if len(album_tracks) >= 4:
222 | rand_idx = random.sample(range(0, len(album_tracks)), 4)
223 | for idx in rand_idx:
224 | if album_tracks[idx][0] == ref_id:
225 | continue
226 |
227 | else:
228 | url_id = album_tracks[idx][5]
229 | url_pin = album_tracks[idx][6]
230 | track_url = track_url_encoder(url_id, url_pin)
231 | track_dict = {
232 | "track_id": album_tracks[idx][0],
233 | "track_name": album_tracks[idx][1],
234 | "album_name": album_tracks[idx][2],
235 | "artist_name": album_tracks[idx][3],
236 | "album_image": album_tracks[idx][4],
237 | "track_url": track_url
238 | }
239 | item_list.append(track_dict)
240 |
241 | if len(item_list) == 4:
242 | item_list = item_list[: 3]
243 |
244 | sim = np.load("src/database/edm-light.npy")
245 | recoms_list = sim[query, :]
246 | for i in recoms_list:
247 | idx = 'edm' + str(int(i))
248 | cur.execute("""
249 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
250 | FROM tracks_fts4 t
251 | INNER JOIN albums a
252 | ON t.album_id = a.album_id
253 | INNER JOIN artists art
254 | ON a.artist_id = art.artist_id
255 | WHERE t.ref_id = ?
256 | ORDER BY art.artist_popularity DESC """, (idx, ))
257 | track = cur.fetchone()
258 | url_id = track[5]
259 | url_pin = track[6]
260 | track_url = track_url_encoder(url_id, url_pin)
261 | track_dict = {
262 | "track_id": track[0],
263 | "track_name": track[1],
264 | "album_name": track[2],
265 | "artist_name": track[3],
266 | "album_image": track[4],
267 | "track_url": track_url
268 | }
269 | item_list.append(track_dict)
270 | conn.close()
271 | return item_list
272 |
273 |
274 | def recommend_rock(query):
275 | BASE_DIR = os.getcwd()
276 | DB_DIR = "src/database/rezo.db"
277 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
278 | conn = sqlite3.connect(DB_PATH)
279 | cur = conn.cursor()
280 | item_list = []
281 |
282 | ref_id = "roc" + str(query)
283 | cur.execute("SELECT album_id FROM tracks_fts4 WHERE ref_id = ?", (ref_id, ))
284 | album_id = cur.fetchone()[0]
285 | cur.execute(
286 | """
287 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
288 | FROM tracks_fts4 t
289 | INNER JOIN albums a
290 | ON t.album_id = a.album_id
291 | INNER JOIN artists art
292 | ON a.artist_id = art.artist_id
293 | WHERE t.album_id = ?
294 | ORDER BY art.artist_popularity DESC
295 | LIMIT 10
296 | """, (album_id, ))
297 | album_tracks = cur.fetchall()
298 | if len(album_tracks) >= 4:
299 | rand_idx = random.sample(range(0, len(album_tracks)), 4)
300 | for idx in rand_idx:
301 | if album_tracks[idx][0] == ref_id:
302 | continue
303 |
304 | else:
305 | url_id = album_tracks[idx][5]
306 | url_pin = album_tracks[idx][6]
307 | track_url = track_url_encoder(url_id, url_pin)
308 | track_dict = {
309 | "track_id": album_tracks[idx][0],
310 | "track_name": album_tracks[idx][1],
311 | "album_name": album_tracks[idx][2],
312 | "artist_name": album_tracks[idx][3],
313 | "album_image": album_tracks[idx][4],
314 | "track_url": track_url
315 | }
316 | item_list.append(track_dict)
317 |
318 | if len(item_list) == 4:
319 | item_list = item_list[: 3]
320 |
321 | sim = np.load("src/database/roc-light.npy")
322 | recoms_list = sim[query, :]
323 | for i in recoms_list:
324 | idx = 'roc' + str(int(i))
325 | cur.execute("""
326 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
327 | FROM tracks_fts4 t
328 | INNER JOIN albums a
329 | ON t.album_id = a.album_id
330 | INNER JOIN artists art
331 | ON a.artist_id = art.artist_id
332 | WHERE t.ref_id = ?
333 | ORDER BY art.artist_popularity DESC """, (idx, ))
334 | track = cur.fetchone()
335 | url_id = track[5]
336 | url_pin = track[6]
337 | track_url = track_url_encoder(url_id, url_pin)
338 | track_dict = {
339 | "track_id": track[0],
340 | "track_name": track[1],
341 | "album_name": track[2],
342 | "artist_name": track[3],
343 | "album_image": track[4],
344 | "track_url": track_url
345 | }
346 | item_list.append(track_dict)
347 | conn.close()
348 | return item_list
349 |
350 |
351 | def recommend_ind(query):
352 | BASE_DIR = os.getcwd()
353 | DB_DIR = "src/database/rezo.db"
354 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
355 | conn = sqlite3.connect(DB_PATH)
356 | cur = conn.cursor()
357 | item_list = []
358 |
359 | ref_id = "ind" + str(query)
360 | cur.execute("SELECT album_id FROM tracks_fts4 WHERE ref_id = ?", (ref_id, ))
361 | album_id = cur.fetchone()[0]
362 | cur.execute(
363 | """
364 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
365 | FROM tracks_fts4 t
366 | INNER JOIN albums a
367 | ON t.album_id = a.album_id
368 | INNER JOIN artists art
369 | ON a.artist_id = art.artist_id
370 | WHERE t.album_id = ?
371 | ORDER BY art.artist_popularity DESC
372 | LIMIT 10
373 | """, (album_id, ))
374 | album_tracks = cur.fetchall()
375 | if len(album_tracks) >= 4:
376 | rand_idx = random.sample(range(0, len(album_tracks)), 4)
377 | for idx in rand_idx:
378 | if album_tracks[idx][0] == ref_id:
379 | continue
380 |
381 | else:
382 | url_id = album_tracks[idx][5]
383 | url_pin = album_tracks[idx][6]
384 | track_url = track_url_encoder(url_id, url_pin)
385 | track_dict = {
386 | "track_id": album_tracks[idx][0],
387 | "track_name": album_tracks[idx][1],
388 | "album_name": album_tracks[idx][2],
389 | "artist_name": album_tracks[idx][3],
390 | "album_image": album_tracks[idx][4],
391 | "track_url": track_url
392 | }
393 | item_list.append(track_dict)
394 |
395 | if len(item_list) == 4:
396 | item_list = item_list[: 3]
397 |
398 | sim = np.load("src/database/ind-light.npy")
399 | recoms_list = sim[query, :]
400 | for i in recoms_list:
401 | idx = 'ind' + str(int(i))
402 | cur.execute("""
403 | SELECT ref_id, track_name, a.album_name, art.artist_name, a.album_img, url_id, url_pin
404 | FROM tracks_fts4 t
405 | INNER JOIN albums a
406 | ON t.album_id = a.album_id
407 | INNER JOIN artists art
408 | ON a.artist_id = art.artist_id
409 | WHERE t.ref_id = ?
410 | ORDER BY art.artist_popularity DESC """, (idx, ))
411 | track = cur.fetchone()
412 | url_id = track[5]
413 | url_pin = track[6]
414 | track_url = track_url_encoder(url_id, url_pin)
415 | track_dict = {
416 | "track_id": track[0],
417 | "track_name": track[1],
418 | "album_name": track[2],
419 | "artist_name": track[3],
420 | "album_image": track[4],
421 | "track_url": track_url
422 | }
423 | item_list.append(track_dict)
424 | conn.close()
425 | return item_list
426 |
427 |
428 |
429 |
--------------------------------------------------------------------------------
/src/scripts/search.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import sqlite_spellfix
3 | import json
4 | import redis
5 | import datetime
6 | import os
7 | import secrets
8 | from . import secret
9 |
10 |
11 | def track_url_encoder(url_id: str, url_pin: str) -> str:
12 | cdn_list = secret.cdn_list
13 | cdn = secrets.choice(cdn_list)
14 | BASE_URL = secret.BASE_URL
15 | BITRATE = "96"
16 | track_url = f"https://{cdn}.{BASE_URL}/{url_pin}/{url_id}_{BITRATE}.mp4"
17 |
18 | return track_url
19 |
20 |
21 | def search_tracks(param):
22 | redis_client = redis.Redis(host='localhost', port=6379, db=0)
23 | cached_data = redis_client.get(param)
24 | if cached_data is not None:
25 | json_data = json.loads(cached_data)
26 | return json_data
27 |
28 | else:
29 | BASE_DIR = os.getcwd()
30 | DB_DIR = "src/database/rezo.db"
31 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
32 | conn = sqlite3.connect(DB_PATH)
33 | conn.enable_load_extension(True)
34 | conn.load_extension(sqlite_spellfix.extension_path())
35 | cur = conn.cursor()
36 | correctedquery = []
37 | for term in param.split():
38 | spellfix_query = "SELECT word FROM tracks_spell WHERE word MATCH ? and top=1"
39 | cur.execute(spellfix_query, (term,))
40 | r = cur.fetchone()
41 | correctedquery.append(r[0] if r is not None else term) # correct the word if any match in the spellfix table; if no match, keep the word spelled as it is (then the search will give no result!)
42 | correctedquery = ' '.join(correctedquery)
43 | correctedquery += '*'
44 | cur.execute(
45 | """
46 | SELECT ref_id, track_name, a.album_name, art.artist_name, t.url_id, cast(t.url_pin as text), a.album_img, t.track_id
47 | FROM tracks_fts4 t
48 | INNER JOIN albums a
49 | ON t.album_id = a.album_id
50 | INNER JOIN artists art
51 | ON a.artist_id = art.artist_id
52 | WHERE tracks_fts4
53 | MATCH ?
54 | ORDER BY art.artist_popularity DESC
55 | LIMIT 50
56 | """, (correctedquery, )
57 | )
58 |
59 | dict_list = []
60 | for item in cur.fetchmany(50):
61 | url_id = item[4]
62 | url_pin = str(item[5])
63 | track_url = track_url_encoder(url_id, url_pin)
64 | item_dict = {
65 | "ref_id": item[0],
66 | "track_name": item[1],
67 | "album_name": item[2],
68 | "artist_name": item[3],
69 | "album_image": item[6],
70 | "track_url": track_url,
71 | "track_id": item[7]
72 | }
73 | dict_list.append(item_dict)
74 |
75 | unq_list = list({track['track_id']:track for track in dict_list}.values())
76 | cur.close()
77 | conn.close()
78 | redis_client.setex(param, datetime.timedelta(hours=12), json.dumps(unq_list))
79 | return unq_list
80 |
81 |
82 |
83 | def search_artists(param):
84 | redis_client = redis.Redis(host='localhost', port=6379, db=1)
85 | cached_data = redis_client.get(param)
86 |
87 | if cached_data is not None:
88 | json_data = json.loads(cached_data)
89 | return json_data
90 |
91 | else:
92 | BASE_DIR = os.getcwd()
93 | DB_DIR = "src/database/rezo.db"
94 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
95 | conn = sqlite3.connect(DB_PATH)
96 | cur = conn.cursor()
97 |
98 | cur.execute(
99 | """
100 | SELECT * FROM artists
101 | WHERE artist_name
102 | LIKE ('%' || ? || '%')
103 | ORDER BY artist_popularity DESC
104 | LIMIT 50
105 | """, (param, )
106 | )
107 |
108 | dict_list = []
109 | query_result = cur.fetchall()
110 |
111 | for item in query_result:
112 | item_dict = {
113 | "artist_id": item[0],
114 | "artist_name": item[1],
115 | "artist_image": item[4],
116 | }
117 | dict_list.append(item_dict)
118 | conn.close()
119 | redis_client.setex(param, datetime.timedelta(days=7), json.dumps(dict_list))
120 | return dict_list
121 |
122 |
123 |
124 | def search_albums(param):
125 | redis_client = redis.Redis(host='localhost', port=6379, db=2)
126 | cached_data = redis_client.get(param)
127 |
128 | if cached_data is not None:
129 | json_data = json.loads(cached_data)
130 | return json_data
131 |
132 | else:
133 | BASE_DIR = os.getcwd()
134 | DB_DIR = "src/database/rezo.db"
135 | DB_PATH = os.path.join(BASE_DIR, DB_DIR)
136 | conn = sqlite3.connect(DB_PATH)
137 | conn.enable_load_extension(True)
138 | conn.load_extension(sqlite_spellfix.extension_path())
139 | cur = conn.cursor()
140 |
141 | correctedquery = []
142 | for term in param.split():
143 | spellfix_query = "SELECT word FROM albums_spell WHERE word MATCH ? and top=1"
144 | cur.execute(spellfix_query, (term,))
145 | r = cur.fetchone()
146 | correctedquery.append(r[0] if r is not None else term) # correct the word if any match in the spellfix table; if no match, keep the word spelled as it is (then the search will give no result!)
147 |
148 | correctedquery = ' '.join(correctedquery)
149 | correctedquery += '*'
150 |
151 | cur.execute(
152 | """
153 | SELECT * FROM albums_fts4
154 | WHERE albums_fts4
155 | MATCH ?
156 | ORDER BY album_popularity DESC
157 | LIMIT 50
158 | """, (correctedquery, )
159 | )
160 |
161 | dict_list = []
162 | query_result = cur.fetchall()
163 | for album in query_result:
164 | album_dict = {
165 | 'artist_id': album[0],
166 | 'album_id': album[1],
167 | 'album_img': album[2],
168 | 'artist_name': album[4],
169 | 'album_name': album[5],
170 | }
171 | dict_list.append(album_dict)
172 |
173 | cur.close()
174 | conn.close()
175 | redis_client.setex(param, datetime.timedelta(days=1), json.dumps(dict_list))
176 | return dict_list
177 |
178 |
179 |
--------------------------------------------------------------------------------
/src/scripts/trending.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | from . import secret
4 | import redis
5 | import datetime
6 |
7 |
8 | def fetch_trending():
9 | redis_client = redis.Redis(host='localhost', port=6379, db=3)
10 | cached_data = redis_client.get('data')
11 |
12 | if cached_data is not None:
13 | json_data = json.loads(cached_data)
14 | return json_data
15 |
16 | else:
17 | res = requests.get("https://www.jiosaavn.com/featured/now-trending---english/pm49jiq,CNs_").text
18 | id = res.split('"type":"playlist","id":"')[1].split('"')[0]
19 | res = requests.get(f"https://www.jiosaavn.com/api.php?__call=playlist.getDetails&_format=json&cc=in&_marker=0%3F_marker%3D0&listid={id}")
20 |
21 | songs_json = res.text.encode().decode('unicode-escape')
22 | songs_json = json.loads(songs_json)
23 | songs_list = songs_json['songs']
24 |
25 | result_list = []
26 |
27 | for item in songs_list:
28 | try:
29 | song_prev_url = item['media_preview_url']
30 | except Exception as e:
31 | continue
32 |
33 | song_id = song_prev_url.split('/')[3]
34 | song_url = song_prev_url.split('/')[4].split('_')[0]
35 | url = f"https://{secret.cdn_list[0]}.{secret.BASE_URL}/{song_id}/{song_url}_96.mp4"
36 |
37 | img_url = item['image']
38 | img_suffix = "500x500.jpg"
39 | album_image = img_url[: -11] + img_suffix
40 |
41 | song_dict = {
42 | "ref_id": "trending",
43 | "track_name": item['song'],
44 | "album_name": item['album'],
45 | "artist_name": item['primary_artists'],
46 | "album_image": album_image,
47 | "track_url": url
48 | }
49 |
50 | result_list.append(song_dict)
51 |
52 | redis_client.setex('data', datetime.timedelta(days=1), json.dumps(result_list))
53 | return result_list
54 |
55 |
--------------------------------------------------------------------------------
/tests/test_main.py:
--------------------------------------------------------------------------------
1 | from fastapi.param_functions import Query
2 | from fastapi.testclient import TestClient
3 |
4 | from main import app
5 |
6 | import redis
7 | import logging
8 |
9 |
10 | client = TestClient(app)
11 | logger = logging.getLogger()
12 |
13 |
14 | def test_search_tracks():
15 | body = {
16 | "query": "everything has changed"
17 | }
18 |
19 | response = client.post('/search/tracks', json=body)
20 | assert response.status_code == 200
21 |
22 |
23 |
24 | def test_album_search():
25 | body = {
26 | "query": "Red (Big Machine Radio Release Special) Taylor Swift"
27 | }
28 |
29 | response = client.post('/search/albums', json=body)
30 | assert response.status_code == 200
31 |
32 |
33 |
34 | def test_artist_search():
35 | body = {
36 | "query": "taylor swift"
37 | }
38 |
39 | response = client.post('/search/artists', json=body)
40 | assert response.status_code == 200
41 |
42 |
43 |
44 | def test_recommendation():
45 | pop_query = {
46 | "ref_id": "pop120"
47 | }
48 | rap_query = {
49 | "ref_id": "rap120"
50 | }
51 | roc_query = {
52 | "ref_id": "roc120"
53 | }
54 | edm_query = {
55 | "ref_id": "edm120"
56 | }
57 | ind_query = {
58 | "ref_id": "ind120"
59 | }
60 |
61 | response = client.post("/recommend", json=pop_query)
62 | assert response.status_code == 200
63 | response = client.post("/recommend", json=rap_query)
64 | assert response.status_code == 200
65 | response = client.post("/recommend", json=roc_query)
66 | assert response.status_code == 200
67 | response = client.post("/recommend", json=edm_query)
68 | assert response.status_code == 200
69 | response = client.post("/recommend", json=ind_query)
70 | assert response.status_code == 200
71 |
72 |
73 | def test_fetch_tracks():
74 | data = {
75 | "album_id": "34OkZVpuzBa9y40DCy0LPR"
76 | }
77 |
78 | response = client.post("/fetch/tracks", json=data)
79 | assert response.status_code == 200
80 |
81 |
82 | def test_fetch_albums():
83 | data = {
84 | "artist_id": "6qqNVTkY8uBg9cP3Jd7DAH"
85 | }
86 |
87 | response = client.post("/fetch/albums", json=data)
88 | assert response.status_code == 200
89 |
90 | def test_redis_server():
91 |
92 | redis_client = redis.Redis(host='localhost', port=6379, db=0)
93 | x = redis_client.ping()
94 | assert x == True
--------------------------------------------------------------------------------