├── requirements.txt ├── animethemes-win.py ├── animethemes └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | pyfzf==0.3.1 2 | requests==2.28.2 3 | -------------------------------------------------------------------------------- /animethemes-win.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from pyfzf.pyfzf import FzfPrompt 3 | import subprocess 4 | 5 | 6 | def play_anime_theme(): 7 | fzf = FzfPrompt() 8 | 9 | animetitle = input("Give an anime title: ") 10 | themetype = input("Opening or Ending [OP/ED]: ") 11 | 12 | res = requests.get(f" https://myanimelist.net/search/prefix.json?type=anime&keyword={animetitle}&v=1").json() 13 | 14 | animeinfo = res['categories'][0]['items'] 15 | 16 | animes = { 17 | f"{gotanimeid['name']}": str(gotanimeid["id"]) 18 | for gotanimeid in animeinfo 19 | if gotanimeid.get("payload", {}).get('media_type') in ["TV", "OVA"] 20 | } 21 | 22 | selectedtitle = fzf.prompt(animes) 23 | 24 | selectedid = animes.get(selectedtitle[0]) 25 | if selectedid is None: 26 | print("Invalid selection.") 27 | return 28 | 29 | headers = { 30 | "User-Agent": "Mozilla/5.0 (compatible; MSIE 8.0; Windows 98; Win 9x 4.90; Trident/4.0)" 31 | } 32 | 33 | resp = requests.get( 34 | f"https://api.animethemes.moe/anime?include=animesynonyms,series,animethemes,animethemes.animethemeentries.videos,animethemes.song,animethemes.song.artists,studios,images,resources&fields%5Banime%5D=id,name,slug,year&filter%5Bhas%5D=resources&filter%5Bsite%5D=myanimelist&filter%5Bexternal_id%5D={selectedid}", 35 | headers=headers, 36 | ) 37 | 38 | if resp.status_code != 200: 39 | print(f"Error occurred: {resp.status_code} {resp.text}") 40 | return 41 | 42 | resp = resp.json() 43 | 44 | if not resp["anime"]: 45 | print("Not found, probably not added yet!") 46 | return 47 | 48 | animethemes = resp["anime"][0]["animethemes"] 49 | animename = resp["anime"][0]["name"] 50 | 51 | # Initialize a list to store the available theme songs 52 | available_themes = [] 53 | 54 | # Iterate over the anime themes and gather all matching theme song titles and links 55 | for theme in animethemes: 56 | if theme["type"] == themetype: 57 | for entry in theme["animethemeentries"]: 58 | available_themes.extend( 59 | { 60 | "title": f"{theme['song']['title']} - {video['filename']}", 61 | "link": video["link"], 62 | } 63 | for video in entry["videos"] 64 | ) 65 | while True: 66 | # Use fzf.prompt to let the user select a theme song 67 | selected_theme = fzf.prompt([theme["title"] for theme in available_themes]) 68 | 69 | if not selected_theme: 70 | # If the user chose to exit, break out of the loop 71 | break 72 | 73 | if selected_link := next( 74 | ( 75 | theme["link"] 76 | for theme in available_themes 77 | if theme["title"] == selected_theme[0] 78 | ), 79 | None, 80 | ): 81 | print(f"Now playing... {selected_theme[0]}") 82 | subprocess.run(["mpv", selected_link]) 83 | else: 84 | print("Invalid selection.") 85 | 86 | askuser = fzf.prompt(["Play another theme from this anime", 87 | "Search for another anime", 88 | "Exit" 89 | ]) 90 | 91 | if askuser[0] == "Play another theme from this anime": 92 | continue 93 | elif askuser[0] == "Search for another anime": 94 | play_anime_theme() 95 | else: 96 | quit() 97 | 98 | # askuser = input("Do you want to exit the selection? [Y/N]") 99 | 100 | # if askuser.upper() == "Y": 101 | # break 102 | # elif askuser.upper() == "N": 103 | # continue 104 | 105 | # while True: 106 | # searchagain = input("Do you want to Search a new anime? [Y/N]") 107 | 108 | # if searchagain.upper() == "Y": 109 | # play_anime_theme() 110 | # else: 111 | # quit() 112 | 113 | 114 | if __name__ == "__main__": 115 | play_anime_theme() 116 | 117 | -------------------------------------------------------------------------------- /animethemes: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import requests 5 | from pyfzf.pyfzf import FzfPrompt 6 | import subprocess 7 | 8 | 9 | def play_anime_theme(): 10 | fzf = FzfPrompt() 11 | 12 | animetitle = input("Give an anime title: ") 13 | themetype = input("Opening or Ending [OP/ED]: ") 14 | 15 | res = requests.get(f" https://myanimelist.net/search/prefix.json?type=anime&keyword={animetitle}&v=1").json() 16 | 17 | animeinfo = res['categories'][0]['items'] 18 | 19 | animes = { 20 | f"{gotanimeid['name']}": str(gotanimeid["id"]) 21 | for gotanimeid in animeinfo 22 | if gotanimeid.get("payload", {}).get('media_type') in ["TV", "OVA"] 23 | } 24 | 25 | selectedtitle = fzf.prompt(animes) 26 | 27 | selectedid = animes.get(selectedtitle[0]) 28 | if selectedid is None: 29 | print("Invalid selection.") 30 | return 31 | 32 | headers = { 33 | "User-Agent": "Mozilla/5.0 (compatible; MSIE 8.0; Windows 98; Win 9x 4.90; Trident/4.0)" 34 | } 35 | 36 | resp = requests.get( 37 | f"https://api.animethemes.moe/anime?include=animesynonyms,series,animethemes,animethemes.animethemeentries.videos,animethemes.song,animethemes.song.artists,studios,images,resources&fields%5Banime%5D=id,name,slug,year&filter%5Bhas%5D=resources&filter%5Bsite%5D=myanimelist&filter%5Bexternal_id%5D={selectedid}", 38 | headers=headers, 39 | ) 40 | 41 | if resp.status_code != 200: 42 | print(f"Error occurred: {resp.status_code} {resp.text}") 43 | return 44 | 45 | resp = resp.json() 46 | 47 | if not resp["anime"]: 48 | print("Not found, probably not added yet!") 49 | return 50 | 51 | animethemes = resp["anime"][0]["animethemes"] 52 | animename = resp["anime"][0]["name"] 53 | 54 | # Initialize a list to store the available theme songs 55 | available_themes = [] 56 | 57 | # Iterate over the anime themes and gather all matching theme song titles and links 58 | for theme in animethemes: 59 | if theme["type"] == themetype: 60 | for entry in theme["animethemeentries"]: 61 | available_themes.extend( 62 | { 63 | "title": f"{theme['song']['title']} - {video['filename']}", 64 | "link": video["link"], 65 | } 66 | for video in entry["videos"] 67 | ) 68 | while True: 69 | # Use fzf.prompt to let the user select a theme song 70 | selected_theme = fzf.prompt([theme["title"] for theme in available_themes]) 71 | 72 | if not selected_theme: 73 | # If the user chose to exit, break out of the loop 74 | break 75 | 76 | if selected_link := next( 77 | ( 78 | theme["link"] 79 | for theme in available_themes 80 | if theme["title"] == selected_theme[0] 81 | ), 82 | None, 83 | ): 84 | print(f"Now playing... {selected_theme[0]}") 85 | subprocess.run(["mpv", selected_link]) 86 | else: 87 | print("Invalid selection.") 88 | 89 | askuser = fzf.prompt(["Play another theme from this anime", 90 | "Search for another anime", 91 | "Exit" 92 | ]) 93 | 94 | if askuser[0] == "Play another theme from this anime": 95 | continue 96 | elif askuser[0] == "Search for another anime": 97 | play_anime_theme() 98 | else: 99 | quit() 100 | 101 | # askuser = input("Do you want to exit the selection? [Y/N]") 102 | 103 | # if askuser.upper() == "Y": 104 | # break 105 | # elif askuser.upper() == "N": 106 | # continue 107 | 108 | # while True: 109 | # searchagain = input("Do you want to Search a new anime? [Y/N]") 110 | 111 | # if searchagain.upper() == "Y": 112 | # play_anime_theme() 113 | # else: 114 | # quit() 115 | 116 | 117 | if __name__ == "__main__": 118 | play_anime_theme() 119 | 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | - [Table of Contents](#table-of-contents) 4 | - [showcase](#showcase) 5 | - [Introduction](#introduction) 6 | - [Requirements](#requirements) 7 | - [Installation](#installation) 8 | - [Usage](#usage) 9 | - [Limitations](#limitations) 10 | - [Credits](#credits) 11 | 12 | ## showcase 13 | 14 | [animethemes_compressed.webm](https://user-images.githubusercontent.com/32393101/229285478-f8077f5d-c109-4423-b1dc-13daef15310c.webm) 15 | 16 | 17 | ## Introduction 18 | 19 | This is a Python script that lets you search for an anime and play its opening or ending theme songs using the mpv media player. The script uses the AnimeThemes API to get the available theme songs and the pyfzf library to prompt the user for selections. 20 | 21 | ## Requirements 22 | 23 | - Python 3.x 24 | - requests module (can be installed via pip) 25 | - pyfzf module (can be installed via pip) 26 | - mpv media player (must be installed separately) 27 | - install fzf as well (depending on your OS) 28 | 29 | 30 | ## Installation 31 | 32 | - For linux 33 | 34 | 35 | ## Using git 36 | 37 | Alternatively, you can "git clone" this repository to any directory and run 38 | [install](https://github.com/junegunn/fzf/blob/master/install) script. 39 | 40 | ```sh 41 | git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf 42 | ~/.fzf/install 43 | ``` 44 | 45 | ## Using Linux package managers 46 | 47 | | Package Manager | Linux Distribution | Command | 48 | | --- | --- | --- | 49 | | APK | Alpine Linux | `sudo apk add fzf` | 50 | | APT | Debian 9+/Ubuntu 19.10+ | `sudo apt install fzf` | 51 | | Conda | | `conda install -c conda-forge fzf` | 52 | | DNF | Fedora | `sudo dnf install fzf` | 53 | | Nix | NixOS, etc. | `nix-env -iA nixpkgs.fzf` | 54 | | Pacman | Arch Linux | `sudo pacman -S fzf` | 55 | | pkg | FreeBSD | `pkg install fzf` | 56 | | pkgin | NetBSD | `pkgin install fzf` | 57 | | pkg_add | OpenBSD | `pkg_add fzf` | 58 | | Portage | Gentoo | `emerge --ask app-shells/fzf` | 59 | | XBPS | Void Linux | `sudo xbps-install -S fzf` | 60 | | Zypper | openSUSE | `sudo zypper install fzf` | 61 | 62 | - Install the script 63 | 64 | ``` 65 | sudo curl -sL https://raw.githubusercontent.com/Laezor/animetheme-player/main/animethemes -o /$HOME/.local/bin/animethemes && 66 | sudo chmod +x /$HOME/.local/bin/animethemes 67 | ``` 68 | 69 | - For windows 70 | 71 | | Package manager | Command | 72 | | --- | --- | 73 | | Chocolatey | `choco install fzf` | 74 | | Scoop | `scoop install fzf` | 75 | | Winget | `winget install fzf` | 76 | 77 | - Install the script 78 | 79 | ``` 80 | iwr "https://raw.githubusercontent.com/Laezor/animetheme-player/main/animethemes-win.py" -OutFile "%userprofile%/Downloads/animethemes-win.py" 81 | ``` 82 | 83 | ## Usage 84 | 85 | To use the script, follow these steps: 86 | 87 | 1. Clone the repository to your local machine. 88 | 2. Open a terminal window and navigate to the directory where the script is located. 89 | 3. Run the script by typing the following command and pressing Enter: 90 | 91 | For windows 92 | 93 | ```python 94 | python animethemes-win.py 95 | ``` 96 | For linux 97 | ``` 98 | animethemes 99 | ``` 100 | 101 | 4. When prompted, enter the title of the anime you want to search for and whether you want to play its opening or ending theme songs. 102 | 5. Select the anime you want to play theme songs for from the list of search results that appear. 103 | 6. Select the theme song you want to play from the list of available theme songs. 104 | 7. The script will automatically launch the mpv media player and play the selected theme song. Press Q to quit mpv. 105 | 8. After the theme song has finished playing, you will be prompted to choose whether to exit the selection or play another theme song from the current anime. 106 | 9. If you choose to play another theme song, the available theme songs for the current anime will be displayed and you can select another one to play. 107 | 10. If you choose to exit the selection, you will be prompted to choose whether to search for a new anime or exit the script. 108 | 11. If you choose to search for a new anime, the script will start over and prompt you for a new anime title and theme type. 109 | 12. If you choose to exit the script, the script will terminate. 110 | 111 | ## Limitations 112 | 113 | - The script only searches for TV and OVA anime titles. 114 | - The script only supports playing opening and ending theme songs. 115 | - The script only plays one theme song at a time. 116 | - The script requires an active internet connection to function properly. 117 | 118 | ## Credits 119 | 120 | - AnimeThemes for providing the API used by the script. 121 | - pyfzf for providing the FzfPrompt library used by the script. 122 | - The creators and contributors of the requests and mpv modules used by the script. 123 | --------------------------------------------------------------------------------