Created
January 4, 2016 10:43
-
-
Save anonymous/2ade7381ecb17fc68c98 to your computer and use it in GitHub Desktop.
Add dlc files to the DSM download station trough the command line
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* This script reads dlc files from a folder and adds the links to the DSM | |
* download station. Most code is copied from https://github.com/downtuned42/ds-dec. | |
* | |
* Usage: | |
* - Put the cron.php file in to the folder /volume1/php/cron.php on your DSM Nas | |
* - Adapt the parameters on the beginning of the file | |
* - Copy a dlc file to /volume1/shares/Downloads/ on your NAS | |
* - Run it on your SSH console like php /volume1/php/cron.php | |
* - The download station should start the downloading the links in the dlc file | |
* - If all is running fine create a task scheduler with the following command: | |
* php /volume1/php/cron.php | |
* and let it run every 5 min. | |
* | |
* Now you can add your dlc files to the folder /volume1/shares/Downloads/ and they | |
* will be added automatically to the download station. | |
* | |
* Credit for the DLC decrypter and Synology API Access goes fully to | |
* @see http://www.coreye.de/blog/downloadstation-decrypter-dlc-fuer-die-synology | |
*/ | |
// Turn on error reporting | |
error_reporting(E_ALL); | |
ini_set('display_errors', 1); | |
// The path to look for dlc files | |
$root = '/volume1/shares/Downloads/'; | |
// The address to make the API calls against | |
$address = 'https://localhost:5001/webapi'; | |
// The NAS username to add the links from the DLC file | |
$user = 'admin'; | |
// The NAS password to add the links from the DLC file | |
$passwd = ''; | |
// Host filter | |
$linksHostFilter = 'share-online.biz'; | |
// Should the links being converted to https | |
$linksAsHttps = true; | |
foreach (scandir($root) as $file) | |
{ | |
if (strpos($file, '.dlc') === false) | |
{ | |
continue; | |
} | |
$dlc = new DlcDecrypter(DlcDecrypter::TYPE_DCRYPTIT); | |
$links = $dlc->decrypt(file_get_contents($root . $file)); | |
foreach ($links as $key => $value) | |
{ | |
if ($linksAsHttps && strpos($value, 'http://') === 0) | |
{ | |
$links[$key] = str_replace('http://', 'https://', $value); | |
} | |
if (! $linksHostFilter || strpos($value, $linksHostFilter) !== false) | |
{ | |
continue; | |
} | |
unset($links[$key]); | |
} | |
$syno = new SynoWebApi($address); | |
$syno->login($user, $passwd); | |
$unpackPasswd = ''; | |
$pwFile = str_replace('.dlc', '.txt', $root . $file); | |
if (file_exists($pwFile)) | |
{ | |
$unpackPasswd = file_get_contents($pwFile); | |
} | |
$syno->addLinks(implode(',', $links), $unpackPasswd); | |
unlink($root . $file); | |
} | |
/** | |
* Simple cURL based Http-Client | |
*/ | |
class HttpClient | |
{ | |
public $lastRequestInfo; | |
public function post ($url, array $args = array(), $headers = array()) | |
{ | |
$query = ''; | |
if (is_array($args) && count($args)) | |
{ | |
$query = http_build_query($args); | |
} | |
$curlOpt = array( | |
CURLOPT_URL => $url, | |
CURLOPT_POST => 1, | |
CURLOPT_POSTFIELDS => $query, | |
CURLOPT_HEADER => 1, // returns header AND response-body as one | |
// string | |
CURLOPT_RETURNTRANSFER => true | |
); | |
if (is_array($headers) && count($headers)) | |
{ | |
$curlOpt[CURLOPT_HTTPHEADER] = $headers; | |
} | |
if ((stripos($url, 'https') === 0) ? true : false) | |
{ | |
$curlOpt[CURLOPT_SSL_VERIFYPEER] = false; | |
// prevents 'curl SSL: certificate subject name | |
// 'my-hostname-from-certificate' | |
// does not match target host name 'my-hostname-from-config.inc.php' | |
// errors | |
$curlOpt[CURLOPT_SSL_VERIFYHOST] = false; | |
} | |
$ch = curl_init(); | |
curl_setopt_array($ch, $curlOpt); | |
$response = curl_exec($ch); | |
// split header and response-body | |
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); | |
$header = substr($response, 0, $headerSize); | |
$body = substr($response, $headerSize); | |
// get http-status of response | |
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |
$reqInfo = new stdClass(); | |
$reqInfo->url = $url; | |
$reqInfo->query = $query; | |
$reqInfo->requestHeader = $headers; | |
$reqInfo->response = $body; | |
$reqInfo->responseHeader = $header; | |
$this->lastRequestInfo = $reqInfo; | |
// Check if any error occurred | |
if (curl_errno($ch) || $status != 200) | |
{ | |
$reqInfo->curlError = curl_error($ch); | |
curl_close($ch); | |
throw new \RuntimeException("Failed issuing request:\n" . "REQUEST-INFO:\n" . print_r($this->lastRequestInfo, true)); | |
} | |
curl_close($ch); | |
return $body; | |
} | |
} | |
class DlcDecrypter extends HttpClient | |
{ | |
const TYPE_LINKDECRYPTER = 1; | |
const TYPE_DCRYPTIT = 2; | |
private static $types = array( | |
self::TYPE_LINKDECRYPTER, | |
self::TYPE_DCRYPTIT | |
); | |
private $type; | |
public function __construct ($type) | |
{ | |
if (! in_array($type, self::$types)) | |
{ | |
throw new \RuntimeException("invalid decrypter-type [$type]. Check self::types for valid types"); | |
} | |
$this->type = $type; | |
} | |
public function decrypt ($dlc) | |
{ | |
if ($this->type == self::TYPE_LINKDECRYPTER) | |
{ | |
return $this->decLinkdecrypter($dlc); | |
} | |
else if ($this->type == self::TYPE_DCRYPTIT) | |
{ | |
return $this->decDcryptit($dlc); | |
} | |
else | |
{ | |
throw new \RuntimeException("Couldn't determine decrypt implementation, shouldn't happen!"); | |
} | |
} | |
private function getDlcContents ($dlc) | |
{ | |
if (stripos($dlc, 'http://') !== false) | |
{ | |
// URL | |
$dlcContents = file_get_contents($dlc); | |
// suppress warning e.g. "Warning: file_exists(): File name is | |
// longer than the maximum allowed path length on this platform | |
// (4096):" | |
// if $dlc contains contents which is most likely if filename is too | |
// long. | |
} | |
else if (@file_exists($dlc)) | |
{ | |
// File | |
$dlcContents = file_get_contents($dlc); | |
} | |
else | |
{ | |
// contents | |
$dlcContents = $dlc; | |
} | |
return $dlcContents; | |
} | |
/** | |
* Decrypts the given DLC-URL. | |
* Decryption is done utilizing the fine service of | |
* http://linkdecrypter.com/ | |
* | |
* @param string $dlc | |
* The URL pointing to the DLC to decrypt, the DLC contents or an | |
* DLC file | |
* | |
* @return array Array, containing the links found in decrypted DLC | |
*/ | |
private function decLinkdecrypter ($dlc) | |
{ | |
$dlcContents = $this->getDlcContents($dlc); | |
$url = 'http://linkdecrypter.com/?ck'; | |
$args = array( | |
'cnt_text' => $dlcContents | |
); | |
$headers = array( | |
'Accept:text/html', | |
'Connection:keep-alive', | |
'User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.39 Safari/537.36' | |
); | |
$res = $this->post($url, $args, $headers); | |
// extract links from textarea, because linkdecrypter.com offers no | |
// service-api we have to parse the html-response | |
// note: this approach will fail if html-response changes in future! | |
preg_match_all('#<textarea id="des".*>([^<]*)</textarea>#Usi', $res, $match); | |
$res = explode("\n\n", $match[1][0]); | |
return $res[1]; | |
} | |
private function decDcryptit ($dlc) | |
{ | |
$dlcContents = $this->getDlcContents($dlc); | |
$headers = array( | |
'Accept:application/json, text/javascript, */*', | |
'Connection:keep-alive', | |
'Content-Type:application/x-www-form-urlencoded', | |
'Host:dcrypt.it', | |
'Origin:http://dcrypt.it', | |
'Referer:http://dcrypt.it/', | |
'User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.55 Safari/537.36', | |
'X-Requested-With:XMLHttpRequest' | |
); | |
$url = 'http://dcrypt.it/decrypt/paste'; | |
$args = array( | |
'content' => $dlcContents | |
); | |
$res = $this->post($url, $args, $headers); | |
$decRes = json_decode($res); | |
if (is_object($decRes) && isset($decRes->success) && is_array($decRes->success->links)) | |
{ | |
$links = $decRes->success->links; | |
} | |
else | |
{ | |
throw new \RuntimeException('Failed parsing response: ' . var_export($res, true)); | |
} | |
return $links; | |
} | |
} | |
class SynoWebApi extends HttpClient | |
{ | |
private $endpoint; | |
private $sid; | |
public function __construct ($endpoint) | |
{ | |
$this->endpoint = $endpoint; | |
} | |
public function login ($user, $passwd) | |
{ | |
$args = array( | |
'version' => 2, | |
'format' => 'cookie', | |
'session' => 'DownloadStation', | |
'api' => 'SYNO.API.Auth', | |
'method' => 'login', | |
'account' => $user, | |
'passwd' => $passwd | |
); | |
$url = $this->endpoint . '/auth.cgi'; | |
$res = $this->post($url, $args); | |
$res = json_decode($res); | |
if (! isset($res->success) || ! $res->success) | |
{ | |
throw new RuntimeException("Got error response from Syno-Api:\n" . "REQUEST-INFO:\n" . print_r($this->lastRequestInfo, true)); | |
} | |
$this->sid = $res->data->sid; | |
return $res; | |
} | |
public function addLinks ($linkList, $unpackPasswd = '') | |
{ | |
$args = array( | |
'version' => 1, | |
'_sid' => $this->sid, | |
'api' => 'SYNO.DownloadStation.Task', | |
'method' => 'create', | |
'uri' => $linkList | |
); | |
if ($unpackPasswd) | |
{ | |
$args['unzip_password'] = $unpackPasswd; | |
} | |
$url = $this->endpoint . '/' . 'DownloadStation/task.cgi'; | |
$res = $this->post($url, $args); | |
$res = json_decode($res); | |
if (! isset($res->success) || ! $res->success) | |
{ | |
throw new RuntimeException("Got error response from Syno-Api:\n" . "REQUEST-INFO:\n" . print_r($this->lastRequestInfo, true)); | |
} | |
return $res; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment