Skip to content

Commit

Permalink
Merge pull request #1 from timbuckley/patch-1
Browse files Browse the repository at this point in the history
Use Github elm/html syntax highlighting on README
  • Loading branch information
danabrams committed Aug 12, 2018
2 parents b2220e9 + 95af09e commit 9784280
Showing 1 changed file with 43 additions and 32 deletions.
75 changes: 43 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A New, port-based wrapper on the HTML Media API

This project requires a port (and some other javascript). You can set it up by importing the "Port/mediaPort.js" file into your html file, and doing something like the following:

```
```html
<script src="main.js"></script>
<script src="../Port/mediaApp.js"></script>

Expand All @@ -28,35 +28,38 @@ You can use this library without any ports or javascript at all, if all you want

1) Put a `Media.State` type in your model:

```
```elm
import Media

type alias Model =
{ mediaState: Media.State}```
{ mediaState: Media.State}
```

2) `Media.State` is an opaque type, so you can't craft one by hand (it's a representation of the internat state of the media element, managed by the browser). It's important, however, to give each media element a unique id, and to try to nudge you into doing say, you have to put it into you `init` function with a function called `newVideo` (or `newAudio` if you're creating an audio player), which takes a String (representing your unique id), and returns a kind of default state of an uninitialized media object.
2) `Media.State` is an opaque type, so you can't craft one by hand (it's a representation of the internat state of the media element, managed by the browser). It's important, however, to give each media element a unique id, and to try to nudge you into doing say, you have to put it into you `init` function with a function called `newVideo` (or `newAudio` if you're creating an audio player), which takes a `String` (representing your unique id), and returns a kind of default state of an uninitialized media object.

```
```elm
import Media.State exposing (newVideo)

init = ({state = newVideo "myVideo"}, Cmd.none)```
init = ({state = newVideo "myVideo"}, Cmd.none)
```

3) Create a video element in your view function with the function `Media.Video`. It takes the state you've already created,so you don't have to worry about messing up the unique id, then it works like any other Html element, with a List (Attribute msg) and a List (Html msg), returning an Html msg.
3) Create a video element in your view function with the function `Media.Video`. It takes the state you've already created,so you don't have to worry about messing up the unique id, then it works like any other `Html` element, with a `List (Attribute msg)` and a `List (Html msg)`, returning an `Html msg`.

More media-centric attributes are available in the Media.Attributes module.
More media-centric attributes are available in the `Media.Attributes` module.

```
```elm
import Media exposing (video)
import Media.Attributes exposing (src, muted, autoplay, controls)

view model =
Media.video model.state [ src "MyVideo.mp4", muted True, autoplay True, controls True][]```
Media.video model.state [ src "MyVideo.mp4", muted True, autoplay True, controls True ] []
```

4) At this point, you have a media element, but you're not actually tracking it's state. We need to create a (State -> Msg) and update our model accordingly.
4) At this point, you have a media element, but you're not actually tracking it's state. We need to create a `(State -> Msg)` and update our model accordingly.

You'll find most of the media-centric events are wrapped in the `Media.Events` model, as well as a useful function allEvents that simply provides a list of all the common media events, so you can update your model frequently, without having to type out several dozen events.

```
```elm
import Media.Events exposing(onTimeUpdate, allEvents)
import Media exposing (video)
import Media.Attributes exposing (src, muted, autoplay, controls)
Expand All @@ -67,80 +70,88 @@ type Msg =
update msg model =
case msg of
MediaStateUpdate s ->
({model | state = state}, Cmd.none)
({ model | state = state }, Cmd.none)

view model =
Media.video model.state ([ src "MyVideo.mp4", muted True, autoplay True, controls True, onTimeUpdate MediaStateUpdate] ++ (allEvents MediaStateUpdate)[]```
Media.video model.state ([ src "MyVideo.mp4", muted True, autoplay True, controls True, onTimeUpdate MediaStateUpdate] ++ (allEvents MediaStateUpdate)[]
```

5) State is an opaque type, so to access the data inside it, you'll need to use the getter functions in `Media.State`, like currentTime or playbackStatus.
5) State is an opaque type, so to access the data inside it, you'll need to use the getter functions in `Media.State`, like `currentTime` or `playbackStatus`.

```import Media.State exposing (currentTime)
```elm
import Media.State exposing (currentTime)
import Html exposing (p, text, div)

view model =
div []
[ Media.video model.state
([ src "MyVideo.mp4", muted True, autoplay True, controls True] ++ (allEvents MediaStateUpdate)
[]
, p [][text ("Current Time: " ++ currentTime model.state)]
]```
, p [] [text ("Current Time: " ++ currentTime model.state)]
]
```

6) (Optional) Elm can't decode the TimeRanges object of a media element without using Native/Kernel code, so if you need to read the seekable, played, or buffered properties, you need to setup an extra piece of javascript in you html file.

I've created a handy function that creates an asArray getter on every TimeRange object, which simply returns the object's values in an array form that Elm can decode. You can set it up like so:
I've created a handy function that creates an `asArray` getter on every TimeRange object, which simply returns the object's values in an array form that Elm can decode. You can set it up like so:

```<script src="/Port/mediaApp.js"></script>
```html
<script src="/Port/mediaApp.js"></script>

<script>
MediaApp.Modify.timeRanges();
/* Adds the "asArray getter to the TimeRanges object prototype, allowing us to decode the media element state without using Native/Kernel Code */
</script>```
</script>
```

### Playback Controls

It's impossible, or wildly impractical to control playback (play, pause, seek, load) of a media element from Elm without using Native/Kernel code, except through a port, so that's what we have here.

You need to create a port in Elm like this:

```
```elm
import Media exposing (PortMsg)

port outbound : PortMsg -> Cmd msg```
port outbound : PortMsg -> Cmd msg
```

And, of course, you'll need a port in defined in your Html to handle the messages it receives. You can write your own, but the one I provide in "Port/mediaApp.js" checks to try and eliminate runtime errors (if you have a runtime error, let me know and I will try to add prevent in future versions).

To use the mediaApp.js port handler function, you just create your port in the html and pass is the `MediaApp.portHandler` function.

PortMsg is a simple type.

```
```elm
type alias PortMsg =
{ tag : String
, id : String
, data : Encode.Value
}```
}
```

You can easily craft your own PortMsg if you want, but the ```Media``` module includes some helper functions to make it easier: play, pause, seek, load.
You can easily craft your own PortMsg if you want, but the `Media` module includes some helper functions to make it easier: `play`, `pause`, `seek`, `load`.

play, pause, and load just take a Media.State record--and your port function--and generate a Cmd Msg to send out a port, so you can use them in an update function.
`play`, `pause`, and `load` just take a Media.State record--and your port function--and generate a `Cmd Msg` to send out a port, so you can use them in an `update` function.

```
```elm
import Media exposing (play)

update msg model =
case msg of
Play ->
(model, play model.state myPort )```
( model, play model.state myPort )
```

seek also takes a float value, which represents the time you want to seek to.
`seek` also takes a float value, which represents the time you want to seek to.

NOTE: It's possible "seek" is a word that seems obvious to me in my media-developer cultural bubble. It just means "change the current time of the player to x."


### Text Tracks

This version of the library FINALLY includes support for subtitles, chapters, captions, and synched metadata...you can find the relevant attributes on the track tag in Media.Attributes.
This version of the library FINALLY includes support for subtitles, chapters, captions, and synched metadata...you can find the relevant attributes on the track tag in `Media.Attributes`.

I hope to improve this part of the library substantially over the next month, so I won't go into much depth for now.

Just reach out on slack or discourse if you have any questions.
Just reach out on slack or discourse if you have any questions.

0 comments on commit 9784280

Please sign in to comment.