iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
0
Modern Web

弄點簡單的 Chrome Extension 讓生活更方便系列 第 26

Chrome Extension 26 - Youtube 筆記功能整理

  • 分享至 

  • xImage
  •  

從頭開始一步一步將 youtube 筆記功能做一下整理

由於有使用 vue ,所以下載 Vue 的範本使用 :

$ npm install -g vue-cli
$ vue init YuraDev/vue-chrome-extension-template my-project
$ cd my-project
$ npm install
$ npm run dev

該範本有使用 eslint ,如果要避免嚴格規則的錯誤,可以到 core\webpack.base.jsrule 底下註解掉 :
https://ithelp.ithome.com.tw/upload/images/20181101/20094223Xspz21eheG.png

下載 JQuery 檔案,並放到 src 的 lib 資料夾

在 core 找到 webpack.base.js 的 entry 添加 jquery : resolve('./lib/jquery-3.3.1.min.js')

  entry: {
    tab: resolve('./tab'),
    popup: resolve('./popup'),
    options: resolve('./options'),
    content: resolve('./content'), 
    devtools: resolve('./devtools'),
    background: resolve('./backend'),
    panel: resolve('./devtools/panel'),
    inject: resolve('./content/inject'),
    jquery : resolve('./lib/jquery-3.3.1.min.js'),
  },

manifest.js 的 content_scripts 修正為 :

  content_scripts: [{
    js: [ 'js/jquery.js','js/content.js' ],
    run_at: 'document_end',
    matches: ['https://www.youtube.com/*'],
    all_frames: true
  }],

修改 content\index.js

  • 利用 JQuery 確認元素是否建立後才執行 vue
import Vue from 'vue'
import root from './content.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false
Vue.use(ElementUI)

var vueLoop = setInterval(function() {

  if($('#related').length > 0 && $('#items').length){
    $('<div id="root"></div').prependTo( "#related");
    new Vue({
      el: '#root',
      render: h => h(root)
    });

    clearTimeout(vueLoop);
  }

  }, 500);

在 content 資料夾底下建立 content.vue
content.vue 內容 :
https://ithelp.ithome.com.tw/upload/images/20181110/20094223S1g3vUze0G.png

其他備註 - 資料儲存發現無法使用變數來存,導致資料存取有問題,所以採取全讀的方式 :

var myName ="helloData";
chrome.storage.sync.set({ myName: "123" }, function() {});

儲存的 key 會是 myName 而不是 helloData

chrome.storage.sync.get("helloData", function(result) {
    console.log(result);
})
chrome.storage.sync.get("myName", function(result) {
    console.log(result);
})

https://ithelp.ithome.com.tw/upload/images/20181110/20094223maZw0WL18c.png

其他備註 - 點選其他影片時不會重載網頁,所以使用迴圈檢查影片參數(v) :

        showTime(){
            var vm = this;
            (function() {
                let animationTimer = setInterval(function() {

                    var videoKey = vm.getParameter('v');

                    if (videoKey != vm.videoKey) {
                        vm.videoKey = videoKey;
                        vm.items = [];
                        vm.loadData();
                    }
                    var video = document.getElementsByClassName('video-stream')[0];
                    vm.isPaused = video.paused;

                    var t = Math.floor(video.currentTime);

                    if (vm.currentTime !== t) {
                        vm.currentTime = t;
                    }

                }, 500);
            })();
        }

其他備註 - youtube 影片使用 Html5 Video 所以可以用它來操作影片相關功能 :

var video = document.getElementsByClassName('video-stream')[0];
video.currentTime = time; //指定時間
video.pause(); //暫停
video.play(); //播放
video.paused; //狀態,是否暫停

content.vue 的 templdate 部分 :

<template>
    <el-container>
        <el-main>
            <el-row v-show="!isPaused">
                <el-col>
                    <el-button type="primary" @click="play()">輸入訊息</el-button>
                </el-col>
            </el-row>
            <el-row v-show="isPaused">
                <el-col>
                    <el-button type="success" @click="addData()">儲存</el-button>
                    <el-button type="primary" @click="play()">繼續撥放</el-button>
                </el-col>
            </el-row>
            <el-row v-show="isPaused">
                <el-col>
                    <el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="currentMessage"></el-input>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="16">
                    <ul v-for="(item,index) in items">
                        <li>
                          <el-button type="danger" size="mini" icon="el-icon-delete" circle @click=deleteItem(index)></el-button>
                          <el-button size="mini" @click="gotoTime(item.time)">{{getMinSec(item.time)}} - {{item.message}}</el-button> 
                        </li>
                    </ul>
                </el-col>
            </el-row>
        </el-main>
    </el-container>
</template>

content.vue 的 script 部分 :

<script>
export default {
    data: () => ({
        currentTime: 0,
        currentMessage: '',
        items: [],
        videoKey: '',
        isPaused: false
    }),
    computed: {},
    created() {},
    mounted() {
        this.videoKey = this.getParameter('v');

        this.showTime();

        this.loadData();
    },
    updated() {

    },
    computed: {

    },

    methods: {
        play() {
            document.querySelector('.ytp-play-button').click();
        },
        getMinSec(t) {
            return Math.floor(t / 60) + '分' + (t % 60) + '秒';
        },
        gotoTime(time){
            var video = document.getElementsByClassName('video-stream')[0];
            video.currentTime = time;
            video.pause();
        },
        showTime(){
            var vm = this;
            (function() {
                let animationTimer = setInterval(function() {

                    var videoKey = vm.getParameter('v');

                    if (videoKey != vm.videoKey) {
                        vm.videoKey = videoKey;
                        vm.items = [];
                        vm.loadData();
                    }
                    var video = document.getElementsByClassName('video-stream')[0];
                    vm.isPaused = video.paused;

                    var t = Math.floor(video.currentTime);

                    if (vm.currentTime !== t) {
                        vm.currentTime = t;
                    }

                }, 500);
            })();
        },
        loadData() {
            var vm = this;
            chrome.storage.sync.get("tempData", function(result) {

                if (result !== undefined) {
                    var tempData = result.tempData;

                    var jsonResult = [];
                    if (tempData !== undefined) {
                        jsonResult = tempData;
                    }

                    jsonResult.forEach(function(video) {
                        if (video.videoKey == vm.videoKey) {
                            vm.items = video.items;
                        }
                    })
                }
                if (vm.items === undefined) {
                    vm.items = [];
                }
            });
        },
        addData() {
            var vm = this;

            var filterByTime = vm.items.filter(function(item, index, array) {
                return item.time == vm.currentTime;
            });
            if (filterByTime.length > 0) {
                vm.items.forEach(function(item, index, array) {
                    if (item.time == vm.currentTime) {
                        item.message = vm.currentMessage;
                    }
                });
            } else {
                var item = {
                    time: vm.currentTime,
                    message: vm.currentMessage
                }
                vm.items.push(item);
            }

            vm.saveData();
        },
        saveData(){
            var vm = this;
            chrome.storage.sync.get("tempData", function(result) {
                var tempData = result.tempData;

                var jsonResult = [];
                if (tempData !== undefined) {
                    jsonResult = tempData;
                }

                var isExist = false;
                jsonResult.forEach(function(video) {
                    if (video.videoKey == vm.videoKey) {
                         video.items = vm.items;
                        isExist = true;
                    }
                });
                if (!isExist) {
                    var videoItem = {
                        videoKey: vm.videoKey,
                        items: vm.items
                    };

                    jsonResult.push(videoItem);
                }

                chrome.storage.sync.set({ "tempData": jsonResult }, function() {});
            });
        },
        deleteItem(index){
             var vm = this;
             vm.items.splice(index,1);
             vm.saveData();
        },
        getParameter(name, url) {
            if (!url) url = window.location.href;
            name = name.replace(/[\[\]]/g, "\\$&");
            var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, " "));
        }
    },
    watch: {
        currentTime: function(newValue, oldValue) {
            var vm = this;

            var filterA = vm.items.filter(function(item, index, array) {
                return item.time == newValue;
            });
            if (filterA.length > 0) {
                vm.currentMessage = filterA[0].message;
            } else {
                vm.currentMessage = '';
            }

        }
    }
}
</script>

執行結果 - 最終完成可以在網頁上增加類似影片標籤的功能 :

https://blog.givemin5.com/wp-content/uploads/2018/11/HyperCamVideos0011.gif

感謝收看 :)


上一篇
Chrome Extension 25 - Youtube 筆記功能修正存取與樣式
下一篇
Chrome Extension 27 - Youtube 在 option 頁面帶出影片資料
系列文
弄點簡單的 Chrome Extension 讓生活更方便30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言