mirror of
https://github.com/mirror/jdownloader.git
synced 2024-10-07 00:53:37 +00:00
1000eb.com: fixes #6828
1000eb.com: speed up folder decrypter refs #6828 RAFDownload: added workaround for server responses != 206 example in 1000eb.com hoster plugin rtmp: * upgrade rtmpdump to version 2.4 with redirect patch and other improvements closes #4813 * new flvfixer class, fixed broken flv streams after resuming * add new Config entry in advanced settings tab rtl2.de: refs #6847 videopremium.net: removed old redirect handling git-svn-id: svn://svn.jdownloader.org/jdownloader/trunk@18818 ebf7c1c2-ba36-0410-9fe8-c592906822b4
This commit is contained in:
parent
6b7e98ffff
commit
c36160338f
Binary file not shown.
337
src/jd/network/rtmp/FlvFixer.java
Normal file
337
src/jd/network/rtmp/FlvFixer.java
Normal file
@ -0,0 +1,337 @@
|
||||
// jDownloader - Downloadmanager
|
||||
// Copyright (C) 2012 JD-Team support@jdownloader.org
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// source@GPLv3: https://github.com/K-S-V/Scripts/blob/master/FlvFixer.php
|
||||
package jd.network.rtmp;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import jd.utils.JDHexUtils;
|
||||
|
||||
import org.appwork.utils.logging2.LogSource;
|
||||
import org.jdownloader.logging.LogController;
|
||||
|
||||
public class FlvFixer {
|
||||
|
||||
public static enum Tag {
|
||||
AUDIO((byte) 0x08),
|
||||
VIDEO((byte) 0x09),
|
||||
SCRIPT_DATA((byte) 0x12),
|
||||
FRAME_TYPE_INFO((byte) 0x05),
|
||||
CODEC_ID_AVC((byte) 0x07),
|
||||
CODEC_ID_AAC((byte) 0x0A),
|
||||
AVC_SEQUENCE_HEADER((byte) 0x00),
|
||||
AAC_SEQUENCE_HEADER((byte) 0x00),
|
||||
AVC_SEQUENCE_END((byte) 0x02);
|
||||
|
||||
private byte hex;
|
||||
|
||||
Tag(byte b) {
|
||||
hex = b;
|
||||
}
|
||||
}
|
||||
|
||||
File corruptFile;
|
||||
|
||||
File fixedFile;
|
||||
|
||||
FileInputStream fis = null;
|
||||
|
||||
FileOutputStream fos = null;
|
||||
|
||||
final static int FRAMEGAP_DURATION = 8;
|
||||
|
||||
final static int INVALID_TIMESTAMP = -1;
|
||||
|
||||
boolean DEBUG = false;
|
||||
|
||||
private final LogSource logger;
|
||||
|
||||
public FlvFixer() {
|
||||
logger = LogController.CL();
|
||||
}
|
||||
|
||||
public boolean setDebug(boolean b) {
|
||||
return DEBUG = b;
|
||||
}
|
||||
|
||||
public void setInputFile(File f) {
|
||||
this.corruptFile = f;
|
||||
}
|
||||
|
||||
public File getoutputFile() {
|
||||
return fixedFile;
|
||||
}
|
||||
|
||||
public boolean scan() throws Exception {
|
||||
|
||||
if (!(corruptFile.exists() && corruptFile.length() > 0)) {
|
||||
logger.severe("File " + corruptFile.getAbsolutePath() + " not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean audio = false;
|
||||
boolean video = false;
|
||||
boolean metadata = true;
|
||||
|
||||
final String formatDebug = "%14s%14s%14s%14s";
|
||||
final String format = "%14s%14s%14s";
|
||||
|
||||
int baseTS = -1;
|
||||
int prevTagSize = 4;
|
||||
int tagHeaderLen = 11;
|
||||
int filePos = 13;
|
||||
|
||||
int prevAudioTS = INVALID_TIMESTAMP;
|
||||
int prevVideoTS = INVALID_TIMESTAMP;
|
||||
long pAudioTagPos = 0l;
|
||||
long pVideoTagPos = 0l;
|
||||
long pMetaTagPos = 0l;
|
||||
|
||||
boolean prevAVCHeader = false;
|
||||
boolean prevAACHeader = false;
|
||||
boolean AVCHeaderWritten = false;
|
||||
boolean AACHeaderWritten = false;
|
||||
|
||||
int fileSize = (int) corruptFile.length();
|
||||
int pFilePos = 0, packetSize = 0, packetTS = 0, tagPos = 0;
|
||||
|
||||
byte[] flvHeader = JDHexUtils.getByteArray("464c5601050000000900000000");
|
||||
byte[] flvTag = new byte[tagHeaderLen];
|
||||
|
||||
final List<Byte> extEnum = new ArrayList<Byte>();
|
||||
extEnum.add((byte) 0x08);
|
||||
extEnum.add((byte) 0x09);
|
||||
extEnum.add((byte) 0x12);
|
||||
|
||||
fixedFile = new File(corruptFile.getAbsolutePath().replace(".part", ".fixed"));
|
||||
|
||||
try {
|
||||
|
||||
fis = new FileInputStream(corruptFile);
|
||||
fos = new FileOutputStream(fixedFile);
|
||||
|
||||
/* FLV? */
|
||||
byte[] flvTagH = new byte[flvHeader.length];
|
||||
fis.read(flvTagH);
|
||||
if (!"FLV".equals(new String(flvTagH).substring(0, 3))) {
|
||||
logger.severe("Input file is not a valid FLV file");
|
||||
return false;
|
||||
} else {
|
||||
fos.write(flvTagH);
|
||||
}
|
||||
|
||||
logger.info("#### FlvFixer: starting scan process... ####");
|
||||
logger.info("processing file: " + corruptFile.getAbsolutePath());
|
||||
if (DEBUG) logOutput("Type :", formatDebug, "CurrentTS", "PreviousTS", "Size", "Position", Level.FINEST);
|
||||
try {
|
||||
|
||||
while (filePos < fileSize) {
|
||||
|
||||
/* 11 byte header */
|
||||
fis.read(flvTag);
|
||||
|
||||
/* size and type of data */
|
||||
byte packetType = flvTag[0];
|
||||
packetSize = getInt(flvTag, 1);
|
||||
packetTS = getInt(flvTag, 4) | (flvTag[7] & 0xff) << 24;
|
||||
|
||||
if (baseTS < 0 && (packetType == Tag.AUDIO.hex || packetType == Tag.VIDEO.hex)) baseTS = packetTS;
|
||||
if (baseTS > 1000) {
|
||||
packetTS -= baseTS;
|
||||
writeFlvTimestamp(packetTS, flvTag);
|
||||
}
|
||||
|
||||
/* header + data */
|
||||
int totalTagLen = tagHeaderLen + packetSize + prevTagSize;
|
||||
byte[] newflvTag = new byte[totalTagLen];
|
||||
System.arraycopy(flvTag, 0, newflvTag, 0, flvTag.length);
|
||||
|
||||
int leftBytes = fis.available();
|
||||
fis.read(newflvTag, tagHeaderLen, packetSize + prevTagSize);
|
||||
|
||||
if (newflvTag.length != totalTagLen || leftBytes < packetSize + prevTagSize) {
|
||||
logger.warning("Broken FLV tag encountered! Aborting further processing.");
|
||||
break;
|
||||
}
|
||||
|
||||
byte AAC_PacketType = 0;
|
||||
byte AVC_PacketType = 0;
|
||||
|
||||
switch (Tag.values()[extEnum.indexOf(packetType)]) {
|
||||
case AUDIO:
|
||||
if (packetTS > prevAudioTS - FRAMEGAP_DURATION * 5) {
|
||||
byte FrameInfo = newflvTag[tagPos + tagHeaderLen];
|
||||
byte CodecID = (byte) ((FrameInfo & 0xF0) >> 4);
|
||||
if (CodecID == Tag.CODEC_ID_AAC.hex) {
|
||||
AAC_PacketType = newflvTag[tagPos + tagHeaderLen + 1];
|
||||
if (AAC_PacketType == Tag.AAC_SEQUENCE_HEADER.hex) {
|
||||
if (AACHeaderWritten) {
|
||||
logOutput("Skipping AAC sequence header AUDIO:", format, packetTS, prevAudioTS, packetSize, Level.INFO);
|
||||
break;
|
||||
} else {
|
||||
logger.info("Writing AAC sequence header");
|
||||
AACHeaderWritten = true;
|
||||
}
|
||||
}
|
||||
if (!AACHeaderWritten) {
|
||||
logOutput("Discarding audio packet received before AAC sequence header AUDIO:", format, packetTS, prevAudioTS, packetSize, Level.WARNING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (packetSize > 0) {
|
||||
/* Check for packets with non-monotonic audio timestamps and fix them */
|
||||
if (!(CodecID == Tag.CODEC_ID_AAC.hex && (AAC_PacketType == Tag.AAC_SEQUENCE_HEADER.hex || prevAACHeader))) {
|
||||
if (prevAudioTS != INVALID_TIMESTAMP && packetTS <= prevAudioTS) {
|
||||
logOutput("Fixing audio timestamp AUDIO:", format, packetTS, prevAudioTS, packetSize, Level.INFO);
|
||||
packetTS += FRAMEGAP_DURATION + (prevAudioTS - packetTS);
|
||||
writeFlvTimestamp(packetTS, flvTag);
|
||||
}
|
||||
}
|
||||
pAudioTagPos = fos.getChannel().position();
|
||||
fos.write(newflvTag);
|
||||
if (DEBUG) logOutput("AUDIO:", formatDebug, packetTS, prevAudioTS, packetSize, pAudioTagPos, Level.FINEST);
|
||||
if (!(CodecID == Tag.CODEC_ID_AAC.hex && AAC_PacketType == Tag.AAC_SEQUENCE_HEADER.hex)) {
|
||||
prevAACHeader = false;
|
||||
prevAudioTS = packetTS;
|
||||
} else {
|
||||
prevAACHeader = true;
|
||||
}
|
||||
} else {
|
||||
logOutput("Skipping small sized audio packet AUDIO:", format, packetTS, prevAudioTS, packetSize, Level.INFO);
|
||||
}
|
||||
} else {
|
||||
logOutput("Skipping audio packet AUDIO:", format, packetTS, prevAudioTS, packetSize, Level.INFO);
|
||||
}
|
||||
if (!audio) audio = true;
|
||||
break;
|
||||
case VIDEO:
|
||||
if (packetTS > prevVideoTS - FRAMEGAP_DURATION * 5) {
|
||||
byte FrameInfo = newflvTag[tagPos + tagHeaderLen];
|
||||
byte FrameType = (byte) ((FrameInfo & 0xF0) >> 4);
|
||||
byte CodecID = (byte) (FrameInfo & 0x0F);
|
||||
if (FrameType == Tag.FRAME_TYPE_INFO.hex) {
|
||||
logOutput("Skipping video info frame VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.WARNING);
|
||||
break;
|
||||
}
|
||||
if (CodecID == Tag.CODEC_ID_AVC.hex) {
|
||||
AVC_PacketType = newflvTag[tagPos + tagHeaderLen + 1];
|
||||
if (AVC_PacketType == Tag.AVC_SEQUENCE_HEADER.hex) {
|
||||
if (AVCHeaderWritten) {
|
||||
logOutput("Skipping AVC sequence header VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.INFO);
|
||||
break;
|
||||
} else {
|
||||
logger.info("Writing AVC sequence header");
|
||||
AVCHeaderWritten = true;
|
||||
}
|
||||
}
|
||||
if (!AVCHeaderWritten) {
|
||||
logOutput("Discarding video packet received before AVC sequence header VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.WARNING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (packetSize > 0) {
|
||||
/* Check for packets with non-monotonic video timestamps and fix them */
|
||||
if (!(CodecID == Tag.CODEC_ID_AVC.hex && (AVC_PacketType == Tag.AVC_SEQUENCE_HEADER.hex || AVC_PacketType == Tag.AVC_SEQUENCE_END.hex || prevAVCHeader))) {
|
||||
if (prevVideoTS != INVALID_TIMESTAMP && packetTS <= prevVideoTS) {
|
||||
logOutput("Fixing video timestamp VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.INFO);
|
||||
packetTS += FRAMEGAP_DURATION + (prevVideoTS - packetTS);
|
||||
writeFlvTimestamp(packetTS, flvTag);
|
||||
}
|
||||
}
|
||||
pVideoTagPos = fos.getChannel().position();
|
||||
fos.write(newflvTag);
|
||||
if (DEBUG) logOutput("VIDEO:", formatDebug, packetTS, prevVideoTS, packetSize, pVideoTagPos, Level.FINEST);
|
||||
if (!(CodecID == Tag.CODEC_ID_AVC.hex && AVC_PacketType == Tag.AVC_SEQUENCE_HEADER.hex)) {
|
||||
prevAVCHeader = false;
|
||||
prevVideoTS = packetTS;
|
||||
} else {
|
||||
prevAVCHeader = true;
|
||||
}
|
||||
} else {
|
||||
logOutput("Skipping small sized video packet VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.INFO);
|
||||
}
|
||||
} else {
|
||||
logOutput("Skipping video packet VIDEO:", format, packetTS, prevVideoTS, packetSize, Level.INFO);
|
||||
}
|
||||
if (!video) video = true;
|
||||
break;
|
||||
case SCRIPT_DATA:
|
||||
if (metadata) {
|
||||
pMetaTagPos = fos.getChannel().position();
|
||||
fos.write(newflvTag);
|
||||
if (DEBUG) logOutput("META :", formatDebug, packetTS, 0, packetSize, pMetaTagPos, Level.FINEST);
|
||||
}
|
||||
break;
|
||||
}
|
||||
filePos += totalTagLen;
|
||||
int cFilePos = (int) filePos / (1024 * 1024);
|
||||
if (cFilePos > pFilePos) {
|
||||
// System.out.printf("Processed %d/%.2f MB\r", cFilePos, fileSize);
|
||||
pFilePos = cFilePos;
|
||||
}
|
||||
}
|
||||
/* Fix flv header when required */
|
||||
if (!(audio && video)) {
|
||||
if (audio && !video) {
|
||||
flvHeader[4] = 4;
|
||||
} else if (video && !audio) {
|
||||
flvHeader[4] = 1;
|
||||
}
|
||||
fos.getChannel().position(0l);
|
||||
fos.write(flvHeader, 0, flvHeader.length);
|
||||
logger.info("Fix flv header --> " + (audio ? "audio" : "video"));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.severe("#### FlvFixer: an error has occurred! " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
try {
|
||||
fos.close();
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
}
|
||||
logger.info("#### FlvFixer: scan process done! ####");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void logOutput(String msg, String formatter, Integer a, Integer b, Integer c, Level l) {
|
||||
logger.log(l, String.format(msg + formatter, a, b, c));
|
||||
}
|
||||
|
||||
private void logOutput(String msg, String formatter, Object a, Object b, Object c, Object d, Level l) {
|
||||
logger.log(l, String.format(msg + formatter, a, b, c, d));
|
||||
}
|
||||
|
||||
private void writeFlvTimestamp(int baseTS, byte[] flvTag) {
|
||||
System.arraycopy(JDHexUtils.getByteArray(String.format("%08x", baseTS)), 0, flvTag, 4, 4);
|
||||
}
|
||||
|
||||
private int getInt(byte[] array, int offset) {
|
||||
return ((0x00 & 0xff) << 24) | ((array[offset] & 0xff) << 16) | ((array[offset + 1] & 0xff) << 8) | (array[offset + 2] & 0xff);
|
||||
}
|
||||
|
||||
}
|
@ -9,12 +9,14 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jd.network.rtmp.url.RtmpUrlConnection;
|
||||
import jd.nutils.JDHash;
|
||||
import jd.plugins.DownloadLink;
|
||||
import jd.plugins.LinkStatus;
|
||||
import jd.plugins.PluginException;
|
||||
import jd.plugins.PluginForHost;
|
||||
import jd.plugins.hoster.RTMPDownload;
|
||||
|
||||
import org.appwork.storage.config.JsonConfig;
|
||||
import org.appwork.utils.Application;
|
||||
import org.appwork.utils.Regex;
|
||||
import org.appwork.utils.formatter.SizeFormatter;
|
||||
@ -23,6 +25,7 @@ import org.appwork.utils.net.throttledconnection.ThrottledConnectionHandler;
|
||||
import org.appwork.utils.os.CrossSystem;
|
||||
import org.appwork.utils.speedmeter.SpeedMeterInterface;
|
||||
import org.jdownloader.nativ.NativeProcess;
|
||||
import org.jdownloader.settings.RtmpdumpSettings;
|
||||
import org.jdownloader.translate._JDT;
|
||||
|
||||
public class RtmpDump extends RTMPDownload {
|
||||
@ -32,7 +35,7 @@ public class RtmpDump extends RTMPDownload {
|
||||
private ThrottledConnectionHandler handler;
|
||||
|
||||
public ThrottledConnectionHandler getHandler() {
|
||||
return this.handler;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
@ -50,7 +53,7 @@ public class RtmpDump extends RTMPDownload {
|
||||
}
|
||||
|
||||
public void setHandler(final ThrottledConnectionHandler manager) {
|
||||
this.handler = manager;
|
||||
handler = manager;
|
||||
}
|
||||
|
||||
public void setLimit(final int kpsLimit) {
|
||||
@ -63,18 +66,28 @@ public class RtmpDump extends RTMPDownload {
|
||||
}
|
||||
|
||||
private Chunk CHUNK;
|
||||
|
||||
private volatile long BYTESLOADED = 0l;
|
||||
|
||||
private long SPEED = 0l;
|
||||
|
||||
private int PID = -1;
|
||||
|
||||
private static String RTMPDUMP = null;
|
||||
|
||||
private static String RTMPVERSION = null;
|
||||
|
||||
private NativeProcess NP;
|
||||
|
||||
private Process P;
|
||||
|
||||
private InputStreamReader R;
|
||||
|
||||
private RtmpdumpSettings config;
|
||||
|
||||
public RtmpDump(final PluginForHost plugin, final DownloadLink downloadLink, final String rtmpURL) throws IOException, PluginException {
|
||||
super(plugin, downloadLink, rtmpURL);
|
||||
config = JsonConfig.create(RtmpdumpSettings.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,14 +124,19 @@ public class RtmpDump extends RTMPDownload {
|
||||
|
||||
private void getProcessId() {
|
||||
try {
|
||||
final Field pidField = this.P.getClass().getDeclaredField("pid");
|
||||
final Field pidField = P.getClass().getDeclaredField("pid");
|
||||
pidField.setAccessible(true);
|
||||
this.PID = pidField.getInt(this.P);
|
||||
PID = pidField.getInt(P);
|
||||
} catch (final Exception e) {
|
||||
this.PID = -1;
|
||||
PID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public String getRtmpDumpChecksum() throws Exception {
|
||||
if (!findRtmpDump()) return null;
|
||||
return JDHash.getMD5(new File(RTMPDUMP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to locate a rtmpdump executable and parse the version number from the 'rtmpdump -h' output.
|
||||
*
|
||||
@ -126,7 +144,7 @@ public class RtmpDump extends RTMPDownload {
|
||||
*/
|
||||
public synchronized String getRtmpDumpVersion() throws Exception {
|
||||
if (RTMPVERSION != null) { return RTMPVERSION; }
|
||||
if (!this.findRtmpDump()) { throw new PluginException(LinkStatus.ERROR_FATAL, "RTMPDump not found!"); }
|
||||
if (!findRtmpDump()) throw new PluginException(LinkStatus.ERROR_FATAL, "RTMPDump not found!");
|
||||
final String arg = " -h";
|
||||
NativeProcess verNP = null;
|
||||
Process verP = null;
|
||||
@ -170,45 +188,48 @@ public class RtmpDump extends RTMPDownload {
|
||||
|
||||
@Override
|
||||
public long getTotalLinkBytesLoadedLive() {
|
||||
return this.BYTESLOADED;
|
||||
return BYTESLOADED;
|
||||
}
|
||||
|
||||
private void sendSIGINT() {
|
||||
this.getProcessId();
|
||||
if (this.PID >= 0) {
|
||||
getProcessId();
|
||||
if (PID >= 0) {
|
||||
try {
|
||||
Runtime.getRuntime().exec("kill -SIGINT " + String.valueOf(this.PID));
|
||||
Runtime.getRuntime().exec("kill -SIGINT " + String.valueOf(PID));
|
||||
} catch (final Throwable e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean start(final RtmpUrlConnection rtmpConnection) throws Exception {
|
||||
if (!this.findRtmpDump()) { throw new PluginException(LinkStatus.ERROR_FATAL, "Error " + RTMPDUMP + " not found!"); }
|
||||
if (!findRtmpDump()) { throw new PluginException(LinkStatus.ERROR_FATAL, "Error " + RTMPDUMP + " not found!"); }
|
||||
|
||||
boolean debug = config.isDebugModeEnabled();
|
||||
|
||||
final ThrottledConnection tcon = new RTMPCon() {
|
||||
@Override
|
||||
public long getSpeedMeter() {
|
||||
return RtmpDump.this.SPEED;
|
||||
return SPEED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transfered() {
|
||||
return RtmpDump.this.BYTESLOADED;
|
||||
return BYTESLOADED;
|
||||
}
|
||||
};
|
||||
try {
|
||||
this.getManagedConnetionHandler().addThrottledConnection(tcon);
|
||||
this.addChunksDownloading(1);
|
||||
this.downloadLink.getLinkStatus().addStatus(LinkStatus.DOWNLOADINTERFACE_IN_PROGRESS);
|
||||
getManagedConnetionHandler().addThrottledConnection(tcon);
|
||||
addChunksDownloading(1);
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.DOWNLOADINTERFACE_IN_PROGRESS);
|
||||
try {
|
||||
this.downloadLink.getDownloadLinkController().getConnectionHandler().addConnectionHandler(this.getManagedConnetionHandler());
|
||||
downloadLink.getDownloadLinkController().getConnectionHandler().addConnectionHandler(getManagedConnetionHandler());
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
rtmpConnection.connect();
|
||||
|
||||
File tmpFile = new File(this.downloadLink.getFileOutput() + ".part");
|
||||
File tmpFile = new File(downloadLink.getFileOutput() + ".part");
|
||||
if (!CrossSystem.isWindows()) {
|
||||
tmpFile = new File(this.downloadLink.getFileOutput().replaceAll("\\s", "\\\\s") + ".part");
|
||||
tmpFile = new File(downloadLink.getFileOutput().replaceAll("\\s", "\\\\s") + ".part");
|
||||
}
|
||||
String line = "", error = "";
|
||||
long iSize = 0;
|
||||
@ -228,24 +249,24 @@ public class RtmpDump extends RTMPDownload {
|
||||
}
|
||||
|
||||
if (cmd.contains(" -e ")) {
|
||||
this.setResume(true);
|
||||
setResume(true);
|
||||
}
|
||||
|
||||
try {
|
||||
if (CrossSystem.isWindows()) {
|
||||
this.NP = new NativeProcess(RTMPDUMP, cmd);
|
||||
this.R = new InputStreamReader(this.NP.getErrorStream());
|
||||
NP = new NativeProcess(RTMPDUMP, cmd);
|
||||
R = new InputStreamReader(NP.getErrorStream());
|
||||
} else {
|
||||
this.P = Runtime.getRuntime().exec(RTMPDUMP + cmd);
|
||||
this.R = new InputStreamReader(this.P.getErrorStream());
|
||||
P = Runtime.getRuntime().exec(RTMPDUMP + cmd);
|
||||
R = new InputStreamReader(P.getErrorStream());
|
||||
}
|
||||
final BufferedReader br = new BufferedReader(this.R);
|
||||
final BufferedReader br = new BufferedReader(R);
|
||||
int sizeCalulateBuffer = 0;
|
||||
float progressFloat = 0;
|
||||
boolean runTimeSize = false;
|
||||
long tmplen = 0, fixedlen = 0, calc = 0;
|
||||
while ((line = br.readLine()) != null) {
|
||||
// System.out.println(line);
|
||||
if (debug) logger.finest(line);
|
||||
if (!line.equals("") && line.startsWith("ERROR:")) {
|
||||
error = line;
|
||||
}
|
||||
@ -255,29 +276,9 @@ public class RtmpDump extends RTMPDownload {
|
||||
String size = new Regex(line, ".*?(\\d.+)").getMatch(0);
|
||||
iSize = SizeFormatter.getSize(size);
|
||||
}
|
||||
// handle redirect
|
||||
if (!this.downloadLink.getBooleanProperty("REDIRECT", false)) {
|
||||
if (line.startsWith("DEBUG:")) {
|
||||
String debugOutput = line.replaceAll("\\s", "");
|
||||
if (debugOutput.startsWith("DEBUG:Property:")) {
|
||||
if (debugOutput.matches("DEBUG:Property:<Name:redirect,STRING:[^>]+>")) {
|
||||
String redirectUrl = new Regex(debugOutput, "STRING:([^>]+)").getMatch(0);
|
||||
this.downloadLink.setProperty("REDIRECT", true);
|
||||
this.downloadLink.setProperty("REDIRECTURL", redirectUrl);
|
||||
if (tmpFile.exists() && tmpFile.length() == 0) {
|
||||
if (!tmpFile.delete()) {
|
||||
this.logger.severe("Could not delete part file " + tmpFile);
|
||||
this.error(LinkStatus.ERROR_LOCAL_IO, _JDT._.system_download_errors_couldnotdelete());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.downloadLink.getDownloadSize() == 0) {
|
||||
this.downloadLink.setDownloadSize(iSize);
|
||||
if (downloadLink.getDownloadSize() == 0) {
|
||||
downloadLink.setDownloadSize(iSize);
|
||||
}
|
||||
// is resumed
|
||||
if (iSize == 0) iSize = downloadLink.getDownloadSize();
|
||||
@ -285,38 +286,38 @@ public class RtmpDump extends RTMPDownload {
|
||||
int pos1 = line.indexOf("(");
|
||||
int pos2 = line.indexOf(")");
|
||||
if (line.toUpperCase().matches("\\d+\\.\\d+\\sKB\\s/\\s\\d+\\.\\d+\\sSEC(\\s\\(\\d+\\.\\d%\\))?")) {
|
||||
progressFloat = (float) (Math.round(this.BYTESLOADED * 100.0F / (float) iSize * 10) / 10.0F);
|
||||
progressFloat = (float) (Math.round(BYTESLOADED * 100.0F / (float) iSize * 10) / 10.0F);
|
||||
if (runTimeSize && pos1 != -1 && pos2 != -1) {
|
||||
progressFloat = Float.parseFloat(line.substring(pos1 + 1, pos2 - 1));
|
||||
}
|
||||
this.BYTESLOADED = SizeFormatter.getSize(line.substring(0, line.toUpperCase().indexOf("KB") + 2));
|
||||
BYTESLOADED = SizeFormatter.getSize(line.substring(0, line.toUpperCase().indexOf("KB") + 2));
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
if (CrossSystem.isWindows()) {
|
||||
this.NP.sendCtrlCSignal();
|
||||
NP.sendCtrlCSignal();
|
||||
} else {
|
||||
this.sendSIGINT();
|
||||
sendSIGINT();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sizeCalulateBuffer > 6 && runTimeSize) {
|
||||
tmplen = (long) (this.BYTESLOADED * 100.0F / progressFloat);
|
||||
fixedlen = this.downloadLink.getDownloadSize();
|
||||
tmplen = (long) (BYTESLOADED * 100.0F / progressFloat);
|
||||
fixedlen = downloadLink.getDownloadSize();
|
||||
calc = Math.abs(((fixedlen / 1024) - (tmplen / 1024)) % 1024);
|
||||
if (calc > 768 && calc < 960) {
|
||||
this.downloadLink.setDownloadSize(tmplen);
|
||||
downloadLink.setDownloadSize(tmplen);
|
||||
}
|
||||
} else {
|
||||
sizeCalulateBuffer++;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - lastTime > 1000) {
|
||||
this.SPEED = (this.BYTESLOADED - before) / (System.currentTimeMillis() - lastTime) * 1000l;
|
||||
SPEED = (BYTESLOADED - before) / (System.currentTimeMillis() - lastTime) * 1000l;
|
||||
lastTime = System.currentTimeMillis();
|
||||
before = this.BYTESLOADED;
|
||||
before = BYTESLOADED;
|
||||
// downloadLink.requestGuiUpdate();
|
||||
this.downloadLink.setChunksProgress(new long[] { this.BYTESLOADED });
|
||||
downloadLink.setChunksProgress(new long[] { BYTESLOADED });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,11 +327,11 @@ public class RtmpDump extends RTMPDownload {
|
||||
// autoresuming when FMS sends NetStatus.Play.Stop and
|
||||
// progress less than 100%
|
||||
if (progressFloat < 99.8 && !line.toLowerCase().contains("download complete")) {
|
||||
System.out.println("Versuch Nr.: " + this.downloadLink.getLinkStatus().getRetryCount() + " ::: " + this.plugin.getMaxRetries(this.downloadLink, null));
|
||||
if (this.downloadLink.getLinkStatus().getRetryCount() >= this.plugin.getMaxRetries(this.downloadLink, null)) {
|
||||
this.downloadLink.getLinkStatus().setRetryCount(0);
|
||||
System.out.println("Versuch Nr.: " + downloadLink.getLinkStatus().getRetryCount() + " ::: " + plugin.getMaxRetries(downloadLink, null));
|
||||
if (downloadLink.getLinkStatus().getRetryCount() >= plugin.getMaxRetries(downloadLink, null)) {
|
||||
downloadLink.getLinkStatus().setRetryCount(0);
|
||||
}
|
||||
this.downloadLink.getLinkStatus().setStatus(LinkStatus.ERROR_DOWNLOAD_INCOMPLETE);
|
||||
downloadLink.getLinkStatus().setStatus(LinkStatus.ERROR_DOWNLOAD_INCOMPLETE);
|
||||
}
|
||||
Thread.sleep(500);
|
||||
break;
|
||||
@ -339,55 +340,79 @@ public class RtmpDump extends RTMPDownload {
|
||||
rtmpConnection.disconnect();
|
||||
try {
|
||||
/* make sure we destroyed the process */
|
||||
this.P.destroy();
|
||||
P.destroy();
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
try {
|
||||
/* close InputStreamReader */
|
||||
this.R.close();
|
||||
R.close();
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
if (this.NP != null) {
|
||||
if (NP != null) {
|
||||
/* close Streams from native */
|
||||
this.NP.closeStreams();
|
||||
NP.closeStreams();
|
||||
}
|
||||
}
|
||||
if (this.downloadLink.getLinkStatus().getStatus() == LinkStatus.ERROR_DOWNLOAD_INCOMPLETE) {
|
||||
return false;
|
||||
} else if (line != null && line.toLowerCase().contains("download complete")) {
|
||||
this.downloadLink.setDownloadSize(this.BYTESLOADED);
|
||||
this.logger.finest("no errors : rename");
|
||||
if (!tmpFile.renameTo(new File(this.downloadLink.getFileOutput()))) {
|
||||
this.logger.severe("Could not rename file " + tmpFile + " to " + this.downloadLink.getFileOutput());
|
||||
this.error(LinkStatus.ERROR_LOCAL_IO, _JDT._.system_download_errors_couldnotrename());
|
||||
|
||||
if (downloadLink.getLinkStatus().getStatus() == LinkStatus.ERROR_DOWNLOAD_INCOMPLETE) return false;
|
||||
if (line != null) {
|
||||
if (line.toLowerCase().contains("download complete")) {
|
||||
downloadLink.setDownloadSize(BYTESLOADED);
|
||||
logger.finest("no errors : rename");
|
||||
if (!tmpFile.renameTo(new File(downloadLink.getFileOutput()))) {
|
||||
logger.severe("Could not rename file " + tmpFile + " to " + downloadLink.getFileOutput());
|
||||
error(LinkStatus.ERROR_LOCAL_IO, _JDT._.system_download_errors_couldnotrename());
|
||||
}
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.FINISHED);
|
||||
/* Stream is succesfully downloaded */
|
||||
return true;
|
||||
}
|
||||
this.downloadLink.getLinkStatus().addStatus(LinkStatus.FINISHED);
|
||||
} else if (error != null && error.toLowerCase().contains("last tag size must be greater/equal zero")) {
|
||||
this.downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_DOWNLOAD_INCOMPLETE);
|
||||
this.downloadLink.reset();
|
||||
return false;
|
||||
} else if (error != null && error.toLowerCase().contains("rtmp_readpacket, failed to read rtmp packet header")) {
|
||||
this.downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE);
|
||||
return false;
|
||||
} else if (line == null && error.isEmpty()) {
|
||||
this.downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_RETRY);
|
||||
this.downloadLink.reset();
|
||||
return false;
|
||||
} else {
|
||||
this.logger.severe("cmd: " + cmd);
|
||||
this.logger.severe(error);
|
||||
if (this.downloadLink.hasProperty("REDIRECT")) this.downloadLink.setProperty("REDIRECT", false);
|
||||
throw new PluginException(LinkStatus.ERROR_FATAL, error);
|
||||
}
|
||||
return true;
|
||||
if (error != null) {
|
||||
if (error.toLowerCase().contains("last tag size must be greater/equal zero")) {
|
||||
FlvFixer flvfix = new FlvFixer();
|
||||
flvfix.setInputFile(tmpFile);
|
||||
if (debug) flvfix.setDebug(true);
|
||||
if (!flvfix.scan()) return false;
|
||||
File fixedFile = flvfix.getoutputFile();
|
||||
if (!fixedFile.exists()) {
|
||||
logger.severe("File " + fixedFile.getAbsolutePath() + " not found!");
|
||||
error(LinkStatus.ERROR_LOCAL_IO, _JDT._.downloadlink_status_error_file_not_found());
|
||||
}
|
||||
if (!tmpFile.delete()) {
|
||||
logger.severe("Could not delete part file " + tmpFile);
|
||||
error(LinkStatus.ERROR_LOCAL_IO, _JDT._.system_download_errors_couldnotdelete());
|
||||
}
|
||||
if (!fixedFile.renameTo(tmpFile)) {
|
||||
logger.severe("Could not rename file " + tmpFile + " to " + downloadLink.getFileOutput());
|
||||
error(LinkStatus.ERROR_LOCAL_IO, _JDT._.system_download_errors_couldnotrename());
|
||||
}
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_DOWNLOAD_INCOMPLETE);
|
||||
} else if (error.toLowerCase().contains("rtmp_readpacket, failed to read rtmp packet header")) {
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE);
|
||||
} else if (error.toLowerCase().contains("netstream.play.streamnotfound")) {
|
||||
downloadLink.deleteFile(true, false);
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_FILE_NOT_FOUND);
|
||||
} else {
|
||||
logger.severe("cmd: " + cmd);
|
||||
logger.severe(error);
|
||||
throw new PluginException(LinkStatus.ERROR_FATAL, error);
|
||||
}
|
||||
}
|
||||
if (error.isEmpty() && line == null) {
|
||||
logger.severe("RtmpDump: An unknown error has occured!");
|
||||
downloadLink.getLinkStatus().addStatus(LinkStatus.ERROR_RETRY);
|
||||
downloadLink.reset();
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
this.downloadLink.setDownloadCurrent(this.BYTESLOADED);
|
||||
this.downloadLink.getLinkStatus().removeStatus(LinkStatus.DOWNLOADINTERFACE_IN_PROGRESS);
|
||||
this.downloadLink.setDownloadInstance(null);
|
||||
this.downloadLink.getLinkStatus().setStatusText(null);
|
||||
this.getManagedConnetionHandler().removeThrottledConnection(tcon);
|
||||
downloadLink.setDownloadCurrent(BYTESLOADED);
|
||||
downloadLink.getLinkStatus().removeStatus(LinkStatus.DOWNLOADINTERFACE_IN_PROGRESS);
|
||||
downloadLink.setDownloadInstance(null);
|
||||
downloadLink.getLinkStatus().setStatusText(null);
|
||||
getManagedConnetionHandler().removeThrottledConnection(tcon);
|
||||
try {
|
||||
this.downloadLink.getDownloadLinkController().getConnectionHandler().removeConnectionHandler(this.getManagedConnetionHandler());
|
||||
downloadLink.getDownloadLinkController().getConnectionHandler().removeConnectionHandler(getManagedConnetionHandler());
|
||||
} catch (final Throwable e) {
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
private static final String KEY_PLAYPATH = "y"; // playpath
|
||||
private static final String KEY_LIVE = "v"; // live
|
||||
private static final String KEY_SUBSCRIBE = "d"; // subscribe
|
||||
private static final String KEY_REALTIME = "R"; // realtime
|
||||
private static final String KEY_START = "A"; // start
|
||||
private static final String KEY_STOP = "B"; // stop
|
||||
private static final String KEY_BUFFER = "b"; // buffer
|
||||
@ -102,23 +103,17 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a communications link to the resource referenced by this URL, if
|
||||
* such a connection has not already been established.
|
||||
* Opens a communications link to the resource referenced by this URL, if such a connection has not already been established.
|
||||
* <p>
|
||||
* If the <code>connect</code> method is called when the connection has
|
||||
* already been opened (indicated by the <code>connected</code> field having
|
||||
* the value <code>true</code>), the call is ignored.
|
||||
* If the <code>connect</code> method is called when the connection has already been opened (indicated by the <code>connected</code>
|
||||
* field having the value <code>true</code>), the call is ignored.
|
||||
* <p>
|
||||
* URLConnection objects go through two phases: first they are created, then
|
||||
* they are connected. After being created, and before being connected,
|
||||
* various options can be specified (e.g., doInput and UseCaches). After
|
||||
* connecting, it is an error to try to set them. Operations that depend on
|
||||
* being connected, like getContentLength, will implicitly perform the
|
||||
* connection, if necessary.
|
||||
* URLConnection objects go through two phases: first they are created, then they are connected. After being created, and before being
|
||||
* connected, various options can be specified (e.g., doInput and UseCaches). After connecting, it is an error to try to set them.
|
||||
* Operations that depend on being connected, like getContentLength, will implicitly perform the connection, if necessary.
|
||||
*
|
||||
* @throws SocketTimeoutException
|
||||
* if the timeout expires before the connection can be
|
||||
* established
|
||||
* if the timeout expires before the connection can be established
|
||||
* @exception IOException
|
||||
* if an I/O error occurs while opening the connection.
|
||||
* @see java.net.URLConnection#connected
|
||||
@ -131,9 +126,8 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that other requests to the server are unlikely in the near
|
||||
* future. Calling disconnect() should not imply that this RtmpURLConnection
|
||||
* instance can be reused for other requests.
|
||||
* Indicates that other requests to the server are unlikely in the near future. Calling disconnect() should not imply that this
|
||||
* RtmpURLConnection instance can be reused for other requests.
|
||||
*/
|
||||
public void disconnect() {
|
||||
connected = false;
|
||||
@ -160,8 +154,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
/**
|
||||
* Returns the value of the <code>content-length</code>.
|
||||
*
|
||||
* @return the content length of the resource that this connection's URL
|
||||
* references, or <code>-1</code> if the content length is not
|
||||
* @return the content length of the resource that this connection's URL references, or <code>-1</code> if the content length is not
|
||||
* known.
|
||||
*/
|
||||
@Override
|
||||
@ -173,8 +166,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
/**
|
||||
* Returns the value of the <code>content-type</code>.
|
||||
*
|
||||
* @return the content type of the resource that the URL references, or
|
||||
* <code>null</code> if not known.
|
||||
* @return the content type of the resource that the URL references, or <code>null</code> if not known.
|
||||
*/
|
||||
@Override
|
||||
public String getContentType() {
|
||||
@ -209,9 +201,8 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of application to connect to on the RTMP server. Overrides the app
|
||||
* in the RTMP URL. Sometimes the librtmp URL parser cannot determine the
|
||||
* app name automatically, so it must be given explicitly using this option.
|
||||
* Name of application to connect to on the RTMP server. Overrides the app in the RTMP URL. Sometimes the librtmp URL parser cannot
|
||||
* determine the app name automatically, so it must be given explicitly using this option.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -229,14 +220,10 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Append arbitrary AMF data to the Connect message. The type must be B for
|
||||
* Boolean, N for number, S for string, O for object, or Z for null. For
|
||||
* Booleans the data must be either 0 or 1 for FALSE or TRUE, respectively.
|
||||
* Likewise for Objects the data must be 0 or 1 to end or begin an object,
|
||||
* respectively. Data items in subobjects may be named, by prefixing the
|
||||
* type with 'N' and specifying the name before the value, e.g. NB:myFlag:1.
|
||||
* This option may be used multiple times to construct arbitrary AMF
|
||||
* sequences. E.g.
|
||||
* Append arbitrary AMF data to the Connect message. The type must be B for Boolean, N for number, S for string, O for object, or Z for
|
||||
* null. For Booleans the data must be either 0 or 1 for FALSE or TRUE, respectively. Likewise for Objects the data must be 0 or 1 to
|
||||
* end or begin an object, respectively. Data items in subobjects may be named, by prefixing the type with 'N' and specifying the name
|
||||
* before the value, e.g. NB:myFlag:1. This option may be used multiple times to construct arbitrary AMF sequences. E.g.
|
||||
* <p>
|
||||
* <tt>
|
||||
* conn=B:1 conn=S:authMe conn=O:1 conn=NN:code:1.23 conn=NS:flag:ok conn=O:0
|
||||
@ -258,8 +245,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of the Flash plugin used to run the SWF player. The default is
|
||||
* "LNX 10,0,32,18".
|
||||
* Version of the Flash plugin used to run the SWF player. The default is "LNX 10,0,32,18".
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -268,8 +254,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the media is a live stream. No resuming or seeking in live
|
||||
* streams is possible.
|
||||
* Specify that the media is a live stream. No resuming or seeking in live streams is possible.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -278,8 +263,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* URL of the web page in which the media was embedded. By default no value
|
||||
* will be sent.
|
||||
* URL of the web page in which the media was embedded. By default no value will be sent.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -288,9 +272,8 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the playpath parsed from the RTMP URL. Sometimes the rtmpdump
|
||||
* URL parser cannot determine the correct playpath automatically, so it
|
||||
* must be given explicitly using this option.
|
||||
* Overrides the playpath parsed from the RTMP URL. Sometimes the rtmpdump URL parser cannot determine the correct playpath
|
||||
* automatically, so it must be given explicitly using this option.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -368,10 +351,17 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how many days to use the cached SWF info before re-checking. Use
|
||||
* 0 to always check the SWF URL. Note that if the check shows that the SWF
|
||||
* file has the same modification timestamp as before, it will not be
|
||||
* retrieved again.
|
||||
* Don't attempt to speed up download via the Pause/Unpause BUFX hack
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void setRealTime() {
|
||||
parameterMap.put(KEY_REALTIME, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how many days to use the cached SWF info before re-checking. Use 0 to always check the SWF URL. Note that if the check shows
|
||||
* that the SWF file has the same modification timestamp as before, it will not be retrieved again.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -407,8 +397,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout the session after num seconds without receiving any data from the
|
||||
* server. The default is 120.
|
||||
* Timeout the session after num seconds without receiving any data from the server. The default is 120.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@ -417,8 +406,7 @@ public class RtmpUrlConnection extends URLConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Key for SecureToken response, used if the server requires SecureToken
|
||||
* authentication.
|
||||
* Key for SecureToken response, used if the server requires SecureToken authentication.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
|
@ -28,7 +28,9 @@ import jd.plugins.DownloadLink;
|
||||
import jd.plugins.FilePackage;
|
||||
import jd.plugins.PluginForDecrypt;
|
||||
|
||||
@DecrypterPlugin(revision = "$Revision$", interfaceVersion = 2, names = { "1000eb.com" }, urls = { "http://(www\\.)?([A-Za-z0-9\\-_]+\\.)?1000eb\\.com/[A-Za-z0-9\\-_]+\\.htm?" }, flags = { 0 })
|
||||
import org.appwork.utils.formatter.SizeFormatter;
|
||||
|
||||
@DecrypterPlugin(revision = "$Revision$", interfaceVersion = 2, names = { "1000eb.com" }, urls = { "http://([\\w\\-\\.]+)?(1000eb\\.com/[\\w\\-]+\\.htm(\\?p=\\d+)?|[\\w\\-\\.]+1000eb\\.com/)" }, flags = { 0 })
|
||||
public class OneThousandEbComFolder extends PluginForDecrypt {
|
||||
|
||||
public OneThousandEbComFolder(final PluginWrapper wrapper) {
|
||||
@ -37,42 +39,72 @@ public class OneThousandEbComFolder extends PluginForDecrypt {
|
||||
|
||||
public ArrayList<DownloadLink> decryptIt(CryptedLink param, ProgressController progress) throws Exception {
|
||||
ArrayList<DownloadLink> decryptedLinks = new ArrayList<DownloadLink>();
|
||||
ArrayList<String> pages = new ArrayList<String>();
|
||||
pages.add("1");
|
||||
String parameter = param.toString();
|
||||
br.getPage(parameter);
|
||||
|
||||
String fpName = br.getRegex("\">我的空间</a>\\ \\>\\>\\ <a href=\"[^<>\"\\']+\" title=\"([^<>\"\\']+)\">").getMatch(0);
|
||||
if (fpName == null) fpName = br.getRegex("<meta name=\"keywords\" content=\"([^,\"]+)").getMatch(0);
|
||||
|
||||
int maxCount = 1;
|
||||
int maxCount = 1, minCount = 1;
|
||||
String lastPage = br.getRegex("class=\"pager\\-golast\"><a href=\"/[A-Za-z0-9\\-_]+\\.htm\\?p=(\\d+)\"").getMatch(0);
|
||||
if (lastPage != null) maxCount = Integer.parseInt(lastPage);
|
||||
for (int i = 1; i <= maxCount; i++) {
|
||||
String firstPage = new Regex(parameter, "\\?p=(\\d+)").getMatch(0);
|
||||
if (firstPage != null) minCount = Integer.parseInt(firstPage);
|
||||
parameter = parameter.replaceAll("\\?p=\\d+", "");
|
||||
if (minCount > maxCount) maxCount = minCount;
|
||||
|
||||
String fpName = br.getRegex("\">我的空间</a>\\ \\>\\>\\ <a href=\"[^<>\"\\']+\" title=\"([^<>\"\\']+)\">").getMatch(0);
|
||||
if (fpName == null) fpName = br.getRegex("<meta name=\"keywords\" content=\"([^,\"]+)").getMatch(0);
|
||||
final FilePackage fp = FilePackage.getInstance();
|
||||
fp.setName(Encoding.htmlDecode(fpName.trim()));
|
||||
|
||||
for (int i = minCount; i <= maxCount; i++) {
|
||||
|
||||
logger.info("Decrypting page " + i + " of " + maxCount);
|
||||
if (i > 1) br.getPage(parameter + "?p=" + i);
|
||||
final String[] links = br.getRegex("class=\"li\\-name\"><a title=\"[^<>\"\\']+\" href=\\'(http://1000eb\\.com/[a-z0-9]+)\\'").getColumn(0);
|
||||
final String[] folderLinks = br.getRegex("\"(/mydirectory[^<>\"]*?)\"").getColumn(0);
|
||||
if ((links == null || links.length == 0) && (folderLinks == null || folderLinks.length == 0)) {
|
||||
logger.warning("Decrypter broken for link: " + parameter);
|
||||
return null;
|
||||
|
||||
try {
|
||||
if (i > minCount) br.getPage(parameter + "?p=" + i);
|
||||
} catch (Throwable e) {
|
||||
logger.warning("1000eb.com: " + e.getMessage());
|
||||
return decryptedLinks;
|
||||
}
|
||||
if (links != null && links.length > 0) {
|
||||
for (final String singleLink : links)
|
||||
decryptedLinks.add(createDownloadlink(singleLink));
|
||||
|
||||
for (final String[] singleLink : br.getRegex("class=\"li\\-name\"><a title=\"[^\"]+\" href=\'(http://1000eb\\.com/[^\']+)\' target=\"_blank\" class=\"ellipsis\">([^<]+)</a></span> <span class=\"li\\-size\">([^<]+)</span>").getMatches()) {
|
||||
DownloadLink dLink = createDownloadlink(singleLink[0]);
|
||||
dLink.setFinalFileName(singleLink[1].trim());
|
||||
try {
|
||||
dLink.setDownloadSize(SizeFormatter.getSize(singleLink[2].replace("M", "MB").replace("K", "KB")));
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
dLink.setAvailable(true);
|
||||
fp.add(dLink);
|
||||
try {
|
||||
if (isAbort()) return decryptedLinks;
|
||||
distribute(dLink);
|
||||
} catch (final Throwable e) {
|
||||
/* does not exist in 09581 */
|
||||
}
|
||||
decryptedLinks.add(dLink);
|
||||
}
|
||||
if (folderLinks != null && folderLinks.length > 0) {
|
||||
final String mainlink = new Regex(parameter, "(http://(www\\.)?[A-Za-z0-9\\-_]+\\.1000eb\\.com)").getMatch(0);
|
||||
for (final String folderLink : folderLinks)
|
||||
decryptedLinks.add(createDownloadlink(mainlink + folderLink));
|
||||
|
||||
sleep(2 * 1000l, param); // prevent a java.out.of.memory error ;-)
|
||||
|
||||
final String mainlink = new Regex(parameter, "(http://(www\\.)?[A-Za-z0-9\\-_]+\\.1000eb\\.com)/?").getMatch(0);
|
||||
for (final String folderLink : br.getRegex("class=\"li\\-name\"><a title=\"[^\"]+\" href=\"([^\"]+)\">[^<]+</a><span class=\"list\\-dir\\-files\">").getColumn(0)) {
|
||||
DownloadLink dLink = createDownloadlink(mainlink + folderLink);
|
||||
fp.add(dLink);
|
||||
try {
|
||||
distribute(dLink);
|
||||
} catch (final Throwable e) {
|
||||
/* does not exist in 09581 */
|
||||
}
|
||||
decryptedLinks.add(dLink);
|
||||
}
|
||||
|
||||
}
|
||||
if (fpName != null) {
|
||||
final FilePackage fp = FilePackage.getInstance();
|
||||
fp.setName(Encoding.htmlDecode(fpName.trim()));
|
||||
fp.addLinks(decryptedLinks);
|
||||
|
||||
if (decryptedLinks == null || decryptedLinks.size() == 0) {
|
||||
logger.warning("Decrypter broken for link: " + parameter);
|
||||
return null;
|
||||
}
|
||||
return decryptedLinks;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -325,7 +325,8 @@ public class OldRAFDownload extends DownloadInterface {
|
||||
if (verifiedSize == false || this.getChunkNum() == 1) {
|
||||
/* we only request a single range */
|
||||
openRangeRequested = true;
|
||||
request.getHeaders().put("Range", "bytes= " + (0) + "-");
|
||||
/* Workaround for server responses != 206 */
|
||||
if (this.downloadLink.getBooleanProperty("ServerComaptibleForByteRangeRequest", true)) request.getHeaders().put("Range", "bytes= " + (0) + "-");
|
||||
} else {
|
||||
/* we request multiple ranges */
|
||||
openRangeRequested = false;
|
||||
@ -369,8 +370,8 @@ public class OldRAFDownload extends DownloadInterface {
|
||||
if (doFilesizeCheck() && (totalLinkBytesLoaded <= 0 || totalLinkBytesLoaded != getFileSize() && getFileSize() > 0)) {
|
||||
if (totalLinkBytesLoaded > getFileSize()) {
|
||||
/*
|
||||
* workaround for old bug deep in this downloadsystem. more data got loaded (maybe just counting bug) than filesize. but in most cases the file
|
||||
* is okay! WONTFIX because new downloadsystem is on its way
|
||||
* workaround for old bug deep in this downloadsystem. more data got loaded (maybe just counting bug) than filesize. but in
|
||||
* most cases the file is okay! WONTFIX because new downloadsystem is on its way
|
||||
*/
|
||||
logger.severe("Filesize: " + getFileSize() + " Loaded: " + totalLinkBytesLoaded);
|
||||
if (!linkStatus.isFailed()) {
|
||||
@ -432,7 +433,8 @@ public class OldRAFDownload extends DownloadInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wartet bis alle Chunks fertig sind, aktuelisiert den downloadlink regelmaesig und fordert beim Controller eine aktualisierung des links an
|
||||
* Wartet bis alle Chunks fertig sind, aktuelisiert den downloadlink regelmaesig und fordert beim Controller eine aktualisierung des
|
||||
* links an
|
||||
*/
|
||||
protected void onChunkFinished() {
|
||||
synchronized (this) {
|
||||
@ -731,7 +733,8 @@ public class OldRAFDownload extends DownloadInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt vor ! dem download dden requesttimeout. Sollte nicht zu niedrig sein weil sonst das automatische kopieren der Connections fehl schlaegt.,
|
||||
* Setzt vor ! dem download dden requesttimeout. Sollte nicht zu niedrig sein weil sonst das automatische kopieren der Connections fehl
|
||||
* schlaegt.,
|
||||
*/
|
||||
public void setRequestTimeout(int requestTimeout) {
|
||||
this.requestTimeout = requestTimeout;
|
||||
|
@ -67,8 +67,10 @@ public class OneThousandEbCom extends PluginForHost {
|
||||
// br.getRegex("src=\"(http://static\\.1000eb\\.com/combo/[^<>]+/file\\.js\\&t=\\d+)\">").getMatch(0);
|
||||
final String dllink = requestDownloadLink("http://1000eb.com/base.js");
|
||||
if (dllink == null) { throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT); }
|
||||
downloadLink.setProperty("ServerComaptibleForByteRangeRequest", false);
|
||||
dl = jd.plugins.BrowserAdapter.openDownload(br, downloadLink, dllink, true, 1);
|
||||
if (dl.getConnection().getContentType().contains("html")) {
|
||||
downloadLink.setProperty("ServerComaptibleForByteRangeRequest", true);
|
||||
br.followConnection();
|
||||
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ public class RTL2De extends PluginForHost {
|
||||
if (urlTmp != null && urlTmp.length < 5) { throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT); }
|
||||
dl = new RTMPDownload(this, downloadLink, DLCONTENT);
|
||||
|
||||
String checksum = ((RTMPDownload) dl).getRtmpDumpChecksum();
|
||||
if (checksum != null && !"422e35de6fb8b333fee3f1fc894ab6e2".equals(checksum)) throw new PluginException(LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE, "JD2 beta update noch nicht vollständig", 24 * 60 * 60 * 1000l);
|
||||
|
||||
String protocol = urlTmp[0];
|
||||
String host = urlTmp[2];
|
||||
String app = urlTmp[3];
|
||||
@ -58,7 +61,7 @@ public class RTL2De extends PluginForHost {
|
||||
|
||||
DLCONTENT = url + "@" + playpath;
|
||||
setupRTMPConnection(dl);
|
||||
if (!((RTMPDownload) dl).startDownload()) throw new PluginException(LinkStatus.ERROR_TEMPORARILY_UNAVAILABLE, 60 * 1000l);
|
||||
((RTMPDownload) dl).startDownload();
|
||||
|
||||
} else {
|
||||
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
|
||||
@ -138,8 +141,8 @@ public class RTL2De extends PluginForHost {
|
||||
rtmp.setFlashVer("WIN 10,1,102,64");
|
||||
rtmp.setUrl(DLCONTENT.split("@")[0]);
|
||||
rtmp.setResume(true);
|
||||
rtmp.setTimeOut(-5);
|
||||
rtmp.setBuffer(60 * 1000);
|
||||
rtmp.setRealTime();
|
||||
rtmp.setTimeOut(-10);
|
||||
}
|
||||
|
||||
}
|
@ -7,11 +7,12 @@ import jd.network.rtmp.RtmpDump;
|
||||
import jd.network.rtmp.url.CustomUrlStreamHandlerFactory;
|
||||
import jd.network.rtmp.url.RtmpUrlConnection;
|
||||
import jd.plugins.DownloadLink;
|
||||
import jd.plugins.LinkStatus;
|
||||
import jd.plugins.PluginException;
|
||||
import jd.plugins.PluginForHost;
|
||||
import jd.plugins.download.RAFDownload;
|
||||
|
||||
//Old librtmp handling in revision < 13938
|
||||
/* Old librtmp handling in revision < 13938 */
|
||||
|
||||
/**
|
||||
* This is a wrapper for RTMP
|
||||
@ -33,7 +34,6 @@ public class RTMPDownload extends RAFDownload {
|
||||
// TODO Auto-generated constructor stub
|
||||
url = new URL(rtmpURL);
|
||||
rtmpConnection = (RtmpUrlConnection) url.openConnection();
|
||||
// setResume(false);
|
||||
downloadLink.setDownloadInstance(this);
|
||||
}
|
||||
|
||||
@ -42,7 +42,18 @@ public class RTMPDownload extends RAFDownload {
|
||||
}
|
||||
|
||||
public boolean startDownload() throws Exception {
|
||||
/* Workaround for retry count loop */
|
||||
if (downloadLink.getLinkStatus().getRetryCount() == plugin.getMaxRetries(downloadLink, null)) { throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT); }
|
||||
final RtmpDump rtmpdump = new RtmpDump(plugin, downloadLink, String.valueOf(url));
|
||||
return rtmpdump.start(rtmpConnection);
|
||||
}
|
||||
|
||||
public String getRtmpDumpChecksum() throws Exception {
|
||||
return new RtmpDump(plugin, downloadLink, String.valueOf(url)).getRtmpDumpChecksum();
|
||||
}
|
||||
|
||||
public String getRtmpDumpVersion() throws Exception {
|
||||
return new RtmpDump(plugin, downloadLink, String.valueOf(url)).getRtmpDumpVersion();
|
||||
}
|
||||
|
||||
}
|
@ -223,36 +223,21 @@ public class VideoPremiumNet extends PluginForHost {
|
||||
private void setupRTMPConnection(String stream, DownloadInterface dl, DownloadLink downloadLink) {
|
||||
jd.network.rtmp.url.RtmpUrlConnection rtmp = ((RTMPDownload) dl).getRtmpConnection();
|
||||
|
||||
/* videopremium.net uses redirections in streams. rtmpdump can't handle this. */
|
||||
String url = stream.split("@")[0];
|
||||
if (downloadLink.getBooleanProperty("REDIRECT", false)) url = downloadLink.getStringProperty("REDIRECTURL", null);
|
||||
|
||||
rtmp.setPlayPath(stream.split("@")[1]);
|
||||
rtmp.setUrl(url);
|
||||
rtmp.setSwfVfy(stream.split("@")[2]);
|
||||
rtmp.setPageUrl(downloadLink.getDownloadURL());
|
||||
rtmp.setResume(true);
|
||||
rtmp.setVerbose(); // needed for redirect feature in rtmpdump.class
|
||||
}
|
||||
|
||||
private void download(DownloadLink downloadLink, String dllink) throws Exception {
|
||||
maxFree.set(10);
|
||||
dl = new RTMPDownload(this, downloadLink, dllink);
|
||||
setupRTMPConnection(dllink, dl, downloadLink);
|
||||
try {
|
||||
if (!((RTMPDownload) dl).startDownload()) {
|
||||
if (downloadLink.getBooleanProperty("REDIRECT", false)) throw new PluginException(LinkStatus.ERROR_RETRY);
|
||||
} else {
|
||||
downloadLink.setProperty("REDIRECT", false);
|
||||
}
|
||||
} catch (PluginException e) {
|
||||
if (e.getLinkStatus() == 4) {
|
||||
logger.info("RTMP: Redirect found! Start download with new url.");
|
||||
} else if (e.getErrorMessage().contains("NetStream.Play.StreamNotFound")) {
|
||||
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
|
||||
} else {
|
||||
throw new PluginException(LinkStatus.ERROR_DOWNLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
((RTMPDownload) dl).startDownload();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
17
src/org/jdownloader/settings/RtmpdumpSettings.java
Normal file
17
src/org/jdownloader/settings/RtmpdumpSettings.java
Normal file
@ -0,0 +1,17 @@
|
||||
package org.jdownloader.settings;
|
||||
|
||||
import org.appwork.storage.config.ConfigInterface;
|
||||
import org.appwork.storage.config.annotations.AboutConfig;
|
||||
import org.appwork.storage.config.annotations.DefaultBooleanValue;
|
||||
import org.appwork.storage.config.annotations.DescriptionForConfigEntry;
|
||||
|
||||
public interface RtmpdumpSettings extends ConfigInterface {
|
||||
|
||||
@AboutConfig
|
||||
@DefaultBooleanValue(false)
|
||||
@DescriptionForConfigEntry("Enable debug mode, nearly everything will be logged!")
|
||||
boolean isDebugModeEnabled();
|
||||
|
||||
void setDebugModeEnabled(boolean b);
|
||||
|
||||
}
|
@ -27,6 +27,7 @@ import org.jdownloader.settings.AccountSettings;
|
||||
import org.jdownloader.settings.GeneralSettings;
|
||||
import org.jdownloader.settings.GraphicalUserInterfaceSettings;
|
||||
import org.jdownloader.settings.InternetConnectionSettings;
|
||||
import org.jdownloader.settings.RtmpdumpSettings;
|
||||
|
||||
public class AdvancedConfigManager {
|
||||
private static final AdvancedConfigManager INSTANCE = new AdvancedConfigManager();
|
||||
@ -36,7 +37,7 @@ public class AdvancedConfigManager {
|
||||
}
|
||||
|
||||
private java.util.List<AdvancedConfigEntry> configInterfaces;
|
||||
private AdvancedConfigEventSender eventSender;
|
||||
private AdvancedConfigEventSender eventSender;
|
||||
|
||||
private AdvancedConfigManager() {
|
||||
configInterfaces = new ArrayList<AdvancedConfigEntry>();
|
||||
@ -57,6 +58,7 @@ public class AdvancedConfigManager {
|
||||
register(JsonConfig.create(StatsManagerConfig.class));
|
||||
register(JsonConfig.create(LogConfig.class));
|
||||
register(JsonConfig.create(ShortcutSettings.class));
|
||||
register(JsonConfig.create(RtmpdumpSettings.class));
|
||||
|
||||
}
|
||||
|
||||
|
3
tools/rtmpdump/rtmpdump-2.4-unofficial
Normal file
3
tools/rtmpdump/rtmpdump-2.4-unofficial
Normal file
@ -0,0 +1,3 @@
|
||||
# with redirect support and other improvements
|
||||
#
|
||||
# https://github.com/svnpenn/rtmpdump
|
Loading…
Reference in New Issue
Block a user