Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions locales/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ actions.stats:
🔍 Analyzed lyrics <code>%{lyrics_analyzed}</code> times
🙈 You ignored <code>%{ignored}</code> track lyrics
🤬 <code>%{lyrics_profane}</code> lyrics were considered profane
💩 <code>%{ai_slop_total}</code> tracks were detected as AI Slop

<blockquote expandable><b>Languages stats:</b>
%{languages}</blockquote>
Expand All @@ -32,6 +33,7 @@ actions.stats:
🔍 Проанализировано текстов <code>%{lyrics_analyzed}</code> раз
🙈 Вы проигнорировали <code>%{ignored}</code> текстов треков
🤬 <code>%{lyrics_profane}</code> текстов были признаны нецензурными
💩 <code>%{ai_slop_total}</code> треков были определены как ИИ шлак

<blockquote expandable><b>Статистика по языкам (названия на английском):</b>
%{languages}</blockquote>
Expand Down
5 changes: 5 additions & 0 deletions migrations/20260308152848_add_ai_slop_detection_counters.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
alter table "user"
add column ai_slop_spotify_ai_blocker bigint not null default 0,
add column ai_slop_soul_over_ai bigint not null default 0,
add column ai_slop_shlabs bigint not null default 0,
add column ai_slop_human_made bigint not null default 0;
36 changes: 36 additions & 0 deletions src/entity/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,38 @@ pub struct Model {
pub name: String,
pub locale: Locale,
pub role: Role,

pub removed_playlists: i64,
pub removed_collection: i64,

pub lyrics_checked: i64,
pub lyrics_analyzed: i64,
pub lyrics_genius: i64,
pub lyrics_musixmatch: i64,
#[sea_orm(enum_name = "LyricsLrcLib")]
pub lyrics_lrclib: i64,
pub lyrics_profane: i64,

#[sea_orm(enum_name = "AISlopSpotifyAIBlocker")]
pub ai_slop_spotify_ai_blocker: i64,
#[sea_orm(enum_name = "AISlopSoulOverAI")]
pub ai_slop_soul_over_ai: i64,
#[sea_orm(enum_name = "AISlopSHLabs")]
pub ai_slop_shlabs: i64,
#[sea_orm(enum_name = "AISlopHumanMade")]
pub ai_slop_human_made: i64,

pub status: Status,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,

pub cfg_check_profanity: bool,
pub cfg_skip_tracks: bool,
pub cfg_skippage_secs: i64,
pub cfg_skippage_enabled: bool,
#[sea_orm(enum_name = "CfgAISlopDetection")]
pub cfg_ai_slop_detection: AISlopDetection,

pub magic_playlist: Option<String>,
pub spotify_state: Uuid,
pub ref_code: Option<String>,
Expand Down Expand Up @@ -70,24 +84,38 @@ pub enum Column {
Name,
Locale,
Role,

RemovedPlaylists,
RemovedCollection,

LyricsChecked,
LyricsAnalyzed,
LyricsGenius,
LyricsMusixmatch,
#[sea_orm(column_name = "lyrics_lrclib")]
LyricsLrcLib,
LyricsProfane,

#[sea_orm(column_name = "ai_slop_spotify_ai_blocker")]
AISlopSpotifyAIBlocker,
#[sea_orm(column_name = "ai_slop_soul_over_ai")]
AISlopSoulOverAI,
#[sea_orm(column_name = "ai_slop_shlabs")]
AISlopSHLabs,
#[sea_orm(column_name = "ai_slop_human_made")]
AISlopHumanMade,

Status,
CreatedAt,
UpdatedAt,

CfgCheckProfanity,
CfgSkipTracks,
CfgSkippageSecs,
CfgSkippageEnabled,
#[sea_orm(column_name = "cfg_ai_slop_detection")]
CfgAISlopDetection,

MagicPlaylist,
SpotifyState,
RefCode,
Expand Down Expand Up @@ -115,14 +143,22 @@ impl ColumnTrait for Column {
Self::Name => ColumnType::Text.def(),
Self::Locale => Locale::db_type(),
Self::Role => Role::db_type(),

Self::RemovedPlaylists => ColumnType::BigInteger.def(),
Self::RemovedCollection => ColumnType::BigInteger.def(),

Self::LyricsChecked => ColumnType::BigInteger.def(),
Self::LyricsAnalyzed => ColumnType::BigInteger.def(),
Self::LyricsGenius => ColumnType::BigInteger.def(),
Self::LyricsMusixmatch => ColumnType::BigInteger.def(),
Self::LyricsLrcLib => ColumnType::BigInteger.def(),
Self::LyricsProfane => ColumnType::BigInteger.def(),

Self::AISlopSpotifyAIBlocker => ColumnType::BigInteger.def(),
Self::AISlopSoulOverAI => ColumnType::BigInteger.def(),
Self::AISlopSHLabs => ColumnType::BigInteger.def(),
Self::AISlopHumanMade => ColumnType::BigInteger.def(),

Self::Status => Status::db_type(),
Self::CreatedAt => ColumnType::DateTime.def(),
Self::UpdatedAt => ColumnType::DateTime.def(),
Expand Down
21 changes: 21 additions & 0 deletions src/metrics/influx_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ struct TrackLanguageStats {
language: String,
}

#[derive(InfluxDbWriteable, Debug)]
struct AISlopDetectionStats {
time: Timestamp,
spotify_ai_blocker: u64,
soul_over_ai: u64,
shlabs: u64,
human_made: u64,
}

#[derive(InfluxDbWriteable, Debug)]
struct Uptime {
time: Timestamp,
Expand Down Expand Up @@ -113,6 +122,10 @@ pub async fn collect(client: &InfluxClient, app: &App) -> anyhow::Result<()> {
lyrics_musixmatch,
lyrics_lrclib,
lyrics_analyzed,
ai_slop_spotify_ai_blocker,
ai_slop_soul_over_ai,
ai_slop_shlabs,
ai_slop_human_made,
} = UserService::get_stats(app.db(), None).await?;

let tick_health_status = utils::tick_health().await;
Expand Down Expand Up @@ -154,6 +167,14 @@ pub async fn collect(client: &InfluxClient, app: &App) -> anyhow::Result<()> {
spotify_429: MetricsService::spotify_429_get(&mut redis_conn).await?,
}
.into_query("errors"),
AISlopDetectionStats {
time,
spotify_ai_blocker: ai_slop_spotify_ai_blocker as u64,
soul_over_ai: ai_slop_soul_over_ai as u64,
shlabs: ai_slop_shlabs as u64,
human_made: ai_slop_human_made as u64,
}
.into_query("ai_slop_detection"),
Uptime::new(time).into_query("uptime"),
];

Expand Down
9 changes: 9 additions & 0 deletions src/metrics/prometheus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct PrometheusMetrics {
pub lyrics_found: IntGauge,
pub lyrics_profane: IntGauge,
pub lyrics_source: IntGaugeVec,
pub ai_slop_detection: IntGaugeVec,
pub process_duration: Histogram,
pub process_check_interval_seconds: Gauge,
pub process_users_checked: IntCounter,
Expand Down Expand Up @@ -111,6 +112,14 @@ impl PrometheusClient {
)
.context("Failed to register lyrics_source metric")?,

ai_slop_detection: register_int_gauge_vec_with_registry!(
"ai_slop_detection_total",
"AI Detection Providers",
&["provider"],
registry
)
.context("Failed to register lyrics_source metric")?,
Comment thread
vtvz marked this conversation as resolved.
Outdated

process_duration: register_histogram_with_registry!(
"process_duration_seconds",
"Processing duration in seconds",
Expand Down
25 changes: 25 additions & 0 deletions src/metrics/prometheus_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ pub async fn collect(client: &PrometheusClient, app: &App) -> anyhow::Result<()>
lyrics_musixmatch,
lyrics_lrclib,
lyrics_analyzed,
ai_slop_spotify_ai_blocker,
ai_slop_soul_over_ai,
ai_slop_shlabs,
ai_slop_human_made,
} = UserService::get_stats(app.db(), None).await?;

let tick_health_status = utils::tick_health().await;
Expand Down Expand Up @@ -87,6 +91,27 @@ pub async fn collect(client: &PrometheusClient, app: &App) -> anyhow::Result<()>
.with_label_values(&["lrclib"])
.set(lyrics_lrclib);

client
.metrics()
.ai_slop_detection
.with_label_values(&["soul_over_ai"])
.set(ai_slop_soul_over_ai);
client
.metrics()
.ai_slop_detection
.with_label_values(&["spotify_ai_blocker"])
.set(ai_slop_spotify_ai_blocker);
client
.metrics()
.ai_slop_detection
.with_label_values(&["shlabs"])
.set(ai_slop_shlabs);
client
.metrics()
.ai_slop_detection
.with_label_values(&["human_made"])
.set(ai_slop_human_made);

client
.metrics()
.ticks
Expand Down
7 changes: 6 additions & 1 deletion src/queue/track_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub async fn consume(data: TrackCheckQueueTask, app: Data<&'static App>) -> anyh
.context("Check lyrics failed")?;

UserService::increase_stats_query(user_state.user_id())
.checked_lyrics(res.profane, res.provider)
.checked_lyrics(res.profane, res.provider.as_ref())
.exec(app.db())
.await?;

Expand Down Expand Up @@ -276,6 +276,11 @@ pub async fn check_ai_slop(
.is_track_ai(&mut app.redis_conn().await?, track)
.await?;

UserService::increase_stats_query(state.user_id())
.ai_slop(ai_detection_result.provider.as_ref())
.exec(app.db())
.await?;
Comment thread
vtvz marked this conversation as resolved.
Outdated

if !ai_detection_result.prediction.is_track_ai() {
return Ok(AISlopCheckResult {
is_ai_slop: false,
Expand Down
51 changes: 49 additions & 2 deletions src/services/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use sea_orm::{

use crate::entity::prelude::*;
use crate::lyrics;
use crate::services::ai_slop_detection;
use crate::utils::Clock;

pub struct UserStatsIncreaseQueryBuilder(UpdateMany<UserEntity>);
Expand Down Expand Up @@ -46,8 +47,7 @@ impl UserStatsIncreaseQueryBuilder {
self
}

#[allow(clippy::needless_pass_by_value)]
pub fn checked_lyrics(mut self, profane: bool, provider: Option<lyrics::Provider>) -> Self {
pub fn checked_lyrics(mut self, profane: bool, provider: Option<&lyrics::Provider>) -> Self {
self.0 = self
.0
.col_expr(
Expand All @@ -71,6 +71,21 @@ impl UserStatsIncreaseQueryBuilder {
self
}

pub fn ai_slop(mut self, provider: Option<&ai_slop_detection::Provider>) -> Self {
let col = match provider {
Some(ai_slop_detection::Provider::SpotifyAIBlocker) => {
UserColumn::AISlopSpotifyAIBlocker
},
Some(ai_slop_detection::Provider::SoulOverAI) => UserColumn::AISlopSoulOverAI,
Some(ai_slop_detection::Provider::SHLabs) => UserColumn::AISlopSHLabs,
None => UserColumn::AISlopHumanMade,
};

self.0 = self.0.col_expr(col, Expr::col(col).add(1));
Comment thread
vtvz marked this conversation as resolved.

self
}

pub fn analyzed_lyrics(mut self) -> Self {
self.0 = self.0.col_expr(
UserColumn::LyricsAnalyzed,
Expand Down Expand Up @@ -99,6 +114,10 @@ pub struct UserStats {
pub lyrics_musixmatch: i64,
pub lyrics_lrclib: i64,
pub lyrics_analyzed: i64,
pub ai_slop_spotify_ai_blocker: i64,
pub ai_slop_soul_over_ai: i64,
pub ai_slop_shlabs: i64,
pub ai_slop_human_made: i64,
}

pub struct UserService;
Expand Down Expand Up @@ -479,6 +498,34 @@ impl UserService {
]),
"lyrics_analyzed",
)
.expr_as(
Func::coalesce([
UserColumn::AISlopSpotifyAIBlocker.sum().cast_as(bigint()),
Expr::val(0).into(),
]),
"ai_slop_spotify_ai_blocker",
)
.expr_as(
Func::coalesce([
UserColumn::AISlopSoulOverAI.sum().cast_as(bigint()),
Expr::val(0).into(),
]),
"ai_slop_soul_over_ai",
)
.expr_as(
Func::coalesce([
UserColumn::AISlopSHLabs.sum().cast_as(bigint()),
Expr::val(0).into(),
]),
"ai_slop_shlabs",
)
.expr_as(
Func::coalesce([
UserColumn::AISlopHumanMade.sum().cast_as(bigint()),
Expr::val(0).into(),
]),
"ai_slop_human_made",
)
.into_model::<UserStats>()
.one(db)
.await?
Expand Down
16 changes: 16 additions & 0 deletions src/telegram/actions/admin_users/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ async fn format_user_details(app: &'static App, user_id: &str) -> anyhow::Result
})
.join("\n");

let ai_slop_total = stats.ai_slop_spotify_ai_blocker
+ stats.ai_slop_soul_over_ai
+ stats.ai_slop_shlabs
+ stats.ai_slop_human_made;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

let text = formatdoc!(
r#"
👤 <b>User Details</b>
Expand Down Expand Up @@ -157,6 +162,13 @@ async fn format_user_details(app: &'static App, user_id: &str) -> anyhow::Result
• MusixMatch: <code>{lyrics_musixmatch}</code>
• LrcLib: <code>{lyrics_lrclib}</code>

<b>AI Slop Detection:</b>
• Total <code>{ai_slop_total}</code>
• Soul Over AI <code>{ai_slop_soul_over_ai}</code>
• Spotify AI Blocker <code>{ai_slop_spotify_ai_blocker}</code>
• SHLabs <code>{ai_slop_shlabs}</code>
• Human Made <code>{ai_slop_human_made}</code>

<blockquote expandable><b>Languages stats:</b>
{languages}</blockquote>
"#,
Expand Down Expand Up @@ -189,6 +201,10 @@ async fn format_user_details(app: &'static App, user_id: &str) -> anyhow::Result
lyrics_genius = stats.lyrics_genius,
lyrics_musixmatch = stats.lyrics_musixmatch,
lyrics_lrclib = stats.lyrics_lrclib,
ai_slop_spotify_ai_blocker = stats.ai_slop_spotify_ai_blocker,
ai_slop_soul_over_ai = stats.ai_slop_soul_over_ai,
ai_slop_shlabs = stats.ai_slop_shlabs,
ai_slop_human_made = stats.ai_slop_human_made,
);

Ok(text)
Expand Down
Loading
Loading