diff --git a/README.md b/README.md index 0cb52d2..0038f1f 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ For example, memory_db would be set by adding `memory_db = true` under the line | `save_state` | boolean | true | save the state of the gui (active channels, tabs, expanded channels) | | `alt_menu` | boolean | false | keep the menu hidden unless revealed with alt key | | `hide_to_tray` | boolean | false | hide abaddon to the system tray on window close | +| `show_deleted_indicator` | boolean | true | show \[deleted\] indicator next to deleted messages instead of actually deleting the message | | `font_scale` | double | | scale font rendering. 1 is unchanged | #### style @@ -324,7 +325,13 @@ For example, memory_db would be set by adding `memory_db = true` under the line | Setting | Type | Default | Description | |---------|--------|------------------------------------|------------------------------------------------------------| -| vad | string | rnnoise if enabled, gate otherwise | Method used for voice activity detection. Changeable in UI | +| `vad` | string | rnnoise if enabled, gate otherwise | Method used for voice activity detection. Changeable in UI | + +#### windows + +| Setting | Type | Default | Description | +|---------------|---------|---------|-------------------------| +| `hideconsole` | boolean | true | Hide console on startup | ### Environment variables diff --git a/res/res/crunchyroll.png b/res/res/crunchyroll.png new file mode 100644 index 0000000..6cbbaeb Binary files /dev/null and b/res/res/crunchyroll.png differ diff --git a/res/res/e1e96d89e192de1997f73730db26e94f.png b/res/res/e1e96d89e192de1997f73730db26e94f.png new file mode 100644 index 0000000..76fec43 Binary files /dev/null and b/res/res/e1e96d89e192de1997f73730db26e94f.png differ diff --git a/res/res/ebay.png b/res/res/ebay.png new file mode 100644 index 0000000..2710a97 Binary files /dev/null and b/res/res/ebay.png differ diff --git a/res/res/instagram.png b/res/res/instagram.png new file mode 100644 index 0000000..e124966 Binary files /dev/null and b/res/res/instagram.png differ diff --git a/res/res/paypal.png b/res/res/paypal.png new file mode 100644 index 0000000..28718f1 Binary files /dev/null and b/res/res/paypal.png differ diff --git a/res/res/playstation.png b/res/res/playstation.png new file mode 100644 index 0000000..92bc8e1 Binary files /dev/null and b/res/res/playstation.png differ diff --git a/res/res/riotgames.png b/res/res/riotgames.png new file mode 100644 index 0000000..436ec17 Binary files /dev/null and b/res/res/riotgames.png differ diff --git a/res/res/tiktok.png b/res/res/tiktok.png new file mode 100644 index 0000000..7245cf6 Binary files /dev/null and b/res/res/tiktok.png differ diff --git a/res/res/twitter.png b/res/res/twitter.png index 610ae24..24ebc93 100644 Binary files a/res/res/twitter.png and b/res/res/twitter.png differ diff --git a/src/audio/manager.cpp b/src/audio/manager.cpp index 739e835..0336699 100644 --- a/src/audio/manager.cpp +++ b/src/audio/manager.cpp @@ -419,13 +419,23 @@ void AudioManager::OnCapturedPCM(const int16_t *pcm, ma_uint32 frames) { if (m_opus_buffer == nullptr || !m_should_capture) return; const double gain = m_capture_gain; - // i have a suspicion i can cast the const away... but i wont + std::vector new_pcm(pcm, pcm + frames * 2); for (auto &val : new_pcm) { const int32_t unclamped = static_cast(val * gain); val = std::clamp(unclamped, INT16_MIN, INT16_MAX); } + if (m_mix_mono) { + for (size_t i = 0; i < frames * 2; i += 2) { + const int sample_L = new_pcm[i]; + const int sample_R = new_pcm[i + 1]; + const int16_t mixed = static_cast((sample_L + sample_R) / 2); + new_pcm[i] = mixed; + new_pcm[i + 1] = mixed; + } + } + UpdateCaptureVolume(new_pcm.data(), frames); static std::array denoised_L; @@ -629,6 +639,14 @@ bool AudioManager::GetSuppressNoise() const { } #endif +void AudioManager::SetMixMono(bool value) { + m_mix_mono = value; +} + +bool AudioManager::GetMixMono() const { + return m_mix_mono; +} + AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() { return m_signal_opus_packet; } diff --git a/src/audio/manager.hpp b/src/audio/manager.hpp index 80a2542..5321cb5 100644 --- a/src/audio/manager.hpp +++ b/src/audio/manager.hpp @@ -87,6 +87,9 @@ public: bool GetSuppressNoise() const; #endif + void SetMixMono(bool value); + bool GetMixMono() const; + private: void OnCapturedPCM(const int16_t *pcm, ma_uint32 frames); @@ -144,6 +147,7 @@ private: std::atomic m_prob_threshold = 0.5; std::atomic m_vad_prob = 0.0; std::atomic m_enable_noise_suppression = false; + std::atomic m_mix_mono = false; std::unordered_set m_muted_ssrcs; std::unordered_map m_volume_ssrc; diff --git a/src/components/chatlist.cpp b/src/components/chatlist.cpp index 4dde8a3..a656a43 100644 --- a/src/components/chatlist.cpp +++ b/src/components/chatlist.cpp @@ -187,8 +187,15 @@ void ChatList::DeleteMessage(Snowflake id) { if (widget == m_id_to_widget.end()) return; auto *x = dynamic_cast(widget->second); - if (x != nullptr) - x->UpdateAttributes(); + + if (x != nullptr) { + if (Abaddon::Get().GetSettings().ShowDeletedIndicator) { + x->UpdateAttributes(); + } else { + RemoveMessageAndHeader(x); + m_id_to_widget.erase(id); + } + } } void ChatList::RefetchMessage(Snowflake id) { diff --git a/src/components/chatmessage.cpp b/src/components/chatmessage.cpp index 7163574..44396a2 100644 --- a/src/components/chatmessage.cpp +++ b/src/components/chatmessage.cpp @@ -1057,10 +1057,9 @@ void ChatMessageHeader::UpdateName() { const auto chan = discord.GetChannel(ChannelID); bool is_guild = chan.has_value() && chan->GuildID.has_value(); if (is_guild) { - const auto member = discord.GetMember(UserID, *chan->GuildID); const auto role_id = discord.GetMemberHoistedRole(*chan->GuildID, UserID, true); const auto role = discord.GetRole(role_id); - const auto name = GetEscapedDisplayName(*user, member); + const auto name = user->GetDisplayNameEscaped(*chan->GuildID); std::string md; if (role.has_value()) @@ -1090,13 +1089,6 @@ void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) { }); } -Glib::ustring ChatMessageHeader::GetEscapedDisplayName(const UserData &user, const std::optional &member) { - if (member.has_value() && !member->Nickname.empty()) - return Glib::Markup::escape_text(member->Nickname); - else - return Glib::Markup::escape_text(user.GetDisplayNameEscaped()); -} - bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) { if (ev->button == GDK_BUTTON_PRIMARY && (ev->state & GDK_SHIFT_MASK)) { m_signal_action_insert_mention.emit(); diff --git a/src/components/chatmessage.hpp b/src/components/chatmessage.hpp index 390e503..99fc872 100644 --- a/src/components/chatmessage.hpp +++ b/src/components/chatmessage.hpp @@ -84,7 +84,6 @@ public: protected: void AttachUserMenuHandler(Gtk::Widget &widget); - static Glib::ustring GetEscapedDisplayName(const UserData &user, const std::optional &member); bool on_author_button_press(GdkEventButton *ev); diff --git a/src/settings.cpp b/src/settings.cpp index f910ff7..c824a34 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -63,6 +63,7 @@ void SettingsManager::ReadSettings() { SMBOOL("gui", "unreads", Unreads); SMBOOL("gui", "alt_menu", AltMenu); SMBOOL("gui", "hide_to_tray", HideToTray); + SMBOOL("gui", "show_deleted_indicator", ShowDeletedIndicator); SMFLT("gui", "font_scale", FontScale); SMINT("http", "concurrent", CacheHTTPConcurrency); SMSTR("http", "user_agent", UserAgent); @@ -153,6 +154,7 @@ void SettingsManager::Close() { SMBOOL("gui", "unreads", Unreads); SMBOOL("gui", "alt_menu", AltMenu); SMBOOL("gui", "hide_to_tray", HideToTray); + SMBOOL("gui", "show_deleted_indicator", ShowDeletedIndicator); SMFLT("gui", "font_scale", FontScale); SMINT("http", "concurrent", CacheHTTPConcurrency); SMSTR("http", "user_agent", UserAgent); diff --git a/src/settings.hpp b/src/settings.hpp index 7c6368f..037233b 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -30,6 +30,7 @@ public: bool Unreads { true }; bool AltMenu { false }; bool HideToTray { false }; + bool ShowDeletedIndicator { true }; double FontScale { -1.0 }; // [http] diff --git a/src/startup.cpp b/src/startup.cpp index 06d6402..89e29a5 100644 --- a/src/startup.cpp +++ b/src/startup.cpp @@ -29,10 +29,22 @@ std::optional> ParseCookie(const Glib::ustri } std::optional GetJavascriptFileFromAppPage(const Glib::ustring &contents) { - auto regex = Glib::Regex::create(R"(app-mount.*(/assets/[\w\d]*.js).*/assets/[\w\d]*.js)"); + auto regex = Glib::Regex::create(R"(/assets/\w{20}.js)"); + std::vector matches; + + // regex->match_all doesnt work for some reason + int start_position = 0; Glib::MatchInfo match; - if (regex->match(contents, match)) { - return match.fetch(1); + while (regex->match(contents, start_position, match)) { + const auto str = match.fetch(0); + matches.push_back(str); + int foo; + match.fetch_pos(0, start_position, foo); + start_position += str.size(); + } + + if (matches.size() >= 6) { + return matches[matches.size() - 6]; } return {}; @@ -52,7 +64,7 @@ std::optional GetBuildNumberFromJSURL(const Glib::ustring &url, const auto res = req.execute(); if (res.error) return {}; - auto regex = Glib::Regex::create(R"("buildNumber",null!==\(t="(\d+)\"\))"); + auto regex = Glib::Regex::create("buildNumber:\"(\\d+)\""); Glib::MatchInfo match; Glib::ustring string = res.text; if (regex->match(string, match)) { diff --git a/src/windows/profile/userinfopane.cpp b/src/windows/profile/userinfopane.cpp index b6f0d59..ab4f50f 100644 --- a/src/windows/profile/userinfopane.cpp +++ b/src/windows/profile/userinfopane.cpp @@ -1,6 +1,34 @@ #include "userinfopane.hpp" #include +static std::string GetConnectionURL(const ConnectionData &conn) { + if (conn.Type == "github") { + return "https://github.com/" + conn.Name; + } else if (conn.Type == "steam") { + return "https://steamcommunity.com/profiles/" + conn.ID; + } else if (conn.Type == "twitch") { + return "https://twitch.tv/" + conn.Name; + } else if (conn.Type == "twitter") { + return "https://twitter.com/i/user/" + conn.ID; + } else if (conn.Type == "spotify") { + return "https://open.spotify.com/user/" + conn.ID; + } else if (conn.Type == "reddit") { + return "https://reddit.com/u/" + conn.Name; + } else if (conn.Type == "youtube") { + return "https://www.youtube.com/channel/" + conn.ID; + } else if (conn.Type == "facebook") { + return "https://www.facebook.com/" + conn.ID; + } else if (conn.Type == "ebay") { + return "https://www.ebay.com/usr/" + conn.Name; + } else if (conn.Type == "instagram") { + return "https://www.instagram.com/" + conn.Name; + } else if (conn.Type == "tiktok") { + return "https://www.tiktok.com/@" + conn.Name; + } + + return ""; +} + ConnectionItem::ConnectionItem(const ConnectionData &conn) : m_box(Gtk::ORIENTATION_HORIZONTAL) , m_name(conn.Name) { @@ -8,23 +36,7 @@ ConnectionItem::ConnectionItem(const ConnectionData &conn) try { pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + conn.Type + ".png"), 32, 32); } catch (const Glib::Exception &e) {} - std::string url; - if (conn.Type == "github") - url = "https://github.com/" + conn.Name; - else if (conn.Type == "steam") - url = "https://steamcommunity.com/profiles/" + conn.ID; - else if (conn.Type == "twitch") - url = "https://twitch.tv/" + conn.Name; - else if (conn.Type == "twitter") - url = "https://twitter.com/i/user/" + conn.ID; - else if (conn.Type == "spotify") - url = "https://open.spotify.com/user/" + conn.ID; - else if (conn.Type == "reddit") - url = "https://reddit.com/u/" + conn.Name; - else if (conn.Type == "youtube") - url = "https://www.youtube.com/channel/" + conn.ID; - else if (conn.Type == "facebook") - url = "https://www.facebook.com/" + conn.ID; + std::string url = GetConnectionURL(conn); if (pixbuf) { m_image = Gtk::manage(new Gtk::Image(pixbuf)); m_image->get_style_context()->add_class("profile-connection-image"); @@ -83,25 +95,33 @@ void ConnectionsContainer::SetConnections(const std::vector &con static const std::unordered_set supported_services = { "battlenet", + "ebay", + "epicgames", + "facebook", "github", + "instagram", "leagueoflegends", + "paypal", + "playstation", "reddit", + "riotgames", "skype", "spotify", "steam", + "tiktok", "twitch", "twitter", "xbox", "youtube", - "facebook" }; - for (size_t i = 0; i < connections.size(); i++) { - const auto &conn = connections[i]; + int i = 0; + for (const auto &conn : connections) { if (supported_services.find(conn.Type) == supported_services.end()) continue; auto widget = Gtk::manage(new ConnectionItem(conn)); widget->show(); - attach(*widget, static_cast(i % 2), static_cast(i / 2), 1, 1); + attach(*widget, i % 2, i / 2, 1, 1); + i++; } set_halign(Gtk::ALIGN_FILL); diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp index 73a09c2..16a9fed 100644 --- a/src/windows/voicewindow.cpp +++ b/src/windows/voicewindow.cpp @@ -89,6 +89,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) , m_mute("Mute") , m_deafen("Deafen") , m_noise_suppression("Suppress Noise") + , m_mix_mono("Mix Mono") , m_channel_id(channel_id) , m_menu_view("View") , m_menu_view_settings("More _Settings", true) { @@ -178,6 +179,11 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) Abaddon::Get().GetAudio().SetSuppressNoise(m_noise_suppression.get_active()); }); + m_mix_mono.set_active(audio.GetMixMono()); + m_mix_mono.signal_toggled().connect([this]() { + Abaddon::Get().GetAudio().SetMixMono(m_mix_mono.get_active()); + }); + auto *playback_renderer = Gtk::make_managed(); m_playback_combo.set_valign(Gtk::ALIGN_END); m_playback_combo.set_hexpand(true); @@ -223,6 +229,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) m_controls.add(m_mute); m_controls.add(m_deafen); m_controls.add(m_noise_suppression); + m_controls.add(m_mix_mono); m_main.add(m_menu_bar); m_main.add(m_controls); m_main.add(m_vad_value); diff --git a/src/windows/voicewindow.hpp b/src/windows/voicewindow.hpp index 6db2563..02e006f 100644 --- a/src/windows/voicewindow.hpp +++ b/src/windows/voicewindow.hpp @@ -54,6 +54,7 @@ private: Gtk::Scale m_capture_gain; Gtk::CheckButton m_noise_suppression; + Gtk::CheckButton m_mix_mono; Gtk::ComboBoxText m_vad_combo; Gtk::ComboBox m_playback_combo;