forked from lolcat/4get
boobs
This commit is contained in:
parent
edc917f5ee
commit
addc5a14a9
31
api.txt
31
api.txt
|
@ -267,20 +267,23 @@
|
|||
Each entry under "song" contains a array index called "stream" that
|
||||
looks like this ::
|
||||
|
||||
endpoint: audio_sc
|
||||
endpoint: sc
|
||||
url: https://api-v2.soundcloud <...>
|
||||
|
||||
|
||||
When the endpoint is "audio_sc", you MUST use 4get's audio_sc
|
||||
endpoint, for example, if you want an audio stream back. Otherwise,
|
||||
you are free to handle the json+m3u8 crap yourself. If the endpoint
|
||||
is equal to "audio", that URL SHOULD return a valid HTTP audio
|
||||
stream, and using the "audio" endpoint becomes optional again.
|
||||
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://4get.ca/audio/<endpoint>?s=<url>
|
||||
|
||||
|
||||
+ /favicon
|
||||
Get the favicon for a website. The only parameter is "s", and must
|
||||
include the protocol.
|
||||
include the protocol for fetching in case the favicon is not cached
|
||||
yet.
|
||||
|
||||
Example ::
|
||||
|
||||
|
@ -313,14 +316,14 @@
|
|||
is set.
|
||||
|
||||
|
||||
+ /audio
|
||||
+ /audio/linear
|
||||
Get a proxied audio file. Does not support "Range" headers, as it's
|
||||
only used to proxy small files.
|
||||
only used to proxy small files (hence why it's called linear DUH)
|
||||
|
||||
The parameter is "s" for the audio link.
|
||||
|
||||
|
||||
+ /audio_sc
|
||||
+ /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
|
||||
|
@ -334,6 +337,14 @@
|
|||
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 lolcat.ca
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
|
||||
new sc_audio();
|
||||
|
||||
class sc_audio{
|
||||
|
||||
public function __construct(){
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
128
captcha.php
128
captcha.php
|
@ -1,47 +1,104 @@
|
|||
<?php
|
||||
|
||||
if(
|
||||
!isset($_GET["k"]) ||
|
||||
isset($_GET["v"]) === false ||
|
||||
is_array($_GET["v"]) === true ||
|
||||
preg_match(
|
||||
'/^c\.[0-9]+$/',
|
||||
$_GET["k"]
|
||||
)
|
||||
'/^c[0-9]+\.[A-Za-z0-9_]{20}$/',
|
||||
$_GET["v"]
|
||||
) === 0
|
||||
){
|
||||
|
||||
http_response_code(401);
|
||||
header("Content-Type: text/plain");
|
||||
echo "Fuck you";
|
||||
echo "Fuck my feathered cloaca";
|
||||
die();
|
||||
}
|
||||
|
||||
header("Content-Type: image/jpeg");
|
||||
//header("Content-Type: image/jpeg");
|
||||
include "data/config.php";
|
||||
|
||||
$grid = apcu_fetch($_GET["k"]);
|
||||
|
||||
if(
|
||||
$grid === false ||
|
||||
$grid[3] === true // has already been generated
|
||||
){
|
||||
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");
|
||||
|
||||
// only generate one captcha with this config
|
||||
// ** 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["k"],
|
||||
[
|
||||
$grid[0],
|
||||
$grid[1],
|
||||
$grid[2],
|
||||
true // has captcha been generated?
|
||||
],
|
||||
120 // we give user another 2 minutes to solve
|
||||
$_GET["v"],
|
||||
$answer_pos,
|
||||
60 // we give user 1 minute to solve
|
||||
);
|
||||
|
||||
// generate image
|
||||
|
||||
if(random_int(0,1) === 0){
|
||||
|
||||
$theme = [
|
||||
|
@ -57,7 +114,7 @@ if(random_int(0,1) === 0){
|
|||
}
|
||||
|
||||
$im = new Imagick();
|
||||
$im->newImage(400, 400, $theme["bg"]);
|
||||
$im->newImage(400, 427, $theme["bg"]);
|
||||
$im->setImageBackgroundColor($theme["bg"]);
|
||||
$im->setImageFormat("jpg");
|
||||
|
||||
|
@ -76,12 +133,18 @@ for($y=0; $y<4; $y++){
|
|||
|
||||
for($x=0; $x<4; $x++){
|
||||
|
||||
$tmp = new Imagick("./data/captcha/" . $grid[0][$i][0] . "/" . random_int(1, $grid[0][$i][1]) . ".png");
|
||||
$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)],
|
||||
|
@ -101,21 +164,15 @@ for($y=0; $y<4; $y++){
|
|||
false
|
||||
);
|
||||
|
||||
$tmp->addNoiseImage($noise[random_int(0, 1)]);
|
||||
|
||||
// append image
|
||||
$im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, $y * 100);
|
||||
$im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, ($y * 100) + 27);
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
// add noise
|
||||
$im->addNoiseImage($noise[random_int(0, 1)]);
|
||||
|
||||
// expand top of image
|
||||
$im->setImageGravity(Imagick::GRAVITY_SOUTH);
|
||||
$im->chopImage(0, -27, 400, 400);
|
||||
$im->extentImage(0, 0, 0, -27);
|
||||
|
||||
// add text
|
||||
$draw = new ImagickDraw();
|
||||
$draw->setFontSize(20);
|
||||
|
@ -123,7 +180,7 @@ $draw->setFillColor($theme["fg"]);
|
|||
//$draw->setTextAntialias(false);
|
||||
$draw->setFont("./data/captcha/font.ttf");
|
||||
|
||||
$text = "Pick " . $grid[1] . " images of " . str_replace("_", " ", $grid[2]);
|
||||
$text = "Pick " . $picks . " images of " . str_replace("_", " ", $choosen[0]);
|
||||
|
||||
$pos = 200 - ($im->queryFontMetrics($draw, $text)["textWidth"] / 2);
|
||||
|
||||
|
@ -143,5 +200,4 @@ for($i=0; $i<strlen($text); $i++){
|
|||
|
||||
$im->setFormat("jpeg");
|
||||
$im->setImageCompressionQuality(90);
|
||||
$im->setImageCompression(Imagick::COMPRESSION_JPEG2000);
|
||||
echo $im->getImageBlob();
|
||||
|
|
|
@ -5,7 +5,7 @@ class config{
|
|||
// any parameters.
|
||||
|
||||
// 4get version. Please keep this updated
|
||||
const VERSION = 6;
|
||||
const VERSION = 7;
|
||||
|
||||
// Will be shown pretty much everywhere.
|
||||
const SERVER_NAME = "4get";
|
||||
|
@ -24,10 +24,10 @@ class config{
|
|||
const API_ENABLED = true;
|
||||
|
||||
// Bot protection
|
||||
// 4get.ca has been hit with 250k bot reqs every single day for months
|
||||
// 4get.ca has been hit with 500k bot reqs every single day for months
|
||||
// you probably want to enable this if your instance is public...
|
||||
// 0 = disabled
|
||||
// 1 = ask for image captcha (requires image dataset & imagick 6.9.11-60)
|
||||
// 1 = ask for image captcha (requires imagemagick v6 or higher)
|
||||
// @TODO: 2 = invite only (users needs a pass)
|
||||
const BOT_PROTECTION = 0;
|
||||
|
||||
|
@ -62,20 +62,27 @@ class config{
|
|||
"https://4get.zzls.xyz",
|
||||
"https://4getus.zzls.xyz",
|
||||
"https://4get.silly.computer",
|
||||
"https://4g.opnxng.com",
|
||||
"https://4get.konakona.moe",
|
||||
"https://4get.lvkaszus.pl",
|
||||
"https://4g.ggtyler.dev",
|
||||
"https://4get.perennialte.ch",
|
||||
"https://4get.sihj.net",
|
||||
"https://4get.sijh.net",
|
||||
"https://4get.hbubli.cc",
|
||||
"https://4get.plunked.party",
|
||||
"https://4get.seitan-ayoub.lol"
|
||||
"https://4get.seitan-ayoub.lol",
|
||||
"https://4get.etenie.pl",
|
||||
"https://4get.lunar.icu",
|
||||
"https://4get.dcs0.hu",
|
||||
"https://4get.kizuki.lol",
|
||||
"https://4get.psily.garden",
|
||||
"https://search.milivojevic.in.rs",
|
||||
"https://4get.snine.nl",
|
||||
"https://4get.datura.network"
|
||||
];
|
||||
|
||||
// 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:109.0) Gecko/20100101 Firefox/120.0";
|
||||
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0";
|
||||
|
||||
// Proxy pool assignments for each scraper
|
||||
// false = Use server's raw IP
|
||||
|
@ -94,6 +101,8 @@ class config{
|
|||
const PROXY_YT = false; // youtube
|
||||
const PROXY_YEP = false;
|
||||
const PROXY_PINTEREST = false;
|
||||
const PROXY_SEZNAM = false;
|
||||
const PROXY_NAVER = false;
|
||||
const PROXY_FTM = false; // findthatmeme
|
||||
const PROXY_IMGUR = false;
|
||||
const PROXY_YANDEX_W = false; // yandex web
|
||||
|
@ -107,8 +116,8 @@ class config{
|
|||
// SOUNDCLOUD
|
||||
// Get these parameters by making a search on soundcloud with network
|
||||
// tab open, then filter URLs using "search?q=". (No need to login)
|
||||
const SC_USER_ID = "361066-632137-891392-693457";
|
||||
const SC_CLIENT_TOKEN = "nUB9ZvnjRiqKF43CkKf3iu69D8bboyKY";
|
||||
const SC_USER_ID = "59333-426459-717969-168008";
|
||||
const SC_CLIENT_TOKEN = "8BBZpqUP1KSN4W6YB64xog2PX4Dw98b1";
|
||||
|
||||
// MARGINALIA
|
||||
// Get an API key by contacting the Marginalia.nu maintainer. The "public" key
|
||||
|
|
|
@ -15,10 +15,11 @@ $get = $frontend->parsegetfilters($_GET, $filters);
|
|||
/*
|
||||
Captcha
|
||||
*/
|
||||
include "lib/captcha_gen.php";
|
||||
new captcha($frontend, $get, $filters, "images", true);
|
||||
include "lib/bot_protection.php";
|
||||
new bot_protection($frontend, $get, $filters, "images", true);
|
||||
|
||||
$payload = [
|
||||
"timetaken" => microtime(true),
|
||||
"images" => "",
|
||||
"nextpage" => ""
|
||||
];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
class captcha{
|
||||
class bot_protection{
|
||||
|
||||
public function __construct($frontend, $get, $filters, $page, $output){
|
||||
|
||||
|
@ -26,7 +26,7 @@ class captcha{
|
|||
if(
|
||||
// check if key is not malformed
|
||||
preg_match(
|
||||
'/^c[0-9]+\.[A-Za-z0-9]{20}$/',
|
||||
'/^k[0-9]+\.[A-Za-z0-9_]{20}$/',
|
||||
$_COOKIE["pass"]
|
||||
) &&
|
||||
// does key exist
|
||||
|
@ -39,7 +39,7 @@ class captcha{
|
|||
// we start counting from 1
|
||||
// when it has been incremented to 102, it has reached
|
||||
// 100 reqs
|
||||
if($inc >= 102){
|
||||
if($inc >= config::MAX_SEARCHES + 2){
|
||||
|
||||
// reached limit, delete and give captcha
|
||||
apcu_delete($_COOKIE["pass"]);
|
||||
|
@ -62,7 +62,7 @@ class captcha{
|
|||
|
||||
if($output === false){
|
||||
|
||||
http_response_code(429); // too many reqs
|
||||
http_response_code(401); // forbidden
|
||||
echo json_encode([
|
||||
"status" => "The \"pass\" token in your cookies is missing or has expired!!"
|
||||
]);
|
||||
|
@ -104,10 +104,13 @@ class captcha{
|
|||
!isset($regex[0][1])
|
||||
){
|
||||
|
||||
// check if its k
|
||||
// check if its the v key
|
||||
if(
|
||||
$line[0] == "k" &&
|
||||
strpos($line[1], "c.") === 0
|
||||
$line[0] == "v" &&
|
||||
preg_match(
|
||||
'/^c[0-9]+\.[A-Za-z0-9_]{20}$/',
|
||||
$line[1]
|
||||
)
|
||||
){
|
||||
|
||||
$key = apcu_fetch($line[1]);
|
||||
|
@ -129,27 +132,21 @@ class captcha{
|
|||
|
||||
$answers[] = $regex;
|
||||
}
|
||||
|
||||
|
||||
if(
|
||||
!$invalid &&
|
||||
$key !== false
|
||||
$key !== false // has captcha been gen'd?
|
||||
){
|
||||
$check = $key[1];
|
||||
$check = count($key);
|
||||
|
||||
// validate answer
|
||||
for($i=0; $i<count($key[0]); $i++){
|
||||
for($i=0; $i<count($answers); $i++){
|
||||
|
||||
if(!in_array($i, $answers)){
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if($key[0][$i][0] == $key[2]){
|
||||
if(in_array($answers[$i], $key)){
|
||||
|
||||
$check--;
|
||||
}else{
|
||||
|
||||
// got a wrong answer
|
||||
$check = -1;
|
||||
break;
|
||||
}
|
||||
|
@ -160,21 +157,8 @@ class captcha{
|
|||
// we passed the captcha
|
||||
// set cookie
|
||||
$inc = apcu_inc("cookie");
|
||||
$chars =
|
||||
array_merge(
|
||||
range("A", "Z"),
|
||||
range("a", "z"),
|
||||
range(0, 9)
|
||||
);
|
||||
|
||||
$c = count($chars) - 1;
|
||||
|
||||
$key = "c" . $inc . ".";
|
||||
|
||||
for($i=0; $i<20; $i++){
|
||||
|
||||
$key .= $chars[random_int(0, $c)];
|
||||
}
|
||||
$key = "k" . $inc . "." . $this->randomchars();
|
||||
|
||||
apcu_inc($key, 1, $stupid, 86400);
|
||||
|
||||
|
@ -203,84 +187,23 @@ class captcha{
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
for($i=0; $i<random_int(3, 6); $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)];
|
||||
}
|
||||
}
|
||||
|
||||
$key = "c." . apcu_inc("captcha_gen", 1) . "." . random_int(0, 100000000);
|
||||
|
||||
apcu_store(
|
||||
$key,
|
||||
[
|
||||
$grid,
|
||||
count($answer_pos),
|
||||
$choosen[0],
|
||||
false // has captcha been generated?
|
||||
],
|
||||
120 // we give user 2 minutes to get captcha, in case of network error
|
||||
);
|
||||
$key = "c" . apcu_inc("captcha_gen", 1) . "." . $this->randomchars();
|
||||
|
||||
$payload = [
|
||||
"timetaken" => microtime(true),
|
||||
"class" => "",
|
||||
"right-left" => "",
|
||||
"right-right" => "",
|
||||
"left" =>
|
||||
'<div class="infobox">' .
|
||||
'<h1>IQ test</h1>' .
|
||||
'Due to getting hit with 20,000 bot requests per day, I had to put this up. Sorry.<br><br>' .
|
||||
'Solving this captcha will allow you to make 100 searches today. I will add a way for legit users to bypass the captcha later. Sorry /g/tards!!' .
|
||||
'IQ test has been enabled due to bot abuse on the network.<br>' .
|
||||
'Solving this IQ test will let you make 100 searches today. I will add an invite system to bypass this soon...' .
|
||||
$error .
|
||||
'<form method="POST" enctype="text/plain" autocomplete="off">' .
|
||||
'<div class="captcha-wrapper">' .
|
||||
'<div class="captcha">' .
|
||||
'<img src="captcha?k=' . $key . '" alt="Captcha image">' .
|
||||
'<img src="captcha.php?v=' . $key . '" alt="Captcha image">' .
|
||||
'<div class="captcha-controls">' .
|
||||
'<input type="checkbox" name="c[0]" id="c0">' .
|
||||
'<label for="c0"></label>' .
|
||||
|
@ -317,13 +240,12 @@ class captcha{
|
|||
'</div>' .
|
||||
'</div>' .
|
||||
'</div>' .
|
||||
'<input type="hidden" name="k" value="' . $key . '">' .
|
||||
'<input type="hidden" name="v" value="' . $key . '">' .
|
||||
'<input type="submit" value="Check IQ" class="captcha-submit">' .
|
||||
'</form>' .
|
||||
'</div>'
|
||||
];
|
||||
|
||||
http_response_code(429); // too many reqs
|
||||
$frontend->loadheader(
|
||||
$get,
|
||||
$filters,
|
||||
|
@ -333,4 +255,27 @@ class captcha{
|
|||
echo $frontend->load("search.html", $payload);
|
||||
die();
|
||||
}
|
||||
|
||||
private function randomchars(){
|
||||
|
||||
$chars =
|
||||
array_merge(
|
||||
range("A", "Z"),
|
||||
range("a", "z"),
|
||||
range(0, 9)
|
||||
);
|
||||
|
||||
$chars[] = "_";
|
||||
|
||||
$c = count($chars) - 1;
|
||||
|
||||
$key = "";
|
||||
|
||||
for($i=0; $i<20; $i++){
|
||||
|
||||
$key .= $chars[random_int(0, $c)];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
BIN
lib/classic.png
BIN
lib/classic.png
Binary file not shown.
Before Width: | Height: | Size: 358 B |
|
@ -39,6 +39,14 @@ class frontend{
|
|||
$replacements["ac"] = '';
|
||||
}
|
||||
|
||||
if(
|
||||
isset($replacements["timetaken"]) &&
|
||||
$replacements["timetaken"] !== null
|
||||
){
|
||||
|
||||
$replacements["timetaken"] = '<div class="timetaken">Took ' . substr(microtime(true) - $replacements["timetaken"], 0, 4) . 's</div>';
|
||||
}
|
||||
|
||||
$handle = fopen("template/{$template}", "r");
|
||||
$data = fread($handle, filesize("template/{$template}"));
|
||||
fclose($handle);
|
||||
|
@ -68,7 +76,7 @@ class frontend{
|
|||
|
||||
echo
|
||||
$this->load("header.html", [
|
||||
"title" => trim($get["s"] . " ({$page})"),
|
||||
"title" => trim(htmlspecialchars($get["s"]) . " ({$page})"),
|
||||
"description" => ucfirst($page) . ' search results for "' . htmlspecialchars($get["s"]) . '"',
|
||||
"index" => "no",
|
||||
"search" => htmlspecialchars($get["s"]),
|
||||
|
@ -88,7 +96,7 @@ class frontend{
|
|||
|
||||
$this->drawerror(
|
||||
"Tshh, blocked!",
|
||||
'You were blocked from viewing this page. If you wish to scrape data from 4get, please consider running <a href="https://git.lolcat.ca/lolcat/4get" rel="noreferrer nofollow">your own 4get instance</a> or using <a href="/api.txt">the API</a>.',
|
||||
'You were blocked from viewing this page. If you wish to scrape data from 4get, please consider running <a href="https://git.lolcat.ca/lolcat/4get" rel="noreferrer nofollow">your own 4get instance</a>.',
|
||||
);
|
||||
die();
|
||||
}
|
||||
|
@ -98,6 +106,7 @@ class frontend{
|
|||
|
||||
echo
|
||||
$this->load("search.html", [
|
||||
"timetaken" => null,
|
||||
"class" => "",
|
||||
"right-left" => "",
|
||||
"right-right" => "",
|
||||
|
|
|
@ -466,19 +466,26 @@ class fuckhtml{
|
|||
|
||||
return
|
||||
preg_replace_callback(
|
||||
'/\\\u[A-Fa-f0-9]{4}|\\\x[A-Fa-f0-9]{2}/',
|
||||
'/\\\u[A-Fa-f0-9]{4}|\\\x[A-Fa-f0-9]{2}|\\\n|\\\r/',
|
||||
function($match){
|
||||
|
||||
if($match[0][1] == "u"){
|
||||
switch($match[0][1]){
|
||||
|
||||
return json_decode('"' . $match[0] . '"');
|
||||
}else{
|
||||
case "u":
|
||||
return json_decode('"' . $match[0] . '"');
|
||||
break;
|
||||
|
||||
return mb_convert_encoding(
|
||||
stripcslashes($match[0]),
|
||||
"utf-8",
|
||||
"windows-1252"
|
||||
);
|
||||
case "x":
|
||||
return mb_convert_encoding(
|
||||
stripcslashes($match[0]),
|
||||
"utf-8",
|
||||
"windows-1252"
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
return " ";
|
||||
break;
|
||||
}
|
||||
},
|
||||
$string
|
||||
|
|
47
music.php
47
music.php
|
@ -15,10 +15,11 @@ $get = $frontend->parsegetfilters($_GET, $filters);
|
|||
/*
|
||||
Captcha
|
||||
*/
|
||||
include "lib/captcha_gen.php";
|
||||
new captcha($frontend, $get, $filters, "music", true);
|
||||
include "lib/bot_protection.php";
|
||||
new bot_protection($frontend, $get, $filters, "music", true);
|
||||
|
||||
$payload = [
|
||||
"timetaken" => microtime(true),
|
||||
"class" => "",
|
||||
"right-left" => "",
|
||||
"right-right" => "",
|
||||
|
@ -36,7 +37,10 @@ try{
|
|||
$categories = [
|
||||
"song" => "",
|
||||
"author" => "",
|
||||
"playlist" => ""
|
||||
"playlist" => "",
|
||||
"album" => "",
|
||||
"podcast" => "",
|
||||
"user" => ""
|
||||
];
|
||||
|
||||
/*
|
||||
|
@ -48,14 +52,26 @@ if(count($results["song"]) !== 0){
|
|||
|
||||
$main = "song";
|
||||
|
||||
}elseif(count($results["author"]) !== 0){
|
||||
}elseif(count($results["album"]) !== 0){
|
||||
|
||||
$main = "author";
|
||||
$main = "album";
|
||||
|
||||
}elseif(count($results["playlist"]) !== 0){
|
||||
|
||||
$main = "playlist";
|
||||
|
||||
}elseif(count($results["podcast"]) !== 0){
|
||||
|
||||
$main = "podcast";
|
||||
|
||||
}elseif(count($results["author"]) !== 0){
|
||||
|
||||
$main = "author";
|
||||
|
||||
}elseif(count($results["user"]) !== 0){
|
||||
|
||||
$main = "user";
|
||||
|
||||
}else{
|
||||
|
||||
// No results found!
|
||||
|
@ -133,12 +149,15 @@ foreach($categories as $name => $data){
|
|||
$customhtml = null;
|
||||
|
||||
if(
|
||||
$name == "song" &&
|
||||
(
|
||||
$name == "song" ||
|
||||
$name == "podcast"
|
||||
) &&
|
||||
$item["stream"]["endpoint"] !== null
|
||||
){
|
||||
|
||||
$customhtml =
|
||||
'<audio src="' . $item["stream"]["endpoint"] . '?s=' . urlencode($item["stream"]["url"]) . '" controls autostart="false" preload="none">';
|
||||
'<audio src="/audio/' . $item["stream"]["endpoint"] . '?s=' . urlencode($item["stream"]["url"]) . '" controls autostart="false" preload="none">';
|
||||
}
|
||||
|
||||
$categories[$name] .= $frontend->drawtextresult($item, $greentext, $duration, $get["s"], $tabindex, $customhtml);
|
||||
|
@ -177,18 +196,8 @@ foreach($categories as $name => $value){
|
|||
'<div class="answer-title">' .
|
||||
'<a class="answer-title" href="?s=' . urlencode($get["s"]);
|
||||
|
||||
switch($name){
|
||||
|
||||
case "playlist":
|
||||
$payload[$write] .=
|
||||
'&type=playlist"><h2>Playlists</h2></a>';
|
||||
break;
|
||||
|
||||
case "author":
|
||||
$payload[$write] .=
|
||||
'&type=people"><h2>Authors</h2></a>';
|
||||
break;
|
||||
}
|
||||
$payload[$write] .=
|
||||
'&type=' . $name . '"><h2>' . ucfirst($name) . 's</h2></a>';
|
||||
|
||||
$payload[$write] .=
|
||||
'</div>' .
|
||||
|
|
5
news.php
5
news.php
|
@ -15,10 +15,11 @@ $get = $frontend->parsegetfilters($_GET, $filters);
|
|||
/*
|
||||
Captcha
|
||||
*/
|
||||
include "lib/captcha_gen.php";
|
||||
new captcha($frontend, $get, $filters, "news", true);
|
||||
include "lib/bot_protection.php";
|
||||
new bot_protection($frontend, $get, $filters, "news", true);
|
||||
|
||||
$payload = [
|
||||
"timetaken" => microtime(true),
|
||||
"class" => "",
|
||||
"right-left" => "",
|
||||
"right-right" => "",
|
||||
|
|
|
@ -5,7 +5,7 @@ include "data/config.php";
|
|||
|
||||
$domain =
|
||||
htmlspecialchars(
|
||||
(strpos(strtolower($_SERVER['SERVER_PROTOCOL']), 'https') === false ? 'http' : 'https') .
|
||||
((isset($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on" || $_SERVER["HTTPS"] === 1)) ? "https" : "http") .
|
||||
'://' . $_SERVER["HTTP_HOST"]
|
||||
);
|
||||
|
||||
|
|
|
@ -602,20 +602,23 @@ class mojeek{
|
|||
);
|
||||
}
|
||||
|
||||
$data["date"] =
|
||||
explode(
|
||||
" - ",
|
||||
$this->fuckhtml
|
||||
->getTextContent(
|
||||
$this->fuckhtml
|
||||
->getElementsByClassName("i", "p")[0]
|
||||
)
|
||||
$date =
|
||||
$this->fuckhtml
|
||||
->getElementsByClassName(
|
||||
"mdate",
|
||||
"span"
|
||||
);
|
||||
|
||||
$data["date"] =
|
||||
strtotime(
|
||||
$data["date"][count($data["date"]) - 1]
|
||||
);
|
||||
if(count($date) !== 0){
|
||||
|
||||
$data["date"] =
|
||||
strtotime(
|
||||
$this->fuckhtml
|
||||
->getTextContent(
|
||||
$date[0]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$out["web"][] = $data;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class sc{
|
|||
"option" => [
|
||||
"any" => "Any type",
|
||||
"track" => "Tracks",
|
||||
"people" => "People",
|
||||
"author" => "People",
|
||||
"album" => "Albums",
|
||||
"playlist" => "Playlists",
|
||||
"goplus" => "Go+ Tracks"
|
||||
|
@ -143,7 +143,7 @@ class sc{
|
|||
];
|
||||
break;
|
||||
|
||||
case "people":
|
||||
case "author":
|
||||
$url = "https://api-v2.soundcloud.com/search/users";
|
||||
$params = [
|
||||
"q" => $search,
|
||||
|
@ -237,7 +237,10 @@ class sc{
|
|||
"npt" => null,
|
||||
"song" => [],
|
||||
"playlist" => [],
|
||||
"author" => []
|
||||
"album" => [],
|
||||
"podcast" => [],
|
||||
"author" => [],
|
||||
"user" => []
|
||||
];
|
||||
|
||||
/*
|
||||
|
@ -346,7 +349,7 @@ class sc{
|
|||
if(stripos($item["monetization_model"], "TIER") === false){
|
||||
|
||||
$stream = [
|
||||
"endpoint" => "audio_sc",
|
||||
"endpoint" => "sc",
|
||||
"url" =>
|
||||
$item["media"]["transcodings"][0]["url"] .
|
||||
"?client_id=" . config::SC_CLIENT_TOKEN .
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,726 @@
|
|||
<?php
|
||||
|
||||
class spotify{
|
||||
|
||||
private const req_web = 0;
|
||||
private const req_api = 1;
|
||||
private const req_clientid = 2;
|
||||
|
||||
public function __construct(){
|
||||
|
||||
include "lib/backend.php";
|
||||
$this->backend = new backend("spotify");
|
||||
|
||||
include "lib/fuckhtml.php";
|
||||
$this->fuckhtml = new fuckhtml();
|
||||
}
|
||||
|
||||
public function getfilters($page){
|
||||
|
||||
return [
|
||||
"category" => [
|
||||
"display" => "Category",
|
||||
"option" => [
|
||||
"any" => "All (no pagination)",
|
||||
"audiobooks" => "Audiobooks",
|
||||
"tracks" => "Songs",
|
||||
"artists" => "Artists",
|
||||
"playlists" => "Playlists",
|
||||
"albums" => "Albums",
|
||||
"podcastAndEpisodes" => "Podcasts & Shows (no pagination)",
|
||||
"episodes" => "Episodes",
|
||||
"users" => "Profiles"
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private function get($proxy, $url, $get = [], $reqtype = self::req_web, $bearer = null, $token = null){
|
||||
|
||||
$curlproc = curl_init();
|
||||
|
||||
switch($reqtype){
|
||||
|
||||
case self::req_api:
|
||||
$headers = [
|
||||
"User-Agent: " . config::USER_AGENT,
|
||||
"Accept: application/json",
|
||||
"Accept-Language: en",
|
||||
"app-platform: WebPlayer",
|
||||
"authorization: Bearer {$bearer}",
|
||||
"client-token: {$token}",
|
||||
"content-type: application/json;charset=UTF-8",
|
||||
"Origin: https://open.spotify.com",
|
||||
"Referer: https://open.spotify.com/",
|
||||
"DNT: 1",
|
||||
"Connection: keep-alive",
|
||||
"Sec-Fetch-Dest: empty",
|
||||
"Sec-Fetch-Mode: cors",
|
||||
"Sec-Fetch-Site: same-site",
|
||||
"spotify-app-version: 1.2.27.93.g7aee53d4",
|
||||
"TE: trailers"
|
||||
];
|
||||
break;
|
||||
|
||||
case self::req_web:
|
||||
$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",
|
||||
"Sec-GPC: 1",
|
||||
"Connection: keep-alive",
|
||||
"Upgrade-Insecure-Requests: 1",
|
||||
"Sec-Fetch-Dest: document",
|
||||
"Sec-Fetch-Mode: navigate",
|
||||
"Sec-Fetch-Site: cross-site"
|
||||
];
|
||||
break;
|
||||
|
||||
case self::req_clientid:
|
||||
$get = json_encode($get);
|
||||
|
||||
curl_setopt($curlproc, CURLOPT_POST, true);
|
||||
curl_setopt($curlproc, CURLOPT_POSTFIELDS, $get);
|
||||
|
||||
$headers = [
|
||||
"User-Agent:" . config::USER_AGENT,
|
||||