twitch-auto-clipper 0.1.7__tar.gz
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.
- twitch_auto_clipper-0.1.7/.coveragerc +2 -0
- twitch_auto_clipper-0.1.7/.github/workflows/test_runner.yml +18 -0
- twitch_auto_clipper-0.1.7/.gitignore +9 -0
- twitch_auto_clipper-0.1.7/LICENSE +19 -0
- twitch_auto_clipper-0.1.7/PKG-INFO +170 -0
- twitch_auto_clipper-0.1.7/README.md +152 -0
- twitch_auto_clipper-0.1.7/examples/logger.py +61 -0
- twitch_auto_clipper-0.1.7/examples/simple.py +19 -0
- twitch_auto_clipper-0.1.7/pyproject.toml +33 -0
- twitch_auto_clipper-0.1.7/pyrightconfig.json +14 -0
- twitch_auto_clipper-0.1.7/scripts/coverage.sh +3 -0
- twitch_auto_clipper-0.1.7/scripts/install.sh +1 -0
- twitch_auto_clipper-0.1.7/scripts/night_run.sh +3 -0
- twitch_auto_clipper-0.1.7/scripts/rebuild.sh +4 -0
- twitch_auto_clipper-0.1.7/scripts/run_test.sh +1 -0
- twitch_auto_clipper-0.1.7/scripts/run_tests.sh +1 -0
- twitch_auto_clipper-0.1.7/tests/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/tests/auth/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/tests/auth/test_TwitchAuthTokens.py +160 -0
- twitch_auto_clipper-0.1.7/tests/chat/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/tests/chat/test_Streamer.py +176 -0
- twitch_auto_clipper-0.1.7/tests/chat/test_TwitchChatIRC.py +161 -0
- twitch_auto_clipper-0.1.7/tests/clip/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/tests/clip/test_Clip.py +29 -0
- twitch_auto_clipper-0.1.7/tests/helpers.py +45 -0
- twitch_auto_clipper-0.1.7/tests/test_TwitchAutoClipper.py +102 -0
- twitch_auto_clipper-0.1.7/tests/utils/LockedCounter.py +28 -0
- twitch_auto_clipper-0.1.7/tests/utils/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/tests/utils/test_ProtectedVar.py +103 -0
- twitch_auto_clipper-0.1.7/tests/utils/test_utils.py +16 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/TwitchAutoClipper.py +88 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/TwitchAutoClipperContext.py +16 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/Urls.py +5 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/auth/TwitchAuthTokens.py +185 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/auth/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/chat/Message.py +5 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/chat/Streamer.py +186 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/chat/TwitchChatIRC.py +108 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/chat/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/clip/Clip.py +25 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/clip/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/type_aliases/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/type_aliases/types.py +6 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/utils/ProtectedVar.py +55 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/utils/__init__.py +0 -0
- twitch_auto_clipper-0.1.7/twitch_auto_clipper/utils/utils.py +14 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Unit Tests
|
|
2
|
+
on: [push]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
steps:
|
|
7
|
+
- uses: actions/checkout@v7
|
|
8
|
+
|
|
9
|
+
- name: Set up Python
|
|
10
|
+
uses: actions/setup-python@v7
|
|
11
|
+
with:
|
|
12
|
+
python-version: '3.10'
|
|
13
|
+
|
|
14
|
+
- name: Install dependencies
|
|
15
|
+
run: pip install .
|
|
16
|
+
|
|
17
|
+
- name: Run tests
|
|
18
|
+
run: ./scripts/run_tests.sh
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: twitch_auto_clipper
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: Automatically create Twitch clips when chat activity spikes
|
|
5
|
+
Project-URL: source, https://github.com/sqrtminustwo/twitch_auto_clipper
|
|
6
|
+
Author-email: Maksym Ziborov <twitch_auto_clipper@blkflag.org>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: dacite>=1.9.2
|
|
13
|
+
Requires-Dist: requests>=2.34.2
|
|
14
|
+
Requires-Dist: sortedcollections>=2.1.0
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: coverage>=7.16.0; extra == 'test'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
[](https://github.com/sqrtminustwo/twitch_auto_clipper/actions/workflows/test_runner.yml)
|
|
20
|
+
|
|
21
|
+
# twitch_auto_clipper
|
|
22
|
+
|
|
23
|
+
Automatically create Twitch clips when chat activity spikes.
|
|
24
|
+
|
|
25
|
+
Twitch Auto Clipper monitors Twitch chat and detects moments where a large number of viewers send the same emote/message within a short period of time. When activity exceeds a configurable threshold, it automatically creates a Twitch clip.
|
|
26
|
+
|
|
27
|
+
[demo]
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install twitch_auto_clipper
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
Minimum Python version supported by `twitch_auto_clipper` is 3.10.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from twitch_auto_clipper_sqrtminusone.TwitchAutoClipper import TwitchAutoClipper
|
|
43
|
+
|
|
44
|
+
clipper = TwitchAutoClipper(
|
|
45
|
+
["Marlon", "Lacy"],
|
|
46
|
+
"YOUR_CLIENT_ID",
|
|
47
|
+
"YOUR_CLIENT_SECRET",
|
|
48
|
+
on_clip=lambda clip: print(clip),
|
|
49
|
+
common_value=1,
|
|
50
|
+
emote_value=2,
|
|
51
|
+
clipable_message_ratio=0.5,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
clipper.start()
|
|
55
|
+
clipper.join()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> [!IMPORTANT]
|
|
59
|
+
> `client_id` and `client_secret` are saved in [TwitchAuthTokens](twitch_auto_clipper_sqrtminusone/auth/TwitchAuthTokens.py) and only sent to the Twitch API.
|
|
60
|
+
|
|
61
|
+
> [!IMPORTANT]
|
|
62
|
+
> Upon initialization of [TwitchAutoClipper](twitch_auto_clipper_sqrtminusone/TwitchAutoClipper.py), your web browser should open to gain clipping permission for your Twitch account (as shown in the demo).
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- Automatically handles Twitch OAuth token refresh
|
|
67
|
+
- Automatically monitors multiple Twitch streams
|
|
68
|
+
- Supports custom emotes/messages as clipping triggers
|
|
69
|
+
- Optional 7TV emote support
|
|
70
|
+
- Runs each streamer in separate thread
|
|
71
|
+
|
|
72
|
+
## How it works
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
Twitch chat
|
|
76
|
+
│
|
|
77
|
+
▼
|
|
78
|
+
Collect messages
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
Count messages / emotes
|
|
82
|
+
│
|
|
83
|
+
▼
|
|
84
|
+
Detect activity spike
|
|
85
|
+
│
|
|
86
|
+
▼
|
|
87
|
+
Threshold exceeded?
|
|
88
|
+
│
|
|
89
|
+
YES
|
|
90
|
+
▼
|
|
91
|
+
Create Twitch clip
|
|
92
|
+
│
|
|
93
|
+
▼
|
|
94
|
+
Call on_clip callback
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
There are two classes that you will typically need:
|
|
100
|
+
|
|
101
|
+
- `TwitchAutoClipper` — monitors Twitch chat and automatically creates clips when a message reaches the configured threshold.
|
|
102
|
+
- `Clip` — contains information about a clip that was created.
|
|
103
|
+
|
|
104
|
+
### `TwitchAutoClipper`
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
TwitchAutoClipper(
|
|
108
|
+
streamers_names,
|
|
109
|
+
client_id,
|
|
110
|
+
client_secret,
|
|
111
|
+
on_clip,
|
|
112
|
+
counter_interval_seconds=...,
|
|
113
|
+
clipable_message_ratio=...,
|
|
114
|
+
clipable_wait=...,
|
|
115
|
+
common_value=...,
|
|
116
|
+
emote_value=...,
|
|
117
|
+
excluded_words=...,
|
|
118
|
+
logging_handlers=...,
|
|
119
|
+
logging_level=...
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
| Argument | Description |
|
|
124
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| `streamers_names` | List of Twitch usernames to monitor. Usernames are case-insensitive and are converted to lowercase. |
|
|
126
|
+
| `client_id` | Your Twitch application client ID. Available from the [Twitch Developer Console](https://dev.twitch.tv/docs/authentication/register-app). |
|
|
127
|
+
| `client_secret` | Your Twitch application client secret. Available from the [Twitch Developer Console](https://dev.twitch.tv/docs/authentication/register-app). |
|
|
128
|
+
| `on_clip` | Callback function invoked with a `Clip` instance when a clip is created. |
|
|
129
|
+
| `counter_interval_seconds` | Time interval over which chat message frequencies are counted. Once the interval ends, the counters are reset. |
|
|
130
|
+
| `clipable_message_ratio` | The weighted frequency of the most frequent message divided by the total number of messages received during the current interval. Because messages can have different values (`common_value` and `emote_value`), the ratio can exceed 1. |
|
|
131
|
+
| `clipable_wait` | Time to wait before creating a clip after a message reaches the clipping threshold. The waiting and clipping are performed in a separate thread, so this does not block the streamer thread. |
|
|
132
|
+
| `common_value` | Value assigned to a regular chat message. A regular message is any message that is not a 7TV emote when the streamer has a 7TV profile. |
|
|
133
|
+
| `emote_value` | Value assigned to a 7TV emote. |
|
|
134
|
+
| `excluded_words` | Words that should be excluded from message frequency counting. |
|
|
135
|
+
| `logging_handlers` | Logging handlers used by the package. See [`examples/logger.py`](examples/logger.py) for an example using multiple handlers. |
|
|
136
|
+
| `logging_level` | Logging level used by the package. See the [Python logging documentation](https://docs.python.org/3/library/logging.html#logging-levels). |
|
|
137
|
+
|
|
138
|
+
> [!NOTE]
|
|
139
|
+
> If multiple streamers are configured, `on_clip` may be called concurrently from multiple threads. Make sure your callback is thread-safe and use the appropriate [Python threading APIs](https://docs.python.org/3/library/threading.html) when necessary.
|
|
140
|
+
|
|
141
|
+
### `Clip`
|
|
142
|
+
|
|
143
|
+
A `Clip` instance contains information about the clip that was created.
|
|
144
|
+
|
|
145
|
+
| Attribute | Description |
|
|
146
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| `message` | The chat message that triggered the clip. |
|
|
148
|
+
| `message_count` | Number of occurrences of the triggering message. |
|
|
149
|
+
| `ratio` | Ratio of the triggering message to the total number of messages received during the counting interval. |
|
|
150
|
+
| `broadcaster_id` | Twitch broadcaster ID of the streamer for whom the clip was created. See the [Twitch API documentation](https://dev.twitch.tv/docs/api/reference). |
|
|
151
|
+
| `url` | URL of the created clip. |
|
|
152
|
+
| `timestamp` | Expected timestamp of the event highlighted by the clip. |
|
|
153
|
+
| `duration` | Clip duration in seconds. |
|
|
154
|
+
|
|
155
|
+
## Motivation
|
|
156
|
+
|
|
157
|
+
Manually watching an entire stream to find moments worth clipping is time-consuming.
|
|
158
|
+
|
|
159
|
+
`twitch_auto_clipper` uses the audience's reaction as a signal: when chat suddenly becomes active around a particular message or emote, the bot can automatically capture the moment.
|
|
160
|
+
|
|
161
|
+
## Roadmap
|
|
162
|
+
|
|
163
|
+
- [x] Analyze Twitch chat through IRC
|
|
164
|
+
- [x] Twitch OAuth authentication
|
|
165
|
+
- [x] Automatically create clips
|
|
166
|
+
- [x] Monitor multiple live streamers
|
|
167
|
+
- [x] Save clips to a log
|
|
168
|
+
- [x] Add automated tests
|
|
169
|
+
- [ ] Pip package
|
|
170
|
+
- [ ] Web page for constants editing
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
[](https://github.com/sqrtminustwo/twitch_auto_clipper/actions/workflows/test_runner.yml)
|
|
2
|
+
|
|
3
|
+
# twitch_auto_clipper
|
|
4
|
+
|
|
5
|
+
Automatically create Twitch clips when chat activity spikes.
|
|
6
|
+
|
|
7
|
+
Twitch Auto Clipper monitors Twitch chat and detects moments where a large number of viewers send the same emote/message within a short period of time. When activity exceeds a configurable threshold, it automatically creates a Twitch clip.
|
|
8
|
+
|
|
9
|
+
[demo]
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install twitch_auto_clipper
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
Minimum Python version supported by `twitch_auto_clipper` is 3.10.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from twitch_auto_clipper_sqrtminusone.TwitchAutoClipper import TwitchAutoClipper
|
|
25
|
+
|
|
26
|
+
clipper = TwitchAutoClipper(
|
|
27
|
+
["Marlon", "Lacy"],
|
|
28
|
+
"YOUR_CLIENT_ID",
|
|
29
|
+
"YOUR_CLIENT_SECRET",
|
|
30
|
+
on_clip=lambda clip: print(clip),
|
|
31
|
+
common_value=1,
|
|
32
|
+
emote_value=2,
|
|
33
|
+
clipable_message_ratio=0.5,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
clipper.start()
|
|
37
|
+
clipper.join()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> [!IMPORTANT]
|
|
41
|
+
> `client_id` and `client_secret` are saved in [TwitchAuthTokens](twitch_auto_clipper_sqrtminusone/auth/TwitchAuthTokens.py) and only sent to the Twitch API.
|
|
42
|
+
|
|
43
|
+
> [!IMPORTANT]
|
|
44
|
+
> Upon initialization of [TwitchAutoClipper](twitch_auto_clipper_sqrtminusone/TwitchAutoClipper.py), your web browser should open to gain clipping permission for your Twitch account (as shown in the demo).
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- Automatically handles Twitch OAuth token refresh
|
|
49
|
+
- Automatically monitors multiple Twitch streams
|
|
50
|
+
- Supports custom emotes/messages as clipping triggers
|
|
51
|
+
- Optional 7TV emote support
|
|
52
|
+
- Runs each streamer in separate thread
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Twitch chat
|
|
58
|
+
│
|
|
59
|
+
▼
|
|
60
|
+
Collect messages
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
Count messages / emotes
|
|
64
|
+
│
|
|
65
|
+
▼
|
|
66
|
+
Detect activity spike
|
|
67
|
+
│
|
|
68
|
+
▼
|
|
69
|
+
Threshold exceeded?
|
|
70
|
+
│
|
|
71
|
+
YES
|
|
72
|
+
▼
|
|
73
|
+
Create Twitch clip
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
Call on_clip callback
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
There are two classes that you will typically need:
|
|
82
|
+
|
|
83
|
+
- `TwitchAutoClipper` — monitors Twitch chat and automatically creates clips when a message reaches the configured threshold.
|
|
84
|
+
- `Clip` — contains information about a clip that was created.
|
|
85
|
+
|
|
86
|
+
### `TwitchAutoClipper`
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
TwitchAutoClipper(
|
|
90
|
+
streamers_names,
|
|
91
|
+
client_id,
|
|
92
|
+
client_secret,
|
|
93
|
+
on_clip,
|
|
94
|
+
counter_interval_seconds=...,
|
|
95
|
+
clipable_message_ratio=...,
|
|
96
|
+
clipable_wait=...,
|
|
97
|
+
common_value=...,
|
|
98
|
+
emote_value=...,
|
|
99
|
+
excluded_words=...,
|
|
100
|
+
logging_handlers=...,
|
|
101
|
+
logging_level=...
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
| Argument | Description |
|
|
106
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `streamers_names` | List of Twitch usernames to monitor. Usernames are case-insensitive and are converted to lowercase. |
|
|
108
|
+
| `client_id` | Your Twitch application client ID. Available from the [Twitch Developer Console](https://dev.twitch.tv/docs/authentication/register-app). |
|
|
109
|
+
| `client_secret` | Your Twitch application client secret. Available from the [Twitch Developer Console](https://dev.twitch.tv/docs/authentication/register-app). |
|
|
110
|
+
| `on_clip` | Callback function invoked with a `Clip` instance when a clip is created. |
|
|
111
|
+
| `counter_interval_seconds` | Time interval over which chat message frequencies are counted. Once the interval ends, the counters are reset. |
|
|
112
|
+
| `clipable_message_ratio` | The weighted frequency of the most frequent message divided by the total number of messages received during the current interval. Because messages can have different values (`common_value` and `emote_value`), the ratio can exceed 1. |
|
|
113
|
+
| `clipable_wait` | Time to wait before creating a clip after a message reaches the clipping threshold. The waiting and clipping are performed in a separate thread, so this does not block the streamer thread. |
|
|
114
|
+
| `common_value` | Value assigned to a regular chat message. A regular message is any message that is not a 7TV emote when the streamer has a 7TV profile. |
|
|
115
|
+
| `emote_value` | Value assigned to a 7TV emote. |
|
|
116
|
+
| `excluded_words` | Words that should be excluded from message frequency counting. |
|
|
117
|
+
| `logging_handlers` | Logging handlers used by the package. See [`examples/logger.py`](examples/logger.py) for an example using multiple handlers. |
|
|
118
|
+
| `logging_level` | Logging level used by the package. See the [Python logging documentation](https://docs.python.org/3/library/logging.html#logging-levels). |
|
|
119
|
+
|
|
120
|
+
> [!NOTE]
|
|
121
|
+
> If multiple streamers are configured, `on_clip` may be called concurrently from multiple threads. Make sure your callback is thread-safe and use the appropriate [Python threading APIs](https://docs.python.org/3/library/threading.html) when necessary.
|
|
122
|
+
|
|
123
|
+
### `Clip`
|
|
124
|
+
|
|
125
|
+
A `Clip` instance contains information about the clip that was created.
|
|
126
|
+
|
|
127
|
+
| Attribute | Description |
|
|
128
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
129
|
+
| `message` | The chat message that triggered the clip. |
|
|
130
|
+
| `message_count` | Number of occurrences of the triggering message. |
|
|
131
|
+
| `ratio` | Ratio of the triggering message to the total number of messages received during the counting interval. |
|
|
132
|
+
| `broadcaster_id` | Twitch broadcaster ID of the streamer for whom the clip was created. See the [Twitch API documentation](https://dev.twitch.tv/docs/api/reference). |
|
|
133
|
+
| `url` | URL of the created clip. |
|
|
134
|
+
| `timestamp` | Expected timestamp of the event highlighted by the clip. |
|
|
135
|
+
| `duration` | Clip duration in seconds. |
|
|
136
|
+
|
|
137
|
+
## Motivation
|
|
138
|
+
|
|
139
|
+
Manually watching an entire stream to find moments worth clipping is time-consuming.
|
|
140
|
+
|
|
141
|
+
`twitch_auto_clipper` uses the audience's reaction as a signal: when chat suddenly becomes active around a particular message or emote, the bot can automatically capture the moment.
|
|
142
|
+
|
|
143
|
+
## Roadmap
|
|
144
|
+
|
|
145
|
+
- [x] Analyze Twitch chat through IRC
|
|
146
|
+
- [x] Twitch OAuth authentication
|
|
147
|
+
- [x] Automatically create clips
|
|
148
|
+
- [x] Monitor multiple live streamers
|
|
149
|
+
- [x] Save clips to a log
|
|
150
|
+
- [x] Add automated tests
|
|
151
|
+
- [ ] Pip package
|
|
152
|
+
- [ ] Web page for constants editing
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from twitch_auto_clipper_sqrtminusone.TwitchAutoClipper import TwitchAutoClipper
|
|
2
|
+
from twitch_auto_clipper_sqrtminusone.utils.utils import now_formated
|
|
3
|
+
from twitch_auto_clipper_sqrtminusone.clip.Clip import Clip
|
|
4
|
+
|
|
5
|
+
import csv
|
|
6
|
+
from dataclasses import asdict, fields, is_dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from threading import Lock
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Logger:
|
|
14
|
+
def __open_writer(self, csvfile):
|
|
15
|
+
return csv.DictWriter(
|
|
16
|
+
csvfile, fieldnames=self.fieldnames, quoting=csv.QUOTE_NONNUMERIC
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def __init__(self, type_to_log, dir=Path("log"), filename=f"{now_formated()}.csv"):
|
|
20
|
+
assert is_dataclass(type_to_log)
|
|
21
|
+
|
|
22
|
+
os.makedirs(dir, exist_ok=True)
|
|
23
|
+
|
|
24
|
+
self.type = type_to_log
|
|
25
|
+
self.path = Path(dir, filename)
|
|
26
|
+
self.fieldnames = [field.name for field in fields(type_to_log)]
|
|
27
|
+
self.file_lock = Lock()
|
|
28
|
+
|
|
29
|
+
with open(self.path, "w+") as csvfile:
|
|
30
|
+
writer = self.__open_writer(csvfile)
|
|
31
|
+
writer.writeheader()
|
|
32
|
+
|
|
33
|
+
def write(self, data):
|
|
34
|
+
assert type(data) is self.type
|
|
35
|
+
with self.file_lock:
|
|
36
|
+
with open(self.path, "a") as csvfile:
|
|
37
|
+
writer = self.__open_writer(csvfile)
|
|
38
|
+
writer.writerow(asdict(data))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
load_dotenv()
|
|
42
|
+
|
|
43
|
+
CLIP_LOGGER = Logger(Clip)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def on_clip(clip):
|
|
47
|
+
CLIP_LOGGER.write(clip)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
clipper = TwitchAutoClipper(
|
|
51
|
+
["Marlon", "Lacy"],
|
|
52
|
+
os.getenv("client_id"),
|
|
53
|
+
os.getenv("client_secret"),
|
|
54
|
+
on_clip=on_clip,
|
|
55
|
+
common_value=1,
|
|
56
|
+
emote_value=2,
|
|
57
|
+
clipable_message_ratio=0.2,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
clipper.start()
|
|
61
|
+
clipper.join()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from twitch_auto_clipper_sqrtminusone.TwitchAutoClipper import TwitchAutoClipper
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
from os import getenv
|
|
5
|
+
|
|
6
|
+
load_dotenv()
|
|
7
|
+
|
|
8
|
+
clipper = TwitchAutoClipper(
|
|
9
|
+
["Marlon", "Lacy"],
|
|
10
|
+
getenv("client_id"),
|
|
11
|
+
getenv("client_secret"),
|
|
12
|
+
on_clip=lambda clip: print(clip),
|
|
13
|
+
common_value=1,
|
|
14
|
+
emote_value=2,
|
|
15
|
+
clipable_message_ratio=0.2,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
clipper.start()
|
|
19
|
+
clipper.join()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.32"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "twitch_auto_clipper"
|
|
7
|
+
version = "0.1.7"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Maksym Ziborov", email="twitch_auto_clipper@blkflag.org" },
|
|
10
|
+
]
|
|
11
|
+
description = "Automatically create Twitch clips when chat activity spikes"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"requests>=2.34.2",
|
|
20
|
+
"sortedcollections>=2.1.0",
|
|
21
|
+
"dacite>=1.9.2"
|
|
22
|
+
]
|
|
23
|
+
license = "MIT"
|
|
24
|
+
license-files = ["LICEN[CS]E*"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
source = "https://github.com/sqrtminustwo/twitch_auto_clipper"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
test = [
|
|
32
|
+
"coverage>=7.16.0",
|
|
33
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pip install -i https://test.pypi.org/simple/ twitch-auto-clipper-sqrtminusone==0.1.4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
python -m unittest "$1"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
python -m unittest discover tests/
|
|
File without changes
|
|
File without changes
|