Skip to content

Instantly share code, notes, and snippets.

Created January 4, 2016 10:43
Show Gist options
  • Save anonymous/2ade7381ecb17fc68c98 to your computer and use it in GitHub Desktop.
Save anonymous/2ade7381ecb17fc68c98 to your computer and use it in GitHub Desktop.
Add dlc files to the DSM download station trough the command line
<?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