aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTolmachev Igor <me@igorek.dev>2024-09-15 13:14:54 +0300
committerTolmachev Igor <me@igorek.dev>2024-09-15 13:14:54 +0300
commit19606a6ac95e83a33c1173dba3e5d6f9f8fb9f94 (patch)
tree206a68ad48ba728c46bf84137106f48f5d7d4595
parent551e69c5ebd7a227478105ac1cc91876f1c2f601 (diff)
downloadvideo2story-19606a6ac95e83a33c1173dba3e5d6f9f8fb9f94.tar.gz
video2story-19606a6ac95e83a33c1173dba3e5d6f9f8fb9f94.zip
Add README and fix package props
-rw-r--r--README.md37
-rw-r--r--pyproject.toml7
-rw-r--r--video2story/__init__.py194
-rw-r--r--video2story/__main__.py195
-rw-r--r--video2story/cutter.py10
-rw-r--r--video2story/uploader.py2
6 files changed, 245 insertions, 200 deletions
diff --git a/README.md b/README.md
index e69de29..376815c 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,37 @@
1# video2story
2
3**video2story** is a Python tool that allows you to convert and upload videos to your Telegram stories. It automatically cuts the video into segments and uploads them to your Telegram account.
4
5## Installation
6
7To install `video2story`, use pip:
8
9```bash
10pip install video2story
11```
12
13## Usage
14
15### 1. Process the video
16
17```bash
18video2story video input.mp4 output_dir/
19```
20
21First, split the video into segments. Replace `input.mp4` with the path to your video file and `output_dir/` with the directory where you want to save the processed segments:
22
23By default, the video will be cut into 60-second segments. To change that, use the `-d` option and specify the duration in seconds:
24
25### 2. Upload the processed video
26
27```bash
28video2story PHONE_NUMBER output_dir/ -p friends
29```
30
31Replace `PHONE_NUMBER` with your Telegram phone number and `output_dir/` with the directory containing the processed video segments.
32
33The `-p` option allows you to choose the privacy level for your story. For more options and detailed information, use the `--help` flag.
34
35## License
36
37This project is licensed under the GPL (GNU General Public License). See the [LICENSE](/LICENSE) file for more details.
diff --git a/pyproject.toml b/pyproject.toml
index 53270a9..5317250 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,9 +1,11 @@
1[tool.poetry] 1[tool.poetry]
2name = "video2story" 2name = "video2story"
3version = "1.0.0" 3version = "1.0.1"
4description = "Simple telegram story uploader" 4description = "Simple telegram story uploader"
5repository = "https://codeberg.org/igorechek06/video2story"
5authors = ["Tolmachev Igor <me@igorek.dev>"] 6authors = ["Tolmachev Igor <me@igorek.dev>"]
6readme = "README.md" 7readme = "README.md"
8packages = [{ include = "video2story" }]
7 9
8[tool.poetry.dependencies] 10[tool.poetry.dependencies]
9python = "^3.10" 11python = "^3.10"
@@ -14,6 +16,9 @@ mypy = "^1.11.2"
14isort = "^5.13.2" 16isort = "^5.13.2"
15black = "^24.8.0" 17black = "^24.8.0"
16 18
19[tool.poetry.scripts]
20video2story = "video2story:main"
21
17[tool.mypy] 22[tool.mypy]
18ignore_missing_imports = true 23ignore_missing_imports = true
19disallow_untyped_defs = true 24disallow_untyped_defs = true
diff --git a/video2story/__init__.py b/video2story/__init__.py
index 77e4018..2fcf2a0 100644
--- a/video2story/__init__.py
+++ b/video2story/__init__.py
@@ -1,2 +1,196 @@
1from argparse import ArgumentParser, RawTextHelpFormatter
2from os.path import normpath
3
1from .cutter import cut 4from .cutter import cut
2from .uploader import upload 5from .uploader import upload
6
7parser = ArgumentParser(
8 prog="video2story",
9 description="Simple telegram story uploader",
10 formatter_class=RawTextHelpFormatter,
11)
12subparsers = parser.add_subparsers(
13 metavar="MODULE",
14 dest="module",
15 required=True,
16)
17
18video_parser = subparsers.add_parser(
19 "video",
20 help="Video processing module",
21 formatter_class=RawTextHelpFormatter,
22)
23
24video_parser.add_argument(
25 "filename",
26 type=str,
27 metavar="FILE",
28 help="Video file name",
29)
30video_parser.add_argument(
31 "output",
32 type=str,
33 metavar="PATH",
34 help="Output folder for processed videos",
35)
36video_parser.add_argument(
37 "-d",
38 "--duration",
39 type=int,
40 metavar="SECONDS",
41 default=60,
42 help=(
43 "Specifies each story duration.\n"
44 "Max value is 60 and min value is 1.\n"
45 "Default is 60."
46 ),
47)
48video_parser.add_argument(
49 "--no-sound",
50 action="store_true",
51 help="Remove sound from video.",
52)
53video_parser.add_argument(
54 "-s",
55 "--start",
56 type=int,
57 metavar="SECONDS",
58 default=None,
59 help="Specifies the second of the video from which processing will begin.",
60)
61video_parser.add_argument(
62 "-e",
63 "--end",
64 type=int,
65 metavar="SECONDS",
66 default=None,
67 help="Specifies the second of the video from which processing will end.",
68)
69
70story_parser = subparsers.add_parser(
71 "story",
72 help="Story publishing module",
73 formatter_class=RawTextHelpFormatter,
74)
75
76story_parser.add_argument(
77 "phone",
78 type=str,
79 metavar="PHONE",
80 help="Your phone number.",
81)
82story_parser.add_argument(
83 "input",
84 type=str,
85 metavar="PATH",
86 help="Input folder with processed videos",
87)
88story_parser.add_argument(
89 "-p",
90 "--privacy",
91 type=str,
92 metavar="PRIVACY-MODE",
93 choices=["everyone", "contacts", "selected", "friends"],
94 default="everyone",
95 help=(
96 "Specifies who can see you story.\n"
97 "Accept values: `everyone`, `contacts`, `selected` and `friends`.\n"
98 "\n"
99 "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n"
100 "If set to `selected`, the --user flag specifies the user who can see your story.\n"
101 "If set to `friends`, the --user flag will have no effect.\n"
102 ),
103)
104story_parser.add_argument(
105 "-u",
106 "--users",
107 type=str,
108 metavar="USERS",
109 nargs="+",
110 help=(
111 "Behavior depends on privacy mode. See `privacy` description.\n"
112 "You can specify a username or user id."
113 ),
114)
115story_parser.add_argument(
116 "-a",
117 "--active-period",
118 type=str,
119 metavar="PERIOD",
120 choices=["6h", "12h", "24h", "48h"],
121 default="24h",
122 help=(
123 "Period after which the story is moved to archive.\n"
124 "Accept values: `6h`, `12h`, `24h` and `48h`.\n"
125 "Default is 24h."
126 ),
127)
128story_parser.add_argument(
129 "--save-to-profile",
130 action="store_true",
131 help="Keep the story accessible after expiration.",
132)
133story_parser.add_argument(
134 "--protected-content",
135 action="store_true",
136 help="Protect story from forwarding and screenshotting.",
137)
138story_parser.add_argument(
139 "--tdlib",
140 type=str,
141 metavar="PATH",
142 help="Path to tdlib library file.",
143)
144story_parser.add_argument(
145 "-s",
146 "--start",
147 type=int,
148 metavar="VIDEO-ID",
149 default=0,
150 help=(
151 "Specifies the start point of the publication.\n"
152 "VIDEO-ID is the number in the name of processed file.\n"
153 "Default is 0."
154 ),
155)
156story_parser.add_argument(
157 "-e",
158 "--end",
159 type=int,
160 metavar="VIDEO-ID",
161 default=None,
162 help=(
163 "Specifies the end point of the publication.\n"
164 "VIDEO-ID is the number in the name of processed file."
165 ),
166)
167
168
169def main():
170 args = parser.parse_args()
171 if args.module == "video":
172 if not (1 <= args.duration <= 60):
173 print("Duration must be between 1 and 60")
174 exit(1)
175
176 cut(
177 normpath(args.filename),
178 normpath(args.output),
179 args.duration,
180 args.no_sound,
181 args.start,
182 args.end,
183 )
184 elif args.module == "story":
185 upload(
186 args.phone,
187 normpath(args.input),
188 args.privacy,
189 args.users,
190 args.active_period,
191 args.save_to_profile,
192 args.protected_content,
193 args.tdlib,
194 args.start,
195 args.end,
196 )
diff --git a/video2story/__main__.py b/video2story/__main__.py
index 43c94b8..8273c4f 100644
--- a/video2story/__main__.py
+++ b/video2story/__main__.py
@@ -1,194 +1,3 @@
1from argparse import ArgumentParser, RawTextHelpFormatter 1from . import main
2from os.path import normpath
3 2
4from video2story import cut, upload 3main()
5
6parser = ArgumentParser(
7 prog="video2story",
8 description="Simple telegram story uploader",
9 formatter_class=RawTextHelpFormatter,
10)
11subparsers = parser.add_subparsers(
12 metavar="MODULE",
13 dest="module",
14 required=True,
15)
16
17video_parser = subparsers.add_parser(
18 "video",
19 help="Video processing module",
20 formatter_class=RawTextHelpFormatter,
21)
22
23video_parser.add_argument(
24 "filename",
25 type=str,
26 metavar="FILE",
27 help="Video file name",
28)
29video_parser.add_argument(
30 "output",
31 type=str,
32 metavar="PATH",
33 help="Output folder for processed videos",
34)
35video_parser.add_argument(
36 "-d",
37 "--duration",
38 type=int,
39 metavar="SECONDS",
40 default=60,
41 help=(
42 "Specifies each story duration.\n"
43 "Max value is 60 and min value is 1.\n"
44 "Default is 60."
45 ),
46)
47video_parser.add_argument(
48 "--no-sound",
49 action="store_true",
50 help="Remove sound from video.",
51)
52video_parser.add_argument(
53 "-s",
54 "--start",
55 type=int,
56 metavar="SECONDS",
57 default=None,
58 help="Specifies the second of the video from which processing will begin.",
59)
60video_parser.add_argument(
61 "-e",
62 "--end",
63 type=int,
64 metavar="SECONDS",
65 default=None,
66 help="Specifies the second of the video from which processing will end.",
67)
68
69story_parser = subparsers.add_parser(
70 "story",
71 help="Story publishing module",
72 formatter_class=RawTextHelpFormatter,
73)
74
75story_parser.add_argument(
76 "phone",
77 type=str,
78 metavar="PHONE",
79 help="Your phone number.",
80)
81story_parser.add_argument(
82 "input",
83 type=str,
84 metavar="PATH",
85 help="Input folder with processed videos",
86)
87story_parser.add_argument(
88 "-p",
89 "--privacy",
90 type=str,
91 metavar="PRIVACY-MODE",
92 choices=["everyone", "contacts", "selected", "friends"],
93 default="everyone",
94 help=(
95 "Specifies who can see you story.\n"
96 "Accept values: `everyone`, `contacts`, `selected` and `friends`.\n"
97 "\n"
98 "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n"
99 "If set to `selected`, the --user flag specifies the user who can see your story.\n"
100 "If set to `friends`, the --user flag will have no effect.\n"
101 ),
102)
103story_parser.add_argument(
104 "-u",
105 "--users",
106 type=str,
107 metavar="USERS",
108 nargs="+",
109 help=(
110 "Behavior depends on privacy mode. See `privacy` description.\n"
111 "You can specify a username or user id."
112 ),
113)
114story_parser.add_argument(
115 "-a",
116 "--active-period",
117 type=str,
118 metavar="PERIOD",
119 choices=["6h", "12h", "24h", "48h"],
120 default="24h",
121 help=(
122 "Period after which the story is moved to archive.\n"
123 "Accept values: `6h`, `12h`, `24h` and `48h`.\n"
124 "Default is 24h."
125 ),
126)
127story_parser.add_argument(
128 "--save-to-profile",
129 action="store_true",
130 help="Keep the story accessible after expiration.",
131)
132story_parser.add_argument(
133 "--protected-content",
134 action="store_true",
135 help="Protect story from forwarding and screenshotting.",
136)
137story_parser.add_argument(
138 "--tdlib",
139 type=str,
140 metavar="PATH",
141 help="Path to tdlib library file.",
142)
143story_parser.add_argument(
144 "-s",
145 "--start",
146 type=int,
147 metavar="VIDEO-ID",
148 default=0,
149 help=(
150 "Specifies the start point of the publication.\n"
151 "VIDEO-ID is the number in the name of processed file.\n"
152 "Default is 0."
153 ),
154)
155story_parser.add_argument(
156 "-e",
157 "--end",
158 type=int,
159 metavar="VIDEO-ID",
160 default=None,
161 help=(
162 "Specifies the end point of the publication.\n"
163 "VIDEO-ID is the number in the name of processed file."
164 ),
165)
166
167
168args = parser.parse_args()
169if args.module == "video":
170 if not (1 <= args.duration <= 60):
171 print("Duration must be between 1 and 60")
172 exit(1)
173
174 cut(
175 normpath(args.filename),
176 normpath(args.output),
177 args.duration,
178 args.no_sound,
179 args.start,
180 args.end,
181 )
182elif args.module == "story":
183 upload(
184 args.phone,
185 normpath(args.input),
186 args.privacy,
187 args.users,
188 args.active_period,
189 args.save_to_profile,
190 args.protected_content,
191 args.tdlib,
192 args.start,
193 args.end,
194 )
diff --git a/video2story/cutter.py b/video2story/cutter.py
index 23f035e..0a83ad1 100644
--- a/video2story/cutter.py
+++ b/video2story/cutter.py
@@ -5,15 +5,15 @@ from subprocess import Popen
5 5
6def cut( 6def cut(
7 filename: str, 7 filename: str,
8 output: str, 8 output_dir: str,
9 duration: int, 9 duration: int,
10 no_sound: bool, 10 no_sound: bool,
11 start: int | None, 11 start: int | None,
12 end: int | None, 12 end: int | None,
13) -> None: 13) -> None:
14 if not exists(output): 14 if not exists(output_dir):
15 makedirs(output) 15 makedirs(output_dir)
16 elif not isdir(output): 16 elif not isdir(output_dir):
17 print("Output is not a directory") 17 print("Output is not a directory")
18 exit(1) 18 exit(1)
19 process = Popen( 19 process = Popen(
@@ -40,7 +40,7 @@ def cut(
40 *("-reset_timestamps", "1"), 40 *("-reset_timestamps", "1"),
41 *("-force_key_frames", f"expr:gte(t,n_forced*{duration})"), 41 *("-force_key_frames", f"expr:gte(t,n_forced*{duration})"),
42 # Output 42 # Output
43 join(output, "%d.mp4"), 43 join(output_dir, "%d.mp4"),
44 ] 44 ]
45 ) 45 )
46 46
diff --git a/video2story/uploader.py b/video2story/uploader.py
index d61ca43..6a2af42 100644
--- a/video2story/uploader.py
+++ b/video2story/uploader.py
@@ -27,7 +27,7 @@ def upload(
27 phone=phone, 27 phone=phone,
28 ) 28 )
29 telegram.login() 29 telegram.login()
30 me = int(telegram.call_method("getMe", block=True).update["id"]) 30 me = int(telegram.call_method("getMe", block=True).update["id"]) # type:ignore
31 31
32 if users is not None: 32 if users is not None:
33 user_ids = [] 33 user_ids = []