diff --git a/.bash_aliases b/.bash_aliases index 302e6b8..9f68795 100644 --- a/.bash_aliases +++ b/.bash_aliases @@ -127,4 +127,5 @@ alias hypr='cd ~/.config/hypr && vim ~/.config/hypr/hyprland.conf && cd -' alias wlc='wl-copy' alias wlp='wl-paste' alias vn32='WINEPREFIX=/home/sudacode/S/lutris/wineprefix32 WINEARCH=win32' -alias impv='mpv --profile=immersion' +alias impv='mpv --profile=subminer' +alias smpv='mpv --profile=subminer' diff --git a/.claude/settings.json##os.Linux b/.claude/settings.json##os.Linux index 2b8d257..ad747a9 100644 --- a/.claude/settings.json##os.Linux +++ b/.claude/settings.json##os.Linux @@ -39,19 +39,23 @@ "pyright-lsp@claude-plugins-official": true, "typescript-lsp@claude-plugins-official": true, "clangd-lsp@claude-plugins-official": true, - "claude-mem@thedotmack": true, "frontend-design@claude-plugins-official": true, "code-review@claude-plugins-official": true, "code-simplifier@claude-plugins-official": true, - "playwright@claude-plugins-official": true + "playwright@claude-plugins-official": true, + "superpowers@claude-plugins-official": true }, "sandbox": { "enabled": false, "autoAllowBashIfSandboxed": true, "network": { - "allowUnixSockets": ["/var/run/docker.sock"], + "allowUnixSockets": [ + "/var/run/docker.sock" + ], "allowLocalBinding": true }, - "excludedCommands": ["docker"] + "excludedCommands": [ + "docker" + ] } } diff --git a/.config/SubMiner/config.jsonc##os.Linux b/.config/SubMiner/config.jsonc##os.Linux new file mode 100644 index 0000000..316c965 --- /dev/null +++ b/.config/SubMiner/config.jsonc##os.Linux @@ -0,0 +1,131 @@ +{ + "keybindings": [], + "shortcuts": { + "copySubtitle": "CommandOrControl+C", + "copySubtitleMultiple": "CommandOrControl+Shift+C", + "updateLastCardFromClipboard": "CommandOrControl+V", + "triggerFieldGrouping": "CommandOrControl+G", + "triggerSubsync": "CommandOrControl+Alt+S", + "mineSentence": "CommandOrControl+S", + "mineSentenceMultiple": "CommandOrControl+Shift+S", + "multiCopyTimeoutMs": 3000, + "toggleSecondarySub": "CommandOrControl+Shift+V", + "markAudioCard": "CommandOrControl+Shift+A", + "openRuntimeOptions": "CommandOrControl+Shift+O", + "toggleVisibleOverlayGlobal": "Alt+Shift+O", + "toggleInvisibleOverlayGlobal": "Alt+Shift+I", + }, + "auto_start_overlay": false, + "bind_visible_overlay_to_mpv_sub_visibility": false, + "texthooker": { + "openBrowser": false, + }, + "websocket": { + "enabled": "auto", + "port": 6677, + }, + "ankiConnect": { + "enabled": true, + "url": "http://127.0.0.1:8765", + "deck": "Minecraft", + "pollingRate": 500, + "fields": { + "audio": "ExpressionAudio", + "image": "Picture", + "sentence": "Sentence", + "miscInfo": "MiscInfo", + "translation": "SelectionText", + }, + "openRouter": { + "enabled": true, + "alwaysUseAiTranslation": true, + "apiKey": "", + "model": "openai/gpt-oss-120b:free", + "baseUrl": "https://openrouter.ai/api/v1", + "sourceLanguage": "Japanese", + "systemPrompt": "You are a translation engine for translating Japanese into natural-sounding, context-aware English. Return only the translated text with no extra explanations or commentary. The translation must preserve the original tone and intent of the source. If the input is not in the target language, translate it to the target language. If the input is already in the target language, return it as is.", + }, + "media": { + "generateAudio": true, + "generateImage": true, + "imageType": "avif", + "imageFormat": "webp", + "animatedFps": 24, + "animatedMaxWidth": 640, + "animatedMaxHeight": null, + "animatedCrf": 35, + "audioPadding": 0.5, + "fallbackDuration": 3, + }, + "behavior": { + "overwriteAudio": false, + "overwriteImage": true, + "mediaInsertMode": "append", + "highlightWord": true, + "notificationType": "system", + "showNotificationOnUpdate": true, + "autoUpdateNewCards": false, + }, + "nPlusOne": { + "decks": ["Minecraft", "Kaishi 1.5k"], + "highlightEnabled": true, + "refreshMinutes": 60, + "matchMode": "headword", + }, + "metadata": { + "pattern": "[SubMiner] %f (%t)", + }, + "isLapis": { + "enabled": true, + "sentenceCardModel": "Lapis Morph", + "sentenceCardSentenceField": "Sentence", + "sentenceCardAudioField": "SentenceAudio", + }, + "isKiku": { + "enabled": true, + "fieldGrouping": "manual", + "deleteDuplicateInAuto": true, + }, + }, + "secondarySub": { + "autoLoadSecondarySub": true, + "secondarySubLanguages": ["en", "eng"], + }, + "subsync": { + "defaultMode": "manual", + "alass_path": null, + "ffsubsync_path": null, + "ffmpeg_path": null, + }, + "subtitleStyle": { + "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif", + "fontSize": 35, + "fontColor": "#cad3f5", + "fontWeight": "normal", + "fontStyle": "normal", + "backgroundColor": "rgb(30, 32, 48, 0.88)", + "secondary": { + "fontSize": 24, + "fontColor": "#cad3f5", + "backgroundColor": "transparent", + }, + }, + "jimaku": { + // "apiKey": "YOUR_API_KEY", + // or use a command that outputs the key: + "apiKeyCommand": "cat ~/.jimaku-api-key", + "apiBaseUrl": "https://jimaku.cc", + "languagePreference": "ja", + "maxEntryResults": 10, + }, + "invisibleOverlay": { + // "platform-default" => hidden on Wayland, visible elsewhere + // other values: "visible", "hidden" + "startupVisibility": "platform-default", + }, + "youtubeSubgen": { + "mode": "automatic", // automatic | preprocess | off + "whisperBin": "/usr/bin/whisper-cli", + "whisperModel": "~/models/whisper.cpp/ggml-small.bin", + }, +} diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index b62ebdc..c6f74b9 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -133,7 +133,7 @@ bind = $mainMod, z, exec, uwsm app -sb -- zen-browser bind = $mainMod SHIFT, s, exec , rofi -show ssh -theme "$HOME/.config/rofi/launchers/type-2/style-2.rasi" -terminal -theme-str 'window{width: 25%;} listview {columns: 1; lines: 10;}' ghostty -ssh-command "ghostty --initial-command='TERM=kitty ssh {host}'" # reload monitors -bind = $mainMod SHIFT, R, exec, hyprctl dispatch dpms off && sleep 1 && hyprctl dispatch dpms on +bind = CTRL $mainMod SHIFT, R, exec, hyprctl dispatch dpms off && sleep 1 && hyprctl dispatch dpms on # Disable keybinds with one master keybind # https://wiki.hypr.land/0.49.0/Configuring/Uncommon-tips--tricks/#disabling-keybinds-with-one-master-keybind diff --git a/.config/mpv-modules/ani-skip b/.config/mpv-modules/ani-skip new file mode 160000 index 0000000..12b4960 --- /dev/null +++ b/.config/mpv-modules/ani-skip @@ -0,0 +1 @@ +Subproject commit 12b4960ecd34082a47ae4387d802b0a2847ec0a2 diff --git a/.config/opencode/opencode.json b/.config/opencode/opencode.json index 6d91777..8d3b32b 100644 --- a/.config/opencode/opencode.json +++ b/.config/opencode/opencode.json @@ -3,142 +3,16 @@ "plugin": [ "opencode-openai-codex-auth", "opencode-antigravity-auth@beta", - "@mohak34/opencode-notifier@latest", + "@mohak34/opencode-notifier@latest" ], + "mcp": { + "backlog": { + "type": "local", + "command": ["backlog", "mcp", "start"], + "enabled": true + } + }, "provider": { - "openai": { - "name": "OpenAI", - "options": { - "reasoningEffort": "medium", - "reasoningSummary": "auto", - "textVerbosity": "medium", - "include": [ - "reasoning.encrypted_content" - ], - "store": false - }, - "models": { - "gpt-5.2": { - "name": "GPT 5.2 (OAuth)", - "limit": { - "context": 272000, - "output": 128000 - }, - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "variants": { - "none": { - "reasoningEffort": "none", - "reasoningSummary": "auto", - "textVerbosity": "medium" - }, - "low": { - "reasoningEffort": "low", - "reasoningSummary": "auto", - "textVerbosity": "medium" - }, - "medium": { - "reasoningEffort": "medium", - "reasoningSummary": "auto", - "textVerbosity": "medium" - }, - "high": { - "reasoningEffort": "high", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - }, - "xhigh": { - "reasoningEffort": "xhigh", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - } - } - }, - "gpt-5.2-codex": { - "name": "GPT 5.2 Codex (OAuth)", - "limit": { - "context": 272000, - "output": 128000 - }, - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "variants": { - "low": { - "reasoningEffort": "low", - "reasoningSummary": "auto", - "textVerbosity": "medium" - }, - "medium": { - "reasoningEffort": "medium", - "reasoningSummary": "auto", - "textVerbosity": "medium" - }, - "high": { - "reasoningEffort": "high", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - }, - "xhigh": { - "reasoningEffort": "xhigh", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - } - } - }, - "gpt-5.2-codex-max": { - "name": "GPT 5.2 Codex Max (OAuth)", - "limit": { - "context": 272000, - "output": 128000 - }, - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "variants": { - "low": { - "reasoningEffort": "low", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - }, - "medium": { - "reasoningEffort": "medium", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - }, - "high": { - "reasoningEffort": "high", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - }, - "xhigh": { - "reasoningEffort": "xhigh", - "reasoningSummary": "detailed", - "textVerbosity": "medium" - } - } - } - } - }, "google": { "name": "Google", "models": { @@ -151,14 +25,8 @@ "output": 65535 }, "modalities": { - "input": [ - "text", - "image", - "pdf" - ], - "output": [ - "text" - ] + "input": ["text", "image", "pdf"], + "output": ["text"] } }, "antigravity-gemini-3-pro-low": { @@ -170,14 +38,8 @@ "output": 65535 }, "modalities": { - "input": [ - "text", - "image", - "pdf" - ], - "output": [ - "text" - ] + "input": ["text", "image", "pdf"], + "output": ["text"] } }, "antigravity-gemini-3-flash": { @@ -188,14 +50,8 @@ "output": 65536 }, "modalities": { - "input": [ - "text", - "image", - "pdf" - ], - "output": [ - "text" - ] + "input": ["text", "image", "pdf"], + "output": ["text"] } } } diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf index 0352340..36f292a 100644 --- a/.config/tmux/tmux.conf +++ b/.config/tmux/tmux.conf @@ -85,6 +85,8 @@ bind j select-pane -D bind k select-pane -U bind l select-pane -R +bind r source-file ~/.tmux.conf \; display "Reloaded tmux config" + set-window-option -g mode-keys vi set -g mode-keys vi set -g status-keys vi diff --git a/.gitmodules b/.gitmodules index 7003048..8385783 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule ".config/mpv-modules/animecards"] path = .config/mpv-modules/animecards url = git@github.com:ksyasuda/Anacreon-Script +[submodule ".config/mpv-modules/ani-skip"] + path = .config/mpv-modules/ani-skip + url = https://github.com/synacktraa/ani-skip.git diff --git a/.zsh/.zshrc##default b/.zsh/.zshrc##default index 01d7dee..b73332f 100644 --- a/.zsh/.zshrc##default +++ b/.zsh/.zshrc##default @@ -102,3 +102,11 @@ zle -N bracketed-paste bracketed-paste-magic alias claude-mem='bun "/home/sudacode/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs"' fpath=(/home/sudacode/.zsh/completions $fpath) autoload -Uz compinit && compinit + +# pnpm +export PNPM_HOME="/home/sudacode/.local/share/pnpm" +case ":$PATH:" in + *":$PNPM_HOME:"*) ;; + *) export PATH="$PNPM_HOME:$PATH" ;; +esac +# pnpm end diff --git a/projects/scripts/mpv-add.sh b/projects/scripts/mpv-add.sh index 9a9a760..bcf867d 100755 --- a/projects/scripts/mpv-add.sh +++ b/projects/scripts/mpv-add.sh @@ -2,28 +2,82 @@ set -Eeuo pipefail -URL="${1:-$(wl-paste -p)}" -MPV_SOCKET=/tmp/mpvsocket +URL="${1:-}" +MPV_SOCKET="/tmp/mpvsocket" ICON_PATH="$HOME/.local/share/icons/Magna-Glassy-Dark-Icons/apps/48/mpv.svg" TITLE="mpv-add.sh" +notify() { + local message="$1" + notify-send -i "$ICON_PATH" "$TITLE" "$message" +} + +trim() { + printf '%s' "$1" | sed 's/^[[:space:]]\+//;s/[[:space:]]\+$//' +} + +play_direct() { + mpv -- "$URL" &> /dev/null & +} + +wait_for_socket() { + local timeout_ms=2000 + local waited=0 + while ((waited < timeout_ms)); do + if [[ -S "$MPV_SOCKET" ]]; then + return 0 + fi + sleep 0.05 + waited=$((waited + 50)) + done + return 1 +} + +is_valid_input() { + local input="$1" + + if [[ -f "$input" ]]; then + return 0 + fi + + if ! command -v yt-dlp > /dev/null 2>&1; then + return 1 + fi + + if yt-dlp --simulate "$input" > /dev/null 2>&1; then + return 0 + fi + + return 1 +} + +if [[ -z "$URL" ]] && command -v wl-paste > /dev/null 2>&1; then + URL="$(wl-paste -p || true)" +fi + +URL="$(trim "$URL")" + if [[ -z "$URL" ]]; then - notify-send -i "$ICON_PATH" "$TITLE" "No URL provided" + notify "No URL provided" exit 1 fi -if ! [[ -f "$URL" ]] && ! yt-dlp --simulate "$URL"; then - notify-send -i "$ICON_PATH" "$TITLE" "Invalid URL" +if ! is_valid_input "$URL"; then + notify "Invalid input: provide a local file path or a yt-dlp supported URL" exit 1 fi if ! pgrep -x mpv &> /dev/null; then - mpv "$URL" &> /dev/null & - notify-send -i "$ICON_PATH" "$TITLE" "Playing $URL" + rm -f "$MPV_SOCKET" + mpv --input-ipc-server="$MPV_SOCKET" -- "$URL" &> /dev/null & + notify "Playing $URL" else - if echo "{ \"command\": [\"script-message\", \"add_to_queue\", \"$URL\" ] }" | socat - "$MPV_SOCKET" &> /dev/null; then - notify-send -i "$ICON_PATH" "$TITLE" "Added $URL to queue" - else - notify-send -i "$ICON_PATH" "$TITLE" "Failed to add $URL to queue" + if [[ -S "$MPV_SOCKET" ]] && wait_for_socket && echo "{ \"command\": [\"script-message\", \"add_to_queue\", \"$URL\" ] }" | socat - "$MPV_SOCKET" &> /dev/null; then + notify "Added $URL to queue" + exit 0 fi + + notify "Queue unavailable, opening in new player" + play_direct + notify "Playing $URL" fi diff --git a/projects/scripts/rmpv b/projects/scripts/rmpv index dddc8be..d91bd0f 100755 --- a/projects/scripts/rmpv +++ b/projects/scripts/rmpv @@ -4,6 +4,7 @@ THEME="${THEME:-$HOME/.local/share/SubMiner/themes/subminer.rasi}" FONTCONFIG_FILE=$HOME/.config/mpv/mpv-fonts.conf COMMAND=mpv VIDEO_EXTENSIONS="mkv|mp4|avi|webm|mov|flv|wmv|m4v|ts|m2ts" +TARGET_DIRECTORY="" # Parse command-line options first while getopts ":st:" opt; do @@ -26,6 +27,19 @@ while getopts ":st:" opt; do done shift $((OPTIND - 1)) +if [[ $# -gt 1 ]]; then + echo "Usage: $0 [-s] [-t theme] [directory]" >&2 + exit 1 +fi + +if [[ $# -eq 1 ]]; then + if [[ ! -d "$1" ]]; then + echo "Invalid directory: $1" >&2 + exit 1 + fi + TARGET_DIRECTORY="$(realpath "$1")" +fi + find_videos() { find "$PWD" -maxdepth 1 -type f -regextype posix-extended \ -iregex ".*\.($VIDEO_EXTENSIONS)$" 2> /dev/null | sort -V @@ -40,6 +54,60 @@ build_rofi_menu() { done < <(find_videos) } +is_video_file() { + local file="$1" + local regex="\\.($VIDEO_EXTENSIONS)$" + + if [[ ! "${file,,}" =~ $regex ]]; then + return 1 + fi + + if command -v file > /dev/null 2>&1; then + local mime + mime=$(file -b --mime-type "$file" 2>/dev/null || true) + [[ "$mime" == video/* ]] && return 0 + fi + + return 0 +} + +select_video_via_rofi() { + if [[ -n "$TARGET_DIRECTORY" ]]; then + local selection="" + local prompt="Choose Video " + while true; do + selection=$(rofi -show filebrowser -show-icons -theme "$THEME" \ + -theme-str 'configuration {font: "JetBrainsMono Nerd Font 10";} listview {columns: 1; lines: 15;} window {width: 88%;}' \ + -p "$prompt" \ + -filebrowser-directory "$TARGET_DIRECTORY" \ + -filebrowser-cancel-returns-1 true) + + [[ -z "$selection" ]] && return 1 + + if [[ "$selection" != /* ]]; then + selection="$TARGET_DIRECTORY/$selection" + fi + + if [[ -d "$selection" ]]; then + prompt="Choose Video (folders are not selectable)" + continue + fi + + if is_video_file "$selection"; then + echo "$selection" + return 0 + fi + + prompt="Choose Video (select a video file)" + done + fi + + build_rofi_menu | rofi -dmenu -i -show-icons -theme "$THEME" \ + -theme-str 'configuration {font: "JetBrainsMono Nerd Font 10";} listview {columns: 1; lines: 15;} window {width: 88%;}' -p "Choose Video " +} + +selection=$(select_video_via_rofi) + get_video_thumbnail() { local video="$1" local thumb_dir="$HOME/.cache/thumbnails/large" @@ -60,15 +128,24 @@ get_video_thumbnail() { fi } -selection=$(build_rofi_menu | rofi -dmenu -i -show-icons -theme "$THEME" \ - -theme-str 'configuration {font: "JetBrainsMono Nerd Font 10";} listview {columns: 1; lines: 15;} window {width: 88%;}' -p "Choose Video ") - if [[ -z "$selection" ]]; then echo "No video selected." exit 1 fi -choice="./$selection" +if [[ -n "$TARGET_DIRECTORY" ]]; then + choice="$selection" + if [[ "$choice" != /* ]]; then + choice="$TARGET_DIRECTORY/$choice" + fi +else + choice="./$selection" +fi + +if [[ ! -f "$choice" ]]; then + echo "Selected item is not a valid file: $choice" >&2 + exit 1 +fi THUMBNAIL_PATH=$(get_video_thumbnail "$choice") if [[ -n "$THUMBNAIL_PATH" && -f "$THUMBNAIL_PATH" ]]; then