/* Echo Media Player
* Copyright (C) 2008 Shane O'Connell
*
* [ The original file includes a copyright header in this location describing
* the file as being released under the terms of the GNU General Public
* License. It has been removed in order to display the file as part of the
* archive. ]
*/
#include "PlaySource.h"
#include <mpegfile.h>
#include <tag.h>
using namespace std;
int ModPlaySource::insert_from_uris(int index, const list<string>& uris)
{
int tracks_added = 0;
for (list<string>::const_iterator iter = uris.begin(); iter != uris.end(); iter++)
tracks_added += insert_from_uri(index, *iter);
return tracks_added;
}
int ModPlaySource::insert_from_uri(int index, const string& uri, bool playlists_ok, bool directories_ok)
{
// TODO set PlaySource to be readonly before this call and unset it after?
// (this may or may not be necessary.. need to think about it)
while (Glib::MainContext::get_default()->pending())
Glib::MainContext::get_default()->iteration(false);
int tracks_added = 0;
try
{
Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("standard::type,standard::fast-content-type");
if (directories_ok && file_info->get_file_type() == Gio::FILE_TYPE_DIRECTORY)
{
Glib::RefPtr<Gio::FileEnumerator> children = file->enumerate_children("standard::display-name");
Glib::RefPtr<Gio::FileInfo> child;
list<Glib::ustring> child_names;
while (child = children->next_file())
child_names.push_back(child->get_display_name());
child_names.sort(caseless_sort);
for (list<Glib::ustring>::iterator iter = child_names.begin(); iter != child_names.end(); iter++)
{
if (iter->size() >= 1 && iter->at(0) != '.')
{ // TODO add some kind of protection against infinitely recursive directories
int tracks_added_from_child = insert_from_uri(index, file->get_child_for_display_name(*iter)->get_uri(), false, true);
tracks_added += tracks_added_from_child;
index += tracks_added_from_child;
}
}
}
else
{
string content_type = file_info->get_attribute_string("standard::fast-content-type");
if (playlists_ok && content_type == "audio/x-mpegurl") { // M3U playlist
tracks_added += insert_m3u_playlist(index, file);
}
else if (file->is_native() && content_type == "audio/mpeg") // MP3
{
// TODO trim whitespace at either side of the mp3 title/artist/album etc.
TagLib::MPEG::File file_tags(file->get_path().c_str());
TrackRef track = TrackRef(new Track(
uri,
file_tags.tag()->title().to8Bit(true),
file_tags.tag()->artist().to8Bit(true),
file_tags.tag()->album().to8Bit(true),
file_tags.tag()->track()));
// TODO make sure tags are okay before accepting
// if they aren't okay use the filename as the title
insert_track(index, track);
tracks_added++;
}
}
}
catch (Gio::Error e)
{
// TODO show error message to user when this happens
return tracks_added;
}
return tracks_added;
}
int ModPlaySource::insert_m3u_playlist(int index, Glib::RefPtr<Gio::File> file)
{
int tracks_added = 0;
char buf[512];
int buf_filled = 0;
int offset = 0;
string line = "";
string ext_line = "";
Glib::RefPtr<Gio::FileInputStream> stream = file->read();
buf_filled = stream->read(buf, 512);
if (buf_filled < 0)
throw Gio::Error(Gio::Error::FAILED, "read() returned -1");
if (buf_filled == 0)
return 0; // I guess this is an empty file?
while (true) {
int line_end;
for (line_end = offset;
buf[line_end] != '\n' && buf[line_end] != '\r' && line_end < buf_filled; line_end++);
line.append(buf + offset, line_end - offset);
if (line_end >= buf_filled) {
buf_filled = stream->read(buf, 512);
if (buf_filled < 0)
throw Gio::Error(Gio::Error::FAILED, "read() returned -1");
if (buf_filled == 0)
break;
offset = 0;
} else {
// ------------------- Start processing this line ---------------------
if (line.substr(0, 8) == "#EXTINF:") {
ext_line = line;
} else if (line.substr(0, 1) != "#" && line.size() != 0) {
Glib::ustring title;
if (ext_line.size() > 0) {
int comma = ext_line.find(',');
string time = ext_line.substr(8, comma - 8);
title = ext_line.substr(comma + 1, ext_line.size() - (comma + 1));
ext_line = "";
} else {
title = line;
}
int tracks_added_from_line = insert_from_uri(index, line, false, false);
if (tracks_added_from_line) {
// TODO keep M3UEXT info if the file doesn't have tag information
index += tracks_added_from_line;
tracks_added += tracks_added_from_line;
} else {
// TODO show a better error message
title = string("Error reading: ") + title;
insert_track(index, TrackRef(new Track(line, title, "", "", -1)));
tracks_added++;
}
}
// ----------------- Finished processing this line --------------
line = "";
// Move to next line
for(offset = line_end;
(buf[offset] == '\n' || buf[offset] == '\r') && offset < buf_filled; offset++);
if (offset >= buf_filled) {
buf_filled = stream->read(buf, 512);
if (buf_filled < 0)
throw Gio::Error(Gio::Error::FAILED, "read() returned -1");
if (buf_filled == 0)
break;
offset = 0;
}
}
}
return tracks_added;
}