Files
mpv-config/scripts/sponsorblock_minimal.lua
T
2026-03-27 07:06:16 +01:00

149 lines
4.8 KiB
Lua

-- sponsorblock_minimal.lua v 0.5.1
--
-- This script skip/mute sponsored segments of YouTube and bilibili videos
-- using data from https://github.com/ajayyy/SponsorBlock
-- and https://github.com/hanydd/BilibiliSponsorBlock
local opt = require 'mp.options'
local utils = require 'mp.utils'
local options = {
youtube_sponsor_server = "https://sponsor.ajay.app/api/skipSegments",
bilibili_sponsor_server = "https://bsbsb.top/api/skipSegments",
-- Categories to fetch
-- Perform skip/mute/mark chapter based on the 'actionType' returned
categories = '"sponsor"',
}
opt.read_options(options)
local ranges = nil
local video_id = nil
local sponsor_server = nil
local cache = {}
local mute = false
local ON = false
local function getranges(url)
local res = mp.command_native{
name = "subprocess",
capture_stdout = true,
playback_only = false,
args = {
"curl", "-L", "-s", "-g",
"-H", "origin: mpv-script/sponsorblock_minimal",
"-H", "x-ext-version: 0.5.1",
url
}
}
if res.status ~= 0 then
return nil
end
return utils.parse_json(res.stdout)
end
local function make_chapter(ranges)
local chapters_time = {}
local chapters_title = {}
local chapter_index = 0
local all_chapters = mp.get_property_native("chapter-list")
for _, v in pairs(ranges) do
table.insert(chapters_time, v.segment[1])
table.insert(chapters_title, v.category)
table.insert(chapters_time, v.segment[2])
table.insert(chapters_title, "normal")
end
for i = 1, #chapters_time do
chapter_index = chapter_index + 1
all_chapters[chapter_index] = {
title = chapters_title[i] or ("Chapter " .. string.format("%02.f", chapter_index)),
time = chapters_time[i]
}
end
table.sort(all_chapters, function(a, b) return a['time'] < b['time'] end)
mp.set_property_native("chapter-list", all_chapters)
end
local function skip_ads(_, pos)
if pos ~= nil and ranges ~= nil then
for _, v in pairs(ranges) do
if v.actionType == "skip" and v.segment[1] <= pos and v.segment[2] > pos then
--this message may sometimes be wrong
--it only seems to be a visual thing though
local time = math.floor(v.segment[2] - pos)
mp.osd_message(string.format("[sponsorblock] skipping forward %ds", time))
--need to do the +0.01 otherwise mpv will start spamming skip sometimes
mp.set_property("time-pos", v.segment[2] + 0.01)
elseif v.actionType == "mute" then
if v.segment[1] <= pos and v.segment[2] >= pos then
cache[v.segment[2]] = nil
mp.set_property_bool("mute", true)
elseif pos > v.segment[2] and not cache[v.segment[2]] and mute ~= false then
cache[v.segment[2]] = true
mp.set_property_bool("mute", false)
end
end
end
end
end
local function file_loaded()
cache = {}
local video_path = mp.get_property("path", "")
local video_referer = mp.get_property("http-header-fields", ""):match("[Rr]eferer:%s*([^,\r\n]+)") or ""
local purl = mp.get_property("metadata/by-key/PURL", "")
local bilibili = video_path:match("bilibili.com/video") or video_referer:match("bilibili.com/video") or false
mute = mp.get_property_bool("mute")
local urls = {
"ytdl://youtu%.be/([%w-_]+).*",
"ytdl://w?w?w?%.?youtube%.com/v/([%w-_]+).*",
"ytdl://w?w?w?%.?bilibili%.com/video/([%w-_]+).*",
"https?://youtu%.be/([%w-_]+).*",
"https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*",
"https?://w?w?w?%.?bilibili%.com/video/([%w-_]+).*",
"/watch.*[?&]v=([%w-_]+).*",
"/embed/([%w-_]+).*",
"^ytdl://([%w-_]+)$",
"-([%w-_]+)%."
}
for _, url in ipairs(urls) do
video_id = video_id or video_path:match(url) or video_referer:match(url) or purl:match(url)
end
if not video_id or string.len(video_id) < 11 then return end
if bilibili then
sponsor_server = options.bilibili_sponsor_server
video_id = string.sub(video_id, 1, 12)
else
sponsor_server = options.youtube_sponsor_server
video_id = string.sub(video_id, 1, 11)
end
local url = ("%s?videoID=%s&categories=[%s]"):format(sponsor_server, video_id, options.categories)
ranges = getranges(url)
if ranges ~= nil then
make_chapter(ranges)
ON = true
mp.observe_property("time-pos", "native", skip_ads)
end
end
local function end_file()
if not ON then return end
mp.unobserve_property(skip_ads)
video_id = nil
cache = nil
ranges = nil
ON = false
end
mp.register_event("file-loaded", file_loaded)
mp.register_event("end-file", end_file)