taslim19 commited on
Commit
4ebaa1c
·
1 Parent(s): 57343c2

refactor(youtube): integrate yt-dlp and remove OWNERR_ID

Browse files
DragMusic/platforms/Youtube.py CHANGED
@@ -6,7 +6,6 @@ from typing import Union
6
  import yt_dlp
7
  from pyrogram.enums import MessageEntityType
8
  from pyrogram.types import Message
9
- from youtubesearchpython.__future__ import VideosSearch
10
 
11
  from DragMusic.utils.database import is_on_off
12
  from DragMusic.utils.formatters import time_to_seconds
@@ -30,324 +29,132 @@ async def shell_cmd(cmd):
30
  class YouTubeAPI:
31
  def __init__(self):
32
  self.base = "https://www.youtube.com/watch?v="
33
- self.regex = r"(?:youtube\.com|youtu\.be)"
34
- self.status = "https://www.youtube.com/oembed?url="
35
- self.listbase = "https://youtube.com/playlist?list="
36
- self.reg = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
37
-
38
- async def exists(self, link: str, videoid: Union[bool, str] = None):
39
- if videoid:
40
- link = self.base + link
41
- if re.search(self.regex, link):
42
- return True
43
- else:
44
- return False
45
-
46
- async def url(self, message_1: Message) -> Union[str, None]:
47
- messages = [message_1]
48
- if message_1.reply_to_message:
49
- messages.append(message_1.reply_to_message)
50
- text = ""
51
- offset = None
52
- length = None
53
- for message in messages:
54
- if offset:
55
- break
56
- if message.entities:
57
- for entity in message.entities:
58
- if entity.type == MessageEntityType.URL:
59
- text = message.text or message.caption
60
- offset, length = entity.offset, entity.length
61
- break
62
- elif message.caption_entities:
63
- for entity in message.caption_entities:
64
- if entity.type == MessageEntityType.TEXT_LINK:
65
- return entity.url
66
- if offset in (None,):
67
- return None
68
- return text[offset : offset + length]
69
-
70
- async def details(self, link: str, videoid: Union[bool, str] = None):
71
- if videoid:
72
- link = self.base + link
73
- if "&" in link:
74
- link = link.split("&")[0]
75
- results = VideosSearch(link, limit=1)
76
- for result in (await results.next())["result"]:
77
- title = result["title"]
78
- duration_min = result["duration"]
79
- thumbnail = result["thumbnails"][0]["url"].split("?")[0]
80
- vidid = result["id"]
81
- if str(duration_min) == "None":
82
- duration_sec = 0
83
- else:
84
- duration_sec = int(time_to_seconds(duration_min))
85
  return title, duration_min, duration_sec, thumbnail, vidid
86
 
87
- async def title(self, link: str, videoid: Union[bool, str] = None):
88
- if videoid:
89
- link = self.base + link
90
- if "&" in link:
91
- link = link.split("&")[0]
92
- results = VideosSearch(link, limit=1)
93
- for result in (await results.next())["result"]:
94
- title = result["title"]
95
- return title
96
-
97
- async def duration(self, link: str, videoid: Union[bool, str] = None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  if videoid:
99
- link = self.base + link
100
- if "&" in link:
101
- link = link.split("&")[0]
102
- results = VideosSearch(link, limit=1)
103
- for result in (await results.next())["result"]:
104
- duration = result["duration"]
105
- return duration
106
-
107
- async def thumbnail(self, link: str, videoid: Union[bool, str] = None):
108
- if videoid:
109
- link = self.base + link
110
- if "&" in link:
111
- link = link.split("&")[0]
112
- results = VideosSearch(link, limit=1)
113
- for result in (await results.next())["result"]:
114
- thumbnail = result["thumbnails"][0]["url"].split("?")[0]
115
- return thumbnail
116
-
117
- async def video(self, link: str, videoid: Union[bool, str] = None):
118
- if videoid:
119
- link = self.base + link
120
- if "&" in link:
121
- link = link.split("&")[0]
122
- proc = await asyncio.create_subprocess_exec(
123
- "yt-dlp",
124
- "--cookies",
125
- "cookies.txt",
126
- "-g",
127
- "-f",
128
- "best[height<=?720][width<=?1280]",
129
- f"{link}",
130
- stdout=asyncio.subprocess.PIPE,
131
- stderr=asyncio.subprocess.PIPE,
132
- )
133
- stdout, stderr = await proc.communicate()
134
- if stdout:
135
- return 1, stdout.decode().split("\n")[0]
136
- else:
137
- return 0, stderr.decode()
138
-
139
- async def playlist(self, link, limit, user_id, videoid: Union[bool, str] = None):
140
- if videoid:
141
- link = self.listbase + link
142
- if "&" in link:
143
- link = link.split("&")[0]
144
- playlist = await shell_cmd(
145
- f"yt-dlp --cookies cookies.txt -i --get-id --flat-playlist --playlist-end {limit} --skip-download {link}"
146
- )
147
- try:
148
- result = playlist.split("\n")
149
- for key in result:
150
- if key == "":
151
- result.remove(key)
152
- except:
153
- result = []
154
- return result
155
-
156
- async def track(self, link: str, videoid: Union[bool, str] = None):
157
- if videoid:
158
- link = self.base + link
159
- if "&" in link:
160
- link = link.split("&")[0]
161
- results = VideosSearch(link, limit=1)
162
- for result in (await results.next())["result"]:
163
- title = result["title"]
164
- duration_min = result["duration"]
165
- vidid = result["id"]
166
- yturl = result["link"]
167
- thumbnail = result["thumbnails"][0]["url"].split("?")[0]
168
  track_details = {
169
- "title": title,
170
- "link": yturl,
171
- "vidid": vidid,
172
- "duration_min": duration_min,
173
- "thumb": thumbnail,
174
  }
175
- return track_details, vidid
176
-
177
- async def formats(self, link: str, videoid: Union[bool, str] = None):
178
- if videoid:
179
- link = self.base + link
180
- if "&" in link:
181
- link = link.split("&")[0]
182
- ytdl_opts = {"cookiefile": "cookies.txt", "quiet": True}
183
- ydl = yt_dlp.YoutubeDL(ytdl_opts)
184
- with ydl:
185
- formats_available = []
186
- r = ydl.extract_info(link, download=False)
187
- for format in r["formats"]:
188
- try:
189
- str(format["format"])
190
- except:
191
- continue
192
- if not "dash" in str(format["format"]).lower():
193
- try:
194
- format["format"]
195
- format["filesize"]
196
- format["format_id"]
197
- format["ext"]
198
- format["format_note"]
199
- except:
200
- continue
201
- formats_available.append(
202
- {
203
- "format": format["format"],
204
- "filesize": format["filesize"],
205
- "format_id": format["format_id"],
206
- "ext": format["ext"],
207
- "format_note": format["format_note"],
208
- "yturl": link,
209
- }
210
- )
211
- return formats_available, link
212
-
213
- async def slider(
214
- self,
215
- link: str,
216
- query_type: int,
217
- videoid: Union[bool, str] = None,
218
- ):
219
- if videoid:
220
- link = self.base + link
221
- if "&" in link:
222
- link = link.split("&")[0]
223
- a = VideosSearch(link, limit=10)
224
- result = (await a.next()).get("result")
225
- title = result[query_type]["title"]
226
- duration_min = result[query_type]["duration"]
227
- vidid = result[query_type]["id"]
228
- thumbnail = result[query_type]["thumbnails"][0]["url"].split("?")[0]
229
- return title, duration_min, thumbnail, vidid
230
-
231
- async def download(
232
- self,
233
- link: str,
234
- mystic,
235
- video: Union[bool, str] = None,
236
- videoid: Union[bool, str] = None,
237
- songaudio: Union[bool, str] = None,
238
- songvideo: Union[bool, str] = None,
239
- format_id: Union[bool, str] = None,
240
- title: Union[bool, str] = None,
241
- ) -> str:
242
- if videoid:
243
- link = self.base + link
244
- loop = asyncio.get_running_loop()
245
-
246
- def audio_dl():
247
- ydl_optssx = {
248
- "cookiefile": "cookies.txt",
249
- "format": "bestaudio/best",
250
- "outtmpl": "downloads/%(id)s.%(ext)s",
251
- "geo_bypass": True,
252
- "nocheckcertificate": True,
253
- "quiet": True,
254
- "no_warnings": True,
255
- }
256
- x = yt_dlp.YoutubeDL(ydl_optssx)
257
- info = x.extract_info(link, False)
258
- xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}")
259
- if os.path.exists(xyz):
260
- return xyz
261
- x.download([link])
262
- return xyz
263
-
264
- def video_dl():
265
- ydl_optssx = {
266
- "format": "(bestvideo[height<=?720][width<=?1280][ext=mp4])+(bestaudio[ext=m4a])",
267
- "outtmpl": "downloads/%(id)s.%(ext)s",
268
- "geo_bypass": True,
269
- "nocheckcertificate": True,
270
- "quiet": True,
271
- "no_warnings": True,
272
- }
273
- x = yt_dlp.YoutubeDL(ydl_optssx)
274
- info = x.extract_info(link, False)
275
- xyz = os.path.join("downloads", f"{info['id']}.{info['ext']}")
276
- if os.path.exists(xyz):
277
- return xyz
278
- x.download([link])
279
- return xyz
280
-
281
- def song_video_dl():
282
- formats = f"{format_id}+140"
283
- fpath = f"downloads/{title}"
284
- ydl_optssx = {
285
- "cookiefile": "cookies.txt",
286
- "format": formats,
287
- "outtmpl": fpath,
288
- "geo_bypass": True,
289
- "nocheckcertificate": True,
290
- "quiet": True,
291
- "no_warnings": True,
292
- "prefer_ffmpeg": True,
293
- "merge_output_format": "mp4",
294
- }
295
- x = yt_dlp.YoutubeDL(ydl_optssx)
296
- x.download([link])
297
-
298
- def song_audio_dl():
299
- fpath = f"downloads/{title}.%(ext)s"
300
- ydl_optssx = {
301
- "cookiefile": "cookies.txt",
302
- "format": format_id,
303
- "outtmpl": fpath,
304
- "geo_bypass": True,
305
- "nocheckcertificate": True,
306
- "quiet": True,
307
- "no_warnings": True,
308
- "prefer_ffmpeg": True,
309
- "postprocessors": [
310
- {
311
- "key": "FFmpegExtractAudio",
312
- "preferredcodec": "mp3",
313
- "preferredquality": "192",
314
- }
315
- ],
316
- }
317
- x = yt_dlp.YoutubeDL(ydl_optssx)
318
- x.download([link])
319
-
320
- if songvideo:
321
- await loop.run_in_executor(None, song_video_dl)
322
- fpath = f"downloads/{title}.mp4"
323
- return fpath
324
- elif songaudio:
325
- await loop.run_in_executor(None, song_audio_dl)
326
- fpath = f"downloads/{title}.mp3"
327
- return fpath
328
- elif video:
329
- if await is_on_off(1):
330
- direct = True
331
- downloaded_file = await loop.run_in_executor(None, video_dl)
332
- else:
333
- proc = await asyncio.create_subprocess_exec(
334
- "yt-dlp",
335
- "--cookies",
336
- "cookies.txt",
337
- "-g",
338
- "-f",
339
- "best[height<=?720][width<=?1280]",
340
- f"{link}",
341
- stdout=asyncio.subprocess.PIPE,
342
- stderr=asyncio.subprocess.PIPE,
343
- )
344
- stdout, stderr = await proc.communicate()
345
- if stdout:
346
- downloaded_file = stdout.decode().split("\n")[0]
347
- direct = None
348
- else:
349
- return
350
- else:
351
- direct = True
352
- downloaded_file = await loop.run_in_executor(None, audio_dl)
353
- return downloaded_file, direct
 
6
  import yt_dlp
7
  from pyrogram.enums import MessageEntityType
8
  from pyrogram.types import Message
 
9
 
10
  from DragMusic.utils.database import is_on_off
11
  from DragMusic.utils.formatters import time_to_seconds
 
29
  class YouTubeAPI:
30
  def __init__(self):
31
  self.base = "https://www.youtube.com/watch?v="
32
+ self.opts = {"nocheckcertificate": True, "quiet": True, "no_warnings": True}
33
+
34
+ async def get_info(self, link: str):
35
+ """Fetches all video information using yt-dlp."""
36
+ full_url = self.base + link if not link.startswith("http") else link
37
+ with yt_dlp.YoutubeDL(self.opts) as ydl:
38
+ info = ydl.extract_info(full_url, download=False)
39
+ return info
40
+
41
+ async def details(self, link: str):
42
+ """Gets essential video details."""
43
+ info = await self.get_info(link)
44
+ title = info.get("title", "Unknown Title")
45
+ duration_min = info.get("duration_string", "0:00")
46
+ duration_sec = info.get("duration", 0)
47
+ thumbnail = info.get("thumbnail", "")
48
+ vidid = info.get("id", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  return title, duration_min, duration_sec, thumbnail, vidid
50
 
51
+ async def url(self, message: "Message") -> Union[str, None]:
52
+ """Extracts a URL from a Pyrogram message."""
53
+ for entity in message.entities or message.caption_entities or []:
54
+ if entity.type in ["url", "text_link"]:
55
+ return entity.url or message.text[entity.offset:entity.offset + entity.length]
56
+ return None
57
+
58
+ async def title(self, link: str):
59
+ info = await self.get_info(link)
60
+ return info.get("title", "Unknown Title")
61
+
62
+ async def duration(self, link: str):
63
+ info = await self.get_info(link)
64
+ return info.get("duration_string", "0:00")
65
+
66
+ async def thumbnail(self, link: str):
67
+ info = await self.get_info(link)
68
+ return info.get("thumbnail", "")
69
+
70
+ async def get_best_audio_url(self, link: str):
71
+ """Gets the URL of the best quality audio stream."""
72
+ info = await self.get_info(link)
73
+ best_audio = None
74
+ for f in info.get("formats", []):
75
+ if f.get("acodec") != "none" and f.get("vcodec") == "none":
76
+ if best_audio is None or f.get("abr", 0) > best_audio.get("abr", 0):
77
+ best_audio = f
78
+ return best_audio["url"] if best_audio else None
79
+
80
+ async def get_best_video_url(self, link: str, quality: str = "720"):
81
+ """Gets the URL of the best video stream for a given quality."""
82
+ info = await self.get_info(link)
83
+ best_video = None
84
+ for f in info.get("formats", []):
85
+ if f.get("height") and f.get("height") <= int(quality) and f.get("vcodec") != "none":
86
+ if best_video is None or f.get("height") > best_video.get("height"):
87
+ best_video = f
88
+ return best_video["url"] if best_video else None
89
+
90
+
91
+ async def playlist(self, link: str, limit: int, user_id, videoid: bool = False):
92
+ """Fetches video IDs from a playlist."""
93
  if videoid:
94
+ link = f"https://www.youtube.com/playlist?list={link}"
95
+
96
+ playlist_opts = self.opts.copy()
97
+ playlist_opts["extract_flat"] = "in_playlist"
98
+ playlist_opts["playlistend"] = limit
99
+
100
+ with yt_dlp.YoutubeDL(playlist_opts) as ydl:
101
+ info = ydl.extract_info(link, download=False)
102
+ return [entry["id"] for entry in info.get("entries", []) if entry]
103
+
104
+ async def track(self, link: str):
105
+ """Gets track details for a single video."""
106
+ info = await self.get_info(link)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  track_details = {
108
+ "title": info.get("title", "Unknown Title"),
109
+ "link": info.get("webpage_url", ""),
110
+ "vidid": info.get("id", ""),
111
+ "duration_min": info.get("duration_string", "0:00"),
112
+ "thumb": info.get("thumbnail", ""),
113
  }
114
+ return track_details, info.get("id", "")
115
+
116
+ async def formats(self, link: str):
117
+ """Gets available formats for a video."""
118
+ info = await self.get_info(link)
119
+ formats_available = []
120
+ for f in info.get("formats", []):
121
+ if not "dash" in str(f.get("format_note", "")).lower():
122
+ formats_available.append({
123
+ "format": f.get("format", ""),
124
+ "filesize": f.get("filesize"),
125
+ "format_id": f.get("format_id", ""),
126
+ "ext": f.get("ext", ""),
127
+ "format_note": f.get("format_note", ""),
128
+ "yturl": info.get("webpage_url", ""),
129
+ })
130
+ return formats_available, info.get("webpage_url", "")
131
+
132
+ async def download(self, link: str, mystic, video: bool = None, format_id: str = None) -> Union[str, None]:
133
+ """Downloads a video or audio file and returns the path."""
134
+ file_path = f"downloads/{link}.%(ext)s"
135
+
136
+ dl_opts = self.opts.copy()
137
+ dl_opts["outtmpl"] = file_path
138
+
139
+ if video:
140
+ dl_opts["format"] = format_id if format_id else "best[height<=?720][width<=?1280]/best"
141
+ else: # audio
142
+ dl_opts["format"] = "bestaudio/best"
143
+ dl_opts["postprocessors"] = [{
144
+ "key": "FFmpegExtractAudio",
145
+ "preferredcodec": "mp3",
146
+ "preferredquality": "192",
147
+ }]
148
+
149
+ try:
150
+ with yt_dlp.YoutubeDL(dl_opts) as ydl:
151
+ ydl.download([link])
152
+
153
+ # Find the downloaded file since the extension is dynamic
154
+ for file in os.listdir("downloads"):
155
+ if link in file:
156
+ return os.path.join("downloads", file)
157
+ return None
158
+ except Exception as e:
159
+ print(str(e))
160
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
DragMusic/plugins/bot/mustjoin.py CHANGED
@@ -21,12 +21,12 @@ async def must_join_channel(app: Client, msg: Message):
21
  chat_info = await app.get_chat(MUST_JOIN)
22
  link = chat_info.invite_link
23
  try:
24
- await msg.reply_text(f"❅ ʜᴇʏ ᴛʜᴇʀᴇ, ɴɪᴄᴇ ᴛᴏ ᴍᴇᴇᴛ ᴜʜʜ !\n\n❅ ɪғ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴜsᴇ ˹ʙᴜɢ ✘ ϻʊsɪx ˼, ᴛʜᴇɴ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ ʙᴇʟᴏᴡ ʙᴜᴛᴛᴏɴ ᴀɴᴅ ʏᴏᴜ ᴊᴏɪɴᴇᴅ, ᴛʜᴇɴ ʏᴏᴜ ᴄᴀɴ ᴜsᴇ ᴀʟʟ ᴍʏ ᴄᴏᴍᴍᴀɴᴅs ",
25
  reply_markup=InlineKeyboardMarkup(
26
  [
27
  [
28
- InlineKeyboardButton("ᴜᴘᴅᴀᴛᴇ", url="https://t.me/haatsoja"),
29
- InlineKeyboardButton("sᴜᴘᴘᴏʀᴛ", url="https://t.me/dragbackup"),
30
  ]
31
  ]
32
  )
 
21
  chat_info = await app.get_chat(MUST_JOIN)
22
  link = chat_info.invite_link
23
  try:
24
+ await msg.reply_text(f"❅ ʜᴇʏ ᴛʜᴇʀᴇ, ɴɪᴄᴇ ᴛᴏ ᴍᴇᴇᴛ ᴜʜʜ !\n\n❅ ɪғ ʏᴏᴜ ᴡᴀɴᴛ ᴛᴏ ᴜsᴇ ˹˼, ᴛʜᴇɴ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ ʙᴇʟᴏᴡ ʙᴜᴛᴛᴏɴ ᴀɴᴅ ʏᴏᴜ ᴊᴏɪɴᴇᴅ, ᴛʜᴇɴ ʏᴏᴜ ᴄᴀɴ ᴜsᴇ ᴀʟʟ ᴍʏ ᴄᴏᴍᴍᴀɴᴅs ",
25
  reply_markup=InlineKeyboardMarkup(
26
  [
27
  [
28
+ InlineKeyboardButton("ᴜᴘᴅᴀᴛᴇ", url="https://t.me/draagbots"),
29
+ InlineKeyboardButton("sᴜᴘᴘᴏʀᴛ", url="https://t.me/dragbotsupport"),
30
  ]
31
  ]
32
  )
DragMusic/plugins/bot/start.py CHANGED
@@ -20,7 +20,7 @@ from DragMusic.utils.database import (
20
  from DragMusic.utils.decorators.language import LanguageStart
21
  from DragMusic.utils.formatters import get_readable_time
22
  from DragMusic.utils.inline import help_pannel, private_panel, start_panel
23
- from config import BANNED_USERS, OWNER_ID, OWNERR_ID
24
  from strings import get_string
25
 
26
 
@@ -50,9 +50,6 @@ async def start_pm(client, message: Message, _):
50
  name = message.text.split(None, 1)[1]
51
  if name.startswith("help"):
52
  keyboard = help_pannel(_)
53
- keyboard.inline_keyboard.append([
54
- InlineKeyboardButton("👑 Owner", url=f"tg://user?id={OWNERR_ID}")
55
- ])
56
  await message.reply_text(
57
  text=(
58
  f"<b>ʏᴏᴏ {message.from_user.mention}, <a href='https://files.catbox.moe/w8m75t.jpg' target='_blank'>🫧</a></b>\n\n"
@@ -117,9 +114,6 @@ async def start_pm(client, message: Message, _):
117
  return
118
  else:
119
  out = private_panel(_)
120
- out.append([
121
- InlineKeyboardButton("👑 Owner", url=f"tg://user?id={OWNERR_ID}")
122
- ])
123
  await message.reply_text(
124
  text=(
125
  f"<b>ʏᴏᴏ {message.from_user.mention}, <a href='https://files.catbox.moe/w8m75t.jpg' target='_blank'>🫧</a></b>\n\n"
 
20
  from DragMusic.utils.decorators.language import LanguageStart
21
  from DragMusic.utils.formatters import get_readable_time
22
  from DragMusic.utils.inline import help_pannel, private_panel, start_panel
23
+ from config import BANNED_USERS, OWNER_ID
24
  from strings import get_string
25
 
26
 
 
50
  name = message.text.split(None, 1)[1]
51
  if name.startswith("help"):
52
  keyboard = help_pannel(_)
 
 
 
53
  await message.reply_text(
54
  text=(
55
  f"<b>ʏᴏᴏ {message.from_user.mention}, <a href='https://files.catbox.moe/w8m75t.jpg' target='_blank'>🫧</a></b>\n\n"
 
114
  return
115
  else:
116
  out = private_panel(_)
 
 
 
117
  await message.reply_text(
118
  text=(
119
  f"<b>ʏᴏᴏ {message.from_user.mention}, <a href='https://files.catbox.moe/w8m75t.jpg' target='_blank'>🫧</a></b>\n\n"
config.py CHANGED
@@ -30,7 +30,7 @@ LOGGER_ID = int(getenv("LOGGER_ID", ))
30
 
31
  # Get this value from @FallenxBot on Telegram by /id
32
  OWNER_ID = int(getenv("OWNER_ID", ))
33
- OWNERR_ID = 7361622601
34
  ## Fill these variables if you're deploying on heroku.
35
  # Your heroku app name
36
  HEROKU_APP_NAME = getenv("HEROKU_APP_NAME")
 
30
 
31
  # Get this value from @FallenxBot on Telegram by /id
32
  OWNER_ID = int(getenv("OWNER_ID", ))
33
+
34
  ## Fill these variables if you're deploying on heroku.
35
  # Your heroku app name
36
  HEROKU_APP_NAME = getenv("HEROKU_APP_NAME")
requirements.txt CHANGED
@@ -30,7 +30,7 @@ lyricsgenius
30
  spotipy
31
  SafoneAPI
32
  youtube_search
33
- youtube-search-python
34
  googlesearch-python
35
  unidecode
36
  #git+https://github.com/joetats/youtube_search@master
 
30
  spotipy
31
  SafoneAPI
32
  youtube_search
33
+ youtube-search-python==1.6.6
34
  googlesearch-python
35
  unidecode
36
  #git+https://github.com/joetats/youtube_search@master