Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /XspfQT/XspfQTDocument.m

Parent Directory Parent Directory | Revision Log Revision Log


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

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