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.
Files changed (91) hide show
  1. package/dist/common/discover/utils.cjs +11 -8
  2. package/dist/common/discover/utils.js +11 -8
  3. package/dist/common/locales.cjs +11 -4
  4. package/dist/common/locales.js +11 -4
  5. package/dist/common/types.d.cts +5 -4
  6. package/dist/common/types.d.ts +5 -4
  7. package/dist/common/uris/guess/utils.cjs +1 -1
  8. package/dist/common/uris/guess/utils.js +1 -1
  9. package/dist/common/uris/headers/index.cjs +1 -1
  10. package/dist/common/uris/headers/index.js +1 -1
  11. package/dist/common/uris/platform/types.d.cts +2 -2
  12. package/dist/common/uris/platform/types.d.ts +2 -2
  13. package/dist/common/utils.cjs +5 -1
  14. package/dist/common/utils.js +5 -1
  15. package/dist/favicons/defaults.cjs +6 -1
  16. package/dist/favicons/defaults.js +6 -1
  17. package/dist/favicons/platform/handlers/bluesky.cjs +1 -1
  18. package/dist/favicons/platform/handlers/bluesky.js +1 -1
  19. package/dist/favicons/platform/handlers/codeberg.cjs +4 -3
  20. package/dist/favicons/platform/handlers/codeberg.js +4 -3
  21. package/dist/favicons/platform/handlers/devto.cjs +31 -0
  22. package/dist/favicons/platform/handlers/devto.js +31 -0
  23. package/dist/favicons/platform/handlers/github.cjs +4 -3
  24. package/dist/favicons/platform/handlers/github.js +4 -3
  25. package/dist/favicons/platform/handlers/githubGist.cjs +4 -3
  26. package/dist/favicons/platform/handlers/githubGist.js +4 -3
  27. package/dist/favicons/platform/handlers/gitlab.cjs +38 -0
  28. package/dist/favicons/platform/handlers/gitlab.js +38 -0
  29. package/dist/favicons/platform/handlers/lobsters.cjs +3 -3
  30. package/dist/favicons/platform/handlers/lobsters.js +3 -3
  31. package/dist/favicons/platform/handlers/mastodon.cjs +5 -3
  32. package/dist/favicons/platform/handlers/mastodon.js +5 -3
  33. package/dist/favicons/platform/handlers/reddit.cjs +10 -8
  34. package/dist/favicons/platform/handlers/reddit.js +10 -8
  35. package/dist/feeds/defaults.cjs +4 -2
  36. package/dist/feeds/defaults.js +4 -2
  37. package/dist/feeds/platform/handlers/applePodcasts.cjs +26 -0
  38. package/dist/feeds/platform/handlers/applePodcasts.js +26 -0
  39. package/dist/feeds/platform/handlers/behance.cjs +2 -2
  40. package/dist/feeds/platform/handlers/behance.js +2 -2
  41. package/dist/feeds/platform/handlers/dailymotion.cjs +4 -4
  42. package/dist/feeds/platform/handlers/dailymotion.js +4 -4
  43. package/dist/feeds/platform/handlers/devto.cjs +6 -4
  44. package/dist/feeds/platform/handlers/devto.js +5 -5
  45. package/dist/feeds/platform/handlers/gitlab.cjs +4 -0
  46. package/dist/feeds/platform/handlers/gitlab.js +1 -1
  47. package/dist/feeds/platform/handlers/hatenablog.cjs +4 -4
  48. package/dist/feeds/platform/handlers/hatenablog.js +4 -4
  49. package/dist/feeds/platform/handlers/itchio.cjs +10 -10
  50. package/dist/feeds/platform/handlers/itchio.js +10 -10
  51. package/dist/feeds/platform/handlers/lobsters.cjs +8 -8
  52. package/dist/feeds/platform/handlers/lobsters.js +8 -8
  53. package/dist/feeds/platform/handlers/mastodon.cjs +4 -4
  54. package/dist/feeds/platform/handlers/mastodon.js +4 -4
  55. package/dist/feeds/platform/handlers/medium.cjs +10 -10
  56. package/dist/feeds/platform/handlers/medium.js +10 -10
  57. package/dist/feeds/platform/handlers/paragraph.cjs +2 -2
  58. package/dist/feeds/platform/handlers/paragraph.js +2 -2
  59. package/dist/feeds/platform/handlers/producthunt.cjs +4 -4
  60. package/dist/feeds/platform/handlers/producthunt.js +4 -4
  61. package/dist/feeds/platform/handlers/reddit.cjs +10 -10
  62. package/dist/feeds/platform/handlers/reddit.js +10 -10
  63. package/dist/feeds/platform/handlers/stackExchange.cjs +6 -6
  64. package/dist/feeds/platform/handlers/stackExchange.js +6 -6
  65. package/dist/feeds/platform/handlers/steam.cjs +4 -4
  66. package/dist/feeds/platform/handlers/steam.js +4 -4
  67. package/dist/feeds/platform/handlers/substack.cjs +10 -3
  68. package/dist/feeds/platform/handlers/substack.js +11 -4
  69. package/dist/feeds/platform/handlers/tumblr.cjs +2 -2
  70. package/dist/feeds/platform/handlers/tumblr.js +2 -2
  71. package/dist/feeds/platform/handlers/v2ex.cjs +4 -4
  72. package/dist/feeds/platform/handlers/v2ex.js +4 -4
  73. package/dist/feeds/platform/handlers/vimeo.cjs +2 -2
  74. package/dist/feeds/platform/handlers/vimeo.js +2 -2
  75. package/dist/feeds/platform/handlers/wordpress.cjs +6 -6
  76. package/dist/feeds/platform/handlers/wordpress.js +6 -6
  77. package/dist/feeds/platform/handlers/ximalaya.cjs +2 -2
  78. package/dist/feeds/platform/handlers/ximalaya.js +2 -2
  79. package/dist/feeds/platform/handlers/youtube.cjs +36 -21
  80. package/dist/feeds/platform/handlers/youtube.js +36 -21
  81. package/dist/hubs/discover/index.cjs +1 -2
  82. package/dist/hubs/discover/index.js +1 -2
  83. package/dist/hubs/feed/index.cjs +4 -4
  84. package/dist/hubs/feed/index.js +4 -4
  85. package/dist/hubs/headers/index.cjs +2 -2
  86. package/dist/hubs/headers/index.js +2 -2
  87. package/dist/hubs/html/index.cjs +2 -2
  88. package/dist/hubs/html/index.js +2 -2
  89. package/package.json +4 -3
  90. package/dist/hubs/discover/utils.cjs +0 -18
  91. 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 userPathRegex = /^\/~([a-zA-Z0-9_-]+)/;
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) && userPathRegex.test(pathname);
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(userPathRegex);
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 userPathRegex = /^\/~([a-zA-Z0-9_-]+)/;
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) && userPathRegex.test(pathname);
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(userPathRegex);
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
- const segments = pathname.split("/").filter(Boolean);
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 data = require_utils$1.parseBodyJson((await fetchFn(`https://${hostname}/api/v1/accounts/lookup?acct=${pathname.split("/").filter(Boolean)[0].replace("@", "")}`)).body);
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
- const segments = pathname.split("/").filter(Boolean);
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 data = parseBodyJson((await fetchFn(`https://${hostname}/api/v1/accounts/lookup?acct=${pathname.split("/").filter(Boolean)[0].replace("@", "")}`)).body);
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
- const segments = pathname.split("/").filter(Boolean);
7
- return segments.length >= 2 && segments[0] === "r";
8
+ return subredditRegex.test(pathname);
8
9
  };
9
10
  const isUserPath = (pathname) => {
10
- const segments = pathname.split("/").filter(Boolean);
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
- if (isSubredditPath(pathname)) {
26
- const data = require_utils$1.parseBodyJson((await fetchFn(`https://www.reddit.com/r/${pathname.split("/").filter(Boolean)[1]}/about.json`)).body);
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
- if (isUserPath(pathname)) {
31
- const data = require_utils$1.parseBodyJson((await fetchFn(`https://www.reddit.com/user/${pathname.split("/").filter(Boolean)[1]}/about.json`)).body);
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
- const segments = pathname.split("/").filter(Boolean);
7
- return segments.length >= 2 && segments[0] === "r";
8
+ return subredditRegex.test(pathname);
8
9
  };
9
10
  const isUserPath = (pathname) => {
10
- const segments = pathname.split("/").filter(Boolean);
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
- if (isSubredditPath(pathname)) {
26
- const data = parseBodyJson((await fetchFn(`https://www.reddit.com/r/${pathname.split("/").filter(Boolean)[1]}/about.json`)).body);
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
- if (isUserPath(pathname)) {
31
- const data = parseBodyJson((await fetchFn(`https://www.reddit.com/user/${pathname.split("/").filter(Boolean)[1]}/about.json`)).body);
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
  }
@@ -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,
@@ -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 userPathRegex = /^\/([a-zA-Z0-9_-]+)(?:\/(appreciated))?\/?$/;
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(userPathRegex);
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 userPathRegex = /^\/([a-zA-Z0-9_-]+)(?:\/(appreciated))?\/?$/;
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(userPathRegex);
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 userPathRegex = /^\/([a-zA-Z0-9_-]+)$/;
5
- const playlistPathRegex = /^\/playlist\/([a-zA-Z0-9_-]+)/;
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(playlistPathRegex);
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(userPathRegex);
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 userPathRegex = /^\/([a-zA-Z0-9_-]+)$/;
5
- const playlistPathRegex = /^\/playlist\/([a-zA-Z0-9_-]+)/;
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(playlistPathRegex);
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(userPathRegex);
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 userPathRegex = /^\/([a-zA-Z0-9_]+)\/?$/;
5
- const tagPathRegex = /^\/t\/([^/]+)/;
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(userPathRegex);
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(tagPathRegex);
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 userPathRegex = /^\/([a-zA-Z0-9_]+)\/?$/;
5
- const tagPathRegex = /^\/t\/([^/]+)/;
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(userPathRegex);
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(tagPathRegex);
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 };
@@ -73,4 +73,8 @@ const gitlabHandler = {
73
73
  }
74
74
  };
75
75
  //#endregion
76
+ exports.excludedPaths = excludedPaths;
76
77
  exports.gitlabHandler = gitlabHandler;
78
+ exports.hosts = hosts;
79
+ exports.isGitlabHeaders = isGitlabHeaders;
80
+ exports.isGitlabHtml = isGitlabHtml;
@@ -73,4 +73,4 @@ const gitlabHandler = {
73
73
  }
74
74
  };
75
75
  //#endregion
76
- export { gitlabHandler };
76
+ export { excludedPaths, gitlabHandler, hosts, isGitlabHeaders, isGitlabHtml };
@@ -5,8 +5,8 @@ const domains = [
5
5
  "hatenablog.jp",
6
6
  "hateblo.jp"
7
7
  ];
8
- const categoryPathRegex = /^\/archive\/category\/([^/]+)/;
9
- const authorPathRegex = /^\/archive\/author\/([^/]+)/;
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(categoryPathRegex);
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(authorPathRegex);
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 categoryPathRegex = /^\/archive\/category\/([^/]+)/;
9
- const authorPathRegex = /^\/archive\/author\/([^/]+)/;
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(categoryPathRegex);
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(authorPathRegex);
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 byUserPathRegex = /^\/games\/by-([^/]+)/;
20
- const tagPathRegex = /^\/games\/tag-([^/]+)/;
21
- const sortPathRegex = /^\/games\/([^/.]+)/;
22
- const sectionPathRegex = /^\/([^/.]+)/;
23
- const gamePathRegex = /^\/([^/]+)/;
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(gamePathRegex);
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(byUserPathRegex);
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(tagPathRegex);
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(sortPathRegex);
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(sectionPathRegex);
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")