├── icon_ban.png ├── icon_error.png ├── icon_key.png ├── icon_list.png ├── icon_love.png ├── icon_tag.png ├── icon_tick.png ├── icon_loading.gif ├── lastfm_black.png ├── English.lproj └── InfoPlist.strings ├── .gitignore ├── Lastify_Prefix.pch ├── class-dump-headers ├── GrowlApplicationBridgeDelegate-Protocol.h ├── NSCopying-Protocol.h ├── NSCoding-Protocol.h ├── NSURL-Additions.h ├── NSMenu-CarbonMenu.h ├── SPWebClipView.h ├── NSTextFieldHand.h ├── MenuSeparatorLine.h ├── NSAlert-SuppressionButtonCompat.h ├── NSAttributedString-Hyperlink.h ├── NSObject-SPInvocationGrabbing.h ├── NSPasteboard-Additions.h ├── NSWindow-liveFrame.h ├── NetworkReachabilityChangeNotifier.h ├── GrowlNotificationProtocol-Protocol.h ├── SPApplication.h ├── SPWindow.h ├── SPMenuItem.h ├── NSFileManager-SPAuthentication.h ├── HighlightableTextFieldCell.h ├── RoundedCornersView.h ├── SPHelpers.h ├── NSView-MATrackingArea.h ├── SPStretchableTextField.h ├── SPThemeFrame.h ├── SPTextFieldFormatter.h ├── SPMultiObjectProxy.h ├── NSString-Additions.h ├── SPGrowlDelegate.h ├── SPTextField.h ├── SPSearchField.h ├── SPSecureTextField.h ├── SPTextView.h ├── GrowlPathUtilities.h ├── SPUndoManager.h ├── Reachability.h ├── SPInvocationGrabber.h ├── SPTokenField.h ├── SPMenu.h ├── SPWebBrowserHelper.h ├── SPPurchasePasswordCheckWindowController.h ├── SPAutocompleteMenuCell.h ├── MATrackingArea.h ├── SPView.h ├── GrowlApplicationBridge.h ├── SPController.h ├── SPAutocompleteMenuWindow.h ├── SPEditMetadataWindowController.h └── CDStructures.h ├── NSString+Lastify.h ├── LastifyDrawerView.h ├── SPGrowlDelegate+Lastify.h ├── NSButton+Lastify.h ├── LastifyDrawerView.m ├── LICENSE ├── NSButton+Lastify.m ├── NSString+Lastify.m ├── Info.plist ├── README.markdown ├── SPGrowlDelegate+Lastify.m ├── LastifyLastfmClient.h ├── SPTypes.h ├── LastifyController.h ├── Rakefile ├── LastifyController.m ├── LastifyLastfmClient.m └── Lastify.xcodeproj └── project.pbxproj /icon_ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_ban.png -------------------------------------------------------------------------------- /icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_error.png -------------------------------------------------------------------------------- /icon_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_key.png -------------------------------------------------------------------------------- /icon_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_list.png -------------------------------------------------------------------------------- /icon_love.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_love.png -------------------------------------------------------------------------------- /icon_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_tag.png -------------------------------------------------------------------------------- /icon_tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_tick.png -------------------------------------------------------------------------------- /icon_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/icon_loading.gif -------------------------------------------------------------------------------- /lastfm_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/lastfm_black.png -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgebrock/lastify/HEAD/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~.nib 4 | 5 | build 6 | 7 | *.pbxuser 8 | *.perspective 9 | *.perspectivev3 10 | 11 | *.mode1v3 12 | *.mode2v3 13 | -------------------------------------------------------------------------------- /Lastify_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Lastify' target in the 'Lastify' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /class-dump-headers/GrowlApplicationBridgeDelegate-Protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | @protocol GrowlApplicationBridgeDelegate 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /class-dump-headers/NSCopying-Protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | @protocol NSCopying 8 | - (id)copyWithZone:(struct _NSZone *)fp8; 9 | @end 10 | 11 | -------------------------------------------------------------------------------- /class-dump-headers/NSCoding-Protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | @protocol NSCoding 8 | - (void)encodeWithCoder:(id)fp8; 9 | - (id)initWithCoder:(id)fp8; 10 | @end 11 | 12 | -------------------------------------------------------------------------------- /NSString+Lastify.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Lastify.h 3 | // Lastify 4 | // 5 | // Created by George on 17/01/2009. 6 | // Copyright 2009 George Brocklehurst. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSString (Lastify) 13 | 14 | - (NSString*)MD5Hash; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /class-dump-headers/NSURL-Additions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSURL.h" 8 | 9 | @interface NSURL (Additions) 10 | + (id)URLWithInternetLocationFile:(id)fp8; 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /class-dump-headers/NSMenu-CarbonMenu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSMenu.h" 8 | 9 | @interface NSMenu (CarbonMenu) 10 | - (struct OpaqueMenuRef *)carbonMenuRef; 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /class-dump-headers/SPWebClipView.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @interface SPWebClipView : NSView 10 | { 11 | } 12 | 13 | - (void)drawRect:(struct _NSRect)fp8; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /class-dump-headers/NSTextFieldHand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTextField.h" 8 | 9 | @interface NSTextFieldHand : NSTextField 10 | { 11 | } 12 | 13 | - (void)resetCursorRects; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /class-dump-headers/MenuSeparatorLine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @interface MenuSeparatorLine : NSView 10 | { 11 | } 12 | 13 | - (void)drawRect:(struct _NSRect)fp8; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /class-dump-headers/NSAlert-SuppressionButtonCompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSAlert.h" 8 | 9 | @interface NSAlert (SuppressionButtonCompat) 10 | - (id)sp_suppressionButtonWithLabel:(id)fp8; 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /LastifyDrawerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyDrawerView.h 3 | // Lastify 4 | // 5 | // Created by George on 11/02/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LastifyDrawerView : NSView { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SPGrowlDelegate+Lastify.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPGrowlDelegate+Lastify.h 3 | // Lastify 4 | // 5 | // Created by George on 17/01/2009. 6 | // Copyright 2009 George Brocklehurst. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SPGrowlDelegate.h" 11 | 12 | @interface SPGrowlDelegate (Lastify) 13 | 14 | + (void)initLastify; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /class-dump-headers/NSAttributedString-Hyperlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSAttributedString.h" 8 | 9 | @interface NSAttributedString (Hyperlink) 10 | + (id)hyperlinkFromString:(id)fp8 withURL:(id)fp12; 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /class-dump-headers/NSObject-SPInvocationGrabbing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface NSObject (SPInvocationGrabbing) 10 | - (id)grab; 11 | - (id)invokeAfter:(double)fp8; 12 | - (id)nextRunloop; 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /class-dump-headers/NSPasteboard-Additions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSPasteboard.h" 8 | 9 | @interface NSPasteboard (Additions) 10 | + (id)dragDataTypesWithFiles:(BOOL)fp8; 11 | - (id)stringData; 12 | - (id)fileData; 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /class-dump-headers/NSWindow-liveFrame.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSWindow.h" 8 | 9 | @interface NSWindow (liveFrame) 10 | - (struct _NSRect)liveFrame; 11 | - (struct _NSRect)convertLiveBaseRectToScreen:(struct _NSRect)fp8; 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /NSButton+Lastify.h: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyDrawerButton.h 3 | // Lastify 4 | // 5 | // Created by George on 11/02/2009. 6 | // Based on code by Matt Gemmell: http://www.pastebuffer.com/2007/10/04/setting-the-text-color-of-an-nsbutton/ 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSButton (Lastify) 13 | 14 | - (void)setTextColor:(NSColor *)textColor; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /class-dump-headers/NetworkReachabilityChangeNotifier.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface NetworkReachabilityChangeNotifier : NSObject 10 | { 11 | } 12 | 13 | - (id)init; 14 | - (void)networkReachabilityChanged; 15 | - (void)dealloc; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /class-dump-headers/GrowlNotificationProtocol-Protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | @protocol GrowlNotificationProtocol 8 | - (oneway void)registerApplicationWithDictionary:(bycopy id)fp8; 9 | - (oneway void)postNotificationWithDictionary:(bycopy id)fp8; 10 | - (bycopy id)growlVersion; 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /class-dump-headers/SPApplication.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSApplication.h" 8 | 9 | @interface SPApplication : NSApplication 10 | { 11 | } 12 | 13 | - (void)processCrashReports; 14 | - (void)terminate:(id)fp8; 15 | - (void)doNothingAtAll:(id)fp8; 16 | - (void)sendEvent:(id)fp8; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /class-dump-headers/SPWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSWindow.h" 8 | 9 | @interface SPWindow : NSWindow 10 | { 11 | } 12 | 13 | - (id)initWithContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 backing:(unsigned int)fp28 defer:(BOOL)fp32; 14 | - (BOOL)canBecomeKeyWindow; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /class-dump-headers/SPMenuItem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSMenuItem.h" 8 | 9 | @interface SPMenuItem : NSMenuItem 10 | { 11 | BOOL _enabledState; 12 | } 13 | 14 | - (id)initWithTitle:(id)fp8 action:(SEL)fp12 keyEquivalent:(id)fp16; 15 | - (BOOL)_enabledState; 16 | - (void)_setEnabledState:(BOOL)fp8; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /class-dump-headers/NSFileManager-SPAuthentication.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSFileManager.h" 8 | 9 | @interface NSFileManager (SPAuthentication) 10 | - (BOOL)currentUserOwnsPath:(id)fp8; 11 | - (BOOL)_copyPathWithForcedAuthentication:(id)fp8 toPath:(id)fp12; 12 | - (BOOL)copyPathWithAuthentication:(id)fp8 toPath:(id)fp12; 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /class-dump-headers/HighlightableTextFieldCell.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTextFieldCell.h" 8 | 9 | @interface HighlightableTextFieldCell : NSTextFieldCell 10 | { 11 | int compat_backgroundStyle; 12 | } 13 | 14 | - (void)compat_setBackgroundStyle:(int)fp8; 15 | - (void)drawWithFrame:(struct _NSRect)fp8 inView:(id)fp24; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /class-dump-headers/RoundedCornersView.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @interface RoundedCornersView : NSView 10 | { 11 | float _cornerRadius; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (void)drawRect:(struct _NSRect)fp8; 16 | - (float)cornerRadius; 17 | - (void)setCornerRadius:(float)fp8; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /class-dump-headers/SPHelpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface SPHelpers : NSObject 10 | { 11 | } 12 | 13 | + (const char *)fontFaceForIndex:(int)fp8; 14 | + (struct CGImage *)CGImageRefFromNSBitmapImageRep:(id)fp8; 15 | + (id)NSColorFromColor:(const unsigned int *)fp8; 16 | + (float)fontFaceHeightMultiplier; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /class-dump-headers/NSView-MATrackingArea.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @interface NSView (MATrackingArea) 10 | - (void)_addTrackingArea:(id)fp8; 11 | - (void)_removeTrackingArea:(id)fp8; 12 | - (id)_trackingAreas; 13 | - (void)compat_addTrackingArea:(id)fp8; 14 | - (void)compat_removeTrackingArea:(id)fp8; 15 | - (id)compat_trackingAreas; 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /class-dump-headers/SPStretchableTextField.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTextField.h" 8 | 9 | @interface SPStretchableTextField : NSTextField 10 | { 11 | BOOL textDidChange; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (void)textDidChange:(id)fp8; 16 | - (void)cancelOperation:(id)fp8; 17 | - (void)textDidEndEditing:(id)fp8; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /class-dump-headers/SPThemeFrame.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSGrayFrame.h" 8 | 9 | @interface SPThemeFrame : NSGrayFrame 10 | { 11 | } 12 | 13 | - (void)drawRect:(struct _NSRect)fp8; 14 | - (struct _NSSize)_topCornerSize; 15 | - (struct _NSSize)_bottomCornerSize; 16 | - (float)roundedCornerRadius; 17 | - (id)contentFill; 18 | - (float)contentAlpha; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /class-dump-headers/SPTextFieldFormatter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSFormatter.h" 8 | 9 | @interface SPTextFieldFormatter : NSFormatter 10 | { 11 | } 12 | 13 | - (id)stringForObjectValue:(id)fp8; 14 | - (BOOL)getObjectValue:(id *)fp8 forString:(id)fp12 errorDescription:(id *)fp16; 15 | - (BOOL)isPartialStringValid:(id)fp8 newEditingString:(id *)fp12 errorDescription:(id *)fp16; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /class-dump-headers/SPMultiObjectProxy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSProxy.h" 8 | 9 | @class NSMutableArray; 10 | 11 | @interface SPMultiObjectProxy : NSProxy 12 | { 13 | NSMutableArray *objects; 14 | } 15 | 16 | - (id)initWithObjects:(id)fp8; 17 | - (void)dealloc; 18 | - (id)objects; 19 | - (void)forwardInvocation:(id)fp8; 20 | - (id)methodSignatureForSelector:(SEL)fp8; 21 | - (BOOL)respondsToSelector:(SEL)fp8; 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /LastifyDrawerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyDrawerView.m 3 | // Lastify 4 | // 5 | // Created by George on 11/02/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import "LastifyDrawerView.h" 10 | 11 | 12 | @implementation LastifyDrawerView 13 | 14 | - (void)drawRect:(NSRect)rect 15 | { 16 | [self lockFocus]; 17 | [[NSColor colorWithDeviceRed:0.235 green:0.235 blue:0.235 alpha:1.0] set]; 18 | [NSBezierPath fillRect:rect]; 19 | [self unlockFocus]; 20 | 21 | [super drawRect:rect]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /class-dump-headers/NSString-Additions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSString.h" 8 | 9 | @interface NSString (Additions) 10 | - (id)stringByRemovingCharactersInSet:(id)fp8 options:(unsigned int)fp12; 11 | - (id)stringByReplacingCharactersInSet:(id)fp8 withString:(id)fp12 options:(unsigned int)fp16; 12 | - (id)stringByRemovingCharactersInSet:(id)fp8; 13 | - (id)stringByRemovingCharacter:(unsigned short)fp8; 14 | - (id)stringWithRemovedSubstring:(id)fp8; 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /class-dump-headers/SPGrowlDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import 8 | 9 | #import "GrowlApplicationBridgeDelegate-Protocol.h" 10 | 11 | @interface SPGrowlDelegate : NSObject 12 | { 13 | } 14 | 15 | + (void)initialize; 16 | + (id)sharedInstance; 17 | - (id)registrationDictionaryForGrowl; 18 | - (void)notificationWithTrackInfo:(struct TrackInfo *)fp8; 19 | - (void)growlNotificationWasClicked:(id)fp8; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /class-dump-headers/SPTextField.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTextField.h" 8 | 9 | @interface SPTextField : NSTextField 10 | { 11 | struct PlatformEditBox *box; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (BOOL)becomeFirstResponder; 16 | - (void)textDidChange:(id)fp8; 17 | - (BOOL)textShouldEndEditing:(id)fp8; 18 | - (void)textDidEndEditing:(id)fp8; 19 | - (struct PlatformEditBox *)platformEditBox; 20 | - (void)setPlatformEditBox:(struct PlatformEditBox *)fp8; 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /class-dump-headers/SPSearchField.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSSearchField.h" 8 | 9 | @interface SPSearchField : NSSearchField 10 | { 11 | struct PlatformEditBox *box; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (BOOL)becomeFirstResponder; 16 | - (void)textDidChange:(id)fp8; 17 | - (BOOL)textShouldEndEditing:(id)fp8; 18 | - (void)textDidEndEditing:(id)fp8; 19 | - (struct PlatformEditBox *)platformEditBox; 20 | - (void)setPlatformEditBox:(struct PlatformEditBox *)fp8; 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /class-dump-headers/SPSecureTextField.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSSecureTextField.h" 8 | 9 | @interface SPSecureTextField : NSSecureTextField 10 | { 11 | struct PlatformEditBox *box; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (BOOL)becomeFirstResponder; 16 | - (void)textDidChange:(id)fp8; 17 | - (BOOL)textShouldEndEditing:(id)fp8; 18 | - (void)textDidEndEditing:(id)fp8; 19 | - (struct PlatformEditBox *)platformEditBox; 20 | - (void)setPlatformEditBox:(struct PlatformEditBox *)fp8; 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, George Brocklehurst ( http://georgebrock.com/ ) 2 | 3 | The contents of all files and directories are licensed for reuse under the 4 | Creative Commons "Attribution-Share Alike 3.0 Unported" license unless 5 | otherwise noted in the individual files. 6 | 7 | Full details of this license can be found at: 8 | 9 | http://creativecommons.org/licenses/by-sa/3.0/ 10 | 11 | 12 | 13 | The Last.fm logo is property of Last.fm 14 | 15 | The love, ban and tag icons are part of the famfamfam.com Silk icon set 16 | They are provided under a Creative Commons Attribution license 17 | See http://www.famfamfam.com/lab/icons/silk/ for more information 18 | -------------------------------------------------------------------------------- /class-dump-headers/SPTextView.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTextView.h" 8 | 9 | @interface SPTextView : NSTextView 10 | { 11 | struct PlatformEditBoxOSX *box; 12 | } 13 | 14 | - (id)initWithFrame:(struct _NSRect)fp8; 15 | - (void)setString:(id)fp8; 16 | - (void)dealloc; 17 | - (BOOL)becomeFirstResponder; 18 | - (void)textDidChange:(id)fp8; 19 | - (void)textDidEndEditing:(id)fp8; 20 | - (struct PlatformEditBoxOSX *)platformEditBox; 21 | - (void)setPlatformEditBox:(struct PlatformEditBoxOSX *)fp8; 22 | - (void)insertTab:(id)fp8; 23 | - (void)insertBacktab:(id)fp8; 24 | 25 | @end 26 | 27 | -------------------------------------------------------------------------------- /class-dump-headers/GrowlPathUtilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface GrowlPathUtilities : NSObject 10 | { 11 | } 12 | 13 | + (id)growlPrefPaneBundle; 14 | + (id)helperAppBundle; 15 | + (id)searchPathForDirectory:(int)fp8 inDomains:(unsigned int)fp12 mustBeWritable:(BOOL)fp16; 16 | + (id)searchPathForDirectory:(int)fp8 inDomains:(unsigned int)fp12; 17 | + (id)growlSupportDirectory; 18 | + (id)screenshotsDirectory; 19 | + (id)ticketsDirectory; 20 | + (id)nextScreenshotName; 21 | + (id)nextScreenshotNameInDirectory:(id)fp8; 22 | + (id)defaultSavePathForTicketWithApplicationName:(id)fp8; 23 | 24 | @end 25 | 26 | -------------------------------------------------------------------------------- /class-dump-headers/SPUndoManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSUndoManager.h" 8 | 9 | @interface SPUndoManager : NSUndoManager 10 | { 11 | id _undo_target; 12 | id _redo_target; 13 | SEL _undo_sel; 14 | SEL _redo_sel; 15 | id _undo_object; 16 | id _redo_object; 17 | } 18 | 19 | - (id)init; 20 | - (void)undo; 21 | - (void)redo; 22 | - (BOOL)canUndo; 23 | - (BOOL)canRedo; 24 | - (id)undoMenuItemTitle; 25 | - (id)redoMenuItemTitle; 26 | - (void)registerUndoTarget:(id)fp8 selector:(SEL)fp12 withObject:(id)fp16; 27 | - (void)registerRedoTarget:(id)fp8 selector:(SEL)fp12 withObject:(id)fp16; 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /class-dump-headers/Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface Reachability : NSObject 10 | { 11 | BOOL localWiFiRef; 12 | struct __SCNetworkReachability *reachabilityRef; 13 | } 14 | 15 | + (id)reachabilityWithHostName:(id)fp8; 16 | + (id)reachabilityWithAddress:(const struct sockaddr_in *)fp8; 17 | + (id)reachabilityForInternetConnection; 18 | + (id)reachabilityForLocalWiFi; 19 | - (BOOL)startNotifer; 20 | - (void)stopNotifer; 21 | - (void)dealloc; 22 | - (int)localWiFiStatusForFlags:(unsigned int)fp8; 23 | - (int)networkStatusForFlags:(unsigned int)fp8; 24 | - (BOOL)connectionRequired; 25 | - (int)currentReachabilityStatus; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /NSButton+Lastify.m: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyDrawerButton.m 3 | // Lastify 4 | // 5 | // Created by George on 11/02/2009. 6 | // Based on code by Matt Gemmell: http://www.pastebuffer.com/2007/10/04/setting-the-text-color-of-an-nsbutton/ 7 | // 8 | 9 | #import "NSButton+Lastify.h" 10 | 11 | 12 | @implementation NSButton (Lastify) 13 | 14 | - (void)setTextColor:(NSColor *)textColor 15 | { 16 | NSMutableAttributedString *attrTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]]; 17 | int len = [attrTitle length]; 18 | NSRange range = NSMakeRange(0, len); 19 | [attrTitle addAttribute:NSForegroundColorAttributeName value:textColor range:range]; 20 | [attrTitle fixAttributesInRange:range]; 21 | [self setAttributedTitle:attrTitle]; 22 | [attrTitle release]; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /class-dump-headers/SPInvocationGrabber.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @class NSInvocation; 10 | 11 | @interface SPInvocationGrabber : NSObject 12 | { 13 | id _object; 14 | NSInvocation *_invocation; 15 | int frameCount; 16 | char **frameStrings; 17 | } 18 | 19 | - (id)initWithObject:(id)fp8; 20 | - (id)initWithObject:(id)fp8 stacktraceSaving:(BOOL)fp12; 21 | - (void)dealloc; 22 | - (id)object; 23 | - (void)setObject:(id)fp8; 24 | - (id)invocation; 25 | - (void)setInvocation:(id)fp8; 26 | - (void)forwardInvocation:(id)fp8; 27 | - (id)methodSignatureForSelector:(SEL)fp8; 28 | - (void)invoke; 29 | - (void)saveBacktrace; 30 | - (void)printBacktrace; 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /NSString+Lastify.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Lastify.m 3 | // Lastify 4 | // 5 | // Created by George on 17/01/2009. 6 | // Copyright 2009 George Brocklehurst. All rights reserved. 7 | // 8 | 9 | #import "NSString+Lastify.h" 10 | #include 11 | 12 | @implementation NSString (Lastify) 13 | 14 | - (NSString*)MD5Hash 15 | { 16 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; 17 | if(data) 18 | { 19 | unsigned char *digest = MD5([data bytes], [data length], NULL); 20 | return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 21 | digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], 22 | digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]]; 23 | } 24 | 25 | return nil; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /class-dump-headers/SPTokenField.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSTokenField.h" 8 | 9 | @class NSString; 10 | 11 | @interface SPTokenField : NSTokenField 12 | { 13 | struct PlatformEditBoxOSX *box; 14 | NSString *autocompleteOutstandingInput; 15 | struct Array autocompleteResult; 16 | } 17 | 18 | - (id)initWithFrame:(struct _NSRect)fp8; 19 | - (BOOL)becomeFirstResponder; 20 | - (void)textDidChange:(id)fp8; 21 | - (void)textDidEndEditing:(id)fp8; 22 | - (struct PlatformEditBoxOSX *)platformEditBox; 23 | - (void)setPlatformEditBox:(struct PlatformEditBoxOSX *)fp8; 24 | - (id).cxx_construct; 25 | - (void).cxx_destruct; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /class-dump-headers/SPMenu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSMenu.h" 8 | 9 | @interface SPMenu : NSMenu 10 | { 11 | struct PopupMenuOSX *menu; 12 | SPMenu *parent; 13 | // Error parsing type: {FastDelegate1="m_Closure"{ClosurePtr="m_pthis"^{GenericClass}"m_pFunction"{?="__pfn"^"__delta"i}}}, name: on_select 14 | } 15 | 16 | + (int)selectedPopupItem; 17 | - (struct FastDelegate1)selectItem; 18 | - (void)setParent:(id)fp8; 19 | - (id)initWithMenu:(struct PopupMenuOSX *)fp8 title:(id)fp12; 20 | - (void)doAction:(id)fp8; 21 | - (void)setCallback:(struct FastDelegate1)fp8; 22 | - (struct IPopupMenu *)menuHandle; 23 | - (void)dealloc; 24 | - (int)numberOfItemsInMenu:(id)fp8; 25 | - (BOOL)menu:(id)fp8 updateItem:(id)fp12 atIndex:(int)fp16 shouldCancel:(BOOL)fp20; 26 | - (BOOL)performKeyEquivalent:(id)fp8; 27 | - (id).cxx_construct; 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /class-dump-headers/SPWebBrowserHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @class NSWindow, WebView; 10 | 11 | @interface SPWebBrowserHelper : NSObject 12 | { 13 | struct WebBrowserOSX *_browser; 14 | NSWindow *_external_links_window; 15 | WebView *_external_links_webview; 16 | } 17 | 18 | - (void)webView:(id)fp8 decidePolicyForNavigationAction:(id)fp12 request:(id)fp16 frame:(id)fp20 decisionListener:(id)fp24; 19 | - (void)webView:(id)fp8 didFinishLoadForFrame:(id)fp12; 20 | - (void)webView:(id)fp8 resource:(id)fp12 didReceiveResponse:(id)fp16 fromDataSource:(id)fp20; 21 | - (void)progressEstimateChanged:(id)fp8; 22 | - (void)progressStarted:(id)fp8; 23 | - (id)webView:(id)fp8 contextMenuItemsForElement:(id)fp12 defaultMenuItems:(id)fp16; 24 | - (unsigned int)webView:(id)fp8 dragDestinationActionMaskForDraggingInfo:(id)fp12; 25 | - (id)webView:(id)fp8 createWebViewWithRequest:(id)fp12; 26 | - (void)setBrowserInstance:(struct WebBrowserOSX *)fp8; 27 | - (void)dealloc; 28 | - (id)init; 29 | 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | English 6 | CFBundleExecutable 7 | ${EXECUTABLE_NAME} 8 | CFBundleName 9 | ${PRODUCT_NAME} 10 | CFBundleIconFile 11 | 12 | CFBundleIdentifier 13 | com.georgebrock.Lastify 14 | CFBundleInfoDictionaryVersion 15 | 6.0 16 | CFBundlePackageType 17 | BNDL 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | 1.0 22 | NSPrincipalClass 23 | LastifyController 24 | SIMBLTargetApplications 25 | 26 | 27 | BundleIdentifier 28 | com.spotify.client 29 | MaxBundleVersion 30 | 50100098 31 | MinBundleVersion 32 | 36574 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /class-dump-headers/SPPurchasePasswordCheckWindowController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSWindowController.h" 8 | 9 | @class NSButton, NSString, NSTextField, NSTextView; 10 | 11 | @interface SPPurchasePasswordCheckWindowController : NSWindowController 12 | { 13 | NSString *_username; 14 | _Bool _rememberMe; 15 | _Bool _result; 16 | struct UserPasswordVerifier *_passwordVerifier; 17 | NSTextField *headerLabel; 18 | NSTextField *descLabel; 19 | NSTextField *usernameLabel; 20 | NSTextField *usernameField; 21 | NSTextField *passwordLabel; 22 | NSTextField *passwordField; 23 | NSTextView *forgotView; 24 | NSButton *rememberMeField; 25 | NSButton *cancelButton; 26 | NSButton *verifyButton; 27 | } 28 | 29 | - (id)initWithPasswordVerifier:(const struct UserPasswordVerifier *)fp8:(id)fp12:(_Bool)fp16; 30 | - (_Bool)result; 31 | - (_Bool)rememberMe; 32 | - (void)verifyAction:(id)fp8; 33 | - (void)alertDidEnd:(id)fp8 returnCode:(int)fp12 contextInfo:(void *)fp16; 34 | - (void)cancelAction:(id)fp8; 35 | - (void)setEnabledFields:(BOOL)fp8; 36 | - (void)windowDidLoad; 37 | 38 | @end 39 | 40 | -------------------------------------------------------------------------------- /class-dump-headers/SPAutocompleteMenuCell.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @class NSTextField; 10 | 11 | @interface SPAutocompleteMenuCell : NSView 12 | { 13 | int _tag; 14 | BOOL _highlighted; 15 | // Error parsing type: {Completion="displayable"{String=""($_2215="t"*"_d"^{StrPtrStruct})}"identifier"{String=""($_2215="t"*"_d"^{StrPtrStruct})}"entry_type"i"show_identifier"B"command"{FastDelegate1="m_Closure"{ClosurePtr="m_pthis"^{GenericClass}"m_pFunction"{?="__pfn"^"__delta"i}}}}, name: _completion 16 | NSTextField *text; 17 | NSTextField *identifier; 18 | } 19 | 20 | - (void)drawRect:(struct _NSRect)fp8; 21 | - (int)tag; 22 | - (void)setTag:(int)fp8; 23 | - (struct Completion)completion; 24 | - (void)setCompletion:(struct Completion)fp8; 25 | - (BOOL)isHighlighted; 26 | - (void)setHighlighted:(BOOL)fp8; 27 | - (BOOL)selectable; 28 | - (id).cxx_construct; 29 | - (void).cxx_destruct; 30 | 31 | @end 32 | 33 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Lastify 2 | 3 | Screenshot of Lastify 4 | 5 | A SIMBL plugin that adds Last.fm Love, Ban and Tagging capabilities to Spotify. 6 | 7 | ### Installation instructions 8 | 9 | 1. Download and install [SIMBL](http://www.culater.net/software/SIMBL/SIMBL.php "Smart Input Manager Bundle Loader") 10 | 2. [Download the latest version of Lastify](https://github.com/georgebrock/lastify/downloads) 11 | 3. Put the Lastify.bundle file into your /Users/your-username-here/Library/Application Support/SIMBL/Plugins directory 12 | 13 | ### Features 14 | 15 | * Add edit and remove Last.fm tags for the track you're listening to in Spotify 16 | * Love the track you're listening to in Spotify on Last.fm 17 | * Ban the track you're listening to in Spotify on Last.fm 18 | * User interface integrated into Spotify 19 | * Securely stores authentication details in the KeyChain (so you don't have to log into Last.fm every time) 20 | 21 | ### Still to do 22 | 23 | * Handle specific errors from Last.fm API (e.g. expired auth tokens should be automatically removed from the KeyChain and re-requested from Last.fm) 24 | 25 | ### Attribution 26 | 27 | Lastify uses the famfamfam Silk icon set. See [famfamfam.com](http://www.famfamfam.com/lab/icons/silk/) for more information. 28 | -------------------------------------------------------------------------------- /class-dump-headers/MATrackingArea.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | #import "NSCoding-Protocol.h" 10 | #import "NSCopying-Protocol.h" 11 | 12 | @class NSDictionary; 13 | 14 | @interface MATrackingArea : NSObject 15 | { 16 | struct _NSRect _rect; 17 | unsigned int _options; 18 | id _owner; 19 | NSDictionary *_userInfo; 20 | struct _NSPoint _lastMovedPoint; 21 | BOOL _inside; 22 | } 23 | 24 | + (void)initialize; 25 | + (void)checkMouseLocation:(id)fp8; 26 | + (void)addTrackingArea:(id)fp8 toView:(id)fp12; 27 | + (void)removeTrackingArea:(id)fp8 fromView:(id)fp12; 28 | + (id)trackingAreasForView:(id)fp8; 29 | - (id)initWithRect:(struct _NSRect)fp8 options:(unsigned int)fp24 owner:(id)fp28 userInfo:(id)fp32; 30 | - (void)dealloc; 31 | - (struct _NSRect)rect; 32 | - (void)setRect:(struct _NSRect)fp8; 33 | - (unsigned int)options; 34 | - (id)owner; 35 | - (id)userInfo; 36 | - (struct _NSPoint)_lastMovedPoint; 37 | - (void)_setLastMovedPoint:(struct _NSPoint)fp8; 38 | - (BOOL)_inside; 39 | - (void)_setInside:(BOOL)fp8; 40 | - (void)_setNotInside; 41 | - (id)copyWithZone:(struct _NSZone *)fp8; 42 | - (id)initWithCoder:(id)fp8; 43 | - (void)encodeWithCoder:(id)fp8; 44 | 45 | @end 46 | 47 | -------------------------------------------------------------------------------- /SPGrowlDelegate+Lastify.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPGrowlDelegate+Lastify.m 3 | // Lastify 4 | // 5 | // Created by George on 17/01/2009. 6 | // Copyright 2009 George Brocklehurst. All rights reserved. 7 | // 8 | 9 | #import "SPGrowlDelegate+Lastify.h" 10 | #import "SPController.h" 11 | #import "LastifyController.h" 12 | #import "SPTypes.h" 13 | 14 | 15 | @interface SPGrowlDelegate (DummyReplacedMethods) 16 | - (void)_lastify_notificationWithTrackInfo:(void*)info; 17 | @end 18 | 19 | @implementation SPGrowlDelegate (Lastify) 20 | 21 | + (void)initLastify 22 | { 23 | [LastifyController swapMethod:@selector(notificationWithTrackInfo:) withMethod:@selector(_lastify_notificationWithTrackInfo:) onClass:[self class]]; 24 | } 25 | 26 | - (void)_lastify_notificationWithTrackInfo:(struct TrackInfo*)info 27 | { 28 | [self _lastify_notificationWithTrackInfo:info]; 29 | 30 | if(info != NULL) 31 | { 32 | NSString *songTitle = [[NSString alloc] initWithCString:info->_field3 encoding:NSUTF8StringEncoding]; 33 | 34 | NSString *dockTitle = [[[[[SPController sharedController] applicationDockMenu:nil] itemArray] objectAtIndex:0] title]; 35 | 36 | int removeLength = [songTitle length] + 3; 37 | NSString *artist = [dockTitle stringByReplacingCharactersInRange:NSMakeRange([dockTitle length]-removeLength, removeLength) withString:@""]; 38 | 39 | [[LastifyController sharedInstance] startNewTrack:songTitle byArtist:artist]; 40 | 41 | [songTitle release], songTitle = nil; 42 | } 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /class-dump-headers/SPView.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSView.h" 8 | 9 | @class MATrackingArea; 10 | 11 | @interface SPView : NSView 12 | { 13 | MATrackingArea *trackingArea; 14 | struct _NSPoint initialMouseLocation; 15 | BOOL isRightClick; 16 | BOOL acceptsDrag; 17 | BOOL _dragging; 18 | } 19 | 20 | - (BOOL)wantsPeriodicDraggingUpdates; 21 | - (unsigned int)draggingEntered:(id)fp8; 22 | - (unsigned int)draggingUpdated:(id)fp8; 23 | - (void)draggingExited:(id)fp8; 24 | - (BOOL)performDragOperation:(id)fp8; 25 | - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)fp8; 26 | - (struct _NSPoint)remapEvent:(id)fp8; 27 | - (BOOL)acceptsFirstMouse:(id)fp8; 28 | - (BOOL)acceptsFirstResponder; 29 | - (id)initWithFrame:(struct _NSRect)fp8; 30 | - (void)dealloc; 31 | - (void)drawRect:(struct _NSRect)fp8; 32 | - (void)resetCursorRects; 33 | - (BOOL)performKeyEquivalent:(id)fp8; 34 | - (void)scrollWheel:(id)fp8; 35 | - (void)mouseEntered:(id)fp8; 36 | - (void)mouseExited:(id)fp8; 37 | - (void)mouseMoved:(id)fp8; 38 | - (id)view:(id)fp8 stringForToolTip:(int)fp12 point:(struct _NSPoint)fp16 userData:(void *)fp24; 39 | - (BOOL)isFlipped; 40 | - (void)mouseDown:(id)fp8; 41 | - (void)mouseUp:(id)fp8; 42 | - (void)mouseDragged:(id)fp8; 43 | - (void)rightMouseDown:(id)fp8; 44 | - (void)rightMouseUp:(id)fp8; 45 | - (void)viewDidEndLiveResize; 46 | - (void)viewWillStartLiveResize; 47 | 48 | @end 49 | 50 | -------------------------------------------------------------------------------- /LastifyLastfmClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyLastfmClient.h 3 | // Lastify 4 | // 5 | // Created by George on 16/01/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LastifyLastfmClient : NSObject 13 | { 14 | NSString *APIKey; 15 | NSString *APISecret; 16 | NSString *authToken; 17 | NSString *sessionKey; 18 | NSString *username; 19 | 20 | BOOL waitingForUserAuth; 21 | BOOL sessionReady; 22 | } 23 | 24 | @property(readwrite, copy) NSString *APIKey; 25 | @property(readwrite, copy) NSString *APISecret; 26 | @property(readwrite, copy) NSString *authToken; 27 | @property(readwrite, copy) NSString *sessionKey; 28 | @property(readwrite, copy) NSString *username; 29 | 30 | @property(readwrite, assign) BOOL waitingForUserAuth; 31 | @property(readwrite, assign) BOOL sessionReady; 32 | 33 | - (id)initWithAPIKey:(NSString*)newAPIKey APISecret:(NSString*)newSecret; 34 | 35 | - (void)authenticate; 36 | - (void)authenticateQuietly; 37 | 38 | - (void)startNewSession; 39 | 40 | - (BOOL)loveTrack:(NSString*)trackName byArtist:(NSString*)artistName; 41 | - (BOOL)banTrack:(NSString*)trackName byArtist:(NSString*)artistName; 42 | - (NSArray*)getTagsForTrack:(NSString*)trackName byArtist:(NSString*)artistName; 43 | - (BOOL)addTags:(NSArray*)tags toTrack:(NSString*)trackName byArtist:(NSString*)artistName; 44 | - (BOOL)removeTags:(NSArray*)tags fromTrack:(NSString*)trackName byArtist:(NSString*)artistName; 45 | - (NSArray*)getPlaylists; 46 | - (BOOL)addTrack:(NSString*)trackName byArtist:(NSString*)artistName toPlaylist:(NSString*)playlistID; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /SPTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPTypes.h 3 | * Lastify 4 | * 5 | */ 6 | 7 | // 8 | // Automatically generated by Class Dump ( http://www.codethecode.com/projects/class-dump/ ) 9 | // A lot of information not directly relevant to this app has been removed, it might be worth starting again if using this for a different purpose 10 | // 11 | 12 | struct ArtistInfo; 13 | 14 | struct FileId { 15 | unsigned char _field1[20]; 16 | }; 17 | 18 | struct MusicFormat { 19 | unsigned char _field1; 20 | unsigned char _field2; 21 | unsigned char _field3; 22 | unsigned char _field4; 23 | int _field5; 24 | }; 25 | 26 | struct RefPtr_AlbumInfo { 27 | struct AlbumInfo *_field1; 28 | }; 29 | 30 | struct RefPtr_ArtistInfo { 31 | struct ArtistInfo *_field1; 32 | }; 33 | 34 | struct RefPtr_TrackInfo { 35 | struct TrackInfo *_field1; 36 | }; 37 | 38 | struct TrackId { 39 | unsigned char _field1[16]; 40 | }; 41 | 42 | struct VersionAndExpiry { 43 | unsigned int _field1; 44 | unsigned int _field2; 45 | }; 46 | 47 | struct TrackInfo { 48 | void **_field1; 49 | long _field2; 50 | char *_field3; 51 | unsigned int _field4; 52 | struct TrackId _field5; 53 | struct FileId _field6; 54 | struct MusicFormat _field7; 55 | struct RefPtr_ArtistInfo _field8; 56 | struct ArtistInfo **_field9; 57 | struct RefPtr_AlbumInfo _field10; 58 | struct RefPtr_TrackInfo _field11; 59 | unsigned char _field12; 60 | unsigned char _field13; 61 | unsigned int a; 62 | unsigned int b; 63 | unsigned int c; 64 | unsigned int d; 65 | unsigned int e; 66 | unsigned int f; 67 | unsigned int g; 68 | unsigned char _field14; 69 | unsigned char _field15; 70 | struct VersionAndExpiry _field16; 71 | struct PurchaseLinks *_field17; 72 | }; 73 | 74 | 75 | -------------------------------------------------------------------------------- /class-dump-headers/GrowlApplicationBridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSObject.h" 8 | 9 | @interface GrowlApplicationBridge : NSObject 10 | { 11 | } 12 | 13 | + (void)setGrowlDelegate:(id)fp8; 14 | + (id)growlDelegate; 15 | + (void)notifyWithTitle:(id)fp8 description:(id)fp12 notificationName:(id)fp16 iconData:(id)fp20 priority:(int)fp24 isSticky:(BOOL)fp28 clickContext:(id)fp32; 16 | + (void)notifyWithTitle:(id)fp8 description:(id)fp12 notificationName:(id)fp16 iconData:(id)fp20 priority:(int)fp24 isSticky:(BOOL)fp28 clickContext:(id)fp32 identifier:(id)fp36; 17 | + (void)notifyWithDictionary:(id)fp8; 18 | + (BOOL)isGrowlInstalled; 19 | + (BOOL)isGrowlRunning; 20 | + (void)displayInstallationPromptIfNeeded; 21 | + (BOOL)registerWithDictionary:(id)fp8; 22 | + (void)reregisterGrowlNotifications; 23 | + (void)setWillRegisterWhenGrowlIsReady:(BOOL)fp8; 24 | + (BOOL)willRegisterWhenGrowlIsReady; 25 | + (id)registrationDictionaryFromDelegate; 26 | + (id)registrationDictionaryFromBundle:(id)fp8; 27 | + (id)bestRegistrationDictionary; 28 | + (id)registrationDictionaryByFillingInDictionary:(id)fp8; 29 | + (id)registrationDictionaryByFillingInDictionary:(id)fp8 restrictToKeys:(id)fp12; 30 | + (id)notificationDictionaryByFillingInDictionary:(id)fp8; 31 | + (id)frameworkInfoDictionary; 32 | + (id)_applicationNameForGrowlSearchingRegistrationDictionary:(id)fp8; 33 | + (id)_applicationIconDataForGrowlSearchingRegistrationDictionary:(id)fp8; 34 | + (void)growlNotificationWasClicked:(id)fp8; 35 | + (void)growlNotificationTimedOut:(id)fp8; 36 | + (void)connectionDidDie:(id)fp8; 37 | + (id)growlProxy; 38 | + (void)_growlIsReady:(id)fp8; 39 | + (BOOL)_launchGrowlIfInstalledWithRegistrationDictionary:(id)fp8; 40 | + (BOOL)launchGrowlIfInstalled; 41 | 42 | @end 43 | 44 | -------------------------------------------------------------------------------- /LastifyController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyController.h 3 | // Lastify 4 | // 5 | // Created by George on 16/01/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import 10 | #import "LastifyLastfmClient.h" 11 | 12 | 13 | @interface LastifyController : NSObject 14 | { 15 | IBOutlet NSDrawer *drawer; 16 | IBOutlet NSPanel *tagPanel; 17 | IBOutlet NSTokenField *tagField; 18 | IBOutlet NSPanel *playlistPanel; 19 | IBOutlet NSArrayController *playlistsController; 20 | LastifyLastfmClient *lastfm; 21 | 22 | IBOutlet NSButton *loveButton; 23 | IBOutlet NSButton *banButton; 24 | IBOutlet NSButton *tagButton; 25 | IBOutlet NSButton *listButton; 26 | IBOutlet NSButton *loginButton; 27 | 28 | IBOutlet NSImageView *statusImage; 29 | 30 | NSString *currentTrack; 31 | NSString *currentArtist; 32 | NSArray *currentTags; 33 | } 34 | 35 | @property(readonly, retain) LastifyLastfmClient *lastfm; 36 | @property(readwrite, retain) NSString *currentTrack; 37 | @property(readwrite, retain) NSString *currentArtist; 38 | @property(readwrite, retain) NSArray *currentTags; 39 | 40 | + (BOOL)swapMethod:(SEL)firstSelector withMethod:(SEL)secondSelector onClass:(Class)class; 41 | + (LastifyController*)sharedInstance; 42 | 43 | - (void)initLastfmConnection; 44 | - (void)loadUserInterface; 45 | 46 | - (void)startNewTrack:(NSString*)trackName byArtist:(NSString*)artistName; 47 | 48 | - (void)displayWorkingIcon; 49 | - (void)displayResultIcon:(BOOL)result; 50 | 51 | - (IBAction)loveTrack:(id)sender; 52 | - (IBAction)banTrack:(id)sender; 53 | 54 | - (IBAction)tagTrack:(id)sender; 55 | - (IBAction)taggingOK:(id)sender; 56 | - (IBAction)taggingCancel:(id)sender; 57 | 58 | - (IBAction)addTrackToPlaylist:(id)sender; 59 | - (IBAction)playlistOK:(id)sender; 60 | - (IBAction)playlistCancel:(id)sender; 61 | 62 | - (IBAction)auth:(id)sender; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /class-dump-headers/SPController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import 8 | 9 | @class SPUndoManager; 10 | 11 | @interface SPController : NSObject 12 | { 13 | SPUndoManager *_undo_manager; 14 | } 15 | 16 | + (id)sharedController; 17 | + (id)allocWithZone:(struct _NSZone *)fp8; 18 | + (int)windowAtTopOfEvent:(id)fp8; 19 | - (id)copyWithZone:(struct _NSZone *)fp8; 20 | - (id)retain; 21 | - (unsigned int)retainCount; 22 | - (void)release; 23 | - (id)autorelease; 24 | - (void)setFeedbackRegardingButton:(id)fp8; 25 | - (void)setFeedbackScreenshotButton:(id)fp8; 26 | - (void)setFeedbackTitleCell:(id)fp8; 27 | - (void)setFeedbackFreeText:(id)fp8; 28 | - (void)closeFeedbackForm:(id)fp8; 29 | - (void)sendFeedback:(id)fp8; 30 | - (void)resetFeedbackForm; 31 | - (void)processFeedback; 32 | - (void)applicationWillFinishLaunching:(id)fp8; 33 | - (void)applicationDidFinishLaunching:(id)fp8; 34 | - (void)applicationWillTerminate:(id)fp8; 35 | - (BOOL)validateMenuItem:(id)fp8; 36 | - (void)doAction:(id)fp8; 37 | - (void)copy:(id)fp8; 38 | - (void)paste:(id)fp8; 39 | - (void)cut:(id)fp8; 40 | - (void)undo:(id)fp8; 41 | - (void)redo:(id)fp8; 42 | - (void)selectAll:(id)fp8; 43 | - (void)selectNone:(id)fp8; 44 | - (void)delete:(id)fp8; 45 | - (void)mediaKeyEvent:(id)fp8; 46 | - (void)getURI:(id)fp8 withReplyEvent:(id)fp12; 47 | - (void)openLink:(id)fp8 userData:(id)fp12 error:(id *)fp16; 48 | - (BOOL)application:(id)fp8 openFile:(id)fp12; 49 | - (void)application:(id)fp8 openFiles:(id)fp12; 50 | - (void)setupWindowAndViews; 51 | - (id)windowWillReturnUndoManager:(id)fp8; 52 | - (BOOL)applicationShouldHandleReopen:(id)fp8 hasVisibleWindows:(BOOL)fp12; 53 | - (void)setSchedulerTimer; 54 | - (void)setWindowController:(struct WindowControllerOSX *)fp8; 55 | - (struct WindowControllerOSX *)windowController; 56 | - (void)startup; 57 | - (id)applicationDockMenu:(id)fp8; 58 | - (void)notificationDelivery:(id)fp8; 59 | - (id)mainWindow; 60 | - (id)feedbackWindow; 61 | - (id)undoManager; 62 | - (void)pasteboard:(id)fp8 provideDataForType:(id)fp12; 63 | - (void)pasteboard:(id)fp8 provideDataForType:(id)fp12 flags:(unsigned int)fp16; 64 | - (void)declareDraggedTypesForPasteboard:(id)fp8; 65 | - (id)dragPasteboard; 66 | - (void)setDragPasteboard:(id)fp8; 67 | - (BOOL)isShuttingDown; 68 | - (void)dealloc; 69 | - (void)prepareForShutdown; 70 | - (void)focusCurrentlyPlayingTrack; 71 | 72 | @end 73 | 74 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rexml/document' 2 | 3 | module Spotify 4 | def self.path 5 | '/Applications/Spotify.app' 6 | end 7 | 8 | def self.bundle_version 9 | plist_value :CFBundleVersion 10 | end 11 | 12 | def self.version 13 | plist_value :CFBundleShortVersionString 14 | end 15 | 16 | def self.plist_value(key) 17 | @spotify_plist ||= REXML::Document.new(File.new("#{path}/Contents/Info.plist")) 18 | REXML::XPath.match(@spotify_plist, %Q{//key[text()="#{key.to_s}"]/following-sibling::string[1]}).pop.text 19 | end 20 | end 21 | 22 | desc 'Update the SIMBL maximum bundle version to the current version of Spotify' 23 | task :update do 24 | lastify_plist = REXML::Document.new(File.new('Info.plist')) 25 | array_node = REXML::XPath.match(lastify_plist, '//key[text()="SIMBLTargetApplications"]/following-sibling::array[1]') 26 | max_version_node = REXML::XPath.match(array_node, '//key[text()="MaxBundleVersion"]/following-sibling::string[1]').pop 27 | max_version_node.text = Spotify.bundle_version 28 | File.open('Info.plist', 'w') {|f| f.write(lastify_plist) } 29 | 30 | puts "\nVersion updated to #{Spotify.version} (#{Spotify.bundle_version})" 31 | end 32 | 33 | desc 'Recompile the plugin' 34 | task :build do 35 | system 'xcodebuild' 36 | end 37 | 38 | desc 'Creates a ZIP file of the current build' 39 | task :package do 40 | Dir.chdir 'build/Release' 41 | system 'zip', '-r', "lastify-#{Spotify.version}.zip", 'Lastify.bundle' 42 | end 43 | 44 | desc 'Uploads a ZIP file of the current build to Github' 45 | task :upload do 46 | begin 47 | require 'net/github-upload' 48 | rescue LoadError 49 | raise 'Please run `gem install net-github-upload` to continue' 50 | end 51 | 52 | login, token = ['github.user', 'github.token'].map{|key| `git config #{key}`.chomp } 53 | github = Net::GitHub::Upload.new(:login => login, :token => token) 54 | github.upload(:repos => 'lastify', :file => "build/Release/lastify-#{Spotify.version}.zip", :description => "for Spotify #{Spotify.version}") 55 | end 56 | 57 | namespace :spotify do 58 | desc "Outputs the current Spotify version" 59 | task :version do 60 | puts "#{Spotify.version} (#{Spotify.bundle_version})" 61 | end 62 | 63 | desc 'Restart Spotify' 64 | task :restart do 65 | system 'killall', 'Spotify' 66 | system 'open', Spotify.path 67 | end 68 | end 69 | 70 | desc 'Runs the "update", "build" and "spotify:restart" tasks' 71 | task :update_and_build => [:update, :build, :'spotify:restart'] 72 | 73 | task :default => :update_and_build 74 | -------------------------------------------------------------------------------- /class-dump-headers/SPAutocompleteMenuWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSWindow.h" 8 | 9 | @class NSControl, NSNib, NSString, SPAutocompleteMenuCell; 10 | 11 | @interface SPAutocompleteMenuWindow : NSWindow 12 | { 13 | NSControl *parentControl; 14 | struct AutocompleteDelegate *_completionDelegate; 15 | struct SPACMWWrap cpp; 16 | struct _NSRect _offset; 17 | NSNib *cellViewNib; 18 | SPAutocompleteMenuCell *headerCell; 19 | SPAutocompleteMenuCell *standardCell; 20 | SPAutocompleteMenuCell *standardWithIdentifierCell; 21 | SPAutocompleteMenuCell *separatorCell; 22 | SPAutocompleteMenuCell *_selectedCell; 23 | // Error parsing type: {FastDelegate2="m_Closure"{ClosurePtr="m_pthis"^{GenericClass}"m_pFunction"{?="__pfn"^"__delta"i}}}, name: _completionMade 24 | BOOL _skipNextSuggestion; 25 | NSString *_originallyTypedString; 26 | BOOL _hasCompleted; 27 | BOOL _showAutomatically; 28 | BOOL _showManually; 29 | } 30 | 31 | - (id)originallyTypedString; 32 | - (void)setOriginallyTypedString:(id)fp8; 33 | - (void)setShowAutomatically:(BOOL)fp8; 34 | - (void)setOffset:(struct _NSRect)fp8; 35 | - (id)initWithContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 backing:(unsigned int)fp28 defer:(BOOL)fp32; 36 | - (void)dealloc; 37 | - (void)moveToBeneathControlWithHeight:(float)fp8; 38 | - (void)addToControlIfNeeded:(id)fp8; 39 | - (void)showOn:(id)fp8; 40 | - (void)reset; 41 | - (void)hide; 42 | - (void)cancel; 43 | - (void)controlTextDidBeginEditing:(id)fp8; 44 | - (void)controlTextDidChange:(id)fp8; 45 | - (void)controlTextDidEndEditing:(id)fp8; 46 | - (BOOL)control:(id)fp8 textView:(id)fp12 doCommandBySelector:(SEL)fp16; 47 | - (id)trackingAreaForView:(id)fp8 completion:(const struct Completion *)fp12; 48 | - (void)setCompletionMade:(struct FastDelegate2)fp8; 49 | - (void)setCompletionDelegate:(struct AutocompleteDelegate *)fp8; 50 | - (void)setCompletions:(const struct Array *)fp8; 51 | - (void)setSelectedCell:(id)fp8; 52 | - (void)userSetSelectedCell:(id)fp8; 53 | - (id)cellBefore:(id)fp8; 54 | - (id)cellAfter:(id)fp8; 55 | - (void)mouseEntered:(id)fp8; 56 | - (void)mouseExited:(id)fp8; 57 | - (void)mouseUp:(id)fp8; 58 | - (void)moveUp:(id)fp8; 59 | - (void)moveDown:(id)fp8; 60 | - (id).cxx_construct; 61 | 62 | @end 63 | 64 | -------------------------------------------------------------------------------- /class-dump-headers/SPEditMetadataWindowController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | #import "NSWindowController.h" 8 | 9 | @class NSButton, NSComboBox, NSImageView, NSTextField; 10 | 11 | @interface SPEditMetadataWindowController : NSWindowController 12 | { 13 | id delegate; 14 | NSTextField *filepathField; 15 | NSTextField *nameLabel; 16 | NSTextField *nameField; 17 | NSTextField *artistLabel; 18 | NSTextField *artistField; 19 | NSTextField *albumArtistLabel; 20 | NSTextField *albumArtistField; 21 | NSTextField *albumLabel; 22 | NSTextField *albumField; 23 | NSTextField *genreLabel; 24 | NSComboBox *genreBox; 25 | NSTextField *trackNumberLabel; 26 | NSTextField *trackNumberField; 27 | NSTextField *discNumberLabel; 28 | NSTextField *discNumberField; 29 | NSTextField *yearLabel; 30 | NSTextField *yearField; 31 | NSTextField *copyrightLabel; 32 | NSTextField *copyrightField; 33 | NSTextField *composerLabel; 34 | NSTextField *composerField; 35 | NSTextField *commentsLabel; 36 | NSTextField *commentsField; 37 | NSButton *cancelButton; 38 | NSButton *applyButton; 39 | NSButton *previousButton; 40 | NSButton *nextButton; 41 | NSImageView *lockImage; 42 | // Error parsing type: ^{MetadataEditor=ii{Array="_mem"^{TrackData}"_alloc"I"_count"I}[18{String=""($_2215="t"*"_d"^{StrPtrStruct})}][18B][18i]{Array="_mem"^^{MetadataParser}"_alloc"I"_count"I}{FastDelegate2="m_Closure"{ClosurePtr="m_pthis"^{GenericClass}"m_pFunction"{?="__pfn"^"__delta"i}}}{FastDelegate0="m_Closure"{ClosurePtr="m_pthis"^{GenericClass}"m_pFunction"{?="__pfn"^"__delta"i}}}{String=""($_2215="t"*"_d"^{StrPtrStruct})}Bi}, name: _editor 43 | struct MetadataEditorWindowDelegate *_windowDelegate; 44 | } 45 | 46 | - (id)initWithEditor:(struct MetadataEditor *)fp8 delegate:(struct MetadataEditorWindowDelegate *)fp12; 47 | - (void)windowDidLoad; 48 | - (void)controlTextDidBeginEditing:(id)fp8; 49 | - (void)controlTextDidChange:(id)fp8; 50 | - (int)numberOfItemsInComboBox:(id)fp8; 51 | - (id)comboBox:(id)fp8 objectValueForItemAtIndex:(int)fp12; 52 | - (void)cancelAction:(id)fp8; 53 | - (void)applyAction:(id)fp8; 54 | - (void)nextAction:(id)fp8; 55 | - (void)previousAction:(id)fp8; 56 | - (void)windowWillClose:(id)fp8; 57 | - (_Bool)commitChanges; 58 | - (void)updateFieldsFromEditor; 59 | - (void)updateEditorFromField; 60 | - (void)updateButtons; 61 | 62 | @end 63 | 64 | -------------------------------------------------------------------------------- /LastifyController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyController.m 3 | // Lastify 4 | // 5 | // Created by George on 16/01/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import 10 | #import "LastifyController.h" 11 | #import "SPController.h" 12 | #import "SPGrowlDelegate+Lastify.h" 13 | #import "NSButton+Lastify.h" 14 | 15 | @implementation LastifyController 16 | 17 | @synthesize 18 | lastfm, 19 | currentTrack, 20 | currentArtist, 21 | currentTags; 22 | 23 | + (void)load 24 | { 25 | [SPGrowlDelegate initLastify]; 26 | [[LastifyController sharedInstance] initLastfmConnection]; 27 | } 28 | 29 | + (LastifyController*)sharedInstance 30 | { 31 | static LastifyController *plugin = nil; 32 | 33 | if(!plugin) 34 | plugin = [[LastifyController alloc] init]; 35 | 36 | return plugin; 37 | } 38 | 39 | + (BOOL)swapMethod:(SEL)firstSelector withMethod:(SEL)secondSelector onClass:(Class)class 40 | { 41 | Method firstMethod, secondMethod; 42 | 43 | firstMethod = class_getInstanceMethod(class, firstSelector); 44 | secondMethod = class_getInstanceMethod(class, secondSelector); 45 | if (firstMethod == nil || secondMethod == nil) 46 | return NO; 47 | 48 | method_exchangeImplementations(firstMethod, secondMethod); 49 | return YES; 50 | } 51 | 52 | - (void)dealloc 53 | { 54 | [lastfm release], lastfm = nil; 55 | [currentTrack release], currentTrack = nil; 56 | [currentArtist release], currentArtist = nil; 57 | [currentTags release], currentTags = nil; 58 | [super dealloc]; 59 | } 60 | 61 | - (void)initLastfmConnection 62 | { 63 | lastfm = [[LastifyLastfmClient alloc] initWithAPIKey:@"aa31898c9c79401a7ddaa6c8f089ccad" APISecret:@"92773b344ec2e14cd6f5780b83c06265"]; 64 | [lastfm authenticateQuietly]; 65 | } 66 | 67 | - (void)loadUserInterface 68 | { 69 | if(drawer != nil || [[SPController sharedController] mainWindow] == nil) 70 | return; 71 | 72 | [NSBundle loadNibNamed:@"LastifyInterface" owner:self]; 73 | 74 | [statusImage setImage:nil]; 75 | 76 | [loveButton setTextColor:[NSColor whiteColor]]; 77 | [banButton setTextColor:[NSColor whiteColor]]; 78 | [tagButton setTextColor:[NSColor whiteColor]]; 79 | [loginButton setTextColor:[NSColor whiteColor]]; 80 | [listButton setTextColor:[NSColor whiteColor]]; 81 | 82 | [drawer setParentWindow:[[SPController sharedController] mainWindow]]; 83 | NSSize contentSize = NSMakeSize(71, 200); 84 | [drawer setMaxContentSize:contentSize]; 85 | [drawer setMinContentSize:contentSize]; 86 | [drawer setLeadingOffset:29]; 87 | [drawer setTrailingOffset:10]; 88 | [drawer openOnEdge:NSMaxXEdge]; 89 | [drawer setDelegate:self]; 90 | 91 | [drawer setContentSize:contentSize]; 92 | } 93 | 94 | - (void)displayWorkingIcon 95 | { 96 | NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"icon_loading" ofType:@"gif"]; 97 | NSImage *img = [[NSImage alloc] initWithContentsOfFile:path]; 98 | [statusImage setImage:img]; 99 | [img release], img = nil; 100 | } 101 | 102 | - (void)displayResultIcon:(BOOL)result 103 | { 104 | NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:(result ? @"icon_tick" : @"icon_error") ofType:@"png"]; 105 | NSImage *img = [[NSImage alloc] initWithContentsOfFile:path]; 106 | [statusImage setImage:img]; 107 | [img release], img = nil; 108 | 109 | [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(clearResultIcon:) userInfo:nil repeats:FALSE]; 110 | } 111 | 112 | - (void)clearResultIcon:(NSTimer*)timer 113 | { 114 | [statusImage setImage:nil]; 115 | } 116 | 117 | - (IBAction)auth:(id)sender 118 | { 119 | if(lastfm.sessionKey) 120 | return; 121 | 122 | [lastfm authenticateQuietly]; 123 | if(lastfm.sessionKey) 124 | return; 125 | 126 | NSAlert *authAlert = [NSAlert 127 | alertWithMessageText:@"You must authorise Lastify to access your Last.fm profile information" 128 | defaultButton:@"Continue" 129 | alternateButton:@"Cancel" 130 | otherButton:nil 131 | informativeTextWithFormat:@"If you click 'Continue' the Last.fm website will open so you can authorise Lastify"]; 132 | 133 | if([authAlert runModal] == NSAlertAlternateReturn) 134 | return; 135 | 136 | [lastfm authenticate]; 137 | 138 | if(lastfm.waitingForUserAuth) 139 | { 140 | NSAlert *authCompleteAlert = [NSAlert 141 | alertWithMessageText:@"Authorisation is complete" 142 | defaultButton:@"Continue" 143 | alternateButton:@"Cancel" 144 | otherButton:nil 145 | informativeTextWithFormat:@"I've authorised Lastify to access my Last.fm profile"]; 146 | 147 | if([authCompleteAlert runModal] == NSAlertAlternateReturn) 148 | return; 149 | 150 | [lastfm startNewSession]; 151 | } 152 | } 153 | 154 | - (IBAction)loveTrack:(id)sender 155 | { 156 | if(!currentTrack || !currentArtist) 157 | return; 158 | 159 | [self displayWorkingIcon]; 160 | BOOL result = [lastfm loveTrack:currentTrack byArtist:currentArtist]; 161 | [self displayResultIcon:result]; 162 | } 163 | 164 | - (IBAction)banTrack:(id)sender 165 | { 166 | if(!currentTrack || !currentArtist) 167 | return; 168 | 169 | [self displayWorkingIcon]; 170 | BOOL result = [lastfm banTrack:currentTrack byArtist:currentArtist]; 171 | [self displayResultIcon:result]; 172 | } 173 | 174 | - (IBAction)tagTrack:(id)sender 175 | { 176 | self.currentTags = [lastfm getTagsForTrack:currentTrack byArtist:currentArtist]; 177 | [tagField setObjectValue:self.currentTags]; 178 | [NSApp beginSheet:tagPanel modalForWindow:[[SPController sharedController] mainWindow] modalDelegate:self didEndSelector:NULL contextInfo:nil]; 179 | } 180 | 181 | - (IBAction)taggingOK:(id)sender 182 | { 183 | [self displayWorkingIcon]; 184 | 185 | NSArray *newTags = [tagField objectValue]; 186 | NSMutableArray *removeTags = [self.currentTags mutableCopy]; 187 | NSMutableArray *addTags = [NSMutableArray arrayWithCapacity:[newTags count]]; 188 | 189 | NSEnumerator *tagEnum = [newTags objectEnumerator]; 190 | NSString *tag; 191 | while(tag = [tagEnum nextObject]) 192 | { 193 | [removeTags removeObject:tag]; 194 | 195 | if(![self.currentTags containsObject:tag]) 196 | [addTags addObject:tag]; 197 | } 198 | 199 | BOOL result = TRUE; 200 | 201 | if([addTags count] > 0) 202 | { 203 | NSLog(@"LASTIFY Adding tags: %@", addTags); 204 | result = [lastfm addTags:addTags toTrack:currentTrack byArtist:currentArtist] && result; 205 | } 206 | 207 | if([removeTags count] > 0) 208 | { 209 | NSLog(@"LASTIFY Removing tags: %@", removeTags); 210 | result = [lastfm removeTags:removeTags fromTrack:currentTrack byArtist:currentArtist] && result; 211 | } 212 | 213 | [removeTags release], removeTags = nil; 214 | 215 | [tagPanel orderOut:nil]; 216 | [NSApp endSheet:tagPanel]; 217 | 218 | [self displayResultIcon:result]; 219 | } 220 | 221 | - (void)startNewTrack:(NSString*)trackName byArtist:(NSString*)artistName 222 | { 223 | NSLog(@"LASTIFY track started: \"%@\" by %@", trackName, artistName); 224 | [self loadUserInterface]; 225 | self.currentTrack = trackName; 226 | self.currentArtist = artistName; 227 | } 228 | 229 | - (IBAction)taggingCancel:(id)sender 230 | { 231 | [tagPanel orderOut:nil]; 232 | [NSApp endSheet:tagPanel]; 233 | } 234 | 235 | - (IBAction)addTrackToPlaylist:(id)sender 236 | { 237 | [playlistsController setContent:[lastfm getPlaylists]]; 238 | [NSApp beginSheet:playlistPanel modalForWindow:[[SPController sharedController] mainWindow] modalDelegate:self didEndSelector:NULL contextInfo:nil]; 239 | } 240 | 241 | - (IBAction)playlistOK:(id)sender 242 | { 243 | NSDictionary *selectedPlaylist = [[playlistsController selectedObjects] objectAtIndex:0]; 244 | if(!selectedPlaylist) 245 | return; 246 | 247 | NSString *playlistID = [selectedPlaylist valueForKey:@"id"]; 248 | BOOL result = [lastfm addTrack:self.currentTrack byArtist:self.currentArtist toPlaylist:playlistID]; 249 | 250 | [self displayResultIcon:result]; 251 | 252 | [playlistPanel orderOut:nil]; 253 | [NSApp endSheet:playlistPanel]; 254 | } 255 | 256 | - (IBAction)playlistCancel:(id)sender 257 | { 258 | [playlistPanel orderOut:nil]; 259 | [NSApp endSheet:playlistPanel]; 260 | } 261 | 262 | - (BOOL)drawerShouldClose:(NSDrawer *)sender 263 | { 264 | return FALSE; 265 | } 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /class-dump-headers/CDStructures.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generated by class-dump 3.1.2. 3 | * 4 | * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard. 5 | */ 6 | 7 | @class SPAutocompleteMenuWindow; 8 | 9 | struct $_2265 { 10 | struct TrackInfo *_field1; 11 | unsigned int _field2; 12 | struct TrackInfo *_field3; 13 | char *_field4; 14 | }; 15 | 16 | struct AdQueueEntry; 17 | 18 | struct AlbumInfo; 19 | 20 | struct Array { 21 | struct Completion *_mem; 22 | unsigned int _alloc; 23 | unsigned int _count; 24 | }; 25 | 26 | struct Array { 27 | struct TrackData *_field1; 28 | unsigned int _field2; 29 | unsigned int _field3; 30 | }; 31 | 32 | struct Array { 33 | struct MetadataParser **_field1; 34 | unsigned int _field2; 35 | unsigned int _field3; 36 | }; 37 | 38 | struct ArtistInfo; 39 | 40 | struct AudioDeviceListenerOSX; 41 | 42 | struct AutocompleteDelegate { 43 | void **_field1; 44 | }; 45 | 46 | struct CGImage; 47 | 48 | struct ClosurePtr { 49 | struct GenericClass *_field1; 50 | struct { 51 | int *_field1; 52 | } _field2; 53 | }; 54 | 55 | struct ClosurePtr { 56 | struct GenericClass *_field1; 57 | struct { 58 | int *_field1; 59 | } _field2; 60 | }; 61 | 62 | struct ClosurePtr { 63 | struct GenericClass *_field1; 64 | struct { 65 | int *_field1; 66 | } _field2; 67 | }; 68 | 69 | struct ClosurePtr { 70 | struct GenericClass *_field1; 71 | struct { 72 | int *_field1; 73 | } _field2; 74 | }; 75 | 76 | struct Completion { 77 | struct String _field1; 78 | struct String _field2; 79 | int _field3; 80 | _Bool _field4; 81 | struct FastDelegate1 _field5; 82 | }; 83 | 84 | struct Delegate; 85 | 86 | struct FastDelegate0 { 87 | struct ClosurePtr _field1; 88 | }; 89 | 90 | struct FastDelegate1 { 91 | struct ClosurePtr _field1; 92 | }; 93 | 94 | struct FastDelegate1 { 95 | struct ClosurePtr _field1; 96 | }; 97 | 98 | struct FastDelegate2 { 99 | struct ClosurePtr _field1; 100 | }; 101 | 102 | struct FileId { 103 | unsigned char _field1[20]; 104 | }; 105 | 106 | struct GenericClass; 107 | 108 | struct GuiManager; 109 | 110 | struct IGenericView; 111 | 112 | struct IPopupMenu { 113 | void **_field1; 114 | }; 115 | 116 | struct MetadataEditor { 117 | int _field1; 118 | int _field2; 119 | struct Array _field3; 120 | struct String _field4[18]; 121 | _Bool _field5[18]; 122 | int _field6[18]; 123 | struct Array _field7; 124 | struct FastDelegate2 _field8; 125 | struct FastDelegate0 _field9; 126 | struct String _field10; 127 | _Bool _field11; 128 | int _field12; 129 | }; 130 | 131 | struct MetadataEditorWindowDelegate { 132 | void **_field1; 133 | }; 134 | 135 | struct MetadataParser; 136 | 137 | struct OpaqueMenuRef; 138 | 139 | struct OsAlias; 140 | 141 | struct PlatformEditBox { 142 | void **_field1; 143 | }; 144 | 145 | struct PlatformEditBoxOSX { 146 | void **_field1; 147 | id _field2; 148 | id _field3; 149 | id _field4; 150 | id _field5; 151 | id _field6; 152 | id _field7; 153 | id _field8; 154 | struct Delegate *_field9; 155 | struct AutocompleteDelegate *_field10; 156 | unsigned int _field11; 157 | struct Rect _field12; 158 | struct Rect _field13; 159 | _Bool _field14; 160 | _Bool _field15; 161 | _Bool _field16; 162 | _Bool _field17; 163 | _Bool _field18; 164 | _Bool _field19; 165 | _Bool _field20; 166 | _Bool _field21; 167 | _Bool _field22; 168 | struct String _field23; 169 | struct String _field24; 170 | int _field25; 171 | }; 172 | 173 | struct PopupMenuOSX { 174 | void **_field1; 175 | id _field2; 176 | _Bool _field3; 177 | }; 178 | 179 | struct PurchaseLinks; 180 | 181 | struct Rect { 182 | int left; 183 | int top; 184 | int right; 185 | int bottom; 186 | }; 187 | 188 | struct RefPtr { 189 | struct AlbumInfo *_field1; 190 | }; 191 | 192 | struct RefPtr { 193 | struct ArtistInfo *_field1; 194 | }; 195 | 196 | struct SPACMWWrap { 197 | SPAutocompleteMenuWindow *objc; 198 | }; 199 | 200 | struct SkinManager; 201 | 202 | struct StrPtrStruct; 203 | 204 | struct String { 205 | union $_2215 ; 206 | }; 207 | 208 | struct TrackData; 209 | 210 | struct TrackId { 211 | unsigned char _field1[16]; 212 | }; 213 | 214 | struct TrackInfo { 215 | void **_field1; 216 | int _field2; 217 | char *_field3; 218 | unsigned int _field4; 219 | struct RefPtr _field5; 220 | struct RefPtr _field6; 221 | unsigned char _field7; 222 | unsigned char _field8; 223 | unsigned char _field9; 224 | unsigned int :1; 225 | unsigned int :1; 226 | unsigned int :1; 227 | unsigned int :1; 228 | unsigned int :1; 229 | unsigned int :3; 230 | unsigned int :2; 231 | unsigned int :1; 232 | unsigned int :1; 233 | unsigned int :3; 234 | unsigned int :1; 235 | unsigned int :2; 236 | unsigned int :2; 237 | unsigned int :1; 238 | unsigned int :1; 239 | unsigned int :1; 240 | unsigned int :1; 241 | unsigned int :1; 242 | unsigned int :1; 243 | unsigned int :1; 244 | unsigned int :1; 245 | unsigned char _field10; 246 | struct TrackInfo *_field11; 247 | struct VersionAndExpiry _field12; 248 | int _field13; 249 | struct TrackRelation *_field14; 250 | struct TrackInfo *_field15; 251 | struct TrackInfo *_field16; 252 | struct ArtistInfo **_field17; 253 | struct FileId _field18[2]; 254 | union $_2264 _field19; 255 | union $_2266 _field20; 256 | struct OsAlias *_field21; 257 | }; 258 | 259 | struct TrackRelation; 260 | 261 | struct UserPasswordVerifier { 262 | void **_field1; 263 | }; 264 | 265 | struct VersionAndExpiry { 266 | unsigned int _field1; 267 | unsigned int _field2; 268 | }; 269 | 270 | struct WebBrowserNotifications; 271 | 272 | struct WebBrowserOSX { 273 | void **_field1; 274 | id _field2; 275 | id _field3; 276 | id _field4; 277 | struct WebBrowserNotifications *_field5; 278 | }; 279 | 280 | struct WindowControllerOSX { 281 | void **_field1; 282 | void **_field2; 283 | struct GuiManager *_field3; 284 | struct WindowSettings _field4; 285 | struct IGenericView *_field5; 286 | int _field6; 287 | struct String _field7; 288 | struct String _field8; 289 | _Bool _field9; 290 | struct String _field10; 291 | struct SkinManager *_field11; 292 | struct String _field12; 293 | unsigned int _field13; 294 | void **_field14; 295 | id _field15; 296 | _Bool _field16; 297 | id _field17; 298 | id _field18; 299 | struct AudioDeviceListenerOSX *_field19; 300 | }; 301 | 302 | struct WindowSettings { 303 | struct Rect _field1; 304 | int _field2; 305 | }; 306 | 307 | struct _NSPoint { 308 | float x; 309 | float y; 310 | }; 311 | 312 | struct _NSRect { 313 | struct _NSPoint origin; 314 | struct _NSSize size; 315 | }; 316 | 317 | struct _NSSize { 318 | float width; 319 | float height; 320 | }; 321 | 322 | struct _NSZone; 323 | 324 | struct __SCNetworkReachability; 325 | 326 | struct in_addr { 327 | unsigned int _field1; 328 | }; 329 | 330 | struct sockaddr_in { 331 | unsigned char _field1; 332 | unsigned char _field2; 333 | unsigned short _field3; 334 | struct in_addr _field4; 335 | char _field5[8]; 336 | }; 337 | 338 | typedef struct { 339 | int *_field1; 340 | } CDAnonymousStruct1; 341 | 342 | union $_2215 { 343 | char *t; 344 | struct StrPtrStruct *_d; 345 | }; 346 | 347 | union $_2264 { 348 | struct TrackId _field1; 349 | struct $_2265 _field2; 350 | }; 351 | 352 | union $_2266 { 353 | struct PurchaseLinks *_field1; 354 | char *_field2; 355 | struct AdQueueEntry *_field3; 356 | }; 357 | 358 | -------------------------------------------------------------------------------- /LastifyLastfmClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // LastifyLastfmClient.m 3 | // Lastify 4 | // 5 | // Created by George on 16/01/2009. 6 | // Copyright 2008 George Brocklehurst. Some rights reserved (see accompanying LICENSE file for details). 7 | // 8 | 9 | #import "LastifyLastfmClient.h" 10 | #import "NSString+Lastify.h" 11 | #import "LastifyController.h" 12 | 13 | 14 | @interface LastifyLastfmClient (Private) 15 | - (NSString*)requestAuthToken; 16 | - (NSString*)callMethod:(NSString*)methodName withParams:(NSDictionary*)params usingPost:(BOOL)post error:(NSError**)error; 17 | - (void)loadSessionKey; 18 | - (void)storeSessionKey; 19 | @end 20 | 21 | @implementation LastifyLastfmClient 22 | 23 | @synthesize 24 | APIKey, 25 | APISecret, 26 | authToken, 27 | sessionKey, 28 | waitingForUserAuth, 29 | sessionReady, 30 | username; 31 | 32 | - (id)initWithAPIKey:(NSString*)newAPIKey APISecret:(NSString*)newAPISecret 33 | { 34 | self = [super init]; 35 | if(!self) 36 | return nil; 37 | 38 | self.APIKey = newAPIKey; 39 | self.APISecret = newAPISecret; 40 | 41 | sessionReady = FALSE; 42 | waitingForUserAuth = FALSE; 43 | 44 | return self; 45 | } 46 | 47 | - (void)dealloc 48 | { 49 | [APIKey release], APIKey = nil; 50 | [APISecret release], APISecret = nil; 51 | [authToken release], authToken = nil; 52 | [sessionKey release], sessionKey = nil; 53 | [username release], username = nil; 54 | [super dealloc]; 55 | } 56 | 57 | - (void)loadSessionKey 58 | { 59 | 60 | SecKeychainSearchRef search; 61 | SecKeychainItemRef item; 62 | SecKeychainAttributeList list; 63 | SecKeychainAttribute attributes[3]; 64 | OSErr result; 65 | 66 | attributes[0].tag = kSecAccountItemAttr; 67 | attributes[0].data = (void*)[self.APIKey UTF8String]; 68 | attributes[0].length = [self.APIKey length]; 69 | 70 | NSString *itemDescription = @"Lastify Last.fm session information"; 71 | attributes[1].tag = kSecDescriptionItemAttr; 72 | attributes[1].data = (void*)[itemDescription UTF8String]; 73 | attributes[1].length = [itemDescription length]; 74 | 75 | NSString *itemLabel = @"Lastify Last.fm session information"; 76 | attributes[2].tag = kSecLabelItemAttr; 77 | attributes[2].data = (void*)[itemLabel UTF8String]; 78 | attributes[2].length = [itemLabel length]; 79 | 80 | list.count = 3; 81 | list.attr = (SecKeychainAttribute*)&attributes; 82 | 83 | result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search); 84 | 85 | if(result != noErr) 86 | return; 87 | 88 | if(SecKeychainSearchCopyNext(search, &item) == noErr) 89 | { 90 | UInt32 length; 91 | char *password = NULL; 92 | OSStatus status; 93 | 94 | status = SecKeychainItemCopyContent(item, NULL, NULL, &length, (void **)&password); 95 | 96 | if(status == noErr) 97 | { 98 | if (password != NULL) 99 | { 100 | char passwordBuffer[length+1]; 101 | strncpy(passwordBuffer, password, length); 102 | passwordBuffer[length] = '\0'; 103 | 104 | NSString *rawLoadedPassword = [NSString stringWithUTF8String:passwordBuffer]; 105 | NSArray *parts = [rawLoadedPassword componentsSeparatedByString:@" / "]; 106 | 107 | if(parts && [parts count] == 2) 108 | { 109 | self.sessionKey = [parts objectAtIndex:0]; 110 | self.username = [parts objectAtIndex:1]; 111 | self.waitingForUserAuth = FALSE; 112 | self.sessionReady = TRUE; 113 | } 114 | } 115 | 116 | SecKeychainItemFreeContent(NULL, password); 117 | } 118 | 119 | CFRelease(item); 120 | CFRelease (search); 121 | } 122 | } 123 | 124 | - (void)storeSessionKey 125 | { 126 | // Create attributes array 127 | SecKeychainAttribute attributes[3]; 128 | 129 | // Set the account name (uses the application's consumer key) 130 | attributes[0].tag = kSecAccountItemAttr; 131 | attributes[0].data = (void*)[self.APIKey UTF8String]; 132 | attributes[0].length = [self.APIKey length]; 133 | 134 | // Set the description 135 | NSString *itemDescription = @"Lastify Last.fm session information"; 136 | attributes[1].tag = kSecDescriptionItemAttr; 137 | attributes[1].data = (void*)[itemDescription UTF8String]; 138 | attributes[1].length = [itemDescription length]; 139 | 140 | // Label the item 141 | NSString *itemLabel = @"Lastify Last.fm session information"; 142 | attributes[2].tag = kSecLabelItemAttr; 143 | attributes[2].data = (void*)[itemLabel UTF8String]; 144 | attributes[2].length = [itemLabel length]; 145 | 146 | // Create list from attributes array 147 | SecKeychainAttributeList list; 148 | list.count = 3; 149 | list.attr = attributes; 150 | 151 | // Store the password 152 | NSString *password = [NSString stringWithFormat:@"%@ / %@", self.sessionKey, self.username]; 153 | SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, [password length], [password UTF8String], NULL,NULL,NULL); 154 | } 155 | 156 | - (void)authenticateQuietly 157 | { 158 | [self loadSessionKey]; 159 | [[LastifyController sharedInstance] loadUserInterface]; 160 | } 161 | 162 | - (void)authenticate 163 | { 164 | // Attempt to authenticate without user interaction 165 | [self authenticateQuietly]; 166 | if(self.sessionKey) 167 | return; 168 | 169 | // We need the user to authenticate 170 | NSString *newAuthToken = [self requestAuthToken]; 171 | 172 | if(!newAuthToken) 173 | { 174 | //TODO: Present an error to the user 175 | return; 176 | } 177 | 178 | self.authToken = newAuthToken; 179 | 180 | // Get the user to authorise the token 181 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.last.fm/api/auth/?api_key=%@&token=%@", self.APIKey, newAuthToken]]]; 182 | self.waitingForUserAuth = TRUE; 183 | 184 | // The authentication will resume with startNewSession when the user has logged in ... 185 | } 186 | 187 | - (void)startNewSession 188 | { 189 | NSError *err = nil; 190 | NSString *response = [self callMethod:@"auth.getSession" withParams:[NSDictionary dictionaryWithObjectsAndKeys:self.authToken, @"token", nil] usingPost:FALSE error:&err]; 191 | 192 | if(err || !response) 193 | { 194 | switch([err code]) 195 | { 196 | case 15: // This token has expired 197 | case 4: // Invalid authentication token supplied 198 | case 14: // This token has not been authorized 199 | case 2: // Invalid service -This service does not exist 200 | case 3: // Invalid Method - No method with that name in this package 201 | case 5: // Invalid format - This service doesn't exist in that format 202 | case 6: // Invalid parameters - Your request is missing a required parameter 203 | case 7: // Invalid resource specified 204 | case 9: // Invalid session key - Please re-authenticate 205 | case 10: // Invalid API key - You must be granted a valid key by last.fm 206 | case 11: // Service Offline - This service is temporarily offline. Try again later. 207 | case 12: // Subscribers Only - This service is only available to paid last.fm subscribers 208 | default: 209 | break; 210 | } 211 | 212 | return; 213 | } 214 | 215 | // Extract the username and the session key 216 | NSString *newSessionKey = nil; 217 | NSString *newUsername = nil; 218 | NSScanner *scanner = [NSScanner scannerWithString:response]; 219 | 220 | [scanner scanUpToString:@"" intoString:NULL]; 221 | [scanner scanString:@"" intoString:NULL]; 222 | [scanner scanUpToString:@"" intoString:&newUsername]; 223 | 224 | [scanner scanUpToString:@"" intoString:NULL]; 225 | [scanner scanString:@"" intoString:NULL]; 226 | [scanner scanUpToString:@"" intoString:&newSessionKey]; 227 | 228 | if(!newSessionKey) 229 | { 230 | //TODO: Handle this (must be an unexpected error if not caught sooner) 231 | return; 232 | } 233 | 234 | self.sessionKey = newSessionKey; 235 | self.username = newUsername; 236 | self.waitingForUserAuth = FALSE; 237 | self.sessionReady = TRUE; 238 | 239 | [self storeSessionKey]; 240 | } 241 | 242 | - (NSString*)requestAuthToken 243 | { 244 | // Request a new auth token from the Last.fm server 245 | NSError *downloadError = nil; 246 | NSString *response = [NSString stringWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://ws.audioscrobbler.com/2.0/?method=auth.gettoken&api_key=%@", self.APIKey]] encoding:NSUTF8StringEncoding error:&downloadError]; 247 | 248 | if(downloadError || !response) 249 | { 250 | NSLog(@"LASTIFY failed to get Auth Token: %@", downloadError); 251 | return nil; 252 | } 253 | 254 | NSString *newAuthToken = nil; 255 | NSScanner *scanner = [NSScanner scannerWithString:response]; 256 | [scanner scanUpToString:@"" intoString:NULL]; 257 | [scanner scanString:@"" intoString:NULL]; 258 | [scanner scanUpToString:@"" intoString:&newAuthToken]; 259 | 260 | return [[[NSString alloc] initWithString:newAuthToken] autorelease]; 261 | } 262 | 263 | - (NSString*)callMethod:(NSString*)methodName withParams:(NSDictionary*)params usingPost:(BOOL)post error:(NSError**)error 264 | { 265 | NSMutableString *urlString = [NSMutableString stringWithString:@"http://ws.audioscrobbler.com/2.0/"]; 266 | 267 | // Add the standard parameters (methodName, APIKey) 268 | NSMutableDictionary *mutableParams = [params mutableCopy]; 269 | [mutableParams setObject:self.APIKey forKey:@"api_key"]; 270 | [mutableParams setObject:methodName forKey:@"method"]; 271 | 272 | // If we have a session key, add it to the parameters 273 | if(sessionKey) 274 | [mutableParams setObject:sessionKey forKey:@"sk"]; 275 | 276 | // Sort the params alphabetically 277 | NSArray *sortedKeys = [[mutableParams allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 278 | 279 | // Add the parameters to the signature base and the URL string 280 | NSMutableString *signatureBase = [NSMutableString stringWithCapacity:50]; 281 | NSMutableString *queryString = [NSMutableString stringWithCapacity:50]; 282 | 283 | NSEnumerator *paramEnumerator = [sortedKeys objectEnumerator]; 284 | NSString *key, *value, *escapedValue; 285 | while(key = [paramEnumerator nextObject]) 286 | { 287 | value = [mutableParams objectForKey:key]; 288 | [signatureBase appendFormat:@"%@%@", key, value]; 289 | 290 | escapedValue = (NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)value, NULL, (CFStringRef)@"&=:/,", kCFStringEncodingUTF8); 291 | [queryString appendFormat:@"%@=%@&", key, escapedValue]; 292 | CFRelease(escapedValue), escapedValue = nil; 293 | } 294 | 295 | // Generate the signature 296 | [signatureBase appendString:self.APISecret]; 297 | [queryString appendFormat:@"api_sig=%@", [signatureBase MD5Hash]]; 298 | 299 | // Tidy up 300 | [mutableParams release], mutableParams = nil; 301 | 302 | // Build the request 303 | NSURLRequest *urlReq; 304 | if(post) 305 | { 306 | NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 307 | [postRequest setHTTPMethod:@"POST"]; 308 | [postRequest setHTTPBody:[queryString dataUsingEncoding:NSUTF8StringEncoding]]; 309 | urlReq = postRequest; 310 | } 311 | else 312 | { 313 | [urlString appendFormat:@"?%@", queryString]; 314 | urlReq = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 315 | } 316 | 317 | // Call the Last.fm API 318 | NSHTTPURLResponse *downloadResponse; 319 | NSError *downloadError = nil; 320 | NSData *downloadData = nil; 321 | downloadData = [NSURLConnection sendSynchronousRequest:urlReq returningResponse:&downloadResponse error:&downloadError]; 322 | 323 | // Check for errors in the response 324 | if(downloadError) 325 | { 326 | NSLog(@"LASTIFY download error: %@", downloadError); 327 | *error = downloadError; 328 | return nil; 329 | } 330 | 331 | if([downloadResponse statusCode] != 200) 332 | { 333 | NSLog(@"LASTIFY bad HTTP response: %d", [downloadResponse statusCode]); 334 | 335 | if(downloadData) 336 | { 337 | NSString *errorResponse = [[NSString alloc] initWithData:downloadData encoding:NSUTF8StringEncoding]; 338 | 339 | NSInteger errNo; 340 | NSString *errMsg = nil; 341 | NSScanner *errScanner = [NSScanner scannerWithString:errorResponse]; 342 | [errScanner scanUpToString:@"" intoString:NULL]; 346 | [errScanner scanString:@">" intoString:NULL]; 347 | [errScanner scanUpToString:@"" intoString:&errMsg]; 348 | 349 | if(errMsg && errNo) 350 | { 351 | *error = [NSError 352 | errorWithDomain:@"com.georgebrock.lastify" 353 | code:errNo 354 | userInfo:[NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]]; 355 | } 356 | 357 | [errorResponse release], errorResponse = nil; 358 | } 359 | 360 | return nil; 361 | } 362 | 363 | // Extract the string from the response 364 | NSString *response = [[NSString alloc] initWithData:downloadData encoding:NSUTF8StringEncoding]; 365 | NSLog(@"LASTIFY response: %@", response); 366 | 367 | return [response autorelease]; 368 | } 369 | 370 | - (BOOL)loveTrack:(NSString*)trackName byArtist:(NSString*)artistName 371 | { 372 | if(!self.sessionKey) 373 | return FALSE; 374 | 375 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 376 | trackName, @"track", 377 | artistName, @"artist", 378 | nil]; 379 | 380 | NSError *loveError = nil; 381 | [self callMethod:@"track.love" withParams:callParams usingPost:TRUE error:&loveError]; 382 | 383 | return !loveError; 384 | } 385 | 386 | - (BOOL)banTrack:(NSString*)trackName byArtist:(NSString*)artistName 387 | { 388 | if(!self.sessionKey) 389 | return FALSE; 390 | 391 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 392 | trackName, @"track", 393 | artistName, @"artist", 394 | nil]; 395 | 396 | NSError *banError = nil; 397 | [self callMethod:@"track.ban" withParams:callParams usingPost:TRUE error:&banError]; 398 | 399 | return !banError; 400 | } 401 | 402 | - (NSArray*)getTagsForTrack:(NSString*)trackName byArtist:(NSString*)artistName 403 | { 404 | if(!self.sessionKey) 405 | return nil; 406 | 407 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 408 | trackName, @"track", 409 | artistName, @"artist", 410 | nil]; 411 | 412 | NSString *response = [self callMethod:@"track.gettags" withParams:callParams usingPost:FALSE error:NULL]; 413 | if(!response) 414 | return nil; 415 | 416 | NSMutableArray *tags = [NSMutableArray arrayWithCapacity:1]; 417 | 418 | NSString *newTag = nil; 419 | NSScanner *scanner = [NSScanner scannerWithString:response]; 420 | while([scanner scanUpToString:@"" intoString:NULL]) 421 | { 422 | [scanner scanString:@"" intoString:NULL]; 423 | if([scanner scanUpToString:@"" intoString:&newTag]) 424 | [tags addObject:newTag]; 425 | } 426 | 427 | return (NSArray*)tags; 428 | } 429 | 430 | - (BOOL)addTags:(NSArray*)tags toTrack:(NSString*)trackName byArtist:(NSString*)artistName 431 | { 432 | if(!self.sessionKey) 433 | return FALSE; 434 | 435 | if([tags count] > 10) 436 | { 437 | NSArray *remainder = [tags subarrayWithRange:NSMakeRange(10, [tags count]-10)]; 438 | [self addTags:remainder toTrack:trackName byArtist:artistName]; 439 | 440 | tags = [tags subarrayWithRange:NSMakeRange(0, 10)]; 441 | } 442 | 443 | NSString *tagString = [tags componentsJoinedByString:@","]; 444 | 445 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 446 | trackName, @"track", 447 | artistName, @"artist", 448 | tagString, @"tags", 449 | nil]; 450 | 451 | NSError *tagError = nil; 452 | [self callMethod:@"track.addtags" withParams:callParams usingPost:TRUE error:&tagError]; 453 | 454 | return !tagError; 455 | } 456 | 457 | - (BOOL)removeTags:(NSArray*)tags fromTrack:(NSString*)trackName byArtist:(NSString*)artistName 458 | { 459 | if(!self.sessionKey) 460 | return FALSE; 461 | 462 | NSString *tag; 463 | NSDictionary *callParams; 464 | NSEnumerator *tagEnum = [tags objectEnumerator]; 465 | BOOL result = TRUE; 466 | while(tag = [tagEnum nextObject]) 467 | { 468 | callParams = [NSDictionary dictionaryWithObjectsAndKeys: 469 | trackName, @"track", 470 | artistName, @"artist", 471 | tag, @"tag", 472 | nil]; 473 | 474 | NSError *tagError = nil; 475 | [self callMethod:@"track.removetag" withParams:callParams usingPost:TRUE error:&tagError]; 476 | result = result && !tagError; 477 | } 478 | 479 | return result; 480 | } 481 | 482 | - (NSArray*)getPlaylists 483 | { 484 | if(!self.sessionKey) 485 | return nil; 486 | 487 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 488 | self.username, @"user", 489 | nil]; 490 | 491 | NSString *response = [self callMethod:@"user.getPlaylists" withParams:callParams usingPost:FALSE error:NULL]; 492 | if(!response) 493 | return nil; 494 | 495 | NSMutableArray *playlists = [NSMutableArray arrayWithCapacity:1]; 496 | 497 | NSScanner *scanner = [NSScanner scannerWithString:response]; 498 | while([scanner scanUpToString:@"" intoString:NULL]) 499 | { 500 | NSString *playlistID = nil; 501 | [scanner scanUpToString:@"" intoString:NULL]; 502 | [scanner scanString:@"" intoString:NULL]; 503 | BOOL foundID = [scanner scanUpToString:@"" intoString:&playlistID]; 504 | 505 | NSString *playlistTitle = nil; 506 | [scanner scanUpToString:@"" intoString:NULL]; 507 | [scanner scanString:@"<title>" intoString:NULL]; 508 | BOOL foundTitle = [scanner scanUpToString:@"" intoString:&playlistTitle]; 509 | 510 | [scanner scanUpToString:@"" intoString:NULL]; 511 | 512 | if(foundID && foundTitle) 513 | [playlists addObject:[NSDictionary dictionaryWithObjectsAndKeys: 514 | playlistID, @"id", 515 | playlistTitle, @"title", 516 | nil]]; 517 | } 518 | 519 | return (NSArray*)playlists; 520 | } 521 | 522 | - (BOOL)addTrack:(NSString*)trackName byArtist:(NSString*)artistName toPlaylist:(NSString*)playlistID 523 | { 524 | if(!self.sessionKey) 525 | return FALSE; 526 | 527 | NSDictionary *callParams = [NSDictionary dictionaryWithObjectsAndKeys: 528 | trackName, @"track", 529 | artistName, @"artist", 530 | playlistID, @"playlistID", 531 | nil]; 532 | 533 | NSError *listError = nil; 534 | [self callMethod:@"playlist.addTrack" withParams:callParams usingPost:TRUE error:&listError]; 535 | 536 | return !listError; 537 | } 538 | 539 | @end 540 | -------------------------------------------------------------------------------- /Lastify.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 44; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; 11 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; 12 | C6158DB20F56D64C006CE651 /* icon_loading.gif in Resources */ = {isa = PBXBuildFile; fileRef = C6158DB10F56D64C006CE651 /* icon_loading.gif */; }; 13 | C6158DB60F56D726006CE651 /* icon_tick.png in Resources */ = {isa = PBXBuildFile; fileRef = C6158DB40F56D726006CE651 /* icon_tick.png */; }; 14 | C6158DB70F56D726006CE651 /* icon_error.png in Resources */ = {isa = PBXBuildFile; fileRef = C6158DB50F56D726006CE651 /* icon_error.png */; }; 15 | C68347C20F70504200132B8E /* icon_list.png in Resources */ = {isa = PBXBuildFile; fileRef = C68347C10F70504200132B8E /* icon_list.png */; }; 16 | C6BE8FF10F431AB5004BBEB7 /* icon_tag.png in Resources */ = {isa = PBXBuildFile; fileRef = C6BE8FEE0F431AB5004BBEB7 /* icon_tag.png */; }; 17 | C6BE8FF20F431AB5004BBEB7 /* icon_ban.png in Resources */ = {isa = PBXBuildFile; fileRef = C6BE8FEF0F431AB5004BBEB7 /* icon_ban.png */; }; 18 | C6BE8FF30F431AB5004BBEB7 /* icon_love.png in Resources */ = {isa = PBXBuildFile; fileRef = C6BE8FF00F431AB5004BBEB7 /* icon_love.png */; }; 19 | C6BE900D0F4321AE004BBEB7 /* LastifyDrawerView.m in Sources */ = {isa = PBXBuildFile; fileRef = C6BE900C0F4321AE004BBEB7 /* LastifyDrawerView.m */; }; 20 | C6BE903B0F4327E4004BBEB7 /* NSButton+Lastify.m in Sources */ = {isa = PBXBuildFile; fileRef = C6BE903A0F4327E4004BBEB7 /* NSButton+Lastify.m */; }; 21 | C6BE906B0F432D3A004BBEB7 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = C6BE906A0F432D3A004BBEB7 /* icon_key.png */; }; 22 | C6D68B570F212558000CB3C6 /* LastifyController.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D68B560F212558000CB3C6 /* LastifyController.m */; }; 23 | C6D68B5E0F212820000CB3C6 /* LastifyInterface.xib in Resources */ = {isa = PBXBuildFile; fileRef = C6D68B5D0F212820000CB3C6 /* LastifyInterface.xib */; }; 24 | C6D68BFA0F21418D000CB3C6 /* lastfm_black.png in Resources */ = {isa = PBXBuildFile; fileRef = C6D68BF90F21418D000CB3C6 /* lastfm_black.png */; }; 25 | C6D68C060F214DAA000CB3C6 /* LastifyLastfmClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D68C050F214DAA000CB3C6 /* LastifyLastfmClient.m */; }; 26 | C6D68C9A0F21EF55000CB3C6 /* NSString+Lastify.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D68C990F21EF55000CB3C6 /* NSString+Lastify.m */; }; 27 | C6D68CF90F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D68CF80F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 32 | 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 34 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 35 | 32DBCF630370AF2F00C91783 /* Lastify_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lastify_Prefix.pch; sourceTree = ""; }; 36 | 8D5B49B6048680CD000E48DA /* Lastify.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Lastify.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | C6158DB10F56D64C006CE651 /* icon_loading.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = icon_loading.gif; sourceTree = ""; }; 39 | C6158DB40F56D726006CE651 /* icon_tick.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_tick.png; sourceTree = ""; }; 40 | C6158DB50F56D726006CE651 /* icon_error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_error.png; sourceTree = ""; }; 41 | C68347C10F70504200132B8E /* icon_list.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_list.png; sourceTree = ""; }; 42 | C6BE8FEE0F431AB5004BBEB7 /* icon_tag.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_tag.png; sourceTree = ""; }; 43 | C6BE8FEF0F431AB5004BBEB7 /* icon_ban.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_ban.png; sourceTree = ""; }; 44 | C6BE8FF00F431AB5004BBEB7 /* icon_love.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_love.png; sourceTree = ""; }; 45 | C6BE900B0F4321AE004BBEB7 /* LastifyDrawerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LastifyDrawerView.h; sourceTree = ""; }; 46 | C6BE900C0F4321AE004BBEB7 /* LastifyDrawerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LastifyDrawerView.m; sourceTree = ""; }; 47 | C6BE90390F4327E4004BBEB7 /* NSButton+Lastify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSButton+Lastify.h"; sourceTree = ""; }; 48 | C6BE903A0F4327E4004BBEB7 /* NSButton+Lastify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSButton+Lastify.m"; sourceTree = ""; }; 49 | C6BE906A0F432D3A004BBEB7 /* icon_key.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_key.png; sourceTree = ""; }; 50 | C6D68B550F212558000CB3C6 /* LastifyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LastifyController.h; sourceTree = ""; }; 51 | C6D68B560F212558000CB3C6 /* LastifyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LastifyController.m; sourceTree = ""; }; 52 | C6D68B5D0F212820000CB3C6 /* LastifyInterface.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LastifyInterface.xib; sourceTree = ""; }; 53 | C6D68B9A0F213660000CB3C6 /* SPController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPController.h; path = "class-dump-headers/SPController.h"; sourceTree = ""; }; 54 | C6D68BF90F21418D000CB3C6 /* lastfm_black.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lastfm_black.png; sourceTree = ""; }; 55 | C6D68C040F214DAA000CB3C6 /* LastifyLastfmClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LastifyLastfmClient.h; sourceTree = ""; }; 56 | C6D68C050F214DAA000CB3C6 /* LastifyLastfmClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LastifyLastfmClient.m; sourceTree = ""; }; 57 | C6D68C980F21EF55000CB3C6 /* NSString+Lastify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Lastify.h"; sourceTree = ""; }; 58 | C6D68C990F21EF55000CB3C6 /* NSString+Lastify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Lastify.m"; sourceTree = ""; }; 59 | C6D68CF60F21FE13000CB3C6 /* SPGrowlDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPGrowlDelegate.h; path = "class-dump-headers/SPGrowlDelegate.h"; sourceTree = ""; }; 60 | C6D68CF70F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SPGrowlDelegate+Lastify.h"; sourceTree = ""; }; 61 | C6D68CF80F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SPGrowlDelegate+Lastify.m"; sourceTree = ""; }; 62 | C6D68D000F21FFBB000CB3C6 /* SPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTypes.h; sourceTree = ""; }; 63 | D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 8D5B49B3048680CD000E48DA /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 089C166AFE841209C02AAC07 /* Lastify */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 08FB77AFFE84173DC02AAC07 /* Classes */, 82 | 32C88E010371C26100C91783 /* Other Sources */, 83 | 089C167CFE841241C02AAC07 /* Resources */, 84 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, 85 | 19C28FB8FE9D52D311CA2CBB /* Products */, 86 | ); 87 | name = Lastify; 88 | sourceTree = ""; 89 | }; 90 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, 94 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, 95 | ); 96 | name = "Frameworks and Libraries"; 97 | sourceTree = ""; 98 | }; 99 | 089C167CFE841241C02AAC07 /* Resources */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | C6BE8FEE0F431AB5004BBEB7 /* icon_tag.png */, 103 | C6BE8FEF0F431AB5004BBEB7 /* icon_ban.png */, 104 | C6BE8FF00F431AB5004BBEB7 /* icon_love.png */, 105 | C6BE906A0F432D3A004BBEB7 /* icon_key.png */, 106 | C6158DB10F56D64C006CE651 /* icon_loading.gif */, 107 | C68347C10F70504200132B8E /* icon_list.png */, 108 | C6D68BF90F21418D000CB3C6 /* lastfm_black.png */, 109 | C6158DB40F56D726006CE651 /* icon_tick.png */, 110 | C6158DB50F56D726006CE651 /* icon_error.png */, 111 | 8D5B49B7048680CD000E48DA /* Info.plist */, 112 | 089C167DFE841241C02AAC07 /* InfoPlist.strings */, 113 | C6D68B5D0F212820000CB3C6 /* LastifyInterface.xib */, 114 | ); 115 | name = Resources; 116 | sourceTree = ""; 117 | }; 118 | 08FB77AFFE84173DC02AAC07 /* Classes */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | C6D68C980F21EF55000CB3C6 /* NSString+Lastify.h */, 122 | C6D68C990F21EF55000CB3C6 /* NSString+Lastify.m */, 123 | C6D68D000F21FFBB000CB3C6 /* SPTypes.h */, 124 | C6D68CF60F21FE13000CB3C6 /* SPGrowlDelegate.h */, 125 | C6D68CF70F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.h */, 126 | C6D68CF80F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.m */, 127 | C6D68B9A0F213660000CB3C6 /* SPController.h */, 128 | C6BE90390F4327E4004BBEB7 /* NSButton+Lastify.h */, 129 | C6BE903A0F4327E4004BBEB7 /* NSButton+Lastify.m */, 130 | C6D68B550F212558000CB3C6 /* LastifyController.h */, 131 | C6D68B560F212558000CB3C6 /* LastifyController.m */, 132 | C6D68C040F214DAA000CB3C6 /* LastifyLastfmClient.h */, 133 | C6D68C050F214DAA000CB3C6 /* LastifyLastfmClient.m */, 134 | C6BE900B0F4321AE004BBEB7 /* LastifyDrawerView.h */, 135 | C6BE900C0F4321AE004BBEB7 /* LastifyDrawerView.m */, 136 | ); 137 | name = Classes; 138 | sourceTree = ""; 139 | }; 140 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, 144 | ); 145 | name = "Linked Frameworks"; 146 | sourceTree = ""; 147 | }; 148 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 089C167FFE841241C02AAC07 /* AppKit.framework */, 152 | D2F7E65807B2D6F200F64583 /* CoreData.framework */, 153 | 089C1672FE841209C02AAC07 /* Foundation.framework */, 154 | ); 155 | name = "Other Frameworks"; 156 | sourceTree = ""; 157 | }; 158 | 19C28FB8FE9D52D311CA2CBB /* Products */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 8D5B49B6048680CD000E48DA /* Lastify.bundle */, 162 | ); 163 | name = Products; 164 | sourceTree = ""; 165 | }; 166 | 32C88E010371C26100C91783 /* Other Sources */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 32DBCF630370AF2F00C91783 /* Lastify_Prefix.pch */, 170 | ); 171 | name = "Other Sources"; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 8D5B49AC048680CD000E48DA /* Lastify */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "Lastify" */; 180 | buildPhases = ( 181 | 8D5B49AF048680CD000E48DA /* Resources */, 182 | 8D5B49B1048680CD000E48DA /* Sources */, 183 | 8D5B49B3048680CD000E48DA /* Frameworks */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = Lastify; 190 | productInstallPath = "$(HOME)/Library/Bundles"; 191 | productName = Lastify; 192 | productReference = 8D5B49B6048680CD000E48DA /* Lastify.bundle */; 193 | productType = "com.apple.product-type.bundle"; 194 | }; 195 | /* End PBXNativeTarget section */ 196 | 197 | /* Begin PBXProject section */ 198 | 089C1669FE841209C02AAC07 /* Project object */ = { 199 | isa = PBXProject; 200 | buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Lastify" */; 201 | compatibilityVersion = "Xcode 3.0"; 202 | developmentRegion = English; 203 | hasScannedForEncodings = 1; 204 | knownRegions = ( 205 | English, 206 | Japanese, 207 | French, 208 | German, 209 | ); 210 | mainGroup = 089C166AFE841209C02AAC07 /* Lastify */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | 8D5B49AC048680CD000E48DA /* Lastify */, 215 | ); 216 | }; 217 | /* End PBXProject section */ 218 | 219 | /* Begin PBXResourcesBuildPhase section */ 220 | 8D5B49AF048680CD000E48DA /* Resources */ = { 221 | isa = PBXResourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */, 225 | C6D68B5E0F212820000CB3C6 /* LastifyInterface.xib in Resources */, 226 | C6D68BFA0F21418D000CB3C6 /* lastfm_black.png in Resources */, 227 | C6BE8FF10F431AB5004BBEB7 /* icon_tag.png in Resources */, 228 | C6BE8FF20F431AB5004BBEB7 /* icon_ban.png in Resources */, 229 | C6BE8FF30F431AB5004BBEB7 /* icon_love.png in Resources */, 230 | C6BE906B0F432D3A004BBEB7 /* icon_key.png in Resources */, 231 | C6158DB20F56D64C006CE651 /* icon_loading.gif in Resources */, 232 | C6158DB60F56D726006CE651 /* icon_tick.png in Resources */, 233 | C6158DB70F56D726006CE651 /* icon_error.png in Resources */, 234 | C68347C20F70504200132B8E /* icon_list.png in Resources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXResourcesBuildPhase section */ 239 | 240 | /* Begin PBXSourcesBuildPhase section */ 241 | 8D5B49B1048680CD000E48DA /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | C6D68B570F212558000CB3C6 /* LastifyController.m in Sources */, 246 | C6D68C060F214DAA000CB3C6 /* LastifyLastfmClient.m in Sources */, 247 | C6D68C9A0F21EF55000CB3C6 /* NSString+Lastify.m in Sources */, 248 | C6D68CF90F21FE2C000CB3C6 /* SPGrowlDelegate+Lastify.m in Sources */, 249 | C6BE900D0F4321AE004BBEB7 /* LastifyDrawerView.m in Sources */, 250 | C6BE903B0F4327E4004BBEB7 /* NSButton+Lastify.m in Sources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | /* End PBXSourcesBuildPhase section */ 255 | 256 | /* Begin PBXVariantGroup section */ 257 | 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = { 258 | isa = PBXVariantGroup; 259 | children = ( 260 | 089C167EFE841241C02AAC07 /* English */, 261 | ); 262 | name = InfoPlist.strings; 263 | sourceTree = ""; 264 | }; 265 | /* End PBXVariantGroup section */ 266 | 267 | /* Begin XCBuildConfiguration section */ 268 | 1DEB913B08733D840010E9CD /* Debug */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | COPY_PHASE_STRIP = NO; 272 | GCC_DYNAMIC_NO_PIC = NO; 273 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 274 | GCC_MODEL_TUNING = G5; 275 | GCC_OPTIMIZATION_LEVEL = 0; 276 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 277 | GCC_PREFIX_HEADER = Lastify_Prefix.pch; 278 | INFOPLIST_FILE = Info.plist; 279 | INSTALL_PATH = "$(HOME)/Library/Bundles"; 280 | OTHER_LDFLAGS = ( 281 | "-undefined", 282 | dynamic_lookup, 283 | ); 284 | PRODUCT_NAME = Lastify; 285 | WRAPPER_EXTENSION = bundle; 286 | ZERO_LINK = YES; 287 | }; 288 | name = Debug; 289 | }; 290 | 1DEB913C08733D840010E9CD /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 294 | GCC_MODEL_TUNING = G5; 295 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 296 | GCC_PREFIX_HEADER = Lastify_Prefix.pch; 297 | INFOPLIST_FILE = Info.plist; 298 | INSTALL_PATH = "$(HOME)/Library/Bundles"; 299 | OTHER_LDFLAGS = ( 300 | "-undefined", 301 | dynamic_lookup, 302 | ); 303 | PRODUCT_NAME = Lastify; 304 | WRAPPER_EXTENSION = bundle; 305 | }; 306 | name = Release; 307 | }; 308 | 1DEB913F08733D840010E9CD /* Debug */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | PREBINDING = NO; 314 | SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; 315 | }; 316 | name = Debug; 317 | }; 318 | 1DEB914008733D840010E9CD /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ARCHS = ( 322 | ppc, 323 | i386, 324 | ); 325 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 326 | GCC_WARN_UNUSED_VARIABLE = YES; 327 | PREBINDING = NO; 328 | SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; 329 | }; 330 | name = Release; 331 | }; 332 | /* End XCBuildConfiguration section */ 333 | 334 | /* Begin XCConfigurationList section */ 335 | 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "Lastify" */ = { 336 | isa = XCConfigurationList; 337 | buildConfigurations = ( 338 | 1DEB913B08733D840010E9CD /* Debug */, 339 | 1DEB913C08733D840010E9CD /* Release */, 340 | ); 341 | defaultConfigurationIsVisible = 0; 342 | defaultConfigurationName = Release; 343 | }; 344 | 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Lastify" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | 1DEB913F08733D840010E9CD /* Debug */, 348 | 1DEB914008733D840010E9CD /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | /* End XCConfigurationList section */ 354 | }; 355 | rootObject = 089C1669FE841209C02AAC07 /* Project object */; 356 | } 357 | --------------------------------------------------------------------------------