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/utility/media_galleries/itunes_library_parser.h"
9 #include "base/logging.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/utility/media_galleries/iapps_xml_utils.h"
14 #include "third_party/libxml/chromium/libxml_utils.h"
16 #include "url/url_canon.h"
17 #include "url/url_util.h"
25 base::FilePath location
;
30 class TrackInfoXmlDictReader
: public iapps::XmlDictReader
{
32 TrackInfoXmlDictReader(XmlReader
* reader
, TrackInfo
* track_info
) :
33 iapps::XmlDictReader(reader
), track_info_(track_info
) {}
35 bool ShouldLoop() override
{
36 return !(Found("Track ID") && Found("Location") &&
37 Found("Album Artist") && Found("Album"));
40 bool HandleKeyImpl(const std::string
& key
) override
{
41 if (key
== "Track ID") {
42 return iapps::ReadInteger(reader_
, &track_info_
->id
);
43 } else if (key
== "Location") {
45 if (!iapps::ReadString(reader_
, &value
))
48 if (!url
.SchemeIsFile())
50 url::RawCanonOutputW
<1024> decoded_location
;
51 url::DecodeURLEscapeSequences(url
.path().c_str() + 1, // Strip /.
52 url
.path().length() - 1,
55 base::string16
location(decoded_location
.data(),
56 decoded_location
.length());
58 base::string16
location16(decoded_location
.data(),
59 decoded_location
.length());
60 std::string location
= "/" + base::UTF16ToUTF8(location16
);
62 track_info_
->location
= base::FilePath(location
);
63 } else if (key
== "Artist") {
64 if (Found("Album Artist"))
66 return iapps::ReadString(reader_
, &track_info_
->artist
);
67 } else if (key
== "Album Artist") {
68 track_info_
->artist
.clear();
69 return iapps::ReadString(reader_
, &track_info_
->artist
);
70 } else if (key
== "Album") {
71 return iapps::ReadString(reader_
, &track_info_
->album
);
72 } else if (!SkipToNext()) {
78 bool FinishedOk() override
{ return Found("Track ID") && Found("Location"); }
81 TrackInfo
* track_info_
;
84 // Walk through a dictionary filling in |result| with track information. Return
85 // true if at least the id and location where found (artist and album may be
86 // empty). In either case, the cursor is advanced out of the dictionary.
87 bool GetTrackInfoFromDict(XmlReader
* reader
, TrackInfo
* result
) {
89 TrackInfoXmlDictReader
dict_reader(reader
, result
);
90 return dict_reader
.Read();
95 ITunesLibraryParser::ITunesLibraryParser() {}
96 ITunesLibraryParser::~ITunesLibraryParser() {}
98 bool ITunesLibraryParser::Parse(const std::string
& library_xml
) {
101 if (!reader
.Load(library_xml
))
104 // Find the plist node and then search within that tag.
105 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "plist"))
110 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "dict"))
113 if (!iapps::SeekInDict(&reader
, "Tracks"))
116 // Once inside the Tracks dict, we expect track dictionaries keyed by id. i.e.
121 // <key>Track ID</key><integer>160</integer>
122 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "dict"))
124 int tracks_dict_depth
= reader
.Depth() + 1;
128 // Once parsing has gotten this far, return whatever is found, even if
129 // some of the data isn't extracted just right.
130 bool no_errors
= true;
131 bool track_found
= false;
132 while (reader
.Depth() >= tracks_dict_depth
) {
133 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "key"))
135 std::string key
; // Should match track id below.
136 if (!reader
.ReadElementContent(&key
))
139 bool id_valid
= base::StringToUint64(key
, &id
);
140 if (!reader
.SkipToElement())
143 TrackInfo track_info
;
144 if (GetTrackInfoFromDict(&reader
, &track_info
) &&
146 id
== track_info
.id
) {
147 if (track_info
.artist
.empty())
148 track_info
.artist
= "Unknown Artist";
149 if (track_info
.album
.empty())
150 track_info
.album
= "Unknown Album";
151 parser::Track
track(track_info
.id
, track_info
.location
);
152 library_
[track_info
.artist
][track_info
.album
].insert(track
);
159 return track_found
|| no_errors
;
162 } // namespace itunes