Import bookmarks and identities from the TeamSpeak 3 client

The official client keeps what a myTeamSpeak account synchronises as protobuf
Item_Data blobs in the ProtobufItems table of settings.db. Decode them, adopt
them on first run when we have no identity yet, and offer a re-import from
Manage Bookmarks and Identities. The settings.db reader moves from contacts to
a new teamspeak package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 19:02:52 +00:00
parent bab89f9b50
commit dbb425403a
20 changed files with 805 additions and 76 deletions

View File

@@ -3,6 +3,9 @@ package com.ts3client.ui;
import com.ts3client.config.Bookmark;
import com.ts3client.config.Bookmarks;
import com.ts3client.config.IdentityStore;
import com.ts3client.config.Settings;
import com.ts3client.teamspeak.TeamSpeakImporter;
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
import javax.swing.BorderFactory;
import javax.swing.Box;
@@ -26,6 +29,7 @@ import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.io.File;
import java.util.function.Consumer;
/**
@@ -36,6 +40,8 @@ import java.util.function.Consumer;
public final class BookmarksDialog extends JDialog {
private final Bookmarks bookmarks;
private final IdentityStore identities;
private final Settings settings;
private final Consumer<Bookmark> onConnect;
private final Runnable onChanged;
private final DefaultListModel<Bookmark> listModel = new DefaultListModel<>();
@@ -56,10 +62,12 @@ public final class BookmarksDialog extends JDialog {
/** Bookmark the form currently shows, so edits can be flushed back into it. */
private Bookmark editing;
public BookmarksDialog(Frame owner, Bookmarks bookmarks, IdentityStore identities,
public BookmarksDialog(Frame owner, Bookmarks bookmarks, IdentityStore identities, Settings settings,
Consumer<Bookmark> onConnect, Runnable onChanged) {
super(owner, "Manage Bookmarks", true);
this.bookmarks = bookmarks;
this.identities = identities;
this.settings = settings;
this.onConnect = onConnect;
this.onChanged = onChanged;
this.identity = new IdentityChooser(identities, true, "");
@@ -151,17 +159,23 @@ public final class BookmarksDialog extends JDialog {
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
addButton(buttons, "Add", this::addBookmark);
addButton(buttons, "Remove", this::removeSelected);
if (TeamSpeakSettingsDb.locate() != null) {
addButton(buttons, "Import from TeamSpeak 3…", this::importFromTeamSpeak)
.setToolTipText("Add the bookmarks of the TeamSpeak 3 client installed on this computer, "
+ "along with the identities they connect with");
}
buttons.add(Box.createHorizontalGlue());
addButton(buttons, "Connect", this::connectSelected);
addButton(buttons, "Close", this::closeDialog);
return buttons;
}
private void addButton(JPanel panel, String text, Runnable action) {
private JButton addButton(JPanel panel, String text, Runnable action) {
JButton b = new JButton(text);
b.addActionListener(e -> action.run());
panel.add(b);
panel.add(Box.createHorizontalStrut(4));
return b;
}
// ---- Form <-> model ----
@@ -228,6 +242,26 @@ public final class BookmarksDialog extends JDialog {
persist();
}
private void importFromTeamSpeak() {
File db = TeamSpeakSettingsDb.locate();
if (db == null) return;
commit();
try {
TeamSpeakImporter.Result result = new TeamSpeakImporter(identities, bookmarks, settings).importBookmarks(db);
identity.reload();
reload();
if (result.bookmarksAdded() > 0) list.setSelectedIndex(listModel.size() - result.bookmarksAdded());
persist();
JOptionPane.showMessageDialog(this, result.bookmarks() + " bookmark(s) read from " + db
+ "\n" + result.bookmarksAdded() + " new, " + result.bookmarksKnown() + " already present; "
+ result.identitiesAdded() + " identit" + (result.identitiesAdded() == 1 ? "y" : "ies") + " imported.",
"Import from TeamSpeak 3", JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Could not read " + db + ":\n" + e.getMessage(),
"Import from TeamSpeak 3", JOptionPane.ERROR_MESSAGE);
}
}
private void connectSelected() {
commit();
Bookmark b = list.getSelectedValue();