diff --git a/src/services/__tests__/__snapshots__/chrome-crawler.test.ts.snap b/src/services/__tests__/__snapshots__/chrome-crawler.test.ts.snap
index 5bb8029..f8f1824 100644
--- a/src/services/__tests__/__snapshots__/chrome-crawler.test.ts.snap
+++ b/src/services/__tests__/__snapshots__/chrome-crawler.test.ts.snap
@@ -223,3 +223,116 @@ Absolutely! Blync is 100% secure.
"weeklyActiveUsers": 40,
}
`;
+
+exports[`Chrome Web Store Crawler should extract extension details from 2026-04-17-mgmdkjcljneegjfajchedjpdhbadklcf.html 1`] = `
+{
+ "iconUrl": "https://lh3.googleusercontent.com/lTplXRdnpEB-7DGRa_1nCKao_3aJ3C_e-GNVs9tQV9hDUXgupc1SsW6OrruxgrkSdFBSOqeia56YWgJI2IgpV1MK49s=s256",
+ "id": "mgmdkjcljneegjfajchedjpdhbadklcf",
+ "lastUpdated": "January 27, 2024",
+ "longDescription":
+"Watch anime faster than ever! The Anime Skip Player lets you automatically skip intros, outros, or anything else that you don't want to watch.
+Anime Skip replaces the video player of streaming services with it's own, standardizing the viewing experience.
+
+✨ Features
+ • Customize what you want to watch and skip the rest
+ • Change the playback speed
+ • Customize your keyboard shortcuts
+ • Multiple color themes
+ • Screenshots
+ • Contribute timestamps and episode info for the community
+
+🌐 Streaming Services
+ • Crunchyroll
+ • Aniwave
+ • Aniwatch
+With more coming soon!
+
+☔ Safe
+The extension is open sourced!
+ https://github.com/anime-skip/web-extension
+
+💕 Enjoying Anime Skip?
+Leave a 5 star review! Reviews help build trust and attract new users, which means more timestamps!"
+,
+ "name": "Anime Skip Player",
+ "rating": 4.4,
+ "reviewCount": 42,
+ "screenshots": [
+ {
+ "index": 3,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/3",
+ "rawUrl": "https://lh3.googleusercontent.com/tIwJ7CWPxY411qKw2_TEkdGAorjCnP8OQJMtLZ1t_4Njx62uP4a58xE8BUzvKpghVoGn4ukFYrw4H5gB-I5WQiAx=s1280",
+ },
+ {
+ "index": 4,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/4",
+ "rawUrl": "https://lh3.googleusercontent.com/d9aFrUwRB5QZTK8HNxJJgZ1Mb4FGIdLlTqbTeO7RfIl9YlZvO4_gRchpnjX5g9OTEocfkiS3ge1e257UBGt17iGKACI=s1280",
+ },
+ {
+ "index": 0,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/0",
+ "rawUrl": "https://lh3.googleusercontent.com/5PBHI8N5FXG2_kJY69KgVUotwRLdxvkzjUnkkXIyFMS8F3y1mtqfIWwkfRodqDH-wcCw9iEObpzXH6vfJ2tiqugsWA=s1280",
+ },
+ {
+ "index": 1,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/1",
+ "rawUrl": "https://lh3.googleusercontent.com/OZ2XFtw0RIGPxeVrBubaZWrK9KJSKz51bwFI6z8m3ZLow9RPN_-zEmxuz48HUfTDG51J97LclUd2ydtFfyRbKm0xMA=s1280",
+ },
+ {
+ "index": 2,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/2",
+ "rawUrl": "https://lh3.googleusercontent.com/lv8T04EmvcMaRAVJ6EiqG5yLa-EUE4l0sTtPzKWLXflkbkkV6dGd1OIhSCJUFj3DlRLc5SWFpuboCAw9LcY73sH7x1M=s1280",
+ },
+ {
+ "index": 3,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/3",
+ "rawUrl": "https://lh3.googleusercontent.com/tIwJ7CWPxY411qKw2_TEkdGAorjCnP8OQJMtLZ1t_4Njx62uP4a58xE8BUzvKpghVoGn4ukFYrw4H5gB-I5WQiAx=s1280",
+ },
+ {
+ "index": 4,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/4",
+ "rawUrl": "https://lh3.googleusercontent.com/d9aFrUwRB5QZTK8HNxJJgZ1Mb4FGIdLlTqbTeO7RfIl9YlZvO4_gRchpnjX5g9OTEocfkiS3ge1e257UBGt17iGKACI=s1280",
+ },
+ {
+ "index": 0,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/0",
+ "rawUrl": "https://lh3.googleusercontent.com/5PBHI8N5FXG2_kJY69KgVUotwRLdxvkzjUnkkXIyFMS8F3y1mtqfIWwkfRodqDH-wcCw9iEObpzXH6vfJ2tiqugsWA=s1280",
+ },
+ {
+ "index": 1,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/1",
+ "rawUrl": "https://lh3.googleusercontent.com/OZ2XFtw0RIGPxeVrBubaZWrK9KJSKz51bwFI6z8m3ZLow9RPN_-zEmxuz48HUfTDG51J97LclUd2ydtFfyRbKm0xMA=s1280",
+ },
+ {
+ "index": 0,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/0",
+ "rawUrl": "https://lh3.googleusercontent.com/5PBHI8N5FXG2_kJY69KgVUotwRLdxvkzjUnkkXIyFMS8F3y1mtqfIWwkfRodqDH-wcCw9iEObpzXH6vfJ2tiqugsWA=s1280",
+ },
+ {
+ "index": 1,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/1",
+ "rawUrl": "https://lh3.googleusercontent.com/OZ2XFtw0RIGPxeVrBubaZWrK9KJSKz51bwFI6z8m3ZLow9RPN_-zEmxuz48HUfTDG51J97LclUd2ydtFfyRbKm0xMA=s1280",
+ },
+ {
+ "index": 2,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/2",
+ "rawUrl": "https://lh3.googleusercontent.com/lv8T04EmvcMaRAVJ6EiqG5yLa-EUE4l0sTtPzKWLXflkbkkV6dGd1OIhSCJUFj3DlRLc5SWFpuboCAw9LcY73sH7x1M=s1280",
+ },
+ {
+ "index": 3,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/3",
+ "rawUrl": "https://lh3.googleusercontent.com/tIwJ7CWPxY411qKw2_TEkdGAorjCnP8OQJMtLZ1t_4Njx62uP4a58xE8BUzvKpghVoGn4ukFYrw4H5gB-I5WQiAx=s1280",
+ },
+ {
+ "index": 4,
+ "indexUrl": "http://localhost:3000/api/rest/chrome-web-store/mgmdkjcljneegjfajchedjpdhbadklcf/screenshots/4",
+ "rawUrl": "https://lh3.googleusercontent.com/d9aFrUwRB5QZTK8HNxJJgZ1Mb4FGIdLlTqbTeO7RfIl9YlZvO4_gRchpnjX5g9OTEocfkiS3ge1e257UBGt17iGKACI=s1280",
+ },
+ ],
+ "shortDescription": "Custom video player for anime streaming websites. Skip intros, outros, and more.",
+ "storeUrl": "https://chromewebstore.google.com/detail/anime-skip-player/mgmdkjcljneegjfajchedjpdhbadklcf",
+ "users": 2000,
+ "version": "2.0.3",
+ "weeklyActiveUsers": 2000,
+}
+`;
diff --git a/src/services/__tests__/fixtures/chrome-web-store/2026-04-17-mgmdkjcljneegjfajchedjpdhbadklcf.html b/src/services/__tests__/fixtures/chrome-web-store/2026-04-17-mgmdkjcljneegjfajchedjpdhbadklcf.html
new file mode 100644
index 0000000..4d6294b
--- /dev/null
+++ b/src/services/__tests__/fixtures/chrome-web-store/2026-04-17-mgmdkjcljneegjfajchedjpdhbadklcf.html
@@ -0,0 +1,163 @@
+
Anime Skip Player - Chrome Web Store
Anime Skip Player Created by the owner of the listed website. The publisher has a good record with no history of violations.
Learn more. anime-skip.com Follows recommended practices for Chrome extensions.
Learn more. Featured 4.4 (42 ratings
)
Ratings are updated daily and may not reflect the most recent reviews.
Custom video player for anime streaming websites. Skip intros, outros, and more.
Watch anime faster than ever! The Anime Skip Player lets you automatically skip intros, outros, or anything else that you don't want to watch.
+
+Anime Skip replaces the video player of streaming services with it's own, standardizing the viewing experience.
+
+
+✨ Features
+
+ • Customize what you want to watch and skip the rest
+ • Change the playback speed
+ • Customize your keyboard shortcuts
+ • Multiple color themes
+ • Screenshots
+ • Contribute timestamps and episode info for the community
+
+
+🌐 Streaming Services
+
+ • Crunchyroll
+ • Aniwave
+ • Aniwatch
+
+With more coming soon!
+
+
+☔ Safe
+
+The extension is open sourced!
+
+ https://github.com/anime-skip/web-extension
+
+
+💕 Enjoying Anime Skip?
+
+Leave a 5 star review! Reviews help build trust and attract new users, which means more timestamps!
Privacy Manage extensions and learn how they're being used in your organization
Try Chrome Enterprise Core
Anime Skip Player has disclosed the following information regarding the collection and usage of your data. More detailed information can be found in the developer's privacy policy .
Anime Skip Player handles the following: Personally identifiable information Authentication information This developer declares that your data is Not being sold to third parties, outside of the approved use cases Not being used or transferred for purposes that are unrelated to the item's core functionality Not being used or transferred to determine creditworthiness or for lending purposes
Support For help with questions, suggestions, or problems, visit the developer's support site
Related AutoSkippy | Skip Intros, Ads, Recaps & More
4.2 Average rating 4.2 out of 5 stars.
Automatically skip intros, ads, recaps and next episode in Netflix, Prime Video, Disney+, Crave and more.
Netflix Skipper: skip intros, recaps & more [QVI]
4.1 Average rating 4.1 out of 5 stars.
Automatically skip intros, recaps, 'Are you still watching?' and next episode on Netflix
Netflix, Prime, JioCinema, Hotstar - Auto intro skipper
3.9 Average rating 3.9 out of 5 stars.
Netflix, Prime, JioCinema, Hotstar Skip Intros Automatically
Crunchyroll Filler Labeler
3.9 Average rating 3.9 out of 5 stars.
Watch anime on Crunchyroll while knowing which episodes are fillers. Supports the new design!
+
+Never watch One Piece, Naruto,…
Aniskip
4.0 Average rating 4.0 out of 5 stars.
An extension which gives the option to skip anime opening and endings on various streaming sites
EpisodeBrain: Anime & TV Show Tracker
4.3 Average rating 4.3 out of 5 stars.
Stop worrying about where you left off for your favorite TV shows and Anime on streaming websites, we'll keep track of it for you.
Myanimelist Redesign
4.8 Average rating 4.8 out of 5 stars.
A redesigned user interface for Myanimelist with multiple themes and layout improvements
MAL-Sync
4.9 Average rating 4.9 out of 5 stars.
Integrates MyAnimeList/AniList/Kitsu/Simkl/Shikimori into various sites, with auto episode tracking.
Funimation Fullscreen
3.7 Average rating 3.7 out of 5 stars.
Persistent Fullscreen, Full Window, and Popup Modes for the Funimation webplayer.
Improve Crunchyroll
4.5 Average rating 4.5 out of 5 stars.
Enhance Crunchyroll: theater mode, skip intros/outros, mark as watched/not watched, fast forward/backward buttons, ...
Search Anime by Screenshot
4.4 Average rating 4.4 out of 5 stars.
Use anime screenshots to search the scene it is taken from.
jut.su Next-Series
4.1 Average rating 4.1 out of 5 stars.
An extension for jut.su that allows videos to autoplay, automatically skip anime intros, and automatically play the next episode.
AutoSkippy | Skip Intros, Ads, Recaps & More
4.2 Average rating 4.2 out of 5 stars.
Automatically skip intros, ads, recaps and next episode in Netflix, Prime Video, Disney+, Crave and more.
Netflix Skipper: skip intros, recaps & more [QVI]
4.1 Average rating 4.1 out of 5 stars.
Automatically skip intros, recaps, 'Are you still watching?' and next episode on Netflix
Netflix, Prime, JioCinema, Hotstar - Auto intro skipper
3.9 Average rating 3.9 out of 5 stars.
Netflix, Prime, JioCinema, Hotstar Skip Intros Automatically
Crunchyroll Filler Labeler
3.9 Average rating 3.9 out of 5 stars.
Watch anime on Crunchyroll while knowing which episodes are fillers. Supports the new design!
+
+Never watch One Piece, Naruto,…
Aniskip
4.0 Average rating 4.0 out of 5 stars.
An extension which gives the option to skip anime opening and endings on various streaming sites
EpisodeBrain: Anime & TV Show Tracker
4.3 Average rating 4.3 out of 5 stars.
Stop worrying about where you left off for your favorite TV shows and Anime on streaming websites, we'll keep track of it for you.
Myanimelist Redesign
4.8 Average rating 4.8 out of 5 stars.
A redesigned user interface for Myanimelist with multiple themes and layout improvements
MAL-Sync
4.9 Average rating 4.9 out of 5 stars.
Integrates MyAnimeList/AniList/Kitsu/Simkl/Shikimori into various sites, with auto episode tracking.
\ No newline at end of file
diff --git a/src/services/chrome-crawler.ts b/src/services/chrome-crawler.ts
index 07901e0..ca0e3b5 100644
--- a/src/services/chrome-crawler.ts
+++ b/src/services/chrome-crawler.ts
@@ -26,7 +26,7 @@ export async function crawlExtension(
// const date = new Date();
// const dateString = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
// Bun.write(
- // `src/crawlers/__tests__/fixtures/chrome-web-store/.new/${dateString}-${id}.html`,
+ // `src/services/__tests__/fixtures/chrome-web-store/.new/${dateString}-${id}.html`,
// html,
// );
}
@@ -60,21 +60,28 @@ export async function crawlExtension(
// Header
- const weeklyActiveUsersText = tryExtract("weeklyActiveUsers", validateInt, [
- () => {
- const userCountRow = document.querySelector(
- "main > * > section:first-child > section > div > div:last-child",
- ) as HTMLElement | null;
- removeAnchorChildren(userCountRow);
- return (
- userCountRow?.textContent
- // "W,XYZ+ users"
- ?.replace(" users", "")
- .replaceAll(",", "")
- .replace("+", "")
- );
- },
- ]);
+ const cleanupUserCount = (str: string | undefined) =>
+ str
+ // "W,XYZ+ users"
+ ?.replace(" users", "")
+ .replaceAll(",", "")
+ .replace("+", "");
+ const userCountSelectors = [
+ "main > * > section:first-child > section > div > div:nth-last-child(2)",
+ "main > * > section:first-child > section > div > div:last-child",
+ ];
+ const weeklyActiveUsersText = tryExtract(
+ "weeklyActiveUsers",
+ validateInt,
+ userCountSelectors.map((selector) => () => {
+ const row = document.querySelector(selector) as HTMLElement | null;
+ if (!row?.textContent?.includes("users"))
+ throw Error("Row did not include users");
+
+ removeAnchorChildren(row);
+ return cleanupUserCount(row?.textContent);
+ }),
+ );
const weeklyActiveUsers = Number(weeklyActiveUsersText);
const rating = tryExtract("rating", validateFloat, [