commit f2a2ce737556b329dda81b67c5a6f87d7fab1952
parent 7eb71148f16e533df01feeb1a642185119e5eac9
Author: Alex Balgavy <a.balgavy@gmail.com>
Date:   Sat, 14 Nov 2020 12:56:31 +0100
mpv: switched to a different sponsorblock script
Former-commit-id: 2abf26b61b4cfac16bdb5c81d602b751852480a3
Diffstat:
5 files changed, 81 insertions(+), 522 deletions(-)
diff --git a/mpv/scripts/shared/sponsorblock.py b/mpv/scripts/shared/sponsorblock.py
@@ -1,110 +0,0 @@
-import urllib.request
-import urllib.parse
-import sqlite3
-import random
-import string
-import json
-import sys
-import os
-
-if sys.argv[1] in ["submit", "stats", "username"]:
-    if not sys.argv[8]:
-        if os.path.isfile(sys.argv[7]):
-            with open(sys.argv[7]) as f:  
-                uid = f.read()
-        else:
-            uid = "".join(random.choices(string.ascii_letters + string.digits, k=36))
-            with open(sys.argv[7], "w") as f:
-                f.write(uid)
-    else:
-        uid = sys.argv[8]
-
-opener = urllib.request.build_opener()
-opener.addheaders = [("User-Agent", "mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)")]
-urllib.request.install_opener(opener)
-
-if sys.argv[1] == "ranges" and not sys.argv[2]:
-    times = []
-    try:
-        response = urllib.request.urlopen(sys.argv[3] + "/api/getVideoSponsorTimes?videoID=" + sys.argv[4])
-        data = json.load(response)
-        for i, time in enumerate(data["sponsorTimes"]):
-            times.append(str(time[0]) + "," + str(time[1]) + "," + data["UUIDs"][i])
-        print(":".join(times))
-    except (TimeoutError, urllib.error.URLError) as e:
-        print("error")
-    except urllib.error.HTTPError as e:
-        if e.code == 404:
-            print("")
-        else:
-            print("error")
-elif sys.argv[1] == "ranges":
-    conn = sqlite3.connect(sys.argv[2])
-    conn.row_factory = sqlite3.Row
-    c = conn.cursor()
-    c.execute("SELECT startTime, endTime, votes, UUID FROM sponsorTimes WHERE videoID = ? AND shadowHidden = 0 AND votes > -1", (sys.argv[4],))
-    times = []
-    sponsors = c.fetchall()
-    best = list(sponsors)
-    dealtwith = []
-    similar = []
-    for sponsor_a in sponsors:
-        for sponsor_b in sponsors:
-            if sponsor_a is not sponsor_b and sponsor_a["startTime"] >= sponsor_b["startTime"] and sponsor_a["startTime"] <= sponsor_b["endTime"]:
-                similar.append([sponsor_a, sponsor_b])
-                if sponsor_a in best:
-                    best.remove(sponsor_a)
-                if sponsor_b in best:
-                    best.remove(sponsor_b)
-    for sponsors_a in similar:
-        if sponsors_a in dealtwith:
-            continue
-        group = set(sponsors_a)
-        for sponsors_b in similar:
-            if sponsors_b[0] in group or sponsors_b[1] in group:
-                group.add(sponsors_b[0])
-                group.add(sponsors_b[1])
-                dealtwith.append(sponsors_b)
-        best.append(max(group, key=lambda x:x["votes"]))
-    for time in best:
-        times.append(str(time["startTime"]) + "," + str(time["endTime"]) + "," + time["UUID"])
-    print(":".join(times))
-elif sys.argv[1] == "update":
-    try:
-        urllib.request.urlretrieve(sys.argv[3] + "/database.db", sys.argv[2] + ".tmp")
-        os.replace(sys.argv[2] + ".tmp", sys.argv[2])
-    except PermissionError:
-        print("database update failed, file currently in use", file=sys.stderr)
-        exit(1)
-    except ConnectionResetError:
-        print("database update failed, connection reset", file=sys.stderr)
-        exit(1)
-    except TimeoutError:
-        print("database update failed, timed out", file=sys.stderr)
-        exit(1)
-    except urllib.error.URLError:
-        print("database update failed", file=sys.stderr)
-        exit(1)
-elif sys.argv[1] == "submit":
-    try:
-        response = urllib.request.urlopen(sys.argv[3] + "/api/postVideoSponsorTimes?videoID=" + sys.argv[4] + "&startTime=" + sys.argv[5] + "&endTime=" + sys.argv[6] + "&userID=" + uid)
-        print("success")
-    except urllib.error.HTTPError as e:
-        print(e.code)
-    except:
-        print("error")
-elif sys.argv[1] == "stats":
-    try:
-        if sys.argv[6]:
-            urllib.request.urlopen(sys.argv[3] + "/api/viewedVideoSponsorTime?UUID=" + sys.argv[5])
-        if sys.argv[9]:
-            urllib.request.urlopen(sys.argv[3] + "/api/voteOnSponsorTime?UUID=" + sys.argv[5] + "&userID=" + uid + "&type=" + sys.argv[9])
-    except:
-        pass
-elif sys.argv[1] == "username":
-    try:
-        data = urllib.parse.urlencode({"userID": uid, "userName": sys.argv[9]}).encode()
-        req =  urllib.request.Request(sys.argv[3] + "/api/setUsername", data=data)
-        urllib.request.urlopen(req)
-    except:
-        pass-
\ No newline at end of file
diff --git a/mpv/scripts/shared/sponsorblock.txt b/mpv/scripts/shared/sponsorblock.txt
@@ -1 +0,0 @@
-HjUNZJ3q8rowUExdUH3jd4rxz4RqTzf0t39g-
\ No newline at end of file
diff --git a/mpv/scripts/sponsorblock.lua b/mpv/scripts/sponsorblock.lua
@@ -1,408 +0,0 @@
--- sponsorblock.lua
---
--- This script skips sponsored segments of YouTube videos
--- using data from https://github.com/ajayyy/SponsorBlock
-
-local ON_WINDOWS = package.config:sub(1,1) ~= '/'
-
-local options = {
-    server_address = "https://api.sponsor.ajay.app",
-
-    python_path = ON_WINDOWS and "python" or "python3",
-
-    -- If true, sponsored segments will only be skipped once
-    skip_once = true,
-
-    -- Note that sponsored segments may ocasionally be inaccurate if this is turned off
-    -- see https://ajay.app/blog.html#voting-and-pseudo-randomness-or-sponsorblock-or-youtube-sponsorship-segment-blocker
-    local_database = true,
-
-    -- Update database on first run, does nothing if local_database is false
-    auto_update = true,
-
-    -- User ID used to submit sponsored segments, leave blank for random
-    user_id = "",
-
-    -- Name to display on the stats page https://sponsor.ajay.app/stats/ leave blank to keep current name
-    display_name = "",
-
-    -- Tell the server when a skip happens
-    report_views = true,
-
-    -- Auto upvote skipped sponsors
-    auto_upvote = true,
-
-    -- Use sponsor times from server if they're more up to date than our local database
-    server_fallback = true,
-
-    -- Minimum duration for sponsors (in seconds), segments under that threshold will be ignored
-    min_duration = 1,
-
-    -- Fade audio for smoother transitions
-    audio_fade = false,
-
-    -- Audio fade step, applied once every 100ms until cap is reached
-    audio_fade_step = 10,
-
-    -- Audio fade cap
-    audio_fade_cap = 0,
-
-    -- Fast forward through sponsors instead of skipping
-    fast_forward = false,
-
-    -- Playback speed modifier when fast forwarding, applied once every second until cap is reached
-    fast_forward_increase = .2,
-
-    -- Playback speed cap
-    fast_forward_cap = 2,
-
-    -- Pattern for video id in local files, ignored if blank
-    -- Recommended value for base youtube-dl is "-([%a%d%-_]+)%.[mw][kpe][v4b][m]?$"
-    local_pattern = ""
-}
-
-mp.options = require "mp.options"
-mp.options.read_options(options, "sponsorblock")
-
-local legacy = mp.command_native_async == nil
-if legacy then
-    options.local_database = false
-end
-
-local utils = require "mp.utils"
-local scripts_dir = mp.find_config_file("scripts")
-local sponsorblock = utils.join_path(scripts_dir, "shared/sponsorblock.py")
-local uid_path = utils.join_path(scripts_dir, "shared/sponsorblock.txt")
-local database_file = options.local_database and utils.join_path(scripts_dir, "shared/sponsorblock.db") or ""
-local youtube_id = nil
-local ranges = {}
-local init = false
-local segment = {a = 0, b = 0, progress = 0}
-local retrying = false
-local last_skip = {uuid = "", dir = nil}
-local speed_timer = nil
-local fade_timer = nil
-local fade_dir = nil
-local volume_before = mp.get_property_number("volume")
-
-function file_exists(name)
-    local f = io.open(name,"r")
-    if f ~= nil then io.close(f) return true else return false end
-end
-
-function t_count(t)
-    local count = 0
-    for _ in pairs(t) do count = count + 1 end
-    return count
-end
-
-function getranges(_, exists, db, more)
-    if type(exists) == "table" and exists["status"] == "1" then
-        if options.server_fallback then
-            mp.add_timeout(0, function() getranges(true, true, "") end)
-        else
-            return mp.osd_message("[sponsorblock] database update failed, gave up")
-        end
-    end
-    if db ~= "" and db ~= database_file then db = database_file end
-    if exists ~= true and not file_exists(db) then
-        if not retrying then
-            mp.osd_message("[sponsorblock] database update failed, retrying...")
-            retrying = true
-        end
-        return update()
-    end
-    if retrying then
-        mp.osd_message("[sponsorblock] database update succeeded")
-        retrying = false
-    end
-    local sponsors
-    local args = {
-        options.python_path,
-        sponsorblock,
-        "ranges",
-        db,
-        options.server_address,
-        youtube_id
-    }
-    if not legacy then
-        sponsors = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
-    else
-        sponsors = utils.subprocess({args = args})
-    end
-    if not string.match(sponsors.stdout, "^%s*(.*%S)") then return end
-    if string.match(sponsors.stdout, "error") then return getranges(true, true) end
-    local new_ranges = {}
-    local r_count = 0
-    if more then r_count = -1 end
-    for t in string.gmatch(sponsors.stdout, "[^:%s]+") do
-        uuid = string.match(t, '[^,]+$')
-        if ranges[uuid] then
-            new_ranges[uuid] = ranges[uuid]
-        else
-            start_time = tonumber(string.match(t, '[^,]+'))
-            end_time = tonumber(string.sub(string.match(t, ',[^,]+'), 2))
-            if end_time - start_time >= options.min_duration then
-                new_ranges[uuid] = {
-                    start_time = start_time,
-                    end_time = end_time,
-                    skipped = false
-                }
-            end
-        end
-        r_count = r_count + 1
-    end
-    local c_count = t_count(ranges)
-    if c_count == 0 or r_count >= c_count then
-        ranges = new_ranges
-    end
-end
-
-function fast_forward()
-    local last_speed = mp.get_property_number("speed")
-    local new_speed = math.min(last_speed + options.fast_forward_increase, options.fast_forward_cap)
-    if new_speed <= last_speed then return end
-    mp.set_property("speed", new_speed)
-end
-
-function fade_audio(step)
-    local last_volume = mp.get_property_number("volume")
-    local new_volume = math.max(options.audio_fade_cap, math.min(last_volume + step, volume_before))
-    if new_volume == last_volume then
-        if step >= 0 then fade_dir = nil end
-        if fade_timer ~= nil then fade_timer:kill() end
-        fade_timer = nil
-        return
-    end
-    mp.set_property("volume", new_volume)
-end
-
-function skip_ads(name, pos)
-    if pos == nil then return end
-    local sponsor_ahead = false
-    for uuid, t in pairs(ranges) do
-        if (options.fast_forward == uuid or not options.skip_once or not t.skipped) and t.start_time <= pos and t.end_time > pos then
-            if options.fast_forward == uuid then return end
-            if options.fast_forward == false then
-                mp.osd_message("[sponsorblock] sponsor skipped")
-                mp.set_property("time-pos", t.end_time)
-            else
-                mp.osd_message("[sponsorblock] skipping sponsor")
-            end
-            t.skipped = true
-            last_skip = {uuid = uuid, dir = nil}
-            if options.report_views or options.auto_upvote then
-                local args = {
-                    options.python_path,
-                    sponsorblock,
-                    "stats",
-                    database_file,
-                    options.server_address,
-                    youtube_id,
-                    uuid,
-                    options.report_views and "1" or "",
-                    uid_path,
-                    options.user_id,
-                    options.auto_upvote and "1" or ""
-                }
-                if not legacy then
-                    mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
-                else
-                    utils.subprocess_detached({args = args})
-                end
-            end
-            if options.fast_forward ~= false then
-                options.fast_forward = uuid
-                speed_timer = mp.add_periodic_timer(1, fast_forward)
-            end
-            return
-        elseif (not options.skip_once or not t.skipped) and t.start_time <= pos + 1 and t.end_time > pos + 1 then
-            sponsor_ahead = true
-        end
-    end
-    if options.audio_fade then
-        if sponsor_ahead then
-            if fade_dir ~= false then
-                if fade_dir == nil then volume_before = mp.get_property_number("volume") end
-                if fade_timer ~= nil then fade_timer:kill() end
-                fade_dir = false
-                fade_timer = mp.add_periodic_timer(.1, function() fade_audio(-options.audio_fade_step) end)
-            end
-        elseif fade_dir == false then
-            fade_dir = true
-            if fade_timer ~= nil then fade_timer:kill() end
-            fade_timer = mp.add_periodic_timer(.1, function() fade_audio(options.audio_fade_step) end)
-        end
-    end
-    if options.fast_forward and options.fast_forward ~= true then
-        options.fast_forward = true
-        speed_timer:kill()
-        mp.set_property("speed", 1)
-    end
-end
-
-function vote(dir)
-    if last_skip.uuid == "" then return mp.osd_message("[sponsorblock] no sponsors skipped, can't submit vote") end
-    local updown = dir == "1" and "up" or "down"
-    if last_skip.dir == dir then return mp.osd_message("[sponsorblock] " .. updown .. "vote already submitted") end
-    last_skip.dir = dir
-    local args = {
-        options.python_path,
-        sponsorblock,
-        "stats",
-        database_file,
-        options.server_address,
-        youtube_id,
-        last_skip.uuid,
-        "",
-        uid_path,
-        options.user_id,
-        dir
-    }
-    if not legacy then
-        mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
-    else
-        utils.subprocess({args = args})
-    end
-    mp.osd_message("[sponsorblock] " .. updown .. "vote submitted")
-end
-
-function update()
-    mp.command_native_async({name = "subprocess", playback_only = false, args = {
-        options.python_path,
-        sponsorblock,
-        "update",
-        database_file,
-        options.server_address
-    }}, getranges)
-end
-
-function file_loaded()
-    local initialized = init
-    ranges = {}
-    segment = {a = 0, b = 0, progress = 0}
-    last_skip = {uuid = "", dir = nil}
-    local video_path = mp.get_property("path")
-    local youtube_id1 = string.match(video_path, "https?://youtu%.be/([%a%d%-_]+).*")
-    local youtube_id2 = string.match(video_path, "https?://w?w?w?%.?youtube%.com/v/([%a%d%-_]+).*")
-    local youtube_id3 = string.match(video_path, "/watch%?v=([%a%d%-_]+).*")
-    local youtube_id4 = string.match(video_path, "/embed/([%a%d%-_]+).*")
-    local local_pattern = nil
-    if options.local_pattern ~= "" then
-        local_pattern = string.match(video_path, options.local_pattern)
-    end
-    youtube_id = youtube_id1 or youtube_id2 or youtube_id3 or youtube_id4 or local_pattern
-    if not youtube_id then return end
-    init = true
-    if not options.local_database then
-        getranges(true, true)
-    else
-        local exists = file_exists(database_file)
-        if exists and options.server_fallback then
-            getranges(true, true)
-            mp.add_timeout(0, function() getranges(true, true, "", true) end)
-        elseif exists then
-            getranges(true, true)
-        elseif options.server_fallback then
-            mp.add_timeout(0, function() getranges(true, true, "") end)
-        end
-    end
-    if initialized then return end
-    mp.observe_property("time-pos", "native", skip_ads)
-    if options.display_name ~= "" then
-        local args = {
-            options.python_path,
-            sponsorblock,
-            "username",
-            database_file,
-            options.server_address,
-            youtube_id,
-            "",
-            "",
-            uid_path,
-            options.user_id,
-            options.display_name
-        }
-        if not legacy then
-            mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
-        else
-            utils.subprocess_detached({args = args})
-        end
-    end
-    if not options.local_database or (not options.auto_update and file_exists(database_file)) then return end
-    update()
-end
-
-function set_segment()
-    if not youtube_id then return end
-    local pos = mp.get_property_number("time-pos")
-    if pos == nil then return end
-    if segment.progress > 1 then
-        segment.progress = segment.progress - 2
-    end
-    if segment.progress == 1 then
-        segment.progress = 0
-        segment.b = pos
-        mp.osd_message("[sponsorblock] segment boundary B set, press again for boundary A", 3)
-    else
-        segment.progress = 1
-        segment.a = pos
-        mp.osd_message("[sponsorblock] segment boundary A set, press again for boundary B", 3)
-    end
-end
-
-function submit_segment()
-    if not youtube_id then return end
-    local start_time = math.min(segment.a, segment.b)
-    local end_time = math.max(segment.a, segment.b)
-    if end_time - start_time == 0 or end_time == 0 then
-        mp.osd_message("[sponsorblock] empty segment, not submitting")
-    elseif segment.progress <= 1 then
-        mp.osd_message(string.format("[sponsorblock] press Shift+G again to confirm: %.2d:%.2d:%.2d to %.2d:%.2d:%.2d", start_time/(60*60), start_time/60%60, start_time%60, end_time/(60*60), end_time/60%60, end_time%60), 5)
-        segment.progress = segment.progress + 2
-    else
-        mp.osd_message("[sponsorblock] submitting segment...", 30)
-        local submit
-        local args = {
-            options.python_path,
-            sponsorblock,
-            "submit",
-            database_file,
-            options.server_address,
-            youtube_id,
-            tostring(start_time),
-            tostring(end_time),
-            uid_path,
-            options.user_id
-        }
-        if not legacy then
-            submit = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
-        else
-            submit = utils.subprocess({args = args})
-        end
-        if string.match(submit.stdout, "success") then
-            segment = {a = 0, b = 0, progress = 0}
-            mp.osd_message("[sponsorblock] segment submitted")
-        elseif string.match(submit.stdout, "error") then
-            mp.osd_message("[sponsorblock] segment submission failed, server may be down. try again", 5)
-        elseif string.match(submit.stdout, "502") then
-            mp.osd_message("[sponsorblock] segment submission failed, server is down. try again", 5)
-        elseif string.match(submit.stdout, "400") then
-            mp.osd_message("[sponsorblock] segment submission failed, impossible inputs", 5)
-            segment = {a = 0, b = 0, progress = 0}
-        elseif string.match(submit.stdout, "429") then
-            mp.osd_message("[sponsorblock] segment submission failed, rate limited. try again", 5)
-        elseif string.match(submit.stdout, "409") then
-            mp.osd_message("[sponsorblock] segment already submitted", 3)
-            segment = {a = 0, b = 0, progress = 0}
-        else
-            mp.osd_message("[sponsorblock] segment submission failed", 5)
-        end
-    end
-end
-
-mp.register_event("file-loaded", file_loaded)
-mp.add_key_binding("g", "sponsorblock_set_segment", set_segment)
-mp.add_key_binding("G", "sponsorblock_submit_segment", submit_segment)
-mp.add_key_binding("h", "sponsorblock_upvote", function() return vote("1") end)
-mp.add_key_binding("H", "sponsorblock_downvote", function() return vote("0") end)
diff --git a/mpv/scripts/sponsorblock_minimal.lua b/mpv/scripts/sponsorblock_minimal.lua
@@ -0,0 +1,80 @@
+-- sponsorblock_minimal.lua
+-- https://codeberg.org/jouni/mpv_sponsorblock_minimal
+-- This script skips sponsored segments of YouTube videos
+-- using data from https://github.com/ajayyy/SponsorBlock
+
+local options = {
+    API = "https://sponsor.ajay.app/api/skipSegments",
+
+    -- Categories to fetch and skip
+    categories = '"sponsor","intro","outro","interaction","selfpromo"'
+}
+
+function getranges()
+    	local args = {
+        	"curl",
+		"-s",
+        	"-d",
+        	"videoID="..youtube_id,
+        	"-d",
+		"categories=["..options.categories.."]",
+		"-G",
+        	options.API}
+    	local sponsors = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
+
+    	if string.match(sponsors.stdout,"%[(.-)%]") then
+		ranges = {}
+		for i in string.gmatch(string.sub(sponsors.stdout,2,-2),"%[(.-)%]") do
+			k,v = string.match(i,"(%d+.?%d*),(%d+.?%d*)")
+			ranges[k] = v
+		end
+	end
+	return
+end
+
+function skip_ads(name,pos)
+	if pos ~= nil then
+		for k,v in pairs(ranges) do
+			if tonumber(k) <= pos and tonumber(v) > pos then
+        			mp.osd_message("[sponsorblock] skipping forward "..math.floor(tonumber(v)-mp.get_property("time-pos")).."s")
+				mp.set_property("time-pos",tonumber(v)+0.01)
+            			return
+    			end
+		end
+	end
+	return
+end
+
+function file_loaded()
+	local video_path = mp.get_property("path")
+	local youtube_id1 = string.match(video_path, "https?://youtu%.be/([%w-_]+).*")
+	local youtube_id2 = string.match(video_path, "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*")
+	local youtube_id3 = string.match(video_path, "/watch.*[?&]v=([%w-_]+).*")
+	local youtube_id4 = string.match(video_path, "/embed/([%w-_]+).*")
+	youtube_id = youtube_id1 or youtube_id2 or youtube_id3 or youtube_id4
+	if not youtube_id or string.len(youtube_id) < 11 then return end
+	youtube_id = string.sub(youtube_id, 1, 11)
+
+	getranges()
+	if ranges then
+		ON = true
+		mp.add_key_binding("b","sponsorblock",toggle)
+		mp.observe_property("time-pos", "native", skip_ads)
+	end
+	return
+end
+
+function toggle()
+	if ON then
+		mp.unobserve_property(skip_ads)
+		mp.osd_message("[sponsorblock] off")
+		ON = false
+		return
+	end
+	mp.observe_property("time-pos", "native", skip_ads)
+	mp.osd_message("[sponsorblock] on")
+	ON = true
+	return
+end
+
+mp.register_event("file-loaded", file_loaded)
diff --git a/mpv/scripts/youtube-quality.lua b/mpv/scripts/youtube-quality.lua
@@ -1,5 +1,5 @@
 -- youtube-quality.lua
---
+-- https://github.com/jgreco/mpv-youtube-quality
 -- Change youtube video quality on the fly.
 --
 -- Diplays a menu that lets you switch to different ytdl-format settings while