diff --git a/core/management/commands/submit_indexnow.py b/core/management/commands/submit_indexnow.py index 191371b..9ff3041 100644 --- a/core/management/commands/submit_indexnow.py +++ b/core/management/commands/submit_indexnow.py @@ -30,6 +30,9 @@ def get_urls_from_sitemap(sitemap_url: str, list_of_urls: list[str]) -> list[str if parser.has_sitemaps(): sitemaps: SitemapIndex = parser.get_sitemaps() for sitemap in sitemaps: + if not sitemap.loc: + continue + list_of_urls.extend( get_urls_from_sitemap( sitemap_url=sitemap.loc, @@ -39,7 +42,7 @@ def get_urls_from_sitemap(sitemap_url: str, list_of_urls: list[str]) -> list[str elif parser.has_urls(): urls: UrlSet = parser.get_urls() - list_of_urls.extend(url.loc for url in urls) + list_of_urls.extend(url.loc for url in urls if url.loc) return list_of_urls @@ -99,6 +102,7 @@ class Command(BaseCommand): api_key_location=api_key_location, ) + status_code: int = 0 urls: list[str] = get_urls_from_sitemap(sitemap_url=sitemap, list_of_urls=[]) chucked_urls: list[list[str]] = get_chucked_urls(urls=urls) for chunk in chucked_urls: diff --git a/core/views.py b/core/views.py index c340e08..d5d2dc3 100644 --- a/core/views.py +++ b/core/views.py @@ -121,7 +121,7 @@ def _build_seo_context( # noqa: PLR0913, PLR0917 def _render_urlset_xml( - url_entries: list[dict[str, str | dict[str, str]]], + url_entries: list[dict[str, str]], ) -> str: """Render a sitemap XML string from URL entries. @@ -184,20 +184,24 @@ def sitemap_view(request: HttpRequest) -> HttpResponse: # Compute last modified per-section so search engines can more intelligently crawl. # Do not fabricate a lastmod date if the section has no data. - twitch_channels_lastmod = Channel.objects.aggregate(max=Max("updated_at"))["max"] - twitch_drops_lastmod = max( - [ + twitch_channels_lastmod: datetime.datetime | None = Channel.objects.aggregate( + max=Max("updated_at"), + )["max"] + + twitch_drops_lastmod: datetime.datetime | None = max( + ( dt for dt in [ DropCampaign.objects.aggregate(max=Max("updated_at"))["max"], RewardCampaign.objects.aggregate(max=Max("updated_at"))["max"], ] if dt is not None - ] - or [None], + ), + default=None, ) - twitch_others_lastmod = max( - [ + + twitch_others_lastmod: datetime.datetime | None = max( + ( dt for dt in [ Game.objects.aggregate(max=Max("updated_at"))["max"], @@ -205,10 +209,13 @@ def sitemap_view(request: HttpRequest) -> HttpResponse: ChatBadgeSet.objects.aggregate(max=Max("updated_at"))["max"], ] if dt is not None - ] - or [None], + ), + default=None, ) - kick_lastmod = KickDropCampaign.objects.aggregate(max=Max("updated_at"))["max"] + + kick_lastmod: datetime.datetime | None = KickDropCampaign.objects.aggregate( + max=Max("updated_at"), + )["max"] sitemap_entries: list[dict[str, str]] = [ {"loc": f"{base_url}/sitemap-static.xml"}, @@ -297,7 +304,7 @@ def sitemap_static_view(request: HttpRequest) -> HttpResponse: "youtube:index", ] - sitemap_urls: list[dict[str, str | dict[str, str]]] = [] + sitemap_urls: list[dict[str, str]] = [] for route_name in static_route_names: url = reverse(route_name) sitemap_urls.append({"url": f"{base_url}{url}"}) @@ -317,13 +324,13 @@ def sitemap_twitch_channels_view(request: HttpRequest) -> HttpResponse: HttpResponse: The rendered sitemap XML. """ base_url: str = _build_base_url(request) - sitemap_urls: list[dict[str, str | dict[str, str]]] = [] + sitemap_urls: list[dict[str, str]] = [] channels: QuerySet[Channel] = Channel.objects.all() for channel in channels: resource_url: str = reverse("twitch:channel_detail", args=[channel.twitch_id]) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str | dict[str, str]] = {"url": full_url} + entry: dict[str, str] = {"url": full_url} if channel.updated_at: entry["lastmod"] = channel.updated_at.isoformat() sitemap_urls.append(entry) @@ -343,16 +350,16 @@ def sitemap_twitch_drops_view(request: HttpRequest) -> HttpResponse: HttpResponse: The rendered sitemap XML. """ base_url: str = _build_base_url(request) - sitemap_urls: list[dict[str, str | dict[str, str]]] = [] + sitemap_urls: list[dict[str, str]] = [] campaigns: QuerySet[DropCampaign] = DropCampaign.objects.all() for campaign in campaigns: resource_url: str = reverse("twitch:campaign_detail", args=[campaign.twitch_id]) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str] = {"url": full_url} + campaign_url_entry: dict[str, str] = {"url": full_url} if campaign.updated_at: - entry["lastmod"] = campaign.updated_at.isoformat() - sitemap_urls.append(entry) + campaign_url_entry["lastmod"] = campaign.updated_at.isoformat() + sitemap_urls.append(campaign_url_entry) reward_campaigns: QuerySet[RewardCampaign] = RewardCampaign.objects.all() for reward_campaign in reward_campaigns: @@ -362,11 +369,13 @@ def sitemap_twitch_drops_view(request: HttpRequest) -> HttpResponse: ) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str | dict[str, str]] = {"url": full_url} + reward_campaign_url_entry: dict[str, str] = {"url": full_url} if reward_campaign.updated_at: - entry["lastmod"] = reward_campaign.updated_at.isoformat() + reward_campaign_url_entry["lastmod"] = ( + reward_campaign.updated_at.isoformat() + ) - sitemap_urls.append(entry) + sitemap_urls.append(reward_campaign_url_entry) xml_content: str = _render_urlset_xml(sitemap_urls) return HttpResponse(xml_content, content_type="application/xml") @@ -383,13 +392,13 @@ def sitemap_twitch_others_view(request: HttpRequest) -> HttpResponse: HttpResponse: The rendered sitemap XML. """ base_url: str = _build_base_url(request) - sitemap_urls: list[dict[str, str | dict[str, str]]] = [] + sitemap_urls: list[dict[str, str]] = [] games: QuerySet[Game] = Game.objects.all() for game in games: resource_url: str = reverse("twitch:game_detail", args=[game.twitch_id]) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str | dict[str, str]] = {"url": full_url} + entry: dict[str, str] = {"url": full_url} if game.updated_at: entry["lastmod"] = game.updated_at.isoformat() @@ -400,7 +409,7 @@ def sitemap_twitch_others_view(request: HttpRequest) -> HttpResponse: for org in orgs: resource_url: str = reverse("twitch:organization_detail", args=[org.twitch_id]) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str | dict[str, str]] = {"url": full_url} + entry: dict[str, str] = {"url": full_url} if org.updated_at: entry["lastmod"] = org.updated_at.isoformat() @@ -431,13 +440,13 @@ def sitemap_kick_view(request: HttpRequest) -> HttpResponse: HttpResponse: The rendered sitemap XML. """ base_url: str = _build_base_url(request) - sitemap_urls: list[dict[str, str | dict[str, str]]] = [] + sitemap_urls: list[dict[str, str]] = [] kick_campaigns: QuerySet[KickDropCampaign] = KickDropCampaign.objects.all() for campaign in kick_campaigns: resource_url: str = reverse("kick:campaign_detail", args=[campaign.kick_id]) full_url: str = f"{base_url}{resource_url}" - entry: dict[str, str | dict[str, str]] = {"url": full_url} + entry: dict[str, str] = {"url": full_url} if campaign.updated_at: entry["lastmod"] = campaign.updated_at.isoformat() @@ -466,7 +475,7 @@ def sitemap_youtube_view(request: HttpRequest) -> HttpResponse: """ base_url: str = _build_base_url(request) - sitemap_urls: list[dict[str, str | dict[str, str]]] = [ + sitemap_urls: list[dict[str, str]] = [ {"url": f"{base_url}{reverse('youtube:index')}"}, ] @@ -980,7 +989,7 @@ def dashboard(request: HttpRequest) -> HttpResponse: kick_campaigns_by_game[game_key]["campaigns"].append({ "campaign": campaign, "channels": list(campaign.channels.all()), - "rewards": list(campaign.rewards.all()), + "rewards": list(campaign.rewards.all()), # pyright: ignore[reportAttributeAccessIssue] }) active_reward_campaigns: QuerySet[RewardCampaign] = ( diff --git a/kick/views.py b/kick/views.py index f87fb08..474806e 100644 --- a/kick/views.py +++ b/kick/views.py @@ -56,18 +56,29 @@ def _build_seo_context( "robots_directive": "index, follow", } if seo_meta: - if seo_meta.get("page_url"): - context["page_url"] = seo_meta["page_url"] - if seo_meta.get("og_type"): - context["og_type"] = seo_meta["og_type"] - if seo_meta.get("robots_directive"): - context["robots_directive"] = seo_meta["robots_directive"] - if seo_meta.get("schema_data"): - context["schema_data"] = json.dumps(seo_meta["schema_data"]) - if seo_meta.get("published_date"): - context["published_date"] = seo_meta["published_date"] - if seo_meta.get("modified_date"): - context["modified_date"] = seo_meta["modified_date"] + page_url: str | None = seo_meta.get("page_url") + if page_url: + context["page_url"] = page_url + + og_type: str | None = seo_meta.get("og_type") + if og_type: + context["og_type"] = og_type + + robots_directive: str | None = seo_meta.get("robots_directive") + if robots_directive: + context["robots_directive"] = robots_directive + + schema_data: dict[str, Any] | None = seo_meta.get("schema_data") + if schema_data: + context["schema_data"] = json.dumps(schema_data) + + published_date: str | None = seo_meta.get("published_date") + if published_date: + context["published_date"] = published_date + + modified_date: str | None = seo_meta.get("modified_date") + if modified_date: + context["modified_date"] = modified_date return context diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png index 6085fb7..d95e48d 100644 Binary files a/static/apple-touch-icon.png and b/static/apple-touch-icon.png differ diff --git a/static/favicon-96x96.png b/static/favicon-96x96.png new file mode 100644 index 0000000..dae8c11 Binary files /dev/null and b/static/favicon-96x96.png differ diff --git a/static/favicon.ico b/static/favicon.ico index ffe57c6..683d2fe 100644 Binary files a/static/favicon.ico and b/static/favicon.ico differ diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..10a1d0c --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1 @@ + diff --git a/static/site.webmanifest b/static/site.webmanifest index 6c094f8..b1e0d67 100644 --- a/static/site.webmanifest +++ b/static/site.webmanifest @@ -1,19 +1,28 @@ { - "name": "ttvdrops", - "short_name": "ttvdrops", + "name": "ttvdrops.lovinator.space", + "short_name": "TTVDrops", + "description": "Track Twitch and Kick drops.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "categories": [ + "games", + "utilities" + ], + "theme_color": "#0c227f", + "background_color": "#000000", "icons": [ { - "src": "/android-chrome-192x192.png", + "src": "web-app-manifest-192x192.png", "sizes": "192x192", - "type": "image/png" + "type": "image/png", + "purpose": "maskable" }, { - "src": "/android-chrome-512x512.png", + "src": "web-app-manifest-512x512.png", "sizes": "512x512", - "type": "image/png" + "type": "image/png", + "purpose": "maskable" } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + ] } diff --git a/static/web-app-manifest-192x192.png b/static/web-app-manifest-192x192.png new file mode 100644 index 0000000..289f1b8 Binary files /dev/null and b/static/web-app-manifest-192x192.png differ diff --git a/static/web-app-manifest-512x512.png b/static/web-app-manifest-512x512.png new file mode 100644 index 0000000..ba8e22b Binary files /dev/null and b/static/web-app-manifest-512x512.png differ diff --git a/templates/base.html b/templates/base.html index f5a6e1a..d274505 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,6 +5,16 @@ + + + + +