summaryrefslogtreecommitdiff
path: root/src/main/java/net
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2017-04-30 18:19:45 -0700
committerJesse Morgan <jesse@jesterpm.net>2017-04-30 18:19:45 -0700
commite150b8230a5490588703b3ba02f31d702bdbfccf (patch)
treec0e1bcfa49d232c16b79fcc407219cc71850d590 /src/main/java/net
parentc5df48d96b5e48845a85f1495d0ed5ab1d1cc37b (diff)
Adding metadata window.HEADmaster
Diffstat (limited to 'src/main/java/net')
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/PodcastUploader.java59
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/config/Config.java137
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/control/ConfigureTask.java61
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/control/MetadataTask.java87
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/control/PublishPodcastTask.java2
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/control/S3UploadTask.java6
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/model/Config.java56
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/model/Metadata.java43
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/ui/Action.java5
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/ui/ConfigurationWindow.java43
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/ui/GUIFactory.java21
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/ui/MetadataWindow.java186
-rw-r--r--src/main/java/net/jesterpm/podcastuploader/ui/UIFactory.java13
13 files changed, 451 insertions, 268 deletions
diff --git a/src/main/java/net/jesterpm/podcastuploader/PodcastUploader.java b/src/main/java/net/jesterpm/podcastuploader/PodcastUploader.java
index f386f0d..b723ac9 100644
--- a/src/main/java/net/jesterpm/podcastuploader/PodcastUploader.java
+++ b/src/main/java/net/jesterpm/podcastuploader/PodcastUploader.java
@@ -4,11 +4,11 @@
package net.jesterpm.podcastuploader;
-import net.jesterpm.podcastuploader.config.Config;
-import net.jesterpm.podcastuploader.control.ConfigureTask;
-import net.jesterpm.podcastuploader.control.UploadTask;
-import net.jesterpm.podcastuploader.ui.ConfigurationWindow;
-import net.jesterpm.podcastuploader.ui.ProgressWindow;
+import net.jesterpm.podcastuploader.model.Config;
+import net.jesterpm.podcastuploader.control.MetadataTask;
+import net.jesterpm.podcastuploader.ui.GUIFactory;
+
+import java.io.*;
/**
* Application entry-point.
@@ -20,45 +20,18 @@ public class PodcastUploader {
+ System.getProperty("file.separator") + ".podcastuploader";
public static void main(String... args) {
- final Config appconfig = new Config(DEFAULT_CONFIG);
-
- if (args.length == 0) {
- startConfigure(appconfig);
-
- } else {
- if (args[0].equals("--help")) {
- printHelp();
- }
-
- startUpload(appconfig, args[0]);
+ // Load the configuration.
+ final Config config;
+ try {
+ config = new Config(DEFAULT_CONFIG);
+ } catch (IOException e) {
+ System.err.println("Exception loading config! " + e.getMessage());
+ System.exit(1);
+ return;
}
- }
-
- private static void printHelp() {
- System.out.println("PodcastUploader - Podcast upload utility.");
- System.out.println("Created by Jesse Morgan <jesse@jesterpm.net>");
- System.out.println("http://jesterpm.net/projects/podcastuploader");
- System.out.println();
- System.out.println("Usage: PodcastUploader [directory]");
- System.out.println(
- "When started with no arguments, the configuration dialog is opened.");
- System.out.println(
- "When started with one argument, it is assumed to be a directory\n"
- + "with a metadata.txt file with upload instructions.");
- System.out.println();
- }
-
- private static void startConfigure(final Config appconfig) {
- ConfigurationWindow win = new ConfigurationWindow();
- ConfigureTask task = new ConfigureTask(appconfig, win);
-
- task.run();
- }
-
- public static void startUpload(final Config appconfig, final String dir) {
- ProgressWindow win = new ProgressWindow();
- UploadTask task = new UploadTask(appconfig, win, dir);
- task.run();
+ GUIFactory uiFactory = new GUIFactory();
+ MetadataTask task = new MetadataTask(config, uiFactory);
+ task.run(() -> System.exit(0));
}
}
diff --git a/src/main/java/net/jesterpm/podcastuploader/config/Config.java b/src/main/java/net/jesterpm/podcastuploader/config/Config.java
deleted file mode 100644
index b9260e0..0000000
--- a/src/main/java/net/jesterpm/podcastuploader/config/Config.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2012 Jesse Morgan
- */
-
-package net.jesterpm.podcastuploader.config;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.File;
-
-import java.text.ParseException;
-
-import java.util.Map;
-import java.util.HashMap;
-
-/**
- * Configuration and metadata parser.
- *
- * @author Jesse Morgan <jesse@jesterpm.net>
- */
-public class Config {
- private final String mFilename;
- private Map<String, String> mConfig;
-
- /**
- * States the parser can be in.
- */
- private enum TokenizerState {
- OUT_OF_ENTRY, IN_ENTRY;
- }
-
- /**
- * Create a new Config object based on the given file.
- */
- public Config(final String filename) {
- mFilename = filename;
- mConfig = new HashMap<String, String>();
- parse();
- }
-
- private void parse() {
- try {
- final File configfile = new File(mFilename);
- if (configfile.isFile()) {
- BufferedReader in = new BufferedReader(new FileReader(mFilename));
-
- int lineno = 0;
- String line;
- String key = "";
- String value = "";
- TokenizerState state = TokenizerState.OUT_OF_ENTRY;
- while ((line = in.readLine()) != null) {
- lineno++;
- if (line.length() == 0) {
- continue;
- }
-
- switch (state) {
- case IN_ENTRY:
- if (Character.isWhitespace(line.charAt(0))) {
- value += "\n" + line.trim();
- break;
-
- } else {
- // Beginning new entry. Save old and pass through.
- mConfig.put(key, value);
- key = value = "";
- state = TokenizerState.OUT_OF_ENTRY;
- // NB Intentionally falling through...
- }
-
- case OUT_OF_ENTRY:
- if (line.charAt(0) == '#') {
- continue;
- }
-
- final int pos = line.indexOf(':');
- if (pos == -1) {
- throw new ParseException("Missing : at line " + lineno, lineno);
- }
- key = line.substring(0, pos).trim().toLowerCase();
- if (key.length() == 0) {
- throw new ParseException("Zero-length key on line " + lineno,
- lineno);
- }
- value = line.substring(pos + 1).trim();
- state = TokenizerState.IN_ENTRY;
- break;
- }
- }
-
- // Catch last key/value
- if (state == TokenizerState.IN_ENTRY) {
- mConfig.put(key, value);
- }
-
- in.close();
- }
-
- } catch (final Exception e) {
- System.err.println("[ERROR] Failed to load config from " + mFilename + ". "
- + e.getMessage());
- }
- }
-
- public void save() {
- try {
- BufferedWriter out = new BufferedWriter(new FileWriter(mFilename));
-
- for (Map.Entry<String, String> entry : mConfig.entrySet()) {
- out.write(entry.getKey());
- out.write(": ");
- out.write(entry.getValue());
- out.newLine();
- }
-
- out.close();
-
- } catch (final Exception e) {
- System.err.println("[ERROR] Failed to save configuration: " + e.getMessage());
- }
- }
-
- public String get(final String key) {
- return mConfig.get(key);
- }
-
- public void put(final String key, final String obj) {
- mConfig.put(key, obj);
- }
-
- public Map<String, String> getMap() {
- return mConfig;
- }
-}
diff --git a/src/main/java/net/jesterpm/podcastuploader/control/ConfigureTask.java b/src/main/java/net/jesterpm/podcastuploader/control/ConfigureTask.java
index 0158155..80fd253 100644
--- a/src/main/java/net/jesterpm/podcastuploader/control/ConfigureTask.java
+++ b/src/main/java/net/jesterpm/podcastuploader/control/ConfigureTask.java
@@ -4,10 +4,12 @@
package net.jesterpm.podcastuploader.control;
-import net.jesterpm.podcastuploader.config.Config;
+import net.jesterpm.podcastuploader.model.Config;
-import net.jesterpm.podcastuploader.ui.Action;
import net.jesterpm.podcastuploader.ui.ConfigurationWindow;
+import net.jesterpm.podcastuploader.ui.UIFactory;
+
+import java.io.IOException;
/**
* Controller for the ConfigurationWindow.
@@ -15,35 +17,24 @@ import net.jesterpm.podcastuploader.ui.ConfigurationWindow;
* @author Jesse Morgan <jesse@jesterpm.net>
*/
public class ConfigureTask {
- private final Config mAppConfig;
+ private final Config mConfig;
private final ConfigurationWindow mWin;
- public ConfigureTask(final Config appconfig, final ConfigurationWindow win) {
- mAppConfig = appconfig;
- mWin = win;
+ public ConfigureTask(final Config config, final UIFactory uiFactory) {
+ mConfig = config;
+ mWin = uiFactory.createConfigurationWindow();
- mWin.addSaveAction(new Action() {
- public void onAction() {
+ mWin.addSaveAction(() -> {
+ try {
populateConfig();
- mAppConfig.save();
- mWin.setVisible(false);
- System.exit(0);
- }
- });
-
- mWin.addCancelAction(new Action() {
- public void onAction() {
- mWin.setVisible(false);
- System.exit(0);
+ mConfig.save();
+ } catch (IOException e) {
+ // TODO
}
+ mWin.setVisible(false);
});
- mWin.addAuthorizeAction(new Action() {
- public void onAction() {
- populateConfig();
- getAuthorization();
- }
- });
+ mWin.addCancelAction(() -> mWin.setVisible(false));
populateWindow();
}
@@ -52,21 +43,18 @@ public class ConfigureTask {
* Set the fields in the configuration window.
*/
private void populateWindow() {
- mWin.setAWSKey(mAppConfig.get("AWSAccessKeyId"));
- mWin.setAWSSecret(mAppConfig.get("AWSSecretKey"));
- mWin.setS3Bucket(mAppConfig.get("S3Bucket"));
- mWin.setMetadataServer(mAppConfig.get("MetadataURL"));
- mWin.setHasAuthKey(mAppConfig.get("MetadataAuthKey") != null);
+ mWin.setAWSKey(mConfig.getProperty("AWSAccessKeyId"));
+ mWin.setAWSSecret(mConfig.getProperty("AWSSecretKey"));
+ mWin.setS3Bucket(mConfig.getProperty("S3Bucket"));
}
/**
* Populate the config from the window.
*/
private void populateConfig() {
- mAppConfig.put("AWSAccessKeyId", mWin.getAWSKey());
- mAppConfig.put("AWSSecretKey", mWin.getAWSSecret());
- mAppConfig.put("S3Bucket", mWin.getS3Bucket());
- mAppConfig.put("MetadataURL", mWin.getMetadataServer());
+ mConfig.put("AWSAccessKeyId", mWin.getAWSKey());
+ mConfig.put("AWSSecretKey", mWin.getAWSSecret());
+ mConfig.put("S3Bucket", mWin.getS3Bucket());
}
/**
@@ -75,11 +63,4 @@ public class ConfigureTask {
public void run() {
mWin.setVisible(true);
}
-
- /**
- * Get an authorization token from the metadata service.
- */
- private void getAuthorization() {
-
- }
}
diff --git a/src/main/java/net/jesterpm/podcastuploader/control/MetadataTask.java b/src/main/java/net/jesterpm/podcastuploader/control/MetadataTask.java
new file mode 100644
index 0000000..3b0b7c8
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/control/MetadataTask.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Jesse Morgan <jesse@jesterpm.net>
+ */
+
+package net.jesterpm.podcastuploader.control;
+
+import net.jesterpm.podcastuploader.model.Config;
+
+import net.jesterpm.podcastuploader.model.Metadata;
+import net.jesterpm.podcastuploader.ui.Action;
+import net.jesterpm.podcastuploader.ui.MetadataWindow;
+import net.jesterpm.podcastuploader.ui.UIFactory;
+
+import java.time.LocalDate;
+
+/**
+ * Controller for the MetadataWindow.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class MetadataTask {
+ private final Config mConfig;
+ private final UIFactory mUIFactory;
+ private final MetadataWindow mWin;
+ private Action mOnComplete;
+
+ public MetadataTask(final Config config, final UIFactory uiFactory) {
+ mConfig = config;
+ mUIFactory = uiFactory;
+ mWin = mUIFactory.createMetadataWindow();
+
+ mWin.addUploadAction(() -> {
+ Metadata metadata = getMetadata();
+ UploadTask task = new UploadTask(config, uiFactory, metadata);
+ mWin.setVisible(false);
+ task.run(mOnComplete);
+ });
+
+ mWin.addCancelAction(() -> {
+ mWin.setVisible(false);
+ mOnComplete.onAction();
+ });
+
+ mWin.addConfigButtonAction(() -> {
+ ConfigureTask task = new ConfigureTask(config, uiFactory);
+ task.run();
+ });
+
+ populateWindow();
+ }
+
+ private Metadata getMetadata() {
+ return new Metadata(
+ mWin.getDate(),
+ mWin.getTitle(),
+ mWin.getSeries(),
+ mWin.getSpeaker(),
+ mWin.getFile());
+ }
+
+ /**
+ * Set the fields in the configuration window.
+ */
+ private void populateWindow() {
+ mWin.setDate(LocalDate.now());
+ mWin.setTitle(mConfig.getProperty("lastTitle"));
+ mWin.setSeries(mConfig.getProperty("lastSeries"));
+ mWin.setSpeaker(mConfig.getProperty("lastSpeaker"));
+ }
+
+ /**
+ * Populate the config from the window.
+ */
+ private void updateConfig() {
+ mConfig.put("lastTitle", mWin.getTitle());
+ mConfig.put("lastSeries", mWin.getSeries());
+ mConfig.put("lastSpeaker", mWin.getSpeaker());
+ }
+
+ /**
+ * Display the window.
+ */
+ public void run(final Action onComplete) {
+ mWin.setVisible(true);
+ mOnComplete = onComplete;
+ }
+}
diff --git a/src/main/java/net/jesterpm/podcastuploader/control/PublishPodcastTask.java b/src/main/java/net/jesterpm/podcastuploader/control/PublishPodcastTask.java
index fdee900..f843b2b 100644
--- a/src/main/java/net/jesterpm/podcastuploader/control/PublishPodcastTask.java
+++ b/src/main/java/net/jesterpm/podcastuploader/control/PublishPodcastTask.java
@@ -6,7 +6,7 @@ package net.jesterpm.podcastuploader.control;
import java.util.Map;
-import net.jesterpm.podcastuploader.config.Config;
+import net.jesterpm.podcastuploader.model.Config;
/**
* Task to publish podcast meta-data to a server.
diff --git a/src/main/java/net/jesterpm/podcastuploader/control/S3UploadTask.java b/src/main/java/net/jesterpm/podcastuploader/control/S3UploadTask.java
index 8e9f9ab..9ffe537 100644
--- a/src/main/java/net/jesterpm/podcastuploader/control/S3UploadTask.java
+++ b/src/main/java/net/jesterpm/podcastuploader/control/S3UploadTask.java
@@ -9,7 +9,7 @@ import com.amazonaws.services.s3.AmazonS3Client;
import java.util.concurrent.ExecutorService;
-import net.jesterpm.podcastuploader.config.Config;
+import net.jesterpm.podcastuploader.model.Config;
/**
* Task for uploading a single file to S3.
@@ -41,9 +41,9 @@ public class S3UploadTask extends ObservableTask implements Runnable {
mExecutor = executor;
mClient = new AmazonS3Client(new BasicAWSCredentials(
- appConfig.get("AWSAccessKeyId"), appConfig.get("AWSSecretKey")));
+ appConfig.getProperty("AWSAccessKeyId"), appConfig.getProperty("AWSSecretKey")));
- mBucket = appConfig.get("S3Bucket");
+ mBucket = appConfig.getProperty("S3Bucket");
mLocalFile = localFile;
mS3Key = remoteFile;
diff --git a/src/main/java/net/jesterpm/podcastuploader/model/Config.java b/src/main/java/net/jesterpm/podcastuploader/model/Config.java
new file mode 100644
index 0000000..9f90fb6
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/model/Config.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012 Jesse Morgan
+ */
+
+package net.jesterpm.podcastuploader.model;
+
+import java.io.*;
+
+import java.text.ParseException;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+
+/**
+ * Configuration and metadata parser.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class Config extends Properties {
+ private static final String COMMENT = "Podcast Uploader Configuration";
+
+ private final String mFilename;
+
+ /**
+ * Create a new Config object based on the given file.
+ */
+ public Config(final String filename) throws IOException {
+ mFilename = filename;
+ load();
+ }
+
+ /**
+ * Load the config file.
+ *
+ * @throws IOException
+ */
+ public void load() throws IOException {
+ try {
+ final FileReader reader = new FileReader(mFilename);
+ load(reader);
+ } catch (FileNotFoundException e) {
+ // No big deal, just start with an empty config.
+ }
+ }
+
+ /**
+ * Save the config file.
+ *
+ * @throws IOException
+ */
+ public void save() throws IOException {
+ final FileWriter writer = new FileWriter(mFilename);
+ store(writer, COMMENT);
+ }
+}
diff --git a/src/main/java/net/jesterpm/podcastuploader/model/Metadata.java b/src/main/java/net/jesterpm/podcastuploader/model/Metadata.java
new file mode 100644
index 0000000..3cc7c0f
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/model/Metadata.java
@@ -0,0 +1,43 @@
+package net.jesterpm.podcastuploader.model;
+
+import java.io.File;
+import java.time.LocalDate;
+
+/**
+ * Podcast Metadata.
+ */
+public class Metadata {
+ private final LocalDate mDate;
+ private final String mTitle;
+ private final String mSeries;
+ private final String mSpeaker;
+ private final File mFile;
+
+ public Metadata(LocalDate date, String title, String series, String speaker, File file) {
+ this.mDate = date;
+ this.mTitle = title;
+ this.mSeries = series;
+ this.mSpeaker = speaker;
+ this.mFile = file;
+ }
+
+ public LocalDate getDate() {
+ return mDate;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getSeries() {
+ return mSeries;
+ }
+
+ public String getSpeaker() {
+ return mSpeaker;
+ }
+
+ public File getFile() {
+ return mFile;
+ }
+}
diff --git a/src/main/java/net/jesterpm/podcastuploader/ui/Action.java b/src/main/java/net/jesterpm/podcastuploader/ui/Action.java
index 5ec4bde..10ea248 100644
--- a/src/main/java/net/jesterpm/podcastuploader/ui/Action.java
+++ b/src/main/java/net/jesterpm/podcastuploader/ui/Action.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Jesse Morgan
+ * Copyright 2012-2017 Jesse Morgan <jesse@jesterpm.net>
*/
package net.jesterpm.podcastuploader.ui;
@@ -9,9 +9,10 @@ package net.jesterpm.podcastuploader.ui;
*
* @author Jesse Morgan <jesse@jesterpm.net>
*/
+@FunctionalInterface
public interface Action {
/**
* This method is called when the action is performed.
*/
- public void onAction();
+ void onAction();
}
diff --git a/src/main/java/net/jesterpm/podcastuploader/ui/ConfigurationWindow.java b/src/main/java/net/jesterpm/podcastuploader/ui/ConfigurationWindow.java
index d0dc96d..dece665 100644
--- a/src/main/java/net/jesterpm/podcastuploader/ui/ConfigurationWindow.java
+++ b/src/main/java/net/jesterpm/podcastuploader/ui/ConfigurationWindow.java
@@ -28,8 +28,6 @@ public class ConfigurationWindow extends JFrame {
private final JTextField mAWSKey;
private final JTextField mAWSSecret;
private final JTextField mS3Bucket;
- private final JTextField mMetadataServer;
- private final JButton mAuthorize;
private final JButton mSave;
public ConfigurationWindow() {
@@ -43,8 +41,6 @@ public class ConfigurationWindow extends JFrame {
mAWSKey = new JTextField();
mAWSSecret = new JTextField();
mS3Bucket = new JTextField();
- mMetadataServer = new JTextField();
- mAuthorize = new JButton("Authorize App");
mSave = new JButton("Save");
mSave.setDefaultCapable(true);
@@ -59,9 +55,6 @@ public class ConfigurationWindow extends JFrame {
fieldConstraint.fill = GridBagConstraints.HORIZONTAL;
fieldConstraint.weightx = 1;
- panel.add(new JLabel("Podcast Server:", JLabel.RIGHT), labelConstraint);
- panel.add(mMetadataServer, fieldConstraint);
-
panel.add(new JLabel("AWS Access Key:", JLabel.RIGHT), labelConstraint);
panel.add(mAWSKey, fieldConstraint);
@@ -72,11 +65,6 @@ public class ConfigurationWindow extends JFrame {
panel.add(mS3Bucket, fieldConstraint);
GridBagConstraints buttonConstraint = new GridBagConstraints();
- buttonConstraint.gridy = 5;
- buttonConstraint.gridwidth = 2;
- buttonConstraint.weighty = 1;
- panel.add(mAuthorize, buttonConstraint);
-
buttonConstraint.gridx = 1;
buttonConstraint.gridy = 7;
buttonConstraint.gridwidth = 1;
@@ -114,37 +102,8 @@ public class ConfigurationWindow extends JFrame {
return mS3Bucket.getText();
}
- public void setMetadataServer(final String value) {
- mMetadataServer.setText(value);
- }
-
- public String getMetadataServer() {
- return mMetadataServer.getText();
- }
-
- public void setHasAuthKey(final boolean value) {
- if (value) {
- mAuthorize.setText("Reauthorize App");
-
- } else {
- mAuthorize.setText("Authorize App");
- }
- }
-
- public void addAuthorizeAction(final Action a) {
- mAuthorize.addActionListener(new ActionListener() {
- public void actionPerformed(final ActionEvent e) {
- a.onAction();
- }
- });
- }
-
public void addSaveAction(final Action a) {
- mSave.addActionListener(new ActionListener() {
- public void actionPerformed(final ActionEvent e) {
- a.onAction();
- }
- });
+ mSave.addActionListener(e -> a.onAction());
}
public void addCancelAction(final Action a) {
diff --git a/src/main/java/net/jesterpm/podcastuploader/ui/GUIFactory.java b/src/main/java/net/jesterpm/podcastuploader/ui/GUIFactory.java
new file mode 100644
index 0000000..61562dd
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/ui/GUIFactory.java
@@ -0,0 +1,21 @@
+package net.jesterpm.podcastuploader.ui;
+
+/**
+ * Implementation of UIFactory which produces a GUI.
+ */
+public class GUIFactory implements UIFactory {
+ @Override
+ public MetadataWindow createMetadataWindow() {
+ return new MetadataWindow();
+ }
+
+ @Override
+ public ProgressWindow createProcessWindow() {
+ return new ProgressWindow();
+ }
+
+ @Override
+ public ConfigurationWindow createConfigurationWindow() {
+ return new ConfigurationWindow();
+ }
+}
diff --git a/src/main/java/net/jesterpm/podcastuploader/ui/MetadataWindow.java b/src/main/java/net/jesterpm/podcastuploader/ui/MetadataWindow.java
new file mode 100644
index 0000000..4be37f4
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/ui/MetadataWindow.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2017 Jesse Morgan <jesse@jesterpm.net>
+ */
+
+package net.jesterpm.podcastuploader.ui;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.io.File;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+/**
+ * UI for gathering podcast metadata.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class MetadataWindow extends JFrame {
+
+ private final JTextField mDate;
+ private final JTextField mTitle;
+ private final JTextField mSeries;
+ private final JTextField mSpeaker;
+
+ private File mFile;
+ private final JTextField mFileField;
+ private final JButton mFileButton;
+
+ private final JButton mConfigButton;
+ private final JButton mUploadButton;
+
+ public MetadataWindow() {
+ super("Podcast Metadata");
+
+ JPanel panel = new JPanel();
+ panel.setBorder(new EmptyBorder(10, 10, 10, 10));
+ panel.setLayout(new GridBagLayout());
+ add(panel);
+
+ mDate = new JTextField();
+ mTitle = new JTextField();
+ mSeries = new JTextField();
+ mSpeaker = new JTextField();
+
+ mFileField = new JTextField();
+ mFileField.setEnabled(false);
+ mFileField.setColumns(40);
+
+ mFileButton = new JButton("Browse");
+ mFileButton.addActionListener(e -> chooseFile());
+
+ mConfigButton = new JButton("Configure");
+
+ mUploadButton = new JButton("Upload");
+ mUploadButton.setDefaultCapable(true);
+
+ GridBagConstraints labelConstraint = new GridBagConstraints();
+ labelConstraint.gridx = 0;
+ labelConstraint.gridy = GridBagConstraints.RELATIVE;
+
+ GridBagConstraints fieldConstraint = new GridBagConstraints();
+ fieldConstraint.gridx = 1;
+ fieldConstraint.gridy = GridBagConstraints.RELATIVE;
+ fieldConstraint.fill = GridBagConstraints.HORIZONTAL;
+ fieldConstraint.weightx = 1;
+ fieldConstraint.gridwidth = 2;
+
+ panel.add(new JLabel("Date:", JLabel.RIGHT), labelConstraint);
+ panel.add(mDate, fieldConstraint);
+
+ panel.add(new JLabel("Title:", JLabel.RIGHT), labelConstraint);
+ panel.add(mTitle, fieldConstraint);
+
+ panel.add(new JLabel("Series:", JLabel.RIGHT), labelConstraint);
+ panel.add(mSeries, fieldConstraint);
+
+ panel.add(new JLabel("Speaker:", JLabel.RIGHT), labelConstraint);
+ panel.add(mSpeaker, fieldConstraint);
+
+ panel.add(new JLabel("File:", JLabel.RIGHT), labelConstraint);
+
+ GridBagConstraints fileFieldConstraint = new GridBagConstraints();
+ fileFieldConstraint.gridx = 1;
+ fileFieldConstraint.gridy = 4;
+ fileFieldConstraint.fill = GridBagConstraints.HORIZONTAL;
+ fileFieldConstraint.weightx = 1;
+ panel.add(mFileField, fileFieldConstraint);
+
+ GridBagConstraints fileButtonConstraint = new GridBagConstraints();
+ fileButtonConstraint.gridx = 2;
+ fileButtonConstraint.gridy = 4;
+ panel.add(mFileButton, fileButtonConstraint);
+
+ GridBagConstraints buttonConstraint = new GridBagConstraints();
+ buttonConstraint.gridx = 0;
+ buttonConstraint.gridy = 7;
+ buttonConstraint.gridwidth = 1;
+ buttonConstraint.anchor = GridBagConstraints.LAST_LINE_END;
+ panel.add(mConfigButton, buttonConstraint);
+
+ buttonConstraint.gridx = 1;
+ buttonConstraint.gridwidth = 2;
+ buttonConstraint.gridy = 7;
+ buttonConstraint.anchor = GridBagConstraints.LAST_LINE_END;
+ panel.add(mUploadButton, buttonConstraint);
+
+ pack();
+ Dimension d = getPreferredSize();
+ d.height += 20;
+ d.width += 50;
+ setMinimumSize(d);
+ }
+
+ public void setDate(final LocalDate value) {
+ mDate.setText(value.format(DateTimeFormatter.ISO_DATE));
+ }
+
+ public LocalDate getDate() {
+ return LocalDate.parse(mDate.getText(), DateTimeFormatter.ISO_DATE);
+ }
+
+ public void setTitle(final String value) {
+ mTitle.setText(value);
+ }
+
+ public String getTitle() {
+ return mTitle.getText();
+ }
+
+ public void setSeries(final String value) {
+ mSeries.setText(value);
+ }
+
+ public String getSeries() {
+ return mSeries.getText();
+ }
+
+ public void setSpeaker(final String value) {
+ mSpeaker.setText(value);
+ }
+
+ public File getFile() {
+ return mFile;
+ }
+
+ public void setFile(final File value) {
+ mFile = value;
+ mSpeaker.setText(value.getAbsolutePath());
+ }
+
+ public String getSpeaker() {
+ return mSpeaker.getText();
+ }
+
+ public void addUploadAction(final Action a) {
+ mUploadButton.addActionListener(e -> a.onAction());
+ }
+
+ public void addConfigButtonAction(final Action a) {
+ mConfigButton.addActionListener(e -> a.onAction());
+ }
+
+ public void addCancelAction(final Action a) {
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(final WindowEvent e) {
+ a.onAction();
+ }
+ });
+ }
+
+ private void chooseFile() {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setFileFilter(new FileNameExtensionFilter("Audio Files", "mp3", "wav"));
+ int result = chooser.showDialog(this, "Select");
+ if (result == JFileChooser.APPROVE_OPTION) {
+ setFile(chooser.getSelectedFile());
+ }
+ }
+}
diff --git a/src/main/java/net/jesterpm/podcastuploader/ui/UIFactory.java b/src/main/java/net/jesterpm/podcastuploader/ui/UIFactory.java
new file mode 100644
index 0000000..8239f85
--- /dev/null
+++ b/src/main/java/net/jesterpm/podcastuploader/ui/UIFactory.java
@@ -0,0 +1,13 @@
+package net.jesterpm.podcastuploader.ui;
+
+/**
+ * Factory which produces various views.
+ */
+public interface UIFactory {
+
+ MetadataWindow createMetadataWindow();
+
+ ProgressWindow createProcessWindow();
+
+ ConfigurationWindow createConfigurationWindow();
+}