Subversion Repositories mdb

Rev

Rev 32 | Rev 35 | 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 FLAC, 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);
        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 (_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);
                strcat(fname, NOWPLAY);

                if (access(fname, R_OK))
                {
                        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, flag, 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;
                }

                flag = FALSE;
                num = 0;
                nextNum = -1;

                if (start > 0 && start <= queueTotal)
                        num = start - 1;

                scanQueue(fd);
                close(fd);

                if (playerRandom)
                        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;
                                }
                        }

                        flag = 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 (!flag)
                        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;
        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)
        {
                syslog(LOG_DAEMON, "Error opening live playback device: %s", strerror(errno));
                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, &current_frame, &frames_left, &current_seconds, &seconds_left);
                        act_sec = current_seconds;
                        flag = TRUE;
                }
                else
                {
                        double cs, sl;
                        mpg123_position(mh, frame, done, &current_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, 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%s\t%s\t%s\t%s\n", path, 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.