Skip to content

Commit 2bc15ae

Browse files
change query, add aprse endpoint
1 parent 1a7df81 commit 2bc15ae

File tree

6 files changed

+74
-36
lines changed

6 files changed

+74
-36
lines changed

README.md

+30-7
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,33 @@ Default behavior is to return HTML as page. You can use query parameters to chan
3232

3333
Query parameters
3434

35-
| Name | Required | Description |
36-
| ------ | -------- | ------------------------------------------------------------------ |
37-
| `url` | true | URL of RSS feed, allow base64 URL |
38-
| `html` | false | Return JSON response with HTML as string |
39-
| `json` | false | Return JSON response with `Podcast` object, it represents RSS feed |
40-
| `xml` | false | Return XML response with RSS feed |
35+
| Name | Required | Type | Default | Description |
36+
| -------- | -------- | --------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
37+
| `url` | true | `string` | `undefined` | URL of RSS feed, allow base64 URL |
38+
| `format` | false | `html`, `json`, `xml` | `html` | Type of response, default `html` will render HTML page, `json` will give JSON response with HTML string and `xml` will give original RSS feed |
4139

4240
> [!WARNING]\
4341
> If crawler is not a browser, response will be original XML feed.
4442
45-
Example: <https://feed-renderer.git-projects.xyz/api/renderer?url=https://feeds.acast.com/public/shows/game-of-roles-magic>
43+
Example: <https://feed-renderer.git-projects.xyz/api/renderer?url=https://2hdp.fr/2HDP.xml>
44+
45+
### Parser
46+
47+
To parse RSS feed, you can use the `/parser` endpoint.
48+
49+
```bash
50+
/api/parser
51+
```
52+
53+
Return a JSON response with `Podcast` object represent RSS feed.
54+
55+
Query parameters
56+
57+
| Name | Required | Type | Default | Description |
58+
| ----- | -------- | -------- | ----------- | --------------------------------- |
59+
| `url` | true | `string` | `undefined` | URL of RSS feed, allow base64 URL |
60+
61+
Example: <https://feed-renderer.git-projects.xyz/api/parser?url=https://2hdp.fr/2HDP.xml>
4662

4763
## Installation
4864

@@ -119,6 +135,13 @@ Run tests
119135
pnpm test
120136
```
121137

138+
## Roadmap
139+
140+
- [x] Render RSS feed
141+
- [ ] Add option for apple podcast banner
142+
- [ ] Add color customization
143+
- [ ] Add multiple feeds options
144+
122145
## Credits
123146

124147
- [`unjs`](https://github.com/unjs): for `unjs/h3`, `unjs/ofetch` and `unjs/listhen`

src/components/feed.css

+11-3
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,18 @@ header {
3232
}
3333

3434
.rss {
35-
position: absolute;
35+
position: fixed;
3636
z-index: 10;
37-
top: 1rem;
38-
left: 1rem;
37+
top: 0;
38+
left: 0;
39+
padding: 0.3rem;
40+
background-color: rgba(0, 0, 0, 0.5);
41+
z-index: 20;
42+
border-bottom-right-radius: 0.375rem;
43+
}
44+
45+
.rss:hover {
46+
background-color: rgba(0, 0, 0, 0.75);
3947
}
4048

4149
.rss svg {

src/components/head.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ export default {
2929
name: 'viewport',
3030
content: 'width=device-width, initial-scale=1',
3131
}),
32-
h('meta', {
33-
name: 'apple-itunes-app',
34-
content: 'app-id=993490336',
35-
}),
32+
// h('meta', {
33+
// name: 'apple-itunes-app',
34+
// content: 'app-id=993490336',
35+
// }),
3636
h('title', props.title),
3737
h('link', {
3838
rel: 'alternate',

src/components/header.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default {
2222

2323
return h('header', [
2424
h('div', { class: 'rss' }, [
25-
h('a', { href: props.podcast?.feedUrl, target: '_blank', rel: 'noopener noreferrer', title: 'RSS feed' }, [
25+
h('a', { href: props.podcast?.xmlRenderUrl, target: '_blank', rel: 'noopener noreferrer', title: 'RSS feed' }, [
2626
h('svg', { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 20 20' }, [
2727
h('g', {}, [
2828
h('path', { d: 'M3.75 3a.75.75 0 0 0-.75.75v.5c0 .414.336.75.75.75H4c6.075 0 11 4.925 11 11v.25c0 .414.336.75.75.75h.5a.75.75 0 0 0 .75-.75V16C17 8.82 11.18 3 4 3h-.25Z' }),
@@ -41,7 +41,7 @@ export default {
4141
h('div', { class: 'header-block__description prose' }, [
4242
h('div', { innerHTML: props.podcast?.description }),
4343
]),
44-
h('a', { class: 'header-block__button', href: props.podcast?.link }, [
44+
h('a', { class: 'header-block__button', href: props.podcast?.feedUrl }, [
4545
h('svg', { class: 'header-block__button__icon', xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24' }, [
4646
h('path', { fill: 'currentColor', d: 'M11 22v-8.275q-.45-.275-.725-.712T10 12q0-.825.588-1.413T12 10q.825 0 1.413.588T14 12q0 .575-.275 1.025t-.725.7V22h-2Zm-5.9-2.75q-1.425-1.375-2.262-3.238T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.15-.838 4.025T18.9 19.25l-1.4-1.4q1.15-1.1 1.825-2.613T20 12q0-3.35-2.325-5.675T12 4Q8.65 4 6.325 6.325T4 12q0 1.725.675 3.225t1.85 2.6L5.1 19.25Zm2.825-2.825q-.875-.825-1.4-1.963T6 12q0-2.5 1.75-4.25T12 6q2.5 0 4.25 1.75T18 12q0 1.325-.525 2.475t-1.4 1.95L14.65 15q.625-.575.988-1.35T16 12q0-1.65-1.175-2.825T12 8q-1.65 0-2.825 1.175T8 12q0 .9.363 1.663T9.35 15l-1.425 1.425Z' }),
4747
]),

src/models/Podcast.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { renderDom } from '../components'
22
import type { Channel } from '../types'
3+
import { Dotenv } from '../services'
34
import { Episode } from './Episode'
45

56
export class Podcast {
67
protected constructor(
78
public feedUrl: string,
9+
public xmlRenderUrl: string,
810
public title?: string,
911
public description?: string,
1012
public image?: string,
@@ -32,7 +34,9 @@ export class Podcast {
3234
) {}
3335

3436
public static make(feedUrl: string, channel: Channel, lang: string = 'en'): Podcast {
35-
const self = new this(feedUrl)
37+
const dotenv = Dotenv.load()
38+
const xmlRenderUrl = `${dotenv.BASE_URL}/api/renderer?url=${feedUrl}&xml=true`
39+
const self = new this(feedUrl, xmlRenderUrl)
3640

3741
self.title = channel.title
3842
self.description = channel.description

src/router.ts

+22-19
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,17 @@ export const router = createRouter()
2929
renderer: {
3030
url: `${dotenv.BASE_URL}/api/renderer`,
3131
query: {
32-
default: 'This endpoint will return HTML render from XML RSS feed, if User-Agent is not a browser, it will return XML RSS feed',
3332
url: '`string`, required, url to RSS feed (could be base64 encoded)',
34-
html: '`boolean`, default: `false`, return HTML data into JSON response',
35-
json: '`boolean`, default: `false`, return Podcast object into JSON response',
36-
xml: '`boolean`, default: `false`, return XML RSS feed',
33+
format: '`html`, `json` or `xml`, optional, default `html`, type of response, default `html` will render HTML page, `json` will give JSON response with HTML string and `xml` will give original RSS feed',
3734
},
38-
about: 'Render HTML from RSS feed',
35+
about: 'Render HTML from RSS feed. If User-Agent is not a browser, it will return XML RSS feed',
3936
},
4037
},
4138
}
4239
}))
4340
.get('/api/renderer', eventHandler(async (event) => {
4441
const query = getQuery(event)
42+
const format = query.format || 'html'
4543
const lang = event.node.req.headers['accept-language'] || 'en-US'
4644
const renderer = await Renderer.make(query, lang)
4745
const error = renderer.getError()
@@ -52,33 +50,38 @@ export const router = createRouter()
5250
}
5351
}
5452

55-
if (query?.html === 'true') {
53+
if (format === 'json') {
5654
return {
5755
url: renderer.getUrl(),
5856
data: await renderer.getRender(),
5957
date: new Date().toISOString(),
6058
}
6159
}
6260

63-
if (query.json === 'true') {
64-
return {
65-
url: renderer.getUrl(),
66-
data: renderer.getPodcast(),
67-
date: new Date().toISOString(),
68-
}
69-
}
70-
71-
if (query.xml === 'true') {
61+
if (format === 'xml' || !isBrowser(event.node.req.headers['user-agent'])) {
7262
event.node.res.setHeader('Content-Type', 'text/xml')
7363

7464
return renderer.getXml()
7565
}
7666

77-
if (!isBrowser(event.node.req.headers['user-agent'])) {
78-
event.node.res.setHeader('Content-Type', 'text/xml')
67+
return await renderer.getRender()
68+
}))
7969

80-
return renderer.getXml()
70+
.get('/api/parser', eventHandler(async (event) => {
71+
const query = getQuery(event)
72+
const lang = event.node.req.headers['accept-language'] || 'en-US'
73+
const renderer = await Renderer.make(query, lang)
74+
const error = renderer.getError()
75+
76+
if (error) {
77+
return {
78+
error,
79+
}
8180
}
8281

83-
return await renderer.getRender()
82+
return {
83+
url: renderer.getUrl(),
84+
data: renderer.getPodcast(),
85+
date: new Date().toISOString(),
86+
}
8487
}))

0 commit comments

Comments
 (0)