feedscout 1.6.2 → 1.7.1
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 +14 -0
- package/dist/blogrolls/extractors.cjs +1 -1
- package/dist/blogrolls/extractors.js +1 -1
- package/dist/blogrolls/index.cjs +3 -2
- package/dist/blogrolls/index.js +5 -4
- package/dist/common/discover/index.cjs +21 -9
- package/dist/common/discover/index.js +21 -9
- package/dist/common/discover/utils.cjs +44 -18
- package/dist/common/discover/utils.d.cts +8 -0
- package/dist/common/discover/utils.d.ts +8 -0
- package/dist/common/discover/utils.js +43 -19
- package/dist/common/locales.cjs +3 -1
- package/dist/common/locales.js +3 -1
- package/dist/common/types.d.cts +6 -4
- package/dist/common/types.d.ts +6 -4
- package/dist/common/uris/guess/utils.cjs +3 -2
- package/dist/common/uris/guess/utils.js +3 -2
- package/dist/common/uris/headers/index.cjs +2 -1
- package/dist/common/uris/headers/index.js +2 -1
- package/dist/common/utils.cjs +15 -4
- package/dist/common/utils.js +14 -4
- package/dist/favicons/extractors.cjs +16 -4
- package/dist/favicons/extractors.js +16 -4
- package/dist/favicons/index.cjs +3 -2
- package/dist/favicons/index.js +5 -4
- package/dist/favicons/platform/handlers/bluesky.cjs +3 -3
- package/dist/favicons/platform/handlers/bluesky.js +3 -3
- package/dist/favicons/platform/handlers/mastodon.cjs +7 -5
- package/dist/favicons/platform/handlers/mastodon.js +7 -5
- package/dist/favicons/platform/handlers/reddit.cjs +5 -6
- package/dist/favicons/platform/handlers/reddit.js +5 -6
- package/dist/favicons/utils.cjs +10 -0
- package/dist/favicons/utils.js +9 -0
- package/dist/feeds/defaults.cjs +2 -0
- package/dist/feeds/defaults.js +2 -0
- package/dist/feeds/extractors.cjs +10 -8
- package/dist/feeds/extractors.js +10 -8
- package/dist/feeds/index.cjs +2 -2
- package/dist/feeds/index.js +3 -3
- package/dist/feeds/platform/handlers/blogspot.cjs +2 -1
- package/dist/feeds/platform/handlers/blogspot.js +2 -1
- package/dist/feeds/platform/handlers/bluesky.cjs +2 -1
- package/dist/feeds/platform/handlers/bluesky.js +2 -1
- package/dist/feeds/platform/handlers/csdn.cjs +2 -1
- package/dist/feeds/platform/handlers/csdn.js +2 -1
- package/dist/feeds/platform/handlers/deviantart.cjs +8 -4
- package/dist/feeds/platform/handlers/deviantart.js +8 -4
- package/dist/feeds/platform/handlers/douban.cjs +4 -2
- package/dist/feeds/platform/handlers/douban.js +4 -2
- package/dist/feeds/platform/handlers/github.cjs +12 -6
- package/dist/feeds/platform/handlers/github.js +12 -6
- package/dist/feeds/platform/handlers/githubGist.cjs +6 -3
- package/dist/feeds/platform/handlers/githubGist.js +6 -3
- package/dist/feeds/platform/handlers/gitlab.cjs +15 -2
- package/dist/feeds/platform/handlers/gitlab.js +16 -3
- package/dist/feeds/platform/handlers/lemmy.cjs +46 -0
- package/dist/feeds/platform/handlers/lemmy.js +46 -0
- package/dist/feeds/platform/handlers/mastodon.cjs +5 -3
- package/dist/feeds/platform/handlers/mastodon.js +5 -3
- package/dist/feeds/platform/handlers/medium.cjs +10 -5
- package/dist/feeds/platform/handlers/medium.js +10 -5
- package/dist/feeds/platform/handlers/reddit.cjs +10 -5
- package/dist/feeds/platform/handlers/reddit.js +10 -5
- package/dist/feeds/platform/handlers/soundcloud.cjs +3 -2
- package/dist/feeds/platform/handlers/soundcloud.js +3 -2
- package/dist/feeds/platform/handlers/stackExchange.cjs +6 -3
- package/dist/feeds/platform/handlers/stackExchange.js +6 -3
- package/dist/feeds/platform/handlers/steam.cjs +4 -2
- package/dist/feeds/platform/handlers/steam.js +4 -2
- package/dist/feeds/platform/handlers/v2ex.cjs +4 -2
- package/dist/feeds/platform/handlers/v2ex.js +4 -2
- package/dist/feeds/platform/handlers/vimeo.cjs +3 -2
- package/dist/feeds/platform/handlers/vimeo.js +3 -2
- package/dist/feeds/platform/handlers/ximalaya.cjs +2 -1
- package/dist/feeds/platform/handlers/ximalaya.js +2 -1
- package/dist/feeds/platform/handlers/youtube.cjs +10 -9
- package/dist/feeds/platform/handlers/youtube.js +10 -9
- package/dist/hubs/discover/index.cjs +4 -4
- package/dist/hubs/discover/index.js +5 -5
- package/dist/hubs/discover/types.d.cts +2 -2
- package/dist/hubs/discover/types.d.ts +2 -2
- package/dist/hubs/feed/index.cjs +7 -5
- package/dist/hubs/feed/index.js +7 -5
- package/dist/hubs/headers/index.cjs +3 -3
- package/dist/hubs/headers/index.js +4 -4
- package/dist/hubs/html/index.cjs +3 -3
- package/dist/hubs/html/index.js +4 -4
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/package.json +3 -3
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/reddit.ts
|
|
3
|
+
const commentsPathRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
|
|
4
|
+
const subredditPathRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
|
|
5
|
+
const multiredditPathRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
|
|
6
|
+
const userPathRegex = /^\/(u|user)\/([^/]+)/;
|
|
7
|
+
const domainPathRegex = /^\/domain\/([^/]+)/;
|
|
3
8
|
const hosts = [
|
|
4
9
|
"reddit.com",
|
|
5
10
|
"www.reddit.com",
|
|
@@ -23,12 +28,12 @@ const redditHandler = {
|
|
|
23
28
|
uri: "https://www.reddit.com/.rss",
|
|
24
29
|
hint: require_utils.composeHint("reddit:posts")
|
|
25
30
|
}];
|
|
26
|
-
const commentsMatch = pathname.match(
|
|
31
|
+
const commentsMatch = pathname.match(commentsPathRegex);
|
|
27
32
|
if (commentsMatch?.[1] && commentsMatch?.[2]) return [{
|
|
28
33
|
uri: `https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`,
|
|
29
34
|
hint: require_utils.composeHint("reddit:post-comments")
|
|
30
35
|
}];
|
|
31
|
-
const subredditMatch = pathname.match(
|
|
36
|
+
const subredditMatch = pathname.match(subredditPathRegex);
|
|
32
37
|
if (subredditMatch?.[1]) {
|
|
33
38
|
const subreddit = subredditMatch[1];
|
|
34
39
|
const sort = subredditMatch[2];
|
|
@@ -47,17 +52,17 @@ const redditHandler = {
|
|
|
47
52
|
});
|
|
48
53
|
return uris;
|
|
49
54
|
}
|
|
50
|
-
const multiredditMatch = pathname.match(
|
|
55
|
+
const multiredditMatch = pathname.match(multiredditPathRegex);
|
|
51
56
|
if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [{
|
|
52
57
|
uri: `https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`,
|
|
53
58
|
hint: require_utils.composeHint("reddit:multireddit")
|
|
54
59
|
}];
|
|
55
|
-
const userMatch = pathname.match(
|
|
60
|
+
const userMatch = pathname.match(userPathRegex);
|
|
56
61
|
if (userMatch?.[2]) return [{
|
|
57
62
|
uri: `https://www.reddit.com/user/${userMatch[2]}/.rss`,
|
|
58
63
|
hint: require_utils.composeHint("reddit:posts")
|
|
59
64
|
}];
|
|
60
|
-
const domainMatch = pathname.match(
|
|
65
|
+
const domainMatch = pathname.match(domainPathRegex);
|
|
61
66
|
if (domainMatch?.[1]) return [{
|
|
62
67
|
uri: `https://www.reddit.com/domain/${domainMatch[1]}/.rss`,
|
|
63
68
|
hint: require_utils.composeHint("reddit:posts")
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/reddit.ts
|
|
3
|
+
const commentsPathRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
|
|
4
|
+
const subredditPathRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
|
|
5
|
+
const multiredditPathRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
|
|
6
|
+
const userPathRegex = /^\/(u|user)\/([^/]+)/;
|
|
7
|
+
const domainPathRegex = /^\/domain\/([^/]+)/;
|
|
3
8
|
const hosts = [
|
|
4
9
|
"reddit.com",
|
|
5
10
|
"www.reddit.com",
|
|
@@ -23,12 +28,12 @@ const redditHandler = {
|
|
|
23
28
|
uri: "https://www.reddit.com/.rss",
|
|
24
29
|
hint: composeHint("reddit:posts")
|
|
25
30
|
}];
|
|
26
|
-
const commentsMatch = pathname.match(
|
|
31
|
+
const commentsMatch = pathname.match(commentsPathRegex);
|
|
27
32
|
if (commentsMatch?.[1] && commentsMatch?.[2]) return [{
|
|
28
33
|
uri: `https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`,
|
|
29
34
|
hint: composeHint("reddit:post-comments")
|
|
30
35
|
}];
|
|
31
|
-
const subredditMatch = pathname.match(
|
|
36
|
+
const subredditMatch = pathname.match(subredditPathRegex);
|
|
32
37
|
if (subredditMatch?.[1]) {
|
|
33
38
|
const subreddit = subredditMatch[1];
|
|
34
39
|
const sort = subredditMatch[2];
|
|
@@ -47,17 +52,17 @@ const redditHandler = {
|
|
|
47
52
|
});
|
|
48
53
|
return uris;
|
|
49
54
|
}
|
|
50
|
-
const multiredditMatch = pathname.match(
|
|
55
|
+
const multiredditMatch = pathname.match(multiredditPathRegex);
|
|
51
56
|
if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [{
|
|
52
57
|
uri: `https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`,
|
|
53
58
|
hint: composeHint("reddit:multireddit")
|
|
54
59
|
}];
|
|
55
|
-
const userMatch = pathname.match(
|
|
60
|
+
const userMatch = pathname.match(userPathRegex);
|
|
56
61
|
if (userMatch?.[2]) return [{
|
|
57
62
|
uri: `https://www.reddit.com/user/${userMatch[2]}/.rss`,
|
|
58
63
|
hint: composeHint("reddit:posts")
|
|
59
64
|
}];
|
|
60
|
-
const domainMatch = pathname.match(
|
|
65
|
+
const domainMatch = pathname.match(domainPathRegex);
|
|
61
66
|
if (domainMatch?.[1]) return [{
|
|
62
67
|
uri: `https://www.reddit.com/domain/${domainMatch[1]}/.rss`,
|
|
63
68
|
hint: composeHint("reddit:posts")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/soundcloud.ts
|
|
3
|
+
const userIdRegex = /soundcloud:\/\/users:(\d+)/;
|
|
3
4
|
const hosts = [
|
|
4
5
|
"soundcloud.com",
|
|
5
6
|
"www.soundcloud.com",
|
|
@@ -15,14 +16,14 @@ const excludedPaths = [
|
|
|
15
16
|
"messages"
|
|
16
17
|
];
|
|
17
18
|
const extractUserIdFromContent = (content) => {
|
|
18
|
-
return content.match(
|
|
19
|
+
return content.match(userIdRegex)?.[1];
|
|
19
20
|
};
|
|
20
21
|
const soundcloudHandler = {
|
|
21
22
|
match: (url) => {
|
|
22
23
|
if (!require_utils.isHostOf(url, hosts)) return false;
|
|
23
24
|
const { pathname } = new URL(url);
|
|
24
25
|
const pathSegments = pathname.split("/").filter(Boolean);
|
|
25
|
-
return pathSegments.length
|
|
26
|
+
return pathSegments.length > 0 && !require_utils.isAnyOf(pathSegments[0], excludedPaths);
|
|
26
27
|
},
|
|
27
28
|
resolve: (_url, content) => {
|
|
28
29
|
if (!content) return [];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/soundcloud.ts
|
|
3
|
+
const userIdRegex = /soundcloud:\/\/users:(\d+)/;
|
|
3
4
|
const hosts = [
|
|
4
5
|
"soundcloud.com",
|
|
5
6
|
"www.soundcloud.com",
|
|
@@ -15,14 +16,14 @@ const excludedPaths = [
|
|
|
15
16
|
"messages"
|
|
16
17
|
];
|
|
17
18
|
const extractUserIdFromContent = (content) => {
|
|
18
|
-
return content.match(
|
|
19
|
+
return content.match(userIdRegex)?.[1];
|
|
19
20
|
};
|
|
20
21
|
const soundcloudHandler = {
|
|
21
22
|
match: (url) => {
|
|
22
23
|
if (!isHostOf(url, hosts)) return false;
|
|
23
24
|
const { pathname } = new URL(url);
|
|
24
25
|
const pathSegments = pathname.split("/").filter(Boolean);
|
|
25
|
-
return pathSegments.length
|
|
26
|
+
return pathSegments.length > 0 && !isAnyOf(pathSegments[0], excludedPaths);
|
|
26
27
|
},
|
|
27
28
|
resolve: (_url, content) => {
|
|
28
29
|
if (!content) return [];
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/stackExchange.ts
|
|
3
|
+
const tagPathRegex = /^\/questions\/tagged\/([\w.+-]+)/;
|
|
4
|
+
const questionPathRegex = /^\/questions\/(\d+)/;
|
|
5
|
+
const userPathRegex = /^\/users\/(\d+)/;
|
|
3
6
|
const domains = [
|
|
4
7
|
"stackoverflow.com",
|
|
5
8
|
"serverfault.com",
|
|
@@ -15,17 +18,17 @@ const stackExchangeHandler = {
|
|
|
15
18
|
},
|
|
16
19
|
resolve: (url) => {
|
|
17
20
|
const { origin, pathname } = new URL(url);
|
|
18
|
-
const tagMatch = pathname.match(
|
|
21
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
19
22
|
if (tagMatch?.[1]) return [{
|
|
20
23
|
uri: `${origin}/feeds/tag/${tagMatch[1]}`,
|
|
21
24
|
hint: require_utils.composeHint("stackexchange:tag")
|
|
22
25
|
}];
|
|
23
|
-
const questionMatch = pathname.match(
|
|
26
|
+
const questionMatch = pathname.match(questionPathRegex);
|
|
24
27
|
if (questionMatch?.[1]) return [{
|
|
25
28
|
uri: `${origin}/feeds/question/${questionMatch[1]}`,
|
|
26
29
|
hint: require_utils.composeHint("stackexchange:question")
|
|
27
30
|
}];
|
|
28
|
-
const userMatch = pathname.match(
|
|
31
|
+
const userMatch = pathname.match(userPathRegex);
|
|
29
32
|
if (userMatch?.[1]) return [{
|
|
30
33
|
uri: `${origin}/feeds/user/${userMatch[1]}`,
|
|
31
34
|
hint: require_utils.composeHint("stackexchange:user")
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { composeHint, isHostOf, isSubdomainOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/stackExchange.ts
|
|
3
|
+
const tagPathRegex = /^\/questions\/tagged\/([\w.+-]+)/;
|
|
4
|
+
const questionPathRegex = /^\/questions\/(\d+)/;
|
|
5
|
+
const userPathRegex = /^\/users\/(\d+)/;
|
|
3
6
|
const domains = [
|
|
4
7
|
"stackoverflow.com",
|
|
5
8
|
"serverfault.com",
|
|
@@ -15,17 +18,17 @@ const stackExchangeHandler = {
|
|
|
15
18
|
},
|
|
16
19
|
resolve: (url) => {
|
|
17
20
|
const { origin, pathname } = new URL(url);
|
|
18
|
-
const tagMatch = pathname.match(
|
|
21
|
+
const tagMatch = pathname.match(tagPathRegex);
|
|
19
22
|
if (tagMatch?.[1]) return [{
|
|
20
23
|
uri: `${origin}/feeds/tag/${tagMatch[1]}`,
|
|
21
24
|
hint: composeHint("stackexchange:tag")
|
|
22
25
|
}];
|
|
23
|
-
const questionMatch = pathname.match(
|
|
26
|
+
const questionMatch = pathname.match(questionPathRegex);
|
|
24
27
|
if (questionMatch?.[1]) return [{
|
|
25
28
|
uri: `${origin}/feeds/question/${questionMatch[1]}`,
|
|
26
29
|
hint: composeHint("stackexchange:question")
|
|
27
30
|
}];
|
|
28
|
-
const userMatch = pathname.match(
|
|
31
|
+
const userMatch = pathname.match(userPathRegex);
|
|
29
32
|
if (userMatch?.[1]) return [{
|
|
30
33
|
uri: `${origin}/feeds/user/${userMatch[1]}`,
|
|
31
34
|
hint: composeHint("stackexchange:user")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/steam.ts
|
|
3
|
+
const appPathRegex = /^\/(?:news\/)?app\/(\d+)/;
|
|
4
|
+
const groupPathRegex = /^\/groups\/([^/]+)/;
|
|
3
5
|
const hosts = ["store.steampowered.com", "steamcommunity.com"];
|
|
4
6
|
const steamHandler = {
|
|
5
7
|
match: (url) => {
|
|
@@ -7,13 +9,13 @@ const steamHandler = {
|
|
|
7
9
|
},
|
|
8
10
|
resolve: (url) => {
|
|
9
11
|
const { hostname, pathname } = new URL(url);
|
|
10
|
-
const appMatch = pathname.match(
|
|
12
|
+
const appMatch = pathname.match(appPathRegex);
|
|
11
13
|
if (appMatch?.[1]) return [{
|
|
12
14
|
uri: `https://store.steampowered.com/feeds/news/app/${appMatch[1]}/`,
|
|
13
15
|
hint: require_utils.composeHint("steam:news")
|
|
14
16
|
}];
|
|
15
17
|
if (hostname === "steamcommunity.com") {
|
|
16
|
-
const groupMatch = pathname.match(
|
|
18
|
+
const groupMatch = pathname.match(groupPathRegex);
|
|
17
19
|
if (groupMatch?.[1]) return [{
|
|
18
20
|
uri: `https://steamcommunity.com/groups/${groupMatch[1]}/rss`,
|
|
19
21
|
hint: require_utils.composeHint("steam:group")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { composeHint, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/steam.ts
|
|
3
|
+
const appPathRegex = /^\/(?:news\/)?app\/(\d+)/;
|
|
4
|
+
const groupPathRegex = /^\/groups\/([^/]+)/;
|
|
3
5
|
const hosts = ["store.steampowered.com", "steamcommunity.com"];
|
|
4
6
|
const steamHandler = {
|
|
5
7
|
match: (url) => {
|
|
@@ -7,13 +9,13 @@ const steamHandler = {
|
|
|
7
9
|
},
|
|
8
10
|
resolve: (url) => {
|
|
9
11
|
const { hostname, pathname } = new URL(url);
|
|
10
|
-
const appMatch = pathname.match(
|
|
12
|
+
const appMatch = pathname.match(appPathRegex);
|
|
11
13
|
if (appMatch?.[1]) return [{
|
|
12
14
|
uri: `https://store.steampowered.com/feeds/news/app/${appMatch[1]}/`,
|
|
13
15
|
hint: composeHint("steam:news")
|
|
14
16
|
}];
|
|
15
17
|
if (hostname === "steamcommunity.com") {
|
|
16
|
-
const groupMatch = pathname.match(
|
|
18
|
+
const groupMatch = pathname.match(groupPathRegex);
|
|
17
19
|
if (groupMatch?.[1]) return [{
|
|
18
20
|
uri: `https://steamcommunity.com/groups/${groupMatch[1]}/rss`,
|
|
19
21
|
hint: composeHint("steam:group")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/v2ex.ts
|
|
3
|
+
const nodePathRegex = /^\/go\/([^/]+)/;
|
|
4
|
+
const memberPathRegex = /^\/member\/([^/]+)/;
|
|
3
5
|
const hosts = ["www.v2ex.com", "v2ex.com"];
|
|
4
6
|
const v2exHandler = {
|
|
5
7
|
match: (url) => {
|
|
@@ -7,12 +9,12 @@ const v2exHandler = {
|
|
|
7
9
|
},
|
|
8
10
|
resolve: (url) => {
|
|
9
11
|
const { pathname, searchParams } = new URL(url);
|
|
10
|
-
const nodeMatch = pathname.match(
|
|
12
|
+
const nodeMatch = pathname.match(nodePathRegex);
|
|
11
13
|
if (nodeMatch?.[1]) return [{
|
|
12
14
|
uri: `https://www.v2ex.com/feed/${nodeMatch[1]}.xml`,
|
|
13
15
|
hint: require_utils.composeHint("v2ex:node")
|
|
14
16
|
}];
|
|
15
|
-
const memberMatch = pathname.match(
|
|
17
|
+
const memberMatch = pathname.match(memberPathRegex);
|
|
16
18
|
if (memberMatch?.[1]) return [{
|
|
17
19
|
uri: `https://www.v2ex.com/feed/member/${memberMatch[1]}.xml`,
|
|
18
20
|
hint: require_utils.composeHint("v2ex:member")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { composeHint, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/v2ex.ts
|
|
3
|
+
const nodePathRegex = /^\/go\/([^/]+)/;
|
|
4
|
+
const memberPathRegex = /^\/member\/([^/]+)/;
|
|
3
5
|
const hosts = ["www.v2ex.com", "v2ex.com"];
|
|
4
6
|
const v2exHandler = {
|
|
5
7
|
match: (url) => {
|
|
@@ -7,12 +9,12 @@ const v2exHandler = {
|
|
|
7
9
|
},
|
|
8
10
|
resolve: (url) => {
|
|
9
11
|
const { pathname, searchParams } = new URL(url);
|
|
10
|
-
const nodeMatch = pathname.match(
|
|
12
|
+
const nodeMatch = pathname.match(nodePathRegex);
|
|
11
13
|
if (nodeMatch?.[1]) return [{
|
|
12
14
|
uri: `https://www.v2ex.com/feed/${nodeMatch[1]}.xml`,
|
|
13
15
|
hint: composeHint("v2ex:node")
|
|
14
16
|
}];
|
|
15
|
-
const memberMatch = pathname.match(
|
|
17
|
+
const memberMatch = pathname.match(memberPathRegex);
|
|
16
18
|
if (memberMatch?.[1]) return [{
|
|
17
19
|
uri: `https://www.v2ex.com/feed/member/${memberMatch[1]}.xml`,
|
|
18
20
|
hint: composeHint("v2ex:member")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/vimeo.ts
|
|
3
|
+
const numericPathRegex = /^\d+$/;
|
|
3
4
|
const hosts = ["vimeo.com", "www.vimeo.com"];
|
|
4
5
|
const excludedPaths = [
|
|
5
6
|
"about",
|
|
@@ -47,9 +48,9 @@ const vimeoHandler = {
|
|
|
47
48
|
uri: `${origin}/groups/${pathSegments[1]}/videos/rss`,
|
|
48
49
|
hint: require_utils.composeHint("vimeo:group")
|
|
49
50
|
}];
|
|
50
|
-
if (pathSegments.length
|
|
51
|
+
if (pathSegments.length > 0) {
|
|
51
52
|
const user = pathSegments[0];
|
|
52
|
-
if (!require_utils.isAnyOf(user, excludedPaths) &&
|
|
53
|
+
if (!require_utils.isAnyOf(user, excludedPaths) && !numericPathRegex.test(user)) {
|
|
53
54
|
const feeds = [{
|
|
54
55
|
uri: `${origin}/${user}/videos/rss`,
|
|
55
56
|
hint: require_utils.composeHint("vimeo:videos")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/vimeo.ts
|
|
3
|
+
const numericPathRegex = /^\d+$/;
|
|
3
4
|
const hosts = ["vimeo.com", "www.vimeo.com"];
|
|
4
5
|
const excludedPaths = [
|
|
5
6
|
"about",
|
|
@@ -47,9 +48,9 @@ const vimeoHandler = {
|
|
|
47
48
|
uri: `${origin}/groups/${pathSegments[1]}/videos/rss`,
|
|
48
49
|
hint: composeHint("vimeo:group")
|
|
49
50
|
}];
|
|
50
|
-
if (pathSegments.length
|
|
51
|
+
if (pathSegments.length > 0) {
|
|
51
52
|
const user = pathSegments[0];
|
|
52
|
-
if (!isAnyOf(user, excludedPaths) &&
|
|
53
|
+
if (!isAnyOf(user, excludedPaths) && !numericPathRegex.test(user)) {
|
|
53
54
|
const feeds = [{
|
|
54
55
|
uri: `${origin}/${user}/videos/rss`,
|
|
55
56
|
hint: composeHint("vimeo:videos")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/ximalaya.ts
|
|
3
|
+
const albumPathRegex = /^\/album\/(\d+)/;
|
|
3
4
|
const hosts = ["www.ximalaya.com", "ximalaya.com"];
|
|
4
5
|
const ximalayaHandler = {
|
|
5
6
|
match: (url) => {
|
|
@@ -7,7 +8,7 @@ const ximalayaHandler = {
|
|
|
7
8
|
},
|
|
8
9
|
resolve: (url) => {
|
|
9
10
|
const { pathname } = new URL(url);
|
|
10
|
-
const id = pathname.match(
|
|
11
|
+
const id = pathname.match(albumPathRegex)?.[1];
|
|
11
12
|
if (!id) return [];
|
|
12
13
|
return [{
|
|
13
14
|
uri: `https://www.ximalaya.com/album/${id}.xml`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { composeHint, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/ximalaya.ts
|
|
3
|
+
const albumPathRegex = /^\/album\/(\d+)/;
|
|
3
4
|
const hosts = ["www.ximalaya.com", "ximalaya.com"];
|
|
4
5
|
const ximalayaHandler = {
|
|
5
6
|
match: (url) => {
|
|
@@ -7,7 +8,7 @@ const ximalayaHandler = {
|
|
|
7
8
|
},
|
|
8
9
|
resolve: (url) => {
|
|
9
10
|
const { pathname } = new URL(url);
|
|
10
|
-
const id = pathname.match(
|
|
11
|
+
const id = pathname.match(albumPathRegex)?.[1];
|
|
11
12
|
if (!id) return [];
|
|
12
13
|
return [{
|
|
13
14
|
uri: `https://www.ximalaya.com/album/${id}.xml`,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/youtube.ts
|
|
3
|
+
const channelIdRegex = /"(?:channelId|externalId)":"(UC[a-zA-Z0-9_-]+)"/;
|
|
4
|
+
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
5
|
+
const handlePathRegex = /^\/@([^/]+)/;
|
|
6
|
+
const userPathRegex = /^\/user\/([^/]+)/;
|
|
7
|
+
const customPathRegex = /^\/c\/([^/]+)/;
|
|
8
|
+
const channelPrefixRegex = /^UC/;
|
|
3
9
|
const hosts = [
|
|
4
10
|
"youtube.com",
|
|
5
11
|
"www.youtube.com",
|
|
@@ -7,25 +13,20 @@ const hosts = [
|
|
|
7
13
|
"youtu.be",
|
|
8
14
|
"www.youtu.be"
|
|
9
15
|
];
|
|
10
|
-
const channelIdRegex = /"(?:channelId|externalId)":"(UC[a-zA-Z0-9_-]+)"/;
|
|
11
|
-
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
12
|
-
const handlePathRegex = /^\/@([^/]+)/;
|
|
13
|
-
const userPathRegex = /^\/user\/([^/]+)/;
|
|
14
|
-
const customPathRegex = /^\/c\/([^/]+)/;
|
|
15
16
|
const extractChannelIdFromContent = (content) => {
|
|
16
17
|
return content.match(channelIdRegex)?.[1];
|
|
17
18
|
};
|
|
18
19
|
const getAllUploadsPlaylistId = (channelId) => {
|
|
19
|
-
return channelId.replace(
|
|
20
|
+
return channelId.replace(channelPrefixRegex, "UU");
|
|
20
21
|
};
|
|
21
22
|
const getVideosOnlyPlaylistId = (channelId) => {
|
|
22
|
-
return channelId.replace(
|
|
23
|
+
return channelId.replace(channelPrefixRegex, "UULF");
|
|
23
24
|
};
|
|
24
25
|
const getShortsOnlyPlaylistId = (channelId) => {
|
|
25
|
-
return channelId.replace(
|
|
26
|
+
return channelId.replace(channelPrefixRegex, "UUSH");
|
|
26
27
|
};
|
|
27
28
|
const getLiveStreamsOnlyPlaylistId = (channelId) => {
|
|
28
|
-
return channelId.replace(
|
|
29
|
+
return channelId.replace(channelPrefixRegex, "UULV");
|
|
29
30
|
};
|
|
30
31
|
const feedUrl = (param, value) => {
|
|
31
32
|
return `https://www.youtube.com/feeds/videos.xml?${param}=${value}`;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { composeHint, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/youtube.ts
|
|
3
|
+
const channelIdRegex = /"(?:channelId|externalId)":"(UC[a-zA-Z0-9_-]+)"/;
|
|
4
|
+
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
5
|
+
const handlePathRegex = /^\/@([^/]+)/;
|
|
6
|
+
const userPathRegex = /^\/user\/([^/]+)/;
|
|
7
|
+
const customPathRegex = /^\/c\/([^/]+)/;
|
|
8
|
+
const channelPrefixRegex = /^UC/;
|
|
3
9
|
const hosts = [
|
|
4
10
|
"youtube.com",
|
|
5
11
|
"www.youtube.com",
|
|
@@ -7,25 +13,20 @@ const hosts = [
|
|
|
7
13
|
"youtu.be",
|
|
8
14
|
"www.youtu.be"
|
|
9
15
|
];
|
|
10
|
-
const channelIdRegex = /"(?:channelId|externalId)":"(UC[a-zA-Z0-9_-]+)"/;
|
|
11
|
-
const channelPathRegex = /^\/channel\/(UC[a-zA-Z0-9_-]+)/;
|
|
12
|
-
const handlePathRegex = /^\/@([^/]+)/;
|
|
13
|
-
const userPathRegex = /^\/user\/([^/]+)/;
|
|
14
|
-
const customPathRegex = /^\/c\/([^/]+)/;
|
|
15
16
|
const extractChannelIdFromContent = (content) => {
|
|
16
17
|
return content.match(channelIdRegex)?.[1];
|
|
17
18
|
};
|
|
18
19
|
const getAllUploadsPlaylistId = (channelId) => {
|
|
19
|
-
return channelId.replace(
|
|
20
|
+
return channelId.replace(channelPrefixRegex, "UU");
|
|
20
21
|
};
|
|
21
22
|
const getVideosOnlyPlaylistId = (channelId) => {
|
|
22
|
-
return channelId.replace(
|
|
23
|
+
return channelId.replace(channelPrefixRegex, "UULF");
|
|
23
24
|
};
|
|
24
25
|
const getShortsOnlyPlaylistId = (channelId) => {
|
|
25
|
-
return channelId.replace(
|
|
26
|
+
return channelId.replace(channelPrefixRegex, "UUSH");
|
|
26
27
|
};
|
|
27
28
|
const getLiveStreamsOnlyPlaylistId = (channelId) => {
|
|
28
|
-
return channelId.replace(
|
|
29
|
+
return channelId.replace(channelPrefixRegex, "UULV");
|
|
29
30
|
};
|
|
30
31
|
const feedUrl = (param, value) => {
|
|
31
32
|
return `https://www.youtube.com/feeds/videos.xml?${param}=${value}`;
|
|
@@ -10,19 +10,19 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
10
10
|
"headers",
|
|
11
11
|
"feed",
|
|
12
12
|
"html"
|
|
13
|
-
], fetchFn = require_utils$1.defaultFetchFn,
|
|
13
|
+
], fetchFn = require_utils$1.defaultFetchFn, resolveUrlFn = require_utils.resolveUrl } = options;
|
|
14
14
|
const normalizedInput = await require_utils$2.normalizeInput(input, fetchFn);
|
|
15
15
|
const results = [];
|
|
16
16
|
if (methods.includes("headers") && normalizedInput.headers) {
|
|
17
|
-
const headerHubs = require_index$1.discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url,
|
|
17
|
+
const headerHubs = require_index$1.discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url, resolveUrlFn);
|
|
18
18
|
results.push(...headerHubs);
|
|
19
19
|
}
|
|
20
20
|
if (methods.includes("feed") && normalizedInput.content) {
|
|
21
|
-
const feedHubs = require_index.discoverHubsFromFeed(normalizedInput.content, normalizedInput.url);
|
|
21
|
+
const feedHubs = require_index.discoverHubsFromFeed(normalizedInput.content, normalizedInput.url, resolveUrlFn);
|
|
22
22
|
results.push(...feedHubs);
|
|
23
23
|
}
|
|
24
24
|
if (methods.includes("html") && normalizedInput.content) {
|
|
25
|
-
const htmlHubs = require_index$2.discoverHubsFromHtml(normalizedInput.content, normalizedInput.url,
|
|
25
|
+
const htmlHubs = require_index$2.discoverHubsFromHtml(normalizedInput.content, normalizedInput.url, resolveUrlFn);
|
|
26
26
|
results.push(...htmlHubs);
|
|
27
27
|
}
|
|
28
28
|
return results;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveUrl } from "../../common/utils.js";
|
|
2
2
|
import { defaultFetchFn } from "../../common/discover/utils.js";
|
|
3
3
|
import { discoverHubsFromFeed } from "../feed/index.js";
|
|
4
4
|
import { discoverHubsFromHeaders } from "../headers/index.js";
|
|
@@ -10,19 +10,19 @@ const discoverHubs = async (input, options = {}) => {
|
|
|
10
10
|
"headers",
|
|
11
11
|
"feed",
|
|
12
12
|
"html"
|
|
13
|
-
], fetchFn = defaultFetchFn,
|
|
13
|
+
], fetchFn = defaultFetchFn, resolveUrlFn = resolveUrl } = options;
|
|
14
14
|
const normalizedInput = await normalizeInput(input, fetchFn);
|
|
15
15
|
const results = [];
|
|
16
16
|
if (methods.includes("headers") && normalizedInput.headers) {
|
|
17
|
-
const headerHubs = discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url,
|
|
17
|
+
const headerHubs = discoverHubsFromHeaders(normalizedInput.headers, normalizedInput.url, resolveUrlFn);
|
|
18
18
|
results.push(...headerHubs);
|
|
19
19
|
}
|
|
20
20
|
if (methods.includes("feed") && normalizedInput.content) {
|
|
21
|
-
const feedHubs = discoverHubsFromFeed(normalizedInput.content, normalizedInput.url);
|
|
21
|
+
const feedHubs = discoverHubsFromFeed(normalizedInput.content, normalizedInput.url, resolveUrlFn);
|
|
22
22
|
results.push(...feedHubs);
|
|
23
23
|
}
|
|
24
24
|
if (methods.includes("html") && normalizedInput.content) {
|
|
25
|
-
const htmlHubs = discoverHubsFromHtml(normalizedInput.content, normalizedInput.url,
|
|
25
|
+
const htmlHubs = discoverHubsFromHtml(normalizedInput.content, normalizedInput.url, resolveUrlFn);
|
|
26
26
|
results.push(...htmlHubs);
|
|
27
27
|
}
|
|
28
28
|
return results;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DiscoverFetchFn,
|
|
1
|
+
import { DiscoverFetchFn, DiscoverResolveUrlFn } from "../../common/types.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/hubs/discover/types.d.ts
|
|
4
4
|
type HubResult = {
|
|
@@ -9,7 +9,7 @@ type DiscoverHubsMethodsConfig = Array<'headers' | 'html' | 'feed'>;
|
|
|
9
9
|
type DiscoverHubsOptions = {
|
|
10
10
|
methods?: DiscoverHubsMethodsConfig;
|
|
11
11
|
fetchFn?: DiscoverFetchFn;
|
|
12
|
-
|
|
12
|
+
resolveUrlFn?: DiscoverResolveUrlFn;
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
15
15
|
export { DiscoverHubsMethodsConfig, DiscoverHubsOptions, HubResult };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DiscoverFetchFn,
|
|
1
|
+
import { DiscoverFetchFn, DiscoverResolveUrlFn } from "../../common/types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/hubs/discover/types.d.ts
|
|
4
4
|
type HubResult = {
|
|
@@ -9,7 +9,7 @@ type DiscoverHubsMethodsConfig = Array<'headers' | 'html' | 'feed'>;
|
|
|
9
9
|
type DiscoverHubsOptions = {
|
|
10
10
|
methods?: DiscoverHubsMethodsConfig;
|
|
11
11
|
fetchFn?: DiscoverFetchFn;
|
|
12
|
-
|
|
12
|
+
resolveUrlFn?: DiscoverResolveUrlFn;
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
15
15
|
export { DiscoverHubsMethodsConfig, DiscoverHubsOptions, HubResult };
|
package/dist/hubs/feed/index.cjs
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
+
const require_utils = require("../../common/utils.cjs");
|
|
1
2
|
let feedsmith = require("feedsmith");
|
|
2
3
|
//#region src/hubs/feed/index.ts
|
|
3
4
|
const getLinksWithRel = (links, rel) => {
|
|
4
5
|
return links?.filter((link) => link.rel === rel && link.href).map((link) => link.href) ?? [];
|
|
5
6
|
};
|
|
6
|
-
const discoverHubsFromFeed = (content, baseUrl) => {
|
|
7
|
+
const discoverHubsFromFeed = (content, baseUrl, resolveUrlFn = require_utils.resolveUrl) => {
|
|
7
8
|
try {
|
|
8
9
|
const { format, feed } = (0, feedsmith.parseFeed)(content);
|
|
9
10
|
if (format === "json") {
|
|
10
11
|
const hubs = feed.hubs ?? [];
|
|
11
|
-
const topic = feed.feed_url ?? baseUrl;
|
|
12
|
+
const topic = feed.feed_url ? resolveUrlFn(feed.feed_url, baseUrl) ?? feed.feed_url : baseUrl;
|
|
12
13
|
return hubs.filter((hub) => hub.url).map((hub) => ({
|
|
13
|
-
hub: hub.url,
|
|
14
|
+
hub: resolveUrlFn(hub.url, baseUrl) ?? hub.url,
|
|
14
15
|
topic
|
|
15
16
|
}));
|
|
16
17
|
}
|
|
17
18
|
const links = format === "atom" ? feed.links : feed.atom?.links;
|
|
18
19
|
const hubUris = getLinksWithRel(links, "hub");
|
|
19
20
|
if (hubUris.length > 0) {
|
|
20
|
-
const
|
|
21
|
+
const selfUris = getLinksWithRel(links, "self");
|
|
22
|
+
const topic = selfUris[0] ? resolveUrlFn(selfUris[0], baseUrl) ?? selfUris[0] : baseUrl;
|
|
21
23
|
return hubUris.map((hub) => ({
|
|
22
|
-
hub,
|
|
24
|
+
hub: resolveUrlFn(hub, baseUrl) ?? hub,
|
|
23
25
|
topic
|
|
24
26
|
}));
|
|
25
27
|
}
|
package/dist/hubs/feed/index.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
+
import { resolveUrl } from "../../common/utils.js";
|
|
1
2
|
import { parseFeed } from "feedsmith";
|
|
2
3
|
//#region src/hubs/feed/index.ts
|
|
3
4
|
const getLinksWithRel = (links, rel) => {
|
|
4
5
|
return links?.filter((link) => link.rel === rel && link.href).map((link) => link.href) ?? [];
|
|
5
6
|
};
|
|
6
|
-
const discoverHubsFromFeed = (content, baseUrl) => {
|
|
7
|
+
const discoverHubsFromFeed = (content, baseUrl, resolveUrlFn = resolveUrl) => {
|
|
7
8
|
try {
|
|
8
9
|
const { format, feed } = parseFeed(content);
|
|
9
10
|
if (format === "json") {
|
|
10
11
|
const hubs = feed.hubs ?? [];
|
|
11
|
-
const topic = feed.feed_url ?? baseUrl;
|
|
12
|
+
const topic = feed.feed_url ? resolveUrlFn(feed.feed_url, baseUrl) ?? feed.feed_url : baseUrl;
|
|
12
13
|
return hubs.filter((hub) => hub.url).map((hub) => ({
|
|
13
|
-
hub: hub.url,
|
|
14
|
+
hub: resolveUrlFn(hub.url, baseUrl) ?? hub.url,
|
|
14
15
|
topic
|
|
15
16
|
}));
|
|
16
17
|
}
|
|
17
18
|
const links = format === "atom" ? feed.links : feed.atom?.links;
|
|
18
19
|
const hubUris = getLinksWithRel(links, "hub");
|
|
19
20
|
if (hubUris.length > 0) {
|
|
20
|
-
const
|
|
21
|
+
const selfUris = getLinksWithRel(links, "self");
|
|
22
|
+
const topic = selfUris[0] ? resolveUrlFn(selfUris[0], baseUrl) ?? selfUris[0] : baseUrl;
|
|
21
23
|
return hubUris.map((hub) => ({
|
|
22
|
-
hub,
|
|
24
|
+
hub: resolveUrlFn(hub, baseUrl) ?? hub,
|
|
23
25
|
topic
|
|
24
26
|
}));
|
|
25
27
|
}
|
|
@@ -3,13 +3,13 @@ const require_index = require("../../common/uris/headers/index.cjs");
|
|
|
3
3
|
//#region src/hubs/headers/index.ts
|
|
4
4
|
const hubSelector = [{ rel: "hub" }];
|
|
5
5
|
const selfSelector = [{ rel: "self" }];
|
|
6
|
-
const discoverHubsFromHeaders = (headers, baseUrl,
|
|
6
|
+
const discoverHubsFromHeaders = (headers, baseUrl, resolveUrlFn = require_utils.resolveUrl) => {
|
|
7
7
|
const hubUris = require_index.discoverUrisFromHeaders(headers, { linkSelectors: hubSelector });
|
|
8
8
|
if (hubUris.length === 0) return [];
|
|
9
9
|
const selfUris = require_index.discoverUrisFromHeaders(headers, { linkSelectors: selfSelector });
|
|
10
|
-
const topic = selfUris[0] ?
|
|
10
|
+
const topic = selfUris[0] ? resolveUrlFn(selfUris[0], baseUrl) ?? selfUris[0] : baseUrl;
|
|
11
11
|
return hubUris.map((hub) => ({
|
|
12
|
-
hub:
|
|
12
|
+
hub: resolveUrlFn(hub, baseUrl) ?? hub,
|
|
13
13
|
topic
|
|
14
14
|
}));
|
|
15
15
|
};
|