Ivan000 commited on
Commit
89e8c7a
·
verified ·
1 Parent(s): 33f6f2c

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +42 -10
main.py CHANGED
@@ -7,6 +7,7 @@ from bs4 import BeautifulSoup
7
  from urllib.parse import quote
8
  import xml.etree.ElementTree as ET
9
  from datetime import datetime
 
10
 
11
  app = FastAPI()
12
 
@@ -53,7 +54,6 @@ def duckduckgo_search(query: str):
53
  return results
54
 
55
  # ========== OPDS Feed Generators ==========
56
-
57
  def generate_root_feed():
58
  ns = "http://www.w3.org/2005/Atom"
59
  ET.register_namespace("", ns)
@@ -62,7 +62,7 @@ def generate_root_feed():
62
  ET.SubElement(feed, "title").text = "DuckDuckGo OPDS Catalog"
63
  ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
64
 
65
- # Entry for search subsection
66
  entry_search = ET.SubElement(feed, "entry")
67
  ET.SubElement(entry_search, "id").text = "urn:uuid:duckopds-search-section"
68
  ET.SubElement(entry_search, "title").text = "Search"
@@ -73,7 +73,7 @@ def generate_root_feed():
73
  "type": "application/atom+xml;profile=opds-catalog;kind=acquisition"
74
  })
75
 
76
- # Entry for cached subsection (placeholder)
77
  entry_cached = ET.SubElement(feed, "entry")
78
  ET.SubElement(entry_cached, "id").text = "urn:uuid:duckopds-cached-section"
79
  ET.SubElement(entry_cached, "title").text = "Cached"
@@ -87,14 +87,30 @@ def generate_root_feed():
87
  return ET.tostring(feed, encoding="utf-8", xml_declaration=True)
88
 
89
 
90
- def generate_search_feed(query: str, results):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  ns = "http://www.w3.org/2005/Atom"
92
  ET.register_namespace("", ns)
93
  feed = ET.Element("feed", xmlns=ns)
94
  ET.SubElement(feed, "id").text = f"urn:uuid:duckopds-search-{quote(query)}"
95
  ET.SubElement(feed, "title").text = f"Search results for '{query}'"
96
  ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
97
-
98
  for title, url in results:
99
  entry = ET.SubElement(feed, "entry")
100
  ET.SubElement(entry, "id").text = url
@@ -108,7 +124,6 @@ def generate_search_feed(query: str, results):
108
  return ET.tostring(feed, encoding="utf-8", xml_declaration=True)
109
 
110
  # ========== Routes ==========
111
-
112
  @app.get("/opds", include_in_schema=False)
113
  def opds_root() -> Response:
114
  xml_data = generate_root_feed()
@@ -119,17 +134,34 @@ def opds_root() -> Response:
119
 
120
  @app.get("/opds/search")
121
  def opds_search(
122
- q: str = Query(..., description="Search query"),
123
- searchType: str = Query(None, alias="searchType")
124
  ) -> Response:
125
- # Ignore searchType parameter
 
 
 
 
126
  results = duckduckgo_search(q)
127
- xml_data = generate_search_feed(q, results)
128
  return Response(
129
  content=xml_data,
130
  media_type="application/atom+xml;profile=opds-catalog;kind=acquisition"
131
  )
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  @app.get("/download")
134
  def download_fb2(url: str) -> Response:
135
  res = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)
 
7
  from urllib.parse import quote
8
  import xml.etree.ElementTree as ET
9
  from datetime import datetime
10
+ from typing import Optional
11
 
12
  app = FastAPI()
13
 
 
54
  return results
55
 
56
  # ========== OPDS Feed Generators ==========
 
57
  def generate_root_feed():
58
  ns = "http://www.w3.org/2005/Atom"
59
  ET.register_namespace("", ns)
 
62
  ET.SubElement(feed, "title").text = "DuckDuckGo OPDS Catalog"
63
  ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
64
 
65
+ # Entry: Search Section
66
  entry_search = ET.SubElement(feed, "entry")
67
  ET.SubElement(entry_search, "id").text = "urn:uuid:duckopds-search-section"
68
  ET.SubElement(entry_search, "title").text = "Search"
 
73
  "type": "application/atom+xml;profile=opds-catalog;kind=acquisition"
74
  })
75
 
76
+ # Entry: Cached Section
77
  entry_cached = ET.SubElement(feed, "entry")
78
  ET.SubElement(entry_cached, "id").text = "urn:uuid:duckopds-cached-section"
79
  ET.SubElement(entry_cached, "title").text = "Cached"
 
87
  return ET.tostring(feed, encoding="utf-8", xml_declaration=True)
88
 
89
 
90
+ def generate_search_form_feed():
91
+ ns = "http://www.w3.org/2005/Atom"
92
+ ET.register_namespace("", ns)
93
+ feed = ET.Element("feed", xmlns=ns)
94
+ ET.SubElement(feed, "id").text = "urn:uuid:duckopds-search-form"
95
+ ET.SubElement(feed, "title").text = "Search"
96
+ ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
97
+ # Templated search link
98
+ feed.append(ET.Element("link", {
99
+ "rel": "search",
100
+ "type": "application/atom+xml;profile=opds-catalog;kind=acquisition",
101
+ "href": "/opds/search?q={searchTerms}",
102
+ "templated": "true"
103
+ }))
104
+ return ET.tostring(feed, encoding="utf-8", xml_declaration=True)
105
+
106
+
107
+ def generate_search_results_feed(query: str, results):
108
  ns = "http://www.w3.org/2005/Atom"
109
  ET.register_namespace("", ns)
110
  feed = ET.Element("feed", xmlns=ns)
111
  ET.SubElement(feed, "id").text = f"urn:uuid:duckopds-search-{quote(query)}"
112
  ET.SubElement(feed, "title").text = f"Search results for '{query}'"
113
  ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
 
114
  for title, url in results:
115
  entry = ET.SubElement(feed, "entry")
116
  ET.SubElement(entry, "id").text = url
 
124
  return ET.tostring(feed, encoding="utf-8", xml_declaration=True)
125
 
126
  # ========== Routes ==========
 
127
  @app.get("/opds", include_in_schema=False)
128
  def opds_root() -> Response:
129
  xml_data = generate_root_feed()
 
134
 
135
  @app.get("/opds/search")
136
  def opds_search(
137
+ q: Optional[str] = Query(None, description="Search query"),
138
+ searchType: Optional[str] = Query(None, alias="searchType")
139
  ) -> Response:
140
+ if not q:
141
+ xml_data = generate_search_form_feed()
142
+ return Response(content=xml_data,
143
+ media_type="application/atom+xml;profile=opds-catalog;kind=search")
144
+ # perform actual search for q
145
  results = duckduckgo_search(q)
146
+ xml_data = generate_search_results_feed(q, results)
147
  return Response(
148
  content=xml_data,
149
  media_type="application/atom+xml;profile=opds-catalog;kind=acquisition"
150
  )
151
 
152
+ @app.get("/opds/cached")
153
+ def opds_cached() -> Response:
154
+ # placeholder empty feed
155
+ feed = ET.Element("feed", xmlns="http://www.w3.org/2005/Atom")
156
+ ET.SubElement(feed, "id").text = "urn:uuid:duckopds-cached"
157
+ ET.SubElement(feed, "title").text = "Cached Items"
158
+ ET.SubElement(feed, "updated").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
159
+ xml_data = ET.tostring(feed, encoding="utf-8", xml_declaration=True)
160
+ return Response(
161
+ content=xml_data,
162
+ media_type="application/atom+xml;profile=opds-catalog;kind=navigation"
163
+ )
164
+
165
  @app.get("/download")
166
  def download_fb2(url: str) -> Response:
167
  res = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)