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_data_provider.h"
10 #include "base/callback.h"
11 #include "base/format_macros.h"
12 #include "base/i18n/i18n_constants.h"
13 #include "base/i18n/icu_string_conversions.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "chrome/browser/media_galleries/fileapi/file_path_watcher_util.h"
21 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
22 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
23 #include "chrome/common/media_galleries/itunes_library.h"
24 #include "storage/browser/fileapi/native_file_util.h"
25 #include "third_party/icu/source/common/unicode/locid.h"
31 // Colon and slash are not allowed in filenames, replace them with underscore.
32 std::string
SanitizeName(const std::string
& input
) {
34 base::ConvertToUtf8AndNormalize(input
, base::kCodepageUTF8
, &result
);
35 base::ReplaceChars(result
, ":/", "_", &result
);
39 ITunesDataProvider::Album
MakeUniqueTrackNames(const parser::Album
& album
) {
40 // TODO(vandebo): It would be nice to ensure that names returned from here
41 // are stable, but aside from persisting every name returned, it's not
42 // obvious how to do that (without including the track id in every name).
43 typedef std::set
<const parser::Track
*> TrackRefs
;
44 typedef std::map
<ITunesDataProvider::TrackName
, TrackRefs
> AlbumInfo
;
46 ITunesDataProvider::Album result
;
47 AlbumInfo duped_tracks
;
49 parser::Album::const_iterator album_it
;
50 for (album_it
= album
.begin(); album_it
!= album
.end(); ++album_it
) {
51 const parser::Track
& track
= *album_it
;
52 std::string name
= SanitizeName(track
.location
.BaseName().AsUTF8Unsafe());
53 duped_tracks
[name
].insert(&track
);
56 for (AlbumInfo::const_iterator name_it
= duped_tracks
.begin();
57 name_it
!= duped_tracks
.end();
59 const TrackRefs
& track_refs
= name_it
->second
;
60 if (track_refs
.size() == 1) {
61 result
[name_it
->first
] = (*track_refs
.begin())->location
;
63 for (TrackRefs::const_iterator track_it
= track_refs
.begin();
64 track_it
!= track_refs
.end();
66 base::FilePath track_file_name
= (*track_it
)->location
.BaseName();
68 base::StringPrintf(" (%" PRId64
")", (*track_it
)->id
);
69 std::string uniquified_track_name
=
70 track_file_name
.InsertBeforeExtensionASCII(id
).AsUTF8Unsafe();
71 std::string escaped_track_name
= SanitizeName(uniquified_track_name
);
72 result
[escaped_track_name
] = (*track_it
)->location
;
80 // |result_path| is set if |locale_string| maps to a localized directory name
81 // and it exists in the filesystem.
82 bool CheckLocaleStringAutoAddPath(
83 const base::FilePath
& media_path
,
84 const std::map
<std::string
, std::string
>& localized_dir_names
,
85 const std::string
& locale_string
,
86 base::FilePath
* result_path
) {
87 DCHECK(!media_path
.empty());
88 DCHECK(!localized_dir_names
.empty());
89 DCHECK(!locale_string
.empty());
92 std::map
<std::string
, std::string
>::const_iterator it
=
93 localized_dir_names
.find(locale_string
);
94 if (it
== localized_dir_names
.end())
97 base::FilePath localized_auto_add_path
=
98 media_path
.Append(base::FilePath::FromUTF8Unsafe(it
->second
));
99 if (!storage::NativeFileUtil::DirectoryExists(localized_auto_add_path
))
102 *result_path
= localized_auto_add_path
;
106 // This method is complex because Apple localizes the directory name in versions
107 // of iTunes before 10.6.
108 base::FilePath
GetAutoAddPath(const base::FilePath
& library_path
) {
109 const char kiTunesMediaDir
[] = "iTunes Media";
110 base::FilePath media_path
=
111 library_path
.DirName().AppendASCII(kiTunesMediaDir
);
113 // Test 'universal' path first.
114 base::FilePath universal_auto_add_path
=
115 media_path
.AppendASCII("Automatically Add to iTunes.localized");
116 if (storage::NativeFileUtil::DirectoryExists(universal_auto_add_path
))
117 return universal_auto_add_path
;
119 // Test user locale. Localized directory names encoded in UTF-8.
120 std::map
<std::string
, std::string
> localized_dir_names
;
121 localized_dir_names
["nl"] = "Voeg automatisch toe aan iTunes";
122 localized_dir_names
["en"] = "Automatically Add to iTunes";
123 localized_dir_names
["fr"] = "Ajouter automatiquement \xC3\xA0 iTunes";
124 localized_dir_names
["de"] = "Automatisch zu iTunes hinzuf\xC3\xBCgen";
125 localized_dir_names
["it"] = "Aggiungi automaticamente a iTunes";
126 localized_dir_names
["ja"] = "iTunes \xE3\x81\xAB\xE8\x87\xAA\xE5\x8B\x95\xE7"
127 "\x9A\x84\xE3\x81\xAB\xE8\xBF\xBD\xE5\x8A\xA0";
128 localized_dir_names
["es"] = "A\xC3\xB1""adir autom\xC3\xA1ticamente a iTunes";
129 localized_dir_names
["da"] = "F\xC3\xB8j automatisk til iTunes";
130 localized_dir_names
["en-GB"] = "Automatically Add to iTunes";
131 localized_dir_names
["fi"] = "Lis\xC3\xA4\xC3\xA4 automaattisesti iTunesiin";
132 localized_dir_names
["ko"] = "iTunes\xEC\x97\x90 \xEC\x9E\x90\xEB\x8F\x99\xEC"
133 "\x9C\xBC\xEB\xA1\x9C \xEC\xB6\x94\xEA\xB0\x80";
134 localized_dir_names
["no"] = "Legg til automatisk i iTunes";
135 localized_dir_names
["pl"] = "Automatycznie dodaj do iTunes";
136 localized_dir_names
["pt"] = "Adicionar Automaticamente ao iTunes";
137 localized_dir_names
["pt-PT"] = "Adicionar ao iTunes automaticamente";
138 localized_dir_names
["ru"] = "\xD0\x90\xD0\xB2\xD1\x82\xD0\xBE\xD0\xBC\xD0\xB0"
139 "\xD1\x82\xD0\xB8\xD1\x87\xD0\xB5\xD1\x81\xD0\xBA"
140 "\xD0\xB8 \xD0\xB4\xD0\xBE\xD0\xB1\xD0\xB0\xD0"
141 "\xB2\xD0\xBB\xD1\x8F\xD1\x82\xD1\x8C \xD0\xB2"
143 localized_dir_names
["sv"] = "L\xC3\xA4gg automatiskt till i iTunes";
144 localized_dir_names
["zh-CN"] = "\xE8\x87\xAA\xE5\x8A\xA8\xE6\xB7\xBB\xE5\x8A"
145 "\xA0\xE5\x88\xB0 iTunes";
146 localized_dir_names
["zh-TW"] = "\xE8\x87\xAA\xE5\x8B\x95\xE5\x8A\xA0\xE5\x85"
149 const icu::Locale locale
= icu::Locale::getDefault();
150 const char* language
= locale
.getLanguage();
151 const char* country
= locale
.getCountry();
153 base::FilePath result_path
;
154 if (language
!= NULL
&& *language
!= '\0') {
155 if (country
!= NULL
&& *country
!= '\0' &&
156 CheckLocaleStringAutoAddPath(media_path
, localized_dir_names
,
157 std::string(language
) + "-" + country
,
162 if (CheckLocaleStringAutoAddPath(media_path
, localized_dir_names
,
163 language
, &result_path
)) {
168 // Fallback to trying English.
169 if (CheckLocaleStringAutoAddPath(media_path
, localized_dir_names
,
170 "en", &result_path
)) {
174 return base::FilePath();
179 ITunesDataProvider::ITunesDataProvider(const base::FilePath
& library_path
)
180 : iapps::IAppsDataProvider(library_path
),
181 auto_add_path_(GetAutoAddPath(library_path
)),
182 weak_factory_(this) {}
184 ITunesDataProvider::~ITunesDataProvider() {}
186 void ITunesDataProvider::DoParseLibrary(
187 const base::FilePath
& library_path
,
188 const ReadyCallback
& ready_callback
) {
189 xml_parser_
= new iapps::SafeIAppsLibraryParser
;
190 xml_parser_
->ParseITunesLibrary(
192 base::Bind(&ITunesDataProvider::OnLibraryParsed
,
193 weak_factory_
.GetWeakPtr(),
197 const base::FilePath
& ITunesDataProvider::auto_add_path() const {
198 return auto_add_path_
;
201 bool ITunesDataProvider::KnownArtist(const ArtistName
& artist
) const {
202 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
204 return ContainsKey(library_
, artist
);
207 bool ITunesDataProvider::KnownAlbum(const ArtistName
& artist
,
208 const AlbumName
& album
) const {
209 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
211 Library::const_iterator library_it
= library_
.find(artist
);
212 if (library_it
== library_
.end())
214 return ContainsKey(library_it
->second
, album
);
217 base::FilePath
ITunesDataProvider::GetTrackLocation(
218 const ArtistName
& artist
, const AlbumName
& album
,
219 const TrackName
& track
) const {
220 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
222 Library::const_iterator library_it
= library_
.find(artist
);
223 if (library_it
== library_
.end())
224 return base::FilePath();
226 Artist::const_iterator artist_it
= library_it
->second
.find(album
);
227 if (artist_it
== library_it
->second
.end())
228 return base::FilePath();
230 Album::const_iterator album_it
= artist_it
->second
.find(track
);
231 if (album_it
== artist_it
->second
.end())
232 return base::FilePath();
233 return album_it
->second
;
236 std::set
<ITunesDataProvider::ArtistName
>
237 ITunesDataProvider::GetArtistNames() const {
238 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
240 std::set
<ArtistName
> result
;
241 Library::const_iterator it
;
242 for (it
= library_
.begin(); it
!= library_
.end(); ++it
) {
243 result
.insert(it
->first
);
248 std::set
<ITunesDataProvider::AlbumName
> ITunesDataProvider::GetAlbumNames(
249 const ArtistName
& artist
) const {
250 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
252 std::set
<AlbumName
> result
;
253 Library::const_iterator artist_lookup
= library_
.find(artist
);
254 if (artist_lookup
== library_
.end())
257 const Artist
& artist_entry
= artist_lookup
->second
;
258 Artist::const_iterator it
;
259 for (it
= artist_entry
.begin(); it
!= artist_entry
.end(); ++it
) {
260 result
.insert(it
->first
);
265 ITunesDataProvider::Album
ITunesDataProvider::GetAlbum(
266 const ArtistName
& artist
, const AlbumName
& album
) const {
267 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
270 Library::const_iterator artist_lookup
= library_
.find(artist
);
271 if (artist_lookup
!= library_
.end()) {
272 Artist::const_iterator album_lookup
= artist_lookup
->second
.find(album
);
273 if (album_lookup
!= artist_lookup
->second
.end())
274 result
= album_lookup
->second
;
279 void ITunesDataProvider::OnLibraryParsed(const ReadyCallback
& ready_callback
,
281 const parser::Library
& library
) {
282 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
286 for (parser::Library::const_iterator artist_it
= library
.begin();
287 artist_it
!= library
.end();
289 std::string artist_name
= SanitizeName(artist_it
->first
);
290 for (parser::Albums::const_iterator album_it
= artist_it
->second
.begin();
291 album_it
!= artist_it
->second
.end();
293 std::string album_name
= SanitizeName(album_it
->first
);
294 library_
[artist_name
][album_name
] =
295 MakeUniqueTrackNames(album_it
->second
);
299 ready_callback
.Run(valid());
302 } // namespace itunes