diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..48b1182 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/NOTES.txt b/NOTES.txt new file mode 100644 index 0000000..9bc3a21 --- /dev/null +++ b/NOTES.txt @@ -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'. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4de1c90 --- /dev/null +++ b/README.md @@ -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 diff --git a/actions/index.js b/actions/index.js new file mode 100644 index 0000000..cd7be98 --- /dev/null +++ b/actions/index.js @@ -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', + } +}; diff --git a/components/index.js b/components/index.js new file mode 100644 index 0000000..25c1df4 --- /dev/null +++ b/components/index.js @@ -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}) => ( +
+ {value} + +
+)); + +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 ( + + ) + } +} + +// NewDocForm +const NewDocForm = connect()( + class extends Component { + render() { + const kindOptions = [ + {value: 'video', label: 'Video'}, + {value: 'comic', label: 'Comic'}, + ]; + return ( +
{ + e.preventDefault(); + this.props.dispatch(newDoc(this.kindVal, this.languageVal)); + }}> + { this.languageVal = v; }} /> + +
+ ); + } + } +); + +// FileChooserForm +const FileChooser = ({label, accept, onChoose}) => ( + +); + +// VideoImportControls +class VideoImportControls extends Component { + render() { + const {dispatch} = this.props; + return ( +
+
+ { dispatch(importVideoFile(file)); }} /> + +
+ { dispatch(importSubsFile(file, this.subLanguageVal)); }} /> +