# -*- coding: utf-8 -*-
"""
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see .
"""
import re
from urlparse import urljoin
from module.common.json_layer import json_loads
from module.plugins.Crypter import Crypter
from module.utils import save_join
API_URL = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0"
class YoutubeBatch(Crypter):
__name__ = "YoutubeBatch"
__version__ = "1.00"
__type__ = "crypter"
__pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?Puser|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P[\w-]+)'
__config__ = [("likes", "bool", "Grab user (channel) liked videos", False),
("favorites", "bool", "Grab user (channel) favorite videos", False),
("uploads", "bool", "Grab channel unplaylisted videos", True)]
__description__ = """Youtube.com channel & playlist decrypter plugin"""
__author_name__ = "Walter Purcaro"
__author_mail__ = "vuolter@gmail.com"
def api_response(self, ref, req):
req.update({"key": API_KEY})
url = urljoin("https://www.googleapis.com/youtube/v3/", ref)
page = self.load(url, get=req)
return json_loads(page)
def getChannel(self, user):
channels = self.api_response("channels", {"part": "id,snippet,contentDetails", "forUsername": user, "maxResults": "50"})
if channels['items']:
channel = channels['items'][0]
return {"id": channel['id'],
"title": channel['snippet']['title'],
"relatedPlaylists": channel['contentDetails']['relatedPlaylists'],
"user": user} # One lone channel for user?
def getPlaylist(self, p_id):
playlists = self.api_response("playlists", {"part": "snippet", "id": p_id})
if playlists['items']:
playlist = playlists['items'][0]
return {"id": p_id,
"title": playlist['snippet']['title'],
"channelId": playlist['snippet']['channelId'],
"channelTitle": playlist['snippet']['channelTitle']}
def _getPlaylists(self, id, token=None):
req = {"part": "id", "maxResults": "50", "channelId": id}
if token:
req.update({"pageToken": token})
playlists = self.api_response("playlists", req)
for playlist in playlists['items']:
yield playlist['id']
if "nextPageToken" in playlists:
for item in self._getPlaylists(id, playlists['nextPageToken']):
yield item
def getPlaylists(self, ch_id):
return map(self.getPlaylist, self._getPlaylists(ch_id))
def _getVideosId(self, id, token=None):
req = {"part": "contentDetails", "maxResults": "50", "playlistId": id}
if token:
req.update({"pageToken": token})
playlist = self.api_response("playlistItems", req)
for item in playlist['items']:
yield item['contentDetails']['videoId']
if "nextPageToken" in playlist:
for item in self._getVideosId(id, playlist['nextPageToken']):
yield item
def getVideosId(self, p_id):
return list(self._getVideosId(p_id))
def decrypt(self, pyfile):
match = re.match(self.__pattern__, pyfile.url)
m_id = match.group("ID")
m_type = match.group("TYPE")
if m_type == "user":
self.logDebug("Url recognized as Channel")
user = m_id
channel = self.getChannel(user)
if channel:
playlists = self.getPlaylists(channel['id'])
self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel['title']))
relatedplaylist = {p_name: self.getPlaylist(p_id) for p_name, p_id in channel['relatedPlaylists'].iteritems()}
self.logDebug("Channel's related playlists found = %s" % relatedplaylist.keys())
relatedplaylist['uploads']['title'] = "Unplaylisted videos"
relatedplaylist['uploads']['checkDups'] = True #: checkDups flag
for p_name, p_data in relatedplaylist.iteritems():
if self.getConfig(p_name):
p_data['title'] += " of " + user
playlists.append(p_data)
else:
playlists = []
else:
self.logDebug("Url recognized as Playlist")
playlists = [self.getPlaylist(m_id)]
if not playlists:
self.fail("No playlist available")
addedvideos = []
urlize = lambda x: "https://www.youtube.com/watch?v=" + x
for p in playlists:
p_name = p['title']
p_videos = self.getVideosId(p['id'])
p_folder = save_join(self.config['general']['download_folder'], p['channelTitle'], p_name)
self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
if not p_videos:
continue
elif "checkDups" in p:
p_urls = [urlize(v_id) for v_id in p_videos if v_id not in addedvideos]
self.logDebug("%s video\s available on playlist \"%s\" after duplicates cleanup" % (len(p_urls), p_name))
else:
p_urls = map(urlize, p_videos)
self.packages.append((p_name, p_urls, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
addedvideos.extend(p_videos)