1
0
forked from 50n50/sources

Compare commits

366 Commits

Author SHA1 Message Date
Gitea Actions Bot 400cf96447 chore: update library.json 2026-06-02 11:53:00 +00:00
sobet 059bc19173 Update animeworld/animeworld.js 2026-06-02 11:52:41 +00:00
50n50 74f821afd0 Upload files to "anistream" 2026-06-01 17:18:27 +00:00
Gitea Actions Bot 6ce219db8d chore: update library.json 2026-06-01 17:13:16 +00:00
50n50 0d335470ea Update anistream/anistream.json 2026-06-01 17:12:58 +00:00
Gitea Actions Bot 0c67e8db83 chore: update library.json 2026-06-01 17:11:42 +00:00
aka paul 6307f70497 push 2026-06-01 19:11:20 +02:00
50n50 1358cbbfff Update index.json 2026-06-01 16:32:40 +00:00
Gitea Actions Bot cd001b9eab chore: update library.json 2026-06-01 16:32:04 +00:00
aka paul 02147221e8 Push 2026-06-01 18:31:41 +02:00
aka paul aa6a0dd3f9 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-05-29 17:08:22 +02:00
aka paul 61d2d36b69 fix 2026-05-29 17:08:09 +02:00
Gitea Actions Bot 8c689ff476 chore: update library.json 2026-05-29 15:07:16 +00:00
aka paul 5cf145e516 add 2026-05-29 17:06:32 +02:00
Gitea Actions Bot 8ac56b2787 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-05-27 19:44:45 +00:00
aka paul 8c448c853e add subtitles 2026-05-27 21:43:10 +02:00
Gitea Actions Bot 4062b4a7be chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-05-27 19:19:42 +00:00
aka paul 9b18dd39d6 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-05-27 21:18:21 +02:00
aka paul eb3154f765 Add Shirox 2026-05-27 21:14:19 +02:00
Gitea Actions Bot 9be224e467 chore: update library.json 2026-05-27 19:02:38 +00:00
aka paul ebfcd4d6dc Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-05-27 21:02:00 +02:00
aka paul d7146f94da Update index.json 2026-05-27 21:00:06 +02:00
Gitea Actions Bot 64ef556d75 chore: update library.json 2026-05-27 18:31:01 +00:00
aka paul 597f279e49 update 2026-05-27 20:30:23 +02:00
Gitea Actions Bot caffaabc88 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-05-10 11:35:04 +00:00
aka paul 4fbb2bc138 jythy 2026-05-10 13:34:03 +02:00
Gitea Actions Bot 01b25ec53d chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-05-10 11:33:26 +00:00
aka paul c4fccf85bd fix 2026-05-10 13:32:22 +02:00
Gitea Actions Bot aa56034bf7 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-05-10 10:59:30 +00:00
aka paul 6400b9c508 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-05-10 12:58:28 +02:00
aka paul 92fa874883 update 2026-05-10 12:58:17 +02:00
Gitea Actions Bot fab874dbc5 chore: update library.json 2026-05-10 10:39:24 +00:00
aka paul c2efefaf0e Update animekai/animekai.json 2026-05-10 10:38:23 +00:00
aka paul 11d6986232 Update animekai/animekai.js 2026-05-10 10:38:09 +00:00
Gitea Actions Bot 4bbde44022 chore: update library.json 2026-05-08 16:24:42 +00:00
aka paul 7cced4311b removal 2026-05-08 18:24:05 +02:00
aka paul e324070a32 Fix subtitles 2026-05-05 17:11:07 +02:00
Gitea Actions Bot 7b038968f3 chore: update library.json 2026-05-05 14:11:33 +00:00
sobet 837659590b Update streamingunity/streamingunity.json 2026-05-05 14:10:37 +00:00
sobet db7958ff83 Update streamingunity/streamingunity.js 2026-05-05 14:10:20 +00:00
Gitea Actions Bot b9472ac7a2 chore: update library.json 2026-04-30 15:14:13 +00:00
aka paul 6d987a6242 fix 2026-04-30 17:13:44 +02:00
aka paul 5a4ff66443 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-30 16:52:54 +02:00
aka paul 286041b093 Remove proxy and fix subtitles 2026-04-30 16:52:45 +02:00
Gitea Actions Bot d1e170cfd4 chore: update library.json 2026-04-30 14:44:53 +00:00
aka paul d55b0dbcfc Fix subtitles videasy 2026-04-30 16:44:23 +02:00
Gitea Actions Bot af610ae9c5 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-27 18:38:55 +00:00
aka paul 3c756e2890 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-27 20:37:28 +02:00
aka paul 5a83892bc1 b, 2026-04-27 20:37:18 +02:00
Gitea Actions Bot a651afa1ba chore: update library.json 2026-04-27 18:36:47 +00:00
aka paul 951663b6d0 update too 2026-04-27 20:36:11 +02:00
aka paul 7b03037a59 update 2026-04-27 20:35:54 +02:00
Gitea Actions Bot 388f6f4991 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-24 14:28:45 +00:00
aka paul b6d9e70dc7 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-24 16:27:30 +02:00
aka paul 3b720ebd25 Add modules by emp0ry 2026-04-24 16:27:23 +02:00
Gitea Actions Bot efa598937b chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-23 20:16:24 +00:00
aka paul 6f0a381e77 Update ashi.json 2026-04-23 22:15:00 +02:00
aka paul db52efdb5b Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-23 22:13:12 +02:00
aka paul f7e4efd254 fix 2026-04-23 22:12:20 +02:00
Gitea Actions Bot a853ec6aee chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-23 20:11:39 +00:00
aka paul c10e3b71b7 fix 2026-04-23 22:09:43 +02:00
aka paul 84a8284a50 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-23 22:07:41 +02:00
aka paul cbabd0643e fix 2026-04-23 22:07:29 +02:00
Gitea Actions Bot 7a2c05abb6 chore: update library.json 2026-04-23 20:01:04 +00:00
aka paul 5a8c5cbb62 fix 2026-04-23 22:00:27 +02:00
Gitea Actions Bot 3d44fa0bb2 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-23 14:28:35 +00:00
aka paul 1098637c15 Update gojowtf/gojowtf.json 2026-04-23 14:26:17 +00:00
aka paul 7d4e12986b Update gojowtf/gojowtf.js 2026-04-23 14:26:02 +00:00
Gitea Actions Bot 904a711918 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-23 14:11:19 +00:00
aka paul 2c2d6b05a1 Update gojowtf/gojowtf.json 2026-04-23 14:10:06 +00:00
aka paul 72cb66f38a Update gojowtf/gojowtf.json 2026-04-23 14:09:16 +00:00
Gitea Actions Bot d009e3ed98 chore: update library.json 2026-04-23 14:08:58 +00:00
aka paul c2b6118abc Update gojowtf/gojowtf.js 2026-04-23 14:08:30 +00:00
Gitea Actions Bot 9546ddee03 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-18 18:33:10 +00:00
aka paul 03f3bc6ffc Update dora-video/dora-video.json 2026-04-18 18:31:25 +00:00
aka paul 17b3606d4f Update dora-video/dora-video.js 2026-04-18 18:31:13 +00:00
Gitea Actions Bot 5605f80b0f chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-17 18:48:51 +00:00
aka paul 9643b4a963 Update animedefenders/animedefenders.json 2026-04-17 18:47:02 +00:00
aka paul 58788a8cf2 Update animedefenders/animedefenders.js 2026-04-17 18:46:51 +00:00
Gitea Actions Bot 4ad76c5519 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-17 12:16:49 +00:00
aka paul d56e8784f6 Update animedefenders/animedefenders.json 2026-04-17 12:14:54 +00:00
aka paul c3dcbceefb Update animedefenders/animedefenders.js 2026-04-17 12:14:39 +00:00
Gitea Actions Bot 29c1eeb15e chore: update library.json 2026-04-16 16:09:55 +00:00
aka paul e5f71d3e0f Add .gitea/workflows/library.yml 2026-04-16 16:09:28 +00:00
Gitea Actions Bot b3c073a4f6 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-13 16:01:46 +00:00
aka paul 81ef2f9398 Add Hiyoku value 2026-04-13 18:00:46 +02:00
aka paul 1ced6f7e76 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-04-11 22:33:18 +02:00
aka paul 85e42df413 add Tsumi 2026-04-11 22:32:51 +02:00
Gitea Actions Bot 79990ff562 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-11 20:29:23 +00:00
aka paul ab217a7b5a add Anymex 2026-04-11 22:28:31 +02:00
Gitea Actions Bot b1675936a6 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-10 13:38:03 +00:00
aka paul dd92aea52e Update animepahe/animepahe.json 2026-04-10 13:37:18 +00:00
aka paul 284414a994 Update animepahe/animepahe.js 2026-04-10 13:37:08 +00:00
Gitea Actions Bot ea29804e86 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-04-02 21:21:39 +00:00
aka paul 6f9b7112ec Update animepahe/animepahe.json 2026-04-02 21:21:02 +00:00
aka paul cddda574cc Update animepahe/animepahe.js 2026-04-02 21:20:18 +00:00
aka paul 98ae0a541b Update 2026-03-28 13:10:11 +01:00
aka paul 5e26b3b171 Fix 2026-03-28 13:07:42 +01:00
Gitea Actions Bot 352b88b030 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-25 17:21:16 +00:00
aka paul 441dbbd9a2 Update animelib/animelib.json 2026-03-25 17:20:35 +00:00
aka paul 611cbc8e1a Update animelib/animelib.js 2026-03-25 17:20:22 +00:00
Gitea Actions Bot 2fc80e1206 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-25 16:57:57 +00:00
aka paul e4345774bd Update ashi/ashi.json 2026-03-25 16:57:10 +00:00
aka paul 6acea93a3c Update ashi/ashi.js 2026-03-25 16:56:54 +00:00
aka paul 58f398383e Update animekai/dub/animekai.json 2026-03-25 16:51:06 +00:00
aka paul e2bc89511c Update animekai/dub/animekai.js 2026-03-25 16:50:55 +00:00
aka paul 19ffb8254b Update animekai/hardsub/animekai.json 2026-03-25 16:49:16 +00:00
aka paul 484e9b20e4 Update animekai/hardsub/animekai.js 2026-03-25 16:49:05 +00:00
Gitea Actions Bot 2e823488d5 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-25 16:47:19 +00:00
aka paul f700830e2f Update animekai/animekai.json 2026-03-25 16:46:27 +00:00
aka paul 14f0aea33d Update animekai/animekai.js 2026-03-25 16:45:27 +00:00
Gitea Actions Bot 35986ceb2b chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-14 23:43:00 +00:00
aka paul 8a7a8f3a6a Update hianime/hianime.json 2026-03-14 23:42:20 +00:00
aka paul df457e7ade Update hianime/hianime.js 2026-03-14 23:42:03 +00:00
Gitea Actions Bot f8a332fdb7 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-14 21:13:56 +00:00
aka paul aff574469c Update ashi/ashi.json 2026-03-14 21:13:12 +00:00
aka paul 71e8747998 Update ashi/ashi.js 2026-03-14 21:04:25 +00:00
aka paul b856529fce Update turkish123/turkish123.json 2026-03-14 11:41:42 +00:00
Gitea Actions Bot d09299eea2 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-14 11:41:15 +00:00
aka paul cbdac19e16 Update turkish123/turkish123.json 2026-03-14 11:40:24 +00:00
aka paul 78992bbd25 Update turkish123/turkish123.js 2026-03-14 11:40:09 +00:00
aka paul a32db404cd Update lncrawler/lncrawler.js 2026-03-14 11:34:14 +00:00
aka paul 207dfbcac9 Update lncrawler/lncrawler.js 2026-03-14 11:30:24 +00:00
aka paul 0149e192d4 Update lncrawler/lncrawler.js 2026-03-14 11:28:53 +00:00
aka paul 0c4b055562 push 2026-03-13 23:56:02 +01:00
Gitea Actions Bot 6f51c9a900 chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-09 16:02:19 +00:00
aka paul d4c72a54ee add support 2026-03-09 17:01:36 +01:00
aka paul cfac44b405 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-03-09 16:26:51 +01:00
aka paul e2413ee76c sort 2026-03-09 16:26:43 +01:00
aka paul c8092410a5 Delete asset.png 2026-03-09 15:26:25 +00:00
aka paul c477fb7bb0 sort 2026-03-09 16:25:36 +01:00
aka paul 2a3817f04f sort 2026-03-09 16:24:48 +01:00
Gitea Actions Bot cb3aa79a4b chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-09 15:20:20 +00:00
aka paul a9963bf9a1 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-03-09 16:19:37 +01:00
aka paul 68850c048e should be alright 2026-03-09 16:18:24 +01:00
Gitea Actions Bot b00be4f74d chore: sync version strings from individual JSON files
Automatically updated version fields in index.json to match their respective source files

Generated by sync-versions workflow
2026-03-09 13:51:40 +00:00
aka paul a88ed329a5 Merge branch 'main' of https://git.luna-app.eu/50n50/sources 2026-03-09 14:49:08 +01:00
aka paul 5c34fb248b test 2026-03-09 14:48:59 +01:00
aka paul 1c0d5e9b09 Merge pull request 'Moved AniWorld modules to Cufiy/sora-modules repo' (#10) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/10
2026-03-09 00:05:22 +00:00
Cufiy a48b98ead1 merge upstream 2026-03-09 00:01:25 +00:00
JMcrafter26 6e8aabff02 Moved AniWorld modules to Cufiy/sora-modules repo 2026-03-09 01:01:09 +01:00
aka paul 8f7d0c3b10 Update vidfast/vidfast.js 2026-03-08 20:21:41 +00:00
aka paul 6b97575731 Update animekai/dub/animekai.json 2026-03-08 19:05:08 +00:00
aka paul a7ccb640d4 Update animekai/dub/animekai.js 2026-03-08 19:04:58 +00:00
aka paul b6a8c394f6 Update animekai/arabic/animekai.json 2026-03-08 19:04:13 +00:00
aka paul d160cb3e50 Update animekai/arabic/animekai.js 2026-03-08 19:04:03 +00:00
aka paul ccbe5d5233 Update animekai/dub/animekai.json 2026-03-08 18:55:11 +00:00
aka paul 8318259db4 Update animekai/dub/animekai.js 2026-03-08 18:55:01 +00:00
aka paul 108227235c Update comet/comet.json 2026-03-08 17:34:57 +00:00
aka paul 2ea1a3f27d Update comet/comet.js 2026-03-08 17:33:06 +00:00
aka paul f94d9548a6 Update comet/comet.js 2026-03-08 17:31:20 +00:00
aka paul 1f45c6d93c test 2026-03-08 17:26:48 +01:00
aka paul 20636e80cc test 2026-03-08 17:23:08 +01:00
aka paul 7c53568b2f test generic 2026-03-08 17:21:06 +01:00
aka paul ee0fa333ce try docker 2026-03-08 17:20:30 +01:00
aka paul b237d707e4 fix action 2026-03-08 17:18:47 +01:00
aka paul 2e85203aed push action 2026-03-08 17:16:39 +01:00
aka paul b8517d88b5 Update mangafreak/mangafreak.json 2026-03-07 20:57:41 +00:00
aka paul 5df41e8fc0 Update mangafreak/mangafreak.js 2026-03-07 20:57:15 +00:00
aka paul 7362a5ddad Update mangafire/mangafire.json 2026-03-06 12:12:54 +00:00
aka paul 083098d20e Update mangafire/mangafire.js 2026-03-05 21:20:30 +00:00
aka paul bf095bc654 Update comix/comix.js 2026-03-03 18:28:42 +00:00
aka paul 007c21a192 Update comix/comix.js 2026-03-03 18:20:41 +00:00
aka paul 9e550f6517 Update index.json 2026-03-02 16:53:08 +00:00
aka paul 5fbae69eac Add animekai/animekai.json 2026-03-02 14:52:28 +00:00
aka paul 892c83e5b0 Add animekai/animekai.js 2026-03-02 14:51:42 +00:00
aka paul 81af6b15df Update videasy/videasy.json 2026-03-01 16:58:01 +00:00
aka paul e2d0051863 Update videasy/videasy.js 2026-03-01 16:57:49 +00:00
aka paul e7c8fc8e68 Update vidfast/vidfast.json 2026-03-01 12:33:23 +00:00
aka paul fd1bfed288 Update vidfast/vidfast.js 2026-03-01 12:32:38 +00:00
aka paul d34df3a2f1 Update animeworld/animeworld.json 2026-03-01 11:58:07 +00:00
aka paul e1eb629ce9 Update animeworld/animeworld.json 2026-03-01 11:57:13 +00:00
aka paul 2250aca5d2 Update animeworld/animeworld.js 2026-03-01 11:57:04 +00:00
aka paul 3c2b21f4d3 Update streamingunity/streamingunity.json 2026-02-18 20:47:20 +00:00
50n50 7de95ae00a update 2026-02-18 21:44:10 +01:00
aka paul dc57820a54 Update yummyanime/yummyanime.json 2026-02-17 15:16:02 +00:00
aka paul f3d337eb8d Add yummyanime/yummyanime.js 2026-02-17 15:15:36 +00:00
aka paul 8dc4461655 Delete yummyanime/yummyanime.js 2026-02-17 15:15:27 +00:00
aka paul e8f11a3eb8 Update animevost/animevost.json 2026-02-15 15:13:35 +00:00
aka paul 41e2330293 Update yummyanime/yummyanime.json 2026-02-15 15:13:12 +00:00
aka paul 10d57783dd Update yummyanime/yummyanime.js 2026-02-15 15:12:58 +00:00
aka paul 984881911e Update yummyanime/yummyanime.js 2026-02-14 17:46:48 +00:00
aka paul 76c135dab9 Add yummyanime/yummyanime.json 2026-02-14 17:29:15 +00:00
aka paul 92fec8ace8 Add yummyanime/yummyanime.js 2026-02-14 17:28:37 +00:00
aka paul c2f7b20f36 Update streamingunity/streamingunity.json 2026-02-13 20:52:21 +00:00
aka paul 5a0934e1bf Update streamingunity/streamingunity.js 2026-02-13 20:52:02 +00:00
aka paul be3ad0e7e0 Update animedefenders/animedefenders.json 2026-02-09 14:28:01 +00:00
aka paul 9c21adde0f Update animedefenders/animedefenders.js 2026-02-09 14:27:49 +00:00
aka paul 4086596664 Update aniliberty/aniliberty.json 2026-02-09 13:42:51 +00:00
aka paul 38abfb0145 Update aniliberty/aniliberty.js 2026-02-09 13:42:08 +00:00
aka paul 0b31d1a086 Update kickassanimes/kickassanimes.json 2026-02-08 16:00:09 +00:00
aka paul b6ebc52403 Update kickassanimes/kickassanimes.js 2026-02-08 15:59:39 +00:00
aka paul 3e0ffb51d0 Update index.json 2026-02-07 15:12:14 +00:00
aka paul b10b3435ef Update 123anime/123anime.json 2026-02-07 15:10:43 +00:00
aka paul 74df9103f7 Add 123anime/123anime.json 2026-02-07 15:09:10 +00:00
aka paul 494f820496 Add 123anime/123anime.js 2026-02-07 15:06:26 +00:00
aka paul ecc07a4c8c Update animedefenders/animedefenders.json 2026-02-06 17:22:39 +00:00
aka paul 9f36faa961 Update animedefenders/animedefenders.js 2026-02-06 17:22:26 +00:00
aka paul 8c0d6a9c3d Merge pull request 'fix: s.to module - add base URL to relative links and images, update version to 0.4.2' (#9) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/9
2026-02-06 00:22:31 +00:00
JMcrafter26 eb9ae7f6ed fix: s.to module - add base URL to relative links and images, update version to 0.4.2 2026-02-06 01:12:02 +01:00
aka paul d4ba0bca7f Update jiao/jiao.js 2026-02-04 20:34:07 +00:00
aka paul fc26da62d1 Add jiao/jiao.js 2026-02-04 20:19:44 +00:00
aka paul 534c66f496 Add jiao/jiao.json 2026-02-04 20:19:25 +00:00
aka paul 1899c113e6 Update animelib/animelib.json 2026-02-04 18:50:43 +00:00
aka paul 879a962872 Update animelib/animelib.js 2026-02-04 18:50:33 +00:00
aka paul 34dc12f3c7 Update animelib/animelib.json 2026-02-04 18:17:28 +00:00
aka paul b9dd2c370f Update animelib/animelib.js 2026-02-04 18:17:14 +00:00
aka paul 85b2c8c73a Merge pull request 'fix: Improve movie link detection' (#8) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/8
2026-01-30 22:58:36 +00:00
Cufiy c46815ca85 merge upstream 2026-01-30 22:51:52 +00:00
JMcrafter26 554e323dde fix: Improve movie link detection 2026-01-30 23:51:26 +01:00
aka paul 0d5b0b588a Update anime-sama/anime-sama.json 2026-01-30 16:52:09 +00:00
aka paul 6661660310 Update anime-sama/anime-sama.js 2026-01-30 16:52:00 +00:00
aka paul 7d4d3f90e9 Merge pull request 'fix: Update s.to source to support new site structure and improve episode extraction' (#7) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/7
2026-01-29 21:56:02 +00:00
JMcrafter26 7bba0e50f5 refactor: Remove commented-out HTML parsing code 2026-01-29 22:51:42 +01:00
JMcrafter26 484bd2dbc2 fix: Update s.to source to support new site structure and improve episode extraction 2026-01-29 22:44:03 +01:00
aka paul 7824ca4508 Update hicine/hicine.json 2026-01-29 16:16:00 +00:00
aka paul fa3a84eb4a Add hicine/hicine.json 2026-01-29 16:14:50 +00:00
aka paul 1b3c5ab3c6 Add hicine/hicine.js 2026-01-29 16:13:38 +00:00
aka paul 85328a8b5c Update verseriesonline/verseriesonline.json 2026-01-29 15:16:33 +00:00
aka paul 53cedeb8da Update verseriesonline/verseriesonline.js 2026-01-29 15:16:22 +00:00
aka paul 44484f82c9 Update ashi/ashi.json 2026-01-29 14:51:43 +00:00
aka paul 10d6691a07 Update ashi/ashi.js 2026-01-29 14:51:33 +00:00
aka paul 243bf022b2 Update 1movies/1movies.json 2026-01-29 14:49:57 +00:00
aka paul aa11a6811d Update 1movies/1movies.js 2026-01-29 14:49:27 +00:00
aka paul 3c48260b8d Update animekai/hardsub/animekai.json 2026-01-29 14:48:06 +00:00
aka paul 1d011529f9 Update animekai/hardsub/animekai.js 2026-01-29 14:47:54 +00:00
aka paul d0bae7b61c Update animekai/dub/animekai.json 2026-01-29 14:47:23 +00:00
aka paul 2baadb69de Update animekai/dub/animekai.js 2026-01-29 14:46:35 +00:00
aka paul 25456bb152 Update animekai/dub/animekai.js 2026-01-29 14:44:47 +00:00
aka paul df16c09601 Update ashi/ashi.json 2026-01-29 14:09:58 +00:00
aka paul 90277d5789 Update ashi/ashi.js 2026-01-29 14:08:17 +00:00
aka paul a99e9a0a36 Update 1movies/1movies.json 2026-01-29 14:03:50 +00:00
aka paul 2049f57dc3 Update 1movies/1movies.js 2026-01-29 14:03:36 +00:00
aka paul 923467c19a Update animekai/dub/animekai.json 2026-01-29 13:54:23 +00:00
aka paul 866685c31d Update animekai/dub/animekai.js 2026-01-29 13:54:10 +00:00
aka paul fa1dd967eb Update animekai/hardsub/animekai.json 2026-01-29 13:53:17 +00:00
aka paul 58526a1c72 Update animekai/hardsub/animekai.js 2026-01-29 13:53:03 +00:00
aka paul 96e4a65b5a Update streamingunity/streamingunity.json 2026-01-27 15:47:39 +00:00
aka paul e81bffe073 Update streamingunity/streamingunity.json 2026-01-27 15:45:28 +00:00
aka paul edaf38b50e Update streamingunity/streamingunity.js 2026-01-27 15:45:14 +00:00
aka paul be4616bfca Update tidal/tidal.json 2026-01-23 20:18:09 +00:00
aka paul e0436ccea9 Update tidal/tidal.js 2026-01-23 20:16:29 +00:00
aka paul 887e32bae7 Update tidal/tidal.js 2026-01-23 20:13:04 +00:00
aka paul 1cd2eabe65 Update anicrush/anicrush.js 2026-01-17 23:34:17 +00:00
aka paul fae1acab94 Update anicrush/anicrush.json 2026-01-17 23:19:20 +00:00
aka paul d35b83e61d Update anicrush/anicrush.js 2026-01-17 23:19:08 +00:00
aka paul 925c657e8e Update dev/dev.js 2026-01-17 20:06:55 +00:00
aka paul ab785225d2 Update dev/dev.js 2026-01-17 19:25:05 +00:00
aka paul e7e7b98445 Update dev/dev.js 2026-01-17 19:23:53 +00:00
aka paul 613d297a79 Update dev/dev.js 2026-01-17 19:22:41 +00:00
aka paul c287215dfb Update dev/dev.js 2026-01-17 19:18:40 +00:00
aka paul 38252cef80 Merge pull request 'Remove vidmoly because it slows down the process and often fails to load.' (#6) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/6
2026-01-16 17:55:54 +00:00
JMcrafter26 3d67182e5b Remove vidmoly because it slows down the process and often fails to load. 2026-01-16 01:12:30 +01:00
50n50 00b1292625 update mangas 2026-01-14 00:07:39 +01:00
aka paul 6df3f100cc Update weebcentral/weebcentral.js 2026-01-09 21:18:38 +00:00
aka paul 75536650ac Add weebcentral/weebcentral.json 2026-01-09 21:12:38 +00:00
aka paul 4ecc55e778 Add weebcentral/weebcentral.js 2026-01-09 21:10:52 +00:00
aka paul 09109d880c Update mangadex/mangadex.json 2026-01-09 20:49:07 +00:00
aka paul 5378fd54bc Add mangadex/mangadex.json 2026-01-09 20:47:45 +00:00
aka paul 4cd946aa4a Add mangadex/mangadex.js 2026-01-09 20:44:54 +00:00
aka paul f08ad16a56 Update index.json 2026-01-07 16:17:00 +00:00
aka paul 05fa125880 Update kickassanimes/kickassanimes.js 2026-01-07 16:10:18 +00:00
aka paul 26d94d6d65 Update index.json 2026-01-06 21:52:21 +00:00
aka paul 0456891ac1 Add tokyoinsider/tokyoinsider.json 2026-01-06 21:42:52 +00:00
aka paul 915b9c93e8 Add tokyoinsider/tokyoinsider.js 2026-01-06 21:39:10 +00:00
aka paul 8a06300b26 Update anime-sama/anime-sama.json 2026-01-06 20:27:50 +00:00
aka paul 6ec67bcfb8 Update anime-sama/anime-sama.js 2026-01-06 20:27:33 +00:00
aka paul cafc9dea38 Update senshi/senshi.js 2026-01-06 20:12:26 +00:00
aka paul 1569479457 Update senshi/senshi.js 2026-01-06 20:08:58 +00:00
aka paul 3d8d90c33e Add senshi/senshi.json 2026-01-06 20:02:10 +00:00
aka paul 7296e211b7 Add senshi/senshi.js 2026-01-06 19:56:26 +00:00
aka paul 6435ab818b Update README.md 2026-01-06 16:44:06 +00:00
aka paul ef7d8b4485 Update README.md 2026-01-06 16:41:10 +00:00
aka paul 0d2111dba1 Update README.md 2026-01-06 16:36:16 +00:00
aka paul 51b4227631 Update kickassanimes/kickassanimes.js 2026-01-06 15:33:05 +00:00
aka paul a2df765da5 Update kickassanimes/kickassanimes.js 2026-01-06 15:27:30 +00:00
aka paul faf20a23f7 Add kickassanimes/kickassanimes.json 2026-01-06 15:22:28 +00:00
aka paul 4f17b819f3 Add kickassanimes/kickassanimes.js 2026-01-06 15:19:54 +00:00
aka paul a713e7ec09 Update index.json 2026-01-06 14:45:18 +00:00
aka paul 100d6dabbb Update animez/animez.js 2026-01-06 14:40:41 +00:00
aka paul cc1733742e Add animez/animez.json 2026-01-06 14:28:11 +00:00
aka paul bd16389dc8 Add animez/animez.js 2026-01-06 14:25:25 +00:00
aka paul a4269d7b8b Update index.json 2026-01-04 19:22:10 +00:00
aka paul b49fcac090 Update animegg/animegg.js 2026-01-04 19:20:58 +00:00
aka paul b19d0f23b7 Add animegg/animegg.json 2026-01-04 19:18:46 +00:00
aka paul 44ca65cf6c Add animegg/animegg.js 2026-01-04 19:15:29 +00:00
aka paul c259f310ed Update index.json 2026-01-04 18:55:19 +00:00
aka paul fe5c007bb9 Update anicrush/anicrush.json 2026-01-04 18:53:37 +00:00
aka paul c5e06aa55c Add anicrush/anicrush.json 2026-01-04 18:51:10 +00:00
aka paul c686cfbab3 Add anicrush/anicrush.js 2026-01-04 18:49:58 +00:00
aka paul a32c786156 Update animepahe/animepahe.json 2026-01-04 17:10:17 +00:00
aka paul 0b00de663c Update animepahe/animepahe.js 2026-01-04 17:08:27 +00:00
aka paul e003f58b44 Update index.json 2026-01-03 20:51:43 +00:00
aka paul b9eb0166cc Update animepahe/animepahe.js 2026-01-03 20:44:29 +00:00
aka paul 8d10fefb7e Update animepahe/animepahe.js 2026-01-03 20:42:24 +00:00
aka paul 0c9db9005f Update animepahe/animepahe.js 2026-01-03 20:32:48 +00:00
aka paul dc859c0dbf Update animepahe/animepahe.js 2026-01-03 20:31:12 +00:00
aka paul e90e56a163 Update animepahe/animepahe.js 2026-01-03 20:29:49 +00:00
aka paul fbbed00c89 Update animepahe/animepahe.js 2026-01-03 20:28:25 +00:00
aka paul 1e56c7c785 Update animepahe/animepahe.js 2026-01-03 20:25:30 +00:00
aka paul e2ef7a93ec Update animepahe/animepahe.js 2026-01-03 20:23:28 +00:00
aka paul c76c6ccfb2 Update animepahe/animepahe.js 2026-01-03 20:15:35 +00:00
aka paul b29f9b9336 Update animepahe/animepahe.js 2026-01-03 20:07:40 +00:00
aka paul 9287773e75 Update animepahe/animepahe.js 2026-01-03 19:58:57 +00:00
aka paul 2bd6ed54a1 Add animepahe/animepahe.json 2026-01-03 19:54:08 +00:00
aka paul 007dedb72c Add animepahe/animepahe.js 2026-01-03 19:51:40 +00:00
aka paul d4a687b5bc Merge pull request 'Improve AniWorld module and update GlobalExtractor' (#5) from Cufiy/sources-fork:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/5
2026-01-03 19:10:55 +00:00
Cufiy 773bf76c33 Merge branch 'main' into main 2026-01-03 18:55:36 +00:00
aka paul 5442db82c3 Update dev/dev.js 2026-01-03 18:49:35 +00:00
aka paul 35bb4daaf2 Update dev/dev.js 2026-01-03 18:47:53 +00:00
JMcrafter26 92f3a20556 Bump versions to 0.3.17 for English and German Dubs; update global extractor to version 1.2.0 with enhanced stream URL extraction and new extractor functions for oneupload, packer, and smoothpre. Refactor stream handling to support headers and improve error handling in fetch requests. 2026-01-03 19:42:27 +01:00
aka paul 71f7d30321 Update index.json 2026-01-03 17:14:15 +00:00
aka paul 8f84613441 Update torrentio/torrentio.json 2026-01-03 17:13:25 +00:00
aka paul c943ee0e2a Update mediafusion/mediafusion.json 2026-01-03 17:13:15 +00:00
aka paul 01f247a38f Update comet/comet.json 2026-01-03 17:12:54 +00:00
aka paul 4549876366 Update README.md 2026-01-02 17:09:15 +00:00
aka paul a3f5d391fb Update hollymoviehd/hollymoviehd.js 2026-01-01 18:48:47 +00:00
aka paul e4362cfb95 Update hollymoviehd/hollymoviehd.js 2026-01-01 18:36:58 +00:00
aka paul aebf2b3e06 Update hollymoviehd/hollymoviehd.js 2026-01-01 18:34:03 +00:00
aka paul e21a030392 Update hollymoviehd/hollymoviehd.js 2026-01-01 16:33:34 +00:00
aka paul db51e71e74 Update hollymoviehd/hollymoviehd.json 2026-01-01 16:26:36 +00:00
aka paul ffce779935 Update hollymoviehd/hollymoviehd.js 2026-01-01 16:24:30 +00:00
aka paul 0ac092ef30 Update hollymoviehd/hollymoviehd.js 2026-01-01 16:15:33 +00:00
aka paul ac3a928325 Update hollymoviehd/hollymoviehd.js 2026-01-01 16:01:20 +00:00
aka paul 3c1b8650a5 Update hollymoviehd/hollymoviehd.js 2026-01-01 15:18:35 +00:00
aka paul db30644f7f Update hollymoviehd/hollymoviehd.js 2026-01-01 15:14:10 +00:00
aka paul 0851988b8d Add hollymoviehd/hollymoviehd.json 2026-01-01 14:47:54 +00:00
aka paul dc420790e6 Add hollymoviehd/hollymoviehd.js 2026-01-01 14:41:30 +00:00
aka paul 5f64c89c7a Update animekai/arabic/animekai.js 2025-12-28 23:24:21 +00:00
aka paul ffa786c01e Update animekai/arabic/animekai.js 2025-12-28 23:09:50 +00:00
aka paul 548d822cd4 Update animekai/arabic/animekai.js 2025-12-28 23:04:12 +00:00
aka paul cb1a96f2f7 Add animekai/arabic/animekai.json 2025-12-28 22:58:17 +00:00
aka paul 1c652aa064 Add animekai/arabic/animekai.js 2025-12-28 22:57:15 +00:00
aka paul a0adb99585 Update ashi/ashi.json 2025-12-28 14:36:53 +00:00
aka paul dbd558ba53 Update 1movies/1movies.json 2025-12-28 14:34:04 +00:00
aka paul fadb2e796a Update 1movies/1movies.js 2025-12-28 14:33:46 +00:00
aka paul 186fd09c30 Update animekai/hardsub/animekai.json 2025-12-28 14:33:29 +00:00
aka paul 350ce531b1 Update animekai/hardsub/animekai.js 2025-12-28 14:33:18 +00:00
aka paul 11b0a3d928 Update animekai/dub/animekai.json 2025-12-28 14:33:02 +00:00
aka paul f5c5564ca6 Update animekai/dub/animekai.js 2025-12-28 14:32:34 +00:00
aka paul 1fa493a0b9 Update ashi/ashi.js 2025-12-28 14:30:38 +00:00
aka paul a7694851e8 Update ashi/ashi.json 2025-12-28 14:04:26 +00:00
aka paul 3e98c074da Update ashi/ashi.js 2025-12-28 14:04:15 +00:00
aka paul 739a503c60 Update 1movies/1movies.json 2025-12-28 13:59:52 +00:00
aka paul 757916982b Update 1movies/1movies.js 2025-12-28 13:59:38 +00:00
aka paul 2ec41daa2a Update animekai/hardsub/animekai.json 2025-12-28 13:58:09 +00:00
aka paul b20283d1bc Update animekai/hardsub/animekai.js 2025-12-28 13:57:54 +00:00
aka paul a68dd1d709 Update animekai/dub/animekai.json 2025-12-28 13:55:51 +00:00
aka paul caee19ed05 Update animekai/dub/animekai.js 2025-12-28 13:55:39 +00:00
aka paul a75bbb782e Update checkmate/checkmate.json 2025-12-28 13:03:51 +00:00
aka paul 5fdb173990 Update checkmate/checkmate.js 2025-12-28 13:03:36 +00:00
aka paul 9fec9d8fff Update hexa/hexa.json 2025-12-28 12:56:40 +00:00
aka paul 62135e381b Update hexa/hexa.js 2025-12-28 12:56:27 +00:00
aka paul b8f7930684 Update videasy/videasy.json 2025-12-28 12:45:05 +00:00
aka paul 419994f016 Update videasy/videasy.js 2025-12-28 12:44:49 +00:00
aka paul f59a8b3a40 Update kimcartoon/kimcartoon.json 2025-12-27 15:34:01 +00:00
aka paul e22faebe94 Update kimcartoon/kimcartoon.js 2025-12-27 15:33:51 +00:00
aka paul cfaf9a7bab Update hexa/hexa.json 2025-12-27 15:27:13 +00:00
aka paul 4c9db45dac Update hexa/hexa.js 2025-12-27 15:27:00 +00:00
aka paul 3be93d7509 Update animesdigital/animesdigital.json 2025-12-27 14:51:35 +00:00
aka paul 93a3a1a48d Update animesdigital/animesdigital.js 2025-12-27 14:50:45 +00:00
aka paul 7b097156e0 Update animeepisodeseries/animeepisodeseries.json 2025-12-27 14:18:42 +00:00
50n50 836f22b348 T3 2025-12-22 15:22:18 +01:00
50n50 8e6c64630a T2 2025-12-22 14:51:13 +01:00
50n50 cd54a8935f T 2025-12-22 14:43:01 +01:00
aka paul 2508a024de Merge pull request 'json' (#4) from ibro/sources:main into main
Reviewed-on: https://git.luna-app.eu/50n50/sources/pulls/4
2025-12-21 03:01:47 +00:00
504 changed files with 21593 additions and 15177 deletions
+24
View File
@@ -0,0 +1,24 @@
{
"sourceName": "111477",
"iconUrl": "https://media.tenor.com/tIOhF5a8McEAAAAe/heart-emoji-love-nonchalant.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Multi Language",
"streamType": "MKV",
"quality": "1080p",
"baseUrl": "https://a.111477.xyz/",
"searchBaseUrl": "https://a.111477.xyz/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/111477/111477.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"note": "USE AN EXTERNAL PLAYER (E.G., VLC/MPV)",
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
@@ -27,7 +27,7 @@ async function searchResults(query) {
if (fullHref && imageSrc && cleanTitle) { if (fullHref && imageSrc && cleanTitle) {
results.push({ results.push({
href: fullHref, href: fullHref,
image: imageSrc, image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle title: cleanTitle
}); });
} }
@@ -44,7 +44,7 @@ async function searchResults(query) {
`${searchBaseUrl}${encodedQuery}&page=3` `${searchBaseUrl}${encodedQuery}&page=3`
]; ];
const responses = await Promise.all(urls.map(url => fetchv2(url))); const responses = await Promise.all(urls.map(url => fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))));
const htmlTexts = await Promise.all(responses.map(response => response.text())); const htmlTexts = await Promise.all(responses.map(response => response.text()));
@@ -60,9 +60,9 @@ async function searchResults(query) {
href: "", href: "",
image: "", image: "",
title: "Search failed: " + error.message title: "Search failed: " + error.message
}]); }]);
}
} }
}
async function extractDetails(url) { async function extractDetails(url) {
try { try {
@@ -128,6 +128,11 @@ async function extractEpisodes(movieUrl) {
} }
async function extractStreamUrl(url) { async function extractStreamUrl(url) {
const headers = {
"Referer": "https://1movies.bz/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
};
try { try {
const eidMatch = url.match(/eid=([^&]+)/); const eidMatch = url.match(/eid=([^&]+)/);
if (eidMatch && eidMatch[1]) { if (eidMatch && eidMatch[1]) {
@@ -138,31 +143,25 @@ async function extractStreamUrl(url) {
url = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`); url = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
} }
const fetchUrl = `${url}`; const response = await fetchv2(url);
const response = await fetchv2(fetchUrl);
const responseData = await response.json(); const responseData = await response.json();
const cleanedHtml = cleanJsonHtml(responseData.result); const cleanedHtml = cleanJsonHtml(responseData.result);
const server1Regex = /<div class="server wnav-item"[^>]*data-lid="([^"]+)"[^>]*>\s*<span>Server 1<\/span>/; const spanRegex = /<div class="server wnav-item"[^>]*data-lid="([^"]+)"[^>]*>/g;
const server1Match = server1Regex.exec(cleanedHtml); const ids = [];
let match;
while ((match = spanRegex.exec(cleanedHtml)) !== null) ids.push(match[1]);
if (!server1Match) { if (ids.length === 0) {
console.log("Server 1 not found"); console.log("No servers found");
return "error"; return "error";
} }
const serverId = server1Match[1]; const serverId = ids.length > 1 ? ids[1] : ids[0];
const tokenRequestData = [{ name: "Server1", data: serverId }]; const tokenData = await fetchv2(`https://enc-dec.app/api/enc-movies-flix?text=${encodeURIComponent(serverId)}`)
.then(res => res.json());
const tokenBatchResponse = await fetchv2( const token = tokenData.result;
"https://ilovekai.simplepostrequest.workers.dev/ilovethighs",
{},
"POST",
JSON.stringify(tokenRequestData)
);
const tokenResults = await tokenBatchResponse.json();
const token = tokenResults[0]?.data;
if (!token) { if (!token) {
console.log("Token not found"); console.log("Token not found");
@@ -178,45 +177,53 @@ async function extractStreamUrl(url) {
return "error"; return "error";
} }
const decryptRequestData = [{ name: "Server1", data: streamData.result }]; const decryptData = await fetchv2(
`https://enc-dec.app/api/dec-movies-flix?text=${streamData.result}`,
headers
).then(res => res.json());
const decryptBatchResponse = await fetchv2( const decryptedUrl = decryptData.result?.url;
"https://ilovekai.simplepostrequest.workers.dev/iloveboobs",
{},
"POST",
JSON.stringify(decryptRequestData)
);
const decryptedResponse = await decryptBatchResponse.json();
const decryptedUrl = decryptedResponse[0]?.data.url;
const subListEncoded = decryptedUrl.split("sub.list=")[1]?.split("&")[0];
let subtitles = "N/A";
if (subListEncoded) {
try {
const subListUrl = decodeURIComponent(subListEncoded);
const subResponse = await fetchv2(subListUrl);
subtitles = await subResponse.json();
} catch {
subtitles = "N/A";
}
}
const englishSubUrl = Array.isArray(subtitles)
? subtitles.find(sub => sub.label === "English")?.file.replace(/\\\//g, "/")
: "N/A";
if (!decryptedUrl) { if (!decryptedUrl) {
console.log("Decryption failed"); console.log("Decryption failed");
return "error"; return "error";
} }
const headers = { const subListEncoded = decryptedUrl.split("sub.list=")[1]?.split("&")[0];
"Referer": "https://1movies.bz/", let subtitles = "N/A";
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" if (subListEncoded) {
}; try {
const subListUrl = decodeURIComponent(subListEncoded);
const subResponse = await fetchv2(subListUrl);
subtitles = await subResponse.json();
} catch {
subtitles = "N/A";
}
}
const mediaResponse = await fetchv2(decryptedUrl.replace("/e/", "/media/"), headers); const englishSubUrl = Array.isArray(subtitles)
? subtitles.find(sub => sub.label === "English")?.file.replace(/\\\//g, "/")
: "N/A";
let finalUrl = decryptedUrl;
if (finalUrl.includes(".to/iframe")) {
try {
const iframeResponse = await fetchv2(finalUrl, headers);
const iframeHtml = await iframeResponse.text();
const iframeSrcMatch = iframeHtml.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
finalUrl = iframeSrcMatch[1];
} else {
console.log("No iframe src found in:", finalUrl);
return "error";
}
} catch (e) {
console.log("Error fetching iframe:", e);
return "error";
}
}
const mediaResponse = await fetchv2(finalUrl.replace("/e/", "/media/"), headers);
const mediaJson = await mediaResponse.json(); const mediaJson = await mediaResponse.json();
const result = mediaJson?.result; const result = mediaJson?.result;
@@ -225,55 +232,73 @@ async function extractStreamUrl(url) {
return "error"; return "error";
} }
const postData = { const finalJson = await fetchv2(
"text": result, "https://enc-dec.app/api/dec-mega",
"Useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" { "Content-Type": "application/json" },
}; "POST",
JSON.stringify({ text: result, agent: headers["User-Agent"] })
).then(res => res.json());
console.log("Final JSON: " + JSON.stringify(finalJson));
const m3u8Link = finalJson?.result?.sources?.[0]?.file;
const finalResponse = await fetchv2("https://ilovekai.simplepostrequest.workers.dev/ilovebush", {}, "POST", JSON.stringify(postData)); const streams = [];
const finalJson = await finalResponse.json(); if (m3u8Link) {
let pushedQualities = 0;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
const m3u8Link = finalJson?.result?.sources?.[0]?.file; for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
let quality = 'Unknown';
const m3u8Response = await fetchv2(m3u8Link); if (resolutionMatch) {
const m3u8Text = await m3u8Response.text(); const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
}
const baseUrl = m3u8Link.substring(0, m3u8Link.lastIndexOf('/') + 1); if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
const streams = []; let absolutePath;
const lines = m3u8Text.split('\n'); if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
for (let i = 0; i < lines.length; i++) { } else if (streamPath.startsWith('http')) {
const line = lines[i].trim(); absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(streamPath);
if (line.startsWith('#EXT-X-STREAM-INF:')) { } else {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/); const baseUrl = m3u8Link.substring(0, m3u8Link.lastIndexOf('/') + 1);
let quality = 'Unknown'; absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(baseUrl + streamPath);
}
if (resolutionMatch) { streams.push({
const [width, height] = resolutionMatch[1].split('x'); title: quality,
quality = `${height}p`; streamUrl: absolutePath
} });
pushedQualities++;
if (i + 1 < lines.length) { }
const streamPath = lines[i + 1].trim();
const streamUrl = baseUrl + streamPath;
streams.push({
title: quality,
streamUrl: streamUrl
});
} }
} }
} catch (e) {
console.log("Failed to extract qualities:", e);
} }
const returnValue = { if (pushedQualities === 0) {
streams: streams, streams.push({
subtitle: englishSubUrl !== "N/A" ? englishSubUrl : "" title: "Source",
}; streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link)
console.log("RETURN: " + JSON.stringify(returnValue)); });
return JSON.stringify(returnValue); }
}
const returnValue = {
streams: streams,
subtitles: englishSubUrl !== "N/A" ? englishSubUrl : ""
};
console.log("RETURN: " + JSON.stringify(returnValue));
return JSON.stringify(returnValue);
} catch (error) { } catch (error) {
console.log("Fetch error:"+ error); console.log("Fetch error:" + error);
return "https://error.org"; return "https://error.org";
} }
} }
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "1Movies",
"iconUrl": "https://1movies.bz/assets/uploads/675b5c22f2829fc8e3a4030af7f4284acad017e5241280b3dc21.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.2.5",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://1movies.bz/home",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/1movies/1movies.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "Anicore",
"iconUrl": "https://anicore.tv/_app/immutable/assets/favicon.Bq_xX7js.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://anicore.tv/",
"searchBaseUrl": "https://anicore.tv/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anicore/anicore.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+297
View File
@@ -0,0 +1,297 @@
async function searchResults(keyword) {
const results = [];
const headers = {
"Host": "api.anicrush.to",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"x-site": "anicrush",
"Origin": "https://anicrush.to",
"Connection": "keep-alive",
"Referer": "https://anicrush.to/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"TE": "trailers"
};
try {
const response = await fetchv2("https://api.anicrush.to/shared/v2/movie/list?keyword=" + encodeURIComponent(keyword) + "&limit=24&page=1", headers);
const data = await response.json();
if (data.status && data.result && data.result.movies) {
for (const movie of data.result.movies) {
if (movie.has_sub) {
results.push({
title: (movie.name_english || movie.name) + " [SUB]",
image: getImage(movie.poster_path),
href: "/watch/" + movie.slug + "." + movie.id + "?type=SUB"
});
}
if (movie.has_dub) {
results.push({
title: (movie.name_english || movie.name) + " [DUB]",
image: getImage(movie.poster_path),
href: "/watch/" + movie.slug + "." + movie.id + "?type=DUB"
});
}
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
const id = url.split('.').pop();
const headers = {
"Host": "api.anicrush.to",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"x-site": "anicrush",
"Origin": "https://anicrush.to",
"Connection": "keep-alive",
"Referer": "https://anicrush.to/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"TE": "trailers"
};
try {
const response = await fetchv2(`https://api.anicrush.to/shared/v2/movie/getById/${id}`, headers);
const data = await response.json();
if (data.status && data.result) {
const result = data.result;
const description = result.overview || "N/A";
const aliases = result.name_synonyms || "N/A";
const airdate = result.aired_from || "N/A";
return JSON.stringify([{
description: description,
aliases: aliases,
airdate: airdate
}]);
} else {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
const id = url.split('.').pop().split('?')[0];
const typeMatch = url.match(/[?&]type=([^&]+)/);
const type = typeMatch ? typeMatch[1] : "SUB";
const headers = {
"Host": "api.anicrush.to",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"x-site": "anicrush",
"Origin": "https://anicrush.to",
"Connection": "keep-alive",
"Referer": "https://anicrush.to/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"TE": "trailers"
};
try {
const url = `https://api.anicrush.to/shared/v2/episode/list?_movieId=${id}`;
const response = await fetchv2(url, headers);
const data = await response.json();
if (data.status && data.result) {
const seasons = Object.keys(data.result);
for (const seasonKey of seasons) {
const season = data.result[seasonKey];
const isMovie = seasonKey === "001 - 001";
for (const ep of season) {
const href = isMovie ? `?_movieId=${id}&ep=1&type=${type}` : `?_movieId=${id}&ep=${ep.number}&type=${type}`;
results.push({
href: href,
number: ep.number
});
}
}
}
return JSON.stringify(results);
} catch (err) {
console.error(err);
return JSON.stringify([{ id: "Error", href: "Error", number: "Error", title: "Error" }]);
}
}
async function extractStreamUrl(ID, type = "DUB") {
const typeMatch = ID.match(/[?&]type=([^&]+)/);
if (typeMatch) {
type = typeMatch[1];
}
const headers = {
"Host": "api.anicrush.to",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"x-site": "anicrush",
"Origin": "https://anicrush.to",
"Connection": "keep-alive",
"Referer": "https://anicrush.to/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"TE": "trailers"
};
const headersTwo = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Referer": "https://hianime.to/"
};
try {
const url = `https://api.anicrush.to/shared/v2/episode/servers${ID}`;
const serversResp = await fetchv2(url, headers);
const serversJson = await serversResp.json();
console.log(JSON.stringify(serversJson));
const subServer = serversJson.result?.sub?.[0];
const dubServer = serversJson.result?.dub?.[0];
const rawServer = serversJson.result?.raw?.[0];
const subServerId = subServer?.server;
const dubServerId = dubServer?.server;
const rawServerId = rawServer?.server;
const processServer = async (serverId, title) => {
try {
const sourcesResp = await fetchv2(`https://api.anicrush.to/shared/v2/episode/sources${ID}&sv=${serverId}&sc=${title.toLowerCase()}`, headers);
const sourcesJson = await sourcesResp.json();
const iframeUrl = sourcesJson.result?.link;
if (!iframeUrl) return null;
const iframeResp = await fetchv2(iframeUrl, headersTwo);
const iframeHtml = await iframeResp.text();
const videoTagMatch = iframeHtml.match(/data-id="([^"]+)"/);
if (!videoTagMatch) return null;
const fileId = videoTagMatch[1];
const nonceMatch = iframeHtml.match(/\b[a-zA-Z0-9]{48}\b/) ||
iframeHtml.match(/\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b/);
if (!nonceMatch) return null;
const nonce = nonceMatch.length === 4 ?
nonceMatch[1] + nonceMatch[2] + nonceMatch[3] :
nonceMatch[0];
const urlParts = iframeUrl.split('/');
const protocol = iframeUrl.startsWith('https') ? 'https:' : 'http:';
const hostname = urlParts[2];
const defaultDomain = `${protocol}//${hostname}/`;
const getSourcesUrl = `${defaultDomain}embed-2/v3/e-1/getSources?id=${fileId}&_k=${nonce}`;
const getSourcesResp = await fetchv2(getSourcesUrl, headersTwo);
const getSourcesJson = await getSourcesResp.json();
console.log(JSON.stringify(getSourcesJson));
const videoUrl = getSourcesJson.sources?.[0]?.file || "";
if (!videoUrl) return null;
const streamHeaders = {
"Referer": defaultDomain,
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36"
};
return {
title: title,
streamUrl: videoUrl,
headers: streamHeaders,
sourcesData: getSourcesJson
};
} catch (e) {
console.log(`${title} failed:`, e);
return null;
}
};
const serverPromises = [];
if (type === "SUB" && subServerId) serverPromises.push(processServer(subServerId, "SUB"));
else if (type === "DUB" && dubServerId) serverPromises.push(processServer(dubServerId, "DUB"));
else if (type === "RAW" && rawServerId) serverPromises.push(processServer(rawServerId, "RAW"));
const results = await Promise.all(serverPromises);
const streams = results.filter(r => r !== null);
if (streams.length === 0) {
return "https://error.org/";
}
const finalStreams = streams.map(s => ({
title: s.title,
streamUrl: s.streamUrl,
headers: s.headers
}));
let subtitle = null;
if (streams.length > 0 && streams[0].sourcesData.tracks) {
const englishTrack = streams[0].sourcesData.tracks.find(t => t.kind === "captions" && t.label === "English");
subtitle = englishTrack ? englishTrack.file : null;
}
const result = {
streams: finalStreams,
subtitles: subtitle
};
console.log(JSON.stringify(result));
return JSON.stringify(result);
} catch (err) {
console.log(err);
return "https://error.oraag/";
}
}
function getImage(path, type = "poster") {
const baseUrl = "https://static.gniyonna.com/media/poster";
const hashWithExt = path.split('/')[3];
const hash = hashWithExt.split('.')[0];
let reversedHash = '';
for (let i = hash.length - 1; i >= 0; i--) {
reversedHash += hash[i];
}
const ext = hashWithExt.split('.').pop();
const size = type === "poster" ? "300x400" : "900x600";
const imageUrl = `${baseUrl}/${size}/100/${reversedHash}.${ext}`;
return imageUrl;
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AniCrush",
"iconUrl": "https://pbs.twimg.com/profile_images/1734825444543733760/dFCwrelN_400x400.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "English (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://megacloud.blog/",
"searchBaseUrl": "https://megacloud.blog/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anicrush/anicrush.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+697
View File
@@ -0,0 +1,697 @@
// AniDub module for Sora (AsyncJS)
// Author: emp0ry
// Version: 1.0.0
const BASE_URL = "https://anidub.org";
const SEARCH_URL = BASE_URL + "/index.php?do=search&subaction=search";
const PLAYLIST_API = "https://plapi.cdnvideohub.com/api/v1/player/sv/playlist";
const VIDEO_API = "https://plapi.cdnvideohub.com/api/v1/player/sv/video/";
const DEFAULT_PUB = "19";
const DEFAULT_AGGR = "mali";
const DEFAULT_SUBTITLE = "https://none.com";
function _ua() {
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
}
function _safeJsonParse(value, fallback) {
try {
return JSON.parse(value);
} catch (_) {
return fallback;
}
}
function _htmlDecode(value) {
const s = String(value || "");
if (!s) return "";
const named = {
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
"&quot;": '"',
"&#39;": "'",
"&apos;": "'",
"&nbsp;": " ",
"&laquo;": "«",
"&raquo;": "»"
};
const withNamed = s.replace(/&(amp|lt|gt|quot|#39|apos|nbsp|laquo|raquo);/g, m => named[m] || m);
const withDec = withNamed.replace(/&#(\d+);/g, (_, d) => {
const code = parseInt(d, 10);
return Number.isFinite(code) ? String.fromCharCode(code) : "";
});
return withDec.replace(/&#x([0-9a-f]+);/gi, (_, h) => {
const code = parseInt(h, 16);
return Number.isFinite(code) ? String.fromCharCode(code) : "";
});
}
function _stripTags(value) {
return _htmlDecode(String(value || "")
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<\/p>/gi, "\n")
.replace(/<[^>]+>/g, " ")
.replace(/\s+/g, " ")
.trim());
}
function _attr(tag, name) {
const block = String(tag || "");
if (!block) return "";
const quoted = new RegExp(name + "\\s*=\\s*(['\"])(.*?)\\1", "i").exec(block);
if (quoted && quoted[2]) return _htmlDecode(quoted[2]);
const plain = new RegExp(name + "\\s*=\\s*([^\\s>]+)", "i").exec(block);
return plain && plain[1] ? _htmlDecode(plain[1]) : "";
}
function _extractMeta(html, attrName, attrValue) {
const src = String(html || "");
const re = new RegExp(
"<meta[^>]*" + attrName + "=['\"]" + attrValue + "['\"][^>]*content=['\"]([^'\"]+)['\"][^>]*>",
"i"
);
const m = src.match(re);
return m && m[1] ? _htmlDecode(m[1]).trim() : "";
}
function _absUrl(url, base) {
const raw = String(url || "").trim();
if (!raw) return "";
if (raw.startsWith("http://") || raw.startsWith("https://")) return raw;
if (raw.startsWith("//")) return "https:" + raw;
const root = String(base || BASE_URL).replace(/\/+$/, "");
return root + "/" + raw.replace(/^\/+/, "");
}
function _cleanUrl(url) {
const raw = String(url || "").trim();
if (!raw) return null;
if (raw.startsWith("//")) return "https:" + raw;
return raw;
}
function _scoreTitle(title, keyword) {
const t = String(title || "").toLowerCase().trim();
const k = String(keyword || "").toLowerCase().trim();
if (!t || !k) return 99;
if (t === k) return 0;
if (t.startsWith(k)) return 1;
if (t.includes(k)) return 2;
return 3;
}
function _voiceRank(name) {
const s = String(name || "").toLowerCase();
const order = [
"anidub online",
"anidub",
"aniliberty",
"anilibria",
"shiza",
"onwave",
"anistar",
"animevost",
"студийная банда",
"studio band",
"dream cast",
"dreamcast",
"jam club",
"jam",
"субтит",
"sub"
];
for (let i = 0; i < order.length; i++) {
if (s.includes(order[i])) return i;
}
return 999;
}
function _packAnime(payload) {
return "anidub-release:" + encodeURIComponent(JSON.stringify(payload || {}));
}
function _unpackAnime(href) {
const raw = String(href || "");
if (!raw.startsWith("anidub-release:")) return null;
return _safeJsonParse(decodeURIComponent(raw.slice("anidub-release:".length)), null);
}
function _packEpisode(payload) {
return "anidub:" + encodeURIComponent(JSON.stringify(payload || {}));
}
function _unpackEpisode(href) {
const raw = String(href || "");
if (!raw.startsWith("anidub:")) return null;
return _safeJsonParse(decodeURIComponent(raw.slice("anidub:".length)), null);
}
function _searchHeaders() {
return {
"User-Agent": _ua(),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "ru-RU,ru;q=0.9,en;q=0.8",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": BASE_URL,
"Referer": BASE_URL + "/"
};
}
function _htmlHeaders(referer) {
return {
"User-Agent": _ua(),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "ru-RU,ru;q=0.9,en;q=0.8",
"Referer": referer || BASE_URL + "/",
"Origin": BASE_URL
};
}
function _jsonHeaders(referer) {
return {
"User-Agent": _ua(),
"Accept": "application/json, text/plain, */*",
"Accept-Language": "ru-RU,ru;q=0.9,en;q=0.8",
"Referer": referer || BASE_URL + "/"
};
}
function _streamHeaders() {
return {
"User-Agent": _ua(),
"Referer": BASE_URL + "/"
};
}
async function _postSearch(query) {
const q = encodeURIComponent(String(query || ""));
const withParams = `${SEARCH_URL}&story=${q}`;
try {
const r = await fetchv2(withParams, _searchHeaders(), "POST", "");
const txt = await r.text();
if (txt && txt.includes("all__item")) return txt;
} catch (_) {}
const body = "do=search&subaction=search&story=" + q;
const fallback = await fetchv2(BASE_URL + "/index.php?do=search", _searchHeaders(), "POST", body);
return fallback.text();
}
function _parseSearchResults(html) {
const src = String(html || "");
const out = [];
const seen = new Set();
const blocks = src.match(/<a\b[^>]*class=["'][^"']*all__item[^"']*["'][^>]*>[\s\S]*?<\/a>/gi) || [];
for (const block of blocks) {
const openTag = (block.match(/<a\b[^>]*>/i) || [""])[0];
const imgTag = (block.match(/<img\b[^>]*>/i) || [""])[0];
const titleMatch = block.match(
/<span\b[^>]*class=["'][^"']*all__item-title[^"']*["'][^>]*>([\s\S]*?)<\/span>/i
);
const href = _absUrl(_attr(openTag, "href"), BASE_URL);
const title = _stripTags((titleMatch && titleMatch[1]) || _attr(openTag, "title") || "Unknown title");
const image = _absUrl(_attr(imgTag, "src") || _attr(imgTag, "data-src"), BASE_URL);
if (!href || !title || seen.has(href)) continue;
seen.add(href);
out.push({
title,
image,
href: _packAnime({
animeUrl: href,
title,
image
})
});
}
return out;
}
function _extractAnimeUrl(input) {
const packed = _unpackAnime(input);
if (packed?.animeUrl) return _absUrl(packed.animeUrl, BASE_URL);
const ep = _unpackEpisode(input);
if (ep?.animeUrl) return _absUrl(ep.animeUrl, BASE_URL);
return _absUrl(input, BASE_URL);
}
function _extractTitleId(html) {
const src = String(html || "");
const m1 = src.match(/data-title-id=["'](\d+)["']/i);
if (m1 && m1[1]) return m1[1];
const m2 = src.match(/[?&]titleId=(\d+)/i);
if (m2 && m2[1]) return m2[1];
return "";
}
function _extractPublisherId(html) {
const src = String(html || "");
const m = src.match(/data-publisher-id=["'](\d+)["']/i);
return m && m[1] ? m[1] : DEFAULT_PUB;
}
function _extractAggregator(html) {
const src = String(html || "");
const m1 = src.match(/data-aggregator=["']([^"']+)["']/i);
if (m1 && m1[1]) return m1[1];
const m2 = src.match(/[?&]aggr=([^"'&<>\s]+)/i);
return m2 && m2[1] ? decodeURIComponent(m2[1]) : DEFAULT_AGGR;
}
async function _getAnimePage(animeUrl) {
const r = await fetchv2(animeUrl, _htmlHeaders(BASE_URL + "/"));
return r.text();
}
async function _getPlaylist(titleId, pub, aggr, referer) {
const url =
`${PLAYLIST_API}?pub=${encodeURIComponent(pub || DEFAULT_PUB)}` +
`&aggr=${encodeURIComponent(aggr || DEFAULT_AGGR)}` +
`&id=${encodeURIComponent(titleId)}`;
const r = await fetchv2(url, _jsonHeaders(referer || BASE_URL + "/"));
return r.json();
}
async function _getVideo(vkId, referer) {
const url = VIDEO_API + encodeURIComponent(String(vkId || ""));
const r = await fetchv2(url, _jsonHeaders(referer || BASE_URL + "/"));
return r.json();
}
function _detailsDescription(html) {
const title =
_stripTags((html.match(/<h1\b[^>]*class=["'][^"']*movie__title[^"']*["'][^>]*>([\s\S]*?)<\/h1>/i) || [null, ""])[1]) ||
_extractMeta(html, "property", "og:title") ||
_extractMeta(html, "name", "title") ||
"AniDub";
const subtitle = _stripTags(
(html.match(/<span\b[^>]*class=["'][^"']*movie__subtitle[^"']*["'][^>]*>([\s\S]*?)<\/span>/i) || [null, ""])[1]
);
const desc =
_stripTags((html.match(/<div\b[^>]*class=["'][^"']*movie__description[^"']*["'][^>]*>([\s\S]*?)<\/div>/i) || [null, ""])[1]) ||
_extractMeta(html, "property", "og:description") ||
_extractMeta(html, "name", "description") ||
"No description available.";
return [title, subtitle, desc].filter(Boolean).join("\n\n");
}
function _detailsAliases(html, titleId, pub, aggr) {
const parts = [];
const addField = (display, pattern) => {
const re = new RegExp(
"<div[^>]*class=[\"'][^\"']*movie__type[^\"']*[\"'][^>]*>\\s*<span>" +
pattern +
":<\\/span>\\s*([\\s\\S]*?)<\\/div>",
"i"
);
const m = html.match(re);
const val = m && m[1] ? _stripTags(m[1]) : "";
if (val) parts.push(`${display}: ${val}`);
};
addField("Жанр", "Жанр");
addField("Тип", "Тип");
addField("Статус", "Статус");
addField("Год выхода", "Год выхода");
addField("Длительность", "Длительность \\(мин\\.\\)");
addField("Кол-во эпизодов", "Кол-во эпизодов");
addField("Другие названия", "Другие названия");
if (titleId) parts.push(`titleId: ${titleId}`);
if (pub) parts.push(`pub: ${pub}`);
if (aggr) parts.push(`aggr: ${aggr}`);
return parts.join(" | ");
}
function _airdate(html) {
const premiere = html.match(
/<div[^>]*class=["'][^"']*movie__type[^"']*["'][^>]*>\s*<span>Премьера:<\/span>\s*([\s\S]*?)<\/div>/i
);
const added = html.match(
/<div[^>]*class=["'][^"']*movie__added[^"']*["'][^>]*>[\s\S]*?<span>Добавлено<\/span>[\s\S]*?<span>([\s\S]*?)<\/span>/i
);
return _stripTags((premiere && premiere[1]) || (added && added[1]) || "Unknown");
}
function _episodeKey(season, episode) {
return `${season || 1}:${episode || 0}`;
}
function _episodeTitle(season, episode) {
if (season && season > 1) return `S${season}E${episode}`;
return `Episode ${episode}`;
}
function _voiceName(item) {
const studio = String(item?.voiceStudio || "").trim();
const type = String(item?.voiceType || "").trim();
if (studio && type) return `${studio} · ${type}`;
if (studio) return studio;
if (type) return type;
return "Unknown voiceover";
}
function _sourceUrl(sources, key) {
return _cleanUrl(sources?.[key] || "");
}
function _buildQualityUrls(videoJson) {
const sources = videoJson?.sources || {};
const url2160 = _sourceUrl(sources, "mpeg4kUrl");
const url1440 = _sourceUrl(sources, "mpeg2kUrl") || _sourceUrl(sources, "mpegQhdUrl");
const url1080 = _sourceUrl(sources, "mpegFullHdUrl");
const url720 = _sourceUrl(sources, "mpegHighUrl");
const url480 = _sourceUrl(sources, "mpegMediumUrl");
const url360 = _sourceUrl(sources, "mpegLowUrl");
const url240 = _sourceUrl(sources, "mpegLowestUrl");
const url144 = _sourceUrl(sources, "mpegTinyUrl");
return {
url2160,
url1440,
url1080,
url720,
url480,
url360,
url240,
url144
};
}
function _pickBestMp4(qualityUrls) {
const ordered = [
{ quality: 2160, url: qualityUrls.url2160 },
{ quality: 1440, url: qualityUrls.url1440 },
{ quality: 1080, url: qualityUrls.url1080 },
{ quality: 720, url: qualityUrls.url720 },
{ quality: 480, url: qualityUrls.url480 },
{ quality: 360, url: qualityUrls.url360 },
{ quality: 240, url: qualityUrls.url240 },
{ quality: 144, url: qualityUrls.url144 }
];
for (const item of ordered) {
if (item.url) return item;
}
return null;
}
function _hasAnyQuality(qualityUrls) {
return !!(
qualityUrls.url2160 ||
qualityUrls.url1440 ||
qualityUrls.url1080 ||
qualityUrls.url720 ||
qualityUrls.url480 ||
qualityUrls.url360 ||
qualityUrls.url240 ||
qualityUrls.url144
);
}
function _buildVoiceoverStream(voiceName, videoJson) {
const qualityUrls = _buildQualityUrls(videoJson);
if (!_hasAnyQuality(qualityUrls)) {
return null;
}
const best = _pickBestMp4(qualityUrls);
if (!best?.url) return null;
const headers = _streamHeaders();
return {
title: voiceName || "Voiceover",
streamUrl: best.url,
// Main Sora quality-picker fields.
url1080: qualityUrls.url1080,
url720: qualityUrls.url720,
url480: qualityUrls.url480,
// Extra qualities. Harmless if Sora ignores them.
url2160: qualityUrls.url2160,
url1440: qualityUrls.url1440,
url360: qualityUrls.url360,
url240: qualityUrls.url240,
url144: qualityUrls.url144,
headers
};
}
// ------------------------------------------------------------
// Search
// ------------------------------------------------------------
async function searchResults(keyword) {
try {
const query = String(keyword || "").trim();
if (!query) return JSON.stringify([]);
const html = await _postSearch(query);
const results = _parseSearchResults(html);
for (const item of results) {
item._score = _scoreTitle(item.title, query);
}
results.sort((a, b) => a._score - b._score);
return JSON.stringify(results.map(({ _score, ...rest }) => rest));
} catch (_) {
return JSON.stringify([]);
}
}
// ------------------------------------------------------------
// Details
// ------------------------------------------------------------
async function extractDetails(href) {
try {
const animeUrl = _extractAnimeUrl(href);
if (!animeUrl) return JSON.stringify([]);
const html = await _getAnimePage(animeUrl);
const titleId = _extractTitleId(html);
const pub = _extractPublisherId(html);
const aggr = _extractAggregator(html);
return JSON.stringify([{
description: _detailsDescription(html),
aliases: _detailsAliases(html, titleId, pub, aggr) || "AniDub",
airdate: _airdate(html)
}]);
} catch (_) {
return JSON.stringify([]);
}
}
// ------------------------------------------------------------
// Episodes
// Unique episodes only.
// Store all voiceovers in href payload.
// ------------------------------------------------------------
async function extractEpisodes(href) {
try {
const animeUrl = _extractAnimeUrl(href);
if (!animeUrl) return JSON.stringify([]);
const html = await _getAnimePage(animeUrl);
const titleId = _extractTitleId(html);
const pub = _extractPublisherId(html);
const aggr = _extractAggregator(html);
if (!titleId) return JSON.stringify([]);
const playlist = await _getPlaylist(titleId, pub, aggr, animeUrl);
const items = Array.isArray(playlist?.items) ? playlist.items : [];
const byEpisode = new Map();
for (const item of items) {
const vkId = String(item?.vkId || "").trim();
if (!vkId) continue;
const season = Number.isFinite(item?.season) ? item.season : 1;
const episode = Number.isFinite(item?.episode) ? item.episode : 0;
if (!episode) continue;
const key = _episodeKey(season, episode);
if (!byEpisode.has(key)) {
byEpisode.set(key, {
season,
episode,
options: []
});
}
const ep = byEpisode.get(key);
ep.options.push({
vkId,
cvhId: String(item?.cvhId || ""),
voiceStudio: String(item?.voiceStudio || "").trim(),
voiceType: String(item?.voiceType || "").trim(),
voiceName: _voiceName(item),
name: String(item?.name || "").trim(),
season,
episode
});
}
const out = Array.from(byEpisode.values())
.sort((a, b) => {
if (a.season !== b.season) return a.season - b.season;
return a.episode - b.episode;
})
.map(ep => {
ep.options.sort((a, b) => _voiceRank(a.voiceName) - _voiceRank(b.voiceName));
return {
href: _packEpisode({
animeUrl,
titleId,
pub,
aggr,
season: ep.season,
episode: ep.episode,
options: ep.options
}),
number: ep.episode,
title: _episodeTitle(ep.season, ep.episode)
};
});
return JSON.stringify(out);
} catch (_) {
return JSON.stringify([]);
}
}
// ------------------------------------------------------------
// Stream
// Correct voiceover + quality picker:
// - one stream per voiceover
// - each stream carries url1080/url720/url480/etc.
// - HLS skipped.
// ------------------------------------------------------------
async function extractStreamUrl(href) {
try {
const payload = _unpackEpisode(href);
const options = Array.isArray(payload?.options) ? payload.options : [];
if (!options.length) {
return JSON.stringify({
streams: [],
subtitle: DEFAULT_SUBTITLE
});
}
options.sort((a, b) => _voiceRank(a.voiceName) - _voiceRank(b.voiceName));
const streams = [];
const seen = new Set();
for (const opt of options) {
const vkId = String(opt?.vkId || "").trim();
if (!vkId || seen.has(vkId)) continue;
seen.add(vkId);
try {
const videoJson = await _getVideo(vkId, payload?.animeUrl || BASE_URL + "/");
const stream = _buildVoiceoverStream(
String(opt?.voiceName || "Voiceover").trim(),
videoJson
);
if (stream) streams.push(stream);
} catch (_) {}
}
return JSON.stringify({
streams,
subtitle: DEFAULT_SUBTITLE
});
} catch (_) {
return JSON.stringify({
streams: [],
subtitle: DEFAULT_SUBTITLE
});
}
}
function _defaultExport() {
return {
searchResults,
extractDetails,
extractEpisodes,
extractStreamUrl
};
}
try {
globalThis.default = _defaultExport;
} catch (_) {}
try {
this.default = _defaultExport;
} catch (_) {}
try {
globalThis.module = globalThis.module || {};
globalThis.module.exports = { default: _defaultExport };
} catch (_) {}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AniDub",
"iconUrl": "https://anidub.org/templates/CinemaBlue/images/jutfun.png",
"author": {
"name": "emp0ry",
"icon": "https://avatars.githubusercontent.com/u/64217088"
},
"version": "1.0.0",
"language": "Russian",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://anidub.org/",
"searchBaseUrl": "https://anidub.org/index.php?do=search&subaction=search&story=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anidub/anidub.js",
"asyncJS": true,
"streamAsyncJS": true,
"softsub": false,
"type": "anime",
"downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsShirox": true
}
@@ -1,25 +1,54 @@
// AniLiberty module for Sora (AsyncJS) // AniLiberty module for Sora (AsyncJS)
// Author: emp0ry // Author: emp0ry
// Version: 1.0.1 // Version: 1.0.2
const IMAGE_HOST = "https://anilibria.top"; const DEFAULT_IMAGE_HOST = "https://aniliberty.top";
function fullImg(path) { function originFromApi(urlOrBase) {
if (!urlOrBase) return DEFAULT_IMAGE_HOST;
const raw = String(urlOrBase);
const m = raw.match(/^(https?:\/\/[^/]+)\/api\/v1\/?/i);
if (m && m[1]) return m[1];
const m2 = raw.match(/^(https?:\/\/[^/]+)/i);
return (m2 && m2[1]) ? m2[1] : DEFAULT_IMAGE_HOST;
}
function fullImg(path, host) {
if (!path) return; if (!path) return;
return path.startsWith("http") ? path : `${IMAGE_HOST}${path}`; if (path.startsWith("http")) return path;
const base = host || DEFAULT_IMAGE_HOST;
return `${base}${path}`;
} }
function pickBestHls(ep) { function pickBestHls(ep) {
return ep?.hls_1080 || ep?.hls_720 || ep?.hls_480 || null; return ep?.hls_1080 || ep?.hls_720 || ep?.hls_480 || null;
} }
function _packEpisode(payload) {
return "aniliberty:" + encodeURIComponent(JSON.stringify(payload || {}));
}
function _unpackEpisode(href) {
const s = String(href || "");
if (!s.startsWith("aniliberty:")) return null;
return _safeJsonParse(decodeURIComponent(s.slice("aniliberty:".length)), null);
}
function _safeJsonParse(s, fallback) {
try {
return JSON.parse(s);
} catch (_) {
return fallback;
}
}
// ------------------------------------------------------------ // ------------------------------------------------------------
// Detect working API domain // Detect working API domain
// ------------------------------------------------------------ // ------------------------------------------------------------
async function checkApiStatus() { async function checkApiStatus() {
const domains = [ const domains = [
"https://anilibria.top/api/v1/",
"https://aniliberty.top/api/v1/", "https://aniliberty.top/api/v1/",
"https://anilibria.top/api/v1/",
"https://anilibria.wtf/api/v1/" "https://anilibria.wtf/api/v1/"
]; ];
for (const base of domains) { for (const base of domains) {
@@ -29,7 +58,7 @@ async function checkApiStatus() {
if (data?.is_alive || data?.result === "ok") return base; if (data?.is_alive || data?.result === "ok") return base;
} catch (_) {} } catch (_) {}
} }
return "https://anilibria.top/api/v1/"; return "https://aniliberty.top/api/v1/";
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
@@ -38,6 +67,7 @@ async function checkApiStatus() {
async function searchResults(keyword) { async function searchResults(keyword) {
try { try {
const base = await checkApiStatus(); const base = await checkApiStatus();
const origin = originFromApi(base);
const url = `${base}app/search/releases?query=${encodeURIComponent(keyword)}&include=id,name.main,poster.src`; const url = `${base}app/search/releases?query=${encodeURIComponent(keyword)}&include=id,name.main,poster.src`;
const res = await fetchv2(url); const res = await fetchv2(url);
@@ -45,7 +75,7 @@ async function searchResults(keyword) {
const out = (Array.isArray(data) ? data : []).map(it => ({ const out = (Array.isArray(data) ? data : []).map(it => ({
title: it?.name?.main || "Unknown title", title: it?.name?.main || "Unknown title",
image: fullImg(it?.poster?.src), image: fullImg(it?.poster?.src, origin),
href: href:
`${base}anime/releases/${it.id}?` + `${base}anime/releases/${it.id}?` +
[ [
@@ -99,19 +129,24 @@ async function extractEpisodes(url) {
const res = await fetchv2(url); const res = await fetchv2(url);
const data = await res.json(); const data = await res.json();
const seriesPoster = fullImg(data?.poster?.src); const origin = originFromApi(url);
const seriesPoster = fullImg(data?.poster?.src, origin);
const eps = Array.isArray(data?.episodes) ? data.episodes : []; const eps = Array.isArray(data?.episodes) ? data.episodes : [];
const out = eps.map((ep, idx) => { const out = eps.map((ep, idx) => {
const href = pickBestHls(ep); const url1080 = ep?.hls_1080 || null;
if (!href) return null; const url720 = ep?.hls_720 || null;
const url480 = ep?.hls_480 || null;
const best = url1080 || url720 || url480;
if (!best) return null;
const num = Number.isFinite(ep?.ordinal) const num = Number.isFinite(ep?.ordinal)
? ep.ordinal ? ep.ordinal
: (Number.isFinite(ep?.sort_order) ? ep.sort_order : (idx + 1)); : (Number.isFinite(ep?.sort_order) ? ep.sort_order : (idx + 1));
const title = ep?.name ? String(ep.name) : `Episode ${num}`; const title = ep?.name ? String(ep.name) : `Episode ${num}`;
const image = fullImg(ep?.preview?.src) || seriesPoster; const image = fullImg(ep?.preview?.src, origin) || seriesPoster;
// Build skip blocks only if numbers present // Build skip blocks only if numbers present
const opening = (ep?.opening && Number.isFinite(ep.opening.start) && Number.isFinite(ep.opening.stop)) const opening = (ep?.opening && Number.isFinite(ep.opening.start) && Number.isFinite(ep.opening.stop))
@@ -123,7 +158,12 @@ async function extractEpisodes(url) {
: undefined; : undefined;
const entry = { const entry = {
href, href: _packEpisode({
url1080,
url720,
url480,
fallback: best
}),
number: num, number: num,
title, title,
image image
@@ -148,7 +188,68 @@ async function extractEpisodes(url) {
// ------------------------------------------------------------ // ------------------------------------------------------------
async function extractStreamUrl(url) { async function extractStreamUrl(url) {
try { try {
return url; // direct HLS const payload = _unpackEpisode(url);
if (!payload) {
return url; // backward compatibility for old episode href format
}
const url1080 = (payload.url1080 || "").toString().trim() || null;
const url720 = (payload.url720 || "").toString().trim() || null;
const url480 = (payload.url480 || "").toString().trim() || null;
const fallback = (payload.fallback || "").toString().trim() || null;
const streams = [];
if (url1080) {
streams.push({
title: "1080p",
streamUrl: url1080,
url1080,
url720,
url480,
headers: {
"User-Agent": "Mozilla/5.0",
"Referer": DEFAULT_IMAGE_HOST + "/"
}
});
}
if (url720) {
streams.push({
title: "720p",
streamUrl: url720,
url1080,
url720,
url480,
headers: {
"User-Agent": "Mozilla/5.0",
"Referer": DEFAULT_IMAGE_HOST + "/"
}
});
}
if (url480) {
streams.push({
title: "480p",
streamUrl: url480,
url1080,
url720,
url480,
headers: {
"User-Agent": "Mozilla/5.0",
"Referer": DEFAULT_IMAGE_HOST + "/"
}
});
}
if (!streams.length) {
return fallback;
}
return JSON.stringify({
streams,
subtitle: "https://none.com"
});
} catch (e) { } catch (e) {
return null; return null;
} }
@@ -1,20 +1,25 @@
{ {
"sourceName": "AniLiberty", "sourceName": "AniLiberty",
"iconUrl": "https://anilibria.top/static/apple-touch-icon.png", "iconUrl": "https://aniliberty.top/static/favicon-96x96.png",
"author": { "author": {
"name": "emp0ry", "name": "emp0ry",
"icon": "https://avatars.githubusercontent.com/u/64217088" "icon": "https://avatars.githubusercontent.com/u/64217088"
}, },
"version": "1.0.2", "version": "1.0.4",
"language": "Russian", "language": "Russian",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
"baseUrl": "https://anilibria.top/api/v1/", "baseUrl": "https://aniliberty.top/api/v1/",
"searchBaseUrl": "https://anilibria.top/api/v1/app/search/releases?query=%s", "searchBaseUrl": "https://aniliberty.top/api/v1/app/search/releases?query=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniliberty/aniliberty.js", "scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniliberty/aniliberty.js",
"asyncJS": true, "asyncJS": true,
"streamAsyncJS": false, "streamAsyncJS": false,
"softsub": false, "softsub": false,
"type": "anime", "type": "anime",
"downloadSupport": false "downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsShirox": true
} }
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "AniLibria",
"iconUrl": "https://github.com/50n50/sources/blob/main/anilibria/icon.png?raw=true",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Russian",
"streamType": "HLS",
"quality": "720p",
"baseUrl": "https://api.anilibria.tv/",
"searchBaseUrl": "https://api.anilibria.tv/v3/title/search?search=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anilibria/anilibria.js",
"asyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

@@ -1,23 +1,76 @@
async function searchResults(keyword) { async function getDomainsList() {
const results = []; try {
const headers = { console.log("Fetching domains from anime-sama.pw...");
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", const response = await fetchv2("https://anime-sama.pw/");
"X-Requested-With": "XMLHttpRequest", const html = await response.text();
"referer": "https://anime-sama.org/" console.log("Fetched HTML length:", html.length);
};
const domainRegex = /{ name: '([^']+)' }/g;
const domains = [];
let match;
while ((match = domainRegex.exec(html)) !== null) {
domains.push(match[1]);
}
console.log("Parsed domains:", domains);
return domains.length > 0 ? domains : ["anime-sama.tv"];
} catch (err) {
console.error("Error in getDomainsList:", err);
return ["anime-sama.tv"];
}
}
async function searchResults(keyword) {
console.log("searchResults keyword:", keyword);
const domains = await getDomainsList();
const regex = /<a[^>]+href="([^"]+)"[\s\S]*?<img[^>]+src="([^"]+)"[\s\S]*?<h3[^>]*>(.*?)<\/h3>/gi; const regex = /<a[^>]+href="([^"]+)"[\s\S]*?<img[^>]+src="([^"]+)"[\s\S]*?<h3[^>]*>(.*?)<\/h3>/gi;
const firstDomain = domains[0];
console.log("Trying first domain:", firstDomain);
const firstResult = await trySearch(firstDomain, keyword, regex);
if (firstResult && firstResult.length > 0) {
console.log("Results found in first domain:", firstDomain);
return JSON.stringify(firstResult);
}
console.log("No results in first domain, trying others...");
const otherDomains = domains.slice(1);
const promises = otherDomains.map(domain => trySearch(domain, keyword, regex));
const results = await Promise.all(promises);
for (let i = 0; i < results.length; i++) {
const result = results[i];
if (result && result.length > 0) {
console.log("Results found in domain:", otherDomains[i]);
return JSON.stringify(result);
}
}
console.log("No results found in any domain.");
return JSON.stringify([]);
}
async function trySearch(domain, keyword, regex) {
try { try {
console.log("trySearch domain:", domain, "keyword:", keyword);
const headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"referer": `https://${domain}/`
};
const response = await fetchv2( const response = await fetchv2(
"https://anime-sama.eu/template-php/defaut/fetch.php", `https://${domain}/template-php/defaut/fetch.php`,
headers, headers,
"POST", "POST",
`query=${encodeURIComponent(keyword)}` `query=${encodeURIComponent(keyword)}`
); );
const html = await response.text(); const html = await response.text();
console.log("trySearch HTML length:", html.length);
const results = [];
let match; let match;
regex.lastIndex = 0;
while ((match = regex.exec(html)) !== null) { while ((match = regex.exec(html)) !== null) {
results.push({ results.push({
title: match[3].trim(), title: match[3].trim(),
@@ -26,13 +79,11 @@ async function searchResults(keyword) {
}); });
} }
return JSON.stringify(results); console.log("trySearch found results:", results.length);
return results;
} catch (err) { } catch (err) {
return JSON.stringify([{ console.error("trySearch error for domain", domain, ":", err);
title: "Error", return [];
image: "Error",
href: "Error"
}]);
} }
} }
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Anime3rb",
"iconUrl": "https://anime3rb.com/favicon/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.2.0",
"language": "Arabic (SUB)",
"streamType": "MP4",
"quality": "1080p - 720p - 360p",
"baseUrl": "https://anime3rb.com/",
"searchBaseUrl": "https://anime3rb.com/search?q=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anime3rb/anime3rb.js",
"streamAsyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

+26
View File
@@ -0,0 +1,26 @@
{
"sourceName": "Anime4Up",
"iconUrl": "https://ww.anime4up.rest/wp-content/uploads/2019/03/Anime4up-Icon-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"encrypted": true,
"quality": "1080p",
"baseUrl": "https://uqload.cx/",
"searchBaseUrl": "https://uqload.cx/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anime4up/anime4up.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+24
View File
@@ -0,0 +1,24 @@
{
"sourceName": "AnimeBalkan",
"iconUrl": "https://i.ibb.co/7tRnVMqY/favicon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Croatian",
"streamType": "MP4",
"quality": "720p",
"baseUrl": "https://animebalkan.org//",
"searchBaseUrl": "https://animebalkan.org/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animebalkan/animebalkan.js",
"type": "anime",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimeBum",
"iconUrl": "https://files.catbox.moe/i96cs1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.animebum.net/",
"searchBaseUrl": "https://www.animebum.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animebum/animebum.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
@@ -0,0 +1,26 @@
{
"sourceName": "AnimeEpisodeSeries",
"iconUrl": "https://animeepisodeseries.com/wp-content/uploads/2019/10/cropped-anime-episode-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English (DUB/SUB)",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://www.4shared.com/",
"searchBaseUrl": "https://www.4shared.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeepisodeseries/animeepisodeseries.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Use external player.",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "AnimeFHD",
"iconUrl": "https://animefhd.net/wp-content/uploads/2024/12/270.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "Portuguese (SUB)",
"streamType": "MP4",
"quality": "1080p",
"baseUrl": "https://animefhd.net/",
"searchBaseUrl": "https://animefhd.net/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animefhd/animefhd.js",
"asyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeIndo",
"iconUrl": "https://animeindo.skin/favicon/favicon-32x32.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Indonesian",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeindo.skin/home",
"searchBaseUrl": "https://animeindo.skin/home",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeindo/animeindo.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+337
View File
@@ -0,0 +1,337 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2(searchUrl);
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch
? (imageMatch[1].startsWith("http") ? imageMatch[1] : baseUrl + imageMatch[1])
: null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: imageSrc,
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const htmlText = await response.text();
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2(actualUrl)).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2(episodeListUrl)).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
console.log(`[extractStreamUrl] ${type} ids:`, ids);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const dubType = url.includes("dub") ? "dub" : "sub";
const types = dubType === "sub" ? ["sub", "softsub"] : ["dub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewRes = await fetchv2(`https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
console.log(`[extractStreamUrl] fallback iframeUrl for ${type}:`, iframeUrl);
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { sub: "Hardsub English", softsub: "Original audio", dub: "Dubbed English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const proxyReqUrl = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file);
const m3u8Response = await fetchv2(proxyReqUrl);
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
const nameMatch = line.match(/NAME="([^"]+)"/i) || line.match(/BANDWIDTH=(\d+)/i);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
} else if (nameMatch && nameMatch[1]) {
quality = nameMatch[1];
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeKai",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.4",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
@@ -1,7 +1,7 @@
async function searchResults(query) { async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword); const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://animekai.to/browser?keyword="; const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://animekai.to"; const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g; const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g; const titleRegex = /class="title"[^>]*title="[^"]*"/g;
@@ -13,7 +13,7 @@ async function searchResults(query) {
try { try {
const encodedQuery = encodeQuery(query); const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery; const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2(searchUrl); const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text(); const htmlText = await response.text();
const results = []; const results = [];
@@ -40,7 +40,7 @@ async function searchResults(query) {
if (fullHref && imageSrc && cleanTitle) { if (fullHref && imageSrc && cleanTitle) {
results.push({ results.push({
href: fullHref, href: fullHref,
image: imageSrc, image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle title: cleanTitle
}); });
} }
@@ -58,7 +58,7 @@ async function searchResults(query) {
async function extractDetails(url) { async function extractDetails(url) {
try { try {
const response = await fetchv2(url); const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text(); const htmlText = await response.text();
console.log(htmlText); console.log(htmlText);
@@ -83,7 +83,7 @@ async function extractDetails(url) {
async function extractEpisodes(url) { async function extractEpisodes(url) {
try { try {
const actualUrl = url.replace("Animekai:", "").trim(); const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2(actualUrl)).text(); const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1]; const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]); if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
@@ -91,8 +91,8 @@ async function extractEpisodes(url) {
const tokenData = await tokenResponse.json(); const tokenData = await tokenResponse.json();
const token = tokenData.result; const token = tokenData.result;
const episodeListUrl = `https://animekai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`; const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2(episodeListUrl)).json(); const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result); const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g; const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
@@ -100,7 +100,7 @@ async function extractEpisodes(url) {
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({ const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10), number: parseInt(episodeNum, 10),
href: `https://animekai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME` href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
})); }));
return JSON.stringify(episodes); return JSON.stringify(episodes);
@@ -125,7 +125,7 @@ async function extractStreamUrl(url) {
} }
const fetchUrl = `${url}`; const fetchUrl = `${url}`;
const response = await fetchv2(fetchUrl); const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(fetchUrl));
const text = await response.text(); const text = await response.text();
const cleanedHtml = cleanJsonHtml(text); const cleanedHtml = cleanJsonHtml(text);
const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/; const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/;
@@ -164,7 +164,7 @@ async function extractStreamUrl(url) {
}; };
return { return {
type: result.name, type: result.name,
url: `https://animekai.to/ajax/links/view?id=${serverIdMap[result.name]}&_=${result.data}` url: `https://anikai.to/ajax/links/view?id=${serverIdMap[result.name]}&_=${result.data}`
}; };
}); });
@@ -172,7 +172,7 @@ async function extractStreamUrl(url) {
const streamResponses = await Promise.all( const streamResponses = await Promise.all(
streamUrls.map(async ({ type, url }) => { streamUrls.map(async ({ type, url }) => {
try { try {
const res = await fetchv2(url); const res = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const json = await res.json(); const json = await res.json();
return { return {
type: type, type: type,
@@ -223,30 +223,46 @@ async function extractStreamUrl(url) {
}; };
const decryptedUrls = await processStreams(streamUrls); const decryptedUrls = await processStreams(streamUrls);
const decryptedDub = decryptedUrls.Dub || decryptedUrls.Sub || decryptedUrls.Softsub; const decryptedSoftsub = decryptedUrls.Softsub || decryptedUrls.Dub || decryptedUrls.Sub;
console.log(decryptedDub);
const headers = { const headers = {
"Referer": "https://animekai.to/", "Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
}; };
if (decryptedDub) { if (decryptedSoftsub) {
const response = await fetchv2(decryptedDub.replace("/e/", "/media/"), headers); const mediaUrl = decryptedSoftsub.replace("/e/", "/media/");
const response = await fetchv2(mediaUrl, headers);
const responseJson = await response.json(); const responseJson = await response.json();
const result = responseJson?.result; const result = responseJson?.result;
const postData = { const postData = {
"text": result, "text": result,
"Useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" "agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
} }
const finalResponse = await fetchv2("https://ilovekai.simplepostrequest.workers.dev/ilovebush", {}, "POST", JSON.stringify(postData)); const finalResponse = await fetchv2("https://enc-dec.app/api/dec-mega", { "Content-Type": "application/json" }, "POST", JSON.stringify(postData));
const finalJson = await finalResponse.json(); const finalJson = await finalResponse.json();
const m3u8Link = finalJson?.result?.sources?.[0]?.file; const m3u8Link = finalJson?.result?.sources?.[0]?.file;
return m3u8Link; const tracks = finalJson?.result?.tracks || [];
const arabicSub = tracks.find(track => track.label && (track.label.includes("Arabic") || track.label.includes("العربية")));
let subtitleUrl = null;
if (arabicSub) {
subtitleUrl = arabicSub.file;
} else {
const englishSub = tracks.find(track => track.label && track.label.includes("English"));
if (englishSub) {
subtitleUrl = englishSub.file;
}
}
const finalResult = {
stream: m3u8Link,
subtitles: subtitleUrl ? "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url="+ encodeURIComponent(subtitleUrl) : null
};
return JSON.stringify(finalResult);
} }
return "error"; return "error";
+28
View File
@@ -0,0 +1,28 @@
{
"sourceName": "AnimeKai Arabic",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/arabic/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+324
View File
@@ -0,0 +1,324 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
console.log(htmlText);
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const types = ["dub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewUrl = `https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`;
const viewRes = await fetchv2(viewUrl);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { dub: "Dubbed English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+28
View File
@@ -0,0 +1,28 @@
{
"sourceName": "AnimeKai (Dub)",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.1.7",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/dub/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+317
View File
@@ -0,0 +1,317 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
console.log(htmlText);
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const types = ["sub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewUrl = `https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`;
const viewRes = await fetchv2(viewUrl);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { sub: "Hardsub English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+28
View File
@@ -0,0 +1,28 @@
{
"sourceName": "AnimeKai (Hardsub)",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.1.6",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/hardsub/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "Animeler",
"iconUrl": "https://animeler.pw/wp-content/uploads/cropped-animelerpw-224x224.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Turkish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeler.pw/",
"searchBaseUrl": "https://animeler.pw/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeler/animeler.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimeMeow",
"iconUrl": "https://files.catbox.moe/5phbht.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animemeow.xyz/",
"searchBaseUrl": "https://animemeow.xyz/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animemeow/animemeow.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeNana",
"iconUrl": "https://animenana.com/favicon.ico",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "English (Hardsub)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animenana.com/",
"searchBaseUrl": "https://animenana.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animenana/animenana.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeNix",
"iconUrl": "https://i3.wp.com/animenix.com/wp-content/uploads/2024/11/cropped-favicon-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.yourupload.com/",
"searchBaseUrl": "https://www.yourupload.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animenix/animenix.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "Animeq",
"iconUrl": "https://animeq.blog/wp-content/uploads/2025/06/Favicon-AnimeQ-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animeq.blog/",
"searchBaseUrl": "https://animeq.blog/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeq/animeq.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimeSaturn",
"iconUrl": "https://www.animesaturn.cx/immagini/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Italian (SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.animesaturn.cx/",
"searchBaseUrl": "https://www.animesaturn.cx/?q=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesaturn/animesaturn.js",
"asyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimesDrive",
"iconUrl": "https://animesdrive.blog/wp-content/uploads/2025/08/cropped-ico-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animesdrive.blog/",
"searchBaseUrl": "https://animesdrive.blog/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesdrive/animesdrive.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimesOnline",
"iconUrl": "https://animesonline.cloud/wp-content/uploads/2025/06/Icone.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animesonline.cloud",
"searchBaseUrl": "https://animesonline.cloud",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesonline/animesonline.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "AnimesRoll",
"iconUrl": "https://cdn.countryflags.com/thumbs/portugal/flag-button-round-250.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.anroll.net/",
"searchBaseUrl": "https://www.anroll.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesroll/animesroll.js",
"type": "animes",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+309
View File
@@ -0,0 +1,309 @@
// AnimeVost for Sora (AsyncJS)
// Author: emp0ry
// Version: 1.0.2
const API_BASE = "https://api.animevost.org/v1/";
const FORM_CT = "application/x-www-form-urlencoded; charset=UTF-8";
const SITE_BASE = "https://animevost.org";
const DEFAULT_SUBTITLE = "https://none.com";
// --- utils ---
function encodeForm(fields) {
return Object.keys(fields)
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(fields[k])}`)
.join("&");
}
async function postForm(url, fields) {
const bodyStr = encodeForm(fields);
try {
const resA = await fetchv2(url, { "Content-Type": FORM_CT }, "POST", bodyStr);
if (resA && typeof resA.text === "function") return resA;
} catch (_) {}
return await fetchv2(url, {
method: "POST",
headers: { "Content-Type": FORM_CT },
body: bodyStr
});
}
async function parseJsonSafe(res) {
const txt = await res.text();
try {
return JSON.parse(txt);
} catch (_) {
return JSON.parse(txt.replace(/^\uFEFF/, "").trim());
}
}
function cleanTitle(raw) {
if (!raw || typeof raw !== "string") return "Unknown title";
let t = raw.split(" /")[0];
return t.replace(/\s*\[.*?\]\s*$/g, "").trim() || "Unknown title";
}
function htmlToText(html) {
if (!html || typeof html !== "string") return "";
return html
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<\/p>/gi, "\n")
.replace(/<[^>]+>/g, "")
.replace(/\n{3,}/g, "\n\n")
.trim();
}
function cleanUrl(url) {
const s = String(url || "").trim();
return s || null;
}
function _safeJsonParse(value, fallback) {
try {
return JSON.parse(value);
} catch (_) {
return fallback;
}
}
// Pack payload into href to avoid relying on global state.
function makeHrefFromPayload(obj) {
return `animevost://payload/${encodeURIComponent(JSON.stringify(obj || {}))}`;
}
function readPayloadFromHref(href) {
const m = String(href || "").match(/^animevost:\/\/payload\/(.+)$/);
if (!m) return null;
try {
return JSON.parse(decodeURIComponent(m[1]));
} catch (_) {
return null;
}
}
function parseIdFromAny(hrefOrId) {
const p = readPayloadFromHref(hrefOrId);
if (p && p.id) return parseInt(p.id, 10);
const m1 = String(hrefOrId || "").match(/^animevost:\/\/release\/(\d+)$/);
if (m1) return parseInt(m1[1], 10);
const m2 = String(hrefOrId || "").match(/[?&]id=(\d+)/);
if (m2) return parseInt(m2[1], 10);
if (/^\d+$/.test(String(hrefOrId || ""))) {
return parseInt(hrefOrId, 10);
}
return null;
}
// Same packing style as AniLiberty / SameBand.
function _packEpisode(payload) {
return "animevost:" + encodeURIComponent(JSON.stringify(payload || {}));
}
function _unpackEpisode(href) {
const raw = String(href || "");
if (!raw.startsWith("animevost:")) return null;
return _safeJsonParse(decodeURIComponent(raw.slice("animevost:".length)), null);
}
function _streamHeaders() {
return {
"User-Agent": "Mozilla/5.0",
"Referer": SITE_BASE + "/"
};
}
// --- search: POST /search ---
async function searchResults(keyword) {
try {
let res = await postForm(API_BASE + "search", { name: String(keyword) });
let json = await parseJsonSafe(res);
if (json?.error || !Array.isArray(json?.data) || json.data.length === 0) {
res = await postForm(API_BASE + "search", { name: `"${String(keyword)}"` });
json = await parseJsonSafe(res);
}
if (json?.error) {
return JSON.stringify([]);
}
const list = Array.isArray(json?.data) ? json.data : [];
if (!list.length) {
return JSON.stringify([]);
}
const tiles = list.map(item => {
const payload = {
id: item.id,
title: cleanTitle(item.title),
description: htmlToText(item.description || ""),
year: item.year || "",
type: item.type || "",
image: item.urlImagePreview || ""
};
return {
title: payload.title,
image: payload.image,
href: makeHrefFromPayload(payload)
};
});
return JSON.stringify(tiles);
} catch (_) {
return JSON.stringify([]);
}
}
// --- details: from payload ---
async function extractDetails(href) {
try {
const p = readPayloadFromHref(href);
const out = [{
description: p?.description || "No description available.",
aliases: `Type: ${p?.type || "Unknown"}`,
airdate: p?.year ? String(p.year) : "Unknown"
}];
return JSON.stringify(out);
} catch (_) {
return JSON.stringify([]);
}
}
// --- episodes: POST /playlist ---
async function extractEpisodes(href) {
try {
const p = readPayloadFromHref(href);
const id = p?.id ?? parseIdFromAny(href);
if (!id) {
return JSON.stringify([]);
}
const res = await postForm(API_BASE + "playlist", { id: String(id) });
const arr = await parseJsonSafe(res);
if (!Array.isArray(arr)) {
return JSON.stringify([]);
}
const out = arr.map((ep, idx) => {
const name = ep?.name || "";
const m = name.match(/(\d+)/);
const num = m ? parseInt(m[1], 10) : (idx + 1);
// AnimeVost usually provides only hd/std.
// Match Sora picker format used in AniLiberty/SameBand:
// hd -> 720p
// std -> 480p
const url1080 = null;
const url720 = cleanUrl(ep?.hd);
const url480 = cleanUrl(ep?.std);
const fallback = url720 || url480;
if (!fallback) return null;
return {
href: _packEpisode({
url1080,
url720,
url480,
fallback
}),
number: num,
title: name || `Episode ${num}`,
image: ep?.preview || ""
};
}).filter(Boolean);
return JSON.stringify(out);
} catch (_) {
return JSON.stringify([]);
}
}
// --- stream: returns quality picker JSON ---
async function extractStreamUrl(href) {
try {
const payload = _unpackEpisode(href);
// Backward compatibility for old AnimeVost episode href format.
if (!payload) {
return href;
}
const url1080 = cleanUrl(payload.url1080);
const url720 = cleanUrl(payload.url720);
const url480 = cleanUrl(payload.url480);
const fallback = cleanUrl(payload.fallback);
const headers = _streamHeaders();
const streams = [];
if (url1080) {
streams.push({
title: "1080p",
streamUrl: url1080,
url1080,
url720,
url480,
headers
});
}
if (url720) {
streams.push({
title: "720p",
streamUrl: url720,
url1080,
url720,
url480,
headers
});
}
if (url480) {
streams.push({
title: "480p",
streamUrl: url480,
url1080,
url720,
url480,
headers
});
}
if (!streams.length && fallback) {
streams.push({
title: "720p",
streamUrl: fallback,
url1080: null,
url720: fallback,
url480: null,
headers
});
}
return JSON.stringify({
streams,
subtitle: DEFAULT_SUBTITLE
});
} catch (_) {
return JSON.stringify({
streams: [],
subtitle: DEFAULT_SUBTITLE
});
}
}
@@ -5,7 +5,7 @@
"name": "emp0ry", "name": "emp0ry",
"icon": "https://avatars.githubusercontent.com/u/64217088" "icon": "https://avatars.githubusercontent.com/u/64217088"
}, },
"version": "1.0.0", "version": "1.0.2",
"language": "Russian", "language": "Russian",
"streamType": "MP4", "streamType": "MP4",
"quality": "720p", "quality": "720p",
@@ -16,5 +16,10 @@
"streamAsyncJS": false, "streamAsyncJS": false,
"softsub": false, "softsub": false,
"type": "anime", "type": "anime",
"downloadSupport": false "downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsShirox": true
} }
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimeYTX",
"iconUrl": "https://i1.wp.com/animeytx.net/wp-content/uploads/2024/09/cropped-hgn584ghj45-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeytx.net/",
"searchBaseUrl": "https://animeytx.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeytx/animeytx.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+114
View File
@@ -0,0 +1,114 @@
async function searchResults(keyword) {
const regex = /<a href="([^"]+)">\s*<img src="([^"]+)"[^>]*>\s*<h3>([^<]+)<\/h3>/g;
const results = [];
try {
const response = await fetchv2("https://animeyy.com/?act=ajax&code=search_manga&keyword=" + encodeURIComponent(keyword));
const html = await response.text();
let match;
while ((match = regex.exec(html)) !== null) {
let title = match[3].trim();
title = title.replace(/&#39;/g, "'").replace(/&quot;/g, '"').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
const imageUrl = "https://animeyy.com/" + match[2].trim();
const passthroughImage = "https://passthrough-worker.simplepostrequest.workers.dev/?simple=" + encodeURIComponent(imageUrl) + "&referer=https://animeyy.com/";
results.push({
title: title,
image: passthroughImage,
href: "https://animeyy.com" + match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const aliasesMatch = html.match(/<strong>Alternative:<\/strong>\s*([^<]+)/);
let aliases = aliasesMatch ? aliasesMatch[1].trim() : "N/A";
aliases = aliases.replace(/&#39;/g, "'").replace(/&quot;/g, '"').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
const descMatch = html.match(/<div id="summary_shortened">([\s\S]*?)<\/div>/);
let description = descMatch ? descMatch[1].trim() : "N/A";
description = description.replace(/&#39;/g, "'").replace(/&quot;/g, '"').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
return JSON.stringify([{
description: description,
aliases: aliases,
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
const episodesRegex = /<li class="wp-manga-chapter"><a href="([^"]+)">(\d+)<\/a>/g;
try {
const response = await fetchv2(url);
const html = await response.text();
let match;
while ((match = episodesRegex.exec(html)) !== null) {
results.push({
href: "https://animeyy.com" + match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results.reverse());
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const iframeMatch = html.match(/<iframe src="([^"]+)"/);
if (iframeMatch) {
const streamUrl = iframeMatch[1].replace('/embed/', '/anime/');
return JSON.stringify({
streams: [
{
title: "Server 1",
streamUrl: streamUrl,
headers: {
Referer: "https://www.animeyy.com/"
}
}
],
subtitle: ""
});
} else {
return JSON.stringify({
streams: [],
subtitle: ""
});
}
} catch (err) {
return JSON.stringify({
streams: [],
subtitle: ""
});
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "Animez",
"iconUrl": "https://files.catbox.moe/gnkywd.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English (Hardsub)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeyy.com/",
"searchBaseUrl": "https://animeyy.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animez/animez.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AniWatch",
"iconUrl": "https://aniwatchtv.to/apple-touch-icon.png?v=0.1",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://megacloud.blog/",
"searchBaseUrl": "https://bshar1865-hianime.vercel.app/api/v2/hianime/search?q=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/aniwatch/aniwatch.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AnimesRoll",
"iconUrl": "https://files.catbox.moe/9lx2bp.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.anroll.net/",
"searchBaseUrl": "https://www.anroll.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anroll/anroll.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "ArabLionz",
"iconUrl": "https://arablionztv.cam/wp-content/uploads/2023/04/vRA6ewKk_400x400-280x280.jpeg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://arablionztv.cam/",
"searchBaseUrl": "https://arablionztv.cam/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/arablionz/arablionz.js",
"type": "shows/movies",
"asyncJS": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "ArabSeed",
"iconUrl": "https://a.asd.homes/wp-content/themes/Elshaikh2021/UI/images/logo333.webp",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://a.asd.homes/",
"searchBaseUrl": "https://a.asd.homes/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/arabseed/arabseed.js",
"type": "shows/movies/anime",
"asyncJS": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+980
View File
@@ -0,0 +1,980 @@
//
//
// Main functions
//
//
const DENO_PROXY_PREFIX = "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=";
const ANIKAI_HOME_TITLE_REGEX = /<title>Home - AnimeKai - Watch Free Anime Online, Stream Subbed &amp; Dubbed Anime in HD<\/title>/i;
const ANIKAI_CHECK_TIMEOUT_MS = 900;
let animekaiBlockCheckPromise = null;
function proxyUrl(url) {
return DENO_PROXY_PREFIX + encodeURIComponent(url);
}
async function isAnimekaiBlockedForUser() {
if (!animekaiBlockCheckPromise) {
animekaiBlockCheckPromise = (async () => {
let timeoutId;
try {
const htmlText = await Promise.race([
fetchv2("https://anikai.to/home").then(response => response.text()),
new Promise(resolve => {
timeoutId = setTimeout(() => resolve(""), ANIKAI_CHECK_TIMEOUT_MS);
})
]);
if (timeoutId) {
clearTimeout(timeoutId);
}
return !ANIKAI_HOME_TITLE_REGEX.test(htmlText);
} catch (error) {
if (timeoutId) {
clearTimeout(timeoutId);
}
console.error("Animekai accessibility check failed:" + error);
return true;
}
})();
}
return animekaiBlockCheckPromise;
}
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const MIN_RESULTS_FAST_RETURN = 8;
const HIGH_CONFIDENCE_SCORE = 900;
const queryNormalized = query.toLowerCase().trim();
const queryTokens = queryNormalized.split(/\s+/).filter(Boolean);
const isSpecificQuery = queryTokens.length <= 2 && queryNormalized.length >= 5;
const decodeHtmlEntities = (str) => {
if (!str) return str;
return str.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
.replace(/&quot;/g, '"')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>');
};
const fuzzyMatch = (query, title) => {
const q = query.toLowerCase().trim();
const t = title.toLowerCase().trim();
if (t === q) return 1000;
if (t.startsWith(q + ' ') || t.startsWith(q + ':') || t.startsWith(q + '-')) return 950;
const wordBoundaryRegex = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
if (wordBoundaryRegex.test(t)) return 900;
const qTokens = q.split(/\s+/).filter(token => token.length > 0);
const tTokens = t.split(/[\s\-:]+/).filter(token => token.length > 0);
const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for', 'with']);
let score = 0;
let exactMatches = 0;
let partialMatches = 0;
let significantMatches = 0;
qTokens.forEach(qToken => {
const isStopword = stopwords.has(qToken);
let bestMatch = 0;
let hasExactMatch = false;
tTokens.forEach(tToken => {
let matchScore = 0;
if (tToken === qToken) {
matchScore = isStopword ? 25 : 120;
hasExactMatch = true;
if (!isStopword) significantMatches++;
}
else if (qToken.includes(tToken) && tToken.length >= 3 && qToken.length <= tToken.length + 2) {
matchScore = isStopword ? 8 : 40;
if (!isStopword) significantMatches++;
}
else if (tToken.startsWith(qToken) && qToken.length >= 3) {
matchScore = isStopword ? 12 : 70;
if (!isStopword) significantMatches++;
}
else if (qToken.length >= 4 && tToken.length >= 4) {
if (Math.abs(qToken.length - tToken.length) > 2) {
return;
}
if (qToken[0] !== tToken[0]) {
return;
}
const dist = levenshteinDistance(qToken, tToken);
const maxLen = Math.max(qToken.length, tToken.length);
const similarity = 1 - (dist / maxLen);
if (similarity > 0.8) {
matchScore = Math.floor(similarity * 60);
if (!isStopword) significantMatches++;
}
}
bestMatch = Math.max(bestMatch, matchScore);
});
if (bestMatch > 0) {
score += bestMatch;
if (hasExactMatch) exactMatches++;
else partialMatches++;
}
});
const significantTokens = qTokens.filter(t => !stopwords.has(t)).length;
const requiredMatches = Math.max(1, Math.ceil(significantTokens * 0.8));
if (significantMatches < requiredMatches) {
return 0;
}
if (exactMatches + partialMatches >= qTokens.length) {
score += 80;
}
score += exactMatches * 20;
const extraWords = tTokens.length - qTokens.length;
if (extraWords > 2) {
score -= (extraWords - 2) * 25;
}
let orderBonus = 0;
for (let i = 0; i < qTokens.length - 1; i++) {
const currentTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i]));
const nextTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i + 1]));
if (currentTokenIndex !== -1 && nextTokenIndex !== -1 && currentTokenIndex < nextTokenIndex) {
orderBonus += 15;
}
}
score += orderBonus;
return Math.max(0, score);
};
const levenshteinDistance = (a, b) => {
const matrix = [];
for (let i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
return matrix[b.length][a.length];
};
const animekaiSearch = async () => {
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
let useProxy = true;
const extractResultsFromHTML = (htmlText) => {
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let i = 0; i < minLength; i++) {
const hrefMatch = posterMatches[i].match(extractHrefRegex);
const fullHref = hrefMatch ? (hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) : null;
const imageMatch = imageMatches[i].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[i].match(extractTitleRegex);
const cleanTitle = titleMatch ? decodeHtmlEntities(titleMatch[1]) : null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: `Animekai:${fullHref}`,
image: useProxy ? proxyUrl(imageSrc) : imageSrc,
title: cleanTitle
});
}
}
return results;
};
try {
const encodedQuery = encodeQuery(query);
useProxy = await isAnimekaiBlockedForUser();
const fetchPages = async (pages) => {
const urls = pages.map(page =>
page === 1
? `${searchBaseUrl}${encodedQuery}`
: `${searchBaseUrl}${encodedQuery}&page=${page}`
);
const responses = await Promise.all(urls.map(url => fetchv2(useProxy ? proxyUrl(url) : url)));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
};
const page1Results = await fetchPages([1]);
return {
page1Results,
fetchRemaining: () => fetchPages([2, 3])
};
} catch (error) {
console.error("Animekai search error:" + error);
return {
page1Results: [],
fetchRemaining: async () => []
};
}
};
const oneMoviesSearch = async () => {
const searchBaseUrl = "https://1movies.bz/browser?keyword=";
const baseUrl = "https://1movies.bz";
const posterHrefRegex = /href="([^"]*)" class="poster"/g;
const titleRegex = /class="title" href="[^"]*">([^<]*)</g;
const imageRegex = /data-src="([^"]*)"/g;
const extractResultsFromHTML = (htmlText) => {
const results = [];
const posterMatches = [...htmlText.matchAll(posterHrefRegex)];
const titleMatches = [...htmlText.matchAll(titleRegex)];
const imageMatches = [...htmlText.matchAll(imageRegex)];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let i = 0; i < minLength; i++) {
const href = posterMatches[i][1];
const fullHref = href.startsWith("http") ? href : baseUrl + href;
const imageSrc = imageMatches[i][1];
const title = decodeHtmlEntities(titleMatches[i][1]);
results.push({ href: fullHref, image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc), title });
}
return results;
};
try {
const encodedQuery = encodeQuery(query);
const fetchPages = async (pages) => {
const urls = pages.map(page =>
page === 1
? `${searchBaseUrl}${encodedQuery}`
: `${searchBaseUrl}${encodedQuery}&page=${page}`
);
const responses = await Promise.all(urls.map(url => fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
};
const page1Results = await fetchPages([1]);
return {
page1Results,
fetchRemaining: () => fetchPages([2, 3])
};
} catch (error) {
console.error("1Movies search error:" + error);
return {
page1Results: [],
fetchRemaining: async () => []
};
}
};
try {
const animekaiPromise = animekaiSearch();
const animekaiData = await animekaiPromise;
const dedupeResults = (results) => {
const seen = new Set();
return results.filter(item => {
const key = `${item.href}|${item.title}`;
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
};
const scoreCache = new Map();
const rankResults = (results) => {
const scoredResults = results.map(r => {
const cacheKey = r.title;
const cached = scoreCache.get(cacheKey);
const score = cached !== undefined ? cached : fuzzyMatch(query, r.title);
if (cached === undefined) {
scoreCache.set(cacheKey, score);
}
return {
...r,
score
};
});
const sorted = scoredResults
.filter(r => r.score > 50)
.sort((a, b) => b.score - a.score);
return {
topScore: sorted[0]?.score || 0,
items: sorted.map(({ score, ...rest }) => rest)
};
};
const shouldFastReturn = (resultCount, topScore) => {
if (resultCount >= MIN_RESULTS_FAST_RETURN) {
return true;
}
return isSpecificQuery && resultCount >= 1 && topScore >= HIGH_CONFIDENCE_SCORE;
};
let mergedResults = dedupeResults([...animekaiData.page1Results]);
let rankedResults = rankResults(mergedResults);
let filteredResults = rankedResults.items;
if (shouldFastReturn(filteredResults.length, rankedResults.topScore)) {
return JSON.stringify(filteredResults);
}
const oneMoviesData = await oneMoviesSearch();
mergedResults = dedupeResults([
...mergedResults,
...oneMoviesData.page1Results
]);
rankedResults = rankResults(mergedResults);
filteredResults = rankedResults.items;
if (shouldFastReturn(filteredResults.length, rankedResults.topScore)) {
return JSON.stringify(filteredResults);
}
const [animekaiExtra, oneMoviesExtra] = await Promise.all([
animekaiData.fetchRemaining(),
oneMoviesData.fetchRemaining()
]);
mergedResults = dedupeResults([
...mergedResults,
...animekaiExtra,
...oneMoviesExtra
]);
rankedResults = rankResults(mergedResults);
filteredResults = rankedResults.items;
return JSON.stringify(filteredResults.length > 0 ? filteredResults : [{
href: "",
image: "",
title: "No results found, please refine query."
}]);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
if (url.startsWith("Animekai:")) {
const actualUrl = url.replace("Animekai:", "").trim();
try {
const useProxy = await isAnimekaiBlockedForUser();
const response = await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl);
const htmlText = await response.text();
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching Animekai details:" + error);
return JSON.stringify([{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}]);
}
} else {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
const descriptionMatch = (/<div class="description text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
const airdateMatch = (/<li>Released:\s*<span[^>]*>(.*?)<\/span>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not aliases",
airdate: airdateMatch ? cleanHtmlSymbols(airdateMatch) : "Not available"
}]);
} catch (error) {
console.error("Error fetching 1Movies details:"+ error);
return JSON.stringify([{
description: "Error loading description",
aliases: "Not available",
airdate: "Not available"
}]);
}
}
}
async function extractEpisodes(url) {
try {
if (url.startsWith("Animekai:")) {
const actualUrl = url.replace("Animekai:", "").trim();
const useProxy = await isAnimekaiBlockedForUser();
const htmlText = await (await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl)).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2(useProxy ? proxyUrl(episodeListUrl) : episodeListUrl)).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `Animekai:https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} else {
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))).text();
const movieIDMatch = (htmlText.match(/<div class="detail-lower"[^>]*id="movie-rating"[^>]*data-id="([^"]+)"/) || [])[1];
if (!movieIDMatch) return JSON.stringify([{ error: "MovieID not found" }]);
const tokenResponse = await fetchv2("https://enc-dec.app/api/enc-movies-flix?text=" + encodeURIComponent(movieIDMatch));
const temp = await tokenResponse.json();
const token = temp.result;
const episodeListUrl = `https://1movies.bz/ajax/episodes/list?id=${movieIDMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+eid="([^"]+)"[^>]+num="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeToken, episodeNum]) => ({
number: parseInt(episodeNum, 10),
href: `https://1movies.bz/ajax/links/list?eid=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
}
} catch (err) {
console.error("Error fetching episodes:" + err);
return JSON.stringify([{ number: 1, href: "Error fetching episodes" }]);
}
}
async function extractStreamUrl(url) {
let source, actualUrl;
if (url.startsWith("Animekai:")) {
source = "Animekai";
actualUrl = url.replace("Animekai:", "").trim();
} else if (url.includes("1movies.bz")) {
source = "1Movies";
actualUrl = url.trim();
} else {
console.log("Failed to match URL:", url);
return "Invalid URL format: " + url;
}
if (source === "Animekai") {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
try {
const useProxy = await isAnimekaiBlockedForUser();
const tokenMatch = actualUrl.match(/token=([^&]+)/);
if (tokenMatch && tokenMatch[1]) {
const rawToken = tokenMatch[1];
const encryptResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encryptData = await encryptResponse.json();
const encryptedToken = encryptData.result;
actualUrl = actualUrl.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
}
const response = await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/;
const softsubRegex = /<div class="server-items lang-group" data-id="softsub"[^>]*>([\s\S]*?)<\/div>/;
const dubRegex = /<div class="server-items lang-group" data-id="dub"[^>]*>([\s\S]*?)<\/div>/;
const subContent = subRegex.exec(serverHtmlSource)?.[1]?.trim() || "";
const softsubContent = softsubRegex.exec(serverHtmlSource)?.[1]?.trim() || "";
const dubContent = dubRegex.exec(serverHtmlSource)?.[1]?.trim() || "";
const extractServerId = (content) => {
if (!content) return null;
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const serverIdDub = extractServerId(dubContent);
const serverIdSoftsub = extractServerId(softsubContent);
const serverIdSub = extractServerId(subContent);
const tokenRequestData = [
{ name: "Dub", data: serverIdDub },
{ name: "Softsub", data: serverIdSoftsub },
{ name: "Sub", data: serverIdSub }
].filter(item => item.data);
const tokenResults = await Promise.all(tokenRequestData.map(item =>
fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(item.data)}`)
.then(res => res.json())
.then(json => ({ name: item.name, data: json.result }))
.catch(err => ({ name: item.name, error: err.toString() }))
));
const serverIdMap = { "Dub": serverIdDub, "Softsub": serverIdSoftsub, "Sub": serverIdSub };
const streamUrls = tokenResults.map(result => ({
type: result.name,
url: `https://anikai.to/ajax/links/view?id=${serverIdMap[result.name]}&_=${result.data}`
}));
const streamResponses = await Promise.all(
streamUrls.map(async ({ type, url }) => {
try {
const res = await fetchv2(useProxy ? proxyUrl(url) : url);
const json = await res.json();
return { type, result: json.result };
} catch (error) {
console.log(`Error fetching ${type} stream:` + error);
return { type, result: null };
}
})
);
const decryptResults = await Promise.all(
streamResponses
.filter(item => item.result)
.map(item =>
fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: item.result })
)
.then(res => res.json())
.then(json => {
console.log(`decrypted${item.type} URL:` + json.result?.url);
return { name: item.type, url: json.result?.url || null };
})
.catch(err => {
console.log(`Error parsing ${item.type} result:` + err);
return { name: item.type, url: null };
})
)
);
const urlMap = Object.fromEntries(decryptResults.map(i => [i.name, i.url]));
const decryptedSub = urlMap.Sub;
const decryptedDub = urlMap.Dub;
const decryptedRaw = urlMap.Softsub;
async function getStream(url) {
try {
if (url.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(url, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
url = iframeSrcMatch[1];
}
}
const response = await fetchv2(url.replace("/e/", "/media/").replace("/e2/", "/media/"), headers);
const responseJson = await response.json();
const result = responseJson?.result;
const finalResponse = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: result, agent: headers["User-Agent"] })
);
const finalJson = await finalResponse.json();
return finalJson?.result?.sources?.[0]?.file || null;
} catch {
return null;
}
}
const [subStream, dubStream, rawStream] = await Promise.all([
decryptedSub ? getStream(decryptedSub) : Promise.resolve(null),
decryptedDub ? getStream(decryptedDub) : Promise.resolve(null),
decryptedRaw ? getStream(decryptedRaw) : Promise.resolve(null)
]);
const streams = [];
async function addStreamQualities(m3u8Link, baseTitle) {
let pushedQualities = 0;
try {
const proxyReqUrl = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link);
const m3u8Response = await fetchv2(proxyReqUrl);
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
const nameMatch = line.match(/NAME="([^"]+)"/i) || line.match(/BANDWIDTH=(\d+)/i);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
} else if (nameMatch && nameMatch[1]) {
quality = nameMatch[1];
}
if (i + 1 < lines.length) {
const streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(streamPath);
} else {
const baseUrl = m3u8Link.substring(0, m3u8Link.lastIndexOf('/') + 1);
absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link) });
}
}
if (subStream) await addStreamQualities(subStream, "Hardsub English");
if (dubStream) await addStreamQualities(dubStream, "Dubbed English");
if (rawStream) await addStreamQualities(rawStream, "Original audio");
const final = { streams, subtitles: "" };
console.log("RETURN: " + JSON.stringify(final));
return JSON.stringify(final);
} catch (error) {
console.log("Animekai fetch error:" + error);
return "https://error.org";
}
} else if (source === "1Movies") {
const headers = {
"Referer": "https://1movies.bz/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
};
try {
const eidMatch = actualUrl.match(/eid=([^&]+)/);
if (eidMatch && eidMatch[1]) {
const rawEpisodeToken = eidMatch[1];
const encryptResponse = await fetchv2(`https://enc-dec.app/api/enc-movies-flix?text=${rawEpisodeToken}`);
const encryptData = await encryptResponse.json();
const encryptedToken = encryptData.result;
actualUrl = actualUrl.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
}
const response = await fetchv2(actualUrl);
const responseData = await response.json();
const cleanedHtml = cleanJsonHtml(responseData.result);
const spanRegex = /<div class="server wnav-item"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(cleanedHtml)) !== null) ids.push(match[1]);
if (ids.length === 0) {
console.log("No servers found");
return "error";
}
const serverId = ids.length > 1 ? ids[1] : ids[0];
const tokenData = await fetchv2(`https://enc-dec.app/api/enc-movies-flix?text=${encodeURIComponent(serverId)}`)
.then(res => res.json());
const token = tokenData.result;
if (!token) {
console.log("Token not found");
return "error";
}
const streamUrl = `https://1movies.bz/ajax/links/view?id=${serverId}&_=${token}`;
const streamResponse = await fetchv2(streamUrl);
const streamData = await streamResponse.json();
if (!streamData.result) {
console.log("Stream result not found");
return "error";
}
const decryptData = await fetchv2(
`https://enc-dec.app/api/dec-movies-flix?text=${streamData.result}`,
headers
).then(res => res.json());
console.log("Decrypted response:" + JSON.stringify(decryptData));
const decryptedUrl = decryptData.result?.url;
if (!decryptedUrl) {
console.log("Decryption failed");
return "error";
}
const subListEncoded = decryptedUrl.split("sub.list=")[1]?.split("&")[0];
let subtitles = "N/A";
if (subListEncoded) {
try {
const subListUrl = decodeURIComponent(subListEncoded);
const subResponse = await fetchv2(subListUrl);
subtitles = await subResponse.json();
} catch {
subtitles = "N/A";
}
}
const englishSubUrl = Array.isArray(subtitles)
? subtitles.find(sub => sub.label === "English")?.file.replace(/\\\//g, "/")
: "N/A";
let finalUrl = decryptedUrl;
if (finalUrl.includes(".to/iframe")) {
try {
const iframeResponse = await fetchv2(finalUrl, headers);
const iframeHtml = await iframeResponse.text();
const iframeSrcMatch = iframeHtml.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
finalUrl = iframeSrcMatch[1];
} else {
console.log("No iframe src found in:", finalUrl);
return "error";
}
} catch (e) {
console.log("Error fetching iframe:", e);
return "error";
}
}
const mediaResponse = await fetchv2(finalUrl.replace("/e/", "/media/"), headers);
const mediaJson = await mediaResponse.json();
const result = mediaJson?.result;
if (!result) {
console.log("Media result not found");
return "error";
}
const finalResponse = await fetchv2(`https://enc-dec.app/api/dec-rapid?text=${encodeURIComponent(result)}&agent=${encodeURIComponent(headers["User-Agent"])}`);
const finalJson = JSON.parse(await finalResponse.text());
const m3u8Link = finalJson?.result?.sources?.[0]?.file;
const streams = [];
if (m3u8Link) {
let pushedQualities = 0;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
const nameMatch = line.match(/NAME="([^"]+)"/i) || line.match(/BANDWIDTH=(\d+)/i);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
} else if (nameMatch && nameMatch[1]) {
quality = nameMatch[1];
}
if (i + 1 < lines.length) {
const streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(streamPath);
} else {
const baseUrl = m3u8Link.substring(0, m3u8Link.lastIndexOf('/') + 1);
absolutePath = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: quality,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({
title: "Source",
streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(m3u8Link)
});
}
}
const returnValue = {
streams,
subtitles: englishSubUrl !== "N/A" ? englishSubUrl : ""
};
console.log("RETURN: " + JSON.stringify(returnValue));
return JSON.stringify(returnValue);
} catch (error) {
console.log("1Movies fetch error:" + error);
return "https://error.org";
}
}
}
///
///
/// Helper functions
///
///
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Ashi (あし) - Literally Everything",
"iconUrl": "https://files.catbox.moe/ptq3a5.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.3.3",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/ashi/ashi.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
@@ -0,0 +1,23 @@
{
"sourceName": "AsiaLiveAction",
"iconUrl": "https://asialiveaction.com/wp-content/uploads/cropped-favicon-200x200.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://asialiveaction.com/",
"searchBaseUrl": "https://asialiveaction.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/asialiveaction/asialiveaction.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+2 -1
View File
@@ -7,5 +7,6 @@
"author": { "author": {
"name": "50/50", "name": "50/50",
"iconURL": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s" "iconURL": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
} },
"supportsLuna": true
} }
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Audible",
"iconUrl": "https://m.media-amazon.com/images/I/51iKw5dFQoL.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English",
"streamType": "MP3",
"quality": "Audio",
"baseUrl": "https://www.audible.co.uk/",
"searchBaseUrl": "https://www.audible.co.uk/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/audible/audible.js",
"type": "audiobooks",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "AZ-Animex",
"iconUrl": "https://www.az-animex.com/wp-content/uploads/2023/11/Logo-icono-144x144-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.az-animex.com/",
"searchBaseUrl": "https://www.az-animex.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/az-animex/az-animex.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+26
View File
@@ -0,0 +1,26 @@
{
"sourceName": "Beatz-Anime",
"iconUrl": "https://files.catbox.moe/vij8dw.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.beatz-anime.net/",
"searchBaseUrl": "https://www.beatz-anime.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/beatz-anime/beatz-anime.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"note": "Use external player!",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
@@ -317,59 +317,58 @@ async function extractEpisodes(url) {
async function extractStreamUrl(ID) { async function extractStreamUrl(ID) {
try { try {
let subtitle = null;
if (ID.includes('movie')) {
const tmdbID = ID.split('/')[2];
const subResponse = await fetchv2(`https://sub.wyzie.ru/search?id=${tmdbID}&format=srt`);
const subtitles = await subResponse.json();
subtitle = subtitles.find(sub => sub.language.toLowerCase() === 'en')?.url || null;
} else if (ID.includes('tv')) {
const parts = ID.split('/');
const tmdbID = parts[2];
const seasonNumber = parts[3];
const episodeNumber = parts[4];
const subResponse = await fetchv2(`https://sub.wyzie.ru/search?id=${tmdbID}&format=srt&season=${seasonNumber}&episode=${episodeNumber}`);
const subtitles = await subResponse.json();
subtitle = subtitles.find(sub => sub.language.toLowerCase() === 'en')?.url || null;
}
const alphaPromise = alpha(ID).then(result => ({ source: 'alpha', data: JSON.parse(result) })).catch(err => ({ source: 'alpha', error: err })); const alphaPromise = alpha(ID).then(result => ({ source: 'alpha', data: JSON.parse(result) })).catch(err => ({ source: 'alpha', error: err }));
const betaPromise = beta(ID).then(result => ({ source: 'beta', data: JSON.parse(result) })).catch(err => ({ source: 'beta', error: err }));
const gammaPromise = gamma(ID).then(result => ({ source: 'gamma', data: JSON.parse(result) })).catch(err => ({ source: 'gamma', error: err })); const gammaPromise = gamma(ID).then(result => ({ source: 'gamma', data: JSON.parse(result) })).catch(err => ({ source: 'gamma', error: err }));
const deltaPromise = delta(ID).then(result => ({ source: 'delta', data: JSON.parse(result) })).catch(err => ({ source: 'delta', error: err })); const deltaPromise = delta(ID).then(result => ({ source: 'delta', data: JSON.parse(result) })).catch(err => ({ source: 'delta', error: err }));
const epsilonPromise = epsilon(ID).then(result => ({ source: 'epsilon', data: JSON.parse(result) })).catch(err => ({ source: 'epsilon', error: err }));
const racePromise = Promise.race([alphaPromise, gammaPromise, deltaPromise]); const racePromise = Promise.race([alphaPromise, gammaPromise, deltaPromise]);
const [fastestResult, betaResult, alphaResult] = await Promise.all([racePromise, betaPromise, alphaPromise]); const [fastestResult, alphaResult] = await Promise.all([racePromise, alphaPromise]);
let epsilonResult = null;
const alphaHas4K = alphaResult && !alphaResult.error && alphaResult.data && alphaResult.data.streams &&
alphaResult.data.streams.some((_, i, arr) => i % 2 === 0 && arr[i] && arr[i].toLowerCase().includes('4k'));
if (!alphaHas4K) {
epsilonResult = await epsilonPromise;
}
const resultStreams = []; const resultStreams = [];
let subtitle = null;
if (fastestResult && !fastestResult.error && fastestResult.data && fastestResult.data.streams && fastestResult.data.streams.length > 0) { if (fastestResult && !fastestResult.error && fastestResult.data && fastestResult.data.streams && fastestResult.data.streams.length > 0) {
const streamUrl = fastestResult.data.streams.length >= 2 ? fastestResult.data.streams[1] : fastestResult.data.streams[0]; let streamUrl;
let headers = {};
if (typeof fastestResult.data.streams[0] === 'string') {
streamUrl = fastestResult.data.streams.length >= 2 ? fastestResult.data.streams[1] : fastestResult.data.streams[0];
} else {
streamUrl = fastestResult.data.streams[0].streamUrl;
headers = fastestResult.data.streams[0].headers || {};
}
resultStreams.push({ resultStreams.push({
title: "Default (1080p)", title: "Default (1080p)",
streamUrl: streamUrl, streamUrl: streamUrl,
headers: fastestResult.data.referer ? { "Referer": fastestResult.data.referer } : {} headers: { ...headers, ...(fastestResult.data.referer ? { "Referer": fastestResult.data.referer } : {}) }
}); });
subtitle = fastestResult.data.subtitles || null;
} else {
if (betaResult && !betaResult.error && betaResult.data && betaResult.data.streams && betaResult.data.streams.length > 0) {
const streamUrl = betaResult.data.streams.length >= 2 ? betaResult.data.streams[1] : betaResult.data.streams[0];
resultStreams.push({
title: "Default (1080p)",
streamUrl: streamUrl,
headers: betaResult.data.referer ? { "Referer": betaResult.data.referer } : {}
});
subtitle = betaResult.data.subtitles || null;
}
} }
if (alphaResult && !alphaResult.error && alphaResult.data && alphaResult.data.streams) { if (alphaResult && !alphaResult.error && alphaResult.data && alphaResult.data.streams) {
const streams = alphaResult.data.streams; const streams = alphaResult.data.streams;
let fourKUrl = null; let fourKUrl = null;
let fourKHeaders = {};
for (let i = 0; i < streams.length; i += 2) { for (let stream of streams) {
if (streams[i] && streams[i].toLowerCase().includes('4k') && streams[i + 1]) { if (stream.title && stream.title.toLowerCase().includes('4k') && stream.streamUrl) {
fourKUrl = streams[i + 1]; fourKUrl = stream.streamUrl;
fourKHeaders = stream.headers || {};
break; break;
} }
} }
@@ -378,36 +377,10 @@ async function extractStreamUrl(ID) {
resultStreams.push({ resultStreams.push({
title: "4K", title: "4K",
streamUrl: fourKUrl, streamUrl: fourKUrl,
headers: { headers: fourKHeaders
"Origin": "https://player.videasy.net",
"Referer": "https://player.videasy.net/"
}
}); });
} else { } else {
console.log('Alpha did not return a 4K stream, checking epsilon as fallback'); console.log('Alpha did not return a 4K stream');
if (epsilonResult && !epsilonResult.error && epsilonResult.data && epsilonResult.data.streams) {
const epsilonStreams = epsilonResult.data.streams;
let epsilonFourKUrl = null;
for (const stream of epsilonStreams) {
if (stream.title && stream.title.includes('4K') && stream.streamUrl) {
epsilonFourKUrl = stream;
break;
}
}
if (epsilonFourKUrl) {
console.log('Using epsilon as 4K fallback provider');
resultStreams.push({
title: "4K",
streamUrl: epsilonFourKUrl.streamUrl,
headers: epsilonFourKUrl.headers || { "Referer": "https://mapple.uk/", "Origin": "https://mapple.uk" }
});
} else {
console.log('Epsilon fallback also did not return a 4K stream');
}
}
} }
} }
console.log(JSON.stringify({ console.log(JSON.stringify({
@@ -457,7 +430,7 @@ async function alpha(ID) {
const imdbId = cinebyData.external_ids?.imdb_id || ''; const imdbId = cinebyData.external_ids?.imdb_id || '';
const tmdbId = cinebyData.id; const tmdbId = cinebyData.id;
const fullUrl = `https://api.videasy.net/myflixerzupcloud/sources-with-title?title=${title}&mediaType=movie&year=${year}&episodeId=1&seasonId=1&tmdbId=${tmdbId}&imdbId=${imdbId}`; const fullUrl = `https://api.videasy.net/cdn/sources-with-title?title=${title}&mediaType=movie&year=${year}&episodeId=1&seasonId=1&tmdbId=${tmdbId}&imdbId=${imdbId}`;
console.log('Full URL:' + fullUrl); console.log('Full URL:' + fullUrl);
@@ -472,9 +445,9 @@ async function alpha(ID) {
const postData = JSON.stringify({ const postData = JSON.stringify({
text: encrypted, text: encrypted,
id: tmdbID id: tmdbID.split('-')[0]
}); });
console.log('Post Data:' + postData);
const decryptedResponse = await fetchv2("https://enc-dec.app/api/dec-videasy", headers, "POST", postData); const decryptedResponse = await fetchv2("https://enc-dec.app/api/dec-videasy", headers, "POST", postData);
const decryptedData = await decryptedResponse.json(); const decryptedData = await decryptedResponse.json();
console.log('Decrypted Data:' + JSON.stringify(decryptedData)); console.log('Decrypted Data:' + JSON.stringify(decryptedData));
@@ -485,15 +458,22 @@ async function alpha(ID) {
const nonHDRSources = sources.filter(s => !s.quality.includes("HDR")); const nonHDRSources = sources.filter(s => !s.quality.includes("HDR"));
const streams = nonHDRSources.flatMap(src => [src.quality, src.url]); const streamObjects = nonHDRSources.map(src => ({
title: src.quality,
streamUrl: src.url,
headers: {
"Origin": "https://player.videasy.net",
"Referer": "https://player.videasy.net/"
}
}));
const englishSubtitle = subtitles.find( const englishSubtitle = subtitles.find(
sub => sub.language.toLowerCase().includes('english') sub => sub.language.toLowerCase().includes('english')
)?.url || null; )?.url || null;
return JSON.stringify({ return JSON.stringify({
streams, streams: streamObjects,
subtitles: englishSubtitle subtitle: englishSubtitle
}); });
} else if (ID.includes('tv')) { } else if (ID.includes('tv')) {
const parts = ID.split('/'); const parts = ID.split('/');
@@ -501,7 +481,7 @@ async function alpha(ID) {
const seasonNumber = parts[3]; const seasonNumber = parts[3];
const episodeNumber = parts[4]; const episodeNumber = parts[4];
const cinebyResponse = await soraFetch(`hhttps://db.videasy.net/3/tv/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`); const cinebyResponse = await soraFetch(`https://db.videasy.net/3/tv/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`);
const cinebyData = await cinebyResponse.json(); const cinebyData = await cinebyResponse.json();
const title = encodeURIComponent(cinebyData.name); const title = encodeURIComponent(cinebyData.name);
@@ -509,7 +489,7 @@ async function alpha(ID) {
const imdbId = cinebyData.external_ids?.imdb_id || ''; const imdbId = cinebyData.external_ids?.imdb_id || '';
const tmdbId = cinebyData.id; const tmdbId = cinebyData.id;
const fullUrl = `https://api.videasy.net/myflixerzupcloud/sources-with-title?title==${title}&mediaType=tv&year=${year}&episodeId=${episodeNumber}&seasonId=${seasonNumber}&tmdbId=${tmdbId}&imdbId=${imdbId}`; const fullUrl = `https://api.videasy.net/cdn/sources-with-title?title=${title}&mediaType=tv&year=${year}&episodeId=${episodeNumber}&seasonId=${seasonNumber}&tmdbId=${tmdbId}&imdbId=${imdbId}`;
console.log('Full URL:' + fullUrl); console.log('Full URL:' + fullUrl);
@@ -524,7 +504,7 @@ async function alpha(ID) {
const postData = JSON.stringify({ const postData = JSON.stringify({
text: encrypted, text: encrypted,
id: tmdbID id: tmdbID.split('-')[0]
}); });
const decryptedResponse = await fetchv2("https://enc-dec.app/api/dec-videasy", headers, "POST", postData); const decryptedResponse = await fetchv2("https://enc-dec.app/api/dec-videasy", headers, "POST", postData);
@@ -537,101 +517,26 @@ async function alpha(ID) {
const nonHDRSources = sources.filter(s => !s.quality.includes("HDR")); const nonHDRSources = sources.filter(s => !s.quality.includes("HDR"));
const streams = nonHDRSources.flatMap(src => [src.quality, src.url]); const streamObjects = nonHDRSources.map(src => ({
title: src.quality,
streamUrl: src.url,
headers: {
"Origin": "https://player.videasy.net",
"Referer": "https://player.videasy.net/"
}
}));
const englishSubtitle = subtitles.find( const englishSubtitle = subtitles.find(
sub => sub.language.toLowerCase().includes('english') sub => sub.language.toLowerCase().includes('english')
)?.url || null; )?.url || null;
return JSON.stringify({ return JSON.stringify({
streams, streams: streamObjects,
subtitles: englishSubtitle subtitle: englishSubtitle
}); });
} }
} }
async function beta(ID) {
if (ID.includes('movie')) {
const tmdbID = ID.replace('/movie/', '');
const headersOne = {
"Content-Type": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI2NTQ0MWU0MTg4NjhhMWI0NDZiM2I0Mzg1MmE4OWQ2NyIsIm5iZiI6MTYzMDg4NDI0My40NzMsInN1YiI6IjYxMzU1MTkzZmQ0YTk2MDA0NDVkMTJjNiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Hm0W-hUx-7ph-ASvk2TpMxZbMtwVa5DEXWcgNgcqXJM",
"Referer": "https://player.smashystream.com/",
"Origin": "https://player.smashystream.com",
"X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"
};
const tmdbResponse = await fetchv2(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/${tmdbID}?append_to_response=external_ids`)}&simple=true`, headersOne);
const tmdbData = await tmdbResponse.json();
const imdbID = tmdbData.imdb_id;
const streamResponse = await ilovefeet(imdbID, false, null, null, 'm3u8');
const streams = [];
if (streamResponse && streamResponse.defaultUrl) {
streams.push("1080p", streamResponse.defaultUrl);
}
if (streamResponse && streamResponse.vFastUrl) {
const fourKResult = await ilovearmpits(streamResponse.vFastUrl);
if (fourKResult.available && fourKResult.url) {
streams.push("4K", fourKResult.url);
}
}
const final = {
streams,
subtitles: streamResponse.subtitles || "None",
referer: "https://vidfast.pro/"
};
console.log(JSON.stringify(final));
return JSON.stringify(final);
} else if (ID.includes('tv')) {
const parts = ID.split('/');
const tmdbID = parts[2];
const seasonNumber = parts[3];
const episodeNumber = parts[4];
console.log(`TMDB ID: ${tmdbID}, Season: ${seasonNumber}, Episode: ${episodeNumber}`);
const headersOne = {
"Content-Type": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI2NTQ0MWU0MTg4NjhhMWI0NDZiM2I0Mzg1MmE4OWQ2NyIsIm5iZiI6MTYzMDg4NDI0My40NzMsInN1YiI6IjYxMzU1MTkzZmQ0YTk2MDA0NDVkMTJjNiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Hm0W-hUx-7ph-ASvk2TpMxZbMtwVa5DEXWcgNgcqXJM",
"Referer": "https://player.smashystream.com/",
"Origin": "https://player.smashystream.com",
"X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"
};
const tmdbResponse = await fetchv2(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${tmdbID}?append_to_response=external_ids`)}&simple=true`, headersOne);
const tmdbData = await tmdbResponse.json();
const imdbID = tmdbData.external_ids.imdb_id;
const streamResponse = await ilovefeet(imdbID, true, seasonNumber, episodeNumber, 'm3u8');
const streams = [];
if (streamResponse && streamResponse.defaultUrl) {
streams.push("1080p", streamResponse.defaultUrl);
}
if (streamResponse && streamResponse.vFastUrl) {
const fourKResult = await ilovearmpits(streamResponse.vFastUrl);
if (fourKResult.available && fourKResult.url) {
streams.push("4K", fourKResult.url);
}
}
const final = {
streams,
subtitles: streamResponse.subtitles || "None",
referer: "https://vidfast.pro/"
};
console.log(JSON.stringify(final));
return JSON.stringify(final);
}
}
async function gamma(ID) { async function gamma(ID) {
if (ID.includes('movie')) { if (ID.includes('movie')) {
const parts = ID.split('/'); const parts = ID.split('/');
@@ -690,9 +595,9 @@ async function delta(ID) {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"X-Api-Key": "24ef089ebcab51d107a4e4709e87861ef609bace89ac23af13235f6ea743488f" "X-Api-Key": "24ef089ebcab51d107a4e4709e87861ef609bace89ac23af13235f6ea743488f"
}; };
const response = await fetchv2(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://themoviedb.hexa.watch/api/tmdb/movie/${ID.split('/')[2]}/images`)}&simple=true`, headersOne); const response = await fetchv2(`https://themoviedb.hexa.su/api/tmdb/movie/${ID.split('/')[2]}/images`, headersOne);
const data = await response.text(); const data = await response.text();
console.log('Encrypted Data:' + data);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept": "application/json", "Accept": "application/json",
@@ -725,8 +630,7 @@ async function delta(ID) {
return JSON.stringify({ return JSON.stringify({
streams, streams,
subtitles: englishSubtitle, subtitles: englishSubtitle
referer: "https://hexa.watch/"
}); });
} else if (ID.includes('tv')) { } else if (ID.includes('tv')) {
const parts = ID.split('/'); const parts = ID.split('/');
@@ -737,7 +641,7 @@ async function delta(ID) {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"X-Api-Key": "24ef089ebcab51d107a4e4709e87861ef609bace89ac23af13235f6ea743488f" "X-Api-Key": "24ef089ebcab51d107a4e4709e87861ef609bace89ac23af13235f6ea743488f"
}; };
const response = await fetchv2(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://themoviedb.hexa.watch/api/tmdb/tv/${tmdbID}/season/${seasonNumber}/episode/${episodeNumber}/images`)}&simple=true`, headersOne); const response = await fetchv2(`https://themoviedb.hexa.su/api/tmdb/tv/${tmdbID}/season/${seasonNumber}/episode/${episodeNumber}/images`, headersOne);
const data = await response.text(); const data = await response.text();
const headers = { const headers = {
@@ -772,208 +676,11 @@ async function delta(ID) {
return JSON.stringify({ return JSON.stringify({
streams, streams,
subtitles: null, subtitles: englishSubtitle
referer: "https://hexa.watch/"
}); });
} }
} }
async function epsilon(ID) {
const actionIdentifier = await ilovethighs("https://mapple.uk/watch/movie/1061474-superman");
console.log("Action Identifier: " + actionIdentifier);
const sessionResponse = await soraFetch("https://enc-dec.app/api/enc-mapple");
const sessionData = await sessionResponse.json();
console.log("Session Data: " + JSON.stringify(sessionData));
const sessionID = sessionData.result.sessionId || "N/A";
console.log("ID: " + ID);
if (ID.startsWith('/movie/') || ID.startsWith('movie/')) {
const match = ID.match(/movie\/(\d+)/);
const tmdbID = match ? match[1] : '';
console.log("Extracted TMDB ID: " + tmdbID);
const headers = {
"Content-Type": "text/plain",
"next-action": actionIdentifier
};
const postData = `[{"mediaId":${tmdbID},"mediaType":"movie","tv_slug":"","source":"mapple","useFallbackVideo":false,"sessionId":"${sessionID}"}]`;
const response = await fetchv2("https://mapple.uk/watch/"+ ID, headers, "POST", postData);
const data = await response.text();
console.log("Stream data: " + data);
const lines = data.split('\n');
let streamData = null;
for (const line of lines) {
if (line.includes('"success":true')) {
streamData = JSON.parse(line.substring(line.indexOf('{')));
break;
}
}
if (streamData && streamData.data && streamData.data.stream_url) {
const m3u8Response = await soraFetch(streamData.data.stream_url, {
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
});
const m3u8Text = await m3u8Response.text();
console.log("M3U8 Playlist: " + m3u8Text);
const streams = [];
const lines = m3u8Text.split('\n');
const resolutionNames = {
"3840x2160": "4K",
"1920x1080": "1080p",
"1280x720": "720p",
"640x360": "360p"
};
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXT-X-STREAM-INF')) {
const resolutionMatch = lines[i].match(/RESOLUTION=(\d+x\d+)/);
const streamUrl = lines[i + 1];
if (resolutionMatch && streamUrl && streamUrl.trim()) {
const resolution = resolutionMatch[1];
const friendlyName = resolutionNames[resolution] || resolution;
streams.push({
title: friendlyName,
streamUrl: streamUrl.trim(),
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
});
}
}
}
let englishSubtitleUrl = "";
try {
const subResponse = await soraFetch(`https://mapple.uk/api/subtitles?id=${tmdbID}&mediaType=movie`);
const subData = await subResponse.json();
const englishSub = subData.find(sub => sub.language === "en");
if (englishSub) {
englishSubtitleUrl = englishSub.url;
}
} catch (e) {
englishSubtitleUrl = "";
}
console.log("English Subtitle URL: " + englishSubtitleUrl);
console.log("Extracted streams: " + JSON.stringify(streams));
return JSON.stringify({
streams: streams.length > 0 ? streams : [
{
title: "Mapple Server",
streamUrl: streamData.data.stream_url,
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
}
],
subtitle: englishSubtitleUrl || ""
});
} else {
throw new Error("Failed to extract stream URL");
}
} else if (ID.startsWith('/tv/') || ID.startsWith('tv/')) {
const match = ID.match(/tv\/(\d+)-(\d+)-(\d+)/);
if (!match) throw new Error("Invalid TV URL format");
const tmdbID = match[1];
const seasonNumber = match[2];
const episodeNumber = match[3];
const headers = {
"Content-Type": "text/plain",
"next-action": actionIdentifier
};
const postData = `[{"mediaId":${tmdbID},"mediaType":"tv","tv_slug":"${seasonNumber}-${episodeNumber}","source":"mapple","useFallbackVideo":false,"sessionId":"${sessionID}"}]`;
const response = await fetchv2("https://mapple.uk/watch"+ ID, headers, "POST", postData);
const data = await response.text();
console.log("Stream data: " + data);
const lines = data.split('\n');
let streamData = null;
for (const line of lines) {
if (line.includes('"success":true')) {
streamData = JSON.parse(line.substring(line.indexOf('{')));
break;
}
}
if (streamData && streamData.data && streamData.data.stream_url) {
const m3u8Response = await soraFetch(streamData.data.stream_url, {
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
});
const m3u8Text = await m3u8Response.text();
console.log("M3U8 Playlist: " + m3u8Text);
const streams = [];
const lines = m3u8Text.split('\n');
const resolutionNames = {
"3840x2160": "4K",
"1920x1080": "1080p",
"1280x720": "720p",
"640x360": "360p"
};
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXT-X-STREAM-INF')) {
const resolutionMatch = lines[i].match(/RESOLUTION=(\d+x\d+)/);
const streamUrl = lines[i + 1];
if (resolutionMatch && streamUrl && streamUrl.trim()) {
const resolution = resolutionMatch[1];
const friendlyName = resolutionNames[resolution] || resolution;
streams.push({
title: friendlyName,
streamUrl: streamUrl.trim(),
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
});
}
}
}
let englishSubtitleUrl = "";
try {
const subResponse = await soraFetch(`https://mapple.uk/api/subtitles?id=${tmdbID}&mediaType=tv&season=${seasonNumber}&episode=${episodeNumber}`);
const subData = await subResponse.json();
const englishSub = subData.find(sub => sub.language === "en");
if (englishSub) {
englishSubtitleUrl = englishSub.url;
}
} catch (e) {
englishSubtitleUrl = "";
}
console.log("English Subtitle URL: " + englishSubtitleUrl);
console.log("Extracted streams: " + JSON.stringify(streams));
return JSON.stringify({
streams: streams.length > 0 ? streams : [
{
title: "Mapple Server",
streamUrl: streamData.data.stream_url,
headers: {
"referer": "https://mapple.uk/",
"origin": "https://mapple.uk"
}
}
],
subtitle: englishSubtitleUrl || ""
});
} else {
throw new Error("Failed to extract stream URL");
}
}
}
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) { async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) {
try { try {
return await fetchv2( return await fetchv2(
@@ -1362,50 +1069,7 @@ async function ilovefeet(imdbId, isSeries = false, season = null, episode = null
}; };
} }
async function ilovethighs(watchUrl) {
const htmlResponse = await soraFetch(watchUrl);
const htmlText = await htmlResponse.text();
const layoutMatch = htmlText.match(/<script[^>]*src="([^"]*app\/watch\/movie\/[^"]*layout-[^"]*\.js)"[^>]*><\/script><link rel="preload"/);
if (!layoutMatch || !layoutMatch[1]) {
throw new Error("error 1");
}
const beforeLayoutMatch = htmlText.match(/<script[^>]*src="([^"]*\.js)"[^>]*><\/script><script[^>]*src="[^"]*app\/watch\/(movie|tv)\/[^"]*layout-[^"]*\.js"/);
if (!beforeLayoutMatch || !beforeLayoutMatch[1]) {
throw new Error("error 2");
}
let targetUrl = beforeLayoutMatch[1];
if (targetUrl.startsWith('/_next/')) {
targetUrl = 'https://mapple.uk' + targetUrl;
} else if (!targetUrl.startsWith('http')) {
targetUrl = 'https://mapple.uk/' + targetUrl;
}
try {
const response = await soraFetch(targetUrl);
const text = await response.text();
let actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"[^"]*"getStreamUrl/);
if (!actionMatch) {
actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"/);
}
if (!actionMatch) {
actionMatch = text.match(/"([a-f0-9]{40,})"[^"]*"getStreamUrl/);
}
if (actionMatch && actionMatch[1]) {
return actionMatch[1];
}
} catch (e) {
throw new Error("error 3: " + e);
}
throw new Error("error 4");
}
function wordArrayToUint8Array(wordArray) { function wordArrayToUint8Array(wordArray) {
const words = wordArray.words; const words = wordArray.words;
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Tsumi (詰み) - Literally Everything 2.0",
"iconUrl": "https://files.catbox.moe/krovkt.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.7",
"language": "English",
"streamType": "HLS",
"quality": "4K",
"baseUrl": "https://google.com/",
"searchBaseUrl": "https://google.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/checkmate/checkmate.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "CineCalidad",
"iconUrl": "https://www.cinecalidad.ec/wp-content/themes/Cinecalidad/assets/img/favicon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.cinecalidad.ec/",
"searchBaseUrl": "https://www.cinecalidad.ec/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cinecalidad/cinecalidad.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+25
View File
@@ -0,0 +1,25 @@
{
"sourceName": "CKSub",
"iconUrl": "https://cksub.org/wp-content/uploads/2024/12/cropped-i-am-the-fated-villain-gu-changge-192x192.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Chinese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://cksub.org/",
"searchBaseUrl": "https://cksub.org/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cksub/cksub.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Cloudy",
"iconUrl": "https://cloudy.pk/wp-content/uploads/2018/02/cloudy.pk-icon-200x200.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Hindi (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://cloudy.pk/",
"searchBaseUrl": "https://cloudy.pk/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cloudy/cloudy.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+32 -2
View File
@@ -15,6 +15,36 @@ const allowEnglishInLanguages = false; // [true, false]
const removeUnknownLanguages = false; // [true, false] const removeUnknownLanguages = false; // [true, false]
// Settings end // Settings end
function btoa(str) {
if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function" && globalThis.btoa !== btoa) {
return globalThis.btoa(str);
}
if (typeof Buffer !== "undefined") {
return Buffer.from(str, "utf8").toString("base64");
}
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let result = "";
let i = 0;
while (i < str.length) {
const byte1 = str.charCodeAt(i++) & 0xff;
const hasByte2 = i < str.length;
const byte2 = hasByte2 ? str.charCodeAt(i++) & 0xff : 0;
const hasByte3 = i < str.length;
const byte3 = hasByte3 ? str.charCodeAt(i++) & 0xff : 0;
const chunk = (byte1 << 16) | (byte2 << 8) | byte3;
result += chars[(chunk >> 18) & 63];
result += chars[(chunk >> 12) & 63];
result += hasByte2 ? chars[(chunk >> 6) & 63] : "=";
result += hasByte3 ? chars[chunk & 63] : "=";
}
return result;
}
async function searchResults(keyword) { async function searchResults(keyword) {
try { try {
const moviesresponse = await fetchv2( const moviesresponse = await fetchv2(
@@ -186,8 +216,8 @@ async function extractStreamUrl(ID) {
try { try {
const endpoint = type === "movie" const endpoint = type === "movie"
? "https://comet.elfhosted.com/" + encodedConfig + "/stream/movie/" + actualID + ".json" ? "https://comet.feels.legal/" + encodedConfig + "/stream/movie/" + actualID + ".json"
: "https://comet.elfhosted.com/" + encodedConfig + "/stream/series/" + actualID + ".json"; : "https://comet.feels.legal/" + encodedConfig + "/stream/series/" + actualID + ".json";
const response = await fetchv2(endpoint); const response = await fetchv2(endpoint);
const data = await response.json(); const data = await response.json();
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Comet",
"iconUrl": "https://i.imgur.com/jmVoVMu.jpeg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "English",
"streamType": "MKV",
"quality": "4K",
"baseUrl": "https://www.google.com/",
"searchBaseUrl": "https://www.google.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/comet/comet.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"settings": true,
"supportsLuna": true
}
@@ -0,0 +1,27 @@
{
"sourceName": "CrimsonFanSubs",
"iconUrl": "https://i3.wp.com/crimsonfansubs.com/wp-content/uploads/2021/06/cropped-favicon-image-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Chinese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://crimsonfansubs.com/",
"searchBaseUrl": "https://crimsonfansubs.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/crimsonfansubs/crimsonfansubs.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true,
"supportsShirox": true
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Cuevana3",
"iconUrl": "https://www.cuevana3.eu/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.cuevana3.eu/",
"searchBaseUrl": "https://www.cuevana3.eu/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cuevana3/cuevana3.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true,
"supportsTsumi": true,
"supportsHiyoku": true
}

Some files were not shown because too many files have changed in this diff Show More