AbhiCalls 2.0.0__py3-none-any.whl → 2.9.8__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/_engine/__init__.py +1 -0
- AbhiCalls/_engine/native.py +44 -0
- AbhiCalls/controller.py +23 -7
- AbhiCalls/models.py +7 -2
- AbhiCalls/yt.py +30 -14
- {abhicalls-2.0.0.dist-info → abhicalls-2.9.8.dist-info}/METADATA +1 -1
- abhicalls-2.9.8.dist-info/RECORD +14 -0
- abhicalls-2.0.0.dist-info/RECORD +0 -12
- {abhicalls-2.0.0.dist-info → abhicalls-2.9.8.dist-info}/WHEEL +0 -0
- {abhicalls-2.0.0.dist-info → abhicalls-2.9.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .native import _NativeEngine
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from pytgcalls import PyTgCalls, filters
|
|
2
|
+
from pytgcalls.types import MediaStream, AudioQuality, StreamEnded
|
|
3
|
+
|
|
4
|
+
class _NativeEngine:
|
|
5
|
+
def __init__(self, app):
|
|
6
|
+
self._core = PyTgCalls(app)
|
|
7
|
+
self.on_end = None
|
|
8
|
+
|
|
9
|
+
@self._core.on_update(filters.stream_end())
|
|
10
|
+
async def _ended(_, update: StreamEnded):
|
|
11
|
+
if self.on_end:
|
|
12
|
+
await self.on_end(update.chat_id)
|
|
13
|
+
|
|
14
|
+
async def start(self):
|
|
15
|
+
await self._core.start()
|
|
16
|
+
|
|
17
|
+
async def play(self, chat_id, stream):
|
|
18
|
+
await self._core.play(
|
|
19
|
+
chat_id,
|
|
20
|
+
MediaStream(
|
|
21
|
+
media_path=stream,
|
|
22
|
+
audio_parameters=AudioQuality.STUDIO,
|
|
23
|
+
video_flags=MediaStream.Flags.IGNORE,
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
async def stop(self, chat_id):
|
|
28
|
+
await self._core.leave_call(chat_id)
|
|
29
|
+
|
|
30
|
+
async def pause(self, chat_id):
|
|
31
|
+
await self._core.pause(chat_id)
|
|
32
|
+
|
|
33
|
+
async def resume(self, chat_id):
|
|
34
|
+
await self._core.resume(chat_id)
|
|
35
|
+
|
|
36
|
+
async def mute(self, chat_id):
|
|
37
|
+
await self._core.mute(chat_id)
|
|
38
|
+
|
|
39
|
+
async def unmute(self, chat_id):
|
|
40
|
+
await self._core.unmute(chat_id)
|
|
41
|
+
|
|
42
|
+
async def change_volume(self, chat_id, volume: int = 200):
|
|
43
|
+
await self._core.change_volume(chat_id, volume)
|
|
44
|
+
|
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
|
|
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
|
-
|
|
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
|
-
|
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
|
-
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
def extract_video_id(url: str):
|
|
18
19
|
try:
|
|
19
20
|
if "watch?v=" in url:
|
|
20
|
-
|
|
21
|
+
return url.split("watch?v=")[1].split("&")[0]
|
|
21
22
|
elif "youtu.be/" in url:
|
|
22
|
-
|
|
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
|
-
"
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
82
|
-
"
|
|
97
|
+
"video_id": vid,
|
|
98
|
+
"thumb": i.get("thumbnail") or yt_thumbnail(i["url"]),
|
|
83
99
|
"stream": stream,
|
|
84
100
|
}]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
AbhiCalls/__init__.py,sha256=8Bgye84bSUJVFLhm2HPc2piO3YHZb67EihvJlGZxvDs,134
|
|
2
|
+
AbhiCalls/controller.py,sha256=KYppQPpWhTpWj-gwmZqyGgX5liZHAaWRIMqksZPzXzU,3241
|
|
3
|
+
AbhiCalls/models.py,sha256=fwMdHKE0z6qB98nHJ8E1uYU3JSk25LGxaaeAz1JkAH0,791
|
|
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=h0QRNK9sy-5wIDueiy-uMUxruV7z5m3xjUyRPoFT6_s,2594
|
|
9
|
+
AbhiCalls/_engine/__init__.py,sha256=pTqDs11-vYr-wuwtd7h3EkspDquGu84jWfN54ajl0kM,34
|
|
10
|
+
AbhiCalls/_engine/native.py,sha256=KJajnL9urwrx0PaOEJj4-pGGnhz4X9FDI6rTGrbCLoI,1272
|
|
11
|
+
abhicalls-2.9.8.dist-info/METADATA,sha256=VzXnK3k_eeFbdczlR7RAFG8jmLrdA53aHFRUB1NQEvc,1209
|
|
12
|
+
abhicalls-2.9.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
abhicalls-2.9.8.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
|
|
14
|
+
abhicalls-2.9.8.dist-info/RECORD,,
|
abhicalls-2.0.0.dist-info/RECORD
DELETED
|
@@ -1,12 +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-2.0.0.dist-info/METADATA,sha256=fwxTMFzaxcSalzSGhILYJ5dinSo0qxx1KtHyVHsv8L4,1209
|
|
10
|
-
abhicalls-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
11
|
-
abhicalls-2.0.0.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
|
|
12
|
-
abhicalls-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|