package io.swtc; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamCompositeDriver; import com.github.sarxos.webcam.WebcamDevice; import com.github.sarxos.webcam.ds.buildin.WebcamDefaultDriver; import com.github.sarxos.webcam.ds.ipcam.*; import io.swtc.networking.CameraConfig; import io.swtc.networking.CameraSettings; import io.swtc.proccessing.ui.IconSetter; import io.swtc.proccessing.ui.ShowError; import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.util.List; public class SwingCCTVManager { private static final String TIMEOUT_MS = "1500"; private static final int REFRESH_INTERVAL = 30000; private static final String STATUS_ONLINE = "ONLINE"; private static final String STATUS_OFFLINE = "OFFLINE"; static { System.setProperty("ipcam.connection.timeout", TIMEOUT_MS); Webcam.setDriver(new WebcamCompositeDriver() {{ add(new WebcamDefaultDriver()); add(new IpCamDriver()); }}); loadSavedCameras(); } private final JFrame frame; private final JTable deviceTable; private final DefaultTableModel tableModel; private final SwingIFrame IFrame; private boolean isRefreshing = false; public SwingCCTVManager() { frame = new JFrame("Dashboard"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(1100, 600); frame.setIconImage(IconSetter.getIcon()); this.IFrame = new SwingIFrame(); this.IFrame.show(); tableModel = new DefaultTableModel(new String[]{"Status", "Device Name", "Type", "Resolution", "Address"}, 0) { @Override public boolean isCellEditable(int r, int c) { return false; } }; deviceTable = new JTable(tableModel); setupTableAppearance(); setupDragAndDrop(); // Initialize DND setupListeners(); JToolBar toolBar = new JToolBar(); setupBrandedToolbar(toolBar); frame.add(toolBar, BorderLayout.SOUTH); frame.add(new JScrollPane(deviceTable), BorderLayout.CENTER); startAutoRefresh(); refreshTable(); } private void setupBrandedToolbar(JToolBar toolBar) { Image ogIcon = IconSetter.getIcon(); if (ogIcon != null) { Image scaledIcon = ogIcon.getScaledInstance(32, 32, Image.SCALE_SMOOTH); toolBar.add(new JLabel(new ImageIcon(scaledIcon))); } toolBar.add(new JLabel("SWT-CCTV")); toolBar.add(Box.createRigidArea(new Dimension(10, 0))); toolBar.addSeparator(new Dimension(10, 32)); JButton btnAdd = new JButton("Add IP Cam"); JButton btnImport = new JButton("Import Config"); // New Import Button JButton btnLaunch = new JButton("Launch Stream"); btnAdd.setPreferredSize(new Dimension(100, 32)); btnImport.setPreferredSize(new Dimension(110, 32)); btnLaunch.setPreferredSize(new Dimension(120, 32)); toolBar.add(Box.createRigidArea(new Dimension(5, 0))); toolBar.add(btnAdd); toolBar.add(btnImport); toolBar.addSeparator(); toolBar.add(btnLaunch); btnAdd.addActionListener(e -> showAddCameraDialog()); btnImport.addActionListener(e -> showImportDialog()); btnLaunch.addActionListener(e -> launchSelected()); } private void setupDragAndDrop() { deviceTable.setDragEnabled(true); deviceTable.setTransferHandler(new TransferHandler() { @Override public boolean canImport(TransferSupport support) { return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor); } @Override public boolean importData(TransferSupport support) { if (!canImport(support)) return false; try { @SuppressWarnings("unchecked") List files = (List) support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); for (File file : files) { if (file.getName().endsWith(".json")) { List configs = CameraSettings.loadFromFile(file); importCameras(configs); } } return true; } catch (Exception e) { return false; } } }); } private void showImportDialog() { JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Select Camera Configuration JSON"); if (chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) { File file = chooser.getSelectedFile(); List configs = CameraSettings.loadFromFile(file); importCameras(configs); } } private void importCameras(List configs) { if (configs.isEmpty()) { ShowError.warning(frame, "Import Empty", "No valid camera configurations found in file."); return; } for (CameraConfig config : configs) { try { if (!IpCamDeviceRegistry.isRegistered(config.getName())) { IpCamDeviceRegistry.register(config.getName(), config.getUrl(), IpCamMode.PUSH); CameraSettings.save(config); } } catch (Exception ignored) {} } refreshTable(); } private void setupTableAppearance() { deviceTable.setRowHeight(30); deviceTable.getColumnModel().getColumn(0).setMaxWidth(80); deviceTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable t, Object v, boolean s, boolean f, int r, int c) { Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c); setHorizontalAlignment(JLabel.CENTER); comp.setForeground(STATUS_ONLINE.equals(v) ? new Color(0, 150, 0) : Color.RED); return comp; } }); } private synchronized void refreshTable() { if (isRefreshing) return; isRefreshing = true; final int selectedRow = deviceTable.getSelectedRow(); new SwingWorker() { @Override protected Void doInBackground() { for (WebcamDevice device : new WebcamDefaultDriver().getDevices()) { Dimension res = (device.getResolutions().length > 0) ? device.getResolutions()[0] : new Dimension(0,0); publish(new Object[]{STATUS_ONLINE, device.getName(), "USB Hardware", res.width + "x" + res.height, "Local"}); } for (IpCamDevice ipDevice : IpCamDeviceRegistry.getIpCameras()) { publish(probeIpCamera(ipDevice)); } return null; } @Override protected void process(List chunks) { if (!Boolean.TRUE.equals(frame.getRootPane().getClientProperty("cleared"))) { tableModel.setRowCount(0); frame.getRootPane().putClientProperty("cleared", true); } for (Object[] row : chunks) tableModel.addRow(row); } @Override protected void done() { frame.getRootPane().putClientProperty("cleared", null); if (selectedRow != -1 && selectedRow < tableModel.getRowCount()) { deviceTable.setRowSelectionInterval(selectedRow, selectedRow); } isRefreshing = false; } }.execute(); } private Object[] probeIpCamera(IpCamDevice ipDevice) { String status = STATUS_OFFLINE; String resText = "N/A"; try { URL url = ipDevice.getURL(); try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(url.getHost(), url.getPort() != -1 ? url.getPort() : 80), 800); Dimension d = ipDevice.getResolution(); if (d != null) { status = STATUS_ONLINE; resText = d.width + "x" + d.height; } } } catch (Exception e) { status = STATUS_OFFLINE; } return new Object[]{status, ipDevice.getName(), "IP Stream", resText, "Network"}; } private void launchSelected() { int row = deviceTable.getSelectedRow(); if (row == -1) return; String name = (String) tableModel.getValueAt(row, 1); if (STATUS_OFFLINE.equals(tableModel.getValueAt(row, 0))) { ShowError.warning(frame, "Device Offline", "Cannot connect to '" + name + "'."); return; } new Thread(() -> { Webcam selected = Webcam.getWebcams().stream().filter(w -> w.getName().equals(name)).findFirst().orElse(null); if (selected != null) IFrame.addCameraInternalFrame(selected); }).start(); } private void showAddCameraDialog() { JPanel p = new JPanel(new GridLayout(2, 2, 5, 5)); JTextField n = new JTextField(); JTextField u = new JTextField(); p.add(new JLabel("Name:")); p.add(n); p.add(new JLabel("URL:")); p.add(u); if (JOptionPane.showConfirmDialog(frame, p, "Register IP Camera", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { importCameras(List.of(new CameraConfig(n.getText(), u.getText()))); } } private void setupListeners() { deviceTable.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { deviceTable.requestFocusInWindow(); if (e.getClickCount() == 2 && deviceTable.getSelectedRow() != -1) launchSelected(); if (SwingUtilities.isRightMouseButton(e)) showContextMenu(e); } }); } private void showContextMenu(MouseEvent e) { int row = deviceTable.rowAtPoint(e.getPoint()); if (row >= 0) deviceTable.setRowSelectionInterval(row, row); JPopupMenu menu = new JPopupMenu(); JMenuItem launch = new JMenuItem("Launch Live Stream"); launch.addActionListener(al -> launchSelected()); menu.add(launch); menu.show(deviceTable, e.getX(), e.getY()); } private static void loadSavedCameras() { for (CameraConfig config : CameraSettings.load()) { try { IpCamDeviceRegistry.register(config.getName(), config.getUrl(), IpCamMode.PUSH); } catch (Exception ignored) {} } } private void startAutoRefresh() { new Timer(REFRESH_INTERVAL, e -> refreshTable()).start(); } public void open() { frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new SwingCCTVManager().open()); } }