📜  Chrome 扩展程序 – Youtube 书签

📅  最后修改于: 2021-10-29 06:27:05             🧑  作者: Mango

在本文中,我们将开发一个 chrome 扩展,用户可以使用它创建对应于不同时间戳的书签,将它们存储在某处(目前在 chrome 本地存储中),并检索书签(存储为带时间戳的 youtube 视频链接)。

代码托管在这里:GitHub。主扩展的视频解释器可以在这里找到(在主扩展的 repo 中)。请注意,在撰写本文时,该项目仍在进行中。

让我们开始吧。从广义上讲,我们将这篇文章分为 3 个部分。在第一部分中,我们将连接扩展程序,以便我们可以在 Chrome 中访问我们正在开发的扩展程序。在第二部分,我们将看看一个基本的扩展,并讨论代码和架构。在第三部分,我们将开发主要扩展。

1. 在开发过程中测试我们的扩展

为了在开发阶段测试和调试我们的扩展,我们将按照以下步骤启动和运行基本扩展。

  1. 创建如上所示的 manifest.json 文件和项目结构,或从文章顶部的链接下载它们。
  2. 从浏览器转到 chrome://extensions。
  3. 将“开发人员模式”切换为“开”。
  4. 单击“加载未打包”选项以加载扩展文件夹以进行测试、调试和进一步开发。
  5. 此时,您应该会在 chrome 地址栏的右侧看到您的扩展程序。

浏览器屏幕片段显示用于设置功能扩展的各种选项

现在我们已经在 chrome 上加载了我们的扩展文件夹,我们准备构建一个基本的 chrome 扩展并测试它。

2. 基本的 Chrome 扩展 – 你好!

我们将构建一个扩展,它会说:“嗨,那里!”当用户单击扩展图标时。代码可以在这里找到。

单击“H”图标会打开 popup.html,并显示“嗨,在那里!”信息

为此,我们需要以下文件。

  • manifest.json – 保存有关 Chrome 扩展程序的信息。
  • popup.html – 说,“嗨,那里!”当用户点击扩展图标时。
  • popup.js – 此时此文件没有重要的工作。虽然,我们会把它留在这里。
  • background.js – 加载具有基本后端功能的扩展。不需要的嗨,有!延期。
  • jQuery.js – 我们将包含 jQuery 来帮助开发。

1. manifest.json 文件

{
"name": "Hi, there! Extension",
"version": "1.0",
"description": "I greet",
"permissions": ["activeTab"],
"options_page": "options.html",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_title": "Hi, there Extension"
},
"manifest_version": 2
}

让我们更详细地查看 manifest.json 文件中的键值对。

  1. name – 这是扩展的名称。在浏览器屏幕快照中,您可以看到“嗨,那里!延期。
  2. version – 这是扩展的版本。它被认为是 1.0,因为当我们将它上传到 chrome 网上商店进行审核时,它最初是 1.0。在开发阶段,您可以将其命名为 0.0,然后是 0.1,依此类推。
  3. description – 扩展的描述。保持简洁是一个很好的做法。
  4. 权限– 权限键值对包含扩展需要访问才能正常工作的不同权限。该值是一个字符串数组。数组的元素可以是已知字符串,也可以是模式匹配(通常用于匹配网址)。例如,https://youtube.com/* -> 授予对域名为 youtube.com 的所有链接的扩展名的权限。此处,“*”是通配符模式。另请注意,此处声明的某些权限可能也需要用户的明确批准。您可以在官方文档中阅读有关权限的更多信息。
  5. options_page – 选项页面用于为用户提供更多选项。用户可以通过右键单击扩展程序并单击菜单中的“选项”按钮或从“chrome://extensions”页面转到选项页面来访问选项页面。上述清单中使用的设置将导致 options.html 页面在新选项卡中打开。也可以以嵌入的方式在同一选项卡中打开“选项”页面。在基本的“嗨,在那里!”扩展,我们不需要这个选项。您可以在官方文档中阅读有关“选项”的更多信息。也许在这里添加更多
  6. background – 背景脚本在此处声明。后台脚本用于监听事件并对它们做出反应,因为扩展是一个事件驱动的范例。后台页面在需要时加载,空闲时卸载,即后台脚本在执行操作时将继续运行,并在操作执行后卸载。在我们的“嗨,在那里!”扩展,我们还没有使用后台脚本功能。您还可以注意到“persistent”键被设置为 false,因为这是标准做法,正如文档中提到的,我们应该将“persistent”设置为 true 的唯一时间是当扩展使用 chrome.webRequest API 时阻止或修改网络请求,我们没有这样做。
  7. browser_action – 扩展程序使用 browser_action 将图标放在 chrome 地址栏的右侧。然后扩展程序监听点击,当点击图标时,它会做一些事情。在我们的例子中,它打开包含 Hi, there! 的 popup.html 页面。问候。 ‘default_title’ 字段值是一个字符串,当用户将鼠标悬停在扩展程序的图标上时会显示该字符串。
  8. manifest_version – 在撰写本文时,我们鼓励开发人员试用即将推出的 manifest_version 3。但是,版本 2 仍然可用且熟悉,因此我们将使用版本 2。对于版本 3,你可以从这里开始。

2. popup.html 文件

HTML


  
  
    
Hi, there!
       


HTML


  
  
    
      
        
Youtube video Bookmarker
        xx:xx                                  
      
        

Bookmarked points

         
                       
    
                        


Javascript
'use strict';
  
$(function() {
  
        //retrieve data from local for already stored notes
        // chrome.runtime.sendMessage({ method: "getbookmarks" })
          //todo, not connected to background.js
  
        // $('#bookmark_ulist > li > span > a').on("click", function() {
        // console.log('li clicked event fired');
    }) //todo
  
//todo
//make same page reload of youtube video to bookmarked point
  
$('#bookmarkdesc').focus(function() {
  
        console.log('focus bookmark description input field') //executing
  
        //for sending a message
        chrome.runtime.sendMessage({ method: "gettimestampforcurrentpoint" });
  
        //for listening any message which comes from runtime
        chrome.runtime.onMessage.addListener(tsvalue);
  
  
  
        var ts, tslink;
  
        function tsvalue(msg) {
            // Do your work here
            if (msg.method == "tsfind") {
                ts = msg.tsvaltopopup;
                tslink = msg.fl;
  
                // console.log('ts tslink' + ts + ' ' + tslink) 
                $('#submitbookmark').on('click', function() {
                    // console.log('submitnote button clicked')
                    //#bookmark_ulist
  
                    var bookmarkinput = $('#bookmarkdesc').val();
  
                    // console.log('#bookmarkinput val ' + bookmarkinput); 
                    $('#bookmark_ulist').append('
  • ' + ts +                                                 ' - ' +                                                 bookmarkinput +                                                  '
  • ');                     console.log('list item appended to bookmark_ulist')                    // chrome.storage.local.set({ "bklocal": bookmarkinput,                 // "tslocal": ts, "vidlinklocal": tslink })                    //popup > bg > fg while setting                 //while getting, see..                 // chrome.runtime.sendMessage({ method: "setlocalstorage",                 // bookmarkvalue: bookmarkinput, timestamp: ts, vidlink: tslink})                       });                    $('#currts').text(msg.tsvaltopopup)                 $('#receiptts').text('got timestamp')                       // makeentryinstorage(bookmarkinput, ts, tslink);                }         }           })     // });          // chrome.storage.local.set({ note: inputnote, //timestamp: time, videolink: link });    // function makeentryinstorage(bookmarkinput, time, link) { //chrome.runtime.sendMessage({ method: "storeinlocal", note: bookmarkinput, // timestamp: time, videolink: link });    // } //todo    // makeentryinstorage(bookmarkinput, ts, tslink);       // $('#pointsli').append('
  • ' + // noteinput + '
  • ');       // console.log('msg obj popup.js ' + msg); // console.log('popupjs noteinput ' + noteinput); // $('#pointsli').append('
  • ' +  // noteinput + '
  • ');    // { method: "tsfind", tsvaltopopup: msg.tsval, fl: msg.finallink }


    Javascript
    "use strict";
      
    // to check connection of fg with bg
      
    chrome.runtime.onMessage.addListener(checktimestamp);
      
    function checktimestamp(msg) {
      // Do your work here
      if (msg.method == "gettimestampforcurrentpoint") {
        console.log("bg.js gettimestampforcurrrentpoint called");
        chrome.tabs.executeScript(null, { file: "./gettimestamp.js" }, () => {
          console.log("injected gettimestamp.js file into YT window DOM.");
          // gettimestamp.js will execute now in main chrome window which
          // is running youtube.com/somevideo
        });
      }
    }
      
    // first this runs
    chrome.tabs.onActivated.addListener((tab) => {
      console.log(tab);
      chrome.tabs.get(tab.tabId, (c) => {
        // console.log(c.url);
        if (/^https:\/\/www\.youtube/.test(c.url)) {
          // above pattern tests for the youtube hostname.
          // If youtube is running in the active tab,
          // it injects ./foreground.js in DOM.
      
          chrome.tabs.executeScript(null, { file: "./foreground.js" }, () => {
            console.log("i injected fg using bg script in youtube webpages");
          });
        }
      });
      
      // fetch data from local storage
      // var windowlink;
      // chrome.tabs.get(tab.tabId, a => {
      //     windowlink = a.url;
      // });
      // console.log('testretrieval bg.js')
      // chrome.runtime.sendMessage({ method: "testretrieval" });
    });
      
    // chrome.browserAction.onClicked.addListener(function(tab) {
    //     console.log('browser action called' + tab);
    //     //     // Run the following code when the popup is opened
    // });
      
    //for sending a message
    // chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
      
    // });
      
    // chrome.runtime.onMessage.addListener(retrievenotes)
      
    // function retrievenotes(msg) {
    //     if (msg.method == "getnotes") {
    //         //todo
    //     }
      
    // }
      
    // chrome.runtime.onMessage.addListener(storelocal); //todo
      
    // function storelocal(msg) { //todo
    //     if (msg.method == "storeinlocal") {
    //         chrome.storage.local.set({ note: inputnote, timestamp: time,
    //          videolink: link });
    //     }
    // }
      
    chrome.runtime.onMessage.addListener(getcurrenttimestamp);
      
    function getcurrenttimestamp(msg) {
      if (msg.method == "sendtimestamptobg") {
        var temp1 = msg.tsvalue;
        var temp2 = msg.finallink;
        console.log("msg.tsvalue value: " + msg.tsval);
        console.log("msg.finallink " + msg.finallink);
        //tsval and finallink being received properly in the bg consolelog
      
        chrome.runtime.sendMessage({
          method: "tsfind",
          tsvaltopopup: temp1,
          fl: temp2,
        });
        // , function() {
        //     console.log('tsval to popup');
        // })
      }
    }
      
    chrome.runtime.onMessage.addListener(localstorageset);
      
    function localstorageset(msg) {
      if (msg.method == "setlocalstorage") {
        console.log("setlocalstorage background.js"); //called
        // chrome.runtime.sendMessage({ method: "setlocalstorage",
        // bookmarkvalue: bookmarkinput, timestamp: ts, vidlink: tslink })
        // chrome.storage.local.set({ "bklocal": msg.bookmarkvalue,
        // "tslocal": msg.timestamp, "vidlinklocal": msg.vidlink })
        // chrome.storage.local.set({ "password": "123" })
        // chrome.runtime.sendMessage({ method: "localstoragesetrequest",
        // pass: "hellopass" });
      }
    }
      
    // chrome.runtime.onInstalled.addListener(function() {
    //   chrome.storage.sync.set({color: '#3aa757'}, function() {
    //     console.log('The color is green.');
    //   });
    //   chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
    //     chrome.declarativeContent.onPageChanged.addRules([{
    //       conditions: [new chrome.declarativeContent.PageStateMatcher({
    //         pageUrl: {hostEquals: 'developer.chrome.com'},
    //       })],
    //       actions: [new chrome.declarativeContent.ShowPageAction()]
    //     }]);
    //   });
    // });


    Javascript
    var result1 = document
      .querySelector(
        "#movie_player > div.ytp-chrome-bottom >div.ytp-progress-bar-container >div.ytp-progress-bar"
      )
      .getAttribute("aria-valuetext");
    //example of result1 is 1 Hours 48 Minutes 31 Seconds of 2 Hours 56 Minutes 33 Seconds.
    // Here, the timestamp is 01:48:31 in hh:mm:ss format out of a 02:56:33 long video.
      
    // construct link to exact point here
    var temparr = result1.split(" ");
      
    var tshhmmss_string;
      
    if (temparr[6] == "Hours") {
      tshhmmss_string = +"00:" + temparr[0] + ":" + temparr[2];
    } else if (temparr[1] == "Hours") {
      tshhmmss_string = temparr[0] + ":" + temparr[2] + ":" + temparr[4];
    } else if (temparr[6] == "Minutes") {
      tshhmmss_string = "00:" + temparr[0] + ":" + temparr[2];
    }
      
    console.log("gettimestamp.js " + result1);
      
    var windowlink = window.location.href;
    // note that values like https://www.youtube.com/watch?v=JaIU4CteN50,
    // https://youtu.be/JaIU4CteN50?t=88,
    // https://www.youtube.com/watch?v=PlSr4_moZGA&list=PLqM7alHXFySGnMRMboNceiibQw5ZU-ix9,
    // https://youtu.be/PlSr4_moZGA?list=PLqM7alHXFySGnMRMboNceiibQw5ZU-ix9&t=104
    // can be stored in pagelink.
      
    console.log("gettimestamp.js windowlink " + windowlink);
      
    // find index of v= substring
    var idx = windowlink.indexOf("v=");
    // return index from where the 'v=' substring starts in the windowlink.
    // For example, in https://www.youtube.com/watch?v=JaIU4CteN50, idx = 30.
    // indexOf function returns -1 if the substring is not found in the string.
      
    console.log("gettimestamp.js idx value " + idx);
      
    // console.log('tres gettimestamp.js ' + tres); fine - format hh:mm:ss
      
    // console.log('typeof tres gettimestamp.js ' + typeof(tres));
    //working fine - returns string
      
    function getseconds(timestamphhmmss) {
      var x = timestamphhmmss.split(":");
      var seconds = parseInt(x[0]) * 60 * 60 + parseInt(x[1]) * 60 + parseInt(x[2]);
      console.log("seconds calculated gettimestamp.js getinseconds" + seconds);
      return seconds;
    } //function working fine
      
    var timeinseconds = getseconds(tshhmmss_string); //working fine - returns number
      
    // console.log('tsinsec gettimestamp.js ' + tsinsec); fine
      
    var windowlinkfinal;
      
    if (idx == -1) {
      windowlink = "https://youtube.com";
      //in case of substring not found, i.e. bad case, store youtube.com as default.
    } else {
      windowlinkfinal =
        "https://youtube.com/watch?v=" +
        windowlink.substr(idx + 2, 11) +
        "&t=" +
        timeinseconds;
      console.log("gettimestamp.js windowlinkfinal " + windowlinkfinal);
    } // pagelinkfinal fine
      
    chrome.runtime.sendMessage({
      method: "sendtimestamptobg",
      tsvalue: tshhmmss_string,
      finallink: windowlinkfinal,
    });


    基本 Chrome 扩展程序如何工作?

    在我们“加载解压”chrome 扩展后,将处理 manifest.json 文件。此后,运行“后台”以初始化扩展并侦听其他事件。现在,我们已经从开发的角度了解了 chrome 扩展,我们将看看我们想要构建的主要扩展——YouTube 书签扩展。

    主要扩展 – YouTube 书签

    可以在此处找到 YouTube 书签扩展的视频演示。

    扩展的特点:

    • 在 Chrome 扩展程序中的 youtube.com 标签上工作,以便为视频中的点添加书签。
    • 当用户单击 popup.html 窗口中的 标签时,扩展会记录 标签处于“焦点”状态的时间戳。在从 标签中获取输入后,它会将其附加到 popup.html 的“书签点”部分,如解说视频中所见。

    项目目录结构:

    项目目录结构

    源代码:

    1.由于扩展程序将访问和存储网址,即 youtube 视频地址,因此必须查看我们可以从 DOM 访问的不同类型的时间戳。

    如果一个运行

    var result1 = document.querySelector('#movie_player >
    div.ytp-chrome-bottom > div.ytp-progress-bar-container >
    div.ytp-progress-bar').getAttribute("aria-valuetext");

    它将以 word 格式给出时间戳,定义如下。我们鼓励您在可通过按“Ctrl+Shift+J”访问的 chrome 窗口控制台中运行上述查询选择器。

    下面是可以通过运行上面的查询选择器以 word 格式检索的时间戳类型。

    1 Hours 48 Minutes 31 Seconds of 2 Hours 56 Minutes 33 Seconds
    //for video greater than 1 hours long and the current
    //timestamp being greater than 1 hour too.
    
    
    -> 0 Minutes 46 Seconds of 2 Hours 56 Minutes 33 Seconds
    //for video greater than 1 hours long and
    //current timestamp < 1 minute
    
    -> 8 Minutes 33 Seconds of 2 Hours 56 Minutes 33 Seconds
    //for video greater than 1 hours long and
    //current timestamp >= 1 minute and less than 1 hour
    
    -> 0 Minutes 0 Seconds of 0 Minutes 56 Seconds
    //for video less than 1 minute long and
    //current timestamp < 1 minute

    2.清单.json:

    {
    "name": "Youtube video bookmarker",
    "version": "1.0",
    "description": "An extension that bookmarks time points in youtube video",
    "permissions": ["activeTab", "declarativeContent",
             "storage", "tabs", "https://www.youtube.com/*"],
    "options_page": "options.html",
    "background": {
    "scripts": ["background.js"],
    "persistent": false
    },
    "externally_connectable": {
    "matches": ["*://*.youtube.com/*"]
    },
    "browser_action": {
    "default_popup": "popup.html",
    "default_title": "YouTube Video Bookmarker - GFG"
    },
    "manifest_version": 2
    }
    

    manifest.json文件的字段定义如下:

    • 名称、版本、描述、browser_action。 manifest_version, options_page, –在之前的 manifest.json 中已经有足够的细节
    • 权限–字符串数组。
      • activeTab – 可以访问活动选项卡
      • storage – 可以访问 chrome 的存储 API。
      • https://youtube.com/* – 基于模式的主机名,如果需要操作和工作扩展,可以访问该主机名
      • tabs – 可以访问“tabs”API。
      • declarativeContent – 根据当前页面的内容采取行动。

    3. popup.html:

    HTML

    
    
      
      
        
          
            
    Youtube video Bookmarker
            xx:xx                                  
          
            

    Bookmarked points

             
                         
        
                            

    这里我们有popup.html 文件的实现:

    • 我们有 2 个
      标签。
      • #bookmarktakerdiv – 将书签和时间戳作为用户的输入。
      • #bookmarklistdiv – 保存已为当前视频拍摄的书签的无序列表。此处的“当前视频”是指当前在当前活动的选项卡上播放的视频。
    • 我们有 1 个
        (#bookmark_ulist) 保存超链接书签及其时间戳。

      4. popup.js:

      Javascript

      'use strict';
        
      $(function() {
        
              //retrieve data from local for already stored notes
              // chrome.runtime.sendMessage({ method: "getbookmarks" })
                //todo, not connected to background.js
        
              // $('#bookmark_ulist > li > span > a').on("click", function() {
              // console.log('li clicked event fired');
          }) //todo
        
      //todo
      //make same page reload of youtube video to bookmarked point
        
      $('#bookmarkdesc').focus(function() {
        
              console.log('focus bookmark description input field') //executing
        
              //for sending a message
              chrome.runtime.sendMessage({ method: "gettimestampforcurrentpoint" });
        
              //for listening any message which comes from runtime
              chrome.runtime.onMessage.addListener(tsvalue);
        
        
        
              var ts, tslink;
        
              function tsvalue(msg) {
                  // Do your work here
                  if (msg.method == "tsfind") {
                      ts = msg.tsvaltopopup;
                      tslink = msg.fl;
        
                      // console.log('ts tslink' + ts + ' ' + tslink) 
                      $('#submitbookmark').on('click', function() {
                          // console.log('submitnote button clicked')
                          //#bookmark_ulist
        
                          var bookmarkinput = $('#bookmarkdesc').val();
        
                          // console.log('#bookmarkinput val ' + bookmarkinput); 
                          $('#bookmark_ulist').append('
    • ' + ts +                                                 ' - ' +                                                 bookmarkinput +                                                  '
    • ');                     console.log('list item appended to bookmark_ulist')                    // chrome.storage.local.set({ "bklocal": bookmarkinput,                 // "tslocal": ts, "vidlinklocal": tslink })                    //popup > bg > fg while setting                 //while getting, see..                 // chrome.runtime.sendMessage({ method: "setlocalstorage",                 // bookmarkvalue: bookmarkinput, timestamp: ts, vidlink: tslink})                       });                    $('#currts').text(msg.tsvaltopopup)                 $('#receiptts').text('got timestamp')                       // makeentryinstorage(bookmarkinput, ts, tslink);                }         }           })     // });          // chrome.storage.local.set({ note: inputnote, //timestamp: time, videolink: link });    // function makeentryinstorage(bookmarkinput, time, link) { //chrome.runtime.sendMessage({ method: "storeinlocal", note: bookmarkinput, // timestamp: time, videolink: link });    // } //todo    // makeentryinstorage(bookmarkinput, ts, tslink);       // $('#pointsli').append('
    • ' + // noteinput + '
    • ');       // console.log('msg obj popup.js ' + msg); // console.log('popupjs noteinput ' + noteinput); // $('#pointsli').append('
    • ' +  // noteinput + '
    • ');    // { method: "tsfind", tsvaltopopup: msg.tsval, fl: msg.finallink }

      下面的列表解释了我们为 popup.js 采取的方法:

      • 当用户单击由 id ‘ bookmarkdesc ‘ 唯一标识的 标签时,该事件将使用 { method: “ gettimestampforcurrentpoint ” } 对象触发。这个事件由 background.js 捕获,我们稍后会看到。
      • 函数tsvalue () 从 background.js 中触发,将视频信息从浏览器窗口框架传输到扩展窗口框架。
      • tsvalue ()函数内部,’# submitbookmark ‘ 按钮上的点击事件将包含时间戳的
      • 元素和由带时间戳的 YouTube视频链接超链接的书签附加到 popup.html 中的 #bookmark_ulist。
      • 所以,再次解释一下,在用户点击扩展图标后,如果视频是主浏览器窗口框架中的 YouTube 视频,时间戳和书签被内置到一个
      • 元素中,该元素被附加到
          中弹出窗口.html。
        • 如您所见,popup.js 为 popup.html 添加了交互性,并使用事件驱动范例与 background.js 进行通信。

        5.背景.js

        Javascript

        "use strict";
          
        // to check connection of fg with bg
          
        chrome.runtime.onMessage.addListener(checktimestamp);
          
        function checktimestamp(msg) {
          // Do your work here
          if (msg.method == "gettimestampforcurrentpoint") {
            console.log("bg.js gettimestampforcurrrentpoint called");
            chrome.tabs.executeScript(null, { file: "./gettimestamp.js" }, () => {
              console.log("injected gettimestamp.js file into YT window DOM.");
              // gettimestamp.js will execute now in main chrome window which
              // is running youtube.com/somevideo
            });
          }
        }
          
        // first this runs
        chrome.tabs.onActivated.addListener((tab) => {
          console.log(tab);
          chrome.tabs.get(tab.tabId, (c) => {
            // console.log(c.url);
            if (/^https:\/\/www\.youtube/.test(c.url)) {
              // above pattern tests for the youtube hostname.
              // If youtube is running in the active tab,
              // it injects ./foreground.js in DOM.
          
              chrome.tabs.executeScript(null, { file: "./foreground.js" }, () => {
                console.log("i injected fg using bg script in youtube webpages");
              });
            }
          });
          
          // fetch data from local storage
          // var windowlink;
          // chrome.tabs.get(tab.tabId, a => {
          //     windowlink = a.url;
          // });
          // console.log('testretrieval bg.js')
          // chrome.runtime.sendMessage({ method: "testretrieval" });
        });
          
        // chrome.browserAction.onClicked.addListener(function(tab) {
        //     console.log('browser action called' + tab);
        //     //     // Run the following code when the popup is opened
        // });
          
        //for sending a message
        // chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
          
        // });
          
        // chrome.runtime.onMessage.addListener(retrievenotes)
          
        // function retrievenotes(msg) {
        //     if (msg.method == "getnotes") {
        //         //todo
        //     }
          
        // }
          
        // chrome.runtime.onMessage.addListener(storelocal); //todo
          
        // function storelocal(msg) { //todo
        //     if (msg.method == "storeinlocal") {
        //         chrome.storage.local.set({ note: inputnote, timestamp: time,
        //          videolink: link });
        //     }
        // }
          
        chrome.runtime.onMessage.addListener(getcurrenttimestamp);
          
        function getcurrenttimestamp(msg) {
          if (msg.method == "sendtimestamptobg") {
            var temp1 = msg.tsvalue;
            var temp2 = msg.finallink;
            console.log("msg.tsvalue value: " + msg.tsval);
            console.log("msg.finallink " + msg.finallink);
            //tsval and finallink being received properly in the bg consolelog
          
            chrome.runtime.sendMessage({
              method: "tsfind",
              tsvaltopopup: temp1,
              fl: temp2,
            });
            // , function() {
            //     console.log('tsval to popup');
            // })
          }
        }
          
        chrome.runtime.onMessage.addListener(localstorageset);
          
        function localstorageset(msg) {
          if (msg.method == "setlocalstorage") {
            console.log("setlocalstorage background.js"); //called
            // chrome.runtime.sendMessage({ method: "setlocalstorage",
            // bookmarkvalue: bookmarkinput, timestamp: ts, vidlink: tslink })
            // chrome.storage.local.set({ "bklocal": msg.bookmarkvalue,
            // "tslocal": msg.timestamp, "vidlinklocal": msg.vidlink })
            // chrome.storage.local.set({ "password": "123" })
            // chrome.runtime.sendMessage({ method: "localstoragesetrequest",
            // pass: "hellopass" });
          }
        }
          
        // chrome.runtime.onInstalled.addListener(function() {
        //   chrome.storage.sync.set({color: '#3aa757'}, function() {
        //     console.log('The color is green.');
        //   });
        //   chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
        //     chrome.declarativeContent.onPageChanged.addRules([{
        //       conditions: [new chrome.declarativeContent.PageStateMatcher({
        //         pageUrl: {hostEquals: 'developer.chrome.com'},
        //       })],
        //       actions: [new chrome.declarativeContent.ShowPageAction()]
        //     }]);
        //   });
        // });
        

        让我们看看我们在 background.js 文件中做了什么:

        • 当用户点击 字段制作书签时,在后台脚本中,调用 checktimestamp ()函数将gettimestamp .js 脚本注入浏览器窗口。正如文件名所暗示的那样,它将为我们提供当前时间戳和带时间戳的 YouTube视频链接,我们将使用eventListenergetcurrenttimestamp 收听
        • 函数getcurrenttimestamp将在从使用对象属性 {msg.method = ‘ sendtimestamptobg ‘} 触发的事件接收到时间戳(以秒为单位)和带时间戳的视频链接后,将其发送到 popup.js(扩展的上下文/帧)。此事件侦听从gettimestamp .js 脚本发送的消息。

        6. 获取时间戳.js

        Javascript

        var result1 = document
          .querySelector(
            "#movie_player > div.ytp-chrome-bottom >div.ytp-progress-bar-container >div.ytp-progress-bar"
          )
          .getAttribute("aria-valuetext");
        //example of result1 is 1 Hours 48 Minutes 31 Seconds of 2 Hours 56 Minutes 33 Seconds.
        // Here, the timestamp is 01:48:31 in hh:mm:ss format out of a 02:56:33 long video.
          
        // construct link to exact point here
        var temparr = result1.split(" ");
          
        var tshhmmss_string;
          
        if (temparr[6] == "Hours") {
          tshhmmss_string = +"00:" + temparr[0] + ":" + temparr[2];
        } else if (temparr[1] == "Hours") {
          tshhmmss_string = temparr[0] + ":" + temparr[2] + ":" + temparr[4];
        } else if (temparr[6] == "Minutes") {
          tshhmmss_string = "00:" + temparr[0] + ":" + temparr[2];
        }
          
        console.log("gettimestamp.js " + result1);
          
        var windowlink = window.location.href;
        // note that values like https://www.youtube.com/watch?v=JaIU4CteN50,
        // https://youtu.be/JaIU4CteN50?t=88,
        // https://www.youtube.com/watch?v=PlSr4_moZGA&list=PLqM7alHXFySGnMRMboNceiibQw5ZU-ix9,
        // https://youtu.be/PlSr4_moZGA?list=PLqM7alHXFySGnMRMboNceiibQw5ZU-ix9&t=104
        // can be stored in pagelink.
          
        console.log("gettimestamp.js windowlink " + windowlink);
          
        // find index of v= substring
        var idx = windowlink.indexOf("v=");
        // return index from where the 'v=' substring starts in the windowlink.
        // For example, in https://www.youtube.com/watch?v=JaIU4CteN50, idx = 30.
        // indexOf function returns -1 if the substring is not found in the string.
          
        console.log("gettimestamp.js idx value " + idx);
          
        // console.log('tres gettimestamp.js ' + tres); fine - format hh:mm:ss
          
        // console.log('typeof tres gettimestamp.js ' + typeof(tres));
        //working fine - returns string
          
        function getseconds(timestamphhmmss) {
          var x = timestamphhmmss.split(":");
          var seconds = parseInt(x[0]) * 60 * 60 + parseInt(x[1]) * 60 + parseInt(x[2]);
          console.log("seconds calculated gettimestamp.js getinseconds" + seconds);
          return seconds;
        } //function working fine
          
        var timeinseconds = getseconds(tshhmmss_string); //working fine - returns number
          
        // console.log('tsinsec gettimestamp.js ' + tsinsec); fine
          
        var windowlinkfinal;
          
        if (idx == -1) {
          windowlink = "https://youtube.com";
          //in case of substring not found, i.e. bad case, store youtube.com as default.
        } else {
          windowlinkfinal =
            "https://youtube.com/watch?v=" +
            windowlink.substr(idx + 2, 11) +
            "&t=" +
            timeinseconds;
          console.log("gettimestamp.js windowlinkfinal " + windowlinkfinal);
        } // pagelinkfinal fine
          
        chrome.runtime.sendMessage({
          method: "sendtimestamptobg",
          tsvalue: tshhmmss_string,
          finallink: windowlinkfinal,
        });
        

        让我们看看我们在上面的gettimestamp.js文件中做了什么:

        • gettimestamp.js脚本在浏览器框架/上下文中运行。此脚本使用querySelector提取 word 格式的时间戳,将其转换为hh:mm:ss格式,以秒为单位。
        • 它还会获取当前的视频链接并将其转换为通用格式。

        hh:mm:ss格式的时间戳和带时间戳的视频链接由background.js文件发出以供使用。该事件由 ‘ method: “sendtimestamptobg” ‘ 键标识。

        进一步发展范围供您试用

        随时在 repo 上提出 PR 或问题。直接 WIP 正在将其连接到 chrome.localStorage。

        • 将扩展程序连接到本地数据库或 Firebase 以实现解耦数据存储。
        • 存储数据时的数据结构研究与实现。
        • 与 Chrome 的本地存储断开连接,并使用“同步存储”,以便其他设备上也可以使用书签
        • 改进扩展的 UI/UX
        • 设计图标并将它们存储在用于自定义扩展图标的项目目录中