kmediad 0.5.0a
A cross-platform Web-based audio player
Defines | Functions
kmediad_gui.c File Reference

Implementation of the kmediad_Gui class. More...

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <klib.h>
#include <khttpd.h>
#include "kmediad_utils.h"
#include "kmediad_gui.h"
#include "kmediad_server.h"
#include "kmediad_database.h"
#include "kmediad_album.h"
#include "kmediad_mediainfo.h"

Defines

#define EMPTY_DIR   "Directory is empty"
#define NO_ALBUMS   "No matching albums"
#define NO_TRACKS   "No matching tracks"
#define NO_ARTISTS   "No matching artists"
#define NO_COMPOSERS   "No matching composers"
#define NO_GENRES   "No matching genres"
#define EMPTY_PLAYLIST   "Playlist is empty"
#define NO_SEARCH_TERM   "Search term empty"
#define HTML_ADD   "<span class=\"buttonspan\">Add</span>"
#define HTML_ADDALL   "<span class=\"buttonspan\">Add all</span>"
#define HTML_PLAY   "<span class=\"buttonspan\">Play&nbsp;now</span>"
#define HTML_INFO   "<span class=\"buttonspan\">Info</span>"
#define HTML_ALBUMS   "<span class=\"buttonspan\">Albums</span>"
#define HTML_TRACKS   "<span class=\"buttonspan\">Tracks</span>"
#define HTML_ARTISTS   "<span class=\"buttonspan\">Artists</span>"
#define HTML_COMPOSERS   "<span class=\"buttonspan\">Composers</span>"
#define HTML_GENRES   "<span class=\"buttonspan\">Genres</span>"
#define HTML_RANDOM   "<span class=\"buttonspan\">Random</span>"
#define ALBUMS_ACROSS   4
#define ALBUMS_PER_PAGE   (ALBUMS_ACROSS * 5)
#define ARTISTS_ACROSS   3
#define ARTISTS_PER_PAGE   (ARTISTS_ACROSS * 10)
#define COMPOSERS_ACROSS   3
#define COMPOSERS_PER_PAGE   (COMPOSERS_ACROSS * 10)
#define GENRES_ACROSS   3
#define GENRES_PER_PAGE   (GENRES_ACROSS * 10)
#define TRACKS_PER_PAGE   20

Functions

void kmediad_gui_construct (kmediad_Gui *self, const char *class_name, XineInterface *xine_interface, kmediad_Server *server)
kmediad_Guikmediad_gui_new (XineInterface *xine_interface, kmediad_Server *server)
void kmediad_gui_dispose (kmediad_Gui *self)
klib_Stringkmediad_gui_make_error_page (const klib_Error *error)
klib_Stringkmediad_gui_process_request_log (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_display_name (kmediad_Gui *self, klib_String *path)
klib_Stringkmediad_gui_make_album_cell (const kmediad_Gui *self, const kmediad_Album *album)
klib_Stringkmediad_gui_make_page_links (int items_per_page, int count, int this_first, kmediad_SearchConstraints *sc, const char *base_url, const char *text)
klib_Stringkmediad_gui_make_track_row (const kmediad_MediaInfo *mi)
klib_Stringkmediad_gui_make_track_list (kmediad_Gui *self, kmediad_SearchConstraints *sc, klib_Error **error)
klib_Stringkmediad_gui_make_artist_list (kmediad_Gui *self, kmediad_SearchConstraints *sc, klib_Error **error)
klib_Stringkmediad_gui_make_composer_list (kmediad_Gui *self, kmediad_SearchConstraints *sc, klib_Error **error)
klib_Stringkmediad_gui_make_genre_list (kmediad_Gui *self, kmediad_SearchConstraints *sc, klib_Error **error)
klib_Stringkmediad_gui_make_album_list (kmediad_Gui *self, kmediad_SearchConstraints *sc, klib_Error **error)
klib_Stringkmediad_gui_process_request_albums (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_tracks (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_artists (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_composers (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_genres (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_simple_search (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_playlist (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_home (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request_files (kmediad_Gui *self, const klib_AssocArray *args)
klib_Stringkmediad_gui_process_request (kmediad_Gui *self, const char *url, const klib_AssocArray *args)
 Process a single request from an HTTP client whose URL was url.
void kmediad_gui_load_templates (kmediad_Gui *self, const klib_Path *template_dir)
 Read all the page templates into memory.
void kmediad_gui_set_media_dir (kmediad_Gui *self, klib_Path *media_dir)
 Sets the current media directory.

Detailed Description

Implementation of the kmediad_Gui class.


Define Documentation

#define ALBUMS_ACROSS   4
#define ALBUMS_PER_PAGE   (ALBUMS_ACROSS * 5)
#define ARTISTS_ACROSS   3
#define ARTISTS_PER_PAGE   (ARTISTS_ACROSS * 10)
#define COMPOSERS_ACROSS   3
#define COMPOSERS_PER_PAGE   (COMPOSERS_ACROSS * 10)
#define EMPTY_DIR   "Directory is empty"
#define EMPTY_PLAYLIST   "Playlist is empty"
#define GENRES_ACROSS   3
#define GENRES_PER_PAGE   (GENRES_ACROSS * 10)
#define HTML_ADD   "<span class=\"buttonspan\">Add</span>"
#define HTML_ADDALL   "<span class=\"buttonspan\">Add all</span>"
#define HTML_ALBUMS   "<span class=\"buttonspan\">Albums</span>"
#define HTML_ARTISTS   "<span class=\"buttonspan\">Artists</span>"
#define HTML_COMPOSERS   "<span class=\"buttonspan\">Composers</span>"
#define HTML_GENRES   "<span class=\"buttonspan\">Genres</span>"
#define HTML_INFO   "<span class=\"buttonspan\">Info</span>"
#define HTML_PLAY   "<span class=\"buttonspan\">Play&nbsp;now</span>"
#define HTML_RANDOM   "<span class=\"buttonspan\">Random</span>"
#define HTML_TRACKS   "<span class=\"buttonspan\">Tracks</span>"
#define NO_ALBUMS   "No matching albums"
#define NO_ARTISTS   "No matching artists"
#define NO_COMPOSERS   "No matching composers"
#define NO_GENRES   "No matching genres"
#define NO_SEARCH_TERM   "Search term empty"
#define NO_TRACKS   "No matching tracks"
#define TRACKS_PER_PAGE   20

Function Documentation

void kmediad_gui_construct ( kmediad_Gui self,
const char *  class_name,
XineInterface xine_interface,
kmediad_Server server 
)
{
  if (klib_object_trace_flags & KLIB_OBJECT_TRACE_CONSTRUCTION)
    klib_log (KLIB_LOG_TRACE, "Called constructor for kmediad_Gui");
  klib_object_construct ((klib_Object *) self, class_name);
  ((klib_Object *)self)->dispose_func = (klib_Object_DisposeFunc) 
     kmediad_gui_dispose;
  self->main_template = klib_string_new 
    ("Page template missing!<p/>%%CONTENT%%");
  self->disposing = FALSE;
  self->server = server;
  self->xine_interface = xine_interface;
  klib_object_add_ref ((klib_Object *)xine_interface);
}
klib_String* kmediad_gui_display_name ( kmediad_Gui self,
klib_String path 
)
  {
  if (!path) return klib_string_new_empty(); // Should not happen
  if (!path->str) return klib_string_new_empty(); // Should not happen
  if (strlen (path->str) == 0) return klib_string_new_empty(); 

  if (path->str[0] == '!')
    return klib_string_new (path->str); // TODO -- tart this up

  char *r = strrchr (path->str, '/');
  if (r)
    return klib_string_new (r + 1); 

  return klib_string_clone (path);
  }
void kmediad_gui_dispose ( kmediad_Gui self)
  {
  if (self->disposing) return;
  self->disposing = TRUE;
  
  if (klib_object_trace_flags & KLIB_OBJECT_TRACE_CONSTRUCTION)
    klib_log (KLIB_LOG_TRACE, "Called destructor for kmediad_Gui");

  if (self->main_template) 
    {
    klib_object_unref ((klib_Object *) self->main_template);
    self->main_template = NULL;
    }

  if (self->media_dir) 
    {
    klib_object_unref ((klib_Object *) self->media_dir);
    self->media_dir = NULL;
    }

  if (self->server) 
    {
    klib_object_unref ((klib_Object *) self->server);
    self->server = NULL;
    }

  if (self->xine_interface) 
    {
    klib_object_unref ((klib_Object *) self->xine_interface);
    self->xine_interface = NULL;
    }

  klib_object_dispose ((klib_Object *) self); 
  }
void kmediad_gui_load_templates ( kmediad_Gui self,
const klib_Path template_dir 
)

Read all the page templates into memory.

These are assumed not to change for the life of the process, so this is quicker then reading them on each request

  {
  klib_log (KLIB_LOG_TRACE, "Loading templates from %s", 
    template_dir->base.str);
 
  klib_Path *main_template_path = klib_path_concatenate
    (template_dir, "_main_template.html"); 

  klib_Error *error = NULL;
  klib_String *s = klib_path_read_string (main_template_path, &error);
  if (error)
    {
    klib_log (KLIB_LOG_ERROR, "Can't read template file %s", 
       main_template_path);
    klib_object_unref ((klib_Object *)error);
    }
  else
    {
    if (self->main_template) 
      klib_object_unref ((klib_Object *) self->main_template);
    self->main_template = s;
    }

  klib_object_unref ((klib_Object *) main_template_path);
  }
klib_String* kmediad_gui_make_album_cell ( const kmediad_Gui self,
const kmediad_Album album 
)
  { 
  klib_String *s = klib_string_new_empty();

  const klib_String *albumname = kmediad_album_name (album); 

  klib_String *albumname_js = klib_string_clone_safe (albumname);
  klib_String *albumname_url = klib_string_clone_safe (albumname);
  klib_string_encode_url (albumname_url);
  klib_string_escape_squote (albumname_js);
  
  klib_String *img = klib_string_new_empty();

  klib_string_append (s, "<table class=\"albumcelltable\">\n");
  klib_string_append (s, "<tr>\n");

  klib_string_append (s, "<td class=\"albumcellalbumnamecell\">\n");
  klib_string_append (s, albumname->str);
  klib_string_append (s, "</td>\n");

  klib_string_append (s, "<td>\n");
  klib_string_append (s, "</tr>\n");

  klib_string_append (s, "<tr>\n");
  klib_string_append (s, "<td class=\"albumcellthumbnailcell\">\n");
  const klib_String *caimg = 
    kmedia_album_coverart_img (album);
  klib_string_printf (img, 
      "<img class=\"coverartthumbnailimg\" src=\"/coverart%s\"/>", 
      caimg->str);
  klib_String *show_tracks_href = klib_string_new_empty();
  klib_string_printf (show_tracks_href, "/gui_tracks?album-is=%s", 
     albumname_url->str); 
  klib_string_printf (s, "<a href=\"%s\">%s</a>", show_tracks_href->str, 
    img->str); 
  klib_object_unref ((klib_Object *)show_tracks_href);
  klib_string_append (s, "</td>\n");
  klib_string_append (s, "</tr>\n");

  klib_string_append (s, "<tr>\n");
  klib_string_append (s, "<td>\n");
  klib_string_printf (s, "%d track%s", kmediad_album_num_tracks 
    (album), kmediad_album_num_tracks (album)==1?"":"s");
  klib_String *play_href = klib_string_new ("");
  klib_string_printf (play_href, "javascript:play_album('%s')",
    albumname_js->str);
  klib_string_printf (s, " <a href=\"%s\">"HTML_PLAY"</a>", play_href->str); 
  klib_object_unref ((klib_Object *)play_href);
  klib_String *add_href = klib_string_new ("");
  klib_string_printf (add_href, "javascript:add_album('%s')",
    albumname_js->str);
  klib_string_printf (s, " <a href=\"%s\">"HTML_ADD"</a>", add_href->str); 
  klib_object_unref ((klib_Object *)add_href);
  klib_string_append (s, "</td>\n");
  klib_string_append (s, "</tr>\n");

  klib_string_append (s, "<tr>\n");
  klib_string_append (s, "<td>\n");
  const klib_List *artists = kmediad_album_artists (album);
  int l = klib_list_length (artists);
  if (l == 1)
   {
   const klib_String *artist = (klib_String *) klib_list_get (artists, 0);
   klib_string_append (s, artist->str);
   }
  else
     klib_string_append (s, "Various artists");

  klib_string_append (s, "</td>\n");
  klib_string_append (s, "</tr>\n");

  klib_string_append (s, "</tr>\n");
  klib_string_append (s, "</table>\n");

  klib_object_unref ((klib_Object *)img);
  klib_object_unref ((klib_Object *)albumname_js);
  klib_object_unref ((klib_Object *)albumname_url);

  return s;
  }
klib_String* kmediad_gui_make_album_list ( kmediad_Gui self,
kmediad_SearchConstraints sc,
klib_Error **  error 
)
  {
  klib_String *page = klib_string_new_empty();
  *error = NULL;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, error);
  if (*error)
    return NULL;

  int count = 0;
  sc->limit = ALBUMS_PER_PAGE; // override request arg
  klib_List *list = kmediad_album_get_albums (self->media_dir, 
    db, sc, &count, error);
  if (*error)
      {
      klib_object_unref ((klib_Object *)db);
      return NULL;
      }

  klib_String *page_links = kmediad_gui_make_page_links (ALBUMS_PER_PAGE,
    count, sc->first, sc, "/gui_albums", "albums");
  klib_string_append (page, page_links->str);
  klib_object_unref ((klib_Object *) page_links);

  klib_string_append (page, "<p/>");

  int i, l = klib_list_length(list);
  // TODO message if there are no matches
  if (l > 0) 
    {
    klib_string_append (page, "<table class=\"albumlisttable\"><tr>\n");

    int rows = l / ALBUMS_ACROSS + 1;
    int cells = ALBUMS_ACROSS * rows;
    for (i = 0; i < cells; i++)
      {
      if (i < l)
        {
        kmediad_Album *album = (kmediad_Album *) klib_list_get (list, i);
        klib_String *albumcell = kmediad_gui_make_album_cell
            (self, album);
        klib_string_append (page, "<td class=\"albumcellcell\">\n");
        klib_string_append (page, albumcell->str);
        klib_string_append (page, "</td>\n");
        klib_object_unref ((klib_Object *)albumcell);
        }
      else
        {
        klib_string_append (page, "<td class=\"albumcellcell\"></td>\n");
        }

      if ((i + 1) % ALBUMS_ACROSS == 0)
        {
        klib_string_append (page, "</tr><tr>\n");
        } 
      }

    klib_string_append (page, "</tr></table>\n");
    }
  else
    klib_string_append (page, NO_ALBUMS);
 
  klib_object_unref ((klib_Object *)list);
  klib_object_unref ((klib_Object *)db);

  klib_string_append (page, "<p/>");
  return page;
  }
klib_String* kmediad_gui_make_artist_list ( kmediad_Gui self,
kmediad_SearchConstraints sc,
klib_Error **  error 
)
  {
  *error = NULL;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, error);
  if (*error)
    return NULL;

  klib_String *page = klib_string_new_empty();

  int count= 0;
  sc->limit = ARTISTS_PER_PAGE; // override request arg
  klib_List *list = kmediad_database_get_artists (db, sc, &count, error);
  if (*error)
      {
      klib_object_unref ((klib_Object *)db);
      return NULL;
      }

  klib_String *page_links = kmediad_gui_make_page_links (ARTISTS_PER_PAGE,
    count, sc->first, sc, "/gui_artists", "artists");
  klib_string_append (page, page_links->str);
  klib_object_unref ((klib_Object *)page_links);
  klib_string_append (page, "<p/>");

  int i, l = klib_list_length (list); 
  if (l > 0)
    {
    klib_string_append (page, "<table class=\"artistlisttable\"><tr>\n");
    int rows = l / ARTISTS_ACROSS + 1;
    int cells = ARTISTS_ACROSS * rows;
    for (i = 0; i < cells; i++)
      {
      if (i < l)
        {
        const klib_String *artistname = (const klib_String *) 
          klib_list_get (list, i);
        klib_String *artist_url = klib_string_clone_safe (artistname);
        klib_String *artist_js = klib_string_clone_safe (artistname);
        klib_string_encode_url (artist_url);
        klib_string_escape_squote (artist_url);
        klib_string_printf (page, "<td>\n");
        klib_string_printf (page, "%s<br/>\n", artistname->str);
        klib_String *tracks_href = klib_string_new_empty();
        klib_string_printf (tracks_href, "/gui_tracks?artist-is=%s", 
          artist_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_TRACKS"</a>", 
          tracks_href->str);
        klib_object_unref ((klib_Object *)tracks_href);
        klib_String *albums_href = klib_string_new_empty();
        klib_string_printf (albums_href, "/gui_albums?artist-is=%s", 
          artist_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_ALBUMS"</a>", 
          albums_href->str);
        klib_object_unref ((klib_Object *)albums_href);
        klib_String *genres_href = klib_string_new_empty();
        klib_string_printf (genres_href, "/gui_genres?artist-is=%s", 
          artist_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_GENRES"</a>", 
          genres_href->str);
        klib_object_unref ((klib_Object *)genres_href);
        klib_String *random_href = klib_string_new_empty();
        klib_string_printf (random_href, "javascript_add_artist_random('%s')", 
          artist_js->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_RANDOM"</a>", 
          random_href->str);
        klib_object_unref ((klib_Object *)random_href);
        klib_string_printf (page, "</td>\n");
        klib_object_unref ((klib_Object *)artist_url);
        klib_object_unref ((klib_Object *)artist_js);
        }
      else
        {
        klib_string_append (page, "</td><td>\n");
        }
      if ((i + 1) % ARTISTS_ACROSS == 0)
        {
        klib_string_append (page, "</tr><tr>\n");
        } 
      } 
    klib_string_append (page, "<tr></table>\n");
    }
  else
    klib_string_append (page, NO_ARTISTS);

  klib_object_unref ((klib_Object *)list);
  klib_object_unref ((klib_Object *)db);

  klib_string_append (page, "<p>\n");
  return page;
  }
klib_String* kmediad_gui_make_composer_list ( kmediad_Gui self,
kmediad_SearchConstraints sc,
klib_Error **  error 
)
  {
  *error = NULL;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, error);
  if (*error)
    return NULL;

  klib_String *page = klib_string_new_empty();

  int count= 0;
  sc->limit = COMPOSERS_PER_PAGE; // override request arg
  klib_List *list = kmediad_database_get_composers (db, sc, &count, error);
  if (*error)
      {
      klib_object_unref ((klib_Object *)db);
      return NULL;
      }

  klib_String *page_links = kmediad_gui_make_page_links (COMPOSERS_PER_PAGE,
    count, sc->first, sc, "/gui_composers", "composers");
  klib_string_append (page, page_links->str);
  klib_object_unref ((klib_Object *)page_links);
  klib_string_append (page, "<p/>");

  int i, l = klib_list_length (list); 
  if (l > 0)
    {
    int rows = l / COMPOSERS_ACROSS + 1;
    int cells = COMPOSERS_ACROSS * rows;
    klib_string_append (page, "<table class=\"composerlisttable\"><tr>\n");
    for (i = 0; i < cells; i++)
      {
      if (i < l)
        {
        const klib_String *composername = (const klib_String *) 
          klib_list_get (list, i);
        klib_String *composer_url = klib_string_clone_safe (composername);
        klib_String *composer_js = klib_string_clone_safe (composername);
        klib_string_escape_squote (composer_js);
        klib_string_encode_url (composer_url);
        klib_string_printf (page, "<td>\n");
        klib_string_printf (page, "%s<br/>\n", composername->str);
        klib_String *tracks_href = klib_string_new_empty ();
        klib_string_printf (tracks_href, "/gui_tracks?composer-is=%s", 
          composer_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_TRACKS"</a>", 
          tracks_href->str);
        klib_object_unref ((klib_Object *)tracks_href);
        klib_String *albums_href = klib_string_new_empty ();
        klib_string_printf (albums_href, "/gui_albums?composer-is=%s", 
          composer_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_ALBUMS"</a>", 
          albums_href->str);
        klib_object_unref ((klib_Object *)albums_href);
        klib_String *genres_href = klib_string_new_empty ();
        klib_string_printf (genres_href, "/gui_genres?composer-is=%s", 
          composer_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_GENRES"</a>", 
          genres_href->str);
        klib_object_unref ((klib_Object *)genres_href);
        klib_String *random_href = klib_string_new_empty ();
        klib_string_printf (random_href, 
          "javascript:add_composer_random('%s')", 
          composer_js->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_RANDOM"</a>", 
          random_href->str);
        klib_object_unref ((klib_Object *)random_href);
        klib_string_printf (page, "</td>\n");
        klib_object_unref ((klib_Object *) composer_url);
        klib_object_unref ((klib_Object *) composer_js);
        }
      else
        {
        klib_string_printf (page, "</tr><tr>\n");
        }
      if ((i + 1) % COMPOSERS_ACROSS == 0)
        {
        klib_string_append (page, "</tr><tr>\n");
        } 
      } 
    klib_string_append (page, "</tr></table>\n");
    }
  else
    klib_string_append (page, NO_COMPOSERS);

  klib_object_unref ((klib_Object *)list);
  klib_object_unref ((klib_Object *)db);

  klib_string_append (page, "<p/>\n");

  return page;
  }
klib_String* kmediad_gui_make_error_page ( const klib_Error error)
  {
  // TODO tart this up
  klib_String *page = klib_string_new_empty();
  klib_String *message = klib_error_full_message (error);
  klib_string_append (page, message->str);
  klib_object_unref ((klib_Object *) message); 
  return page;
  }
klib_String* kmediad_gui_make_genre_list ( kmediad_Gui self,
kmediad_SearchConstraints sc,
klib_Error **  error 
)
  {
  *error = NULL;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, error);
  if (*error)
    return NULL;

  klib_String *page = klib_string_new_empty();

  int count= 0;
  sc->limit = GENRES_PER_PAGE; // override request arg
  klib_List *list = kmediad_database_get_genres (db, sc, &count, error);
  if (*error)
      {
      klib_object_unref ((klib_Object *)db);
      return NULL;
      }

  klib_String *page_links = kmediad_gui_make_page_links (GENRES_PER_PAGE,
    count, sc->first, sc, "/gui_genres", "genres");
  klib_string_append (page, page_links->str);
  klib_object_unref ((klib_Object *)page_links);
  klib_string_append (page, "<p/>");

  int i, l = klib_list_length (list); 
  if (l > 0)
    {
    klib_string_append (page, "<table class=\"genrelisttable\"><tr>\n");
    int cells = GENRES_PER_PAGE;
    for (i = 0; i < cells; i++)
      {
      if (i < l)
        {
        const klib_String *genrename = (const klib_String *) 
          klib_list_get (list, i);
        klib_String *genre_url = klib_string_clone_safe (genrename);
        klib_String *genre_js = klib_string_clone_safe (genrename);
        klib_string_escape_dquote (genre_js);
        klib_string_printf (page, "<td>\n");
        klib_string_printf (page, "%s<br/>\n", genrename->str);
        klib_String *tracks_href = klib_string_new_empty ();
        klib_string_printf (tracks_href, "/gui_tracks?genre-is=%s", 
          genre_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_TRACKS"</a>", 
          tracks_href->str);
        klib_object_unref ((klib_Object *)tracks_href);
        klib_String *albums_href = klib_string_new_empty ();
        klib_string_printf (albums_href, "/gui_albums?genre-is=%s", 
          genre_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_ALBUMS"</a>", 
          albums_href->str);
        klib_object_unref ((klib_Object *)albums_href);
        klib_String *artists_href = klib_string_new_empty ();
        klib_string_printf (artists_href, "/gui_artists?genre-is=%s", 
          genre_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_ARTISTS"</a>", 
          artists_href->str);
        klib_object_unref ((klib_Object *)artists_href);
        klib_String *composers_href = klib_string_new_empty ();
        klib_string_printf (composers_href, "/gui_composers?genre-is=%s", 
          genre_url->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_COMPOSERS"</a>", 
          composers_href->str);
        klib_object_unref ((klib_Object *)composers_href);
        klib_String *random_href = klib_string_new_empty ();
        klib_string_printf (random_href, "javascript:add_genre_random('%s')", 
          genre_js->str);
        klib_string_printf (page, " <a href=\"%s\">"HTML_RANDOM"</a>", 
          random_href->str);
        klib_object_unref ((klib_Object *)random_href);
        klib_string_printf (page, "</td>\n");
        klib_object_unref ((klib_Object *)genre_url);
        klib_object_unref ((klib_Object *)genre_js);
        } 
      else
        {
        klib_string_printf (page, "<td></td>\n");
        }
       if ((i + 1) % GENRES_ACROSS == 0)
        {
        klib_string_append (page, "</tr><tr>\n");
        } 
      }
    klib_string_append (page, "</tr></table>\n");
    }
  else
    klib_string_append (page, NO_GENRES);

  klib_object_unref ((klib_Object *)list);
  klib_object_unref ((klib_Object *)db);

  klib_string_append (page, "<p/>\n");

  return page;
  }
klib_String* kmediad_gui_make_page_links ( int  items_per_page,
int  count,
int  this_first,
kmediad_SearchConstraints sc,
const char *  base_url,
const char *  text 
)
  {
  klib_String *page = klib_string_new_empty();
  if (count <= sc->limit) return page;

  int old_limit = sc->limit;
  int old_first = sc->first;

  int i, pages = count / items_per_page + 1;
  int current_page = this_first / items_per_page;

  klib_string_printf (page, "Showing page %d of %d, %s %d to %d of %d<p/>\n",
      current_page + 1, pages, text, this_first + 1, this_first + sc->limit, count);

  klib_string_printf (page, "<p class=\"pagelinkpara\">\n");

  int first_page_to_show = current_page - 10;
  int last_page_to_show = current_page + 10;
  if (first_page_to_show < 0) 
     {
     first_page_to_show = 0;
     last_page_to_show = 20;
     }
  if (last_page_to_show > pages) last_page_to_show = pages;

  sc->first = 0; 
  sc->limit = items_per_page; 

  klib_String *href = klib_string_new_empty();
  klib_String *argstr = kmedia_search_contraints_make_argstr (sc);
    klib_string_printf (href, "%s?%s",
       base_url, argstr->str);
  klib_String *s = klib_string_new_empty();
  klib_string_printf (s, 
         "<a href=\"%s\"><span class=\"pagelinkspan\">Start</span><a/> ",
         href->str, 0 + 1);
  klib_string_append (page, s->str);
  klib_object_unref ((klib_Object *)s);
  klib_object_unref ((klib_Object *)argstr);
     klib_object_unref ((klib_Object *)href);

  if (first_page_to_show != 0)
    {
    sc->first = (current_page - 1) * items_per_page; 
    sc->limit = items_per_page; 
    klib_String *href = klib_string_new_empty();
    klib_String *argstr = kmedia_search_contraints_make_argstr (sc);
      klib_string_printf (href, "%s?%s",
         base_url, argstr->str);
    klib_String *s = klib_string_new_empty();
    klib_string_printf (s, 
      "<a href=\"%s\"><span class=\"pagelinkspan\">&lt;</span><a/> ",
         href->str, 0 + 1);
    klib_string_append (page, s->str);
    klib_object_unref ((klib_Object *)s);
    klib_object_unref ((klib_Object *)argstr);
       klib_object_unref ((klib_Object *)href);
    }

  for (i = first_page_to_show; i < last_page_to_show; i++)
    {
    klib_String *href = klib_string_new_empty();
    sc->first = i * items_per_page;
    sc->limit = items_per_page; 
    klib_String *argstr = kmedia_search_contraints_make_argstr (sc);
    klib_string_printf (href, "%s?%s",
       base_url, argstr->str);
    klib_String *s = klib_string_new_empty();
    if (i * ALBUMS_PER_PAGE != this_first)
      klib_string_printf (s, 
         "<a href=\"%s\"><span class=\"pagelinkspan\">%d</span><a/> ",
         href->str, i + 1);
    else
      klib_string_printf (s, "<span class=\"pagelinkspan\">%d</span> ",
         i + 1);

    klib_string_append (page, s->str);
    klib_object_unref ((klib_Object *)s);
    klib_object_unref ((klib_Object *)argstr);
       klib_object_unref ((klib_Object *)href);
    }

  if (current_page < pages - 10)
    {
    sc->first = (current_page + 1) * items_per_page; 
    sc->limit = items_per_page; 
    klib_String *href = klib_string_new_empty();
    klib_String *argstr = kmedia_search_contraints_make_argstr (sc);
      klib_string_printf (href, "%s?%s",
         base_url, argstr->str);
    klib_String *s = klib_string_new_empty();
    klib_string_printf (s, 
      "<a href=\"%s\"><span class=\"pagelinkspan\">&gt;</span><a/> ",
         href->str, i + 1);
    klib_string_append (page, s->str);
    klib_object_unref ((klib_Object *)s);
    klib_object_unref ((klib_Object *)argstr);
       klib_object_unref ((klib_Object *)href);
    }

  klib_string_printf (page, "</p>\n");
  sc->first = old_first;
  sc->limit = old_limit;
  
  return page;
  }    
klib_String* kmediad_gui_make_track_list ( kmediad_Gui self,
kmediad_SearchConstraints sc,
klib_Error **  error 
)
  {
  *error = NULL;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, error);
  if (*error)
    return NULL;

  klib_String *page = klib_string_new_empty();

  int count= 0;
  sc->limit = TRACKS_PER_PAGE; // override request arg
  klib_List *list = kmediad_mediainfo_get_tracks (self->media_dir, 
    db, sc, &count, error);
  if (*error)
      {
      klib_object_unref ((klib_Object *)db);
      return NULL;
      }

  klib_String *page_links = kmediad_gui_make_page_links (ALBUMS_PER_PAGE,
    count, sc->first, sc, "/gui_tracks", "tracks");
  klib_string_append (page, page_links->str);
  klib_object_unref ((klib_Object *)page_links);
  klib_string_append (page, "<p/>");

  int i, l = klib_list_length (list); 
  if (l > 0)
    {
    klib_string_append (page, "<table class=\"tracklisttable\">\n");
    klib_string_append (page, "<tr><th></th><th>Title</th><th>Album</th><th>Track</th><th>Artist</th><th>Composer</th><th>Genre</th><th>Date</th></tr>");
    for (i = 0; i < l; i++)
      {
      const kmediad_MediaInfo *mi = (kmediad_MediaInfo *) 
         klib_list_get (list, i);
      klib_String *row = kmediad_gui_make_track_row (mi);
      klib_string_append (page, row->str);
      klib_object_unref ((klib_Object *)row);
      } 
    klib_string_append (page, "</table>\n");
    }
  else
    klib_string_append (page, NO_TRACKS);

  klib_object_unref ((klib_Object *)list);
  klib_object_unref ((klib_Object *)db);

  klib_string_append (page, "<p/>\n");

  return page;
  }
klib_String* kmediad_gui_make_track_row ( const kmediad_MediaInfo mi)
  {
  klib_String *path_js = klib_string_clone_safe (mi->path);
  klib_String *artist_url = klib_string_clone_safe (mi->artist);
  klib_String *album_url = klib_string_clone_safe (mi->album);
  klib_String *album_js = klib_string_clone_safe (mi->album);
  klib_String *composer_url = klib_string_clone_safe (mi->composer);
  klib_String *genre_url = klib_string_clone_safe (mi->genre);
  klib_string_escape_squote (path_js);
  klib_string_encode_url (artist_url);
  klib_string_encode_url (album_url);
  klib_string_encode_url (composer_url);
  klib_string_encode_url (genre_url);
  klib_string_escape_squote (album_js);
  klib_String *row = klib_string_new_empty();
  klib_string_printf (row, "<tr class=\"tracklistrow\">\n");

  klib_string_printf (row, "<td>");
  klib_String *play_href = klib_string_new_empty();
  klib_string_printf (play_href, "javascript:play_now('%s')", path_js->str);
  klib_string_printf (row, "<a href=\"%s\">"HTML_PLAY"</a>", play_href->str);
  klib_object_unref ((klib_Object *)play_href);
  klib_String *add_href = klib_string_new_empty();
  klib_string_printf (add_href, "javascript:add_to_playlist('%s')", 
    path_js->str);
  klib_string_printf (row, " <a href=\"%s\">"HTML_ADD"</a>", add_href->str);
  klib_object_unref ((klib_Object *)add_href);
  klib_String *info_href = klib_string_new_empty();
  klib_string_printf (info_href, "javascript:get_file_info('%s')", 
    path_js->str);
  klib_string_printf (row, " <a href=\"%s\">"HTML_INFO"</a>", info_href->str);
  klib_object_unref ((klib_Object *)info_href);
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");
  klib_string_printf (row, kmediad_gui_filename_or_title_from_mi 
    (mi));
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");
  if (!klib_string_is_null_or_empty (mi->artist))
    {
    klib_string_printf (row, mi->album->str);
    klib_string_append (row, "<br/n>");
    klib_String *tracks_href = klib_string_new_empty();
    klib_string_printf (tracks_href, "/gui_tracks?album-is=%s", 
      album_url->str);
    klib_string_printf (row, "<a href=\"%s\">"HTML_TRACKS"</a>", 
      tracks_href->str);
    klib_object_unref ((klib_Object *)tracks_href);

    klib_String *play_href = klib_string_new ("");
    klib_string_printf (play_href, "javascript:play_album('%s')",
      album_js->str);
    klib_string_printf (row, " <a href=\"%s\">"HTML_PLAY"</a>", play_href->str); 
    klib_object_unref ((klib_Object *)play_href);

    }
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");
  klib_string_printf (row, mi->track->str);
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");
  if (!klib_string_is_null_or_empty (mi->artist))
    {
    klib_string_printf (row, mi->artist->str);
    klib_string_printf (row, "<br/>");
    klib_String *tracks_href = klib_string_new_empty();
    klib_string_printf (tracks_href, "/gui_tracks?artist-is=%s", 
      artist_url->str);
    klib_string_printf (row, "<a href=\"%s\">"HTML_TRACKS"</a>", 
      tracks_href->str);
    klib_object_unref ((klib_Object *)tracks_href);
    klib_String *albums_href = klib_string_new_empty();
    klib_string_printf (albums_href, "/gui_albums?artist-is=%s", 
      artist_url->str);
    klib_string_printf (row, "&nbsp;<a href=\"%s\">"HTML_ALBUMS"</a>", 
      albums_href->str);
    klib_object_unref ((klib_Object *)albums_href);
    }
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");

  if (!klib_string_is_null_or_empty (mi->composer))
    {
    klib_string_printf (row, mi->composer->str);
    klib_string_printf (row, "<br/>");
    klib_String *tracks_href = klib_string_new_empty();
    klib_string_printf (tracks_href, "/gui_tracks?composer-is=%s", 
      composer_url->str);
    klib_string_printf (row, "<a href=\"%s\">"HTML_TRACKS"</a>", 
      tracks_href->str);
    klib_object_unref ((klib_Object *)tracks_href);
    klib_String *albums_href = klib_string_new_empty();
    klib_string_printf (albums_href, "/gui_albums?composer-is=%s", 
      composer_url->str);
    klib_string_printf (row, "&nbsp;<a href=\"%s\">"HTML_ALBUMS"</a>", 
      albums_href->str);
    klib_object_unref ((klib_Object *)albums_href);
    }
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");

  if (!klib_string_is_null_or_empty (mi->genre))
    {
    klib_string_printf (row, mi->genre->str);
    klib_string_printf (row, "<br/>");
    klib_String *albums_href = klib_string_new_empty();
    klib_string_printf (albums_href, "/gui_albums?genre-is=%s", 
      genre_url->str);
    klib_string_printf (row, "<a href=\"%s\">"HTML_ALBUMS"</a>", 
      albums_href->str);
    klib_object_unref ((klib_Object *)albums_href);
    klib_String *artists_href = klib_string_new_empty();
    klib_string_printf (artists_href, "/gui_artists?genre-is=%s", 
      genre_url->str);
    klib_string_printf (row, "&nbsp;<a href=\"%s\">"HTML_ARTISTS"</a>", 
      artists_href->str);
    klib_object_unref ((klib_Object *)artists_href);
    klib_String *composers_href = klib_string_new_empty();
    klib_string_printf (composers_href, "/gui_composers?genre-is=%s", 
      genre_url->str);
    klib_string_printf (row, "&nbsp;<a href=\"%s\">"HTML_COMPOSERS"</a>", 
      composers_href->str);
    klib_object_unref ((klib_Object *)composers_href);
    }

  klib_string_printf (row, "</td>");

  klib_string_printf (row, "<td>");
  klib_string_printf (row, mi->year->str);
  klib_string_printf (row, "</td>");

  klib_string_printf (row, "</tr>\n");
  klib_object_unref ((klib_Object *)path_js);
  klib_object_unref ((klib_Object *)artist_url);
  klib_object_unref ((klib_Object *)album_url);
  klib_object_unref ((klib_Object *)composer_url);
  klib_object_unref ((klib_Object *)genre_url);
  klib_object_unref ((klib_Object *)album_js);
  return row;
  }
kmediad_Gui* kmediad_gui_new ( XineInterface xine_interface,
kmediad_Server server 
)
{
  kmediad_Gui *self = (kmediad_Gui *) malloc (sizeof (kmediad_Gui)); 
  memset (self, 0, sizeof (kmediad_Gui));
  kmediad_gui_construct (self, "kmediad_Gui", xine_interface, server);
  return self;
}
klib_String* kmediad_gui_process_request ( kmediad_Gui self,
const char *  url,
const klib_AssocArray args 
)

Process a single request from an HTTP client whose URL was url.

Return a klib_String suitable to be used as the body of an HTTP response. Typically this will be an HTML page encoded in UTF-8 format

  {
  klib_log (KLIB_LOG_TRACE, "Process GUI request, URL %s", url); 
  klib_String *page_text = NULL;
  if (klib_string_is_null_or_empty ((klib_String *)self->media_dir))
    page_text = klib_string_new 
     ("Media directory has not been set"); 
  else if (strstr (url, "log") == url)
    page_text = kmediad_gui_process_request_log (self, 
      args);
  else if (strstr (url, "albums") == url)
    page_text = kmediad_gui_process_request_albums (self, 
      args);
  else if (strstr (url, "playlist") == url)
    page_text = kmediad_gui_process_request_playlist (self, 
      args);
  else if (strstr (url, "tracks") == url)
    page_text = kmediad_gui_process_request_tracks (self, 
      args);
  else if (strstr (url, "artists") == url)
    page_text = kmediad_gui_process_request_artists (self, 
      args);
  else if (strstr (url, "genres") == url)
    page_text = kmediad_gui_process_request_genres (self, 
      args);
  else if (strstr (url, "composers") == url)
    page_text = kmediad_gui_process_request_composers (self, 
      args);
  else if (strstr (url, "simple_search") == url)
    page_text = kmediad_gui_process_request_simple_search (self, 
      args);
  else if (strstr (url, "files") == url)
    page_text = kmediad_gui_process_request_files (self, 
      args);
   else
    page_text = kmediad_gui_process_request_home (self, 
      args);
  klib_String *s = klib_string_clone_safe (self->main_template);
  klib_string_replace_all (s, "%%CONTENT%%", page_text->str);
  klib_object_unref ((klib_Object *) page_text); 
  return s;
  }
klib_String* kmediad_gui_process_request_albums ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Albums</span><p/>");

   kmediad_SearchConstraints *sc = 
      kmediad_searchconstraints_new_from_args (args);

  klib_String *sctext = kmediad_searchconstraints_make_displayable (sc); 
  klib_string_printf (page, sctext->str);
  klib_object_unref ((klib_Object *)sctext);
  klib_string_printf (page, "<p/>");

  klib_Error *error = NULL;
  klib_String *embedded_page = kmediad_gui_make_album_list 
    (self, sc, &error); 

  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    embedded_page = kmediad_gui_make_error_page (error);
    klib_object_unref ((klib_Object *)error);
    };

  klib_string_append (page, embedded_page->str);
  klib_object_unref ((klib_Object *)embedded_page);

  return page;
  }
klib_String* kmediad_gui_process_request_artists ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Artists</span><p/>");

   kmediad_SearchConstraints *sc = 
      kmediad_searchconstraints_new_from_args (args);

  klib_String *sctext = kmediad_searchconstraints_make_displayable (sc); 
  klib_string_printf (page, sctext->str);
  klib_object_unref ((klib_Object *)sctext);
  klib_string_printf (page, "<p/>");

  klib_Error *error = NULL;
  klib_String *embedded_page = kmediad_gui_make_artist_list 
    (self, sc, &error); 

  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    embedded_page = kmediad_gui_make_error_page (error);
    klib_object_unref ((klib_Object *)error);
    };

  klib_string_append (page, embedded_page->str);
  klib_object_unref ((klib_Object *)embedded_page);

  return page;
  }
klib_String* kmediad_gui_process_request_composers ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Composers</span><p/>");

   kmediad_SearchConstraints *sc = 
      kmediad_searchconstraints_new_from_args (args);

  klib_String *sctext = kmediad_searchconstraints_make_displayable (sc); 
  klib_string_printf (page, sctext->str);
  klib_object_unref ((klib_Object *)sctext);
  klib_string_printf (page, "<p/>");

  klib_Error *error = NULL;
  klib_String *embedded_page = kmediad_gui_make_composer_list 
    (self, sc, &error); 

  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    embedded_page = kmediad_gui_make_error_page (error);
    klib_object_unref ((klib_Object *)error);
    };

  klib_string_append (page, embedded_page->str);
  klib_object_unref ((klib_Object *)embedded_page);

  return page;
  }
klib_String* kmediad_gui_process_request_files ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Files</span><p/>");
  klib_String *path = (klib_String*) klib_assocarray_get (args, "path");

  // Do uplinks -- still display even if no contents

  if (kmedia_utils_path_is_root (path))
    {
    klib_string_append (page, "<span id=\"foldertitlespan\">[top]</span>\n");
    }
  else
    {
    klib_string_append (page, 
      "<a href=gui_files?path=/>[top]</a><br/>\n");
    
    klib_String *pathcopy = klib_string_clone_safe (path);
    klib_String *uplink_path = klib_string_new ("");

    int j, depth = 1;
    char *tok = strtok (pathcopy->str, "/");
    do
      {
      klib_string_append (uplink_path, "/"); 
      klib_string_append (uplink_path, tok); 
      klib_String *uplink_path_encoded = klib_string_new (uplink_path->str);
      klib_string_encode_url (uplink_path_encoded);
      for (j = 0; j < depth; j++)
        klib_string_append (page, "&nbsp;&nbsp;&nbsp;");
      if (strcmp (uplink_path->str, path->str))
        {
        klib_string_printf (page, "<a href=\"gui_files?path=%s\">", 
          uplink_path_encoded->str);
        }
      else
        {
        klib_string_append (page, "<span id=\"foldertitlespan\">");
        }
      klib_string_printf (page, "%s\n", tok);
      klib_object_unref ((klib_Object *)uplink_path_encoded);
      if (strcmp (uplink_path->str, path->str))
        {
        klib_string_printf (page, "</a>\n");
        }
      else
        {
        klib_string_append (page, "</span>");
        }
      klib_string_append (page, "<br/>\n");
      tok = strtok (NULL, "/");
      depth++;
      } while (tok);

    klib_object_unref ((klib_Object *) uplink_path);
    klib_object_unref ((klib_Object *) pathcopy);
    }

  klib_string_append (page, "<p/>\n");

  // Now expand directory

  klib_Path *ospath = kmediad_utils_make_ospath_from_media_path 
    (self->media_dir, path);

  klib_List *files = klib_list_new();
  klib_List *dirs = klib_list_new();

  klib_Error *error = NULL;
  klib_path_list_directory (ospath, files, dirs, 0, &error);
  if (error)
    {
    klib_String *fullerror = klib_error_full_message (error);
    klib_string_append (page, fullerror->str);
    klib_object_unref ((klib_Object *) fullerror);
    klib_object_unref ((klib_Object *) error);
    }
  else
    {
    klib_list_sort (files, klib_list_sort_string_ascending);
    klib_list_sort (dirs, klib_list_sort_string_ascending);

    klib_list_filter (files, kmediad_utils_filter_playable, self);

    // Show files table
    
    if (klib_list_length (files) > 0)
      {
      klib_String *file_row_template = klib_string_new 
        ("<tr class=\"filelistrow\">"
           "<td>%%add%% %%play%% %%info%%</td><td>%%file%%</td>"
         "</tr>\n");

      klib_string_append (page, "<table class=\"filelisttable\">\n");

      int i, l = klib_list_length (files);
      for (i = 0; i < l; i++)
        {
        klib_String *file = (klib_String *)klib_list_get (files, i);
        klib_String *temp = klib_string_clone (file_row_template);
        klib_String *fullfile = kmediad_utils_make_full_path (path, file); 
        klib_String *fullfile_url = klib_string_clone (fullfile);
        klib_String *fullfile_js = klib_string_clone (fullfile);
        klib_string_encode_url (fullfile_url);
        klib_string_escape_squote (fullfile_js);
        klib_String *add_href = klib_string_new_empty();
        klib_string_printf (add_href, 
          "<a href=\"javascript:add_to_playlist('%s')\">%s</a>", 
          fullfile_js->str, HTML_ADD);
        klib_String *play_href = klib_string_new_empty();
        klib_string_printf (play_href, 
          "<a href=\"javascript:play_now('%s')\">%s</a>", 
          fullfile_js->str, HTML_PLAY);
        klib_String *info_href = klib_string_new_empty();
        klib_string_printf (info_href, 
          "<a href=\"javascript:get_file_info('%s')\">%s</a>", 
          fullfile_js->str, HTML_INFO);
        klib_string_replace_all (temp, "%%add%%", add_href->str);
        klib_string_replace_all (temp, "%%play%%", play_href->str);
        klib_string_replace_all (temp, "%%info%%", info_href->str);
        klib_string_replace_all (temp, "%%file%%", file->str);
        klib_string_append (page, temp->str);
        klib_object_unref ((klib_Object *) add_href); 
        klib_object_unref ((klib_Object *) play_href); 
        klib_object_unref ((klib_Object *) info_href); 
        klib_object_unref ((klib_Object *) temp); 
        klib_object_unref ((klib_Object *) fullfile); 
        klib_object_unref ((klib_Object *) fullfile_url); 
        klib_object_unref ((klib_Object *) fullfile_js); 
        }

      klib_object_unref ((klib_Object *) file_row_template); 

      klib_string_append (page, "</table>\n");
      }

    klib_string_append (page, "<p/>\n");
 
    // Show dirs table
    
    if (klib_list_length (dirs) > 0)
      {
      klib_String *dir_row_template = klib_string_new 
        ("<tr class=\"filelistrow\">"
           "<td>%%add%% %%addall%%</td><td>%%dir%%</td>"
         "</tr>\n");

      klib_string_append (page, "<table class=\"filelisttable\">\n");

      int i, l = klib_list_length (dirs);
      for (i = 0; i < l; i++)
        {
        klib_String *dir = (klib_String *)klib_list_get (dirs, i);
        klib_String *temp = klib_string_clone (dir_row_template);
        klib_String *fulldir = kmediad_utils_make_full_path (path, dir); 
        klib_String *fulldir_url = klib_string_clone (fulldir);
        klib_String *fulldir_js = klib_string_clone (fulldir);
        klib_string_encode_url (fulldir_url);
        klib_string_escape_squote (fulldir_js);
        klib_String *dir_href = klib_string_new_empty();
        klib_string_printf (dir_href, "<a href=\"/gui_files?path=%s\">%s</a>", 
          fulldir_url->str, dir->str);
        klib_String *add_href = klib_string_new_empty();
        klib_string_printf (add_href, 
          "<a href=\"javascript:add_to_playlist('%s')\">%s</a>", 
          fulldir_js->str, HTML_ADD);
        klib_String *addall_href = klib_string_new_empty();
        klib_string_printf (addall_href, 
          "<a href=\"javascript:add_to_playlist_recurse('%s')\">%s</a>", 
          fulldir_js->str, HTML_ADDALL);
        klib_string_replace_all (temp, "%%add%%", add_href->str);
        klib_string_replace_all (temp, "%%addall%%", addall_href->str);
        klib_string_replace_all (temp, "%%dir%%", dir_href->str);
        klib_string_append (page, temp->str);
        klib_object_unref ((klib_Object *) dir_href); 
        klib_object_unref ((klib_Object *) add_href); 
        klib_object_unref ((klib_Object *) addall_href); 
        klib_object_unref ((klib_Object *) temp); 
        klib_object_unref ((klib_Object *) fulldir); 
        klib_object_unref ((klib_Object *) fulldir_url); 
        klib_object_unref ((klib_Object *) fulldir_js); 
        }

      klib_object_unref ((klib_Object *) dir_row_template); 

      klib_string_append (page, "</table>\n");
      }
    }

  klib_object_unref ((klib_Object *) files); 
  klib_object_unref ((klib_Object *) dirs); 
  klib_object_unref ((klib_Object *) ospath); 

  return page;
  }
klib_String* kmediad_gui_process_request_genres ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Genres</span><p/>");

   kmediad_SearchConstraints *sc = 
      kmediad_searchconstraints_new_from_args (args);

  klib_String *sctext = kmediad_searchconstraints_make_displayable (sc); 
  klib_string_printf (page, sctext->str);
  klib_object_unref ((klib_Object *)sctext);
  klib_string_printf (page, "<p/>");

  klib_Error *error = NULL;
  klib_String *embedded_page = kmediad_gui_make_genre_list 
    (self, sc, &error); 

  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    embedded_page = kmediad_gui_make_error_page (error);
    klib_object_unref ((klib_Object *)error);
    };

  klib_string_append (page, embedded_page->str);
  klib_object_unref ((klib_Object *)embedded_page);

  return page;
  }
klib_String* kmediad_gui_process_request_home ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new_empty();

  // Find out if the collection is indexed, so we don't fill the 
  //  home page with error messages if it isn't
  klib_Error *error = NULL;
  BOOL is_indexed = TRUE;
  kmediad_Database *db = kmediad_database_new_default (self->media_dir, 
     FALSE, FALSE, &error);
  if (error)
    {
    klib_object_unref ((klib_Object *)error); 
    is_indexed = FALSE;
    }

  if (!is_indexed)
    klib_string_printf 
      (page, "Media directory is set, but the collection does not appear "
              "to be indexed. Most features will not be available. To "
              "begin indexing the collection, "
              " <a href=\"javascript:rebuild_index()\">click here</a>.<p/>\n");
    

  klib_string_printf 
    (page, "<a href=\"/gui_files?path=/\">Browse files</a> on disk\n");
  klib_string_append (page, "<p/>\n");

  if (is_indexed)
    {
    klib_string_printf (page, 
      "<table class=\"hometable\"><tr><td class=\"hometablecellleft\">\n");

    klib_string_printf 
      (page, "<span class=\"listsubcaptionspan\">Artists</span><p/>");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=a\">A</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=b\">B</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=c\">C</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=d\">D</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=e\">E</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=f\">F</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=g\">G</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=h\">H</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=i\">I</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=j\">J</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=k\">K</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=l\">L</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=m\">M</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=n\">N</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=o\">O</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=p\">P</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=q\">Q</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=r\">R</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=s\">S</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=t\">T</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=u\">U</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=v\">V</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=w\">W</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=x\">X</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=y\">Y</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_artists?artist-begins=z\">Z</a>\n");

    klib_string_append (page, "<p/>\n");

    klib_string_printf 
      (page, "<span class=\"listsubcaptionspan\">Albums</span><p/>");

    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=a\">A</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=b\">B</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=c\">C</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=d\">D</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=e\">E</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=f\">F</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=g\">G</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=h\">H</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=i\">I</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=j\">J</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=k\">K</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=l\">L</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=m\">M</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=n\">N</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=o\">O</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=p\">P</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=q\">Q</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=r\">R</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=s\">S</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=t\">T</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=u\">U</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=v\">V</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=w\">W</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=x\">X</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=y\">Y</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_albums?album-begins=z\">Z</a>\n");

    klib_string_append (page, "<p/>\n");

    klib_string_printf 
      (page, "<span class=\"listsubcaptionspan\">Composers</span><p/>");

    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=a\">A</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=b\">B</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=c\">C</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=d\">D</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=e\">E</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=f\">F</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=g\">G</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=h\">H</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=i\">I</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=j\">J</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=k\">K</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=l\">L</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=m\">M</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=n\">N</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=o\">O</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=p\">P</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=q\">Q</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=r\">R</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=s\">S</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=t\">T</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=u\">U</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=v\">V</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=w\">W</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=x\">X</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=y\">Y</a> |\n");
    klib_string_printf 
      (page, "<a href=\"/gui_composers?composer-begins=z\">Z</a>\n");

    klib_string_append (page, "</td>\n");
    klib_string_append (page, "<td class=\"hometablecellright\">\n");

    error = NULL;
    klib_List *albums = kmediad_database_get_random_album_selection 
      (db, 1, &error); 
    if (error)
      {
      klib_log_error (error);
      klib_object_unref ((klib_Object *)error); 
      }
    else
      {
      klib_string_printf 
        (page, "<span class=\"listsubcaptionspan\">Random album</span><p/>");

      int i, l = klib_list_length (albums);
      // One one in loop at present
      for (i = 0; i < l; i++)
        {
        klib_String *albumname = (klib_String *) klib_list_get (albums, i);
        //klib_string_printf (page, "%s<br/>\n", albumname->str);

        kmediad_Album *album = kmediad_album_new (albumname);
        kmediad_album_populate_from_db (self->media_dir, album, db);
        klib_String *albumcell = kmediad_gui_make_album_cell
            (self, album);
        //klib_string_append (page, "<td class=\"albumcellcell\">\n");
        klib_string_append (page, albumcell->str);
        //klib_string_append (page, "</td>\n");
        klib_object_unref ((klib_Object *)albumcell);
        klib_object_unref ((klib_Object *)album);
        }
      if (albums) klib_object_unref ((klib_Object *)albums);
      }

    klib_string_append (page, "</td>\n");

    klib_string_append (page, 
      "</tr></table>\n");
    }

  if (db) klib_object_unref ((klib_Object *)db); 
  return page;
  }
klib_String* kmediad_gui_process_request_log ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Log</span><p/>");

  klib_string_append (page, "<table class=\"loglisttable\">\n");

  klib_Array *log = self->server->log_array;
  int i, l = klib_array_length (log);
  for (i = 0; i < l; i++)
    {
    klib_String *s = (klib_String *)klib_array_get (log, i);
    if (s)
      {
      klib_string_append (page, "<tr class=\"loglistrow\"><td>");
      klib_string_append (page, s->str);
      klib_string_append (page, "</td></tr>\n");
      }
    }
  
  klib_string_append (page, "</table>\n");

  return page;
  }
klib_String* kmediad_gui_process_request_playlist ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Playlist</span><p/>");

  int i, l = xine_interface_playlist_length (self->xine_interface);
  if (l == 0)
    {
    klib_string_append (page, "Playlist is empty");
    }
  else
    {
    klib_string_append (page, "<table class=\"playlistlisttable\">\n");
    for (i = 0; i < l; i++)
      {
      const char *file = xine_interface_playlist_item 
        (self->xine_interface, i);
      if (file)
        {
        klib_String *sfile = kmediad_utils_sanitise_path_for_client
          (self->media_dir, file);
        klib_String *ssfile = kmediad_gui_display_name
          (self, sfile);
        klib_String *file_row_template = klib_string_new 
          ("<tr class=\"playlistrow\">"
             "<td>"
                "<a href=\"%%playhref%%\">"HTML_PLAY"</a> "
                "<a href=\"%%infohref%%\">"HTML_INFO"</a>"
              "</td>"
               "<td>%%item%%</td>"
               "<td>%%file%%</td>"
           "</tr>\n");
   
        klib_String *path_js = klib_string_clone (sfile);
        klib_string_escape_squote (path_js);

        klib_String *play_href = klib_string_new_empty ();
        klib_string_printf (play_href, "javascript:play_index(%d)", i);
        klib_String *info_href = klib_string_new_empty ();
        klib_string_printf (info_href, "javascript:get_file_info('%s')", 
          path_js->str);

        klib_String *ssitem = klib_string_new_empty();
        klib_string_printf (ssitem, "%d", i+1);
        klib_string_replace_all (file_row_template, "%%item%%", ssitem->str);
        klib_string_replace_all (file_row_template, "%%file%%", ssfile->str);
        klib_string_replace_all (file_row_template, "%%playhref%%", 
          play_href->str);
        klib_string_replace_all (file_row_template, "%%infohref%%", 
          info_href->str);
  
        klib_string_append (page, file_row_template->str); 
        klib_object_unref ((klib_Object *)ssfile); 
        klib_object_unref ((klib_Object *)ssitem); 
        klib_object_unref ((klib_Object *)sfile); 
        klib_object_unref ((klib_Object *)play_href); 
        klib_object_unref ((klib_Object *)info_href); 
        klib_object_unref ((klib_Object *)path_js); 
        klib_object_unref ((klib_Object *)file_row_template); 
        }
      }
  
    klib_string_append (page, "</table>\n");
    }

  return page;
  }
klib_String* kmediad_gui_process_request_simple_search ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new_empty();

  const klib_String *search = (klib_String *) klib_assocarray_get 
      (args, "search");
  if (klib_string_is_null_or_empty (search))
    {
    klib_string_append (page, "No search term entered");
    return page;
    }

  klib_string_printf (page, 
    "<span class=\"listcaptionspan\">Search results for '%s'</span><p/>", search->str);

  klib_string_append (page, 
    "<span class=\"listsubcaptionspan\">Title matches</span><p/>");

  klib_Error *error = NULL;
  kmediad_SearchConstraints *sc = kmedia_searchconstraints_new_from_arg 
    ("title-contains", search->str); 
  klib_String *track_page = kmediad_gui_make_track_list
   (self, sc, &error); 
  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    klib_String *error_page = kmediad_gui_make_error_page (error);
    klib_string_append (page, error_page->str);
    klib_object_unref ((klib_Object *)error);
    klib_object_unref ((klib_Object *)error_page);
    }
  else
    {
    klib_string_append (page, track_page->str);
    klib_object_unref ((klib_Object *)track_page);
    }

  
  klib_string_append (page, 
    "<span class=\"listsubcaptionspan\">Album matches</span><p/>");

  error = NULL;
  sc = kmedia_searchconstraints_new_from_arg 
    ("album-contains", search->str); 
  klib_String *album_page = kmediad_gui_make_album_list
   (self, sc, &error); 
  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    klib_String *error_page = kmediad_gui_make_error_page (error);
    klib_string_append (page, error_page->str);
    klib_object_unref ((klib_Object *)error);
    klib_object_unref ((klib_Object *)error_page);
    }
  else
    {
    klib_string_append (page, album_page->str);
    klib_object_unref ((klib_Object *)album_page);
    }

  
  klib_string_append (page, 
    "<span class=\"listsubcaptionspan\">Artist matches</span><p/>");

  error = NULL;
  sc = kmedia_searchconstraints_new_from_arg 
    ("artist-contains", search->str); 
  klib_String *artist_page = kmediad_gui_make_artist_list
   (self, sc, &error); 
  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    klib_String *error_page = kmediad_gui_make_error_page (error);
    klib_string_append (page, error_page->str);
    klib_object_unref ((klib_Object *)error);
    klib_object_unref ((klib_Object *)error_page);
    }
  else
    {
    klib_string_append (page, artist_page->str);
    klib_object_unref ((klib_Object *)artist_page);
    }
  
  klib_string_append (page, 
    "<span class=\"listsubcaptionspan\">Composer matches</span><p/>");

  error = NULL;
  sc = kmedia_searchconstraints_new_from_arg 
    ("composer-contains", search->str); 
  klib_String *composer_page = kmediad_gui_make_composer_list
   (self, sc, &error); 
  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    klib_String *error_page = kmediad_gui_make_error_page (error);
    klib_string_append (page, error_page->str);
    klib_object_unref ((klib_Object *)error);
    klib_object_unref ((klib_Object *)error_page);
    }
  else
    {
    klib_string_append (page, composer_page->str);
    klib_object_unref ((klib_Object *)composer_page);
    }

  
  return page;
  }
klib_String* kmediad_gui_process_request_tracks ( kmediad_Gui self,
const klib_AssocArray args 
)
  {
  klib_String *page = klib_string_new 
    ("<span class=\"listcaptionspan\">Tracks</span><p/>");

   kmediad_SearchConstraints *sc = 
      kmediad_searchconstraints_new_from_args (args);

  klib_String *sctext = kmediad_searchconstraints_make_displayable (sc); 
  klib_string_printf (page, sctext->str);
  klib_object_unref ((klib_Object *)sctext);
  klib_string_printf (page, "<p/>");

  klib_Error *error = NULL;
  klib_String *embedded_page = kmediad_gui_make_track_list 
    (self, sc, &error); 

  klib_object_unref ((klib_Object *)sc);

  if (error)
    {
    embedded_page = kmediad_gui_make_error_page (error);
    klib_object_unref ((klib_Object *)error);
    };

  klib_string_append (page, embedded_page->str);
  klib_object_unref ((klib_Object *)embedded_page);

  return page;
  }
void kmediad_gui_set_media_dir ( kmediad_Gui self,
klib_Path media_dir 
)

Sets the current media directory.

Most operations will fail if this method has not been called

  {
  if (self->media_dir) 
    klib_object_unref ((klib_Object *)self->media_dir);
  self->media_dir = media_dir;
  klib_object_add_ref ((klib_Object *)self->media_dir); 
  }