mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-06-11 21:13:32 -07:00
update
This commit is contained in:
@@ -210,6 +210,9 @@ trust_level = "trusted"
|
|||||||
[projects."/home/sudacode/.local/share/Anki2"]
|
[projects."/home/sudacode/.local/share/Anki2"]
|
||||||
trust_level = "trusted"
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/sudacode/projects/japanese/subminer.moe"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
[notice.model_migrations]
|
[notice.model_migrations]
|
||||||
"gpt-5.3-codex" = "gpt-5.4"
|
"gpt-5.3-codex" = "gpt-5.4"
|
||||||
|
|
||||||
@@ -234,6 +237,9 @@ enabled = true
|
|||||||
[plugins."browser@openai-bundled"]
|
[plugins."browser@openai-bundled"]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
|
[plugins."coderabbit@openai-curated"]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
[tui.model_availability_nux]
|
[tui.model_availability_nux]
|
||||||
"gpt-5.5" = 4
|
"gpt-5.5" = 4
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ hl.monitor({
|
|||||||
position = "0x0",
|
position = "0x0",
|
||||||
scale = 1,
|
scale = 1,
|
||||||
vrr = 2,
|
vrr = 2,
|
||||||
-- cm = srgb
|
cm = srgb,
|
||||||
-- Optional HDR settings
|
-- Optional HDR settings
|
||||||
cm = "hdr",
|
-- cm = "hdr",
|
||||||
bitdepth = 10,
|
bitdepth = 10,
|
||||||
sdr_min_luminance = 0.005,
|
sdr_min_luminance = 0.005,
|
||||||
sdr_max_luminance = 200,
|
sdr_max_luminance = 200,
|
||||||
|
|||||||
@@ -337,17 +337,8 @@ hl.window_rule({
|
|||||||
|
|
||||||
hl.window_rule({
|
hl.window_rule({
|
||||||
match = {
|
match = {
|
||||||
class = "SubMiner",
|
class = "^SubMiner$",
|
||||||
},
|
},
|
||||||
opacity = "1.0 override",
|
|
||||||
pin = false,
|
|
||||||
})
|
|
||||||
|
|
||||||
hl.window_rule({
|
|
||||||
match = {
|
|
||||||
class = "SubMiner",
|
|
||||||
},
|
|
||||||
|
|
||||||
float = true,
|
float = true,
|
||||||
border_size = 0,
|
border_size = 0,
|
||||||
xray = false,
|
xray = false,
|
||||||
@@ -356,8 +347,7 @@ hl.window_rule({
|
|||||||
no_dim = true,
|
no_dim = true,
|
||||||
opaque = true,
|
opaque = true,
|
||||||
dim_around = false,
|
dim_around = false,
|
||||||
allows_input = false,
|
opacity = "1.0 override 1.0 override",
|
||||||
opacity = "1.0 override",
|
|
||||||
pin = false,
|
pin = false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,454 +0,0 @@
|
|||||||
----------------------
|
|
||||||
-- #example ytdl_preload.conf
|
|
||||||
-- # make sure lines do not have trailing whitespace
|
|
||||||
-- # ytdl_opt has no sanity check and should be formatted exactly how it would appear in yt-dlp CLI, they are split into a key/value pair on whitespace
|
|
||||||
-- # at least on Windows, do not escape '\' in temp, just us a single one for each divider
|
|
||||||
|
|
||||||
-- #temp=R:\ytdltest
|
|
||||||
-- #ytdl_opt1=-r 50k
|
|
||||||
-- #ytdl_opt2=-N 5
|
|
||||||
-- #ytdl_opt#=etc
|
|
||||||
----------------------
|
|
||||||
local nextIndex
|
|
||||||
local caught = true
|
|
||||||
-- local pop = false
|
|
||||||
local ytdl = "yt-dlp"
|
|
||||||
local utils = require("mp.utils")
|
|
||||||
|
|
||||||
local options = require("mp.options")
|
|
||||||
local opts = {
|
|
||||||
temp = "/tmp/ytdl-preload",
|
|
||||||
ytdl_opt1 = "",
|
|
||||||
ytdl_opt2 = "",
|
|
||||||
ytdl_opt3 = "",
|
|
||||||
ytdl_opt4 = "",
|
|
||||||
ytdl_opt5 = "",
|
|
||||||
ytdl_opt6 = "",
|
|
||||||
ytdl_opt7 = "",
|
|
||||||
ytdl_opt8 = "",
|
|
||||||
ytdl_opt9 = "",
|
|
||||||
}
|
|
||||||
options.read_options(opts, "ytdl_preload")
|
|
||||||
local additionalOpts = {}
|
|
||||||
for k, v in pairs(opts) do
|
|
||||||
if k:find("ytdl_opt%d") and v ~= "" then
|
|
||||||
additionalOpts[k] = v
|
|
||||||
-- print("entry")
|
|
||||||
-- print(k .. v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local cachePath = opts.temp
|
|
||||||
|
|
||||||
local chapter_list = {}
|
|
||||||
local json = ""
|
|
||||||
local filesToDelete = {}
|
|
||||||
|
|
||||||
local function exists(file)
|
|
||||||
local ok, err, code = os.rename(file, file)
|
|
||||||
if not ok then
|
|
||||||
if code == 13 then -- Permission denied, but it exists
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ok, err
|
|
||||||
end
|
|
||||||
local function useNewLoadfile()
|
|
||||||
for _, c in pairs(mp.get_property_native("command-list")) do
|
|
||||||
if c["name"] == "loadfile" then
|
|
||||||
for _, a in pairs(c["args"]) do
|
|
||||||
if a["name"] == "index" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--from ytdl_hook
|
|
||||||
local function time_to_secs(time_string)
|
|
||||||
local ret
|
|
||||||
local a, b, c = time_string:match("(%d+):(%d%d?):(%d%d)")
|
|
||||||
if a ~= nil then
|
|
||||||
ret = (a * 3600 + b * 60 + c)
|
|
||||||
else
|
|
||||||
a, b = time_string:match("(%d%d?):(%d%d)")
|
|
||||||
if a ~= nil then
|
|
||||||
ret = (a * 60 + b)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
local function extract_chapters(data, video_length)
|
|
||||||
local ret = {}
|
|
||||||
for line in data:gmatch("[^\r\n]+") do
|
|
||||||
local time = time_to_secs(line)
|
|
||||||
if time and (time < video_length) then
|
|
||||||
table.insert(ret, { time = time, title = line })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(ret, function(a, b)
|
|
||||||
return a.time < b.time
|
|
||||||
end)
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
local function chapters()
|
|
||||||
if json.chapters then
|
|
||||||
for i = 1, #json.chapters do
|
|
||||||
local chapter = json.chapters[i]
|
|
||||||
local title = chapter.title or ""
|
|
||||||
if title == "" then
|
|
||||||
title = string.format("Chapter %02d", i)
|
|
||||||
end
|
|
||||||
table.insert(chapter_list, { time = chapter.start_time, title = title })
|
|
||||||
end
|
|
||||||
elseif not (json.description == nil) and not (json.duration == nil) then
|
|
||||||
chapter_list = extract_chapters(json.description, json.duration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--end ytdl_hook
|
|
||||||
local title = ""
|
|
||||||
local fVideo = ""
|
|
||||||
local fAudio = ""
|
|
||||||
local function load_files(dtitle, destination, audio, wait)
|
|
||||||
if wait then
|
|
||||||
if exists(destination .. ".mka") then
|
|
||||||
print("---wait success: found mka---")
|
|
||||||
audio = "audio-file=" .. destination .. ".mka,"
|
|
||||||
else
|
|
||||||
print("---could not find mka after wait, audio may be missing---")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- if audio ~= "" then
|
|
||||||
-- table.insert(filesToDelete, destination .. ".mka")
|
|
||||||
-- end
|
|
||||||
-- table.insert(filesToDelete, destination .. ".mkv")
|
|
||||||
dtitle = dtitle:gsub("-" .. ("[%w_-]"):rep(11) .. "$", "")
|
|
||||||
dtitle = dtitle:gsub("^" .. ("%d"):rep(10) .. "%-", "")
|
|
||||||
if useNewLoadfile() then
|
|
||||||
mp.commandv(
|
|
||||||
"loadfile",
|
|
||||||
destination .. ".mkv",
|
|
||||||
"append",
|
|
||||||
-1,
|
|
||||||
audio .. 'force-media-title="' .. dtitle .. '",demuxer-max-back-bytes=1MiB,demuxer-max-bytes=3MiB,ytdl=no'
|
|
||||||
)
|
|
||||||
else
|
|
||||||
mp.commandv(
|
|
||||||
"loadfile",
|
|
||||||
destination .. ".mkv",
|
|
||||||
"append",
|
|
||||||
audio .. 'force-media-title="' .. dtitle .. '",demuxer-max-back-bytes=1MiB,demuxer-max-bytes=3MiB,ytdl=no'
|
|
||||||
) --,sub-file="..destination..".en.vtt") --in case they are not set up to autoload
|
|
||||||
end
|
|
||||||
mp.commandv("playlist_move", mp.get_property("playlist-count") - 1, nextIndex)
|
|
||||||
mp.commandv("playlist_remove", nextIndex + 1)
|
|
||||||
caught = true
|
|
||||||
title = ""
|
|
||||||
-- pop = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local listenID = ""
|
|
||||||
local function listener(event)
|
|
||||||
if not caught and event.prefix == mp.get_script_name() and string.find(event.text, listenID) then
|
|
||||||
local destination = string.match(event.text, "%[download%] Destination: (.+).mkv")
|
|
||||||
or string.match(event.text, "%[download%] (.+).mkv has already been downloaded")
|
|
||||||
-- if destination then print("---"..cachePath) end;
|
|
||||||
if destination and string.find(destination, string.gsub(cachePath, "~/", "")) then
|
|
||||||
-- print(listenID)
|
|
||||||
mp.unregister_event(listener)
|
|
||||||
_, title = utils.split_path(destination)
|
|
||||||
local audio = ""
|
|
||||||
if fAudio == "" then
|
|
||||||
load_files(title, destination, audio, false)
|
|
||||||
else
|
|
||||||
if exists(destination .. ".mka") then
|
|
||||||
audio = "audio-file=" .. destination .. ".mka,"
|
|
||||||
load_files(title, destination, audio, false)
|
|
||||||
else
|
|
||||||
print("---expected mka but could not find it, waiting for 2 seconds---")
|
|
||||||
mp.add_timeout(2, function()
|
|
||||||
load_files(title, destination, audio, true)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--from ytdl_hook
|
|
||||||
mp.add_hook("on_preloaded", 10, function()
|
|
||||||
if string.find(mp.get_property("path"), cachePath) then
|
|
||||||
chapters()
|
|
||||||
if next(chapter_list) ~= nil then
|
|
||||||
mp.set_property_native("chapter-list", chapter_list)
|
|
||||||
chapter_list = {}
|
|
||||||
json = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
--end ytdl_hook
|
|
||||||
function dump(o)
|
|
||||||
if type(o) == "table" then
|
|
||||||
local s = "{ "
|
|
||||||
for k, v in pairs(o) do
|
|
||||||
if type(k) ~= "number" then
|
|
||||||
k = '"' .. k .. '"'
|
|
||||||
end
|
|
||||||
s = s .. "[" .. k .. "] = " .. dump(v) .. ","
|
|
||||||
end
|
|
||||||
return s .. "} "
|
|
||||||
else
|
|
||||||
return tostring(o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function addOPTS(old)
|
|
||||||
for k, v in pairs(additionalOpts) do
|
|
||||||
-- print(k)
|
|
||||||
if string.find(v, "%s") then
|
|
||||||
for l, w in string.gmatch(v, "([-%w]+) (.+)") do
|
|
||||||
table.insert(old, l)
|
|
||||||
table.insert(old, w)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(old, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- print(dump(old))
|
|
||||||
return old
|
|
||||||
end
|
|
||||||
|
|
||||||
local AudioDownloadHandle = {}
|
|
||||||
local VideoDownloadHandle = {}
|
|
||||||
local JsonDownloadHandle = {}
|
|
||||||
local function download_files(id, success, result, error)
|
|
||||||
if result.killed_by_us then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local jfile = cachePath .. "/" .. id .. ".json"
|
|
||||||
|
|
||||||
local jfileIO = io.open(jfile, "w")
|
|
||||||
jfileIO:write(result.stdout)
|
|
||||||
jfileIO:close()
|
|
||||||
json = utils.parse_json(result.stdout)
|
|
||||||
-- print(dump(json))
|
|
||||||
if json.requested_downloads[1].requested_formats ~= nil then
|
|
||||||
local args = {
|
|
||||||
ytdl,
|
|
||||||
"--no-continue",
|
|
||||||
"-q",
|
|
||||||
"-f",
|
|
||||||
fAudio,
|
|
||||||
"--restrict-filenames",
|
|
||||||
"--no-playlist",
|
|
||||||
"--no-part",
|
|
||||||
"-o",
|
|
||||||
cachePath .. "/" .. id .. "-%(title)s-%(id)s.mka",
|
|
||||||
"--load-info-json",
|
|
||||||
jfile,
|
|
||||||
}
|
|
||||||
args = addOPTS(args)
|
|
||||||
AudioDownloadHandle = mp.command_native_async({
|
|
||||||
name = "subprocess",
|
|
||||||
args = args,
|
|
||||||
playback_only = false,
|
|
||||||
}, function() end)
|
|
||||||
else
|
|
||||||
fAudio = ""
|
|
||||||
fVideo = fVideo:gsub("bestvideo", "best")
|
|
||||||
fVideo = fVideo:gsub("bv", "best")
|
|
||||||
end
|
|
||||||
|
|
||||||
local args = {
|
|
||||||
ytdl,
|
|
||||||
"--no-continue",
|
|
||||||
"-f",
|
|
||||||
fVideo .. "/best",
|
|
||||||
"--restrict-filenames",
|
|
||||||
"--no-playlist",
|
|
||||||
"--no-part",
|
|
||||||
"-o",
|
|
||||||
cachePath .. "/" .. id .. "-%(title)s-%(id)s.mkv",
|
|
||||||
"--load-info-json",
|
|
||||||
jfile,
|
|
||||||
}
|
|
||||||
args = addOPTS(args)
|
|
||||||
VideoDownloadHandle = mp.command_native_async({
|
|
||||||
name = "subprocess",
|
|
||||||
args = args,
|
|
||||||
playback_only = false,
|
|
||||||
}, function() end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function DL()
|
|
||||||
local index = tonumber(mp.get_property("playlist-pos"))
|
|
||||||
if
|
|
||||||
mp.get_property("playlist/" .. index .. "/filename"):find("/videos$")
|
|
||||||
and mp.get_property("playlist/" .. index + 1 .. "/filename"):find("/shorts$")
|
|
||||||
then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if
|
|
||||||
tonumber(mp.get_property("playlist-pos-1")) > 0
|
|
||||||
and mp.get_property("playlist-pos-1") ~= mp.get_property("playlist-count")
|
|
||||||
then
|
|
||||||
nextIndex = index + 1
|
|
||||||
local nextFile = mp.get_property("playlist/" .. nextIndex .. "/filename")
|
|
||||||
if nextFile and caught and nextFile:find("://", 0, false) then
|
|
||||||
caught = false
|
|
||||||
mp.enable_messages("info")
|
|
||||||
mp.register_event("log-message", listener)
|
|
||||||
local ytFormat = mp.get_property("ytdl-format")
|
|
||||||
fVideo = string.match(ytFormat, "(.+)%+.+//?") or "bestvideo"
|
|
||||||
fAudio = string.match(ytFormat, ".+%+(.+)//?") or "bestaudio"
|
|
||||||
-- print("start"..nextFile)
|
|
||||||
listenID = tostring(os.time())
|
|
||||||
local args = {
|
|
||||||
ytdl,
|
|
||||||
"--dump-single-json",
|
|
||||||
"--no-simulate",
|
|
||||||
"--skip-download",
|
|
||||||
"--restrict-filenames",
|
|
||||||
"--no-playlist",
|
|
||||||
"--sub-lang",
|
|
||||||
"en",
|
|
||||||
"--write-sub",
|
|
||||||
"--no-part",
|
|
||||||
"-o",
|
|
||||||
cachePath .. "/" .. listenID .. "-%(title)s-%(id)s.%(ext)s",
|
|
||||||
nextFile,
|
|
||||||
}
|
|
||||||
args = addOPTS(args)
|
|
||||||
-- print(dump(args))
|
|
||||||
table.insert(filesToDelete, listenID)
|
|
||||||
JsonDownloadHandle = mp.command_native_async({
|
|
||||||
name = "subprocess",
|
|
||||||
args = args,
|
|
||||||
capture_stdout = true,
|
|
||||||
capture_stderr = true,
|
|
||||||
playback_only = false,
|
|
||||||
}, function(...)
|
|
||||||
download_files(listenID, ...)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function clearCache()
|
|
||||||
-- print(pop)
|
|
||||||
|
|
||||||
--if pop == true then
|
|
||||||
mp.abort_async_command(AudioDownloadHandle)
|
|
||||||
mp.abort_async_command(VideoDownloadHandle)
|
|
||||||
mp.abort_async_command(JsonDownloadHandle)
|
|
||||||
-- for k, v in pairs(filesToDelete) do
|
|
||||||
-- print("remove: " .. v)
|
|
||||||
-- os.remove(v)
|
|
||||||
-- end
|
|
||||||
local ftd = io.open(cachePath .. "/temp.files", "a")
|
|
||||||
for k, v in pairs(filesToDelete) do
|
|
||||||
ftd:write(v .. "\n")
|
|
||||||
if package.config:sub(1, 1) ~= "/" then
|
|
||||||
os.execute('del /Q /F "' .. cachePath .. "\\" .. v .. '*"')
|
|
||||||
else
|
|
||||||
os.execute("rm -f " .. cachePath .. "/" .. v .. "*")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ftd:close()
|
|
||||||
print("clear")
|
|
||||||
mp.command("quit")
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
mp.add_hook("on_unload", 50, function()
|
|
||||||
-- mp.abort_async_command(AudioDownloadHandle)
|
|
||||||
-- mp.abort_async_command(VideoDownloadHandle)
|
|
||||||
mp.abort_async_command(JsonDownloadHandle)
|
|
||||||
mp.unregister_event(listener)
|
|
||||||
caught = true
|
|
||||||
listenID = "resetYtdlPreloadListener"
|
|
||||||
-- print(listenID)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local skipInitial
|
|
||||||
mp.observe_property("playlist-count", "number", function()
|
|
||||||
if skipInitial then
|
|
||||||
DL()
|
|
||||||
else
|
|
||||||
skipInitial = true
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--from ytdl_hook
|
|
||||||
local platform_is_windows = (package.config:sub(1, 1) == "\\")
|
|
||||||
local o = {
|
|
||||||
exclude = "",
|
|
||||||
try_ytdl_first = false,
|
|
||||||
use_manifests = false,
|
|
||||||
all_formats = false,
|
|
||||||
force_all_formats = true,
|
|
||||||
ytdl_path = "",
|
|
||||||
}
|
|
||||||
local paths_to_search = { "yt-dlp", "yt-dlp_x86", "youtube-dl" }
|
|
||||||
--local options = require 'mp.options'
|
|
||||||
options.read_options(o, "ytdl_hook")
|
|
||||||
|
|
||||||
local separator = platform_is_windows and ";" or ":"
|
|
||||||
if o.ytdl_path:match("[^" .. separator .. "]") then
|
|
||||||
paths_to_search = {}
|
|
||||||
for path in o.ytdl_path:gmatch("[^" .. separator .. "]+") do
|
|
||||||
table.insert(paths_to_search, path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function exec(args)
|
|
||||||
local ret = mp.command_native({
|
|
||||||
name = "subprocess",
|
|
||||||
args = args,
|
|
||||||
capture_stdout = true,
|
|
||||||
capture_stderr = true,
|
|
||||||
})
|
|
||||||
return ret.status, ret.stdout, ret, ret.killed_by_us
|
|
||||||
end
|
|
||||||
|
|
||||||
local msg = require("mp.msg")
|
|
||||||
local command = {}
|
|
||||||
for _, path in pairs(paths_to_search) do
|
|
||||||
-- search for youtube-dl in mpv's config dir
|
|
||||||
local exesuf = platform_is_windows and ".exe" or ""
|
|
||||||
local ytdl_cmd = mp.find_config_file(path .. exesuf)
|
|
||||||
if ytdl_cmd then
|
|
||||||
msg.verbose("Found youtube-dl at: " .. ytdl_cmd)
|
|
||||||
ytdl = ytdl_cmd
|
|
||||||
break
|
|
||||||
else
|
|
||||||
msg.verbose("No youtube-dl found with path " .. path .. exesuf .. " in config directories")
|
|
||||||
--search in PATH
|
|
||||||
command[1] = path
|
|
||||||
es, json, result, aborted = exec(command)
|
|
||||||
if result.error_string == "init" then
|
|
||||||
msg.verbose("youtube-dl with path " .. path .. exesuf .. " not found in PATH or not enough permissions")
|
|
||||||
else
|
|
||||||
msg.verbose("Found youtube-dl with path " .. path .. exesuf .. " in PATH")
|
|
||||||
ytdl = path
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--end ytdl_hook
|
|
||||||
|
|
||||||
mp.register_event("start-file", DL)
|
|
||||||
mp.register_event("shutdown", clearCache)
|
|
||||||
local ftd = io.open(cachePath .. "/temp.files", "r")
|
|
||||||
while ftd ~= nil do
|
|
||||||
local line = ftd:read()
|
|
||||||
if line == nil or line == "" then
|
|
||||||
ftd:close()
|
|
||||||
io.open(cachePath .. "/temp.files", "w"):close()
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- print("DEL::"..line)
|
|
||||||
if package.config:sub(1, 1) ~= "/" then
|
|
||||||
os.execute('del /Q /F "' .. cachePath .. "\\" .. line .. '*" >nul 2>nul')
|
|
||||||
else
|
|
||||||
os.execute("rm -f " .. cachePath .. "/" .. line .. "* &> /dev/null")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user