ani-auto 1.4.3 → 2.0.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 (90) hide show
  1. package/anime/LICENSE +21 -0
  2. package/anime/api/index.js +110 -0
  3. package/anime/app.js +79 -0
  4. package/anime/controllers/animeInfoController.js +48 -0
  5. package/anime/controllers/animeListController.js +21 -0
  6. package/anime/controllers/homeController.js +42 -0
  7. package/anime/controllers/playController.js +140 -0
  8. package/anime/controllers/queueController.js +20 -0
  9. package/anime/controllers/testController.js +667 -0
  10. package/anime/index.js +139 -0
  11. package/anime/middleware/cache.js +64 -0
  12. package/anime/middleware/errorHandler.js +33 -0
  13. package/anime/middleware/rateLimiter.js +73 -0
  14. package/anime/models/animeInfoModel.js +193 -0
  15. package/anime/models/animeListModel.js +69 -0
  16. package/anime/models/homeModel.js +33 -0
  17. package/anime/models/playModel.js +528 -0
  18. package/anime/models/queueModel.js +22 -0
  19. package/anime/package.json +27 -0
  20. package/anime/pnpm-lock.yaml +1774 -0
  21. package/anime/readme.md +236 -0
  22. package/anime/routes/animeInfoRoutes.js +9 -0
  23. package/anime/routes/animeListRoutes.js +9 -0
  24. package/anime/routes/homeRoutes.js +10 -0
  25. package/anime/routes/playRoutes.js +11 -0
  26. package/anime/routes/queueRoutes.js +8 -0
  27. package/anime/routes/testRoutes.js +325 -0
  28. package/anime/routes/webhookRoutes.js +23 -0
  29. package/anime/scrapers/animepahe.js +1053 -0
  30. package/anime/test-server.js +10 -0
  31. package/anime/test.sh +275 -0
  32. package/anime/utils/browser.js +172 -0
  33. package/anime/utils/config.js +209 -0
  34. package/anime/utils/dataProcessor.js +172 -0
  35. package/anime/utils/diskCache.js +228 -0
  36. package/anime/utils/flaresolverr.js +361 -0
  37. package/anime/utils/jsParser.js +19 -0
  38. package/anime/utils/redis.js +64 -0
  39. package/anime/utils/requestManager.js +706 -0
  40. package/anime/utils/urlConverter.js +88 -0
  41. package/anime/utils/webhookNotifier.js +190 -0
  42. package/cookiereader.py +291 -0
  43. package/package.json +14 -6
  44. package/src/anilist.js +19 -2
  45. package/src/api/anilist.js +32 -0
  46. package/src/api/anime.js +181 -0
  47. package/src/api/cf-bypass.js +131 -0
  48. package/src/api/config.js +134 -0
  49. package/src/api/daemon.js +62 -0
  50. package/src/api/download.js +98 -0
  51. package/src/api/match.js +58 -0
  52. package/src/api/runs.js +15 -0
  53. package/src/api/update.js +96 -0
  54. package/src/cli.js +18 -2
  55. package/src/config.js +14 -2
  56. package/src/daemon.js +1 -1
  57. package/src/db.js +38 -11
  58. package/src/engine.js +66 -13
  59. package/src/notify.js +82 -7
  60. package/src/scraper.js +2 -2
  61. package/src/server.js +108 -0
  62. package/utils.js +21 -21
  63. package/web/index.html +13 -0
  64. package/web/package-lock.json +1420 -0
  65. package/web/package.json +24 -0
  66. package/web/src/App.vue +200 -0
  67. package/web/src/api/client.js +75 -0
  68. package/web/src/assets/styles/base.css +961 -0
  69. package/web/src/components/UpdateModal.vue +157 -0
  70. package/web/src/components/sidebar/SidebarResizable.vue +170 -0
  71. package/web/src/components/sidebar/sidebarConfig.js +24 -0
  72. package/web/src/main.js +104 -0
  73. package/web/src/router/index.js +44 -0
  74. package/web/src/stores/config.js +27 -0
  75. package/web/src/stores/download.js +107 -0
  76. package/web/src/stores/update.js +73 -0
  77. package/web/src/views/About.vue +42 -0
  78. package/web/src/views/AniListSync.vue +122 -0
  79. package/web/src/views/AnimeDetail.vue +202 -0
  80. package/web/src/views/AnimeList.vue +110 -0
  81. package/web/src/views/CFResult.vue +312 -0
  82. package/web/src/views/DaemonControl.vue +83 -0
  83. package/web/src/views/Dashboard.vue +187 -0
  84. package/web/src/views/DownloadCenter.vue +153 -0
  85. package/web/src/views/MatchFinder.vue +131 -0
  86. package/web/src/views/OAuthCallback.vue +44 -0
  87. package/web/src/views/Onboarding.vue +93 -0
  88. package/web/src/views/RunHistory.vue +122 -0
  89. package/web/src/views/Settings.vue +410 -0
  90. package/web/vite.config.js +23 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Test server wrapper for standalone pahe-api testing.
3
+ * Usage: node test-server.js
4
+ */
5
+ const app = require('./app');
6
+ const PORT = process.env.PORT || 3000;
7
+
8
+ app.listen(PORT, () => {
9
+ console.log(`API running on http://localhost:${PORT}`);
10
+ });
package/anime/test.sh ADDED
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================
3
+ # pahe-api — Full endpoint test with response validation
4
+ # Usage: chmod +x test.sh && ./test.sh
5
+ # ============================================================
6
+
7
+ BASE="http://localhost:3000"
8
+ PASS=0
9
+ FAIL=0
10
+ WARN=0
11
+
12
+ # Colors
13
+ GREEN='\033[0;32m'
14
+ RED='\033[0;31m'
15
+ YELLOW='\033[1;33m'
16
+ CYAN='\033[0;36m'
17
+ NC='\033[0m'
18
+
19
+ # Helper: validate JSON structure
20
+ check() {
21
+ local name="$1"
22
+ local json="$2"
23
+ local path="$3"
24
+ local expected_type="$4"
25
+
26
+ local val
27
+ val=$(echo "$json" | python3 -c "
28
+ import sys, json
29
+ try:
30
+ d = json.load(sys.stdin)
31
+ keys = '$path'.split('.')
32
+ obj = d
33
+ for k in keys:
34
+ if k.isdigit():
35
+ obj = obj[int(k)]
36
+ else:
37
+ obj = obj[k]
38
+ print(type(obj).__name__, repr(obj)[:80])
39
+ except Exception as e:
40
+ print('ERROR', str(e))
41
+ " 2>&1)
42
+
43
+ local actual_type="${val%% *}"
44
+ local actual_val="${val#* }"
45
+
46
+ if [[ "$val" == ERROR* ]]; then
47
+ echo -e " ${RED}✗ FAIL${NC} $name → path '$path' not found: ${actual_val#ERROR }"
48
+ ((FAIL++))
49
+ elif [[ -n "$expected_type" && "$actual_type" != "$expected_type" ]]; then
50
+ echo -e " ${YELLOW}⚠ WARN${NC} $name → $path: expected $expected_type, got $actual_type ($actual_val)"
51
+ ((WARN++))
52
+ else
53
+ echo -e " ${GREEN}✓ PASS${NC} $name → $path: $actual_type $actual_val"
54
+ ((PASS++))
55
+ fi
56
+ }
57
+
58
+ # Helper: check HTTP status
59
+ status_check() {
60
+ local name="$1"
61
+ local url="$2"
62
+ local expected_code="$3"
63
+ local max_time="${4:-30}"
64
+
65
+ local response
66
+ response=$(curl -s -w "\n%{http_code}" --connect-timeout 10 --max-time "$max_time" "$url" 2>&1)
67
+ local http_code
68
+ http_code=$(echo "$response" | tail -1)
69
+ local body
70
+ body=$(echo "$response" | sed '$d')
71
+
72
+ if [[ "$http_code" == "$expected_code" ]]; then
73
+ echo -e "${GREEN}✓ PASS${NC} [$http_code] $name"
74
+ ((PASS++))
75
+ echo "$body"
76
+ else
77
+ echo -e "${RED}✗ FAIL${NC} [$http_code] $name (expected $expected_code)"
78
+ ((FAIL++))
79
+ echo "$body" | head -5
80
+ fi
81
+ }
82
+
83
+ echo -e "${CYAN}============================================${NC}"
84
+ echo -e "${CYAN} pahe-api — Endpoint Validation Tests${NC}"
85
+ echo -e "${CYAN}============================================${NC}"
86
+ echo ""
87
+
88
+ # ────────────────────────────────────────────────────────────
89
+ echo -e "${CYAN}[1/13] GET /airing${NC}"
90
+ json=$(status_check "Airing anime" "$BASE/airing" "200")
91
+ echo ""
92
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
93
+ check "paginationInfo.total" "$json" "paginationInfo.total" "int"
94
+ check "paginationInfo.perPage" "$json" "paginationInfo.perPage" "int"
95
+ check "paginationInfo.currentPage" "$json" "paginationInfo.currentPage" "int"
96
+ check "data (array)" "$json" "data" "list"
97
+ check "data[0].id" "$json" "data.0.id" "int"
98
+ check "data[0].title" "$json" "data.0.title" "str"
99
+ check "data[0].episode" "$json" "data.0.episode" "int"
100
+ check "data[0].fansub" "$json" "data.0.fansub" "str"
101
+ check "data[0].image (URL)" "$json" "data.0.image" "str"
102
+ check "data[0].session" "$json" "data.0.session" "str"
103
+ fi
104
+ echo ""
105
+
106
+ # ────────────────────────────────────────────────────────────
107
+ echo -e "${CYAN}[2/13] GET /search?q=naruto${NC}"
108
+ json=$(status_check "Search anime" "$BASE/search?q=naruto" "200")
109
+ echo ""
110
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
111
+ check "paginationInfo.total" "$json" "paginationInfo.total" "int"
112
+ check "data (array)" "$json" "data" "list"
113
+ check "data[0].id" "$json" "data.0.id" "int"
114
+ check "data[0].title" "$json" "data.0.title" "str"
115
+ check "data[0].status" "$json" "data.0.status" "str"
116
+ check "data[0].type" "$json" "data.0.type" "str"
117
+ check "data[0].episodes" "$json" "data.0.episodes" "int"
118
+ check "data[0].score" "$json" "data.0.score" "float"
119
+ check "data[0].year" "$json" "data.0.year" "int"
120
+ check "data[0].poster (URL)" "$json" "data.0.poster" "str"
121
+ check "data[0].session" "$json" "data.0.session" "str"
122
+ fi
123
+ echo ""
124
+
125
+ # ────────────────────────────────────────────────────────────
126
+ echo -e "${CYAN}[3/13] GET /search (page 2)?q=naruto&page=2${NC}"
127
+ json=$(status_check "Search (page 2)" "$BASE/search?q=naruto&page=2" "200")
128
+ echo ""
129
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
130
+ check "paginationInfo.currentPage" "$json" "paginationInfo.currentPage" "int"
131
+ check "data (array)" "$json" "data" "list"
132
+ fi
133
+ echo ""
134
+
135
+ # ────────────────────────────────────────────────────────────
136
+ echo -e "${CYAN}[4/13] GET /list (A-Z) (A-Z List)${NC}"
137
+ json=$(status_check "Anime list" "$BASE/list" "200")
138
+ echo ""
139
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
140
+ check "data (array)" "$json" "0" "list"
141
+ check "[0].title" "$json" "0.title" "str"
142
+ check "[0].url" "$json" "0.url" "str"
143
+ fi
144
+ echo ""
145
+
146
+ # ────────────────────────────────────────────────────────────
147
+ echo -e "${CYAN}[5/13] GET /list?tab=N (by letter)${NC}"
148
+ json=$(status_check "Anime list (letter N)" "$BASE/list?tab=N" "200")
149
+ echo ""
150
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
151
+ check "[0].title" "$json" "0.title" "str"
152
+ check "[0].url" "$json" "0.url" "str"
153
+ fi
154
+ echo ""
155
+
156
+ # ────────────────────────────────────────────────────────────
157
+ echo -e "${CYAN}[6/13] GET /list/genre/action (by tag)${NC}"
158
+ json=$(status_check "Anime list (genre/action)" "$BASE/list/genre/action" "200")
159
+ echo ""
160
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
161
+ check "[0].title" "$json" "0.title" "str"
162
+ check "[0].url" "$json" "0.url" "str"
163
+ fi
164
+ echo ""
165
+
166
+ # ────────────────────────────────────────────────────────────
167
+ echo -e "${CYAN}[7/13] GET /:id (Anime Info) (Anime Info)${NC}"
168
+ ANIME_ID="66e575d9-1c71-2117-bf09-acef2f6a0deb"
169
+ json=$(status_check "Anime info (Naruto)" "$BASE/$ANIME_ID" "200")
170
+ echo ""
171
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
172
+ check "title" "$json" "title" "str"
173
+ check "image (URL)" "$json" "image" "str"
174
+ check "synopsis" "$json" "synopsis" "str"
175
+ check "type" "$json" "type" "str"
176
+ check "episodes (str)" "$json" "episodes" "str"
177
+ check "status" "$json" "status" "str"
178
+ check "season" "$json" "season" "str"
179
+ check "studio" "$json" "studio" "str"
180
+ check "ids.animepahe_id" "$json" "ids.animepahe_id" "str"
181
+ check "ids.mal" "$json" "ids.mal" "str"
182
+ check "ids.anilist" "$json" "ids.anilist" "str"
183
+ fi
184
+ echo ""
185
+
186
+ # ────────────────────────────────────────────────────────────
187
+ echo -e "${CYAN}[8/13] GET /:id/releases (Episodes) (Episodes)${NC}"
188
+ json=$(status_check "Episodes (Naruto)" "$BASE/$ANIME_ID/releases" "200")
189
+ echo ""
190
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
191
+ check "paginationInfo.total" "$json" "paginationInfo.total" "int"
192
+ check "paginationInfo.perPage" "$json" "paginationInfo.perPage" "int"
193
+ check "paginationInfo.lastPage" "$json" "paginationInfo.lastPage" "int"
194
+ check "data (array)" "$json" "data" "list"
195
+ check "data[0].id" "$json" "data.0.id" "int"
196
+ check "data[0].episode" "$json" "data.0.episode" "int"
197
+ check "data[0].audio" "$json" "data.0.audio" "str"
198
+ check "data[0].duration" "$json" "data.0.duration" "str"
199
+ check "data[0].session" "$json" "data.0.session" "str"
200
+ fi
201
+ echo ""
202
+
203
+ # ────────────────────────────────────────────────────────────
204
+ echo -e "${CYAN}[9/13] GET /:id/releases (sorted)?sort=&page=2${NC}"
205
+ EP_ID="9a11cba2022e39f2531d2d885a89558263c6a54bb07bb5fb46ef48a5b7e24c82"
206
+ json=$(status_check "Episodes (sorted + paged)" "$BASE/$ANIME_ID/releases?sort=episode_desc&page=2" "200")
207
+ echo ""
208
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
209
+ check "paginationInfo.currentPage" "$json" "paginationInfo.currentPage" "int"
210
+ check "data (array)" "$json" "data" "list"
211
+ fi
212
+ echo ""
213
+
214
+ # ────────────────────────────────────────────────────────────
215
+ echo -e "${CYAN}[10/13] GET /:id/:ep (Stream + Downloads)${NC}"
216
+ json=$(status_check "Play (Naruto Ep 220)" "$BASE/$ANIME_ID/$EP_ID" "200" "120")
217
+ echo ""
218
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
219
+ check "provider" "$json" "provider" "str"
220
+ check "episode" "$json" "episode" "str"
221
+ check "anime_title" "$json" "anime_title" "str"
222
+ check "sources (array)" "$json" "sources" "list"
223
+ check "sources[0].url (m3u8)" "$json" "sources.0.url" "str"
224
+ check "sources[0].isM3U8" "$json" "sources.0.isM3U8" "bool"
225
+ check "sources[0].embed (URL)" "$json" "sources.0.embed" "str"
226
+ check "sources[0].resolution (str)" "$json" "sources.0.resolution" "str"
227
+ check "sources[0].download (URL)" "$json" "sources.0.download" "str"
228
+ check "downloads (array)" "$json" "downloads" "list"
229
+ check "downloads[0].fansub" "$json" "downloads.0.fansub" "str"
230
+ check "downloads[0].quality" "$json" "downloads.0.quality" "str"
231
+ check "downloads[0].filesize" "$json" "downloads.0.filesize" "str"
232
+ check "downloads[0].isDub" "$json" "downloads.0.isDub" "bool"
233
+ check "downloads[0].pahe (URL)" "$json" "downloads.0.pahe" "str"
234
+ check "downloads[0].download (URL)" "$json" "downloads.0.download" "str"
235
+ fi
236
+ echo ""
237
+
238
+ # ────────────────────────────────────────────────────────────
239
+ echo -e "${CYAN}[11/13] GET /:id/:ep?downloads=false (Stream Only)${NC}"
240
+ json=$(status_check "Play (stream only)" "$BASE/$ANIME_ID/$EP_ID?downloads=false" "200" "60")
241
+ echo ""
242
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
243
+ check "sources (array)" "$json" "sources" "list"
244
+ check "downloads (empty)" "$json" "downloads" "list"
245
+ fi
246
+ echo ""
247
+
248
+ # ────────────────────────────────────────────────────────────
249
+ echo -e "${CYAN}[12/13] GET /download-links?url= (DEPRECATED)${NC}"
250
+ json=$(status_check "Download links (deprecated)" "$BASE/download-links?url=https://pahe.win/ecJzv" "200" "60")
251
+ echo ""
252
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
253
+ check "status" "$json" "status" "int"
254
+ check "message (deprecated)" "$json" "message" "str"
255
+ fi
256
+ echo ""
257
+
258
+ # ────────────────────────────────────────────────────────────
259
+ echo -e "${CYAN}[13/13] GET /queue${NC}"
260
+ json=$(status_check "Queue" "$BASE/queue" "200")
261
+ echo ""
262
+ if echo "$json" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
263
+ check "data (array)" "$json" "data" "list"
264
+ fi
265
+ echo ""
266
+
267
+ # ────────────────────────────────────────────────────────────
268
+ echo -e "${CYAN}============================================${NC}"
269
+ echo -e "${GREEN}PASSED: $PASS${NC} | ${RED}FAILED: $FAIL${NC} | ${YELLOW}WARNINGS: $WARN${NC}"
270
+ echo -e "${CYAN}============================================${NC}"
271
+
272
+ if [[ $FAIL -gt 0 ]]; then
273
+ exit 1
274
+ fi
275
+ exit 0
@@ -0,0 +1,172 @@
1
+ const fs = require('fs/promises');
2
+ const path = require('path');
3
+ const { existsSync } = require('fs');
4
+ const os = require('os');
5
+
6
+ function resolveBrowserRuntime() {
7
+ let chromiumBinary = null;
8
+ let chromium = null;
9
+ let useServerlessChromium = false;
10
+
11
+ if (String(process.env.USE_STEALTH).toLowerCase() === 'true') {
12
+ try {
13
+ const playwrightExtra = require('playwright-extra');
14
+ const stealth = require('playwright-extra-plugin-stealth')();
15
+ chromium = playwrightExtra.chromium;
16
+ chromium.use(stealth);
17
+ console.log('Using playwright-extra with stealth plugin (USE_STEALTH=true)');
18
+ } catch (err) {
19
+ console.warn('USE_STEALTH=true but playwright-extra or stealth plugin not installed; falling back to regular browser runtime');
20
+ }
21
+ }
22
+
23
+ try {
24
+ if (!chromium) {
25
+ chromiumBinary = require('@sparticuz/chromium');
26
+ chromium = require('playwright-core').chromium;
27
+ }
28
+
29
+ if (os.platform() === 'linux') {
30
+ useServerlessChromium = true;
31
+ } else {
32
+ console.warn('⚠️ Detected non-Linux OS. Disabling @sparticuz/chromium for local dev.');
33
+ }
34
+ } catch (e) {
35
+ if (!chromium) {
36
+ try {
37
+ chromium = require('playwright').chromium;
38
+ console.warn('Falling back to full Playwright (probably running locally)');
39
+ } catch (inner) {
40
+ const error = new Error(
41
+ 'Browser runtime is not installed. Set FLARESOLVERR_URL or reinstall the Playwright browser stack.'
42
+ );
43
+ error.cause = inner;
44
+ throw error;
45
+ }
46
+ }
47
+ }
48
+
49
+ return { chromium, chromiumBinary, useServerlessChromium };
50
+ }
51
+
52
+ /**
53
+ * Launches a Chromium browser with appropriate settings for the current environment.
54
+ * Automatically supports both serverless and local development.
55
+ */
56
+ async function launchBrowser() {
57
+ const { chromium, chromiumBinary, useServerlessChromium } = resolveBrowserRuntime();
58
+ const isServerless = process.env.VERCEL || process.env.NETLIFY || process.env.AWS_LAMBDA_FUNCTION_NAME;
59
+ const isContainerizedLinux =
60
+ os.platform() === 'linux' &&
61
+ (
62
+ process.env.CONTAINER === 'true' ||
63
+ process.env.DOCKER === 'true' ||
64
+ process.env.KUBERNETES_SERVICE_HOST ||
65
+ existsSync('/.dockerenv')
66
+ );
67
+
68
+ const baseArgs = [
69
+ '--no-sandbox',
70
+ '--disable-setuid-sandbox',
71
+ '--disable-dev-shm-usage',
72
+ '--disable-blink-features=AutomationControlled',
73
+ '--disable-gpu',
74
+ '--disable-background-networking',
75
+ '--disable-default-apps',
76
+ '--disable-extensions',
77
+ '--disable-infobars',
78
+ '--disable-notifications',
79
+ '--disable-offline-sync',
80
+ '--disable-sync',
81
+ '--disable-translate',
82
+ '--no-first-run',
83
+ '--no-zygote'
84
+ ];
85
+
86
+ // Serverless-specific optimizations
87
+ const serverlessArgs = [
88
+ '--disable-background-timer-throttling',
89
+ '--disable-backgrounding-occluded-windows',
90
+ '--disable-renderer-backgrounding',
91
+ '--disable-features=TranslateUI',
92
+ '--disable-ipc-flooding-protection',
93
+ '--disable-hang-monitor',
94
+ '--disable-prompt-on-repost',
95
+ '--disable-domain-reliability',
96
+ '--disable-component-extensions-with-background-pages',
97
+ '--memory-pressure-off',
98
+ '--max_old_space_size=4096'
99
+ ];
100
+
101
+ // Default headless behavior:
102
+ // - in serverless environments we want headless=true unless explicitly overridden
103
+ // - allow overriding with CHROME_HEADLESS env var ("true"/"false") for local testing
104
+ const envHeadless = typeof process.env.CHROME_HEADLESS !== 'undefined'
105
+ ? String(process.env.CHROME_HEADLESS).toLowerCase() === 'true'
106
+ : null;
107
+
108
+ // Containers usually do not have a usable display server, so default to headless there too.
109
+ const defaultHeadless = isServerless || isContainerizedLinux ? true : false;
110
+
111
+ const launchOptions = {
112
+ headless: envHeadless === null ? defaultHeadless : envHeadless,
113
+ args: isServerless ? [...baseArgs, ...serverlessArgs] : baseArgs,
114
+ // Reduced timeouts for serverless
115
+ timeout: isServerless ? 30000 : 60000
116
+ };
117
+
118
+ // Add user agent
119
+ launchOptions.args.push(
120
+ '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
121
+ );
122
+
123
+ if (useServerlessChromium && chromiumBinary) {
124
+ console.log('Using serverless Chromium binary');
125
+
126
+ try {
127
+ const executablePath = await chromiumBinary.executablePath();
128
+
129
+ if (existsSync(executablePath)) {
130
+ launchOptions.executablePath = executablePath;
131
+ // Use chromium.args but merge with our custom args
132
+ // Prepend serverless chromium recommended args so they run first
133
+ launchOptions.args = [...chromiumBinary.args, ...launchOptions.args];
134
+
135
+ console.log('Serverless Chromium configured successfully');
136
+ } else {
137
+ console.warn('⚠️ Chromium binary not found at expected path. Falling back to default.');
138
+ }
139
+ } catch (error) {
140
+ console.error('Error setting up serverless Chromium:', error);
141
+ }
142
+ }
143
+
144
+ console.log('Launching browser with headless=%s and args count=%d', launchOptions.headless, launchOptions.args.length);
145
+
146
+ try {
147
+ const browser = await chromium.launch(launchOptions);
148
+ console.log('Browser launched successfully');
149
+ return browser;
150
+ } catch (error) {
151
+ console.error('Failed to launch browser:', error);
152
+
153
+ // Fallback with minimal args
154
+ console.log('Attempting fallback launch with minimal configuration...');
155
+ const fallbackOptions = {
156
+ headless: true,
157
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
158
+ };
159
+
160
+ if (useServerlessChromium && chromiumBinary) {
161
+ try {
162
+ fallbackOptions.executablePath = await chromiumBinary.executablePath();
163
+ } catch (e) {
164
+ console.warn('Could not set executable path for fallback');
165
+ }
166
+ }
167
+
168
+ return await chromium.launch(fallbackOptions);
169
+ }
170
+ }
171
+
172
+ module.exports = { launchBrowser };
@@ -0,0 +1,209 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const dotenv = require("dotenv");
4
+
5
+ dotenv.config({
6
+ path: path.join(__dirname, "..", "..", ".env"),
7
+ });
8
+
9
+ class Config {
10
+ constructor() {
11
+ this.hostUrl = "";
12
+ this.baseUrl = "https://animepahe.pw";
13
+ this.iframeBaseUrl = "uwucdn.top";
14
+ this.userAgent =
15
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
16
+ this.extraHTTPHeaders = {
17
+ Accept:
18
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
19
+ "Accept-Language": "en-US,en;q=0.9",
20
+ "Accept-Encoding": "gzip, deflate, br",
21
+ DNT: "1",
22
+ Connection: "keep-alive",
23
+ "Upgrade-Insecure-Requests": "1",
24
+ };
25
+ this.cfClearance = "";
26
+ this.cookiesRefreshInterval = 14 * 24 * 60 * 60 * 1000;
27
+ this.proxies = [];
28
+ this.proxyEnabled = false;
29
+
30
+ // Environment-specific settings
31
+ this.isServerless = !!(
32
+ process.env.VERCEL ||
33
+ process.env.NETLIFY ||
34
+ process.env.AWS_LAMBDA_FUNCTION_NAME
35
+ );
36
+ this.maxRetries = this.isServerless ? 1 : 3; // Reduced retries on Vercel
37
+ this.requestTimeout = this.isServerless ? 10000 : 30000; // Reduced timeout on Vercel
38
+ this.challengeTimeout = this.isServerless ? 10000 : 30000; // Reduced timeout on Vercel
39
+ }
40
+
41
+ setHostUrl(protocol, host) {
42
+ if (!this.hostUrl && protocol && host) {
43
+ this.hostUrl = `${protocol}://${host}`;
44
+ console.log(`Host URL set to: ${this.hostUrl}`);
45
+ }
46
+ }
47
+
48
+ setCfClearance(value) {
49
+ if (!value) {
50
+ console.warn("Warning: cf_clearance value is empty");
51
+ return false;
52
+ }
53
+ this.cfClearance = value.replace(/^cf_clearance=/, "");
54
+ console.log("\x1b[36m%s\x1b[0m", "cf_clearance set");
55
+ return true;
56
+ }
57
+
58
+ setCookies(cookieString) {
59
+ if (!cookieString) return false;
60
+ const match = cookieString.match(/cf_clearance=([^;]+)/);
61
+ if (match) {
62
+ this.cfClearance = match[1];
63
+ return true;
64
+ }
65
+ return false;
66
+ }
67
+
68
+ get cookies() {
69
+ return this.cfClearance ? `cf_clearance=${this.cfClearance}` : "";
70
+ }
71
+
72
+ setProxy(proxyString) {
73
+ if (!proxyString) {
74
+ console.warn("Warning: Proxy string is empty");
75
+ return false;
76
+ }
77
+
78
+ try {
79
+ // Validate proxy format (basic check)
80
+ const proxyUrl = new URL(
81
+ proxyString.startsWith("http") ? proxyString : `http://${proxyString}`,
82
+ );
83
+ if (proxyUrl.hostname) {
84
+ return true;
85
+ }
86
+ return false;
87
+ } catch (error) {
88
+ console.warn(`Invalid proxy format: ${proxyString}`);
89
+ return false;
90
+ }
91
+ }
92
+
93
+ getRandomProxy() {
94
+ if (this.proxies.length === 0) {
95
+ console.log("No proxies available.");
96
+ }
97
+ return this.proxies[Math.floor(Math.random() * this.proxies.length)];
98
+ }
99
+
100
+ // Method to update proxies dynamically
101
+ updateProxies(newProxies) {
102
+ this.proxies = newProxies;
103
+ }
104
+
105
+ getUrl(section, params = {}) {
106
+ const { page, tab, genre } = params;
107
+ const primary = params.primary || params.animeId || params.id || "";
108
+ const secondary = params.secondary || params.episodeId || params.ep || "";
109
+
110
+ const paths = {
111
+ home: "/",
112
+ queue: "/queue",
113
+ animeInfo: `/anime/${primary}`,
114
+ animeList: "/anime",
115
+ play: `/play/${primary}/${secondary}`,
116
+ };
117
+
118
+ if (!paths[section]) {
119
+ throw new Error(`Invalid section: ${section}`);
120
+ }
121
+
122
+ let url = `${this.baseUrl}${paths[section]}`;
123
+
124
+ // Add query params for animeList
125
+ if (section === "animeList") {
126
+ const queryParams = new URLSearchParams();
127
+ if (page) queryParams.append("page", page);
128
+ if (tab) queryParams.append("tab", tab);
129
+ if (genre) queryParams.append("genre", genre);
130
+
131
+ const queryString = queryParams.toString();
132
+ if (queryString) {
133
+ url += `?${queryString}`;
134
+ }
135
+ }
136
+
137
+ return url;
138
+ }
139
+
140
+ loadFromEnv() {
141
+ if (process.env.BASE_URL) {
142
+ this.baseUrl = process.env.BASE_URL;
143
+ }
144
+
145
+ if (process.env.USER_AGENT) {
146
+ this.userAgent = process.env.USER_AGENT;
147
+ }
148
+
149
+ if (process.env.HOST_URL) {
150
+ this.hostUrl = process.env.HOST_URL;
151
+ }
152
+
153
+ if (process.env.cookiesRefreshInterval) {
154
+ this.cookiesRefreshInterval = process.env.cookiesRefreshInterval;
155
+ }
156
+
157
+ // Handle cf_clearance
158
+ if (process.env.CF_CLEARANCE) {
159
+ this.setCfClearance(process.env.CF_CLEARANCE);
160
+ }
161
+
162
+ // Handle proxies
163
+ if (process.env.PROXIES) {
164
+ try {
165
+ const proxyList = process.env.PROXIES.split(",").map((proxy) =>
166
+ proxy.trim(),
167
+ );
168
+ const validProxies = proxyList.filter((proxy) => this.setProxy(proxy));
169
+
170
+ if (validProxies.length === 0) {
171
+ console.warn("No valid proxies found in environment variables");
172
+ this.proxies = [];
173
+ } else {
174
+ this.proxies = validProxies;
175
+ console.log(`Successfully loaded ${validProxies.length} proxies`);
176
+ }
177
+ } catch (error) {
178
+ console.error(
179
+ "Error processing proxies from environment variables:",
180
+ error.message,
181
+ );
182
+ this.proxies = [];
183
+ }
184
+ }
185
+
186
+ if (process.env.IFRAME_BASE_URL) {
187
+ this.iframeBaseUrl = process.env.IFRAME_BASE_URL;
188
+ }
189
+
190
+ this.proxyEnabled = process.env.USE_PROXY === "true";
191
+ if (this.proxyEnabled && this.proxies.length === 0) {
192
+ console.warn(
193
+ "Proxy usage is enabled but no valid proxies are configured",
194
+ );
195
+ }
196
+ }
197
+
198
+ validate() {
199
+ if (!this.baseUrl) {
200
+ throw new Error("Base URL is required in configuration.");
201
+ }
202
+ if (!this.userAgent) {
203
+ throw new Error("User-Agent is required in configuration.");
204
+ }
205
+ }
206
+ }
207
+
208
+ const config = new Config();
209
+ module.exports = config;