/* XMMS2 - X Music Multiplexer System * Copyright (C) 2003-2007 XMMS2 Team * * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ #include "xmms/xmms_plugin.h" #include "xmms/xmms_transport.h" #include "xmms/xmms_log.h" #include "xmms/xmms_plsplugins.h" #include "xmms/xmms_medialib.h" #include #include #include #include #include /* * Function prototypes */ static gboolean xmms_html_read_playlist (xmms_transport_t *transport, guint playlist_id); static GString *xmms_html_write_playlist (guint32 *list); static gchar *escape_html (const gchar *in); static gboolean valid_suffix (gchar **suffix, gchar *path); static gchar* parse_tag (const gchar *tag, const gchar *plspath); static gchar* build_url (const gchar *plspath, const gchar *file); static gchar* path_get_body (const gchar *path); static gchar html_header[] = "\n" "\n" "\n" "\n" " XMMS2 Playlist\n" " \n" " \n" " \n" " \n" "\n" "\n" "

XMMS2 Playlist

\n" "\n" "
\n" "

\n" " Number of tracks: %i\n" "
\n" " Total playtime: %i:%02i:%02i\n" "

\n" "\n" "

Tracks

\n" "\n" "
    \n"; static gchar html_entry_even[] = "\t\t\t
  1. %s
  2. \n"; static gchar html_entry_odd[] = "\t\t\t
  3. %s
  4. \n"; static gchar html_footer[] = "
\n" " \n" "\n" "\n"; /* * Plugin header */ xmms_plugin_t * xmms_plugin_get (void) { xmms_plugin_t *plugin; plugin = xmms_plugin_new (XMMS_PLUGIN_TYPE_PLAYLIST, XMMS_PLAYLIST_PLUGIN_API_VERSION, "html", "HTML Playlist", XMMS_VERSION, "HTML Playlist reader"); if (!plugin) { return NULL; } xmms_plugin_info_add (plugin, "URL", "http://www.xmms.org/"); xmms_plugin_info_add (plugin, "Author", "XMMS Team"); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_READ_PLAYLIST, xmms_html_read_playlist); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_WRITE_PLAYLIST, xmms_html_write_playlist); xmms_plugin_config_property_register (plugin, "suffixes", "mp3,ogg,flac,wav,spx,sid", NULL, NULL); xmms_plugin_magic_add (plugin, "html w/ doctype", "text/html", "0 string 0) { break; } g_free (buffer); return FALSE; } readlen += ret; g_assert (readlen >= 0); } tags = g_strsplit (buffer, "<", 0); g_free (buffer); val = xmms_config_lookup ("playlist.html.suffixes"); suffix = g_strsplit (xmms_config_property_get_string (val), ",", 0); plsurl = xmms_transport_url_get (transport); session = xmms_medialib_begin_write (); for (cnt = 0; tags[cnt] != NULL; cnt++) { gchar *url, *full; xmms_medialib_entry_t entry; url = parse_tag (tags[cnt], plsurl); if (!url) { continue; } if (!valid_suffix (suffix, url)) { g_free (url); continue; } full = build_url (plsurl, url); entry = xmms_medialib_entry_new (session, full, &error); if (entry) xmms_medialib_playlist_add (session, playlist_id, entry); g_free (url); g_free (full); } g_strfreev (suffix); g_strfreev (tags); xmms_medialib_end (session); return TRUE; } static GString * xmms_html_write_playlist (guint32 *list) { GString *ret; xmms_medialib_session_t *session; gboolean is_even = TRUE; guint num_entries = 0, total_len = 0; guint i; g_return_val_if_fail (list, FALSE); session = xmms_medialib_begin (); /* get the playlists total playtime */ while (list[num_entries]) { xmms_medialib_entry_t entry = list[num_entries]; total_len += xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION); num_entries++; } ret = g_string_new (NULL); g_string_append_printf (ret, html_header, num_entries, total_len / 3600000, (total_len / 60000) % 60, (total_len / 1000) % 60); i = 0; while (list[i]) { gchar buf[256], *artist, *title, *url; xmms_medialib_entry_t entry; guint len; entry = list[i]; artist = escape_html (xmms_medialib_entry_property_get_str (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_ARTIST)); title = escape_html (xmms_medialib_entry_property_get_str (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_TITLE)); len = xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION); url = escape_html (xmms_medialib_entry_property_get_str (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_URL)); if (!artist && !title) { g_snprintf (buf, sizeof (buf), "%s (%02i:%02i)", url, len / 60000, (len / 1000) % 60); } else { g_snprintf (buf, sizeof (buf), "%s - %s (%02i:%02i)", artist ? artist : "Unknown artist", title ? title : "Unknown title", len / 60000, (len / 1000) % 60); } if (artist) g_free (artist); if (title) g_free (title); if (url) g_free (url); g_string_append_printf (ret, is_even ? html_entry_even : html_entry_odd, buf); is_even = !is_even; i++; } xmms_medialib_end (session); g_string_append (ret, html_footer); return ret; } static gchar * escape_html (const gchar *in) { gchar *ret = NULL, *retptr; const gchar *inptr; gsize len = 0; gboolean need_escape = FALSE; if (!in) return NULL; /* check whether we need to escape this string at all, * and if we do, get the required length of the new buffer. */ for (inptr = in; *inptr; inptr++) { switch (*inptr) { case '"': need_escape = TRUE; len += 6; /* " */ break; case '&': need_escape = TRUE; len += 5; /* & */ break; case '>': case '<': need_escape = TRUE; len += 4; /* > resp < */ break; default: len++; } } if (!need_escape) return g_strdup (in); len++; /* terminating NUL */ retptr = ret = g_malloc (len); *ret = '\0'; for (inptr = in; *inptr; inptr++) { gint n; switch (*inptr) { case '"': n = g_snprintf (retptr, len, """); break; case '&': n = g_snprintf (retptr, len, "&"); break; case '>': n = g_snprintf (retptr, len, ">"); break; case '<': n = g_snprintf (retptr, len, "<"); break; default: n = g_snprintf (retptr, len, "%c", *inptr); } len -= n; retptr += n; } return ret; } static gboolean valid_suffix (gchar **suffix, gchar *path) { guint current; g_return_val_if_fail (suffix, FALSE); g_return_val_if_fail (path, FALSE); for (current = 0; suffix[current] != NULL; current++) { if (g_str_has_suffix (path, g_strstrip (suffix[current]))) return TRUE; } return FALSE; } static gchar * build_url (const gchar *plspath, const gchar *file) { gchar *url; gchar *path; g_return_val_if_fail (plspath, NULL); g_return_val_if_fail (file, NULL); if (strstr (file, "://") != NULL) { return g_strdup (file); } if (file[0] == '/') { path = path_get_body (plspath); url = g_build_filename (path, file, NULL); g_free (path); return url; } path = g_path_get_dirname (plspath); url = g_build_filename (path, file, NULL); g_free (path); return url; } /* * proto://domain/dir/file -> proto://domain * domain/dir/file -> domain **/ static gchar * path_get_body (const gchar *path) { gchar *beg, *end; g_return_val_if_fail (path, NULL); beg = strstr (path, "://"); if (!beg) { return g_strndup (path, strcspn (path, "/")); } beg += 3; end = strchr (beg, '/'); if (!end) { return g_strdup (path); } return g_strndup (path, end - path); } static gchar * parse_tag (const gchar *tag, const gchar *plspath) { size_t urlend; gchar *href, *url; g_return_val_if_fail (tag, NULL); g_return_val_if_fail (plspath, NULL); if (g_ascii_strncasecmp (tag, "a ", 2) != 0) { return NULL; } href = strstr (tag, "href=\""); if (!href) { href = strstr (tag, "HREF=\""); } if (!href) { return NULL; } urlend = strcspn (href + 6, "\""); url = g_strndup (href + 6, urlend); return url; }