mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-11 13:35:05 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7075f6291d | ||
|
|
170d769476 | ||
|
|
1ff3457744 | ||
|
|
dc352a2202 | ||
|
|
c8750e42d1 | ||
|
|
20448bfeb2 | ||
|
|
f08fd658e6 | ||
|
|
f6e9d55838 | ||
|
|
07cc78c2f1 | ||
|
|
affa34848c | ||
|
|
45ee03aecc | ||
|
|
c6e27ca809 |
39
.github/workflows/release.yml
vendored
39
.github/workflows/release.yml
vendored
@@ -26,7 +26,20 @@ jobs:
|
||||
# Extract version from tag, strip 'v' prefix and everything after first dash
|
||||
VERSION=${GITHUB_REF/refs\/tags\/v/}
|
||||
VERSION=${VERSION%%-*}
|
||||
echo ::set-output name=VERSION::$VERSION
|
||||
# Save display version for filenames (e.g., 0.96.1)
|
||||
echo ::set-output name=DISPLAY_VERSION::$VERSION
|
||||
# Count dots to determine version format
|
||||
DOTS="${VERSION//[^.]}"
|
||||
PART_COUNT=$((${#DOTS} + 1))
|
||||
# MSI requires 4-part version (major.minor.build.revision)
|
||||
if [ "$PART_COUNT" -eq 2 ]; then
|
||||
MSI_VERSION="${VERSION}.0.0"
|
||||
elif [ "$PART_COUNT" -eq 3 ]; then
|
||||
MSI_VERSION="${VERSION}.0"
|
||||
else
|
||||
MSI_VERSION="${VERSION}"
|
||||
fi
|
||||
echo ::set-output name=VERSION::$MSI_VERSION
|
||||
shell: bash
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2.0.0
|
||||
@@ -68,6 +81,14 @@ jobs:
|
||||
- name: Copy files to directory for installer
|
||||
run: mkdir installer; cp ./x64/Release-Full/ccextractorwinfull.exe ./installer; cp ./x64/Release-Full/*.dll ./installer
|
||||
working-directory: ./windows
|
||||
- name: Download tessdata for OCR support
|
||||
run: |
|
||||
mkdir -p ./installer/tessdata
|
||||
# Download English traineddata from tessdata_fast (smaller, faster, good for most use cases)
|
||||
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata" -OutFile "./installer/tessdata/eng.traineddata"
|
||||
# Download OSD (Orientation and Script Detection) for automatic script detection
|
||||
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/osd.traineddata" -OutFile "./installer/tessdata/osd.traineddata"
|
||||
working-directory: ./windows
|
||||
- name: install WiX
|
||||
run: dotnet tool uninstall --global wix; dotnet tool install --global wix --version 6.0.2 && wix extension add -g WixToolset.UI.wixext/6.0.2
|
||||
- name: Make sure WiX works
|
||||
@@ -85,15 +106,15 @@ jobs:
|
||||
run: Get-ChildItem -Recurse ./installer
|
||||
working-directory: ./windows
|
||||
- name: Create portable zip
|
||||
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor_win_portable.zip
|
||||
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip
|
||||
working-directory: ./windows
|
||||
- name: Build installer
|
||||
run: wix build -ext WixToolset.UI.wixext -d "AppVersion=${{ steps.get_version.outputs.VERSION }}.0.0" -o CCExtractor.msi installer.wxs CustomUI.wxs
|
||||
run: wix build -ext WixToolset.UI.wixext -d "AppVersion=${{ steps.get_version.outputs.VERSION }}" -o CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi installer.wxs CustomUI.wxs
|
||||
working-directory: ./windows
|
||||
- name: Upload as asset
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: './windows/CCExtractor.msi;./windows/CCExtractor_win_portable.zip'
|
||||
files: './windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi;./windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
create_linux_package:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -101,10 +122,16 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: ./ccextractor
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF/refs\/tags\/v/}
|
||||
VERSION=${VERSION%%-*}
|
||||
echo ::set-output name=DISPLAY_VERSION::$VERSION
|
||||
- name: Create .tar.gz without git and windows folders
|
||||
run: tar -pczf ./ccextractor_minimal.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
|
||||
run: tar -pczf ./ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
|
||||
- name: Upload as asset
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: './ccextractor_minimal.tar.gz'
|
||||
files: './ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
0.96.2 (2025-12-26)
|
||||
-------------------
|
||||
- Rebundle Windows version to include required runtime files to process hardcoded subtitles
|
||||
(hardcodex mode).
|
||||
- New: Add optional -system-libs flag to Linux build script for package manager compatibility
|
||||
|
||||
0.96.1 (2025-12-25)
|
||||
-------------------
|
||||
- Rebundle Windows version to include an updated GUI. No changes in CCExtractor itself.
|
||||
|
||||
0.96 (2025-12-23)
|
||||
-----------------
|
||||
- New: Multi-page teletext extraction support (#665)
|
||||
|
||||
66
linux/build
66
linux/build
@@ -2,6 +2,7 @@
|
||||
|
||||
RUST_LIB="rust/release/libccx_rust.a"
|
||||
RUST_PROFILE="--release"
|
||||
USE_SYSTEM_LIBS=false
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-debug)
|
||||
@@ -23,6 +24,10 @@ while [[ $# -gt 0 ]]; do
|
||||
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lxcb-shm -lxcb -lX11 -llzma -lswresample"
|
||||
shift
|
||||
;;
|
||||
-system-libs)
|
||||
USE_SYSTEM_LIBS=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
@@ -30,7 +35,49 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
if [ "$USE_SYSTEM_LIBS" = true ]; then
|
||||
command -v pkg-config >/dev/null || {
|
||||
echo "Error: pkg-config is required for -system-libs mode"
|
||||
exit 1
|
||||
}
|
||||
|
||||
MISSING=""
|
||||
for lib in libpng zlib freetype2 libutf8proc; do
|
||||
if ! pkg-config --exists "$lib" 2>/dev/null; then
|
||||
MISSING="$MISSING $lib"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$MISSING" ]; then
|
||||
echo "Error: Missing required system libraries:$MISSING"
|
||||
echo ""
|
||||
echo "On Debian/Ubuntu: sudo apt install libpng-dev zlib1g-dev libfreetype-dev libutf8proc-dev"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for hdr in leptonica/allheaders.h tesseract/capi.h; do
|
||||
if ! echo "#include <$hdr>" | gcc -E - >/dev/null 2>&1; then
|
||||
echo "Error: Missing headers for <$hdr>"
|
||||
echo "On Debian/Ubuntu: sudo apt install libleptonica-dev libtesseract-dev"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
PKG_CFLAGS="$(pkg-config --cflags libpng zlib freetype2 libutf8proc)"
|
||||
PKG_LIBS="$(pkg-config --libs libpng zlib freetype2 libutf8proc)"
|
||||
|
||||
UTF8PROC_COMPAT=""
|
||||
if [ ! -d /usr/include/utf8proc ] && [ -f /usr/include/utf8proc.h ]; then
|
||||
mkdir -p ./utf8proc_compat/utf8proc
|
||||
ln -sf /usr/include/utf8proc.h ./utf8proc_compat/utf8proc/utf8proc.h
|
||||
UTF8PROC_COMPAT="-I./utf8proc_compat"
|
||||
fi
|
||||
fi
|
||||
|
||||
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
if [ "$USE_SYSTEM_LIBS" != true ]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DFT2_BUILD_LIBRARY"
|
||||
fi
|
||||
bit_os=$(getconf LONG_BIT)
|
||||
if [ "$bit_os" == "64" ]
|
||||
then
|
||||
@@ -87,6 +134,19 @@ SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_UTF8PROC $SRC_FREETYPE"
|
||||
BLD_LINKER="$BLD_LINKER -lm -zmuldefs -l tesseract -l leptonica -lpthread -ldl -lgpac"
|
||||
|
||||
if [ "$USE_SYSTEM_LIBS" = true ]; then
|
||||
LEPTONICA_CFLAGS="$(pkg-config --cflags --silence-errors lept)"
|
||||
TESSERACT_CFLAGS="$(pkg-config --cflags --silence-errors tesseract)"
|
||||
GPAC_CFLAGS="$(pkg-config --cflags --silence-errors gpac)"
|
||||
|
||||
BLD_INCLUDE="-I../src -I../src/lib_ccx -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash \
|
||||
$UTF8PROC_COMPAT $PKG_CFLAGS $LEPTONICA_CFLAGS $TESSERACT_CFLAGS $GPAC_CFLAGS"
|
||||
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_HASH"
|
||||
BLD_LINKER="$PKG_LIBS -ltesseract -lleptonica -lgpac -lpthread -ldl -lm"
|
||||
fi
|
||||
|
||||
|
||||
echo "Running pre-build script..."
|
||||
./pre-build.sh
|
||||
echo "Trying to compile..."
|
||||
@@ -149,3 +209,7 @@ if [[ "$out" != "" ]] ; then
|
||||
else
|
||||
echo "Compilation successful, no compiler messages."
|
||||
fi
|
||||
|
||||
if [ -d ./utf8proc_compat ]; then
|
||||
rm -rf ./utf8proc_compat
|
||||
fi
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
#include <dirent.h>
|
||||
#include "ccx_encoders_helpers.h"
|
||||
#include "ccx_encoders_spupng.h"
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
#include "ocr.h"
|
||||
|
||||
struct ocrCtx
|
||||
@@ -100,6 +105,68 @@ void delete_ocr(void **arg)
|
||||
freep(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_executable_directory
|
||||
*
|
||||
* Returns the directory containing the executable.
|
||||
* Returns a pointer to a static buffer, or NULL on failure.
|
||||
*/
|
||||
static const char *get_executable_directory(void)
|
||||
{
|
||||
static char exe_dir[1024] = {0};
|
||||
static int initialized = 0;
|
||||
|
||||
if (initialized)
|
||||
return exe_dir[0] ? exe_dir : NULL;
|
||||
|
||||
initialized = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
char exe_path[MAX_PATH];
|
||||
DWORD len = GetModuleFileNameA(NULL, exe_path, MAX_PATH);
|
||||
if (len == 0 || len >= MAX_PATH)
|
||||
return NULL;
|
||||
|
||||
// Find the last backslash and truncate there
|
||||
char *last_sep = strrchr(exe_path, '\\');
|
||||
if (last_sep)
|
||||
{
|
||||
*last_sep = '\0';
|
||||
strncpy(exe_dir, exe_path, sizeof(exe_dir) - 1);
|
||||
exe_dir[sizeof(exe_dir) - 1] = '\0';
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
char exe_path[1024];
|
||||
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
exe_path[len] = '\0';
|
||||
|
||||
char *last_sep = strrchr(exe_path, '/');
|
||||
if (last_sep)
|
||||
{
|
||||
*last_sep = '\0';
|
||||
strncpy(exe_dir, exe_path, sizeof(exe_dir) - 1);
|
||||
exe_dir[sizeof(exe_dir) - 1] = '\0';
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
char exe_path[1024];
|
||||
uint32_t size = sizeof(exe_path);
|
||||
if (_NSGetExecutablePath(exe_path, &size) != 0)
|
||||
return NULL;
|
||||
|
||||
char *last_sep = strrchr(exe_path, '/');
|
||||
if (last_sep)
|
||||
{
|
||||
*last_sep = '\0';
|
||||
strncpy(exe_dir, exe_path, sizeof(exe_dir) - 1);
|
||||
exe_dir[sizeof(exe_dir) - 1] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
return exe_dir[0] ? exe_dir : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* probe_tessdata_location
|
||||
*
|
||||
@@ -107,8 +174,10 @@ void delete_ocr(void **arg)
|
||||
*
|
||||
* Priority of Tesseract traineddata file search paths:-
|
||||
* 1. tessdata in TESSDATA_PREFIX, if it is specified. Overrides others
|
||||
* 2. tessdata in current working directory
|
||||
* 3. tessdata in /usr/share
|
||||
* 2. tessdata in executable directory (for bundled tessdata)
|
||||
* 3. tessdata in current working directory
|
||||
* 4. tessdata in system locations (/usr/share, etc.)
|
||||
* 5. tessdata in default Tesseract install location (Windows)
|
||||
*/
|
||||
char *probe_tessdata_location(const char *lang)
|
||||
{
|
||||
@@ -116,6 +185,7 @@ char *probe_tessdata_location(const char *lang)
|
||||
|
||||
const char *paths[] = {
|
||||
getenv("TESSDATA_PREFIX"),
|
||||
get_executable_directory(),
|
||||
"./",
|
||||
"/usr/share/",
|
||||
"/usr/local/share/",
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
<MediaTemplate EmbedCab="yes"/>
|
||||
<Feature Id="CCX" Title="CCExtractor Setup" Level="1">
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder"/>
|
||||
<ComponentGroupRef Id="CCX_Components_tessdata"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_assets"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_fonts"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_shaders"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_cupertino"/>
|
||||
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_window_manager"/>
|
||||
<ComponentRef Id="ApplicationShortcutDesktop"/>
|
||||
</Feature>
|
||||
<Icon Id="ccxgui.exe" SourceFile="./installer/ccxgui.exe"/>
|
||||
@@ -38,14 +41,19 @@
|
||||
</StandardDirectory>
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="CCExtractor">
|
||||
<Directory Id="CCX_tessdata" Name="tessdata"/>
|
||||
<Directory Id="CCX_data" Name="data">
|
||||
<Directory Id="CCX_data_flutter_assets" Name="flutter_assets">
|
||||
<Directory Id="CCX_data_flutter_assets_assets" Name="assets"/>
|
||||
<Directory Id="CCX_data_flutter_assets_fonts" Name="fonts"/>
|
||||
<Directory Id="CCX_data_flutter_assets_shaders" Name="shaders"/>
|
||||
<Directory Id="dirEE44DD2D485FE70BEAFB55755745AB6E" Name="packages">
|
||||
<Directory Id="dir382F01892F72FB688A688BE6E01B93A3" Name="cupertino_icons">
|
||||
<Directory Id="CCX_data_flutter_assets_cupertino" Name="assets"/>
|
||||
</Directory>
|
||||
<Directory Id="dirWindowManager" Name="window_manager">
|
||||
<Directory Id="dirWindowManagerImages" Name="images"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
@@ -113,7 +121,10 @@
|
||||
<File Source="./installer/desktop_drop_plugin.dll" KeyPath="yes"/>
|
||||
</Component>
|
||||
<Component Guid="{BE7FE765-EBA8-4FAB-8864-8561C50D39CF}">
|
||||
<File Source="./installer/window_size_plugin.dll" KeyPath="yes"/>
|
||||
<File Source="./installer/screen_retriever_plugin.dll" KeyPath="yes"/>
|
||||
</Component>
|
||||
<Component Guid="{29012345-6789-0123-2345-789012345678}">
|
||||
<File Source="./installer/window_manager_plugin.dll" KeyPath="yes"/>
|
||||
</Component>
|
||||
<!-- VC++ Runtime -->
|
||||
<Component Guid="{32F0A64B-0C07-4807-A48C-714B2533A03C}">
|
||||
@@ -135,6 +146,9 @@
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets" Directory="CCX_data_flutter_assets">
|
||||
<Component Id="cmpAssetManifestBin" Guid="{3A012345-6789-0123-3456-890123456789}">
|
||||
<File Id="filAssetManifestBin" KeyPath="yes" Source="./installer/data/flutter_assets/AssetManifest.bin"/>
|
||||
</Component>
|
||||
<Component Id="cmp1C63471C238EEA92D0AE37BC7DF9E605" Guid="{DEAF277D-3D05-4B37-A732-9514B503B74A}">
|
||||
<File Id="fil3F43BEEF0A85EE6619E4674CA43CB616" KeyPath="yes" Source="./installer/data/flutter_assets/AssetManifest.json"/>
|
||||
</Component>
|
||||
@@ -163,6 +177,34 @@
|
||||
<File Id="fil341334402AF57DB92DF3F7C92E983317" KeyPath="yes" Source="./installer/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets_shaders" Directory="CCX_data_flutter_assets_shaders">
|
||||
<Component Id="cmpInkSparkleFrag" Guid="{4B012345-6789-0123-4567-901234567890}">
|
||||
<File Id="filInkSparkleFrag" KeyPath="yes" Source="./installer/data/flutter_assets/shaders/ink_sparkle.frag"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets_window_manager" Directory="dirWindowManagerImages">
|
||||
<Component Id="cmpWmClose" Guid="{5C012345-6789-0123-5678-012345678901}">
|
||||
<File Id="filWmClose" KeyPath="yes" Source="./installer/data/flutter_assets/packages/window_manager/images/ic_chrome_close.png"/>
|
||||
</Component>
|
||||
<Component Id="cmpWmMaximize" Guid="{6D012345-6789-0123-6789-123456789012}">
|
||||
<File Id="filWmMaximize" KeyPath="yes" Source="./installer/data/flutter_assets/packages/window_manager/images/ic_chrome_maximize.png"/>
|
||||
</Component>
|
||||
<Component Id="cmpWmMinimize" Guid="{7E012345-6789-0123-7890-234567890123}">
|
||||
<File Id="filWmMinimize" KeyPath="yes" Source="./installer/data/flutter_assets/packages/window_manager/images/ic_chrome_minimize.png"/>
|
||||
</Component>
|
||||
<Component Id="cmpWmUnmaximize" Guid="{8F012345-6789-0123-8901-345678901234}">
|
||||
<File Id="filWmUnmaximize" KeyPath="yes" Source="./installer/data/flutter_assets/packages/window_manager/images/ic_chrome_unmaximize.png"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<!-- Tesseract OCR data files for HardSubx feature -->
|
||||
<ComponentGroup Id="CCX_Components_tessdata" Directory="CCX_tessdata">
|
||||
<Component Id="cmpTessdataEng" Guid="{A1234567-8901-2345-6789-0123456789AB}">
|
||||
<File Id="filEngTraineddata" KeyPath="yes" Source="./installer/tessdata/eng.traineddata"/>
|
||||
</Component>
|
||||
<Component Id="cmpTessdataOsd" Guid="{B2345678-9012-3456-7890-123456789ABC}">
|
||||
<File Id="filOsdTraineddata" KeyPath="yes" Source="./installer/tessdata/osd.traineddata"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user