Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /XspfQT/XspfQTDocument.m

Parent Directory Parent Directory | Revision Log Revision Log


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

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