2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from mygpo
.api
.basic_auth
import require_valid_user
, check_username
19 from django
.http
import HttpResponse
, HttpResponseBadRequest
20 from mygpo
.api
.models
import Device
, Podcast
, SuggestionEntry
21 from mygpo
.api
.opml
import Exporter
, Importer
22 from mygpo
.api
.httpresponse
import JsonResponse
23 from mygpo
.api
.sanitizing
import sanitize_url
24 from mygpo
.api
.backend
import get_toplist
, get_all_subscriptions
25 from django
.views
.decorators
.csrf
import csrf_exempt
26 from django
.shortcuts
import get_object_or_404
27 from mygpo
.search
.models
import SearchEntry
28 from django
.utils
.translation
import ugettext
as _
29 from mygpo
.decorators
import allowed_methods
35 # Python 2.5 seems to have a different json module
36 if not 'dumps' in dir(json
):
40 import simplejson
as json
43 ALLOWED_FORMATS
= ('txt', 'opml', 'json')
46 def tmp(request
, format
, *args
, **kwargs
):
47 if not format
in ALLOWED_FORMATS
:
48 return HttpResponseBadRequest('Invalid format')
50 return fn(request
, *args
, format
=format
, **kwargs
)
58 @allowed_methods(['GET', 'PUT', 'POST'])
59 def subscriptions(request
, username
, device_uid
, format
):
61 if request
.method
== 'GET':
62 title
= _('%(username)s\'s Subscription List') % {'username': username
}
63 subscriptions
= get_subscriptions(request
.user
, device_uid
)
64 return format_podcast_list(subscriptions
, format
, title
)
66 elif request
.method
in ('PUT', 'POST'):
67 subscriptions
= parse_subscription(request
.raw_post_data
, format
)
68 return set_subscriptions(subscriptions
, request
.user
, device_uid
)
75 @allowed_methods(['GET'])
76 def all_subscriptions(request
, username
, format
):
77 subscriptions
= get_all_subscriptions(request
.user
)
78 title
= _('%(username)s\'s Subscription List') % {'username': username
}
79 return format_podcast_list(subscriptions
, format
, title
)
82 def format_podcast_list(obj_list
, format
, title
, get_podcast
=lambda x
: x
, json_map
=lambda x
: x
.url
):
84 Formats a list of podcasts for use in a API response
86 obj_list is a list of podcasts or objects that contain podcasts
87 format is one if txt, opml or json
88 title is a label of the list
89 if obj_list is a list of objects containing podcasts, get_podcast is the
90 function used to get the podcast out of the each of these objects
91 json_map is a function returning the contents of an object (from obj_list)
92 that should be contained in the result (only used for format='json')
95 podcasts
= map(get_podcast
, obj_list
)
96 s
= '\n'.join([p
.url
for p
in podcasts
] + [''])
97 return HttpResponse(s
, mimetype
='text/plain')
99 elif format
== 'opml':
100 podcasts
= map(get_podcast
, obj_list
)
101 exporter
= Exporter(title
)
102 opml
= exporter
.generate(podcasts
)
103 return HttpResponse(opml
, mimetype
='text/xml')
105 elif format
== 'json':
106 objs
= map(json_map
, obj_list
)
107 return JsonResponse(objs
)
113 def get_subscriptions(user
, device_uid
):
114 device
= get_object_or_404(Device
, uid
=device_uid
, user
=user
, deleted
=False)
115 return [s
.podcast
for s
in device
.get_subscriptions()]
118 def parse_subscription(raw_post_data
, format
):
120 urls
= raw_post_data
.split('\n')
122 elif format
== 'opml':
123 begin
= raw_post_data
.find('<?xml')
124 end
= raw_post_data
.find('</opml>') + 7
125 i
= Importer(content
=raw_post_data
[begin
:end
])
126 urls
= [p
['url'] for p
in i
.items
]
128 elif format
== 'json':
129 begin
= raw_post_data
.find('[')
130 end
= raw_post_data
.find(']') + 1
131 urls
= json
.loads(raw_post_data
[begin
:end
])
136 urls
= map(sanitize_url
, urls
)
137 urls
= filter(lambda x
: x
, urls
)
142 def set_subscriptions(urls
, user
, device_uid
):
143 device
, created
= Device
.objects
.get_or_create(user
=user
, uid
=device_uid
)
145 # undelete a previously deleted device
147 device
.deleted
= False
150 old
= [s
.podcast
.url
for s
in device
.get_subscriptions()]
151 new
= [p
for p
in urls
if p
not in old
]
152 rem
= [p
for p
in old
if p
not in urls
]
155 p
= Podcast
.objects
.get(url
=r
)
156 p
.unsubscribe(device
)
160 p
, created
= Podcast
.objects
.get_or_create(url
=n
)
164 # Only an empty response is a successful response
165 return HttpResponse('', mimetype
='text/plain')
169 @allowed_methods(['GET'])
170 def toplist(request
, count
, format
):
171 if int(count
) not in range(1,100):
174 toplist
= get_toplist(count
)
175 json_map
= lambda t
: {'url': t
.get_podcast().url
,
176 'title':t
.get_podcast().title
,
177 'description':t
.get_podcast().description
,
178 'subscribers':t
.subscriptions
,
179 'subscribers_last_week':t
.oldplace
}
180 title
= _('gpodder.net - Top %(count)d') % {'count': len(toplist
)}
181 return format_podcast_list(toplist
,
184 get_podcast
=lambda x
: x
.get_podcast(),
189 @allowed_methods(['GET'])
190 def search(request
, format
):
191 query
= request
.GET
.get('q', '').encode('utf-8')
194 return HttpResponseBadRequest('/search.opml|txt|json?q={query}')
196 results
= [r
.get_podcast() for r
in SearchEntry
.objects
.search(query
)[:20]]
198 json_map
= lambda p
: {'url':p
.url
, 'title':p
.title
, 'description':p
.description
}
199 title
= _('gpodder.net - Search')
200 return format_podcast_list(results
, format
, title
, json_map
=json_map
)
205 @allowed_methods(['GET'])
206 def suggestions(request
, count
, format
):
207 if int(count
) not in range(1,100):
210 suggestions
= SuggestionEntry
.objects
.for_user(user
)[:int(count
)]
211 json_map
= lambda p
: {'url': p
.url
, 'title': p
.title
, 'description': p
.description
}
212 title
= _('gpodder.net - %(count)d Suggestions') % {'count': len(suggestions
)}
213 return format_podcast_list(suggestions
, format
, title
, json_map
=json_map
)