Skip to content

Commit 26a8f7b

Browse files
authored
feat: Add support for alternative video qualities in messages.
New Features: - Introduce support for alternative video qualities in the message handling, allowing for different video resolutions and codecs. CI: - Update the CI workflow to use the latest version of the 'create-pull-request' action and specify the version for 'setup-rye'. Documentation: - Add documentation for the new 'AlternativeVideo' class, describing its attributes and usage.
1 parent afae6ed commit 26a8f7b

File tree

10 files changed

+237
-86
lines changed

10 files changed

+237
-86
lines changed

.github/dependabot.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "pip"
4+
directory: "/"
5+
schedule:
6+
interval: "daily"
7+
time: "22:00"
8+
open-pull-requests-limit: 10

.github/workflows/error_scrape.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
python sort.py sort
2323
2424
- name: Open Pull Request
25-
uses: peter-evans/create-pull-request@v4
25+
uses: peter-evans/create-pull-request@v7
2626
with:
2727
commit-message: >
2828
Update unknown_errors
@@ -32,4 +32,4 @@ jobs:
3232
This is an automated PR. Please check the diff, and the action logs, to check for any funky behaviour.
3333
branch: automated/api-error-scrape
3434
labels: automated
35-
delete-branch: true
35+
delete-branch: true

.github/workflows/format_and_test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020

2121
- name: Install the latest version of rye
2222
uses: eifinger/setup-rye@v4
23+
with:
24+
version: 'latest'
2325

2426
- name: Install dependencies
2527
run: |

.github/workflows/tag_and_publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
4242
- name: Create New Release
4343
if: env.RELEASE_EXISTS == 'false'
44-
uses: actions/create-release@v1
44+
uses: comnoco/create-release-action@v2
4545
env:
4646
GITHUB_TOKEN: ${{ secrets.GX_TOKEN }}
4747
with:

compiler/docs/compiler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ def get_title_list(s: str) -> list:
476476
AvailableEffect
477477
Document
478478
Animation
479+
AlternativeVideo
479480
LabeledPrice
480481
Video
481482
Voice

compiler/errors/sort.py

Lines changed: 46 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,67 @@
1+
# ruff: noqa: E741
12
from __future__ import annotations
23

34
import csv
45
import re
56
import sys
67
from pathlib import Path
78

8-
import requests # requests==2.28.1
9+
import requests
910

11+
if len(sys.argv) != 2:
12+
sys.exit(1)
1013

11-
def sort_tsv_files():
12-
"""Sorts TSV files in the 'source' directory by their keys."""
14+
if sys.argv[1] == "sort":
1315
for p in Path("source").glob("*.tsv"):
14-
with p.open() as f: # Replaced open() with Path.open()
16+
with p.open() as f:
1517
reader = csv.reader(f, delimiter="\t")
16-
dct = {k: v for k, v in reader if k != "id"} # Dictionary comprehension
18+
dct = {k: v for k, v in reader if k != "id"}
1719
keys = sorted(dct)
1820

19-
with p.open("w") as f: # Replaced open() with Path.open()
21+
with p.open("w") as f:
2022
f.write("id\tmessage\n")
2123
for i, item in enumerate(keys, start=1):
2224
f.write(f"{item}\t{dct[item]}")
2325
if i != len(keys):
2426
f.write("\n")
2527

28+
elif sys.argv[1] == "scrape":
29+
b = "https://core.telegram.org"
30+
c = "/api/errors"
31+
a = requests.get(b + c)
32+
d = a.text
33+
e = r"\<a\ href\=\"(.*)\"\>here.*\<\/a\>"
34+
f = re.search(e, d)
35+
if f:
36+
a = requests.get(b + f.group(1))
37+
d = a.json()
38+
e = d.get("errors", [])
39+
for h in e:
40+
dct = {}
41+
j = d.get("errors").get(h)
42+
for k in j:
43+
if k.endswith("_*"):
44+
continue
45+
g = d.get("descriptions")
46+
l = g.get(k)
47+
m = k.replace("_%d", "_X")
48+
l = l.replace("%d", "{value}")
49+
l = l.replace("&raquo;", "»")
50+
l = l.replace("&laquo;", "«")
51+
l = l.replace("](/api/", f"]({b}/api/")
52+
dct[m] = l
53+
54+
for p in Path("source/").glob(f"{h}*.tsv"):
55+
with p.open() as f:
56+
reader = csv.reader(f, delimiter="\t")
57+
for k, v in reader:
58+
if k != "id":
59+
dct[k] = v
2660

27-
def scrape_telegram_errors():
28-
"""Scrapes error data from Telegram's API and updates corresponding TSV files."""
29-
base_url = "https://corefork.telegram.org"
30-
api_errors_path = "/api/errors"
31-
response = requests.get(base_url + api_errors_path)
32-
if response.status_code != 200:
33-
print("Failed to fetch errors from Telegram API")
34-
sys.exit(1)
35-
36-
# Extract link to additional error details
37-
html_content = response.text
38-
link_pattern = r'<a href="(.*)">here.*</a>'
39-
match = re.search(link_pattern, html_content)
40-
if not match:
41-
print("No error details found")
42-
return
43-
44-
error_url = base_url + match[1]
45-
error_response = requests.get(error_url)
46-
if error_response.status_code != 200:
47-
print("Failed to fetch detailed errors")
48-
sys.exit(1)
49-
50-
errors_data = error_response.json()
51-
process_errors(errors_data, base_url)
52-
53-
54-
def process_errors(errors_data: dict, base_url: str):
55-
"""Processes errors and updates the TSV files with error messages."""
56-
error_entries = errors_data.get("errors", {})
57-
descriptions = errors_data.get("descriptions", {})
58-
59-
for error_code, error_list in error_entries.items():
60-
dct = {}
61-
62-
# Process each error
63-
for error in error_list:
64-
if error.endswith("_*"):
65-
continue
66-
67-
description = descriptions.get(error, "")
68-
description = format_description(description, base_url)
69-
formatted_key = error.replace("_%d", "_X")
70-
dct[formatted_key] = description
71-
72-
update_tsv_files(error_code, dct)
73-
74-
75-
def format_description(description: str, base_url: str) -> str:
76-
"""Formats the description by replacing specific characters and links."""
77-
description = description.replace("%d", "{value}")
78-
description = description.replace("&raquo;", "»")
79-
description = description.replace("&laquo;", "«")
80-
return description.replace("](/api/", f"]({base_url}/api/")
81-
82-
83-
def update_tsv_files(error_code: str, dct: dict):
84-
"""Updates the TSV files for the given error code with the provided dictionary of error messages."""
85-
for p in Path("source/").glob(f"{error_code}*.tsv"):
86-
with p.open() as f: # Replaced open() with Path.open()
87-
reader = csv.reader(f, delimiter="\t")
88-
dct.update(
89-
{k: v for k, v in reader if k != "id"}
90-
) # Dictionary comprehension
91-
92-
keys = sorted(dct)
93-
94-
with p.open("w") as f: # Replaced open() with Path.open()
95-
f.write("id\tmessage\n")
96-
for i, item in enumerate(keys, start=1):
97-
f.write(f"{item}\t{dct[item]}\n")
98-
61+
keys = sorted(dct)
9962

100-
if sys.argv[1] == "sort":
101-
sort_tsv_files()
102-
elif sys.argv[1] == "scrape":
103-
scrape_telegram_errors()
63+
for p in Path("source/").glob(f"{h}*.tsv"):
64+
with p.open("w") as f:
65+
f.write("id\tmessage\n")
66+
for i, item in enumerate(keys, start=1):
67+
f.write(f"{item}\t{dct[item]}\n")

pyrogram/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
__version__ = "v0.0.192"
3+
__version__ = "v0.1.192"
44
__license__ = "MIT License"
55

66
from concurrent.futures.thread import ThreadPoolExecutor

pyrogram/types/messages_and_media/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from .alternative_video import AlternativeVideo
34
from .animation import Animation
45
from .audio import Audio
56
from .available_effect import AvailableEffect
@@ -62,6 +63,7 @@
6263
from .web_page_preview import WebPagePreview
6364

6465
__all__ = [
66+
"AlternativeVideo",
6567
"Animation",
6668
"Audio",
6769
"AvailableEffect",
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import pyrogram
6+
from pyrogram import raw, types, utils
7+
from pyrogram.file_id import (
8+
FileId,
9+
FileType,
10+
FileUniqueId,
11+
FileUniqueType,
12+
)
13+
from pyrogram.types.object import Object
14+
15+
if TYPE_CHECKING:
16+
from datetime import datetime
17+
18+
19+
class AlternativeVideo(Object):
20+
"""Describes an alternative reencoded quality of a video file.
21+
22+
Parameters:
23+
file_id (``str``):
24+
Identifier for this file, which can be used to download or reuse the file.
25+
26+
file_unique_id (``str``):
27+
Unique identifier for this file, which is supposed to be the same over time and for different accounts.
28+
Can't be used to download or reuse the file.
29+
30+
width (``int``):
31+
Video width as defined by sender.
32+
33+
height (``int``):
34+
Video height as defined by sender.
35+
36+
codec (``str``):
37+
Codec used for video file encoding, for example, "h264", "h265", or "av1".
38+
39+
duration (``int``):
40+
Duration of the video in seconds as defined by sender.
41+
42+
file_name (``str``, *optional*):
43+
Video file name.
44+
45+
mime_type (``str``, *optional*):
46+
Mime type of a file as defined by sender.
47+
48+
file_size (``int``, *optional*):
49+
File size.
50+
51+
supports_streaming (``bool``, *optional*):
52+
True, if the video was uploaded with streaming support.
53+
54+
date (:py:obj:`~datetime.datetime`, *optional*):
55+
Date the video was sent.
56+
57+
thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
58+
Video thumbnails.
59+
60+
"""
61+
62+
def __init__(
63+
self,
64+
*,
65+
client: pyrogram.Client = None,
66+
file_id: str,
67+
file_unique_id: str,
68+
width: int,
69+
height: int,
70+
codec: str,
71+
duration: int,
72+
file_name: str | None = None,
73+
mime_type: str | None = None,
74+
file_size: int | None = None,
75+
supports_streaming: bool | None = None,
76+
date: datetime | None = None,
77+
thumbs: list[types.Thumbnail] | None = None,
78+
):
79+
super().__init__(client)
80+
self.file_id = file_id
81+
self.file_unique_id = file_unique_id
82+
self.width = width
83+
self.height = height
84+
self.codec = codec
85+
self.duration = duration
86+
self.file_name = file_name
87+
self.mime_type = mime_type
88+
self.file_size = file_size
89+
self.supports_streaming = supports_streaming
90+
self.date = date
91+
self.thumbs = thumbs
92+
93+
@staticmethod
94+
def _parse(
95+
client: pyrogram.Client,
96+
video: raw.types.Document,
97+
video_attributes: raw.types.DocumentAttributeVideo,
98+
file_name: str,
99+
) -> AlternativeVideo:
100+
file_id = (
101+
FileId(
102+
file_type=FileType.VIDEO,
103+
dc_id=video.dc_id,
104+
media_id=video.id,
105+
access_hash=video.access_hash,
106+
file_reference=video.file_reference,
107+
).encode()
108+
if video
109+
else None
110+
)
111+
112+
file_unique_id = (
113+
FileUniqueId(
114+
file_unique_type=FileUniqueType.DOCUMENT, media_id=video.id
115+
).encode()
116+
if video
117+
else None
118+
)
119+
120+
return AlternativeVideo(
121+
client=client,
122+
file_id=file_id,
123+
file_unique_id=file_unique_id,
124+
width=video_attributes.w if video_attributes else 0,
125+
height=video_attributes.h if video_attributes else 0,
126+
codec=video_attributes.video_codec if video_attributes else "",
127+
duration=video_attributes.duration if video_attributes else 0,
128+
file_name=file_name,
129+
mime_type=video.mime_type if video else "",
130+
supports_streaming=video_attributes.supports_streaming
131+
if video_attributes
132+
else False,
133+
file_size=video.size if video else 0,
134+
date=utils.timestamp_to_datetime(video.date) if video else None,
135+
thumbs=types.Thumbnail._parse(client, video) if video else None,
136+
)

0 commit comments

Comments
 (0)