1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media_galleries/fileapi/itunes_file_util.h"
11 #include "base/bind_helpers.h"
12 #include "base/file_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
15 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
16 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "webkit/browser/fileapi/file_system_operation_context.h"
19 #include "webkit/browser/fileapi/file_system_url.h"
20 #include "webkit/browser/fileapi/native_file_util.h"
21 #include "webkit/common/blob/shareable_file_reference.h"
22 #include "webkit/common/fileapi/file_system_util.h"
24 using fileapi::DirectoryEntry;
30 base::PlatformFileError MakeDirectoryFileInfo(
31 base::PlatformFileInfo* file_info) {
32 base::PlatformFileInfo result;
33 result.is_directory = true;
35 return base::PLATFORM_FILE_OK;
40 const char kITunesLibraryXML[] = "iTunes Music Library.xml";
41 const char kITunesMediaDir[] = "iTunes Media";
42 const char kITunesMusicDir[] = "Music";
43 const char kITunesAutoAddDir[] = "Automatically Add to iTunes";
45 ITunesFileUtil::ITunesFileUtil(MediaPathFilter* media_path_filter)
46 : NativeMediaFileUtil(media_path_filter),
48 imported_registry_(NULL) {
51 ITunesFileUtil::~ITunesFileUtil() {
54 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
55 scoped_ptr<fileapi::FileSystemOperationContext> context,
56 const fileapi::FileSystemURL& url,
57 const GetFileInfoCallback& callback) {
58 GetDataProvider()->RefreshData(
59 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
60 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
64 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
65 scoped_ptr<fileapi::FileSystemOperationContext> context,
66 const fileapi::FileSystemURL& url,
67 const ReadDirectoryCallback& callback) {
68 GetDataProvider()->RefreshData(
69 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
70 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
74 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
75 scoped_ptr<fileapi::FileSystemOperationContext> context,
76 const fileapi::FileSystemURL& url,
77 const CreateSnapshotFileCallback& callback) {
78 GetDataProvider()->RefreshData(
79 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
80 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
84 // Contents of the iTunes media gallery:
86 // /iTunes Music Library.xml - library xml file
87 // /iTunes Media/Automatically Add to iTunes - auto-import directory
88 // /iTunes Media/Music/<Artist>/<Album>/<Track> - tracks
90 base::PlatformFileError ITunesFileUtil::GetFileInfoSync(
91 fileapi::FileSystemOperationContext* context,
92 const fileapi::FileSystemURL& url,
93 base::PlatformFileInfo* file_info,
94 base::FilePath* platform_path) {
95 std::vector<std::string> components;
96 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
98 if (components.size() == 0)
99 return MakeDirectoryFileInfo(file_info);
101 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
102 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
103 // uses the MediaPathFilter. At this point, |library_path_| is known good
104 // because GetFileInfoWithFreshDataProvider() gates access to this method.
105 base::FilePath file_path = GetDataProvider()->library_path();
107 *platform_path = file_path;
108 return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
111 if (components[0] != kITunesMediaDir)
112 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
114 if (components[1] == kITunesAutoAddDir) {
115 if (GetDataProvider()->auto_add_path().empty())
116 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
117 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
121 if (components[1] == kITunesMusicDir) {
122 switch (components.size()) {
124 return MakeDirectoryFileInfo(file_info);
127 if (GetDataProvider()->KnownArtist(components[2]))
128 return MakeDirectoryFileInfo(file_info);
132 if (GetDataProvider()->KnownAlbum(components[2], components[3]))
133 return MakeDirectoryFileInfo(file_info);
137 base::FilePath location =
138 GetDataProvider()->GetTrackLocation(components[2], components[3],
140 if (!location.empty()) {
141 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
149 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
152 base::PlatformFileError ITunesFileUtil::ReadDirectorySync(
153 fileapi::FileSystemOperationContext* context,
154 const fileapi::FileSystemURL& url,
155 EntryList* file_list) {
156 DCHECK(file_list->empty());
157 std::vector<std::string> components;
158 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
160 if (components.size() == 0) {
161 base::PlatformFileInfo xml_info;
162 if (!file_util::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
163 return base::PLATFORM_FILE_ERROR_IO;
164 file_list->push_back(DirectoryEntry(kITunesLibraryXML,
165 DirectoryEntry::FILE,
166 xml_info.size, xml_info.last_modified));
167 file_list->push_back(DirectoryEntry(kITunesMediaDir,
168 DirectoryEntry::DIRECTORY,
170 return base::PLATFORM_FILE_OK;
173 if (components.size() == 1 && components[0] == kITunesLibraryXML)
174 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
176 if (components[0] != kITunesMediaDir || components.size() > 5)
177 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
179 if (components.size() == 1) {
180 if (!GetDataProvider()->auto_add_path().empty()) {
181 file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
182 DirectoryEntry::DIRECTORY,
185 file_list->push_back(DirectoryEntry(kITunesMusicDir,
186 DirectoryEntry::DIRECTORY,
188 return base::PLATFORM_FILE_OK;
191 if (components[1] == kITunesAutoAddDir &&
192 !GetDataProvider()->auto_add_path().empty()) {
193 return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
196 if (components[1] != kITunesMusicDir)
197 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
199 if (components.size() == 2) {
200 std::set<ITunesDataProvider::ArtistName> artists =
201 GetDataProvider()->GetArtistNames();
202 std::set<ITunesDataProvider::ArtistName>::const_iterator it;
203 for (it = artists.begin(); it != artists.end(); ++it)
204 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
206 return base::PLATFORM_FILE_OK;
209 if (components.size() == 3) {
210 std::set<ITunesDataProvider::AlbumName> albums =
211 GetDataProvider()->GetAlbumNames(components[2]);
212 if (albums.size() == 0)
213 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
214 std::set<ITunesDataProvider::AlbumName>::const_iterator it;
215 for (it = albums.begin(); it != albums.end(); ++it)
216 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
218 return base::PLATFORM_FILE_OK;
221 if (components.size() == 4) {
222 ITunesDataProvider::Album album =
223 GetDataProvider()->GetAlbum(components[2], components[3]);
224 if (album.size() == 0)
225 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
226 ITunesDataProvider::Album::const_iterator it;
227 for (it = album.begin(); it != album.end(); ++it) {
228 base::PlatformFileInfo file_info;
229 if (media_path_filter()->Match(it->second) &&
230 file_util::GetFileInfo(it->second, &file_info)) {
231 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
233 file_info.last_modified));
236 return base::PLATFORM_FILE_OK;
239 // At this point, the only choice is one of two errors, but figuring out
240 // which one is required.
241 DCHECK_EQ(4UL, components.size());
242 base::FilePath location;
243 location = GetDataProvider()->GetTrackLocation(components[1], components[2],
245 if (!location.empty())
246 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
247 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
250 base::PlatformFileError ITunesFileUtil::CreateSnapshotFileSync(
251 fileapi::FileSystemOperationContext* context,
252 const fileapi::FileSystemURL& url,
253 base::PlatformFileInfo* file_info,
254 base::FilePath* platform_path,
255 scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
256 DCHECK(!url.path().IsAbsolute());
257 if (url.path() != base::FilePath().AppendASCII(kITunesLibraryXML)) {
258 return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
259 platform_path, file_ref);
262 // The following code is different than
263 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
264 // library xml file is not a directory and it doesn't run mime sniffing on the
265 // file. The only way to get here is by way of
266 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
267 // parsed and deemed valid.
268 *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
269 return GetFileInfoSync(context, url, file_info, platform_path);
272 base::PlatformFileError ITunesFileUtil::GetLocalFilePath(
273 fileapi::FileSystemOperationContext* context,
274 const fileapi::FileSystemURL& url,
275 base::FilePath* local_file_path) {
276 std::vector<std::string> components;
277 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
279 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
280 *local_file_path = GetDataProvider()->library_path();
281 return base::PLATFORM_FILE_OK;
284 if (components.size() >= 2 && components[0] == kITunesMediaDir &&
285 components[1] == kITunesAutoAddDir) {
286 *local_file_path = GetDataProvider()->auto_add_path();
287 if (local_file_path->empty())
288 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
290 for (size_t i = 2; i < components.size(); ++i) {
291 *local_file_path = local_file_path->Append(
292 base::FilePath::FromUTF8Unsafe(components[i]));
294 return base::PLATFORM_FILE_OK;
297 // Should only get here for files, i.e. the xml file and tracks.
298 if (components[0] != kITunesMediaDir || components[1] != kITunesMusicDir||
299 components.size() != 5) {
300 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
303 *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
306 if (!local_file_path->empty())
307 return base::PLATFORM_FILE_OK;
309 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
312 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
313 scoped_ptr<fileapi::FileSystemOperationContext> context,
314 const fileapi::FileSystemURL& url,
315 const GetFileInfoCallback& callback,
318 if (!callback.is_null()) {
319 content::BrowserThread::PostTask(
320 content::BrowserThread::IO,
322 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO,
323 base::PlatformFileInfo()));
327 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
331 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
332 scoped_ptr<fileapi::FileSystemOperationContext> context,
333 const fileapi::FileSystemURL& url,
334 const ReadDirectoryCallback& callback,
337 if (!callback.is_null()) {
338 content::BrowserThread::PostTask(
339 content::BrowserThread::IO,
341 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, EntryList(),
346 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
350 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
351 scoped_ptr<fileapi::FileSystemOperationContext> context,
352 const fileapi::FileSystemURL& url,
353 const CreateSnapshotFileCallback& callback,
356 if (!callback.is_null()) {
357 base::PlatformFileInfo file_info;
358 base::FilePath platform_path;
359 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
360 content::BrowserThread::PostTask(
361 content::BrowserThread::IO,
363 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, file_info,
364 platform_path, file_ref));
368 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
372 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
373 if (!imported_registry_)
374 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
375 return imported_registry_->ITunesDataProvider();
378 } // namespace itunes