More core templates to core template dir
All checks were successful
Deploy to Server / deploy (push) Successful in 12s

This commit is contained in:
Joakim Hellsén 2026-03-17 05:50:01 +01:00
commit 70298fdd1e
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
6 changed files with 61 additions and 44 deletions

View file

@ -0,0 +1,54 @@
{% extends "base.html" %}
{% block title %}
Dataset
{% endblock title %}
{% block content %}
<main>
<h1>Dataset Backups</h1>
<section>
<h2>About this dataset</h2>
<p>This site tracks and publishes open Twitch and Kick drop campaign data.</p>
<p>
The exported datasets on this page are released under <strong>CC0</strong> so you can reuse them freely.
The underlying source data is scraped from Twitch/Kick APIs and pages, so we do not control the
upstream content and cannot guarantee upstream accuracy or permanence.
</p>
<p>Note that some drops has missing or incomplete data due to Twitch API limitations.</p>
<p>
Need a special format for your workflow or research pipeline?
<a href="https://github.com/TheLovinator1/ttvdrops/issues">Contact me via GitHub issues</a>
and describe what you need.
</p>
</section>
{% if datasets %}
<table>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Updated</th>
</tr>
</thead>
<tbody>
{% for dataset in datasets %}
<tr>
<td>
<a href="{% url 'core:dataset_backup_download' dataset.download_path %}">{{ dataset.name }}</a>
</td>
<td>{{ dataset.size }}</td>
<td>
<time datetime="{{ dataset.updated_at|date:'c' }}"
title="{{ dataset.updated_at|date:'DATETIME_FORMAT' }}">
{{ dataset.updated_at|timesince }} ago ({{ dataset.updated_at|date:'M d, Y H:i' }})
</time>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Found {{ dataset_count }} datasets.</p>
{% else %}
<p>No dataset backups found.</p>
{% endif %}
</main>
{% endblock content %}

173
templates/core/debug.html Normal file
View file

@ -0,0 +1,173 @@
{% extends "base.html" %}
{% block title %}
Debug
{% endblock title %}
{% block content %}
<h1>Debug</h1>
<p>
Generated at: <time datetime="{{ now|date:'c' }}"
title="{{ now|date:'DATETIME_FORMAT' }}">{{ now }}</time>
</p>
<section>
<h2>Distinct GraphQL operation_names ({{ operation_names_with_counts|length }})</h2>
{% if operation_names_with_counts %}
<table id="operation-names-table">
<thead>
<tr>
<th>Operation Name</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for item in operation_names_with_counts %}
<tr>
<td>{{ item.trimmed_op }}</td>
<td>{{ item.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</section>
<section>
<h2>Games Without an Assigned Owner ({{ games_without_owner|length }})</h2>
{% if games_without_owner %}
<ul>
{% for game in games_without_owner %}
<li>
<a href="{% url 'twitch:game_detail' game.twitch_id %}">{{ game.display_name }}</a> (ID: {{ game.twitch_id }})
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Campaigns without Images ({{ broken_image_campaigns|length }})</h2>
{% if broken_image_campaigns %}
<ul>
{% for c in broken_image_campaigns %}
<li>
<a href="{% url 'twitch:campaign_detail' c.twitch_id %}">{{ c.name }}</a>
(Game: <a href="{% url 'twitch:game_detail' c.game.twitch_id %}">{{ c.game.display_name }}</a>)
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Benefits without image URLs ({{ broken_benefit_images|length }})</h2>
{% if broken_benefit_images %}
<ul>
{% for b in broken_benefit_images %}
{% with first_drop=b.drops.all.0 %}
<li>
{{ b.name }}
{% if first_drop and first_drop.campaign and first_drop.campaign.game %}
(Game: <a href="{% url 'twitch:game_detail' first_drop.campaign.game.twitch_id %}">{{ first_drop.campaign.game.display_name }}</a>)
{% else %}
(Game: Not linked)
{% endif %}
- URL: {{ b.image_best_url|default:b.image_asset_url|default:'(empty)' }}
</li>
{% endwith %}
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Active Campaigns without Images ({{ active_missing_image|length }})</h2>
{% if active_missing_image %}
<ul>
{% for c in active_missing_image %}
<li>
<a href="{% url 'twitch:campaign_detail' c.twitch_id %}">{{ c.name }}</a>
(Game: <a href="{% url 'twitch:game_detail' c.game.twitch_id %}">{{ c.game.display_name }}</a>)
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Time-Based Drops Without Benefits ({{ drops_without_benefits|length }})</h2>
{% if drops_without_benefits %}
<ul>
{% for d in drops_without_benefits %}
<li>
{{ d.name }}
(Campaign: <a href="{% url 'twitch:campaign_detail' d.campaign.twitch_id %}">{{ d.campaign.name }}</a>
in Game: <a href="{% url 'twitch:game_detail' d.campaign.game.twitch_id %}">{{ d.campaign.game.display_name }}</a>)
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Campaigns With Invalid Dates ({{ invalid_date_campaigns|length }})</h2>
{% if invalid_date_campaigns %}
<ul>
{% for c in invalid_date_campaigns %}
<li>
<a href="{% url 'twitch:campaign_detail' c.twitch_id %}">{{ c.name }}</a>
(Game: <a href="{% url 'twitch:game_detail' c.game.twitch_id %}">{{ c.game.display_name }}</a>)
- Start: {{ c.start_at|default:'(none)' }} / End: {{ c.end_at|default:'(none)' }}
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Duplicate Campaign Names Per Game ({{ duplicate_name_campaigns|length }})</h2>
{% if duplicate_name_campaigns %}
<table>
<thead>
<tr>
<th>Game</th>
<th>Campaign Name</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for row in duplicate_name_campaigns %}
<tr>
<td>
<a href="{% url 'twitch:game_detail' row.game__twitch_id %}">{{ row.game__display_name }}</a>
</td>
<td>{{ row.name }}</td>
<td>{{ row.name_count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
<section>
<h2>Campaigns Missing DropCampaignDetails ({{ campaigns_missing_dropcampaigndetails|length }})</h2>
{% if campaigns_missing_dropcampaigndetails %}
<ul>
{% for c in campaigns_missing_dropcampaigndetails %}
<li>
<a href="{% url 'twitch:campaign_detail' c.twitch_id %}">{{ c.name }}</a>
(Game: <a href="{% url 'twitch:game_detail' c.game.twitch_id %}">{{ c.game.display_name }}</a>)
- Operations: {{ c.operation_names|join:", "|default:'(none)' }}
</li>
{% endfor %}
</ul>
{% else %}
<p>None ✅</p>
{% endif %}
</section>
{% endblock content %}

View file

@ -0,0 +1,155 @@
{% extends "base.html" %}
{% load static %}
{% block title %}
RSS Feeds Documentation
{% endblock title %}
{% block content %}
<main>
<h1>RSS Feeds Documentation</h1>
<p>
You have three types of feeds available for Twitch drops data: RSS, Atom, and Discord.
RSS and Atom feeds are similar and can be used in any RSS reader application.
The main difference is that Atom feeds include additional metadata and support for more complex content, while RSS feeds are more widely supported by older applications.
</p>
<p>
Discord feeds are available under the <code>/discord/</code> endpoints. These are Atom feeds
that include Discord relative timestamps (e.g., <code>&lt;t:1773450272:R&gt;</code>) for dates,
making them ideal for Discord bots and integrations. Future enhancements may include Discord-specific formatting or content.
</p>
<section>
<h2>Global RSS Feeds</h2>
<table>
<thead>
<tr>
<th>Description</th>
<th>RSS</th>
<th>Atom</th>
<th>Discord</th>
</tr>
</thead>
<tbody>
<tr>
<td>New Twitch games</td>
<td>
<a href="https://ttvdrops.lovinator.space/rss/games/">https://ttvdrops.lovinator.space/rss/games/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/atom/games/">https://ttvdrops.lovinator.space/atom/games/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/games/">https://ttvdrops.lovinator.space/discord/games/</a>
</td>
</tr>
<tr>
<td>Latest Twitch drop campaigns</td>
<td>
<a href="https://ttvdrops.lovinator.space/rss/campaigns/">https://ttvdrops.lovinator.space/rss/campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/atom/campaigns/">https://ttvdrops.lovinator.space/atom/campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/campaigns/">https://ttvdrops.lovinator.space/discord/campaigns/</a>
</td>
</tr>
<tr>
<td>Latest Twitch organizations</td>
<td>
<a href="https://ttvdrops.lovinator.space/rss/organizations/">https://ttvdrops.lovinator.space/rss/organizations/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/atom/organizations/">https://ttvdrops.lovinator.space/atom/organizations/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/organizations/">https://ttvdrops.lovinator.space/discord/organizations/</a>
</td>
</tr>
<tr>
<td>Latest Twitch reward campaigns</td>
<td>
<a href="https://ttvdrops.lovinator.space/rss/reward-campaigns/">https://ttvdrops.lovinator.space/rss/reward-campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/atom/reward-campaigns/">https://ttvdrops.lovinator.space/atom/reward-campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/reward-campaigns/">https://ttvdrops.lovinator.space/discord/reward-campaigns/</a>
</td>
</tr>
<tr>
<td>Latest Kick campaigns</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/rss/campaigns/">https://ttvdrops.lovinator.space/kick/rss/campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/atom/campaigns/">https://ttvdrops.lovinator.space/kick/atom/campaigns/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/campaigns/">https://ttvdrops.lovinator.space/discord/campaigns/</a>
</td>
</tr>
<tr>
<td>Latest Kick games</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/rss/games/">https://ttvdrops.lovinator.space/kick/rss/games/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/atom/games/">https://ttvdrops.lovinator.space/kick/atom/games/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/games/">https://ttvdrops.lovinator.space/discord/games/</a>
</td>
</tr>
<tr>
<td>Latest Kick organizations</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/rss/organizations/">https://ttvdrops.lovinator.space/kick/rss/organizations/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/kick/atom/organizations/">https://ttvdrops.lovinator.space/kick/atom/organizations/</a>
</td>
<td>
<a href="https://ttvdrops.lovinator.space/discord/organizations/">https://ttvdrops.lovinator.space/discord/organizations/</a>
</td>
</tr>
</tbody>
</table>
</section>
{% if game %}
<section>
<h2>Filtered RSS Feeds</h2>
<p>You can subscribe to RSS feeds scoped to a specific game.</p>
<table>
<thead>
<tr>
<th>Game</th>
<th>RSS</th>
<th>Atom</th>
<th>Discord</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ game.display_name }}</td>
<td>
<a href="{% url 'core:game_campaign_feed' game.twitch_id %}">
https://ttvdrops.lovinator.space/rss/games/{{ game.twitch_id }}/
</a>
</td>
<td>
<a href="{% url 'core:game_campaign_feed_atom' game.twitch_id %}">
https://ttvdrops.lovinator.space/atom/games/{{ game.twitch_id }}/
</a>
</td>
<td>
<a href="{% url 'core:game_campaign_feed_discord' game.twitch_id %}">
https://ttvdrops.lovinator.space/discord/games/{{ game.twitch_id }}/
</a>
</td>
</tr>
</tbody>
</table>
</section>
{% endif %}
</main>
{% endblock content %}

View file

@ -0,0 +1,108 @@
{% extends "base.html" %}
{% block title %}
Search Results for "{{ query }}"
{% endblock title %}
{% block content %}
<div class="container" id="search-results-container">
<h1 id="page-title">Search Results for "{{ query }}"</h1>
{% if not results.organizations and not results.games and not results.campaigns and not results.drops and not results.benefits and not results.reward_campaigns and not results.badge_sets and not results.badges %}
<p id="no-results">No results found.</p>
{% else %}
{% if results.organizations %}
<h2 id="organizations-header">Organizations</h2>
<ul id="organizations-list">
{% for org in results.organizations %}
<li id="org-{{ org.twitch_id }}">
<a href="{% url 'twitch:organization_detail' org.twitch_id %}">{{ org.name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.games %}
<h2 id="games-header">Games</h2>
<ul id="games-list">
{% for game in results.games %}
<li id="game-{{ game.twitch_id }}">
<a href="{% url 'twitch:game_detail' game.twitch_id %}">{{ game.display_name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.campaigns %}
<h2 id="campaigns-header">Campaigns</h2>
<ul id="campaigns-list">
{% for campaign in results.campaigns %}
<li id="campaign-{{ campaign.twitch_id }}">
<a href="{% url 'twitch:campaign_detail' campaign.twitch_id %}">{{ campaign.name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.drops %}
<h2 id="drops-header">Drops</h2>
<ul id="drops-list">
{% for drop in results.drops %}
<li id="drop-{{ drop.twitch_id }}">
{% if drop.campaign and drop.campaign.twitch_id %}
<a href="{% url 'twitch:campaign_detail' drop.campaign.twitch_id %}">{{ drop.name }}</a> (in {{ drop.campaign.name }})
{% else %}
{{ drop.name }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.benefits %}
<h2 id="benefits-header">Benefits</h2>
<ul id="benefits-list">
{% for benefit in results.benefits %}
<li id="benefit-{{ benefit.twitch_id }}">
{% with first_drop=benefit.drops.first %}
{% if first_drop and first_drop.campaign and first_drop.campaign.twitch_id %}
<a href="{% url 'twitch:campaign_detail' first_drop.campaign.twitch_id %}">{{ benefit.name }}</a>
{% else %}
{{ benefit.name }}
{% endif %}
{% endwith %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.reward_campaigns %}
<h2 id="reward-campaigns-header">Reward Campaigns</h2>
<ul id="reward-campaigns-list">
{% for campaign in results.reward_campaigns %}
<li id="reward-campaign-{{ campaign.twitch_id }}">
{% if campaign.brand %}
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">{{ campaign.brand }}: {{ campaign.name }}</a>
{% else %}
<a href="{% url 'twitch:reward_campaign_detail' campaign.twitch_id %}">{{ campaign.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.badge_sets %}
<h2 id="badge-sets-header">Badge Sets</h2>
<ul id="badge-sets-list">
{% for badge_set in results.badge_sets %}
<li id="badge-set-{{ badge_set.set_id }}">
<a href="{% url 'twitch:badge_set_detail' set_id=badge_set.set_id %}">{{ badge_set.set_id }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.badges %}
<h2 id="badges-header">Chat Badges</h2>
<ul id="badges-list">
{% for badge in results.badges %}
<li id="badge-{{ badge.badge_set.set_id }}-{{ badge.badge_id }}">
<a href="{% url 'twitch:badge_set_detail' set_id=badge.badge_set.set_id %}">{{ badge.title }}</a>
<small>({{ badge.badge_set.set_id }}/{{ badge.badge_id }})</small>
</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</div>
{% endblock content %}