mirror of
https://github.com/9001/copyparty.git
synced 2026-02-27 22:13:10 -07:00
opds: add opensearch support
Tested with koreader and works great! Discussed with original OPDS author @Scotsguy here https://github.com/9001/copyparty/pull/779#issuecomment-3482160504 Bassed on these specs https://specs.opds.io/opds-1.2#3-search https://github.com/koreader/koreader/pull/7380 This PR complies with the DCO; https://developercertificate.org/ Signed-off-by: Brandon Philips <brandon@ifup.org>
This commit is contained in:
parent
e8609b87af
commit
738d3c295f
|
|
@ -7412,12 +7412,67 @@ class HttpCli(object):
|
||||||
dirs.sort(key=itemgetter("name"))
|
dirs.sort(key=itemgetter("name"))
|
||||||
|
|
||||||
if is_opds:
|
if is_opds:
|
||||||
|
# OpenSearch Description format requires a full-qualified URL and a "Short Name" under 16 characters
|
||||||
|
# which will be the longname truncated in the template.
|
||||||
|
# Relevant specs:
|
||||||
|
# https://specs.opds.io/opds-1.2#3-search
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/XML/Guides/OpenSearch
|
||||||
|
if "osd" in self.uparam:
|
||||||
|
j2a["longname"] = "%s %s" % (self.args.bname, self.vpath)
|
||||||
|
j2a["search_url"] = "/" + self.args.RS + vpath
|
||||||
|
|
||||||
|
xml = self.j2s("opds_osd", **j2a)
|
||||||
|
self.reply(xml.encode("utf-8"), mime="application/opensearchdescription+xml")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if "q" in self.uparam:
|
||||||
|
q = self.uparam["q"]
|
||||||
|
idx = self.conn.get_u2idx()
|
||||||
|
if not idx:
|
||||||
|
raise Pebkac(500, "indexer not available")
|
||||||
|
|
||||||
|
# generate a raw query similar to web interface for multiple words
|
||||||
|
r = " and ".join(f"name like *{part}*" for part in q.split())
|
||||||
|
|
||||||
|
hits, _, _ = idx.search(self.uname, [self.vn], r, 1000)
|
||||||
|
|
||||||
|
# clear files and dirs for search results
|
||||||
|
files = []
|
||||||
|
dirs = []
|
||||||
|
|
||||||
|
prefix = vpath + "/" if vpath else ""
|
||||||
|
|
||||||
|
for h in hits:
|
||||||
|
rp = h["rp"]
|
||||||
|
|
||||||
|
# if user starts in a subfolder they shouldn't see hits from parent
|
||||||
|
if prefix and not rp.startswith(prefix):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# remove base path assuming user knows where they are already in their structure
|
||||||
|
name = rp[len(prefix):]
|
||||||
|
|
||||||
|
dt = datetime.fromtimestamp(h["ts"], UTC).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
item = {
|
||||||
|
"lead": "-",
|
||||||
|
"href": "/" + self.args.RS + rp,
|
||||||
|
"name": unquotep(name),
|
||||||
|
"sz": h["sz"],
|
||||||
|
"dt": dt,
|
||||||
|
"ts": h["ts"],
|
||||||
|
}
|
||||||
|
files.append(item)
|
||||||
|
|
||||||
# exclude files which don't match --opds-exts
|
# exclude files which don't match --opds-exts
|
||||||
allowed_exts = vf.get("opds_exts") or self.args.opds_exts
|
allowed_exts = vf.get("opds_exts") or self.args.opds_exts
|
||||||
if allowed_exts:
|
if allowed_exts:
|
||||||
files = [
|
files = [
|
||||||
x for x in files if x["name"].rsplit(".", 1)[-1] in allowed_exts
|
x for x in files if x["name"].rsplit(".", 1)[-1] in allowed_exts
|
||||||
]
|
]
|
||||||
|
|
||||||
|
j2a["opds_osd"] = "/%s?opds&osd" % (self.args.RS + quotep(vpath))
|
||||||
|
|
||||||
for item in dirs:
|
for item in dirs:
|
||||||
href = item["href"]
|
href = item["href"]
|
||||||
href += ("&" if "?" in href else "?") + "opds"
|
href += ("&" if "?" in href else "?") + "opds"
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ class HttpSrv(object):
|
||||||
]
|
]
|
||||||
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
||||||
self.j2["opds"] = env.get_template("opds.xml")
|
self.j2["opds"] = env.get_template("opds.xml")
|
||||||
|
self.j2["opds_osd"] = env.get_template("opds_osd.xml")
|
||||||
self.prism = has_resource(self.E, "web/deps/prism.js.gz")
|
self.prism = has_resource(self.E, "web/deps/prism.js.gz")
|
||||||
|
|
||||||
if self.args.ipu:
|
if self.args.ipu:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<link rel="search"
|
||||||
|
href="{{ opds_osd | e }}"
|
||||||
|
type="application/opensearchdescription+xml"
|
||||||
|
title="Search"/>
|
||||||
{%- for d in dirs %}
|
{%- for d in dirs %}
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{ d.name }}</title>
|
<title>{{ d.name }}</title>
|
||||||
|
|
|
||||||
6
copyparty/web/opds_osd.xml
Normal file
6
copyparty/web/opds_osd.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
|
<ShortName>CP {{ longname | truncate(13) }}</ShortName>
|
||||||
|
<Description>Copyparty {{ longname }}</Description>
|
||||||
|
<Url type="application/atom+xml;profile=opds-catalog" template="{{ search_url }}?opds&q={searchTerms}"/>
|
||||||
|
</OpenSearchDescription>
|
||||||
|
|
@ -102,6 +102,7 @@ copyparty/web/mde.html,
|
||||||
copyparty/web/mde.js,
|
copyparty/web/mde.js,
|
||||||
copyparty/web/msg.html,
|
copyparty/web/msg.html,
|
||||||
copyparty/web/opds.xml,
|
copyparty/web/opds.xml,
|
||||||
|
copyparty/web/opds_osd.xml,
|
||||||
copyparty/web/rups.css,
|
copyparty/web/rups.css,
|
||||||
copyparty/web/rups.html,
|
copyparty/web/rups.html,
|
||||||
copyparty/web/rups.js,
|
copyparty/web/rups.js,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue