forked from goonj/goonj
-
Notifications
You must be signed in to change notification settings - Fork 1
/
GXSPFPlaylist.m
182 lines (141 loc) · 5.39 KB
/
GXSPFPlaylist.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
File: GXSPFPlaylist.m
Description: XSPF playlist support (implementation).
This file is part of Goonj.
Goonj is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Goonj is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Goonj. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 Pratul Kalia.
Copyright 2009 Ankur Sethi.
*/
#import "GXSPFPlaylist.h"
@implementation GXSPFPlaylist
- (id) initWithFile:(NSString *)aURL
{
if (self = [super init]) {
trackList = [[NSMutableArray alloc] initWithCapacity:0];
aURL = [aURL stringByExpandingTildeInPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:aURL] == YES)
[self loadCollection:[aURL stringByExpandingTildeInPath]];
return self;
}
return nil;
}
- (void) addTrack:(GTrack *)track
{
[trackList addObject:track];
}
- (void) addTrack:(GTrack *)track atIndex:(NSUInteger)index
{
[trackList insertObject:track atIndex:index];
}
- (void) removeTrack:(GTrack *)track
{
[trackList removeObject:track];
}
- (void) removeTrackAtIndex:(NSUInteger)index
{
[trackList removeObjectAtIndex:index];
}
- (void) removeTracksAtIndexes:(NSIndexSet *)indexes
{
[trackList removeObjectsAtIndexes:indexes];
}
- (void) moveTrackFromIndex:(NSUInteger)initIndex toIndex:(NSUInteger)endIndex
{
id something = [trackList objectAtIndex:initIndex];
[trackList removeObjectAtIndex:initIndex];
[trackList insertObject:something atIndex:endIndex];
}
- (void) clearPlaylist
{
[trackList removeAllObjects];
}
- (NSUInteger) count
{
return [trackList count];
}
- (GTrack *) trackAtIndex:(NSUInteger)index
{
return [trackList objectAtIndex:index];
}
- (BOOL) isLocalCollection
{
return YES;
}
- (BOOL) saveCollectionAs:(NSString *)aURL
{
// TODO: save more metadata.
// Set up the namespace.
NSXMLNode *XSPFNamespace = [[NSXMLNode alloc] initWithKind:NSXMLNamespaceKind];
[XSPFNamespace setName:@""];
[XSPFNamespace setStringValue:@"http://xspf.org/ns/0/"];
// Why do I have to create a separate NSXMLNode for every single
// attribute? Meh.
NSXMLNode *XSPFVersion = [[NSXMLNode alloc] initWithKind:NSXMLAttributeKind];
[XSPFVersion setName:@"version"];
[XSPFVersion setStringValue:@"1.0"];
// Create root element.
NSXMLElement *XSPFRoot = [[NSXMLElement alloc] initWithName:@"playlist"];
[XSPFRoot addNamespace:XSPFNamespace];
[XSPFRoot addAttribute:XSPFVersion];
// Create the actual document.
NSXMLDocument *XSPFDoc = [[NSXMLDocument alloc] initWithRootElement:XSPFRoot];
[XSPFDoc setVersion:@"1.0"];
[XSPFDoc setCharacterEncoding:@"UTF-8"];
NSXMLElement *XSPFTrackList = [[NSXMLElement alloc] initWithName:@"trackList"];
[XSPFRoot addChild:XSPFTrackList];
NSXMLElement *XSPFTrack;
NSXMLElement *XSPFLocation;
NSURL *fileURL;
// It might be a good idea to check if the playlist is being saved in the
// same directory as the music files. If yes, then we can use relative
// paths instead of absolute paths.
for (GTrack *track in trackList) {
XSPFTrack = [[NSXMLElement alloc] initWithName:@"track"];
[XSPFTrackList addChild:XSPFTrack];
XSPFLocation = [[NSXMLElement alloc] initWithName:@"location"];
fileURL = [[NSURL alloc] initWithScheme:NSURLFileScheme host:@"" path:[track path]];
[XSPFLocation setStringValue:[[fileURL absoluteString]
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[XSPFTrack addChild:XSPFLocation];
}
// Write data to file. TODO: replace existing file when saving.
NSData *XMLData = [XSPFDoc XMLDataWithOptions:NSXMLNodePrettyPrint];
return [XMLData writeToFile:[aURL stringByExpandingTildeInPath] atomically:NO];
}
- (BOOL) loadCollection:(NSString *)aURL
{
NSError *err = nil;
NSXMLDocument *XSPFDoc = [[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:aURL]
options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA)
error:&err];
if (XSPFDoc == nil)
return NO;
// Loading the playlist was a success, so clear the old playlist.
[trackList removeAllObjects];
NSXMLElement *XSPFRoot = [XSPFDoc rootElement];
// There should be only ONE tracklist in a playlist, anyway.
// If there are more than one tracklists, the remaining lists are ignored.
NSXMLElement *XSPFTrackList = [[XSPFRoot elementsForName:@"trackList"] objectAtIndex:0];
NSArray *XSPFTracks = [XSPFTrackList elementsForName:@"track"];
GTrack *track;
NSString *unescapedString;
for (NSXMLElement *XSPFTrack in XSPFTracks) {
NSXMLElement *XSPFLocation = [[XSPFTrack elementsForName:@"location"] objectAtIndex:0];
// Need to call stringByReplacingPercentEscapesUsingEncoding twice. Insane, I tell you.
unescapedString = [[XSPFLocation stringValue] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
unescapedString = [unescapedString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
track = [[GTrack alloc] initWithFile:unescapedString];
[self addTrack:track];
}
return YES;
}
@end