run_input.cxx 5.18 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * http://www.musicpd.org
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

20
#include "config.h"
21
#include "TagSave.hxx"
22
#include "tag/Tag.hxx"
23 24 25
#include "config/File.hxx"
#include "config/Migrate.hxx"
#include "config/Data.hxx"
Max Kellermann's avatar
Max Kellermann committed
26 27
#include "input/InputStream.hxx"
#include "input/Init.hxx"
28 29 30
#include "input/Registry.hxx"
#include "input/InputPlugin.hxx"
#include "input/RemoteTagScanner.hxx"
31
#include "input/ScanTags.hxx"
32
#include "event/Thread.hxx"
33
#include "thread/Cond.hxx"
34
#include "Log.hxx"
35
#include "LogBackend.hxx"
36
#include "fs/Path.hxx"
37 38
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/StdioOutputStream.hxx"
39
#include "util/ConstBuffer.hxx"
40
#include "util/OptionDef.hxx"
41
#include "util/OptionParser.hxx"
42
#include "util/PrintException.hxx"
43

44
#ifdef ENABLE_ARCHIVE
45
#include "archive/ArchiveList.hxx"
46 47
#endif

48 49
#include <stdexcept>

50
#include <unistd.h>
51
#include <stdlib.h>
52

53 54
struct CommandLine {
	const char *uri = nullptr;
55

56
	Path config_path = nullptr;
57 58

	bool verbose = false;
59 60

	bool scan = false;
61 62 63 64
};

enum Option {
	OPTION_CONFIG,
65
	OPTION_VERBOSE,
66
	OPTION_SCAN,
67 68 69 70
};

static constexpr OptionDef option_defs[] = {
	{"config", 0, true, "Load a MPD configuration file"},
71
	{"verbose", 'v', false, "Verbose logging"},
72
	{"scan", 0, false, "Scan tags instead of reading raw data"},
73 74 75 76 77 78 79
};

static CommandLine
ParseCommandLine(int argc, char **argv)
{
	CommandLine c;

80
	OptionParser option_parser(option_defs, argc, argv);
81
	while (auto o = option_parser.Next()) {
82 83 84 85
		switch (Option(o.index)) {
		case OPTION_CONFIG:
			c.config_path = Path::FromFS(o.value);
			break;
86 87 88 89

		case OPTION_VERBOSE:
			c.verbose = true;
			break;
90 91 92 93

		case OPTION_SCAN:
			c.scan = true;
			break;
94
		}
95 96 97 98
	}

	auto args = option_parser.GetRemaining();
	if (args.size != 1)
99
		throw std::runtime_error("Usage: run_input [--verbose] [--config=FILE] URI");
100 101 102 103 104

	c.uri = args.front();
	return c;
}

105
class GlobalInit {
106
	ConfigData config;
107
	EventThread io_thread;
108 109

public:
110 111 112
	GlobalInit(Path config_path, bool verbose) {
		SetLogThreshold(verbose ? LogLevel::DEBUG : LogLevel::INFO);

113 114 115 116
		if (!config_path.IsNull()) {
			ReadConfigFile(config, config_path);
			Migrate(config);
		}
117

118
		io_thread.Start();
119

120 121 122
#ifdef ENABLE_ARCHIVE
		archive_plugin_init_all();
#endif
123
		input_stream_global_init(config,
124
					 io_thread.GetEventLoop());
125 126 127 128 129 130 131 132 133 134
	}

	~GlobalInit() {
		input_stream_global_finish();
#ifdef ENABLE_ARCHIVE
		archive_plugin_deinit_all();
#endif
	}
};

135 136 137 138 139 140 141 142 143
static void
tag_save(FILE *file, const Tag &tag)
{
	StdioOutputStream sos(file);
	BufferedOutputStream bos(sos);
	tag_save(bos, tag);
	bos.Flush();
}

144
static int
145
dump_input_stream(InputStream *is)
146
{
147
	const std::lock_guard<Mutex> protect(is->mutex);
148

149 150
	/* print meta data */

151 152
	if (is->HasMimeType())
		fprintf(stderr, "MIME type: %s\n", is->GetMimeType());
153 154 155

	/* read data and tags from the stream */

156
	while (!is->IsEOF()) {
157 158 159 160 161 162
		{
			auto tag = is->ReadTag();
			if (tag) {
				fprintf(stderr, "Received a tag:\n");
				tag_save(stderr, *tag);
			}
163 164
		}

165
		char buffer[4096];
166 167
		size_t num_read = is->Read(buffer, sizeof(buffer));
		if (num_read == 0)
168 169
			break;

170
		ssize_t num_written = write(1, buffer, num_read);
171 172 173 174
		if (num_written <= 0)
			break;
	}

175
	is->Check();
176

177 178 179
	return 0;
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
class DumpRemoteTagHandler final : public RemoteTagHandler {
	Mutex mutex;
	Cond cond;

	Tag tag;
	std::exception_ptr error;

	bool done = false;

public:
	Tag Wait() {
		const std::lock_guard<Mutex> lock(mutex);
		while (!done)
			cond.wait(mutex);

		if (error)
			std::rethrow_exception(error);

		return std::move(tag);
	}

	/* virtual methods from RemoteTagHandler */
	void OnRemoteTag(Tag &&_tag) noexcept override {
		const std::lock_guard<Mutex> lock(mutex);
		tag = std::move(_tag);
		done = true;
		cond.broadcast();
	}

	void OnRemoteTagError(std::exception_ptr e) noexcept override {
		const std::lock_guard<Mutex> lock(mutex);
		error = std::move(e);
		done = true;
		cond.broadcast();
	}
};

static int
Scan(const char *uri)
{
	DumpRemoteTagHandler handler;

222 223 224 225
	auto scanner = InputScanTags(uri, handler);
	if (!scanner) {
		fprintf(stderr, "Unsupported URI\n");
		return EXIT_FAILURE;
226 227
	}

228 229 230
	scanner->Start();
	tag_save(stdout, handler.Wait());
	return EXIT_SUCCESS;
231 232
}

233
int main(int argc, char **argv)
234
try {
235
	const auto c = ParseCommandLine(argc, argv);
236 237 238

	/* initialize MPD */

239
	const GlobalInit init(c.config_path, c.verbose);
240

241 242 243
	if (c.scan)
		return Scan(c.uri);

244 245
	/* open the stream and dump it */

246
	Mutex mutex;
247
	auto is = InputStream::OpenReady(c.uri, mutex);
248
	return dump_input_stream(is.get());
249 250
} catch (...) {
	PrintException(std::current_exception());
251
	return EXIT_FAILURE;
252
}