code:
window:
  dimensions:
    columns: 140
    lines: 38

  padding:
    x: 10
    y: 10

  decorations: none

scrolling:
  history: 10000
  multiplier: 3

# background_opacity: 0.95
window_opacity: 0.80

# Font configuration
font:
  normal:
    # family: Input
    # family: Fira Code
    # style: Regular

    family: JetBrains Mono
    # style: Thin

    # family: Source Code Pro
    # family: Iosevka Term
    # family: Terminus
    style: Light

  bold:
    # family: Source Code Pro
    # family: Terminus
    family: JetBrains Mono
    style: Regular
    # Bold font face
    bold:
    # Font family
    #
    # If the bold family is not specified, it will fall back to the
    # value specified for the normal font.
    #family: monospace
    # family: Terminus
    family: JetBrains Mono

    # The `style` can be specified to pick a specific face.
    style: Bold

    # Italic font face
    italic:
    # Font family
    #
    # If the italic family is not specified, it will fall back to the
    # value specified for the normal font.
    #family: monospace
    # family: Terminus
    family: JetBrains Mono

    # The `style` can be specified to pick a specific face.
    style: Italic

    # Bold italic font face
    bold_italic:
    # Font family
    #
    # If the bold italic family is not specified, it will fall back to the
    # value specified for the normal font.
    #family: moospace
    # family: Terminus
    family: JetBrains Mono

    # The `style` can be specified to pick a specific face.
    style: Bold Italic

    # Point size
  size: 10.0

  offset:
    x: 0
    y: 0

  glyph_offset:
    x: 0
    y: 0
    # use_thin_strokes: true

    # If `true`, bold text is drawn using the bright color variants.
    #draw_bold_text_with_bright_colors: false

    # Colors (Tomorrow Night)
    #colors:
    # Default colors
    # primary:
    # background: '#1d1f21'
    # foreground: '#c5c8c6'

    # Bright and dim foreground colors
    #
    # The dimmed foreground color is calculated automatically if it is not present.
    # If the bright foreground color is not set, or `draw_bold_text_with_bright_colors`
    # is `false`, the normal foreground color will be used.
    #dim_foreground: '#828482'
    #bright_foreground: '#eaeaea'

    # Cursor colors
    #
    # Colors which should be used to draw the terminal cursor.
    #
    # Allowed values are CellForeground and CellBackground, which reference the
    # affected cell, or hexadecimal colors like #ff00ff.
    #cursor:
    #  text: CellBackground
    #  cursor: CellForeground

    # Vi mode cursor colors
    #
    # Colors for the cursor when the vi mode is active.
    #
    # Allowed values are CellForeground and CellBackground, which reference the
    # affected cell, or hexadecimal colors like #ff00ff.
    #vi_mode_cursor:
    #  text: CellBackground
    #  cursor: CellForeground

    # Selection colors
    #
    # Colors which should be used to draw the selection area.
    #
    # Allowed values are CellForeground and CellBackground, which reference the
    # affected cell, or hexadecimal colors like #ff00ff.
    #selection:
    #  text: CellBackground
    #  background: CellForeground

    # Search colors
    #
    # Colors used for the search bar and match highlighting.
    #search:
    # Allowed values are CellForeground and CellBackground, which reference the
    # affected cell, or hexadecimal colors like #ff00ff.
    #matches:
    #  foreground: '#000000'
    #  background: '#ffffff'

    #bar:
    #  background: '#c5c8c6'
    #  foreground: '#1d1f21'

    # Normal colors
    #normal:
    #  black:   '#1d1f21'
    #  red:     '#cc6666'
    #  green:   '#b5bd68'
    #  yellow:  '#f0c674'
    #  blue:    '#81a2be'
    #  magenta: '#b294bb'
    #  cyan:    '#8abeb7'
    #  white:   '#c5c8c6'

    # Bright colors
    #bright:
    #  black:   '#373b41'
    #  red:     '#a54242'
    #  green:   '#8c9440'
    #  yellow:  '#de935f'
    #  blue:    '#5f819d'
    #  magenta: '#85678f'
    #  cyan:    '#5e8d87'
    #  white:   '#707880'

    # Dim colors
    #
    # If the dim colors are not set, they will be calculated automatically based
    # on the `normal` colors.
    #dim:
    #  black:   '#131415'
    #  red:     '#864343'
    #  green:   '#777c44'
    #  yellow:  '#9e824c'
    #  blue:    '#556a7d'
    #  magenta: '#75617b'
    #  cyan:    '#5b7d78'
    #  white:   '#828482'

    # Indexed Colors
    #
    # The indexed colors include all colors from 16 to 256.
    # When these are not set, they're filled with sensible defaults.
    #
    # Example:
    #   `- { index: 16, color: '#ff00ff' }`
    #
    #indexed_colors: []

    # Bell
    #
    # The bell is rung every time the BEL control character is received.
    #bell:
    # Visual Bell Animation
    #
    # Animation effect for flashing the screen when the visual bell is rung.
    #
    # Values for `animation`:
    #   - Ease
    #   - EaseOut
    #   - EaseOutSine
    #   - EaseOutQuad
    #   - EaseOutCubic
    #   - EaseOutQuart
    #   - EaseOutQuint
    #   - EaseOutExpo
    #   - EaseOutCirc
    #   - Linear
    #animation: EaseOutExpo

    # Duration of the visual bell flash. A `duration` of `0` will disable the
    # visual bell animation.
    #duration: 0

    # Visual bell animation color.
    #color: '#ffffff'

    # Bell Command
    #
    # This program is executed whenever the bell is rung.
    #
    # When set to `command: None`, no command will be executed.
    #
    # Example:
    #   command:
    #     program: notify-send
    #     args: ["Hello, World!"]
    #
    #command: None

    # Background opacity
    #
    # Window opacity as a floating point number from `0.0` to `1.0`.
    # The value `0.0` is completely transparent and `1.0` is opaque.
    #background_opacity: 1.0

    #selection:
    # This string contains all characters that are used as separators for "semantic words" in Alacritty.
    #semantic_escape_chars: ",│`|:\"' ()[]{}<>\t"

    # When set to `true`, selected text will be copied to the primary clipboard.
    #save_to_clipboard: false

cursor:
  # Cursor style
  #
  # Values for `style`:
  #   - ▇ Block
  #   - _ Underline
  #   - | Beam
  style: Block

  # Vi mode cursor style
  #
  # If the vi mode cursor style is `None` or not specified, it will fall back to
  # the style of the active value of the normal cursor.
  #
  # See `cursor.style` for available options.
  #vi_mode_style: None

  # If this is `true`, the cursor will be rendered as a hollow box when the
  # window is not focused.
  unfocused_hollow: true
  # Thickness of the cursor relative to the cell width as floating point number
  # from `0.0` to `1.0`.
  #thickness: 0.15

  # Live config reload (changes require restart)
  #live_config_reload: true

  # Shell
  #
  # You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`.
  # Entries in `shell.args` are passed unmodified as arguments to the shell.
  #
  # Default:
  #   - (macOS) /bin/bash --login
  #   - (Linux/BSD) user login shell
  #   - (Windows) powershell
  #shell:
  #  program: /bin/bash
  #  args:
  #    - --login

  # Startup directory
  #
  # Directory the shell is started in. If this is unset, or `None`, the working
  # directory of the parent process will be used.
  #working_directory: None

  # WinPTY backend (Windows only)
  #
  # Alacritty defaults to using the newer ConPTY backend if it is available,
  # since it resolves a lot of bugs and is quite a bit faster. If it is not
  # available, the WinPTY backend will be used instead.
  #
  # Setting this option to `true` makes Alacritty use the legacy WinPTY backend,
  # even if the ConPTY backend is available.
  #winpty_backend: false

  # Send ESC (\x1b) before characters when alt is pressed.
  #alt_send_esc: true

  #mouse:
  # Click settings
  #
  # The `double_click` and `triple_click` settings control the time
  # alacritty should wait for accepting multiple clicks as one double
  # or triple click.
  #double_click: { threshold: 300 }
  #triple_click: { threshold: 300 }

  # If this is `true`, the cursor is temporarily hidden when typing.
  #hide_when_typing: false

  #url:
  # URL launcher
  #
  # This program is executed when clicking on a text which is recognized as a URL.
  # The URL is always added to the command as the last parameter.
  #
  # When set to `launcher: None`, URL launching will be disabled completely.
  #
  # Default:
  #   - (macOS) open
  #   - (Linux/BSD) xdg-open
  #   - (Windows) explorer
  #launcher:
  #  program: xdg-open
  #  args: []

  # URL modifiers
  #
  # These are the modifiers that need to be held down for opening URLs when clicking
  # on them. The available modifiers are documented in the key binding section.
  #modifiers: None

  # Mouse bindings
  #
  # Mouse bindings are specified as a list of objects, much like the key
  # bindings further below.
  #
  # To trigger mouse bindings when an application running within Alacritty captures the mouse, the
  # `Shift` modifier is automatically added as a requirement.
  #
  # Each mouse binding will specify a:
  #
  # - `mouse`:
  #
  #   - Middle
  #   - Left
  #   - Right
  #   - Numeric identifier such as `5`
  #
  # - `action` (see key bindings)
  #
  # And optionally:
  #
  # - `mods` (see key bindings)
  #mouse_bindings:
  #  - { mouse: Middle, action: PasteSelection }

  # Key bindings
  #
  # Key bindings are specified as a list of objects. For example, this is the
  # default paste binding:
  #
  # `- { key: V, mods: Control|Shift, action: Paste }`
  #
  # Each key binding will specify a:
  #
  # - `key`: Identifier of the key pressed
  #
  #    - A-Z
  #    - F1-F24
  #    - Key0-Key9
  #
  #    A full list with available key codes can be found here:
  #    https://docs.rs/glutin/*/glutin/event/enum.VirtualKeyCode.html#variants
  #
  #    Instead of using the name of the keys, the `key` field also supports using
  #    the scancode of the desired key. Scancodes have to be specified as a
  #    decimal number. This command will allow you to display the hex scancodes
  #    for certain keys:
  #
  #       `showkey --scancodes`.
  #
  # Then exactly one of:
  #
  # - `chars`: Send a byte sequence to the running application
  #
  #    The `chars` field writes the specified string to the terminal. This makes
  #    it possible to pass escape sequences. To find escape codes for bindings
  #    like `PageUp` (`"\x1b[5~"`), you can run the command `showkey -a` outside
  #    of tmux. Note that applications use terminfo to map escape sequences back
  #    to keys. It is therefore required to update the terminfo when changing an
  #    escape sequence.
  #
  # - `action`: Execute a predefined action
  #
  #   - ToggleViMode
  #   - SearchForward
  #       Start searching toward the right of the search origin.
  #   - SearchBackward
  #       Start searching toward the left of the search origin.
  #   - Copy
  #   - Paste
  #   - IncreaseFontSize
  #   - DecreaseFontSize
  #   - ResetFontSize
  #   - ScrollPageUp
  #   - ScrollPageDown
  #   - ScrollHalfPageUp
  #   - ScrollHalfPageDown
  #   - ScrollLineUp
  #   - ScrollLineDown
  #   - ScrollToTop
  #   - ScrollToBottom
  #   - ClearHistory
  #       Remove the terminal's scrollback history.
  #   - Hide
  #       Hide the Alacritty window.
  #   - Minimize
  #       Minimize the Alacritty window.
  #   - Quit
  #       Quit Alacritty.
  #   - ToggleFullscreen
  #   - SpawnNewInstance
  #       Spawn a new instance of Alacritty.
  #   - ClearLogNotice
  #       Clear Alacritty's UI warning and error notice.
  #   - ClearSelection
  #       Remove the active selection.
  #   - ReceiveChar
  #   - None
  #
  #   (`mode: Vi` only):
  #   - Open
  #       Open URLs at the cursor location with the launcher configured in `url.launcher`.
  #   - Up
  #       Move the vi mode cursor up by one line.
  #   - Down
  #       Move the vi mode cursor down by one line.
  #   - Left
  #       Move the vi mode cursor left by one character.
  #   - Right
  #       Move the vi mode cursor right by one character.
  #   - First
  #       Move the vi mode cursor to the start of the line, searching across newlines if it is already at the beginning.
  #   - Last
  #       Move the vi mode cursor to the end of the line, searching across newlines if it is already at the end.
  #   - FirstOccupied
  #       Move the vi mode cursor to the first non-empty cell in this line, searching across newlines if it is already at the first one.
  #   - High
  #       Move the vi mode cursor to the top of the screen.
  #   - Middle
  #       Move the vi mode cursor to the middle of the screen.
  #   - Low
  #       Move the vi mode cursor to the bottom of the screen.
  #   - SemanticLeft
  #       Move the vi mode cursor to the start of the previous semantically separated word.
  #   - SemanticRight
  #       Move the vi mode cursor to the start of the next semantically separated word.
  #   - SemanticLeftEnd
  #       Move the vi mode cursor to the end of the previous semantically separated word.
  #   - SemanticRightEnd
  #       Move the vi mode cursor to the end of the next semantically separated word.
  #   - WordLeft
  #       Move the vi mode cursor to the start of the previous whitespace separated word.
  #   - WordRight
  #       Move the vi mode cursor to the start of the next whitespace separated word.
  #   - WordLeftEnd
  #       Move the vi mode cursor to the end of the previous whitespace separated word.
  #   - WordRightEnd
  #       Move the vi mode cursor to the end of the next whitespace separated word.
  #   - Bracket
  #       Move the vi mode cursor to the next character that matches the bracket at the cursor's
  #       current location.
  #   - ToggleNormalSelection
  #   - ToggleLineSelection
  #   - ToggleBlockSelection
  #   - ToggleSemanticSelection
  #       Toggle semantic selection based on `selection.semantic_escape_chars`.
  #   - SearchNext
  #       Jump to the beginning of the next match.
  #   - SearchPrevious
  #       Jump to the beginning of the previous match.
  #   - SearchStart
  #       Jump to the next start of a match to the left of the vi mode cursor.
  #   - SearchEnd
  #       Jump to the next end of a match to the left of the vi mode cursor.
  #
  #   (macOS only):
  #   - ToggleSimpleFullscreen
  #       Enter fullscreen without occupying another space.
  #
  #   (Linux/BSD only):
  #   - CopySelection
  #       Copy from the selection buffer.
  #   - PasteSelection
  #       Paste from the selection buffer.
  #
  # - `command`: Fork and execute a specified command plus arguments
  #
  #    The `command` field must be a map containing a `program` string and an
  #    `args` array of command line parameter strings. For example:
  #       `{ program: "alacritty", args: ["-e", "vttest"] }`
  #
  # And optionally:
  #
  # - `mods`: Key modifiers to filter binding actions
  #
  #    - Command
  #    - Control
  #    - Option
  #    - Super
  #    - Shift
  #    - Alt
  #
  #    Multiple `mods` can be combined using `|` like this:
  #       `mods: Control|Shift`.
  #    Whitespace and capitalization are relevant and must match the example.
  #
  # - `mode`: Indicate a binding for only specific terminal reported modes
  #
  #    This is mainly used to send applications the correct escape sequences
  #    when in different modes.
  #
  #    - AppCursor
  #    - AppKeypad
  #    - Alt
  #    - Vi
  #
  #    A `~` operator can be used before a mode to apply the binding whenever
  #    the mode is *not* active, e.g. `~Alt`.
  #
  # Bindings are always filled by default, but will be replaced when a new
  # binding with the same triggers is defined. To unset a default binding, it can
  # be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for
  # a no-op if you do not wish to receive input characters for that binding.
  #
  # If the same trigger is assigned to multiple actions, all of them are executed
  # in the order they were defined in.
key_bindings:
  #- { key: Paste,                                action: Paste          }
  #- { key: Copy,                                 action: Copy           }
  #- { key: L,         mods: Control,             action: ClearLogNotice }
  #- { key: L,         mods: Control, mode: ~Vi,  chars: "\x0c"          }
  #- { key: PageUp,    mods: Shift,   mode: ~Alt, action: ScrollPageUp,  }
  #- { key: PageDown,  mods: Shift,   mode: ~Alt, action: ScrollPageDown }
  #- { key: Home,      mods: Shift,   mode: ~Alt, action: ScrollToTop,   }
  #- { key: End,       mods: Shift,   mode: ~Alt, action: ScrollToBottom }

  # Vi Mode
  #- { key: Space,  mods: Shift|Control, mode: Vi, action: ScrollToBottom          }
  #- { key: Space,  mods: Shift|Control,           action: ToggleViMode            }
  #- { key: Escape,                      mode: Vi, action: ClearSelection          }
  #- { key: I,                           mode: Vi, action: ScrollToBottom          }
  #- { key: I,                           mode: Vi, action: ToggleViMode            }
  #- { key: C,      mods: Control,       mode: Vi, action: ToggleViMode            }
  #- { key: Y,      mods: Control,       mode: Vi, action: ScrollLineUp            }
  #- { key: E,      mods: Control,       mode: Vi, action: ScrollLineDown          }
  #- { key: G,                           mode: Vi, action: ScrollToTop             }
  #- { key: G,      mods: Shift,         mode: Vi, action: ScrollToBottom          }
  #- { key: B,      mods: Control,       mode: Vi, action: ScrollPageUp            }
  #- { key: F,      mods: Control,       mode: Vi, action: ScrollPageDown          }
  #- { key: U,      mods: Control,       mode: Vi, action: ScrollHalfPageUp        }
  #- { key: D,      mods: Control,       mode: Vi, action: ScrollHalfPageDown      }
  #- { key: Y,                           mode: Vi, action: Copy                    }
  #- { key: Y,                           mode: Vi, action: ClearSelection          }
  #- { key: Copy,                        mode: Vi, action: ClearSelection          }
  #- { key: V,                           mode: Vi, action: ToggleNormalSelection   }
  #- { key: V,      mods: Shift,         mode: Vi, action: ToggleLineSelection     }
  #- { key: V,      mods: Control,       mode: Vi, action: ToggleBlockSelection    }
  #- { key: V,      mods: Alt,           mode: Vi, action: ToggleSemanticSelection }
  #- { key: Return,                      mode: Vi, action: Open                    }
  #- { key: K,                           mode: Vi, action: Up                      }
  #- { key: J,                           mode: Vi, action: Down                    }
  #- { key: H,                           mode: Vi, action: Left                    }
  #- { key: L,                           mode: Vi, action: Right                   }
  #- { key: Up,                          mode: Vi, action: Up                      }
  #- { key: Down,                        mode: Vi, action: Down                    }
  #- { key: Left,                        mode: Vi, action: Left                    }
  #- { key: Right,                       mode: Vi, action: Right                   }
  #- { key: Key0,                        mode: Vi, action: First                   }
  #- { key: Key4,   mods: Shift,         mode: Vi, action: Last                    }
  #- { key: Key6,   mods: Shift,         mode: Vi, action: FirstOccupied           }
  #- { key: H,      mods: Shift,         mode: Vi, action: High                    }
  #- { key: M,      mods: Shift,         mode: Vi, action: Middle                  }
  #- { key: L,      mods: Shift,         mode: Vi, action: Low                     }
  #- { key: B,                           mode: Vi, action: SemanticLeft            }
  #- { key: W,                           mode: Vi, action: SemanticRight           }
  #- { key: E,                           mode: Vi, action: SemanticRightEnd        }
  #- { key: B,      mods: Shift,         mode: Vi, action: WordLeft                }
  #- { key: W,      mods: Shift,         mode: Vi, action: WordRight               }
  #- { key: E,      mods: Shift,         mode: Vi, action: WordRightEnd            }
  #- { key: Key5,   mods: Shift,         mode: Vi, action: Bracket                 }
  #- { key: Slash,                       mode: Vi, action: SearchForward           }
  #- { key: Slash,  mods: Shift,         mode: Vi, action: SearchBackward          }
  #- { key: N,                           mode: Vi, action: SearchNext              }
  #- { key: N,      mods: Shift,         mode: Vi, action: SearchPrevious          }

  # (Windows, Linux, and BSD only)
  #- { key: V,              mods: Control|Shift,           action: Paste            }
  #- { key: C,              mods: Control|Shift,           action: Copy             }
  #- { key: F,              mods: Control|Shift,           action: SearchForward    }
  #- { key: B,              mods: Control|Shift,           action: SearchBackward   }
  #- { key: C,              mods: Control|Shift, mode: Vi, action: ClearSelection   }
  #- { key: Insert,         mods: Shift,                   action: PasteSelection   }
  #- { key: Key0,           mods: Control,                 action: ResetFontSize    }
  #- { key: Equals,         mods: Control,                 action: IncreaseFontSize }
  #- { key: Plus,           mods: Control,                 action: IncreaseFontSize }
  #- { key: NumpadAdd,      mods: Control,                 action: IncreaseFontSize }
  #- { key: Minus,          mods: Control,                 action: DecreaseFontSize }
  #- { key: NumpadSubtract, mods: Control,                 action: DecreaseFontSize }

  # (Windows only)
  #- { key: Return,   mods: Alt,           action: ToggleFullscreen }

  # (macOS only)
  #- { key: K,              mods: Command, mode: ~Vi, chars: "\x0c"            }
  #- { key: Key0,           mods: Command,            action: ResetFontSize    }
  #- { key: Equals,         mods: Command,            action: IncreaseFontSize }
  #- { key: Plus,           mods: Command,            action: IncreaseFontSize }
  #- { key: NumpadAdd,      mods: Command,            action: IncreaseFontSize }
  #- { key: Minus,          mods: Command,            action: DecreaseFontSize }
  #- { key: NumpadSubtract, mods: Command,            action: DecreaseFontSize }
  #- { key: K,              mods: Command,            action: ClearHistory     }
  #- { key: V,              mods: Command,            action: Paste            }
  #- { key: C,              mods: Command,            action: Copy             }
  #- { key: C,              mods: Command, mode: Vi,  action: ClearSelection   }
  #- { key: H,              mods: Command,            action: Hide             }
  #- { key: M,              mods: Command,            action: Minimize         }
  #- { key: Q,              mods: Command,            action: Quit             }
  #- { key: W,              mods: Command,            action: Quit             }
  - { key: N, mods: Command, action: SpawnNewInstance }
  - { key: F, mods: Control, action: ToggleFullscreen }
  #- { key: F,              mods: Command|Control,    action: ToggleFullscreen }
  #- { key: F,              mods: Command,            action: SearchForward    }
  #- { key: B,              mods: Command,            action: SearchBackward   }

  #debug:
  # Display the time it takes to redraw each frame.
  #render_timer: false

  # Keep the log file after quitting Alacritty.
  #persistent_logging: false

  # Log level
  #
  # Values for `log_level`:
  #   - None
  #   - Error
  #   - Warn
  #   - Info
  #   - Debug
  #   - Trace
  #log_level: Warn

  # Print all received window events.
  #print_events: false
  #
  #
  # ------------------------------------
  # colors:
  #   # Default colors
  #   primary:
  #     background: '0x1d1f21'
  #     foreground: '0xc5c8c6'
  #
  #   # Normal colors
  #   normal:
  #     black:   '0x282a2e'
  #     red:     '0xa54242'
  #     green:   '0x8c9440'
  #     yellow:  '0xde935f'
  #     blue:    '0x5f819d'
  #     magenta: '0x85678f'
  #     cyan:    '0x5e8d87'
  #     white:   '0x707880'
  #
  #   # Bright colors
  #   bright:
  #     black:   '0x373b41'
  #     red:     '0xcc6666'
  #     green:   '0xb5bd68'
  #     yellow:  '0xf0c674'
  #     blue:    '0x81a2be'
  #     magenta: '0xb294bb'
  #     cyan:    '0x8abeb7'
  #     white:   '0xc5c8c6'
  # ----------------------------------
colors:
  # Default colors
  primary:
    background: "0x1d1f21"
    foreground: "0xc5c8c6"

  # Normal colors
  normal:
    black: "0x282a2e"
    red: "0xa54242"
    green: "0x8c9440"
    yellow: "0xde935f"
    blue: "0x5f819d"
    magenta: "0x85678f"
    cyan: "0x5e8d87"
    white: "0x707880"

  # Bright colors
  bright:
    black: "0x373b41"
    red: "0xcc6666"
    green: "0xb5bd68"
    yellow: "0xf0c674"
    blue: "0x81a2be"
    magenta: "0xb294bb"
    cyan: "0x8abeb7"
    white: "0xc5c8c6"

  cursor:
    text: "0xc5c8c6"
    cursor: "0xcc6666"


code:
#! /bin/sh

# ----------------------------------------------------
# AUTOSTART
# ----------------------------------------------------
pgrep -x sxhkd > /dev/null || sxhkd &

# FIX CURSOR.
xsetroot -cursor_name left_ptr

# RUN NOTIFICATION DAEMON.
dunst --config $HOME/.config/dunst/dunstrc &

# SYSTRAY
# nm-applet &
# volctl &
# volumeicon &
# xbacklight -set 50
# sleep 3 && sbxkb &
# sleep 4 && udiskie --automount --notify --tray &

dunst &
unclutter &
# picom -m &
# picom --backend glx --vsync &
# picom &
numlockx &

# BAR POLYBAR
polybar &
# ~/.config/polybar/./launch.sh --shapes &

# xrdb merge ~/.Xresources

# ----------------------------------------------------
# BSPWM
# ----------------------------------------------------

# bspc monitor -d I II III IV V VI VII VIII IX X
bspc monitor -d '1:' '2:' '3:󰟞' '4:󰍩' '5:󰠮' '6:󰎄' '7:󱉟' '8:󰈫' '9:󰣠'

bspc config border_width         2
bspc config window_gap           4

bspc config split_ratio          0.52
bspc config borderless_monocle   true
bspc config gapless_monocle      true

bspc config focused_border_color     "#de935f"
bspc config normal_border_color      "#1d1f21"
bspc config presel_feedback_color    '#81a2be'

bspc rule -a firefox desktop='^2' follow=on focus=on
bspc rule -a TelegramDesktop desktop='^4' follow=on focus=on
bspc rule -a Element desktop='^4' follow=on focus=on

bspc rule -a calibre desktop='^7' follow=on focus=on

bspc rule -a obs desktop='^8' follow=on focus=on
bspc rule -a kdenlive desktop='^8' follow=on focus=on

bspc rule -a mpv state=floating focus=on follow=on

bspc rule -a obsidian desktop='^5' focus=on follow=on
bspc rule -a qBittorrent desktop='^9' focus=on follow=on

# bspc rule -a Gimp desktop='^8' state=floating follow=on
# bspc rule -a Chromium desktop='^2'
# bspc rule -a mplayer2 state=floating
# bspc rule -a Kupfer.py focus=on
# bspc rule -a Screenkey manage=off

# ----------------------------------------------------
# POST LOADING BSPWM
# ----------------------------------------------------
#
# WALLPAPERS
# feh --bg-fill ~/Wallpapers/archkali.png &

path: config/bspwm/bspwmrc

license: mit


code:
## Configuration file for CAVA. Default values are commented out. Use either ';' or '#' for commenting.

[general]

# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
; mode = normal

# Accepts only non-negative values.
; framerate = 60

# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = off
# new as of 0.6.0 autosens of low values (dynamic range)
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
; autosens = 1
; overshoot = 20

# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
# 200 means double height. Accepts only non-negative values.
; sensitivity = 100

# The number of bars (0-200). 0 sets it to auto (fill up console).
# Bars' width and space between bars in number of characters.
; bars = 0
bar_width = 0
; bar_spacing = 1

# Lower and higher cutoff frequencies for lowest and highest bars
# the bandwidth of the visualizer.
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
# Cava will automatically increase the higher cutoff if a too low band is specified.
; lower_cutoff_freq = 50
; higher_cutoff_freq = 10000

# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
; sleep_timer = 0

[input]

# Audio capturing method. Possible methods are: 'pulse', 'alsa', 'fifo', 'sndio' or 'shmem'
# Defaults to 'pulse', 'alsa' or 'fifo', in that order, dependent on what support cava was built with.
#
# All input methods uses the same config variable 'source'
# to define where it should get the audio.
#
# For pulseaudio 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
#
# For alsa 'source' will be the capture device.
# For fifo 'source' will be the path to fifo-file.
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
method = pulse
source = auto

; method = alsa
; source = hw:Loopback,1

; method = fifo
; source = /tmp/mpd.fifo
; sample_rate = 44100
; sample_bits = 16

; method = shmem
; source = /squeezelite-AA:BB:CC:DD:EE:FF

; method = portaudio
; source = auto

[output]

# Output method. Can be 'ncurses', 'noncurses' or 'raw'.
# 'noncurses' uses a custom framebuffer technique and draws only changes
# from frame to frame. 'ncurses' is default if supported
#
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
# stream of the bar heights that can be used to send to other applications.
# 'raw' defaults to 200 bars, which can be adjusted in the 'bars' option above.
; method = ncurses

# Visual channels. Can be 'stereo' or 'mono'.
# 'stereo' mirrors both channels with low frequencies in center.
# 'mono' outputs left to right lowest to highest frequencies.
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
; channels = stereo
; mono_option = average

# Raw output target. A fifo will be created if target does not exist.
; raw_target = /dev/stdout

# Raw data format. Can be 'binary' or 'ascii'.
; data_format = binary

# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
; bit_format = 16bit

# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
; ascii_max_range = 1000

# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
; bar_delimiter = 59
; frame_delimiter = 10

[color]

# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
# ncurses output method and a terminal that can change color definitions such as Gnome-terminal or rxvt.
# if supported, ncurses mode will be forced on if user defined colors are used.
# default is to keep current terminal color
; background = default
foreground = magenta

# Gradient mode, only hex defined colors (and thereby ncurses mode) are supported,
# background must also be defined in hex  or remain commented out. 1 = on, 0 = off.
# You can define as many as 8 different colors. They range from bottom to top of screen
; gradient = 1
; gradient_count = 8
; gradient_color_1 = '#59cc33'
; gradient_color_2 = '#80cc33'
; gradient_color_3 = '#a6cc33'
; gradient_color_4 = '#cccc33'
; gradient_color_5 = '#cca633'
; gradient_color_6 = '#cc8033'
; gradient_color_7 = '#cc5933'
; gradient_color_8 = '#cc3333'

[smoothing]

# Percentage value for integral smoothing. Takes values from 0 - 100.
# Higher values means smoother, but less precise. 0 to disable.
; integral = 77

# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
; monstercat = 0
; waves = 0

# Set gravity percentage for "drop off". Higher values means bars will drop faster.
# Accepts only non-negative values. 50 means half gravity, 200 means double. Set to 0 to disable "drop off".
; gravity = 100

# In bar height, bars that would have been lower that this will not be drawn.
; ignore = 0

[eq]

# This one is tricky. You can have as much keys as you want.
# Remember to uncomment more then one key! More keys = more precision.
# Look at readme.md on github for further explanations and examples.
; 1 = 1 # bass
; 2 = 1
; 3 = 1 # midtone
; 4 = 1
; 5 = 1 # treble

path: config/cava/config


code:
[global]
    ### Display ###

    # Which monitor should the notifications be displayed on.
    monitor = 0

    # Display notification on focused monitor.  Possible modes are:
    #   mouse: follow mouse pointer
    #   keyboard: follow window with keyboard focus
    #   none: don't follow anything
    #
    # "keyboard" needs a window manager that exports the
    # _NET_ACTIVE_WINDOW property.
    # This should be the case for almost all modern window managers.
    #
    # If this option is set to mouse or keyboard, the monitor option
    # will be ignored.
    follow = mouse

    # The geometry of the window:
    #   [{width}]x{height}[+/-{x}+/-{y}]
    # The geometry of the message window.
    # The height is measured in number of notifications everything else
    # in pixels.  If the width is omitted but the height is given
    # ("-geometry x2"), the message window expands over the whole screen
    # (dmenu-like).  If width is 0, the window expands to the longest
    # message displayed.  A positive x is measured from the left, a
    # negative from the right side of the screen.  Y is measured from
    # the top and down respectively.
    # The width can be negative.  In this case the actual width is the
    # screen width minus the width defined in within the geometry option.
    geometry = "300x5-30+20"

    # Show how many messages are currently hidden (because of geometry).
    indicate_hidden = yes

    # Shrink window if it's smaller than the width.  Will be ignored if
    # width is 0.
    shrink = no

    # The transparency of the window.  Range: [0; 100].
    # This option will only work if a compositing window manager is
    # present (e.g. xcompmgr, compiz, etc.).
    transparency = 0

    # The height of the entire notification.  If the height is smaller
    # than the font height and padding combined, it will be raised
    # to the font height and padding.
    notification_height = 0

    # Draw a line of "separator_height" pixel height between two
    # notifications.
    # Set to 0 to disable.
    separator_height = 2

    # Padding between text and separator.
    padding = 8

    # Horizontal padding.
    horizontal_padding = 8

    # Defines width in pixels of frame around the notification window.
    # Set to 0 to disable.
    frame_width = 3

    # Defines color of the frame around the notification window.
    frame_color = "#373b41"

    # Define a color for the separator.
    # possible values are:
    #  * auto: dunst tries to find a color fitting to the background;
    #  * foreground: use the same color as the foreground;
    #  * frame: use the same color as the frame;
    #  * anything else will be interpreted as a X color.
    separator_color = frame

    # Sort messages by urgency.
    sort = yes

    # Don't remove messages, if the user is idle (no mouse or keyboard input)
    # for longer than idle_threshold seconds.
    # Set to 0 to disable.
    # A client can set the 'transient' hint to bypass this. See the rules
    # section for how to disable this if necessary
    idle_threshold = 120

    ### Text ###

    # font = Monospace 9
    font = JetBrains Mono Medium 10

    # The spacing between lines.  If the height is smaller than the
    # font height, it will get raised to the font height.
    line_height = 0

    # Possible values are:
    # full: Allow a small subset of html markup in notifications:
    #        bold
    #        italic
    #        <s>strikethrough</s>
    #        underline
    #
    #        For a complete reference see
    #        <https://developer.gnome.org/pango/stable/pango-Markup.html>.
    #
    # strip: This setting is provided for compatibility with some broken
    #        clients that send markup even though it's not enabled on the
    #        server. Dunst will try to strip the markup but the parsing is
    #        simplistic so using this option outside of matching rules for
    #        specific applications *IS GREATLY DISCOURAGED*.
    #
    # no:    Disable markup parsing, incoming notifications will be treated as
    #        plain text. Dunst will not advertise that it has the body-markup
    #        capability if this is set as a global setting.
    #
    # It's important to note that markup inside the format option will be parsed
    # regardless of what this is set to.
    markup = full

    # The format of the message.  Possible variables are:
    #   %a  appname
    #   %s  summary
    #   %b  body
    #   %i  iconname (including its path)
    #   %I  iconname (without its path)
    #   %p  progress value if set ([  0%] to [100%]) or nothing
    #   %n  progress value if set without any extra characters
    #   %%  Literal %
    # Markup is allowed
    format = "%s\n%b"

    # Alignment of message text.
    # Possible values are "left", "center" and "right".
    alignment = left

    # Vertical alignment of message text and icon.
    # Possible values are "top", "center" and "bottom".
    vertical_alignment = center

    # Show age of message if message is older than show_age_threshold
    # seconds.
    # Set to -1 to disable.
    show_age_threshold = 60

    # Split notifications into multiple lines if they don't fit into
    # geometry.
    word_wrap = yes

    # When word_wrap is set to no, specify where to make an ellipsis in long lines.
    # Possible values are "start", "middle" and "end".
    ellipsize = middle

    # Ignore newlines '\n' in notifications.
    ignore_newline = no

    # Stack together notifications with the same content
    stack_duplicates = true

    # Hide the count of stacked notifications with the same content
    hide_duplicate_count = false

    # Display indicators for URLs (U) and actions (A).
    show_indicators = yes

    ### Icons ###

    # Align icons left/right/off
    icon_position = left

    # Scale small icons up to this size, set to 0 to disable. Helpful
    # for e.g. small files or high-dpi screens. In case of conflict,
    # max_icon_size takes precedence over this.
    min_icon_size = 0

    # Scale larger icons down to this size, set to 0 to disable
    max_icon_size = 32

    # Paths to default icons.
    icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/

    ### History ###

    # Should a notification popped up from history be sticky or timeout
    # as if it would normally do.
    sticky_history = yes

    # Maximum amount of notifications kept in history
    history_length = 20

    ### Misc/Advanced ###

    # dmenu path.
    dmenu = /usr/bin/dmenu -p dunst:

    # Browser for opening urls in context menu.
    browser = /usr/bin/firefox -new-tab

    # Always run rule-defined scripts, even if the notification is suppressed
    always_run_script = true

    # Define the title of the windows spawned by dunst
    title = Dunst

    # Define the class of the windows spawned by dunst
    class = Dunst

    # Print a notification on startup.
    # This is mainly for error detection, since dbus (re-)starts dunst
    # automatically after a crash.
    startup_notification = false

    # Manage dunst's desire for talking
    # Can be one of the following values:
    #  crit: Critical features. Dunst aborts
    #  warn: Only non-fatal warnings
    #  mesg: Important Messages
    #  info: all unimportant stuff
    # debug: all less than unimportant stuff
    verbosity = mesg

    # Define the corner radius of the notification window
    # in pixel size. If the radius is 0, you have no rounded
    # corners.
    # The radius will be automatically lowered if it exceeds half of the
    # notification height to avoid clipping text and/or icons.
    corner_radius = 0

    # Ignore the dbus closeNotification message.
    # Useful to enforce the timeout set by dunst configuration. Without this
    # parameter, an application may close the notification sent before the
    # user defined timeout.
    ignore_dbusclose = false

    ### Legacy

    # Use the Xinerama extension instead of RandR for multi-monitor support.
    # This setting is provided for compatibility with older nVidia drivers that
    # do not support RandR and using it on systems that support RandR is highly
    # discouraged.
    #
    # By enabling this setting dunst will not be able to detect when a monitor
    # is connected or disconnected which might break follow mode if the screen
    # layout changes.
    force_xinerama = false

    ### mouse

    # Defines list of actions for each mouse event
    # Possible values are:
    # * none: Don't do anything.
    # * do_action: If the notification has exactly one action, or one is marked as default,
    #              invoke it. If there are multiple and no default, open the context menu.
    # * close_current: Close current notification.
    # * close_all: Close all notifications.
    # These values can be strung together for each mouse event, and
    # will be executed in sequence.
    mouse_left_click = close_current
    mouse_middle_click = do_action, close_current
    mouse_right_click = close_all

# Experimental features that may or may not work correctly. Do not expect them
# to have a consistent behaviour across releases.
[experimental]
    # Calculate the dpi to use on a per-monitor basis.
    # If this setting is enabled the Xft.dpi value will be ignored and instead
    # dunst will attempt to calculate an appropriate dpi value for each monitor
    # using the resolution and physical size. This might be useful in setups
    # where there are multiple screens with very different dpi values.
    per_monitor_dpi = false

[shortcuts]

    # Shortcuts are specified as [modifier+][modifier+]...key
    # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2",
    # "mod3" and "mod4" (windows-key).
    # Xev might be helpful to find names for keys.

    # Close notification.
    close = ctrl+space

    # Close all notifications.
    close_all = ctrl+shift+space

    # Redisplay last message(s).
    # On the US keyboard layout "grave" is normally above TAB and left
    # of "1". Make sure this key actually exists on your keyboard layout,
    # e.g. check output of 'xmodmap -pke'
    history = ctrl+grave

    # Context menu.
    context = ctrl+shift+period

[urgency_low]
    # IMPORTANT: colors have to be defined in quotation marks.
    # Otherwise the "#" and following would be interpreted as a comment.
    background = "#1d1f21"
    foreground = "#c5c8c6"
    timeout = 10
    # Icon for notifications with low urgency, uncomment to enable
    #icon = /path/to/icon

[urgency_normal]
    background = "#1d1f21"
    foreground = "#c5c8c6"
    timeout = 10
    # Icon for notifications with normal urgency, uncomment to enable
    #icon = /path/to/icon

[urgency_critical]
    background = "#a54242"
    foreground = "#c5c8c6"
    frame_color = "#cc6666"
    timeout = 0
    # Icon for notifications with critical urgency, uncomment to enable
    #icon = /path/to/icon

# Every section that isn't one of the above is interpreted as a rules to
# override settings for certain messages.
#
# Messages can be matched by
#    appname (discouraged, see desktop_entry)
#    body
#    category
#    desktop_entry
#    icon
#    match_transient
#    msg_urgency
#    stack_tag
#    summary
#
# and you can override the
#    background
#    foreground
#    format
#    frame_color
#    fullscreen
#    new_icon
#    set_stack_tag
#    set_transient
#    timeout
#    urgency
#
# Shell-like globbing will get expanded.
#
# Instead of the appname filter, it's recommended to use the desktop_entry filter.
# GLib based applications export their desktop-entry name. In comparison to the appname,
# the desktop-entry won't get localized.
#
# SCRIPTING
# You can specify a script that gets run when the rule matches by
# setting the "script" option.
# The script will be called as follows:
#   script appname summary body icon urgency
# where urgency can be "LOW", "NORMAL" or "CRITICAL".
#
# NOTE: if you don't want a notification to be displayed, set the format
# to "".
# NOTE: It might be helpful to run dunst -print in a terminal in order
# to find fitting options for rules.

# Disable the transient hint so that idle_threshold cannot be bypassed from the
# client
#[transient_disable]
#    match_transient = yes
#    set_transient = no
#
# Make the handling of transient notifications more strict by making them not
# be placed in history.
#[transient_history_ignore]
#    match_transient = yes
#    history_ignore = yes

# fullscreen values
# show: show the notifications, regardless if there is a fullscreen window opened
# delay: displays the new notification, if there is no fullscreen window active
#        If the notification is already drawn, it won't get undrawn.
# pushback: same as delay, but when switching into fullscreen, the notification will get
#           withdrawn from screen again and will get delayed like a new notification
#[fullscreen_delay_everything]
#    fullscreen = delay
#[fullscreen_show_critical]
#    msg_urgency = critical
#    fullscreen = show

#[espeak]
#    summary = "*"
#    script = dunst_espeak.sh

#[script-test]
#    summary = "*script*"
#    script = dunst_test.sh

#[ignore]
#    # This notification will not be displayed
#    summary = "foobar"
#    format = ""

#[history-ignore]
#    # This notification will not be saved in history
#    summary = "foobar"
#    history_ignore = yes

#[skip-display]
#    # This notification will not be displayed, but will be included in the history
#    summary = "foobar"
#    skip_display = yes

#[signed_on]
#    appname = Pidgin
#    summary = "*signed on*"
#    urgency = low
#
#[signed_off]
#    appname = Pidgin
#    summary = *signed off*
#    urgency = low
#
#[says]
#    appname = Pidgin
#    summary = *says*
#    urgency = critical
#
#[twitter]
#    appname = Pidgin
#    summary = *twitter.com*
#    urgency = normal
#
[stack-volumes]
   appname = "some_volume_notifiers"
   set_stack_tag = "volume"
#
# vim: ft=cfg

path: config/dunst/dunstrc


code:
[Filechooser Settings]
LocationMode=path-bar
ShowHidden=false
ShowSizeColumn=true
GeometryX=253
GeometryY=236
GeometryWidth=840
GeometryHeight=630
SortColumn=name
SortOrder=ascending
StartupMode=recent


code:
file:///home/user/Downloads Downloads
file:///home/user/Documents Documents
file:///home/user/Documents/Project Project


code:
[Settings]
gtk-theme-name=Arc-Lighter
gtk-icon-theme-name=kora-light
gtk-font-name=Cantarell 11
gtk-cursor-theme-size=0
gtk-toolbar-style=GTK_TOOLBAR_BOTH
gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
gtk-button-images=1
gtk-menu-images=1
gtk-enable-event-sounds=1
gtk-enable-input-feedback-sounds=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintfull
gtk-cursor-theme-name=Adwaita


code:
[Settings]
gtk-theme-name=Arc-Dark
gtk-icon-theme-name=kora-light
gtk-font-name=Cantarell 11
gtk-cursor-theme-size=0
gtk-toolbar-style=GTK_TOOLBAR_BOTH
gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
gtk-button-images=1
gtk-menu-images=1
gtk-enable-event-sounds=1
gtk-enable-input-feedback-sounds=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintfull
gtk-cursor-theme-name=Adwaita


code:
# font_family Iosevka Trem
# font_family JetBrains Mono
# font_family Source Code Pro
# font_family ShureTechMono Nerd Font
# font_family AnonymicePro Nerd Font Mono
# font_family VictorMono Nerd Font
# font_family Inconsolata Nerd Font
# font_family 3270 Nerd Font

# font_family Terminess Nerd Font
font_family Terminess Nerd Font Mono

# font_family Terminus

bold_font        auto
italic_font      false
bold_italic_font auto

font_size 14

window_padding_width 6.0

background_opacity 0.9
background_blur 1
copy_on_select yes

# The color for the border of the active window
# active_border_color #00ff00

# The color for the border of inactive windows
# inactive_border_color #cccccc

tab_powerline_style angled

macos_hide_titlebar yes

# Theme Color
background #1d1f21
foreground #c5c8c6
cursor #cc6666
selection_background #c5c8c6
color0 #282a2e
color8 #373b41
color1 #a54242
color9 #cc6666
color2 #8c9440
color10 #b5bd68
color3 #de935f
color11 #f0c674
color4 #5f819d
color12 #81a2be
color5 #85678f
color13 #b294bb
color6 #5e8d87
color14 #8abeb7
color7 #707880
color15 #c5c8c6
selection_foreground #1d1f21


code:
# Recommended location for database
db_file            "~/.config/mpd/database"

# Logs to systemd journal
log_file           "syslog"

# The music directory is by default the XDG directory, uncomment to amend and choose a different directory
# music_directory    "~/Music"
music_directory    "/run/media/user/MyStor/_muz/lossless"
# music_directory    "/mnt/MyStore/_muz/lossless"

# Uncomment to refresh the database whenever files in the music_directory are changed
#auto_update "yes"

# Uncomment to enable the functionalities
playlist_directory "~/.config/mpd/playlists"
pid_file           "~/.config/mpd/pid"
state_file         "~/.config/mpd/state"
sticker_file       "~/.config/mpd/sticker.sql"

audio_output {
        type          "alsa"
        name          "ALSA sound card"
        # Optional
        #device        "iec958:CARD=Intel,DEV=0"
        #mixer_control "PCM"
}

audio_output {
        type          "pulse"
        name          "pulse audio"
        # Optional
        #device        "iec958:CARD=Intel,DEV=0"
        #mixer_control "PCM"
}


code:
no-border                               # no window title bar
msg-module                              # prepend module name to log messages
msg-color                               # color log messages on terminal
term-osd-bar                            # display a progress bar on the terminal
use-filedir-conf                        # look for additional config files in the directory of the opened file
pause                                   # no autoplay
keep-open                               # keep the player open when a file's end is reached
autofit-larger=60%x60%                  # resize window in case it's larger than W%xH% of the screen
cursor-autohide-fs-only                 # don't autohide the cursor in window mode, only fullscreen
input-media-keys=no                     # enable/disable OSX media keys
cursor-autohide=1000                    # autohide the curser after 1s
prefetch-playlist=yes
force-seekable=yes

volume-max=100
volume=50

geometry=30%

on-all-workspaces

[ytdl-desktop]
profile-desc=cond:dedicated_gpu()
ytdl-format=bestvideo[height<=?2160]+bestaudio/best

[ytdl-laptop]
profile-desc=cond:not dedicated_gpu()
ytdl-format=bestvideo[height<=?1080][fps<=?30][vcodec!=?vp9][protocol!=http_dash_segments]+bestaudio/best

[default]

pause

# воспроизведение с предыдущей позиции
save-position-on-quit

# всегда поверх остальных окон
ontop=yes


code:
-- 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://sponsor.ajay.app",

    python_path = ON_WINDOWS and "python" or "python3",

    -- Categories to fetch
    categories = "sponsor,intro,outro,interaction,selfpromo",

    -- Categories to skip automatically
    skip_categories = "sponsor",

    -- 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://blog.ajay.app/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,

    -- How long to wait between local database updates
    -- Format: "X[d,h,m]", leave blank to update on every mpv run
    auto_update_interval = "6h",

    -- 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 = false,

    -- Use sponsor times from server if they're more up to date than our local database
    server_fallback = true,

    -- Create chapters at sponsor boundaries for OSC display and manual skipping
    make_chapters = 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,

    -- Length of the sha256 prefix (3-32) when querying server, 0 to disable
    sha256_length = 4,

    -- Pattern for video id in local files, ignored if blank
    -- Recommended value for base youtube-dl is "-([%w-_]+)%.[mw][kpe][v4b]m?$"
    local_pattern = "",

    -- Legacy option, use skip_categories instead
    skip = true
}

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"
scripts_dir = mp.find_config_file("scripts")

local sponsorblock = utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.py")
local uid_path = utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.txt")
local database_file = options.local_database and utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.db") or ""
local youtube_id = nil
local ranges = {}
local init = false
local segment = {a = 0, b = 0, progress = 0, first = true}
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")
local categories = {}
local all_categories = {"sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"}
local chapter_cache = {}

for category in string.gmatch(options.skip_categories, "([^,]+)") do
    categories[category] = true
end

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 time_sort(a, b)
    if a.time == b.time then
        return string.match(a.title, "segment end")
    end
    return a.time < b.time
end

function parse_update_interval()
    local s = options.auto_update_interval
    if s == "" then return 0 end -- Interval Disabled

    local num, mod = s:match "^(%d+)([hdm])$"

    if num == nil or mod == nil then
        mp.osd_message("[sponsorblock] auto_update_interval " .. s .. " is invalid", 5)
        return nil
    end

    local time_table = {
        m = 60,
        h = 60 * 60,
        d = 60 * 60 * 24,
    }

    return num * time_table[mod]
end

function clean_chapters()
    local chapters = mp.get_property_native("chapter-list")
    local new_chapters = {}
    for _, chapter in pairs(chapters) do
        if chapter.title ~= "Preview segment start" and chapter.title ~= "Preview segment end" then
            table.insert(new_chapters, chapter)
        end
    end
    mp.set_property_native("chapter-list", new_chapters)
end

function create_chapter(chapter_title, chapter_time)
    local chapters = mp.get_property_native("chapter-list")
    local duration = mp.get_property_native("duration")
    table.insert(chapters, {title=chapter_title, time=(duration == nil or duration > chapter_time) and chapter_time or duration - .001})
    table.sort(chapters, time_sort)
    mp.set_property_native("chapter-list", chapters)
end

function process(uuid, t, new_ranges)
    start_time = tonumber(string.match(t, "[^,]+"))
    end_time = tonumber(string.sub(string.match(t, ",[^,]+"), 2))
    for o_uuid, o_t in pairs(ranges) do
        if (start_time >= o_t.start_time and start_time <= o_t.end_time) or (o_t.start_time >= start_time and o_t.start_time <= end_time) then
            new_ranges[o_uuid] = o_t
            return
        end
    end
    category = string.match(t, "[^,]+$")
    if categories[category] and end_time - start_time >= options.min_duration then
        new_ranges[uuid] = {
            start_time = start_time,
            end_time = end_time,
            category = category,
            skipped = false
        }
    end
    if options.make_chapters and not chapter_cache[uuid] then
        chapter_cache[uuid] = true
        local category_title = (category:gsub("^%l", string.upper):gsub("_", " "))
        create_chapter(category_title .. " segment start (" .. string.sub(uuid, 1, 6) .. ")", start_time)
        create_chapter(category_title .. " segment end (" .. string.sub(uuid, 1, 6) .. ")", end_time)
    end
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,
        options.categories,
        tostring(options.sha256_length)
    }
    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
    mp.msg.debug("Got: " .. string.gsub(sponsors.stdout, "[\n\r]", ""))
    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
            process(uuid, t, new_ranges)
        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()
    if options.fast_forward and options.fast_forward == true then
        speed_timer = nil
        mp.set_property("speed", 1)
    end
    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] " .. t.category .. " skipped")
                mp.set_property("time-pos", t.end_time)
            else
                mp.osd_message("[sponsorblock] skipping " .. t.category)
            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
                if speed_timer ~= nil then speed_timer:kill() end
                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()
        speed_timer = nil
        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, first = true}
    last_skip = {uuid = "", dir = nil}
    chapter_cache = {}
    local video_path = mp.get_property("path", "")
    mp.msg.debug("Path: " .. video_path)
    local video_referer = string.match(mp.get_property("http-header-fields", ""), "Referer:([^,]+)") or ""
    mp.msg.debug("Referer: " .. video_referer)

    local urls = {
        "https?://youtu%.be/([%w-_]+).*",
        "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*",
        "/watch.*[?&]v=([%w-_]+).*",
        "/embed/([%w-_]+).*"
    }
    youtube_id = nil
    for i,url in ipairs(urls) do
        youtube_id = youtube_id or string.match(video_path, url) or string.match(video_referer, url)
    end
    youtube_id = youtube_id or string.match(video_path, options.local_pattern)

    if not youtube_id or string.len(youtube_id) < 11 or (local_pattern and string.len(youtube_id) ~= 11) then return end
    youtube_id = string.sub(youtube_id, 1, 11)
    mp.msg.debug("Found YouTube ID: " .. youtube_id)
    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
    if options.skip then
        mp.observe_property("time-pos", "native", skip_ads)
    end
    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

    if file_exists(database_file) then
        local db_info = utils.file_info(database_file)
        local cur_time = os.time(os.date("*t"))
        local upd_interval = parse_update_interval()
        if upd_interval == nil or os.difftime(cur_time, db_info.mtime) < upd_interval then return end
    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
    if options.make_chapters and not segment.first then
        local start_time = math.min(segment.a, segment.b)
        local end_time = math.max(segment.a, segment.b)
        if end_time - start_time ~= 0 and end_time ~= 0 then
            clean_chapters()
            create_chapter("Preview segment start", start_time)
            create_chapter("Preview segment end", end_time)
        end
    end
    segment.first = false
end

function select_category(selected)
    for category in string.gmatch(options.categories, "([^,]+)") do
        mp.remove_key_binding("select_category_"..category)
        mp.remove_key_binding("kp_select_category_"..category)
    end
    submit_segment(selected)
end

function submit_segment(category)
    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
        segment.progress = segment.progress + 2
        local category_list = ""
        for category_id, category in pairs(all_categories) do
            local category_title = (category:gsub("^%l", string.upper):gsub("_", " "))
            category_list = category_list .. category_id .. ": " .. category_title .. "\n"
            mp.add_forced_key_binding(tostring(category_id), "select_category_"..category, function() select_category(category) end)
            mp.add_forced_key_binding("KP"..tostring(category_id), "kp_select_category_"..category, function() select_category(category) end)
        end
        mp.osd_message(string.format("[sponsorblock] press a number to select category for segment: %.2d:%.2d:%.2d to %.2d:%.2d:%.2d\n\n" .. category_list .. "\nyou can press Shift+G again for default (Sponsor) or hide this message with g", math.floor(start_time/(60*60)), math.floor(start_time/60%60), math.floor(start_time%60), math.floor(end_time/(60*60)), math.floor(end_time/60%60), math.floor(end_time%60)), 30)
    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,
            category or "sponsor"
        }
        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, first = true}
            mp.osd_message("[sponsorblock] segment submitted")
            if options.make_chapters then
                clean_chapters()
                create_chapter("Submitted segment start", start_time)
                create_chapter("Submitted segment end", end_time)
            end
        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, first = true}
        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, first = true}
        else
            mp.osd_message("[sponsorblock] segment submission failed", 5)
        end
    end
end

mp.register_event("file-loaded", file_loaded)
mp.add_key_binding("g", "set_segment", set_segment)
mp.add_key_binding("G", "submit_segment", submit_segment)
mp.add_key_binding("h", "upvote_segment", function() return vote("1") end)
mp.add_key_binding("H", "downvote_segment", function() return vote("0") end)
-- Bindings below are for backwards compatibility and could be removed at any time
mp.add_key_binding(nil, "sponsorblock_set_segment", set_segment)
mp.add_key_binding(nil, "sponsorblock_submit_segment", submit_segment)
mp.add_key_binding(nil, "sponsorblock_upvote", function() return vote("1") end)
mp.add_key_binding(nil, "sponsorblock_downvote", function() return vote("0") end)


code:
-- This is a dummy main.lua
-- required for mpv 0.33
-- do not delete


code:
import urllib.request
import urllib.parse
import hashlib
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] or not os.path.isfile(sys.argv[2])):
    sha = None
    if 3 <= int(sys.argv[6]) <= 32:
        sha = hashlib.sha256(sys.argv[4].encode()).hexdigest()[:int(sys.argv[6])]
    times = []
    try:
        response = urllib.request.urlopen(sys.argv[3] + "/api/skipSegments" + ("/" + sha + "?" if sha else "?videoID=" + sys.argv[4] + "&") + urllib.parse.urlencode([("categories", json.dumps(sys.argv[5].split(",")))]))
        segments = json.load(response)
        for segment in segments:
            if sha and sys.argv[4] != segment["videoID"]:
                continue
            if sha:
                for s in segment["segments"]:
                    times.append(str(s["segment"][0]) + "," + str(s["segment"][1]) + "," + s["UUID"] + "," + s["category"])
            else:
                times.append(str(segment["segment"][0]) + "," + str(segment["segment"][1]) + "," + segment["UUID"] + "," + segment["category"])
        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()
    times = []
    for category in sys.argv[5].split(","):
        c.execute("SELECT startTime, endTime, votes, UUID, category FROM sponsorTimes WHERE videoID = ? AND shadowHidden = 0 AND votes > -1 AND category = ?", (sys.argv[4], category))
        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"] + "," + time["category"])
    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)
        sys.exit(1)
    except ConnectionResetError:
        print("database update failed, connection reset", file=sys.stderr)
        sys.exit(1)
    except TimeoutError:
        print("database update failed, timed out", file=sys.stderr)
        sys.exit(1)
    except urllib.error.URLError:
        print("database update failed", file=sys.stderr)
        sys.exit(1)
elif sys.argv[1] == "submit":
    try:
        req = urllib.request.Request(sys.argv[3] + "/api/skipSegments", data=json.dumps({"videoID": sys.argv[4], "segments": [{"segment": [float(sys.argv[5]), float(sys.argv[6])], "category": sys.argv[9]}], "userID": uid}).encode(), headers={"Content-Type": "application/json"})
        response = urllib.request.urlopen(req)
        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