diff --git a/api/opds_json.py b/api/opds_json.py
index 3bd5843e..21d55271 100644
--- a/api/opds_json.py
+++ b/api/opds_json.py
@@ -30,8 +30,9 @@ FORMAT_TO_MIMETYPE = {'pdf':"application/pdf",
'text':"text/html"}
UNGLUEIT_URL= 'https://unglue.it'
-ACQUISITION = "application/vnd.opds.acquisition+json"
+ACQUISITION = "application/opds+json"
FACET_RELATION = "opds:facet"
+JSONCONTEXT = "http://opds-spec.org/opds.jsonld"
def feeds():
for facet_path in facets.get_all_facets('Format'):
@@ -53,92 +54,106 @@ def isbn_node(isbn):
def work_node(work, facet=None):
- content={}
+ metadata = {"@type": "http://schema.org/EBook"}
+ links = []
+ images = []
+ acquires = []
+ content = {
+ "metadata": metadata,
+ "links": links,
+ "images": images,
+ "acquire": acquires
+ }
# title
- content["title"] = work.title
+ metadata["title"] = work.title
# id
- content.update(text_node('id', "{base}{url}".format(base=UNGLUEIT_URL,url=reverse('work_identifier',kwargs={'work_id':work.id}))))
+ links.append({
+ "rel": "self",
+ "href": "{base}{url}?work={id}".format(
+ base=UNGLUEIT_URL,
+ url=reverse('opdsjson_acqusition', args=['all']),
+ id=work.id
+ ),
+ "type": "application/opds-publication+json"
+ })
updated = None
# links for all ebooks
ebooks = facet.filter_model("Ebook",work.ebooks()) if facet else work.ebooks()
versions = set()
- content['_links'] = links = {}
for ebook in ebooks:
if updated is None:
# most recent ebook, first ebook in loop
updated = ebook.created.isoformat()
- content.update(text_node('updated', updated))
+ metadata['updated'] = updated
if not ebook.version_label in versions:
versions.add(ebook.version_label)
- link_node_attrib = {}
- ebookfiles = links.get("opds:acquisition:open-access",[])
- ebookfiles.append(link_node_attrib)
# ebook.download_url is an absolute URL with the protocol, domain, and path baked in
- link_node_attrib.update({"href":add_query_component(ebook.download_url, "feed=opds"),
- "rights": str(ebook.rights)})
+ acquire = {
+ "rel": "opds:acquisition:open-access",
+ "href": add_query_component(ebook.download_url, "feed=opds"),
+ "rights": str(ebook.rights)
+ }
if ebook.is_direct():
- link_node_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "")
+ acquire["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "")
else:
""" indirect acquisition, i.e. google books """
- link_node_attrib["type"] = "text/html"
- indirect_attrib = {}
- indirect = {"indirectAcquisition":indirect_attrib}
- indirect_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "")
- link_node_attrib.update(indirect)
+ acquire["type"] = "text/html"
+ acquire["indirectAcquisition"] = {
+ "type": FORMAT_TO_MIMETYPE.get(ebook.format)
+ }
if ebook.version_label:
- link_node_attrib.update({"version": ebook.version_label})
- links["opds:acquisition:open-access"] = ebookfiles
+ acquire["version"] = ebook.version_label
+
+ acquires.append(acquire)
# get the cover -- assume jpg?
if work.cover_image_small():
- cover_node_attrib = {}
- cover_node = {"opds:image:thumbnail": cover_node_attrib}
- cover_node_attrib.update({"href":work.cover_image_small(),
- "type":"image/"+work.cover_filetype(),
- })
- links.update(cover_node)
+ cover_node = {
+ "href": work.cover_image_small(),
+ "type": "image/"+work.cover_filetype(),
+ }
+ images.append(cover_node)
if work.cover_image_thumbnail():
- cover_node2_attrib = {}
- cover_node2 = {"opds:image": cover_node2_attrib}
- cover_node2_attrib.update({"href":work.cover_image_thumbnail(),
- "type":"image/"+work.cover_filetype(),
- })
- links.update(cover_node2)
+ cover_node2 = {
+ "href": work.cover_image_thumbnail(),
+ "type": "image/"+work.cover_filetype(),
+ }
+ images.append(cover_node2)
# 2012
- content.update({"issued": work.publication_date})
+ metadata["issued"] = work.publication_date
# author
# TO DO: include all authors?
- content["contributor"] = {"name": work.author()}
+ metadata["author"] = work.author()
# publisher
#Open Book Publishers
if len(work.publishers()):
- content["publishers"] = [{"publisher": publisher.name.name}
+ metadata["publishers"] = [{"publisher": publisher.name.name}
for publisher in work.publishers()]
# language
- content["language"] = work.language
+ metadata["language"] = work.language
# description
- content["summary"] = work.description
+ metadata["summary"] = work.description
# identifiers
- if work.identifiers.filter(type='isbn'):
- content['identifers'] = [isbn_node(isbn.value)
+ if work.identifiers.filter(type='identifier'):
+ metadata['other_identifiers'] = [isbn_node(isbn.value)
for isbn in work.identifiers.filter(type='isbn')[0:9]] #10 should be more than enough
# subject tags
subjects = [subject.name for subject in work.subjects.all()]
if subjects:
- content["category"] = subjects
+ metadata["subjects"] = subjects
# age level
#
@@ -148,11 +163,11 @@ def work_node(work, facet=None):
age_level_node_attrib["scheme"] = 'http://schema.org/typicalAgeRange'
age_level_node_attrib["term"] = work.age_level
age_level_node_attrib["label"] = work.get_age_level_display()
- content.update(age_level_node)
+ metadata.update(age_level_node)
# rating
- content["Rating"] = {"ratingValue":"{:}".format(work.priority())}
+ metadata["rating"] = {"ratingValue":"{:}".format(work.priority())}
return content
class Facet:
@@ -188,6 +203,9 @@ def get_facet_facet(facet_path):
def opds_feed_for_work(work_id):
class single_work_facet:
def __init__(self, work_id):
+ class NullFacet(facets.BaseFacet):
+ def get_other_groups(self):
+ return[]
try:
works=models.Work.objects.filter(id=work_id)
except models.Work.DoesNotExist:
@@ -198,7 +216,7 @@ def opds_feed_for_work(work_id):
self.works=works
self.title='Unglue.it work #%s' % work_id
self.feed_path=''
- self.facet_object= facets.BaseFacet(None)
+ self.facet_object= NullFacet(None)
return opds_feed_for_works( single_work_facet(work_id) )
def opds_feed_for_works(the_facet, page=None, order_by='newest'):
@@ -206,49 +224,34 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'):
works = the_facet.works
feed_path = the_facet.feed_path
title = the_facet.title
-
- feed = {'_type': ACQUISITION}
+ metadata = {"title": title}
+ links = []
+ feedlist = []
+ feed = {"@context": JSONCONTEXT, "metadata": metadata, "links": links, "publications": feedlist}
# add title
# TO DO: will need to calculate the number items and where in the feed we are
- feed.update(text_node('title', title + ' - sorted by ' + order_by))
-
- # id
-
- feed.update(text_node('id', "{url}/api/opdsjson/{feed_path}/?order_by={order_by}".format(url=UNGLUEIT_URL,
- feed_path=urlquote(feed_path), order_by=order_by)))
-
- # updated
- # TO DO: fix time zone?
- # also use our wrapped datetime code
-
- feed.update(text_node('updated',
- pytz.utc.localize(datetime.datetime.utcnow()).isoformat()))
-
- # author
-
- author_node = {"author":{'name': 'unglue.it','uri': UNGLUEIT_URL}}
- feed.update(author_node)
+ metadata['title'] = title + ' - sorted by ' + order_by
# links: start, self, next/prev (depending what's necessary -- to start with put all CC books)
- feed['_links'] = {}
- # start link
- append_navlink(feed, 'start', feed_path, None , order_by, title="First {}".format(books_per_page))
-
- # next link
if not page:
- page =0
+ page = 0
else:
try:
- page=int(page)
+ page = int(page)
except TypeError:
page=0
+
+ # self link
+ append_navlink(feed, 'self', feed_path, page , order_by, title="First {}".format(books_per_page))
+ # next link
try:
works[books_per_page * page + books_per_page]
- append_navlink(feed, 'next', feed_path, page+1 , order_by, title="Next {}".format(books_per_page))
+ append_navlink(feed, 'next', feed_path, page+1 , order_by,
+ title="Next {}".format(books_per_page))
except IndexError:
pass
@@ -264,24 +267,21 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'):
works = islice(works, books_per_page * page, books_per_page * page + books_per_page)
if page > 0:
append_navlink(feed, 'previous', feed_path, page-1, order_by, title="Previous {}".format(books_per_page))
- feedlist = []
- feed['publications'] = feedlist
for work in works:
node = work_node(work, facet=the_facet.facet_object)
feedlist.append(node)
return json.dumps(feed,indent=2, separators=(',', ': '), sort_keys=False)
def append_navlink(feed, rel, path, page, order_by, group=None, active=None , title=""):
- if page==None:
- return
- link = {
- "href": UNGLUEIT_URL + "/api/opdsjson/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ),
- "type": ACQUISITION,
- "title": title,
- }
+ link = {
+ "rel": rel,
+ "href": UNGLUEIT_URL + "/api/opdsjson/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ),
+ "type": ACQUISITION,
+ "title": title,
+ }
if rel == FACET_RELATION:
if group:
link['facetGroup'] = group
if active:
link['activeFacet'] = 'true'
- feed['_links'][rel] = link
\ No newline at end of file
+ feed['links'].append(link)
\ No newline at end of file
diff --git a/api/templates/opds.json b/api/templates/opds.json
index 85b33edb..06d20912 100644
--- a/api/templates/opds.json
+++ b/api/templates/opds.json
@@ -1,22 +1,17 @@
{
- "_type": "application/vnd.opds.navigation+json",
- "title": "Unglue.it Catalog",
+ "@context": "http://opds-spec.org/opds.jsonld",
+ "metadata":{"title": "Unglue.it Catalog"},
- "_links": {
- "start": { "href": "https://unglue.it{% url 'opds' %}", "type": "application/vnd.opds.navigation+json" },
- "opds:featured": {"href": "{{ feed.feed_path }}/?order_by=popular", "type": "application/vnd.opds.acquisition+json" }
- },
+ "links": [
+ {"rel":"self", "href": "https://unglue.it{% url 'opds' %}", "type": "application/opds+json" },
+ {"rel":"opds:featured", "href": "{{ feed.feed_path|urlencode }}/?order_by=popular", "type": "application/opds+json" }
+ ],
- "navigation": [{
- "title": {{ feed.title }} - Popular,
- "_links": {"opds:featured": { "href": "{{ feed.feed_path }}/?order_by=popular", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json"}}
- },{
- "title": "{{ feed.title }} - New",
- "_links": {"opds:new": { "href": "{{ feed.feed_path }}/?order_by=newest", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json" }}
- }
- {% for feed in feeds %},{
- "title": "{{ feed.title }}",
- "_links": {"opds:new": { "href": "{{ feed.feed_path }}", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json" }}
- }
- {% endfor %}]
+ "navigation": [
+ {"title": "{{ feed.title }} - Popular", "href": "{{ feed.feed_path|urlencode }}/?order_by=popular", "type": "application/opds+json"},
+ {"title": "{{ feed.title }} - New", "href": "{{ feed.feed_path|urlencode }}/?order_by=newest", "type": "application/opds+json" },
+ {% for feed in feeds %}
+ {"title": "{{ feed.title }}", "href": "{{ feed.feed_path|urlencode }}/", "type": "application/opds+json" },
+ {% endfor %}
+ ]
}
diff --git a/api/views.py b/api/views.py
index ec362bd7..05369a22 100755
--- a/api/views.py
+++ b/api/views.py
@@ -194,7 +194,7 @@ class OPDSAcquisitionView(View):
if work:
if self.json:
return HttpResponse(opds_json.opds_feed_for_work(work),
- content_type="application/json;profile=opds-catalog;kind=acquisition")
+ content_type="application/opds-publication+json")
else:
return HttpResponse(opds.opds_feed_for_work(work),
content_type="application/atom+xml;profile=opds-catalog;kind=acquisition")
@@ -208,7 +208,7 @@ class OPDSAcquisitionView(View):
if self.json:
facet_class = opds_json.get_facet_class(facet)()
return HttpResponse(facet_class.feed(page,order_by),
- content_type="application/vnd.opds.acquisition+json")
+ content_type="application/opds+json")
else:
facet_class = opds.get_facet_class(facet)()
return HttpResponse(facet_class.feed(page,order_by),