610 Commits

Author SHA1 Message Date
Jorrin
39ce1023c2 Merge pull request #154 from ztpn/tugaflix
add Tugaflix source
2024-04-16 22:47:55 +02:00
Jorrin
4f351d91ed Merge branch 'dev' into tugaflix 2024-04-16 22:45:42 +02:00
Jorrin
d639c0b0cf Merge pull request #155 from ztpn/bflix
add nites.is source and bflix embed
2024-04-16 19:21:55 +02:00
TPN
3a9c687d56 adjust ranks 2024-04-16 22:02:57 +05:30
TPN
af22162bfd add source nites and embed bflix 2024-04-16 20:33:06 +05:30
TPN
c8b2ea3e81 change rank and add comments 2024-04-15 19:25:05 +05:30
TPN
423c07cc56 add tugaflix 2024-04-15 19:24:08 +05:30
Jorrin
89a78e4ae9 disable vidsrc 2024-04-12 18:30:21 +02:00
Jorrin
3826b8d887 Merge branch 'master' into dev 2024-04-12 17:43:37 +02:00
Jorrin
f0d9ee2944 Merge branch 'dev' of https://github.com/movie-web/providers into dev 2024-04-12 17:42:52 +02:00
Jorrin
8de7e61eab bump version + changelog 2024-04-12 17:42:44 +02:00
Jorrin
95617763d0 Merge pull request #151 from lonelil/dev
add WarezCDN
2024-04-12 17:37:01 +02:00
lonelil
0a2259b1f4 remove user-agent 2024-04-12 16:59:51 +08:00
lonelil
79a8fdca6d fix series 2024-04-12 16:56:14 +08:00
Jorrin
94bfcd0c31 skip validation check for warezcdnmp4 2024-04-11 20:42:37 +02:00
Jorrin
ef600dd08c Merge branch 'dev' into pr/151 2024-04-11 19:55:23 +02:00
Jorrin
025b6821e6 Merge pull request #146 from infvortx/dev
Add Insertunit provider
2024-04-11 19:46:21 +02:00
Jorrin
356286dfaa Merge branch 'dev' of https://github.com/infvortx/providers-dev into pr/146 2024-04-11 19:44:19 +02:00
Jorrin
534b53f9b9 Merge branch 'dev' into pr/146 2024-04-11 19:44:06 +02:00
lonelil
089c6aa6ce add flag and change rank 2024-04-12 01:11:29 +08:00
teddyHV11
2553909e8d Merge branch 'dev' of https://github.com/infvortx/providers-dev into dev 2024-04-11 17:49:33 +03:00
teddyHV11
6a0ac52908 Fix prettier errors 2024-04-11 17:49:32 +03:00
lonelil
cffbc844e0 complete requested changes 2024-04-11 22:06:53 +08:00
Jorrin
116cc1c3c3 Merge pull request #149 from movie-web/feature/soapertv
Add soapertv source
2024-04-11 15:50:32 +02:00
Jorrin
0ede244c2b Merge branch 'dev' into dev 2024-04-11 15:48:11 +02:00
chaos
5be81a3142 Merge branch 'dev' into feature/soapertv 2024-04-11 14:37:36 +03:00
lonelil
3f68b00ffa Merge branch 'movie-web:dev' into dev 2024-04-11 19:30:04 +08:00
lonelil
9ba05ab991 add types 2024-04-11 19:29:35 +08:00
lonelil
c17b74c8ba use worker proxy 2024-04-11 19:23:46 +08:00
Jorrin
047b69c309 Merge pull request #150 from Ciarands/dev
Fix vidsrc.me
2024-04-11 13:18:57 +02:00
Ciarands
61952b0e87 Remove CORS_ALLOWED flag and add headers 2024-04-11 12:08:31 +01:00
Ciarands
125021e432 remove CORS_ALLOWED flag 2024-04-11 12:08:03 +01:00
Ciarands
9e06035e27 Update RCP base url 2024-04-11 12:03:04 +01:00
teddyHV11
8281c3141a Rename insertunit.ts to index.ts 2024-04-11 06:31:53 +03:00
teddyHV11
8796b39a63 Fix prettier errors 2024-04-11 04:18:38 +03:00
teddyHV11
6b038a288c Clarity fixes 2024-04-11 03:57:42 +03:00
Jorrin
ec6524389c add soapertv source 2024-04-10 22:40:55 +02:00
teddyHV11
5d6b93385e Types adjustment, extract captions to a function 2024-04-10 07:24:53 +03:00
teddyHV11
5b836a4839 Fix mistake done with captions
Swap languages and type's place
2024-04-09 17:44:41 +03:00
infvortx
082d2754be Merge branch 'movie-web:dev' into dev 2024-04-09 09:49:32 +03:00
teddyHV11
921c35b3ed Add caption scraping + types 2024-04-09 07:16:41 +03:00
William Oldham
09eadfd306 Merge pull request #148 from movie-web/feature/disable-showbox
Disable showbox
2024-04-08 15:10:51 +01:00
Jorrin
fbbb671967 Merge branch 'dev' into dev 2024-04-08 16:09:37 +02:00
Jorrin
2daded1d79 Disable showbox 2024-04-08 15:37:41 +02:00
lonelil
1a754ad951 fix hls url format 2024-04-08 15:05:41 +08:00
lonelil
51c58aae75 split embeds 2024-04-07 22:53:52 +08:00
lonelil
aabfbfbeea Merge branch 'movie-web:dev' into dev 2024-04-07 22:13:42 +08:00
teddyHV11
7dc5a5ac83 Fix series scraping
Series scraping was off for some series, this fixes it + also adds support for episodes that are in multiple parts (continuation of each other) as the provider returns them as a single file in episode ranges.
2024-04-07 15:33:04 +03:00
Jorrin
ecf46817d4 Merge pull request #147 from ztpn/vidsrc.me
Vidsrc.me
2024-04-07 13:43:20 +02:00
Jorrin
7d87e77695 Merge branch 'dev' into vidsrc.me 2024-04-07 13:39:59 +02:00
TPN
e75fe05ad2 revert settings.json 2024-04-07 11:37:38 +00:00
TPN
fa8e3fa7e1 fix vidsrc 2024-04-07 11:21:48 +00:00
teddyHV11
73facc0184 Merge branch 'dev' of https://github.com/infvortx/providers-dev into dev 2024-04-07 11:54:37 +03:00
teddyHV11
68fe85065f Updated provider name 2024-04-07 11:54:34 +03:00
infvortx
8a711265cb Update insertunit.ts 2024-04-07 11:48:04 +03:00
infvortx
a58ea29ae5 Merge branch 'movie-web:dev' into dev 2024-04-07 10:53:44 +03:00
teddyHV11
d99a22d734 Add insertunit 2024-04-07 10:53:09 +03:00
lonelil
0c7aa1756b add WarezCDN (broken) 2024-04-06 15:53:28 +08:00
Jorrin
53fbc3b42d Merge pull request #144 from movie-web/dev
Version 2.2.9
2024-04-05 20:04:21 +02:00
Jorrin
29d0ea4276 add ridomovies search to changelog 2024-04-05 20:01:45 +02:00
Jorrin
3ae517c2d2 Merge pull request #145 from ztpn/rido
fix ridomovies search
2024-04-05 20:00:14 +02:00
TPN
718eb97c06 check year 2024-04-05 23:28:49 +05:30
Jorrin
68a70b1177 Merge branch 'dev' into rido 2024-04-05 19:54:00 +02:00
Jorrin
a6667ade8c remove unused flag 2024-04-05 19:52:43 +02:00
TPN
301c9200ad fix ridomovies search 2024-04-05 17:41:43 +00:00
Jorrin
2661e24908 bump version and changelog 2024-04-05 19:26:01 +02:00
Jorrin
95e4578eee Merge pull request #142 from movie-web/dependabot/npm_and_yarn/dot-docs/undici-5.28.4
Bump undici from 5.28.3 to 5.28.4 in /.docs
2024-04-05 17:36:10 +02:00
Jorrin
18a3d9d9e6 Merge pull request #143 from ztpn/smashystream
fix smashystream
2024-04-05 17:35:57 +02:00
TPN
296216c734 fix all.ts 2024-04-05 20:54:21 +05:30
TPN
7d5dca227d Update all.ts 2024-04-05 20:51:32 +05:30
TPN
4238672f28 fix smashystream 2024-04-05 11:33:28 +00:00
dependabot[bot]
867313fce1 Bump undici from 5.28.3 to 5.28.4 in /.docs
Bumps [undici](https://github.com/nodejs/undici) from 5.28.3 to 5.28.4.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.3...v5.28.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 17:40:39 +00:00
Jorrin
c4343b81c7 Merge pull request #138 from ztpn/embeds
Embeds
2024-04-03 20:39:41 +02:00
TPN
0382438fe1 fixes 2024-04-03 23:26:54 +05:30
TPN
b488ff569f Merge branch 'dev' into embeds 2024-04-03 21:26:44 +05:30
Jorrin
3d7e8aacec Merge pull request #140 from Ciarands/dev
Add CORS restrictions to vidsrcto source and vidplay embed
2024-04-03 17:31:26 +02:00
Jorrin
d25e1eaede filemoon fix 2024-04-03 17:30:01 +02:00
Ciarands
464f34d574 Remove console.log 2024-04-03 15:48:41 +01:00
Ciarands
b8570bb560 Add referrer and origin to vidplay 2024-04-03 15:47:55 +01:00
Ciarands
494b662af7 Merge branch 'dev' of https://github.com/Ciarands/mw-providers into dev 2024-04-03 15:32:33 +01:00
Ciarands
97008a6439 Add IP_LOCKED flag to vidsrcto 2024-04-03 15:32:18 +01:00
Ciarands
49d1e58681 Add IP_LOCKED flag to vidplay 2024-04-03 15:31:50 +01:00
Jorrin
314b7ccfcb Merge pull request #139 from Ciarands/dev
Fix vidsrc.to
2024-04-03 14:48:25 +02:00
Ciarands
8551101b5d Merge branch 'dev' of https://github.com/Ciarands/mw-providers into dev 2024-04-03 13:44:51 +01:00
Ciarands
2d909d72cd Update default encryption key 2024-04-03 13:18:18 +01:00
TPN
6a72ec3481 add dropload, filelions and vtube 2024-04-03 16:45:36 +05:30
TPN
03874e8e24 return thumbnailTracks and add ip-locked flag to mixdrop 2024-04-03 10:51:02 +00:00
Jorrin
8abc8b96e4 Merge pull request #137 from movie-web/dev
Version 2.2.8
2024-04-02 22:09:13 +02:00
Jorrin
c91974a584 Merge branch 'master' into dev 2024-04-02 17:24:42 +02:00
Jorrin
21879b2230 bump version + changelog 2024-04-02 16:33:29 +02:00
Jorrin
b8286ddf3a Merge pull request #136 from movie-web/fix/export
Fixing package.json export
2024-04-02 16:30:04 +02:00
Jorrin
183ab4db14 Update package.json 2024-04-02 16:06:09 +02:00
Jorrin
bc34b95647 Merge pull request #135 from ztpn/fix-gomovies
Fix gomovies
2024-04-01 18:54:00 +02:00
TPN
ecdc3a670e disable gomovies 2024-04-01 16:13:39 +00:00
TPN
32e8bd39fa more changes 2024-04-01 15:46:45 +00:00
TPN
f6871f8c64 disabled upcloud and vidcloud 2024-04-01 14:38:02 +00:00
TPN
45a3f2835d fix mixdrop 2024-04-01 14:35:40 +00:00
TPN
641821164c make required changes 2024-04-01 14:06:51 +00:00
TPN
c22608a4d6 remove package-lock.json 2024-04-01 13:22:16 +00:00
TPN
38d4c97032 use thumnails from vidplay 2024-04-01 12:48:35 +00:00
TPN
7e5d06434d fix filemoon 2024-04-01 12:00:36 +00:00
TPN
1e66b0b7e3 fix mixdrop 2024-04-01 11:56:55 +00:00
TPN
b804af5ab6 enable gomovies 2024-04-01 11:40:45 +00:00
TPN
e58b6b64c2 fix gomovies 2024-04-01 11:39:12 +00:00
William Oldham
0e941923d5 Merge pull request #134 from movie-web/dev
Version 2.2.7: Fixing showbox once again
2024-03-31 23:28:53 +01:00
Jorrin
9119c0675e bump version and changelog 2024-04-01 00:27:10 +02:00
William Oldham
f7bddecdd0 Merge pull request #133 from movie-web/fix/showbox
Fix showbox
2024-03-31 23:26:01 +01:00
Jorrin
090c892bda woops we also need that check here 2024-04-01 00:18:42 +02:00
Jorrin
77540229a0 Update valid.ts 2024-04-01 00:13:04 +02:00
Jorrin
32f5df2f95 Merge pull request #132 from movie-web/dev
Version 2.2.6 - Fixing showbox once again
2024-03-31 20:52:38 +02:00
Jorrin
84105c3ac2 upgrade pnpm/action-setup 2024-03-31 20:48:09 +02:00
Jorrin
12404cd25f Merge branch 'dev' of https://github.com/movie-web/providers into dev 2024-03-31 20:31:40 +02:00
Jorrin
5caae7ac18 add changelog, bump version 2024-03-31 20:31:33 +02:00
Jorrin
d6bd15e2b4 Merge pull request #131 from movie-web/feature/check-for-playable-streams
Validate if a stream is actually playable
2024-03-31 20:23:08 +02:00
Jorrin
db4a7e82dd fix scraper crashing if an embed is not playable 2024-03-31 20:02:35 +02:00
Jorrin
423b728595 Merge branch 'dev' into feature/check-for-playable-streams 2024-03-31 19:32:25 +02:00
Jorrin
9258c97de8 only use normal fetcher if its cors-allowed 2024-03-31 19:11:09 +02:00
Jorrin
442b0ea800 Merge pull request #129 from movie-web/dependabot/npm_and_yarn/ip-1.1.9
Bump ip from 1.1.8 to 1.1.9
2024-03-31 18:48:22 +02:00
Jorrin
55f963611c Check if a stream returns a 200 2024-03-31 17:25:07 +02:00
dependabot[bot]
c964c55d33 Bump ip from 1.1.8 to 1.1.9
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-31 14:27:51 +00:00
Jorrin
5e49375daf Merge pull request #126 from movie-web/feature/pnpm
update deps, migrate to pnpm
2024-03-31 16:26:57 +02:00
chaos
46502bf36c Merge branch 'dev' into feature/pnpm 2024-03-31 17:01:14 +03:00
Jorrin
dcfab4bd80 remove comments 2024-03-31 15:57:26 +02:00
Jorrin
d1d7f5cf72 we go again 2024-03-31 15:44:55 +02:00
Jorrin
cce3b810c3 test 2024-03-31 15:41:57 +02:00
Jorrin
761de36e3b Update tests.yml 2024-03-31 15:33:19 +02:00
Jorrin
d6824f1b71 Update startup.mjs 2024-03-31 15:26:33 +02:00
Jorrin
035b2e3ea7 Update startup.mjs 2024-03-31 15:25:30 +02:00
Jorrin
9da234f0ea try with shell instead 2024-03-31 15:21:58 +02:00
Jorrin
1e7523d4b5 fix tests? 2024-03-31 15:19:06 +02:00
Jorrin
f3310e9e8c fix tests hanging 2024-03-31 15:16:54 +02:00
Jorrin
9e5c40ae45 test 2024-03-31 15:08:49 +02:00
chaos
56dc8b113f Merge pull request #127 from lonelil/dev
febbox-mp4: remove removeBadUrlParams
2024-03-31 11:48:34 +03:00
lonelil
92db2c1c3f remove removeBadUrlParams 2024-03-31 16:43:28 +08:00
Jorrin
21f1fd3cee update deps, migrate to pnpm 2024-03-29 21:23:32 +01:00
Jorrin
f8a5120064 Merge pull request #124 from movie-web/dev
2.2.5 Primewire, updated providers
2024-03-29 18:21:09 +01:00
Jorrin
1024467636 Bump version, add changelog and reordered providers 2024-03-29 16:51:57 +01:00
William Oldham
a5bf932299 Merge pull request #125 from zisra/primewire
Fix febbox
2024-03-29 08:27:28 +00:00
zisra
fa30ed299b Merge branch 'movie-web:dev' into primewire 2024-03-28 22:45:32 -05:00
f4d0a7a05e Fix febbox 2024-03-28 22:10:33 -05:00
Jorrin
234209e90b Merge pull request #123 from Ciarands/dev
Fix filemoon
2024-03-28 23:38:27 +01:00
Jorrin
be5ba8f87b Merge branch 'dev' into dev 2024-03-28 23:36:48 +01:00
Jorrin
943c8b81ca Merge pull request #116 from zisra/primewire
Primewire
2024-03-28 23:35:34 +01:00
Jorrin
f7592cb837 Merge branch 'dev' into primewire 2024-03-28 23:32:50 +01:00
Ciarands
9edaea0125 Fix filemoon 2024-03-28 22:32:43 +00:00
Jorrin
acb9531cfe Merge pull request #122 from Ciarands/dev
Disable sources that no longer work
2024-03-28 23:31:22 +01:00
Ciarands
5356d7336c disable zoechip 2024-03-28 21:58:21 +00:00
Ciarands
35721b553b disable smashystream 2024-03-28 21:57:32 +00:00
Ciarands
e6210b5d93 disable nepu 2024-03-28 21:56:13 +00:00
Ciarands
3b29a685cc disable goojara 2024-03-28 21:53:53 +00:00
Ciarands
d5d735d089 disable gomovies 2024-03-28 21:51:12 +00:00
Ciarands
4f490dd8f1 disable flixhq 2024-03-28 21:50:36 +00:00
f1b962dd5d Use proxy & reorder 2024-03-28 15:57:17 -05:00
a03fe2825a Capital name 2024-03-28 00:11:03 -05:00
3e3bd5d719 Unknown quality check 2024-03-28 00:08:34 -05:00
8063c5f245 Streamtape scraper 2024-03-28 00:07:43 -05:00
c8f32145fc Fix dood 2024-03-27 23:43:23 -05:00
22766ca82a Fixes 2024-03-27 21:45:23 -05:00
b505336882 Add streamvid + voe 2024-03-27 14:40:17 -05:00
zisra
ca70828efd Merge branch 'movie-web:dev' into primewire 2024-03-26 16:15:55 -05:00
Jorrin
d3363eafde Merge pull request #121 from lonelil/dev
vidsrcto: use tmdbId when imdbId is not provided
2024-03-26 20:35:08 +01:00
lonelil
e381465d53 vidsrcto: use tmdbId when imdbId is not provided 2024-03-27 02:54:20 +08:00
Jorrin
7b1a7b7ef5 Merge branch 'master' into dev 2024-03-23 22:34:47 +01:00
Jorrin
c686467b11 Merge pull request #120 from erynith/patch-1
Disable remotestream
2024-03-23 22:28:26 +01:00
erynith
33f6fe5953 Disable remotestream 2024-03-23 17:26:25 -04:00
William Oldham
3d5ce44aa3 Stop ignoring console errors 2024-03-19 17:17:42 +00:00
William Oldham
28be597b71 Merge pull request #118 from movie-web/dev
2.2.4 - Remove Console Log
2024-03-19 17:11:39 +00:00
William Oldham
83e18900a9 Bump version 2024-03-19 17:01:57 +00:00
Jorrin
264302af76 Merge pull request #117 from TecEash1/patch-1
Remove url logging
2024-03-17 17:00:06 +01:00
TechyGiraffe999
d5b0da5659 Remove url logging 2024-03-17 15:53:18 +00:00
zisra
c5e87669bf Merge branch 'dev' into primewire 2024-03-15 19:09:44 -05:00
fae92715b5 Fix all lint errors 2024-03-15 11:24:39 -05:00
af443bff96 Fix more errors 2024-03-14 23:39:44 -05:00
9a920f86d2 Fix package.json 2024-03-14 23:30:18 -05:00
1fd98debc6 Primewire 2024-03-14 23:28:44 -05:00
Jorrin
f066fe1371 Merge pull request #114 from movie-web/dev
Version 2.2.3
2024-03-14 22:54:50 +01:00
Jorrin
6bc765caed Merge pull request #105 from capak07/fix227
Updated compare function to check a edge case
2024-03-14 21:52:21 +01:00
Jorrin
baecc3f6eb Merge branch 'dev' into fix227 2024-03-14 21:44:56 +01:00
Jorrin
10f07f8282 Merge pull request #115 from movie-web/dependabot/npm_and_yarn/dot-docs/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6 in /.docs
2024-03-14 21:44:34 +01:00
Jorrin
7a2c255ea8 prettier fix 2024-03-14 21:44:00 +01:00
dependabot[bot]
eefa501938 Bump follow-redirects from 1.15.4 to 1.15.6 in /.docs
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-14 20:43:17 +00:00
Jorrin
54e54261f6 bump version and changelog 2024-03-14 21:33:59 +01:00
William Oldham
35d48c192c Merge branch 'dev' into fix227 2024-03-14 20:31:57 +00:00
Jorrin
5ae85a500d Merge pull request #112 from Paradox-77/dev
Fix embedUrl matching for Vidplay and Filemoon sources
2024-03-14 20:58:10 +01:00
Jorrin
d776069613 fix build 2024-03-14 20:51:49 +01:00
Jorrin
db4a27e51a add cors allowed flags 2024-03-14 20:45:40 +01:00
Paradox-77
44686f76de Fixed vidplay subtitle oversight and refactored filemoon url to use vidplay's subtitles 2024-03-15 00:41:32 +05:30
Paradox-77
bd0f1e2ddc Refactored Source to Url association, no longer breaks if domain changes 2024-03-15 00:18:30 +05:30
Paradox-77
fed8678ef1 Fix embedUrl matching for Vidplay and Filemoon sources 2024-03-14 22:45:19 +05:30
Jorrin
147884a74c Merge pull request #111 from lonelil/dev
rezka: check for translator
2024-03-13 01:36:25 +01:00
lonelil
afb261717d complete requested changes 2024-03-13 08:34:23 +08:00
lonelil
59e1700ea1 rezka: check for translator 2024-03-13 08:17:36 +08:00
Jorrin
f9cc1f6bf6 Merge pull request #110 from movie-web/feature/hdrezka
Add hdrezka
2024-03-13 00:36:19 +01:00
Jorrin
e1a0ce72cc add IP_LOCKED flag 2024-03-13 00:30:02 +01:00
Jorrin
845bf7c3db Update types.ts 2024-03-13 00:27:49 +01:00
Jorrin
dd3942488c cleanup 2024-03-13 00:19:21 +01:00
Jorrin
af9d9c17cd add hdrezka 2024-03-13 00:12:18 +01:00
William Oldham
c348686dc2 Merge pull request #108 from movie-web/fix/#997
Check if dood returns a valid url
2024-03-11 14:38:57 +00:00
Jorrin
284ad4d222 check if dood returns a valid url 2024-03-11 14:49:21 +01:00
Jorrin
2aa7585b79 Merge pull request #107 from movie-web/fix/improve-rn-docs
docs: Add setup steps to React native
2024-03-07 23:46:31 +01:00
Jorrin
85ef0d1628 Merge branch 'dev' into fix/improve-rn-docs 2024-03-07 23:44:59 +01:00
Jorrin
853da85d41 Add setup steps to React native 2024-03-07 23:40:40 +01:00
capak07
bbd88fd966 Fixed if condition 2024-03-07 13:20:19 -04:00
William Oldham
2715ca5cb3 Merge pull request #106 from vijaysingh2219/dev
Refactor for readability and simplicity.
2024-03-07 16:10:30 +00:00
Vijay
284226166e Modified the try-catch block 2024-03-07 21:32:46 +05:30
Vijay
da7c83774c Refactor for readability and simplicity. 2024-03-06 15:40:53 +05:30
capak07
bbb39a3aa7 Addressed comments 2024-03-04 21:30:02 -04:00
capak07
8a3155999b Updated compare function to check a edge case 2024-03-04 18:16:33 -04:00
William Oldham
1e63402914 Merge pull request #102 from movie-web/dev
Version 2.2.2
2024-02-29 23:09:11 +00:00
Jorrin
89ae529dd1 Merge branch 'dev' of https://github.com/movie-web/providers into dev 2024-03-01 00:04:32 +01:00
William Oldham
02d8e475f2 Update discord link in SECURITY.MD 2024-02-29 23:04:31 +00:00
Jorrin
d89b189b9e add one more fix in the changelog 2024-03-01 00:04:30 +01:00
Jorrin
868e96e707 Merge branch 'master' into dev 2024-03-01 00:00:38 +01:00
Jorrin
70f70475df bump version + changelog 2024-02-29 23:59:50 +01:00
William Oldham
9e4e06b4c2 Merge pull request #103 from movie-web/fix/revert-urlsearchparams
Fix URLSearchParams usage for react-native
2024-02-29 22:12:55 +00:00
Jorrin
b0da041ba9 set header for urlsearchparams request 2024-02-29 23:11:11 +01:00
Jorrin
36d4b41baa Fix URLSearchParams usage for react-native 2024-02-29 22:44:38 +01:00
William Oldham
c735ce33a0 Merge pull request #100 from thehairy/dev
Fix showbox scraper for native app
2024-02-29 13:12:03 +00:00
William Oldham
6ae186f436 Merge branch 'dev' into dev 2024-02-29 13:10:36 +00:00
William Oldham
f97c907720 Merge pull request #88 from Joristdh/fix-upcloud-captions
Fix missing captions from UpCloud/VidCloud embeds
2024-02-29 13:10:22 +00:00
William Oldham
9bbd0c0976 Merge branch 'dev' into fix-upcloud-captions 2024-02-29 13:05:26 +00:00
William Oldham
e4e56dd7b1 Merge pull request #101 from movie-web/fix/ridomovies
fix ridomovies not working for some shows and movies
2024-02-29 12:59:18 +00:00
Jorrin
b2f372fbb9 fix ridomovies not working for some sources 2024-02-28 00:02:21 +01:00
thehairy
f47a2b0f40 move showbox fix to util function 2024-02-27 22:26:43 +01:00
thehairy
85cb751542 Update sendRequest.ts 2024-02-27 21:16:32 +01:00
William Oldham
f8f0d4696a Merge pull request #99 from TheScreechingBagel/patch-1
More old domain mentions
2024-02-26 09:24:43 +00:00
Screeching Bagel
5df9123edb Update package.json (#3)
anotherr mention
2024-02-26 01:19:48 -08:00
Screeching Bagel
b5dcd7d133 Update 0.introduction.md (#2)
another mention but i did take some liberty with that one
2024-02-26 01:18:53 -08:00
Screeching Bagel
ad2ee21c00 Update SECURITY.md (#1)
updated the discord link, but the rest needs more thought
2024-02-26 01:17:28 -08:00
Screeching Bagel
e0bb7d5a30 Update 0.usage-on-x.md
another mention
2024-02-26 01:02:48 -08:00
William Oldham
bc5a1893e7 Merge pull request #98 from TheScreechingBagel/patch-1
fix docs link
2024-02-26 08:49:25 +00:00
Screeching Bagel
b10cfd9132 Merge branch 'movie-web:dev' into patch-1 2024-02-26 00:45:51 -08:00
William Oldham
01fcb392fd Merge pull request #89 from MemeCornucopia/fix-goojara
Fix Goojara Scraping and Capitalization of Id
2024-02-26 08:30:57 +00:00
Screeching Bagel
66b27ef21e update docs links in README.md
updated to https://movie-web.github.io/providers/
2024-02-25 17:33:53 -08:00
William Oldham
fb6cf96d12 Merge pull request #97 from movie-web/dev
Version 2.2.1 - Closeload and Docs update
2024-02-23 08:43:55 +00:00
William Oldham
66b9004af9 Update 4.changelog.md 2024-02-23 08:42:13 +00:00
William Oldham
7ebf23c3d3 Merge pull request #96 from movie-web/dependabot/npm_and_yarn/ip-1.1.9
Bump ip from 1.1.8 to 1.1.9
2024-02-23 08:41:18 +00:00
William Oldham
09bde8b82c Merge pull request #95 from movie-web/dependabot/npm_and_yarn/dot-docs/ip-2.0.1
Bump ip from 2.0.0 to 2.0.1 in /.docs
2024-02-23 08:41:03 +00:00
William Oldham
8457a85ca0 Merge pull request #94 from movie-web/dependabot/npm_and_yarn/dot-docs/undici-5.28.3
Bump undici from 5.27.2 to 5.28.3 in /.docs
2024-02-23 08:40:51 +00:00
William Oldham
6445906453 Bump version 2024-02-23 08:40:28 +00:00
William Oldham
5a937abcaf Update base path of docs 2024-02-23 08:38:14 +00:00
dependabot[bot]
234c5108ac Bump ip from 1.1.8 to 1.1.9
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 03:03:12 +00:00
dependabot[bot]
8a39e6ae59 Bump ip from 2.0.0 to 2.0.1 in /.docs
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 01:39:02 +00:00
MemeCornucopia
4329bf9995 Merge branch 'dev' into fix-goojara 2024-02-17 11:41:50 -05:00
memecornucopia
8e4a87421c Final Fixes 2024-02-17 16:41:19 -05:00
dependabot[bot]
5161c6ed86 Bump undici from 5.27.2 to 5.28.3 in /.docs
Bumps [undici](https://github.com/nodejs/undici) from 5.27.2 to 5.28.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.27.2...v5.28.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-16 18:20:42 +00:00
mrjvs
5078c176ec Merge pull request #93 from movie-web/fix/closeload
Fix closeload scraper
2024-02-13 19:56:41 +01:00
Jorrin
2c34396897 fix closeload scraper 2024-02-13 18:34:29 +01:00
Joris te Dorsthorst
b34e2fb619 Merge branch 'dev' into fix-upcloud-captions 2024-02-11 12:23:08 +01:00
mrjvs
2201ff5822 Merge pull request #92 from movie-web/dev
Version 2.2
2024-02-10 21:12:32 +01:00
William Oldham
805e520f82 Add 2.2 changelog and re-order sources 2024-02-10 19:23:30 +00:00
William Oldham
311eefb74f Update package-lock.json 2024-02-10 19:12:00 +00:00
William Oldham
8a5392f08b Bump version to 2.2 2024-02-10 17:48:51 +00:00
William Oldham
c4e2fcbd5e Merge branch 'master' into dev 2024-02-10 17:45:44 +00:00
William Oldham
822b7a3965 Merge pull request #91 from movie-web/thumbnail-tracks
Add thumbnail track option to response
2024-02-10 17:44:43 +00:00
mrjvs
f4bc3831e1 Add thumbnail track option to response 2024-02-07 19:24:41 +01:00
Joris te Dorsthorst
0dafb434d4 Merge branch 'dev' into fix-upcloud-captions 2024-02-05 14:10:30 +01:00
mrjvs
06dcde8036 Merge pull request #90 from movie-web/fix/vidplay-cached-keys
Fix Vidplay cached keys
2024-02-03 14:25:54 +01:00
Jorrin
16dcca270c that should be proxied obviously 2024-02-03 02:29:09 +01:00
Jorrin
fe90bdaa9a fix keys being cached for 5 minutes 2024-02-03 01:28:37 +01:00
memecornucopia
ac2261bbd1 Fix Wootly, Fix Goojara, Fix Id. 2024-02-01 14:34:11 -05:00
memecornucopia
ccb029db1d Fix Goojara Scraping and Capitilization 2024-02-01 12:19:28 -05:00
Joris te Dorsthorst
69da27d6dc Strip native language suffix from caption label 2024-02-01 11:19:02 +01:00
mrjvs
28b15e5a36 Merge pull request #73 from movie-web/vidcloud
Add vidcloud to flixhq and zoechip
2024-01-31 17:14:24 +01:00
mrjvs
59dfc22c95 Merge branch 'dev' into vidcloud 2024-01-31 16:55:51 +01:00
mrjvs
9ee1ca7cae Merge pull request #84 from lonelil/dev
Add Nepu Provider
2024-01-31 16:53:23 +01:00
mrjvs
96a455c4c2 fix linting 2024-01-31 16:50:49 +01:00
mrjvs
e2aa6e2bc0 Merge branch 'dev' into dev 2024-01-31 16:40:08 +01:00
lonelil
cb44f663ca Change embed selector 2024-01-31 23:39:44 +08:00
mrjvs
07a862d78a Merge pull request #86 from movie-web/fix/vidsrcto-without-subtitles
Fixed VidSrcTo crashing if no subtitles are found
2024-01-30 20:51:05 +01:00
lonelil
fc052a9f08 re-add headers 2024-01-31 03:38:30 +08:00
Jorrin
f683cbf8ac Fixed VidSrcTo crashing if no subtitles are found 2024-01-30 19:56:58 +01:00
mrjvs
d5e0f0bf81 Merge pull request #76 from MemeCornucopia/goojara.to
Adding the most epic source ever Goojara.to
2024-01-30 19:31:30 +01:00
mrjvs
7061e808ba Move cookie file 2024-01-30 19:13:23 +01:00
lonelil
a30881cf5d Add requested changes 2024-01-30 07:31:43 +08:00
MemeCornucopia
8b149458e0 Use cookie and set-cookie-parser 2024-01-29 13:43:32 -05:00
MemeCornucopia
c8ad3387c5 Added Cookie.ts. Changed request changes. 2024-01-29 13:32:21 -05:00
lonelil
bd1e3234d1 fix linting issues 2024-01-29 21:54:47 +08:00
lonelil
43faeec1e7 add searchResult filtering 2024-01-29 20:58:46 +08:00
lonelil
c423a51b4c add nepu provider 2024-01-29 20:36:36 +08:00
MemeCornucopia
46253bad0c Fixed linting errors 2024-01-28 15:49:10 -05:00
MemeCornucopia
53f4d6699a Added reasoning 2024-01-28 15:44:09 -05:00
MemeCornucopia
63bbd8b858 All changes done 2024-01-28 15:41:40 -05:00
MemeCornucopia
1d4c556d78 Merge branch 'dev' into goojara.to 2024-01-28 15:28:04 -05:00
mrjvs
ff29bc3299 Merge pull request #52 from JorrinKievit/ridomovies
Added ridomovies with Ridoo and Closeload embed
2024-01-28 21:23:13 +01:00
MemeCornucopia
d6624efc5a Added Base URL Declaration 2024-01-28 14:27:07 -05:00
MemeCornucopia
5edd540a50 Merge branch 'movie-web:dev' into goojara.to 2024-01-27 19:30:32 -05:00
MemeCornucopia
6580e3b1a8 Merge branch 'goojara.to' of https://github.com/MemeCornucopia/providers-contribution into goojara.to 2024-01-27 19:30:20 -05:00
MemeCornucopia
fb12c6bfa9 Changes requests by jvs 2024-01-27 19:30:18 -05:00
Jorrin
03fc42fa9c Merge branch 'dev' into vidcloud 2024-01-28 00:45:36 +01:00
Jorrin
8247ad6ef0 Merge branch 'dev' into pr/52 2024-01-28 00:34:15 +01:00
Jorrin
25dbadd909 rename 2024-01-28 00:33:33 +01:00
Jorrin
c6c921f80b add cors restriction to caption, add preferredHeaders 2024-01-28 00:29:52 +01:00
Jip Frijlink
96b090a033 Merge pull request #83 from Ciarands/dev
Fix vidsrc.me url decoding
2024-01-27 23:39:18 +01:00
Ciarands
4591bcbc2e Fix vidsrc.me base64 decoding 2024-01-26 23:16:37 +00:00
William Oldham
d321f5a5b6 Merge pull request #82 from movie-web/dev
Providers v2.1.1
2024-01-24 19:10:48 +00:00
mrjvs
3c2e3bc076 Update 4.changelog.md 2024-01-24 20:08:43 +01:00
mrjvs
e243b27704 Merge branch 'master' into dev 2024-01-24 20:07:07 +01:00
mrjvs
89c7f49be2 Update package.json 2024-01-24 20:06:08 +01:00
mrjvs
12b2a071e3 Merge pull request #81 from movie-web/fix/vidplay
replace vidplay decryption keys and domain
2024-01-24 19:17:06 +01:00
Jorrin
5adca068a8 replace vidplay decryption keys and domain
- Massive thanks to @Ciarands!
2024-01-24 17:32:34 +01:00
MemeCornucopia
289d072582 Merge branch 'movie-web:dev' into goojara.to 2024-01-20 23:10:32 -05:00
William Oldham
462905a342 Merge pull request #80 from movie-web/dev
v2.1.0 provider lib
2024-01-20 17:07:09 +00:00
mrjvs
65fb0eb862 Merge branch 'master' into dev 2024-01-20 18:04:38 +01:00
William Oldham
f6061f6945 Merge pull request #79 from movie-web/versionbump
Version bump + lookmovie reorder
2024-01-20 17:04:28 +00:00
mrjvs
e5d9a3b23c add a new line to changelog 2024-01-20 18:03:39 +01:00
mrjvs
09cfafcaa2 update changelog 2024-01-20 18:02:49 +01:00
mrjvs
2df4f10f64 Version bump + lookmovie reorder 2024-01-20 17:54:44 +01:00
William Oldham
54db7efcee Merge pull request #74 from movie-web/provider-tests
Provider unit tests
2024-01-20 16:50:50 +00:00
William Oldham
cc880e9da2 Merge branch 'dev' into provider-tests 2024-01-20 16:49:13 +00:00
William Oldham
a21ec86cb2 Merge pull request #71 from movie-web/extension
Extension support for providers
2024-01-20 16:49:04 +00:00
William Oldham
f77144808f Merge branch 'dev' into extension 2024-01-20 16:47:42 +00:00
mrjvs
f9670bf3c6 Merge branch 'dev' into provider-tests 2024-01-20 17:46:54 +01:00
William Oldham
2834b1eff2 Merge pull request #75 from movie-web/vidsrc-decoding-fix
VidSrc: Remove equal signs inside base64 encoded string
2024-01-20 16:45:52 +00:00
mrjvs
889bb88fac Merge branch 'dev' into vidsrc-decoding-fix 2024-01-20 15:15:51 +01:00
mrjvs
a22537ad66 Merge branch 'dev' into extension 2024-01-20 15:14:54 +01:00
mrjvs
3dddfd7678 Merge branch 'dev' into extension 2024-01-20 15:14:37 +01:00
mrjvs
66e59b36ae Merge pull request #78 from movie-web/dependabot/npm_and_yarn/vite-4.5.2
Bump vite from 4.5.1 to 4.5.2
2024-01-20 15:09:32 +01:00
mrjvs
4be3e60bc8 Merge branch 'dev' into dependabot/npm_and_yarn/vite-4.5.2 2024-01-20 15:09:24 +01:00
mrjvs
3b00511eb3 Merge pull request #77 from movie-web/dependabot/npm_and_yarn/dot-docs/vite-4.5.2
Bump vite from 4.5.1 to 4.5.2 in /.docs
2024-01-20 15:09:10 +01:00
mrjvs
f2073e89d9 Fix remotestream and linting error 2024-01-20 14:18:44 +01:00
dependabot[bot]
efa4857b20 Bump vite from 4.5.1 to 4.5.2
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-20 05:53:00 +00:00
dependabot[bot]
ec4f2d02ed Bump vite from 4.5.1 to 4.5.2 in /.docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.2/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-20 05:46:23 +00:00
MemeCornucopia
5eb3ade82e deleted env copy xd 2024-01-19 15:48:35 -05:00
MemeCornucopia
0396272dd9 more prettier stuff 2024-01-19 15:29:11 -05:00
MemeCornucopia
9291bd6828 more prettier crap 2024-01-19 15:27:37 -05:00
MemeCornucopia
e11885a1df fixed imports 2024-01-19 15:26:14 -05:00
MemeCornucopia
bcb1d29218 OMG GOOJARA!?!?!?!?!??! EPIC SOURCE!?!?!?!? 2024-01-19 15:19:38 -05:00
MemeCornucopia
198dfdec87 EPIC GOOJARA LESS FUCKING GO 2024-01-19 15:08:29 -05:00
Jorrin
b13578cefa improve regex 2024-01-19 20:13:44 +01:00
Jorrin
f3e4786b72 Remove equal signs inside base64 encoded string 2024-01-19 20:04:50 +01:00
mrjvs
e8a8909ecb Add better embed testing + add everything 2024-01-17 21:15:56 +01:00
mrjvs
949fe487bc fix some stuff 2024-01-17 19:22:37 +01:00
mrjvs
03a628ea34 Improve logging of provider unit tests 2024-01-17 18:54:42 +01:00
mrjvs
616c8a1e1b Add embed testing 2024-01-17 18:34:32 +01:00
mrjvs
327ba025c2 Enabled febbox subtitles 2024-01-17 18:18:08 +01:00
mrjvs
528e2774b5 Add basic provider unit testing 2024-01-17 18:03:10 +01:00
mrjvs
9798659af8 add basic categories to tests 2024-01-17 17:18:12 +01:00
Jorrin
8819648023 add vidcloud to flixhq and zoechip 2024-01-15 19:27:36 +01:00
mrjvs
b3212bd327 Merge pull request #72 from movie-web/dependabot/npm_and_yarn/dot-docs/follow-redirects-1.15.4
Bump follow-redirects from 1.15.2 to 1.15.4 in /.docs
2024-01-11 19:19:28 +01:00
mrjvs
7a5cbb3428 Add CF_BLOCKED flags 2024-01-11 19:14:21 +01:00
dependabot[bot]
854dec111d Bump follow-redirects from 1.15.2 to 1.15.4 in /.docs
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 00:39:32 +00:00
Jorrin
7697c76721 add headers 2024-01-10 21:21:47 +01:00
Jorrin
1dc7c879af add preferredHeaders to vidplay 2024-01-08 16:27:59 +01:00
William Oldham
e1f23f571d Merge pull request #70 from movie-web/fix/included-disabled-embeds
Add disabled embed filter to scrapeInvidualSource and runAllProviders
2024-01-07 00:35:13 +00:00
Jorrin
6212a014a0 filter out disabled embeds in runAll 2024-01-07 01:12:17 +01:00
Jorrin
b5b2d341f9 add disabled embed filter 2024-01-07 00:42:32 +01:00
William Oldham
75613df6c5 Merge pull request #69 from movie-web/dev
V2.0.5 - disable febbox subs
2024-01-06 20:56:59 +00:00
mrjvs
53ed51013c Merge branch 'master' into dev 2024-01-06 21:56:53 +01:00
mrjvs
777f3d78e0 Merge pull request #68 from movie-web/disable-febbox-subs
Disable febbox subs
2024-01-06 21:56:24 +01:00
mrjvs
c3bd575621 Update docs 2024-01-06 21:53:09 +01:00
mrjvs
48b3fcdd17 Bump version and update febbox mp4 to not have subs as endpoint broke 2024-01-06 21:52:01 +01:00
William Oldham
f427173e87 Merge pull request #67 from movie-web/dev
Version 2.0.4: Providers and Fixes™️
2024-01-06 15:01:40 +00:00
William Oldham
c7dff57a40 Update changelog to 2.0.4 2024-01-06 14:59:54 +00:00
William Oldham
6d3db3269a Merge branch 'master' into dev 2024-01-06 14:54:55 +00:00
William Oldham
5ef048c28f Bump Version 2024-01-06 14:49:09 +00:00
William Oldham
efe5b6d67b Merge pull request #56 from systemcrash/spfix
spelling/grammar
2024-01-06 14:47:28 +00:00
William Oldham
30fdfcc9af Merge branch 'dev' into spfix 2024-01-06 14:45:52 +00:00
William Oldham
0de21c17c6 Merge pull request #66 from movie-web/fix-showbox-useragent
Fix showbox + add support for new simple-proxy version
2024-01-06 14:44:00 +00:00
William Oldham
e728f8c211 Merge branch 'dev' into fix-showbox-useragent 2024-01-06 14:41:31 +00:00
William Oldham
48a3f3ad1e Merge pull request #50 from JorrinKievit/vidsrcto
VidSrcTo with Vidplay and Filemoon embed
2024-01-06 14:41:22 +00:00
William Oldham
78c269a882 Merge branch 'dev' into fix-showbox-useragent 2024-01-06 14:40:59 +00:00
William Oldham
c91e18ec91 Merge branch 'dev' into vidsrcto 2024-01-06 14:39:43 +00:00
Jorrin
f67d82200f Merge branch 'dev' into ridomovies 2024-01-05 23:54:10 +01:00
Jorrin
7e1c897e30 fix ranks and feedback 2024-01-05 21:23:32 +01:00
mrjvs
d4e2d234f3 Merge pull request #63 from movie-web/captions-update
Fix LookMovie caption codes, create removeDuplicateLanguages util
2024-01-05 20:05:48 +01:00
mrjvs
0ba1183e34 Merge branch 'dev' into vidsrcto 2024-01-05 20:05:12 +01:00
Jorrin
9276694ead remove flag 2024-01-05 19:52:28 +01:00
mrjvs
e210d39a39 Merge branch 'dev' into ridomovies 2024-01-05 19:49:42 +01:00
mrjvs
4b08c0101d Merge branch 'dev' into captions-update 2024-01-05 19:41:35 +01:00
mrjvs
a2b5aeecfc Merge pull request #61 from erynith/dev
Fix remotestream base
2024-01-05 19:37:52 +01:00
mrjvs
d49c408197 Fix remotestream for real 2024-01-05 19:36:26 +01:00
mrjvs
39a6082a19 Merge branch 'dev' into dev 2024-01-05 19:34:42 +01:00
mrjvs
5199213a70 Merge pull request #47 from JorrinKievit/pr-14-v2
Added VidSrc and StreamBucket Part 2
2024-01-05 19:14:56 +01:00
mrjvs
4e4b405398 Merge branch 'dev' into pr-14-v2 2024-01-05 19:12:33 +01:00
Jorrin
1970e11443 applied feedback from ciarands 2024-01-05 17:01:54 +01:00
Jorrin
605b9d78d1 filemoon added CORS 2024-01-05 16:12:19 +01:00
Jorrin
fe1f8d364b reintroduce the password endpoint 2024-01-05 14:51:54 +01:00
Jorrin
7b5c53e6bd browser compatible decodeSrc function 2024-01-04 22:35:02 +01:00
Jorrin
2f892eec7b remove ip lock from ridoo 2024-01-04 21:21:18 +01:00
mrjvs
a694230bee Add another simple-proxy header 2024-01-04 21:20:14 +01:00
mrjvs
f1c29aab90 Fix showbox + add support for new simple-proxy version 2024-01-04 21:11:24 +01:00
Jorrin
d7134d4daf proxied fetcher 2024-01-04 20:18:25 +01:00
Jorrin
cc400aebb7 ip locked ridoo 2024-01-03 14:02:08 +01:00
Jorrin
69e66ab855 proxied fetcher 2024-01-03 13:55:11 +01:00
William Oldham
ae440b1620 Merge pull request #65 from movie-web/dev
Update
2024-01-02 21:31:34 +00:00
William Oldham
1d2dbcf903 Merge branch 'master' into dev 2024-01-02 21:31:24 +00:00
mrjvs
0d36d51dd4 asdfua 2024-01-02 22:31:08 +01:00
William Oldham
c38c5c834a Merge pull request #64 from movie-web/fixasdgasdg
Update
2024-01-02 21:30:29 +00:00
mrjvs
cdb59c6046 Fix sujtasdfASDFG 2024-01-02 22:29:14 +01:00
Jip Fr
7ea4ac0c48 Fix LookMovie caption codes, create removeDuplicateLanguages util 2024-01-02 20:54:57 +01:00
mrjvs
570cb63c52 Merge pull request #60 from movie-web/dev
Providers update
2024-01-02 20:37:37 +01:00
mrjvs
7dfeeb2700 Fix CLI not working for ip locked sources + disable lookmovie due to bug 2024-01-02 20:33:43 +01:00
mrjvs
2ac9be5fdc Merge branch 'master' into dev 2024-01-02 20:29:28 +01:00
mrjvs
147029f123 Merge pull request #62 from movie-web/remove-febbox-hls
remove febbox HLS, it doesnt work
2024-01-02 20:23:47 +01:00
mrjvs
7f4e412b9d Remove binary 2024-01-02 20:22:06 +01:00
mrjvs
10eb0cc8f6 Update codeowner file 2024-01-02 20:21:55 +01:00
mrjvs
98230470f1 remove febbox HLS, it doesnt work 2024-01-02 20:12:28 +01:00
erynith
27e1ae7e0b Fix remotestream base 2024-01-02 13:54:13 -05:00
Paul Dee
1296915a42 spelling/grammar 2024-01-01 23:51:13 +00:00
Jip Frijlink
34a2dd2ec5 Increase package version 2024-01-01 22:06:00 +01:00
mrjvs
239c0cc7e8 Merge pull request #59 from movie-web/fix-febbox-dupe-subs
Fix language showing up more than once
2024-01-01 19:03:02 +01:00
mrjvs
2dd19da7eb Merge pull request #57 from lonelil/dev
Remove CORS Allowed flag for febbox hls
2024-01-01 19:02:43 +01:00
mrjvs
db17675ebe Merge branch 'dev' into fix-febbox-dupe-subs 2024-01-01 18:53:15 +01:00
Jip Fr
2e9c1b4baa Fix language showing up more than once 2024-01-01 18:20:50 +01:00
lonelil
7cce9a9e5a Merge branch 'dev' into dev 2024-01-01 07:02:34 +09:00
Jip Frijlink
00cd55402c Merge pull request #58 from iMahir/dev
feat(lookmovie): add captions support
2023-12-31 18:55:02 +01:00
lonelil
79181ff87d Remove CORS flag for febbox hls 2024-01-01 01:48:19 +09:00
Mahir
67d936f58c feat(lookmovie): add captions support 2023-12-31 21:26:27 +05:30
Jorrin
a07f54e0cf add credits to decryption code 2023-12-29 22:03:54 +01:00
mrjvs
b0df27f41b Merge pull request #54 from movie-web/dev
Providers v2.0.1
2023-12-29 21:04:58 +01:00
mrjvs
ac3dfb98e1 Update src/runners/runner.ts
Co-authored-by: Jip Frijlink <jipfrijlink@gmail.com>
2023-12-29 21:01:18 +01:00
mrjvs
336859f359 Merge branch 'master' into dev 2023-12-29 20:52:01 +01:00
mrjvs
3c072f0d11 Update docs 2023-12-29 20:46:03 +01:00
William Oldham
6233bbcdcb Merge pull request #53 from movie-web/fix-febbox-qualities
Fix febbox qualities
2023-12-29 19:36:36 +00:00
mrjvs
9b7314321e Fix 4k for febbox 2023-12-29 20:32:24 +01:00
Jorrin
d49ebf00d2 Merge branch 'dev' into ridomovies 2023-12-29 19:33:57 +01:00
Jorrin
f88021fff6 added ridomovies 2023-12-29 19:24:34 +01:00
mrjvs
81668fe9e7 Fix febbox qualities 2023-12-29 18:33:58 +01:00
mrjvs
a775a5a92b Merge pull request #51 from movie-web/dev
Version 2.0.0
2023-12-29 18:06:08 +01:00
mrjvs
5058a1edb5 Merge pull request #48 from movie-web/revamp-docs
Revamp documentation
2023-12-29 17:55:21 +01:00
mrjvs
eb0f916957 Merge branch 'revamp-docs' of https://github.com/movie-web/providers into revamp-docs 2023-12-29 17:53:40 +01:00
mrjvs
6a071509fc Link to fetcher source code 2023-12-29 17:52:32 +01:00
mrjvs
cb0145d58f Capitalization 2023-12-29 17:51:21 +01:00
mrjvs
cd7368d980 Quotes 2023-12-29 17:47:49 +01:00
mrjvs
9ad12286ae Update .docs/content/2.essentials/4.using-streams.md
Co-authored-by: Jip Frijlink <jipfrijlink@gmail.com>
2023-12-29 17:41:29 +01:00
mrjvs
8589185c53 Update .docs/content/2.essentials/0.usage-on-x.md
Co-authored-by: Jip Frijlink <jipfrijlink@gmail.com>
2023-12-29 17:41:04 +01:00
mrjvs
a1e172125c Remove lock file, wrong package manager 2023-12-29 17:35:09 +01:00
mrjvs
57204d76e1 Bump version 2023-12-29 17:34:25 +01:00
mrjvs
3732f03852 Add changelog 2023-12-29 17:34:09 +01:00
mrjvs
829d87cdd5 Update readme 2023-12-29 17:24:34 +01:00
mrjvs
17662b01b9 Add development CLI to docs 2023-12-29 17:22:09 +01:00
mrjvs
2d68d443ec Redo folder structure 2023-12-29 17:05:01 +01:00
mrjvs
030b512c43 Update navigation titles 2023-12-29 17:03:28 +01:00
mrjvs
8ac11d9067 Write more documentation 2023-12-29 16:59:30 +01:00
Jorrin
9b338b6f3b fixed feedback, added external_ids 2023-12-28 18:39:49 +01:00
mrjvs
8904e87b7e Fix zoechip bug 2023-12-28 01:26:10 +01:00
mrjvs
fb3f237808 Better types for fetchers 2023-12-27 22:33:13 +01:00
mrjvs
c9a611d6b7 Add console logs to browser dev cli 2023-12-27 22:27:37 +01:00
Jorrin
a208aef364 remove Buffer 2023-12-27 21:14:18 +01:00
Jorrin
c44d13f0bd fix ranks 2023-12-27 20:42:22 +01:00
Jorrin
30e6067a3f ranks 2023-12-27 20:36:50 +01:00
Jorrin
f39aaca3e3 fix vidplay, add captions to filemoon 2023-12-27 20:35:08 +01:00
Jorrin
8b7e840814 vidsrcto draft 2023-12-27 02:03:53 +01:00
mrjvs
7c40c8a2c1 Updated icons 2023-12-27 00:28:15 +01:00
mrjvs
68fa20a12c Add more type exports 2023-12-27 00:23:05 +01:00
Jorrin
06acec4675 remove references of embed headers 2023-12-27 00:21:13 +01:00
mrjvs
53b83fe24c Write some pages 2023-12-27 00:15:23 +01:00
mrjvs
582b46cd39 fix type export 2023-12-27 00:15:02 +01:00
mrjvs
7143b06c55 Doc directory structure 2023-12-26 23:35:58 +01:00
Jorrin
2117b417f4 fix ci 2023-12-26 23:23:20 +01:00
Jorrin
65bbf28442 merge with dev 2023-12-26 23:16:26 +01:00
mrjvs
ddf612c6cc fix fetcher on scrapecontext 2023-12-26 23:13:01 +01:00
Jorrin
304ef68c5f Merge branch 'dev' into pr-14-v2 2023-12-26 21:48:32 +01:00
William Oldham
d8f2d0553a Merge pull request #46 from movie-web/pupeteer-testing
Provider builder and a lot of long overdue cleanup
2023-12-26 20:01:05 +00:00
mrjvs
c26e135d74 dev-cli with browser based fetching 2023-12-26 19:13:15 +01:00
mrjvs
75d4b9edcb Seperate scraper code from index cli file 2023-12-26 17:39:40 +01:00
mrjvs
edd08446cf Move runScraper to use the options instead of providers itself 2023-12-26 17:22:23 +01:00
mrjvs
aa4b7cda9e Update readme with new cli command 2023-12-26 17:01:02 +01:00
mrjvs
feddf9c215 Refactored dev-cli 2023-12-26 17:00:35 +01:00
mrjvs
af00bcf7c1 export extra fetcher it is 2023-12-26 16:24:36 +01:00
mrjvs
4b1e8288b8 Add headers and preferred headers to streams 2023-12-26 16:14:10 +01:00
mrjvs
4eaae64e4a Fix fetcher tests 2023-12-26 16:02:25 +01:00
mrjvs
ffe7ae0985 More elaborate fetcher API's 2023-12-26 15:43:52 +01:00
Jorrin
d8d004e4cb Merge branch 'dev' into pr-14-v2 2023-12-25 23:23:45 +01:00
Jorrin
d978e3b3fe Merge branch 'pr/14' into pr-14-v2 2023-12-25 22:52:49 +01:00
Jorrin
64050df350 fix vidsrc 2023-12-25 22:51:55 +01:00
Jorrin
82034b91ed Merge branch 'dev' into pr/14 2023-12-25 22:41:25 +01:00
mrjvs
09ba0ebcc1 Fix browser integration test 2023-12-25 01:18:05 +01:00
mrjvs
4a557b8140 Add builder for adding custom sources 2023-12-25 01:00:19 +01:00
mrjvs
b70d9aaaf7 Make runners compatible with multi stream output 2023-12-25 00:23:21 +01:00
mrjvs
e5989ffbb0 Export more useful types 2023-12-24 19:48:27 +01:00
mrjvs
0affe83d24 Allow embeds and videos to return multiple streams + add identifiers to list returns 2023-12-24 19:46:12 +01:00
mrjvs
d44320e362 Add type checking in build step 2023-12-24 19:45:43 +01:00
mrjvs
a64a80cf12 Rename flags + rename targets + add disallowed section to feature mapping 2023-12-24 19:18:46 +01:00
mrjvs
0fe2fb40e1 Check browser compile compatibility in integration tests 2023-12-24 18:02:56 +01:00
William Oldham
c00ed69801 Merge pull request #45 from movie-web/febbox-hls
Split superstream into showbox and febbox
2023-12-23 22:44:44 +00:00
mrjvs
1a11ccb406 Merge branch 'dev' into febbox-hls 2023-12-23 23:42:31 +01:00
mrjvs
b89602b87e rename to parseInputUrl 2023-12-23 23:41:36 +01:00
mrjvs
1f8dd2f77d Create LICENSE 2023-12-23 23:24:29 +01:00
mrjvs
af6ede4a39 add show support to febbox and add captions 2023-12-23 19:55:28 +01:00
mrjvs
4be2da76ba Split superstream into showbox and febbox. add febbox HLS scraper 2023-12-23 18:57:28 +01:00
William Oldham
0477b876ec Merge pull request #44 from lonelil/dev
Correct function name for runEmbedScraper
2023-12-21 09:08:34 +00:00
lonelil
6597e8f981 Correct function name for runEmbedScraper 2023-12-21 17:01:14 +08:00
mrjvs
c93cc4babc Merge pull request #43 from JorrinKievit/dev
Smashystream scraper
2023-12-18 22:32:43 +01:00
Jorrin
80f3f84b3d Merge branch 'dev' of https://github.com/JorrinKievit/providers into dev 2023-12-18 21:24:00 +01:00
Jorrin
95ce136863 filter captions with invalid language code or caption type 2023-12-18 21:23:59 +01:00
mrjvs
4096109a3e Merge branch 'dev' into dev 2023-12-18 21:12:26 +01:00
Jorrin
cb57e0eef4 pr feedback 2023-12-18 20:48:52 +01:00
mrjvs
9feca64b9e Merge pull request #41 from erynith/dev
fix flixhq show searching
2023-12-18 20:46:23 +01:00
erynith
c711f168ae Update search.ts 2023-12-18 14:41:59 -05:00
erynith
2e3ee28503 Update search.ts 2023-12-18 14:15:16 -05:00
erynith
9ff9cc5938 Update search.ts 2023-12-18 14:02:43 -05:00
erynith
d95de0b602 Update search.ts 2023-12-18 13:36:24 -05:00
erynith
7fa098fb0c Update search.ts 2023-12-18 13:22:46 -05:00
erynith
8c97760dc0 Update search.ts 2023-12-18 13:13:24 -05:00
Jorrin
9cdc9b1fad add smashystream 2023-12-17 16:22:42 +01:00
erynith
2a43934788 Update search.ts 2023-12-16 16:30:11 -05:00
mrjvs
ab2ab028bf Merge pull request #40 from movie-web/dev
providers version 1.1.5
2023-12-15 22:33:55 +01:00
mrjvs
0dc7baeca7 Merge branch 'master' into dev 2023-12-15 22:28:40 +01:00
Jip Frijlink
e4b8c225e1 Merge pull request #39 from movie-web/rerankings
Rerankings
2023-12-15 20:55:36 +01:00
mrjvs
8a278976c3 Version bump 2023-12-15 19:23:28 +01:00
mrjvs
ca6ae98f4d Rerank providers 2023-12-15 19:23:15 +01:00
mrjvs
afe6f8b2ed Merge pull request #31 from MemeCornucopia/lookmovie
Added Lookmovie Source
2023-12-14 20:13:15 +01:00
MemeCornucopia
65aeda6beb Remove json5 dependency 2023-12-14 13:12:09 -05:00
MemeCornucopia
e63d150506 Refactor type definitions and update function signatures 2023-12-14 13:11:17 -05:00
MemeCornucopia
0e47b53bba Fix parameter type in getVideoSources function 2023-12-14 12:43:20 -05:00
MemeCornucopia
7dcd9dd3bb Add IP_LOCKED flag to LookMovie provider 2023-12-14 12:41:12 -05:00
MemeCornucopia
671c558c55 Added IP_LOCKED flag amonst other things requested by mrjvs 2023-12-14 12:40:01 -05:00
MemeCornucopia
f3720161ac Fixed syntaxing more 2023-12-12 20:45:34 -05:00
MemeCornucopia
35d61d19a2 Merge branch 'lookmovie' of https://github.com/MemeCornucopia/providers-lookmovie into lookmovie 2023-12-12 20:12:59 -05:00
MemeCornucopia
1cf837dff7 fixed some syntaxing 2023-12-12 20:12:54 -05:00
MemeCornucopia
65184e07c4 fixed some syntaxing 2023-12-12 20:12:13 -05:00
MemeCornucopia
c68b7db9e7 Merge branch 'dev' into lookmovie 2023-12-07 20:58:18 -05:00
MemeCornucopia
c548796263 Merge branch 'lookmovie' of https://github.com/MemeCornucopia/providers-lookmovie into lookmovie 2023-12-07 20:57:58 -05:00
MemeCornucopia
38cdb1313f All the Changes Requested Complete 2023-12-07 20:53:49 -05:00
mrjvs
163f96c21d Merge pull request #37 from movie-web/dev
v1.1.4
2023-12-07 14:30:41 +01:00
mrjvs
f465ac5984 Merge branch 'master' into dev 2023-12-07 14:29:34 +01:00
mrjvs
34164933eb bump version 2023-12-07 14:28:26 +01:00
mrjvs
b569fad014 Merge pull request #35 from movie-web/dependabot/npm_and_yarn/dot-docs/vite-4.5.1
Bump vite from 4.5.0 to 4.5.1 in /.docs
2023-12-07 14:27:10 +01:00
mrjvs
4ff5878dad Merge branch 'dev' into dependabot/npm_and_yarn/dot-docs/vite-4.5.1 2023-12-07 14:27:01 +01:00
mrjvs
37880f4e74 Merge pull request #36 from elpaxel/patch-1
Superstream captions fix
2023-12-07 14:26:32 +01:00
mrjvs
4dc7179328 Fix linting 2023-12-07 14:22:26 +01:00
elpaxel
44f3318202 Quotes fix 2023-12-07 12:20:15 +02:00
elpaxel
eb14d8d069 Superstream captions fix 2023-12-07 12:01:25 +02:00
dependabot[bot]
c59cc88077 Bump vite from 4.5.0 to 4.5.1 in /.docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.1/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.1/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-06 00:07:26 +00:00
mrjvs
24122ce035 Merge pull request #34 from movie-web/dev
providers v1.1.3
2023-12-04 21:44:01 +01:00
mrjvs
21647f586f Merge branch 'master' into dev 2023-12-04 21:42:10 +01:00
William Oldham
628d8d7f72 Merge pull request #33 from movie-web/superstream-sub-fix
error handling and fixing caching problems
2023-12-02 01:08:40 +00:00
mrjvs
a2c0a7045f bump version 2023-12-02 01:11:18 +01:00
mrjvs
50c3166e70 Fix script caching on upcloud 2023-12-02 01:08:31 +01:00
mrjvs
2f02522ca6 Better error handling for superstream 2023-12-02 00:51:41 +01:00
mrjvs
9324e0c132 Merge branch 'dev' into lookmovie 2023-11-25 15:21:37 +01:00
MemeCornucopia
283b569b7c Finished Lookmovie Scraper 2023-11-21 20:20:31 -05:00
MemeCornucopia
e962bb410f Removed test comment changed search url 2023-11-21 19:36:42 -05:00
MemeCornucopia
36d70fa6fd Add test comment 2023-11-21 19:23:19 -05:00
mrjvs
099993d7f3 Merge branch 'dev' 2023-11-19 16:12:50 +01:00
mrjvs
fee543642e bump version 2023-11-19 16:12:44 +01:00
mrjvs
c7cb8c2d1e Fixed superstream for shows + remove console log + better subtitle selection 2023-11-19 16:12:31 +01:00
mrjvs
4d55c26666 Merge branch 'dev' 2023-11-19 14:48:12 +01:00
mrjvs
af1f7e4964 Bug fix for captions on mp4 streams 2023-11-19 14:48:02 +01:00
William Oldham
be0858b14c Merge branch 'dev' 2023-11-19 00:04:56 +00:00
William Oldham
5af65e6c0d Fix package-lock.json 2023-11-19 00:04:48 +00:00
William Oldham
44342c48c9 Merge pull request #30 from movie-web/dev
Version 1.1.0
2023-11-18 23:58:05 +00:00
mrjvs
fcade4671c Merge branch 'master' into dev 2023-11-19 00:55:16 +01:00
William Oldham
20229a4667 Merge pull request #29 from movie-web/linked-captions
Linked captions
2023-11-18 23:03:11 +00:00
mrjvs
78e0150944 actually a bigger bump is probably better 2023-11-18 23:21:31 +01:00
mrjvs
7d803f9506 bump version 2023-11-18 23:20:11 +01:00
mrjvs
ab5dcc7b42 Subtitle label conversion 2023-11-18 23:14:06 +01:00
mrjvs
fe4882b43e add upcloud captions 2023-11-18 23:00:50 +01:00
mrjvs
4ce41e828d add linked subs to superstream 2023-11-18 22:44:34 +01:00
mrjvs
d31deeb907 introduce captions 2023-11-18 21:26:53 +01:00
mrjvs
3cd023bb10 Merge pull request #28 from movie-web/dependabot/npm_and_yarn/dot-docs/axios-and-nuxt/devtools-1.6.1
Bump axios and @nuxt/devtools in /.docs
2023-11-11 11:53:16 +01:00
dependabot[bot]
9fa6be9b58 Bump axios and @nuxt/devtools in /.docs
Bumps [axios](https://github.com/axios/axios) to 1.6.1 and updates ancestor dependency [@nuxt/devtools](https://github.com/nuxt/devtools/tree/HEAD/packages/devtools). These dependencies need to be updated together.


Updates `axios` from 0.27.2 to 1.6.1
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.1)

Updates `@nuxt/devtools` from 0.6.7 to 1.0.1
- [Release notes](https://github.com/nuxt/devtools/releases)
- [Changelog](https://github.com/nuxt/devtools/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nuxt/devtools/commits/v1.0.1/packages/devtools)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
- dependency-name: "@nuxt/devtools"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-11 07:32:01 +00:00
mrjvs
fdca62cad2 Merge pull request #27 from movie-web/dev
v1.0.5
2023-11-07 21:17:15 +01:00
mrjvs
67c2a7c809 Merge branch 'master' into dev 2023-11-07 21:13:26 +01:00
William Oldham
bf4dc79078 Merge pull request #26 from movie-web/fix-upcloud-small-fix
allow numbers in variable names
2023-11-07 20:07:36 +00:00
mrjvs
378c99e0bd allow numbers in variable names 2023-11-07 21:05:23 +01:00
William Oldham
a474502ca3 Merge pull request #25 from movie-web/fix-upcloud
Fix upcloud
2023-11-07 19:57:20 +00:00
mrjvs
43ff52d158 bump version 2023-11-07 20:52:20 +01:00
mrjvs
5dee60c69a add upcloud key extractor
Co-authored-by: William Oldham <github@binaryoverload.co.uk>
2023-11-07 20:51:39 +01:00
mrjvs
00134c1487 delete temp upcloud file
Co-authored-by: William Oldham <github@binaryoverload.co.uk>
2023-11-07 20:51:30 +01:00
mrjvs
222241a293 formatted 2023-11-07 19:09:25 +01:00
mrjvs
3c6959e86e upcloud js file 2023-11-07 19:08:53 +01:00
William Oldham
8a2db08b1a Merge pull request #24 from movie-web/dev
Fix superstream
2023-11-01 14:21:52 +00:00
Jip Frijlink
e5aa402b9e Merge pull request #23 from movie-web/fix-superstream
Fix superstream
2023-11-01 15:19:38 +01:00
Jip Frijlink
a6e2355cac Merge branch 'dev' into fix-superstream 2023-11-01 15:16:04 +01:00
mrjvs
f9f9ddf852 bump version 2023-11-01 14:54:39 +01:00
mrjvs
dcb662e44a Fix superstream 2023-11-01 14:53:47 +01:00
mrjvs
506955e7ae Merge pull request #22 from movie-web/dependabot/npm_and_yarn/dot-docs/undici-5.27.0
Bump undici from 5.24.0 to 5.27.0 in /.docs
2023-10-31 22:45:37 +01:00
dependabot[bot]
9d98331ce0 Bump undici from 5.24.0 to 5.27.0 in /.docs
Bumps [undici](https://github.com/nodejs/undici) from 5.24.0 to 5.27.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.24.0...v5.27.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-29 20:20:19 +00:00
mrjvs
757efaf693 Merge pull request #21 from movie-web/dev
Provider package v1.0.3
2023-10-29 21:02:51 +01:00
mrjvs
1049a3e25d Merge branch 'master' into dev 2023-10-29 21:01:12 +01:00
mrjvs
b5bfa9f6fa version bump 2023-10-29 20:59:34 +01:00
mrjvs
df67574821 Merge pull request #20 from movie-web/fix-incomplete-error
Fix "incomplete stream" error on flixhq & zoechip
2023-10-29 20:59:20 +01:00
mrjvs
cc2bcd2781 remove temp test 2023-10-29 20:53:46 +01:00
mrjvs
57a22c4a76 fix bug where runAll() doesn't work for embed based returns 2023-10-29 20:41:43 +01:00
mrjvs
07f81707a3 add isValidStream unit tests 2023-10-29 20:41:29 +01:00
mrjvs
457ecab529 Merge pull request #19 from movie-web/dev
V1.0.2
2023-10-26 21:36:13 +02:00
William Oldham
3e7d1f19d3 Update src/providers/sources/showbox/index.ts 2023-10-26 20:33:20 +01:00
mrjvs
964aee7c78 Merge branch 'master' into dev 2023-10-26 21:31:01 +02:00
William Oldham
55ea64e64b Merge pull request #18 from movie-web/fix-vuln-in-packages
Fix vuln in packages (and bump version)
2023-10-26 20:24:43 +01:00
mrjvs
b868ec1f81 bump version 2023-10-26 21:18:19 +02:00
mrjvs
8647d08e22 fix security in dependencies 2023-10-26 21:17:44 +02:00
mrjvs
0f35983087 Merge pull request #10 from zisra/dev
Add showbox, febbox, and flixhq shows
2023-10-26 21:15:12 +02:00
mrjvs
bec7c07881 check validity of stream before returning 2023-10-26 21:07:11 +02:00
mrjvs
9d204c381f Fix febbox 2023-10-26 20:52:07 +02:00
6f9c0517b8 fix: Exclude capital 'P' from qualities 2023-10-03 19:58:54 -05:00
aaa01a629b Use correct nanoid version 2023-10-03 17:57:52 -05:00
6ecc866fed Reintroduce nanoid 2023-10-03 17:54:53 -05:00
35cfa4747e Fix no-extraneous-dependencies 2023-10-03 17:51:08 -05:00
64b493eaf2 Fix linter errors 2023-10-03 17:48:40 -05:00
zisra
381c6d4e14 Merge branch 'dev' into dev 2023-10-03 17:44:10 -05:00
6879dcdbe7 fix: no double log 2023-10-03 17:33:19 -05:00
856a8fcac1 fix: use updated imports 2023-10-03 16:50:54 -05:00
11af17b19b fix: correct order of functions 2023-10-03 16:49:39 -05:00
c3de70bbc0 fix: seperate function 2023-10-03 16:48:23 -05:00
f82457ed62 fix: use proxied fetcher 2023-10-03 16:46:27 -05:00
8baeb90bee fix: refactor showOutput to logDeepObject 2023-10-03 16:44:34 -05:00
c9bd98517f fix: emoty string files for superstream 2023-10-03 12:23:23 -05:00
791caa88fd refactor: use util.inspect instead of a stringify 2023-10-01 15:18:14 -05:00
Jip Fr
19f03949f4 Add progress 2023-10-01 22:16:46 +02:00
Jip Fr
bbc4f60c73 Add lookmovie 2023-10-01 22:07:37 +02:00
mrjvs
c8374ef139 Merge branch 'dev' 2023-10-01 20:02:34 +02:00
Jonathan Barrow
849347afbe add vidsrc source+embed and StreamBucket embed 2023-09-30 17:44:32 -04:00
7feaf9256f febbox use actual qualities 2023-09-29 22:44:59 -05:00
William Oldham
f9d2aab106 Merge pull request #13 from movie-web/dev
Version 1.0.0
2023-09-29 21:01:32 +01:00
2f289d93d0 provider: showbox.media 2023-09-28 15:55:02 -05:00
e070701905 chore: fix typo 2023-09-28 15:51:39 -05:00
3c292baf26 Add flixhq shows 2023-09-27 22:49:22 -05:00
216 changed files with 22471 additions and 28553 deletions

View File

@@ -8,7 +8,7 @@ layout: page
---
cta:
- Get Started
- /guide/usage
- /get-started/introduction
secondary:
- Open on GitHub →
- https://github.com/movie-web/providers

View File

@@ -1,13 +0,0 @@
# Targets
When making an instance of the library using `makeProviders()`. It will immediately require choosing a target.
::alert{type="info"}
A target is the device where the stream will be played on.
**Where the scraping is run has nothing to do with the target**, only where the stream is finally played in the end is significant in choosing a target.
::
#### Possible targets
- **`targets.BROWSER`** Stream will be played in a browser with CORS
- **`targets.NATIVE`** Stream will be played natively
- **`targets.ALL`** Stream will be played on a device with no restrictions of any kind

View File

@@ -1,47 +0,0 @@
# Fetchers
When making an instance of the library using `makeProviders()`. It will immediately make a fetcher.
This comes with some considerations depending on the environment you're running.
## Using `fetch()`
In most cases, you can use the `fetch()` API. This will work in newer versions of Node.js (18 and above) and on the browser.
```ts
const fetcher = makeDefaultFetcher(fetch);
```
If you using older version of Node.js. You can use the npm package `node-fetch` to polyfill fetch:
```ts
import fetch from "node-fetch";
const fetcher = makeDefaultFetcher(fetch);
```
## Using fetchers on the browser
When using this library on a browser, you will need a proxy. Browsers come with many restrictions on when a web request can be made, and to bypass those restrictions, you will need a cors proxy.
The movie-web team has a proxy pre-made and pre-configured for you to use. For more information, check out [movie-web/simple-proxy](https://github.com/movie-web/simple-proxy). After installing, you can use this proxy like so:
```ts
const fetcher = makeSimpleProxyFetcher("https://your.proxy.workers.dev/", fetch);
```
If you aren't able to use this specific proxy and need to use a different one, you can make your own fetcher in the next section.
## Making a custom fetcher
In some rare cases, a custom fetcher will need to be made. This can be quite difficult to do from scratch so it's recommended to base it off an existing fetcher and building your own functionality around it.
```ts
export function makeCustomFetcher(): Fetcher {
const fetcher = makeStandardFetcher(f);
const customFetcher: Fetcher = (url, ops) => {
return fetcher(url, ops);
};
return customFetcher;
}
```
If you need to make your own fetcher for a proxy. Make sure you make it compatible with the following headers: `Cookie`, `Referer`, `Origin`. Proxied fetchers need to be able to write those headers when making a request.

View File

@@ -1,2 +0,0 @@
icon: ph:book-open-fill
navigation.redirect: /guide/usage

View File

@@ -0,0 +1,14 @@
# Introduction
## What is `@movie-web/providers`?
`@movie-web/providers` is the soul of [movie-web](https://github.com/movie-web/movie-web). It's a collection of scrapers of various streaming sites. It extracts the raw streams from those sites, so you can watch them without any extra fluff from the original sites.
## What can I use this on?
We support many different environments, here are a few examples:
- In browser, watch streams without needing a server to scrape (does need a proxy)
- In a native app, scrape in the app itself
- In a backend server, scrape on the server and give the streams to the client to watch.
To find out how to configure the library for your environment, You can read [How to use on X](../2.essentials/0.usage-on-x.md).

View File

@@ -1,4 +1,6 @@
# Usage
# Quick start
## Installation
Let's get started with `@movie-web/providers`. First lets install the package.
@@ -18,11 +20,15 @@ Let's get started with `@movie-web/providers`. First lets install the package.
To get started with scraping on the **server**, first you have to make an instance of the providers.
```ts
import { makeProviders, makeDefaultFetcher, targets } from '@movie-web/providers';
::alert{type="warning"}
This snippet will only work on a **server**. For other environments, check out [Usage on X](../2.essentials/0.usage-on-x.md).
::
```ts [index.ts (server)]
import { makeProviders, makeStandardFetcher, targets } from '@movie-web/providers';
// this is how the library will make http requests
const myFetcher = makeDefaultFetcher(fetch);
const myFetcher = makeStandardFetcher(fetch);
// make an instance of the providers library
const providers = makeProviders({
@@ -33,7 +39,8 @@ const providers = makeProviders({
})
```
Perfect, now we can start scraping a stream:
Perfect. You now have an instance of the providers you can reuse everywhere.
Now let's scrape an item:
```ts [index.ts (server)]
// fetch some data from TMDB
@@ -47,7 +54,7 @@ const media = {
const output = await providers.runAll({
media: media
})
if (!output) console.log("No stream found")
console.log(`stream url: ${output.stream.playlist}`)
```
Now we have our stream in the output variable. (If the output is `null` then nothing could be found.)
To find out how to use the streams, check out [Using streams](../2.essentials/4.using-streams.md).

View File

@@ -0,0 +1,5 @@
# Examples
::alert{type="warning"}
There are no examples yet, stay tuned!
::

View File

@@ -0,0 +1,128 @@
---
title: 'Changelog'
---
# Version 2.3.0
- Fixed RidoMovies search results
- Added Insertunit, SoaperTV, and WarezCDN providers
- Disabled Showbox and VidSrc
# Version 2.2.9
- Fixed VidSrcTo (both Vidplay and Filemoon embeds)
- Added dropload, filelions and vtube embeds to Primewire
- Fixed and enabled Smashystream
- Improved RidoMovies search results
# Version 2.2.8
- Fix package exports for CJS and ESM
- Fixed Mixdrop embed
- Added thumbnailTrack to Vidplay embed
# Version 2.2.7
- Fix showbox
# Version 2.2.6
- Fix febbox
- Validate if a stream is actually playable. Streams that are not responding are no longer returned.
# Version 2.2.5
- Add Primewire provider
- Improve VidSrcTo search results
- Fixed Filemoon embeds
- Fixed febbox
- Disabled non-working providers
- Reordered providers in ranking
# Version 2.2.4
- Hotfix for HDRezka provider
# Version 2.2.3
- Fix VidSrcTo
- Add HDRezka provider
- Fix Goojara causing a crash
- Improve react-native URLSearchParams implementation
- Cover an edge case where the title contains 'the movie' or 'the show'
# Version 2.2.2
- Fix subtitles not appearing if the name of the subtitle is in its native tongue.
- Remove references to the old domain
- Fixed ridomovies not working for some shows and movies
- Fixed Showbox not working in react-native.
# Version 2.2.1
- Fixed Closeload scraper
# Version 2.2.0
- Fixed vidsrc.me URL decoding.
- Added ridomovies with Ridoo and Closeload embed.
- Added Goojara.to source.
- Fixed VidSrcTo crashing if no subtitles are found.
- Added Nepu Provider.
- Added vidcloud to flixhq and zoechip.
- Add thumbnail track option to response (Not supported by any providers yet).
- Disabled Lookmovie and swapped Showbox and VidSrcTo in ranking.
# Version 2.1.1
- Fixed vidplay decryption keys being wrong and switched the domain to one that works
# Version 2.1.0
- Add preferedHeaders to most sources
- Add CF_BLOCKED flag to sources that have blocked cloudflare API's
- Fix vidsrc sometimes having an equal sign where it shouldnt
- Increase ranking of lookmovie
- Re-enabled subtitles for febbox-mp4
# Version 2.0.5
- Disable subtitles for febbox-mp4. As their endpoint doesn't work anymore.
# Version 2.0.4
- Added providers:
- Add VidSrcTo provider with Vidplay and Filemoon embeds
- Add VidSrc provider with StreamBucket embeds
- Fixed providers:
- RemoteStream
- LookMovie - Fixed captions
- ShowBox
- Updated documentation to fix spelling + grammar
- User-agent header fix
- Needs the latest simple-proxy update
- Added utility to not return multiple subs for the same language - Applies to Lookmovie and Showbox
# Version 2.0.3
- Actually remove Febbox HLS
# Version 2.0.2
- Added Lookmovie caption support
- Fix Febbox duplicate subtitle languages
- Remove Febbox HLS
# Version 2.0.1
- Fixed issue where febbox-mp4 would not show all qualities
- Fixed issue where discoverEmbeds event would not show the embeds in the right order
# Version 2.0.0
::alert{type="warning"}
There are breaking changes in this list, make sure to read them thoroughly if you plan on updating.
::
**Development tooling:**
- Added integration test for browser. To make sure the package keeps working in the browser
- Add type checking when building, previously it ignored them
- Refactored the main folder, now called entrypoint.
- Dev-cli code has been split up a bit more, a bit cleaner to navigate
- Dev-cli is now moved to `npm run cli`
- Dev-cli has now has support for running in a headless browser using a proxy URL.
- Fetchers can now return a full response with headers and everything
**New features:**
- Added system to allow scraping IP locked sources through the consistentIpforRequests option.
- There is now a `buildProviders()` function that gives a builder for the `ProviderControls`. It's an alternative to `makeProviders()`.
- Streams can now return a headers object and a `preferredHeaders` object. which is required and optional headers for when using the stream.
**Notable changes:**
- Renamed the NO_CORS flag to CORS_ALLOWED (meaning that resource sharing is allowed)
- Export Fetcher and Stream types with all types related to it
- Providers can now return a list of streams instead of just one.
- Captions now have identifiers returned with them. Just generally useful to have
- New targets and some of them renamed

View File

@@ -0,0 +1,2 @@
icon: ph:shooting-star-fill
navigation.redirect: /get-started/introduction

View File

@@ -1,20 +0,0 @@
# `makeStandardFetcher`
Make a fetcher from a `fetch()` API. It is used for making a instance of providers with `makeProviders()`.
## Example
```ts
import { targets, makeProviders, makeDefaultFetcher } from "@movie-web/providers";
const providers = makeProviders({
fetcher: makeDefaultFetcher(fetch),
target: targets.NATIVE,
});
```
## Type
```ts
function makeDefaultFetcher(fetchApi: typeof fetch): Fetcher;
```

View File

@@ -1,3 +0,0 @@
icon: ph:file-code-fill
navigation.redirect: /api/makeproviders
navigation.title: API

View File

@@ -0,0 +1,67 @@
# How to use on X
The library can run in many environments, so it can be tricky to figure out how to set it up.
Here is a checklist. For more specific environments, keep reading below:
- When requests are very restricted (like browser client-side). Configure a proxied fetcher.
- When your requests come from the same device on which it will be streamed (not compatible with proxied fetcher). Set `consistentIpForRequests: true`.
- To set a target. Consult [Targets](./1.targets.md).
To make use of the examples below, check out the following pages:
- [Quick start](../1.get-started/1.quick-start.md)
- [Using streams](../2.essentials/4.using-streams.md)
## NodeJs server
```ts
import { makeProviders, makeStandardFetcher, targets } from '@movie-web/providers';
const providers = makeProviders({
fetcher: makeStandardFetcher(fetch),
target: chooseYourself, // check out https://movie-web.github.io/providers/essentials/targets
})
```
## Browser client-side
Using the provider package client-side requires a hosted version of simple-proxy.
Read more [about proxy fetchers](./2.fetchers.md#using-fetchers-on-the-browser).
```ts
import { makeProviders, makeStandardFetcher, targets } from '@movie-web/providers';
const proxyUrl = "https://your.proxy.workers.dev/";
const providers = makeProviders({
fetcher: makeStandardFetcher(fetch),
proxiedFetcher: makeSimpleProxyFetcher(proxyUrl, fetch),
target: target.BROWSER,
})
```
## React native
To use the library in a react native app, you would also need a couple of polyfills to polyfill crypto and base64.
1. First install the polyfills:
```bash
npm install @react-native-anywhere/polyfill-base64 react-native-quick-crypto
```
2. Add the polyfills to your app:
```ts
// Import in your entry file
import '@react-native-anywhere/polyfill-base64';
```
And follow the [react-native-quick-crypto documentation](https://github.com/margelo/react-native-quick-crypto) to set up the crypto polyfill.
3. Then you can use the library like this:
```ts
import { makeProviders, makeStandardFetcher, targets } from '@movie-web/providers';
const providers = makeProviders({
fetcher: makeStandardFetcher(fetch),
target: target.NATIVE,
consistentIpForRequests: true,
})
```

View File

@@ -0,0 +1,14 @@
# Targets
When creating provider controls, you will immediately be required to choose a target.
::alert{type="warning"}
A target is the device on which the stream will be played.
**Where the scraping is run has nothing to do with the target**, only where the stream is finally played in the end is significant in choosing a target.
::
#### Possible targets
- **`targets.BROWSER`** Stream will be played in a browser with CORS
- **`targets.BROWSER_EXTENSION`** Stream will be played in a browser using the movie-web extension (WIP)
- **`targets.NATIVE`** Stream will be played on a native video player
- **`targets.ANY`** No restrictions for selecting streams, will just give all of them

View File

@@ -0,0 +1,74 @@
# Fetchers
When creating provider controls, a fetcher will need to be configured.
Depending on your environment, this can come with some considerations:
## Using `fetch()`
In most cases, you can use the `fetch()` API. This will work in newer versions of Node.js (18 and above) and on the browser.
```ts
const fetcher = makeStandardFetcher(fetch);
```
If you using older version of Node.js. You can use the npm package `node-fetch` to polyfill fetch:
```ts
import fetch from "node-fetch";
const fetcher = makeStandardFetcher(fetch);
```
## Using fetchers on the browser
When using this library on a browser, you will need a proxy. Browsers restrict when a web request can be made. To bypass those restrictions, you will need a CORS proxy.
The movie-web team has a proxy pre-made and pre-configured for you to use. For more information, check out [movie-web/simple-proxy](https://github.com/movie-web/simple-proxy). After installing, you can use this proxy like so:
```ts
const fetcher = makeSimpleProxyFetcher("https://your.proxy.workers.dev/", fetch);
```
If you aren't able to use this specific proxy and need to use a different one, you can make your own fetcher in the next section.
## Making a derived fetcher
In some rare cases, a custom fetcher is necessary. This can be quite difficult to make from scratch so it's recommended to base it off of an existing fetcher and building your own functionality around it.
```ts
export function makeCustomFetcher(): Fetcher {
const fetcher = makeStandardFetcher(f);
const customFetcher: Fetcher = (url, ops) => {
// Do something with the options and URL here
return fetcher(url, ops);
};
return customFetcher;
}
```
If you need to make your own fetcher for a proxy, ensure you make it compatible with the following headers: `Set-Cookie`, `Cookie`, `Referer`, `Origin`. Proxied fetchers need to be able to write/read those headers when making a request.
## Making a fetcher from scratch
In some rare cases, you need to make a fetcher from scratch.
This is the list of features it needs:
- Send/read every header
- Parse JSON, otherwise parse as text
- Send JSON, Formdata or normal strings
- get final destination URL
It's not recommended to do this at all. If you have to, you can base your code on the original implementation of `makeStandardFetcher`. Check out the [source code for it here](https://github.com/movie-web/providers/blob/dev/src/fetchers/standardFetch.ts).
Here is a basic template on how to make your own custom fetcher:
```ts
const myFetcher: Fetcher = (url, ops) => {
// Do some fetching
return {
body: {},
finalUrl: '',
headers: new Headers(), // should only contain headers from ops.readHeaders
statusCode: 200,
};
}
```

View File

@@ -0,0 +1,74 @@
# Customize providers
You make the provider controls in two ways. Either with `makeProviders()` (the simpler option) or with `buildProviders()` (more elaborate and extensive option).
## `makeProviders()` (simple)
To know what to set the configuration to, you can read [How to use on X](./0.usage-on-x.md) for a detailed guide on how to configure your controls.
```ts
const providers = makeProviders({
// fetcher, every web request gets called through here
fetcher: makeStandardFetcher(fetch),
// proxied fetcher, if the scraper needs to access a CORS proxy. this fetcher will be called instead
// of the normal fetcher. Defaults to the normal fetcher.
proxiedFetcher: undefined;
// target of where the streams will be used
target: targets.NATIVE;
// Set this to true, if the requests will have the same IP as
// the device that the stream will be played on.
consistentIpForRequests: false;
})
```
## `buildProviders()` (advanced)
To know what to set the configuration to, you can read [How to use on X](./0.usage-on-x.md) for a detailed guide on how to configure your controls.
### Standard setup
```ts
const providers = buildProviders()
.setTarget(targets.NATIVE) // target of where the streams will be used
.setFetcher(makeStandardFetcher(fetch)) // fetcher, every web request gets called through here
.addBuiltinProviders() // add all builtin providers, if this is not called, no providers will be added to the controls
.build();
```
### Adding only select few providers
Not all providers are great quality, so you can make an instance of the controls with only the providers you want.
```ts
const providers = buildProviders()
.setTarget(targets.NATIVE) // target of where the streams will be used
.setFetcher(makeStandardFetcher(fetch)) // fetcher, every web request gets called through here
.addSource('showbox') // only add showbox source
.addEmbed('febbox-hls') // add febbox-hls embed, which is returned by showbox
.build();
```
### Adding your own scrapers to the providers
If you have your own scraper and still want to use the nice utilities of the provider library or just want to add on to the built-in providers, you can add your own custom source.
```ts
const providers = buildProviders()
.setTarget(targets.NATIVE) // target of where the streams will be used
.setFetcher(makeStandardFetcher(fetch)) // fetcher, every web request gets called through here
.addSource({ // add your own source
id: 'my-scraper',
name: 'My scraper',
rank: 800,
flags: [],
scrapeMovie(ctx) {
throw new Error('Not implemented');
}
})
.build();
```

View File

@@ -0,0 +1,84 @@
# Using streams
Streams can sometimes be quite picky on how they can be used. So here is a guide on how to use them.
## Essentials
All streams have the same common parameters:
- `Stream.type`: The type of stream. Either `hls` or `file`
- `Stream.id`: The id of this stream, unique per scraper output.
- `Stream.flags`: A list of flags that apply to this stream. Most people won't need to use it.
- `Stream.captions`: A list of captions/subtitles for this stream.
- `Stream.headers`: Either undefined or a key value object of headers you must set to use the stream.
- `Stream.preferredHeaders`: Either undefined or a key value object of headers you may want to set if you want optimal playback - but not required.
Now let's delve deeper into how to watch these streams!
## Streams with type `hls`
HLS streams can be tough to watch. They're not normal files you can just use.
These streams have an extra property `Stream.playlist` which contains the m3u8 playlist.
Here is a code sample of how to use HLS streams in web context using hls.js
```html
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<video id="video"></video>
<script>
const stream = null; // add your stream here
if (Hls.isSupported()) {
var video = document.getElementById('video');
var hls = new Hls();
hls.loadSource(stream.playlist);
hls.attachMedia(video);
}
</script>
```
## Streams with type `file`
File streams are quite easy to use, they just return a new property: `Stream.qualities`.
This property is a map of quality and a stream file. So if you want to get 1080p quality you do `stream["1080"]` to get your stream file. It will return undefined if that quality is absent.
The possibly qualities are: `unknown`, `360`, `480`, `720`, `1080`, `4k`.
File based streams are always guaranteed to have one quality.
Once you get a streamfile, you have the following parameters:
- `StreamFile.type`: Right now it can only be `mp4`.
- `StreamFile.url`: The URL linking to the video file.
Here is a code sample of how to watch a file based stream in a browser:
```html
<video id="video"></video>
<script>
const stream = null; // add your stream here
const video = document.getElementById('video');
const qualityEntries = Object.keys(stream.qualities);
const firstQuality = qualityEntries[0];
video.src = firstQuality.url;
</script>
```
## Streams with headers
Streams have both a `Stream.headers` and a `Stream.preferredHeaders`.
The difference between the two is that `Stream.headers` **must** be set in order for the stream to work. While the other is optional, and enhances the quality or performance.
If your target is set to `BROWSER`, headers will never be required, as it's not possible to do.
## Using captions/subtitles
All streams have a list of captions at `Stream.captions`. The structure looks like this:
```ts
type Caption = {
type: CaptionType; // Language type, either "srt" or "vtt"
id: string; // Unique per stream
url: string; // The URL pointing to the subtitle file
hasCorsRestrictions: boolean; // If true, you will need to proxy it if you're running in a browser
language: string; // Language code of the caption
};
```

View File

@@ -0,0 +1,3 @@
icon: ph:info-fill
navigation.redirect: /essentials/usage
navigation.title: "Get started"

View File

@@ -1,31 +0,0 @@
# Testing Providers
In order to test providers effectively, a built-in tool can be used which allows testing of individual source and embed providers.
There are two ways to use the tool:
- Command Line Mode - For passing in arguments directly to the script. This is useful in non-interactive environments such as CI or to repeatedly test during development.
- Question Mode - Where the script asks you questions about which source you wish to test.
::code-group
```bash [Interactive]
npm run test:dev
```
```bash [Command Line]
npm run test:dev -- [options]
# Example testing FlixHQ with "Spirited Away"
npm run test:dev -- -sid flixhq -tid 129 -t movie
```
::
The following Command Line Mode arguments are available:
| Argument | Alias | Description | Default |
|---------------|--------|-------------------------------------------------------------------------|--------------|
| `--fetcher` | `-f` | Fetcher type. Either `node-fetch` or `native` | `node-fetch` |
| `--source-id` | `-sid` | Source ID for the source to be tested | |
| `--tmdb-id` | `-tid` | TMDB ID for the media to scrape. Only used if source is a provider | |
| `--type` | `-t` | Media type. Either `movie` or `show`. Only used if source is a provider | `movie` |
| `--season` | `-s` | Season number. Only used if type is `show` | `0` |
| `--episode` | `-e` | Episode number. Only used if type is `show` | `0` |
| `--url` | `-u` | URL to a video embed. Only used if source is an embed | |
| `--help` | `-h` | Shows help for the command arguments | |

View File

@@ -1,2 +0,0 @@
icon: ph:code
navigation.redirect: /developer/dev-cli

View File

@@ -0,0 +1,11 @@
# Sources vs embeds
::alert{type="warning"}
This page isn't quite done yet, stay tuned!
::
<!--
TODO
- How do sources and embeds differ
- How do sources and embeds interact
-->

View File

@@ -0,0 +1,12 @@
# New providers
::alert{type="warning"}
This page isn't quite done yet, stay tuned!
::
<!--
TODO
- How to make new sources or embeds
- Ranking
- Link to flags
-->

View File

@@ -0,0 +1,10 @@
# Flags
Flags is the primary way the library separates entities between different environments.
For example, some sources only give back content that has the CORS headers set to allow anyone, so that source gets the flag `CORS_ALLOWED`. Now if you set your target to `BROWSER`, sources without that flag won't even get listed.
This concept is applied in multiple away across the library.
## Flag options
- `CORS_ALLOWED`: Headers from the output streams are set to allow any origin.
- `IP_LOCKED`: The streams are locked by IP: requester and watcher must be the same.

View File

@@ -0,0 +1,3 @@
icon: ph:atom-fill
navigation.redirect: /in-depth/sources-and-embeds
navigation.title: "In-depth"

View File

@@ -0,0 +1,72 @@
# Development / contributing
::alert{type="warning"}
This page isn't quite done yet, stay tuned!
::
<!--
TODO
- Development setup
- How to make new sources/embeds (link to the page)
- How to use the fetchers, when to use proxiedFetcher
- How to use the context
-->
## Testing using the CLI
Testing can be quite difficult for this library, unit tests can't really be made because of the unreliable nature of scrapers.
But manually testing by writing an entry-point is also really annoying.
Our solution is to make a CLI that you can use to run the scrapers. For everything else there are unit tests.
### Setup
Make a `.env` file in the root of the repository and add a TMDB API key: `MOVIE_WEB_TMDB_API_KEY=KEY_HERE`.
Then make sure you've run `npm i` to get all the dependencies.
### Mode 1 - interactive
To run the CLI without needing to learn all the arguments, simply run the following command and go with the flow.
```sh
npm run cli
```
### Mode 2 - arguments
For repeatability, it can be useful to specify the arguments one by one.
To see all the arguments, you can run the help command:
```sh
npm run cli -- -h
```
Then just run it with your arguments, for example:
```sh
npm run cli -- -sid showbox -tid 556574
```
### Examples
```sh
# Spirited away - showbox
npm run cli -- -sid showbox -tid 129
# Hamilton - flixhq
npm run cli -- -sid flixhq -tid 556574
# Arcane S1E1 - showbox
npm run cli -- -sid zoechip -tid 94605 -s 1 -e 1
# febbox mp4 - get streams from an embed (gotten from a source output)
npm run cli -- -sid febbox-mp4 -u URL_HERE
```
### Fetcher options
The CLI comes with a few built-in fetchers:
- `node-fetch`: Fetch using the "node-fetch" library.
- `native`: Use the new fetch built into Node.JS (undici).
- `browser`: Start up headless chrome, and run the library in that context using a proxied fetcher.
::alert{type="warning"}
The browser fetcher will require you to run `npm run build` before running the CLI. Otherwise you will get outdated results.
::

View File

@@ -0,0 +1,3 @@
icon: ph:aperture-fill
navigation.redirect: /extra-topics/development
navigation.title: "Extra topics"

View File

@@ -1,12 +1,12 @@
# `makeProviders`
Make an instance of providers with configuration.
This is the main entrypoint of the library. It is recommended to make one instance globally and reuse it throughout your application.
Make an instance of provider controls with configuration.
This is the main entry-point of the library. It is recommended to make one instance globally and reuse it throughout your application.
## Example
```ts
import { targets, makeProviders, makeDefaultFetcher } from "@movie-web/providers";
import { targets, makeProviders, makeDefaultFetcher } from '@movie-web/providers';
const providers = makeProviders({
fetcher: makeDefaultFetcher(fetch),
@@ -23,9 +23,9 @@ interface ProviderBuilderOptions {
// instance of a fetcher, all webrequests are made with the fetcher.
fetcher: Fetcher;
// instance of a fetcher, in case the request has cors restrictions.
// instance of a fetcher, in case the request has CORS restrictions.
// this fetcher will be called instead of normal fetcher.
// if your environment doesnt have cors restrictions (like nodejs), there is no need to set this.
// if your environment doesn't have CORS restrictions (like Node.JS), there is no need to set this.
proxiedFetcher?: Fetcher;
// target to get streams for

View File

@@ -1,7 +1,7 @@
# `ProviderControls.runAll`
Run all providers one by one in order of their built-in ranking.
You can attach events if you need to know what is going on while its processing.
You can attach events if you need to know what is going on while it is processing.
## Example
@@ -9,9 +9,9 @@ You can attach events if you need to know what is going on while its processing.
// media from TMDB
const media = {
type: 'movie',
title: "Hamilton",
title: 'Hamilton',
releaseYear: 2020,
tmdbId: "556574"
tmdbId: '556574'
}
// scrape a stream
@@ -20,7 +20,7 @@ const stream = await providers.runAll({
})
// scrape a stream, but prioritize flixhq above all
// (other scrapers are stil ran if flixhq fails, it just has priority)
// (other scrapers are still run if flixhq fails, it just has priority)
const flixhqStream = await providers.runAll({
media: media,
sourceOrder: ['flixhq']
@@ -33,12 +33,12 @@ const flixhqStream = await providers.runAll({
function runAll(runnerOps: RunnerOptions): Promise<RunOutput | null>;
interface RunnerOptions {
// overwrite the order of sources to run. list of ids
// any omitted ids are in added to the end in order of rank (highest first)
// overwrite the order of sources to run. List of IDs
// any omitted IDs are added to the end in order of rank (highest first)
sourceOrder?: string[];
// overwrite the order of embeds to run. list of ids
// any omitted ids are in added to the end in order of rank (highest first)
// overwrite the order of embeds to run. List of IDs
// any omitted IDs are added to the end in order of rank (highest first)
embedOrder?: string[];
// object of event functions
@@ -49,13 +49,13 @@ interface RunnerOptions {
}
type RunOutput = {
// source scraper id
// source scraper ID
sourceId: string;
// if from an embed, this is the embed scraper id
// if from an embed, this is the embed scraper ID
embedId?: string;
// the outputed stream
// the emitted stream
stream: Stream;
};
```

View File

@@ -1,18 +1,18 @@
# `ProviderControls.runSourceScraper`
Run a specific source scraper and get its outputted streams.
Run a specific source scraper and get its emitted streams.
## Example
```ts
import { SourcererOutput, NotFoundError } from "@movie-web/providers";
import { SourcererOutput, NotFoundError } from '@movie-web/providers';
// media from TMDB
const media = {
type: 'movie',
title: "Hamilton",
title: 'Hamilton',
releaseYear: 2020,
tmdbId: "556574"
tmdbId: '556574'
}
// scrape a stream from flixhq
@@ -24,15 +24,15 @@ try {
})
} catch (err) {
if (err instanceof NotFoundError) {
console.log("source doesnt have this media");
console.log('source does not have this media');
} else {
console.log("failed to scrape")
console.log('failed to scrape')
}
return;
}
if (!output.stream && output.embeds.length === 0) {
console.log("no streams found");
console.log('no streams found');
}
```
@@ -48,13 +48,13 @@ interface SourceRunnerOptions {
// the media you want to see sources from
media: ScrapeMedia;
// id of the source scraper you want to scrape from
// ID of the source scraper you want to scrape from
id: string;
}
type SourcererOutput = {
// list of embeds that the source scraper found.
// embed id is a reference to an embed scraper
// embed ID is a reference to an embed scraper
embeds: {
embedId: string;
url: string;

View File

@@ -1,21 +1,21 @@
# `ProviderControls.runEmbedScraper`
Run a specific embed scraper and get its outputted streams.
Run a specific embed scraper and get its emitted streams.
## Example
```ts
import { SourcererOutput } from "@movie-web/providers";
import { SourcererOutput } from '@movie-web/providers';
// scrape a stream from upcloud
let output: EmbedOutput;
try {
output = await providers.runSourceScraper({
output = await providers.runEmbedScraper({
id: 'upcloud',
url: 'https://example.com/123',
})
} catch (err) {
console.log("failed to scrape")
console.log('failed to scrape')
return;
}
@@ -31,10 +31,10 @@ interface EmbedRunnerOptions {
// object of event functions
events?: IndividualScraperEvents;
// the embed url
// the embed URL
url: string;
// id of the embed scraper you want to scrape from
// ID of the embed scraper you want to scrape from
id: string;
}

View File

@@ -1,13 +1,13 @@
# `ProviderControls.listSources`
List all source scrapers that applicable for the target.
They are sorted by rank, highest first
List all source scrapers that are applicable for the target.
They are sorted by rank; highest first
## Example
```ts
const sourceScrapers = providers.listSources();
// Guaranteed to only return type: 'source'
// Guaranteed to only return the type: 'source'
```
## Type

View File

@@ -1,13 +1,13 @@
# `ProviderControls.listEmbeds`
List all embed scrapers that applicable for the target.
They are sorted by rank, highest first
List all embed scrapers that are applicable for the target.
They are sorted by rank; highest first
## Example
```ts
const embedScrapers = providers.listEmbeds();
// Guaranteed to only return type: 'embed'
// Guaranteed to only return the type: 'embed'
```
## Type

View File

@@ -1,7 +1,7 @@
# `ProviderControls.getMetadata`
Get meta data for a scraper, can be either source or embed scraper.
Returns null if the `id` is not recognized.
Returns `null` if the `id` is not recognized.
## Example

View File

@@ -0,0 +1,20 @@
# `makeStandardFetcher`
Make a fetcher from a `fetch()` API. It is used for making an instance of provider controls.
## Example
```ts
import { targets, makeProviders, makeDefaultFetcher } from '@movie-web/providers';
const providers = makeProviders({
fetcher: makeStandardFetcher(fetch),
target: targets.ANY,
});
```
## Type
```ts
function makeStandardFetcher(fetchApi: typeof fetch): Fetcher;
```

View File

@@ -5,9 +5,9 @@ Make a fetcher to use with [movie-web/simple-proxy](https://github.com/movie-web
## Example
```ts
import { targets, makeProviders, makeDefaultFetcher, makeSimpleProxyFetcher } from "@movie-web/providers";
import { targets, makeProviders, makeDefaultFetcher, makeSimpleProxyFetcher } from '@movie-web/providers';
const proxyUrl = "https://your.proxy.workers.dev/"
const proxyUrl = 'https://your.proxy.workers.dev/'
const providers = makeProviders({
fetcher: makeDefaultFetcher(fetch),

View File

@@ -0,0 +1,3 @@
icon: ph:code-simple-fill
navigation.redirect: /api/makeproviders
navigation.title: "Api reference"

19980
.docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

5
.docs/package.json Executable file → Normal file
View File

@@ -7,11 +7,12 @@
"build": "nuxi build",
"generate": "nuxi generate",
"preview": "nuxi preview",
"lint": "eslint ."
"lint": "eslint .",
"preinstall": "npx -y only-allow pnpm"
},
"devDependencies": {
"@nuxt-themes/docus": "^1.13.1",
"@nuxt/devtools": "^0.6.7",
"@nuxt/devtools": "^1.0.1",
"@nuxt/eslint-config": "^0.1.1",
"@nuxtjs/plausible": "^0.2.1",
"@types/node": "^20.4.0",

10025
.docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

72
.eslintrc Normal file
View File

@@ -0,0 +1,72 @@
{
"env": {
"browser": true
},
"extends": ["airbnb-base", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
"ignorePatterns": ["lib/*", "tests/*", "/*.js", "/*.ts", "/src/__test__/*", "/**/*.test.ts", "test/*"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"tsconfigRootDir": "./"
},
"settings": {
"import/resolver": {
"typescript": {
"project": "./tsconfig.json"
}
}
},
"plugins": ["@typescript-eslint", "import", "prettier"],
"rules": {
"no-plusplus": "off",
"class-methods-use-this": "off",
"no-bitwise": "off",
"no-underscore-dangle": "off",
"@typescript-eslint/no-explicit-any": "off",
"no-console": ["error", { "allow": ["warn", "error"] }],
"@typescript-eslint/no-this-alias": "off",
"import/prefer-default-export": "off",
"@typescript-eslint/no-empty-function": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
"no-restricted-syntax": "off",
"import/no-unresolved": ["error", { "ignore": ["^virtual:"] }],
"consistent-return": "off",
"no-continue": "off",
"no-eval": "off",
"no-await-in-loop": "off",
"no-nested-ternary": "off",
"no-param-reassign": ["error", { "props": false }],
"prefer-destructuring": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"tsx": "never"
}
],
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal", ["sibling", "parent"], "index", "unknown"],
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
],
"sort-imports": [
"error",
{
"ignoreCase": false,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"],
"allowSeparatedGroups": true
}
]
}
}

View File

@@ -1,69 +0,0 @@
module.exports = {
env: {
browser: true,
},
extends: ['airbnb-base', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
ignorePatterns: ['lib/*', 'tests/*', '/*.js', '/*.ts', '/**/*.test.ts', 'test/*'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: './',
},
settings: {
'import/resolver': {
typescript: {
project: './tsconfig.json',
},
},
},
plugins: ['@typescript-eslint', 'import', 'prettier'],
rules: {
'no-underscore-dangle': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'no-console': 'off',
'@typescript-eslint/no-this-alias': 'off',
'import/prefer-default-export': 'off',
'@typescript-eslint/no-empty-function': 'off',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'no-restricted-syntax': 'off',
'import/no-unresolved': ['error', { ignore: ['^virtual:'] }],
'consistent-return': 'off',
'no-continue': 'off',
'no-eval': 'off',
'no-await-in-loop': 'off',
'no-nested-ternary': 'off',
'no-param-reassign': ['error', { props: false }],
'prefer-destructuring': 'off',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'import/extensions': [
'error',
'ignorePackages',
{
ts: 'never',
tsx: 'never',
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', ['sibling', 'parent'], 'index', 'unknown'],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
'sort-imports': [
'error',
{
ignoreCase: false,
ignoreDeclarationSort: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
allowSeparatedGroups: true,
},
],
},
};

4
.github/CODEOWNERS vendored
View File

@@ -1,3 +1 @@
* @movie-web/core
.github @binaryoverload
* @movie-web/project-leads

2
.github/SECURITY.md vendored
View File

@@ -11,4 +11,4 @@ Support is not provided for any forks or mirrors of movie-web.
There are two ways you can contact the movie-web maintainers to report a vulnerability:
- Email [security@movie-web.app](mailto:security@movie-web.app)
- Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app)
- Report the vulnerability in the [movie-web Discord server](https://movie-web.github.io/links/discord)

View File

@@ -11,26 +11,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v3
with:
version: 8
- name: Install packages
working-directory: ./.docs
run: npm install
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Build project
working-directory: ./.docs
run: npm run generate
- name: Install packages
working-directory: ./.docs
run: pnpm install
- name: Upload production-ready build files
uses: actions/upload-pages-artifact@v1
with:
path: ./.docs/.output/public
- name: Build project
working-directory: ./.docs
run: pnpm run generate
env:
NUXT_APP_BASE_URL: /providers/
- name: Upload production-ready build files
uses: actions/upload-pages-artifact@v1
with:
path: ./.docs/.output/public
deploy:
name: Deploy

View File

@@ -14,16 +14,21 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
- uses: pnpm/action-setup@v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
version: 8
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
registry-url: "https://registry.npmjs.org"
- name: Install packages
run: npm ci
run: pnpm install --frozen-lockfile
- name: Publish
run: npm publish --access public
run: pnpm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -13,22 +13,30 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v3
with:
version: 8
- name: Install packages
run: npm install
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Run tests
run: npm run test
- name: Install packages
run: pnpm install --frozen-lockfile
- name: Run integration tests
run: npm run build && npm run test:integration
- name: Install puppeteer
run: node ./node_modules/puppeteer/install.mjs
- name: Run linting
run: npm run lint
- name: Run tests
run: pnpm run test
- name: Run integration tests
run: pnpm run build && pnpm run test:integration
- name: Run linting
run: pnpm run lint

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@ node_modules/
/lib
coverage
.env
.eslintcache

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 movie-web
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -7,29 +7,8 @@ features:
- scrape popular streaming websites
- works in both browser and server-side
Visit documentation here: https://providers.docs.movie-web.app/
Visit documentation here: https://movie-web.github.io/providers/
## Development
To make testing scrapers easier during development a CLI tool is available to run specific sources. To run the CLI testing tool, use `npm run test:dev`. The script supports 2 execution modes
## How to run locally or test my changes
- CLI Mode, for passing in arguments directly to the script
- Question Mode, where the script asks you questions about which source you wish to test
The following CLI Mode arguments are available
| Argument | Alias | Description | Default |
|---------------|--------|-------------------------------------------------------------------------|--------------|
| `--fetcher` | `-f` | Fetcher type. Either `node-fetch` or `native` | `node-fetch` |
| `--source-id` | `-sid` | Source ID for the source to be tested | |
| `--tmdb-id` | `-tid` | TMDB ID for the media to scrape. Only used if source is a provider | |
| `--type` | `-t` | Media type. Either `movie` or `show`. Only used if source is a provider | `movie` |
| `--season` | `-s` | Season number. Only used if type is `show` | `0` |
| `--episode` | `-e` | Episode number. Only used if type is `show` | `0` |
| `--url` | `-u` | URL to a video embed. Only used if source is an embed | |
| `--help` | `-h` | Shows help for the command arguments | |
Example testing the FlixHQ source on the movie "Spirited Away"
```bash
npm run test:dev -- -sid flixhq -tid 129 -t movie
```
These topics are also covered in the documentation, [read about it here](https://movie-web.github.io/providers/extra-topics/development).

6752
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,9 @@
{
"name": "@movie-web/providers",
"version": "1.0.1",
"version": "2.3.0",
"description": "Package that contains all the providers of movie-web",
"main": "./lib/index.umd.js",
"type": "module",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"files": [
"./lib"
@@ -10,12 +11,12 @@
"exports": {
".": {
"import": {
"types": "./lib/index.d.mts",
"default": "./lib/index.mjs"
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"require": {
"types": "./lib/index.d.ts",
"default": "./lib/index.umd.js"
"default": "./lib/index.umd.cjs"
}
}
},
@@ -32,55 +33,64 @@
"bugs": {
"url": "https://github.com/movie-web/providers/issues"
},
"homepage": "https://providers.docs.movie-web.app/",
"homepage": "https://movie-web.github.io/providers/",
"scripts": {
"build": "vite build",
"build": "vite build && tsc --noEmit",
"cli": "vite-node ./src/dev-cli/index.ts",
"test": "vitest run",
"test:dev": "ts-node ./src/dev-cli.ts",
"test:watch": "vitest",
"test:integration": "node ./tests/cjs && node ./tests/esm",
"test:providers": "cross-env MW_TEST_PROVIDERS=true vitest run --reporter verbose",
"test:integration": "node ./tests/cjs && node ./tests/esm && node ./tests/browser",
"test:coverage": "vitest run --coverage",
"lint": "eslint --ext .ts,.js src/",
"lint:fix": "eslint --fix --ext .ts,.js src/",
"lint:report": "eslint --ext .ts,.js --output-file eslint_report.json --format json src/",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run lint"
"preinstall": "npx -y only-allow pnpm",
"prepare": "pnpm run build",
"prepublishOnly": "pnpm test && pnpm run lint"
},
"devDependencies": {
"@types/crypto-js": "^4.1.1",
"@types/node-fetch": "^2.6.6",
"@types/randombytes": "^2.0.1",
"@types/spinnies": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"@vitest/coverage-v8": "^0.34.3",
"commander": "^11.0.0",
"dotenv": "^16.3.1",
"@nabla/vite-plugin-eslint": "^2.0.2",
"@types/cookie": "^0.6.0",
"@types/crypto-js": "^4.2.2",
"@types/node-fetch": "^2.6.11",
"@types/randombytes": "^2.0.3",
"@types/set-cookie-parser": "^2.4.7",
"@types/spinnies": "^0.5.3",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@vitest/coverage-v8": "^1.4.0",
"commander": "^12.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"enquirer": "^2.4.1",
"eslint": "^8.30.0",
"eslint": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"node-fetch": "^2.7.0",
"prettier": "^2.6.2",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"node-fetch": "^3.3.2",
"prettier": "^3.2.5",
"puppeteer": "^22.6.1",
"spinnies": "^0.5.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.6.7",
"tsc-alias": "^1.8.8",
"tsconfig-paths": "^4.2.0",
"typescript": "^4.6.3",
"vite": "^4.0.0",
"vite-plugin-dts": "^3.5.3",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^0.32.2"
"typescript": "^5.4.3",
"vite": "^5.2.7",
"vite-node": "^1.4.0",
"vite-plugin-dts": "^3.8.1",
"vitest": "^1.4.0"
},
"dependencies": {
"cheerio": "^1.0.0-rc.12",
"crypto-js": "^4.1.1",
"cookie": "^0.6.0",
"crypto-js": "^4.2.0",
"form-data": "^4.0.0",
"nanoid": "^3.3.6",
"node-fetch": "^2.7.0",
"iso-639-1": "^3.1.2",
"nanoid": "^3.3.7",
"node-fetch": "^3.3.2",
"set-cookie-parser": "^2.6.0",
"unpacker": "^1.0.1"
}
}

4346
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
import { serializeBody } from "@/fetchers/body";
import FormData from "form-data";
import { describe, expect, it } from "vitest";
describe("serializeBody()", () => {
it('should work with standard text', () => {
expect(serializeBody("hello world")).toEqual({
headers: {},
body: "hello world"
})
})
it('should work with objects', () => {
expect(serializeBody({ hello: "world", a: 42 })).toEqual({
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ hello: "world", a: 42 })
})
})
it('should work x-www-form-urlencoded', () => {
const obj = new URLSearchParams()
obj.set("a", "b");
expect(serializeBody(obj)).toEqual({
headers: {},
body: obj
})
})
it('should work multipart/form-data', () => {
const obj = new FormData()
obj.append("a", "b");
expect(serializeBody(obj)).toEqual({
headers: {},
body: obj
})
})
})

View File

@@ -1,125 +0,0 @@
import { makeSimpleProxyFetcher } from "@/fetchers/simpleProxy";
import { DefaultedFetcherOptions, FetcherOptions } from "@/fetchers/types";
import { Headers } from "node-fetch";
import { afterEach, describe, expect, it, vi } from "vitest";
describe("makeSimpleProxyFetcher()", () => {
const fetch = vi.fn();
const fetcher = makeSimpleProxyFetcher("https://example.com/proxy", fetch);
afterEach(() => {
vi.clearAllMocks();
});
function setResult(type: "text" | "json", value: any) {
if (type === 'text') return fetch.mockResolvedValueOnce({
headers: new Headers({
"content-type": "text/plain",
}),
text() {
return Promise.resolve(value);
},
});
if (type === 'json') return fetch.mockResolvedValueOnce({
headers: new Headers({
"content-type": "application/json",
}),
json() {
return Promise.resolve(value);
},
});
}
function expectFetchCall(ops: { inputUrl: string, input: DefaultedFetcherOptions, outputUrl?: string, output: any, outputBody: any }) {
expect(fetcher(ops.inputUrl, ops.input)).resolves.toEqual(ops.outputBody);
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
vi.clearAllMocks();
}
it('should pass options through', () => {
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
method: "GET",
query: {},
headers: {
"X-Hello": "world",
},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: "GET",
headers: {
"X-Hello": "world",
},
},
outputBody: "hello world"
})
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
method: "GET",
headers: {},
query: {
"a": 'b',
}
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/?a=b')}`,
output: {
method: "GET",
headers: {},
},
outputBody: "hello world"
})
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
method: "GET",
query: {},
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: "GET",
headers: {},
},
outputBody: "hello world"
})
});
it('should parse response correctly', () => {
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com/",
input: {
method: "POST",
query: {},
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: "POST",
headers: {},
},
outputBody: "hello world"
})
setResult("json", { hello: 42 });
expectFetchCall({
inputUrl: "https://google.com/",
input: {
method: "POST",
query: {},
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: "POST",
headers: {},
},
outputBody: { hello: 42 }
})
});
});

View File

@@ -1,125 +0,0 @@
import { makeStandardFetcher } from "@/fetchers/standardFetch";
import { DefaultedFetcherOptions } from "@/fetchers/types";
import { Headers } from "node-fetch";
import { afterEach, describe, expect, it, vi } from "vitest";
describe("makeStandardFetcher()", () => {
const fetch = vi.fn();
const fetcher = makeStandardFetcher(fetch);
afterEach(() => {
vi.clearAllMocks();
});
function setResult(type: "text" | "json", value: any) {
if (type === 'text') return fetch.mockResolvedValueOnce({
headers: new Headers({
"content-type": "text/plain",
}),
text() {
return Promise.resolve(value);
},
});
if (type === 'json') return fetch.mockResolvedValueOnce({
headers: new Headers({
"content-type": "application/json",
}),
json() {
return Promise.resolve(value);
},
});
}
function expectFetchCall(ops: { inputUrl: string, input: DefaultedFetcherOptions, outputUrl?: string, output: any, outputBody: any }) {
expect(fetcher(ops.inputUrl, ops.input)).resolves.toEqual(ops.outputBody);
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
vi.clearAllMocks();
}
it('should pass options through', () => {
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
method: "GET",
query: {},
headers: {
"X-Hello": "world",
},
},
outputUrl: "https://google.com/",
output: {
method: "GET",
headers: {
"X-Hello": "world",
},
},
outputBody: "hello world"
})
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
method: "GET",
headers: {},
query: {
"a": 'b',
}
},
outputUrl: "https://google.com/?a=b",
output: {
method: "GET",
headers: {},
},
outputBody: "hello world"
})
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com",
input: {
query: {},
headers: {},
method: "GET"
},
outputUrl: "https://google.com/",
output: {
method: "GET",
headers: {},
},
outputBody: "hello world"
})
});
it('should parse response correctly', () => {
setResult("text", "hello world");
expectFetchCall({
inputUrl: "https://google.com/",
input: {
query: {},
headers: {},
method: "POST"
},
outputUrl: "https://google.com/",
output: {
method: "POST",
headers: {},
},
outputBody: "hello world"
})
setResult("json", { hello: 42 });
expectFetchCall({
inputUrl: "https://google.com/",
input: {
query: {},
headers: {},
method: "POST"
},
outputUrl: "https://google.com/",
output: {
method: "POST",
headers: {},
},
outputBody: { hello: 42 }
})
});
});

View File

@@ -0,0 +1,90 @@
import { buildProviders } from '@/entrypoint/builder';
import { ScrapeMedia } from '@/entrypoint/utils/media';
import { targets } from '@/entrypoint/utils/targets';
import { makeStandardFetcher } from '@/fetchers/standardFetch';
import { Embed, Sourcerer, SourcererEmbed } from '@/providers/base';
import { TestTypes } from './providerUtils';
import { describe, expect, it } from 'vitest';
import { ProviderControls } from '@/entrypoint/controls';
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
export interface TestEmbedOptions {
embed: Embed;
source: Sourcerer;
testSuite: ScrapeMedia[];
types: TestTypes[];
debug?: boolean;
expect: {
embeds: number;
streams?: number;
error?: boolean;
};
}
function makeBaseEmbedProviders() {
const builder = buildProviders().setTarget(targets.ANY).setFetcher(makeStandardFetcher(fetch));
return builder;
}
export function testEmbed(ops: TestEmbedOptions) {
if (ops.testSuite.length === 0) throw new Error('Test suite must have at least one test');
describe(`embed:${ops.source.id}:${ops.embed.id}`, () => {
ops.testSuite.forEach((test) => {
describe(`test ${test.title}`, async () => {
async function gatherEmbeds(providers: ProviderControls): Promise<SourcererEmbed[]> {
const results = await providers.runSourceScraper({
id: ops.source.id,
media: test,
});
if (results.embeds.length !== ops.expect.embeds)
throw new Error(
`Embeds don't match expected amount of embeds (${ops.source.id}, ${ops.embed.id}, got ${results.embeds.length} but expected ${ops.expect.embeds})`,
);
return results.embeds;
}
async function runTest(providers: ProviderControls, embedUrl: string) {
let hasError = false;
let streamCount = 0;
try {
const result = await providers.runEmbedScraper({
id: ops.embed.id,
url: embedUrl,
});
if (ops.debug) console.log(result);
streamCount = (result.stream ?? []).length;
} catch (err) {
if (ops.debug) console.log(err);
hasError = true;
}
expect(ops.expect.error ?? false).toBe(hasError);
expect(ops.expect.streams ?? 0).toBe(streamCount);
}
for (const t of ops.types) {
const builder = makeBaseEmbedProviders().addSource(ops.source).addEmbed(ops.embed);
if (t === 'standard') {
} else if (t === 'ip:standard') builder.enableConsistentIpForRequests();
else if (t === 'proxied') {
if (!process.env.MOVIE_WEB_PROXY_URL)
throw new Error('Cant use proxied test without setting MOVIE_WEB_PROXY_URL env');
builder.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch));
}
const providers = builder.build();
try {
const embeds = await gatherEmbeds(providers);
embeds.forEach((embed, i) => {
it(`${t} - embed ${i}`, async () => {
await runTest(providers, embed.url);
});
});
} catch (err) {
it(`${t} - embed ??`, () => {
throw new Error('Failed to get streams: ' + err);
});
}
}
});
});
});
}

View File

@@ -0,0 +1,118 @@
import dotenv from 'dotenv';
import { febboxMp4Scraper } from '@/providers/embeds/febbox/mp4';
import { testEmbed } from './embedUtils';
import { showboxScraper } from '@/providers/sources/showbox';
import { testMedia } from './testMedia';
import { flixhqScraper } from '@/providers/sources/flixhq';
import { upcloudScraper } from '@/providers/embeds/upcloud';
import { goMoviesScraper } from '@/providers/sources/gomovies';
import { smashyStreamScraper } from '@/providers/sources/smashystream';
import { smashyStreamDScraper } from '@/providers/embeds/smashystream/dued';
import { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
import { vidsrcScraper } from '@/providers/sources/vidsrc';
import { vidSrcToScraper } from '@/providers/sources/vidsrcto';
import { vidplayScraper } from '@/providers/embeds/vidplay';
import { fileMoonScraper } from '@/providers/embeds/filemoon';
import { zoechipScraper } from '@/providers/sources/zoechip';
import { mixdropScraper } from '@/providers/embeds/mixdrop';
dotenv.config();
testEmbed({
embed: febboxMp4Scraper,
source: showboxScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: upcloudScraper,
source: flixhqScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: upcloudScraper,
source: goMoviesScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: smashyStreamDScraper,
source: smashyStreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: vidsrcembedScraper,
source: vidsrcScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: vidplayScraper,
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: fileMoonScraper,
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
},
});
testEmbed({
embed: upcloudScraper,
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
streams: 1,
},
});
testEmbed({
embed: mixdropScraper,
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
streams: 1,
},
});

View File

@@ -0,0 +1,92 @@
import { ScrapeMedia } from '@/entrypoint/utils/media';
import { Embed, Sourcerer, SourcererEmbed } from '@/providers/base';
import { buildProviders } from '@/entrypoint/builder';
import { describe, expect, it } from 'vitest';
import { makeStandardFetcher } from '@/fetchers/standardFetch';
import { ProviderControls } from '@/entrypoint/controls';
import { NotFoundError } from '@/utils/errors';
import { targets } from '@/entrypoint/utils/targets';
import { getBuiltinEmbeds } from '@/entrypoint/providers';
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
export type TestTypes = 'standard' | 'ip:standard' | 'proxied';
export interface TestSourceOptions {
source: Sourcerer;
testSuite: ScrapeMedia[];
types: TestTypes[];
debug?: boolean;
expect: {
embeds?: number;
streams?: number;
error?: boolean;
notfound?: boolean;
};
}
function makeBaseProviders() {
const builder = buildProviders().setTarget(targets.ANY).setFetcher(makeStandardFetcher(fetch));
const embeds = getBuiltinEmbeds();
embeds.forEach((embed) => builder.addEmbed(embed));
return builder;
}
export function testSource(ops: TestSourceOptions) {
if (ops.testSuite.length === 0) throw new Error('Test suite must have at least one test');
describe(`source:${ops.source.id}`, () => {
ops.testSuite.forEach((test) => {
describe(`test ${test.title}`, () => {
async function runTest(providers: ProviderControls) {
let hasNotFound = false;
let hasError = false;
let streamCount = 0;
let embedCount = 0;
let embeds = [];
try {
const result = await providers.runSourceScraper({
id: ops.source.id,
media: test,
});
if (ops.debug) console.log(result);
streamCount = (result.stream ?? []).length;
embedCount = result.embeds.length;
} catch (err) {
if (ops.debug) console.log(err);
if (err instanceof NotFoundError) hasNotFound = true;
else hasError = true;
}
expect(ops.expect.error ?? false).toBe(hasError);
expect(ops.expect.notfound ?? false).toBe(hasNotFound);
expect(ops.expect.streams ?? 0).toBe(streamCount);
expect(ops.expect.embeds ?? 0).toBe(embedCount);
}
if (ops.types.includes('standard')) {
it(`standard`, async () => {
const providers = makeBaseProviders().addSource(ops.source).build();
await runTest(providers);
});
}
if (ops.types.includes('ip:standard')) {
it(`standard:ip`, async () => {
const providers = makeBaseProviders().addSource(ops.source).enableConsistentIpForRequests().build();
await runTest(providers);
});
}
if (ops.types.includes('proxied')) {
it(`proxied`, async () => {
if (!process.env.MOVIE_WEB_PROXY_URL)
throw new Error('Cant use proxied test without setting MOVIE_WEB_PROXY_URL env');
const providers = makeBaseProviders()
.addSource(ops.source)
.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch))
.build();
await runTest(providers);
});
}
});
});
});
}

View File

@@ -0,0 +1,95 @@
import { testSource } from './providerUtils';
import { lookmovieScraper } from '@/providers/sources/lookmovie';
import { testMedia } from './testMedia';
import { showboxScraper } from '@/providers/sources/showbox';
import dotenv from 'dotenv';
import { flixhqScraper } from '@/providers/sources/flixhq';
import { goMoviesScraper } from '@/providers/sources/gomovies';
import { smashyStreamScraper } from '@/providers/sources/smashystream';
import { vidsrcScraper } from '@/providers/sources/vidsrc';
import { vidSrcToScraper } from '@/providers/sources/vidsrcto';
import { zoechipScraper } from '@/providers/sources/zoechip';
import { remotestreamScraper } from '@/providers/sources/remotestream';
dotenv.config();
testSource({
source: lookmovieScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['ip:standard'],
expect: {
streams: 1,
},
});
testSource({
source: showboxScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
},
});
testSource({
source: flixhqScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
},
});
testSource({
source: goMoviesScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
},
});
testSource({
source: smashyStreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
},
});
testSource({
source: vidsrcScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
},
});
testSource({
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
},
});
testSource({
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 3,
},
});
testSource({
source: remotestreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
streams: 1,
},
});

View File

@@ -0,0 +1,30 @@
import { ScrapeMedia } from '@/entrypoint/utils/media';
function makeMedia(media: ScrapeMedia): ScrapeMedia {
return media;
}
export const testMedia = {
arcane: makeMedia({
type: 'show',
title: 'Arcane',
tmdbId: '94605',
releaseYear: 2021,
episode: {
number: 1,
tmdbId: '1953812',
},
season: {
number: 1,
tmdbId: '134187',
},
imdbId: 'tt11126994',
}),
hamilton: makeMedia({
type: 'movie',
tmdbId: '556574',
imdbId: 'tt8503618',
releaseYear: 2020,
title: 'Hamilton',
}),
};

View File

@@ -0,0 +1,39 @@
import { serializeBody } from '@/fetchers/body';
import FormData from 'form-data';
import { describe, expect, it } from 'vitest';
describe('serializeBody()', () => {
it('should work with standard text', () => {
expect(serializeBody('hello world')).toEqual({
headers: {},
body: 'hello world',
});
});
it('should work with objects', () => {
expect(serializeBody({ hello: 'world', a: 42 })).toEqual({
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ hello: 'world', a: 42 }),
});
});
it('should work x-www-form-urlencoded', () => {
const obj = new URLSearchParams();
obj.set('a', 'b');
expect(serializeBody(obj)).toEqual({
headers: {},
body: obj,
});
});
it('should work multipart/form-data', () => {
const obj = new FormData();
obj.append('a', 'b');
expect(serializeBody(obj)).toEqual({
headers: {},
body: obj,
});
});
});

View File

@@ -1,48 +1,62 @@
import { makeFullUrl } from "@/fetchers/common";
import { describe, expect, it } from "vitest";
import { makeFullUrl } from '@/fetchers/common';
import { describe, expect, it } from 'vitest';
describe("makeFullUrl()", () => {
describe('makeFullUrl()', () => {
it('should pass normal url if no options', () => {
expect(makeFullUrl('https://example.com/hello/world')).toEqual("https://example.com/hello/world")
expect(makeFullUrl('https://example.com/hello/world?a=b')).toEqual("https://example.com/hello/world?a=b")
expect(makeFullUrl('https://example.com/hello/world?a=b#hello')).toEqual("https://example.com/hello/world?a=b#hello")
expect(makeFullUrl('https://example.com/hello/world#hello')).toEqual("https://example.com/hello/world#hello")
})
expect(makeFullUrl('https://example.com/hello/world')).toEqual('https://example.com/hello/world');
expect(makeFullUrl('https://example.com/hello/world?a=b')).toEqual('https://example.com/hello/world?a=b');
expect(makeFullUrl('https://example.com/hello/world?a=b#hello')).toEqual(
'https://example.com/hello/world?a=b#hello',
);
expect(makeFullUrl('https://example.com/hello/world#hello')).toEqual('https://example.com/hello/world#hello');
});
it('should append baseurl correctly', () => {
const correctResult = "https://example.com/hello/world";
expect(makeFullUrl(correctResult, { baseUrl: '' })).toEqual(correctResult)
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult)
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult)
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult)
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult)
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult)
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult)
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult)
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult)
expect(makeFullUrl('world?a=b', { baseUrl: 'https://example.com/hello' })).toEqual("https://example.com/hello/world?a=b")
})
const correctResult = 'https://example.com/hello/world';
expect(makeFullUrl(correctResult, { baseUrl: '' })).toEqual(correctResult);
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult);
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult);
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult);
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult);
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult);
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult);
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult);
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult);
expect(makeFullUrl('world?a=b', { baseUrl: 'https://example.com/hello' })).toEqual(
'https://example.com/hello/world?a=b',
);
});
it('should throw with invalid baseurl combinations', () => {
expect(() => makeFullUrl('example.com/hello/world', { baseUrl: '' })).toThrowError()
expect(() => makeFullUrl('/hello/world', { baseUrl: 'example.com' })).toThrowError()
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError()
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError()
})
expect(() => makeFullUrl('example.com/hello/world', { baseUrl: '' })).toThrowError();
expect(() => makeFullUrl('/hello/world', { baseUrl: 'example.com' })).toThrowError();
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError();
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError();
});
it('should add/merge query parameters', () => {
expect(makeFullUrl('https://example.com/hello/world', { query: { a: 'b' } })).toEqual("https://example.com/hello/world?a=b")
expect(makeFullUrl('https://example.com/hello/world/', { query: { a: 'b' } })).toEqual("https://example.com/hello/world/?a=b")
expect(makeFullUrl('https://example.com', { query: { a: 'b' } })).toEqual("https://example.com/?a=b")
expect(makeFullUrl('https://example.com/', { query: { a: 'b' } })).toEqual("https://example.com/?a=b")
expect(makeFullUrl('https://example.com/hello/world', { query: { a: 'b' } })).toEqual(
'https://example.com/hello/world?a=b',
);
expect(makeFullUrl('https://example.com/hello/world/', { query: { a: 'b' } })).toEqual(
'https://example.com/hello/world/?a=b',
);
expect(makeFullUrl('https://example.com', { query: { a: 'b' } })).toEqual('https://example.com/?a=b');
expect(makeFullUrl('https://example.com/', { query: { a: 'b' } })).toEqual('https://example.com/?a=b');
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: { a: 'b' } })).toEqual("https://example.com/hello/world?c=d&a=b")
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: {} })).toEqual("https://example.com/hello/world?c=d")
expect(makeFullUrl('https://example.com/hello/world?c=d')).toEqual("https://example.com/hello/world?c=d")
expect(makeFullUrl('https://example.com/hello/world?c=d', {})).toEqual("https://example.com/hello/world?c=d")
})
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: { a: 'b' } })).toEqual(
'https://example.com/hello/world?c=d&a=b',
);
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: {} })).toEqual(
'https://example.com/hello/world?c=d',
);
expect(makeFullUrl('https://example.com/hello/world?c=d')).toEqual('https://example.com/hello/world?c=d');
expect(makeFullUrl('https://example.com/hello/world?c=d', {})).toEqual('https://example.com/hello/world?c=d');
});
it('should work with a mix of multiple options', () => {
expect(makeFullUrl('/hello/world?c=d', { baseUrl: 'https://example.com/', query: { a: 'b' } })).toEqual("https://example.com/hello/world?c=d&a=b")
})
})
expect(makeFullUrl('/hello/world?c=d', { baseUrl: 'https://example.com/', query: { a: 'b' } })).toEqual(
'https://example.com/hello/world?c=d&a=b',
);
});
});

View File

@@ -0,0 +1,148 @@
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
import { DefaultedFetcherOptions, FetcherOptions } from '@/fetchers/types';
import { Headers } from 'node-fetch';
import { afterEach, describe, expect, it, vi } from 'vitest';
describe('makeSimpleProxyFetcher()', () => {
const fetch = vi.fn();
const fetcher = makeSimpleProxyFetcher('https://example.com/proxy', fetch);
afterEach(() => {
vi.clearAllMocks();
});
function setResult(type: 'text' | 'json', value: any) {
if (type === 'text')
return fetch.mockResolvedValueOnce({
headers: new Headers({
'content-type': 'text/plain',
}),
status: 204,
url: 'test123',
text() {
return Promise.resolve(value);
},
});
if (type === 'json')
return fetch.mockResolvedValueOnce({
headers: new Headers({
'content-type': 'application/json',
}),
status: 204,
url: 'test123',
json() {
return Promise.resolve(value);
},
});
}
function expectFetchCall(ops: {
inputUrl: string;
input: DefaultedFetcherOptions;
outputUrl?: string;
output: any;
outputBody: any;
}) {
const prom = fetcher(ops.inputUrl, ops.input);
expect((async () => (await prom).body)()).resolves.toEqual(ops.outputBody);
expect((async () => Array.from((await prom).headers.entries()))()).resolves.toEqual(
Array.from(new Headers().entries()),
);
expect((async () => (await prom).statusCode)()).resolves.toEqual(204);
expect((async () => (await prom).finalUrl)()).resolves.toEqual('test123');
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
vi.clearAllMocks();
}
it('should pass options through', () => {
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
method: 'GET',
query: {},
readHeaders: [],
headers: {
'X-Hello': 'world',
},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: 'GET',
headers: {
'X-Hello': 'world',
},
},
outputBody: 'hello world',
});
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
method: 'GET',
headers: {},
readHeaders: [],
query: {
a: 'b',
},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/?a=b')}`,
output: {
method: 'GET',
headers: {},
},
outputBody: 'hello world',
});
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
method: 'GET',
query: {},
readHeaders: [],
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: 'GET',
headers: {},
},
outputBody: 'hello world',
});
});
it('should parse response correctly', () => {
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com/',
input: {
method: 'POST',
query: {},
readHeaders: [],
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: 'POST',
headers: {},
},
outputBody: 'hello world',
});
setResult('json', { hello: 42 });
expectFetchCall({
inputUrl: 'https://google.com/',
input: {
method: 'POST',
query: {},
readHeaders: [],
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: 'POST',
headers: {},
},
outputBody: { hello: 42 },
});
});
});

View File

@@ -0,0 +1,149 @@
import { makeStandardFetcher } from '@/fetchers/standardFetch';
import { DefaultedFetcherOptions } from '@/fetchers/types';
import { Headers } from 'node-fetch';
import { afterEach, describe, expect, it, vi } from 'vitest';
describe('makeStandardFetcher()', () => {
const fetch = vi.fn();
const fetcher = makeStandardFetcher(fetch);
afterEach(() => {
vi.clearAllMocks();
});
function setResult(type: 'text' | 'json', value: any) {
if (type === 'text')
return fetch.mockResolvedValueOnce({
headers: new Headers({
'content-type': 'text/plain',
}),
status: 204,
url: 'test123',
text() {
return Promise.resolve(value);
},
});
if (type === 'json')
return fetch.mockResolvedValueOnce({
headers: new Headers({
'content-type': 'application/json',
}),
status: 204,
url: 'test123',
json() {
return Promise.resolve(value);
},
});
}
function expectFetchCall(ops: {
inputUrl: string;
input: DefaultedFetcherOptions;
outputUrl?: string;
output: any;
outputBody: any;
}) {
const prom = fetcher(ops.inputUrl, ops.input);
expect((async () => (await prom).body)()).resolves.toEqual(ops.outputBody);
expect((async () => Array.from((await prom).headers.entries()))()).resolves.toEqual(
Array.from(new Headers().entries()),
);
expect((async () => (await prom).statusCode)()).resolves.toEqual(204);
expect((async () => (await prom).finalUrl)()).resolves.toEqual('test123');
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
vi.clearAllMocks();
}
it('should pass options through', () => {
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
method: 'GET',
query: {},
readHeaders: [],
headers: {
'X-Hello': 'world',
},
},
outputUrl: 'https://google.com/',
output: {
method: 'GET',
headers: {
'X-Hello': 'world',
},
body: undefined,
},
outputBody: 'hello world',
});
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
method: 'GET',
headers: {},
readHeaders: [],
query: {
a: 'b',
},
},
outputUrl: 'https://google.com/?a=b',
output: {
method: 'GET',
headers: {},
},
outputBody: 'hello world',
});
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com',
input: {
query: {},
headers: {},
readHeaders: [],
method: 'GET',
},
outputUrl: 'https://google.com/',
output: {
method: 'GET',
headers: {},
},
outputBody: 'hello world',
});
});
it('should parse response correctly', () => {
setResult('text', 'hello world');
expectFetchCall({
inputUrl: 'https://google.com/',
input: {
query: {},
headers: {},
readHeaders: [],
method: 'POST',
},
outputUrl: 'https://google.com/',
output: {
method: 'POST',
headers: {},
},
outputBody: 'hello world',
});
setResult('json', { hello: 42 });
expectFetchCall({
inputUrl: 'https://google.com/',
input: {
query: {},
headers: {},
readHeaders: [],
method: 'POST',
},
outputUrl: 'https://google.com/',
output: {
method: 'POST',
headers: {},
},
outputBody: { hello: 42 },
});
});
});

View File

@@ -2,7 +2,7 @@
import { vi } from 'vitest';
import { gatherAllEmbeds, gatherAllSources } from '@/providers/all';
import { Embed, Sourcerer } from '@/providers/base';
import { makeEmbed, makeSourcerer } from '@/providers/base';
export function makeProviderMocks() {
const embedsMock = vi.fn<Parameters<typeof gatherAllEmbeds>, ReturnType<typeof gatherAllEmbeds>>();
@@ -13,91 +13,104 @@ export function makeProviderMocks() {
};
}
const sourceA = {
const sourceA = makeSourcerer({
id: 'a',
name: 'A',
rank: 1,
disabled: false,
} as Sourcerer;
const sourceB = {
flags: [],
});
const sourceB = makeSourcerer({
id: 'b',
name: 'B',
rank: 2,
disabled: false,
} as Sourcerer;
const sourceCDisabled = {
flags: [],
});
const sourceCDisabled = makeSourcerer({
id: 'c',
name: 'C',
rank: 3,
disabled: true,
} as Sourcerer;
const sourceAHigherRank = {
flags: [],
});
const sourceAHigherRank = makeSourcerer({
id: 'a',
name: 'A',
rank: 100,
disabled: false,
} as Sourcerer;
const sourceGSameRankAsA = {
flags: [],
});
const sourceGSameRankAsA = makeSourcerer({
id: 'g',
name: 'G',
rank: 1,
disabled: false,
} as Sourcerer;
const fullSourceYMovie = {
flags: [],
});
const fullSourceYMovie = makeSourcerer({
id: 'y',
name: 'Y',
rank: 105,
scrapeMovie: vi.fn(),
} as Sourcerer;
const fullSourceYShow = {
flags: [],
});
const fullSourceYShow = makeSourcerer({
id: 'y',
name: 'Y',
rank: 105,
scrapeShow: vi.fn(),
} as Sourcerer;
const fullSourceZBoth = {
flags: [],
});
const fullSourceZBoth = makeSourcerer({
id: 'z',
name: 'Z',
rank: 106,
scrapeMovie: vi.fn(),
scrapeShow: vi.fn(),
} as Sourcerer;
flags: [],
});
const embedD = {
const embedD = makeEmbed({
id: 'd',
rank: 4,
disabled: false,
} as Embed;
const embedA = {
} as any);
const embedA = makeEmbed({
id: 'a',
rank: 5,
disabled: false,
} as Embed;
const embedEDisabled = {
} as any);
const embedEDisabled = makeEmbed({
id: 'e',
rank: 6,
disabled: true,
} as Embed;
const embedDHigherRank = {
} as any);
const embedDHigherRank = makeEmbed({
id: 'd',
rank: 4000,
disabled: false,
} as Embed;
const embedFSameRankAsA = {
} as any);
const embedFSameRankAsA = makeEmbed({
id: 'f',
rank: 5,
disabled: false,
} as Embed;
const embedHSameRankAsSourceA = {
} as any);
const embedHSameRankAsSourceA = makeEmbed({
id: 'h',
rank: 1,
disabled: false,
} as Embed;
const fullEmbedX = {
} as any);
const fullEmbedX = makeEmbed({
id: 'x',
name: 'X',
rank: 104,
} as Embed;
const fullEmbedZ = {
} as any);
const fullEmbedZ = makeEmbed({
id: 'z',
name: 'Z',
rank: 109,
} as Embed;
} as any);
export const mockSources = {
sourceA,

View File

@@ -1,13 +1,16 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests';
import { mockEmbeds, mockSources } from '../providerTests';
import { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers';
import { FeatureMap } from '@/entrypoint/utils/targets';
import { getProviders } from '@/providers/get';
import { vi, describe, it, expect, afterEach } from 'vitest';
const mocks = await vi.hoisted(async () => (await import('../providerTests.ts')).makeProviderMocks());
const mocks = await vi.hoisted(async () => (await import('../providerTests')).makeProviderMocks());
vi.mock('@/providers/all', () => mocks);
const features = {
const features: FeatureMap = {
requires: [],
}
disallowed: [],
};
describe('getProviders()', () => {
afterEach(() => {
@@ -17,7 +20,12 @@ describe('getProviders()', () => {
it('should return providers', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
expect(getProviders(features)).toEqual({
expect(
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toEqual({
sources: [mockSources.sourceA, mockSources.sourceB],
embeds: [mockEmbeds.embedD],
});
@@ -26,7 +34,12 @@ describe('getProviders()', () => {
it('should filter out disabled providers', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedEDisabled]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceCDisabled, mockSources.sourceB]);
expect(getProviders(features)).toEqual({
expect(
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toEqual({
sources: [mockSources.sourceA, mockSources.sourceB],
embeds: [mockEmbeds.embedD],
});
@@ -35,31 +48,56 @@ describe('getProviders()', () => {
it('should throw on duplicate ids in sources', () => {
mocks.gatherAllEmbeds.mockReturnValue([]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceAHigherRank, mockSources.sourceA, mockSources.sourceB]);
expect(() => getProviders(features)).toThrowError();
expect(() =>
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toThrowError();
});
it('should throw on duplicate ids in embeds', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedDHigherRank, mockEmbeds.embedA]);
mocks.gatherAllSources.mockReturnValue([]);
expect(() => getProviders(features)).toThrowError();
expect(() =>
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toThrowError();
});
it('should throw on duplicate ids between sources and embeds', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedA]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
expect(() => getProviders(features)).toThrowError();
expect(() =>
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toThrowError();
});
it('should throw on duplicate rank between sources and embeds', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedA]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
expect(() => getProviders(features)).toThrowError();
expect(() =>
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toThrowError();
});
it('should not throw with same rank between sources and embeds', () => {
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedHSameRankAsSourceA]);
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
expect(getProviders(features)).toEqual({
expect(
getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
}),
).toEqual({
sources: [mockSources.sourceA, mockSources.sourceB],
embeds: [mockEmbeds.embedD, mockEmbeds.embedHSameRankAsSourceA],
});

View File

@@ -1,6 +1,6 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests';
import { makeProviders } from '@/main/builder';
import { targets } from '@/main/targets.ts';
import { mockEmbeds, mockSources } from '../providerTests.ts';
import { makeProviders } from '@/entrypoint/declare';
import { targets } from '@/entrypoint/utils/targets';
import { afterEach, describe, expect, it, vi } from 'vitest';
const mocks = await vi.hoisted(async () => (await import('../providerTests.ts')).makeProviderMocks());

View File

@@ -1,6 +1,6 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests';
import { makeProviders } from '@/main/builder';
import { targets } from '@/main/targets.ts';
import { mockEmbeds, mockSources } from '../providerTests.ts';
import { makeProviders } from '@/entrypoint/declare';
import { targets } from '@/entrypoint/utils/targets';
import { afterEach, describe, expect, it, vi } from 'vitest';
const mocks = await vi.hoisted(async () => (await import('../providerTests.ts')).makeProviderMocks());

View File

@@ -0,0 +1,137 @@
import { FeatureMap, Flags, flags, flagsAllowedInFeatures } from '@/entrypoint/utils/targets';
import { describe, it, expect } from 'vitest';
describe('flagsAllowedInFeatures()', () => {
function checkFeatures(featureMap: FeatureMap, flags: Flags[], output: boolean) {
expect(flagsAllowedInFeatures(featureMap, flags)).toEqual(output);
}
it('should check required correctly', () => {
checkFeatures(
{
requires: [],
disallowed: [],
},
[],
true,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [],
},
[flags.CORS_ALLOWED],
true,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [],
},
[],
false,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED, flags.IP_LOCKED],
disallowed: [],
},
[flags.CORS_ALLOWED, flags.IP_LOCKED],
true,
);
checkFeatures(
{
requires: [flags.IP_LOCKED],
disallowed: [],
},
[flags.CORS_ALLOWED],
false,
);
checkFeatures(
{
requires: [flags.IP_LOCKED],
disallowed: [],
},
[],
false,
);
});
it('should check disallowed correctly', () => {
checkFeatures(
{
requires: [],
disallowed: [],
},
[],
true,
);
checkFeatures(
{
requires: [],
disallowed: [flags.CORS_ALLOWED],
},
[],
true,
);
checkFeatures(
{
requires: [],
disallowed: [flags.CORS_ALLOWED],
},
[flags.CORS_ALLOWED],
false,
);
checkFeatures(
{
requires: [],
disallowed: [flags.CORS_ALLOWED],
},
[flags.IP_LOCKED],
true,
);
checkFeatures(
{
requires: [],
disallowed: [flags.CORS_ALLOWED, flags.IP_LOCKED],
},
[flags.CORS_ALLOWED],
false,
);
});
it('should pass mixed tests', () => {
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [flags.IP_LOCKED],
},
[],
false,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [flags.IP_LOCKED],
},
[flags.CORS_ALLOWED],
true,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [flags.IP_LOCKED],
},
[flags.IP_LOCKED],
false,
);
checkFeatures(
{
requires: [flags.CORS_ALLOWED],
disallowed: [flags.IP_LOCKED],
},
[flags.IP_LOCKED, flags.CORS_ALLOWED],
false,
);
});
});

View File

@@ -1,16 +1,16 @@
import { reorderOnIdList } from "@/utils/list";
import { describe, it, expect } from "vitest";
import { reorderOnIdList } from '@/utils/list';
import { describe, it, expect } from 'vitest';
function list(def: string) {
return def.split(",").map(v=>({
return def.split(',').map((v) => ({
rank: parseInt(v),
id: v,
}))
}));
}
function expectListToEqual(l1: ReturnType<typeof list>, l2: ReturnType<typeof list>) {
function flatten(l: ReturnType<typeof list>) {
return l.map(v=>v.id).join(",");
return l.map((v) => v.id).join(',');
}
expect(flatten(l1)).toEqual(flatten(l2));
}
@@ -18,7 +18,7 @@ function expectListToEqual(l1: ReturnType<typeof list>, l2: ReturnType<typeof li
describe('reorderOnIdList()', () => {
it('should reorder based on rank', () => {
const l = list('2,1,4,3');
const sortedList = list('4,3,2,1')
const sortedList = list('4,3,2,1');
expectListToEqual(reorderOnIdList([], l), sortedList);
});
@@ -28,26 +28,26 @@ describe('reorderOnIdList()', () => {
it('should reorder based on id list', () => {
const l = list('4,2,1,3');
const sortedList = list('4,3,2,1')
expectListToEqual(reorderOnIdList(["4","3","2","1"], l), sortedList);
const sortedList = list('4,3,2,1');
expectListToEqual(reorderOnIdList(['4', '3', '2', '1'], l), sortedList);
});
it('should reorder based on id list and rank second', () => {
const l = list('4,2,1,3');
const sortedList = list('4,3,2,1')
expectListToEqual(reorderOnIdList(["4","3"], l), sortedList);
const sortedList = list('4,3,2,1');
expectListToEqual(reorderOnIdList(['4', '3'], l), sortedList);
});
it('should work with only one item', () => {
const l = list('1');
const sortedList = list('1')
expectListToEqual(reorderOnIdList(["1"], l), sortedList);
const sortedList = list('1');
expectListToEqual(reorderOnIdList(['1'], l), sortedList);
expectListToEqual(reorderOnIdList([], l), sortedList);
});
it('should not affect original list', () => {
const l = list('4,3,2,1');
const unsortedList = list('4,3,2,1')
const unsortedList = list('4,3,2,1');
reorderOnIdList([], l);
expectListToEqual(l, unsortedList);
});

View File

@@ -0,0 +1,71 @@
import { isValidStream } from '@/utils/valid';
import { describe, it, expect } from 'vitest';
describe('isValidStream()', () => {
it('should pass valid streams', () => {
expect(
isValidStream({
type: 'file',
id: 'a',
flags: [],
captions: [],
qualities: {
'1080': {
type: 'mp4',
url: 'hello-world',
},
},
}),
).toBe(true);
expect(
isValidStream({
type: 'hls',
id: 'a',
flags: [],
captions: [],
playlist: 'hello-world',
}),
).toBe(true);
});
it('should detect empty qualities', () => {
expect(
isValidStream({
type: 'file',
id: 'a',
flags: [],
captions: [],
qualities: {},
}),
).toBe(false);
});
it('should detect empty stream urls', () => {
expect(
isValidStream({
type: 'file',
id: 'a',
flags: [],
captions: [],
qualities: {
'1080': {
type: 'mp4',
url: '',
},
},
}),
).toBe(false);
});
it('should detect emtpy HLS playlists', () => {
expect(
isValidStream({
type: 'hls',
id: 'a',
flags: [],
captions: [],
playlist: '',
}),
).toBe(false);
});
});

View File

@@ -1,417 +0,0 @@
/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
import { program } from 'commander';
import dotenv from 'dotenv';
import { prompt } from 'enquirer';
import nodeFetch from 'node-fetch';
import Spinnies from 'spinnies';
import { MetaOutput, MovieMedia, ProviderControls, ShowMedia, makeProviders, makeStandardFetcher, targets } from '.';
dotenv.config();
type ProviderSourceAnswers = {
id: string;
type: string;
};
type EmbedSourceAnswers = {
url: string;
};
type CommonAnswers = {
fetcher: string;
source: string;
};
type ShowAnswers = {
season: string;
episode: string;
};
type CommandLineArguments = {
fetcher: string;
sourceId: string;
tmdbId: string;
type: string;
season: string;
episode: string;
url: string;
};
const TMDB_API_KEY = process.env.MOVIE_WEB_TMDB_API_KEY ?? '';
if (!TMDB_API_KEY?.trim()) {
throw new Error('Missing MOVIE_WEB_TMDB_API_KEY environment variable');
}
function getAllSources() {
// * The only way to get a list of all sources is to
// * create all these things. Maybe this should change
const providers = makeProviders({
fetcher: makeStandardFetcher(nodeFetch),
target: targets.NATIVE,
});
const combined = [...providers.listSources(), ...providers.listEmbeds()];
// * Remove dupes
const map = new Map(combined.map((source) => [source.id, source]));
return [...map.values()];
}
// * Defined here cuz ESLint didn't like the order these were defined in
const sources = getAllSources();
async function makeTMDBRequest(url: string): Promise<Response> {
const headers: {
accept: 'application/json';
authorization?: string;
} = {
accept: 'application/json',
};
// * Used to get around ESLint
// * Assignment to function parameter 'url'. eslint (no-param-reassign)
let requestURL = url;
// * JWT keys always start with ey and are ONLY valid as a header.
// * All other keys are ONLY valid as a query param.
// * Thanks TMDB.
if (TMDB_API_KEY.startsWith('ey')) {
headers.authorization = `Bearer ${TMDB_API_KEY}`;
} else {
requestURL += `?api_key=${TMDB_API_KEY}`;
}
return fetch(requestURL, {
method: 'GET',
headers,
});
}
async function getMovieMediaDetails(id: string): Promise<MovieMedia> {
const response = await makeTMDBRequest(`https://api.themoviedb.org/3/movie/${id}`);
const movie = await response.json();
if (movie.success === false) {
throw new Error(movie.status_message);
}
if (!movie.release_date) {
throw new Error(`${movie.title} has no release_date. Assuming unreleased`);
}
return {
type: 'movie',
title: movie.title,
releaseYear: Number(movie.release_date.split('-')[0]),
tmdbId: id,
};
}
async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumber: string): Promise<ShowMedia> {
// * TV shows require the TMDB ID for the series, season, and episode
// * and the name of the series. Needs multiple requests
let response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}`);
const series = await response.json();
if (series.success === false) {
throw new Error(series.status_message);
}
if (!series.first_air_date) {
throw new Error(`${series.name} has no first_air_date. Assuming unaired`);
}
response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}`);
const season = await response.json();
if (season.success === false) {
throw new Error(season.status_message);
}
response = await makeTMDBRequest(
`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}/episode/${episodeNumber}`,
);
const episode = await response.json();
if (episode.success === false) {
throw new Error(episode.status_message);
}
return {
type: 'show',
title: series.name,
releaseYear: Number(series.first_air_date.split('-')[0]),
tmdbId: id,
episode: {
number: episode.episode_number,
tmdbId: episode.id,
},
season: {
number: season.season_number,
tmdbId: season.id,
},
};
}
function joinMediaTypes(mediaTypes: string[] | undefined) {
if (mediaTypes) {
const formatted = mediaTypes
.map((type: string) => {
return `${type[0].toUpperCase() + type.substring(1).toLowerCase()}s`;
})
.join(' / ');
return `(${formatted})`;
}
return ''; // * Embed sources pass through here too
}
async function runScraper(providers: ProviderControls, source: MetaOutput, options: CommandLineArguments) {
const spinnies = new Spinnies();
if (source.type === 'embed') {
spinnies.add('scrape', { text: `Running ${source.name} scraper on ${options.url}` });
try {
const result = await providers.runEmbedScraper({
url: options.url,
id: source.id,
});
spinnies.succeed('scrape', { text: 'Done!' });
console.log(result);
} catch (error) {
let message = 'Unknown error';
if (error instanceof Error) {
message = error.message;
}
spinnies.fail('scrape', { text: `ERROR: ${message}` });
}
} else {
let media;
if (options.type === 'movie') {
media = await getMovieMediaDetails(options.tmdbId);
} else {
media = await getShowMediaDetails(options.tmdbId, options.season, options.episode);
}
spinnies.add('scrape', { text: `Running ${source.name} scraper on ${media.title}` });
try {
const result = await providers.runSourceScraper({
media,
id: source.id,
});
spinnies.succeed('scrape', { text: 'Done!' });
console.log(result);
} catch (error) {
let message = 'Unknown error';
if (error instanceof Error) {
message = error.message;
}
spinnies.fail('scrape', { text: `ERROR: ${message}` });
}
}
}
async function processOptions(options: CommandLineArguments) {
if (options.fetcher !== 'node-fetch' && options.fetcher !== 'native') {
throw new Error("Fetcher must be either 'native' or 'node-fetch'");
}
if (!options.sourceId.trim()) {
throw new Error('Source ID must be provided');
}
const source = sources.find(({ id }) => id === options.sourceId);
if (!source) {
throw new Error('Invalid source ID. No source found');
}
if (source.type === 'embed' && !options.url.trim()) {
throw new Error('Must provide an embed URL for embed sources');
}
if (source.type === 'source') {
if (!options.tmdbId.trim()) {
throw new Error('Must provide a TMDB ID for provider sources');
}
if (Number.isNaN(Number(options.tmdbId)) || Number(options.tmdbId) < 0) {
throw new Error('TMDB ID must be a number greater than 0');
}
if (!options.type.trim()) {
throw new Error('Must provide a type for provider sources');
}
if (options.type !== 'movie' && options.type !== 'show') {
throw new Error("Invalid media type. Must be either 'movie' or 'show'");
}
if (options.type === 'show') {
if (!options.season.trim()) {
throw new Error('Must provide a season number for TV shows');
}
if (!options.episode.trim()) {
throw new Error('Must provide an episode number for TV shows');
}
if (Number.isNaN(Number(options.season)) || Number(options.season) <= 0) {
throw new Error('Season number must be a number greater than 0');
}
if (Number.isNaN(Number(options.episode)) || Number(options.episode) <= 0) {
throw new Error('Episode number must be a number greater than 0');
}
}
}
let fetcher;
if (options.fetcher === 'native') {
fetcher = makeStandardFetcher(fetch);
} else {
fetcher = makeStandardFetcher(nodeFetch);
}
const providers = makeProviders({
fetcher,
target: targets.NATIVE,
});
await runScraper(providers, source, options);
}
async function runQuestions() {
const options = {
fetcher: 'node-fetch',
sourceId: '',
tmdbId: '',
type: 'movie',
season: '0',
episode: '0',
url: '',
};
const answers = await prompt<CommonAnswers>([
{
type: 'select',
name: 'fetcher',
message: 'Select a fetcher',
choices: [
{
message: 'Native',
name: 'native',
},
{
message: 'Node fetch',
name: 'node-fetch',
},
],
},
{
type: 'select',
name: 'source',
message: 'Select a source',
choices: sources.map((source) => ({
message: `[${source.type.toLocaleUpperCase()}] ${source.name} ${joinMediaTypes(source.mediaTypes)}`.trim(),
name: source.id,
})),
},
]);
options.fetcher = answers.fetcher;
options.sourceId = answers.source;
const source = sources.find(({ id }) => id === answers.source);
if (!source) {
throw new Error(`No source with ID ${answers.source} found`);
}
if (source.type === 'embed') {
const sourceAnswers = await prompt<EmbedSourceAnswers>([
{
type: 'input',
name: 'url',
message: 'Embed URL',
},
]);
options.url = sourceAnswers.url;
} else {
const sourceAnswers = await prompt<ProviderSourceAnswers>([
{
type: 'input',
name: 'id',
message: 'TMDB ID',
},
{
type: 'select',
name: 'type',
message: 'Media type',
choices: [
{
message: 'Movie',
name: 'movie',
},
{
message: 'TV Show',
name: 'show',
},
],
},
]);
options.tmdbId = sourceAnswers.id;
options.type = sourceAnswers.type;
if (sourceAnswers.type === 'show') {
const seriesAnswers = await prompt<ShowAnswers>([
{
type: 'input',
name: 'season',
message: 'Season',
},
{
type: 'input',
name: 'episode',
message: 'Episode',
},
]);
options.season = seriesAnswers.season;
options.episode = seriesAnswers.episode;
}
}
await processOptions(options);
}
async function runCommandLine() {
program
.option('-f, --fetcher <fetcher>', "Fetcher to use. Either 'native' or 'node-fetch'", 'node-fetch')
.option('-sid, --source-id <id>', 'ID for the source to use. Either an embed or provider', '')
.option('-tid, --tmdb-id <id>', 'TMDB ID for the media to scrape. Only used if source is a provider', '')
.option('-t, --type <type>', "Media type. Either 'movie' or 'show'. Only used if source is a provider", 'movie')
.option('-s, --season <number>', "Season number. Only used if type is 'show'", '0')
.option('-e, --episode <number>', "Episode number. Only used if type is 'show'", '0')
.option('-u, --url <embed URL>', 'URL to a video embed. Only used if source is an embed', '');
program.parse();
await processOptions(program.opts());
}
if (process.argv.length === 2) {
runQuestions();
} else {
runCommandLine();
}

1
src/dev-cli/browser/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scraper CLI</title>
</head>
<body>
<script src="./index.ts" type="module"></script>
</body>
</html>

View File

@@ -0,0 +1,17 @@
import { makeProviders, makeSimpleProxyFetcher, makeStandardFetcher, targets } from '../../../lib';
(window as any).scrape = (proxyUrl: string, type: 'source' | 'embed', input: any) => {
const providers = makeProviders({
fetcher: makeStandardFetcher(fetch),
target: targets.BROWSER,
proxiedFetcher: makeSimpleProxyFetcher(proxyUrl, fetch),
});
if (type === 'source') {
return providers.runSourceScraper(input);
}
if (type === 'embed') {
return providers.runEmbedScraper(input);
}
throw new Error('Input input type');
};

16
src/dev-cli/config.ts Normal file
View File

@@ -0,0 +1,16 @@
export function getConfig() {
let tmdbApiKey = process.env.MOVIE_WEB_TMDB_API_KEY ?? '';
tmdbApiKey = tmdbApiKey.trim();
if (!tmdbApiKey) {
throw new Error('Missing MOVIE_WEB_TMDB_API_KEY environment variable');
}
let proxyUrl: undefined | string = process.env.MOVIE_WEB_PROXY_URL;
proxyUrl = !proxyUrl ? undefined : proxyUrl;
return {
tmdbApiKey,
proxyUrl,
};
}

189
src/dev-cli/index.ts Normal file
View File

@@ -0,0 +1,189 @@
/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
import { program } from 'commander';
import dotenv from 'dotenv';
import { prompt } from 'enquirer';
import { runScraper } from '@/dev-cli/scraper';
import { processOptions } from '@/dev-cli/validate';
import { getBuiltinEmbeds, getBuiltinSources } from '..';
dotenv.config();
type ProviderSourceAnswers = {
id: string;
type: string;
};
type EmbedSourceAnswers = {
url: string;
};
type CommonAnswers = {
fetcher: string;
source: string;
};
type ShowAnswers = {
season: string;
episode: string;
};
const sourceScrapers = getBuiltinSources().sort((a, b) => b.rank - a.rank);
const embedScrapers = getBuiltinEmbeds().sort((a, b) => b.rank - a.rank);
const sources = [...sourceScrapers, ...embedScrapers];
function joinMediaTypes(mediaTypes: string[] | undefined) {
if (mediaTypes) {
const formatted = mediaTypes
.map((type: string) => {
return `${type[0].toUpperCase() + type.substring(1).toLowerCase()}s`;
})
.join(' / ');
return `(${formatted})`;
}
return ''; // * Embed sources pass through here too
}
async function runQuestions() {
const options = {
fetcher: 'node-fetch',
sourceId: '',
tmdbId: '',
type: 'movie',
season: '0',
episode: '0',
url: '',
};
const answers = await prompt<CommonAnswers>([
{
type: 'select',
name: 'fetcher',
message: 'Select a fetcher mode',
choices: [
{
message: 'Native',
name: 'native',
},
{
message: 'Node fetch',
name: 'node-fetch',
},
{
message: 'Browser',
name: 'browser',
},
],
},
{
type: 'select',
name: 'source',
message: 'Select a source',
choices: sources.map((source) => ({
message: `[${source.type.toLocaleUpperCase()}] ${source.name} ${joinMediaTypes(source.mediaTypes)}`.trim(),
name: source.id,
})),
},
]);
options.fetcher = answers.fetcher;
options.sourceId = answers.source;
const source = sources.find(({ id }) => id === answers.source);
if (!source) {
throw new Error(`No source with ID ${answers.source} found`);
}
if (source.type === 'embed') {
const sourceAnswers = await prompt<EmbedSourceAnswers>([
{
type: 'input',
name: 'url',
message: 'Embed URL',
},
]);
options.url = sourceAnswers.url;
} else {
const sourceAnswers = await prompt<ProviderSourceAnswers>([
{
type: 'input',
name: 'id',
message: 'TMDB ID',
},
{
type: 'select',
name: 'type',
message: 'Media type',
choices: [
{
message: 'Movie',
name: 'movie',
},
{
message: 'TV Show',
name: 'show',
},
],
},
]);
options.tmdbId = sourceAnswers.id;
options.type = sourceAnswers.type;
if (sourceAnswers.type === 'show') {
const seriesAnswers = await prompt<ShowAnswers>([
{
type: 'input',
name: 'season',
message: 'Season',
},
{
type: 'input',
name: 'episode',
message: 'Episode',
},
]);
options.season = seriesAnswers.season;
options.episode = seriesAnswers.episode;
}
}
const { providerOptions, source: validatedSource, options: validatedOps } = await processOptions(sources, options);
await runScraper(providerOptions, validatedSource, validatedOps);
}
async function runCommandLine() {
program
.option('-f, --fetcher <fetcher>', "Fetcher to use. Either 'native' or 'node-fetch'", 'node-fetch')
.option('-sid, --source-id <id>', 'ID for the source to use. Either an embed or provider', '')
.option('-tid, --tmdb-id <id>', 'TMDB ID for the media to scrape. Only used if source is a provider', '')
.option('-t, --type <type>', "Media type. Either 'movie' or 'show'. Only used if source is a provider", 'movie')
.option('-s, --season <number>', "Season number. Only used if type is 'show'", '0')
.option('-e, --episode <number>', "Episode number. Only used if type is 'show'", '0')
.option('-u, --url <embed URL>', 'URL to a video embed. Only used if source is an embed', '');
program.parse();
const {
providerOptions,
source: validatedSource,
options: validatedOps,
} = await processOptions(sources, program.opts());
await runScraper(providerOptions, validatedSource, validatedOps);
}
if (process.argv.length === 2) {
runQuestions()
.catch(() => console.error('Exited.'))
.finally(() => process.exit(0));
} else {
runCommandLine()
.catch(() => console.error('Exited.'))
.finally(() => process.exit(0));
}

7
src/dev-cli/logging.ts Normal file
View File

@@ -0,0 +1,7 @@
import { inspect } from 'node:util';
export function logDeepObject(object: Record<any, any>) {
// This is the dev cli, so we can use console.log
// eslint-disable-next-line no-console
console.log(inspect(object, { showHidden: false, depth: null, colors: true }));
}

141
src/dev-cli/scraper.ts Normal file
View File

@@ -0,0 +1,141 @@
/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
import { existsSync } from 'fs';
import { join } from 'path';
import puppeteer, { Browser } from 'puppeteer';
import Spinnies from 'spinnies';
import { PreviewServer, build, preview } from 'vite';
import { getConfig } from '@/dev-cli/config';
import { logDeepObject } from '@/dev-cli/logging';
import { getMovieMediaDetails, getShowMediaDetails } from '@/dev-cli/tmdb';
import { CommandLineArguments } from '@/dev-cli/validate';
import { MetaOutput, ProviderMakerOptions, makeProviders } from '..';
async function runBrowserScraping(
providerOptions: ProviderMakerOptions,
source: MetaOutput,
options: CommandLineArguments,
) {
if (!existsSync(join(__dirname, '../../lib/index.js')))
throw new Error('Please compile before running cli in browser mode');
const config = getConfig();
if (!config.proxyUrl)
throw new Error('Simple proxy url must be set in the environment (MOVIE_WEB_PROXY_URL) for browser mode to work');
const root = join(__dirname, 'browser');
let server: PreviewServer | undefined;
let browser: Browser | undefined;
try {
// setup browser
await build({
root,
});
server = await preview({
root,
});
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
// This is the dev cli, so we can use console.log
// eslint-disable-next-line no-console
page.on('console', (message) => console.log(`${message.type().slice(0, 3).toUpperCase()} ${message.text()}`));
if (!server.resolvedUrls?.local.length) throw new Error('Server did not start');
await page.goto(server.resolvedUrls.local[0]);
await page.waitForFunction('!!window.scrape', { timeout: 5000 });
// get input media
let input: any;
if (source.type === 'embed') {
input = {
url: options.url,
id: source.id,
};
} else if (source.type === 'source') {
let media;
if (options.type === 'movie') {
media = await getMovieMediaDetails(options.tmdbId);
} else {
media = await getShowMediaDetails(options.tmdbId, options.season, options.episode);
}
input = {
media,
id: source.id,
};
} else {
throw new Error('Wrong source input type');
}
return await page.evaluate(
async (proxy, type, inp) => {
return (window as any).scrape(proxy, type, inp);
},
config.proxyUrl,
source.type,
input,
);
} finally {
server?.httpServer.close();
await browser?.close();
}
}
async function runActualScraping(
providerOptions: ProviderMakerOptions,
source: MetaOutput,
options: CommandLineArguments,
): Promise<any> {
if (options.fetcher === 'browser') return runBrowserScraping(providerOptions, source, options);
const providers = makeProviders(providerOptions);
if (source.type === 'embed') {
return providers.runEmbedScraper({
url: options.url,
id: source.id,
});
}
if (source.type === 'source') {
let media;
if (options.type === 'movie') {
media = await getMovieMediaDetails(options.tmdbId);
} else {
media = await getShowMediaDetails(options.tmdbId, options.season, options.episode);
}
return providers.runSourceScraper({
media,
id: source.id,
});
}
throw new Error('Invalid source type');
}
export async function runScraper(
providerOptions: ProviderMakerOptions,
source: MetaOutput,
options: CommandLineArguments,
) {
const spinnies = new Spinnies();
spinnies.add('scrape', { text: `Running ${source.name} scraper` });
try {
const result = await runActualScraping(providerOptions, source, options);
spinnies.succeed('scrape', { text: 'Done!' });
logDeepObject(result);
} catch (error) {
let message = 'Unknown error';
if (error instanceof Error) {
message = error.message;
}
spinnies.fail('scrape', { text: `ERROR: ${message}` });
console.error(error);
}
}

101
src/dev-cli/tmdb.ts Normal file
View File

@@ -0,0 +1,101 @@
import { getConfig } from '@/dev-cli/config';
import { MovieMedia, ShowMedia } from '..';
export async function makeTMDBRequest(url: string, appendToResponse?: string): Promise<Response> {
const headers: {
accept: 'application/json';
authorization?: string;
} = {
accept: 'application/json',
};
const requestURL = new URL(url);
const key = getConfig().tmdbApiKey;
// * JWT keys always start with ey and are ONLY valid as a header.
// * All other keys are ONLY valid as a query param.
// * Thanks TMDB.
if (key.startsWith('ey')) {
headers.authorization = `Bearer ${key}`;
} else {
requestURL.searchParams.append('api_key', key);
}
if (appendToResponse) {
requestURL.searchParams.append('append_to_response', appendToResponse);
}
return fetch(requestURL, {
method: 'GET',
headers,
});
}
export async function getMovieMediaDetails(id: string): Promise<MovieMedia> {
const response = await makeTMDBRequest(`https://api.themoviedb.org/3/movie/${id}`, 'external_ids');
const movie = await response.json();
if (movie.success === false) {
throw new Error(movie.status_message);
}
if (!movie.release_date) {
throw new Error(`${movie.title} has no release_date. Assuming unreleased`);
}
return {
type: 'movie',
title: movie.title,
releaseYear: Number(movie.release_date.split('-')[0]),
tmdbId: id,
imdbId: movie.imdb_id,
};
}
export async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumber: string): Promise<ShowMedia> {
// * TV shows require the TMDB ID for the series, season, and episode
// * and the name of the series. Needs multiple requests
let response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}`, 'external_ids');
const series = await response.json();
if (series.success === false) {
throw new Error(series.status_message);
}
if (!series.first_air_date) {
throw new Error(`${series.name} has no first_air_date. Assuming unaired`);
}
response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}`);
const season = await response.json();
if (season.success === false) {
throw new Error(season.status_message);
}
response = await makeTMDBRequest(
`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}/episode/${episodeNumber}`,
);
const episode = await response.json();
if (episode.success === false) {
throw new Error(episode.status_message);
}
return {
type: 'show',
title: series.name,
releaseYear: Number(series.first_air_date.split('-')[0]),
tmdbId: id,
episode: {
number: episode.episode_number,
tmdbId: episode.id,
},
season: {
number: season.season_number,
tmdbId: season.id,
},
imdbId: series.external_ids.imdb_id,
};
}

92
src/dev-cli/validate.ts Normal file
View File

@@ -0,0 +1,92 @@
import nodeFetch from 'node-fetch';
import { Embed, Sourcerer } from '@/providers/base';
import { ProviderMakerOptions, makeStandardFetcher, targets } from '..';
export type CommandLineArguments = {
fetcher: string;
sourceId: string;
tmdbId: string;
type: string;
season: string;
episode: string;
url: string;
};
export async function processOptions(sources: Array<Embed | Sourcerer>, options: CommandLineArguments) {
const fetcherOptions = ['node-fetch', 'native', 'browser'];
if (!fetcherOptions.includes(options.fetcher)) {
throw new Error(`Fetcher must be any of: ${fetcherOptions.join()}`);
}
if (!options.sourceId.trim()) {
throw new Error('Source ID must be provided');
}
const source = sources.find(({ id }) => id === options.sourceId);
if (!source) {
throw new Error('Invalid source ID. No source found');
}
if (source.type === 'embed' && !options.url.trim()) {
throw new Error('Must provide an embed URL for embed sources');
}
if (source.type === 'source') {
if (!options.tmdbId.trim()) {
throw new Error('Must provide a TMDB ID for provider sources');
}
if (Number.isNaN(Number(options.tmdbId)) || Number(options.tmdbId) < 0) {
throw new Error('TMDB ID must be a number greater than 0');
}
if (!options.type.trim()) {
throw new Error('Must provide a type for provider sources');
}
if (options.type !== 'movie' && options.type !== 'show') {
throw new Error("Invalid media type. Must be either 'movie' or 'show'");
}
if (options.type === 'show') {
if (!options.season.trim()) {
throw new Error('Must provide a season number for TV shows');
}
if (!options.episode.trim()) {
throw new Error('Must provide an episode number for TV shows');
}
if (Number.isNaN(Number(options.season)) || Number(options.season) <= 0) {
throw new Error('Season number must be a number greater than 0');
}
if (Number.isNaN(Number(options.episode)) || Number(options.episode) <= 0) {
throw new Error('Episode number must be a number greater than 0');
}
}
}
let fetcher;
if (options.fetcher === 'native') {
fetcher = makeStandardFetcher(fetch);
} else {
fetcher = makeStandardFetcher(nodeFetch);
}
const providerOptions: ProviderMakerOptions = {
fetcher,
target: targets.ANY,
consistentIpForRequests: true,
};
return {
providerOptions,
options,
source,
};
}

93
src/entrypoint/builder.ts Normal file
View File

@@ -0,0 +1,93 @@
import { ProviderControls, makeControls } from '@/entrypoint/controls';
import { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers';
import { Targets, getTargetFeatures } from '@/entrypoint/utils/targets';
import { Fetcher } from '@/fetchers/types';
import { Embed, Sourcerer } from '@/providers/base';
import { getProviders } from '@/providers/get';
export type ProviderBuilder = {
setTarget(target: Targets): ProviderBuilder;
setFetcher(fetcher: Fetcher): ProviderBuilder;
setProxiedFetcher(fetcher: Fetcher): ProviderBuilder;
addSource(scraper: Sourcerer): ProviderBuilder;
addSource(name: string): ProviderBuilder;
addEmbed(scraper: Embed): ProviderBuilder;
addEmbed(name: string): ProviderBuilder;
addBuiltinProviders(): ProviderBuilder;
enableConsistentIpForRequests(): ProviderBuilder;
build(): ProviderControls;
};
export function buildProviders(): ProviderBuilder {
let consistentIpForRequests = false;
let target: Targets | null = null;
let fetcher: Fetcher | null = null;
let proxiedFetcher: Fetcher | null = null;
const embeds: Embed[] = [];
const sources: Sourcerer[] = [];
const builtinSources = getBuiltinSources();
const builtinEmbeds = getBuiltinEmbeds();
return {
enableConsistentIpForRequests() {
consistentIpForRequests = true;
return this;
},
setFetcher(f) {
fetcher = f;
return this;
},
setProxiedFetcher(f) {
proxiedFetcher = f;
return this;
},
setTarget(t) {
target = t;
return this;
},
addSource(input) {
if (typeof input !== 'string') {
sources.push(input);
return this;
}
const matchingSource = builtinSources.find((v) => v.id === input);
if (!matchingSource) throw new Error('Source not found');
sources.push(matchingSource);
return this;
},
addEmbed(input) {
if (typeof input !== 'string') {
embeds.push(input);
return this;
}
const matchingEmbed = builtinEmbeds.find((v) => v.id === input);
if (!matchingEmbed) throw new Error('Embed not found');
embeds.push(matchingEmbed);
return this;
},
addBuiltinProviders() {
sources.push(...builtinSources);
embeds.push(...builtinEmbeds);
return this;
},
build() {
if (!target) throw new Error('Target not set');
if (!fetcher) throw new Error('Fetcher not set');
const features = getTargetFeatures(target, consistentIpForRequests);
const list = getProviders(features, {
embeds,
sources,
});
return makeControls({
fetcher,
proxiedFetcher: proxiedFetcher ?? undefined,
embeds: list.embeds,
sources: list.sources,
features,
});
},
};
}

View File

@@ -1,24 +1,19 @@
import { makeFullFetcher } from '@/fetchers/common';
import { FullScraperEvents, IndividualScraperEvents } from '@/entrypoint/utils/events';
import { ScrapeMedia } from '@/entrypoint/utils/media';
import { MetaOutput, getAllEmbedMetaSorted, getAllSourceMetaSorted, getSpecificId } from '@/entrypoint/utils/meta';
import { FeatureMap } from '@/entrypoint/utils/targets';
import { makeFetcher } from '@/fetchers/common';
import { Fetcher } from '@/fetchers/types';
import { FullScraperEvents, IndividualScraperEvents } from '@/main/events';
import { scrapeIndividualEmbed, scrapeInvidualSource } from '@/main/individualRunner';
import { ScrapeMedia } from '@/main/media';
import { MetaOutput, getAllEmbedMetaSorted, getAllSourceMetaSorted, getSpecificId } from '@/main/meta';
import { RunOutput, runAllProviders } from '@/main/runner';
import { Targets, getTargetFeatures } from '@/main/targets';
import { EmbedOutput, SourcererOutput } from '@/providers/base';
import { getProviders } from '@/providers/get';
import { Embed, EmbedOutput, Sourcerer, SourcererOutput } from '@/providers/base';
import { scrapeIndividualEmbed, scrapeInvidualSource } from '@/runners/individualRunner';
import { RunOutput, runAllProviders } from '@/runners/runner';
export interface ProviderBuilderOptions {
// fetcher, every web request gets called through here
export interface ProviderControlsInput {
fetcher: Fetcher;
// proxied fetcher, if the scraper needs to access a CORS proxy. this fetcher will be called instead
// of the normal fetcher. Defaults to the normal fetcher.
proxiedFetcher?: Fetcher;
// target of where the streams will be used
target: Targets;
features: FeatureMap;
sources: Sourcerer[];
embeds: Embed[];
}
export interface RunnerOptions {
@@ -80,13 +75,16 @@ export interface ProviderControls {
listEmbeds(): MetaOutput[];
}
export function makeProviders(ops: ProviderBuilderOptions): ProviderControls {
const features = getTargetFeatures(ops.target);
const list = getProviders(features);
export function makeControls(ops: ProviderControlsInput): ProviderControls {
const list = {
embeds: ops.embeds,
sources: ops.sources,
};
const providerRunnerOps = {
features,
fetcher: makeFullFetcher(ops.fetcher),
proxiedFetcher: makeFullFetcher(ops.proxiedFetcher ?? ops.fetcher),
features: ops.features,
fetcher: makeFetcher(ops.fetcher),
proxiedFetcher: makeFetcher(ops.proxiedFetcher ?? ops.fetcher),
};
return {

37
src/entrypoint/declare.ts Normal file
View File

@@ -0,0 +1,37 @@
import { makeControls } from '@/entrypoint/controls';
import { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers';
import { Targets, getTargetFeatures } from '@/entrypoint/utils/targets';
import { Fetcher } from '@/fetchers/types';
import { getProviders } from '@/providers/get';
export interface ProviderMakerOptions {
// fetcher, every web request gets called through here
fetcher: Fetcher;
// proxied fetcher, if the scraper needs to access a CORS proxy. this fetcher will be called instead
// of the normal fetcher. Defaults to the normal fetcher.
proxiedFetcher?: Fetcher;
// target of where the streams will be used
target: Targets;
// Set this to true, if the requests will have the same IP as
// the device that the stream will be played on
consistentIpForRequests?: boolean;
}
export function makeProviders(ops: ProviderMakerOptions) {
const features = getTargetFeatures(ops.target, ops.consistentIpForRequests ?? false);
const list = getProviders(features, {
embeds: getBuiltinEmbeds(),
sources: getBuiltinSources(),
});
return makeControls({
embeds: list.embeds,
sources: list.sources,
features,
fetcher: ops.fetcher,
proxiedFetcher: ops.proxiedFetcher,
});
}

View File

@@ -0,0 +1,10 @@
import { gatherAllEmbeds, gatherAllSources } from '@/providers/all';
import { Embed, Sourcerer } from '@/providers/base';
export function getBuiltinSources(): Sourcerer[] {
return gatherAllSources().filter((v) => !v.disabled);
}
export function getBuiltinEmbeds(): Embed[] {
return gatherAllEmbeds().filter((v) => !v.disabled);
}

View File

@@ -1,4 +1,4 @@
import { MediaTypes } from '@/main/media';
import { MediaTypes } from '@/entrypoint/utils/media';
import { Embed, Sourcerer } from '@/providers/base';
import { ProviderList } from '@/providers/get';

View File

@@ -0,0 +1,68 @@
export const flags = {
// CORS are set to allow any origin
CORS_ALLOWED: 'cors-allowed',
// the stream is locked on IP, so only works if
// request maker is same as player (not compatible with proxies)
IP_LOCKED: 'ip-locked',
// The source/embed is blocking cloudflare ip's
// This flag is not compatible with a proxy hosted on cloudflare
CF_BLOCKED: 'cf-blocked',
} as const;
export type Flags = (typeof flags)[keyof typeof flags];
export const targets = {
// browser with CORS restrictions
BROWSER: 'browser',
// browser, but no CORS restrictions through a browser extension
BROWSER_EXTENSION: 'browser-extension',
// native app, so no restrictions in what can be played
NATIVE: 'native',
// any target, no target restrictions
ANY: 'any',
} as const;
export type Targets = (typeof targets)[keyof typeof targets];
export type FeatureMap = {
requires: Flags[];
disallowed: Flags[];
};
export const targetToFeatures: Record<Targets, FeatureMap> = {
browser: {
requires: [flags.CORS_ALLOWED],
disallowed: [],
},
'browser-extension': {
requires: [],
disallowed: [],
},
native: {
requires: [],
disallowed: [],
},
any: {
requires: [],
disallowed: [],
},
};
export function getTargetFeatures(target: Targets, consistentIpForRequests: boolean): FeatureMap {
const features = targetToFeatures[target];
if (!consistentIpForRequests) features.disallowed.push(flags.IP_LOCKED);
return features;
}
export function flagsAllowedInFeatures(features: FeatureMap, inputFlags: Flags[]): boolean {
const hasAllFlags = features.requires.every((v) => inputFlags.includes(v));
if (!hasAllFlags) return false;
const hasDisallowedFlag = features.disallowed.some((v) => inputFlags.includes(v));
if (hasDisallowedFlag) return false;
return true;
}

View File

@@ -1,6 +1,7 @@
import FormData from 'form-data';
import { FetcherOptions } from '@/fetchers/types';
import { isReactNative } from '@/utils/native';
export interface SeralizedBody {
headers: Record<string, string>;
@@ -8,11 +9,20 @@ export interface SeralizedBody {
}
export function serializeBody(body: FetcherOptions['body']): SeralizedBody {
if (body === undefined || typeof body === 'string' || body instanceof URLSearchParams || body instanceof FormData)
if (body === undefined || typeof body === 'string' || body instanceof URLSearchParams || body instanceof FormData) {
if (body instanceof URLSearchParams && isReactNative()) {
return {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body.toString(),
};
}
return {
headers: {},
body,
};
}
// serialize as JSON
return {

View File

@@ -26,14 +26,18 @@ export function makeFullUrl(url: string, ops?: FullUrlOptions): string {
return parsedUrl.toString();
}
export function makeFullFetcher(fetcher: Fetcher): UseableFetcher {
return (url, ops) => {
export function makeFetcher(fetcher: Fetcher): UseableFetcher {
const newFetcher = (url: string, ops?: FetcherOptions) => {
return fetcher(url, {
headers: ops?.headers ?? {},
method: ops?.method ?? 'GET',
query: ops?.query ?? {},
baseUrl: ops?.baseUrl ?? '',
readHeaders: ops?.readHeaders ?? [],
body: ops?.body,
});
};
const output: UseableFetcher = async (url, ops) => (await newFetcher(url, ops)).body;
output.full = newFetcher;
return output;
}

View File

@@ -11,12 +11,17 @@ export type FetchOps = {
export type FetchHeaders = {
get(key: string): string | null;
set(key: string, value: string): void;
};
export type FetchReply = {
text(): Promise<string>;
json(): Promise<any>;
extraHeaders?: FetchHeaders;
extraUrl?: string;
headers: FetchHeaders;
url: string;
status: number;
};
export type FetchLike = (url: string, ops?: FetchOps | undefined) => Promise<FetchReply>;

View File

@@ -7,11 +7,32 @@ const headerMap: Record<string, string> = {
cookie: 'X-Cookie',
referer: 'X-Referer',
origin: 'X-Origin',
'user-agent': 'X-User-Agent',
'x-real-ip': 'X-X-Real-Ip',
};
const responseHeaderMap: Record<string, string> = {
'x-set-cookie': 'Set-Cookie',
};
export function makeSimpleProxyFetcher(proxyUrl: string, f: FetchLike): Fetcher {
const fetcher = makeStandardFetcher(f);
const proxiedFetch: Fetcher = async (url, ops) => {
const fetcher = makeStandardFetcher(async (a, b) => {
const res = await f(a, b);
// set extra headers that cant normally be accessed
res.extraHeaders = new Headers();
Object.entries(responseHeaderMap).forEach((entry) => {
const value = res.headers.get(entry[0]);
if (!value) return;
res.extraHeaders?.set(entry[0].toLowerCase(), value);
});
// set correct final url
res.extraUrl = res.headers.get('X-Final-Destination') ?? res.url;
return res;
});
const fullUrl = makeFullUrl(url, ops);
const headerEntries = Object.entries(ops.headers).map((entry) => {

View File

@@ -1,8 +1,20 @@
import { serializeBody } from '@/fetchers/body';
import { makeFullUrl } from '@/fetchers/common';
import { FetchLike } from '@/fetchers/fetch';
import { FetchLike, FetchReply } from '@/fetchers/fetch';
import { Fetcher } from '@/fetchers/types';
function getHeaders(list: string[], res: FetchReply): Headers {
const output = new Headers();
list.forEach((header) => {
const realHeader = header.toLowerCase();
const value = res.headers.get(realHeader);
const extraValue = res.extraHeaders?.get(realHeader);
if (!value) return;
output.set(realHeader, extraValue ?? value);
});
return output;
}
export function makeStandardFetcher(f: FetchLike): Fetcher {
const normalFetch: Fetcher = async (url, ops) => {
const fullUrl = makeFullUrl(url, ops);
@@ -17,9 +29,17 @@ export function makeStandardFetcher(f: FetchLike): Fetcher {
body: seralizedBody.body,
});
let body: any;
const isJson = res.headers.get('content-type')?.includes('application/json');
if (isJson) return res.json();
return res.text();
if (isJson) body = await res.json();
else body = await res.text();
return {
body,
finalUrl: res.extraUrl ?? res.url,
headers: getHeaders(ops.readHeaders, res),
statusCode: res.status,
};
};
return normalFetch;

View File

@@ -4,23 +4,36 @@ export type FetcherOptions = {
baseUrl?: string;
headers?: Record<string, string>;
query?: Record<string, string>;
method?: 'GET' | 'POST';
method?: 'HEAD' | 'GET' | 'POST';
readHeaders?: string[];
body?: Record<string, any> | string | FormData | URLSearchParams;
};
// Version of the options that always has the defaults set
// This is to make making fetchers yourself easier
export type DefaultedFetcherOptions = {
baseUrl?: string;
body?: Record<string, any> | string | FormData;
headers: Record<string, string>;
query: Record<string, string>;
method: 'GET' | 'POST';
readHeaders: string[];
method: 'HEAD' | 'GET' | 'POST';
};
export type Fetcher<T = any> = {
(url: string, ops: DefaultedFetcherOptions): Promise<T>;
export type FetcherResponse<T = any> = {
statusCode: number;
headers: Headers;
finalUrl: string;
body: T;
};
// this feature has some quality of life features
export type UseableFetcher<T = any> = {
(url: string, ops?: FetcherOptions): Promise<T>;
// This is the version that will be inputted by library users
export type Fetcher = {
<T = any>(url: string, ops: DefaultedFetcherOptions): Promise<FetcherResponse<T>>;
};
// This is the version that scrapers will be interacting with
export type UseableFetcher = {
<T = any>(url: string, ops?: FetcherOptions): Promise<T>;
full: <T = any>(url: string, ops?: FetcherOptions) => Promise<FetcherResponse<T>>;
};

View File

@@ -1,19 +1,21 @@
export type { EmbedOutput, SourcererOutput } from '@/providers/base';
export type { RunOutput } from '@/main/runner';
export type { MetaOutput } from '@/main/meta';
export type { FullScraperEvents } from '@/main/events';
export type { Targets, Flags } from '@/main/targets';
export type { MediaTypes, ShowMedia, ScrapeMedia, MovieMedia } from '@/main/media';
export type {
ProviderBuilderOptions,
ProviderControls,
RunnerOptions,
EmbedRunnerOptions,
SourceRunnerOptions,
} from '@/main/builder';
export type { Stream, StreamFile, FileBasedStream, HlsBasedStream, Qualities } from '@/providers/streams';
export type { Fetcher, DefaultedFetcherOptions, FetcherOptions, FetcherResponse } from '@/fetchers/types';
export type { RunOutput } from '@/runners/runner';
export type { MetaOutput } from '@/entrypoint/utils/meta';
export type { FullScraperEvents } from '@/entrypoint/utils/events';
export type { Targets, Flags } from '@/entrypoint/utils/targets';
export type { MediaTypes, ShowMedia, ScrapeMedia, MovieMedia } from '@/entrypoint/utils/media';
export type { ProviderControls, RunnerOptions, EmbedRunnerOptions, SourceRunnerOptions } from '@/entrypoint/controls';
export type { ProviderBuilder } from '@/entrypoint/builder';
export type { ProviderMakerOptions } from '@/entrypoint/declare';
export type { MovieScrapeContext, ShowScrapeContext, EmbedScrapeContext, ScrapeContext } from '@/utils/context';
export type { SourcererOptions, EmbedOptions } from '@/providers/base';
export { NotFoundError } from '@/utils/errors';
export { makeProviders } from '@/main/builder';
export { makeProviders } from '@/entrypoint/declare';
export { buildProviders } from '@/entrypoint/builder';
export { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers';
export { makeStandardFetcher } from '@/fetchers/standardFetch';
export { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
export { flags, targets } from '@/main/targets';
export { flags, targets } from '@/entrypoint/utils/targets';

View File

@@ -1,175 +0,0 @@
import { UseableFetcher } from '@/fetchers/types';
import { FullScraperEvents } from '@/main/events';
import { ScrapeMedia } from '@/main/media';
import { FeatureMap, flagsAllowedInFeatures } from '@/main/targets';
import { EmbedOutput, SourcererOutput } from '@/providers/base';
import { ProviderList } from '@/providers/get';
import { Stream } from '@/providers/streams';
import { ScrapeContext } from '@/utils/context';
import { NotFoundError } from '@/utils/errors';
import { reorderOnIdList } from '@/utils/list';
export type RunOutput = {
sourceId: string;
embedId?: string;
stream: Stream;
};
export type SourceRunOutput = {
sourceId: string;
stream?: Stream;
embeds: [];
};
export type EmbedRunOutput = {
embedId: string;
stream?: Stream;
};
export type ProviderRunnerOptions = {
fetcher: UseableFetcher;
proxiedFetcher: UseableFetcher;
features: FeatureMap;
sourceOrder?: string[];
embedOrder?: string[];
events?: FullScraperEvents;
media: ScrapeMedia;
};
export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOptions): Promise<RunOutput | null> {
const sources = reorderOnIdList(ops.sourceOrder ?? [], list.sources).filter((v) => {
if (ops.media.type === 'movie') return !!v.scrapeMovie;
if (ops.media.type === 'show') return !!v.scrapeShow;
return false;
});
const embeds = reorderOnIdList(ops.embedOrder ?? [], list.embeds);
const embedIds = embeds.map((v) => v.id);
let lastId = '';
const contextBase: ScrapeContext = {
fetcher: ops.fetcher,
proxiedFetcher: ops.proxiedFetcher,
progress(val) {
ops.events?.update?.({
id: lastId,
percentage: val,
status: 'pending',
});
},
};
ops.events?.init?.({
sourceIds: sources.map((v) => v.id),
});
for (const s of sources) {
ops.events?.start?.(s.id);
lastId = s.id;
// run source scrapers
let output: SourcererOutput | null = null;
try {
if (ops.media.type === 'movie' && s.scrapeMovie)
output = await s.scrapeMovie({
...contextBase,
media: ops.media,
});
else if (ops.media.type === 'show' && s.scrapeShow)
output = await s.scrapeShow({
...contextBase,
media: ops.media,
});
if (output?.stream && !flagsAllowedInFeatures(ops.features, output.stream.flags)) {
throw new NotFoundError("stream doesn't satisfy target feature flags");
}
} catch (err) {
if (err instanceof NotFoundError) {
ops.events?.update?.({
id: s.id,
percentage: 100,
status: 'notfound',
reason: err.message,
});
continue;
}
ops.events?.update?.({
id: s.id,
percentage: 100,
status: 'failure',
error: err,
});
continue;
}
if (!output) throw new Error('Invalid media type');
// return stream is there are any
if (output.stream) {
return {
sourceId: s.id,
stream: output.stream,
};
}
if (output.embeds.length > 0) {
ops.events?.discoverEmbeds?.({
embeds: output.embeds.map((v, i) => ({
id: [s.id, i].join('-'),
embedScraperId: v.embedId,
})),
sourceId: s.id,
});
}
// run embed scrapers on listed embeds
const sortedEmbeds = output.embeds;
sortedEmbeds.sort((a, b) => embedIds.indexOf(a.embedId) - embedIds.indexOf(b.embedId));
for (const ind in sortedEmbeds) {
if (!Object.prototype.hasOwnProperty.call(sortedEmbeds, ind)) continue;
const e = sortedEmbeds[ind];
const scraper = embeds.find((v) => v.id === e.embedId);
if (!scraper) throw new Error('Invalid embed returned');
// run embed scraper
const id = [s.id, ind].join('-');
ops.events?.start?.(id);
lastId = id;
let embedOutput: EmbedOutput;
try {
embedOutput = await scraper.scrape({
...contextBase,
url: e.url,
});
if (!flagsAllowedInFeatures(ops.features, embedOutput.stream.flags)) {
throw new NotFoundError("stream doesn't satisfy target feature flags");
}
} catch (err) {
if (err instanceof NotFoundError) {
ops.events?.update?.({
id,
percentage: 100,
status: 'notfound',
reason: err.message,
});
continue;
}
ops.events?.update?.({
id,
percentage: 100,
status: 'failure',
error: err,
});
continue;
}
return {
sourceId: s.id,
embedId: scraper.id,
stream: embedOutput.stream,
};
}
}
// no providers or embeds returns streams
return null;
}

View File

@@ -1,39 +0,0 @@
export const flags = {
NO_CORS: 'no-cors',
} as const;
export type Flags = (typeof flags)[keyof typeof flags];
export const targets = {
BROWSER: 'browser',
NATIVE: 'native',
ALL: 'all',
} as const;
export type Targets = (typeof targets)[keyof typeof targets];
export type FeatureMap = {
requires: readonly Flags[];
};
export const targetToFeatures: Record<Targets, FeatureMap> = {
browser: {
requires: [flags.NO_CORS],
},
native: {
requires: [],
},
all: {
requires: [],
},
} as const;
export function getTargetFeatures(target: Targets): FeatureMap {
return targetToFeatures[target];
}
export function flagsAllowedInFeatures(features: FeatureMap, inputFlags: Flags[]): boolean {
const hasAllFlags = features.requires.every((v) => inputFlags.includes(v));
if (!hasAllFlags) return false;
return true;
}

View File

@@ -1,22 +1,108 @@
import { Embed, Sourcerer } from '@/providers/base';
import { doodScraper } from '@/providers/embeds/dood';
import { droploadScraper } from '@/providers/embeds/dropload';
import { febboxHlsScraper } from '@/providers/embeds/febbox/hls';
import { febboxMp4Scraper } from '@/providers/embeds/febbox/mp4';
import { filelionsScraper } from '@/providers/embeds/filelions';
import { mixdropScraper } from '@/providers/embeds/mixdrop';
import { mp4uploadScraper } from '@/providers/embeds/mp4upload';
import { streambucketScraper } from '@/providers/embeds/streambucket';
import { streamsbScraper } from '@/providers/embeds/streamsb';
import { upcloudScraper } from '@/providers/embeds/upcloud';
import { upstreamScraper } from '@/providers/embeds/upstream';
import { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
import { vTubeScraper } from '@/providers/embeds/vtube';
import { flixhqScraper } from '@/providers/sources/flixhq/index';
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
import { insertunitScraper } from '@/providers/sources/insertunit';
import { kissAsianScraper } from '@/providers/sources/kissasian/index';
import { lookmovieScraper } from '@/providers/sources/lookmovie';
import { remotestreamScraper } from '@/providers/sources/remotestream';
import { superStreamScraper } from '@/providers/sources/superstream/index';
import { showboxScraper } from '@/providers/sources/showbox/index';
import { tugaflixScraper } from '@/providers/sources/tugaflix';
import { vidsrcScraper } from '@/providers/sources/vidsrc/index';
import { zoechipScraper } from '@/providers/sources/zoechip';
import { bflixScraper } from './embeds/bflix';
import { closeLoadScraper } from './embeds/closeload';
import { fileMoonScraper } from './embeds/filemoon';
import { ridooScraper } from './embeds/ridoo';
import { smashyStreamOScraper } from './embeds/smashystream/opstream';
import { smashyStreamFScraper } from './embeds/smashystream/video1';
import { streamtapeScraper } from './embeds/streamtape';
import { streamvidScraper } from './embeds/streamvid';
import { vidCloudScraper } from './embeds/vidcloud';
import { vidplayScraper } from './embeds/vidplay';
import { voeScraper } from './embeds/voe';
import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls';
import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4';
import { wootlyScraper } from './embeds/wootly';
import { goojaraScraper } from './sources/goojara';
import { hdRezkaScraper } from './sources/hdrezka';
import { nepuScraper } from './sources/nepu';
import { nitesScraper } from './sources/nites';
import { primewireScraper } from './sources/primewire';
import { ridooMoviesScraper } from './sources/ridomovies';
import { smashyStreamScraper } from './sources/smashystream';
import { soaperTvScraper } from './sources/soapertv';
import { vidSrcToScraper } from './sources/vidsrcto';
import { warezcdnScraper } from './sources/warezcdn';
export function gatherAllSources(): Array<Sourcerer> {
// all sources are gathered here
return [flixhqScraper, remotestreamScraper, kissAsianScraper, superStreamScraper, goMoviesScraper, zoechipScraper];
return [
flixhqScraper,
remotestreamScraper,
kissAsianScraper,
showboxScraper,
goMoviesScraper,
zoechipScraper,
vidsrcScraper,
lookmovieScraper,
smashyStreamScraper,
ridooMoviesScraper,
vidSrcToScraper,
nepuScraper,
goojaraScraper,
hdRezkaScraper,
primewireScraper,
warezcdnScraper,
insertunitScraper,
nitesScraper,
soaperTvScraper,
tugaflixScraper,
];
}
export function gatherAllEmbeds(): Array<Embed> {
// all embeds are gathered here
return [upcloudScraper, mp4uploadScraper, streamsbScraper, upstreamScraper, mixdropScraper];
return [
upcloudScraper,
vidCloudScraper,
mp4uploadScraper,
streamsbScraper,
upstreamScraper,
febboxMp4Scraper,
febboxHlsScraper,
mixdropScraper,
vidsrcembedScraper,
streambucketScraper,
smashyStreamFScraper,
smashyStreamOScraper,
ridooScraper,
closeLoadScraper,
fileMoonScraper,
vidplayScraper,
wootlyScraper,
doodScraper,
streamvidScraper,
voeScraper,
streamtapeScraper,
droploadScraper,
filelionsScraper,
vTubeScraper,
warezcdnembedHlsScraper,
warezcdnembedMp4Scraper,
bflixScraper,
];
}

View File

@@ -1,35 +1,52 @@
import { MovieMedia, ShowMedia } from '@/main/media';
import { Flags } from '@/main/targets';
import { Flags } from '@/entrypoint/utils/targets';
import { Stream } from '@/providers/streams';
import { EmbedScrapeContext, ScrapeContext } from '@/utils/context';
import { EmbedScrapeContext, MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
export type SourcererOutput = {
embeds: {
embedId: string;
url: string;
}[];
stream?: Stream;
export type MediaScraperTypes = 'show' | 'movie';
export type SourcererEmbed = {
embedId: string;
url: string;
};
export type Sourcerer = {
export type SourcererOutput = {
embeds: SourcererEmbed[];
stream?: Stream[];
};
export type SourcererOptions = {
id: string;
name: string; // displayed in the UI
rank: number; // the higher the number, the earlier it gets put on the queue
disabled?: boolean;
flags: Flags[];
scrapeMovie?: (input: ScrapeContext & { media: MovieMedia }) => Promise<SourcererOutput>;
scrapeShow?: (input: ScrapeContext & { media: ShowMedia }) => Promise<SourcererOutput>;
scrapeMovie?: (input: MovieScrapeContext) => Promise<SourcererOutput>;
scrapeShow?: (input: ShowScrapeContext) => Promise<SourcererOutput>;
};
export function makeSourcerer(state: Sourcerer): Sourcerer {
return state;
export type Sourcerer = SourcererOptions & {
type: 'source';
disabled: boolean;
mediaTypes: MediaScraperTypes[];
};
export function makeSourcerer(state: SourcererOptions): Sourcerer {
const mediaTypes: MediaScraperTypes[] = [];
if (state.scrapeMovie) mediaTypes.push('movie');
if (state.scrapeShow) mediaTypes.push('show');
return {
...state,
type: 'source',
disabled: state.disabled ?? false,
mediaTypes,
};
}
export type EmbedOutput = {
stream: Stream;
stream: Stream[];
};
export type Embed = {
export type EmbedOptions = {
id: string;
name: string; // displayed in the UI
rank: number; // the higher the number, the earlier it gets put on the queue
@@ -37,6 +54,17 @@ export type Embed = {
scrape: (input: EmbedScrapeContext) => Promise<EmbedOutput>;
};
export function makeEmbed(state: Embed): Embed {
return state;
export type Embed = EmbedOptions & {
type: 'embed';
disabled: boolean;
mediaTypes: undefined;
};
export function makeEmbed(state: EmbedOptions): Embed {
return {
...state,
type: 'embed',
disabled: state.disabled ?? false,
mediaTypes: undefined,
};
}

43
src/providers/captions.ts Normal file
View File

@@ -0,0 +1,43 @@
import ISO6391 from 'iso-639-1';
export const captionTypes = {
srt: 'srt',
vtt: 'vtt',
};
export type CaptionType = keyof typeof captionTypes;
export type Caption = {
type: CaptionType;
id: string; // only unique per stream
url: string;
hasCorsRestrictions: boolean;
language: string;
};
export function getCaptionTypeFromUrl(url: string): CaptionType | null {
const extensions = Object.keys(captionTypes) as CaptionType[];
const type = extensions.find((v) => url.endsWith(`.${v}`));
if (!type) return null;
return type;
}
export function labelToLanguageCode(label: string): string | null {
const code = ISO6391.getCode(label);
if (code.length === 0) return null;
return code;
}
export function isValidLanguageCode(code: string | null): boolean {
if (!code) return false;
return ISO6391.validate(code);
}
export function removeDuplicatedLanguages(list: Caption[]) {
const beenSeen: Record<string, true> = {};
return list.filter((sub) => {
if (beenSeen[sub.language]) return false;
beenSeen[sub.language] = true;
return true;
});
}

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