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