package de.zigapeda.flowspring;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.Painter;
import javax.swing.UIManager;
import jxgrabkey.HotkeyConflictException;
import jxgrabkey.HotkeyListener;
import jxgrabkey.JXGrabKey;
import jxgrabkey.X11KeysymDefinitions;
import uk.co.caprica.vlcj.discovery.NativeDiscovery;
import uk.co.caprica.vlcj.version.LibVlcVersion;
import com.melloware.jintellitype.IntellitypeListener;
import com.melloware.jintellitype.JIntellitype;
import de.zigapeda.flowspring.controller.Database;
import de.zigapeda.flowspring.controller.Settings;
import de.zigapeda.flowspring.controller.Tagreader;
import de.zigapeda.flowspring.data.PlaylistTrack;
import de.zigapeda.flowspring.data.Title;
import de.zigapeda.flowspring.gui.MainWindow;
import de.zigapeda.flowspring.gui.ReadWindow;
import de.zigapeda.flowspring.gui.Splash;
public class Main {
private static final String DIRECTORY = "/.flowspring/";
// private static final String DIRECTORY = "/.flowspringdev/";
private static Database database;
private static MainWindow window;
private static ReadWindow readwindow;
private static JFrame ontopwindow;
private static String pid;
private static Splash splash;
private static String appdata;
public static void main(String[] args) {
if(Main.checkInstance() == false) {
Main.showSplash();
Main.setupAppdataDir();
Main.setupLookandfeel();
// new File(appdata + "flowspring.lck").delete();
// new File(appdata + "flowspring.log").delete();
// new File(appdata + "flowspring.properties").delete();
// new File(appdata + "flowspring.script").delete();
Main.setupDatabase();
Main.setupVLC();
Main.setupApplication();
Main.setupMediakeylistener();
Main.window.setVisible(true);
Main.splash.setVisible(false);
Main.checkArgs(args);
Main.setupOpenlistener();
} else {
Main.createOpenfile(args);
}
}
public static Connection getDatabase() {
return Main.database.getConnection();
}
public static MainWindow getWindow() {
return Main.window;
}
public static ReadWindow getReadWindow() {
return Main.readwindow;
}
public static void setReadWindow(ReadWindow window) {
Main.readwindow = window;
}
public static String getAppdata() {
return Main.appdata;
}
private static boolean checkInstance() {
File temppath = new File(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + DIRECTORY);
if(temppath.exists() == false) {
temppath.mkdir();
}
File pidfile = new File(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + DIRECTORY + "pid.lock");
boolean running = false;
if(pidfile.exists()) {
String pid = null;
String exec;
try {
BufferedReader br = new BufferedReader(new FileReader(pidfile));
pid = br.readLine();
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if(System.getProperty("os.name").toLowerCase().startsWith("windows")) {
exec = System.getenv("windir") +"/system32/"+"tasklist.exe";
} else {
exec = "ps -e";
}
if(pid != null) {
try {
String line;
Process p = Runtime.getRuntime().exec(exec);
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
if(line.contains(" " + pid + " ")) {
running = true;
Main.pid = pid;
}
}
input.close();
} catch (Exception err) {
err.printStackTrace();
}
}
}
if(running == false) {
String pid = ManagementFactory.getRuntimeMXBean().getName();
Main.pid = pid.substring(0, pid.indexOf("@"));
FileWriter fw;
try {
fw = new FileWriter(pidfile);
fw.write(Main.pid);
fw.close();
pidfile.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
return running;
}
private static void showSplash() {
Main.splash = new Splash();
}
private static void setupDatabase() {
Main.database = new Database();
}
private static void setupVLC() {
String vlcPath = Settings.loadSettings("vlc");
if(vlcPath == null) {
if(new NativeDiscovery().discover() == false) {
if(System.getProperty("os.name").toLowerCase().startsWith("windows")) {
int opt = JOptionPane.showConfirmDialog(null,
"The player can't find an installation of VLC. May flowspring download VLC automatically?", "No VLC found",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if(opt == JOptionPane.YES_OPTION) {
JOptionPane.showMessageDialog(null,
"This can take a few minutes. flowspring will start automatically after the download is succeeded.", "Download VLC",
JOptionPane.PLAIN_MESSAGE);
if(downloadVlc() == true) {
Settings.saveSettings("vlc", "vlc");
System.setProperty("jna.library.path", "vlc");
}
} else {
JFileChooser chooser = new JFileChooser(".");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if(chooser.showDialog(null, "Select") == JFileChooser.APPROVE_OPTION) {
Settings.saveSettings("vlc", chooser.getSelectedFile().toString());
System.setProperty("jna.library.path", chooser.getSelectedFile().toString());
}
}
} else {
JOptionPane.showMessageDialog(null,
"The player can't find an installation of VLC. May you select the path where VLC is installed.", "No VLC found",
JOptionPane.PLAIN_MESSAGE);
JFileChooser chooser = new JFileChooser(".");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if(chooser.showDialog(null, "Select") == JFileChooser.APPROVE_OPTION) {
Settings.saveSettings("vlc", chooser.getSelectedFile().toString());
System.setProperty("jna.library.path", chooser.getSelectedFile().toString());
}
}
} else {
Settings.saveSettings("vlc", "discovery");
}
} else {
if(vlcPath.equals("discovery")) {
new NativeDiscovery().discover();
} else {
System.setProperty("jna.library.path", vlcPath);
}
}
try {
LibVlcVersion.getVersion();
} catch (Throwable e) {
Settings.saveSettings("vlc", null);
JOptionPane.showMessageDialog(null,
"The player can't find an installation of VLC. Please restart flowspring to configure VLC again.", "No VLC found",
JOptionPane.WARNING_MESSAGE);
System.exit(0);
}
}
private static boolean downloadVlc() {
final String url = "http://download.videolan.org/pub/videolan/vlc/last/win";
try {
URL u;
if (System.getProperty("os.arch").contains("64")) {
u = new URL(url + "64/");
} else {
u = new URL(url + "32/");
}
URLConnection uc = u.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));
String s = null;
while ((s = br.readLine()) != null) {
if (s.contains("zip</a>")) {
int pos = s.indexOf("\"");
if (pos > -1) {
URL vlc = new URL(u.toString() + s.substring(pos + 1, s.indexOf("\"", pos + 1)));
try {
uc = vlc.openConnection();
ReadableByteChannel rbc = Channels.newChannel(uc.getInputStream());
FileOutputStream fos = new FileOutputStream("vlc.zip");
fos.getChannel().transferFrom(rbc, 0, Long.valueOf(s.substring(s.lastIndexOf(" ") + 1)));
fos.close();
return extractVlc();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean extractVlc() {
File destDir = new File("vlc");
if (!destDir.exists()) {
destDir.mkdir();
}
ZipFile zipFile;
try {
zipFile = new ZipFile("vlc.zip");
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryFileName = entry.getName();
if (!entry.isDirectory()) {
if(entryFileName.contains("plugins")) {
String dest = "vlc/" + entryFileName.substring(entryFileName.indexOf("plugins"));
int pos = dest.lastIndexOf("\\");
if(pos == -1) {
pos = dest.lastIndexOf("/");
}
File f = new File(dest.substring(0, pos));
if(!f.exists()) {
f.mkdirs();
}
extract(zipFile, entry, new File(dest));
} else if(entryFileName.endsWith("libvlc.dll")) {
extract(zipFile, entry, new File("vlc/libvlc.dll"));
} else if(entryFileName.endsWith("libvlccore.dll")) {
extract(zipFile, entry, new File("vlc/libvlccore.dll"));
}
}
}
zipFile.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private static void extract(ZipFile zipFile, ZipEntry entry, File file) {
try {
byte[] buffer = new byte[16384];
int len;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
while ((len = bis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
bos.flush();
bos.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void setupAppdataDir() {
if(System.getProperty("os.name").toLowerCase().startsWith("windows")) {
Main.appdata = new File(System.getenv("appdata")).getAbsolutePath();
} else {
Main.appdata = new File(System.getProperty("user.home")).getAbsolutePath();
}
Main.appdata = Main.appdata + DIRECTORY;
File f = new File(Main.appdata);
if(f.exists() == false) {
f.mkdir();
Main.copyResource("libJXGrabKey32.so");
Main.copyResource("libJXGrabKey64.so");
}
}
private static void copyResource(String resname) {
File file = new File(Main.appdata + resname);
if(!file.exists()) {
InputStream in = Main.class.getClass().getResourceAsStream("/de/zigapeda/flowspring/res/" + resname);
BufferedInputStream bufIn = new BufferedInputStream(in);
BufferedOutputStream bufOut = null;
try {
bufOut = new BufferedOutputStream(new FileOutputStream(Main.appdata + resname));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
byte[] inByte = new byte[4096];
int count = -1;
try {
while ((count = bufIn.read(inByte))!=-1) {
bufOut.write(inByte, 0, count);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
bufOut.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bufIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void setupLookandfeel() {
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.put("Tree.collapsedIcon", new ImageIcon(Main.class.getClass().getResource("/de/zigapeda/flowspring/res/collapsed.png")));
UIManager.put("Tree.expandedIcon", new ImageIcon(Main.class.getClass().getResource("/de/zigapeda/flowspring/res/expanded.png")));
UIManager.put("Tree[Enabled].collapsedIconPainter", new Painter<JComponent>() {
public void paint(Graphics2D g, JComponent object, int width,
int height) {
Path2D path = getCollapsedPath();
g.setPaint(new Color(0,0,0));
g.fill(path);
g.setPaint(new Color(0,255,0));
g.drawLine(3, 1, 3, 5);
g.drawLine(1, 3, 5, 3);
}
});
UIManager.put("Tree[Enabled].expandedIconPainter", new Painter<JComponent>() {
public void paint(Graphics2D g, JComponent object, int width,
int height) {
Path2D path = getExpandedPath();
g.setPaint(new Color(0,0,0));
g.fill(path);
g.setPaint(new Color(0,255,0));
g.drawLine(1, 3, 5, 3);
}
});
UIManager.put("Tree[Enabled+Selected].collapsedIconPainter", new Painter<JComponent>() {
public void paint(Graphics2D g, JComponent object, int width,
int height) {
Path2D path = getCollapsedPath();
g.setPaint(new Color(0,0,0));
g.fill(path);
g.setPaint(new Color(0,255,0));
g.drawLine(3, 1, 3, 5);
g.drawLine(1, 3, 5, 3);
}
});
UIManager.put("Tree[Enabled+Selected].expandedIconPainter", new Painter<JComponent>() {
public void paint(Graphics2D g, JComponent object, int width,
int height) {
Path2D path = getExpandedPath();
g.setPaint(new Color(0,0,0));
g.fill(path);
g.setPaint(new Color(0,255,0));
g.drawLine(1, 3, 5, 3);
}
});
UIManager.put("control", new Color(32,32,32));
UIManager.put("nimbusBase", new Color(16,16,16));
UIManager.put("nimbusBlueGrey", new Color(32,32,32));
UIManager.put("nimbusFocus", new Color(0,255,0));
UIManager.put("nimbusLightBackground", new Color(32,32,32));
UIManager.put("nimbusOrange", new Color(0,255,0));
UIManager.put("nimbusSelectedText", new Color(0,0,0));
UIManager.put("nimbusSelectionBackground", new Color(0,255,0));
UIManager.put("Table.gridColor", new Color(32,32,32));
UIManager.put("text", new Color(0,255,0));
} catch (Exception e) {
e.printStackTrace();
}
}
private static Path2D getCollapsedPath() {
Path2D path = new Path2D.Float();
path.reset();
path.moveTo(0, 2);
path.lineTo(0, 5);
path.lineTo(2, 5);
path.lineTo(2, 7);
path.lineTo(5, 7);
path.lineTo(5, 5);
path.lineTo(7, 5);
path.lineTo(7, 2);
path.lineTo(5, 2);
path.lineTo(5, 0);
path.lineTo(2, 0);
path.lineTo(2, 2);
path.lineTo(0, 2);
path.closePath();
return path;
}
private static Path2D getExpandedPath() {
Path2D path = new Path2D.Float();
path.reset();
path.moveTo(0, 2);
path.lineTo(7, 2);
path.lineTo(7, 5);
path.lineTo(0, 5);
path.lineTo(0, 2);
path.closePath();
return path;
}
private static void setupApplication() {
Main.window = new MainWindow();
if(Main.window.getControlllayout().getComponent(0) instanceof JComboBox<?>) {
((JComboBox<?>)Main.window.getControlllayout().getComponent(0)).setSelectedIndex(1);
}
}
private static void setupMediakeylistener() {
if(System.getProperty("os.name").toLowerCase().startsWith("windows")) {
try {
JIntellitype.getInstance().addIntellitypeListener(new IntellitypeListener() {
public void onIntellitype(int acommand) {
switch(acommand) {
case JIntellitype.APPCOMMAND_MEDIA_PLAY_PAUSE:
Main.getWindow().getPlayercontroller().play();
break;
case JIntellitype.APPCOMMAND_MEDIA_NEXTTRACK:
Main.getWindow().getPlayercontroller().next();
break;
case JIntellitype.APPCOMMAND_MEDIA_PREVIOUSTRACK:
Main.getWindow().getPlayercontroller().previous();
break;
case JIntellitype.APPCOMMAND_MEDIA_STOP:
Main.getWindow().getPlayercontroller().stop();
break;
}
}
});
} catch(Exception e) {
for(StackTraceElement b: e.getStackTrace()) {
System.out.println(b.toString());
}
}
} else {
try {
if(System.getProperty("os.arch").contains("64")) {
System.load(new File(Main.appdata + "libJXGrabKey64.so").getCanonicalPath());
} else if(System.getProperty("os.arch").contains("86") || System.getProperty("os.arch").contains("32")) {
System.load(new File(Main.appdata + "libJXGrabKey32.so").getCanonicalPath());
}
} catch (IOException e) {
e.printStackTrace();
}
try {
JXGrabKey.getInstance().registerX11Hotkey(0, 0, X11KeysymDefinitions.AUDIO_PLAY);
JXGrabKey.getInstance().registerX11Hotkey(1, 0, X11KeysymDefinitions.AUDIO_PAUSE);
JXGrabKey.getInstance().registerX11Hotkey(2, 0, X11KeysymDefinitions.AUDIO_NEXT);
JXGrabKey.getInstance().registerX11Hotkey(3, 0, X11KeysymDefinitions.AUDIO_PREV);
JXGrabKey.getInstance().registerX11Hotkey(4, 0, X11KeysymDefinitions.AUDIO_STOP);
} catch (HotkeyConflictException e) {
e.printStackTrace();
}
JXGrabKey.getInstance().addHotkeyListener(new HotkeyListener() {
public void onHotkey(int id) {
switch(id) {
case 0:
case 1:
Main.getWindow().getPlayercontroller().play();
break;
case 2:
Main.getWindow().getPlayercontroller().next();
break;
case 3:
Main.getWindow().getPlayercontroller().previous();
break;
case 4:
Main.getWindow().getPlayercontroller().stop();
break;
}
}
});
}
}
private static void setupOpenlistener() {
while (true) {
try {
Path dir = Paths.get(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + DIRECTORY);
WatchService watcher = dir.getFileSystem().newWatchService();
dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey watchkey;
watchkey = watcher.take();
List<WatchEvent<?>> events = watchkey.pollEvents();
for(WatchEvent<?> event : events) {
if(event.kind() == StandardWatchEventKinds.ENTRY_CREATE || event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
if(event.context().toString().equals("open.lock")) {
File f = new File(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + DIRECTORY + "open.lock");
if(f.exists()) {
BufferedReader br = new BufferedReader(new FileReader(f));
String line = br.readLine();
if(line.equals(Main.pid)) {
while((line = br.readLine()) != null) {
Title temp = new Tagreader(new File(line).toPath()).getTitle();
Main.window.getPlaylist().addTrack(new PlaylistTrack(temp.getArtist() + " - " + temp.getName(), temp.getInt(), line));
}
}
br.close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void createOpenfile(String[] args) {
if(args.length > 0) {
File of = new File(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + DIRECTORY + "open.lock");
try {
FileWriter fw = new FileWriter(of);
fw.write(Main.pid + "\n");
for(String s: args) {
fw.write(s + "\n");
}
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void checkArgs(String[] args) {
if(args.length > 0) {
for(String s: args) {
File file = new File(s);
if(file.exists()) {
Title temp = new Tagreader(file.toPath()).getTitle();
Main.window.getPlaylist().addTrack(new PlaylistTrack(temp.getArtist() + " - " + temp.getName(), temp.getInt(), s));
}
}
}
}
public static JFrame getOntopwindow() {
if(ontopwindow != null) {
if(ontopwindow.isShowing() == false) {
ontopwindow = null;
}
}
return ontopwindow;
}
public static void setOntopwindow(JFrame ontopwindow) {
Main.ontopwindow = ontopwindow;
}
}