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