Develop and Download Open Source Software

Browse Subversion Repository

Contents of /XspfQT/XspfQTDocument.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 378 - (show annotations) (download)
Sun Mar 4 02:31:49 2012 UTC (12 years ago) by masakih
File size: 16619 byte(s)
[New] XspfQTMovieLoaderにdelegateを引数にとらないイニシャライザを追加

1 //
2 // XspfQTDocument.m
3 // XspfQT
4 //
5 // Created by Hori,Masaki on 08/08/29.
6 //
7
8 /*
9 This source code is release under the New BSD License.
10 Copyright (c) 2008-2010,2012, masakih
11 All rights reserved.
12
13 ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満たす場合に
14 限り、再頒布および使用が許可されます。
15
16 1, ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項を含
17 めること。
18 2, バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作権表
19 示、本条件一覧、および下記免責条項を含めること。
20 3, 書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進に、
21 コントリビューターの名前を使用してはならない。
22 本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供されており、
23 明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性に関する暗黙の保証
24 も含め、またそれに限定されない、いかなる保証もありません。著作権者もコントリビューター
25 も、事由のいかんを問わず、 損害発生の原因いかんを問わず、かつ責任の根拠が契約であるか
26 厳格責任であるか(過失その他の)不法行為であるかを問わず、仮にそのような損害が発生する
27 可能性を知らされていたとしても、本ソフトウェアの使用によって発生した(代替品または代用
28 サービスの調達、使用の喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定
29 されない)直接損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害につい
30 て、一切責任を負わないものとします。
31 -------------------------------------------------------------------
32 Copyright (c) 2008-2010,2012, masakih
33 All rights reserved.
34
35 Redistribution and use in source and binary forms, with or without
36 modification, are permitted provided that the following conditions
37 are met:
38
39 1, Redistributions of source code must retain the above copyright
40 notice, this list of conditions and the following disclaimer.
41 2, Redistributions in binary form must reproduce the above copyright
42 notice, this list of conditions and the following disclaimer in
43 the documentation and/or other materials provided with the
44 distribution.
45 3, The names of its contributors may be used to endorse or promote
46 products derived from this software without specific prior
47 written permission.
48 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
51 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
52 COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
53 INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
54 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
55 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
56 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
58 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 POSSIBILITY OF SUCH DAMAGE.
60 */
61
62 #import "XspfQTDocument.h"
63 #import "XspfQTAppDelegate.h"
64 #import "XspfQTPreference.h"
65 #import "HMXSPFComponent.h"
66 #import "XspfQTMovieWindowController.h"
67 #import "XspfQTPlayListWindowController.h"
68 #import <QTKit/QTKit.h>
69
70 #import "NSURL-HMExtensions.h"
71 #import "XspfQTMovieLoader.h"
72 #import "XspfQTValueTransformers.h"
73
74 #import "XspfQTMovieTimer.h"
75
76
77 #pragma mark #### Global Variables ####
78 /********* Global variables *******/
79 NSString *XspfQTDocumentWillCloseNotification = @"XspfQTDocumentWillCloseNotification";
80
81 /**********************************/
82
83 @interface XspfQTDocument()
84 @property (retain) HMXSPFComponent *playlist;
85 @property (retain) QTMovie *playingMovie;
86 @property (readonly) NSXMLDocument *XMLDocument;
87
88 // private
89 @property (retain) XspfQTMovieLoader *loader;
90 @property NSTimeInterval playingMovieDuration;
91 @property BOOL didPreloading;
92 @end
93
94 @interface XspfQTDocument (Private)
95 - (NSData *)dataFromURL:(NSURL *)url error:(NSError **)outError;
96
97 inline static BOOL isXspfFileType(NSString *typeName);
98 inline static BOOL isReadableMovieType(NSString *typeName);
99 @end
100
101 static NSString *XspfDocumentType = @"XML Shareable Playlist Format";
102 static NSString *QuickTimeMovieDocumentType = @"QuickTime Movie";
103 static NSString *MatroskaVideoDocumentType = @"Matroska Video";
104 static NSString *DivXMediaFormatDocumentType = @"DivX Media Format";
105
106 static NSString *XspfUTI = @"com.masakih.xspf";
107
108 static NSString *XspfQTCurrentTrackKey = @"currentTrack";
109
110 @implementation XspfQTDocument
111 @synthesize playlist = _playlist;
112 @synthesize playingMovie = _playingMovie;
113
114 @synthesize loader = _loader;
115 @synthesize playingMovieDuration = _playingMovieDuration;
116 @synthesize didPreloading = _didPreloading;
117
118 static XspfQTMovieTimer* timer = nil;
119 + (void)initialize
120 {
121 timer = [[XspfQTMovieTimer movieTimer] retain];
122 }
123
124 - (id)init
125 {
126 self = [super init];
127 if(self) {
128 self.loader = [[XspfQTMovieLoader loaderWithMovieURL:nil] retain];
129 }
130
131 return self;
132 }
133 - (id)initWithType:(NSString *)typeName error:(NSError **)outError
134 {
135 [self init];
136
137 id newPlaylist = [HMXSPFComponent xspfPlaylist];
138 if(!newPlaylist) {
139 [self autorelease];
140 return nil;
141 }
142
143 self.playlist = newPlaylist;
144
145 return self;
146 }
147 - (void)dealloc
148 {
149 self.playingMovie = nil;
150 self.playlist = nil;
151 [playListWindowController release];
152 [movieWindowController release];
153 self.loader = nil;
154
155 [super dealloc];
156 }
157
158 - (void)makeWindowControllers
159 {
160 playListWindowController = [[XspfQTPlayListWindowController alloc] init];
161 [self addWindowController:playListWindowController];
162
163 movieWindowController = [[XspfQTMovieWindowController alloc] init];
164 [movieWindowController setShouldCloseDocument:YES];
165 [self addWindowController:movieWindowController];
166
167 [movieWindowController showWindow:nil];
168
169 [timer put:self];
170 }
171
172 - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
173 {
174 return [self.XMLDocument XMLDataWithOptions:NSXMLNodePrettyPrint];
175 }
176
177 - (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
178 {
179 if(!isReadableMovieType(typeName)) {
180 NSData *data = [self dataFromURL:absoluteURL error:outError];
181 if(!data) return NO;
182
183 return [self readFromData:data ofType:typeName error:outError];
184 }
185
186 id new = [HMXSPFComponent xspfTrackWithLocation:absoluteURL];
187 if(!new) {
188 if(outError) {
189 *outError = [NSError errorWithDomain:@"XspfQTErrorDomain" code:1 userInfo:nil];
190 }
191 return NO;
192 }
193
194 id pl = [HMXSPFComponent xspfPlaylist];
195 if(!pl) {
196 return NO;
197 }
198
199 [[[pl children] objectAtIndex:0] addChild:new];
200
201 self.playlist = pl;
202 HMXSPFComponent *t = self.trackList;
203 if(!t.title) {
204 t.title = [[[self fileURL] path] lastPathComponent];
205 }
206
207 [self setFileType:XspfDocumentType];
208 [self setFileURL:nil];
209
210 return YES;
211 }
212 - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
213 {
214 if(!isXspfFileType(typeName)) {
215 return NO;
216 }
217
218 NSError *error = nil;
219 NSXMLDocument *d = [[[NSXMLDocument alloc] initWithData:data
220 options:0
221 error:&error] autorelease];
222 if(error) {
223 NSLog(@"%@", error);
224 if(outError) {
225 *outError = error;
226 }
227 return NO;
228 }
229 NSXMLElement *root = [d rootElement];
230 id pl = [HMXSPFComponent xspfComponentWithXMLElement:root];
231 if(!pl) {
232 NSLog(@"Can not create HMXSPFComponent.");
233 return NO;
234 }
235 self.playlist = pl;
236
237 HMXSPFComponent *t = self.trackList;
238 if(!t.title) {
239 t.title = [[[[self fileURL] path] lastPathComponent] stringByDeletingPathExtension];
240 }
241
242 return YES;
243 }
244
245 - (void)close
246 {
247 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
248 [nc postNotificationName:XspfQTDocumentWillCloseNotification object:self];
249
250 [self removeWindowController:playListWindowController];
251 [playListWindowController release];
252 playListWindowController = nil;
253
254 [self removeWindowController:movieWindowController];
255 [movieWindowController release];
256 movieWindowController = nil;
257
258 [super close];
259 }
260
261 #pragma mark### Actions ###
262 - (IBAction)togglePlayAndPause:(id)sender
263 {
264 [movieWindowController togglePlayAndPause:sender];
265 }
266 - (IBAction)showPlayList:(id)sender
267 {
268 [playListWindowController showWindow:self];
269 }
270 - (IBAction)showHidePlayList:(id)sender
271 {
272 [playListWindowController showHideWindow:self];
273 }
274 - (IBAction)setThumbnailFrame:(id)sender
275 {
276 HMXSPFComponent *currentTrack = self.trackList.currentTrack;
277 QTTime currentQTTime = [self.playingMovie currentTime];
278
279 NSTimeInterval currentTI;
280 QTGetTimeInterval(currentQTTime, &currentTI);
281
282 HMXSPFComponent *prevThumbnailTrack = self.playlist.thumbnailTrack;
283 NSTimeInterval ti = self.playlist.thumbnailTimeInterval;
284
285 [self.playlist setThumbnailComponent:currentTrack timeIntarval:currentTI];
286
287 id undo = [self undoManager];
288 if(prevThumbnailTrack) {
289 [[undo prepareWithInvocationTarget:self.playlist] setThumbnailComponent:prevThumbnailTrack timeIntarval:ti];
290 [undo setActionName:NSLocalizedString(@"Change Thumbnail frame.", @"Undo Action Name Change Thumbnail frame")];
291 } else {
292 [[undo prepareWithInvocationTarget:self.playlist] removeThumbnailFrame];
293 [undo setActionName:NSLocalizedString(@"Add Thumbnail frame.", @"Undo Action Name Add Thumbnail frame")];
294 }
295 }
296 - (IBAction)removeThumbnail:(id)sender
297 {
298 HMXSPFComponent *prevThumbnailTrack = self.playlist.thumbnailTrack;
299 NSTimeInterval ti = self.playlist.thumbnailTimeInterval;
300
301 [self.playlist removeThumbnailFrame];
302
303 if(prevThumbnailTrack) {
304 id undo = [self undoManager];
305 [[undo prepareWithInvocationTarget:self.playlist] setThumbnailComponent:prevThumbnailTrack timeIntarval:ti];
306 [undo setActionName:NSLocalizedString(@"Remove Thumbnail frame.", @"Undo Action Name Remove Thumbnail frame")];
307 }
308 }
309 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
310 {
311 SEL action = [menuItem action];
312
313 if(action == @selector(removeThumbnail:)) {
314 HMXSPFComponent *component = self.playlist.thumbnailTrack;
315 if(!component) return NO;
316 }
317
318 return YES;
319 }
320
321 - (void)setPlaylist:(HMXSPFComponent *)newList
322 {
323 if(_playlist == newList) return;
324
325 [[_playlist childAtIndex:0] removeObserver:self forKeyPath:XspfQTCurrentTrackKey];
326 [_playlist autorelease];
327 _playlist = [newList retain];
328 [[_playlist childAtIndex:0] addObserver:self
329 forKeyPath:XspfQTCurrentTrackKey
330 options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
331 context:NULL];
332 }
333 - (HMXSPFComponent *)playlist
334 {
335 return _playlist;
336 }
337
338 - (HMXSPFComponent *)trackList
339 {
340 return [self.playlist childAtIndex:0];
341 }
342
343 + (NSSet *)keyPathsForValuesAffectingPlayingMovieDuration
344 {
345 return [NSSet setWithObject:@"playingMovie"];
346 }
347 - (void)setPlayingMovie:(QTMovie *)newMovie
348 {
349 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
350 if(_playingMovie) {
351 [nc removeObserver:self
352 name:nil
353 object:_playingMovie];
354 }
355
356 [_playingMovie autorelease];
357 _playingMovie = [newMovie retain];
358 self.playingMovieDuration = 0;
359
360 if(_playingMovie) {
361 [nc addObserver:self
362 selector:@selector(notifee:)
363 name:QTMovieRateDidChangeNotification
364 object:_playingMovie];
365 }
366 }
367 - (QTMovie *)playingMovie
368 {
369 return _playingMovie;
370 }
371 - (void)setPlayingMovieDuration:(NSTimeInterval)playingMovieDuration
372 {
373 _playingMovieDuration = playingMovieDuration;
374 }
375 - (NSTimeInterval)playingMovieDuration
376 {
377 if(_playingMovieDuration == 0) {
378 QTTime qttime = [self.playingMovie duration];
379 if(!QTGetTimeInterval(qttime, &_playingMovieDuration)) _playingMovieDuration = 0;
380 }
381
382 return _playingMovieDuration;
383 }
384 - (void)loadMovie
385 {
386 NSURL *location = self.trackList.movieLocation;
387
388 if(self.playingMovie) {
389 id movieURL = [self.playingMovie attributeForKey:QTMovieURLAttribute];
390 if([location isEqualUsingLocalhost:movieURL]) return;
391 }
392
393 self.loader.movieURL = location;
394 [self.loader load];
395 QTMovie *newMovie = self.loader.qtMovie;
396 self.playingMovie = newMovie;
397
398 QTTime qttime = [newMovie duration];
399 id t = [NSValueTransformer valueTransformerForName:@"XspfQTTimeDateTransformer"];
400 [self.trackList setCurrentTrackDuration:[t transformedValue:[NSValue valueWithQTTime:qttime]]];
401
402 self.didPreloading = NO;
403 }
404
405 - (void)observeValueForKeyPath:(NSString *)keyPath
406 ofObject:(id)object
407 change:(NSDictionary *)change
408 context:(void *)context
409 {
410 if([keyPath isEqualToString:XspfQTCurrentTrackKey]) {
411 [self loadMovie];
412 }
413 }
414
415 - (NSXMLDocument *)XMLDocument
416 {
417 id root = self.playlist.XMLElement;
418
419 id d = [[[NSXMLDocument alloc] initWithRootElement:root] autorelease];
420 [d setVersion:@"1.0"];
421 [d setCharacterEncoding:@"UTF-8"];
422
423 return d;
424 }
425
426 - (void)insertComponentFromURL:(NSURL *)url atIndex:(NSUInteger)index
427 {
428 id new = [HMXSPFComponent xspfTrackWithLocation:url];
429 if(!new) {
430 @throw self;
431 }
432
433 [self insertComponent:new atIndex:index];
434 }
435 - (void)insertComponent:(HMXSPFComponent *)item atIndex:(NSUInteger)index
436 {
437 id undo = [self undoManager];
438 [undo registerUndoWithTarget:self selector:@selector(removeComponent:) object:item];
439 [self.trackList insertChild:item atIndex:index];
440 [undo setActionName:NSLocalizedString(@"Insert Movie", @"Undo Action Name Insert Movie")];
441 }
442 - (void)removeComponent:(HMXSPFComponent *)item
443 {
444 NSUInteger index = [self.trackList indexOfChild:item];
445 if(index == NSNotFound) {
446 NSLog(@"Can not found item (%@)", item);
447 return;
448 }
449
450 id undo = [self undoManager];
451 [[undo prepareWithInvocationTarget:self] insertComponent:item atIndex:index];
452 [self.trackList removeChild:item];
453 [undo setActionName:NSLocalizedString(@"Remove Movie", @"Undo Action Name Remove Movie")];
454 }
455 - (void)moveComponentFromIndex:(NSUInteger)from toIndex:(NSUInteger)to
456 {
457 id undo = [self undoManager];
458 [[undo prepareWithInvocationTarget:self] moveComponentFromIndex:to toIndex:from];
459 [self.trackList moveChildFromIndex:from toIndex:to];
460 [undo setActionName:NSLocalizedString(@"Move Movie", @"Undo Action Name Move Movie")];
461 }
462 - (void)moveComponent:(HMXSPFComponent *)item toIndex:(NSUInteger)index
463 {
464 NSUInteger from = [self.trackList indexOfChild:item];
465 if(from == NSNotFound) return;
466 [self moveComponentFromIndex:from toIndex:index];
467 }
468
469 - (NSData *)dataFromURL:(NSURL *)url error:(NSError **)outError
470 {
471 NSURLRequest *req = [NSURLRequest requestWithURL:url];
472 NSURLResponse *res = nil;
473 NSError *err = nil;
474 NSData *data = [NSURLConnection sendSynchronousRequest:req
475 returningResponse:&res
476 error:&err];
477 if(err) {
478 if(outError) {
479 *outError = err;
480 }
481 return nil;
482 }
483
484 return data;
485 }
486
487 - (void)notifee:(id)notification
488 {
489 // NSLog(@"Notifed: name -> (%@)\ndict -> (%@)", [notification name], [notification userInfo]);
490
491 HMXSPFComponent *track = self.trackList.currentTrack;
492 NSNumber *rateValue = [[notification userInfo] objectForKey:QTMovieRateDidChangeNotificationParameter];
493 if(rateValue) {
494 float rate = [rateValue floatValue];
495 if(rate == 0) {
496 track.isPlayed = NO;
497 } else {
498 track.isPlayed = YES;
499 }
500 }
501 }
502
503 // call from XspfQTMovieTimer.
504 - (void)checkPreload:(NSTimer *)timer
505 {
506 if(![XspfQTPref preloadingEnabled]) return;
507 if(self.didPreloading) return;
508
509 NSTimeInterval duration;
510 NSTimeInterval current;
511 QTTime qttime = [self.playingMovie currentTime];
512 if(!QTGetTimeInterval(qttime, &current)) return;
513
514 duration = self.playingMovieDuration;
515
516 if( current / duration > XspfQTPref.beginingPreloadPercent ) {
517 self.didPreloading = YES;
518 HMXSPFComponent *list = self.trackList;
519 NSUInteger nextIndex = list.selectionIndex + 1;
520 NSUInteger max = list.childrenCount;
521 if(max <= nextIndex) return;
522
523 HMXSPFComponent *nextTrack = [list childAtIndex:nextIndex];
524 NSURL *nextMovieURL = nextTrack.movieLocation;
525 self.loader.movieURL = nextMovieURL;
526 [self.loader load];
527 }
528 }
529
530 inline static BOOL isXspfFileType(NSString *typeName)
531 {
532 return [typeName isEqualToString:XspfDocumentType] || [typeName isEqualToString:XspfUTI];
533 }
534 inline static BOOL isReadableMovieType(NSString *typeName)
535 {
536 return [typeName isEqualToString:QuickTimeMovieDocumentType]
537 || [typeName isEqualToString:MatroskaVideoDocumentType]
538 || [typeName isEqualToString:DivXMediaFormatDocumentType];
539 }
540
541 @end
542

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26