kiến thức Tổng hợp những addon chất cho Firefox / Chromium

ngon r fen 😃
Yên tâm là Dark Mode nửa mùa vẫn còn đang ở giai đoạn tiến hóa, sắp tới mình sẽ nghiên cứu thêm mà đây là cái ảnh đề mô thật ra nó còn sâu lắm, nghĩa là nó còn có thể tự sửa cho trường hợp như Dark Mode, cứ để đây để cùng nghiên cứu @haidangtueba @Chu Cong Canh :

browser.display.background_color.dark#1C1B22
browser.display.foreground_color.dark#FBFBFE
browser.active_color.dark#FF6666
browser.anchor_color.dark#8C8CFF
browser.visited_color.dark#FFADFF

Bật focus:

browser.display.use_focus_colorstrue
browser.display.focus_background_color#117722
browser.display.focus_text_color#ffffff

Nghĩa là đường cách mệnh vẫn còn dài lắm.

Cái này chưa rõ lắm: https://searchfox.org/mozilla-central/source/modules/libpref/init/StaticPrefList.yaml#1203
browser.display.suppress_canvas_background_image_on_forced_colorstrue

Còn mấy cái ẩn đi nữa cho viền focus:
Code:
# Whether we should suppress the background-image of the canvas (the root
# frame) if we're in forced colors mode.
#
# This is important because some sites use background-image with a plain color
# and it causes undesirable results in high-contrast mode.
#
# See bug 1614921 for example.
- name: browser.display.suppress_canvas_background_image_on_forced_colors
  type: bool
  value: true
  mirror: always

- name: browser.display.focus_ring_on_anything
  type: bool
  value: false
  mirror: always

- name: browser.display.focus_ring_width
  type: uint32_t
  value: 1
  mirror: always

- name: browser.display.focus_background_color
  type: String
  value: "#117722"
  mirror: never

# Focus ring border style.
# 0 = solid border, 1 = dotted border
- name: browser.display.focus_ring_style
  type: uint32_t
  value: 1
  mirror: always

- name: browser.display.focus_text_color
  type: String
  value: "#ffffff"
  mirror: never

- name: browser.display.foreground_color
  type: String
  value: "#000000"
  mirror: never

- name: browser.display.foreground_color.dark
  type: String
  value: "#FBFBFE"
  mirror: never

Demo sau khi bật focus lên, lại thêm rất nhiều thứ để chỉnh sau đó vì focus giúp ích việc hoàn thiện Dark Mode nửa mùa rất nhiều:
1696155971892.png
 
Last edited:
Cập nhập Handlers dành cho MPV, hỗ trợ thêm rất nhiều tính năng khi so sánh với phiên bản trước đó, cách sử dụng cũng nhẹ nhàng hơn :D

Nếu các bạn thấy lỗi báo cho mình bởi mình chưa có test kỹ.

Tính năng:
  • Kéo thả link qua bên tay phải để mở qua MPV, kéo qua tay trái để mở qua streamlink, kéo xuống dưới để tải với yt-dlp
  • Hỗ trợ thêm streamlink và yt-dlp
  • Nhẹ và đơn giản hơn so với code cũ gấp tỉ lần, rất dễ cho việc phát triển thêm từ phía các bạn chứ không chỉ mình
  • Có thể tùy ý thêm tính năng nếu các bạn muốn, bởi script này hỗ trợ 8 hướng

Video demo: https://streamable.com/o3sxe1

Cài đặt:
1. Cài đặt tại Greasyfork.org hoặc Từ addon quản lý Userscript như Violent/Fire/Tamplermonkey, tạo script mới rồi paste chỗ này vào, Save.

JavaScript:
// ==UserScript==
// @name        Handlers Helper
// @include       *://*/*
// @grant       none
// @version     1.6
// @author      -
// @description Helper for protocol_hook.lua
// @namespace Violentmonkey Scripts
// ==/UserScript==

var collected_urls = {};
function GM_getParentByTagName(el, tagName) {
  tagName = tagName.toLowerCase();
  if (el.tagName.toLowerCase() == tagName) {
    return el;
  }
  while (el && el.parentNode) {
    el = el.parentNode;
    if (el.tagName && el.tagName.toLowerCase() == tagName) {
      return el;
    }
  }
  return "undefined";
}

function attachDrag(elem) {

  function GM_btoaUrl(url) {
    return btoa(url).replace(/\//g, "_").replace(/\+/g, "-").replace(/\=/g, "");
  }

  function EA(attr, type) {
    var url = '';
    var subs = '';
    var s = '';
    console.log(attr, type)
    if (attr.startsWith('http')) {
      url = attr;
    } else if (attr.startsWith('http')) {
      location.href = attr;
      return;
    }
    if (url == '') {
      url = location.href;
    }
    console.log(collected_urls);
    if (Object.keys(collected_urls).length > 0) {
        for (link in collected_urls) {
            console.log(link, collected_urls[link]);
            collected_urls[link].style.boxSizing = 'unset';
            collected_urls[link].style.border = 'unset';
            s += link + ' ';
        }
        s = s.trim(' ');
        console.log(s);
        //var s = collected_urls.join(" ");
    } else {
        var s = url;
    }
    collected_urls = {};
    var app = 'play';
    if (type != 'vid') {
      var app = type.toLowerCase();
    }
    var bs = GM_btoaUrl(s);
    var url2 = 'mpv://' + app + '/' + bs + '/' + "?referer=" + GM_btoaUrl(location.href);
    if (subs != '') {
      url2 = url2 + '?subs=' + GM_btoaUrl(subs);
    }
    //alert(url2);
    location.href = url2;
  }

  // Define the enum-like directory
  const DirectionEnum = {
    RIGHT: 6,
    LEFT: 4,
    UP: 2,
    DOWN: 8,
    UP_LEFT: 1,
    UP_RIGHT: 3,
    DOWN_LEFT: 7,
    DOWN_RIGHT: 9
  };

  function getDirection(x, y, cx, cy) {
    /*=================
    |                 |
    | 1↖   2↑   3↗ |
    |                 |
    | 4←    5    6→ |
    |                 |
    | 7↙   8↓   9↘ |
    |                 |
    |=================*/
    let d, t;
    if ((cx - x) >= -50 && (cx - x) <= 50 && (cy - y) >= -50 && (cy - y) <= 50) {
      return 5;
    }
    // Change (4 == 4) to (8 == 4) to enable 8 directions
    if (4 == 4) { //4 directions
      if (Math.abs(cx - x) < Math.abs(cy - y)) {
        d = cy > y ? "8" : "2";
      } else {
        d = cx > x ? "6" : "4";
      }
    } else { //8 directions
      t = (cy - y) / (cx - x);
      if (-0.4142 <= t && t < 0.4142) d = cx > x ? '6' : "4";
      else if (2.4142 <= t || t < -2.4142) d = cy > y ? '8' : '2';
      else if (0.4142 <= t && t < 2.4142) d = cx > x ? '9' : '1';
      else d = cy > y ? '7' : '3';
    }
    return d;
  }
  elem.addEventListener('dragstart', function(e) {
    //console.log(e.target);
    //console.log(e.target.shadowRoot);
    /*if (e.target.nodeName != "A") {
    e.stopPropagation();
    e.stopImmediatePropagation();
    //e.preventDefault();
    }*/
    console.log('dragstart');
    var x1 = e.clientX;
    var y1 = e.clientY;
    var dragend = elem.addEventListener('dragend', function doEA(e) {
      var x2 = e.clientX;
      var y2 = e.clientY;
      var direction = getDirection(x1, y1, x2, y2);
      //if ((x2 - x1) >= -50 && (x2 - x1) <= 50 && (y2 - y1) >= -50 && (y2 - y1) <= 50) {direction = 5;console.log(5);}
      //if (e.target.nodeName == "A" && e.target.href.match(/youtube.com|youtu.be|streamable.com/)) {
      console.log('Direction: ' + direction);
      console.log(x1, y1, x2, y2, direction);

      const targetHref = e.target.href || e.target.src;

      switch (+direction) {
        case DirectionEnum.RIGHT:
          console.log('MPV: ' + targetHref);
          EA(targetHref, 'vid');
          break;
        case DirectionEnum.LEFT:
          console.log('Streamlink: ' + targetHref);
          EA(targetHref, 'stream');
          break;
        case DirectionEnum.UP:
          console.log('Open: ' + targetHref);
          EA(targetHref, 'list');
          break;
        case DirectionEnum.DOWN:
          console.log('YTDL: ' + targetHref);
          EA(targetHref, 'ytdl');
          break;

        case DirectionEnum.UP_LEFT:
        case DirectionEnum.UP_RIGHT:
        case DirectionEnum.DOWN_LEFT:
        case DirectionEnum.DOWN_RIGHT:
        default:
          break;
      }
      //}
      console.log(direction);
      this.removeEventListener('dragend', doEA);
    }, false);
  }, false);
}

var count = 0;
var mouseIsDown = false;
var held = false;
document.addEventListener("mousedown", function (e) {
    var link = GM_getParentByTagName(e.target, 'A');
    if (link.nodeName == 'A') {

      mouseIsDown = true;
      document.addEventListener("mouseup", function mouseup(e) {
          mouseIsDown = false;
          this.removeEventListener('mouseup', mouseup);
      });
      document.addEventListener("contextmenu", function contextmenu(e) {
          if (held == true) {
              held = false;
              e.preventDefault();
          }
          held = false;
          this.removeEventListener('contextmenu', contextmenu);
      });
      if (e.button === 2) {
          setTimeout(function () {
              if (mouseIsDown) {
                      if (collected_urls[link.href] == undefined) {
                          //var ele = GM_eleTOPele(e.target);
                          //document.body.appendChild(ele);
                          //collected_urls[link.href] = ele;
                          collected_urls[link.href] = e.target;
                          e.target.style.boxSizing = 'border-box';
                          e.target.style.border = 'solid yellow 4px';
                          //popup('Added: ' + link.href, e.clientX, e.clientY)
                      } else {
                          //collected_urls[link.href].parentNode.removeChild(collected_urls[link.href]);
                          collected_urls[link.href].style.boxSizing = 'unset';
                          collected_urls[link.href].style.border = 'unset';
                          delete collected_urls[link.href];
                          //e.target.style.boxSizing = 'unset';
                          //e.target.style.border = 'unset';
                      }
                  console.log(collected_urls);
                  count = 0;
                  mouseIsDown = false;
              held = true;
              }
          }, 200);
      }
    }

});

attachDrag(document);
var attachedeles = [];
document.addEventListener('mouseover', function(e) {
  if (e.target.shadowRoot) {
    if (attachedeles.includes(e.target) == false) {
      console.log(attachedeles);
      attachedeles.push(e.target);
      attachDrag(e.target.shadowRoot);
    }
  }
});

2. Tài file protocol_hook.zip bên dưới vào thẳng folder scripts của MPV, giải nén Extract Here.
3. Mở file protocol_hook.lua bằng Notepad rồi tìm dòng local cwd = 'D:/mpv', nếu thư mục chứa MPV của các bạn khác cái này thì tự sửa lại, nhớ dùng / phân cách các phần đường dẫn để đảm bảo tính tương thích nhiều hệ điều hành, giống của mình bên trên ấy. (bản mới tự nhận ra đường dẫn, không nhận mới cần tự làm)

3. Phần handlers.json Firefox:

  • Từ Firefox gõ about:support
  • Open Profile Folder
  • Mở file handlers.json lên, sẽ thấy đoạn kiểu "schemes":{{...}, {...}, {...}}, thêm , "mpv":{"action":2,"handlers":[{"name":"MPV","path":"D:\\mpv\\mpv.exe"}] vào cuối cùng của cái đoạn đó, mà sẽ thành như này: "schemes":{{...}, {...}, {...}, "mpv":{"action":2,"handlers":[{"name":"MPV","path":"D:\\mpv\\mpv.exe"}]}
  • Ví dụ file của mình (nếu muốn có thể lấy file này luôn): {"defaultHandlersVersion":{},"mimeTypes":{},"schemes":{"tg":{"action":4}, "mpv":{"action":2,"handlers":[{"name":"MPV","path":"D:\\mpv\\mpv.exe"}]}},"isDownloadsImprovementsAlreadyMigrated":true,"isSVGXMLAlreadyMigrated":true}
  • Tắt Firefox
  • Save lại rồi khởi động lại Firefox
Chú ý: Nếu vị trí của MPV ở chỗ khác thì tự chỉnh nhé.

Thưởng thức thôi.
1696158610907.png
1696158627942.png

bác cho e hỏi vs ạ e có làm theo bác nhưng sao trong mpv của e k có mục scripts ạ
0kWR44v.gif
 
Nhìn chung thì đây là thứ tự các bước gâu bờ dồ khi chơi MPV với Firefox, để mình làm một bài đúng quy trình luôn:

Chú ý những cái (Tùy nhu cầu) có thể bỏ qua nếu không cần tới.

(Chú ý) Với người dùng Linux thì cài bằng Package Manager, Mac thì có thể dùng cách tạo symlink (nhanh nhất), Homebrew, MacPorts hoặc cài từ mã nguồn cho máu. Chỉ khác biệt duy nhất là các bước sau đó người dùng Linux và Mac sẽ không cần đường dẫn mà dùng mpv
  1. Tải mpv, ffmpeg, yt-dlp và giải nén vào thư mục D:\mpv
  2. Cách sử dụng Handlers của Firefox để xổ MPV không cần addon+native-client
Kết cục:

ezgif-5-76a8e47ef4.webp
 
Last edited:
Bác Gei chắc nên tinh giản các bài hướng dẫn lại, đoạn này bắt đầu tẩu hỏa nhập ma rồi, e theo dõi từ đầu thì có thể làm theo được, nếu pack được thành 1 gói ăn sẵn là oke nhất, có khi góp ý cho Floorp mấy cái tweak của mình cho nó native được luôn
 
Còn vụ xem stream hiển thị live comments nữa thím @toi la gay :sosad: ơi :(:oops:
Hiện tại lên Handlers rồi mấy cái này sau này làm được dễ dàng hơn nhiều, tại Handlers Helper giờ phân bu rõ ràng là kéo trái là xem stream, giờ mod lại bồi code từ

Vào là ok, test thử xem nhé bụng to không chịu trách nhiệm :D

Code:
// ==UserScript==
// @name        Handlers Helper
// @include       *://*/*
// @grant       none
// @version     1.7
// @author      -
// @description Helper for protocol_hook.lua
// @namespace Violentmonkey Scripts
// ==/UserScript==

var collected_urls = {};
function GM_getParentByTagName(el, tagName) {
  tagName = tagName.toLowerCase();
  if (el.tagName.toLowerCase() == tagName) {
    return el;
  }
  while (el && el.parentNode) {
    el = el.parentNode;
    if (el.tagName && el.tagName.toLowerCase() == tagName) {
      return el;
    }
  }
  return "undefined";
}

function attachDrag(elem) {

  function GM_btoaUrl(url) {
    return btoa(url).replace(/\//g, "_").replace(/\+/g, "-").replace(/\=/g, "");
  }

  function EA(attr, type) {
    var url = '';
    var subs = '';
    var s = '';
    console.log(attr, type)
    if (attr.startsWith('http')) {
      url = attr;
    } else if (attr.startsWith('mpv://')) {
      location.href = attr;
      return;
    }
    if (url == '') {
      url = location.href;
    }
    console.log(collected_urls);
    if (Object.keys(collected_urls).length > 0) {
        for (link in collected_urls) {
            console.log(link, collected_urls[link]);
            collected_urls[link].style.boxSizing = 'unset';
            collected_urls[link].style.border = 'unset';
            s += link + ' ';
        }
        s = s.trim(' ');
        console.log(s);
        //var s = collected_urls.join(" ");
    } else {
        var s = url;
    }
    collected_urls = {};
    var app = 'play';
    if (type != 'vid') {
      var app = type.toLowerCase();
    }
    var bs = GM_btoaUrl(s);
    var url2 = 'mpv://' + app + '/' + bs + '/' + "?referer=" + GM_btoaUrl(location.href);
    if (subs != '') {
      url2 = url2 + '?subs=' + GM_btoaUrl(subs);
    }
    //alert(url2);
    if (app == 'stream') {
        var nurl = new URL(url);
        if (nurl.href.indexOf('www.youtube.com/watch') != -1 || nurl.href.indexOf('m.youtube.com/watch') != -1) {
        window.open("https://www.youtube.com/live_chat?is_popout=1&v=" + nurl.search.split("v=")[1], "", "fullscreen=no,toolbar=no,titlebar=no,menubar=no,location=no,width=800,height=600")

        } else if (nurl.href.match('https://.*?.twitch.tv/.')) {
        window.open("https://www.twitch.tv/popout" + nurl.pathname + "/chat?popout=", "", "fullscreen=no,toolbar=no,titlebar=no,menubar=no,location=no,width=800,height=600")
        }
    }
    location.href = url2;
  }

  // Define the enum-like directory
  const DirectionEnum = {
    RIGHT: 6,
    LEFT: 4,
    UP: 2,
    DOWN: 8,
    UP_LEFT: 1,
    UP_RIGHT: 3,
    DOWN_LEFT: 7,
    DOWN_RIGHT: 9
  };

  function getDirection(x, y, cx, cy) {
    /*=================
    |                 |
    | 1↖   2↑   3↗ |
    |                 |
    | 4←    5    6→ |
    |                 |
    | 7↙   8↓   9↘ |
    |                 |
    |=================*/
    let d, t;
    if ((cx - x) >= -50 && (cx - x) <= 50 && (cy - y) >= -50 && (cy - y) <= 50) {
      return 5;
    }
    // Change (4 == 4) to (8 == 4) to enable 8 directions
    if (4 == 4) { //4 directions
      if (Math.abs(cx - x) < Math.abs(cy - y)) {
        d = cy > y ? "8" : "2";
      } else {
        d = cx > x ? "6" : "4";
      }
    } else { //8 directions
      t = (cy - y) / (cx - x);
      if (-0.4142 <= t && t < 0.4142) d = cx > x ? '6' : "4";
      else if (2.4142 <= t || t < -2.4142) d = cy > y ? '8' : '2';
      else if (0.4142 <= t && t < 2.4142) d = cx > x ? '9' : '1';
      else d = cy > y ? '7' : '3';
    }
    return d;
  }
  elem.addEventListener('dragstart', function(e) {
    //console.log(e.target);
    //console.log(e.target.shadowRoot);
    /*if (e.target.nodeName != "A") {
    e.stopPropagation();
    e.stopImmediatePropagation();
    //e.preventDefault();
    }*/
    console.log('dragstart');
    var x1 = e.clientX;
    var y1 = e.clientY;
    var dragend = elem.addEventListener('dragend', function doEA(e) {
      var x2 = e.clientX;
      var y2 = e.clientY;
      var direction = getDirection(x1, y1, x2, y2);
      //if ((x2 - x1) >= -50 && (x2 - x1) <= 50 && (y2 - y1) >= -50 && (y2 - y1) <= 50) {direction = 5;console.log(5);}
      //if (e.target.nodeName == "A" && e.target.href.match(/youtube.com|youtu.be|streamable.com/)) {
      console.log('Direction: ' + direction);
      console.log(x1, y1, x2, y2, direction);

      const targetHref = e.target.href || e.target.src;

      switch (+direction) {
        case DirectionEnum.RIGHT:
          console.log('MPV: ' + targetHref);
          EA(targetHref, 'vid');
          break;
        case DirectionEnum.LEFT:
          console.log('Streamlink: ' + targetHref);
          EA(targetHref, 'stream');
          break;
        case DirectionEnum.UP:
          console.log('Open: ' + targetHref);
          EA(targetHref, 'list');
          break;
        case DirectionEnum.DOWN:
          console.log('YTDL: ' + targetHref);
          EA(targetHref, 'ytdl');
          break;

        case DirectionEnum.UP_LEFT:
        case DirectionEnum.UP_RIGHT:
        case DirectionEnum.DOWN_LEFT:
        case DirectionEnum.DOWN_RIGHT:
        default:
          break;
      }
      //}
      console.log(direction);
      this.removeEventListener('dragend', doEA);
    }, false);
  }, false);
}

var count = 0;
var mouseIsDown = false;
var held = false;
document.addEventListener("mousedown", function (e) {
    var link = GM_getParentByTagName(e.target, 'A');
    if (link.nodeName == 'A') {

      mouseIsDown = true;
      document.addEventListener("mouseup", function mouseup(e) {
          mouseIsDown = false;
          this.removeEventListener('mouseup', mouseup);
      });
      document.addEventListener("contextmenu", function contextmenu(e) {
          if (held == true) {
              held = false;
              e.preventDefault();
          }
          held = false;
          this.removeEventListener('contextmenu', contextmenu);
      });
      if (e.button === 2) {
          setTimeout(function () {
              if (mouseIsDown) {
                      if (collected_urls[link.href] == undefined) {
                          //var ele = GM_eleTOPele(e.target);
                          //document.body.appendChild(ele);
                          //collected_urls[link.href] = ele;
                          collected_urls[link.href] = e.target;
                          e.target.style.boxSizing = 'border-box';
                          e.target.style.border = 'solid yellow 4px';
                          //popup('Added: ' + link.href, e.clientX, e.clientY)
                      } else {
                          //collected_urls[link.href].parentNode.removeChild(collected_urls[link.href]);
                          collected_urls[link.href].style.boxSizing = 'unset';
                          collected_urls[link.href].style.border = 'unset';
                          delete collected_urls[link.href];
                          //e.target.style.boxSizing = 'unset';
                          //e.target.style.border = 'unset';
                      }
                  console.log(collected_urls);
                  count = 0;
                  mouseIsDown = false;
              held = true;
              }
          }, 200);
      }
    }

});

attachDrag(document);
var attachedeles = [];
document.addEventListener('mouseover', function(e) {
  if (e.target.shadowRoot) {
    if (attachedeles.includes(e.target) == false) {
      console.log(attachedeles);
      attachedeles.push(e.target);
      attachDrag(e.target.shadowRoot);
    }
  }
});
 
Bác Gei chắc nên tinh giản các bài hướng dẫn lại, đoạn này bắt đầu tẩu hỏa nhập ma rồi, e theo dõi từ đầu thì có thể làm theo được, nếu pack được thành 1 gói ăn sẵn là oke nhất, có khi góp ý cho Floorp mấy cái tweak của mình cho nó native được luôn
Cái Dark Mode nửa mùa này không phải tối ưu đâu mà nó mang tính cá nhân hóa, người này thấy đẹp nhưng rất nhiều người khác thấy xấu, sau này cứ làm thành file user.js copy+paste là nhanh nhất, thích trường phái nào thì copy+paste thôi.
 
1696169288657.png

Đã mất công cài mpv để xem youtube thì cài thêm cái này nữa thôi các fence
https://github.com/cvzi/mpv-youtube-upnext tự nhảy youtube sang bài tiếp theo :adore:
với
https://github.com/CogentRedTester/mpv-scripts/blob/master/youtube-search.lua để search youtube trực tiếp trên mpv :beauty:

muốn nghe linh tinh thì cũng không cần mở firefox nữa, lên mpv rồi search rồi vứt đấy cho nó tự chạy

chạy mpv --no-video --force-window là chả khác gì Youtube Music

1696169798850.png
 
mở link youtube bằng mpv bình thường, nhưng mà cái handlers helper khi kéo link thì nó hiện dấu 🚫 này, không biết fix sao các bác nhỉ
 
mở link youtube bằng mpv bình thường, nhưng mà cái handlers helper khi kéo link thì nó hiện dấu 🚫 này, không biết fix sao các bác nhỉ
Chắc tắt mất cái drag preview rồi:
nglayout.enable_drag_imagestrue

Thường một số tối ưu sẽ bảo tắt đi, ví dụ như mình nhưng ở thread này mình chưa phổ biến cái này tại nó phế, cái dấu
🚫
tắt triệt bằng CSS được nhưng mà cũng hơi tốn công mà không cần thiết:
1696170135344.png
 
Back
Top