一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件

这篇具有很好参考价值的文章主要介绍了一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说在前面

平时大家都是怎么管理自己的浏览器书签数据的呢?有没有过公司和家里的电脑浏览器书签不同步的情况?有没有过电脑突然坏了但书签数据没有导出,导致书签数据丢失了?解决这些问题的方法有很多,我选择自己写个chrome插件来做书签同步。

实现方案

通过 gitee 来做存取

建一个私有仓库来保存自己的书签目录信息,需要同步的时候再获取 gitee 仓库的书签目录到本地。这样不用自己写服务端对数据进行存储,减少了很多不必要的开发工作。

实现步骤

一、准备工作

1、新建 gitee 仓库

直接在gitee上新建仓库即可。

我们不想要书签信息公开,所以选择勾选上私有:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

创建完的初始仓库是这样的:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

我们再新增一个目录,用于存放和书签相关的文件:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

在该目录下新增一个文件,用于保存书签导出的数据:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

二、插件编写

完成前面的准备工作,新建完 gitee 仓库之后,我们便可以正式开始进行插件的编写了。

1、插件模板
  • 安装依赖jyeontu
npm i -g jyeontu
  • 获取模板
jyeontu create

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

  • 生成模板

根据提示输入相关信息即可

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

2、giteeAPI

我们可以通过 giteeAPI 来对 gitee 仓库进行操作,下面是 giteeAPI 的操作文档:
https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no

获取gitee指定文件的内容

我们可以通过下面代码来获取到gitee指定仓库指定文件的内容:

async function fetchFileContent(apiUrl, accessToken) {
  const response = await fetch(apiUrl, {
    headers: {
      Authorization: "token " + accessToken,
    },
  });
  const fileData = await response.json();
  return fileData.content;
}

export async function getFile(gitInfo) {
  const accessToken = gitInfo.token;
  const apiUrl =
    "https://gitee.com/api/v5/repos/" +
    gitInfo.owner +
    "/" +
    gitInfo.repo +
    "/contents/" +
    gitInfo.filePath;

  const fileContent = await fetchFileContent(apiUrl, accessToken);
  const decodedContent = atob(fileContent); // 解码Base64编码的文件内容
  const decoder = new TextDecoder();
  const decodedData = decoder.decode(
    new Uint8Array([...decodedContent].map((char) => char.charCodeAt(0)))
  );
  return JSON.parse(decodedData);
}
修改指定文件的内容数据

我们需要先获取到文件,拿到文件的sha值,后面通过sha来对文件进行编辑操作。
btoa函数只能处理Latin1字符范围内的字符串,对超出Latin1字符范围的字符串进行Base64编码,我们需要进行以下操作,使用TextEncoder对象来将字符串转换为字节数组,然后再进行Base64编码。

async function fetchFileContent(apiUrl, accessToken) {
  const response = await fetch(apiUrl, {
    headers: {
      Authorization: "token " + accessToken,
    },
  });
  const fileData = await response.json();
  return fileData.content;
}
async function getDecodedContent(content) {
  const decodedContent = atob(content); // 解码Base64编码的文件内容
  const decoder = new TextDecoder();
  const decodedData = decoder.decode(
    new Uint8Array([...decodedContent].map((char) => char.charCodeAt(0)))
  );
  return JSON.parse(decodedData);
}
async function putFileContent(apiUrl, accessToken, encodedContent, sha) {
  const commitData = {
    access_token: accessToken,
    content: encodedContent,
    message: "Modified file",
    sha: sha,
  };

  const putResponse = await fetch(apiUrl, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: "token " + accessToken,
    },
    body: JSON.stringify(commitData),
  });

  if (putResponse.ok) {
    console.log("File modified successfully.");
  } else {
    console.error("Failed to modify file.");
  }
}
export async function modifyFile(gitInfo, modifiedContent) {
  const accessToken = gitInfo.token;
  const apiUrl =
    "https://gitee.com/api/v5/repos/" +
    gitInfo.owner +
    "/" +
    gitInfo.repo +
    "/contents/" +
    gitInfo.filePath;

  try {
    const fileContent = await fetchFileContent(apiUrl, accessToken);
    const content = await getDecodedContent(fileContent);
    modifiedContent = mergeBookmarks(content, modifiedContent);
    modifiedContent = JSON.stringify(modifiedContent);
    const encoder = new TextEncoder();
    const data = encoder.encode(modifiedContent);
    const encodedContent = btoa(
      String.fromCharCode.apply(null, new Uint8Array(data))
    );

    await putFileContent(apiUrl, accessToken, encodedContent, fileContent.sha);
  } catch (error) {
    console.error("An error occurred:", error);
  }
}
3、indexDb存取

我们不希望每次打开都需要去重新填写gitee仓库的相关信息,所以这里我们使用indexDb来对gitee仓库的相关信息做一个保存。

export class IndexedDB {
  constructor(databaseName, storeName) {
    this.databaseName = databaseName;
    this.storeName = storeName;
    this.db = null;
  }

  open() {
    return new Promise((resolve, reject) => {
      const request = window.indexedDB.open(this.databaseName);

      request.onerror = () => {
        reject(new Error("Failed to open database"));
      };

      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };

      request.onupgradeneeded = (event) => {
        this.db = event.target.result;
        if (!this.db.objectStoreNames.contains(this.storeName)) {
          this.db.createObjectStore(this.storeName, {
            keyPath: "id",
            autoIncrement: true,
          });
        }
      };
    });
  }

  createDatabase() {
    return new Promise((resolve, reject) => {
      const request = window.indexedDB.open(this.databaseName);

      request.onerror = () => {
        reject(new Error("Failed to create database"));
      };

      request.onsuccess = () => {
        this.db = request.result;
        this.db.close();
        resolve();
      };

      request.onupgradeneeded = (event) => {
        this.db = event.target.result;
        if (!this.db.objectStoreNames.contains(this.storeName)) {
          this.db.createObjectStore(this.storeName, {
            keyPath: "id",
            autoIncrement: true,
          });
        }
        this.db.close();
        resolve();
      };
    });
  }

  close() {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }

  add(data) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, "readwrite");
      const objectStore = transaction.objectStore(this.storeName);
      const request = objectStore.add(data);

      request.onsuccess = () => {
        resolve(request.result);
      };

      request.onerror = () => {
        reject(new Error("Failed to add data"));
      };
    });
  }

  getAll() {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, "readonly");
      const objectStore = transaction.objectStore(this.storeName);
      const request = objectStore.getAll();

      request.onsuccess = () => {
        resolve(request.result);
      };

      request.onerror = () => {
        reject(new Error("Failed to get data"));
      };
    });
  }

  getById(id) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, "readonly");
      const objectStore = transaction.objectStore(this.storeName);
      const request = objectStore.get(id);

      request.onsuccess = () => {
        resolve(request.result);
      };

      request.onerror = () => {
        reject(new Error("Failed to get data"));
      };
    });
  }

  delete(id) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, "readwrite");
      const objectStore = transaction.objectStore(this.storeName);
      const request = objectStore.delete(id);

      request.onsuccess = () => {
        resolve();
      };

      request.onerror = () => {
        reject(new Error("Failed to delete data"));
      };
    });
  }

  update(id, newData) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, "readwrite");
      const objectStore = transaction.objectStore(this.storeName);
      const getRequest = objectStore.get(id);

      getRequest.onsuccess = () => {
        const oldData = getRequest.result;

        if (!oldData) {
          const addRequest = objectStore.add({ ...newData, id });

          addRequest.onsuccess = () => {
            resolve({ ...newData, id });
          };

          addRequest.onerror = () => {
            reject(new Error("Failed to add data"));
          };
        } else {
          const mergedData = { ...oldData, ...newData };
          const putRequest = objectStore.put(mergedData);

          putRequest.onsuccess = () => {
            resolve(mergedData);
          };

          putRequest.onerror = () => {
            reject(new Error("Failed to update data"));
          };
        }
      };
      getRequest.onerror = () => {
        reject(new Error("Failed to get data"));
      };
    });
  }
}
4、书签存取
获取chrome书签

要获取 Chrome 浏览器的书签目录,我们可以使用 Chrome 浏览器提供的 API——chrome.bookmarks。下面是一个示例代码,演示如何使用chrome.bookmarks API 获取 Chrome 浏览器的书签目录:

export const getBookmarks = () => {
  return new Promise((resolve) => {
    chrome.bookmarks.getTree(function (bookmarkTreeNodes) {
      resolve(bookmarkTreeNodes);
    });
  });
};

在上述代码中,我们首先使用chrome.bookmarks.getTree()方法获取 Chrome 浏览器的书签目录树。

请注意,要使用chrome.bookmarks API,你需要在你的 Chrome 插件中声明"bookmarks"权限。具体来说,在插件清单文件(manifest.json)中添加以下内容:

{
  "manifest_version": 2,
  "name": "你的插件名称",
  "version": "1.0",
  "permissions": [
    "bookmarks"
  ],
  "background": {
    "scripts": [
      "bg.js"
    ]
  }
}

在上述代码中,我们在"permissions"字段中声明了"bookmarks"权限,以便我们可以使用chrome.bookmarks API。同时,在"background"字段中指定了一个后台脚本(bg.js),以便我们在后台执行上述代码。

删除chrome浏览器书签

导入书签前我们需要先清除一下当前浏览器的书签,通过chrome.bookmarks.removeTree可以删除书签节点。

export function removeBookmarks(bookmarkTreeNodes) {
  // 遍历书签树,删除所有的书签
  function traverseBookmarks(bookmarkNodes) {
    for (const node of bookmarkNodes) {
      if (node.children) {
        traverseBookmarks(node.children);
      }
      // 删除书签节点
      chrome.bookmarks.removeTree(node.id);
    }
  }
  traverseBookmarks(bookmarkTreeNodes);
}
导入书签

使用chrome.bookmarks.create来新建书签。

export function importBookmarks(bookmarkTreeNodes) {
  // 遍历书签树
  function traverseBookmarks(bookmarkNodes, parentId) {
    for (const node of bookmarkNodes) {
      // 如果节点是文件夹
      if (node.children) {
        // 创建一个新的文件夹节点
        chrome.bookmarks.create(
          {
            parentId: parentId,
            title: node.title,
          },
          function (newFolderNode) {
            // 递归遍历子节点
            traverseBookmarks(node.children, newFolderNode.id);
          }
        );
      }
      // 如果节点是书签
      else {
        // 创建一个新的书签节点
        chrome.bookmarks.create({
          parentId: parentId,
          title: node.title,
          url: node.url,
        });
      }
    }
  }

  // 从根节点开始遍历书签树
  traverseBookmarks(bookmarkTreeNodes[0].children, "1");
}

插件使用

1、插件下载

直接到gitee上下载源码即可:

源码地址:https://gitee.com/zheng_yongtao/chrome-plug-in.git

2、导入插件

书签同步插件的目录如下:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

下载完后打开浏览器扩展程序管理页面(chrome://extensions/),选择加载已解压的扩展程序:

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

选择插件目录导入即可:

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

导入成功后就可以看到下面这个插件了
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

可以勾选上下面这个,勾选后插件就会显示在导航栏上
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

3、补充gitee仓库信息数据

导入插件后,我们点击导航栏的插件图标,可以看到这样一个面板,其中有四个数据需要我们填写:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

获取 token

进入到giteeAPI文档进行授权获取到返回填写即可,具体步骤如下:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

仓库所属空间地址(owner)

就是个人主页的一个空间地址,如下图:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

仓库路径(repo)

前面新建仓库的路径(仓库名),如下图:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

书签文件路径(filePath)

新建用于保存书签数据的文件,想保存多份不同的数据的话可以多件几个不同的文件分别进行存储,同步的时候选择对应的目录即可,如下图:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

将对应信息填写上之后我们就可以开始进行同步操作了:
一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件,浏览器扩展插件,chrome,前端,javascript

4、同步方式

(1)覆盖保存

使用当前浏览器书签数据覆盖保存到gitee仓库中。

(2)合并保存

将当前浏览器书签数据与gitee仓库中的书签数据合并好再进行保存。

(3)覆盖获取

使用gitee仓库中的书签数据覆盖掉本地的书签数据。

(4)合并获取

将gitee仓库中的书签数据和本地的书签数据合并后再覆盖掉本地的书签数据。

(5)合并规则

同一层级并且同名的目录我们会将其子节点合并到同一目录下,同一层级下我们会根据 书签名 + 书签url 对该层级的书签进行去重。

源码

1、gitee

gitee 地址:https://gitee.com/zheng_yongtao/chrome-plug-in/tree/master/chrome-bookmarks-manage

2、公众号

关注公众号『前端也能这么有趣』发送 chrome插件即可获取源码。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。文章来源地址https://www.toymoban.com/news/detail-719679.html

到了这里,关于一键同步,无处不在的书签体验:探索多电脑Chrome书签同步插件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 不可思议但又无处不在的漏洞,WEB安全基础入门—业务逻辑漏洞

    欢迎关注订阅专栏! WEB安全系列包括如下三个专栏: 《WEB安全基础-服务器端漏洞》 《WEB安全基础-客户端漏洞》 《WEB安全高级-综合利用》 知识点全面细致,逻辑清晰、结合实战,并配有大量练习靶场,让你读一篇、练一篇,掌握一篇,在学习路上事半功倍,少走弯路! 欢

    2024年02月02日
    浏览(43)
  • [小尘送书-第二期]《从零开始读懂量子力学》由浅入深,解释科学原理;从手机到超导,量子无处不在;从微观到宏观,遐想人生的意义!

    大家好,我是小尘,欢迎关注,一起交流学习!欢迎大家在CSDN后台私信我!一起讨论学习,讨论如何找到满意的工作! 从微小的原子到浩瀚的宇宙,从每一滴水到闪亮的钻石,从划破夜空的激光到你身边的手机……所有事物的背后都有量子力学在主宰!你看过世界级畅销书

    2024年02月15日
    浏览(46)
  • 使用 github 同步谷歌浏览器书签

    想必使用谷歌浏览器Chrome的用户一定非常头疼的一件事就是:账户不能登录,书签收藏夹不能同步,换一台电脑书签收藏夹没有了! 下面教大家一招亲测有效适用的方法解决书签同步问题,在任何电脑都可以同步了 1、去下载谷歌浏览器 可以在百度搜索chrome或谷歌浏览器, 如

    2024年02月14日
    浏览(36)
  • 发现便捷,畅游互联网世界——【书签导航】带你领略全新体验!

      互联网世界汇聚了大量的学习资源,但是如何快速找到适合自己的学习材料却是一项挑战。 于是,我倾尽毕生所学强势推出项目 【 书签导航 】聚合平台,这是一个是针对广大互联网学习者的最佳导航和学习工具平台。 无论你是程序员、设计师、学生,或者对任何领域的

    2024年02月16日
    浏览(38)
  • windows下Edge浏览器&Google Chrome与Safari双向同步书签

    最近刚入手了 iPad,在上面装了edge浏览器后实现了收藏夹,浏览记录同步的问题,可是Safari浏览器也同样好用,于是想体验一下不同系统之间的协同 设置和使用 Windows 版 iCloud 1 新建TXT文件,填入内容如下: 2 修改txt文件名为.reg格式,双击导入注册表 3 打开桌面版iCloud应用,

    2023年04月25日
    浏览(62)
  • 一键Run带你体验扩散模型的魅力

    本文分享自华为云社区《爆圈Sora横空出世,AGI通用人工智能时代真的要来了吗?一键Run带你体验扩散模型的魅力!》,作者: 码上开花_Lancer。 Sora这几天的爆炸性新闻,让所有人工智能相关从业者及对应用感兴趣的人群都感到沸腾,震撼到央视也在进行相关的讨论,简直可

    2024年03月09日
    浏览(34)
  • 区块链开源底层软件平台——长安链一键上链体验过程

    长安链作为区块链开源底层软件平台,包涵区块链核心框架、丰富的组件库和工具集,致力于为用户高效、精准地解决差异化区块链实现需求,构建高性能、高可信、高安全的新型数字基础设施,同时也是国内首个自主可控区块链软硬件技术体系,最近腾讯云区块链,区块链

    2024年02月17日
    浏览(41)
  • 一键切割,激发无限创意:体验全新图片批量编辑器

    在数字创意的时代,图片编辑成为了表达个性和创造力的关键。然而,传统的图片编辑工具常常让人望而生畏,复杂的操作和高门槛的技术要求使得许多人望而却步。现在,我们为您带来一款全新的图片批量编辑器,只需一键切割,就能释放您无限的创意火花! “图片批量编

    2024年03月18日
    浏览(55)
  • Chunjun数据同步工具初体验

    chunjun (纯钧) 官方文档纯钧 chunjun 有四种运行方式:local、standalone、yarn session、yarn pre-job 。 运行方式/环境依赖 flink环境 hadoop环境 local × × standalone √ × yarn session √ √ yarn pre-job √ √ 官网已经提供了编译好的插件压缩包,可以直接下载:https://github.com/DTStack/chunjun/releases

    2024年02月03日
    浏览(73)
  • WPS-AI 体验笔记1:一键生成 PPT

    WPS Win客户端 版本:11.1.0.14650 上小节介绍了怎么获取 WPS AI 的“入场券”和一些“入场”准备工作,这一篇开始来实战应用一下。 使用 PPT 的【一键生成幻灯片】的功能,来制作一篇 PPT:赏析李白的经典诗歌《静夜思》。 接下来一起来赏析一下李白的《静夜思》。 新建一个

    2024年02月11日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包