Compare commits
559 commits
screenshot
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff3d68bac | ||
|
|
8834f32d40 | ||
|
|
57ad661c74 | ||
|
|
cc12b0a9a9 | ||
|
|
f737ea3d5d | ||
|
|
17205d5285 | ||
|
|
5435f605c9 | ||
|
|
faddc125f6 | ||
|
|
e4e490cf63 | ||
|
|
b5807a2928 | ||
|
|
354fd00768 | ||
|
|
7684b0e0ba | ||
|
|
68401c05f1 | ||
|
|
55542460d7 | ||
|
|
602276cd35 | ||
|
|
a03726e0a9 | ||
|
|
61b3c81373 | ||
|
|
e4da9ad453 | ||
|
|
666e58aa4f | ||
|
|
f4ab4a1354 | ||
|
|
c21045ffed | ||
|
|
34d0c6c6a4 | ||
|
|
0d9a818e6d | ||
|
|
e3c5fd0deb | ||
|
|
ba3170bba0 | ||
|
|
67a32248e0 | ||
|
|
6285f9b05a | ||
|
|
7b06e8061e | ||
|
|
90647a7f0a | ||
|
|
9f170b7a76 | ||
|
|
24271a087e | ||
|
|
f3e845b3b7 | ||
|
|
b7ea56d72d | ||
|
|
2801a25ddf | ||
|
|
39dfd2b586 | ||
|
|
814f585057 | ||
|
|
5202a7740e | ||
|
|
6375baee7a | ||
|
|
c8a773c9fd | ||
|
|
1744099791 | ||
|
|
cc90b4105b | ||
|
|
7495be975e | ||
|
|
de9bc757c4 | ||
|
|
8265ec53e3 | ||
|
|
d570df212d | ||
|
|
4b76a30e3f | ||
|
|
791826c3c9 | ||
|
|
dffb779403 | ||
|
|
499197a59d | ||
|
|
e869c3cfee | ||
|
|
cea82dd040 | ||
|
|
970fe5d81c | ||
|
|
3adc787cf6 | ||
|
|
0b89b4a917 | ||
|
|
37ef74bf5d | ||
|
|
ce2cc568a2 | ||
|
|
5d54fe8b25 | ||
|
|
900c891713 | ||
|
|
5fe963dce4 | ||
|
|
76c1a241a0 | ||
|
|
3f361d56ae | ||
|
|
ca6d0d04a1 | ||
|
|
2cae2d260a | ||
|
|
c27b6649b3 | ||
|
|
752056a02e | ||
|
|
458f266f18 | ||
|
|
c7edf0fd01 | ||
|
|
e83cec01e2 | ||
|
|
301d94c269 | ||
|
|
6ae0c65e68 | ||
|
|
3f284a05fc | ||
|
|
3bbb1599ee | ||
|
|
1b69828fb6 | ||
|
|
e2e3b526c3 | ||
|
|
7f2eca5840 | ||
|
|
c910bb9f7c | ||
|
|
c0f96a3e6d | ||
|
|
7bd3c80c60 | ||
|
|
463425746e | ||
|
|
b85ec3aba2 | ||
|
|
d5113bedc5 | ||
|
|
a5122ac96e | ||
|
|
1b84c01390 | ||
|
|
01835f3958 | ||
|
|
48dbe1386a | ||
|
|
9ac4a08379 | ||
|
|
24605be091 | ||
|
|
7ead8db4d8 | ||
|
|
80130a357f | ||
|
|
16bb2126f1 | ||
|
|
979e998f55 | ||
|
|
92f716c547 | ||
|
|
21c0e197d1 | ||
|
|
c2ebf8cce6 | ||
|
|
263fc76e3e | ||
|
|
d2752504e5 | ||
|
|
9ef890773e | ||
|
|
36451c230c | ||
|
|
3e3cdb94c4 | ||
|
|
4bc64789c5 | ||
|
|
59ce118eec | ||
|
|
09e79f6e59 | ||
|
|
40e83490c7 | ||
|
|
4afd9461ad | ||
|
|
3c667e59f5 | ||
|
|
d972abbe87 | ||
|
|
eebb7d72ed | ||
|
|
2ca9be2319 | ||
|
|
decd0b9759 | ||
|
|
c62e132126 | ||
|
|
7eedf0449c | ||
|
|
11b9223b88 | ||
|
|
fd4ffedef3 | ||
|
|
9e370b16aa | ||
|
|
8bde27c857 | ||
|
|
a9a6aed1bb | ||
|
|
71c81f448a | ||
|
|
333d7aef69 | ||
|
|
55d4f1745f | ||
|
|
8671db9ef3 | ||
|
|
e76efcbd7c | ||
|
|
37c3b1b179 | ||
|
|
ea22b7f4cd | ||
|
|
34c818a2cd | ||
|
|
e6d839506f | ||
|
|
94f64a2e79 | ||
|
|
ce8be66d86 | ||
|
|
81517a2bcd | ||
|
|
85881230e3 | ||
|
|
4cc8f54fa4 | ||
|
|
6d971cb4fb | ||
|
|
9cf14d0d46 | ||
|
|
f54f71d3ff | ||
|
|
e59d703484 | ||
|
|
d8f5cb3975 | ||
|
|
86d4b73257 | ||
|
|
8046a81d16 | ||
|
|
64635de462 | ||
|
|
73c6509c4e | ||
|
|
e936b60623 | ||
|
|
ff2a7fa557 | ||
|
|
8eb3ccb793 | ||
|
|
3906233019 | ||
|
|
96f40a2e9c | ||
|
|
6bba7e0d4b | ||
|
|
1664c238ff | ||
|
|
aa0a0563d6 | ||
|
|
76e17aed9d | ||
|
|
e18af3aa79 | ||
|
|
26bb82df8a | ||
|
|
c970fb211e | ||
|
|
fb5640ec3b | ||
|
|
bfdebcfa5e | ||
|
|
d731680b1d | ||
|
|
e523e14ca3 | ||
|
|
8084885244 | ||
|
|
7695344aee | ||
|
|
b69c5dfa5f | ||
|
|
cbdb59f795 | ||
|
|
2b764e40ef | ||
|
|
8b68030674 | ||
|
|
2d6b41c90e | ||
|
|
f44ce1b6f9 | ||
|
|
9a626c5d9f | ||
|
|
d7bce6a7fc | ||
|
|
2403ce71c2 | ||
|
|
68e5849bc9 | ||
|
|
3e6b0a4f16 | ||
|
|
7dd7b2e8ab | ||
|
|
8395d2df9e | ||
|
|
7584c28bb5 | ||
|
|
0ff2f005be | ||
|
|
ebba6a94a5 | ||
|
|
fb18ee975f | ||
|
|
10c9d4b15e | ||
|
|
7bf707d8bc | ||
|
|
2a829ea3fd | ||
|
|
5bd1347feb | ||
|
|
3df56430c1 | ||
|
|
dee597d41d | ||
|
|
c74156e01a | ||
|
|
6232ed7aed | ||
|
|
b021c84478 | ||
|
|
9f1f00dd18 | ||
|
|
567b59f67c | ||
|
|
b05a2bfb4e | ||
|
|
85a3996ff6 | ||
|
|
a37074b6dd | ||
|
|
e3812c168f | ||
|
|
21dba5bf72 | ||
|
|
20d6ba6c19 | ||
|
|
e159fe9b16 | ||
|
|
65a07d6796 | ||
|
|
bab407b3e3 | ||
|
|
423e1da501 | ||
|
|
44c8514b4e | ||
|
|
37e3b206a1 | ||
|
|
a5a48a8760 | ||
|
|
36c1fbbe94 | ||
|
|
5c85c33dae | ||
|
|
bf58a599ec | ||
|
|
54bfec70e1 | ||
|
|
2392a10c5f | ||
|
|
62741a79f2 | ||
|
|
abd7e06eea | ||
|
|
c8d5613288 | ||
|
|
8f121c136e | ||
|
|
95945137da | ||
|
|
0a81a9ab82 | ||
|
|
e89de932e3 | ||
|
|
915d339351 | ||
|
|
8b70217248 | ||
|
|
36d6304837 | ||
|
|
2e14f3ad8e | ||
|
|
966cea943b | ||
|
|
468c9757e4 | ||
|
|
ca0cd15152 | ||
|
|
e6d1dbe356 | ||
|
|
3d4284baad | ||
|
|
a3a4479922 | ||
|
|
6514cdd36e | ||
|
|
67c14d0f95 | ||
|
|
ebcd1065b2 | ||
|
|
f3f8aef7d0 | ||
|
|
66969de042 | ||
|
|
683effe550 | ||
|
|
e5cfb41c40 | ||
|
|
a7faa05038 | ||
|
|
076005c715 | ||
|
|
90c8ce01cb | ||
|
|
f6386c5da8 | ||
|
|
49363694a4 | ||
|
|
8a5ecf0a85 | ||
|
|
9c5127f54e | ||
|
|
fd97370fcc | ||
|
|
37222cbb84 | ||
|
|
07545f8843 | ||
|
|
9c4260cef2 | ||
|
|
5e6f35dd22 | ||
|
|
9942e332a7 | ||
|
|
2c2c04a897 | ||
|
|
c1d2b9f38c | ||
|
|
a51943bac4 | ||
|
|
1b496ac313 | ||
|
|
e0901a7154 | ||
|
|
3c5696e6a5 | ||
|
|
6bf8fa6018 | ||
|
|
86922fff77 | ||
|
|
8784a20560 | ||
|
|
9b9150366f | ||
|
|
9f614fe664 | ||
|
|
2428842da3 | ||
|
|
6f9727883f | ||
|
|
2940e029cd | ||
|
|
6161949090 | ||
|
|
d5e911ad26 | ||
|
|
ac1a8f19cc | ||
|
|
1e01f73fb4 | ||
|
|
2c97ce81cb | ||
|
|
c0edb5c0c5 | ||
|
|
b9224f725a | ||
|
|
baa8ddd6a5 | ||
|
|
c83d3a7472 | ||
|
|
8da19368c7 | ||
|
|
f423f60380 | ||
|
|
6f47acd819 | ||
|
|
8149b4c0b1 | ||
|
|
2d1def7ac9 | ||
|
|
1141e53c0b | ||
|
|
fd074790e8 | ||
|
|
c19638eb6d | ||
|
|
433ab9ceea | ||
|
|
a6eaf30579 | ||
|
|
0c7eccf180 | ||
|
|
c635cffdf2 | ||
|
|
7414887be2 | ||
|
|
749861af61 | ||
|
|
8c6cd36d9c | ||
|
|
20668f2829 | ||
|
|
c7d2cae26b | ||
|
|
9f97699b53 | ||
|
|
7778873a09 | ||
|
|
92529e06a9 | ||
|
|
e2d52be133 | ||
|
|
4e6ac0bdb4 | ||
|
|
ed39ba33f7 | ||
|
|
52c55a8d85 | ||
|
|
8522088783 | ||
|
|
8541a92f83 | ||
|
|
c163b9fed4 | ||
|
|
afd8d6ba31 | ||
|
|
cf661ba909 | ||
|
|
7d1fd73dc4 | ||
|
|
f4bf7c24b0 | ||
|
|
caf6f36eef | ||
|
|
5075d6b75a | ||
|
|
c5e181a82b | ||
|
|
5eb0fbd513 | ||
|
|
16122da36d | ||
|
|
5ee227b6de | ||
|
|
dc979b30d9 | ||
|
|
d173331829 | ||
|
|
53bf4ac3b3 | ||
|
|
a4d2b620a3 | ||
|
|
a5a85aee35 | ||
|
|
828b9b89b0 | ||
|
|
12287770b0 | ||
|
|
88dc90f2d7 | ||
|
|
5a539c5d0b | ||
|
|
cfd05732c6 | ||
|
|
e9865029b4 | ||
|
|
a97e16a072 | ||
|
|
f5eb6611dc | ||
|
|
a82dcee6b2 | ||
|
|
e6f460d86d | ||
|
|
ae46720fbf | ||
|
|
7d18d6c1eb | ||
|
|
81031978a0 | ||
|
|
4388fb03ff | ||
|
|
1b61f97fae | ||
|
|
c1e1da1bf8 | ||
|
|
c88780ccfb | ||
|
|
eb9bb3b8f2 | ||
|
|
d54e6a5794 | ||
|
|
099e52f5b2 | ||
|
|
d7e8142433 | ||
|
|
1e935c6082 | ||
|
|
f862f29c26 | ||
|
|
f775ee2118 | ||
|
|
806b1fed32 | ||
|
|
e80a70f2b0 | ||
|
|
63797dcfcd | ||
|
|
62913d55f1 | ||
|
|
0c6dc2b586 | ||
|
|
528ba54c6f | ||
|
|
c8ca805ecb | ||
|
|
d2ecb573b7 | ||
|
|
d914a25cce | ||
|
|
576a2e5b1e | ||
|
|
8b7039b5e3 | ||
|
|
60f1e30475 | ||
|
|
c877345aac | ||
|
|
86919cdff2 | ||
|
|
9eea568829 | ||
|
|
c03aa7986e | ||
|
|
2e830863ff | ||
|
|
9957b9b213 | ||
|
|
eb222dedb8 | ||
|
|
d551f71bf9 | ||
|
|
7c345c3279 | ||
|
|
1dc88b7d7a | ||
|
|
cfc7da44dd | ||
|
|
ab6b766459 | ||
|
|
8ea195e5f1 | ||
|
|
b1f2dc23aa | ||
|
|
d7d9c859cf | ||
|
|
9b8ba7b7f1 | ||
|
|
1302f70b66 | ||
|
|
96203e8a93 | ||
|
|
924976161c | ||
|
|
d87042a0a0 | ||
|
|
820e089c59 | ||
|
|
4ec2bdbb50 | ||
|
|
806482c5a3 | ||
|
|
906ad5f57e | ||
|
|
43fcb00405 | ||
|
|
4f009be6a4 | ||
|
|
b2ee317592 | ||
|
|
af4b241523 | ||
|
|
db029c0806 | ||
|
|
6e08887d1f | ||
|
|
17bd4a761a | ||
|
|
258eed505b | ||
|
|
722dcdea3d | ||
|
|
36c270edd8 | ||
|
|
057cf25dd8 | ||
|
|
b361b887c2 | ||
|
|
a59b478d2d | ||
|
|
46a8ba3266 | ||
|
|
c7c2863f9e | ||
|
|
b2d331834a | ||
|
|
e5802aaac3 | ||
|
|
bc95f1b8fd | ||
|
|
13c5b002d0 | ||
|
|
24825d0d9c | ||
|
|
235dc9ca6f | ||
|
|
6a3d306911 | ||
|
|
5a921411e7 | ||
|
|
c37049b75c | ||
|
|
810d083cc0 | ||
|
|
f55b797ff5 | ||
|
|
19b9a298bc | ||
|
|
97bcc51d92 | ||
|
|
8b1504296b | ||
|
|
d0a4a40da9 | ||
|
|
e452ef9fa1 | ||
|
|
d5713877d1 | ||
|
|
9d9ce1c6a4 | ||
|
|
3f779fb496 | ||
|
|
efa6ebf066 | ||
|
|
99c896ce8a | ||
|
|
9616e0a5b0 | ||
|
|
619c8694a4 | ||
|
|
ec0ea73cb2 | ||
|
|
b23811eb60 | ||
|
|
c635ead19a | ||
|
|
5bded6dd5f | ||
|
|
dc5dbe8941 | ||
|
|
70673d5fb6 | ||
|
|
10b68b370e | ||
|
|
4b1ebb62b4 | ||
|
|
90bc78d887 | ||
|
|
268eae032d | ||
|
|
cec0889b51 | ||
|
|
7f30b24cce | ||
|
|
96d33aa3bf | ||
|
|
719614cf3a | ||
|
|
070b1fed65 | ||
|
|
cdf4ab6f45 | ||
|
|
cc7cac1aa5 | ||
|
|
8d40365b16 | ||
|
|
b1590c5cfd | ||
|
|
781448d7e0 | ||
|
|
9255328005 | ||
|
|
0bd8dc206e | ||
|
|
a5c8639dbf | ||
|
|
2d01092aa5 | ||
|
|
d89329155d | ||
|
|
4c816b4d2f | ||
|
|
4f5c43d259 | ||
|
|
a14cdc1b42 | ||
|
|
a02a184321 | ||
|
|
2171bf0f4c | ||
|
|
7be0b71393 | ||
|
|
6d8706e0f0 | ||
|
|
90e2605a78 | ||
|
|
7972cf8660 | ||
|
|
d9d720cc91 | ||
|
|
4040390767 | ||
|
|
21af7707e3 | ||
|
|
5103964110 | ||
|
|
5ceb56b097 | ||
|
|
0454a95617 | ||
|
|
42dcef9e3b | ||
|
|
b4355830e6 | ||
|
|
d661cd7d51 | ||
|
|
50bd8c65fd | ||
|
|
4720c5bfea | ||
|
|
66970d2ccb | ||
|
|
6cf198e4ce | ||
|
|
c7c002df47 | ||
|
|
912d7d18a5 | ||
|
|
e501914fb6 | ||
|
|
e36f6955ca | ||
|
|
749164ceb6 | ||
|
|
afb2db57ec | ||
|
|
b454d7c644 | ||
|
|
3be880e7bf | ||
|
|
f8cf21d64a | ||
|
|
245338a2ff | ||
|
|
0529d1cdb9 | ||
|
|
c567a70c6b | ||
|
|
85fb4df439 | ||
|
|
0255a20e09 | ||
|
|
4a9900237d | ||
|
|
c871514bde | ||
|
|
2df62af242 | ||
|
|
42a9353d68 | ||
|
|
c8bc5bd3ff | ||
|
|
2ca6b5ef2c | ||
|
|
eaa434f025 | ||
|
|
8a5a74725d | ||
|
|
daad7fcaa2 | ||
|
|
6027e0d548 | ||
|
|
670f00cc86 | ||
|
|
82952b0e7e | ||
|
|
c9223b7461 | ||
|
|
b89ae8402a | ||
|
|
6aa707c109 | ||
|
|
9fb95e820a | ||
|
|
c620cb095a | ||
|
|
b76835a0a7 | ||
|
|
73bc8ce75c | ||
|
|
b4542881ab | ||
|
|
1661ef9149 | ||
|
|
9341b1d0a3 | ||
|
|
e420a10df9 | ||
|
|
d4dd99a3cd | ||
|
|
b28c106d87 | ||
|
|
bc03517eff | ||
|
|
de8e3cd7ae | ||
|
|
45113c5cec | ||
|
|
419426b804 | ||
|
|
21daead997 | ||
|
|
8f01b54e0c | ||
|
|
cba8e6d226 | ||
|
|
ce00c7c6e4 | ||
|
|
074fd3a682 | ||
|
|
15ed18b6e1 | ||
|
|
9ca3f7111f | ||
|
|
a482eaffff | ||
|
|
97b15a98da | ||
|
|
f090e07c87 | ||
|
|
0151b18632 | ||
|
|
7fc352dccd | ||
|
|
b07a153189 | ||
|
|
e30014431f | ||
|
|
3fb11d396d | ||
|
|
3768fc19b5 | ||
|
|
0ba0d5044e | ||
|
|
c8f78a4b58 | ||
|
|
f0731f18d2 | ||
|
|
cf02442fde | ||
|
|
adab4b4528 | ||
|
|
a7aab58977 | ||
|
|
2101a7da10 | ||
|
|
2555a554c0 | ||
|
|
fe9ade5589 | ||
|
|
a0b22d1587 | ||
|
|
daa8d05e76 | ||
|
|
81d87815a6 | ||
|
|
f461f09f7e | ||
|
|
e652e827e2 | ||
|
|
cb9ab18d97 | ||
|
|
7aa39bcbc6 | ||
|
|
ad3f44c7bb | ||
|
|
cf0e35367a | ||
|
|
02f666db10 | ||
|
|
a88ab18abd | ||
|
|
a0df41e8b8 | ||
|
|
cbf5bffde4 | ||
|
|
e9863dc307 | ||
|
|
a5f7eb27f6 | ||
|
|
67bb9c88ed | ||
|
|
dd876615f5 | ||
|
|
a5d04c67c9 | ||
|
|
f5f98fe65c | ||
|
|
c99e481807 | ||
|
|
f4b0f893ae | ||
|
|
0f47a8ce8f | ||
|
|
870140dae4 | ||
|
|
c65f1d8813 | ||
|
|
9affe93790 | ||
|
|
6ddbbc80ae | ||
|
|
09977b86be | ||
|
|
975178da9b | ||
|
|
d90d33b6b1 | ||
|
|
1dd038bdd2 | ||
|
|
83762012fb | ||
|
|
ea0a6fb38c | ||
|
|
5f93542bcd | ||
|
|
bd5adfdccc | ||
|
|
0ca21162b3 | ||
|
|
84fbd77bad | ||
|
|
52461baf30 | ||
|
|
d4e777483c | ||
|
|
3662f5028a | ||
|
|
7db7e20916 | ||
|
|
538d82f926 |
1
.github/ISSUE_TEMPLATE/request_source.yml
vendored
|
|
@ -36,7 +36,6 @@ body:
|
|||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
Example: "+18/NSFW = yes"
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
|
|
|
|||
1
.github/workflows/gen_index.yml
vendored
|
|
@ -27,5 +27,6 @@ jobs:
|
|||
git checkout main
|
||||
git add index.json
|
||||
git add anime_index.json
|
||||
git add novel_index.json
|
||||
git commit -m "Update extensions"
|
||||
git push origin main --force
|
||||
|
|
@ -1,120 +1,38 @@
|
|||
# Contributing
|
||||
|
||||
This guide have some instructions and tips on how to create a new Mangayomi extension.
|
||||
This guide have some instructions and tips on how to create a new Mangayomi extension on Dart extension.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start, please note that the ability to use following technologies is **required**.
|
||||
Before starting please have installed the recent desktop version of the mangayomi application preferably or if you want with a tablet too.
|
||||
|
||||
- [Flutter development](https://flutter.dev/)
|
||||
- [Dart](https://dart.dev/)
|
||||
- Web scraping
|
||||
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
|
||||
- [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)
|
||||
|
||||
This project uses the capabilities of the [dart_eval](https://pub.dev/packages/dart_eval) package
|
||||
|
||||
### Tools
|
||||
- [Android Studio](https://developer.android.com/studio)
|
||||
- [VS Code](https://code.visualstudio.com/)
|
||||
- Any platform emulator
|
||||
|
||||
### Writing your extension
|
||||
1. Clone [Mangayomi](https://github.com/kodjodevf/mangayomi) next, read the README file to learn how to run the project.
|
||||
2. Open the project then go to :
|
||||
```console
|
||||
lib
|
||||
└── sources
|
||||
└── source_test.dart
|
||||
```
|
||||
and you will see this :
|
||||
1. Open the app.
|
||||
2. Go to extension tab :
|
||||

|
||||
3. then click `+` and you will see :
|
||||

|
||||
4. Fill in the fields with your new source that you would like to create,
|
||||

|
||||
NB: only the `ApiUrl` field is optional
|
||||
then click on save
|
||||
5. you will see your new source in the extension list
|
||||

|
||||
click to open settings
|
||||
6. After click on edit code
|
||||

|
||||
7. Finally you can now write the extension
|
||||

|
||||
- This page contains three parts:
|
||||
- Code editor: where you will write your code
|
||||
- Fecth result: where you will test the different implemented methods by having a result in the expected format
|
||||
- Console: which will show you the logs
|
||||
|
||||
```bash
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
//For testing purposes, set to true
|
||||
const useTestSourceCode = true;
|
||||
|
||||
final testSourceModelList = [
|
||||
Source(
|
||||
id: "Test Source".hashCode,
|
||||
name: "Test Source",
|
||||
// Example: https://gogoanime3.net
|
||||
baseUrl: "",
|
||||
// source code
|
||||
sourceCode: testSourceCode,
|
||||
// Example: en
|
||||
lang: "",
|
||||
// Example: false for anime true for manga
|
||||
isManga: false)
|
||||
];
|
||||
|
||||
const testSourceCode = r'''
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
|
||||
class TestSource extends MProvider {
|
||||
TestSource({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
bool get supportsLatest => true;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// For anime episode video list
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// For manga chapter pages
|
||||
@override
|
||||
Future<List<String>> getPageList(String url) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
|
||||
TestSource main(MSource source) {
|
||||
return TestSource(source:source);
|
||||
}
|
||||
|
||||
''';
|
||||
|
||||
```
|
||||
For testing your source set `useTestSourceCode` to true.
|
||||
During the development it is recommended (but not necessary) to write it directly mangayomi application project.
|
||||
Once extension is ready you can relocate your code into `mangayomi-extension` project in a `src` or `multisrc` package and create a Pull Request.
|
||||
Once extension is ready you can relocate your code into `mangayomi-extension` project in a `src` or `multisrc` package
|
||||
Create the folder with the name of the source such as [this example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/dart/anime/src/en/kisskh)
|
||||
after go either to the anime_source_list.dart file for anime or manga_source_list.dart for the manga and import the extension then
|
||||
create a Pull Request.
|
||||
|
||||
### Source
|
||||
|
||||
|
|
@ -215,7 +133,7 @@ All control filters can have a default state set. It's usually recommended if th
|
|||
|
||||
#### Chapter pages
|
||||
|
||||
- When user opens an chapter, `getPageList` will be called and it will return a `List<String>` that are used by the reader.
|
||||
- When user opens an chapter, `getPageList` will be called and it will return a `List<String>` or `List<Map<String,dynamic>>` with a list of `{"url":string,"headers":Map<String,String>}` that are used by the reader.
|
||||
|
||||
#### Episode Videos
|
||||
|
||||
|
|
@ -223,11 +141,11 @@ All control filters can have a default state set. It's usually recommended if th
|
|||
|
||||
## Example sources that can help you understand how to create your source
|
||||
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/anime/src/en/kisskh/kisskh.dart)
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/dart/anime/src/en/kisskh/kisskh.dart)
|
||||
of Json API usage.
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/manga/src/en/mangahere/mangahere.dart)
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/dart/manga/src/en/mangahere/mangahere.dart)
|
||||
of HTML parsing using xpath selector.
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/manga/multisrc/madara/madara.dart)
|
||||
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/dart/manga/multisrc/madara/madara.dart)
|
||||
of HTML parsing using HTML DOM selector.
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Contributing
|
||||
|
||||
This guide have some instructions and tips on how to create a new Mangayomi extension on js extension.
|
||||
This guide have some instructions and tips on how to create a new Mangayomi extension on JavaScript extension.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ a.k.a. the Latest source entry point in the app (invoked by tapping on the "Late
|
|||
|
||||
#### Chapter pages
|
||||
|
||||
- When user opens an chapter, `getPageList` will be called and it will return an array of string that are used by the reader.
|
||||
- When user opens an chapter, `getPageList` will be called and it will return an array of string or an array of map like `{ url:string,headers:map }` that are used by the reader.
|
||||
|
||||
#### Episode Videos
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ console.log(res.body);
|
|||
|
||||
const client = new Client();
|
||||
|
||||
final res = await client.post("http://example.com",{"Referer": "http://example.com"},{'name':'test'});
|
||||
const res = await client.post("http://example.com",{"Referer": "http://example.com"},{'name':'test'});
|
||||
|
||||
console.log(res.body);
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ console.log(document.selectFirst("a").attr("href")); // https://github.com/kodjo
|
|||
console.log(document.selectFirst("td").text); // 1
|
||||
|
||||
```
|
||||
See [`dom_selector`](https://github.com/kodjodevf/mangayomi/blob/lib/eval/javascript/dom_selector.dart) to see available methods.
|
||||
See [`dom_selector`](https://github.com/kodjodevf/mangayomi/blob/main/lib/eval/javascript/dom_selector.dart) to see available methods.
|
||||
|
||||
|
||||
### String utils
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ This repository contains the available extension catalogues for the [Mangayomi](
|
|||
|
||||
Contributions are welcome!
|
||||
|
||||
To get started with development, see [CONTRIBUTING.md](./CONTRIBUTING.md) for create sources in Dart or [CONTRIBUTING-JS.md](./CONTRIBUTING-JS.md) for create sources in JavaScript.
|
||||
To get started with development, see [CONTRIBUTING-DART.md](./CONTRIBUTING-DART.md) for create sources in Dart or [CONTRIBUTING-JS.md](./CONTRIBUTING-JS.md) for create sources in JavaScript.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import 'multisrc/zorotheme/sources.dart';
|
|||
import 'src/all/animeworldindia/sources.dart';
|
||||
import 'src/all/nyaa/source.dart';
|
||||
import 'src/ar/okanime/source.dart';
|
||||
import 'src/de/aniflix/source.dart';
|
||||
import 'src/de/animetoast/source.dart';
|
||||
import 'src/en/animepahe/source.dart';
|
||||
import 'src/en/aniwave/source.dart';
|
||||
import 'src/en/dramacool/source.dart';
|
||||
import 'src/en/gogoanime/source.dart';
|
||||
import 'src/en/nineanimetv/source.dart';
|
||||
import 'src/es/animeonlineninja/source.dart';
|
||||
import 'src/fr/animesama/source.dart';
|
||||
import 'src/fr/anizone/source.dart';
|
||||
import 'src/hi/yomovies/source.dart';
|
||||
import 'src/en/kisskh/source.dart';
|
||||
import 'src/en/uhdmovies/source.dart';
|
||||
|
|
@ -25,6 +25,7 @@ import 'src/id/otakudesu/source.dart';
|
|||
import 'src/it/animesaturn/source.dart';
|
||||
import 'src/pt/animesvision/source.dart';
|
||||
import 'src/sq/filma24/source.dart';
|
||||
import 'src/tr/diziwatch/source.dart';
|
||||
|
||||
List<Source> dartAnimesourceList = [
|
||||
gogoanimeSource,
|
||||
|
|
@ -37,7 +38,6 @@ List<Source> dartAnimesourceList = [
|
|||
otakudesu,
|
||||
nimegami,
|
||||
oploverz,
|
||||
aniwave,
|
||||
...dopeflixSourcesList,
|
||||
animesaturn,
|
||||
uhdmoviesSource,
|
||||
|
|
@ -47,10 +47,12 @@ List<Source> dartAnimesourceList = [
|
|||
yomoviesSource,
|
||||
animesamaSource,
|
||||
nineanimetv,
|
||||
aniflix,
|
||||
...animeworldindiaSourcesList,
|
||||
nyaaSource,
|
||||
animepaheSource,
|
||||
animetoast,
|
||||
animesvision
|
||||
animesvision,
|
||||
diziwatchSource,
|
||||
aniZoneSource,
|
||||
animeonlineninjaSource
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
|
||||
class DataLifeEngine extends MProvider {
|
||||
DataLifeEngine({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
final Client client =
|
||||
Client(source, json.encode({"useDartHttpClient": true}));
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client
|
||||
.get(Uri.parse("${source.baseUrl}${getPath(source)}page/$page")))
|
||||
.body;
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl${getPath(source)}page/$page")))
|
||||
.body;
|
||||
return animeFromElement(res);
|
||||
}
|
||||
|
||||
|
|
@ -27,10 +31,10 @@ class DataLifeEngine extends MProvider {
|
|||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
final baseUrl = source.baseUrl;
|
||||
String res = "";
|
||||
if (query.isNotEmpty) {
|
||||
if (query.length < 4) return MPages([], false);
|
||||
if (query.length < 4)
|
||||
throw "La recherche est suspendue! La chaîne de recherche est vide ou contient moins de 4 caractères.";
|
||||
final headers = {
|
||||
"Host": Uri.parse(baseUrl).host,
|
||||
"Origin": baseUrl,
|
||||
|
|
@ -71,7 +75,9 @@ class DataLifeEngine extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
String res = (await client.get(Uri.parse(url))).body;
|
||||
String res =
|
||||
(await client.get(Uri.parse("$baseUrl${getUrlWithoutDomain(url)}")))
|
||||
.body;
|
||||
MManga anime = MManga();
|
||||
final description = xpath(res, '//span[@itemprop="description"]/text()');
|
||||
anime.description = description.isNotEmpty ? description.first : "";
|
||||
|
|
@ -89,25 +95,40 @@ class DataLifeEngine extends MProvider {
|
|||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
final eps = xpath(res,
|
||||
'//*[@class="hostsblock"]/div/a[contains(@href,"https")]/parent::div/@class');
|
||||
if (eps.isNotEmpty) {
|
||||
for (var i = 0; i < eps.length; i++) {
|
||||
final epUrls = xpath(res,
|
||||
'//*[@class="hostsblock"]/div[@class="${eps[i]}"]/a[contains(@href,"https")]/@href');
|
||||
final doc = parseHtml(res);
|
||||
final elements = doc
|
||||
.select(".hostsblock div:has(a)")
|
||||
.where((MElement e) => e.outerHtml.contains("loadVideo('https://"))
|
||||
.toList();
|
||||
if (elements.isNotEmpty) {
|
||||
for (var element in elements) {
|
||||
element = element as MElement;
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Episode ${i + 1}";
|
||||
ep.url = epUrls.join(",").replaceAll("/vd.php?u=", "");
|
||||
ep.scanlator = eps[i].contains('vf') ? 'VF' : 'VOSTFR';
|
||||
ep.name = element.className
|
||||
.replaceAll("ep", "Episode ")
|
||||
.replaceAll("vs", " VOSTFR")
|
||||
.replaceAll("vf", " VF");
|
||||
ep.url = element
|
||||
.select("a")
|
||||
.map((MElement e) => substringBefore(
|
||||
substringAfter(e.attr('onclick'), "loadVideo('"), "')"))
|
||||
.toList()
|
||||
.join(",")
|
||||
.replaceAll("/vd.php?u=", "");
|
||||
ep.scanlator = element.className.contains('vf') ? 'VF' : 'VOSTFR';
|
||||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
anime.status = MStatus.completed;
|
||||
final epUrls = xpath(res,
|
||||
'//*[contains(@class,"filmlinks")]/div/a[contains(@href,"https")]/@href');
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Film";
|
||||
ep.url = epUrls.join(",").replaceAll("/vd.php?u=", "");
|
||||
ep.url = doc
|
||||
.select("a")
|
||||
.where((MElement e) => e.outerHtml.contains("loadVideo('https://"))
|
||||
.map((MElement e) => substringBefore(
|
||||
substringAfter(e.attr('onclick'), "loadVideo('"), "')"))
|
||||
.toList()
|
||||
.join(",")
|
||||
.replaceAll("/vd.php?u=", "");
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
|
|
@ -122,13 +143,10 @@ class DataLifeEngine extends MProvider {
|
|||
final sUrls = url.split(',');
|
||||
for (var sUrl in sUrls) {
|
||||
List<MVideo> a = [];
|
||||
if (sUrl.contains("dood")) {
|
||||
if (sUrl.contains("dood") || sUrl.contains("d000")) {
|
||||
a = await doodExtractor(sUrl, "DoodStream");
|
||||
} else if (sUrl.contains("voe.sx")) {
|
||||
a = await voeExtractor(sUrl, "Voe");
|
||||
} else if (sUrl.contains("streamvid") ||
|
||||
sUrl.contains("guccihide") ||
|
||||
sUrl.contains("streamhide")) {
|
||||
} else if (["streamhide", "guccihide", "streamvid", "dhtpre"]
|
||||
.any((a) => sUrl.contains(a))) {
|
||||
a = await streamHideExtractor(sUrl);
|
||||
} else if (sUrl.contains("uqload")) {
|
||||
a = await uqloadExtractor(sUrl);
|
||||
|
|
@ -138,6 +156,12 @@ class DataLifeEngine extends MProvider {
|
|||
a = await sibnetExtractor(sUrl);
|
||||
} else if (sUrl.contains("ok.ru")) {
|
||||
a = await okruExtractor(sUrl);
|
||||
} else if (sUrl.contains("vidmoly")) {
|
||||
a = await vidmolyExtractor(sUrl);
|
||||
} else if (sUrl.contains("streamtape")) {
|
||||
a = await streamTapeExtractor(sUrl, "");
|
||||
} else if (sUrl.contains("voe.sx")) {
|
||||
a = await voeExtractor(sUrl, "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
|
@ -156,7 +180,7 @@ class DataLifeEngine extends MProvider {
|
|||
MManga anime = MManga();
|
||||
anime.name =
|
||||
"$name ${season.isNotEmpty ? season.first.replaceAll("\n", " ") : ""}";
|
||||
anime.imageUrl = "${source.baseUrl}$image";
|
||||
anime.imageUrl = "$baseUrl$image";
|
||||
anime.link = url;
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
|
@ -246,11 +270,70 @@ class DataLifeEngine extends MProvider {
|
|||
return [video];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidmolyExtractor(String url) async {
|
||||
final headers = {
|
||||
'Referer': 'https://vidmoly.to',
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes =
|
||||
await client.get(Uri.parse(playlistUrl), headers: headers);
|
||||
|
||||
if (masterPlaylistRes.statusCode == 200) {
|
||||
for (var it
|
||||
in substringAfter(masterPlaylistRes.body, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Vidmoly $quality"
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
if (source.name == "French Anime") return "/animes-vostfr/";
|
||||
return "/serie-en-streaming/";
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
if (source.name == "Wiflix")
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://wiflix-hd.vip",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://wiflix-hd.vip"),
|
||||
if (source.name == "French Anime")
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://french-anime.com",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://french-anime.com"),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||
import 'src/frenchanime/frenchanime.dart';
|
||||
import 'src/wiflix/wiflix.dart';
|
||||
|
||||
const _datalifeengineVersion = "0.0.35";
|
||||
const _datalifeengineVersion = "0.0.6";
|
||||
const _datalifeengineSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/datalifeengine.dart";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Source _frenchanimeSource = Source(
|
|||
baseUrl: "https://french-anime.com",
|
||||
lang: "fr",
|
||||
typeSource: "datalifeengine",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ Source get wiflixSource => _wiflixSource;
|
|||
|
||||
Source _wiflixSource = Source(
|
||||
name: "Wiflix",
|
||||
baseUrl: "https://wiflix.voto",
|
||||
baseUrl: "https://wiflix-hd.vip",
|
||||
lang: "fr",
|
||||
typeSource: "datalifeengine",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ class DopeFlix extends MProvider {
|
|||
]),
|
||||
SelectFilter("ReleaseYearFilter", "Released at", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("2024", "2024"),
|
||||
SelectFilterOption("2023", "2023"),
|
||||
SelectFilterOption("2022", "2022"),
|
||||
SelectFilterOption("2021", "2021"),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||
import 'src/dopebox/dopebox.dart';
|
||||
import 'src/sflix/sflix.dart';
|
||||
|
||||
const _dopeflixVersion = "0.0.5";
|
||||
const _dopeflixVersion = "0.0.55";
|
||||
const _dopeflixSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/dopeflix.dart";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Source _dopeboxSource = Source(
|
|||
baseUrl: "https://dopebox.to",
|
||||
lang: "en",
|
||||
typeSource: "dopeflix",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/dopebox/icon.png",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Source _sflixSource = Source(
|
|||
baseUrl: "https://sflix.to",
|
||||
lang: "en",
|
||||
typeSource: "dopeflix",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/sflix/icon.png",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||
import 'src/hianime/hianime.dart';
|
||||
import 'src/kaido/kaido.dart';
|
||||
|
||||
const _zorothemeVersion = "0.0.9";
|
||||
const _zorothemeVersion = "0.1.2";
|
||||
const _zorothemeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/zorotheme.dart";
|
||||
|
||||
|
|
@ -15,5 +15,6 @@ List<Source> _zorothemeSourcesList = [
|
|||
]
|
||||
.map((e) => e
|
||||
..sourceCodeUrl = _zorothemeSourceCodeUrl
|
||||
..appMinVerReq = "0.4.0"
|
||||
..version = _zorothemeVersion)
|
||||
.toList();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Source _aniwatchSource = Source(
|
|||
id: 814067600,
|
||||
name: "HiAnime",
|
||||
baseUrl: "https://hianime.to",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
lang: "en",
|
||||
typeSource: "zorotheme",
|
||||
iconUrl:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Source _kaidoSource = Source(
|
|||
name: "Kaido.to",
|
||||
baseUrl: "https://kaido.to",
|
||||
lang: "en",
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
typeSource: "zorotheme",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/src/kaido/icon.png",
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ class AnimeWorldIndia extends MProvider {
|
|||
url += "${ll(url)}s_genre=";
|
||||
if (genre.isNotEmpty) {
|
||||
for (var st in genre) {
|
||||
url += "${st.value}".toLowerCase().replaceAll(" ", "-");
|
||||
String value = st.value;
|
||||
url += value.toLowerCase().replaceAll(" ", "-");
|
||||
if (genre.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
const _animeworldindiaVersion = "0.0.25";
|
||||
const _animeworldindiaVersion = "0.0.3";
|
||||
const _animeworldindiaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/animeworldindia/animeworldindia.dart";
|
||||
|
||||
|
|
@ -28,6 +28,6 @@ List<Source> _animeworldindiaSourcesList = _languages
|
|||
typeSource: "multiple",
|
||||
iconUrl: _iconUrl,
|
||||
version: _animeworldindiaVersion,
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
sourceCodeUrl: _animeworldindiaSourceCodeUrl))
|
||||
.toList();
|
||||
|
|
|
|||
|
|
@ -59,6 +59,16 @@ class Nyaa extends MProvider {
|
|||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = url
|
||||
..originalUrl = url
|
||||
..quality = "";
|
||||
return [video];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
const _nyaaVersion = "0.0.2";
|
||||
const _nyaaVersion = "0.0.25";
|
||||
const _nyaaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/nyaa/nyaa.dart";
|
||||
|
||||
|
|
@ -15,5 +15,5 @@ Source _nyaaSource = Source(
|
|||
typeSource: "torrent",
|
||||
iconUrl: _iconUrl,
|
||||
version: _nyaaVersion,
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
sourceCodeUrl: _nyaaSourceCodeUrl);
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Source _okanimeSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/ar/okanime/icon.png",
|
||||
sourceCodeUrl: _okanimeSourceCodeUrl,
|
||||
version: _okanimeVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AniFlix extends MProvider {
|
||||
AniFlix({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
final res = (await client.get(
|
||||
Uri.parse("${source.baseUrl}/api/show/new/${page - 1}"),
|
||||
headers: headers))
|
||||
.body;
|
||||
|
||||
return parseAnimeList(res, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
final res = (await client.get(
|
||||
Uri.parse("${source.baseUrl}/api/show/airing/${page - 1}"),
|
||||
headers: headers))
|
||||
.body;
|
||||
final datas = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
List<String> ids = [];
|
||||
for (var data in datas) {
|
||||
final anim = data["season"]["show"];
|
||||
if (!ids.contains(anim["id"])) {
|
||||
ids.add(anim["id"]);
|
||||
MManga anime = MManga();
|
||||
anime.name = anim["name"];
|
||||
anime.imageUrl =
|
||||
"${source.baseUrl}/storage/" + (anim["cover_portrait"] ?? "");
|
||||
anime.link =
|
||||
getUrlWithoutDomain("${source.baseUrl}/api/show/${anim['url']}");
|
||||
anime.description = anim["description"];
|
||||
if (anim["airing"] == 0) {
|
||||
anime.status = MStatus.completed;
|
||||
} else if (anim["airing"] == 1) {
|
||||
anime.status = MStatus.ongoing;
|
||||
}
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res = await client.post(
|
||||
Uri.parse("${source.baseUrl}/api/show/search"),
|
||||
headers: {'Referer': source.baseUrl},
|
||||
body: {"search": query});
|
||||
return parseAnimeList(res.body, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final jsonRes = json.decode(res);
|
||||
anime.name = jsonRes["name"];
|
||||
if (jsonRes["cover_portrait"] != null) {
|
||||
anime.imageUrl = "${source.baseUrl}/storage/" + jsonRes["cover_portrait"];
|
||||
}
|
||||
anime.description = jsonRes["description"];
|
||||
anime.genre = (jsonRes["genres"] as List<Map<String, dynamic>>)
|
||||
.map((e) => e["name"])
|
||||
.toList();
|
||||
var seasons = jsonRes["seasons"];
|
||||
final animeUrl = jsonRes["url"];
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var season in seasons) {
|
||||
List<Map<String, dynamic>> episodes = season["episodes"];
|
||||
int page = 1;
|
||||
final res = (await client.get(Uri.parse(
|
||||
"${source.baseUrl}/api/show/$animeUrl/${season["id"]}/$page")))
|
||||
.body;
|
||||
|
||||
bool hasMoreResult =
|
||||
(json.decode(res)["episodes"] as List<Map<String, dynamic>>)
|
||||
.isNotEmpty;
|
||||
|
||||
while (hasMoreResult) {
|
||||
final res = (await client.get(Uri.parse(
|
||||
"${source.baseUrl}/api/show/$animeUrl/${season["id"]}/$page")))
|
||||
.body;
|
||||
final epList =
|
||||
json.decode(res)["episodes"] as List<Map<String, dynamic>>;
|
||||
page++;
|
||||
episodes.addAll(epList);
|
||||
hasMoreResult = epList.isNotEmpty;
|
||||
}
|
||||
for (var episode in episodes) {
|
||||
String name = episode["name"] ?? "";
|
||||
if (name.toLowerCase().contains("folge") ||
|
||||
name.toLowerCase().contains("episode")) {
|
||||
name = "";
|
||||
} else {
|
||||
name = ": $name";
|
||||
}
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Staffel ${season["number"]} Folge ${episode["number"]}$name";
|
||||
ep.url =
|
||||
"/api/episode/show/$animeUrl/season/${season["number"]}/episode/${episode["number"]}";
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"),
|
||||
headers: getHeader(source.baseUrl)))
|
||||
.body;
|
||||
final jsonRes = json.decode(res)["streams"];
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
for (var stream in jsonRes) {
|
||||
List<MVideo> a = [];
|
||||
String quality = '${stream["hoster"]["name"]} - ${stream["lang"]}';
|
||||
String link = stream["link"];
|
||||
if ((link.contains("https://dood") || link.contains("https://d0")) &&
|
||||
hosterSelection.contains("doodstream")) {
|
||||
a = await doodExtractor(link, quality);
|
||||
} else if (link.contains("https://streamtape") &&
|
||||
hosterSelection.contains("streamtape")) {
|
||||
a = await streamTapeExtractor(link, quality);
|
||||
} else if (link.contains("https://voe.sx") &&
|
||||
hosterSelection.contains("voe")) {
|
||||
a = await voeExtractor(link, quality);
|
||||
} else if (link.contains("https://streamlare") &&
|
||||
hosterSelection.contains("streamlare")) {
|
||||
a = await streamlareExtractor(link, quality, '', '');
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res, bool hasNextPage) {
|
||||
final datas = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
|
||||
for (var data in datas) {
|
||||
MManga anime = MManga();
|
||||
anime.name = data["name"];
|
||||
anime.imageUrl =
|
||||
"${source.baseUrl}/storage/" + (data["cover_portrait"] ?? "");
|
||||
anime.link =
|
||||
getUrlWithoutDomain("${source.baseUrl}/api/show/${data['url']}");
|
||||
anime.description = data["description"];
|
||||
if (data["airing"] == 0) {
|
||||
anime.status = MStatus.completed;
|
||||
} else if (data["airing"] == 1) {
|
||||
anime.status = MStatus.ongoing;
|
||||
}
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String hoster = getPreferenceValue(sourceId, "preferred_hoster");
|
||||
String sub = getPreferenceValue(sourceId, "preferred_sub");
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int hosterMatchA = 0;
|
||||
if (a.url.toLowerCase().contains(hoster.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(sub.toLowerCase())) {
|
||||
hosterMatchA = 1;
|
||||
}
|
||||
int hosterMatchB = 0;
|
||||
if (b.url.toLowerCase().contains(hoster.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(sub.toLowerCase())) {
|
||||
hosterMatchB = 1;
|
||||
}
|
||||
return hosterMatchB - hosterMatchA;
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
|
||||
List<String> preferenceHosterSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "hoster_selectionn");
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_hoster",
|
||||
title: "Standard-Hoster",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: [
|
||||
"Streamtape",
|
||||
"Doodstream",
|
||||
"Voe",
|
||||
"Streamlare"
|
||||
],
|
||||
entryValues: [
|
||||
"https://streamtape.com",
|
||||
"https://dood",
|
||||
"https://voe.sx",
|
||||
"https://streamlare.com"
|
||||
]),
|
||||
ListPreference(
|
||||
key: "preferred_sub",
|
||||
title: "Standardmäßig Sub oder Dub?",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Sub", "Dub"],
|
||||
entryValues: ["Sub", "Dub"]),
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selectionn",
|
||||
title: "Hoster auswählen",
|
||||
summary: "",
|
||||
entries: ["Streamtape", "Doodstream", "Voe", "Streamlare"],
|
||||
entryValues: ["streamtape", "doodstream", "voe", "streamlare"],
|
||||
values: ["streamtape", "doodstream", "voe", "streamlare"]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> getHeader(String url) {
|
||||
return {'Referer': url};
|
||||
}
|
||||
|
||||
AniFlix main(MSource source) {
|
||||
return AniFlix(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,16 +0,0 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get aniflix => _aniflix;
|
||||
const _aniflixVersion = "0.0.3";
|
||||
const _aniflixCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/aniflix/aniflix.dart";
|
||||
Source _aniflix = Source(
|
||||
name: "Aniflix",
|
||||
baseUrl: "https://aniflix.cc",
|
||||
lang: "de",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/aniflix/icon.png",
|
||||
sourceCodeUrl: _aniflixCodeUrl,
|
||||
version: _aniflixVersion,
|
||||
isManga: false);
|
||||
|
|
@ -76,8 +76,9 @@ class AnimeToast extends MProvider {
|
|||
} else {
|
||||
elements = document.select("#multi_link_tab1");
|
||||
}
|
||||
|
||||
for (var element in elements) {
|
||||
final episodeElement = element.selectFirst("div.tab-pane a");
|
||||
final episodeElement = element.selectFirst("a");
|
||||
final epT = episodeElement.text;
|
||||
if (epT.contains(":") || epT.contains("-")) {
|
||||
final url = episodeElement.attr("href");
|
||||
|
|
@ -92,7 +93,7 @@ class AnimeToast extends MProvider {
|
|||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
final episodeElements = element.select("div.tab-pane a");
|
||||
final episodeElements = element.select("a");
|
||||
for (var epElement in episodeElements) {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = epElement.text;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animetoast => _animetoast;
|
||||
const _animetoastVersion = "0.0.15";
|
||||
const _animetoastVersion = "0.0.2";
|
||||
const _animetoastCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/animetoast.dart";
|
||||
Source _animetoast = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _animetoast = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/icon.png",
|
||||
sourceCodeUrl: _animetoastCodeUrl,
|
||||
version: _animetoastVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ class AnimePahe extends MProvider {
|
|||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "preferred_domain");
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {'cookie': '__ddg1_=;__ddg2_=;'};
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return await getLatestUpdates(page);
|
||||
|
|
@ -19,8 +22,9 @@ class AnimePahe extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/api?m=airing&page=$page"))).body;
|
||||
final res = (await client.get(Uri.parse("$baseUrl/api?m=airing&page=$page"),
|
||||
headers: headers))
|
||||
.body;
|
||||
final jsonResult = json.decode(res);
|
||||
final hasNextPage = jsonResult["current_page"] < jsonResult["last_page"];
|
||||
List<MManga> animeList = [];
|
||||
|
|
@ -37,9 +41,10 @@ class AnimePahe extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/api?m=search&l=8&q=$query")))
|
||||
.body;
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/api?m=search&l=8&q=$query"),
|
||||
headers: headers))
|
||||
.body;
|
||||
final jsonResult = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
for (var item in jsonResult["data"]) {
|
||||
|
|
@ -61,9 +66,10 @@ class AnimePahe extends MProvider {
|
|||
final id = substringBefore(substringAfterLast(url, "?anime_id="), "&name=");
|
||||
final name = substringAfterLast(url, "&name=");
|
||||
final session = await getSession(name, id);
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/anime/$session?anime_id=$id")))
|
||||
.body;
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/anime/$session?anime_id=$id"),
|
||||
headers: headers))
|
||||
.body;
|
||||
final document = parseHtml(res);
|
||||
final status =
|
||||
(document.xpathFirst('//div/p[contains(text(),"Status:")]/text()') ??
|
||||
|
|
@ -91,7 +97,7 @@ class AnimePahe extends MProvider {
|
|||
anime.description += "\n\n$synonyms";
|
||||
}
|
||||
final epUrl = "$baseUrl/api?m=release&id=$session&sort=episode_desc&page=1";
|
||||
final resEp = (await client.get(Uri.parse(epUrl))).body;
|
||||
final resEp = (await client.get(Uri.parse(epUrl), headers: headers)).body;
|
||||
final episodes = await recursivePages(epUrl, resEp, session);
|
||||
|
||||
anime.chapters = episodes;
|
||||
|
|
@ -114,15 +120,17 @@ class AnimePahe extends MProvider {
|
|||
}
|
||||
if (hasNextPage) {
|
||||
final newUrl = "${substringBeforeLast(url, "&page=")}&page=${page + 1}";
|
||||
final newRes = (await client.get(Uri.parse(newUrl))).body;
|
||||
final newRes =
|
||||
(await client.get(Uri.parse(newUrl), headers: headers)).body;
|
||||
animeList.addAll(await recursivePages(newUrl, newRes, session));
|
||||
}
|
||||
return animeList;
|
||||
}
|
||||
|
||||
Future<String> getSession(String title, String animeId) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/api?m=search&q=$title"))).body;
|
||||
final res = (await client.get(Uri.parse("$baseUrl/api?m=search&q=$title"),
|
||||
headers: headers))
|
||||
.body;
|
||||
return substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(res, "\"id\":$animeId"), "\"session\":\""),
|
||||
|
|
@ -131,19 +139,25 @@ class AnimePahe extends MProvider {
|
|||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url")));
|
||||
//by default we use rhttp package but it does not support `followRedirects`
|
||||
//so setting `useDartHttpClient` to true allows us to use a Dart http package that supports `followRedirects`
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"), headers: headers));
|
||||
final document = parseHtml(res.body);
|
||||
final downloadLinks = document.select("div#pickDownload > a");
|
||||
final buttons = document.select("div#resolutionMenu > button");
|
||||
List<MVideo> videos = [];
|
||||
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
final btn = buttons[i];
|
||||
final audio = btn.attr("data-audio"); // Get audio type (jpn/eng). Japanese or Dubbed.
|
||||
final kwikLink = btn.attr("data-src");
|
||||
final quality = btn.text;
|
||||
final paheWinLink = downloadLinks[i].attr("href");
|
||||
|
||||
if (getPreferenceValue(source.id, "preffered_link_type")) {
|
||||
final noRedirectClient =
|
||||
Client(source, json.encode({"followRedirects": false}));
|
||||
final noRedirectClient = Client(source,
|
||||
json.encode({"followRedirects": false, "useDartHttpClient": true}));
|
||||
final kwikHeaders =
|
||||
(await noRedirectClient.get(Uri.parse("${paheWinLink}/i"))).headers;
|
||||
final kwikUrl =
|
||||
|
|
@ -165,9 +179,11 @@ class AnimePahe extends MProvider {
|
|||
getMapValue(json.encode(res.request.headers), "cookie");
|
||||
cookie +=
|
||||
"; ${getMapValue(json.encode(reskwik.headers), "set-cookie").replaceAll("path=/;", "")}";
|
||||
final resNo =
|
||||
await Client(source, json.encode({"followRedirects": false}))
|
||||
.post(Uri.parse(url), headers: {
|
||||
final resNo = await Client(
|
||||
source,
|
||||
json.encode(
|
||||
{"followRedirects": false, "useDartHttpClient": true}))
|
||||
.post(Uri.parse(url), headers: {
|
||||
"referer": reskwik.request.url.toString(),
|
||||
"cookie": cookie,
|
||||
"user-agent":
|
||||
|
|
@ -253,8 +269,20 @@ class AnimePahe extends MProvider {
|
|||
|
||||
List<MVideo> sortVideos(List<MVideo> videos) {
|
||||
String quality = getPreferenceValue(source.id, "preferred_quality");
|
||||
String preferredAudio = getPreferenceValue(source.id, "preferred_audio"); // get user's audio preference
|
||||
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
// Prioritize audio first.
|
||||
// Preferred Audio: Videos with matching preferred audio are ranked highest.
|
||||
int audioMatchA = a.quality.contains(preferredAudio) ? 1 : 0;
|
||||
int audioMatchB = b.quality.contains(preferredAudio) ? 1 : 0;
|
||||
if (audioMatchA != audioMatchB) {
|
||||
return audioMatchB - audioMatchA;
|
||||
}
|
||||
|
||||
// quality prioritized next
|
||||
// Preferred Video Quality: If audio matches, videos with preferred video quality are ranked higher.
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.contains(quality)) {
|
||||
qualityMatchA = 1;
|
||||
|
|
@ -308,6 +336,14 @@ class AnimePahe extends MProvider {
|
|||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "360p"],
|
||||
entryValues: ["1080", "720", "360"]),
|
||||
|
||||
ListPreference(
|
||||
key: "preferred_audio", // Add new preference for audio
|
||||
title: "Preferred Audio",
|
||||
summary: "Select your preferred audio language (Japanese or English).",
|
||||
valueIndex: 0, // Default to Japanese (or whichever you prefer)
|
||||
entries: ["Japanese", "English"],
|
||||
entryValues: ["jpn", "eng"]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animepaheSource => _animepaheSource;
|
||||
const _animepaheVersion = "0.0.35";
|
||||
const _animepaheVersion = "0.0.5";
|
||||
const _animepaheSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/animepahe.dart";
|
||||
Source _animepaheSource = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _animepaheSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/icon.png",
|
||||
sourceCodeUrl: _animepaheSourceCodeUrl,
|
||||
version: _animepaheVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -1,688 +0,0 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class Aniwave extends MProvider {
|
||||
Aniwave({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "preferred_domain1");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client
|
||||
.get(Uri.parse("$baseUrl/filter?sort=trending&page=$page")))
|
||||
.body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client
|
||||
.get(Uri.parse("$baseUrl/filter?sort=recently_updated&page=$page")))
|
||||
.body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "$baseUrl/filter?keyword=$query";
|
||||
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "OrderFilter") {
|
||||
final order = filter.values[filter.state].value;
|
||||
url += "${ll(url)}sort=$order";
|
||||
} else if (filter.type == "GenreFilter") {
|
||||
final genre = (filter.state as List).where((e) => e.state).toList();
|
||||
if (genre.isNotEmpty) {
|
||||
for (var st in genre) {
|
||||
url += "${ll(url)}genre[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "CountryFilter") {
|
||||
final country = (filter.state as List).where((e) => e.state).toList();
|
||||
if (country.isNotEmpty) {
|
||||
for (var st in country) {
|
||||
url += "${ll(url)}country[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "SeasonFilter") {
|
||||
final season = (filter.state as List).where((e) => e.state).toList();
|
||||
if (season.isNotEmpty) {
|
||||
for (var st in season) {
|
||||
url += "${ll(url)}season[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "YearFilter") {
|
||||
final year = (filter.state as List).where((e) => e.state).toList();
|
||||
if (year.isNotEmpty) {
|
||||
for (var st in year) {
|
||||
url += "${ll(url)}year[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "TypeFilter") {
|
||||
final type = (filter.state as List).where((e) => e.state).toList();
|
||||
if (type.isNotEmpty) {
|
||||
for (var st in type) {
|
||||
url += "${ll(url)}type[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "StatusFilter") {
|
||||
final status = (filter.state as List).where((e) => e.state).toList();
|
||||
if (status.isNotEmpty) {
|
||||
for (var st in status) {
|
||||
url += "${ll(url)}status[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "LanguageFilter") {
|
||||
final language = (filter.state as List).where((e) => e.state).toList();
|
||||
if (language.isNotEmpty) {
|
||||
for (var st in language) {
|
||||
url += "${ll(url)}language[]=${st.value}";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "RatingFilter") {
|
||||
final rating = (filter.state as List).where((e) => e.state).toList();
|
||||
if (rating.isNotEmpty) {
|
||||
for (var st in rating) {
|
||||
url += "${ll(url)}rating[]=${st.value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse("$url&page=$page"))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Releasing": 0, "Completed": 1}
|
||||
];
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final status = xpath(res, '//div[contains(text(),"Status")]/span/text()');
|
||||
if (status.isNotEmpty) {
|
||||
anime.status = parseStatus(status.first, statusList);
|
||||
}
|
||||
final description = xpath(res,
|
||||
'//*[contains(@class,"synopsis")]/div[@class="shorting"]/div[@class="content"]/text()');
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first;
|
||||
}
|
||||
final author = xpath(res, '//div[contains(text(),"Studio")]/span/text()');
|
||||
if (author.isNotEmpty) {
|
||||
anime.author = author.first;
|
||||
}
|
||||
|
||||
anime.genre = xpath(res, '//div[contains(text(),"Genre")]/span/a/text()');
|
||||
final id = parseHtml(res).selectFirst("div[data-id]").attr("data-id");
|
||||
final encrypt = vrfEncrypt(id);
|
||||
final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
|
||||
|
||||
final resEp =
|
||||
(await client.get(Uri.parse("$baseUrl/ajax/episode/list/$id?$vrf")))
|
||||
.body;
|
||||
|
||||
final html = json.decode(resEp)["result"];
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
final epsHtmls = parseHtml(html).select("div.episodes ul > li");
|
||||
|
||||
for (var epH in epsHtmls) {
|
||||
final epHtml = epH.outerHtml;
|
||||
final title = xpath(epHtml, '//li/@title').isNotEmpty
|
||||
? xpath(epHtml, '//li/@title').first
|
||||
: "";
|
||||
final ids = xpath(epHtml, '//a/@data-ids').first;
|
||||
final sub = xpath(epHtml, '//a/@data-sub').first;
|
||||
final dub = xpath(epHtml, '//a/@data-dub').first;
|
||||
final softsub = title.toLowerCase().contains("softsub") ? "1" : "";
|
||||
final fillerEp = title.toLowerCase().contains("filler") ? "1" : "";
|
||||
final epNum = xpath(epHtml, '//a/@data-num').first;
|
||||
String scanlator = "";
|
||||
if (sub == "1") {
|
||||
scanlator += "Sub";
|
||||
}
|
||||
if (softsub == "1") {
|
||||
scanlator += ", Softsub";
|
||||
}
|
||||
if (dub == "1") {
|
||||
scanlator += ", Dub";
|
||||
}
|
||||
if (fillerEp == "1") {
|
||||
scanlator += ", • Filler Episode";
|
||||
}
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Episode $epNum";
|
||||
episode.scanlator = scanlator;
|
||||
episode.url = "$ids&epurl=$url/ep-$epNum";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final ids = substringBefore(url, "&");
|
||||
final encrypt = vrfEncrypt(ids);
|
||||
final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/ajax/server/list/$ids?$vrf")))
|
||||
.body;
|
||||
final html = json.decode(res)["result"];
|
||||
|
||||
final vidsHtmls = parseHtml(html).select("div.servers > div");
|
||||
|
||||
List<MVideo> videos = [];
|
||||
for (var vidH in vidsHtmls) {
|
||||
final vidHtml = vidH.outerHtml;
|
||||
final type = xpath(vidHtml, '//div/@data-type').first;
|
||||
final serversIds = xpath(vidHtml, '//li/@data-link-id');
|
||||
for (int i = 0; i < serversIds.length; i++) {
|
||||
final serverId = serversIds[i];
|
||||
|
||||
final encrypt = vrfEncrypt(serverId);
|
||||
final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/ajax/server/$serverId?$vrf")))
|
||||
.body;
|
||||
final status = json.decode(res)["status"];
|
||||
if (status == 200) {
|
||||
List<MVideo> a = [];
|
||||
final url = vrfDecrypt(json.decode(res)["result"]["url"]);
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
final typeSelection = preferenceTypeSelection(source.id);
|
||||
if (typeSelection.contains(type.toLowerCase())) {
|
||||
if (url.contains("vidplay") || url.contains("mcloud")) {
|
||||
final hosterName =
|
||||
url.contains("vidplay") ? "VidPlay" : "MyCloud";
|
||||
if (hosterSelection.contains(hosterName.toLowerCase())) {
|
||||
a = await vidsrcExtractor(url, hosterName, type);
|
||||
}
|
||||
} else if (url.contains("mp4upload") &&
|
||||
hosterSelection.contains("mp4upload")) {
|
||||
a = await mp4UploadExtractor(url, null, "", type);
|
||||
} else if (url.contains("streamtape") &&
|
||||
hosterSelection.contains("streamtape")) {
|
||||
a = await streamTapeExtractor(url, "StreamTape - $type");
|
||||
} else if (url.contains("filemoon") &&
|
||||
hosterSelection.contains("filemoon")) {
|
||||
a = await filemoonExtractor(url, "", type);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//div[@class="item "]/div/div/div/a/@href');
|
||||
final names = xpath(res, '//div[@class="item "]/div/div/div/a/text()');
|
||||
final images = xpath(res, '//div[@class="item "]/div/div/a/img/@src');
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MManga anime = MManga();
|
||||
anime.name = names[i];
|
||||
anime.imageUrl = images[i];
|
||||
anime.link = urls[i];
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
List<int> rc4Encrypt(String key, List<int> message) {
|
||||
List<int> _key = utf8.encode(key);
|
||||
int _i = 0, _j = 0;
|
||||
List<int> _box = List.generate(256, (i) => i);
|
||||
|
||||
int x = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
x = (x + _box[i] + _key[i % _key.length]) % 256;
|
||||
var tmp = _box[i];
|
||||
_box[i] = _box[x];
|
||||
_box[x] = tmp;
|
||||
}
|
||||
|
||||
List<int> out = [];
|
||||
for (var char in message) {
|
||||
_i = (_i + 1) % 256;
|
||||
_j = (_j + _box[_i]) % 256;
|
||||
|
||||
var tmp = _box[_i];
|
||||
_box[_i] = _box[_j];
|
||||
_box[_j] = tmp;
|
||||
|
||||
final c = char ^ (_box[(_box[_i] + _box[_j]) % 256]);
|
||||
out.add(c);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
String vrfEncrypt(String input) {
|
||||
final rc4 = rc4Encrypt("ysJhV6U27FVIjjuk", input.codeUnits);
|
||||
final vrf = base64Url.encode(rc4);
|
||||
final vrf1 = base64.encode(vrf.codeUnits);
|
||||
List<int> vrf2 = vrfShift(vrf1.codeUnits);
|
||||
final vrf3 = base64.encode(vrf2);
|
||||
return utf8.decode(rot13(vrf3.codeUnits));
|
||||
}
|
||||
|
||||
String vrfDecrypt(String input) {
|
||||
final decode = base64Url.decode(input);
|
||||
final rc4 = rc4Encrypt("hlPeNwkncH0fq9so", decode);
|
||||
return Uri.decodeComponent(utf8.decode(rc4));
|
||||
}
|
||||
|
||||
List<int> vrfShift(List<int> vrf) {
|
||||
var shifts = [-3, 3, -4, 2, -2, 5, 4, 5];
|
||||
for (var i = 0; i < vrf.length; i++) {
|
||||
var shift = shifts[i % 8];
|
||||
vrf[i] = (vrf[i] + shift) & 0xFF;
|
||||
}
|
||||
return vrf;
|
||||
}
|
||||
|
||||
List<int> rot13(List<int> vrf) {
|
||||
for (var i = 0; i < vrf.length; i++) {
|
||||
var byte = vrf[i];
|
||||
if (byte >= 'A'.codeUnitAt(0) && byte <= 'Z'.codeUnitAt(0)) {
|
||||
vrf[i] = (byte - 'A'.codeUnitAt(0) + 13) % 26 + 'A'.codeUnitAt(0);
|
||||
} else if (byte >= 'a'.codeUnitAt(0) && byte <= 'z'.codeUnitAt(0)) {
|
||||
vrf[i] = (byte - 'a'.codeUnitAt(0) + 13) % 26 + 'a'.codeUnitAt(0);
|
||||
}
|
||||
}
|
||||
return vrf;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidsrcExtractor(
|
||||
String url, String name, String type) async {
|
||||
List<String> keys = json.decode((await client.get(Uri.parse(
|
||||
"https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json")))
|
||||
.body);
|
||||
List<MVideo> videoList = [];
|
||||
final host = Uri.parse(url).host;
|
||||
final apiUrl = await getApiUrl(url, keys);
|
||||
|
||||
final res = await client.get(Uri.parse(apiUrl), headers: {
|
||||
"Host": host,
|
||||
"Referer": Uri.decodeComponent(url),
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
});
|
||||
final result = json.decode(res.body)['result'];
|
||||
|
||||
if (result != 404) {
|
||||
String masterUrl =
|
||||
((result['sources'] as List<Map<String, dynamic>>).first)['file'];
|
||||
final tracks = (result['tracks'] as List)
|
||||
.where((e) => e['kind'] == 'captions' ? true : false)
|
||||
.toList();
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in tracks) {
|
||||
try {
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle
|
||||
..label = sub["label"]
|
||||
..file = sub["file"];
|
||||
subtitles.add(subtitle);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$name - $type - $quality"
|
||||
..headers = {"Referer": "https://$host/"}
|
||||
..subtitles = subtitles;
|
||||
videoList.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videoList;
|
||||
}
|
||||
|
||||
Future<String> getApiUrl(String url, List<String> keyList) async {
|
||||
final host = Uri.parse(url).host;
|
||||
final paramsToString = Uri.parse(url)
|
||||
.queryParameters
|
||||
.entries
|
||||
.map((e) => "${e.key}=${e.value}")
|
||||
.join("&");
|
||||
var vidId = substringBefore(substringAfterLast(url, "/"), "?");
|
||||
var encodedID = encodeID(vidId, keyList);
|
||||
final apiSlug = await callFromFuToken(host, encodedID);
|
||||
String apiUrlString = "";
|
||||
apiUrlString += "https://$host/$apiSlug";
|
||||
if (paramsToString.isNotEmpty) {
|
||||
apiUrlString += "?$paramsToString";
|
||||
}
|
||||
|
||||
return apiUrlString;
|
||||
}
|
||||
|
||||
String encodeID(String vidId, List<String> keyList) {
|
||||
var rc4Key1 = keyList[0];
|
||||
var rc4Key2 = keyList[1];
|
||||
final rc4 = rc4Encrypt(rc4Key1, vidId.codeUnits);
|
||||
final rc41 = rc4Encrypt(rc4Key2, rc4);
|
||||
return base64.encode(rc41).replaceAll("/", "_").trim();
|
||||
}
|
||||
|
||||
Future<String> callFromFuToken(String host, String data) async {
|
||||
final fuTokenScript =
|
||||
(await client.get(Uri.parse("https://$host/futoken"))).body;
|
||||
|
||||
String js = "";
|
||||
js += "(function";
|
||||
js += substringBefore(
|
||||
substringAfter(substringAfter(fuTokenScript, "window"), "function")
|
||||
.replaceAll("jQuery.ajax(", ""),
|
||||
"+location.search");
|
||||
js += "}(\"$data\"))";
|
||||
final jsRes = await evalJs(js);
|
||||
if (jsRes == "error") return "";
|
||||
return jsRes;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SelectFilter("OrderFilter", "Sort order", 0, [
|
||||
SelectFilterOption("Most relevance", "most_relevance"),
|
||||
SelectFilterOption("Recently updated", "recently_updated"),
|
||||
SelectFilterOption("Recently added", "recently_added"),
|
||||
SelectFilterOption("Release date", "release_date"),
|
||||
SelectFilterOption("Trending", "trending"),
|
||||
SelectFilterOption("Name A-Z", "title_az"),
|
||||
SelectFilterOption("Scores", "scores"),
|
||||
SelectFilterOption("MAL scores", "mal_scores"),
|
||||
SelectFilterOption("Most watched", "most_watched"),
|
||||
SelectFilterOption("Most favourited", "most_favourited"),
|
||||
SelectFilterOption("Number of episodes", "number_of_episodes"),
|
||||
]),
|
||||
SeparatorFilter(),
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "1"),
|
||||
CheckBoxFilter("Adventure", "2"),
|
||||
CheckBoxFilter("Avant Garde", "2262888"),
|
||||
CheckBoxFilter("Boys Love", "2262603"),
|
||||
CheckBoxFilter("Comedy", "4"),
|
||||
CheckBoxFilter("Demons", "4424081"),
|
||||
CheckBoxFilter("Drama", "7"),
|
||||
CheckBoxFilter("Ecchi", "8"),
|
||||
CheckBoxFilter("Fantasy", "9"),
|
||||
CheckBoxFilter("Girls Love", "2263743"),
|
||||
CheckBoxFilter("Gourmet", "2263289"),
|
||||
CheckBoxFilter("Harem", "11"),
|
||||
CheckBoxFilter("Horror", "14"),
|
||||
CheckBoxFilter("Isekai", "3457284"),
|
||||
CheckBoxFilter("Iyashikei", "4398552"),
|
||||
CheckBoxFilter("Josei", "15"),
|
||||
CheckBoxFilter("Kids", "16"),
|
||||
CheckBoxFilter("Magic", "4424082"),
|
||||
CheckBoxFilter("Mahou Shoujo", "3457321"),
|
||||
CheckBoxFilter("Martial Arts", "18"),
|
||||
CheckBoxFilter("Mecha", "19"),
|
||||
CheckBoxFilter("Military", "20"),
|
||||
CheckBoxFilter("Music", "21"),
|
||||
CheckBoxFilter("Mystery", "22"),
|
||||
CheckBoxFilter("Parody", "23"),
|
||||
CheckBoxFilter("Psychological", "25"),
|
||||
CheckBoxFilter("Reverse Harem", "4398403"),
|
||||
CheckBoxFilter("Romance", "26"),
|
||||
CheckBoxFilter("School", "28"),
|
||||
CheckBoxFilter("Sci-Fi", "29"),
|
||||
CheckBoxFilter("Seinen", "30"),
|
||||
CheckBoxFilter("Shoujo", "31"),
|
||||
CheckBoxFilter("Shounen", "33"),
|
||||
CheckBoxFilter("Slice of Life", "35"),
|
||||
CheckBoxFilter("Space", "36"),
|
||||
CheckBoxFilter("Sports", "37"),
|
||||
CheckBoxFilter("Super Power", "38"),
|
||||
CheckBoxFilter("Supernatural", "39"),
|
||||
CheckBoxFilter("Suspense", "2262590"),
|
||||
CheckBoxFilter("Thriller", "40"),
|
||||
CheckBoxFilter("Vampire", "41")
|
||||
]),
|
||||
GroupFilter("CountryFilter", "Country", [
|
||||
CheckBoxFilter("China", "120823"),
|
||||
CheckBoxFilter("Japan", "120822")
|
||||
]),
|
||||
GroupFilter("SeasonFilter", "Season", [
|
||||
CheckBoxFilter("Fall", "fall"),
|
||||
CheckBoxFilter("Summer", "summer"),
|
||||
CheckBoxFilter("Spring", "spring"),
|
||||
CheckBoxFilter("Winter", "winter"),
|
||||
CheckBoxFilter("Unknown", "unknown")
|
||||
]),
|
||||
GroupFilter("YearFilter", "Year", [
|
||||
CheckBoxFilter("2023", "2023"),
|
||||
CheckBoxFilter("2022", "2022"),
|
||||
CheckBoxFilter("2021", "2021"),
|
||||
CheckBoxFilter("2020", "2020"),
|
||||
CheckBoxFilter("2019", "2019"),
|
||||
CheckBoxFilter("2018", "2018"),
|
||||
CheckBoxFilter("2017", "2017"),
|
||||
CheckBoxFilter("2016", "2016"),
|
||||
CheckBoxFilter("2015", "2015"),
|
||||
CheckBoxFilter("2014", "2014"),
|
||||
CheckBoxFilter("2013", "2013"),
|
||||
CheckBoxFilter("2012", "2012"),
|
||||
CheckBoxFilter("2011", "2011"),
|
||||
CheckBoxFilter("2010", "2010"),
|
||||
CheckBoxFilter("2009", "2009"),
|
||||
CheckBoxFilter("2008", "2008"),
|
||||
CheckBoxFilter("2007", "2007"),
|
||||
CheckBoxFilter("2006", "2006"),
|
||||
CheckBoxFilter("2005", "2005"),
|
||||
CheckBoxFilter("2004", "2004"),
|
||||
CheckBoxFilter("2003", "2003"),
|
||||
CheckBoxFilter("2000s", "2000s"),
|
||||
CheckBoxFilter("1990s", "1990s"),
|
||||
CheckBoxFilter("1980s", "1980s"),
|
||||
CheckBoxFilter("1970s", "1970s"),
|
||||
CheckBoxFilter("1960s", "1960s"),
|
||||
CheckBoxFilter("1950s", "1950s"),
|
||||
CheckBoxFilter("1940s", "1940s"),
|
||||
CheckBoxFilter("1930s", "1930s"),
|
||||
CheckBoxFilter("1920s", "1920s"),
|
||||
CheckBoxFilter("1910s", "1910s")
|
||||
]),
|
||||
GroupFilter("TypeFilter", "Type", [
|
||||
CheckBoxFilter("Movie", "movie"),
|
||||
CheckBoxFilter("TV", "tv"),
|
||||
CheckBoxFilter("OVA", "ova"),
|
||||
CheckBoxFilter("ONA", "ona"),
|
||||
CheckBoxFilter("Special", "special"),
|
||||
CheckBoxFilter("Music", "music")
|
||||
]),
|
||||
GroupFilter("StatusFilter", "Status", [
|
||||
CheckBoxFilter("Not Yet Aired", "info"),
|
||||
CheckBoxFilter("Releasing", "releasing"),
|
||||
CheckBoxFilter("Completed", "completed")
|
||||
]),
|
||||
GroupFilter("LanguageFilter", "Language", [
|
||||
CheckBoxFilter("Sub and Dub", "subdub"),
|
||||
CheckBoxFilter("Sub", "sub"),
|
||||
CheckBoxFilter("Dub", "dub")
|
||||
]),
|
||||
GroupFilter("RatingFilter", "Rating", [
|
||||
CheckBoxFilter("G - All Ages", "g"),
|
||||
CheckBoxFilter("PG - Children", "pg"),
|
||||
CheckBoxFilter("PG 13 - Teens 13 and Older", "pg_13"),
|
||||
CheckBoxFilter("R - 17+, Violence & Profanity", "r"),
|
||||
CheckBoxFilter("R+ - Profanity & Mild Nudity", "r+"),
|
||||
CheckBoxFilter("Rx - Hentai", "rx")
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_domain1",
|
||||
title: "Preferred domain",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: [
|
||||
"aniwave.to",
|
||||
"aniwave.ws",
|
||||
"aniwave.li",
|
||||
"aniwave.vc"
|
||||
],
|
||||
entryValues: [
|
||||
"https://aniwave.to",
|
||||
"https://aniwave.ws",
|
||||
"https://aniwave.li",
|
||||
"https://aniwave.vc"
|
||||
]),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred Quality",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"]),
|
||||
ListPreference(
|
||||
key: "preferred_language",
|
||||
title: "Preferred Type",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Sub", "Softsub", "Dub"],
|
||||
entryValues: ["Sub", "Softsub", "Dub"]),
|
||||
ListPreference(
|
||||
key: "preferred_server",
|
||||
title: "Preferred server",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: [
|
||||
"VidPlay",
|
||||
"MyCloud",
|
||||
"Filemoon",
|
||||
"StreamTape",
|
||||
"Mp4Upload"
|
||||
],
|
||||
entryValues: [
|
||||
"vidplay",
|
||||
"mycloud",
|
||||
"filemoon",
|
||||
"streamtape",
|
||||
"mp4upload"
|
||||
]),
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selection",
|
||||
title: "Enable/Disable Hosts",
|
||||
summary: "",
|
||||
entries: [
|
||||
"VidPlay",
|
||||
"MyCloud",
|
||||
"Filemoon",
|
||||
"StreamTape",
|
||||
"Mp4Upload"
|
||||
],
|
||||
entryValues: [
|
||||
"vidplay",
|
||||
"mycloud",
|
||||
"filemoon",
|
||||
"streamtape",
|
||||
"mp4upload"
|
||||
],
|
||||
values: [
|
||||
"vidplay",
|
||||
"mycloud",
|
||||
"filemoon",
|
||||
"streamtape",
|
||||
"mp4upload"
|
||||
]),
|
||||
MultiSelectListPreference(
|
||||
key: "type_selection",
|
||||
title: "Enable/Disable Type",
|
||||
summary: "",
|
||||
entries: ["Sub", "Softsub", "Dub"],
|
||||
entryValues: ["sub", "softsub", "dub"],
|
||||
values: ["sub", "softsub", "dub"]),
|
||||
];
|
||||
}
|
||||
|
||||
List<String> preferenceHosterSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "hoster_selection");
|
||||
}
|
||||
|
||||
List<String> preferenceTypeSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "type_selection");
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
String server = getPreferenceValue(sourceId, "preferred_server");
|
||||
String lang = getPreferenceValue(sourceId, "preferred_language");
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(lang.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(lang.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchB = 1;
|
||||
}
|
||||
if (qualityMatchA != qualityMatchB) {
|
||||
return qualityMatchB - qualityMatchA;
|
||||
}
|
||||
|
||||
final regex = RegExp(r'(\d+)p');
|
||||
final matchA = regex.firstMatch(a.quality);
|
||||
final matchB = regex.firstMatch(b.quality);
|
||||
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
|
||||
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
|
||||
return qualityNumB - qualityNumA;
|
||||
});
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
Aniwave main(MSource source) {
|
||||
return Aniwave(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.7 KiB |
|
|
@ -1,16 +0,0 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get aniwave => _aniwave;
|
||||
const _aniwaveVersion = "0.0.65";
|
||||
const _aniwaveCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/aniwave/aniwave.dart";
|
||||
Source _aniwave = Source(
|
||||
name: "Aniwave",
|
||||
baseUrl: "https://aniwave.to",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/aniwave/icon.png",
|
||||
sourceCodeUrl: _aniwaveCodeUrl,
|
||||
version: _aniwaveVersion,
|
||||
isManga: false);
|
||||
|
|
@ -13,4 +13,4 @@ Source _dramacoolSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/dramacool/icon.png",
|
||||
sourceCodeUrl: _dramacoolSourceCodeUrl,
|
||||
version: _dramacoolVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -35,27 +35,23 @@ class GogoAnime extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse(
|
||||
"https://ajax.gogo-load.com/ajax/page-recent-release-ongoing.html?page=$page&type=1")))
|
||||
.body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls =
|
||||
xpath(res, '//*[@class="added_series_body popular"]/ul/li/a[1]/@href');
|
||||
final names = xpath(
|
||||
res, '//*[//*[@class="added_series_body popular"]/ul/li/a[1]/@title');
|
||||
List<String> images = [];
|
||||
List<String> imagess = xpath(res,
|
||||
'//*[//*[@class="added_series_body popular"]/ul/li/a/div[@class="thumbnail-popular"]/@style');
|
||||
for (var url in imagess) {
|
||||
images.add(url.replaceAll("background: url('", "").replaceAll("');", ""));
|
||||
String url = baseUrl;
|
||||
if (baseUrl.toLowerCase().contains("gogo")) {
|
||||
url = url + "/?page=$page";
|
||||
} else {
|
||||
url = url + "/home.html?page=$page";
|
||||
}
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select("div.img a");
|
||||
List<MManga> animeList = [];
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MManga anime = MManga();
|
||||
anime.name = names[i];
|
||||
anime.imageUrl = images[i];
|
||||
anime.link = urls[i];
|
||||
for (var element in elements) {
|
||||
var anime = MManga();
|
||||
anime.name = element.attr("title");
|
||||
anime.imageUrl = element.selectFirst("img")?.attr("src") ?? "";
|
||||
final slug = substringBefore(element.attr("href"), "-episode-");
|
||||
anime.link = "/category/$slug";
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
|
|
@ -175,10 +171,10 @@ class GogoAnime extends MProvider {
|
|||
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][5]/text()')
|
||||
.first
|
||||
.replaceAll("Status: ", "");
|
||||
anime.description = xpath(
|
||||
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][2]/text()')
|
||||
.first
|
||||
.replaceAll("Plot Summary: ", "");
|
||||
anime.description = parseHtml(res)
|
||||
.selectFirst("div.anime_info_body_bg > div.description")
|
||||
?.text ??
|
||||
"";
|
||||
anime.status = parseStatus(status, statusList);
|
||||
anime.genre = xpath(
|
||||
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][3]/text()')
|
||||
|
|
@ -589,6 +585,7 @@ class GogoAnime extends MProvider {
|
|||
CheckBoxFilter("Winter", "winter"),
|
||||
]),
|
||||
GroupFilter("YearFilter", "Year", [
|
||||
CheckBoxFilter("2024", "2024"),
|
||||
CheckBoxFilter("2023", "2023"),
|
||||
CheckBoxFilter("2022", "2022"),
|
||||
CheckBoxFilter("2021", "2021"),
|
||||
|
|
@ -985,6 +982,9 @@ class GogoAnime extends MProvider {
|
|||
SelectFilter("SeasonIFilter", "Season", 0, [
|
||||
SelectFilterOption("", ""),
|
||||
SelectFilterOption("Latest season", "new-season.html"),
|
||||
SelectFilterOption("Summer 2024", "sub-category/summer-2024-anime"),
|
||||
SelectFilterOption("Spring 2024", "sub-category/spring-2024-anime"),
|
||||
SelectFilterOption("Winter 2024", "sub-category/winter-2024-anime"),
|
||||
SelectFilterOption("Summer 2023", "sub-category/summer-2023-anime"),
|
||||
SelectFilterOption("Spring 2023", "sub-category/spring-2023-anime"),
|
||||
SelectFilterOption("Winter 2023", "sub-category/winter-2023-anime"),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get gogoanimeSource => _gogoanimeSource;
|
||||
const _gogoanimeVersion = "0.0.9";
|
||||
const _gogoanimeVersion = "0.1.15";
|
||||
const _gogoanimeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/gogoanime.dart";
|
||||
Source _gogoanimeSource = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _gogoanimeSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/icon.png",
|
||||
sourceCodeUrl: _gogoanimeSourceCodeUrl,
|
||||
version: _gogoanimeVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class KissKh extends MProvider {
|
|||
List<MManga> animeList = [];
|
||||
|
||||
for (var data in datas) {
|
||||
MManga anime = MManga();
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
|
|
@ -42,7 +42,7 @@ class KissKh extends MProvider {
|
|||
List<MManga> animeList = [];
|
||||
|
||||
for (var data in datas) {
|
||||
MManga anime = MManga();
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
|
|
@ -63,7 +63,7 @@ class KissKh extends MProvider {
|
|||
final jsonRes = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
for (var data in jsonRes) {
|
||||
MManga anime = MManga();
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
|
|
@ -79,7 +79,7 @@ class KissKh extends MProvider {
|
|||
{"Ongoing": 0, "Completed": 1}
|
||||
];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
var anime = MManga();
|
||||
final jsonRes = json.decode(res);
|
||||
final status = jsonRes["status"] ?? "";
|
||||
anime.description = jsonRes["description"];
|
||||
|
|
@ -123,22 +123,24 @@ class KissKh extends MProvider {
|
|||
final subRes =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/api/Sub/$id"))).body;
|
||||
var jsonSubRes = json.decode(subRes);
|
||||
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in jsonSubRes) {
|
||||
try {
|
||||
final subUrl = sub["src"];
|
||||
final label = sub["label"];
|
||||
MTrack subtitle = MTrack();
|
||||
final subUrl = sub["src"] as String;
|
||||
final label = sub["label"];
|
||||
if (subUrl.endsWith("txt")) {
|
||||
var subtitle = await getSubtitle(subUrl, label);
|
||||
subtitles.add(subtitle);
|
||||
} else {
|
||||
var subtitle = MTrack();
|
||||
subtitle
|
||||
..label = label
|
||||
..file = subUrl;
|
||||
subtitles.add(subtitle);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
final videoUrl = jsonRes["Video"];
|
||||
MVideo video = MVideo();
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
|
|
@ -150,6 +152,31 @@ class KissKh extends MProvider {
|
|||
};
|
||||
return [video];
|
||||
}
|
||||
|
||||
Future<MTrack> getSubtitle(String subUrl, String subLang) async {
|
||||
final response = await client.get(Uri.parse(subUrl), headers: {
|
||||
"referer": "https://kisskh.me/",
|
||||
"origin": "https://kisskh.me"
|
||||
});
|
||||
final subtitleData = response.body;
|
||||
String decrypted = "\n";
|
||||
for (String line in subtitleData.split('\n')) {
|
||||
decrypted += "${decrypt(line.trim())}\n";
|
||||
}
|
||||
var subtitle = MTrack();
|
||||
subtitle
|
||||
..label = subLang
|
||||
..file = decrypted;
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
String decrypt(String data) {
|
||||
final key = utf8.decode(
|
||||
[56, 48, 53, 54, 52, 56, 51, 54, 52, 54, 51, 50, 56, 55, 54, 51]);
|
||||
final iv = utf8.decode(
|
||||
[54, 56, 53, 50, 54, 49, 50, 51, 55, 48, 49, 56, 53, 50, 55, 51]);
|
||||
return cryptoHandler(data, iv, key, false);
|
||||
}
|
||||
}
|
||||
|
||||
KissKh main(MSource source) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get kisskhSource => _kisskhSource;
|
||||
const _kisskhVersion = "0.0.55";
|
||||
const _kisskhVersion = "0.0.6";
|
||||
const _kisskhSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/kisskh.dart";
|
||||
Source _kisskhSource = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _kisskhSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/icon.png",
|
||||
sourceCodeUrl: _kisskhSourceCodeUrl,
|
||||
version: _kisskhVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -244,7 +244,8 @@ class NineAnimeTv extends MProvider {
|
|||
}
|
||||
videoResJson = decryptAESCryptoJS(ciphertext, password);
|
||||
} else {
|
||||
videoResJson = resServer;
|
||||
videoResJson = json.encode(
|
||||
(json.decode(resServer)["sources"] as List<Map<String, dynamic>>));
|
||||
}
|
||||
|
||||
String masterUrl =
|
||||
|
|
@ -399,6 +400,7 @@ class NineAnimeTv extends MProvider {
|
|||
CheckBoxFilter("Winter", "4")
|
||||
]),
|
||||
GroupFilter("YearFilter", "Year", [
|
||||
CheckBoxFilter("2024", "2024"),
|
||||
CheckBoxFilter("2023", "2023"),
|
||||
CheckBoxFilter("2022", "2022"),
|
||||
CheckBoxFilter("2021", "2021"),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get nineanimetv => _nineanimetv;
|
||||
const _nineanimetvVersion = "0.0.3";
|
||||
const _nineanimetvVersion = "0.0.4";
|
||||
const _nineanimetvCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/nineanimetv.dart";
|
||||
Source _nineanimetv = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _nineanimetv = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/icon.png",
|
||||
sourceCodeUrl: _nineanimetvCodeUrl,
|
||||
version: _nineanimetvVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get uhdmoviesSource => _uhdmoviesSource;
|
||||
const _uhdmoviesVersion = "0.0.35";
|
||||
const _uhdmoviesVersion = "0.0.45";
|
||||
const _uhdmoviesSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/uhdmovies.dart";
|
||||
Source _uhdmoviesSource = Source(
|
||||
name: "UHD Movies",
|
||||
baseUrl: "https://uhdmovies.zip",
|
||||
baseUrl: "https://uhdmovies.fans",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/icon.png",
|
||||
sourceCodeUrl: _uhdmoviesSourceCodeUrl,
|
||||
version: _uhdmoviesVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class UHDMovies extends MProvider {
|
|||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain");
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain_new");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
|
|
@ -126,13 +126,13 @@ class UHDMovies extends MProvider {
|
|||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "pref_domain",
|
||||
key: "pref_domain_new",
|
||||
title: "Currently used domain",
|
||||
summary: "",
|
||||
value: "https://uhdmovies.zip",
|
||||
value: "https://uhdmovies.fans",
|
||||
dialogTitle: "Currently used domain",
|
||||
dialogMessage: "",
|
||||
text: "https://uhdmovies.zip"),
|
||||
text: "https://uhdmovies.fans"),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
311
dart/anime/src/es/animeonlineninja/animeonlineninja.dart
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeOnlineNinja extends MProvider {
|
||||
AnimeOnlineNinja({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/tendencias"))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String pageStr = page == 1 ? "" : "page/$page/";
|
||||
final res = (await client.get(Uri.parse(
|
||||
"${source.baseUrl}/$pageStr?s=${query.replaceAll(" ", "+")}")))
|
||||
.body;
|
||||
return parseAnimeList(res,
|
||||
selector: "div.result-item div.image a",
|
||||
hasNextPage: parseHtml(res)
|
||||
.selectFirst(
|
||||
"div.pagination > *:last-child:not(span):not(.current)")
|
||||
?.text !=
|
||||
null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final document = parseHtml(res);
|
||||
anime.description = document.selectFirst("div#info").text;
|
||||
anime.genre = document
|
||||
.selectFirst("div.sheader")
|
||||
.select("div.data > div.sgeneros > a")
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
final seasonElements = document.select("div#seasons > div");
|
||||
if (seasonElements.isEmpty) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Película";
|
||||
episode.url = getUrlWithoutDomain(url);
|
||||
episodesList.add(episode);
|
||||
} else {
|
||||
for (var seasonElement in seasonElements) {
|
||||
final seasonName = seasonElement.selectFirst("span.se-t").text;
|
||||
for (var epElement in seasonElement.select("ul.episodios > li")) {
|
||||
final href = epElement.selectFirst("a[href]");
|
||||
final epNum = epElement.selectFirst('div.numerando')?.text ?? "0 - 0";
|
||||
MChapter episode = MChapter();
|
||||
episode.name =
|
||||
"Season $seasonName x ${substringAfter(epNum, '- ')} ${href.text}";
|
||||
episode.url = getUrlWithoutDomain(href!.getHref);
|
||||
episodesList.add(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
final players = document.select("ul#playeroptionsul li");
|
||||
List<MVideo> videos = [];
|
||||
for (var player in players) {
|
||||
final name = player.selectFirst("span.title").text;
|
||||
final type = player.attr("data-type");
|
||||
final id = player.attr("data-post");
|
||||
final num = player.attr("data-nume");
|
||||
final resUrl = (await client.get(Uri.parse(
|
||||
"${source.baseUrl}/wp-json/dooplayer/v1/post/$id?type=$type&source=$num")))
|
||||
.body;
|
||||
final url =
|
||||
substringBefore(substringAfter(resUrl, "\"embed_url\":\""), "\",")
|
||||
.replaceAll("\\", "");
|
||||
videos.addAll(await extractVideos(url, name));
|
||||
}
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractVideos(String url, String lang) async {
|
||||
List<MVideo> videos = [];
|
||||
List<MVideo> a = [];
|
||||
if (url.contains("saidochesto.top") || lang == "MULTISERVER") {
|
||||
return await extractFromMulti(url);
|
||||
} else if (["filemoon", "moon", "filemooon"].any((a) => url.contains(a))) {
|
||||
a = await filemoonExtractor(url, "$lang Filemoon - ", "");
|
||||
} else if (["https://dood", "https://ds2play", "https://d0"]
|
||||
.any((a) => url.contains(a))) {
|
||||
a = await doodExtractor(url, "$lang DoodStream");
|
||||
} else if (["streamtape", "stp", "stape"].any((a) => url.contains(a))) {
|
||||
a = await streamTapeExtractor(url, "$lang StreamTape");
|
||||
} else if (url.contains("uqload")) {
|
||||
a = await uqloadExtractor(url, lang);
|
||||
} else if (url.contains("wolfstream")) {
|
||||
final resUrl = (await client.get(Uri.parse(url))).body;
|
||||
final jsData =
|
||||
parseHtml(resUrl).selectFirst("script:contains(sources)").text;
|
||||
final videoUrl =
|
||||
substringBefore(substringAfter(jsData, "{file:\""), "\"");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang WolfStream";
|
||||
|
||||
a = [video];
|
||||
} else if (["wishembed", "streamwish", "strwish", "wish"]
|
||||
.any((a) => url.contains(a))) {
|
||||
a = await streamWishExtractor(url, "$lang StreamWish");
|
||||
} else if (url.contains("mp4upload")) {
|
||||
a = await mp4UploadExtractor(url, null, "$lang", "");
|
||||
} else if (["vidhide", "filelions.top", "vid."]
|
||||
.any((a) => url.contains(a))) {
|
||||
a = await streamHideExtractor(url, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamHideExtractor(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(
|
||||
substringAfter(unpackJs(res), "sources:"), "file:\""),
|
||||
"src:\""),
|
||||
'"');
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
List<MVideo> videos = [];
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$prefix StreamHideVid - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> uqloadExtractor(String url, String lang) async {
|
||||
final Client client =
|
||||
Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "sources:")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final videoUrl =
|
||||
substringBefore(substringAfter(js.first, "sources: [\""), '"');
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang Uqload"
|
||||
..headers = {"Referer": "${Uri.parse(url).origin}/"};
|
||||
return [video];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractFromMulti(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
|
||||
final prefLang = getPreferenceValue(source.id, "preferred_lang");
|
||||
String langSelector = "";
|
||||
if (prefLang.isEmpty) {
|
||||
langSelector = "div";
|
||||
} else {
|
||||
langSelector = "div.OD_$prefLang";
|
||||
}
|
||||
List<MVideo> videos = [];
|
||||
for (var element in document.select("div.ODDIV $langSelector > li")) {
|
||||
final hosterUrl =
|
||||
substringBefore(substringAfter(element.attr("onclick"), "('"), "')");
|
||||
String lang = "";
|
||||
if (langSelector == "div") {
|
||||
lang = substringBefore(
|
||||
substringAfter(element.parent?.attr("class"), "OD_", ""), " ");
|
||||
} else {
|
||||
lang = prefLang;
|
||||
}
|
||||
videos.addAll(await extractVideos(hosterUrl, lang));
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res,
|
||||
{String selector = "article.w_item_a > a", bool hasNextPage = false}) {
|
||||
final elements = parseHtml(res).select(selector);
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
final url = getUrlWithoutDomain(element.getHref);
|
||||
if (!url.startsWith("/episodio/")) {
|
||||
MManga anime = MManga();
|
||||
final img = element.selectFirst("img");
|
||||
anime.name = img.attr("alt");
|
||||
anime.imageUrl = img?.attr("data-src") ??
|
||||
img?.attr("data-lazy-src") ??
|
||||
img?.attr("srcset") ??
|
||||
img?.getSrc;
|
||||
anime.link = url;
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_lang",
|
||||
title: "Preferred language",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["SUB", "All", "ES", "LAT"],
|
||||
entryValues: ["SUB", "", "ES", "LAT"]),
|
||||
ListPreference(
|
||||
key: "preferred_server1",
|
||||
title: "Preferred server",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: [
|
||||
"Filemoon",
|
||||
"DoodStream",
|
||||
"StreamTape",
|
||||
"Uqload",
|
||||
"WolfStream",
|
||||
"saidochesto.top",
|
||||
"VidHide",
|
||||
"StreamWish",
|
||||
"Mp4Upload"
|
||||
],
|
||||
entryValues: [
|
||||
"Filemoon",
|
||||
"DoodStream",
|
||||
"StreamTape",
|
||||
"Uqload",
|
||||
"WolfStream",
|
||||
"saidochesto.top",
|
||||
"VidHide",
|
||||
"StreamWish",
|
||||
"Mp4Upload"
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String prefLang = getPreferenceValue(source.id, "preferred_lang");
|
||||
String server = getPreferenceValue(sourceId, "preferred_server1");
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
|
||||
if (a.quality.toLowerCase().contains(prefLang.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.toLowerCase().contains(prefLang.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchB = 1;
|
||||
}
|
||||
if (qualityMatchA != qualityMatchB) {
|
||||
return qualityMatchB - qualityMatchA;
|
||||
}
|
||||
|
||||
final regex = RegExp(r'(\d+)p');
|
||||
final matchA = regex.firstMatch(a.quality);
|
||||
final matchB = regex.firstMatch(b.quality);
|
||||
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
|
||||
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
|
||||
return qualityNumB - qualityNumA;
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AnimeOnlineNinja main(MSource source) {
|
||||
return AnimeOnlineNinja(source: source);
|
||||
}
|
||||
BIN
dart/anime/src/es/animeonlineninja/icon.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
16
dart/anime/src/es/animeonlineninja/source.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animeonlineninjaSource => _animeonlineninjaSource;
|
||||
const _animeonlineninjaVersion = "0.0.3";
|
||||
const _animeonlineninjaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/animeonlineninja.dart";
|
||||
Source _animeonlineninjaSource = Source(
|
||||
name: "AnimeOnline.Ninja",
|
||||
baseUrl: "https://ww3.animeonline.ninja",
|
||||
lang: "es",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/icon.png",
|
||||
sourceCodeUrl: _animeonlineninjaSourceCodeUrl,
|
||||
version: _animeonlineninjaVersion,
|
||||
itemType: ItemType.anime);
|
||||
|
|
@ -32,10 +32,10 @@ class AnimeSama extends MProvider {
|
|||
final latest = document
|
||||
.select("h2")
|
||||
.where((MElement e) =>
|
||||
e.outerHtml.toLowerCase().contains("derniers ajouts"))
|
||||
e.outerHtml.toLowerCase().contains("derniers épisodes ajoutés"))
|
||||
.toList();
|
||||
final seasonElements = (latest.first.nextElementSibling as MElement)
|
||||
.select(".scrollBarStyled > div")
|
||||
final seasonElements = (latest.first.parent.nextElementSibling as MElement)
|
||||
.select("div")
|
||||
.toList();
|
||||
List<MManga> seasons = [];
|
||||
for (var seasonElement in seasonElements) {
|
||||
|
|
@ -128,13 +128,22 @@ class AnimeSama extends MProvider {
|
|||
List<MChapter>? episodesList = [];
|
||||
for (var episodeNumber = 0; episodeNumber < maxLength; episodeNumber++) {
|
||||
List<String> langs = [];
|
||||
bool isVf = false;
|
||||
int iVostfr = 0;
|
||||
int iVf = 0;
|
||||
List<Map<String, dynamic>> players = [];
|
||||
for (var playerListt in playersList) {
|
||||
for (var player in playerListt["players"]) {
|
||||
for (var playerList in playersList) {
|
||||
for (var player in playerList["players"]) {
|
||||
if (player.length > episodeNumber) {
|
||||
langs.add(playerListt["lang"]);
|
||||
players.add(
|
||||
{"lang": playerListt["lang"], "player": player[episodeNumber]});
|
||||
isVf = playerList["lang"] == "vf";
|
||||
if ((isVf && iVf < 2) || (!isVf && iVostfr < 2)) {
|
||||
var lang = playerList["lang"];
|
||||
if (!langs.contains(lang)) {
|
||||
langs.add(lang);
|
||||
}
|
||||
players.add({"lang": lang, "player": player[episodeNumber]});
|
||||
isVf ? iVf++ : iVostfr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -161,16 +170,9 @@ class AnimeSama extends MProvider {
|
|||
String playerUrl = player["player"];
|
||||
List<MVideo> a = [];
|
||||
if (playerUrl.contains("sendvid")) {
|
||||
a = await sendVidExtractor(playerUrl, null, lang);
|
||||
} else if (playerUrl.contains("anime-sama.fr")) {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = playerUrl
|
||||
..originalUrl = playerUrl
|
||||
..quality = "${lang} - AS Player";
|
||||
a = [video];
|
||||
} else if (playerUrl.contains("sibnet.ru")) {
|
||||
a = await sibnetExtractor(playerUrl, lang);
|
||||
a = await sendVidExtractorr(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("vidmoly")) {
|
||||
a = await vidmolyExtractor(playerUrl, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
|
@ -178,6 +180,94 @@ class AnimeSama extends MProvider {
|
|||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidmolyExtractor(String url, String lang) async {
|
||||
final headers = {
|
||||
'Referer': 'https://vidmoly.to',
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes =
|
||||
await client.get(Uri.parse(playlistUrl), headers: headers);
|
||||
|
||||
if (masterPlaylistRes.statusCode == 200) {
|
||||
for (var it
|
||||
in substringAfter(masterPlaylistRes.body, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang Vidmoly $quality"
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
|
|
@ -240,74 +330,73 @@ class AnimeSama extends MProvider {
|
|||
|
||||
Future<List<MManga>> fetchAnimeSeasons(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
var document = parseHtml(res);
|
||||
String animeName = document.getElementById("titreOeuvre")?.text ?? "";
|
||||
|
||||
var seasonRegex =
|
||||
RegExp("^\\s*panneauAnime\\(\"(.*)\", \"(.*)\"\\)", multiLine: true);
|
||||
var seasonRegex = RegExp(r'panneauAnime\("(.*?)",\s*"(.*?)"\);');
|
||||
var scripts = document
|
||||
.select("h2 + p + div > script, h2 + div > script")
|
||||
.map((MElement element) => element.text)
|
||||
.toList()
|
||||
.join("");
|
||||
List<MManga> animeList = [];
|
||||
|
||||
List<RegExpMatch> seasonRegexReg = seasonRegex.allMatches(scripts).toList();
|
||||
for (var animeIndex = 0; animeIndex < seasonRegexReg.length; animeIndex++) {
|
||||
final seasonName = seasonRegexReg[animeIndex].group(1);
|
||||
final seasonStem = seasonRegexReg[animeIndex].group(2);
|
||||
if (seasonStem.toLowerCase().contains("film")) {
|
||||
var moviesUrl = "$url/$seasonStem";
|
||||
var movies = await fetchPlayers(moviesUrl);
|
||||
if (movies.isNotEmpty) {
|
||||
var movieNameRegex =
|
||||
RegExp("^\\s*newSPF\\(\"(.*)\"\\);", multiLine: true);
|
||||
var moviesDoc = (await client.get(Uri.parse(moviesUrl))).body;
|
||||
List<RegExpMatch> matches =
|
||||
movieNameRegex.allMatches(moviesDoc).toList();
|
||||
if (seasonName != "nom" && seasonStem != "url") {
|
||||
if (seasonStem.toLowerCase().contains("film")) {
|
||||
var moviesUrl = "$url/$seasonStem";
|
||||
var movies = await fetchPlayers(moviesUrl);
|
||||
if (movies.isNotEmpty) {
|
||||
var movieNameRegex =
|
||||
RegExp("^\\s*newSPF\\(\"(.*)\"\\);", multiLine: true);
|
||||
var moviesDoc = (await client.get(Uri.parse(moviesUrl))).body;
|
||||
List<RegExpMatch> matches =
|
||||
movieNameRegex.allMatches(moviesDoc).toList();
|
||||
|
||||
for (var i = 0; i < movies.length; i++) {
|
||||
var title = "";
|
||||
if (animeIndex == 0 && movies.length == 1) {
|
||||
title = animeName;
|
||||
} else if (matches.length > i) {
|
||||
title = "$animeName ${(matches[i]).group(1)}";
|
||||
} else if (movies.length == 1) {
|
||||
title = "$animeName Film";
|
||||
} else {
|
||||
title = "$animeName Film ${i + 1}";
|
||||
for (var i = 0; i < movies.length; i++) {
|
||||
var title = "";
|
||||
if (animeIndex == 0 && movies.length == 1) {
|
||||
title = animeName;
|
||||
} else if (matches.length > i) {
|
||||
title = "$animeName ${(matches[i]).group(1)}";
|
||||
} else if (movies.length == 1) {
|
||||
title = "$animeName Film";
|
||||
} else {
|
||||
title = "$animeName Film ${i + 1}";
|
||||
}
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()') ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description = document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()') ??
|
||||
"";
|
||||
|
||||
anime.name = title;
|
||||
anime.link = "$moviesUrl#$i";
|
||||
anime.status = MStatus.completed;
|
||||
animeList.add(anime);
|
||||
}
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()') ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description = document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()') ??
|
||||
"";
|
||||
|
||||
anime.name = title;
|
||||
anime.link = "$moviesUrl#$i";
|
||||
anime.status = MStatus.completed;
|
||||
animeList.add(anime);
|
||||
}
|
||||
} else {
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()') ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description = document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()') ??
|
||||
"";
|
||||
anime.name =
|
||||
'$animeName ${substringBefore(seasonName, ',').replaceAll('"', "")}';
|
||||
anime.link = "$url/$seasonStem";
|
||||
animeList.add(anime);
|
||||
}
|
||||
} else {
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()') ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description = document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()') ??
|
||||
"";
|
||||
anime.name =
|
||||
'$animeName ${substringBefore(seasonName, ',').replaceAll('"', "")}';
|
||||
anime.link = "$url/$seasonStem";
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
return animeList;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesamaSource => _animesama;
|
||||
const animesamaVersion = "0.0.25";
|
||||
const animesamaVersion = "0.0.4";
|
||||
const animesamaCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesama/animesama.dart";
|
||||
Source _animesama = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _animesama = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesama/icon.png",
|
||||
sourceCodeUrl: animesamaCodeUrl,
|
||||
version: animesamaVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ class AnimesUltra extends MProvider {
|
|||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
String get baseUrl => source.baseUrl;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).body;
|
||||
final res = (await client.get(Uri.parse(baseUrl))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res,
|
||||
|
|
@ -33,7 +36,7 @@ class AnimesUltra extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).body;
|
||||
final res = (await client.get(Uri.parse(baseUrl))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res,
|
||||
|
|
@ -56,7 +59,10 @@ class AnimesUltra extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).body;
|
||||
query = query.trim().replaceAll(" ", "+");
|
||||
final res = (await client.get(Uri.parse(
|
||||
"$baseUrl/index.php?do=search&subaction=search&story=$query")))
|
||||
.body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//*[@class="film-poster"]/a/@href');
|
||||
|
|
@ -71,7 +77,7 @@ class AnimesUltra extends MProvider {
|
|||
animeList.add(anime);
|
||||
}
|
||||
|
||||
return MPages(animeList, false);
|
||||
return MPages(animeList.reversed.toList(), false);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -81,7 +87,8 @@ class AnimesUltra extends MProvider {
|
|||
];
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
var anime = MManga();
|
||||
final doc = parseHtml(res);
|
||||
anime.description =
|
||||
xpath(res, '//*[@class="film-description m-hide"]/text()').first;
|
||||
|
||||
|
|
@ -91,66 +98,115 @@ class AnimesUltra extends MProvider {
|
|||
anime.status = parseStatus(status, statusList);
|
||||
anime.genre = xpath(res,
|
||||
'//*[@class="item item-list" and contains(text(),"Genres:")]/a/text()');
|
||||
anime.author = xpath(res,
|
||||
'//*[@class="item item-title" and contains(text(),"Studio:")]/span[2]/text()')
|
||||
.first;
|
||||
final urlEp = url.replaceAll('.html', '/episode-1.html');
|
||||
final resEpWebview =
|
||||
await getHtmlViaWebview(urlEp, '//*[@class="ss-list"]/a/@href');
|
||||
final epUrls =
|
||||
xpath(resEpWebview, '//*[@class="ss-list"]/a/@href').reversed.toList();
|
||||
final names = xpath(resEpWebview,
|
||||
'//*[@class="ss-list"]/a/div[@class="ssli-detail"]/div/text()')
|
||||
.reversed
|
||||
.toList();
|
||||
|
||||
anime.author = doc.xpathFirst(
|
||||
'//*[@class="item item-title" and contains(text(),"Studio:")]/span[2]/text()');
|
||||
final episodesLength = int.parse(substringBefore(
|
||||
doc.xpathFirst('//*[@class="film-stats"]/span[7]/text()'), "/")
|
||||
.replaceAll("Ep", ""));
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = names[i];
|
||||
episode.url = epUrls[i];
|
||||
|
||||
for (var i = 0; i < episodesLength; i++) {
|
||||
var episode = MChapter();
|
||||
episode.name = "Episode ${i + 1}";
|
||||
episode.url = url.replaceAll('.html', '/episode-${i + 1}.html');
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList;
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final resWebview = await getHtmlViaWebview(
|
||||
url, '//*[@class="ps__-list"]/div/@data-server-id');
|
||||
final resHtml = (await client.get(Uri.parse(url))).body;
|
||||
final id = url.split('/')[4].split('-')[0];
|
||||
final resServer = (await client
|
||||
.get(Uri.parse("$baseUrl/engine/ajax/full-story.php?newsId=$id")))
|
||||
.body;
|
||||
|
||||
final serverIds =
|
||||
xpath(resWebview, '//*[@class="ps__-list"]/div/@data-server-id');
|
||||
final serverNames =
|
||||
xpath(resWebview, '//*[@class="ps__-list"]/div/a/text()');
|
||||
xpath(resHtml, '//*[@class="ps__-list"]/div/@data-server-id');
|
||||
final serverNames = xpath(resHtml, '//*[@class="ps__-list"]/div/a/text()');
|
||||
List<String> serverUrls = [];
|
||||
for (var id in serverIds) {
|
||||
final serversUrls =
|
||||
xpath(resWebview, '//*[@id="content_player_${id}"]/text()').first;
|
||||
final serversUrls = xpath(jsonDecode(resServer)["html"],
|
||||
'//*[@id="content_player_${id}"]/text()')
|
||||
.first;
|
||||
serverUrls.add(serversUrls);
|
||||
}
|
||||
List<MVideo> videos = [];
|
||||
for (var i = 0; i < serverNames.length; i++) {
|
||||
final name = serverNames[i];
|
||||
final url = serverUrls[i];
|
||||
|
||||
List<MVideo> a = [];
|
||||
if (name.contains("Sendvid")) {
|
||||
a = await sendVidExtractor(url.replaceAll("https:////", "https://"),
|
||||
json.encode({"Referer": "${source.baseUrl}/"}), "");
|
||||
a = await sendVidExtractorr(
|
||||
url.replaceAll("https:////", "https://"), "");
|
||||
} else if (name.contains("Sibnet")) {
|
||||
a = await sibnetExtractor(
|
||||
"https://video.sibnet.ru/shell.php?videoid=$url");
|
||||
} else if (name.contains("Mytv")) {
|
||||
a = await myTvExtractor("https://www.myvi.tv/embed/$url");
|
||||
} else if (name.contains("Fmoon")) {
|
||||
a = await filemoonExtractor(url, "", "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AnimesUltra main(MSource source) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesultraSource => _animesultraSource;
|
||||
const _animesultraVersion = "0.0.65";
|
||||
const _animesultraVersion = "0.0.75";
|
||||
const _animesultraSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesultra/animesultra.dart";
|
||||
Source _animesultraSource = Source(
|
||||
name: "AnimesUltra",
|
||||
baseUrl: "https://ww.animesultra.net",
|
||||
baseUrl: "https://w2.animesultra.net",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesultra/icon.png",
|
||||
sourceCodeUrl: _animesultraSourceCodeUrl,
|
||||
version: _animesultraVersion,
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: false);
|
||||
|
|
|
|||
490
dart/anime/src/fr/anizone/anizone.dart
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AniZone extends MProvider {
|
||||
AniZone({required this.source});
|
||||
|
||||
final MSource source;
|
||||
final Client client = Client(source);
|
||||
|
||||
// Constants for the xpath
|
||||
static const String urlXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-poster"]/a/@href';
|
||||
static const String nameXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-detail"]/h3/text()';
|
||||
static const String imageXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-poster"]/img/@data-src';
|
||||
|
||||
// Methods for fetching the manga list (popular, latest & search)
|
||||
Future<MPages> _getMangaList(String url) async {
|
||||
final doc = (await client.get(Uri.parse(url))).body;
|
||||
List<MManga> animeList = [];
|
||||
|
||||
final urls = xpath(doc, urlXpath);
|
||||
final names = xpath(doc, nameXpath);
|
||||
final images = xpath(doc, imageXpath);
|
||||
|
||||
if (urls.isEmpty || names.isEmpty || images.isEmpty) {
|
||||
return MPages([], false);
|
||||
}
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MManga anime = MManga();
|
||||
anime.name = names[i];
|
||||
anime.imageUrl = images[i];
|
||||
anime.link = urls[i];
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
return MPages(animeList, urls.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return _getMangaList("${source.baseUrl}/most-popular/?page=$page");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return _getMangaList("${source.baseUrl}/recently-added/?page=$page");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String baseUrl = "${source.baseUrl}/filter?keyword=$query";
|
||||
|
||||
Map<String, List<String>> filterMap = {
|
||||
"type": [],
|
||||
"status": [],
|
||||
"season": [],
|
||||
"lang": [],
|
||||
"genre": []
|
||||
};
|
||||
|
||||
// Regroupement des filtres avec une logique générique
|
||||
final filterHandlers = {
|
||||
"TypeFilter": "type",
|
||||
"LanguageFilter": "lang",
|
||||
"SaisonFilter": "season",
|
||||
"StatusFilter": "status",
|
||||
"GenreFilter": "genre"
|
||||
};
|
||||
|
||||
for (var filter in filterList.filters) {
|
||||
if (filterHandlers.containsKey(filter.type)) {
|
||||
var key = filterHandlers[filter.type]!;
|
||||
for (var stateItem in filter.state as List) {
|
||||
if (stateItem.state == true) {
|
||||
filterMap[key]?.add(stateItem.value as String);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add filters to the url dynamically
|
||||
for (var entry in filterMap.entries) {
|
||||
List<String> values = entry.value;
|
||||
if (values.isNotEmpty) {
|
||||
baseUrl += '&${entry.key}=${values.join("%2C")}';
|
||||
}
|
||||
}
|
||||
|
||||
return _getMangaList("$baseUrl&page=$page");
|
||||
}
|
||||
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga anime = MManga();
|
||||
try {
|
||||
final doc = (await client.get(Uri.parse(url))).body;
|
||||
final description = xpath(doc, '//p[contains(@class,"short")]/text()');
|
||||
anime.description = description.isNotEmpty ? description.first : "";
|
||||
|
||||
final statusList = xpath(doc,
|
||||
'//div[contains(@class,"col2")]//div[contains(@class,"item")]//div[contains(@class,"item-content")]/text()');
|
||||
if (statusList.isNotEmpty) {
|
||||
if (statusList[0] == "Terminer") {
|
||||
anime.status = MStatus.completed;
|
||||
} else if (statusList[0] == "En cours") {
|
||||
anime.status = MStatus.ongoing;
|
||||
} else {
|
||||
anime.status = MStatus.unknown;
|
||||
}
|
||||
} else {
|
||||
anime.status = MStatus.unknown;
|
||||
}
|
||||
|
||||
anime.genre = xpath(doc,
|
||||
'//div[contains(@class,"item")]//div[contains(@class,"item-content")]//a[contains(@href,"genre")]/text()');
|
||||
|
||||
final regex = RegExp(r'(\d+)$');
|
||||
final match = regex.firstMatch(url);
|
||||
|
||||
if (match == null) {
|
||||
throw Exception('Numéro de l\'épisode non trouvé dans l\'URL.');
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse(
|
||||
"${source.baseUrl}/ajax/episode/list/${match.group(1)}")))
|
||||
.body;
|
||||
|
||||
List<MChapter> episodesList = [];
|
||||
|
||||
final episodeElements =
|
||||
parseHtml(json.decode(res)["html"]).select(".ep-item");
|
||||
|
||||
// Associer chaque titre à son URL et récupérer les vidéos
|
||||
for (var element in episodeElements) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = element.attr("title");
|
||||
|
||||
String id = substringAfterLast(element.attr("href"), "=");
|
||||
episode.url = "${source.baseUrl}/ajax/episode/servers?episodeId=$id";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
|
||||
return anime;
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des détails: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final videoRes = (await client
|
||||
.get(Uri.parse(url), headers: {"Referer": "${source.baseUrl}/"}))
|
||||
.body;
|
||||
|
||||
final lang = xpath(videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/@data-type');
|
||||
final links = xpath(videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/@data-id');
|
||||
final playersNames = xpath(videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/text()');
|
||||
List<Map<String, String>> players = [];
|
||||
for (int j = 0; j < links.length; j++) {
|
||||
// schema of players https://v1.animesz.xyz/ajax/episode/servers?episodeId=(id_episode)
|
||||
// schema or url https://v1.animesz.xyz/ajax/episode/sources?id=(player_id)&epid=(id_episode)
|
||||
if (playersNames.isNotEmpty && playersNames[j] == "sibnet") {
|
||||
final playerUrl =
|
||||
"https://video.sibnet.ru/shell.php?videoid=${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "sendvid") {
|
||||
final playerUrl = "https://sendvid.com/embed/${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "VidCDN") {
|
||||
final playerUrl =
|
||||
"https://r.vidcdn.xyz/v1/api/get_sources/${links[j].replaceFirst(RegExp(r'vidcdn$'), '')}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "Voe") {
|
||||
final playerUrl = "https://voe.sx/e/${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "Fmoon") {
|
||||
final playerUrl =
|
||||
"https://filemoon.sx/e/${links[j]}&data-realid=${links[j]}&epid=${substringAfter(url, "episodeId=")}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
}
|
||||
}
|
||||
|
||||
List<MVideo> videos = [];
|
||||
for (var player in players) {
|
||||
String lang = (player["lang"] as String).toUpperCase();
|
||||
String playerUrl = player["player"];
|
||||
List<MVideo> a = [];
|
||||
if (playerUrl.contains("sendvid")) {
|
||||
a = await sendVidExtractorr(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("sibnet.ru")) {
|
||||
a = await sibnetExtractor(playerUrl, lang);
|
||||
} else if (playerUrl.contains("voe.sx")) {
|
||||
a = await voeExtractor(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("vidcdn")) {
|
||||
a = await vidcdnExtractor(playerUrl, lang);
|
||||
} else if (playerUrl.contains("filemoon")) {
|
||||
a = await filemoonExtractor(playerUrl, "$lang Filemoon - ", "");
|
||||
} else if (playerUrl.contains("vidhide")) {
|
||||
a = await streamHideExtractor(playerUrl, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamHideExtractor(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(
|
||||
substringAfter(unpackJs(res), "sources:"), "file:\""),
|
||||
"src:\""),
|
||||
'"');
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
List<MVideo> videos = [];
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$prefix StreamHideVid - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
GroupFilter("TypeFilter", "Type", [
|
||||
CheckBoxFilter("Film", "1"),
|
||||
CheckBoxFilter("Anime", "2"),
|
||||
CheckBoxFilter("OVA", "3"),
|
||||
CheckBoxFilter("ONA", "4"),
|
||||
CheckBoxFilter("Special", "5"),
|
||||
CheckBoxFilter("Music", "6"),
|
||||
]),
|
||||
GroupFilter("LanguageFilter", "Langue", [
|
||||
CheckBoxFilter("VF", "3"),
|
||||
CheckBoxFilter("VOSTFR", "4"),
|
||||
CheckBoxFilter("Multicc", "2"),
|
||||
CheckBoxFilter("EN", "1"),
|
||||
]),
|
||||
GroupFilter("SaisonFilter", "Saison", [
|
||||
CheckBoxFilter("Printemps", "1"),
|
||||
CheckBoxFilter("Été", "2"),
|
||||
CheckBoxFilter("Automne", "3"),
|
||||
CheckBoxFilter("Hiver", "4"),
|
||||
]),
|
||||
GroupFilter("StatusFilter", "Statut", [
|
||||
CheckBoxFilter("Terminés", "1"),
|
||||
CheckBoxFilter("En cours", "2"),
|
||||
CheckBoxFilter("Pas encore diffusés", "3"),
|
||||
]),
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "1"),
|
||||
CheckBoxFilter("Aventure", "2"),
|
||||
CheckBoxFilter("Voitures", "3"),
|
||||
CheckBoxFilter("Comédie", "4"),
|
||||
CheckBoxFilter("Démence", "5"),
|
||||
CheckBoxFilter("Démons", "6"),
|
||||
CheckBoxFilter("Drame", "8"),
|
||||
CheckBoxFilter("Ecchi", "9"),
|
||||
CheckBoxFilter("Fantastique", "10"),
|
||||
CheckBoxFilter("Jeu", "11"),
|
||||
CheckBoxFilter("Harem", "35"),
|
||||
CheckBoxFilter("Historique", "13"),
|
||||
CheckBoxFilter("Horreur", "14"),
|
||||
CheckBoxFilter("Isekai", "44"),
|
||||
CheckBoxFilter("Josei", "43"),
|
||||
CheckBoxFilter("Enfants", "25"),
|
||||
CheckBoxFilter("Magie", "16"),
|
||||
CheckBoxFilter("Arts martiaux", "17"),
|
||||
CheckBoxFilter("Mecha", "18"),
|
||||
CheckBoxFilter("Militaire", "38"),
|
||||
CheckBoxFilter("Musique", "19"),
|
||||
CheckBoxFilter("Mystère", "7"),
|
||||
CheckBoxFilter("Parodie", "20"),
|
||||
CheckBoxFilter("Police", "39"),
|
||||
CheckBoxFilter("Psychologique", "40"),
|
||||
CheckBoxFilter("Romance", "22"),
|
||||
CheckBoxFilter("Samouraï", "21"),
|
||||
CheckBoxFilter("École", "23"),
|
||||
CheckBoxFilter("Science-Fiction", "24"),
|
||||
CheckBoxFilter("Seinen", "42"),
|
||||
CheckBoxFilter("Shoujo Ai", "26"),
|
||||
CheckBoxFilter("Shoujo", "25"),
|
||||
CheckBoxFilter("Shounen Ai", "28"),
|
||||
CheckBoxFilter("Tranche de vie", "36"),
|
||||
CheckBoxFilter("Shounen", "27"),
|
||||
CheckBoxFilter("Espace", "29"),
|
||||
CheckBoxFilter("Sports", "30"),
|
||||
CheckBoxFilter("Super Pouvoir", "31"),
|
||||
CheckBoxFilter("Surnaturel", "37"),
|
||||
CheckBoxFilter("Vampire", "32"),
|
||||
CheckBoxFilter("Yaoi", "33"),
|
||||
CheckBoxFilter("Yuri", "34"),
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualité préférée",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"]),
|
||||
ListPreference(
|
||||
key: "voices_preference",
|
||||
title: "Préférence des voix",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Préférer VOSTFR", "Préférer VF"],
|
||||
entryValues: ["vostfr", "vf"]),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
String voice = getPreferenceValue(sourceId, "voices_preference");
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(voice)) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(voice)) {
|
||||
qualityMatchB = 1;
|
||||
}
|
||||
if (qualityMatchA != qualityMatchB) {
|
||||
return qualityMatchB - qualityMatchA;
|
||||
}
|
||||
|
||||
final regex = RegExp(r'(\d+)p');
|
||||
final matchA = regex.firstMatch(a.quality);
|
||||
final matchB = regex.firstMatch(b.quality);
|
||||
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
|
||||
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
|
||||
return qualityNumB - qualityNumA;
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
print(masterUrl);
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidcdnExtractor(String url, String prefix) async {
|
||||
final res = await client.get(Uri.parse(url));
|
||||
if (res.statusCode != 200) {
|
||||
print("Erreur lors de la récupération de la page : ${res.statusCode}");
|
||||
return [];
|
||||
}
|
||||
final jsonResponse = jsonDecode(res.body);
|
||||
|
||||
String masterUrl = jsonResponse['sources'][0]['file'] ?? '';
|
||||
final quality = jsonResponse['quality'] ?? '';
|
||||
|
||||
List<MVideo> videos = [];
|
||||
|
||||
final masterPlaylistRes = await client.get(Uri.parse(masterUrl));
|
||||
if (masterPlaylistRes.statusCode != 200) {
|
||||
print(
|
||||
"Error lors de la récupération de la playlist M3U8 : ${masterPlaylistRes.statusCode}");
|
||||
return [];
|
||||
}
|
||||
|
||||
final masterPlaylistBody = masterPlaylistRes.body;
|
||||
|
||||
final playlistLines = masterPlaylistBody.split("\n");
|
||||
|
||||
for (int i = 0; i < playlistLines.length; i++) {
|
||||
final line = playlistLines[i];
|
||||
if (line.startsWith("#EXT-X-STREAM-INF")) {
|
||||
final resolutionLine = line.split("RESOLUTION=").last;
|
||||
final resolution = resolutionLine.split(",").first;
|
||||
final width = int.parse(resolution.split("x").first);
|
||||
final height = int.parse(resolution.split("x").last);
|
||||
|
||||
String videoQuality;
|
||||
if (height >= 1080) {
|
||||
videoQuality = "1080p";
|
||||
} else if (height >= 720) {
|
||||
videoQuality = "720p";
|
||||
} else if (height >= 480) {
|
||||
videoQuality = "480p";
|
||||
} else if (height >= 360) {
|
||||
videoQuality = "360p";
|
||||
} else {
|
||||
videoQuality = "${height}p";
|
||||
}
|
||||
|
||||
String videoUrl = playlistLines[i + 1].trim();
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.substring(0, masterUrl.lastIndexOf('/'))}/$videoUrl";
|
||||
}
|
||||
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "$prefix VidCDN:$videoQuality";
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AniZone main(MSource source) {
|
||||
return AniZone(source: source);
|
||||
}
|
||||
BIN
dart/anime/src/fr/anizone/icon.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
16
dart/anime/src/fr/anizone/source.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get aniZoneSource => _aniZoneSource;
|
||||
const _aniZoneVersion = "0.0.2";
|
||||
const _aniZoneSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/anizone/anizone.dart";
|
||||
Source _aniZoneSource = Source(
|
||||
name: "AniZone",
|
||||
baseUrl: "https://v1.animesz.xyz",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/anizone/icon.png",
|
||||
sourceCodeUrl: _aniZoneSourceCodeUrl,
|
||||
version: _aniZoneVersion,
|
||||
itemType: ItemType.anime);
|
||||
|
|
@ -146,14 +146,14 @@ class FrAnime extends MProvider {
|
|||
.body;
|
||||
|
||||
List<MVideo> a = [];
|
||||
print(playerName);
|
||||
if (playerName.contains("vido")) {
|
||||
videos.add(video
|
||||
..url = playerUrl
|
||||
..originalUrl = playerUrl
|
||||
..quality = "FRAnime (Vido)");
|
||||
} else if (playerName.contains("sendvid")) {
|
||||
a = await sendVidExtractor(
|
||||
playerUrl, json.encode({"Referer": "https://franime.fr/"}), "");
|
||||
a = await sendVidExtractorr(playerUrl, "");
|
||||
} else if (playerName.contains("sibnet")) {
|
||||
a = await sibnetExtractor(playerUrl);
|
||||
}
|
||||
|
|
@ -335,6 +335,59 @@ class FrAnime extends MProvider {
|
|||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
FrAnime main(MSource source) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get franimeSource => _franimeSource;
|
||||
const _franimeVersion = "0.0.7";
|
||||
const _franimeVersion = "0.0.75";
|
||||
const _franimeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/franime/franime.dart";
|
||||
Source _franimeSource = Source(
|
||||
|
|
@ -14,5 +14,5 @@ Source _franimeSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/franime/icon.png",
|
||||
sourceCodeUrl: _franimeSourceCodeUrl,
|
||||
version: _franimeVersion,
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: true);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ class OtakuFr extends MProvider {
|
|||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/en-cours/page/$page")))
|
||||
.body;
|
||||
(await client.get(Uri.parse("$baseUrl/en-cours/page/$page"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls =
|
||||
xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href');
|
||||
|
|
@ -34,8 +36,7 @@ class OtakuFr extends MProvider {
|
|||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/page/$page/"))).body;
|
||||
final res = (await client.get(Uri.parse("$baseUrl/page/$page/"))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//*[@class="episode"]/div/a/@href');
|
||||
|
|
@ -77,19 +78,17 @@ class OtakuFr extends MProvider {
|
|||
final filters = filterList.filters;
|
||||
String url = "";
|
||||
if (query.isNotEmpty) {
|
||||
url = "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query";
|
||||
url = "$baseUrl/toute-la-liste-affiches/page/$page/?q=$query";
|
||||
} else {
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "GenreFilter") {
|
||||
if (filter.state != 0) {
|
||||
url =
|
||||
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
|
||||
url = "$baseUrl/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
} else if (filter.type == "SubPageFilter") {
|
||||
if (url.isEmpty) {
|
||||
if (filter.state != 0) {
|
||||
url =
|
||||
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
|
||||
url = "$baseUrl/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +121,9 @@ class OtakuFr extends MProvider {
|
|||
final statusList = [
|
||||
{"En cours": 0, "Terminé": 1}
|
||||
];
|
||||
String res = (await client.get(Uri.parse(url))).body;
|
||||
String res =
|
||||
(await client.get(Uri.parse("$baseUrl${getUrlWithoutDomain(url)}")))
|
||||
.body;
|
||||
MManga anime = MManga();
|
||||
final originalUrl = xpath(res,
|
||||
'//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href');
|
||||
|
|
@ -173,13 +174,16 @@ class OtakuFr extends MProvider {
|
|||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl${getUrlWithoutDomain(url)}")))
|
||||
.body;
|
||||
|
||||
final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src');
|
||||
final servers = parseHtml(res).select("div.tab-content iframe[src]");
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
for (var url in servers) {
|
||||
final resServer = (await client.get(Uri.parse(fixUrl(url)),
|
||||
final urll = url.getSrc != "about:blank" ? url.getSrc : url.getDataSrc;
|
||||
final resServer = (await client.get(Uri.parse(fixUrl(urll)),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"}))
|
||||
.body;
|
||||
final serverUrl =
|
||||
|
|
@ -188,27 +192,24 @@ class OtakuFr extends MProvider {
|
|||
if (serverUrl.contains("https://streamwish") &&
|
||||
hosterSelection.contains("Streamwish")) {
|
||||
a = await streamWishExtractor(serverUrl, "StreamWish");
|
||||
} else if (serverUrl.contains("sibnet") &&
|
||||
hosterSelection.contains("Sibnet")) {
|
||||
a = await sibnetExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://doo") &&
|
||||
hosterSelection.contains("Doodstream")) {
|
||||
a = await doodExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://voe.sx") &&
|
||||
hosterSelection.contains("Voe")) {
|
||||
a = await voeExtractor(serverUrl, null);
|
||||
} else if (containsVidBom(serverUrl) &&
|
||||
hosterSelection.contains("VidBom")) {
|
||||
a = await vidBomExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://ok.ru") &&
|
||||
hosterSelection.contains("Okru")) {
|
||||
a = await okruExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("vadbam") &&
|
||||
hosterSelection.contains("Vidbm")) {
|
||||
a = await vidbmExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("upstream") &&
|
||||
hosterSelection.contains("Upstream")) {
|
||||
a = await upstreamExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("sendvid") &&
|
||||
hosterSelection.contains("Sendvid")) {
|
||||
a = await sendVidExtractor(serverUrl, null, "");
|
||||
a = await sendVidExtractorr(serverUrl, "");
|
||||
} else if (serverUrl.contains("voe.sx") &&
|
||||
hosterSelection.contains("Voe")) {
|
||||
a = await voeExtractor(serverUrl, "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
|
@ -220,6 +221,17 @@ class OtakuFr extends MProvider {
|
|||
return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0);
|
||||
}
|
||||
|
||||
bool containsVidBom(String url) {
|
||||
url = url;
|
||||
final list = ["vidbam", "vadbam", "vidbom", "vidbm"];
|
||||
for (var n in list) {
|
||||
if (url.contains(n)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
|
|
@ -279,6 +291,14 @@ class OtakuFr extends MProvider {
|
|||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://otakufr.cc",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://otakufr.cc"),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualité préférée",
|
||||
|
|
@ -287,14 +307,14 @@ class OtakuFr extends MProvider {
|
|||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"]),
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selection",
|
||||
key: "hoster_selection_",
|
||||
title: "Enable/Disable Hosts",
|
||||
summary: "",
|
||||
entries: [
|
||||
"Streamwish",
|
||||
"Doodstream",
|
||||
"Sendvid",
|
||||
"Vidbm",
|
||||
"VidBom",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
|
|
@ -304,7 +324,7 @@ class OtakuFr extends MProvider {
|
|||
"Streamwish",
|
||||
"Doodstream",
|
||||
"Sendvid",
|
||||
"Vidbm",
|
||||
"VidBom",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
|
|
@ -351,7 +371,60 @@ class OtakuFr extends MProvider {
|
|||
}
|
||||
|
||||
List<String> preferenceHosterSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "hoster_selection");
|
||||
return getPreferenceValue(sourceId, "hoster_selection_");
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> upstreamExtractor(String url) async {
|
||||
|
|
@ -385,56 +458,6 @@ class OtakuFr extends MProvider {
|
|||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidbmExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res,
|
||||
'//script[contains(text(), "m3u8") or contains(text(), "mp4")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
final masterUrl = substringBefore(substringAfter(js.first, "source"), "\"");
|
||||
final quality = substringBefore(
|
||||
substringAfter(
|
||||
substringBefore(
|
||||
substringAfter(substringAfter(js.first, "source"), "file"),
|
||||
"]"),
|
||||
"label:\""),
|
||||
"\"");
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains("m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")) {
|
||||
final quality =
|
||||
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
|
||||
|
||||
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Vidbm - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
} else {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "Vidbm - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
OtakuFr main(MSource source) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get otakufr => _otakufr;
|
||||
const otakufrVersion = "0.0.8";
|
||||
const otakufrVersion = "0.0.95";
|
||||
const otakufrCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/otakufr/otakufr.dart";
|
||||
Source _otakufr = Source(
|
||||
name: "OtakuFr",
|
||||
baseUrl: "https://otakufr.co",
|
||||
baseUrl: "https://otakufr.cc",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/otakufr/icon.png",
|
||||
sourceCodeUrl: otakufrCodeUrl,
|
||||
version: otakufrVersion,
|
||||
isManga: false,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: false);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get yomoviesSource => _yomoviesSource;
|
||||
const _yomoviesVersion = "0.0.2";
|
||||
const _yomoviesVersion = "0.0.25";
|
||||
const _yomoviesSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/hi/yomovies/yomovies.dart";
|
||||
Source _yomoviesSource = Source(
|
||||
|
|
@ -13,4 +13,4 @@ Source _yomoviesSource = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/hi/yomovies/icon.png",
|
||||
sourceCodeUrl: _yomoviesSourceCodeUrl,
|
||||
version: _yomoviesVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ class YoMovies extends MProvider {
|
|||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Bollywood", "/genre/bollywood"),
|
||||
SelectFilterOption("Trending", "/genre/top-rated"),
|
||||
SelectFilterOption("Bollywood (2024)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2024&wpas=1"),
|
||||
SelectFilterOption("Bollywood (2023)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2023&wpas=1"),
|
||||
SelectFilterOption("Bollywood (2022)",
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Source _nimegami = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/nimegami/icon.png",
|
||||
sourceCodeUrl: _nimegamiCodeUrl,
|
||||
version: _nimegamiVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Source _oploverz = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/oploverz/icon.png",
|
||||
sourceCodeUrl: _oploverzCodeUrl,
|
||||
version: _oploverzVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Source _otakudesu = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/otakudesu/icon.png",
|
||||
sourceCodeUrl: _otakudesuCodeUrl,
|
||||
version: _otakudesuVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class AnimeSaturn extends MProvider {
|
|||
|
||||
final urls = xpath(res, '//*[@class="card mb-4 shadow-sm"]/a/@href');
|
||||
|
||||
final names = xpath(res, '///*[@class="card mb-4 shadow-sm"]/a/@title');
|
||||
final names = xpath(res, '//*[@class="card mb-4 shadow-sm"]/a/@title');
|
||||
|
||||
final images = xpath(res,
|
||||
'//*[@class="card mb-4 shadow-sm"]/a/img[@class="new-anime"]/@src');
|
||||
|
|
@ -149,15 +149,18 @@ class AnimeSaturn extends MProvider {
|
|||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final details = xpath(res,
|
||||
'//div[@class="container shadow rounded bg-dark-as-box mb-3 p-3 w-100 text-white"]/text()')
|
||||
.first;
|
||||
final detailsList = xpath(res,
|
||||
'//div[@class="container shadow rounded bg-dark-as-box mb-3 p-3 w-100 text-white"]/text()');
|
||||
if (detailsList.isNotEmpty) {
|
||||
final details = detailsList.first;
|
||||
|
||||
anime.status = parseStatus(
|
||||
details.substring(
|
||||
details.indexOf("Stato:") + 6, details.indexOf("Data di uscita:")),
|
||||
statusList);
|
||||
anime.author = details.substring(7, details.indexOf("Stato:"));
|
||||
}
|
||||
|
||||
anime.status = parseStatus(
|
||||
details.substring(
|
||||
details.indexOf("Stato:") + 6, details.indexOf("Data di uscita:")),
|
||||
statusList);
|
||||
anime.author = details.substring(7, details.indexOf("Stato:"));
|
||||
final description = xpath(res, '//*[@id="shown-trama"]/text()');
|
||||
final descriptionFull = xpath(res, '//*[@id="full-trama"]/text()');
|
||||
if (description.isNotEmpty) {
|
||||
|
|
@ -356,4 +359,4 @@ class AnimeSaturn extends MProvider {
|
|||
|
||||
AnimeSaturn main(MSource source) {
|
||||
return AnimeSaturn(source: source);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesaturn => _animesaturn;
|
||||
const _animesaturnVersion = "0.0.35";
|
||||
const _animesaturnVersion = "0.0.5";
|
||||
const _animesaturnCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/it/animesaturn/animesaturn.dart";
|
||||
Source _animesaturn = Source(
|
||||
name: "AnimeSaturn",
|
||||
baseUrl: "https://www.animesaturn.tv",
|
||||
baseUrl: "https://www.animesaturn.cx",
|
||||
lang: "it",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/it/animesaturn/icon.png",
|
||||
sourceCodeUrl: _animesaturnCodeUrl,
|
||||
version: _animesaturnVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ class AnimesVision extends MProvider {
|
|||
@override
|
||||
String get baseUrl => source.baseUrl;
|
||||
|
||||
Map<String, dynamic> get headers => {
|
||||
@override
|
||||
Map<String, String> get headers => {
|
||||
"Referer": baseUrl,
|
||||
"Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ Source _animesvision = Source(
|
|||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/pt/animesvision/icon.png",
|
||||
sourceCodeUrl: _animesvisionCodeUrl,
|
||||
version: _animesvisionVersion,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ class Filma24 extends MProvider {
|
|||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
final Client client =
|
||||
Client(source, json.encode({"useDartHttpClient": true}));
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain");
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain_new");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
|
|
@ -121,13 +122,13 @@ class Filma24 extends MProvider {
|
|||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "pref_domain",
|
||||
key: "pref_domain_new",
|
||||
title: "Domeni i përdorur aktualisht",
|
||||
summary: "",
|
||||
value: "https://www.filma24.pl",
|
||||
value: "https://www.filma24.band",
|
||||
dialogTitle: "Domeni i përdorur aktualisht",
|
||||
dialogMessage: "",
|
||||
text: "https://www.filma24.pl"),
|
||||
text: "https://www.filma24.band"),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +137,7 @@ class Filma24 extends MProvider {
|
|||
return [
|
||||
SelectFilter("ReleaseFilter", "Viti", 0, [
|
||||
SelectFilterOption("<Select>", ""),
|
||||
SelectFilterOption("2024", "2024"),
|
||||
SelectFilterOption("2023", "2023"),
|
||||
SelectFilterOption("2022", "2022"),
|
||||
SelectFilterOption("2021", "2021"),
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get filma24 => _filma24;
|
||||
const _filma24Version = "0.0.25";
|
||||
const _filma24Version = "0.0.45";
|
||||
const _filma24CodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/sq/filma24/filma24.dart";
|
||||
Source _filma24 = Source(
|
||||
name: "Filma24",
|
||||
baseUrl: "https://www.filma24.pl",
|
||||
baseUrl: "https://www.filma24.band",
|
||||
lang: "sq",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/sq/filma24/icon.png",
|
||||
sourceCodeUrl: _filma24CodeUrl,
|
||||
version: _filma24Version,
|
||||
isManga: false);
|
||||
itemType: ItemType.anime);
|
||||
|
|
|
|||
213
dart/anime/src/tr/diziwatch/diziwatch.dart
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class DiziWatch extends MProvider {
|
||||
DiziWatch({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
bool get supportsLatest => true;
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {};
|
||||
|
||||
Future<MPages> parseMainList(int index) async {
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse(source.baseUrl))) .body);
|
||||
List<MManga> list = [];
|
||||
MElement containingElement = dom.select("#list-series-hizala2")[index];
|
||||
List<MElement> results = containingElement.select("#list-series-main");
|
||||
for (MElement result in results) {
|
||||
MElement a = result.selectFirst("a");
|
||||
MElement img = a.selectFirst("img");
|
||||
MManga anime = new MManga();
|
||||
anime.name = img.attr("alt");
|
||||
anime.link = a.getHref;
|
||||
anime.imageUrl = img.getSrc;
|
||||
list.add(anime);
|
||||
}
|
||||
|
||||
return MPages(list, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return parseMainList(1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return parseMainList(2);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String orderby = "";
|
||||
String year = "";
|
||||
String imdb = "";
|
||||
String genre = "";
|
||||
for (var filter in filterList.filters) {
|
||||
if (filter.type == "Sort") {
|
||||
orderby = filter.values[filter.state].value;
|
||||
} else if (filter.type == "Year") {
|
||||
year = filter.state;
|
||||
} else if (filter.type == "MinIMDBRating") {
|
||||
imdb = filter.values[filter.state].value;
|
||||
} else if (filter.type == "Genre") {
|
||||
genre = filter.values[filter.state].value;
|
||||
}
|
||||
}
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse("${source.baseUrl}/anime-arsivi/page/${page}/?orderby=${orderby}&yil=${year}&imdb=${imdb}&isim=${query}&tur=${genre}"))) .body);
|
||||
List<MElement> results = dom.select("#list-series");
|
||||
List<MManga> list = [];
|
||||
for (MElement result in results) {
|
||||
MElement a = result.select("a")[1];
|
||||
MElement img = a.selectFirst("img");
|
||||
MManga anime = new MManga();
|
||||
anime.name = result.selectFirst("div.cat-title a").text;
|
||||
anime.link = a.getHref;
|
||||
anime.imageUrl = img.getSrc;
|
||||
list.add(anime);
|
||||
}
|
||||
|
||||
MElement paginateLinksDiv = dom.selectFirst("div.paginate-links");
|
||||
int lastPage = int.parse(paginateLinksDiv.selectFirst("a.next").previousElementSibling.text ?? "1");
|
||||
|
||||
return MPages(list, lastPage > page);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse(url))) .body);
|
||||
|
||||
var anime = new MManga();
|
||||
|
||||
anime.name = dom.selectFirst("h1.title-border").text;
|
||||
anime.link = url;
|
||||
anime.imageUrl = dom.selectFirst("div.category_image img").getSrc;
|
||||
anime.description = dom.selectFirst("div#series-info").text;
|
||||
|
||||
List<String> genres = dom.selectFirst("span.dizi-tur").text.split(", ");
|
||||
genres.remove("Anime"); // not needed
|
||||
anime.genre = genres;
|
||||
|
||||
List<MElement> results = dom.select("div.bolumust");
|
||||
List<MChapter> chapters = [];
|
||||
for (MElement result in results) {
|
||||
MElement a = result.select("a")[1];
|
||||
MChapter chapter = new MChapter();
|
||||
chapter.name = result.selectFirst(".baslik").text + " | " + result.selectFirst("#bolum-ismi").text;
|
||||
chapter.url = a.getHref;
|
||||
chapters.add(chapter);
|
||||
}
|
||||
anime.chapters = chapters.reversed.toList();
|
||||
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse(url))) .body);
|
||||
String id = dom.selectFirst("#takip_et_izledim_Calis").attr("data-ilanid");
|
||||
var json = json.decode((await client.get(Uri.parse("${source.baseUrl}/wp-admin/admin-ajax.php?action=playlist&pid=${id}"))).body)[0];
|
||||
var sources = json["sources"];
|
||||
List<MVideo> videos = [];
|
||||
for (var source in sources) {
|
||||
MVideo video = new MVideo();
|
||||
video.url = source["file"];
|
||||
video.originalUrl = source["file"];
|
||||
video.quality = source["label"];
|
||||
video.headers = {"Referer": url};
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
String quality = getPreferenceValue(source.id, "preferred_quality");
|
||||
videos.sort((MVideo a, MVideo b) => (b.quality.contains(quality) ? 1 : 0) - (a.quality.contains(quality) ? 1 : 0));
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
HeaderFilter("Filtrele"),
|
||||
SelectFilter("Sort", "Sırala", 0, [
|
||||
SelectFilterOption("IMDb Puanına Göre", "meta_value"),
|
||||
SelectFilterOption("Alfabetik", "name"),
|
||||
SelectFilterOption("Eklenme Tarihine Göre", "ID")
|
||||
]),
|
||||
TextFilter("Year", "Yapım Yılı"),
|
||||
SelectFilter("Genre", "Tür", 0, [
|
||||
SelectFilterOption("Kategori Seçin", ""),
|
||||
SelectFilterOption("Aksiyon", "aksiyon"),
|
||||
SelectFilterOption("Arabalar", "araba"),
|
||||
SelectFilterOption("Askeri", "askeri"),
|
||||
SelectFilterOption("Bilim Kurgu", "bilim"),
|
||||
SelectFilterOption("Büyü", "buyu"),
|
||||
SelectFilterOption("Doğaüstü Güçler", "doga"),
|
||||
SelectFilterOption("Dövüş Sanatları", "dovus"),
|
||||
SelectFilterOption("Dram", "dram"),
|
||||
SelectFilterOption("Ecchi", "ecchi"),
|
||||
SelectFilterOption("Fantastik", "fantastik"),
|
||||
SelectFilterOption("Gerilim", "gerilim"),
|
||||
SelectFilterOption("Gizem", "gizem"),
|
||||
SelectFilterOption("Harem", "harem"),
|
||||
SelectFilterOption("Isekai", "isekai"),
|
||||
SelectFilterOption("Komedi", "komedi"),
|
||||
SelectFilterOption("Korku", "korku"),
|
||||
SelectFilterOption("Macera", "macera"),
|
||||
SelectFilterOption("Mecha", "mecha"),
|
||||
SelectFilterOption("Müzik", "muzik"),
|
||||
SelectFilterOption("Okul", "okul"),
|
||||
SelectFilterOption("Oyun", "oyun"),
|
||||
SelectFilterOption("Parodi", "parodi"),
|
||||
SelectFilterOption("Polisiye", "polisiye"),
|
||||
SelectFilterOption("Psikolojik", "psikolojik"),
|
||||
SelectFilterOption("Romantizm", "romantizm"),
|
||||
SelectFilterOption("Samuray", "samuray"),
|
||||
SelectFilterOption("Seinen", "seinen"),
|
||||
SelectFilterOption("Shoujo", "shoujo"),
|
||||
SelectFilterOption("Shounen", "shounen"),
|
||||
SelectFilterOption("Spor", "spor"),
|
||||
SelectFilterOption("Suç", "suc"),
|
||||
SelectFilterOption("Süper Güçler", "super"),
|
||||
SelectFilterOption("Şeytanlar", "seytan"),
|
||||
SelectFilterOption("Şizofreni", "sizofreni"),
|
||||
SelectFilterOption("Tarihi", "tarihi"),
|
||||
SelectFilterOption("Uzay", "uzay"),
|
||||
SelectFilterOption("Vampir", "vampir"),
|
||||
SelectFilterOption("Yaşamdan Kesitler", "yasam")
|
||||
]),
|
||||
SelectFilter("MinIMDBRating", "Min. IMBD Puanı", 0, [
|
||||
SelectFilterOption("1", "0"), // value 1 looks like buggy so use 0 it wont make any difference.
|
||||
SelectFilterOption("2", "2"),
|
||||
SelectFilterOption("3", "3"),
|
||||
SelectFilterOption("4", "4"),
|
||||
SelectFilterOption("5", "5"),
|
||||
SelectFilterOption("6", "6"),
|
||||
SelectFilterOption("7", "7"),
|
||||
SelectFilterOption("8", "8"),
|
||||
SelectFilterOption("9", "9")
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Tercih edilen kalite",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "480p"], // I only saw 1080p and 480p in diziWatch.
|
||||
entryValues: ["1080", "480"])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
DiziWatch main(MSource source) {
|
||||
return DiziWatch(source:source);
|
||||
}
|
||||
BIN
dart/anime/src/tr/diziwatch/icon.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
16
dart/anime/src/tr/diziwatch/source.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get diziwatchSource => _diziwatchSource;
|
||||
const _diziwatchVersion = "0.0.1";
|
||||
const _diziwatchSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/tr/diziwatch/diziwatch.dart";
|
||||
Source _diziwatchSource = Source(
|
||||
name: "diziWatch",
|
||||
baseUrl: "https://diziwatch.net",
|
||||
lang: "tr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/tr/diziwatch/icon.png",
|
||||
sourceCodeUrl: _diziwatchSourceCodeUrl,
|
||||
version: _diziwatchVersion,
|
||||
itemType: ItemType.anime);
|
||||
|
|
@ -1,26 +1,16 @@
|
|||
import '../../model/source.dart';
|
||||
import 'multisrc/heancms/sources.dart';
|
||||
import 'multisrc/madara/sources.dart';
|
||||
import 'multisrc/mangabox/sources.dart';
|
||||
import 'multisrc/mangareader/sources.dart';
|
||||
import 'multisrc/mmrcms/sources.dart';
|
||||
import 'multisrc/nepnep/sources.dart';
|
||||
import 'src/all/batoto/sources.dart';
|
||||
import 'src/all/comick/sources.dart';
|
||||
import 'src/all/mangadex/sources.dart';
|
||||
import 'src/all/nhentai/sources.dart';
|
||||
import 'src/en/mangahere/source.dart';
|
||||
|
||||
List<Source> dartMangasourceList = [
|
||||
...madaraSourcesList,
|
||||
...comickSourcesList,
|
||||
...mangaDexSourcesList,
|
||||
...mangareaderSourcesList,
|
||||
...mmrcmsSourcesList,
|
||||
...heancmsSourcesList,
|
||||
mangahereSource,
|
||||
...nepnepSourcesList,
|
||||
...mangaboxSourcesList,
|
||||
...batotoSourcesList,
|
||||
...nhentaiSourcesList
|
||||
...mangaboxSourcesList
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,246 +0,0 @@
|
|||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class HeanCms extends MProvider {
|
||||
HeanCms({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
String res = "";
|
||||
if (!useNewQueryEndpoint(source.name)) {
|
||||
final url = "${source.apiUrl}/series/querysearch";
|
||||
|
||||
final body = {
|
||||
"page": page,
|
||||
"order": "desc",
|
||||
"order_by": "total_views",
|
||||
"series_status": "Ongoing",
|
||||
"series_type": "Comic"
|
||||
};
|
||||
res = (await client.post(Uri.parse(url), headers: headers, body: body))
|
||||
.body;
|
||||
} else {
|
||||
final newEndpointUrl =
|
||||
"${source.apiUrl}/query/?page=$page&query_string=&series_status=All&order=desc&orderBy=total_views&perPage=12&tags_ids=[]&series_type=Comic";
|
||||
res =
|
||||
(await client.get(Uri.parse(newEndpointUrl), headers: headers)).body;
|
||||
}
|
||||
return mMangaRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
String res = "";
|
||||
if (!useNewQueryEndpoint(source.name)) {
|
||||
final url = "${source.apiUrl}/series/querysearch";
|
||||
final body = {
|
||||
"page": page,
|
||||
"order": "desc",
|
||||
"order_by": "latest",
|
||||
"series_status": "Ongoing",
|
||||
"series_type": "Comic"
|
||||
};
|
||||
res = (await client.post(Uri.parse(url), headers: headers, body: body))
|
||||
.body;
|
||||
} else {
|
||||
final newEndpointUrl =
|
||||
"${source.apiUrl}/query/?page=$page&query_string=&series_status=All&order=desc&orderBy=latest&perPage=12&tags_ids=[]&series_type=Comic";
|
||||
res =
|
||||
(await client.get(Uri.parse(newEndpointUrl), headers: headers)).body;
|
||||
}
|
||||
return mMangaRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
String res = "";
|
||||
if (!useNewQueryEndpoint(source.source)) {
|
||||
final url = "${source.apiUrl}/series/search";
|
||||
final body = {"term": query};
|
||||
res = (await client.post(Uri.parse(url), headers: headers, body: body))
|
||||
.body;
|
||||
} else {
|
||||
final newEndpointUrl =
|
||||
"${source.apiUrl}/query/?page=$page&query_string=$query&series_status=All&order=desc&orderBy=total_views&perPage=12&tags_ids=[]&series_type=Comic";
|
||||
res =
|
||||
(await client.get(Uri.parse(newEndpointUrl), headers: headers)).body;
|
||||
}
|
||||
return mMangaRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga manga = MManga();
|
||||
String currentSlug = substringAfterLast(url, "/");
|
||||
final headers = getHeader(source.baseUrl);
|
||||
final res = (await client.get(
|
||||
Uri.parse("${source.apiUrl}/series/$currentSlug"),
|
||||
headers: headers))
|
||||
.body;
|
||||
manga.author = getMapValue(res, "author");
|
||||
manga.description = getMapValue(res, "description");
|
||||
manga.genre = jsonPathToString(res, r"$.tags[*].name", "._").split("._");
|
||||
List<String> chapterTitles = [];
|
||||
List<String> chapterUrls = [];
|
||||
List<String> chapterDates = [];
|
||||
|
||||
if (!useNewQueryEndpoint(source.name)) {
|
||||
for (var chapter in json.decode(res)["chapters"]) {
|
||||
final chapterName = chapter["chapter_name"];
|
||||
final chapterSlug = chapter["chapter_slug"];
|
||||
final chapterId = chapter["id"];
|
||||
final createdAt = chapter["created_at"];
|
||||
chapterUrls.add("/series/$currentSlug/$chapterSlug#$chapterId");
|
||||
chapterTitles.add(chapterName);
|
||||
chapterDates.add(createdAt);
|
||||
}
|
||||
} else {
|
||||
final seasons = json.decode(res)["seasons"].first;
|
||||
for (var chapter in seasons["chapters"]) {
|
||||
final chapterName = chapter["chapter_name"];
|
||||
final chapterSlug = chapter["chapter_slug"];
|
||||
final chapterId = chapter["id"];
|
||||
final createdAt = chapter["created_at"];
|
||||
chapterUrls.add("/series/$currentSlug/$chapterSlug#$chapterId");
|
||||
chapterTitles.add(chapterName);
|
||||
chapterDates.add(createdAt);
|
||||
}
|
||||
}
|
||||
final dateUploads =
|
||||
parseDates(chapterDates, source.dateFormat, source.dateFormatLocale);
|
||||
List<MChapter>? chaptersList = [];
|
||||
for (var i = 0; i < chapterTitles.length; i++) {
|
||||
MChapter chapter = MChapter();
|
||||
chapter.name = chapterTitles[i];
|
||||
chapter.url = chapterUrls[i];
|
||||
chapter.dateUpload = dateUploads[i];
|
||||
chaptersList.add(chapter);
|
||||
}
|
||||
if (!useNewQueryEndpoint(source.name)) {
|
||||
chaptersList = chaptersList.reversed.toList();
|
||||
}
|
||||
|
||||
manga.chapters = chaptersList;
|
||||
return manga;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getPageList(String url) async {
|
||||
final headers = getHeader(source.baseUrl);
|
||||
|
||||
String res = "".toString();
|
||||
if (!useslugStrategy(source.name)) {
|
||||
String chapterId = substringAfter(url, '#');
|
||||
res = (await client.get(
|
||||
Uri.parse("${source.apiUrl}/series/chapter/$chapterId"),
|
||||
headers: headers))
|
||||
.body;
|
||||
} else {
|
||||
res = (await client.get(Uri.parse("${source.baseUrl}$url"),
|
||||
headers: headers))
|
||||
.body;
|
||||
|
||||
List<String> pageUrls = [];
|
||||
var imagesRes = parseHtml(res)
|
||||
.selectFirst("div.min-h-screen > div.container > p.items-center")
|
||||
.innerHtml;
|
||||
|
||||
pageUrls = xpath(imagesRes, '//img/@src');
|
||||
|
||||
pageUrls.addAll(xpath(imagesRes, '//img/@data-src'));
|
||||
|
||||
return pageUrls.where((e) => e.isNotEmpty).toList();
|
||||
}
|
||||
|
||||
final pages = jsonPathToList(res, r"$.content.images[*]", 0);
|
||||
List<String> pageUrls = [];
|
||||
for (var u in pages) {
|
||||
final url = u.replaceAll('"', "");
|
||||
if (url.startsWith("http")) {
|
||||
pageUrls.add(url);
|
||||
} else {
|
||||
pageUrls.add("${source.apiUrl}/$url");
|
||||
}
|
||||
}
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
MPages mMangaRes(String res) {
|
||||
bool hasNextPage = true;
|
||||
List<MManga> mangaList = [];
|
||||
List<String> names = [];
|
||||
List<String> urls = [];
|
||||
List<String> images = [];
|
||||
if (res.startsWith("{")) {
|
||||
for (var a in json.decode(res)["data"]) {
|
||||
String thumbnail = a["thumbnail"];
|
||||
if (thumbnail.startsWith("https://")) {
|
||||
images.add(thumbnail);
|
||||
} else {
|
||||
images.add("${source.apiUrl}/cover/$thumbnail");
|
||||
}
|
||||
names.add(a["title"]);
|
||||
final seriesSlug = a["series_slug"];
|
||||
urls.add("/series/$seriesSlug");
|
||||
}
|
||||
} else {
|
||||
for (var a in json.decode(res)) {
|
||||
String thumbnail = a["thumbnail"];
|
||||
if (thumbnail.startsWith("https://")) {
|
||||
images.add(thumbnail);
|
||||
} else {
|
||||
images.add("${source.apiUrl}/cover/$thumbnail");
|
||||
}
|
||||
names.add(a["title"]);
|
||||
final seriesSlug = a["series_slug"];
|
||||
urls.add("/series/$seriesSlug");
|
||||
}
|
||||
hasNextPage = false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MManga manga = MManga();
|
||||
manga.name = names[i];
|
||||
manga.imageUrl = images[i];
|
||||
manga.link = urls[i];
|
||||
mangaList.add(manga);
|
||||
}
|
||||
return MPages(mangaList, hasNextPage);
|
||||
}
|
||||
|
||||
bool useNewQueryEndpoint(String sourceName) {
|
||||
List<String> sources = [
|
||||
"YugenMangas",
|
||||
"Perf Scan",
|
||||
"Reaper Scans",
|
||||
"OmegaScans"
|
||||
];
|
||||
return sources.contains(sourceName);
|
||||
}
|
||||
|
||||
bool useslugStrategy(String sourceName) {
|
||||
List<String> sources = [
|
||||
"YugenMangas",
|
||||
"Reaper Scans",
|
||||
"Perf Scan",
|
||||
"OmegaScans"
|
||||
];
|
||||
return sources.contains(sourceName);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> getHeader(String url) {
|
||||
final headers = {'Origin': url, 'Referer': '$url/'};
|
||||
return headers;
|
||||
}
|
||||
|
||||
HeanCms main(MSource source) {
|
||||
return HeanCms(source: source);
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import '../../../../model/source.dart';
|
||||
import 'src/yugenmangas/yugenmangas.dart';
|
||||
import 'src/omegascans/omegascans.dart';
|
||||
import 'src/perfscan/perfscan.dart';
|
||||
|
||||
const heancmsVersion = "0.0.75";
|
||||
const heancmsSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/heancms/heancms.dart";
|
||||
|
||||
List<Source> get heancmsSourcesList => _heancmsSourcesList;
|
||||
List<Source> _heancmsSourcesList = [
|
||||
//YugenMangas (ES)
|
||||
yugenmangasSource,
|
||||
//OmegaScans (EN)
|
||||
omegascansSource,
|
||||
//Perf Scan (FR)
|
||||
perfscanSource,
|
||||
]
|
||||
.map((e) => e
|
||||
..sourceCodeUrl = heancmsSourceCodeUrl
|
||||
..version = heancmsVersion)
|
||||
.toList();
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
|
|
@ -1,16 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get omegascansSource => _omegascansSource;
|
||||
|
||||
Source _omegascansSource = Source(
|
||||
name: "OmegaScans",
|
||||
baseUrl: "https://omegascans.org",
|
||||
apiUrl: "https://api.omegascans.org",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: "heancms",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/heancms/src/omegascans/icon.png",
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
dateFormatLocale: "en",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 5 KiB |
|
|
@ -1,15 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get perfscanSource => _perfscanSource;
|
||||
|
||||
Source _perfscanSource = Source(
|
||||
name: "Perf Scan",
|
||||
baseUrl: "https://perf-scan.fr",
|
||||
apiUrl: "https://api.perf-scan.fr",
|
||||
lang: "fr",
|
||||
typeSource: "heancms",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/heancms/src/perfscan/icon.png",
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
dateFormatLocale: "en",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
|
|
@ -1,16 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get yugenmangasSource => _yugenmangasSource;
|
||||
|
||||
Source _yugenmangasSource = Source(
|
||||
name: "YugenMangas",
|
||||
baseUrl: "https://yugenmangas.lat",
|
||||
apiUrl: "https://api.yugenmangas.net",
|
||||
lang: "es",
|
||||
isNsfw: true,
|
||||
typeSource: "heancms",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/heancms/src/yugenmangas/icon.png",
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
dateFormatLocale: "en",
|
||||
);
|
||||
|
|
@ -168,21 +168,10 @@ class Madara extends MProvider {
|
|||
"";
|
||||
|
||||
final imageElement = document.selectFirst("div.summary_image img");
|
||||
var image = imageElement?.attr("src") ??
|
||||
imageElement?.attr("data-src") ??
|
||||
imageElement?.attr("data-lazy-src") ??
|
||||
imageElement?.attr("srcset");
|
||||
if (image != null) {
|
||||
if (image.contains("dflazy")) {
|
||||
image = imageElement?.attr("data-src") ??
|
||||
imageElement?.attr("data-src") ??
|
||||
imageElement?.attr("data-lazy-src") ??
|
||||
imageElement?.attr("srcset");
|
||||
}
|
||||
if (image != null) {
|
||||
manga.imageUrl = image;
|
||||
}
|
||||
}
|
||||
manga.imageUrl = imageElement?.attr("data-src") ??
|
||||
imageElement?.attr("data-lazy-src") ??
|
||||
imageElement?.attr("srcset") ??
|
||||
imageElement?.getSrc;
|
||||
|
||||
final id = document
|
||||
.selectFirst("div[id^=manga-chapters-holder]")
|
||||
|
|
@ -228,19 +217,30 @@ class Madara extends MProvider {
|
|||
|
||||
@override
|
||||
Future<List<String>> getPageList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final pageElement = document.selectFirst(
|
||||
"div.page-break, li.blocks-gallery-item, .reading-content, .text-left img");
|
||||
final res = (await client.get(Uri.parse(url)));
|
||||
final document = parseHtml(res.body);
|
||||
|
||||
List<String> imgs = pageElement.select("img").map((e) => e.getSrc).toList();
|
||||
final pageElements = document.select(
|
||||
"div.page-break, li.blocks-gallery-item, .reading-content .text-left:not(:has(.blocks-gallery-item)) img");
|
||||
|
||||
List<String> imgs = [];
|
||||
for (var element in pageElements) {
|
||||
try {
|
||||
final imgElement = element.selectFirst("img");
|
||||
final img = imgElement.attr("src") ??
|
||||
imgElement.attr("data-src") ??
|
||||
imgElement.attr("data-lazy-src") ??
|
||||
imgElement.attr("srcset");
|
||||
imgs.add(img);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
List<String> pageUrls = [];
|
||||
|
||||
if (imgs.length == 1) {
|
||||
final pagesNumber =
|
||||
document.selectFirst("#single-pager").select("option").length;
|
||||
final imgUrl = pageElement.selectFirst("img").getSrc;
|
||||
final imgUrl = imgs.first;
|
||||
for (var i = 0; i < pagesNumber; i++) {
|
||||
final val = i + 1;
|
||||
if (i.toString().length == 1) {
|
||||
|
|
|
|||
|
|
@ -1,59 +1,31 @@
|
|||
import '../../../../model/source.dart';
|
||||
import 'src/firstkissdashmanga/firstkissdashmanga.dart';
|
||||
import 'src/firstmanhwa/firstmanhwa.dart';
|
||||
import 'src/manhwaworld/manhwaworld.dart';
|
||||
import 'src/adultwebtoon/adultwebtoon.dart';
|
||||
import 'src/akumanotenshi/akumanotenshi.dart';
|
||||
import 'src/anikiga/anikiga.dart';
|
||||
import 'src/apollcomics/apollcomics.dart';
|
||||
import 'src/apolltoons/apolltoons.dart';
|
||||
import 'src/araznovel/araznovel.dart';
|
||||
import 'src/arthurscan/arthurscan.dart';
|
||||
import 'src/astralmanga/astralmanga.dart';
|
||||
import 'src/asurascansus/asurascansus.dart';
|
||||
import 'src/bananamanga/bananamanga.dart';
|
||||
import 'src/barmanga/barmanga.dart';
|
||||
import 'src/bestmanga/bestmanga.dart';
|
||||
import 'src/birdtoon/birdtoon.dart';
|
||||
import 'src/cerisescans/cerisescans.dart';
|
||||
import 'src/chibimanga/chibimanga.dart';
|
||||
import 'src/clovermanga/clovermanga.dart';
|
||||
import 'src/cocorip/cocorip.dart';
|
||||
import 'src/coffeemanga/coffeemanga.dart';
|
||||
import 'src/comicznetv2/comicznetv2.dart';
|
||||
import 'src/decadencescans/decadencescans.dart';
|
||||
import 'src/dessertscan/dessertscan.dart';
|
||||
import 'src/doodmanga/doodmanga.dart';
|
||||
import 'src/elitemanga/elitemanga.dart';
|
||||
import 'src/emperorscan/emperorscan.dart';
|
||||
import 'src/evascans/evascans.dart';
|
||||
import 'src/frscan/frscan.dart';
|
||||
import 'src/factmanga/factmanga.dart';
|
||||
import 'src/finalscans/finalscans.dart';
|
||||
import 'src/flextapescans/flextapescans.dart';
|
||||
import 'src/freemanga/freemanga.dart';
|
||||
import 'src/freemangatop/freemangatop.dart';
|
||||
import 'src/ghostscan/ghostscan.dart';
|
||||
import 'src/girlslovemanga/girlslovemanga.dart';
|
||||
import 'src/glorymanga/glorymanga.dart';
|
||||
import 'src/gooffansub/gooffansub.dart';
|
||||
import 'src/hm2d/hm2d.dart';
|
||||
import 'src/harimanga/harimanga.dart';
|
||||
import 'src/hentaicube/hentaicube.dart';
|
||||
import 'src/hentaimanga/hentaimanga.dart';
|
||||
import 'src/hentaiteca/hentaiteca.dart';
|
||||
import 'src/hentai3z/hentai3z.dart';
|
||||
import 'src/hentaiwebtoon/hentaiwebtoon.dart';
|
||||
import 'src/hentaixcomic/hentaixcomic.dart';
|
||||
import 'src/hentaixyuri/hentaixyuri.dart';
|
||||
import 'src/hentaizone/hentaizone.dart';
|
||||
import 'src/hipercool/hipercool.dart';
|
||||
import 'src/hiperdex/hiperdex.dart';
|
||||
import 'src/immortalupdates/immortalupdates.dart';
|
||||
import 'src/imperioscans/imperioscans.dart';
|
||||
import 'src/inmortalscan/inmortalscan.dart';
|
||||
import 'src/isekaiscanmanga/isekaiscanmanga.dart';
|
||||
import 'src/jiangzaitoon/jiangzaitoon.dart';
|
||||
import 'src/jimanga/jimanga.dart';
|
||||
import 'src/ksgroupscans/ksgroupscans.dart';
|
||||
import 'src/kakuseiproject/kakuseiproject.dart';
|
||||
|
|
@ -62,17 +34,11 @@ import 'src/komikchan/komikchan.dart';
|
|||
import 'src/lhtranslation/lhtranslation.dart';
|
||||
import 'src/ladyestelarscan/ladyestelarscan.dart';
|
||||
import 'src/leviatanscans/leviatanscans.dart';
|
||||
import 'src/linkstartscan/linkstartscan.dart';
|
||||
import 'src/lolicon/lolicon.dart';
|
||||
import 'src/luffymanga/luffymanga.dart';
|
||||
import 'src/maidscan/maidscan.dart';
|
||||
import 'src/manga18h/manga18h.dart';
|
||||
import 'src/mangabee/mangabee.dart';
|
||||
import 'src/mangadistrict/mangadistrict.dart';
|
||||
import 'src/mangadiyari/mangadiyari.dart';
|
||||
import 'src/mangafenix/mangafenix.dart';
|
||||
import 'src/mangagalaxy/mangagalaxy.dart';
|
||||
import 'src/mangaqueencom/mangaqueencom.dart';
|
||||
import 'src/mangaread/mangaread.dart';
|
||||
import 'src/mangatxgg/mangatxgg.dart';
|
||||
import 'src/mangaweebs/mangaweebs.dart';
|
||||
|
|
@ -80,50 +46,27 @@ import 'src/mangasehri/mangasehri.dart';
|
|||
import 'src/mangatxunoriginal/mangatxunoriginal.dart';
|
||||
import 'src/mangaclash/mangaclash.dart';
|
||||
import 'src/mangafreakonline/mangafreakonline.dart';
|
||||
import 'src/mangagoyaoi/mangagoyaoi.dart';
|
||||
import 'src/mangagreat/mangagreat.dart';
|
||||
import 'src/mangakomi/mangakomi.dart';
|
||||
import 'src/mangaowlone/mangaowlone.dart';
|
||||
import 'src/mangapure/mangapure.dart';
|
||||
import 'src/mangareadorg/mangareadorg.dart';
|
||||
import 'src/mangarolls/mangarolls.dart';
|
||||
import 'src/mangarubycom/mangarubycom.dart';
|
||||
import 'src/mangatyrant/mangatyrant.dart';
|
||||
import 'src/mangaus/mangaus.dart';
|
||||
import 'src/mangaland/mangaland.dart';
|
||||
import 'src/mangaryu/mangaryu.dart';
|
||||
import 'src/mangasorigines/mangasorigines.dart';
|
||||
import 'src/mangasushi/mangasushi.dart';
|
||||
import 'src/mangaxico/mangaxico.dart';
|
||||
import 'src/mangananquim/mangananquim.dart';
|
||||
import 'src/manhuaes/manhuaes.dart';
|
||||
import 'src/manhuazonghe/manhuazonghe.dart';
|
||||
import 'src/manhuaus/manhuaus.dart';
|
||||
import 'src/manhuazone/manhuazone.dart';
|
||||
import 'src/manhwa18app/manhwa18app.dart';
|
||||
import 'src/manhwa18org/manhwa18org.dart';
|
||||
import 'src/manhwa68/manhwa68.dart';
|
||||
import 'src/manhwaclan/manhwaclan.dart';
|
||||
import 'src/manhwamanhua/manhwamanhua.dart';
|
||||
import 'src/manhwanew/manhwanew.dart';
|
||||
import 'src/manhwafull/manhwafull.dart';
|
||||
import 'src/manhwahentaime/manhwahentaime.dart';
|
||||
import 'src/mantrazscan/mantrazscan.dart';
|
||||
import 'src/milftoon/milftoon.dart';
|
||||
import 'src/momonohanascan/momonohanascan.dart';
|
||||
import 'src/monarcamanga/monarcamanga.dart';
|
||||
import 'src/moonwitchinlove/moonwitchinlove.dart';
|
||||
import 'src/moonloversscan/moonloversscan.dart';
|
||||
import 'src/murimscan/murimscan.dart';
|
||||
import 'src/nekopostco/nekopostco.dart';
|
||||
import 'src/nijitranslations/nijitranslations.dart';
|
||||
import 'src/novelcrow/novelcrow.dart';
|
||||
import 'src/pantheonscan/pantheonscan.dart';
|
||||
import 'src/pawmanga/pawmanga.dart';
|
||||
import 'src/pinkseaunicorn/pinkseaunicorn.dart';
|
||||
import 'src/pojokmanga/pojokmanga.dart';
|
||||
import 'src/portalyaoi/portalyaoi.dart';
|
||||
import 'src/prismahentais/prismahentais.dart';
|
||||
import 'src/projetoscanlator/projetoscanlator.dart';
|
||||
import 'src/mangasoverall/mangasoverall.dart';
|
||||
import 'src/ragnarokscanlation/ragnarokscanlation.dart';
|
||||
|
|
@ -142,29 +85,107 @@ import 'src/shadowtrad/shadowtrad.dart';
|
|||
import 'src/shibamanga/shibamanga.dart';
|
||||
import 'src/sinensis/sinensis.dart';
|
||||
import 'src/sweettimescan/sweettimescan.dart';
|
||||
import 'src/taberumangas/taberumangas.dart';
|
||||
import 'src/tankouhentai/tankouhentai.dart';
|
||||
import 'src/tatakaescan/tatakaescan.dart';
|
||||
import 'src/taurusfansub/taurusfansub.dart';
|
||||
import 'src/thebeginningaftertheend/thebeginningaftertheend.dart';
|
||||
import 'src/toonizy/toonizy.dart';
|
||||
import 'src/tortugaceviri/tortugaceviri.dart';
|
||||
import 'src/valkyriescan/valkyriescan.dart';
|
||||
import 'src/vermanhwas/vermanhwas.dart';
|
||||
import 'src/vinmanga/vinmanga.dart';
|
||||
import 'src/wickedwitchscan/wickedwitchscan.dart';
|
||||
import 'src/winterscan/winterscan.dart';
|
||||
import 'src/wonderlandscan/wonderlandscan.dart';
|
||||
import 'src/yanpfansub/yanpfansub.dart';
|
||||
import 'src/yaoicomics/yaoicomics.dart';
|
||||
import 'src/yuriverso/yuriverso.dart';
|
||||
import 'src/zeroscan/zeroscan.dart';
|
||||
import 'src/falconmanga/falconmanga.dart';
|
||||
import 'src/comicarab/comicarab.dart';
|
||||
import 'src/manga3asq/manga3asq.dart';
|
||||
import 'src/bakamh/bakamh.dart';
|
||||
import 'src/grabberzone/grabberzone.dart';
|
||||
import 'src/gatemanga/gatemanga.dart';
|
||||
import 'src/gmangasite/gmangasite.dart';
|
||||
import 'src/mangalink/mangalink.dart';
|
||||
import 'src/mangalionz/mangalionz.dart';
|
||||
import 'src/mangarose/mangarose.dart';
|
||||
import 'src/mangaspark/mangaspark.dart';
|
||||
import 'src/mangastarz/mangastarz.dart';
|
||||
import 'src/mangatime/mangatime.dart';
|
||||
import 'src/olaoe/olaoe.dart';
|
||||
import 'src/rocksmanga/rocksmanga.dart';
|
||||
import 'src/dragontea/dragontea.dart';
|
||||
import 'src/globalbloging/globalbloging.dart';
|
||||
import 'src/manga347/manga347.dart';
|
||||
import 'src/mangaeffect/mangaeffect.dart';
|
||||
import 'src/mangafastcom/mangafastcom.dart';
|
||||
import 'src/mangaleveling/mangaleveling.dart';
|
||||
import 'src/neatmanga/neatmanga.dart';
|
||||
import 'src/nvmanga/nvmanga.dart';
|
||||
import 'src/pmscans/pmscans.dart';
|
||||
import 'src/readmanhua/readmanhua.dart';
|
||||
import 'src/stonescape/stonescape.dart';
|
||||
import 'src/tappytoonnet/tappytoonnet.dart';
|
||||
import 'src/zinmanganet/zinmanganet.dart';
|
||||
import 'src/bokugentranslation/bokugentranslation.dart';
|
||||
import 'src/daprob/daprob.dart';
|
||||
import 'src/demanhuas/demanhuas.dart';
|
||||
import 'src/herenscan/herenscan.dart';
|
||||
import 'src/housemangas/housemangas.dart';
|
||||
import 'src/infrafandub/infrafandub.dart';
|
||||
import 'src/inmoralnofansub/inmoralnofansub.dart';
|
||||
import 'src/jeazscans/jeazscans.dart';
|
||||
import 'src/jobsibe/jobsibe.dart';
|
||||
import 'src/legendsnofansub/legendsnofansub.dart';
|
||||
import 'src/mangacrab/mangacrab.dart';
|
||||
import 'src/mangasnosekai/mangasnosekai.dart';
|
||||
import 'src/manhuaonline/manhuaonline.dart';
|
||||
import 'src/mhscans/mhscans.dart';
|
||||
import 'src/noblessetranslations/noblessetranslations.dart';
|
||||
import 'src/princediciones/princediciones.dart';
|
||||
import 'src/sapphirescan/sapphirescan.dart';
|
||||
import 'src/tresdaosscan/tresdaosscan.dart';
|
||||
import 'src/unitoon/unitoon.dart';
|
||||
import 'src/mangascantrad/mangascantrad.dart';
|
||||
import 'src/mangasoriginesfr/mangasoriginesfr.dart';
|
||||
import 'src/raijinscans/raijinscans.dart';
|
||||
import 'src/reaperscans/reaperscans.dart';
|
||||
import 'src/softepsilonscan/softepsilonscan.dart';
|
||||
import 'src/hwago/hwago.dart';
|
||||
import 'src/klikmanga/klikmanga.dart';
|
||||
import 'src/komikuzan/komikuzan.dart';
|
||||
import 'src/mgkomik/mgkomik.dart';
|
||||
import 'src/siimanga/siimanga.dart';
|
||||
import 'src/yubikiri/yubikiri.dart';
|
||||
import 'src/mangahona/mangahona.dart';
|
||||
import 'src/alonescanlator/alonescanlator.dart';
|
||||
import 'src/borutoexplorer/borutoexplorer.dart';
|
||||
import 'src/crystalcomics/crystalcomics.dart';
|
||||
import 'src/fayscans/fayscans.dart';
|
||||
import 'src/galinhasamuraiscan/galinhasamuraiscan.dart';
|
||||
import 'src/hikariganai/hikariganai.dart';
|
||||
import 'src/imperiodabritannia/imperiodabritannia.dart';
|
||||
import 'src/leitordemanga/leitordemanga.dart';
|
||||
import 'src/lscans/lscans.dart';
|
||||
import 'src/lunarscan/lunarscan.dart';
|
||||
import 'src/minitwoscan/minitwoscan.dart';
|
||||
import 'src/nexoscans/nexoscans.dart';
|
||||
import 'src/ninjascan/ninjascan.dart';
|
||||
import 'src/onepieceteca/onepieceteca.dart';
|
||||
import 'src/prismascans/prismascans.dart';
|
||||
import 'src/sussyscan/sussyscan.dart';
|
||||
import 'src/kingsmanga/kingsmanga.dart';
|
||||
import 'src/mangadeemak/mangadeemak.dart';
|
||||
import 'src/manhuabug/manhuabug.dart';
|
||||
import 'src/manhuathai/manhuathai.dart';
|
||||
import 'src/manhwabreakup/manhwabreakup.dart';
|
||||
import 'src/atikrost/atikrost.dart';
|
||||
import 'src/diamondfansub/diamondfansub.dart';
|
||||
import 'src/garciamanga/garciamanga.dart';
|
||||
import 'src/guncelmanga/guncelmanga.dart';
|
||||
import 'src/lilyumfansub/lilyumfansub.dart';
|
||||
import 'src/mangagezgini/mangagezgini.dart';
|
||||
import 'src/mangakeyfi/mangakeyfi.dart';
|
||||
import 'src/mangaoku/mangaoku.dart';
|
||||
import 'src/mangawow/mangawow.dart';
|
||||
import 'src/merlinscans/merlinscans.dart';
|
||||
import 'src/ruyamanga/ruyamanga.dart';
|
||||
import 'src/turkcemangaoku/turkcemangaoku.dart';
|
||||
import 'src/webtoonhatti/webtoonhatti.dart';
|
||||
|
||||
const madaraVersion = "0.0.95";
|
||||
const madaraVersion = "0.1.15";
|
||||
const madaraSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/madara.dart";
|
||||
|
||||
|
|
@ -172,20 +193,12 @@ List<Source> get madaraSourcesList => _madaraSourcesList;
|
|||
List<Source> _madaraSourcesList = [
|
||||
//1st Kiss-Manga (unoriginal) (EN)
|
||||
firstkissdashmangaSource,
|
||||
//1st Manhwa (EN)
|
||||
firstmanhwaSource,
|
||||
//AQUA Scans (EN)
|
||||
manhwaworldSource,
|
||||
//Adult Webtoon (EN)
|
||||
adultwebtoonSource,
|
||||
//Akuma no Tenshi (PT-BR)
|
||||
akumanotenshiSource,
|
||||
//Anikiga (TR)
|
||||
anikigaSource,
|
||||
//ApollComics (ES)
|
||||
apollcomicsSource,
|
||||
//Apolltoons (ES)
|
||||
apolltoonsSource,
|
||||
//ArazNovel (TR)
|
||||
araznovelSource,
|
||||
//Arthur Scan (PT-BR)
|
||||
|
|
@ -194,16 +207,10 @@ List<Source> _madaraSourcesList = [
|
|||
astralmangaSource,
|
||||
//Asura Scans.us (unoriginal) (EN)
|
||||
asurascansusSource,
|
||||
//Banana Manga (EN)
|
||||
bananamangaSource,
|
||||
//BarManga (ES)
|
||||
barmangaSource,
|
||||
//BestManga (RU)
|
||||
bestmangaSource,
|
||||
//BirdToon (ID)
|
||||
birdtoonSource,
|
||||
//Cerise Scan (PT-BR)
|
||||
cerisescansSource,
|
||||
//Chibi Manga (EN)
|
||||
chibimangaSource,
|
||||
//Clover Manga (TR)
|
||||
|
|
@ -212,12 +219,6 @@ List<Source> _madaraSourcesList = [
|
|||
cocoripSource,
|
||||
//Coffee Manga (EN)
|
||||
coffeemangaSource,
|
||||
//Comicz.net v2 (ALL)
|
||||
comicznetv2Source,
|
||||
//Decadence Scans (EN)
|
||||
decadencescansSource,
|
||||
//Dessert Scan (PT-BR)
|
||||
dessertscanSource,
|
||||
//Doodmanga (TH)
|
||||
doodmangaSource,
|
||||
//Elite Manga (EN)
|
||||
|
|
@ -230,46 +231,12 @@ List<Source> _madaraSourcesList = [
|
|||
frscanSource,
|
||||
//FactManga (EN)
|
||||
factmangaSource,
|
||||
//Final Scans (PT-BR)
|
||||
finalscansSource,
|
||||
//Flex Tape Scans (EN)
|
||||
flextapescansSource,
|
||||
//Free Manga (EN)
|
||||
freemangaSource,
|
||||
//FreeMangaTop (EN)
|
||||
freemangatopSource,
|
||||
//Ghost Scan (PT-BR)
|
||||
ghostscanSource,
|
||||
//Girls Love Manga! (EN)
|
||||
girlslovemangaSource,
|
||||
//Glory Manga (TR)
|
||||
glorymangaSource,
|
||||
//Goof Fansub (PT-BR)
|
||||
gooffansubSource,
|
||||
//HM2D (EN)
|
||||
hm2dSource,
|
||||
//Harimanga (EN)
|
||||
harimangaSource,
|
||||
//Hentai CB (VI)
|
||||
hentaicubeSource,
|
||||
//Hentai Manga (EN)
|
||||
hentaimangaSource,
|
||||
//Hentai Teca (PT-BR)
|
||||
hentaitecaSource,
|
||||
//Hentai3z (EN)
|
||||
hentai3zSource,
|
||||
//HentaiWebtoon (EN)
|
||||
hentaiwebtoonSource,
|
||||
//HentaiXComic (EN)
|
||||
hentaixcomicSource,
|
||||
//HentaiXYuri (EN)
|
||||
hentaixyuriSource,
|
||||
//HentaiZone (FR)
|
||||
hentaizoneSource,
|
||||
//HipercooL (PT-BR)
|
||||
hipercoolSource,
|
||||
//Hiperdex (EN)
|
||||
hiperdexSource,
|
||||
//Immortal Updates (EN)
|
||||
immortalupdatesSource,
|
||||
//Império Scans (PT-BR)
|
||||
|
|
@ -278,8 +245,6 @@ List<Source> _madaraSourcesList = [
|
|||
inmortalscanSource,
|
||||
//IsekaiScanManga (unoriginal) (EN)
|
||||
isekaiscanmangaSource,
|
||||
//Jiangzaitoon (TR)
|
||||
jiangzaitoonSource,
|
||||
//Jimanga (EN)
|
||||
jimangaSource,
|
||||
//KSGroupScans (EN)
|
||||
|
|
@ -296,28 +261,16 @@ List<Source> _madaraSourcesList = [
|
|||
ladyestelarscanSource,
|
||||
//Leviatan Scans (EN)
|
||||
leviatanscansSource,
|
||||
//Link Start Scan (PT-BR)
|
||||
linkstartscanSource,
|
||||
//Lolicon (EN)
|
||||
loliconSource,
|
||||
//Luffy Manga (EN)
|
||||
luffymangaSource,
|
||||
//Maid Scan (PT-BR)
|
||||
maidscanSource,
|
||||
//Manga 18h (EN)
|
||||
manga18hSource,
|
||||
//Manga Bee (EN)
|
||||
mangabeeSource,
|
||||
//Manga District (EN)
|
||||
mangadistrictSource,
|
||||
//Manga Diyari (TR)
|
||||
mangadiyariSource,
|
||||
//Manga Fenix (ES)
|
||||
mangafenixSource,
|
||||
//Manga Galaxy (EN)
|
||||
mangagalaxySource,
|
||||
//Manga Queen.com (EN)
|
||||
mangaqueencomSource,
|
||||
//Manga Read (EN)
|
||||
mangareadSource,
|
||||
//Manga Tx.gg (unoriginal) (EN)
|
||||
|
|
@ -332,94 +285,48 @@ List<Source> _madaraSourcesList = [
|
|||
mangaclashSource,
|
||||
//MangaFreak.online (EN)
|
||||
mangafreakonlineSource,
|
||||
//MangaGo Yaoi (EN)
|
||||
mangagoyaoiSource,
|
||||
//MangaGreat (EN)
|
||||
mangagreatSource,
|
||||
//MangaKomi (EN)
|
||||
mangakomiSource,
|
||||
//MangaOwl.one (unoriginal) (EN)
|
||||
mangaowloneSource,
|
||||
//MangaPure (EN)
|
||||
mangapureSource,
|
||||
//MangaRead.org (EN)
|
||||
mangareadorgSource,
|
||||
//MangaRolls (EN)
|
||||
mangarollsSource,
|
||||
//MangaRuby.com (EN)
|
||||
mangarubycomSource,
|
||||
//MangaTyrant (EN)
|
||||
mangatyrantSource,
|
||||
//MangaUS (EN)
|
||||
mangausSource,
|
||||
//Mangaland (ES)
|
||||
mangalandSource,
|
||||
//Mangaryu (EN)
|
||||
mangaryuSource,
|
||||
//Mangas Origines (FR)
|
||||
mangasoriginesSource,
|
||||
//Mangasushi (EN)
|
||||
mangasushiSource,
|
||||
//Mangaxico (ES)
|
||||
mangaxicoSource,
|
||||
//Mangá Nanquim (PT-BR)
|
||||
mangananquimSource,
|
||||
//Manhua ES (EN)
|
||||
manhuaesSource,
|
||||
//Manhua Zonghe (EN)
|
||||
manhuazongheSource,
|
||||
//ManhuaUS (EN)
|
||||
manhuausSource,
|
||||
//ManhuaZone (EN)
|
||||
manhuazoneSource,
|
||||
//Manhwa18.app (EN)
|
||||
manhwa18appSource,
|
||||
//Manhwa18.org (EN)
|
||||
manhwa18orgSource,
|
||||
//Manhwa68 (EN)
|
||||
manhwa68Source,
|
||||
//ManhwaClan (EN)
|
||||
manhwaclanSource,
|
||||
//ManhwaManhua (EN)
|
||||
manhwamanhuaSource,
|
||||
//ManhwaNew (EN)
|
||||
manhwanewSource,
|
||||
//Manhwafull (EN)
|
||||
manhwafullSource,
|
||||
//Manhwahentai.me (EN)
|
||||
manhwahentaimeSource,
|
||||
//Mantraz Scan (ES)
|
||||
mantrazscanSource,
|
||||
//Milftoon (EN)
|
||||
milftoonSource,
|
||||
//Momo no Hana Scan (PT-BR)
|
||||
momonohanascanSource,
|
||||
//MonarcaManga (ES)
|
||||
monarcamangaSource,
|
||||
//Moon Witch In Love (PT-BR)
|
||||
moonwitchinloveSource,
|
||||
//MoonLovers Scan (PT-BR)
|
||||
moonloversscanSource,
|
||||
//MurimScan (EN)
|
||||
murimscanSource,
|
||||
//NekoPost.co (unoriginal) (TH)
|
||||
nekopostcoSource,
|
||||
//Niji Translations (AR)
|
||||
nijitranslationsSource,
|
||||
//NovelCrow (EN)
|
||||
novelcrowSource,
|
||||
//Pantheon Scan (FR)
|
||||
pantheonscanSource,
|
||||
//Paw Manga (EN)
|
||||
pawmangaSource,
|
||||
//Pink Sea Unicorn (PT-BR)
|
||||
pinkseaunicornSource,
|
||||
//Pojok Manga (ID)
|
||||
pojokmangaSource,
|
||||
//Portal Yaoi (PT-BR)
|
||||
portalyaoiSource,
|
||||
//Prisma Hentais (PT-BR)
|
||||
prismahentaisSource,
|
||||
//Projeto Scanlator (PT-BR)
|
||||
projetoscanlatorSource,
|
||||
//ROG Mangás (PT-BR)
|
||||
|
|
@ -456,50 +363,215 @@ List<Source> _madaraSourcesList = [
|
|||
sinensisSource,
|
||||
//Sweet Time Scan (PT-BR)
|
||||
sweettimescanSource,
|
||||
//Taberu Mangás (PT-BR)
|
||||
taberumangasSource,
|
||||
//Tankou Hentai (PT-BR)
|
||||
tankouhentaiSource,
|
||||
//Tatakae Scan (PT-BR)
|
||||
tatakaescanSource,
|
||||
//Taurus Fansub (ES)
|
||||
taurusfansubSource,
|
||||
//The Beginning After The End (FR)
|
||||
thebeginningaftertheendSource,
|
||||
//Toonizy (EN)
|
||||
toonizySource,
|
||||
//Tortuga Ceviri (TR)
|
||||
tortugaceviriSource,
|
||||
//Valkyrie Scan (PT-BR)
|
||||
valkyriescanSource,
|
||||
//Ver Manhwas (ES)
|
||||
vermanhwasSource,
|
||||
//VinManga (EN)
|
||||
vinmangaSource,
|
||||
//Wicked Witch Scan (PT-BR)
|
||||
wickedwitchscanSource,
|
||||
//Winter Scan (PT-BR)
|
||||
winterscanSource,
|
||||
//Wonderland Scan (PT-BR)
|
||||
wonderlandscanSource,
|
||||
//YANP Fansub (PT-BR)
|
||||
yanpfansubSource,
|
||||
//Yaoi Comics (PT-BR)
|
||||
yaoicomicsSource,
|
||||
//Yuri Verso (PT-BR)
|
||||
yuriversoSource,
|
||||
//Zero Scan (PT-BR)
|
||||
zeroscanSource,
|
||||
//فالكون مانجا (AR)
|
||||
falconmangaSource,
|
||||
//كوميك العرب (AR)
|
||||
comicarabSource,
|
||||
//مانجا العاشق (AR)
|
||||
manga3asqSource,
|
||||
//巴卡漫画 (ZH)
|
||||
bakamhSource
|
||||
//Grabber Zone (ALL)
|
||||
grabberzoneSource,
|
||||
//Gatemanga (AR)
|
||||
gatemangaSource,
|
||||
//GMANGA (unoriginal) (AR)
|
||||
gmangasiteSource,
|
||||
//مانجا لينك (AR)
|
||||
mangalinkSource,
|
||||
//MangaLionz (AR)
|
||||
mangalionzSource,
|
||||
//Manga Rose (AR)
|
||||
mangaroseSource,
|
||||
//MangaSpark (AR)
|
||||
mangasparkSource,
|
||||
//Manga Starz (AR)
|
||||
mangastarzSource,
|
||||
//Manga Time (AR)
|
||||
mangatimeSource,
|
||||
//Olaoe (AR)
|
||||
olaoeSource,
|
||||
//Rocks Manga (AR)
|
||||
rocksmangaSource,
|
||||
//DragonTea (EN)
|
||||
dragonteaSource,
|
||||
//Global Bloging (EN)
|
||||
globalblogingSource,
|
||||
//Manga347 (EN)
|
||||
manga347Source,
|
||||
//MangaEffect (EN)
|
||||
mangaeffectSource,
|
||||
//Manga-fast.com (EN)
|
||||
mangafastcomSource,
|
||||
//Manga Leveling (EN)
|
||||
mangalevelingSource,
|
||||
//NeatManga (EN)
|
||||
neatmangaSource,
|
||||
//NvManga (EN)
|
||||
nvmangaSource,
|
||||
//PMScans (EN)
|
||||
pmscansSource,
|
||||
//ReadManhua (EN)
|
||||
readmanhuaSource,
|
||||
//StoneScape (EN)
|
||||
stonescapeSource,
|
||||
//TappyToon.Net (EN)
|
||||
tappytoonnetSource,
|
||||
//Zinmanga.net (EN)
|
||||
zinmanganetSource,
|
||||
//BokugenTranslation (ES)
|
||||
bokugentranslationSource,
|
||||
//DapRob (ES)
|
||||
daprobSource,
|
||||
//DeManhuas (ES)
|
||||
demanhuasSource,
|
||||
//HerenScan (ES)
|
||||
herenscanSource,
|
||||
//HouseMangas (ES)
|
||||
housemangasSource,
|
||||
//InfraFandub (ES)
|
||||
infrafandubSource,
|
||||
//Inmoral No Fansub (ES)
|
||||
inmoralnofansubSource,
|
||||
//JeazScans (ES)
|
||||
jeazscansSource,
|
||||
//Jobsibe (ES)
|
||||
jobsibeSource,
|
||||
//LegnMangas (ES)
|
||||
legendsnofansubSource,
|
||||
//Manga Crab (ES)
|
||||
mangacrabSource,
|
||||
//MMFenix (ES)
|
||||
mangafenixSource,
|
||||
//Mangas No Sekai (ES)
|
||||
mangasnosekaiSource,
|
||||
//Manhua Online (ES)
|
||||
manhuaonlineSource,
|
||||
//MHScans (ES)
|
||||
mhscansSource,
|
||||
//Visormonarca (ES)
|
||||
monarcamangaSource,
|
||||
//Noblesse Translations (ES)
|
||||
noblessetranslationsSource,
|
||||
//Prince Ediciones (ES)
|
||||
princedicionesSource,
|
||||
//SapphireScan (ES)
|
||||
sapphirescanSource,
|
||||
//Tres Daos Scan (ES)
|
||||
tresdaosscanSource,
|
||||
//Unitoon (ES)
|
||||
unitoonSource,
|
||||
//Manga-Scantrad (FR)
|
||||
mangascantradSource,
|
||||
//Mangas-Origines.fr (FR)
|
||||
mangasoriginesfrSource,
|
||||
//Raijin Scans (FR)
|
||||
raijinscansSource,
|
||||
//Reaper Scans (EN)
|
||||
reaperscansSource,
|
||||
//Soft Epsilon Scan (FR)
|
||||
softepsilonscanSource,
|
||||
//Hwago (ID)
|
||||
hwagoSource,
|
||||
//KlikManga (ID)
|
||||
klikmangaSource,
|
||||
//Komikuzan (ID)
|
||||
komikuzanSource,
|
||||
//MG Komik (ID)
|
||||
mgkomikSource,
|
||||
//Siimanga (ID)
|
||||
siimangaSource,
|
||||
//Yubikiri (ID)
|
||||
yubikiriSource,
|
||||
//MangaHoNa (PL)
|
||||
mangahonaSource,
|
||||
//Alone Scanlator (PT-BR)
|
||||
alonescanlatorSource,
|
||||
//Boruto Explorer (PT-BR)
|
||||
borutoexplorerSource,
|
||||
//Crystal Comics (PT-BR)
|
||||
crystalcomicsSource,
|
||||
//Fay Scans (PT-BR)
|
||||
fayscansSource,
|
||||
//Galinha Samurai Scan (PT-BR)
|
||||
galinhasamuraiscanSource,
|
||||
//Hikari Ga Nai (PT-BR)
|
||||
hikariganaiSource,
|
||||
//Sagrado Império da Britannia (PT-BR)
|
||||
imperiodabritanniaSource,
|
||||
//Neroxus (PT-BR)
|
||||
imperioscansSource,
|
||||
//Leitor de Mangá (PT-BR)
|
||||
leitordemangaSource,
|
||||
//L Scans (PT-BR)
|
||||
lscansSource,
|
||||
//Lunar Scan (PT-BR)
|
||||
lunarscanSource,
|
||||
//MiniTwo Scan (PT-BR)
|
||||
minitwoscanSource,
|
||||
//Nexo Scans (PT-BR)
|
||||
nexoscansSource,
|
||||
//Ninja Scan (PT-BR)
|
||||
ninjascanSource,
|
||||
//One Piece TECA (PT-BR)
|
||||
onepiecetecaSource,
|
||||
//Demon Sect (PT-BR)
|
||||
prismascansSource,
|
||||
//Sussy Scan (PT-BR)
|
||||
sussyscanSource,
|
||||
//Kings-Manga (TH)
|
||||
kingsmangaSource,
|
||||
//MangaDeemak (TH)
|
||||
mangadeemakSource,
|
||||
//ManhuaBug (TH)
|
||||
manhuabugSource,
|
||||
//ManhuaThai (TH)
|
||||
manhuathaiSource,
|
||||
//ManhwaBreakup (TH)
|
||||
manhwabreakupSource,
|
||||
//Atikrost (TR)
|
||||
atikrostSource,
|
||||
//DiamondFansub (TR)
|
||||
diamondfansubSource,
|
||||
//ManWe (TR)
|
||||
evascansSource,
|
||||
//Garcia Manga (TR)
|
||||
garciamangaSource,
|
||||
//GuncelManga (TR)
|
||||
guncelmangaSource,
|
||||
//LilyumFansub (TR)
|
||||
lilyumfansubSource,
|
||||
//MangaGezgini (TR)
|
||||
mangagezginiSource,
|
||||
//Manga Keyfi (TR)
|
||||
mangakeyfiSource,
|
||||
//Manga Oku (TR)
|
||||
mangaokuSource,
|
||||
//MangaWOW (TR)
|
||||
mangawowSource,
|
||||
//Merlin Scans (TR)
|
||||
merlinscansSource,
|
||||
//Rüya Manga (TR)
|
||||
ruyamangaSource,
|
||||
//Türkçe Manga Oku (TR)
|
||||
turkcemangaokuSource,
|
||||
//Webtoon Hatti (TR)
|
||||
webtoonhattiSource,
|
||||
]
|
||||
.map((e) => e
|
||||
..itemType = ItemType.manga
|
||||
..sourceCodeUrl = madaraSourceCodeUrl
|
||||
..version = madaraVersion)
|
||||
.toList();
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get adultwebtoonSource => _adultwebtoonSource;
|
||||
|
||||
Source _adultwebtoonSource = Source(
|
||||
name: "Adult Webtoon",
|
||||
baseUrl: "https://adultwebtoon.com",
|
||||
lang: "en",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/adultwebtoon/icon.png",
|
||||
dateFormat:"MMMM dd, yyyy",
|
||||
dateFormatLocale:"en_us",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 4.7 KiB |
|
|
@ -0,0 +1,13 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get alonescanlatorSource => _alonescanlatorSource;
|
||||
Source _alonescanlatorSource = Source(
|
||||
name: "Alone Scanlator",
|
||||
baseUrl: "https://alonescanlator.com.br",
|
||||
lang: "pt-br",
|
||||
isNsfw:false,
|
||||
typeSource: "madara",
|
||||
iconUrl: "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/dart/manga/multisrc/madara/src/alonescanlator/icon.png",
|
||||
dateFormat:"dd/MM/yyyy",
|
||||
dateFormatLocale:"pt-br"
|
||||
);
|
||||
BIN
dart/manga/multisrc/madara/src/alonescanlator/icon.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get apollcomicsSource => _apollcomicsSource;
|
||||
|
||||
Source _apollcomicsSource = Source(
|
||||
name: "ApollComics",
|
||||
baseUrl: "https://apollcomics.xyz",
|
||||
lang: "es",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/apollcomics/icon.png",
|
||||
dateFormat:"dd MMMM, yyyy",
|
||||
dateFormatLocale:"es",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get apolltoonsSource => _apolltoonsSource;
|
||||
|
||||
Source _apolltoonsSource = Source(
|
||||
name: "Apolltoons",
|
||||
baseUrl: "https://apolltoons.xyz",
|
||||
lang: "es",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/apolltoons/icon.png",
|
||||
dateFormat:"dd MMMMM, yyyy",
|
||||
dateFormatLocale:"es",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
13
dart/manga/multisrc/madara/src/atikrost/atikrost.dart
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get atikrostSource => _atikrostSource;
|
||||
Source _atikrostSource = Source(
|
||||
name: "Atikrost",
|
||||
baseUrl: "https://www.atikrost.com",
|
||||
lang: "tr",
|
||||
isNsfw:false,
|
||||
typeSource: "madara",
|
||||
iconUrl: "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/dart/manga/multisrc/madara/src/atikrost/icon.png",
|
||||
dateFormat:"d MMMM yyyy",
|
||||
dateFormatLocale:"tr"
|
||||
);
|
||||
BIN
dart/manga/multisrc/madara/src/atikrost/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get bakamhSource => _bakamhSource;
|
||||
|
||||
Source _bakamhSource = Source(
|
||||
name: "巴卡漫画",
|
||||
baseUrl: "https://bakamh.com",
|
||||
lang: "zh",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/bakamh/icon.png",
|
||||
|
||||
|
||||
);
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get bananamangaSource => _bananamangaSource;
|
||||
|
||||
Source _bananamangaSource = Source(
|
||||
name: "Banana Manga",
|
||||
baseUrl: "https://bananamanga.net",
|
||||
lang: "en",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/bananamanga/icon.png",
|
||||
dateFormat:"MMMM dd, yyyy",
|
||||
dateFormatLocale:"en_us",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
|
|
@ -1,14 +0,0 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get birdtoonSource => _birdtoonSource;
|
||||
|
||||
Source _birdtoonSource = Source(
|
||||
name: "BirdToon",
|
||||
baseUrl: "https://birdtoon.net",
|
||||
lang: "id",
|
||||
isNsfw:true,
|
||||
typeSource: "madara",
|
||||
iconUrl:"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/multisrc/madara/src/birdtoon/icon.png",
|
||||
dateFormat:"MMMM dd, yyyy",
|
||||
dateFormatLocale:"en_us",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,13 @@
|
|||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get bokugentranslationSource => _bokugentranslationSource;
|
||||
Source _bokugentranslationSource = Source(
|
||||
name: "BokugenTranslation",
|
||||
baseUrl: "https://bokugents.com",
|
||||
lang: "es",
|
||||
isNsfw:false,
|
||||
typeSource: "madara",
|
||||
iconUrl: "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/dart/manga/multisrc/madara/src/bokugentranslation/icon.png",
|
||||
dateFormat:"MMMM dd, yyyy",
|
||||
dateFormatLocale:"es"
|
||||
);
|
||||
BIN
dart/manga/multisrc/madara/src/bokugentranslation/icon.png
Normal file
|
After Width: | Height: | Size: 11 KiB |