def on_start(self): proxy = httpclient.format_proxy(self.config["proxy"]) youtube.Video.proxy = proxy headers = { "user-agent": httpclient.format_user_agent(self.user_agent), "Cookie": "PREF=hl=en;", "Accept-Language": "en;q=0.8", } if youtube.api_enabled is True: if youtube_api.youtube_api_key is None: logger.error("No YouTube API key provided, disabling API") youtube.api_enabled = False else: youtube.Entry.api = youtube_api.API(proxy, headers) if youtube.Entry.search(q="test") is None: logger.error( "Failed to verify YouTube API key, disabling API" ) youtube.api_enabled = False else: logger.info("YouTube API key verified") if youtube.api_enabled is False: # regex based api # logger.info("Using scrAPI") # youtube.Entry.api = youtube_scrapi.scrAPI(proxy, headers) # # beautiful soup 4 based api logger.info("using bs4API") youtube.Entry.api = youtube_bs4api.bs4API(proxy, headers)
def search(cls, q): """ Search for both videos and playlists using a single API call. Fetches only title, thumbnails, channel (extra queries are needed for length and video_count) """ def create_object(item): set_api_data = ["title", "channel"] if item["id"]["kind"] == "youtube#video": obj = Video.get(item["id"]["videoId"]) if "contentDetails" in item: set_api_data.append("length") elif item["id"]["kind"] == "youtube#playlist": obj = Playlist.get(item["id"]["playlistId"]) if "contentDetails" in item: set_api_data.append("video_count") else: obj = [] return obj if "thumbnails" in item["snippet"]: set_api_data.append("thumbnails") obj._set_api_data(set_api_data, item) return obj try: data = cls.api.search(q) except Exception as e: logger.error('search error "%s"', e) return None try: return list(map(create_object, data["items"])) except Exception as e: logger.error('map error "%s"', e) return None
def search(cls, q): def create_object(item): set_api_data = ['title', 'channel'] if item['id']['kind'] == 'youtube#video': obj = Video.get(item['id']['videoId']) if 'contentDetails' in item: set_api_data.append('length') elif item['id']['kind'] == 'youtube#playlist': obj = Playlist.get(item['id']['playlistId']) if 'contentDetails' in item: set_api_data.append('video_count') # elif item['id']['kind'] == 'youtube#radiolist': # obj = Video.get(item['id']['videoId']) # set_api_data = ['title', 'video_count'] else: obj = [] return obj if 'thumbnails' in item: set_api_data.append('thumbnails') obj._set_api_data(set_api_data, item) return obj try: data = cls.api.search(q) except Exception as e: logger.error('search error "%s"', e) return None try: return map(create_object, data['items']) except Exception as e: logger.error('map error "%s"', e) return None
def on_start(self): proxy = httpclient.format_proxy(self.config['proxy']) youtube.Video.proxy = proxy headers = { 'user-agent': httpclient.format_user_agent(self.user_agent), 'Cookie': 'PREF=hl=en;', 'Accept-Language': 'en;q=0.8' } if youtube.api_enabled is True: if youtube.API.youtube_api_key is None: logger.error('No YouTube API key provided, disabling API') youtube.api_enabled = False else: youtube.Entry.api = youtube.API(proxy, headers) if youtube.Entry.search(q='test') is None: logger.error( 'Failed to verify YouTube API key, disabling API') youtube.api_enabled = False else: logger.info('YouTube API key verified') if youtube.api_enabled is False: logger.info('Using scrAPI') youtube.Entry.api = youtube.scrAPI(proxy, headers)
def job(sublist): try: data = cls.api.list_playlists([x.id for x in sublist]) dict = {item["id"]: item for item in data["items"]} except Exception as e: logger.error('list_playlists error "%s"', e) dict = {} for pl in sublist: pl._set_api_data(fields, dict.get(pl.id))
def job(sublist): try: data = cls.api.list_videos([x.id for x in sublist]) dict = {item["id"]: item for item in data["items"]} except Exception as e: logger.error('list_videos error "%s"', e) dict = {} for video in sublist: video._set_api_data(fields, dict.get(video.id))
def translate_uri(self, uri): logger.info('youtube PlaybackProvider.translate_uri "%s"', uri) if "youtube:video/" not in uri: return None try: id = extract_id(uri) return youtube.Video.get(id).audio_url.get() except Exception as e: logger.error('translate_uri error "%s"', e) return None
def search(self, query=None, uris=None, exact=False): # TODO Support exact search logger.info('youtube LibraryProvider.search "%s"', query) # handle only searching (queries with 'any') not browsing! if not (query and "any" in query): return None search_query = " ".join(query["any"]) logger.info('Searching YouTube for query "%s"', search_query) try: entries = youtube.Entry.search(search_query) except Exception as e: logger.error('search error "%s"', e) return None # load playlist info (to get video_count) of all playlists together playlists = [entry for entry in entries if not entry.is_video] youtube.Playlist.load_info(playlists) tracks = [] for entry in entries: if entry.is_video: uri_base = "youtube:video" album = "YouTube Video" length = int(entry.length.get()) * 1000 else: uri_base = "youtube:playlist" album = "YouTube Playlist (%s videos)" % entry.video_count.get() length = 0 name = entry.title.get() tracks.append( Track( name=name.replace(";", ""), comment=entry.id, length=length, artists=[Artist(name=entry.channel.get())], album=Album(name=album), uri="%s/%s.%s" % (uri_base, safe_url(name), entry.id), ) ) # load video info and playlist videos in the background. they should be # ready by the time the user adds search results to the playing queue for pl in playlists: pl.videos # start loading return SearchResult(uri="youtube:search", tracks=tracks)
def worker(cls): while True: cls.lock.acquire() if len(cls.jobs): f, args = cls.jobs.pop() else: # no more jobs, exit thread cls.threads_active -= 1 cls.lock.release() break cls.lock.release() try: f(*args) except Exception as e: logger.error("youtube thread error: %s\n%s", e, traceback.format_exc())
def job(): try: info = youtube_dl.YoutubeDL({ 'format': 'mp4/bestaudio/vorbis/m4a/best', 'proxy': self.proxy, 'nocheckcertificate': True }).extract_info(url="https://www.youtube.com/watch?v=%s" % self.id, download=False, ie_key=None, extra_info={}, process=True, force_generic_extractor=False) except Exception as e: logger.error('audio_url error "%s"', e) self._audio_url.set(None) return self._audio_url.set(info['url'])
def job(): data = {"items": []} page = "" while (page is not None and len(data["items"]) < self.playlist_max_videos): try: max_results = min( int(self.playlist_max_videos) - len(data["items"]), 50) result = self.api.list_playlistitems( self.id, page, max_results) except Exception as e: logger.error('list playlist items error "%s"', e) break if "error" in result: logger.error( "error in list playlist items data for", "playlist {}, page {}".format(self.id, page), ) break page = result.get("nextPageToken") or None data["items"].extend(result["items"]) del data["items"][int(self.playlist_max_videos):] myvideos = [] for item in data["items"]: set_api_data = ["title", "channel"] if "contentDetails" in item: set_api_data.append("length") if "thumbnails" in item["snippet"]: set_api_data.append("thumbnails") video = Video.get(item["snippet"]["resourceId"]["videoId"]) video._set_api_data(set_api_data, item) myvideos.append(video) # start loading video info in the background Video.load_info([ x for _, x in zip(range(self.playlist_max_videos), myvideos) ]) # noqa: E501 self._videos.set([ x for _, x in zip(range(self.playlist_max_videos), myvideos) ]) # noqa: E501
def translate_uri(self, uri): """ Called when a track us ready to play, we need to return the actual url of the audio. uri must be of the form youtube:video/<title>.<id> (only videos can be played, playlists are expended into tracks by YouTubeLibraryProvider.lookup) """ logger.info('youtube PlaybackProvider.translate_uri "%s"', uri) if "youtube:video/" not in uri: return None try: id = extract_id(uri) return youtube.Video.get(id).audio_url.get() except Exception as e: logger.error('translate_uri error "%s"', e) return None
def job(): try: info = youtube_dl.YoutubeDL({ "format": "bestaudio/best", "proxy": self.proxy, "nocheckcertificate": True, }).extract_info( url="https://www.youtube.com/watch?v=%s" % self.id, download=False, ie_key=None, extra_info={}, process=True, force_generic_extractor=False, ) except Exception as e: logger.error('audio_url error "%s"', e) self._audio_url.set(None) return self._audio_url.set(info["url"])
def job(): all_videos = [] page = '' while page is not None \ and len(all_videos) < self.playlist_max_videos: try: max_results = min( self.playlist_max_videos - len(all_videos), 50) data = self.api.list_playlistitems(self.id, page, max_results) except Exception as e: logger.error('list playlist items error "%s"', e) break if 'error' in data: logger.error('error in list playlist items data') break page = data.get('nextPageToken') or None myvideos = [] for item in data['items']: set_api_data = ['title', 'channel'] if 'contentDetails' in item: set_api_data.append('length') if 'thumbnails' in item['snippet']: set_api_data.append('thumbnails') video = Video.get(item['snippet']['resourceId']['videoId']) video._set_api_data(set_api_data, item) myvideos.append(video) all_videos += myvideos # start loading video info for this batch in the background Video.load_info([ x for _, x in zip(range(self.playlist_max_videos), myvideos) ]) # noqa: E501 self._videos.set([ x for _, x in zip(range(self.playlist_max_videos), all_videos) ]) # noqa: E501
def lookup(self, uri): logger.info('youtube LibraryProvider.lookup "%s"', uri) video_id = playlist_id = None if "youtube.com" in uri: url = urlparse(uri.replace("yt:", "").replace("youtube:", "")) req = parse_qs(url.query) if "list" in req: playlist_id = req.get("list")[0] else: video_id = req.get("v")[0] elif "video/" in uri: video_id = extract_id(uri) else: playlist_id = extract_id(uri) if video_id: video = youtube.Video.get(video_id) video.audio_url # start loading return [ Track( name=video.title.get().replace(";", ""), comment=video.id, length=video.length.get() * 1000, artists=[Artist(name=video.channel.get())], album=Album(name="YouTube Video",), uri="youtube:video/%s.%s" % (safe_url(video.title.get()), video.id), ) ] else: playlist = youtube.Playlist.get(playlist_id) if not playlist.videos.get(): logger.error('Cannot load "%s"', uri) return [] # ignore videos for which no info was found (removed, etc) videos = [ v for v in playlist.videos.get() if v.length.get() is not None ] # load audio_url in the background to be ready for playback for video in videos: video.audio_url # start loading return [ Track( name=video.title.get().replace(";", ""), comment=video.id, length=video.length.get() * 1000, track_no=count, artists=[Artist(name=video.channel.get())], album=Album(name=playlist.title.get(),), uri="youtube:video/%s.%s" % (safe_url(video.title.get()), video.id), ) for count, video in enumerate(videos, 1) ]
def lookup(self, uri): """ Called when the user adds a track to the playing queue, either from the search results, or directly by adding a yt:http://youtube.com/.... uri. uri can be of the form [yt|youtube]:<url to youtube video> [yt|youtube]:<url to youtube playlist> youtube:video/<title>.<id> youtube:playlist/<title>.<id> If uri is a video then a single track is returned. If it's a playlist the list of all videos in the playlist is returned. We also start loading the audio_url of all videos in the background, to be ready for playback (see YouTubePlaybackProvider.translate_uri). """ logger.info('youtube LibraryProvider.lookup "%s"', uri) video_id = playlist_id = None if "youtube.com" in uri: url = urlparse(uri.replace("yt:", "").replace("youtube:", "")) req = parse_qs(url.query) if "list" in req: playlist_id = req.get("list")[0] else: video_id = req.get("v")[0] elif "video/" in uri: video_id = extract_id(uri) else: playlist_id = extract_id(uri) if video_id: video = youtube.Video.get(video_id) video.audio_url # start loading return [ Track( name=video.title.get().replace(";", ""), comment=video.id, length=video.length.get() * 1000, artists=[Artist(name=video.channel.get())], album=Album(name="YouTube Video", ), uri="youtube:video/%s.%s" % (safe_url(video.title.get()), video.id), ) ] else: playlist = youtube.Playlist.get(playlist_id) if not playlist.videos.get(): logger.error('Cannot load "%s"', uri) return [] # ignore videos for which no info was found (removed, etc) videos = [ v for v in playlist.videos.get() if v.length.get() is not None ] # load audio_url in the background to be ready for playback for video in videos: video.audio_url # start loading return [ Track( name=video.title.get().replace(";", ""), comment=video.id, length=video.length.get() * 1000, track_no=count, artists=[Artist(name=video.channel.get())], album=Album(name=playlist.title.get(), ), uri="youtube:video/%s.%s" % (safe_url(video.title.get()), video.id), ) for count, video in enumerate(videos, 1) ]