added delete option for youtube-dl download;added options for copying links;
This commit is contained in:
@@ -8,7 +8,7 @@ Search for torrents within the app from mutiple BT sites;
|
|||||||
Control Aria2 and manage download tasks from the web;
|
Control Aria2 and manage download tasks from the web;
|
||||||
download videos from 700+ video sites(youtube,youku,vimo,dailymotion,twitter,facebook and the likes
|
download videos from 700+ video sites(youtube,youku,vimo,dailymotion,twitter,facebook and the likes
|
||||||
</description>
|
</description>
|
||||||
<version>0.6.1</version>
|
<version>0.6.5</version>
|
||||||
<licence>agpl</licence>
|
<licence>agpl</licence>
|
||||||
<author mail="freefallbenson@gmail.com" homepage="https://github.com/shiningw">jiaxinhuang</author>
|
<author mail="freefallbenson@gmail.com" homepage="https://github.com/shiningw">jiaxinhuang</author>
|
||||||
<namespace>NCDownloader</namespace>
|
<namespace>NCDownloader</namespace>
|
||||||
|
|||||||
1
img/clippy.svg
Normal file
1
img/clippy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" viewbox="0 0 16 16" fill="#397ca1" style="fill:#397ca1"><path d="m13 15h-11l0.0005-10h11v3.0002l1-0.0004 0.0005-5.0001c0.000058-0.5834-0.4165-1.0002-1.0005-1.0001l-3.467 0.0005c0.0008-1.183-0.9492-2.0001-2.1325-2.0001s-2.1333 0.8171-2.1333 2.0004h-3.2c-0.5834 0-1.0662 0.4166-1.0662 0.9999l-0.0005 12c-0.0000243 0.584 0.4833 1 1.0667 1l10.933-0.0005c0.584-0.001 1-0.416 1-1v-3h-1zm-8.8005-12h1.0672c0.5833 0 1.0666-0.4162 1.0666-0.9996 0-0.5833 0.4834-0.9337 1.0667-0.9337s1.0667 0.3504 1.0667 0.9337c0 0.5834 0.5333 0.9996 1.0666 0.9996h1.2667c0.517 0 1.2 0.4166 1.2 1h-9c-0.0004-0.65 0.5988-1 1.1988-1zm-1.1995 8h2v-1h-2zm7.9998-2v-2l-4 3 3.9998 3v-2l5.0002-0.00005v-2l-4.9998-0.00005zm-8 4h4v-1h-4zm6-7h-6v1h6zm-3 2h-3v1h3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 813 B |
@@ -50,18 +50,18 @@ class YoutubeController extends Controller
|
|||||||
$folderLink = $this->urlGenerator->linkToRoute('files.view.index', $params);
|
$folderLink = $this->urlGenerator->linkToRoute('files.view.index', $params);
|
||||||
foreach ($data as $value) {
|
foreach ($data as $value) {
|
||||||
$tmp = [];
|
$tmp = [];
|
||||||
|
$extra = unserialize($value['data']);
|
||||||
$filename = sprintf('<a class="download-file-folder" href="%s">%s</a>', $folderLink, $value['filename']);
|
$filename = sprintf('<a class="download-file-folder" href="%s">%s</a>', $folderLink, $value['filename']);
|
||||||
$fileInfo = sprintf("%s | %s", $value['filesize'], date("Y-m-d H:i:s", $value['timestamp']));
|
$fileInfo = sprintf('<div class="ncd-file-info"><button id="icon-clipboard" class="icon-clipboard" data-text="%s"></button> %s | % s</div>', $extra['link'], $value['filesize'], date("Y-m-d H:i:s", $value['timestamp']));
|
||||||
$tmp['filename'] = array($filename, $fileInfo);
|
$tmp['filename'] = array($filename, $fileInfo);
|
||||||
$tmp['speed'] = explode("|", $value['speed']);
|
$tmp['speed'] = explode("|", $value['speed']);
|
||||||
$tmp['progress'] = $value['progress'];
|
$tmp['progress'] = $value['progress'];
|
||||||
if ((int) $value['status'] == Helper::STATUS['COMPLETE']) {
|
|
||||||
$path = $this->urlGenerator->linkToRoute('ncdownloader.Youtube.Delete');
|
$path = $this->urlGenerator->linkToRoute('ncdownloader.Youtube.Delete');
|
||||||
$tmp['actions'][] = ['name' => 'delete', 'path' => $path];
|
$tmp['actions'][] = ['name' => 'delete', 'path' => $path];
|
||||||
} else {
|
$path = $this->urlGenerator->linkToRoute('ncdownloader.Youtube.Redownload');
|
||||||
$path = $this->urlGenerator->linkToRoute('ncdownloader.Youtube.Redownload');
|
$tmp['actions'][] = ['name' => 'refresh', 'path' => $path];
|
||||||
$tmp['actions'][] = ['name' => 'refresh', 'path' => $path];
|
|
||||||
}
|
|
||||||
$tmp['data_gid'] = $value['gid'] ?? 0;
|
$tmp['data_gid'] = $value['gid'] ?? 0;
|
||||||
array_push($resp['row'], $tmp);
|
array_push($resp['row'], $tmp);
|
||||||
}
|
}
|
||||||
@@ -111,10 +111,30 @@ class YoutubeController extends Controller
|
|||||||
return new JSONResponse(['error' => "no gid value is received!"]);
|
return new JSONResponse(['error' => "no gid value is received!"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->dbconn->deleteByGid($gid)) {
|
$row = $this->dbconn->getByGid($gid);
|
||||||
return new JSONResponse(['message' => $gid . " Deleted!"]);
|
$data = unserialize($row['data']);
|
||||||
|
if (!isset($data['pid'])) {
|
||||||
|
if ($this->dbconn->deleteByGid($gid)) {
|
||||||
|
$msg = sprintf("%s is deleted from database!", $gid);
|
||||||
|
}
|
||||||
|
return new JSONResponse(['message' => $msg]);
|
||||||
}
|
}
|
||||||
|
$pid = $data['pid'];
|
||||||
|
if (!Helper::isRunning($pid)) {
|
||||||
|
if ($this->dbconn->deleteByGid($gid)) {
|
||||||
|
$msg = sprintf("%s is deleted from database!", $gid);
|
||||||
|
} else {
|
||||||
|
$msg = sprintf("process %d is not running!", $pid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Helper::stop($pid)) {
|
||||||
|
$msg = sprintf("process %d has been terminated!", $pid);
|
||||||
|
} else {
|
||||||
|
$msg = sprintf("failed to terminate process %d!", $pid);
|
||||||
|
}
|
||||||
|
$this->dbconn->deleteByGid($gid);
|
||||||
|
}
|
||||||
|
return new JSONResponse(['message' => $msg]);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
@@ -129,6 +149,7 @@ class YoutubeController extends Controller
|
|||||||
$row = $this->dbconn->getByGid($gid);
|
$row = $this->dbconn->getByGid($gid);
|
||||||
$data = unserialize($row['data']);
|
$data = unserialize($row['data']);
|
||||||
if (!empty($data['link'])) {
|
if (!empty($data['link'])) {
|
||||||
|
//$this->dbconn->deleteByGid($gid);
|
||||||
$resp = $this->youtube->forceIPV4()->download($data['link']);
|
$resp = $this->youtube->forceIPV4()->download($data['link']);
|
||||||
folderScan::sync();
|
folderScan::sync();
|
||||||
return new JSONResponse($resp);
|
return new JSONResponse($resp);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class torrentSearch
|
|||||||
if (!$value) {
|
if (!$value) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$value['actions'][] = array("name" => 'download', 'path' => '/index.php/apps/ncdownloader/new');
|
$value['actions'] = [["name" => 'download', 'path' => '/index.php/apps/ncdownloader/new'], ['name' => 'clipboard']];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class Helper
|
|||||||
{
|
{
|
||||||
$host = parse_url($url, PHP_URL_HOST);
|
$host = parse_url($url, PHP_URL_HOST);
|
||||||
//$sites = ['twitter.com', 'www.twitter.com'];
|
//$sites = ['twitter.com', 'www.twitter.com'];
|
||||||
|
$sites = [];
|
||||||
return (bool) (in_array($host, $sites));
|
return (bool) (in_array($host, $sites));
|
||||||
}
|
}
|
||||||
public static function parseUrl($url)
|
public static function parseUrl($url)
|
||||||
@@ -134,7 +135,7 @@ class Helper
|
|||||||
public static function debug($msg)
|
public static function debug($msg)
|
||||||
{
|
{
|
||||||
$logger = \OC::$server->getLogger();
|
$logger = \OC::$server->getLogger();
|
||||||
$logger->debug($msg, ['app' => 'ncdownloader']);
|
$logger->error($msg, ['app' => 'ncdownloader']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function log($msg, $file = "/tmp/nc.log")
|
public static function log($msg, $file = "/tmp/nc.log")
|
||||||
@@ -298,4 +299,28 @@ class Helper
|
|||||||
return filter_var($string, FILTER_SANITIZE_STRING);
|
return filter_var($string, FILTER_SANITIZE_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function doSignal($pid, $signal): bool
|
||||||
|
{
|
||||||
|
if (\function_exists('posix_kill')) {
|
||||||
|
$ok = @posix_kill($pid, $signal);
|
||||||
|
} elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
|
||||||
|
$ok = false === fgets($pipes[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isRunning($pid)
|
||||||
|
{
|
||||||
|
return self::doSignal($pid, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stop($pid)
|
||||||
|
{
|
||||||
|
return self::doSignal($pid, 9);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,11 +154,13 @@ class Youtube
|
|||||||
//\OC::$server->getLogger()->error($process->getCommandLine(), ['app' => 'PHP']);
|
//\OC::$server->getLogger()->error($process->getCommandLine(), ['app' => 'PHP']);
|
||||||
$process = new Process($this->options, null, $this->env);
|
$process = new Process($this->options, null, $this->env);
|
||||||
$process->setTimeout($this->timeout);
|
$process->setTimeout($this->timeout);
|
||||||
$process->run(function ($type, $buffer) use ($url) {
|
$data = ['link' => $url];
|
||||||
|
$process->run(function ($type, $buffer) use ($data, $process) {
|
||||||
if (Process::ERR === $type) {
|
if (Process::ERR === $type) {
|
||||||
$this->onError($buffer);
|
// $this->onError($buffer);
|
||||||
} else {
|
} else {
|
||||||
$this->onOutput($buffer, $url);
|
$data['pid'] = $process->getPid();
|
||||||
|
$this->onOutput($buffer, $data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ($process->isSuccessful()) {
|
if ($process->isSuccessful()) {
|
||||||
@@ -173,9 +175,9 @@ class Youtube
|
|||||||
$this->helper->log($buffer);
|
$this->helper->log($buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onOutput($buffer, $url)
|
public function onOutput($buffer, $extra)
|
||||||
{
|
{
|
||||||
$this->helper->run($buffer, $url);
|
$this->helper->run($buffer, $extra);
|
||||||
}
|
}
|
||||||
public function getDownloadUrl($url)
|
public function getDownloadUrl($url)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class YoutubeHelper
|
|||||||
'(\s+in\s+(?<totalTime>[\d:]{2,8}))?#i';
|
'(\s+in\s+(?<totalTime>[\d:]{2,8}))?#i';
|
||||||
public $file = null;
|
public $file = null;
|
||||||
public $filesize = null;
|
public $filesize = null;
|
||||||
|
protected $pid = 0;
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->dbconn = new DbHelper();
|
$this->dbconn = new DbHelper();
|
||||||
@@ -47,9 +48,13 @@ class YoutubeHelper
|
|||||||
//$sql = sprintf("UPDATE %s set status = ? WHERE gid = ?", $this->tablename);
|
//$sql = sprintf("UPDATE %s set status = ? WHERE gid = ?", $this->tablename);
|
||||||
$this->dbconn->updateStatus($this->gid, $this->status);
|
$this->dbconn->updateStatus($this->gid, $this->status);
|
||||||
}
|
}
|
||||||
public function run($buffer, $url)
|
public function setPid($pid)
|
||||||
{
|
{
|
||||||
$this->gid = Helper::generateGID($url);
|
$this->pid = $pid;
|
||||||
|
}
|
||||||
|
public function run($buffer, $extra)
|
||||||
|
{
|
||||||
|
$this->gid = Helper::generateGID($extra['link']);
|
||||||
$file = $this->getFilePath($buffer);
|
$file = $this->getFilePath($buffer);
|
||||||
if ($file) {
|
if ($file) {
|
||||||
$data = [
|
$data = [
|
||||||
@@ -59,7 +64,7 @@ class YoutubeHelper
|
|||||||
'filename' => basename($file),
|
'filename' => basename($file),
|
||||||
'status' => Helper::STATUS['ACTIVE'],
|
'status' => Helper::STATUS['ACTIVE'],
|
||||||
'timestamp' => time(),
|
'timestamp' => time(),
|
||||||
'data' => serialize(['link' => $url]),
|
'data' => serialize($extra),
|
||||||
];
|
];
|
||||||
//save the filename as this runs only once
|
//save the filename as this runs only once
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import Http from '../lib/http'
|
import Http from '../lib/http'
|
||||||
import helper from '../utils/helper'
|
import helper from '../utils/helper'
|
||||||
import eventHandler from '../lib/eventHandler'
|
import eventHandler from '../lib/eventHandler'
|
||||||
|
import Clipboard from '../utils/clipboard'
|
||||||
|
import '../css/clipboard.scss';
|
||||||
|
|
||||||
const buttonHandler = (event, type) => {
|
const buttonHandler = (event, type) => {
|
||||||
let element = event.target;
|
let element = event.target;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -9,6 +12,11 @@ const buttonHandler = (event, type) => {
|
|||||||
let row, data = {};
|
let row, data = {};
|
||||||
let removeRow = true;
|
let removeRow = true;
|
||||||
if (row = element.closest('.table-row-search')) {
|
if (row = element.closest('.table-row-search')) {
|
||||||
|
if (element.className == 'icon-clipboard') {
|
||||||
|
const clippy = new Clipboard(element, row.dataset.link);
|
||||||
|
clippy.Copy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
data['text-input-value'] = row.dataset.link;
|
data['text-input-value'] = row.dataset.link;
|
||||||
} else {
|
} else {
|
||||||
row = element.closest('.table-row')
|
row = element.closest('.table-row')
|
||||||
@@ -38,5 +46,10 @@ const buttonHandler = (event, type) => {
|
|||||||
export default {
|
export default {
|
||||||
run: function () {
|
run: function () {
|
||||||
eventHandler.add("click", "#ncdownloader-table-wrapper", ".table-cell-action-item .button-container button", e => buttonHandler(e, ''));
|
eventHandler.add("click", "#ncdownloader-table-wrapper", ".table-cell-action-item .button-container button", e => buttonHandler(e, ''));
|
||||||
|
eventHandler.add("click", "#ncdownloader-table-wrapper", ".table-row button.icon-clipboard", function (e) {
|
||||||
|
let element = e.target;
|
||||||
|
const clippy = new Clipboard(element);
|
||||||
|
clippy.Copy();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
src/css/clipboard.scss
Normal file
5
src/css/clipboard.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#ncdownloader-tooltip {
|
||||||
|
color : #5d6395;
|
||||||
|
font-size : medium;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -41,7 +41,9 @@
|
|||||||
.icon-add {
|
.icon-add {
|
||||||
background-image: url('../../img/add.svg');
|
background-image: url('../../img/add.svg');
|
||||||
}
|
}
|
||||||
|
.icon-clipboard {
|
||||||
|
background-image: url('../../img/clippy.svg');
|
||||||
|
}
|
||||||
#ncdownloader-form-wrapper {
|
#ncdownloader-form-wrapper {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
|
|
||||||
|
|||||||
50
src/lib/tooltip.js
Normal file
50
src/lib/tooltip.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import $ from 'jquery'
|
||||||
|
|
||||||
|
class Tooltip {
|
||||||
|
id = "ncdownloader-tooltip";
|
||||||
|
messageNode;
|
||||||
|
style = {};
|
||||||
|
text;
|
||||||
|
constructor(element, text) {
|
||||||
|
if (typeof element !== 'string' && !(element instanceof HTMLElement))
|
||||||
|
throw ("invalid element!");
|
||||||
|
this.element = typeof element == 'object' ? element : document.querySelector(element);
|
||||||
|
this.style = {
|
||||||
|
position: 'fixed',
|
||||||
|
display: 'block',
|
||||||
|
}
|
||||||
|
this.text = text || element.getAttribute("data-text");
|
||||||
|
}
|
||||||
|
create(id) {
|
||||||
|
this.messageNode = document.createElement("div");
|
||||||
|
this.messageNode.classList.add(this.id);
|
||||||
|
this.messageNode.setAttribute("id", this.id);
|
||||||
|
this.messageNode.style.display = this.style.display;
|
||||||
|
this.messageNode.style.position = this.style.position;
|
||||||
|
this.messageNode.style.zIndex = 10000;
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.setAttribute("id", id);
|
||||||
|
let text = document.createTextNode(this.text);
|
||||||
|
div.appendChild(text);
|
||||||
|
this.messageNode.appendChild(div);
|
||||||
|
this.setPosition();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
document.body.appendChild(this.messageNode);
|
||||||
|
}
|
||||||
|
html() {
|
||||||
|
return this.messageNode;
|
||||||
|
}
|
||||||
|
setPosition(bottomMargin, leftMargin) {
|
||||||
|
bottomMargin = bottomMargin || 20;
|
||||||
|
leftMargin = leftMargin || 0;
|
||||||
|
let rect = this.element.getBoundingClientRect();
|
||||||
|
let top = (rect['top'] + bottomMargin) + "px";
|
||||||
|
let left = (rect['left'] - leftMargin) + "px";
|
||||||
|
this.messageNode.style.top = top;
|
||||||
|
this.messageNode.style.left = left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tooltip;
|
||||||
66
src/utils/clipboard.js
Normal file
66
src/utils/clipboard.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import Tooltip from "../lib/tooltip";
|
||||||
|
|
||||||
|
class Clipboard {
|
||||||
|
element;
|
||||||
|
text;
|
||||||
|
|
||||||
|
constructor(element, text) {
|
||||||
|
if (typeof element !== 'string' && !(element instanceof HTMLElement))
|
||||||
|
throw ("invalid element!");
|
||||||
|
this.element = typeof element == 'object' ? element : document.querySelector(element);
|
||||||
|
this.text = text || element.getAttribute("data-text");
|
||||||
|
}
|
||||||
|
|
||||||
|
_copy(text) {
|
||||||
|
let textArea = document.createElement("textarea");
|
||||||
|
textArea.value = text;
|
||||||
|
|
||||||
|
textArea.style.top = "0";
|
||||||
|
textArea.style.left = "0";
|
||||||
|
textArea.style.position = "fixed";
|
||||||
|
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.focus();
|
||||||
|
textArea.select();
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = document.execCommand('copy');
|
||||||
|
//console.log('copied using exceCommand');
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('failed to copy', err);
|
||||||
|
result = false;
|
||||||
|
} finally {
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
this.ShowMsg("Copied!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowMsg(msg) {
|
||||||
|
let tip = new Tooltip(this.element, msg);
|
||||||
|
let html = tip.create('copy-alert').html();
|
||||||
|
document.body.appendChild(html);
|
||||||
|
const callback = (element) => {
|
||||||
|
element.remove()
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
callback(html)
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Copy() {
|
||||||
|
if (!navigator.clipboard) {
|
||||||
|
return this._copy(this.text);
|
||||||
|
}
|
||||||
|
return navigator.clipboard.writeText(this.text).then(() => {
|
||||||
|
this.ShowMsg("Copied!");
|
||||||
|
}, function (err) {
|
||||||
|
console.error('failed to copy text: ', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Clipboard;
|
||||||
@@ -172,6 +172,12 @@ const helper = {
|
|||||||
></span
|
></span
|
||||||
><span class="visually-hidden">Loading...</span>`;
|
><span class="visually-hidden">Loading...</span>`;
|
||||||
return html;
|
return html;
|
||||||
|
},
|
||||||
|
getCssVar(prop) {
|
||||||
|
return window.getComputedStyle(document.documentElement).getPropertyValue(prop);
|
||||||
|
},
|
||||||
|
getScrollTop() {
|
||||||
|
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user