feedscout 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/dist/blogrolls/index.cjs +8 -3
- package/dist/blogrolls/index.d.cts +1 -1
- package/dist/blogrolls/index.d.ts +1 -1
- package/dist/blogrolls/index.js +8 -3
- package/dist/common/discover/utils.cjs +14 -0
- package/dist/common/discover/utils.js +14 -1
- package/dist/common/types.d.cts +4 -4
- package/dist/common/types.d.ts +4 -4
- package/dist/common/uris/headers/index.cjs +6 -3
- package/dist/common/uris/headers/index.js +6 -3
- package/dist/common/utils.cjs +1 -0
- package/dist/common/utils.d.cts +9 -0
- package/dist/common/utils.d.ts +9 -0
- package/dist/common/utils.js +1 -1
- package/dist/feeds/defaults.cjs +18 -0
- package/dist/feeds/defaults.js +18 -0
- package/dist/feeds/index.cjs +9 -3
- package/dist/feeds/index.d.cts +1 -1
- package/dist/feeds/index.d.ts +1 -1
- package/dist/feeds/index.js +9 -3
- package/dist/feeds/platform/handlers/behance.cjs +45 -0
- package/dist/feeds/platform/handlers/behance.js +45 -0
- package/dist/feeds/platform/handlers/blogspot.cjs +13 -4
- package/dist/feeds/platform/handlers/blogspot.js +13 -5
- package/dist/feeds/platform/handlers/dailymotion.cjs +66 -0
- package/dist/feeds/platform/handlers/dailymotion.js +66 -0
- package/dist/feeds/platform/handlers/deviantart.cjs +50 -0
- package/dist/feeds/platform/handlers/deviantart.js +50 -0
- package/dist/feeds/platform/handlers/devto.cjs +44 -0
- package/dist/feeds/platform/handlers/devto.js +44 -0
- package/dist/feeds/platform/handlers/github.cjs +9 -3
- package/dist/feeds/platform/handlers/github.js +9 -3
- package/dist/feeds/platform/handlers/githubGist.cjs +42 -0
- package/dist/feeds/platform/handlers/githubGist.js +42 -0
- package/dist/feeds/platform/handlers/gitlab.cjs +36 -2
- package/dist/feeds/platform/handlers/gitlab.js +37 -3
- package/dist/feeds/platform/handlers/kickstarter.cjs +4 -7
- package/dist/feeds/platform/handlers/kickstarter.js +4 -7
- package/dist/feeds/platform/handlers/lobsters.cjs +34 -0
- package/dist/feeds/platform/handlers/lobsters.js +34 -0
- package/dist/feeds/platform/handlers/medium.cjs +47 -0
- package/dist/feeds/platform/handlers/medium.js +47 -0
- package/dist/feeds/platform/handlers/pinterest.cjs +48 -0
- package/dist/feeds/platform/handlers/pinterest.js +48 -0
- package/dist/feeds/platform/handlers/producthunt.cjs +22 -0
- package/dist/feeds/platform/handlers/producthunt.js +22 -0
- package/dist/feeds/platform/handlers/reddit.cjs +3 -0
- package/dist/feeds/platform/handlers/reddit.js +3 -0
- package/dist/feeds/platform/handlers/soundcloud.cjs +1 -1
- package/dist/feeds/platform/handlers/soundcloud.js +2 -2
- package/dist/feeds/platform/handlers/tumblr.cjs +4 -1
- package/dist/feeds/platform/handlers/tumblr.js +4 -1
- package/dist/feeds/platform/handlers/wordpress.cjs +17 -6
- package/dist/feeds/platform/handlers/wordpress.js +17 -6
- package/dist/feeds/platform/handlers/youtube.cjs +16 -4
- package/dist/feeds/platform/handlers/youtube.js +16 -4
- package/dist/hubs/discover/index.cjs +7 -6
- package/dist/hubs/discover/index.js +5 -4
- package/dist/hubs/discover/types.d.cts +2 -1
- package/dist/hubs/discover/types.d.ts +2 -1
- package/dist/hubs/headers/index.cjs +3 -3
- package/dist/hubs/headers/index.js +3 -3
- package/dist/hubs/html/index.cjs +3 -3
- package/dist/hubs/html/index.js +3 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/utils.cjs +8 -0
- package/dist/utils.d.cts +2 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +3 -0
- package/package.json +18 -19
- package/dist/adapters.cjs +0 -6
- package/dist/adapters.d.cts +0 -2
- package/dist/adapters.d.ts +0 -2
- package/dist/adapters.js +0 -3
- package/dist/common/discover/adapters.cjs +0 -76
- package/dist/common/discover/adapters.d.cts +0 -10
- package/dist/common/discover/adapters.d.ts +0 -10
- package/dist/common/discover/adapters.js +0 -72
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isAnyOf, isHostOf, isSubdomainOf } from "../../../common/utils.js";
|
|
2
|
+
|
|
3
|
+
//#region src/feeds/platform/handlers/medium.ts
|
|
4
|
+
const hosts = ["medium.com", "www.medium.com"];
|
|
5
|
+
const excludedPaths = [
|
|
6
|
+
"search",
|
|
7
|
+
"me",
|
|
8
|
+
"new-story",
|
|
9
|
+
"plans",
|
|
10
|
+
"membership"
|
|
11
|
+
];
|
|
12
|
+
const mediumHandler = {
|
|
13
|
+
match: (url) => {
|
|
14
|
+
return isHostOf(url, hosts) || isSubdomainOf(url, "medium.com");
|
|
15
|
+
},
|
|
16
|
+
resolve: (url) => {
|
|
17
|
+
const { hostname, pathname } = new URL(url);
|
|
18
|
+
const lowerHostname = hostname.toLowerCase();
|
|
19
|
+
if (hosts.includes(lowerHostname)) {
|
|
20
|
+
const userMatch = pathname.match(/^\/@([^/]+)/);
|
|
21
|
+
if (userMatch?.[1]) return [`https://medium.com/feed/@${userMatch[1]}`];
|
|
22
|
+
const tagMatch = pathname.match(/^\/tag\/([^/]+)/);
|
|
23
|
+
if (tagMatch?.[1]) return [`https://medium.com/feed/tag/${tagMatch[1]}`];
|
|
24
|
+
const pubTagMatch = pathname.match(/^\/([^/@][^/]+)\/tagged\/([^/]+)/);
|
|
25
|
+
if (pubTagMatch?.[1] && pubTagMatch?.[2]) {
|
|
26
|
+
const publication = pubTagMatch[1];
|
|
27
|
+
const tag = pubTagMatch[2];
|
|
28
|
+
if (!isAnyOf(publication, excludedPaths)) return [`https://medium.com/feed/${publication}/tagged/${tag}`];
|
|
29
|
+
}
|
|
30
|
+
const pubMatch = pathname.match(/^\/([^/@][^/]+)/);
|
|
31
|
+
if (pubMatch?.[1]) {
|
|
32
|
+
const publication = pubMatch[1];
|
|
33
|
+
if (!isAnyOf(publication, excludedPaths)) return [`https://medium.com/feed/${publication}`];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (lowerHostname.endsWith(".medium.com") && lowerHostname !== "medium.com" && lowerHostname !== "www.medium.com") {
|
|
37
|
+
const subdomain = lowerHostname.replace(".medium.com", "");
|
|
38
|
+
const tagMatch = pathname.match(/^\/tagged\/([^/]+)/);
|
|
39
|
+
if (tagMatch?.[1]) return [`https://medium.com/feed/${subdomain}/tagged/${tagMatch[1]}`];
|
|
40
|
+
return [`https://medium.com/feed/${subdomain}`];
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { mediumHandler };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const require_utils = require('../../../common/utils.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/feeds/platform/handlers/pinterest.ts
|
|
4
|
+
const hosts = [
|
|
5
|
+
"pinterest.com",
|
|
6
|
+
"www.pinterest.com",
|
|
7
|
+
"pin.it"
|
|
8
|
+
];
|
|
9
|
+
const excludedPaths = [
|
|
10
|
+
"_",
|
|
11
|
+
"about",
|
|
12
|
+
"business",
|
|
13
|
+
"convert",
|
|
14
|
+
"explore",
|
|
15
|
+
"ideas",
|
|
16
|
+
"login",
|
|
17
|
+
"news_hub",
|
|
18
|
+
"password",
|
|
19
|
+
"pin",
|
|
20
|
+
"privacy",
|
|
21
|
+
"resource",
|
|
22
|
+
"search",
|
|
23
|
+
"settings",
|
|
24
|
+
"terms",
|
|
25
|
+
"today",
|
|
26
|
+
"topics"
|
|
27
|
+
];
|
|
28
|
+
const pinterestHandler = {
|
|
29
|
+
match: (url) => {
|
|
30
|
+
return require_utils.isHostOf(url, hosts);
|
|
31
|
+
},
|
|
32
|
+
resolve: (url) => {
|
|
33
|
+
const { pathname } = new URL(url);
|
|
34
|
+
const pathSegments = pathname.split("/").filter(Boolean);
|
|
35
|
+
if (pathSegments.length === 0) return [];
|
|
36
|
+
const username = pathSegments[0];
|
|
37
|
+
if (require_utils.isAnyOf(username, excludedPaths)) return [];
|
|
38
|
+
if (pathSegments.length >= 2) {
|
|
39
|
+
const boardname = pathSegments[1];
|
|
40
|
+
if (boardname.startsWith("_") || boardname === "pins" || boardname === "boards") return [`https://www.pinterest.com/${username}/feed.rss`];
|
|
41
|
+
return [`https://www.pinterest.com/${username}/${boardname}.rss`];
|
|
42
|
+
}
|
|
43
|
+
return [`https://www.pinterest.com/${username}/feed.rss`];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
exports.pinterestHandler = pinterestHandler;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
|
+
|
|
3
|
+
//#region src/feeds/platform/handlers/pinterest.ts
|
|
4
|
+
const hosts = [
|
|
5
|
+
"pinterest.com",
|
|
6
|
+
"www.pinterest.com",
|
|
7
|
+
"pin.it"
|
|
8
|
+
];
|
|
9
|
+
const excludedPaths = [
|
|
10
|
+
"_",
|
|
11
|
+
"about",
|
|
12
|
+
"business",
|
|
13
|
+
"convert",
|
|
14
|
+
"explore",
|
|
15
|
+
"ideas",
|
|
16
|
+
"login",
|
|
17
|
+
"news_hub",
|
|
18
|
+
"password",
|
|
19
|
+
"pin",
|
|
20
|
+
"privacy",
|
|
21
|
+
"resource",
|
|
22
|
+
"search",
|
|
23
|
+
"settings",
|
|
24
|
+
"terms",
|
|
25
|
+
"today",
|
|
26
|
+
"topics"
|
|
27
|
+
];
|
|
28
|
+
const pinterestHandler = {
|
|
29
|
+
match: (url) => {
|
|
30
|
+
return isHostOf(url, hosts);
|
|
31
|
+
},
|
|
32
|
+
resolve: (url) => {
|
|
33
|
+
const { pathname } = new URL(url);
|
|
34
|
+
const pathSegments = pathname.split("/").filter(Boolean);
|
|
35
|
+
if (pathSegments.length === 0) return [];
|
|
36
|
+
const username = pathSegments[0];
|
|
37
|
+
if (isAnyOf(username, excludedPaths)) return [];
|
|
38
|
+
if (pathSegments.length >= 2) {
|
|
39
|
+
const boardname = pathSegments[1];
|
|
40
|
+
if (boardname.startsWith("_") || boardname === "pins" || boardname === "boards") return [`https://www.pinterest.com/${username}/feed.rss`];
|
|
41
|
+
return [`https://www.pinterest.com/${username}/${boardname}.rss`];
|
|
42
|
+
}
|
|
43
|
+
return [`https://www.pinterest.com/${username}/feed.rss`];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
export { pinterestHandler };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const require_utils = require('../../../common/utils.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/feeds/platform/handlers/producthunt.ts
|
|
4
|
+
const hosts = ["producthunt.com", "www.producthunt.com"];
|
|
5
|
+
const topicPathRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
|
|
6
|
+
const categoryPathRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
|
|
7
|
+
const producthuntHandler = {
|
|
8
|
+
match: (url) => {
|
|
9
|
+
return require_utils.isHostOf(url, hosts);
|
|
10
|
+
},
|
|
11
|
+
resolve: (url) => {
|
|
12
|
+
const { pathname } = new URL(url);
|
|
13
|
+
const topicMatch = pathname.match(topicPathRegex);
|
|
14
|
+
if (topicMatch?.[1]) return [`https://www.producthunt.com/feed?topic=${topicMatch[1]}`];
|
|
15
|
+
const categoryMatch = pathname.match(categoryPathRegex);
|
|
16
|
+
if (categoryMatch?.[1]) return [`https://www.producthunt.com/feed?category=${categoryMatch[1]}`];
|
|
17
|
+
return ["https://www.producthunt.com/feed"];
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
exports.producthuntHandler = producthuntHandler;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isHostOf } from "../../../common/utils.js";
|
|
2
|
+
|
|
3
|
+
//#region src/feeds/platform/handlers/producthunt.ts
|
|
4
|
+
const hosts = ["producthunt.com", "www.producthunt.com"];
|
|
5
|
+
const topicPathRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
|
|
6
|
+
const categoryPathRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
|
|
7
|
+
const producthuntHandler = {
|
|
8
|
+
match: (url) => {
|
|
9
|
+
return isHostOf(url, hosts);
|
|
10
|
+
},
|
|
11
|
+
resolve: (url) => {
|
|
12
|
+
const { pathname } = new URL(url);
|
|
13
|
+
const topicMatch = pathname.match(topicPathRegex);
|
|
14
|
+
if (topicMatch?.[1]) return [`https://www.producthunt.com/feed?topic=${topicMatch[1]}`];
|
|
15
|
+
const categoryMatch = pathname.match(categoryPathRegex);
|
|
16
|
+
if (categoryMatch?.[1]) return [`https://www.producthunt.com/feed?category=${categoryMatch[1]}`];
|
|
17
|
+
return ["https://www.producthunt.com/feed"];
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { producthuntHandler };
|
|
@@ -20,6 +20,7 @@ const redditHandler = {
|
|
|
20
20
|
},
|
|
21
21
|
resolve: (url) => {
|
|
22
22
|
const { pathname } = new URL(url);
|
|
23
|
+
if (pathname.split("/").filter(Boolean).length === 0) return ["https://www.reddit.com/.rss"];
|
|
23
24
|
const commentsMatch = pathname.match(/^\/r\/([^/]+)\/comments\/([^/]+)/);
|
|
24
25
|
if (commentsMatch?.[1] && commentsMatch?.[2]) return [`https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`];
|
|
25
26
|
const subredditMatch = pathname.match(/^\/r\/([^/]+)(?:\/([^/]+))?/);
|
|
@@ -32,6 +33,8 @@ const redditHandler = {
|
|
|
32
33
|
uris.push(`https://www.reddit.com/r/${subreddit}/comments/.rss`);
|
|
33
34
|
return uris;
|
|
34
35
|
}
|
|
36
|
+
const multiredditMatch = pathname.match(/^\/user\/([^/]+)\/m\/([^/]+)/);
|
|
37
|
+
if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [`https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`];
|
|
35
38
|
const userMatch = pathname.match(/^\/(u|user)\/([^/]+)/);
|
|
36
39
|
if (userMatch?.[2]) return [`https://www.reddit.com/user/${userMatch[2]}/.rss`];
|
|
37
40
|
const domainMatch = pathname.match(/^\/domain\/([^/]+)/);
|
|
@@ -20,6 +20,7 @@ const redditHandler = {
|
|
|
20
20
|
},
|
|
21
21
|
resolve: (url) => {
|
|
22
22
|
const { pathname } = new URL(url);
|
|
23
|
+
if (pathname.split("/").filter(Boolean).length === 0) return ["https://www.reddit.com/.rss"];
|
|
23
24
|
const commentsMatch = pathname.match(/^\/r\/([^/]+)\/comments\/([^/]+)/);
|
|
24
25
|
if (commentsMatch?.[1] && commentsMatch?.[2]) return [`https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`];
|
|
25
26
|
const subredditMatch = pathname.match(/^\/r\/([^/]+)(?:\/([^/]+))?/);
|
|
@@ -32,6 +33,8 @@ const redditHandler = {
|
|
|
32
33
|
uris.push(`https://www.reddit.com/r/${subreddit}/comments/.rss`);
|
|
33
34
|
return uris;
|
|
34
35
|
}
|
|
36
|
+
const multiredditMatch = pathname.match(/^\/user\/([^/]+)\/m\/([^/]+)/);
|
|
37
|
+
if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [`https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`];
|
|
35
38
|
const userMatch = pathname.match(/^\/(u|user)\/([^/]+)/);
|
|
36
39
|
if (userMatch?.[2]) return [`https://www.reddit.com/user/${userMatch[2]}/.rss`];
|
|
37
40
|
const domainMatch = pathname.match(/^\/domain\/([^/]+)/);
|
|
@@ -23,7 +23,7 @@ const soundcloudHandler = {
|
|
|
23
23
|
if (!require_utils.isHostOf(url, hosts)) return false;
|
|
24
24
|
const { pathname } = new URL(url);
|
|
25
25
|
const pathSegments = pathname.split("/").filter(Boolean);
|
|
26
|
-
return pathSegments.length >= 1 && !
|
|
26
|
+
return pathSegments.length >= 1 && !require_utils.isAnyOf(pathSegments[0], excludedPaths);
|
|
27
27
|
},
|
|
28
28
|
resolve: (_url, content) => {
|
|
29
29
|
if (!content) return [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isHostOf } from "../../../common/utils.js";
|
|
1
|
+
import { isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
|
|
3
3
|
//#region src/feeds/platform/handlers/soundcloud.ts
|
|
4
4
|
const hosts = [
|
|
@@ -23,7 +23,7 @@ const soundcloudHandler = {
|
|
|
23
23
|
if (!isHostOf(url, hosts)) return false;
|
|
24
24
|
const { pathname } = new URL(url);
|
|
25
25
|
const pathSegments = pathname.split("/").filter(Boolean);
|
|
26
|
-
return pathSegments.length >= 1 && !
|
|
26
|
+
return pathSegments.length >= 1 && !isAnyOf(pathSegments[0], excludedPaths);
|
|
27
27
|
},
|
|
28
28
|
resolve: (_url, content) => {
|
|
29
29
|
if (!content) return [];
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const require_utils = require('../../../common/utils.cjs');
|
|
2
2
|
|
|
3
3
|
//#region src/feeds/platform/handlers/tumblr.ts
|
|
4
|
+
const tagPathRegex = /^\/tagged\/([^/]+)/;
|
|
4
5
|
const tumblrHandler = {
|
|
5
6
|
match: (url) => {
|
|
6
7
|
return require_utils.isSubdomainOf(url, "tumblr.com");
|
|
7
8
|
},
|
|
8
9
|
resolve: (url) => {
|
|
9
|
-
const { origin } = new URL(url);
|
|
10
|
+
const { origin, pathname } = new URL(url);
|
|
11
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
12
|
+
if (tagMatch?.[1]) return [`${origin}/tagged/${tagMatch[1]}/rss`];
|
|
10
13
|
return [`${origin}/rss`];
|
|
11
14
|
}
|
|
12
15
|
};
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { isSubdomainOf } from "../../../common/utils.js";
|
|
2
2
|
|
|
3
3
|
//#region src/feeds/platform/handlers/tumblr.ts
|
|
4
|
+
const tagPathRegex = /^\/tagged\/([^/]+)/;
|
|
4
5
|
const tumblrHandler = {
|
|
5
6
|
match: (url) => {
|
|
6
7
|
return isSubdomainOf(url, "tumblr.com");
|
|
7
8
|
},
|
|
8
9
|
resolve: (url) => {
|
|
9
|
-
const { origin } = new URL(url);
|
|
10
|
+
const { origin, pathname } = new URL(url);
|
|
11
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
12
|
+
if (tagMatch?.[1]) return [`${origin}/tagged/${tagMatch[1]}/rss`];
|
|
10
13
|
return [`${origin}/rss`];
|
|
11
14
|
}
|
|
12
15
|
};
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
const require_utils = require('../../../common/utils.cjs');
|
|
2
2
|
|
|
3
3
|
//#region src/feeds/platform/handlers/wordpress.ts
|
|
4
|
+
const categoryPathRegex = /^\/category\/([^/]+)/;
|
|
5
|
+
const tagPathRegex = /^\/tag\/([^/]+)/;
|
|
6
|
+
const authorPathRegex = /^\/author\/([^/]+)/;
|
|
4
7
|
const wordpressHandler = {
|
|
5
8
|
match: (url) => {
|
|
6
9
|
return require_utils.isSubdomainOf(url, "wordpress.com");
|
|
7
10
|
},
|
|
8
11
|
resolve: (url) => {
|
|
9
|
-
const { origin } = new URL(url);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
];
|
|
12
|
+
const { origin, pathname } = new URL(url);
|
|
13
|
+
const uris = [];
|
|
14
|
+
const categoryMatch = pathname.match(categoryPathRegex);
|
|
15
|
+
if (categoryMatch?.[1]) uris.push(`${origin}/category/${categoryMatch[1]}/feed/`);
|
|
16
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
17
|
+
if (tagMatch?.[1]) uris.push(`${origin}/tag/${tagMatch[1]}/feed/`);
|
|
18
|
+
const authorMatch = pathname.match(authorPathRegex);
|
|
19
|
+
if (authorMatch?.[1]) uris.push(`${origin}/author/${authorMatch[1]}/feed/`);
|
|
20
|
+
uris.push(`${origin}/feed/`);
|
|
21
|
+
uris.push(`${origin}/feed/rss2/`);
|
|
22
|
+
uris.push(`${origin}/feed/rdf/`);
|
|
23
|
+
uris.push(`${origin}/feed/atom/`);
|
|
24
|
+
uris.push(`${origin}/comments/feed/`);
|
|
25
|
+
return uris;
|
|
15
26
|
}
|
|
16
27
|
};
|
|
17
28
|
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { isSubdomainOf } from "../../../common/utils.js";
|
|
2
2
|
|
|
3
3
|
//#region src/feeds/platform/handlers/wordpress.ts
|
|
4
|
+
const categoryPathRegex = /^\/category\/([^/]+)/;
|
|
5
|
+
const tagPathRegex = /^\/tag\/([^/]+)/;
|
|
6
|
+
const authorPathRegex = /^\/author\/([^/]+)/;
|
|
4
7
|
const wordpressHandler = {
|
|
5
8
|
match: (url) => {
|
|
6
9
|
return isSubdomainOf(url, "wordpress.com");
|
|
7
10
|
},
|
|
8
11
|
resolve: (url) => {
|
|
9
|
-
const { origin } = new URL(url);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
];
|
|
12
|
+
const { origin, pathname } = new URL(url);
|
|
13
|
+
const uris = [];
|
|
14
|
+
const categoryMatch = pathname.match(categoryPathRegex);
|
|
15
|
+
if (categoryMatch?.[1]) uris.push(`${origin}/category/${categoryMatch[1]}/feed/`);
|
|
16
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
17
|
+
if (tagMatch?.[1]) uris.push(`${origin}/tag/${tagMatch[1]}/feed/`);
|
|
18
|
+
const authorMatch = pathname.match(authorPathRegex);
|
|
19
|
+
if (authorMatch?.[1]) uris.push(`${origin}/author/${authorMatch[1]}/feed/`);
|
|
20
|
+
uris.push(`${origin}/feed/`);
|
|
21
|
+
uris.push(`${origin}/feed/rss2/`);
|
|
22
|
+
uris.push(`${origin}/feed/rdf/`);
|
|
23
|
+
uris.push(`${origin}/feed/atom/`);
|
|
24
|
+
uris.push(`${origin}/comments/feed/`);
|
|
25
|
+
return uris;
|
|
15
26
|
}
|
|
16
27
|
};
|
|
17
28
|
|
|
@@ -4,14 +4,24 @@ const require_utils = require('../../../common/utils.cjs');
|
|
|
4
4
|
const hosts = [
|
|
5
5
|
"youtube.com",
|
|
6
6
|
"www.youtube.com",
|
|
7
|
-
"m.youtube.com"
|
|
7
|
+
"m.youtube.com",
|
|
8
|
+
"youtu.be",
|
|
9
|
+
"www.youtu.be"
|
|
8
10
|
];
|
|
11
|
+
const channelIdRegex = /"channelId":"(UC[a-zA-Z0-9_-]+)"/;
|
|
12
|
+
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
13
|
+
const handlePathRegex = /^\/@([^/]+)/;
|
|
14
|
+
const userPathRegex = /^\/user\/([^/]+)/;
|
|
15
|
+
const customPathRegex = /^\/c\/([^/]+)/;
|
|
9
16
|
const extractChannelIdFromContent = (content) => {
|
|
10
|
-
return content.match(
|
|
17
|
+
return content.match(channelIdRegex)?.[1];
|
|
11
18
|
};
|
|
12
19
|
const getVideosOnlyPlaylistId = (channelId) => {
|
|
13
20
|
return channelId.replace(/^UC/, "UULF");
|
|
14
21
|
};
|
|
22
|
+
const getShortsOnlyPlaylistId = (channelId) => {
|
|
23
|
+
return channelId.replace(/^UC/, "UUSH");
|
|
24
|
+
};
|
|
15
25
|
const youtubeHandler = {
|
|
16
26
|
match: (url) => {
|
|
17
27
|
return require_utils.isHostOf(url, hosts);
|
|
@@ -19,20 +29,22 @@ const youtubeHandler = {
|
|
|
19
29
|
resolve: (url, content) => {
|
|
20
30
|
const parsedUrl = new URL(url);
|
|
21
31
|
const uris = [];
|
|
22
|
-
const channelMatch = parsedUrl.pathname.match(
|
|
32
|
+
const channelMatch = parsedUrl.pathname.match(channelPathRegex);
|
|
23
33
|
if (channelMatch?.[1]) {
|
|
24
34
|
const channelId = channelMatch[1];
|
|
25
35
|
uris.push(`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`);
|
|
26
36
|
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getVideosOnlyPlaylistId(channelId)}`);
|
|
37
|
+
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getShortsOnlyPlaylistId(channelId)}`);
|
|
27
38
|
}
|
|
28
39
|
const playlistId = parsedUrl.searchParams.get("list");
|
|
29
40
|
if (playlistId) uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${playlistId}`);
|
|
30
41
|
if (uris.length === 0 && content) {
|
|
31
|
-
if (parsedUrl.pathname.match(
|
|
42
|
+
if (parsedUrl.searchParams.has("v") || parsedUrl.hostname.includes("youtu.be") && parsedUrl.pathname.length > 1 || parsedUrl.pathname.match(handlePathRegex) || parsedUrl.pathname.match(userPathRegex) || parsedUrl.pathname.match(customPathRegex)) {
|
|
32
43
|
const channelId = extractChannelIdFromContent(content);
|
|
33
44
|
if (channelId) {
|
|
34
45
|
uris.push(`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`);
|
|
35
46
|
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getVideosOnlyPlaylistId(channelId)}`);
|
|
47
|
+
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getShortsOnlyPlaylistId(channelId)}`);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
}
|
|
@@ -4,14 +4,24 @@ import { isHostOf } from "../../../common/utils.js";
|
|
|
4
4
|
const hosts = [
|
|
5
5
|
"youtube.com",
|
|
6
6
|
"www.youtube.com",
|
|
7
|
-
"m.youtube.com"
|
|
7
|
+
"m.youtube.com",
|
|
8
|
+
"youtu.be",
|
|
9
|
+
"www.youtu.be"
|
|
8
10
|
];
|
|
11
|
+
const channelIdRegex = /"channelId":"(UC[a-zA-Z0-9_-]+)"/;
|
|
12
|
+
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
13
|
+
const handlePathRegex = /^\/@([^/]+)/;
|
|
14
|
+
const userPathRegex = /^\/user\/([^/]+)/;
|
|
15
|
+
const customPathRegex = /^\/c\/([^/]+)/;
|
|
9
16
|
const extractChannelIdFromContent = (content) => {
|
|
10
|
-
return content.match(
|
|
17
|
+
return content.match(channelIdRegex)?.[1];
|
|
11
18
|
};
|
|
12
19
|
const getVideosOnlyPlaylistId = (channelId) => {
|
|
13
20
|
return channelId.replace(/^UC/, "UULF");
|
|
14
21
|
};
|
|
22
|
+
const getShortsOnlyPlaylistId = (channelId) => {
|
|
23
|
+
return channelId.replace(/^UC/, "UUSH");
|
|
24
|
+
};
|
|
15
25
|
const youtubeHandler = {
|
|
16
26
|
match: (url) => {
|
|
17
27
|
return isHostOf(url, hosts);
|
|
@@ -19,20 +29,22 @@ const youtubeHandler = {
|
|
|
19
29
|
resolve: (url, content) => {
|
|
20
30
|
const parsedUrl = new URL(url);
|
|
21
31
|
const uris = [];
|
|
22
|
-
const channelMatch = parsedUrl.pathname.match(
|
|
32
|
+
const channelMatch = parsedUrl.pathname.match(channelPathRegex);
|
|
23
33
|
if (channelMatch?.[1]) {
|
|
24
34
|
const channelId = channelMatch[1];
|
|
25
35
|
uris.push(`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`);
|
|
26
36
|
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getVideosOnlyPlaylistId(channelId)}`);
|
|
37
|
+
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getShortsOnlyPlaylistId(channelId)}`);
|
|
27
38
|
}
|
|
28
39
|
const playlistId = parsedUrl.searchParams.get("list");
|
|
29
40
|
if (playlistId) uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${playlistId}`);
|
|
30
41
|
if (uris.length === 0 && content) {
|
|
31
|
-
if (parsedUrl.pathname.match(
|
|
42
|
+
if (parsedUrl.searchParams.has("v") || parsedUrl.hostname.includes("youtu.be") && parsedUrl.pathname.length > 1 || parsedUrl.pathname.match(handlePathRegex) || parsedUrl.pathname.match(userPathRegex) || parsedUrl.pathname.match(customPathRegex)) {
|
|
32
43
|
const channelId = extractChannelIdFromContent(content);
|
|
33
44
|
if (channelId) {
|
|
34
45
|
uris.push(`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`);
|
|
35
46
|
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getVideosOnlyPlaylistId(channelId)}`);
|
|
47
|
+
uris.push(`https://www.youtube.com/feeds/videos.xml?playlist_id=${getShortsOnlyPlaylistId(channelId)}`);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_utils = require('../../common/utils.cjs');
|
|
2
|
+
const require_utils$1 = require('../../common/discover/utils.cjs');
|
|
2
3
|
const require_index = require('../feed/index.cjs');
|
|
3
4
|
const require_index$1 = require('../headers/index.cjs');
|
|
4
5
|
const require_index$2 = require('../html/index.cjs');
|
|
5
|
-
const require_utils = require('./utils.cjs');
|
|
6
|
+
const require_utils$2 = require('./utils.cjs');
|
|
6
7
|
|
|
7
8
|
//#region src/hubs/discover/index.ts
|
|
8
9
|
const discoverHubs = async (input, options = {}) => {
|
|
@@ -10,11 +11,11 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
10
11
|
"headers",
|
|
11
12
|
"feed",
|
|
12
13
|
"html"
|
|
13
|
-
], fetchFn =
|
|
14
|
-
const normalizedInput = await require_utils.normalizeInput(input, fetchFn);
|
|
14
|
+
], fetchFn = require_utils$1.defaultFetchFn, normalizeUrlFn = require_utils.normalizeUrl } = options;
|
|
15
|
+
const normalizedInput = await require_utils$2.normalizeInput(input, fetchFn);
|
|
15
16
|
const results = [];
|
|
16
17
|
if (methods.includes("headers") && normalizedInput.headers) {
|
|
17
|
-
const headerHubs = require_index$1.discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url);
|
|
18
|
+
const headerHubs = require_index$1.discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url, normalizeUrlFn);
|
|
18
19
|
results.push(...headerHubs);
|
|
19
20
|
}
|
|
20
21
|
if (methods.includes("feed") && normalizedInput.content) {
|
|
@@ -22,7 +23,7 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
22
23
|
results.push(...feedHubs);
|
|
23
24
|
}
|
|
24
25
|
if (methods.includes("html") && normalizedInput.content) {
|
|
25
|
-
const htmlHubs = require_index$2.discoverHubsFromHtml(normalizedInput.content, normalizedInput.url);
|
|
26
|
+
const htmlHubs = require_index$2.discoverHubsFromHtml(normalizedInput.content, normalizedInput.url, normalizeUrlFn);
|
|
26
27
|
results.push(...htmlHubs);
|
|
27
28
|
}
|
|
28
29
|
return results;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeUrl } from "../../common/utils.js";
|
|
2
|
+
import { defaultFetchFn } from "../../common/discover/utils.js";
|
|
2
3
|
import { discoverHubsFromFeed } from "../feed/index.js";
|
|
3
4
|
import { discoverHubsFromHeaders } from "../headers/index.js";
|
|
4
5
|
import { discoverHubsFromHtml } from "../html/index.js";
|
|
@@ -10,11 +11,11 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
10
11
|
"headers",
|
|
11
12
|
"feed",
|
|
12
13
|
"html"
|
|
13
|
-
], fetchFn =
|
|
14
|
+
], fetchFn = defaultFetchFn, normalizeUrlFn = normalizeUrl } = options;
|
|
14
15
|
const normalizedInput = await normalizeInput(input, fetchFn);
|
|
15
16
|
const results = [];
|
|
16
17
|
if (methods.includes("headers") && normalizedInput.headers) {
|
|
17
|
-
const headerHubs = discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url);
|
|
18
|
+
const headerHubs = discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url, normalizeUrlFn);
|
|
18
19
|
results.push(...headerHubs);
|
|
19
20
|
}
|
|
20
21
|
if (methods.includes("feed") && normalizedInput.content) {
|
|
@@ -22,7 +23,7 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
22
23
|
results.push(...feedHubs);
|
|
23
24
|
}
|
|
24
25
|
if (methods.includes("html") && normalizedInput.content) {
|
|
25
|
-
const htmlHubs = discoverHubsFromHtml(normalizedInput.content, normalizedInput.url);
|
|
26
|
+
const htmlHubs = discoverHubsFromHtml(normalizedInput.content, normalizedInput.url, normalizeUrlFn);
|
|
26
27
|
results.push(...htmlHubs);
|
|
27
28
|
}
|
|
28
29
|
return results;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DiscoverFetchFn } from "../../common/types.cjs";
|
|
1
|
+
import { DiscoverFetchFn, DiscoverNormalizeUrlFn } from "../../common/types.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/hubs/discover/types.d.ts
|
|
4
4
|
type HubResult = {
|
|
@@ -9,6 +9,7 @@ type DiscoverHubsMethodsConfig = Array<'headers' | 'html' | 'feed'>;
|
|
|
9
9
|
type DiscoverHubsOptions = {
|
|
10
10
|
methods?: DiscoverHubsMethodsConfig;
|
|
11
11
|
fetchFn?: DiscoverFetchFn;
|
|
12
|
+
normalizeUrlFn?: DiscoverNormalizeUrlFn;
|
|
12
13
|
};
|
|
13
14
|
//#endregion
|
|
14
15
|
export { DiscoverHubsMethodsConfig, DiscoverHubsOptions, HubResult };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DiscoverFetchFn } from "../../common/types.js";
|
|
1
|
+
import { DiscoverFetchFn, DiscoverNormalizeUrlFn } from "../../common/types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/hubs/discover/types.d.ts
|
|
4
4
|
type HubResult = {
|
|
@@ -9,6 +9,7 @@ type DiscoverHubsMethodsConfig = Array<'headers' | 'html' | 'feed'>;
|
|
|
9
9
|
type DiscoverHubsOptions = {
|
|
10
10
|
methods?: DiscoverHubsMethodsConfig;
|
|
11
11
|
fetchFn?: DiscoverFetchFn;
|
|
12
|
+
normalizeUrlFn?: DiscoverNormalizeUrlFn;
|
|
12
13
|
};
|
|
13
14
|
//#endregion
|
|
14
15
|
export { DiscoverHubsMethodsConfig, DiscoverHubsOptions, HubResult };
|
|
@@ -4,13 +4,13 @@ const require_index = require('../../common/uris/headers/index.cjs');
|
|
|
4
4
|
//#region src/hubs/headers/index.ts
|
|
5
5
|
const hubSelector = [{ rel: "hub" }];
|
|
6
6
|
const selfSelector = [{ rel: "self" }];
|
|
7
|
-
const discoverHubsFromHeaders = (headers, baseUrl) => {
|
|
7
|
+
const discoverHubsFromHeaders = (headers, baseUrl, normalizeUrlFn = require_utils.normalizeUrl) => {
|
|
8
8
|
const hubUris = require_index.discoverUrisFromHeaders(headers, { linkSelectors: hubSelector });
|
|
9
9
|
if (hubUris.length === 0) return [];
|
|
10
10
|
const selfUris = require_index.discoverUrisFromHeaders(headers, { linkSelectors: selfSelector });
|
|
11
|
-
const topic = selfUris[0] ?
|
|
11
|
+
const topic = selfUris[0] ? normalizeUrlFn(selfUris[0], baseUrl) : baseUrl;
|
|
12
12
|
return hubUris.map((hub) => ({
|
|
13
|
-
hub:
|
|
13
|
+
hub: normalizeUrlFn(hub, baseUrl),
|
|
14
14
|
topic
|
|
15
15
|
}));
|
|
16
16
|
};
|
|
@@ -4,13 +4,13 @@ import { discoverUrisFromHeaders } from "../../common/uris/headers/index.js";
|
|
|
4
4
|
//#region src/hubs/headers/index.ts
|
|
5
5
|
const hubSelector = [{ rel: "hub" }];
|
|
6
6
|
const selfSelector = [{ rel: "self" }];
|
|
7
|
-
const discoverHubsFromHeaders = (headers, baseUrl) => {
|
|
7
|
+
const discoverHubsFromHeaders = (headers, baseUrl, normalizeUrlFn = normalizeUrl) => {
|
|
8
8
|
const hubUris = discoverUrisFromHeaders(headers, { linkSelectors: hubSelector });
|
|
9
9
|
if (hubUris.length === 0) return [];
|
|
10
10
|
const selfUris = discoverUrisFromHeaders(headers, { linkSelectors: selfSelector });
|
|
11
|
-
const topic = selfUris[0] ?
|
|
11
|
+
const topic = selfUris[0] ? normalizeUrlFn(selfUris[0], baseUrl) : baseUrl;
|
|
12
12
|
return hubUris.map((hub) => ({
|
|
13
|
-
hub:
|
|
13
|
+
hub: normalizeUrlFn(hub, baseUrl),
|
|
14
14
|
topic
|
|
15
15
|
}));
|
|
16
16
|
};
|
package/dist/hubs/html/index.cjs
CHANGED
|
@@ -9,7 +9,7 @@ const htmlOptions = {
|
|
|
9
9
|
anchorIgnoredUris: [],
|
|
10
10
|
anchorLabels: []
|
|
11
11
|
};
|
|
12
|
-
const discoverHubsFromHtml = (content, baseUrl) => {
|
|
12
|
+
const discoverHubsFromHtml = (content, baseUrl, normalizeUrlFn = require_utils.normalizeUrl) => {
|
|
13
13
|
const hubUris = require_index.discoverUrisFromHtml(content, {
|
|
14
14
|
...htmlOptions,
|
|
15
15
|
linkSelectors: hubSelector
|
|
@@ -19,9 +19,9 @@ const discoverHubsFromHtml = (content, baseUrl) => {
|
|
|
19
19
|
...htmlOptions,
|
|
20
20
|
linkSelectors: selfSelector
|
|
21
21
|
});
|
|
22
|
-
const topic = selfUris[0] ?
|
|
22
|
+
const topic = selfUris[0] ? normalizeUrlFn(selfUris[0], baseUrl) : baseUrl;
|
|
23
23
|
return hubUris.map((hub) => ({
|
|
24
|
-
hub:
|
|
24
|
+
hub: normalizeUrlFn(hub, baseUrl),
|
|
25
25
|
topic
|
|
26
26
|
}));
|
|
27
27
|
};
|
package/dist/hubs/html/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const htmlOptions = {
|
|
|
9
9
|
anchorIgnoredUris: [],
|
|
10
10
|
anchorLabels: []
|
|
11
11
|
};
|
|
12
|
-
const discoverHubsFromHtml = (content, baseUrl) => {
|
|
12
|
+
const discoverHubsFromHtml = (content, baseUrl, normalizeUrlFn = normalizeUrl) => {
|
|
13
13
|
const hubUris = discoverUrisFromHtml(content, {
|
|
14
14
|
...htmlOptions,
|
|
15
15
|
linkSelectors: hubSelector
|
|
@@ -19,9 +19,9 @@ const discoverHubsFromHtml = (content, baseUrl) => {
|
|
|
19
19
|
...htmlOptions,
|
|
20
20
|
linkSelectors: selfSelector
|
|
21
21
|
});
|
|
22
|
-
const topic = selfUris[0] ?
|
|
22
|
+
const topic = selfUris[0] ? normalizeUrlFn(selfUris[0], baseUrl) : baseUrl;
|
|
23
23
|
return hubUris.map((hub) => ({
|
|
24
|
-
hub:
|
|
24
|
+
hub: normalizeUrlFn(hub, baseUrl),
|
|
25
25
|
topic
|
|
26
26
|
}));
|
|
27
27
|
};
|