diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5029e31 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.sh] +indent_style = space +indent_size = 4 +switch_case_indent = true diff --git a/.github/workflows/inverse-lobster.yml b/.github/workflows/inverse-lobster.yml index b3e6059..47f4171 100644 --- a/.github/workflows/inverse-lobster.yml +++ b/.github/workflows/inverse-lobster.yml @@ -1,11 +1,11 @@ # Added due to required but conditional checks # Read more: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/troubleshooting-required-status-checks#handling-skipped-but-required-checks -name: 'lobster checks' +name: "lobster checks" on: pull_request: paths-ignore: - - "**lobster" + - "lobster.sh" jobs: sh-checker: name: Shellcheck + Shfmt diff --git a/.github/workflows/lobster.yml b/.github/workflows/lobster.yml index 339fd60..4f59d4e 100644 --- a/.github/workflows/lobster.yml +++ b/.github/workflows/lobster.yml @@ -1,28 +1,28 @@ -name: 'lobster checks' +name: "lobster checks" on: push: branches: - main pull_request: - paths: - - "**lobster" + paths: + - "lobster.sh" jobs: sh-checker: name: Shellcheck + Shfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Run the sh-checker - uses: luizm/action-sh-checker@master - env: - SHELLCHECK_OPTS: -s sh -o all -e 2250 - SHFMT_OPTS: -i 4 -ci -d - + - uses: actions/checkout@v3 + - name: Run the sh-checker + uses: luizm/action-sh-checker@master + env: + SHELLCHECK_OPTS: -s sh -o all -e 2250 + SHFMT_OPTS: -i 4 -ci -d + check-exec: name: Executable Bit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: test exec bit - run: test -x "./lobster.sh" + - uses: actions/checkout@v3 + - name: test exec bit + run: test -x "./lobster.sh" diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 1f34949..8a3870c 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -1,16 +1,15 @@ -name: 'lobster checks' +name: "lobster checks" on: pull_request: - paths: - - "**lobster" - + paths: + - "lobster.sh" jobs: version-bump: name: Version Bump runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: check version bump - run: git diff origin/master | grep LOBSTER_VERSION + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: check version bump + run: git diff origin/main | grep LOBSTER_VERSION diff --git a/README.md b/README.md index 704c53a..668ea61 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,12 @@ https://github.com/justchokingaround/lobster/assets/44473782/d597335c-42a9-4e45- - [`-c` / `--continue`](#-c----continue-argument) - [`--clear-history / --delete-history`](#--clear-history----delete-history-argument) - [`-d` / `--download`](#-d----download-path-argument) + - [`--discord` / `--discord-presence` / `--rpc` / `--presence`](#--discord----discord-presence----rpc----presence-argument) - [`-e` / `--edit`](#-e----edit-argument) - [`-i` / `--image-preview`](#-i----image-preview-argument) - [`-j` / `--json`](#-j----json-argument) - [`-l` / `--language`](#-l----language-language-argument) - - [`--rofi` / `--dmenu` / `--external-menu`](#--rofi----dmenu----external-menu-argument) + - [`--rofi` / `--external-menu`](#--rofi----external-menu-argument) - [`-p` / `--provider`](#-p----provider-provider-argument) - [`-q` / `--quality`](#-q----quality-quality-argument) - [`--quiet`](#--quiet-argument) @@ -45,7 +46,8 @@ https://github.com/justchokingaround/lobster/assets/44473782/d597335c-42a9-4e45- #### Arch -Note: it is recommended to use the `lobster-git` package, as it is more up to date, and as the project is currently being actively maintained +Note: it is recommended to use the `lobster-git` package, as it is more up to +date, and as the project is currently being actively maintained ```sh paru -S lobster-git @@ -85,7 +87,8 @@ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/prebuilt-mpr-archive-keyring sudo apt update && sudo apt install mist ``` -During this step when prompted to `Review files for 'lobster-git'? [Y/n]`, write `n` and enter. +During this step when prompted to `Review files for 'lobster-git'? [Y/n]`, write +`n` and enter. ```sh mist update && mist install lobster-git @@ -102,27 +105,33 @@ sudo chmod +x /usr/local/bin/lobster Add this to you flake.nix -``` nix +```nix inputs.lobster.url = "github:justchokingaround/lobster"; ``` Add this to you configuration.nix -``` nix +```nix environment.systemPackages = [ inputs.lobster.packages..lobster ]; ``` ##### Or for run the script once use + ```sh nix run github:justchokingaround/lobster#lobster ``` ##### Nixos (Flake) update -When encoutering errors first run the nix flake update command in the cloned project and second add new/missing [dependencies](#dependencies) to the default.nix file. Use the [nixos package search](https://search.nixos.org/packages) to find the correct name. -``` nix +When encoutering errors first run the nix flake update command in the cloned +project and second add new/missing [dependencies](#dependencies) to the +default.nix file. Use the +[nixos package search](https://search.nixos.org/packages) to find the correct +name. + +```nix nix flake update ``` @@ -138,11 +147,16 @@ chmod +x "$(brew --prefix)"/bin/lobster
Windows installation instructions -* This guide covers how to install and use lobster with the windows terminal, you could also use a different terminal emulator, that supports fzf, like for example wezterm -* Note that the git bash terminal does *not* have proper fzf support +- This guide covers how to install and use lobster with the windows terminal, + you could also use a different terminal emulator, that supports fzf, like for + example wezterm +- Note that the git bash terminal does _not_ have proper fzf support + 1. Install scoop -Open a PowerShell terminal https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.2#msi (version 5.1 or later) and run: +Open a PowerShell terminal +https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.2#msi +(version 5.1 or later) and run: ```ps Set-ExecutionPolicy RemoteSigned -Scope CurrentUser @@ -155,30 +169,35 @@ irm get.scoop.sh | iex scoop bucket add extras scoop install git mpv fzf ``` -3. Install windows terminal (you don't need to have a microsoft account for that) - https://learn.microsoft.com/en-us/windows/terminal/install -4. Install git bash (select the option to add it to the windows terminal during installation) - https://git-scm.com/download/win +3. Install windows terminal (you don't need to have a microsoft account for + that) https://learn.microsoft.com/en-us/windows/terminal/install + +4. Install git bash (select the option to add it to the windows terminal during + installation) https://git-scm.com/download/win (The next steps are to be done in the windows terminal, in a bash shell) 5. Download the script file to the current directory + ```sh curl -O "https://raw.githubusercontent.com/justchokingaround/lobster/main/lobster.sh" ``` 6. Give it executable permissions + ```sh chmod +x lobster.sh ``` 7. Copy the script to path + ```sh cp lobster.sh /usr/bin/lobster ``` 8. Use lobster + ```sh lobster or lobster [movie/tv show] ``` @@ -196,24 +215,24 @@ Options: Continue watching from current history -d, --download [path] Downloads movie or episode that is selected (if no path is provided, it defaults to the current directory) + --discord, --discord-presence, --rpc, --presence + Enables discord rich presence (beta feature, but should work fine on Linux) -e, --edit Edit config file using an editor defined with lobster_editor in the config (\$EDITOR by default) -h, --help Show this help message and exit -i, --image-preview - Shows image previews during media selection (requires ueberzugpp to be installed to work with fzf) + Shows image previews during media selection (requires chafa, you can optionally use ueberzugpp) -j, --json Outputs the json containing video links, subtitle links, referrers etc. to stdout -l, --language [language] Specify the subtitle language (if no language is provided, it defaults to english) - --rofi, --dmenu, --external-menu + --rofi, --external-menu Use rofi instead of fzf -p, --provider - Specify the provider to watch from (if no provider is provided, it defaults to UpCloud) (currently supported: Upcloud, Vidcloud) + Specify the provider to watch from (if no provider is provided, it defaults to Vidcloud) (currently supported: Vidcloud, UpCloud) -q, --quality Specify the video quality (if no quality is provided, it defaults to 1080) - --quiet - Suppress the output from mpv when playing a video -r, --recent [movies|tv] Lets you select from the most recent movies or tv shows (if no argument is provided, it defaults to movies) -s, --syncplay @@ -235,25 +254,33 @@ Options: lobster -i a silent voice --rofi lobster -l spanish -q 720 fight club -i -d lobster -l spanish blade runner --json - ``` ### `-c` / `--continue` argument -This feature is disabled by default because it relies on history, to enable it, you need add the following line to the `lobster_config.txt` file: +This feature is disabled by default because it relies on history, to enable it, +you need add the following line to the `lobster_config.sh` file: ```sh -history=1 +history=true ``` -In a similar fashion to how saving your position when you watch videos on YouTube or Netflix works, lobster has history support and saves the last minute you watched for a Movie or TV Show episode. To use this feature, simply watch a Movie or an Episode from a TV Show, and after you quit mpv the history will be automatically updated. The next time you want to resume from the last position watched, you can just run +In a similar fashion to how saving your position when you watch videos on +YouTube or Netflix works, lobster has history support and saves the last minute +you watched for a Movie or TV Show episode. To use this feature, simply watch a +Movie or an Episode from a TV Show, and after you quit mpv the history will be +automatically updated. The next time you want to resume from the last position +watched, you can just run ```sh lobster -c ``` -which will prompt you to chose which of the saved Movies/TV Shows you'd like to resume from. -Upon the completion of a movie or an episode, the corresponding entry is either deleted (in case of a movie, or the last episode of a show), or it is updated to the next available episode (if it's the last episode of a season, it will update to the first episode of the next season). +which will prompt you to chose which of the saved Movies/TV Shows you'd like to +resume from. Upon the completion of a movie or an episode, the corresponding +entry is either deleted (in case of a movie, or the last episode of a show), or +it is updated to the next available episode (if it's the last episode of a +season, it will update to the first episode of the next season).
Showcase @@ -269,7 +296,8 @@ Upon the completion of a movie or an episode, the corresponding entry is either #### Please note: - The history file can be found at `~/.local/share/lobster/lobster_history.txt` -- A movie or TV show episode is automatically marked as completed/updated after the user watches more than 90% of its content +- A movie or TV show episode is automatically marked as completed/updated after + the user watches more than 90% of its content ### --`clear-history` / `--delete-history` argument @@ -277,7 +305,11 @@ This argument allows you to delete the history file ### `-d` / `--download` `` argument -This option lets you use lobster as you normally would, with the exception that instead of playing the video in your player of choice, it will instead download the video. If no path is specified when passing this argument, then it will download to the current working directory, as an example, it would look like this: +This option lets you use lobster as you normally would, with the exception that +instead of playing the video in your player of choice, it will instead download +the video. If no path is specified when passing this argument, then it will +download to the current working directory, as an example, it would look like +this: ```sh lobster -d '.' rick and morty @@ -289,8 +321,9 @@ or lobster rick and morty -d ``` -If you want to specify a path to which you would like to download the video, you can do so by passing an additional parameter to the `-d` or `--download` argument, for instance: -using a full path: +If you want to specify a path to which you would like to download the video, you +can do so by passing an additional parameter to the `-d` or `--download` +argument, for instance: using a full path: ```sh lobster -d "/home/chomsky/tv_shows/rick_and_morty/" rick and morty @@ -302,17 +335,38 @@ or using a relative path: lobster -d "../rick_and_morty/" rick and morty ``` +### `--discord` / `--discord-presence` / `--rpc` / `--presence` argument + +#### Note: beta feature + +By passing this argument you make use of discord rich presence so you can let +your friends know what you are watching. + +This argument requires BSD netcat to be installed. + +On Arch Linux you can install it using either pacman or your aur helper of +choice with: + +```sh +paru -S openbsd-netcat +``` + ### `-e` / `--edit` argument -By passing this argument you can edit the config file using an editor of your choice. By default it will use the editor defined in the `lobster_config.txt` file, but if you don't have one defined, it will use the `$EDITOR` environment variable (if it's not set, it will default to `vim`). +By passing this argument you can edit the config file using an editor of your +choice. By default it will use the editor defined in the `lobster_config.sh` +file, but if you don't have one defined, it will use the `$EDITOR` environment +variable (if it's not set, it will default to `vim`). ### `-i` / `--image-preview` argument By passing this argument you can see image previews when selecting an entry. -For `rofi` it will work out of the box, if you have icons enabled in your default configuration. +For `rofi` it will work out of the box, if you have icons enabled in your +default configuration. -Example using my custom rofi configuration (to customize how your rofi image preview looks, please check the [configuration](#configuration) section) +Example using my custom rofi configuration (to customize how your rofi image +preview looks, please check the [configuration](#configuration) section)
Showcase @@ -321,7 +375,9 @@ Example using my custom rofi configuration (to customize how your rofi image pre
-For `fzf` you will need to install [ueberzugpp](https://github.com/jstkdng/ueberzugpp/). +For `fzf` you will need to either install +[chafa](https://github.com/hpjansson/chafa/) or +[ueberzugpp](https://github.com/jstkdng/ueberzugpp/).
Showcase @@ -331,10 +387,16 @@ For `fzf` you will need to install [ueberzugpp](https://github.com/jstkdng/ueber
-Installation instructions for ueberzugpp +Installation instructions for chafa/ueberzugpp On Arch Linux you can install it using your aur helper of choice with: +```sh +paru -S chafa +``` + +or + ```sh paru -S ueberzugpp ``` @@ -347,17 +409,28 @@ brew install ./ueberzugpp rm ueberzugpp ``` -In other cases, you can build it from [source](https://github.com/jstkdng/ueberzugpp/#build-from-source). +In other cases, you can build it from +[source](https://github.com/jstkdng/ueberzugpp/#build-from-source). + +Using ueberzugpp is disabled by default in favor of chafa, to enable it, you +need add the following line to the `lobster_config.sh` file: + +```sh +use_ueberzugpp=true +```
### `-j` / `--json` argument -By passing this argument, you can output the json for the currently selected media to stdout, with the decrypted video link. +By passing this argument, you can output the json for the currently selected +media to stdout, with the decrypted video link. ### `-l` / `--language` `` argument -By passing this argument, you can specify your preferred language for the subtitles of a video. If no parameter is specified, it will default to `english`. +By passing this argument, you can specify your preferred language for the +subtitles of a video. If no parameter is specified, it will default to +`english`. Example use case: @@ -371,11 +444,15 @@ This is also valid, and will use english as the defined subtitles language: lobster -l weathering with you ``` -### `--rofi` / `--dmenu` / `--external-menu` argument +### `--rofi` / `--external-menu` argument -By passing this argument, you can use rofi instead of fzf to interact with the lobster script. +By passing this argument, you can use rofi instead of fzf to interact with the +lobster script. -This is the recommended way to use lobster, and is a core philosophy of this script. My use case is that I have a keybind in my WM configuration that calls lobster, that way I can watch Movies and TV Shows without ever even opening the terminal. +This is the recommended way to use lobster, and is a core philosophy of this +script. My use case is that I have a keybind in my WM configuration that calls +lobster, that way I can watch Movies and TV Shows without ever even opening the +terminal. Here is an example of that looks like (without image preview): @@ -388,7 +465,9 @@ Here is an example of that looks like (without image preview): ### `-p` / `--provider` `` argument -By passing this argument, you can specify a preferred provider. The script currently supports the following providers: `UpCloud`, `Vidcloud`. If you don't pass any provider in the parameters, it will default to `UpCloud`. +By passing this argument, you can specify a preferred provider. The script +currently supports the following providers: `UpCloud`, `Vidcloud`. If you don't +pass any provider in the parameters, it will default to `UpCloud`. Example use case: @@ -404,7 +483,9 @@ lobster -p shawshank redemption ### `-q` / `--quality` `` argument -By passing this argument, you can specify a preferred quality for the video (if those are present in the source). If you don't pass any quality in the parameters, it will default to `1080`. +By passing this argument, you can specify a preferred quality for the video (if +those are present in the source). If you don't pass any quality in the +parameters, it will default to `1080`. Example use case: @@ -418,19 +499,12 @@ This is also valid, but will use `1080` instead: lobster the godfather -q ``` -### `--quiet` argument - -By passing this argument, you can suppress the output of mpv, when playing a video. - -Example use case: - -```sh -lobster --quiet fight club -``` - ### `-r` / `--recent` `` argument -By passing this argument, you can see watch most recently released movies and TV shows. You can specify if you want to see movies or TV shows by passing the `tv` or `movie` parameter. If you don't pass any parameter, it will default to `movie`. +By passing this argument, you can see watch most recently released movies and TV +shows. You can specify if you want to see movies or TV shows by passing the `tv` +or `movie` parameter. If you don't pass any parameter, it will default to +`movie`. Example use case: @@ -446,7 +520,9 @@ lobster -r ### `-s` / `--syncplay` argument -By passing this argument, you can use [syncplay](https://syncplay.pl/) to watch videos with your friends. This will only work if you have syncplay installed and configured. +By passing this argument, you can use [syncplay](https://syncplay.pl/) to watch +videos with your friends. This will only work if you have syncplay installed and +configured. ### `-t` / `--trending` argument @@ -466,33 +542,47 @@ sudo lobster -u ### `-v` / `-V` / `--version` argument -By passing this argument, you can see the current version of the script. This is useful if you want to check if you have the latest version installed. +By passing this argument, you can see the current version of the script. This is +useful if you want to check if you have the latest version installed. ### `-x` / `--debug` argument -By passing this argument, you can see the debug output of the script. This will redirect all the stderr output to stdout, printing it to the terminal, while also saving it to a log file: `/tmp/lobter.log` +By passing this argument, you can see the debug output of the script. This will +redirect all the stderr output to stdout, printing it to the terminal, while +also saving it to a log file: `/tmp/lobter.log` -Note: fzf prints the finder to stderr, so this will also be redirected to stdout, and by extension printed to the terminal and saved to the log file. +Note: fzf prints the finder to stderr, so this will also be redirected to +stdout, and by extension printed to the terminal and saved to the log file. ## Configuration -Please refer to the [wiki](https://github.com/justchokingaround/lobster/wiki/Configuration) for information on how to configure the script using the config file. +Please refer to the +[wiki](https://github.com/justchokingaround/lobster/wiki/Configuration) for +information on how to configure the script using the config file. ## Contributing -All contributions are welcome, and I will to review them as soon as possible. If you want to contribute, please follow the following recommendations: +All contributions are welcome, and I will to review them as soon as possible. If +you want to contribute, please follow the following recommendations: - All help is appreciated, even if it's just a typo fix, or a small improvement -- You do not need to be a programmer to contribute, you can also help by opening issues, or by testing the script and reporting bugs -- You do not need to be very experienced with shell scripting to contribute, I will gladly help you with any questions you might have, and I will also review your code -- If you are unsure about something, please open an issue first, start a discussion or message me personally +- You do not need to be a programmer to contribute, you can also help by opening + issues, or by testing the script and reporting bugs +- You do not need to be very experienced with shell scripting to contribute, I + will gladly help you with any questions you might have, and I will also review + your code +- If you are unsure about something, please open an issue first, start a + discussion or message me personally - Please make sure that your code is POSIX compliant (no bashisms) - Please make sure that your code passes `shellcheck` - Please use `shfmt` to format your code -- If you are adding a new feature, please make sure that it is configurable (either through the config file and/or through command line arguments) -- I recommend reading the philosophy section of the README, to get a better understanding of the project (TODO) +- If you are adding a new feature, please make sure that it is configurable + (either through the config file and/or through command line arguments) +- I recommend reading the philosophy section of the README, to get a better + understanding of the project (TODO) -You can find the current roadmap here, which contains TODOs and the current progress of the project: +You can find the current roadmap here, which contains TODOs and the current +progress of the project: https://github.com/users/justchokingaround/projects/2/views/1?query=is%3Aopen+sort%3Aupdated-desc ## Dependencies @@ -502,6 +592,7 @@ https://github.com/users/justchokingaround/projects/2/views/1?query=is%3Aopen+so - grep - sed - patch +- awk - mpv - html-xml-utils (for fixing html encoded characters) (optional) - rofi (external menu) diff --git a/default.nix b/default.nix index 5f38c16..8be2b73 100644 --- a/default.nix +++ b/default.nix @@ -16,7 +16,7 @@ }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "lobster"; - version = "4.1.1"; + version = "4.3.0"; src = builtins.path { name = "${finalAttrs.pname}-${finalAttrs.version}"; @@ -71,3 +71,4 @@ stdenvNoCC.mkDerivation (finalAttrs: { sourceProvenance = [lib.sourceTypes.fromSource]; }; }) + diff --git a/examples/lobster_config.txt b/examples/lobster_config.txt deleted file mode 100644 index 16aed3c..0000000 --- a/examples/lobster_config.txt +++ /dev/null @@ -1,22 +0,0 @@ -# This is an example configuration file for lobster. This configuration includes all of the defaults, which you can change to you likings. The script will behave the exact same if you remove all of the values present here. - -lobster_editor=${VISUAL:-${EDITOR:-vim}} -player=mpv -download_dir="$PWD" -provider="Vidcloud" -history=0 -subs_language="english" -histfile="$HOME/.local/share/lobster/lobster_history.txt" -use_external_menu=0 -image_preview=0 -debug=0 -quiet_output=0 -preview_window_size=50% -ueberzug_x=$(($(tput cols) - 70)) -ueberzug_y=$(($(tput lines) / 10)) -ueberzug_max_width=100 -ueberzug_max_height=100 - -download_video() { - ffmpeg -loglevel error -stats -i "$1" -c copy "$3/$2".mp4 -} diff --git a/flake.nix b/flake.nix index b5e89d0..8ddb45b 100644 --- a/flake.nix +++ b/flake.nix @@ -26,3 +26,4 @@ }); }; } + diff --git a/lobster.sh b/lobster.sh index d1852dc..77b78f9 100755 --- a/lobster.sh +++ b/lobster.sh @@ -1,61 +1,76 @@ #!/usr/bin/env sh -LOBSTER_VERSION="4.2.8" +LOBSTER_VERSION="4.3.0" -config_file="$HOME/.config/lobster/lobster_config.txt" +### General Variables ### +config_file="$HOME/.config/lobster/lobster_config.sh" lobster_editor=${VISUAL:-${EDITOR}} - -if [ "$1" = "--edit" ] || [ "$1" = "-e" ]; then - if [ -f "$config_file" ]; then - #shellcheck disable=1090 - . "${config_file}" - [ -z "$lobster_editor" ] && lobster_editor="vim" - "$lobster_editor" "$config_file" - else - printf "No configuration file found. Would you like to generate a default one? [y/N] " && read -r generate - case "$generate" in - "Yes" | "yes" | "y" | "Y") - [ ! -d "$HOME/.config/lobster" ] && mkdir -p "$HOME/.config/lobster" - printf "Getting the latest example config from github...\n" - curl -s "https://raw.githubusercontent.com/justchokingaround/lobster/main/examples/lobster_config.txt" -o "$config_file" - printf "New config generated!\n" - #shellcheck disable=1090 - . "${config_file}" - [ -z "$lobster_editor" ] && lobster_editor="vim" - "$lobster_editor" "$config_file" - ;; - *) exit 0 ;; - esac +tmp_dir="/tmp/lobster" && mkdir -p "$tmp_dir" +lobster_socket="/tmp/lobster.sock" # Used by mpv (check the play_video function) +applications="$HOME/.local/share/applications/lobster" # Used for external menus (for now just rofi) +images_cache_dir="/tmp/lobster/lobster-images" # Used for storing downloaded images of movie covers + +### Notifications ### +command -v notify-send >/dev/null 2>&1 && notify="true" || notify="false" # check if notify-send is installed +# send_notification [message] [timeout] [icon] [title] +send_notification() { + [ "$json_output" = "true" ] && return + if [ "$use_external_menu" = "false" ] || [ -z "$use_external_menu" ]; then + [ -z "$4" ] && printf "\33[2K\r\033[1;34m%s\n\033[0m" "$1" && return + [ -n "$4" ] && printf "\33[2K\r\033[1;34m%s - %s\n\033[0m" "$1" "$4" && return fi - exit 0 -fi + [ -z "$2" ] && timeout=3000 || timeout="$2" # default timeout is 3 seconds + if [ "$notify" = "true" ]; then + [ -z "$3" ] && notify-send "$1" "$4" -t "$timeout" -h string:x-dunst-stack-tag:vol # the -h string:x-dunst-stack-tag:vol is used for overriding previous notifications + [ -n "$3" ] && notify-send "$1" "$4" -t "$timeout" -i "$3" -h string:x-dunst-stack-tag:vol + fi +} -if [ "$1" = "--clear-history" ] || [ "$1" = "--delete-history" ]; then - printf "" - while true; do - printf "This will delete your lobster history. Are you sure? [Y/n] " - read -r choice - case $choice in - [Yy]* | "") - #shellcheck disable=1090 - [ -f "$config_file" ] && . "$config_file" - [ -z "$histfile" ] && histfile="$HOME/.local/share/lobster/lobster_history.txt" - rm "$histfile" - echo "History deleted." - exit 0 - ;; - [Nn]*) - return 1 - ;; - *) echo "Please answer yes or no." ;; - esac - done +### HTML Decoding ### +command -v "hxunent" >/dev/null 2>&1 && hxunent="hxunent" || hxunent="tee /dev/null" # use hxunent if installed, else do nothing + +### Discord Rich Presence Variables ### +# Note: experimental feature +presence_client_id="1239340948048187472" # Discord Client ID +# shellcheck disable=SC2154 +discord_ipc="${XDG_RUNTIME_DIR}/discord-ipc-0" # Discord IPC Socket (Could also be discord-ipc-1 if using arRPC afaik) +handshook="$tmp_dir/handshook" # Indicates if the RPC handshake has been done +ipclog="$tmp_dir/ipclog" # Logs the RPC events +presence="$tmp_dir/presence" # Used by the rich presence function +small_image="https://www.pngarts.com/files/9/Juvenile-American-Lobster-PNG-Transparent-Image.png" + +### OS Specific Variables ### +separator=':' # default value +path_thing="\\" # default value +sed='sed' # default value +# shellcheck disable=SC2249 +case "$(uname -s)" in + MINGW* | *Msys) separator=';' && path_thing='' ;; + *arwin) sed="gsed" ;; +esac + +# Checks if any of the provided arguments are -e or --edit +# If so, it will edit the config file +# This was added for pure convenience (for me) +if printf "%s" "$*" | grep -qE "\-\-edit|\-e" 2>/dev/null; then + #shellcheck disable=1090 + . "${config_file}" + [ -z "$lobster_editor" ] && lobster_editor="nano" + "$lobster_editor" "$config_file" + exit 0 fi +### Cleanup Functions ### +rpc_cleanup() { + pkill -f "nc -U $discord_ipc" >/dev/null + pkill -f "tail -f $presence" >/dev/null + rm "$handshook" "$ipclog" "$presence" >/dev/null +} cleanup() { - [ "$debug" != 1 ] && rm -rf /tmp/lobster/ - [ "$remove_tmp_lobster" = 1 ] && rm -rf /tmp/lobster/ - if [ "$image_preview" = "1" ] && [ "$use_external_menu" = "0" ]; then + [ "$debug" != "true" ] && rm -rf /tmp/lobster/ + [ "$remove_tmp_lobster" = "true" ] && rm -rf /tmp/lobster/ + + if [ "$image_preview" = "true" ] && [ "$use_external_menu" = "false" ] && [ "$use_ueberzugpp" = "true" ]; then killall ueberzugpp 2>/dev/null rm -f /tmp/ueberzugpp-* fi @@ -63,143 +78,35 @@ cleanup() { } trap cleanup EXIT INT TERM -{ - applications="$HOME/.local/share/applications/lobster" - images_cache_dir="/tmp/lobster/lobster-images" - tmp_position="/tmp/lobster_position" - case "$(uname -s)" in - MINGW* | *Msys) separator=';' && path_thing='' && sed="sed" ;; - *arwin) separator=':' && path_thing="\\" && sed="gsed" ;; - *) separator=':' && path_thing="\\" && sed="sed" ;; - esac - - command -v notify-send >/dev/null 2>&1 && notify="true" || notify="false" - send_notification() { - [ "$json_output" = "1" ] && return - if [ "$use_external_menu" = "0" ] || [ "$use_external_menu" = "" ]; then - [ -z "$4" ] && printf "\33[2K\r\033[1;34m%s\n\033[0m" "$1" && return - [ -n "$4" ] && printf "\33[2K\r\033[1;34m%s - %s\n\033[0m" "$1" "$4" && return - fi - [ -z "$2" ] && timeout=3000 || timeout="$2" - if [ "$notify" = "true" ]; then - [ -z "$3" ] && notify-send "$1" "$4" -t "$timeout" -h string:x-dunst-stack-tag:vol - [ -n "$3" ] && notify-send "$1" "$4" -t "$timeout" -i "$3" -h string:x-dunst-stack-tag:vol - fi - } - if command -v "hxunent" >/dev/null; then - hxunent="hxunent" - else - hxunent="tee /dev/null" - fi - dep_ch() { - for dep; do - command -v "$dep" >/dev/null || send_notification "Program \"$dep\" not found. Please install it." - command -v "$dep" >/dev/null || exit 1 - done - } - dep_ch "grep" "$sed" "curl" "fzf" || true - if [ "$use_external_menu" = "1" ]; then - dep_ch "rofi" || true - fi - - configuration() { - [ -n "$XDG_CONFIG_HOME" ] && config_dir="$XDG_CONFIG_HOME/lobster" || config_dir="$HOME/.config/lobster" - [ -n "$XDG_DATA_HOME" ] && data_dir="$XDG_DATA_HOME/lobster" || data_dir="$HOME/.local/share/lobster" - [ ! -d "$config_dir" ] && mkdir -p "$config_dir" - [ ! -d "$data_dir" ] && mkdir -p "$data_dir" - #shellcheck disable=1090 - [ -f "$config_file" ] && . "${config_file}" - [ -z "$base" ] && base="flixhq.to" - [ -z "$player" ] && player="mpv" - [ -z "$download_dir" ] && download_dir="$PWD" - [ -z "$provider" ] && provider="Vidcloud" - [ -z "$history" ] && history=0 - [ -z "$subs_language" ] && subs_language="english" - subs_language="$(printf "%s" "$subs_language" | cut -c2-)" - [ -z "$histfile" ] && histfile="$data_dir/lobster_history.txt" && mkdir -p "$(dirname "$histfile")" - [ -z "$use_external_menu" ] && use_external_menu="0" - [ -z "$image_preview" ] && image_preview="0" - [ -z "$debug" ] && debug=0 - [ -z "$preview_window_size" ] && preview_window_size=up:60%:wrap - [ -z "$ueberzug_x" ] && ueberzug_x=10 - [ -z "$ueberzug_y" ] && ueberzug_y=3 - [ -z "$ueberzug_max_width" ] && ueberzug_max_width=$(($(tput lines) / 2)) - [ -z "$ueberzug_max_height" ] && ueberzug_max_height=$(($(tput lines) / 2)) - [ -z "$remove_tmp_lobster" ] && remove_tmp_lobster=1 - [ -z "$json_output" ] && json_output=0 - } - - generate_desktop() { - cat </dev/null; then + send_notification "Program \"$dep\" not found. Please install it." + exit 1 + fi + done +} + +### Default Configuration ### +# this function is ran after the user's config file is "checked" (source'd) +configuration() { + [ -n "$XDG_CONFIG_HOME" ] && config_dir="$XDG_CONFIG_HOME/lobster" || config_dir="$HOME/.config/lobster" + [ -n "$XDG_DATA_HOME" ] && data_dir="$XDG_DATA_HOME/lobster" || data_dir="$HOME/.local/share/lobster" + [ ! -d "$config_dir" ] && mkdir -p "$config_dir" + [ ! -d "$data_dir" ] && mkdir -p "$data_dir" + #shellcheck disable=1090 + [ -f "$config_file" ] && . "${config_file}" # source the user's config file + [ -z "$base" ] && base="flixhq.to" + [ -z "$player" ] && player="mpv" + [ -z "$download_dir" ] && download_dir="$PWD" + [ -z "$provider" ] && provider="Vidcloud" + [ -z "$subs_language" ] && subs_language="english" + subs_language="$(printf "%s" "$subs_language" | cut -c2-)" + [ -z "$histfile" ] && histfile="$data_dir/lobster_history.txt" && mkdir -p "$(dirname "$histfile")" + [ -z "$history" ] && history=false + [ -z "$use_external_menu" ] && use_external_menu="false" + [ -z "$image_preview" ] && image_preview="false" + [ -z "$debug" ] && debug="false" + [ -z "$preview_window_size" ] && preview_window_size=up:60%:wrap + if [ -z "$use_ueberzugpp" ]; then + use_ueberzugpp="false" + elif [ "$use_ueberzugpp" = "true" ]; then + [ -z "$ueberzug_x" ] && ueberzug_x=10 + [ -z "$ueberzug_y" ] && ueberzug_y=3 + [ -z "$ueberzug_max_width" ] && ueberzug_max_width=$(($(tput lines) / 2)) + [ -z "$ueberzug_max_height" ] && ueberzug_max_height=$(($(tput lines) / 2)) + fi + [ -z "$chafa_dims" ] && chafa_dims=30x40 + [ -z "$remove_tmp_lobster" ] && remove_tmp_lobster="true" + [ -z "$json_output" ] && json_output="false" + [ -z "$discord_presence" ] && discord_presence="false" + case "$(uname -s)" in + MINGW* | *Msys) + if [ -z "$watchlater_dir" ]; then + # shellcheck disable=SC2154 + case "$(command -v "$player")" in + *scoop*) watchlater_dir="$HOMEPATH/scoop/apps/mpv/current/portable_config/watch_later/" ;; + *) watchlater_dir="$LOCALAPPDATA/mpv/watch_later" ;; + esac + fi + ;; + *) [ -z "$watchlater_dir" ] && watchlater_dir="$tmp_dir/watchlater" && mkdir -p "$watchlater_dir" ;; + esac +} + +# The reason I use additional file descriptors is because of the use of tee +# which when piped into would hijack the terminal, which was unwanted behavior +# since there are SSH use cases for mpv and since I wanted to have a logging mechanism +exec 3>&1 4>&2 1>/tmp/lobster.log 2>&1 +{ + # check that the necessary programs are installed + dep_ch "grep" "$sed" "curl" "fzf" || true + if [ "$use_external_menu" = "true" ]; then + dep_ch "rofi" || true + fi + if [ "$player" = "mpv" ]; then + dep_ch "awk" "nc" || true + fi + + ### Launchers stuff (rofi, fzf, etc.) ### + generate_desktop() { + cat <([^<]*).*@\1\t\3\t\2\t\4 [\5]@p") + [ -z "$response" ] && send_notification "Error" "1000" "" "No results found" && exit 1 + } + choose_episode() { + if [ -z "$season_id" ]; then + tmp_season_id=$(curl -s "https://${base}/ajax/v2/tv/seasons/${media_id}" | $sed -nE "s@.*href=\".*-([0-9]*)\">(.*)@\2\t\1@p" | launcher "Select a season: " "1") + [ -z "$tmp_season_id" ] && exit 1 + season_title=$(printf "%s" "$tmp_season_id" | cut -f1) + season_id=$(printf "%s" "$tmp_season_id" | cut -f2) + tmp_ep_id=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | + $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent | launcher "Select an episode: " "1") + [ -z "$tmp_ep_id" ] && exit 1 + fi + [ -z "$episode_title" ] && episode_title=$(printf "%s" "$tmp_ep_id" | cut -f1) + [ -z "$data_id" ] && data_id=$(printf "%s" "$tmp_ep_id" | cut -f2) + episode_id=$(curl -s "https://${base}/ajax/v2/episode/servers/${data_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | + $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\".*@\1\t\2@p" | grep "$provider" | cut -f1) + } + next_episode_exists() { + episodes_list=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | + $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent) + next_episode=$(printf "%s" "$episodes_list" | $sed -n "/$data_id/{n;p;}") + [ -n "$next_episode" ] && return + tmp_season_id=$(curl -s "https://${base}/ajax/v2/tv/seasons/${media_id}" | $sed -n "/href=\".*-$season_id/{n;n;n;n;p;}" | $sed -nE "s@.*href=\".*-([0-9]*)\">(.*)@\2\t\1@p") + [ -z "$tmp_season_id" ] && return + season_title=$(printf "%s" "$tmp_season_id" | cut -f1) + season_id=$(printf "%s" "$tmp_season_id" | cut -f2) + next_episode=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | + $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent | head -1) + [ -n "$next_episode" ] && return + } + ### Image Preview ### download_thumbnails() { + echo "$1" >"$tmp_dir/image_links" # used for the discord rich presence thumbnail printf "%s\n" "$1" | while read -r cover_url id type title; do cover_url=$(printf "%s" "$cover_url" | sed -E 's/\/[[:digit:]]+x[[:digit:]]+\//\/1000x1000\//') curl -s -o "$images_cache_dir/ $title ($type) $id.jpg" "$cover_url" & - if [ "$use_external_menu" = "1" ]; then + if [ "$use_external_menu" = "true" ]; then entry=/tmp/lobster/applications/"$id.desktop" + # The reason for the spaces is so that only the title can be displayed when using rofi + # or fzf, while still keeping the id and type in the string after it's selected generate_desktop "$title ($type)" "$images_cache_dir/ $title ($type) $id.jpg" >"$entry" & fi done sleep "$2" } - + # defaults to chafa image_preview_fzf() { - UB_PID_FILE="/tmp/lobster/.$(uuidgen)" - if [ -z "$ueberzug_output" ]; then - ueberzugpp layer --no-stdin --silent --use-escape-codes --pid-file "$UB_PID_FILE" + if [ "$use_ueberzugpp" = "true" ]; then + UB_PID_FILE="/tmp/lobster/.$(uuidgen)" + if [ -z "$ueberzug_output" ]; then + ueberzugpp layer --no-stdin --silent --use-escape-codes --pid-file "$UB_PID_FILE" + else + ueberzugpp layer -o "$ueberzug_output" --no-stdin --silent --use-escape-codes --pid-file "$UB_PID_FILE" + fi + UB_PID="$(cat "$UB_PID_FILE")" + LOBSTER_UEBERZUG_SOCKET=/tmp/ueberzugpp-"$UB_PID".socket + choice=$(find "$images_cache_dir" -type f -exec basename {} \; | fzf -i -q "$1" --cycle --preview-window="$preview_window_size" --preview="ueberzugpp cmd -s $LOBSTER_UEBERZUG_SOCKET -i fzfpreview -a add -x $ueberzug_x -y $ueberzug_y --max-width $ueberzug_max_width --max-height $ueberzug_max_height -f $images_cache_dir/{}" --reverse --with-nth 2 -d " ") + ueberzugpp cmd -s "$LOBSTER_UEBERZUG_SOCKET" -a exit else - ueberzugpp layer -o "$ueberzug_output" --no-stdin --silent --use-escape-codes --pid-file "$UB_PID_FILE" + dep_ch "chafa" || true + choice=$(find "$images_cache_dir" -type f -exec basename {} \; | fzf -i -q "$1" --cycle --preview-window="$preview_window_size" --preview="chafa -f sixels -s $chafa_dims $images_cache_dir/{}" --reverse --with-nth 2 -d " ") fi - UB_PID="$(cat "$UB_PID_FILE")" - LOBSTER_UEBERZUG_SOCKET=/tmp/ueberzugpp-"$UB_PID".socket - choice=$(find "$images_cache_dir" -type f -exec basename {} \; | fzf -i -q "$1" --cycle --preview-window="$preview_window_size" --preview="ueberzugpp cmd -s $LOBSTER_UEBERZUG_SOCKET -i fzfpreview -a add -x $ueberzug_x -y $ueberzug_y --max-width $ueberzug_max_width --max-height $ueberzug_max_height -f $images_cache_dir/{}" --reverse --with-nth 2 -d " ") - ueberzugpp cmd -s "$LOBSTER_UEBERZUG_SOCKET" -a exit } - select_desktop_entry() { - if [ "$use_external_menu" = "1" ]; then + if [ "$use_external_menu" = "true" ]; then [ -n "$image_config_path" ] && choice=$(rofi -show drun -drun-categories lobster -filter "$1" -show-icons -theme "$image_config_path" | $sed -nE "s@.*/([0-9]*)\.desktop@\1@p") 2>/dev/null || choice=$(rofi -show drun -drun-categories lobster -filter "$1" -show-icons | $sed -nE "s@.*/([0-9]*)\.desktop@\1@p") 2>/dev/null media_id=$(printf "%s" "$choice" | cut -d\ -f1) title=$(printf "%s" "$choice" | $sed -nE "s@[0-9]* (.*) \((tv|movie)\)@\1@p") media_type=$(printf "%s" "$choice" | $sed -nE "s@[0-9]* (.*) \((tv|movie)\)@\2@p") else image_preview_fzf "$1" + tput reset media_id=$(printf "%s" "$choice" | $sed -nE "s@.* ([0-9]*)\.jpg@\1@p") title=$(printf "%s" "$choice" | $sed -nE "s@[[:space:]]* (.*) \[.*\] \((tv|movie)\) [0-9]*\.jpg@\1@p") media_type=$(printf "%s" "$choice" | $sed -nE "s@[[:space:]]* (.*) \[.*\] \((tv|movie)\) [0-9]*\.jpg@\2@p") fi } - search() { - if [ "$image_preview" = "1" ]; then - response=$(curl -s "https://${base}/search/$query" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p") - else - response=$(curl -s "https://${base}/search/$query" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*([^<]*).*@\3 (\1) [\4]\t\2@p" | $hxunent) - fi - [ -z "$response" ] && send_notification "Error" "1000" "" "No results found" && exit 1 - } - - choose_episode() { - if [ -z "$season_id" ]; then - tmp_season_id=$(curl -s "https://${base}/ajax/v2/tv/seasons/${media_id}" | $sed -nE "s@.*href=\".*-([0-9]*)\">(.*)@\2\t\1@p" | launcher "Select a season: " "1") - [ -z "$tmp_season_id" ] && exit 1 - season_title=$(printf "%s" "$tmp_season_id" | cut -f1) - season_id=$(printf "%s" "$tmp_season_id" | cut -f2) - tmp_ep_id=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | - $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent | launcher "Select an episode: " "1") - [ -z "$tmp_ep_id" ] && exit 1 - fi - [ -z "$episode_title" ] && episode_title=$(printf "%s" "$tmp_ep_id" | cut -f1) - [ -z "$data_id" ] && data_id=$(printf "%s" "$tmp_ep_id" | cut -f2) - episode_id=$(curl -s "https://${base}/ajax/v2/episode/servers/${data_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | - $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\".*@\1\t\2@p" | grep "$provider" | cut -f1) - } - + ### Scraping/Decryption ### get_embed() { if [ "$media_type" = "movie" ]; then # request to get the episode id @@ -319,36 +358,44 @@ EOF exit 1 fi } - extract_from_json() { video_link=$(printf "%s" "$json_data" | tr '[' '\n' | $sed -nE 's@.*\"file\":\"(.*\.m3u8).*@\1@p' | head -1) [ -n "$quality" ] && video_link=$(printf "%s" "$video_link" | $sed -e "s|/playlist.m3u8|/$quality/index.m3u8|") - [ "$json_output" = "1" ] && printf "%s\n" "$json_data" && exit 0 + [ "$json_output" = "true" ] && printf "%s\n" "$json_data" && exit 0 subs_links=$(printf "%s" "$json_data" | tr "{}" "\n" | $sed -nE "s@.*\"file\":\"([^\"]*)\",\"label\":\"(.$subs_language)[,\"\ ].*@\1@p") subs_arg="--sub-file" num_subs=$(printf "%s" "$subs_links" | wc -l) if [ "$num_subs" -gt 0 ]; then subs_links=$(printf "%s" "$subs_links" | $sed -e "s/:/\\$path_thing:/g" -e "H;1h;\$!d;x;y/\n/$separator/" -e "s/$separator\$//") - [ "$num_subs" -gt 1 ] && subs_arg="--sub-files" + subs_arg="--sub-files" fi [ -z "$subs_links" ] && send_notification "No subtitles found" } - + json_from_id() { + # json_data=$(curl -s "http://localhost:8888/.netlify/functions/decrypt?id=${source_id}") + json_data=$(curl -s "https://lobster-decryption.netlify.app/decrypt?id=${source_id}") + } get_json() { # get the juicy links parse_embed=$(printf "%s" "$embed_link" | $sed -nE "s_(.*)/embed-(4|6)/(.*)\?z=\$_\1\t\2\t\3_p") - provider_link=$(printf "%s" "$parse_embed" | cut -f1) + _provider_link=$(printf "%s" "$parse_embed" | cut -f1) source_id=$(printf "%s" "$parse_embed" | cut -f3) - embed_type=$(printf "%s" "$parse_embed" | cut -f2) - json_data=$(curl -s "https://lobster-decryption.netlify.app/decrypt?id=${source_id}") - [ -n "$json_data" ] && extract_from_json + _embed_type=$(printf "%s" "$parse_embed" | cut -f2) + json_from_id + if [ -n "$json_data" ]; then + extract_from_json + else + send_notification "Error" "Could not get json data" + exit 1 + fi } + ### History ### check_history() { if [ ! -f "$histfile" ]; then - [ "$image_preview" = "1" ] && send_notification "Now Playing" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$title" - [ "$json_output" != "1" ] && send_notification "Now Playing" "5000" "" "$title" + [ "$image_preview" = "true" ] && send_notification "Now Playing" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$title" + [ "$json_output" != "true" ] && send_notification "Now Playing" "5000" "" "$title" return fi case $media_type in @@ -374,83 +421,6 @@ EOF esac } - - play_video() { - [ "$media_type" = "tv" ] && displayed_title="$title - $season_title - $episode_title" || displayed_title="$title" - case $player in - iina | celluloid) - if [ -n "$subs_links" ]; then - [ "$player" = "iina" ] && iina --no-stdin --keep-running --mpv-sub-files="$subs_links" --mpv-force-media-title="$displayed_title" "$video_link" - [ "$player" = "celluloid" ] && celluloid --mpv-sub-files="$subs_links" --mpv-force-media-title="$displayed_title" "$video_link" 2>/dev/null - else - [ "$player" = "iina" ] && iina --no-stdin --keep-running --mpv-force-media-title="$displayed_title" "$video_link" - [ "$player" = "celluloid" ] && celluloid --mpv-force-media-title="$displayed_title" "$video_link" 2>/dev/null - fi - ;; - vlc) - vlc_subs_links=$(printf "%s" "$subs_links" | sed 's/https\\:/https:/g; s/:\([^\/]\)/#\1/g') - vlc "$video_link" --meta-title "$displayed_title" --input-slave="$vlc_subs_links" - ;; - mpv | mpv.exe) - [ -z "$continue_choice" ] && check_history - if [ "$history" = 1 ]; then - if [ -n "$subs_links" ]; then - if [ -n "$resume_from" ]; then - "$player" --start="$resume_from" "$subs_arg"="$subs_links" --force-media-title="$displayed_title" "$video_link" 2>&1 | tee "$tmp_position" - else - "$player" --sub-file="$subs_links" --force-media-title="$displayed_title" "$video_link" 2>&1 | tee "$tmp_position" - fi - else - if [ -n "$resume_from" ]; then - "$player" --start="$resume_from" --force-media-title="$displayed_title" "$video_link" 2>&1 | tee "$tmp_position" - else - "$player" --force-media-title="$displayed_title" "$video_link" 2>&1 | tee "$tmp_position" - fi - fi - - position=$($sed -nE "s@.*AV: ([0-9:]*) / ([0-9:]*) \(([0-9]*)%\).*@\1@p" "$tmp_position" | tail -1) - progress=$($sed -nE "s@.*AV: ([0-9:]*) / ([0-9:]*) \(([0-9]*)%\).*@\3@p" "$tmp_position" | tail -1) - [ -n "$position" ] && send_notification "Stopped at" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$position" - - else - if [ -n "$subs_links" ]; then - if [ "$quiet_output" = 1 ]; then - [ -z "$resume_from" ] && "$player" "$subs_arg"="$subs_links" --force-media-title="$displayed_title" "$video_link" >/dev/null 2>&1 - [ -n "$resume_from" ] && "$player" "$subs_arg"="$subs_links" --start="$resume_from" --force-media-title="$displayed_title" "$video_link" >/dev/null 2>&1 - else - [ -z "$resume_from" ] && "$player" "$subs_arg"="$subs_links" --force-media-title="$displayed_title" "$video_link" - [ -n "$resume_from" ] && "$player" "$subs_arg"="$subs_links" --start="$resume_from" --force-media-title="$displayed_title" "$video_link" - fi - else - if [ "$quiet_output" = 1 ]; then - [ -z "$resume_from" ] && "$player" --force-media-title="$displayed_title" "$video_link" >/dev/null 2>&1 - [ -n "$resume_from" ] && "$player" --start="$resume_from" --force-media-title="$displayed_title" "$video_link" >/dev/null 2>&1 - else - [ -z "$resume_from" ] && "$player" --force-media-title="$displayed_title" "$video_link" - [ -n "$resume_from" ] && "$player" --start="$resume_from" --force-media-title="$displayed_title" "$video_link" - fi - fi - fi - ;; - *yncpla*) nohup "syncplay" "$video_link" -- --force-media-title="${displayed_title}" >/dev/null 2>&1 & ;; - *) $player "$video_link" ;; - esac - } - - next_episode_exists() { - episodes_list=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | - $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent) - next_episode=$(printf "%s" "$episodes_list" | $sed -n "/$data_id/{n;p;}") - [ -n "$next_episode" ] && return - tmp_season_id=$(curl -s "https://${base}/ajax/v2/tv/seasons/${media_id}" | $sed -n "/href=\".*-$season_id/{n;n;n;n;p;}" | $sed -nE "s@.*href=\".*-([0-9]*)\">(.*)@\2\t\1@p") - [ -z "$tmp_season_id" ] && return - season_title=$(printf "%s" "$tmp_season_id" | cut -f1) - season_id=$(printf "%s" "$tmp_season_id" | cut -f2) - next_episode=$(curl -s "https://${base}/ajax/v2/season/episodes/${season_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | - $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\">.*@\2\t\1@p" | $hxunent | head -1) - [ -n "$next_episode" ] && return - } - save_history() { case $media_type in movie) @@ -460,8 +430,10 @@ EOF else if grep -q -- "$media_id" "$histfile" 2>/dev/null; then $sed -i "s|\t[0-9:]*\t$media_id|\t$position\t$media_id|1" "$histfile" + send_notification "Saved to history" "5000" "" "$title" else - printf "%s\t%s\t%s\t%s\n" "$title" "$position" "$media_id" "$media_type" >>"$histfile" + printf "%s\t%s\t%s\t%s\t%s\n" "$title" "$position" "$media_id" "$media_type" "$image_link" >>"$histfile" + send_notification "Saved to history" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$title" fi fi ;; @@ -471,7 +443,8 @@ EOF if [ -n "$next_episode" ]; then episode_title=$(printf "%s" "$next_episode" | cut -f1) data_id=$(printf "%s" "$next_episode" | cut -f2) - episode_id=$(curl -s "https://${base}/ajax/v2/episode/servers/${data_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\".*@\1\t\2@p" | grep "$provider" | cut -f1) + episode_id=$(curl -s "https://${base}/ajax/v2/episode/servers/${data_id}" | $sed ':a;N;$!ba;s/\n//g;s/class="nav-item"/\n/g' | + $sed -nE "s@.*data-id=\"([0-9]*)\".*title=\"([^\"]*)\".*@\1\t\2@p" | grep "$provider" | cut -f1) $sed -i "s|\t[0-9:]*\t[0-9]*\ttv\t[0-9]*\t[0-9]*.*\t.*\t[0-9]*|\t00:00:00\t$media_id\ttv\t$season_id\t$episode_id\t$season_title\t$episode_title\t$data_id|1" "$histfile" send_notification "Updated to next episode" "5000" "" "$episode_title" else @@ -481,52 +454,248 @@ EOF else if grep -q -- "$media_id" "$histfile" 2>/dev/null; then $sed -i "/$media_id/d" "$histfile" + send_notification "Deleted from history" "5000" "" "$title" fi - printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$title" "$position" "$media_id" "$media_type" "$season_id" "$episode_id" "$season_title" "$episode_title" "$data_id" >>"$histfile" + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$title" "$position" "$media_id" "$media_type" \ + "$season_id" "$episode_id" "$season_title" "$episode_title" "$data_id" "$image_link" >>"$histfile" + send_notification "Saved to history" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$title" fi ;; *) notify-send "Error" "Unknown media type" ;; esac } + # TODO: Add image_preview support + play_from_history() { + [ ! -f "$histfile" ] && send_notification "No history file found" "5000" "" && exit 1 + [ "$watched_history" = 1 ] && exit 0 + watched_history=1 + choice=$($sed -n "1h;1!{x;H;};\${g;p;}" "$histfile" | nl -w 1 | nth "Choose an entry: ") + [ -z "$choice" ] && exit 1 + media_type=$(printf "%s" "$choice" | cut -f4) + title=$(printf "%s" "$choice" | cut -f1) + resume_from=$(printf "%s" "$choice" | cut -f2) + media_id=$(printf "%s" "$choice" | cut -f3) + if [ "$media_type" = "tv" ]; then + season_id=$(printf "%s" "$choice" | cut -f5) + episode_id=$(printf "%s" "$choice" | cut -f6) + season_title=$(printf "%s" "$choice" | cut -f7) + episode_title=$(printf "%s" "$choice" | cut -f8) + data_id=$(printf "%s" "$choice" | cut -f9) + image_link=$(printf "%s" "$choice" | cut -f10) + choose_episode + fi + keep_running="true" && loop + } + + ### Discord Rich Presence ### + set_activity() { + len=${#1} + printf "\\001\\000\\000\\000" + for i in 0 8 16 24; do + len=$((len >> i)) + #shellcheck disable=SC2059 + printf "\\$(printf "%03o" "$len")" + done + printf "%s" "$1" + } + update_rich_presence() { + state=$1 + payload='{"cmd":"SET_ACTIVITY","args":{"pid":"786","activity":{"state":"'"$state"'","details":"'"$displayed_title"'","assets":{"large_image":"'"$image_link"'","large_text":"'"$title"'","small_image":"'"$small_image"'","small_text":"powered by lobster"}}},"nonce":"'"$(date)"'"}' + if [ ! -e "$handshook" ]; then + handshake='{"v":1,"client_id":"'$presence_client_id'"}' + printf "\\000\\000\\000\\000\\$(printf "%03o" "${#handshake}")\\000\\000\\000%s" "$handshake" >"$presence" + sleep 2 + touch "$handshook" + fi + set_activity "$payload" >"$presence" + } + + ### Video Playback ### + update_discord_presence() { + total=$(printf "%02d:%02d:%02d" $((total_duration / 3600)) $((total_duration % 3600 / 60)) $((total_duration % 60))) + + [ -z "$image_link" ] && image_link="$(grep "$media_id" "$tmp_dir/image_links" | cut -f1)" + sleep 2 + + while :; do + if command -v nc >/dev/null 2>&1 && [ -S "$lobster_socket" ] 2>/dev/null; then + position=$(echo '{ "command": ["get_property", "time-pos"] }' | nc -U "$lobster_socket" 2>/dev/null | head -1) + [ -z "$position" ] && break + position=$(printf "%s" "$position" | sed -nE "s@.*\"data\":([0-9]*)\..*@\1@p") + position=$(printf "%02d:%02d:%02d" $((position / 3600)) $((position % 3600 / 60)) $((position % 60))) + update_rich_presence "$(printf "%s / %s" "$position" "$total")" & + else + # Fallback method if nc or Unix domain sockets are not available + sleep 5 + update_rich_presence "Watching" & + fi + sleep 0.5 + done + + rpc_cleanup + } + save_progress() { + position=$(cat "$watchlater_dir/"* 2>/dev/null | grep -A1 "$video_link" | $sed -nE "s@start=([0-9.]*)@\1@p" | cut -d'.' -f1) + if [ -n "$position" ]; then + progress=$((position * 100 / total_duration)) + position=$(printf "%02d:%02d:%02d" $((position / 3600)) $((position / 60 % 60)) $((position % 60))) + send_notification "Stopped at" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$position" + fi + } + play_video() { + [ "$media_type" = "tv" ] && displayed_title="$title - $season_title - $episode_title" || displayed_title="$title" + case $player in + iina | celluloid) + if [ -n "$subs_links" ]; then + [ "$player" = "iina" ] && iina --no-stdin --keep-running --mpv-sub-files="$subs_links" --mpv-force-media-title="$displayed_title" "$video_link" + [ "$player" = "celluloid" ] && celluloid --mpv-sub-files="$subs_links" --mpv-force-media-title="$displayed_title" "$video_link" 2>/dev/null + else + [ "$player" = "iina" ] && iina --no-stdin --keep-running --mpv-force-media-title="$displayed_title" "$video_link" + [ "$player" = "celluloid" ] && celluloid --mpv-force-media-title="$displayed_title" "$video_link" 2>/dev/null + fi + ;; + vlc) + vlc_subs_links=$(printf "%s" "$subs_links" | sed 's/https\\:/https:/g; s/:\([^\/]\)/#\1/g') + vlc "$video_link" --meta-title "$displayed_title" --input-slave="$vlc_subs_links" + ;; + mpv | mpv.exe) + [ -z "$continue_choice" ] && check_history + player_cmd="$player" + [ -n "$resume_from" ] && player_cmd="$player_cmd --start='$resume_from'" + [ -n "$subs_links" ] && player_cmd="$player_cmd $subs_arg='$subs_links'" + player_cmd="$player_cmd --force-media-title='$displayed_title' '$video_link'" + case "$(uname -s)" in + MINGW* | *Msys) player_cmd="$player_cmd --write-filename-in-watch-later-config --save-position-on-quit --quiet" ;; + *) player_cmd="$player_cmd --watch-later-dir='$watchlater_dir' --write-filename-in-watch-later-config --save-position-on-quit --quiet" ;; + esac + + # Check if the system supports Unix domain sockets + if command -v nc >/dev/null 2>&1 && [ -S "$lobster_socket" ] 2>/dev/null; then + player_cmd="$player_cmd --input-ipc-server='$lobster_socket'" + fi + + # Use eval to properly handle spaces in the command + eval "$player_cmd" >&3 & + + if [ -z "$quality" ]; then + link=$(printf "%s" "$video_link" | $sed "s/\/playlist.m3u8/\/1080\/index.m3u8/g") + else + link=$video_link + fi + + content=$(curl -s "$link") + durations=$(printf "%s" "$content" | grep -oE 'EXTINF:[0-9.]+,' | cut -d':' -f2 | tr -d ',') + total_duration=$(printf "%s" "$durations" | xargs echo | awk '{for(i=1;i<=NF;i++)sum+=$i} END {print sum}' | cut -d'.' -f1) + + [ "$discord_presence" = "true" ] && update_discord_presence + wait + save_progress + ;; + *yncpla*) nohup "syncplay" "$video_link" -- --force-media-title="${displayed_title}" >/dev/null 2>&1 & ;; + *) $player "$video_link" ;; + esac + } + ### Misc ### + update_script() { + which_lobster="$(command -v lobster)" + [ -z "$which_lobster" ] && send_notification "Can't find lobster in PATH" + [ -z "$which_lobster" ] && exit 1 + update=$(curl -s "https://raw.githubusercontent.com/justchokingaround/lobster/main/lobster.sh" || exit 1) + update="$(printf '%s\n' "$update" | diff -u "$which_lobster" -)" + if [ -z "$update" ]; then + send_notification "Script is up to date :)" + else + if printf '%s\n' "$update" | patch "$which_lobster" -; then + send_notification "Script has been updated!" + else + send_notification "Can't update for some reason!" + fi + fi + exit 0 + } + # download_video [url] [title] [download_dir] [json_data] [thumbnail_file (only when image_preview is enabled)] download_video() { - dir="$(printf "%s/%s" "$3" "$2" | tr -d ':')" - ffmpeg -loglevel error -stats -i "$1" -c copy "$dir.mp4" + title="$(printf "%s" "$2" | tr -d ':/')" + dir="${3}/${title}" + # ik this is dumb idc + language=$(printf "%s" "$4" | sed -nE "s@.*\"file\":\"[^\"]*\".*\"label\":\"(.$subs_language)[,\"\ ].*@\1@p") + num_subs="$(printf "%s" "$subs_links" | sed 's/:\([^\/]\)/\n\\1/g' | wc -l)" + ffmpeg_subs_links=$(printf "%s" "$subs_links" | sed 's/:\([^\/]\)/\nh/g; s/\\:/:/g' | while read -r sub_link; do + printf " -i %s" "$sub_link" + done) + sub_ops="$ffmpeg_subs_links -map 0:v -map 0:a" + if [ "$num_subs" -eq 0 ]; then + sub_ops=" -i $subs_links -map 0:v -map 0:a -map 1" + ffmpeg_meta="-metadata:s:s:0 language=$language" + else + i=1 + for i in $(seq 1 "$num_subs"); do + ffmpeg_maps="$ffmpeg_maps -map $i" + ffmpeg_meta="$ffmpeg_meta -metadata:s:s:$((i - 1)) language=$(printf "%s_%s" "$language" "$i")" + i=$((i + 1)) + done + fi + + sub_ops="$sub_ops $ffmpeg_maps -c:v copy -c:a copy -c:s srt $ffmpeg_meta" + # shellcheck disable=SC2086 + ffmpeg -loglevel error -stats -i "$1" $sub_ops -c copy "$dir.mkv" + } + choose_from_trending_or_recent() { + path=$1 + section=$2 + if [ "$path" = "home" ]; then + response=$(curl -s "https://${base}/${path}" | $sed -n "/id=\"${section}\"/,/class=\"block_area block_area_home section-id-02\"/p" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | + $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p" | $hxunent) + else + response=$(curl -s "https://${base}/${path}" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | + $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p" | $hxunent) + fi + main } + ### Main ### loop() { while [ "$keep_running" = "true" ]; do get_embed [ -z "$embed_link" ] && exit 1 get_json [ -z "$video_link" ] && exit 1 - if [ "$download" = "1" ]; then + if [ "$download" = "true" ]; then if [ "$media_type" = "movie" ]; then - if [ "$image_preview" = 1 ]; then - download_video "$video_link" "$title" "$download_dir" "$images_cache_dir/ $title ($media_type) $media_id.jpg" || exit 1 + if [ "$image_preview" = "true" ]; then + download_video "$video_link" "$title" "$download_dir" "$json_data" "$images_cache_dir/ $title ($media_type) $media_id.jpg" & send_notification "Finished downloading" "5000" "$images_cache_dir/ $title ($media_type) $media_id.jpg" "$title" else - download_video "$video_link" "$title" "$download_dir" || exit 1 + download_video "$video_link" "$title" "$download_dir" "$json_data" & send_notification "Finished downloading" "5000" "" "$title" fi else - if [ "$image_preview" = 1 ]; then - download_video "$video_link" "$title - $season_title - $episode_title" "$download_dir" "$images_cache_dir/ $title ($media_type) $media_id.jpg" || exit 1 + if [ "$image_preview" = "true" ]; then + download_video "$video_link" "$title - $season_title - $episode_title" "$download_dir" "$json_data" "$images_cache_dir/ $title ($media_type) $media_id.jpg" & send_notification "Finished downloading" "5000" "$images_cache_dir/ $title - $season_title - $episode_title ($media_type) $media_id.jpg" "$title - $season_title - $episode_title" else - download_video "$video_link" "$title - $season_title - $episode_title" "$download_dir" || exit 1 + download_video "$video_link" "$title - $season_title - $episode_title" "$download_dir" "$json_data" & send_notification "Finished downloading" "5000" "" "$title - $season_title - $episode_title" fi fi exit fi - play_video && wait - [ "$history" = 1 ] && save_history + if [ "$discord_presence" = "true" ]; then + [ -p "$presence" ] || mkfifo "$presence" + rm -f "$handshook" >/dev/null + tail -f "$presence" | nc -U "$discord_ipc" >"$ipclog" & + update_rich_presence "00:00:00" & + fi + play_video + if [ -n "$position" ] && [ "$history" = "true" ]; then + save_history + fi prompt_to_continue case "$continue_choice" in "Next episode") resume_from="" - [ "$history" = 0 ] && next_episode_exists + next_episode_exists if [ -n "$next_episode" ]; then episode_title=$(printf "%s" "$next_episode" | cut -f1) data_id=$(printf "%s" "$next_episode" | cut -f2) @@ -544,7 +713,6 @@ EOF ;; "Search") rm -f "$images_cache_dir"/* - rm -f "$tmp_position" query="" response="" season_id="" @@ -559,104 +727,35 @@ EOF esac done } - main() { if [ -z "$response" ]; then [ -z "$query" ] && get_input search "$query" [ -z "$response" ] && exit 1 fi - if [ "$image_preview" = "1" ]; then - if [ "$use_external_menu" = "0" ]; then - command -v "ueberzugpp" >/dev/null || send_notification "Please install ueberzugpp if you want to use image preview with fzf" + if [ "$image_preview" = "true" ]; then + if [ "$use_external_menu" = "false" ] && [ "$use_ueberzugpp" = "true" ]; then + command -v "ueberzugpp" >/dev/null || send_notification "Please install ueberzugpp if you want to use it for image previews" + use_ueberzugpp="false" fi download_thumbnails "$response" "3" select_desktop_entry "" else - [ "$use_external_menu" = "1" ] && choice=$(printf "%s" "$response" | rofi -dmenu -i -p "" -mesg "Choose a Movie or TV Show" -display-columns 1) - [ "$use_external_menu" = "0" ] && choice=$(printf "%s" "$response" | fzf --reverse --with-nth 1 -d "\t" --header "Choose a Movie or TV Show") - title=$(printf "%s" "$choice" | $sed -nE "s@(.*) \((movie|tv)\).*@\1@p") - media_type=$(printf "%s" "$choice" | $sed -nE "s@(.*) \((movie|tv)\).*@\2@p") + if [ "$use_external_menu" = "true" ]; then + choice=$(printf "%s" "$response" | rofi -dmenu -i -p "" -mesg "Choose a Movie or TV Show" -display-columns 4) + else + choice=$(printf "%s" "$response" | fzf --reverse --with-nth 4 -d "\t" --header "Choose a Movie or TV Show") + fi + image_link=$(printf "%s" "$choice" | cut -f1) media_id=$(printf "%s" "$choice" | cut -f2) + title=$(printf "%s" "$choice" | $sed -nE "s@.* *(tv|movie)[[:space:]]*(.*) \[.*\]@\2@p") + media_type=$(printf "%s" "$choice" | $sed -nE "s@.* *(tv|movie)[[:space:]]*(.*) \[.*\]@\1@p") fi [ "$media_type" = "tv" ] && choose_episode keep_running="true" loop } - play_from_history() { - [ ! -f "$histfile" ] && send_notification "No history file found" "5000" "" && exit 1 - [ "$watched_history" = 1 ] && exit 0 - watched_history=1 - choice=$($sed -n "1h;1!{x;H;};\${g;p;}" "$histfile" | nl -w 1 | nth "Choose an entry:") - [ -z "$choice" ] && exit 1 - media_type=$(printf "%s" "$choice" | cut -f4) - title=$(printf "%s" "$choice" | cut -f1) - resume_from=$(printf "%s" "$choice" | cut -f2) - media_id=$(printf "%s" "$choice" | cut -f3) - if [ "$media_type" = "tv" ]; then - season_id=$(printf "%s" "$choice" | cut -f5) - episode_id=$(printf "%s" "$choice" | cut -f6) - season_title=$(printf "%s" "$choice" | cut -f7) - episode_title=$(printf "%s" "$choice" | cut -f8) - data_id=$(printf "%s" "$choice" | cut -f9) - choose_episode - fi - keep_running="true" && loop - } - - # TODO: remove code duplication - choose_from_trending() { - if [ "$image_preview" = "1" ]; then - response=$(curl -s "https://${base}/home" | $sed -n '/id="trending-movies"/,/class="block_area block_area_home section-id-02"/p' | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p" | $hxunent) - else - response=$(curl -s "https://${base}/home" | $sed -n '/id="trending-movies"/,/id="trending-tv"/p' | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*([^<]*).*@\3 (\1) [\4]\t\2@p" | $hxunent) - fi - main - } - - choose_from_recent_movie() { - if [ "$image_preview" = "1" ]; then - response=$(curl -s "https://${base}/movie" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p" | $hxunent) - else - response=$(curl -s "https://${base}/movie" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*([^<]*).*@\3 (\1) [\4]\t\2@p" | $hxunent) - fi - main - } - - choose_from_recent_tv() { - if [ "$image_preview" = "1" ]; then - response=$(curl -s "https://${base}/tv-show" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*img data-src=\"([^\"]*)\".*([^<]*).*@\1\t\3\t\2\t\4 [\5]@p" | $hxunent) - else - response=$(curl -s "https://${base}/tv-show" | $sed ':a;N;$!ba;s/\n//g;s/class="flw-item"/\n/g' | - $sed -nE "s@.*([^<]*).*@\3 (\1) [\4]\t\2@p" | $hxunent) - fi - main - } - - update_script() { - which_lobster="$(command -v lobster)" - [ -z "$which_lobster" ] && send_notification "Can't find lobster in PATH" - [ -z "$which_lobster" ] && exit 1 - update=$(curl -s "https://raw.githubusercontent.com/justchokingaround/lobster/main/lobster.sh" || exit 1) - update="$(printf '%s\n' "$update" | diff -u "$which_lobster" -)" - if [ -z "$update" ]; then - send_notification "Script is up to date :)" - else - if printf '%s\n' "$update" | patch "$which_lobster" -; then - send_notification "Script has been updated!" - else - send_notification "Can't update for some reason!" - fi - fi - exit 0 - } - configuration # Edge case for Windows, just exits with dep_ch's error message if it can't find mpv.exe either @@ -668,8 +767,9 @@ EOF fi fi - [ "$debug" = 1 ] && set -x + [ "$debug" = "true" ] && set -x query="" + # Command line arguments parsing while [ $# -gt 0 ]; do case "$1" in --) @@ -677,11 +777,11 @@ EOF query="$*" break ;; - -c | --continue) - play_from_history && exit - ;; + # TODO: don't immediately exit if --continue is passed, since this ignores other arguments as soon as -c or --continue is found + -c | --continue) play_from_history && exit ;; + --discord | --discord-presence | --rpc | --presence) discord_presence="true" && shift ;; -d | --download) - download="1" + download="true" if [ -n "$download_dir" ]; then shift else @@ -699,17 +799,9 @@ EOF fi fi ;; - -h | --help) - usage && exit 0 - ;; - -i | --image-preview) - image_preview="1" - shift - ;; - -j | --json) - json_output="1" - shift - ;; + -h | --help) usage && exit 0 ;; + -i | --image-preview) image_preview="true" && shift ;; + -j | --json) json_output="true" && shift ;; -l | --language) subs_language="$2" if [ -z "$subs_language" ]; then @@ -725,10 +817,7 @@ EOF fi fi ;; - --rofi | --dmenu | --external-menu) - use_external_menu="1" - shift - ;; + --rofi | --external-menu) use_external_menu="true" && shift ;; -p | --provider) provider="$2" if [ -z "$provider" ]; then @@ -757,10 +846,6 @@ EOF fi fi ;; - --quiet) - quiet_output="1" - shift - ;; -r | --recent) recent="$2" if [ -z "$recent" ]; then @@ -775,23 +860,13 @@ EOF fi fi ;; - -s | --syncplay) - player="syncplay" - shift - ;; - -t | --trending) - trending="1" - shift - ;; - -u | -U | --update) - update_script - ;; - -v | -V | --version) - send_notification "Lobster Version: $LOBSTER_VERSION" && exit 0 - ;; + -s | --syncplay) player="syncplay" && shift ;; + -t | --trending) trending="1" && shift ;; + -u | -U | --update) update_script ;; + -v | -V | --version) send_notification "Lobster Version: $LOBSTER_VERSION" && exit 0 ;; -x | --debug) set -x - debug=1 + debug="true" shift ;; *) @@ -805,17 +880,19 @@ EOF esac done query="$(printf "%s" "$query" | tr ' ' '-' | $sed "s/^-//g")" - if [ "$image_preview" = 1 ]; then + if [ "$image_preview" = "true" ]; then test -d "$images_cache_dir" || mkdir -p "$images_cache_dir" - if [ "$use_external_menu" = 1 ]; then + if [ "$use_external_menu" = "true" ]; then mkdir -p "/tmp/lobster/applications/" [ ! -L "$applications" ] && ln -sf "/tmp/lobster/applications/" "$applications" fi fi [ -z "$provider" ] && provider="Vidcloud" - [ "$trending" = "1" ] && choose_from_trending - [ "$recent" = "movie" ] && choose_from_recent_movie - [ "$recent" = "tv" ] && choose_from_recent_tv + [ "$trending" = "1" ] && choose_from_trending_or_recent "home" "trending-movies" + [ "$recent" = "movie" ] && choose_from_trending_or_recent "movie" "" + [ "$recent" = "tv" ] && choose_from_trending_or_recent "tv-show" "" main -} 2>&1 | tee /tmp/lobster.log 2>/dev/null + +} 2>&1 | tee /tmp/lobster.log >&3 2>&4 +exec 1>&3 2>&4