aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-08-26 20:20:15 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-08-26 20:20:15 +0200
commit6f0b086f7830f27a7d938bc03db0c73295d745e2 (patch)
treeb1b1a7229a9573ed20079452de7919364619d807
parentchore(version): v1.8.0 (diff)
downloadyt-6f0b086f7830f27a7d938bc03db0c73295d745e2.zip
feat(yt/commands/subs): Implement disabling subscriptions
This allows for a softer version of `unsubscribe`, as the subscription can be enabled again.
-rw-r--r--crates/yt/src/commands/subscriptions/implm.rs42
-rw-r--r--crates/yt/src/commands/subscriptions/mod.rs26
-rw-r--r--crates/yt/src/commands/update/implm/mod.rs2
-rw-r--r--crates/yt/src/storage/db/get/subscription.rs16
-rw-r--r--crates/yt/src/storage/db/insert/subscription.rs33
-rw-r--r--crates/yt/src/storage/db/subscription.rs6
-rw-r--r--crates/yt/src/storage/migrate/mod.rs14
-rw-r--r--crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql12
8 files changed, 140 insertions, 11 deletions
diff --git a/crates/yt/src/commands/subscriptions/implm.rs b/crates/yt/src/commands/subscriptions/implm.rs
index 31b714e..1e2e545 100644
--- a/crates/yt/src/commands/subscriptions/implm.rs
+++ b/crates/yt/src/commands/subscriptions/implm.rs
@@ -12,7 +12,7 @@ use std::str::FromStr;
use crate::{
app::App,
- commands::subscriptions::SubscriptionCommand,
+ commands::subscriptions::{SubscriptionCommand, SubscriptionStatus},
storage::db::{
insert::{Operations, subscription::Operation},
subscription::{Subscription, Subscriptions, check_url},
@@ -55,11 +55,41 @@ impl SubscriptionCommand {
.await
.with_context(|| format!("Failed to unsubscribe from {name:?}"))?;
}
- SubscriptionCommand::List {} => {
+ SubscriptionCommand::SetStatus { name, new_status } => {
+ let mut present_subscriptions = Subscriptions::get(app).await?;
+
+ let mut ops = Operations::new("Subscribe: Set Status");
+ if let Some(subscription) = present_subscriptions.0.remove(&name) {
+ subscription.set_is_active(
+ match new_status {
+ SubscriptionStatus::Active => true,
+ SubscriptionStatus::Inactive => false,
+ },
+ &mut ops,
+ );
+ } else {
+ bail!("Couldn't find subscription: '{}'", &name);
+ }
+ ops.commit(app)
+ .await
+ .with_context(|| format!("Failed to change status of {name:?}"))?;
+ }
+ SubscriptionCommand::List { active } => {
let all_subs = Subscriptions::get(app).await?;
+ let all_subs = if active {
+ all_subs.remove_inactive()
+ } else {
+ all_subs
+ };
+
for (key, val) in all_subs.0 {
- println!("{}: '{}'", key, val.url);
+ println!(
+ "{}: '{}' ({})",
+ key,
+ val.url,
+ if val.is_active { "active" } else { "inactive" }
+ );
}
}
SubscriptionCommand::Export {} => {
@@ -245,7 +275,11 @@ async fn actual_subscribe(
);
}
- let sub = Subscription { name, url };
+ let sub = Subscription {
+ name,
+ url,
+ is_active: true,
+ };
sub.add(ops);
diff --git a/crates/yt/src/commands/subscriptions/mod.rs b/crates/yt/src/commands/subscriptions/mod.rs
index edd41c6..6a16a9a 100644
--- a/crates/yt/src/commands/subscriptions/mod.rs
+++ b/crates/yt/src/commands/subscriptions/mod.rs
@@ -10,7 +10,7 @@
use std::path::PathBuf;
-use clap::Subcommand;
+use clap::{Subcommand, ValueEnum};
use clap_complete::ArgValueCompleter;
use url::Url;
@@ -41,6 +41,18 @@ pub(super) enum SubscriptionCommand {
name: String,
},
+ /// Change the status of an subscription.
+ ///
+ /// An active subscription will be updated in `yt update`, while an inactive one will not.
+ SetStatus {
+ /// The human readable name of the subscription
+ #[arg(add = ArgValueCompleter::new(complete_subscription))]
+ name: String,
+
+ /// What should this subscription be considered now?
+ new_status: SubscriptionStatus,
+ },
+
/// Import a bunch of URLs as subscriptions.
Import {
/// The file containing the URLs. Will use Stdin otherwise.
@@ -58,5 +70,15 @@ pub(super) enum SubscriptionCommand {
Export {},
/// List all subscriptions
- List {},
+ List {
+ /// Only show active subscriptions
+ #[arg(short, long, default_value_t = false)]
+ active: bool,
+ },
+}
+
+#[derive(ValueEnum, Debug, Clone, Copy)]
+pub(in crate::commands) enum SubscriptionStatus {
+ Active,
+ Inactive,
}
diff --git a/crates/yt/src/commands/update/implm/mod.rs b/crates/yt/src/commands/update/implm/mod.rs
index 53c7415..10626ac 100644
--- a/crates/yt/src/commands/update/implm/mod.rs
+++ b/crates/yt/src/commands/update/implm/mod.rs
@@ -28,7 +28,7 @@ impl UpdateCommand {
subscriptions: subscription_names_to_update,
} = self;
- let mut all_subs = Subscriptions::get(app).await?;
+ let mut all_subs = Subscriptions::get(app).await?.remove_inactive();
let max_backlog = max_backlog.unwrap_or(app.config.update.max_backlog);
diff --git a/crates/yt/src/storage/db/get/subscription.rs b/crates/yt/src/storage/db/get/subscription.rs
index 16a6e8b..1d0b660 100644
--- a/crates/yt/src/storage/db/get/subscription.rs
+++ b/crates/yt/src/storage/db/get/subscription.rs
@@ -39,6 +39,13 @@ impl Subscriptions {
Subscription::new(
sub.name,
Url::parse(&sub.url).expect("It was an URL, when we inserted it."),
+ if sub.is_active == 1 {
+ true
+ } else if sub.is_active == 0 {
+ false
+ } else {
+ unreachable!("These are the only two options")
+ },
),
)
})
@@ -46,4 +53,13 @@ impl Subscriptions {
Ok(Subscriptions(subscriptions))
}
+
+ pub(crate) fn remove_inactive(self) -> Self {
+ Self(
+ self.0
+ .into_iter()
+ .filter(|(_, sub)| sub.is_active)
+ .collect(),
+ )
+ }
}
diff --git a/crates/yt/src/storage/db/insert/subscription.rs b/crates/yt/src/storage/db/insert/subscription.rs
index d25a209..54409a9 100644
--- a/crates/yt/src/storage/db/insert/subscription.rs
+++ b/crates/yt/src/storage/db/insert/subscription.rs
@@ -21,6 +21,10 @@ use sqlx::query;
pub(crate) enum Operation {
Add(Subscription),
Remove(Subscription),
+ SetIsActive {
+ target: Subscription,
+ is_active: bool,
+ },
}
impl Committable for Operation {
@@ -72,6 +76,26 @@ impl Committable for Operation {
Ok(())
}
+ Operation::SetIsActive { target, is_active } => {
+ query!(
+ "
+ UPDATE subscriptions
+ SET is_active = ?
+ WHERE name = ?;
+ ",
+ is_active,
+ target.name
+ )
+ .execute(txn)
+ .await?;
+
+ println!(
+ "Marked '{}' as '{}'",
+ target.name,
+ if is_active { "active" } else { "inactive" }
+ );
+ Ok(())
+ }
}
}
}
@@ -84,6 +108,15 @@ impl Subscription {
pub(crate) fn remove(self, ops: &mut Operations<Operation>) {
ops.push(Operation::Remove(self));
}
+
+ pub(crate) fn set_is_active(self, is_active: bool, ops: &mut Operations<Operation>) {
+ if self.is_active != is_active {
+ ops.push(Operation::SetIsActive {
+ target: self,
+ is_active,
+ });
+ }
+ }
}
impl Subscriptions {
diff --git a/crates/yt/src/storage/db/subscription.rs b/crates/yt/src/storage/db/subscription.rs
index c111b52..403938e 100644
--- a/crates/yt/src/storage/db/subscription.rs
+++ b/crates/yt/src/storage/db/subscription.rs
@@ -23,12 +23,14 @@ pub(crate) struct Subscription {
/// The URL this subscription subscribes to
pub(crate) url: Url,
+
+ pub(crate) is_active: bool,
}
impl Subscription {
#[must_use]
- pub(crate) fn new(name: String, url: Url) -> Self {
- Self { name, url }
+ pub(crate) fn new(name: String, url: Url, is_active: bool) -> Self {
+ Self { name, url, is_active }
}
}
diff --git a/crates/yt/src/storage/migrate/mod.rs b/crates/yt/src/storage/migrate/mod.rs
index 418c893..c5187ee 100644
--- a/crates/yt/src/storage/migrate/mod.rs
+++ b/crates/yt/src/storage/migrate/mod.rs
@@ -97,8 +97,11 @@ pub(crate) enum DbVersion {
/// Introduced: 2025-07-20.
Five,
+
+ /// Introduced: 2025-08-26.
+ Six,
}
-const CURRENT_VERSION: DbVersion = DbVersion::Five;
+const CURRENT_VERSION: DbVersion = DbVersion::Six;
async fn add_error_context(
function: impl Future<Output = Result<()>>,
@@ -151,6 +154,7 @@ impl DbVersion {
DbVersion::Three => 3,
DbVersion::Four => 4,
DbVersion::Five => 5,
+ DbVersion::Six => 6,
DbVersion::Empty => unreachable!("A empty version does not have an associated integer"),
}
@@ -164,6 +168,7 @@ impl DbVersion {
(3, "yt") => Ok(DbVersion::Three),
(4, "yt") => Ok(DbVersion::Four),
(5, "yt") => Ok(DbVersion::Five),
+ (6, "yt") => Ok(DbVersion::Six),
(0, other) => bail!("Db version is Zero, but got unknown namespace: '{other}'"),
(1, other) => bail!("Db version is One, but got unknown namespace: '{other}'"),
@@ -171,6 +176,7 @@ impl DbVersion {
(3, other) => bail!("Db version is Three, but got unknown namespace: '{other}'"),
(4, other) => bail!("Db version is Four, but got unknown namespace: '{other}'"),
(5, other) => bail!("Db version is Five, but got unknown namespace: '{other}'"),
+ (6, other) => bail!("Db version is Six, but got unknown namespace: '{other}'"),
(other, "yt") => bail!("Got unkown version for 'yt' namespace: {other}"),
(num, nasp) => bail!("Got unkown version number ({num}) and namespace ('{nasp}')"),
@@ -208,8 +214,12 @@ impl DbVersion {
make_upgrade! {app, Self::Four, Self::Five, "./sql/5_Four_to_Five.sql"}
}
- // This is the current_version
Self::Five => {
+ make_upgrade! {app, Self::Five, Self::Six, "./sql/6_Five_to_Six.sql"}
+ }
+
+ // This is the current_version
+ Self::Six => {
assert_eq!(self, CURRENT_VERSION);
assert_eq!(self, get_version(app).await?);
Ok(())
diff --git a/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql b/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql
new file mode 100644
index 0000000..6a2cbcc
--- /dev/null
+++ b/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql
@@ -0,0 +1,12 @@
+-- yt - A fully featured command line YouTube client
+--
+-- Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+-- SPDX-License-Identifier: GPL-3.0-or-later
+--
+-- This file is part of Yt.
+--
+-- You should have received a copy of the License along with this program.
+-- If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+ALTER TABLE subscriptions
+ADD COLUMN is_active INTEGER NOT NULL DEFAULT 1 CHECK (is_active IN (0, 1));