Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 4 commits
  • 4 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jun 07, 2014
Sam Jarman Adds a button to set download location (useful if saving to an extern…
…al drive
2e08a84
Zach Drayer Formatting nitpicks. Explicitly exclude files. Copy the URL path into…
… an ivar. Remove a ?:.
ef9db40
Zach Drayer Rename some methods, remove a ?: in favor of a single download locati…
…on state that can be cleared as needed. alert to explain the behavior of setting the downloads location. AutoLayout to fix window resizing issues
b947af8
Zach Drayer Credits update dd285a8
View
4 WWDCDownloader/WWDCWebsiteInteractionController.h
@@ -14,8 +14,10 @@
@property (assign) IBOutlet NSButton *PDFCheckbox;
@property (assign) IBOutlet NSButton *downloadButton;
+@property (assign) IBOutlet NSButton *saveToButton;
@property (assign) IBOutlet NSProgressIndicator *downloadProgressBar;
-- (IBAction) download:(id) sender;
+- (IBAction) downloadFiles:(id) sender;
+- (IBAction) pickDownloadsFolder:(id) sender;
@end
View
117 WWDCDownloader/WWDCWebsiteInteractionController.m
@@ -59,6 +59,8 @@ @implementation WWDCWebsiteInteractionController {
BOOL _loggedIn;
BOOL _pastFirstLoad;
NSURL *_WWDCVideosURL;
+ NSString *_userDownloadLocation;
+ NSString *_downloadsFolder;
}
- (id) init {
@@ -72,11 +74,10 @@ - (void) awakeFromNib {
_WWDCVideosURL = [NSURL URLWithString:@"https://developer.apple.com/videos/wwdc/2014/"];
- // Apparnetly login isn't required anymore for 2014
- _loggedIn = YES;
- _pastFirstLoad = YES;
+ // Apparently login isn't required anymore
+ _loggedIn = YES;
+ _pastFirstLoad = YES;
-// [self.webView setHidden:YES];
[self.webView.mainFrame loadRequest:[NSURLRequest requestWithURL:_WWDCVideosURL]];
self.webView.frameLoadDelegate = self;
@@ -90,11 +91,52 @@ - (void) awakeFromNib {
#pragma mark -
-- (IBAction) download:(id) sender {
- [self.downloadProgressBar setHidden:!_foundVideosPage];
+- (NSString *) downloadsFolder {
+ @synchronized(self) {
+ if (!_downloadsFolder) {
+ _downloadsFolder = [_userDownloadLocation copy];
- // find content first
+ if (!_downloadsFolder) {
+ NSString *temporaryFolder = [NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ _downloadsFolder = [[temporaryFolder stringByAppendingPathComponent:@"WWDC 2014/"] copy];
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath:_downloadsFolder]) {
+ NSError *error = nil;
+
+ if (![[NSFileManager defaultManager] createDirectoryAtPath:_downloadsFolder withIntermediateDirectories:YES attributes:nil error:&error]) {
+ NSLog(@"Unable to create folder \"%@\" to download files to", error);
+ }
+ }
+ }
+ }
+
+ return _downloadsFolder;
+ }
+}
+
+- (void) resetDownloadsFolder {
+ @synchronized(self) {
+ _downloadsFolder = nil;
+ }
+}
+
+#pragma mark -
+
+- (IBAction) downloadFiles:(id) sender {
__weak typeof(self) weakSelf = self;
+
+ if (_foundVideosPage) {
+ [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+
+ [NSAnimationContext currentContext].duration = (1. / 3.);
+ [NSAnimationContext currentContext].allowsImplicitAnimation = YES;
+
+ [strongSelf.downloadProgressBar setHidden:!strongSelf->_foundVideosPage];
+ } completionHandler:NULL];
+ }
+
+ // find content first
[self.webView.mainFrameDocument.body.children wwdc_enumerateObjectsUsingBlock:^(DOMHTMLElement *contentElement, unsigned contentIndex, BOOL *stopContentEnumeration) {
if (![contentElement.className isEqualToString:@"content"]) {
return;
@@ -143,24 +185,61 @@ - (IBAction) download:(id) sender {
}];
}
-#pragma mark -
+- (IBAction) pickDownloadsFolder:(id) sender {
+ void (^presentDownloadLocationPanel)() = ^{
+ NSOpenPanel *downloadLocationPanel = [NSOpenPanel openPanel];
+ downloadLocationPanel.canChooseDirectories = YES;
+ downloadLocationPanel.canCreateDirectories = YES;
+ downloadLocationPanel.canChooseFiles = NO;
+
+ __weak typeof(self) weakSelf = self;
+ [downloadLocationPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
+ if (result == NSFileHandlingPanelOKButton) {
+ for (NSURL *url in downloadLocationPanel.URLs) {
+ if (!url.isFileURL) {
+ continue;
+ }
-// see above for the element that is passed in
-- (void) downloadFromAnchorElement:(DOMHTMLAnchorElement *) anchorElement forSessionNamed:(NSString *) sessionName {
- static NSString *downloadsFolder = nil;
- if (!downloadsFolder) {
- NSString *temporaryFolder = [NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES) objectAtIndex:0];
- downloadsFolder = [[temporaryFolder stringByAppendingPathComponent:@"WWDC 2014/"] copy];
+ // Double check and verify that the file exists and is a directory.
+ // (isDirectory is an out parameter)
+ BOOL isDirectory = NO;
+ if ([[NSFileManager defaultManager] fileExistsAtPath: url.path isDirectory:&isDirectory] && isDirectory) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ strongSelf->_userDownloadLocation = [url.path copy];
+ [strongSelf resetDownloadsFolder];
+ }
+ }
+ }
+ }];
+ };
- if (![[NSFileManager defaultManager] fileExistsAtPath:downloadsFolder]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:downloadsFolder withIntermediateDirectories:YES attributes:nil error:NULL];
- }
+ if ([NSOperationQueue wwdc_requestQueue].operationCount) {
+ NSAlert *alert = [[NSAlert alloc] init];
+ alert.alertStyle = NSWarningAlertStyle;
+ alert.messageText = @"Notice";
+ alert.informativeText = @"Setting the download location will affect currently pending download operations.";
+
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse response) {
+ if (response != NSModalResponseAbort) {
+ // do this async to give the alert sheet a chance to dismiss, before we present a second sheet on the window
+ dispatch_async(dispatch_get_main_queue(), presentDownloadLocationPanel);
+ }
+ }];
+ } else {
+ presentDownloadLocationPanel();
}
+}
+
+#pragma mark -
+// see above for the element that is passed in
+- (void) downloadFromAnchorElement:(DOMHTMLAnchorElement *) anchorElement forSessionNamed:(NSString *) sessionName {
NSString *name = [anchorElement.href.lastPathComponent componentsSeparatedByString:@"?"][0];
NSArray *components = [name componentsSeparatedByString:@"."];
NSString *saveLocation = [NSString stringWithFormat:@"%@ %@.%@", components[0], sessionName, components[1]];
- saveLocation = [downloadsFolder stringByAppendingPathComponent:saveLocation];
+ saveLocation = [self.downloadsFolder stringByAppendingPathComponent:saveLocation];
__weak typeof(self) weakSelf = self;
WWDCURLRequest *request = [WWDCURLRequest requestWithRemoteAddress:anchorElement.href savePath:saveLocation];
@@ -239,7 +318,7 @@ - (void) webView:(WebView *) sender didFinishLoadForFrame:(WebFrame *) frame {
self.window.title = frame.name;
if (!_foundVideosPage) {
- [self download:nil];
+ [self downloadFiles:nil];
_loggedIn = YES;
}
View
121 WWDCDownloader/WWDCWebsiteInteractionController.xib
@@ -1,18 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4457.6" systemVersion="12E55" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6154.17" systemVersion="14A238x" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
- <deployment defaultVersion="1080" identifier="macosx"/>
<development version="5000" identifier="xcode"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4457.6"/>
- <plugIn identifier="com.apple.WebKitIBPlugin" version="3330"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6154.17"/>
+ <plugIn identifier="com.apple.WebKitIBPlugin" version="6154.17"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="WWDCWebsiteInteractionController">
<connections>
- <action selector="download:" destination="NZy-fD-Iji" id="waN-aq-q9q"/>
<outlet property="PDFCheckbox" destination="tdz-K1-rIS" id="i9j-Fz-A4t"/>
- <outlet property="downloadButton" destination="NZy-fD-Iji" id="nSj-tc-pSv"/>
+ <outlet property="downloadButton" destination="iHP-45-A4Y" id="KVo-Me-GHB"/>
<outlet property="downloadProgressBar" destination="iTS-qZ-m1M" id="f4n-WS-5yZ"/>
+ <outlet property="saveToButton" destination="NZy-fD-Iji" id="0sp-me-4fZ"/>
<outlet property="videoPopUpButton" destination="wfg-Qd-HcC" id="22e-HQ-bgG"/>
<outlet property="webView" destination="xep-Fu-xaN" id="PpM-oc-Lag"/>
<outlet property="window" destination="uGQ-0R-zCI" id="314-UV-mDt"/>
@@ -22,44 +21,48 @@
<customObject id="-3" userLabel="Application"/>
<window title="WWDCDownloader" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="uGQ-0R-zCI">
<windowStyleMask key="styleMask" titled="YES" miniaturizable="YES" resizable="YES"/>
- <rect key="contentRect" x="335" y="390" width="661" height="469"/>
- <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
+ <windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
+ <rect key="contentRect" x="335" y="390" width="1024" height="453"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<view key="contentView" id="sRh-du-LW5">
- <rect key="frame" x="0.0" y="0.0" width="661" height="469"/>
+ <rect key="frame" x="0.0" y="0.0" width="1024" height="453"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
- <webView translatesAutoresizingMaskIntoConstraints="NO" id="xep-Fu-xaN">
- <rect key="frame" x="0.0" y="54" width="661" height="415"/>
- <autoresizingMask key="autoresizingMask"/>
- <webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12" javaEnabled="NO">
+ <webView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xep-Fu-xaN">
+ <rect key="frame" x="0.0" y="54" width="1024" height="480"/>
+ <webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12" javaEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO">
<nil key="identifier"/>
</webPreferences>
</webView>
- <button translatesAutoresizingMaskIntoConstraints="NO" id="tdz-K1-rIS">
- <rect key="frame" x="218" y="21" width="120" height="18"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <constraints>
- <constraint firstAttribute="width" constant="116" id="193-Gd-yys"/>
- </constraints>
+ <button misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tdz-K1-rIS">
+ <rect key="frame" x="186" y="18" width="120" height="18"/>
<buttonCell key="cell" type="check" title="Download PDFs" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="bIJ-Kc-XfD">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
- <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NZy-fD-Iji">
- <rect key="frame" x="544" y="13" width="103" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="push" title="Download" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="o4o-7T-gMz">
+ <button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NZy-fD-Iji">
+ <rect key="frame" x="769" y="9" width="138" height="32"/>
+ <buttonCell key="cell" type="push" title="Save Location..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="o4o-7T-gMz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
+ <connections>
+ <action selector="pickDownloadsFolder:" target="-2" id="upX-Dz-mlM"/>
+ </connections>
</button>
- <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wfg-Qd-HcC">
- <rect key="frame" x="18" y="17" width="181" height="26"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <constraints>
- <constraint firstAttribute="width" constant="176" id="WDY-qN-XCh"/>
- </constraints>
+ <button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iHP-45-A4Y">
+ <rect key="frame" x="907" y="9" width="103" height="32"/>
+ <buttonCell key="cell" type="push" title="Download" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="dMl-C2-OfP">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="downloadFiles:" target="-2" id="yCl-9D-ZNR"/>
+ </connections>
+ </button>
+ <popUpButton verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wfg-Qd-HcC">
+ <rect key="frame" x="18" y="13" width="165" height="26"/>
<popUpButtonCell key="cell" type="push" title="Download HD Quality" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="60l-2B-Ki4" id="M1E-JN-qCI">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
@@ -75,53 +78,35 @@
</menu>
</popUpButtonCell>
</popUpButton>
- <progressIndicator maxValue="100" style="bar" translatesAutoresizingMaskIntoConstraints="NO" id="iTS-qZ-m1M">
- <rect key="frame" x="344" y="19" width="198" height="20"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <progressIndicator misplaced="YES" maxValue="100" style="bar" translatesAutoresizingMaskIntoConstraints="NO" id="iTS-qZ-m1M">
+ <rect key="frame" x="312" y="15" width="455" height="20"/>
</progressIndicator>
</subviews>
<constraints>
- <constraint firstItem="NZy-fD-Iji" firstAttribute="baseline" secondItem="tdz-K1-rIS" secondAttribute="baseline" constant="-1" id="8qg-yV-l1M"/>
- <constraint firstItem="tdz-K1-rIS" firstAttribute="baseline" secondItem="wfg-Qd-HcC" secondAttribute="baseline" constant="1" id="Ch6-na-Cv7"/>
- <constraint firstAttribute="trailing" secondItem="NZy-fD-Iji" secondAttribute="trailing" constant="20" id="N2H-ip-Utj"/>
- <constraint firstItem="xep-Fu-xaN" firstAttribute="leading" secondItem="sRh-du-LW5" secondAttribute="leading" id="OrM-le-Xvd"/>
- <constraint firstItem="tdz-K1-rIS" firstAttribute="leading" secondItem="wfg-Qd-HcC" secondAttribute="trailing" constant="24" id="PAK-wI-twl"/>
- <constraint firstItem="iTS-qZ-m1M" firstAttribute="leading" secondItem="tdz-K1-rIS" secondAttribute="trailing" constant="8" id="RPZ-YM-shK"/>
- <constraint firstAttribute="bottom" secondItem="tdz-K1-rIS" secondAttribute="bottom" constant="23" id="d3h-XR-R9F"/>
- <constraint firstItem="xep-Fu-xaN" firstAttribute="trailing" secondItem="sRh-du-LW5" secondAttribute="trailing" id="eQs-yK-aAX"/>
- <constraint firstAttribute="bottom" secondItem="xep-Fu-xaN" secondAttribute="bottom" constant="54" id="ejj-be-Yfa"/>
- <constraint firstItem="NZy-fD-Iji" firstAttribute="leading" secondItem="iTS-qZ-m1M" secondAttribute="trailing" constant="8" id="ih1-cg-h4N"/>
- <constraint firstItem="wfg-Qd-HcC" firstAttribute="leading" secondItem="sRh-du-LW5" secondAttribute="leading" constant="20" id="muP-kJ-S4d"/>
- <constraint firstAttribute="bottom" secondItem="iTS-qZ-m1M" secondAttribute="bottom" constant="20" id="nAN-HR-Iy1"/>
- <constraint firstItem="xep-Fu-xaN" firstAttribute="top" secondItem="sRh-du-LW5" secondAttribute="top" id="wrU-5b-JAK"/>
+ <constraint firstItem="wfg-Qd-HcC" firstAttribute="leading" secondItem="sRh-du-LW5" secondAttribute="leading" constant="20" id="1j8-pI-r4P"/>
+ <constraint firstAttribute="bottom" secondItem="iTS-qZ-m1M" secondAttribute="bottom" constant="16" id="3Pc-cV-i83"/>
+ <constraint firstAttribute="bottom" secondItem="xep-Fu-xaN" secondAttribute="bottom" constant="54" id="3vh-gc-Rfr"/>
+ <constraint firstItem="iTS-qZ-m1M" firstAttribute="top" secondItem="xep-Fu-xaN" secondAttribute="bottom" constant="20" id="B7W-TR-Q0L"/>
+ <constraint firstAttribute="bottom" secondItem="NZy-fD-Iji" secondAttribute="bottom" constant="16" id="F7c-nH-Gqi"/>
+ <constraint firstItem="xep-Fu-xaN" firstAttribute="leading" secondItem="sRh-du-LW5" secondAttribute="leading" id="Ksg-Da-Mi0"/>
+ <constraint firstItem="NZy-fD-Iji" firstAttribute="leading" secondItem="iTS-qZ-m1M" secondAttribute="trailing" constant="8" id="Qlz-pX-CZD"/>
+ <constraint firstItem="xep-Fu-xaN" firstAttribute="top" secondItem="sRh-du-LW5" secondAttribute="top" id="UeY-0S-tZX"/>
+ <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="iTS-qZ-m1M" secondAttribute="bottom" constant="16" id="WsT-pT-4CA"/>
+ <constraint firstItem="iHP-45-A4Y" firstAttribute="leading" secondItem="NZy-fD-Iji" secondAttribute="trailing" constant="12" id="aGP-AT-fVy"/>
+ <constraint firstItem="iTS-qZ-m1M" firstAttribute="leading" secondItem="tdz-K1-rIS" secondAttribute="trailing" constant="8" id="aYZ-qJ-nTK"/>
+ <constraint firstAttribute="bottom" secondItem="iHP-45-A4Y" secondAttribute="bottom" constant="16" id="dOY-ld-BUz"/>
+ <constraint firstAttribute="bottom" secondItem="wfg-Qd-HcC" secondAttribute="bottom" constant="16" id="eCC-ol-VSw"/>
+ <constraint firstAttribute="trailing" secondItem="xep-Fu-xaN" secondAttribute="trailing" id="odc-Kd-9WI"/>
+ <constraint firstItem="tdz-K1-rIS" firstAttribute="leading" secondItem="wfg-Qd-HcC" secondAttribute="trailing" constant="8" id="r1A-BP-AnJ"/>
+ <constraint firstAttribute="bottom" secondItem="tdz-K1-rIS" secondAttribute="bottom" constant="20" id="sH8-1C-fip"/>
+ <constraint firstAttribute="trailing" secondItem="iHP-45-A4Y" secondAttribute="trailing" constant="20" id="u35-Ks-wCL"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="KtR-5K-w1D"/>
<outlet property="initialFirstResponder" destination="xep-Fu-xaN" id="b5S-xi-1o2"/>
</connections>
+ <point key="canvasLocation" x="690" y="336.5"/>
</window>
</objects>
- <classes>
- <class className="NSLayoutConstraint" superclassName="NSObject">
- <source key="sourceIdentifier" type="project" relativePath="./Classes/NSLayoutConstraint.h"/>
- </class>
- <class className="WWDCWebsiteInteractionController" superclassName="NSWindowController">
- <source key="sourceIdentifier" type="project" relativePath="./Classes/WWDCWebsiteInteractionController.h"/>
- <relationships>
- <relationship kind="action" name="download:"/>
- <relationship kind="outlet" name="PDFCheckbox" candidateClass="NSButton"/>
- <relationship kind="outlet" name="downloadButton" candidateClass="NSButton"/>
- <relationship kind="outlet" name="downloadProgressBar" candidateClass="NSProgressIndicator"/>
- <relationship kind="outlet" name="videoPopUpButton" candidateClass="NSPopUpButton"/>
- <relationship kind="outlet" name="webView" candidateClass="WebView"/>
- </relationships>
- </class>
- <class className="WebView">
- <source key="sourceIdentifier" type="project" relativePath="./Classes/WebView.h"/>
- <relationships>
- <relationship kind="action" name="reloadFromOrigin:"/>
- </relationships>
- </class>
- </classes>
-</document>
View
1  WWDCDownloader/en.lproj/Credits.rtf
@@ -9,6 +9,7 @@
Zach Drayer\
Kelan Champagne\
Philippe Casgrain\
+ Sam Jarman\
\
\b With special thanks to:

No commit comments for this range

Something went wrong with that request. Please try again.