diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/icons/icon.png b/icons/icon.png new file mode 100644 index 0000000..387038a Binary files /dev/null and b/icons/icon.png differ diff --git a/package.json b/package.json index f4b3378..15eadf7 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "yui", "version": "0.0.1", - "description": "Electron製のYouTubeプレイヤー「結衣」", + "description": "Electron製のYouTubeプレイヤー「Shuvi」", "main": "src/bin/js/bin.js", "scripts": { "start": "electron .", "build": "gulp", "watch": "gulp watch", - "package:mac": "electron-packager . 結衣 --out=__release__ --platform=darwin --arch=x64 --icon=icons/icon.icns --overwrite", - "package:win": "electron-packager . 結衣 --out=__release__ --platform=win32 --arch=x64 --icon=icons/icon.ico --overwrite", - "package:linux": "electron-packager . 結衣 --out=__release__ --platform=linux --arch=x64 --icon=icons/icon.icns --overwrite" + "package:mac": "electron-packager . Shuvi --out=__release__ --platform=darwin --arch=x64 --icon=icons/icon.icns --overwrite", + "package:win": "electron-packager . Shuvi --out=__release__ --platform=win32 --arch=x64 --icon=icons/icon.ico --overwrite", + "package:linux": "electron-packager . Shuvi --out=__release__ --platform=linux --arch=x64 --icon=icons/icon.icns --overwrite" }, "repository": { "type": "git", @@ -17,15 +17,15 @@ }, "keywords": [ "Electron", - "yui", + "Shuvi", "YouTube" ], "author": "yuki540", "license": "MIT", "bugs": { - "url": "https://github.com/yuki540net/yui/issues" + "url": "https://github.com/yuki540net/Shuvi/issues" }, - "homepage": "https://github.com/yuki540net/yui#readme", + "homepage": "https://github.com/yuki540net/Shuvi#readme", "dependencies": { "coffee-script": "^1.12.5", "electron": "^1.6.6", diff --git a/src/config/window.json b/src/config/window.json index f4e808c..aac3fad 100644 --- a/src/config/window.json +++ b/src/config/window.json @@ -1,6 +1,6 @@ { - "width" : 450, - "height" : 300, + "width" : 570, + "height" : 380, "minWidth" : 450, "minHeight" : 300, "frame" : false, diff --git a/src/images/submit.png b/src/images/submit.png new file mode 100644 index 0000000..0f9af3a Binary files /dev/null and b/src/images/submit.png differ diff --git a/src/scripts/app.coffee b/src/scripts/app.coffee index 26c100c..4d9ad9d 100644 --- a/src/scripts/app.coffee +++ b/src/scripts/app.coffee @@ -36,10 +36,38 @@ window.addEventListener 'resize', -> youtube.resize width, height +# preload ------------------------------------------------------ +observer.on 'preload', (params) -> + history = JSON.parse localStorage['history'] + + for val, i in history + if not val + continue + if val.id is params.id + history.splice i, 1 + + history.unshift params + + if history.length > 100 + history.splice (history.length - 1), 1 + + localStorage['id'] = params.id + localStorage['history'] = JSON.stringify history + ## # 初期化 ## top.init = -> + # 初期動画 + id = 'fQN2WC_Acpg' + title = 'https://i.ytimg.com/vi/fQN2WC_Acpg/default.jpg' + thumb = '映画『ノーゲーム・ノーライフ ゼロ』 PV 第1弾' + if not localStorage['id'] - localStorage['id'] = 'MGt25mv4-2Q' + localStorage['id'] = id + localStorage['history'] = JSON.stringify([{ + id : id + title : title + thumb : thumb + }]) diff --git a/src/scripts/component/tag/application.tag b/src/scripts/component/tag/application.tag index fdf7a34..f394164 100644 --- a/src/scripts/component/tag/application.tag +++ b/src/scripts/component/tag/application.tag @@ -1,7 +1,10 @@ application( style="width:{ width }px;height:{ height }px" ) - title-bar + title-bar( + width="{ width }" + height="{ height }" + ) controls( width="{ width }" height="{ height }" @@ -35,9 +38,31 @@ application( @update() # mouse over ---------------------------------------- + timer = null window.addEventListener 'mouseover', (e) => + clearTimeout timer + observer.trigger 'show' + + timer = setTimeout => + observer.trigger 'hidden' + , 3000 + + window.addEventListener 'mousemove', (e) => + clearTimeout timer observer.trigger 'show' + timer = setTimeout => + observer.trigger 'hidden' + , 3000 + + window.addEventListener 'click', (e) => + clearTimeout timer + observer.trigger 'show' + + timer = setTimeout => + observer.trigger 'hidden' + , 3000 + # mouse out ----------------------------------------- window.addEventListener 'mouseout',(e) => observer.trigger 'hidden' \ No newline at end of file diff --git a/src/scripts/component/tag/history.tag b/src/scripts/component/tag/history.tag index d0a71fa..7763891 100644 --- a/src/scripts/component/tag/history.tag +++ b/src/scripts/component/tag/history.tag @@ -1,33 +1,96 @@ history( style="height:{ height }px" ) + div.li( + each="{ history }" + data-state="{ state }" + data-id="{ id }" + onclick="{ onSelect }" + ) + div.thumb(style="background-image:url({ thumb })") + div.title { title } style(scoped). :scope { position: absolute; top: 0; - right: -200px; + right: -301px; display: block; - width: 200px; + width: 300px; background-color: #222; + overflow: auto; + border-left: solid 1px #c85399; z-index: 10; } :scope[data-state="active"] { animation: show 0.3s ease 0s forwards; } :scope[data-state="passive"] { animation: hidden 0.3s ease 0s forwards; } @keyframes show { - 0% { right: -200px; } + 0% { right: -301px; } 100% { right: 0px; } } @keyframes hidden { 0% { right: 0px; } - 100% { right: -200px; } + 100% { right: -301px; } + } + :scope .li { + width: 280px; + height: 45px; + background-color: #444; + margin: 10px auto; + overflow: hidden; + border-radius: 3px; + cursor: pointer; + } + :scope .li[data-state="true"] { + background-color: #c85399; + } + :scope .li[data-state="true"] .title { + color: #fff; + } + :scope .li:after { + content: ""; + display: block; + clear: both; + } + :scope .li .thumb { + float: left; + width: 80px; + height: 45px; + background-size: cover; + background-position: center; + } + :scope .li .title { + float: right; + width: 200px; + height: 45px; + font-size: 10px; + padding: 0 5px; + box-sizing: border-box; + color: #ccc; + line-height: 45px; + white-space: nowrap; } script(type="coffee"). + ## + # 履歴更新 + ## + @reload = -> + id = localStorage['id'] + history = JSON.parse localStorage['history'] + for val, i in history + if val.id is id + history[i].state = true + else + history[i].state = false + + @history = history + # mount --------------------------------------------- @on 'mount', -> @height = parseInt opts.height + @reload() @update() # resize -------------------------------------------- @@ -35,6 +98,17 @@ history( @height = params.height @update() + # select -------------------------------------------- + @onSelect = (e) -> + id = e.path[1].getAttribute 'data-id' + youtube.getInfo id, (params) => + youtube.change params.id + + # load ---------------------------------------------- + observer.on 'load', => + @reload() + @update() + # open menu ----------------------------------------- observer.on 'open-menu', => @root.setAttribute 'data-state', 'active' diff --git a/src/scripts/component/tag/info.tag b/src/scripts/component/tag/info.tag index e171021..56728b2 100644 --- a/src/scripts/component/tag/info.tag +++ b/src/scripts/component/tag/info.tag @@ -65,7 +65,7 @@ info bottom: 0; width: 5px; height: 2px; - background-color: #E27171; + background-color: #c85399; } :scope .art[data-state="active"] .line1 div { animation: art 2s ease 0s infinite; } :scope .art[data-state="active"] .line2 div { animation: art 2s ease 0.3s infinite; } @@ -94,8 +94,8 @@ info script(type="coffee"). - # load ---------------------------------------------- - observer.on 'load', (params) => + # preload ------------------------------------------- + observer.on 'preload', (params) => @title = params.title @update() diff --git a/src/scripts/component/tag/play.tag b/src/scripts/component/tag/play.tag index 161fed6..fedd99e 100644 --- a/src/scripts/component/tag/play.tag +++ b/src/scripts/component/tag/play.tag @@ -33,18 +33,30 @@ play script(type="coffee"). - ## - # 再生・停止 - ## + @active = -> + @root.children[0].setAttribute 'data-state', 'active' + + @passive = -> + @root.children[0].setAttribute 'data-state', '' + + # play or pause ------------------------------------- @onPlay = (e) -> state = e.target.getAttribute 'data-state' if state is 'active' - e.target.setAttribute 'data-state', '' + @passive youtube.pause() else - e.target.setAttribute 'data-state', 'active' + @active youtube.play() + # play ---------------------------------------------- + observer.on 'play', => + @active() + + # puase --------------------------------------------- + observer.on 'pause', => + @passive() + # mount --------------------------------------------- @on 'mount', -> diff --git a/src/scripts/component/tag/seek.tag b/src/scripts/component/tag/seek.tag index d275011..6b1aecb 100644 --- a/src/scripts/component/tag/seek.tag +++ b/src/scripts/component/tag/seek.tag @@ -47,7 +47,7 @@ seek top: 0; left: 0; height: 5px; - background-color: #E27171; + background-color: #c85399; } :scope .seek .load { position: absolute; @@ -61,7 +61,7 @@ seek top: -6.5px; width: 8px; height: 20px; - background-color: #E27171; + background-color: #c85399; border-radius: 2px; cursor: pointer; } diff --git a/src/scripts/component/tag/title-bar.tag b/src/scripts/component/tag/title-bar.tag index ae97b00..d8c127e 100644 --- a/src/scripts/component/tag/title-bar.tag +++ b/src/scripts/component/tag/title-bar.tag @@ -1,5 +1,9 @@ title-bar div.close(onclick="{ onClose }") + div.inner(style="width:{ width - 60 }px") + div.form + input#movie-url(type="text", placeholder="YouTubeの動画URLを入力してください。") + div#submit(onclick="{ onSubmit }") div.menu(onclick="{ onMenu }") style(scoped). @@ -21,12 +25,53 @@ title-bar display: block; clear: both; } + :scope .inner { + float: left; + height: 30px; + } + :scope .inner .form { + width: 390px; + height: 30px; + margin: 0 auto; + background-color: #333; + -webkit-app-region: no-drag; + } + :scope .inner .form:after { + content: ""; + display: block; + clear: both; + } + :scope .inner .form #movie-url { + float: left; + width: 350px; + height: 30px; + font-size: 11px; + color: #c85399; + background-color: transparent; + padding: 0 10px; + box-sizing: border-box; + } + :scope .inner .form #movie-url::-webkit-input-placeholder { + color: #999; + } + :scope .inner .form #movie-url:focus { + outline: none; + } + :scope .inner .form #submit { + float: right; + width: 40px; + height: 30px; + background-image: url(../../images/submit.png); + background-position: center; + background-size: auto 60%; + background-repeat: no-repeat; + } :scope .close { position: relative; float: left; width: 30px; height: 30px; - background-color: #444; + -webkit-app-region: no-drag; } :scope .close:before, :scope .close:after { @@ -37,7 +82,7 @@ title-bar display: block; width: 20px; height: 1px; - background-color: #E27171; + background-color: #c85399; } :scope .close:before { transform: rotate(45deg); } :scope .close:after { transform: rotate(-45deg); } @@ -46,7 +91,7 @@ title-bar float: right; width: 30px; height: 30px; - background-color: #444; + -webkit-app-region: no-drag; } :scope .menu:after, :scope .menu:before { @@ -56,7 +101,7 @@ title-bar display: block; width: 15px; height: 1px; - background-color: #E27171; + background-color: #c85399; } :scope .menu:after { top: 9.5px; } :scope .menu:before { top: 19px; } @@ -87,9 +132,34 @@ title-bar @onMenu = -> observer.trigger 'open-menu' - # mount --------------------------------------------- - @on 'mount', -> + # submit -------------------------------------------- + @onSubmit = -> + url = @root.children[1].children[0].children[0].value + url = url.replace /(\s|<|>)+/g, '' + + @root.children[1].children[0].children[0].value = '' + + id = youtube.getId url + if not id + alert 'URLが不正です。' + return + youtube.getInfo id, (params) => + if not params + alert 'URLが不正です。' + return + youtube.change params.id + # mount --------------------------------------------- + @on 'mount', -> + @width = parseInt opts.width + @height = parseInt opts.height + @update() + # resize -------------------------------------------- + observer.on 'resize', (params) => + console.log params + @width = parseInt params.width + @height = parseInt params.height + @update() diff --git a/src/scripts/component/tag/volume.tag b/src/scripts/component/tag/volume.tag index ee1bca1..4ca92ce 100644 --- a/src/scripts/component/tag/volume.tag +++ b/src/scripts/component/tag/volume.tag @@ -39,7 +39,7 @@ volume :scope .volume .bar { position: absolute; height: 3px; - background-color: #E27171; + background-color: #c85399; } :scope .volume .picker { position: absolute; diff --git a/src/scripts/youtube.coffee b/src/scripts/youtube.coffee index a0c28a8..7008d2c 100644 --- a/src/scripts/youtube.coffee +++ b/src/scripts/youtube.coffee @@ -28,7 +28,9 @@ class YouTube events : { onReady: (e) => @load = true - observer.trigger 'load', params + youtube.play() + observer.trigger 'play' + observer.trigger 'load' } } @change id @@ -39,17 +41,24 @@ class YouTube # @param fn : コールバック関数 ## getInfo: (id, fn) -> + youtube.pause() + observer.trigger 'pause' + @api.search id, 1, (err, result) => items = result.items if not items.length fn false - enty = items[0].snippet - fn + enty = items[0].snippet + params = + id : id title : enty.title thumb : enty.thumbnails.default.url + observer.trigger 'preload', params + fn params + ## # 動画の変更 # @param id : 動画ID @@ -88,10 +97,18 @@ class YouTube # 再生 ## play: -> + if not @load + return + @player.playVideo() observer.trigger 'play' @timer = setInterval => + duration = @duration() + current = @current() + if duration <= current + @seek 0 + observer.trigger 'seek' , 100 @@ -99,6 +116,9 @@ class YouTube # 停止 ## pause: -> + if not @load + return + @player.pauseVideo() observer.trigger 'pause' diff --git a/src/views/jade/player.jade b/src/views/jade/player.jade index 78a7c67..2310be6 100644 --- a/src/views/jade/player.jade +++ b/src/views/jade/player.jade @@ -4,7 +4,7 @@ html meta(charset="utf-8") link(rel="stylesheet", href="../../stylesheets/font.css") link(rel="stylesheet", href="../../stylesheets/base.css") - title 結衣 + title Shuvi script(src="https://www.youtube.com/iframe_api") script. require('coffee-script/register')