From f39da85605d71f71b54a2860723e100f4479dbc5 Mon Sep 17 00:00:00 2001 From: tonikelope Date: Sun, 2 Oct 2016 17:46:32 +0200 Subject: [PATCH] Refactoring and cleaning 1.6 --- src/megabasterd/AboutDialog.java | 8 +- src/megabasterd/Download.java | 30 +- src/megabasterd/DownloadManager.java | 6 +- src/megabasterd/FolderLinkDialog.java | 14 +- src/megabasterd/KissVideoStreamServer.java | 570 +++++++++++++----- .../KissVideoStreamServerHandler.java | 301 --------- src/megabasterd/MainPanel.java | 45 +- src/megabasterd/MainPanelView.java | 36 +- src/megabasterd/MegaAPI.java | 2 - src/megabasterd/MiscTools.java | 25 + src/megabasterd/StreamerDialog.java | 4 +- src/megabasterd/TransferenceManager.java | 15 +- 12 files changed, 496 insertions(+), 560 deletions(-) delete mode 100644 src/megabasterd/KissVideoStreamServerHandler.java diff --git a/src/megabasterd/AboutDialog.java b/src/megabasterd/AboutDialog.java index a7fb8e441..eb5bc9b4e 100644 --- a/src/megabasterd/AboutDialog.java +++ b/src/megabasterd/AboutDialog.java @@ -166,7 +166,7 @@ public final class AboutDialog extends javax.swing.JDialog { swingReflectionInvoke("setEnabled", check_version_button, false); - final AboutDialog main = this; + final AboutDialog tthis = this; THREAD_POOL.execute(new Runnable(){ @Override @@ -186,7 +186,7 @@ public final class AboutDialog extends javax.swing.JDialog { if(((String)current_node.get("name")).contains("_"+VERSION.replaceAll(" *beta *", "")+".")) { - JOptionPane.showMessageDialog(main, "You have the latest version ;)"); + JOptionPane.showMessageDialog(tthis, "You have the latest version ;)"); new_version = false; @@ -196,14 +196,14 @@ public final class AboutDialog extends javax.swing.JDialog { if(new_version) { - JOptionPane.showMessageDialog(main, "NEW VERSION IS AVAILABLE!"); + JOptionPane.showMessageDialog(tthis, "NEW VERSION IS AVAILABLE!"); } } catch (Exception ex) { getLogger(AboutDialog.class.getName()).log(Level.SEVERE, null, ex); } - swingReflectionInvoke("setEnabled", main.check_version_button, true); + swingReflectionInvoke("setEnabled", check_version_button, true); }}); diff --git a/src/megabasterd/Download.java b/src/megabasterd/Download.java index a6aac9d79..691118a3a 100644 --- a/src/megabasterd/Download.java +++ b/src/megabasterd/Download.java @@ -14,8 +14,6 @@ import static java.lang.Long.valueOf; import static java.lang.Math.ceil; import static java.lang.System.out; import static java.lang.Thread.sleep; -import java.net.URL; -import java.net.URLConnection; import java.nio.channels.FileChannel; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -36,11 +34,10 @@ import static megabasterd.CryptTools.genCrypter; import static megabasterd.DBTools.deleteDownload; import static megabasterd.DBTools.insertDownload; import static megabasterd.DBTools.selectSettingValueFromDB; -import static megabasterd.MainPanel.CONNECTION_TIMEOUT; import static megabasterd.MainPanel.THREAD_POOL; -import static megabasterd.MainPanel.USER_AGENT; import static megabasterd.MiscTools.UrlBASE642Bin; import static megabasterd.MiscTools.bin2i32a; +import static megabasterd.MiscTools.checkMegaDownloadUrl; import static megabasterd.MiscTools.findFirstRegex; import static megabasterd.MiscTools.formatBytes; import static megabasterd.MiscTools.getWaitTimeExpBackOff; @@ -855,30 +852,11 @@ public final class Download implements Transference, Runnable, SecureNotifiable } - public boolean checkDownloadUrl(String string_url) - { - try { - URL url = new URL(string_url+"/0-0"); - URLConnection connection = url.openConnection(); - connection.setConnectTimeout(CONNECTION_TIMEOUT); - connection.setRequestProperty("User-Agent", USER_AGENT); - - try (InputStream is = connection.getInputStream()) { - while(is.read()!=-1); - } - - return true; - - }catch (Exception ex) { - - return false; - } - } - + /* OJO!! -> ESTO ESTÁ CAMBIADO Y NO COMPROBADO!! */ public synchronized String getDownloadUrlForWorker() throws IOException { - if(_last_download_url != null && checkDownloadUrl(_last_download_url)) { + if(_last_download_url != null && checkMegaDownloadUrl(_last_download_url)) { return _last_download_url; } @@ -905,7 +883,7 @@ public final class Download implements Transference, Runnable, SecureNotifiable download_url = MegaCrypterAPI.getMegaFileDownloadUrl(_url, _file_pass, _file_noexpire); } - if(checkDownloadUrl(download_url)) { + if(checkMegaDownloadUrl(download_url)) { _last_download_url = download_url; diff --git a/src/megabasterd/DownloadManager.java b/src/megabasterd/DownloadManager.java index 689941110..add242b69 100644 --- a/src/megabasterd/DownloadManager.java +++ b/src/megabasterd/DownloadManager.java @@ -45,7 +45,7 @@ public final class DownloadManager extends TransferenceManager { try { - this._provision((Download)download, false); + _provision((Download)download, false); secureNotify(); @@ -53,15 +53,13 @@ public final class DownloadManager extends TransferenceManager { System.out.println("Provision failed! Retrying in separated thread..."); - final DownloadManager tthis = this; - THREAD_POOL.execute(new Runnable(){ @Override public void run(){ try { - tthis._provision((Download)download, true); + _provision((Download)download, true); } catch (MegaAPIException | MegaCrypterAPIException ex1) { diff --git a/src/megabasterd/FolderLinkDialog.java b/src/megabasterd/FolderLinkDialog.java index d04861402..0d2162010 100644 --- a/src/megabasterd/FolderLinkDialog.java +++ b/src/megabasterd/FolderLinkDialog.java @@ -269,23 +269,23 @@ public final class FolderLinkDialog extends javax.swing.JDialog { swingReflectionInvoke("setEnabled", file_tree, false); - final FolderLinkDialog main = this; + THREAD_POOL.execute(new Runnable(){ @Override public void run() { - main._loadMegaDirTree(); + _loadMegaDirTree(); - main._genDownloadLiks(); + _genDownloadLiks(); - swingReflectionInvoke("setEnabled", main.restore_button, true); + swingReflectionInvoke("setEnabled", restore_button, true); - swingReflectionInvoke("setVisible", main.restore_button, false); + swingReflectionInvoke("setVisible", restore_button, false); - swingReflectionInvoke("setEnabled", main.dance_button, true); + swingReflectionInvoke("setEnabled", dance_button, true); - swingReflectionInvoke("setEnabled", main.file_tree, true); + swingReflectionInvoke("setEnabled", file_tree, true); }}); }//GEN-LAST:event_restore_buttonActionPerformed diff --git a/src/megabasterd/KissVideoStreamServer.java b/src/megabasterd/KissVideoStreamServer.java index 21a8bdb0f..d46ae0c25 100644 --- a/src/megabasterd/KissVideoStreamServer.java +++ b/src/megabasterd/KissVideoStreamServer.java @@ -1,125 +1,211 @@ package megabasterd; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.awt.Color; +import static java.awt.Frame.NORMAL; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URL; import java.net.URLConnection; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; +import java.util.logging.Level; +import static java.util.logging.Level.SEVERE; +import java.util.logging.Logger; +import static java.util.logging.Logger.getLogger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.crypto.CipherInputStream; import static megabasterd.MainPanel.STREAMER_PORT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.checkMegaDownloadUrl; import static megabasterd.MiscTools.findFirstRegex; import static megabasterd.MiscTools.getWaitTimeExpBackOff; import static megabasterd.MiscTools.swingReflectionInvoke; -public final class KissVideoStreamServer { +public final class KissVideoStreamServer implements HttpHandler, SecureNotifiable { + + public static final int WORKER_STATUS_FILE_INFO=0x01; + public static final int WORKER_STATUS_CONNECT=0x02; + public static final int WORKER_STATUS_STREAM=0x03; + public static final int WORKER_STATUS_RETRY=0x04; + public static final int WORKER_STATUS_EXIT=0x05; - public static final int TIMEOUT=30000; - public static final int EXP_BACKOFF_BASE=2; - public static final int EXP_BACKOFF_SECS_RETRY=1; - public static final int EXP_BACKOFF_MAX_WAIT_TIME=128; private HttpServer _httpserver; - private final MainPanelView _main_panel; + private final MainPanel _main_panel; private final ConcurrentHashMap _link_cache; - private final ConcurrentHashMap _working; + private final ConcurrentHashMap _working_threads; private final ContentType _ctype; - private KissVideoStreamServerHandler _http_handler; - public KissVideoStreamServer(MainPanelView panel) { + private boolean _notified; + private final Object _secure_notify_lock; + + public KissVideoStreamServer(MainPanel panel) { _main_panel = panel; _link_cache = new ConcurrentHashMap(); - _working = new ConcurrentHashMap(); + _working_threads = new ConcurrentHashMap(); _ctype = new ContentType(); + _notified=false; + _secure_notify_lock = new Object(); } - - public KissVideoStreamServerHandler getHandler() - { - return _http_handler; - } - - public MainPanelView getPanel() - { + + public MainPanel getMain_panel() { return _main_panel; } - - public ContentType getCtype() - { + + public ConcurrentHashMap getLink_cache() { + return _link_cache; + } + + public ConcurrentHashMap getWorking_threads() { + return _working_threads; + } + + public ContentType getCtype() { return _ctype; } - public ConcurrentHashMap getStreaming() + @Override + public void secureNotify() { - return _working; + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notify(); + } } - public boolean isWorking() - { - return !_working.isEmpty(); + @Override + public void secureWait() { + + synchronized(_secure_notify_lock) + { + while(!_notified) { + + try { + _secure_notify_lock.wait(); + } catch (InterruptedException ex) { + getLogger(Download.class.getName()).log(SEVERE, null, ex); + } + } + + _notified = false; + } } - + @Override + public void secureNotifyAll() { + + synchronized(_secure_notify_lock) { + + _notified = true; + + _secure_notify_lock.notifyAll(); + } + } + public void start(int port, String context) throws IOException { + swingReflectionInvoke("setForeground", _main_panel.getView().getKiss_server_status(), new Color(0,128,0)); + + swingReflectionInvoke("setText", _main_panel.getView().getKiss_server_status(), "Kissvideostreamer on localhost:"+STREAMER_PORT+" (Waiting for request...)"); + _httpserver = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0); - printStatusOK("Kissvideostreamer on localhost:"+STREAMER_PORT+" (Waiting for request...)"); - _httpserver.createContext(context, (_http_handler = new KissVideoStreamServerHandler(this, _main_panel))); - _httpserver.setExecutor(Executors.newCachedThreadPool()); + + _httpserver.createContext(context, this); + + _httpserver.setExecutor(THREAD_POOL); + _httpserver.start(); - } - - public void stop() - { - _httpserver.stop(0); - } - - public void printStatusError(String message) - { - swingReflectionInvoke("setForeground", _main_panel.getKiss_server_status(), Color.red); - swingReflectionInvoke("setText", _main_panel.getKiss_server_status(), message); - } - - public void printStatusOK(String message) - { - swingReflectionInvoke("setForeground", _main_panel.getKiss_server_status(), new Color(0,128,0)); - swingReflectionInvoke("setText", _main_panel.getKiss_server_status(), message); - } - - public String[] getFromLinkCache(String link) - { - return _link_cache.containsKey(link)?_link_cache.get(link):null; - } - - public void updateLinkCache(String link, String[] info) { - _link_cache.put(link, info); + THREAD_POOL.execute(new Runnable() { + @Override + public void run() { + + while(true) { + + secureWait(); + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Logger.getLogger(KissVideoStreamServer.class.getName()).log(Level.SEVERE, null, ex); + } + + if(_working_threads.isEmpty()) { + + swingReflectionInvoke("setExtendedState", _main_panel.getView(), NORMAL); + + swingReflectionInvoke("setVisible", _main_panel.getView(), true); + } + } + } + }); } - public void removeFromLinkCache(String link) { - _link_cache.remove(link); - } - - public String[] getMegaFileMetadata(String link, MainPanelView panel) throws IOException, InterruptedException - { + private void updateStatus(Integer new_status) { - + if(new_status != WORKER_STATUS_EXIT) { + + getWorking_threads().put(Thread.currentThread(), new_status); + + } else { + + getWorking_threads().remove(Thread.currentThread()); + } + + int conta_info=0, conta_connect=0, conta_stream=0, conta_retry=0; + + for(Integer thread_status:getWorking_threads().values()) { + + switch(thread_status) { + + case WORKER_STATUS_FILE_INFO: + conta_info++; + break; + + case WORKER_STATUS_CONNECT: + conta_connect++; + break; + + case WORKER_STATUS_STREAM: + conta_stream++; + break; + + case WORKER_STATUS_RETRY: + conta_retry++; + break; + } + } + + String status="Kissvideostreamer on localhost:"+STREAMER_PORT+" Info: "+conta_info+" / Conn: "+conta_connect+" / Stream: "+conta_stream+" / Retry: "+conta_retry; + + swingReflectionInvoke("setText", _main_panel.getView().getKiss_server_status(), status); + } + + private String[] getMegaFileMetadata(String link, MainPanelView panel) throws IOException, InterruptedException + { String[] file_info=null; - int retry=0, error_code=0; + int retry=0; boolean error; do { + updateStatus(WORKER_STATUS_FILE_INFO); + error=false; try { - - - if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) + if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) { MegaAPI ma = new MegaAPI(); @@ -128,17 +214,14 @@ public final class KissVideoStreamServer { else { file_info = MegaCrypterAPI.getMegaFileMetadata(link, panel); - } - - + } + } catch(MegaAPIException | MegaCrypterAPIException e) { error=true; - error_code = Integer.parseInt(e.getMessage()); - - switch(error_code) + switch(Integer.parseInt(e.getMessage())) { case -2: throw new IOException("Mega link is not valid!"); @@ -156,23 +239,17 @@ public final class KissVideoStreamServer { throw new IOException("MegaCrypter link has expired!"); default: + + updateStatus(WORKER_STATUS_RETRY); for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) { - if(error_code == -18) - { - printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); - } - else - { - printStatusError("Mega/MC APIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); - } - try { Thread.sleep(1000); } catch (InterruptedException ex) {} } } + } catch(Exception ex) { } @@ -183,65 +260,36 @@ public final class KissVideoStreamServer { return file_info; } - public String getMegaFileDownloadUrl(String link, String pass_hash, String noexpire_token) throws IOException, InterruptedException + private String getMegaFileDownloadUrl(String link, String pass_hash, String noexpire_token) throws IOException, InterruptedException { - - String dl_url=null; - int retry=0, error_code; + int retry=0; boolean error; do { + updateStatus(WORKER_STATUS_FILE_INFO); + error=false; try { - + if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) + { + MegaAPI ma = new MegaAPI(); - - if( findFirstRegex("://mega(\\.co)?\\.nz/", link, 0) != null) - { - MegaAPI ma = new MegaAPI(); - - dl_url = ma.getMegaFileDownloadUrl(link); - } - else - { - dl_url = MegaCrypterAPI.getMegaFileDownloadUrl(link,pass_hash,noexpire_token); - } - - + dl_url = ma.getMegaFileDownloadUrl(link); + } + else + { + dl_url = MegaCrypterAPI.getMegaFileDownloadUrl(link,pass_hash,noexpire_token); + } } - catch(MegaAPIException e) + catch(MegaAPIException | MegaCrypterAPIException e) { error=true; - error_code = Integer.parseInt(e.getMessage()); - - for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) - { - if(error_code == -18) - { - printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); - } - else - { - printStatusError("MegaAPIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); - } - - try { - Thread.sleep(1000); - } catch (InterruptedException ex) {} - } - } - catch(MegaCrypterAPIException e) - { - error=true; - - error_code = Integer.parseInt(e.getMessage()); - - switch(error_code) + switch(Integer.parseInt(e.getMessage())) { case 22: throw new IOException("MegaCrypter link is not valid!"); @@ -253,17 +301,11 @@ public final class KissVideoStreamServer { throw new IOException("MegaCrypter link has expired!"); default: + + updateStatus(WORKER_STATUS_RETRY); + for(long i=getWaitTimeExpBackOff(retry++); i>0; i--) { - if(error_code == -18) - { - printStatusError("File temporarily unavailable! (Retrying in "+i+" secs...)"); - } - else - { - printStatusError("MegaCrypterAPIException error "+e.getMessage()+" (Retrying in "+i+" secs...)"); - } - try { Thread.sleep(1000); } catch (InterruptedException ex) {} @@ -275,28 +317,8 @@ public final class KissVideoStreamServer { return dl_url; } - - public boolean checkDownloadUrl(String string_url) - { - try { - URL url = new URL(string_url+"/0-0"); - URLConnection connection = url.openConnection(); - connection.setConnectTimeout(TIMEOUT); - connection.setRequestProperty("User-Agent", MainPanel.USER_AGENT); - connection.setRequestProperty("Connection", "close"); - try (InputStream is = connection.getInputStream()) { - while(is.read()!=-1); - } - - return true; - - }catch (Exception ex) { - - return false; - } - } - public long[] parseRangeHeader(String header) + private long[] parseRangeHeader(String header) { Pattern pattern = Pattern.compile("bytes\\=([0-9]+)\\-([0-9]+)?"); @@ -319,16 +341,238 @@ public final class KissVideoStreamServer { return ranges; } - public String cookRangeUrl(String url, long[] ranges, int sync_bytes) + private String cookRangeUrl(String url, long[] ranges, int sync_bytes) { return url+"/"+String.valueOf(ranges[0]-sync_bytes)+(ranges[1]>=0?"-"+String.valueOf(ranges[1]):""); } - public void restoreMainWindow() { - - _main_panel.setExtendedState(javax.swing.JFrame.NORMAL); - swingReflectionInvoke("setVisible", _main_panel, true); - } + @Override + public void handle(HttpExchange xchg) throws IOException { + + long clength; + + OutputStream os; + + CipherInputStream cis = null; + + String httpmethod = xchg.getRequestMethod(); + + try{ + + Headers reqheaders=xchg.getRequestHeaders(); + + Headers resheaders = xchg.getResponseHeaders(); + + String url_path = xchg.getRequestURI().getPath(); + + String link = url_path.substring(url_path.indexOf("/video/")+7); + + if(link.indexOf("mega/") == 0) + { + link = link.replaceAll("mega/", "https://mega.co.nz/#"); + } + else + { + String mc_host = findFirstRegex("^[^/]+/", link, 0); + + link = "http://" + mc_host + link; + } + + String[] cache_info, file_info; + + cache_info = getLink_cache().get(link); + + if(cache_info!=null) { + + file_info = new String[6]; + + System.arraycopy( cache_info, 0, file_info, 0, cache_info.length ); + + } else { + + file_info = getMegaFileMetadata(link, _main_panel.getView()); + + cache_info = new String[6]; + + System.arraycopy( file_info, 0, cache_info, 0, file_info.length ); + + cache_info[5]=null; + + } + + String file_name = file_info[0]; + + long file_size = Long.parseLong(file_info[1]); + + String file_key = file_info[2]; + + String pass_hash, noexpire_token; + + if(file_info.length >= 5) + { + pass_hash = file_info[3]; + + noexpire_token = file_info[4]; + + } else { + pass_hash = null; + + noexpire_token = null; + } + + String file_ext = file_name.substring(file_name.lastIndexOf('.')+1).toLowerCase(); + + URLConnection urlConn; + + if(httpmethod.equals("HEAD")) { + + resheaders.add("Accept-Ranges", "bytes"); + + resheaders.add("transferMode.dlna.org", "Streaming"); + + resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); + + resheaders.add("Content-Type", getCtype().getMIME(file_ext)); + + resheaders.add("Content-Length", String.valueOf(file_size)); + + resheaders.add("Connection", "close"); + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); + + } else if(httpmethod.equals("GET")) { + + resheaders.add("Accept-Ranges", "bytes"); + + resheaders.add("transferMode.dlna.org", "Streaming"); + + resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); + + resheaders.add("Content-Type", getCtype().getMIME(file_ext)); + + resheaders.add("Connection", "close"); + + byte[] buffer = new byte[16*1024]; + + int reads; + + String temp_url; + + if(cache_info[5]!=null) { + + temp_url = cache_info[5]; + + if(!checkMegaDownloadUrl(temp_url)) { + + temp_url = getMegaFileDownloadUrl(link,pass_hash,noexpire_token); + + cache_info[5] = temp_url; + + getLink_cache().put(link, file_info); + } + + } else { + temp_url = getMegaFileDownloadUrl(link,pass_hash,noexpire_token); + + cache_info[5] = temp_url; + + getLink_cache().put(link, cache_info); + } + + long[] ranges=new long[2]; + + int sync_bytes=0; + + String header_range=null; + + InputStream is; + + URL url; + + if(reqheaders.containsKey("Range")) + { + header_range = "Range"; + + } else if(reqheaders.containsKey("range")) { + + header_range = "range"; + } + + if(header_range != null) + { + List ranges_raw = reqheaders.get(header_range); + + String range_header=ranges_raw.get(0); + + ranges = parseRangeHeader(range_header); + + sync_bytes = (int)ranges[0] % 16; + + if(ranges[1]>=0 && ranges[1]>=ranges[0]) { + + clength = ranges[1]-ranges[0]+1; + + } else { + + clength = file_size - ranges[0]; + } + + resheaders.add("Content-Range", "bytes "+ranges[0]+"-"+(ranges[1]>=0?ranges[1]:(file_size-1))+"/"+file_size); + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_PARTIAL, clength); + + url = new URL(cookRangeUrl(temp_url, ranges, sync_bytes)); + + } else { + + xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, file_size); + + url = new URL(temp_url); + } + + updateStatus(WORKER_STATUS_CONNECT); + + urlConn = url.openConnection(); + + urlConn.setConnectTimeout(MainPanel.CONNECTION_TIMEOUT); + + urlConn.setRequestProperty("User-Agent", MainPanel.USER_AGENT); + + urlConn.setRequestProperty("Connection", "close"); + + is = urlConn.getInputStream(); + + byte[] iv = CryptTools.initMEGALinkKeyIV(file_key); + + cis = new CipherInputStream(is, CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", CryptTools.initMEGALinkKey(file_key), (header_range!=null && (ranges[0]-sync_bytes)>0)?CryptTools.forwardMEGALinkKeyIV(iv, ranges[0]-sync_bytes):iv)); + + os = xchg.getResponseBody(); + + cis.skip(sync_bytes); + + updateStatus(WORKER_STATUS_STREAM); + + while((reads=cis.read(buffer))!=-1) { + + os.write(buffer, 0, reads); + } + + } + } + catch(Exception ex){ } + finally + { + if(cis!=null) { + cis.close(); + } + + xchg.close(); + + updateStatus(WORKER_STATUS_EXIT); + + secureNotify(); + } + } } diff --git a/src/megabasterd/KissVideoStreamServerHandler.java b/src/megabasterd/KissVideoStreamServerHandler.java deleted file mode 100644 index 69a707e24..000000000 --- a/src/megabasterd/KissVideoStreamServerHandler.java +++ /dev/null @@ -1,301 +0,0 @@ -package megabasterd; - -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.logging.Level; -import static java.util.logging.Logger.getLogger; -import javax.crypto.CipherInputStream; -import javax.crypto.NoSuchPaddingException; -import static megabasterd.MainPanel.STREAMER_PORT; -import static megabasterd.MiscTools.findFirstRegex; - - -public final class KissVideoStreamServerHandler implements HttpHandler { - - private final KissVideoStreamServer _httpserver; - - private final MainPanelView _view; - - private String _file_name; - - private long _file_size; - - private String _file_key; - - private String _pass_hash; - - private String _noexpire_token; - - public KissVideoStreamServerHandler(KissVideoStreamServer server, MainPanelView view) { - - _httpserver = server; - _view = view; - } - - @Override - public void handle(HttpExchange xchg) throws IOException { - - _httpserver.getStreaming().put(Thread.currentThread(), true); - - long clength; - - OutputStream os; - - CipherInputStream cis = null; - - String httpmethod = xchg.getRequestMethod(); - - _httpserver.printStatusOK("Kissvideostreamer (Request received! Dispatching it...)"); - - Headers reqheaders=xchg.getRequestHeaders(); - - Headers resheaders = xchg.getResponseHeaders(); - - String url_path = xchg.getRequestURI().getPath(); - - String link = url_path.substring(url_path.indexOf("/video/")+7); - - if(link.indexOf("mega/") == 0) - { - link = link.replaceAll("mega/", "https://mega.co.nz/#"); - } - else - { - String mc_host = findFirstRegex("^[^/]+/", link, 0); - - link = "http://" + mc_host + link; - } - - _httpserver.printStatusOK("Kissvideostreamer (Retrieving file metadata...)"); - - String[] cache_info, file_info=null; - - cache_info = _httpserver.getFromLinkCache(link); - - if(cache_info!=null) { - - file_info = new String[6]; - - System.arraycopy( cache_info, 0, file_info, 0, cache_info.length ); - - } else { - - try { - - file_info = _httpserver.getMegaFileMetadata(link, _view); - - } catch (InterruptedException ex) { - getLogger(KissVideoStreamServerHandler.class.getName()).log(Level.SEVERE, null, ex); - } - - cache_info = new String[6]; - - System.arraycopy( file_info, 0, cache_info, 0, file_info.length ); - - cache_info[5]=null; - } - - _file_name = file_info[0]; - - _file_size = Long.parseLong(file_info[1]); - - _file_key = file_info[2]; - - if(file_info.length >= 5) - { - _pass_hash = file_info[3]; - - _noexpire_token = file_info[4]; - - } else { - _pass_hash = null; - - _noexpire_token = null; - } - - String file_ext = _file_name.substring(_file_name.lastIndexOf('.')+1).toLowerCase(); - - URLConnection urlConn; - - try{ - - if(httpmethod.equals("HEAD")) { - - resheaders.add("Accept-Ranges", "bytes"); - - resheaders.add("transferMode.dlna.org", "Streaming"); - - resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); - - resheaders.add("Content-Type", _httpserver.getCtype().getMIME(file_ext)); - - resheaders.add("Content-Length", String.valueOf(_file_size)); - - resheaders.add("Connection", "close"); - - xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); - - } else if(httpmethod.equals("GET")) { - - resheaders.add("Accept-Ranges", "bytes"); - - resheaders.add("transferMode.dlna.org", "Streaming"); - - resheaders.add("contentFeatures.dlna.org", "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000"); - - resheaders.add("Content-Type", _httpserver.getCtype().getMIME(file_ext)); - - resheaders.add("Connection", "close"); - - byte[] buffer = new byte[16*1024]; - - int reads; - - _httpserver.printStatusOK("Kissvideostreamer (Retrieving mega temp url...)"); - - String temp_url; - - if(cache_info[5]!=null) { - - temp_url = cache_info[5]; - - if(!_httpserver.checkDownloadUrl(temp_url)) { - - temp_url = _httpserver.getMegaFileDownloadUrl(link,_pass_hash,_noexpire_token); - - cache_info[5] = temp_url; - - _httpserver.updateLinkCache(link, cache_info); - } - - } else { - temp_url = _httpserver.getMegaFileDownloadUrl(link,_pass_hash,_noexpire_token); - - cache_info[5] = temp_url; - - _httpserver.updateLinkCache(link, cache_info); - } - - _httpserver.printStatusOK("Kissvideostreamer (Connecting...)"); - - long[] ranges=new long[2]; - - int sync_bytes=0; - - String header_range=null; - - InputStream is; - - URL url; - - if(reqheaders.containsKey("Range")) - { - header_range = "Range"; - - } else if(reqheaders.containsKey("range")) { - - header_range = "range"; - } - - if(header_range != null) - { - List ranges_raw = reqheaders.get(header_range); - - String range_header=ranges_raw.get(0); - - ranges = _httpserver.parseRangeHeader(range_header); - - sync_bytes = (int)ranges[0] % 16; - - if(ranges[1]>=0 && ranges[1]>=ranges[0]) { - - clength = ranges[1]-ranges[0]+1; - - } else { - - clength = _file_size - ranges[0]; - } - - resheaders.add("Content-Range", "bytes "+ranges[0]+"-"+(ranges[1]>=0?ranges[1]:(_file_size-1))+"/"+_file_size); - - xchg.sendResponseHeaders(HttpURLConnection.HTTP_PARTIAL, clength); - - url = new URL(_httpserver.cookRangeUrl(temp_url, ranges, sync_bytes)); - - } else { - - xchg.sendResponseHeaders(HttpURLConnection.HTTP_OK, _file_size); - - url = new URL(temp_url); - } - - urlConn = url.openConnection(); - urlConn.setConnectTimeout(KissVideoStreamServer.TIMEOUT); - urlConn.setRequestProperty("User-Agent", MainPanel.USER_AGENT); - urlConn.setRequestProperty("Connection", "close"); - is = urlConn.getInputStream(); - - byte[] iv = CryptTools.initMEGALinkKeyIV(_file_key); - - try { - - cis = new CipherInputStream(is, CryptTools.genDecrypter("AES", "AES/CTR/NoPadding", CryptTools.initMEGALinkKey(_file_key), (header_range!=null && (ranges[0]-sync_bytes)>0)?CryptTools.forwardMEGALinkKeyIV(iv, ranges[0]-sync_bytes):iv)); - - } catch ( NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException ex) { - getLogger(KissVideoStreamServerHandler.class.getName()).log(Level.SEVERE, null, ex); - } - - os = xchg.getResponseBody(); - - _httpserver.printStatusOK("Kissvideostreamer (Streaming file "+_file_name+" ...)"); - - //Skip sync bytes - cis.skip(sync_bytes); - - while((reads=cis.read(buffer))!=-1) { - - os.write(buffer, 0, reads); - } - - } - } - catch(Exception ex) - { - - } - finally - { - if(cis!=null) { - cis.close(); - } - - xchg.close(); - - _httpserver.printStatusOK("Kissvideostreamer on localhost:"+STREAMER_PORT+" (Waiting for request...)"); - - _httpserver.getStreaming().remove(Thread.currentThread()); - - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - getLogger(KissVideoStreamServerHandler.class.getName()).log(Level.SEVERE, null, ex); - } - - if(!_httpserver.isWorking()) { - - _httpserver.restoreMainWindow(); - } - } - } -} diff --git a/src/megabasterd/MainPanel.java b/src/megabasterd/MainPanel.java index 224409e8c..9c186daa9 100644 --- a/src/megabasterd/MainPanel.java +++ b/src/megabasterd/MainPanel.java @@ -57,7 +57,7 @@ import static megabasterd.Transference.MAX_TRANSFERENCE_SPEED_DEFAULT; */ public final class MainPanel { - public static final String VERSION="1.5"; + public static final String VERSION="1.6"; public static final String USER_AGENT="Mozilla/5.0 (X11; Linux x8664; rv:48.0) Gecko/20100101 Firefox/48.0"; public static final int CONNECTION_TIMEOUT = 30000; public static final int THROTTLE_SLICE_SIZE=16*1024; @@ -138,7 +138,7 @@ public final class MainPanel { resumeUploads(); - _streamserver = new KissVideoStreamServer(getView()); + _streamserver = new KissVideoStreamServer(this); try { _streamserver.start(STREAMER_PORT, "/video"); @@ -341,7 +341,7 @@ public final class MainPanel { public void _byebye() { - if(_streamserver.isWorking()) { + if(!_streamserver.getWorking_threads().isEmpty()) { Object[] options = {"No", "Yes"}; @@ -437,7 +437,7 @@ public final class MainPanel { swingReflectionInvoke("setText", getView().getStatus_down_label(), "Resuming previous downloads, please wait..."); - final MainPanel main =this; + final MainPanel tthis =this; THREAD_POOL.execute(new Runnable(){ @@ -452,9 +452,9 @@ public final class MainPanel { for(HashMap o:res) { - Download download = new Download(main, (String)o.get("url"), (String)o.get("path"), (String)o.get("filename"), (String)o.get("filekey"), (Long)o.get("filesize"), (String)o.get("filepass"), (String)o.get("filenoexpire"), main._use_slots_down, main._default_slots_down, false); + Download download = new Download(tthis, (String)o.get("url"), (String)o.get("path"), (String)o.get("filename"), (String)o.get("filekey"), (Long)o.get("filesize"), (String)o.get("filepass"), (String)o.get("filenoexpire"), _use_slots_down, _default_slots_down, false); - main.getDownload_manager().getTransference_provision_queue().add(download); + getDownload_manager().getTransference_provision_queue().add(download); conta_downloads++; } @@ -466,15 +466,15 @@ public final class MainPanel { if(conta_downloads>0) { - swingReflectionInvoke("setText", main.getView().getStatus_down_label(), "Starting downloads provisioning, please wait..."); + swingReflectionInvoke("setText", getView().getStatus_down_label(), "Starting downloads provisioning, please wait..."); - main.getDownload_manager().secureNotify(); + getDownload_manager().secureNotify(); - main.getView().getjTabbedPane1().setSelectedIndex(0); + getView().getjTabbedPane1().setSelectedIndex(0); } - swingReflectionInvoke("setText", main.getView().getStatus_down_label(), ""); + swingReflectionInvoke("setText", getView().getStatus_down_label(), ""); }}); @@ -517,12 +517,11 @@ public final class MainPanel { MenuItem closeItem = new MenuItem("EXIT"); - final MainPanel main=this; closeItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - main._byebye(); + _byebye(); } }); @@ -565,7 +564,7 @@ public final class MainPanel { swingReflectionInvoke("setText", getView().getStatus_up_label(), "Resuming previous uploads, please wait..."); - final MainPanel main =this; + final MainPanel tthis =this; THREAD_POOL.execute(new Runnable(){ @Override @@ -585,11 +584,11 @@ public final class MainPanel { MegaAPI ma; - if(main._mega_accounts.get(email) != null) { + if(_mega_accounts.get(email) != null) { - final HashMap account_info = (HashMap)main._mega_accounts.get(email); + final HashMap account_info = (HashMap)_mega_accounts.get(email); - ma = main._mega_active_accounts.get(email); + ma = _mega_active_accounts.get(email); if(ma == null) { @@ -599,16 +598,16 @@ public final class MainPanel { ma.login(email, bin2i32a(BASE642Bin((String)account_info.get("password_aes"))), (String)account_info.get("user_hash")); - main._mega_active_accounts.put(email, ma); + _mega_active_accounts.put(email, ma); } catch (Exception ex) { getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); } } - Upload upload = new Upload(main, ma, (String)o.get("filename"), (String)o.get("parent_node"), (String)o.get("ul_key")!=null?bin2i32a(BASE642Bin((String)o.get("ul_key"))):null, (String)o.get("url"), (String)o.get("root_node"), BASE642Bin((String)o.get("share_key")), (String)o.get("folder_link"), main._use_slots_up, main._default_slots_up, false); + Upload upload = new Upload(tthis, ma, (String)o.get("filename"), (String)o.get("parent_node"), (String)o.get("ul_key")!=null?bin2i32a(BASE642Bin((String)o.get("ul_key"))):null, (String)o.get("url"), (String)o.get("root_node"), BASE642Bin((String)o.get("share_key")), (String)o.get("folder_link"), _use_slots_up, _default_slots_up, false); - main.getUpload_manager().getTransference_provision_queue().add(upload); + getUpload_manager().getTransference_provision_queue().add(upload); conta_uploads++; @@ -620,15 +619,15 @@ public final class MainPanel { if(conta_uploads>0) { - swingReflectionInvoke("setText", main.getView().getStatus_up_label(), "Starting uploads provisioning, please wait..."); + swingReflectionInvoke("setText", getView().getStatus_up_label(), "Starting uploads provisioning, please wait..."); - main.getUpload_manager().secureNotify(); + getUpload_manager().secureNotify(); - main.getView().getjTabbedPane1().setSelectedIndex(1); + getView().getjTabbedPane1().setSelectedIndex(1); } - swingReflectionInvoke("setText", main.getView().getStatus_up_label(), ""); + swingReflectionInvoke("setText", getView().getStatus_up_label(), ""); } catch (Exception ex) { getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); diff --git a/src/megabasterd/MainPanelView.java b/src/megabasterd/MainPanelView.java index c3ca96481..125d12109 100644 --- a/src/megabasterd/MainPanelView.java +++ b/src/megabasterd/MainPanelView.java @@ -510,7 +510,7 @@ public final class MainPanelView extends javax.swing.JFrame { @Override public void run() { - swingReflectionInvoke("setText", tthis.status_down_label, "Pre-processing downloads, please wait..."); + swingReflectionInvoke("setText", status_down_label, "Pre-processing downloads, please wait..."); Set urls = new HashSet(findAllRegex("(?:https?|mega)://[^/]*/(#.*?)?!.+![^\r\n]+", dialog.getLinks_textarea().getText(), 0)); @@ -555,9 +555,9 @@ public final class MainPanelView extends javax.swing.JFrame { for(HashMap folder_link:folder_links) { - download = new Download(tthis.getMain_panel(), (String)folder_link.get("url"), dl_path, (String)folder_link.get("filename"), (String)folder_link.get("filekey"), (long)folder_link.get("filesize"), null, null, tthis.getMain_panel().isUse_slots_down(), tthis.getMain_panel().getDefault_slots_down(), true); + download = new Download(getMain_panel(), (String)folder_link.get("url"), dl_path, (String)folder_link.get("filename"), (String)folder_link.get("filekey"), (long)folder_link.get("filesize"), null, null, getMain_panel().isUse_slots_down(), getMain_panel().getDefault_slots_down(), true); - tthis.getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); + getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); conta_downloads++; } @@ -570,9 +570,9 @@ public final class MainPanelView extends javax.swing.JFrame { } else { - download = new Download(tthis.getMain_panel(), url, dl_path, null, null, null, null, null, tthis.getMain_panel().isUse_slots_down(), tthis.getMain_panel().getDefault_slots_down(), false); + download = new Download(getMain_panel(), url, dl_path, null, null, null, null, null, getMain_panel().isUse_slots_down(), getMain_panel().getDefault_slots_down(), false); - tthis.getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); + getMain_panel().getDownload_manager().getTransference_provision_queue().add(download); conta_downloads++; } @@ -580,18 +580,18 @@ public final class MainPanelView extends javax.swing.JFrame { if(conta_downloads > 0) { - swingReflectionInvoke("setText", tthis.status_down_label, "Starting downloads provisioning, please wait..."); + swingReflectionInvoke("setText", status_down_label, "Starting downloads provisioning, please wait..."); - tthis.getMain_panel().getDownload_manager().secureNotify(); + getMain_panel().getDownload_manager().secureNotify(); } } - swingReflectionInvoke("setText", tthis.status_down_label, ""); + swingReflectionInvoke("setText", status_down_label, ""); swingReflectionInvoke("setEnabled", new_download_menu, true); - tthis.getMain_panel().getDownload_manager().setPreprocessing_transferences(false); + getMain_panel().getDownload_manager().setPreprocessing_transferences(false); }}); } else { @@ -742,21 +742,19 @@ public final class MainPanelView extends javax.swing.JFrame { final ArrayList files = dialog.getFiles(); - final MainPanelView main = this; - jTabbedPane1.setSelectedIndex(1); THREAD_POOL.execute(new Runnable(){ @Override public void run() { - MegaAPI ma=main.getMain_panel().getMega_active_accounts().get(mega_account)!=null?main.getMain_panel().getMega_active_accounts().get(mega_account):new MegaAPI(); + MegaAPI ma=getMain_panel().getMega_active_accounts().get(mega_account)!=null?getMain_panel().getMega_active_accounts().get(mega_account):new MegaAPI(); try { ma.login(mega_account, mega_aes_pass, mega_user_hash); - main.getMain_panel().getMega_active_accounts().put(mega_account, ma); + getMain_panel().getMega_active_accounts().put(mega_account, ma); byte[] parent_key = ma.genFolderKey(); @@ -778,7 +776,7 @@ public final class MainPanelView extends javax.swing.JFrame { for(File f:files) { - swingReflectionInvoke("setText", main.status_up_label, "Pre-processing ("+(conta++)+"/"+files.size()+") uploads, please wait..."); + swingReflectionInvoke("setText", status_up_label, "Pre-processing ("+(conta++)+"/"+files.size()+") uploads, please wait..."); String file_path = f.getParentFile().getAbsolutePath().replace(base_path, ""); @@ -813,15 +811,15 @@ public final class MainPanelView extends javax.swing.JFrame { } } - Upload upload = new Upload(main.getMain_panel(), ma, f.getAbsolutePath(), file_parent, null, null, parent_node, share_key, folder_link, main.getMain_panel().isUse_slots_up(), main.getMain_panel().getDefault_slots_up(), false); + Upload upload = new Upload(getMain_panel(), ma, f.getAbsolutePath(), file_parent, null, null, parent_node, share_key, folder_link, getMain_panel().isUse_slots_up(), getMain_panel().getDefault_slots_up(), false); - main.getMain_panel().getUpload_manager().getTransference_provision_queue().add(upload); + getMain_panel().getUpload_manager().getTransference_provision_queue().add(upload); } - swingReflectionInvoke("setText", main.status_up_label, "Starting uploads provisioning, please wait..."); + swingReflectionInvoke("setText", status_up_label, "Starting uploads provisioning, please wait..."); - main.getMain_panel().getUpload_manager().secureNotify(); + getMain_panel().getUpload_manager().secureNotify(); } catch (Exception ex) { @@ -830,7 +828,7 @@ public final class MainPanelView extends javax.swing.JFrame { swingReflectionInvoke("setEnabled", new_upload_menu, true); - main.getMain_panel().getUpload_manager().setPreprocessing_transferences(false); + getMain_panel().getUpload_manager().setPreprocessing_transferences(false); } }); diff --git a/src/megabasterd/MegaAPI.java b/src/megabasterd/MegaAPI.java index 29569c935..ea3532a89 100644 --- a/src/megabasterd/MegaAPI.java +++ b/src/megabasterd/MegaAPI.java @@ -58,12 +58,10 @@ public final class MegaAPI { private String _root_id; - private String _inbox_id; private String _email; - private String _trashbin_id; private String _req_id; diff --git a/src/megabasterd/MiscTools.java b/src/megabasterd/MiscTools.java index 2a1de3861..49158fdd6 100644 --- a/src/megabasterd/MiscTools.java +++ b/src/megabasterd/MiscTools.java @@ -50,6 +50,8 @@ import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.xml.bind.DatatypeConverter; +import static megabasterd.MainPanel.CONNECTION_TIMEOUT; +import static megabasterd.MainPanel.USER_AGENT; public final class MiscTools { @@ -812,6 +814,29 @@ public final class MiscTools { return res; } + + public static boolean checkMegaDownloadUrl(String string_url) { + + boolean url_ok=false; + + try { + URL url = new URL(string_url+"/0-0"); + URLConnection connection = url.openConnection(); + connection.setConnectTimeout(CONNECTION_TIMEOUT); + connection.setRequestProperty("User-Agent", USER_AGENT); + + try (InputStream is = connection.getInputStream()) { + while(is.read()!=-1); + } + + url_ok=true; + + }catch (Exception ex) {} + + return url_ok; + } + + private MiscTools() { } diff --git a/src/megabasterd/StreamerDialog.java b/src/megabasterd/StreamerDialog.java index 58f87583a..ae4eb968f 100644 --- a/src/megabasterd/StreamerDialog.java +++ b/src/megabasterd/StreamerDialog.java @@ -179,9 +179,9 @@ public final class StreamerDialog extends javax.swing.JDialog implements Clipboa JOptionPane.showMessageDialog(tthis, "Streaming link was copied to clipboard!\n(Remember to keep MegaBasterd running in background while playing)"); - tthis.dispose(); + dispose(); - tthis.getParent().dispatchEvent(new WindowEvent(tthis, WINDOW_CLOSING)); + getParent().dispatchEvent(new WindowEvent(tthis, WINDOW_CLOSING)); } catch (IOException ex) { Logger.getLogger(StreamerDialog.class.getName()).log(Level.SEVERE, null, ex); diff --git a/src/megabasterd/TransferenceManager.java b/src/megabasterd/TransferenceManager.java index 16ec4d090..a6fb2b8dd 100644 --- a/src/megabasterd/TransferenceManager.java +++ b/src/megabasterd/TransferenceManager.java @@ -270,8 +270,6 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable @Override public void run() { - - final TransferenceManager tthis = this; while(true) { @@ -283,7 +281,6 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable @Override public void run(){ - while(!getTransference_provision_queue().isEmpty()) { final Transference transference = getTransference_provision_queue().poll(); @@ -295,9 +292,9 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable } } - tthis.setProvisioning_transferences(false); + setProvisioning_transferences(false); - tthis.secureNotify(); + secureNotify(); }}); @@ -321,9 +318,9 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable } } - tthis.setRemoving_transferences(false); + setRemoving_transferences(false); - tthis.secureNotify(); + secureNotify(); }}); } @@ -346,9 +343,9 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable } } - tthis.setStarting_transferences(false); + setStarting_transferences(false); - tthis.secureNotify(); + secureNotify(); }}); }