diff options
Diffstat (limited to '.bin')
| -rwxr-xr-x | .bin/cmus-consistency | 23 | ||||
| -rwxr-xr-x | .bin/cmus-find-unused | 20 | ||||
| -rwxr-xr-x | .bin/cmus-find-unused-dir | 24 | ||||
| -rwxr-xr-x | .bin/config.ini | 8 | ||||
| -rwxr-xr-x | .bin/dot-sync | 11 | ||||
| -rwxr-xr-x | .bin/dslr-webcam | 27 | ||||
| -rwxr-xr-x | .bin/dwm-start | 2 | ||||
| -rwxr-xr-x | .bin/music | 175 | ||||
| -rwxr-xr-x | .bin/python-chad | 15 | ||||
| -rwxr-xr-x | .bin/ssh-personal | 14 | ||||
| -rwxr-xr-x | .bin/synchronize-pub-dots | 5 | ||||
| -rwxr-xr-x | .bin/undock | 1 | ||||
| -rwxr-xr-x | .bin/x11-config | 3 | ||||
| -rwxr-xr-x | .bin/x11-screen | 4 | 
14 files changed, 270 insertions, 62 deletions
| diff --git a/.bin/cmus-consistency b/.bin/cmus-consistency new file mode 100755 index 0000000..dae667b --- /dev/null +++ b/.bin/cmus-consistency @@ -0,0 +1,23 @@ +#!/bin/sh + +set -xe + +music_urls_file="${HOME}/.config/music/urls" +music_path="${HOME}/music" +music_album_list_file="$(mktemp)" + +find "${music_path}" -maxdepth 1 -type d -not -path "${music_path}" -not -name ".*" \ +	| sort \ +	| xargs -I {} basename {} \ +	> "${music_album_list_file}" + +grep -v "^http" "${music_urls_file}" \ +	| grep -v "^#" \ +	| tr -s "\n" \ +	| sort \ +	| uniq \ +	| diff - "${music_album_list_file}" \ +	| grep -e "^>" -e "^<" \ +	| sort -V + +rm "${music_album_list_file}" diff --git a/.bin/cmus-find-unused b/.bin/cmus-find-unused new file mode 100755 index 0000000..2fd5f1c --- /dev/null +++ b/.bin/cmus-find-unused @@ -0,0 +1,20 @@ +#!/bin/sh +# Find tracks in no playlist + +set -xe + +cmus_path="${HOME}/.config/cmus" +music_path="${HOME}/music" +cmus_playlist_files="$(mktemp)" + +cat "${cmus_path}/playlists/"* \ +	| sort \ +	| uniq \ +	> "${cmus_playlist_files}" + +find "${music_path}" -type f -not -name ".*" \ +	| sort \ +	| uniq \ +	| comm -23 - "${cmus_playlist_files}" + +rm "${cmus_playlist_files}" diff --git a/.bin/cmus-find-unused-dir b/.bin/cmus-find-unused-dir new file mode 100755 index 0000000..189bb12 --- /dev/null +++ b/.bin/cmus-find-unused-dir @@ -0,0 +1,24 @@ +#!/bin/sh + +set -xe + +cmus_path="${HOME}/.config/cmus" +music_path="${HOME}/music" +cmus_playlist_files="$(mktemp)" + +cat "${cmus_path}/playlists/"* \ +	| xargs -d "\n" -I {} dirname {} \ +	| xargs -I {} basename {} \ +	| sort \ +	| uniq \ +	> "${cmus_playlist_files}" + +cmus-find-unused \ +	| xargs -d "\n" -I {} dirname {} \ +	| grep -v -e "^${music_path}$" -e "\." \ +	| xargs -I {} basename {} \ +	| sort \ +	| uniq \ +	| comm -23 - "${cmus_playlist_files}" + +rm "${cmus_playlist_files}" diff --git a/.bin/config.ini b/.bin/config.ini new file mode 100755 index 0000000..9f4986e --- /dev/null +++ b/.bin/config.ini @@ -0,0 +1,8 @@ +[AAAA-aaa] + +a = "a" + +[bbb] +bb +cc +dd diff --git a/.bin/dot-sync b/.bin/dot-sync index f12f878..f41b936 100755 --- a/.bin/dot-sync +++ b/.bin/dot-sync @@ -4,11 +4,11 @@ set -xe  export GIT_SSH_COMMAND="ssh -o ConnectTimeout=1 -o ConnectionAttempts=1" -yadm stash -yadm pull --rebase -yadm stash pop +yadm stash || true +yadm pull --rebase || true +yadm stash pop || true -yadm add -u +yadm add -u || true  yadm add \  	"${HOME}/.bin" \  	"${HOME}/.config/cmus/playlists" \ @@ -28,7 +28,8 @@ yadm add \  	"${HOME}/.config/yadm" \  	"${HOME}/.config/waybar" \  	"${HOME}/.config/zk" \ -	"${HOME}/.public-keys" +	"${HOME}/.public-keys" \ +	|| true  yadm push diff --git a/.bin/dslr-webcam b/.bin/dslr-webcam new file mode 100755 index 0000000..a1c9084 --- /dev/null +++ b/.bin/dslr-webcam @@ -0,0 +1,27 @@ +#!/bin/sh + +set -xe + +VIDEO_INDEX="${1:-1}" + +# gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -f v4l2 "/dev/video${VIDEO_INDEX}" + +gphoto2 --stdout --capture-movie | +	ffmpeg \ +	-threads:v 2 \ +	-threads:a 8 \ +	-filter_threads 2 \ +	-thread_queue_size 512 \ +	-hwaccel opencl \ +	-fflags nobuffer \ +	-flags low_delay \ +	-strict experimental \ +	-probesize 819200 \ +	-analyzeduration 0 \ +	-i - \ +	-vcodec rawvideo \ +	-preset ultrafast \ +	-tune zerolatency \ +	-pix_fmt yuv420p \ +	-f v4l2 "/dev/video${VIDEO_INDEX}" \ +	-framerate 30 diff --git a/.bin/dwm-start b/.bin/dwm-start index b42252a..e2dcc00 100755 --- a/.bin/dwm-start +++ b/.bin/dwm-start @@ -22,7 +22,7 @@ while true; do  	# x11 configuration  	x11-config -	. ~/.bin/x11-screen +	ag-autorandr  	# dwm  	dwm 2> ~/.dwm.log @@ -1,63 +1,37 @@  #!/usr/bin/python3 +import logging  import os  import sys -import yt_dlp -from dataclasses import dataclass - - -def _match_filter(info: dict, *, incomplete) -> str | None: -    _duration = info.get("duration") -    _duration_min = 60 - -    if _duration and int(_duration) < _duration_min: -        return "Duration too short: < _duration_min" -    return None +import yt_dlp  # type: ignore[import] +from dataclasses import dataclass -@dataclass(frozen=True) -class Collection: -    """A music collection.""" - -    title: str -    links: frozenset[str] - -    def __eq__(self, other) -> bool: -        if isinstance(other, Collection): -            return self.title == other.title -        raise NotImplementedError - +logging.basicConfig() +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) -def parse_raw_to_collections(raw_data: list[str]) -> frozenset[Collection]: -    collections: set[Collection] = set() -    _collection_data: list[str] = [] -    for index, line in enumerate(raw_data): -        if line.startswith("#"): -            continue -        elif line == "" or index + 1 == len(raw_data): -            if len(_collection_data) == 0: -                continue +def get_ytdlp_options(output_dir: str) -> dict: +    """yt_dlp download and convertion options.""" -            collections.add( -                Collection(_collection_data[0], frozenset(_collection_data[1:])) -            ) -            _collection_data.clear() -        else: -            _collection_data.append(line) +    def match_filter(info: dict, *, incomplete) -> str | None: +        duration = info.get("duration") +        duration_min = 60 -    return frozenset(collections) +        if duration is not None and int(duration) < duration_min: +            return "Duration too short: < _duration_min" +        return None -def get_ytdlp_options(output_dir: str) -> dict:      return {          "format": "bestaudio/best", -        "match_filter": _match_filter, +        "match_filter": match_filter,          "postprocessors": [              {                  "key": "FFmpegExtractAudio", -                #"preferredcodec": "m4a", +                # "preferredcodec": "m4a",              },              {                  "key": "FFmpegMetadata", @@ -71,31 +45,128 @@ def get_ytdlp_options(output_dir: str) -> dict:          "outtmpl": f"{output_dir}/%(title)s.%(ext)s",          "restrictfilenames": True,          "ignoreerrors": True, +        "writethumbnail": True,      } -def download_collection(collection: Collection, parent_dir: str) -> None: -    output_dir = os.path.join(parent_dir, collection.title) +def parse_raw_lines(lines: list[str]) -> list[list[str]]: +    """Parse collections of name + link(s) + +    (Usually stored in a text file). +    """ +    entries: list[list[str]] = list() +    entry: list[str] = list() + +    for index, line in enumerate(lines): + +        # entries are separated by an empty line. +        if line == "": +            entries.append(entry) +            entry = list() +            continue + +        entry.append(line) -    if os.path.isdir(output_dir): -        return +        # handle the last entry when reaching the end of the file. +        if index + 1 == len(lines): +            entries.append(entry) +            entry = list() -    os.makedirs(output_dir, exist_ok=True) +    return entries -    with yt_dlp.YoutubeDL(get_ytdlp_options(output_dir)) as downloader: -        downloader.download(collection.links) + +@dataclass(frozen=True) +class Link: +    """A music link.""" + +    url: str +    is_enabled: bool + + +@dataclass(frozen=True) +class Collection: +    """A music collection.""" + +    name: str +    links: tuple[Link, ...] +    is_enabled: bool + +    def __eq__(self, other) -> bool: +        if isinstance(other, Collection): +            return self.name == other.name + +        raise NotImplementedError + + +def sanitize_entry_informations( +    entry: str, indicator: str = "#" +) -> tuple[str, bool]: + +    is_comment = entry.startswith(indicator) + +    if is_comment: +        entry = entry.split(indicator, 1)[1].lstrip() + +    return entry, not is_comment + + +def create_link(entry: str) -> Link: +    url, is_enabled = sanitize_entry_informations(entry) +    return Link(url=url, is_enabled=is_enabled) + + +def create_collection(entry: list[str]) -> Collection: +    """Create a collection from a raw entry.""" +    name, is_enabled = sanitize_entry_informations(entry[0]) +    links = [create_link(_link) for _link in entry[1:]] + +    return Collection( +        name=name, +        links=tuple(links), +        is_enabled=is_enabled +    ) + + +def get_collection_dir(collection: Collection, parent_dir: str) -> str: +    return os.path.join(parent_dir, collection.name) + + +def download_collection(collection: Collection, directory: str) -> None: +    """Download a music collection to the local filesystem.""" + +    # create directory and download/convert with opinionated settings. +    os.makedirs(directory, exist_ok=True) + +    with yt_dlp.YoutubeDL(get_ytdlp_options(directory)) as downloader: +        for link in collection.links: +            if not link.is_enabled: +                logger.info(f"Skipping {collection.name}, {link}") +                continue + +            logger.info(f"Downloading {collection.name}, {link}") +            downloader.download(link.url)  def main() -> int: -    # input handling +    """Main entrypoint.""" + +    # argument handling      if len(sys.argv) != 2:          return 1      with open(sys.argv[1], "r") as file:          filedata = file.read().splitlines() -    for collection in parse_raw_to_collections(filedata): -        download_collection(collection, os.getcwd()) +    for entry in parse_raw_lines(filedata): +        collection = create_collection(entry) +        output_dir = get_collection_dir(collection, os.getcwd()) + +        if os.path.isdir(output_dir) or not collection.is_enabled: +            logger.info(f"Skipping {collection.name}") +            continue + +        logger.info(f"Handling {collection.name}") +        download_collection(collection, output_dir)      return 0 diff --git a/.bin/python-chad b/.bin/python-chad new file mode 100755 index 0000000..3fc230e --- /dev/null +++ b/.bin/python-chad @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +venv_path=$(poetry env info -p || true) + +if [ "${?}" -eq 0 ]; then +	export PATH="${venv_path}/bin:${PATH}" +fi + +set -x + +isort "${1:-.}" +black "${1:-.}" +flake8 "${1:-.}" diff --git a/.bin/ssh-personal b/.bin/ssh-personal new file mode 100755 index 0000000..e99764c --- /dev/null +++ b/.bin/ssh-personal @@ -0,0 +1,14 @@ +#!/bin/sh +# Used for work laptop. +# Add personal key and start the ssh-agent with it. + +set -xe + +ssh_personal_key="${HOME}/.ssh/id_personal" + +test -f "${ssh_personal_key}" + +eval "$(ssh-agent)" +ssh-add "${ssh_personal_key}" + +set +xe diff --git a/.bin/synchronize-pub-dots b/.bin/synchronize-pub-dots index 8c7a9c4..852a75c 100755 --- a/.bin/synchronize-pub-dots +++ b/.bin/synchronize-pub-dots @@ -2,8 +2,11 @@  set -e +# perform pre-flight checks  command -v yadm git  git rev-parse --is-inside-work-tree +[ "$(yadm rev-parse --show-toplevel)" != "$(git rev-parse --show-toplevel)" ] +  allowed_patterns="-e ^.bin  	-e ^.config/alacritty/ @@ -39,8 +42,6 @@ allowed_patterns="-e ^.bin  	-e ^.zshrc  	" -[ "$(yadm rev-parse --show-toplevel)" != "$(git rev-parse --show-toplevel)" ] -  # retrieve existing files  upstream_files=$(cd "${HOME}" && yadm ls-files)  local_files=$(find . -not -path "./.git*" -not -path ".") diff --git a/.bin/undock b/.bin/undock index c592fbf..4a9c14f 100755 --- a/.bin/undock +++ b/.bin/undock @@ -3,4 +3,5 @@  set -xe  dock +xrandr --output eDP1 --mode 1920x1080 --primary  xrandr -s 0 diff --git a/.bin/x11-config b/.bin/x11-config index 554f00c..d304547 100755 --- a/.bin/x11-config +++ b/.bin/x11-config @@ -7,7 +7,8 @@ synclient_options="TapButton1=1 \  	TapButton2=3 \  	TapButton3=2 \  	PalmDetect=1 \ -	TouchpadOff=0" +	TouchpadOff=0 \ +	VertScrollDelta=111"  # synaptic  if command -v syndaemon; then diff --git a/.bin/x11-screen b/.bin/x11-screen index 38ee006..82f8650 100755 --- a/.bin/x11-screen +++ b/.bin/x11-screen @@ -1,5 +1,7 @@  #!/bin/sh +set -x +  __hidpi() {  	xrdb -merge ~/.Xresources.hidpi @@ -25,7 +27,7 @@ if [ "${hostname}" = "ws-xps01" ]; then  	__hidpi  fi -if [ "${hostname}" = "work-01" ]; then +if [ "${hostname}" = "ws-work01" ]; then  	xrandr --output eDP1 --mode 1920x1080  	xrandr --output DP3 --off  	xrandr --addmode DP3 1920x1080 |