├── .gitignore ├── README.md ├── docs ├── assets │ ├── logo.png │ └── style.css └── index.html ├── src └── main │ └── java │ └── net │ └── jacobpeterson │ ├── download │ ├── format │ │ ├── SongDownload.java │ │ ├── VideoDownload.java │ │ └── AudioDownload.java │ ├── FFMPEGProcessor.java │ └── Download.java │ ├── view │ ├── groupcomposite │ │ ├── AudioInfoComposite.java │ │ ├── VideoInfoComposite.java │ │ ├── song │ │ │ └── AlbumCoverCanvas.java │ │ └── EditSongComposite.java │ ├── UpdateShellComposite.java │ ├── albumcovertab │ │ ├── ThumbnailTabItem.java │ │ ├── AlbumCoverTabItem.java │ │ └── CustomTabItem.java │ ├── ApplicationShell.java │ ├── EditAlbumCoverComposite.java │ └── ApplicationShellContents.java │ ├── controller │ ├── DownloadsController.java │ ├── ApplicationDataController.java │ └── viewcontroller │ │ ├── EditAlbumCoverCompositeController.java │ │ └── ApplicationShellController.java │ ├── util │ ├── ExceptionUtils.java │ └── SWTUtils.java │ ├── MediaFission.java │ └── MediaFissionCommandLine.java ├── lib └── Lib Download Links.txt ├── MediaFission.iml ├── resources ├── logo.svg └── Random Code.txt └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | target/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MediaFission 2 | Visit https://petersoj.github.io/MediaFission/ for more info. -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MediaFission/master/docs/assets/logo.png -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/download/format/SongDownload.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.download.format; 2 | 3 | public class SongDownload { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/download/FFMPEGProcessor.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.download; 2 | 3 | public class FFMPEGProcessor { 4 | 5 | 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/download/format/VideoDownload.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.download.format; 2 | 3 | import net.jacobpeterson.download.Download; 4 | 5 | public class VideoDownload { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/download/format/AudioDownload.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.download.format; 2 | 3 | import net.jacobpeterson.download.Download; 4 | 5 | public class AudioDownload { 6 | 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /lib/Lib Download Links.txt: -------------------------------------------------------------------------------- 1 | Lib download links 2 | 3 | http://download.eclipse.org/eclipse/downloads/ 4 | 5 | Install maven jar to local maven repo! 6 | 7 | 8 | To install to local maven repo: 9 | https://stackoverflow.com/questions/30347310/adding-a-system-dependency-to-maven -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/download/Download.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.download; 2 | 3 | import java.io.File; 4 | import java.net.URL; 5 | 6 | public class Download { 7 | 8 | private File dataLocation; 9 | private URL downloadURL; 10 | 11 | public Download(File dataLocation) { 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/groupcomposite/AudioInfoComposite.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.groupcomposite; 2 | 3 | import org.eclipse.swt.widgets.Group; 4 | 5 | public class AudioInfoComposite { 6 | 7 | private Group groupContainer; 8 | 9 | public AudioInfoComposite(Group groupContainer) { 10 | this.groupContainer = groupContainer; 11 | 12 | } 13 | 14 | public void setup() { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/controller/DownloadsController.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.controller; 2 | 3 | import net.jacobpeterson.MediaFission; 4 | 5 | public class DownloadsController { 6 | 7 | private MediaFission mediaFission; 8 | 9 | public DownloadsController(MediaFission mediaFission) { 10 | this.mediaFission = mediaFission; 11 | } 12 | 13 | public void download(String url) { 14 | 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/groupcomposite/VideoInfoComposite.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.groupcomposite; 2 | 3 | import org.eclipse.swt.widgets.Group; 4 | 5 | public class VideoInfoComposite { 6 | 7 | private Group groupContainer; 8 | 9 | public VideoInfoComposite(Group groupContainer) { 10 | this.groupContainer = groupContainer; 11 | 12 | } 13 | 14 | public void setup() { 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/controller/ApplicationDataController.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.controller; 2 | 3 | import net.jacobpeterson.MediaFission; 4 | 5 | import java.io.File; 6 | 7 | public class ApplicationDataController { 8 | 9 | private MediaFission mediaFission; 10 | 11 | private File applicationDataLocation; 12 | 13 | public ApplicationDataController(MediaFission mediaFission) { 14 | this.mediaFission = mediaFission; 15 | 16 | this.applicationDataLocation = new File(System.getProperty("user.home") + "/Library/Application Support/MediaFission/"); // Only for Mac 17 | } 18 | 19 | public void setup() { 20 | if (!applicationDataLocation.mkdir()) { // Failed to create app support directory 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/UpdateShellComposite.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.widgets.Label; 5 | import org.eclipse.swt.widgets.Shell; 6 | 7 | public class UpdateShellComposite { 8 | 9 | private Shell shell; 10 | 11 | private Label updatingLabel; 12 | 13 | public UpdateShellComposite(Shell shell) { 14 | this.shell = shell; 15 | 16 | this.updatingLabel = new Label(shell, SWT.WRAP | SWT.CENTER); 17 | } 18 | 19 | public void setup() { 20 | this.setupShell(); 21 | this.setupWidgets(); 22 | this.setupLayout(); 23 | } 24 | 25 | private void setupShell() { 26 | 27 | } 28 | 29 | private void setupWidgets() { 30 | 31 | } 32 | 33 | private void setupLayout() { 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /MediaFission.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/util/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.util; 2 | 3 | import net.jacobpeterson.MediaFission; 4 | 5 | public class ExceptionUtils { 6 | 7 | private static MediaFission mediaFissionInstance; 8 | 9 | public static void setMediaFissionInstance(MediaFission mediaFission) { 10 | mediaFissionInstance = mediaFission; 11 | } 12 | 13 | public static void handleException(Exception e, boolean fatal) { 14 | e.printStackTrace(); // If ever debugging 15 | if (fatal) { 16 | System.exit(0); 17 | } 18 | } 19 | 20 | public static void handleError(String error, boolean openDialog, boolean fatal) { 21 | try { 22 | if (openDialog) { 23 | // MessageBox dialog = new MessageBox(mediaFissionInstance.getShellController().getApplicationShell().getShell(), SWT.ICON_ERROR | SWT.YES | SWT.NO); 24 | // dialog.setMessage("ERROR: " + error); 25 | } 26 | } catch (NullPointerException e) { // In case one of the above instances is null 27 | fatal = true; 28 | } 29 | if (fatal) { 30 | System.exit(0); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /resources/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/Random Code.txt: -------------------------------------------------------------------------------- 1 | /Users/Jacob/Downloads/ffmpeg -i /Users/Jacob/Desktop/f/Post\ Malone\ -\ No\ Option.webm -vn -ab 128k -ar 44100 -y -metadata title="No Option" -metadata artist="Post Malone" /Users/Jacob/Desktop/out9.mp3 2 | 3 | 4 | To help combine audio and video: 5 | https://superuser.com/a/590210 6 | 7 | To get/test album art: 8 | https://stackoverflow.com/a/4501009 - for test key 9 | https://www.last.fm/api/show/track.getInfo - for track info 10 | http://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key=bc67636b1f06495cdee8b3dbcc49f477&artist= &track= &format=json 11 | 12 | https://www.codeproject.com/articles/36001/using-public-web-services-to-get-information-about 13 | 14 | 15 | Drawing centered text: 16 | http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/DrawTextDemo.htm 17 | 18 | 19 | Example youtube-dl command: 20 | /Users/Jacob/Desktop/youtube-dl -v -f bestaudio --no-playlist --dump-json --playlist-items 1 21 | 22 | FFMPEG commands: 23 | /Users/Jacob/Desktop/ffmpeg -i -vn -aq 128k -ar 44100 -y out.mp3 - http://bytefreaks.net/gnulinux/bash/ffmpeg-extract-audio-from-webm-to-mp3 24 | /Users/Jacob/Desktop/ffmpeg -i /Users/Jacob/input.mp3 -i album_cover.jpg -c copy -map 0:0 -map 1:0 -metadata title=" " -metadata artist=" " -metadata album=" " -metadata album_artist=" " -metadata genre=" " -metadata:s:v title="Cover (front)" output.mp3 25 | 26 | /Users/Jacob/Desktop/ffmpeg -i -i -y -map 0 -map 1 -metadata title=" " -metadata artist=" " -metadata album=" " -metadata album_artist=" " -metadata genre=" " -metadata:s:1 title="Cover (front)" 27 | 28 | 29 | DESCRIPTION: Download Youtube, Soundcloud, and etc. as Audio, Video, or as a Song (album cover and other metadata)! -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/albumcovertab/ThumbnailTabItem.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.albumcovertab; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.layout.FormAttachment; 5 | import org.eclipse.swt.layout.FormData; 6 | import org.eclipse.swt.layout.FormLayout; 7 | import org.eclipse.swt.widgets.Composite; 8 | import org.eclipse.swt.widgets.Label; 9 | import org.eclipse.swt.widgets.TabFolder; 10 | import org.eclipse.swt.widgets.TabItem; 11 | 12 | public class ThumbnailTabItem { 13 | 14 | private TabFolder tabFolder; 15 | 16 | private TabItem tabItem; 17 | private Composite composite; 18 | private Label explanationLabel; 19 | 20 | public ThumbnailTabItem(TabFolder tabFolder) { 21 | this.tabFolder = tabFolder; 22 | 23 | this.tabItem = new TabItem(tabFolder, SWT.NONE); 24 | this.composite = new Composite(tabFolder, SWT.NONE); 25 | this.explanationLabel = new Label(composite, SWT.WRAP | SWT.CENTER); 26 | } 27 | 28 | public void setup() { 29 | this.setupWidgets(); 30 | this.setupLayout(); 31 | } 32 | 33 | private void setupWidgets() { 34 | this.tabItem.setText("Thumbnail"); 35 | this.tabItem.setControl(composite); 36 | this.explanationLabel.setText("This will attempt to use the thumbnail from the given media link."); 37 | } 38 | 39 | private void setupLayout() { 40 | FormLayout layout = new FormLayout(); 41 | layout.marginTop = 10; 42 | layout.marginLeft = 10; 43 | layout.marginBottom = 10; 44 | layout.marginRight = 10; 45 | this.composite.setLayout(layout); 46 | 47 | FormData formData = new FormData(); 48 | formData.top = new FormAttachment(40); 49 | formData.left = new FormAttachment(0); 50 | formData.right = new FormAttachment(100); 51 | this.explanationLabel.setLayoutData(formData); 52 | 53 | this.composite.layout(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/ApplicationShell.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view; 2 | 3 | import net.jacobpeterson.MediaFission; 4 | import org.eclipse.swt.graphics.Rectangle; 5 | import org.eclipse.swt.widgets.Display; 6 | import org.eclipse.swt.widgets.Shell; 7 | 8 | public class ApplicationShell { 9 | 10 | private MediaFission mediaFission; 11 | 12 | private Display display; 13 | private Shell shell; 14 | 15 | private ApplicationShellContents applicationShellContents; 16 | 17 | public ApplicationShell(MediaFission mediaFission) { 18 | this.mediaFission = mediaFission; 19 | this.display = new Display(); 20 | this.shell = new Shell(display); 21 | } 22 | 23 | public void setup() { 24 | this.setupShell(); 25 | this.setupLayout(); 26 | } 27 | 28 | private void setupShell() { 29 | this.shell.setText("MediaFission"); 30 | this.shell.setMinimumSize(600, 350); 31 | this.shell.setSize(900, 500); 32 | } 33 | 34 | private void setupLayout() { 35 | // Position Shell in center of Display 36 | Rectangle screenSize = shell.getDisplay().getPrimaryMonitor().getBounds(); 37 | Rectangle shellBounds = shell.getBounds(); 38 | this.shell.setLocation((screenSize.width - shellBounds.width) / 2, (screenSize.height - shellBounds.height) / 2); 39 | } 40 | 41 | public void createContents() { 42 | this.applicationShellContents = new ApplicationShellContents(this); 43 | this.applicationShellContents.setup(); 44 | } 45 | 46 | 47 | // Blocking method 48 | public void start() { 49 | this.shell.layout(); // Layout everything 50 | this.shell.open(); // Shows the Window 51 | 52 | while (!shell.isDisposed()) { 53 | if (!display.readAndDispatch()) { 54 | display.sleep(); 55 | } 56 | } 57 | this.display.dispose(); 58 | } 59 | 60 | 61 | public MediaFission getMediaFission() { 62 | return mediaFission; 63 | } 64 | 65 | public Display getDisplay() { 66 | return display; 67 | } 68 | 69 | public Shell getShell() { 70 | return shell; 71 | } 72 | 73 | public ApplicationShellContents getContents() { 74 | return applicationShellContents; 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/controller/viewcontroller/EditAlbumCoverCompositeController.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.controller.viewcontroller; 2 | 3 | import net.jacobpeterson.view.EditAlbumCoverComposite; 4 | import org.eclipse.swt.events.SelectionAdapter; 5 | import org.eclipse.swt.events.SelectionEvent; 6 | 7 | public class EditAlbumCoverCompositeController { 8 | 9 | private ApplicationShellController applicationShellController; 10 | private EditAlbumCoverComposite editAlbumCoverComposite; 11 | 12 | public EditAlbumCoverCompositeController(ApplicationShellController applicationShellController) { 13 | this.applicationShellController = applicationShellController; 14 | this.editAlbumCoverComposite = new EditAlbumCoverComposite(applicationShellController.getApplicationShell()); 15 | } 16 | 17 | public void setup() { 18 | this.editAlbumCoverComposite.setup(); 19 | this.editAlbumCoverComposite.getComposite().setVisible(false); // Hide until needed 20 | 21 | this.addListeners(); 22 | } 23 | 24 | private void addListeners() { 25 | this.editAlbumCoverComposite.getTabFolder().addSelectionListener(new SelectionAdapter() { 26 | @Override 27 | public void widgetSelected(SelectionEvent selectionEvent) { 28 | handleTabFolderSelection(selectionEvent); 29 | } 30 | }); 31 | // May want to use MouseAdapter for press listener!!! 32 | this.editAlbumCoverComposite.getAlbumCoverTabItem().getRefreshButton().addSelectionListener(new SelectionAdapter() { 33 | @Override 34 | public void widgetSelected(SelectionEvent selectionEvent) { 35 | handleAlbumArtRefreshButtonPress(selectionEvent); 36 | } 37 | }); 38 | } 39 | 40 | private void handleTabFolderSelection(SelectionEvent selectionEvent) { 41 | System.out.println(selectionEvent.item == editAlbumCoverComposite.getAlbumCoverTabItem().getTabItem()); 42 | } 43 | 44 | private void handleAlbumArtRefreshButtonPress(SelectionEvent selectionEvent) { 45 | 46 | } 47 | 48 | private void handleCustomChooseFileButtonPress(SelectionEvent selectionEvent) { 49 | 50 | } 51 | 52 | private void handleCustomImageURLButtonPress() { 53 | 54 | } 55 | 56 | private void handleRemoveButtonPress() { 57 | 58 | } 59 | 60 | private void handleDoneButtonPress() { 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/groupcomposite/song/AlbumCoverCanvas.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.groupcomposite.song; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.events.*; 5 | import org.eclipse.swt.graphics.GC; 6 | import org.eclipse.swt.graphics.Image; 7 | import org.eclipse.swt.layout.FormData; 8 | import org.eclipse.swt.widgets.Canvas; 9 | import org.eclipse.swt.widgets.Composite; 10 | import org.eclipse.swt.widgets.Control; 11 | 12 | public class AlbumCoverCanvas implements PaintListener, MouseMoveListener, MouseWheelListener { 13 | 14 | private Composite composite; 15 | 16 | private Canvas canvas; 17 | private Image albumCover; 18 | 19 | public AlbumCoverCanvas(Composite composite) { 20 | this.composite = composite; 21 | this.canvas = new Canvas(composite, SWT.BORDER); 22 | } 23 | 24 | public void setup() { 25 | // All added listeners are for UI updating and interacting 26 | this.canvas.addPaintListener(this); 27 | this.canvas.addMouseWheelListener(this); 28 | this.canvas.addMouseMoveListener(this); 29 | this.canvas.addListener(SWT.Resize, e -> { 30 | // Used to maintain a 1:1 aspect ration for this canvas. 31 | // Will overflow on the right side, but eh. 32 | FormData formData = (FormData) canvas.getLayoutData(); 33 | formData.width = canvas.getSize().y; 34 | canvas.setLayoutData(formData); 35 | canvas.getShell().layout(new Control[]{canvas}, SWT.CHANGED); 36 | }); 37 | } 38 | 39 | @Override 40 | public void paintControl(PaintEvent paintEvent) { 41 | GC gc = paintEvent.gc; 42 | } 43 | 44 | @Override 45 | public void mouseMove(MouseEvent mouseEvent) { 46 | if (mouseEvent.stateMask == SWT.BUTTON1) { 47 | 48 | } 49 | } 50 | 51 | @Override 52 | public void mouseScrolled(MouseEvent mouseEvent) { 53 | 54 | } 55 | 56 | public void setAlbumCoverImage(Image albumCover) { 57 | if (albumCover != null) { 58 | this.canvas.setCursor(composite.getDisplay().getSystemCursor(SWT.CURSOR_HAND)); // Lets user know they can drag the image around. 59 | } else { 60 | this.canvas.setCursor(null); // Reset cursor to normal 61 | } 62 | this.albumCover = albumCover; 63 | } 64 | 65 | 66 | public Canvas getCanvas() { 67 | return canvas; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/util/SWTUtils.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.util; 2 | 3 | import org.eclipse.swt.graphics.ImageData; 4 | import org.eclipse.swt.graphics.PaletteData; 5 | import org.eclipse.swt.graphics.RGB; 6 | 7 | import java.awt.image.BufferedImage; 8 | import java.awt.image.DirectColorModel; 9 | import java.awt.image.IndexColorModel; 10 | import java.awt.image.WritableRaster; 11 | 12 | public class SWTUtils { 13 | 14 | public static ImageData convertToSWT(BufferedImage bufferedImage) { 15 | if (bufferedImage.getColorModel() instanceof DirectColorModel) { 16 | DirectColorModel colorModel = (DirectColorModel) bufferedImage.getColorModel(); 17 | PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(), colorModel.getBlueMask()); 18 | ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette); 19 | for (int y = 0; y < data.height; y++) { 20 | for (int x = 0; x < data.width; x++) { 21 | int rgb = bufferedImage.getRGB(x, y); 22 | int pixel = palette.getPixel(new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF)); 23 | data.setPixel(x, y, pixel); 24 | if (colorModel.hasAlpha()) { 25 | data.setAlpha(x, y, (rgb >> 24) & 0xFF); 26 | } 27 | } 28 | } 29 | return data; 30 | } else if (bufferedImage.getColorModel() instanceof IndexColorModel) { 31 | IndexColorModel colorModel = (IndexColorModel) bufferedImage.getColorModel(); 32 | int size = colorModel.getMapSize(); 33 | byte[] reds = new byte[size]; 34 | byte[] greens = new byte[size]; 35 | byte[] blues = new byte[size]; 36 | colorModel.getReds(reds); 37 | colorModel.getGreens(greens); 38 | colorModel.getBlues(blues); 39 | RGB[] rgbs = new RGB[size]; 40 | for (int i = 0; i < rgbs.length; i++) { 41 | rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF); 42 | } 43 | PaletteData palette = new PaletteData(rgbs); 44 | ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette); 45 | data.transparentPixel = colorModel.getTransparentPixel(); 46 | WritableRaster raster = bufferedImage.getRaster(); 47 | int[] pixelArray = new int[1]; 48 | for (int y = 0; y < data.height; y++) { 49 | for (int x = 0; x < data.width; x++) { 50 | raster.getPixel(x, y, pixelArray); 51 | data.setPixel(x, y, pixelArray[0]); 52 | } 53 | } 54 | return data; 55 | } 56 | return null; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.jacobpeterson 8 | MediaFission 9 | 0.1 10 | 11 | 12 | 13 | com.google.code.gson 14 | gson 15 | LATEST 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.eclipse.swt 24 | org.eclipse.swt.cocoa.macosx.x86_64 25 | 4.7.2 26 | compile 27 | 28 | 29 | 30 | 31 | com.mpatric 32 | mp3agic 33 | 0.9.1 34 | compile 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-compiler-plugin 43 | 3.6.1 44 | 45 | 1.8 46 | 1.8 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-assembly-plugin 52 | 53 | 54 | package 55 | 56 | single 57 | 58 | 59 | 60 | 61 | 62 | 63 | true 64 | net.jacobpeterson.MediaFission 65 | 66 | 67 | 68 | jar-with-dependencies 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/controller/viewcontroller/ApplicationShellController.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.controller.viewcontroller; 2 | 3 | import net.jacobpeterson.MediaFission; 4 | import net.jacobpeterson.view.ApplicationShell; 5 | import org.eclipse.swt.events.KeyEvent; 6 | import org.eclipse.swt.events.SelectionEvent; 7 | import org.eclipse.swt.widgets.Event; 8 | 9 | public class ApplicationShellController { 10 | 11 | private MediaFission mediaFission; 12 | 13 | private ApplicationShell applicationShell; 14 | 15 | public ApplicationShellController(MediaFission mediaFission) { 16 | this.mediaFission = mediaFission; 17 | 18 | this.applicationShell = new ApplicationShell(mediaFission); 19 | } 20 | 21 | public void setup() { 22 | this.applicationShell.setup(); 23 | 24 | 25 | this.addListeners(); 26 | } 27 | 28 | public void start() { 29 | this.applicationShell.start(); // Blocks until main shell is disposed 30 | } 31 | 32 | private void addListeners() { 33 | // this.applicationShell.getShell().addListener(SWT.Close, this::handleClose); 34 | // this.applicationShell.getDisplay().addListener(SWT.Close, this::handleClose); 35 | // this.applicationShell.getURLText().addKeyListener(new KeyAdapter() { 36 | // @Override 37 | // public void keyReleased(KeyEvent keyEvent) { 38 | // handleURLTextKeyRelease(keyEvent); 39 | // } 40 | // }); 41 | // //this.applicationShell.getURLText().addModifyListener(this::handleURLTextModification); maybe implement later for soundcloud links (remove video option) 42 | // this.applicationShell.getDownloadAsCombo().addSelectionListener(new SelectionAdapter() { 43 | // @Override 44 | // public void widgetSelected(SelectionEvent selectionEvent) { 45 | // handleDownloadAsComboSelection(selectionEvent); 46 | // } 47 | // }); 48 | // this.applicationShell.getDownloadList().addSelectionListener(new SelectionAdapter() { 49 | // @Override 50 | // public void widgetSelected(SelectionEvent selectionEvent) { 51 | // handleDownloadListSelect(selectionEvent); 52 | // } 53 | // }); 54 | } 55 | 56 | private void handleURLTextKeyRelease(KeyEvent keyEvent) { 57 | if (keyEvent.character == 0x0D) { // ASCII hex code for the Return key 58 | // this.mediaFission.getDownloadsController().download(applicationShell.getURLText().getText()); 59 | } 60 | } 61 | 62 | private void handleDownloadAsComboSelection(SelectionEvent selectionEvent) { 63 | // this.mediaFission.getDownloadsController().download(applicationShell.getURLText().getText()); 64 | } 65 | 66 | private void handleDownloadListSelect(SelectionEvent selectionEvent) { 67 | 68 | } 69 | 70 | private void handleClose(Event event) { 71 | // event.doit = false if there is a download and stuff 72 | // MessageBox dialog = new MessageBox(shell, SWT.ICON_ERROR | SWT.YES | SWT.NO); 73 | // dialog.setMessage("You have active downloads!\nQuiting will cancel these downloads.\n\nAre you sure you want to quit?"); 74 | // 75 | // if (dialog.open() == SWT.NO) { 76 | // event.doit = false; 77 | // } 78 | } 79 | 80 | public ApplicationShell getApplicationShell() { 81 | return applicationShell; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/EditAlbumCoverComposite.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view; 2 | 3 | import net.jacobpeterson.view.albumcovertab.AlbumCoverTabItem; 4 | import net.jacobpeterson.view.albumcovertab.CustomTabItem; 5 | import net.jacobpeterson.view.albumcovertab.ThumbnailTabItem; 6 | import org.eclipse.swt.SWT; 7 | import org.eclipse.swt.layout.FormAttachment; 8 | import org.eclipse.swt.layout.FormData; 9 | import org.eclipse.swt.layout.FormLayout; 10 | import org.eclipse.swt.widgets.Button; 11 | import org.eclipse.swt.widgets.Composite; 12 | import org.eclipse.swt.widgets.TabFolder; 13 | 14 | public class EditAlbumCoverComposite { 15 | 16 | private ApplicationShell applicationShell; 17 | private Composite composite; 18 | 19 | private TabFolder tabFolder; 20 | private AlbumCoverTabItem albumCoverTabItem; 21 | private ThumbnailTabItem thumbnailTabItem; 22 | private CustomTabItem customTabItem; 23 | private Button removeButton; 24 | private Button doneButton; 25 | 26 | public EditAlbumCoverComposite(ApplicationShell applicationShell) { 27 | this.applicationShell = applicationShell; 28 | this.composite = new Composite(applicationShell.getShell(), SWT.NONE); 29 | 30 | this.tabFolder = new TabFolder(composite, SWT.TOP); 31 | this.albumCoverTabItem = new AlbumCoverTabItem(tabFolder); 32 | this.thumbnailTabItem = new ThumbnailTabItem(tabFolder); 33 | this.customTabItem = new CustomTabItem(tabFolder); 34 | this.removeButton = new Button(composite, SWT.PUSH); 35 | this.doneButton = new Button(composite, SWT.PUSH); 36 | } 37 | 38 | public void setup() { 39 | this.setupWidgets(); 40 | this.setupLayout(); 41 | this.setupListeners(); 42 | } 43 | 44 | private void setupWidgets() { 45 | this.albumCoverTabItem.setup(); 46 | this.thumbnailTabItem.setup(); 47 | this.customTabItem.setup(); 48 | 49 | this.removeButton.setText("Remove"); 50 | this.doneButton.setText("Done"); 51 | } 52 | 53 | private void setupLayout() { 54 | // External Layout 55 | 56 | ApplicationShellContents contents = applicationShell.getContents(); 57 | 58 | FormData externalFormData = new FormData(); 59 | externalFormData.top = new FormAttachment(contents.getDownloadGroup(), 0, SWT.TOP); 60 | externalFormData.left = new FormAttachment(0); 61 | externalFormData.bottom = new FormAttachment(100); 62 | externalFormData.right = new FormAttachment(contents.getDownloadGroup(), 0, SWT.LEFT); 63 | this.composite.setLayoutData(externalFormData); 64 | this.composite.moveAbove(contents.getDownloadList()); 65 | 66 | // Internal Layout 67 | 68 | FormLayout layout = new FormLayout(); 69 | this.composite.setLayout(layout); 70 | 71 | FormData internalFormData = new FormData(); 72 | internalFormData.top = new FormAttachment(0); 73 | internalFormData.left = new FormAttachment(0); 74 | internalFormData.bottom = new FormAttachment(doneButton, 5, SWT.TOP); 75 | internalFormData.right = new FormAttachment(100); 76 | this.tabFolder.setLayoutData(internalFormData); 77 | 78 | internalFormData = new FormData(); 79 | internalFormData.bottom = new FormAttachment(100); 80 | internalFormData.right = new FormAttachment(doneButton, 5, SWT.LEFT); 81 | this.removeButton.setLayoutData(internalFormData); 82 | 83 | internalFormData = new FormData(); 84 | internalFormData.bottom = new FormAttachment(100); 85 | internalFormData.right = new FormAttachment(100); 86 | this.doneButton.setLayoutData(internalFormData); 87 | 88 | this.composite.layout(); 89 | } 90 | 91 | private void setupListeners() { 92 | 93 | } 94 | 95 | public Composite getComposite() { 96 | return composite; 97 | } 98 | 99 | public TabFolder getTabFolder() { 100 | return tabFolder; 101 | } 102 | 103 | public AlbumCoverTabItem getAlbumCoverTabItem() { 104 | return albumCoverTabItem; 105 | } 106 | 107 | public ThumbnailTabItem getThumbnailTabItem() { 108 | return thumbnailTabItem; 109 | } 110 | 111 | public CustomTabItem getCustomTabItem() { 112 | return customTabItem; 113 | } 114 | 115 | public Button getRemoveButton() { 116 | return removeButton; 117 | } 118 | 119 | public Button getDoneButton() { 120 | return doneButton; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 37 | 38 | 45 | 46 | 47 | 48 |

Nothing to see here yet!

49 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/albumcovertab/AlbumCoverTabItem.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.albumcovertab; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.graphics.Color; 5 | import org.eclipse.swt.layout.FormAttachment; 6 | import org.eclipse.swt.layout.FormData; 7 | import org.eclipse.swt.layout.FormLayout; 8 | import org.eclipse.swt.widgets.*; 9 | 10 | public class AlbumCoverTabItem { 11 | 12 | private TabFolder tabFolder; 13 | 14 | private TabItem tabItem; 15 | private Composite composite; 16 | private Label explanationLabel; 17 | private Label actualTitleLabel; 18 | private Text actualTitleText; 19 | private Label actualArtistLabel; 20 | private Text actualArtistText; 21 | private Label actualExplanationLabel; 22 | private Button refreshButton; 23 | 24 | public AlbumCoverTabItem(TabFolder tabFolder) { 25 | this.tabFolder = tabFolder; 26 | 27 | this.tabItem = new TabItem(tabFolder, SWT.NONE); 28 | this.composite = new Composite(tabFolder, SWT.NONE); 29 | this.explanationLabel = new Label(composite, SWT.WRAP | SWT.CENTER); 30 | this.actualTitleLabel = new Label(composite, SWT.NONE); 31 | this.actualTitleText = new Text(composite, SWT.SINGLE | SWT.BORDER); 32 | this.actualArtistLabel = new Label(composite, SWT.NONE); 33 | this.actualArtistText = new Text(composite, SWT.SINGLE | SWT.BORDER); 34 | this.actualExplanationLabel = new Label(composite, SWT.WRAP | SWT.CENTER); 35 | this.refreshButton = new Button(composite, SWT.PUSH); 36 | } 37 | 38 | public void setup() { 39 | this.setupWidgets(); 40 | this.setupLayout(); 41 | } 42 | 43 | private void setupWidgets() { 44 | this.tabItem.setText("Album Cover"); 45 | this.tabItem.setControl(composite); 46 | 47 | this.explanationLabel.setText("This program uses an external web service to load the album covers of various songs. " + 48 | "Sometimes, depending on the title and artist name format, the album cover is not loaded due to varying titles/artists of the same song/artist. " + 49 | "If the album cover fails to load, try changing the below words to something the web service database may have indexed."); 50 | this.actualTitleLabel.setText("Actual Title"); 51 | this.actualArtistLabel.setText("Actual Artist"); 52 | this.actualExplanationLabel.setText("Generally, removing words like \'remix\' helps. Click the button below to re-attempt fetching the album cover."); 53 | this.refreshButton.setText("Refresh"); 54 | 55 | Color explanationColor = new Color(tabFolder.getDisplay(), 127, 127, 127); 56 | this.explanationLabel.setForeground(explanationColor); 57 | this.actualExplanationLabel.setForeground(explanationColor); 58 | } 59 | 60 | private void setupLayout() { 61 | FormLayout layout = new FormLayout(); 62 | layout.marginTop = 10; 63 | layout.marginLeft = 10; 64 | layout.marginBottom = 10; 65 | layout.marginRight = 10; 66 | this.composite.setLayout(layout); 67 | 68 | FormData formData = new FormData(); 69 | formData.top = new FormAttachment(0); 70 | formData.left = new FormAttachment(0); 71 | formData.right = new FormAttachment(100); 72 | this.explanationLabel.setLayoutData(formData); 73 | 74 | formData = new FormData(); 75 | formData.top = new FormAttachment(actualTitleText, 0, SWT.CENTER); 76 | formData.right = new FormAttachment(actualArtistLabel, 0, SWT.RIGHT); 77 | this.actualTitleLabel.setLayoutData(formData); 78 | 79 | formData = new FormData(); 80 | formData.top = new FormAttachment(explanationLabel, 10, SWT.BOTTOM); 81 | formData.left = new FormAttachment(actualTitleLabel, 10, SWT.RIGHT); 82 | formData.right = new FormAttachment(100); 83 | this.actualTitleText.setLayoutData(formData); 84 | 85 | formData = new FormData(); 86 | formData.top = new FormAttachment(actualArtistText, 0, SWT.CENTER); 87 | formData.left = new FormAttachment(0); 88 | this.actualArtistLabel.setLayoutData(formData); 89 | 90 | formData = new FormData(); 91 | formData.top = new FormAttachment(actualTitleText, 10, SWT.BOTTOM); 92 | formData.left = new FormAttachment(actualArtistLabel, 10, SWT.RIGHT); 93 | formData.right = new FormAttachment(100); 94 | this.actualArtistText.setLayoutData(formData); 95 | 96 | formData = new FormData(); 97 | formData.top = new FormAttachment(actualArtistText, 10, SWT.BOTTOM); 98 | formData.left = new FormAttachment(0); 99 | formData.right = new FormAttachment(100); 100 | this.actualExplanationLabel.setLayoutData(formData); 101 | 102 | formData = new FormData(); 103 | formData.top = new FormAttachment(actualExplanationLabel, 10, SWT.BOTTOM); 104 | formData.right = new FormAttachment(100); 105 | this.refreshButton.setLayoutData(formData); 106 | 107 | this.composite.layout(); 108 | } 109 | 110 | public TabItem getTabItem() { 111 | return tabItem; 112 | } 113 | 114 | public Text getActualTitleText() { 115 | return actualTitleText; 116 | } 117 | 118 | public Text getActualArtistText() { 119 | return actualArtistText; 120 | } 121 | 122 | public Button getRefreshButton() { 123 | return refreshButton; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/albumcovertab/CustomTabItem.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.albumcovertab; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.graphics.Color; 5 | import org.eclipse.swt.layout.FormAttachment; 6 | import org.eclipse.swt.layout.FormData; 7 | import org.eclipse.swt.layout.FormLayout; 8 | import org.eclipse.swt.widgets.*; 9 | 10 | public class CustomTabItem { 11 | 12 | private TabFolder tabFolder; 13 | 14 | private TabItem tabItem; 15 | private Composite composite; 16 | private Button chooseFileButton; 17 | private Label fileExplanationLabel; 18 | private Label orLabel; 19 | private Label orSeparatorLabel; 20 | private Label imageURLLabel; 21 | private Text imageURLText; 22 | private Label imageExplanationLabel; 23 | private Button loadImageURLButton; 24 | 25 | public CustomTabItem(TabFolder tabFolder) { 26 | this.tabFolder = tabFolder; 27 | 28 | this.tabItem = new TabItem(tabFolder, SWT.NONE); 29 | this.composite = new Composite(tabFolder, SWT.NONE); 30 | this.chooseFileButton = new Button(composite, SWT.PUSH); 31 | this.fileExplanationLabel = new Label(composite, SWT.WRAP | SWT.CENTER); 32 | this.orLabel = new Label(composite, SWT.CENTER); 33 | this.orSeparatorLabel = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); 34 | this.imageURLLabel = new Label(composite, SWT.NONE); 35 | this.imageURLText = new Text(composite, SWT.SINGLE | SWT.BORDER); 36 | this.imageExplanationLabel = new Label(composite, SWT.WRAP | SWT.CENTER); 37 | this.loadImageURLButton = new Button(composite, SWT.PUSH); 38 | } 39 | 40 | public void setup() { 41 | this.setupWidgets(); 42 | this.setupLayout(); 43 | } 44 | 45 | private void setupWidgets() { 46 | this.tabItem.setText("Custom"); 47 | this.tabItem.setControl(composite); 48 | 49 | this.chooseFileButton.setText("Choose File"); 50 | this.fileExplanationLabel.setText("Choose and image from your computer using the button above or " + 51 | "drag n' drop an image onto the button to set the custom album cover."); 52 | this.orLabel.setText("OR"); 53 | 54 | Color grayColor = new Color(tabFolder.getDisplay(), 127, 127, 127); 55 | 56 | this.fileExplanationLabel.setForeground(grayColor); 57 | this.orLabel.setBackground(new Color(tabFolder.getDisplay(), 223, 222, 223)); // Color of group container background. 58 | 59 | this.imageURLLabel.setText("Image URL"); 60 | this.imageExplanationLabel.setText("Paste an image URL above to set the custom album cover as an image from the internet. " + 61 | "This URL usually ends with a \'.jpg\' or \'.png\'"); 62 | 63 | this.imageExplanationLabel.setForeground(grayColor); 64 | 65 | this.loadImageURLButton.setText("Load Image URL"); 66 | } 67 | 68 | private void setupLayout() { 69 | FormLayout layout = new FormLayout(); 70 | layout.marginTop = 10; 71 | layout.marginLeft = 10; 72 | layout.marginBottom = 10; 73 | layout.marginRight = 10; 74 | this.composite.setLayout(layout); 75 | 76 | FormData formData = new FormData(); 77 | formData.top = new FormAttachment(0); 78 | formData.left = new FormAttachment(fileExplanationLabel, 0, SWT.CENTER); 79 | this.chooseFileButton.setLayoutData(formData); 80 | 81 | formData = new FormData(); 82 | formData.top = new FormAttachment(chooseFileButton, 0, SWT.BOTTOM); 83 | formData.left = new FormAttachment(0); 84 | formData.right = new FormAttachment(100); 85 | this.fileExplanationLabel.setLayoutData(formData); 86 | 87 | formData = new FormData(); 88 | formData.top = new FormAttachment(fileExplanationLabel, 15, SWT.BOTTOM); 89 | formData.left = new FormAttachment(fileExplanationLabel, 0, SWT.CENTER); 90 | this.orLabel.setLayoutData(formData); 91 | 92 | formData = new FormData(); 93 | formData.top = new FormAttachment(orLabel, 0, SWT.CENTER); 94 | formData.left = new FormAttachment(2); 95 | formData.right = new FormAttachment(98); 96 | this.orSeparatorLabel.setLayoutData(formData); 97 | 98 | formData = new FormData(); 99 | formData.top = new FormAttachment(imageURLText, 0, SWT.CENTER); 100 | formData.left = new FormAttachment(0); 101 | this.imageURLLabel.setLayoutData(formData); 102 | 103 | formData = new FormData(); 104 | formData.top = new FormAttachment(orLabel, 15, SWT.BOTTOM); 105 | formData.left = new FormAttachment(imageURLLabel, 10, SWT.RIGHT); 106 | formData.right = new FormAttachment(100); 107 | this.imageURLText.setLayoutData(formData); 108 | 109 | formData = new FormData(); 110 | formData.top = new FormAttachment(imageURLText, 10, SWT.BOTTOM); 111 | formData.left = new FormAttachment(0); 112 | formData.right = new FormAttachment(100); 113 | this.imageExplanationLabel.setLayoutData(formData); 114 | 115 | formData = new FormData(); 116 | formData.top = new FormAttachment(imageExplanationLabel, 5, SWT.BOTTOM); 117 | formData.right = new FormAttachment(100); 118 | this.loadImageURLButton.setLayoutData(formData); 119 | 120 | this.composite.layout(); 121 | } 122 | 123 | public TabItem getTabItem() { 124 | return tabItem; 125 | } 126 | 127 | public Button getChooseFileButton() { 128 | return chooseFileButton; 129 | } 130 | 131 | public Text getImageURLText() { 132 | return imageURLText; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/ApplicationShellContents.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view; 2 | 3 | import net.jacobpeterson.view.groupcomposite.EditSongComposite; 4 | import org.eclipse.swt.SWT; 5 | import org.eclipse.swt.graphics.Color; 6 | import org.eclipse.swt.graphics.Font; 7 | import org.eclipse.swt.graphics.FontData; 8 | import org.eclipse.swt.layout.FormAttachment; 9 | import org.eclipse.swt.layout.FormData; 10 | import org.eclipse.swt.layout.FormLayout; 11 | import org.eclipse.swt.widgets.*; 12 | 13 | public class ApplicationShellContents { 14 | 15 | private ApplicationShell applicationShell; 16 | private Shell shell; 17 | 18 | private Text urlText; 19 | private Label downloadAsLabel; 20 | private Combo downloadAsCombo; 21 | private Label leftColumnLabel; 22 | private List downloadList; 23 | private Group downloadGroup; 24 | private EditAlbumCoverComposite editAlbumCoverComposite; 25 | 26 | public final String DOWNLOADS_COLUMN_LABEL = "Downloads"; 27 | public final String EDIT_ALBUM_COVER_COLUMN_LABEL = "Edit Album Cover"; 28 | public final String[] DOWNLOAD_AS_LIST = {"Video", " mp4", "Audio", " mp3", "Song (Adds album art, etc.)", " mp3"}; 29 | 30 | public ApplicationShellContents(ApplicationShell applicationShell) { 31 | this.applicationShell = applicationShell; 32 | this.shell = applicationShell.getShell(); 33 | 34 | this.urlText = new Text(shell, SWT.SINGLE | SWT.BORDER); 35 | this.downloadAsLabel = new Label(shell, SWT.NONE); 36 | this.downloadAsCombo = new Combo(shell, SWT.READ_ONLY); 37 | this.leftColumnLabel = new Label(shell, SWT.NONE); 38 | this.downloadList = new List(shell, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); 39 | this.downloadGroup = new Group(shell, SWT.NONE); 40 | this.editAlbumCoverComposite = new EditAlbumCoverComposite(applicationShell); 41 | } 42 | 43 | public void setup() { 44 | this.setupWidgets(); 45 | this.setupLayout(); 46 | this.setupListeners(); 47 | } 48 | 49 | private void setupWidgets() { 50 | this.urlText.setMessage("Enter YouTube, Soundcloud, etc. URL"); 51 | this.downloadAsLabel.setText("Download as:"); 52 | this.downloadAsCombo.setItems(DOWNLOAD_AS_LIST); 53 | this.downloadAsCombo.select(2); 54 | 55 | this.leftColumnLabel.setText(DOWNLOADS_COLUMN_LABEL); 56 | FontData fontData = leftColumnLabel.getFont().getFontData()[0]; 57 | fontData.setHeight(11); 58 | this.leftColumnLabel.setFont(new Font(shell.getDisplay(), fontData)); 59 | this.leftColumnLabel.setForeground(new Color(shell.getDisplay(), 60, 60, 60)); // gray-black like the downloadGroup font 60 | 61 | this.downloadGroup.setText("Download Info/Edit"); 62 | EditSongComposite composite = new EditSongComposite(downloadGroup); 63 | composite.setup(); 64 | this.downloadGroup.layout(); 65 | } 66 | 67 | private void setupLayout() { 68 | FormLayout layout = new FormLayout(); 69 | layout.marginTop = 20; 70 | layout.marginBottom = 20; 71 | layout.marginLeft = 20; 72 | layout.marginRight = 20; 73 | this.shell.setLayout(layout); 74 | 75 | FormData formData = new FormData(); 76 | formData.top = new FormAttachment(0); 77 | formData.left = new FormAttachment(0); 78 | formData.right = new FormAttachment(downloadAsLabel, -8, SWT.LEFT); 79 | this.urlText.setLayoutData(formData); 80 | 81 | formData = new FormData(); 82 | formData.top = new FormAttachment(urlText, 0, SWT.CENTER); 83 | formData.right = new FormAttachment(downloadAsCombo, 0, SWT.LEFT); 84 | this.downloadAsLabel.setLayoutData(formData); 85 | 86 | formData = new FormData(); 87 | formData.top = new FormAttachment(urlText, 0, SWT.CENTER); 88 | formData.right = new FormAttachment(100); 89 | formData.width = 150; 90 | this.downloadAsCombo.setLayoutData(formData); 91 | 92 | formData = new FormData(); 93 | formData.top = new FormAttachment(urlText, 16, SWT.BOTTOM); 94 | formData.left = new FormAttachment(0); 95 | this.leftColumnLabel.setLayoutData(formData); 96 | 97 | formData = new FormData(); 98 | formData.top = new FormAttachment(leftColumnLabel, 2, SWT.BOTTOM); 99 | formData.left = new FormAttachment(0); 100 | formData.bottom = new FormAttachment(100); 101 | formData.right = new FormAttachment(37); 102 | this.downloadList.setLayoutData(formData); 103 | 104 | formData = new FormData(); 105 | formData.top = new FormAttachment(leftColumnLabel, 0, SWT.TOP); 106 | formData.left = new FormAttachment(downloadList, 10, SWT.RIGHT); 107 | formData.bottom = new FormAttachment(100); 108 | formData.right = new FormAttachment(100); 109 | this.downloadGroup.setLayoutData(formData); 110 | 111 | // DownloadGroup layout 112 | this.downloadGroup.setLayout(new FormLayout()); 113 | } 114 | 115 | private void setupListeners() { 116 | 117 | } 118 | 119 | public Text getUrlText() { 120 | return urlText; 121 | } 122 | 123 | public Label getDownloadAsLabel() { 124 | return downloadAsLabel; 125 | } 126 | 127 | public Combo getDownloadAsCombo() { 128 | return downloadAsCombo; 129 | } 130 | 131 | public Label getLeftColumnLabel() { 132 | return leftColumnLabel; 133 | } 134 | 135 | public List getDownloadList() { 136 | return downloadList; 137 | } 138 | 139 | public Group getDownloadGroup() { 140 | return downloadGroup; 141 | } 142 | 143 | public EditAlbumCoverComposite getEditAlbumCoverComposite() { 144 | return editAlbumCoverComposite; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/view/groupcomposite/EditSongComposite.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.view.groupcomposite; 2 | 3 | import net.jacobpeterson.view.groupcomposite.song.AlbumCoverCanvas; 4 | import org.eclipse.swt.SWT; 5 | import org.eclipse.swt.graphics.Color; 6 | import org.eclipse.swt.graphics.Font; 7 | import org.eclipse.swt.graphics.FontData; 8 | import org.eclipse.swt.internal.cocoa.NSButton; 9 | import org.eclipse.swt.internal.cocoa.NSImage; 10 | import org.eclipse.swt.internal.cocoa.NSSize; 11 | import org.eclipse.swt.internal.cocoa.NSString; 12 | import org.eclipse.swt.layout.FormAttachment; 13 | import org.eclipse.swt.layout.FormData; 14 | import org.eclipse.swt.layout.FormLayout; 15 | import org.eclipse.swt.widgets.*; 16 | 17 | public class EditSongComposite { 18 | 19 | private Composite composite; 20 | 21 | private Label titleLabel; 22 | private Text titleText; 23 | private Label artistLabel; 24 | private Text artistText; 25 | private Label albumLabel; 26 | private Text albumText; 27 | private Label genreLabel; 28 | private Combo genreCombo; 29 | private Label albumCoverLabel; 30 | private AlbumCoverCanvas albumCoverCanvas; 31 | private Label albumCoverStatusLabel; 32 | private Button editAlbumCoverButton; 33 | private ProgressBar progressBar; 34 | private Label progressLabel; 35 | private Button cancelButton; 36 | private Button saveButton; 37 | 38 | public EditSongComposite(Group groupContainer) { 39 | this.composite = new Composite(groupContainer, SWT.NONE); 40 | 41 | this.titleLabel = new Label(composite, SWT.NONE); 42 | this.titleText = new Text(composite, SWT.SINGLE | SWT.BORDER); 43 | this.artistLabel = new Label(composite, SWT.NONE); 44 | this.artistText = new Text(composite, SWT.SINGLE | SWT.BORDER); 45 | this.albumLabel = new Label(composite, SWT.NONE); 46 | this.albumText = new Text(composite, SWT.SINGLE | SWT.BORDER); 47 | this.genreLabel = new Label(composite, SWT.NONE); 48 | this.genreCombo = new Combo(composite, SWT.SIMPLE); 49 | this.albumCoverLabel = new Label(composite, SWT.NONE); 50 | this.albumCoverCanvas = new AlbumCoverCanvas(composite); 51 | this.albumCoverStatusLabel = new Label(composite, SWT.CENTER | SWT.WRAP); 52 | this.editAlbumCoverButton = new Button(composite, SWT.FLAT); 53 | this.progressBar = new ProgressBar(composite, SWT.SMOOTH | SWT.HORIZONTAL); 54 | this.progressLabel = new Label(composite, SWT.NONE); 55 | this.cancelButton = new Button(composite, SWT.FLAT); 56 | this.saveButton = new Button(composite, SWT.PUSH); 57 | } 58 | 59 | public void setup() { 60 | this.setupWidgets(); 61 | this.setupLayout(); 62 | 63 | // Test code below 64 | 65 | // this.editAlbumCoverButton.addSelectionListener(new SelectionAdapter() { 66 | // @Override 67 | // public void widgetSelected(SelectionEvent selectionEvent) { 68 | // FormData data =(FormData) editAlbumCoverButton.getLayoutData(); 69 | // data.width = SWT.DEFAULT; 70 | // editAlbumCoverButton.setText("DLKJDDDDDddddddd"); 71 | // editAlbumCoverButton.requestLayout(); 72 | // } 73 | // }); 74 | 75 | 76 | // DropTarget dt = new DropTarget(albumCoverCanvas, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK); 77 | // dt.setTransfer(FileTransfer.getInstance()); 78 | // dt.addDropListener(new DropTargetAdapter() { 79 | // public void drop(DropTargetEvent event) { 80 | // String fileList[] = null; 81 | // FileTransfer ft = FileTransfer.getInstance(); 82 | // if (event.data == null) { // no data to copy, indicate failure in event.detail 83 | // event.detail = DND.DROP_NONE; 84 | // return; 85 | // } 86 | // if (ft.isSupportedType(event.currentDataType)) { 87 | // fileList = (String[]) event.data; 88 | // } 89 | // System.out.println(Arrays.toString(fileList)); 90 | // } 91 | // 92 | // @Override 93 | // public void dropAccept(DropTargetEvent event) { 94 | // Object object = FileTransfer.getInstance().nativeToJava(event.currentDataType); 95 | // if (object instanceof String[]) { 96 | // String[] strs = (String[]) object; 97 | // System.out.println("dropAccept " + strs[0]); 98 | // event.detail = DND.DROP_COPY; 99 | // } 100 | // event.detail = DND.DROP_COPY; 101 | // } 102 | // 103 | // @Override 104 | // public void dragEnter(DropTargetEvent event) { 105 | // event.detail = DND.DROP_COPY; 106 | // } 107 | // 108 | // @Override 109 | // public void dragOver(DropTargetEvent dropTargetEvent) { 110 | // dropTargetEvent.detail = DND.DROP_COPY; 111 | // dropTargetEvent.feedback = DND.FEEDBACK_SELECT; 112 | // } 113 | // }); 114 | // 115 | // albumCoverCanvas.addMouseWheelListener(new MouseWheelListener() { 116 | // @Override 117 | // public void mouseScrolled(MouseEvent mouseEvent) { 118 | // System.out.println(mouseEvent.count); 119 | // } 120 | // }); 121 | } 122 | 123 | private void setupWidgets() { 124 | this.titleLabel.setText("Title"); 125 | this.artistLabel.setText("Artist"); 126 | this.albumLabel.setText("Album"); 127 | this.genreLabel.setText("Genre"); 128 | this.genreCombo.setItems( 129 | "Alternative", 130 | "Blues", 131 | "Classical", 132 | "Country", 133 | "Dance", 134 | "Electronic", 135 | "Hip-Hop/Rap", 136 | "Indie", 137 | "Inspirational", 138 | "Instrumental", 139 | "Jazz", 140 | "Latino", 141 | "Opera", 142 | "Pop", 143 | "R&B/Soul", 144 | "Reggae", 145 | "Rock", 146 | "Soundtrack", 147 | "Vocal", 148 | "World"); 149 | 150 | this.albumCoverCanvas.setup(); 151 | 152 | this.albumCoverLabel.setText("Album Cover"); 153 | this.albumCoverStatusLabel.setText("\n\n"); // Reserve 2 lines for centering vertically in setLayout() 154 | this.albumCoverStatusLabel.setForeground(new Color(composite.getDisplay(), 255, 0, 0)); 155 | this.albumCoverStatusLabel.setVisible(false); 156 | 157 | this.editAlbumCoverButton.setText("Edit"); 158 | this.saveButton.setText("Save"); 159 | 160 | NSButton nsEditAlbumCoverButton = (NSButton) editAlbumCoverButton.view; 161 | nsEditAlbumCoverButton.setBezelStyle(15); // Inline button bezel style 162 | FontData albumFontData = editAlbumCoverButton.getFont().getFontData()[0]; 163 | albumFontData.setHeight(11); 164 | albumFontData.setStyle(SWT.BOLD); 165 | editAlbumCoverButton.setForeground(new Color(composite.getDisplay(), 255, 255, 255)); 166 | editAlbumCoverButton.setFont(new Font(composite.getDisplay(), albumFontData)); 167 | 168 | this.progressLabel.setText("Status"); 169 | FontData fontData = progressLabel.getFont().getFontData()[0]; 170 | fontData.setHeight(11); 171 | this.progressLabel.setFont(new Font(composite.getDisplay(), fontData)); 172 | 173 | NSButton nsCancelButton = (NSButton) cancelButton.view; 174 | nsCancelButton.setBezelStyle(15); // Inline button bezel style 175 | NSImage stopProgressImage = NSImage.imageNamed(NSString.stringWith("NSStopProgressTemplate")); 176 | NSSize imageSize = new NSSize(); 177 | imageSize.height = 8; 178 | imageSize.width = 8; 179 | stopProgressImage.setSize(imageSize); 180 | nsCancelButton.setImage(stopProgressImage); 181 | } 182 | 183 | private void setupLayout() { 184 | // External Layout 185 | 186 | FormData externalFormData = new FormData(); 187 | externalFormData.top = new FormAttachment(0); 188 | externalFormData.left = new FormAttachment(0); 189 | externalFormData.bottom = new FormAttachment(100); 190 | externalFormData.right = new FormAttachment(100); 191 | this.composite.setLayoutData(externalFormData); 192 | 193 | // Internal Layout 194 | 195 | FormLayout layout = new FormLayout(); 196 | layout.marginTop = 10; 197 | layout.marginBottom = 10; 198 | layout.marginLeft = 10; 199 | layout.marginRight = 10; 200 | this.composite.setLayout(layout); 201 | 202 | FormData formData = new FormData(); 203 | formData.top = new FormAttachment(titleText, 0, SWT.CENTER); 204 | formData.right = new FormAttachment(albumCoverLabel, 0, SWT.RIGHT); 205 | this.titleLabel.setLayoutData(formData); 206 | 207 | formData = new FormData(); 208 | formData.top = new FormAttachment(0); 209 | formData.left = new FormAttachment(titleLabel, 10, SWT.RIGHT); 210 | formData.right = new FormAttachment(100); 211 | this.titleText.setLayoutData(formData); 212 | 213 | formData = new FormData(); 214 | formData.top = new FormAttachment(artistText, 0, SWT.CENTER); 215 | formData.right = new FormAttachment(albumCoverLabel, 0, SWT.RIGHT); 216 | this.artistLabel.setLayoutData(formData); 217 | 218 | formData = new FormData(); 219 | formData.top = new FormAttachment(titleText, 11, SWT.BOTTOM); 220 | formData.left = new FormAttachment(titleText, 0, SWT.LEFT); 221 | formData.right = new FormAttachment(100); 222 | this.artistText.setLayoutData(formData); 223 | 224 | formData = new FormData(); 225 | formData.top = new FormAttachment(albumText, 0, SWT.CENTER); 226 | formData.right = new FormAttachment(albumCoverLabel, 0, SWT.RIGHT); 227 | this.albumLabel.setLayoutData(formData); 228 | 229 | formData = new FormData(); 230 | formData.top = new FormAttachment(artistText, 11, SWT.BOTTOM); 231 | formData.left = new FormAttachment(artistText, 0, SWT.LEFT); 232 | formData.right = new FormAttachment(100); 233 | this.albumText.setLayoutData(formData); 234 | 235 | formData = new FormData(); 236 | formData.top = new FormAttachment(genreCombo, 0, SWT.CENTER); 237 | formData.right = new FormAttachment(albumCoverLabel, 0, SWT.RIGHT); 238 | this.genreLabel.setLayoutData(formData); 239 | 240 | formData = new FormData(); 241 | formData.top = new FormAttachment(albumText, 11, SWT.BOTTOM); 242 | formData.left = new FormAttachment(albumText, 0, SWT.LEFT); 243 | formData.right = new FormAttachment(100); 244 | this.genreCombo.setLayoutData(formData); 245 | 246 | formData = new FormData(); 247 | formData.top = new FormAttachment(albumCoverCanvas.getCanvas(), 0, SWT.CENTER); 248 | formData.left = new FormAttachment(0); 249 | this.albumCoverLabel.setLayoutData(formData); 250 | 251 | formData = new FormData(); 252 | formData.top = new FormAttachment(genreCombo, 11, SWT.BOTTOM); 253 | formData.left = new FormAttachment(genreCombo, 0, SWT.LEFT); 254 | formData.bottom = new FormAttachment(progressBar, -20, SWT.TOP); 255 | formData.width = 300; 256 | this.albumCoverCanvas.getCanvas().setLayoutData(formData); 257 | 258 | formData = new FormData(); 259 | formData.top = new FormAttachment(albumCoverCanvas.getCanvas(), 0, SWT.CENTER); 260 | formData.left = new FormAttachment(albumCoverCanvas.getCanvas(), 0, SWT.LEFT); 261 | formData.right = new FormAttachment(albumCoverCanvas.getCanvas(), 0, SWT.RIGHT); 262 | this.albumCoverStatusLabel.setLayoutData(formData); 263 | 264 | formData = new FormData(); 265 | formData.top = new FormAttachment(albumCoverLabel, 3, SWT.BOTTOM); 266 | formData.left = new FormAttachment(albumCoverLabel, 0, SWT.CENTER); 267 | formData.width = 40; 268 | this.editAlbumCoverButton.setLayoutData(formData); 269 | 270 | formData = new FormData(); 271 | formData.left = new FormAttachment(0); 272 | formData.bottom = new FormAttachment(progressLabel, 8, SWT.TOP); 273 | formData.width = 100; 274 | this.progressBar.setLayoutData(formData); 275 | 276 | formData = new FormData(); 277 | formData.bottom = new FormAttachment(progressLabel, 0, SWT.TOP); 278 | formData.left = new FormAttachment(0); 279 | formData.width = 150; 280 | this.progressBar.setLayoutData(formData); 281 | 282 | formData = new FormData(); 283 | formData.bottom = new FormAttachment(100); 284 | formData.left = new FormAttachment(0); 285 | this.progressLabel.setLayoutData(formData); 286 | 287 | formData = new FormData(); 288 | formData.top = new FormAttachment(progressBar, 0, SWT.CENTER); 289 | formData.left = new FormAttachment(progressBar, 5, SWT.RIGHT); 290 | this.cancelButton.setLayoutData(formData); 291 | 292 | formData = new FormData(); 293 | formData.bottom = new FormAttachment(100); 294 | formData.right = new FormAttachment(100); 295 | this.saveButton.setLayoutData(formData); 296 | 297 | this.composite.layout(); 298 | } 299 | 300 | public Composite getComposite() { 301 | return composite; 302 | } 303 | 304 | public Label getTitleLabel() { 305 | return titleLabel; 306 | } 307 | 308 | public Text getTitleText() { 309 | return titleText; 310 | } 311 | 312 | public Label getArtistLabel() { 313 | return artistLabel; 314 | } 315 | 316 | public Text getArtistText() { 317 | return artistText; 318 | } 319 | 320 | public Label getAlbumLabel() { 321 | return albumLabel; 322 | } 323 | 324 | public Text getAlbumText() { 325 | return albumText; 326 | } 327 | 328 | public Label getGenreLabel() { 329 | return genreLabel; 330 | } 331 | 332 | public Combo getGenreCombo() { 333 | return genreCombo; 334 | } 335 | 336 | public Label getAlbumCoverLabel() { 337 | return albumCoverLabel; 338 | } 339 | 340 | public AlbumCoverCanvas getAlbumCoverCanvas() { 341 | return albumCoverCanvas; 342 | } 343 | 344 | public Button getEditAlbumCoverButton() { 345 | return editAlbumCoverButton; 346 | } 347 | 348 | public ProgressBar getProgressBar() { 349 | return progressBar; 350 | } 351 | 352 | public Label getProgressLabel() { 353 | return progressLabel; 354 | } 355 | 356 | public Button getCancelButton() { 357 | return cancelButton; 358 | } 359 | 360 | public Button getSaveButton() { 361 | return saveButton; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /docs/assets/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | 7 | body { 8 | margin: 0 9 | } 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | menu, 21 | nav, 22 | section, 23 | summary { 24 | display: block 25 | } 26 | 27 | audio, 28 | canvas, 29 | progress, 30 | video { 31 | display: inline-block; 32 | vertical-align: baseline 33 | } 34 | 35 | audio:not([controls]) { 36 | display: none; 37 | height: 0 38 | } 39 | 40 | [hidden], 41 | template { 42 | display: none 43 | } 44 | 45 | a { 46 | background-color: transparent; 47 | } 48 | 49 | a:active, 50 | a:hover { 51 | outline: 0; 52 | } 53 | 54 | abbr[title] { 55 | border-bottom: 1px dotted 56 | } 57 | 58 | b, 59 | strong { 60 | font-weight: bold 61 | } 62 | 63 | dfn { 64 | font-style: italic 65 | } 66 | 67 | h1 { 68 | font-size: 2em; 69 | margin: 0.67em 0 70 | } 71 | 72 | mark { 73 | background: #ff0; 74 | color: #000 75 | } 76 | 77 | small { 78 | font-size: 80% 79 | } 80 | 81 | sub, 82 | sup { 83 | font-size: 75%; 84 | line-height: 0; 85 | position: relative; 86 | vertical-align: baseline 87 | } 88 | 89 | sup { 90 | top: -0.5em 91 | } 92 | 93 | sub { 94 | bottom: -0.25em 95 | } 96 | 97 | img { 98 | border: 0 99 | } 100 | 101 | #michaelscott { 102 | display: block; 103 | margin: 0 auto; 104 | } 105 | 106 | svg:not(:root) { 107 | overflow: hidden 108 | } 109 | 110 | figure { 111 | margin: 1em 40px 112 | } 113 | 114 | hr { 115 | box-sizing: content-box; 116 | height: 0 117 | } 118 | 119 | pre { 120 | overflow: auto 121 | } 122 | 123 | code, 124 | kbd, 125 | pre, 126 | samp { 127 | font-family: monospace, monospace; 128 | font-size: 1em 129 | } 130 | 131 | button, 132 | input, 133 | optgroup, 134 | select, 135 | textarea { 136 | color: inherit; 137 | font: inherit; 138 | margin: 0 139 | } 140 | 141 | button { 142 | overflow: visible 143 | } 144 | 145 | button, 146 | select { 147 | text-transform: none 148 | } 149 | 150 | button, 151 | html input[type="button"], 152 | input[type="reset"], 153 | input[type="submit"] { 154 | -webkit-appearance: button; 155 | cursor: pointer 156 | } 157 | 158 | button[disabled], 159 | html input[disabled] { 160 | cursor: default 161 | } 162 | 163 | button::-moz-focus-inner, 164 | input::-moz-focus-inner { 165 | border: 0; 166 | padding: 0 167 | } 168 | 169 | .osicon { 170 | position: relative; 171 | width: 1.4em; 172 | height: 1.2em; 173 | right: 0.3em; 174 | top: 0.1em; 175 | } 176 | 177 | input { 178 | line-height: normal 179 | } 180 | 181 | input[type="checkbox"], 182 | input[type="radio"] { 183 | box-sizing: border-box; 184 | padding: 0 185 | } 186 | 187 | input[type="number"]::-webkit-inner-spin-button, 188 | input[type="number"]::-webkit-outer-spin-button { 189 | height: auto 190 | } 191 | 192 | input[type="search"] { 193 | -webkit-appearance: textfield; 194 | box-sizing: content-box 195 | } 196 | 197 | input[type="search"]::-webkit-search-cancel-button, 198 | input[type="search"]::-webkit-search-decoration { 199 | -webkit-appearance: none 200 | } 201 | 202 | fieldset { 203 | border: 1px solid #c0c0c0; 204 | margin: 0 2px; 205 | padding: 0.35em 0.625em 0.75em 206 | } 207 | 208 | legend { 209 | border: 0; 210 | padding: 0 211 | } 212 | 213 | textarea { 214 | overflow: auto 215 | } 216 | 217 | optgroup { 218 | font-weight: bold 219 | } 220 | 221 | table { 222 | border-collapse: collapse; 223 | border-spacing: 0 224 | } 225 | 226 | td, 227 | th { 228 | padding: 0 229 | } 230 | 231 | .highlight table td { 232 | padding: 5px 233 | } 234 | 235 | .highlight table pre { 236 | margin: 0 237 | } 238 | 239 | .highlight .cm { 240 | color: #999988; 241 | font-style: italic 242 | } 243 | 244 | .highlight .cp { 245 | color: #999999; 246 | font-weight: bold 247 | } 248 | 249 | .highlight .c1 { 250 | color: #999988; 251 | font-style: italic 252 | } 253 | 254 | .highlight .cs { 255 | color: #999999; 256 | font-weight: bold; 257 | font-style: italic 258 | } 259 | 260 | .highlight .c, 261 | .highlight .cd { 262 | color: #999988; 263 | font-style: italic 264 | } 265 | 266 | .highlight .err { 267 | color: #a61717; 268 | background-color: #e3d2d2 269 | } 270 | 271 | .highlight .gd { 272 | color: #000000; 273 | background-color: #ffdddd 274 | } 275 | 276 | .highlight .ge { 277 | color: #000000; 278 | font-style: italic 279 | } 280 | 281 | .highlight .gr { 282 | color: #aa0000 283 | } 284 | 285 | .highlight .gh { 286 | color: #999999 287 | } 288 | 289 | .highlight .gi { 290 | color: #000000; 291 | background-color: #ddffdd 292 | } 293 | 294 | .highlight .go { 295 | color: #888888 296 | } 297 | 298 | .highlight .gp { 299 | color: #555555 300 | } 301 | 302 | .highlight .gs { 303 | font-weight: bold 304 | } 305 | 306 | .highlight .gu { 307 | color: #aaaaaa 308 | } 309 | 310 | .highlight .gt { 311 | color: #aa0000 312 | } 313 | 314 | .highlight .kc { 315 | color: #000000; 316 | font-weight: bold 317 | } 318 | 319 | .highlight .kd { 320 | color: #000000; 321 | font-weight: bold 322 | } 323 | 324 | .highlight .kn { 325 | color: #000000; 326 | font-weight: bold 327 | } 328 | 329 | .highlight .kp { 330 | color: #000000; 331 | font-weight: bold 332 | } 333 | 334 | .highlight .kr { 335 | color: #000000; 336 | font-weight: bold 337 | } 338 | 339 | .highlight .kt { 340 | color: #445588; 341 | font-weight: bold 342 | } 343 | 344 | .highlight .k, 345 | .highlight .kv { 346 | color: #000000; 347 | font-weight: bold 348 | } 349 | 350 | .highlight .mf { 351 | color: #009999 352 | } 353 | 354 | .highlight .mh { 355 | color: #009999 356 | } 357 | 358 | .highlight .il { 359 | color: #009999 360 | } 361 | 362 | .highlight .mi { 363 | color: #009999 364 | } 365 | 366 | .highlight .mo { 367 | color: #009999 368 | } 369 | 370 | .highlight .m, 371 | .highlight .mb, 372 | .highlight .mx { 373 | color: #009999 374 | } 375 | 376 | .highlight .sb { 377 | color: #d14 378 | } 379 | 380 | .highlight .sc { 381 | color: #d14 382 | } 383 | 384 | .highlight .sd { 385 | color: #d14 386 | } 387 | 388 | .highlight .s2 { 389 | color: #d14 390 | } 391 | 392 | .highlight .se { 393 | color: #d14 394 | } 395 | 396 | .highlight .sh { 397 | color: #d14 398 | } 399 | 400 | .highlight .si { 401 | color: #d14 402 | } 403 | 404 | .highlight .sx { 405 | color: #d14 406 | } 407 | 408 | .highlight .sr { 409 | color: #009926 410 | } 411 | 412 | .highlight .s1 { 413 | color: #d14 414 | } 415 | 416 | .highlight .ss { 417 | color: #990073 418 | } 419 | 420 | .highlight .s { 421 | color: #d14 422 | } 423 | 424 | .highlight .na { 425 | color: #008080 426 | } 427 | 428 | .highlight .bp { 429 | color: #999999 430 | } 431 | 432 | .highlight .nb { 433 | color: #0086B3 434 | } 435 | 436 | .highlight .nc { 437 | color: #445588; 438 | font-weight: bold 439 | } 440 | 441 | .highlight .no { 442 | color: #008080 443 | } 444 | 445 | .highlight .nd { 446 | color: #3c5d5d; 447 | font-weight: bold 448 | } 449 | 450 | .highlight .ni { 451 | color: #800080 452 | } 453 | 454 | .highlight .ne { 455 | color: #990000; 456 | font-weight: bold 457 | } 458 | 459 | .highlight .nf { 460 | color: #990000; 461 | font-weight: bold 462 | } 463 | 464 | .highlight .nl { 465 | color: #990000; 466 | font-weight: bold 467 | } 468 | 469 | .highlight .nn { 470 | color: #555555 471 | } 472 | 473 | .highlight .nt { 474 | color: #000080 475 | } 476 | 477 | .highlight .vc { 478 | color: #008080 479 | } 480 | 481 | .highlight .vg { 482 | color: #008080 483 | } 484 | 485 | .highlight .vi { 486 | color: #008080 487 | } 488 | 489 | .highlight .nv { 490 | color: #008080 491 | } 492 | 493 | .highlight .ow { 494 | color: #000000; 495 | font-weight: bold 496 | } 497 | 498 | .highlight .o { 499 | color: #000000; 500 | font-weight: bold 501 | } 502 | 503 | .highlight .w { 504 | color: #bbbbbb 505 | } 506 | 507 | .highlight { 508 | background-color: #f8f8f8 509 | } 510 | 511 | * { 512 | box-sizing: border-box 513 | } 514 | 515 | body { 516 | padding: 0; 517 | margin: 0; 518 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 519 | font-size: 16px; 520 | line-height: 1.5; 521 | color: #606c71 522 | } 523 | 524 | a { 525 | color: #1e6bb8; 526 | text-decoration: none 527 | } 528 | 529 | a:hover { 530 | text-decoration: underline 531 | } 532 | 533 | .btn { 534 | display: inline-block; 535 | margin-bottom: 1rem; 536 | color: rgba(255, 255, 255, 0.7); 537 | background-color: rgba(255, 255, 255, 0.08); 538 | border-color: rgba(255, 255, 255, 0.2); 539 | border-style: solid; 540 | border-width: 1px; 541 | border-radius: 0.3rem; 542 | transition: color 0.2s, background-color 0.2s, border-color 0.2s 543 | } 544 | 545 | .btn:hover { 546 | color: rgba(255, 255, 255, 0.8); 547 | text-decoration: none; 548 | background-color: rgba(255, 255, 255, 0.2); 549 | border-color: rgba(255, 255, 255, 0.3) 550 | } 551 | 552 | .btn+.btn { 553 | margin-left: 1rem 554 | } 555 | 556 | @media screen and (min-width: 64em) { 557 | .btn { 558 | padding: 0.75rem 1rem 559 | } 560 | } 561 | 562 | @media screen and (min-width: 46em) and (max-width: 64em) { 563 | .btn { 564 | padding: 0.6rem 0.9rem; 565 | font-size: 0.9rem 566 | } 567 | } 568 | 569 | @media screen and (max-width: 46em) { 570 | .btn { 571 | display: block; 572 | width: 100%; 573 | padding: 0.75rem; 574 | font-size: 0.9rem 575 | } 576 | .btn+.btn { 577 | margin-top: 1rem; 578 | margin-left: 0 579 | } 580 | } 581 | 582 | .page-header { 583 | color: #fff; 584 | text-align: center; 585 | background-color: #3498db; 586 | background-image: linear-gradient(120deg, #3498db 50%, #2ecc71); 587 | } 588 | 589 | @media screen and (min-width: 64em) { 590 | .page-header { 591 | padding: 1.5rem 6rem 592 | } 593 | } 594 | 595 | @media screen and (min-width: 42em) and (max-width: 64em) { 596 | .page-header { 597 | padding: 1.5rem 4rem 598 | } 599 | } 600 | 601 | @media screen and (max-width: 42em) { 602 | .page-header { 603 | padding: 1.5rem 1rem 604 | } 605 | } 606 | 607 | .project-name { 608 | margin-top: 0; 609 | margin-bottom: 0.1rem 610 | } 611 | 612 | @media screen and (min-width: 64em) { 613 | .project-name { 614 | font-size: 3.25rem 615 | } 616 | } 617 | 618 | @media screen and (min-width: 42em) and (max-width: 64em) { 619 | .project-name { 620 | font-size: 2.25rem 621 | } 622 | } 623 | 624 | @media screen and (max-width: 42em) { 625 | .project-name { 626 | font-size: 1.75rem 627 | } 628 | } 629 | 630 | .project-tagline { 631 | margin-bottom: 2rem; 632 | font-weight: normal; 633 | opacity: 0.9 634 | } 635 | 636 | @media screen and (min-width: 64em) { 637 | .project-tagline { 638 | font-size: 1.25rem 639 | } 640 | } 641 | 642 | @media screen and (min-width: 42em) and (max-width: 64em) { 643 | .project-tagline { 644 | font-size: 1.15rem 645 | } 646 | } 647 | 648 | @media screen and (max-width: 42em) { 649 | .project-tagline { 650 | font-size: 1rem 651 | } 652 | } 653 | 654 | .main-content { 655 | word-wrap: break-word 656 | } 657 | 658 | .main-content :first-child { 659 | margin-top: 0 660 | } 661 | 662 | @media screen and (min-width: 64em) { 663 | .main-content { 664 | max-width: 64rem; 665 | padding: 2rem 6rem; 666 | margin: 0 auto; 667 | font-size: 1.1rem 668 | } 669 | } 670 | 671 | @media screen and (min-width: 42em) and (max-width: 64em) { 672 | .main-content { 673 | padding: 2rem 4rem; 674 | font-size: 1.1rem 675 | } 676 | } 677 | 678 | @media screen and (max-width: 42em) { 679 | .main-content { 680 | padding: 2rem 1rem; 681 | font-size: 1rem 682 | } 683 | } 684 | 685 | img { 686 | max-width: 100% 687 | } 688 | 689 | .main-content h1, 690 | .main-content h2, 691 | .main-content h3, 692 | .main-content h4, 693 | .main-content h5, 694 | .main-content h6 { 695 | margin-top: 2rem; 696 | margin-bottom: 1rem; 697 | font-weight: normal; 698 | color: #3498db; 699 | } 700 | 701 | #coming-soon { 702 | margin: 0em 0 0 0; 703 | color: #fff; 704 | opacity: 0.6; 705 | } 706 | 707 | 708 | @media screen and (min-width: 64em) { 709 | #logo-full { 710 | max-width: 50%; 711 | } 712 | } 713 | 714 | @media screen and (min-width: 42em) and (max-width: 64em) { 715 | #logo-full { 716 | max-width: 75%; 717 | } 718 | } 719 | 720 | @media screen and (max-width: 42em) { 721 | #logo-full { 722 | max-width: 100%; 723 | } 724 | } 725 | 726 | 727 | .main-content p { 728 | margin-bottom: 1em 729 | } 730 | 731 | .main-content code { 732 | padding: 2px 4px; 733 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 734 | font-size: 0.9rem; 735 | color: #567482; 736 | background-color: #f3f6fa; 737 | border-radius: 0.3rem 738 | } 739 | 740 | .main-content pre { 741 | padding: 0.8rem; 742 | margin-top: 0; 743 | margin-bottom: 1rem; 744 | font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; 745 | color: #567482; 746 | word-wrap: normal; 747 | background-color: #f3f6fa; 748 | border: solid 1px #dce6f0; 749 | border-radius: 0.3rem 750 | } 751 | 752 | .main-content pre>code { 753 | padding: 0; 754 | margin: 0; 755 | font-size: 0.9rem; 756 | color: #567482; 757 | word-break: normal; 758 | white-space: pre; 759 | background: transparent; 760 | border: 0 761 | } 762 | 763 | .main-content .highlight { 764 | margin-bottom: 1rem 765 | } 766 | 767 | .main-content .highlight pre { 768 | margin-bottom: 0; 769 | word-break: normal 770 | } 771 | 772 | .main-content .highlight pre, 773 | .main-content pre { 774 | padding: 0.8rem; 775 | overflow: auto; 776 | font-size: 0.9rem; 777 | line-height: 1.45; 778 | border-radius: 0.3rem; 779 | -webkit-overflow-scrolling: touch 780 | } 781 | 782 | .main-content pre code, 783 | .main-content pre tt { 784 | display: inline; 785 | max-width: initial; 786 | padding: 0; 787 | margin: 0; 788 | overflow: initial; 789 | line-height: inherit; 790 | word-wrap: normal; 791 | background-color: transparent; 792 | border: 0 793 | } 794 | 795 | .main-content pre code:before, 796 | .main-content pre code:after, 797 | .main-content pre tt:before, 798 | .main-content pre tt:after { 799 | content: normal 800 | } 801 | 802 | .main-content ul, 803 | .main-content ol { 804 | margin-top: 0 805 | } 806 | 807 | .main-content blockquote { 808 | padding: 0 1rem; 809 | margin-left: 0; 810 | color: #819198; 811 | border-left: 0.3rem solid #dce6f0 812 | } 813 | 814 | .main-content blockquote>:first-child { 815 | margin-top: 0 816 | } 817 | 818 | .main-content blockquote>:last-child { 819 | margin-bottom: 0 820 | } 821 | 822 | .main-content table { 823 | display: block; 824 | width: 100%; 825 | overflow: auto; 826 | word-break: normal; 827 | word-break: keep-all; 828 | -webkit-overflow-scrolling: touch 829 | } 830 | 831 | .main-content table th { 832 | font-weight: bold 833 | } 834 | 835 | .main-content table th, 836 | .main-content table td { 837 | padding: 0.5rem 1rem; 838 | border: 1px solid #e9ebec 839 | } 840 | 841 | .main-content dl { 842 | padding: 0 843 | } 844 | 845 | .main-content dl dt { 846 | padding: 0; 847 | margin-top: 1rem; 848 | font-size: 1rem; 849 | font-weight: bold 850 | } 851 | 852 | .main-content dl dd { 853 | padding: 0; 854 | margin-bottom: 1rem 855 | } 856 | 857 | .main-content hr { 858 | height: 2px; 859 | padding: 0; 860 | margin: 1rem 0; 861 | background-color: #eff0f1; 862 | border: 0 863 | } 864 | 865 | .site-footer { 866 | padding-top: 2rem; 867 | padding-bottom: 1rem; 868 | margin-top: 1rem; 869 | border-top: solid 1px #eff0f1; 870 | text-align: center; 871 | position: relative; 872 | } 873 | 874 | .site-footer::before { 875 | content: ""; 876 | background-color: #3498db; 877 | background-image: linear-gradient(120deg, #3498db 50%, #2ecc71); 878 | background-repeat: no-repeat; 879 | background-position: 50% 0; 880 | opacity: 0.8; 881 | display: block; 882 | position: absolute; 883 | left: 0; 884 | top: 0; 885 | width: 100%; 886 | height: 100%; 887 | z-index: -1; 888 | } 889 | 890 | .site-footer p { 891 | color: white; 892 | } 893 | 894 | @media screen and (min-width: 64em) { 895 | .site-footer { 896 | font-size: 1rem 897 | } 898 | } 899 | 900 | @media screen and (min-width: 42em) and (max-width: 64em) { 901 | .site-footer { 902 | font-size: 1rem 903 | } 904 | } 905 | 906 | @media screen and (max-width: 42em) { 907 | .site-footer { 908 | font-size: 0.9rem 909 | } 910 | } 911 | 912 | .site-footer-owner { 913 | display: block; 914 | font-weight: bold 915 | } 916 | 917 | .site-footer-credits { 918 | color: #819198 919 | } -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/MediaFission.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson; 2 | 3 | import net.jacobpeterson.controller.DownloadsController; 4 | import net.jacobpeterson.view.ApplicationShell; 5 | 6 | public class MediaFission { 7 | 8 | private ApplicationShell applicationShell; 9 | private DownloadsController downloadsController; 10 | 11 | public MediaFission() { 12 | this.applicationShell = new ApplicationShell(this); 13 | this.downloadsController = new DownloadsController(this); 14 | } 15 | 16 | public void setup() { 17 | this.applicationShell.setup(); 18 | } 19 | 20 | public void start() { 21 | this.applicationShell.start(); // Blocking method that opens the GUI 22 | } 23 | 24 | public DownloadsController getDownloadsController() { 25 | return downloadsController; 26 | } 27 | 28 | 29 | public static void main(String[] args) { 30 | // args should be: java -jar MediaFission.jar --youtube-dl --ffmpeg --out --url 31 | 32 | // Temporary CommandLine version of MediaFission (No GUI) 33 | MediaFissionCommandLine mediaFissionCommandLine = new MediaFissionCommandLine(args); 34 | mediaFissionCommandLine.execute(); 35 | 36 | // MediaFission mediaFission = new MediaFission(); // Instantiates everything 37 | // mediaFission.setup(); // Configures everything 38 | // mediaFission.start(); // Open/shows/starts everything 39 | } 40 | 41 | 42 | // TONS OF TEST CODE BELOW WOW HOLY CRAP! 43 | // Will be removed later! 44 | 45 | private static void callTempTestCode() { 46 | // canvas scroll bar listener 47 | 48 | // Display display = new Display(); 49 | // Shell shell = new Shell(display); 50 | // shell.setLayout(new FillLayout()); 51 | // Image originalImage = null; 52 | // FileDialog dialog = new FileDialog(shell, SWT.OPEN); 53 | // dialog.setText("Open an image file or cancel"); 54 | // String string = dialog.open(); 55 | // if (string != null) { 56 | // originalImage = new Image(display, string); 57 | // } 58 | // if (originalImage == null) { 59 | // int width = 150, height = 200; 60 | // originalImage = new Image(display, width, height); 61 | // GC gc = new GC(originalImage); 62 | // gc.fillRectangle(0, 0, width, height); 63 | // gc.drawLine(0, 0, width, height); 64 | // gc.drawLine(0, height, width, 0); 65 | // gc.drawText("Default Image", 10, 10); 66 | // gc.dispose(); 67 | // } 68 | // final Image image = originalImage; 69 | // final Point origin = new Point(0, 0); 70 | // final Canvas canvas = new Canvas(shell, SWT.NO_BACKGROUND | 71 | // SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL | SWT.H_SCROLL); 72 | // final ScrollBar hBar = canvas.getHorizontalBar(); 73 | // hBar.addListener(SWT.Selection, e -> { 74 | // int hSelection = hBar.getSelection(); 75 | // int destX = -hSelection - origin.x; 76 | // Rectangle rect = image.getBounds(); 77 | // canvas.scroll(destX, 0, 0, 0, rect.width, rect.height, false); 78 | // origin.x = -hSelection; 79 | // }); 80 | // final ScrollBar vBar = canvas.getVerticalBar(); 81 | // vBar.addListener(SWT.Selection, e -> { 82 | // int vSelection = vBar.getSelection(); 83 | // int destY = -vSelection - origin.y; 84 | // Rectangle rect = image.getBounds(); 85 | // canvas.scroll(0, destY, 0, 0, rect.width, rect.height, false); 86 | // origin.y = -vSelection; 87 | // }); 88 | // canvas.addListener(SWT.Resize, e -> { 89 | // Rectangle rect = image.getBounds(); 90 | // Rectangle client = canvas.getClientArea(); 91 | // hBar.setMaximum(rect.width); 92 | // vBar.setMaximum(rect.height); 93 | // hBar.setThumb(Math.min(rect.width, client.width)); 94 | // vBar.setThumb(Math.min(rect.height, client.height)); 95 | // int hPage = rect.width - client.width; 96 | // int vPage = rect.height - client.height; 97 | // int hSelection = hBar.getSelection(); 98 | // int vSelection = vBar.getSelection(); 99 | // if (hSelection >= hPage) { 100 | // if (hPage <= 0) hSelection = 0; 101 | // origin.x = -hSelection; 102 | // } 103 | // if (vSelection >= vPage) { 104 | // if (vPage <= 0) vSelection = 0; 105 | // origin.y = -vSelection; 106 | // } 107 | // canvas.redraw(); 108 | // }); 109 | // canvas.addListener(SWT.Paint, e -> { 110 | // GC gc = e.gc; 111 | // gc.drawImage(image, origin.x, origin.y); 112 | // Rectangle rect = image.getBounds(); 113 | // Rectangle client = canvas.getClientArea(); 114 | // int marginWidth = client.width - rect.width; 115 | // if (marginWidth > 0) { 116 | // gc.fillRectangle(rect.width, 0, marginWidth, client.height); 117 | // } 118 | // int marginHeight = client.height - rect.height; 119 | // if (marginHeight > 0) { 120 | // gc.fillRectangle(0, rect.height, client.width, marginHeight); 121 | // } 122 | // }); 123 | // Rectangle rect = image.getBounds(); 124 | // shell.setSize(Math.max(200, rect.width - 100), Math.max(150, rect.height - 100)); 125 | // shell.open(); 126 | // while (!shell.isDisposed()) { 127 | // if (!display.readAndDispatch()) display.sleep(); 128 | // } 129 | // originalImage.dispose(); 130 | // display.dispose(); 131 | 132 | 133 | // FileDialog dialog = new FileDialog(shell, SWT.SHEET); 134 | // dialog.open(); 135 | 136 | // Shell other = new Shell(shell, SWT.MODELESS); 137 | // other.setSize(200, 100); 138 | // other.setLocation(800, 500); 139 | // 140 | // TabFolder tab = new TabFolder(other, SWT.TOP); 141 | // TabItem item = new TabItem(tab, SWT.NONE); 142 | // item.setText("asdf"); 143 | // TabItem item2 = new TabItem(tab, SWT.NONE); 144 | // item2.setText("asd2f"); 145 | // 146 | // tab.addSelectionListener(new SelectionAdapter() { 147 | // @Override 148 | // public void widgetSelected(SelectionEvent selectionEvent) { 149 | // System.out.println("asdffff"); 150 | // } 151 | // }); 152 | // 153 | // other.setLayout(new FillLayout()); 154 | // 155 | // other.open(); 156 | 157 | // Shell other = new Shell(shell, SWT.CLOSE | SWT.BORDER | SWT.TITLE | SWT.RESIZE); 158 | // other.setSize(200, 100); 159 | // other.setLayout(new FillLayout()); 160 | // Combo combo = new Combo(other, SWT.READ_ONLY); 161 | // combo.add("Video"); 162 | // combo.add(" mp4"); 163 | // combo.add(" mov"); 164 | // combo.add("Audio"); 165 | // combo.add(" mp3"); 166 | // combo.add(" m4a"); 167 | // combo.add(" ogg"); 168 | // 169 | // other.setLocation(shell.getBounds().x + shellContent.getAudioButton().getBounds().x, shell.getBounds().y + shellContent.getAudioButton().getBounds().y); 170 | // other.open(); 171 | 172 | 173 | // shell.setLayout(new FillLayout(SWT.VERTICAL)); 174 | // 175 | // final Text text = new Text(shell, SWT.BORDER); 176 | // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); 177 | // 178 | // DropTarget dt = new DropTarget(text, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK); 179 | // dt.setTransfer(FileTransfer.getInstance()); 180 | // dt.addDropListener(new DropTargetAdapter() { 181 | // public void drop(DropTargetEvent event) { 182 | // String fileList[] = null; 183 | // FileTransfer ft = FileTransfer.getInstance(); 184 | // if (ft.isSupportedType(event.currentDataType)) { 185 | // fileList = (String[]) event.data; 186 | // } 187 | // System.out.println(Arrays.toString(fileList)); 188 | // } 189 | // }); 190 | // 191 | // EditSongComposite c = new EditSongComposite(shellContent); 192 | // c.setLayout(new FillLayout()); 193 | // for (int i = 0; i < 20; i++){ 194 | // Button butto = new Button(shell, SWT.PUSH); 195 | // NSButton buttons = (NSButton) butto.view; 196 | // buttons.cell().setControlSize(1); // NSControlSize.small 197 | // butto.setText("asdf"); 198 | // } 199 | // NSButton nsRefreshAlbumArtButton = (NSButton) refreshAlbumArtButton.view; 200 | // nsRefreshAlbumArtButton.cell().setControlSize(2); // NSControlSize.mini 201 | // this.refreshAlbumArtButton.setText("Refresh"); 202 | // 203 | // 204 | // List list = new List(shell, SWT.SINGLE); 205 | // list.add("asdfasdfadfs"); 206 | // list.add("asdfasdfadfs"); 207 | // list.add("asdfasdfadfs"); 208 | // list.add("asdfasdfadfs"); 209 | // list.add("asdfasdfadfs"); 210 | // 211 | // 212 | // shell.open(); 213 | // 214 | // 215 | 216 | // Menu me = new Menu(button); 217 | // 218 | // MenuItem item = new MenuItem(me, SWT.NONE); 219 | // item.setText("asdf"); 220 | // 221 | // button.setMenu(me); 222 | // 223 | // 224 | // button.add("asdf"); 225 | // button.add("asdf"); 226 | // button.add("asdf"); 227 | // button.add("asdf"); 228 | // button.add("asdf"); 229 | // button.add("asdf"); 230 | // button.add("asdf"); 231 | // button.setListVisible(true); 232 | // NSButton button1 = (NSButton) button.view; 233 | // button1.setBezelStyle(9); 234 | 235 | 236 | // final Display display = new Display(); 237 | // final Shell shell = new Shell(display); 238 | // shell.setText("StackOverflow"); 239 | // shell.setLayout(new FillLayout()); 240 | // 241 | // final SashForm sashForm = new SashForm(shell, SWT.HORIZONTAL); 242 | // 243 | // Text text1 = new Text(sashForm, SWT.CENTER); 244 | // text1.setText("Text in pane #1"); 245 | // Text text2 = new Text(sashForm, SWT.CENTER); 246 | // text2.setText("Text in pane #2"); 247 | // 248 | // final SashForm sashForm2 = new SashForm(sashForm, SWT.VERTICAL); 249 | // 250 | // final Label labelA = new Label(sashForm2, SWT.BORDER | SWT.CENTER); 251 | // labelA.setText("Label in pane A"); 252 | // final Label labelB = new Label(sashForm2, SWT.BORDER | SWT.CENTER); 253 | // labelB.setText("Label in pane B"); 254 | // 255 | // sashForm.setWeights(new int[] { 1, 2, 3 }); 256 | // 257 | // new Label(shell, SWT.NONE).setText("Label"); 258 | // 259 | // shell.pack(); 260 | // shell.setSize(400, 300); 261 | // shell.open(); 262 | // 263 | // while (!shell.isDisposed()) 264 | // { 265 | // if (!display.readAndDispatch()) 266 | // display.sleep(); 267 | // } 268 | // display.dispose(); 269 | 270 | // final Display display = new Display(); 271 | // Shell shell = new Shell(display); 272 | // shell.setLayout(new FillLayout()); 273 | // 274 | // Table table = new Table(shell, SWT.VIRTUAL | SWT.BORDER_DASH); 275 | // table.setLinesVisible(true); 276 | // table.setHeaderVisible(true); 277 | // String[] titles = { " ", "C", "!", "Description", "Resource", 278 | // "In Folder", "Location" }; 279 | // for (int i = 0; i < titles.length; i++) { 280 | // TableColumn column = new TableColumn(table, SWT.NONE); 281 | // column.setText(titles[i]); 282 | // } 283 | // int count = 50; 284 | // for (int i = 0; i < count; i++) { 285 | // TableItem item = new TableItem(table, SWT.NONE); 286 | // item.setText(0, "x"); 287 | // item.setText(1, "y"); 288 | // item.setText(2, "!"); 289 | // item.setText(3, "this stuff behaves the way I expect"); 290 | // item.setText(4, "almost everywhere"); 291 | // item.setText(5, "some.folder"); 292 | // item.setText(6, "line " + i + " in nowhere"); 293 | // } 294 | // for (int i = 0; i < titles.length; i++) { 295 | // table.getColumn(i).pack(); 296 | // } 297 | // table.setSize(table.computeSize(SWT.DEFAULT, 200)); 298 | // shell.pack(); 299 | // shell.open(); 300 | // while (!shell.isDisposed()) { 301 | // if (!display.readAndDispatch()) 302 | // display.sleep(); 303 | // } 304 | // display.dispose(); 305 | 306 | 307 | // Display display = new Display(); 308 | // Shell shell = new MediaFission().createShell(display); 309 | // shell.open(); 310 | // while (!shell.isDisposed()) { 311 | // if (!display.readAndDispatch()) 312 | // display.sleep(); 313 | // } 314 | } 315 | 316 | // 317 | // 318 | // Image dogImage; 319 | // Text dogNameText; 320 | // Combo dogBreedCombo; 321 | // Canvas dogPhoto; 322 | // List categories; 323 | // Text nameText; 324 | // Text phoneText; 325 | // 326 | // 327 | // public Shell createShell(final Display display) { 328 | // final Shell shell = new Shell(display); 329 | // 330 | // try { 331 | // BufferedImage image = ImageIO.read(new File("/Users/Jacob/Documents/Websites/jacobpeterson.net NEW/favicon.png")); 332 | // } catch (Exception e) { 333 | // e.printStackTrace(); 334 | // } 335 | // FormLayout layout = new FormLayout(); 336 | // layout.marginWidth = 20; 337 | // layout.marginHeight = 20; 338 | // shell.setLayout(layout); 339 | // shell.setText("Dog Show Entry"); 340 | // 341 | // Group ownerInfo = new Group(shell, SWT.NONE); 342 | // ownerInfo.setText("Owner Info"); 343 | // FormLayout ownerLayout = new FormLayout(); 344 | // ownerLayout.marginWidth = 50; 345 | // ownerLayout.marginHeight = 5; 346 | // ownerInfo.setLayout(ownerLayout); 347 | // 348 | // Label dogName = new Label(shell, SWT.NONE); 349 | // dogName.setText("Dog's Name:"); 350 | // dogNameText = new Text(shell, SWT.SINGLE | SWT.BORDER); 351 | // 352 | // Label dogBreed = new Label(shell, SWT.NONE); 353 | // dogBreed.setText("Breed:"); 354 | // 355 | // dogBreedCombo = new Combo(shell, SWT.NONE); 356 | // dogBreedCombo.setItems("Collie", "Pitbull", "Poodle", 357 | // "Scottie", "Black Lab"); 358 | // 359 | // Label photo = new Label(shell, SWT.NONE); 360 | // photo.setText("Photo:"); 361 | // dogPhoto = new Canvas(shell, SWT.BORDER); 362 | // 363 | // Button browse = new Button(shell, SWT.PUSH); 364 | // browse.setText("Browse..."); 365 | // 366 | // Button delete = new Button(shell, SWT.PUSH); 367 | // delete.setText("Delete"); 368 | // 369 | // Label cats = new Label(shell, SWT.NONE); 370 | // cats.setText("Categories"); 371 | // categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL 372 | // | SWT.H_SCROLL); 373 | // categories.setItems("Best of Breed", "Prettiest Female", 374 | // "Handsomest Male", "Best Dressed", "Fluffiest Ears", 375 | // "Most Colors", "Best Performer", "Loudest Bark", 376 | // "Best Behaved", "Prettiest Eyes", "Most Hair", "Longest Tail", 377 | // "Cutest Trick"); 378 | // 379 | // Button enter = new Button(shell, SWT.PUSH); 380 | // enter.setText("Enter"); 381 | // FormData data = new FormData(); 382 | // data.top = new FormAttachment(dogNameText, 0, SWT.CENTER); 383 | // dogName.setLayoutData(data); 384 | // data = new FormData(); 385 | // data.left = new FormAttachment(dogName, 5); 386 | // data.right = new FormAttachment(100, 0); 387 | // dogNameText.setLayoutData(data); 388 | // 389 | // data = new FormData(); 390 | // data.top = new FormAttachment(dogBreedCombo, 0, SWT.CENTER); 391 | // dogBreed.setLayoutData(data); 392 | // data = new FormData(); 393 | // data.top = new FormAttachment(dogNameText, 5); 394 | // data.left = new FormAttachment(dogNameText, 0, SWT.LEFT); 395 | // data.right = new FormAttachment(categories, -5); 396 | // dogBreedCombo.setLayoutData(data); 397 | // 398 | // data = new FormData(80, 80); 399 | // data.top = new FormAttachment(dogBreedCombo, 5); 400 | // data.left = new FormAttachment(dogNameText, 0, SWT.LEFT); 401 | // data.right = new FormAttachment(categories, -5); 402 | // data.bottom = new FormAttachment(ownerInfo, -5); 403 | // dogPhoto.setLayoutData(data); 404 | // dogPhoto.addPaintListener(event -> { 405 | // if (dogImage != null) { 406 | // event.gc.drawImage(dogImage, 0, 0); 407 | // } 408 | // }); 409 | // data = new FormData(); 410 | // data.top = new FormAttachment(dogPhoto, 0, SWT.TOP); 411 | // photo.setLayoutData(data); 412 | // data = new FormData(); 413 | // data.top = new FormAttachment(photo, 5); 414 | // data.right = new FormAttachment(dogPhoto, -5); 415 | // data.bottom = new FormAttachment(browse, 15); 416 | // browse.setLayoutData(data); 417 | // browse.addSelectionListener(new SelectionAdapter() { 418 | // public void widgetSelected(SelectionEvent event) { 419 | // String fileName = new FileDialog(shell).open(); 420 | // if (fileName != null) { 421 | // dogImage = new Image(display, fileName); 422 | // dogPhoto.redraw(); 423 | // } 424 | // } 425 | // }); 426 | // 427 | // data = new FormData(); 428 | // data.left = new FormAttachment(browse, 0, SWT.LEFT); 429 | // data.top = new FormAttachment(browse, 5); 430 | // data.right = new FormAttachment(dogPhoto, -5); 431 | // delete.setLayoutData(data); 432 | // delete.addSelectionListener(new SelectionAdapter() { 433 | // public void widgetSelected(SelectionEvent event) { 434 | // if (dogImage != null) { 435 | // dogImage.dispose(); 436 | // dogImage = null; 437 | // dogPhoto.redraw(); 438 | // } 439 | // } 440 | // }); 441 | // 442 | // data = new FormData(90, 140); 443 | // data.top = new FormAttachment(dogPhoto, 0, SWT.TOP); 444 | // data.right = new FormAttachment(100, 0); 445 | // data.bottom = new FormAttachment(enter, -5); 446 | // categories.setLayoutData(data); 447 | // 448 | // data = new FormData(); 449 | // data.bottom = new FormAttachment(categories, -5); 450 | // data.left = new FormAttachment(categories, 0, SWT.CENTER); 451 | // cats.setLayoutData(data); 452 | // 453 | // data = new FormData(); 454 | // data.right = new FormAttachment(100, 0); 455 | // data.bottom = new FormAttachment(100, 0); 456 | // enter.setLayoutData(data); 457 | // enter.addSelectionListener(new SelectionAdapter() { 458 | // public void widgetSelected(SelectionEvent event) { 459 | // System.out.println("\nDog Name: " + dogNameText.getText()); 460 | // System.out.println("Dog Breed: " + dogBreedCombo.getText()); 461 | // System.out.println("Owner Name: " + nameText.getText()); 462 | // System.out.println("Owner Phone: " + phoneText.getText()); 463 | // System.out.println("Categories:"); 464 | // String cats[] = categories.getSelection(); 465 | // for (String cat : cats) { 466 | // System.out.println("\t" + cat); 467 | // } 468 | // } 469 | // }); 470 | // 471 | // data = new FormData(); 472 | // data.bottom = new FormAttachment(enter, -225); 473 | // data.left = new FormAttachment(0, 0); 474 | // data.right = new FormAttachment(categories, -5); 475 | // ownerInfo.setLayoutData(data); 476 | // 477 | // Label name = new Label(ownerInfo, SWT.NULL); 478 | // name.setText("Name:"); 479 | // 480 | // Label phone = new Label(ownerInfo, SWT.PUSH); 481 | // phone.setText("Phone:"); 482 | // 483 | // nameText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); 484 | // phoneText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); 485 | // 486 | // data = new FormData(); 487 | // data.top = new FormAttachment(nameText, 0, SWT.CENTER); 488 | // name.setLayoutData(data); 489 | // 490 | // data = new FormData(); 491 | // data.top = new FormAttachment(phoneText, 0, SWT.CENTER); 492 | // phone.setLayoutData(data); 493 | // 494 | // data = new FormData(); 495 | // data.left = new FormAttachment(phone, 5); 496 | // data.right = new FormAttachment(100, 0); 497 | // nameText.setLayoutData(data); 498 | // 499 | // data = new FormData(); 500 | // data.left = new FormAttachment(nameText, 0, SWT.LEFT); 501 | // data.right = new FormAttachment(100, 0); 502 | // data.top = new FormAttachment(55, 0); 503 | // phoneText.setLayoutData(data); 504 | // 505 | // shell.setMinimumSize(200, 200); 506 | // shell.pack(); 507 | // 508 | // return shell; 509 | // } 510 | } 511 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/MediaFissionCommandLine.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson; 2 | 3 | import com.google.gson.*; 4 | 5 | import java.io.*; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import java.nio.file.Files; 9 | import java.nio.file.StandardCopyOption; 10 | import java.util.Scanner; 11 | 12 | public class MediaFissionCommandLine { 13 | 14 | private String[] args; 15 | private File temporaryDataDirectory; 16 | private File mediaDataFile; 17 | private File destinationDirectory; 18 | private String songInfoURL; 19 | private String[] songGenres; 20 | private String[] youtubeDLJSONCommand; 21 | private String[] youtubeDLCommand; 22 | private String[] ffmpegConvertCommand; 23 | 24 | public MediaFissionCommandLine(String[] args) { 25 | this.args = args; 26 | // Create random-named directory to allow for multiple MediaFission instances 27 | this.temporaryDataDirectory = new File(System.getProperty("user.home"), ".tempMediaFission/" + String.valueOf(Math.random()).substring(5)); 28 | this.mediaDataFile = new File(temporaryDataDirectory, "mediaData.json"); 29 | 30 | this.songInfoURL = "http://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key=bc67636b1f06495cdee8b3dbcc49f477&format=json"; // + "&artist=&track=" 31 | 32 | this.songGenres = new String[]{ 33 | "Alternative", 34 | "Blues", 35 | "Classical", 36 | "Country", 37 | "Dance", 38 | "Electronic", 39 | "Hip-Hop/Rap", 40 | "Indie", 41 | "Inspirational", 42 | "Instrumental", 43 | "Jazz", 44 | "Latino", 45 | "Opera", 46 | "Pop", 47 | "R&B/Soul", 48 | "Reggae", 49 | "Rock", 50 | "Soundtrack", 51 | "Vocal", 52 | "World" 53 | }; 54 | 55 | this.youtubeDLJSONCommand = new String[]{ 56 | "", 57 | "-f", 58 | "bestaudio", 59 | "--no-playlist", 60 | "--playlist-items", 61 | "1", 62 | "--dump-json", 63 | "" 64 | }; 65 | this.youtubeDLCommand = new String[]{ 66 | "", 67 | "-f", 68 | "bestaudio", 69 | "--no-playlist", 70 | "--playlist-items", 71 | "1", 72 | "--load-info-json", 73 | "", 74 | }; 75 | this.ffmpegConvertCommand = new String[]{ 76 | "", 77 | "-i", 78 | "", 79 | "-i", 80 | "", 81 | "-y", 82 | "-map", "0", 83 | "-map", "1", 84 | "-metadata", "title=", 85 | "-metadata", "artist=<artist placeholder>", 86 | "-metadata", "album=<album placeholder>", 87 | "-metadata", "album_artist=<album_artist placeholder>", 88 | "-metadata", "genre=<genre placeholder>", 89 | "-metadata:s:1", "title=Cover (front)", 90 | "<output path placeholder>" 91 | }; 92 | } 93 | 94 | public void execute() { 95 | 96 | String url = this.setupEnvironmentAndGetURL(); 97 | JsonObject mediaJSON = this.downloadJSONData(url); 98 | File mediaFile = this.downloadMedia(mediaJSON); 99 | JsonObject songMetadata = this.fetchSongMetadata(mediaJSON); 100 | File finalMediaFile = this.applyMetadataAndCreateFinal(mediaFile, songMetadata); 101 | this.cleanUpAndMove(finalMediaFile); 102 | 103 | System.out.println(); 104 | System.out.println("Successfully downloaded: " + finalMediaFile.getName()); 105 | 106 | } 107 | 108 | private String setupEnvironmentAndGetURL() { 109 | // Create temporaryDataDirectory 110 | if (!temporaryDataDirectory.exists() & !temporaryDataDirectory.mkdirs()) { 111 | throwError("Could not create temporaryDataDirectory!", false, true); 112 | } 113 | 114 | String returnURL = ""; 115 | 116 | // Check proper args - java -jar MediaFission.jar --youtube-dl <path> --ffmpeg <path> --out <path> --url <url> 117 | if (args.length != 8) { 118 | throwError("No URL in args! Use: command <url>", false, true); 119 | } 120 | for (int index = 0; index < args.length; index++) { 121 | String arg = args[index]; 122 | if (arg.equals("--youtube-dl")) { 123 | this.youtubeDLJSONCommand[0] = args[index + 1]; 124 | this.youtubeDLCommand[0] = args[index + 1]; 125 | } else if (arg.equals("--ffmpeg")) { 126 | this.ffmpegConvertCommand[0] = args[index + 1]; 127 | } else if (arg.equals("--out")) { 128 | this.destinationDirectory = new File(args[index + 1]); 129 | } else if (arg.equals("--url")) { 130 | returnURL = args[index + 1]; 131 | } 132 | } 133 | 134 | // Check if youtube-dl, FFMPEG, and out directory exist 135 | if (!new File(youtubeDLJSONCommand[0]).exists()) { 136 | throwError("youtube-dl executable does not exist!", false, true); 137 | } 138 | if (!new File(ffmpegConvertCommand[0]).exists()) { 139 | throwError("ffmpeg executable does not exist!", false, true); 140 | } 141 | if (!destinationDirectory.exists()) { 142 | throwError("Out Directory does not exist!", false, true); 143 | } 144 | 145 | System.out.println("Setup - Temporary data directory obtained and arguments are valid!"); 146 | 147 | return returnURL; 148 | } 149 | 150 | private JsonObject downloadJSONData(String url) { 151 | System.out.println("Downloading JSON - Fetching JSON data from: " + url + " ..."); 152 | 153 | this.replacePlaceholder(youtubeDLJSONCommand, youtubeDLJSONCommand.length - 1, url); 154 | 155 | try { 156 | Process youtubeDLJSONProcess = this.createCommandProcess(youtubeDLJSONCommand, temporaryDataDirectory); 157 | if (youtubeDLJSONProcess == null) { 158 | throwError("Could not create Process for youtubeDLCommand.", true, true); 159 | } 160 | 161 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(youtubeDLJSONProcess.getInputStream())); 162 | String line; 163 | while ((line = bufferedReader.readLine()) != null) { 164 | if (line.startsWith("{")) { // Start of main JSON object 165 | break; // Use 'line' variable for JSON 166 | } 167 | } 168 | bufferedReader.close(); 169 | youtubeDLJSONProcess.destroy(); // We don't need the process anymore after we have the JSON 170 | 171 | if (line == null) { 172 | throwError("Could not get media JSON from youtube-dl!", true, true); 173 | } 174 | System.out.println("Downloaded JSON - Finished fetching JSON and creating JSON file..."); 175 | 176 | // Write the JSON data created from youtube-dl so we can get some metadata from that before downloading the actual file 177 | Files.write(mediaDataFile.toPath(), line.getBytes()); // Default StandardOpenOptions are fine here (see javadocs) 178 | System.out.println("Created JSON file - Wrote JSON metadata file to temporary data directory."); 179 | 180 | JsonElement metadataElement = new JsonParser().parse(line); // Parse metadata from youtube-dl so we can get the name and artists as well as the destination file location 181 | if (!(metadataElement instanceof JsonObject)) { 182 | throwError("Could not cast JsonElement as JsonObject!", true, true); 183 | } 184 | return (JsonObject) metadataElement; 185 | } catch (JsonParseException | IOException e) { 186 | e.printStackTrace(); 187 | } 188 | return null; 189 | } 190 | 191 | private File downloadMedia(JsonObject mediaData) { 192 | System.out.println("Downloading Media - downloading media using JSON..."); 193 | 194 | this.replacePlaceholder(youtubeDLCommand, youtubeDLCommand.length - 1, mediaDataFile.getAbsolutePath()); 195 | 196 | try { 197 | Process youtubeDLProcess = this.createCommandProcess(youtubeDLCommand, temporaryDataDirectory); 198 | if (youtubeDLProcess == null) { 199 | throwError("Could not create Process for youtubeDLCommand.", true, true); 200 | } 201 | try { 202 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(youtubeDLProcess.getInputStream())); 203 | String line; 204 | while ((line = bufferedReader.readLine()) != null) { 205 | if (line.contains("[download")) { // Start of main JSON object 206 | System.out.println(line); 207 | } 208 | } 209 | bufferedReader.close(); 210 | } catch (IOException e) { 211 | e.printStackTrace(); 212 | } 213 | youtubeDLProcess.waitFor(); 214 | youtubeDLProcess.destroy(); // We don't need the process anymore after we have the downloaded media (in case it doesn't end) 215 | } catch (InterruptedException e) { 216 | e.printStackTrace(); 217 | } 218 | return new File(temporaryDataDirectory, mediaData.get("_filename").getAsString()); 219 | } 220 | 221 | private JsonObject fetchSongMetadata(JsonObject mediaData) { 222 | String artistKey = "&artist="; 223 | String trackKey = "&track="; 224 | 225 | String artistGuess = null; 226 | String trackGuess = null; 227 | 228 | String extractor = mediaData.get("extractor").getAsString(); 229 | String title = mediaData.get("title").getAsString(); 230 | if (extractor.equals("youtube") && title.contains(" - ")) { // " - " means that this could be a song so we'll try to parse out the artist and track names 231 | String[] splitTitle = title.split(" - "); 232 | artistGuess = splitTitle[0]; 233 | trackGuess = splitTitle[1]; 234 | } else if (extractor.equals("soundcloud")) { 235 | artistGuess = mediaData.get("uploader").getAsString(); 236 | trackGuess = title; 237 | } 238 | 239 | System.out.print("\n\n"); 240 | 241 | Scanner inputScanner = new Scanner(System.in); 242 | 243 | boolean conclude = false; 244 | int loopStatus = 0; // Tells whether to print confirmation, edit artist, or edit track information 245 | while (!conclude) { 246 | if (loopStatus == 0) { 247 | System.out.println("Please validate or edit the artist and track name..."); 248 | System.out.println(); 249 | System.out.println("1: Artist - " + artistGuess); 250 | System.out.println("2: Track - " + trackGuess); 251 | System.out.println(); 252 | System.out.println("Press '1' to edit the Artist name"); 253 | System.out.println("Press '2' to edit the Track name"); 254 | System.out.println("Press the 'Return' key to continue with these names"); 255 | System.out.println("Enter an option here: "); 256 | 257 | String input = inputScanner.nextLine(); 258 | if (input.isEmpty()) { // Carriage Return key 259 | conclude = true; 260 | } else { 261 | int inputASCIICharacter = input.charAt(0); // Get first character 262 | if (inputASCIICharacter == 0x31) { // '1' Key 263 | loopStatus = 1; 264 | } else if (inputASCIICharacter == 0x32) { // '2' Key 265 | loopStatus = 2; 266 | } else { 267 | System.out.println("Invalid Character!"); 268 | } 269 | } 270 | } else if (loopStatus == 1) { // Artist Name 271 | System.out.print("Type the new Artist name here: "); 272 | artistGuess = inputScanner.nextLine(); 273 | loopStatus = 0; 274 | } else if (loopStatus == 2) { // Track Name 275 | System.out.print("Type the new Track name here: "); 276 | trackGuess = inputScanner.nextLine(); 277 | loopStatus = 0; 278 | } 279 | } 280 | 281 | this.songInfoURL += artistKey + artistGuess + trackKey + trackGuess; 282 | System.out.println(songInfoURL); 283 | 284 | String songInfoString = this.makeHTTPRequest(songInfoURL); 285 | if (songInfoString == null) { 286 | throwError("HTTP Request empty!", false, true); 287 | } 288 | JsonElement songInfoJsonElement = new JsonParser().parse(songInfoString); 289 | if (!(songInfoJsonElement instanceof JsonObject)) { 290 | throwError("Could not cast JsonElement as JsonObject!", true, false); 291 | return null; 292 | } 293 | JsonObject songInfoJsonObject = (JsonObject) songInfoJsonElement; 294 | if (songInfoJsonObject.has("error")) { 295 | throwError("Error in JSON response " + songInfoJsonObject.get("message").getAsString(), false, false); 296 | return null; 297 | } 298 | 299 | System.out.println("Fetched Song Info - Successfully fetched song info from last.fm service!"); 300 | 301 | return songInfoJsonObject.get("track").getAsJsonObject(); 302 | } 303 | 304 | private File applyMetadataAndCreateFinal(File mediaFile, JsonObject songInfoJsonObject) { 305 | System.out.println("Parsing Metadata - reading from JSON into objects..."); 306 | 307 | File albumArtFile = null; 308 | String trackName = "\"\""; 309 | String artistName = "\"\""; 310 | String albumName = "\"\""; 311 | String genreName = "\"\""; 312 | 313 | try { 314 | trackName = songInfoJsonObject.get("name").getAsString(); 315 | 316 | if (songInfoJsonObject.has("artist")) { 317 | artistName = songInfoJsonObject.get("artist").getAsJsonObject().get("name").getAsString(); 318 | } 319 | 320 | if (songInfoJsonObject.has("album")) { 321 | JsonObject albumJsonObject = songInfoJsonObject.get("album").getAsJsonObject(); 322 | albumName = albumJsonObject.get("title").getAsString(); 323 | 324 | JsonArray albumImageArray = albumJsonObject.get("image").getAsJsonArray(); 325 | String albumArtURL = albumImageArray.get(albumImageArray.size() - 1).getAsJsonObject().get("#text").getAsString(); 326 | albumArtFile = new File(temporaryDataDirectory, albumArtURL.substring(albumArtURL.lastIndexOf('/'))); 327 | try (InputStream in = new URL(albumArtURL).openStream()) { 328 | Files.copy(in, albumArtFile.toPath(), StandardCopyOption.REPLACE_EXISTING); 329 | } catch (IOException e) { 330 | e.printStackTrace(); 331 | } 332 | } 333 | 334 | if (songInfoJsonObject.has("toptags")) { 335 | genreName = ""; 336 | JsonArray songTagsArray = songInfoJsonObject.get("toptags").getAsJsonObject().get("tag").getAsJsonArray(); 337 | for (JsonElement tagElement : songTagsArray) { 338 | if (tagElement instanceof JsonObject) { 339 | JsonObject tagObject = (JsonObject) tagElement; 340 | String tagName = tagObject.get("name").getAsString(); 341 | for (String songGenre : songGenres) { 342 | if (songGenre.toLowerCase().contains(tagName)) { 343 | genreName = songGenre; 344 | break; 345 | } 346 | } 347 | } 348 | } 349 | } 350 | } catch (NullPointerException e) { 351 | e.printStackTrace(); 352 | } 353 | 354 | if (albumArtFile == null) { 355 | System.out.println("Could not find album art for this song!"); 356 | System.out.println("Input an Image file URL manually here: "); 357 | Scanner scanner = new Scanner(System.in); 358 | String url = scanner.nextLine(); 359 | if (url != null && !url.isEmpty()) { 360 | albumArtFile = new File(url); 361 | } else { 362 | throwError("No album art included!", false, true); 363 | } 364 | System.out.println("Album art located at: " + albumArtFile.getAbsolutePath()); 365 | } 366 | 367 | 368 | File finalMediaFile = new File(temporaryDataDirectory, artistName + " - " + trackName + ".mp3"); 369 | 370 | this.replacePlaceholder(ffmpegConvertCommand, 2, mediaFile.getAbsolutePath()); 371 | this.replacePlaceholder(ffmpegConvertCommand, 4, albumArtFile.getAbsolutePath()); 372 | this.replacePlaceholder(ffmpegConvertCommand, 11, trackName); 373 | this.replacePlaceholder(ffmpegConvertCommand, 13, artistName); 374 | this.replacePlaceholder(ffmpegConvertCommand, 15, albumName); 375 | this.replacePlaceholder(ffmpegConvertCommand, 17, artistName); 376 | this.replacePlaceholder(ffmpegConvertCommand, 19, genreName); 377 | this.replacePlaceholder(ffmpegConvertCommand, 22, finalMediaFile.getAbsolutePath()); 378 | 379 | System.out.println("FFMPEG - Converting and applying metadata..."); 380 | try { 381 | Process ffmpegConvertProcess = this.createCommandProcess(ffmpegConvertCommand, temporaryDataDirectory); 382 | if (ffmpegConvertProcess == null) { 383 | throwError("Could not create Process for youtubeDLCommand.", true, true); 384 | } 385 | ffmpegConvertProcess.waitFor(); 386 | ffmpegConvertProcess.destroy(); // We don't need the process anymore after we have the downloaded media (in case it doesn't end) 387 | } catch (InterruptedException e) { 388 | e.printStackTrace(); 389 | } 390 | 391 | System.out.println("Applied Metadata - successfully attached metadata to media."); 392 | 393 | return finalMediaFile; 394 | } 395 | 396 | private void cleanUpAndMove(File finalMediaFile) { 397 | System.out.println("Moving and cleaning - Moving media file to " + " and cleaning temporary data directory..."); 398 | 399 | // Move media file 400 | try { 401 | Files.move(finalMediaFile.toPath(), destinationDirectory.toPath().resolve(finalMediaFile.getName()), StandardCopyOption.REPLACE_EXISTING); 402 | } catch (IOException e) { 403 | e.printStackTrace(); 404 | } 405 | 406 | // Clean temp data directory 407 | File[] dataFiles = temporaryDataDirectory.listFiles(); 408 | if (dataFiles == null) { 409 | throwError("Could not list data files!", true, true); 410 | } 411 | for (File file : dataFiles) { 412 | if (!file.delete()) { 413 | throwError("Could not clean up file: " + file.getName(), true, true); 414 | } 415 | } 416 | if (!temporaryDataDirectory.delete()) { 417 | throwError("Could not clean up temporaryDataDirectory!", true, true); 418 | } 419 | 420 | System.out.println("Moved and cleaned - Successfully moved media and cleaned temp data directory!"); 421 | } 422 | 423 | 424 | private void replacePlaceholder(String[] command, int index, String argument) { 425 | int indexOfLessThan = command[index].indexOf("<"); 426 | int indexOfGreaterThan = command[index].indexOf(">"); 427 | 428 | String newArgument = command[index].substring(0, indexOfLessThan) + argument + command[index].substring(indexOfGreaterThan + 1); 429 | command[index] = newArgument; 430 | } 431 | 432 | private Process createCommandProcess(String[] command, File workingDirectory) { 433 | try { 434 | return Runtime.getRuntime().exec(command, null, workingDirectory); 435 | } catch (IOException e) { 436 | e.printStackTrace(); 437 | } 438 | return null; 439 | } 440 | 441 | private String makeHTTPRequest(String url) { 442 | try { 443 | URL httpURL = new URL(url); 444 | URLConnection urlConnection = httpURL.openConnection(); 445 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); 446 | 447 | StringBuilder stringBuilder = new StringBuilder(); 448 | String line; 449 | while ((line = bufferedReader.readLine()) != null) { 450 | stringBuilder.append(line); 451 | } 452 | bufferedReader.close(); 453 | 454 | return stringBuilder.toString(); 455 | } catch (IOException e) { 456 | e.printStackTrace(); 457 | } 458 | return null; 459 | } 460 | 461 | private static void throwError(String message, boolean includeStacktrace, boolean fatal) { 462 | if (message != null) { 463 | if (includeStacktrace) { 464 | new RuntimeException(message).printStackTrace(); 465 | } else { 466 | System.err.println(message); 467 | } 468 | } 469 | if (fatal) { 470 | System.err.println("Fatal error - exiting!"); 471 | System.exit(1); 472 | } 473 | } 474 | 475 | private void debugOutput(Process process) { 476 | final Thread ioThread = new Thread(() -> { 477 | try { 478 | final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream())); 479 | String line; 480 | while ((line = reader.readLine()) != null) { 481 | System.out.println(line); 482 | } 483 | reader.close(); 484 | } catch (Exception e) { 485 | e.printStackTrace(); 486 | } 487 | }); 488 | ioThread.start(); 489 | } 490 | } 491 | --------------------------------------------------------------------------------