pig-media-server 0.3.2 → 2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/bin/pig-media-server +4 -1
  4. data/gulpfile.js +33 -0
  5. data/javascripts/application.js +117 -0
  6. data/javascripts/chromecast.js +57 -0
  7. data/javascripts/components/head.js +106 -0
  8. data/javascripts/components/list.js +104 -0
  9. data/javascripts/components/new_flag.js +23 -0
  10. data/javascripts/components/player.js +171 -0
  11. data/javascripts/components/query_list.js +68 -0
  12. data/javascripts/components/sort.js +64 -0
  13. data/javascripts/components/watch.js +48 -0
  14. data/javascripts/controller.js +62 -0
  15. data/javascripts/custom_list.js +14 -0
  16. data/javascripts/event_dispatcher.js +21 -0
  17. data/javascripts/recent.js +34 -0
  18. data/javascripts/utils.js +22 -0
  19. data/javascripts/video.js +10 -0
  20. data/lib/pig-media-server/api.rb +96 -0
  21. data/lib/pig-media-server/aspect.rb +10 -5
  22. data/lib/pig-media-server/backup.rb +69 -0
  23. data/lib/pig-media-server/cli.rb +60 -25
  24. data/lib/pig-media-server/crawl.rb +41 -5
  25. data/lib/pig-media-server/kindle_send.rb +31 -24
  26. data/lib/pig-media-server/model/data.rb +106 -0
  27. data/lib/pig-media-server/model/migrate.rb +34 -0
  28. data/lib/pig-media-server/model/pig.rb +11 -2
  29. data/lib/pig-media-server/version.rb +1 -1
  30. data/lib/pig-media-server/views/_custom_links.haml +4 -0
  31. data/lib/pig-media-server/views/_ft_flv.haml +1 -0
  32. data/lib/pig-media-server/views/_ft_pdf.haml +0 -0
  33. data/lib/pig-media-server/views/_ft_video.haml +9 -4
  34. data/lib/pig-media-server/views/_link.haml +4 -1
  35. data/lib/pig-media-server/views/_new_flag.haml +1 -1
  36. data/lib/pig-media-server/views/app.scss +10 -1
  37. data/lib/pig-media-server/views/bundle.js +13 -0
  38. data/lib/pig-media-server/views/chromecast.coffee +54 -0
  39. data/lib/pig-media-server/views/config.coffee +13 -1
  40. data/lib/pig-media-server/views/config.haml +27 -4
  41. data/lib/pig-media-server/views/feed.builder +3 -2
  42. data/lib/pig-media-server/views/flv.coffee +33 -0
  43. data/lib/pig-media-server/views/index.haml +11 -5
  44. data/lib/pig-media-server/views/meta.haml +4 -4
  45. data/lib/pig-media-server/views/movie.coffee +65 -39
  46. data/lib/pig-media-server/views/react.haml +22 -0
  47. data/lib/pig-media-server/views/remote.haml +2 -2
  48. data/lib/pig-media-server/views/session.coffee +5 -0
  49. data/lib/pig-media-server/views/storage.coffee +5 -16
  50. data/lib/pig-media-server/views/sub.haml +5 -6
  51. data/lib/pig-media-server/views/subview.coffee +109 -19
  52. data/lib/pig-media-server/views/tv.coffee +97 -0
  53. data/lib/pig-media-server/views/tv.haml +33 -0
  54. data/lib/pig-media-server/views/unread.coffee +10 -0
  55. data/lib/pig-media-server/web.rb +102 -37
  56. data/package.json +23 -0
  57. data/pig-media-server.gemspec +5 -0
  58. metadata +123 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f32013836d4bf857c153d9c1637166e3cf261ca
4
- data.tar.gz: 0bffdde44cb7536875cae6239a5b2014ab0f874d
3
+ metadata.gz: 03172c241d9a720259504c431a3f24d3226dae28
4
+ data.tar.gz: cbd95152cf18824e2898e32562d61942d1ac525a
5
5
  SHA512:
6
- metadata.gz: 16333ea7be32edb98d89f82e1dbf7b37e9d3b407d810a09c66208ddf545c744ac7d4f020f66910c68601e56de6e29f5e381d63d8e0d4cb7b31c43a394b79f147
7
- data.tar.gz: d84211f9cb6b5ea9bd351b6e8d6752fa3b9833602928ac17d2468c13ebffcaf4e6f7e373d84b7179976c77fc7e16d3f68805ef83968792eedfff24f4b59e07e1
6
+ metadata.gz: 3e086f8d14b480451cd612f665b3971862601eaae32c43bf7771337befc492653b68fd1d132873e10a6408896519956145bb331f76c0c0ef8273605cb0e7cf9a
7
+ data.tar.gz: a95b3789b246eff2c3a0937def42c8983793b36696eec54229d52c2cc6b37d3687b38fccbf1d776e758b4e121fa4e851b94b338698d85d6defac8723b7a021dd
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  config.yaml
19
19
  vendor/bundle
20
+ node_modules
data/bin/pig-media-server CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+ require 'bundler/setup'
3
+ Bundler.require
3
4
  require 'pig-media-server'
4
5
  require 'pig-media-server/cli'
6
+ PigMediaServer::CLI.start
7
+
data/gulpfile.js ADDED
@@ -0,0 +1,33 @@
1
+ var gulp = require('gulp');
2
+ var browserify = require('browserify');
3
+ var babelify = require('babelify');
4
+ var source = require('vinyl-source-stream');
5
+ var uglify = require('gulp-uglify');
6
+ var buffer = require('vinyl-buffer');
7
+ var cssDir = 'stylesheets'
8
+
9
+ gulp.task('browserify', function() {
10
+ browserify('./javascripts/application.js', { debug: true })
11
+ .transform(babelify)
12
+ .bundle()
13
+ .on("error", function (err) { console.log("Error : " + err.message); })
14
+ .pipe(source('bundle.js'))
15
+ .pipe(gulp.dest('./lib/pig-media-server/views/'))
16
+ });
17
+
18
+ gulp.task('build', function(){
19
+ browserify('./javascripts/application.js', { debug: true })
20
+ .transform(babelify)
21
+ .bundle()
22
+ .on("error", function (err) { console.log("Error : " + err.message); })
23
+ .pipe(source('bundle.js'))
24
+ .pipe(buffer())
25
+ .pipe(uglify())
26
+ .pipe(gulp.dest('./lib/pig-media-server/views/'))
27
+
28
+ });
29
+
30
+
31
+ gulp.task('watch', ['browserify'], function(){
32
+ gulp.watch('./javascripts/**/*.js', ['browserify']);
33
+ });
@@ -0,0 +1,117 @@
1
+ window.React = require('react');
2
+ window.ReactDOM = require('react-dom');
3
+ window.$ = window.jQuery = require('jquery');
4
+ window.moment = require('moment-timezone');
5
+
6
+ require('./chromecast.js');
7
+
8
+ require('./utils.js');
9
+ require('./controller.js');
10
+
11
+ require('./event_dispatcher.js');
12
+
13
+ require('./recent.js');
14
+ require('./video.js');
15
+ require('./custom_list.js');
16
+
17
+ require('./components/head.js');
18
+ require('./components/sort.js');
19
+ require('./components/list.js');
20
+ require('./components/query_list.js');
21
+ require('./components/new_flag.js');
22
+ require('./components/watch.js');
23
+ require('./components/player.js');
24
+
25
+ class Application extends React.Component {
26
+ constructor(props){
27
+ super(props);
28
+ this.controller = new Controller();
29
+ this.recent = new Recent(this);
30
+ this.video = new Video();
31
+ this.custom_list = new CustomList(this);
32
+
33
+ this.state = {
34
+ config: {},
35
+ session: {},
36
+ items: [],
37
+ recent: {},
38
+ video: null,
39
+
40
+ open: (link)=>{this.open(link)},
41
+ initialize: ()=>{this.initialize()},
42
+ update_state: ()=>{this.update_state()},
43
+
44
+ controller: this.controller,
45
+
46
+ models: {video: this.video, recent: this.recent, custom_list: this.custom_list}
47
+ }
48
+
49
+ this.state.models.video.addEventListener('videoUpdated', ()=>{ this.setState(this.state); });
50
+
51
+ window.addEventListener('popstate',(ev)=>{ this.initialize(); },false);
52
+ }
53
+
54
+ open(link){
55
+ history.pushState('', '', link);
56
+ this.initialize();
57
+ }
58
+
59
+ update_state(){ this.setState(this.state); }
60
+
61
+ load_from_api(url){
62
+ if(!!url){
63
+ $.get(url).done((data)=>{ this.state.items = data; this.update_state(); });
64
+ } else {
65
+ this.state.items = [];
66
+ this.update_state();
67
+ }
68
+ }
69
+
70
+ load_config(){ $.get("/api/r/config").done((data)=>{ this.state.config = data; this.update_state(); })}
71
+ load_session(){
72
+ $.get("/api/r/session").done((data)=>{
73
+ this.state.session = data;
74
+ this.update_state();
75
+ this.load_recent();
76
+ })
77
+ }
78
+
79
+ load_recent(){ this.recent.load() }
80
+
81
+ routing(){
82
+ var route = this.controller.route();
83
+ switch(route.page){
84
+ case "list":
85
+ this.load_from_api(this.controller.route().api_url);
86
+ this.state.active_page = 'list';
87
+ this.update_state();
88
+ break;
89
+ }
90
+ }
91
+
92
+ initialize(){
93
+ this.load_config();
94
+ this.load_session();
95
+ this.custom_list.load();
96
+ this.routing()
97
+ }
98
+
99
+ componentDidMount(){ this.initialize() }
100
+
101
+ render(){
102
+ return <div>
103
+ <Player state={this.state} />
104
+ <div id='all'>
105
+ <Head state={this.state} />
106
+ <SearchBox state={this.state}/>
107
+ <List state={this.state}/>
108
+ <QueryList state={this.state}/>
109
+ </div>
110
+ </div>
111
+ }
112
+ }
113
+
114
+ ReactDOM.render(<Application />, document.querySelector("#application"));
115
+
116
+
117
+
@@ -0,0 +1,57 @@
1
+ var currentMedia = null;
2
+ var currentVolume = 0.5;
3
+ var progressFlag = 1;
4
+ var mediaCurrentTime = 0;
5
+ var session = null;
6
+
7
+ var null_func = (obj)=>{''}
8
+
9
+ var sessionListener = (e)=>{
10
+ console.log('New session ID: ' + e.sessionId)
11
+ session = e
12
+ }
13
+
14
+ var onRequestSessionSuccess = (e)=>{
15
+ console.log("session success: " + e.sessionId)
16
+ session = e
17
+ if(session.media.length != 0){
18
+ onMediaDiscovered('onRequestSession', session.media[0])
19
+ }
20
+ session.addMediaListener(
21
+ onMediaDiscovered.bind(this, 'addMediaListener'))
22
+ }
23
+
24
+ var onMediaDiscovered = (how, media)=>{
25
+ console.log("new media session ID:" + media.mediaSessionId)
26
+ currentMedia = media
27
+ mediaCurrentTime = currentMedia.currentTime
28
+ }
29
+
30
+ var initializeCastApi = ()=>{
31
+ var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID)
32
+ var apiConfig = new chrome.cast.ApiConfig(sessionRequest, sessionListener, null_func)
33
+ chrome.cast.initialize(apiConfig, null_func, null_func)
34
+
35
+ console.log("launching app...")
36
+ chrome.cast.requestSession(onRequestSessionSuccess, null_func)
37
+ $('a.chromecast').show()
38
+ }
39
+
40
+ window.chrome_cast = (mediaURL, key)=>{
41
+ if(mediaURL.match(/pig.ssig33.com/)){
42
+ mediaURL = mediaURL.replace(/pig.ssig33.com\/volume/, 'ashare.ssig33.com');
43
+ mediaURL = mediaURL.replace(/https/, 'http');
44
+ mediaURL = mediaURL.replace(/^\/\//, 'http://');
45
+ console.log(mediaURL);
46
+ }
47
+ var mediaInfo = new chrome.cast.media.MediaInfo(mediaURL)
48
+ mediaInfo.contentType = 'video/mp4'
49
+ var request = new chrome.cast.media.LoadRequest(mediaInfo)
50
+ request.autoplay = true
51
+ request.currentTime = 0
52
+ session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), null_func)
53
+ }
54
+
55
+ window['__onGCastApiAvailable'] = (loaded, errorInfo)=>{
56
+ if(loaded){ initializeCastApi() }
57
+ }
@@ -0,0 +1,106 @@
1
+ class LoginAs extends React.Component {
2
+ render(){
3
+ return <span>Login as {this.props.user_id}</span>
4
+ }
5
+ }
6
+
7
+ class LoginLink extends React.Component {
8
+ render(){
9
+ return <a href='/sessions'>Login</a>
10
+ }
11
+ }
12
+
13
+ class Session extends React.Component {
14
+ render(){
15
+ return <div>
16
+ {this.props.state.session.user_id ?
17
+ <LoginAs user_id={this.props.state.session.user_id} /> :
18
+ <LoginLink />
19
+ }
20
+ </div>
21
+ }
22
+ }
23
+
24
+ class Head extends React.Component {
25
+ click(){
26
+ this.props.state.open('/');
27
+ }
28
+ render(){
29
+ return <div>
30
+ <Session state={this.props.state} />
31
+ <h2>
32
+ <a href='javascript:void(0)' onClick={()=> this.click()}>
33
+ {this.props.state.config.page_title}
34
+ </a>
35
+ </h2>
36
+ </div>
37
+ }
38
+ }
39
+
40
+ class CustomList extends React.Component{
41
+ click(){
42
+ this.props.state.open(`/custom?name=${encodeURIComponent(this.props.name)}`)
43
+ }
44
+ render(){
45
+ return <a href='javascript:void(0)' onClick={()=> this.click()}>{this.props.name}</a>
46
+ }
47
+ }
48
+
49
+ class Recommend extends React.Component{
50
+ click(){ this.props.state.open(`/recommend?name=${localStorage.user_id}`) }
51
+ render(){
52
+ return <span>
53
+ {!!localStorage.user_id ? <a href='javascript:void(0)' onClick={()=> this.click()}><b>Recommend</b></a> : null}
54
+ </span>
55
+ }
56
+ }
57
+
58
+ class SearchBox extends React.Component {
59
+ submit(event){
60
+ event.preventDefault();
61
+ var query = this.refs.input.value;
62
+ var url = `/?query=${encodeURIComponent(query)}`
63
+ history.pushState(url, '', url);
64
+ this.props.state.initialize();
65
+ }
66
+ click(e){ this.props.state.open(e.target.dataset.url); }
67
+
68
+ full_screen(){
69
+ var elem = document.querySelector("body");
70
+ if (elem.requestFullScreen) {
71
+ elem.requestFullScreen();
72
+ } else if (elem.mozRequestFullScreen) {
73
+ elem.mozRequestFullScreen();
74
+ } else if (elem.webkitRequestFullScreen) {
75
+ elem.webkitRequestFullScreen();
76
+ }
77
+ }
78
+
79
+ componentDidUpdate(){
80
+ var query = decodeURIComponent(this.props.state.controller.query());
81
+ if(query == 'undefined' || query == 'null' || !query){query = ''}
82
+ this.refs.input.value = query;
83
+ }
84
+
85
+
86
+
87
+ change(){ }
88
+
89
+ render(){
90
+ var query = decodeURIComponent(this.props.state.controller.query());
91
+ if(query == 'undefined' || query == 'null' || !query){query = ''}
92
+ var custom_list = $.map(this.props.state.models.custom_list.list, (v,k)=>{return <CustomList key={k} name={k} state={this.props.state}/>});
93
+ return <form onSubmit={(e)=> this.submit(e)}>
94
+ <input ref='input' defaultValue={query} onChange={()=>{this.change()}}/><button>Search</button>
95
+ <a href='javascript:void(0)' onClick={(e)=>this.click(e)} data-url='/latest'>Latest</a>
96
+ <a href='/config'>Config</a>
97
+ <a href='javascript:void(0)' onClick={()=> this.full_screen()}>Full Screen</a>
98
+ <Recommend state={this.props.state} />
99
+ <br />
100
+ {custom_list}
101
+ </form>
102
+ }
103
+ }
104
+
105
+ window.Head = Head;
106
+ window.SearchBox = SearchBox;
@@ -0,0 +1,104 @@
1
+ class CustomLinks extends React.Component{
2
+ render(){
3
+ return <span dangerouslySetInnerHTML={{__html: this.props.item.custom_links}}/>
4
+ }
5
+ }
6
+
7
+ class Item extends React.Component {
8
+ mtime(){
9
+ //moment(this.props.item.mtime)
10
+ return this.props.item.mtime;
11
+ }
12
+
13
+ render(){
14
+ var item = this.props.item;
15
+ var meta = `/meta/${this.props.item.key}`;
16
+ var sub = `/sub/${this.props.item.key}`;
17
+ return <span className='main_span'>
18
+ <NewFlag
19
+ item={this.props.item}
20
+ state={this.props.state}
21
+ />
22
+ <a href={item.url} className='main_link'>{item.name}</a>&nbsp;
23
+
24
+ <Watch
25
+ item={this.props.item}
26
+ state={this.props.state}
27
+ />
28
+ <ChromeCast
29
+ item={this.props.item}
30
+ state={this.props.state}
31
+ />
32
+ <span className='mtime'>{this.mtime()}</span>&nbsp;
33
+ <span className='size'>{size_pretty(item.size)}</span>&nbsp;
34
+ <a className='meta' href={meta}>Meta</a>
35
+ <a className='meta' href={sub}>Sub</a>
36
+ <CustomLinks item={this.props.item} />
37
+ </span>
38
+ }
39
+ }
40
+
41
+ class Pager extends React.Component {
42
+ constructor(props){
43
+ super(props);
44
+ this.page = 1;
45
+ var page = this.props.state.controller.params().page;
46
+ if(page){ this.page = parseInt(page)}
47
+ this.bind();
48
+ }
49
+
50
+ bind(){
51
+ $(window).on("scroll",()=>{
52
+ var scrollHeight = $(document).height();
53
+ var scrollPosition = $(window).height() + $(window).scrollTop();
54
+ if ((scrollHeight - scrollPosition) / scrollHeight === 0) {
55
+ if(this.props.state.controller.can_sort_and_paging()|| location.pathname == '/latest'){
56
+ this.click();
57
+ }
58
+ }
59
+ });
60
+ }
61
+ click(){
62
+ this.page ++;
63
+ if(location.pathname == '/latest'){
64
+ var url = this.props.state.controller.route().api_url += `?page=${this.page}`;
65
+ } else {
66
+ var url = this.props.state.controller.route().api_url += `&page=${this.page}`;
67
+ }
68
+ $.get(url).done((data)=>{
69
+ this.props.state.items = this.props.state.items.concat(data);
70
+ this.props.state.update_state();
71
+ if(location.pathname == '/latest'){
72
+ var new_url = `/latest?page=${this.page}`;
73
+ } else {
74
+ var new_url = `/?query=${this.props.state.controller.query()}&page=${this.page}`;
75
+ }
76
+ history.pushState('', '', new_url);
77
+ });
78
+ }
79
+
80
+ render(){ return <a href='javascript:void(0)' onClick={()=> this.click()}>Next</a> }
81
+ }
82
+
83
+
84
+ class List extends React.Component {
85
+ can_sort(){ return this.props.state.controller.can_sort_and_paging()}
86
+ render(){
87
+
88
+ var items = $.map(this.props.state.items, (item)=>{
89
+ return <Item
90
+ key={item.key}
91
+ state={this.props.state}
92
+ item={item}
93
+ />
94
+ });
95
+
96
+ return <div>
97
+ {this.can_sort() ? <Sort state={this.props.state} /> : null }
98
+ <p>{items}</p>
99
+ <p>{this.can_sort() || location.pathname == '/latest' ? <Pager state={this.props.state} /> : null }</p>
100
+ </div>
101
+ }
102
+ }
103
+
104
+ window.List = List;
@@ -0,0 +1,23 @@
1
+ class NewFlag extends React.Component{
2
+ is_new(){
3
+ if(!!this.props.state.recents){
4
+ var ext = window.ext(this.props.item.name);
5
+ if(ext == 'mp4' || ext == 'flv'){
6
+ var result = true;
7
+ $.each(this.props.state.recents, (k,v)=>{ if(k == `movie/${this.props.item.key}`){ result = false } });
8
+ return result
9
+ } else {
10
+ return false;
11
+ }
12
+ } else {
13
+ return false;
14
+ }
15
+ }
16
+
17
+ render(){
18
+ var is_new = this.is_new();
19
+ return <span>{is_new ? <span className='new_flag' style={{color:'lime', "fontWeight":'bold'}}>New</span> : null }</span>
20
+ }
21
+ }
22
+
23
+ window.NewFlag = NewFlag;
@@ -0,0 +1,171 @@
1
+ class Player extends React.Component {
2
+ constructor(props){
3
+ super(props);
4
+ this.current_url = '';
5
+ }
6
+ close(){ this.props.state.models.video.set(null); }
7
+ dom(){ return this.refs.video; }
8
+
9
+ video(){ return this.props.state.models.video.item; }
10
+
11
+ video_url(){
12
+ var url = null;
13
+ if(!!this.video()){ url = this.video().url; }
14
+ return url;
15
+ }
16
+
17
+ to_play(url){
18
+ if(!!url){
19
+ if(url != this.current_url){
20
+ this.current_url = url;
21
+ return true;
22
+ } else {
23
+ return false;
24
+ }
25
+ } else {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ next(){
31
+ var next = false;
32
+ var result;
33
+ var index = $.map(this.props.state.items, (e,i)=>{return e.key}).indexOf(this.props.state.models.video.item.key);
34
+ this.props.state.models.video.set(this.props.state.items[index-1]);
35
+ }
36
+
37
+ play(prev){
38
+ if(this.to_play(this.video_url())){
39
+ var node = this.dom();
40
+ node.addEventListener('canplay', (e)=>{
41
+ var target = e.target;
42
+ target.play();
43
+ this.props.state.models.recent.use(`movie/${this.video().key}`);
44
+
45
+ });
46
+ node.addEventListener('ended', (e)=>{ this.next(); });
47
+ node.load();
48
+ }
49
+ }
50
+
51
+ size_loop(){
52
+ if(!!this.video_url()){
53
+ var video_width = this.dom().videoWidth;
54
+ var video_height = this.dom().videoHeight;
55
+
56
+ var width = $(window).width();
57
+ var height = $(window).height();
58
+
59
+ var new_w, new_h;
60
+
61
+ if(parseFloat(video_height) / parseFloat(video_width) < parseFloat(height) / parseFloat(width)){
62
+ new_w = width;
63
+ new_h = parseInt((parseFloat(video_height) * (parseFloat(width) / parseFloat(video_width))));
64
+ } else {
65
+ new_w = parseInt((parseFloat(video_width) * (parseFloat(height) / parseFloat(video_height))));
66
+ new_h = height;
67
+ }
68
+ new_w = new_w - 10;
69
+ new_h = new_h - 10;
70
+
71
+ var spacer = (height - new_h) / 2;
72
+
73
+ $(this.dom()).css({width: new_w, height: new_h});
74
+ $(this.refs.spacer).css({height: spacer});
75
+ }
76
+ setTimeout(()=>{ this.size_loop()}, 200)
77
+ }
78
+
79
+ capture(){
80
+ var c = this.refs.canvas;
81
+ var v = this.dom();
82
+ var context = c.getContext('2d');
83
+ c.width = v.videoWidth;
84
+ c.height = v.videoHeight;
85
+ context.drawImage(v, 0, 0);
86
+ return c.toDataURL();
87
+ }
88
+
89
+ seek(count){ this.dom().currentTime += count; }
90
+ pause(){
91
+ this.dom().paused ? this.dom().play() : this.dom().pause();
92
+ }
93
+ gyazo(){
94
+ var url = this.capture();
95
+ $.post('/gyazo', {url: url, point: localStorage.gyazo}).done((data)=>{
96
+ window.open(data.url, "", "width=500,height=400");
97
+ });
98
+ }
99
+ tweet(){ $.post('/gyazo/tweet', {url: this.capture()}).success((data)=>{return true}); }
100
+ comment_tweet(){
101
+ var url = this.capture();
102
+ var comment = prompt('tweet');
103
+ if(!!comment){
104
+ $.post('/gyazo/tweet', {url: url, comment: comment}).success((data)=>{return true});
105
+ }
106
+ }
107
+
108
+
109
+ bind(){
110
+ $(window).keyup((e)=>{
111
+ if(!!$(this.dom()).attr('src')){
112
+ if(document.activeElement.tagName != 'INPUT'){
113
+ switch(e.keyCode){
114
+ case 74:
115
+ this.seek(14);
116
+ break;
117
+ case 75:
118
+ this.seek(-15);
119
+ break;
120
+ case 80:
121
+ this.pause();
122
+ break;
123
+ case 71:
124
+ this.gyazo();
125
+ break;
126
+ case 78:
127
+ this.next();
128
+ break;
129
+ case 67:
130
+ this.comment_tweet();
131
+ break;
132
+ case 84:
133
+ this.tweet();
134
+ break;
135
+ }
136
+ }
137
+ }
138
+ });
139
+ }
140
+
141
+ componentDidMount(){ this.size_loop(); this.bind(); }
142
+ componentDidUpdate(prev, prevState){ this.play(prev); }
143
+ video_name(){
144
+ if(!!this.video_url()){
145
+ return this.video().name;
146
+ } else {
147
+ return null;
148
+ }
149
+ }
150
+
151
+ class_name(){ if(!!this.video_url()){ return "" } else { return "none" } }
152
+
153
+ render(){
154
+ return <div className={this.class_name()} id='area'>
155
+ <div ref='spacer' />
156
+ <video
157
+ ref='video'
158
+ src={this.video_url()}
159
+ controls='controls'
160
+ onClick={()=> this.pause()}
161
+ />
162
+ <canvas className='none' ref='canvas' />
163
+ <div style={{height: '300px'}}/>
164
+ <a href='javascript:void(0)' onClick={()=> this.close()}>Close Video</a>
165
+ <br />
166
+ <span>{this.video_name()}</span>
167
+ </div>
168
+ }
169
+ }
170
+
171
+ window.Player = Player;