- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / fileapi / itunes_data_provider.cc
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.
4
5 #include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
6
7 #include <map>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/format_macros.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/platform_file.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "chrome/browser/media_galleries/fileapi/file_path_watcher_util.h"
20 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
21 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
22 #include "chrome/common/media_galleries/itunes_library.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "third_party/icu/source/common/unicode/locid.h"
25 #include "webkit/browser/fileapi/native_file_util.h"
26
27 namespace itunes {
28
29 namespace {
30
31 // Colon and slash are not allowed in filenames, replace them with underscore.
32 std::string EscapeBadCharacters(const std::string& input) {
33   std::string result;
34   ReplaceChars(input, ":/", "_", &result);
35   return result;
36 }
37
38 ITunesDataProvider::Album MakeUniqueTrackNames(const parser::Album& album) {
39   // TODO(vandebo): It would be nice to ensure that names returned from here
40   // are stable, but aside from persisting every name returned, it's not
41   // obvious how to do that (without including the track id in every name).
42   typedef std::set<const parser::Track*> TrackRefs;
43   typedef std::map<ITunesDataProvider::TrackName, TrackRefs> AlbumInfo;
44
45   ITunesDataProvider::Album result;
46   AlbumInfo duped_tracks;
47
48   parser::Album::const_iterator album_it;
49   for (album_it = album.begin(); album_it != album.end(); ++album_it) {
50     const parser::Track& track = *album_it;
51     std::string name =
52         EscapeBadCharacters(track.location.BaseName().AsUTF8Unsafe());
53     duped_tracks[name].insert(&track);
54   }
55
56   for (AlbumInfo::const_iterator name_it = duped_tracks.begin();
57        name_it != duped_tracks.end();
58        ++name_it) {
59     const TrackRefs& track_refs = name_it->second;
60     if (track_refs.size() == 1) {
61       result[name_it->first] = (*track_refs.begin())->location;
62     } else {
63       for (TrackRefs::const_iterator track_it = track_refs.begin();
64            track_it != track_refs.end();
65            ++track_it) {
66         base::FilePath track_file_name = (*track_it)->location.BaseName();
67         std::string id =
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 =
72             EscapeBadCharacters(uniquified_track_name);
73         result[escaped_track_name] = (*track_it)->location;
74       }
75     }
76   }
77
78   return result;
79 }
80
81 // |result_path| is set if |locale_string| maps to a localized directory name
82 // and it exists in the filesystem.
83 bool CheckLocaleStringAutoAddPath(
84     const base::FilePath& media_path,
85     const std::map<std::string, std::string>& localized_dir_names,
86     const std::string& locale_string,
87     base::FilePath* result_path) {
88   DCHECK(!media_path.empty());
89   DCHECK(!localized_dir_names.empty());
90   DCHECK(!locale_string.empty());
91   DCHECK(result_path);
92
93   std::map<std::string, std::string>::const_iterator it =
94       localized_dir_names.find(locale_string);
95   if (it == localized_dir_names.end())
96     return false;
97
98   base::FilePath localized_auto_add_path =
99       media_path.Append(base::FilePath::FromUTF8Unsafe(it->second));
100   if (!fileapi::NativeFileUtil::DirectoryExists(localized_auto_add_path))
101     return false;
102
103   *result_path = localized_auto_add_path;
104   return true;
105 }
106
107 // This method is complex because Apple localizes the directory name in versions
108 // of iTunes before 10.6.
109 base::FilePath GetAutoAddPath(const base::FilePath& library_path) {
110   const char kiTunesMediaDir[] = "iTunes Media";
111   base::FilePath media_path =
112       library_path.DirName().AppendASCII(kiTunesMediaDir);
113
114   // Test 'universal' path first.
115   base::FilePath universal_auto_add_path =
116       media_path.AppendASCII("Automatically Add to iTunes.localized");
117   if (fileapi::NativeFileUtil::DirectoryExists(universal_auto_add_path))
118     return universal_auto_add_path;
119
120   // Test user locale. Localized directory names encoded in UTF-8.
121   std::map<std::string, std::string> localized_dir_names;
122   localized_dir_names["nl"] = "Voeg automatisch toe aan iTunes";
123   localized_dir_names["en"] = "Automatically Add to iTunes";
124   localized_dir_names["fr"] = "Ajouter automatiquement \xC3\xA0 iTunes";
125   localized_dir_names["de"] = "Automatisch zu iTunes hinzuf\xC3\xBCgen";
126   localized_dir_names["it"] = "Aggiungi automaticamente a iTunes";
127   localized_dir_names["ja"] = "iTunes \xE3\x81\xAB\xE8\x87\xAA\xE5\x8B\x95\xE7"
128                               "\x9A\x84\xE3\x81\xAB\xE8\xBF\xBD\xE5\x8A\xA0";
129   localized_dir_names["es"] = "A\xC3\xB1""adir autom\xC3\xA1ticamente a iTunes";
130   localized_dir_names["da"] = "F\xC3\xB8j automatisk til iTunes";
131   localized_dir_names["en-GB"] = "Automatically Add to iTunes";
132   localized_dir_names["fi"] = "Lis\xC3\xA4\xC3\xA4 automaattisesti iTunesiin";
133   localized_dir_names["ko"] = "iTunes\xEC\x97\x90 \xEC\x9E\x90\xEB\x8F\x99\xEC"
134                               "\x9C\xBC\xEB\xA1\x9C \xEC\xB6\x94\xEA\xB0\x80";
135   localized_dir_names["no"] = "Legg til automatisk i iTunes";
136   localized_dir_names["pl"] = "Automatycznie dodaj do iTunes";
137   localized_dir_names["pt"] = "Adicionar Automaticamente ao iTunes";
138   localized_dir_names["pt-PT"] = "Adicionar ao iTunes automaticamente";
139   localized_dir_names["ru"] = "\xD0\x90\xD0\xB2\xD1\x82\xD0\xBE\xD0\xBC\xD0\xB0"
140                               "\xD1\x82\xD0\xB8\xD1\x87\xD0\xB5\xD1\x81\xD0\xBA"
141                               "\xD0\xB8 \xD0\xB4\xD0\xBE\xD0\xB1\xD0\xB0\xD0"
142                               "\xB2\xD0\xBB\xD1\x8F\xD1\x82\xD1\x8C \xD0\xB2"
143                               "iTunes";
144   localized_dir_names["sv"] = "L\xC3\xA4gg automatiskt till i iTunes";
145   localized_dir_names["zh-CN"] = "\xE8\x87\xAA\xE5\x8A\xA8\xE6\xB7\xBB\xE5\x8A"
146                                  "\xA0\xE5\x88\xB0 iTunes";
147   localized_dir_names["zh-TW"] = "\xE8\x87\xAA\xE5\x8B\x95\xE5\x8A\xA0\xE5\x85"
148                                  "\xA5 iTunes";
149
150   const icu::Locale locale = icu::Locale::getDefault();
151   const char* language = locale.getLanguage();
152   const char* country = locale.getCountry();
153
154   base::FilePath result_path;
155   if (language != NULL && *language != '\0') {
156     if (country != NULL && *country != '\0' &&
157         CheckLocaleStringAutoAddPath(media_path, localized_dir_names,
158                                      std::string(language) + "-" + country,
159                                      &result_path)) {
160       return result_path;
161     }
162
163     if (CheckLocaleStringAutoAddPath(media_path, localized_dir_names,
164                                      language, &result_path)) {
165       return result_path;
166     }
167   }
168
169   // Fallback to trying English.
170   if (CheckLocaleStringAutoAddPath(media_path, localized_dir_names,
171                                    "en", &result_path)) {
172     return result_path;
173   }
174
175   return base::FilePath();
176 }
177
178 }  // namespace
179
180 ITunesDataProvider::ITunesDataProvider(const base::FilePath& library_path)
181     : iapps::IAppsDataProvider(library_path),
182       auto_add_path_(GetAutoAddPath(library_path)),
183       weak_factory_(this) {}
184
185 ITunesDataProvider::~ITunesDataProvider() {}
186
187 void ITunesDataProvider::DoParseLibrary(
188     const base::FilePath& library_path,
189     const ReadyCallback& ready_callback) {
190   xml_parser_ = new iapps::SafeIAppsLibraryParser;
191   xml_parser_->ParseITunesLibrary(
192       library_path,
193       base::Bind(&ITunesDataProvider::OnLibraryParsed,
194                  weak_factory_.GetWeakPtr(),
195                  ready_callback));
196 }
197
198 const base::FilePath& ITunesDataProvider::auto_add_path() const {
199   return auto_add_path_;
200 }
201
202 bool ITunesDataProvider::KnownArtist(const ArtistName& artist) const {
203   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
204   DCHECK(valid());
205   return ContainsKey(library_, artist);
206 }
207
208 bool ITunesDataProvider::KnownAlbum(const ArtistName& artist,
209                                     const AlbumName& album) const {
210   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
211   DCHECK(valid());
212   Library::const_iterator library_it = library_.find(artist);
213   if (library_it == library_.end())
214     return false;
215   return ContainsKey(library_it->second, album);
216 }
217
218 base::FilePath ITunesDataProvider::GetTrackLocation(
219     const ArtistName& artist, const AlbumName& album,
220     const TrackName& track) const {
221   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
222   DCHECK(valid());
223   Library::const_iterator library_it = library_.find(artist);
224   if (library_it == library_.end())
225     return base::FilePath();
226
227   Artist::const_iterator artist_it = library_it->second.find(album);
228   if (artist_it == library_it->second.end())
229     return base::FilePath();
230
231   Album::const_iterator album_it = artist_it->second.find(track);
232   if (album_it == artist_it->second.end())
233     return base::FilePath();
234   return album_it->second;
235 }
236
237 std::set<ITunesDataProvider::ArtistName>
238 ITunesDataProvider::GetArtistNames() const {
239   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
240   DCHECK(valid());
241   std::set<ArtistName> result;
242   Library::const_iterator it;
243   for (it = library_.begin(); it != library_.end(); ++it) {
244     result.insert(it->first);
245   }
246   return result;
247 }
248
249 std::set<ITunesDataProvider::AlbumName> ITunesDataProvider::GetAlbumNames(
250     const ArtistName& artist) const {
251   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
252   DCHECK(valid());
253   std::set<AlbumName> result;
254   Library::const_iterator artist_lookup = library_.find(artist);
255   if (artist_lookup == library_.end())
256     return result;
257
258   const Artist& artist_entry = artist_lookup->second;
259   Artist::const_iterator it;
260   for (it = artist_entry.begin(); it != artist_entry.end(); ++it) {
261     result.insert(it->first);
262   }
263   return result;
264 }
265
266 ITunesDataProvider::Album ITunesDataProvider::GetAlbum(
267     const ArtistName& artist, const AlbumName& album) const {
268   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
269   DCHECK(valid());
270   Album result;
271   Library::const_iterator artist_lookup = library_.find(artist);
272   if (artist_lookup != library_.end()) {
273     Artist::const_iterator album_lookup = artist_lookup->second.find(album);
274     if (album_lookup != artist_lookup->second.end())
275       result = album_lookup->second;
276   }
277   return result;
278 }
279
280 void ITunesDataProvider::OnLibraryParsed(const ReadyCallback& ready_callback,
281                                          bool result,
282                                          const parser::Library& library) {
283   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
284   set_valid(result);
285   if (valid()) {
286     library_.clear();
287     for (parser::Library::const_iterator artist_it = library.begin();
288          artist_it != library.end();
289          ++artist_it) {
290       std::string artist_name = EscapeBadCharacters(artist_it->first);
291       for (parser::Albums::const_iterator album_it = artist_it->second.begin();
292            album_it != artist_it->second.end();
293            ++album_it) {
294         std::string album_name = EscapeBadCharacters(album_it->first);
295         library_[artist_name][album_name] =
296             MakeUniqueTrackNames(album_it->second);
297       }
298     }
299   }
300   ready_callback.Run(valid());
301 }
302
303 }  // namespace itunes