ani-auto 1.4.4 → 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 (89) 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 +15 -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 +62 -11
  59. package/src/scraper.js +2 -2
  60. package/src/server.js +108 -0
  61. package/utils.js +21 -4
  62. package/web/index.html +13 -0
  63. package/web/package-lock.json +1420 -0
  64. package/web/package.json +24 -0
  65. package/web/src/App.vue +200 -0
  66. package/web/src/api/client.js +75 -0
  67. package/web/src/assets/styles/base.css +961 -0
  68. package/web/src/components/UpdateModal.vue +157 -0
  69. package/web/src/components/sidebar/SidebarResizable.vue +170 -0
  70. package/web/src/components/sidebar/sidebarConfig.js +24 -0
  71. package/web/src/main.js +104 -0
  72. package/web/src/router/index.js +44 -0
  73. package/web/src/stores/config.js +27 -0
  74. package/web/src/stores/download.js +107 -0
  75. package/web/src/stores/update.js +73 -0
  76. package/web/src/views/About.vue +42 -0
  77. package/web/src/views/AniListSync.vue +122 -0
  78. package/web/src/views/AnimeDetail.vue +202 -0
  79. package/web/src/views/AnimeList.vue +110 -0
  80. package/web/src/views/CFResult.vue +312 -0
  81. package/web/src/views/DaemonControl.vue +83 -0
  82. package/web/src/views/Dashboard.vue +187 -0
  83. package/web/src/views/DownloadCenter.vue +153 -0
  84. package/web/src/views/MatchFinder.vue +131 -0
  85. package/web/src/views/OAuthCallback.vue +44 -0
  86. package/web/src/views/Onboarding.vue +93 -0
  87. package/web/src/views/RunHistory.vue +122 -0
  88. package/web/src/views/Settings.vue +410 -0
  89. package/web/vite.config.js +23 -0
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "ani-auto-web",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@hugeicons/core-free-icons": "^4.2.2",
13
+ "@hugeicons/vue": "^1.0.7",
14
+ "@primeuix/themes": "^2.0.3",
15
+ "pinia": "^2.1.0",
16
+ "primevue": "^4.5.5",
17
+ "vue": "^3.4.0",
18
+ "vue-router": "^4.3.0"
19
+ },
20
+ "devDependencies": {
21
+ "@vitejs/plugin-vue": "^5.0.0",
22
+ "vite": "^5.4.0"
23
+ }
24
+ }
@@ -0,0 +1,200 @@
1
+ <template>
2
+ <div v-if="isFullLayout" id="app">
3
+ <router-view />
4
+ </div>
5
+ <div v-else id="app">
6
+ <Toast position="top-right" />
7
+ <Toast position="top-left" />
8
+ <div class="sidebar-overlay" v-if="mobileSidebarOpen" @click="closeMobileSidebar"></div>
9
+ <SidebarResizable
10
+ :collapsed="collapsed"
11
+ :width="sidebarWidth"
12
+ :mobileOpen="mobileSidebarOpen"
13
+ @update:collapsed="collapsed = $event"
14
+ @update:width="sidebarWidth = $event"
15
+ @navigate="closeMobileSidebar"
16
+ />
17
+ <div class="main-column" :style="mainColumnStyle">
18
+ <header class="top-bar">
19
+ <div class="top-bar-left">
20
+ <button class="hamburger" @click="toggleMobileSidebar" aria-label="Toggle navigation">
21
+ <HugeiconsIcon :icon="Menu01Icon" :size="20" />
22
+ </button>
23
+ <span class="top-bar-label">{{ pageLabel }}</span>
24
+ </div>
25
+ <div class="top-bar-right">
26
+ <span
27
+ v-if="updateStore.updateAvailable"
28
+ class="update-pill"
29
+ @click="showUpdateModal = true"
30
+ >Update v{{ updateStore.latestVersion }}</span>
31
+ <span class="top-bar-time">{{ topBarStatus }}</span>
32
+ </div>
33
+ </header>
34
+ <main class="main">
35
+ <router-view />
36
+ </main>
37
+ </div>
38
+ <UpdateModal :visible="showUpdateModal" @close="onModalClose" />
39
+ </div>
40
+ </template>
41
+
42
+ <script setup>
43
+ import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
44
+ import { useRoute } from 'vue-router'
45
+ import { useConfigStore } from './stores/config'
46
+ import { useDownloadStore } from './stores/download'
47
+ import { useUpdateStore } from './stores/update'
48
+ import { useToast } from 'primevue/usetoast'
49
+ import { HugeiconsIcon } from '@hugeicons/vue'
50
+ import { Menu01Icon } from '@hugeicons/core-free-icons'
51
+ import SidebarResizable from './components/sidebar/SidebarResizable.vue'
52
+ import UpdateModal from './components/UpdateModal.vue'
53
+
54
+ const route = useRoute()
55
+ const configStore = useConfigStore()
56
+ const downloadStore = useDownloadStore()
57
+ const updateStore = useUpdateStore()
58
+ const toast = useToast()
59
+
60
+ const isFullLayout = computed(() => route.meta?.layout === 'full')
61
+
62
+ const pageLabels = {
63
+ 'dashboard': 'Dashboard',
64
+ 'onboarding': 'Onboarding',
65
+ 'oauth-callback': 'OAuth',
66
+ 'anime': 'Anime',
67
+ 'anime-detail': 'Anime',
68
+ 'anilist-sync': 'AniList Sync',
69
+ 'match': 'Match Finder',
70
+ 'download': 'Download Center',
71
+ 'settings': 'Settings',
72
+ 'daemon': 'Daemon Control',
73
+ 'runs': 'Run History',
74
+ 'about': 'About',
75
+ }
76
+
77
+ const pageLabel = computed(() => pageLabels[route.name] || 'ani-auto')
78
+
79
+ const topBarStatus = computed(() => {
80
+ const ds = downloadStore
81
+ if (ds.running) {
82
+ const active = Object.keys(ds.progress).length
83
+ return active ? `${active} downloading` : 'Starting...'
84
+ }
85
+ return 'Idle'
86
+ })
87
+
88
+ const showUpdateModal = ref(false)
89
+
90
+ // Resizable sidebar
91
+ const collapsed = ref(false)
92
+ const sidebarWidth = ref(200)
93
+ const lastExpandedWidth = ref(200)
94
+ const STORAGE_KEY = 'ani-auto-sidebar-w'
95
+
96
+ const mainColumnStyle = computed(() => {
97
+ if (isMobile.value) return {}
98
+ return { marginLeft: collapsed.value ? '56px' : sidebarWidth.value + 'px' }
99
+ })
100
+
101
+ // Mobile sidebar
102
+ const mobileSidebarOpen = ref(false)
103
+ const isMobile = ref(false)
104
+
105
+ function checkMobile() {
106
+ isMobile.value = window.innerWidth < 768
107
+ if (!isMobile.value) mobileSidebarOpen.value = false
108
+ }
109
+
110
+ function toggleMobileSidebar() {
111
+ mobileSidebarOpen.value = !mobileSidebarOpen.value
112
+ }
113
+
114
+ function closeMobileSidebar() {
115
+ mobileSidebarOpen.value = false
116
+ }
117
+
118
+ function onModalClose() {
119
+ showUpdateModal.value = false
120
+ updateStore.reset()
121
+ }
122
+
123
+ watch(() => updateStore.updateAvailable, (available) => {
124
+ if (available) {
125
+ toast.add({
126
+ severity: 'info',
127
+ summary: 'Update Available',
128
+ detail: `v${updateStore.latestVersion}`,
129
+ group: 'top-left',
130
+ life: 6000,
131
+ })
132
+ }
133
+ })
134
+
135
+ watch(() => updateStore.installDone, (done) => {
136
+ if (done) {
137
+ toast.add({
138
+ severity: 'success',
139
+ summary: 'Update Complete',
140
+ detail: updateStore.installMessage,
141
+ group: 'top-left',
142
+ life: 8000,
143
+ })
144
+ }
145
+ })
146
+
147
+ watch(() => updateStore.installError, (err) => {
148
+ if (err) {
149
+ toast.add({
150
+ severity: 'error',
151
+ summary: 'Update Failed',
152
+ detail: err,
153
+ group: 'top-left',
154
+ life: 10000,
155
+ })
156
+ }
157
+ })
158
+
159
+ onMounted(async () => {
160
+ checkMobile()
161
+ window.addEventListener('resize', checkMobile)
162
+
163
+ const saved = localStorage.getItem(STORAGE_KEY)
164
+ if (saved) {
165
+ const parsed = JSON.parse(saved)
166
+ sidebarWidth.value = parsed.width || 200
167
+ collapsed.value = parsed.collapsed || false
168
+ lastExpandedWidth.value = parsed.lastExpanded || 200
169
+ }
170
+
171
+ if (!isFullLayout.value) {
172
+ await configStore.fetch()
173
+ if (configStore.isConfigured) {
174
+ downloadStore.connectWebSocket()
175
+ downloadStore.onEvent = (msg) => {
176
+ if (msg.type?.startsWith('update:')) {
177
+ updateStore.handleEvent(msg)
178
+ }
179
+ }
180
+ if (configStore.config?.autoUpdateCheck !== false) {
181
+ updateStore.check()
182
+ }
183
+ }
184
+ }
185
+ })
186
+
187
+ watch([collapsed, sidebarWidth], () => {
188
+ if (isMobile.value) return
189
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({
190
+ width: sidebarWidth.value,
191
+ collapsed: collapsed.value,
192
+ lastExpanded: lastExpandedWidth.value,
193
+ }))
194
+ })
195
+
196
+ onUnmounted(() => {
197
+ window.removeEventListener('resize', checkMobile)
198
+ downloadStore.onEvent = null
199
+ })
200
+ </script>
@@ -0,0 +1,75 @@
1
+ const BASE = '/api'
2
+
3
+ async function request(method, path, body = null) {
4
+ const opts = {
5
+ method,
6
+ headers: { 'Content-Type': 'application/json' },
7
+ }
8
+ if (body) opts.body = JSON.stringify(body)
9
+ const res = await fetch(`${BASE}${path}`, opts)
10
+ if (!res.ok) {
11
+ const err = await res.json().catch(() => ({ error: res.statusText }))
12
+ throw new Error(err.error || `HTTP ${res.status}`)
13
+ }
14
+ return res.json()
15
+ }
16
+
17
+ export const api = {
18
+ config: {
19
+ get: () => request('GET', '/config'),
20
+ update: (data) => request('PUT', '/config', data),
21
+ oauthUrl: () => request('GET', '/config/oauth-url'),
22
+ verifyToken: (token) => request('POST', '/config/verify-token', { token }),
23
+ },
24
+ cfBypass: {
25
+ detect: () => request('POST', '/cf-bypass/detect'),
26
+ save: (data) => request('POST', '/cf-bypass/save', data),
27
+ status: () => request('GET', '/cf-bypass/status'),
28
+ clear: () => request('DELETE', '/cf-bypass'),
29
+ },
30
+ anilist: {
31
+ list: (statuses) => request('GET', `/anilist/list${statuses ? '?statuses=' + statuses : ''}`),
32
+ search: (q) => request('GET', `/anilist/search?q=${encodeURIComponent(q)}`),
33
+ },
34
+ anime: {
35
+ list: () => request('GET', '/anime'),
36
+ get: (id) => request('GET', `/anime/${id}`),
37
+ episodes: (id) => request('GET', `/anime/${id}/episodes`),
38
+ add: (data) => request('POST', '/anime', data),
39
+ remove: (id) => request('DELETE', `/anime/${id}`),
40
+ setEnabled: (id, enabled) => request('PUT', `/anime/${id}/enable`, { enabled }),
41
+ setQuality: (id, quality) => request('PUT', `/anime/${id}/quality`, { quality }),
42
+ setLanguage: (id, language) => request('PUT', `/anime/${id}/language`, { language }),
43
+ },
44
+ match: {
45
+ search: (q, anilistId) => request('GET', `/match/search?q=${encodeURIComponent(q)}${anilistId ? '&anilistId=' + anilistId : ''}`),
46
+ confirm: (data) => request('POST', '/match/confirm', data),
47
+ },
48
+ download: {
49
+ run: (opts) => request('POST', '/download/run', opts),
50
+ anime: (id, opts) => request('POST', `/download/anime/${id}`, opts),
51
+ retry: () => request('POST', '/download/retry'),
52
+ },
53
+ runs: {
54
+ list: (limit) => request('GET', `/runs${limit ? '?limit=' + limit : ''}`),
55
+ },
56
+ daemon: {
57
+ status: () => request('GET', '/daemon/status'),
58
+ start: () => request('POST', '/daemon/start'),
59
+ stop: () => request('POST', '/daemon/stop'),
60
+ restart: () => request('POST', '/daemon/restart'),
61
+ enable: () => request('POST', '/daemon/enable'),
62
+ disable: () => request('POST', '/daemon/disable'),
63
+ },
64
+ update: {
65
+ check: () => request('GET', '/update/check'),
66
+ install: () => request('POST', '/update/install'),
67
+ },
68
+ }
69
+
70
+ export function createWebSocket() {
71
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
72
+ const host = window.location.host
73
+ const ws = new WebSocket(`${protocol}//${host}/ws`)
74
+ return ws
75
+ }