blob: 641b382039ce130ed1be81ae9f023ada2b1219d5 [file] [log] [blame]
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01001// 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.
4
5#include "chrome/browser/media_galleries/fileapi/itunes_file_util.h"
6
Ben Murdocheb525c52013-07-10 11:40:50 +01007#include <set>
8#include <string>
9#include <vector>
10
11#include "base/bind_helpers.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010012#include "base/file_util.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010015#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_file_util.h"
19#include "webkit/browser/fileapi/file_system_operation_context.h"
20#include "webkit/browser/fileapi/file_system_url.h"
21#include "webkit/browser/fileapi/native_file_util.h"
22#include "webkit/common/blob/shareable_file_reference.h"
23#include "webkit/common/fileapi/file_system_util.h"
24
25using fileapi::DirectoryEntry;
26
27namespace itunes {
28
29namespace {
30
31const char kiTunesLibraryXML[] = "iTunes Music Library.xml";
32const char kiTunesMediaDir[] = "iTunes Media";
33
34base::PlatformFileError MakeDirectoryFileInfo(
35 base::PlatformFileInfo* file_info) {
36 base::PlatformFileInfo result;
37 result.is_directory = true;
38 *file_info = result;
39 return base::PLATFORM_FILE_OK;
40}
41
42} // namespace
43
Ben Murdochbb1529c2013-08-08 10:24:53 +010044ItunesFileUtil::ItunesFileUtil(chrome::MediaPathFilter* media_path_filter)
45 : chrome::NativeMediaFileUtil(media_path_filter),
46 weak_factory_(this),
47 imported_registry_(NULL) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010048}
49
50ItunesFileUtil::~ItunesFileUtil() {
51}
52
53void ItunesFileUtil::GetFileInfoOnTaskRunnerThread(
Ben Murdocheb525c52013-07-10 11:40:50 +010054 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010055 const fileapi::FileSystemURL& url,
56 const GetFileInfoCallback& callback) {
57 GetDataProvider()->RefreshData(
58 base::Bind(&ItunesFileUtil::GetFileInfoWithFreshDataProvider,
Ben Murdocheb525c52013-07-10 11:40:50 +010059 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
60 callback));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010061}
62
63void ItunesFileUtil::ReadDirectoryOnTaskRunnerThread(
Ben Murdocheb525c52013-07-10 11:40:50 +010064 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010065 const fileapi::FileSystemURL& url,
66 const ReadDirectoryCallback& callback) {
67 GetDataProvider()->RefreshData(
68 base::Bind(&ItunesFileUtil::ReadDirectoryWithFreshDataProvider,
Ben Murdocheb525c52013-07-10 11:40:50 +010069 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
70 callback));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010071}
72
73void ItunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
Ben Murdocheb525c52013-07-10 11:40:50 +010074 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010075 const fileapi::FileSystemURL& url,
76 const CreateSnapshotFileCallback& callback) {
77 GetDataProvider()->RefreshData(
78 base::Bind(&ItunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
Ben Murdocheb525c52013-07-10 11:40:50 +010079 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
80 callback));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010081}
82
83// Contents of the iTunes media gallery:
84// / - root directory
85// /iTunes Music Library.xml - library xml file
86// /iTunes Media/<Artist>/<Album>/<Track> - tracks
87//
88base::PlatformFileError ItunesFileUtil::GetFileInfoSync(
89 fileapi::FileSystemOperationContext* context,
90 const fileapi::FileSystemURL& url,
91 base::PlatformFileInfo* file_info,
92 base::FilePath* platform_path) {
93 std::vector<std::string> components;
94 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
95
96 if (components.size() == 0)
97 return MakeDirectoryFileInfo(file_info);
98
99 if (components.size() == 1 && components[0] == kiTunesLibraryXML) {
100 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
101 // uses the MediaPathFilter. At this point, |library_path_| is known good
102 // because GetFileInfoWithFreshDataProvider() gates access to this method.
103 base::FilePath file_path = GetDataProvider()->library_path();
104 if (platform_path)
105 *platform_path = file_path;
106 return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
107 }
108
109 if (components[0] != kiTunesMediaDir || components.size() > 4)
110 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
111
112 switch (components.size()) {
113 case 1:
114 return MakeDirectoryFileInfo(file_info);
115
116 case 2:
117 if (GetDataProvider()->KnownArtist(components[1]))
118 return MakeDirectoryFileInfo(file_info);
119 break;
120
121 case 3:
122 if (GetDataProvider()->KnownAlbum(components[1], components[2]))
123 return MakeDirectoryFileInfo(file_info);
124 break;
125
126 case 4: {
127 base::FilePath location =
128 GetDataProvider()->GetTrackLocation(components[1], components[2],
129 components[3]);
130 if (!location.empty()) {
131 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
132 platform_path);
133 }
134 break;
135 }
136
137 default:
138 NOTREACHED();
139 }
140
141 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
142}
143
144base::PlatformFileError ItunesFileUtil::ReadDirectorySync(
145 fileapi::FileSystemOperationContext* context,
146 const fileapi::FileSystemURL& url,
147 EntryList* file_list) {
148 DCHECK(file_list->empty());
149 std::vector<std::string> components;
150 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
151
152 if (components.size() == 0) {
153 base::PlatformFileInfo xml_info;
154 if (!file_util::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
155 return base::PLATFORM_FILE_ERROR_IO;
156 file_list->push_back(DirectoryEntry(kiTunesLibraryXML,
157 DirectoryEntry::FILE,
158 xml_info.size, xml_info.last_modified));
159 file_list->push_back(DirectoryEntry(kiTunesMediaDir,
160 DirectoryEntry::DIRECTORY,
161 0, base::Time()));
162 return base::PLATFORM_FILE_OK;
163 }
164
165 if (components.size() == 1 && components[0] == kiTunesLibraryXML)
166 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
167
168 if (components[0] != kiTunesMediaDir || components.size() > 4)
169 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
170
171 if (components.size() == 1) {
172 std::set<ITunesDataProvider::ArtistName> artists =
173 GetDataProvider()->GetArtistNames();
174 std::set<ITunesDataProvider::ArtistName>::const_iterator it;
175 for (it = artists.begin(); it != artists.end(); ++it)
176 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
177 0, base::Time()));
178 return base::PLATFORM_FILE_OK;
179 }
180
181 if (components.size() == 2) {
182 std::set<ITunesDataProvider::AlbumName> albums =
183 GetDataProvider()->GetAlbumNames(components[1]);
184 if (albums.size() == 0)
185 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
186 std::set<ITunesDataProvider::AlbumName>::const_iterator it;
187 for (it = albums.begin(); it != albums.end(); ++it)
188 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
189 0, base::Time()));
190 return base::PLATFORM_FILE_OK;
191 }
192
193 if (components.size() == 3) {
194 ITunesDataProvider::Album album =
195 GetDataProvider()->GetAlbum(components[1], components[2]);
196 if (album.size() == 0)
197 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100198 ITunesDataProvider::Album::const_iterator it;
199 for (it = album.begin(); it != album.end(); ++it) {
200 base::PlatformFileInfo file_info;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100201 if (media_path_filter()->Match(it->second) &&
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100202 file_util::GetFileInfo(it->second, &file_info)) {
203 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
204 file_info.size,
205 file_info.last_modified));
206 }
207 }
208 return base::PLATFORM_FILE_OK;
209 }
210
211 // At this point, the only choice is one of two errors, but figuring out
212 // which one is required.
213 DCHECK_EQ(4UL, components.size());
214 base::FilePath location;
215 location = GetDataProvider()->GetTrackLocation(components[1], components[2],
216 components[3]);
217 if (!location.empty())
218 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
219 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
220}
221
222base::PlatformFileError ItunesFileUtil::CreateSnapshotFileSync(
223 fileapi::FileSystemOperationContext* context,
224 const fileapi::FileSystemURL& url,
225 base::PlatformFileInfo* file_info,
226 base::FilePath* platform_path,
227 scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
228 DCHECK(!url.path().IsAbsolute());
229 if (url.path() != base::FilePath().AppendASCII(kiTunesLibraryXML)) {
230 return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
231 platform_path, file_ref);
232 }
233
234 // The following code is different than
235 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
236 // library xml file is not a directory and it doesn't run mime sniffing on the
237 // file. The only way to get here is by way of
238 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
239 // parsed and deemed valid.
240 *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
241 return GetFileInfoSync(context, url, file_info, platform_path);
242}
243
244base::PlatformFileError ItunesFileUtil::GetLocalFilePath(
245 fileapi::FileSystemOperationContext* context,
246 const fileapi::FileSystemURL& url,
247 base::FilePath* local_file_path) {
248 // Should only get here for files, i.e. the xml file and tracks.
249 std::vector<std::string> components;
250 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
251
252 if (components.size() == 1 && components[0] == kiTunesLibraryXML) {
253 *local_file_path = GetDataProvider()->library_path();
254 return base::PLATFORM_FILE_OK;
255 }
256
257 if (components[0] != kiTunesMediaDir || components.size() != 4)
258 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
259
260 *local_file_path = GetDataProvider()->GetTrackLocation(components[1],
261 components[2],
262 components[3]);
263 if (!local_file_path->empty())
264 return base::PLATFORM_FILE_OK;
265
266 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
267}
268
269void ItunesFileUtil::GetFileInfoWithFreshDataProvider(
Ben Murdocheb525c52013-07-10 11:40:50 +0100270 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100271 const fileapi::FileSystemURL& url,
272 const GetFileInfoCallback& callback,
273 bool valid_parse) {
274 if (!valid_parse) {
275 if (!callback.is_null()) {
276 content::BrowserThread::PostTask(
277 content::BrowserThread::IO,
278 FROM_HERE,
279 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO,
280 base::PlatformFileInfo()));
281 }
282 return;
283 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100284 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
285 callback);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100286}
287
288void ItunesFileUtil::ReadDirectoryWithFreshDataProvider(
Ben Murdocheb525c52013-07-10 11:40:50 +0100289 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100290 const fileapi::FileSystemURL& url,
291 const ReadDirectoryCallback& callback,
292 bool valid_parse) {
293 if (!valid_parse) {
294 if (!callback.is_null()) {
295 content::BrowserThread::PostTask(
296 content::BrowserThread::IO,
297 FROM_HERE,
298 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, EntryList(),
299 false));
300 }
301 return;
302 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100303 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
304 callback);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100305}
306
307void ItunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
Ben Murdocheb525c52013-07-10 11:40:50 +0100308 scoped_ptr<fileapi::FileSystemOperationContext> context,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100309 const fileapi::FileSystemURL& url,
310 const CreateSnapshotFileCallback& callback,
311 bool valid_parse) {
312 if (!valid_parse) {
313 if (!callback.is_null()) {
314 base::PlatformFileInfo file_info;
315 base::FilePath platform_path;
316 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
317 content::BrowserThread::PostTask(
318 content::BrowserThread::IO,
319 FROM_HERE,
320 base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, file_info,
321 platform_path, file_ref));
322 }
323 return;
324 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100325 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100326 callback);
327}
328
329ITunesDataProvider* ItunesFileUtil::GetDataProvider() {
330 if (!imported_registry_)
331 imported_registry_ = chrome::ImportedMediaGalleryRegistry::GetInstance();
332 return imported_registry_->ITunesDataProvider();
333}
334
335} // namespace itunes