diff options
| -rw-r--r-- | .vscode/settings.json | 2 | ||||
| -rw-r--r-- | video2story/__main__.py | 73 | ||||
| -rw-r--r-- | video2story/uploader.py | 125 |
3 files changed, 176 insertions, 24 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index a205f43..d6063cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -3,5 +3,5 @@ | |||
| 3 | "**/__pycache__": true, | 3 | "**/__pycache__": true, |
| 4 | "**/.venv": true | 4 | "**/.venv": true |
| 5 | }, | 5 | }, |
| 6 | "cSpell.words": ["screenshotting"] | 6 | "cSpell.words": ["screenshotting", "tdlib"] |
| 7 | } | 7 | } |
diff --git a/video2story/__main__.py b/video2story/__main__.py index 8e08cac..43c94b8 100644 --- a/video2story/__main__.py +++ b/video2story/__main__.py | |||
| @@ -40,8 +40,7 @@ video_parser.add_argument( | |||
| 40 | default=60, | 40 | default=60, |
| 41 | help=( | 41 | help=( |
| 42 | "Specifies each story duration.\n" | 42 | "Specifies each story duration.\n" |
| 43 | "When set to 0 each story will be an image.\n" | 43 | "Max value is 60 and min value is 1.\n" |
| 44 | "Max value is 60 and min value is 0.\n" | ||
| 45 | "Default is 60." | 44 | "Default is 60." |
| 46 | ), | 45 | ), |
| 47 | ) | 46 | ) |
| @@ -67,7 +66,6 @@ video_parser.add_argument( | |||
| 67 | help="Specifies the second of the video from which processing will end.", | 66 | help="Specifies the second of the video from which processing will end.", |
| 68 | ) | 67 | ) |
| 69 | 68 | ||
| 70 | |||
| 71 | story_parser = subparsers.add_parser( | 69 | story_parser = subparsers.add_parser( |
| 72 | "story", | 70 | "story", |
| 73 | help="Story publishing module", | 71 | help="Story publishing module", |
| @@ -75,27 +73,31 @@ story_parser = subparsers.add_parser( | |||
| 75 | ) | 73 | ) |
| 76 | 74 | ||
| 77 | story_parser.add_argument( | 75 | story_parser.add_argument( |
| 78 | "action", | 76 | "phone", |
| 79 | type=str, | 77 | type=str, |
| 80 | metavar="ACTION", | 78 | metavar="PHONE", |
| 81 | choices=["publish-all", "publish-one"], | 79 | help="Your phone number.", |
| 82 | help=( | 80 | ) |
| 83 | "Specifies the type of publishing action.\n" | 81 | story_parser.add_argument( |
| 84 | "Accept values: `publish-all` and `publish-one`.\n" | 82 | "input", |
| 85 | ), | 83 | type=str, |
| 84 | metavar="PATH", | ||
| 85 | help="Input folder with processed videos", | ||
| 86 | ) | 86 | ) |
| 87 | story_parser.add_argument( | 87 | story_parser.add_argument( |
| 88 | "privacy", | 88 | "-p", |
| 89 | "--privacy", | ||
| 89 | type=str, | 90 | type=str, |
| 90 | metavar="PRIVACY-MODE", | 91 | metavar="PRIVACY-MODE", |
| 91 | choices=["everyone", "contacts", "selected-users", "close-friends"], | 92 | choices=["everyone", "contacts", "selected", "friends"], |
| 93 | default="everyone", | ||
| 92 | help=( | 94 | help=( |
| 93 | "Specifies who can see you story.\n" | 95 | "Specifies who can see you story.\n" |
| 94 | "Accept values: `everyone`, `contacts`, `selected-users` and `close-friends`.\n" | 96 | "Accept values: `everyone`, `contacts`, `selected` and `friends`.\n" |
| 95 | "\n" | 97 | "\n" |
| 96 | "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n" | 98 | "If set to `everyone` or `contacts`, the --user flag excludes the user who can see your story.\n" |
| 97 | "If set to `selected-users`, the --user flag specifies 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" |
| 98 | "If set to `close-friends`, the --user flag will have no effect.\n" | 100 | "If set to `friends`, the --user flag will have no effect.\n" |
| 99 | ), | 101 | ), |
| 100 | ) | 102 | ) |
| 101 | story_parser.add_argument( | 103 | story_parser.add_argument( |
| @@ -104,7 +106,10 @@ story_parser.add_argument( | |||
| 104 | type=str, | 106 | type=str, |
| 105 | metavar="USERS", | 107 | metavar="USERS", |
| 106 | nargs="+", | 108 | nargs="+", |
| 107 | help="Behavior depends on privacy mode. See `privacy` description.", | 109 | help=( |
| 110 | "Behavior depends on privacy mode. See `privacy` description.\n" | ||
| 111 | "You can specify a username or user id." | ||
| 112 | ), | ||
| 108 | ) | 113 | ) |
| 109 | story_parser.add_argument( | 114 | story_parser.add_argument( |
| 110 | "-a", | 115 | "-a", |
| @@ -120,13 +125,24 @@ story_parser.add_argument( | |||
| 120 | ), | 125 | ), |
| 121 | ) | 126 | ) |
| 122 | story_parser.add_argument( | 127 | story_parser.add_argument( |
| 128 | "--save-to-profile", | ||
| 129 | action="store_true", | ||
| 130 | help="Keep the story accessible after expiration.", | ||
| 131 | ) | ||
| 132 | story_parser.add_argument( | ||
| 123 | "--protected-content", | 133 | "--protected-content", |
| 124 | action="store_true", | 134 | action="store_true", |
| 125 | help="Protect story from forwarding and screenshotting.", | 135 | help="Protect story from forwarding and screenshotting.", |
| 126 | ) | 136 | ) |
| 127 | story_parser.add_argument( | 137 | story_parser.add_argument( |
| 128 | "-f", | 138 | "--tdlib", |
| 129 | "--from", | 139 | type=str, |
| 140 | metavar="PATH", | ||
| 141 | help="Path to tdlib library file.", | ||
| 142 | ) | ||
| 143 | story_parser.add_argument( | ||
| 144 | "-s", | ||
| 145 | "--start", | ||
| 130 | type=int, | 146 | type=int, |
| 131 | metavar="VIDEO-ID", | 147 | metavar="VIDEO-ID", |
| 132 | default=0, | 148 | default=0, |
| @@ -137,8 +153,8 @@ story_parser.add_argument( | |||
| 137 | ), | 153 | ), |
| 138 | ) | 154 | ) |
| 139 | story_parser.add_argument( | 155 | story_parser.add_argument( |
| 140 | "-t", | 156 | "-e", |
| 141 | "--to", | 157 | "--end", |
| 142 | type=int, | 158 | type=int, |
| 143 | metavar="VIDEO-ID", | 159 | metavar="VIDEO-ID", |
| 144 | default=None, | 160 | default=None, |
| @@ -151,6 +167,10 @@ story_parser.add_argument( | |||
| 151 | 167 | ||
| 152 | args = parser.parse_args() | 168 | args = parser.parse_args() |
| 153 | if args.module == "video": | 169 | if args.module == "video": |
| 170 | if not (1 <= args.duration <= 60): | ||
| 171 | print("Duration must be between 1 and 60") | ||
| 172 | exit(1) | ||
| 173 | |||
| 154 | cut( | 174 | cut( |
| 155 | normpath(args.filename), | 175 | normpath(args.filename), |
| 156 | normpath(args.output), | 176 | normpath(args.output), |
| @@ -160,4 +180,15 @@ if args.module == "video": | |||
| 160 | args.end, | 180 | args.end, |
| 161 | ) | 181 | ) |
| 162 | elif args.module == "story": | 182 | elif args.module == "story": |
| 163 | pass | 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/uploader.py b/video2story/uploader.py index dde8c98..d61ca43 100644 --- a/video2story/uploader.py +++ b/video2story/uploader.py | |||
| @@ -1,2 +1,123 @@ | |||
| 1 | def upload() -> None: | 1 | from os import listdir |
| 2 | pass \ No newline at end of file | 2 | from os.path import isdir, join |
| 3 | from threading import Event | ||
| 4 | from time import sleep | ||
| 5 | |||
| 6 | from telegram.client import Telegram | ||
| 7 | |||
| 8 | |||
| 9 | def upload( | ||
| 10 | phone: str, | ||
| 11 | input_dir: str, | ||
| 12 | privacy: str, | ||
| 13 | users: list[str] | None, | ||
| 14 | active_period: str, | ||
| 15 | save_to_profile: bool, | ||
| 16 | protected_content: bool, | ||
| 17 | tdlib: str | None, | ||
| 18 | start: int, | ||
| 19 | end: int | None, | ||
| 20 | ) -> None: | ||
| 21 | telegram = Telegram( | ||
| 22 | api_id=2092395, | ||
| 23 | api_hash="38e26914cf0fda6356fda8f9d28f3bb9", | ||
| 24 | database_encryption_key="just a secret", | ||
| 25 | tdlib_verbosity=0, | ||
| 26 | library_path=tdlib, | ||
| 27 | phone=phone, | ||
| 28 | ) | ||
| 29 | telegram.login() | ||
| 30 | me = int(telegram.call_method("getMe", block=True).update["id"]) | ||
| 31 | |||
| 32 | if users is not None: | ||
| 33 | user_ids = [] | ||
| 34 | for u in users: | ||
| 35 | if u.isdigit(): | ||
| 36 | user_ids.append(int(u)) | ||
| 37 | continue | ||
| 38 | |||
| 39 | try: | ||
| 40 | user_ids.append( | ||
| 41 | telegram.call_method( | ||
| 42 | "searchPublicChat", | ||
| 43 | {"username": u.strip().removeprefix("@")}, | ||
| 44 | block=True, | ||
| 45 | ).update["id"] | ||
| 46 | ) | ||
| 47 | except Exception: | ||
| 48 | print(f"An error occurred while getting user id ({u}).") | ||
| 49 | else: | ||
| 50 | user_ids = None | ||
| 51 | |||
| 52 | if not isdir(input_dir): | ||
| 53 | print("Input folder does not exists") | ||
| 54 | exit(1) | ||
| 55 | |||
| 56 | queue = set() | ||
| 57 | done = Event() | ||
| 58 | |||
| 59 | for i in range(start, (end + 1) if end is not None else len(listdir(input_dir))): | ||
| 60 | print(f"Adding to the uploading queue. (VIDEO-ID: {i})") | ||
| 61 | |||
| 62 | queue.add( | ||
| 63 | telegram.call_method( | ||
| 64 | "sendStory", | ||
| 65 | { | ||
| 66 | "chat_id": me, | ||
| 67 | "content": { | ||
| 68 | "@type": "inputStoryContentVideo", | ||
| 69 | "video": { | ||
| 70 | "@type": "inputFileLocal", | ||
| 71 | "path": join(input_dir, f"{i}.mp4"), | ||
| 72 | }, | ||
| 73 | }, | ||
| 74 | "areas": None, | ||
| 75 | "caption": None, | ||
| 76 | "privacy_settings": { | ||
| 77 | "everyone": { | ||
| 78 | "@type": "storyPrivacySettingsEveryone", | ||
| 79 | "except_user_ids": user_ids, | ||
| 80 | }, | ||
| 81 | "contacts": { | ||
| 82 | "@type": "storyPrivacySettingsContacts", | ||
| 83 | "except_user_ids": user_ids, | ||
| 84 | }, | ||
| 85 | "selected": { | ||
| 86 | "@type": "storyPrivacySettingsSelectedUsers", | ||
| 87 | "user_ids": user_ids, | ||
| 88 | }, | ||
| 89 | "friends": { | ||
| 90 | "@type": "storyPrivacySettingsCloseFriends", | ||
| 91 | }, | ||
| 92 | }[privacy], | ||
| 93 | "active_period": { | ||
| 94 | "6h": 21600, | ||
| 95 | "12h": 43200, | ||
| 96 | "24h": 86400, | ||
| 97 | "48h": 172800, | ||
| 98 | }[active_period], | ||
| 99 | "is_posted_to_chat_page": save_to_profile, | ||
| 100 | "protect_content": protected_content, | ||
| 101 | }, | ||
| 102 | block=True, | ||
| 103 | ).update["content"]["video"]["video"]["id"] | ||
| 104 | ) | ||
| 105 | sleep(0.5) | ||
| 106 | |||
| 107 | print(f"Uploading. {len(queue)} left") | ||
| 108 | |||
| 109 | def update(file: dict[str, object]) -> None: | ||
| 110 | if ( | ||
| 111 | file["file"]["id"] in queue | ||
| 112 | and not file["file"]["remote"]["is_uploading_active"] | ||
| 113 | ): | ||
| 114 | queue.remove(file["file"]["id"]) | ||
| 115 | |||
| 116 | if len(queue) == 0: | ||
| 117 | print("Done") | ||
| 118 | done.set() | ||
| 119 | else: | ||
| 120 | print(f"Uploading. {len(queue)} left") | ||
| 121 | |||
| 122 | telegram.add_update_handler("updateFile", update) | ||
| 123 | done.wait() | ||
