From 19606a6ac95e83a33c1173dba3e5d6f9f8fb9f94 Mon Sep 17 00:00:00 2001 From: Tolmachev Igor Date: Sun, 15 Sep 2024 13:14:54 +0300 Subject: Add README and fix package props --- README.md | 37 +++++++++ pyproject.toml | 7 +- video2story/__init__.py | 194 +++++++++++++++++++++++++++++++++++++++++++++++ video2story/__main__.py | 195 +----------------------------------------------- video2story/cutter.py | 10 +-- video2story/uploader.py | 2 +- 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 @@ +# video2story + +**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. + +## Installation + +To install `video2story`, use pip: + +```bash +pip install video2story +``` + +## Usage + +### 1. Process the video + +```bash +video2story video input.mp4 output_dir/ +``` + +First, 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: + +By default, the video will be cut into 60-second segments. To change that, use the `-d` option and specify the duration in seconds: + +### 2. Upload the processed video + +```bash +video2story PHONE_NUMBER output_dir/ -p friends +``` + +Replace `PHONE_NUMBER` with your Telegram phone number and `output_dir/` with the directory containing the processed video segments. + +The `-p` option allows you to choose the privacy level for your story. For more options and detailed information, use the `--help` flag. + +## License + +This 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 @@ [tool.poetry] name = "video2story" -version = "1.0.0" +version = "1.0.1" description = "Simple telegram story uploader" +repository = "https://codeberg.org/igorechek06/video2story" authors = ["Tolmachev Igor "] readme = "README.md" +packages = [{ include = "video2story" }] [tool.poetry.dependencies] python = "^3.10" @@ -14,6 +16,9 @@ mypy = "^1.11.2" isort = "^5.13.2" black = "^24.8.0" +[tool.poetry.scripts] +video2story = "video2story:main" + [tool.mypy] ignore_missing_imports = true disallow_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 @@ +from argparse import ArgumentParser, RawTextHelpFormatter +from os.path import normpath + from .cutter import cut from .uploader import upload + +parser = ArgumentParser( + prog="video2story", + description="Simple telegram story uploader", + formatter_class=RawTextHelpFormatter, +) +subparsers = parser.add_subparsers( + metavar="MODULE", + dest="module", + required=True, +) + +video_parser = subparsers.add_parser( + "video", + help="Video processing module", + formatter_class=RawTextHelpFormatter, +) + +video_parser.add_argument( + "filename", + type=str, + metavar="FILE", + help="Video file name", +) +video_parser.add_argument( + "output", + type=str, + metavar="PATH", + help="Output folder for processed videos", +) +video_parser.add_argument( + "-d", + "--duration", + type=int, + metavar="SECONDS", + default=60, + help=( + "Specifies each story duration.\n" + "Max value is 60 and min value is 1.\n" + "Default is 60." + ), +) +video_parser.add_argument( + "--no-sound", + action="store_true", + help="Remove sound from video.", +) +video_parser.add_argument( + "-s", + "--start", + type=int, + metavar="SECONDS", + default=None, + help="Specifies the second of the video from which processing will begin.", +) +video_parser.add_argument( + "-e", + "--end", + type=int, + metavar="SECONDS", + default=None, + help="Specifies the second of the video from which processing will end.", +) + +story_parser = subparsers.add_parser( + "story", + help="Story publishing module", + formatter_class=RawTextHelpFormatter, +) + +story_parser.add_argument( + "phone", + type=str, + metavar="PHONE", + help="Your phone number.", +) +story_parser.add_argument( + "input", + type=str, + metavar="PATH", + help="Input folder with processed videos", +) +story_parser.add_argument( + "-p", + "--privacy", + type=str, + metavar="PRIVACY-MODE", + choices=["everyone", "contacts", "selected", "friends"], + default="everyone", + help=( + "Specifies who can see you story.\n" + "Accept values: `everyone`, `contacts`, `selected` and `friends`.\n" + "\n" + "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n" + "If set to `selected`, the --user flag specifies the user who can see your story.\n" + "If set to `friends`, the --user flag will have no effect.\n" + ), +) +story_parser.add_argument( + "-u", + "--users", + type=str, + metavar="USERS", + nargs="+", + help=( + "Behavior depends on privacy mode. See `privacy` description.\n" + "You can specify a username or user id." + ), +) +story_parser.add_argument( + "-a", + "--active-period", + type=str, + metavar="PERIOD", + choices=["6h", "12h", "24h", "48h"], + default="24h", + help=( + "Period after which the story is moved to archive.\n" + "Accept values: `6h`, `12h`, `24h` and `48h`.\n" + "Default is 24h." + ), +) +story_parser.add_argument( + "--save-to-profile", + action="store_true", + help="Keep the story accessible after expiration.", +) +story_parser.add_argument( + "--protected-content", + action="store_true", + help="Protect story from forwarding and screenshotting.", +) +story_parser.add_argument( + "--tdlib", + type=str, + metavar="PATH", + help="Path to tdlib library file.", +) +story_parser.add_argument( + "-s", + "--start", + type=int, + metavar="VIDEO-ID", + default=0, + help=( + "Specifies the start point of the publication.\n" + "VIDEO-ID is the number in the name of processed file.\n" + "Default is 0." + ), +) +story_parser.add_argument( + "-e", + "--end", + type=int, + metavar="VIDEO-ID", + default=None, + help=( + "Specifies the end point of the publication.\n" + "VIDEO-ID is the number in the name of processed file." + ), +) + + +def main(): + args = parser.parse_args() + if args.module == "video": + if not (1 <= args.duration <= 60): + print("Duration must be between 1 and 60") + exit(1) + + cut( + normpath(args.filename), + normpath(args.output), + args.duration, + args.no_sound, + args.start, + args.end, + ) + elif args.module == "story": + upload( + args.phone, + normpath(args.input), + args.privacy, + args.users, + args.active_period, + args.save_to_profile, + args.protected_content, + args.tdlib, + args.start, + args.end, + ) 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 @@ -from argparse import ArgumentParser, RawTextHelpFormatter -from os.path import normpath +from . import main -from video2story import cut, upload - -parser = ArgumentParser( - prog="video2story", - description="Simple telegram story uploader", - formatter_class=RawTextHelpFormatter, -) -subparsers = parser.add_subparsers( - metavar="MODULE", - dest="module", - required=True, -) - -video_parser = subparsers.add_parser( - "video", - help="Video processing module", - formatter_class=RawTextHelpFormatter, -) - -video_parser.add_argument( - "filename", - type=str, - metavar="FILE", - help="Video file name", -) -video_parser.add_argument( - "output", - type=str, - metavar="PATH", - help="Output folder for processed videos", -) -video_parser.add_argument( - "-d", - "--duration", - type=int, - metavar="SECONDS", - default=60, - help=( - "Specifies each story duration.\n" - "Max value is 60 and min value is 1.\n" - "Default is 60." - ), -) -video_parser.add_argument( - "--no-sound", - action="store_true", - help="Remove sound from video.", -) -video_parser.add_argument( - "-s", - "--start", - type=int, - metavar="SECONDS", - default=None, - help="Specifies the second of the video from which processing will begin.", -) -video_parser.add_argument( - "-e", - "--end", - type=int, - metavar="SECONDS", - default=None, - help="Specifies the second of the video from which processing will end.", -) - -story_parser = subparsers.add_parser( - "story", - help="Story publishing module", - formatter_class=RawTextHelpFormatter, -) - -story_parser.add_argument( - "phone", - type=str, - metavar="PHONE", - help="Your phone number.", -) -story_parser.add_argument( - "input", - type=str, - metavar="PATH", - help="Input folder with processed videos", -) -story_parser.add_argument( - "-p", - "--privacy", - type=str, - metavar="PRIVACY-MODE", - choices=["everyone", "contacts", "selected", "friends"], - default="everyone", - help=( - "Specifies who can see you story.\n" - "Accept values: `everyone`, `contacts`, `selected` and `friends`.\n" - "\n" - "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n" - "If set to `selected`, the --user flag specifies the user who can see your story.\n" - "If set to `friends`, the --user flag will have no effect.\n" - ), -) -story_parser.add_argument( - "-u", - "--users", - type=str, - metavar="USERS", - nargs="+", - help=( - "Behavior depends on privacy mode. See `privacy` description.\n" - "You can specify a username or user id." - ), -) -story_parser.add_argument( - "-a", - "--active-period", - type=str, - metavar="PERIOD", - choices=["6h", "12h", "24h", "48h"], - default="24h", - help=( - "Period after which the story is moved to archive.\n" - "Accept values: `6h`, `12h`, `24h` and `48h`.\n" - "Default is 24h." - ), -) -story_parser.add_argument( - "--save-to-profile", - action="store_true", - help="Keep the story accessible after expiration.", -) -story_parser.add_argument( - "--protected-content", - action="store_true", - help="Protect story from forwarding and screenshotting.", -) -story_parser.add_argument( - "--tdlib", - type=str, - metavar="PATH", - help="Path to tdlib library file.", -) -story_parser.add_argument( - "-s", - "--start", - type=int, - metavar="VIDEO-ID", - default=0, - help=( - "Specifies the start point of the publication.\n" - "VIDEO-ID is the number in the name of processed file.\n" - "Default is 0." - ), -) -story_parser.add_argument( - "-e", - "--end", - type=int, - metavar="VIDEO-ID", - default=None, - help=( - "Specifies the end point of the publication.\n" - "VIDEO-ID is the number in the name of processed file." - ), -) - - -args = parser.parse_args() -if args.module == "video": - if not (1 <= args.duration <= 60): - print("Duration must be between 1 and 60") - exit(1) - - cut( - normpath(args.filename), - normpath(args.output), - args.duration, - args.no_sound, - args.start, - args.end, - ) -elif args.module == "story": - upload( - args.phone, - normpath(args.input), - args.privacy, - args.users, - args.active_period, - args.save_to_profile, - args.protected_content, - args.tdlib, - args.start, - args.end, - ) +main() 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 def cut( filename: str, - output: str, + output_dir: str, duration: int, no_sound: bool, start: int | None, end: int | None, ) -> None: - if not exists(output): - makedirs(output) - elif not isdir(output): + if not exists(output_dir): + makedirs(output_dir) + elif not isdir(output_dir): print("Output is not a directory") exit(1) process = Popen( @@ -40,7 +40,7 @@ def cut( *("-reset_timestamps", "1"), *("-force_key_frames", f"expr:gte(t,n_forced*{duration})"), # Output - join(output, "%d.mp4"), + join(output_dir, "%d.mp4"), ] ) 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( phone=phone, ) telegram.login() - me = int(telegram.call_method("getMe", block=True).update["id"]) + me = int(telegram.call_method("getMe", block=True).update["id"]) # type:ignore if users is not None: user_ids = [] -- cgit v1.3