Roll src/third_party/WebKit 4aace46:1b4a5ed (svn 202009:202010)
[chromium-blink-merge.git] / chrome / utility / media_galleries / itunes_library_parser.cc
blobbf23e6be51d03e9a77c8174631cbbfe185fb4f23
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"
7 #include <string>
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"
15 #include "url/gurl.h"
16 #include "url/url_canon.h"
17 #include "url/url_util.h"
19 namespace itunes {
21 namespace {
23 struct TrackInfo {
24 uint64 id;
25 base::FilePath location;
26 std::string artist;
27 std::string album;
30 class TrackInfoXmlDictReader : public iapps::XmlDictReader {
31 public:
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") {
44 std::string value;
45 if (!iapps::ReadString(reader_, &value))
46 return false;
47 GURL url(value);
48 if (!url.SchemeIsFile())
49 return false;
50 url::RawCanonOutputW<1024> decoded_location;
51 url::DecodeURLEscapeSequences(url.path().c_str() + 1, // Strip /.
52 url.path().length() - 1,
53 &decoded_location);
54 #if defined(OS_WIN)
55 base::string16 location(decoded_location.data(),
56 decoded_location.length());
57 #else
58 base::string16 location16(decoded_location.data(),
59 decoded_location.length());
60 std::string location = "/" + base::UTF16ToUTF8(location16);
61 #endif
62 track_info_->location = base::FilePath(location);
63 } else if (key == "Artist") {
64 if (Found("Album Artist"))
65 return false;
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()) {
73 return false;
75 return true;
78 bool FinishedOk() override { return Found("Track ID") && Found("Location"); }
80 private:
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) {
88 DCHECK(result);
89 TrackInfoXmlDictReader dict_reader(reader, result);
90 return dict_reader.Read();
93 } // namespace
95 ITunesLibraryParser::ITunesLibraryParser() {}
96 ITunesLibraryParser::~ITunesLibraryParser() {}
98 bool ITunesLibraryParser::Parse(const std::string& library_xml) {
99 XmlReader reader;
101 if (!reader.Load(library_xml))
102 return false;
104 // Find the plist node and then search within that tag.
105 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist"))
106 return false;
107 if (!reader.Read())
108 return false;
110 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
111 return false;
113 if (!iapps::SeekInDict(&reader, "Tracks"))
114 return false;
116 // Once inside the Tracks dict, we expect track dictionaries keyed by id. i.e.
117 // <key>Tracks</key>
118 // <dict>
119 // <key>160</key>
120 // <dict>
121 // <key>Track ID</key><integer>160</integer>
122 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
123 return false;
124 int tracks_dict_depth = reader.Depth() + 1;
125 if (!reader.Read())
126 return false;
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"))
134 return track_found;
135 std::string key; // Should match track id below.
136 if (!reader.ReadElementContent(&key))
137 return track_found;
138 uint64 id;
139 bool id_valid = base::StringToUint64(key, &id);
140 if (!reader.SkipToElement())
141 return track_found;
143 TrackInfo track_info;
144 if (GetTrackInfoFromDict(&reader, &track_info) &&
145 id_valid &&
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);
153 track_found = true;
154 } else {
155 no_errors = false;
159 return track_found || no_errors;
162 } // namespace itunes