feedscout 1.7.0 → 1.8.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/dist/common/discover/utils.cjs +11 -8
- package/dist/common/discover/utils.js +11 -8
- package/dist/common/locales.cjs +11 -4
- package/dist/common/locales.js +11 -4
- package/dist/common/types.d.cts +5 -4
- package/dist/common/types.d.ts +5 -4
- package/dist/common/uris/guess/utils.cjs +1 -1
- package/dist/common/uris/guess/utils.js +1 -1
- package/dist/common/uris/headers/index.cjs +1 -1
- package/dist/common/uris/headers/index.js +1 -1
- package/dist/common/uris/platform/types.d.cts +2 -2
- package/dist/common/uris/platform/types.d.ts +2 -2
- package/dist/common/utils.cjs +5 -1
- package/dist/common/utils.js +5 -1
- package/dist/favicons/defaults.cjs +6 -1
- package/dist/favicons/defaults.js +6 -1
- package/dist/favicons/platform/handlers/bluesky.cjs +1 -1
- package/dist/favicons/platform/handlers/bluesky.js +1 -1
- package/dist/favicons/platform/handlers/codeberg.cjs +4 -3
- package/dist/favicons/platform/handlers/codeberg.js +4 -3
- package/dist/favicons/platform/handlers/devto.cjs +31 -0
- package/dist/favicons/platform/handlers/devto.js +31 -0
- package/dist/favicons/platform/handlers/github.cjs +4 -3
- package/dist/favicons/platform/handlers/github.js +4 -3
- package/dist/favicons/platform/handlers/githubGist.cjs +4 -3
- package/dist/favicons/platform/handlers/githubGist.js +4 -3
- package/dist/favicons/platform/handlers/gitlab.cjs +38 -0
- package/dist/favicons/platform/handlers/gitlab.js +38 -0
- package/dist/favicons/platform/handlers/lobsters.cjs +3 -3
- package/dist/favicons/platform/handlers/lobsters.js +3 -3
- package/dist/favicons/platform/handlers/mastodon.cjs +5 -3
- package/dist/favicons/platform/handlers/mastodon.js +5 -3
- package/dist/favicons/platform/handlers/reddit.cjs +10 -8
- package/dist/favicons/platform/handlers/reddit.js +10 -8
- package/dist/feeds/defaults.cjs +4 -2
- package/dist/feeds/defaults.js +4 -2
- package/dist/feeds/platform/handlers/applePodcasts.cjs +26 -0
- package/dist/feeds/platform/handlers/applePodcasts.js +26 -0
- package/dist/feeds/platform/handlers/behance.cjs +2 -2
- package/dist/feeds/platform/handlers/behance.js +2 -2
- package/dist/feeds/platform/handlers/dailymotion.cjs +4 -4
- package/dist/feeds/platform/handlers/dailymotion.js +4 -4
- package/dist/feeds/platform/handlers/devto.cjs +6 -4
- package/dist/feeds/platform/handlers/devto.js +5 -5
- package/dist/feeds/platform/handlers/gitlab.cjs +4 -0
- package/dist/feeds/platform/handlers/gitlab.js +1 -1
- package/dist/feeds/platform/handlers/hatenablog.cjs +4 -4
- package/dist/feeds/platform/handlers/hatenablog.js +4 -4
- package/dist/feeds/platform/handlers/itchio.cjs +10 -10
- package/dist/feeds/platform/handlers/itchio.js +10 -10
- package/dist/feeds/platform/handlers/lobsters.cjs +8 -8
- package/dist/feeds/platform/handlers/lobsters.js +8 -8
- package/dist/feeds/platform/handlers/mastodon.cjs +4 -4
- package/dist/feeds/platform/handlers/mastodon.js +4 -4
- package/dist/feeds/platform/handlers/medium.cjs +10 -10
- package/dist/feeds/platform/handlers/medium.js +10 -10
- package/dist/feeds/platform/handlers/paragraph.cjs +2 -2
- package/dist/feeds/platform/handlers/paragraph.js +2 -2
- package/dist/feeds/platform/handlers/producthunt.cjs +4 -4
- package/dist/feeds/platform/handlers/producthunt.js +4 -4
- package/dist/feeds/platform/handlers/reddit.cjs +10 -10
- package/dist/feeds/platform/handlers/reddit.js +10 -10
- package/dist/feeds/platform/handlers/stackExchange.cjs +6 -6
- package/dist/feeds/platform/handlers/stackExchange.js +6 -6
- package/dist/feeds/platform/handlers/steam.cjs +4 -4
- package/dist/feeds/platform/handlers/steam.js +4 -4
- package/dist/feeds/platform/handlers/substack.cjs +10 -3
- package/dist/feeds/platform/handlers/substack.js +11 -4
- package/dist/feeds/platform/handlers/tumblr.cjs +2 -2
- package/dist/feeds/platform/handlers/tumblr.js +2 -2
- package/dist/feeds/platform/handlers/v2ex.cjs +4 -4
- package/dist/feeds/platform/handlers/v2ex.js +4 -4
- package/dist/feeds/platform/handlers/vimeo.cjs +2 -2
- package/dist/feeds/platform/handlers/vimeo.js +2 -2
- package/dist/feeds/platform/handlers/wordpress.cjs +6 -6
- package/dist/feeds/platform/handlers/wordpress.js +6 -6
- package/dist/feeds/platform/handlers/ximalaya.cjs +2 -2
- package/dist/feeds/platform/handlers/ximalaya.js +2 -2
- package/dist/feeds/platform/handlers/youtube.cjs +36 -21
- package/dist/feeds/platform/handlers/youtube.js +36 -21
- package/dist/hubs/discover/index.cjs +1 -2
- package/dist/hubs/discover/index.js +1 -2
- package/dist/hubs/feed/index.cjs +4 -4
- package/dist/hubs/feed/index.js +4 -4
- package/dist/hubs/headers/index.cjs +2 -2
- package/dist/hubs/headers/index.js +2 -2
- package/dist/hubs/html/index.cjs +2 -2
- package/dist/hubs/html/index.js +2 -2
- package/package.json +4 -3
- package/dist/hubs/discover/utils.cjs +0 -18
- package/dist/hubs/discover/utils.js +0 -18
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
|
+
import { isNonEmptyString, parseBodyJson } from "../../utils.js";
|
|
3
|
+
import { excludedPaths, hosts, isGitlabHeaders, isGitlabHtml } from "../../../feeds/platform/handlers/gitlab.js";
|
|
4
|
+
//#region src/favicons/platform/handlers/gitlab.ts
|
|
5
|
+
const userRegex = /^\/([^/]+?)(?:\.atom)?(?:\/|$)/;
|
|
6
|
+
const fetchAvatarUrl = async (apiUrl, fetchFn) => {
|
|
7
|
+
const data = parseBodyJson((await fetchFn(apiUrl)).body);
|
|
8
|
+
const entry = Array.isArray(data) ? data[0] : data;
|
|
9
|
+
if (isNonEmptyString(entry?.avatar_url)) return entry.avatar_url;
|
|
10
|
+
};
|
|
11
|
+
const gitlabHandler = {
|
|
12
|
+
match: (url, content, headers) => {
|
|
13
|
+
try {
|
|
14
|
+
if (isHostOf(url, hosts)) return true;
|
|
15
|
+
const { pathname } = new URL(url);
|
|
16
|
+
if (!userRegex.test(pathname)) return false;
|
|
17
|
+
if (content && isGitlabHtml(content)) return true;
|
|
18
|
+
if (headers && isGitlabHeaders(headers)) return true;
|
|
19
|
+
} catch {}
|
|
20
|
+
return false;
|
|
21
|
+
},
|
|
22
|
+
resolve: async (url, _content, _headers, fetchFn) => {
|
|
23
|
+
if (!fetchFn) return [];
|
|
24
|
+
try {
|
|
25
|
+
const { origin, pathname } = new URL(url);
|
|
26
|
+
const match = pathname.match(userRegex);
|
|
27
|
+
if (!match?.[1]) return [];
|
|
28
|
+
const username = match[1];
|
|
29
|
+
if (isAnyOf(username, excludedPaths)) return [];
|
|
30
|
+
const encodedName = encodeURIComponent(username);
|
|
31
|
+
const avatarUrl = await fetchAvatarUrl(`${origin}/api/v4/users?username=${encodedName}`, fetchFn) ?? await fetchAvatarUrl(`${origin}/api/v4/groups/${encodedName}`, fetchFn);
|
|
32
|
+
if (avatarUrl) return [{ uri: avatarUrl }];
|
|
33
|
+
} catch {}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { gitlabHandler };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
const require_lobsters = require("../../../feeds/platform/handlers/lobsters.cjs");
|
|
3
3
|
//#region src/favicons/platform/handlers/lobsters.ts
|
|
4
|
-
const
|
|
4
|
+
const userRegex = /^\/~([a-zA-Z0-9_-]+)/;
|
|
5
5
|
const lobstersHandler = {
|
|
6
6
|
match: (url) => {
|
|
7
7
|
try {
|
|
8
8
|
const { pathname } = new URL(url);
|
|
9
|
-
return require_utils.isHostOf(url, require_lobsters.hosts) &&
|
|
9
|
+
return require_utils.isHostOf(url, require_lobsters.hosts) && userRegex.test(pathname);
|
|
10
10
|
} catch {}
|
|
11
11
|
return false;
|
|
12
12
|
},
|
|
13
13
|
resolve: (url) => {
|
|
14
14
|
const { pathname } = new URL(url);
|
|
15
|
-
const match = pathname.match(
|
|
15
|
+
const match = pathname.match(userRegex);
|
|
16
16
|
if (!match?.[1]) return [];
|
|
17
17
|
return [{ uri: `https://lobste.rs/avatars/${match[1]}-100.png` }];
|
|
18
18
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { isHostOf } from "../../../common/utils.js";
|
|
2
2
|
import { hosts } from "../../../feeds/platform/handlers/lobsters.js";
|
|
3
3
|
//#region src/favicons/platform/handlers/lobsters.ts
|
|
4
|
-
const
|
|
4
|
+
const userRegex = /^\/~([a-zA-Z0-9_-]+)/;
|
|
5
5
|
const lobstersHandler = {
|
|
6
6
|
match: (url) => {
|
|
7
7
|
try {
|
|
8
8
|
const { pathname } = new URL(url);
|
|
9
|
-
return isHostOf(url, hosts) &&
|
|
9
|
+
return isHostOf(url, hosts) && userRegex.test(pathname);
|
|
10
10
|
} catch {}
|
|
11
11
|
return false;
|
|
12
12
|
},
|
|
13
13
|
resolve: (url) => {
|
|
14
14
|
const { pathname } = new URL(url);
|
|
15
|
-
const match = pathname.match(
|
|
15
|
+
const match = pathname.match(userRegex);
|
|
16
16
|
if (!match?.[1]) return [];
|
|
17
17
|
return [{ uri: `https://lobste.rs/avatars/${match[1]}-100.png` }];
|
|
18
18
|
}
|
|
@@ -2,9 +2,9 @@ const require_utils = require("../../../common/utils.cjs");
|
|
|
2
2
|
const require_utils$1 = require("../../utils.cjs");
|
|
3
3
|
//#region src/favicons/platform/handlers/mastodon.ts
|
|
4
4
|
const mastodonRegex = /mastodon/i;
|
|
5
|
+
const profileRegex = /^\/@([^/.]+(?:@[^/.]+\.[^/.]+)?)(?:\.rss)?\/?$/;
|
|
5
6
|
const isProfilePath = (pathname) => {
|
|
6
|
-
|
|
7
|
-
return segments.length === 1 && segments[0].startsWith("@");
|
|
7
|
+
return profileRegex.test(pathname);
|
|
8
8
|
};
|
|
9
9
|
const isMastodonHtml = (content) => {
|
|
10
10
|
return require_utils.hasMetaContent(content, "generator", "Mastodon");
|
|
@@ -26,7 +26,9 @@ const mastodonHandler = {
|
|
|
26
26
|
if (!fetchFn) return [];
|
|
27
27
|
try {
|
|
28
28
|
const { hostname, pathname } = new URL(url);
|
|
29
|
-
const
|
|
29
|
+
const match = pathname.match(profileRegex);
|
|
30
|
+
if (!match?.[1]) return [];
|
|
31
|
+
const data = require_utils$1.parseBodyJson((await fetchFn(`https://${hostname}/api/v1/accounts/lookup?acct=${match[1]}`)).body);
|
|
30
32
|
if (require_utils$1.isNonEmptyString(data.avatar)) return [{ uri: data.avatar }];
|
|
31
33
|
} catch {}
|
|
32
34
|
return [];
|
|
@@ -2,9 +2,9 @@ import { hasMetaContent } from "../../../common/utils.js";
|
|
|
2
2
|
import { isNonEmptyString, parseBodyJson } from "../../utils.js";
|
|
3
3
|
//#region src/favicons/platform/handlers/mastodon.ts
|
|
4
4
|
const mastodonRegex = /mastodon/i;
|
|
5
|
+
const profileRegex = /^\/@([^/.]+(?:@[^/.]+\.[^/.]+)?)(?:\.rss)?\/?$/;
|
|
5
6
|
const isProfilePath = (pathname) => {
|
|
6
|
-
|
|
7
|
-
return segments.length === 1 && segments[0].startsWith("@");
|
|
7
|
+
return profileRegex.test(pathname);
|
|
8
8
|
};
|
|
9
9
|
const isMastodonHtml = (content) => {
|
|
10
10
|
return hasMetaContent(content, "generator", "Mastodon");
|
|
@@ -26,7 +26,9 @@ const mastodonHandler = {
|
|
|
26
26
|
if (!fetchFn) return [];
|
|
27
27
|
try {
|
|
28
28
|
const { hostname, pathname } = new URL(url);
|
|
29
|
-
const
|
|
29
|
+
const match = pathname.match(profileRegex);
|
|
30
|
+
if (!match?.[1]) return [];
|
|
31
|
+
const data = parseBodyJson((await fetchFn(`https://${hostname}/api/v1/accounts/lookup?acct=${match[1]}`)).body);
|
|
30
32
|
if (isNonEmptyString(data.avatar)) return [{ uri: data.avatar }];
|
|
31
33
|
} catch {}
|
|
32
34
|
return [];
|
|
@@ -2,13 +2,13 @@ const require_utils = require("../../../common/utils.cjs");
|
|
|
2
2
|
const require_utils$1 = require("../../utils.cjs");
|
|
3
3
|
const require_reddit = require("../../../feeds/platform/handlers/reddit.cjs");
|
|
4
4
|
//#region src/favicons/platform/handlers/reddit.ts
|
|
5
|
+
const subredditRegex = /^\/r\/([^/.]+)/;
|
|
6
|
+
const userRegex = /^\/(u|user)\/([^/.]+)/;
|
|
5
7
|
const isSubredditPath = (pathname) => {
|
|
6
|
-
|
|
7
|
-
return segments.length >= 2 && segments[0] === "r";
|
|
8
|
+
return subredditRegex.test(pathname);
|
|
8
9
|
};
|
|
9
10
|
const isUserPath = (pathname) => {
|
|
10
|
-
|
|
11
|
-
return segments.length >= 2 && (segments[0] === "u" || segments[0] === "user");
|
|
11
|
+
return userRegex.test(pathname);
|
|
12
12
|
};
|
|
13
13
|
const redditHandler = {
|
|
14
14
|
match: (url) => {
|
|
@@ -22,13 +22,15 @@ const redditHandler = {
|
|
|
22
22
|
if (!fetchFn) return [];
|
|
23
23
|
try {
|
|
24
24
|
const { pathname } = new URL(url);
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const subredditMatch = pathname.match(subredditRegex);
|
|
26
|
+
if (subredditMatch?.[1]) {
|
|
27
|
+
const data = require_utils$1.parseBodyJson((await fetchFn(`https://www.reddit.com/r/${subredditMatch[1]}/about.json`)).body);
|
|
27
28
|
const icon = data?.data?.community_icon?.split("?")[0] || data?.data?.icon_img;
|
|
28
29
|
if (require_utils$1.isNonEmptyString(icon)) return [{ uri: icon }];
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const userMatch = pathname.match(userRegex);
|
|
32
|
+
if (userMatch?.[2]) {
|
|
33
|
+
const data = require_utils$1.parseBodyJson((await fetchFn(`https://www.reddit.com/user/${userMatch[2]}/about.json`)).body);
|
|
32
34
|
const icon = data?.data?.icon_img || data?.data?.snoovatar_img;
|
|
33
35
|
if (require_utils$1.isNonEmptyString(icon)) return [{ uri: icon }];
|
|
34
36
|
}
|
|
@@ -2,13 +2,13 @@ import { isHostOf } from "../../../common/utils.js";
|
|
|
2
2
|
import { isNonEmptyString, parseBodyJson } from "../../utils.js";
|
|
3
3
|
import { hosts } from "../../../feeds/platform/handlers/reddit.js";
|
|
4
4
|
//#region src/favicons/platform/handlers/reddit.ts
|
|
5
|
+
const subredditRegex = /^\/r\/([^/.]+)/;
|
|
6
|
+
const userRegex = /^\/(u|user)\/([^/.]+)/;
|
|
5
7
|
const isSubredditPath = (pathname) => {
|
|
6
|
-
|
|
7
|
-
return segments.length >= 2 && segments[0] === "r";
|
|
8
|
+
return subredditRegex.test(pathname);
|
|
8
9
|
};
|
|
9
10
|
const isUserPath = (pathname) => {
|
|
10
|
-
|
|
11
|
-
return segments.length >= 2 && (segments[0] === "u" || segments[0] === "user");
|
|
11
|
+
return userRegex.test(pathname);
|
|
12
12
|
};
|
|
13
13
|
const redditHandler = {
|
|
14
14
|
match: (url) => {
|
|
@@ -22,13 +22,15 @@ const redditHandler = {
|
|
|
22
22
|
if (!fetchFn) return [];
|
|
23
23
|
try {
|
|
24
24
|
const { pathname } = new URL(url);
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const subredditMatch = pathname.match(subredditRegex);
|
|
26
|
+
if (subredditMatch?.[1]) {
|
|
27
|
+
const data = parseBodyJson((await fetchFn(`https://www.reddit.com/r/${subredditMatch[1]}/about.json`)).body);
|
|
27
28
|
const icon = data?.data?.community_icon?.split("?")[0] || data?.data?.icon_img;
|
|
28
29
|
if (isNonEmptyString(icon)) return [{ uri: icon }];
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const userMatch = pathname.match(userRegex);
|
|
32
|
+
if (userMatch?.[2]) {
|
|
33
|
+
const data = parseBodyJson((await fetchFn(`https://www.reddit.com/user/${userMatch[2]}/about.json`)).body);
|
|
32
34
|
const icon = data?.data?.icon_img || data?.data?.snoovatar_img;
|
|
33
35
|
if (isNonEmptyString(icon)) return [{ uri: icon }];
|
|
34
36
|
}
|
package/dist/feeds/defaults.cjs
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
const require_codeberg = require("./platform/handlers/codeberg.cjs");
|
|
2
2
|
const require_deviantart = require("./platform/handlers/deviantart.cjs");
|
|
3
|
+
const require_devto = require("./platform/handlers/devto.cjs");
|
|
3
4
|
const require_github = require("./platform/handlers/github.cjs");
|
|
4
5
|
const require_githubGist = require("./platform/handlers/githubGist.cjs");
|
|
6
|
+
const require_gitlab = require("./platform/handlers/gitlab.cjs");
|
|
5
7
|
const require_lobsters = require("./platform/handlers/lobsters.cjs");
|
|
6
8
|
const require_reddit = require("./platform/handlers/reddit.cjs");
|
|
7
9
|
const require_sourceforge = require("./platform/handlers/sourceforge.cjs");
|
|
8
10
|
const require_tumblr = require("./platform/handlers/tumblr.cjs");
|
|
11
|
+
const require_applePodcasts = require("./platform/handlers/applePodcasts.cjs");
|
|
9
12
|
const require_behance = require("./platform/handlers/behance.cjs");
|
|
10
13
|
const require_blogspot = require("./platform/handlers/blogspot.cjs");
|
|
11
14
|
const require_bluesky = require("./platform/handlers/bluesky.cjs");
|
|
12
15
|
const require_csdn = require("./platform/handlers/csdn.cjs");
|
|
13
16
|
const require_dailymotion = require("./platform/handlers/dailymotion.cjs");
|
|
14
|
-
const require_devto = require("./platform/handlers/devto.cjs");
|
|
15
17
|
const require_douban = require("./platform/handlers/douban.cjs");
|
|
16
|
-
const require_gitlab = require("./platform/handlers/gitlab.cjs");
|
|
17
18
|
const require_goodreads = require("./platform/handlers/goodreads.cjs");
|
|
18
19
|
const require_hashnode = require("./platform/handlers/hashnode.cjs");
|
|
19
20
|
const require_hatenablog = require("./platform/handlers/hatenablog.cjs");
|
|
@@ -123,6 +124,7 @@ const defaultHtmlOptions = {
|
|
|
123
124
|
const defaultHeadersOptions = { linkSelectors };
|
|
124
125
|
const defaultGuessOptions = { uris: urisBalanced };
|
|
125
126
|
const defaultPlatformOptions = { handlers: [
|
|
127
|
+
require_applePodcasts.applePodcastsHandler,
|
|
126
128
|
require_behance.behanceHandler,
|
|
127
129
|
require_blogspot.blogspotHandler,
|
|
128
130
|
require_bluesky.blueskyHandler,
|
package/dist/feeds/defaults.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { codebergHandler } from "./platform/handlers/codeberg.js";
|
|
2
2
|
import { deviantartHandler } from "./platform/handlers/deviantart.js";
|
|
3
|
+
import { devtoHandler } from "./platform/handlers/devto.js";
|
|
3
4
|
import { githubHandler } from "./platform/handlers/github.js";
|
|
4
5
|
import { githubGistHandler } from "./platform/handlers/githubGist.js";
|
|
6
|
+
import { gitlabHandler } from "./platform/handlers/gitlab.js";
|
|
5
7
|
import { lobstersHandler } from "./platform/handlers/lobsters.js";
|
|
6
8
|
import { redditHandler } from "./platform/handlers/reddit.js";
|
|
7
9
|
import { sourceforgeHandler } from "./platform/handlers/sourceforge.js";
|
|
8
10
|
import { tumblrHandler } from "./platform/handlers/tumblr.js";
|
|
11
|
+
import { applePodcastsHandler } from "./platform/handlers/applePodcasts.js";
|
|
9
12
|
import { behanceHandler } from "./platform/handlers/behance.js";
|
|
10
13
|
import { blogspotHandler } from "./platform/handlers/blogspot.js";
|
|
11
14
|
import { blueskyHandler } from "./platform/handlers/bluesky.js";
|
|
12
15
|
import { csdnHandler } from "./platform/handlers/csdn.js";
|
|
13
16
|
import { dailymotionHandler } from "./platform/handlers/dailymotion.js";
|
|
14
|
-
import { devtoHandler } from "./platform/handlers/devto.js";
|
|
15
17
|
import { doubanHandler } from "./platform/handlers/douban.js";
|
|
16
|
-
import { gitlabHandler } from "./platform/handlers/gitlab.js";
|
|
17
18
|
import { goodreadsHandler } from "./platform/handlers/goodreads.js";
|
|
18
19
|
import { hashnodeHandler } from "./platform/handlers/hashnode.js";
|
|
19
20
|
import { hatenablogHandler } from "./platform/handlers/hatenablog.js";
|
|
@@ -123,6 +124,7 @@ const defaultHtmlOptions = {
|
|
|
123
124
|
const defaultHeadersOptions = { linkSelectors };
|
|
124
125
|
const defaultGuessOptions = { uris: urisBalanced };
|
|
125
126
|
const defaultPlatformOptions = { handlers: [
|
|
127
|
+
applePodcastsHandler,
|
|
126
128
|
behanceHandler,
|
|
127
129
|
blogspotHandler,
|
|
128
130
|
blueskyHandler,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const require_utils = require("../../../common/utils.cjs");
|
|
2
|
+
//#region src/feeds/platform/handlers/applePodcasts.ts
|
|
3
|
+
const hosts = ["podcasts.apple.com"];
|
|
4
|
+
const podcastRegex = /^\/[a-z]{2}\/podcast\/[^/]+\/id\d+/;
|
|
5
|
+
const feedUrlRegex = /"feedUrl"\s*:\s*"([^"]+)"/;
|
|
6
|
+
const extractFeedUrlFromContent = (content) => {
|
|
7
|
+
return content.match(feedUrlRegex)?.[1];
|
|
8
|
+
};
|
|
9
|
+
const applePodcastsHandler = {
|
|
10
|
+
match: (url) => {
|
|
11
|
+
if (!require_utils.isHostOf(url, hosts)) return false;
|
|
12
|
+
const { pathname } = new URL(url);
|
|
13
|
+
return podcastRegex.test(pathname);
|
|
14
|
+
},
|
|
15
|
+
resolve: (_url, content) => {
|
|
16
|
+
if (!content) return [];
|
|
17
|
+
const feedUrl = extractFeedUrlFromContent(content);
|
|
18
|
+
if (!feedUrl) return [];
|
|
19
|
+
return [{
|
|
20
|
+
uri: feedUrl,
|
|
21
|
+
hint: require_utils.composeHint("apple-podcasts:podcast")
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
exports.applePodcastsHandler = applePodcastsHandler;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { composeHint, isHostOf } from "../../../common/utils.js";
|
|
2
|
+
//#region src/feeds/platform/handlers/applePodcasts.ts
|
|
3
|
+
const hosts = ["podcasts.apple.com"];
|
|
4
|
+
const podcastRegex = /^\/[a-z]{2}\/podcast\/[^/]+\/id\d+/;
|
|
5
|
+
const feedUrlRegex = /"feedUrl"\s*:\s*"([^"]+)"/;
|
|
6
|
+
const extractFeedUrlFromContent = (content) => {
|
|
7
|
+
return content.match(feedUrlRegex)?.[1];
|
|
8
|
+
};
|
|
9
|
+
const applePodcastsHandler = {
|
|
10
|
+
match: (url) => {
|
|
11
|
+
if (!isHostOf(url, hosts)) return false;
|
|
12
|
+
const { pathname } = new URL(url);
|
|
13
|
+
return podcastRegex.test(pathname);
|
|
14
|
+
},
|
|
15
|
+
resolve: (_url, content) => {
|
|
16
|
+
if (!content) return [];
|
|
17
|
+
const feedUrl = extractFeedUrlFromContent(content);
|
|
18
|
+
if (!feedUrl) return [];
|
|
19
|
+
return [{
|
|
20
|
+
uri: feedUrl,
|
|
21
|
+
hint: composeHint("apple-podcasts:podcast")
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { applePodcastsHandler };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/behance.ts
|
|
3
3
|
const hosts = ["behance.net", "www.behance.net"];
|
|
4
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_-]+)(?:\/(appreciated))?\/?$/;
|
|
5
5
|
const excludedPaths = [
|
|
6
6
|
"search",
|
|
7
7
|
"galleries",
|
|
@@ -27,7 +27,7 @@ const behanceHandler = {
|
|
|
27
27
|
},
|
|
28
28
|
resolve: (url) => {
|
|
29
29
|
const { pathname } = new URL(url);
|
|
30
|
-
const userMatch = pathname.match(
|
|
30
|
+
const userMatch = pathname.match(userRegex);
|
|
31
31
|
if (userMatch?.[1]) {
|
|
32
32
|
const username = userMatch[1];
|
|
33
33
|
const subpage = userMatch[2];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/behance.ts
|
|
3
3
|
const hosts = ["behance.net", "www.behance.net"];
|
|
4
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_-]+)(?:\/(appreciated))?\/?$/;
|
|
5
5
|
const excludedPaths = [
|
|
6
6
|
"search",
|
|
7
7
|
"galleries",
|
|
@@ -27,7 +27,7 @@ const behanceHandler = {
|
|
|
27
27
|
},
|
|
28
28
|
resolve: (url) => {
|
|
29
29
|
const { pathname } = new URL(url);
|
|
30
|
-
const userMatch = pathname.match(
|
|
30
|
+
const userMatch = pathname.match(userRegex);
|
|
31
31
|
if (userMatch?.[1]) {
|
|
32
32
|
const username = userMatch[1];
|
|
33
33
|
const subpage = userMatch[2];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/dailymotion.ts
|
|
3
3
|
const hosts = ["dailymotion.com", "www.dailymotion.com"];
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_-]+)$/;
|
|
5
|
+
const playlistRegex = /^\/playlist\/([a-zA-Z0-9_-]+)/;
|
|
6
6
|
const excludedPaths = [
|
|
7
7
|
"signin",
|
|
8
8
|
"signout",
|
|
@@ -50,12 +50,12 @@ const dailymotionHandler = {
|
|
|
50
50
|
},
|
|
51
51
|
resolve: (url) => {
|
|
52
52
|
const { pathname } = new URL(url);
|
|
53
|
-
const playlistMatch = pathname.match(
|
|
53
|
+
const playlistMatch = pathname.match(playlistRegex);
|
|
54
54
|
if (playlistMatch?.[1]) return [{
|
|
55
55
|
uri: `https://www.dailymotion.com/rss/playlist/${playlistMatch[1]}`,
|
|
56
56
|
hint: require_utils.composeHint("dailymotion:playlist")
|
|
57
57
|
}];
|
|
58
|
-
const userMatch = pathname.match(
|
|
58
|
+
const userMatch = pathname.match(userRegex);
|
|
59
59
|
if (userMatch?.[1]) {
|
|
60
60
|
const username = userMatch[1];
|
|
61
61
|
if (!require_utils.isAnyOf(username, excludedPaths)) return [{
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/dailymotion.ts
|
|
3
3
|
const hosts = ["dailymotion.com", "www.dailymotion.com"];
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_-]+)$/;
|
|
5
|
+
const playlistRegex = /^\/playlist\/([a-zA-Z0-9_-]+)/;
|
|
6
6
|
const excludedPaths = [
|
|
7
7
|
"signin",
|
|
8
8
|
"signout",
|
|
@@ -50,12 +50,12 @@ const dailymotionHandler = {
|
|
|
50
50
|
},
|
|
51
51
|
resolve: (url) => {
|
|
52
52
|
const { pathname } = new URL(url);
|
|
53
|
-
const playlistMatch = pathname.match(
|
|
53
|
+
const playlistMatch = pathname.match(playlistRegex);
|
|
54
54
|
if (playlistMatch?.[1]) return [{
|
|
55
55
|
uri: `https://www.dailymotion.com/rss/playlist/${playlistMatch[1]}`,
|
|
56
56
|
hint: composeHint("dailymotion:playlist")
|
|
57
57
|
}];
|
|
58
|
-
const userMatch = pathname.match(
|
|
58
|
+
const userMatch = pathname.match(userRegex);
|
|
59
59
|
if (userMatch?.[1]) {
|
|
60
60
|
const username = userMatch[1];
|
|
61
61
|
if (!isAnyOf(username, excludedPaths)) return [{
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_utils = require("../../../common/utils.cjs");
|
|
2
2
|
//#region src/feeds/platform/handlers/devto.ts
|
|
3
3
|
const hosts = ["dev.to", "www.dev.to"];
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_]+)\/?$/;
|
|
5
|
+
const tagRegex = /^\/t\/([^/]+)/;
|
|
6
6
|
const excludedPaths = [
|
|
7
7
|
"tag",
|
|
8
8
|
"tags",
|
|
@@ -28,7 +28,7 @@ const devtoHandler = {
|
|
|
28
28
|
},
|
|
29
29
|
resolve: (url) => {
|
|
30
30
|
const { pathname } = new URL(url);
|
|
31
|
-
const userMatch = pathname.match(
|
|
31
|
+
const userMatch = pathname.match(userRegex);
|
|
32
32
|
if (userMatch?.[1]) {
|
|
33
33
|
const username = userMatch[1];
|
|
34
34
|
if (!require_utils.isAnyOf(username, excludedPaths)) return [{
|
|
@@ -36,7 +36,7 @@ const devtoHandler = {
|
|
|
36
36
|
hint: require_utils.composeHint("devto:posts")
|
|
37
37
|
}];
|
|
38
38
|
}
|
|
39
|
-
const tagMatch = pathname.match(
|
|
39
|
+
const tagMatch = pathname.match(tagRegex);
|
|
40
40
|
if (tagMatch?.[1]) return [{
|
|
41
41
|
uri: `https://dev.to/feed/tag/${tagMatch[1]}`,
|
|
42
42
|
hint: require_utils.composeHint("devto:tag")
|
|
@@ -46,3 +46,5 @@ const devtoHandler = {
|
|
|
46
46
|
};
|
|
47
47
|
//#endregion
|
|
48
48
|
exports.devtoHandler = devtoHandler;
|
|
49
|
+
exports.excludedPaths = excludedPaths;
|
|
50
|
+
exports.hosts = hosts;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
|
|
2
2
|
//#region src/feeds/platform/handlers/devto.ts
|
|
3
3
|
const hosts = ["dev.to", "www.dev.to"];
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const userRegex = /^\/([a-zA-Z0-9_]+)\/?$/;
|
|
5
|
+
const tagRegex = /^\/t\/([^/]+)/;
|
|
6
6
|
const excludedPaths = [
|
|
7
7
|
"tag",
|
|
8
8
|
"tags",
|
|
@@ -28,7 +28,7 @@ const devtoHandler = {
|
|
|
28
28
|
},
|
|
29
29
|
resolve: (url) => {
|
|
30
30
|
const { pathname } = new URL(url);
|
|
31
|
-
const userMatch = pathname.match(
|
|
31
|
+
const userMatch = pathname.match(userRegex);
|
|
32
32
|
if (userMatch?.[1]) {
|
|
33
33
|
const username = userMatch[1];
|
|
34
34
|
if (!isAnyOf(username, excludedPaths)) return [{
|
|
@@ -36,7 +36,7 @@ const devtoHandler = {
|
|
|
36
36
|
hint: composeHint("devto:posts")
|
|
37
37
|
}];
|
|
38
38
|
}
|
|
39
|
-
const tagMatch = pathname.match(
|
|
39
|
+
const tagMatch = pathname.match(tagRegex);
|
|
40
40
|
if (tagMatch?.[1]) return [{
|
|
41
41
|
uri: `https://dev.to/feed/tag/${tagMatch[1]}`,
|
|
42
42
|
hint: composeHint("devto:tag")
|
|
@@ -45,4 +45,4 @@ const devtoHandler = {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
//#endregion
|
|
48
|
-
export { devtoHandler };
|
|
48
|
+
export { devtoHandler, excludedPaths, hosts };
|
|
@@ -5,8 +5,8 @@ const domains = [
|
|
|
5
5
|
"hatenablog.jp",
|
|
6
6
|
"hateblo.jp"
|
|
7
7
|
];
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const categoryRegex = /^\/archive\/category\/([^/]+)/;
|
|
9
|
+
const authorRegex = /^\/archive\/author\/([^/]+)/;
|
|
10
10
|
const hatenablogHandler = {
|
|
11
11
|
match: (url) => {
|
|
12
12
|
return require_utils.isSubdomainOf(url, domains);
|
|
@@ -14,7 +14,7 @@ const hatenablogHandler = {
|
|
|
14
14
|
resolve: (url) => {
|
|
15
15
|
const { origin, pathname } = new URL(url);
|
|
16
16
|
const uris = [];
|
|
17
|
-
const categoryMatch = pathname.match(
|
|
17
|
+
const categoryMatch = pathname.match(categoryRegex);
|
|
18
18
|
if (categoryMatch?.[1]) {
|
|
19
19
|
const category = categoryMatch[1];
|
|
20
20
|
uris.push({
|
|
@@ -26,7 +26,7 @@ const hatenablogHandler = {
|
|
|
26
26
|
hint: require_utils.composeHint("hatenablog:category-atom")
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
-
const authorMatch = pathname.match(
|
|
29
|
+
const authorMatch = pathname.match(authorRegex);
|
|
30
30
|
if (authorMatch?.[1]) {
|
|
31
31
|
const author = authorMatch[1];
|
|
32
32
|
uris.push({
|
|
@@ -5,8 +5,8 @@ const domains = [
|
|
|
5
5
|
"hatenablog.jp",
|
|
6
6
|
"hateblo.jp"
|
|
7
7
|
];
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const categoryRegex = /^\/archive\/category\/([^/]+)/;
|
|
9
|
+
const authorRegex = /^\/archive\/author\/([^/]+)/;
|
|
10
10
|
const hatenablogHandler = {
|
|
11
11
|
match: (url) => {
|
|
12
12
|
return isSubdomainOf(url, domains);
|
|
@@ -14,7 +14,7 @@ const hatenablogHandler = {
|
|
|
14
14
|
resolve: (url) => {
|
|
15
15
|
const { origin, pathname } = new URL(url);
|
|
16
16
|
const uris = [];
|
|
17
|
-
const categoryMatch = pathname.match(
|
|
17
|
+
const categoryMatch = pathname.match(categoryRegex);
|
|
18
18
|
if (categoryMatch?.[1]) {
|
|
19
19
|
const category = categoryMatch[1];
|
|
20
20
|
uris.push({
|
|
@@ -26,7 +26,7 @@ const hatenablogHandler = {
|
|
|
26
26
|
hint: composeHint("hatenablog:category-atom")
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
-
const authorMatch = pathname.match(
|
|
29
|
+
const authorMatch = pathname.match(authorRegex);
|
|
30
30
|
if (authorMatch?.[1]) {
|
|
31
31
|
const author = authorMatch[1];
|
|
32
32
|
uris.push({
|
|
@@ -16,11 +16,11 @@ const sorts = [
|
|
|
16
16
|
"top-sellers",
|
|
17
17
|
"on-sale"
|
|
18
18
|
];
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
19
|
+
const byUserRegex = /^\/games\/by-([^/]+)/;
|
|
20
|
+
const tagRegex = /^\/games\/tag-([^/]+)/;
|
|
21
|
+
const sortRegex = /^\/games\/([^/.]+)/;
|
|
22
|
+
const sectionRegex = /^\/([^/.]+)/;
|
|
23
|
+
const gameRegex = /^\/([^/]+)/;
|
|
24
24
|
const itchioHandler = {
|
|
25
25
|
match: (url) => {
|
|
26
26
|
return require_utils.isHostOf(url, mainHosts) || require_utils.isSubdomainOf(url, "itch.io");
|
|
@@ -30,7 +30,7 @@ const itchioHandler = {
|
|
|
30
30
|
const lowerHostname = hostname.toLowerCase();
|
|
31
31
|
if (!mainHosts.includes(lowerHostname) && lowerHostname.endsWith(".itch.io")) {
|
|
32
32
|
const creator = lowerHostname.replace(".itch.io", "");
|
|
33
|
-
const gameMatch = pathname.match(
|
|
33
|
+
const gameMatch = pathname.match(gameRegex);
|
|
34
34
|
if (gameMatch?.[1]) return [{
|
|
35
35
|
uri: `https://${creator}.itch.io/${gameMatch[1]}/devlog.rss`,
|
|
36
36
|
hint: require_utils.composeHint("itchio:devlog")
|
|
@@ -40,17 +40,17 @@ const itchioHandler = {
|
|
|
40
40
|
hint: require_utils.composeHint("itchio:games")
|
|
41
41
|
}];
|
|
42
42
|
}
|
|
43
|
-
const byUserMatch = pathname.match(
|
|
43
|
+
const byUserMatch = pathname.match(byUserRegex);
|
|
44
44
|
if (byUserMatch?.[1]) return [{
|
|
45
45
|
uri: `https://itch.io/games/by-${byUserMatch[1]}.xml`,
|
|
46
46
|
hint: require_utils.composeHint("itchio:games")
|
|
47
47
|
}];
|
|
48
|
-
const tagMatch = pathname.match(
|
|
48
|
+
const tagMatch = pathname.match(tagRegex);
|
|
49
49
|
if (tagMatch?.[1]) return [{
|
|
50
50
|
uri: `https://itch.io/games/tag-${tagMatch[1]}.xml`,
|
|
51
51
|
hint: require_utils.composeHint("itchio:tag")
|
|
52
52
|
}];
|
|
53
|
-
const sortMatch = pathname.match(
|
|
53
|
+
const sortMatch = pathname.match(sortRegex);
|
|
54
54
|
if (sortMatch?.[1] && require_utils.isAnyOf(sortMatch[1], sorts)) return [{
|
|
55
55
|
uri: `https://itch.io/games/${sortMatch[1]}.xml`,
|
|
56
56
|
hint: require_utils.composeHint("itchio:games")
|
|
@@ -63,7 +63,7 @@ const itchioHandler = {
|
|
|
63
63
|
uri: "https://itch.io/devlogs.xml",
|
|
64
64
|
hint: require_utils.composeHint("itchio:devlog")
|
|
65
65
|
}];
|
|
66
|
-
const sectionMatch = pathname.match(
|
|
66
|
+
const sectionMatch = pathname.match(sectionRegex);
|
|
67
67
|
if (sectionMatch?.[1] && require_utils.isAnyOf(sectionMatch[1], sections)) return [{
|
|
68
68
|
uri: `https://itch.io/${sectionMatch[1]}.xml`,
|
|
69
69
|
hint: require_utils.composeHint("itchio:section")
|