ani-web 2.0.7
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/LICENSE +21 -0
- package/README.md +220 -0
- package/client/dist/assets/AnimeInfo-B88ZA3gl.js +1 -0
- package/client/dist/assets/AnimeInfo-R63luGTP.css +1 -0
- package/client/dist/assets/AnimeInfoPage-xGVarrXG.js +2 -0
- package/client/dist/assets/Button-Fq9KaUOg.css +1 -0
- package/client/dist/assets/Button-lkEUHIDS.js +1 -0
- package/client/dist/assets/ErrorMessage-BVWNgHMx.css +1 -0
- package/client/dist/assets/ErrorMessage-BXKDLzn8.js +1 -0
- package/client/dist/assets/Home-O1FbN8t_.js +1 -0
- package/client/dist/assets/Home-r0eYbWNc.css +1 -0
- package/client/dist/assets/Insights-CF4K-oO5.css +1 -0
- package/client/dist/assets/Insights-opcB-aTo.js +1 -0
- package/client/dist/assets/MAL-BGM33N5c.js +1 -0
- package/client/dist/assets/MAL-DeQNXXPx.css +1 -0
- package/client/dist/assets/Player-BarbgKjI.css +1 -0
- package/client/dist/assets/Player-CSyGax33.js +9 -0
- package/client/dist/assets/PlayerSettings-BaCVQyw6.js +1 -0
- package/client/dist/assets/PlayerSettings-l6aLKrHh.css +1 -0
- package/client/dist/assets/QueueRail-Cxn5U8kE.js +1 -0
- package/client/dist/assets/QueueRail-DOM_pWkE.css +1 -0
- package/client/dist/assets/RemoveConfirmationModal-BBiogSdf.css +1 -0
- package/client/dist/assets/RemoveConfirmationModal-DatCZQKq.js +1 -0
- package/client/dist/assets/Search-BzO-aRP7.css +1 -0
- package/client/dist/assets/Search-DJxo3BYH.js +1 -0
- package/client/dist/assets/SearchableSelect-BkCrf6E8.css +1 -0
- package/client/dist/assets/SearchableSelect-BzYsMz8B.js +1 -0
- package/client/dist/assets/Settings-Bv9fX-x3.css +1 -0
- package/client/dist/assets/Settings-C5adinOe.js +1 -0
- package/client/dist/assets/SynopsisText-C3AK-aRc.js +1 -0
- package/client/dist/assets/SynopsisText-DsI3mW5v.css +1 -0
- package/client/dist/assets/ToggleSwitch-BIlQxIjg.css +1 -0
- package/client/dist/assets/ToggleSwitch-CrXim14A.js +1 -0
- package/client/dist/assets/Watchlist-CXw0vbNx.js +1 -0
- package/client/dist/assets/Watchlist-a2RHQogs.css +1 -0
- package/client/dist/assets/hls.light-DcbkToIY.js +27 -0
- package/client/dist/assets/index-BzX_xmnf.css +1 -0
- package/client/dist/assets/index-Ciivz6fh.js +178 -0
- package/client/dist/assets/useAnimeInfoData-Dqthchpa.js +1 -0
- package/client/dist/assets/useIsMobile-BviODivc.js +1 -0
- package/client/dist/assets/vendor-Bc4EraM_.js +3 -0
- package/client/dist/favicon.ico +0 -0
- package/client/dist/index.html +35 -0
- package/client/dist/logo.png +0 -0
- package/client/dist/placeholder.svg +4 -0
- package/client/dist/robots.txt +3 -0
- package/client/package.json +58 -0
- package/orchestrator.js +323 -0
- package/package.json +88 -0
- package/server/.env +1 -0
- package/server/dist/config.js +89 -0
- package/server/dist/constants.json +1359 -0
- package/server/dist/controllers/auth.controller.js +215 -0
- package/server/dist/controllers/data.controller.js +232 -0
- package/server/dist/controllers/insights.controller.js +200 -0
- package/server/dist/controllers/proxy.controller.js +353 -0
- package/server/dist/controllers/settings.controller.js +159 -0
- package/server/dist/controllers/watchlist.controller.js +749 -0
- package/server/dist/db.js +152 -0
- package/server/dist/discord-rpc.js +279 -0
- package/server/dist/github-sync.js +342 -0
- package/server/dist/google.js +310 -0
- package/server/dist/logger.js +21 -0
- package/server/dist/providers/123anime.provider.js +226 -0
- package/server/dist/providers/allanime.provider.js +736 -0
- package/server/dist/providers/animepahe.provider.js +457 -0
- package/server/dist/providers/animeya.provider.js +787 -0
- package/server/dist/providers/megaplay.provider.js +264 -0
- package/server/dist/providers/provider.interface.js +2 -0
- package/server/dist/rclone.js +126 -0
- package/server/dist/repositories/insights.repository.js +42 -0
- package/server/dist/repositories/notifications.repository.js +30 -0
- package/server/dist/repositories/queue.repository.js +38 -0
- package/server/dist/repositories/settings.repository.js +14 -0
- package/server/dist/repositories/shows-meta.repository.js +41 -0
- package/server/dist/repositories/watched-episodes.repository.js +67 -0
- package/server/dist/repositories/watchlist.repository.js +80 -0
- package/server/dist/routes/auth.routes.js +26 -0
- package/server/dist/routes/data.routes.js +42 -0
- package/server/dist/routes/insights.routes.js +12 -0
- package/server/dist/routes/proxy.routes.js +14 -0
- package/server/dist/routes/settings.routes.js +27 -0
- package/server/dist/routes/watchlist.routes.js +46 -0
- package/server/dist/server.js +229 -0
- package/server/dist/sync-config.js +28 -0
- package/server/dist/sync.js +427 -0
- package/server/dist/utils/db-utils.js +15 -0
- package/server/dist/utils/env.utils.js +79 -0
- package/server/dist/utils/machine-id.js +46 -0
- package/server/dist/utils/request-context.js +5 -0
- package/server/package.json +19 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports._123AnimeProvider = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
8
|
+
const BASE_URL = 'https://shirayuki-scrapper-api.onrender.com';
|
|
9
|
+
class _123AnimeProvider {
|
|
10
|
+
name = '123Anime';
|
|
11
|
+
cache;
|
|
12
|
+
constructor(cache) {
|
|
13
|
+
this.cache = cache;
|
|
14
|
+
}
|
|
15
|
+
createSlug(title) {
|
|
16
|
+
return title
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^\w\s-]/g, '')
|
|
19
|
+
.replace(/\s+/g, '-')
|
|
20
|
+
.replace(/-+/g, '-')
|
|
21
|
+
.replace(/^-+|-+$/g, '');
|
|
22
|
+
}
|
|
23
|
+
normalizeSlugForSearch(title) {
|
|
24
|
+
return title
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^\w\s-]/g, '')
|
|
27
|
+
.replace(/['"]/g, '')
|
|
28
|
+
.replace(/\s+/g, '-')
|
|
29
|
+
.replace(/-+/g, '-')
|
|
30
|
+
.replace(/^-+|-+$/g, '');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Picks the best-matching show from a list of search results by comparing
|
|
34
|
+
* how closely each result's title / id matches the query.
|
|
35
|
+
* Scoring (highest wins):
|
|
36
|
+
* 3 – id/slug exact match
|
|
37
|
+
* 2 – title exact match (case-insensitive)
|
|
38
|
+
* 1 – title starts with query
|
|
39
|
+
* 0 – title contains query word (partial)
|
|
40
|
+
* -1 – no match (but still returned as last resort)
|
|
41
|
+
*/
|
|
42
|
+
bestMatch(results, query) {
|
|
43
|
+
const q = query.toLowerCase().trim();
|
|
44
|
+
const qSlug = this.normalizeSlugForSearch(q);
|
|
45
|
+
let best = results[0];
|
|
46
|
+
let bestScore = -1;
|
|
47
|
+
for (const s of results) {
|
|
48
|
+
const id = (s.id || s._id || '').toLowerCase();
|
|
49
|
+
const title = (s.name || '').toLowerCase();
|
|
50
|
+
let score = -1;
|
|
51
|
+
if (id === qSlug || id === q) {
|
|
52
|
+
score = 3;
|
|
53
|
+
}
|
|
54
|
+
else if (title === q) {
|
|
55
|
+
score = 2;
|
|
56
|
+
}
|
|
57
|
+
else if (title.startsWith(q)) {
|
|
58
|
+
score = 1;
|
|
59
|
+
}
|
|
60
|
+
else if (title.includes(q) || id.startsWith(qSlug)) {
|
|
61
|
+
score = 0;
|
|
62
|
+
}
|
|
63
|
+
if (score > bestScore) {
|
|
64
|
+
bestScore = score;
|
|
65
|
+
best = s;
|
|
66
|
+
if (score === 3)
|
|
67
|
+
break; // can't do better
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return best;
|
|
71
|
+
}
|
|
72
|
+
extractSlugFromUrl(url) {
|
|
73
|
+
if (!url)
|
|
74
|
+
return null;
|
|
75
|
+
try {
|
|
76
|
+
const parts = url.split('/');
|
|
77
|
+
const lastPart = parts[parts.length - 1];
|
|
78
|
+
if (lastPart) {
|
|
79
|
+
return lastPart.replace(/\.(jpg|jpeg|png|webp|gif)$/i, '');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// ignore
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
async search(options) {
|
|
88
|
+
try {
|
|
89
|
+
const rawQuery = options.query || '';
|
|
90
|
+
const query = rawQuery.replace(/[""]/g, '').replace(/[']/g, '').replace(/\s+/g, ' ').trim();
|
|
91
|
+
const performSearch = async (q) => {
|
|
92
|
+
const url = `${BASE_URL}/search?keyword=${encodeURIComponent(q)}`;
|
|
93
|
+
const response = await fetch(url);
|
|
94
|
+
if (!response.ok)
|
|
95
|
+
return [];
|
|
96
|
+
const data = (await response.json());
|
|
97
|
+
if (!data.success || !data.data)
|
|
98
|
+
return [];
|
|
99
|
+
return data.data.filter((anime) => anime.title !== 'Dogge');
|
|
100
|
+
};
|
|
101
|
+
let results = await performSearch(query);
|
|
102
|
+
if (results.length === 0) {
|
|
103
|
+
if (query.includes(':')) {
|
|
104
|
+
results = await performSearch(query.split(':')[0].trim());
|
|
105
|
+
}
|
|
106
|
+
if (results.length === 0 && query.includes('-')) {
|
|
107
|
+
results = await performSearch(query.split('-')[0].trim());
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return results.map((anime) => {
|
|
111
|
+
const imageUrl = anime.thumbnail || anime.image || anime.poster;
|
|
112
|
+
const slugFromUrl = this.extractSlugFromUrl(imageUrl);
|
|
113
|
+
const titleForSlug = anime.japanese_title || anime.title;
|
|
114
|
+
const id = anime.id || slugFromUrl || this.normalizeSlugForSearch(titleForSlug);
|
|
115
|
+
return {
|
|
116
|
+
_id: id,
|
|
117
|
+
id: id,
|
|
118
|
+
name: anime.title,
|
|
119
|
+
englishName: anime.title,
|
|
120
|
+
thumbnail: imageUrl,
|
|
121
|
+
type: anime.type,
|
|
122
|
+
availableEpisodesDetail: {
|
|
123
|
+
sub: Array.from({ length: Number(anime.episode) || 0 }, (_, i) => (i + 1).toString()),
|
|
124
|
+
dub: [],
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
logger_1.default.error({ err: error }, '123Anime search failed');
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async getEpisodes(showId) {
|
|
135
|
+
try {
|
|
136
|
+
const cacheKey = `123anime_eps_${showId}`;
|
|
137
|
+
const cached = this.cache.get(cacheKey);
|
|
138
|
+
if (cached) {
|
|
139
|
+
return cached;
|
|
140
|
+
}
|
|
141
|
+
const results = await this.search({ query: showId.replace(/ /g, '-') });
|
|
142
|
+
const show = results.find((s) => s.id === showId || s._id === showId) ||
|
|
143
|
+
(results.length > 0 ? this.bestMatch(results, showId) : undefined);
|
|
144
|
+
if (!show || !show.availableEpisodesDetail) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const episodes = show.availableEpisodesDetail.sub || [];
|
|
148
|
+
const result = {
|
|
149
|
+
episodes,
|
|
150
|
+
description: '',
|
|
151
|
+
};
|
|
152
|
+
this.cache.set(cacheKey, result, 3600);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
logger_1.default.error({ err: error, showId }, '123Anime getEpisodes failed');
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async getStreamUrls(showId, episodeNumber) {
|
|
161
|
+
try {
|
|
162
|
+
const query = showId.replace(/ /g, '-');
|
|
163
|
+
const searchResults = await this.search({ query });
|
|
164
|
+
if (!searchResults || searchResults.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const match = searchResults.find((s) => s.id === showId || s._id === showId) ||
|
|
168
|
+
this.bestMatch(searchResults, showId);
|
|
169
|
+
const animeId = match.id || match._id;
|
|
170
|
+
const url = `${BASE_URL}/episode-stream?id=${animeId}&ep=${episodeNumber}`;
|
|
171
|
+
const response = await fetch(url);
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
logger_1.default.warn({ url, status: response.status }, '123Anime stream request failed');
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const data = (await response.json());
|
|
177
|
+
if (!data.success || !data.data) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const streamingLink = data.data['streaming_link'] || data.data['stream'] || data.data['url'];
|
|
181
|
+
if (!streamingLink) {
|
|
182
|
+
logger_1.default.warn({ data }, '123Anime No streaming link found in response data');
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const separator = streamingLink.includes('?') ? '&' : '?';
|
|
186
|
+
const finalUrl = `${streamingLink}${separator}autoplay=1`;
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
sourceName: '123Anime',
|
|
190
|
+
links: [
|
|
191
|
+
{
|
|
192
|
+
resolutionStr: 'auto',
|
|
193
|
+
link: finalUrl,
|
|
194
|
+
hls: false,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
type: 'iframe',
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
logger_1.default.error({ err: error, showId, episodeNumber }, '123Anime getStreamUrls failed');
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async getShowMeta(showId) {
|
|
207
|
+
const results = await this.search({ query: showId.replace(/ /g, '-') });
|
|
208
|
+
return results.find((s) => s.id === showId || s._id === showId) || null;
|
|
209
|
+
}
|
|
210
|
+
async getPopular(_timeframe, _page, _size) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
async getSchedule(_date) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
async getSeasonal(_page) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
async getLatestReleases(_page, _size) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
async getSkipTimes(_showId, _episodeNumber) {
|
|
223
|
+
return { found: false, results: [] };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports._123AnimeProvider = _123AnimeProvider;
|