Compare commits

..

16 Commits

Author SHA1 Message Date
Benex254
c66cc52d53 tests: add tests for cache and completions command 2024-08-09 23:14:46 +03:00
Benex254
e5f4a61a4e chore:bump version 2024-08-09 23:08:55 +03:00
Benex254
739d041c58 chore: update readme 2024-08-09 23:08:35 +03:00
Benex254
f12d5ab06c feat(cli): add helper command completions 2024-08-09 22:59:26 +03:00
Benex254
c3a3041cfb feat(interface): use click.edit 2024-08-09 22:58:48 +03:00
Benex254
594c687c8b chore: remove art as dep 2024-08-09 16:02:29 +03:00
Benex254
91b5d3ea40 chore: bump version 2024-08-09 16:00:49 +03:00
Benex254
8c30a7667c feat(fzf): remove art and instead use a static header 2024-08-09 16:00:30 +03:00
Benex254
179fbe59ac feat(config): let python-mpv to bee disabled by default 2024-08-09 15:59:56 +03:00
Benex254
5bfc210f59 feat(cli): add option to enable or disable python-mpv 2024-08-09 15:59:31 +03:00
Benex254
eb9c200fca feat(mpv): remove useless print statement 2024-08-09 15:58:50 +03:00
Benex254
603efd56e8 chore: remove dbus-python 2024-08-09 13:48:36 +03:00
Benex254
4d74dfa339 feat(mpv): improve auto next 2024-08-09 13:47:23 +03:00
Benex254
4681e38153 chore: bump version 2024-08-09 01:09:34 +03:00
Benex254
242003500d chore: add dbus-python as dep for notifications 2024-08-09 01:07:32 +03:00
Benex254
66ab365657 feat(mpv): add ytdl to true 2024-08-09 01:06:53 +03:00
11 changed files with 188 additions and 37 deletions

View File

@@ -151,7 +151,7 @@ fastanime --version
### External Dependencies
The only required external dependency, unless you won't be streaming, is [MPV](https://mpv.io/installation/), which i recommend installing with [uosc](https://github.com/tomasklaen/uosc) and [thumbfast](https://github.com/po5/thumbfast) for the best experience since they add a better interface to it.
The only required external dependency, unless you won't be streaming, is [MPV](https://mpv.io/installation/), which i recommend installing with [uosc](https://github.com/tomasklaen/uosc) :fire: and [thumbfast](https://github.com/po5/thumbfast) for the best experience since they add a better interface to it.
> [!NOTE]
>
@@ -166,7 +166,7 @@ The only required external dependency, unless you won't be streaming, is [MPV](h
- [chafa](https://github.com/hpjansson/chafa) currently the best cross platform and cross terminal image viewer for the terminal.
- [icat](https://sw.kovidgoyal.net/kitty/kittens/icat/) an image viewer that only works in [kitty terminal](https://sw.kovidgoyal.net/kitty/), which is currently the best terminal in my opinion, and by far the best image renderer for the terminal thanks to kitty's terminal graphics protocol. Its terminal graphics is so op that you can [run a browser on it](https://github.com/chase/awrit?tab=readme-ov-file)!!
- [bash](https://www.gnu.org/software/bash/) is used as the preview script language.
- [ani-skip](https://github.com/synacktraa/ani-skip) :fire: used for skipping the opening and ending theme songs
- [ani-skip](https://github.com/synacktraa/ani-skip) used for skipping the opening and ending theme songs
## Usage
@@ -216,6 +216,7 @@ Available options include:
- `--log` allow logging to stdout
- `--log-file` allow logging to a file
- `--rich-traceback` allow rich traceback
- `--use-mpv-mod/--use-default-player` whether to use python-mpv
#### The anilist command :fire: :fire: :fire:
@@ -372,6 +373,21 @@ fastanime cache --size
fastanime cache
```
#### completions subcommand
Helper command to setup shell completions
**Syntax:**
```bash
# print fish completions
fastanime completions --fish
# print bash completions
fastanime completions --bash
# print zsh completions
fastanime completions --zsh
```
## MPV specific commands
The project now allows on the fly media controls directly from mpv. This means you can go to the next or previous episode without the window ever closing thus offering a seamless experience.
@@ -387,12 +403,17 @@ This is all powered with [python-mpv]() which enables writing mpv scripts with p
`<shift>+a` toggle auto next episode
`<shit>+r` reload episode
### Added script messages
Examples:
```bash
# to select episode from mpv without window closing
script-message select-episode <episode-number>
# to select server from mpv without window closing
script-message select-server <server-name>
```
## Configuration
@@ -414,7 +435,7 @@ skip=false
# the maximum delta time in minutes after which the episode should be considered as completed
# used in the continue from time stamp
error=3
use_mpv_mod=True
use_mpv_mod=False
# the format of downloaded anime and trailer
# based on yt-dlp format and passed directly to it

View File

@@ -6,7 +6,7 @@ if sys.version_info < (3, 10):
) # noqa: F541
__version__ = "v0.60.3"
__version__ = "v0.61.5"
APP_NAME = "FastAnime"
AUTHOR = "Benex254"

View File

@@ -15,6 +15,7 @@ commands = {
"config": "config.config",
"downloads": "downloads.downloads",
"cache": "cache.cache",
"completions": "completions.completions",
}
@@ -126,6 +127,9 @@ signal.signal(signal.SIGINT, handle_exit)
help="Rofi theme to use for the user input prompt",
type=click.Path(),
)
@click.option(
"--use-mpv-mod/--use-default-player", help="Whether to use python-mpv", type=bool
)
@click.pass_context
def run_cli(
ctx: click.Context,
@@ -155,6 +159,7 @@ def run_cli(
rofi_theme,
rofi_theme_confirm,
rofi_theme_input,
use_mpv_mod,
):
from .config import Config
@@ -217,6 +222,11 @@ def run_cli(
== click.core.ParameterSource.COMMANDLINE
):
ctx.obj.auto_select = auto_select
if (
ctx.get_parameter_source("use_mpv_mod")
== click.core.ParameterSource.COMMANDLINE
):
ctx.obj.use_mpv_mod = use_mpv_mod
if sort_by:
ctx.obj.sort_by = sort_by
if downloads_dir:

View File

@@ -0,0 +1,110 @@
import click
@click.command(help="Helper command to get shell completions")
@click.option("--fish", is_flag=True)
@click.option("--zsh", is_flag=True)
@click.option("--bash", is_flag=True)
def completions(fish, zsh, bash):
if fish:
print(
"""
function _fastanime_completion;
set -l response (env _FASTANIME_COMPLETE=fish_complete COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) fastanime);
for completion in $response;
set -l metadata (string split "," $completion);
if test $metadata[1] = "dir";
__fish_complete_directories $metadata[2];
else if test $metadata[1] = "file";
__fish_complete_path $metadata[2];
else if test $metadata[1] = "plain";
echo $metadata[2];
end;
end;
end;
complete --no-files --command fastanime --arguments "(_fastanime_completion)";
"""
)
elif zsh:
print(
"""
#compdef fastanime
_fastanime_completion() {
local -a completions
local -a completions_with_descriptions
local -a response
(( ! $+commands[fastanime] )) && return 1
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _FASTANIME_COMPLETE=zsh_complete fastanime)}")
for type key descr in ${response}; do
if [[ "$type" == "plain" ]]; then
if [[ "$descr" == "_" ]]; then
completions+=("$key")
else
completions_with_descriptions+=("$key":"$descr")
fi
elif [[ "$type" == "dir" ]]; then
_path_files -/
elif [[ "$type" == "file" ]]; then
_path_files -f
fi
done
if [ -n "$completions_with_descriptions" ]; then
_describe -V unsorted completions_with_descriptions -U
fi
if [ -n "$completions" ]; then
compadd -U -V unsorted -a completions
fi
}
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
# autoload from fpath, call function directly
_fastanime_completion "$@"
else
# eval/source/. command, register function for later
compdef _fastanime_completion fastanime
fi
"""
)
elif bash:
print(
"""
_fastanime_completion() {
local IFS=$'\n'
local response
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _FASTANIME_COMPLETE=bash_complete $1)
for completion in $response; do
IFS=',' read type value <<< "$completion"
if [[ $type == 'dir' ]]; then
COMPREPLY=()
compopt -o dirnames
elif [[ $type == 'file' ]]; then
COMPREPLY=()
compopt -o default
elif [[ $type == 'plain' ]]; then
COMPREPLY+=($value)
fi
done
return 0
}
_fastanime_completion_setup() {
complete -o nosort -F _fastanime_completion fastanime
}
_fastanime_completion_setup;
"""
)
else:
print("Specify either --zsh/--fish/--bash")

View File

@@ -47,7 +47,7 @@ class Config(object):
"rofi_theme": "",
"rofi_theme_input": "",
"rofi_theme_confirm": "",
"use_mpv_mod": "true",
"use_mpv_mod": "false",
"force_window": "immediate",
}
)

View File

@@ -931,9 +931,9 @@ def anilist(config: "Config", anilist_config: QueryDict):
return AniList.search(id_in=anime_list)
def edit_config():
import subprocess
from click import edit
subprocess.run([os.environ.get("EDITOR", "open"), USER_CONFIG_PATH])
edit(filename=USER_CONFIG_PATH)
if config.use_rofi:
config.load_config()
config.use_rofi = True

View File

@@ -27,6 +27,7 @@ class MpvPlayer(object):
last_stop_time_secs = 0
last_total_time_secs = 0
current_media_title = ""
player_fetching = False
def get_episode(
self,
@@ -148,6 +149,7 @@ class MpvPlayer(object):
input_default_bindings=True,
input_vo_keyboard=True,
osc=True,
ytdl=True,
)
mpv_player.force_window = config.force_window
# mpv_player.cache = "yes"
@@ -160,6 +162,7 @@ class MpvPlayer(object):
@mpv_player.event_callback("file-loaded")
def set_total_time(event, *args):
d = mpv_player._get_property("duration")
self.player_fetching = False
if isinstance(d, float):
self.last_total_time = format_time(d)
@@ -171,17 +174,19 @@ class MpvPlayer(object):
self.last_stop_time = format_time(value)
@mpv_player.property_observer("time-remaining")
def handle_time_remaining_update(*args):
if len(args) > 1:
value = args[1]
if value is not None:
if value < 10 and config.auto_next:
url = self.get_episode("next")
if url:
mpv_player.loadfile(
url,
)
mpv_player.title = self.current_media_title
def handle_time_remaining_update(
property, time_remaining: float | None = None, *args
):
if time_remaining is not None:
if time_remaining < 1 and config.auto_next and not self.player_fetching:
print("Auto Fetching Next Episode")
self.player_fetching = True
url = self.get_episode("next")
if url:
mpv_player.loadfile(
url,
)
mpv_player.title = self.current_media_title
# -- keybindings --
@mpv_player.on_key_press("shift+n")

View File

@@ -6,7 +6,6 @@ import sys
from typing import Callable, List
# TODO: will probably scrap art not to useful
from art import text2art
from click import clear
from rich import print
@@ -22,6 +21,17 @@ FZF_DEFAULT_OPTS = """
--marker=">" --pointer="" --separator="" --scrollbar=""
"""
HEADER = """
███████╗░█████╗░░██████╗████████╗░█████╗░███╗░░██╗██╗███╗░░░███╗███████╗
██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔══██╗████╗░██║██║████╗░████║██╔════╝
█████╗░░███████║╚█████╗░░░░██║░░░███████║██╔██╗██║██║██╔████╔██║█████╗░░
██╔══╝░░██╔══██║░╚═══██╗░░░██║░░░██╔══██║██║╚████║██║██║╚██╔╝██║██╔══╝░░
██║░░░░░██║░░██║██████╔╝░░░██║░░░██║░░██║██║░╚███║██║██║░╚═╝░██║███████╗
╚═╝░░░░░╚═╝░░╚═╝╚═════╝░░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚══╝╚═╝╚═╝░░░░░╚═╝╚══════╝
"""
class FZF:
"""an abstraction over the fzf commandline utility
@@ -149,7 +159,7 @@ class FZF:
_commands = [
*self.default_options,
"--header",
text2art(header),
HEADER,
"--header-first",
"--prompt",
prompt.title(),

16
poetry.lock generated
View File

@@ -1,19 +1,5 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "art"
version = "6.2"
description = "ASCII Art Library For Python"
optional = false
python-versions = ">=3.5"
files = [
{file = "art-6.2-py3-none-any.whl", hash = "sha256:d632d1d3f5fabcaf8673abe934b51df0017bc914d106e89d45ae4ebef0e3149a"},
{file = "art-6.2.tar.gz", hash = "sha256:506a0c4f261289a0e0d088de7beffcb1835078c4e44b0c5353bdaf47b490e76f"},
]
[package.extras]
dev = ["bandit (>=1.5.1)", "coverage (>=4.1)", "pydocstyle (>=3.0.0)", "vulture (>=1.0)"]
[[package]]
name = "autoflake"
version = "2.3.1"
@@ -1421,4 +1407,4 @@ test = ["pytest (>=8.1,<9.0)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "5305621bc02d824065519913f0d754e269f1e4525ba8796be08504120614259d"
content-hash = "83ec7de7d9466dcd1fadef4b21eec2a879cc9a7d526992ed280b6af53b49d9f1"

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "fastanime"
version = "0.60.3.dev1"
version = "0.61.5.dev1"
description = "A browser anime site experience from the terminal"
authors = ["Benextempest <benextempest@gmail.com>"]
license = "UNLICENSE"
@@ -13,7 +13,6 @@ rich = "^13.7.1"
click = "^8.1.7"
inquirerpy = "^0.3.4"
platformdirs = "^4.2.2"
art = "^6.2"
python-dotenv = "^1.0.1"
thefuzz = "^0.22.1"
requests = "^2.32.3"

View File

@@ -45,6 +45,16 @@ def test_search_help(runner: CliRunner):
assert result.exit_code == 0
def test_cache_help(runner: CliRunner):
result = runner.invoke(run_cli, ["cache", "--help"])
assert result.exit_code == 0
def test_completions_help(runner: CliRunner):
result = runner.invoke(run_cli, ["completions", "--help"])
assert result.exit_code == 0
def test_anilist_help(runner: CliRunner):
result = runner.invoke(run_cli, ["anilist", "--help"])
assert result.exit_code == 0