Compare commits

...

232 Commits
v2.6.0 ... v4

Author SHA1 Message Date
Darkangeel_hd 6c44d5a712 Fixed getImages urls
No more "https:https://"
Will this work tomorrow, i don't know
2 years ago
Darkangeel_hd cef7574083 Minor updates
Changed PromoVideos redis keyname, using mal_id now
Changed schedule key TTL from 2 to 6 hours
2 years ago
Darkangeel_hd 7c6307cb1e Expire getRelatedMAL_* faster
TTL set to 1h
Also, forgot to mention on last commit that we changed the key name to a more friendly one, using the mal_id now.
As the relation between mal_id and its content is 1:1 collisions are not possible
That way is easier to identify on the db
2 years ago
Darkangeel_hd 58ac761098 Reimplemented getRelatedAnimesMAL
Reimplemented getRelatedAnimesMAL using jikan v4 and aruppi DB
what a pain in the ass was this one...
2 years ago
Darkangeel_hd 3a633b2ff2 Migrating to jikan v4
moved v3 endpoints to jikan v4
Quick fixes, will review later (as in between a day and 2 years later UwU)
2 years ago
dependabot[bot] e39bb486b0 Bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
dependabot[bot] 189a5bfb79 Bump mongoose from 5.13.13 to 5.13.15
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.13.13 to 5.13.15.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.13.13...5.13.15)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
dependabot[bot] df30fd785e Bump json5 from 1.0.1 to 1.0.2
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
dependabot[bot] 656d4ce1ba Bump express from 4.17.1 to 4.17.3
Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.17.3.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.1...4.17.3)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
dependabot[bot] a90e522c21 Bump minimatch from 3.0.4 to 3.1.2
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
Darkangeel_hd faf0afee8c Updated utils/requestCall
Added new options to spoof user agent
New Aruppi APi user agent
Adapt relevant code to new funcionality

fixed line break on urls.ts
3 years ago
Darkangeel_hd 736ff79707 Update qwant api url 3 years ago
dependabot[bot] 2b4e3d0d7d
Bump minimist from 1.2.5 to 1.2.6 (#58)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3 years ago
Jesús María fdb744582f
Merge pull request #57 from aruppi/dependabot/npm_and_yarn/got-11.8.5
Bump got from 11.8.3 to 11.8.5
3 years ago
dependabot[bot] c3fa6cd854
Bump got from 11.8.3 to 11.8.5
Bumps [got](https://github.com/sindresorhus/got) from 11.8.3 to 11.8.5.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v11.8.3...v11.8.5)

---
updated-dependencies:
- dependency-name: got
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
3 years ago
Angel 868a958e36
Update README.md
Fixed Akiyama logo
Updated copyright year
3 years ago
Jesús María 99d636fa3f Fixed the episode number in recent episodes 4 years ago
Jesús María 361d0553e9 Fixed getEpisodes 4 years ago
Jesús María 08fb60749b Fixed lastEpisodes 4 years ago
Jesús María 5f3720446b Fixed MonosChinos failed queries 4 years ago
Jesús María 6912a2eddd Fixed video request 4 years ago
Jesús María 033b35aee9 Merge branch 'develop' into v4 4 years ago
Jesús María 9ba3e1035e Include updates for security dependant bots 4 years ago
Jesús María 11ed6d5731 Merge branch 'develop' into v4 4 years ago
Jesús María dafe02215a Update dependencies 4 years ago
Jesús María 90cb8bab66
Merge pull request #54 from aruppi/dependabot/npm_and_yarn/path-parse-1.0.7
Bump path-parse from 1.0.6 to 1.0.7
4 years ago
dependabot[bot] 31075ce33d
Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
4 years ago
Jesús María 5c06108a3e
Merge pull request #50 from aruppi/dependabot/npm_and_yarn/css-what-5.0.1
Bump css-what from 5.0.0 to 5.0.1
4 years ago
dependabot[bot] 78315e4017
Bump css-what from 5.0.0 to 5.0.1
Bumps [css-what](https://github.com/fb55/css-what) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/fb55/css-what/releases)
- [Commits](https://github.com/fb55/css-what/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: css-what
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
4 years ago
Jesús María 7be55d1f87
Merge pull request #49 from aruppi/dependabot/npm_and_yarn/normalize-url-4.5.1
Bump normalize-url from 4.5.0 to 4.5.1
4 years ago
dependabot[bot] 3bcdc34012
Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
4 years ago
capitanwesler 24fa86de87 Merge remote-tracking branch 'origin/develop' into v4 4 years ago
capitanwesler a9bca94952 Merge branch 'v4' into origin/develop 4 years ago
capitanwesler 22141c16f8 Encoding URI 4 years ago
capitanwesler 5719ef1b17 Removing encodeURI 4 years ago
capitanwesler f32ca2bdcf 😎 Encoding URI parameters in qwant call 4 years ago
capitanwesler 57dedd3ab5 Setting cache-control in qwant 4 years ago
capitanwesler 0a60814065 😎 Refactoring the getAnimeServers
Adding the possibility to target another source, if one of them doesn't work, if any source doesn't work at all, just return undefined to the data options.
4 years ago
capitanwesler 2afd59cf7a 😎 Changing the version control 4 years ago
capitanwesler 10cdb63ac4 😎 Refactoring the getLastEpisodes call
Refactoring the lastEpisodes in the API, removed a unused function and remove unused switches.
4 years ago
capitanwesler 327c635467 💀Fixed unused type from node:http 4 years ago
capitanwesler 116e89e6c5 👾 Updating @types/node 4 years ago
capitanwesler c46b5eeb9d 🤖 Fixing error in the parameters of lastEpisodes 4 years ago
Jesús María c0d4fbe317
Merge pull request #48 from aruppi/develop
🤖 Changin version control.
4 years ago
capitanwesler bcc22b436d 🦾 Changing version control
Changing version control.
4 years ago
Jesús María 21e8fc2637
Merge pull request #47 from aruppi/develop
🤖 Fixing error in callback
4 years ago
capitanwesler e53fefd653 🤖 Fixing error in callback
Fixing error in callback to get the respective information fo myanimelist.
4 years ago
Guillermo Rivas b529f6f13a
Merge pull request #46 from aruppi/develop 4 years ago
capitanwesler d5550bbaeb 👾 Fixing build error compile 4 years ago
capitanwesler f2e13f5201 🤖 Fixing some issues with extraInfo in the call to getEpisodes 4 years ago
capitanwesler 5708086201 🤖 Fixing some issues with packages and other plugins from eslint 4 years ago
Guillermo Rivas c1c33cb4a9
Merge pull request #45 from aruppi/develop
🦾 Error with index in episodesList
4 years ago
capitanwesler 177ddcb924 🦾 Error with index in episodesList 4 years ago
Jesús María fdef7d04e2
Merge pull request #44 from aruppi/develop
🐧 Fixing genres to get them from the directory
4 years ago
capitanwesler 6480c272ab 👾 Changing control version 4 years ago
capitanwesler 3fc5ddab5d 🤖 Implementing TioAnime as source of anime 4 years ago
capitanwesler 04b30632f8 👓 Adding the changes to the other fonts 4 years ago
capitanwesler f8ab77b289 👓 Changing the version control 4 years ago
capitanwesler 77513c4820 👓 Adding the lastEpisodes with animeflv function 4 years ago
capitanwesler adad49eaae 🦊 Sorting genres by the order 4 years ago
capitanwesler b0e5b4c7bc 🐧 Fixing genres to get them from the directory 4 years ago
Jéluchu ea1ee08c78 Merge branch 'develop' into v4 4 years ago
Jéluchu c0e1e5172b 🥺 Fix versionCode 4 years ago
Jesús María 893a9e7386
Merge pull request #42 from aruppi/develop
Adding the anime state and the nextEpisodeDate
4 years ago
capitanwesler 87cecdafec 🔧 Fixing format date 4 years ago
capitanwesler 0be12412ce 🔧 Fixing wrong date and work without redis 4 years ago
capitanwesler a4d9332510 🛠 Deleting unused console.log 4 years ago
capitanwesler 482891e39b 🧢 Changing the from how it takes the nextEpisode 4 years ago
capitanwesler 76994fdbe9 🧢 Changing date format on the nextEpisodeDate 4 years ago
Guillermo Rivas 44afd0c085
Merge pull request #43 from aruppi/feature/NormalizeId
👓 Updating package and README version control
4 years ago
capitanwesler 4f2bbbd417 👓 Updating package and README version control 4 years ago
Guillermo Rivas d34241a0ac
Merge pull request #41 from aruppi/feature/NormalizeId
🦾 Adding the nextEpisodeDate and the anime state
4 years ago
capitanwesler 533ae5da2d 🦾 Adding the nextEpisodeDate and the anime state 4 years ago
Jéluchu 8bd8be8370 Delete packge-lock.json 4 years ago
Jesús María 526a24c3d7
Merge pull request #40 from aruppi/develop
💀 lower case from anime servers
4 years ago
capitanwesler 27955db82e 👹 Changing version control 4 years ago
capitanwesler 9bdbc458fc 💀 lower case from anime servers 4 years ago
Jesús María d64e2df268
Merge pull request #39 from aruppi/develop
Feature/upgrading monoschinos
4 years ago
Guillermo Rivas e7d45c19d2
Merge pull request #38 from aruppi/feature/upgradingMonoschinos
Feature/upgrading monoschinos
4 years ago
capitanwesler cda086a583 💀 Adding monoschinos lastest episodes 4 years ago
capitanwesler d8cc3626f2 🤖 Deleting palomitron from urls from the news 4 years ago
Jesús María 133e800b75
Update README.md 4 years ago
Jéluchu a2f1a08d0d Update cover image 4 years ago
Jéluchu fcd1ddc87b Merge remote-tracking branch 'origin/v4' into v4 4 years ago
Jéluchu db943834e8 Merge branch 'develop' into v4 4 years ago
Jesús María a634921de8
Merge pull request #33 from aruppi/develop
Update README.md
4 years ago
Jesús María 841c5ad166
Update README.md 4 years ago
Jéluchu 4a7b775159 Updated README.md 4 years ago
capitanwesler 3f7d86be67 👨‍💻Fixing name in type 4 years ago
capitanwesler 021af8f19d 🦾 Re-adapting jkanime servers function 4 years ago
capitanwesler dabc7f6c23 💀 Fixing changed body response 4 years ago
capitanwesler 421efd2961 😎 Updating monoschinos episode 4 years ago
capitanwesler f855479650 👾 Updating the monoschinos and redirecting 4 years ago
capitanwesler c4d83df972 🤖Implementing servers and episodes to monoschinos 4 years ago
capitanwesler 189bf729c0 💀 Fixing error in Controller with (TV) animes 4 years ago
capitanwesler 596524f03b 🤖Changing the field jkanime to future changes 4 years ago
capitanwesler 819109d371 😎 Error with time in cache 4 years ago
capitanwesler 31573b631d 😎 Fixing error in date time for cache expire key 4 years ago
capitanwesler a13981d49d 🤖Changing times of cache 4 years ago
capitanwesler e0b22d4edb 👻Fixing wrong property on reponse 4 years ago
capitanwesler aa016be0c8 💀Fixing error in moreInfo, null assert 4 years ago
capitanwesler f490c81cf1 🤖 Setting the password for redis-server in .env 4 years ago
capitanwesler 1dead82ddc 🤖Adding cache to utilitys 4 years ago
capitanwesler faa3743569 👽Adding redis cache to the routes where is needed 4 years ago
capitanwesler 8b53015280 🤖 Fixing a issue with specials route 4 years ago
capitanwesler 375ba8bb81 🔨 Importing util to animeController 4 years ago
capitanwesler 79114fa000 🔨 Error in the name hash in the getServers route 4 years ago
capitanwesler 2effa8e95c 🕵️ Adding the cache for getServers 4 years ago
capitanwesler cf7e3a39a9 🕵️ Making some modifications to moreInfo 4 years ago
capitanwesler 75e6eaf364 🕵️‍♂️ Implementing the hashing keys and expireat 4 years ago
capitanwesler c04b349995 🔦 Adding redis to the moreInfo route in the API 4 years ago
capitanwesler aefe45a723 💡 Moving the documentation of the README to a Wiki page 4 years ago
capitanwesler 967f35ef77 🔧 Fixing package.json error in build 4 years ago
capitanwesler 3bc8ec2f38 📟 Adding the route to the waifu generator and implementing it 4 years ago
capitanwesler 7f13918f85 🔧 Updating the README.md with the doc of the v4 4 years ago
capitanwesler a48eef2383 👺 Adding the waifu model 4 years ago
capitanwesler 19f470c077 😎 Doing some modifications to package 4 years ago
capitanwesler 61830c85cb ⚙️ Changing the text for html in the news route 4 years ago
Jéluchu 37d9516b0d 🆙 Fixing lastEpisodes id 4 years ago
capitanwesler 5507023b39 🆙 Fixing moreInfo, wrong prop type being object 4 years ago
capitanwesler 065b1dff0b 🆙Missing prop of directory controller 4 years ago
capitanwesler 3c117fd98d 🆙 Wrong spelling in prop of response of schedule 4 years ago
capitanwesler fd3f8dc778 🆙 Fixing changes with the top route and movies 4 years ago
capitanwesler aefe93de52 🆙Updating README with yarn version 4 years ago
capitanwesler 7b8dca11fc 🔨 Fixing some of the responses with bad names 4 years ago
capitanwesler 7345d7fc45 👨‍💻 Fixing a callback in allDirectory route 4 years ago
capitanwesler b4cde8964e 🤖 Adding routes deprecated in the routes.ts 4 years ago
capitanwesler 6325d31cc1 📙 Finishing implementing routes of v3 4 years ago
capitanwesler e327b4be11 🧱 To work the themeParser and adding more routes 4 years ago
capitanwesler d35db8944e 💥 Implementing the models for each collection 4 years ago
capitanwesler fc2dc9ae79 🎦 Implementing the sectioned videos from youtube 4 years ago
capitanwesler 40d8fe8a3a 🎥 Fix YouTube Call 4 years ago
capitanwesler da96f84228 🎥 Implementing video functions to get the videos 4 years ago
capitanwesler 8ebd8dbceb 📷 Implementing the getImages function 4 years ago
capitanwesler 121bc98a89 🧳 Implementing the search function 4 years ago
capitanwesler afa20d36df 🦾 Migrating getAnimeServers and getEpisodes 4 years ago
capitanwesler 0fea12c350 🤖 Updating README 5 years ago
capitanwesler 16c17ac0ca 👾Migrating routes and functions 5 years ago
capitanwesler 563d0a7c4c 👾 Migrating routes and functions 5 years ago
capitanwesler c0bab5e174 🍃 Setting everything up with TypeScript & Eslint 5 years ago
capitanwesler c7b313857c Adding some missing score in an anime from the directory 5 years ago
capitanwesler 55b09858a3 Fixing some issues with the directory.json in the v2 of the api 5 years ago
capitanwesler ebdbd6987b Deleting fuzzball, unused 5 years ago
capitanwesler ba24488e72 Fixing the error with the automatic merge from develop to this branch 5 years ago
Caleídoscopio 62a059c1fd
Merge branch 'develop' into feature/improving 5 years ago
capitanwesler 01d23d5076 Adding again the .includes in the allDirectory 5 years ago
capitanwesler 56d063bf9b Fixing issues with the .json data and deleting some unused variables 5 years ago
Caleídoscopio 4a0846b02b Solving a issue with some animes with the (TV) in their names 5 years ago
Caleídoscopio 992d35799b Going back to the same directory 5 years ago
Caleídoscopio 1783b49c22 Fixing the title of black clover, to get exact information 5 years ago
Jéluchu 18724d7226 Change version of v2 (deprecated) 5 years ago
Caleídoscopio 8fcc690dce
Update version 3.4.1 5 years ago
Caleídoscopio f80aaf6d12
Update version 3.4.1 5 years ago
Caleídoscopio 8de23d0bd9
Update version 3.4.1 5 years ago
Caleídoscopio dbf8b494cd
Merge pull request #22 from aruppi/feature/improving
Adding the funcionality to the v2 with the new directory
5 years ago
capitanwesler 2f18461bf4 Adding the funcionality to the v2 with the new directory 5 years ago
Jéluchu 9a7d302854 Updated to v3.4.0 5 years ago
Jesús María 441223a0a0
Merge pull request #21 from aruppi/feature/improving
Adding assets in utils as a constants for perfomance
5 years ago
capitanwesler 4165ef546e Adding assets in utils as a constants for perfomance 5 years ago
Jéluchu 0ed7927283 Added new type of anime legal platforms 5 years ago
Jesús María 412897ca90
Merge pull request #20 from aruppi/feature/improving
Fixing and adding the functions to get the related animes to a random…
5 years ago
capitanwesler 43296328c7 Fixing and adding the functions to get the related animes to a randomAnime route 5 years ago
Jesús María cf5b050917
Merge pull request #19 from aruppi/feature/improving
Feature/improving fixing some issue with the related animes, not getting some animes
5 years ago
capitanwesler e4fded0057 Fixing getting related themes, not obtaining some animes with the property jkanime 5 years ago
Jesús María 9a511f9814
Merge pull request #18 from aruppi/feature/improving
Feature/improving adding the new directory with the parameter mal_id
5 years ago
capitanwesler 31feb54230 Adding fuzzball to do the compration of strings 5 years ago
capitanwesler e7136dba71 Adding the new propertie mal_id to the directory and the deleting the mal_title, and adapting the app to the new property 5 years ago
Jesús María b26a0dbd35
Merge pull request #15 from aruppi/feature/improving
Feature/improving and solving some issues with the animeTheme.js
5 years ago
capitanwesler 355f38b1dc Fixing the repeated directory with the function getMoreInfo, so return a object async 5 years ago
capitanwesler 7930667295 Fixing the issue with randomAnime not showing the exact object and adding it to the schedule 5 years ago
capitanwesler 720cddb4d5 Adding the randomAnime route, and fixing some issue with the themeYears function 5 years ago
Jéluchu 17510dbb5b Produccers refactor, Type Related and Movies, Ovas, Specials, etc 5 years ago
Jesús María 26c0d766cd
Merge pull request #14 from aruppi/feature/improving
Fixing some issues with jkanime servers
5 years ago
capitanwesler d671c6fc58 Missing return parameter in getPoster function 5 years ago
capitanwesler 1e44d6f286 Adding the related property to moreInfo, to show the respective related animes to that specific anime 5 years ago
capitanwesler 02293276bf Adding the function to obtain the real url from desu server 5 years ago
capitanwesler 2746613f9f Fixing issue with videoServersJK not getting the url from a episode 5 years ago
capitanwesler 1532e15bbf Missing a function to the funcionality of the app 5 years ago
capitanwesler b8374812e7 Fixing the animeTheme.js and refactoring in some files 5 years ago
capitanwesler 3c505cf54e Branch for improving and refactoring 5 years ago
capitanwesler 04cdccc8f6 Commiting Branch for improving the existing API 5 years ago
Jéluchu 6c6334759d v3.3.7 - Refactor MoreInfo and GetEpisodes 5 years ago
Jéluchu 7cd965261d v3.3.6 - Update Directory 5 years ago
Jéluchu 1bd6f42937 v3.3.5 - Update Directory 5 years ago
Jéluchu 0c14e0f4bf v3.3.4 - Update Directory and Add GetEpisodes Service 5 years ago
Jéluchu c65a5bd3e4 Fix filtering directory 5 years ago
Jéluchu 9107698f04 Add Directory Not NSWF 5 years ago
Jéluchu d65fc54821 New items to directory 5 years ago
Jéluchu 26f2341631 Fix directory 5 years ago
Jéluchu 872bc25204 New item to directory 5 years ago
Jéluchu 214a0aa15c v3.3.1 - Fix v2 MoreInfo 5 years ago
Jéluchu 22c99e0641 v3.3.0 - Refactor, fixes and update DIrectory and Themes 5 years ago
Jéluchu a67057ed9d v3.2.10 - Fix Produccer Video Channel 5 years ago
Jéluchu fc028ea441 v3.2.9 - Gocdn not direct links 5 years ago
Jéluchu 87421d8978 v3.2.8 - Added More Animes to Directory 5 years ago
Jéluchu 324f5a4a1e v3.2.7 - Added More Animes to Directory 5 years ago
Jesús María dacdcdd88f
Added New Project Akiyama to Aruppi API 5 years ago
Jesús María dfa6283a38
Merge pull request #3 from Fmaldonado6/non-case-sensitive-search
Non case sensitive search
5 years ago
Angel fabfcb969b
Merge pull request #4 from Darkangeel-hd/cache-improvements
Cache-control improvements
5 years ago
Darkangeel-hd a89decfc35 Cache-control improvements 5 years ago
Jéluchu 64601c4914 v3.2.6 - Added type, subtype and page tops 5 years ago
Jéluchu d80ab08a1a Data Genres 5 years ago
Jéluchu 4a89b12a2c v3.2.5 - Added genre and page field 5 years ago
Fmaldonado6 27528a0fb1 Non case sensitive search 5 years ago
Jéluchu 1a8c1e1258 v3.2.4 - Fix AllThemes and delete duplicates 5 years ago
Jéluchu 9ea6a60d1f v3.2.3 - Delete empty fields themes 5 years ago
Jéluchu 21cfacba70 v3.2.2 - Fix fields themes 5 years ago
Jéluchu 933ec74822 v3.2.1 - Fix ids themes 5 years ago
Jéluchu d3558c7f33 v3.2.0 - Type videos and AllThemes 5 years ago
Jéluchu 5c151093a9 Add moreInfo on tops 5 years ago
Jéluchu 078a75073b Fixes podcast and add more info on top 5 years ago
Jéluchu 9f4e8fc865 v3.1.0 - Legal Platforms, refactor and improve genres 5 years ago
Jéluchu 0d1c2ee1fe v3.0.3 - Filter 'Remasterización' 5 years ago
Jéluchu c6e6194701 v3.0.2 - Add MAL Link on future seasons 5 years ago
Jéluchu 5124765eac v3.0.1 - Fix Next Episode and Episode Number 5 years ago
Jéluchu 1e357ad206 v3.0.0 - Full Directory - Supports v2.6.8 5 years ago
Jéluchu cfa6b05917 v2.7.1 - Fix Ansatsu Kyoushitsu 5 years ago
Jéluchu 2b88dfb7a1 v2.7.0 - Optimizations, acceleration, and refactoring 5 years ago
Jéluchu 78ada322f0 v2.6.8 - Add Gocdn 5 years ago
Jéluchu d7e2609b56 v2.6.7 - Improve RandomThemes 5 years ago
Jéluchu f01535e187 Fixes images of news 5 years ago
Jéluchu 0c71bfda9a Fix readme an add serie 5 years ago
Jéluchu fd0583de4f v2.6.5 - Improve versions 5 years ago
Jéluchu 0c57e47bb4 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/lodash-4.17.19' 5 years ago
Jéluchu 36e1665ea6 v2.6.4 - Fixes some series 5 years ago
dependabot[bot] b70747ee99
Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
5 years ago
Jéluchu 61c14e9ccd Test non cache 5 years ago
Jéluchu bd7b22ddc8 v2.6.3 - Added genres to categories 5 years ago
Jéluchu fa6bd4bcb9 Fix Vercel Build 5 years ago
Jéluchu d33e9c1a8a Improve servers 5 years ago
Jéluchu 25b57ce716 v2.6.2 - Fix Epidosdes List 5 years ago
Jéluchu bc9e712614 v2.6.1 - Fix Servers List 5 years ago

@ -0,0 +1,6 @@
PORT_LISTEN=5000
DATABASE_HOST=localhost
DATABASE_PORT=27017
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=aruppiTime

@ -0,0 +1,32 @@
module.exports = {
env: {
browser: true,
commonjs: true,
node: true,
},
extends: ['prettier', 'eslint:recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: false,
},
},
plugins: ['@typescript-eslint/eslint-plugin'],
rules: {
'no-underscore-dangle': 'off',
'class-methods-use-this': 'off',
camelcase: 'off',
'no-unused-vars': 'warn',
'no-undef': 'warn',
},
settings: {
'import/resolver': {
node: {
extensions: ['.ts'],
typescript: {},
},
},
},
};

@ -0,0 +1,5 @@
module.exports = {
singleQuote: true,
trailingComma: 'all',
arrowParens: 'avoid',
};

@ -1,29 +1,32 @@
# **Aruppi API** (v2.6.0)
# **Aruppi API** (v4.2.2)
> This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture
> This API has everything about Japan, from anime, music, radio, images, videos ... to japanese culture
>
> These are the services used for the Aruppi App (only available in Spanish language)
![node version](https://img.shields.io/badge/node->=12.17.x-brightgreen.svg)
![npm version](https://img.shields.io/badge/npm->=6.14.x-brightgreen.svg)
![yarn version](https://img.shields.io/badge/yarn->=1.22.x-brightgreen.svg)
![type code](https://img.shields.io/badge/aruppi-API-brightgreen.svg)
![maintenance](https://img.shields.io/badge/maintained-Yes-brightgreen.svg)
![now](https://badgen.net/badge/icon/now?icon=now&label)
![gitrepo](https://img.shields.io/github/stars/aruppi/aruppi-api?style=social)
---
<img src="./assets/img/logo.png" width="100%" alt="">
#
## 📖 API Documentation
Soon we will add the documentation information in a link
<img src="./assets/img/cover.png" width="100%" alt="">
&nbsp;
&nbsp;
&nbsp;
## :rocket: Custom Aruppi API Link
Link to access the [Aruppi API](https://aruppi-api.jeluchu.now.sh/api/v2)
&nbsp;
&nbsp;
&nbsp;
## 📚 **Development Diary**
Describe the purpose of the project and give clues about what the code does.
For more information go to the following link [Diary Reference](./development_diary/README.md).
Aruppi API has been developed to bring together all the information about Japanese culture, from anime and manga to the most secret places in Japan, its gastronomy and even its festivities.
We are in continuous development to implement more features and improvements in the functioning of it, to later implement it in the mobile application.
## **:wrench: Developer usage**
@ -32,18 +35,25 @@ For more information go to the following link [Diary Reference](./development_di
Before cloning the repo **be sure** you have installed:
- [**NODE**](https://www.google.com/search?q=how+to+install+node) (version >= 12.17.x)
In your package manager you can use either **yarn** or **npm**,
you need to have installed these versions:
- [**NPM**](https://www.google.com/search?q=how+to+install+npm) (version >= 6.14.x)
- [**YARN**](https://www.google.com/search?q=how+to+install+yarn) (version >= 1.22.x)
Then:
- Choose a folder project in your system and switch in `cd [folder path]`
- Clone the repo in your folder path `git clone https://github.com/aruppi/aruppi-api`
- Clone the repo in your folder path `git clone https://github.com/aruppi/aruppi-api`.
Here are the steps to get started with the project on both platforms, use the corresponding commands if **npm** or **yarn**.
---
### **Installation**
In order to install the project and all dependencies, enter in the project folder and run `npm install`
In order to install the project and all dependencies, enter in the project folder and run `npm install` or you can do the same with yarn with `yarn` in the project
---
@ -53,46 +63,42 @@ In order to install the project and all dependencies, enter in the project folde
npm start
```
### Test the project
```bash
npm test
yarn start
```
---
## Deprecated v1 for API
Aruppi has grown since it was launched and we need to continue improving the application along with the services to be able to give new features.
### Build the Project
But if you need to see the code or the operation of the old version you can do it
- [Aruppi API GitHub (v1) [Deprecated]](https://github.com/aruppi/aruppi-api-v1)
- [Aruppi API Custom Link(v1) [Deprecated]](https://aruppi.herokuapp.com/api/Aruppi/)
```bash
npm build
```
## **:handshake: Contributing**
```bash
yarn build
```
- Fork it!
- Create your feature branch: `git checkout -b my-new-feature`
- Commit your changes: `git commit -am 'Add some feature'`
- Push to the branch: `git push origin my-new-feature`
- Submit a pull request
### Test the project
---
```bash
npm test
```
### **:busts_in_silhouette: Credits**
```bash
yarn test
```
- [Darkangeel](https://github.com/Darkangeel-hd) (System administration authority (SYSADM))
- [Jéluchu](https://github.com/Jeluchu) (Android Developer, designer, and others)
## 📖 API Documentation
---
**Documentation coming soon** at the following link: [**Aruppi Wiki**](https://github.com/aruppi/aruppi-api/wiki)
Where we will show more information about the calls and queries together with the response obtained or the different types of variables in some of the queries.
### **:heart: Show your support**
## Countdown to deprecation of v3 API
Please :star: this repository if you like it or this project helped you!\
Feel free to open issues or submit pull-requests to help me improving my work.
Aruppi API version 3.x.x has been deprecated, that's why all of you who are using it should migrate as soon as possible to version 4.x.x which we have already released.
Otherwise, if you want to use older versions you can host them yourself on your servers, and download the code in the corresponding branches of [**v2.x.x**](https://github.com/aruppi/aruppi-api/tree/v2) and [**v3.x.x**](https://github.com/aruppi/aruppi-api/tree/v3). In case you want to use an even lower version of the API we recommend you to have a look at this other version [**v1.x.x**](https://github.com/aruppi/aruppi-api-v1)
---
Currently the Aruppi app on Android is already using the v4.x.x API services from version v2.0.8, which you can download from our website: [**Download Aruppi App**](https://aruppi.jeluchu.com/download)
### **📚 Projects that use the API**
@ -108,17 +114,46 @@ Feel free to open issues or submit pull-requests to help me improving my work.
<sub>Anime y Manga</sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Fmaldonado6/Akiyama">
<img src="https://raw.githubusercontent.com/Fmaldonado6/Akiyama/master/images/logo/logo.png" width="75px;" alt="Jeluchu"/><br />
<sub>
<b>Akiyama</b>
</sub>
</a><br/>
<sub>Web and App</sub>
</a>
</td>
</tr>
</table>
## Contributors ✨
Here are the **main contributors to the API**, along with Aruppi's creator
<table>
<tr>
<td align="center"><a href="https://github.com/Jeluchu"><img src="https://avatars.githubusercontent.com/u/32357592?v=4" width="100px;" alt=""/><br /><sub><b>Jéluchu</b></sub></a><br /><a href="https://www.instagram.com/jeluchu/" title="Instagram">📸</a> <a href="https://about.jeluchu.com/" title="About Jelu">🌍</a> <a href="https://twitter.com/Jeluchu" title="Twitter">📢</a><a href="https://www.linkedin.com/in/jesusmariacalderon/" title="LinkedIn">🔍</a></td>
<td align="center"><a href="https://github.com/capitanwesler"><img src="https://avatars.githubusercontent.com/u/61250854?v=4" width="100px;" alt=""/><br /><sub><b>Guillermo</b></sub></a><br/><a href="https://www.facebook.com/profile.php?id=100009163736196" title="Facebook">👀</a> <a href="mailto:guillermo.campanudo@hotmail.com" title="E-mail">📧</a> <a href="https://www.linkedin.com/in/guillermo-campanudo/" title="LinkedIn">🔍</a></td>
<td align="center"><a href="https://github.com/Darkangeel-hd"><img src="https://i.pinimg.com/564x/24/73/c0/2473c02e2ac93f617a28b2b5058bb41d.jpg" width="100px;" alt=""/><br /><sub><b>Darkangeel-hd</b></sub></a><br /><a href="https://i.pinimg.com/originals/19/41/22/1941222eaee4de7d08dc21cc3993e791.jpg" title="Let's All Love lain!">👀</a></td>
</tr>
</table>
There are also **people who have made contributions** and therefore it is also important to highlight them.
### **:robot: Author**
<table>
<tr>
<td align="center"><a href="https://github.com/Fmaldonado6"><img src="https://avatars.githubusercontent.com/u/28517542?v=4" width="100px;" alt=""/><br /><sub><b>Fmaldonado6</b></sub></a><br/></td>
<td align="center"><a href="https://github.com/HernanSsj"><img src="https://avatars.githubusercontent.com/u/41026227?v=4" width="100px;" alt=""/><br /><sub><b>HernanSsj</b></sub></a><br/></td>
</tr>
</table>
_*Jéluchu*_
#
> You can follow me on
[github](https://github.com/Jeluchu)&nbsp;&middot;&nbsp;[twitter](https://twitter.com/Jeluchu)
### **:busts_in_silhouette: Credits**
---
- [Darkangeel](https://github.com/Darkangeel-hd) (System administration authority (SYSADM))
- [Jéluchu](https://github.com/Jeluchu) (Multiplatform Developer, designer, and others)
- [Capitanwesler](https://github.com/capitanwesler) (Backend developer, web developer and others)
Copyright © 2020 [Jéluchu](https://about.jeluchu.com/).
Copyright © 2022 [Jéluchu](https://about.jeluchu.com/).

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

1759
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,10 +1,12 @@
{
"name": "aruppi",
"version": "2.6.0",
"version": "4.2.2",
"description": "Aruppi is a custom API to obtain data from the Japanese culture for the mobile app",
"main": "./src/api/api.js",
"main": "./src/api/api.ts",
"scripts": {
"start": "node src/index.js"
"start": "node ./dist/server.js",
"dev": "tsnd --transpile-only --ignore-watch node_modules --respawn src/server.ts",
"build": "tsc -p ."
},
"keywords": [
"aruppi",
@ -27,6 +29,11 @@
"name": "Darkangeel",
"url": "https://github.com/Darkangeel-hd",
"reason": "System administration authority (SYSADM)"
},
{
"name": "Capitanwesler",
"url": "https://github.com/capitanwesler",
"reason": "Backend and Frontend developer"
}
],
"engines": {
@ -43,15 +50,35 @@
"license": "MIT",
"dependencies": {
"body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.3",
"cheerio": "^1.0.0-rc.6",
"compose-middleware": "^5.0.1",
"cors": "^2.8.5",
"express": "^4.16.4",
"got": "^11.3.0",
"helmet": "^3.23.3",
"hooman": "^1.2.5",
"rss-to-json": "^1.1.1",
"dotenv": "^8.2.0",
"express": "^4.17.3",
"got": "^11.8.5",
"helmet": "^4.5.0",
"mongodb": "^3.6.6",
"mongoose": "^5.13.15",
"redis": "^3.1.2",
"rss-parser": "^3.12.0",
"tough-cookie": "^4.0.0",
"node-fetch": "^2.6.0"
"ts-node-dev": "^1.1.1"
},
"devDependencies": {
"@types/cheerio": "^0.22.28",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11",
"@types/node": "^15.0.2",
"@types/redis": "^2.8.28",
"@types/tough-cookie": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.4.0",
"prettier": "^2.2.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.4"
}
}

@ -1,575 +0,0 @@
const rss = require('rss-to-json');
const {
homgot
} = require('../api/apiCall');
const {
jkanimeInfo,
animeflvInfo,
imageUrlToBase64,
getAnimeCharacters,
getAnimeVideoPromo,
animeExtraInfo,
searchAnime,
transformUrlServer,
obtainPreviewNews,
structureThemes,
getAnimes,
getDirectory,
helper,
videoServersJK
} = require('../utils/index');
const ThemeParser = require('../utils/animetheme');
const parserThemes = new ThemeParser()
const {
BASE_ANIMEFLV_JELU, BASE_JIKAN, BASE_IVOOX, BASE_QWANT, BASE_YOUTUBE, GENRES_URL
} = require('./urls');
const schedule = async (day) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}schedule/${day.current}`, options);
const body = data[day.current];
const promises = []
body.map(doc =>{
promises.push({
title: doc.title,
malid: doc.mal_id,
image: doc.image_url
});
});
return promises;
};
const top = async (type, subtype, page) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}top/${type}/${page}/${subtype}`, options);
return data.top;
};
const getAllAnimes = async () =>{
let data = await getAnimes()
return data.map(item => ({
index: item[0],
animeId: item[3],
title: item[1],
id: item[2],
type: item[4]
}));
};
const getAllDirectory = async () =>{ return await getDirectory(); };
const getAnitakume = async () =>{
const promises = []
await rss.load(BASE_IVOOX).then(rss => {
const body = JSON.parse(JSON.stringify(rss, null, 3)).items
body.map(doc =>{
let time = new Date(doc.created)
const monthNames = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
let day = time.getDate()
let month = monthNames[time.getMonth()]
let year = time.getFullYear()
let date
if(month < 10){
date = `${day} de 0${month} de ${year}`
}else{
date = `${day} de ${month} de ${year}`
}
promises.push({
title: doc.title,
duration: doc.itunes_duration,
created: date,
mp3: doc.enclosures.map(x => x.url)
});
});
});
return promises;
};
const getNews = async (pageRss) =>{
const promises = []
for(let i = 0; i <= pageRss.length -1; i++) {
await rss.load(pageRss[i].url).then(rss => {
const body = JSON.parse(JSON.stringify(rss, null, 3)).items
body.map(doc => {
promises.push({
title: doc.title,
url: doc.link,
author: pageRss[i].author,
thumbnail: obtainPreviewNews(doc[pageRss[i].content]),
content: doc[pageRss[i].content]
});
});
});
}
return promises;
};
const season = async (year, type) =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/${year}/${type}`, options);
let body = data.anime;
const promises = []
body.map(doc =>{
promises.push({
title: doc.title,
malid: doc.mal_id,
image: doc.image_url,
genres: doc.genres.map(x => x.name)
});
});
return promises;
};
const allSeasons = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/archive`, options);
let body = data.archive;
const promises = []
body.map(doc =>{
promises.push({
year: doc.year,
seasons: doc.seasons,
});
});
return promises;
};
const laterSeasons = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_JIKAN}season/later`, options);
let body = data.anime;
const promises = []
body.map(doc =>{
promises.push({
malid: doc.mal_id,
title: doc.title,
image: doc.image_url
});
});
return promises;
};
const getLastEpisodes = async () =>{
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}LatestEpisodesAdded`, options);
let body = data.episodes;
const promises = []
body.map(doc => {
promises.push(helper().then(async () => ({
id: doc.id,
title: doc.title,
image: doc.poster,
episode: doc.episode,
servers: await transformUrlServer(JSON.parse(JSON.stringify(doc.servers)))
})));
});
return Promise.all(promises);
};
const getSpecials = async (type, subType, page) =>{
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}${type.url}/${subType}/${page}`, options);
let body = data[type.prop];
const promises = []
body.map(doc =>{
promises.push({
id: doc.id,
title: doc.title,
type: doc.type,
banner: doc.banner,
image: doc.poster,
synopsis: doc.synopsis,
status: doc.debut,
rate: doc.rating,
genres: doc.genres.map(x => x),
episodes: doc.episodes.map(x => x)
});
});
return promises;
};
const getMoreInfo = async (title) =>{
const promises = []
let animeTitle = ''
let animeId = ''
let animeType = ''
let animeIndex = ''
let seriesTitle
let position
const jkAnimeTitles = [
{ title: 'The God of High School', id: 'the-god-of-high-school' },
{ title: 'Kami no Tou', id: 'kami-no-tou' },
{ title: 'BNA', id: 'bna' }
];
let jkanime = false
let jkanimeID
let jkanimeName
for (let name in jkAnimeTitles) {
if (title === jkAnimeTitles[name].title) {
jkanime = true
jkanimeID = jkAnimeTitles[name].id
jkanimeName = jkAnimeTitles[name].title
}
}
if (jkanime === false) {
const titles = [
{ animeflv: 'Kaguya-sama wa Kokurasetai: Tensai-tachi no Renai Zunousen 2nd Season', myanimelist: 'Kaguya-sama wa Kokurasetai?: Tensai-tachi no Renai Zunousen', alternative: 'Kaguya-sama wa Kokurasetai'},
{ animeflv: 'Naruto Shippuden', myanimelist: 'Naruto: Shippuuden' },
{ animeflv: 'Rock Lee no Seishun Full-Power Ninden', myanimelist: 'Naruto SD: Rock Lee no Seishun Full-Power Ninden' },
{ animeflv: 'BAKI: dai reitaisai-hen', myanimelist: 'Baki 2nd Season' }
];
for (let name in titles) {
if (title === titles[name].animeflv || title === titles[name].myanimelist || title === titles[name].alternative) {
seriesTitle = titles[name].animeflv
position = name
}
}
if (seriesTitle === undefined) {
seriesTitle = title
}
await getAllAnimes().then(animes => {
for (const i in animes) {
if (animes[i].title.split('\t')[0] === seriesTitle.split('\t')[0] || animes[i].title === `${seriesTitle} (TV)`) {
if (animes[i].title.includes('(TV)', 0)) { animeTitle = animes[i].title.split('\t')[0].replace(' (TV)', '') }
else { animeTitle = animes[i].title.split('\t')[0] }
animeId = animes[i].id
animeIndex = animes[i].index
animeType = animes[i].type.toLowerCase()
if (position !== undefined) {
seriesTitle = titles[position].myanimelist
}
break;
}
}
});
try{
if (animeType === 'tv') {
promises.push(await animeflvInfo(animeId, animeIndex).then(async extra => ({
title: animeTitle || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
moreInfo: await animeExtraInfo(seriesTitle).then(info =>{
return info || null
}),
promo: await getAnimeVideoPromo(seriesTitle).then(promo =>{
return promo || null
}),
characters: await getAnimeCharacters(seriesTitle).then(characters =>{
return characters || null
})
})));
} else {
promises.push(await animeflvInfo(animeId).then(async extra => ({
title: animeTitle || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
})));
}
}catch(err){
console.log(err)
}
} else {
promises.push(await jkanimeInfo(jkanimeID).then(async extra => ({
title: jkanimeName || null,
poster: await imageUrlToBase64(extra.animeExtraInfo[0].poster) || null,
synopsis: extra.animeExtraInfo[0].synopsis || null,
status: extra.animeExtraInfo[0].debut || null,
type: extra.animeExtraInfo[0].type || null,
rating: extra.animeExtraInfo[0].rating || null,
genres: extra.genres || null,
episodes: extra.listByEps || null,
moreInfo: await animeExtraInfo(jkanimeName).then(info =>{
return info || null
}),
promo: await getAnimeVideoPromo(jkanimeName).then(promo =>{
return promo || null
}),
characters: await getAnimeCharacters(jkanimeName).then(characters =>{
return characters || null
})
})));
}
return promises;
};
const getAnimeServers = async (id) => {
const jkAnimeIDs = [
{ id: 'the-god-of-high-school' },
{ id: 'kami-no-tou' },
{ id: 'bna' }
];
let jkanime = false
let jkanimeID
for (let name in jkAnimeIDs) {
if (id.includes(jkAnimeIDs[name].id)) {
jkanime = true
jkanimeID = id
}
}
if (jkanime === false) {
let options = { parse: true }
const data = await homgot(`${BASE_ANIMEFLV_JELU}GetAnimeServers/${id}`, options);
let body = data.servers;
return await transformUrlServer(body);
} else {
return await videoServersJK(jkanimeID)
}
};
const search = async (title) =>{ return await searchAnime(title); };
const getImages = async (query) => {
let options = { parse: true }
const data = await homgot(`${BASE_QWANT}count=${query.count}&q=${query.title}&t=${query.type}&safesearch=${query.safesearch}&locale=${query.country}&uiv=4`, options);
const body = data.data.result.items;
const promises = []
body.map(doc =>{
promises.push({
type: doc.thumb_type,
thumbnail: `https:${doc.thumbnail}`,
fullsize: `https:${doc.media_fullsize}`
});
});
return promises;
};
const getYoutubeVideos = async (channelId) => {
let options = { parse: true }
const data = await homgot(`${BASE_YOUTUBE}${channelId.id}&part=${channelId.part}&order=${channelId.order}&maxResults=${channelId.maxResults}`, options);
const body = data[channelId.prop];
const promises = []
body.map(doc =>{
promises.push({
title: doc.snippet.title,
videoId: doc.id.videoId,
thumbDefault: doc.snippet.thumbnails.default.url,
thumbMedium: doc.snippet.thumbnails.medium.url,
thumbHigh: doc.snippet.thumbnails.high.url
});
});
return promises;
};
const getRadioStations = async () => {
return require('../assets/radiostations.json');
}
const getOpAndEd = async (title) => {
let data = await parserThemes.serie(title)
return await structureThemes(data, true)
};
const getThemesYear = async (year) => {
let data = []
if (year === undefined) {
return await parserThemes.allYears();
} else {
data = await parserThemes.year(year)
return await structureThemes(data, false)
}
};
const getRandomTheme = async () => {
let promise = []
let data = await parserThemes.random()
let random = Math.round(Math.random()*(data.themes.length - 1));
promise.push({
name: data.title,
title: data.themes[random].name.split('"')[1] || null,
link: data.themes[random].link
})
return promise;
};
const getArtist = async (id) => {
let data
if (id === undefined) {
return await parserThemes.artists();
} else {
data = await parserThemes.artist(id)
return await structureThemes(data, false)
}
};
const getAnimeGenres = async(genre, order, page) => {
let $
let promises = []
let options = { scrapy: true }
if (page !== undefined) {
$ = await homgot(`${GENRES_URL}genre%5B%5D=${genre}&order=${order}&page=${page}`,options)
} else {
$ = await homgot(`${GENRES_URL}genre%5B%5D=${genre}&order=${order}`,options)
}
$('div.Container ul.ListAnimes li article').each((index , element) =>{
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
const poster = $element.find('a div.Image figure img').attr('src');
const banner = poster.replace('covers' , 'banners').trim();
const type = $element.find('div.Description p span.Type').text();
const synopsis = $element.find('div.Description p').eq(1).text().trim();
const rating = $element.find('div.Description p span.Vts').text();
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
poster: await imageUrlToBase64(poster) || null,
banner: banner || null,
synopsis: synopsis || null,
type: type || null,
rating: rating || null
})))
})
return Promise.all(promises);
};
const getAllThemes = async () => {
let data = await parserThemes.all()
return await structureThemes(data, false)
};
module.exports = {
schedule,
top,
getAllAnimes,
getAllDirectory,
getAnitakume,
getNews,
season,
allSeasons,
laterSeasons,
getLastEpisodes,
getSpecials,
getMoreInfo,
getAnimeServers,
search,
getImages,
getYoutubeVideos,
getRadioStations,
getOpAndEd,
getThemesYear,
getRandomTheme,
getArtist,
getAnimeGenres,
getAllThemes
};

@ -1,30 +0,0 @@
const hooman = require('hooman');
const { CookieJar } = require('tough-cookie');
const cookieJar = new CookieJar();
const cheerio = require('cheerio');
let response
let data
const homgot = async (url, options) => {
response = await hooman.get(url, cookieJar);
if (options !== undefined) {
if (options.scrapy) {
data = await cheerio.load(response.body)
}
if (options.parse) {
data = JSON.parse(response.body)
}
} else {
data = response
}
return data
}
module.exports = {
homgot
}

@ -1,47 +0,0 @@
const express = require('express');
const routes = require('./routes/index');
const router = express.Router();
router.get('/', (req, res) => {
res.json({
message: 'Aruppi API - 🎏',
author: 'Jéluchu',
version: '2.6.0',
credits: 'The bitch loves APIs that offers data to Aruppi App',
entries: [
{
'Schedule': '/api/v2/schedule/:day',
'Top': '/api/v2/top/:type/:subtype/:page',
'AllAnimes': '/api/v2/allAnimes',
'Anitakume': '/api/v2/anitakume',
'News': '/api/v2/news',
'Season': '/api/v2/season/:year/:type',
'All Seasons': '/api/v2/allSeasons',
'All Directory': '/api/v2/allDirectory',
'Genres': '/api/v2/getByGenres/:genre/:order/:page?',
'Futures Seasons': '/api/v2/laterSeasons',
'LastEpisodes': '/api/v2/lastEpisodes',
'Movies': '/api/v2/movies/:type/:page',
'Ovas': '/api/v2/ovas/:type/:page',
'Specials': '/api/v2/specials/:type/:page',
'Tv': '/api/v2/tv/:type/:page',
'MoreInfo': '/api/v2/moreInfo/:title',
'GetAnimeServers': '/api/v2/getAnimeServers/:id',
'Search': '/api/v2/search/:title',
'Images': '/api/v2/images/:query',
'Videos': '/api/v2/videos/:channelId',
'Radios': '/api/v2/radio',
'All Themes': '/api/v2/allThemes',
'Themes': '/api/v2/themes/:title',
'Year Themes': '/api/v2/themesYear/:year?',
'Random Theme': '/api/v2/randomTheme',
'Artists Theme': '/api/v2/artists/:id?'
}
]
});
});
router.use('/', routes);
module.exports = router;

@ -1,500 +0,0 @@
const express = require('express');
const router = express.Router();
const api = require('../api');
const { BASE_KUDASAI, BASE_PALOMITRON, BASE_RAMENPARADOS, BASE_CRUNCHYROLL } = require('../urls');
router.get('/schedule/:day' , (req, res) =>{
let day = {current: req.params.day}
api.schedule(day)
.then(day =>{
if (day.length > 0) {
res.status(200).json({
day
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/top/:type/:subtype/:page' , (req, res) =>{
let type = req.params.type;
let subtype = req.params.subtype;
let page = req.params.page;
api.top(type, subtype, page)
.then(top =>{
if (top.length > 0) {
res.status(200).json({
top
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allAnimes' , (req, res) =>{
api.getAllAnimes()
.then(animes =>{
if (animes.length > 0) {
res.status(200).json({
animes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allDirectory' , (req, res) =>{
api.getAllDirectory()
.then(directory =>{
if (directory.length > 0) {
res.status(200).json({
directory
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/anitakume' , (req, res) =>{
api.getAnitakume()
.then(podcast =>{
if (podcast.length > 0) {
res.status(200).json({
podcast
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/news' , (req, res) =>{
let pagesRss = [
{ url: BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
{ url: BASE_PALOMITRON, author: 'Palomitron', content: 'description' },
{ url: BASE_RAMENPARADOS, author: 'Ramen para dos', content: 'content' },
{ url: BASE_CRUNCHYROLL, author: 'Crunchyroll', content: 'content_encoded' }
];
api.getNews(pagesRss)
.then(news =>{
if (news.length > 0) {
res.status(200).json({
news
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/season/:year/:type' , (req, res) =>{
let year = req.params.year;
let type = req.params.type;
api.season(year, type)
.then(season =>{
if (season.length > 0) {
res.status(200).json({
season
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allSeasons' , (req, res) =>{
api.allSeasons()
.then(archive =>{
if (archive.length > 0) {
res.status(200).json({
archive
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/laterSeasons' , (req, res) =>{
api.laterSeasons()
.then(future =>{
if (future.length > 0) {
res.status(200).json({
future
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/lastEpisodes' , (req, res) =>{
api.getLastEpisodes()
.then(episodes =>{
if (episodes.length > 0) {
res.status(200).json({
episodes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/movies/:type/:page' , (req, res) =>{
let type = {url: 'Movies', prop: 'movies'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(movies =>{
if (movies.length > 0) {
res.status(200).json({
movies
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/ovas/:type/:page' , (req, res) =>{
let type = {url: 'Ova', prop: 'ova'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(ovas =>{
if (ovas.length > 0) {
res.status(200).json({
ovas
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/specials/:type/:page' , (req, res) =>{
let type = {url: 'Special', prop: 'special'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(specials =>{
if (specials.length > 0) {
res.status(200).json({
specials
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/tv/:type/:page' , (req, res) =>{
let type = {url: 'Tv', prop: 'tv'}
let subType = req.params.type;
let page = req.params.page;
api.getSpecials(type, subType, page)
.then(tv =>{
if (tv.length > 0) {
res.status(200).json({
tv
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/moreInfo/:title' , (req, res) =>{
let title = req.params.title;
api.getMoreInfo(title)
.then(info =>{
if (info.length > 0) {
res.status(200).json({
info
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/getAnimeServers/:id([^/]+/[^/]+)' , (req, res) =>{
let id = req.params.id;
api.getAnimeServers(id)
.then(servers =>{
if (servers.length > 0) {
res.status(200).json({
servers
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/search/:title' , (req, res) =>{
let title = req.params.title;
api.search(title)
.then(search =>{
if (search.length > 0) {
res.status(200).json({
search
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/images/:query' , (req, res) =>{
let query = { title: req.params.query, count: '51', type: 'images', safesearch: '1', country: 'es_ES', uiv: '4' };
api.getImages(query)
.then(images =>{
if (images.length > 0) {
res.status(200).json({
images
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/videos/:channelId' , (req, res) =>{
let channelId = { id: req.params.channelId, part: 'snippet,id', order: 'date', maxResults: '50', prop: 'items' };
api.getYoutubeVideos(channelId)
.then(videos =>{
if (videos.length > 0) {
res.status(200).json({
videos
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/radio' , (req, res) =>{
api.getRadioStations()
.then(stations =>{
if (stations.length > 0) {
res.status(200).json({
stations
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/allThemes', (req, res) =>{
api.getAllThemes()
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/themes/:title' , (req, res) =>{
let title = req.params.title;
api.getOpAndEd(title)
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/themesYear/:year?', (req, res) =>{
let year = req.params.year;
let season = req.params.season
api.getThemesYear(year, season)
.then(themes =>{
if (themes.length > 0) {
res.status(200).json({
themes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/randomTheme', (req, res) =>{
api.getRandomTheme()
.then(random =>{
if (random.length > 0) {
res.status(200).json({
random
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/artists/:id?', (req, res) =>{
let id = req.params.id;
api.getArtist(id)
.then(artists =>{
if (artists.length > 0) {
res.status(200).json({
artists
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
router.get('/getByGenres/:genre/:order/:page?' , (req , res) =>{
let genre = req.params.genre;
let order = req.params.order;
let page = req.params.page;
api.getAnimeGenres(genre, order , page)
.then(animes =>{
if (animes.length > 0) {
res.status(200).json({
animes
});
} else (
res.status(500).json({ message: 'Aruppi lost in the shell'})
)
}).catch((err) =>{
console.error(err);
});
});
module.exports = router;

@ -1,20 +0,0 @@
module.exports = {
BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/',
BASE_ANIMEFLV: 'https://animeflv.net/',
BASE_JKANIME: 'https://jkanime.net/',
BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/',
BASE_YOUTUBE: 'https://aruppi.jeluchu.xyz/apis/youtube/v3/search?channelId=',
BASE_JIKAN: 'https://aruppi.jeluchu.xyz/apis/jikan/v3/',
BASE_IVOOX: 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml',
BASE_KUDASAI: 'https://somoskudasai.com/feed/',
BASE_PALOMITRON: 'https://elpalomitron.com/category/animemanga/feed/',
BASE_RAMENPARADOS: 'https://ramenparados.com/category/noticias/anime/feed/',
BASE_CRUNCHYROLL: 'https://www.crunchyroll.com/newsrss?lang=esES',
SEARCH_URL: 'https://animeflv.net/browse?q=',
JKANIME_URL: 'https://jkanime.net/buscar/',
GENRES_URL: 'https://animeflv.net/browse?',
SEARCH_DIRECTORY: 'https://animeflv.net/browse?order=title&page=',
BASE_EPISODE_IMG_URL: 'https://cdn.animeflv.net/screenshots/',
BASE_QWANT: 'https://api.qwant.com/search/images?',
REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/'
};

@ -1,37 +0,0 @@
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const bodyParser = require('body-parser');
const middlewares = require('./middlewares/index').middleware;
const api = require('./api');
const app = express();
app.use(helmet());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', (req, res) => {
res.redirect('/api/')
});
app.get('/api/', (req, res) => {
res.json({
message: 'Tu~tu~ruuu! You have traveled to the API Black Hole'
});
});
app.get('/api/v1', (req, res) => {
res.json({
message: 'Sorry, version v1 is deprecated, if you want to see content go to v2'
});
});
app.use('/api/v2', api);
app.use(middlewares);
module.exports = app;

@ -1,42 +0,0 @@
[
{ "name": " Acción", "value": "accion" },
{ "name": " Artes Marciales", "value": "artes-marciales" },
{ "name": " Aventuras", "value": "aventura" },
{ "name": " Carreras", "value": "carreras" },
{ "name": " Ciencia Ficción", "value": "ciencia-ficcion" },
{ "name": " Comedia", "value": "comedia" },
{ "name": " Demencia", "value": "demencia" },
{ "name": " Demonios", "value": "demonios" },
{ "name": " Deportes", "value": "deportes" },
{ "name": " Drama", "value": "drama" },
{ "name": " Ecchi", "value": "ecchi" },
{ "name": " Escolares", "value": "escolares" },
{ "name": " Espacial", "value": "espacial" },
{ "name": " Fantasía", "value": "fantasia" },
{ "name": " Harem", "value": "harem" },
{ "name": " Historico", "value": "historico" },
{ "name": " Infantil", "value": "infantil" },
{ "name": " Josei", "value": "josei" },
{ "name": " Juegos", "value": "juegos" },
{ "name": " Magia", "value": "magia" },
{ "name": " Mecha", "value": "mecha" },
{ "name": " Militar", "value": "militar" },
{ "name": " Misterio", "value": "misterio" },
{ "name": " Música", "value": "musica" },
{ "name": " Parodia", "value": "parodia" },
{ "name": " Policía", "value": "policia" },
{ "name": " Psicológico", "value": "psicologico" },
{ "name": " Recuentos de la vida", "value": "recuentos-de-la-vida" },
{ "name": " Romance", "value": "romance" },
{ "name": " Samurai", "value": "samurai" },
{ "name": " Seinen", "value": "seinen" },
{ "name": " Shoujo", "value": "shoujo" },
{ "name": " Shounen", "value": "shounen" },
{ "name": " Sobrenatural", "value": "sobrenatural" },
{ "name": " Superpoderes", "value": "superpoderes" },
{ "name": " Suspenso", "value": "suspenso" },
{ "name": " Terror", "value": "terror" },
{ "name": " Vampiros", "value": "vampiros" },
{ "name": " Yaoi", "value": "yaoi" },
{ "name": " Yuri", "value": "yuri" }
]

@ -1,306 +0,0 @@
[
{
"name": "Ghost Anime Radio",
"url": "http://animeradio.su:8000/"
},
{
"name": "Vocaloid Radio",
"url": "http://curiosity.shoutca.st:8019/stream"
},
{
"name": "NyanServer (J-Trance)",
"url": "http://radio.nyan.pw/station/stream"
},
{
"name": "Listen Radio (J-Pop)",
"url": "https://listen.moe/stream"
},
{
"name": "Listen Radio (K-Pop)",
"url": "https://listen.moe/kpop/stream"
},
{
"name": "Anison FM",
"url": "http://pool.anison.fm:9000/AniSonFM(128)"
},
{
"name": "Radio Nami",
"url": "https://radionami.com/play_radio.m3u"
},
{
"name": "R/a/dio",
"url": "http://relay0.r-a-d.io/main.mp3"
},
{
"name": "Chiru.no",
"url": "http://chiru.no:8000/stream.mp3"
},
{
"name": "Vocaloid Radio VRX",
"url": "http://vrx.piro.moe:8000/stream-192"
},
{
"name": "Asian Wave Japan",
"url": "https://listen1.myradio24.com/7934"
},
{
"name": "Radio Vocaloid",
"url": "http://142.4.217.133:9848/stream"
},
{
"name": "Final Fantasy Radio",
"url": "http://finalfantasystation.com:8000/stream"
},
{
"name": "Shinsen Radio",
"url": "http://shinsen-radio.org:8000/shinsen-radio.128.mp3"
},
{
"name": "Anime Nexus",
"url": "http://radio.animenexus.mx:8000/animenexus"
},
{
"name": "Yggdrasil Radio",
"url": "http://shirayuki.org:9100/"
},
{
"name": "Eden Radio",
"url": "http://edenofthewest.com:8080/eden.mp3"
},
{
"name": "Gensokyo Radio",
"url": "http://stream.gensokyoradio.net:8000/"
},
{
"name": "Radio J-Hero",
"url": "http://stm1.radiojhero.com:8008/;"
},
{
"name": "Phate Radio",
"url": "http://stream.phate.io/phatecc"
},
{
"name": "91.8 The fan",
"url": "http://198.27.80.154:8800/live"
},
{
"name": "Radio AOI",
"url": "http://radioaoi.pl/stream.m3u"
},
{
"name": "Radio Touhou",
"url": "http://www.touhouradio.com/touhouradio.m3u"
},
{
"name": "Radio MultiAnime",
"url": "http://67.20.61.70:8301"
},
{
"name": "Radio Fan World Anime",
"url": "http://stream.miradio.in:2199/tunein/fanworld.pls"
},
{
"name": "Radio Japan-A",
"url": "http://www.japanaradio.com/free/48kaacp.pls"
},
{
"name": "Radio JPopsuki",
"url": "http://jpopsuki.fm:2199/tunein/jpopsuki-stream.pls"
},
{
"name": "Radio Hot Mix",
"url": "http://hotmixradio-japan.ice.infomaniak.ch/hotmixradio-japan-128.mp3"
},
{
"name": "Dada more Radio",
"url": "http://dadamore2.ddo.jp:8000/listen.pls"
},
{
"name": "Initial D World",
"url": "http://69.163.186.124:9001/listen.aac"
},
{
"name": "Radio Blast",
"url": "http://192.99.150.31:8315/"
},
{
"name": "Kibo FM",
"url": "http://listen.kibo.fm:8000/kibofm"
},
{
"name": "Power 945",
"url": "http://38.96.148.28:8754/stream"
},
{
"name": "Japan Fans",
"url": "http://159.253.37.137:9984/listen.pls"
},
{
"name": "Radio Aniterasu",
"url": "http://aniterasu.com:8000/;?1442956789440.mp3"
},
{
"name": "Big B Radio's J-Pop",
"url": "http://64.71.79.181:6059/stream"
},
{
"name": "Radio Blue Heron",
"url": "http://cp3.digistream.info:8170"
},
{
"name": "Friends Forever",
"url": "http://23.29.71.154:8066/"
},
{
"name": "Radio Greek Otaku",
"url": "http://192.99.4.210:3684/stream"
},
{
"name": "Radio UR",
"url": "http://listen.ur-radio.de/anime.mp3"
},
{
"name": "Radio Anime",
"url": "http://stream.animeradio.de/animeradio.mp3"
},
{
"name": "PowerPlay J-Pop",
"url": "http://agnes.torontocast.com:8102"
},
{
"name": "Radio Asia Dream",
"url": "http://bluford.torontocast.com:8526"
},
{
"name": "J-Pop Kawaii",
"url": "http://bb31.sonixcast.com:20002/stream/1/"
},
{
"name": "J-Club HipHop",
"url": "http://agnes.torontocast.com:8051"
},
{
"name": "J-Rock",
"url": "http://cristina.torontocast.com:8057"
},
{
"name": "J-Pop Sakura",
"url": "http://bb31.sonixcast.com:20278/stream/1/"
},
{
"name": "J-Pop Haru Sakura",
"url": "http://184.75.223.178:8087/"
},
{
"name": "Radio Ronin",
"url": "https://s3.radio.co/sff133d65b/listen"
},
{
"name": "Radio Shinka",
"url": "http://5.9.65.9:8171/live"
},
{
"name": "Radio Naihatsu",
"url": "http://108.163.223.242:8305/"
},
{
"name": "J-Pop Project",
"url": "http://agnes.torontocast.com:8083/"
},
{
"name": "J-idols Project",
"url": "http://agnes.torontocast.com:8011/"
},
{
"name": "Radio J1",
"url": "https://jenny.torontocast.com:2000/stream/J1HITS"
},
{
"name": "J1 XTRA",
"url": "https://jenny.torontocast.com:2000/stream/J1XTRA"
},
{
"name": "J1 GOLD",
"url": "https://jenny.torontocast.com:2000/stream/J1GOLD"
},
{
"name": "Animu FM",
"url": "http://cast.animu.com.br:9021/stream"
},
{
"name": "Radio Wave Anime",
"url": "http://s04.radio-tochka.com:5470/mount"
},
{
"name": "Radio Anime Stream",
"url": "https://radioanime.radioca.st/stream"
},
{
"name": "Radio Baka",
"url": "http://144.217.203.184:8398/;"
},
{
"name": "Radio Animecol",
"url": "http://node-15.zeno.fm/6bfysacxc6quv"
},
{
"name": "JMusic Anime",
"url": "http://ample-zeno-24.radiojar.com/ddetxwuhkpeuv"
},
{
"name": "Radio Japanese Music",
"url": "http://live.japanesemusicid.com:8000/japanesemusic"
},
{
"name": "Radio Japannext",
"url": "https://perseus.shoutca.st/tunein/japannex.pls"
},
{
"name": "Radio Akari",
"url": "http://ample-zeno-22.radiojar.com/0t952vqukfeuv"
},
{
"name": "Anime Universe",
"url": "http://176.31.241.17:8147/;"
},
{
"name": "Geek Radio Music",
"url": "http://stream.zenolive.com/8d0xskxsxxquv"
},
{
"name": "Radio Aniterasu",
"url": "http://aniterasuradio.com:8000/;"
},
{
"name": "Radio Akiba",
"url": "http://stm24.srvstm.com:9526/;"
},
{
"name": "Radio Caprice",
"url": "http://79.111.119.111:8002/anime"
},
{
"name": "Radio Caprice J-Rock",
"url": "http://79.111.119.111:8002/jpop"
},
{
"name": "Nihonara!",
"url": "http://79.111.119.111:8002/jrock"
},
{
"name": "Radio Opening",
"url": "http://5.39.86.120:8000/nihonara_128.mp3"
},
{
"name": "Radio Aewen K-J-Pop",
"url": "http://stream.zeno.fm/tza2ayy47qruv"
},
{
"name": "Radio Wkend",
"url": "http://209.58.145.135:8031/stream"
},
{
"name": "Nihongo FM",
"url": "http://199.180.72.2:9004/stream"
}
]

@ -0,0 +1,782 @@
import { NextFunction, Request, Response } from 'express';
import { requestGot } from '../utils/requestCall';
import {
imageUrlToBase64,
jkanimeInfo,
monoschinosInfo,
tioanimeInfo,
videoServersJK,
videoServersMonosChinos,
videoServersTioAnime,
} from '../utils/util';
import { transformUrlServer } from '../utils/transformerUrl';
import AnimeModel, { Anime as ModelA } from '../database/models/anime.model';
import util from 'util';
import { hashStringMd5 } from '../utils/util';
import {
animeExtraInfo,
getAnimeVideoPromo,
getAnimeCharacters,
getRelatedAnimesMAL,
} from '../utils/util';
import urls from '../utils/urls';
import { redisClient } from '../database/connection';
// @ts-ignore
redisClient.get = util.promisify(redisClient.get);
/*
AnimeController - a class to manage the schedule,
top, all the animes, return the last episodes
with async to return promises.
*/
interface Schedule {
title: string;
mal_id: number;
image_url: any;
}
interface Anime {
index: string;
animeId: string;
title: string;
id: string;
type: string;
}
interface Top {
rank: string;
title: string;
url: string;
image_url: string;
type: string;
subtype: string;
page: number;
score: string;
}
interface Episode {
id: string;
title: string;
image: string;
episode: number;
servers: { id: string; url: string; direct: boolean };
}
interface Movie {
id: string;
title: string;
type: string;
page: string;
banner: string;
image: string;
synopsis: string;
status: string;
rate: string;
genres: string[];
episodes: object[];
}
export default class AnimeController {
async schedule(req: Request, res: Response, next: NextFunction) {
const { day } = req.params;
let info: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`schedule_${hashStringMd5(day)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
info = await requestGot(`${urls.BASE_JIKAN}schedules?filter=${day}`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const animeList: Schedule[] = info.data.map((item: any) => ({
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
malid: item.mal_id,
image: item.images.jpg.image_url,
}));
if (animeList.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`schedule_${hashStringMd5(day)}`,
JSON.stringify({ day: animeList }),
);
/* After 6hrs expire the key. */
redisClient.expire(
`schedule_${hashStringMd5(day)}`,
+ 21600,
);
}
res.status(200).json({
day: animeList,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async top(req: Request, res: Response, next: NextFunction) {
const { type, subtype, page } = req.params;
let info: any;
try {
if (redisClient.connected) {
let resultQueryRedis: any;
if (subtype) {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`top_${hashStringMd5(`${type}:${page}`)}`,
);
}
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
if (subtype !== undefined) {
info = await requestGot(
`${urls.BASE_JIKAN}top/${type}?filter=${subtype}&page=${page}`,
{ parse: true, scrapy: false },
);
} else {
info = await requestGot(`${urls.BASE_JIKAN}top/${type}?page=${page}`, {
parse: true,
scrapy: false,
});
}
} catch (err) {
return next(err);
}
const top: Top[] = info.data.map((item: any, index: number) => ({
// A little hacky way to fix null ranks
rank: item.rank || index + 1 + (info.pagination.current_page-1)*info.pagination.items.per_page,
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
url: item.url,
image_url: item.images.jpg.image_url,
type: type,
subtype: subtype,
page: page,
score: item.score,
}));
if (top.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
if (subtype) {
redisClient.set(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
JSON.stringify({ top }),
);
} else {
redisClient.set(
`top_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ top }),
);
}
/* After 24hrs expire the key. */
if (subtype) {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${subtype}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`top_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
}
return res.status(200).json({ top });
} else {
return res.status(400).json({ message: 'Aruppi lost in the shell' });
}
}
async getAllAnimes(req: Request, res: Response, next: NextFunction) {
let data: any;
try {
data = await requestGot(`${urls.BASE_ANIMEFLV}api/animes/list`, {
parse: true,
scrapy: false,
spoof: true,
});
} catch (err) {
return next(err);
}
const animes: Anime[] = data.map((item: any) => ({
index: item[0],
animeId: item[3],
title: item[1],
id: item[2],
type: item[4],
}));
if (animes.length > 0) {
res.status(200).send({ animes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getLastEpisodes(req: Request, res: Response, next: NextFunction) {
let lastEpisodes;
let episodes: Episode[] = [];
let animeList: any[] = [];
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
lastEpisodes = await requestGot(`${urls.BASE_ARUPPI_MONOSCHINOS}lastest`, {
scrapy: false,
parse: true,
});
} catch (err) {
return next(err);
}
for (const anime of lastEpisodes) {
animeList.push({
id: `ver/${anime.id}`,
title: anime.title,
image: anime.image,
episode: anime.no,
});
}
for (const anime of animeList) {
episodes.push({
id: anime.id,
title: anime.title,
image: await imageUrlToBase64(anime.image),
episode: anime.episode,
servers: await videoServersMonosChinos(anime.id),
});
}
if (episodes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
JSON.stringify({ episodes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`lastEpisodes_${hashStringMd5('lastEpisodes')}`,
parseInt(`${+new Date() / 1000}`, 10) + 1800,
);
}
res.status(200).json({
episodes,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getContentTv(req: Request, res: Response, next: NextFunction) {
const { type, page } = req.params;
const url = 'tv';
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
const animes: Movie[] = data[url].map((item: any) => {
return {
id: item.id,
title: item.title,
type: url,
page: page,
banner: item.banner,
image: item.poster,
synopsis: item.synopsis,
status: item.debut,
rate: item.rating,
genres: item.genres.map((genre: any) => genre),
episodes: item.episodes.map((episode: any) => episode),
};
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`contentTv_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({
animes,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getContentSpecial(req: Request, res: Response, next: NextFunction) {
const { type, page } = req.params;
const url = 'special';
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
const animes: Movie[] = data[url].map((item: any) => {
return {
id: item.id,
title: item.title,
type: url,
page: page,
banner: item.banner,
image: item.poster,
synopsis: item.synopsis,
status: item.debut,
rate: item.rating,
genres: item.genres.map((genre: any) => genre),
episodes: item.episodes.map((episode: any) => episode),
};
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`contentSpecial_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({
animes,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getContentOva(req: Request, res: Response, next: NextFunction) {
const { type, page } = req.params;
const url = 'ova';
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
const animes: Movie[] = data[url].map((item: any) => {
return {
id: item.id,
title: item.title,
type: url,
page: page,
banner: item.banner,
image: item.poster,
synopsis: item.synopsis,
status: item.debut,
rate: item.rating,
genres: item.genres.map((genre: any) => genre),
episodes: item.episodes.map((episode: any) => episode),
};
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`contentOva_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({
animes,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getContentMovie(req: Request, res: Response, next: NextFunction) {
const { type, page } = req.params;
const url = 'movies';
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_ANIMEFLV_JELU}${
url.charAt(0).toUpperCase() + url.slice(1)
}/${type}/${page}`,
{
parse: true,
scrapy: false,
},
);
} catch (err) {
return next(err);
}
const animes: Movie[] = data[url].map((item: any) => {
return {
id: item.id,
title: item.title,
type: url,
page: page,
banner: item.banner,
image: item.poster,
synopsis: item.synopsis,
status: item.debut,
rate: item.rating,
genres: item.genres.map((genre: any) => genre),
episodes: item.episodes.map((episode: any) => episode),
};
});
if (animes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
JSON.stringify({ animes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`contentMovie_${hashStringMd5(`${type}:${page}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({
animes,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getEpisodes(req: Request, res: Response, next: NextFunction) {
const { title } = req.params;
let searchAnime: ModelA | null;
let episodes: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`episodes_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
searchAnime = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
} catch (err) {
return next(err);
}
switch (searchAnime?.source) {
case 'jkanime':
episodes = await jkanimeInfo(searchAnime?.id, searchAnime?.mal_id);
break;
case 'monoschinos':
episodes = await monoschinosInfo(searchAnime?.id, searchAnime?.mal_id);
break;
case 'tioanime':
episodes = await tioanimeInfo(searchAnime?.id, searchAnime?.mal_id);
break;
default:
episodes = undefined;
break;
}
if (episodes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`episodes_${hashStringMd5(title)}`,
JSON.stringify({ episodes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`episodes_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ episodes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getServers(req: Request, res: Response, next: NextFunction) {
const { id } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`servers_${hashStringMd5(id)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
let indicator = false;
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersTioAnime(id);
if (!data.name) {
indicator = true;
}
}
if (id.split('/')[0] === 'ver' && !indicator) {
data = await videoServersMonosChinos(id);
if (!data.name) {
console.log(data.name);
indicator = true;
}
}
if (!indicator) {
data = undefined;
indicator = true;
/*
This part is just for handling the error
if the two above doesn't complete the operation
does not make sense to have the getServers from
JKAnime.
*/
}
if (data) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`servers_${hashStringMd5(id)}`,
JSON.stringify({ servers: data }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`servers_${hashStringMd5(id)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ servers: data });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
} catch (err) {
return next(err);
}
}
async getRandomAnime(req: Request, res: Response, next: NextFunction) {
let animeQuery: ModelA[] | null;
let animeResult: any;
try {
animeQuery = await AnimeModel.aggregate([{ $sample: { size: 1 } }]);
} catch (err) {
return next(err);
}
animeResult = {
title: animeQuery[0].title || null,
poster: animeQuery[0].poster || null,
synopsis: animeQuery[0].description || null,
type: animeQuery[0].type || null,
rating: animeQuery[0].score || null,
genres: animeQuery[0].genres || null,
moreInfo: [await animeExtraInfo(animeQuery[0].mal_id)],
promo: await getAnimeVideoPromo(animeQuery[0].mal_id),
characters: await getAnimeCharacters(animeQuery[0].mal_id),
related: await getRelatedAnimesMAL(animeQuery[0].mal_id),
};
if (animeResult) {
res.set('Cache-Control', 'no-store');
res.status(200).json(animeResult);
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
}

@ -0,0 +1,478 @@
import { NextFunction, Request, Response } from 'express';
import { requestGot } from '../utils/requestCall';
import AnimeModel, { Anime } from '../database/models/anime.model';
import util from 'util';
import { hashStringMd5 } from '../utils/util';
import {
animeExtraInfo,
getAnimeVideoPromo,
getAnimeCharacters,
getRelatedAnimesMAL,
} from '../utils/util';
import urls from '../utils/urls';
import { redisClient } from '../database/connection';
// @ts-ignore
redisClient.get = util.promisify(redisClient.get);
/*
DirectoryController - async functions controlling the directory
in the database of MongoDB, functions like getAllDirectory from the DB
other functions with realation to the directory, like the season and stuff.
*/
interface TypeAnime {
title: string;
image: string;
genres: string[];
}
interface Season {
title: string;
image: string;
malink: string;
}
interface Archive {
year: string;
seasons: string[];
}
export default class DirectoryController {
async getAllDirectory(req: Request, res: Response, next: NextFunction) {
const { genres } = req.params;
try {
if (genres === 'sfw') {
await AnimeModel.find(
{
genres: { $nin: ['ecchi', 'Ecchi'] },
},
(err: any, docs: Anime[]) => {
let directory: any[] = [];
for (const item of docs) {
directory.push({
id: item.id,
title: item.title,
mal_id: item.mal_id,
poster: item.poster,
type: item.type,
genres: item.genres,
score: item.score,
source: item.source,
description: item.description,
});
}
if (directory.length > 0) {
res.status(200).json({ directory });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
},
);
} else {
await AnimeModel.find((err: any, docs: Anime[]) => {
let directory: any[] = [];
for (const item of docs) {
directory.push({
id: item.id,
title: item.title,
mal_id: item.mal_id,
poster: item.poster,
type: item.type,
genres: item.genres,
score: item.score,
source: item.source,
description: item.description,
});
}
if (directory.length > 0) {
res.status(200).json({ directory });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
});
}
} catch (err) {
return next(err);
}
}
async getSeason(req: Request, res: Response, next: NextFunction) {
const { year, type } = req.params;
let info: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`season_${hashStringMd5(`${year}:${type}`)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
info = await requestGot(`${urls.BASE_JIKAN}seasons/${year}/${type}`, {
scrapy: false,
parse: true,
});
} catch (err) {
return next(err);
}
const season: TypeAnime[] = info.data.map((item: any) => {
return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
image: item.images.jpg.image_url,
genres: item.genres.map((genre: any) => genre.name),
};
});
if (season.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`season_${hashStringMd5(`${year}:${type}`)}`,
JSON.stringify({ season }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`season_${hashStringMd5(`${year}:${type}`)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({
season,
});
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async allSeasons(req: Request, res: Response, next: NextFunction) {
let info: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`allSeasons_${hashStringMd5('allSeasons')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
info = await requestGot(`${urls.BASE_JIKAN}seasons`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const archive: Archive[] = info.data.map((item: any) => {
return {
year: item.year,
seasons: item.seasons,
};
});
if (archive.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`allSeasons_${hashStringMd5('allSeasons')}`,
JSON.stringify({ archive }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`allSeasons_${hashStringMd5('allSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ archive });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async laterSeasons(req: Request, res: Response, next: NextFunction) {
let info: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
info = await requestGot(`${urls.BASE_JIKAN}seasons/upcoming`, {
parse: true,
scrapy: false,
});
} catch (err) {
return next(err);
}
const future: Season[] = info.data.map((item: any) => {
return {
title: item.titles.find((x: { type: string; }) => x.type === "Default").title,
image: item.images.jpg.image_url,
malink: item.url,
};
});
if (future.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
JSON.stringify({ future }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`laterSeasons_${hashStringMd5('laterSeasons')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ future });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getMoreInfo(req: Request, res: Response, next: NextFunction) {
const { title } = req.params;
let resultQuery: Anime | null;
let resultAnime: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`moreInfo_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
resultQuery = await AnimeModel.findOne({
$or: [{ title: { $eq: title } }, { title: { $eq: `${title} (TV)` } }],
});
const extraInfo: any = await animeExtraInfo(resultQuery!.mal_id);
resultAnime = {
//aruppi_key: hashStringMd5(title),
title: resultQuery?.title,
poster: resultQuery?.poster,
synopsis: resultQuery?.description,
status: !extraInfo.aired.to ? 'En emisión' : 'Finalizado',
type: resultQuery?.type,
rating: resultQuery?.score,
genres: resultQuery?.genres,
moreInfo: [extraInfo],
promo: await getAnimeVideoPromo(resultQuery!.mal_id),
characters: await getAnimeCharacters(resultQuery!.mal_id),
related: await getRelatedAnimesMAL(resultQuery!.mal_id),
};
} catch (err) {
return next(err);
}
if (resultAnime) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`moreInfo_${hashStringMd5(title)}`,
JSON.stringify(resultAnime),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`moreInfo_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json(resultAnime);
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async search(req: Request, res: Response, next: NextFunction) {
const { title } = req.params;
let results: Anime[] | null;
try {
results = await AnimeModel.find({
title: { $regex: new RegExp(title, 'i') },
});
} catch (err) {
return next(err);
}
const resultAnimes: any[] = results.map((item: any) => {
return {
id: item.id,
title: item.title,
type: item.type,
image: item.poster,
};
});
if (resultAnimes.length > 0) {
res.status(200).json({ search: resultAnimes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getAnimeGenres(req: Request, res: Response, next: NextFunction) {
const { genre, order, page } = req.params;
let result: any;
const genres: any = {
accion: 'Acción',
'artes-marciales': 'Artes Marciales',
aventura: 'Aventuras',
carreras: 'Carreras',
'ciencia-ficcion': 'Ciencia Ficción',
comedia: 'Comedia',
demencia: 'Demencia',
demonios: 'Demonios',
deportes: 'Deportes',
drama: 'Drama',
ecchi: 'Ecchi',
escolares: 'Escolares',
espacial: 'Espacial',
fantasia: 'Fantasía',
harem: 'Harem',
historico: 'Historico',
infantil: 'Infantil',
josei: 'Josei',
juegos: 'Juegos',
magia: 'Magia',
mecha: 'Mecha',
militar: 'Militar',
misterio: 'Misterio',
musica: 'Música',
parodia: 'Parodia',
policia: 'Policía',
psicologico: 'Psicológico',
'recuentos-de-la-vida': 'Recuentos de la vida',
romance: 'Romance',
samurai: 'Samurai',
seinen: 'Seinen',
shoujo: 'Shoujo',
shounen: 'Shounen',
sobrenatural: 'Sobrenatural',
superpoderes: 'Superpoderes',
suspenso: 'Suspenso',
terror: 'Terror',
vampiros: 'Vampiros',
yaoi: 'Yaoi',
yuri: 'Yuri',
};
try {
if (genre === undefined && order === undefined && page === undefined) {
result = await AnimeModel.aggregate([{ $sample: { size: 25 } }]);
} else {
// eslint-disable-next-line no-prototype-builtins
if (genres.hasOwnProperty(genre)) {
if (page !== undefined && parseInt(page) > 1) {
if (order === 'asc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page))
.sort({ title: 'ascending' });
} else if (order === 'desc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page))
.sort({ title: 'descending' });
} else {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.skip(25 * parseInt(page));
}
} else {
if (order === 'asc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.sort({ title: 'ascending' });
} else if (order === 'desc') {
result = await AnimeModel.find({ genres: genres[genre] })
.limit(25)
.sort({ title: 'descending' });
} else {
result = await AnimeModel.find({ genres: genres[genre] }).limit(
25,
);
}
}
} else {
return res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
} catch (err) {
return next(err);
}
const animes: any[] = result.map((item: any) => {
return {
id: item.id,
title: item.title.trim(),
mention: genre,
page: page,
poster: item.poster,
banner: item.banner,
synopsis: item.synopsis,
type: item.type,
rating: item.rating,
genre: item.genre,
};
});
if (animes.length > 0) {
res.status(200).json({ animes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
}

@ -0,0 +1,818 @@
import { NextFunction, Request, Response } from 'express';
import Parser from 'rss-parser';
import urls from '../utils/urls';
import { obtainPreviewNews } from '../utils/obtainPreviews';
import { requestGot } from '../utils/requestCall';
import RadioStationModel, {
RadioStation,
} from '../database/models/radiostation.model';
import ThemeModel, { Theme } from '../database/models/theme.model';
import ThemeParser from '../utils/animeTheme';
import { structureThemes } from '../utils/util';
import { getThemes } from '../utils/util';
import WaifuModel, { Waifu } from '../database/models/waifu.model';
import util from 'util';
import { hashStringMd5 } from '../utils/util';
import { redisClient } from '../database/connection';
// @ts-ignore
redisClient.get = util.promisify(redisClient.get);
/*
UtilsController - controller to parse the
feed and get news, all with scraping and
parsing RSS.
*/
const themeParser = new ThemeParser();
type CustomFeed = {
foo: string;
};
type CustomItem = {
bar: number;
itunes: { duration: string; image: string };
'content:encoded': string;
'content:encodedSnippet': string;
};
const parser: Parser<CustomFeed, CustomItem> = new Parser({
customFields: {
feed: ['foo'],
item: ['bar'],
},
});
interface News {
title?: string;
url?: string;
author?: string;
thumbnail?: string;
content?: string;
}
interface Podcast {
title?: string;
duration?: string;
created?: Date | string;
mp3?: string;
}
interface rssPage {
url: string;
author: string;
content: string;
}
export default class UtilsController {
async getAnitakume(req: Request, res: Response, next: NextFunction) {
let feed: CustomFeed & Parser.Output<CustomItem>;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`anitakume_${hashStringMd5('anitakume')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
feed = await parser.parseURL(urls.BASE_IVOOX);
} catch (err) {
return next(err);
}
const podcast: Podcast[] = [];
const monthNames = [
'Enero',
'Febrero',
'Marzo',
'Abril',
'Mayo',
'Junio',
'Julio',
'Agosto',
'Septiembre',
'Octubre',
'Noviembre',
'Diciembre',
];
feed.items.forEach((item: any) => {
const date: Date = new Date(item.pubDate!);
const formattedObject: Podcast = {
title: item.title,
duration: item.itunes.duration,
created: `${date.getDate()} de ${
monthNames[date.getMonth()]
} de ${date.getFullYear()}`,
mp3: item.enclosure?.url,
};
podcast.push(formattedObject);
});
if (podcast.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`anitakume_${hashStringMd5('anitakume')}`,
JSON.stringify({ podcast }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`anitakume_${hashStringMd5('anitakume')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ podcast });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getNews(req: Request, res: Response, next: NextFunction) {
const news: News[] = [];
const pagesRss: rssPage[] = [
{ url: urls.BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
{
url: urls.BASE_RAMENPARADOS,
author: 'Ramen para dos',
content: 'content',
},
{
url: urls.BASE_CRUNCHYROLL,
author: 'Crunchyroll',
content: 'content_encoded',
},
];
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`news_${hashStringMd5('news')}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
for (const rssPage of pagesRss) {
const feed = await parser.parseURL(rssPage.url);
feed.items.forEach((item: any) => {
const formattedObject: News = {
title: item.title,
url: item.link,
author: feed.title?.includes('Crunchyroll')
? 'Crunchyroll'
: feed.title,
thumbnail: obtainPreviewNews(item['content:encoded']),
content: item['content:encoded'],
};
news.push(formattedObject);
});
}
} catch (err) {
return next(err);
}
if (news.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`news_${hashStringMd5('news')}`,
JSON.stringify({ news }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`news_${hashStringMd5('news')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ news });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getImages(req: Request, res: Response, next: NextFunction) {
const { title } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`images_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_QWANT}t=images&q=${encodeURIComponent(
title,
)}&count=51&locale=es_ES&safesearch=1`,
{ scrapy: false, parse: true, spoof: true, },
);
} catch (err) {
return next(err);
}
const results: any[] = data.data.result.items.map((item: any) => {
return {
type: item.thumb_type,
thumbnail: `${item.thumbnail}`,
fullsize: `${item.media_fullsize}`,
};
});
if (results.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`images_${hashStringMd5(title)}`,
JSON.stringify({ images: results }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`images_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.set('Cache-Control', 'max-age=604800');
res.status(200).json({ images: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getVideos(req: Request, res: Response, next: NextFunction) {
const { channelId } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`videos_${hashStringMd5(channelId)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`,
{ scrapy: false, parse: true },
);
} catch (err) {
return next(err);
}
const results: any[] = data.items.map((item: any) => {
return {
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
};
});
if (results.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`videos_${hashStringMd5(channelId)}`,
JSON.stringify({ videos: results }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`videos_${hashStringMd5(channelId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ videos: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getPlaylists(req: Request, res: Response, next: NextFunction) {
const { playlistId } = req.params;
let data: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = redisClient.get(
`videos_${hashStringMd5(playlistId)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
data = await requestGot(
`${urls.BASE_YOUTUBE_PLAYLIST}${playlistId}`,
{ scrapy: false, parse: true },
);
} catch (err) {
return next(err);
}
const results: any[] = data.items.map((item: any) => {
return {
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
};
});
if (results.length > 0) {
if (redisClient.connected) {
/!* Set the key in the redis cache. *!/
redisClient.set(
`videos_${hashStringMd5(playlistId)}`,
JSON.stringify({ videos: results }),
);
/!* After 24hrs expire the key. *!/
redisClient.expireat(
`videos_${hashStringMd5(playlistId)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ videos: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getSectionVideos(req: Request, res: Response, next: NextFunction) {
const { type } = req.params;
let y1: any, y2: any, y3: any;
let data: any;
try {
if (type === 'learn') {
data = await requestGot(
`${urls.BASE_YOUTUBE}UCCyQwSS6m2mVB0-H2FOFJtw&part=snippet,id&order=date&maxResults=50`,
{ parse: true, scrapy: false },
);
} else if (type === 'amv') {
y1 = await requestGot(
`${urls.BASE_YOUTUBE}UCkTFkshjAsLMKwhAe1uPC1A&part=snippet,id&order=date&maxResults=25`,
{ parse: true, scrapy: false },
);
y2 = await requestGot(
`${urls.BASE_YOUTUBE}UC2cpvlLeowpqnR6bQofwNew&part=snippet,id&order=date&maxResults=25`,
{ parse: true, scrapy: false },
);
} else if (type === 'produccer') {
y1 = await requestGot(
`${urls.BASE_YOUTUBE}UC-5MT-BUxTzkPTWMediyV0w&part=snippet,id&order=date&maxResults=25`,
{ parse: true, scrapy: false },
);
y2 = await requestGot(
`${urls.BASE_YOUTUBE}UCwUeTOXP3DD9DIvHttowuSA&part=snippet,id&order=date&maxResults=25`,
{ parse: true, scrapy: false },
);
y3 = await requestGot(
`${urls.BASE_YOUTUBE}UCA8Vj7nN8bzT3rsukD2ypUg&part=snippet,id&order=date&maxResults=25`,
{ parse: true, scrapy: false },
);
}
} catch (err) {
return next(err);
}
if (data && !y1 && !y2 && !y3) {
const results: any[] = data.items.map((item: any) => ({
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
}));
res.status(200).json({ videos: results });
} else if (!data && y1 && y2 && !y3) {
const results: any[] = y1.items.concat(y2.items).map((item: any) => ({
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
}));
res.status(200).json({ videos: results });
} else if (!data && y1 && y2 && y3) {
const results: any[] = y1.items
.concat(y2.items.concat(y3.items))
.map((item: any) => ({
title: item.snippet.title,
videoId: item.id.videoId,
thumbDefault: item.snippet.thumbnails.default.url,
thumbMedium: item.snippet.thumbnails.medium.url,
thumbHigh: item.snippet.thumbnails.high.url,
}));
res.status(200).json({ videos: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getRadioStations(req: Request, res: Response, next: NextFunction) {
let data: RadioStation[];
try {
data = await RadioStationModel.find();
} catch (err) {
return next(err);
}
const results: any[] = data.map((item: RadioStation) => {
return {
name: item.name,
url: item.url,
};
});
if (results.length > 0) {
res.status(200).json({ stations: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getAllThemes(req: Request, res: Response, next: NextFunction) {
let data: Theme[];
try {
data = await ThemeModel.find();
} catch (err) {
return next(err);
}
const results: any[] = data.map((item: Theme) => {
return {
id: item.id,
title: item.title,
year: item.year,
themes: item.themes,
};
});
if (results.length > 0) {
res.status(200).json({ themes: results });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getOpAndEd(req: Request, res: Response, next: NextFunction) {
const { title } = req.params;
let themes: any;
try {
if (redisClient.connected) {
const resultQueryRedis: any = await redisClient.get(
`oped_${hashStringMd5(title)}`,
);
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
themes = await structureThemes(await themeParser.serie(title), true);
} catch (err) {
return next(err);
}
if (themes) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
redisClient.set(
`oped_${hashStringMd5(title)}`,
JSON.stringify({ themes }),
);
/* After 24hrs expire the key. */
redisClient.expireat(
`oped_${hashStringMd5(title)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
res.status(200).json({ themes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getThemesYear(req: Request, res: Response, next: NextFunction) {
const { year } = req.params;
let themes: any;
let resultQueryRedis: any;
try {
if (redisClient.connected) {
if (year) {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5(year)}`,
);
} else {
resultQueryRedis = await redisClient.get(
`themesyear_${hashStringMd5('allYear')}`,
);
}
if (resultQueryRedis) {
const resultRedis: any = JSON.parse(resultQueryRedis);
return res.status(200).json(resultRedis);
}
}
if (year === undefined) {
themes = await themeParser.allYears();
} else {
themes = await structureThemes(await themeParser.year(year), false);
}
} catch (err) {
return next(err);
}
if (themes.length > 0) {
if (redisClient.connected) {
/* Set the key in the redis cache. */
if (year) {
redisClient.set(
`themesyear_${hashStringMd5(year)}`,
JSON.stringify({ themes }),
);
} else {
redisClient.set(
`themesyear_${hashStringMd5('allYear')}`,
JSON.stringify({ themes }),
);
}
/* After 24hrs expire the key. */
if (year) {
redisClient.expireat(
`themesyear_${hashStringMd5(year)}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
} else {
redisClient.expireat(
`themesyear_${hashStringMd5('allYear')}`,
parseInt(`${+new Date() / 1000}`, 10) + 7200,
);
}
}
res.status(200).json({ themes });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async randomTheme(req: Request, res: Response, next: NextFunction) {
let data: any;
try {
data = await requestGot(`${urls.BASE_THEMEMOE}roulette`, {
parse: true,
scrapy: false,
spoof: true,
});
} catch (err) {
return next(err);
}
const random: any[] = getThemes(data.themes);
if (random.length > 0) {
res.set('Cache-Control', 'no-store');
res.status(200).json({ random });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getArtist(req: Request, res: Response, next: NextFunction) {
const { id } = req.params;
let artists: any;
try {
if (id === undefined) {
artists = await themeParser.artists();
} else {
artists = await structureThemes(await themeParser.artist(id), false);
}
} catch (err) {
return next(err);
}
if (artists.length > 0) {
res.status(200).json({ artists });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getDestAnimePlatforms(req: Request, res: Response, next: NextFunction) {
let data: any;
try {
data = await requestGot(
`${urls.BASE_ARUPPI}res/documents/animelegal/top.json`,
{ parse: true, scrapy: false },
);
} catch (err) {
return next(err);
}
const destPlatforms: any[] = data.map((item: any) => {
return {
id: item.id,
name: item.name,
logo: item.logo,
link: item.link,
};
});
if (destPlatforms.length > 0) {
res.status(200).json({ destPlatforms });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getPlatforms(req: Request, res: Response, next: NextFunction) {
const { id } = req.params;
let data: any;
try {
if (id === undefined) {
data = await requestGot(
`${urls.BASE_ARUPPI}res/documents/animelegal/typeplatforms.json`,
{ parse: true, scrapy: false },
);
} else if (
id === 'producers' ||
id === 'apps' ||
id === 'publishers' ||
'events'
) {
data = await requestGot(
`${urls.BASE_ARUPPI}res/documents/animelegal/type/${id}.json`,
{ parse: true, scrapy: false },
);
} else {
data = await requestGot(
`${urls.BASE_ARUPPI}res/documents/animelegal/type/${id}.json`,
{ parse: true, scrapy: false },
);
}
} catch (err) {
return next(err);
}
const platforms: any[] = data.map((item: any) => {
if (id === undefined) {
return {
id: item.id,
name: item.name,
comming: item.comming || false,
cover: item.cover,
};
} else if (
id === 'producers' ||
id === 'apps' ||
id === 'publishers' ||
'events'
) {
return {
id: item.id,
name: item.name,
logo: item.logo,
cover: item.cover,
description: item.description,
type: item.type,
moreInfo: item.moreInfo,
facebook: item.facebook,
twitter: item.twitter,
instagram: item.instagram,
webInfo: item.webInfo,
webpage: item.webpage,
};
} else {
return {
id: item.id,
name: item.name,
type: item.type,
logo: item.logo,
cover: item.cover,
webpage: item.webpage,
};
}
});
if (platforms.length > 0) {
res.status(200).json({ platforms });
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
async getWaifuRandom(req: Request, res: Response, next: NextFunction) {
let waifuQuery: Waifu[] | null;
let waifuResult: any;
try {
waifuQuery = await WaifuModel.aggregate([{ $sample: { size: 1 } }]);
} catch (err) {
return next(err);
}
if (waifuQuery.length > 0) {
waifuResult = {
id: waifuQuery[0].id,
name: waifuQuery[0].name,
weight: waifuQuery[0].weight,
series: waifuQuery[0].series,
height: waifuQuery[0].height,
birthday: waifuQuery[0].birthday,
likes: waifuQuery[0].likes,
trash: waifuQuery[0].trash,
blood_type: waifuQuery[0].blood_type,
hip: waifuQuery[0].hip,
bust: waifuQuery[0].bust,
description: waifuQuery[0].description,
display_picture: waifuQuery[0].display_picture,
waist: waifuQuery[0].waist,
};
}
if (waifuResult) {
res.set('Cache-Control', 'no-store');
res.status(200).json(waifuResult);
} else {
res.status(500).json({ message: 'Aruppi lost in the shell' });
}
}
}

@ -0,0 +1,69 @@
import mongoose from 'mongoose';
import redis, { RedisClient } from 'redis';
import dotenv from 'dotenv';
// Configuring dotenv to read the variable from .env file
dotenv.config();
/*
Create the connection to the database
of mongodb.
*/
export const createConnectionMongo = (databaseObj: {
port: string | undefined;
host: string | undefined;
}) => {
mongoose.connect(
`mongodb://${databaseObj.host}:${databaseObj.port}/anime-directory`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
},
);
mongoose.connection.on('error', err => {
console.log('err', err);
});
mongoose.connection.on('connected', (err, res) => {
console.log('Database connected: mongoose.');
});
};
/*
Create the connection to the cache of
redis, and exporting the redis client
with the call of this file.
*/
export const redisClient: RedisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT!),
password: process.env.REDIS_PASSWORD,
retry_strategy: function (options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error('The server refused the connection');
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error('Retry time exhausted');
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
});
redisClient.on('connect', () => {
console.log('Redis connected: redis.');
});
redisClient.on('error', function (err) {
console.log('Redis error: ' + err);
});

@ -0,0 +1,33 @@
import { Document, model, Types, Schema } from 'mongoose';
/*
This is the model for each anime
of the directory, the anime model.
*/
export interface Anime extends Document {
id: string;
title: string;
mal_id: number;
poster: string;
type: string;
genres: Types.Array<string>;
score: string;
source: string;
description: string;
}
// Schema for the anime
const AnimeSchema: Schema = new Schema({
id: { type: String },
title: { type: String },
mal_id: { type: Number },
poster: { type: String },
type: { type: String },
genres: [{ type: String }],
score: { type: String },
source: { type: String },
description: { type: String },
});
export default model<Anime>('Anime', AnimeSchema);

@ -0,0 +1,19 @@
import { Document, model, Types, Schema } from 'mongoose';
/*
This is the model for each genre
of the directory, the genre model.
*/
export interface Genre extends Document {
name: string;
value: string;
}
// Schema for the theme
const GenreSchema: Schema = new Schema({
name: { type: String },
value: { type: String },
});
export default model<Genre>('Genre', GenreSchema);

@ -0,0 +1,19 @@
import { Document, model, Schema } from 'mongoose';
/*
This is the model for each radiostation
of the directory, the radiostation model.
*/
export interface RadioStation extends Document {
name: string;
url: string;
}
// Schema for the theme
const RadioStationSchema: Schema = new Schema({
name: { type: String },
url: { type: String },
});
export default model<RadioStation>('RadioStation', RadioStationSchema);

@ -0,0 +1,29 @@
import { Document, model, Types, Schema } from 'mongoose';
/*
This is the model for each theme
of the directory, the theme model.
*/
interface TInterface {
title: string;
video: string;
type: string;
}
export interface Theme extends Document {
id: string;
title: string;
year: string;
themes: Types.Array<TInterface>;
}
// Schema for the theme
const ThemeSchema: Schema = new Schema({
id: { type: String },
title: { type: String },
year: { type: String },
themes: [{ type: Object }],
});
export default model<Theme>('Theme', ThemeSchema);

@ -0,0 +1,43 @@
import { Document, model, Schema } from 'mongoose';
/*
This is the model for each anime
of the directory, the anime model.
*/
export interface Waifu extends Document {
id: string;
name: string;
weight: string;
series: object;
height: string;
birthday: string;
likes: number;
trash: number;
blood_type: string;
hip: string;
bust: string;
description: string;
display_picture: string;
waist: string;
}
// Schema for the Waifu
const WaifuSchema: Schema = new Schema({
id: { type: String },
name: { type: String },
weight: { type: String },
series: { type: Object },
height: { type: String },
birthday: { type: String },
likes: { type: Number },
trash: { type: Number },
blood_type: { type: String },
hip: { type: String },
bust: { type: String },
description: { type: String },
display_picture: { type: String },
waist: { type: String },
});
export default model<Waifu>('Waifu', WaifuSchema);

@ -1,20 +0,0 @@
const app = require('./app');
const port = process.env.PORT || 5000;
const addr = isNaN(port) ?
'' :
(process.env.ADDR || '0.0.0.0');
server = app.listen(port, addr, () => {
/* eslint-disable no-console */
console.log(`\n🚀 ... Listening: ${addr}${addr ? '\:' : 'unix://'}${port}`);
/* eslint-enable no-console */
});
function shutdown() {
server.close(); // socket file is automatically removed here
process.exit();
}
process.on('SIGINT', shutdown);
process.on('SIGQUIT', shutdown);
process.on('SIGTERM', shutdown);

@ -1,12 +0,0 @@
const compose = require('compose-middleware').compose;
const {errorHandler , notFound , requestLoggerMiddleware} = require('./middlewares');
const middleware = compose([
notFound,
errorHandler,
requestLoggerMiddleware
]);
module.exports = {
middleware
};

@ -0,0 +1,48 @@
import {
Request,
Response,
NextFunction,
ErrorRequestHandler,
RequestHandler,
} from 'express';
/*
Error handler and notFound handler
for all the API like a middleware with
the function next of express.
*/
export const errorHandler: ErrorRequestHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction,
) => {
const statusCode = res.statusCode !== 200 ? res.statusCode : 500;
res.status(statusCode).json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack,
});
};
export const notFound: any = (
req: Request,
res: Response,
next: NextFunction,
) => {
res.status(404);
const error = new Error(`🔍 - Not Found - ${req.originalUrl}`);
next(error);
};
// export const requestLoggerMiddleWare: RequestHandler = (req, res, next) => {
// console.log(`${req.method} ${req.originalUrl}`);
// const start: number = new Date().getTime();
// res.on('finish', () => {
// const elapsed: number = new Date().getTime() - start;
// console.info(
// `${req.method} ${req.originalUrl} ${req.statusCode} ${elapsed}ms`,
// );
// });
// next();
// };

@ -1,32 +0,0 @@
const notFound = (req, res, next) => {
res.status(404);
const error = new Error(`🔍 - Not Found - ${req.originalUrl}`);
next(error);
};
/* eslint-disable no-unused-vars */
const errorHandler = (err, req, res, next) => {
/* eslint-enable no-unused-vars */
const statusCode = res.statusCode !== 200 ? res.statusCode : 500;
res.status(statusCode);
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack
});
};
const requestLoggerMiddleware = (req, res, next) => {
console.log(`${req.method} ${req.originalUrl}`);
const start = new Date().getTime();
res.on('finish', () => {
const elapsed = new Date().getTime() - start;
console.info(`${req.method} ${req.originalUrl} ${req.statusCode} ${elapsed}ms`);
});
next();
};
module.exports = {
notFound,
errorHandler,
requestLoggerMiddleware
};

@ -0,0 +1,144 @@
import { Router, Request, Response, NextFunction } from 'express';
import AnimeController from './controllers/AnimeController';
import DirectoryController from './controllers/DirectoryController';
import UtilsController from './controllers/UtilsController';
const routes = Router();
const animeController = new AnimeController();
const directoryController = new DirectoryController();
const utilsController = new UtilsController();
routes.get('/', (req: Request, res: Response) => {
// We dont want to enforce the redirect storing, so just check
res.set('Cache-Control', 'no-cache,proxy-revalidate');
res.redirect('/api/v4/');
});
/*
Routes - JSON
Message with the JSON of all the routes in the
/api, parameters and some examples, how to call the
endpoints of the /api.
*/
routes.get('/api/v4/', (req: Request, res: Response) => {
res.set('Cache-Control', 'no-store');
res.json({
message: 'Aruppi /api - 🎏',
author: 'Jéluchu',
version: '4.2.2',
credits: 'The bitch loves /apis that offers data to Aruppi App',
entries: [
{
Schedule: '/api/v4/schedule/:day',
Top: '/api/v4/top/:type/:page/:subtype',
AllAnimes: '/api/v4/allAnimes',
RandomAnime: '/api/v4/randomAnime',
Anitakume: '/api/v4/anitakume',
News: '/api/v4/news',
Season: '/api/v4/season/:year/:type',
'All Seasons': '/api/v4/allSeasons',
'All Directory': '/api/v4/allDirectory/:type',
Genres: '/api/v4/getByGenres/:genre?/:order?/:page?',
'Futures Seasons': '/api/v4/laterSeasons',
LastEpisodes: '/api/v4/lastEpisodes',
Movies: '/api/v4/movies/:type/:page',
Ovas: '/api/v4/ova/:type/:page',
Specials: '/api/v4/special/:type/:page',
Tv: '/api/v4/tv/:type/:page',
MoreInfo: '/api/v4/moreInfo/:title',
GetEpisodes: '/api/v4/getEpisodes/:title',
GetAnimeServers: '/api/v4/getAnimeServers/:id',
Search: '/api/v4/search/:title',
Images: '/api/v4/images/:query',
Videos: '/api/v4/videos/:channelId',
Playlist: '/api/v4/playlistVideos/:playlistId',
'Type Videos': '/api/v4/sectionedVideos/:type',
Radios: '/api/v4/radio',
'All Themes': '/api/v4/allThemes',
Themes: '/api/v4/themes/:title',
'Year Themes': '/api/v4/themesYear/:year?',
'Random Theme': '/api/v4/randomTheme',
'Artists Theme': '/api/v4/artists/:id?',
'Famous Platforms': '/api/v4/destAnimePlatforms',
'Legal Platforms': '/api/v4/platforms/:id?',
},
],
});
});
/* Routes of the app below */
/* Anime Controller */
routes.get('/api/v4/schedule/:day', animeController.schedule);
routes.get('/api/v4/top/:type/:subtype?/:page', animeController.top);
routes.get('/api/v4/allAnimes', animeController.getAllAnimes);
routes.get('/api/v4/lastEpisodes', animeController.getLastEpisodes);
routes.get('/api/v4/movies/:type/:page', animeController.getContentMovie);
routes.get('/api/v4/ova/:type/:page', animeController.getContentOva);
routes.get('/api/v4/special/:type/:page', animeController.getContentSpecial);
routes.get('/api/v4/tv/:type/:page', animeController.getContentTv);
routes.get('/api/v4/getEpisodes/:title', animeController.getEpisodes);
routes.get(
'/api/v4/getAnimeServers/:id([^/]+/[^/]+)',
animeController.getServers,
);
routes.get('/api/v4/randomAnime', animeController.getRandomAnime);
/* Directory Controller */
routes.get(
'/api/v4/allDirectory/:genres?',
directoryController.getAllDirectory,
);
routes.get('/api/v4/season/:year/:type', directoryController.getSeason);
routes.get('/api/v4/allSeasons', directoryController.allSeasons);
routes.get('/api/v4/laterSeasons', directoryController.laterSeasons);
routes.get('/api/v4/moreInfo/:title', directoryController.getMoreInfo);
routes.get('/api/v4/search/:title', directoryController.search);
routes.get(
'/api/v4/getByGenres/:genre?/:order?/:page?',
directoryController.getAnimeGenres,
);
/* Utils Controller */
routes.get('/api/v4/anitakume', utilsController.getAnitakume);
routes.get('/api/v4/news', utilsController.getNews);
routes.get('/api/v4/images/:title', utilsController.getImages);
routes.get('/api/v4/videos/:channelId', utilsController.getVideos);
routes.get('/api/v4/playlistVideos/:playlistId', utilsController.getPlaylists);
routes.get('/api/v4/sectionedVideos/:type', utilsController.getSectionVideos);
routes.get('/api/v4/radio', utilsController.getRadioStations);
routes.get('/api/v4/allThemes', utilsController.getAllThemes);
routes.get('/api/v4/themes/:title', utilsController.getOpAndEd);
routes.get('/api/v4/themesYear/:year?', utilsController.getThemesYear);
routes.get('/api/v4/randomTheme', utilsController.randomTheme);
routes.get('/api/v4/artists/:id?', utilsController.getArtist);
routes.get('/api/v4/destAnimePlatforms', utilsController.getDestAnimePlatforms);
routes.get('/api/v4/platforms/:id?', utilsController.getPlatforms);
routes.get('/api/v4/generateWaifu/', utilsController.getWaifuRandom);
/* Routes to handling the v3 deprecated */
routes.get('/api/v3/*', (req: Request, res: Response, next: NextFunction) => {
res.status(302).redirect('/api/v3');
});
routes.get('/api/v3', (req: Request, res: Response, next: NextFunction) => {
res.status(200).json({
message:
'Sorry, version v3 is not avaiable, if you want to see content go to v4',
});
});
/* Routes to handling the v2 deprecated */
routes.get('/api/v2/*', (req: Request, res: Response, next: NextFunction) => {
res.status(302).redirect('/api/v2');
});
routes.get('/api/v2', (req: Request, res: Response, next: NextFunction) => {
res.status(200).json({
message:
'Sorry, version v2 is not avaiable, if you want to see content go to v4',
});
});
export default routes;

@ -0,0 +1,41 @@
import express, { Application } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import dotenv from 'dotenv';
import { errorHandler, notFound } from './middlewares/middleware';
import {
createConnectionMongo,
} from './database/connection';
import routes from './routes';
const app: Application = express();
dotenv.config();
createConnectionMongo({
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
});
app.use(cors());
app.use(helmet());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(routes);
app.use(notFound);
app.use(errorHandler);
/*
Starting the server on the .env process
you can define the PORT where the server
is going to listen in the server.
ex: PORT=3000.
*/
const server = app.listen(process.env.PORT_LISTEN || 3000);
function shutdown(): void {
server.close();
process.exit();
}
process.on('SIGINT', shutdown);
process.on('SIGQUIT', shutdown);
process.on('SIGTERM', shutdown);

@ -0,0 +1,302 @@
import cheerio from 'cheerio';
import { requestGot } from './requestCall';
import urls from './urls';
export default class ThemeParser {
animes: any[] = [];
$: any = '';
async all() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseLinks();
} catch (err) {
console.log(err);
}
}
async allYears() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseYears();
} catch (err) {
console.log(err);
}
}
async serie(title: string) {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseSerie(title);
} catch (err) {
console.log(err);
}
}
async artists() {
try {
this.animes = [];
this.$ = await redditocall('artist');
return await this.parseArtists();
} catch (err) {
console.log(err);
}
}
async artist(id: string) {
try {
this.animes = [];
this.$ = await redditocall(`artist/${id}`);
return await this.parseArtist();
} catch (err) {
console.log(err);
}
}
async random() {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseRandom();
} catch (err) {
console.log(err);
}
}
async year(date: string) {
let animes: any = [];
this.$ = await redditocall(date);
this.$('h3').each((index: number, element: cheerio.Element) => {
let parsed = this.parseAnime(this.$(element));
parsed.year = date;
animes.push(parsed);
});
return animes;
}
parseRandom() {
return new Promise(async resolve => {
let data = this.$('p a');
const origin: any = '1';
let randomize = Math.round(
Math.random() * (data.length - 1 - origin) + parseInt(origin),
);
this.$ = await redditocall(
this.$('p a')
[randomize].attribs.href.split('/r/AnimeThemes/wiki/')[1]
.split('#wiki')[0],
);
let rand = Math.round(Math.random() * this.$('h3').length - 1);
let parsed = this.parseAnime(this.$('h3')[rand]);
resolve(parsed);
});
}
/* -ParseYears
Get the data from the year
get the name and the id to do the respective
scrapping.
*/
parseYears() {
return new Promise(async resolve => {
let years: any[] = [];
this.$('h3 a').each((index: number, element: cheerio.Element) => {
years.push({
id: this.$(element).attr('href').split('/')[4],
name: this.$(element).text(),
});
});
resolve(years);
});
}
parseArtists() {
return new Promise(async resolve => {
let promises = [];
let data = this.$('p a').filter((x: any) => x > 0);
for (let i = 0; i < data.length; i++) {
promises.push({
id: data[i].children[0].parent.attribs.href.split('/')[5],
name: data[i].children[0].data,
});
if (i === data.length - 1) {
resolve(promises);
}
}
});
}
parseArtist() {
return new Promise(async resolve => {
let promises = [];
let data = this.$('h3');
for (let i = 0; i < data.length; i++) {
let parsed = await this.parseAnime(data[i]);
promises.push(parsed);
if (i === data.length - 1) {
resolve(promises);
}
}
});
}
/* - ParseSerie
Parse the HTML from the redditocall
and search for the h3 tag to be the
same of the title and resolve a object.
*/
parseSerie(title: string) {
return new Promise(async resolve => {
let data = this.$('p a');
for (let i = 0; i < data.length; i++) {
let serieElement = data[i].children[0].data;
if (serieElement.split(' (')[0] === title) {
let year = this.$('p a')
[i].attribs.href.split('/r/AnimeThemes/wiki/')[1]
.split('#wiki')[0];
this.$ = await redditocall(
this.$('p a')
[i].attribs.href.split('/r/AnimeThemes/wiki/')[1]
.split('#wiki')[0],
);
for (let i = 0; i < this.$('h3').length; i++) {
if (this.$('h3')[i].children[0].children[0].data === title) {
let parsed = this.parseAnime(this.$('h3')[i]);
parsed.year = year;
resolve(parsed);
}
}
}
}
});
}
parseLinks() {
return new Promise(async resolve => {
let years = this.$('h3 a');
for (let i = 0; i < years.length; i++) {
let yearElement = years[i];
await this.year(this.$(yearElement).attr('href').split('/')[4]).then(
async animes => {
this.animes = this.animes.concat(animes);
if (i === years.length - 1) {
resolve(this.animes);
}
},
);
}
});
}
/* - ParseAnime
Parse the h3 tag and get the table
for the next function to parse the table
and get the information about the ending and
openings.
*/
parseAnime(element: cheerio.Element) {
let el = this.$(element).find('a');
let title = this.$(el).text();
let mal_id = this.$(el).attr('href').split('/')[4];
let next = this.$(element).next();
let theme: any = {
id: mal_id,
title,
};
if (this.$(next).prop('tagName') === 'TABLE') {
theme.themes = this.parseTable(this.$(next));
} else if (this.$(next).prop('tagName') === 'P') {
theme.themes = this.parseTable(this.$(next).next());
}
return theme;
}
/* - ParseTable
Parse the table tag from the HTML
and returns a object with all the
information.
*/
parseTable(element: cheerio.Element): any {
if (this.$(element).prop('tagName') !== 'TABLE') {
return this.parseTable(this.$(element).next());
}
let themes: any = [];
this.$(element)
.find('tbody')
.find('tr')
.each((index: number, element: cheerio.Element) => {
let name = replaceAll(
this.$(element).find('td').eq(0).text(),
'&quot;',
'"',
);
let link = this.$(element).find('td').eq(1).find('a').attr('href');
let linkDesc = this.$(element).find('td').eq(1).find('a').text();
let episodes =
this.$(element).find('td').eq(2).text().length > 0
? this.$(element).find('td').eq(2).text()
: '';
let notes =
this.$(element).find('td').eq(3).text().length > 0
? this.$(element).find('td').eq(3).text()
: '';
themes.push({
name,
link,
desc: linkDesc,
type: name.startsWith('OP')
? `OP${name[2]}`
: name.startsWith('ED')
? `ED${name[2]}`
: 'OP/ED',
episodes,
notes,
});
});
return themes;
}
}
async function redditocall(href: string) {
const resp = await requestGot(urls.REDDIT_ANIMETHEMES + href + '.json', {
parse: true,
scrapy: false,
spoof: true,
});
return cheerio.load(getHTML(resp.data.content_html));
}
function getHTML(str: string) {
let html = replaceAll(str, '&lt;', '<');
html = replaceAll(html, '&gt;', '>');
return html;
}
function replaceAll(str: string, find: string, replace: string) {
return str.replace(new RegExp(find, 'g'), replace);
}

@ -1,288 +0,0 @@
const cheerio = require('cheerio');
const {
homgot
} = require('../api/apiCall');
const {
REDDIT_ANIMETHEMES
} = require('../api/urls');
class ThemeParser {
constructor() {}
async all() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseLinks();
}
catch(err) {
throw err;
}
}
async allYears() {
try {
this.animes = [];
this.$ = await redditocall('year_index');
return await this.parseYears();
}
catch(err) {
throw err;
}
}
async serie(query) {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseSerie(query);
}
catch(err) {
throw err;
}
}
async artists() {
try {
this.animes = [];
this.$ = await redditocall('artist');
return await this.parseArtists();
}
catch(err) {
throw err;
}
}
async artist(id) {
try {
this.animes = [];
this.$ = await redditocall(`artist/${id}`);
return await this.parseArtist();
}
catch(err) {
throw err;
}
}
async random(query) {
try {
this.animes = [];
this.$ = await redditocall('anime_index');
return await this.parseRandom(query);
}
catch(err) {
throw err;
}
}
async year(date) {
let animes = [];
this.$ = await redditocall(date)
this.$('h3').each((i, el) => {
let parsed = this.parseAnime(el);
parsed.year = date;
animes.push(parsed);
})
return animes;
}
parseRandom() {
return new Promise(async resolve => {
let data = this.$('p a');
const origin = '1'
let randomize = Math.round(Math.random()*((data.length-1)-origin)+parseInt(origin));
this.$ = await redditocall(this.$('p a')[randomize].attribs.href.split('/r/AnimeThemes/wiki/')[1].split('#wiki')[0]);
let rand = Math.round(Math.random()*this.$('h3').length - 1);
let parsed = this.parseAnime(this.$('h3')[rand]);
resolve(parsed);
})
}
parseYears(){
return new Promise(async resolve => {
let promises = []
let data = this.$('h3 a');
for (let i = 0; i < data.length; i++) {
promises.push({
id: data[i].children[0].parent.attribs.href.split('/')[4],
name: data[i].children[0].data
})
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseArtists(){
return new Promise(async resolve => {
let promises = []
let data = this.$('p a').filter(x => x > 0);
for (let i = 0; i < data.length; i++) {
promises.push({
id: data[i].children[0].parent.attribs.href.split('/')[5],
name: data[i].children[0].data
})
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseArtist(){
return new Promise(async resolve => {
let promises = []
let data = this.$('h3');
for (let i = 0; i < data.length; i++) {
let parsed = await this.parseAnime(data[i])
promises.push(parsed)
if (i === data.length - 1) {
resolve(promises)
}
}
})
}
parseSerie(query){
return new Promise(async resolve => {
let data = this.$('p a');
for (let i = 0; i < data.length; i++) {
let serieElement = data[i].children[0].data
if (serieElement.split(" (")[0] === query) {
this.$ = await redditocall(this.$('p a')[i].attribs.href.split('/r/AnimeThemes/wiki/')[1].split('#wiki')[0]);
for (let i = 0; i < this.$('h3').length; i++) {
if (this.$('h3')[i].children[0].children[0].data === query) {
let parsed = this.parseAnime(this.$('h3')[i]);
resolve(parsed);
}
}
}
}
})
}
parseLinks() {
return new Promise(async resolve => {
let years = this.$('h3 a');
this.$('h3 a')[0].children[0].data
for (let i = 0; i < years.length; i++) {
let yearElement = years[i];
await this.year(this.$(yearElement).attr('href').split('/')[4])
.then(async animes => {
this.animes = this.animes.concat(animes);
if(i === years.length - 1) {
resolve(this.animes);
}
})
}
})
}
parseAnime(dat) {
let el = this.$(dat).children('a');
let title = el.text();
let malId = el.attr('href').split('/')[4];
let next = this.$(dat).next();
let theme = {
id: malId,
title
}
if (next.prop("tagName") === "P") {
theme.themes = this.parseTable(next.next());
} else if (next.prop("tagName") === "TABLE") {
theme.themes = this.parseTable(next);
}
return theme;
}
parseTable(table) {
if (table.prop('tagName') !== "TABLE") {
return this.parseTable(table.next());
}
let themes = [];
table.children('tbody').children('tr').each(function () {
const $ = cheerio.load(this);
const td = $('td'); // Theme row
let name = replaceAll(td.first().text(), "&quot;", "\"")
let linkEl = td.eq(1).children().first();
let link = linkEl.attr('href');
let linkDesc = linkEl.text();
let episodes = td.eq(2).text();
let notes = td.eq(3).text();
themes.push({
name,
link,
desc: linkDesc,
type: (name.startsWith('OP') ? 'opening' : 'ending'),
episodes,
notes
})
})
return themes;
}
}
async function redditocall(href) {
let options = { parse: true }
let resp = await homgot(REDDIT_ANIMETHEMES + href + ".json", options)
return cheerio.load(getHTML(resp.data.content_html));
}
function getHTML(str) {
let html = replaceAll(str, "&lt;", "<")
html = replaceAll(html, "&gt;", ">")
return html;
}
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
}
module.exports = ThemeParser;

@ -1,665 +0,0 @@
const {
BASE_ANIMEFLV, BASE_JIKAN, BASE_EPISODE_IMG_URL, SEARCH_URL, SEARCH_DIRECTORY, BASE_ARUPPI, BASE_JKANIME, JKANIME_URL
} = require('../api/urls');
const {
homgot
} = require('../api/apiCall');
function btoa(str) {
let buffer;
if (str instanceof Buffer) {
buffer = str;
}
else {
buffer = Buffer.from(str.toString(), 'binary');
}
return buffer.toString('base64');
}
global.btoa = btoa;
async function videoServersJK(id) {
let options = { scrapy: true }
const $ = await homgot(`${BASE_JKANIME}${id}`, options);
const scripts = $('script');
const totalEps = $('div#container div#reproductor-box div ul li').length;
const serverNames = [];
let servers = [];
$('div#container div#reproductor-box div ul li').each((index , element) =>{
const $element = $(element);
const serverName = $element.find('a').text();
serverNames.push(serverName);
})
for(let i = 0; i < scripts.length; i++){
const $script = $(scripts[i]);
const contents = $script.html();
try{
if ((contents || '').includes('var video = [];')) {
Array.from({length: totalEps} , (v , k) =>{
let index = Number(k + 1);
let videoPageURL = contents.split(`video[${index}] = \'<iframe class="player_conte" src="`)[1].split('"')[0];
servers.push({iframe: videoPageURL});
});
}
}catch(err) {
return null;
}
}
let serverList = [];
let serverTempList = [];
for(const [key , value] of Object.entries(servers)) {
let video = await getVideoURL(value.iframe)
serverTempList.push(video);
}
Array.from({length: serverTempList.length} , (v , k) =>{
let name = serverNames[k];
let video = serverTempList[k];
serverList.push({server: name, video: video});
});
serverList = serverList.filter(function( obj ) {
return obj.server !== 'Xtreme S';
});
return await Promise.all(serverList);
}
async function getVideoURL(url) {
let options = { scrapy: true }
const $ = await homgot(url, options);
const video = $('video');
if(video.length){
const src = $(video).find('source').attr('src');
return src || null;
}
else{
const scripts = $('script');
const l = global;
const ll = String;
const $script2 = $(scripts[1]).html();
eval($script2);
return l.ss || null;
}
}
const jkanimeInfo = async (id) => {
let poster = ""
let banner = ""
let synopsis = ""
let rating = ""
let debut = ""
let type = ""
let $
try {
let options = { scrapy: true }
$ = await homgot(`${BASE_JKANIME}${id}`, options);
const animeExtraInfo = [];
const genres = [];
let listByEps;
poster = $('div[id="container"] div.serie-info div.cap-portada')[0].children[1].attribs.src;
banner = $('div[id="container"] div.serie-info div.cap-portada')[0].children[1].attribs.src;
synopsis = $('div[id="container"] div.serie-info div.sinopsis-box p')[0].children[1].data;
rating = "Sin calificación"
debut = $('div[id="container"] div.serie-info div.info-content div')[6].children[3].children[0].children[0].data;
type = $('div[id="container"] div.serie-info div.info-content div')[0].children[3].children[0].data
animeExtraInfo.push({
poster: poster,
banner: banner,
synopsis: synopsis,
rating: rating,
debut: debut,
type: type,
})
let rawGenres = $('div[id="container"] div.serie-info div.info-content div')[1].children[3].children
for (let i = 0; i <= rawGenres.length -1; i++) {
if (rawGenres[i].name === 'a') {
const genre = rawGenres[i].children[0].data
genres.push(genre)
}
}
let nextEpisodeDate
let rawNextEpisode = $('div[id="container"] div.left-container div[id="proxep"] p')[0]
if (rawNextEpisode === undefined) {
nextEpisodeDate = null
} else {
if (rawNextEpisode.children[1].data === ' ') {
nextEpisodeDate = null
} else {
nextEpisodeDate = rawNextEpisode
}
}
const eps_temp_list = [];
let episodes_aired = '';
$('div#container div.left-container div.navigation a').each(async(index , element) => {
const $element = $(element);
const total_eps = $element.text();
eps_temp_list.push(total_eps);
})
try{episodes_aired = eps_temp_list[0].split('-')[1].trim();}catch(err){}
const animeListEps = [{nextEpisodeDate: nextEpisodeDate}];
for (let i = 0; i <= episodes_aired; i++) {
let episode = i;
let animeId = $('div[id="container"] div.content-box div[id="episodes-content"]')[0].children[1].children[3].attribs.src.split('/')[7].split('.jpg')[0];
let imagePreview = $('div[id="container"] div.content-box div[id="episodes-content"]')[0].children[1].children[3].attribs.src
let link = `${animeId}/${episode}`
animeListEps.push({
episode: episode,
id: link,
imagePreview: imagePreview
})
}
listByEps = animeListEps;
return {listByEps, genres, animeExtraInfo};
} catch (err) {
console.error(err)
}
};
const animeflvInfo = async (id, index) => {
let poster = ""
let banner = ""
let synopsis = ""
let rating = ""
let debut = ""
let type = ""
let $
try {
let options = { scrapy: true }
$ = await homgot(`${BASE_ANIMEFLV}anime/${id}`, options);
const scripts = $('script');
const anime_info_ids = [];
const anime_eps_data = [];
const animeExtraInfo = [];
const genres = [];
let listByEps;
poster = `${BASE_ANIMEFLV}` + $('body div div div div div aside div.AnimeCover div.Image figure img').attr('src')
banner = poster.replace('covers', 'banners').trim();
synopsis = $('body div div div div div main section div.Description p').text().trim();
rating = $('body div div div.Ficha.fchlt div.Container div.vtshr div.Votes span#votes_prmd').text();
debut = $('body div.Wrapper div.Body div div.Container div.BX.Row.BFluid.Sp20 aside.SidebarA.BFixed p.AnmStts').text();
type = $('body div.Wrapper div.Body div div.Ficha.fchlt div.Container span.Type').text()
animeExtraInfo.push({
poster: poster,
banner: banner,
synopsis: synopsis,
rating: rating,
debut: debut,
type: type,
})
$('main.Main section.WdgtCn nav.Nvgnrs a').each((index, element) => {
const $element = $(element);
const genre = $element.attr('href').split('=')[1] || null;
genres.push(genre);
});
Array.from({length: scripts.length}, (v, k) => {
const $script = $(scripts[k]);
const contents = $script.html();
if ((contents || '').includes('var anime_info = [')) {
let anime_info = contents.split('var anime_info = ')[1].split(';\n')[0];
let dat_anime_info = JSON.parse(anime_info);
anime_info_ids.push(dat_anime_info);
}
if ((contents || '').includes('var episodes = [')) {
let episodes = contents.split('var episodes = ')[1].split(';')[0];
let eps_data = JSON.parse(episodes)
anime_eps_data.push(eps_data);
}
});
const AnimeThumbnailsId = index;
const animeId = id;
let nextEpisodeDate
if (anime_info_ids.length > 0) {
if (anime_info_ids[0].length === 4) {
nextEpisodeDate = anime_info_ids[0][3]
} else {
nextEpisodeDate = null
}
}
const amimeTempList = [];
for (const [key, value] of Object.entries(anime_eps_data)) {
let episode = anime_eps_data[key].map(x => x[0]);
let episodeId = anime_eps_data[key].map(x => x[1]);
amimeTempList.push(episode, episodeId);
}
const animeListEps = [{nextEpisodeDate: nextEpisodeDate}];
Array.from({length: amimeTempList[1].length}, (v, k) => {
let data = amimeTempList.map(x => x[k]);
let episode = data[0];
let id = data[1];
let imagePreview = `${BASE_EPISODE_IMG_URL}${AnimeThumbnailsId}/${episode}/th_3.jpg`
let link = `${id}/${animeId}-${episode}`
animeListEps.push({
episode: episode,
id: link,
imagePreview: imagePreview
})
})
listByEps = animeListEps;
return {listByEps, genres, animeExtraInfo};
} catch (err) {
console.error(err)
}
};
const getAnimeCharacters = async (title) => {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}/characters_staff`, options);
let body = data.characters;
if (typeof body === 'undefined') return null;
const charactersId = body.map(doc => {
return doc.mal_id
})
const charactersNames = body.map(doc => {
return doc.name;
});
const charactersImages = body.map(doc => {
return doc.image_url
});
let characters = [];
Array.from({length: charactersNames.length}, (v, k) => {
const id = charactersId[k];
let name = charactersNames[k];
let characterImg = charactersImages[k];
characters.push({
id: id,
name: name,
image: characterImg
});
});
return Promise.all(characters);
};
const getAnimeVideoPromo = async (title) => {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}/videos`, options);
const body = data.promo;
const promises = [];
body.map(doc => {
promises.push({
title: doc.title,
previewImage: doc.image_url,
videoURL: doc.video_url
});
});
return Promise.all(promises);
};
const animeExtraInfo = async (title) => {
let options = { parse: true }
const res = await homgot(`${BASE_JIKAN}search/anime?q=${title}`, options);
const matchAnime = res.results.filter(x => x.title === title);
const malId = matchAnime[0].mal_id;
if (typeof matchAnime[0].mal_id === 'undefined') return null;
const data = await homgot(`${BASE_JIKAN}anime/${malId}`, options);
const body = Array(data);
const promises = [];
body.map(doc => {
let airDay = {
'mondays': 'Lunes',
'monday': 'Lunes',
'tuesdays': 'Martes',
'tuesday': 'Martes',
'wednesdays': 'Miércoles',
'wednesday': 'Miércoles',
'thursdays': 'Jueves',
'thursday': 'Jueves',
'fridays': 'Viernes',
'friday': 'Viernes',
'saturdays': 'Sábados',
'saturday': 'Sábados',
'sundays': 'Domingos',
'sunday': 'Domingos',
'default': 'Sin emisión'
};
let broadcast
if (doc.broadcast === null) {
broadcast = null
} else {
broadcast = airDay[doc.broadcast.split('at')[0].replace(" ", "").toLowerCase()]
}
promises.push({
titleJapanese: doc.title_japanese,
source: doc.source,
totalEpisodes: doc.episodes,
aired: {
from: doc.aired.from,
to: doc.aired.to
},
duration: doc.duration.split('per')[0],
rank: doc.rank,
broadcast: broadcast,
producers: doc.producers.map(x => x.name) || null,
licensors: doc.licensors.map(x => x.name) || null,
studios: doc.studios.map(x => x.name) || null,
openingThemes: doc.opening_themes || null,
endingThemes: doc.ending_themes || null
});
});
return Promise.all(promises);
};
const imageUrlToBase64 = async (url) => {
let img = await homgot(url)
return img.rawBody.toString('base64');
};
const helper = async () => {}
const searchAnime = async (query) => {
let $
let promises = []
const jkAnimeTitles = [
{ title: 'BNA', search: 'BNA'},
{ title: 'The God of High School', search: 'The god' }
];
let jkanime = false
let jkanimeName
for (let name in jkAnimeTitles) {
if (query === jkAnimeTitles[name].title) {
jkanime = true
jkanimeName = jkAnimeTitles[name].search
}
}
if (jkanime === false) {
let options = { scrapy: true }
$ = await homgot(`${SEARCH_URL}${query}`, options);
$('div.Container ul.ListAnimes li article').each((index, element) => {
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
let poster = $element.find('a div.Image figure img').attr('src') || $element.find('a div.Image figure img').attr('data-cfsrc');
const type = $element.find('div.Description p span.Type').text();
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: type || null,
image: await imageUrlToBase64(poster) || null
})));
})
} else {
let options = { scrapy: true }
$ = await homgot(`${JKANIME_URL}${jkanimeName}`, options);
$('.portada-box').each(function (index, element) {
const $element = $(element);
const title = $element.find('h2.portada-title a').attr('title');
const id = $element.find('a.let-link').attr('href').split('/')[3];
const poster = $element.find('a').children('img').attr('src');
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: 'Anime',
image: await imageUrlToBase64(poster) || null
})))
});
}
return Promise.all(promises);
};
const transformUrlServer = async (urlReal) => {
let res
let data
const promises = []
for (const index in urlReal) {
if (urlReal[index].server === 'amus' || urlReal[index].server === 'natsuki') {
let options = { parse: true }
res = await homgot(urlReal[index].code.replace("embed", "check"), options);
urlReal[index].code = res.file || null
urlReal[index].direct = true
} else {
urlReal[index].direct = false
}
}
urlReal.map(doc => {
promises.push({
id: doc.title.toLowerCase(),
url: doc.code,
direct: doc.direct
});
});
return promises;
}
const obtainPreviewNews = (encoded) => {
let image;
if (encoded.includes('<img title=')) {
image = encoded.substring(encoded.indexOf("<img title=\""), encoded.indexOf("\" alt")).split('src=\"')[1]
} else if (encoded.includes('<img src=')) {
image = encoded
.substring(encoded.indexOf("<img src=\""), encoded.indexOf("\" alt"))
.substring(10).replace("http", "https")
.replace("httpss", "https")
} else if (encoded.includes('<img')) {
image = encoded.split("src=")[1].split(" class=")[0].replace("\"", '').replace('\"', '')
} else if (encoded.includes('https://www.youtube.com/embed/')) {
let getSecondThumb = encoded.split('https://www.youtube.com/embed/')[1].split('?feature')[0]
image = `https://img.youtube.com/vi/${getSecondThumb}/0.jpg`
} else if (encoded.includes('https://www.dailymotion.com/')) {
let getDailymotionThumb = encoded
.substring(encoded.indexOf("\" src=\""), encoded.indexOf("\" a"))
.substring(47)
image = `https://www.dailymotion.com/thumbnail/video/${getDailymotionThumb}`
} else {
let number = Math.floor(Math.random() * 30);
image = `${BASE_ARUPPI}news/${number}.png`
}
return image;
}
const structureThemes = async (body, indv) => {
const promises = []
let themes
if (indv === true) {
themes = await getThemesData(body.themes)
promises.push({
title: body.title,
year: body.year,
themes: themes,
});
} else {
for (let i = 0; i <= body.length - 1; i++) {
themes = await getThemesData(body[i].themes)
promises.push({
title: body[i].title,
year: body[i].year,
themes: themes,
});
}
}
return promises;
};
const getThemesData = async (themes) => {
let promises = []
for (let i = 0; i <= themes.length - 1; i++) {
promises.push({
title: themes[i].name.split('"')[1] || 'Remasterización',
type: themes[i].name.split('"')[0] || 'OP/ED',
episodes: themes[i].episodes || null,
video: themes[i].link
});
}
return promises;
};
const getThemes = async (themes) => {
let promises = []
themes.map(doc => {
promises.push({
name: doc.themeName,
type: doc.themeType,
video: doc.mirror.mirrorURL
});
});
return promises;
};
const getAnimes = async () => {
let options = { parse: true }
return await homgot(`${BASE_ANIMEFLV}api/animes/list`, options);
};
const getDirectory = async () => {
let $
let promises = []
let options = { scrapy: true }
$ = await homgot(`${SEARCH_URL}`, options);
const lastPage = $('body div.Wrapper div.Container main div.NvCnAnm ul li a')[11].children[0].data
for (let i = 1; i <= lastPage; i++) {
let options = { scrapy: true }
$ = await homgot(`${SEARCH_DIRECTORY}${i}`, options);
$('div.Container ul.ListAnimes li article').each((index, element) => {
const $element = $(element);
const id = $element.find('div.Description a.Button').attr('href').slice(1);
const title = $element.find('a h3').text();
let poster = $element.find('a div.Image figure img').attr('src') || $element.find('a div.Image figure img').attr('data-cfsrc');
const type = $element.find('div.Description p span.Type').text();
promises.push(helper().then(async () => ({
id: id || null,
title: title || null,
type: type || null,
image: await imageUrlToBase64(poster) || null
})));
})
}
return Promise.all(promises);
};
module.exports = {
jkanimeInfo,
animeflvInfo,
getAnimeCharacters,
getAnimeVideoPromo,
animeExtraInfo,
imageUrlToBase64,
searchAnime,
transformUrlServer,
obtainPreviewNews,
structureThemes,
getThemes,
getAnimes,
getDirectory,
helper,
videoServersJK
}

@ -0,0 +1,54 @@
import urls from './urls';
export const obtainPreviewNews = (encoded: string) => {
let image: string;
try {
if (encoded.includes('src="https://img1.ak.crunchyroll.com/')) {
if (
encoded.split('https://img1.ak.crunchyroll.com/')[1].includes('.jpg')
) {
image = `https://img1.ak.crunchyroll.com/${
encoded.split('https://img1.ak.crunchyroll.com/')[1].split('.jpg')[0]
}.jpg`;
} else {
image = `https://img1.ak.crunchyroll.com/${
encoded.split('https://img1.ak.crunchyroll.com/')[1].split('.png')[0]
}.png`;
}
} else if (encoded.includes('<img title=')) {
image = encoded
.substring(encoded.indexOf('<img title="'), encoded.indexOf('" alt'))
.split('src="')[1];
} else if (encoded.includes('<img src=')) {
image = encoded
.substring(encoded.indexOf('<img src="'), encoded.indexOf('" alt'))
.substring(10)
.replace('http', 'https')
.replace('httpss', 'https');
} else if (encoded.includes('<img')) {
image = encoded
.split('src=')[1]
.split(' class=')[0]
.replace('"', '')
.replace('"', '');
} else if (encoded.includes('https://www.youtube.com/embed/')) {
let getSecondThumb = encoded
.split('https://www.youtube.com/embed/')[1]
.split('?feature')[0];
image = `https://img.youtube.com/vi/${getSecondThumb}/0.jpg`;
} else if (encoded.includes('https://www.dailymotion.com/')) {
let getDailymotionThumb = encoded
.substring(encoded.indexOf('" src="'), encoded.indexOf('" a'))
.substring(47);
image = `https://www.dailymotion.com/thumbnail/video/${getDailymotionThumb}`;
} else {
let number = Math.floor(Math.random() * 30);
image = `${urls.BASE_ARUPPI}news/${number}.png`;
}
return image;
} catch (err) {
console.log(err);
}
};

@ -0,0 +1,52 @@
import got from 'got';
import cheerio from 'cheerio';
import { CookieJar } from 'tough-cookie';
// @ts-ignore
import * as got_pjson from 'got/package.json'
const pjson = require('../../package.json');
const cookieJar = new CookieJar();
const aruppi_options: any = {
cookieJar,
'headers': {
'user-agent': `Aruppi-API/${pjson.version} ${got_pjson.name}/${got_pjson.version}`,
'x-client': 'aruppi-api'
},
};
interface Options {
scrapy?: boolean,
parse?: boolean,
spoof?: boolean
}
export const requestGot = async (
url: string,
options?: Options,
): Promise<any> => {
const got_options: any = {...got.defaults.options, ...aruppi_options}
if (options) {
if (options.spoof != null) {
got_options.headers["user-agent"] = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/69.0";
delete got_options.headers['x-client'];
if (!options.spoof)
got_options.headers['user-agent'] = got.defaults.options.headers['user-agent'];
} else if (process.env.ALPI_KEY && (new URL(url)).hostname.match(/\.jeluchu\.xyz$/)) {
got_options.headers['x-aruppi-key'] = process.env.ALPI_KEY;
}
if (options.scrapy) {
const response = await got(url, got_options);
return cheerio.load(response.body);
}
if (options.parse) {
got_options.responseType = 'json';
const response = await got(url, got_options);
return response.body;
}
}
const response = await got.get(url, got_options);
return response;
};

@ -0,0 +1,22 @@
import { requestGot } from './requestCall';
export const transformUrlServer = async (urlReal: any) => {
for (const data of urlReal) {
if (data.server === 'amus' || data.server === 'natsuki') {
let res = await requestGot(data.code.replace('embed', 'check'), {
parse: true,
scrapy: false,
});
data.code = res.file || null;
data.direct = true;
}
}
return urlReal.map((item: any) => {
return {
id: item.title.toLowerCase(),
url: item.code,
direct: item.direct || false,
};
});
};

@ -0,0 +1,23 @@
export default {
BASE_ARUPPI: 'https://aruppi.jeluchu.xyz/',
BASE_ANIMEFLV: 'https://www3.animeflv.net/',
BASE_MONOSCHINOS: 'https://monoschinos2.com/',
BASE_TIOANIME: 'https://tioanime.com/',
BASE_JKANIME: 'https://jkanime.net/',
BASE_ANIMEFLV_JELU: 'https://aruppi.jeluchu.xyz/apis/animeflv/v1/',
BASE_YOUTUBE: 'https://aruppi.jeluchu.xyz/api/Youtube/?channelId=',
BASE_YOUTUBE_PLAYLIST: 'https://aruppi.jeluchu.xyz/api/Youtube/playlist/?playlistId=',
BASE_JIKAN: 'https://aruppi.jeluchu.xyz/apis/jikan/v4/',
BASE_IVOOX: 'https://www.ivoox.com/podcast-anitakume_fg_f1660716_filtro_1.xml',
BASE_KUDASAI: 'https://somoskudasai.com/feed/',
BASE_RAMENPARADOS: 'https://ramenparados.com/category/noticias/anime/feed/',
BASE_CRUNCHYROLL: 'https://www.crunchyroll.com/newsrss?lang=esES',
JKANIME_SEARCH: 'https://jkanime.net/buscar/',
ANIMEFLV_SEARCH: 'https://animeflv.net/browse?',
SEARCH_DIRECTORY: 'https://animeflv.net/browse?order=title&page=',
BASE_EPISODE_IMG_URL: 'https://cdn.animeflv.net/screenshots/',
BASE_QWANT: 'https://api.qwant.com/v3/search/images?',
REDDIT_ANIMETHEMES: 'https://reddit.com/r/AnimeThemes/wiki/',
BASE_THEMEMOE: 'https://themes.moe/api/',
BASE_ARUPPI_MONOSCHINOS: 'https://aruppi.jeluchu.xyz/apis/monoschinos/',
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,70 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./" /* Concatenate and emit output to single file. */,
"outDir": "./dist" /* Redirect output structure to the directory. */,
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save