Rev 39 | Rev 49 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2015 by Andreas Theofilu <andreas@theosys.at>
*
* All rights reserved. No warranty, explicit or implicit, provided.
*
* NOTICE: All information contained herein is, and remains
* the property of Andreas Theofilu and his suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to Andreas Theofilu and its suppliers and
* may be covered by European and Foreign Patents, patents in process,
* and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Andreas Theofilu.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <ctype.h>
#include <math.h>
#include <syslog.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <sqlite3.h>
#include <ao/ao.h>
#include <mpg123.h>
#include "config.h"
#include "mdb.h"
#include "helplib.h"
#include "play.h"
#include "play_flac.h"
#include "user.h"
#define CMD_SET_NONE 0
#define CMD_SET1 1
#define CMD_SET2 2
#define NOWPLAY "/nowplaying.list"
#define BITS 8
int nextCommand = PLAY_NONE;
int playStatus;
int playerActive = FALSE;
int playerRepeat = FALSE;
int playerRandom = FALSE;
ST_PLAYING playCurrent;
QUEUE *pqueue = NULL;
int queueTotal = 0;
char aoOutPlayers[15][16] =
{
"aixs",
"alsa",
"arts",
"esd",
"irix",
"macosx",
"nas",
"null",
"oss",
"pulse",
"roar",
"sndio",
"sun",
"wmm",
"\0"
};
/* Prototypes */
static int playCallback(void *hint, int argc, char **argv, char **azColName);
void playMP3(int s1, char *file);
int checkQueueDouble(int id);
void freeQueue();
void setCurrentQueue(QUEUE *q);
QUEUE *getRandomQueue();
QUEUE *getQueue(int idx);
void setCurrentQueue(QUEUE *q);
QUEUE *addToQueue(int id, char *path, int fType, char *title, char *artist, char *album, char *genre, char *cover);
/*
* This function is called as a thread.
* It uses the libmpg123 library to decode a MP3 file and the library libao
* to play the decoded samples.
* TODO: Implement the selection for a different audio hardware.
* TODO: Implement a player for the formats OGG and WAV
*/
void *pthr_playfile(void *pV_data)
{
char fname[256], query[8192], hv0[256];
int rc, flag, start;
sqlite3 *db;
char *zErrMsg = 0;
struct ST_PlayPars *_playPars;
playerActive = TRUE; /* Kind of locking :-) */
_playPars = (struct ST_PlayPars *)pV_data;
start = 0;
if (_playPars == NULL)
{
syslog(LOG_DAEMON, "Internal error: Invalid or no parameters for thread \"pthr_playfile\"!");
playerActive = FALSE;
return NULL;
}
/* retrieve the data from the database */
strcpy(fname, configs.home); /* Base file name */
strcat(fname, MUSICDB); /* File name of database */
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:PLAY:Error opening database;");
write (_playPars->s1, query, strlen(query));
playerActive = FALSE;
return NULL;
}
strcpy (query, "select id, path, type, title, interpret, album, genre, cover from \"main\".\"musicdb\" where ");
flag = FALSE;
if (strcasecmp(_playPars->type, "ID") == 0)
{
strcat(query, "id = ");
strcat(query, _playPars->what);
strcpy(fname, configs.home);
strcat(fname, NOWPLAY);
unlink(fname);
freeQueue();
flag = TRUE;
}
else if (strcasecmp(_playPars->type, "TITLE") == 0)
{
strcat(query, "title = \"");
strcat(query, _playPars->what);
strcat(query, "\" order by title");
flag = TRUE;
}
else if (strcasecmp(_playPars->type, "ARTIST") == 0)
{
strcat(query, "interpret = \"");
strcat(query, _playPars->what);
strcat(query, "\" order by interpret");
flag = TRUE;
}
else if (strcasecmp(_playPars->type, "ALBUM") == 0)
{
strcat(query, "album = \"");
strcat(query, _playPars->what);
strcat(query, "\" order by album");
flag = TRUE;
}
else if (strcasecmp(_playPars->type, "GENRE") == 0)
{
strcat(query, "genre = \"");
strcat(query, _playPars->what);
strcat(query, "\" order by genre");
flag = TRUE;
}
else if (strcasecmp(_playPars->type, "QUEUE") == 0)
{
strcpy(fname, configs.home); /* Base path */
strcat(fname, NOWPLAY); /* Append file name of queue to path */
if (access(fname, R_OK)) /* In case we've no access or the file doesn't exist, quit this thread. */
{
strcpy (hv0, "ERROR:PLAY:No or empty queue;");
write (_playPars->s1, hv0, strlen(hv0));
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
start = atoi(_playPars->what);
}
else if (strcasecmp(_playPars->type, "PLAYLIST") == 0)
{
USERS *act;
if (userchain == NULL)
{
strcpy (hv0, "ERROR:PLAY:No user selected;");
write (_playPars->s1, hv0, strlen(hv0));
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
/* find uid */
if ((act = findPlaylist(userchain->uname, _playPars->what)) != NULL)
{
if (!playlistToQueue(act->id, TRUE))
{
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
}
else if (isnumeric(_playPars->what))
{
int id;
id = atoi(_playPars->what);
if (!playlistToQueue(id, TRUE))
{
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
}
else
{
strcpy (hv0, "ERROR:PLAY:No playlist found;");
write (_playPars->s1, hv0, strlen(hv0));
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
}
else
{
strcpy(hv0, "ERROR:PLAY:Missing type of argument;");
write(_playPars->s1, hv0, strlen(hv0));
sqlite3_close(db);
playerActive = FALSE;
return NULL;
}
if (flag && (rc = sqlite3_exec(db, query, playCallback, (void *)_playPars->what, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
strcpy(query, "ERROR:PLAY:SQL error;");
write (_playPars->s1, query, strlen(query));
playerActive = FALSE;
return NULL;
}
strcpy (fname, configs.home);
strcat (fname, NOWPLAY);
/* start the player and play the file(s) */
if (!access(fname, R_OK))
{
int fd, iflag, num, nextNum;
QUEUE *act;
if ((fd = open(fname, O_RDONLY)) < 0)
{
syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
playerActive = FALSE;
return NULL;
}
iflag = FALSE;
num = 0;
nextNum = -1;
if (start > 0 && start <= queueTotal)
num = start - 1;
scanQueue(fd);
close(fd);
if (playerRandom && !flag && start <= 0)
act = getRandomQueue();
else
act = getQueue(num);
while (act)
{
char *title, *artist, *album, *cover;
if (nextNum >= 0)
{
if (num == nextNum)
nextNum = -1;
else
{
num++;
act = getQueue(num);
continue;
}
}
iflag = TRUE;
title = urlencode(act->title);
artist = urlencode(act->artist);
album = urlencode(act->album);
cover = urlencode(act->cover);
sprintf(query, "PLAYING:%d:", act->id);
if (title != NULL)
{
strcat(query, title);
free(title);
}
else
strcat(query, act->title);
strcat(query, ":");
if (artist != NULL)
{
strcat(query, artist);
free(artist);
}
else
strcat(query, act->artist);
strcat(query, ":");
if (album != NULL)
{
strcat(query, album);
free(album);
}
else
strcat(query, act->album);
strcat(query, ":");
strcat(query, act->genre);
strcat(query, ":");
if (cover != NULL)
{
strcat(query, cover);
free(cover);
}
else
strcat(query, act->cover);
strcat(query, ";");
write(_playPars->s1, query, strlen(query));
setCurrentQueue(act);
act->played = TRUE;
if (act->fType == FILE_TYPE_MP3)
playMP3(_playPars->s1, act->path);
else
playFlac(_playPars->s1, act->path);
if (playStatus == PLAY_STATUS_SKIPF)
playStatus = PLAY_STATUS_STOP;
else if (playStatus == PLAY_STATUS_SKIPR && !playerRandom)
{
if (num > 0)
{
nextNum = num - 1;
num = 0;
act = pqueue;
continue;
}
playStatus = PLAY_STATUS_STOP;
}
else if (playStatus == PLAY_STATUS_STOP)
break;
num++;
if (playerRandom)
act = getRandomQueue();
else
act = getQueue(num);
if (playerRepeat && act == NULL)
{
num = 0;
act = pqueue;
}
}
if (!iflag)
syslog(LOG_WARNING, "Found no files to play!");
close (fd);
}
else
syslog(LOG_WARNING, "Error accessing file %s: %s", fname, strerror(errno));
playerActive = FALSE;
return NULL;
}
static int playCallback(void *hint, int argc, char **argv, char **azColName)
{
int i, id, fd, fType;
char path[512], id3_title[256], id3_artist[256], id3_album[256], id3_genre[256], id3_cover[512];
char buffer[8192], *what;
what = (char *)hint;
memset(path, 0, sizeof(path));
memset(id3_title, 0, sizeof(id3_title));
memset(id3_artist, 0, sizeof(id3_artist));
memset(id3_album, 0, sizeof(id3_album));
memset(id3_genre, 0, sizeof(id3_genre));
memset(id3_cover, 0, sizeof(id3_cover));
id = -1;
for(i = 0; i < argc; i++)
{
if (strcasecmp(azColName[i], "id") == 0)
if (argv[i])
id = atoi(argv[i]);
if (strcasecmp(azColName[i], "path") == 0)
if (argv[i])
strncpy(path, argv[i], sizeof(path));
if (strcasecmp(azColName[i], "type") == 0)
if (argv[i])
fType = atoi(argv[i]);
if (strcasecmp(azColName[i], "title") == 0)
if (argv[i])
strncpy(id3_title, argv[i], sizeof(id3_title));
if (strcasecmp(azColName[i], "interpret") == 0)
if (argv[i])
strncpy(id3_artist, argv[i], sizeof(id3_artist));
if (strcasecmp(azColName[i], "album") == 0)
if (argv[i])
strncpy(id3_album, argv[i], sizeof(id3_album));
if (strcasecmp(azColName[i], "genre") == 0)
if (argv[i])
strncpy(id3_genre, argv[i], sizeof(id3_genre));
if (strcasecmp(azColName[i], "cover") == 0)
if (argv[i])
strncpy(id3_cover, argv[i], sizeof(id3_cover));
}
if (id != -1 && strlen(path) > 0)
{
char fname[512];
strcpy(fname, configs.home);
strcat(fname, NOWPLAY);
if (checkQueueDouble(id) == TRUE) /* Make sure every ID in the queue is unique */
return 0;
if ((fd = open(fname, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP)) < 0)
{
syslog(LOG_WARNING, "Error creating or writing to file %s: %s", fname, strerror(errno));
return 1;
}
sprintf (buffer, "%s\t%d\t%d\t%s\t%s\t%s\t%s\t%s\n", path, fType, id, id3_title, id3_artist, id3_album, id3_genre, id3_cover);
write (fd, buffer, strlen(buffer));
addToQueue(id, path, fType, id3_title, id3_artist, id3_album, id3_genre, id3_cover);
close (fd);
}
else
syslog(LOG_WARNING, "Found no data to play");
return 0;
}
void playMP3(int s1, char *file)
{
mpg123_handle *mh;
unsigned char *buffer;
size_t buffer_size;
size_t done;
off_t frame;
int err, i, flag;
char hv0[128];
int driver;
ao_device *dev;
ao_option ao_opts;
ao_sample_format format;
int channels, encoding;
long rate;
double current_seconds, seconds_left, act_sec;
off_t current_frame, frames_left;
if(file == NULL || strlen(file) == 0)
{
syslog(LOG_DAEMON, "No or invalid file was passed to player!");
return;
}
/* Check if we've a valid sound driver defined */
flag = FALSE;
i = 0;
while (aoOutPlayers[i][0])
{
if (!strcasecmp(aoOutPlayers[i], configs.player))
{
flag = TRUE;
if (configs.debug)
syslog(LOG_DEBUG, "Found sound driver %s to use for playback.", aoOutPlayers[i]);
break;
}
i++;
}
/* initializations */
ao_initialize();
if (flag)
{
if ((driver = ao_driver_id(configs.player)) == -1)
{
syslog(LOG_DAEMON, "Error finding the audio out driver %s!", configs.player);
ao_shutdown();
return;
}
}
else if ((driver = ao_default_driver_id()) == -1)
{
syslog(LOG_DAEMON, "Error finding a default audio driver!");
ao_shutdown();
return;
}
mpg123_init();
if ((mh = mpg123_new(NULL, &err)) == NULL)
{
syslog(LOG_DAEMON, "Error creating new MP3 handle: %s", mpg123_plain_strerror(err));
ao_shutdown();
return;
}
buffer_size = mpg123_outblock(mh);
if ((buffer = (unsigned char*)malloc(buffer_size * sizeof(unsigned char))) == NULL)
{
syslog(LOG_DAEMON, "Error allocating memory for MP3 player: %s", strerror(errno));
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
return;
}
/* open the file and get the decoding format */
if (mpg123_open(mh, file) != MPG123_OK)
{
syslog(LOG_DAEMON, "Error opening MP3 file %s: %s", file, mpg123_strerror(mh));
free(buffer);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
return;
}
if (mpg123_scan(mh) != MPG123_OK)
syslog(LOG_WARNING, "Error scanning MP3 file %s: %s", file, mpg123_strerror(mh));
if (mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK)
{
syslog(LOG_DAEMON, "Error getting the format of MP3 file %s: %s", file, mpg123_strerror(mh));
free(buffer);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
return;
}
/* set the output format and open the output device */
format.bits = mpg123_encsize(encoding) * BITS;
format.rate = rate;
format.channels = channels;
format.byte_format = AO_FMT_NATIVE;
format.matrix = 0;
if (configs.debug)
{
syslog(LOG_DEBUG, "Format: Bits: %d", format.bits);
syslog(LOG_DEBUG, "Format: Rate: %ld", rate);
syslog(LOG_DEBUG, "Format: Channels: %d", channels);
}
memset(&ao_opts, 0, sizeof(ao_option));
if (configs.debug)
{
strcpy(hv0, "debug");
ao_opts.key = strdup(hv0);
strcpy(hv0, "1");
ao_opts.value = strdup(hv0);
dev = ao_open_live(driver, &format, &ao_opts);
syslog(LOG_DEBUG, "Switching on debugging for AO library.");
}
else
dev = ao_open_live(driver, &format, NULL);
if (dev == NULL)
{
switch (errno)
{
case AO_ENODRIVER: sprintf(hv0, "No driver corresponds to \"driver_id\"."); break;
case AO_ENOTLIVE: sprintf(hv0, "This driver (%s) is not a live output device.", configs.player); break;
case AO_EBADOPTION: sprintf(hv0, "A valid option key has an invalid value."); break;
case AO_EOPENDEVICE:sprintf(hv0, "Cannot open the device."); break;
default:
sprintf(hv0, "%s", strerror(errno));
}
syslog(LOG_DAEMON, "Error opening live playback device: %s", hv0);
free(buffer);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
if (configs.debug)
{
free(ao_opts.key);
free(ao_opts.value);
}
return;
}
playStatus = PLAY_STATUS_PLAY;
strcpy (hv0, "PLAYER:PLAY;");
write(s1, hv0, strlen(hv0));
act_sec = 0.0;
flag = FALSE;
frame = mpg123_tellframe(mh);
/* decode and play */
while (mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK)
{
int todo;
char hv1[32], hv2[32], hv3[32];
todo = check_command(s1);
if (todo == PLAY_STATUS_STOP)
break;
else if (todo == PLAY_STATUS_FWD)
{
mpg123_seek_frame(mh, 100, SEEK_CUR);
playStatus = PLAY_STATUS_PLAY;
continue;
}
else if (todo == PLAY_STATUS_REW)
{
off_t fr;
fr = mpg123_tellframe(mh);
if (fr > 100)
fr -= 100;
else
fr = 0;
mpg123_seek_frame(mh, fr, SEEK_SET);
playStatus = PLAY_STATUS_PLAY;
continue;
}
/* get position */
if (!flag)
{
mpg123_position(mh, mpg123_tellframe(mh), done, ¤t_frame, &frames_left, ¤t_seconds, &seconds_left);
act_sec = current_seconds;
flag = TRUE;
}
else
{
double cs, sl;
mpg123_position(mh, frame, done, ¤t_frame, &frames_left, &cs, &sl);
frame = mpg123_tellframe(mh) / 100;
act_sec = cs;
}
sprintf(hv0, "POSITION:%s:%s:%s;", secondsToString(act_sec, &hv1[0]), secondsToString((current_seconds + seconds_left) - act_sec, &hv2[0]), secondsToString(current_seconds + seconds_left, &hv3[0]));
write(s1, hv0, strlen(hv0));
ao_play(dev, (char *)buffer, done);
}
/* clean up */
strcpy(hv0, "PLAYER:STOP;");
write (s1, hv0, strlen(hv0));
free(buffer);
ao_close(dev);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
if (configs.debug)
{
free(ao_opts.key);
free(ao_opts.value);
}
}
int check_command(int s1)
{
char hv0[64];
if (nextCommand == PLAY_STOP_NOW)
{
playStatus = PLAY_STATUS_STOP;
return playStatus;
}
if (nextCommand == PLAY_PLAY && (playStatus == PLAY_STATUS_FWD || playStatus == PLAY_STATUS_REW))
{
playStatus = PLAY_STATUS_PLAY;
return playStatus;
}
if (nextCommand == PLAY_STOP && (playStatus == PLAY_STATUS_PLAY || playStatus == PLAY_STATUS_PAUSE))
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_STOP;
return playStatus;
}
if ((nextCommand == PLAY_PAUSE || nextCommand == PLAY_PLAYPAUSE) && playStatus == PLAY_STATUS_PLAY)
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_PAUSE;
strcpy (hv0, "PLAYER:PAUSE;");
write (s1, hv0, strlen(hv0));
while (nextCommand != PLAY_PLAY && nextCommand != PLAY_PLAYPAUSE)
sleep(1);
playStatus = PLAY_STATUS_PLAY;
strcpy (hv0, "PLAYER:PLAY;");
write (s1, hv0, strlen(hv0));
nextCommand = PLAY_NONE;
}
if (nextCommand == PLAY_FWD)
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_FWD;
return PLAY_STATUS_FWD;
}
if (nextCommand == PLAY_REW)
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_REW;
return PLAY_STATUS_REW;
}
if (nextCommand == PLAY_SKIP_FWD)
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_SKIPF;
return PLAY_STATUS_STOP;
}
if (nextCommand == PLAY_SKIP_REW)
{
nextCommand = PLAY_NONE;
playStatus = PLAY_STATUS_SKIPR;
return PLAY_STATUS_STOP;
}
return 0;
}
int checkQueueDouble(int id)
{
QUEUE *act;
act = pqueue;
while (act)
{
if (act->id == id)
return TRUE;
act = act->next;
}
return FALSE;
}
void appendToQueue(int s1, char *type, char *what)
{
char fname[256], query[1024];
int rc;
sqlite3 *db;
char *zErrMsg = 0;
/* retrieve the data from the database */
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:PLAY:Error opening database;");
write (s1, query, strlen(query));
playerActive = FALSE;
return;
}
strcpy (query, "select id, path, type, title, interpret, album, genre, cover from \"main\".\"musicdb\" where ");
if (strcasecmp(type, "ID") == 0)
{
strcat(query, "id = ");
strcat(query, what);
}
else if (strcasecmp(type, "TITLE") == 0)
{
strcat(query, "title = \"");
strcat(query, what);
strcat(query, "\" order by title");
}
else if (strcasecmp(type, "ARTIST") == 0)
{
strcat(query, "interpret = \"");
strcat(query, what);
strcat(query, "\" order by interpret");
}
else if (strcasecmp(type, "ALBUM") == 0)
{
strcat(query, "album = \"");
strcat(query, what);
strcat(query, "\" order by album");
}
else if (strcasecmp(type, "GENRE") == 0)
{
strcat(query, "genre = \"");
strcat(query, what);
strcat(query, "\" order by genre");
}
else if (strcasecmp(type, "PLAYLIST") == 0)
{
USERS *act;
sqlite3_close(db);
act = userchain;
while (act)
{
if (strcmp(act->playlist, what) == 0)
{
playlistToQueue(act->id, FALSE);
break;
}
act = act->next;
}
if (act == NULL)
{
strcpy(query, "ERROR:PLAY:Error transfering playlist entries to queue;");
write (s1, query, strlen(query));
}
return;
}
if ((rc = sqlite3_exec(db, query, playCallback, (void *)what, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
strcpy(query, "ERROR:PLAY:SQL error;");
write (s1, query, strlen(query));
}
readQueue();
}
QUEUE *addToQueue(int id, char *path, int fType, char *title, char *artist, char *album, char *genre, char *cover)
{
QUEUE *act, *neu;
/* find the end of the chain */
act = pqueue;
while (act && act->next)
{
if (act->id == id) /* If the ID is already in the chain, return the pointer to it */
return act;
act = act->next;
}
/* Allocate memory for a new element in the chain */
if ((neu = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
{
syslog(LOG_DAEMON, "Error allocating %lu bytes for an entry in the queue: %s", sizeof(QUEUE), strerror(errno));
return NULL;
}
memset(neu, 0, sizeof(QUEUE));
/* Copy the data into the new element */
neu->id = id;
neu->fType = fType;
if (path)
strncpy (neu->path, path, sizeof(neu->path)-1);
if (title)
strncpy (neu->title, title, sizeof(neu->title)-1);
if (artist)
strncpy (neu->artist, artist, sizeof(neu->artist)-1);
if (album)
strncpy (neu->album, album, sizeof(neu->album)-1);
if (genre)
strncpy (neu->genre, genre, sizeof(neu->genre)-1);
if (cover)
strncpy (neu->cover, cover, sizeof(neu->cover)-1);
/* Append the element to the end of the chain */
if (act == NULL)
act = pqueue = neu;
else
act->next = neu;
return neu;
}
/*
* This function copies the content of a playlist into the queue.
* The parameter defines whether the current queue should be deleted or not.
* If the queue should not be deleted, the playlist will be appended.
*/
int playlistToQueue(int uid, int del)
{
char fname[256], query[1024], hv0[256], buffer[8192];
int rc;
sqlite3 *db;
sqlite3_stmt *res;
char path[512], id3_title[256], id3_artist[256], id3_album[256], id3_genre[256];
int id, fd, fType;
unsigned int mode;
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
return FALSE;
}
strcpy (query, "select id, path, type, title, interpret, album, genre from \"main\".\"musicdb\" as a where ");
strcat (query, "(select musicid from \"main\".\"playlists\" as b where a.id = b.musicid and b.userid = ");
sprintf(hv0, "%d", uid);
strcat (query, hv0);
strcat (query, ")");
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
sqlite3_close(db);
return FALSE;
}
/* Open the queue */
strcpy(fname, configs.home);
strcat(fname, NOWPLAY);
if (!del)
mode = O_RDWR | O_APPEND | O_CREAT;
else
mode = O_RDWR | O_TRUNC | O_CREAT;
if ((fd = open(fname, mode, S_IWUSR | S_IRUSR | S_IRGRP)) <= 0)
{
syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
sqlite3_finalize(res);
sqlite3_close(db);
return FALSE;
}
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
memset(path, 0, sizeof(path));
memset(id3_title, 0, sizeof(id3_title));
memset(id3_artist, 0, sizeof(id3_artist));
memset(id3_album, 0, sizeof(id3_album));
memset(id3_genre, 0, sizeof(id3_genre));
id = sqlite3_column_int(res, 0);
strncpy(path, (const char *)sqlite3_column_text(res, 1), sizeof(path));
fType = sqlite3_column_int(res, 2);
strncpy(id3_title, (const char *)sqlite3_column_text(res, 3), sizeof(id3_title));
strncpy(id3_artist, (const char *)sqlite3_column_text(res, 4), sizeof(id3_artist));
strncpy(id3_album, (const char *)sqlite3_column_text(res, 5), sizeof(id3_album));
strncpy(id3_genre, (const char *)sqlite3_column_text(res, 6), sizeof(id3_genre));
sprintf (buffer, "%s\t%d\t%d\t%s\t%s\t%s\t%s\n", path, fType, id, id3_title, id3_artist, id3_album, id3_genre);
write (fd, buffer, strlen(buffer));
}
scanQueue(fd);
close(fd);
sqlite3_finalize(res);
sqlite3_close(db);
return TRUE;
}
int QueueToPlaylist(int s1, char *user, char *playlist)
{
char fname[256], query[1024], hv0[256];
int rc;
sqlite3 *db;
char *zErrMsg = 0;
sqlite3_stmt *res;
int num;
USERS *usp;
QUEUE *act;
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
return FALSE;
}
if ((usp = findPlaylist(user, playlist)) != NULL)
{
sprintf(query, "delete from playlists where userid = %d", usp->id);
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
sqlite3_close(db);
return FALSE;
}
sqlite3_step(res);
}
else if (createUser(s1, user, playlist))
{
if ((usp = findPlaylist(user, playlist)) == NULL)
return FALSE;
}
else
return FALSE;
/* Update the queue in case there is none in memory*/
if (pqueue == NULL)
readQueue();
act = pqueue;
num = 0;
while (act)
{
strcpy (query, "insert into playlists (userid, musicid) values (");
sprintf(hv0, "%d", usp->id);
strcat (query, hv0);
strcat (query, ",");
sprintf(hv0, "%d", act->id);
strcat (query, hv0);
strcat (query, ")");
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return FALSE;
}
num++;
act = act->next;
}
sqlite3_close(db);
sprintf(hv0, "TRANSFERED:%d;", num);
write (s1, hv0, strlen(hv0));
return TRUE;
}
void freeQueue()
{
QUEUE *act;
act = pqueue;
while (act)
{
pqueue = act->next;
free (act);
act = pqueue;
}
pqueue = NULL;
queueTotal = 0;
}
int readQueue()
{
int fd;
char fname[256];
strcpy (fname, configs.home);
strcat (fname, NOWPLAY);
/* start the player and play the file(s) */
if (!access(fname, R_OK))
{
if ((fd = open(fname, O_RDONLY)) < 0)
{
syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
return FALSE;
}
scanQueue(fd);
close(fd);
}
else
return FALSE;
return TRUE;
}
int scanQueue(int fd)
{
QUEUE *act;
int count, x;
char buffer[8192], *t;
if (pqueue != NULL)
freeQueue();
count = 0;
lseek (fd, 0L, SEEK_SET);
while (readLine (fd, buffer, sizeof(buffer)) != NULL)
{
QUEUE *new;
if ((new = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
{
syslog(LOG_DAEMON, "Error allocating %lu bytes of memory: %s", sizeof(QUEUE), strerror(errno));
lseek(fd, 0L, SEEK_SET);
return -1;
}
memset(new, 0, sizeof(QUEUE));
if (count > 0)
act->next = new;
else
pqueue = new;
act = new;
x = 0;
t = strsplitt(buffer, '\t');
while (t)
{
switch (x)
{
case 0: strncpy(act->path, t, sizeof(act->path)-1); break;
case 1: act->fType = atoi(t); break;
case 2: act->id = atoi(t); break;
case 3: strncpy(act->title, t, sizeof(act->title)-1); break;
case 4: strncpy(act->artist, t, sizeof(act->artist)-1); break;
case 5: strncpy(act->album, t, sizeof(act->album)-1); break;
case 6: strncpy(act->genre, t, sizeof(act->genre)-1); break;
case 7: strncpy(act->cover, t, sizeof(act->cover)-1); break;
}
t = strsplitt(NULL, '\t');
x++;
}
count++;
}
queueTotal = count;
return count;
}
void setCurrentQueue(QUEUE *q)
{
if (q == NULL)
return;
memset(&playCurrent, 0, sizeof(ST_PLAYING));
playCurrent.id = q->id;
strncpy (playCurrent.title, q->title, sizeof(playCurrent.title));
strncpy (playCurrent.artist, q->artist, sizeof(playCurrent.artist));
strncpy (playCurrent.album, q->album, sizeof(playCurrent.album));
strncpy (playCurrent.genre, q->genre, sizeof(playCurrent.genre));
strncpy (playCurrent.cover, q->cover, sizeof(playCurrent.cover));
}
QUEUE *getQueue(int idx)
{
QUEUE *act;
int pos;
act = pqueue;
pos = 0;
while (act)
{
if (pos == idx)
return act;
pos++;
act = act->next;
}
return NULL;
}
QUEUE *getRandomQueue()
{
QUEUE *act;
int pos;
long r;
if (pqueue == NULL)
return NULL;
if (queueTotal > 1)
r = random_at_most((long)queueTotal-1);
else
{
if (pqueue->played)
return pqueue->next;
else
return pqueue;
}
pos = 0;
act = pqueue;
/* Scan queue and check wheter all entries are allready played */
while (act)
{
if (!act->played)
break;
act = act->next;
}
if (act == NULL) /* If all is played, we reset the status and start again */
{
act = pqueue;
while (act)
{
act->played = FALSE;
act = act->next;
}
}
act = pqueue;
while (act)
{
if (pos == r) /* have we reached the random position? */
{ /* Yes, then check if this entry was already played. */
if (act->played) /* Was it played? */
{ /* Yes, then reset and try again */
r = random_at_most((long)queueTotal - 1);
act = pqueue;
pos = 0;
continue;
}
else /* It was not played before so */
return act; /* return the pointer to it */
}
pos++;
act = act->next;
}
return NULL;
}
Generated by GNU Enscript 1.6.5.90.