Skip to content

Commit

Permalink
Support YouTube start time param
Browse files Browse the repository at this point in the history
  • Loading branch information
cookpete committed Jan 26, 2016
1 parent bdebc5c commit 7908463
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 4 deletions.
11 changes: 8 additions & 3 deletions src/players/YouTube.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import loadScript from 'load-script'

import Base from './Base'
import { parseStartTime } from '../utils'

const SDK_URL = '//www.youtube.com/iframe_api'
const SDK_GLOBAL = 'YT'
Expand Down Expand Up @@ -48,7 +49,10 @@ export default class YouTube extends Base {
load (url) {
const id = url && url.match(MATCH_URL)[1]
if (this.isReady) {
this.player.cueVideoById(id)
this.player.cueVideoById({
videoId: id,
startSeconds: parseStartTime(url)
})
return
}
if (this.loadingSDK) {
Expand All @@ -63,7 +67,8 @@ export default class YouTube extends Base {
videoId: id,
playerVars: {
...DEFAULT_PLAYER_VARS,
...this.props.youtubeConfig.playerVars
...this.props.youtubeConfig.playerVars,
start: parseStartTime(url)
},
events: {
onReady: () => {
Expand Down Expand Up @@ -110,7 +115,7 @@ export default class YouTube extends Base {
return this.player.getDuration()
}
getFractionPlayed () {
if (!this.isReady || !this.player.getCurrentTime) return null
if (!this.isReady || !this.getDuration()) return null
return this.player.getCurrentTime() / this.getDuration()
}
getFractionLoaded () {
Expand Down
32 changes: 32 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const MATCH_START_QUERY = /[\?&#](?:start|t)=([0-9hms]+)/
const MATCH_START_STAMP = /(\d+)(h|m|s)/g
const MATCH_NUMERIC = /^\d+$/

// Parse YouTube URL for a start time param, ie ?t=1h14m30s
// and return the start time in seconds
export function parseStartTime (url) {
const match = url.match(MATCH_START_QUERY)
if (match) {
const stamp = match[1]
if (stamp.match(MATCH_START_STAMP)) {
return parseStartStamp(stamp)
}
if (MATCH_NUMERIC.test(stamp)) {
return parseInt(stamp, 10)
}
}
return 0
}

function parseStartStamp (stamp) {
let seconds = 0
let array = MATCH_START_STAMP.exec(stamp)
while (array !== null) {
const [, count, period] = array
if (period === 'h') seconds += parseInt(count, 10) * 60 * 60
if (period === 'm') seconds += parseInt(count, 10) * 60
if (period === 's') seconds += parseInt(count, 10)
array = MATCH_START_STAMP.exec(stamp)
}
return seconds
}
9 changes: 8 additions & 1 deletion test/karma/ReactPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { render, unmountComponentAtNode } from 'react-dom'
import ReactPlayer from '../../src/ReactPlayer'

const { describe, it, beforeEach, afterEach } = window
const TEST_YOUTUBE_URL = 'https://www.youtube.com/watch?v=GlCmAC4MHek'
const TEST_YOUTUBE_URL = 'https://www.youtube.com/watch?v=M7lc1UVf-VE'
const TEST_SOUNDCLOUD_URL = 'https://soundcloud.com/miami-nights-1984/accelerated'
const TEST_VIMEO_URL = 'https://vimeo.com/90509568'
const TEST_FILE_URL = 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.ogv'
Expand Down Expand Up @@ -71,6 +71,13 @@ describe('ReactPlayer', () => {
it('fires onError for Vimeo video', done => testError(TEST_VIMEO_ERROR, done))
it('fires onError for file', done => testError(TEST_FILE_ERROR, done))

it('plays YouTube video at a specified time', done => {
const onProgress = state => {
if (state.played > 0.9) done()
}
render(<ReactPlayer url={TEST_YOUTUBE_URL + '?start=22m10s'} playing onProgress={onProgress} />, div)
})

it('switches between media', function (done) {
const renderFilePlayer = () => testPlay(TEST_FILE_URL, done)
const renderVimeoPlayer = () => testPlay(TEST_VIMEO_URL, renderFilePlayer)
Expand Down
45 changes: 45 additions & 0 deletions test/mocha/parseStartTime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, it } from 'mocha'
import { expect } from 'chai'

import { parseStartTime } from '../../src/utils'

const YOUTUBE_URL = 'http://youtu.be/12345678901'

describe('parseStartTime', () => {
it('parses seconds', () => {
expect(parseStartTime(YOUTUBE_URL + '?start=162')).to.equal(162)
})

it('parses stamps', () => {
expect(parseStartTime(YOUTUBE_URL + '?start=48s')).to.equal(48)
expect(parseStartTime(YOUTUBE_URL + '?start=3m15s')).to.equal(195)
expect(parseStartTime(YOUTUBE_URL + '?start=1h36m17s')).to.equal(5777)
})

it('parses with other params', () => {
expect(parseStartTime(YOUTUBE_URL + '?param=1&start=32')).to.equal(32)
})

it('parses using t', () => {
expect(parseStartTime(YOUTUBE_URL + '?t=32')).to.equal(32)
})

it('parses using a hash', () => {
expect(parseStartTime(YOUTUBE_URL + '#t=32')).to.equal(32)
expect(parseStartTime(YOUTUBE_URL + '#start=32')).to.equal(32)
})

it('parses using a hash', () => {
expect(parseStartTime(YOUTUBE_URL + '#t=32')).to.equal(32)
expect(parseStartTime(YOUTUBE_URL + '#start=32')).to.equal(32)
})

it('returns 0 for invalid stamps', () => {
expect(parseStartTime(YOUTUBE_URL)).to.equal(0)
expect(parseStartTime(YOUTUBE_URL + '?start=')).to.equal(0)
expect(parseStartTime(YOUTUBE_URL + '?start=hms')).to.equal(0)
expect(parseStartTime(YOUTUBE_URL + '?start=invalid')).to.equal(0)
expect(parseStartTime(YOUTUBE_URL + '?strat=32')).to.equal(0)
expect(parseStartTime(YOUTUBE_URL + '#s=32')).to.equal(0)
})
})

0 comments on commit 7908463

Please sign in to comment.