nekostream 2026.2.8 → 2026.2.10

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/index.js CHANGED
@@ -576,12 +576,7 @@ async function checkUpdate() {
576
576
  const latestVersion = data.version;
577
577
  if (latestVersion && latestVersion !== currentVersion) {
578
578
  (0, ui_1.clearScreen)();
579
- console.log(chalk_1.default.yellow('╭─────────────────────────────────────────────────────────────╮'));
580
- console.log(chalk_1.default.yellow('│ │'));
581
- console.log(chalk_1.default.yellow(`│ NEKOSTREAM ĐÃ CÓ CẬP NHẬT MỚI ! ${chalk_1.default.red(currentVersion)} → ${chalk_1.default.green(latestVersion)} │`));
582
- console.log(chalk_1.default.yellow('│ Vui lòng cập nhật ứng dụng CLI để tiếp tục sử dụng ứng dụng. │'));
583
- console.log(chalk_1.default.yellow('│ │'));
584
- console.log(chalk_1.default.yellow('╰─────────────────────────────────────────────────────────────╯\n'));
579
+ (0, ui_1.printUpdateNotice)(currentVersion, latestVersion, NPM_PACKAGE_NAME);
585
580
  const { update } = await (0, prompts_wrapper_1.default)({
586
581
  type: 'confirm',
587
582
  name: 'update',
@@ -120,6 +120,13 @@ async function getAdblocker() {
120
120
  * Call applyAdBlockingToPage(page) after creating each page for network blocking.
121
121
  */
122
122
  async function applyAdBlocking(context) {
123
+ await context.route('**/*', (route) => {
124
+ const url = route.request().url().toLowerCase();
125
+ if (/win88|yo88|i9bet|sunwin|fb88|vsbet|five88|hit\.club|gemwin|789bet|7zwin|rikvip|rik\.|nohu|nổ-hũ|casino|doubleclick|googlesyndication|googleadservices|adservice|exoclick|trafficjunky|popads|adcash|propellerads|adsterra|mgid|outbrain|taboola/.test(url)) {
126
+ return route.abort();
127
+ }
128
+ return route.continue();
129
+ });
123
130
  // CSS + JS pop-up blocking at context level (runs on every page before load)
124
131
  await context.addInitScript(() => {
125
132
  window.open = () => null;
@@ -128,52 +135,61 @@ async function applyAdBlocking(context) {
128
135
  window.prompt = () => null;
129
136
  const hideAds = () => {
130
137
  const style = document.createElement('style');
131
- style.textContent = `
132
- a[href*="win88"], a[href*="yo88"], a[href*="i9bet"],
133
- a[href*="sunwin"], a[href*="vsbet"], a[href*="five88"],
134
- a[href*="hit.club"], a[href*="gemwin"], a[href*="789bet"],
135
- img[src*="win88"], img[src*="yo88"], img[src*="i9bet"],
136
- img[src*="sunwin"], img[src*="gemwin"],
137
- [class*="quang-cao"], [id*="quang-cao"],
138
- [class*="popup-ad"], [class*="ad-overlay"],
139
- [class*="sticky-ad"], [class*="fixed-ad"], [class*="float-ad"],
140
- div.MnBr.EcBgA, div.announcement, div a img, section.Wdgt,
141
- aside div ul, footer.Footer, div.header-ads-pc, ol.breadcrumb,
142
- aside.widget-area, header.Header.MnBrCn.BgA.HdOp1 {
143
- display: none !important;
144
- visibility: hidden !important;
145
- pointer-events: none !important;
146
- }
138
+ style.textContent = `
139
+ a[href*="win88"], a[href*="yo88"], a[href*="i9bet"],
140
+ a[href*="sunwin"], a[href*="vsbet"], a[href*="five88"],
141
+ a[href*="hit.club"], a[href*="gemwin"], a[href*="789bet"],
142
+ a[href*="7zwin"], a[href*="rikvip"], a[href*="casino"],
143
+ img[src*="win88"], img[src*="yo88"], img[src*="i9bet"],
144
+ img[src*="sunwin"], img[src*="gemwin"],
145
+ img[src*="vsbet"], img[src*="five88"], img[src*="hit.club"],
146
+ img[src*="7zwin"], img[src*="rikvip"], img[src*="casino"],
147
+ [class*="quang-cao"], [id*="quang-cao"],
148
+ [class*="advert"], [id*="advert"], [class*="banner-ad"], [id*="banner-ad"],
149
+ [class*="popup-ad"], [class*="ad-overlay"],
150
+ [class*="sticky-ad"], [class*="fixed-ad"], [class*="float-ad"],
151
+ div.header-ads-pc, div[class*="header-ads"], div[id*="header-ads"] {
152
+ display: none !important;
153
+ visibility: hidden !important;
154
+ pointer-events: none !important;
155
+ }
147
156
  `;
148
157
  document.head?.appendChild(style);
149
158
  };
159
+ const adPattern = /win88|yo88|i9bet|sunwin|fb88|vsbet|five88|hit\.club|gemwin|789bet|7zwin|rikvip|rik\.|nohu|nổ\s*hũ|casino|worldcup|uy\s*lực/i;
160
+ const removeAdNode = (node) => {
161
+ const container = node.closest('a, iframe, [class*="banner"], [id*="banner"], [class*="ads"], [id*="ads"], [class*="ad-"], [id*="ad-"]');
162
+ (container || node).remove();
163
+ };
164
+ const cleanupAds = () => {
165
+ try {
166
+ const candidates = document.querySelectorAll('a[href], img[src], iframe[src], [style*="position: fixed"], [style*="z-index"]');
167
+ for (const el of Array.from(candidates)) {
168
+ const marker = `${el.getAttribute('href') || ''} ${el.getAttribute('src') || ''} ${el.getAttribute('alt') || ''} ${el.getAttribute('title') || ''} ${el.textContent || ''}`;
169
+ if (adPattern.test(marker))
170
+ removeAdNode(el);
171
+ }
172
+ for (const el of Array.from(document.querySelectorAll('div, section, aside'))) {
173
+ const html = el.outerHTML.slice(0, 2000);
174
+ const rect = el.getBoundingClientRect();
175
+ if (adPattern.test(html) && (rect.width > 250 || rect.height > 80))
176
+ el.remove();
177
+ }
178
+ }
179
+ catch { }
180
+ };
150
181
  if (document.readyState === 'loading') {
151
- document.addEventListener('DOMContentLoaded', hideAds);
182
+ document.addEventListener('DOMContentLoaded', () => {
183
+ hideAds();
184
+ cleanupAds();
185
+ });
152
186
  }
153
187
  else {
154
188
  hideAds();
189
+ cleanupAds();
155
190
  }
156
- const startObserver = () => {
157
- // Periodic aggressive cleanup to handle SPAs and race conditions
158
- setInterval(() => {
159
- try {
160
- const elements = document.querySelectorAll('div.MnBr.EcBgA, div.announcement, div a img, section.Wdgt, aside div ul, footer.Footer, div.header-ads-pc, ol.breadcrumb, aside.widget-area, header.Header.MnBrCn.BgA.HdOp1, [class*="quang-cao"], [id*="quang-cao"], [class*="popup-ad"], [class*="ad-overlay"], [class*="sticky-ad"], [class*="fixed-ad"], [class*="float-ad"]');
161
- for (const el of Array.from(elements)) {
162
- el.remove();
163
- }
164
- // Also check all links and images for ad keywords
165
- const allElements = document.querySelectorAll('a, img');
166
- for (const el of Array.from(allElements)) {
167
- const html = (el.outerHTML || '').toLowerCase();
168
- if (/win88|yo88|i9bet|sunwin|vsbet|five88|hit\.club|gemwin|789bet/.test(html)) {
169
- el.remove();
170
- }
171
- }
172
- }
173
- catch (e) { }
174
- }, 50);
175
- };
176
- startObserver();
191
+ new MutationObserver(cleanupAds).observe(document.documentElement, { childList: true, subtree: true });
192
+ setInterval(cleanupAds, 500);
177
193
  });
178
194
  }
179
195
  /**
@@ -186,16 +202,6 @@ async function applyAdBlockingToPage(page) {
186
202
  if (blocker) {
187
203
  await blocker.enableBlockingInPage(page);
188
204
  }
189
- else {
190
- // Fallback: manual domain blocking
191
- await page.route('**/*', (route) => {
192
- const url = route.request().url().toLowerCase();
193
- if (/win88|yo88|i9bet|sunwin|fb88|vsbet|five88|hit\.club|gemwin|789bet|doubleclick|googlesyndication|exoclick|trafficjunky|popads|adcash|propellerads/.test(url)) {
194
- return route.abort();
195
- }
196
- return route.continue();
197
- });
198
- }
199
205
  }
200
206
  function getAuthStatus(provider) {
201
207
  const session = (0, storage_1.loadAuthSession)(provider);
@@ -240,37 +246,11 @@ async function loginAnimeVietsubInteractive() {
240
246
  Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
241
247
  globalThis.chrome = { runtime: {} };
242
248
  });
243
- // Skip adblocking for AnimeVietsub login as requested by the user ("không cần adblock cho provider này")
244
- // await applyAdBlocking(context)
249
+ await applyAdBlocking(context);
245
250
  const page = await context.newPage();
246
- // await applyAdBlockingToPage(page)
251
+ await applyAdBlockingToPage(page);
247
252
  try {
248
253
  await page.goto(AVS_LOGIN_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
249
- // Force inject CSS bypassed for AnimeVietsub to avoid triggering adblocker detection
250
- /*
251
- await page.addStyleTag({
252
- content: `
253
- a[href*="win88"], a[href*="yo88"], a[href*="i9bet"],
254
- a[href*="sunwin"], a[href*="vsbet"], a[href*="five88"],
255
- a[href*="hit.club"], a[href*="gemwin"], a[href*="789bet"],
256
- img[src*="win88"], img[src*="yo88"], img[src*="i9bet"],
257
- img[src*="sunwin"], img[src*="gemwin"],
258
- [class*="quang-cao"], [id*="quang-cao"],
259
- [class*="popup-ad"], [class*="ad-overlay"],
260
- [class*="sticky-ad"], [class*="fixed-ad"], [class*="float-ad"],
261
- div.MnBr.EcBgA, div.announcement, div a img, section.Wdgt,
262
- aside div ul, footer.Footer, div.header-ads-pc, ol.breadcrumb,
263
- aside.widget-area, header.Header.MnBrCn.BgA.HdOp1 {
264
- display: none !important;
265
- visibility: hidden !important;
266
- pointer-events: none !important;
267
- opacity: 0 !important;
268
- width: 0 !important;
269
- height: 0 !important;
270
- }
271
- `
272
- }).catch(() => {})
273
- */
274
254
  // Wait for successful login (redirect away from login page)
275
255
  await page.waitForFunction(() => !location.href.includes('/login') && !location.href.includes('/dang-nhap'), { timeout: 180000, polling: 1000 });
276
256
  // Extra settle time for cookies to be written
package/dist/ui.js CHANGED
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.clearScreen = clearScreen;
40
40
  exports.printBanner = printBanner;
41
+ exports.printUpdateNotice = printUpdateNotice;
41
42
  exports.printStatusStrip = printStatusStrip;
42
43
  exports.drawAnimeCard = drawAnimeCard;
43
44
  exports.drawAnimeInfoCard = drawAnimeInfoCard;
@@ -132,6 +133,21 @@ function printBanner(title, subtitle) {
132
133
  console.log(borderLine(width, theme_1.glyphs.bottomLeft, theme_1.glyphs.bottomRight));
133
134
  console.log('');
134
135
  }
136
+ function printUpdateNotice(currentVersion, latestVersion, packageName) {
137
+ const width = (0, text_1.terminalWidth)(86, 62);
138
+ const innerWidth = width - 4;
139
+ const title = `${theme_1.uiText.warning('Cập nhật mới')} ${theme_1.uiText.danger(currentVersion)} ${theme_1.uiText.subtle('->')} ${theme_1.uiText.success(latestVersion)}`;
140
+ const command = `npm i -g ${packageName}@latest`;
141
+ console.log('');
142
+ console.log(borderLine(width, theme_1.glyphs.topLeft, theme_1.glyphs.topRight, true));
143
+ console.log(boxedLine(`${theme_1.uiText.brand('NekoStream')} ${theme_1.uiText.subtle(theme_1.glyphs.dot)} ${title}`, width, true));
144
+ console.log(borderLine(width, theme_1.glyphs.teeLeft, theme_1.glyphs.teeRight));
145
+ for (const line of (0, text_1.wrapText)('Vui lòng cập nhật CLI để tiếp tục sử dụng ứng dụng.', innerWidth)) {
146
+ console.log(boxedLine(theme_1.uiText.value(line), width));
147
+ }
148
+ console.log(boxedLine(`${theme_1.uiText.label('Lệnh')} ${theme_1.uiText.focus(command)}`, width));
149
+ console.log(`${borderLine(width, theme_1.glyphs.bottomLeft, theme_1.glyphs.bottomRight)}\n`);
150
+ }
135
151
  function printStatusStrip(items) {
136
152
  const width = (0, text_1.terminalWidth)(92, 58);
137
153
  const innerWidth = width - 4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nekostream",
3
- "version": "2026.2.8",
3
+ "version": "2026.2.10",
4
4
  "description": "Bettter version of NekoStream ",
5
5
  "keywords": [
6
6
  "anime",