{"action":"created","issue":{"url":"https://api.github.com/repos/nodecg/nodecg/issues/156","repository_url":"https://api.github.com/repos/nodecg/nodecg","labels_url":"https://api.github.com/repos/nodecg/nodecg/issues/156/labels{/name}","comments_url":"https://api.github.com/repos/nodecg/nodecg/issues/156/comments","events_url":"https://api.github.com/repos/nodecg/nodecg/issues/156/events","html_url":"https://github.com/nodecg/nodecg/pull/156","id":146517225,"number":156,"title":"Add Sounds feature, rename Uploads to Assets, add categories to Asets","user":{"login":"Lange","id":873012,"avatar_url":"https://avatars.githubusercontent.com/u/873012?v=3","gravatar_id":"","url":"https://api.github.com/users/Lange","html_url":"https://github.com/Lange","followers_url":"https://api.github.com/users/Lange/followers","following_url":"https://api.github.com/users/Lange/following{/other_user}","gists_url":"https://api.github.com/users/Lange/gists{/gist_id}","starred_url":"https://api.github.com/users/Lange/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lange/subscriptions","organizations_url":"https://api.github.com/users/Lange/orgs","repos_url":"https://api.github.com/users/Lange/repos","events_url":"https://api.github.com/users/Lange/events{/privacy}","received_events_url":"https://api.github.com/users/Lange/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2016-04-07T06:03:12Z","updated_at":"2016-04-09T00:46:10Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/nodecg/nodecg/pulls/156","html_url":"https://github.com/nodecg/nodecg/pull/156","diff_url":"https://github.com/nodecg/nodecg/pull/156.diff","patch_url":"https://github.com/nodecg/nodecg/pull/156.patch"},"body":"This PR makes a breaking change to the Assets system and introduces the Sounds system.\r\n\r\n# Assets\r\n\r\n### Why change the Uploads system?\r\n\r\nThere are two reasons for this change.\r\n\r\nThe first reason is that under the existing system, all uploads to a given bundle are lumped into one top-level category. This was suboptimal, as it prevented a bundle from having two separate features that leveraged the Uploads system. For example, say that a bundle had one feature that displayed thumbnails of game box art, and another feature that displayed full box art images. In the current Uploads system, there would be no way to differentiate between these files.\r\n\r\nThe new Assets system adds the concept of \"categories\" to these uploads. This enables a single bundle to make use of Assets in multiple ways.\r\n\r\nThe second reason is that I wanted to build the Sounds system on top of the Assets system. A nice feature of [lfg-sounds](https://github.com/SupportClass/lfg-sounds) (the current de facto method of managing and playing sounds in NodeCG bundles) is that it enables the end user to replace the sound file played by any of the cues. Adding this \"categories\" concept to the Assets system was a requirement of making a similar feature available in the Sounds system. Users will upload their replacement sound files via the Assets page on the dashboard.\r\n\r\n## The new Assets format\r\n\r\nBelow is a sample declaration of `assetCategories` in a bundle's `package.json`:\r\n```json\r\n{\r\n  \"nodecg\": {\r\n    \"assetCategories\": [\r\n      {\r\n        \"name\": \"thumbnails\",\r\n        \"title\": \"Thumbnails\",\r\n        \"allowedTypes\": [\r\n          \"jpg\",\r\n          \"jpeg\",\r\n          \"gif\",\r\n          \"png\"\r\n        ]\r\n      },\r\n      {\r\n        \"name\": \"boxart\",\r\n        \"title\": \"Boxart\",\r\n        \"allowedTypes\": [\r\n          \"jpg\",\r\n          \"jpeg\",\r\n          \"gif\",\r\n          \"png\"\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\n... and here is a screenshot of what a bundle's Assets card looks like on the dashboard:\r\n![image](https://cloud.githubusercontent.com/assets/873012/14340986/c3ee1e80-fc52-11e5-9a03-b29a468f9c2e.png)\r\n\r\n# Sounds\r\nNodeCG v0.8 adds a Sound system for management and playback of sound cues. Cues are declared in a bundle's `package.json`:\r\n```json\r\n{\r\n  \"nodecg\": {\r\n    \"soundCues\": [\r\n      {\r\n        \"name\": \"name-only\"\r\n      },\r\n      {\r\n        \"name\": \"default-volume\",\r\n        \"defaultVolume\": 80\r\n      },\r\n      {\r\n        \"name\": \"non-assignable\",\r\n        \"assignable\": false\r\n      },\r\n      {\r\n        \"name\": \"default-file\",\r\n        \"defaultFile\": \"sound/default-file.ogg\"\r\n      }\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\nEach `cue` has the following structure:\r\n - `name` (`string`) **Required** - The name of the cue.\r\n - `assignable` (`boolean`) *Optional* - Whether or not this cue can be re-assigned to play a file other than `defaultFile`. If `true`, users can select any of the uploaded sounds via a dropdown select on this cue's entry in the Mixer page. Defaults to `true`.\r\n - `defaultVolume` (`0-100`) *Optional* - The default volume of this cue. Defaults to 30.\r\n - `defaultFile` (`string`) *Optional* - The relative path (relative from the root of the bundle) to the sound file that should be played by default when this cue is invoked. `defaultFile`s are shipped with the bundle.\r\n\r\nWhen a bundle has at least one cue with `assignable: true`, it gains a \"Sounds\" Assets category.\r\n\r\n![image](https://cloud.githubusercontent.com/assets/873012/14341958/b0b8bc86-fc5b-11e5-8325-695992473e73.png)\r\n\r\nSound cues are managed from a new page called the \"Mixer\":\r\n![image](https://cloud.githubusercontent.com/assets/873012/14341918/4d7af7c4-fc5b-11e5-95bc-d595c2373c6d.png)\r\n\r\nTo change the file that is played when an assignable cue is invoked, the end user selects the desired file from the dropdown next to that cue's name. Any files uploaded to that bundle's \"Sounds\" Assets category are available. Additionally, cues with a `defaultFile` have a \"Default\" option.\r\n\r\n## Volume Control\r\n\r\nEach cue has its own volume fader. Each bundle also has its own fader which controls all cues in that bundle. Beyond that, the NodeCG instance as a whole has a Master fader that adjusts all sound cues. When a cue is played, the values of these faders are combined to determine the final playback volume.\r\n\r\n## API\r\n### nodecg.playSound(name, opts)\r\nPlays the sound cue with the given name in the current bundle. Silently fails if that cue has no assigned audio file. Returns a SoundJS [`AbstractSoundInstance`](http://www.createjs.com/docs/soundjs/classes/AbstractSoundInstance.html). This `AbstractSoundInstance` can be further manipulated by userland code.\r\n\r\nBy default, changes made to the cue fader, bundle fader, or master fader will automatically update the volume of the returned `AbstractSoundInstance`. To disable this behavior, set `opts.updateVolume` to `false`.\r\n\r\nIt is not currently possible to play sounds that belong to another bundle.\r\n\r\n### nodecg.stopSound(name)\r\nStops all playing instances of a cue.\r\n\r\n### nodecg.stopAllSounds()\r\nStops all sounds."},"comment":{"url":"https://api.github.com/repos/nodecg/nodecg/issues/comments/207664852","html_url":"https://github.com/nodecg/nodecg/pull/156#issuecomment-207664852","issue_url":"https://api.github.com/repos/nodecg/nodecg/issues/156","id":207664852,"user":{"login":"Lange","id":873012,"avatar_url":"https://avatars.githubusercontent.com/u/873012?v=3","gravatar_id":"","url":"https://api.github.com/users/Lange","html_url":"https://github.com/Lange","followers_url":"https://api.github.com/users/Lange/followers","following_url":"https://api.github.com/users/Lange/following{/other_user}","gists_url":"https://api.github.com/users/Lange/gists{/gist_id}","starred_url":"https://api.github.com/users/Lange/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lange/subscriptions","organizations_url":"https://api.github.com/users/Lange/orgs","repos_url":"https://api.github.com/users/Lange/repos","events_url":"https://api.github.com/users/Lange/events{/privacy}","received_events_url":"https://api.github.com/users/Lange/received_events","type":"User","site_admin":false},"created_at":"2016-04-09T00:46:10Z","updated_at":"2016-04-09T00:46:10Z","body":"I'll merge this tomorrow morning if this PR receives no further comments."}}