Merge lp:~urbanape/ubuntuone-ios-music/chunked-downloads into lp:ubuntuone-ios-music

Proposed by Zachery Bir
Status: Merged
Approved by: Jason Foreman
Approved revision: 235
Merged at revision: 215
Proposed branch: lp:~urbanape/ubuntuone-ios-music/chunked-downloads
Merge into: lp:ubuntuone-ios-music
Diff against target: 4309 lines (+2621/-931)
58 files modified
Models/Album.h (+26/-0)
Models/Album.m (+114/-0)
Models/Artist.h (+26/-0)
Models/Artist.m (+114/-0)
Models/Generated/_Album.h (+122/-0)
Models/Generated/_Album.m (+86/-0)
Models/Generated/_Artist.h (+97/-0)
Models/Generated/_Artist.m (+79/-0)
Models/Generated/_Playlist.h (+79/-0)
Models/Generated/_Playlist.m (+68/-0)
Models/Generated/_PlaylistSongIndex.h (+79/-0)
Models/Generated/_PlaylistSongIndex.m (+81/-0)
Models/Generated/_Song.h (+327/-0)
Models/Generated/_Song.m (+298/-0)
Models/Playlist.h (+29/-0)
Models/Playlist.m (+130/-0)
Models/PlaylistSongIndex.h (+20/-0)
Models/PlaylistSongIndex.m (+22/-0)
Models/Song.h (+25/-0)
Models/Song.m (+96/-0)
U1Music-Info.plist (+1/-1)
U1Music.xcodeproj/project.pbxproj (+87/-23)
controls/AlbumArtistUITableViewCell.h (+3/-0)
controls/AlbumArtistUITableViewCell.m (+1/-1)
controls/AlbumUITableViewCell.h (+3/-0)
controls/AlbumUITableViewCell.m (+1/-1)
controls/ArtistUITableViewCell.h (+24/-0)
controls/ArtistUITableViewCell.m (+23/-0)
controls/PlaylistUITableViewCell.h (+23/-0)
controls/PlaylistUITableViewCell.m (+22/-0)
models/Album.h (+0/-58)
models/Album.m (+0/-131)
models/Artist.h (+0/-59)
models/Artist.m (+0/-135)
models/Playlist.h (+0/-47)
models/Playlist.m (+0/-136)
models/PlaylistSongIndex.h (+0/-32)
models/PlaylistSongIndex.m (+0/-27)
models/Song.h (+0/-67)
models/Song.m (+0/-113)
music.xcdatamodeld/.xccurrentversion (+1/-1)
utilities/AlbumParser.m (+1/-1)
utilities/ArtistParser.m (+1/-1)
utilities/Globals.h (+1/-0)
utilities/Globals.m (+6/-0)
utilities/PlaylistParser.m (+1/-1)
utilities/operations/Downloader.m (+117/-26)
utilities/operations/U1ChunkDownloadOperation.h (+24/-0)
utilities/operations/U1ChunkDownloadOperation.m (+68/-0)
utilities/operations/U1SerializedDownloadOperation.h (+22/-0)
utilities/operations/U1SerializedDownloadOperation.m (+57/-0)
view_controllers/AlbumListViewController.m (+33/-0)
view_controllers/AlbumViewController.m (+59/-59)
view_controllers/ArtistListViewController.m (+46/-5)
view_controllers/ArtistViewController.m (+34/-1)
view_controllers/PlaylistListViewController.m (+43/-2)
view_controllers/PlaylistViewController.m (+1/-1)
xibs/MainWindow.xib (+0/-2)
To merge this branch: bzr merge lp:~urbanape/ubuntuone-ios-music/chunked-downloads
Reviewer Review Type Date Requested Status
Jason Foreman (community) Approve
Review via email: mp+91498@code.launchpad.net

Description of the change

This branch provides for downloading files in chunks. This only replaces the downloader's functionality; it doesn't provide means to resume partial downloads.

To post a comment you must log in.
Revision history for this message
Zachery Bir (urbanape) wrote :

In an effort to allow this branch to land, it includes the single request code path as well, and uses it by default.

Revision history for this message
Jason Foreman (threeve) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'Models'
2=== added file 'Models/Album.h'
3--- Models/Album.h 1970-01-01 00:00:00 +0000
4+++ Models/Album.h 2012-02-03 20:23:18 +0000
5@@ -0,0 +1,26 @@
6+//
7+// Copyright 2012 Canonical Ltd.
8+//
9+// This program is free software: you can redistribute it and/or modify it
10+// under the terms of the GNU Affero General Public License version 3,
11+// as published by the Free Software Foundation.
12+//
13+// This program is distributed in the hope that it will be useful, but
14+// WITHOUT ANY WARRANTY; without even the implied warranties of
15+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16+// PURPOSE. See the GNU Affero General Public License for more details.
17+//
18+// You should have received a copy of the GNU Affero General Public License
19+// along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+#import "_Album.h"
22+
23+@interface Album : _Album {}
24++ (BOOL)albumWithIdExists:(NSString*)anAlbumId;
25++ (Album*)albumWithId:(NSString*)anAlbumId;
26++ (Album*)albumWithTitle:(NSString*)albumTitle artist:(NSString*)albumArtist;
27+- (BOOL)matchesSearchQuery:(NSString *)query;
28+- (NSError*)loadSongs;
29+- (NSString*)dearticlizedTitle;
30+- (BOOL)hasCachedSongs;
31+@end
32
33=== added file 'Models/Album.m'
34--- Models/Album.m 1970-01-01 00:00:00 +0000
35+++ Models/Album.m 2012-02-03 20:23:18 +0000
36@@ -0,0 +1,114 @@
37+//
38+// Copyright 2012 Canonical Ltd.
39+//
40+// This program is free software: you can redistribute it and/or modify it
41+// under the terms of the GNU Affero General Public License version 3,
42+// as published by the Free Software Foundation.
43+//
44+// This program is distributed in the hope that it will be useful, but
45+// WITHOUT ANY WARRANTY; without even the implied warranties of
46+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
47+// PURPOSE. See the GNU Affero General Public License for more details.
48+//
49+// You should have received a copy of the GNU Affero General Public License
50+// along with this program. If not, see <http://www.gnu.org/licenses/>.
51+
52+#import "Album.h"
53+#import "Artist.h"
54+#import "NSMutableArray+Extras.h"
55+#import "NSString+Extras.h"
56+#import "AlbumParser.h"
57+#import "Subsonic.h"
58+
59+@interface Album ()
60++ (Album*)albumMatchingPredicate:(NSPredicate*)predicate;
61+@end
62+
63+@implementation Album
64+
65++ (BOOL)albumWithIdExists:(NSString*)anAlbumId
66+{
67+ return (nil != [Album albumWithId:anAlbumId]);
68+}
69+
70++ (Album*)albumWithId:(NSString*)anAlbumId
71+{
72+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"albumId == %@", anAlbumId];
73+ return [self albumMatchingPredicate:predicate];
74+}
75+
76++ (Album*)albumWithTitle:(NSString*)albumTitle artist:(NSString*)albumArtist;
77+{
78+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"title == %@ AND artist = %@", albumTitle, albumArtist];
79+ return [self albumMatchingPredicate:predicate];
80+}
81+
82++ (Album*)albumMatchingPredicate:(NSPredicate*)predicate;
83+{
84+ NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
85+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Album" inManagedObjectContext:PerThreadManagedObjectContext()];
86+ [fetch setEntity:entityDescription];
87+ [fetch setPredicate:predicate];
88+ NSError * error = nil;
89+ NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
90+ [fetch release];
91+
92+ return [matches lastObject];
93+}
94+
95+- (NSString *)dearticlizedTitle
96+{
97+ return [self.title dearticlizedString];
98+}
99+
100+- (NSError*)loadSongs
101+{
102+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
103+ NSError *error = nil;
104+
105+ NSMutableArray *parameters = [NSMutableArray array];
106+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.albumId, nil]];
107+ NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getAlbum.view" parameters:parameters];
108+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
109+
110+ AlbumParser *parser = [[AlbumParser alloc] init];
111+ parser.albumObjectId = [self objectID];
112+
113+ [xmlParser setDelegate:parser];
114+
115+ if (![xmlParser parse])
116+ {
117+ error = [[xmlParser parserError] copy];
118+ }
119+
120+ [xmlParser release];
121+ [parser release];
122+ [pool release];
123+
124+ return [error autorelease];
125+}
126+
127+- (NSString*)description
128+{
129+ return [NSString stringWithFormat:@"%@ - %@ (%@) (art: %@) MOC %@",self.artist,self.title,self.albumId,self.coverArtId, [self managedObjectContext]];
130+}
131+
132+- (BOOL)matchesSearchQuery:(NSString *)query
133+{
134+ NSRange titleResultsRange = [self.title rangeOfString:query options:NSCaseInsensitiveSearch];
135+ return titleResultsRange.length > 0;
136+}
137+
138+- (BOOL)hasCachedSongs;
139+{
140+ NSManagedObjectContext *moc = [self managedObjectContext];
141+ NSEntityDescription *songEntity = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:moc];
142+ NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
143+ [fetch setEntity:songEntity];
144+ [fetch setPredicate:[NSPredicate predicateWithFormat:@"album = %@ and cachedSongPath != NIL", self.title]];
145+
146+ NSArray *results = [[self managedObjectContext] executeFetchRequest:fetch error:NULL];
147+ return [results count] > 0;
148+}
149+
150+@end
151
152=== added file 'Models/Artist.h'
153--- Models/Artist.h 1970-01-01 00:00:00 +0000
154+++ Models/Artist.h 2012-02-03 20:23:18 +0000
155@@ -0,0 +1,26 @@
156+//
157+// Copyright 2012 Canonical Ltd.
158+//
159+// This program is free software: you can redistribute it and/or modify it
160+// under the terms of the GNU Affero General Public License version 3,
161+// as published by the Free Software Foundation.
162+//
163+// This program is distributed in the hope that it will be useful, but
164+// WITHOUT ANY WARRANTY; without even the implied warranties of
165+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
166+// PURPOSE. See the GNU Affero General Public License for more details.
167+//
168+// You should have received a copy of the GNU Affero General Public License
169+// along with this program. If not, see <http://www.gnu.org/licenses/>.
170+
171+#import "_Artist.h"
172+
173+@interface Artist : _Artist {}
174++ (Artist*)artistWithId:(NSString*)anArtistId;
175++ (BOOL)artistWithIdExists:(NSString*)anArtistId;
176++ (Artist*)artistWithName:(NSString*)artistName;
177+- (BOOL)matchesSearchQuery:(NSString *)query;
178+- (NSString *)dearticlizedName;
179+- (NSError*)loadAlbums;
180+- (BOOL)hasCachedSongs;
181+@end
182
183=== added file 'Models/Artist.m'
184--- Models/Artist.m 1970-01-01 00:00:00 +0000
185+++ Models/Artist.m 2012-02-03 20:23:18 +0000
186@@ -0,0 +1,114 @@
187+//
188+// Copyright 2012 Canonical Ltd.
189+//
190+// This program is free software: you can redistribute it and/or modify it
191+// under the terms of the GNU Affero General Public License version 3,
192+// as published by the Free Software Foundation.
193+//
194+// This program is distributed in the hope that it will be useful, but
195+// WITHOUT ANY WARRANTY; without even the implied warranties of
196+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
197+// PURPOSE. See the GNU Affero General Public License for more details.
198+//
199+// You should have received a copy of the GNU Affero General Public License
200+// along with this program. If not, see <http://www.gnu.org/licenses/>.
201+
202+#import "Artist.h"
203+#import "Album.h"
204+#import "Subsonic.h"
205+#import "NSMutableArray+Extras.h"
206+#import "NSString+Extras.h"
207+#import "ArtistParser.h"
208+
209+@interface Artist ()
210++ (Artist*)artistMatchingPredicate:(NSPredicate*)predicate;
211+@end
212+
213+@implementation Artist
214+
215++ (BOOL)artistWithIdExists:(NSString*)anArtistId
216+{
217+ return [Artist artistWithId:anArtistId] != nil;
218+}
219+
220++ (Artist*)artistWithId:(NSString*)anArtistId
221+{
222+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artistId == %@", anArtistId];
223+ return [self artistMatchingPredicate:predicate];
224+}
225+
226++ (Artist*)artistWithName:(NSString*)artistName;
227+{
228+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", artistName];
229+ return [self artistMatchingPredicate:predicate];
230+}
231+
232++ (Artist*)artistMatchingPredicate:(NSPredicate*)predicate;
233+{
234+ NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
235+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Artist" inManagedObjectContext:PerThreadManagedObjectContext()];
236+ [fetch setEntity:entityDescription];
237+ [fetch setPredicate:predicate];
238+ NSError * error = nil;
239+ NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
240+ [fetch release];
241+
242+ return [matches lastObject];
243+}
244+
245+- (NSString*)description
246+{
247+ return [NSString stringWithFormat:@"%@ (%@) MOC %@",self.name, self.artistId, [self managedObjectContext]];
248+}
249+
250+- (BOOL)matchesSearchQuery:(NSString *)query
251+{
252+ NSRange titleResultsRange = [self.name rangeOfString:query options:NSCaseInsensitiveSearch];
253+ return titleResultsRange.length > 0;
254+}
255+
256+- (NSString *)dearticlizedName
257+{
258+ return [self.name dearticlizedString];
259+}
260+
261+// Note: executes synchronously!
262+- (NSError*)loadAlbums
263+{
264+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
265+ NSError *error = nil;
266+
267+ NSMutableArray *parameters = [NSMutableArray array];
268+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.artistId, nil]];
269+ NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getMusicDirectory.view" parameters:parameters];
270+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
271+ ArtistParser *parser = [[ArtistParser alloc] init];
272+ parser.artistManagedObjectID = [self objectID];
273+ [xmlParser setDelegate:parser];
274+
275+ if (![xmlParser parse])
276+ {
277+ error = [[xmlParser parserError] copy];
278+ }
279+
280+ [xmlParser release];
281+ [parser release];
282+ [pool release];
283+
284+ return [error autorelease];
285+}
286+
287+- (BOOL)hasCachedSongs;
288+{
289+ NSManagedObjectContext *moc = [self managedObjectContext];
290+ NSEntityDescription *songEntity = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:moc];
291+ NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
292+ [fetch setEntity:songEntity];
293+ [fetch setPredicate:[NSPredicate predicateWithFormat:@"artist = %@ and cachedSongPath != NIL", self.name]];
294+
295+ NSError *error = nil;
296+ NSArray *results = [moc executeFetchRequest:fetch error:&error];
297+ return [results count] > 0;
298+}
299+
300+@end
301
302=== renamed file 'models/CachedSongsPlaylist.h' => 'Models/CachedSongsPlaylist.h'
303=== renamed file 'models/CachedSongsPlaylist.m' => 'Models/CachedSongsPlaylist.m'
304=== added directory 'Models/Generated'
305=== added file 'Models/Generated/_Album.h'
306--- Models/Generated/_Album.h 1970-01-01 00:00:00 +0000
307+++ Models/Generated/_Album.h 2012-02-03 20:23:18 +0000
308@@ -0,0 +1,122 @@
309+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
310+// Make changes to Album.h instead.
311+
312+#import <CoreData/CoreData.h>
313+
314+
315+@class Artist;
316+@class Song;
317+
318+
319+
320+
321+
322+
323+@interface AlbumID : NSManagedObjectID {}
324+@end
325+
326+@interface _Album : NSManagedObject {}
327++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
328++ (NSString*)entityName;
329++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
330+- (AlbumID*)objectID;
331+
332+
333+
334+
335+@property (nonatomic, retain) NSString *albumId;
336+
337+
338+//- (BOOL)validateAlbumId:(id*)value_ error:(NSError**)error_;
339+
340+
341+
342+
343+@property (nonatomic, retain) NSString *artist;
344+
345+
346+//- (BOOL)validateArtist:(id*)value_ error:(NSError**)error_;
347+
348+
349+
350+
351+@property (nonatomic, retain) NSString *coverArtId;
352+
353+
354+//- (BOOL)validateCoverArtId:(id*)value_ error:(NSError**)error_;
355+
356+
357+
358+
359+@property (nonatomic, retain) NSString *title;
360+
361+
362+//- (BOOL)validateTitle:(id*)value_ error:(NSError**)error_;
363+
364+
365+
366+
367+
368+@property (nonatomic, retain) Artist* artistEntity;
369+
370+//- (BOOL)validateArtistEntity:(id*)value_ error:(NSError**)error_;
371+
372+
373+
374+
375+@property (nonatomic, retain) NSSet* songs;
376+
377+- (NSMutableSet*)songsSet;
378+
379+
380+
381+
382+@end
383+
384+@interface _Album (CoreDataGeneratedAccessors)
385+
386+- (void)addSongs:(NSSet*)value_;
387+- (void)removeSongs:(NSSet*)value_;
388+- (void)addSongsObject:(Song*)value_;
389+- (void)removeSongsObject:(Song*)value_;
390+
391+@end
392+
393+@interface _Album (CoreDataGeneratedPrimitiveAccessors)
394+
395+
396+- (NSString*)primitiveAlbumId;
397+- (void)setPrimitiveAlbumId:(NSString*)value;
398+
399+
400+
401+
402+- (NSString*)primitiveArtist;
403+- (void)setPrimitiveArtist:(NSString*)value;
404+
405+
406+
407+
408+- (NSString*)primitiveCoverArtId;
409+- (void)setPrimitiveCoverArtId:(NSString*)value;
410+
411+
412+
413+
414+- (NSString*)primitiveTitle;
415+- (void)setPrimitiveTitle:(NSString*)value;
416+
417+
418+
419+
420+
421+- (Artist*)primitiveArtistEntity;
422+- (void)setPrimitiveArtistEntity:(Artist*)value;
423+
424+
425+
426+- (NSMutableSet*)primitiveSongs;
427+- (void)setPrimitiveSongs:(NSMutableSet*)value;
428+
429+
430+@end
431
432=== added file 'Models/Generated/_Album.m'
433--- Models/Generated/_Album.m 1970-01-01 00:00:00 +0000
434+++ Models/Generated/_Album.m 2012-02-03 20:23:18 +0000
435@@ -0,0 +1,86 @@
436+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
437+// Make changes to Album.m instead.
438+
439+#import "_Album.h"
440+
441+@implementation AlbumID
442+@end
443+
444+@implementation _Album
445+
446++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
447+ NSParameterAssert(moc_);
448+ return [NSEntityDescription insertNewObjectForEntityForName:@"Album" inManagedObjectContext:moc_];
449+}
450+
451++ (NSString*)entityName {
452+ return @"Album";
453+}
454+
455++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
456+ NSParameterAssert(moc_);
457+ return [NSEntityDescription entityForName:@"Album" inManagedObjectContext:moc_];
458+}
459+
460+- (AlbumID*)objectID {
461+ return (AlbumID*)[super objectID];
462+}
463+
464++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
465+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
466+
467+
468+ return keyPaths;
469+}
470+
471+
472+
473+
474+@dynamic albumId;
475+
476+
477+
478+
479+
480+
481+@dynamic artist;
482+
483+
484+
485+
486+
487+
488+@dynamic coverArtId;
489+
490+
491+
492+
493+
494+
495+@dynamic title;
496+
497+
498+
499+
500+
501+
502+@dynamic artistEntity;
503+
504+
505+
506+@dynamic songs;
507+
508+
509+- (NSMutableSet*)songsSet {
510+ [self willAccessValueForKey:@"songs"];
511+ NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"songs"];
512+ [self didAccessValueForKey:@"songs"];
513+ return result;
514+}
515+
516+
517+
518+
519+
520+
521+@end
522
523=== added file 'Models/Generated/_Artist.h'
524--- Models/Generated/_Artist.h 1970-01-01 00:00:00 +0000
525+++ Models/Generated/_Artist.h 2012-02-03 20:23:18 +0000
526@@ -0,0 +1,97 @@
527+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
528+// Make changes to Artist.h instead.
529+
530+#import <CoreData/CoreData.h>
531+
532+
533+@class Album;
534+@class Song;
535+
536+
537+
538+
539+@interface ArtistID : NSManagedObjectID {}
540+@end
541+
542+@interface _Artist : NSManagedObject {}
543++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
544++ (NSString*)entityName;
545++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
546+- (ArtistID*)objectID;
547+
548+
549+
550+
551+@property (nonatomic, retain) NSString *artistId;
552+
553+
554+//- (BOOL)validateArtistId:(id*)value_ error:(NSError**)error_;
555+
556+
557+
558+
559+@property (nonatomic, retain) NSString *name;
560+
561+
562+//- (BOOL)validateName:(id*)value_ error:(NSError**)error_;
563+
564+
565+
566+
567+
568+@property (nonatomic, retain) NSSet* albums;
569+
570+- (NSMutableSet*)albumsSet;
571+
572+
573+
574+
575+@property (nonatomic, retain) NSSet* songs;
576+
577+- (NSMutableSet*)songsSet;
578+
579+
580+
581+
582+@end
583+
584+@interface _Artist (CoreDataGeneratedAccessors)
585+
586+- (void)addAlbums:(NSSet*)value_;
587+- (void)removeAlbums:(NSSet*)value_;
588+- (void)addAlbumsObject:(Album*)value_;
589+- (void)removeAlbumsObject:(Album*)value_;
590+
591+- (void)addSongs:(NSSet*)value_;
592+- (void)removeSongs:(NSSet*)value_;
593+- (void)addSongsObject:(Song*)value_;
594+- (void)removeSongsObject:(Song*)value_;
595+
596+@end
597+
598+@interface _Artist (CoreDataGeneratedPrimitiveAccessors)
599+
600+
601+- (NSString*)primitiveArtistId;
602+- (void)setPrimitiveArtistId:(NSString*)value;
603+
604+
605+
606+
607+- (NSString*)primitiveName;
608+- (void)setPrimitiveName:(NSString*)value;
609+
610+
611+
612+
613+
614+- (NSMutableSet*)primitiveAlbums;
615+- (void)setPrimitiveAlbums:(NSMutableSet*)value;
616+
617+
618+
619+- (NSMutableSet*)primitiveSongs;
620+- (void)setPrimitiveSongs:(NSMutableSet*)value;
621+
622+
623+@end
624
625=== added file 'Models/Generated/_Artist.m'
626--- Models/Generated/_Artist.m 1970-01-01 00:00:00 +0000
627+++ Models/Generated/_Artist.m 2012-02-03 20:23:18 +0000
628@@ -0,0 +1,79 @@
629+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
630+// Make changes to Artist.m instead.
631+
632+#import "_Artist.h"
633+
634+@implementation ArtistID
635+@end
636+
637+@implementation _Artist
638+
639++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
640+ NSParameterAssert(moc_);
641+ return [NSEntityDescription insertNewObjectForEntityForName:@"Artist" inManagedObjectContext:moc_];
642+}
643+
644++ (NSString*)entityName {
645+ return @"Artist";
646+}
647+
648++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
649+ NSParameterAssert(moc_);
650+ return [NSEntityDescription entityForName:@"Artist" inManagedObjectContext:moc_];
651+}
652+
653+- (ArtistID*)objectID {
654+ return (ArtistID*)[super objectID];
655+}
656+
657++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
658+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
659+
660+
661+ return keyPaths;
662+}
663+
664+
665+
666+
667+@dynamic artistId;
668+
669+
670+
671+
672+
673+
674+@dynamic name;
675+
676+
677+
678+
679+
680+
681+@dynamic albums;
682+
683+
684+- (NSMutableSet*)albumsSet {
685+ [self willAccessValueForKey:@"albums"];
686+ NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"albums"];
687+ [self didAccessValueForKey:@"albums"];
688+ return result;
689+}
690+
691+
692+@dynamic songs;
693+
694+
695+- (NSMutableSet*)songsSet {
696+ [self willAccessValueForKey:@"songs"];
697+ NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"songs"];
698+ [self didAccessValueForKey:@"songs"];
699+ return result;
700+}
701+
702+
703+
704+
705+
706+
707+@end
708
709=== added file 'Models/Generated/_Playlist.h'
710--- Models/Generated/_Playlist.h 1970-01-01 00:00:00 +0000
711+++ Models/Generated/_Playlist.h 2012-02-03 20:23:18 +0000
712@@ -0,0 +1,79 @@
713+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
714+// Make changes to Playlist.h instead.
715+
716+#import <CoreData/CoreData.h>
717+
718+
719+@class PlaylistSongIndex;
720+
721+
722+
723+
724+@interface PlaylistID : NSManagedObjectID {}
725+@end
726+
727+@interface _Playlist : NSManagedObject {}
728++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
729++ (NSString*)entityName;
730++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
731+- (PlaylistID*)objectID;
732+
733+
734+
735+
736+@property (nonatomic, retain) NSString *name;
737+
738+
739+//- (BOOL)validateName:(id*)value_ error:(NSError**)error_;
740+
741+
742+
743+
744+@property (nonatomic, retain) NSString *playlistId;
745+
746+
747+//- (BOOL)validatePlaylistId:(id*)value_ error:(NSError**)error_;
748+
749+
750+
751+
752+
753+@property (nonatomic, retain) NSSet* playlistSongIndexes;
754+
755+- (NSMutableSet*)playlistSongIndexesSet;
756+
757+
758+
759+
760+@end
761+
762+@interface _Playlist (CoreDataGeneratedAccessors)
763+
764+- (void)addPlaylistSongIndexes:(NSSet*)value_;
765+- (void)removePlaylistSongIndexes:(NSSet*)value_;
766+- (void)addPlaylistSongIndexesObject:(PlaylistSongIndex*)value_;
767+- (void)removePlaylistSongIndexesObject:(PlaylistSongIndex*)value_;
768+
769+@end
770+
771+@interface _Playlist (CoreDataGeneratedPrimitiveAccessors)
772+
773+
774+- (NSString*)primitiveName;
775+- (void)setPrimitiveName:(NSString*)value;
776+
777+
778+
779+
780+- (NSString*)primitivePlaylistId;
781+- (void)setPrimitivePlaylistId:(NSString*)value;
782+
783+
784+
785+
786+
787+- (NSMutableSet*)primitivePlaylistSongIndexes;
788+- (void)setPrimitivePlaylistSongIndexes:(NSMutableSet*)value;
789+
790+
791+@end
792
793=== added file 'Models/Generated/_Playlist.m'
794--- Models/Generated/_Playlist.m 1970-01-01 00:00:00 +0000
795+++ Models/Generated/_Playlist.m 2012-02-03 20:23:18 +0000
796@@ -0,0 +1,68 @@
797+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
798+// Make changes to Playlist.m instead.
799+
800+#import "_Playlist.h"
801+
802+@implementation PlaylistID
803+@end
804+
805+@implementation _Playlist
806+
807++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
808+ NSParameterAssert(moc_);
809+ return [NSEntityDescription insertNewObjectForEntityForName:@"Playlist" inManagedObjectContext:moc_];
810+}
811+
812++ (NSString*)entityName {
813+ return @"Playlist";
814+}
815+
816++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
817+ NSParameterAssert(moc_);
818+ return [NSEntityDescription entityForName:@"Playlist" inManagedObjectContext:moc_];
819+}
820+
821+- (PlaylistID*)objectID {
822+ return (PlaylistID*)[super objectID];
823+}
824+
825++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
826+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
827+
828+
829+ return keyPaths;
830+}
831+
832+
833+
834+
835+@dynamic name;
836+
837+
838+
839+
840+
841+
842+@dynamic playlistId;
843+
844+
845+
846+
847+
848+
849+@dynamic playlistSongIndexes;
850+
851+
852+- (NSMutableSet*)playlistSongIndexesSet {
853+ [self willAccessValueForKey:@"playlistSongIndexes"];
854+ NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"playlistSongIndexes"];
855+ [self didAccessValueForKey:@"playlistSongIndexes"];
856+ return result;
857+}
858+
859+
860+
861+
862+
863+
864+@end
865
866=== added file 'Models/Generated/_PlaylistSongIndex.h'
867--- Models/Generated/_PlaylistSongIndex.h 1970-01-01 00:00:00 +0000
868+++ Models/Generated/_PlaylistSongIndex.h 2012-02-03 20:23:18 +0000
869@@ -0,0 +1,79 @@
870+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
871+// Make changes to PlaylistSongIndex.h instead.
872+
873+#import <CoreData/CoreData.h>
874+
875+
876+@class Playlist;
877+@class Song;
878+
879+
880+
881+@interface PlaylistSongIndexID : NSManagedObjectID {}
882+@end
883+
884+@interface _PlaylistSongIndex : NSManagedObject {}
885++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
886++ (NSString*)entityName;
887++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
888+- (PlaylistSongIndexID*)objectID;
889+
890+
891+
892+
893+@property (nonatomic, retain) NSNumber *index;
894+
895+
896+@property int indexValue;
897+- (int)indexValue;
898+- (void)setIndexValue:(int)value_;
899+
900+//- (BOOL)validateIndex:(id*)value_ error:(NSError**)error_;
901+
902+
903+
904+
905+
906+@property (nonatomic, retain) Playlist* playlistEntity;
907+
908+//- (BOOL)validatePlaylistEntity:(id*)value_ error:(NSError**)error_;
909+
910+
911+
912+
913+@property (nonatomic, retain) Song* songEntity;
914+
915+//- (BOOL)validateSongEntity:(id*)value_ error:(NSError**)error_;
916+
917+
918+
919+
920+@end
921+
922+@interface _PlaylistSongIndex (CoreDataGeneratedAccessors)
923+
924+@end
925+
926+@interface _PlaylistSongIndex (CoreDataGeneratedPrimitiveAccessors)
927+
928+
929+- (NSNumber*)primitiveIndex;
930+- (void)setPrimitiveIndex:(NSNumber*)value;
931+
932+- (int)primitiveIndexValue;
933+- (void)setPrimitiveIndexValue:(int)value_;
934+
935+
936+
937+
938+
939+- (Playlist*)primitivePlaylistEntity;
940+- (void)setPrimitivePlaylistEntity:(Playlist*)value;
941+
942+
943+
944+- (Song*)primitiveSongEntity;
945+- (void)setPrimitiveSongEntity:(Song*)value;
946+
947+
948+@end
949
950=== added file 'Models/Generated/_PlaylistSongIndex.m'
951--- Models/Generated/_PlaylistSongIndex.m 1970-01-01 00:00:00 +0000
952+++ Models/Generated/_PlaylistSongIndex.m 2012-02-03 20:23:18 +0000
953@@ -0,0 +1,81 @@
954+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
955+// Make changes to PlaylistSongIndex.m instead.
956+
957+#import "_PlaylistSongIndex.h"
958+
959+@implementation PlaylistSongIndexID
960+@end
961+
962+@implementation _PlaylistSongIndex
963+
964++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
965+ NSParameterAssert(moc_);
966+ return [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistSongIndex" inManagedObjectContext:moc_];
967+}
968+
969++ (NSString*)entityName {
970+ return @"PlaylistSongIndex";
971+}
972+
973++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
974+ NSParameterAssert(moc_);
975+ return [NSEntityDescription entityForName:@"PlaylistSongIndex" inManagedObjectContext:moc_];
976+}
977+
978+- (PlaylistSongIndexID*)objectID {
979+ return (PlaylistSongIndexID*)[super objectID];
980+}
981+
982++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
983+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
984+
985+ if ([key isEqualToString:@"indexValue"]) {
986+ NSSet *affectingKey = [NSSet setWithObject:@"index"];
987+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
988+ }
989+
990+ return keyPaths;
991+}
992+
993+
994+
995+
996+@dynamic index;
997+
998+
999+
1000+- (int)indexValue {
1001+ NSNumber *result = [self index];
1002+ return [result intValue];
1003+}
1004+
1005+- (void)setIndexValue:(int)value_ {
1006+ [self setIndex:[NSNumber numberWithInt:value_]];
1007+}
1008+
1009+- (int)primitiveIndexValue {
1010+ NSNumber *result = [self primitiveIndex];
1011+ return [result intValue];
1012+}
1013+
1014+- (void)setPrimitiveIndexValue:(int)value_ {
1015+ [self setPrimitiveIndex:[NSNumber numberWithInt:value_]];
1016+}
1017+
1018+
1019+
1020+
1021+
1022+@dynamic playlistEntity;
1023+
1024+
1025+
1026+@dynamic songEntity;
1027+
1028+
1029+
1030+
1031+
1032+
1033+
1034+@end
1035
1036=== added file 'Models/Generated/_Song.h'
1037--- Models/Generated/_Song.h 1970-01-01 00:00:00 +0000
1038+++ Models/Generated/_Song.h 2012-02-03 20:23:18 +0000
1039@@ -0,0 +1,327 @@
1040+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
1041+// Make changes to Song.h instead.
1042+
1043+#import <CoreData/CoreData.h>
1044+
1045+
1046+@class Album;
1047+@class Artist;
1048+@class PlaylistSongIndex;
1049+
1050+
1051+
1052+
1053+
1054+
1055+
1056+
1057+
1058+
1059+
1060+
1061+
1062+
1063+
1064+
1065+@interface SongID : NSManagedObjectID {}
1066+@end
1067+
1068+@interface _Song : NSManagedObject {}
1069++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
1070++ (NSString*)entityName;
1071++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
1072+- (SongID*)objectID;
1073+
1074+
1075+
1076+
1077+@property (nonatomic, retain) NSString *album;
1078+
1079+
1080+//- (BOOL)validateAlbum:(id*)value_ error:(NSError**)error_;
1081+
1082+
1083+
1084+
1085+@property (nonatomic, retain) NSString *artist;
1086+
1087+
1088+//- (BOOL)validateArtist:(id*)value_ error:(NSError**)error_;
1089+
1090+
1091+
1092+
1093+@property (nonatomic, retain) NSNumber *bitRate;
1094+
1095+
1096+@property int bitRateValue;
1097+- (int)bitRateValue;
1098+- (void)setBitRateValue:(int)value_;
1099+
1100+//- (BOOL)validateBitRate:(id*)value_ error:(NSError**)error_;
1101+
1102+
1103+
1104+
1105+@property (nonatomic, retain) NSString *cachedSongPath;
1106+
1107+
1108+//- (BOOL)validateCachedSongPath:(id*)value_ error:(NSError**)error_;
1109+
1110+
1111+
1112+
1113+@property (nonatomic, retain) NSString *coverArtId;
1114+
1115+
1116+//- (BOOL)validateCoverArtId:(id*)value_ error:(NSError**)error_;
1117+
1118+
1119+
1120+
1121+@property (nonatomic, retain) NSNumber *discNumber;
1122+
1123+
1124+@property int discNumberValue;
1125+- (int)discNumberValue;
1126+- (void)setDiscNumberValue:(int)value_;
1127+
1128+//- (BOOL)validateDiscNumber:(id*)value_ error:(NSError**)error_;
1129+
1130+
1131+
1132+
1133+@property (nonatomic, retain) NSNumber *duration;
1134+
1135+
1136+@property int durationValue;
1137+- (int)durationValue;
1138+- (void)setDurationValue:(int)value_;
1139+
1140+//- (BOOL)validateDuration:(id*)value_ error:(NSError**)error_;
1141+
1142+
1143+
1144+
1145+@property (nonatomic, retain) NSString *genre;
1146+
1147+
1148+//- (BOOL)validateGenre:(id*)value_ error:(NSError**)error_;
1149+
1150+
1151+
1152+
1153+@property (nonatomic, retain) NSString *path;
1154+
1155+
1156+//- (BOOL)validatePath:(id*)value_ error:(NSError**)error_;
1157+
1158+
1159+
1160+
1161+@property (nonatomic, retain) NSNumber *size;
1162+
1163+
1164+@property int sizeValue;
1165+- (int)sizeValue;
1166+- (void)setSizeValue:(int)value_;
1167+
1168+//- (BOOL)validateSize:(id*)value_ error:(NSError**)error_;
1169+
1170+
1171+
1172+
1173+@property (nonatomic, retain) NSString *songId;
1174+
1175+
1176+//- (BOOL)validateSongId:(id*)value_ error:(NSError**)error_;
1177+
1178+
1179+
1180+
1181+@property (nonatomic, retain) NSString *title;
1182+
1183+
1184+//- (BOOL)validateTitle:(id*)value_ error:(NSError**)error_;
1185+
1186+
1187+
1188+
1189+@property (nonatomic, retain) NSNumber *track;
1190+
1191+
1192+@property int trackValue;
1193+- (int)trackValue;
1194+- (void)setTrackValue:(int)value_;
1195+
1196+//- (BOOL)validateTrack:(id*)value_ error:(NSError**)error_;
1197+
1198+
1199+
1200+
1201+@property (nonatomic, retain) NSNumber *year;
1202+
1203+
1204+@property int yearValue;
1205+- (int)yearValue;
1206+- (void)setYearValue:(int)value_;
1207+
1208+//- (BOOL)validateYear:(id*)value_ error:(NSError**)error_;
1209+
1210+
1211+
1212+
1213+
1214+@property (nonatomic, retain) Album* albumEntity;
1215+
1216+//- (BOOL)validateAlbumEntity:(id*)value_ error:(NSError**)error_;
1217+
1218+
1219+
1220+
1221+@property (nonatomic, retain) Artist* artistEntity;
1222+
1223+//- (BOOL)validateArtistEntity:(id*)value_ error:(NSError**)error_;
1224+
1225+
1226+
1227+
1228+@property (nonatomic, retain) NSSet* playlistSongIndexes;
1229+
1230+- (NSMutableSet*)playlistSongIndexesSet;
1231+
1232+
1233+
1234+
1235+@end
1236+
1237+@interface _Song (CoreDataGeneratedAccessors)
1238+
1239+- (void)addPlaylistSongIndexes:(NSSet*)value_;
1240+- (void)removePlaylistSongIndexes:(NSSet*)value_;
1241+- (void)addPlaylistSongIndexesObject:(PlaylistSongIndex*)value_;
1242+- (void)removePlaylistSongIndexesObject:(PlaylistSongIndex*)value_;
1243+
1244+@end
1245+
1246+@interface _Song (CoreDataGeneratedPrimitiveAccessors)
1247+
1248+
1249+- (NSString*)primitiveAlbum;
1250+- (void)setPrimitiveAlbum:(NSString*)value;
1251+
1252+
1253+
1254+
1255+- (NSString*)primitiveArtist;
1256+- (void)setPrimitiveArtist:(NSString*)value;
1257+
1258+
1259+
1260+
1261+- (NSNumber*)primitiveBitRate;
1262+- (void)setPrimitiveBitRate:(NSNumber*)value;
1263+
1264+- (int)primitiveBitRateValue;
1265+- (void)setPrimitiveBitRateValue:(int)value_;
1266+
1267+
1268+
1269+
1270+- (NSString*)primitiveCachedSongPath;
1271+- (void)setPrimitiveCachedSongPath:(NSString*)value;
1272+
1273+
1274+
1275+
1276+- (NSString*)primitiveCoverArtId;
1277+- (void)setPrimitiveCoverArtId:(NSString*)value;
1278+
1279+
1280+
1281+
1282+- (NSNumber*)primitiveDiscNumber;
1283+- (void)setPrimitiveDiscNumber:(NSNumber*)value;
1284+
1285+- (int)primitiveDiscNumberValue;
1286+- (void)setPrimitiveDiscNumberValue:(int)value_;
1287+
1288+
1289+
1290+
1291+- (NSNumber*)primitiveDuration;
1292+- (void)setPrimitiveDuration:(NSNumber*)value;
1293+
1294+- (int)primitiveDurationValue;
1295+- (void)setPrimitiveDurationValue:(int)value_;
1296+
1297+
1298+
1299+
1300+- (NSString*)primitiveGenre;
1301+- (void)setPrimitiveGenre:(NSString*)value;
1302+
1303+
1304+
1305+
1306+- (NSString*)primitivePath;
1307+- (void)setPrimitivePath:(NSString*)value;
1308+
1309+
1310+
1311+
1312+- (NSNumber*)primitiveSize;
1313+- (void)setPrimitiveSize:(NSNumber*)value;
1314+
1315+- (int)primitiveSizeValue;
1316+- (void)setPrimitiveSizeValue:(int)value_;
1317+
1318+
1319+
1320+
1321+- (NSString*)primitiveSongId;
1322+- (void)setPrimitiveSongId:(NSString*)value;
1323+
1324+
1325+
1326+
1327+- (NSString*)primitiveTitle;
1328+- (void)setPrimitiveTitle:(NSString*)value;
1329+
1330+
1331+
1332+
1333+- (NSNumber*)primitiveTrack;
1334+- (void)setPrimitiveTrack:(NSNumber*)value;
1335+
1336+- (int)primitiveTrackValue;
1337+- (void)setPrimitiveTrackValue:(int)value_;
1338+
1339+
1340+
1341+
1342+- (NSNumber*)primitiveYear;
1343+- (void)setPrimitiveYear:(NSNumber*)value;
1344+
1345+- (int)primitiveYearValue;
1346+- (void)setPrimitiveYearValue:(int)value_;
1347+
1348+
1349+
1350+
1351+
1352+- (Album*)primitiveAlbumEntity;
1353+- (void)setPrimitiveAlbumEntity:(Album*)value;
1354+
1355+
1356+
1357+- (Artist*)primitiveArtistEntity;
1358+- (void)setPrimitiveArtistEntity:(Artist*)value;
1359+
1360+
1361+
1362+- (NSMutableSet*)primitivePlaylistSongIndexes;
1363+- (void)setPrimitivePlaylistSongIndexes:(NSMutableSet*)value;
1364+
1365+
1366+@end
1367
1368=== added file 'Models/Generated/_Song.m'
1369--- Models/Generated/_Song.m 1970-01-01 00:00:00 +0000
1370+++ Models/Generated/_Song.m 2012-02-03 20:23:18 +0000
1371@@ -0,0 +1,298 @@
1372+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
1373+// Make changes to Song.m instead.
1374+
1375+#import "_Song.h"
1376+
1377+@implementation SongID
1378+@end
1379+
1380+@implementation _Song
1381+
1382++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
1383+ NSParameterAssert(moc_);
1384+ return [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:moc_];
1385+}
1386+
1387++ (NSString*)entityName {
1388+ return @"Song";
1389+}
1390+
1391++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
1392+ NSParameterAssert(moc_);
1393+ return [NSEntityDescription entityForName:@"Song" inManagedObjectContext:moc_];
1394+}
1395+
1396+- (SongID*)objectID {
1397+ return (SongID*)[super objectID];
1398+}
1399+
1400++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
1401+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
1402+
1403+ if ([key isEqualToString:@"bitRateValue"]) {
1404+ NSSet *affectingKey = [NSSet setWithObject:@"bitRate"];
1405+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1406+ }
1407+ if ([key isEqualToString:@"discNumberValue"]) {
1408+ NSSet *affectingKey = [NSSet setWithObject:@"discNumber"];
1409+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1410+ }
1411+ if ([key isEqualToString:@"durationValue"]) {
1412+ NSSet *affectingKey = [NSSet setWithObject:@"duration"];
1413+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1414+ }
1415+ if ([key isEqualToString:@"sizeValue"]) {
1416+ NSSet *affectingKey = [NSSet setWithObject:@"size"];
1417+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1418+ }
1419+ if ([key isEqualToString:@"trackValue"]) {
1420+ NSSet *affectingKey = [NSSet setWithObject:@"track"];
1421+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1422+ }
1423+ if ([key isEqualToString:@"yearValue"]) {
1424+ NSSet *affectingKey = [NSSet setWithObject:@"year"];
1425+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey];
1426+ }
1427+
1428+ return keyPaths;
1429+}
1430+
1431+
1432+
1433+
1434+@dynamic album;
1435+
1436+
1437+
1438+
1439+
1440+
1441+@dynamic artist;
1442+
1443+
1444+
1445+
1446+
1447+
1448+@dynamic bitRate;
1449+
1450+
1451+
1452+- (int)bitRateValue {
1453+ NSNumber *result = [self bitRate];
1454+ return [result intValue];
1455+}
1456+
1457+- (void)setBitRateValue:(int)value_ {
1458+ [self setBitRate:[NSNumber numberWithInt:value_]];
1459+}
1460+
1461+- (int)primitiveBitRateValue {
1462+ NSNumber *result = [self primitiveBitRate];
1463+ return [result intValue];
1464+}
1465+
1466+- (void)setPrimitiveBitRateValue:(int)value_ {
1467+ [self setPrimitiveBitRate:[NSNumber numberWithInt:value_]];
1468+}
1469+
1470+
1471+
1472+
1473+
1474+@dynamic cachedSongPath;
1475+
1476+
1477+
1478+
1479+
1480+
1481+@dynamic coverArtId;
1482+
1483+
1484+
1485+
1486+
1487+
1488+@dynamic discNumber;
1489+
1490+
1491+
1492+- (int)discNumberValue {
1493+ NSNumber *result = [self discNumber];
1494+ return [result intValue];
1495+}
1496+
1497+- (void)setDiscNumberValue:(int)value_ {
1498+ [self setDiscNumber:[NSNumber numberWithInt:value_]];
1499+}
1500+
1501+- (int)primitiveDiscNumberValue {
1502+ NSNumber *result = [self primitiveDiscNumber];
1503+ return [result intValue];
1504+}
1505+
1506+- (void)setPrimitiveDiscNumberValue:(int)value_ {
1507+ [self setPrimitiveDiscNumber:[NSNumber numberWithInt:value_]];
1508+}
1509+
1510+
1511+
1512+
1513+
1514+@dynamic duration;
1515+
1516+
1517+
1518+- (int)durationValue {
1519+ NSNumber *result = [self duration];
1520+ return [result intValue];
1521+}
1522+
1523+- (void)setDurationValue:(int)value_ {
1524+ [self setDuration:[NSNumber numberWithInt:value_]];
1525+}
1526+
1527+- (int)primitiveDurationValue {
1528+ NSNumber *result = [self primitiveDuration];
1529+ return [result intValue];
1530+}
1531+
1532+- (void)setPrimitiveDurationValue:(int)value_ {
1533+ [self setPrimitiveDuration:[NSNumber numberWithInt:value_]];
1534+}
1535+
1536+
1537+
1538+
1539+
1540+@dynamic genre;
1541+
1542+
1543+
1544+
1545+
1546+
1547+@dynamic path;
1548+
1549+
1550+
1551+
1552+
1553+
1554+@dynamic size;
1555+
1556+
1557+
1558+- (int)sizeValue {
1559+ NSNumber *result = [self size];
1560+ return [result intValue];
1561+}
1562+
1563+- (void)setSizeValue:(int)value_ {
1564+ [self setSize:[NSNumber numberWithInt:value_]];
1565+}
1566+
1567+- (int)primitiveSizeValue {
1568+ NSNumber *result = [self primitiveSize];
1569+ return [result intValue];
1570+}
1571+
1572+- (void)setPrimitiveSizeValue:(int)value_ {
1573+ [self setPrimitiveSize:[NSNumber numberWithInt:value_]];
1574+}
1575+
1576+
1577+
1578+
1579+
1580+@dynamic songId;
1581+
1582+
1583+
1584+
1585+
1586+
1587+@dynamic title;
1588+
1589+
1590+
1591+
1592+
1593+
1594+@dynamic track;
1595+
1596+
1597+
1598+- (int)trackValue {
1599+ NSNumber *result = [self track];
1600+ return [result intValue];
1601+}
1602+
1603+- (void)setTrackValue:(int)value_ {
1604+ [self setTrack:[NSNumber numberWithInt:value_]];
1605+}
1606+
1607+- (int)primitiveTrackValue {
1608+ NSNumber *result = [self primitiveTrack];
1609+ return [result intValue];
1610+}
1611+
1612+- (void)setPrimitiveTrackValue:(int)value_ {
1613+ [self setPrimitiveTrack:[NSNumber numberWithInt:value_]];
1614+}
1615+
1616+
1617+
1618+
1619+
1620+@dynamic year;
1621+
1622+
1623+
1624+- (int)yearValue {
1625+ NSNumber *result = [self year];
1626+ return [result intValue];
1627+}
1628+
1629+- (void)setYearValue:(int)value_ {
1630+ [self setYear:[NSNumber numberWithInt:value_]];
1631+}
1632+
1633+- (int)primitiveYearValue {
1634+ NSNumber *result = [self primitiveYear];
1635+ return [result intValue];
1636+}
1637+
1638+- (void)setPrimitiveYearValue:(int)value_ {
1639+ [self setPrimitiveYear:[NSNumber numberWithInt:value_]];
1640+}
1641+
1642+
1643+
1644+
1645+
1646+@dynamic albumEntity;
1647+
1648+
1649+
1650+@dynamic artistEntity;
1651+
1652+
1653+
1654+@dynamic playlistSongIndexes;
1655+
1656+
1657+- (NSMutableSet*)playlistSongIndexesSet {
1658+ [self willAccessValueForKey:@"playlistSongIndexes"];
1659+ NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"playlistSongIndexes"];
1660+ [self didAccessValueForKey:@"playlistSongIndexes"];
1661+ return result;
1662+}
1663+
1664+
1665+
1666+
1667+
1668+
1669+@end
1670
1671=== renamed file 'models/MOC.h' => 'Models/MOC.h'
1672=== renamed file 'models/MOC.m' => 'Models/MOC.m'
1673=== added file 'Models/Playlist.h'
1674--- Models/Playlist.h 1970-01-01 00:00:00 +0000
1675+++ Models/Playlist.h 2012-02-03 20:23:18 +0000
1676@@ -0,0 +1,29 @@
1677+//
1678+// Copyright 2012 Canonical Ltd.
1679+//
1680+// This program is free software: you can redistribute it and/or modify it
1681+// under the terms of the GNU Affero General Public License version 3,
1682+// as published by the Free Software Foundation.
1683+//
1684+// This program is distributed in the hope that it will be useful, but
1685+// WITHOUT ANY WARRANTY; without even the implied warranties of
1686+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1687+// PURPOSE. See the GNU Affero General Public License for more details.
1688+//
1689+// You should have received a copy of the GNU Affero General Public License
1690+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1691+
1692+#import "_Playlist.h"
1693+
1694+@class Song;
1695+
1696+@interface Playlist : _Playlist {}
1697++ (BOOL)playlistWithNameExists:(NSString*)aPlaylistName;
1698++ (Playlist*)playlistWithName:(NSString*)aPlaylistName;
1699+- (BOOL)matchesSearchQuery:(NSString *)query;
1700+- (NSError*)loadSongs;
1701+- (NSArray *)songs;
1702+- (BOOL)isEditable;
1703+- (void)addSong:(Song*)song;
1704+- (void)removeSong:(Song*)song;
1705+@end
1706
1707=== added file 'Models/Playlist.m'
1708--- Models/Playlist.m 1970-01-01 00:00:00 +0000
1709+++ Models/Playlist.m 2012-02-03 20:23:18 +0000
1710@@ -0,0 +1,130 @@
1711+//
1712+// Copyright 2012 Canonical Ltd.
1713+//
1714+// This program is free software: you can redistribute it and/or modify it
1715+// under the terms of the GNU Affero General Public License version 3,
1716+// as published by the Free Software Foundation.
1717+//
1718+// This program is distributed in the hope that it will be useful, but
1719+// WITHOUT ANY WARRANTY; without even the implied warranties of
1720+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1721+// PURPOSE. See the GNU Affero General Public License for more details.
1722+//
1723+// You should have received a copy of the GNU Affero General Public License
1724+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1725+
1726+#import "Playlist.h"
1727+#import "Subsonic.h"
1728+#import "NSMutableArray+Extras.h"
1729+#import "PlaylistParser.h"
1730+#import "PlaylistSongIndex.h"
1731+#import "Song.h"
1732+
1733+@implementation Playlist
1734+
1735++ (BOOL)playlistWithNameExists:(NSString *)aPlaylistName
1736+{
1737+ return (nil != [Playlist playlistWithName:aPlaylistName]);
1738+}
1739+
1740++ (Playlist *)playlistWithName:(NSString *)aPlaylistName
1741+{
1742+ NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
1743+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Playlist" inManagedObjectContext:PerThreadManagedObjectContext()];
1744+ [fetch setEntity:entityDescription];
1745+
1746+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", aPlaylistName];
1747+ [fetch setPredicate:predicate];
1748+
1749+ NSError * error = nil;
1750+ NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
1751+ [fetch release];
1752+
1753+ if ([matches count] > 0)
1754+ {
1755+ return [matches objectAtIndex:0];
1756+ }
1757+ else
1758+ {
1759+ return nil;
1760+ }
1761+}
1762+
1763+- (NSError*)loadSongs
1764+{
1765+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1766+ NSError *error = nil;
1767+
1768+ NSMutableArray *parameters = [NSMutableArray array];
1769+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.playlistId, nil]];
1770+ NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getPlaylist.view" parameters:parameters];
1771+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
1772+
1773+ PlaylistParser *parser = [[PlaylistParser alloc] init];
1774+ parser.playlistObjectId = [self objectID];
1775+
1776+ [xmlParser setDelegate:parser];
1777+
1778+ if (![xmlParser parse])
1779+ {
1780+ error = [[xmlParser parserError] copy];
1781+ }
1782+
1783+ [xmlParser release];
1784+ [parser release];
1785+ [pool release];
1786+
1787+ return [error autorelease];
1788+}
1789+
1790+- (NSArray *)songs
1791+{
1792+ // return playlistSongIndexes sorted by index
1793+ NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES];
1794+ NSArray *results = [[self.playlistSongIndexes sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] valueForKey:@"songEntity"];
1795+ return results;
1796+}
1797+
1798+// Do the crazy PlaylistSongIndex lookup/clobber dance inside these methods
1799+- (void)addSong:(Song *)song
1800+{
1801+ PlaylistSongIndex *playlistSongIndex = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistSongIndex" inManagedObjectContext:PerThreadManagedObjectContext()];
1802+ playlistSongIndex.playlistEntity = self;
1803+ playlistSongIndex.songEntity = song;
1804+ playlistSongIndex.index = [NSNumber numberWithUnsignedInt:[self.playlistSongIndexes count]];
1805+ SaveContext();
1806+}
1807+
1808+- (void)removeSong:(Song *)song
1809+{
1810+ // Look up (potentially) all records where playlistEntity is self, and songEntity is song, and blow them away
1811+ NSSet *condemned = [self.playlistSongIndexes filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"songEntity = %@", song]];
1812+ for (PlaylistSongIndex *playlistSongIndex in condemned)
1813+ {
1814+ [PerThreadManagedObjectContext() deleteObject:playlistSongIndex];
1815+ }
1816+ SaveContext();
1817+
1818+ [[self.playlistSongIndexes sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
1819+ ((PlaylistSongIndex *)obj).index = [NSNumber numberWithUnsignedInt:idx];
1820+ }];
1821+ SaveContext();
1822+}
1823+
1824+- (NSString*)description
1825+{
1826+ return [NSString stringWithFormat:@"%@ (%@) MOC %@", self.name, self.playlistId, [self managedObjectContext]];
1827+}
1828+
1829+- (BOOL)matchesSearchQuery:(NSString *)query
1830+{
1831+ NSRange titleResultsRange = [self.name rangeOfString:query options:NSCaseInsensitiveSearch];
1832+ return titleResultsRange.length > 0;
1833+}
1834+
1835+- (BOOL)isEditable;
1836+{
1837+ return YES;
1838+}
1839+
1840+@end
1841
1842=== added file 'Models/PlaylistSongIndex.h'
1843--- Models/PlaylistSongIndex.h 1970-01-01 00:00:00 +0000
1844+++ Models/PlaylistSongIndex.h 2012-02-03 20:23:18 +0000
1845@@ -0,0 +1,20 @@
1846+//
1847+// Copyright 2012 Canonical Ltd.
1848+//
1849+// This program is free software: you can redistribute it and/or modify it
1850+// under the terms of the GNU Affero General Public License version 3,
1851+// as published by the Free Software Foundation.
1852+//
1853+// This program is distributed in the hope that it will be useful, but
1854+// WITHOUT ANY WARRANTY; without even the implied warranties of
1855+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1856+// PURPOSE. See the GNU Affero General Public License for more details.
1857+//
1858+// You should have received a copy of the GNU Affero General Public License
1859+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1860+
1861+#import "_PlaylistSongIndex.h"
1862+
1863+@interface PlaylistSongIndex : _PlaylistSongIndex {}
1864+// Custom logic goes here.
1865+@end
1866
1867=== added file 'Models/PlaylistSongIndex.m'
1868--- Models/PlaylistSongIndex.m 1970-01-01 00:00:00 +0000
1869+++ Models/PlaylistSongIndex.m 2012-02-03 20:23:18 +0000
1870@@ -0,0 +1,22 @@
1871+//
1872+// Copyright 2012 Canonical Ltd.
1873+//
1874+// This program is free software: you can redistribute it and/or modify it
1875+// under the terms of the GNU Affero General Public License version 3,
1876+// as published by the Free Software Foundation.
1877+//
1878+// This program is distributed in the hope that it will be useful, but
1879+// WITHOUT ANY WARRANTY; without even the implied warranties of
1880+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1881+// PURPOSE. See the GNU Affero General Public License for more details.
1882+//
1883+// You should have received a copy of the GNU Affero General Public License
1884+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1885+
1886+#import "PlaylistSongIndex.h"
1887+
1888+@implementation PlaylistSongIndex
1889+
1890+// Custom logic goes here.
1891+
1892+@end
1893
1894=== added file 'Models/Song.h'
1895--- Models/Song.h 1970-01-01 00:00:00 +0000
1896+++ Models/Song.h 2012-02-03 20:23:18 +0000
1897@@ -0,0 +1,25 @@
1898+//
1899+// Copyright 2012 Canonical Ltd.
1900+//
1901+// This program is free software: you can redistribute it and/or modify it
1902+// under the terms of the GNU Affero General Public License version 3,
1903+// as published by the Free Software Foundation.
1904+//
1905+// This program is distributed in the hope that it will be useful, but
1906+// WITHOUT ANY WARRANTY; without even the implied warranties of
1907+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1908+// PURPOSE. See the GNU Affero General Public License for more details.
1909+//
1910+// You should have received a copy of the GNU Affero General Public License
1911+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1912+
1913+#import "_Song.h"
1914+
1915+@interface Song : _Song {}
1916++ (BOOL)songWithIdExists:(NSString*)aSongId;
1917++ (Song*)songWithId:(NSString*)aSongId;
1918+- (BOOL)matchesSearchQuery:(NSString *)query;
1919+- (NSString *)dearticlizedTitle;
1920+- (BOOL)cachedSongExists;
1921+- (NSURL*)cachedSongURL;
1922+@end
1923
1924=== added file 'Models/Song.m'
1925--- Models/Song.m 1970-01-01 00:00:00 +0000
1926+++ Models/Song.m 2012-02-03 20:23:18 +0000
1927@@ -0,0 +1,96 @@
1928+//
1929+// Copyright 2012 Canonical Ltd.
1930+//
1931+// This program is free software: you can redistribute it and/or modify it
1932+// under the terms of the GNU Affero General Public License version 3,
1933+// as published by the Free Software Foundation.
1934+//
1935+// This program is distributed in the hope that it will be useful, but
1936+// WITHOUT ANY WARRANTY; without even the implied warranties of
1937+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1938+// PURPOSE. See the GNU Affero General Public License for more details.
1939+//
1940+// You should have received a copy of the GNU Affero General Public License
1941+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1942+
1943+#import "Song.h"
1944+#import "Artist.h"
1945+#import "Album.h"
1946+#import "NSString+Extras.h"
1947+
1948+@implementation Song
1949+
1950++ (BOOL)songWithIdExists:(NSString*)aSongId
1951+{
1952+ return (nil != [Song songWithId:aSongId]);
1953+}
1954+
1955++ (Song*)songWithId:(NSString*)aSongId
1956+{
1957+ NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
1958+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:PerThreadManagedObjectContext()];
1959+ [fetch setEntity:entityDescription];
1960+
1961+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"songId == %@", aSongId];
1962+ [fetch setPredicate:predicate];
1963+
1964+ NSError * error = nil;
1965+ NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
1966+ [fetch release];
1967+
1968+ if ([matches count] > 0)
1969+ {
1970+ return [matches objectAtIndex:0];
1971+ }
1972+ else
1973+ {
1974+ return nil;
1975+ }
1976+}
1977+
1978+- (NSURL*)cachedSongURL
1979+{
1980+ return [CachedMusicDirectory() URLByAppendingPathComponent:self.songId];
1981+}
1982+
1983+- (BOOL)cachedSongExists
1984+{
1985+ return self.cachedSongPath != nil;
1986+}
1987+
1988+- (NSComparisonResult)compare:(Song*)s
1989+{
1990+ NSUInteger selfPathArray[] = {[self.discNumber integerValue], [self.track integerValue]};
1991+ NSUInteger otherPathArray[] = {[s.discNumber integerValue], [s.track integerValue]};
1992+
1993+ NSIndexPath *selfPath = [NSIndexPath indexPathWithIndexes:selfPathArray length:2];
1994+ NSIndexPath *otherPath = [NSIndexPath indexPathWithIndexes:otherPathArray length:2];
1995+
1996+ return [selfPath compare:otherPath];
1997+}
1998+
1999+- (NSString*)description
2000+{
2001+ NSArray *keys = [NSArray arrayWithObjects:@"title", @"songId", @"artist", @"album", @"genre", @"coverArtId", @"path", @"duration", @"bitRate", @"track", @"year", @"size", nil];
2002+
2003+ NSMutableString *desc = [NSMutableString string];
2004+
2005+ for (NSString *k in keys)
2006+ {
2007+ [desc appendFormat:@"%@:%@ :: ",k, [self valueForKey:k]];
2008+ }
2009+ return desc;
2010+}
2011+
2012+- (BOOL)matchesSearchQuery:(NSString *)query
2013+{
2014+ NSRange titleResultsRange = [self.title rangeOfString:query options:NSCaseInsensitiveSearch];
2015+ return titleResultsRange.length > 0;
2016+}
2017+
2018+- (NSString *)dearticlizedTitle
2019+{
2020+ return [self.title dearticlizedString];
2021+}
2022+
2023+@end
2024
2025=== modified file 'U1Music-Info.plist'
2026--- U1Music-Info.plist 2011-11-10 19:50:55 +0000
2027+++ U1Music-Info.plist 2012-02-03 20:23:18 +0000
2028@@ -39,7 +39,7 @@
2029 </dict>
2030 </array>
2031 <key>CFBundleVersion</key>
2032- <string>30</string>
2033+ <string>31</string>
2034 <key>LSRequiresIPhoneOS</key>
2035 <false/>
2036 <key>NSMainNibFile</key>
2037
2038=== modified file 'U1Music.xcodeproj/project.pbxproj'
2039--- U1Music.xcodeproj/project.pbxproj 2011-11-03 18:33:32 +0000
2040+++ U1Music.xcodeproj/project.pbxproj 2012-02-03 20:23:18 +0000
2041@@ -3,7 +3,7 @@
2042 archiveVersion = 1;
2043 classes = {
2044 };
2045- objectVersion = 45;
2046+ objectVersion = 46;
2047 objects = {
2048
2049 /* Begin PBXBuildFile section */
2050@@ -57,15 +57,26 @@
2051 91406E9813849F2400A7DA67 /* uncached.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9213849F2400A7DA67 /* uncached.png */; };
2052 91406E9913849F2400A7DA67 /* uncached@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9313849F2400A7DA67 /* uncached@2x.png */; };
2053 91406EEA138AEEE000A7DA67 /* NSString+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 91406EE9138AEEE000A7DA67 /* NSString+Extras.m */; };
2054+ 914AC3FF14B61D8A00C7D5A3 /* CachedSongsPlaylist.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3F114B61D8A00C7D5A3 /* CachedSongsPlaylist.m */; };
2055+ 914AC40014B61D8A00C7D5A3 /* _Album.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3F414B61D8A00C7D5A3 /* _Album.m */; };
2056+ 914AC40114B61D8A00C7D5A3 /* _Artist.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3F614B61D8A00C7D5A3 /* _Artist.m */; };
2057+ 914AC40214B61D8A00C7D5A3 /* _Playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3F814B61D8A00C7D5A3 /* _Playlist.m */; };
2058+ 914AC40314B61D8A00C7D5A3 /* _PlaylistSongIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3FA14B61D8A00C7D5A3 /* _PlaylistSongIndex.m */; };
2059+ 914AC40414B61D8A00C7D5A3 /* _Song.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3FC14B61D8A00C7D5A3 /* _Song.m */; };
2060+ 914AC40514B61D8A00C7D5A3 /* PlaylistSongIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = 914AC3FE14B61D8A00C7D5A3 /* PlaylistSongIndex.m */; };
2061 919376F3135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 919376F2135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m */; };
2062 91B3A4441344CF92006C8193 /* SubsonicIndexedTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 91B3A4431344CF91006C8193 /* SubsonicIndexedTableViewController.m */; };
2063 91BE4018138E8D2300D44D68 /* UIActionSheet+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE4015138E8D2300D44D68 /* UIActionSheet+Blocks.m */; };
2064 91BE4019138E8D2300D44D68 /* UIAlertView+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE4017138E8D2300D44D68 /* UIAlertView+Blocks.m */; };
2065 91BE401C138E8D3A00D44D68 /* RIButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */; };
2066+ 91C8CCAF14B7883600A0E311 /* ArtistUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C8CCAE14B7883600A0E311 /* ArtistUITableViewCell.m */; };
2067 91D18DAB13B228D3001BEB42 /* uncached-disabled.png in Resources */ = {isa = PBXBuildFile; fileRef = 91D18DA913B228D3001BEB42 /* uncached-disabled.png */; };
2068 91D18DAC13B228D3001BEB42 /* uncached-disabled@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91D18DAA13B228D3001BEB42 /* uncached-disabled@2x.png */; };
2069+ 91D3A16F14B93B2A003D4FC0 /* MOC.m in Sources */ = {isa = PBXBuildFile; fileRef = 91D3A16E14B93B2A003D4FC0 /* MOC.m */; };
2070+ 91D3A17214BE12FE003D4FC0 /* PlaylistUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 91D3A17114BE12FE003D4FC0 /* PlaylistUITableViewCell.m */; };
2071 91DB6B9F13E9C7190029BC77 /* about_logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 91DB6B9E13E9C7190029BC77 /* about_logo.png */; };
2072- 91E0778913ABAD6200AA7CB2 /* PlaylistSongIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E0778813ABAD6200AA7CB2 /* PlaylistSongIndex.m */; };
2073+ 91E3E46414D33A2D006ED32F /* U1SerializedDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E3E46314D33A2D006ED32F /* U1SerializedDownloadOperation.m */; };
2074+ 91E3E46814D33A6F006ED32F /* U1ChunkDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E3E46714D33A6F006ED32F /* U1ChunkDownloadOperation.m */; };
2075 91E88609132DA82000618994 /* PlaylistParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E88608132DA82000618994 /* PlaylistParser.m */; };
2076 9316628812264A74003B0EB7 /* NSDate+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 9316628712264A74003B0EB7 /* NSDate+Extras.m */; };
2077 932E7A6E1254747E00E7C8FF /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 932E7A6D1254747E00E7C8FF /* Default@2x.png */; };
2078@@ -106,7 +117,6 @@
2079 93D6B5151252CB34007880B0 /* music_57@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93D6B5141252CB34007880B0 /* music_57@2x.png */; };
2080 93D6B54B1252CE57007880B0 /* URLQueryStringParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D6B54A1252CE57007880B0 /* URLQueryStringParser.m */; };
2081 93DFFE3F135D70B60061F29F /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93DFFE3E135D70B60061F29F /* CoreData.framework */; };
2082- 93DFFE45135D71030061F29F /* MOC.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE44135D71030061F29F /* MOC.m */; };
2083 93DFFE4D135D71760061F29F /* music.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE4C135D71760061F29F /* music.xcdatamodeld */; };
2084 93DFFE54135D72420061F29F /* NSManagedObjectContext+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE53135D72420061F29F /* NSManagedObjectContext+Additions.m */; };
2085 93EE2AF0124993F100E7E060 /* ArtistListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 93EE2AEF124993F100E7E060 /* ArtistListParser.m */; };
2086@@ -142,7 +152,6 @@
2087 93FA43A7124DEE0E0080DF62 /* whitetrack.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA43A4124DEE0E0080DF62 /* whitetrack.png */; };
2088 93FA43B5124DF07C0080DF62 /* player_overlay_bg.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA43B3124DF07C0080DF62 /* player_overlay_bg.png */; };
2089 93FA43B6124DF07C0080DF62 /* player_overlay_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA43B4124DF07C0080DF62 /* player_overlay_bg@2x.png */; };
2090- 9646DB7D13A8FF4700CB42D2 /* CachedSongsPlaylist.m in Sources */ = {isa = PBXBuildFile; fileRef = 9646DB7C13A8FF4700CB42D2 /* CachedSongsPlaylist.m */; };
2091 964FA3C213CA5C4F0018A65B /* JSONKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 964FA39513CA5C040018A65B /* JSONKit.m */; };
2092 964FA3C313CA5C4F0018A65B /* NSMutableURLRequest+Parameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 964FA39A13CA5C040018A65B /* NSMutableURLRequest+Parameters.m */; };
2093 964FA3C413CA5C4F0018A65B /* NSString+URLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 964FA39C13CA5C040018A65B /* NSString+URLEncoding.m */; };
2094@@ -248,6 +257,21 @@
2095 91406E9313849F2400A7DA67 /* uncached@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "uncached@2x.png"; sourceTree = "<group>"; };
2096 91406EE8138AEEE000A7DA67 /* NSString+Extras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Extras.h"; sourceTree = "<group>"; };
2097 91406EE9138AEEE000A7DA67 /* NSString+Extras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Extras.m"; sourceTree = "<group>"; };
2098+ 914AC3EE14B60ECC00C7D5A3 /* U1Music 2.2-31.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "U1Music 2.2-31.xcdatamodel"; sourceTree = "<group>"; };
2099+ 914AC3F014B61D8A00C7D5A3 /* CachedSongsPlaylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CachedSongsPlaylist.h; path = Models/CachedSongsPlaylist.h; sourceTree = SOURCE_ROOT; };
2100+ 914AC3F114B61D8A00C7D5A3 /* CachedSongsPlaylist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CachedSongsPlaylist.m; path = Models/CachedSongsPlaylist.m; sourceTree = SOURCE_ROOT; };
2101+ 914AC3F314B61D8A00C7D5A3 /* _Album.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _Album.h; sourceTree = "<group>"; };
2102+ 914AC3F414B61D8A00C7D5A3 /* _Album.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _Album.m; sourceTree = "<group>"; };
2103+ 914AC3F514B61D8A00C7D5A3 /* _Artist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _Artist.h; sourceTree = "<group>"; };
2104+ 914AC3F614B61D8A00C7D5A3 /* _Artist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _Artist.m; sourceTree = "<group>"; };
2105+ 914AC3F714B61D8A00C7D5A3 /* _Playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _Playlist.h; sourceTree = "<group>"; };
2106+ 914AC3F814B61D8A00C7D5A3 /* _Playlist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _Playlist.m; sourceTree = "<group>"; };
2107+ 914AC3F914B61D8A00C7D5A3 /* _PlaylistSongIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _PlaylistSongIndex.h; sourceTree = "<group>"; };
2108+ 914AC3FA14B61D8A00C7D5A3 /* _PlaylistSongIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _PlaylistSongIndex.m; sourceTree = "<group>"; };
2109+ 914AC3FB14B61D8A00C7D5A3 /* _Song.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _Song.h; sourceTree = "<group>"; };
2110+ 914AC3FC14B61D8A00C7D5A3 /* _Song.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _Song.m; sourceTree = "<group>"; };
2111+ 914AC3FD14B61D8A00C7D5A3 /* PlaylistSongIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistSongIndex.h; path = Models/PlaylistSongIndex.h; sourceTree = SOURCE_ROOT; };
2112+ 914AC3FE14B61D8A00C7D5A3 /* PlaylistSongIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlaylistSongIndex.m; path = Models/PlaylistSongIndex.m; sourceTree = SOURCE_ROOT; };
2113 919376F1135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistEditSongUITableViewCell.h; sourceTree = "<group>"; };
2114 919376F2135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistEditSongUITableViewCell.m; sourceTree = "<group>"; };
2115 91B3A4421344CF90006C8193 /* SubsonicIndexedTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubsonicIndexedTableViewController.h; sourceTree = "<group>"; };
2116@@ -258,11 +282,19 @@
2117 91BE4017138E8D2300D44D68 /* UIAlertView+Blocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+Blocks.m"; sourceTree = "<group>"; };
2118 91BE401A138E8D3A00D44D68 /* RIButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIButtonItem.h; sourceTree = "<group>"; };
2119 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIButtonItem.m; sourceTree = "<group>"; };
2120+ 91C8CCAD14B7883600A0E311 /* ArtistUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArtistUITableViewCell.h; sourceTree = "<group>"; };
2121+ 91C8CCAE14B7883600A0E311 /* ArtistUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArtistUITableViewCell.m; sourceTree = "<group>"; };
2122 91D18DA913B228D3001BEB42 /* uncached-disabled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "uncached-disabled.png"; sourceTree = "<group>"; };
2123 91D18DAA13B228D3001BEB42 /* uncached-disabled@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "uncached-disabled@2x.png"; sourceTree = "<group>"; };
2124+ 91D3A16D14B93B2A003D4FC0 /* MOC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOC.h; path = Models/MOC.h; sourceTree = SOURCE_ROOT; };
2125+ 91D3A16E14B93B2A003D4FC0 /* MOC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOC.m; path = Models/MOC.m; sourceTree = SOURCE_ROOT; };
2126+ 91D3A17014BE12FE003D4FC0 /* PlaylistUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistUITableViewCell.h; sourceTree = "<group>"; };
2127+ 91D3A17114BE12FE003D4FC0 /* PlaylistUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistUITableViewCell.m; sourceTree = "<group>"; };
2128 91DB6B9E13E9C7190029BC77 /* about_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = about_logo.png; sourceTree = "<group>"; };
2129- 91E0778713ABAD6100AA7CB2 /* PlaylistSongIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistSongIndex.h; sourceTree = "<group>"; };
2130- 91E0778813ABAD6200AA7CB2 /* PlaylistSongIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistSongIndex.m; sourceTree = "<group>"; };
2131+ 91E3E46214D33A2D006ED32F /* U1SerializedDownloadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1SerializedDownloadOperation.h; sourceTree = "<group>"; };
2132+ 91E3E46314D33A2D006ED32F /* U1SerializedDownloadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1SerializedDownloadOperation.m; sourceTree = "<group>"; };
2133+ 91E3E46614D33A6F006ED32F /* U1ChunkDownloadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1ChunkDownloadOperation.h; sourceTree = "<group>"; };
2134+ 91E3E46714D33A6F006ED32F /* U1ChunkDownloadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1ChunkDownloadOperation.m; sourceTree = "<group>"; };
2135 91E88607132DA82000618994 /* PlaylistParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistParser.h; sourceTree = "<group>"; };
2136 91E88608132DA82000618994 /* PlaylistParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistParser.m; sourceTree = "<group>"; };
2137 9316628612264A74003B0EB7 /* NSDate+Extras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+Extras.h"; sourceTree = "<group>"; };
2138@@ -326,8 +358,6 @@
2139 93D6B5491252CE57007880B0 /* URLQueryStringParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLQueryStringParser.h; sourceTree = "<group>"; };
2140 93D6B54A1252CE57007880B0 /* URLQueryStringParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLQueryStringParser.m; sourceTree = "<group>"; };
2141 93DFFE3E135D70B60061F29F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
2142- 93DFFE43135D71030061F29F /* MOC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOC.h; sourceTree = "<group>"; };
2143- 93DFFE44135D71030061F29F /* MOC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOC.m; sourceTree = "<group>"; };
2144 93DFFE48135D71550061F29F /* music.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = music.xcdatamodel; sourceTree = "<group>"; };
2145 93DFFE52135D72420061F29F /* NSManagedObjectContext+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectContext+Additions.h"; sourceTree = "<group>"; };
2146 93DFFE53135D72420061F29F /* NSManagedObjectContext+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectContext+Additions.m"; sourceTree = "<group>"; };
2147@@ -381,8 +411,6 @@
2148 93FA43A4124DEE0E0080DF62 /* whitetrack.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = whitetrack.png; sourceTree = "<group>"; };
2149 93FA43B3124DF07C0080DF62 /* player_overlay_bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = player_overlay_bg.png; sourceTree = "<group>"; };
2150 93FA43B4124DF07C0080DF62 /* player_overlay_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "player_overlay_bg@2x.png"; sourceTree = "<group>"; };
2151- 9646DB7B13A8FF4700CB42D2 /* CachedSongsPlaylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedSongsPlaylist.h; sourceTree = "<group>"; };
2152- 9646DB7C13A8FF4700CB42D2 /* CachedSongsPlaylist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CachedSongsPlaylist.m; sourceTree = "<group>"; };
2153 964FA39313CA5C040018A65B /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = CHANGELOG.md; sourceTree = "<group>"; };
2154 964FA39413CA5C040018A65B /* JSONKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONKit.h; sourceTree = "<group>"; };
2155 964FA39513CA5C040018A65B /* JSONKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSONKit.m; sourceTree = "<group>"; };
2156@@ -641,6 +669,24 @@
2157 path = "TestFlight SDK";
2158 sourceTree = "<group>";
2159 };
2160+ 914AC3F214B61D8A00C7D5A3 /* Generated */ = {
2161+ isa = PBXGroup;
2162+ children = (
2163+ 914AC3F314B61D8A00C7D5A3 /* _Album.h */,
2164+ 914AC3F414B61D8A00C7D5A3 /* _Album.m */,
2165+ 914AC3F514B61D8A00C7D5A3 /* _Artist.h */,
2166+ 914AC3F614B61D8A00C7D5A3 /* _Artist.m */,
2167+ 914AC3F714B61D8A00C7D5A3 /* _Playlist.h */,
2168+ 914AC3F814B61D8A00C7D5A3 /* _Playlist.m */,
2169+ 914AC3F914B61D8A00C7D5A3 /* _PlaylistSongIndex.h */,
2170+ 914AC3FA14B61D8A00C7D5A3 /* _PlaylistSongIndex.m */,
2171+ 914AC3FB14B61D8A00C7D5A3 /* _Song.h */,
2172+ 914AC3FC14B61D8A00C7D5A3 /* _Song.m */,
2173+ );
2174+ name = Generated;
2175+ path = Models/Generated;
2176+ sourceTree = SOURCE_ROOT;
2177+ };
2178 9316628512264A74003B0EB7 /* Categories */ = {
2179 isa = PBXGroup;
2180 children = (
2181@@ -702,6 +748,10 @@
2182 936F230912284D1900070F43 /* NamedTextFieldCell.m */,
2183 912D1262134A162000721EE4 /* AlertPrompt.h */,
2184 912D1263134A162000721EE4 /* AlertPrompt.m */,
2185+ 91C8CCAD14B7883600A0E311 /* ArtistUITableViewCell.h */,
2186+ 91C8CCAE14B7883600A0E311 /* ArtistUITableViewCell.m */,
2187+ 91D3A17014BE12FE003D4FC0 /* PlaylistUITableViewCell.h */,
2188+ 91D3A17114BE12FE003D4FC0 /* PlaylistUITableViewCell.m */,
2189 );
2190 name = Controls;
2191 path = controls;
2192@@ -710,8 +760,9 @@
2193 936F20651227363800070F43 /* Models */ = {
2194 isa = PBXGroup;
2195 children = (
2196- 93DFFE43135D71030061F29F /* MOC.h */,
2197- 93DFFE44135D71030061F29F /* MOC.m */,
2198+ 914AC3F214B61D8A00C7D5A3 /* Generated */,
2199+ 91D3A16D14B93B2A003D4FC0 /* MOC.h */,
2200+ 91D3A16E14B93B2A003D4FC0 /* MOC.m */,
2201 93F3344E1247FA2C006C6707 /* Album.h */,
2202 93F3344F1247FA2C006C6707 /* Album.m */,
2203 93F334501247FA2C006C6707 /* Artist.h */,
2204@@ -720,10 +771,10 @@
2205 936F208F12273D9000070F43 /* Song.m */,
2206 936F20661227364200070F43 /* Playlist.h */,
2207 936F20671227364200070F43 /* Playlist.m */,
2208- 9646DB7B13A8FF4700CB42D2 /* CachedSongsPlaylist.h */,
2209- 9646DB7C13A8FF4700CB42D2 /* CachedSongsPlaylist.m */,
2210- 91E0778713ABAD6100AA7CB2 /* PlaylistSongIndex.h */,
2211- 91E0778813ABAD6200AA7CB2 /* PlaylistSongIndex.m */,
2212+ 914AC3FD14B61D8A00C7D5A3 /* PlaylistSongIndex.h */,
2213+ 914AC3FE14B61D8A00C7D5A3 /* PlaylistSongIndex.m */,
2214+ 914AC3F014B61D8A00C7D5A3 /* CachedSongsPlaylist.h */,
2215+ 914AC3F114B61D8A00C7D5A3 /* CachedSongsPlaylist.m */,
2216 );
2217 name = Models;
2218 path = models;
2219@@ -732,6 +783,10 @@
2220 937FAA0C137CFC1B00507E51 /* operations */ = {
2221 isa = PBXGroup;
2222 children = (
2223+ 91E3E46214D33A2D006ED32F /* U1SerializedDownloadOperation.h */,
2224+ 91E3E46314D33A2D006ED32F /* U1SerializedDownloadOperation.m */,
2225+ 91E3E46614D33A6F006ED32F /* U1ChunkDownloadOperation.h */,
2226+ 91E3E46714D33A6F006ED32F /* U1ChunkDownloadOperation.m */,
2227 937FAA16137CFC7200507E51 /* DownloadOperation.h */,
2228 937FAA17137CFC7200507E51 /* DownloadOperation.m */,
2229 937FAA13137CFC5000507E51 /* Downloader.h */,
2230@@ -976,10 +1031,11 @@
2231 29B97313FDCFA39411CA2CEA /* Project object */ = {
2232 isa = PBXProject;
2233 attributes = {
2234+ LastUpgradeCheck = 0420;
2235 ORGANIZATIONNAME = Canonical;
2236 };
2237 buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "U1Music" */;
2238- compatibilityVersion = "Xcode 3.1";
2239+ compatibilityVersion = "Xcode 3.2";
2240 developmentRegion = English;
2241 hasScannedForEncodings = 1;
2242 knownRegions = (
2243@@ -1162,7 +1218,6 @@
2244 91018B4E135A724B0051EFDC /* PlaylistEditArtistViewController.m in Sources */,
2245 91018B4F135A724B0051EFDC /* PlaylistEditSongListViewController.m in Sources */,
2246 919376F3135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m in Sources */,
2247- 93DFFE45135D71030061F29F /* MOC.m in Sources */,
2248 93DFFE4D135D71760061F29F /* music.xcdatamodeld in Sources */,
2249 93DFFE54135D72420061F29F /* NSManagedObjectContext+Additions.m in Sources */,
2250 937FAA11137CFC1B00507E51 /* AlbumArtLoader.m in Sources */,
2251@@ -1176,14 +1231,24 @@
2252 91BE4019138E8D2300D44D68 /* UIAlertView+Blocks.m in Sources */,
2253 91BE401C138E8D3A00D44D68 /* RIButtonItem.m in Sources */,
2254 9674C2C513A7DC01004509E4 /* UORadialProgressControl.m in Sources */,
2255- 9646DB7D13A8FF4700CB42D2 /* CachedSongsPlaylist.m in Sources */,
2256 96FB790A13AA628700D8D4A4 /* UONetworkStatusCoordinator.m in Sources */,
2257- 91E0778913ABAD6200AA7CB2 /* PlaylistSongIndex.m in Sources */,
2258 9654C9BC13C7826900D0EEA0 /* UOSSOCredentialsViewController.m in Sources */,
2259 9654C9C713C7AF9200D0EEA0 /* UOMusicLoginController.m in Sources */,
2260 964FA3DF13CA5D1D0018A65B /* UOJSONFetchOperation.m in Sources */,
2261 964FA3EA13CA848D0018A65B /* UOHTTPFetchOperation.m in Sources */,
2262 964FA3ED13CA8C490018A65B /* UOSSOMusicCredsFetchOperation.m in Sources */,
2263+ 914AC3FF14B61D8A00C7D5A3 /* CachedSongsPlaylist.m in Sources */,
2264+ 914AC40014B61D8A00C7D5A3 /* _Album.m in Sources */,
2265+ 914AC40114B61D8A00C7D5A3 /* _Artist.m in Sources */,
2266+ 914AC40214B61D8A00C7D5A3 /* _Playlist.m in Sources */,
2267+ 914AC40314B61D8A00C7D5A3 /* _PlaylistSongIndex.m in Sources */,
2268+ 914AC40414B61D8A00C7D5A3 /* _Song.m in Sources */,
2269+ 914AC40514B61D8A00C7D5A3 /* PlaylistSongIndex.m in Sources */,
2270+ 91C8CCAF14B7883600A0E311 /* ArtistUITableViewCell.m in Sources */,
2271+ 91D3A16F14B93B2A003D4FC0 /* MOC.m in Sources */,
2272+ 91D3A17214BE12FE003D4FC0 /* PlaylistUITableViewCell.m in Sources */,
2273+ 91E3E46414D33A2D006ED32F /* U1SerializedDownloadOperation.m in Sources */,
2274+ 91E3E46814D33A6F006ED32F /* U1ChunkDownloadOperation.m in Sources */,
2275 );
2276 runOnlyForDeploymentPostprocessing = 0;
2277 };
2278@@ -1266,7 +1331,6 @@
2279 GCC_WARN_UNUSED_VARIABLE = YES;
2280 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
2281 OTHER_CFLAGS = "";
2282- PREBINDING = NO;
2283 PROVISIONING_PROFILE = "";
2284 "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2285 SDKROOT = iphoneos;
2286@@ -1285,7 +1349,6 @@
2287 GCC_WARN_ABOUT_RETURN_TYPE = YES;
2288 GCC_WARN_UNUSED_VARIABLE = YES;
2289 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
2290- PREBINDING = NO;
2291 PROVISIONING_PROFILE = "";
2292 "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2293 SDKROOT = iphoneos;
2294@@ -1319,9 +1382,10 @@
2295 93DFFE4C135D71760061F29F /* music.xcdatamodeld */ = {
2296 isa = XCVersionGroup;
2297 children = (
2298+ 914AC3EE14B60ECC00C7D5A3 /* U1Music 2.2-31.xcdatamodel */,
2299 93DFFE48135D71550061F29F /* music.xcdatamodel */,
2300 );
2301- currentVersion = 93DFFE48135D71550061F29F /* music.xcdatamodel */;
2302+ currentVersion = 914AC3EE14B60ECC00C7D5A3 /* U1Music 2.2-31.xcdatamodel */;
2303 path = music.xcdatamodeld;
2304 sourceTree = "<group>";
2305 versionGroupType = wrapper.xcdatamodel;
2306
2307=== modified file 'controls/AlbumArtistUITableViewCell.h'
2308--- controls/AlbumArtistUITableViewCell.h 2011-05-05 16:38:37 +0000
2309+++ controls/AlbumArtistUITableViewCell.h 2012-02-03 20:23:18 +0000
2310@@ -19,6 +19,8 @@
2311
2312 #import <UIKit/UIKit.h>
2313
2314+@class Album;
2315+
2316 @interface AlbumArtistUITableViewCell : UITableViewCell {
2317 UIImageView *coverArtView;
2318 UILabel *albumNameLabel;
2319@@ -27,4 +29,5 @@
2320 @property (nonatomic, retain) UIImageView *coverArtView;
2321 @property (nonatomic, retain) UILabel *albumNameLabel;
2322 @property (nonatomic, retain) UILabel *artistNameLabel;
2323+@property (nonatomic, retain) Album *album;
2324 @end
2325
2326=== modified file 'controls/AlbumArtistUITableViewCell.m'
2327--- controls/AlbumArtistUITableViewCell.m 2011-05-05 16:38:37 +0000
2328+++ controls/AlbumArtistUITableViewCell.m 2012-02-03 20:23:18 +0000
2329@@ -20,7 +20,7 @@
2330 #import "AlbumArtistUITableViewCell.h"
2331
2332 @implementation AlbumArtistUITableViewCell
2333-@synthesize coverArtView, albumNameLabel, artistNameLabel;
2334+@synthesize coverArtView, albumNameLabel, artistNameLabel, album;
2335
2336 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
2337 {
2338
2339=== modified file 'controls/AlbumUITableViewCell.h'
2340--- controls/AlbumUITableViewCell.h 2011-05-27 18:22:51 +0000
2341+++ controls/AlbumUITableViewCell.h 2012-02-03 20:23:18 +0000
2342@@ -30,6 +30,8 @@
2343
2344 #import <UIKit/UIKit.h>
2345
2346+@class Album;
2347+
2348 @interface AlbumUITableViewCell : UITableViewCell {
2349 UIImageView *coverArtView;
2350 UILabel *albumNameLabel;
2351@@ -37,5 +39,6 @@
2352
2353 @property (nonatomic, retain) UIImageView *coverArtView;
2354 @property (nonatomic, retain) UILabel *albumNameLabel;
2355+@property (nonatomic, retain) Album *album;
2356
2357 @end
2358
2359=== modified file 'controls/AlbumUITableViewCell.m'
2360--- controls/AlbumUITableViewCell.m 2011-05-27 18:22:51 +0000
2361+++ controls/AlbumUITableViewCell.m 2012-02-03 20:23:18 +0000
2362@@ -32,7 +32,7 @@
2363
2364 @implementation AlbumUITableViewCell
2365
2366-@synthesize coverArtView, albumNameLabel;
2367+@synthesize coverArtView, albumNameLabel, album;
2368
2369 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
2370 if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
2371
2372=== added file 'controls/ArtistUITableViewCell.h'
2373--- controls/ArtistUITableViewCell.h 1970-01-01 00:00:00 +0000
2374+++ controls/ArtistUITableViewCell.h 2012-02-03 20:23:18 +0000
2375@@ -0,0 +1,24 @@
2376+//
2377+// Copyright 2012 Canonical Ltd.
2378+//
2379+// This program is free software: you can redistribute it and/or modify it
2380+// under the terms of the GNU Affero General Public License version 3,
2381+// as published by the Free Software Foundation.
2382+//
2383+// This program is distributed in the hope that it will be useful, but
2384+// WITHOUT ANY WARRANTY; without even the implied warranties of
2385+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2386+// PURPOSE. See the GNU Affero General Public License for more details.
2387+//
2388+// You should have received a copy of the GNU Affero General Public License
2389+// along with this program. If not, see <http://www.gnu.org/licenses/>.
2390+
2391+#import <UIKit/UIKit.h>
2392+
2393+@class Artist;
2394+
2395+@interface ArtistUITableViewCell : UITableViewCell {}
2396+
2397+@property (nonatomic, retain) Artist *artist;
2398+
2399+@end
2400
2401=== added file 'controls/ArtistUITableViewCell.m'
2402--- controls/ArtistUITableViewCell.m 1970-01-01 00:00:00 +0000
2403+++ controls/ArtistUITableViewCell.m 2012-02-03 20:23:18 +0000
2404@@ -0,0 +1,23 @@
2405+//
2406+// Copyright 2012 Canonical Ltd.
2407+//
2408+// This program is free software: you can redistribute it and/or modify it
2409+// under the terms of the GNU Affero General Public License version 3,
2410+// as published by the Free Software Foundation.
2411+//
2412+// This program is distributed in the hope that it will be useful, but
2413+// WITHOUT ANY WARRANTY; without even the implied warranties of
2414+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2415+// PURPOSE. See the GNU Affero General Public License for more details.
2416+//
2417+// You should have received a copy of the GNU Affero General Public License
2418+// along with this program. If not, see <http://www.gnu.org/licenses/>.
2419+
2420+#import "ArtistUITableViewCell.h"
2421+#import "Artist.h"
2422+
2423+@implementation ArtistUITableViewCell
2424+
2425+@synthesize artist;
2426+
2427+@end
2428
2429=== added file 'controls/PlaylistUITableViewCell.h'
2430--- controls/PlaylistUITableViewCell.h 1970-01-01 00:00:00 +0000
2431+++ controls/PlaylistUITableViewCell.h 2012-02-03 20:23:18 +0000
2432@@ -0,0 +1,23 @@
2433+//
2434+// Copyright 2012 Canonical Ltd.
2435+//
2436+// This program is free software: you can redistribute it and/or modify it
2437+// under the terms of the GNU Affero General Public License version 3,
2438+// as published by the Free Software Foundation.
2439+//
2440+// This program is distributed in the hope that it will be useful, but
2441+// WITHOUT ANY WARRANTY; without even the implied warranties of
2442+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2443+// PURPOSE. See the GNU Affero General Public License for more details.
2444+//
2445+// You should have received a copy of the GNU Affero General Public License
2446+// along with this program. If not, see <http://www.gnu.org/licenses/>.
2447+
2448+#import <UIKit/UIKit.h>
2449+
2450+@class Playlist;
2451+
2452+@interface PlaylistUITableViewCell : UITableViewCell {}
2453+@property (nonatomic, retain) Playlist *playlist;
2454+
2455+@end
2456
2457=== added file 'controls/PlaylistUITableViewCell.m'
2458--- controls/PlaylistUITableViewCell.m 1970-01-01 00:00:00 +0000
2459+++ controls/PlaylistUITableViewCell.m 2012-02-03 20:23:18 +0000
2460@@ -0,0 +1,22 @@
2461+//
2462+// Copyright 2012 Canonical Ltd.
2463+//
2464+// This program is free software: you can redistribute it and/or modify it
2465+// under the terms of the GNU Affero General Public License version 3,
2466+// as published by the Free Software Foundation.
2467+//
2468+// This program is distributed in the hope that it will be useful, but
2469+// WITHOUT ANY WARRANTY; without even the implied warranties of
2470+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2471+// PURPOSE. See the GNU Affero General Public License for more details.
2472+//
2473+// You should have received a copy of the GNU Affero General Public License
2474+// along with this program. If not, see <http://www.gnu.org/licenses/>.
2475+
2476+#import "PlaylistUITableViewCell.h"
2477+
2478+@implementation PlaylistUITableViewCell
2479+
2480+@synthesize playlist;
2481+
2482+@end
2483
2484=== removed directory 'models'
2485=== removed file 'models/Album.h'
2486--- models/Album.h 2011-06-14 17:06:21 +0000
2487+++ models/Album.h 1970-01-01 00:00:00 +0000
2488@@ -1,58 +0,0 @@
2489-//
2490-// Album.h
2491-// iSub
2492-//
2493-// Created by Ben Baron on 2/28/10.
2494-// Copyright 2010 Ben Baron. All rights reserved.
2495-//
2496-// Redistribution and use in source and binary forms, with or without modification,
2497-// are permitted provided that the following conditions are met:
2498-//
2499-// * Redistributions of source code must retain the above copyright notice, this
2500-// list of conditions and the following disclaimer.
2501-// * Redistributions in binary form must reproduce the above copyright notice,
2502-// this list of conditions and the following disclaimer in the documentation
2503-// and/or other materials provided with the distribution.
2504-// * Neither the my name nor the names of my contributors may be used to endorse
2505-// or promote products derived from this software without specific prior written
2506-// permission.
2507-//
2508-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
2509-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2510-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
2511-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2512-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2513-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2514-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2515-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2516-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2517-// DAMAGE.
2518-
2519-@class Artist, Song;
2520-
2521-@interface Album : NSManagedObject
2522-{
2523-}
2524-@property (nonatomic, retain) NSString *title;
2525-@property (nonatomic, retain) NSString *artist;
2526-@property (nonatomic, retain) NSString *albumId;
2527-@property (nonatomic, retain) NSString *coverArtId;
2528-@property (nonatomic, retain) Artist * artistEntity;
2529-@property (nonatomic, retain) NSSet *songs;
2530-
2531-+ (BOOL)albumWithIdExists:(NSString*)anAlbumId;
2532-+ (Album*)albumWithId:(NSString*)anAlbumId;
2533-+ (Album*)albumWithTitle:(NSString*)albumTitle artist:(NSString*)albumArtist;
2534-- (BOOL)matchesSearchQuery:(NSString *)query;
2535-- (NSError*)loadSongs;
2536-- (NSString*)dearticlizedTitle;
2537-- (BOOL)hasCachedSongs;
2538-@end
2539-
2540-// coalesce these into one @interface Album (CoreDataGeneratedAccessors) section
2541-@interface Album (CoreDataGeneratedAccessors)
2542-- (void)addSongsObject:(Song *)value;
2543-- (void)removeSongsObject:(Song *)value;
2544-- (void)addSongs:(NSSet *)value;
2545-- (void)removeSongs:(NSSet *)value;
2546-@end
2547
2548=== removed file 'models/Album.m'
2549--- models/Album.m 2011-11-05 00:42:21 +0000
2550+++ models/Album.m 1970-01-01 00:00:00 +0000
2551@@ -1,131 +0,0 @@
2552-//
2553-// Album.m
2554-// iSub
2555-//
2556-// Created by Ben Baron on 2/28/10.
2557-// Copyright 2010 Ben Baron. All rights reserved.
2558-//
2559-// Redistribution and use in source and binary forms, with or without modification,
2560-// are permitted provided that the following conditions are met:
2561-//
2562-// * Redistributions of source code must retain the above copyright notice, this
2563-// list of conditions and the following disclaimer.
2564-// * Redistributions in binary form must reproduce the above copyright notice,
2565-// this list of conditions and the following disclaimer in the documentation
2566-// and/or other materials provided with the distribution.
2567-// * Neither the my name nor the names of my contributors may be used to endorse
2568-// or promote products derived from this software without specific prior written
2569-// permission.
2570-//
2571-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
2572-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2573-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
2574-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2575-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2576-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2577-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2578-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2579-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2580-// DAMAGE.
2581-
2582-#import "Album.h"
2583-#import "Artist.h"
2584-#import "NSMutableArray+Extras.h"
2585-#import "NSString+Extras.h"
2586-#import "AlbumParser.h"
2587-#import "Subsonic.h"
2588-
2589-@interface Album ()
2590-+ (Album*)albumMatchingPredicate:(NSPredicate*)predicate;
2591-@end
2592-
2593-@implementation Album
2594-@dynamic title, artist, albumId, coverArtId;
2595-@dynamic artistEntity, songs;
2596-
2597-+ (BOOL)albumWithIdExists:(NSString*)anAlbumId
2598-{
2599- return (nil != [Album albumWithId:anAlbumId]);
2600-}
2601-
2602-+ (Album*)albumWithId:(NSString*)anAlbumId
2603-{
2604- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"albumId == %@", anAlbumId];
2605- return [self albumMatchingPredicate:predicate];
2606-}
2607-
2608-+ (Album*)albumWithTitle:(NSString*)albumTitle artist:(NSString*)albumArtist;
2609-{
2610- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"title == %@ AND artist = %@", albumTitle, albumArtist];
2611- return [self albumMatchingPredicate:predicate];
2612-}
2613-
2614-+ (Album*)albumMatchingPredicate:(NSPredicate*)predicate;
2615-{
2616- NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
2617- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Album" inManagedObjectContext:PerThreadManagedObjectContext()];
2618- [fetch setEntity:entityDescription];
2619- [fetch setPredicate:predicate];
2620- NSError * error = nil;
2621- NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
2622- [fetch release];
2623-
2624- return [matches lastObject];
2625-}
2626-
2627-- (NSString *)dearticlizedTitle
2628-{
2629- return [self.title dearticlizedString];
2630-}
2631-
2632-- (NSError*)loadSongs
2633-{
2634- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2635- NSError *error = nil;
2636-
2637- NSMutableArray *parameters = [NSMutableArray array];
2638- [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.albumId, nil]];
2639- NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getAlbum.view" parameters:parameters];
2640- NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
2641-
2642- AlbumParser *parser = [[AlbumParser alloc] init];
2643- parser.albumObjectId = [self objectID];
2644-
2645- [xmlParser setDelegate:parser];
2646-
2647- if (![xmlParser parse])
2648- {
2649- error = [[xmlParser parserError] copy];
2650- }
2651-
2652- [xmlParser release];
2653- [parser release];
2654- [pool release];
2655-
2656- return [error autorelease];
2657-}
2658-
2659-- (NSString*)description
2660-{
2661- return [NSString stringWithFormat:@"%@ - %@ (%@) (art: %@) MOC %@",self.artist,self.title,self.albumId,self.coverArtId, [self managedObjectContext]];
2662-}
2663-
2664-- (BOOL)matchesSearchQuery:(NSString *)query
2665-{
2666- NSRange titleResultsRange = [self.title rangeOfString:query options:NSCaseInsensitiveSearch];
2667- return titleResultsRange.length > 0;
2668-}
2669-
2670-- (BOOL)hasCachedSongs;
2671-{
2672- NSManagedObjectContext *moc = [self managedObjectContext];
2673- NSEntityDescription *songEntity = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:moc];
2674- NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
2675- [fetch setEntity:songEntity];
2676- [fetch setPredicate:[NSPredicate predicateWithFormat:@"album = %@ and cachedSongPath != NIL", self.title]];
2677-
2678- NSArray *results = [[self managedObjectContext] executeFetchRequest:fetch error:NULL];
2679- return [results count] > 0;
2680-}
2681-
2682-@end
2683
2684=== removed file 'models/Artist.h'
2685--- models/Artist.h 2011-06-14 17:06:21 +0000
2686+++ models/Artist.h 1970-01-01 00:00:00 +0000
2687@@ -1,59 +0,0 @@
2688-//
2689-// Artist.h
2690-// iSub
2691-//
2692-// Created by Ben Baron on 2/27/10.
2693-// Copyright 2010 Ben Baron. All rights reserved.
2694-//
2695-// Redistribution and use in source and binary forms, with or without modification,
2696-// are permitted provided that the following conditions are met:
2697-//
2698-// * Redistributions of source code must retain the above copyright notice, this
2699-// list of conditions and the following disclaimer.
2700-// * Redistributions in binary form must reproduce the above copyright notice,
2701-// this list of conditions and the following disclaimer in the documentation
2702-// and/or other materials provided with the distribution.
2703-// * Neither the my name nor the names of my contributors may be used to endorse
2704-// or promote products derived from this software without specific prior written
2705-// permission.
2706-//
2707-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
2708-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2709-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
2710-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2711-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2712-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2713-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2714-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2715-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2716-// DAMAGE.
2717-
2718-@class Album, Song;
2719-
2720-@interface Artist : NSManagedObject
2721-{
2722-}
2723-@property (nonatomic, retain) NSString *name;
2724-@property (nonatomic, retain) NSString *artistId;
2725-@property (nonatomic, retain) NSSet* albums;
2726-@property (nonatomic, retain) NSSet* songs;
2727-+ (Artist*)artistWithId:(NSString*)anArtistId;
2728-+ (BOOL)artistWithIdExists:(NSString*)anArtistId;
2729-+ (Artist*)artistWithName:(NSString*)artistName;
2730-- (BOOL)matchesSearchQuery:(NSString *)query;
2731-- (NSString *)dearticlizedName;
2732-- (NSError*)loadAlbums;
2733-- (BOOL)hasCachedSongs;
2734-@end
2735-
2736-@interface Artist (CoreDataGeneratedAccessors)
2737-- (void)addAlbumsObject:(Album *)value;
2738-- (void)removeAlbumsObject:(Album *)value;
2739-- (void)addAlbums:(NSSet *)value;
2740-- (void)removeAlbums:(NSSet *)value;
2741-
2742-- (void)addSongsObject:(Song *)value;
2743-- (void)removeSongsObject:(Song *)value;
2744-- (void)addSongs:(NSSet *)value;
2745-- (void)removeSongs:(NSSet *)value;
2746-@end
2747
2748=== removed file 'models/Artist.m'
2749--- models/Artist.m 2011-11-05 00:42:21 +0000
2750+++ models/Artist.m 1970-01-01 00:00:00 +0000
2751@@ -1,135 +0,0 @@
2752-//
2753-// Artist.m
2754-// iSub
2755-//
2756-// Created by Ben Baron on 2/27/10.
2757-// Copyright 2010 Ben Baron. All rights reserved.
2758-//
2759-// Redistribution and use in source and binary forms, with or without modification,
2760-// are permitted provided that the following conditions are met:
2761-//
2762-// * Redistributions of source code must retain the above copyright notice, this
2763-// list of conditions and the following disclaimer.
2764-// * Redistributions in binary form must reproduce the above copyright notice,
2765-// this list of conditions and the following disclaimer in the documentation
2766-// and/or other materials provided with the distribution.
2767-// * Neither the my name nor the names of my contributors may be used to endorse
2768-// or promote products derived from this software without specific prior written
2769-// permission.
2770-//
2771-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
2772-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2773-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
2774-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2775-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2776-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2777-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2778-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2779-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2780-// DAMAGE.
2781-
2782-#import "Artist.h"
2783-#import "Album.h"
2784-#import "Subsonic.h"
2785-#import "NSMutableArray+Extras.h"
2786-#import "NSString+Extras.h"
2787-#import "ArtistParser.h"
2788-
2789-@interface Artist ()
2790-+ (Artist*)artistMatchingPredicate:(NSPredicate*)predicate;
2791-@end
2792-
2793-
2794-@implementation Artist
2795-
2796-@dynamic name;
2797-@dynamic artistId;
2798-@dynamic albums;
2799-@dynamic songs;
2800-
2801-+ (BOOL)artistWithIdExists:(NSString*)anArtistId
2802-{
2803- return [Artist artistWithId:anArtistId] != nil;
2804-}
2805-
2806-+ (Artist*)artistWithId:(NSString*)anArtistId
2807-{
2808- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artistId == %@", anArtistId];
2809- return [self artistMatchingPredicate:predicate];
2810-}
2811-
2812-+ (Artist*)artistWithName:(NSString*)artistName;
2813-{
2814- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", artistName];
2815- return [self artistMatchingPredicate:predicate];
2816-}
2817-
2818-+ (Artist*)artistMatchingPredicate:(NSPredicate*)predicate;
2819-{
2820- NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
2821- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Artist" inManagedObjectContext:PerThreadManagedObjectContext()];
2822- [fetch setEntity:entityDescription];
2823- [fetch setPredicate:predicate];
2824- NSError * error = nil;
2825- NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
2826- [fetch release];
2827-
2828- return [matches lastObject];
2829-}
2830-
2831-- (NSString*)description
2832-{
2833- return [NSString stringWithFormat:@"%@ (%@) MOC %@",self.name, self.artistId, [self managedObjectContext]];
2834-}
2835-
2836-- (BOOL)matchesSearchQuery:(NSString *)query
2837-{
2838- NSRange titleResultsRange = [self.name rangeOfString:query options:NSCaseInsensitiveSearch];
2839- return titleResultsRange.length > 0;
2840-}
2841-
2842-- (NSString *)dearticlizedName
2843-{
2844- return [self.name dearticlizedString];
2845-}
2846-
2847-// Note: executes synchronously!
2848-- (NSError*)loadAlbums
2849-{
2850- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2851- NSError *error = nil;
2852-
2853- NSMutableArray *parameters = [NSMutableArray array];
2854- [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.artistId, nil]];
2855- NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getMusicDirectory.view" parameters:parameters];
2856- NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
2857- ArtistParser *parser = [[ArtistParser alloc] init];
2858- parser.artistManagedObjectID = [self objectID];
2859- [xmlParser setDelegate:parser];
2860-
2861- if (![xmlParser parse])
2862- {
2863- error = [[xmlParser parserError] copy];
2864- }
2865-
2866- [xmlParser release];
2867- [parser release];
2868- [pool release];
2869-
2870- return [error autorelease];
2871-}
2872-
2873-- (BOOL)hasCachedSongs;
2874-{
2875- NSManagedObjectContext *moc = [self managedObjectContext];
2876- NSEntityDescription *songEntity = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:moc];
2877- NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
2878- [fetch setEntity:songEntity];
2879- [fetch setPredicate:[NSPredicate predicateWithFormat:@"artist = %@ and cachedSongPath != NIL", self.name]];
2880-
2881- NSError *error = nil;
2882- NSArray *results = [moc executeFetchRequest:fetch error:&error];
2883- return [results count] > 0;
2884-}
2885-
2886-@end
2887
2888=== removed file 'models/Playlist.h'
2889--- models/Playlist.h 2011-06-18 02:04:11 +0000
2890+++ models/Playlist.h 1970-01-01 00:00:00 +0000
2891@@ -1,47 +0,0 @@
2892-//
2893-// Playlist.h
2894-// iSub
2895-//
2896-// Created by Aaron Brethorst on 8/26/10.
2897-// Copyright 2010 Canonical Ltd.
2898-//
2899-// This program is free software: you can redistribute it and/or modify it
2900-// under the terms of the GNU Affero General Public License version 3,
2901-// as published by the Free Software Foundation.
2902-//
2903-// This program is distributed in the hope that it will be useful, but
2904-// WITHOUT ANY WARRANTY; without even the implied warranties of
2905-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2906-// PURPOSE. See the GNU Affero General Public License for more details.
2907-//
2908-// You should have received a copy of the GNU Affero General Public License
2909-// along with this program. If not, see <http://www.gnu.org/licenses/>.
2910-
2911-#import <Foundation/Foundation.h>
2912-
2913-@class Song, PlaylistSongIndex;
2914-
2915-@interface Playlist : NSManagedObject
2916-{
2917-}
2918-@property(nonatomic,retain) NSString *name;
2919-@property(nonatomic,retain) NSString *playlistId;
2920-@property(nonatomic,retain) NSSet *playlistSongIndexes;
2921-@property(nonatomic,readonly) NSArray *songs;
2922-
2923-+ (BOOL)playlistWithNameExists:(NSString*)aPlaylistName;
2924-+ (Playlist*)playlistWithName:(NSString*)aPlaylistName;
2925-- (BOOL)matchesSearchQuery:(NSString *)query;
2926-- (NSError*)loadSongs;
2927-- (BOOL)isEditable;
2928-- (void)addSong:(Song *)song;
2929-- (void)removeSong:(Song *)song;
2930-@end
2931-
2932-// coalesce these into one @interface Album (CoreDataGeneratedAccessors) section
2933-@interface Playlist (CoreDataGeneratedAccessors)
2934-- (void)addPlaylistSongIndexesObject:(PlaylistSongIndex *)value;
2935-- (void)removePlaylistSongIndexesObject:(PlaylistSongIndex *)value;
2936-- (void)addPlaylistSongIndexes:(NSSet *)value;
2937-- (void)removePlaylistSongIndexes:(NSSet *)value;
2938-@end
2939
2940=== removed file 'models/Playlist.m'
2941--- models/Playlist.m 2011-11-05 00:42:21 +0000
2942+++ models/Playlist.m 1970-01-01 00:00:00 +0000
2943@@ -1,136 +0,0 @@
2944-//
2945-// Playlist.m
2946-// iSub
2947-//
2948-// Created by Aaron Brethorst on 8/26/10.
2949-// Copyright 2010 Canonical Ltd.
2950-//
2951-// This program is free software: you can redistribute it and/or modify it
2952-// under the terms of the GNU Affero General Public License version 3,
2953-// as published by the Free Software Foundation.
2954-//
2955-// This program is distributed in the hope that it will be useful, but
2956-// WITHOUT ANY WARRANTY; without even the implied warranties of
2957-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2958-// PURPOSE. See the GNU Affero General Public License for more details.
2959-//
2960-// You should have received a copy of the GNU Affero General Public License
2961-// along with this program. If not, see <http://www.gnu.org/licenses/>.
2962-
2963-#import "Playlist.h"
2964-#import "Subsonic.h"
2965-#import "NSMutableArray+Extras.h"
2966-#import "PlaylistParser.h"
2967-#import "PlaylistSongIndex.h"
2968-#import "Song.h"
2969-
2970-@implementation Playlist
2971-
2972-@dynamic playlistId, name, playlistSongIndexes;
2973-
2974-+ (BOOL)playlistWithNameExists:(NSString *)aPlaylistName
2975-{
2976- return (nil != [Playlist playlistWithName:aPlaylistName]);
2977-}
2978-
2979-+ (Playlist *)playlistWithName:(NSString *)aPlaylistName
2980-{
2981- NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
2982- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Playlist" inManagedObjectContext:PerThreadManagedObjectContext()];
2983- [fetch setEntity:entityDescription];
2984-
2985- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", aPlaylistName];
2986- [fetch setPredicate:predicate];
2987-
2988- NSError * error = nil;
2989- NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
2990- [fetch release];
2991-
2992- if ([matches count] > 0)
2993- {
2994- return [matches objectAtIndex:0];
2995- }
2996- else
2997- {
2998- return nil;
2999- }
3000-}
3001-
3002-- (NSError*)loadSongs
3003-{
3004- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3005- NSError *error = nil;
3006-
3007- NSMutableArray *parameters = [NSMutableArray array];
3008- [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.playlistId, nil]];
3009- NSURL *url = [[Subsonic sharedSubsonic] getMetadataURL:@"getPlaylist.view" parameters:parameters];
3010- NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
3011-
3012- PlaylistParser *parser = [[PlaylistParser alloc] init];
3013- parser.playlistObjectId = [self objectID];
3014-
3015- [xmlParser setDelegate:parser];
3016-
3017- if (![xmlParser parse])
3018- {
3019- error = [[xmlParser parserError] copy];
3020- }
3021-
3022- [xmlParser release];
3023- [parser release];
3024- [pool release];
3025-
3026- return [error autorelease];
3027-}
3028-
3029-- (NSArray *)songs
3030-{
3031- // return playlistSongIndexes sorted by index
3032- NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES];
3033- NSArray *results = [[self.playlistSongIndexes sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] valueForKey:@"songEntity"];
3034- return results;
3035-}
3036-
3037-// Do the crazy PlaylistSongIndex lookup/clobber dance inside these methods
3038-- (void)addSong:(Song *)song
3039-{
3040- PlaylistSongIndex *playlistSongIndex = [NSEntityDescription insertNewObjectForEntityForName:@"PlaylistSongIndex" inManagedObjectContext:PerThreadManagedObjectContext()];
3041- playlistSongIndex.playlistEntity = self;
3042- playlistSongIndex.songEntity = song;
3043- playlistSongIndex.index = [NSNumber numberWithUnsignedInt:[self.playlistSongIndexes count]];
3044- SaveContext();
3045-}
3046-
3047-- (void)removeSong:(Song *)song
3048-{
3049- // Look up (potentially) all records where playlistEntity is self, and songEntity is song, and blow them away
3050- NSSet *condemned = [self.playlistSongIndexes filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"songEntity = %@", song]];
3051- for (PlaylistSongIndex *playlistSongIndex in condemned)
3052- {
3053- [PerThreadManagedObjectContext() deleteObject:playlistSongIndex];
3054- }
3055- SaveContext();
3056-
3057- [[self.playlistSongIndexes sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
3058- ((PlaylistSongIndex *)obj).index = [NSNumber numberWithUnsignedInt:idx];
3059- }];
3060- SaveContext();
3061-}
3062-
3063-- (NSString*)description
3064-{
3065- return [NSString stringWithFormat:@"%@ (%@) MOC %@", self.name, self.playlistId, [self managedObjectContext]];
3066-}
3067-
3068-- (BOOL)matchesSearchQuery:(NSString *)query
3069-{
3070- NSRange titleResultsRange = [self.name rangeOfString:query options:NSCaseInsensitiveSearch];
3071- return titleResultsRange.length > 0;
3072-}
3073-
3074-- (BOOL)isEditable;
3075-{
3076- return YES;
3077-}
3078-
3079-@end
3080
3081=== removed file 'models/PlaylistSongIndex.h'
3082--- models/PlaylistSongIndex.h 2011-06-21 15:27:36 +0000
3083+++ models/PlaylistSongIndex.h 1970-01-01 00:00:00 +0000
3084@@ -1,32 +0,0 @@
3085-//
3086-// PlaylistSongIndex.h
3087-// iSub
3088-//
3089-// Created by Zachery Bir on 6/17/11.
3090-// Copyright 2011 Canonical Ltd.
3091-//
3092-// This program is free software: you can redistribute it and/or modify it
3093-// under the terms of the GNU Affero General Public License version 3,
3094-// as published by the Free Software Foundation.
3095-//
3096-// This program is distributed in the hope that it will be useful, but
3097-// WITHOUT ANY WARRANTY; without even the implied warranties of
3098-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3099-// PURPOSE. See the GNU Affero General Public License for more details.
3100-//
3101-// You should have received a copy of the GNU Affero General Public License
3102-// along with this program. If not, see <http://www.gnu.org/licenses/>.
3103-
3104-#import <Foundation/Foundation.h>
3105-
3106-@class Playlist, Song;
3107-
3108-@interface PlaylistSongIndex : NSManagedObject {
3109-@private
3110-
3111-}
3112-@property (nonatomic, retain) NSNumber *index;
3113-
3114-@property (nonatomic, retain) Playlist *playlistEntity;
3115-@property (nonatomic, retain) Song *songEntity;
3116-@end
3117
3118=== removed file 'models/PlaylistSongIndex.m'
3119--- models/PlaylistSongIndex.m 2011-06-21 15:27:36 +0000
3120+++ models/PlaylistSongIndex.m 1970-01-01 00:00:00 +0000
3121@@ -1,27 +0,0 @@
3122-//
3123-// PlaylistSongIndex.m
3124-// iSub
3125-//
3126-// Created by Zachery Bir on 6/17/11.
3127-// Copyright 2011 Canonical Ltd.
3128-//
3129-// This program is free software: you can redistribute it and/or modify it
3130-// under the terms of the GNU Affero General Public License version 3,
3131-// as published by the Free Software Foundation.
3132-//
3133-// This program is distributed in the hope that it will be useful, but
3134-// WITHOUT ANY WARRANTY; without even the implied warranties of
3135-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3136-// PURPOSE. See the GNU Affero General Public License for more details.
3137-//
3138-// You should have received a copy of the GNU Affero General Public License
3139-// along with this program. If not, see <http://www.gnu.org/licenses/>.
3140-
3141-#import "PlaylistSongIndex.h"
3142-
3143-
3144-@implementation PlaylistSongIndex
3145-
3146-@dynamic index, playlistEntity, songEntity;
3147-
3148-@end
3149
3150=== removed file 'models/Song.h'
3151--- models/Song.h 2011-06-21 15:29:37 +0000
3152+++ models/Song.h 1970-01-01 00:00:00 +0000
3153@@ -1,67 +0,0 @@
3154-//
3155-// Song.h
3156-// iSub
3157-//
3158-// Created by Ben Baron on 2/28/10.
3159-// Copyright 2010 Ben Baron. All rights reserved.
3160-//
3161-// Redistribution and use in source and binary forms, with or without modification,
3162-// are permitted provided that the following conditions are met:
3163-//
3164-// * Redistributions of source code must retain the above copyright notice, this
3165-// list of conditions and the following disclaimer.
3166-// * Redistributions in binary form must reproduce the above copyright notice,
3167-// this list of conditions and the following disclaimer in the documentation
3168-// and/or other materials provided with the distribution.
3169-// * Neither the my name nor the names of my contributors may be used to endorse
3170-// or promote products derived from this software without specific prior written
3171-// permission.
3172-//
3173-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
3174-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3175-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
3176-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
3177-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
3178-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
3179-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3180-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3181-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3182-// DAMAGE.
3183-
3184-@class Artist, Album, PlaylistSongIndex;
3185-
3186-@interface Song : NSManagedObject
3187-{
3188-}
3189-@property (nonatomic, retain) NSString *title;
3190-@property (nonatomic, retain) NSString *songId;
3191-@property (nonatomic, retain) NSString *artist;
3192-@property (nonatomic, retain) NSString *album;
3193-@property (nonatomic, retain) NSString *genre;
3194-@property (nonatomic, retain) NSString *coverArtId;
3195-@property (nonatomic, retain) NSString *path;
3196-@property (nonatomic, retain) NSNumber *duration;
3197-@property (nonatomic, retain) NSNumber *bitRate;
3198-@property (nonatomic, retain) NSNumber *track;
3199-@property (nonatomic, retain) NSNumber *discNumber;
3200-@property (nonatomic, retain) NSNumber *year;
3201-@property (nonatomic, retain) NSNumber *size;
3202-@property (nonatomic, retain) Artist *artistEntity;
3203-@property (nonatomic, retain) Album *albumEntity;
3204-@property (nonatomic, retain) NSSet *playlistSongIndexes;
3205-@property (nonatomic, retain) NSString *cachedSongPath;
3206-
3207-+ (BOOL)songWithIdExists:(NSString*)aSongId;
3208-+ (Song*)songWithId:(NSString*)aSongId;
3209-- (BOOL)matchesSearchQuery:(NSString *)query;
3210-- (NSString *)dearticlizedTitle;
3211-- (BOOL)cachedSongExists;
3212-- (NSURL*)cachedSongURL;
3213-@end
3214-
3215-@interface Song (CoreDataGeneratedAccessors)
3216-- (void)addPlaylistSongIndexesObject:(PlaylistSongIndex *)value;
3217-- (void)removePlaylistSongIndexesObject:(PlaylistSongIndex *)value;
3218-- (void)addPlaylistSongIndexes:(NSSet *)value;
3219-- (void)removePlaylistSongIndexes:(NSSet *)value;
3220-@end
3221
3222=== removed file 'models/Song.m'
3223--- models/Song.m 2011-06-21 17:04:19 +0000
3224+++ models/Song.m 1970-01-01 00:00:00 +0000
3225@@ -1,113 +0,0 @@
3226-//
3227-// Song.m
3228-// iSub
3229-//
3230-// Created by Ben Baron on 2/28/10.
3231-// Copyright 2010 Ben Baron. All rights reserved.
3232-//
3233-// Redistribution and use in source and binary forms, with or without modification,
3234-// are permitted provided that the following conditions are met:
3235-//
3236-// * Redistributions of source code must retain the above copyright notice, this
3237-// list of conditions and the following disclaimer.
3238-// * Redistributions in binary form must reproduce the above copyright notice,
3239-// this list of conditions and the following disclaimer in the documentation
3240-// and/or other materials provided with the distribution.
3241-// * Neither the my name nor the names of my contributors may be used to endorse
3242-// or promote products derived from this software without specific prior written
3243-// permission.
3244-//
3245-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
3246-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3247-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
3248-// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
3249-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
3250-// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
3251-// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3252-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3253-// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3254-// DAMAGE.
3255-
3256-#import "Song.h"
3257-#import "Artist.h"
3258-#import "Album.h"
3259-#import "NSString+Extras.h"
3260-
3261-@implementation Song
3262-@dynamic title, songId, artist, album, genre, coverArtId, path, duration, bitRate, track, discNumber, year, size;
3263-@dynamic artistEntity, albumEntity, playlistSongIndexes, cachedSongPath;
3264-
3265-+ (BOOL)songWithIdExists:(NSString*)aSongId
3266-{
3267- return (nil != [Song songWithId:aSongId]);
3268-}
3269-
3270-+ (Song*)songWithId:(NSString*)aSongId
3271-{
3272- NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
3273- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:PerThreadManagedObjectContext()];
3274- [fetch setEntity:entityDescription];
3275-
3276- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"songId == %@", aSongId];
3277- [fetch setPredicate:predicate];
3278-
3279- NSError * error = nil;
3280- NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
3281- [fetch release];
3282-
3283- if ([matches count] > 0)
3284- {
3285- return [matches objectAtIndex:0];
3286- }
3287- else
3288- {
3289- return nil;
3290- }
3291-}
3292-
3293-- (NSURL*)cachedSongURL
3294-{
3295- return [CachedMusicDirectory() URLByAppendingPathComponent:self.songId];
3296-}
3297-
3298-- (BOOL)cachedSongExists
3299-{
3300- return self.cachedSongPath != nil;
3301-}
3302-
3303-- (NSComparisonResult)compare:(Song*)s
3304-{
3305- NSUInteger selfPathArray[] = {[self.discNumber integerValue], [self.track integerValue]};
3306- NSUInteger otherPathArray[] = {[s.discNumber integerValue], [s.track integerValue]};
3307-
3308- NSIndexPath *selfPath = [NSIndexPath indexPathWithIndexes:selfPathArray length:2];
3309- NSIndexPath *otherPath = [NSIndexPath indexPathWithIndexes:otherPathArray length:2];
3310-
3311- return [selfPath compare:otherPath];
3312-}
3313-
3314-- (NSString*)description
3315-{
3316- NSArray *keys = [NSArray arrayWithObjects:@"title", @"songId", @"artist", @"album", @"genre", @"coverArtId", @"path", @"duration", @"bitRate", @"track", @"year", @"size", nil];
3317-
3318- NSMutableString *desc = [NSMutableString string];
3319-
3320- for (NSString *k in keys)
3321- {
3322- [desc appendFormat:@"%@:%@ :: ",k, [self valueForKey:k]];
3323- }
3324- return desc;
3325-}
3326-
3327-- (BOOL)matchesSearchQuery:(NSString *)query
3328-{
3329- NSRange titleResultsRange = [self.title rangeOfString:query options:NSCaseInsensitiveSearch];
3330- return titleResultsRange.length > 0;
3331-}
3332-
3333-- (NSString *)dearticlizedTitle
3334-{
3335- return [self.title dearticlizedString];
3336-}
3337-
3338-@end
3339
3340=== modified file 'music.xcdatamodeld/.xccurrentversion'
3341--- music.xcdatamodeld/.xccurrentversion 2011-04-19 08:05:04 +0000
3342+++ music.xcdatamodeld/.xccurrentversion 2012-02-03 20:23:18 +0000
3343@@ -3,6 +3,6 @@
3344 <plist version="1.0">
3345 <dict>
3346 <key>_XCCurrentVersionName</key>
3347- <string>music.xcdatamodel</string>
3348+ <string>U1Music 2.2-31.xcdatamodel</string>
3349 </dict>
3350 </plist>
3351
3352=== added directory 'music.xcdatamodeld/U1Music 2.2-31.xcdatamodel'
3353=== added file 'music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/elements'
3354Binary files music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/elements 1970-01-01 00:00:00 +0000 and music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/elements 2012-02-03 20:23:18 +0000 differ
3355=== added file 'music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/layout'
3356Binary files music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/layout 1970-01-01 00:00:00 +0000 and music.xcdatamodeld/U1Music 2.2-31.xcdatamodel/layout 2012-02-03 20:23:18 +0000 differ
3357=== modified file 'utilities/AlbumParser.m'
3358--- utilities/AlbumParser.m 2011-06-21 15:29:37 +0000
3359+++ utilities/AlbumParser.m 2012-02-03 20:23:18 +0000
3360@@ -97,7 +97,7 @@
3361
3362 - (void)setAlbumObjectId:(NSManagedObjectID *)oid
3363 {
3364- Album *anAlbum = [[PerThreadManagedObjectContext() objectWithID:oid] retain];
3365+ Album *anAlbum = (Album *)[[PerThreadManagedObjectContext() objectWithID:oid] retain];
3366 [album release];
3367 album = anAlbum;
3368 }
3369
3370=== modified file 'utilities/ArtistParser.m'
3371--- utilities/ArtistParser.m 2011-05-27 18:23:44 +0000
3372+++ utilities/ArtistParser.m 2012-02-03 20:23:18 +0000
3373@@ -37,7 +37,7 @@
3374
3375 - (void)setArtistManagedObjectID:(NSManagedObjectID *)moid
3376 {
3377- Artist *anArtist = [[PerThreadManagedObjectContext() objectWithID:moid] retain];
3378+ Artist *anArtist = (Artist*)[[PerThreadManagedObjectContext() objectWithID:moid] retain];
3379 [artist release];
3380 artist = anArtist;
3381 }
3382
3383=== modified file 'utilities/Globals.h'
3384--- utilities/Globals.h 2011-06-21 15:29:37 +0000
3385+++ utilities/Globals.h 2012-02-03 20:23:18 +0000
3386@@ -21,6 +21,7 @@
3387
3388 NSURL* ApplicationDocumentsDirectory( void );
3389 NSURL* DatabaseFile( void );
3390+NSURL* TemporaryDownloadsDirectory( void );
3391 BOOL CacheContainsFile(NSString *aFilePath);
3392 NSURL* CachedMusicDirectory( void );
3393 NSString* CachedMusicPathForFilename(NSString *aFileName);
3394
3395=== modified file 'utilities/Globals.m'
3396--- utilities/Globals.m 2011-06-21 15:29:37 +0000
3397+++ utilities/Globals.m 2012-02-03 20:23:18 +0000
3398@@ -30,6 +30,12 @@
3399 return [docs URLByAppendingPathComponent:@"music.sqlite"];
3400 }
3401
3402+NSURL* TemporaryDownloadsDirectory( void )
3403+{
3404+ NSURL* docs = ApplicationDocumentsDirectory();
3405+ return [docs URLByAppendingPathComponent:@"tmp"];
3406+}
3407+
3408 BOOL CacheContainsFile(NSString *aFilePath)
3409 {
3410 return [[NSFileManager defaultManager] fileExistsAtPath:aFilePath];
3411
3412=== modified file 'utilities/PlaylistParser.m'
3413--- utilities/PlaylistParser.m 2011-06-18 02:04:11 +0000
3414+++ utilities/PlaylistParser.m 2012-02-03 20:23:18 +0000
3415@@ -100,7 +100,7 @@
3416
3417 - (void)setPlaylistObjectId:(NSManagedObjectID *)oid
3418 {
3419- Playlist *aPlaylist = [[PerThreadManagedObjectContext() objectWithID:oid] retain];
3420+ Playlist *aPlaylist = (Playlist *)[[PerThreadManagedObjectContext() objectWithID:oid] retain];
3421 [playlist release];
3422 playlist = aPlaylist;
3423 }
3424
3425=== modified file 'utilities/operations/Downloader.m'
3426--- utilities/operations/Downloader.m 2011-06-21 15:29:37 +0000
3427+++ utilities/operations/Downloader.m 2012-02-03 20:23:18 +0000
3428@@ -24,6 +24,8 @@
3429 #import "Album.h"
3430 #import "Song.h"
3431 #import "Subsonic.h"
3432+#import "U1ChunkDownloadOperation.h"
3433+#import "U1SerializedDownloadOperation.h"
3434
3435 static const NSUInteger kMaxConcurrentDownloads = 4;
3436
3437@@ -35,6 +37,7 @@
3438
3439 - (void)startAlbumEnqueueing:(Album*)anAlbum;
3440 - (void)completeAlbumEnqueueing:(Album*)anAlbum;
3441+- (void)downloadFile:(NSURL*)url withName:(NSString*)fileName completionBlock:(void(^)(NSString *path))completionBlock chunked:(BOOL)inChunks;
3442 @end
3443
3444 @implementation Downloader
3445@@ -85,38 +88,126 @@
3446
3447 - (void)downloadFile:(NSURL*)url withName:(NSString*)fileName
3448 {
3449- [self downloadFile:url withName:fileName completionBlock:NULL];
3450+ [self downloadFile:url withName:fileName completionBlock:NULL chunked:NO];
3451+}
3452+
3453+- (void)downloadFile:(NSURL *)url withName:(NSString *)fileName completionBlock:(void (^)(NSString *))completionBlock;
3454+{
3455+ [self downloadFile:url withName:fileName completionBlock:completionBlock chunked:NO];
3456 }
3457
3458 - (void)downloadFile:(NSURL*)url withName:(NSString*)fileName completionBlock:(void(^)(NSString *path))completionBlock
3459+ chunked:(BOOL)inChunks;
3460 {
3461 @synchronized(self)
3462 {
3463- if ([downloads containsObject:fileName])
3464- {
3465- return;
3466- }
3467-
3468- NSString *path = CachedMusicPathForFilename(fileName);
3469-
3470- if (CacheContainsFile(path))
3471- {
3472- // Partially downloaded file, delete and try again
3473- [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
3474- }
3475-
3476- [downloads addObject:fileName];
3477- // Disable idle timer sleep while downloading
3478- [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
3479-
3480- DownloadOperation *download = [DownloadOperation downloadOperationWithURL:url
3481- filePath:path];
3482- download.delegate = self;
3483- [download setCompletionBlock:^(void) {
3484- if (completionBlock)
3485- completionBlock(path);
3486- }];
3487- [queue addOperation:download];
3488+ if (inChunks)
3489+ {
3490+ NSURL* tmpDir = [TemporaryDownloadsDirectory() URLByAppendingPathComponent:fileName];
3491+
3492+ if ([downloads containsObject:fileName] || [[NSFileManager defaultManager] fileExistsAtPath:[tmpDir path]])
3493+ {
3494+ return;
3495+ }
3496+
3497+ NSError *error = nil;
3498+ BOOL yup = [[NSFileManager defaultManager] createDirectoryAtURL:tmpDir withIntermediateDirectories:YES attributes:nil error:&error];
3499+
3500+ NSString *path = CachedMusicPathForFilename(fileName);
3501+
3502+ [downloads addObject:fileName];
3503+ // Disable idle timer sleep while downloading
3504+ [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
3505+
3506+ U1SerializedDownloadOperation *serializer = [[U1SerializedDownloadOperation alloc] initWithPath:path andChunkDirectoryPath:[tmpDir path]];
3507+
3508+ [serializer setCompletionBlock:^(void) {
3509+ dispatch_async(dispatch_get_main_queue(), ^{
3510+ [downloads removeObject:[path lastPathComponent]];
3511+ if ([downloads count] == 0)
3512+ {
3513+ // If downloads is empty, go back to idle timing for sleep
3514+ [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
3515+ }
3516+ // Coarse-grained, blanket notification that any download has completed.
3517+ [[NSNotificationCenter defaultCenter] postNotificationName:NOTIF_downloadComplete object:nil];
3518+
3519+ if (completionBlock)
3520+ completionBlock(path);
3521+ });
3522+ }];
3523+
3524+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3525+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
3526+ [request setHTTPMethod:@"HEAD"];
3527+ NSError *error = nil;
3528+ NSHTTPURLResponse *response = nil;
3529+ [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
3530+
3531+ NSString *contentLengthString = [[response allHeaderFields] objectForKey:@"Content-Length"];
3532+ NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
3533+ NSNumber *contentLength = [nf numberFromString:contentLengthString];
3534+
3535+ static NSUInteger minChunkSize = 100000;
3536+ static NSUInteger chunkSize = 500000;
3537+ NSUInteger remaining = [contentLength unsignedIntegerValue];
3538+ NSUInteger offset = 0;
3539+
3540+ NSMutableArray *chunkRanges = [NSMutableArray array];
3541+
3542+ while (remaining > 0)
3543+ {
3544+ // If remaining is between chunkSize and (chunkSize + minChunkSize), just make this chunkSize that much more.
3545+ if ((remaining - chunkSize) < minChunkSize)
3546+ {
3547+ chunkSize = remaining;
3548+ }
3549+ NSRange chunkRange = NSMakeRange(offset, MIN(remaining, chunkSize));
3550+ [chunkRanges addObject:[NSValue valueWithRange:chunkRange]];
3551+ offset += chunkRange.length;
3552+ remaining -= chunkRange.length;
3553+ }
3554+
3555+ [chunkRanges enumerateObjectsUsingBlock:^(id rangeValue, NSUInteger idx, BOOL *stop) {
3556+ NSRange range = [rangeValue rangeValue];
3557+
3558+ NSString *path = [[tmpDir path] stringByAppendingPathComponent:[NSString stringWithFormat:@"%lu.chunk", idx]];
3559+
3560+ U1ChunkDownloadOperation *chunk = [[U1ChunkDownloadOperation alloc] initWithUrl:url path:path andRange:range];
3561+
3562+ [queue addOperation:chunk];
3563+ [serializer addDependency:chunk];
3564+ }];
3565+
3566+ [queue addOperation:serializer];
3567+ });
3568+ }
3569+ else
3570+ {
3571+ if ([downloads containsObject:fileName])
3572+ {
3573+ return;
3574+ }
3575+ NSString *path = CachedMusicPathForFilename(fileName);
3576+ if (CacheContainsFile(path))
3577+ {
3578+ // Partially downloaded file, delete and try again
3579+ [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
3580+ }
3581+
3582+ [downloads addObject:fileName];
3583+ // Disable idle timer sleep while downloading
3584+ [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
3585+
3586+ DownloadOperation *download = [DownloadOperation downloadOperationWithURL:url
3587+ filePath:path];
3588+ download.delegate = self;
3589+ [download setCompletionBlock:^(void) {
3590+ if (completionBlock)
3591+ completionBlock(path);
3592+ }];
3593+ [queue addOperation:download];
3594+ }
3595 }
3596 }
3597
3598
3599=== added file 'utilities/operations/U1ChunkDownloadOperation.h'
3600--- utilities/operations/U1ChunkDownloadOperation.h 1970-01-01 00:00:00 +0000
3601+++ utilities/operations/U1ChunkDownloadOperation.h 2012-02-03 20:23:18 +0000
3602@@ -0,0 +1,24 @@
3603+//
3604+// Copyright 2012 Canonical Ltd.
3605+//
3606+// This program is free software: you can redistribute it and/or modify it
3607+// under the terms of the GNU Affero General Public License version 3,
3608+// as published by the Free Software Foundation.
3609+//
3610+// This program is distributed in the hope that it will be useful, but
3611+// WITHOUT ANY WARRANTY; without even the implied warranties of
3612+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3613+// PURPOSE. See the GNU Affero General Public License for more details.
3614+//
3615+// You should have received a copy of the GNU Affero General Public License
3616+// along with this program. If not, see <http://www.gnu.org/licenses/>.
3617+
3618+#import "AbstractNetworkOperation.h"
3619+
3620+@interface U1ChunkDownloadOperation : AbstractNetworkOperation
3621+@property (retain, nonatomic) NSString *path;
3622+@property (retain, nonatomic) NSFileHandle *fileHandle;
3623+@property (assign) NSRange range;
3624+
3625+- (id)initWithUrl:(NSURL*)url path:(NSString*)path andRange:(NSRange)aRange;
3626+@end
3627
3628=== added file 'utilities/operations/U1ChunkDownloadOperation.m'
3629--- utilities/operations/U1ChunkDownloadOperation.m 1970-01-01 00:00:00 +0000
3630+++ utilities/operations/U1ChunkDownloadOperation.m 2012-02-03 20:23:18 +0000
3631@@ -0,0 +1,68 @@
3632+//
3633+// Copyright 2012 Canonical Ltd.
3634+//
3635+// This program is free software: you can redistribute it and/or modify it
3636+// under the terms of the GNU Affero General Public License version 3,
3637+// as published by the Free Software Foundation.
3638+//
3639+// This program is distributed in the hope that it will be useful, but
3640+// WITHOUT ANY WARRANTY; without even the implied warranties of
3641+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3642+// PURPOSE. See the GNU Affero General Public License for more details.
3643+//
3644+// You should have received a copy of the GNU Affero General Public License
3645+// along with this program. If not, see <http://www.gnu.org/licenses/>.
3646+
3647+#import "U1ChunkDownloadOperation.h"
3648+
3649+@implementation U1ChunkDownloadOperation
3650+
3651+@synthesize path, fileHandle, range;
3652+
3653+- (id)initWithUrl:(NSURL *)url path:(NSString *)aPath andRange:(NSRange)aRange;
3654+{
3655+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
3656+ NSString *requestRange = [NSString stringWithFormat:@"bytes=%lu-%lu", aRange.location, NSMaxRange(aRange) - 1];
3657+ [request setValue:requestRange forHTTPHeaderField:@"Range"];
3658+
3659+ self = [super initWithRequest:request];
3660+ if (self == nil)
3661+ {
3662+ return nil;
3663+ }
3664+
3665+ self.range = aRange;
3666+ self.path = aPath;
3667+ [[NSFileManager defaultManager] createFileAtPath:self.path contents:[NSData data] attributes:nil];
3668+ fileHandle = [[NSFileHandle fileHandleForWritingAtPath:self.path] retain];
3669+
3670+ return self;
3671+}
3672+
3673+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
3674+{
3675+ [super connection:connection didReceiveResponse:response];
3676+}
3677+
3678+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
3679+{
3680+ [super connection:connection didReceiveData:data];
3681+
3682+ [fileHandle writeData:data];
3683+}
3684+
3685+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
3686+{
3687+ [fileHandle closeFile];
3688+
3689+ [super connectionDidFinishLoading:connection];
3690+}
3691+
3692+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
3693+{
3694+ [fileHandle closeFile];
3695+ [[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
3696+
3697+ [super connection:connection didFailWithError:error];
3698+}
3699+@end
3700
3701=== added file 'utilities/operations/U1SerializedDownloadOperation.h'
3702--- utilities/operations/U1SerializedDownloadOperation.h 1970-01-01 00:00:00 +0000
3703+++ utilities/operations/U1SerializedDownloadOperation.h 2012-02-03 20:23:18 +0000
3704@@ -0,0 +1,22 @@
3705+//
3706+// Copyright 2012 Canonical Ltd.
3707+//
3708+// This program is free software: you can redistribute it and/or modify it
3709+// under the terms of the GNU Affero General Public License version 3,
3710+// as published by the Free Software Foundation.
3711+//
3712+// This program is distributed in the hope that it will be useful, but
3713+// WITHOUT ANY WARRANTY; without even the implied warranties of
3714+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3715+// PURPOSE. See the GNU Affero General Public License for more details.
3716+//
3717+// You should have received a copy of the GNU Affero General Public License
3718+// along with this program. If not, see <http://www.gnu.org/licenses/>.
3719+
3720+#import <Foundation/Foundation.h>
3721+
3722+@interface U1SerializedDownloadOperation : NSOperation
3723+@property (retain, nonatomic) NSString *path;
3724+@property (retain, nonatomic) NSString *chunkDirectoryPath;
3725+- (id)initWithPath:(NSString*)aPath andChunkDirectoryPath:(NSString*)aChunkDirectoryPath;
3726+@end
3727
3728=== added file 'utilities/operations/U1SerializedDownloadOperation.m'
3729--- utilities/operations/U1SerializedDownloadOperation.m 1970-01-01 00:00:00 +0000
3730+++ utilities/operations/U1SerializedDownloadOperation.m 2012-02-03 20:23:18 +0000
3731@@ -0,0 +1,57 @@
3732+//
3733+// Copyright 2012 Canonical Ltd.
3734+//
3735+// This program is free software: you can redistribute it and/or modify it
3736+// under the terms of the GNU Affero General Public License version 3,
3737+// as published by the Free Software Foundation.
3738+//
3739+// This program is distributed in the hope that it will be useful, but
3740+// WITHOUT ANY WARRANTY; without even the implied warranties of
3741+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3742+// PURPOSE. See the GNU Affero General Public License for more details.
3743+//
3744+// You should have received a copy of the GNU Affero General Public License
3745+// along with this program. If not, see <http://www.gnu.org/licenses/>.
3746+
3747+#import "U1SerializedDownloadOperation.h"
3748+
3749+@implementation U1SerializedDownloadOperation
3750+
3751+@synthesize path, chunkDirectoryPath;
3752+
3753+- (id)initWithPath:(NSString *)aPath andChunkDirectoryPath:(NSString *)aChunkDirectoryPath;
3754+{
3755+ self = [super init];
3756+ if (self == nil)
3757+ {
3758+ return nil;
3759+ }
3760+
3761+ self.path = aPath;
3762+ self.chunkDirectoryPath = aChunkDirectoryPath;
3763+
3764+ return self;
3765+}
3766+
3767+- (void)main;
3768+{
3769+ [[NSFileManager defaultManager] createFileAtPath:self.path contents:[NSData data] attributes:nil];
3770+ NSFileHandle *fileHandle = [[NSFileHandle fileHandleForWritingAtPath:self.path] retain];
3771+
3772+ NSError *error = nil;
3773+ NSArray *filenames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.chunkDirectoryPath error:&error];
3774+
3775+ filenames = [filenames sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
3776+
3777+ [filenames enumerateObjectsUsingBlock:^(id filename, NSUInteger idx, BOOL *stop) {
3778+ NSString *filePath = [NSString pathWithComponents:[NSArray arrayWithObjects:self.chunkDirectoryPath, filename, nil]];
3779+ NSData *fileData = [[NSFileManager defaultManager] contentsAtPath:filePath];
3780+ [fileHandle writeData:fileData];
3781+ }];
3782+
3783+ [fileHandle closeFile];
3784+
3785+ error = nil;
3786+ [[NSFileManager defaultManager] removeItemAtPath:self.chunkDirectoryPath error:&error];
3787+}
3788+@end
3789
3790=== modified file 'view_controllers/AlbumListViewController.m'
3791--- view_controllers/AlbumListViewController.m 2011-06-16 19:12:54 +0000
3792+++ view_controllers/AlbumListViewController.m 2012-02-03 20:23:18 +0000
3793@@ -22,10 +22,12 @@
3794 #import "AlbumArtistUITableViewCell.h"
3795 #import "Album.h"
3796 #import "AlbumViewController.h"
3797+#import "Downloader.h"
3798 #import "NSString+Extras.h"
3799
3800 @interface AlbumListViewController ()
3801 - (void)configureCell:(AlbumArtistUITableViewCell*)cell forTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath;
3802+- (void)downloadAlbum:(UILongPressGestureRecognizer*)sender;
3803 @end
3804
3805 @implementation AlbumListViewController
3806@@ -62,6 +64,30 @@
3807 [loader release], loader = nil;
3808 }
3809
3810+- (void)downloadAlbum:(UILongPressGestureRecognizer *)sender;
3811+{
3812+ if ((sender.state == UIGestureRecognizerStateBegan) && canCache)
3813+ {
3814+ Album *album = ((AlbumArtistUITableViewCell *)sender.view).album;
3815+ dispatch_queue_t dispatch_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3816+ dispatch_async(dispatch_queue, ^{
3817+ [album loadSongs];
3818+ for (Song *song in [album songs])
3819+ {
3820+ if (![song cachedSongExists])
3821+ {
3822+ [[Downloader sharedDownloader] downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:song.songId] withName:song.songId completionBlock:^(NSString *path) {
3823+ dispatch_async(dispatch_get_main_queue(), ^(void) {
3824+ song.cachedSongPath = path;
3825+ SaveContext();
3826+ });
3827+ }];
3828+ }
3829+ }
3830+ });
3831+ }
3832+}
3833+
3834 #pragma mark - Data Loading
3835
3836 - (void)loadLocalData
3837@@ -147,6 +173,7 @@
3838 album = [self.tableData objectAtIndex:indexPath.row];
3839 }
3840 }
3841+ cell.album = album;
3842
3843 cell.albumNameLabel.text = album.title;
3844 cell.artistNameLabel.text = album.artist;
3845@@ -170,6 +197,12 @@
3846 cell.albumNameLabel.textColor = [UIColor lightGrayColor];
3847 cell.accessoryType = UITableViewCellAccessoryNone;
3848 }
3849+
3850+// UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(downloadAlbum:)];
3851+// longPressGesture.minimumPressDuration = 1.0f;
3852+// longPressGesture.numberOfTouchesRequired = 1;
3853+// [cell addGestureRecognizer:longPressGesture];
3854+// [longPressGesture release];
3855 }
3856
3857 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
3858
3859=== modified file 'view_controllers/AlbumViewController.m'
3860--- view_controllers/AlbumViewController.m 2011-06-22 20:11:03 +0000
3861+++ view_controllers/AlbumViewController.m 2012-02-03 20:23:18 +0000
3862@@ -138,63 +138,6 @@
3863 return cell;
3864 }
3865
3866-- (void)configureCell:(SongUITableViewCell*)cell forSongAtIndexPath:(NSIndexPath*)indexPath
3867-{
3868- Song *song = [self.songs objectAtIndex:indexPath.row];
3869-
3870- // explicitly re-set the default state of the button/spinner
3871- [cell.cacheButton setImage:(canCache ? [UIImage imageNamed:@"uncached"] : [UIImage imageNamed:@"uncached-disabled"]) forState:UIControlStateNormal];
3872- [cell.spinner stopAnimating];
3873-
3874- cell.cacheButton.tag = indexPath.row;
3875- [cell.cacheButton addTarget:self action:@selector(toggleCacheForSong:) forControlEvents:UIControlEventTouchUpInside];
3876-
3877- if ([[Downloader sharedDownloader] isDownloading:song.songId])
3878- {
3879- [cell.spinner startAnimating];
3880- }
3881- else if ([song cachedSongExists])
3882- {
3883- [cell.cacheButton setImage:[UIImage imageNamed:@"cached"] forState:UIControlStateNormal];
3884- }
3885-
3886- cell.songNameLabel.text = song.title;
3887-
3888- cell.positionLabel.text = [NSString stringWithFormat:@"%@", [song.track intValue] > 0 ? [song.track stringValue] : @""];
3889-
3890- if (song.duration && [song.duration intValue] > 0)
3891- {
3892- cell.durationLabel.text = [NSDate stringFromDuration:song.duration];
3893- }
3894- else
3895- {
3896- cell.durationLabel.text = @"";
3897- }
3898-
3899- if ([StreamingPlayer sharedStreamingPlayer].currentSong && [song.songId isEqual:[StreamingPlayer sharedStreamingPlayer].currentSong.songId])
3900- {
3901- cell.nowPlaying.hidden = NO;
3902- }
3903- else
3904- {
3905- cell.nowPlaying.hidden = YES;
3906- }
3907-
3908- cell.evenRow = (0 == indexPath.row % 2);
3909- if (song.cachedSongPath == nil && !canStream)
3910- {
3911- cell.songNameLabel.textColor = [UIColor lightGrayColor];
3912- cell.positionLabel.textColor = [UIColor lightGrayColor];
3913- cell.durationLabel.textColor = [UIColor lightGrayColor];
3914- }
3915- else
3916- {
3917- cell.songNameLabel.textColor = [UIColor blackColor];
3918- cell.positionLabel.textColor = [UIColor blackColor];
3919- cell.durationLabel.textColor = [UIColor blackColor];
3920- }
3921-}
3922-
3923
3924 #pragma mark -
3925 #pragma mark Table view delegate
3926@@ -268,6 +211,63 @@
3927
3928 @implementation AlbumViewController (Private)
3929
3930+- (void)configureCell:(SongUITableViewCell*)cell forSongAtIndexPath:(NSIndexPath*)indexPath
3931+{
3932+ Song *song = [self.songs objectAtIndex:indexPath.row];
3933+
3934+ // explicitly re-set the default state of the button/spinner
3935+ [cell.cacheButton setImage:(canCache ? [UIImage imageNamed:@"uncached"] : [UIImage imageNamed:@"uncached-disabled"]) forState:UIControlStateNormal];
3936+ [cell.spinner stopAnimating];
3937+
3938+ cell.cacheButton.tag = indexPath.row;
3939+ [cell.cacheButton addTarget:self action:@selector(toggleCacheForSong:) forControlEvents:UIControlEventTouchUpInside];
3940+
3941+ if ([[Downloader sharedDownloader] isDownloading:song.songId])
3942+ {
3943+ [cell.spinner startAnimating];
3944+ }
3945+ else if ([song cachedSongExists])
3946+ {
3947+ [cell.cacheButton setImage:[UIImage imageNamed:@"cached"] forState:UIControlStateNormal];
3948+ }
3949+
3950+ cell.songNameLabel.text = song.title;
3951+
3952+ cell.positionLabel.text = [NSString stringWithFormat:@"%@", [song.track intValue] > 0 ? [song.track stringValue] : @""];
3953+
3954+ if (song.duration && [song.duration intValue] > 0)
3955+ {
3956+ cell.durationLabel.text = [NSDate stringFromDuration:song.duration];
3957+ }
3958+ else
3959+ {
3960+ cell.durationLabel.text = @"";
3961+ }
3962+
3963+ if ([StreamingPlayer sharedStreamingPlayer].currentSong && [song.songId isEqual:[StreamingPlayer sharedStreamingPlayer].currentSong.songId])
3964+ {
3965+ cell.nowPlaying.hidden = NO;
3966+ }
3967+ else
3968+ {
3969+ cell.nowPlaying.hidden = YES;
3970+ }
3971+
3972+ cell.evenRow = (0 == indexPath.row % 2);
3973+ if (song.cachedSongPath == nil && !canStream)
3974+ {
3975+ cell.songNameLabel.textColor = [UIColor lightGrayColor];
3976+ cell.positionLabel.textColor = [UIColor lightGrayColor];
3977+ cell.durationLabel.textColor = [UIColor lightGrayColor];
3978+ }
3979+ else
3980+ {
3981+ cell.songNameLabel.textColor = [UIColor blackColor];
3982+ cell.positionLabel.textColor = [UIColor blackColor];
3983+ cell.durationLabel.textColor = [UIColor blackColor];
3984+ }
3985+}
3986+
3987 - (void)toggleCacheForSong:(id)sender
3988 {
3989 // This is a binary toggle, and right now, we don't bother trying to cancel a download in progress.
3990@@ -320,8 +320,8 @@
3991 [defaults setBool:YES forKey:@"acknowledged-no-cache-on-3g"];
3992 };
3993
3994- UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Caching Disabled", @"")
3995- message:NSLocalizedString(@"Caching songs can only be done on WiFi networks.", @"")
3996+ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Downloading Disabled", @"")
3997+ message:NSLocalizedString(@"Downloading songs for offline use can only be done on WiFi networks.", @"")
3998 cancelButtonItem:cancelButton otherButtonItems:dismissButton, nil];
3999 [alertView show];
4000 [alertView release];
4001
4002=== modified file 'view_controllers/ArtistListViewController.m'
4003--- view_controllers/ArtistListViewController.m 2011-06-16 19:12:54 +0000
4004+++ view_controllers/ArtistListViewController.m 2012-02-03 20:23:18 +0000
4005@@ -30,13 +30,17 @@
4006
4007 #import "ArtistListViewController.h"
4008
4009+#import "Album.h"
4010+#import "ArtistUITableViewCell.h"
4011 #import "ArtistViewController.h"
4012 #import "Artist.h"
4013 #import "ArtistListParser.h"
4014+#import "Downloader.h"
4015 #import "NSString+Extras.h"
4016
4017 @interface ArtistListViewController ()
4018-- (void)configureCell:(UITableViewCell*)cell forTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath;
4019+- (void)configureCell:(ArtistUITableViewCell*)cell forTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath;
4020+- (void)downloadArtist:(UILongPressGestureRecognizer*)sender;
4021 @end
4022
4023 @implementation ArtistListViewController
4024@@ -54,6 +58,36 @@
4025 return nav;
4026 }
4027
4028+- (void)downloadArtist:(UILongPressGestureRecognizer *)sender;
4029+{
4030+ if ((sender.state == UIGestureRecognizerStateBegan) && canCache)
4031+ {
4032+ Artist *artist = ((ArtistUITableViewCell *)sender.view).artist;
4033+ dispatch_queue_t dispatch_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4034+ dispatch_async(dispatch_queue, ^{
4035+ [artist loadAlbums];
4036+ for (Album *album in [artist albums])
4037+ {
4038+ dispatch_async(dispatch_get_main_queue(), ^{
4039+ [album loadSongs];
4040+ for (Song *song in [album songs])
4041+ {
4042+ if (![song cachedSongExists])
4043+ {
4044+ [[Downloader sharedDownloader] downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:song.songId] withName:song.songId completionBlock:^(NSString *path) {
4045+ dispatch_async(dispatch_get_main_queue(), ^(void) {
4046+ song.cachedSongPath = path;
4047+ SaveContext();
4048+ });
4049+ }];
4050+ }
4051+ }
4052+ });
4053+ }
4054+ });
4055+ }
4056+}
4057+
4058 #pragma mark - Data Loading
4059
4060 - (void)loadLocalData
4061@@ -94,7 +128,7 @@
4062 {
4063 [super updateReachability:reachability];
4064
4065- for (UITableViewCell *cell in [self.tableView visibleCells])
4066+ for (ArtistUITableViewCell *cell in [self.tableView visibleCells])
4067 {
4068 [self configureCell:cell forTableView:self.tableView indexPath:[self.tableView indexPathForCell:cell]];
4069 }
4070@@ -106,16 +140,16 @@
4071 {
4072 static NSString *CellIdentifier = @"Cell";
4073
4074- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
4075+ ArtistUITableViewCell *cell = (ArtistUITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
4076 if (nil == cell)
4077 {
4078- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
4079+ cell = [[[ArtistUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
4080 }
4081 [self configureCell:cell forTableView:tableView indexPath:indexPath];
4082 return cell;
4083 }
4084
4085-- (void)configureCell:(UITableViewCell*)cell forTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath
4086+- (void)configureCell:(ArtistUITableViewCell*)cell forTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath
4087 {
4088 Artist *artist = nil;
4089 if (self.searchDisplayController.searchResultsTableView == tableView)
4090@@ -137,6 +171,7 @@
4091 cell.accessoryType = (canStream || [artist hasCachedSongs]) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
4092 }
4093 }
4094+ cell.artist = artist;
4095
4096 cell.textLabel.text = artist.name;
4097 if (canStream || [artist hasCachedSongs])
4098@@ -147,6 +182,12 @@
4099 {
4100 cell.textLabel.textColor = [UIColor lightGrayColor];
4101 }
4102+
4103+// UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(downloadArtist:)];
4104+// longPressGesture.minimumPressDuration = 1.0f;
4105+// longPressGesture.numberOfTouchesRequired = 1;
4106+// [cell addGestureRecognizer:longPressGesture];
4107+// [longPressGesture release];
4108 }
4109
4110 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
4111
4112=== modified file 'view_controllers/ArtistViewController.m'
4113--- view_controllers/ArtistViewController.m 2011-06-21 17:04:19 +0000
4114+++ view_controllers/ArtistViewController.m 2012-02-03 20:23:18 +0000
4115@@ -33,11 +33,13 @@
4116 #import "AlbumUITableViewCell.h"
4117 #import "Artist.h"
4118 #import "Album.h"
4119+#import "Downloader.h"
4120 #import "Song.h"
4121
4122
4123 @interface ArtistViewController ()
4124 - (void)configureCell:(UITableViewCell*)cell forAlbumAtIndexPath:(NSIndexPath*)indexPath;
4125+- (void)downloadAlbum:(UILongPressGestureRecognizer*)sender;
4126 @end
4127
4128
4129@@ -60,7 +62,7 @@
4130 [super viewDidLoad];
4131 self.tableView.rowHeight = 60.0f;
4132 self.title = self.artist.name;
4133-
4134+
4135 loader = [[AlbumArtLoader alloc] init];
4136 loader.delegate = self;
4137 loader.imageSize = 120;
4138@@ -100,6 +102,30 @@
4139 [self.tableView reloadData];
4140 }
4141
4142+- (void)downloadAlbum:(UILongPressGestureRecognizer *)sender;
4143+{
4144+ if ((sender.state == UIGestureRecognizerStateBegan) && canCache)
4145+ {
4146+ Album *album = ((AlbumUITableViewCell *)sender.view).album;
4147+ dispatch_queue_t dispatch_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4148+ dispatch_async(dispatch_queue, ^{
4149+ [album loadSongs];
4150+ for (Song *song in [album songs])
4151+ {
4152+ if (![song cachedSongExists])
4153+ {
4154+ [[Downloader sharedDownloader] downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:song.songId] withName:song.songId completionBlock:^(NSString *path) {
4155+ dispatch_async(dispatch_get_main_queue(), ^(void) {
4156+ song.cachedSongPath = path;
4157+ SaveContext();
4158+ });
4159+ }];
4160+ }
4161+ }
4162+ });
4163+ }
4164+}
4165+
4166 - (void)beginLoadingRemoteData:(NSString*)force
4167 {
4168 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4169@@ -199,6 +225,7 @@
4170 if (0 == indexPath.section)
4171 {
4172 Album *album = [self.albums objectAtIndex:indexPath.row];
4173+ cell.album = album;
4174 cell.albumNameLabel.text = album.title;
4175
4176 NSError *error = nil;
4177@@ -226,6 +253,12 @@
4178 Song *song = [self.songs objectAtIndex:indexPath.row];
4179 cell.textLabel.text = song.title;
4180 }
4181+
4182+ UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(downloadAlbum:)];
4183+ longPressGesture.minimumPressDuration = 1.0f;
4184+ longPressGesture.numberOfTouchesRequired = 1;
4185+// [cell addGestureRecognizer:longPressGesture];
4186+ [longPressGesture release];
4187 }
4188
4189 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
4190
4191=== modified file 'view_controllers/PlaylistListViewController.m'
4192--- view_controllers/PlaylistListViewController.m 2011-06-21 17:04:19 +0000
4193+++ view_controllers/PlaylistListViewController.m 2012-02-03 20:23:18 +0000
4194@@ -18,13 +18,20 @@
4195 // along with this program. If not, see <http://www.gnu.org/licenses/>.
4196
4197 #import "PlaylistListViewController.h"
4198+
4199+#import "Downloader.h"
4200 #import "PlaylistViewController.h"
4201 #import "PlaylistEditViewController.h"
4202 #import "PlaylistListParser.h"
4203+#import "PlaylistUITableViewCell.h"
4204 #import "Playlist.h"
4205 #import "CachedSongsPlaylist.h"
4206 #import "AlertPrompt.h"
4207
4208+@interface PlaylistListViewController ()
4209+- (void)downloadPlaylist:(UILongPressGestureRecognizer*)sender;
4210+@end
4211+
4212 @implementation PlaylistListViewController
4213
4214 + (UINavigationController *)navigableViewController
4215@@ -61,6 +68,30 @@
4216 return self;
4217 }
4218
4219+- (void)downloadPlaylist:(UILongPressGestureRecognizer *)sender;
4220+{
4221+ if ((sender.state == UIGestureRecognizerStateBegan) && canCache)
4222+ {
4223+ Playlist *playlist = ((PlaylistUITableViewCell *)sender.view).playlist;
4224+ dispatch_queue_t dispatch_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4225+ dispatch_async(dispatch_queue, ^{
4226+ [playlist loadSongs];
4227+ for (Song *song in [playlist songs])
4228+ {
4229+ if (![song cachedSongExists])
4230+ {
4231+ [[Downloader sharedDownloader] downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:song.songId] withName:song.songId completionBlock:^(NSString *path) {
4232+ dispatch_async(dispatch_get_main_queue(), ^(void) {
4233+ song.cachedSongPath = path;
4234+ SaveContext();
4235+ });
4236+ }];
4237+ }
4238+ }
4239+ });
4240+ }
4241+}
4242+
4243 #pragma mark - Data Loading
4244
4245 - (void)loadLocalData
4246@@ -93,10 +124,10 @@
4247 {
4248 static NSString *CellIdentifier = @"Cell";
4249
4250- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
4251+ PlaylistUITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
4252 if (nil == cell)
4253 {
4254- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
4255+ cell = [[[PlaylistUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
4256 }
4257
4258 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
4259@@ -111,7 +142,17 @@
4260 p = [self.tableData objectAtIndex:indexPath.row];
4261 }
4262
4263+ cell.playlist = p;
4264 cell.textLabel.text = p.name;
4265+
4266+// if (p.playlistId != nil)
4267+// {
4268+// UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(downloadPlaylist:)];
4269+// longPressGesture.minimumPressDuration = 1.0f;
4270+// longPressGesture.numberOfTouchesRequired = 1;
4271+// [cell addGestureRecognizer:longPressGesture];
4272+// [longPressGesture release];
4273+// }
4274 return cell;
4275 }
4276
4277
4278=== modified file 'view_controllers/PlaylistViewController.m'
4279--- view_controllers/PlaylistViewController.m 2011-11-05 00:42:21 +0000
4280+++ view_controllers/PlaylistViewController.m 2012-02-03 20:23:18 +0000
4281@@ -86,7 +86,7 @@
4282 [self.songs removeAllObjects];
4283 if ([self.playlist isKindOfClass:[NSManagedObject class]])
4284 [PerThreadManagedObjectContext() refreshObject:self.playlist mergeChanges:YES]; //force the playlist to reload its set of songs from the persistent store.
4285- [self.songs addObjectsFromArray:self.playlist.songs];
4286+ [self.songs addObjectsFromArray:[self.playlist songs]];
4287 // this should sort based on something else (like the index on the playlist or something)
4288 // [self.songs sortUsingSelector:@selector(compare:)];
4289 }
4290
4291=== modified file 'xibs/MainWindow.xib'
4292--- xibs/MainWindow.xib 2011-11-01 15:14:34 +0000
4293+++ xibs/MainWindow.xib 2012-02-03 20:23:18 +0000
4294@@ -47,7 +47,6 @@
4295 <object class="NSPSMatrix" key="NSFrameMatrix"/>
4296 <string key="NSFrameSize">{320, 480}</string>
4297 <reference key="NSSuperview"/>
4298- <reference key="NSNextKeyView"/>
4299 <object class="NSColor" key="IBUIBackgroundColor">
4300 <int key="NSColorSpace">1</int>
4301 <bytes key="NSRGB">MSAxIDEAA</bytes>
4302@@ -169,7 +168,6 @@
4303 <int key="NSvFlags">266</int>
4304 <string key="NSFrame">{{0, 431}, {320, 49}}</string>
4305 <reference key="NSSuperview"/>
4306- <reference key="NSNextKeyView"/>
4307 <object class="NSColor" key="IBUIBackgroundColor">
4308 <int key="NSColorSpace">3</int>
4309 <bytes key="NSWhite">MCAwAA</bytes>

Subscribers

People subscribed via source and target branches