├── icon.png ├── icons ├── active.png ├── airpods.png └── inactive.png ├── images ├── screenshot.png ├── setup-screen-1.png └── setup-screen-2.png ├── get_current_input.py ├── get_current_output.py ├── set_input_source.py ├── set_output_source.py ├── list_sources.py ├── .github └── workflows │ └── release.yml ├── README.md ├── SwitchAudioSource.py ├── .gitignore └── info.plist /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/icon.png -------------------------------------------------------------------------------- /icons/active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/icons/active.png -------------------------------------------------------------------------------- /icons/airpods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/icons/airpods.png -------------------------------------------------------------------------------- /icons/inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/icons/inactive.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /images/setup-screen-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/images/setup-screen-1.png -------------------------------------------------------------------------------- /images/setup-screen-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexlafroscia/alfred-switch-audio-source/HEAD/images/setup-screen-2.png -------------------------------------------------------------------------------- /get_current_input.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from SwitchAudioSource import get_current_input 4 | 5 | get_current_input() 6 | -------------------------------------------------------------------------------- /get_current_output.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from SwitchAudioSource import get_current_output 4 | 5 | get_current_output() 6 | -------------------------------------------------------------------------------- /set_input_source.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import argv 4 | from SwitchAudioSource import set_input 5 | 6 | query = argv[1] 7 | set_input(query) 8 | -------------------------------------------------------------------------------- /set_output_source.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import argv 4 | from SwitchAudioSource import set_output 5 | 6 | query = argv[1] 7 | set_output(query) 8 | -------------------------------------------------------------------------------- /list_sources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import stdout, argv 4 | from json import dumps 5 | from SwitchAudioSource import get_sources 6 | 7 | 8 | output_items = filter(lambda source: source.type == argv[1], get_sources()) 9 | items = map(lambda source: source.__dict__, output_items) 10 | 11 | json_output = dumps({ 12 | "items": items 13 | }) 14 | 15 | stdout.write(json_output) 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Alfred Workflow 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Build Alfred Workflow 14 | id: alfred_builder 15 | uses: mperezi/build-alfred-workflow@v1 16 | with: 17 | workflow_dir: . 18 | exclude_patterns: '*.pyc *__pycache__/* *.git*' 19 | - name: Create Release 20 | id: create_release 21 | uses: actions/create-release@v1 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | with: 25 | tag_name: ${{ github.ref }} 26 | release_name: ${{ github.ref }} 27 | draft: false 28 | prerelease: false 29 | - name: Upload Alfred Workflow 30 | uses: actions/upload-release-asset@v1 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | with: 34 | upload_url: ${{ steps.create_release.outputs.upload_url }} 35 | asset_path: ${{ steps.alfred_builder.outputs.workflow_file }} 36 | asset_name: ${{ steps.alfred_builder.outputs.workflow_file }} 37 | asset_content_type: application/zip 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alfred Switch Audio 2 | 3 | An [Alfred][alfred] front-end for [`switchaudio-osx`][switchaudio-osx] 4 | 5 | ## Installation 6 | 7 | 1. Download the [latest version of the workflow][download-link] 8 | 2. Install [`switchaudio-osx`][switchaudio-osx], probably through [Homebrew][homebrew] 9 | 10 | ```bash 11 | brew install switchaudio-osx 12 | ``` 13 | 14 | 3. Take note of where the program was installed 15 | 16 | ```bash 17 | which SwitchAudioSource 18 | ``` 19 | 20 | 4. Configure the environment variable in the Alfred plugin to point to that path 21 | 22 | a. Open your Alfred preferences and select this plugin 23 | 24 | ![Plugin Selection](./images/setup-screen-1.png) 25 | 26 | b. Open the Configuration panel highlighted in the image below 27 | c. Change the _Workflow Environment Variable_ to match the value found in the previous step 28 | 29 | ![Plugin Configuration](./images/setup-screen-2.png) 30 | 31 | ## Usage 32 | 33 | This plugin provides an `audio out` and `audio in` command that will list the available devices. Select one of them to make that the active audio output device. 34 | 35 | ![Workflow Screenshot](./images/screenshot.png) 36 | 37 | The additional command `toggle audio output` will toggle between your last audio output and your current one. 38 | Both commands have the ability to add a hotkey for quick access. 39 | 40 | [alfred]: https://www.alfredapp.com 41 | [switchaudio-osx]: https://github.com/deweller/switchaudio-osx 42 | [download-link]: https://github.com/alexlafroscia/alfred-switch-audio-source/releases/latest/download/Switch.Audio.alfredworkflow 43 | [homebrew]: http://brew.sh 44 | -------------------------------------------------------------------------------- /SwitchAudioSource.py: -------------------------------------------------------------------------------- 1 | from subprocess import check_output 2 | from json import dumps, loads 3 | from sys import stdout 4 | from os import environ 5 | 6 | 7 | PATH_TO_SWITCH_AUDIO= environ['SWITCH_AUDIO_SOURCE_PATH'] 8 | LOOKUP_WARNING = "Error: Could not find SwitchAudioSource" 9 | 10 | 11 | class AudioSource: 12 | def __init__(self, description, active_output, active_input): 13 | audioSourceJSON = loads(description) 14 | title = audioSourceJSON["name"] 15 | self.uid = audioSourceJSON["uid"] 16 | self.arg = audioSourceJSON["id"] 17 | self.title = title 18 | self.autocomplete = title 19 | self.type = audioSourceJSON["type"] 20 | 21 | if self.type == "output": 22 | active = active_output 23 | else: 24 | active = active_input 25 | 26 | self.icon = {"path": "icons/active.png" if active == 27 | title else "icons/inactive.png"} 28 | 29 | def __str__(self): 30 | return str(self.__dict__) 31 | 32 | 33 | def get_sources(): 34 | active_output = check_output([ 35 | PATH_TO_SWITCH_AUDIO, '-c', '-t' 'output' 36 | ]).strip() 37 | 38 | active_input = check_output([ 39 | PATH_TO_SWITCH_AUDIO, '-c', '-t' 'input' 40 | ]).strip() 41 | 42 | command_output = check_output([ 43 | PATH_TO_SWITCH_AUDIO, '-a', '-f', 'json' 44 | ]) 45 | 46 | return map(lambda line: AudioSource(line, active_output, active_input), command_output.splitlines()) 47 | 48 | 49 | def get_current_output(): 50 | command_output = check_output([ 51 | PATH_TO_SWITCH_AUDIO, '-c', '-t', 'output', '-f', 'json' # being explicit, but should default to `-t output` 52 | ]).replace("\n", "") 53 | stdout.write(loads(command_output)["id"]) 54 | 55 | def get_current_input(): 56 | command_output = check_output([ 57 | PATH_TO_SWITCH_AUDIO, '-c', '-t', 'input', '-f', 'json' 58 | ]).replace("\n", "") 59 | stdout.write(loads(command_output)["id"]) 60 | 61 | def set_input(device): 62 | command_output = check_output([ 63 | PATH_TO_SWITCH_AUDIO, '-i', device, '-t', 'input' 64 | ]).capitalize() 65 | stdout.write(command_output) 66 | 67 | 68 | def set_output(device): 69 | check_output([ 70 | PATH_TO_SWITCH_AUDIO, '-i', device, '-t', 'output' 71 | ]) 72 | stdout.write(device) 73 | 74 | 75 | def no_path_provided(): 76 | stdout.write(dumps({ 77 | "items": [{ 78 | "title": LOOKUP_WARNING 79 | }] 80 | })) 81 | exit() 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ropeproject/ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ 141 | -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.alexlafroscia.alfred-switch-audio 7 | connections 8 | 9 | 01C1D384-588A-4E64-B466-664C0D6ADE55 10 | 11 | 12 | destinationuid 13 | 55E5F39C-074E-44D4-A590-B8388F3005B2 14 | modifiers 15 | 0 16 | modifiersubtext 17 | 18 | vitoclose 19 | 20 | 21 | 22 | 24202296-CD21-4A75-B283-D6FB3C02BEE0 23 | 24 | 25 | destinationuid 26 | 3A6F07FF-03D6-4253-AC52-4FBB09A77B8E 27 | modifiers 28 | 0 29 | modifiersubtext 30 | 31 | vitoclose 32 | 33 | 34 | 35 | 250412F0-CF46-4DA1-B4F4-A8E377270C06 36 | 37 | 38 | destinationuid 39 | 806CE116-4364-45F7-8273-D597083F1BA3 40 | modifiers 41 | 0 42 | modifiersubtext 43 | 44 | vitoclose 45 | 46 | 47 | 48 | 2ACBC07C-367B-45D2-AAAF-0BF58336AD2E 49 | 50 | 51 | destinationuid 52 | B7BF0FED-2EB3-41B4-A004-0BED5C4753D3 53 | modifiers 54 | 0 55 | modifiersubtext 56 | 57 | vitoclose 58 | 59 | 60 | 61 | 3A6F07FF-03D6-4253-AC52-4FBB09A77B8E 62 | 63 | 64 | destinationuid 65 | 9B0A7504-520C-4429-A16C-B7DDB416FFE0 66 | modifiers 67 | 0 68 | modifiersubtext 69 | 70 | vitoclose 71 | 72 | 73 | 74 | 3C0FE678-9CDF-4E1F-AD87-D5E4CACDCDA5 75 | 76 | 77 | destinationuid 78 | 55E5F39C-074E-44D4-A590-B8388F3005B2 79 | modifiers 80 | 0 81 | modifiersubtext 82 | 83 | vitoclose 84 | 85 | 86 | 87 | 55E5F39C-074E-44D4-A590-B8388F3005B2 88 | 89 | 90 | destinationuid 91 | B7BF0FED-2EB3-41B4-A004-0BED5C4753D3 92 | modifiers 93 | 0 94 | modifiersubtext 95 | 96 | vitoclose 97 | 98 | 99 | 100 | 57D701DA-B28B-4DEC-9791-119AD96662D4 101 | 102 | 103 | destinationuid 104 | 24202296-CD21-4A75-B283-D6FB3C02BEE0 105 | modifiers 106 | 0 107 | modifiersubtext 108 | 109 | vitoclose 110 | 111 | 112 | 113 | 64A4FE4B-39EF-4C01-A1AF-AC8A03820354 114 | 115 | 116 | destinationuid 117 | 6C7977A3-08B6-40D5-9978-E48D89B78165 118 | modifiers 119 | 0 120 | modifiersubtext 121 | 122 | vitoclose 123 | 124 | 125 | 126 | 69B4002C-BCE5-4A9D-A0B9-622198D9A856 127 | 128 | 129 | destinationuid 130 | 2ACBC07C-367B-45D2-AAAF-0BF58336AD2E 131 | modifiers 132 | 0 133 | modifiersubtext 134 | 135 | vitoclose 136 | 137 | 138 | 139 | 6C7977A3-08B6-40D5-9978-E48D89B78165 140 | 141 | 142 | destinationuid 143 | 57D701DA-B28B-4DEC-9791-119AD96662D4 144 | modifiers 145 | 0 146 | modifiersubtext 147 | 148 | vitoclose 149 | 150 | 151 | 152 | 806CE116-4364-45F7-8273-D597083F1BA3 153 | 154 | 155 | destinationuid 156 | 24202296-CD21-4A75-B283-D6FB3C02BEE0 157 | modifiers 158 | 0 159 | modifiersubtext 160 | 161 | vitoclose 162 | 163 | 164 | 165 | 9029507C-1509-4DC4-85FD-809669989074 166 | 167 | 168 | destinationuid 169 | D92CEBA5-F3C7-42D9-9A06-E62F7CECD5FA 170 | modifiers 171 | 0 172 | modifiersubtext 173 | 174 | vitoclose 175 | 176 | 177 | 178 | 9B0A7504-520C-4429-A16C-B7DDB416FFE0 179 | 180 | 181 | destinationuid 182 | EBADB9C0-4E7A-4715-B940-D34A495F5917 183 | modifiers 184 | 0 185 | modifiersubtext 186 | 187 | vitoclose 188 | 189 | 190 | 191 | B7BF0FED-2EB3-41B4-A004-0BED5C4753D3 192 | 193 | 194 | destinationuid 195 | 9029507C-1509-4DC4-85FD-809669989074 196 | modifiers 197 | 0 198 | modifiersubtext 199 | 200 | vitoclose 201 | 202 | 203 | 204 | D53AC138-E101-47F0-9C68-3E67A02A3E09 205 | 206 | 207 | destinationuid 208 | 806CE116-4364-45F7-8273-D597083F1BA3 209 | modifiers 210 | 0 211 | modifiersubtext 212 | 213 | vitoclose 214 | 215 | 216 | 217 | D92CEBA5-F3C7-42D9-9A06-E62F7CECD5FA 218 | 219 | 220 | destinationuid 221 | ED6B7E79-8B79-4DE6-9BCD-8D7BE2EDA3AF 222 | modifiers 223 | 0 224 | modifiersubtext 225 | 226 | vitoclose 227 | 228 | 229 | 230 | E5B88B4F-44DD-4DA4-9E55-D8A2183C63D5 231 | 232 | 233 | destinationuid 234 | 69B4002C-BCE5-4A9D-A0B9-622198D9A856 235 | modifiers 236 | 0 237 | modifiersubtext 238 | 239 | vitoclose 240 | 241 | 242 | 243 | EBADB9C0-4E7A-4715-B940-D34A495F5917 244 | 245 | 246 | destinationuid 247 | 85E6E113-106D-4CBE-9E06-7487AAD98C38 248 | modifiers 249 | 0 250 | modifiersubtext 251 | 252 | vitoclose 253 | 254 | 255 | 256 | ED6B7E79-8B79-4DE6-9BCD-8D7BE2EDA3AF 257 | 258 | 259 | destinationuid 260 | 6EC1992B-3103-4619-90C8-339F817E7BD6 261 | modifiers 262 | 0 263 | modifiersubtext 264 | 265 | vitoclose 266 | 267 | 268 | 269 | 270 | createdby 271 | Alex LaFroscia 272 | description 273 | Change active audio device 274 | disabled 275 | 276 | name 277 | Switch Audio 278 | objects 279 | 280 | 281 | config 282 | 283 | action 284 | 0 285 | argument 286 | 0 287 | focusedappvariable 288 | 289 | focusedappvariablename 290 | 291 | hotkey 292 | 0 293 | hotmod 294 | 0 295 | hotstring 296 | 297 | leftcursor 298 | 299 | modsmode 300 | 0 301 | relatedAppsMode 302 | 0 303 | 304 | type 305 | alfred.workflow.trigger.hotkey 306 | uid 307 | E5B88B4F-44DD-4DA4-9E55-D8A2183C63D5 308 | version 309 | 2 310 | 311 | 312 | config 313 | 314 | alfredfiltersresults 315 | 316 | alfredfiltersresultsmatchmode 317 | 0 318 | argumenttreatemptyqueryasnil 319 | 320 | argumenttrimmode 321 | 0 322 | argumenttype 323 | 1 324 | escaping 325 | 68 326 | keyword 327 | audio out 328 | queuedelaycustom 329 | 3 330 | queuedelayimmediatelyinitially 331 | 332 | queuedelaymode 333 | 0 334 | queuemode 335 | 1 336 | runningsubtext 337 | 338 | script 339 | #!/bin/bash 340 | 341 | ./list_sources.py output 342 | scriptargtype 343 | 1 344 | scriptfile 345 | ./list_sources.py 346 | subtext 347 | 348 | title 349 | Change the audio output device 350 | type 351 | 0 352 | withspace 353 | 354 | 355 | type 356 | alfred.workflow.input.scriptfilter 357 | uid 358 | 69B4002C-BCE5-4A9D-A0B9-622198D9A856 359 | version 360 | 3 361 | 362 | 363 | config 364 | 365 | argument 366 | {query} 367 | passthroughargument 368 | 369 | variables 370 | 371 | NEXT_OUTPUT 372 | {query} 373 | 374 | 375 | type 376 | alfred.workflow.utility.argument 377 | uid 378 | 2ACBC07C-367B-45D2-AAAF-0BF58336AD2E 379 | version 380 | 1 381 | 382 | 383 | config 384 | 385 | concurrently 386 | 387 | escaping 388 | 0 389 | script 390 | 391 | scriptargtype 392 | 1 393 | scriptfile 394 | ./set_output_source.py 395 | type 396 | 8 397 | 398 | type 399 | alfred.workflow.action.script 400 | uid 401 | ED6B7E79-8B79-4DE6-9BCD-8D7BE2EDA3AF 402 | version 403 | 2 404 | 405 | 406 | config 407 | 408 | concurrently 409 | 410 | escaping 411 | 68 412 | script 413 | function run(argv) { 414 | // Sanity Checks 415 | //if (!Array.isArray(argv) && !argv.length) return argv; 416 | //if (typeof argv[0] !== 'string') return argv; 417 | 418 | var device = argv[0]; 419 | ObjC.import('stdlib'); 420 | Application('com.runningwithcrayons.Alfred').setConfiguration('LAST_OUTPUT', { 421 | toValue: device, 422 | inWorkflow: $.getenv('alfred_workflow_bundleid'), 423 | exportable: true 424 | }); 425 | 426 | return device; 427 | } 428 | scriptargtype 429 | 1 430 | scriptfile 431 | 432 | type 433 | 7 434 | 435 | type 436 | alfred.workflow.action.script 437 | uid 438 | 9029507C-1509-4DC4-85FD-809669989074 439 | version 440 | 2 441 | 442 | 443 | config 444 | 445 | concurrently 446 | 447 | escaping 448 | 0 449 | script 450 | 451 | scriptargtype 452 | 1 453 | scriptfile 454 | ./get_current_output.py 455 | type 456 | 8 457 | 458 | type 459 | alfred.workflow.action.script 460 | uid 461 | B7BF0FED-2EB3-41B4-A004-0BED5C4753D3 462 | version 463 | 2 464 | 465 | 466 | config 467 | 468 | argument 469 | {var:NEXT_OUTPUT} 470 | passthroughargument 471 | 472 | variables 473 | 474 | 475 | type 476 | alfred.workflow.utility.argument 477 | uid 478 | D92CEBA5-F3C7-42D9-9A06-E62F7CECD5FA 479 | version 480 | 1 481 | 482 | 483 | config 484 | 485 | action 486 | 0 487 | argument 488 | 0 489 | focusedappvariable 490 | 491 | focusedappvariablename 492 | 493 | hotkey 494 | 0 495 | hotmod 496 | 0 497 | hotstring 498 | 499 | leftcursor 500 | 501 | modsmode 502 | 0 503 | relatedAppsMode 504 | 0 505 | 506 | type 507 | alfred.workflow.trigger.hotkey 508 | uid 509 | 3C0FE678-9CDF-4E1F-AD87-D5E4CACDCDA5 510 | version 511 | 2 512 | 513 | 514 | config 515 | 516 | argument 517 | {query} 518 | passthroughargument 519 | 520 | variables 521 | 522 | NEXT_OUTPUT 523 | {var:LAST_OUTPUT} 524 | 525 | 526 | type 527 | alfred.workflow.utility.argument 528 | uid 529 | 55E5F39C-074E-44D4-A590-B8388F3005B2 530 | version 531 | 1 532 | 533 | 534 | config 535 | 536 | lastpathcomponent 537 | 538 | onlyshowifquerypopulated 539 | 540 | removeextension 541 | 542 | text 543 | Output audio device set to "{query}" 544 | title 545 | Audio Output Device Changed 546 | 547 | type 548 | alfred.workflow.output.notification 549 | uid 550 | 6EC1992B-3103-4619-90C8-339F817E7BD6 551 | version 552 | 1 553 | 554 | 555 | config 556 | 557 | argumenttype 558 | 2 559 | keyword 560 | toggle audio output 561 | subtext 562 | 563 | text 564 | Toggle last audio output 565 | withspace 566 | 567 | 568 | type 569 | alfred.workflow.input.keyword 570 | uid 571 | 01C1D384-588A-4E64-B466-664C0D6ADE55 572 | version 573 | 1 574 | 575 | 576 | config 577 | 578 | alfredfiltersresults 579 | 580 | alfredfiltersresultsmatchmode 581 | 0 582 | argumenttreatemptyqueryasnil 583 | 584 | argumenttrimmode 585 | 0 586 | argumenttype 587 | 1 588 | escaping 589 | 68 590 | keyword 591 | audio in 592 | queuedelaycustom 593 | 3 594 | queuedelayimmediatelyinitially 595 | 596 | queuedelaymode 597 | 0 598 | queuemode 599 | 1 600 | runningsubtext 601 | 602 | script 603 | #!/bin/bash 604 | 605 | ./list_sources.py input 606 | scriptargtype 607 | 1 608 | scriptfile 609 | ./list_sources.py 610 | subtext 611 | 612 | title 613 | Change the audio input device 614 | type 615 | 0 616 | withspace 617 | 618 | 619 | type 620 | alfred.workflow.input.scriptfilter 621 | uid 622 | 6C7977A3-08B6-40D5-9978-E48D89B78165 623 | version 624 | 3 625 | 626 | 627 | config 628 | 629 | action 630 | 0 631 | argument 632 | 0 633 | focusedappvariable 634 | 635 | focusedappvariablename 636 | 637 | hotkey 638 | 0 639 | hotmod 640 | 0 641 | hotstring 642 | 643 | leftcursor 644 | 645 | modsmode 646 | 0 647 | relatedAppsMode 648 | 0 649 | 650 | type 651 | alfred.workflow.trigger.hotkey 652 | uid 653 | 64A4FE4B-39EF-4C01-A1AF-AC8A03820354 654 | version 655 | 2 656 | 657 | 658 | config 659 | 660 | argument 661 | {query} 662 | passthroughargument 663 | 664 | variables 665 | 666 | NEXT_INPUT 667 | {query} 668 | 669 | 670 | type 671 | alfred.workflow.utility.argument 672 | uid 673 | 57D701DA-B28B-4DEC-9791-119AD96662D4 674 | version 675 | 1 676 | 677 | 678 | config 679 | 680 | concurrently 681 | 682 | escaping 683 | 0 684 | script 685 | 686 | scriptargtype 687 | 1 688 | scriptfile 689 | ./get_current_input.py 690 | type 691 | 8 692 | 693 | type 694 | alfred.workflow.action.script 695 | uid 696 | 24202296-CD21-4A75-B283-D6FB3C02BEE0 697 | version 698 | 2 699 | 700 | 701 | config 702 | 703 | concurrently 704 | 705 | escaping 706 | 0 707 | script 708 | 709 | scriptargtype 710 | 1 711 | scriptfile 712 | ./set_input_source.py 713 | type 714 | 8 715 | 716 | type 717 | alfred.workflow.action.script 718 | uid 719 | EBADB9C0-4E7A-4715-B940-D34A495F5917 720 | version 721 | 2 722 | 723 | 724 | config 725 | 726 | concurrently 727 | 728 | escaping 729 | 68 730 | script 731 | function run(argv) { 732 | // Sanity Checks 733 | if (!Array.isArray(argv) && !argv.length) return argv; 734 | if (typeof argv[0] !== 'string') return argv; 735 | 736 | var device = argv[0]; 737 | ObjC.import('stdlib'); 738 | Application('com.runningwithcrayons.Alfred').setConfiguration('LAST_INPUT', { 739 | toValue: device, 740 | inWorkflow: $.getenv('alfred_workflow_bundleid'), 741 | exportable: true 742 | }); 743 | 744 | return device; 745 | } 746 | scriptargtype 747 | 1 748 | scriptfile 749 | 750 | type 751 | 7 752 | 753 | type 754 | alfred.workflow.action.script 755 | uid 756 | 3A6F07FF-03D6-4253-AC52-4FBB09A77B8E 757 | version 758 | 2 759 | 760 | 761 | config 762 | 763 | argument 764 | {var:NEXT_INPUT} 765 | passthroughargument 766 | 767 | variables 768 | 769 | 770 | type 771 | alfred.workflow.utility.argument 772 | uid 773 | 9B0A7504-520C-4429-A16C-B7DDB416FFE0 774 | version 775 | 1 776 | 777 | 778 | config 779 | 780 | action 781 | 0 782 | argument 783 | 0 784 | focusedappvariable 785 | 786 | focusedappvariablename 787 | 788 | hotkey 789 | 0 790 | hotmod 791 | 0 792 | leftcursor 793 | 794 | modsmode 795 | 0 796 | relatedAppsMode 797 | 0 798 | 799 | type 800 | alfred.workflow.trigger.hotkey 801 | uid 802 | D53AC138-E101-47F0-9C68-3E67A02A3E09 803 | version 804 | 2 805 | 806 | 807 | config 808 | 809 | argument 810 | {query} 811 | passthroughargument 812 | 813 | variables 814 | 815 | NEXT_INPUT 816 | {var:LAST_INPUT} 817 | 818 | 819 | type 820 | alfred.workflow.utility.argument 821 | uid 822 | 806CE116-4364-45F7-8273-D597083F1BA3 823 | version 824 | 1 825 | 826 | 827 | config 828 | 829 | lastpathcomponent 830 | 831 | onlyshowifquerypopulated 832 | 833 | removeextension 834 | 835 | text 836 | Input audio device set to "{query}" 837 | title 838 | Audio Input Device Changed 839 | 840 | type 841 | alfred.workflow.output.notification 842 | uid 843 | 85E6E113-106D-4CBE-9E06-7487AAD98C38 844 | version 845 | 1 846 | 847 | 848 | config 849 | 850 | argumenttype 851 | 2 852 | keyword 853 | toggle audio input 854 | subtext 855 | 856 | text 857 | Toggle last audio input 858 | withspace 859 | 860 | 861 | type 862 | alfred.workflow.input.keyword 863 | uid 864 | 250412F0-CF46-4DA1-B4F4-A8E377270C06 865 | version 866 | 1 867 | 868 | 869 | readme 870 | Control audi output device through SwitchAudioSource 871 | 872 | https://github.com/deweller/switchaudio-osx 873 | 874 | It can easily be installed through Homebrew. 875 | 876 | ```bash 877 | brew install switchaudio-osx 878 | ``` 879 | 880 | Once installed, make sure that the "Environment Variable" that points to the executable is set correctly for this script. 881 | uidata 882 | 883 | 01C1D384-588A-4E64-B466-664C0D6ADE55 884 | 885 | note 886 | Keyword for toggle last audio output 887 | xpos 888 | 20 889 | ypos 890 | 375 891 | 892 | 24202296-CD21-4A75-B283-D6FB3C02BEE0 893 | 894 | note 895 | Get current device 896 | xpos 897 | 470 898 | ypos 899 | 620 900 | 901 | 250412F0-CF46-4DA1-B4F4-A8E377270C06 902 | 903 | note 904 | Keyword for toggle last audio input 905 | xpos 906 | 20 907 | ypos 908 | 870 909 | 910 | 2ACBC07C-367B-45D2-AAAF-0BF58336AD2E 911 | 912 | note 913 | Save next output 914 | xpos 915 | 350 916 | ypos 917 | 60 918 | 919 | 3A6F07FF-03D6-4253-AC52-4FBB09A77B8E 920 | 921 | note 922 | Save current device 923 | xpos 924 | 615 925 | ypos 926 | 620 927 | 928 | 3C0FE678-9CDF-4E1F-AD87-D5E4CACDCDA5 929 | 930 | note 931 | Toggle last output 932 | xpos 933 | 20 934 | ypos 935 | 225 936 | 937 | 55E5F39C-074E-44D4-A590-B8388F3005B2 938 | 939 | note 940 | Save next output 941 | xpos 942 | 350 943 | ypos 944 | 255 945 | 946 | 57D701DA-B28B-4DEC-9791-119AD96662D4 947 | 948 | note 949 | Save next input 950 | xpos 951 | 350 952 | ypos 953 | 555 954 | 955 | 64A4FE4B-39EF-4C01-A1AF-AC8A03820354 956 | 957 | note 958 | Trigger list of devices 959 | xpos 960 | 20 961 | ypos 962 | 525 963 | 964 | 69B4002C-BCE5-4A9D-A0B9-622198D9A856 965 | 966 | note 967 | List output devices 968 | xpos 969 | 180 970 | ypos 971 | 30 972 | 973 | 6C7977A3-08B6-40D5-9978-E48D89B78165 974 | 975 | note 976 | List input devices 977 | xpos 978 | 180 979 | ypos 980 | 525 981 | 982 | 6EC1992B-3103-4619-90C8-339F817E7BD6 983 | 984 | colorindex 985 | 1 986 | xpos 987 | 875 988 | ypos 989 | 280 990 | 991 | 806CE116-4364-45F7-8273-D597083F1BA3 992 | 993 | note 994 | Save next input 995 | xpos 996 | 350 997 | ypos 998 | 750 999 | 1000 | 85E6E113-106D-4CBE-9E06-7487AAD98C38 1001 | 1002 | colorindex 1003 | 1 1004 | xpos 1005 | 875 1006 | ypos 1007 | 775 1008 | 1009 | 9029507C-1509-4DC4-85FD-809669989074 1010 | 1011 | note 1012 | Save current device 1013 | xpos 1014 | 615 1015 | ypos 1016 | 125 1017 | 1018 | 9B0A7504-520C-4429-A16C-B7DDB416FFE0 1019 | 1020 | note 1021 | Pass next input 1022 | xpos 1023 | 780 1024 | ypos 1025 | 650 1026 | 1027 | B7BF0FED-2EB3-41B4-A004-0BED5C4753D3 1028 | 1029 | note 1030 | Get current device 1031 | xpos 1032 | 470 1033 | ypos 1034 | 125 1035 | 1036 | D53AC138-E101-47F0-9C68-3E67A02A3E09 1037 | 1038 | note 1039 | Toggle last input 1040 | xpos 1041 | 20 1042 | ypos 1043 | 720 1044 | 1045 | D92CEBA5-F3C7-42D9-9A06-E62F7CECD5FA 1046 | 1047 | note 1048 | Pass next input 1049 | xpos 1050 | 780 1051 | ypos 1052 | 155 1053 | 1054 | E5B88B4F-44DD-4DA4-9E55-D8A2183C63D5 1055 | 1056 | note 1057 | Trigger list of output devices 1058 | xpos 1059 | 20 1060 | ypos 1061 | 30 1062 | 1063 | EBADB9C0-4E7A-4715-B940-D34A495F5917 1064 | 1065 | colorindex 1066 | 1 1067 | note 1068 | Set input device 1069 | xpos 1070 | 875 1071 | ypos 1072 | 620 1073 | 1074 | ED6B7E79-8B79-4DE6-9BCD-8D7BE2EDA3AF 1075 | 1076 | colorindex 1077 | 1 1078 | note 1079 | Set output device 1080 | xpos 1081 | 875 1082 | ypos 1083 | 125 1084 | 1085 | 1086 | variables 1087 | 1088 | LAST_INPUT 1089 | 1090 | NEXT_INPUT 1091 | 1092 | LAST_OUTPUT 1093 | 1094 | NEXT_OUTPUT 1095 | 1096 | SWITCH_AUDIO_SOURCE_PATH 1097 | /opt/homebrew/bin/SwitchAudioSource 1098 | 1099 | variablesdontexport 1100 | 1101 | SWITCH_AUDIO_SOURCE_PATH 1102 | LAST_INPUT 1103 | NEXT_INPUT 1104 | LAST_OUTPUT 1105 | NEXT_OUTPUT 1106 | 1107 | version 1108 | 1.2.0 1109 | webaddress 1110 | 1111 | 1112 | --------------------------------------------------------------------------------