Controller.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*****************************************************************************
  2. * Copyright (C) 2007-2012 Pierre d'Herbemont and VideoLAN
  3. *
  4. * Authors: Pierre d'Herbemont
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation; either version 2.1 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  19. *****************************************************************************/
  20. #import "Controller.h"
  21. /**********************************************************
  22. * First off, some value transformer to easily play with
  23. * bindings
  24. */
  25. @interface VLCFloat10000FoldTransformer : NSObject
  26. @end
  27. @implementation VLCFloat10000FoldTransformer
  28. + (Class)transformedValueClass
  29. {
  30. return [NSNumber class];
  31. }
  32. + (BOOL)allowsReverseTransformation
  33. {
  34. return YES;
  35. }
  36. - (id)transformedValue:(id)value
  37. {
  38. if( !value ) return nil;
  39. if(![value respondsToSelector: @selector(floatValue)])
  40. {
  41. [NSException raise: NSInternalInconsistencyException
  42. format: @"Value (%@) does not respond to -floatValue.",
  43. [value class]];
  44. return nil;
  45. }
  46. return [NSNumber numberWithFloat: [value floatValue]*10000.];
  47. }
  48. - (id)reverseTransformedValue:(id)value
  49. {
  50. if( !value ) return nil;
  51. if(![value respondsToSelector: @selector(floatValue)])
  52. {
  53. [NSException raise: NSInternalInconsistencyException
  54. format: @"Value (%@) does not respond to -floatValue.",
  55. [value class]];
  56. return nil;
  57. }
  58. return [NSNumber numberWithFloat: [value floatValue]/10000.];
  59. }
  60. @end
  61. /**********************************************************
  62. * @implementation Controller
  63. */
  64. @interface Controller ()
  65. @property (retain,readwrite) NSString * outputFolderPath;
  66. @end
  67. @implementation Controller
  68. - (id)init
  69. {
  70. if(self = [super init])
  71. {
  72. [self bind:@"outputFolderPath" toObject:[NSUserDefaultsController sharedUserDefaultsController]
  73. withKeyPath:@"values.outputFolderPath" options:nil];
  74. [[[NSUserDefaultsController sharedUserDefaultsController] values] bind:@"outputFolderPath" toObject:self
  75. withKeyPath:@"outputFolderPath" options:nil];
  76. VLCFloat10000FoldTransformer *float100fold;
  77. float100fold = [[[VLCFloat10000FoldTransformer alloc] init] autorelease];
  78. [NSValueTransformer setValueTransformer:(id)float100fold forName:@"Float10000FoldTransformer"];
  79. self.media = nil;
  80. self.streamSession = nil;
  81. selectedStreamOutput = [[NSNumber alloc] initWithInt:0];
  82. self.remoteURLAsString = @"http://youtube.com/watch?v=IXpx2OEWBdA&feature=bz303";
  83. outputFilePath = nil;
  84. if( !self.outputFolderPath || [self.outputFolderPath isKindOfClass:[NSNull class]])
  85. self.outputFolderPath = [@"~/Movies/Flash Video Converted" stringByExpandingTildeInPath];
  86. }
  87. return self;
  88. }
  89. - (void)dealloc
  90. {
  91. [outputFilePath release];
  92. [remoteURLAsString release];
  93. [streamSession release];
  94. [media release];
  95. [super dealloc];
  96. }
  97. @synthesize streamSession;
  98. @synthesize selectedStreamOutput;
  99. @synthesize media;
  100. @synthesize outputFolderPath;
  101. - (void)awakeFromNib
  102. {
  103. [window setShowsResizeIndicator:NO];
  104. [NSApp setDelegate: self];
  105. }
  106. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
  107. {
  108. [VLCLibrary sharedLibrary];
  109. }
  110. - (NSString *)remoteURLAsString
  111. {
  112. return remoteURLAsString;
  113. }
  114. - (void)setRemoteURLAsString:(NSString *)newURLAsString
  115. {
  116. [remoteURLAsString release];
  117. remoteURLAsString = [[newURLAsString copy] retain];
  118. media = [[newURLAsString copy] retain];
  119. [self setMedia:[VLCMedia mediaWithPath:newURLAsString]];
  120. }
  121. + (NSSet *)keyPathsForValuesAffectingOutputFilePath
  122. {
  123. return [NSSet setWithObjects:@"media.metaDictionary.title", nil];
  124. }
  125. - (void)freezeOutputFilePath
  126. {
  127. [outputFilePath release];
  128. outputFilePath = nil;
  129. outputFilePath = [self outputFilePath];
  130. [outputFilePath retain];
  131. }
  132. - (NSString *)outputFilePath
  133. {
  134. if(outputFilePath)
  135. return [outputFilePath copy];
  136. VLCMedia * aMedia = self.streamSession ? self.streamSession.media ? self.streamSession.media : self.media : self.media;
  137. NSString * name = [[[aMedia metaDictionary] objectForKey:@"title"] lastPathComponent];
  138. NSString * extension = [selectedStreamOutput intValue] == 2 ? @"mpeg" : @"mp4";
  139. NSString * path = [NSString stringWithFormat:@"%@/%@.%@", self.outputFolderPath, name, extension ];
  140. int i;
  141. for( i = 0; [[NSFileManager defaultManager] fileExistsAtPath:path]; i ++)
  142. {
  143. path = [NSString stringWithFormat:@"%@/%@ %d.%@", self.outputFolderPath, name, i, extension ];
  144. if( i > 256 )
  145. {
  146. /* Don't got too far */
  147. /* FIXME: Be nicer with the user and give him a choice for the new name */
  148. NSRunAlertPanelRelativeToWindow(@"File already exists",
  149. [NSString stringWithFormat:
  150. @"File '%@', already exists. The old one will be deleted when the OK button will be pressed", path],
  151. @"OK", nil, nil, window);
  152. break;
  153. }
  154. }
  155. return path;
  156. }
  157. - (IBAction)convert:(id)sender
  158. {
  159. VLCStreamOutput * streamOutput;
  160. [self.streamSession removeObserver:self forKeyPath:@"isComplete"];
  161. self.streamSession = [VLCStreamSession streamSession];
  162. [self freezeOutputFilePath];
  163. if([selectedStreamOutput intValue] == 2)
  164. {
  165. streamOutput = [VLCStreamOutput mpeg2StreamOutputWithFilePath:[self outputFilePath]];
  166. }
  167. else if([selectedStreamOutput intValue] == 1)
  168. {
  169. streamOutput = [VLCStreamOutput mpeg4StreamOutputWithFilePath:[self outputFilePath]];
  170. }
  171. else
  172. streamOutput = [VLCStreamOutput ipodStreamOutputWithFilePath:[self outputFilePath]];
  173. /* Make sure we are exporting to a well known directory */
  174. [[NSFileManager defaultManager] createDirectoryAtPath:self.outputFolderPath attributes:nil];
  175. [self.streamSession setStreamOutput:streamOutput];
  176. [self.streamSession setMedia:self.media];
  177. [self.streamSession startStreaming];
  178. [self.streamSession addObserver:self forKeyPath:@"isComplete" options:NSKeyValueObservingOptionNew context:nil];
  179. /* Show the new view */
  180. [[window contentView] addSubview:workingView];
  181. NSRect frame = [workingView frame];
  182. frame.origin.y -= NSHeight([window contentRectForFrameRect:[window frame]]) + 20.f;
  183. [workingView setFrame:frame];
  184. [[window animator] setFrame:NSMakeRect([window frame].origin.x, [window frame].origin.y-NSHeight([workingView frame]), NSWidth([window frame]), NSHeight([window frame])+NSHeight([workingView frame])) display:YES];
  185. }
  186. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  187. {
  188. if([keyPath isEqualToString:@"isComplete"])
  189. {
  190. if([self.streamSession isComplete])
  191. {
  192. /* Notify the user */
  193. [[NSSound soundNamed:@"Glass"] play];
  194. /* Set the icon */
  195. [openConvertedFileButton setImage:[[NSWorkspace sharedWorkspace] iconForFile:[self outputFilePath]]];
  196. /* Rename the link with a nicer name */
  197. NSString * oldPath = [self outputFilePath];
  198. [self freezeOutputFilePath];
  199. [[NSFileManager defaultManager] moveItemAtPath:oldPath toPath:[self outputFilePath] error:NULL];
  200. }
  201. return;
  202. }
  203. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  204. }
  205. - (IBAction)openConvertedFile:(id)sender
  206. {
  207. [[NSWorkspace sharedWorkspace] openFile:[self outputFilePath]];
  208. }
  209. - (IBAction)pickOutputFolderPath:(id)sender;
  210. {
  211. NSOpenPanel * panel = [NSOpenPanel openPanel];
  212. [panel setCanChooseFiles:NO];
  213. [panel setCanChooseDirectories:YES];
  214. [panel setAllowsMultipleSelection:NO];
  215. [panel beginSheetForDirectory:self.outputFolderPath file:nil types:nil modalForWindow:[sender window] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
  216. }
  217. - (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
  218. {
  219. if(returnCode != NSOKButton || ![[panel filenames] count])
  220. return;
  221. self.outputFolderPath = [[panel filenames] objectAtIndex:0];
  222. }
  223. - (IBAction)openConvertedEnclosingFolder:(id)sender
  224. {
  225. [[NSWorkspace sharedWorkspace] selectFile:[self outputFilePath] inFileViewerRootedAtPath:[[self outputFilePath] stringByDeletingLastPathComponent]];
  226. }
  227. - (IBAction)cancel:(id)sender
  228. {
  229. [self.streamSession stop];
  230. }
  231. @end