Skip to content

Commit eac7c22

Browse files
143mailliwWilliam Whittaker
authored andcommitted
feat: feature-gate online services (#395)
Reviewed-on: https://codeberg.org/hummingbird/hummingbird/pulls/395
1 parent 310678d commit eac7c22

9 files changed

Lines changed: 300 additions & 177 deletions

File tree

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ categories = ["multimedia::audio"]
1010
[features]
1111
runtime_shaders = ["gpui_platform/runtime_shaders"]
1212
console = ["dep:console-subscriber", "tokio/tracing"]
13-
update = ["dep:semver", "dep:minisign-verify", "dep:winreg"]
13+
update = ["dep:semver", "dep:minisign-verify", "dep:winreg", "online"]
14+
online = ["dep:zed-reqwest"]
15+
libre-services = ["online"]
16+
proprietary-services = ["online"]
17+
default = ["libre-services", "proprietary-services"]
1418

1519
[dependencies]
1620
anyhow = "1"
@@ -73,7 +77,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
7377
unicode-segmentation = "1"
7478
url = "2.5"
7579
urlencoding = "2"
76-
zed-reqwest = { version = "0.12.15-zed", default-features = false, features = [
80+
zed-reqwest = { version = "0.12.15-zed", default-features = false, optional = true, features = [
7781
"json",
7882
"macos-system-configuration",
7983
# this was native-roots, but this caused intermittent issues on Windows

docs/building.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ Some members of the community have provided a Nix flake. We try to keep it fairl
2828
## Environment
2929
If you wish to use last.fm support with your build, you'll have to set `LASTFM_API_KEY` and `LASTFM_API_SECRET` in either your environment variables or in your `.env` file. If you don't set these variables, Hummingbird will still build, but last.fm support will be disabled, and you'll get a warning in the logs.
3030

31+
## Offline Builds/Libre-only Services
32+
Some users may wish to prevent Hummingbird from accessing the internet, or prevent it from accessing proprietary online services (like last.fm). Hummingbird's online services can be disabled using cargo features.
33+
34+
To disable all online services, use the `--no-default-features` flag when building:
35+
36+
```sh
37+
cargo build --release --no-default-features
38+
```
39+
40+
To enable only libre services, use the `--features libre-services` flag (with `--no-default-features`) when building:
41+
42+
```sh
43+
cargo build --release --features libre-services --no-default-features
44+
```
45+
3146
## Building
3247
```sh
3348
git clone https://codeberg.org/hummingbird/hummingbird

src/services/mmb.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub mod discord;
2+
#[cfg(feature = "proprietary-services")]
23
pub mod lastfm;
4+
#[cfg(feature = "libre-services")]
35
pub mod listenbrainz;
46

57
use std::{path::PathBuf, sync::Arc};

src/ui/app.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ use gpui::*;
1313
use gpui_platform::current_platform;
1414
use prelude::FluentBuilder;
1515
use sqlx::SqlitePool;
16-
use tracing::{debug, warn};
16+
use tracing::debug;
1717

18+
#[cfg(feature = "proprietary-services")]
19+
use crate::services::mmb::lastfm;
1820
use crate::{
1921
library::{
2022
db::create_pool,
@@ -26,10 +28,7 @@ use crate::{
2628
session_storage::PlaybackSessionStorageWorker, thread::PlaybackThread,
2729
},
2830
power::PowerManager,
29-
services::{
30-
controllers::{init_pbc_task, register_pbc_event_handlers},
31-
mmb::lastfm,
32-
},
31+
services::controllers::{init_pbc_task, register_pbc_event_handlers},
3332
settings::{
3433
SettingsGlobal, setup_settings,
3534
storage::{Storage, StorageData},
@@ -355,12 +354,15 @@ pub fn run() -> anyhow::Result<()> {
355354
tracing::error!(?error, "fatal: unable to create database pool");
356355
})?;
357356

357+
#[cfg(feature = "proprietary-services")]
358358
if !lastfm::is_available() {
359-
warn!(
359+
tracing::warn!(
360360
"Last.fm authentication disabled. \
361361
Set `LASTFM_API_KEY` and `LASTFM_API_SECRET` to allow connecting to Last.fm."
362362
);
363-
warn!("These can additionally be set at compile time to bake them into the binary.");
363+
tracing::warn!(
364+
"These can additionally be set at compile time to bake them into the binary."
365+
);
364366
}
365367

366368
let application = Application::with_platform(current_platform(false))

src/ui/header/services.rs

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ use cntp_i18n::tr;
22
use gpui::{prelude::FluentBuilder, *};
33

44
use crate::{
5-
services::mmb::{
6-
discord::DiscordRpcStatus,
7-
lastfm::{LastFMState, is_available},
8-
listenbrainz::ListenBrainzState,
9-
},
5+
services::mmb::discord::DiscordRpcStatus,
106
settings::{Settings, SettingsGlobal, save_settings},
117
ui::{
128
components::{
@@ -17,17 +13,25 @@ use crate::{
1713
tooltip::build_tooltip,
1814
},
1915
models::Models,
20-
settings::{
21-
SettingsSectionKind, lastfm as lastfm_ui, listenbrainz as listenbrainz_ui,
22-
open_settings_window_with_section,
23-
},
16+
settings::{SettingsSectionKind, open_settings_window_with_section},
2417
theme::Theme,
2518
},
2619
};
20+
#[cfg(feature = "proprietary-services")]
21+
use crate::{
22+
services::mmb::lastfm::{LastFMState, is_available},
23+
ui::settings::lastfm as lastfm_ui,
24+
};
25+
#[cfg(feature = "libre-services")]
26+
use crate::{
27+
services::mmb::listenbrainz::ListenBrainzState, ui::settings::listenbrainz as listenbrainz_ui,
28+
};
2729

2830
pub struct ServicesIndicator {
2931
settings: Entity<Settings>,
32+
#[cfg(feature = "proprietary-services")]
3033
lastfm: Entity<LastFMState>,
34+
#[cfg(feature = "libre-services")]
3135
listenbrainz: Entity<ListenBrainzState>,
3236
discord_rpc: Entity<DiscordRpcStatus>,
3337
show_popover: bool,
@@ -37,18 +41,29 @@ impl ServicesIndicator {
3741
pub fn new(cx: &mut App) -> Entity<Self> {
3842
cx.new(|cx| {
3943
let settings = cx.global::<SettingsGlobal>().model.clone();
40-
let lastfm = cx.global::<Models>().lastfm.clone();
41-
let listenbrainz = cx.global::<Models>().listenbrainz.clone();
4244
let discord_rpc = cx.global::<Models>().discord_rpc.clone();
4345

4446
cx.observe(&settings, |_, _, cx| cx.notify()).detach();
45-
cx.observe(&lastfm, |_, _, cx| cx.notify()).detach();
46-
cx.observe(&listenbrainz, |_, _, cx| cx.notify()).detach();
4747
cx.observe(&discord_rpc, |_, _, cx| cx.notify()).detach();
4848

49+
#[cfg(feature = "proprietary-services")]
50+
let lastfm = {
51+
let lastfm = cx.global::<Models>().lastfm.clone();
52+
cx.observe(&lastfm, |_, _, cx| cx.notify()).detach();
53+
lastfm
54+
};
55+
#[cfg(feature = "libre-services")]
56+
let listenbrainz = {
57+
let listenbrainz = cx.global::<Models>().listenbrainz.clone();
58+
cx.observe(&listenbrainz, |_, _, cx| cx.notify()).detach();
59+
listenbrainz
60+
};
61+
4962
Self {
5063
settings,
64+
#[cfg(feature = "proprietary-services")]
5165
lastfm,
66+
#[cfg(feature = "libre-services")]
5267
listenbrainz,
5368
discord_rpc,
5469
show_popover: false,
@@ -64,31 +79,39 @@ impl ServicesIndicator {
6479

6580
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6681
enum ServiceKind {
82+
#[cfg(feature = "proprietary-services")]
6783
LastFm,
84+
#[cfg(feature = "libre-services")]
6885
ListenBrainz,
6986
DiscordRpc,
7087
}
7188

7289
impl ServiceKind {
7390
fn name(self) -> SharedString {
7491
match self {
92+
#[cfg(feature = "proprietary-services")]
7593
Self::LastFm => lastfm_ui::title(),
94+
#[cfg(feature = "libre-services")]
7695
Self::ListenBrainz => listenbrainz_ui::title(),
7796
Self::DiscordRpc => tr!("SERVICES_DISCORD_RPC_TITLE").into(),
7897
}
7998
}
8099

81100
fn row_id(self) -> &'static str {
82101
match self {
102+
#[cfg(feature = "proprietary-services")]
83103
Self::LastFm => "services-toggle-lastfm",
104+
#[cfg(feature = "libre-services")]
84105
Self::ListenBrainz => "services-toggle-listenbrainz",
85106
Self::DiscordRpc => "services-toggle-discord",
86107
}
87108
}
88109

89110
fn button_id(self) -> &'static str {
90111
match self {
112+
#[cfg(feature = "proprietary-services")]
91113
Self::LastFm => "services-toggle-lastfm-btn",
114+
#[cfg(feature = "libre-services")]
92115
Self::ListenBrainz => "services-toggle-listenbrainz-btn",
93116
Self::DiscordRpc => "services-toggle-discord-btn",
94117
}
@@ -99,6 +122,7 @@ impl ServiceKind {
99122
pub(super) enum ServiceStatus {
100123
Connected,
101124
Disconnected,
125+
#[cfg(feature = "proprietary-services")]
102126
PendingSignIn,
103127
}
104128

@@ -118,13 +142,14 @@ struct ServiceEntry {
118142

119143
fn collect_services(
120144
settings: &Settings,
121-
lastfm_state: &LastFMState,
122-
listenbrainz_state: &ListenBrainzState,
145+
#[cfg(feature = "proprietary-services")] lastfm_state: &LastFMState,
146+
#[cfg(feature = "libre-services")] listenbrainz_state: &ListenBrainzState,
123147
discord_rpc: &DiscordRpcStatus,
124-
lastfm_available: bool,
148+
#[cfg(feature = "proprietary-services")] lastfm_available: bool,
125149
) -> Vec<ServiceEntry> {
126150
let mut services = Vec::new();
127151

152+
#[cfg(feature = "proprietary-services")]
128153
if lastfm_available {
129154
let lastfm_entry = match lastfm_state {
130155
LastFMState::Connected(_) => Some((ServiceStatus::Connected, None)),
@@ -142,6 +167,7 @@ fn collect_services(
142167
}
143168
}
144169

170+
#[cfg(feature = "libre-services")]
145171
if matches!(listenbrainz_state, ListenBrainzState::Connected(_)) {
146172
services.push(ServiceEntry {
147173
kind: ServiceKind::ListenBrainz,
@@ -193,13 +219,15 @@ fn toggle_service(
193219
kind: ServiceKind,
194220
enabled: bool,
195221
settings: Entity<Settings>,
196-
lastfm: Entity<LastFMState>,
197-
listenbrainz: Entity<ListenBrainzState>,
222+
#[cfg(feature = "proprietary-services")] lastfm: Entity<LastFMState>,
223+
#[cfg(feature = "libre-services")] listenbrainz: Entity<ListenBrainzState>,
198224
) {
199225
match kind {
226+
#[cfg(feature = "proprietary-services")]
200227
ServiceKind::LastFm => {
201228
lastfm_ui::toggle_lastfm(cx, enabled, settings, lastfm);
202229
}
230+
#[cfg(feature = "libre-services")]
203231
ServiceKind::ListenBrainz => {
204232
listenbrainz_ui::toggle_listenbrainz(cx, enabled, settings, listenbrainz);
205233
}
@@ -215,14 +243,19 @@ fn toggle_service(
215243

216244
impl Render for ServicesIndicator {
217245
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
246+
#[cfg(feature = "proprietary-services")]
218247
let lastfm = self.lastfm.read(cx).clone();
248+
#[cfg(feature = "libre-services")]
219249
let listenbrainz = self.listenbrainz.read(cx).clone();
220250
let discord_rpc = self.discord_rpc.read(cx).clone();
221251
let services = collect_services(
222252
self.settings.read(cx),
253+
#[cfg(feature = "proprietary-services")]
223254
&lastfm,
255+
#[cfg(feature = "libre-services")]
224256
&listenbrainz,
225257
&discord_rpc,
258+
#[cfg(feature = "proprietary-services")]
226259
is_available(),
227260
);
228261
let indicator = indicator_icon(&services);
@@ -267,7 +300,9 @@ impl Render for ServicesIndicator {
267300
let theme = cx.global::<Theme>().clone();
268301
for entry in &services {
269302
let settings = self.settings.clone();
303+
#[cfg(feature = "proprietary-services")]
270304
let lastfm = self.lastfm.clone();
305+
#[cfg(feature = "libre-services")]
271306
let listenbrainz = self.listenbrainz.clone();
272307
let status = status_dot(entry);
273308
let kind = entry.kind;
@@ -300,7 +335,9 @@ impl Render for ServicesIndicator {
300335
kind,
301336
enabled,
302337
settings.clone(),
338+
#[cfg(feature = "proprietary-services")]
303339
lastfm.clone(),
340+
#[cfg(feature = "libre-services")]
304341
listenbrainz.clone(),
305342
);
306343
})
@@ -354,7 +391,7 @@ impl Render for ServicesIndicator {
354391
}
355392
}
356393

357-
#[cfg(test)]
394+
#[cfg(all(test, feature = "proprietary-services", feature = "libre-services"))]
358395
mod tests {
359396
use crate::{
360397
services::mmb::{

0 commit comments

Comments
 (0)