├── src ├── icons │ ├── user.png │ ├── counter.png │ ├── status.png │ ├── arrow-090.png │ ├── arrow-270.png │ ├── hourglass.png │ ├── megaphone.png │ ├── rocket-fly.png │ ├── calendar-day.png │ ├── edit-number.png │ ├── exclamation.png │ ├── globe-green.png │ ├── plug-connect.png │ ├── status-busy.png │ ├── status-offline.png │ ├── application--plus.png │ ├── arrow-branch-090.png │ ├── arrow-branch-270.png │ ├── chart-pie-separate.png │ ├── icons-32 │ │ ├── counter.png │ │ ├── megaphone.png │ │ ├── application.png │ │ └── exclamation.png │ ├── network-ethernet.png │ └── folder-horizontal-open.png ├── JRatioBoost.java ├── TokenElement.java ├── SizeConvert.java ├── UpdateConnectionTask.java ├── StartConnectionTask.java ├── JArrow.java ├── JSpinLoader.java ├── Blex.java ├── JCircleProgress.java ├── TrackerConnect.java ├── TorrentInfo.java ├── TorrentElement.java └── Gui.java └── README.md /src/icons/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/user.png -------------------------------------------------------------------------------- /src/icons/counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/counter.png -------------------------------------------------------------------------------- /src/icons/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/status.png -------------------------------------------------------------------------------- /src/icons/arrow-090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/arrow-090.png -------------------------------------------------------------------------------- /src/icons/arrow-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/arrow-270.png -------------------------------------------------------------------------------- /src/icons/hourglass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/hourglass.png -------------------------------------------------------------------------------- /src/icons/megaphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/megaphone.png -------------------------------------------------------------------------------- /src/icons/rocket-fly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/rocket-fly.png -------------------------------------------------------------------------------- /src/icons/calendar-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/calendar-day.png -------------------------------------------------------------------------------- /src/icons/edit-number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/edit-number.png -------------------------------------------------------------------------------- /src/icons/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/exclamation.png -------------------------------------------------------------------------------- /src/icons/globe-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/globe-green.png -------------------------------------------------------------------------------- /src/icons/plug-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/plug-connect.png -------------------------------------------------------------------------------- /src/icons/status-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/status-busy.png -------------------------------------------------------------------------------- /src/icons/status-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/status-offline.png -------------------------------------------------------------------------------- /src/icons/application--plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/application--plus.png -------------------------------------------------------------------------------- /src/icons/arrow-branch-090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/arrow-branch-090.png -------------------------------------------------------------------------------- /src/icons/arrow-branch-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/arrow-branch-270.png -------------------------------------------------------------------------------- /src/icons/chart-pie-separate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/chart-pie-separate.png -------------------------------------------------------------------------------- /src/icons/icons-32/counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/icons-32/counter.png -------------------------------------------------------------------------------- /src/icons/icons-32/megaphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/icons-32/megaphone.png -------------------------------------------------------------------------------- /src/icons/network-ethernet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/network-ethernet.png -------------------------------------------------------------------------------- /src/icons/icons-32/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/icons-32/application.png -------------------------------------------------------------------------------- /src/icons/icons-32/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/icons-32/exclamation.png -------------------------------------------------------------------------------- /src/icons/folder-horizontal-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flightcrank/JRatioBoost/HEAD/src/icons/folder-horizontal-open.png -------------------------------------------------------------------------------- /src/JRatioBoost.java: -------------------------------------------------------------------------------- 1 | 2 | import javax.swing.SwingUtilities; 3 | import javax.swing.UIManager; 4 | 5 | 6 | /** 7 | * 8 | * @author karma 9 | */ 10 | public class JRatioBoost { 11 | 12 | public static void main(String args[]) { 13 | 14 | SwingUtilities.invokeLater(new Runnable() { 15 | 16 | @Override 17 | public void run() { 18 | 19 | //Use native look and feel 20 | try { 21 | 22 | UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); 23 | 24 | //native l&f 25 | //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 26 | 27 | } catch (Exception e) { 28 | 29 | System.out.println(e); 30 | } 31 | 32 | //create and show the Swing GUI 33 | new Gui(args).setVisible(true); 34 | } 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/TokenElement.java: -------------------------------------------------------------------------------- 1 | 2 | //object to represent a bencoded token type and value 3 | class TokenElement { 4 | 5 | public enum Token { 6 | 7 | BYTESTRING, INTEGER, LIST, DICTIONARY, END; 8 | } 9 | 10 | Token type; //the type of token encountered 11 | byte value[]; //the value (if any) associated with a INTEGER or BYTESTRING token 12 | long position; //the byte offset position in the file the token was found at 13 | int endIndex; //DICTIONARY, LIST, BYTESTRING and INTEGER tokens will hold the array index on their END token. END tokens will hold the index of the calling tokens 14 | 15 | public String getValueString() { 16 | 17 | if (value != null) { 18 | 19 | String str = new String(value); 20 | 21 | if (str.matches(".+")) { 22 | 23 | return str; 24 | } 25 | } 26 | 27 | return null; 28 | } 29 | 30 | public String toString() { 31 | 32 | return String.format("[%s, %s, %d, %d]", type, this.getValueString(), position, endIndex); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/SizeConvert.java: -------------------------------------------------------------------------------- 1 | 2 | //This class can convert bytes into kilo, mega, or gigabytes 3 | class SizeConvert { 4 | 5 | enum SizeType { 6 | 7 | B, KB, MB, GB; 8 | } 9 | 10 | float value; 11 | SizeType size; 12 | 13 | public SizeConvert(long bytes) { 14 | 15 | long oneKB = 1024; 16 | long oneMB = (long) Math.pow(1024, 2); 17 | long oneGB = (long) Math.pow(1024, 3); 18 | 19 | if (bytes >= 1024 && bytes < oneMB) { 20 | 21 | value = kiloBytes(bytes); 22 | size = SizeType.KB; 23 | 24 | } else if (bytes >= oneMB && bytes < oneGB) { 25 | 26 | value = megaBytes(bytes); 27 | size = SizeType.MB; 28 | 29 | } else if (bytes >= oneGB) { 30 | 31 | value = gigaBytes(bytes); 32 | size = SizeType.GB; 33 | 34 | } else { 35 | 36 | value = (float) bytes; 37 | size = SizeType.B; 38 | } 39 | } 40 | 41 | public static float kiloBytes(long bytes) { 42 | 43 | return (float) bytes / 1024.0f; 44 | } 45 | 46 | public static float megaBytes(long bytes) { 47 | 48 | return kiloBytes(bytes) / 1024.0f; 49 | } 50 | 51 | public static float gigaBytes(long bytes) { 52 | 53 | return megaBytes(bytes) / 1024.0f; 54 | } 55 | 56 | public static long KBToB(int kb) { 57 | 58 | return kb * 1024; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | 64 | return String.format("%.02f %s", value, size); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/UpdateConnectionTask.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.IOException; 3 | import java.net.MalformedURLException; 4 | import java.util.concurrent.ExecutionException; 5 | import javax.swing.SwingWorker; 6 | 7 | /** 8 | * 9 | * @author karma 10 | */ 11 | public class UpdateConnectionTask extends SwingWorker { 12 | 13 | private TorrentElement te; 14 | private TrackerConnect tc; 15 | private Gui gui; 16 | public static final String UPDATE_SUCCESSFULL = "Update Connection Successfull"; 17 | 18 | public UpdateConnectionTask(TorrentElement te, Gui gui) { 19 | 20 | this.te = te; 21 | this.tc = te.gettConn(); 22 | this.gui = gui; 23 | } 24 | 25 | //preform connection to tracker in its own background thread to prevent blocking 26 | //the GUI on the Event dispatch thread. 27 | @Override 28 | protected String doInBackground() throws Exception { 29 | 30 | try { 31 | 32 | //send get request to tracker with upload/download data 33 | tc.connect(String.format("%d", te.getUploadAmount()), "0"); 34 | 35 | } catch (MalformedURLException ex) { 36 | 37 | return "URL Error: The tracker URL is not properly formatted.\n" + ex.getMessage(); 38 | 39 | } catch (IOException ex) { 40 | 41 | return "Error IO (network or timeout): \n" + ex.getMessage(); 42 | 43 | } 44 | 45 | return UPDATE_SUCCESSFULL; 46 | } 47 | 48 | @Override 49 | protected void done() { 50 | 51 | String result; 52 | 53 | try { 54 | 55 | result = get(); 56 | 57 | if (result.equals(UpdateConnectionTask.UPDATE_SUCCESSFULL)) { 58 | 59 | //keep track if the amount of spoofed uploads that has been sent so far 60 | long num = te.getUploadSent() + (te.getUploadAmount() - te.getUploadSent()); 61 | te.setUploadSent(num); 62 | 63 | //add a new announcement to the list 64 | String announce = String.format("%s?info_hash=%s&peer_id=%s&port=%s&uploaded=%d&downloaded=%d&left=0&compact=1", te.gettInfo().announce, te.gettInfo().hexStringUrlEnc(0), te.gettInfo().hexStringUrlEnc(1), tc.port, te.getUploadAmount(), te.getDownloadAmount()); 65 | te.getNumAnnouce().add(announce); 66 | 67 | //reset interval timer 68 | te.setTimeLeft(Integer.parseInt(te.gettConn().interval)); 69 | 70 | te.getTimer().start(); 71 | 72 | System.out.println(result + ": " + te.getNumAnnouce().get(te.getNumAnnouce().size() - 1)); 73 | 74 | } else { 75 | 76 | te.getErrorMsg().add(result); // Error Type 77 | gui.errorsButton.setText(String.valueOf(te.getErrorMsg().size())); 78 | gui.stopAction(te.getIndex()); 79 | } 80 | 81 | } catch (InterruptedException | ExecutionException e) { 82 | 83 | String errMsg = "InterruptedException/ExecutionException: " + e.getMessage(); 84 | 85 | te.getErrorMsg().add(errMsg); // Error content 86 | gui.errorsButton.setText(String.valueOf(te.getErrorMsg().size())); 87 | gui.stopAction(te.getIndex()); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 🚀 JRatioBoost 3 | 4 | ![Static Badge](https://img.shields.io/badge/Supports%20Java-%208%20and%20above-orange) 5 | ![GitHub Downloads](https://img.shields.io/github/downloads/flightcrank/jratioboost/total?style=flat&label=Downloaded) 6 | 7 | JRatioBoost is now at version 2.1. It is used to spoof uploads to private torrent trackers. 8 | It will simply report to the torrent tracker that you are uploading, when you are really uploading nothing at all. 9 | This will give you a good "ratio" on any private tracker that you use. It will work on any major operating system as long as 10 | Java is installed on that system. 11 | 12 | This program has been updated to version 2.1 and is considered complete. It does what it set out to do on every torrent tracker I have tested it on. 13 | I will continue to maintain this program. If there are any issues contact me and ill see if i can solve them. This project started in 2014 as a C program and 2018 14 | as a java program to fill the need of a spoofing program on linux as there were no other at the time. 15 | 16 | it is designed to work on any Java version, from Java 8 or above. 17 | 18 | features... 19 | 20 | * 🆕 updated GUI (new progress bar) 21 | * runs multiple torrents at a time 22 | * can open multiple .torrent files as command line arguments or through the GUI file picker 23 | * spoofs multiple torrent clients (kTorrent, Transmission, uTorrent and more) choose any version number for any of them ! 24 | * choose from any announce URL listed in the torrent file 25 | * change port number reported to tracker 26 | * runs on any operating system Yay for Linux users 🐧! 27 | * free and open source software ! 28 | 29 | change log 30 | 31 | 21/11/2025 32 | * improved error checking 33 | * added custom progress bar 34 | * fixed missing download label arrow icon 35 | 36 | Compilation instructions are as follows... (browse to the src directory) 37 | 38 | $ javac JRatioBoost.java 39 | 40 | that should compile all necessary .java files. to run the program type the command 41 | 42 | $ java JRatioBoost 43 | 44 | If you just want to run the program, then go to "Releases" and download the latest version that it ready for use 45 | and follow the instructions on how to run the program 46 | 47 | [Releases Page](https://github.com/flightcrank/JRatioBoost/releases) 48 | 49 | NOTE: for users on openSuse or fedora based distros having trouble establishing a connection to **https** trackers. Changing 50 | this line in the java.security file from true to **false** should solve connection issues you may have on java 8. 51 | 52 | `security.useSystemPropertiesFile=true` 53 | 54 | to 55 | 56 | `security.useSystemPropertiesFile=false` 57 | 58 | ## Images 59 | screenshot of version 2.1 60 | ![swing screenshot 3](https://i.imgur.com/fygr4h4.png) 61 | 62 | screenshot of version 2.0 63 | ![swing screenshot 2](https://i.imgur.com/QJICBO5.png) 64 | 65 | screenshot of previous version 1.0 66 | ![swing screenshot 1](https://i.imgur.com/8G9l9Ra.png) 67 | -------------------------------------------------------------------------------- /src/StartConnectionTask.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.IOException; 3 | import java.net.MalformedURLException; 4 | import java.util.concurrent.ExecutionException; 5 | import javax.swing.SwingWorker; 6 | import javax.swing.Timer; 7 | 8 | /** 9 | * 10 | * @author karma 11 | */ 12 | public class StartConnectionTask extends SwingWorker { 13 | 14 | TorrentElement te; 15 | Gui gui; 16 | public static final String CONNECTION_SUCCESSFULL = "Started Connection Successfull"; 17 | 18 | public StartConnectionTask(TorrentElement te, Gui gui) { 19 | 20 | this.te = te; 21 | this.gui = gui; 22 | } 23 | 24 | @Override 25 | protected String doInBackground() throws Exception { 26 | 27 | try { 28 | 29 | te.settConn(new TrackerConnect(te.gettInfo(), te.getPort(), te.getCustomUserAgent())); 30 | 31 | } catch (MalformedURLException ex) { 32 | 33 | return "URL Error: The tracker URL is not properly formatted.\n" + ex.getMessage(); 34 | 35 | } catch (IOException ex) { 36 | 37 | return "Error IO (network or timeout): \n" + ex.getMessage(); 38 | 39 | } catch (Exception ex) { 40 | 41 | return "Exception: \n" + ex.getMessage(); 42 | } 43 | 44 | return StartConnectionTask.CONNECTION_SUCCESSFULL; 45 | } 46 | 47 | @Override 48 | protected void done() { 49 | 50 | String result; 51 | 52 | try { 53 | 54 | result = get(); 55 | 56 | if (result.equals(StartConnectionTask.CONNECTION_SUCCESSFULL)) { 57 | 58 | String announce = String.format("%s?info_hash=%s&peer_id=%s&port=%s&uploaded=0&downloaded=0&left=0&compact=1&event=started", te.gettInfo().announce, te.gettInfo().hexStringUrlEnc(0), te.gettInfo().hexStringUrlEnc(1), te.gettConn().port); 59 | te.getNumAnnouce().add(announce); 60 | System.out.println(result + ": " + te.getNumAnnouce().get(te.getNumAnnouce().size() - 1)); 61 | 62 | //valid connection made 63 | if (te.gettConn() != null && te.gettConn().valid == true) { 64 | 65 | gui.connectButton.setText("Stop"); 66 | gui.torrentList.repaint(); 67 | 68 | Timer t = new Timer(1000, e -> { 69 | 70 | // This code runs on the EDT, guaranteed safe for GUI updates! 71 | gui.periodicUpdate(te); 72 | }); 73 | 74 | te.setTimeLeft(Integer.parseInt(te.gettConn().interval)); 75 | te.setTimer(t); 76 | t.start(); 77 | } 78 | 79 | } else { 80 | 81 | te.getErrorMsg().add(result); // Error content 82 | gui.errorsButton.setText(String.valueOf(te.getErrorMsg().size())); 83 | gui.stopAction(te.getIndex()); 84 | } 85 | 86 | } catch (InterruptedException | ExecutionException e) { 87 | 88 | String errMsg = new String("Error during execution: " + e.getCause().getMessage()); 89 | 90 | te.getErrorMsg().add(errMsg); // Error content 91 | gui.errorsButton.setText(String.valueOf(te.getErrorMsg().size())); 92 | gui.stopAction(te.getIndex()); 93 | 94 | } finally { 95 | 96 | gui.WindowPanel.setCursor(null); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/JArrow.java: -------------------------------------------------------------------------------- 1 | 2 | import java.awt.*; 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import javax.swing.*; 6 | import java.awt.image.BufferedImage; 7 | 8 | public class JArrow extends JComponent { 9 | 10 | public final static int LEFT = 0; 11 | public final static int RIGHT = 1; 12 | public final static int UP = 2; 13 | public final static int DOWN = 3; 14 | private int direction = UP; 15 | 16 | public JArrow() { 17 | 18 | setDoubleBuffered(true); 19 | setPreferredSize(new Dimension(30, 30)); 20 | } 21 | 22 | public void setDirection(int num) { 23 | 24 | this.direction = num; 25 | } 26 | 27 | public static BufferedImage createArrow(int width, int height, int direction) { 28 | 29 | BufferedImage arrowImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 30 | Graphics2D g2d = arrowImg.createGraphics(); 31 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 32 | g2d.setColor(new Color(150, 150, 150)); 33 | 34 | Polygon p = new Polygon(); 35 | int centerX = width / 2; 36 | int centerY = height / 2; 37 | int base = width / 3; 38 | 39 | p.addPoint(centerX,0); 40 | p.addPoint(width, centerY); 41 | p.addPoint(width - base, centerY); 42 | p.addPoint(width - base, height); 43 | p.addPoint(base , height); 44 | p.addPoint(base, centerY); 45 | p.addPoint(0, centerY); 46 | 47 | switch (direction) { 48 | 49 | case RIGHT: 50 | 51 | g2d.rotate(Math.PI / 2, centerX, centerY); 52 | g2d.fillPolygon(p); 53 | break; 54 | case LEFT: 55 | 56 | g2d.rotate((Math.PI / 2) * 3, centerX, centerY); 57 | g2d.fillPolygon(p); 58 | break; 59 | case DOWN: 60 | 61 | g2d.rotate(Math.PI, centerX, centerY); 62 | g2d.fillPolygon(p); 63 | break; 64 | default: 65 | g2d.fillPolygon(p); 66 | } 67 | 68 | return arrowImg; 69 | } 70 | 71 | public void drawArrowImg(Graphics g) { 72 | 73 | Graphics2D g2d = (Graphics2D) g; 74 | BufferedImage img = createArrow(50, 50, direction); 75 | 76 | int centerX = this.getWidth() / 2; 77 | int centerY = this.getHeight() / 2; 78 | 79 | g2d.translate(centerX - 25, centerY - 25); 80 | g2d.drawImage(img, null, 0, 0); 81 | } 82 | 83 | public void drawArrow(Graphics g) { 84 | 85 | Graphics2D g2d = (Graphics2D) g; 86 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 87 | g2d.setPaint(new Color(150, 150, 150)); 88 | 89 | Polygon p = new Polygon(); 90 | int centerX = this.getWidth() / 2; 91 | int centerY = this.getHeight() / 2; 92 | int base = this.getWidth() / 3; 93 | 94 | p.addPoint(centerX,0); 95 | p.addPoint(this.getWidth(), centerY); 96 | p.addPoint(this.getWidth() - base, centerY); 97 | p.addPoint(this.getWidth() - base, this.getHeight()); 98 | p.addPoint(base , this.getHeight()); 99 | p.addPoint(base, centerY); 100 | p.addPoint(0, centerY); 101 | 102 | switch (direction) { 103 | 104 | case RIGHT: 105 | 106 | g2d.rotate(Math.PI / 2, centerX, centerY); 107 | g2d.fillPolygon(p); 108 | break; 109 | case LEFT: 110 | 111 | g2d.rotate((Math.PI / 2) * 3, centerX, centerY); 112 | g2d.fillPolygon(p); 113 | break; 114 | case DOWN: 115 | 116 | g2d.rotate(Math.PI, centerX, centerY); 117 | g2d.fillPolygon(p); 118 | break; 119 | default: 120 | g2d.fillPolygon(p); 121 | } 122 | } 123 | 124 | @Override 125 | public void paintComponent(Graphics g) { 126 | 127 | super.paintComponent(g); 128 | Toolkit.getDefaultToolkit().sync(); //smoother animation in linux 129 | drawArrow(g); 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/JSpinLoader.java: -------------------------------------------------------------------------------- 1 | import java.awt.*; 2 | import java.awt.event.ActionEvent; 3 | import java.awt.event.ActionListener; 4 | import java.awt.image.BufferedImage; 5 | import javax.swing.*; 6 | 7 | public class JSpinLoader extends JComponent { 8 | 9 | int numSteps = 8; 10 | int size = 5; 11 | double rot = 0; 12 | double speed = .05; 13 | boolean clockwise = true; 14 | ImageIcon spinnerIcon = null; 15 | Timer timer; 16 | ActionListener taskPerformer; 17 | 18 | public JSpinLoader() { 19 | 20 | setDoubleBuffered(true); 21 | setPreferredSize(new Dimension(30, 30)); 22 | 23 | taskPerformer = new ActionListener() { 24 | 25 | @Override 26 | public void actionPerformed(ActionEvent evt) { 27 | 28 | animate(); 29 | repaint(); 30 | } 31 | }; 32 | 33 | timer = new Timer(33, taskPerformer); 34 | } 35 | 36 | public JSpinLoader(int width, int height, JButton jb) { 37 | 38 | setDoubleBuffered(true); 39 | setPreferredSize(new Dimension(30, 30)); 40 | 41 | taskPerformer = new ActionListener() { 42 | 43 | @Override 44 | public void actionPerformed(ActionEvent evt) { 45 | 46 | animate(); 47 | jb.setIcon(new ImageIcon(createSpinnerImage(width, height, rot))); 48 | } 49 | }; 50 | 51 | timer = new Timer(33, taskPerformer); 52 | } 53 | 54 | public void start() { 55 | 56 | if (!timer.isRunning()) { 57 | 58 | timer.start(); 59 | } 60 | } 61 | 62 | public void stop() { 63 | 64 | if (timer.isRunning()) { 65 | 66 | timer.stop(); 67 | } 68 | } 69 | 70 | public void setSize(int num) { 71 | 72 | this.size = num; 73 | } 74 | 75 | public void setSteps(int num) { 76 | 77 | this.numSteps = num; 78 | } 79 | 80 | public void setDirection(boolean direction) { 81 | 82 | clockwise = direction; 83 | } 84 | 85 | public void setSpeed(double num) { 86 | 87 | this.speed = num; 88 | } 89 | 90 | private void animate() { 91 | 92 | if (clockwise == true) { 93 | 94 | rot += speed; 95 | 96 | } else { 97 | 98 | rot -= speed; 99 | } 100 | 101 | if (rot >= 2 * Math.PI || rot <= -2 * Math.PI) { 102 | 103 | rot = 0; 104 | } 105 | } 106 | 107 | public BufferedImage createSpinnerImage(int width, int height, double direction) { 108 | 109 | BufferedImage spinnerImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 110 | Graphics2D g2d = spinnerImage.createGraphics(); 111 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 112 | g2d.setColor(new Color(150, 150, 150)); 113 | 114 | int centerX = width / 2; 115 | int centerY = height / 2; 116 | double angleAmount = (2 * Math.PI) / numSteps; 117 | int radius = centerY - (int) (size / 2); 118 | 119 | g2d.translate(centerX, centerY); 120 | g2d.rotate(direction); 121 | 122 | for (int i = 0; i < numSteps; i++) { 123 | 124 | double x = Math.sin(angleAmount * i) * radius; 125 | double y = Math.cos(angleAmount * i) * radius; 126 | g2d.fillOval((int) x - (size / 2) ,(int) y - (size / 2) , size, size); 127 | } 128 | 129 | return spinnerImage; 130 | } 131 | 132 | public void draw(Graphics g) { 133 | 134 | Graphics2D g2d = (Graphics2D) g; 135 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 136 | g2d.setPaint(new Color(150, 150, 150)); 137 | //g2d.drawString("Java 2D", 50, 50); 138 | int centerX = this.getWidth() / 2; 139 | int centerY = this.getHeight() / 2; 140 | double angleAmount = (2 * Math.PI) / numSteps; 141 | int radius = centerY - (int) (size / 2); 142 | 143 | g2d.translate(centerX, centerY); 144 | g2d.rotate(rot); 145 | 146 | for (int i = 0; i < numSteps; i++) { 147 | 148 | double x = Math.sin(angleAmount * i) * radius; 149 | double y = Math.cos(angleAmount * i) * radius; 150 | g2d.fillOval((int) x - (size / 2) ,(int) y - (size / 2) , size, size); 151 | } 152 | } 153 | 154 | @Override 155 | public void paintComponent(Graphics g) { 156 | 157 | super.paintComponent(g); 158 | Toolkit.getDefaultToolkit().sync(); //smoother animation in linux 159 | draw(g); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Blex.java: -------------------------------------------------------------------------------- 1 | 2 | //Lexical analyser for bencoded torrent files 3 | //JDK 7 or above 4 | 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.nio.channels.FileChannel; 8 | import java.util.ArrayDeque; 9 | import java.util.ArrayList; 10 | 11 | //a class to lexically scan a bencoded file and store the tokens and their values in an ArrayList 12 | class Blex { 13 | 14 | ArrayList tokenList; //array of all token elements and data 15 | boolean valid = false; 16 | 17 | Blex(FileInputStream fis) throws IOException { 18 | 19 | tokenList = new ArrayList(); 20 | valid = this.readFile(fis); 21 | } 22 | 23 | public String toString() { 24 | 25 | return String.format("Blex: [valid=%b, num_tokens=%d]", valid, tokenList.size()); 26 | } 27 | 28 | //open and process the bencoded file 29 | private boolean readFile(FileInputStream file) throws IOException { 30 | 31 | int fileIndex; //store the current byte read from a file 32 | StringBuffer len = new StringBuffer(); //store a string of numbers to represent the INTEGER token value or the BYTESTRING length 33 | ArrayDeque stack = new ArrayDeque();//object to track that all the required tokens have a END token 34 | 35 | FileChannel fChan = file.getChannel(); //object to obtain offset current position within the file 36 | 37 | do { 38 | 39 | fileIndex = file.read(); 40 | 41 | //DICTIONARY token found 42 | if (fileIndex == (int) 'd') { 43 | 44 | TokenElement token = new TokenElement(); 45 | 46 | //populate tokens instance variables 47 | token.type = TokenElement.Token.DICTIONARY; 48 | token.position = fChan.position(); 49 | this.tokenList.add(token); 50 | stack.push(tokenList.size() - 1);//subtract 1 to get the index number not the size of an array 51 | 52 | //LIST token found 53 | } else if (fileIndex == (int) 'l') { 54 | 55 | TokenElement token = new TokenElement(); 56 | 57 | //populate tokens instance variables 58 | token.type = TokenElement.Token.LIST; 59 | token.position = fChan.position(); 60 | this.tokenList.add(token); 61 | stack.push(tokenList.size() - 1);//subtract 1 to get the index number not the size of an array 62 | 63 | //INTEGER token found 64 | } else if (fileIndex == (int) 'i') { 65 | 66 | TokenElement token = new TokenElement(); 67 | 68 | //populate tokens instance variables 69 | token.type = TokenElement.Token.INTEGER; 70 | token.position = fChan.position(); 71 | this.tokenList.add(token); 72 | stack.push(tokenList.size() - 1);//subtract 1 to get the index number not the size of an array 73 | 74 | //END token found 75 | } else if (fileIndex == (int) 'e') { 76 | 77 | TokenElement token = new TokenElement(); 78 | TokenElement startToken; 79 | TokenElement prevToken = tokenList.get(tokenList.size() - 1); 80 | 81 | if (prevToken.type == TokenElement.Token.INTEGER) { 82 | 83 | prevToken.value = len.toString().getBytes(); 84 | } 85 | 86 | //populate tokens instance variables 87 | token.type = TokenElement.Token.END; 88 | token.position = fChan.position(); 89 | 90 | if (stack.size() != 0) { 91 | 92 | token.endIndex = stack.peek();//store the index of the TOKEN this END token ends. 93 | } 94 | 95 | this.tokenList.add(token); 96 | 97 | len.delete(0, len.length());//could be end of an integer so clear the len StringBuffer for the next time a string of numbers is encountered 98 | 99 | if (stack.size() != 0) { 100 | 101 | stack.pop(); 102 | } 103 | 104 | //populate the endIndex field of the calling token with the index of this END token 105 | int index = this.tokenList.size() - 1;//-1 to obtain index number size 106 | startToken = this.tokenList.get(token.endIndex); 107 | startToken.endIndex = index; 108 | 109 | //number found: read in string of number/s and save to a StringBuffer 110 | } else if (fileIndex >= (int) '0' && fileIndex <= (int) '9' ) { 111 | 112 | len.append((char) fileIndex); 113 | 114 | //BYTESTRING token found: read in number of bytes stored by len (a StringBuffer) 115 | //and store into a byte array 116 | } else if (fileIndex == (int) ':') { 117 | 118 | int byteStrLen = Integer.parseInt(len.toString()); 119 | byte byteStr[] = new byte[byteStrLen]; 120 | TokenElement token = new TokenElement(); 121 | 122 | token.type = TokenElement.Token.BYTESTRING; 123 | token.position = fChan.position(); 124 | fileIndex = file.read(byteStr); 125 | token.value = byteStr; 126 | this.tokenList.add(token); 127 | len.delete(0, len.length());//reset len(StringBuffer) for the next time numbers are encountered 128 | } 129 | 130 | } while (fileIndex > -1); 131 | 132 | //validates the file, making sure all required tokens have a matching END token 133 | return stack.isEmpty(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/JCircleProgress.java: -------------------------------------------------------------------------------- 1 | 2 | import java.awt.Color; 3 | import java.awt.Font; 4 | import java.awt.FontMetrics; 5 | import java.awt.Graphics; 6 | import java.awt.Graphics2D; 7 | import java.awt.RenderingHints; 8 | import java.awt.Shape; 9 | import java.awt.geom.Area; 10 | import java.awt.geom.Ellipse2D; 11 | import java.awt.geom.Rectangle2D; 12 | import javax.swing.JProgressBar; 13 | 14 | /** 15 | * 16 | * @author karma 17 | */ 18 | 19 | public class JCircleProgress extends JProgressBar { 20 | 21 | private boolean outline; 22 | private Color c; 23 | private FontMetrics fm; 24 | 25 | public JCircleProgress() { 26 | 27 | this.setValue(0); 28 | this.setMaximum(100); 29 | this.setStringPainted(true); 30 | this.outline = true; 31 | this.c = Color.ORANGE; 32 | this.setFont(new Font("Arial", Font.PLAIN, 18)); 33 | this.fm = getFontMetrics(this.getFont()); 34 | } 35 | 36 | public Color getColour() { 37 | 38 | return c; 39 | } 40 | 41 | public void setColour(Color c) { 42 | 43 | this.c = c; 44 | } 45 | 46 | public boolean isOutline() { 47 | 48 | return outline; 49 | } 50 | 51 | public void setOutline(boolean outline) { 52 | 53 | this.outline = outline; 54 | } 55 | 56 | @Override 57 | public void paintComponent(Graphics grphcs) { 58 | 59 | //super.paintComponent(grphcs); 60 | 61 | Graphics2D g2d = (Graphics2D) grphcs.create(); 62 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); 63 | g2d.setColor(c); 64 | 65 | int v = this.getValue(); 66 | int max = this.getMaximum(); 67 | 68 | float f = (float) v / max; 69 | float r = f * 360; 70 | 71 | double mainDiameter; 72 | 73 | 74 | //choose the smallest of the width or height of the JComponent to determine the diameter of the circle 75 | if (this.getWidth() > this.getHeight()) { 76 | 77 | mainDiameter = this.getHeight() - 2; 78 | 79 | } else { 80 | 81 | mainDiameter = this.getWidth() - 2; 82 | } 83 | 84 | //Define the dimensions and position of the hole 85 | double innerDiameter = mainDiameter / 2; 86 | 87 | //define the upper left corner of where to begin drawing from in the JComponent 88 | int main_circle_X = (this.getWidth() / 2) - (int)mainDiameter / 2; 89 | int main_circle_Y = (this.getHeight() / 2) - (int)mainDiameter / 2; 90 | 91 | int inner_circle_X = (this.getWidth() / 2) - (int)innerDiameter / 2; 92 | int inner_circle_Y = (this.getHeight() / 2) - (int)innerDiameter / 2; 93 | 94 | // Define the Round Hole Shape 95 | Ellipse2D.Double holeShape = new Ellipse2D.Double(inner_circle_X, inner_circle_Y, innerDiameter, innerDiameter); 96 | 97 | //Create a Custom Clipping Area 98 | //Start with the full bounds of the component 99 | Area customClip = new Area(new Rectangle2D.Double(0, 0, getWidth(), getHeight())); 100 | 101 | //Subtract the hole from the clipping area 102 | customClip.subtract(new Area(holeShape)); 103 | 104 | //Save and Set the Clip 105 | Shape oldClip = g2d.getClip(); // Save existing clip to restore later 106 | g2d.setClip(customClip); // Apply the custom clip with the hole 107 | 108 | //draw the main progress circle 109 | g2d.fillArc(main_circle_X, main_circle_Y, (int)mainDiameter, (int)mainDiameter, 90, -(int)r); 110 | 111 | //Restore the original clip 112 | g2d.setClip(oldClip); 113 | 114 | //Apply the font to the Graphics context 115 | g2d.setFont(this.getFont()); 116 | 117 | 118 | if (outline) { 119 | 120 | //outer outline 121 | g2d.setColor(new Color(153,153,153)); 122 | g2d.draw(new Ellipse2D.Double(main_circle_X, main_circle_Y, mainDiameter, mainDiameter)); 123 | 124 | g2d.setColor(Color.WHITE); 125 | g2d.draw(new Ellipse2D.Double(main_circle_X + 1, main_circle_Y + 1, mainDiameter - 2, mainDiameter - 2)); 126 | 127 | //inner outline 128 | g2d.setColor(new Color(153,153,153)); 129 | g2d.draw(new Ellipse2D.Double(inner_circle_X + 1, inner_circle_Y + 1, innerDiameter - 2, innerDiameter - 2)); 130 | 131 | g2d.setColor(Color.WHITE); 132 | g2d.draw(new Ellipse2D.Double(inner_circle_X, inner_circle_Y, innerDiameter, innerDiameter)); 133 | } 134 | 135 | if (this.paintString) { 136 | 137 | //int textWidth = this.fm.stringWidth(this.getString() + "%"); // This is the width in pixels 138 | int textWidth = this.fm.stringWidth(this.getString()); // This is the width in pixels 139 | int n = textWidth / 2; 140 | g2d.setColor(Color.BLACK); 141 | g2d.drawString(this.getString(), (this.getWidth() / 2) - n, (this.getHeight() / 2) + this.fm.getAscent() / 2); 142 | } 143 | 144 | } 145 | 146 | public float calculatePercentage () { 147 | 148 | return ((float) this.getValue() / this.getMaximum()) * 100; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/TrackerConnect.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.*; 3 | import java.net.MalformedURLException; 4 | import java.net.URL; 5 | import java.net.URLConnection; 6 | import java.util.ArrayList; 7 | 8 | //class to connect to a remote torrent tracker and issue HTML GET requests 9 | class TrackerConnect { 10 | 11 | public String seeders; 12 | public String leechers; 13 | public String interval; 14 | public String minInterval; 15 | public String port; 16 | public String customUserAgent; 17 | public boolean valid; 18 | public TorrentInfo tInfo; 19 | 20 | TrackerConnect(TorrentInfo tInfo, String port, String userAgent) throws MalformedURLException, IOException, Exception { 21 | 22 | this.tInfo = tInfo; 23 | this.valid = false; 24 | this.port = port; 25 | this.customUserAgent = userAgent; 26 | connect(); 27 | } 28 | 29 | //request to start a new torrent connection to tracker. using this method with no arguments will 30 | //also send the events=started message to the tracker. 31 | public final void connect() throws MalformedURLException, IOException, Exception { 32 | 33 | URL tracker; 34 | URLConnection conn = null; 35 | 36 | //check if the annouce URL in the torrent file has a query sting already in it. 37 | char q = (tInfo.announce.contains("?") == true) ? '&' : '?'; 38 | 39 | String queryString = String.format("info_hash=%s&peer_id=%s&port=%s&uploaded=0&downloaded=0&left=0&compact=1&event=started", tInfo.hexStringUrlEnc(0), tInfo.hexStringUrlEnc(1), this.port); 40 | String request = String.format("%s%s%s", tInfo.announce, q, queryString); 41 | 42 | this.doRequest(request); 43 | } 44 | 45 | //request to send upload and download data to tracker. Note this method omits the "event" key in the 46 | //query string as this overloaded method is for updating the ongoing connection 47 | public final void connect(String uploaded, String downloaded) throws MalformedURLException, IOException, Exception { 48 | 49 | //check if the annouce URL in the torrent file has a query sting already in it. 50 | char q = (tInfo.announce.contains("?") == true) ? '&' : '?'; 51 | 52 | String queryString = String.format("info_hash=%s&peer_id=%s&port=%s&uploaded=%s&downloaded=%s&left=0&compact=1", tInfo.hexStringUrlEnc(0), tInfo.hexStringUrlEnc(1), this.port, uploaded, downloaded); 53 | String request = String.format("%s%s%s", tInfo.announce, q, queryString); 54 | 55 | this.doRequest(request); 56 | } 57 | 58 | private void doRequest(String request) throws MalformedURLException, FileNotFoundException, IOException, Exception { 59 | 60 | URL tracker; 61 | URLConnection conn = null; 62 | String userAgent = getUserAgent(); 63 | 64 | if (this.customUserAgent != null) { 65 | 66 | userAgent = this.customUserAgent; 67 | } 68 | 69 | //connect to torrent tracker 70 | tracker = new URL(request); 71 | conn = tracker.openConnection(); 72 | conn.setRequestProperty("User-Agent", userAgent); 73 | 74 | Blex blex = responce(conn); 75 | 76 | if (blex.valid) { 77 | 78 | valid = true; 79 | checkFailResponse(blex.tokenList); 80 | setInfo(blex.tokenList); 81 | 82 | } else { 83 | 84 | valid = false; 85 | throw new Exception("Invalid bencoded responce."); 86 | } 87 | } 88 | 89 | private String getUserAgent() { 90 | 91 | String userAgent; 92 | char[] client = {(char)tInfo.peerId[1], (char)tInfo.peerId[2]}; 93 | char[] ver = {(char)tInfo.peerId[3],(char)tInfo.peerId[4],(char)tInfo.peerId[5],(char)tInfo.peerId[6]}; 94 | 95 | switch (new String(client)) { 96 | 97 | case "KT": 98 | userAgent = "KTorrent/" + ver[0] + "." + ver[1] + "." + ver[2]+ "." + ver[3]; 99 | break; 100 | case "TR": 101 | userAgent = "Transmission/" + ver[0] + "." + ver[1] + "." + ver[2]+ "." + ver[3]; 102 | break; 103 | case "JT": 104 | userAgent = "JavaTorrent/" + ver[0] + "." + ver[1] + "." + ver[2]+ "." + ver[3]; 105 | break; 106 | case "qB": 107 | userAgent = "qBittorrent/" + ver[0] + "." + ver[1] + "." + ver[2]+ "." + ver[3]; 108 | break; 109 | case "UT": 110 | userAgent = "µTorrent/" + ver[0] + "." + ver[1] + "." + ver[2]+ "." + ver[3]; 111 | break; 112 | 113 | default: 114 | userAgent = "KTorrent/5.1.1"; 115 | } 116 | 117 | return userAgent; 118 | } 119 | 120 | private void checkFailResponse(ArrayList tokenList) throws Exception { 121 | 122 | for (int i = 0; i < tokenList.size(); i++) { 123 | 124 | String str = tokenList.get(i).getValueString(); 125 | 126 | if (str != null && str.equals("failure reason")) { 127 | 128 | this.valid = false; 129 | String failReason = tokenList.get(i + 1).getValueString(); 130 | throw new Exception("\nTracker Failure Reason, " + failReason); 131 | } 132 | } 133 | } 134 | 135 | //populate instace variables from the responce recieved from the tracker 136 | private void setInfo(ArrayList tokenList) { 137 | 138 | for (int i = 0; i -1); 185 | 186 | return new Blex(new FileInputStream(tempFile.getPath())); 187 | } 188 | 189 | @Override 190 | public String toString() { 191 | 192 | return String.format("Tracker Connect: [seeders:%s leechers:%s interval:%s min interval:%s]", seeders, leechers, interval, minInterval); 193 | } 194 | } 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/TorrentInfo.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.FileInputStream; 3 | import java.io.IOException; 4 | import java.nio.channels.FileChannel; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.ArrayDeque; 8 | import java.util.ArrayList; 9 | 10 | class TorrentInfo { 11 | 12 | String announce; //tracker announce url 13 | String creationDate; //torrent creation date 14 | String createdBy; //author for torrent file 15 | String comment; //torrent comments 16 | String size; //download size of torrent 17 | String name; //filename (singlefile) or dirname (multifile) 18 | String encoding; 19 | ArrayList announceList; //list of announce urls 20 | byte[] infoHash; //20 byte torrent SHA1 info hash 21 | byte[] peerId; //20 byte torrent SHA1 info hash 22 | public static final int INFO_HASH = 0; 23 | public static final int PEER_ID = 1; 24 | 25 | public TorrentInfo(FileInputStream file) throws IOException, Exception { 26 | 27 | Blex lexTree = new Blex(file); 28 | 29 | if (lexTree.valid) { 30 | 31 | announceList = new ArrayList(); 32 | infoHash = new byte[20]; 33 | peerId = new byte[20]; 34 | 35 | setInfo(lexTree.tokenList); 36 | computeInfoHash(lexTree.tokenList, file); 37 | computePeerId("TR4050"); 38 | 39 | } else { 40 | 41 | throw new Exception("File did not pass bencoded validation test"); 42 | } 43 | } 44 | 45 | public String toString() { 46 | 47 | return String.format("TorrentInfo: [%s, %s, %s, %s]", announce, this.hexString(infoHash), this.hexString(peerId), this.hexStringUrlEnc(1)); 48 | } 49 | 50 | //calculate the client peer id 51 | public void computePeerId(String client) { 52 | 53 | byte[] b = client.getBytes(); 54 | 55 | for (int i = 0; i < peerId.length; i++) { 56 | 57 | peerId[i] = (byte) (Math.random() * 256.0); 58 | } 59 | 60 | peerId[0] = (byte) '-'; 61 | peerId[1] = b[0]; 62 | peerId[2] = b[1]; 63 | peerId[3] = b[2]; 64 | peerId[4] = b[3]; 65 | peerId[5] = b[4]; 66 | peerId[6] = b[5]; 67 | peerId[7] = (byte) '-'; 68 | } 69 | 70 | //covert info hash or peer id to a hexadecimal string 71 | public String hexString(byte[] a) { 72 | 73 | StringBuilder sb = new StringBuilder(a.length * 2); 74 | 75 | for(byte b : a) { 76 | 77 | sb.append(String.format("%02x", b)); 78 | } 79 | 80 | return sb.toString(); 81 | } 82 | 83 | //urlencode info hash or peerid hex string 84 | public String hexStringUrlEnc(int i) { 85 | 86 | byte[] hString; 87 | 88 | if (i == INFO_HASH) { 89 | 90 | hString = infoHash; 91 | 92 | } else { 93 | 94 | hString = peerId; 95 | } 96 | 97 | StringBuilder sb = new StringBuilder(); 98 | 99 | for (byte b : hString) { 100 | 101 | if (b >= (int) '0' && b <= (int) '9') { 102 | 103 | sb.append((char) b); 104 | 105 | } else if (b >= (int) 'a' && b <= (int) 'z') { 106 | 107 | sb.append((char) b); 108 | 109 | } else if (b >= (int) 'A' && b <= (int) 'Z') { 110 | 111 | sb.append((char) b); 112 | 113 | } else if (b == (int) '.' || b == (int) '-' || b == (int) '_' || b == (int) '~') { 114 | 115 | sb.append((char) b); 116 | 117 | } else { 118 | 119 | sb.append(String.format("%%%02x", b)); 120 | } 121 | } 122 | 123 | return sb.toString(); 124 | } 125 | 126 | //populate instance variables with data from the tokenList generated by the Blex object 127 | private void setInfo(ArrayList tokenArray) { 128 | 129 | long size = 0; 130 | 131 | for(int i = 0; i < tokenArray.size(); i++) { 132 | 133 | String str = tokenArray.get(i).getValueString(); 134 | 135 | if (str != null && str.equals("announce")) { 136 | 137 | i++; 138 | this.announce = tokenArray.get(i).getValueString(); 139 | 140 | } else if (str != null && str.equals("created by")) { 141 | 142 | i++; 143 | this.createdBy = tokenArray.get(i).getValueString(); 144 | 145 | } else if (str != null && str.equals("creation date")) { 146 | 147 | i++; 148 | this.creationDate = tokenArray.get(i).getValueString(); 149 | 150 | } else if (str != null && str.equals("comment")) { 151 | 152 | i++; 153 | this.comment = tokenArray.get(i).getValueString(); 154 | 155 | } else if (str != null && str.equals("encoding")) { 156 | 157 | i++; 158 | this.encoding = tokenArray.get(i).getValueString(); 159 | 160 | } else if (str != null && str.equals("name")) { 161 | 162 | i++; 163 | this.name = tokenArray.get(i).getValueString(); 164 | 165 | } else if (str != null && str.equals("length")) { 166 | 167 | i++; 168 | String num = tokenArray.get(i).getValueString(); 169 | size += Long.parseLong(num); 170 | 171 | } else if (str != null && str.equals("announce-list")) { 172 | 173 | i++; 174 | ArrayDeque announceStack = new ArrayDeque(); 175 | announceStack.push(tokenArray.get(i)); 176 | 177 | //check if there is a list of LIST token on a stack 178 | while (!announceStack.isEmpty()) { 179 | 180 | i++; 181 | 182 | if (tokenArray.get(i).type == TokenElement.Token.END) { 183 | 184 | announceStack.pop(); 185 | 186 | } else if (tokenArray.get(i).type == TokenElement.Token.LIST) { 187 | 188 | announceStack.push(tokenArray.get(i)); 189 | 190 | } else if (tokenArray.get(i).type == TokenElement.Token.BYTESTRING) { 191 | 192 | this.announceList.add(tokenArray.get(i).getValueString()); 193 | } 194 | } 195 | } 196 | 197 | this.size = String.format("%d", size); 198 | } 199 | } 200 | 201 | //calculate the infoHash 202 | private void computeInfoHash(ArrayList tokenList, FileInputStream file) throws IOException{ 203 | 204 | TokenElement[] tokenArray = new TokenElement[tokenList.size()]; 205 | long startOffset = 0; 206 | long endOffset = 0; 207 | 208 | tokenArray = tokenList.toArray(tokenArray); 209 | 210 | for (int i = 0; i < tokenArray.length; i++) { 211 | 212 | String value = tokenArray[i].getValueString(); 213 | 214 | //search for the info token 215 | if (value != null && value.equals("info")) { 216 | 217 | TokenElement infoDictionary = tokenArray[i + 1]; 218 | TokenElement endToken = tokenArray[infoDictionary.endIndex]; 219 | startOffset = infoDictionary.position - 1;//the read() function will consume the next by so set it 1 position back 220 | endOffset = endToken.position; 221 | } 222 | } 223 | 224 | long len = endOffset - startOffset; 225 | byte[] hashData = new byte[(int)len]; 226 | FileChannel fChan = file.getChannel(); //object to obtain offset current position within the file 227 | 228 | fChan.position(startOffset); 229 | file.read(hashData); 230 | 231 | try { 232 | 233 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 234 | this.infoHash = md.digest(hashData); 235 | 236 | } catch (NoSuchAlgorithmException e) { 237 | 238 | System.out.println("error:" + e); 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/TorrentElement.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.ArrayList; 3 | import javax.swing.Timer; 4 | 5 | /** 6 | * 7 | * This class contains all the torrent data such as torrent name, size, tracker 8 | * info etc. It also contains the elements such as the thread objects so more than 9 | * one torrent can be spoofed at the same time. 10 | * 11 | * @author karma 12 | * @version 1.0 13 | * 14 | */ 15 | public class TorrentElement { 16 | 17 | /** 18 | * int that represents the object index in an array 19 | */ 20 | private int index; 21 | 22 | /** 23 | * int to keep track of how much has been uploaded 24 | */ 25 | private long uploadAmount; 26 | 27 | /** 28 | * int to keep track of how much has been downloaded 29 | */ 30 | private long downloadAmount; 31 | 32 | /** 33 | * int to set the simulated speed of the upload 34 | */ 35 | private int uploadSpeed; 36 | 37 | /** 38 | * int to set the simulated speed of the downloaded 39 | */ 40 | private int downloadSpeed; 41 | 42 | /** 43 | * int to keep track of the number of seconds left to send the upload/download data 44 | */ 45 | private int timeLeft; 46 | 47 | /** 48 | * int that keeps track of how much spoofed upload data has been received by the tracker 49 | */ 50 | private long uploadSent; 51 | 52 | /** 53 | * String to keep track of what port the program will report its using to the tracker 54 | */ 55 | private String port; 56 | 57 | /** 58 | * String set a custom user agent 59 | */ 60 | private String customUserAgent; 61 | 62 | /** 63 | * object that represents a new thread to perform the upload 64 | */ 65 | private Timer timer; 66 | 67 | /** 68 | * object that contains all relevant torrent info 69 | */ 70 | private TorrentInfo tInfo; 71 | 72 | /** 73 | * object that represents the spoofed upload info to send 74 | */ 75 | private TrackerConnect tConn; 76 | 77 | /** 78 | * value that keeps track of how many announcement's have been made to the tracker 79 | */ 80 | private ArrayList numAnnouce; 81 | 82 | /** 83 | * A string array of error messages this torrent may or may not get. e.g unable to connect to tracker 84 | */ 85 | private ArrayList errorMsg; 86 | 87 | /** 88 | * Constructor that initialises all member variables to their default values 89 | */ 90 | public TorrentElement() { 91 | 92 | this.uploadAmount = 0; 93 | this.downloadAmount = 0; 94 | this.uploadSpeed = 40; 95 | this.downloadSpeed = 10; 96 | this.timeLeft = 0; 97 | this.uploadSent = 0; 98 | this.port = "6881"; 99 | this.timer = null; 100 | this.customUserAgent = null; 101 | this.numAnnouce = new ArrayList<>(); 102 | this.errorMsg = new ArrayList<>(); 103 | } 104 | 105 | /** 106 | * 107 | * @return A String representing a User Agent specified by the user 108 | */ 109 | public String getCustomUserAgent() { 110 | 111 | return customUserAgent; 112 | } 113 | 114 | /** 115 | * 116 | * @return The amount in bytes that has been sent to the torrent tracker 117 | */ 118 | public long getUploadSent() { 119 | 120 | return uploadSent; 121 | } 122 | 123 | /** 124 | * 125 | * @return A String that represents the port number set to the torrent tracker 126 | */ 127 | public String getPort() { 128 | 129 | return port; 130 | } 131 | 132 | /** 133 | * 134 | * @return A ArrayList of Strings that contain the announcements that have been sent to the torrent tracker 135 | */ 136 | public ArrayList getNumAnnouce() { 137 | 138 | return numAnnouce; 139 | } 140 | 141 | /** 142 | * 143 | * @return A ArrayList of Strings that contain error messages 144 | */ 145 | public ArrayList getErrorMsg() { 146 | 147 | return errorMsg; 148 | } 149 | 150 | /** 151 | * 152 | * @return A integer that represents the number of seconds left until the nest announcement is made 153 | */ 154 | public int getTimeLeft() { 155 | 156 | return timeLeft; 157 | } 158 | 159 | /** 160 | * 161 | * @return a integer that represents the upload speed in KiloBytes per second 162 | */ 163 | public int getUploadSpeed() { 164 | 165 | return uploadSpeed; 166 | } 167 | 168 | /** 169 | * 170 | * @return a integer that represents the download speed in KiloBytes per second 171 | */ 172 | public int getDownloadSpeed() { 173 | 174 | return downloadSpeed; 175 | } 176 | 177 | /** 178 | * 179 | * @return A TorrentInfo object that represents all the information held within the torrent file. 180 | */ 181 | public TorrentInfo gettInfo() { 182 | 183 | return tInfo; 184 | } 185 | 186 | /** 187 | * 188 | * @return A TrackerConnect object that represents all the info that the tracker sends back 189 | * after a request has been made. 190 | */ 191 | public TrackerConnect gettConn() { 192 | 193 | return tConn; 194 | } 195 | 196 | /** 197 | * 198 | * @return a Timer object that represents a thread that runs periodically. 199 | */ 200 | public Timer getTimer() { 201 | 202 | return timer; 203 | } 204 | 205 | /** 206 | * 207 | * @return A integer that represents the index that an instance of this object is in a ArrayList. 208 | */ 209 | public int getIndex() { 210 | 211 | return index; 212 | } 213 | 214 | /** 215 | * 216 | * @return A long integer that represents the amount of spoofed uploads in bytes. 217 | */ 218 | public long getUploadAmount() { 219 | 220 | return uploadAmount; 221 | } 222 | 223 | /** 224 | * 225 | * @return A long integer that represents the amount of spoofed downloads in bytes. 226 | */ 227 | public long getDownloadAmount() { 228 | 229 | return downloadAmount; 230 | } 231 | 232 | /** 233 | * This function sets the customUserAgent member variable. If an empty sting is given as a parameter 234 | * it will set the member variable to null as a precaution. 235 | * @param customUserAgent A String that represents the user agent string that is sent to the tracker 236 | */ 237 | public void setCustomUserAgent(String customUserAgent) { 238 | 239 | if (customUserAgent.isEmpty()) { 240 | 241 | this.customUserAgent = null; 242 | 243 | } else { 244 | 245 | this.customUserAgent = customUserAgent; 246 | } 247 | } 248 | 249 | /** 250 | * Sets the port number to be spoofed 251 | * @param port the port number as a string 252 | */ 253 | public void setPort(String port) { 254 | 255 | this.port = port; 256 | } 257 | 258 | /** 259 | * This function sets the amount of uploads that the tracker has received 260 | * @param uploadSent the amount of upload data sent in bytes 261 | */ 262 | public void setUploadSent(long uploadSent) { 263 | 264 | this.uploadSent = uploadSent; 265 | } 266 | 267 | /** 268 | * This function set the amount of time left until the next announcement is made 269 | * @param timeLeft time left until the next announcement in seconds 270 | */ 271 | public void setTimeLeft(int timeLeft) { 272 | 273 | this.timeLeft = timeLeft; 274 | } 275 | 276 | /** 277 | * This function sets the upload speed per-second in bytes 278 | * @param uploadSpeed upload speed in bytes 279 | */ 280 | public void setUploadSpeed(int uploadSpeed) { 281 | 282 | this.uploadSpeed = uploadSpeed; 283 | } 284 | 285 | /** 286 | * This function sets the download speed per-second in bytes 287 | * @param downloadSpeed 288 | */ 289 | public void setDownloadSpeed(int downloadSpeed) { 290 | 291 | this.downloadSpeed = downloadSpeed; 292 | } 293 | 294 | /** 295 | * This function sets the spoofed upload amount in bytes 296 | * @param uploadAmount spoofed upload amount in bytes 297 | */ 298 | public void setUploadAmount(long uploadAmount) { 299 | 300 | this.uploadAmount = uploadAmount; 301 | } 302 | 303 | /** 304 | * This function sets the spoofed download amount in bytes 305 | * @param downloadAmount spoofed download amount in bytes 306 | */ 307 | public void setDownloadAmount(long downloadAmount) { 308 | 309 | this.downloadAmount = downloadAmount; 310 | } 311 | 312 | /** 313 | * This function sets the index member variable that keeps track of where in an ArrayList 314 | * an instance of this object is. 315 | * @param index position in an ArrayList. 316 | */ 317 | public void setIndex(int index) { 318 | 319 | this.index = index; 320 | } 321 | 322 | /** 323 | * This function stores a TorrentInfo object in a member variable that contains all the data 324 | * from a torrent retrieved from a torrent file 325 | * @param tInfo torrent data from a torrent file 326 | */ 327 | public void settInfo(TorrentInfo tInfo) { 328 | 329 | this.tInfo = tInfo; 330 | } 331 | 332 | /** 333 | * This function stores TrackerConnect object in a member variable that contains the data 334 | * that is received from a tracker when a connection to the tracker is made. 335 | * @param uInfo data received from a connection to a tracker 336 | */ 337 | public void settConn(TrackerConnect uInfo) { 338 | 339 | this.tConn = uInfo; 340 | } 341 | 342 | /** 343 | * This function sets a Timer object as a member variable to represent a thread that executes 344 | * a task periodically. 345 | * @param timer 346 | */ 347 | public void setTimer(Timer timer) { 348 | 349 | this.timer = timer; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/Gui.java: -------------------------------------------------------------------------------- 1 | 2 | import java.awt.*; 3 | import java.awt.event.*; 4 | import java.io.*; 5 | import java.text.*; 6 | import java.util.*; 7 | import java.util.regex.*; 8 | import javax.swing.*; 9 | import javax.swing.Timer; 10 | import javax.swing.event.*; 11 | import javax.swing.filechooser.FileNameExtensionFilter; 12 | 13 | /** 14 | * This is the JRatioBoost GUI program that spoofs uploads on private torrent trackers 15 | * 16 | * @author Karma Chameleon 17 | * @version 2.1 18 | */ 19 | public class Gui extends javax.swing.JFrame { 20 | 21 | /** 22 | * Holds a reference to the currently selected torrent in the JList 23 | */ 24 | int indexSelected = 0; 25 | 26 | /** 27 | * Holds all the information of a torrent that has been opened by the program 28 | */ 29 | ArrayList torrentElement; 30 | 31 | /** 32 | * Holds the programs icon to be displayed in the JFrame 33 | */ 34 | ImageIcon img; 35 | 36 | /** 37 | * Creates a new JFrame that represents the GUI for the JRatioBoost program 38 | * @param args A string array of .torrent files to add to the program 39 | */ 40 | public Gui(String[] args) { 41 | 42 | img = new ImageIcon(getClass().getResource("icons/rocket-fly.png")); 43 | initComponents(); 44 | torrentList.setCellRenderer(new customRenderer()); 45 | torrentElement = new ArrayList<>(); 46 | jCircleProgress1.setVisible(false); 47 | // jCircleProgress1.setOutline(false); 48 | this.setIconImage(img.getImage()); 49 | this.pack(); 50 | loadArgs(args); 51 | } 52 | 53 | /** 54 | * This function is generated by the GUI builder in the NetBeans IDE. It sets up all the GUI components 55 | * used in this program 56 | */ 57 | // //GEN-BEGIN:initComponents 58 | private void initComponents() { 59 | java.awt.GridBagConstraints gridBagConstraints; 60 | 61 | jSeparator1 = new javax.swing.JSeparator(); 62 | jPopupMenu1 = new javax.swing.JPopupMenu(); 63 | changeTracker = new javax.swing.JMenu(); 64 | changeClientMenu = new javax.swing.JMenu(); 65 | jPopupMenu1.add(changeTracker); 66 | jPopupMenu1.add(changeClientMenu); 67 | updatePortMenu = new javax.swing.JMenuItem(); 68 | updateIntervalMenu = new javax.swing.JMenuItem(); 69 | jPopupMenu1.add(updateIntervalMenu); 70 | jPopupMenu1.add(updatePortMenu); 71 | jPopupMenu1.add(jSeparator1); 72 | aboutMenuItem = new javax.swing.JMenuItem(); 73 | jPopupMenu1.add(aboutMenuItem); 74 | kTorrentMenuItem = new javax.swing.JMenuItem(); 75 | transmissionMenuItem = new javax.swing.JMenuItem(); 76 | customClientMenu = new javax.swing.JMenuItem(); 77 | changeClientMenu.add(kTorrentMenuItem); 78 | changeClientMenu.add(transmissionMenuItem); 79 | changeClientMenu.add(customClientMenu); 80 | jSpinLoader1 = null; 81 | jFileChooser1 = new javax.swing.JFileChooser(); 82 | updateIntervalDialog = new javax.swing.JDialog(); 83 | jPanel7 = new javax.swing.JPanel(); 84 | jPanel5 = new javax.swing.JPanel(); 85 | jLabel10 = new javax.swing.JLabel(); 86 | changeUpdateSpinner = new javax.swing.JSpinner(); 87 | jPanel6 = new javax.swing.JPanel(); 88 | changeUpdateOkButton = new javax.swing.JButton(); 89 | changeUpdateCancelButton = new javax.swing.JButton(); 90 | changePortDialog = new javax.swing.JDialog(); 91 | jPanel8 = new javax.swing.JPanel(); 92 | jPanel9 = new javax.swing.JPanel(); 93 | jLabel11 = new javax.swing.JLabel(); 94 | changePortSpinner = new javax.swing.JSpinner(); 95 | jPanel10 = new javax.swing.JPanel(); 96 | changePortOkButton = new javax.swing.JButton(); 97 | jButton2 = new javax.swing.JButton(); 98 | customClientDialog = new javax.swing.JDialog(); 99 | jPanel13 = new javax.swing.JPanel(); 100 | jPanel12 = new javax.swing.JPanel(); 101 | jLabel12 = new javax.swing.JLabel(); 102 | clientComboBox = new javax.swing.JComboBox<>(); 103 | jLabel13 = new javax.swing.JLabel(); 104 | changeClientVersionSpinner = new javax.swing.JSpinner(); 105 | customUserAgentTextField = new javax.swing.JTextField(); 106 | customUserAgentCheckBox = new javax.swing.JCheckBox(); 107 | jPanel11 = new javax.swing.JPanel(); 108 | changeClientOkButton = new javax.swing.JButton(); 109 | changeClientCancelButton = new javax.swing.JButton(); 110 | msgDialog = new javax.swing.JDialog(this); 111 | jPanel14 = new javax.swing.JPanel(); 112 | msgLabel = new javax.swing.JLabel(); 113 | jScrollPane2 = new javax.swing.JScrollPane(); 114 | msgEditorPane = new javax.swing.JEditorPane(); 115 | jPanel15 = new javax.swing.JPanel(); 116 | msgOkButton = new javax.swing.JButton(); 117 | buttonGroup1 = new javax.swing.ButtonGroup(); 118 | WindowPanel = new javax.swing.JPanel(); 119 | WindowPanel.setComponentPopupMenu(jPopupMenu1); 120 | listPanel = new javax.swing.JPanel(); 121 | jPanel1 = new javax.swing.JPanel(); 122 | jScrollPane1 = new javax.swing.JScrollPane(); 123 | torrentList = new javax.swing.JList<>(); 124 | DefaultListModel demoList = new DefaultListModel(); 125 | torrentList.setModel(demoList); 126 | 127 | torrentList.addListSelectionListener(new ListSelectionListener() { 128 | @Override 129 | public void valueChanged(ListSelectionEvent e) { 130 | 131 | listListen(e); 132 | } 133 | }); 134 | toolBar = new javax.swing.JPanel(); 135 | jToolBar1 = new javax.swing.JToolBar(); 136 | errorsButton = new javax.swing.JButton(); 137 | uploadsSentButton = new javax.swing.JButton(); 138 | announceButton = new javax.swing.JButton(); 139 | jPanel2 = new javax.swing.JPanel(); 140 | TorrentDataPanel = new javax.swing.JPanel(); 141 | InfoPanel = new javax.swing.JPanel(); 142 | jLabel1 = new javax.swing.JLabel(); 143 | jLabel3 = new javax.swing.JLabel(); 144 | jLabel4 = new javax.swing.JLabel(); 145 | jLabel8 = new javax.swing.JLabel(); 146 | jLabel9 = new javax.swing.JLabel(); 147 | tracker = new javax.swing.JLabel(); 148 | info_hash = new javax.swing.JLabel(); 149 | peer_id = new javax.swing.JLabel(); 150 | size = new javax.swing.JLabel(); 151 | date = new javax.swing.JLabel(); 152 | OutputPanel = new javax.swing.JPanel(); 153 | jLabel2 = new javax.swing.JLabel(); 154 | jLabel15 = new javax.swing.JLabel(); 155 | jLabel16 = new javax.swing.JLabel(); 156 | jLabel17 = new javax.swing.JLabel(); 157 | jLabel18 = new javax.swing.JLabel(); 158 | seeders = new javax.swing.JLabel(); 159 | leechers = new javax.swing.JLabel(); 160 | downloaded = new javax.swing.JLabel(); 161 | uploaded = new javax.swing.JLabel(); 162 | update = new javax.swing.JLabel(); 163 | jCircleProgress1 = new JCircleProgress(); 164 | jSeparator2 = new javax.swing.JSeparator(); 165 | ConnectPanel = new javax.swing.JPanel(); 166 | uploadSpeedSpinner = new javax.swing.JSpinner(); 167 | connectButton = new javax.swing.JButton(); 168 | jSpinLoader1 = new JSpinLoader(25, 25, connectButton); 169 | connectButton.setIcon(new ImageIcon(jSpinLoader1.createSpinnerImage(25, 25, 0f))); 170 | //connectButton.setHorizontalTextPosition(textPosition); 171 | jArrow1 = new JArrow(); 172 | jLabel6 = new javax.swing.JLabel(); 173 | openPanel = new javax.swing.JPanel(); 174 | torrent_name = new javax.swing.JLabel(); 175 | openFileButton = new javax.swing.JButton(); 176 | 177 | jPopupMenu1.setFont(new java.awt.Font("Noto Sans Regular", 0, 17)); // NOI18N 178 | jPopupMenu1.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { 179 | public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { 180 | } 181 | public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) { 182 | } 183 | public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) { 184 | jPopupMenu1PopupMenuWillBecomeVisible(evt); 185 | } 186 | }); 187 | 188 | changeTracker.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/globe-green.png"))); // NOI18N 189 | changeTracker.setText("Tracker"); 190 | changeTracker.setEnabled(false); 191 | changeTracker.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 192 | 193 | changeClientMenu.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/application--plus.png"))); // NOI18N 194 | changeClientMenu.setText("Client"); 195 | changeClientMenu.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 196 | 197 | updatePortMenu.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 198 | updatePortMenu.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/network-ethernet.png"))); // NOI18N 199 | updatePortMenu.setText("Update Port"); 200 | updatePortMenu.addActionListener(new java.awt.event.ActionListener() { 201 | public void actionPerformed(java.awt.event.ActionEvent evt) { 202 | updatePortMenuActionPerformed(evt); 203 | } 204 | }); 205 | 206 | updateIntervalMenu.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 207 | updateIntervalMenu.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/hourglass.png"))); // NOI18N 208 | updateIntervalMenu.setText("Update Interval"); 209 | updateIntervalMenu.addActionListener(new java.awt.event.ActionListener() { 210 | public void actionPerformed(java.awt.event.ActionEvent evt) { 211 | updateIntervalMenuActionPerformed(evt); 212 | } 213 | }); 214 | 215 | aboutMenuItem.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 216 | aboutMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/user.png"))); // NOI18N 217 | aboutMenuItem.setText("About"); 218 | aboutMenuItem.addActionListener(new java.awt.event.ActionListener() { 219 | public void actionPerformed(java.awt.event.ActionEvent evt) { 220 | aboutMenuItemActionPerformed(evt); 221 | } 222 | }); 223 | 224 | kTorrentMenuItem.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 225 | kTorrentMenuItem.setText("KTorrent 5.11"); 226 | kTorrentMenuItem.addActionListener(new java.awt.event.ActionListener() { 227 | public void actionPerformed(java.awt.event.ActionEvent evt) { 228 | kTorrentMenuItemActionPerformed(evt); 229 | } 230 | }); 231 | 232 | transmissionMenuItem.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 233 | transmissionMenuItem.setText("Transmission 4.05"); 234 | transmissionMenuItem.addActionListener(new java.awt.event.ActionListener() { 235 | public void actionPerformed(java.awt.event.ActionEvent evt) { 236 | transmissionMenuItemActionPerformed(evt); 237 | } 238 | }); 239 | 240 | customClientMenu.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 241 | customClientMenu.setText("Custom Client"); 242 | customClientMenu.addActionListener(new java.awt.event.ActionListener() { 243 | public void actionPerformed(java.awt.event.ActionEvent evt) { 244 | customClientMenuActionPerformed(evt); 245 | } 246 | }); 247 | 248 | jFileChooser1.setDialogTitle("Open Torrents"); 249 | jFileChooser1.setFileFilter(null); 250 | jFileChooser1.setFont(new java.awt.Font("Noto Sans", 0, 18)); // NOI18N 251 | jFileChooser1.setPreferredSize(new java.awt.Dimension(700, 500)); 252 | 253 | updateIntervalDialog.setTitle("Change Update Interval"); 254 | updateIntervalDialog.setIconImage(this.img.getImage()); 255 | 256 | jPanel7.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); 257 | jPanel7.setLayout(new java.awt.BorderLayout()); 258 | 259 | jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Change Update Interval", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 1, 17))); // NOI18N 260 | jPanel5.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 15)); 261 | 262 | jLabel10.setFont(jLabel10.getFont().deriveFont(jLabel10.getFont().getSize()+4f)); 263 | jLabel10.setText("Update Interval"); 264 | jPanel5.add(jLabel10); 265 | 266 | changeUpdateSpinner.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 267 | changeUpdateSpinner.setModel(new javax.swing.SpinnerNumberModel(100, 1, 5000, 1)); 268 | jPanel5.add(changeUpdateSpinner); 269 | 270 | jPanel7.add(jPanel5, java.awt.BorderLayout.CENTER); 271 | 272 | jPanel6.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 273 | jPanel6.setInheritsPopupMenu(true); 274 | jPanel6.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); 275 | 276 | changeUpdateOkButton.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 277 | changeUpdateOkButton.setText("OK"); 278 | changeUpdateOkButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 279 | changeUpdateOkButton.addActionListener(new java.awt.event.ActionListener() { 280 | public void actionPerformed(java.awt.event.ActionEvent evt) { 281 | changeUpdateOkButtonActionPerformed(evt); 282 | } 283 | }); 284 | jPanel6.add(changeUpdateOkButton); 285 | 286 | changeUpdateCancelButton.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 287 | changeUpdateCancelButton.setText("Cancel"); 288 | changeUpdateCancelButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 289 | changeUpdateCancelButton.addActionListener(new java.awt.event.ActionListener() { 290 | public void actionPerformed(java.awt.event.ActionEvent evt) { 291 | changeUpdateCancelButtonActionPerformed(evt); 292 | } 293 | }); 294 | jPanel6.add(changeUpdateCancelButton); 295 | 296 | jPanel7.add(jPanel6, java.awt.BorderLayout.SOUTH); 297 | 298 | updateIntervalDialog.getContentPane().add(jPanel7, java.awt.BorderLayout.CENTER); 299 | 300 | changePortDialog.setTitle("Change Port"); 301 | changePortDialog.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 302 | changePortDialog.setIconImage(this.img.getImage()); 303 | 304 | jPanel8.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); 305 | jPanel8.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 306 | jPanel8.setPreferredSize(new java.awt.Dimension(350, 200)); 307 | jPanel8.setLayout(new java.awt.BorderLayout()); 308 | 309 | jPanel9.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Change Port", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 1, 17))); // NOI18N 310 | jPanel9.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 311 | 312 | jLabel11.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 313 | jLabel11.setText("Port Number"); 314 | jPanel9.add(jLabel11); 315 | 316 | changePortSpinner.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 317 | changePortSpinner.setModel(new javax.swing.SpinnerNumberModel(6881, 0, 9999, 1)); 318 | changePortSpinner.setEditor(new javax.swing.JSpinner.NumberEditor(changePortSpinner, "#")); 319 | jPanel9.add(changePortSpinner); 320 | 321 | jPanel8.add(jPanel9, java.awt.BorderLayout.CENTER); 322 | 323 | jPanel10.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 324 | jPanel10.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); 325 | 326 | changePortOkButton.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 327 | changePortOkButton.setText("OK"); 328 | changePortOkButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 329 | changePortOkButton.addActionListener(new java.awt.event.ActionListener() { 330 | public void actionPerformed(java.awt.event.ActionEvent evt) { 331 | changePortOkButtonActionPerformed(evt); 332 | } 333 | }); 334 | jPanel10.add(changePortOkButton); 335 | 336 | jButton2.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 337 | jButton2.setText("Cancel"); 338 | jButton2.setMargin(new java.awt.Insets(5, 15, 5, 15)); 339 | jButton2.addActionListener(new java.awt.event.ActionListener() { 340 | public void actionPerformed(java.awt.event.ActionEvent evt) { 341 | jButton2ActionPerformed(evt); 342 | } 343 | }); 344 | jPanel10.add(jButton2); 345 | 346 | jPanel8.add(jPanel10, java.awt.BorderLayout.SOUTH); 347 | 348 | changePortDialog.getContentPane().add(jPanel8, java.awt.BorderLayout.CENTER); 349 | 350 | customClientDialog.setTitle("Custom Client"); 351 | customClientDialog.setIconImage(this.img.getImage()); 352 | 353 | jPanel13.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); 354 | jPanel13.setLayout(new java.awt.BorderLayout()); 355 | 356 | jPanel12.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Change Client", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 1, 16))); // NOI18N 357 | jPanel12.setLayout(new java.awt.GridBagLayout()); 358 | 359 | jLabel12.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 360 | jLabel12.setText("Client"); 361 | jLabel12.setToolTipText(""); 362 | gridBagConstraints = new java.awt.GridBagConstraints(); 363 | gridBagConstraints.gridx = 0; 364 | gridBagConstraints.gridy = 0; 365 | gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; 366 | jPanel12.add(jLabel12, gridBagConstraints); 367 | 368 | clientComboBox.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 369 | clientComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "KTorrent", "Transmission", "qBitTorrent", "µTorrent" })); 370 | gridBagConstraints = new java.awt.GridBagConstraints(); 371 | gridBagConstraints.gridx = 1; 372 | gridBagConstraints.gridy = 0; 373 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 374 | gridBagConstraints.insets = new java.awt.Insets(10, 10, 10, 0); 375 | jPanel12.add(clientComboBox, gridBagConstraints); 376 | 377 | jLabel13.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 378 | jLabel13.setText("Version"); 379 | gridBagConstraints = new java.awt.GridBagConstraints(); 380 | gridBagConstraints.gridx = 0; 381 | gridBagConstraints.gridy = 1; 382 | gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; 383 | jPanel12.add(jLabel13, gridBagConstraints); 384 | 385 | changeClientVersionSpinner.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 386 | changeClientVersionSpinner.setModel(new javax.swing.SpinnerNumberModel(1000, 0, 9999, 1)); 387 | changeClientVersionSpinner.setEditor(new javax.swing.JSpinner.NumberEditor(changeClientVersionSpinner, "#")); 388 | gridBagConstraints = new java.awt.GridBagConstraints(); 389 | gridBagConstraints.gridx = 1; 390 | gridBagConstraints.gridy = 1; 391 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 392 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 10, 0); 393 | jPanel12.add(changeClientVersionSpinner, gridBagConstraints); 394 | 395 | customUserAgentTextField.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 396 | customUserAgentTextField.setText("(Auto)"); 397 | customUserAgentTextField.setEnabled(false); 398 | gridBagConstraints = new java.awt.GridBagConstraints(); 399 | gridBagConstraints.gridx = 0; 400 | gridBagConstraints.gridy = 3; 401 | gridBagConstraints.gridwidth = 2; 402 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 403 | gridBagConstraints.insets = new java.awt.Insets(0, 0, 10, 0); 404 | jPanel12.add(customUserAgentTextField, gridBagConstraints); 405 | 406 | customUserAgentCheckBox.setFont(new java.awt.Font("Noto Sans", 0, 16)); // NOI18N 407 | customUserAgentCheckBox.setText("Use Custom User Agent"); 408 | customUserAgentCheckBox.setToolTipText("Enter in your own Custom User Agent or leave unchecked to have one auto generated based off the version number entered"); 409 | customUserAgentCheckBox.addActionListener(new java.awt.event.ActionListener() { 410 | public void actionPerformed(java.awt.event.ActionEvent evt) { 411 | customUserAgentCheckBoxActionPerformed(evt); 412 | } 413 | }); 414 | gridBagConstraints = new java.awt.GridBagConstraints(); 415 | gridBagConstraints.gridx = 0; 416 | gridBagConstraints.gridy = 2; 417 | gridBagConstraints.gridwidth = 2; 418 | gridBagConstraints.insets = new java.awt.Insets(0, 0, 10, 0); 419 | jPanel12.add(customUserAgentCheckBox, gridBagConstraints); 420 | 421 | jPanel13.add(jPanel12, java.awt.BorderLayout.CENTER); 422 | 423 | jPanel11.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 424 | jPanel11.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); 425 | 426 | changeClientOkButton.setText("OK"); 427 | changeClientOkButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); 428 | changeClientOkButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 429 | changeClientOkButton.addActionListener(new java.awt.event.ActionListener() { 430 | public void actionPerformed(java.awt.event.ActionEvent evt) { 431 | changeClientOkButtonActionPerformed(evt); 432 | } 433 | }); 434 | jPanel11.add(changeClientOkButton); 435 | 436 | changeClientCancelButton.setText("Cancel"); 437 | changeClientCancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); 438 | changeClientCancelButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 439 | changeClientCancelButton.addActionListener(new java.awt.event.ActionListener() { 440 | public void actionPerformed(java.awt.event.ActionEvent evt) { 441 | changeClientCancelButtonActionPerformed(evt); 442 | } 443 | }); 444 | jPanel11.add(changeClientCancelButton); 445 | 446 | jPanel13.add(jPanel11, java.awt.BorderLayout.SOUTH); 447 | 448 | customClientDialog.getContentPane().add(jPanel13, java.awt.BorderLayout.CENTER); 449 | 450 | msgDialog.setIconImage(this.img.getImage()); 451 | msgDialog.setPreferredSize(new java.awt.Dimension(700, 400)); 452 | msgDialog.addComponentListener(new java.awt.event.ComponentAdapter() { 453 | public void componentShown(java.awt.event.ComponentEvent evt) { 454 | msgDialogComponentShown(evt); 455 | } 456 | }); 457 | 458 | jPanel14.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); 459 | jPanel14.setLayout(new java.awt.BorderLayout()); 460 | 461 | msgLabel.setFont(new java.awt.Font("Noto Sans", 0, 18)); // NOI18N 462 | msgLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); 463 | msgLabel.setText("Errors"); 464 | msgLabel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 10, 1)); 465 | msgLabel.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 466 | msgLabel.setIconTextGap(10); 467 | jPanel14.add(msgLabel, java.awt.BorderLayout.NORTH); 468 | 469 | msgEditorPane.setEditable(false); 470 | msgEditorPane.setContentType("text/html"); // NOI18N 471 | msgEditorPane.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 472 | msgEditorPane.setText(""); 473 | jScrollPane2.setViewportView(msgEditorPane); 474 | 475 | jPanel14.add(jScrollPane2, java.awt.BorderLayout.CENTER); 476 | 477 | msgOkButton.setText("OK"); 478 | msgOkButton.setMargin(new java.awt.Insets(5, 15, 5, 15)); 479 | msgOkButton.addActionListener(new java.awt.event.ActionListener() { 480 | public void actionPerformed(java.awt.event.ActionEvent evt) { 481 | msgOkButtonActionPerformed(evt); 482 | } 483 | }); 484 | jPanel15.add(msgOkButton); 485 | 486 | jPanel14.add(jPanel15, java.awt.BorderLayout.SOUTH); 487 | 488 | msgDialog.getContentPane().add(jPanel14, java.awt.BorderLayout.CENTER); 489 | 490 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 491 | setTitle("JRatioBoost"); 492 | setName("JRatioBoost"); // NOI18N 493 | setSize(new java.awt.Dimension(788, 600)); 494 | 495 | WindowPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); 496 | WindowPanel.addMouseListener(new java.awt.event.MouseAdapter() { 497 | public void mousePressed(java.awt.event.MouseEvent evt) { 498 | windowFocus(evt); 499 | } 500 | }); 501 | WindowPanel.setLayout(new java.awt.BorderLayout()); 502 | 503 | listPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 0, 0, 0)); 504 | listPanel.setOpaque(false); 505 | listPanel.setPreferredSize(new java.awt.Dimension(250, 159)); 506 | listPanel.setLayout(new java.awt.BorderLayout()); 507 | 508 | jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Torrent List", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 0, 17))); // NOI18N 509 | jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.LINE_AXIS)); 510 | 511 | jScrollPane1.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); 512 | 513 | torrentList.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 514 | torrentList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); 515 | jScrollPane1.setViewportView(torrentList); 516 | 517 | jPanel1.add(jScrollPane1); 518 | 519 | listPanel.add(jPanel1, java.awt.BorderLayout.CENTER); 520 | 521 | jToolBar1.setFloatable(false); 522 | jToolBar1.setRollover(true); 523 | jToolBar1.setBorderPainted(false); 524 | jToolBar1.setMargin(new java.awt.Insets(5, 0, 0, 0)); 525 | jToolBar1.setName("infoToolBar"); // NOI18N 526 | 527 | errorsButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/exclamation.png"))); // NOI18N 528 | errorsButton.setText("0"); 529 | errorsButton.setToolTipText("Error messages"); 530 | errorsButton.setFocusable(false); 531 | errorsButton.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 532 | errorsButton.setIconTextGap(5); 533 | errorsButton.setMargin(new java.awt.Insets(5, 5, 5, 5)); 534 | errorsButton.addActionListener(new java.awt.event.ActionListener() { 535 | public void actionPerformed(java.awt.event.ActionEvent evt) { 536 | errorsButtonActionPerformed(evt); 537 | } 538 | }); 539 | jToolBar1.add(errorsButton); 540 | 541 | uploadsSentButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/counter.png"))); // NOI18N 542 | uploadsSentButton.setText("0"); 543 | uploadsSentButton.setToolTipText("Upload amount sent to the tracker"); 544 | uploadsSentButton.setFocusable(false); 545 | uploadsSentButton.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 546 | uploadsSentButton.setIconTextGap(5); 547 | uploadsSentButton.setMargin(new java.awt.Insets(5, 5, 5, 5)); 548 | uploadsSentButton.addActionListener(new java.awt.event.ActionListener() { 549 | public void actionPerformed(java.awt.event.ActionEvent evt) { 550 | uploadsSentButtonActionPerformed(evt); 551 | } 552 | }); 553 | jToolBar1.add(uploadsSentButton); 554 | 555 | announceButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/megaphone.png"))); // NOI18N 556 | announceButton.setText("0"); 557 | announceButton.setToolTipText("Announcements recieved by the tracker"); 558 | announceButton.setFocusable(false); 559 | announceButton.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 560 | announceButton.setIconTextGap(5); 561 | announceButton.setMargin(new java.awt.Insets(5, 5, 5, 5)); 562 | announceButton.addActionListener(new java.awt.event.ActionListener() { 563 | public void actionPerformed(java.awt.event.ActionEvent evt) { 564 | announceButtonActionPerformed(evt); 565 | } 566 | }); 567 | jToolBar1.add(announceButton); 568 | 569 | toolBar.add(jToolBar1); 570 | 571 | listPanel.add(toolBar, java.awt.BorderLayout.SOUTH); 572 | 573 | WindowPanel.add(listPanel, java.awt.BorderLayout.WEST); 574 | 575 | jPanel2.setLayout(new java.awt.BorderLayout()); 576 | 577 | TorrentDataPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 10, 0, 0)); 578 | TorrentDataPanel.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 579 | TorrentDataPanel.setInheritsPopupMenu(true); 580 | TorrentDataPanel.setLayout(new javax.swing.BoxLayout(TorrentDataPanel, javax.swing.BoxLayout.Y_AXIS)); 581 | 582 | InfoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Torrent Info", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 0, 17))); // NOI18N 583 | InfoPanel.setLayout(new java.awt.GridBagLayout()); 584 | 585 | jLabel1.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 586 | jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 587 | jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/globe-green.png"))); // NOI18N 588 | jLabel1.setText("Tracker"); 589 | jLabel1.setAlignmentX(0.5F); 590 | jLabel1.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 591 | gridBagConstraints = new java.awt.GridBagConstraints(); 592 | gridBagConstraints.gridx = 0; 593 | gridBagConstraints.gridy = 0; 594 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 595 | gridBagConstraints.insets = new java.awt.Insets(10, 5, 5, 0); 596 | InfoPanel.add(jLabel1, gridBagConstraints); 597 | 598 | jLabel3.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 599 | jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 600 | jLabel3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/edit-number.png"))); // NOI18N 601 | jLabel3.setText("Info_Hash"); 602 | jLabel3.setAlignmentX(0.5F); 603 | jLabel3.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 604 | gridBagConstraints = new java.awt.GridBagConstraints(); 605 | gridBagConstraints.gridx = 0; 606 | gridBagConstraints.gridy = 1; 607 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 608 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 609 | InfoPanel.add(jLabel3, gridBagConstraints); 610 | 611 | jLabel4.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 612 | jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 613 | jLabel4.setIcon(new javax.swing.ImageIcon("/home/karma/Code/java/j-ratio-boost/src/icons/edit-number.png")); // NOI18N 614 | jLabel4.setText("Peer_ID"); 615 | jLabel4.setAlignmentX(0.5F); 616 | jLabel4.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 617 | gridBagConstraints = new java.awt.GridBagConstraints(); 618 | gridBagConstraints.gridx = 0; 619 | gridBagConstraints.gridy = 2; 620 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 621 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 622 | InfoPanel.add(jLabel4, gridBagConstraints); 623 | 624 | jLabel8.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 625 | jLabel8.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 626 | jLabel8.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/chart-pie-separate.png"))); // NOI18N 627 | jLabel8.setText("Size"); 628 | jLabel8.setAlignmentX(0.5F); 629 | jLabel8.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 630 | gridBagConstraints = new java.awt.GridBagConstraints(); 631 | gridBagConstraints.gridx = 0; 632 | gridBagConstraints.gridy = 3; 633 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 634 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 635 | InfoPanel.add(jLabel8, gridBagConstraints); 636 | 637 | jLabel9.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 638 | jLabel9.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 639 | jLabel9.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/calendar-day.png"))); // NOI18N 640 | jLabel9.setText("Date"); 641 | jLabel9.setVerticalAlignment(javax.swing.SwingConstants.TOP); 642 | jLabel9.setAlignmentY(0.0F); 643 | jLabel9.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 644 | gridBagConstraints = new java.awt.GridBagConstraints(); 645 | gridBagConstraints.gridx = 0; 646 | gridBagConstraints.gridy = 4; 647 | gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 648 | gridBagConstraints.weighty = 1.0; 649 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 650 | InfoPanel.add(jLabel9, gridBagConstraints); 651 | 652 | tracker.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 653 | tracker.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 654 | gridBagConstraints = new java.awt.GridBagConstraints(); 655 | gridBagConstraints.gridx = 1; 656 | gridBagConstraints.gridy = 0; 657 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 658 | gridBagConstraints.weightx = 1.0; 659 | gridBagConstraints.insets = new java.awt.Insets(10, 10, 5, 5); 660 | InfoPanel.add(tracker, gridBagConstraints); 661 | 662 | info_hash.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 663 | info_hash.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 664 | gridBagConstraints = new java.awt.GridBagConstraints(); 665 | gridBagConstraints.gridx = 1; 666 | gridBagConstraints.gridy = 1; 667 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 668 | gridBagConstraints.weightx = 1.0; 669 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 670 | InfoPanel.add(info_hash, gridBagConstraints); 671 | 672 | peer_id.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 673 | peer_id.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 674 | gridBagConstraints = new java.awt.GridBagConstraints(); 675 | gridBagConstraints.gridx = 1; 676 | gridBagConstraints.gridy = 2; 677 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 678 | gridBagConstraints.weightx = 1.0; 679 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 680 | InfoPanel.add(peer_id, gridBagConstraints); 681 | 682 | size.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 683 | size.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 684 | gridBagConstraints = new java.awt.GridBagConstraints(); 685 | gridBagConstraints.gridx = 1; 686 | gridBagConstraints.gridy = 3; 687 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 688 | gridBagConstraints.weightx = 1.0; 689 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 690 | InfoPanel.add(size, gridBagConstraints); 691 | 692 | date.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 693 | date.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 694 | gridBagConstraints = new java.awt.GridBagConstraints(); 695 | gridBagConstraints.gridx = 1; 696 | gridBagConstraints.gridy = 4; 697 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 698 | gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; 699 | gridBagConstraints.weightx = 1.0; 700 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 701 | InfoPanel.add(date, gridBagConstraints); 702 | 703 | TorrentDataPanel.add(InfoPanel); 704 | 705 | OutputPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Tracker Output", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.TOP, new java.awt.Font("Noto Sans", 0, 17))); // NOI18N 706 | OutputPanel.setLayout(new java.awt.GridBagLayout()); 707 | 708 | jLabel2.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 709 | jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 710 | jLabel2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/arrow-branch-090.png"))); // NOI18N 711 | jLabel2.setText("Seeders"); 712 | jLabel2.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 713 | gridBagConstraints = new java.awt.GridBagConstraints(); 714 | gridBagConstraints.gridx = 0; 715 | gridBagConstraints.gridy = 0; 716 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 717 | gridBagConstraints.insets = new java.awt.Insets(10, 5, 5, 0); 718 | OutputPanel.add(jLabel2, gridBagConstraints); 719 | 720 | jLabel15.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 721 | jLabel15.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 722 | jLabel15.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/arrow-branch-270.png"))); // NOI18N 723 | jLabel15.setText("Leechers"); 724 | jLabel15.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 725 | gridBagConstraints = new java.awt.GridBagConstraints(); 726 | gridBagConstraints.gridx = 0; 727 | gridBagConstraints.gridy = 1; 728 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 729 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 730 | OutputPanel.add(jLabel15, gridBagConstraints); 731 | 732 | jLabel16.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 733 | jLabel16.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 734 | jLabel16.setIcon(new javax.swing.ImageIcon("/home/karma/Code/java/j-ratio-boost/src/icons/arrow-270.png")); // NOI18N 735 | jLabel16.setText("Downloads"); 736 | jLabel16.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 737 | gridBagConstraints = new java.awt.GridBagConstraints(); 738 | gridBagConstraints.gridx = 0; 739 | gridBagConstraints.gridy = 2; 740 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 741 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 742 | OutputPanel.add(jLabel16, gridBagConstraints); 743 | 744 | jLabel17.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 745 | jLabel17.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 746 | jLabel17.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/arrow-090.png"))); // NOI18N 747 | jLabel17.setText("Uploads"); 748 | jLabel17.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 749 | gridBagConstraints = new java.awt.GridBagConstraints(); 750 | gridBagConstraints.gridx = 0; 751 | gridBagConstraints.gridy = 3; 752 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 753 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 754 | OutputPanel.add(jLabel17, gridBagConstraints); 755 | 756 | jLabel18.setFont(new java.awt.Font("Noto Sans", 1, 15)); // NOI18N 757 | jLabel18.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); 758 | jLabel18.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/hourglass.png"))); // NOI18N 759 | jLabel18.setText("Update Interval"); 760 | jLabel18.setVerticalAlignment(javax.swing.SwingConstants.TOP); 761 | jLabel18.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 762 | gridBagConstraints = new java.awt.GridBagConstraints(); 763 | gridBagConstraints.gridx = 0; 764 | gridBagConstraints.gridy = 4; 765 | gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 766 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 0); 767 | OutputPanel.add(jLabel18, gridBagConstraints); 768 | 769 | seeders.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 770 | seeders.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 771 | seeders.setIconTextGap(5); 772 | gridBagConstraints = new java.awt.GridBagConstraints(); 773 | gridBagConstraints.gridx = 1; 774 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 775 | gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 776 | gridBagConstraints.weightx = 1.0; 777 | gridBagConstraints.insets = new java.awt.Insets(10, 10, 5, 5); 778 | OutputPanel.add(seeders, gridBagConstraints); 779 | 780 | leechers.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 781 | leechers.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 782 | leechers.setIconTextGap(5); 783 | gridBagConstraints = new java.awt.GridBagConstraints(); 784 | gridBagConstraints.gridx = 1; 785 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 786 | gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 787 | gridBagConstraints.weightx = 1.0; 788 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 789 | OutputPanel.add(leechers, gridBagConstraints); 790 | 791 | downloaded.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 792 | downloaded.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 793 | downloaded.setIconTextGap(5); 794 | gridBagConstraints = new java.awt.GridBagConstraints(); 795 | gridBagConstraints.gridx = 1; 796 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 797 | gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 798 | gridBagConstraints.weightx = 1.0; 799 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 800 | OutputPanel.add(downloaded, gridBagConstraints); 801 | 802 | uploaded.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 803 | uploaded.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 804 | uploaded.setIconTextGap(5); 805 | gridBagConstraints = new java.awt.GridBagConstraints(); 806 | gridBagConstraints.gridx = 1; 807 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 808 | gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 809 | gridBagConstraints.weightx = 1.0; 810 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 811 | OutputPanel.add(uploaded, gridBagConstraints); 812 | 813 | update.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 814 | update.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); 815 | update.setVerticalAlignment(javax.swing.SwingConstants.TOP); 816 | update.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 817 | update.setIconTextGap(5); 818 | gridBagConstraints = new java.awt.GridBagConstraints(); 819 | gridBagConstraints.gridx = 1; 820 | gridBagConstraints.gridy = 4; 821 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 822 | gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 823 | gridBagConstraints.weightx = 1.0; 824 | gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); 825 | OutputPanel.add(update, gridBagConstraints); 826 | 827 | jCircleProgress1.setColour(new java.awt.Color(255, 153, 0)); 828 | jCircleProgress1.setString(""); 829 | gridBagConstraints = new java.awt.GridBagConstraints(); 830 | gridBagConstraints.gridx = 2; 831 | gridBagConstraints.gridy = 0; 832 | gridBagConstraints.gridheight = 5; 833 | gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 834 | gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; 835 | gridBagConstraints.weightx = 20.0; 836 | OutputPanel.add(jCircleProgress1, gridBagConstraints); 837 | 838 | jSeparator2.setEnabled(false); 839 | gridBagConstraints = new java.awt.GridBagConstraints(); 840 | gridBagConstraints.gridx = 0; 841 | gridBagConstraints.gridy = 5; 842 | gridBagConstraints.gridwidth = 3; 843 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 844 | gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; 845 | gridBagConstraints.weighty = 1.0; 846 | OutputPanel.add(jSeparator2, gridBagConstraints); 847 | 848 | TorrentDataPanel.add(OutputPanel); 849 | 850 | jPanel2.add(TorrentDataPanel, java.awt.BorderLayout.CENTER); 851 | 852 | ConnectPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 0, 0, 0)); 853 | ConnectPanel.setPreferredSize(new java.awt.Dimension(500, 50)); 854 | ConnectPanel.setLayout(new java.awt.GridBagLayout()); 855 | 856 | uploadSpeedSpinner.setFont(new java.awt.Font("Noto Sans Regular", 0, 17)); // NOI18N 857 | uploadSpeedSpinner.setModel(new javax.swing.SpinnerNumberModel(40, 0, 9999, 1)); 858 | uploadSpeedSpinner.addChangeListener(new javax.swing.event.ChangeListener() { 859 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 860 | jSpinnerStateChanged(evt); 861 | } 862 | }); 863 | gridBagConstraints = new java.awt.GridBagConstraints(); 864 | gridBagConstraints.gridx = 2; 865 | gridBagConstraints.gridy = 0; 866 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 867 | gridBagConstraints.weightx = 0.5; 868 | ConnectPanel.add(uploadSpeedSpinner, gridBagConstraints); 869 | 870 | connectButton.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 871 | connectButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/plug-connect.png"))); // NOI18N 872 | connectButton.setText("Connect"); 873 | connectButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 874 | connectButton.setIconTextGap(5); 875 | connectButton.setPreferredSize(new java.awt.Dimension(150, 32)); 876 | connectButton.addActionListener(new java.awt.event.ActionListener() { 877 | public void actionPerformed(java.awt.event.ActionEvent evt) { 878 | connectButtonActionPerformed(evt); 879 | } 880 | }); 881 | gridBagConstraints = new java.awt.GridBagConstraints(); 882 | gridBagConstraints.gridx = 3; 883 | gridBagConstraints.gridy = 0; 884 | gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 885 | gridBagConstraints.weightx = 1.0; 886 | gridBagConstraints.weighty = 1.0; 887 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); 888 | ConnectPanel.add(connectButton, gridBagConstraints); 889 | 890 | jArrow1.setMinimumSize(new java.awt.Dimension(20, 20)); 891 | jArrow1.setPreferredSize(new java.awt.Dimension(20, 20)); 892 | gridBagConstraints = new java.awt.GridBagConstraints(); 893 | gridBagConstraints.gridx = 0; 894 | gridBagConstraints.gridy = 0; 895 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5); 896 | ConnectPanel.add(jArrow1, gridBagConstraints); 897 | 898 | jLabel6.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 899 | jLabel6.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); 900 | jLabel6.setText("Upload (kb/s)"); 901 | jLabel6.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); 902 | gridBagConstraints = new java.awt.GridBagConstraints(); 903 | gridBagConstraints.gridx = 1; 904 | gridBagConstraints.gridy = 0; 905 | gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 906 | gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5); 907 | ConnectPanel.add(jLabel6, gridBagConstraints); 908 | 909 | jPanel2.add(ConnectPanel, java.awt.BorderLayout.SOUTH); 910 | 911 | WindowPanel.add(jPanel2, java.awt.BorderLayout.CENTER); 912 | 913 | openPanel.setLayout(new java.awt.BorderLayout()); 914 | 915 | torrent_name.setFont(torrent_name.getFont().deriveFont(torrent_name.getFont().getSize()+10f)); 916 | torrent_name.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 10, 0, 0)); 917 | torrent_name.setPreferredSize(new java.awt.Dimension(300, 0)); 918 | openPanel.add(torrent_name, java.awt.BorderLayout.CENTER); 919 | 920 | openFileButton.setFont(new java.awt.Font("Noto Sans", 0, 14)); // NOI18N 921 | openFileButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/folder-horizontal-open.png"))); // NOI18N 922 | openFileButton.setText("Open Torrent(s)"); 923 | openFileButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); 924 | openFileButton.setIconTextGap(5); 925 | openFileButton.setPreferredSize(new java.awt.Dimension(250, 40)); 926 | openFileButton.addActionListener(new java.awt.event.ActionListener() { 927 | public void actionPerformed(java.awt.event.ActionEvent evt) { 928 | openFileButtonActionPerformed(evt); 929 | } 930 | }); 931 | openPanel.add(openFileButton, java.awt.BorderLayout.WEST); 932 | 933 | WindowPanel.add(openPanel, java.awt.BorderLayout.NORTH); 934 | 935 | getContentPane().add(WindowPanel, java.awt.BorderLayout.CENTER); 936 | 937 | pack(); 938 | }// //GEN-END:initComponents 939 | 940 | /** 941 | * Function that takes a array of strings that represents .torrent files and coverts them 942 | * into an absolute file path to be opened either from command line arguments or from 943 | * a open file dialog. 944 | * 945 | * @param a An array of strings of .torrent file names 946 | */ 947 | private void loadArgs(String[] a) { 948 | 949 | File[] f = new File[a.length]; 950 | 951 | for (int i = 0; i < a.length; i++) { 952 | 953 | f[i] = new File(a[i]); 954 | String s = f[i].getAbsolutePath(); 955 | openTorrentFile(s); 956 | } 957 | } 958 | 959 | /** 960 | * This method sets the font size for the text in a JFileChooser as the default font size 961 | * is to small 962 | * @param comp the swing component to change the font size of 963 | */ 964 | private void setFileChooserFont(Component[] comp) { 965 | 966 | Font font = new Font("SansSerif",Font.PLAIN, 16); 967 | 968 | for (int x = 0; x < comp.length; x++) { 969 | 970 | if (comp[x] instanceof Container) { 971 | 972 | setFileChooserFont(((Container) comp[x]).getComponents()); 973 | } 974 | 975 | try { 976 | 977 | comp[x].setFont(font); 978 | 979 | } catch (Exception e) { 980 | }//do nothing 981 | } 982 | } 983 | 984 | /** 985 | * 986 | * This function takes a TorrentElement object and updates the GUI with the data held within it. 987 | * @param te A TorrentElement object 988 | */ 989 | private void updateLabels(TorrentElement te) { 990 | 991 | TorrentInfo tInfo = te.gettInfo(); 992 | 993 | //update Labels 994 | Pattern p = Pattern.compile("//[a-z.]+"); 995 | Matcher m = p.matcher(tInfo.announce); 996 | 997 | if (m.find()) { 998 | 999 | tracker.setText(m.group().substring(2)); 1000 | 1001 | } else { 1002 | 1003 | tracker.setText(tInfo.announce); 1004 | } 1005 | 1006 | torrent_name.setText(tInfo.name); 1007 | info_hash.setText(tInfo.hexString(tInfo.infoHash)); 1008 | peer_id.setText(tInfo.hexString(tInfo.peerId)); 1009 | size.setText(new SizeConvert(Long.parseLong(tInfo.size)).toString()); 1010 | 1011 | if (tInfo.creationDate != null) { 1012 | 1013 | Date d = new Date(Long.parseLong(tInfo.creationDate) * 1000); 1014 | date.setText(DateFormat.getDateInstance(DateFormat.SHORT).format(d)); 1015 | 1016 | } else { 1017 | 1018 | date.setText("Unknown"); 1019 | } 1020 | 1021 | //remove all item from the tracker submenu so any subsequent calls to 1022 | //OpenAction doesn't continual add list of JMenuItems to the submenu 1023 | changeTracker.removeAll(); 1024 | 1025 | //if torrent has multiple trackers listed, add them to a popumenu list 1026 | int arrSize = tInfo.announceList.size(); 1027 | 1028 | if (arrSize > 0) { 1029 | 1030 | changeTracker.setEnabled(true); 1031 | 1032 | for (String val : tInfo.announceList) { 1033 | 1034 | JMenuItem item = changeTracker.add(val); 1035 | item.addActionListener(new ChangeTrackerAction()); 1036 | } 1037 | } 1038 | } 1039 | 1040 | /** 1041 | * This function stops any currently running upload of the torrent specified by this functions argument. 1042 | * It also updates relevant GUI elements to inform the user this action has taken place 1043 | * @param tIndex the index into the list of torrents stored in the member variable torrentElement ArrayList. 1044 | */ 1045 | public void stopAction(int tIndex) { 1046 | 1047 | openFileButton.setEnabled(true); 1048 | connectButton.setText("Connect"); 1049 | updatePortMenu.setEnabled(true); 1050 | jSpinLoader1.stop(); 1051 | 1052 | TorrentElement te = torrentElement.get(tIndex); 1053 | Timer timer = te.getTimer(); 1054 | 1055 | if (timer != null) { 1056 | 1057 | timer.stop(); 1058 | } 1059 | 1060 | te.setTimer(null); 1061 | torrentList.repaint(); 1062 | } 1063 | 1064 | /** 1065 | * This function takes as input an absolute path of a torrent file on the file-system. Adds 1066 | * it to the torrentElemnt member variable array. Then updates the GUI with some of the torrent files 1067 | * data, to inform the user that the torrent as been successfully opened. It will display a 1068 | * JOptionPane message box if the file was unable to be opened. 1069 | * @param filePath The absolute file path of a torrent file. 1070 | */ 1071 | private void openTorrentFile(String filePath) { 1072 | 1073 | try (FileInputStream file = new FileInputStream(filePath)) { 1074 | 1075 | //create a new TorrentElemnt object 1076 | TorrentElement te = new TorrentElement(); 1077 | 1078 | //populate the torrent element object with the info found in the torrent file 1079 | //and add it to the torrentElement array 1080 | te.settInfo(new TorrentInfo(file)); 1081 | torrentElement.add(te); 1082 | 1083 | //display torret info on the GUI 1084 | TorrentInfo tInfo = te.gettInfo(); 1085 | updateLabels(te); 1086 | 1087 | //Add the torrent opened to the JList 1088 | DefaultListModel mod = (DefaultListModel) torrentList.getModel(); 1089 | mod.addElement(tInfo.name); 1090 | 1091 | int lastIndex = mod.getSize() - 1; 1092 | //int lastIndex = torrentList.getLastVisibleIndex(); 1093 | torrentList.setSelectedIndex(lastIndex); 1094 | //make the index of the sleceted list item global 1095 | indexSelected = lastIndex; 1096 | te.setIndex(indexSelected); 1097 | 1098 | //resize window to fit updated JLabels 1099 | this.pack(); 1100 | 1101 | //remove all item from the tracker submenu so any subsequent calls to 1102 | //OpenAction doesn't continual add list of JMenuItems to the submenu 1103 | changeTracker.removeAll(); 1104 | 1105 | //if torrent has multiple trackers listed, add them to a popumenu list 1106 | int arrSize = tInfo.announceList.size(); 1107 | 1108 | if (arrSize > 0) { 1109 | 1110 | changeTracker.setEnabled(true); 1111 | 1112 | for (String val : tInfo.announceList) { 1113 | 1114 | JMenuItem item = changeTracker.add(val); 1115 | item.addActionListener(new ChangeTrackerAction()); 1116 | item.setFont(new java.awt.Font("Noto Sans Regular", 0, 14)); // NOI18N 1117 | } 1118 | } 1119 | 1120 | } catch (FileNotFoundException ex) { 1121 | 1122 | JOptionPane.showMessageDialog(this, "Error: Could not find file \n(" + filePath + ")\n" + ex.getMessage(), "Error message", JOptionPane.ERROR_MESSAGE); 1123 | 1124 | } catch (IOException ex) { 1125 | 1126 | JOptionPane.showMessageDialog(this, "IO Error: Could not read file contents \n(" + filePath + ")\n" + ex.getMessage(), "Error message", JOptionPane.ERROR_MESSAGE); 1127 | 1128 | } catch (Exception ex) { 1129 | 1130 | JOptionPane.showMessageDialog(this, "Error: Invalid torrent file. \n(" + filePath + ")\n" + ex.getMessage(), "Error message", JOptionPane.ERROR_MESSAGE); 1131 | } 1132 | } 1133 | 1134 | /** 1135 | * This function is triggered when ever a user presses the openFile button in the GUI. It will open 1136 | * a JFileDialog and allow the user to select one or multiple .torrent files. 1137 | * @param evt 1138 | */ 1139 | private void openFileButtonActionPerformed(java.awt.event.ActionEvent evt) { 1140 | 1141 | FileNameExtensionFilter ff = new FileNameExtensionFilter("Torrents", "torrent"); 1142 | jFileChooser1.setFileFilter(ff); 1143 | jFileChooser1.setMultiSelectionEnabled(true); 1144 | setFileChooserFont(jFileChooser1.getComponents()); 1145 | int res = jFileChooser1.showDialog(this, "open"); 1146 | 1147 | //if a new torrent file is open make sure the spinloader isn't active for a previous torrent 1148 | //that may be running. Also make sure the button is in its connect state. As a previous torrent that 1149 | //is running with change it to its stopped state 1150 | jSpinLoader1.stop(); 1151 | connectButton.setText("Connect"); 1152 | 1153 | //clear any old data from labels because a new file has been opened 1154 | uploaded.setText(""); 1155 | seeders.setText(""); 1156 | leechers.setText(""); 1157 | downloaded.setText(""); 1158 | update.setText(""); 1159 | jCircleProgress1.setVisible(false); 1160 | uploadsSentButton.setText("0"); 1161 | announceButton.setText("0"); 1162 | errorsButton.setText("0"); 1163 | 1164 | if (res == JFileChooser.APPROVE_OPTION) { 1165 | 1166 | File[] files = jFileChooser1.getSelectedFiles(); 1167 | String[] paths = new String[files.length]; 1168 | 1169 | for (int i = 0; i < files.length; i++) { 1170 | 1171 | paths[i] = files[i].getAbsolutePath(); 1172 | } 1173 | 1174 | loadArgs(paths); 1175 | } 1176 | } 1177 | 1178 | /** 1179 | * This function is triggered whenever a user presses the connectButton on the GUI. It will 1180 | * spawn a new thread to preform a HTTP connection to prevent the GUI from pausing as a connection 1181 | * take some time. It will update elements of the GUI to inform the user a connection is taking place 1182 | * @param evt 1183 | */ 1184 | private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) { 1185 | 1186 | if (torrentElement.isEmpty()) { 1187 | 1188 | return; 1189 | } 1190 | 1191 | if (connectButton.getText().equals("Connect")) { 1192 | 1193 | TorrentElement te = torrentElement.get(indexSelected); 1194 | te.settConn(null); 1195 | updatePortMenu.setEnabled(false); 1196 | te.setUploadAmount(0); 1197 | connectButton.setText("Connecting.."); 1198 | jSpinLoader1.start(); 1199 | WindowPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 1200 | 1201 | //connect to tracker in a different thread so it wont 1202 | //stop the GUI from responding while the TrackerConnect object 1203 | //precesses the request 1204 | 1205 | StartConnectionTask sct = new StartConnectionTask(te, this); 1206 | sct.execute(); 1207 | 1208 | //stop button was pressed 1209 | } else { 1210 | 1211 | stopAction(indexSelected); 1212 | } 1213 | } 1214 | 1215 | /** 1216 | * This function is triggered when the user selects the updatePort popup menu option. It allows 1217 | * the user to change the port number that is reported to the torrent tracker 1218 | * @param evt 1219 | */ 1220 | private void updatePortMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_updatePortMenuActionPerformed 1221 | 1222 | changePortDialog.pack(); 1223 | changePortDialog.setLocationRelativeTo(null); 1224 | changePortDialog.setVisible(true); 1225 | }//GEN-LAST:event_updatePortMenuActionPerformed 1226 | 1227 | /** 1228 | * This function will update the uploadSpeed value in the currently selected torrent if 1229 | * the buttons on the JSpinner are clicked on or the enter key is pressed while it has focus 1230 | * @param evt 1231 | */ 1232 | private void jSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinnerStateChanged 1233 | 1234 | //If there a items in the list 1235 | if (!torrentList.isSelectionEmpty()) { 1236 | 1237 | TorrentElement te = torrentElement.get(indexSelected); 1238 | te.setUploadSpeed((Integer) uploadSpeedSpinner.getValue()); 1239 | } 1240 | }//GEN-LAST:event_jSpinnerStateChanged 1241 | 1242 | /** 1243 | * This function commits the value in the JSpinner when ever the user clicks 1244 | * outside anywhere in the window. So you don't have to press enter when manually 1245 | * entering a number in the JSpinner. It will output an error to a message box 1246 | * if the user enters an invalid value 1247 | * @param evt 1248 | */ 1249 | private void windowFocus(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_windowFocus 1250 | 1251 | try { 1252 | 1253 | uploadSpeedSpinner.commitEdit(); 1254 | 1255 | } catch (ParseException ex) { 1256 | 1257 | System.out.println(ex); 1258 | JOptionPane.showMessageDialog(this, "Error: invalid upload speed value \n(" + ex.getMessage() + ")\n", "Error message", JOptionPane.ERROR_MESSAGE); 1259 | } 1260 | }//GEN-LAST:event_windowFocus 1261 | 1262 | /** 1263 | * This function hides the UpdateInterval JDialog from view if the cancel button is pressed. 1264 | * @param evt 1265 | */ 1266 | private void changeUpdateCancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeUpdateCancelButtonActionPerformed 1267 | 1268 | updateIntervalDialog.setVisible(false); 1269 | }//GEN-LAST:event_changeUpdateCancelButtonActionPerformed 1270 | 1271 | /** 1272 | * This function changes the time it will take in seconds until the next update announcement is triggered. 1273 | * It will happen when the OK button is pressed from the Update Interval JDialog. This JDialog is triggered 1274 | * when the user selects the updateInterval JMenuItem in the popup menu. 1275 | * @param evt 1276 | */ 1277 | private void changeUpdateOkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeUpdateOkButtonActionPerformed 1278 | 1279 | TorrentElement te = torrentElement.get(indexSelected); 1280 | int sVal = (Integer) changeUpdateSpinner.getValue(); 1281 | te.setTimeLeft(sVal); 1282 | updateIntervalDialog.setVisible(false); 1283 | 1284 | }//GEN-LAST:event_changeUpdateOkButtonActionPerformed 1285 | 1286 | /** 1287 | * This function is triggered when the popup menu is shown. Its purpose is to enable or disable items 1288 | * in the menu depending on the state of the selected torrent at that time. e.g if the torrent is running 1289 | * some options are disabled, can can only be changed before the torrent is makes a connection 1290 | * @param evt 1291 | */ 1292 | private void jPopupMenu1PopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_jPopupMenu1PopupMenuWillBecomeVisible 1293 | 1294 | //disable options in the popup menu if there are not opened torrents for them to operate on 1295 | if (torrentElement.isEmpty()) { 1296 | 1297 | updateIntervalMenu.setEnabled(false); 1298 | changeTracker.setEnabled(false); 1299 | changeClientMenu.setEnabled(false); 1300 | updatePortMenu.setEnabled(false); 1301 | 1302 | } else { 1303 | 1304 | TorrentElement te = torrentElement.get(indexSelected); 1305 | 1306 | if (te.getTimer() != null) { 1307 | 1308 | updateIntervalMenu.setEnabled(true); 1309 | updatePortMenu.setEnabled(false); 1310 | 1311 | } else { 1312 | 1313 | updateIntervalMenu.setEnabled(false); 1314 | changeClientMenu.setEnabled(true); 1315 | updatePortMenu.setEnabled(true); 1316 | } 1317 | } 1318 | }//GEN-LAST:event_jPopupMenu1PopupMenuWillBecomeVisible 1319 | 1320 | /** 1321 | * This function hides the changePort JDialog from view if the cancel button is pressed. 1322 | * @param evt 1323 | */ 1324 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed 1325 | 1326 | changePortDialog.setVisible(false); 1327 | }//GEN-LAST:event_jButton2ActionPerformed 1328 | 1329 | /** 1330 | * This function changes values in the currently selected torrent that relate to the port number it reports 1331 | * to the tracker. It happens when the OK button is pressed in the changePort JDialog. This dialog is triggered 1332 | * when the changePort JMenu item is selected from the popup menu 1333 | * @param evt 1334 | */ 1335 | private void changePortOkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changePortOkButtonActionPerformed 1336 | 1337 | TorrentElement te = torrentElement.get(indexSelected); 1338 | 1339 | int i = (Integer) changePortSpinner.getValue(); 1340 | te.setPort(String.format("%d", i)); 1341 | changePortDialog.setVisible(false); 1342 | }//GEN-LAST:event_changePortOkButtonActionPerformed 1343 | 1344 | /** 1345 | * This function hides the customClient JDialog from view if the cancel button is pressed. 1346 | * @param evt 1347 | */ 1348 | private void changeClientCancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeClientCancelButtonActionPerformed 1349 | 1350 | customClientDialog.setVisible(false); 1351 | }//GEN-LAST:event_changeClientCancelButtonActionPerformed 1352 | 1353 | /** 1354 | * This function changes the torrent client that the program reports to the torrent tracker. This event 1355 | * happens when the OK button is pressed from the customClient JDialog 1356 | * @param evt 1357 | */ 1358 | private void changeClientOkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeClientOkButtonActionPerformed 1359 | 1360 | TorrentElement te = torrentElement.get(indexSelected); 1361 | 1362 | String client = (String) clientComboBox.getModel().getSelectedItem(); 1363 | String clientCode; 1364 | int version = (Integer) changeClientVersionSpinner.getValue(); 1365 | 1366 | switch (client) { 1367 | 1368 | case "KTorrent": 1369 | 1370 | clientCode = "KT"; 1371 | break; 1372 | 1373 | case "Transmission": 1374 | 1375 | clientCode = "TR"; 1376 | break; 1377 | 1378 | case "qBitTorrent": 1379 | 1380 | clientCode = "qB"; 1381 | break; 1382 | 1383 | case "µTorrent": 1384 | 1385 | clientCode = "UT"; 1386 | break; 1387 | 1388 | default: 1389 | 1390 | clientCode = "KT"; 1391 | } 1392 | 1393 | if (customUserAgentCheckBox.isSelected()) { 1394 | 1395 | String s = customUserAgentTextField.getText(); 1396 | te.setCustomUserAgent(s); 1397 | 1398 | } else { 1399 | 1400 | te.setCustomUserAgent(null); 1401 | } 1402 | 1403 | String codeVersion = String.format("%s%d", clientCode, version); 1404 | te.gettInfo().computePeerId(codeVersion); 1405 | peer_id.setText(te.gettInfo().hexString(te.gettInfo().peerId)); 1406 | customClientDialog.setVisible(false); 1407 | }//GEN-LAST:event_changeClientOkButtonActionPerformed 1408 | 1409 | /** 1410 | * This function hides the msgDialog JDialog from view if the OK button is pressed. 1411 | * @param evt 1412 | */ 1413 | private void msgOkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_msgOkButtonActionPerformed 1414 | 1415 | msgDialog.setVisible(false); 1416 | }//GEN-LAST:event_msgOkButtonActionPerformed 1417 | 1418 | /** 1419 | * This function triggers when a user clicks on the errors JButton in the JToolBar below the List of torrents. 1420 | * It displays a JDialog that displays any errors that the program has detected while sending that torrents 1421 | * data to the torrent tracker 1422 | * @param evt 1423 | */ 1424 | private void errorsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_errorsButtonActionPerformed 1425 | 1426 | String s = errorsButton.getText(); 1427 | 1428 | if (!s.equals("0")) { 1429 | 1430 | TorrentElement te = torrentElement.get(indexSelected); 1431 | String line = ""; 1432 | 1433 | for (int i = 0; i < te.getErrorMsg().size(); i++) { 1434 | 1435 | String[] errMsg = te.getErrorMsg().get(i).split(":"); 1436 | String tmp = String.format("
%s
\n", errMsg[0]); 1437 | line = line.concat(tmp); 1438 | line = line.concat(String.format("%s
", errMsg[1])); 1439 | } 1440 | 1441 | msgEditorPane.setText(line); 1442 | 1443 | } else { 1444 | 1445 | //there are no errors 1446 | msgEditorPane.setText("

There have been no errors !

"); 1447 | } 1448 | 1449 | msgEditorPane.setCaretPosition(0); 1450 | msgLabel.setText("

Error Messages

"); 1451 | msgLabel.setIcon(new ImageIcon(getClass().getResource("/icons/icons-32/exclamation.png"))); 1452 | msgDialog.setTitle("Error Messages"); 1453 | msgDialog.pack(); 1454 | msgDialog.setLocationRelativeTo(null); 1455 | msgDialog.getRootPane().repaint(); 1456 | msgDialog.setVisible(true); 1457 | }//GEN-LAST:event_errorsButtonActionPerformed 1458 | 1459 | /** 1460 | * This function triggers when a user clicks on the Uploads Sent JButton in the JToolBar below the List of torrents. 1461 | * It displays a JDialog that displays how much data the program has successfully sent to the torrent tracker. 1462 | * @param evt 1463 | */ 1464 | private void uploadsSentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_uploadsSentButtonActionPerformed 1465 | 1466 | if (!uploadsSentButton.getText().equals("0")) { 1467 | 1468 | TorrentElement te = torrentElement.get(indexSelected); 1469 | String s = uploadsSentButton.getText(); 1470 | String lineOne = String.format("
%s
has been successfully spoofed and recieved by the tracker
", s); 1471 | msgEditorPane.setText(lineOne); 1472 | 1473 | } else { 1474 | 1475 | msgEditorPane.setText("
No upload data has been sent to the tracker

"); 1476 | } 1477 | 1478 | msgLabel.setIcon(new ImageIcon(getClass().getResource("/icons/icons-32/counter.png"))); 1479 | msgLabel.setText("

Upload Amount

"); 1480 | msgDialog.setTitle("Upload Amount"); 1481 | msgDialog.pack(); 1482 | msgDialog.setLocationRelativeTo(null); 1483 | msgDialog.getRootPane().repaint(); 1484 | msgDialog.setVisible(true); 1485 | }//GEN-LAST:event_uploadsSentButtonActionPerformed 1486 | 1487 | /** 1488 | * This function triggers when a user clicks on the announcements JButton in the JToolBar below the List of torrents. 1489 | * It displays a JDialog that displays all the announcements that the program has sent to the torrent tracker 1490 | * @param evt 1491 | */ 1492 | private void announceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_announceButtonActionPerformed 1493 | 1494 | if (!announceButton.getText().equals("0")) { 1495 | 1496 | TorrentElement te = torrentElement.get(indexSelected); 1497 | String line = "
The following announcements have been sent to the tracker

"; 1498 | 1499 | for (String val : te.getNumAnnouce()) { 1500 | 1501 | line = line.concat(String.format("%s
", val)); 1502 | } 1503 | 1504 | line = line.concat("
"); 1505 | msgEditorPane.setText(line); 1506 | 1507 | } else { 1508 | 1509 | msgEditorPane.setText("
No announcements have been made

"); 1510 | 1511 | } 1512 | 1513 | msgEditorPane.setCaretPosition(0); 1514 | msgLabel.setIcon(new ImageIcon(getClass().getResource("/icons/icons-32/megaphone.png"))); 1515 | msgLabel.setText("

Announcements

"); 1516 | msgDialog.setTitle("Announcements"); 1517 | msgDialog.pack(); 1518 | msgDialog.setLocationRelativeTo(null); 1519 | msgDialog.getRootPane().repaint(); 1520 | msgDialog.setVisible(true); 1521 | }//GEN-LAST:event_announceButtonActionPerformed 1522 | 1523 | /** 1524 | * This function simply repaints the msgDialog JDialog when ever it is shown. It was added to 1525 | * support java 8 JRE. Without it the JDialog will show blank on java 8. 1526 | * @param evt 1527 | */ 1528 | private void msgDialogComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_msgDialogComponentShown 1529 | 1530 | msgDialog.repaint(); 1531 | }//GEN-LAST:event_msgDialogComponentShown 1532 | 1533 | /** 1534 | * This function is triggered when the user clicks on a JCheckBox in the Change Tracker dialog. 1535 | * based on the selection is changes text in the JTexField 1536 | * @param evt 1537 | */ 1538 | private void customUserAgentCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_customUserAgentCheckBoxActionPerformed 1539 | 1540 | if (customUserAgentCheckBox.isSelected()) { 1541 | 1542 | customUserAgentTextField.setEnabled(true); 1543 | customUserAgentTextField.setText(""); 1544 | 1545 | } else { 1546 | 1547 | customUserAgentTextField.setEnabled(false); 1548 | customUserAgentTextField.setText("(Auto)"); 1549 | } 1550 | }//GEN-LAST:event_customUserAgentCheckBoxActionPerformed 1551 | 1552 | /** 1553 | * This function is triggered whenever the user makes a new selection on the JList. It sets the 1554 | * member variable indexSelected to the appropriate integer index location. This allows the program 1555 | * to know what torrent has been selected in the JList and can associate that selection with the 1556 | * corresponding object in the torrentElement ArrayList. 1557 | * @param e 1558 | */ 1559 | private void listListen(ListSelectionEvent e) { 1560 | 1561 | if (e.getValueIsAdjusting()) { 1562 | 1563 | if (!torrentList.isSelectionEmpty()) { 1564 | 1565 | indexSelected = torrentList.getSelectedIndex(); 1566 | 1567 | TorrentElement te = torrentElement.get(indexSelected); 1568 | TorrentInfo tInfo = te.gettInfo(); 1569 | updateLabels(te); 1570 | uploadSpeedSpinner.setValue(te.getUploadSpeed()); 1571 | jCircleProgress1.setVisible(false); 1572 | uploadsSentButton.setText(new SizeConvert(te.getUploadSent()).toString()); 1573 | announceButton.setText(String.format("%d", te.getNumAnnouce().size())); 1574 | errorsButton.setText(String.format("%d", te.getErrorMsg().size())); 1575 | 1576 | if (te.getTimer() == null) { 1577 | 1578 | connectButton.setText("Connect"); 1579 | jSpinLoader1.stop(); 1580 | 1581 | //clear labels if there is not a thread running to clear out any old data that may still be there 1582 | uploaded.setText(""); 1583 | seeders.setText(""); 1584 | leechers.setText(""); 1585 | downloaded.setText(""); 1586 | update.setText(""); 1587 | jCircleProgress1.setValue(0); 1588 | jCircleProgress1.setString(null); 1589 | 1590 | } else { 1591 | 1592 | connectButton.setText("Stop"); 1593 | jSpinLoader1.start(); 1594 | } 1595 | } 1596 | } 1597 | } 1598 | 1599 | /** 1600 | * This function is triggered when the user clicks on the about MenuItem in the popup menu. It shows 1601 | * a JDialog, which displays information about this program to the user 1602 | * @param evt 1603 | */ 1604 | private void aboutMenuItemActionPerformed(java.awt.event.ActionEvent evt) { 1605 | 1606 | String ver = System.getProperty("java.version"); 1607 | String line = String.format("
Version: 2.1
Author: Karma Chameleon
Web site: https://gitlab.com/karma_chameleon/j-ratio-boost
Fugue Icons by: https://p.yusukekamiyamane.com/

Java Version: %s

", ver); 1608 | 1609 | msgEditorPane.setText(line); 1610 | msgLabel.setText("

JRatioBoost

"); 1611 | Image img = new ImageIcon(getClass().getResource("icons/rocket-fly.png")).getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); 1612 | msgLabel.setIcon(new ImageIcon(img)); 1613 | msgDialog.setTitle("About"); 1614 | msgDialog.pack(); 1615 | msgDialog.setLocationRelativeTo(null); 1616 | msgDialog.setVisible(true); 1617 | } 1618 | 1619 | /** 1620 | * This function is triggered when the user clicks on the Update Interval MenuItem in the popup menu. It shows 1621 | * a JDialog, which displays a GUI that allows the user to change the time it will take in seconds to make the 1622 | * next announcement to the torrent tracker for the selected torrent 1623 | * @param evt 1624 | */ 1625 | private void updateIntervalMenuActionPerformed(java.awt.event.ActionEvent evt) { 1626 | 1627 | TorrentElement te = torrentElement.get(indexSelected); 1628 | TrackerConnect tc = te.gettConn(); 1629 | updateIntervalDialog.pack(); 1630 | updateIntervalDialog.setLocationRelativeTo(null); 1631 | updateIntervalDialog.setVisible(true); 1632 | } 1633 | 1634 | /** 1635 | * This function is triggered when the user clicks on the KTorrent MenuItem in the popup menu. It will change 1636 | * the client data in the currently selected torrent, from its current value to KTorrent. 1637 | * @param evt 1638 | */ 1639 | private void kTorrentMenuItemActionPerformed(java.awt.event.ActionEvent evt) { 1640 | 1641 | int n = torrentList.getSelectedIndex(); 1642 | TorrentInfo tInfo = torrentElement.get(indexSelected).gettInfo(); 1643 | 1644 | tInfo.computePeerId("KT5110"); 1645 | peer_id.setText(tInfo.hexString(tInfo.peerId)); 1646 | } 1647 | 1648 | /** 1649 | * This function is triggered when the user clicks on the Transmission MenuItem in the popup menu. It will change 1650 | * the client data in the currently selected torrent, from its current value to Transmission. 1651 | * @param evt 1652 | */ 1653 | private void transmissionMenuItemActionPerformed(java.awt.event.ActionEvent evt) { 1654 | 1655 | int n = torrentList.getSelectedIndex(); 1656 | TorrentInfo tInfo = torrentElement.get(indexSelected).gettInfo(); 1657 | 1658 | tInfo.computePeerId("TR4050"); 1659 | peer_id.setText(tInfo.hexString(tInfo.peerId)); 1660 | } 1661 | 1662 | /** 1663 | * This function is triggered when the user clicks on the Custom Client MenuItem in the popup menu. It shows 1664 | * a JDialog, which allows the user to select from different torrent clients to report to the tracker 1665 | * @param evt 1666 | */ 1667 | private void customClientMenuActionPerformed(java.awt.event.ActionEvent evt) { 1668 | 1669 | TorrentElement te = torrentElement.get(indexSelected); 1670 | 1671 | if (te.getCustomUserAgent() == null) { 1672 | 1673 | customUserAgentCheckBox.setSelected(false); 1674 | customUserAgentTextField.setEnabled(false); 1675 | customUserAgentTextField.setText("(Auto)"); 1676 | 1677 | } else { 1678 | 1679 | customUserAgentCheckBox.setSelected(true); 1680 | customUserAgentTextField.setEnabled(true); 1681 | customUserAgentTextField.setText(te.getCustomUserAgent()); 1682 | } 1683 | 1684 | customClientDialog.pack(); 1685 | customClientDialog.setLocationRelativeTo(null); 1686 | customClientDialog.setVisible(true); 1687 | customClientDialog.repaint(); 1688 | } 1689 | 1690 | public static String formatSeconds(long totalSeconds) { 1691 | 1692 | final long SECONDS_PER_MINUTE = 60; 1693 | final long SECONDS_PER_HOUR = 3600; 1694 | 1695 | // Calculate hours 1696 | long hours = totalSeconds / SECONDS_PER_HOUR; 1697 | 1698 | // Calculate remaining seconds after hours are accounted for 1699 | long remainingSeconds = totalSeconds % SECONDS_PER_HOUR; 1700 | 1701 | // Calculate minutes from the remaining seconds 1702 | long minutes = remainingSeconds / SECONDS_PER_MINUTE; 1703 | 1704 | // Calculate seconds from the final remainder 1705 | long seconds = remainingSeconds % SECONDS_PER_MINUTE; 1706 | 1707 | return String.format("%02d:%02d", minutes, seconds); 1708 | } 1709 | 1710 | /** 1711 | * This inner class is dynamically added as an ActionListener object to JMenuItems. It is used so the user can 1712 | * select from a list of announce URL's located in the torrent files. 1713 | */ 1714 | private class ChangeTrackerAction implements ActionListener { 1715 | 1716 | int n = torrentList.getSelectedIndex(); 1717 | TorrentInfo tInfo = torrentElement.get(indexSelected).gettInfo(); 1718 | 1719 | @Override 1720 | public void actionPerformed(ActionEvent e) { 1721 | 1722 | JMenuItem item = (JMenuItem) e.getSource(); 1723 | tInfo.announce = item.getText(); 1724 | 1725 | Pattern p = Pattern.compile("[^\\/]+\\.[comorgnetvlub]{2,4}"); 1726 | Matcher m = p.matcher(tInfo.announce); 1727 | 1728 | if (m.find()) { 1729 | 1730 | tracker.setText(m.group()); 1731 | 1732 | } else { 1733 | 1734 | tracker.setText(tInfo.announce); 1735 | } 1736 | } 1737 | } 1738 | 1739 | /** 1740 | * This method get called periodically to update the progress bar and run the torrent for the specified update interval received from the tracker. 1741 | **/ 1742 | 1743 | public void periodicUpdate(TorrentElement te) { 1744 | 1745 | TrackerConnect tc; 1746 | Timer timer; 1747 | 1748 | tc = te.gettConn(); 1749 | timer = te.getTimer(); 1750 | 1751 | int upSpeed = te.getUploadSpeed(); 1752 | long n = te.getUploadAmount(); 1753 | te.setUploadAmount(n += SizeConvert.KBToB(upSpeed)); 1754 | 1755 | if (te.getIndex() == indexSelected) { 1756 | 1757 | //update GUI label with tracker response info 1758 | seeders.setText(tc.seeders); 1759 | leechers.setText(tc.leechers); 1760 | update.setText(tc.interval); 1761 | downloaded.setText("100%"); 1762 | uploaded.setText(new SizeConvert(te.getUploadAmount()).toString()); 1763 | announceButton.setText("" + te.getNumAnnouce().size()); 1764 | uploadsSentButton.setText(new SizeConvert(te.getUploadSent()).toString()); 1765 | jCircleProgress1.setVisible(true); 1766 | } 1767 | 1768 | if (te.getTimeLeft() <= 0) { 1769 | 1770 | //stop the timer thread from executing to prevent the timer 1771 | //from continuing to execute while the http request is being made 1772 | //by the TrackerConnect instance 1773 | timer.stop(); 1774 | 1775 | UpdateConnectionTask uct = new UpdateConnectionTask(te, this); 1776 | uct.execute(); 1777 | } 1778 | 1779 | //only update the progress bar for the currently selected torrent, otherwise all running torrents 1780 | //would be updating the same progress bar at the same time 1781 | if (te.getIndex() == indexSelected) { 1782 | 1783 | int total = Integer.parseInt(tc.interval); 1784 | int num = total - te.getTimeLeft(); 1785 | String timeLeft = Gui.formatSeconds(te.getTimeLeft()); 1786 | 1787 | jCircleProgress1.setMaximum(total); 1788 | jCircleProgress1.setValue(num); 1789 | jCircleProgress1.setString(timeLeft); 1790 | } 1791 | 1792 | //update the variable that determines how much time is left for the selected torrent until it updates 1793 | te.setTimeLeft(te.getTimeLeft() - 1); 1794 | } 1795 | 1796 | /** 1797 | * This class serves to change the DefaultListCellrenderer of a JList to one that can display a JLabel with 1798 | * and icon. The Icon has the ability to change depending on the programs current state 1799 | */ 1800 | private class customRenderer extends DefaultListCellRenderer { 1801 | 1802 | @Override 1803 | public Component getListCellRendererComponent(JList l, Object val, int index, boolean isSelected, boolean cellHasFocus) { 1804 | 1805 | JLabel label = (JLabel) super.getListCellRendererComponent(l, val, index, isSelected, cellHasFocus); 1806 | label.setHorizontalTextPosition(JLabel.RIGHT); 1807 | 1808 | TorrentElement te = torrentElement.get(index); 1809 | 1810 | //if torrents are running 1811 | if (te.getTimer() != null) { 1812 | 1813 | //display green icon 1814 | label.setIcon(new ImageIcon(getClass().getResource("icons/status.png"))); 1815 | 1816 | //torrents are not running 1817 | } else { 1818 | 1819 | //if the torrent has no errors 1820 | if (te.getErrorMsg().isEmpty()) { 1821 | 1822 | //display transparent icon 1823 | label.setIcon(new ImageIcon(getClass().getResource("icons/status-offline.png"))); 1824 | 1825 | } else { 1826 | 1827 | //display red icon 1828 | label.setIcon(new ImageIcon(getClass().getResource("icons/status-busy.png"))); 1829 | } 1830 | } 1831 | 1832 | return label; 1833 | } 1834 | } 1835 | 1836 | // Variables declaration - do not modify//GEN-BEGIN:variables 1837 | private javax.swing.JPanel ConnectPanel; 1838 | private javax.swing.JPanel InfoPanel; 1839 | private javax.swing.JPanel OutputPanel; 1840 | private javax.swing.JPanel TorrentDataPanel; 1841 | public javax.swing.JPanel WindowPanel; 1842 | private javax.swing.JMenuItem aboutMenuItem; 1843 | private javax.swing.JButton announceButton; 1844 | private javax.swing.ButtonGroup buttonGroup1; 1845 | private javax.swing.JButton changeClientCancelButton; 1846 | private javax.swing.JMenu changeClientMenu; 1847 | private javax.swing.JButton changeClientOkButton; 1848 | private javax.swing.JSpinner changeClientVersionSpinner; 1849 | private javax.swing.JDialog changePortDialog; 1850 | private javax.swing.JButton changePortOkButton; 1851 | private javax.swing.JSpinner changePortSpinner; 1852 | private javax.swing.JMenu changeTracker; 1853 | private javax.swing.JButton changeUpdateCancelButton; 1854 | private javax.swing.JButton changeUpdateOkButton; 1855 | private javax.swing.JSpinner changeUpdateSpinner; 1856 | private javax.swing.JComboBox clientComboBox; 1857 | public javax.swing.JButton connectButton; 1858 | private javax.swing.JDialog customClientDialog; 1859 | private javax.swing.JMenuItem customClientMenu; 1860 | private javax.swing.JCheckBox customUserAgentCheckBox; 1861 | private javax.swing.JTextField customUserAgentTextField; 1862 | private javax.swing.JLabel date; 1863 | private javax.swing.JLabel downloaded; 1864 | public javax.swing.JButton errorsButton; 1865 | private javax.swing.JLabel info_hash; 1866 | private JArrow jArrow1; 1867 | private javax.swing.JButton jButton2; 1868 | private JCircleProgress jCircleProgress1; 1869 | private javax.swing.JFileChooser jFileChooser1; 1870 | private javax.swing.JLabel jLabel1; 1871 | private javax.swing.JLabel jLabel10; 1872 | private javax.swing.JLabel jLabel11; 1873 | private javax.swing.JLabel jLabel12; 1874 | private javax.swing.JLabel jLabel13; 1875 | private javax.swing.JLabel jLabel15; 1876 | private javax.swing.JLabel jLabel16; 1877 | private javax.swing.JLabel jLabel17; 1878 | private javax.swing.JLabel jLabel18; 1879 | private javax.swing.JLabel jLabel2; 1880 | private javax.swing.JLabel jLabel3; 1881 | private javax.swing.JLabel jLabel4; 1882 | private javax.swing.JLabel jLabel6; 1883 | private javax.swing.JLabel jLabel8; 1884 | private javax.swing.JLabel jLabel9; 1885 | private javax.swing.JPanel jPanel1; 1886 | private javax.swing.JPanel jPanel10; 1887 | private javax.swing.JPanel jPanel11; 1888 | private javax.swing.JPanel jPanel12; 1889 | private javax.swing.JPanel jPanel13; 1890 | private javax.swing.JPanel jPanel14; 1891 | private javax.swing.JPanel jPanel15; 1892 | private javax.swing.JPanel jPanel2; 1893 | private javax.swing.JPanel jPanel5; 1894 | private javax.swing.JPanel jPanel6; 1895 | private javax.swing.JPanel jPanel7; 1896 | private javax.swing.JPanel jPanel8; 1897 | private javax.swing.JPanel jPanel9; 1898 | private javax.swing.JPopupMenu jPopupMenu1; 1899 | private javax.swing.JScrollPane jScrollPane1; 1900 | private javax.swing.JScrollPane jScrollPane2; 1901 | private javax.swing.JSeparator jSeparator1; 1902 | private javax.swing.JSeparator jSeparator2; 1903 | private JSpinLoader jSpinLoader1; 1904 | private javax.swing.JToolBar jToolBar1; 1905 | private javax.swing.JMenuItem kTorrentMenuItem; 1906 | private javax.swing.JLabel leechers; 1907 | private javax.swing.JPanel listPanel; 1908 | private javax.swing.JDialog msgDialog; 1909 | private javax.swing.JEditorPane msgEditorPane; 1910 | private javax.swing.JLabel msgLabel; 1911 | private javax.swing.JButton msgOkButton; 1912 | private javax.swing.JButton openFileButton; 1913 | private javax.swing.JPanel openPanel; 1914 | private javax.swing.JLabel peer_id; 1915 | private javax.swing.JLabel seeders; 1916 | private javax.swing.JLabel size; 1917 | private javax.swing.JPanel toolBar; 1918 | public javax.swing.JList torrentList; 1919 | private javax.swing.JLabel torrent_name; 1920 | private javax.swing.JLabel tracker; 1921 | private javax.swing.JMenuItem transmissionMenuItem; 1922 | private javax.swing.JLabel update; 1923 | private javax.swing.JDialog updateIntervalDialog; 1924 | private javax.swing.JMenuItem updateIntervalMenu; 1925 | private javax.swing.JMenuItem updatePortMenu; 1926 | private javax.swing.JSpinner uploadSpeedSpinner; 1927 | private javax.swing.JLabel uploaded; 1928 | private javax.swing.JButton uploadsSentButton; 1929 | // End of variables declaration//GEN-END:variables 1930 | } --------------------------------------------------------------------------------