aboutsummaryrefslogtreecommitdiffstats
path: root/src/playlist
diff options
context:
space:
mode:
Diffstat (limited to 'src/playlist')
-rw-r--r--src/playlist/AsxPlaylistPlugin.cxx38
-rw-r--r--src/playlist/CuePlaylistPlugin.cxx5
-rw-r--r--src/playlist/DespotifyPlaylistPlugin.cxx12
-rw-r--r--src/playlist/EmbeddedCuePlaylistPlugin.cxx8
-rw-r--r--src/playlist/ExtM3uPlaylistPlugin.cxx14
-rw-r--r--src/playlist/PlsPlaylistPlugin.cxx59
-rw-r--r--src/playlist/RssPlaylistPlugin.cxx38
-rw-r--r--src/playlist/SoundCloudPlaylistPlugin.cxx153
-rw-r--r--src/playlist/XspfPlaylistPlugin.cxx34
9 files changed, 158 insertions, 203 deletions
diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx
index 94198b8c3..ccd98a711 100644
--- a/src/playlist/AsxPlaylistPlugin.cxx
+++ b/src/playlist/AsxPlaylistPlugin.cxx
@@ -23,7 +23,7 @@
#include "MemorySongEnumerator.hxx"
#include "InputStream.hxx"
#include "Song.hxx"
-#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@@ -31,7 +31,6 @@
#include <glib.h>
-#include <assert.h>
#include <string.h>
static constexpr Domain asx_domain("asx");
@@ -58,7 +57,7 @@ struct AsxParser {
* valid if state==ENTRY. TAG_NUM_OF_ITEM_TYPES means there
* is no (known) tag.
*/
- TagType tag;
+ TagType tag_type;
/**
* The current song. It is allocated after the "location"
@@ -66,6 +65,8 @@ struct AsxParser {
*/
Song *song;
+ TagBuilder tag_builder;
+
AsxParser()
:state(ROOT) {}
@@ -96,7 +97,7 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
if (StringEqualsCaseASCII(element_name, "entry")) {
parser->state = AsxParser::ENTRY;
parser->song = Song::NewRemote("asx:");
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
break;
@@ -107,27 +108,22 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
attribute_values,
"href");
if (href != nullptr) {
- /* create new song object, and copy
- the existing tag over; we cannot
+ /* create new song object; we cannot
replace the existing song's URI,
because that attribute is
immutable */
Song *song = Song::NewRemote(href);
-
- if (parser->song != nullptr) {
- song->tag = parser->song->tag;
- parser->song->tag = nullptr;
+ if (parser->song != nullptr)
parser->song->Free();
- }
parser->song = song;
}
} else if (StringEqualsCaseASCII(element_name, "author"))
/* is that correct? or should it be COMPOSER
or PERFORMER? */
- parser->tag = TAG_ARTIST;
+ parser->tag_type = TAG_ARTIST;
else if (StringEqualsCaseASCII(element_name, "title"))
- parser->tag = TAG_TITLE;
+ parser->tag_type = TAG_TITLE;
break;
}
@@ -146,14 +142,16 @@ asx_end_element(gcc_unused GMarkupParseContext *context,
case AsxParser::ENTRY:
if (StringEqualsCaseASCII(element_name, "entry")) {
- if (strcmp(parser->song->uri, "asx:") != 0)
+ if (strcmp(parser->song->uri, "asx:") != 0) {
+ assert(parser->song->tag == nullptr);
+ parser->song->tag = parser->tag_builder.CommitNew();
parser->songs.emplace_front(parser->song);
- else
+ } else
parser->song->Free();
parser->state = AsxParser::ROOT;
} else
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
break;
}
@@ -171,11 +169,9 @@ asx_text(gcc_unused GMarkupParseContext *context,
break;
case AsxParser::ENTRY:
- if (parser->tag != TAG_NUM_OF_ITEM_TYPES) {
- if (parser->song->tag == nullptr)
- parser->song->tag = new Tag();
- parser->song->tag->AddItem(parser->tag,
- text, text_len);
+ if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) {
+ parser->tag_builder.AddItem(parser->tag_type,
+ text, text_len);
}
break;
diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/CuePlaylistPlugin.cxx
index 42a43bbad..00aa758b0 100644
--- a/src/playlist/CuePlaylistPlugin.cxx
+++ b/src/playlist/CuePlaylistPlugin.cxx
@@ -21,13 +21,10 @@
#include "CuePlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "SongEnumerator.hxx"
-#include "tag/Tag.hxx"
-#include "Song.hxx"
#include "cue/CueParser.hxx"
#include "TextInputStream.hxx"
-#include <assert.h>
-#include <string.h>
+#include <string>
class CuePlaylist final : public SongEnumerator {
InputStream &is;
diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx
index a1a865c08..67400e20a 100644
--- a/src/playlist/DespotifyPlaylistPlugin.cxx
+++ b/src/playlist/DespotifyPlaylistPlugin.cxx
@@ -34,7 +34,7 @@ extern "C" {
#include <stdlib.h>
static void
-add_song(std::forward_list<SongPointer> &songs, struct ds_track *track)
+add_song(std::forward_list<SongPointer> &songs, ds_track &track)
{
const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
Song *song;
@@ -45,15 +45,15 @@ add_song(std::forward_list<SongPointer> &songs, struct ds_track *track)
snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
ds_uri = uri + strlen(dsp_scheme) + 3;
- if (despotify_track_to_uri(track, ds_uri) != ds_uri) {
+ if (despotify_track_to_uri(&track, ds_uri) != ds_uri) {
/* Should never really fail, but let's be sure */
FormatDebug(despotify_domain,
- "Can't add track %s", track->title);
+ "Can't add track %s", track.title);
return;
}
song = Song::NewRemote(uri);
- song->tag = mpd_despotify_tag_from_track(track);
+ song->tag = new Tag(mpd_despotify_tag_from_track(track));
songs.emplace_front(song);
}
@@ -67,7 +67,7 @@ parse_track(struct despotify_session *session,
if (track == nullptr)
return false;
- add_song(songs, track);
+ add_song(songs, *track);
return true;
}
@@ -82,7 +82,7 @@ parse_playlist(struct despotify_session *session,
for (ds_track *track = playlist->tracks; track != nullptr;
track = track->next)
- add_song(songs, track);
+ add_song(songs, *track);
return true;
}
diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
index d758650eb..9dfecbf46 100644
--- a/src/playlist/EmbeddedCuePlaylistPlugin.cxx
+++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
@@ -27,7 +27,6 @@
#include "EmbeddedCuePlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "SongEnumerator.hxx"
-#include "tag/Tag.hxx"
#include "tag/TagHandler.hxx"
#include "tag/TagId3.hxx"
#include "tag/ApeTag.hxx"
@@ -38,7 +37,6 @@
#include "fs/AllocatedPath.hxx"
#include "util/ASCII.hxx"
-#include <assert.h>
#include <string.h>
class EmbeddedCuePlaylist final : public SongEnumerator {
@@ -95,7 +93,7 @@ embcue_playlist_open_uri(const char *uri,
gcc_unused Mutex &mutex,
gcc_unused Cond &cond)
{
- if (!PathTraits::IsAbsoluteUTF8(uri))
+ if (!PathTraitsUTF8::IsAbsolute(uri))
/* only local files supported */
return nullptr;
@@ -105,7 +103,7 @@ embcue_playlist_open_uri(const char *uri,
const auto playlist = new EmbeddedCuePlaylist();
- tag_file_scan(path_fs, &embcue_tag_handler, playlist);
+ tag_file_scan(path_fs, embcue_tag_handler, playlist);
if (playlist->cuesheet.empty()) {
tag_ape_scan2(path_fs, &embcue_tag_handler, playlist);
if (playlist->cuesheet.empty())
@@ -118,7 +116,7 @@ embcue_playlist_open_uri(const char *uri,
return nullptr;
}
- playlist->filename = PathTraits::GetBaseUTF8(uri);
+ playlist->filename = PathTraitsUTF8::GetBase(uri);
playlist->next = &playlist->cuesheet[0];
playlist->parser = new CueParser();
diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx
index 8d260fec7..4f0c111ad 100644
--- a/src/playlist/ExtM3uPlaylistPlugin.cxx
+++ b/src/playlist/ExtM3uPlaylistPlugin.cxx
@@ -23,11 +23,10 @@
#include "SongEnumerator.hxx"
#include "Song.hxx"
#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx"
#include "TextInputStream.hxx"
-#include <glib.h>
-
#include <string.h>
#include <stdlib.h>
@@ -74,7 +73,6 @@ extm3u_parse_tag(const char *line)
long duration;
char *endptr;
const char *name;
- Tag *tag;
duration = strtol(line, &endptr, 10);
if (endptr[0] != ',')
@@ -91,16 +89,16 @@ extm3u_parse_tag(const char *line)
object */
return NULL;
- tag = new Tag();
- tag->time = duration;
+ TagBuilder tag;
+ tag.SetTime(duration);
/* unfortunately, there is no real specification for the
EXTM3U format, so we must assume that the string after the
comma is opaque, and is just the song name*/
if (*name != 0)
- tag->AddItem(TAG_NAME, name);
+ tag.AddItem(TAG_NAME, name);
- return tag;
+ return tag.CommitNew();
}
Song *
@@ -119,7 +117,7 @@ ExtM3uPlaylist::NextSong()
line_s = line.c_str();
- if (g_str_has_prefix(line_s, "#EXTINF:")) {
+ if (StringStartsWith(line_s, "#EXTINF:")) {
delete tag;
tag = extm3u_parse_tag(line_s + 8);
continue;
diff --git a/src/playlist/PlsPlaylistPlugin.cxx b/src/playlist/PlsPlaylistPlugin.cxx
index 99be3ad35..3fd420d89 100644
--- a/src/playlist/PlsPlaylistPlugin.cxx
+++ b/src/playlist/PlsPlaylistPlugin.cxx
@@ -23,7 +23,7 @@
#include "MemorySongEnumerator.hxx"
#include "InputStream.hxx"
#include "Song.hxx"
-#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
@@ -32,13 +32,14 @@
#include <string>
+#include <stdio.h>
+
static constexpr Domain pls_domain("pls");
static void
pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
{
gchar *value;
- int length;
GError *error = nullptr;
int num_entries = g_key_file_get_integer(keyfile, "playlist",
"NumberOfEntries", &error);
@@ -57,9 +58,7 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
}
}
- while (num_entries > 0) {
- Song *song;
-
+ for (; num_entries > 0; --num_entries) {
char key[64];
sprintf(key, "File%u", num_entries);
value = g_key_file_get_string(keyfile, "playlist", key,
@@ -71,36 +70,27 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
return;
}
- song = Song::NewRemote(value);
+ Song *song = Song::NewRemote(value);
g_free(value);
+ TagBuilder tag;
+
sprintf(key, "Title%u", num_entries);
value = g_key_file_get_string(keyfile, "playlist", key,
- &error);
- if(error == nullptr && value){
- if (song->tag == nullptr)
- song->tag = new Tag();
- song->tag->AddItem(TAG_TITLE, value);
- }
- /* Ignore errors? Most likely value not present */
- if(error) g_error_free(error);
- error = nullptr;
+ nullptr);
+ if (value != nullptr)
+ tag.AddItem(TAG_TITLE, value);
+
g_free(value);
sprintf(key, "Length%u", num_entries);
- length = g_key_file_get_integer(keyfile, "playlist", key,
- &error);
- if(error == nullptr && length > 0){
- if (song->tag == nullptr)
- song->tag = new Tag();
- song->tag->time = length;
- }
- /* Ignore errors? Most likely value not present */
- if(error) g_error_free(error);
- error = nullptr;
+ int length = g_key_file_get_integer(keyfile, "playlist", key,
+ nullptr);
+ if (length > 0)
+ tag.SetTime(length);
+ song->tag = tag.CommitNew();
songs.emplace_front(song);
- num_entries--;
}
}
@@ -110,15 +100,12 @@ pls_open_stream(InputStream &is)
{
GError *error = nullptr;
Error error2;
- size_t nbytes;
- char buffer[1024];
- bool success;
- GKeyFile *keyfile;
std::string kf_data;
do {
- nbytes = is.LockRead(buffer, sizeof(buffer), error2);
+ char buffer[1024];
+ size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2);
if (nbytes == 0) {
if (error2.IsDefined()) {
LogError(error2);
@@ -137,12 +124,10 @@ pls_open_stream(InputStream &is)
return nullptr;
}
- keyfile = g_key_file_new();
- success = g_key_file_load_from_data(keyfile,
- kf_data.data(), kf_data.length(),
- G_KEY_FILE_NONE, &error);
-
- if (!success) {
+ GKeyFile *keyfile = g_key_file_new();
+ if (!g_key_file_load_from_data(keyfile,
+ kf_data.data(), kf_data.length(),
+ G_KEY_FILE_NONE, &error)) {
FormatError(pls_domain,
"KeyFile parser failed: %s", error->message);
g_error_free(error);
diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx
index e2a44bfd3..c4dd2f8c3 100644
--- a/src/playlist/RssPlaylistPlugin.cxx
+++ b/src/playlist/RssPlaylistPlugin.cxx
@@ -23,7 +23,7 @@
#include "MemorySongEnumerator.hxx"
#include "InputStream.hxx"
#include "Song.hxx"
-#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@@ -31,7 +31,6 @@
#include <glib.h>
-#include <assert.h>
#include <string.h>
static constexpr Domain rss_domain("rss");
@@ -58,7 +57,7 @@ struct RssParser {
* valid if state==ITEM. TAG_NUM_OF_ITEM_TYPES means there
* is no (known) tag.
*/
- TagType tag;
+ TagType tag_type;
/**
* The current song. It is allocated after the "location"
@@ -66,6 +65,8 @@ struct RssParser {
*/
Song *song;
+ TagBuilder tag_builder;
+
RssParser()
:state(ROOT) {}
};
@@ -95,7 +96,7 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
if (StringEqualsCaseASCII(element_name, "item")) {
parser->state = RssParser::ITEM;
parser->song = Song::NewRemote("rss:");
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
break;
@@ -106,25 +107,21 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
attribute_values,
"url");
if (href != nullptr) {
- /* create new song object, and copy
- the existing tag over; we cannot
+ /* create new song object; we cannot
replace the existing song's URI,
because that attribute is
immutable */
Song *song = Song::NewRemote(href);
- if (parser->song != nullptr) {
- song->tag = parser->song->tag;
- parser->song->tag = nullptr;
+ if (parser->song != nullptr)
parser->song->Free();
- }
parser->song = song;
}
} else if (StringEqualsCaseASCII(element_name, "title"))
- parser->tag = TAG_TITLE;
+ parser->tag_type = TAG_TITLE;
else if (StringEqualsCaseASCII(element_name, "itunes:author"))
- parser->tag = TAG_ARTIST;
+ parser->tag_type = TAG_ARTIST;
break;
}
@@ -143,14 +140,16 @@ rss_end_element(gcc_unused GMarkupParseContext *context,
case RssParser::ITEM:
if (StringEqualsCaseASCII(element_name, "item")) {
- if (strcmp(parser->song->uri, "rss:") != 0)
+ if (strcmp(parser->song->uri, "rss:") != 0) {
+ assert(parser->song->tag == nullptr);
+ parser->song->tag = parser->tag_builder.CommitNew();
parser->songs.emplace_front(parser->song);
- else
+ } else
parser->song->Free();
parser->state = RssParser::ROOT;
} else
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
break;
}
@@ -168,12 +167,9 @@ rss_text(gcc_unused GMarkupParseContext *context,
break;
case RssParser::ITEM:
- if (parser->tag != TAG_NUM_OF_ITEM_TYPES) {
- if (parser->song->tag == nullptr)
- parser->song->tag = new Tag();
- parser->song->tag->AddItem(parser->tag,
- text, text_len);
- }
+ if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
+ parser->tag_builder.AddItem(parser->tag_type,
+ text, text_len);
break;
}
diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx
index f6797b14d..cfbd0e8b5 100644
--- a/src/playlist/SoundCloudPlaylistPlugin.cxx
+++ b/src/playlist/SoundCloudPlaylistPlugin.cxx
@@ -24,7 +24,8 @@
#include "ConfigData.hxx"
#include "InputStream.hxx"
#include "Song.hxx"
-#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
+#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
@@ -45,7 +46,8 @@ static constexpr Domain soundcloud_domain("soundcloud");
static bool
soundcloud_init(const config_param &param)
{
- soundcloud_config.apikey = param.GetBlockValue("apikey", "");
+ // APIKEY for MPD application, registered under DarkFox' account.
+ soundcloud_config.apikey = param.GetBlockValue("apikey", "a25e51780f7f86af0afa91f241d091f8");
if (soundcloud_config.apikey.empty()) {
LogDebug(soundcloud_domain,
"disabling the soundcloud playlist plugin "
@@ -62,19 +64,20 @@ soundcloud_init(const config_param &param)
* @return Constructed URL. Must be freed with g_free.
*/
static char *
-soundcloud_resolve(const char* uri) {
+soundcloud_resolve(const char* uri)
+{
char *u, *ru;
- if (g_str_has_prefix(uri, "http://")) {
+ if (StringStartsWith(uri, "https://")) {
u = g_strdup(uri);
- } else if (g_str_has_prefix(uri, "soundcloud.com")) {
- u = g_strconcat("http://", uri, nullptr);
+ } else if (StringStartsWith(uri, "soundcloud.com")) {
+ u = g_strconcat("https://", uri, nullptr);
} else {
/* assume it's just a path on soundcloud.com */
- u = g_strconcat("http://soundcloud.com/", uri, nullptr);
+ u = g_strconcat("https://soundcloud.com/", uri, nullptr);
}
- ru = g_strconcat("http://api.soundcloud.com/resolve.json?url=",
+ ru = g_strconcat("https://api.soundcloud.com/resolve.json?url=",
u, "&client_id=",
soundcloud_config.apikey.c_str(), nullptr);
g_free(u);
@@ -108,12 +111,13 @@ struct parse_data {
std::forward_list<SongPointer> songs;
};
-static int handle_integer(void *ctx,
- long
+static int
+handle_integer(void *ctx,
+ long
#ifndef HAVE_YAJL1
- long
+ long
#endif
- intval)
+ intval)
{
struct parse_data *data = (struct parse_data *) ctx;
@@ -128,26 +132,25 @@ static int handle_integer(void *ctx,
return 1;
}
-static int handle_string(void *ctx, const unsigned char* stringval,
+static int
+handle_string(void *ctx, const unsigned char* stringval,
#ifdef HAVE_YAJL1
- unsigned int
+ unsigned int
#else
- size_t
+ size_t
#endif
- stringlen)
+ stringlen)
{
struct parse_data *data = (struct parse_data *) ctx;
const char *s = (const char *) stringval;
switch (data->key) {
case Title:
- if (data->title != nullptr)
- g_free(data->title);
+ g_free(data->title);
data->title = g_strndup(s, stringlen);
break;
case Stream_URL:
- if (data->stream_url != nullptr)
- g_free(data->stream_url);
+ g_free(data->stream_url);
data->stream_url = g_strndup(s, stringlen);
data->got_url = 1;
break;
@@ -158,13 +161,14 @@ static int handle_string(void *ctx, const unsigned char* stringval,
return 1;
}
-static int handle_mapkey(void *ctx, const unsigned char* stringval,
+static int
+handle_mapkey(void *ctx, const unsigned char* stringval,
#ifdef HAVE_YAJL1
- unsigned int
+ unsigned int
#else
- size_t
+ size_t
#endif
- stringlen)
+ stringlen)
{
struct parse_data *data = (struct parse_data *) ctx;
@@ -181,7 +185,8 @@ static int handle_mapkey(void *ctx, const unsigned char* stringval,
return 1;
}
-static int handle_start_map(void *ctx)
+static int
+handle_start_map(void *ctx)
{
struct parse_data *data = (struct parse_data *) ctx;
@@ -191,7 +196,8 @@ static int handle_start_map(void *ctx)
return 1;
}
-static int handle_end_map(void *ctx)
+static int
+handle_end_map(void *ctx)
{
struct parse_data *data = (struct parse_data *) ctx;
@@ -206,19 +212,16 @@ static int handle_end_map(void *ctx)
/* got_url == 1, track finished, make it into a song */
data->got_url = 0;
- Song *s;
- char *u;
-
- u = g_strconcat(data->stream_url, "?client_id=",
- soundcloud_config.apikey.c_str(), nullptr);
- s = Song::NewRemote(u);
+ char *u = g_strconcat(data->stream_url, "?client_id=",
+ soundcloud_config.apikey.c_str(), nullptr);
+ Song *s = Song::NewRemote(u);
g_free(u);
- Tag *t = new Tag();
- t->time = data->duration / 1000;
+ TagBuilder tag;
+ tag.SetTime(data->duration / 1000);
if (data->title != nullptr)
- t->AddItem(TAG_NAME, data->title);
- s->tag = t;
+ tag.AddItem(TAG_NAME, data->title);
+ s->tag = tag.CommitNew();
data->songs.emplace_front(s);
@@ -249,12 +252,9 @@ static int
soundcloud_parse_json(const char *url, yajl_handle hand,
Mutex &mutex, Cond &cond)
{
- char buffer[4096];
- unsigned char *ubuffer = (unsigned char *)buffer;
-
Error error;
- InputStream *input_stream = InputStream::Open(url, mutex, cond,
- error);
+ InputStream *input_stream = InputStream::OpenReady(url, mutex, cond,
+ error);
if (input_stream == nullptr) {
if (error.IsDefined())
LogError(error);
@@ -262,12 +262,13 @@ soundcloud_parse_json(const char *url, yajl_handle hand,
}
mutex.lock();
- input_stream->WaitReady();
yajl_status stat;
int done = 0;
while (!done) {
+ char buffer[4096];
+ unsigned char *ubuffer = (unsigned char *)buffer;
const size_t nbytes =
input_stream->Read(buffer, sizeof(buffer), error);
if (nbytes == 0) {
@@ -318,80 +319,62 @@ soundcloud_parse_json(const char *url, yajl_handle hand,
* soundcloud://playlist/<playlist-id>
* soundcloud://url/<url or path of soundcloud page>
*/
-
static SongEnumerator *
soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
{
- char *s, *p;
- char *scheme, *arg, *rest;
- s = g_strdup(uri);
- scheme = s;
- for (p = s; *p; p++) {
- if (*p == ':' && *(p+1) == '/' && *(p+2) == '/') {
- *p = 0;
- p += 3;
- break;
- }
- }
- arg = p;
- for (; *p; p++) {
- if (*p == '/') {
- *p = 0;
- p++;
- break;
- }
- }
- rest = p;
-
- if (strcmp(scheme, "soundcloud") != 0) {
- FormatWarning(soundcloud_domain,
- "incompatible scheme for soundcloud plugin: %s",
- scheme);
- g_free(s);
- return nullptr;
- }
+ assert(memcmp(uri, "soundcloud://", 13) == 0);
+ uri += 13;
char *u = nullptr;
- if (strcmp(arg, "track") == 0) {
- u = g_strconcat("http://api.soundcloud.com/tracks/",
+ if (memcmp(uri, "track/", 6) == 0) {
+ const char *rest = uri + 6;
+ u = g_strconcat("https://api.soundcloud.com/tracks/",
rest, ".json?client_id=",
soundcloud_config.apikey.c_str(), nullptr);
- } else if (strcmp(arg, "playlist") == 0) {
- u = g_strconcat("http://api.soundcloud.com/playlists/",
+ } else if (memcmp(uri, "playlist/", 9) == 0) {
+ const char *rest = uri + 9;
+ u = g_strconcat("https://api.soundcloud.com/playlists/",
rest, ".json?client_id=",
soundcloud_config.apikey.c_str(), nullptr);
- } else if (strcmp(arg, "url") == 0) {
+ } else if (memcmp(uri, "user/", 5) == 0) {
+ const char *rest = uri + 5;
+ u = g_strconcat("https://api.soundcloud.com/users/",
+ rest, "/tracks.json?client_id=",
+ soundcloud_config.apikey.c_str(), nullptr);
+ } else if (memcmp(uri, "search/", 7) == 0) {
+ const char *rest = uri + 7;
+ u = g_strconcat("https://api.soundcloud.com/tracks.json?q=",
+ rest, "&client_id=",
+ soundcloud_config.apikey.c_str(), nullptr);
+ } else if (memcmp(uri, "url/", 4) == 0) {
+ const char *rest = uri + 4;
/* Translate to soundcloud resolver call. libcurl will automatically
follow the redirect to the right resource. */
u = soundcloud_resolve(rest);
}
- g_free(s);
if (u == nullptr) {
LogWarning(soundcloud_domain, "unknown soundcloud URI");
return nullptr;
}
- yajl_handle hand;
struct parse_data data;
-
data.got_url = 0;
data.title = nullptr;
data.stream_url = nullptr;
#ifdef HAVE_YAJL1
- hand = yajl_alloc(&parse_callbacks, nullptr, nullptr, (void *) &data);
+ yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, nullptr,
+ &data);
#else
- hand = yajl_alloc(&parse_callbacks, nullptr, (void *) &data);
+ yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data);
#endif
int ret = soundcloud_parse_json(u, hand, mutex, cond);
g_free(u);
yajl_free(hand);
- if (data.title != nullptr)
- g_free(data.title);
- if (data.stream_url != nullptr)
- g_free(data.stream_url);
+ g_free(data.title);
+ g_free(data.stream_url);
if (ret == -1)
return nullptr;
diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx
index dcfab5a80..08fe49191 100644
--- a/src/playlist/XspfPlaylistPlugin.cxx
+++ b/src/playlist/XspfPlaylistPlugin.cxx
@@ -22,14 +22,13 @@
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
#include "InputStream.hxx"
-#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include <glib.h>
-#include <assert.h>
#include <string.h>
static constexpr Domain xspf_domain("xspf");
@@ -57,7 +56,7 @@ struct XspfParser {
* valid if state==TRACK. TAG_NUM_OF_ITEM_TYPES means there
* is no (known) tag.
*/
- TagType tag;
+ TagType tag_type;
/**
* The current song. It is allocated after the "location"
@@ -65,6 +64,8 @@ struct XspfParser {
*/
Song *song;
+ TagBuilder tag_builder;
+
XspfParser()
:state(ROOT) {}
};
@@ -95,7 +96,7 @@ xspf_start_element(gcc_unused GMarkupParseContext *context,
if (strcmp(element_name, "track") == 0) {
parser->state = XspfParser::TRACK;
parser->song = nullptr;
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
break;
@@ -104,17 +105,17 @@ xspf_start_element(gcc_unused GMarkupParseContext *context,
if (strcmp(element_name, "location") == 0)
parser->state = XspfParser::LOCATION;
else if (strcmp(element_name, "title") == 0)
- parser->tag = TAG_TITLE;
+ parser->tag_type = TAG_TITLE;
else if (strcmp(element_name, "creator") == 0)
/* TAG_COMPOSER would be more correct
according to the XSPF spec */
- parser->tag = TAG_ARTIST;
+ parser->tag_type = TAG_ARTIST;
else if (strcmp(element_name, "annotation") == 0)
- parser->tag = TAG_COMMENT;
+ parser->tag_type = TAG_COMMENT;
else if (strcmp(element_name, "album") == 0)
- parser->tag = TAG_ALBUM;
+ parser->tag_type = TAG_ALBUM;
else if (strcmp(element_name, "trackNum") == 0)
- parser->tag = TAG_TRACK;
+ parser->tag_type = TAG_TRACK;
break;
@@ -148,12 +149,15 @@ xspf_end_element(gcc_unused GMarkupParseContext *context,
case XspfParser::TRACK:
if (strcmp(element_name, "track") == 0) {
- if (parser->song != nullptr)
+ if (parser->song != nullptr) {
+ assert(parser->song->tag == nullptr);
+ parser->song->tag = parser->tag_builder.CommitNew();
parser->songs.emplace_front(parser->song);
+ }
parser->state = XspfParser::TRACKLIST;
} else
- parser->tag = TAG_NUM_OF_ITEM_TYPES;
+ parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
break;
@@ -178,11 +182,9 @@ xspf_text(gcc_unused GMarkupParseContext *context,
case XspfParser::TRACK:
if (parser->song != nullptr &&
- parser->tag != TAG_NUM_OF_ITEM_TYPES) {
- if (parser->song->tag == nullptr)
- parser->song->tag = new Tag();
- parser->song->tag->AddItem(parser->tag, text, text_len);
- }
+ parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
+ parser->tag_builder.AddItem(parser->tag_type,
+ text, text_len);
break;