run_input.cxx 5.01 KB
Newer Older
1
/*
2
 * Copyright 2003-2019 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
#include "ConfigGlue.hxx"
Max Kellermann's avatar
Max Kellermann committed
24 25
#include "input/InputStream.hxx"
#include "input/Init.hxx"
26 27 28
#include "input/Registry.hxx"
#include "input/InputPlugin.hxx"
#include "input/RemoteTagScanner.hxx"
29
#include "input/ScanTags.hxx"
30
#include "event/Thread.hxx"
31
#include "thread/Cond.hxx"
32
#include "Log.hxx"
33
#include "LogBackend.hxx"
34
#include "fs/Path.hxx"
35 36
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/StdioOutputStream.hxx"
37
#include "util/ConstBuffer.hxx"
38
#include "util/OptionDef.hxx"
39
#include "util/OptionParser.hxx"
40
#include "util/PrintException.hxx"
41

42
#ifdef ENABLE_ARCHIVE
43
#include "archive/ArchiveList.hxx"
44 45
#endif

46 47
#include <stdexcept>

48
#include <unistd.h>
49
#include <stdlib.h>
50

51 52
struct CommandLine {
	const char *uri = nullptr;
53

54
	Path config_path = nullptr;
55 56

	bool verbose = false;
57 58

	bool scan = false;
59 60 61 62
};

enum Option {
	OPTION_CONFIG,
63
	OPTION_VERBOSE,
64
	OPTION_SCAN,
65 66 67 68
};

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

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

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

		case OPTION_VERBOSE:
			c.verbose = true;
			break;
88 89 90 91

		case OPTION_SCAN:
			c.scan = true;
			break;
92
		}
93 94 95 96
	}

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

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

103
class GlobalInit {
104
	const ConfigData config;
105
	EventThread io_thread;
106

107 108 109 110
#ifdef ENABLE_ARCHIVE
	const ScopeArchivePluginsInit archive_plugins_init;
#endif

111 112
	const ScopeInputPluginsInit input_plugins_init;

113
public:
114
	explicit GlobalInit(Path config_path)
115 116
		:config(AutoLoadConfigFile(config_path)),
		 input_plugins_init(config, io_thread.GetEventLoop())
117
	{
118
		io_thread.Start();
119 120 121
	}
};

122 123 124 125 126 127 128 129 130
static void
tag_save(FILE *file, const Tag &tag)
{
	StdioOutputStream sos(file);
	BufferedOutputStream bos(sos);
	tag_save(bos, tag);
	bos.Flush();
}

131
static int
132
dump_input_stream(InputStream *is)
133
{
134
	const std::lock_guard<Mutex> protect(is->mutex);
135

136 137
	/* print meta data */

138 139
	if (is->HasMimeType())
		fprintf(stderr, "MIME type: %s\n", is->GetMimeType());
140 141 142

	/* read data and tags from the stream */

143
	while (!is->IsEOF()) {
144 145 146 147 148 149
		{
			auto tag = is->ReadTag();
			if (tag) {
				fprintf(stderr, "Received a tag:\n");
				tag_save(stderr, *tag);
			}
150 151
		}

152
		char buffer[4096];
153 154
		size_t num_read = is->Read(buffer, sizeof(buffer));
		if (num_read == 0)
155 156
			break;

157
		ssize_t num_written = write(1, buffer, num_read);
158 159 160 161
		if (num_written <= 0)
			break;
	}

162
	is->Check();
163

164 165 166
	return 0;
}

167 168 169 170 171 172 173 174 175 176 177 178 179 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
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;

209 210 211 212
	auto scanner = InputScanTags(uri, handler);
	if (!scanner) {
		fprintf(stderr, "Unsupported URI\n");
		return EXIT_FAILURE;
213 214
	}

215 216 217
	scanner->Start();
	tag_save(stdout, handler.Wait());
	return EXIT_SUCCESS;
218 219
}

220
int main(int argc, char **argv)
221
try {
222
	const auto c = ParseCommandLine(argc, argv);
223 224 225

	/* initialize MPD */

226 227
	SetLogThreshold(c.verbose ? LogLevel::DEBUG : LogLevel::INFO);
	const GlobalInit init(c.config_path);
228

229 230 231
	if (c.scan)
		return Scan(c.uri);

232 233
	/* open the stream and dump it */

234
	Mutex mutex;
235
	auto is = InputStream::OpenReady(c.uri, mutex);
236
	return dump_input_stream(is.get());
237 238
} catch (...) {
	PrintException(std::current_exception());
239
	return EXIT_FAILURE;
240
}