1# -*- coding: utf-8 -*-
2#
3# gPodder - A media aggregator and podcast client
4# Copyright (c) 2005-2018 The gPodder Team
5#
6# gPodder is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3 of the License, or
9# (at your option) any later version.
10#
11# gPodder is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19
20
21#
22# gpodder.directory - Podcast directory and search providers
23# Thomas Perl <thp@gpodder.org>; 2014-10-22
24#
25
26import json
27import os
28import urllib.error
29import urllib.parse
30import urllib.request
31
32import gpodder
33from gpodder import opml, util
34
35_ = gpodder.gettext
36
37
38class DirectoryEntry(object):
39    def __init__(self, title, url, image=None, subscribers=-1, description=None):
40        self.title = title
41        self.url = url
42        self.image = image
43        self.subscribers = subscribers
44        self.description = description
45
46
47class DirectoryTag(object):
48    def __init__(self, tag, weight):
49        self.tag = tag
50        self.weight = weight
51
52
53class Provider(object):
54    PROVIDER_SEARCH, PROVIDER_URL, PROVIDER_FILE, PROVIDER_TAGCLOUD, PROVIDER_STATIC = list(range(5))
55
56    def __init__(self):
57        self.name = ''
58        self.kind = self.PROVIDER_SEARCH
59        self.icon = None
60
61    def on_search(self, query):
62        # Should return a list of DirectoryEntry objects
63        raise NotImplemented()
64
65    def on_url(self, url):
66        # Should return a list of DirectoryEntry objects
67        raise NotImplemented()
68
69    def on_file(self, filename):
70        # Should return a list of DirectoryEntry objects
71        raise NotImplemented()
72
73    def on_tag(self, tag):
74        # Should return a list of DirectoryEntry objects
75        raise NotImplemented()
76
77    def on_static(self):
78        # Should return a list of DirectoryEntry objects
79        raise NotImplemented()
80
81    def get_tags(self):
82        # Should return a list of DirectoryTag objects
83        raise NotImplemented()
84
85
86def directory_entry_from_opml(url):
87    return [DirectoryEntry(d['title'], d['url'], description=d['description']) for d in opml.Importer(url).items]
88
89
90def directory_entry_from_mygpo_json(url):
91    return [DirectoryEntry(d['title'], d['url'], d['logo_url'], d['subscribers'], d['description'])
92            for d in json.load(util.urlopen(url))]
93
94
95class GPodderNetSearchProvider(Provider):
96    def __init__(self):
97        self.name = _('gpodder.net search')
98        self.kind = Provider.PROVIDER_SEARCH
99        self.icon = 'directory-gpodder.png'
100
101    def on_search(self, query):
102        return directory_entry_from_mygpo_json('http://gpodder.net/search.json?q=' + urllib.parse.quote(query))
103
104
105class OpmlWebImportProvider(Provider):
106    def __init__(self):
107        self.name = _('OPML from web')
108        self.kind = Provider.PROVIDER_URL
109        self.icon = 'directory-opml.png'
110
111    def on_url(self, url):
112        return directory_entry_from_opml(url)
113
114
115class OpmlFileImportProvider(Provider):
116    def __init__(self):
117        self.name = _('OPML file')
118        self.kind = Provider.PROVIDER_FILE
119        self.icon = 'directory-opml.png'
120
121    def on_file(self, filename):
122        return directory_entry_from_opml(filename)
123
124
125class GPodderRecommendationsProvider(Provider):
126    def __init__(self):
127        self.name = _('Getting started')
128        self.kind = Provider.PROVIDER_STATIC
129        self.icon = 'directory-examples.png'
130
131    def on_static(self):
132        return directory_entry_from_opml('http://gpodder.org/directory.opml')
133
134
135class GPodderNetToplistProvider(Provider):
136    def __init__(self):
137        self.name = _('gpodder.net Top 50')
138        self.kind = Provider.PROVIDER_STATIC
139        self.icon = 'directory-toplist.png'
140
141    def on_static(self):
142        return directory_entry_from_mygpo_json('http://gpodder.net/toplist/50.json')
143
144
145class GPodderNetTagsProvider(Provider):
146    def __init__(self):
147        self.name = _('gpodder.net Tags')
148        self.kind = Provider.PROVIDER_TAGCLOUD
149        self.icon = 'directory-tags.png'
150
151    def on_tag(self, tag):
152        return directory_entry_from_mygpo_json('http://gpodder.net/api/2/tag/%s/50.json' % urllib.parse.quote(tag))
153
154    def get_tags(self):
155        return [DirectoryTag(d['tag'], d['usage']) for d in json.load(util.urlopen('http://gpodder.net/api/2/tags/40.json'))]
156
157
158class SoundcloudSearchProvider(Provider):
159    def __init__(self):
160        self.name = _('Soundcloud search')
161        self.kind = Provider.PROVIDER_SEARCH
162        self.icon = 'directory-soundcloud.png'
163
164    def on_search(self, query):
165        # XXX: This cross-import of the plugin here is bad, but it
166        # works for now (no proper plugin architecture...)
167        from gpodder.plugins.soundcloud import search_for_user
168
169        return [DirectoryEntry(entry['username'], entry['permalink_url']) for entry in search_for_user(query)]
170
171
172class FixedOpmlFileProvider(Provider):
173    def __init__(self, filename):
174        self.name = _('Imported OPML file')
175        self.kind = Provider.PROVIDER_STATIC
176        self.icon = 'directory-opml.png'
177
178        self.filename = filename
179
180    def on_static(self):
181        return directory_entry_from_opml(self.filename)
182
183
184PROVIDERS = [
185    GPodderRecommendationsProvider,
186    None,
187    GPodderNetSearchProvider,
188    GPodderNetToplistProvider,
189    # GPodderNetTagsProvider,
190    None,
191    OpmlWebImportProvider,
192    # OpmlFileImportProvider,
193    None,
194    SoundcloudSearchProvider,
195]
196