{"id":23153807,"url":"https://github.com/nypublicradio/ember-hifi","last_synced_at":"2025-08-17T21:33:19.520Z","repository":{"id":11112579,"uuid":"65047657","full_name":"nypublicradio/ember-hifi","owner":"nypublicradio","description":"The easy way to play audio in your ember app","archived":false,"fork":false,"pushed_at":"2023-07-19T07:09:53.000Z","size":6039,"stargazers_count":45,"open_issues_count":32,"forks_count":10,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-11-11T18:59:59.965Z","etag":null,"topics":["audio","ember","ember-addon","howlerjs","mp3","mp3-player","sound"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nypublicradio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-05T20:40:05.000Z","updated_at":"2024-05-30T02:28:01.000Z","dependencies_parsed_at":"2024-06-18T18:39:50.105Z","dependency_job_id":"8c4d3911-3e3b-40ba-b5a5-4e16693cd8b7","html_url":"https://github.com/nypublicradio/ember-hifi","commit_stats":{"total_commits":476,"total_committers":12,"mean_commits":"39.666666666666664","dds":"0.48949579831932777","last_synced_commit":"e396f985ea1357b913b42f9dedb12fb914fa9bf4"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nypublicradio%2Fember-hifi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nypublicradio%2Fember-hifi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nypublicradio%2Fember-hifi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nypublicradio%2Fember-hifi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nypublicradio","download_url":"https://codeload.github.com/nypublicradio/ember-hifi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230161912,"owners_count":18182964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","ember","ember-addon","howlerjs","mp3","mp3-player","sound"],"created_at":"2024-12-17T20:10:03.852Z","updated_at":"2024-12-17T20:10:05.134Z","avatar_url":"https://github.com/nypublicradio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ember-hifi\n\n## The easy way to play audio in your ember app\n\n![Download count all time](https://img.shields.io/npm/dt/ember-hifi.svg) [![npm version](https://img.shields.io/npm/v/ember-hifi.svg?style=flat-square)](https://www.npmjs.com/package/ember-hifi) [![CircleCI](https://img.shields.io/circleci/project/github/nypublicradio/ember-hifi/master.svg?style=flat-square)](https://circleci.com/gh/nypublicradio/ember-hifi/tree/master) [![Ember Observer Score](http://emberobserver.com/badges/ember-hifi.svg)](http://emberobserver.com/addons/ember-hifi)\n[![Maintainability](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/maintainability)](https://codeclimate.com/github/nypublicradio/ember-hifi/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/test_coverage)](https://codeclimate.com/github/nypublicradio/ember-hifi/test_coverage)\n\nThis addon exposes a `hifi` service which produces `Sound` objects which represent a playable piece of audio.\n\nThe `hifi` service makes it easy to play audio in the unfriendly landscape that is the current state of audio on the web. Forget worrying about formats and browsers and just give `hifi` a list of URLs to try and it'll play the first one that works.\n\n## Installing The Addon\n\n```shell\nnpm install ember-hifi\n```\n\n### Upgrading from \u003c 1.11.0\n`ember-hifi` no longer adds bower dependencies. If you are upgrading, you should edit your app's `bower.json` to remove the `hls.js` and `howler.js` entries added by previous versions of `ember-hifi`. NPM's dependency graph will take care of installing these libraries, and they will be added to your app's vendor tree at build time. Thanks to [@gmurphey](https://github.com/gmurphey) for [#41](https://github.com/nypublicradio/ember-hifi/pull/41).\n\n## Usage\n\n\n### API\n\n#### Service API\n`hifi` plays one sound at a time. Multiple sounds can be loaded and ready to go, but only one sound plays at a time. The currently playing sound is set to `currentSound` on the service, and most methods and properties on the service simply proxy to that sound.\n\n###### Methods\n\n- `play(urlsOrPromise, options)`\n\n`play` calls `load` with the same arguments, and then on success plays the sound, returning it to you.\n\n`play` can take one or more URLs, or a promise returning one or more URLs.\n\nIf the audio URLs are not known at the time of a play event, give `play` the promise to resolve, otherwise your mobile users might have to click the play button twice (due to some restrictions on autoplaying audio).\n\n```javascript\nexport default Ember.Route.extend({\n  hifi: service(),\n  ...\n  actions: {\n    play(id) {\n      let urlPromise = this.store.findRecord('story', id).then(story =\u003e story.getProperties('aacUrl', 'hlsUrl'))\n\n      this.hifi.play(urlPromise).then(({sound}) =\u003e {\n        // sound object\n\n      }).catch(error =\u003e {\n\n      })\n    }\n  }\n});\n```\nIf you already know the URLs, just pass them in.\n\n```javascript\nexport default Ember.Route.extend({\n  hifi: service(),\n  ...\n  actions: {\n    play(urls) {\n      this.hifi.play(urls).then(({sound}) =\u003e {\n        // sound object\n\n      }).catch(error =\u003e {\n\n      })\n    }\n  }\n});\n```\n\n- `pause()`\nPauses the current sound\n\n- `togglePause()`\nToggles the play state of the current sound\n\n- `fastForward(duration)`\nMoves the playhead of the current sound forwards by duration (in ms)\n\n- `rewind(duration)`\nMoves the playhead of the current sound backwards by duration (in ms)\n\n- `load(urlsOrPromise, options)`\nTries each hifi connection with each url and returns the ready `sound` from the first combination that works. The sound is cached internally so on subsequent load requests with the same url the already prepared sound will be returned. Calling `play` on the returned sound will start playback immediately.\n\n###### Gettable/Settable Properties\n- `volume`          (integer, 0-100)\n\nSystem volume. Bind a range element to this property for a simple volume control\n```javascript\n\n//component.js\nimport { inject as service } from \"@ember/service\";\nexport default Component.extend({\n  hifi: service(),\n})\n\n//template.hbs\n{{input type=\"range\" value=hifi.volume}}\n```\n\n- `position`        (integer, in ms)\n\nHere's a silly way to make a position control, too.\n```javascript\n//component.js\nexport default Component.extend({\n  hifi: service(),\n})\n\n//template.hbs\n{{input type=\"range\" value=hifi.position min=0 max=hifi.duration step=1000}}\n```\n\n###### Read Only Properties\n\n- `isLoading`         (boolean)\n- `isPlaying`         (boolean)\n- `isStream`          (boolean)\n- `isFastForwardable` (boolean)\n- `isRewindable`      (boolean)\n\n- `duration`          (integer, in ms)\n- `percentLoaded`     (integer 0-100, when available)\n\n- `currentSound`      the currently loaded sound\n\n### Sound API\n###### Methods\n- `play()`\nPlays the sound\n- `pause()`\nPauses the sound\n- `togglePause()`\nToggles the play state of the sound\n- `fastForward(duration)`\nMoves the playhead of the sound forwards by duration (in ms)\n- `rewind(duration)`\nMoves the playhead of the sound backwards by duration (in ms)\n\n###### Gettable/Settable Properties\n- `position` (integer, in ms)\n\n###### Read Only Properties\n- `isLoading` (boolean)\n- `isPlaying` (boolean)\n- `isStream` (boolean)\n- `isFastForwardable` (boolean)\n- `isRewindable` (boolean)\n\n- `duration` (integer, in ms)\n- `percentLoaded` (integer, not always available)\n- `url` the url of the sound\n\n### Events\nThe `hifi` service and the `sound` objects are extended with [Ember.Evented](https://www.emberjs.com/api/classes/Ember.Evented.html). You can subscribe to the following events in your application.\n\n###### Triggered on both the sound and relayed through the hifi service\n\n- `audio-played` (sound) - the sound started playing\n- `audio-paused` (sound) - the sound was paused\n- `audio-ended` (sound) - the sound finished playing\n- `audio-load-error` (error) - loading sound failed\n- `audio-ready` (sound) - the sound is ready to play\n- `audio-will-rewind` (sound, {currentPosition, newPosition}) - fired before rewinding a sound\n- `audio-will-fast-forward` (sound, {currentPosition, newPosition}) - fired before fast-forwarding a sound\n- `audio-position-will-change` (sound, {currentPosition, newPosition}) - fired before audio position change\n\n###### Hifi service events\n- `current-sound-changed` (currentSound, previousSound) - triggered when the current sound changes. On initial play, previousSound will be undefined.\n- `current-sound-interrupted` (currentSound, previousSound) - triggered when a sound has been playing and a new one takes its place by being played, pausing the first one\n- `new-load-request` ({loadPromise, urlsOrPromise, options}) - triggered whenever `.load` or `.play` is called.\n- `pre-load` ({loadPromise, urlsOrPromise, options}) - triggered whenever `.load` or `.play` is called.\n\n## Details\n\n### Included audio connections\n\n1. `NativeAudio` - Uses the native `\u003caudio\u003e` element for playing and streaming audio\n1. `HLS` - Uses HLS.js for playing HLS streams on the desktop.\n1. `Howler` - Uses [howler](http://howlerjs.com) to play audio\n\n`hifi` will take a list of urls and find the first connection/url combo that works. For desktop browsers, we'll try each url on each connection in the order the urls were specified.\n\nFor mobile browsers, we'll first try all the URLs on the NativeAudio using a technique to (hopefully) get around any autoplaying restrictions that sometimes require mobile users to click a play button twice.\n\n## Test Helpers\n#### Acceptance Tests\n\nImport this helper into acceptance tests to stub out hifi.\n\n```javascript\nimport '[your-app-name]/tests/helpers/hifi-acceptance-helper';\n```\n\n#### Unit Tests + Integration Tests\n\nIf you have a unit test that interacts with ember-hifi, you might get some errors if hifi's needs aren't met. Hifi uses some internal services that we'd hate for you to have to know about or type out, so just use our helper instead.\n\n```javascript\nimport { hifiNeeds, dummyHifi } from 'overhaul/tests/helpers/hifi-integration-helpers';\n\nmoduleFor('[your module]', 'Unit | [type] | [your module]', {\n  needs: [...hifiNeeds]\n\n...\n});\n```\n\nIf you need to fake out the hifiService to test how your app handles hifi events, you can use the dummyHifi service\n\n```javascript\nimport { hifiNeeds, dummyHifi } from 'overhaul/tests/helpers/hifi-integration-helpers';\n\nmoduleFor('[your module]', 'Integration | [type] | [your module]', {\n  needs: [...hifiNeeds],\n\n  beforeEach() {\n    this.register('service:hifi', dummyHifi);\n    this.inject.service('hifi');\n  }\n...\n});\n```\n\nAfter stubbing out the service with the dummyHifi service you can pass it some special urls in the format `/:status/:length/:name` to mimic responses, where `status` can be `good` or `bad`, and `length` can be an integer representing the duration in ms, or `stream`.\n\nA 10 second audio clip: `/good/10000/test`\n\nA web stream: `/good/stream/test`\n\nA url that will fail: `/bad/stream/test`\n\n\n## [Writing Your Own Hifi Connection](CUSTOM_CONNECTIONS.md)\n\nDo you need to support a funky audio format that hifi's built-in connections can't handle? Read more about how to write your own custom connection [here](CUSTOM_CONNECTIONS.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnypublicradio%2Fember-hifi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnypublicradio%2Fember-hifi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnypublicradio%2Fember-hifi/lists"}