mirror of
https://github.com/Llewellynvdm/conky.git
synced 2024-11-17 18:45:10 +00:00
audacious support added
git-svn-id: https://conky.svn.sourceforge.net/svnroot/conky/trunk/conky@482 7f574dfc-610e-0410-a909-a81674777703
This commit is contained in:
parent
0411afa5e4
commit
d4ab28ae2e
@ -1,5 +1,8 @@
|
||||
# $Id$
|
||||
|
||||
2006-01-07
|
||||
* Added Audacious media player support.
|
||||
|
||||
2006-01-06
|
||||
* Fixed infopipe bug (select() affects timer as a side effect on
|
||||
Linux!)
|
||||
|
22
configure.in
22
configure.in
@ -1,6 +1,6 @@
|
||||
AC_INIT([Conky],[1.3.6_CVS_20060106],[brenden1@users.sourceforge.net])
|
||||
AC_INIT([Conky],[1.3.6_CVS_20060107],[brenden1@users.sourceforge.net])
|
||||
|
||||
AM_INIT_AUTOMAKE(conky, 1.3.6_CVS_20060106)
|
||||
AM_INIT_AUTOMAKE(conky, 1.3.6_CVS_20060107)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
@ -78,6 +78,24 @@ if test $dah = "yes"; then
|
||||
AC_DEFINE(PROC_UPTIME, 1, [Define if you want to use /proc/uptime for uptime])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl Audacious
|
||||
dnl
|
||||
|
||||
want_audacious=no
|
||||
AC_ARG_ENABLE(audacious,
|
||||
[ --enable-audacious enable if you want Audacious support [[default=no]]],
|
||||
[want_audacious="$enableval"])
|
||||
|
||||
AM_CONDITIONAL(BUILD_AUDACIOUS, test x$want_audacious = xyes)
|
||||
if test x$want_audacious = xyes; then
|
||||
PKG_CHECK_MODULES([AUDACIOUS], [audacious >= 0.1],
|
||||
[], [AC_MSG_ERROR([Audacious 0.1 or higher is not installed])])
|
||||
CFLAGS="$CFLAGS $AUDACIOUS_CFLAGS"
|
||||
LIBS="$LIBS $AUDACIOUS_LIBS"
|
||||
AC_DEFINE(AUDACIOUS, 1, [Define if you want BMPx support])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl BMPx
|
||||
dnl
|
||||
|
@ -1,5 +1,9 @@
|
||||
bin_PROGRAMS = conky
|
||||
|
||||
if BUILD_AUDACIOUS
|
||||
audacious = audacious.c
|
||||
endif
|
||||
|
||||
if BUILD_BMPX
|
||||
bmpx = bmpx.c
|
||||
endif
|
||||
@ -46,7 +50,7 @@ if BUILD_X11
|
||||
x11 = x11.c
|
||||
endif
|
||||
|
||||
conky_SOURCES = common.c fs.c $(linux) mail.c mixer.c $(seti) $(mpd) $(solaris) $(freebsd) $(netbsd) $(port_monitors) conky.c conky.h $(x11) $(mldonkey) remoted.c remoted.h remotec.c remotec.h $(bmpx) $(infopipe)
|
||||
conky_SOURCES = common.c fs.c $(linux) mail.c mixer.c $(seti) $(mpd) $(solaris) $(freebsd) $(netbsd) $(port_monitors) conky.c conky.h $(x11) $(mldonkey) remoted.c remoted.h remotec.c remotec.h $(audacious) $(bmpx) $(infopipe)
|
||||
|
||||
AM_LDFLAGS = $(X11_LIBS) $(XFT_LIBS) $(CAIRO_LIBS) $(PTHREAD_LIBS) -lm
|
||||
|
||||
|
154
src/audacious.c
Normal file
154
src/audacious.c
Normal file
@ -0,0 +1,154 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
* audacious.c: conky support for Audacious media player
|
||||
*
|
||||
* http://audacious-media-player.org
|
||||
*
|
||||
* Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* --------------------------------------------------------------------------- */
|
||||
|
||||
#include <pthread.h>
|
||||
/*#include <glib.h>*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <audacious/beepctrl.h>
|
||||
#include "audacious.h"
|
||||
#include "conky.h"
|
||||
|
||||
/* access to this item array is synchronized with mutexes */
|
||||
static audacious_t g_items;
|
||||
|
||||
/* ----------------------------------------
|
||||
* Conky update function for Audacious data.
|
||||
* ---------------------------------------- */
|
||||
void update_audacious(void)
|
||||
{
|
||||
/*
|
||||
The worker thread is updating ihe g_items array asynchronously to the main
|
||||
conky thread. We merely copy the g_items array into the main thread's info
|
||||
structure when the main thread's update cycle fires. Note that using the
|
||||
mutexes here makes it easier since we won't have to do any sync in conky.c.
|
||||
*/
|
||||
pthread_mutex_lock(&info.audacious.item_mutex);
|
||||
memcpy(&info.audacious.items,g_items,sizeof(g_items));
|
||||
pthread_mutex_unlock(&info.audacious.item_mutex);
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------
|
||||
* Worker thread function for Audacious data sampling.
|
||||
* -------------------------------------------------- */
|
||||
void *audacious_thread_func(void *pvoid)
|
||||
{
|
||||
int runnable;
|
||||
static audacious_t items;
|
||||
int session,playpos,frames,length;
|
||||
int rate,freq,chans;
|
||||
char *psong;
|
||||
|
||||
pvoid=(void*)pvoid; /* useless cast to avoid unused var warning */
|
||||
session=0;
|
||||
|
||||
/* Grab the runnable signal. Should be non-zero here or we do nothing. */
|
||||
pthread_mutex_lock(&info.audacious.runnable_mutex);
|
||||
runnable=info.audacious.runnable;
|
||||
pthread_mutex_unlock(&info.audacious.runnable_mutex );
|
||||
|
||||
/* Loop until the main thread sets the runnable signal to 0. */
|
||||
while(runnable) {
|
||||
|
||||
for (;;) { /* convenience loop so we can break below */
|
||||
|
||||
if (!xmms_remote_is_running(session)) {
|
||||
memset(&items,0,sizeof(items));
|
||||
strcpy(items[AUDACIOUS_STATUS],"Not running");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Player status */
|
||||
if (xmms_remote_is_paused(session))
|
||||
strcpy(items[AUDACIOUS_STATUS],"Paused");
|
||||
else if (xmms_remote_is_playing(session))
|
||||
strcpy(items[AUDACIOUS_STATUS],"Playing");
|
||||
else
|
||||
strcpy(items[AUDACIOUS_STATUS],"Stopped");
|
||||
|
||||
/* Current song title */
|
||||
playpos = (int) xmms_remote_get_playlist_pos(session);
|
||||
psong = (char *) xmms_remote_get_playlist_title(session, playpos);
|
||||
if (psong)
|
||||
strncpy(items[AUDACIOUS_SONG],psong,sizeof(items[AUDACIOUS_SONG]));
|
||||
|
||||
/* Current song length as MM:SS */
|
||||
frames = xmms_remote_get_playlist_time(session,playpos);
|
||||
length = frames / 1000;
|
||||
snprintf(items[AUDACIOUS_SONG_LENGTH],sizeof(items[AUDACIOUS_SONG_LENGTH]),
|
||||
"%d:%.2d", length / 60, length % 60);
|
||||
|
||||
/* Current song length in seconds */
|
||||
snprintf(items[AUDACIOUS_SONG_LENGTH_SECONDS],sizeof(items[AUDACIOUS_SONG_LENGTH_SECONDS]),
|
||||
"%d", length);
|
||||
|
||||
/* Current song length in frames */
|
||||
snprintf(items[AUDACIOUS_SONG_LENGTH_FRAMES],sizeof(items[AUDACIOUS_SONG_LENGTH_FRAMES]),
|
||||
"%d", frames);
|
||||
|
||||
/* Current song output length as MM:SS */
|
||||
frames = xmms_remote_get_output_time(session);
|
||||
length = frames / 1000;
|
||||
snprintf(items[AUDACIOUS_SONG_OUTPUT_LENGTH],sizeof(items[AUDACIOUS_SONG_OUTPUT_LENGTH]),
|
||||
"%d:%.2d", length / 60, length % 60);
|
||||
|
||||
/* Current song output length in seconds */
|
||||
snprintf(items[AUDACIOUS_SONG_OUTPUT_LENGTH_SECONDS],sizeof(items[AUDACIOUS_SONG_OUTPUT_LENGTH_SECONDS]),
|
||||
"%d", length);
|
||||
|
||||
/* Current song output length in frames */
|
||||
snprintf(items[AUDACIOUS_SONG_OUTPUT_LENGTH_FRAMES],sizeof(items[AUDACIOUS_SONG_OUTPUT_LENGTH_FRAMES]),
|
||||
"%d", frames);
|
||||
|
||||
/* Current song bitrate */
|
||||
xmms_remote_get_info(session, &rate, &freq, &chans);
|
||||
snprintf(items[AUDACIOUS_SONG_BITRATE],sizeof(items[AUDACIOUS_SONG_BITRATE]), "%d", rate);
|
||||
|
||||
/* Current song frequency */
|
||||
snprintf(items[AUDACIOUS_SONG_FREQUENCY],sizeof(items[AUDACIOUS_SONG_FREQUENCY]), "%d", freq);
|
||||
|
||||
/* Current song channels */
|
||||
snprintf(items[AUDACIOUS_SONG_CHANNELS],sizeof(items[AUDACIOUS_SONG_CHANNELS]), "%d", chans);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Deliver the refreshed items array to g_items. */
|
||||
pthread_mutex_lock(&info.audacious.item_mutex);
|
||||
memcpy(&g_items,items,sizeof(items));
|
||||
pthread_mutex_unlock(&info.audacious.item_mutex);
|
||||
|
||||
/* Grab the runnable signal for next loop. */
|
||||
pthread_mutex_lock(&info.audacious.runnable_mutex);
|
||||
runnable=info.audacious.runnable;
|
||||
pthread_mutex_unlock(&info.audacious.runnable_mutex);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
52
src/audacious.h
Normal file
52
src/audacious.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
* audacious.h: conky support for Audacious audio player
|
||||
*
|
||||
* http://audacious-media-player.org
|
||||
*
|
||||
* Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* --------------------------------------------------------------------------- */
|
||||
|
||||
#ifndef AUDACIOUS_H
|
||||
#define AUDACIOUS_H
|
||||
|
||||
/* 11 keys comprise the audacious information. */
|
||||
enum _audacious_keys {
|
||||
AUDACIOUS_STATUS,
|
||||
AUDACIOUS_SONG,
|
||||
AUDACIOUS_SONG_LENGTH,
|
||||
AUDACIOUS_SONG_LENGTH_SECONDS,
|
||||
AUDACIOUS_SONG_LENGTH_FRAMES,
|
||||
AUDACIOUS_SONG_OUTPUT_LENGTH,
|
||||
AUDACIOUS_SONG_OUTPUT_LENGTH_SECONDS,
|
||||
AUDACIOUS_SONG_OUTPUT_LENGTH_FRAMES,
|
||||
AUDACIOUS_SONG_BITRATE,
|
||||
AUDACIOUS_SONG_FREQUENCY,
|
||||
AUDACIOUS_SONG_CHANNELS
|
||||
};
|
||||
|
||||
/* 11 slots for the audacious values */
|
||||
typedef char audacious_t[11][128];
|
||||
|
||||
/* Service routine for the conky main thread */
|
||||
void update_audacious(void);
|
||||
|
||||
/* Thread function */
|
||||
void *audacious_thread_func(void *);
|
||||
|
||||
#endif
|
@ -226,6 +226,10 @@ void update_stuff()
|
||||
if (NEED(INFO_MPD))
|
||||
update_mpd();
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
if (NEED(INFO_AUDACIOUS))
|
||||
update_audacious();
|
||||
#endif
|
||||
#ifdef BMPX
|
||||
if (NEED(INFO_BMPX))
|
||||
update_bmpx();
|
||||
|
103
src/conky.c
103
src/conky.c
@ -892,6 +892,20 @@ enum text_object_type {
|
||||
OBJ_mpd_track,
|
||||
OBJ_mpd_percent,
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
OBJ_audacious_status,
|
||||
OBJ_audacious_song,
|
||||
OBJ_audacious_song_length,
|
||||
OBJ_audacious_song_length_seconds,
|
||||
OBJ_audacious_song_length_frames,
|
||||
OBJ_audacious_song_output_length,
|
||||
OBJ_audacious_song_output_length_seconds,
|
||||
OBJ_audacious_song_output_length_frames,
|
||||
OBJ_audacious_song_bitrate,
|
||||
OBJ_audacious_song_frequency,
|
||||
OBJ_audacious_song_channels,
|
||||
OBJ_audacious_bar,
|
||||
#endif
|
||||
#ifdef BMPX
|
||||
OBJ_bmpx_title,
|
||||
OBJ_bmpx_artist,
|
||||
@ -1811,6 +1825,22 @@ int a = stippled_borders, b = 1;
|
||||
(void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
|
||||
END
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
OBJ(audacious_status, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_length, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_length_seconds, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_length_frames, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_output_length, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_output_length_seconds, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_output_length_frames, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_bitrate, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_frequency, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_song_channels, INFO_AUDACIOUS) END
|
||||
OBJ(audacious_bar, INFO_AUDACIOUS)
|
||||
(void) scan_bar(arg, &obj->a, &obj->b);
|
||||
END
|
||||
#endif
|
||||
#ifdef BMPX
|
||||
OBJ(bmpx_title, INFO_BMPX)
|
||||
memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
|
||||
@ -3105,6 +3135,48 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
|
||||
255.0f));
|
||||
}
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
OBJ(audacious_status) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_STATUS]);
|
||||
}
|
||||
OBJ(audacious_song) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG]);
|
||||
}
|
||||
OBJ(audacious_song_length) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_LENGTH]);
|
||||
}
|
||||
OBJ(audacious_song_length_seconds) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_LENGTH_SECONDS]);
|
||||
}
|
||||
OBJ(audacious_song_length_frames) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_LENGTH_FRAMES]);
|
||||
}
|
||||
OBJ(audacious_song_output_length) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_OUTPUT_LENGTH]);
|
||||
}
|
||||
OBJ(audacious_song_output_length_seconds) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_OUTPUT_LENGTH_SECONDS]);
|
||||
}
|
||||
OBJ(audacious_song_output_length_frames) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_OUTPUT_LENGTH_FRAMES]);
|
||||
}
|
||||
OBJ(audacious_song_bitrate) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_BITRATE]);
|
||||
}
|
||||
OBJ(audacious_song_frequency) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_SONG_FREQUENCY]);
|
||||
}
|
||||
OBJ(audacious_song_channels) {
|
||||
snprintf(p, p_max_size, "%s", cur->audacious.items[AUDACIOUS_STATUS]);
|
||||
}
|
||||
OBJ(audacious_bar) {
|
||||
double progress;
|
||||
progress= atof(cur->audacious.items[AUDACIOUS_SONG_OUTPUT_LENGTH_SECONDS]) /
|
||||
atof(cur->audacious.items[AUDACIOUS_SONG_LENGTH_SECONDS]);
|
||||
new_bar(p,obj->a,obj->b,(int)(progress*255.0f));
|
||||
|
||||
}
|
||||
#endif
|
||||
#ifdef BMPX
|
||||
OBJ(bmpx_title) {
|
||||
snprintf(p, p_max_size, "%s", cur->bmpx.title);
|
||||
@ -5493,6 +5565,22 @@ int main(int argc, char **argv)
|
||||
ERR("error setting signal handler: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
#ifdef AUDACIOUS
|
||||
/* joinable thread for audacious activity */
|
||||
pthread_attr_init(&info.audacious.thread_attr);
|
||||
pthread_attr_setdetachstate(&info.audacious.thread_attr, PTHREAD_CREATE_JOINABLE);
|
||||
/* init mutexex */
|
||||
pthread_mutex_init(&info.audacious.item_mutex, NULL);
|
||||
pthread_mutex_init(&info.audacious.runnable_mutex, NULL);
|
||||
/* init runnable condition for worker thread */
|
||||
pthread_mutex_lock(&info.audacious.runnable_mutex);
|
||||
info.audacious.runnable=1;
|
||||
pthread_mutex_unlock(&info.audacious.runnable_mutex);
|
||||
if (pthread_create(&info.audacious.thread, &info.audacious.thread_attr, audacious_thread_func, NULL))
|
||||
{
|
||||
CRIT_ERR("unable to create audacious thread!");
|
||||
}
|
||||
#endif
|
||||
#ifdef INFOPIPE
|
||||
/* joinable thread for infopipe activity */
|
||||
pthread_attr_init(&info.infopipe.thread_attr);
|
||||
@ -5512,6 +5600,21 @@ int main(int argc, char **argv)
|
||||
|
||||
main_loop();
|
||||
|
||||
#ifdef AUDACIOUS
|
||||
/* signal audacious worker thread to terminate */
|
||||
pthread_mutex_lock(&info.audacious.runnable_mutex);
|
||||
info.audacious.runnable=0;
|
||||
pthread_mutex_unlock(&info.audacious.runnable_mutex);
|
||||
/* destroy thread attribute and wait for thread */
|
||||
pthread_attr_destroy(&info.audacious.thread_attr);
|
||||
if (pthread_join(info.audacious.thread, NULL))
|
||||
{
|
||||
ERR("error joining audacious thread");
|
||||
}
|
||||
/* destroy mutexes */
|
||||
pthread_mutex_destroy(&info.audacious.item_mutex);
|
||||
pthread_mutex_destroy(&info.audacious.runnable_mutex);
|
||||
#endif
|
||||
#ifdef INFOPIPE
|
||||
/* signal infopipe worker thread to terminate */
|
||||
pthread_mutex_lock(&info.infopipe.runnable_mutex);
|
||||
|
20
src/conky.h
20
src/conky.h
@ -9,7 +9,7 @@
|
||||
#ifndef _conky_h_
|
||||
#define _conky_h_
|
||||
|
||||
#ifdef INFOPIPE
|
||||
#if defined(AUDACIOUS) || defined(INFOPIPE)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#if defined(HAS_MCHECK_H)
|
||||
@ -127,6 +127,18 @@ struct mpd_s {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef AUDACIOUS
|
||||
#include "audacious.h"
|
||||
struct audacious_s {
|
||||
audacious_t items; /* e.g. items[AUDACIOUS_STATUS] yields char[] */
|
||||
int runnable; /* used to signal infopipe thread to stop */
|
||||
pthread_t thread; /* worker thread for infopipe updating */
|
||||
pthread_attr_t thread_attr; /* thread attributes */
|
||||
pthread_mutex_t item_mutex; /* mutex for item array */
|
||||
pthread_mutex_t runnable_mutex; /* mutex for runnable flag */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef BMPX
|
||||
void update_bmpx();
|
||||
struct bmpx_s {
|
||||
@ -194,6 +206,9 @@ enum {
|
||||
#ifdef INFOPIPE
|
||||
INFO_INFOPIPE = 24,
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
INFO_AUDACIOUS = 25,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -237,6 +252,9 @@ struct information {
|
||||
struct mpd_s mpd;
|
||||
mpd_Connection *conn;
|
||||
#endif
|
||||
#ifdef AUDACIOUS
|
||||
struct audacious_s audacious;
|
||||
#endif
|
||||
#ifdef BMPX
|
||||
struct bmpx_s bmpx;
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user