# -*- coding: utf-8 -*-
import re
import urlparse
from module.common.json_layer import json_loads
from module.plugins.internal.Crypter import Crypter
from module.utils import save_join as fs_join
class YoutubeComFolder(Crypter):
__name__ = "YoutubeComFolder"
__type__ = "crypter"
__version__ = "1.03"
__status__ = "testing"
__pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)'
__config__ = [("use_subfolder" , "bool", "Save package to subfolder" , True ),
("subfolder_per_pack", "bool", "Create a subfolder for each package", True ),
("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"""
__license__ = "GPLv3"
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0"
def api_response(self, ref, req):
req.update({'key': self.API_KEY})
url = urlparse.urljoin("https://www.googleapis.com/youtube/v3/", ref)
html = self.load(url, get=req)
return json_loads(html)
def get_channel(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 get_playlist(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 _get_playlists(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._get_playlists(id, playlists['nextPageToken']):
yield item
def get_playlists(self, ch_id):
return map(self.get_playlist, self._get_playlists(ch_id))
def _get_videos_id(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._get_videos_id(id, playlist['nextPageToken']):
yield item
def get_videos_id(self, p_id):
return list(self._get_videos_id(p_id))
def decrypt(self, pyfile):
m = re.match(self.__pattern__, pyfile.url)
m_id = m.group('ID')
m_type = m.group('TYPE')
if m_type == "user":
self.log_debug("Url recognized as Channel")
user = m_id
channel = self.get_channel(user)
if channel:
playlists = self.get_playlists(channel['id'])
self.log_debug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel['title']))
relatedplaylist = {p_name: self.get_playlist(p_id) for p_name, p_id in channel['relatedPlaylists'].items()}
self.log_debug("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.items():
if self.get_config(p_name):
p_data['title'] += " of " + user
playlists.append(p_data)
else:
playlists = []
else:
self.log_debug("Url recognized as Playlist")
playlists = [self.get_playlist(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.get_videos_id(p['id'])
p_folder = fs_join(self.pyload.config.get("general", "download_folder"), p['channelTitle'], p_name)
self.log_debug("%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.log_debug("%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)