|
1 | | -import DomHandler, { DomHandlerOptions, Node, Element } from "domhandler"; |
2 | | -import * as DomUtils from "domutils"; |
| 1 | +import DomHandler, { DomHandlerOptions } from "domhandler"; |
| 2 | +import { getFeed, Feed } from "domutils"; |
3 | 3 | import { Parser, ParserOptions } from "./Parser"; |
4 | 4 |
|
5 | | -enum FeedItemMediaMedium { |
6 | | - image, |
7 | | - audio, |
8 | | - video, |
9 | | - document, |
10 | | - executable, |
11 | | -} |
12 | | - |
13 | | -enum FeedItemMediaExpression { |
14 | | - sample, |
15 | | - full, |
16 | | - nonstop, |
17 | | -} |
18 | | - |
19 | | -interface FeedItemMedia { |
20 | | - url?: string; |
21 | | - fileSize?: number; |
22 | | - type?: string; |
23 | | - medium: FeedItemMediaMedium | undefined; |
24 | | - isDefault: boolean; |
25 | | - expression?: FeedItemMediaExpression; |
26 | | - bitrate?: number; |
27 | | - framerate?: number; |
28 | | - samplingrate?: number; |
29 | | - channels?: number; |
30 | | - duration?: number; |
31 | | - height?: number; |
32 | | - width?: number; |
33 | | - lang?: string; |
34 | | -} |
35 | | - |
36 | | -interface FeedItem { |
37 | | - id?: string; |
38 | | - title?: string; |
39 | | - link?: string; |
40 | | - description?: string; |
41 | | - pubDate?: Date; |
42 | | - media?: FeedItemMedia[]; |
43 | | -} |
44 | | - |
45 | | -interface Feed { |
46 | | - type?: string; |
47 | | - id?: string; |
48 | | - title?: string; |
49 | | - link?: string; |
50 | | - description?: string; |
51 | | - updated?: Date; |
52 | | - author?: string; |
53 | | - items?: FeedItem[]; |
54 | | -} |
| 5 | +export { getFeed }; |
55 | 6 |
|
56 | 7 | /** @deprecated Handler is no longer necessary; use `getFeed` or `parseFeed` instead. */ |
57 | 8 | export class FeedHandler extends DomHandler { |
@@ -85,189 +36,6 @@ export class FeedHandler extends DomHandler { |
85 | 36 | } |
86 | 37 | } |
87 | 38 |
|
88 | | -/** |
89 | | - * Get the feed object from the root of a DOM tree. |
90 | | - * |
91 | | - * @param dom - The DOM to to extract the feed from. |
92 | | - * @returns The feed. |
93 | | - */ |
94 | | -export function getFeed(dom: Node[]): Feed | null { |
95 | | - const feedRoot = getOneElement(isValidFeed, dom); |
96 | | - |
97 | | - if (!feedRoot) return null; |
98 | | - |
99 | | - const feed: Feed = {}; |
100 | | - |
101 | | - if (feedRoot.name === "feed") { |
102 | | - const childs = feedRoot.children; |
103 | | - feed.type = "atom"; |
104 | | - addConditionally(feed, "id", "id", childs); |
105 | | - addConditionally(feed, "title", "title", childs); |
106 | | - const href = getAttribute("href", getOneElement("link", childs)); |
107 | | - if (href) { |
108 | | - feed.link = href; |
109 | | - } |
110 | | - addConditionally(feed, "description", "subtitle", childs); |
111 | | - |
112 | | - const updated = fetch("updated", childs); |
113 | | - if (updated) { |
114 | | - feed.updated = new Date(updated); |
115 | | - } |
116 | | - |
117 | | - addConditionally(feed, "author", "email", childs, true); |
118 | | - feed.items = getElements("entry", childs).map((item) => { |
119 | | - const entry: FeedItem = {}; |
120 | | - const { children } = item; |
121 | | - |
122 | | - addConditionally(entry, "id", "id", children); |
123 | | - addConditionally(entry, "title", "title", children); |
124 | | - |
125 | | - const href = getAttribute("href", getOneElement("link", children)); |
126 | | - if (href) { |
127 | | - entry.link = href; |
128 | | - } |
129 | | - |
130 | | - const description = |
131 | | - fetch("summary", children) || fetch("content", children); |
132 | | - if (description) { |
133 | | - entry.description = description; |
134 | | - } |
135 | | - |
136 | | - const pubDate = fetch("updated", children); |
137 | | - if (pubDate) { |
138 | | - entry.pubDate = new Date(pubDate); |
139 | | - } |
140 | | - |
141 | | - entry.media = getMediaElements(children); |
142 | | - |
143 | | - return entry; |
144 | | - }); |
145 | | - } else { |
146 | | - const childs = |
147 | | - getOneElement("channel", feedRoot.children)?.children ?? []; |
148 | | - feed.type = feedRoot.name.substr(0, 3); |
149 | | - feed.id = ""; |
150 | | - |
151 | | - addConditionally(feed, "title", "title", childs); |
152 | | - addConditionally(feed, "link", "link", childs); |
153 | | - addConditionally(feed, "description", "description", childs); |
154 | | - |
155 | | - const updated = fetch("lastBuildDate", childs); |
156 | | - if (updated) { |
157 | | - feed.updated = new Date(updated); |
158 | | - } |
159 | | - |
160 | | - addConditionally(feed, "author", "managingEditor", childs, true); |
161 | | - |
162 | | - feed.items = getElements("item", feedRoot.children).map( |
163 | | - (item: Element) => { |
164 | | - const entry: FeedItem = {}; |
165 | | - const { children } = item; |
166 | | - addConditionally(entry, "id", "guid", children); |
167 | | - addConditionally(entry, "title", "title", children); |
168 | | - addConditionally(entry, "link", "link", children); |
169 | | - addConditionally(entry, "description", "description", children); |
170 | | - const pubDate = fetch("pubDate", children); |
171 | | - if (pubDate) entry.pubDate = new Date(pubDate); |
172 | | - entry.media = getMediaElements(children); |
173 | | - return entry; |
174 | | - } |
175 | | - ); |
176 | | - } |
177 | | - |
178 | | - return feed; |
179 | | -} |
180 | | - |
181 | | -function getMediaElements(where: Node | Node[]): FeedItemMedia[] { |
182 | | - return getElements("media:content", where).map((elem) => { |
183 | | - const media: FeedItemMedia = { |
184 | | - medium: elem.attribs.medium as unknown as |
185 | | - | FeedItemMediaMedium |
186 | | - | undefined, |
187 | | - isDefault: !!elem.attribs.isDefault, |
188 | | - }; |
189 | | - |
190 | | - if (elem.attribs.url) { |
191 | | - media.url = elem.attribs.url; |
192 | | - } |
193 | | - if (elem.attribs.fileSize) { |
194 | | - media.fileSize = parseInt(elem.attribs.fileSize, 10); |
195 | | - } |
196 | | - if (elem.attribs.type) { |
197 | | - media.type = elem.attribs.type; |
198 | | - } |
199 | | - if (elem.attribs.expression) { |
200 | | - media.expression = elem.attribs |
201 | | - .expression as unknown as FeedItemMediaExpression; |
202 | | - } |
203 | | - if (elem.attribs.bitrate) { |
204 | | - media.bitrate = parseInt(elem.attribs.bitrate, 10); |
205 | | - } |
206 | | - if (elem.attribs.framerate) { |
207 | | - media.framerate = parseInt(elem.attribs.framerate, 10); |
208 | | - } |
209 | | - if (elem.attribs.samplingrate) { |
210 | | - media.samplingrate = parseInt(elem.attribs.samplingrate, 10); |
211 | | - } |
212 | | - if (elem.attribs.channels) { |
213 | | - media.channels = parseInt(elem.attribs.channels, 10); |
214 | | - } |
215 | | - if (elem.attribs.duration) { |
216 | | - media.duration = parseInt(elem.attribs.duration, 10); |
217 | | - } |
218 | | - if (elem.attribs.height) { |
219 | | - media.height = parseInt(elem.attribs.height, 10); |
220 | | - } |
221 | | - if (elem.attribs.width) { |
222 | | - media.width = parseInt(elem.attribs.width, 10); |
223 | | - } |
224 | | - if (elem.attribs.lang) { |
225 | | - media.lang = elem.attribs.lang; |
226 | | - } |
227 | | - |
228 | | - return media; |
229 | | - }); |
230 | | -} |
231 | | - |
232 | | -function getElements(tagName: string, where: Node | Node[]) { |
233 | | - return DomUtils.getElementsByTagName(tagName, where, true); |
234 | | -} |
235 | | -function getOneElement( |
236 | | - tagName: string | ((name: string) => boolean), |
237 | | - node: Node | Node[] |
238 | | -): Element | null { |
239 | | - return DomUtils.getElementsByTagName(tagName, node, true, 1)[0]; |
240 | | -} |
241 | | -function fetch(tagName: string, where: Node | Node[], recurse = false): string { |
242 | | - return DomUtils.textContent( |
243 | | - DomUtils.getElementsByTagName(tagName, where, recurse, 1) |
244 | | - ).trim(); |
245 | | -} |
246 | | - |
247 | | -function getAttribute(name: string, elem: Element | null): string | null { |
248 | | - if (!elem) { |
249 | | - return null; |
250 | | - } |
251 | | - |
252 | | - const { attribs } = elem; |
253 | | - return attribs[name]; |
254 | | -} |
255 | | - |
256 | | -function addConditionally<T>( |
257 | | - obj: T, |
258 | | - prop: keyof T, |
259 | | - what: string, |
260 | | - where: Node | Node[], |
261 | | - recurse = false |
262 | | -) { |
263 | | - const tmp = fetch(what, where, recurse); |
264 | | - if (tmp) obj[prop] = tmp as unknown as T[keyof T]; |
265 | | -} |
266 | | - |
267 | | -function isValidFeed(value: string) { |
268 | | - return value === "rss" || value === "feed" || value === "rdf:RDF"; |
269 | | -} |
270 | | - |
271 | 39 | /** |
272 | 40 | * Parse a feed. |
273 | 41 | * |
|
0 commit comments