1import re
2from .pafy import call_gdata
3from .playlist import Playlist
4from .backend_shared import pyver
5
6
7def get_channel(channel_url, basic=False, gdata=False,
8                size=False, callback=None):
9    """Return a Channel object
10
11    The returned Pafy and Playlist objects are initialised using the arguments
12    to get_channel() in the manner documented for pafy.new()
13
14    """
15
16    return Channel.from_url(channel_url, basic, gdata, size, callback)
17
18
19class Channel(object):
20    def __init__(self, channel_url, basic, gdata, size, callback):
21
22        self._channel_url = channel_url
23        self._channel_id = None
24        self._title = None
25        self._description = None
26        self._logo = None
27        self._subscriberCount = None
28        self._uploads = None
29        self._basic = basic
30        self._gdata = gdata
31        self._size = size
32        self._callback = callback
33        self._playlists = None
34        self._subscriptions = None
35        self._have_basic = False
36
37    @classmethod
38    def from_dict(cls, ch, basic, gdata, size, callback):
39        t = cls(ch['id'], basic, gdata, size, callback)
40        t._channel_id = ch['id']
41        t._title = ch['title']
42        t._description = ch['description']
43        t._logo = ch['logo']
44        t._subscriberCount = ch['subscriberCount']
45        t._uploads = ch['uploads']
46        t._have_basic = True
47
48        return t
49
50    @classmethod
51    def from_url(cls, url, basic, gdata, size, callback):
52        t = cls(url, basic, gdata, size, callback)
53        t._fetch_basic()
54        return t
55
56    @property
57    def channel_id(self):
58        if not self._have_basic:
59            self._fetch_basic()
60        return self._channel_id
61
62    @property
63    def title(self):
64        if not self._have_basic:
65            self._fetch_basic()
66        return self._title
67
68    @property
69    def description(self):
70        if not self._have_basic:
71            self._fetch_basic()
72        return self._description
73
74    @property
75    def logo(self):
76        if not self._have_basic:
77            self._fetch_basic()
78        return self._logo
79
80    @property
81    def subscriberCount(self):
82        if not self._have_basic:
83            self._fetch_basic()
84        return self._subscriberCount
85
86    @property
87    def uploads(self):
88        if not self._uploads:
89            self._fetch_basic()
90        if type(self._uploads) != Playlist:
91            self._uploads = Playlist.from_url(self._uploads, self._basic,
92                                              self._gdata, self._size,
93                                              self._callback)
94
95        return self._uploads
96
97    @property
98    def playlists(self):
99        if self._playlists is not None:
100            for playlist in self._playlists:
101                yield playlist
102            return
103
104        playlists = []
105
106        query = {'part': 'snippet,contentDetails',
107                 'maxResults': 50,
108                 'channelId': self.channel_id}
109
110        while True:
111            playlistList = call_gdata('playlists', query)
112
113            for pl in playlistList['items']:
114                try:
115                    thumbnail = pl['snippet']['thumbnails']['standard']['url']
116                except KeyError:
117                    thumbnail = None
118                pl_data = dict(
119                    id=pl['id'],
120                    title=pl['snippet']['title'],
121                    author=pl['snippet']['channelTitle'],
122                    description=pl['snippet']['description'],
123                    thumbnail=thumbnail,
124                    len=pl['contentDetails']['itemCount']
125                )
126
127                pl_obj = Playlist.from_dict(pl_data, self._basic, self._gdata,
128                                            self._size, self._callback)
129                playlists.append(pl_obj)
130                if self._callback:
131                    self._callback("Added playlist: %s" % pl_data['title'])
132                yield pl_obj
133
134            if not playlistList.get('nextPageToken'):
135                break
136            query['pageToken'] = playlistList['nextPageToken']
137
138        self._playlists = playlists
139
140    @property
141    def subscriptions(self):
142        if self._subscriptions is not None:
143            for sub in self._subscriptions:
144                yield sub
145            return
146
147        subscriptions = []
148        query = {'part': 'snippet',
149                 'maxResults': 50,
150                 'channelId': self.channel_id}
151
152        while True:
153            subs_data = call_gdata('subscriptions', query)
154            sub_ids = []
155
156            for sub in subs_data['items']:
157                sub_ids.append(sub['snippet']['resourceId']['channelId'])
158
159            query2 = {'part': 'snippet, contentDetails, statistics',
160                      'id': ','.join(sub_ids),
161                      'maxResults': 50}
162
163            data = call_gdata('channels', query2)
164
165            for ch in data['items']:
166                channel_data = dict(
167                    id=ch['id'],
168                    title=ch['snippet']['title'],
169                    description=ch['snippet']['description'],
170                    logo=ch['snippet']['thumbnails']['default']['url'],
171                    subscriberCount=ch['statistics']['subscriberCount'],
172                    uploads=ch['contentDetails']['relatedPlaylists']['uploads']
173                )
174                sub_obj = Channel.from_dict(channel_data, self._basic,
175                                            self._gdata, self._size,
176                                            self._callback)
177                subscriptions.append(sub_obj)
178                yield sub_obj
179
180            if not subs_data.get('nextPageToken'):
181                break
182            query['pageToken'] = subs_data['nextPageToken']
183
184        self._subscriptions = subscriptions
185
186    def __repr__(self):
187        if not self._have_basic:
188            self._fetch_basic()
189
190        info = [("Type", "Channel"),
191                ("Title", self.title),
192                ("Description", self.description),
193                ("SubscriberCount", self.subscriberCount)]
194
195        nfo = "\n".join(["%s: %s" % i for i in info])
196
197        return nfo.encode("utf8", "replace") if pyver == 2 else nfo
198
199    def _fetch_basic(self):
200        query = None
201        chanR = re.compile('.+channel\/([^\/]+)$')
202        userR = re.compile('.+user\/([^\/]+)$')
203        channel_id = None
204        channel_url = self._channel_url
205        if chanR.match(channel_url):
206            channel_id = chanR.search(channel_url).group(1)
207        elif userR.match(channel_url):
208            username = userR.search(channel_url).group(1)
209            query = {'part': 'snippet, contentDetails, statistics',
210                     'forUsername': username}
211        elif len(channel_url) == 24 and channel_url[:2] == 'UC':
212            channel_id = channel_url
213        else:
214            username = channel_url
215            query = {'part': 'snippet, contentDetails, statistics',
216                     'forUsername': username}
217
218        if query is None:
219            query = {'part': 'snippet, contentDetails, statistics',
220                     'id': channel_id}
221        allinfo = call_gdata('channels', query)
222
223        try:
224            ch = allinfo['items'][0]
225        except IndexError:
226            err = "Unrecognized channel id, url or name : %s"
227            raise ValueError(err % channel_url)
228
229        self._channel_id = ch['id']
230        self._title = ch['snippet']['title']
231        self._description = ch['snippet']['description']
232        self._logo = ch['snippet']['thumbnails']['default']['url']
233        self._subscriberCount = ch['statistics']['subscriberCount']
234        self._uploads = ch['contentDetails']['relatedPlaylists']['uploads']
235        self._have_basic = True
236