Skip to content

Commit

Permalink
cueの挿入機能を復元
Browse files Browse the repository at this point in the history
これで最低限の機能は取り戻せたと思う
  • Loading branch information
Sotalbireo committed May 23, 2019
1 parent a74a39b commit 3a7a191
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 44 deletions.
1 change: 1 addition & 0 deletions .eslintignore
@@ -1,2 +1,3 @@
node_modules/
dist/
src/**/*.min.js
37 changes: 29 additions & 8 deletions README.md
Expand Up @@ -2,7 +2,7 @@

日本語りどみ

**注意:不安定開発版につき、機能はなかったりなかったりする**
**注意:不安定開発版につき、機能はあったりなかったりする**

## これなん

Expand All @@ -17,7 +17,7 @@ YouTube などの動画に字幕として使える「WebVTT」ファイルデー
### とりあえず使いたい人

```bash
cd ~ && git clone https://github.com/Sotalbireo/WebVTT-Editor.git && yarn
cd ~ && git clone https://github.com/Sotalbireo/WebVTT-Editor.git && yarn && yarn dev
```

### こだわりのある人
Expand All @@ -37,6 +37,8 @@ cd ~ && git clone https://github.com/Sotalbireo/WebVTT-Editor.git && yarn

### キーボードショートカット

#### 動画ビューワ

key | action
:---:|---
Space | 再生・一時停止
Expand All @@ -48,21 +50,40 @@ x | 動画を10秒戻す
c | 動画を10秒送る
v | 動画を1分送る
Shift-v | 動画を10分送る
Ctrl-s | 字幕ファイルを上書き保存して反映させる

#### 字幕エディタ(Rawモード)

key | action
:---:|---
Ctrl-s | 字幕ファイルを上書き保存して動画ビューワに反映させる
y | 直前に入力したキュー操作を取り消す(最大10回)
j | 字幕ファイル末尾に開始キューを打つ
k | 字幕ファイル末尾に終了キューと開始キューを連続して打つ
l | 字幕ファイル末尾に終了キューを打つ

## 動作確認環境

以下の環境では動作を確認しています。

* macOS mojave (10.14.3) / Node 10.15.x
* Windows 10 Home (1809)
* macOS mojave (10.14.3)

## 開発環境

よその環境を試していないので、問題があったら揃えてみてください。

* macOS mojave (10.14.3)
* Node 10.15.1 (LTS)
* yarn 1.13.0
* Node ^10.15.1 (LTS)
* yarn ^1.13.0

## 将来の予定

将来的に実装したい機能などは以下です。
これらは今後の実装を保証するものではなく、また予告なく変更・削除の恐れがあります。

- グラフィカルエディタ
- 別途用意した字幕原稿とのマージ
- linter
- YouTube, Vimeo 等、動画プラットフォーム風字幕スタイルシート
- 音声解析によるタイムスタンプ自動挿入

## 参考

Expand Down
10 changes: 8 additions & 2 deletions electron-builder.json
@@ -1,5 +1,5 @@
{
"appId": "jp.siky.webvtt-editor",
"appId": "jp.siky.webvtt-editor",
"files": [
{
"from": "dist",
Expand All @@ -15,5 +15,11 @@
"package.json"
]
}
]
],
"mac": {
"target": "dmg"
},
"win": {
"target": "portable"
}
}
4 changes: 1 addition & 3 deletions nuxt.config.ts
Expand Up @@ -24,9 +24,7 @@ const nuxtConfig: NuxtConfiguration = {

build: {
extend(config, { isClient, isDev }) {
if (isClient) {
config.target = 'electron-renderer'
}
config.target = 'electron-renderer'
if (isDev && isClient) {
config.module!.rules.push({
enforce: 'pre',
Expand Down
18 changes: 10 additions & 8 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "webvtt-editor",
"version": "0.2.1",
"version": "0.2.2",
"author": "sasaky <athene609.cyl@gmail.com>",
"description": "The editor to create WebVTT how subtitles at html5 video.",
"keywords": [
Expand All @@ -17,18 +17,20 @@
},
"bugs": "http://github.com/sotalbireo/webvtt-editor/issues",
"main": "dist/main",
"engines": {
"node": "^10.15.3"
},
"scripts": {
"nuxt:generate": "nuxt generate .",
"nuxt:dev": "nuxt dev .",
"tsc:build": "tsc --build tsconfig.main.json",
"tsc:watch": "tsc --build tsconfig.main.json --watch",
"build:dist": "yarn nuxt:generate && yarn tsc:build",
"build:pack": "electron-builder",
"build:pack": "electron-builder -mw",
"build": "yarn build:dist && yarn build:pack",
"dev": "yarn tsc:build && concurrently -k -n tsc,main,renderer -c cyan,cyan,green \"yarn tsc:watch\" \"cross-env NODE_ENV=development electron .\" \"yarn nuxt:dev\"",
"lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .",
"precommit": "yarn lint",
"post-update": "yarn upgrade --latest"
"lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore --ignore-path .eslintignore .",
"precommit": "yarn lint"
},
"dependencies": {
"@nuxt/typescript": "^2.6.1",
Expand All @@ -48,7 +50,7 @@
"devDependencies": {
"@nuxtjs/eslint-config": "^0.0.1",
"@types/mousetrap": "^1.6.2",
"@types/node": "^11.13.0",
"@types/node": "^12.0.2",
"@typescript-eslint/eslint-plugin": "^1.6.0",
"blob-util": "^2.0.2",
"concurrently": "^4.1.0",
Expand All @@ -60,11 +62,11 @@
"eslint-loader": "^2.1.2",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-node": "^9.0.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.2.2",
"fibers": "^3.1.1",
"fibers": "^4.0.1",
"mousetrap": "^1.6.3",
"sass": "^1.17.4",
"sass-loader": "^7.1.0",
Expand Down
15 changes: 13 additions & 2 deletions src/renderer/components/MainContainer.vue
Expand Up @@ -2,7 +2,9 @@
<div class="ui centered grid">
<div id="UpperContent" class="row">
<template v-if="hasVideoPath">
<VideoViewer />
<VideoViewer
ref="video"
/>
</template>
<template v-else>
<FileGetter
Expand All @@ -13,7 +15,10 @@
</div>
<div id="LowerContent" class="row">
<template v-if="hasSubtitles">
<RawEditor />
<RawEditor
ref="editor"
@get-videos-currentpos="getVideosCurrentPos"
/>
</template>
<template v-else>
<FileGetter
Expand Down Expand Up @@ -44,6 +49,12 @@ const nsSubtitle = namespace('subtitle')
export default class MainContainer extends Vue {
@Getter hasVideoPath
@nsSubtitle.Getter hasSubtitles
videoCurrentTime = 0
getVideosCurrentPos() {
(this.$refs.editor as RawEditor).videosCurrentPos = (this.$refs.video as VideoViewer).readableCurrentTime()
}
}
</script>

Expand Down
25 changes: 23 additions & 2 deletions src/renderer/components/SideNavigation.vue
@@ -1,5 +1,23 @@
<template>
<div />
<div
class="ui vertical tertiary segment"
>
<div class="ui centered grid">
<div class="row">
<button class="big ui icon compact button" disabled>
<i class="file alternate icon" />
</button>
</div>
<div class="row">
<button
class="big ui icon compact button"
disabled
>
<i class="building outline icon" />
</button>
</div>
</div>
</div>
</template>

<script lang="ts">
Expand All @@ -9,4 +27,7 @@ import { Component, Vue } from 'vue-property-decorator'
export default class SideNavigation extends Vue {}
</script>

<style lang="sass" scoped></style>
<style lang="sass" scoped>
button
margin-right: 0
</style>
15 changes: 13 additions & 2 deletions src/renderer/components/VideoViewer.vue
Expand Up @@ -64,8 +64,19 @@ export default class VideoViewer extends Vue {
/**
* Gets the current playback position, in seconds.
*/
get currentTime() : number {
return this.videoElement!.currentTime
currentTime = () => this.videoElement!.currentTime
/**
* Gets the current playback position, in human readable format.
* (HH:MM:SS.sss)
*/
readableCurrentTime = () => {
const t = this.videoElement!.currentTime
const h = ('0' + Math.floor(t / 3600) % 60).slice(-2)
const m = ('0' + Math.floor(t / 60) % 60).slice(-2)
const s = ('0' + Math.floor(t) % 60).slice(-2)
const sss = ('00' + Math.floor(t % 10 * 1000)).slice(-3)
return `${h}:${m}:${s}.${sss}`
}
/**
Expand Down
32 changes: 32 additions & 0 deletions src/renderer/components/modals/SelectPlatform.vue
@@ -0,0 +1,32 @@
<template>
<div class="ui mini modal">
<div class="header">
再生プラットフォームを選択する
</div>
<div class="content">
<div class="grouped fields">
<label for="platform">それっぽいデザインでシミュレートできます</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="platform" checked="" tabindex="0" class="hidden">
<label>YouTube</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="platform" tabindex="0" class="hidden">
<label>Vimeo</label>
</div>
</div>
</div>
</div>
<div class="actions">
<div class="ui approve button">
Approve
</div>
<div class="ui cancel button">
Cancel
</div>
</div>
</div>
</template>
68 changes: 53 additions & 15 deletions src/renderer/components/subtitles/RawEditor.vue
Expand Up @@ -4,11 +4,10 @@
<div class="field">
<textarea
id="RawEditor"
v-model="rawString"
name=""
autocomplete="off"
rows=""
:value="vtt"
class="mousetrap"
/>
</div>
</div>
Expand All @@ -23,27 +22,64 @@ import Mousetrap from 'mousetrap'
const nsSubtitle = namespace('subtitle')
/* eslint no-console:0 */
@Component
export default class RawEditor extends Vue {
@Getter hasSubtitles
@nsSubtitle.Getter subtitles
@nsSubtitle.Action save
@nsSubtitle.Action update
rawString = ''
videosCurrentPos = ''
insertedNotesLength: number[] = []
vtt = ''
/**
* Insert note
* (Replace from "<timestamp>" to Videos current playback position)
*/
insertNote(str: string) {
this.$emit('get-videos-currentpos')
const note = str.replace(/<timestamp>/g, this.videosCurrentPos)
if (this.insertedNotesLength.length > 9) {
this.insertedNotesLength.splice(0, 1)
}
this.insertedNotesLength.push(note.length)
this.rawString += note
}
mounted() {
this.vtt = readFileSync(this.subtitles[0].path, 'utf8')
Mousetrap.bind(
['ctrl+s', 'command+s'],
(e) => {
e.stopPropagation()
e.preventDefault()
const newdata = (<HTMLTextAreaElement>document.getElementById('RawEditor')!).value
this.save({ newdata })
})
this.rawString = readFileSync(this.subtitles[0].path, 'utf8').trim()
/**
* Save vtt file
*/
Mousetrap.bind(['ctrl+s', 'command+s'], () => {
this.save({ newdata: this.rawString })
this.update()
})
/**
* Insert begin cue
*/
Mousetrap.bind('j', () => this.insertNote((this.rawString.slice(-1) !== `\n` ? `\n` : ``) + `\n<timestamp> -->`))
/**
* Insert end cue & next begin cue
*/
Mousetrap.bind('k', () => this.insertNote(` <timestamp>\n[subtitle]\n\n<timestamp> -->`))
/**
* Insert end cue and provisional note
*/
Mousetrap.bind('l', () => this.insertNote(` <timestamp>\n[subtitle]\n`))
/**
* Delete insert value
*/
Mousetrap.bind('y', () => {
if (this.insertedNotesLength.length === 0) return
this.rawString = this.rawString.slice(0, -1 * this.insertedNotesLength.splice(-1)[0])
})
}
}
</script>
Expand All @@ -56,4 +92,6 @@ export default class RawEditor extends Vue {
min-height: 100%
textarea
resize: none
&:focus
box-shadow: 0 0 6px 2px #85b7d9 !important
</style>

0 comments on commit 3a7a191

Please sign in to comment.