A powerful Lavalink library for Discord.py.

Overview

logo.png?raw=true

https://api.codacy.com/project/badge/Grade/d020ed97fd2a46fcb1f42bd3bc397e63

A robust and powerful Lavalink wrapper for Discord.py!

Documentation

Official Documentation.

Support

For support using WaveLink, please join the official support server on Discord.

Discord

Installation

The following commands are currently the valid ways of installing WaveLink.

WaveLink requires Python 3.7+

Windows

py -3.7 -m pip install Wavelink

Linux

python3.7 -m pip install Wavelink

Getting Started

A quick and easy bot example:

import discord
import wavelink
from discord.ext import commands


class Bot(commands.Bot):

    def __init__(self):
        super(Bot, self).__init__(command_prefix=['audio ', 'wave ','aw '])

        self.add_cog(Music(self))

    async def on_ready(self):
        print(f'Logged in as {self.user.name} | {self.user.id}')


class Music(commands.Cog):

    def __init__(self, bot):
        self.bot = bot

        if not hasattr(bot, 'wavelink'):
            self.bot.wavelink = wavelink.Client(bot=self.bot)

        self.bot.loop.create_task(self.start_nodes())

    async def start_nodes(self):
        await self.bot.wait_until_ready()

        # Initiate our nodes. For this example we will use one server.
        # Region should be a discord.py guild.region e.g sydney or us_central (Though this is not technically required)
        await self.bot.wavelink.initiate_node(host='127.0.0.1',
                                              port=2333,
                                              rest_uri='http://127.0.0.1:2333',
                                              password='youshallnotpass',
                                              identifier='TEST',
                                              region='us_central')

    @commands.command(name='connect')
    async def connect_(self, ctx, *, channel: discord.VoiceChannel=None):
        if not channel:
            try:
                channel = ctx.author.voice.channel
            except AttributeError:
                raise discord.DiscordException('No channel to join. Please either specify a valid channel or join one.')

        player = self.bot.wavelink.get_player(ctx.guild.id)
        await ctx.send(f'Connecting to **`{channel.name}`**')
        await player.connect(channel.id)

    @commands.command()
    async def play(self, ctx, *, query: str):
        tracks = await self.bot.wavelink.get_tracks(f'ytsearch:{query}')

        if not tracks:
            return await ctx.send('Could not find any songs with that query.')

        player = self.bot.wavelink.get_player(ctx.guild.id)
        if not player.is_connected:
            await ctx.invoke(self.connect_)

        await ctx.send(f'Added {str(tracks[0])} to the queue.')
        await player.play(tracks[0])


bot = Bot()
bot.run('TOKEN')
Comments
  • TypeError: 'coroutine' object is not callable

    TypeError: 'coroutine' object is not callable

    I'm getting this error when I try and play music on using my discord bot.

    Task exception was never retrieved future: <Task finished name='Task-32' coro=<Websocket.process_data() done, defined at C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py:137> exception=TypeError("'coroutine' object is not callable")> Traceback (most recent call last): File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py", line 156, in process_data event, payload = await self._get_event_payload(data['type'], data) File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py", line 184, in get_event_payload track = await self.node.build_track(cls=wavelink.Track, identifier=base64) File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\pool.py", line 294, in build_track return cls(identifier, data) TypeError: 'coroutine' object is not callable Task exception was never retrieved future: <Task finished name='Task-39' coro=<Websocket.process_data() done, defined at C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py:137> exception=TypeError("'coroutine' object is not callable")> Traceback (most recent call last): File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py", line 156, in process_data event, payload = await self._get_event_payload(data['type'], data) File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\websocket.py", line 184, in get_event_payload track = await self.node.build_track(cls=wavelink.Track, identifier=base64) File "C:\Users\icep2\AppData\Local\Programs\Python\Python39\lib\site-packages\wavelink\pool.py", line 294, in build_track return cls(identifier, data) TypeError: 'coroutine' object is not callable

    Not a Bug 
    opened by Kaz213 15
  • Player.is_playing always returns False after track replace

    Player.is_playing always returns False after track replace

    Was broken in this commit: https://github.com/PythonistaGuild/Wavelink/commit/f126755662cdf17199b7814dbe4abcb5f038966b After first track has been replaced with Player.play(track), track_end event triggered after Player.play done. So, after first track replaced, Player._source is always None and Player.is_playing() always returns False.

    Consider changing code from commit above to something like this:

    if event == 'track_end' and payload.get('reason') == 'FINISHED':
        player._source = None
    
    opened by wladbelsky 7
  • Add support for

    Add support for "Session resuming" & "session data queueing".

    This PR is Implemented in #66

    Added New attributes

    • resume_session: bool
    • resume_timeout: float
    • resume_key: str (Best left None)
    • payload_timeout: float This is basically how recent requests shall be sent to the server
    • Websocket.reset() method.

    What are the new features?

    Websocket queueing.

    • Websocket._send() method now queues any data that is requested to be sent while the WS is disconnected.
    • New method Websocket.send_queue() which is called when connecting, and sends queue only when a session is resumed and Websocket is connected.
    • New exception NodeSessionClosedError raised when node closes a session or doesn't resume the old session. Handled within _connect method.

    Resuming

    • example of use of new features
    import string
    import secret
    class key:
        def __init__(self, len, first_key="NoSnoopingOnMyLava"):
            self.len = len
            self.persistent = first_key
    
        def __str__(self):
            return self.persistent
    
        def __repr__(self):
            """This should generate a key and shall make it persistent """
            self.persistent = SomeSecurePasswordGenerator(self.len)
            return self.persistent
    
    ---------------
    
     async def start_nodes(self) -> None:
        """Connect and intiate nodes."""
        nodes = {'MAIN': {'host': 'lavaserver.badhost.com',
                          'port': 80,
                          'rest_uri': 'http://lavaserver.badhost.com',
                          'password': "verytrivialpassword",
                          'identifier': 'MAIN',
                          'region': 'us_central',
                          'heartbeat': 40.0, # ping the server every 40s
                          'resume_session': True,
                          'resume_timeout': 90.0,
                          'resume_key': key(10) # or "Astring"
                          'payload_timeout': 40.0
                          }}
    
        for n in nodes.values():
            await self.bot.wavelink.initiate_node(**n)
    
    • or simply by setting 'resume_session': True
     async def start_nodes(self) -> None:
        """Connect and intiate nodes."""
        nodes = {'MAIN': {'host': 'lavaserver.badhost.com',
                          'port': 80,
                          'rest_uri': 'http://lavaserver.badhost.com',
                          'password': "verytrivialpassword",
                          'identifier': 'MAIN',
                          'region': 'us_central',
                          'resume_session': True
                          }}
    
        for n in nodes.values():
            await self.bot.wavelink.initiate_node(**n)
    

    Though the former has more use cases by using a Key Object we can:

    • Make the key persistent across Clusters
    • log the events, since __str__ is called when configuring the server, while __repr__ is called when the Node's session is closed to generate a new key.
    • To Generate a more secure password
    • Increase length of the password (The default is 32 characters)
    • Use

    How does resuming work?

    When the bot disconnects from the node (eg: 1006) then the lavalink server keeps on playing the music until session timeout, this allows the bot to reconnect and take control of the session. This PR implements this lavalink feature. The changes are not breaking, assuming no-one initializes Node instances directly.

    How does Queueing work?

    After the Node disconnects, we try to reconnect twice before timeout. during this time the requests to the node are queued in an asyncio.Queue subclass whose payload expires after some time. This could be useful when you have an higher timeout but want to only send recent requests

    opened by WizzyGeek 7
  • Add /decodetracks endpoint support

    Add /decodetracks endpoint support

    In addition to the Client/Node.build_track method, this adds the corresponding POST to /decodetracks to batch decode track identifiers. Useful if there are a large number of tracks that need to be decoded.

    The definition of the endpoint can be found here: https://github.com/Frederikam/Lavalink/blob/18ee6778674b7f9a817893b676a91a9b96e642f3/LavalinkServer/src/main/java/lavalink/server/player/AudioLoaderRestHandler.java#L140

    opened by james7132 6
  • AttributeError: '_MissingSentinel' object has no attribute 'guild'

    AttributeError: '_MissingSentinel' object has no attribute 'guild'

    Hi I am trying to make a setvolume command but I keep getting this error: AttributeError: '_MissingSentinel' object has no attribute 'guild'

    Code:

    @bot.command()
    async def setVolume(ctx, *, volume: float):
        vc = ctx.voice_client
        custom_player = CustomPlayer()
    
        if not vc:
            await ctx.send("I ain't in a vc bro")
        else:
            await custom_player.set_volume(volume=volume)
    opened by Necrosis000 5
  • Is there a better way to play a local file?

    Is there a better way to play a local file?

    I've finally got playing a local file (to lavalink) from the bot. (No more ffmpeg)

    I'm Just throwing this out here as a general question:

    is there a better way to search for a Local Track? I see SearchableTrack, having a return_first=true. Just trying to get my head around the API. Any help is apperciated?

    @slash_command(name="lplay", description="Play some local music or SFX") async def lplay(self, inter: Interaction, search): path = "/media/" + search
        search = wavelink.PartialTrack(query=path, cls=wavelink.LocalTrack)
    
        if inter.user.voice is None:
            return await inter.send(f'You are not connected to any voice channel!')
    
        # If the bot isn't in a voice channel
        if not inter.guild.voice_client:
            vc: wavelink.Player = await inter.user.voice.channel.connect(cls=wavelink.Player)
        else:
          #See if the bot is in another channel
          if inter.guild.voice_client.channel != inter.user.voice.channel:
            await inter.guild.voice_client.move_to(inter.user.voice.channel) 
            await inter.send(f'Connected to {inter.user.voice.channel}.')
            
        vc: wavelink.Player = inter.guild.voice_client
       
        try: 
           await vc.play(search)
           await inter.send(f'Playing {search.title}')
        except:
           await inter.send(f'{path} Not found!') 
    
    opened by Mudpuppy12 4
  • queue error please help

    queue error please help

    @bot.event async def on_wavelink_track_end(player: wavelink.Player, track: wavelink.Track, reason): ctx = player.ctx vc: player = ctx.voice_client if vc.loop: return await vc.play(track) next_song = vc.queue.get() await vc.play(next_song) await ctx.send(f"Now playing {next_song.title}")

    • File "/home/container/discord-bot/main.py", line 118, in on_wavelink_track_end next_song = vc.queue.get() File "/home/container/.local/lib/python3.10/site-packages/wavelink/queue.py", line 212, in get raise QueueEmpty("No items in the queue.") wavelink.errors.QueueEmpty: No items in the queue.
    opened by slefgameRz 4
  • wavelink.Player.connect incompatibility with Discord.py 2.0

    wavelink.Player.connect incompatibility with Discord.py 2.0

    Discord.py 2.0 introduced a new self_deaf and self_mute parameter to the connect method. This causes TypeError: Player.connect() got an unexpected keyword argument 'self_deaf'

    opened by Luc1412 4
  • module 'wavelink' has no attribute 'client'

    module 'wavelink' has no attribute 'client'

    Traceback (most recent call last): File "/Users/admin/Desktop/Cogs/music1.py", line 272, in bot = Bot() File "/Users/admin/Desktop/Cogs/music1.py", line 22, in init self.add_cog(Music(self)) File "/Users/admin/Desktop/Cogs/music1.py", line 69, in init self.bot.wavelink = wavelink.Client(bot=self.bot) AttributeError: module 'wavelink' has no attribute 'Client'

    opened by sanjaysanooj22 4
  • v1.2.0 No Longer Supports Python Version 3.8

    v1.2.0 No Longer Supports Python Version 3.8

    In the newest version (v1.2.0), the implementation of the YouTube Playlist searching has removed support for Python Version 3.8. The use of the method .removeprefix() is a python 3.9+ feature.

    The line in question: https://github.com/PythonistaGuild/Wavelink/blob/8a4fb12404a5e908bd7a22ecba1ace7dc950d73f/wavelink/tracks.py#L187

    PEP 616: String methods to remove prefixes and suffixes https://www.python.org/dev/peps/pep-0616/

    If this is intentional to remove Python 3.8 version, then please can the documentation be updated. Otherwise, an implementation to get around this could be:

    def removePrefix(url, prefix):
        if string.startswith(prefix):
            return string[len(prefix):]
        else:
            return string
    
    opened by TwoSails 4
  • [Suggestion] Advanced exmple

    [Suggestion] Advanced exmple

    Hello, Wavelink is working good but in my opinion there a a few missing things:

    • loop song & queue
    • lyrics command
    • pitch (1.00 standard 5.00 max)
    • option to keep the bot into the voicechannel even if nothing is playing.
    opened by FeelsBadMan1 4
  • Add payload_args optional argument to the play method in player.py

    Add payload_args optional argument to the play method in player.py

    This argument allows the user to pass arguments which will be directly sent to Lavalink in the play method. This gives the ability to interact with Lavalink plugins

    opened by Rajdave69 0
  • getting this error on 2.0

    getting this error on 2.0

    File "/home/subrata/Wavelink/wavelink/node.py", line 106, in <genexpr>
        version_tuple = tuple(int(v) for v in version.split('.'))
    ValueError: invalid literal for int() with base 10: 'c597fb10d9d323a174b69efa66fcebd616fb3f1e-SNAPSHOT_Unofficial'
    
    bug 2.0 
    opened by Subrata2402 0
  • Update pool.py

    Update pool.py

    Fixed bug with 'wavelink.NodePool.get_node(region="rotterdam")' raise ZeroConnectedNodes(f "No Nodes for region <{region}> exist on this pool."), although there is at least one node with this region.

    opened by Marco12223 1
  • Player running endless (without sound)

    Player running endless (without sound)

    I currently play an MP3 on the lavalink server in a loop. The loop has been accomplished by following code.

    @commands.Cog.listener(name="on_wavelink_track_end")
        async def _on_track_end(self, player: RadioPlayer, track: wavelink.Track, reason: str):
            if reason != 'FINISHED':
                return
            await player.play(track)
    

    This transition works fine so far. I also added on_wavelink_track_exception and on_wavelink_track_stuck for debug reasons, but both won't get triggered.

    I experience that the player randomly stops playing. The client is still connected. The player got is_connected() and is_playing() set to True. But when checking the position it got an inappropriate high number set.

    My track is about an hour long, but postion is set to 21h in seconds.

    Pausing and resuming resumes the playback.

    opened by Luc1412 0
  • cant search for youtube playlist

    cant search for youtube playlist

    I try to use wavelink.YouTubePlaylist to search for youtubeplaylist and I get error

    code @commands.command(aliases=['P', 'PLAY', 'Play','p']) async def play(self,ctx: commands.Context, *, search:wavelink.YouTubePlaylist): print(search)

    error Ignoring exception in on_message Traceback (most recent call last): File "C:\Python3.10\lib\site-packages\discord\ext\commands\core.py", line 456, in _actual_conversion ret = await method(ctx, argument) File "C:\Python3.10\lib\site-packages\wavelink\tracks.py", line 218, in convert return results[0] TypeError: 'YouTubePlaylist' object is not subscriptable

    Did i miss something?

    and also sorry for my bad English

    opened by tunwit 2
Releases(v1.3.4)
  • v1.3.4(Dec 22, 2022)

  • v1.3.3(Oct 4, 2022)

  • v1.3.2(Jul 10, 2022)

  • v1.3.1(Jun 6, 2022)

  • v1.3.0(Jun 6, 2022)

    Implemented Lavalink Filters, some documentation can be found here: https://wavelink.readthedocs.io/en/latest/wavelink.html#filters This is still a WIP.

    Fixed an issue with YouTubePlaylist converter failing. Bumped aiohttp inline with discord.py

    Source code(tar.gz)
    Source code(zip)
  • v1.2.5(Apr 22, 2022)

  • v1.2.4(Mar 21, 2022)

  • v1.2.3(Mar 21, 2022)

  • v1.2.2(Mar 13, 2022)

  • v1.2.1(Mar 10, 2022)

  • v1.2.0(Mar 7, 2022)

  • v1.1.1(Feb 8, 2022)

  • v1.1.0(Feb 4, 2022)

  • v1.0.2(Jan 24, 2022)

  • v1.0.1(Jan 24, 2022)

  • v1.0.0(Jan 24, 2022)

    Wavelink 1.0.0

    This release is a whole rewrite of the library. It supports discord.py 2.0 and some derivatives.

    See the documentaion for more info and examples. Documentation

    If the documentation is still displaying as the old version, please clear your cache.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.10(Jul 13, 2021)

  • v0.9.9(Mar 8, 2021)

  • v0.9.8(Feb 25, 2021)

    Add force keyword argument to Node.destroy() and Player.destroy()/disconnect(). This prevents a bug from occuring when a player is being destroyed, and the bot is no longer apart of the Guild the player was associated with.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.7(Feb 23, 2021)

  • v0.9.6(Sep 9, 2020)

    Fix a bug in player setting current track to None on TrackStuck and TrackException.

    Since Lavalink sends two events on Exceptions, an End and Exception event this causes a possible race condition in the player, setting the current Track to None when one is actually playing.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.5(Sep 5, 2020)

    Added Exponential Backoff attempts to get_tracks. Retrieving tracks will now by default retry on failure up to a maximum of 5 attempts with an Exponential Backoff. You can set this to False with the kwarg retry_on_failure=False, which will only attempt to retrieve tracks once.

    Added support for custom JSON Encoders.

    Changed YouTube's Track Image to hqdefault, in place of maxresdefault.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.4(Aug 5, 2020)

    Changes to Equalizer which allow for construction without the build() classmethod.

    Added name parameters to the build() classmethod and Equalizer constructor to allow setting a custom name.

    Added a name property to Equalizer. Setting the name should be done by constrcutor or the build() classmethod

    Added __str__ and __repr__ to Equalizer.

    Fixed a bug where set_eq was setting a blank Equalizer class on the player.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.3(Jul 30, 2020)

  • v0.9.2(Jun 20, 2020)

    Add heartbeat to node and websocket.

    This stabilizes connection to Nodes hosted on a VPS with a router or any such intermediate process that closes the connection if it remains idle. Which puts the bot in a "reconnection" loop.

    Source code(tar.gz)
    Source code(zip)
  • v0.9.1(Jun 20, 2020)

  • v0.9.0(Jun 8, 2020)

    Added wavelink.WavelinkMixin to be used in conjunction with a discord.py commands.Cog. This class allows for the registration of the new wavelink listeners:

    on_node_ready on_track_start on_track_end on_track_stuck on_track_exception on_websocket_closed

    All listeners must be decorated with the wavelink.WavelinkMixin.listener() decorator. All listeners must be in a wavelink.WavelinkMixin class. Listeners can be stacked.

    Docs

    Event Payloads WavelinkMixin

    Example

    class Music(commands.Cog, wavelink.WavelinkMixin):
        
        @wavelink.WavelinkMixin.listener()
        async def on_node_ready(self, node):
            print(f'Node {node.identifier} is ready!')
    
    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(May 18, 2020)

  • v0.7.2(May 16, 2020)

  • v0.7.1(May 2, 2020)

Owner
Pythonista
Organisation for the Pythonista Guild
Pythonista
Sukoshi is a proof-of-concept Python implant that leverages the MQTT protocol for C2 and uses AWS IoT Core as infrastructure.

Sukoshi | 少し Overview Sukoshi is a proof-of-concept Python implant that leverages the MQTT protocol for C2 and uses AWS IoT Core as infrastructure. It

Steven Patterson 37 Oct 29, 2022
Userbot Telegram + Music Voice Chats. Dibuat Untuk Bersenang - Senang , Dan Mempermudah Kegiatan. Created By Rio.

RIO - USERBOT Disclaimer Saya tidak bertanggung jawab atas penyalahgunaan bot ini. Bot ini dimaksudkan untuk bersenang-senang sekaligus membantu Anda

RioProjectX 1 Nov 10, 2021
`python-jamf` is a library for connecting to a Jamf Server that maps directly to the Jamf Pro Classic API.

`python-jamf` is a library for connecting to a Jamf Server that maps directly to the Jamf Pro Classic API. It is the basis for the `jctl` tool to automate patch management & packages and many other i

University of Utah, Marriott Library, Apple Support 38 Dec 13, 2022
Automatically mass follows tons of NameMC profiles.

Automatically mass follows tons of NameMC profiles. (Creates REAL traffic to your profile)

Jam 3 Jun 29, 2022
A python script for hitting the kik API to enumerate people based on a username/userlist

kick3d Recon script for enumerating users off of the Kik API. This script has the ability to check single usernames or run through a userlist of usern

Sakura Samurai 19 Oct 04, 2021
Auto file forward bot with python

Auto-File-Forward-Bot Auto file forward bot. Without Admin Permission in FROM_CHANNEL Only Give Permission In your Telegram Personal Channel Please fo

Milas 1 Oct 15, 2021
Discord Mass Report script that uses multiple tokens

Discord-Mass-Report Discord Mass Report script that uses multiple tokens, full credits to https://github.com/hoki0/Discord-mass-report who made it in

cChimney 4 Jun 08, 2022
Discord bot that plays cricket with the user

CricBot Table of content Commands Installation Game rules License Commands S.No Command Use 1. cric Open the home window. This command is not necessa

Raveesh Yadav 1 Nov 19, 2021
An enhanced discord.py, based off of the now-archived discord.py project

enhanced-discord.py A modern, maintained, easy to use, feature-rich, and async ready API wrapper for Discord written in Python. The Future of enhanced

Devision 2 Dec 21, 2022
A Telegram Userbot to play Audio and Video songs / files in Telegram Voice Chats.

VC UserBot A Telegram Userbot to play Audio and Video songs / files in Telegram Voice Chats. It's made with PyTgCalls and Pyrogram Requirements Python

조던 1 Nov 29, 2021
Pycord, a maintained fork of discord.py, is a python wrapper for the Discord API

pycord A fork of discord.py. PyCord is a modern, easy to use, feature-rich, and async ready API wrapper for Discord written in Python. Key Features Mo

Pycord Development 2.3k Dec 31, 2022
Event-driven-model-serving - Unified API of Apache Kafka and Google PubSub

event-driven-model-serving Unified API of Apache Kafka and Google PubSub 1. Proj

Danny Toeun Kim 4 Sep 23, 2022
An API or getting Optifine VersionsList/Version/Download-URL.

Optifine-API An API for getting Optifine VersionsList/Versions/Download-URL. Table of contents Get Versions List Get Specify Versions Download Optifin

2 Dec 04, 2022
The successor of GeoSnipe, a pythonic Minecraft username sniper based on AsyncIO.

OneSnipe The successor of GeoSnipe, a pythonic Minecraft username sniper based on AsyncIO. Documentation View Documentation Features • Mojang & Micros

1 Jan 14, 2022
Satoshi is a discord bot template in python using discord.py that allow you to track some live crypto prices with your own discord bot.

Satoshi ~ DiscordCryptoBot Satoshi is a simple python discord bot using discord.py that allow you to track your favorites cryptos prices with your own

Théo 2 Sep 15, 2022
This automation protect against subdomain takeover on AWS env which also send alerts on slack.

AWS_Subdomain_Takeover_Detector Purpose The purpose of this automation is to detect misconfigured Route53 entries which are vulnerable to subdomain ta

Puneet Kumar Maurya 8 May 18, 2022
wrapper for facebook messenger

pyfacebook pyfacebook library for python. Requirements common Help Got a question? File a GitHub issue. Contributing Bug Reports & Feature Requests Pl

Luis Mayta 3 Nov 12, 2021
Estimate the total emissions for popular CryptoArt platforms.

cryptoart-footprint Estimate the total CO2 footprint for popular CryptoArt platforms. The goal is to accurately quantify the ecological damage of Ethe

Kyle McDonald 182 Oct 12, 2022
Paid Udemy Courses with Coupons

Freedemy Paid Udemy Courses with Coupons Steps to run pip3 install -r requirements.txt python3 free-courses.py Then you can click the Enroll Link and

GOKUL A.P 23 Dec 14, 2022
ResolveURL - Fork of UrlResolver by eldorados, tknorris and jsergio123

ResolveURL Fork of UrlResolver by eldorados, tknorris and jsergio123 I am in no

gujal 60 Jan 03, 2023