Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 873bb7b
Showing
12 changed files
with
561 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2016 Russel Simmons | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
Next steps: | ||
- Reconsider how we store media/subs; rather than by language, might want just uids. Could mark whichever media/textChunks as 'native' vs. 'translation' | ||
- Check style guide on curlies spacing, etc. | ||
- Get eslint going? | ||
- Display the computed current subs somewhere | ||
- Jump-back button and kbd shortcut | ||
- Un-halfwidth subs on import | ||
- Display more summary info (sub tracks, media presence) | ||
- Integrate JS-mecab | ||
- Try JS-mecab on simple OCR output? | ||
- Consume OCR result | ||
- 0028.jpg is good, has errors/confusion | ||
- Do we need to ignore summary result and go off pieces alone? Is summary result always the concat of pieces? Seems like we want to go through and make spans with uids, and make corresponding divs over image. | ||
|
||
Keyboard shortcuts: | ||
- general idea is to keep one hand in position on keyboard. let's say left hand, since most people mouse with right. and let's say home row, so asdf+space | ||
- controls: | ||
- SPACE: toggle play/pause for video, go to to next page for comic | ||
- A: jump-back N seconds for video, go to previous page for comic | ||
- S: ? | ||
- D: toggle transcription | ||
- F: toggle translation | ||
|
||
What are controls? | ||
- new document (of given type, language) | ||
- import media | ||
- import subs | ||
- re-analyze current section? | ||
- media nav (depends on document type, has keyboard shortcuts) | ||
- comics | ||
- forward/back pages | ||
- video/audio | ||
- pause/resume | ||
- rewind, replay last section | ||
- auto-pause checkbox | ||
|
||
What contraints should hold for text? | ||
- For a video, it seems that all text should be inside a timed chunk? And the times should be sequential (in start times at least, overlaps could be allowed). Times could also be nested perhaps(?), but then they should still be in order. | ||
- For a comic, it seems that similarly all text should be inside a "page" chunk, and pages must be sequential. Pages could be skipped of course. Pages shouldn't nest. | ||
- For just-text (novel, article), it doesn't seem like we have any basic structural constraints. | ||
- For videos and comics and such, should match the media. | ||
|
||
Could we allow videos and comics to have text outisde chunks/pages, but just emit warnings? | ||
|
||
Should we wrap videos and comics and such in different top-level custom elements? Maybe just a data attribute or something to indicate which 'type' it is? Or do we just infer that by the top-level setting for the whole 'document'. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Immersion Player | ||
|
||
Immersion Player (*needs a real name*) is a power tool for foreign language learners to get the most out of watching videos, readings comics, etc. It lets you easily: | ||
- toggle the display of transcriptions/translations | ||
- look up definitions of unknown words | ||
- save excerpts for later SRS study | ||
- (for video) quickly rewind to hear tricky speech again | ||
- (for comics) extract text from images with OCR |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {parseSRT} from '../util/subtitles' | ||
|
||
export const incrementCount = () => ({ | ||
type: 'incrementCount', | ||
}); | ||
|
||
export const newDoc = (kind, language) => ({ | ||
type: 'newDoc', | ||
kind, | ||
language, | ||
}); | ||
|
||
export const importVideoFile = (file) => ({ | ||
type: 'importVideoFile', | ||
file, | ||
}); | ||
|
||
export const importSubsParsed = (subChunks, language) => ({ | ||
type: 'importSubsParsed', | ||
subChunks, | ||
language, | ||
}); | ||
|
||
export const importSubsFile = (file, language) => ( | ||
(dispatch) => { // return thunk | ||
// Start async file load and parse | ||
const reader = new FileReader(); | ||
reader.onload = (e) => { | ||
// Parse loaded file data and dispatch action for result | ||
dispatch(importSubsParsed(parseSRT(e.target.result), language)); | ||
} | ||
reader.readAsText(file); | ||
} | ||
); | ||
|
||
export const videoTimeUpdate = (time) => ({ | ||
type: 'videoTimeUpdate', | ||
time, | ||
}); | ||
|
||
export const controlBack = () => { | ||
|
||
return { | ||
type: 'controlBack', | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import React, { Component, PropTypes } from 'react' | ||
import { connect } from 'react-redux' | ||
|
||
import { incrementCount, newDoc, importVideoFile, importSubsFile, videoTimeUpdate, controlBack } from '../actions' | ||
|
||
// Counter | ||
const Counter = connect( | ||
(state) => ({ // mapStateToProps | ||
value: state.count, | ||
}), | ||
(dispatch) => ({ // mapDispatchToProps | ||
onIncreaseClick: () => dispatch(incrementCount()), | ||
}) | ||
)(({value, onIncreaseClick}) => ( | ||
<div> | ||
<span>{value}</span> | ||
<button onClick={onIncreaseClick}>Increase</button> | ||
</div> | ||
)); | ||
|
||
const languageOptions = [ | ||
{value: 'ja', label: 'Japanese'}, | ||
{value: 'en', label: 'English'}, | ||
]; | ||
|
||
// Select, "uncontrolled" but watches changes | ||
class Select extends Component { | ||
componentWillMount() { | ||
const {options, onSet} = this.props; | ||
if (options.length > 0) { | ||
onSet(options[0].value); | ||
} | ||
} | ||
|
||
render() { | ||
const {options, onSet} = this.props; | ||
return ( | ||
<select onChange={e => onSet(e.target.value)}> | ||
{options.map((o, i) => <option key={i} value={o.value}>{o.label}</option>)} | ||
</select> | ||
) | ||
} | ||
} | ||
|
||
// NewDocForm | ||
const NewDocForm = connect()( | ||
class extends Component { | ||
render() { | ||
const kindOptions = [ | ||
{value: 'video', label: 'Video'}, | ||
{value: 'comic', label: 'Comic'}, | ||
]; | ||
return ( | ||
<form onSubmit={e => { | ||
e.preventDefault(); | ||
this.props.dispatch(newDoc(this.kindVal, this.languageVal)); | ||
}}> | ||
<Select options={kindOptions} onSet={v => { this.kindVal = v; }} /> | ||
<Select options={languageOptions} onSet={v => { this.languageVal = v; }} /> | ||
<button type="submit">New Document</button> | ||
</form> | ||
); | ||
} | ||
} | ||
); | ||
|
||
// FileChooserForm | ||
const FileChooser = ({label, accept, onChoose}) => ( | ||
<label>{label} <input type="file" accept={accept} onChange={e => { onChoose(e.target.files[0]); e.target.value = null; }}/></label> | ||
); | ||
|
||
// VideoImportControls | ||
class VideoImportControls extends Component { | ||
render() { | ||
const {dispatch} = this.props; | ||
return ( | ||
<div> | ||
<form> | ||
<FileChooser label="Import Video" accept="video/*" onChoose={(file) => { dispatch(importVideoFile(file)); }} /> | ||
</form> | ||
<form> | ||
<FileChooser label="Import Subs (SRT)" accept=".srt" onChoose={(file) => { dispatch(importSubsFile(file, this.subLanguageVal)); }} /> | ||
<Select options={languageOptions} onSet={v => { this.subLanguageVal = v; }} /> | ||
</form> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
class VideoMedia extends Component { | ||
render() { | ||
const {media, onTimeUpdate, mountedVideoElement} = this.props; | ||
return ( | ||
<div>{ media ? ( | ||
<video src={media.videoURL} controls onTimeUpdate={e => {onTimeUpdate(e.target.currentTime)}} ref={(el) => { mountedVideoElement(el); }}/> | ||
) : "No video media" | ||
}</div> | ||
); | ||
} | ||
} | ||
|
||
class PlayControls extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.handleKeyDown = this.handleKeyDown.bind(this); | ||
} | ||
|
||
handleKeyDown(e) { | ||
const {onBack, onTogglePause} = this.props; | ||
|
||
// console.log(e); | ||
if (!e.repeat) { | ||
switch (e.keyCode) { | ||
case 65: // a | ||
onBack(); | ||
break; | ||
|
||
case 32: // space | ||
onTogglePause(); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
document.body.addEventListener('keydown', this.handleKeyDown); | ||
} | ||
|
||
componentWillUnmount() { | ||
document.body.removeEventListener('keydown', this.handleKeyDown); | ||
} | ||
|
||
render() { | ||
return null; | ||
} | ||
} | ||
|
||
// Doc | ||
class Doc extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.videoElement = null; | ||
} | ||
|
||
render() { | ||
const {doc, dispatch} = this.props; | ||
return ( | ||
<div> | ||
<div>Kind: { doc.kind }, Language: { doc.language }</div> | ||
<VideoImportControls dispatch={dispatch}/> | ||
<VideoMedia media={doc.media} onTimeUpdate={time => { dispatch(videoTimeUpdate(time)); }} mountedVideoElement={(el) => { this.videoElement = el; }} /> | ||
<PlayControls dispatch={dispatch} onBack={ | ||
() => { | ||
if (this.videoElement) { | ||
const nt = this.videoElement.currentTime - 3.0; | ||
this.videoElement.currentTime = nt >= 0 ? nt : 0; | ||
} | ||
} | ||
} onTogglePause={ | ||
() => { | ||
if (this.videoElement) { | ||
if (this.videoElement.paused) { | ||
this.videoElement.play(); | ||
} else { | ||
this.videoElement.pause(); | ||
} | ||
} | ||
} | ||
} /> | ||
<div>{JSON.stringify(doc.currentTextChunks)}</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
// MaybeDoc | ||
const MaybeDoc = connect( | ||
(state) => ({ | ||
doc: state.doc, | ||
}) | ||
)(({doc, dispatch}) => ( | ||
<div>{ doc ? <Doc doc={doc} dispatch={dispatch} /> : "No document" }</div> | ||
)); | ||
|
||
// App | ||
const App = () => ( | ||
<div> | ||
<Counter /> | ||
<NewDocForm /> | ||
<MaybeDoc /> | ||
</div> | ||
); | ||
|
||
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<html> | ||
<head> | ||
<title>Immersion Player</title> | ||
<link href="main.css" rel="stylesheet" type="text/css"> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="text/javascript" src="bundle.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react' | ||
import { render } from 'react-dom' | ||
import { createStore, applyMiddleware } from 'redux' | ||
import { Provider } from 'react-redux' | ||
import ReduxThunk from 'redux-thunk' | ||
import createReduxLogger from 'redux-logger' | ||
|
||
import rootReducer from './reducers' | ||
import RootComponent from './components' | ||
|
||
const middlewares = [ReduxThunk]; | ||
|
||
if (process.env.NODE_ENV === 'development') { | ||
middlewares.push(createReduxLogger()); | ||
} | ||
|
||
const store = createStore(rootReducer, applyMiddleware(...middlewares)); | ||
|
||
render( | ||
<Provider store={store}> | ||
<RootComponent /> | ||
</Provider>, | ||
document.getElementById('root') | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
html, body { | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
body { | ||
font-family: Helvetica, Arial, "Lucida Grande", sans-serif; | ||
} |
Oops, something went wrong.