SongUpdate.cxx 4.2 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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" /* must be first for large file support */
21
#include "DetachedSong.hxx"
22 23
#include "db/plugins/simple/Song.hxx"
#include "db/plugins/simple/Directory.hxx"
24 25
#include "storage/StorageInterface.hxx"
#include "storage/FileInfo.hxx"
Max Kellermann's avatar
Max Kellermann committed
26
#include "util/UriUtil.hxx"
27
#include "util/Error.hxx"
28
#include "fs/AllocatedPath.hxx"
29
#include "fs/Traits.hxx"
30
#include "fs/FileSystem.hxx"
31
#include "decoder/DecoderList.hxx"
32
#include "tag/Tag.hxx"
33
#include "tag/TagBuilder.hxx"
34
#include "tag/TagHandler.hxx"
35 36
#include "tag/TagId3.hxx"
#include "tag/ApeTag.hxx"
37
#include "TagFile.hxx"
38
#include "TagStream.hxx"
39

40
#include <assert.h>
41
#include <string.h>
42 43
#include <sys/stat.h>

44 45
#ifdef ENABLE_DATABASE

46
Song *
47
Song::LoadFile(Storage &storage, const char *path_utf8, Directory &parent)
48
{
49
	assert(!uri_has_scheme(path_utf8));
50
	assert(strchr(path_utf8, '\n') == nullptr);
51

52
	Song *song = NewFile(path_utf8, parent);
53 54

	//in archive ?
55
	bool success = parent.device == DEVICE_INARCHIVE
56
		? song->UpdateFileInArchive(storage)
57
		: song->UpdateFile(storage);
58
	if (!success) {
59
		song->Free();
60
		return nullptr;
61 62 63 64 65
	}

	return song;
}

66 67
#endif

68 69 70
/**
 * Attempts to load APE or ID3 tags from the specified file.
 */
71
static bool
72
tag_scan_fallback(Path path,
73
		  const struct tag_handler *handler, void *handler_ctx)
74
{
75 76
	return tag_ape_scan2(path, handler, handler_ctx) ||
		tag_id3_scan(path, handler, handler_ctx);
77 78
}

79 80
#ifdef ENABLE_DATABASE

81
bool
82
Song::UpdateFile(Storage &storage)
83
{
84
	const auto &relative_uri = GetURI();
85

86 87
	FileInfo info;
	if (!storage.GetInfo(relative_uri.c_str(), true, info, IgnoreError()))
88
		return false;
89

90
	if (!info.IsRegular())
91 92
		return false;

93
	TagBuilder tag_builder;
94

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	const auto path_fs = storage.MapFS(relative_uri.c_str());
	if (path_fs.IsNull()) {
		const auto absolute_uri =
			storage.MapUTF8(relative_uri.c_str());
		if (!tag_stream_scan(absolute_uri.c_str(),
				     full_tag_handler, &tag_builder))
			return false;
	} else {
		if (!tag_file_scan(path_fs, full_tag_handler, &tag_builder))
			return false;

		if (tag_builder.IsEmpty())
			tag_scan_fallback(path_fs, &full_tag_handler,
					  &tag_builder);
	}
110

111
	mtime = info.mtime;
112
	tag_builder.Commit(tag);
113
	return true;
114 115 116
}

bool
117
Song::UpdateFileInArchive(const Storage &storage)
118 119 120
{
	/* check if there's a suffix and a plugin */

121
	const char *suffix = uri_get_suffix(uri);
122
	if (suffix == nullptr)
123 124
		return false;

125
	if (!decoder_plugins_supports_suffix(suffix))
126 127
		return false;

128 129 130
	const auto path_fs = parent->IsRoot()
		? storage.MapFS(uri)
		: storage.MapChildFS(parent->GetPath(), uri);
131 132
	if (path_fs.IsNull())
		return false;
133

134 135 136
	TagBuilder tag_builder;
	if (!tag_stream_scan(path_fs.c_str(), full_tag_handler, &tag_builder))
		return false;
137

138
	tag_builder.Commit(tag);
139 140
	return true;
}
141

142 143
#endif

144 145 146 147 148
bool
DetachedSong::Update()
{
	if (IsAbsoluteFile()) {
		const AllocatedPath path_fs =
149
			AllocatedPath::FromUTF8(GetRealURI());
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

		struct stat st;
		if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode))
			return false;

		TagBuilder tag_builder;
		if (!tag_file_scan(path_fs, full_tag_handler, &tag_builder))
			return false;

		if (tag_builder.IsEmpty())
			tag_scan_fallback(path_fs, &full_tag_handler,
					  &tag_builder);

		mtime = st.st_mtime;
		tag_builder.Commit(tag);
		return true;
	} else if (IsRemote()) {
		TagBuilder tag_builder;
		if (!tag_stream_scan(uri.c_str(), full_tag_handler,
				     &tag_builder))
			return false;

		mtime = 0;
		tag_builder.Commit(tag);
		return true;
	} else
		// TODO: implement
		return false;
}