commit
c6e404d2af
132 changed files with 34951 additions and 0 deletions
53
README.md
Normal file
53
README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# 4Get Search
|
||||||
|
**4Get** is a proxy search engine that doesn't suck.
|
||||||
|
|
||||||
|
## About 4Get
|
||||||
|
https://4g.flossboxin.org.in/about
|
||||||
|
|
||||||
|
## Totally unbiased comparison between alternatives
|
||||||
|
|
||||||
|
| | 4Get | searx(ng) | FbIN-LibreY | araa | hearch.co |
|
||||||
|
|----------------------------|-------------------------|-----------|-------------|-----------|-------------------|
|
||||||
|
| RAM usage | 200-400mb~ | 2GB~ | 200-400mb~ | 2GB~ | idk |
|
||||||
|
| Does it suck | no (debunked by snopes) | yes | yes | a little | better than searx |
|
||||||
|
| Does it work | ye | sometimes | sometimes | sometimes | yes |
|
||||||
|
|
||||||
|
## Features
|
||||||
|
1. Rotating proxies on a per-scraper basis
|
||||||
|
2. Search filters, which SearxNG lacks for the most part
|
||||||
|
3. Bot protection that *actually* filters out the bots (when configured)
|
||||||
|
4. Interface doesn't require javascript
|
||||||
|
5. Favicon fetcher with caching support & image proxy
|
||||||
|
6. Bunch of other shits
|
||||||
|
|
||||||
|
tl;dr 4Get is the best way to browse for shit.
|
||||||
|
|
||||||
|
# Supported websites
|
||||||
|
|
||||||
|
| Web | Images | Videos | News | Music | Autocompleter |
|
||||||
|
|------------|--------------|------------|------------|------------|---------------|
|
||||||
|
| DuckDuckGo | DuckDuckGo | YouTube | DuckDuckGo | Soundcloud | Brave |
|
||||||
|
| Brave | Brave | DuckDuckGo | Brave | | DuckDuckGo |
|
||||||
|
| Yandex | Yandex | Brave | Google | | Yandex |
|
||||||
|
| Google | Google | Yandex | Startpage | | Google |
|
||||||
|
| Startpage | Startpage | Google | Qwant | | Startpage |
|
||||||
|
| Qwant | Qwant | Startpage | Mojeek | | Kagi |
|
||||||
|
| Ghostery | Yep | Qwant | | | Qwant |
|
||||||
|
| Yep | Solofield | Solofield | | | Ghostery |
|
||||||
|
| Greppr | Pinterest | | | | Yep |
|
||||||
|
| Crowdview | 500px | | | | Marginalia |
|
||||||
|
| Mwmbl | VSCO | | | | YouTube |
|
||||||
|
| Mojeek | Imgur | | | | Soundcloud |
|
||||||
|
| Solofield | FindThatMeme | | | | |
|
||||||
|
| Marginalia | | | | | |
|
||||||
|
| wiby | | | | | |
|
||||||
|
| Curlie | | | | | |
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
Refer to the <a href="https://git.flossboxin.org.in/FbIN/4Get/src/branch/main/docs/">documentation index</a>. I recommend following the <a href="https://git.flossboxin.org.in/FbIN/4Get/src/branch/main/docs/apache2.md">apache2 guide</a>.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
Shit breaks all the time but I repair it all the time too. Email me here: <b>dev(at)flossboxin(dot)org(dot)in</b> or create an issue.
|
||||||
|
|
||||||
|
## License
|
||||||
|
AGPL
|
||||||
39
about.php
Normal file
39
about.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
echo
|
||||||
|
$frontend->load(
|
||||||
|
"header_nofilters.html",
|
||||||
|
[
|
||||||
|
"title" => "About",
|
||||||
|
"class" => " class=\"about\""
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$left =
|
||||||
|
explode(
|
||||||
|
"\n",
|
||||||
|
file_get_contents("template/about.html")
|
||||||
|
);
|
||||||
|
|
||||||
|
$out = "";
|
||||||
|
|
||||||
|
foreach($left as $line){
|
||||||
|
|
||||||
|
$out .= trim($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo
|
||||||
|
$frontend->load(
|
||||||
|
"search.html",
|
||||||
|
[
|
||||||
|
"timetaken" => null,
|
||||||
|
"class" => "",
|
||||||
|
"right-left" => "",
|
||||||
|
"right-right" => "",
|
||||||
|
"left" => $out
|
||||||
|
]
|
||||||
|
);
|
||||||
27
ami4get.php
Normal file
27
ami4get.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
|
||||||
|
$real_requests = apcu_fetch("real_requests");
|
||||||
|
$bot_requests = apcu_fetch("captcha_gen");
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
"status" => "ok",
|
||||||
|
"service" => "4get",
|
||||||
|
"server" => [
|
||||||
|
"name" => config::SERVER_NAME,
|
||||||
|
"description" => config::SERVER_LONG_DESCRIPTION,
|
||||||
|
"bot_protection" => config::BOT_PROTECTION,
|
||||||
|
"real_requests" => $real_requests === false ? 0 : $real_requests,
|
||||||
|
"bot_requests" => $bot_requests === false ? 0 : $bot_requests,
|
||||||
|
"api_enabled" => config::API_ENABLED,
|
||||||
|
"alt_addresses" => config::ALT_ADDRESSES,
|
||||||
|
"version" => config::VERSION
|
||||||
|
],
|
||||||
|
"instances" => config::INSTANCES
|
||||||
|
]
|
||||||
|
);
|
||||||
357
api.txt
Normal file
357
api.txt
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
44
|
||||||
|
4444444 44
|
||||||
|
44444444 44444 444
|
||||||
|
44444444 444444 444444444
|
||||||
|
44444 44444444 444444444
|
||||||
|
444444444 4444444
|
||||||
|
4444444444 444444
|
||||||
|
4444444444444
|
||||||
|
444444444444444444
|
||||||
|
444444444444444
|
||||||
|
44444444
|
||||||
|
4444
|
||||||
|
44
|
||||||
|
|
||||||
|
+ Welcome to the 4Get API documentation +
|
||||||
|
|
||||||
|
+ Terms of use
|
||||||
|
Do NOT misuse the API. Misuses can include... ::
|
||||||
|
|
||||||
|
1. Serp SEO scanning
|
||||||
|
2. Intensive scraping
|
||||||
|
3. Any other activity that isn't triggered by a human
|
||||||
|
4. Illegal activities in Canada
|
||||||
|
5. Constant "test" queries while developping your program
|
||||||
|
(please cache the API responses!)
|
||||||
|
|
||||||
|
|
||||||
|
Examples of good uses of the API ::
|
||||||
|
|
||||||
|
1. A chatroom bot that presents users with search results
|
||||||
|
2. Personal use
|
||||||
|
3. Any other activity that is initiated by a human
|
||||||
|
|
||||||
|
|
||||||
|
If you wish to engage in the activities listed under "misuses", feel
|
||||||
|
free to download the source code of the project and running 4get
|
||||||
|
under your own terms. Please respect the terms of use listed here so
|
||||||
|
that this website may be available to all in the far future.
|
||||||
|
|
||||||
|
P.s fuck whoever botted my site for months on end, choke on my dick
|
||||||
|
lol!!!!
|
||||||
|
|
||||||
|
Get your instance running here ::
|
||||||
|
https://git.flossboxin.org.in/FbIN/4get
|
||||||
|
|
||||||
|
Thanks!
|
||||||
|
|
||||||
|
|
||||||
|
+ Passes
|
||||||
|
Depending of the instance, you may need to provide a "pass" token
|
||||||
|
in the cookies of your request. These can be obtained from solving
|
||||||
|
a captcha which will allow you to make 100 requests in the next 24
|
||||||
|
hours. In the future, you will be able to ask the serber maintainer
|
||||||
|
for a "pass" which will allow you to bypass the captcha requirement.
|
||||||
|
|
||||||
|
The captcha doesn't need javascript to work.
|
||||||
|
|
||||||
|
|
||||||
|
+ Decode the data
|
||||||
|
All payloads returned by the API are encoded in the JSON format. If
|
||||||
|
you don't know how to tackle the problem, maybe programming is not
|
||||||
|
for you.
|
||||||
|
|
||||||
|
All of the endpoints use the GET method.
|
||||||
|
|
||||||
|
|
||||||
|
+ Check if an API call was successful
|
||||||
|
All API responses come with an array index named "status". If the
|
||||||
|
status is something else than the string "ok", something went wrong.
|
||||||
|
You can supply the content of the "status" string back to your
|
||||||
|
application to inform the user of what went wrong.
|
||||||
|
|
||||||
|
The HTTP code will be 429 if your pass is invalid. It is set to 200
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
+ Get the next page of results
|
||||||
|
All API responses come with an array index named "npt". To get the
|
||||||
|
next page of results, you must make another API call with &npt.
|
||||||
|
|
||||||
|
Example ::
|
||||||
|
|
||||||
|
+ First API call
|
||||||
|
/api/v1/web?s=higurashi
|
||||||
|
|
||||||
|
+ Second API call
|
||||||
|
/api/v1/web?npt=ddg1._rJ2hWmYSjpI2hsXWmYajJx < ... >
|
||||||
|
|
||||||
|
You shouldn't specify the search term, only the &npt parameter
|
||||||
|
suffices.
|
||||||
|
|
||||||
|
The first part of the token before the dot (ddg1) refers to an
|
||||||
|
array position on the serber's memory. The second part is an
|
||||||
|
encryption key used to decode the data at that position. This way,
|
||||||
|
it is impossible to supply invalid pagination data and it is
|
||||||
|
impossible for a 4get operator to peek at the private data of the
|
||||||
|
user after a request has been made.
|
||||||
|
|
||||||
|
The tokens will expire as soon as they are used or after a 15
|
||||||
|
minutes inactivity period, whichever comes first.
|
||||||
|
|
||||||
|
|
||||||
|
+ Beware of null values!
|
||||||
|
Most fields in the API responses can return "null". You don't need
|
||||||
|
to worry about unset values.
|
||||||
|
|
||||||
|
|
||||||
|
+ API Parameters
|
||||||
|
To construct a valid request, you can use the 4get web interface
|
||||||
|
to craft a valid request, and replace "/web" with "/api/v1/web".
|
||||||
|
|
||||||
|
|
||||||
|
+ "date" and "time" parameters
|
||||||
|
"date" always refer to a calendar date.
|
||||||
|
"time" always refer to the duration of some media.
|
||||||
|
|
||||||
|
They are both integers that uses seconds as its unit. The "date"
|
||||||
|
parameter specifies the number of seconds that passed since January
|
||||||
|
1st 1970.
|
||||||
|
|
||||||
|
|
||||||
|
______ __ _ __
|
||||||
|
/ ____/___ ____/ /___ ____ (_)___ / /______
|
||||||
|
/ __/ / __ \/ __ / __ \/ __ \/ / __ \/ __/ ___/
|
||||||
|
/ /___/ / / / /_/ / /_/ / /_/ / / / / / /_(__ )
|
||||||
|
/_____/_/ /_/\__,_/ .___/\____/_/_/ /_/\__/____/
|
||||||
|
/_/
|
||||||
|
|
||||||
|
+ /ami4get
|
||||||
|
Tells you basic information about the 4get instance. CORS requests
|
||||||
|
are allowed on this endpoint.
|
||||||
|
|
||||||
|
|
||||||
|
+ /api/v1/web
|
||||||
|
+ &extendedsearch
|
||||||
|
When using the ddg(DuckDuckGo) scraper, you may make use of the
|
||||||
|
&extendedsearch parameter. If you need rich answer data from
|
||||||
|
additional sources like StackOverflow, music lyrics sites, etc.,
|
||||||
|
you need to specify the value of (string)"true".
|
||||||
|
|
||||||
|
The default value is "false" for API calls.
|
||||||
|
|
||||||
|
|
||||||
|
+ Parse the "spelling"
|
||||||
|
The array index named "spelling" contains 3 indexes ::
|
||||||
|
|
||||||
|
spelling:
|
||||||
|
type: "including"
|
||||||
|
using: "4chan"
|
||||||
|
correction: '"4cha"'
|
||||||
|
|
||||||
|
|
||||||
|
The "type" may be any of these 3 values. When rendering the
|
||||||
|
autocorrect text inside your application, it should look like
|
||||||
|
what follows right after the parameter value ::
|
||||||
|
|
||||||
|
no_correction <Empty>
|
||||||
|
including Including results for %using%. Did you mean
|
||||||
|
%correction%?
|
||||||
|
|
||||||
|
not_many Not many results for %using%. Did you mean
|
||||||
|
%correction%?
|
||||||
|
|
||||||
|
|
||||||
|
As of right now, the "spelling" is only available on
|
||||||
|
"/api/v1/web".
|
||||||
|
|
||||||
|
|
||||||
|
+ Parse the "answer"
|
||||||
|
The array index named "answer" may contain a list of multiple
|
||||||
|
answers. The array index "description" contains a linear list of
|
||||||
|
nodes that can help you construct rich formatted data inside of
|
||||||
|
your application. The structure is similar to the one below:
|
||||||
|
|
||||||
|
answer:
|
||||||
|
0:
|
||||||
|
title: "Higurashi"
|
||||||
|
description:
|
||||||
|
0:
|
||||||
|
type: "text"
|
||||||
|
value: "Higurashi is a great show!"
|
||||||
|
1:
|
||||||
|
type: "quote"
|
||||||
|
value: "Source: my ass"
|
||||||
|
|
||||||
|
|
||||||
|
Each "description" node contains an array index named "type".
|
||||||
|
Here is a list of them:
|
||||||
|
|
||||||
|
text
|
||||||
|
+ title
|
||||||
|
italic
|
||||||
|
+ quote
|
||||||
|
+ code
|
||||||
|
inline_code
|
||||||
|
link
|
||||||
|
+ image
|
||||||
|
+ audio
|
||||||
|
|
||||||
|
|
||||||
|
Each individual node prepended with a "+" should be prepended by
|
||||||
|
a newline when constructing the rendered description object.
|
||||||
|
|
||||||
|
There are some nodes that differ from the type-value format.
|
||||||
|
Please parse them accordingly ::
|
||||||
|
|
||||||
|
+ link
|
||||||
|
type: "link"
|
||||||
|
url: "https://flossboxin.org.in"
|
||||||
|
value: "Visit my website!"
|
||||||
|
|
||||||
|
|
||||||
|
+ image
|
||||||
|
type: "image"
|
||||||
|
url: "https://flossboxin.org.in/static/pixels.png"
|
||||||
|
|
||||||
|
|
||||||
|
+ audio
|
||||||
|
type: "audio"
|
||||||
|
url: "https://flossboxin.org.in/static/whatever.mp3"
|
||||||
|
|
||||||
|
|
||||||
|
The array index named "table" is an associative array. You can
|
||||||
|
loop over the data using this PHP code, for example ::
|
||||||
|
|
||||||
|
foreach($table as $website_name => $url){ // ...
|
||||||
|
|
||||||
|
|
||||||
|
The rest of the JSON is pretty self explanatory.
|
||||||
|
|
||||||
|
|
||||||
|
+ /api/v1/images
|
||||||
|
All images are contained within "image". The structure looks like
|
||||||
|
below ::
|
||||||
|
|
||||||
|
image:
|
||||||
|
0:
|
||||||
|
title: "My awesome Higurashi image"
|
||||||
|
source:
|
||||||
|
0:
|
||||||
|
url: "https://flossboxin.org.in/static/profile_pix.png"
|
||||||
|
width: 400
|
||||||
|
height: 400
|
||||||
|
1:
|
||||||
|
url: "https://flossboxin.org.in/static/pixels.png"
|
||||||
|
width: 640
|
||||||
|
height: 640
|
||||||
|
2:
|
||||||
|
url: "https://tse1.mm.bing.net/th?id=OIP.VBM3BQg
|
||||||
|
euf0-xScO1bl1UgHaGG"
|
||||||
|
width: 194
|
||||||
|
height: 160
|
||||||
|
|
||||||
|
|
||||||
|
The last image of the "source" array is always the thumbnail, and is
|
||||||
|
a good fallback to use when other sources fail to load. There can be
|
||||||
|
more than 1 source; this is especially true when using the Yandex
|
||||||
|
scraper, but beware of captcha rate limits.
|
||||||
|
|
||||||
|
|
||||||
|
+ /api/v1/videos
|
||||||
|
The "time" parameter for videos may be set to "_LIVE". For live
|
||||||
|
streams, the amount of people currently watching is passed in
|
||||||
|
"views".
|
||||||
|
|
||||||
|
|
||||||
|
+ /api/v1/news
|
||||||
|
Just make a request to "/api/v1/news?s=elon+musk". The payload
|
||||||
|
has nothing special about it and is very self explanatory, just like
|
||||||
|
the endpoint above.
|
||||||
|
|
||||||
|
|
||||||
|
+ /api/v1/music
|
||||||
|
Each entry under "song" contains a array index called "stream" that
|
||||||
|
looks like this ::
|
||||||
|
|
||||||
|
endpoint: sc
|
||||||
|
url: https://api-v2.soundcloud <...>
|
||||||
|
|
||||||
|
|
||||||
|
When the endpoint is something else than "linear", you MUST use
|
||||||
|
the specified endpoint. Otherwise, you are free to handle that
|
||||||
|
json+m3u8 crap yourself. If the endpoint is equal to "linear", the
|
||||||
|
URL should return a valid HTTP audio stream. To access the endpoint,
|
||||||
|
you must add the following prefix in your request, like so:
|
||||||
|
|
||||||
|
https://4g.flossboxin.org.in/audio/<endpoint>?s=<url>
|
||||||
|
|
||||||
|
|
||||||
|
+ /favicon
|
||||||
|
Get the favicon for a website. The only parameter is "s", and must
|
||||||
|
include the protocol for fetching in case the favicon is not cached
|
||||||
|
yet.
|
||||||
|
|
||||||
|
Example ::
|
||||||
|
|
||||||
|
/favicon?s=https://flossboxin.org.in
|
||||||
|
|
||||||
|
|
||||||
|
If we had to revert to using Google's favicon cache, it will throw
|
||||||
|
an error in the X-Error header field. If Google's favicon cache
|
||||||
|
also failed to return an image, or if you're too retarded to specify
|
||||||
|
a valid domain name, a default placeholder image will be returned
|
||||||
|
alongside the "404" HTTP error code.
|
||||||
|
|
||||||
|
|
||||||
|
+ /proxy
|
||||||
|
Get a proxied image. Useful if you don't want to leak your user's IP
|
||||||
|
address. The parameters are "i" for the image link and "s" for the
|
||||||
|
size.
|
||||||
|
|
||||||
|
Acceptable "s" parameters:
|
||||||
|
|
||||||
|
portrait 90x160
|
||||||
|
landscape 160x90
|
||||||
|
square 90x90
|
||||||
|
thumb 236x180
|
||||||
|
cover 207x270
|
||||||
|
original <Original resolution>
|
||||||
|
|
||||||
|
You can also ommit the "s" parameter if you wish to view the
|
||||||
|
original image. When an error occurs, an "X-Error" header field
|
||||||
|
is set.
|
||||||
|
|
||||||
|
|
||||||
|
+ /audio/linear
|
||||||
|
Get a proxied audio file. Does not support "Range" headers, as it's
|
||||||
|
only used to proxy small files (hence why it's called linear DUH)
|
||||||
|
|
||||||
|
The parameter is "s" for the audio link.
|
||||||
|
|
||||||
|
|
||||||
|
+ /audio/sc
|
||||||
|
Get a proxied audio file for SoundCloud. Does not support downloads
|
||||||
|
trough WGET or CURL, since it returns 30kb~160kb "206 Partial
|
||||||
|
Content" parts, due to technical limitations that comes with
|
||||||
|
converting m3u8 playlists to seekable audio files. If you use this
|
||||||
|
endpoint, you must support these 206 codes and also handle the
|
||||||
|
initial 302 HTTP redirect. I used this method as I didn't want to
|
||||||
|
store information about your request needlessly. This method also
|
||||||
|
allows noJS users to access the files.
|
||||||
|
|
||||||
|
The parameter is "s" for the SoundCloud JSON m3u8 abomination. It
|
||||||
|
does not support "normal" SoundCloud URLs at this time.
|
||||||
|
|
||||||
|
|
||||||
|
+ /audio/spotify
|
||||||
|
Get a proxied Spotify audio file. Accepts a track ID for the "s"
|
||||||
|
parameter. Will only allow you to fetch the 30 second preview since
|
||||||
|
I don't feel like fucking with cookies and accounts every fucking
|
||||||
|
living moment of my life. You must handle the initial 302 redirect
|
||||||
|
to the /audio/linear endpoint.
|
||||||
|
|
||||||
|
|
||||||
|
+ Appendix
|
||||||
|
If you have any questions or need clarifications, please send an
|
||||||
|
email my way to will at flossboxin.org.in
|
||||||
10
api/index.php
Normal file
10
api/index.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
http_response_code(404);
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
"status" => "Unknown endpoint"
|
||||||
|
]
|
||||||
|
);
|
||||||
243
api/v1/ac.php
Normal file
243
api/v1/ac.php
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include "../../data/config.php";
|
||||||
|
new autocomplete();
|
||||||
|
|
||||||
|
class autocomplete{
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
$this->scrapers = [
|
||||||
|
"brave" => "https://search.brave.com/api/suggest?q={searchTerms}",
|
||||||
|
"ddg" => "https://duckduckgo.com/ac/?q={searchTerms}&type=list",
|
||||||
|
"yandex" => "https://suggest.yandex.com/suggest-ff.cgi?part={searchTerms}&uil=en&v=3&sn=5&lr=21276&yu=4861394161661655015",
|
||||||
|
"google" => "https://www.google.com/complete/search?client=mobile-gws-lite&q={searchTerms}",
|
||||||
|
"qwant" => "https://api.qwant.com/v3/suggest/?q={searchTerms}&client=opensearch",
|
||||||
|
"yep" => "https://api.yep.com/ac/?query={searchTerms}",
|
||||||
|
"marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}",
|
||||||
|
"yt" => "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&q={searchTerms}",
|
||||||
|
"sc" => "",
|
||||||
|
"startpage" => "https://www.startpage.com/suggestions?q={searchTerms}&format=opensearch&segment=startpage.defaultffx&lui=english",
|
||||||
|
"kagi" => "https://kagi.com/api/autosuggest?q={searchTerms}",
|
||||||
|
"ghostery" => "https://ghosterysearch.com/suggest?q={searchTerms}"
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sanitize input
|
||||||
|
*/
|
||||||
|
if(!isset($_GET["s"])){
|
||||||
|
|
||||||
|
$this->do404("Missing search(s) parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_string($_GET["s"]) === false){
|
||||||
|
|
||||||
|
$this->do404("Invalid search(s) parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen($_GET["s"]) > 500){
|
||||||
|
|
||||||
|
$this->do404("Search(s) exceeds the 500 char length");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get $scraper
|
||||||
|
*/
|
||||||
|
if(!isset($_GET["scraper"])){
|
||||||
|
|
||||||
|
if(isset($_COOKIE["scraper_ac"])){
|
||||||
|
|
||||||
|
$scraper = $_COOKIE["scraper_ac"];
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$scraper = "brave"; // default option
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$scraper = $_GET["scraper"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($scraper == "disabled"){
|
||||||
|
|
||||||
|
// this shouldnt happen, but let's handle it anyways
|
||||||
|
$this->doempty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure it exists
|
||||||
|
if(!isset($this->scrapers[$scraper])){
|
||||||
|
|
||||||
|
$scraper = "brave"; // default option
|
||||||
|
}
|
||||||
|
|
||||||
|
// return results
|
||||||
|
switch($scraper){
|
||||||
|
|
||||||
|
case "google":
|
||||||
|
case "yt":
|
||||||
|
// handle google cause they want to be a special snowflake :(
|
||||||
|
$js = $this->get($this->scrapers[$scraper], $_GET["s"]);
|
||||||
|
|
||||||
|
preg_match(
|
||||||
|
'/\((\[.*\])\)/',
|
||||||
|
$js,
|
||||||
|
$js
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!isset($js[1])){
|
||||||
|
|
||||||
|
$this->doempty();
|
||||||
|
}
|
||||||
|
|
||||||
|
$js = json_decode($js[1]);
|
||||||
|
$json = [];
|
||||||
|
|
||||||
|
foreach($js[1] as $item){
|
||||||
|
|
||||||
|
$json[] = htmlspecialchars_decode(strip_tags($item[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
$_GET["s"],
|
||||||
|
$json
|
||||||
|
],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sc":
|
||||||
|
// soundcloud
|
||||||
|
chdir("../../");
|
||||||
|
include "scraper/sc.php";
|
||||||
|
$sc = new sc();
|
||||||
|
|
||||||
|
$token = $sc->get_token("raw_ip::::");
|
||||||
|
|
||||||
|
$js = $this->get(
|
||||||
|
"https://api-v2.soundcloud.com/search/queries?q={searchTerms}&client_id=" . $token . "&limit=10&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en",
|
||||||
|
$_GET["s"]
|
||||||
|
);
|
||||||
|
|
||||||
|
$js = json_decode($js, true);
|
||||||
|
|
||||||
|
if(!isset($js["collection"])){
|
||||||
|
|
||||||
|
$this->doempty();
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = [];
|
||||||
|
foreach($js["collection"] as $item){
|
||||||
|
|
||||||
|
$json[] = $item["query"];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
$_GET["s"],
|
||||||
|
$json
|
||||||
|
],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "marginalia":
|
||||||
|
$json = $this->get($this->scrapers[$scraper], $_GET["s"]);
|
||||||
|
|
||||||
|
$json = json_decode($json, true);
|
||||||
|
if($json === null){
|
||||||
|
|
||||||
|
|
||||||
|
$this->doempty();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
$_GET["s"],
|
||||||
|
$json
|
||||||
|
],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// if it respects the openSearch protocol
|
||||||
|
$json = json_decode($this->get($this->scrapers[$scraper], $_GET["s"]), true);
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
$_GET["s"],
|
||||||
|
$json[1] // ensure it contains valid key 0
|
||||||
|
],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get($url, $query){
|
||||||
|
|
||||||
|
try{
|
||||||
|
$curlproc = curl_init();
|
||||||
|
|
||||||
|
$url = str_replace("{searchTerms}", urlencode($query), $url);
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_URL, $url);
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
|
||||||
|
curl_setopt($curlproc, CURLOPT_HTTPHEADER,
|
||||||
|
["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
|
||||||
|
"Accept: application/json, text/javascript, */*; q=0.01",
|
||||||
|
"Accept-Language: en-US,en;q=0.5",
|
||||||
|
"Accept-Encoding: gzip",
|
||||||
|
"DNT: 1",
|
||||||
|
"Connection: keep-alive",
|
||||||
|
"Sec-Fetch-Dest: empty",
|
||||||
|
"Sec-Fetch-Mode: cors",
|
||||||
|
"Sec-Fetch-Site: same-site"]
|
||||||
|
);
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
|
||||||
|
curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
|
curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
|
||||||
|
curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
|
$data = curl_exec($curlproc);
|
||||||
|
|
||||||
|
if(curl_errno($curlproc)){
|
||||||
|
|
||||||
|
throw new Exception(curl_error($curlproc));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($curlproc);
|
||||||
|
return $data;
|
||||||
|
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
do404("Curl error: " . $error->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function do404($error){
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
["error" => $error],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doempty(){
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
$_GET["s"],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
39
api/v1/images.php
Normal file
39
api/v1/images.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
chdir("../../");
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
if(config::API_ENABLED === false){
|
||||||
|
|
||||||
|
echo json_encode(["status" => "The server administrator disabled the API!"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Captcha
|
||||||
|
*/
|
||||||
|
include "lib/bot_protection.php";
|
||||||
|
$null = null;
|
||||||
|
new bot_protection($null, $null, $null, "images", false);
|
||||||
|
|
||||||
|
[$scraper, $filters] = $frontend->getscraperfilters(
|
||||||
|
"images",
|
||||||
|
isset($_GET["scraper"]) ? $_GET["scraper"] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$get = $frontend->parsegetfilters($_GET, $filters);
|
||||||
|
|
||||||
|
try{
|
||||||
|
echo json_encode(
|
||||||
|
$scraper->image($get),
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(Exception $e){
|
||||||
|
|
||||||
|
echo json_encode(["status" => $e->getMessage()]);
|
||||||
|
}
|
||||||
10
api/v1/index.php
Normal file
10
api/v1/index.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
http_response_code(404);
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
"status" => "Unknown endpoint"
|
||||||
|
]
|
||||||
|
);
|
||||||
39
api/v1/music.php
Normal file
39
api/v1/music.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
chdir("../../");
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
if(config::API_ENABLED === false){
|
||||||
|
|
||||||
|
echo json_encode(["status" => "The server administrator disabled the API!"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Captcha
|
||||||
|
*/
|
||||||
|
include "lib/bot_protection.php";
|
||||||
|
$null = null;
|
||||||
|
new bot_protection($null, $null, $null, "music", false);
|
||||||
|
|
||||||
|
[$scraper, $filters] = $frontend->getscraperfilters(
|
||||||
|
"music",
|
||||||
|
isset($_GET["scraper"]) ? $_GET["scraper"] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$get = $frontend->parsegetfilters($_GET, $filters);
|
||||||
|
|
||||||
|
try{
|
||||||
|
echo json_encode(
|
||||||
|
$scraper->music($get),
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(Exception $e){
|
||||||
|
|
||||||
|
echo json_encode(["status" => $e->getMessage()]);
|
||||||
|
}
|
||||||
39
api/v1/news.php
Normal file
39
api/v1/news.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
chdir("../../");
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
if(config::API_ENABLED === false){
|
||||||
|
|
||||||
|
echo json_encode(["status" => "The server administrator disabled the API!"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Captcha
|
||||||
|
*/
|
||||||
|
include "lib/bot_protection.php";
|
||||||
|
$null = null;
|
||||||
|
new bot_protection($null, $null, $null, "news", false);
|
||||||
|
|
||||||
|
[$scraper, $filters] = $frontend->getscraperfilters(
|
||||||
|
"news",
|
||||||
|
isset($_GET["scraper"]) ? $_GET["scraper"] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$get = $frontend->parsegetfilters($_GET, $filters);
|
||||||
|
|
||||||
|
try{
|
||||||
|
echo json_encode(
|
||||||
|
$scraper->news($get),
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(Exception $e){
|
||||||
|
|
||||||
|
echo json_encode(["status" => $e->getMessage()]);
|
||||||
|
}
|
||||||
39
api/v1/videos.php
Normal file
39
api/v1/videos.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
chdir("../../");
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
if(config::API_ENABLED === false){
|
||||||
|
|
||||||
|
echo json_encode(["status" => "The server administrator disabled the API!"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Captcha
|
||||||
|
*/
|
||||||
|
include "lib/bot_protection.php";
|
||||||
|
$null = null;
|
||||||
|
new bot_protection($null, $null, $null, "videos", false);
|
||||||
|
|
||||||
|
[$scraper, $filters] = $frontend->getscraperfilters(
|
||||||
|
"videos",
|
||||||
|
isset($_GET["scraper"]) ? $_GET["scraper"] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$get = $frontend->parsegetfilters($_GET, $filters);
|
||||||
|
|
||||||
|
try{
|
||||||
|
echo json_encode(
|
||||||
|
$scraper->video($get),
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(Exception $e){
|
||||||
|
|
||||||
|
echo json_encode(["status" => $e->getMessage()]);
|
||||||
|
}
|
||||||
52
api/v1/web.php
Normal file
52
api/v1/web.php
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
chdir("../../");
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
include "data/config.php";
|
||||||
|
if(config::API_ENABLED === false){
|
||||||
|
|
||||||
|
echo json_encode(["status" => "The server administrator disabled the API!"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include "lib/frontend.php";
|
||||||
|
$frontend = new frontend();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Captcha
|
||||||
|
*/
|
||||||
|
include "lib/bot_protection.php";
|
||||||
|
$null = null;
|
||||||
|
new bot_protection($null, $null, $null, "web", false);
|
||||||
|
|
||||||
|
[$scraper, $filters] = $frontend->getscraperfilters(
|
||||||
|
"web",
|
||||||
|
isset($_GET["scraper"]) ? $_GET["scraper"] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$get = $frontend->parsegetfilters($_GET, $filters);
|
||||||
|
|
||||||
|
if(
|
||||||
|
isset($_GET["extendedsearch"]) &&
|
||||||
|
$_GET["extendedsearch"] == "yes"
|
||||||
|
){
|
||||||
|
|
||||||
|
$get["extendedsearch"] = "yes";
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$get["extendedsearch"] = "no";
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
echo
|
||||||
|
json_encode(
|
||||||
|
$scraper->web($get),
|
||||||
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(Exception $e){
|
||||||
|
|
||||||
|
echo json_encode(["status" => $e->getMessage()]);
|
||||||
|
}
|
||||||
20
audio/linear.php
Normal file
20
audio/linear.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(!isset($_GET["s"])){
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
header("X-Error: No SOUND(s) provided!");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
include "../data/config.php";
|
||||||
|
include "../lib/curlproxy.php";
|
||||||
|
$proxy = new proxy();
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$proxy->stream_linear_audio($_GET["s"]);
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
header("X-Error: " . $error->getMessage());
|
||||||
|
}
|
||||||
224
audio/sc.php
Normal file
224
audio/sc.php
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
new sc_audio();
|
||||||
|
|
||||||
|
class sc_audio{
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
|
||||||
|
include "../data/config.php";
|
||||||
|
include "../lib/curlproxy.php";
|
||||||
|
$this->proxy = new proxy();
|
||||||
|
|
||||||
|
if(isset($_GET["u"])){
|
||||||
|
|
||||||
|
/*
|
||||||
|
we're now proxying audio
|
||||||
|
*/
|
||||||
|
$viewkey = $_GET["u"];
|
||||||
|
|
||||||
|
if(!isset($_GET["r"])){
|
||||||
|
|
||||||
|
$this->do404("Ranges(r) are missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = explode(",", $_GET["r"]);
|
||||||
|
|
||||||
|
// sanitize ranges
|
||||||
|
foreach($ranges as &$range){
|
||||||
|
|
||||||
|
if(!is_numeric($range)){
|
||||||
|
|
||||||
|
$this->do404("Invalid range specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
$range = (int)$range;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort ranges (just to make sure)
|
||||||
|
sort($ranges);
|
||||||
|
|
||||||
|
// convert ranges to pairs
|
||||||
|
$last = -1;
|
||||||
|
foreach($ranges as &$r){
|
||||||
|
|
||||||
|
$tmp = $r;
|
||||||
|
$r = [$last + 1, $r];
|
||||||
|
|
||||||
|
$last = $tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
$browser_headers = getallheaders();
|
||||||
|
|
||||||
|
// get the requested range from client
|
||||||
|
$client_range = 0;
|
||||||
|
foreach($browser_headers as $key => $value){
|
||||||
|
|
||||||
|
if(strtolower($key) == "range"){
|
||||||
|
|
||||||
|
preg_match(
|
||||||
|
'/bytes=([0-9]+)/',
|
||||||
|
$value,
|
||||||
|
$client_regex
|
||||||
|
);
|
||||||
|
|
||||||
|
if(isset($client_regex[1])){
|
||||||
|
|
||||||
|
$client_range = (int)$client_regex[1];
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$client_range = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
$client_range < 0 ||
|
||||||
|
$client_range > $ranges[count($ranges) - 1][1]
|
||||||
|
){
|
||||||
|
|
||||||
|
// range is not satisfiable
|
||||||
|
http_response_code(416);
|
||||||
|
header("Content-Type: text/plain");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rng = null;
|
||||||
|
for($i=0; $i<count($ranges); $i++){
|
||||||
|
|
||||||
|
if($ranges[$i][0] <= $client_range){
|
||||||
|
|
||||||
|
$rng = $ranges[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxy data!
|
||||||
|
http_response_code(206); // partial content
|
||||||
|
header("Accept-Ranges: bytes");
|
||||||
|
header("Content-Range: bytes {$rng[0]}-{$rng[1]}/" . ($ranges[count($ranges) - 1][1] + 1));
|
||||||
|
|
||||||
|
$viewkey =
|
||||||
|
preg_replace(
|
||||||
|
'/\/media\/([0-9]+)\/[0-9]+\/[0-9]+/',
|
||||||
|
'/media/$1/' . $rng[0] . '/' . $rng[1],
|
||||||
|
$viewkey
|
||||||
|
);
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$this->proxy->stream_linear_audio(
|
||||||
|
$viewkey
|
||||||
|
);
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
$this->do404("Could not read stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
redirect user to correct resource
|
||||||
|
we need to scrape and store the byte positions in the result URL
|
||||||
|
*/
|
||||||
|
if(!isset($_GET["s"])){
|
||||||
|
|
||||||
|
$this->do404("The URL(s) parameter is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewkey = $_GET["s"];
|
||||||
|
|
||||||
|
if(
|
||||||
|
preg_match(
|
||||||
|
'/soundcloud\.com$/',
|
||||||
|
parse_url($viewkey, PHP_URL_HOST)
|
||||||
|
) === false
|
||||||
|
){
|
||||||
|
|
||||||
|
$this->do404("This endpoint can only be used for soundcloud streams");
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$json = $this->proxy->get($viewkey)["body"];
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
$this->do404("Curl error: " . $error->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = json_decode($json, true);
|
||||||
|
|
||||||
|
if(!isset($json["url"])){
|
||||||
|
|
||||||
|
$this->do404("Could not get URL from JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewkey = $json["url"];
|
||||||
|
|
||||||
|
$m3u8 = $this->proxy->get($viewkey)["body"];
|
||||||
|
|
||||||
|
$m3u8 = explode("\n", $m3u8);
|
||||||
|
|
||||||
|
$lineout = null;
|
||||||
|
$streampos_arr = [];
|
||||||
|
foreach($m3u8 as $line){
|
||||||
|
|
||||||
|
$line = trim($line);
|
||||||
|
if($line[0] == "#"){
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($lineout === null){
|
||||||
|
$lineout = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match(
|
||||||
|
'/\/media\/[0-9]+\/([0-9]+)\/([0-9]+)/',
|
||||||
|
$line,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
|
if(isset($matches[0])){
|
||||||
|
|
||||||
|
$streampos_arr[] = [
|
||||||
|
(int)$matches[1],
|
||||||
|
(int)$matches[2]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($lineout === null){
|
||||||
|
|
||||||
|
$this->do404("Could not get stream URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineout =
|
||||||
|
preg_replace(
|
||||||
|
'/\/media\/([0-9]+)\/[0-9]+\/[0-9]+/',
|
||||||
|
'/media/$1/0/0',
|
||||||
|
$lineout
|
||||||
|
);
|
||||||
|
|
||||||
|
$streampos = [];
|
||||||
|
|
||||||
|
foreach($streampos_arr as $pos){
|
||||||
|
|
||||||
|
$streampos[] = $pos[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$streampos = implode(",", $streampos);
|
||||||
|
|
||||||
|
header("Location: /audio/sc?u=" . urlencode($lineout) . "&r=$streampos");
|
||||||
|
header("Accept-Ranges: bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function do404($error){
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
header("Content-Type: text/plain");
|
||||||
|
header("X-Error: $error");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
audio/seekable.php
Normal file
20
audio/seekable.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(!isset($_GET["s"])){
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
header("X-Error: No SOUND(s) provided!");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
include "../data/config.php";
|
||||||
|
include "../lib/curlproxy.php";
|
||||||
|
$proxy = new proxy();
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$proxy->stream_linear_audio($_GET["s"]);
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
header("X-Error: " . $error->getMessage());
|
||||||
|
}
|
||||||
214
audio/spotify.php
Normal file
214
audio/spotify.php
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include "../data/config.php";
|
||||||
|
new spotify();
|
||||||
|
|
||||||
|
class spotify{
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
|
||||||
|
include "../lib/fuckhtml.php";
|
||||||
|
$this->fuckhtml = new fuckhtml();
|
||||||
|
|
||||||
|
if(
|
||||||
|
!isset($_GET["s"]) ||
|
||||||
|
!preg_match(
|
||||||
|
'/^(track|episode)\.([A-Za-z0-9]{22})$/',
|
||||||
|
$_GET["s"],
|
||||||
|
$matches
|
||||||
|
)
|
||||||
|
){
|
||||||
|
|
||||||
|
$this->do404("The track ID(s) parameter is missing or invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
if($matches[1] == "episode"){
|
||||||
|
|
||||||
|
$uri = "show";
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$uri = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$embed =
|
||||||
|
$this->get("https://embed.spotify.com/{$uri}/" . $matches[2]);
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
$this->do404("Failed to fetch embed data");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fuckhtml->load($embed);
|
||||||
|
|
||||||
|
$json =
|
||||||
|
$this->fuckhtml
|
||||||
|
->getElementById(
|
||||||
|
"__NEXT_DATA__",
|
||||||
|
"script"
|
||||||
|
);
|
||||||
|
|
||||||
|
if($json === null){
|
||||||
|
|
||||||
|
$this->do404("Failed to extract JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
$json =
|
||||||
|
json_decode($json["innerHTML"], true);
|
||||||
|
|
||||||
|
if($json === null){
|
||||||
|
|
||||||
|
$this->do404("Failed to decode JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($matches[1]){
|
||||||
|
|
||||||
|
case "track":
|
||||||
|
if(
|
||||||
|
isset(
|
||||||
|
$json
|
||||||
|
["props"]
|
||||||
|
["pageProps"]
|
||||||
|
["state"]
|
||||||
|
["data"]
|
||||||
|
["entity"]
|
||||||
|
["audioPreview"]
|
||||||
|
["url"]
|
||||||
|
)
|
||||||
|
){
|
||||||
|
|
||||||
|
header("Content-type: audio/mpeg");
|
||||||
|
header(
|
||||||
|
"Location: /audio/linear?s=" .
|
||||||
|
urlencode(
|
||||||
|
$json
|
||||||
|
["props"]
|
||||||
|
["pageProps"]
|
||||||
|
["state"]
|
||||||
|
["data"]
|
||||||
|
["entity"]
|
||||||
|
["audioPreview"]
|
||||||
|
["url"]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$this->do404("Could not extract playback URL");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "episode":
|
||||||
|
if(
|
||||||
|
isset(
|
||||||
|
$json
|
||||||
|
["props"]
|
||||||
|
["pageProps"]
|
||||||
|
["state"]
|
||||||
|
["data"]
|
||||||
|
["entity"]
|
||||||
|
["id"]
|
||||||
|
)
|
||||||
|
){
|
||||||
|
|
||||||
|
try{
|
||||||
|
$json =
|
||||||
|
$this->get(
|
||||||
|
"https://spclient.wg.spotify.com/soundfinder/v1/unauth/episode/" .
|
||||||
|
$json
|
||||||
|
["props"]
|
||||||
|
["pageProps"]
|
||||||
|
["state"]
|
||||||
|
["data"]
|
||||||
|
["entity"]
|
||||||
|
["id"] .
|
||||||
|
"/com.widevine.alpha"
|
||||||
|
);
|
||||||
|
}catch(Exception $error){
|
||||||
|
|
||||||
|
$this->do404("Failed to fetch audio resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = json_decode($json, true);
|
||||||
|
|
||||||
|
if($json === null){
|
||||||
|
|
||||||
|
$this->do404("Failed to decode audio resource JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
isset($json["passthrough"]) &&
|
||||||
|
$json["passthrough"] == "ALLOWED" &&
|
||||||
|
isset($json["passthroughUrl"])
|
||||||
|
){
|
||||||
|
|
||||||
|
header(
|
||||||
|
"Location:" .
|
||||||
|
"/audio/linear.php?s=" .
|
||||||
|
urlencode(
|
||||||
|
str_replace(
|
||||||
|
"http://",
|
||||||
|
"https://",
|
||||||
|
$json["passthroughUrl"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$this->do404("Failed to find passthroughUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$this->do404("Failed to find episode ID");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get($url){
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
"User-Agent: " . config::USER_AGENT,
|
||||||
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
|
"Accept-Language: en-US,en;q=0.5",
|
||||||
|
"Accept-Encoding: gzip",
|
||||||
|
"DNT: 1",
|
||||||
|
"Connection: keep-alive",
|
||||||
|
"Upgrade-Insecure-Requests: 1",
|
||||||
|
"Sec-Fetch-Dest: document",
|
||||||
|
"Sec-Fetch-Mode: navigate",
|
||||||
|
"Sec-Fetch-Site: none",
|
||||||
|
"Sec-Fetch-User: ?1"
|
||||||
|
];
|
||||||
|
|
||||||
|
$curlproc = curl_init();
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_URL, $url);
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
|
||||||
|
curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
|
||||||
|
curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
|
||||||
|
curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
|
curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
|
||||||
|
curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
|
$data = curl_exec($curlproc);
|
||||||
|
|
||||||
|
if(curl_errno($curlproc)){
|
||||||
|
throw new Exception(curl_error($curlproc));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($curlproc);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function do404($error){
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
header("Content-Type: text/plain");
|
||||||
|
header("X-Error: $error");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
203
captcha.php
Normal file
203
captcha.php
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(
|
||||||
|
isset($_GET["v"]) === false ||
|
||||||
|
is_array($_GET["v"]) === true ||
|
||||||
|
preg_match(
|
||||||
|
'/^c[0-9]+\.[A-Za-z0-9_]{20}$/',
|
||||||
|
$_GET["v"]
|
||||||
|
) === 0
|
||||||
|
){
|
||||||
|
|
||||||
|
http_response_code(401);
|
||||||
|
header("Content-Type: text/plain");
|
||||||
|
echo "Fuck my feathered cloaca";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
//header("Content-Type: image/jpeg");
|
||||||
|
include "data/config.php";
|
||||||
|
|
||||||
|
if(config::BOT_PROTECTION !== 1){
|
||||||
|
|
||||||
|
header("Content-Type: text/plain");
|
||||||
|
echo "The IQ test is disabled";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$grid = apcu_fetch($_GET["v"]);
|
||||||
|
|
||||||
|
if($grid !== false){
|
||||||
|
|
||||||
|
// captcha already generated
|
||||||
|
http_response_code(304); // not modified
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Content-Type: image/jpeg");
|
||||||
|
header("Last-Modified: Thu, 01 Oct 1970 00:00:00 GMT");
|
||||||
|
|
||||||
|
// ** generate captcha data
|
||||||
|
// get the positions for the answers
|
||||||
|
// will return between 3 and 6 answer positions
|
||||||
|
$range = range(0, 15);
|
||||||
|
$answer_pos = [];
|
||||||
|
|
||||||
|
array_splice($range, 0, 1);
|
||||||
|
|
||||||
|
$picks = random_int(3, 6);
|
||||||
|
|
||||||
|
for($i=0; $i<$picks; $i++){
|
||||||
|
|
||||||
|
$answer_pos_tmp =
|
||||||
|
array_splice(
|
||||||
|
$range,
|
||||||
|
random_int(
|
||||||
|
0,
|
||||||
|
14 - $i
|
||||||
|
),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
$answer_pos[] = $answer_pos_tmp[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose a dataset
|
||||||
|
$c = count(config::CAPTCHA_DATASET);
|
||||||
|
$choosen = config::CAPTCHA_DATASET[random_int(0, $c - 1)];
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
for($i=0; $i<$c; $i++){
|
||||||
|
|
||||||
|
if(config::CAPTCHA_DATASET[$i][0] == $choosen[0]){
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$choices[] = config::CAPTCHA_DATASET[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate grid data
|
||||||
|
$grid = [];
|
||||||
|
|
||||||
|
for($i=0; $i<16; $i++){
|
||||||
|
|
||||||
|
if(in_array($i, $answer_pos)){
|
||||||
|
|
||||||
|
$grid[] = $choosen;
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$grid[] = $choices[random_int(0, count($choices) - 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store grid data for form validation on captcha_gen.php
|
||||||
|
apcu_store(
|
||||||
|
$_GET["v"],
|
||||||
|
$answer_pos,
|
||||||
|
60 // we give user 1 minute to solve
|
||||||
|
);
|
||||||
|
|
||||||
|
// generate image
|
||||||
|
if(random_int(0,1) === 0){
|
||||||
|
|
||||||
|
$theme = [
|
||||||
|
"bg" => "#ebdbb2",
|
||||||
|
"fg" => "#1d2021"
|
||||||
|
];
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$theme = [
|
||||||
|
"bg" => "#1d2021",
|
||||||
|
"fg" => "#ebdbb2"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$im = new Imagick();
|
||||||
|
$im->newImage(400, 427, $theme["bg"]);
|
||||||
|
$im->setImageBackgroundColor($theme["bg"]);
|
||||||
|
$im->setImageFormat("jpg");
|
||||||
|
|
||||||
|
$noise = [
|
||||||
|
imagick::NOISE_GAUSSIAN,
|
||||||
|
imagick::NOISE_LAPLACIAN
|
||||||
|
];
|
||||||
|
|
||||||
|
$distort = [
|
||||||
|
imagick::DISTORTION_AFFINE,
|
||||||
|
imagick::DISTORTION_SHEPARDS
|
||||||
|
];
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
for($y=0; $y<4; $y++){
|
||||||
|
|
||||||
|
for($x=0; $x<4; $x++){
|
||||||
|
|
||||||
|
$tmp = new Imagick("./data/captcha/" . $grid[$i][0] . "/" . random_int(1, $grid[$i][1]) . ".png");
|
||||||
|
|
||||||
|
// convert transparency correctly
|
||||||
|
$tmp->setImageBackgroundColor("black");
|
||||||
|
$tmp->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
||||||
|
|
||||||
|
// randomly mirror
|
||||||
|
if(random_int(0,1) === 1){
|
||||||
|
|
||||||
|
$tmp->flopImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// distort $tmp
|
||||||
|
$tmp->distortImage(
|
||||||
|
$distort[random_int(0,1)],
|
||||||
|
[
|
||||||
|
0, 0,
|
||||||
|
random_int(-15, 15), random_int(-15, 15),
|
||||||
|
|
||||||
|
100, 0,
|
||||||
|
random_int(80, 120), random_int(-15, 15),
|
||||||
|
|
||||||
|
100, 100,
|
||||||
|
random_int(80, 120), random_int(80, 120),
|
||||||
|
|
||||||
|
0, 100,
|
||||||
|
random_int(-15, 15), random_int(80, 120)
|
||||||
|
],
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
$tmp->addNoiseImage($noise[random_int(0, 1)]);
|
||||||
|
|
||||||
|
// append image
|
||||||
|
$im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, ($y * 100) + 27);
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add text
|
||||||
|
$draw = new ImagickDraw();
|
||||||
|
$draw->setFontSize(20);
|
||||||
|
$draw->setFillColor($theme["fg"]);
|
||||||
|
//$draw->setTextAntialias(false);
|
||||||
|
$draw->setFont("./data/fonts/captcha.ttf");
|
||||||
|
|
||||||
|
$text = "Pick " . $picks . " images of " . str_replace("_", " ", $choosen[0]);
|
||||||
|
|
||||||
|
$pos = 200 - ($im->queryFontMetrics($draw, $text)["textWidth"] / 2);
|
||||||
|
|
||||||
|
for($i=0; $i<strlen($text); $i++){
|
||||||
|
|
||||||
|
$im->annotateImage(
|
||||||
|
$draw,
|
||||||
|
$pos,
|
||||||
|
20,
|
||||||
|
random_int(-15, 15),
|
||||||
|
$text[$i]
|
||||||
|
);
|
||||||
|
|
||||||
|
$pos += $im->queryFontMetrics($draw, $text[$i])["textWidth"];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$im->setFormat("jpeg");
|
||||||
|
$im->setImageCompressionQuality(90);
|
||||||
|
echo $im->getImageBlob();
|
||||||
157
data/config.php
Normal file
157
data/config.php
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
<?php
|
||||||
|
class config{
|
||||||
|
// Welcome to the 4Get configuration file
|
||||||
|
// When updating your instance, please make sure this file isn't missing
|
||||||
|
// any parameters.
|
||||||
|
|
||||||
|
// 4Get version. Please keep this updated
|
||||||
|
const VERSION = 8;
|
||||||
|
|
||||||
|
// Will be shown pretty much everywhere.
|
||||||
|
const SERVER_NAME = "FbIN 4Get";
|
||||||
|
|
||||||
|
// Will be shown in <meta> tag on home page
|
||||||
|
const SERVER_SHORT_DESCRIPTION = "4Get is a proxy search engine that doesn't suck.";
|
||||||
|
|
||||||
|
// Will be shown in server list ping (null for no description)
|
||||||
|
const SERVER_LONG_DESCRIPTION = FbIN;
|
||||||
|
|
||||||
|
// Add your own themes in "static/themes". Set to "Dark" for default theme.
|
||||||
|
// Eg. To use "static/themes/Cream.css", specify "Cream".
|
||||||
|
const DEFAULT_THEME = "Catppuccin Latte";
|
||||||
|
|
||||||
|
// Enable the API?
|
||||||
|
const API_ENABLED = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
// BOT PROTECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
// 0 = disabled, 1 = ask for image captcha, @TODO: 2 = invite only (users needs a pass)
|
||||||
|
// VERY useful against a targetted attack
|
||||||
|
const BOT_PROTECTION = 0;
|
||||||
|
|
||||||
|
// if BOT_PROTECTION is set to 1, specify the available datasets here
|
||||||
|
// images should be named from 1.png to X.png, and be 100x100 in size
|
||||||
|
// Eg. data/captcha/birds/1.png up to 2263.png
|
||||||
|
const CAPTCHA_DATASET = [
|
||||||
|
// example:
|
||||||
|
//["birds", 2263],
|
||||||
|
//["fumo_plushies", 1006],
|
||||||
|
//["minecraft", 848]
|
||||||
|
];
|
||||||
|
|
||||||
|
// If this regex expression matches on the user agent, it blocks the request
|
||||||
|
// Not useful at all against a targetted attack
|
||||||
|
const HEADER_REGEX = '/bot|wget|curl|python-requests|scrapy|go-http-client|ruby|yahoo|spider|qwant/i';
|
||||||
|
|
||||||
|
// Block clients who present any of the following headers in their request (SPECIFY IN !!lowercase!!)
|
||||||
|
// Eg: ["x-forwarded-for", "x-via", "forwarded-for", "via"];
|
||||||
|
// Useful for blocking *some* proxies used for botting
|
||||||
|
const FILTERED_HEADER_KEYS = [
|
||||||
|
//"x-forwarded-for",
|
||||||
|
//"x-cluster-client-ip",
|
||||||
|
//"x-client-ip",
|
||||||
|
//"x-real-ip",
|
||||||
|
//"client-ip",
|
||||||
|
//"real-ip",
|
||||||
|
//"forwarded-for",
|
||||||
|
//"forwarded-for-ip",
|
||||||
|
//"forwarded",
|
||||||
|
//"proxy-connection",
|
||||||
|
//"remote-addr",
|
||||||
|
//"via"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Block SSL ciphers used by CLI tools used for botting
|
||||||
|
// Basically a primitive version of Cloudflare's browser integrity check
|
||||||
|
// ** If curl can still access the site (with spoofed headers), please make sure you use the new apache2 config **
|
||||||
|
// https://git.flossboxin.org.in/FbIN/4Get/docs/apache2.md
|
||||||
|
const DISALLOWED_SSL = [
|
||||||
|
// "TLS_AES_256_GCM_SHA384" // used by WGET and CURL
|
||||||
|
];
|
||||||
|
|
||||||
|
// Maximal number of searches per captcha key/pass issued. Counter gets
|
||||||
|
// reset on every APCU cache clear (should happen once a day).
|
||||||
|
// Only useful when BOT_PROTECTION is NOT set to 0
|
||||||
|
const MAX_SEARCHES = 100;
|
||||||
|
|
||||||
|
// List of domains that point to your servers. Include your tor/i2p
|
||||||
|
// addresses here! Must be a valid URL. Won't affect links placed on
|
||||||
|
// the homepage.
|
||||||
|
const ALT_ADDRESSES = [
|
||||||
|
//"https://4get.alt-tld",
|
||||||
|
//"http://4getwebfrq5zr4sxugk6htxvawqehxtdgjrbcn2oslllcol2vepa23yd.onion"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Known 4Get instances. MUST use the https protocol if your instance uses
|
||||||
|
// it. Is used to generate a distributed list of instances.
|
||||||
|
// To appear in the list of an instance, contact the host and if everyone added
|
||||||
|
// eachother your serber should appear everywhere.
|
||||||
|
const INSTANCES = [
|
||||||
|
"https://4g.flossboxin.org.in",
|
||||||
|
"https://4get.ca",
|
||||||
|
"https://4get.nadeko.net",
|
||||||
|
"https://4get.silly.computer",
|
||||||
|
"https://4get.sijh.net",
|
||||||
|
"https://4get.hbubli.cc",
|
||||||
|
"https://4get.lunar.icu",
|
||||||
|
"https://4get.neco.lol",
|
||||||
|
"https://4get.plunked.party/",
|
||||||
|
"https://4get.ch",
|
||||||
|
"https://4get.edmateo.site",
|
||||||
|
"https://4get.sudovanilla.org",
|
||||||
|
"https://search.mint.lgbt"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Default user agent to use for scraper requests. Sometimes ignored to get specific webpages
|
||||||
|
// Changing this might break things.
|
||||||
|
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0";
|
||||||
|
|
||||||
|
// Proxy pool assignments for each scraper
|
||||||
|
// false = Use server's raw IP
|
||||||
|
// string = will load a proxy list from data/proxies
|
||||||
|
// Eg. "onion" will load data/proxies/onion.txt
|
||||||
|
const PROXY_DDG = false; // duckduckgo
|
||||||
|
const PROXY_BRAVE = false;
|
||||||
|
const PROXY_FB = false; // facebook
|
||||||
|
const PROXY_GOOGLE = false;
|
||||||
|
const PROXY_GOOGLE_CSE = false;
|
||||||
|
const PROXY_STARTPAGE = false;
|
||||||
|
const PROXY_QWANT = false;
|
||||||
|
const PROXY_GHOSTERY = false;
|
||||||
|
const PROXY_MARGINALIA = false;
|
||||||
|
const PROXY_MOJEEK = false;
|
||||||
|
const PROXY_SC = false; // soundcloud
|
||||||
|
const PROXY_SPOTIFY = false;
|
||||||
|
const PROXY_SOLOFIELD = false;
|
||||||
|
const PROXY_WIBY = false;
|
||||||
|
const PROXY_CURLIE = false;
|
||||||
|
const PROXY_YT = false; // youtube
|
||||||
|
const PROXY_YEP = false;
|
||||||
|
const PROXY_PINTEREST = false;
|
||||||
|
const PROXY_FIVEHPX = false;
|
||||||
|
const PROXY_VSCO = false;
|
||||||
|
const PROXY_SEZNAM = false;
|
||||||
|
const PROXY_NAVER = false;
|
||||||
|
const PROXY_GREPPR = false;
|
||||||
|
const PROXY_CROWDVIEW = false;
|
||||||
|
const PROXY_MWMBL = false;
|
||||||
|
const PROXY_FTM = false; // findthatmeme
|
||||||
|
const PROXY_IMGUR = false;
|
||||||
|
const PROXY_YANDEX_W = false; // yandex web
|
||||||
|
const PROXY_YANDEX_I = false; // yandex images
|
||||||
|
const PROXY_YANDEX_V = false; // yandex videos
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scraper-specific parameters
|
||||||
|
//
|
||||||
|
|
||||||
|
// GOOGLE CSE
|
||||||
|
const GOOGLE_CX_ENDPOINT = "d4e68b99b876541f0";
|
||||||
|
|
||||||
|
// MARGINALIA
|
||||||
|
// Use "null" to default out to HTML scraping OR specify a string to
|
||||||
|
// use the API (Eg: "public"). API has less filters.
|
||||||
|
const MARGINALIA_API_KEY = null;
|
||||||
|
}
|
||||||
BIN
data/fonts/captcha.ttf
Normal file
BIN
data/fonts/captcha.ttf
Normal file
Binary file not shown.
3
data/proxies/.gitignore
vendored
Normal file
3
data/proxies/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!onion.txt
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue