├── README.md ├── Transistor.xcodeproj └── project.pbxproj ├── resources ├── English.lproj │ ├── InfoPlist.strings │ └── MainMenu.xib ├── Icon.icns ├── TRMainWindow.xib ├── Transistor-Info.plist ├── content_bg.png ├── next.png ├── next_over.png ├── pandora_logo.png ├── pause.png ├── pause_over.png ├── play.png └── play_over.png └── source ├── Classes ├── TRAppDelegate.h ├── TRAppDelegate.m ├── TRArtworkView.h ├── TRArtworkView.m ├── TRContentView.h ├── TRContentView.m ├── TRMainWindowController.h ├── TRMainWindowController.m ├── TRPianobarManager.h ├── TRPianobarManager.m └── TransistorHelper.m ├── Transistor_Prefix.pch └── main.m /README.md: -------------------------------------------------------------------------------- 1 | Transistor is a native mac app that is currently just a GUI for the [pianobar](https://github.com/PromyLOPh/pianobar) Pandora console client. I hate the Pandora flash web interface, and pianobar is a huge improvement, but I still prefer a native app. Transistor is a hacky way of achieving that, but works for now. Pianobar takes care of the all the hardwork of communicating with Pandora via a non-public API. When I have some more time, I'll most likely rewrite the pianobar Pandora API in Objective-C, or incorporate pianobar's C code directly into the app instead of launching a separate executable. 2 | 3 | 4 | ## Warning 5 | Warning, this is definitely buggy and not even an alpha yet, put together in a few hours. I haven't spent any time yet optimizing or looking for memory leaks. But I've been using it pretty much all day for the last week, so feel free to give it a go. You can get binary from the downloads. Open an issue for any bugs or feature requests. 6 | 7 | 8 | ## Installation 9 | First, you need to have pianobar installed. Easiest way is via homebrew, just 10 | 11 | brew install pianobar 12 | 13 | In this first version, it assumes the pianobar binary is at /usr/local/bin/pianobar, in the final version, it'll prompt for the path if it can't find it. Next, you need to have a pianobar config in ~/.config/pianobar/config that includes your pandora username, password, and an event_command entry for the TransistorHelper. Here's an example config: 14 | 15 | username = yourpandorausername 16 | password = yourpandorapassword 17 | event_command = /Applications/Transistor.app/Contents/Resources/TransistorHelper 18 | 19 | 20 | ## Architecture 21 | Transistor consists of two binaries, the Transistor app and a TransistorHelper app. The main app launches and manages the pianobar process, connecting pianobar's stdin and stdout to a Objective-C pipe. In addition to stdin and stdout, pianobar has an external events system that sends events to the TransistorHelper command line app which then sends a distributed notification to the main Transistor app. I could do it all by parsing stdout, but this is simpler for now. 22 | 23 | 24 | ## Todo 25 | - Prompt for username and password, store in keychain 26 | - Better UI for choosing/changing stations 27 | - Support for other Pandora commands (thumbs up, thumbs down, bookmark, etc) 28 | - Growl integration 29 | - Global keyboard shortcuts for play/pause and next song (maybe intercept keyboard shortcuts iTunes uses if we can prevent them from also reaching iTunes) 30 | 31 | 32 | ## Screenshot 33 | ![Transistor 0.2 Screenshot](http://cloud.github.com/downloads/zachwaugh/Transistor/transistor_screenshot.png) 34 | 35 | 36 | -------------------------------------------------------------------------------- /Transistor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 11 | 256AC3DA0F4B6AC300CF3369 /* TRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* TRAppDelegate.m */; }; 12 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 13 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 14 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 15 | 923B97C5130874150057761F /* TRContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 923B97C4130874150057761F /* TRContentView.m */; }; 16 | 923B97C7130874620057761F /* content_bg.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B97C6130874620057761F /* content_bg.png */; }; 17 | 923B97E1130875110057761F /* TRMainWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 923B97E0130875110057761F /* TRMainWindowController.m */; }; 18 | 923B97E31308751E0057761F /* TRMainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 923B97E21308751E0057761F /* TRMainWindow.xib */; }; 19 | 923B97EF130876F50057761F /* TRPianobarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 923B97EE130876F50057761F /* TRPianobarManager.m */; }; 20 | 923B985D130883E40057761F /* TransistorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 923B985B130883C90057761F /* TransistorHelper.m */; }; 21 | 923B9A0513088FF20057761F /* next.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A0213088FF20057761F /* next.png */; }; 22 | 923B9A0613088FF20057761F /* pause.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A0313088FF20057761F /* pause.png */; }; 23 | 923B9A0713088FF20057761F /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A0413088FF20057761F /* play.png */; }; 24 | 923B9A2C1308954C0057761F /* next_over.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A291308954C0057761F /* next_over.png */; }; 25 | 923B9A2D1308954C0057761F /* pause_over.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A2A1308954C0057761F /* pause_over.png */; }; 26 | 923B9A2E1308954C0057761F /* play_over.png in Resources */ = {isa = PBXBuildFile; fileRef = 923B9A2B1308954C0057761F /* play_over.png */; }; 27 | 923B9A38130896610057761F /* TRArtworkView.m in Sources */ = {isa = PBXBuildFile; fileRef = 923B9A37130896610057761F /* TRArtworkView.m */; }; 28 | 923B9B2A1308ADAA0057761F /* TransistorHelper in Resources */ = {isa = PBXBuildFile; fileRef = 923B9851130883A80057761F /* TransistorHelper */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 923B9B231308AD310057761F /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 923B9850130883A80057761F; 37 | remoteInfo = TransistorHelper; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 43 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 44 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 45 | 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 46 | 256AC3D80F4B6AC300CF3369 /* TRAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TRAppDelegate.h; sourceTree = ""; }; 47 | 256AC3D90F4B6AC300CF3369 /* TRAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TRAppDelegate.m; sourceTree = ""; }; 48 | 256AC3F00F4B6AF500CF3369 /* Transistor_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Transistor_Prefix.pch; path = source/Transistor_Prefix.pch; sourceTree = ""; }; 49 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = source/main.m; sourceTree = ""; }; 50 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 51 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 52 | 37A621F21342BD4600F852F5 /* pandora_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pandora_logo.png; sourceTree = ""; }; 53 | 37A621F41342BD6600F852F5 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Icon.icns; sourceTree = ""; }; 54 | 8D1107310486CEB800E47090 /* Transistor-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Transistor-Info.plist"; sourceTree = ""; }; 55 | 8D1107320486CEB800E47090 /* Transistor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transistor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 923B97C3130874150057761F /* TRContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TRContentView.h; sourceTree = ""; }; 57 | 923B97C4130874150057761F /* TRContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = TRContentView.m; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 58 | 923B97C6130874620057761F /* content_bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = content_bg.png; sourceTree = ""; }; 59 | 923B97DF130875110057761F /* TRMainWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TRMainWindowController.h; sourceTree = ""; }; 60 | 923B97E0130875110057761F /* TRMainWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TRMainWindowController.m; sourceTree = ""; }; 61 | 923B97E21308751E0057761F /* TRMainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TRMainWindow.xib; sourceTree = ""; }; 62 | 923B97ED130876F50057761F /* TRPianobarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TRPianobarManager.h; sourceTree = ""; }; 63 | 923B97EE130876F50057761F /* TRPianobarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TRPianobarManager.m; sourceTree = ""; }; 64 | 923B9851130883A80057761F /* TransistorHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TransistorHelper; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 923B985B130883C90057761F /* TransistorHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransistorHelper.m; sourceTree = ""; }; 66 | 923B9A0213088FF20057761F /* next.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = next.png; sourceTree = ""; }; 67 | 923B9A0313088FF20057761F /* pause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pause.png; sourceTree = ""; }; 68 | 923B9A0413088FF20057761F /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = ""; }; 69 | 923B9A291308954C0057761F /* next_over.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = next_over.png; sourceTree = ""; }; 70 | 923B9A2A1308954C0057761F /* pause_over.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pause_over.png; sourceTree = ""; }; 71 | 923B9A2B1308954C0057761F /* play_over.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play_over.png; sourceTree = ""; }; 72 | 923B9A36130896610057761F /* TRArtworkView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TRArtworkView.h; sourceTree = ""; }; 73 | 923B9A37130896610057761F /* TRArtworkView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TRArtworkView.m; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 923B984F130883A80057761F /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | /* End PBXFrameworksBuildPhase section */ 93 | 94 | /* Begin PBXGroup section */ 95 | 080E96DDFE201D6D7F000001 /* Classes */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 923B9855130883BA0057761F /* TransistorHelper */, 99 | 923B97E5130876CC0057761F /* Pianobar */, 100 | 923B97C2130873D70057761F /* Views */, 101 | 256AC3D80F4B6AC300CF3369 /* TRAppDelegate.h */, 102 | 256AC3D90F4B6AC300CF3369 /* TRAppDelegate.m */, 103 | 923B97DF130875110057761F /* TRMainWindowController.h */, 104 | 923B97E0130875110057761F /* TRMainWindowController.m */, 105 | ); 106 | name = Classes; 107 | path = source/Classes; 108 | sourceTree = ""; 109 | }; 110 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 114 | ); 115 | name = "Linked Frameworks"; 116 | sourceTree = ""; 117 | }; 118 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 122 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 123 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 124 | ); 125 | name = "Other Frameworks"; 126 | sourceTree = ""; 127 | }; 128 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 8D1107320486CEB800E47090 /* Transistor.app */, 132 | 923B9851130883A80057761F /* TransistorHelper */, 133 | ); 134 | name = Products; 135 | sourceTree = ""; 136 | }; 137 | 29B97314FDCFA39411CA2CEA /* Panda */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 080E96DDFE201D6D7F000001 /* Classes */, 141 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 142 | 29B97317FDCFA39411CA2CEA /* Resources */, 143 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 144 | 19C28FACFE9D520D11CA2CBB /* Products */, 145 | ); 146 | name = Panda; 147 | sourceTree = ""; 148 | }; 149 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 256AC3F00F4B6AF500CF3369 /* Transistor_Prefix.pch */, 153 | 29B97316FDCFA39411CA2CEA /* main.m */, 154 | ); 155 | name = "Other Sources"; 156 | sourceTree = ""; 157 | }; 158 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 923B9A291308954C0057761F /* next_over.png */, 162 | 923B9A2A1308954C0057761F /* pause_over.png */, 163 | 923B9A2B1308954C0057761F /* play_over.png */, 164 | 923B9A0213088FF20057761F /* next.png */, 165 | 923B9A0313088FF20057761F /* pause.png */, 166 | 923B9A0413088FF20057761F /* play.png */, 167 | 923B97C6130874620057761F /* content_bg.png */, 168 | 37A621F21342BD4600F852F5 /* pandora_logo.png */, 169 | 37A621F41342BD6600F852F5 /* Icon.icns */, 170 | 8D1107310486CEB800E47090 /* Transistor-Info.plist */, 171 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 172 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, 173 | 923B97E21308751E0057761F /* TRMainWindow.xib */, 174 | ); 175 | name = Resources; 176 | path = resources; 177 | sourceTree = ""; 178 | }; 179 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 183 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 184 | ); 185 | name = Frameworks; 186 | sourceTree = ""; 187 | }; 188 | 923B97C2130873D70057761F /* Views */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 923B97C3130874150057761F /* TRContentView.h */, 192 | 923B97C4130874150057761F /* TRContentView.m */, 193 | 923B9A36130896610057761F /* TRArtworkView.h */, 194 | 923B9A37130896610057761F /* TRArtworkView.m */, 195 | ); 196 | name = Views; 197 | sourceTree = ""; 198 | }; 199 | 923B97E5130876CC0057761F /* Pianobar */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 923B97ED130876F50057761F /* TRPianobarManager.h */, 203 | 923B97EE130876F50057761F /* TRPianobarManager.m */, 204 | ); 205 | name = Pianobar; 206 | sourceTree = ""; 207 | }; 208 | 923B9855130883BA0057761F /* TransistorHelper */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | 923B985B130883C90057761F /* TransistorHelper.m */, 212 | ); 213 | name = TransistorHelper; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXGroup section */ 217 | 218 | /* Begin PBXNativeTarget section */ 219 | 8D1107260486CEB800E47090 /* Transistor */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Transistor" */; 222 | buildPhases = ( 223 | 8D1107290486CEB800E47090 /* Resources */, 224 | 8D11072C0486CEB800E47090 /* Sources */, 225 | 8D11072E0486CEB800E47090 /* Frameworks */, 226 | ); 227 | buildRules = ( 228 | ); 229 | dependencies = ( 230 | 923B9B241308AD310057761F /* PBXTargetDependency */, 231 | ); 232 | name = Transistor; 233 | productInstallPath = "$(HOME)/Applications"; 234 | productName = Panda; 235 | productReference = 8D1107320486CEB800E47090 /* Transistor.app */; 236 | productType = "com.apple.product-type.application"; 237 | }; 238 | 923B9850130883A80057761F /* TransistorHelper */ = { 239 | isa = PBXNativeTarget; 240 | buildConfigurationList = 923B9856130883BA0057761F /* Build configuration list for PBXNativeTarget "TransistorHelper" */; 241 | buildPhases = ( 242 | 923B984E130883A80057761F /* Sources */, 243 | 923B984F130883A80057761F /* Frameworks */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | ); 249 | name = TransistorHelper; 250 | productName = TransistorHelper; 251 | productReference = 923B9851130883A80057761F /* TransistorHelper */; 252 | productType = "com.apple.product-type.tool"; 253 | }; 254 | /* End PBXNativeTarget section */ 255 | 256 | /* Begin PBXProject section */ 257 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 258 | isa = PBXProject; 259 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Transistor" */; 260 | compatibilityVersion = "Xcode 3.1"; 261 | developmentRegion = English; 262 | hasScannedForEncodings = 1; 263 | knownRegions = ( 264 | English, 265 | Japanese, 266 | French, 267 | German, 268 | ); 269 | mainGroup = 29B97314FDCFA39411CA2CEA /* Panda */; 270 | projectDirPath = ""; 271 | projectRoot = ""; 272 | targets = ( 273 | 923B9850130883A80057761F /* TransistorHelper */, 274 | 8D1107260486CEB800E47090 /* Transistor */, 275 | ); 276 | }; 277 | /* End PBXProject section */ 278 | 279 | /* Begin PBXResourcesBuildPhase section */ 280 | 8D1107290486CEB800E47090 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 923B9B2A1308ADAA0057761F /* TransistorHelper in Resources */, 285 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 286 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, 287 | 923B97C7130874620057761F /* content_bg.png in Resources */, 288 | 923B97E31308751E0057761F /* TRMainWindow.xib in Resources */, 289 | 923B9A0513088FF20057761F /* next.png in Resources */, 290 | 923B9A0613088FF20057761F /* pause.png in Resources */, 291 | 923B9A0713088FF20057761F /* play.png in Resources */, 292 | 923B9A2C1308954C0057761F /* next_over.png in Resources */, 293 | 923B9A2D1308954C0057761F /* pause_over.png in Resources */, 294 | 923B9A2E1308954C0057761F /* play_over.png in Resources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXResourcesBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | 8D11072C0486CEB800E47090 /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 306 | 256AC3DA0F4B6AC300CF3369 /* TRAppDelegate.m in Sources */, 307 | 923B97C5130874150057761F /* TRContentView.m in Sources */, 308 | 923B97E1130875110057761F /* TRMainWindowController.m in Sources */, 309 | 923B97EF130876F50057761F /* TRPianobarManager.m in Sources */, 310 | 923B9A38130896610057761F /* TRArtworkView.m in Sources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 923B984E130883A80057761F /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 923B985D130883E40057761F /* TransistorHelper.m in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXTargetDependency section */ 325 | 923B9B241308AD310057761F /* PBXTargetDependency */ = { 326 | isa = PBXTargetDependency; 327 | target = 923B9850130883A80057761F /* TransistorHelper */; 328 | targetProxy = 923B9B231308AD310057761F /* PBXContainerItemProxy */; 329 | }; 330 | /* End PBXTargetDependency section */ 331 | 332 | /* Begin PBXVariantGroup section */ 333 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | 089C165DFE840E0CC02AAC07 /* English */, 337 | ); 338 | name = InfoPlist.strings; 339 | sourceTree = ""; 340 | }; 341 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | 1DDD58150DA1D0A300B32029 /* English */, 345 | ); 346 | name = MainMenu.xib; 347 | sourceTree = ""; 348 | }; 349 | /* End PBXVariantGroup section */ 350 | 351 | /* Begin XCBuildConfiguration section */ 352 | 923B9853130883A80057761F /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | COPY_PHASE_STRIP = NO; 357 | GCC_DYNAMIC_NO_PIC = NO; 358 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 359 | GCC_MODEL_TUNING = G5; 360 | GCC_OPTIMIZATION_LEVEL = 0; 361 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 362 | INSTALL_PATH = /usr/local/bin; 363 | OTHER_LDFLAGS = ( 364 | "-framework", 365 | Foundation, 366 | "-framework", 367 | AppKit, 368 | ); 369 | PREBINDING = NO; 370 | PRODUCT_NAME = TransistorHelper; 371 | }; 372 | name = Debug; 373 | }; 374 | 923B9854130883A80057761F /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | COPY_PHASE_STRIP = YES; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 381 | GCC_MODEL_TUNING = G5; 382 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 383 | INSTALL_PATH = /usr/local/bin; 384 | OTHER_LDFLAGS = ( 385 | "-framework", 386 | Foundation, 387 | "-framework", 388 | AppKit, 389 | ); 390 | PREBINDING = NO; 391 | PRODUCT_NAME = TransistorHelper; 392 | ZERO_LINK = NO; 393 | }; 394 | name = Release; 395 | }; 396 | C01FCF4B08A954540054247B /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | COPY_PHASE_STRIP = NO; 401 | GCC_DYNAMIC_NO_PIC = NO; 402 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 403 | GCC_MODEL_TUNING = G5; 404 | GCC_OPTIMIZATION_LEVEL = 0; 405 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 406 | GCC_PREFIX_HEADER = source/Transistor_Prefix.pch; 407 | INFOPLIST_FILE = "resources/Transistor-Info.plist"; 408 | INSTALL_PATH = "$(HOME)/Applications"; 409 | PRODUCT_NAME = Transistor; 410 | VALID_ARCHS = "i386 x86_64"; 411 | }; 412 | name = Debug; 413 | }; 414 | C01FCF4C08A954540054247B /* Release */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ALWAYS_SEARCH_USER_PATHS = NO; 418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 419 | GCC_MODEL_TUNING = G5; 420 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 421 | GCC_PREFIX_HEADER = source/Transistor_Prefix.pch; 422 | INFOPLIST_FILE = "resources/Transistor-Info.plist"; 423 | INSTALL_PATH = "$(HOME)/Applications"; 424 | PRODUCT_NAME = Transistor; 425 | VALID_ARCHS = "i386 x86_64"; 426 | }; 427 | name = Release; 428 | }; 429 | C01FCF4F08A954540054247B /* Debug */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_OPTIMIZATION_LEVEL = 0; 435 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 436 | GCC_WARN_UNUSED_VARIABLE = YES; 437 | ONLY_ACTIVE_ARCH = YES; 438 | PREBINDING = NO; 439 | SDKROOT = macosx10.6; 440 | }; 441 | name = Debug; 442 | }; 443 | C01FCF5008A954540054247B /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 447 | GCC_C_LANGUAGE_STANDARD = gnu99; 448 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 449 | GCC_WARN_UNUSED_VARIABLE = YES; 450 | PREBINDING = NO; 451 | SDKROOT = macosx10.6; 452 | }; 453 | name = Release; 454 | }; 455 | /* End XCBuildConfiguration section */ 456 | 457 | /* Begin XCConfigurationList section */ 458 | 923B9856130883BA0057761F /* Build configuration list for PBXNativeTarget "TransistorHelper" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 923B9853130883A80057761F /* Debug */, 462 | 923B9854130883A80057761F /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Transistor" */ = { 468 | isa = XCConfigurationList; 469 | buildConfigurations = ( 470 | C01FCF4B08A954540054247B /* Debug */, 471 | C01FCF4C08A954540054247B /* Release */, 472 | ); 473 | defaultConfigurationIsVisible = 0; 474 | defaultConfigurationName = Release; 475 | }; 476 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Transistor" */ = { 477 | isa = XCConfigurationList; 478 | buildConfigurations = ( 479 | C01FCF4F08A954540054247B /* Debug */, 480 | C01FCF5008A954540054247B /* Release */, 481 | ); 482 | defaultConfigurationIsVisible = 0; 483 | defaultConfigurationName = Release; 484 | }; 485 | /* End XCConfigurationList section */ 486 | }; 487 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 488 | } 489 | -------------------------------------------------------------------------------- /resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /resources/Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/Icon.icns -------------------------------------------------------------------------------- /resources/TRMainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1060 5 | 10J869 6 | 1306 7 | 1038.35 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 1306 12 | 13 | 14 | YES 15 | NSSecureTextField 16 | NSScroller 17 | NSButton 18 | NSTextFieldCell 19 | NSScrollView 20 | NSButtonCell 21 | NSImageView 22 | NSImageCell 23 | NSSecureTextFieldCell 24 | NSCustomView 25 | NSTextView 26 | NSView 27 | NSWindowTemplate 28 | NSTextField 29 | NSCustomObject 30 | 31 | 32 | YES 33 | com.apple.InterfaceBuilder.CocoaPlugin 34 | 35 | 36 | YES 37 | 38 | YES 39 | 40 | 41 | 42 | 43 | YES 44 | 45 | TRMainWindowController 46 | 47 | 48 | FirstResponder 49 | 50 | 51 | NSApplication 52 | 53 | 54 | 7 55 | 2 56 | {{1142, 619}, {201, 285}} 57 | 1954022400 58 | Transistor 59 | NSWindow 60 | 61 | {201, 285} 62 | {201, 285} 63 | 64 | 65 | 256 66 | 67 | YES 68 | 69 | 70 | 268 71 | {{51, 13}, {36, 31}} 72 | 73 | 74 | 75 | YES 76 | 77 | 67239424 78 | 134250496 79 | 80 | 81 | LucidaGrande 82 | 13 83 | 1044 84 | 85 | 86 | 139215103 87 | 6 88 | 89 | NSImage 90 | next 91 | 92 | 93 | NSImage 94 | next_over 95 | 96 | 97 | 98 | 400 99 | 75 100 | 101 | 102 | 103 | 104 | 268 105 | {{11, 14}, {30, 31}} 106 | 107 | 108 | 109 | YES 110 | 111 | 67239424 112 | 134250496 113 | 114 | 115 | 116 | 138690815 117 | 6 118 | 119 | NSImage 120 | pause 121 | 122 | 123 | NSImage 124 | pause_over 125 | 126 | 127 | IA 128 | 400 129 | 75 130 | 131 | 132 | 133 | 134 | 266 135 | {{92, 21}, {93, 17}} 136 | 137 | 138 | 139 | YES 140 | 141 | 68288064 142 | 272630784 143 | 144 | 145 | HelveticaNeue-Bold 146 | 11 147 | 16 148 | 149 | 150 | 151 | 6 152 | System 153 | controlColor 154 | 155 | 3 156 | MC42NjY2NjY2NjY3AA 157 | 158 | 159 | 160 | 2 161 | MC42MzkyMTU3MDc4IDAuNjYyNzQ1MTE4MSAwLjY4MjM1Mjk2MDEAA 162 | 163 | 164 | 165 | 166 | 167 | 266 168 | {{12, 75}, {174, 19}} 169 | 170 | 171 | 172 | YES 173 | 174 | 68288064 175 | 272630784 176 | 177 | 178 | HelveticaNeue-Bold 179 | 12 180 | 16 181 | 182 | 183 | 184 | 185 | 3 186 | MQA 187 | 188 | 189 | 190 | 191 | 192 | 266 193 | {{12, 56}, {174, 17}} 194 | 195 | 196 | 197 | YES 198 | 199 | 68288064 200 | 272630784 201 | 202 | 203 | 204 | 205 | 206 | 2 207 | MC42MzkyMTU3MDc4IDAuNjYyNzQ1MTE4MSAwLjY4MjM1Mjk2MDEAA 208 | 209 | 210 | 211 | 212 | 213 | 266 214 | {{12, 96}, {174, 17}} 215 | 216 | 217 | 218 | YES 219 | 220 | 68288064 221 | 272630784 222 | 223 | 224 | 225 | 226 | 227 | 2 228 | MC42MzkyMTU3MDc4IDAuNjYyNzQ1MTE4MSAwLjY4MjM1Mjk2MDEAA 229 | 230 | 231 | 232 | 233 | 234 | 268 235 | {{27, 125}, {140, 140}} 236 | 237 | 238 | 239 | YES 240 | 2 241 | TRArtworkView 242 | 243 | 244 | {{7, 11}, {201, 285}} 245 | 246 | 247 | 248 | 249 | {{0, 0}, {1440, 878}} 250 | {201, 307} 251 | {201, 307} 252 | player_window 253 | 254 | 255 | 15 256 | 2 257 | {{340, 293}, {276, 344}} 258 | 611845120 259 | Stations 260 | NSWindow 261 | 262 | {276, 344} 263 | 264 | 265 | 256 266 | 267 | YES 268 | 269 | 270 | 268 271 | {{17, 307}, {59, 17}} 272 | 273 | 274 | YES 275 | 276 | 68288064 277 | 272630784 278 | Stations: 279 | 280 | 281 | 282 | 283 | 6 284 | System 285 | controlTextColor 286 | 287 | 3 288 | MAA 289 | 290 | 291 | 292 | 293 | 294 | 295 | 274 296 | 297 | YES 298 | 299 | 300 | 2304 301 | 302 | YES 303 | 304 | 305 | 2322 306 | {234, 133} 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | YES 319 | 320 | 321 | 134 322 | 323 | 324 | 325 | 234 326 | 1 327 | 328 | 329 | 12261 330 | 0 331 | 332 | 333 | 334 | YES 335 | 336 | YES 337 | NSBackgroundColor 338 | NSColor 339 | 340 | 341 | YES 342 | 343 | 6 344 | System 345 | selectedTextBackgroundColor 346 | 347 | 348 | 349 | 6 350 | System 351 | selectedTextColor 352 | 353 | 354 | 355 | 356 | 357 | 358 | YES 359 | 360 | YES 361 | NSColor 362 | NSCursor 363 | NSUnderline 364 | 365 | 366 | YES 367 | 368 | 1 369 | MCAwIDEAA 370 | 371 | 372 | {8, -8} 373 | 13 374 | 375 | 376 | 377 | 378 | 379 | 380 | 6 381 | {463, 1e+07} 382 | {216, 133} 383 | 384 | 385 | 386 | {{1, 1}, {234, 243}} 387 | 388 | 389 | 390 | 391 | 392 | {4, -5} 393 | 1 394 | 395 | 4 396 | 397 | 398 | 399 | -2147483392 400 | {{220, 1}, {15, 243}} 401 | 402 | 403 | 404 | _doScroller: 405 | 0.99248120300751874 406 | 0.85256409645080566 407 | 408 | 409 | 410 | -2147483392 411 | {{-100, -100}, {87, 18}} 412 | 413 | 414 | 1 415 | 416 | _doScroller: 417 | 1 418 | 0.94565218687057495 419 | 420 | 421 | {{20, 54}, {236, 245}} 422 | 423 | 424 | 530 425 | 426 | 427 | 428 | 429 | 430 | 431 | 290 432 | {{20, 20}, {98, 22}} 433 | 434 | 435 | YES 436 | 437 | -1804468671 438 | 272630784 439 | 440 | 441 | 442 | YES 443 | 444 | 6 445 | System 446 | textBackgroundColor 447 | 448 | 449 | 450 | 6 451 | System 452 | textColor 453 | 454 | 455 | 456 | 457 | 458 | 459 | 257 460 | {{131, 18}, {125, 25}} 461 | 462 | YES 463 | 464 | -2080244224 465 | 134217728 466 | Listen to Station 467 | 468 | 469 | -2038152961 470 | 163 471 | 472 | DQ 473 | 400 474 | 75 475 | 476 | 477 | 478 | {{7, 11}, {276, 344}} 479 | 480 | 481 | {{0, 0}, {1440, 878}} 482 | {276, 366} 483 | {1e+13, 1e+13} 484 | 485 | 486 | 7 487 | 2 488 | {{340, 293}, {480, 195}} 489 | 611845120 490 | Sign In 491 | NSWindow 492 | 493 | {480, 195} 494 | 495 | 496 | 256 497 | 498 | YES 499 | 500 | 501 | 256 502 | 503 | YES 504 | 505 | YES 506 | Apple PDF pasteboard type 507 | Apple PICT pasteboard type 508 | Apple PNG pasteboard type 509 | NSFilenamesPboardType 510 | NeXT Encapsulated PostScript v1.2 pasteboard type 511 | NeXT TIFF v4.0 pasteboard type 512 | 513 | 514 | {{20, 111}, {440, 64}} 515 | 516 | 517 | 518 | YES 519 | 520 | 130560 521 | 33554432 522 | 523 | NSImage 524 | pandora_logo 525 | 526 | 0 527 | 0 528 | 0 529 | YES 530 | 531 | YES 532 | 533 | 534 | 535 | 268 536 | {{164, 81}, {181, 22}} 537 | 538 | 539 | 540 | YES 541 | 542 | -1804468671 543 | 272630784 544 | 545 | 546 | 547 | YES 548 | 549 | 550 | 551 | 552 | 553 | 554 | 268 555 | {{164, 47}, {181, 22}} 556 | 557 | 558 | 559 | YES 560 | 561 | 343014976 562 | 272630848 563 | 564 | 565 | 566 | YES 567 | 568 | 569 | 570 | YES 571 | NSAllRomanInputSourcesLocaleIdentifier 572 | 573 | 574 | 575 | 576 | 577 | 268 578 | {{88, 84}, {71, 17}} 579 | 580 | 581 | 582 | YES 583 | 584 | 68288064 585 | 272630784 586 | Username: 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 268 596 | {{88, 50}, {68, 17}} 597 | 598 | 599 | 600 | YES 601 | 602 | 68288064 603 | 272630784 604 | Password: 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 268 614 | {{370, 12}, {96, 32}} 615 | 616 | 617 | 618 | YES 619 | 620 | 67239424 621 | 134217728 622 | Sign In 623 | 624 | 625 | -2038284033 626 | 129 627 | 628 | DQ 629 | 200 630 | 25 631 | 632 | 633 | 634 | 635 | 268 636 | {{18, 18}, {90, 18}} 637 | 638 | 639 | 640 | YES 641 | 642 | -2080244224 643 | 0 644 | Remember 645 | 646 | 647 | 1211912703 648 | 2 649 | 650 | NSImage 651 | NSSwitch 652 | 653 | 654 | NSSwitch 655 | 656 | 657 | 658 | 200 659 | 25 660 | 661 | 662 | 663 | {{7, 11}, {480, 195}} 664 | 665 | 666 | 667 | 668 | {{0, 0}, {1440, 878}} 669 | {480, 217} 670 | {1e+13, 1e+13} 671 | 672 | 673 | 674 | 675 | YES 676 | 677 | 678 | window 679 | 680 | 681 | 682 | 19 683 | 684 | 685 | 686 | delegate 687 | 688 | 689 | 690 | 20 691 | 692 | 693 | 694 | artist 695 | 696 | 697 | 698 | 22 699 | 700 | 701 | 702 | song 703 | 704 | 705 | 706 | 23 707 | 708 | 709 | 710 | album 711 | 712 | 713 | 714 | 24 715 | 716 | 717 | 718 | time 719 | 720 | 721 | 722 | 25 723 | 724 | 725 | 726 | pause: 727 | 728 | 729 | 730 | 26 731 | 732 | 733 | 734 | next: 735 | 736 | 737 | 738 | 27 739 | 740 | 741 | 742 | pauseButton 743 | 744 | 745 | 746 | 28 747 | 748 | 749 | 750 | artwork 751 | 752 | 753 | 754 | 30 755 | 756 | 757 | 758 | stations 759 | 760 | 761 | 762 | 46 763 | 764 | 765 | 766 | station 767 | 768 | 769 | 770 | 47 771 | 772 | 773 | 774 | stationsWindow 775 | 776 | 777 | 778 | 51 779 | 780 | 781 | 782 | didSelectStation: 783 | 784 | 785 | 786 | 52 787 | 788 | 789 | 790 | signInWindow 791 | 792 | 793 | 794 | 99 795 | 796 | 797 | 798 | signIn: 799 | 800 | 801 | 802 | 101 803 | 804 | 805 | 806 | usernameField 807 | 808 | 809 | 810 | 102 811 | 812 | 813 | 814 | passwordField 815 | 816 | 817 | 818 | 106 819 | 820 | 821 | 822 | 823 | YES 824 | 825 | 0 826 | 827 | 828 | 829 | 830 | 831 | -2 832 | 833 | 834 | File's Owner 835 | 836 | 837 | -1 838 | 839 | 840 | First Responder 841 | 842 | 843 | -3 844 | 845 | 846 | Application 847 | 848 | 849 | 3 850 | 851 | 852 | YES 853 | 854 | 855 | 856 | 857 | 858 | 4 859 | 860 | 861 | YES 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 5 874 | 875 | 876 | YES 877 | 878 | 879 | 880 | 881 | 882 | 6 883 | 884 | 885 | YES 886 | 887 | 888 | 889 | 890 | 891 | 8 892 | 893 | 894 | YES 895 | 896 | 897 | 898 | 899 | 900 | 9 901 | 902 | 903 | YES 904 | 905 | 906 | 907 | 908 | 909 | 10 910 | 911 | 912 | YES 913 | 914 | 915 | 916 | 917 | 918 | 13 919 | 920 | 921 | 922 | 923 | 14 924 | 925 | 926 | 927 | 928 | 15 929 | 930 | 931 | 932 | 933 | 17 934 | 935 | 936 | 937 | 938 | 18 939 | 940 | 941 | 942 | 943 | 29 944 | 945 | 946 | 947 | 948 | 49 949 | 950 | 951 | YES 952 | 953 | 954 | 955 | 956 | 957 | 50 958 | 959 | 960 | YES 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 33 970 | 971 | 972 | YES 973 | 974 | 975 | 976 | 977 | 978 | 34 979 | 980 | 981 | 982 | 983 | 35 984 | 985 | 986 | YES 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 38 995 | 996 | 997 | 998 | 999 | 37 1000 | 1001 | 1002 | 1003 | 1004 | 36 1005 | 1006 | 1007 | 1008 | 1009 | 39 1010 | 1011 | 1012 | YES 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 40 1019 | 1020 | 1021 | 1022 | 1023 | 43 1024 | 1025 | 1026 | YES 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 44 1033 | 1034 | 1035 | 1036 | 1037 | 7 1038 | 1039 | 1040 | YES 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 16 1047 | 1048 | 1049 | 1050 | 1051 | 72 1052 | 1053 | 1054 | YES 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 73 1061 | 1062 | 1063 | YES 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 85 1076 | 1077 | 1078 | YES 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 86 1085 | 1086 | 1087 | YES 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 88 1094 | 1095 | 1096 | YES 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 89 1103 | 1104 | 1105 | YES 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 90 1112 | 1113 | 1114 | YES 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 91 1121 | 1122 | 1123 | YES 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 92 1130 | 1131 | 1132 | 1133 | 1134 | 93 1135 | 1136 | 1137 | 1138 | 1139 | 94 1140 | 1141 | 1142 | 1143 | 1144 | 95 1145 | 1146 | 1147 | 1148 | 1149 | 97 1150 | 1151 | 1152 | 1153 | 1154 | 98 1155 | 1156 | 1157 | 1158 | 1159 | 104 1160 | 1161 | 1162 | YES 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 105 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | YES 1176 | 1177 | YES 1178 | 10.IBPluginDependency 1179 | 10.IBViewBoundsToFrameTransform 1180 | 104.IBPluginDependency 1181 | 105.IBPluginDependency 1182 | 13.IBPluginDependency 1183 | 14.IBPluginDependency 1184 | 15.IBPluginDependency 1185 | 16.IBPluginDependency 1186 | 17.IBPluginDependency 1187 | 18.IBPluginDependency 1188 | 29.IBPluginDependency 1189 | 29.IBViewBoundsToFrameTransform 1190 | 29.IBViewIntegration.shadowBlurRadius 1191 | 29.IBViewIntegration.shadowColor 1192 | 29.IBViewIntegration.shadowOffsetHeight 1193 | 29.IBViewIntegration.shadowOffsetWidth 1194 | 3.IBEditorWindowLastContentRect 1195 | 3.IBPluginDependency 1196 | 3.IBViewEditorWindowController.showingLayoutRectangles 1197 | 3.IBWindowTemplateEditedContentRect 1198 | 3.NSWindowTemplate.visibleAtLaunch 1199 | 3.editorWindowContentRectSynchronizationRect 1200 | 33.IBPluginDependency 1201 | 33.IBViewBoundsToFrameTransform 1202 | 34.IBPluginDependency 1203 | 35.IBPluginDependency 1204 | 35.IBViewBoundsToFrameTransform 1205 | 36.IBPluginDependency 1206 | 37.IBPluginDependency 1207 | 38.IBPluginDependency 1208 | 39.IBPluginDependency 1209 | 39.IBViewBoundsToFrameTransform 1210 | 4.CustomClassName 1211 | 4.IBPluginDependency 1212 | 40.IBPluginDependency 1213 | 43.IBPluginDependency 1214 | 43.IBViewBoundsToFrameTransform 1215 | 44.IBPluginDependency 1216 | 49.IBEditorWindowLastContentRect 1217 | 49.IBPluginDependency 1218 | 49.IBWindowTemplateEditedContentRect 1219 | 49.NSWindowTemplate.visibleAtLaunch 1220 | 5.IBPluginDependency 1221 | 5.IBViewBoundsToFrameTransform 1222 | 50.IBPluginDependency 1223 | 6.IBPluginDependency 1224 | 6.IBViewBoundsToFrameTransform 1225 | 7.IBPluginDependency 1226 | 7.IBViewBoundsToFrameTransform 1227 | 72.IBEditorWindowLastContentRect 1228 | 72.IBPluginDependency 1229 | 72.IBWindowTemplateEditedContentRect 1230 | 72.NSWindowTemplate.visibleAtLaunch 1231 | 73.IBPluginDependency 1232 | 8.IBPluginDependency 1233 | 8.IBViewBoundsToFrameTransform 1234 | 86.IBPluginDependency 1235 | 88.IBPluginDependency 1236 | 89.IBPluginDependency 1237 | 9.IBPluginDependency 1238 | 9.IBViewBoundsToFrameTransform 1239 | 90.IBPluginDependency 1240 | 91.IBPluginDependency 1241 | 92.IBPluginDependency 1242 | 93.IBPluginDependency 1243 | 94.IBPluginDependency 1244 | 95.IBPluginDependency 1245 | 97.IBPluginDependency 1246 | 1247 | 1248 | YES 1249 | com.apple.InterfaceBuilder.CocoaPlugin 1250 | 1251 | P4AAAL+AAABCSAAAwlwAAA 1252 | 1253 | com.apple.InterfaceBuilder.CocoaPlugin 1254 | com.apple.InterfaceBuilder.CocoaPlugin 1255 | com.apple.InterfaceBuilder.CocoaPlugin 1256 | com.apple.InterfaceBuilder.CocoaPlugin 1257 | com.apple.InterfaceBuilder.CocoaPlugin 1258 | com.apple.InterfaceBuilder.CocoaPlugin 1259 | com.apple.InterfaceBuilder.CocoaPlugin 1260 | com.apple.InterfaceBuilder.CocoaPlugin 1261 | com.apple.InterfaceBuilder.CocoaPlugin 1262 | 1263 | AUHIAABC/AAAA 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | {{872, 884}, {201, 285}} 1270 | com.apple.InterfaceBuilder.CocoaPlugin 1271 | 1272 | {{872, 884}, {201, 285}} 1273 | 1274 | {{33, 99}, {480, 360}} 1275 | com.apple.InterfaceBuilder.CocoaPlugin 1276 | 1277 | P4AAAL+AAABBiAAAw5+AAA 1278 | 1279 | com.apple.InterfaceBuilder.CocoaPlugin 1280 | com.apple.InterfaceBuilder.CocoaPlugin 1281 | 1282 | P4AAAL+AAABBoAAAw5MAAA 1283 | 1284 | com.apple.InterfaceBuilder.CocoaPlugin 1285 | com.apple.InterfaceBuilder.CocoaPlugin 1286 | com.apple.InterfaceBuilder.CocoaPlugin 1287 | com.apple.InterfaceBuilder.CocoaPlugin 1288 | 1289 | P4AAAL+AAABBoAAAwhQAAA 1290 | 1291 | TRContentView 1292 | com.apple.InterfaceBuilder.CocoaPlugin 1293 | com.apple.InterfaceBuilder.CocoaPlugin 1294 | com.apple.InterfaceBuilder.CocoaPlugin 1295 | 1296 | P4AAAL+AAABDAwAAwhgAAA 1297 | 1298 | com.apple.InterfaceBuilder.CocoaPlugin 1299 | {{1298, 677}, {276, 344}} 1300 | com.apple.InterfaceBuilder.CocoaPlugin 1301 | {{1298, 677}, {276, 344}} 1302 | 1303 | com.apple.InterfaceBuilder.CocoaPlugin 1304 | 1305 | P4AAAL+AAABAoAAAw38AAA 1306 | 1307 | com.apple.InterfaceBuilder.CocoaPlugin 1308 | com.apple.InterfaceBuilder.CocoaPlugin 1309 | 1310 | P4AAAL+AAABBIAAAwuQAAA 1311 | 1312 | com.apple.InterfaceBuilder.CocoaPlugin 1313 | 1314 | P4AAAL+AAABBIAAAwwkAAA 1315 | 1316 | {{1298, 677}, {276, 344}} 1317 | com.apple.InterfaceBuilder.CocoaPlugin 1318 | {{1298, 677}, {276, 344}} 1319 | 1320 | com.apple.InterfaceBuilder.CocoaPlugin 1321 | com.apple.InterfaceBuilder.CocoaPlugin 1322 | 1323 | P4AAAL+AAABCtgAAwkQAAA 1324 | 1325 | com.apple.InterfaceBuilder.CocoaPlugin 1326 | com.apple.InterfaceBuilder.CocoaPlugin 1327 | com.apple.InterfaceBuilder.CocoaPlugin 1328 | com.apple.InterfaceBuilder.CocoaPlugin 1329 | 1330 | P4AAAL+AAABBIAAAwmAAAA 1331 | 1332 | com.apple.InterfaceBuilder.CocoaPlugin 1333 | com.apple.InterfaceBuilder.CocoaPlugin 1334 | com.apple.InterfaceBuilder.CocoaPlugin 1335 | com.apple.InterfaceBuilder.CocoaPlugin 1336 | com.apple.InterfaceBuilder.CocoaPlugin 1337 | com.apple.InterfaceBuilder.CocoaPlugin 1338 | com.apple.InterfaceBuilder.CocoaPlugin 1339 | 1340 | 1341 | 1342 | YES 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | YES 1349 | 1350 | 1351 | 1352 | 1353 | 106 1354 | 1355 | 1356 | 1357 | YES 1358 | 1359 | TRArtworkView 1360 | NSView 1361 | 1362 | IBProjectSource 1363 | ./Classes/TRArtworkView.h 1364 | 1365 | 1366 | 1367 | TRContentView 1368 | NSView 1369 | 1370 | IBProjectSource 1371 | ./Classes/TRContentView.h 1372 | 1373 | 1374 | 1375 | TRMainWindowController 1376 | NSWindowController 1377 | 1378 | signIn: 1379 | id 1380 | 1381 | 1382 | signIn: 1383 | 1384 | signIn: 1385 | id 1386 | 1387 | 1388 | 1389 | YES 1390 | 1391 | YES 1392 | album 1393 | artist 1394 | artwork 1395 | passwordField 1396 | pauseButton 1397 | signInWindow 1398 | song 1399 | station 1400 | stations 1401 | stationsWindow 1402 | time 1403 | usernameField 1404 | 1405 | 1406 | YES 1407 | NSTextField 1408 | NSTextField 1409 | TRArtworkView 1410 | NSTextField 1411 | NSButton 1412 | NSWindow 1413 | NSTextField 1414 | NSTextField 1415 | NSTextView 1416 | NSWindow 1417 | NSTextField 1418 | NSTextField 1419 | 1420 | 1421 | 1422 | YES 1423 | 1424 | YES 1425 | album 1426 | artist 1427 | artwork 1428 | passwordField 1429 | pauseButton 1430 | signInWindow 1431 | song 1432 | station 1433 | stations 1434 | stationsWindow 1435 | time 1436 | usernameField 1437 | 1438 | 1439 | YES 1440 | 1441 | album 1442 | NSTextField 1443 | 1444 | 1445 | artist 1446 | NSTextField 1447 | 1448 | 1449 | artwork 1450 | TRArtworkView 1451 | 1452 | 1453 | passwordField 1454 | NSTextField 1455 | 1456 | 1457 | pauseButton 1458 | NSButton 1459 | 1460 | 1461 | signInWindow 1462 | NSWindow 1463 | 1464 | 1465 | song 1466 | NSTextField 1467 | 1468 | 1469 | station 1470 | NSTextField 1471 | 1472 | 1473 | stations 1474 | NSTextView 1475 | 1476 | 1477 | stationsWindow 1478 | NSWindow 1479 | 1480 | 1481 | time 1482 | NSTextField 1483 | 1484 | 1485 | usernameField 1486 | NSTextField 1487 | 1488 | 1489 | 1490 | 1491 | IBProjectSource 1492 | ./Classes/TRMainWindowController.h 1493 | 1494 | 1495 | 1496 | 1497 | 0 1498 | IBCocoaFramework 1499 | 1500 | com.apple.InterfaceBuilder.CocoaPlugin.macosx 1501 | 1502 | 1503 | 1504 | com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 1505 | 1506 | 1507 | YES 1508 | 3 1509 | 1510 | YES 1511 | 1512 | YES 1513 | NSSwitch 1514 | next 1515 | next_over 1516 | pandora_logo 1517 | pause 1518 | pause_over 1519 | 1520 | 1521 | YES 1522 | {15, 15} 1523 | {32, 27} 1524 | {36, 31} 1525 | {440, 64} 1526 | {24, 27} 1527 | {28, 31} 1528 | 1529 | 1530 | 1531 | 1532 | -------------------------------------------------------------------------------- /resources/Transistor-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | Icon.icns 11 | CFBundleIdentifier 12 | com.zachwaugh.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.2 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 0.2 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /resources/content_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/content_bg.png -------------------------------------------------------------------------------- /resources/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/next.png -------------------------------------------------------------------------------- /resources/next_over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/next_over.png -------------------------------------------------------------------------------- /resources/pandora_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/pandora_logo.png -------------------------------------------------------------------------------- /resources/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/pause.png -------------------------------------------------------------------------------- /resources/pause_over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/pause_over.png -------------------------------------------------------------------------------- /resources/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/play.png -------------------------------------------------------------------------------- /resources/play_over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/Transistor/93a4095f4fe9bc6cfa5cc1d15cd4d0b419d7bdac/resources/play_over.png -------------------------------------------------------------------------------- /source/Classes/TRAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TransistorAppDelegate.h 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/8/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class TRMainWindowController; 12 | 13 | @interface TRAppDelegate : NSObject 14 | { 15 | TRMainWindowController *windowController; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /source/Classes/TRAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TransistorAppDelegate.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/8/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import "TRAppDelegate.h" 10 | #import "TRPianobarManager.h" 11 | #import "TRMainWindowController.h" 12 | 13 | @implementation TRAppDelegate 14 | 15 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 16 | { 17 | windowController = [[TRMainWindowController alloc] initWithWindowNibName:@"TRMainWindow"]; 18 | [windowController showWindow:self]; 19 | } 20 | 21 | 22 | - (void)applicationWillTerminate:(NSNotification *)notification 23 | { 24 | [[TRPianobarManager sharedManager] quit]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /source/Classes/TRArtworkView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRArtworkView.h 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface TRArtworkView : NSView 13 | { 14 | NSImage *artwork; 15 | NSMutableData *imageData; 16 | } 17 | 18 | @property (retain) NSImage *artwork; 19 | 20 | - (void)setArtworkURL:(NSURL *)artworkURL; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /source/Classes/TRArtworkView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRArtworkView.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import "TRArtworkView.h" 10 | 11 | @implementation TRArtworkView 12 | 13 | @synthesize artwork; 14 | 15 | - (void)dealloc 16 | { 17 | [artwork release]; 18 | artwork = nil; 19 | 20 | [super dealloc]; 21 | } 22 | 23 | 24 | - (void)drawRect:(NSRect)dirtyRect 25 | { 26 | if (self.artwork != nil) 27 | { 28 | NSShadow *shadow = [[NSShadow alloc] init]; 29 | [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5]]; 30 | [shadow setShadowOffset:NSMakeSize(0, -3)]; 31 | [shadow setShadowBlurRadius:5.0]; 32 | [shadow set]; 33 | 34 | [self.artwork drawInRect:NSMakeRect(5, 0, 130, 130) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; 35 | [shadow release]; 36 | } 37 | } 38 | 39 | 40 | // Flip coordinates, I hate bottom-left origin 41 | - (BOOL)isFlipped 42 | { 43 | return YES; 44 | } 45 | 46 | 47 | // Fade out current artwork and async load image 48 | - (void)setArtworkURL:(NSURL *)artworkURL 49 | { 50 | self.artwork = nil; 51 | 52 | [[self animator] setAlphaValue:0.0]; 53 | 54 | imageData = [[NSMutableData data] retain]; 55 | [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:artworkURL] delegate:self]; 56 | } 57 | 58 | 59 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 60 | { 61 | [imageData appendData:data]; 62 | } 63 | 64 | 65 | // Update image and fade in 66 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 67 | { 68 | self.artwork = [[[NSImage alloc] initWithData:imageData] autorelease]; 69 | [self setNeedsDisplay:YES]; 70 | [[self animator] setAlphaValue:1.0]; 71 | [imageData release]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /source/Classes/TRContentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRContentView.h 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface TRContentView : NSView 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /source/Classes/TRContentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRContentView.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import "TRContentView.h" 10 | 11 | 12 | @implementation TRContentView 13 | 14 | // Draw background of window 15 | // TODO: cache image 16 | - (void)drawRect:(NSRect)dirtyRect 17 | { 18 | [[NSImage imageNamed:@"content_bg.png"] drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /source/Classes/TRMainWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRMainWindowController.h 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class TRPianobarManager, TRArtworkView; 12 | 13 | @interface TRMainWindowController : NSWindowController 14 | { 15 | NSTextField *song; 16 | NSTextField *artist; 17 | NSTextField *album; 18 | NSTextField *time; 19 | TRArtworkView *artwork; 20 | NSButton *pauseButton; 21 | 22 | BOOL paused; 23 | TRPianobarManager *pianobar; 24 | 25 | // Stations 26 | NSWindow *stationsWindow; 27 | NSWindow *signInWindow; 28 | NSTextView *stations; 29 | NSTextField *station; 30 | NSTextField *usernameField; 31 | NSTextField *passwordField; 32 | } 33 | 34 | 35 | @property (assign) IBOutlet NSTextField *song; 36 | @property (assign) IBOutlet NSTextField *artist; 37 | @property (assign) IBOutlet NSTextField *album; 38 | @property (assign) IBOutlet NSTextField *time; 39 | @property (assign) IBOutlet TRArtworkView *artwork; 40 | @property (assign) IBOutlet NSButton *pauseButton; 41 | @property (assign) IBOutlet NSWindow *stationsWindow; 42 | @property (assign) IBOutlet NSWindow *signInWindow; 43 | @property (assign) IBOutlet NSTextView *stations; 44 | @property (assign) IBOutlet NSTextField *station; 45 | @property (assign) IBOutlet NSTextField *usernameField; 46 | @property (assign) IBOutlet NSTextField *passwordField; 47 | @property (assign) BOOL paused; 48 | 49 | 50 | - (void)pause:(id)sender; 51 | - (void)next:(id)sender; 52 | - (void)didSelectStation:(id)sender; 53 | - (void)showStations; 54 | - (void)showSignIn; 55 | - (IBAction)signIn:(id)sender; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /source/Classes/TRMainWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRMainWindowController.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import "TRMainWindowController.h" 10 | #import "TRPianobarManager.h" 11 | #import "TRArtworkView.h" 12 | 13 | 14 | @implementation TRMainWindowController 15 | 16 | @synthesize song, artist, album, time, artwork, paused, pauseButton, stations, station, stationsWindow; 17 | @synthesize signInWindow, usernameField, passwordField; 18 | 19 | - (void)dealloc 20 | { 21 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 22 | 23 | [super dealloc]; 24 | } 25 | 26 | 27 | - (void)awakeFromNib 28 | { 29 | // Keep reference to TRPianobarManager, which also automatically starts pianobar 30 | pianobar = [TRPianobarManager sharedManager]; 31 | // Gives window time to load otherwise modal will not slide 32 | [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showSignIn) userInfo:nil repeats:NO]; 33 | } 34 | 35 | 36 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 37 | { 38 | NSObject *newValue = [change objectForKey:NSKeyValueChangeNewKey]; 39 | NSShadow *shadow = [[[NSShadow alloc] init] autorelease]; 40 | [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.35]]; 41 | [shadow setShadowOffset:NSMakeSize(0, 1)]; 42 | 43 | NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:shadow, NSShadowAttributeName, nil]; 44 | 45 | if ([keyPath isEqualToString:@"currentArtist"]) 46 | { 47 | [self.artist setAttributedStringValue:[[[NSAttributedString alloc] initWithString:(NSString *)newValue attributes:attributes] autorelease]]; 48 | } 49 | else if ([keyPath isEqualToString:@"currentSong"]) 50 | { 51 | [self.song setAttributedStringValue:[[[NSAttributedString alloc] initWithString:(NSString *)newValue attributes:attributes] autorelease]]; 52 | } 53 | else if ([keyPath isEqualToString:@"currentAlbum"]) 54 | { 55 | [self.album setAttributedStringValue:[[[NSAttributedString alloc] initWithString:(NSString *)newValue attributes:attributes] autorelease]]; 56 | } 57 | else if ([keyPath isEqualToString:@"currentTime"]) 58 | { 59 | [self.time setAttributedStringValue:[[[NSAttributedString alloc] initWithString:(NSString *)newValue attributes:attributes] autorelease]]; 60 | } 61 | if ([keyPath isEqualToString:@"currentArtworkURL"]) 62 | { 63 | [self.artwork setArtworkURL:(NSURL *)newValue]; 64 | } 65 | } 66 | 67 | 68 | - (void)pianobarNotification:(NSNotification *)notification 69 | { 70 | if ([notification name] == TransistorSelectStationNotification) 71 | { 72 | [self showStations]; 73 | [self.stations setString:[[notification userInfo] objectForKey:@"stations"]]; 74 | } 75 | } 76 | 77 | 78 | #pragma - 79 | #pragma User API 80 | 81 | - (void)pause:(id)sender 82 | { 83 | [pianobar sendCommand:PAUSE]; 84 | 85 | self.paused = !self.paused; 86 | 87 | if (self.paused) 88 | { 89 | [self.pauseButton setImage:[NSImage imageNamed:@"play.png"]]; 90 | [self.pauseButton setAlternateImage:[NSImage imageNamed:@"play_over.png"]]; 91 | } 92 | else 93 | { 94 | [self.pauseButton setImage:[NSImage imageNamed:@"pause.png"]]; 95 | [self.pauseButton setAlternateImage:[NSImage imageNamed:@"pause_over.png"]]; 96 | } 97 | 98 | } 99 | 100 | 101 | - (void)next:(id)sender 102 | { 103 | // If we are paused then unpause it 104 | if (self.paused) { 105 | self.paused = NO; 106 | [self.pauseButton setImage:[NSImage imageNamed:@"pause.png"]]; 107 | [self.pauseButton setAlternateImage:[NSImage imageNamed:@"pause_over.png"]]; 108 | } 109 | [pianobar sendCommand:NEXT]; 110 | } 111 | 112 | 113 | #pragma mark - 114 | #pragma mark Stations 115 | 116 | 117 | - (void)showStations 118 | { 119 | [NSApp beginSheet:stationsWindow modalForWindow:[self window] modalDelegate:self didEndSelector:nil contextInfo:nil]; 120 | } 121 | 122 | - (void)showSignIn 123 | { 124 | [NSApp beginSheet:signInWindow modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd) contextInfo:nil]; 125 | } 126 | 127 | - (IBAction)signIn:(id)sender 128 | { 129 | [NSApp endSheet:signInWindow]; 130 | [signInWindow orderOut:self]; 131 | } 132 | 133 | - (void)sheetDidEnd 134 | { 135 | [pianobar setUsername:[usernameField stringValue]]; 136 | [pianobar setPassword:[passwordField stringValue]]; 137 | [pianobar launch]; 138 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pianobarNotification:) name:nil object:pianobar]; 139 | 140 | // KVO for all the current properties that pianobar exposes for automatic changes 141 | [pianobar addObserver:self forKeyPath:@"currentArtist" options:NSKeyValueObservingOptionNew context:nil]; 142 | [pianobar addObserver:self forKeyPath:@"currentSong" options:NSKeyValueObservingOptionNew context:nil]; 143 | [pianobar addObserver:self forKeyPath:@"currentAlbum" options:NSKeyValueObservingOptionNew context:nil]; 144 | [pianobar addObserver:self forKeyPath:@"currentTime" options:NSKeyValueObservingOptionNew context:nil]; 145 | [pianobar addObserver:self forKeyPath:@"currentArtworkURL" options:NSKeyValueObservingOptionNew context:nil]; 146 | } 147 | 148 | 149 | - (void)didSelectStation:(id)sender 150 | { 151 | [pianobar sendCommand:[station stringValue]]; 152 | [NSApp endSheet:stationsWindow]; 153 | [stationsWindow orderOut:self]; 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /source/Classes/TRPianobarManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRPianobarManager.h 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define QUIT @"q" 12 | #define PAUSE @"p" 13 | #define NEXT @"n" 14 | 15 | extern NSString * const TransistorSelectStationNotification; 16 | 17 | @interface TRPianobarManager : NSObject 18 | { 19 | NSTask *pianobar; 20 | NSPipe *inputPipe; 21 | NSPipe *outputPipe; 22 | NSFileHandle *readHandle; 23 | NSFileHandle *writeHandle; 24 | 25 | NSString *currentArtist; 26 | NSString *currentSong; 27 | NSString *currentAlbum; 28 | NSString *currentTime; 29 | NSURL *currentArtworkURL; 30 | NSString *stationList; 31 | NSString *username; 32 | NSString *password; 33 | 34 | BOOL stationsStarted; 35 | } 36 | 37 | @property (retain) NSString *currentArtist; 38 | @property (retain) NSString *currentSong; 39 | @property (retain) NSString *currentAlbum; 40 | @property (retain) NSString *currentTime; 41 | @property (retain) NSURL *currentArtworkURL; 42 | @property (retain) NSString *stationList; 43 | @property (retain) NSString *username; 44 | @property (retain) NSString *password; 45 | 46 | + (TRPianobarManager *)sharedManager; 47 | - (void)sendCommand:(NSString *)command; 48 | - (void)quit; 49 | - (void)launch; 50 | void SigPipeHandler(int s); 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /source/Classes/TRPianobarManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRPianobarManager.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/13/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import "TRPianobarManager.h" 10 | #import 11 | 12 | // Notifications 13 | NSString * const TransistorSelectStationNotification = @"TransistorSelectStationNotification"; 14 | 15 | 16 | static TRPianobarManager *sharedPianobarManager = nil; 17 | 18 | @interface TRPianobarManager () 19 | 20 | - (void)parseEventInfo:(NSString *)info; 21 | - (void)processOutput:(NSString *)output; 22 | - (void)handleSongStartEvent:(NSNotification *)notification; 23 | 24 | @end 25 | 26 | 27 | @implementation TRPianobarManager 28 | 29 | @synthesize currentArtist, currentSong, currentAlbum, currentTime, currentArtworkURL, stationList; 30 | @synthesize username, password; 31 | 32 | - (id)init 33 | { 34 | if ((self = [super init])) 35 | { 36 | // Notification data available from stdout 37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(outputAvailable:) name:NSFileHandleReadCompletionNotification object:nil]; 38 | 39 | // Notification for data from TransistorHelper via pianobar event_command 40 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSongStartEvent:) name:@"TransistorSongStartEventNotification" object:nil]; 41 | 42 | // Info about current song 43 | currentArtist = @""; 44 | currentSong = @""; 45 | currentAlbum = @""; 46 | currentTime = @""; 47 | stationList = @""; 48 | stationsStarted = NO; 49 | 50 | // Basic plumbing for communicating with the pianobar process 51 | /*outputPipe = [[NSPipe pipe] retain]; 52 | inputPipe = [[NSPipe pipe] retain]; 53 | readHandle = [outputPipe fileHandleForReading]; 54 | writeHandle = [inputPipe fileHandleForWriting]; 55 | 56 | pianobar = [[NSTask alloc] init]; 57 | 58 | // TODO: make path customizable 59 | [pianobar setLaunchPath:@"/usr/local/bin/pianobar"]; 60 | [pianobar setStandardOutput:outputPipe]; 61 | [pianobar setStandardInput:inputPipe]; 62 | [pianobar setStandardError:outputPipe]; 63 | 64 | // get data asynchronously and notify when available 65 | [readHandle readInBackgroundAndNotify]; 66 | 67 | [pianobar launch];*/ 68 | } 69 | 70 | return self; 71 | } 72 | 73 | - (void)launch 74 | { 75 | // Basic plumbing for communicating with the pianobar process 76 | outputPipe = [[NSPipe pipe] retain]; 77 | inputPipe = [[NSPipe pipe] retain]; 78 | readHandle = [outputPipe fileHandleForReading]; 79 | writeHandle = [inputPipe fileHandleForWriting]; 80 | 81 | pianobar = [[NSTask alloc] init]; 82 | 83 | // TODO: make path customizable 84 | [pianobar setLaunchPath:@"/usr/local/bin/pianobar"]; 85 | [pianobar setStandardOutput:outputPipe]; 86 | [pianobar setStandardInput:inputPipe]; 87 | [pianobar setStandardError:outputPipe]; 88 | 89 | // get data asynchronously and notify when available 90 | [readHandle readInBackgroundAndNotify]; 91 | 92 | [pianobar launch]; 93 | } 94 | 95 | 96 | // Quit pianobar process 97 | - (void)quit 98 | { 99 | NSLog(@"quitting pianobar"); 100 | 101 | if ([pianobar isRunning]) 102 | { 103 | [self sendCommand:QUIT]; 104 | [pianobar terminate]; 105 | } 106 | 107 | [pianobar release]; 108 | pianobar = nil; 109 | } 110 | 111 | 112 | // Sends a command to pianobar via stdin 113 | - (void)sendCommand:(NSString *)command 114 | { 115 | NSLog(@"pianobar: sendCommand: %@", command); 116 | // http://stackoverflow.com/questions/1294436/how-to-catch-sigpipe-in-iphone-app 117 | signal(SIGPIPE, SigPipeHandler); 118 | [writeHandle writeData:[[NSString stringWithFormat:@"%@\n", command] dataUsingEncoding:NSUTF8StringEncoding]]; 119 | } 120 | 121 | void SigPipeHandler(int s) 122 | { 123 | // do your handling 124 | } 125 | 126 | 127 | // Notification when output is available from pianobar 128 | - (void)outputAvailable: (NSNotification *)notification 129 | { 130 | NSData *data = [[notification userInfo] objectForKey:@"NSFileHandleNotificationDataItem"]; 131 | 132 | if ([data length]) 133 | { 134 | [self processOutput:[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]]; 135 | 136 | [readHandle readInBackgroundAndNotify]; 137 | } 138 | } 139 | 140 | 141 | // Take all the output and figure out what to do with it 142 | - (void)processOutput:(NSString *)output 143 | { 144 | NSLog(@"raw output:\n%@", output); 145 | 146 | // Remove whitespace and newlines from output as well as the character pianobar starts every line with 147 | NSString *cleaned = [[[output stringByReplacingOccurrencesOfString:@"\033[2K" withString:@""] stringByReplacingOccurrencesOfString:@"\t" withString:@""] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 148 | 149 | // Concatenate station list output 150 | if (stationsStarted) 151 | { 152 | self.stationList = [self.stationList stringByAppendingFormat:@"%@\n", cleaned]; 153 | } 154 | 155 | // Time lines are prefixed with # 156 | if ([cleaned hasPrefix:@"#"]) 157 | { 158 | self.currentTime = [cleaned stringByReplacingOccurrencesOfString:@"# " withString:@""]; 159 | } 160 | else if ([cleaned rangeOfString:@"Username"].location != NSNotFound) 161 | { 162 | // Not using at the moment, username should be in ~/.config/pianobar/config 163 | [self sendCommand:username]; 164 | } 165 | else if ([cleaned rangeOfString:@"Password"].location != NSNotFound) 166 | { 167 | // Not using at the moment, password should be in ~/.config/pianobar/config 168 | [self sendCommand:password]; 169 | } 170 | else if ([cleaned rangeOfString:@"Get stations"].location != NSNotFound) 171 | { 172 | // Start of station list output 173 | stationsStarted = YES; 174 | } 175 | else if ([cleaned rangeOfString:@"Select station"].location != NSNotFound) 176 | { 177 | // End of station list output, prompt for selecting station 178 | stationsStarted = NO; 179 | 180 | // A little more specific cleanup 181 | self.stationList = [self.stationList stringByReplacingOccurrencesOfString:@"Ok.\n" withString:@""]; 182 | self.stationList = [self.stationList stringByReplacingOccurrencesOfString:@"\n[?] Select station:\n" withString:@""]; 183 | 184 | // Notification that we need to choose a station 185 | [[NSNotificationCenter defaultCenter] postNotificationName:TransistorSelectStationNotification object:self userInfo:[NSDictionary dictionaryWithObject:self.stationList forKey:@"stations"]]; 186 | } 187 | } 188 | 189 | 190 | 191 | #pragma mark - 192 | #pragma mark NSDistributedNotification - communication with helper app 193 | 194 | // Only handling a single event, when a new song starts 195 | - (void)handleSongStartEvent:(NSNotification *)notification 196 | { 197 | [self parseEventInfo:[notification object]]; 198 | } 199 | 200 | 201 | // Data passed from helper app is one string of name = value pairs separated by newlines 202 | // Parse into a dictionary so we can get the info we need out easier 203 | - (void)parseEventInfo:(NSString *)info 204 | { 205 | NSLog(@"event info:%@", info); 206 | NSMutableDictionary *data = [NSMutableDictionary dictionary]; 207 | NSArray *lines = [[info stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsSeparatedByString:@"\n"]; 208 | 209 | for (NSString *line in lines) 210 | { 211 | NSArray *keyValue = [line componentsSeparatedByString:@"="]; 212 | [data setObject:[keyValue objectAtIndex:1] forKey:[keyValue objectAtIndex:0]]; 213 | } 214 | 215 | // Use accessors to ensure KVO notifications are sent 216 | self.currentArtist = [data objectForKey:@"artist"]; 217 | self.currentSong = [data objectForKey:@"title"]; 218 | self.currentAlbum = [data objectForKey:@"album"]; 219 | self.currentArtworkURL = [NSURL URLWithString:[data objectForKey:@"coverArt"]]; 220 | } 221 | 222 | 223 | #pragma mark - 224 | #pragma mark Default Apple Singleton code 225 | 226 | + (TRPianobarManager *)sharedManager 227 | { 228 | if (sharedPianobarManager == nil) 229 | { 230 | sharedPianobarManager = [[super allocWithZone:NULL] init]; 231 | } 232 | 233 | return sharedPianobarManager; 234 | } 235 | 236 | 237 | + (id)allocWithZone:(NSZone *)zone 238 | { 239 | return [[self sharedManager] retain]; 240 | } 241 | 242 | 243 | - (id)copyWithZone:(NSZone *)zone 244 | { 245 | return self; 246 | } 247 | 248 | 249 | - (id)retain 250 | { 251 | return self; 252 | } 253 | 254 | 255 | - (NSUInteger)retainCount 256 | { 257 | return NSUIntegerMax; //denotes an object that cannot be released 258 | } 259 | 260 | 261 | - (void)release 262 | { 263 | //do nothing 264 | } 265 | 266 | 267 | - (id)autorelease 268 | { 269 | return self; 270 | } 271 | 272 | @end 273 | -------------------------------------------------------------------------------- /source/Classes/TransistorHelper.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main (int argc, const char * argv[]) 4 | { 5 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 6 | 7 | if (argc > 1) 8 | { 9 | NSString *event = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding]; 10 | NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput]; 11 | NSData *data = [input readDataToEndOfFile]; 12 | 13 | NSString *info = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; 14 | 15 | //NSLog(@"event: %@, info:\n%@", event, info); 16 | 17 | // Only handling songstart event at the moment 18 | if ([event isEqualToString:@"songstart"]) 19 | { 20 | [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"TransistorSongStartEventNotification" object:info]; 21 | } 22 | } 23 | 24 | [pool drain]; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /source/Transistor_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Transistor' target in the 'Panda' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /source/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Transistor 4 | // 5 | // Created by Zach Waugh on 2/8/11. 6 | // Copyright 2011 Giant Comet. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | --------------------------------------------------------------------------------