diff --git a/README.md b/README.md
index 9495400..59c4a24 100644
--- a/README.md
+++ b/README.md
@@ -17,13 +17,12 @@ This program is still quite experimental and is mainly a proof-of-concept as it
- Green nodes indicate the primary channel(s) node and neighbourhood
- Blue nodes indicate all channels that have been *fully* parsed
- Red nodes indicate channel nodes that have been partially parsed, usually by running into limits
+- Size of node is dictated by number of connections
+- If configured, thickness of edges are dictated by number of collabs between two streamers
-You can drag around nodes and hover over them for some more details.
+You can drag around nodes and edges, and hover over them for some more details.
-Note: No handling for rate limiting is implemented yet as this is a POC.
-Due to time it takes to fetch data and parse, when this is single-threaded it should be fine without hitting rate limits as it is sequentially fetching data.
-
-No errors from Twitch API are handled, or rather, only config input errors are handled. Good luck.
+Not all errors from Twitch API are handled. Good luck.
Once you have your config setup, run `main.py` and when it is done (it will log depth progress), a file called `output.html` and folder `lib` will be created in the same directory. Open this in a web browser to view the Collab Network.
@@ -33,13 +32,12 @@ The html file will only work if the `lib` folder is present!
In that case, you can download a portable executable of this program.
-Click on the [latest action here](https://github.com/WolfwithSword/TwitchCollabNetwork/actions/workflows/build.yml?query=branch%3Amain+is%3Asuccess) and download the latest build for your operating system.
-Check that the tag/branch is "main" for latest/nightly. Alternatively, check on the right side of the screen for [versioned releases](https://github.com/WolfwithSword/TwitchCollabNetwork/releases/latest)!
+Check on the right side of the screen for [versioned releases](https://github.com/WolfwithSword/TwitchCollabNetwork/releases/latest)!
+Alternatively, click on the [latest action here](https://github.com/WolfwithSword/TwitchCollabNetwork/actions/workflows/build.yml?query=branch%3Amain+is%3Asuccess) for the latest dev/nightly release!
Extract the zip to its own folder and make sure it has the executable, templates folder and config.ini.
-Configure the config.ini as per below and you're good to go. As for version updating, update whenever you feel like it by downloading a new portable version.
-
+Configure the config.ini as per below and you're good to go. As for version updating, update whenever you feel like it by downloading a new portable version and overwrite the application and template folders. For config, migrate your config manually in case new settings were added.
# Setup
@@ -49,52 +47,57 @@ Configure the config.ini as per below and you're good to go. As for version upda
This was built using Python 3.10. See `requirements.txt` for dependencies
### Config
-You will need to configure the `config.ini` accordingly.
+You will need to configure the `config.ini` accordingly. Alternatively, you can make multiple and use the CLI parameters to run different configs.
-#### [DISPLAY]
-use_images: `true` if you want nodes to use profile pictures. `false` for coloured dots.
-
-primary_channel: `channel_name` for your primary channel(s). Can be a comma separated list of multiple channels to mark as primaries
+#### [DISPLAY]
-blacklisted_users: `twitchname_1,twitchname_2` comma separated list of channels names to ignore in the network generation. Sponsor/Corporate accounts are a good option here as it will help cut down on users!
+| Setting | Type/Default | Description |
+|-------------------|:---------------------------:|-------------------------------------------------------------------------------------------------------------------|
+| use_images | boolean (`true`) | `true` for nodes to use profile pictures.
`false` for coloured dots |
+| primary_channel | string(s) (comma-separated) | One or more channel names to use as primary starting channels |
+| blacklisted_users | string(s) (comma-separated) | Comma separated list of channels to ignore completely.
Useful to add sponsor/corporate/company accounts here. |
+| weighted_edges | boolean (`false`) | Whether or not to thicken lines/edges between users, based on number of times they've collaborated |
#### [DATA]
-max_depth: `7` Max number of outward channels to look at before stopping
+| Setting | Type/Default | Description |
+|--------------|:------------:|-------------------------------------------------------------------------------------------|
+| max_depth | int (`7`) | Max number for depth of outward channels to look at before stopping |
+| max_users | int (`500`) | Max number of channels/users to look at before stopping |
+| max_vods | int (`100`) | Max number of public vods on a channel to scan. Starts at latest first. Hard limit of 100 |
+| max_children | int (`60`) | Max number of children a node can have before it stops processing more for *that* node |
-max_users: `500` Max number of channels/users to look at before stopping
-
-max_vods: `100` Max number of vods on a channel to scan through. Starts at most recent. Hard limit of 100.
-
-max_children: `60` Max number of children a node can have before it stops processing more on *that* node
#### [TWITCH]
See [Twitch Developer Docs](https://dev.twitch.tv/docs/api/get-started/) on how to get your id/secret
-client_id: `your_dev_app_client_id`
+| Setting | Type/Default | Description |
+|---------------|:------------:|------------------------------|
+| client_id | string | Twitch Dev App client id |
+| client_secret | string | Twitch Dev App client secret |
-client_secret: `you_dev_app_client_secret`
#### [CONCURRENCY]
This program supports parallelism / concurrency for user processing either in API requests or cache fetching.
-enabled: `true/false` enable parallel processing concurrency
-
-max_concurrency: `12` max number of concurrent processes to run. Recommend 5-20. If you hit rate-limiting from twitch API, it will pause until the rate opens back up.
+| Setting | Type/Default | Description |
+|-----------------|:----------------:|--------------------------------------------------------------------------------------------------------------------------------------------------|
+| enabled | boolean (`true`) | Enable/Disable parallel processing for API calls to Twitch |
+| max_concurrency | int (`12`) | Max number of concurrent processes. Recommend 5-20.
If you hit Twitch's rate limits, it will wait until your limit resets before continuing. |
#### [CACHE]
This program supports file/disk based caching. Since this program is used to generate an output after running and is not run as a live service, a disk based cache is more useful than in-memory cache, as now API results can persist in between sessions.
-enabled: `true/false` enable local disk caching for twitch API results.
-
-user_expiry_s: `3600` number of seconds to keep user API results from twitch before expiring. This can be a higher number without affecting much.
-
-vodlist_expiry_s: `600` number of seconds to keep list of user's vods with tagged users from twitch API before expiring. Can be long, but if a new public vod goes up, it won't be picked up until this expires
+| Setting | Type/Default | Description |
+|------------------|:----------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| enabled | boolean (`true`) | Enable/Disable local disk caching for Twitch API results |
+| user_expiry_s | int (`3600`) | Number of Seconds to cache API results for Twitch Users.
This is generally okay to have really high, as User data almost never changes. |
+| vodlist_expiry_s | int (`600`) | Number of seconds to cache API results for Twitch Vod titles.
Recommend not too long, as if a streamer goes live before this expires, it will not get the newest vod title until the expiry time lapses. |
### CLI Parameters
diff --git a/config.ini b/config.ini
index 0f2d6c5..e22f332 100644
--- a/config.ini
+++ b/config.ini
@@ -2,6 +2,7 @@
use_images=true
primary_channel=REPLACEME,REPLACEMEORREMOVEME
blacklisted_users=twitchname_1,twitchname_2
+weighted_edges=false
[DATA]
max_depth=7
diff --git a/data/streamer_connection.py b/data/streamer_connection.py
index 69367a3..6140be5 100644
--- a/data/streamer_connection.py
+++ b/data/streamer_connection.py
@@ -9,14 +9,26 @@ def __init__(self, twitch_user: TwitchUser):
self.children = []
self.processed = False
self.color = "blue"
+ self.collab_counts = dict()
def add_child(self, child: Streamer):
if child not in self.children:
self.children.append(child)
+ def add_collab(self, collaborator: Streamer, was_tagged=True):
+ val = 1
+ if not was_tagged:
+ val = 0
+ if collaborator.name not in self.collab_counts:
+ self.collab_counts[collaborator.name] = val
+ else:
+ self.collab_counts[collaborator.name] += val
+ if was_tagged:
+ self.add_child(collaborator)
+
@property
def size(self):
- return len(self.children)
+ return len(self.collab_counts)
@property
def done(self):
@@ -26,4 +38,4 @@ def done(self):
def node_color(self):
if self.processed:
return self.color
- return "red"
\ No newline at end of file
+ return "red"
diff --git a/helpers/config.py b/helpers/config.py
index ba5d4a2..68346fb 100644
--- a/helpers/config.py
+++ b/helpers/config.py
@@ -75,3 +75,7 @@ def blacklisted_channelnames(self) -> list:
@property
def cache_enabled(self) -> bool:
return self.getboolean(section='CACHE', option='enabled', fallback=False)
+
+ @property
+ def weighted_edges(self) -> bool:
+ return self.getboolean(section='DISPLAY', option='weighted_edges', fallback=False)
diff --git a/helpers/twitch_utils.py b/helpers/twitch_utils.py
index 121445e..1651e1d 100644
--- a/helpers/twitch_utils.py
+++ b/helpers/twitch_utils.py
@@ -99,13 +99,16 @@ async def find_connections_from_videos(self, videos: list[Video],
u = await self.get_user_by_name(username=n)
if u:
child = StreamerConnection(u)
- user.add_child(child)
- child.add_child(user) # Bidirectional enforcement
+ user.add_collab(child)
+ child.add_collab(user, was_tagged=False)
users[child.name] = child
elif n not in [x.name.strip() for x in user.children]:
- user.add_child(users[n])
- if user not in users[n].children: # Bidirectional enforcement
- users[n].add_child(user) # Bidirectional enforcement
+ user.add_collab(users[n])
+ if user not in users[n].children:
+ users[n].add_collab(user, was_tagged=False)
+ elif n in users:
+ # Just increment collab counter
+ user.add_collab(users[n])
if len(users) >= self.config.max_connections:
break
if user.size >= self.config.max_children:
diff --git a/main.py b/main.py
index 23f8647..37c8440 100644
--- a/main.py
+++ b/main.py
@@ -142,10 +142,27 @@ async def twitch_run():
url=f"https://twitch.tv/{user.name}", channel_name=user.name, connections=len(user.children),
border=user.node_color)
+ weighted_edges = config.weighted_edges
for u in users:
user = users[u]
for child in user.children:
- G.add_edge(user.name, child.name)
+ if user.name == child.name:
+ title = (f"{user.name} tagged themselves {user.collab_counts.get(child.name, 0)} "
+ f"time{'s' if user.collab_counts.get(child.name, 0) != 1 else ''}")
+ weight = 1
+ else:
+ user_tag_child_count = user.collab_counts.get(child.name, 0)
+ child_tag_user_count = child.collab_counts.get(user.name, 0)
+ title = (f"{user.name} tagged {child.name} {user_tag_child_count} "
+ f"time{'s' if user_tag_child_count != 1 else ''}"
+ f"
{child.name} tagged {user.name} {child_tag_user_count} "
+ f"time{'s' if child_tag_user_count != 1 else ''}")
+ weight = min(max(1,
+ max(user.collab_counts.get(child.name, 1), child.collab_counts.get(user.name, 1))
+ * 0.6), 10)
+ if not weighted_edges:
+ weight = 1
+ G.add_edge(user.name, child.name, title=title, weight=weight, parent=user.name, child=child.name)
net = Network(notebook=False, height="1500px", width="100%",
bgcolor="#222222",
diff --git a/templates/template.html b/templates/template.html
index e24ac52..a048537 100644
--- a/templates/template.html
+++ b/templates/template.html
@@ -508,31 +508,42 @@