blob: 641b382039ce130ed1be81ae9f023ada2b1219d5 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media_galleries/fileapi/itunes_file_util.h"
#include <set>
#include <string>
#include <vector>
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
#include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
#include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/native_file_util.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/file_system_util.h"
using fileapi::DirectoryEntry;
namespace itunes {
namespace {
const char kiTunesLibraryXML[] = "iTunes Music Library.xml";
const char kiTunesMediaDir[] = "iTunes Media";
base::PlatformFileError MakeDirectoryFileInfo(
base::PlatformFileInfo* file_info) {
base::PlatformFileInfo result;
result.is_directory = true;
*file_info = result;
return base::PLATFORM_FILE_OK;
}
} // namespace
ItunesFileUtil::ItunesFileUtil(chrome::MediaPathFilter* media_path_filter)
: chrome::NativeMediaFileUtil(media_path_filter),
weak_factory_(this),
imported_registry_(NULL) {
}
ItunesFileUtil::~ItunesFileUtil() {
}
void ItunesFileUtil::GetFileInfoOnTaskRunnerThread(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const GetFileInfoCallback& callback) {
GetDataProvider()->RefreshData(
base::Bind(&ItunesFileUtil::GetFileInfoWithFreshDataProvider,
weak_factory_.GetWeakPtr(), base::Passed(&context), url,
callback));
}
void ItunesFileUtil::ReadDirectoryOnTaskRunnerThread(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const ReadDirectoryCallback& callback) {
GetDataProvider()->RefreshData(
base::Bind(&ItunesFileUtil::ReadDirectoryWithFreshDataProvider,
weak_factory_.GetWeakPtr(), base::Passed(&context), url,
callback));
}
void ItunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const CreateSnapshotFileCallback& callback) {
GetDataProvider()->RefreshData(
base::Bind(&ItunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
weak_factory_.GetWeakPtr(), base::Passed(&context), url,
callback));
}
// Contents of the iTunes media gallery:
// / - root directory
// /iTunes Music Library.xml - library xml file
// /iTunes Media/<Artist>/<Album>/<Track> - tracks
//
base::PlatformFileError ItunesFileUtil::GetFileInfoSync(
fileapi::FileSystemOperationContext* context,
const fileapi::FileSystemURL& url,
base::PlatformFileInfo* file_info,
base::FilePath* platform_path) {
std::vector<std::string> components;
fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
if (components.size() == 0)
return MakeDirectoryFileInfo(file_info);
if (components.size() == 1 && components[0] == kiTunesLibraryXML) {
// We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
// uses the MediaPathFilter. At this point, |library_path_| is known good
// because GetFileInfoWithFreshDataProvider() gates access to this method.
base::FilePath file_path = GetDataProvider()->library_path();
if (platform_path)
*platform_path = file_path;
return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
}
if (components[0] != kiTunesMediaDir || components.size() > 4)
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
switch (components.size()) {
case 1:
return MakeDirectoryFileInfo(file_info);
case 2:
if (GetDataProvider()->KnownArtist(components[1]))
return MakeDirectoryFileInfo(file_info);
break;
case 3:
if (GetDataProvider()->KnownAlbum(components[1], components[2]))
return MakeDirectoryFileInfo(file_info);
break;
case 4: {
base::FilePath location =
GetDataProvider()->GetTrackLocation(components[1], components[2],
components[3]);
if (!location.empty()) {
return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
platform_path);
}
break;
}
default:
NOTREACHED();
}
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
}
base::PlatformFileError ItunesFileUtil::ReadDirectorySync(
fileapi::FileSystemOperationContext* context,
const fileapi::FileSystemURL& url,
EntryList* file_list) {
DCHECK(file_list->empty());
std::vector<std::string> components;
fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
if (components.size() == 0) {
base::PlatformFileInfo xml_info;
if (!file_util::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
return base::PLATFORM_FILE_ERROR_IO;
file_list->push_back(DirectoryEntry(kiTunesLibraryXML,
DirectoryEntry::FILE,
xml_info.size, xml_info.last_modified));
file_list->push_back(DirectoryEntry(kiTunesMediaDir,
DirectoryEntry::DIRECTORY,
0, base::Time()));
return base::PLATFORM_FILE_OK;
}
if (components.size() == 1 && components[0] == kiTunesLibraryXML)
return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
if (components[0] != kiTunesMediaDir || components.size() > 4)
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
if (components.size() == 1) {
std::set<ITunesDataProvider::ArtistName> artists =
GetDataProvider()->GetArtistNames();
std::set<ITunesDataProvider::ArtistName>::const_iterator it;
for (it = artists.begin(); it != artists.end(); ++it)
file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
0, base::Time()));
return base::PLATFORM_FILE_OK;
}
if (components.size() == 2) {
std::set<ITunesDataProvider::AlbumName> albums =
GetDataProvider()->GetAlbumNames(components[1]);
if (albums.size() == 0)
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
std::set<ITunesDataProvider::AlbumName>::const_iterator it;
for (it = albums.begin(); it != albums.end(); ++it)
file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
0, base::Time()));
return base::PLATFORM_FILE_OK;
}
if (components.size() == 3) {
ITunesDataProvider::Album album =
GetDataProvider()->GetAlbum(components[1], components[2]);
if (album.size() == 0)
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
ITunesDataProvider::Album::const_iterator it;
for (it = album.begin(); it != album.end(); ++it) {
base::PlatformFileInfo file_info;
if (media_path_filter()->Match(it->second) &&
file_util::GetFileInfo(it->second, &file_info)) {
file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
file_info.size,
file_info.last_modified));
}
}
return base::PLATFORM_FILE_OK;
}
// At this point, the only choice is one of two errors, but figuring out
// which one is required.
DCHECK_EQ(4UL, components.size());
base::FilePath location;
location = GetDataProvider()->GetTrackLocation(components[1], components[2],
components[3]);
if (!location.empty())
return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
}
base::PlatformFileError ItunesFileUtil::CreateSnapshotFileSync(
fileapi::FileSystemOperationContext* context,
const fileapi::FileSystemURL& url,
base::PlatformFileInfo* file_info,
base::FilePath* platform_path,
scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
DCHECK(!url.path().IsAbsolute());
if (url.path() != base::FilePath().AppendASCII(kiTunesLibraryXML)) {
return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
platform_path, file_ref);
}
// The following code is different than
// NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
// library xml file is not a directory and it doesn't run mime sniffing on the
// file. The only way to get here is by way of
// CreateSnapshotFileWithFreshDataProvider() so the file has already been
// parsed and deemed valid.
*file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
return GetFileInfoSync(context, url, file_info, platform_path);
}
base::PlatformFileError ItunesFileUtil::GetLocalFilePath(
fileapi::FileSystemOperationContext* context,
const fileapi::FileSystemURL& url,
base::FilePath* local_file_path) {
// Should only get here for files, i.e. the xml file and tracks.
std::vector<std::string> components;
fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
if (components.size() == 1 && components[0] == kiTunesLibraryXML) {
*local_file_path = GetDataProvider()->library_path();
return base::PLATFORM_FILE_OK;
}
if (components[0] != kiTunesMediaDir || components.size() != 4)
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
*local_file_path = GetDataProvider()->GetTrackLocation(components[1],
components[2],
components[3]);
if (!local_file_path->empty())
return base::PLATFORM_FILE_OK;
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
}
void ItunesFileUtil::GetFileInfoWithFreshDataProvider(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const GetFileInfoCallback& callback,
bool valid_parse) {
if (!valid_parse) {
if (!callback.is_null()) {
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(callback, base::PLATFORM_FILE_ERROR_IO,
base::PlatformFileInfo()));
}
return;
}
NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
callback);
}
void ItunesFileUtil::ReadDirectoryWithFreshDataProvider(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const ReadDirectoryCallback& callback,
bool valid_parse) {
if (!valid_parse) {
if (!callback.is_null()) {
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, EntryList(),
false));
}
return;
}
NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
callback);
}
void ItunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& url,
const CreateSnapshotFileCallback& callback,
bool valid_parse) {
if (!valid_parse) {
if (!callback.is_null()) {
base::PlatformFileInfo file_info;
base::FilePath platform_path;
scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, file_info,
platform_path, file_ref));
}
return;
}
NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
callback);
}
ITunesDataProvider* ItunesFileUtil::GetDataProvider() {
if (!imported_registry_)
imported_registry_ = chrome::ImportedMediaGalleryRegistry::GetInstance();
return imported_registry_->ITunesDataProvider();
}
} // namespace itunes