ytplay-cli 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: ytplay-cli
3
+ Version: 0.4.0
4
+ Summary: Search and play YouTube videos from the terminal using mpv and fzf
5
+ Author: Naveen
6
+ Project-URL: Homepage, https://github.com/naveen07-c/yt-cli
7
+ Project-URL: Repository, https://github.com/naveen07-c/yt-cli
8
+ Project-URL: Issues, https://github.com/naveen07-c/yt-cli/issues
9
+ Keywords: youtube,cli,terminal,mpv,fzf
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Environment :: Console
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: yt-dlp
17
+
18
+ # yt-cli
19
+
20
+ Search, preview, and play YouTube videos directly from the terminal.
21
+
22
+ `yt-cli` is a lightweight command-line tool that allows users to search YouTube, browse results interactively, preview video information, and play or download videos — all without leaving the terminal.
23
+
24
+ The tool combines several powerful utilities:
25
+
26
+ - **yt-dlp** → fetches YouTube metadata and streams
27
+ - **fzf** → interactive fuzzy search interface
28
+ - **mpv** → video/audio playback
29
+ - **jq** → JSON parsing for preview data
30
+
31
+ This makes it possible to build a fast, terminal-based YouTube experience.
32
+
33
+ ---
34
+
35
+ # Features
36
+
37
+ - Search YouTube from the terminal
38
+ - Interactive video selection using **fzf**
39
+ - Preview video metadata while browsing results
40
+ - Play videos directly with **mpv**
41
+ - Play **audio-only** mode
42
+ - Download videos using **yt-dlp**
43
+ - Open selected video in the browser
44
+ - Choose playback **quality**
45
+ - Adjust playback **speed**
46
+ - Maintain **search history**
47
+ - Clean CLI interface designed for Linux environments
48
+
49
+ ---
50
+
51
+ # Installation
52
+
53
+ The recommended installation method is **pipx**.
54
+
55
+ `pipx` installs Python CLI applications globally but keeps dependencies isolated.
56
+
57
+ Install pipx if not installed:
58
+
59
+ ```bash
60
+ sudo apt install pipx
61
+ pipx ensurepath
@@ -0,0 +1,44 @@
1
+ # yt-cli
2
+
3
+ Search, preview, and play YouTube videos directly from the terminal.
4
+
5
+ `yt-cli` is a lightweight command-line tool that allows users to search YouTube, browse results interactively, preview video information, and play or download videos — all without leaving the terminal.
6
+
7
+ The tool combines several powerful utilities:
8
+
9
+ - **yt-dlp** → fetches YouTube metadata and streams
10
+ - **fzf** → interactive fuzzy search interface
11
+ - **mpv** → video/audio playback
12
+ - **jq** → JSON parsing for preview data
13
+
14
+ This makes it possible to build a fast, terminal-based YouTube experience.
15
+
16
+ ---
17
+
18
+ # Features
19
+
20
+ - Search YouTube from the terminal
21
+ - Interactive video selection using **fzf**
22
+ - Preview video metadata while browsing results
23
+ - Play videos directly with **mpv**
24
+ - Play **audio-only** mode
25
+ - Download videos using **yt-dlp**
26
+ - Open selected video in the browser
27
+ - Choose playback **quality**
28
+ - Adjust playback **speed**
29
+ - Maintain **search history**
30
+ - Clean CLI interface designed for Linux environments
31
+
32
+ ---
33
+
34
+ # Installation
35
+
36
+ The recommended installation method is **pipx**.
37
+
38
+ `pipx` installs Python CLI applications globally but keeps dependencies isolated.
39
+
40
+ Install pipx if not installed:
41
+
42
+ ```bash
43
+ sudo apt install pipx
44
+ pipx ensurepath
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61,<70", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ytplay-cli"
7
+ version = "0.4.0"
8
+ description = "Search and play YouTube videos from the terminal using mpv and fzf"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+
12
+ authors = [
13
+ { name = "Naveen" }
14
+ ]
15
+
16
+ dependencies = [
17
+ "yt-dlp"
18
+ ]
19
+
20
+ keywords = ["youtube", "cli", "terminal", "mpv", "fzf"]
21
+
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "Environment :: Console",
25
+ "Operating System :: POSIX :: Linux"
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/naveen07-c/yt-cli"
30
+ Repository = "https://github.com/naveen07-c/yt-cli"
31
+ Issues = "https://github.com/naveen07-c/yt-cli/issues"
32
+
33
+ [project.scripts]
34
+ yt = "ytcli.cli:main"
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["."]
38
+ include = ["ytcli*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -0,0 +1,179 @@
1
+ import argparse
2
+ import os
3
+ import subprocess
4
+ import webbrowser
5
+
6
+ from ytcli.search import search_youtube
7
+ from ytcli.player import play_video, play_audio, download_video
8
+ from ytcli.ui import select_video
9
+ from ytcli.utils import format_duration, format_views
10
+ from ytcli.config import load_config
11
+ from ytcli.deps import check_dependencies
12
+ from ytcli import __version__
13
+
14
+
15
+ HISTORY_FILE = os.path.expanduser("~/.local/share/yt-cli/history")
16
+
17
+
18
+ def save_history(query):
19
+
20
+ os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
21
+
22
+ with open(HISTORY_FILE, "a") as f:
23
+ f.write(query + "\n")
24
+
25
+
26
+ def show_history():
27
+
28
+ if not os.path.exists(HISTORY_FILE):
29
+ print("No history yet.")
30
+ return
31
+
32
+ with open(HISTORY_FILE) as f:
33
+ print(f.read())
34
+
35
+
36
+ def fzf_menu(options, prompt):
37
+
38
+ fzf = subprocess.Popen(
39
+ [
40
+ "fzf",
41
+ "--height=40%",
42
+ "--layout=reverse",
43
+ "--border",
44
+ "--prompt", prompt
45
+ ],
46
+ stdin=subprocess.PIPE,
47
+ stdout=subprocess.PIPE,
48
+ text=True
49
+ )
50
+
51
+ stdout, _ = fzf.communicate("\n".join(options))
52
+
53
+ return stdout.strip()
54
+
55
+
56
+ def choose_action():
57
+
58
+ actions = [
59
+ "Play Video",
60
+ "Play Audio Only",
61
+ "Download Video",
62
+ "Open in Browser"
63
+ ]
64
+
65
+ return fzf_menu(actions, "Action > ")
66
+
67
+
68
+ def choose_quality():
69
+
70
+ qualities = [
71
+ "1080",
72
+ "720",
73
+ "480",
74
+ "best"
75
+ ]
76
+
77
+ return fzf_menu(qualities, "Quality > ")
78
+
79
+
80
+ def choose_speed():
81
+
82
+ speeds = [
83
+ "1",
84
+ "1.25",
85
+ "1.5",
86
+ "2"
87
+ ]
88
+
89
+ return fzf_menu(speeds, "Speed > ")
90
+
91
+
92
+ def main():
93
+
94
+ check_dependencies()
95
+
96
+ parser = argparse.ArgumentParser(
97
+ description="YouTube CLI player"
98
+ )
99
+
100
+ parser.add_argument("query", nargs="*")
101
+ parser.add_argument("--audio", action="store_true")
102
+ parser.add_argument("--download", action="store_true")
103
+ parser.add_argument("--play", type=int)
104
+ parser.add_argument("--history", action="store_true")
105
+ parser.add_argument("--version", action="store_true")
106
+
107
+ args = parser.parse_args()
108
+
109
+ if args.version:
110
+ print("yt-cli version", __version__)
111
+ return
112
+
113
+ if args.history:
114
+ show_history()
115
+ return
116
+
117
+ if not args.query:
118
+ parser.print_help()
119
+ return
120
+
121
+ query = " ".join(args.query)
122
+
123
+ save_history(query)
124
+
125
+ config = load_config()
126
+
127
+ videos = search_youtube(query)
128
+
129
+ lines = []
130
+
131
+ for v in videos:
132
+
133
+ title = v.get("title", "Unknown")
134
+ channel = v.get("channel", "Unknown")
135
+ duration = format_duration(v.get("duration"))
136
+ views = format_views(v.get("view_count"))
137
+ vid = v.get("id")
138
+
139
+ lines.append(
140
+ f"{title} | {channel} | {duration} | {views} | {vid}"
141
+ )
142
+
143
+ selection = select_video(lines)
144
+
145
+ if not selection:
146
+ return
147
+
148
+ index = lines.index(selection)
149
+ video = videos[index]
150
+
151
+ video_id = video["id"]
152
+
153
+ # ACTION MENU
154
+ action = choose_action()
155
+
156
+ if action == "Play Audio Only":
157
+
158
+ play_audio(video_id)
159
+ return
160
+
161
+ if action == "Download Video":
162
+
163
+ download_video(video_id)
164
+ return
165
+
166
+ if action == "Open in Browser":
167
+
168
+ webbrowser.open(f"https://youtube.com/watch?v={video_id}")
169
+ return
170
+
171
+ # QUALITY MENU
172
+ quality = choose_quality()
173
+
174
+ # SPEED MENU
175
+ speed = choose_speed()
176
+
177
+ play_video(video_id, quality, speed)
178
+ if __name__ == "__main__":
179
+ main()
@@ -0,0 +1,29 @@
1
+ import os
2
+ import json
3
+
4
+ CONFIG_DIR = os.path.expanduser("~/.config/yt-cli")
5
+ CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
6
+
7
+ DEFAULT_CONFIG = {
8
+ "default_quality": "720",
9
+ "default_speed": "1"
10
+ }
11
+
12
+
13
+ def load_config():
14
+
15
+ if not os.path.exists(CONFIG_DIR):
16
+ os.makedirs(CONFIG_DIR)
17
+
18
+ if not os.path.exists(CONFIG_FILE):
19
+
20
+ with open(CONFIG_FILE, "w") as f:
21
+ json.dump(DEFAULT_CONFIG, f, indent=2)
22
+
23
+ return DEFAULT_CONFIG
24
+
25
+ try:
26
+ with open(CONFIG_FILE) as f:
27
+ return json.load(f)
28
+ except Exception:
29
+ return DEFAULT_CONFIG
@@ -0,0 +1,52 @@
1
+ import shutil
2
+ import subprocess
3
+ import sys
4
+
5
+
6
+ def detect_package_manager():
7
+
8
+ managers = ["apt", "dnf", "pacman"]
9
+
10
+ for m in managers:
11
+ if shutil.which(m):
12
+ return m
13
+
14
+ return None
15
+
16
+
17
+ def install_package(pkg):
18
+
19
+ manager = detect_package_manager()
20
+
21
+ if manager == "apt":
22
+ subprocess.run(["sudo", "apt", "install", "-y", pkg])
23
+
24
+ elif manager == "dnf":
25
+ subprocess.run(["sudo", "dnf", "install", "-y", pkg])
26
+
27
+ elif manager == "pacman":
28
+ subprocess.run(["sudo", "pacman", "-S", "--noconfirm", pkg])
29
+
30
+ else:
31
+ print("No supported package manager found.")
32
+ sys.exit(1)
33
+
34
+
35
+ def ensure_tool(tool, package):
36
+
37
+ if shutil.which(tool) is None:
38
+
39
+ print(f"{tool} not found. Installing {package}...")
40
+ install_package(package)
41
+
42
+
43
+ def check_dependencies():
44
+
45
+ ensure_tool("fzf", "fzf")
46
+ ensure_tool("mpv", "mpv")
47
+ ensure_tool("jq", "jq")
48
+
49
+ if shutil.which("yt-dlp") is None:
50
+
51
+ print("Installing yt-dlp via pip...")
52
+ subprocess.run([sys.executable, "-m", "pip", "install", "yt-dlp"])
@@ -0,0 +1,41 @@
1
+ import subprocess
2
+
3
+
4
+ def play_video(video_id, quality="720", speed="1"):
5
+
6
+ url = f"https://youtube.com/watch?v={video_id}"
7
+
8
+ if quality == "best":
9
+ fmt = "best"
10
+ else:
11
+ fmt = f"bestvideo[height<={quality}]+bestaudio/best"
12
+
13
+ subprocess.run([
14
+ "mpv",
15
+ "--ytdl-format=" + fmt,
16
+ "--speed=" + speed,
17
+ url
18
+ ])
19
+
20
+
21
+
22
+ def play_audio(video_id):
23
+
24
+ url = f"https://youtube.com/watch?v={video_id}"
25
+
26
+ subprocess.run([
27
+ "mpv",
28
+ "--force-window=yes",
29
+ "--no-video",
30
+ url
31
+ ])
32
+
33
+
34
+ def download_video(video_id):
35
+
36
+ url = f"https://youtube.com/watch?v={video_id}"
37
+
38
+ subprocess.run([
39
+ "yt-dlp",
40
+ url
41
+ ])
@@ -0,0 +1,20 @@
1
+ import yt_dlp
2
+
3
+ RESULT_LIMIT = 30
4
+
5
+
6
+ def search_youtube(query):
7
+
8
+ ydl_opts = {
9
+ "quiet": True,
10
+ "extract_flat": "in_playlist",
11
+ "skip_download": True
12
+ }
13
+
14
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
15
+ info = ydl.extract_info(
16
+ f"ytsearch{RESULT_LIMIT}:{query}",
17
+ download=False
18
+ )
19
+
20
+ return info.get("entries", [])
@@ -0,0 +1,57 @@
1
+ import subprocess
2
+
3
+
4
+ def select_video(lines):
5
+
6
+ preview_script = r'''
7
+ id=$(echo {} | awk -F'|' '{print $NF}')
8
+
9
+ json=$(yt-dlp --skip-download --dump-json https://youtube.com/watch?v=$id 2>/dev/null)
10
+
11
+ title=$(echo "$json" | jq -r ".title")
12
+ channel=$(echo "$json" | jq -r ".channel")
13
+ views=$(echo "$json" | jq -r ".view_count")
14
+ duration=$(echo "$json" | jq -r ".duration")
15
+ date=$(echo "$json" | jq -r ".upload_date")
16
+
17
+ # format duration
18
+ h=$((duration / 3600))
19
+ m=$(((duration % 3600) / 60))
20
+ s=$((duration % 60))
21
+
22
+ if [ "$h" -gt 0 ]; then
23
+ duration_fmt=$(printf "%d:%02d:%02d" "$h" "$m" "$s")
24
+ else
25
+ duration_fmt=$(printf "%d:%02d" "$m" "$s")
26
+ fi
27
+
28
+ # format date YYYYMMDD -> YYYY-MM-DD
29
+ date_fmt=$(echo "$date" | sed 's/\(....\)\(..\)\(..\)/\1-\2-\3/')
30
+
31
+ echo "Title: $title"
32
+ echo
33
+ echo "Channel: $channel"
34
+ echo "Views: $views"
35
+ echo "Duration: $duration_fmt"
36
+ echo "Upload Date: $date_fmt"
37
+ '''
38
+
39
+ fzf = subprocess.Popen(
40
+ [
41
+ "fzf",
42
+ "--height=90%",
43
+ "--layout=reverse",
44
+ "--border",
45
+ "--prompt=YouTube > ",
46
+ "--preview",
47
+ preview_script,
48
+ "--preview-window=right:60%"
49
+ ],
50
+ stdin=subprocess.PIPE,
51
+ stdout=subprocess.PIPE,
52
+ text=True
53
+ )
54
+
55
+ stdout, _ = fzf.communicate("\n".join(lines))
56
+
57
+ return stdout.strip()
@@ -0,0 +1,35 @@
1
+ def format_duration(seconds):
2
+ """
3
+ Convert seconds to YouTube-like format:
4
+ HH:MM:SS or MM:SS
5
+ """
6
+
7
+ if seconds is None:
8
+ return "??:??"
9
+
10
+ seconds = int(seconds)
11
+
12
+ minutes, sec = divmod(seconds, 60)
13
+ hours, minutes = divmod(minutes, 60)
14
+
15
+ if hours > 0:
16
+ return f"{hours}:{minutes:02d}:{sec:02d}"
17
+
18
+ return f"{minutes}:{sec:02d}"
19
+
20
+
21
+ def format_views(v):
22
+ """
23
+ Convert views to readable format
24
+ """
25
+
26
+ if not v:
27
+ return "0"
28
+
29
+ if v >= 1_000_000:
30
+ return f"{v/1_000_000:.1f}M"
31
+
32
+ if v >= 1_000:
33
+ return f"{v/1_000:.1f}K"
34
+
35
+ return str(v)
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: ytplay-cli
3
+ Version: 0.4.0
4
+ Summary: Search and play YouTube videos from the terminal using mpv and fzf
5
+ Author: Naveen
6
+ Project-URL: Homepage, https://github.com/naveen07-c/yt-cli
7
+ Project-URL: Repository, https://github.com/naveen07-c/yt-cli
8
+ Project-URL: Issues, https://github.com/naveen07-c/yt-cli/issues
9
+ Keywords: youtube,cli,terminal,mpv,fzf
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Environment :: Console
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: yt-dlp
17
+
18
+ # yt-cli
19
+
20
+ Search, preview, and play YouTube videos directly from the terminal.
21
+
22
+ `yt-cli` is a lightweight command-line tool that allows users to search YouTube, browse results interactively, preview video information, and play or download videos — all without leaving the terminal.
23
+
24
+ The tool combines several powerful utilities:
25
+
26
+ - **yt-dlp** → fetches YouTube metadata and streams
27
+ - **fzf** → interactive fuzzy search interface
28
+ - **mpv** → video/audio playback
29
+ - **jq** → JSON parsing for preview data
30
+
31
+ This makes it possible to build a fast, terminal-based YouTube experience.
32
+
33
+ ---
34
+
35
+ # Features
36
+
37
+ - Search YouTube from the terminal
38
+ - Interactive video selection using **fzf**
39
+ - Preview video metadata while browsing results
40
+ - Play videos directly with **mpv**
41
+ - Play **audio-only** mode
42
+ - Download videos using **yt-dlp**
43
+ - Open selected video in the browser
44
+ - Choose playback **quality**
45
+ - Adjust playback **speed**
46
+ - Maintain **search history**
47
+ - Clean CLI interface designed for Linux environments
48
+
49
+ ---
50
+
51
+ # Installation
52
+
53
+ The recommended installation method is **pipx**.
54
+
55
+ `pipx` installs Python CLI applications globally but keeps dependencies isolated.
56
+
57
+ Install pipx if not installed:
58
+
59
+ ```bash
60
+ sudo apt install pipx
61
+ pipx ensurepath
@@ -0,0 +1,17 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ ytcli/__init__.py
5
+ ytcli/cli.py
6
+ ytcli/config.py
7
+ ytcli/deps.py
8
+ ytcli/player.py
9
+ ytcli/search.py
10
+ ytcli/ui.py
11
+ ytcli/utils.py
12
+ ytplay_cli.egg-info/PKG-INFO
13
+ ytplay_cli.egg-info/SOURCES.txt
14
+ ytplay_cli.egg-info/dependency_links.txt
15
+ ytplay_cli.egg-info/entry_points.txt
16
+ ytplay_cli.egg-info/requires.txt
17
+ ytplay_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ yt = ytcli.cli:main
@@ -0,0 +1 @@
1
+ yt-dlp
@@ -0,0 +1 @@
1
+ ytcli