AbhiCalls 2.9.0__py3-none-any.whl → 2.9.9__py3-none-any.whl

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.
AbhiCalls/Start.py ADDED
@@ -0,0 +1,29 @@
1
+ import requests
2
+ from colorama import init, Fore, Style
3
+ from AbhiCalls import __version__, __author__
4
+
5
+ init(autoreset=True)
6
+
7
+ def check_latest_version(pkg_name="AbhiCalls"):
8
+ try:
9
+ response = requests.get(f"https://pypi.org/pypi/{pkg_name}/json", timeout=3)
10
+ if response.status_code == 200:
11
+ return response.json()["info"]["version"]
12
+ except Exception:
13
+ return None
14
+
15
+ def print_startup_message():
16
+ print(Fore.GREEN + "✅ AbhiCalls started...\n")
17
+
18
+ print(Fore.CYAN + f"Version : {__version__}")
19
+ print(Fore.CYAN + f"Author : {__author__}")
20
+ print(Fore.CYAN + "License : Abhishek Special")
21
+ print(Fore.CYAN + "GitHub : https://github.com/YouTubeMusicAPI/AbhiCalls")
22
+ print(Fore.CYAN + "Telegram : https://t.me/WhyAbhishek")
23
+
24
+ latest = check_latest_version("AbhiCalls")
25
+ if latest and latest != __version__:
26
+ print(Fore.YELLOW + "\nUpdate Available!")
27
+ print(Fore.YELLOW + f"New AbhiCalls v{latest} is now available!")
28
+ else:
29
+ print(Fore.GREEN + "\nYou are using the latest version!")
AbhiCalls/__init__.py CHANGED
@@ -1,5 +1,15 @@
1
1
  from AbhiCalls.vc import VoiceEngine
2
2
  from AbhiCalls.runtime import idle
3
3
  from .player import Player
4
+ from .plugins import Plugin
4
5
 
5
6
  __all__ = ["VoiceEngine", "idle"]
7
+
8
+ __version__ = "2.9.9"
9
+ __author__ = "ABHISHEK THAKUR"
10
+
11
+ try:
12
+ from .Start import print_startup_message
13
+ print_startup_message()
14
+ except Exception:
15
+ pass
AbhiCalls/controller.py CHANGED
@@ -2,6 +2,7 @@ from AbhiCalls.yt import resolve_query
2
2
  from AbhiCalls.models import Song
3
3
  from AbhiCalls.player import Player
4
4
 
5
+
5
6
  class VoiceController:
6
7
  def __init__(self, engine):
7
8
  self.engine = engine
@@ -19,8 +20,11 @@ class VoiceController:
19
20
  try:
20
21
  await fn(*args)
21
22
  except Exception as e:
22
- print(f"[Plugin:{p.name}] {name} error:", e)
23
+ print(f"[Plugin:{getattr(p, 'name', p)}] {name} error:", e)
23
24
 
25
+ # =========================
26
+ # PLAY
27
+ # =========================
24
28
  async def play(self, chat_id, query, requested_by):
25
29
  results = await resolve_query(query)
26
30
  if not results:
@@ -37,6 +41,8 @@ class VoiceController:
37
41
  views=data["views"],
38
42
  stream=data["stream"],
39
43
  requested_by=requested_by,
44
+ video_id=data.get("video_id"),
45
+ thumb=data.get("thumb"),
40
46
  )
41
47
 
42
48
  pos = await self.player.play(chat_id, song)
@@ -46,11 +52,15 @@ class VoiceController:
46
52
  first_pos = pos
47
53
  last_song = song
48
54
 
49
- if first_pos == 1:
55
+ # 🔥 First song actually started
56
+ if first_pos == 1 and last_song:
50
57
  await self._hook("on_song_start", chat_id, last_song)
51
58
 
52
59
  return last_song, first_pos
53
60
 
61
+ # =========================
62
+ # CONTROLS
63
+ # =========================
54
64
  async def skip(self, chat_id):
55
65
  await self.player.skip(chat_id)
56
66
 
@@ -74,7 +84,6 @@ class VoiceController:
74
84
 
75
85
  async def volume(self, chat_id, volume: int):
76
86
  await self.player.volume(chat_id, volume)
77
-
78
87
 
79
88
  def loop(self, chat_id, count=None):
80
89
  return self.player.set_loop(chat_id, count)
@@ -82,15 +91,22 @@ class VoiceController:
82
91
  def eta(self, chat_id):
83
92
  return self.player.eta(chat_id)
84
93
 
94
+ # =========================
95
+ # AUTO-SKIP (STREAM END)
96
+ # =========================
85
97
  async def _on_end(self, chat_id):
86
98
  q = self.player.queues.get(chat_id)
87
- song = q.current() if q else None
88
- if song:
89
- await self._hook("on_song_end", chat_id, song)
90
99
 
100
+ # old song end hook
101
+ old_song = q.current() if q else None
102
+ if old_song:
103
+ await self._hook("on_song_end", chat_id, old_song)
104
+
105
+ # move to next song
91
106
  await self.player.skip(chat_id)
92
107
 
108
+ q = self.player.queues.get(chat_id)
93
109
  next_song = q.current() if q else None
110
+
94
111
  if next_song:
95
112
  await self._hook("on_song_start", chat_id, next_song)
96
-
AbhiCalls/models.py CHANGED
@@ -8,6 +8,8 @@ class Song:
8
8
  stream,
9
9
  requested_by,
10
10
  loop_left=0,
11
+ video_id=None,
12
+ thumb=None,
11
13
  ):
12
14
  self.title = title
13
15
  self.url = url
@@ -16,14 +18,17 @@ class Song:
16
18
  self.stream = stream
17
19
  self.requested_by = requested_by
18
20
  self.loop_left = loop_left
21
+
22
+ self.video_id = video_id
23
+ self.thumb = thumb
24
+
19
25
  self.duration_sec = self._to_seconds(duration)
20
26
 
21
27
  def _to_seconds(self, d):
22
28
  try:
23
- if ":" in d:
29
+ if isinstance(d, str) and ":" in d:
24
30
  m, s = map(int, d.split(":"))
25
31
  return m * 60 + s
26
32
  return int(d)
27
33
  except Exception:
28
34
  return 0
29
-
@@ -0,0 +1 @@
1
+ from .base import Plugin
@@ -0,0 +1,57 @@
1
+ class Plugin:
2
+ name = "base"
3
+
4
+ def __init__(self, app):
5
+ self.app = app # pyrogram Client
6
+
7
+ # =========================
8
+ # ▶️ NOW PLAYING / AUTO-SKIP
9
+ # =========================
10
+ async def on_song_start(self, chat_id, song):
11
+ caption = (
12
+ "▶️ **Now Playing**\n\n"
13
+ f"🎵 **Title:** {song.title}\n"
14
+ f"⏱ **Duration:** {song.duration}\n"
15
+ f"🙋 **Requested by:** {song.requested_by}"
16
+ )
17
+
18
+ if song.thumb:
19
+ await self.app.send_photo(
20
+ chat_id,
21
+ photo=song.thumb,
22
+ caption=caption
23
+ )
24
+ else:
25
+ await self.app.send_message(chat_id, caption)
26
+
27
+ # =========================
28
+ # ⏹ SONG END (optional hook)
29
+ # =========================
30
+ async def on_song_end(self, chat_id, song):
31
+ # Usually no message needed here
32
+ pass
33
+
34
+ # =========================
35
+ # ➕ ADDED TO QUEUE
36
+ # =========================
37
+ async def on_queue_add(self, chat_id, song, position):
38
+ # First song ka message already on_song_start bhej dega
39
+ if position == 1:
40
+ return
41
+
42
+ caption = (
43
+ "➕ **Added to Queue**\n\n"
44
+ f"🎵 **Title:** {song.title}\n"
45
+ f"⏱ **Duration:** {song.duration}\n"
46
+ f"🙋 **Requested by:** {song.requested_by}\n"
47
+ f"📍 **Position:** {position}"
48
+ )
49
+
50
+ if song.thumb:
51
+ await self.app.send_photo(
52
+ chat_id,
53
+ photo=song.thumb,
54
+ caption=caption
55
+ )
56
+ else:
57
+ await self.app.send_message(chat_id, caption)
AbhiCalls/yt.py CHANGED
@@ -6,6 +6,7 @@ from YouTubeMusic.Playlist import get_playlist_songs
6
6
  PLAYLIST_REGEX = re.compile(r"(list=)")
7
7
  YOUTUBE_REGEX = re.compile(r"(youtube\.com|youtu\.be|music\.youtube\.com)")
8
8
 
9
+
9
10
  def sec_to_mmss(sec):
10
11
  try:
11
12
  sec = int(sec)
@@ -13,18 +14,23 @@ def sec_to_mmss(sec):
13
14
  except Exception:
14
15
  return "Unknown"
15
16
 
16
- def yt_thumbnail(url: str):
17
- """Extract thumbnail from YouTube URL"""
17
+
18
+ def extract_video_id(url: str):
18
19
  try:
19
20
  if "watch?v=" in url:
20
- vid = url.split("watch?v=")[1].split("&")[0]
21
+ return url.split("watch?v=")[1].split("&")[0]
21
22
  elif "youtu.be/" in url:
22
- vid = url.split("youtu.be/")[1].split("?")[0]
23
- else:
24
- return None
25
- return f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"
23
+ return url.split("youtu.be/")[1].split("?")[0]
26
24
  except Exception:
25
+ pass
26
+ return None
27
+
28
+
29
+ def yt_thumbnail(url: str):
30
+ vid = extract_video_id(url)
31
+ if not vid:
27
32
  return None
33
+ return f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"
28
34
 
29
35
 
30
36
  async def resolve_query(query: str):
@@ -33,37 +39,45 @@ async def resolve_query(query: str):
33
39
  # 🎵 PLAYLIST
34
40
  if PLAYLIST_REGEX.search(query):
35
41
  playlist = await get_playlist_songs(query)
42
+
36
43
  for item in playlist:
37
44
  stream = get_stream(item["url"])
38
45
  if not stream:
39
46
  continue
40
47
 
48
+ vid = extract_video_id(item["url"])
49
+
41
50
  songs.append({
42
51
  "title": item.get("title", "Unknown"),
43
52
  "url": item["url"],
44
53
  "duration": sec_to_mmss(item.get("duration", "0")),
45
- "views": "Unknown",
46
- "thumbnail": yt_thumbnail(item["url"]),
54
+ "views": item.get("views", "Unknown"),
55
+ "video_id": vid,
56
+ "thumb": yt_thumbnail(item["url"]),
47
57
  "stream": stream,
48
58
  })
59
+
49
60
  return songs or None
50
61
 
51
- # 🔗 DIRECT YT LINK
62
+ # 🔗 DIRECT YOUTUBE LINK
52
63
  if YOUTUBE_REGEX.search(query):
53
64
  stream = get_stream(query)
54
65
  if not stream:
55
66
  return None
56
67
 
68
+ vid = extract_video_id(query)
69
+
57
70
  return [{
58
71
  "title": "YouTube Audio",
59
72
  "url": query,
60
73
  "duration": "Unknown",
61
74
  "views": "Unknown",
62
- "thumbnail": yt_thumbnail(query),
75
+ "video_id": vid,
76
+ "thumb": yt_thumbnail(query),
63
77
  "stream": stream,
64
78
  }]
65
79
 
66
- # 🔍 SEARCH
80
+ # 🔍 SEARCH QUERY
67
81
  res = await Search(query, limit=1)
68
82
  if not res or not res.get("main_results"):
69
83
  return None
@@ -73,12 +87,14 @@ async def resolve_query(query: str):
73
87
  if not stream:
74
88
  return None
75
89
 
90
+ vid = extract_video_id(i["url"])
91
+
76
92
  return [{
77
93
  "title": i["title"],
78
94
  "url": i["url"],
79
95
  "duration": i.get("duration", "Unknown"),
80
96
  "views": i.get("views", "Unknown"),
81
- # 🔥 thumbnail from search (fallback safe)
82
- "thumbnail": i.get("thumbnail") or yt_thumbnail(i["url"]),
97
+ "video_id": vid,
98
+ "thumb": i.get("thumbnail") or yt_thumbnail(i["url"]),
83
99
  "stream": stream,
84
100
  }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbhiCalls
3
- Version: 2.9.0
3
+ Version: 2.9.9
4
4
  Summary: Telegram Voice Chat Music Engine with Queue, Loop, ETA and Player controls
5
5
  Home-page: https://github.com/YouTubeMusicAPI/TgCall
6
6
  Author: Abhishek Thakur
@@ -0,0 +1,17 @@
1
+ AbhiCalls/Start.py,sha256=Z4Kn3p8elM9Sgo3H_7EBP8dfHknsjgiaUaEs1GEqcnY,1068
2
+ AbhiCalls/__init__.py,sha256=uoXjElr_JmTjFdMHzNKBQbNzwGz4m1j9r2ChxrXouAE,322
3
+ AbhiCalls/controller.py,sha256=KYppQPpWhTpWj-gwmZqyGgX5liZHAaWRIMqksZPzXzU,3241
4
+ AbhiCalls/models.py,sha256=fwMdHKE0z6qB98nHJ8E1uYU3JSk25LGxaaeAz1JkAH0,791
5
+ AbhiCalls/player.py,sha256=UlAWmhZqHFjtAplA9-6lXFkVAu4pyEWDGbF8Wj9KYkM,2286
6
+ AbhiCalls/queue.py,sha256=iRrTvt1rU-diarSBCfxjeY-4uJ97oUsvDqtPpbB4UQY,1212
7
+ AbhiCalls/runtime.py,sha256=4_WmHirGwNOyn1Iuzbhecb_ao7qMDh1erFzp0ZaLze0,966
8
+ AbhiCalls/vc.py,sha256=ld3C4-QNDrHBQPrNzIuNTcu7zk1pVOo9rvrQBUsTI8s,309
9
+ AbhiCalls/yt.py,sha256=h0QRNK9sy-5wIDueiy-uMUxruV7z5m3xjUyRPoFT6_s,2594
10
+ AbhiCalls/_engine/__init__.py,sha256=pTqDs11-vYr-wuwtd7h3EkspDquGu84jWfN54ajl0kM,34
11
+ AbhiCalls/_engine/native.py,sha256=KJajnL9urwrx0PaOEJj4-pGGnhz4X9FDI6rTGrbCLoI,1272
12
+ AbhiCalls/plugins/__init__.py,sha256=rSCdGAjFfrzz-XQIFRjOcl629mPCo17n77Fn9lxLcPI,25
13
+ AbhiCalls/plugins/base.py,sha256=wanPA2h4dHvR-YXGIsAgkAGGtGMd6acT0Gg8q35RTgI,1696
14
+ abhicalls-2.9.9.dist-info/METADATA,sha256=3iVxiggOeLOrWg6msDJOCW3DCI8kmfuYRITyb9fKdhI,1209
15
+ abhicalls-2.9.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
16
+ abhicalls-2.9.9.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
17
+ abhicalls-2.9.9.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- AbhiCalls/__init__.py,sha256=8Bgye84bSUJVFLhm2HPc2piO3YHZb67EihvJlGZxvDs,134
2
- AbhiCalls/controller.py,sha256=mdowoTCc6jpDsdljuUWdgbsCMR5NObaw3Jh6LYb-O8I,2739
3
- AbhiCalls/models.py,sha256=ybRrFbVmlpLyfmmn7lG2DpDz_Z9qcq3g9AD5Krc-UP0,664
4
- AbhiCalls/player.py,sha256=UlAWmhZqHFjtAplA9-6lXFkVAu4pyEWDGbF8Wj9KYkM,2286
5
- AbhiCalls/queue.py,sha256=iRrTvt1rU-diarSBCfxjeY-4uJ97oUsvDqtPpbB4UQY,1212
6
- AbhiCalls/runtime.py,sha256=4_WmHirGwNOyn1Iuzbhecb_ao7qMDh1erFzp0ZaLze0,966
7
- AbhiCalls/vc.py,sha256=ld3C4-QNDrHBQPrNzIuNTcu7zk1pVOo9rvrQBUsTI8s,309
8
- AbhiCalls/yt.py,sha256=QnO2LdtgVbEi8XqKEY9ZSp6a6x1AjBYVCFNB2sXFcPU,2386
9
- AbhiCalls/_engine/__init__.py,sha256=pTqDs11-vYr-wuwtd7h3EkspDquGu84jWfN54ajl0kM,34
10
- AbhiCalls/_engine/native.py,sha256=KJajnL9urwrx0PaOEJj4-pGGnhz4X9FDI6rTGrbCLoI,1272
11
- abhicalls-2.9.0.dist-info/METADATA,sha256=NY86xNzR3cQEQgMw3B_nlg6UA4qn7lki3r2L0MguvdY,1209
12
- abhicalls-2.9.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
- abhicalls-2.9.0.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
14
- abhicalls-2.9.0.dist-info/RECORD,,