📄 local.c
字号:
/* * Copyright (c) 2002, 2003, 2004 Jean-Yves Lefort <jylefort@brutele.be> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include "config.h"#include <stdio.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#include <glib/gi18n.h>#ifdef WITH_LOCAL_METADATA#include <tag_c.h>#endif#include "streamtuner.h"/*** cpp *********************************************************************/#define POINTER_TO_STRING(ptr) ((ptr) ? (ptr) : "")/*** type definitions ********************************************************/typedef struct{ STStream stream; char *pathname; /* full pathname, in the fs locale */ char *filename; /* filename only, converted to UTF-8 */ char *title; char *artist; char *album; char *year; char *genre; char *comment; char *duration; int bitrate; int samplerate; int channels;} LocalStream;enum { FIELD_PATHNAME, FIELD_FILENAME, FIELD_TITLE, FIELD_ARTIST, FIELD_ALBUM, FIELD_YEAR, FIELD_GENRE, FIELD_COMMENT, FIELD_DURATION, FIELD_BITRATE, FIELD_SAMPLERATE, FIELD_CHANNELS, FIELD_AUDIO /* meta field (bitrate, samplerate and channels) */};/*** variables ***************************************************************/static STPlugin *local_plugin = NULL;static STHandler *local_handler = NULL;/*** function declarations ***************************************************/static LocalStream *stream_new_cb (gpointer data);static void stream_field_get_cb (LocalStream *stream, STHandlerField *field, GValue *value, gpointer data);static void stream_field_set_cb (LocalStream *stream, STHandlerField *field, const GValue *value, gpointer data);static void stream_stock_field_get_cb (LocalStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data);static gboolean stream_modify_cb (LocalStream *stream, GSList *fields, GSList *values, gpointer data, GError **err);static gboolean stream_delete_cb (LocalStream *stream, gpointer data, GError **err);static void stream_free_cb (LocalStream *stream, gpointer data);static gboolean stream_rename (LocalStream *stream, const GValue *new_filename, GError **err);static gboolean stream_tune_in_multiple_cb (GSList *streams, gpointer data, GError **err);static gboolean stream_browse_cb (LocalStream *stream, gpointer data, GError **err);static gboolean reload_cb (STCategory *category, GNode **categories, GList **streams, gpointer data, GError **err);static gboolean reload_categories (const char *music_dir, GNode *root, GError **err);static gboolean reload_streams (const char *music_dir, STCategory *category, GList **streams, GError **err);#ifdef WITH_LOCAL_METADATAstatic void metadata_read (LocalStream *stream);static gboolean metadata_write (LocalStream *stream, GSList *fields, GSList *values, GError **err);#endif /* WITH_LOCAL_METADATA */static void init_handler (void);static gboolean check_api_version (GError **err);/*** implementation **********************************************************/static LocalStream *stream_new_cb (gpointer data){ return g_new0(LocalStream, 1);}static voidstream_field_get_cb (LocalStream *stream, STHandlerField *field, GValue *value, gpointer data){ switch (field->id) { case FIELD_PATHNAME: g_value_set_string(value, stream->pathname); break; case FIELD_FILENAME: g_value_set_string(value, stream->filename); break; case FIELD_TITLE: g_value_set_string(value, stream->title); break; case FIELD_ARTIST: g_value_set_string(value, stream->artist); break; case FIELD_ALBUM: g_value_set_string(value, stream->album); break; case FIELD_YEAR: g_value_set_string(value, stream->year); break; case FIELD_GENRE: g_value_set_string(value, stream->genre); break; case FIELD_COMMENT: g_value_set_string(value, stream->comment); break; case FIELD_DURATION: g_value_set_string(value, stream->duration); break; case FIELD_BITRATE: g_value_set_int(value, stream->bitrate); break; case FIELD_SAMPLERATE: g_value_set_int(value, stream->samplerate); break; case FIELD_CHANNELS: g_value_set_int(value, stream->channels); break; case FIELD_AUDIO: g_value_take_string(value, st_format_audio_properties(stream->bitrate, stream->samplerate, stream->channels)); break; default: g_assert_not_reached(); }}static voidstream_field_set_cb (LocalStream *stream, STHandlerField *field, const GValue *value, gpointer data){ switch (field->id) { case FIELD_PATHNAME: stream->pathname = g_value_dup_string(value); break; case FIELD_FILENAME: stream->filename = g_value_dup_string(value); break; case FIELD_TITLE: stream->title = g_value_dup_string(value); break; case FIELD_ARTIST: stream->artist = g_value_dup_string(value); break; case FIELD_ALBUM: stream->album = g_value_dup_string(value); break; case FIELD_YEAR: stream->year = g_value_dup_string(value); break; case FIELD_GENRE: stream->genre = g_value_dup_string(value); break; case FIELD_COMMENT: stream->comment = g_value_dup_string(value); break; case FIELD_DURATION: stream->duration = g_value_dup_string(value); break; case FIELD_BITRATE: stream->bitrate = g_value_get_int(value); break; case FIELD_SAMPLERATE: stream->samplerate = g_value_get_int(value); break; case FIELD_CHANNELS: stream->channels = g_value_get_int(value); break; default: g_assert_not_reached(); }}static voidstream_stock_field_get_cb (LocalStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data){ switch (stock_field) { case ST_HANDLER_STOCK_FIELD_NAME: { char *name; if (stream->artist && stream->title) name = g_strdup_printf("%s - %s", stream->artist, stream->title); else if (stream->title) name = g_strdup(stream->title); else name = g_strdup(stream->filename); g_value_set_string(value, name); g_free(name); break; } case ST_HANDLER_STOCK_FIELD_GENRE: g_value_set_string(value, stream->genre); break; case ST_HANDLER_STOCK_FIELD_DESCRIPTION: g_value_set_string(value, stream->comment); break; case ST_HANDLER_STOCK_FIELD_HOMEPAGE: /* nop */ break; case ST_HANDLER_STOCK_FIELD_URI_LIST: { char *uri; GError *err = NULL; uri = g_filename_to_uri(stream->pathname, NULL, &err); if (uri) { GValueArray *value_array; GValue uri_value = { 0, }; value_array = g_value_array_new(1); g_value_init(&uri_value, G_TYPE_STRING); g_value_take_string(&uri_value, uri); g_value_array_append(value_array, &uri_value); g_value_unset(&uri_value); g_value_take_boxed(value, value_array); } else { st_handler_notice(local_handler, _("%s: unable to convert filename to URI: %s"), stream->pathname, err->message); g_error_free(err); } break; } }}static gbooleanstream_modify_cb (LocalStream *stream, GSList *fields, GSList *values, gpointer data, GError **err){ GSList *f; GSList *v; gboolean modify_file = FALSE; for (f = fields, v = values; f && v; f = f->next, v = v->next) { STHandlerField *field = f->data; const GValue *value = v->data; switch (field->id) { case FIELD_FILENAME: if (! stream_rename(stream, value, err)) return FALSE; break; case FIELD_TITLE: case FIELD_ARTIST: case FIELD_ALBUM: case FIELD_YEAR: case FIELD_GENRE: case FIELD_COMMENT: modify_file = TRUE; break; default: g_assert_not_reached(); } } if (modify_file) {#ifdef WITH_LOCAL_METADATA if (! metadata_write(stream, fields, values, err)) return FALSE;#else g_set_error(err, 0, 0, _("metadata support is disabled")); return FALSE;#endif /* WITH_LOCAL_METADATA */ } return TRUE;}static gbooleanstream_delete_cb (LocalStream *stream, gpointer data, GError **err){ if (unlink(stream->pathname) < 0) { g_set_error(err, 0, 0, "%s", g_strerror(errno)); return FALSE; } else return TRUE;}static voidstream_free_cb (LocalStream *stream, gpointer data){ g_free(stream->pathname); g_free(stream->filename); g_free(stream->title); g_free(stream->artist); g_free(stream->album); g_free(stream->year); g_free(stream->genre); g_free(stream->comment); g_free(stream->duration); st_stream_free((STStream *) stream);}static gbooleanstream_rename (LocalStream *stream, const GValue *new_filename, GError **err){ GError *tmp_err = NULL; char *filename; char *directory; char *new_pathname; g_return_val_if_fail(stream != NULL, FALSE); g_return_val_if_fail(G_IS_VALUE(new_filename), FALSE); filename = g_filename_from_utf8(g_value_get_string(new_filename), -1, NULL, NULL, &tmp_err); if (! filename) { g_set_error(err, 0, 0, _("unable to convert filename from UTF-8 encoding: %s"), tmp_err->message); g_error_free(tmp_err); return FALSE; } directory = g_path_get_dirname(stream->pathname); new_pathname = g_build_filename(directory, filename, NULL); g_free(directory); if (g_file_test(new_pathname, G_FILE_TEST_EXISTS)) { g_set_error(err, 0, 0, _("target file already exists")); g_free(filename); g_free(new_pathname); return FALSE; } if (rename(stream->pathname, new_pathname) < 0) { g_set_error(err, 0, 0, "%s", g_strerror(errno)); g_free(filename); g_free(new_pathname); return FALSE; } /* success */ stream->pathname = new_pathname; stream->filename = g_value_dup_string(new_filename); return TRUE;}static gbooleanstream_tune_in_multiple_cb (GSList *streams, gpointer data, GError **err){ char *m3uname; GSList *filenames = NULL; GSList *l; gboolean status; /* create a list of filenames from STREAMS */ for (l = streams; l; l = l->next) { LocalStream *stream = l->data; filenames = g_slist_append(filenames, stream->pathname); } /* write the .m3u */ m3uname = st_m3u_mktemp("streamtuner.local.XXXXXX", filenames, err); g_slist_free(filenames); if (! m3uname) return FALSE; /* open the .m3u */ status = st_action_run("play-m3u", m3uname, err); g_free(m3uname); return status;}static gbooleanstream_browse_cb (LocalStream *stream, gpointer data, GError **err){ char *url; char *s; gboolean status; if (stream->album) url = g_strconcat("http://www.allmusic.com/cg/amg.dll?p=amg&opt1=2&sql=", stream->album, NULL); else if (stream->title) url = g_strconcat("http://www.allmusic.com/cg/amg.dll?p=amg&opt1=3&sql=", stream->title, NULL); else if (stream->artist) url = g_strconcat("http://www.allmusic.com/cg/amg.dll?p=amg&opt1=1&sql=", stream->artist, NULL); else { g_set_error(err, 0, 0, _("file has no album, title or artist information")); return FALSE; } /* allmusic.com needs this */ for (s = url; *s; s++) if (*s == ' ') *s = '|'; status = st_action_run("view-web", url, err); g_free(url); return status;}static gbooleanreload_cb (STCategory *category,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -