weebcli 1.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.
- package/.github/workflows/releases.yml +39 -0
- package/LICENSE +400 -0
- package/README-EN.md +134 -0
- package/README.md +134 -0
- package/aur/.SRCINFO +16 -0
- package/aur/PKGBUILD +43 -0
- package/eslint.config.js +49 -0
- package/jsconfig.json +9 -0
- package/package.json +45 -0
- package/src/constants.js +13 -0
- package/src/functions/episodes.js +38 -0
- package/src/functions/time.js +27 -0
- package/src/functions/variables.js +3 -0
- package/src/i18n/en.json +351 -0
- package/src/i18n/index.js +80 -0
- package/src/i18n/tr.json +348 -0
- package/src/index.js +307 -0
- package/src/jsdoc.js +72 -0
- package/src/sources/allanime.js +195 -0
- package/src/sources/animecix.js +223 -0
- package/src/sources/animely.js +100 -0
- package/src/sources/handlers/allanime.js +318 -0
- package/src/sources/handlers/animecix.js +316 -0
- package/src/sources/handlers/animely.js +338 -0
- package/src/sources/handlers/base.js +391 -0
- package/src/sources/handlers/index.js +4 -0
- package/src/sources/index.js +80 -0
- package/src/utils/anilist.js +193 -0
- package/src/utils/data_manager.js +27 -0
- package/src/utils/discord.js +86 -0
- package/src/utils/download/concurrency.js +27 -0
- package/src/utils/download/download.js +485 -0
- package/src/utils/download/progress.js +84 -0
- package/src/utils/players/mpv.js +251 -0
- package/src/utils/players/vlc.js +120 -0
- package/src/utils/process_queue.js +121 -0
- package/src/utils/resume_watch.js +137 -0
- package/src/utils/search.js +39 -0
- package/src/utils/search_download.js +21 -0
- package/src/utils/speedtest.js +30 -0
- package/src/utils/spinner.js +7 -0
- package/src/utils/storage/cache.js +42 -0
- package/src/utils/storage/config.js +71 -0
- package/src/utils/storage/history.js +69 -0
- package/src/utils/storage/queue.js +43 -0
- package/src/utils/storage/watch_progress.js +104 -0
- package/src/utils/system.js +176 -0
- package/src/utils/ui/box.js +140 -0
- package/src/utils/ui/settings_ui.js +322 -0
- package/src/utils/ui/show_history.js +92 -0
- package/src/utils/ui/stats.js +67 -0
- package/start.js +21 -0
- package/tanitim-en.md +66 -0
- package/tanitim-tr.md +66 -0
package/src/i18n/tr.json
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
{
|
|
2
|
+
"app": {
|
|
3
|
+
"title": "Animely CLI",
|
|
4
|
+
"goodbye": "Görüşmek üzere!",
|
|
5
|
+
"unexpectedError": "Beklenmeyen bir hata oluştu, lütfen daha sonra tekrar deneyiniz.",
|
|
6
|
+
"errorDetail": "Hata detayı: {message}"
|
|
7
|
+
},
|
|
8
|
+
"update": {
|
|
9
|
+
"available": "Yeni güncelleme mevcut: {current} -> {latest}",
|
|
10
|
+
"starting": "Otomatik güncelleme başlatılıyor...",
|
|
11
|
+
"completed": "Güncelleme tamamlandı! Uygulama yeniden başlatılıyor...",
|
|
12
|
+
"failed": "Otomatik güncelleme başarısız oldu.",
|
|
13
|
+
"manualInstall": "Lütfen 'npm install -g animely' komutunu elle çalıştırın."
|
|
14
|
+
},
|
|
15
|
+
"setup": {
|
|
16
|
+
"welcome": "Animely'e hoş geldiniz. İlk kurulumu tamamlayalım.",
|
|
17
|
+
"languagePrompt": "Dil seçin / Select language:",
|
|
18
|
+
"playerConfig": "Medya oynatıcı yapılandırması:",
|
|
19
|
+
"playerQuestion": "Sisteminizde yüklü bir medya oynatıcı (VLC/MPV) var mı?",
|
|
20
|
+
"playerYes": "Evet, var",
|
|
21
|
+
"playerInstall": "Hayır, otomatik indir (Winget)",
|
|
22
|
+
"playerWeb": "Hayır, tarayıcıda aç (VideoLAN.org)",
|
|
23
|
+
"playerSelect": "Hangi oynatıcıyı kurmak istersiniz?",
|
|
24
|
+
"mpvRecommended": "MPV Player (Önerilen)",
|
|
25
|
+
"vlcPlayer": "VLC Media Player",
|
|
26
|
+
"installing": "{player} kuruluyor, lütfen bekleyin...",
|
|
27
|
+
"installSuccess": "Kurulum başarıyla tamamlandı.",
|
|
28
|
+
"installFailed": "Otomatik kurulum başarısız oldu. Lütfen manuel olarak indirin.",
|
|
29
|
+
"webOpened": "İndirme sayfası tarayıcıda açıldı. Kurulumu tamamlayıp geri dönün.",
|
|
30
|
+
"defaultPlayerPrompt": "Varsayılan oynatıcı olarak hangisi kullanılsın:",
|
|
31
|
+
"mpvWithResume": "MPV Player (Önerilen - Kaldığı yerden devam desteği)",
|
|
32
|
+
"settingsSaved": "Ayarlar kaydedildi, uygulama başlatılıyor..."
|
|
33
|
+
},
|
|
34
|
+
"menu": {
|
|
35
|
+
"browsingMenu": "Menüde geziniyor",
|
|
36
|
+
"incompleteDownloads": "Tamamlanmamış {count} indirme görevi var!",
|
|
37
|
+
"selectAction": "Bir işlem seçin:",
|
|
38
|
+
"searchAnime": "Anime Ara",
|
|
39
|
+
"watchHistory": "İzleme Geçmişi",
|
|
40
|
+
"settings": "Ayarlar",
|
|
41
|
+
"resume": "Devam Et: {name} ({episode}. Bölüm)",
|
|
42
|
+
"startQueue": "İndirme Kuyruğunu Başlat ({count} Bölüm)",
|
|
43
|
+
"clearQueue": "İndirme Kuyruğunu Temizle",
|
|
44
|
+
"exit": "Çıkış"
|
|
45
|
+
},
|
|
46
|
+
"search": {
|
|
47
|
+
"enterName": "Aramak istediğiniz animenin adını girin (İptal için 'iptal' yazın):",
|
|
48
|
+
"invalidName": "Lütfen geçerli bir anime adı giriniz.",
|
|
49
|
+
"cancel": "iptal",
|
|
50
|
+
"searching": "Aranıyor...",
|
|
51
|
+
"searchFailed": "Arama yapılamadı.",
|
|
52
|
+
"noResults": "Sonuç bulunamadı.",
|
|
53
|
+
"notFound": "Üzgünüz, aradığınız kriterlere uygun anime bulunamadı.",
|
|
54
|
+
"foundCount": "{count} adet anime bulundu:",
|
|
55
|
+
"selectAnime": "Hangi animeyi seçmek istersiniz?",
|
|
56
|
+
"goBack": "Geri Dön",
|
|
57
|
+
"season": "Sezon",
|
|
58
|
+
"episodes": "Bölüm"
|
|
59
|
+
},
|
|
60
|
+
"episodes": {
|
|
61
|
+
"loading": "Bölümler yükleniyor...",
|
|
62
|
+
"loadFailed": "Bölümler alınamadı.",
|
|
63
|
+
"notFound": "Bu anime için bölüm bulunamadı.",
|
|
64
|
+
"noEpisodes": "Bu anime için henüz bölüm bulunamadı.",
|
|
65
|
+
"episodeCount": "{count} bölüm",
|
|
66
|
+
"selectAction": "Bir işlem seçin:",
|
|
67
|
+
"watch": "Bölümü İzle",
|
|
68
|
+
"download": "Bölümleri İndir",
|
|
69
|
+
"goBack": "Geri Dön",
|
|
70
|
+
"selectEpisode": "İzlemek istediğiniz bölümü seçin:",
|
|
71
|
+
"lastWatched": "Son izlenen",
|
|
72
|
+
"nextUp": "Sıradaki",
|
|
73
|
+
"episode": "Bölüm",
|
|
74
|
+
"episodeNumber": "{number}. Bölüm"
|
|
75
|
+
},
|
|
76
|
+
"player": {
|
|
77
|
+
"gettingLink": "İzleme linki alınıyor...",
|
|
78
|
+
"linkFailed": "İzleme linki alınamadı.",
|
|
79
|
+
"sourceNotFound": "İzleme kaynağı bulunamadı.",
|
|
80
|
+
"selectQuality": "Kalite seçin:",
|
|
81
|
+
"default": "Varsayılan",
|
|
82
|
+
"opening": "{name} — {episode}. Bölüm {player} ile açılıyor...",
|
|
83
|
+
"resumePrompt": "Kaldığınız yerden devam etmek ister misiniz? ({time})",
|
|
84
|
+
"markWatched": "Bölümü 'İzlendi' olarak işaretlemek ister misiniz?",
|
|
85
|
+
"historyUpdated": "İzleme geçmişi güncellendi!",
|
|
86
|
+
"anilistUpdated": "Anilist başarıyla güncellendi!",
|
|
87
|
+
"anilistSearching": "Anilist veritabanında aranıyor...",
|
|
88
|
+
"anilistUpdating": "Anilist güncelleniyor...",
|
|
89
|
+
"playerError": "Oynatıcı hatası: {message}",
|
|
90
|
+
"episodeNotFound": "{episode}. bölüm bulunamadı (Henüz yayınlanmamış olabilir).",
|
|
91
|
+
"linkNotFound": "İzleme linki bulunamadı.",
|
|
92
|
+
"episodeOpening": "{name} — {episode}. bölüm açılıyor ({player})..."
|
|
93
|
+
},
|
|
94
|
+
"download": {
|
|
95
|
+
"downloadTitle": "{name} - İndir",
|
|
96
|
+
"selectionMethod": "Bölümleri nasıl seçmek istersiniz?",
|
|
97
|
+
"selectFromList": "Listeden seç (Tek tek)",
|
|
98
|
+
"enterRange": "Aralık gir (Örn: 1-12, 15)",
|
|
99
|
+
"downloadAll": "Tümünü indir",
|
|
100
|
+
"goBack": "Geri Dön",
|
|
101
|
+
"episodeSelection": "{name} - Bölüm Seçimi",
|
|
102
|
+
"selectEpisodes": "İndirmek istediğiniz bölümleri seçin (Boşluk ile seçip Enter ile onaylayın):",
|
|
103
|
+
"rangeSelection": "{name} - Aralık Seçimi",
|
|
104
|
+
"enterRangePrompt": "Bölüm aralığını giriniz (Örn: 1-12, 15, 20-25):",
|
|
105
|
+
"invalidRange": "Lütfen geçerli bir aralık giriniz.",
|
|
106
|
+
"noEpisodeSelected": "Hiçbir bölüm seçilmedi.",
|
|
107
|
+
"gettingQualities": "Kalite seçenekleri alınıyor...",
|
|
108
|
+
"selectQuality": "İndirme kalitesini seçin:",
|
|
109
|
+
"episodesSelected": "{count} bölüm seçildi. Eşzamanlı en fazla {limit} indirme yapılacak.",
|
|
110
|
+
"downloadComplete": "İndirme işlemi tamamlandı.",
|
|
111
|
+
"episodeDownloaded": "{name} — {episode}. bölüm başarıyla indirildi.",
|
|
112
|
+
"gettingLink": "{episode}. bölüm için link alınıyor...",
|
|
113
|
+
"linkNotFound": "İndirme linki bulunamadı.",
|
|
114
|
+
"addToQueue": "İndirme Kuyruğuna Ekle",
|
|
115
|
+
"downloadNow": "Hemen İndir",
|
|
116
|
+
"whatToDo": "Ne yapmak istersiniz?",
|
|
117
|
+
"addedToQueue": "{count} bölüm kuyruğa eklendi.",
|
|
118
|
+
"downloadStarting": "İndirme başlıyor...",
|
|
119
|
+
"fileSizeTooSmall": "Dosya boyutu çok küçük, hatalı indirme olabilir."
|
|
120
|
+
},
|
|
121
|
+
"queue": {
|
|
122
|
+
"title": "İndirme Kuyruğu",
|
|
123
|
+
"totalEpisodes": "Toplam Bölüm: {count}",
|
|
124
|
+
"estimatedSize": "Tahmini Boyut: ~{size} GB",
|
|
125
|
+
"estimatedTime": "Tahmini Süre: ~{minutes} dk ({speed} Mbps ile)",
|
|
126
|
+
"confirmStart": "İndirme işlemini başlatmak istiyor musunuz?",
|
|
127
|
+
"downloading": "{count} bölüm indirilecek. Eşzamanlı indirme limiti: {limit}",
|
|
128
|
+
"allComplete": "Tüm indirmeler tamamlandı.",
|
|
129
|
+
"notificationComplete": "İndirme işlemi tamamlandı.",
|
|
130
|
+
"confirmClear": "İndirme kuyruğunu temizlemek istediğinize emin misiniz?",
|
|
131
|
+
"cleared": "İndirme kuyruğu başarıyla temizlendi!"
|
|
132
|
+
},
|
|
133
|
+
"history": {
|
|
134
|
+
"title": "İzleme Geçmişi",
|
|
135
|
+
"selectList": "Hangi listeyi görüntülemek istersiniz?",
|
|
136
|
+
"statistics": "İstatistikler",
|
|
137
|
+
"inProgress": "Devam Edenler ({count})",
|
|
138
|
+
"completed": "Tamamlananlar ({count})",
|
|
139
|
+
"goBack": "Geri Dön",
|
|
140
|
+
"completedTitle": "Tamamlanan Animeler",
|
|
141
|
+
"noCompleted": "Henüz tamamlanmış bir anime yok.",
|
|
142
|
+
"noWatching": "Henüz izlenen bir anime yok.",
|
|
143
|
+
"selectForDetails": "Detaylarını görmek istediğiniz animeyi seçin:",
|
|
144
|
+
"lastEpisode": "Son izlenen: {episode}. Bölüm",
|
|
145
|
+
"lastEpisodeLabel": "Son izlenen bölüm: {episode}",
|
|
146
|
+
"totalEpisodes": "Toplam bölüm: {count}",
|
|
147
|
+
"lastWatchedDate": "Son izleme tarihi: {date}",
|
|
148
|
+
"pressEnter": "Geri dönmek için Enter'a basın..."
|
|
149
|
+
},
|
|
150
|
+
"stats": {
|
|
151
|
+
"emptyHistory": "İzleme geçmişiniz henüz boş...",
|
|
152
|
+
"totalAnime": "Toplam Anime:",
|
|
153
|
+
"completedAnime": "Tamamlananlar:",
|
|
154
|
+
"watchingAnime": "Devam Edenler:",
|
|
155
|
+
"totalEpisodes": "Toplam Bölüm:",
|
|
156
|
+
"watchTime": "İzleme Süresi:",
|
|
157
|
+
"hours": "{hours} saat",
|
|
158
|
+
"days": "yaklaşık {days} gün",
|
|
159
|
+
"lastWatched": "Son İzlenen:",
|
|
160
|
+
"pressEnter": "Geri dönmek için Enter'a basın",
|
|
161
|
+
"timeAgo": {
|
|
162
|
+
"years": "{count} yıl önce",
|
|
163
|
+
"months": "{count} ay önce",
|
|
164
|
+
"days": "{count} gün önce",
|
|
165
|
+
"hours": "{count} saat önce",
|
|
166
|
+
"minutes": "{count} dk önce",
|
|
167
|
+
"justNow": "az önce"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"settings": {
|
|
171
|
+
"title": "Ayarlar",
|
|
172
|
+
"whatToChange": "Neyi değiştirmek istersin?",
|
|
173
|
+
"language": "Dil (Mevcut: {current})",
|
|
174
|
+
"animeSource": "Anime Kaynağı (Mevcut: {current})",
|
|
175
|
+
"player": "Oynatıcı (Mevcut: {current})",
|
|
176
|
+
"aria2": "İndirme Yöneticisi (Aria2) ({status})",
|
|
177
|
+
"aria2Connections": " ↳ Aria2 Bağlantı Sayısı (Mevcut: {count}x)",
|
|
178
|
+
"ytDlp": "M3U8 İndirme Yöneticisi (yt-dlp) ({status})",
|
|
179
|
+
"ytDlpConnections": " ↳ yt-dlp Bağlantı Sayısı (Mevcut: {count}x)",
|
|
180
|
+
"concurrentLimit": "Eşzamanlı İndirme Limiti (Mevcut: {count})",
|
|
181
|
+
"downloadLocation": "İndirme Konumu (Mevcut: {path})",
|
|
182
|
+
"retryDownload": "İndirme Tekrarı (Mevcut: {status})",
|
|
183
|
+
"retrySettings": " ↳ Tekrar Ayarları (Mevcut: {count}x / {delay}sn)",
|
|
184
|
+
"showDetails": "Anime Detaylarını Göster (Mevcut: {status})",
|
|
185
|
+
"anilist": "Anilist Entegrasyonu (Durum: {status})",
|
|
186
|
+
"anilistLogout": " ↳ Bağlantıyı Kes",
|
|
187
|
+
"telemetry": "Telemetri (Anonim) (Durum: {status})",
|
|
188
|
+
"backToMenu": "Ana Menüye Dön",
|
|
189
|
+
"active": "Aktif",
|
|
190
|
+
"inactive": "Pasif",
|
|
191
|
+
"notSelected": "Seçilmedi",
|
|
192
|
+
"notConnected": "Bağlı Değil",
|
|
193
|
+
"advancedSearch": "Gelişmiş arama",
|
|
194
|
+
"selectSource": "Anime kaynağını seçin:",
|
|
195
|
+
"sourceUpdated": "Anime kaynağı güncellendi.",
|
|
196
|
+
"detailsEnabled": "Anime detay görünümü etkinleştirildi.",
|
|
197
|
+
"detailsDisabled": "Anime detay görünümü devre dışı bırakıldı.",
|
|
198
|
+
"anilistDisconnectConfirm": "Anilist bağlantısını kesmek istediğinize emin misiniz?",
|
|
199
|
+
"anilistDisconnected": "Anilist bağlantısı kesildi.",
|
|
200
|
+
"operationCancelled": "İşlem iptal edildi.",
|
|
201
|
+
"telemetryEnabled": "Telemetri etkinleştirildi.",
|
|
202
|
+
"telemetryDisabled": "Telemetri devre dışı bırakıldı.",
|
|
203
|
+
"selectPlayer": "Varsayılan oynatıcıyı seçin:",
|
|
204
|
+
"playerNotFound": "Uyarı: {player} sistemde bulunamadı.",
|
|
205
|
+
"autoInstallPrompt": "Otomatik olarak yüklemek ister misiniz?",
|
|
206
|
+
"installingPlayer": "Yükleme başlatılıyor, lütfen bekleyin...",
|
|
207
|
+
"playerInstalled": "{player} başarıyla yüklendi!",
|
|
208
|
+
"installFailed": "Yükleme başarısız oldu. Yine de oynatıcı olarak seçildi.",
|
|
209
|
+
"playerUpdated": "Varsayılan oynatıcı güncellendi.",
|
|
210
|
+
"concurrentPrompt": "Eşzamanlı indirme sayısı (1-10):",
|
|
211
|
+
"concurrentValidation": "Lütfen 1 ile 10 arasında bir değer girin.",
|
|
212
|
+
"concurrentUpdated": "İndirme limiti güncellendi.",
|
|
213
|
+
"aria2Enabled": "Aria2 etkinleştirildi.",
|
|
214
|
+
"aria2Disabled": "Aria2 devre dışı bırakıldı.",
|
|
215
|
+
"aria2ConnectionsPrompt": "Aria2 bağlantı sayısı (1-32):",
|
|
216
|
+
"aria2ConnectionsValidation": "Lütfen 1 ile 32 arasında bir değer girin.",
|
|
217
|
+
"aria2ConnectionsUpdated": "Aria2 bağlantı sayısı güncellendi.",
|
|
218
|
+
"ytDlpEnabled": "yt-dlp etkinleştirildi (M3U8 indirmeleri hızlandırıldı).",
|
|
219
|
+
"ytDlpDisabled": "yt-dlp devre dışı bırakıldı (FFmpeg kullanılacak).",
|
|
220
|
+
"ytDlpConnectionsPrompt": "yt-dlp bağlantı sayısı (1-32):",
|
|
221
|
+
"ytDlpConnectionsValidation": "Lütfen 1 ile 32 arasında bir değer girin.",
|
|
222
|
+
"ytDlpConnectionsUpdated": "yt-dlp bağlantı sayısı güncellendi.",
|
|
223
|
+
"downloadDirPrompt": "Yeni indirme konumu:",
|
|
224
|
+
"downloadDirValidation": "Bu alan boş bırakılamaz.",
|
|
225
|
+
"downloadDirUpdated": "İndirme konumu güncellendi.",
|
|
226
|
+
"retryEnabled": "İndirme tekrarı etkinleştirildi.",
|
|
227
|
+
"retryDisabled": "İndirme tekrarı devre dışı bırakıldı.",
|
|
228
|
+
"retryCountPrompt": "Maksimum tekrar deneme sayısı (0-10):",
|
|
229
|
+
"retryCountValidation": "Lütfen 0 ile 10 arasında bir değer girin.",
|
|
230
|
+
"retryDelayPrompt": "Tekrarlar arası bekleme süresi (saniye):",
|
|
231
|
+
"retryDelayValidation": "Lütfen pozitif bir değer girin.",
|
|
232
|
+
"retryUpdated": "Tekrar ayarları güncellendi.",
|
|
233
|
+
"selectLanguage": "Dil seçin:",
|
|
234
|
+
"languageUpdated": "Dil güncellendi.",
|
|
235
|
+
"turkish": "Türkçe",
|
|
236
|
+
"english": "English"
|
|
237
|
+
},
|
|
238
|
+
"anilist": {
|
|
239
|
+
"verifying": "Token doğrulanıyor...",
|
|
240
|
+
"loginSuccess": "Giriş başarılı! Hoş geldiniz, {username}.",
|
|
241
|
+
"verifyFailed": "Token alındı ancak doğrulama başarısız oldu.",
|
|
242
|
+
"loginFailed": "Giriş yapılamadı: {message}",
|
|
243
|
+
"browserOpening": "Tarayıcı açılıyor... Lütfen AniList hesabınızla giriş yapın.",
|
|
244
|
+
"manualLink": "Eğer açılmazsa bu linke tıklayın: {url}",
|
|
245
|
+
"loggingIn": "Giriş yapılıyor...",
|
|
246
|
+
"loginSuccessPage": "Giriş başarılı! Bu pencereyi kapatabilirsiniz.",
|
|
247
|
+
"tokenNotFound": "Token bulunamadı. Lütfen tekrar deneyin.",
|
|
248
|
+
"updateError": "Anilist güncelleme hatası:",
|
|
249
|
+
"historyUpdating": "Geçmiş güncelleniyor..."
|
|
250
|
+
},
|
|
251
|
+
"spinner": {
|
|
252
|
+
"loading": "Komut yürütülüyor lütfen bekleyiniz.",
|
|
253
|
+
"listUpdating": "Liste güncelleniyor...",
|
|
254
|
+
"fetchingEpisodes": "{name} bölüm listesi getiriliyor..."
|
|
255
|
+
},
|
|
256
|
+
"progress": {
|
|
257
|
+
"waiting": "Bekliyor",
|
|
258
|
+
"downloading": "İndiriliyor",
|
|
259
|
+
"retrying": "Tekrar deneniyor",
|
|
260
|
+
"completed": "Tamamlandı",
|
|
261
|
+
"error": "Hata",
|
|
262
|
+
"errorRetrying": "Hata, tekrar deneniyor",
|
|
263
|
+
"gettingLink": "Link alınıyor",
|
|
264
|
+
"processing": "İşleniyor",
|
|
265
|
+
"remaining": "kalan",
|
|
266
|
+
"downloadStarting": "İndirme başlıyor...",
|
|
267
|
+
"downloadingPercent": "İndiriliyor: {percent}% ({downloaded} / {total})",
|
|
268
|
+
"uploading": "Yükleniyor: {downloaded}",
|
|
269
|
+
"uploadingSpeed": "Yükleniyor: {downloaded} - {speed}/s",
|
|
270
|
+
"progressBar": "[{bar}] {percent}% ({downloaded} / {total}) - {speed}/s - Kalan: {eta}",
|
|
271
|
+
"aria2Progress": "[{bar}] {percent}% - Hız: {speed}",
|
|
272
|
+
"ytDlpProgress": "[{bar}] {percent}% - Hız: {speed} - Kalan: {eta}",
|
|
273
|
+
"processingPercent": "İşleniyor: {percent}% ({downloaded})"
|
|
274
|
+
},
|
|
275
|
+
"errors": {
|
|
276
|
+
"animeListFailed": "Anime listesi alınamadı. İnternet bağlantınızı kontrol edin.",
|
|
277
|
+
"episodesFailed": "Anime bölümleri alınamadı. Lütfen daha sonra tekrar deneyin.",
|
|
278
|
+
"invalidUrl": "Geçersiz URL",
|
|
279
|
+
"noInternet": "İnternet bağlantısı bulunamadı.",
|
|
280
|
+
"timeout": "Bağlantı zaman aşımına uğradı.",
|
|
281
|
+
"downloadFailed": "İndirme başlatılamadı: {message}",
|
|
282
|
+
"serverError": "Sunucu hatası: {status} {statusText}",
|
|
283
|
+
"fileSaved": "Dosya başarıyla kaydedildi: {path}",
|
|
284
|
+
"fileExists": "Dosya zaten indirilmiş: {path}",
|
|
285
|
+
"fileExistsAria2": "Dosya zaten indirilmiş (Aria2): {path}",
|
|
286
|
+
"fileExistsM3U8": "Dosya zaten indirilmiş (M3U8): {path}",
|
|
287
|
+
"resumingDownload": "İndirme devam ettiriliyor: {downloaded} / {total}",
|
|
288
|
+
"fileWriteError": "Dosya yazma hatası: {message}",
|
|
289
|
+
"ffmpegError": "FFmpeg hatası: {message}",
|
|
290
|
+
"aria2Starting": "Aria2 indirme yöneticisi başlatılıyor...",
|
|
291
|
+
"aria2Error": "Aria2c hata kodu ile kapandı: {code}",
|
|
292
|
+
"aria2StartFailed": "Aria2c başlatılamadı: {message}",
|
|
293
|
+
"downloadCompleted": "İndirme tamamlandı: {path}",
|
|
294
|
+
"m3u8Processing": "M3U8 indiriliyor ve işleniyor (Bu işlem biraz zaman alabilir)...",
|
|
295
|
+
"retrying": "Tekrar deneniyor ({attempt}/{total})...",
|
|
296
|
+
"packageNotFound": "Paket tanımı bulunamadı: {name}",
|
|
297
|
+
"wingetNotFound": "Winget paket yöneticisi bulunamadı.",
|
|
298
|
+
"brewNotFound": "Homebrew paket yöneticisi bulunamadı.",
|
|
299
|
+
"packageManagerNotFound": "Desteklenen paket yöneticisi (apt/pacman) bulunamadı.",
|
|
300
|
+
"installError": "Kurulum hatası: {message}",
|
|
301
|
+
"vlcNotFound": "VLC Player bulunamadı. {manager} ile kurulmaya çalışılıyor...",
|
|
302
|
+
"vlcInstalled": "VLC başarıyla kuruldu!",
|
|
303
|
+
"vlcInstallFailed": "VLC kurulumu başarısız oldu.",
|
|
304
|
+
"vlcNotFoundAndFailed": "VLC Player bulunamadı ve kurulamadı.",
|
|
305
|
+
"vlcPathNotFound": "VLC dosya yolu bulunamadı.",
|
|
306
|
+
"vlcStarting": "VLC başlatılıyor...",
|
|
307
|
+
"mpvNotFound": "MPV Player bulunamadı. {manager} ile kurulmaya çalışılıyor...",
|
|
308
|
+
"mpvInstalled": "MPV başarıyla kuruldu!",
|
|
309
|
+
"mpvInstallFailed": "MPV kurulumu başarısız oldu. Lütfen manuel olarak kurunuz: https://mpv.io/installation/",
|
|
310
|
+
"mpvInstallFailedMac": "MPV kurulumu başarısız oldu. Lütfen manuel olarak kurunuz veya Homebrew'in yüklü olduğundan emin olun.",
|
|
311
|
+
"mpvNotFoundAndFailed": "MPV Player bulunamadı ve kurulamadı.",
|
|
312
|
+
"mpvPathNotFound": "MPV Player dosya yolu bulunamadı.",
|
|
313
|
+
"managerDetected": "{manager} tespit edildi. Kurulum başlatılıyor (sudo gerekebilir)...",
|
|
314
|
+
"ytDlpStarting": "yt-dlp ile indirme başlatılıyor (hızlı mod)...",
|
|
315
|
+
"ytDlpFailed": "yt-dlp başarısız oldu, FFmpeg'e geçiliyor...",
|
|
316
|
+
"ytDlpError": "yt-dlp hata kodu ile kapandı: {code}",
|
|
317
|
+
"ytDlpStartFailed": "yt-dlp başlatılamadı: {message}",
|
|
318
|
+
"ytDlpNotFound": "yt-dlp bulunamadı. M3U8 indirmeleri için gerekli.",
|
|
319
|
+
"ytDlpInstalling": "yt-dlp otomatik olarak kuruluyor...",
|
|
320
|
+
"ytDlpInstalled": "yt-dlp başarıyla kuruldu!",
|
|
321
|
+
"ytDlpInstallFailed": "yt-dlp kurulumu başarısız oldu. FFmpeg kullanılacak."
|
|
322
|
+
},
|
|
323
|
+
"common": {
|
|
324
|
+
"yes": "Evet",
|
|
325
|
+
"no": "Hayır",
|
|
326
|
+
"confirm": "Onayla",
|
|
327
|
+
"cancel": "İptal",
|
|
328
|
+
"back": "Geri",
|
|
329
|
+
"next": "İleri",
|
|
330
|
+
"save": "Kaydet",
|
|
331
|
+
"delete": "Sil",
|
|
332
|
+
"edit": "Düzenle",
|
|
333
|
+
"close": "Kapat",
|
|
334
|
+
"loading": "Yükleniyor...",
|
|
335
|
+
"speed": "Hız"
|
|
336
|
+
},
|
|
337
|
+
"ui": {
|
|
338
|
+
"nowPlaying": "Oynatılıyor"
|
|
339
|
+
},
|
|
340
|
+
"discord": {
|
|
341
|
+
"download": "İndir"
|
|
342
|
+
},
|
|
343
|
+
"allanime": {
|
|
344
|
+
"selectAudioType": "Ses türünü seçin:",
|
|
345
|
+
"subbed": "Altyazılı (Sub)",
|
|
346
|
+
"dubbed": "Dublajlı (Dub)"
|
|
347
|
+
}
|
|
348
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spinner } from "./utils/spinner.js";
|
|
3
|
+
import { getConfig, saveConfig } from "./utils/storage/config.js";
|
|
4
|
+
import { loadQueue, saveQueue } from "./utils/storage/queue.js";
|
|
5
|
+
import { initDiscordRpc, setActivity } from "./utils/discord.js";
|
|
6
|
+
import { sources, getSourceById, getSourcesByLanguage, getDefaultSourceForLanguage } from "./sources/index.js";
|
|
7
|
+
import { infoBox } from "./utils/ui/box.js";
|
|
8
|
+
|
|
9
|
+
import { showSettings } from "./utils/ui/settings_ui.js";
|
|
10
|
+
import { showHistory } from "./utils/ui/show_history.js";
|
|
11
|
+
import { processQueue } from "./utils/process_queue.js";
|
|
12
|
+
import { searchAndDownload } from "./utils/search_download.js";
|
|
13
|
+
import { resumeWatch } from "./utils/resume_watch.js";
|
|
14
|
+
import { loadHistory } from "./utils/storage/history.js";
|
|
15
|
+
import { t, setLanguage } from "./i18n/index.js";
|
|
16
|
+
|
|
17
|
+
import { execSync, spawnSync } from "child_process";
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
import fs from "fs";
|
|
20
|
+
import inquirer from "inquirer";
|
|
21
|
+
import updateNotifier from "update-notifier";
|
|
22
|
+
import { runSpeedTest } from "./utils/speedtest.js";
|
|
23
|
+
|
|
24
|
+
process.on('SIGINT', () => {
|
|
25
|
+
console.log(chalk.gray("\n" + t("app.goodbye")));
|
|
26
|
+
process.exit(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
30
|
+
|
|
31
|
+
process.stdout.write(`\x1b]0;Animely CLI | v${pkg.version}\x07`);
|
|
32
|
+
|
|
33
|
+
const notifier = updateNotifier({
|
|
34
|
+
pkg,
|
|
35
|
+
updateCheckInterval: 0
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (notifier.update) {
|
|
39
|
+
console.log(chalk.yellow(`\n${t("update.available", { current: notifier.update.current, latest: notifier.update.latest })}`));
|
|
40
|
+
console.log(chalk.cyan(t("update.starting")));
|
|
41
|
+
try {
|
|
42
|
+
execSync("npm install -g animely");
|
|
43
|
+
console.log(chalk.green(t("update.completed")));
|
|
44
|
+
|
|
45
|
+
const { status } = spawnSync(process.argv[0], process.argv.slice(1), {
|
|
46
|
+
stdio: "inherit"
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
process.exit(status ?? 0);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.log(chalk.red(t("update.failed")));
|
|
52
|
+
console.log(chalk.yellow(t("update.manualInstall")));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
(async () => {
|
|
57
|
+
try {
|
|
58
|
+
await initDiscordRpc();
|
|
59
|
+
|
|
60
|
+
const speedTestPromise = runSpeedTest();
|
|
61
|
+
|
|
62
|
+
spinner.start();
|
|
63
|
+
|
|
64
|
+
const config = getConfig();
|
|
65
|
+
|
|
66
|
+
// Dil ayarını yükle
|
|
67
|
+
if (config.language) {
|
|
68
|
+
setLanguage(config.language);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Dile göre kaynakları filtrele ve mevcut kaynağı kontrol et
|
|
72
|
+
const currentLang = config.language || "tr";
|
|
73
|
+
const availableSources = getSourcesByLanguage(currentLang);
|
|
74
|
+
let currentSource = getSourceById(config.defaultSource);
|
|
75
|
+
|
|
76
|
+
// Eğer mevcut kaynak bu dilde yoksa, varsayılan kaynağı kullan
|
|
77
|
+
if (!currentSource || currentSource.language !== currentLang) {
|
|
78
|
+
currentSource = availableSources[0] || sources[0];
|
|
79
|
+
config.defaultSource = currentSource.id;
|
|
80
|
+
saveConfig(config);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let animes = null;
|
|
84
|
+
if (currentSource.supportsLocalSearch && currentSource.getAnimeList) {
|
|
85
|
+
try {
|
|
86
|
+
const [animesResult] = await Promise.all([
|
|
87
|
+
currentSource.getAnimeList(),
|
|
88
|
+
speedTestPromise
|
|
89
|
+
]);
|
|
90
|
+
animes = animesResult;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
spinner.fail(chalk.red(t("errors.animeListFailed")));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
await speedTestPromise;
|
|
97
|
+
}
|
|
98
|
+
spinner.stop();
|
|
99
|
+
|
|
100
|
+
const downloadQueue = loadQueue();
|
|
101
|
+
|
|
102
|
+
// İlk kurulum - önce dil seçimi
|
|
103
|
+
if (!config.language) {
|
|
104
|
+
console.clear();
|
|
105
|
+
console.log(chalk.bold.green("Animely'e hoş geldiniz / Welcome to Animely\n"));
|
|
106
|
+
|
|
107
|
+
const { language } = await inquirer.prompt([{
|
|
108
|
+
type: "list",
|
|
109
|
+
name: "language",
|
|
110
|
+
message: "Dil seçin / Select language:",
|
|
111
|
+
choices: [
|
|
112
|
+
{ name: "Türkçe", value: "tr" },
|
|
113
|
+
{ name: "English", value: "en" }
|
|
114
|
+
]
|
|
115
|
+
}]);
|
|
116
|
+
|
|
117
|
+
config.language = language;
|
|
118
|
+
setLanguage(language);
|
|
119
|
+
|
|
120
|
+
// Dile göre varsayılan kaynağı ayarla
|
|
121
|
+
const defaultSource = getDefaultSourceForLanguage(language);
|
|
122
|
+
config.defaultSource = defaultSource.id;
|
|
123
|
+
|
|
124
|
+
saveConfig(config);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!config.defaultPlayer) {
|
|
128
|
+
console.clear();
|
|
129
|
+
console.log(chalk.bold.green(t("setup.welcome") + "\n"));
|
|
130
|
+
|
|
131
|
+
console.log(chalk.cyan(t("setup.playerConfig")));
|
|
132
|
+
const { playerConfirm } = await inquirer.prompt([{
|
|
133
|
+
type: "list",
|
|
134
|
+
name: "playerConfirm",
|
|
135
|
+
message: t("setup.playerQuestion"),
|
|
136
|
+
choices: [
|
|
137
|
+
{ name: t("setup.playerYes"), value: "yes" },
|
|
138
|
+
{ name: t("setup.playerInstall"), value: "install" },
|
|
139
|
+
{ name: t("setup.playerWeb"), value: "web" }
|
|
140
|
+
]
|
|
141
|
+
}]);
|
|
142
|
+
|
|
143
|
+
if (playerConfirm === "install") {
|
|
144
|
+
const { playerToInstall } = await inquirer.prompt([{
|
|
145
|
+
type: "list",
|
|
146
|
+
name: "playerToInstall",
|
|
147
|
+
message: t("setup.playerSelect"),
|
|
148
|
+
choices: [
|
|
149
|
+
{ name: t("setup.mpvRecommended"), value: "io.mpv.mpv" },
|
|
150
|
+
{ name: t("setup.vlcPlayer"), value: "VideoLAN.VLC" }
|
|
151
|
+
]
|
|
152
|
+
}]);
|
|
153
|
+
|
|
154
|
+
console.log(chalk.yellow(`\n${t("setup.installing", { player: playerToInstall })}`));
|
|
155
|
+
try {
|
|
156
|
+
spawnSync("winget", ["install", "-e", "--id", playerToInstall], { stdio: "inherit" });
|
|
157
|
+
console.log(chalk.green("\n" + t("setup.installSuccess")));
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.log(chalk.red(t("setup.installFailed")));
|
|
160
|
+
}
|
|
161
|
+
} else if (playerConfirm === "web") {
|
|
162
|
+
const start = (process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open');
|
|
163
|
+
if (process.platform === 'win32') {
|
|
164
|
+
spawnSync("cmd", ["/c", "start", "https://www.videolan.org/vlc/"], { stdio: 'ignore' });
|
|
165
|
+
}
|
|
166
|
+
console.log(chalk.yellow(t("setup.webOpened")));
|
|
167
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log("");
|
|
171
|
+
|
|
172
|
+
const { player } = await inquirer.prompt([{
|
|
173
|
+
type: "list",
|
|
174
|
+
name: "player",
|
|
175
|
+
message: t("setup.defaultPlayerPrompt"),
|
|
176
|
+
choices: [
|
|
177
|
+
{ name: t("setup.mpvWithResume"), value: "mpv" },
|
|
178
|
+
{ name: t("setup.vlcPlayer"), value: "vlc" }
|
|
179
|
+
]
|
|
180
|
+
}]);
|
|
181
|
+
config.defaultPlayer = player;
|
|
182
|
+
saveConfig(config);
|
|
183
|
+
console.log(chalk.green("\n" + t("setup.settingsSaved")));
|
|
184
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
while (true) {
|
|
188
|
+
console.clear();
|
|
189
|
+
setActivity(t("menu.browsingMenu"));
|
|
190
|
+
|
|
191
|
+
const currentConfig = getConfig();
|
|
192
|
+
const currentLangInLoop = currentConfig.language || "tr";
|
|
193
|
+
const availableSourcesInLoop = getSourcesByLanguage(currentLangInLoop);
|
|
194
|
+
let activeSource = getSourceById(currentConfig.defaultSource);
|
|
195
|
+
|
|
196
|
+
// Kaynak dil uyumsuzluğu kontrolü
|
|
197
|
+
if (!activeSource || activeSource.language !== currentLangInLoop) {
|
|
198
|
+
activeSource = availableSourcesInLoop[0] || sources[0];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log(chalk.bgCyan.black(` ${t("app.title")} `) + chalk.gray(` v${pkg.version} | ${activeSource.name}`));
|
|
202
|
+
|
|
203
|
+
if (downloadQueue.length > 0) {
|
|
204
|
+
console.log("");
|
|
205
|
+
infoBox(t("menu.incompleteDownloads", { count: downloadQueue.length }));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const history = loadHistory();
|
|
209
|
+
const lastWatched = Object.values(history)
|
|
210
|
+
.sort((a, b) => new Date(b.lastWatchedAt).getTime() - new Date(a.lastWatchedAt).getTime())[0];
|
|
211
|
+
|
|
212
|
+
let resumeAnime = null;
|
|
213
|
+
let nextEpisode = null;
|
|
214
|
+
|
|
215
|
+
if (lastWatched && !lastWatched.completed && animes && activeSource.id === "animely") {
|
|
216
|
+
const found = animes.find(a => a.NAME === lastWatched.name);
|
|
217
|
+
if (found) {
|
|
218
|
+
resumeAnime = found;
|
|
219
|
+
nextEpisode = lastWatched.lastEpisode + 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log("");
|
|
224
|
+
|
|
225
|
+
const choices = [
|
|
226
|
+
{ name: t("menu.searchAnime"), value: "search" },
|
|
227
|
+
{ name: t("menu.watchHistory"), value: "history" },
|
|
228
|
+
{ name: t("menu.settings"), value: "settings" }
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
if (resumeAnime) {
|
|
232
|
+
choices.unshift({
|
|
233
|
+
name: t("menu.resume", { name: chalk.cyan(resumeAnime.NAME), episode: nextEpisode }),
|
|
234
|
+
value: "resume"
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if (downloadQueue.length > 0) {
|
|
240
|
+
choices.unshift({ name: t("menu.startQueue", { count: downloadQueue.length }), value: "start_queue" });
|
|
241
|
+
choices.unshift({ name: t("menu.clearQueue"), value: "clear_queue" });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
choices.push(new inquirer.Separator());
|
|
245
|
+
choices.push({ name: t("menu.exit"), value: "exit" });
|
|
246
|
+
|
|
247
|
+
const { action } = await inquirer.prompt([{
|
|
248
|
+
type: "list",
|
|
249
|
+
name: "action",
|
|
250
|
+
message: t("menu.selectAction"),
|
|
251
|
+
loop: false,
|
|
252
|
+
choices: choices
|
|
253
|
+
}]);
|
|
254
|
+
|
|
255
|
+
if (action === "exit") {
|
|
256
|
+
console.log(chalk.gray(t("app.goodbye")));
|
|
257
|
+
process.exit(0);
|
|
258
|
+
} else if (action === "resume") {
|
|
259
|
+
await resumeWatch(resumeAnime, nextEpisode);
|
|
260
|
+
} else if (action === "settings") {
|
|
261
|
+
await showSettings();
|
|
262
|
+
} else if (action === "history") {
|
|
263
|
+
await showHistory();
|
|
264
|
+
} else if (action === "search") {
|
|
265
|
+
const searchConfig = getConfig();
|
|
266
|
+
const searchSource = getSourceById(searchConfig.defaultSource) || sources[0];
|
|
267
|
+
|
|
268
|
+
if (searchSource.supportsLocalSearch && searchSource.getAnimeList) {
|
|
269
|
+
try {
|
|
270
|
+
spinner.start(t("spinner.listUpdating"));
|
|
271
|
+
animes = await searchSource.getAnimeList();
|
|
272
|
+
spinner.stop();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
spinner.stop();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
await searchAndDownload(animes, downloadQueue, searchSource);
|
|
278
|
+
} else if (action === "start_queue") {
|
|
279
|
+
await processQueue(downloadQueue);
|
|
280
|
+
} else if (action === "clear_queue") {
|
|
281
|
+
const { confirm } = await inquirer.prompt([{
|
|
282
|
+
type: "confirm",
|
|
283
|
+
name: "confirm",
|
|
284
|
+
message: t("queue.confirmClear"),
|
|
285
|
+
default: false
|
|
286
|
+
}]);
|
|
287
|
+
|
|
288
|
+
if (confirm) {
|
|
289
|
+
downloadQueue.length = 0;
|
|
290
|
+
saveQueue(downloadQueue);
|
|
291
|
+
console.log(chalk.green(t("queue.cleared")));
|
|
292
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
console.log("");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (error.message && error.message.includes("User force closed")) {
|
|
301
|
+
console.log(chalk.gray("\n" + t("app.goodbye")));
|
|
302
|
+
process.exit(0);
|
|
303
|
+
}
|
|
304
|
+
spinner.fail(t("app.unexpectedError"));
|
|
305
|
+
console.error(chalk.gray(t("app.errorDetail", { message: error.message })));
|
|
306
|
+
}
|
|
307
|
+
})();
|