-
Notifications
You must be signed in to change notification settings - Fork 0
/
ytsearch.py
158 lines (115 loc) · 5.36 KB
/
ytsearch.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#ProtOS Discord Bot
#
#Author: fredi_68
#
#Music Search Engines
#Currently available search engines:
# YouTube
# SoundCloud
import logging
import json
from urllib import request
logger = logging.getLogger("ytsearch")
HAS_SOUNDCLOUD = True
try:
import soundcloud
except ImportError:
logger.warn("Unable to import soundcloud module, soundcloud search engine will not be available.")
HAS_SOUNDCLOUD = False
import version
class Search():
"""
Abstract base class for search engines.
This class specifies an interface for music search egines.
It also provides some shared functionality and object instances.
"""
logger = logging.getLogger("GenericSearchEngine")
def __init__(self):
"""
Initialize the search engine.
You may change this constructor to fit your implementations needs,
it is not part of the interface definition.
"""
self.serviceName = "Generic"
def search(self, query, maxresults=10, lang="en", **opt):
"""
Query the search engine and return the results.
This method takes the query string and returns a dictionary of url:title mappings.
The length of the returned dictionary does not have to be maxresult, but may not be
larger. It may be empty.
lang is a ISO 639-1 two-letter language code and should be used to narrow the search results by
language, if applicable.
A search engine may add support for additional options, these should be included as additional
keyword only arguments. All keyword arguments are considered optional.
"""
return {}
class YouTubeSearch(Search):
def __init__(self, token):
"""
Create a new YouTubeSearch instance.
token should be a valid YouTube Data API key.
"""
Search.__init__(self)
self.token = token
self.serviceName = "YouTube"
def search(self, query, maxresults = 10, lang = 'en', **opt):
"""
Searches YouTube for videos using the given query.
Returns a dict of url: title pairs pointing to videos.
If maxresults is given, it should be greater 0 and specifies the maximum amount of
video urls returned. The resulting list may be shorter, or empty.
If lang is given, it should be an ISO 639-1 two-letter language code, or any other
language identifier recognized by the YouTube data API.
For more information, please refer to https://developers.google.com/youtube/v3/docs/search/list
Most of the parameters of this method are directly passed into the url
"""
self.logger.info("Running YouTube search for query %s...", query)
#Added some additional parameters here to ensure only videos come through (and English is preferred as the language)
url = "https://www.googleapis.com/youtube/v3/search?q=%s&maxResults=%i&part=snippet&key=%s&relevanceLanguage=%s&type=video" % (query.replace(" ", "+"), maxresults, self.token, lang)
self.logger.debug("URL is %s" % url)
req = request.Request(url)
req.add_header("User-Agent", version.S_TITLE_VERSION)
req.add_header("Accept-Language", "en-US,en")
res = request.urlopen(req)
self.logger.debug("Server returned code %i: %s" % (res.code, res.msg))
self.logger.debug("Examining response...")
data = json.loads(res.read())
urls = {}
for i in data["items"]:
try:
urls["https://www.youtube.com/watch?v=%s" % i["id"]["videoId"]] = i["snippet"]["title"]
except KeyError: #If for some reason an entry in our response wasn't a video, this will make sure that we don't crash
pass
return urls
class SoundCloudSearch(Search):
def __init__(self, token):
"""
Create a new SoundCloudSearch instance.
token should be a valid SoundCloud Client ID.
"""
Search.__init__(self)
self.token = token
if HAS_SOUNDCLOUD:
self.client = soundcloud.Client(client_id=token)
else:
self.client = None
self.serviceName = "SoundCloud"
def search(self, query, maxresults=10, lang='en', **opt):
"""
Searches SoundCloud for tracks using the given query.
Returns a dict of url: title pairs pointing to tracks.
If maxresults is given, it should be greater 0 and specifies the maximum amount of
urls returned. The resulting list may be shorter, or empty.
The lang option is not implemented by this search engine and will be ignored.
A user may specify additional search terms by adding keyword arguments.
These will be inserted into the query after the general search options.
For more information, please refer to https://developers.soundcloud.com/docs/api/reference#tracks
Most of the parameters of this method are directly passed into the url
"""
if not HAS_SOUNDCLOUD:
return {"Library unavailable": "This search engine cannot function properly, because the soundcloud package isn't installed on this machine. Please contact the bot owner if you believe this is an error."}
tracks = self.client.get("/tracks", q=query, limit=maxresults, **opt)
urls = {}
for track in tracks:
urls[track.permalink_url] = track.title
return urls