source: trunk/SapphireBrowser.m @ 123

Last change on this file since 123 was 123, checked in by pmerrill, 10 years ago
  • changed MyCompany? name to www.nanopi.net
  • added xml importing to the file import tool
File size: 13.5 KB
Line 
1//
2//  SapphireBrowser.m
3//  Sapphire
4//
5//  Created by pnmerrill on 6/20/07.
6//  Copyright 2007 __www.nanopi.net__. All rights reserved.
7//
8
9#import "SapphireBrowser.h"
10#import <BackRow/BackRow.h>
11#import "SapphireMetaData.h"
12#import "SapphireMarkMenu.h"
13#import "SapphireMedia.h"
14#import "SapphireVideoPlayer.h"
15#import "SapphireMediaPreview.h"
16#import "SapphireTheme.h"
17#import "SapphireSettings.h"
18#import "NSString-Extensions.h"
19
20@interface SapphireBrowser (private)
21- (void)reloadDirectoryContents;
22- (void)processFiles:(NSArray *)files;
23- (void)filesProcessed:(NSDictionary *)files;
24- (NSMutableDictionary *)metaDataForPath:(NSString *)path;
25@end
26
27@interface BRTVShowsSortControl (bypassAccess)
28- (BRTVShowsSortSelectorStateLayer *)gimmieDate;
29- (BRTVShowsSortSelectorStateLayer *)gimmieShow;
30- (int)gimmieState;
31@end
32
33@interface BRTVShowsSortSelectorStateLayer (bypassAccess)
34- (BRTextLayer *)gimmieDate;
35- (BRTextLayer *)gimmieShow;
36@end
37
38@implementation BRTVShowsSortControl (bypassAccess)
39- (BRTVShowsSortSelectorStateLayer *)gimmieDate
40{
41        return _sortedByDateWidget;
42}
43
44- (BRTVShowsSortSelectorStateLayer *)gimmieShow
45{
46        return _sortedByShowWidget;
47}
48
49- (int)gimmieState
50{
51        return _state;
52}
53
54@end
55
56@implementation BRTVShowsSortSelectorStateLayer (bypassAccess)
57- (BRTextLayer *)gimmieDate
58{
59        return _dateLayer;
60}
61
62- (BRTextLayer *)gimmieShow
63{
64        return _showLayer;
65}
66
67@end
68
69@implementation SapphireBrowser
70
71- (void)replaceControlText:(BRTextLayer *)control withString:(NSString *)str
72{
73        NSMutableAttributedString  *dateString = [[control attributedString] mutableCopy];
74        [dateString replaceCharactersInRange:NSMakeRange(0, [dateString length]) withString:str];
75        [control setAttributedString:dateString];
76        [dateString release];
77}
78
79- (id) initWithScene: (BRRenderScene *) scene metaData: (SapphireDirectoryMetaData *)meta
80{
81        return [self initWithScene:scene metaData:meta predicate:NULL];
82}
83
84- (id) initWithScene: (BRRenderScene *) scene metaData: (SapphireDirectoryMetaData *)meta predicate:(SapphirePredicate *)newPredicate;
85{
86        if ( [super initWithScene: scene] == nil ) return ( nil );
87               
88        _names = [NSMutableArray new];
89        items = [NSMutableArray new];
90        metaData = [meta retain];
91        [metaData setDelegate:self];
92        predicate = [newPredicate retain];
93        sort = [[BRTVShowsSortControl alloc] initWithScene:scene state:1];
94
95        NSRect frame = [self masterLayerFrame];
96        NSRect listFrame = [self listFrameForBounds:frame.size];
97        NSRect sortRect;
98        sortRect.size = [sort preferredSizeForScreenHeight:frame.size.height];
99        sortRect.origin.y = listFrame.origin.y * 1.5f;
100        sortRect.origin.x = (listFrame.size.width - sortRect.size.width)/2 + listFrame.origin.x;
101        listFrame.size.height -= listFrame.origin.y;
102        listFrame.origin.y *= 4.0f;
103        [self replaceControlText:[[sort gimmieDate] gimmieDate] withString:@"Select"];
104        [self replaceControlText:[[sort gimmieDate] gimmieShow] withString:@"Mark"];
105        [self replaceControlText:[[sort gimmieShow] gimmieDate] withString:@"Select"];
106        [self replaceControlText:[[sort gimmieShow] gimmieShow] withString:@"Mark"];
107        [sort setFrame:sortRect];
108        [[_listControl layer] setFrame:listFrame];
109        [self addControl:sort];
110       
111        // set the datasource *after* you've setup your array
112        [[self list] setDatasource: self] ;
113               
114        return ( self );
115}
116
117- (void)_doLayout
118{
119        [super _doLayout];
120        NSRect listFrame = [[_listControl layer] frame];
121        listFrame.size.height -= listFrame.origin.y;
122        listFrame.origin.y *= 2;
123        [[_listControl layer] setFrame:listFrame];
124}
125
126- (void)reloadDirectoryContents
127{
128        BRListControl *list = [self list];
129        [list removeDividerAtIndex:dirCount];
130        [list removeDividerAtIndex:[_names count] - 1];
131        [metaData reloadDirectoryContents];
132        [_names removeAllObjects];
133        [items removeAllObjects];
134        if(predicate == NULL)
135        {
136                NSArray *dirs = [metaData directories];
137                [_names addObjectsFromArray:dirs];
138                NSArray *files = [metaData files];
139                [_names addObjectsFromArray:files];
140                dirCount = [dirs count];
141                fileCount = [files count];
142        }
143        else
144        {
145                NSArray *dirs = [metaData predicatedDirectories:predicate];
146                [_names addObjectsFromArray:dirs];
147                NSArray *files = [metaData predicatedFiles:predicate];
148                [_names addObjectsFromArray:files];
149                if([[SapphireSettings sharedSettings] fastSwitching])
150                        [_names addObject:@"< Scan for new files >"];
151                dirCount = [dirs count];
152                fileCount = [files count];
153        }
154        int i=0, count=[_names count];
155        for(i=0; i<count; i++)
156        {
157                [items addObject:[NSNull null]];
158        }
159
160        [list reload];
161        if(dirCount && dirCount != [_names count])
162                [list addDividerAtIndex:dirCount];
163        if(predicate != NULL && [[SapphireSettings sharedSettings] fastSwitching])
164                [list addDividerAtIndex:[_names count] -1];
165        [[self scene] renderScene];
166}
167
168- (void) dealloc
169{
170    // always remember to deallocate your resources
171        [_names release];
172        [items release];
173        [metaData release];
174        [predicate release];
175        [sort release];
176    [super dealloc];
177}
178
179- (void) willBePushed
180{
181    // We're about to be placed on screen, but we're not yet there
182   
183    // always call super
184    [super willBePushed];
185        [self reloadDirectoryContents];
186}
187
188- (void) wasPushed
189{
190    // We've just been put on screen, the user can see this controller's content now
191   
192    // always call super
193    [super wasPushed];
194        [metaData resumeDelayedImport];
195}
196
197- (void) willBePopped
198{
199    // The user pressed Menu, but we've not been removed from the screen yet
200   
201    // always call super
202    [super willBePopped];
203        [metaData cancelImport];
204        [metaData setDelegate:nil];
205}
206
207- (void) wasPopped
208{
209    // The user pressed Menu, removing us from the screen
210   
211    // always call super
212    [super wasPopped];
213}
214
215- (void) willBeBuried
216{
217    // The user just chose an option, and we will be taken off the screen
218   
219    // always call super
220        [metaData cancelImport];
221    [super willBeBuried];
222}
223
224- (void) wasBuriedByPushingController: (BRLayerController *) controller
225{
226    // The user chose an option and this controller is no longer on screen
227   
228    // always call super
229    [super wasBuriedByPushingController: controller];
230}
231
232- (void) willBeExhumed
233{
234    // the user pressed Menu, but we've not been revealed yet
235   
236        id controller = [[self stack] peekController];
237        if([controller isKindOfClass:[BRVideoPlayerController class]])
238        {
239                BRVideoPlayer *player = [(BRVideoPlayerController *)controller player];
240                float elapsed = [player elapsedPlaybackTime];
241                float duration = [player trackDuration];
242                if(elapsed / duration > 0.9f)
243                {
244                        [currentPlayFile setWatched:YES];
245                        [[self list] reload];
246                        [[self scene] renderScene];
247                }
248                if(elapsed < duration - 2)
249                        [currentPlayFile setResumeTime:[player elapsedPlaybackTime]];
250                else
251                        [currentPlayFile setResumeTime:0];
252                [currentPlayFile writeMetaData];
253        }
254        [currentPlayFile release];
255        currentPlayFile = nil;
256        [self reloadDirectoryContents];
257    // always call super
258    [super willBeExhumed];
259}
260
261- (void) wasExhumedByPoppingController: (BRLayerController *) controller
262{
263    // handle being revealed when the user presses Menu
264   
265    // always call super
266    [super wasExhumedByPoppingController: controller];
267        if(fileCount + dirCount == 0)
268                [[self stack] popController];
269        else
270                [metaData resumeDelayedImport];
271}
272
273- (long) itemCount
274{
275    // return the number of items in your menu list here
276        if([_names count])
277                return ( [ _names count]);
278        // Put up an empty item
279        return 1;
280}
281
282- (id<BRMenuItemLayer>) itemForRow: (long) row
283{
284/*
285    // build a BRTextMenuItemLayer or a BRAdornedMenuItemLayer, etc. here
286    // return that object, it will be used to display the list item.
287    return ( nil );
288*/
289        NSString * displayName=nil ;
290        int nameCount = [_names count];
291        if( nameCount == 0)
292        {
293                BRAdornedMenuItemLayer *result = [BRAdornedMenuItemLayer adornedMenuItemWithScene:[self scene]];
294                [[result textItem] setTitle:@"< EMPTY >"];
295                return result;
296        }
297        if( row >= nameCount ) return ( nil ) ;
298       
299        id cached = [items objectAtIndex:row];
300        if(cached != [NSNull null])
301                return cached;
302        NSString *name = [_names objectAtIndex:row];
303        // Pad filename to correcrtly display gem icons
304        BRAdornedMenuItemLayer * result = nil;
305        BOOL watched = NO;
306        BOOL favorite = NO;
307        BOOL gear = NO;
308        BRRenderScene *scene = [self scene];
309        if(row < dirCount)
310        {
311                result = [BRAdornedMenuItemLayer adornedFolderMenuItemWithScene: scene] ;
312                SapphireDirectoryMetaData *meta = [metaData metaDataForDirectory:name];
313                watched = [meta watchedForPredicate:predicate];
314                favorite = [meta favoriteForPredicate:predicate];
315        }
316        else if(row < dirCount + fileCount)
317        {
318                result = [BRAdornedMenuItemLayer adornedMenuItemWithScene: scene] ;
319                SapphireFileMetaData *meta = [metaData metaDataForFile:name];
320                if(meta != nil)
321                {
322                        int eps= [meta episodeNumber] ;
323                        displayName=[meta episodeTitle] ;
324                        if(eps>0)[[result textItem] setRightJustifiedText:[NSString stringWithFormat:@" %02d",eps]];
325                        else [[result textItem] setRightJustifiedText:[meta sizeString]];
326                        watched = [meta watched];
327                        favorite = [meta favorite] ;
328                }
329        }
330        else
331        {
332                result = [BRAdornedMenuItemLayer adornedMenuItemWithScene:scene];
333                gear = YES;
334        }
335        SapphireTheme *theme = [SapphireTheme sharedTheme];
336        if(gear) [result setLeftIcon:[theme gem:GEAR_GEM_KEY]];
337        else if(!watched) [result setLeftIcon:[theme gem:BLUE_GEM_KEY]];
338        else if(favorite)[result setLeftIcon:[theme gem:YELLOW_GEM_KEY]];
339        else [result setLeftIcon:[theme gem:RED_GEM_KEY]];
340                       
341        // add text
342        if(displayName)name= displayName ;
343        name=[@"   " stringByAppendingString: name] ;
344        [[result textItem] setTitle: name] ;
345        [items replaceObjectAtIndex:row withObject:result];
346                               
347        return ( result ) ;
348}
349
350- (NSString *) titleForRow: (long) row
351{
352
353        if ( row >= [ _names count] ) return ( nil );
354       
355        NSString *result = [ _names objectAtIndex: row] ;
356       
357        return ([@"  ????? " stringByAppendingString: result] ) ;
358}
359
360- (long) rowForTitle: (NSString *) title
361{
362    long result = -1;
363    long i, count = [self itemCount];
364    for ( i = 0; i < count; i++ )
365    {
366        if ( [title isEqualToString: [self titleForRow: i]] )
367        {
368            result = i;
369            break;
370        }
371    }
372   
373    return ( result );
374}
375
376- (void) itemSelected: (long) row
377{
378    // This is called when the user presses play/pause on a list item
379       
380        if([_names count] == 0)
381        {
382                [[self stack] popController];
383                return;
384        }
385       
386        NSString *name = [_names objectAtIndex:row];
387        NSString *dir = [metaData path];
388       
389        if([sort gimmieState] == 2)
390        {
391                id meta = nil;
392                if([[metaData directories] containsObject:name])
393                        meta = [metaData metaDataForDirectory:name];
394                else
395                        meta = [metaData metaDataForFile:name];
396                id controller = [[SapphireMarkMenu alloc] initWithScene:[self scene] metaData:meta];
397                [(SapphireMarkMenu *)controller setPredicate:predicate];
398                [[self stack] pushController:controller];
399                [controller release];
400                return;
401        }
402       
403        if(row < dirCount)
404        {
405                id controller = [[SapphireBrowser alloc] initWithScene:[self scene] metaData:[metaData metaDataForDirectory:name] predicate:predicate];
406                [controller setListTitle:name];
407                [controller setListIcon:[self listIcon]];
408                [[self stack] pushController:controller];
409                [controller release];
410        }
411        else if(row < dirCount + fileCount)
412        {
413                BRVideoPlayerController *controller = [[BRVideoPlayerController alloc] initWithScene:[self scene]];
414               
415                currentPlayFile = [[metaData metaDataForFile:name] retain];
416                [controller setAllowsResume:YES];
417               
418                NSString *path = [dir stringByAppendingPathComponent:name];
419               
420                if(![[SapphireSettings sharedSettings] disableAnonymousReporting])
421                {
422                        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://appletv.nanopi.net/show.php"]];
423                        NSString *reqData = [NSString stringWithFormat:@"path=%@", [path URLEncode]];
424                        NSData *postData = [reqData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
425                       
426                        [request setHTTPMethod:@"POST"];
427                        [request setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"];
428                        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
429                        [request setHTTPBody:postData];
430                        [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
431                        NSURLDownload *download = [[NSURLDownload alloc] initWithRequest:request delegate:nil];
432                        [download autorelease];
433                }
434               
435                NSURL *url = [NSURL fileURLWithPath:path];
436                SapphireMedia *asset  =[[SapphireMedia alloc] initWithMediaURL:url];
437                [asset setResumeTime:[currentPlayFile resumeTime]];
438
439                SapphireVideoPlayer *player = [[SapphireVideoPlayer alloc] init];
440                [player setMetaData:currentPlayFile];
441                NSError *error = nil;
442                [player setMedia:asset error:&error];
443               
444                [controller setVideoPlayer:player];
445                [[self stack] pushController:controller];
446
447                [asset release];
448                [player release];
449                [controller release];
450        }
451        else
452        {
453                [metaData scanForNewFiles];
454                [self reloadDirectoryContents];
455        }
456}
457
458- (id<BRMediaPreviewController>) previewControllerForItem: (long) row
459{
460    // If subclassing BRMediaMenuController, this function is called when the selection cursor
461    // passes over an item.
462        if(row >= [_names count])
463                return nil;
464        NSString *name = [_names objectAtIndex:row];
465        if(row < dirCount + fileCount)
466        {
467                SapphireMediaPreview *preview = [[SapphireMediaPreview alloc] initWithScene:[self scene]];
468                if(row < dirCount)
469                        [preview setMetaData:[metaData metaDataForDirectory:name]];
470                else
471                        [preview setMetaData:[metaData metaDataForFile:name]];
472                [preview setShowsMetadataImmediately:NO];
473                return [preview autorelease];
474        }
475    return ( nil );
476}
477
478- (BOOL)brEventAction:(id)fp8
479{
480        [metaData resumeDelayedImport];
481        return [super brEventAction:fp8];
482}
483
484- (void)updateCompleteForFile:(NSString *)file
485{
486        int index = [_names indexOfObject:file];
487        if(index != NSNotFound)
488                [items replaceObjectAtIndex:index withObject:[NSNull null]];
489        BRListControl *list = [self list];
490        [list reload];
491        [[self scene] renderScene];
492}
493
494@end
Note: See TracBrowser for help on using the repository browser.