diff options
| author | Bobby <[email protected]> | 2024-09-01 05:37:38 -0400 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-09-01 05:37:38 -0400 |
| commit | 1afd44cda6331a3fbd2a2a83cc1a3850452f8e14 (patch) | |
| tree | 2382e01fbdbac9d21bc02d3a0279616ab78205b4 | |
| parent | 6346d1913bcd54f4402b7ce640fd10c752084c83 (diff) | |
| download | yugen-1afd44cda6331a3fbd2a2a83cc1a3850452f8e14.tar.xz yugen-1afd44cda6331a3fbd2a2a83cc1a3850452f8e14.zip | |
fix list buttons. anime detail page
| -rw-r--r-- | static/css/main.css | 71 | ||||
| -rw-r--r-- | templates/user_profile/user_anime_list.html | 8 | ||||
| -rw-r--r-- | templates/watch/watch.html | 529 | ||||
| -rw-r--r-- | watch/utils.py | 6 | ||||
| -rw-r--r-- | watch/views.py | 51 |
5 files changed, 618 insertions, 47 deletions
diff --git a/static/css/main.css b/static/css/main.css index d40ada8..c6ee6a9 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -705,6 +705,11 @@ video { margin-bottom: 2rem; } +.-mx-2 { + margin-left: -0.5rem; + margin-right: -0.5rem; +} + .mb-2 { margin-bottom: 0.5rem; } @@ -745,6 +750,14 @@ video { margin-top: 2rem; } +.mt-\[0\.35rem\] { + margin-top: 0.35rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + .block { display: block; } @@ -789,6 +802,11 @@ video { height: 1.5rem; } +.size-5 { + width: 1.25rem; + height: 1.25rem; +} + .h-24 { height: 6rem; } @@ -829,6 +847,10 @@ video { height: 100%; } +.h-16 { + height: 4rem; +} + .max-h-24 { max-height: 6rem; } @@ -894,6 +916,14 @@ video { width: max-content; } +.w-48 { + width: 12rem; +} + +.w-12 { + width: 3rem; +} + .min-w-32 { min-width: 8rem; } @@ -931,6 +961,23 @@ video { max-width: calc(100% - 10rem); } +.max-w-fit { + max-width: -moz-fit-content; + max-width: fit-content; +} + +.max-w-\[calc\(100\%-4rem\)\] { + max-width: calc(100% - 4rem); +} + +.max-w-0\.5 { + max-width: 0.125rem; +} + +.flex-1 { + flex: 1 1 0%; +} + .origin-left { transform-origin: left; } @@ -1031,6 +1078,10 @@ video { justify-content: space-around; } +.justify-evenly { + justify-content: space-evenly; +} + .gap-1 { gap: 0.25rem; } @@ -1047,6 +1098,10 @@ video { gap: 2rem; } +.gap-11 { + gap: 2.75rem; +} + .space-y-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); @@ -1514,6 +1569,10 @@ video { text-align: center; } +.text-right { + text-align: right; +} + .text-2xl { font-size: 1.5rem; line-height: 2rem; @@ -2246,6 +2305,14 @@ main { flex-wrap: wrap; } + .sm\:place-content-center { + place-content: center; + } + + .sm\:items-center { + align-items: center; + } + .sm\:gap-2 { gap: 0.5rem; } @@ -2341,6 +2408,10 @@ main { flex-wrap: nowrap; } + .lg\:items-start { + align-items: flex-start; + } + .lg\:items-center { align-items: center; } diff --git a/templates/user_profile/user_anime_list.html b/templates/user_profile/user_anime_list.html index fc7ab3d..c31be13 100644 --- a/templates/user_profile/user_anime_list.html +++ b/templates/user_profile/user_anime_list.html @@ -140,20 +140,20 @@ </section> <section class="flex flex-row justify-between my-4"> {% if prev_offset %} - <a href="{% url 'user_profile:user_profile' %}?category=anime_list&offset={{ prev_offset }}{% if filter %}&filter={{ filter }}{% endif %}" class="bg-purple-600 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2"> + <a href="{% url 'user_profile:user_profile' %}?category=anime_list&offset={{ prev_offset }}{% if filter %}&filter={{ filter }}{% endif %}" class="bg-{{ user.preferences.accent_colour }}-600 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2"> <span>Load Previous</span> </a> {% else %} - <a class="bg-purple-600 bg-opacity-20 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2 cursor-not-allowed"> + <a class="bg-{{ user.preferences.accent_colour }}-600 bg-opacity-20 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2 cursor-not-allowed"> <span>Load Previous</span> </a> {% endif %} {% if next_offset %} - <a href="{% url 'user_profile:user_profile' %}?category=anime_list&offset={{ next_offset }}{% if filter %}&filter={{ filter }}{% endif %}" class="bg-purple-600 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2"> + <a href="{% url 'user_profile:user_profile' %}?category=anime_list&offset={{ next_offset }}{% if filter %}&filter={{ filter }}{% endif %}" class="bg-{{ user.preferences.accent_colour }}-600 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2"> <span>Load Next</span> </a> {% else %} - <a class="bg-purple-600 bg-opacity-20 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2 cursor-not-allowed"> + <a class="bg-{{ user.preferences.accent_colour }}-600 bg-opacity-20 text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2 cursor-not-allowed"> <span>Load Next</span> </a> {% endif %} diff --git a/templates/watch/watch.html b/templates/watch/watch.html index 2ab3cc3..47fcf6c 100644 --- a/templates/watch/watch.html +++ b/templates/watch/watch.html @@ -1,8 +1,10 @@ {% extends "partials/base.html" %} +{% load custom_filters %} {% block css %} <link rel="stylesheet" href="https://cdn.vidstack.io/player/theme.css" /> <link rel="stylesheet" href="https://cdn.vidstack.io/player/audio.css" /> <link rel="stylesheet" href="https://cdn.vidstack.io/player/video.css" /> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/> {% endblock css %} {% block content %} <div class="flex flex-col lg:flex-row mt-4 gap-2"> @@ -25,18 +27,32 @@ <div class="flex flex-col gap-2 h-full max-h-96 lg:h-[39vw] lg:max-h-[761px] overflow-y-auto"> {% for episode in anime_episodes.episodes %} {% if episode.number == current_episode %} - <a id="selected-episode" href="{% url "watch:watch_episode" anime_id episode.number %}" class="flex flex-row justify-between items-center w-full gap-4 bg-{{ user.preferences.accent_colour }}-600 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> + <a id="selected-episode" href="{% url "watch:watch_episode" anime_id episode.number %}{% if request.GET.mode %}?mode={{ request.GET.mode }}{% endif %}" class="flex flex-row gap-4 justify-between items-center w-full bg-{{ user.preferences.accent_colour }}-600 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> <span class="truncate max-w-full overflow-hidden text-ellipsis whitespace-nowrap">{{ episode.number }}. {{ episode.title }}</span> <span class="flex flex-row item-center gap-2"> - {% if anime_selected.episodes.sub >= episode.number %} - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6 bg-green-700 p-1 rounded" title="Available in Sub"> - <path fill-rule="evenodd" d="M4.848 2.771A49.144 49.144 0 0 1 12 2.25c2.43 0 4.817.178 7.152.52 1.978.292 3.348 2.024 3.348 3.97v6.02c0 1.946-1.37 3.678-3.348 3.97a48.901 48.901 0 0 1-3.476.383.39.39 0 0 0-.297.17l-2.755 4.133a.75.75 0 0 1-1.248 0l-2.755-4.133a.39.39 0 0 0-.297-.17 48.9 48.9 0 0 1-3.476-.384c-1.978-.29-3.348-2.024-3.348-3.97V6.741c0-1.946 1.37-3.68 3.348-3.97ZM6.75 8.25a.75.75 0 0 1 .75-.75h9a.75.75 0 0 1 0 1.5h-9a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H7.5Z" clip-rule="evenodd" /> + {% if episode.isFiller %} + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff" class="size-3" style="margin-top:0.35rem;" version="1.1" id="Capa_1" viewBox="0 0 23.758 23.758" xml:space="preserve"> + <g> + <g> + <path d="M4.523,23.758V0h14.712v4.021H9.319v5.625h9.916v4.016H9.319v10.096H4.523z"/> + </g> + </g> + </svg> + {% endif %} + + {% if anime_selected.anime.info.stats.episodes.sub >= episode.number %} + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="size-6" viewBox="0 0 24 24" version="1.1"> + <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g fill="#fff" fill-rule="nonzero"> + <path d="M18.75,4 C20.5449254,4 22,5.45507456 22,7.25 L22,16.754591 C22,18.5495164 20.5449254,20.004591 18.75,20.004591 L5.25,20.004591 C3.45507456,20.004591 2,18.5495164 2,16.754591 L2,7.25 C2,5.51696854 3.35645477,4.10075407 5.06557609,4.00514479 L5.25,4 L18.75,4 Z M10.6216203,8.59854135 C8.21322176,7.22468635 5.5,8.85441664 5.5,12 C5.5,15.1433285 8.21538655,16.7747125 10.6208022,15.4065583 C10.9808502,15.2017699 11.106713,14.7438795 10.9019246,14.3838314 C10.6971362,14.0237834 10.2392458,13.8979206 9.8791978,14.102709 C8.48410774,14.8962094 7,14.0045685 7,12 C7,9.9935733 8.48070939,9.10416685 9.87837972,9.90145865 C10.2381704,10.1066989 10.6962184,9.98141095 10.9014586,9.62162028 C11.1066989,9.2618296 10.981411,8.80378156 10.6216203,8.59854135 Z M18.1216203,8.59854135 C15.7132218,7.22468635 13,8.85441664 13,12 C13,15.1433285 15.7153866,16.7747125 18.1208022,15.4065583 C18.4808502,15.2017699 18.606713,14.7438795 18.4019246,14.3838314 C18.1971362,14.0237834 17.7392458,13.8979206 17.3791978,14.102709 C15.9841077,14.8962094 14.5,14.0045685 14.5,12 C14.5,9.9935733 15.9807094,9.10416685 17.3783797,9.90145865 C17.7381704,10.1066989 18.1962184,9.98141095 18.4014586,9.62162028 C18.6066989,9.2618296 18.481411,8.80378156 18.1216203,8.59854135 Z"></path> + </g> + </g> </svg> {% endif %} - {% if anime_selected.episodes.dub >= episode.number %} - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6 bg-blue-700 p-1 rounded" title="Available in Dub"> + {% if anime_selected.anime.info.stats.episodes.dub >= episode.number %} + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4 mt-1" title="Available in Dub"> <path d="M8.25 4.5a3.75 3.75 0 1 1 7.5 0v8.25a3.75 3.75 0 1 1-7.5 0V4.5Z" /> <path d="M6 10.5a.75.75 0 0 1 .75.75v1.5a5.25 5.25 0 1 0 10.5 0v-1.5a.75.75 0 0 1 1.5 0v1.5a6.751 6.751 0 0 1-6 6.709v2.291h3a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1 0-1.5h3v-2.291a6.751 6.751 0 0 1-6-6.709v-1.5A.75.75 0 0 1 6 10.5Z" /> </svg> @@ -45,17 +61,31 @@ </a> {% else %} - <a href="{% url "watch:watch_episode" anime_id episode.number %}" class="flex flex-row justify-between w-full gap-4 {% if episode.number in watched_episodes %}bg-{{ user.preferences.accent_colour }}-600 bg-opacity-20{% else %}bg-white bg-opacity-10{% endif %} p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> + <a href="{% url "watch:watch_episode" anime_id episode.number %}{% if request.GET.mode %}?mode={{ request.GET.mode }}{% endif %}" class="flex flex-row justify-between w-full gap-4 {% if episode.number in watched_episodes %}bg-{{ user.preferences.accent_colour }}-600 bg-opacity-20{% else %}bg-white bg-opacity-10{% endif %} p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> <span class="truncate max-w-full overflow-hidden text-ellipsis whitespace-nowrap">{{ episode.number }}. {{ episode.title }}</span> <span class="flex flex-row item-center gap-2"> - {% if anime_selected.episodes.sub >= episode.number %} - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6 bg-green-700 p-1 rounded" title="Available in Sub"> - <path fill-rule="evenodd" d="M4.848 2.771A49.144 49.144 0 0 1 12 2.25c2.43 0 4.817.178 7.152.52 1.978.292 3.348 2.024 3.348 3.97v6.02c0 1.946-1.37 3.678-3.348 3.97a48.901 48.901 0 0 1-3.476.383.39.39 0 0 0-.297.17l-2.755 4.133a.75.75 0 0 1-1.248 0l-2.755-4.133a.39.39 0 0 0-.297-.17 48.9 48.9 0 0 1-3.476-.384c-1.978-.29-3.348-2.024-3.348-3.97V6.741c0-1.946 1.37-3.68 3.348-3.97ZM6.75 8.25a.75.75 0 0 1 .75-.75h9a.75.75 0 0 1 0 1.5h-9a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H7.5Z" clip-rule="evenodd" /> + {% if episode.isFiller %} + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff" class="size-3" style="margin-top:0.35rem;" version="1.1" id="Capa_1" viewBox="0 0 23.758 23.758" xml:space="preserve"> + <g> + <g> + <path d="M4.523,23.758V0h14.712v4.021H9.319v5.625h9.916v4.016H9.319v10.096H4.523z"/> + </g> + </g> + </svg> + {% endif %} + + {% if anime_selected.anime.info.stats.episodes.sub >= episode.number %} + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="size-6" viewBox="0 0 24 24" version="1.1"> + <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g fill="#fff" fill-rule="nonzero"> + <path d="M18.75,4 C20.5449254,4 22,5.45507456 22,7.25 L22,16.754591 C22,18.5495164 20.5449254,20.004591 18.75,20.004591 L5.25,20.004591 C3.45507456,20.004591 2,18.5495164 2,16.754591 L2,7.25 C2,5.51696854 3.35645477,4.10075407 5.06557609,4.00514479 L5.25,4 L18.75,4 Z M10.6216203,8.59854135 C8.21322176,7.22468635 5.5,8.85441664 5.5,12 C5.5,15.1433285 8.21538655,16.7747125 10.6208022,15.4065583 C10.9808502,15.2017699 11.106713,14.7438795 10.9019246,14.3838314 C10.6971362,14.0237834 10.2392458,13.8979206 9.8791978,14.102709 C8.48410774,14.8962094 7,14.0045685 7,12 C7,9.9935733 8.48070939,9.10416685 9.87837972,9.90145865 C10.2381704,10.1066989 10.6962184,9.98141095 10.9014586,9.62162028 C11.1066989,9.2618296 10.981411,8.80378156 10.6216203,8.59854135 Z M18.1216203,8.59854135 C15.7132218,7.22468635 13,8.85441664 13,12 C13,15.1433285 15.7153866,16.7747125 18.1208022,15.4065583 C18.4808502,15.2017699 18.606713,14.7438795 18.4019246,14.3838314 C18.1971362,14.0237834 17.7392458,13.8979206 17.3791978,14.102709 C15.9841077,14.8962094 14.5,14.0045685 14.5,12 C14.5,9.9935733 15.9807094,9.10416685 17.3783797,9.90145865 C17.7381704,10.1066989 18.1962184,9.98141095 18.4014586,9.62162028 C18.6066989,9.2618296 18.481411,8.80378156 18.1216203,8.59854135 Z"></path> + </g> + </g> </svg> {% endif %} - {% if anime_selected.episodes.dub >= episode.number %} - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6 bg-blue-700 p-1 rounded" title="Available in Dub"> + {% if anime_selected.anime.info.stats.episodes.dub >= episode.number %} + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4 mt-1" title="Available in Dub"> <path d="M8.25 4.5a3.75 3.75 0 1 1 7.5 0v8.25a3.75 3.75 0 1 1-7.5 0V4.5Z" /> <path d="M6 10.5a.75.75 0 0 1 .75.75v1.5a5.25 5.25 0 1 0 10.5 0v-1.5a.75.75 0 0 1 1.5 0v1.5a6.751 6.751 0 0 1-6 6.709v2.291h3a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1 0-1.5h3v-2.291a6.751 6.751 0 0 1-6-6.709v-1.5A.75.75 0 0 1 6 10.5Z" /> </svg> @@ -67,9 +97,484 @@ </div> </div> </div> +<div class="flex flex-col lg:flex-row my-4 gap-2"> + <div class="w-full lg:w-3/4"> + <div class="flex flex-row gap-2 items-center justify-between"> + <h2 class="text-xl font-bold truncate max-w-full overflow-hidden text-ellipsis whitespace-nowrap">Episode {{ current_episode_data.number }} — {{ current_episode_data.title }}</h2> + <div class="flex flex-row gap-1 items-center"> + <a href="{% url "watch:watch_episode" anime_id current_episode_data.number %}?mode=sub" class="{% if mode == "sub" %}bg-{{ user.preferences.accent_colour }}-600{% else %}bg-white bg-opacity-10{% endif %} text-white text-sm font-bold px-4 py-2 rounded">Sub</a> + <a href="{% url "watch:watch_episode" anime_id current_episode_data.number %}?mode=dub" class="{% if mode == "dub" %}bg-{{ user.preferences.accent_colour }}-600{% else %}bg-white bg-opacity-10{% endif %} text-white text-sm font-bold px-4 py-2 rounded">Dub</a> + </div> + </div> + <div class="flex flex-col lg:flex-row w-full my-4 bg-neutral-950 rounded p-2 gap-4"> + <div class="flex flex-col items-center lg:items-start gap-2 min-w-32"> + <img src="{{ anime_data.image }}" alt="{{ anime_data.title.english }}" class="rounded-lg w-48 h-72 object-cover"/> + <div class="flex flex-row gap-2"> + <a href="https://anilist.co/anime/{{ anime_data.id }}" target="_blank" class="text-xs font-bold bg-white bg-opacity-10 rounded px-2 py-1"> + <svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" height="1.5rem" width="1.5rem" xmlns="http://www.w3.org/2000/svg"><path d="M24 17.53v2.421c0 .71-.391 1.101-1.1 1.101h-5l-.057-.165L11.84 3.736c.106-.502.46-.788 1.053-.788h2.422c.71 0 1.1.391 1.1 1.1v12.38H22.9c.71 0 1.1.392 1.1 1.101zM11.034 2.947l6.337 18.104h-4.918l-1.052-3.131H6.019l-1.077 3.131H0L6.361 2.948h4.673zm-.66 10.96-1.69-5.014-1.541 5.015h3.23z"></path></svg> + </a> + <a href="https://myanimelist.net/anime/{{ anime_data.malId }}" target="_blank" class="ext-xs font-bold bg-white bg-opacity-10 rounded px-2 py-1"> + <svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" height="1.5rem" width="1.5rem" xmlns="http://www.w3.org/2000/svg"><path d="M8.273 7.247v8.423l-2.103-.003v-5.216l-2.03 2.404-1.989-2.458-.02 5.285H.001L0 7.247h2.203l1.865 2.545 2.015-2.546 2.19.001zm8.628 2.069l.025 6.335h-2.365l-.008-2.871h-2.8c.07.499.21 1.266.417 1.779.155.381.298.751.583 1.128l-1.705 1.125c-.349-.636-.622-1.337-.878-2.082a9.296 9.296 0 0 1-.507-2.179c-.085-.75-.097-1.471.107-2.212a3.908 3.908 0 0 1 1.161-1.866c.313-.293.749-.5 1.1-.687.351-.187.743-.264 1.107-.359a7.405 7.405 0 0 1 1.191-.183c.398-.034 1.107-.066 2.39-.028l.545 1.749H14.51c-.593.008-.878.001-1.341.209a2.236 2.236 0 0 0-1.278 1.92l2.663.033.038-1.81h2.309zm3.992-2.099v6.627l3.107.032-.43 1.775h-4.807V7.187l2.13.03z"></path></svg> + </a> + </div> + </div> + <div class="flex flex-col gap-2 w-full"> + <h2 class="text-2xl font-bold text-transparent bg-clip-text block w-full truncate overflow-hidden text-ellipsis whitespace-nowrap" style="background: linear-gradient(-45deg, {% if anime_data.color %}{{ anime_data.color }}{% else %}white{% endif %}, white); -webkit-background-clip: text; background-clip: text;"> + {% if user.preferences.title_language == "english" and anime_data.title.english %} + {{ anime_data.title.english }} + {% elif user.preferences.title_language == "native" and anime_data.title.native %} + {{ anime_data.title.native }} + {% else %} + {{ anime_data.title.romaji }} + {% endif %} + </h2> + <p class="max-h-24 overflow-auto text-sm text-white mb-4 no-scrollbar"> + {{ anime_data.description|strip_html }} + </p> + <div class="flex flex-col gap-2 mb-4"> + <div class="flex flex-row gap-4"> + <div class="flex-1"> + <span class="font-bold">Format: </span>{{ anime_data.type }} + </div> + <div class="flex-1"> + <span class="font-bold">Episodes: </span>{{ anime_data.totalEpisodes }} + </div> + </div> + <div class="flex flex-row gap-4"> + <div class="flex-1"> + <span class="font-bold">Year: </span>{{ anime_data.releaseDate }} + </div> + <div class="flex-1"> + <span class="font-bold">Duration: </span>{{ anime_data.duration }} mins + </div> + </div> + <div class="flex flex-row gap-4"> + <div class="flex-1"> + <span class="font-bold">Status: </span>{{ anime_data.status }} + </div> + <div class="flex-1 capitalize"> + <span class="font-bold">Season: </span>{{ anime_data.season }} + </div> + </div> + <div class="flex flex-row gap-4"> + <div class="flex-1"> + <span class="font-bold">Rating: </span>{{ anime_data.rating }} / 100 + </div> + <div class="flex-1"> + <span class="font-bold">Popularity: </span>{{ anime_data.popularity }} + </div> + </div> + <div class="flex flex-row gap-4"> + <div class="flex-1"> + <span class="font-bold">Country: </span>{{ anime_data.countryOfOrigin }} + </div> + <div class="flex-1"> + <span class="font-bold">Studios: </span> + {% for studio in anime_data.studios %} + <span>{{ studio }}</span>{% if not forloop.last %}, {% endif %} + {% endfor %} + </div> + </div> + </div> + <span class="text-xs sm:text-sm font-bold flex gap-2 flex-row flex-wrap items-center"> + {% if anime_data.status == "Ongoing" %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <span class="text-green-500 pt-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + Ongoing + </span> + {% elif anime_data.status == "Not yet aired" %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <span class="text-yellow-500 pt-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + Not yet aired + </span> + {% else %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <span class="text-blue-500 pt-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + Finished + </span> + {% endif %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" class="mr-1"> + <path d="M3.604 7.197l7.138 -3.109a.96 .96 0 0 1 1.27 .527l4.924 11.902a1 1 0 0 1 -.514 1.304l-7.137 3.109a.96 .96 0 0 1 -1.271 -.527l-4.924 -11.903a1 1 0 0 1 .514 -1.304z"></path> + <path d="M15 4h1a1 1 0 0 1 1 1v3.5"></path> + <path d="M20 6c.264 .112 .52 .217 .768 .315a1 1 0 0 1 .53 1.311l-2.298 5.374"></path> + </svg> + {{ anime_data.type }} + </span> + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> + <path fill-rule="evenodd" d="M6.75 2.25A.75.75 0 0 1 7.5 3v1.5h9V3A.75.75 0 0 1 18 3v1.5h.75a3 3 0 0 1 3 3v11.25a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3V7.5a3 3 0 0 1 3-3H6V3a.75.75 0 0 1 .75-.75Zm13.5 9a1.5 1.5 0 0 0-1.5-1.5H5.25a1.5 1.5 0 0 0-1.5 1.5v7.5a1.5 1.5 0 0 0 1.5 1.5h13.5a1.5 1.5 0 0 0 1.5-1.5v-7.5Z" clip-rule="evenodd" /> + </svg> + {{ anime_data.releaseDate }} + </span> + {% if anime_data.rating %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"/> + </svg> + {{ anime_data.rating }} + </span> + {% endif %} + {% if anime_data.totalEpisodes %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5"/> + </svg> + {{ anime_data.totalEpisodes }} + </span> + {% endif %} + </span> + <span class="text-xs sm:text-sm font-bold flex gap-2 flex-row flex-wrap items-center"> + {% for genre in anime_data.genres %} + {% if genre == "Action" %} + <span class="text-xs font-bold bg-green-100 bg-opacity-10 text-green-300 py-1 px-2 rounded-full"> + {% elif genre == "Adventure" %} + <span class="text-xs font-bold bg-pink-100 bg-opacity-10 text-pink-300 py-1 px-2 rounded-full"> + {% elif genre == "Cars" %} + <span class="text-xs font-bold bg-orange-100 bg-opacity-10 text-orange-300 py-1 px-2 rounded-full"> + {% elif genre == "Comedy" %} + <span class="text-xs font-bold bg-purple-100 bg-opacity-10 text-purple-300 py-1 px-2 rounded-full"> + {% elif genre == "Drama" %} + <span class="text-xs font-bold bg-blue-100 bg-opacity-10 text-blue-300 py-1 px-2 rounded-full"> + {% elif genre == "Fantasy" %} + <span class="text-xs font-bold bg-yellow-100 bg-opacity-10 text-yellow-300 py-1 px-2 rounded-full"> + {% elif genre == "Horror" %} + <span class="text-xs font-bold bg-red-100 bg-opacity-10 text-red-300 py-1 px-2 rounded-full"> + {% elif genre == "Mahou Shoujo" %} + <span class="text-xs font-bold bg-teal-100 bg-opacity-10 text-teal-300 py-1 px-2 rounded-full"> + {% elif genre == "Mecha" %} + <span class="text-xs font-bold bg-indigo-100 bg-opacity-10 text-indigo-300 py-1 px-2 rounded-full"> + {% elif genre == "Music" %} + <span class="text-xs font-bold bg-pink-100 bg-opacity-10 text-pink-300 py-1 px-2 rounded-full"> + {% elif genre == "Mystery" %} + <span class="text-xs font-bold bg-purple-100 bg-opacity-10 text-purple-300 py-1 px-2 rounded-full"> + {% elif genre == "Psychological" %} + <span class="text-xs font-bold bg-blue-100 bg-opacity-10 text-blue-300 py-1 px-2 rounded-full"> + {% elif genre == "Romance" %} + <span class="text-xs font-bold bg-yellow-100 bg-opacity-10 text-yellow-300 py-1 px-2 rounded-full"> + {% elif genre == "Sci-Fi" %} + <span class="text-xs font-bold bg-red-100 bg-opacity-10 text-red-300 py-1 px-2 rounded-full"> + {% elif genre == "Slice of Life" %} + <span class="text-xs font-bold bg-teal-100 bg-opacity-10 text-teal-300 py-1 px-2 rounded-full"> + {% elif genre == "Sports" %} + <span class="text-xs font-bold bg-indigo-100 bg-opacity-10 text-indigo-300 py-1 px-2 rounded-full"> + {% elif genre == "Supernatural" %} + <span class="text-xs font-bold bg-green-100 bg-opacity-10 text-green-300 py-1 px-2 rounded-full"> + {% elif genre == "Thriller" %} + <span class="text-xs font-bold bg-orange-100 bg-opacity-10 text-orange-300 py-1 px-2 rounded-full"> + {% else %} + <span class="text-xs font-bold bg-white bg-opacity-10 text-white py-1 px-2 rounded-full"> + {% endif %} + {{ genre }} + </span> + {% endfor %} + </span> + </div> + </div> + {% if anime_data.characters %} + <div class="my-4"> + <h2 class="text-xl font-bold text-white uppercase flex flex-row items-center gap-1 mb-4"> + Characters & Voice Actors + </h2> + <div class="flex flex-wrap"> + {% for character in anime_data.characters %} + <div class="w-1/2 p-2 flex justify-between"> + <div class="flex flex-row gap-2 items-center"> + <img src="{{ character.image }}" alt="{{ character.name }}" class="rounded-full w-16 h-16 object-cover"/> + <div class="flex flex-col gap-2"> + <span class="font-bold"> + {% if user.preferences.character_name_language == "romaji" %} + {{ character.name.full }} + {% else %} + {{ character.name.native }} + {% endif %} + </span> + <span class="capitalize">{{ character.role }}</span> + </div> + </div> + <div class="flex flex-col items-end"> + {% for voice_actor in character.voiceActors|slice:":1" %} + <div class="flex flex-row gap-2 items-center mb-2"> + <div class="flex flex-col gap-2 text-right"> + <span class="font-bold"> + {% if user.preferences.character_name_language == "romaji" %} + {{ voice_actor.name.full }} + {% else %} + {{ voice_actor.name.native }} + {% endif %} + </span> + <span class="capitalize">{{ voice_actor.language }}</span> + </div> + <img src="{{ voice_actor.image }}" alt="{{ voice_actor.name }}" class="rounded-full w-16 h-16 object-cover"/> + </div> + {% endfor %} + </div> + </div> + {% endfor %} + </div> + </div> + {% endif %} + </div> + <div class="w-full lg:w-1/4"> + {% if anime_data.relations %} + <div class="text-xl font-bold text-white uppercase flex flex-row items-center gap-1 mb-4"> + <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 256 512" class="size-4" xmlns="http://www.w3.org/2000/svg"><path d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"></path></svg> + <span>Related</span> + </div> + <div class="flex flex-col gap-2"> + {% for related in anime_data.relations %} + {% if related.type == "MANGA" or related.type == "NOVEL" %} + <div onClick="showToast('{% if related.type == "MANGA" %}Manga{% else %}Novel{% endif %} reading is not supported yet!', false)" class="cursor-pointer flex flex-row w-full gap-4 bg-white bg-opacity-10 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> + <img src="{{ related.image }}" alt="{{ related.title.english }}" class="rounded-lg w-12 h-16 object-cover"/> + <div class="flex flex-col gap-2"> + <span class="font-bold flex gap-2 flex-row items-center"> + {% if related.status == "Ongoing" %} + <span class="text-green-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% elif related.status == "Not yet aired" %} + <span class="text-yellow-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% else %} + <span class="text-blue-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% endif %} + <span> + {% if user.preferences.title_language == "english" and related.title.english %} + {{ related.title.english }} + {% elif user.preferences.title_language == "native" and related.title.native %} + {{ related.title.native }} + {% else %} + {{ related.title.romaji }} + {% endif %} + </span> + </span> + <span class="text-xs sm:text-sm font-bold flex gap-1 flex-row items-start"> + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" class="mr-1"> + <path d="M3.604 7.197l7.138 -3.109a.96 .96 0 0 1 1.27 .527l4.924 11.902a1 1 0 0 1 -.514 1.304l-7.137 3.109a.96 .96 0 0 1 -1.271 -.527l-4.924 -11.903a1 1 0 0 1 .514 -1.304z"></path> + <path d="M15 4h1a1 1 0 0 1 1 1v3.5"></path> + <path d="M20 6c.264 .112 .52 .217 .768 .315a1 1 0 0 1 .53 1.311l-2.298 5.374"></path> + </svg> + {{ related.type }} + </span> + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> + <path d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" /> + </svg> + {{ related.relationType }} + </span> + {% if related.rating %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"/> + </svg> + {{ related.rating }} + </span> + {% endif %} + </span> + </div> + </div> + {% endif %} + {% if related.type == "TV" or related.type == "MOVIE" or related.type == "OVA" or related.type == "ONA" or related.type == "SPECIAL" or related.type == "TV_SHORT" %} + <a href="{% url 'watch:watch' related.id %}" class="flex flex-row w-full gap-4 bg-white bg-opacity-10 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> + <img src="{{ related.image }}" alt="{{ related.title.english }}" class="rounded-lg w-12 h-16 object-cover"/> + <div class="flex flex-col gap-2"> + <span class="font-bold flex gap-2 flex-row items-center"> + {% if related.status == "Ongoing" %} + <span class="text-green-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% elif related.status == "Not yet aired" %} + <span class="text-yellow-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% else %} + <span class="text-blue-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% endif %} + <span> + {% if user.preferences.title_language == "english" and related.title.english %} + {{ related.title.english }} + {% elif user.preferences.title_language == "native" and related.title.native %} + {{ related.title.native }} + {% else %} + {{ related.title.romaji }} + {% endif %} + </span> + </span> + <span class="text-xs sm:text-sm font-bold flex gap-1 flex-row items-start"> + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" class="mr-1"> + <path d="M3.604 7.197l7.138 -3.109a.96 .96 0 0 1 1.27 .527l4.924 11.902a1 1 0 0 1 -.514 1.304l-7.137 3.109a.96 .96 0 0 1 -1.271 -.527l-4.924 -11.903a1 1 0 0 1 .514 -1.304z"></path> + <path d="M15 4h1a1 1 0 0 1 1 1v3.5"></path> + <path d="M20 6c.264 .112 .52 .217 .768 .315a1 1 0 0 1 .53 1.311l-2.298 5.374"></path> + </svg> + {{ related.type }} + </span> + {% if related.rating %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"/> + </svg> + {{ related.rating }} + </span> + {% endif %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> + <path d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" /> + </svg> + {{ related.relationType }} + </span> + {% if related.episodes %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5"/> + </svg> + {{ related.episodes }} + </span> + {% endif %} + </span> + </div> + </a> + {% endif %} + {% endfor %} + </div> + {% endif %} + {% if anime_data.recommendations %} + <div class="text-xl font-bold text-white uppercase flex flex-row items-center gap-1 my-4"> + <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 256 512" class="size-4" xmlns="http://www.w3.org/2000/svg"><path d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"></path></svg> + <span>Recommendations</span> + </div> + <div class="flex flex-col gap-2"> + {% for recommendation in anime_data.recommendations %} + <a href="{% url 'watch:watch' recommendation.id %}" class="flex flex-row w-full gap-4 bg-white bg-opacity-10 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> + <img src="{{ recommendation.image }}" alt="{{ recommendation.title.english }}" class="rounded-lg w-12 h-16 object-cover"/> + <div class="flex flex-col gap-2 max-w-[calc(100%-4rem)]"> + <span class="font-bold flex gap-2 flex-row items-center"> + {% if recommendation.status == "Ongoing" %} + <span class="text-green-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% elif recommendation.status == "Not yet aired" %} + <span class="text-yellow-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% else %} + <span class="text-blue-500"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-2 sm:size-3"> + <circle cx="12" cy="12" r="12" /> + </svg> + </span> + {% endif %} + <span class="truncate max-w-full overflow-hidden text-ellipsis whitespace-nowrap"> + {% if user.preferences.title_language == "english" and recommendation.title.english %} + {{ recommendation.title.english }} + {% elif user.preferences.title_language == "native" and recommendation.title.native %} + {{ recommendation.title.native }} + {% else %} + {{ recommendation.title.romaji }} + {% endif %} + </span> + </span> + <span class="text-xs sm:text-sm font-bold flex gap-1 flex-row items-start"> + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" class="mr-1"> + <path d="M3.604 7.197l7.138 -3.109a.96 .96 0 0 1 1.27 .527l4.924 11.902a1 1 0 0 1 -.514 1.304l-7.137 3.109a.96 .96 0 0 1 -1.271 -.527l-4.924 -11.903a1 1 0 0 1 .514 -1.304z"></path> + <path d="M15 4h1a1 1 0 0 1 1 1v3.5"></path> + <path d="M20 6c.264 .112 .52 .217 .768 .315a1 1 0 0 1 .53 1.311l-2.298 5.374"></path> + </svg> + {{ recommendation.type }} + </span> + {% if recommendation.rating %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a + .563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"/> + </svg> + {{ recommendation.rating }} + </span> + {% if recommendation.episodes %} + <span class="text-xs font-bold bg-white bg-opacity-10 p-1 rounded flex items-center gap-1"> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"> + <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5"/> + </svg> + {{ recommendation.episodes }} + </span> + {% endif %} + </span> + {% endif %} + </div> + </a> + {% endfor %} + </div> + {% endif %} + </div> +</div> +<div id="toastContainer" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-50 flex flex-col space-y-2"></div> {% endblock content %} {% block scripts %} <script> + function showToast(message, isSuccess) { + const toast = document.createElement('div'); + toast.className = `flex items-center p-4 rounded-md shadow-lg transition-opacity duration-500 ease-in-out animate__animated ${ + isSuccess ? 'bg-green-100 text-green-700 animate__fadeInUp' : 'bg-red-100 text-red-700 animate__fadeInUp' + }`; + + const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /></svg>` + + const errorSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" /></svg>` + + toast.innerHTML = ` + <div class="flex items-center"> + ${isSuccess ? checkSVG : errorSVG} + <span class="ml-2">${message}</span> + </div> + `; + + // Append the toast to the container + toastContainer.appendChild(toast); + + // Remove the toast after 3 seconds + setTimeout(() => { + toast.classList.add('animate__fadeOutDown'); + setTimeout(() => { + toastContainer.removeChild(toast); + }, 500); + }, 3000); + } + window.addEventListener('DOMContentLoaded', (event) => { const selectedEpisode = document.getElementById('selected-episode'); if (selectedEpisode) { diff --git a/watch/utils.py b/watch/utils.py index 1b0435f..9603c63 100644 --- a/watch/utils.py +++ b/watch/utils.py @@ -33,7 +33,11 @@ def get_anime_user_history(user, anime_id): def store_in_redis_cache(anime_id, data): - r.set(anime_id, data, ex=60*60) # 1 hour + try: + r.set(anime_id, data, ex=60*60) # 1 hour + except Exception as e: + print(e) + pass def get_from_redis_cache(anime_id): data = r.get(anime_id) diff --git a/watch/views.py b/watch/views.py index 3b95946..2074903 100644 --- a/watch/views.py +++ b/watch/views.py @@ -9,7 +9,6 @@ import json dotenv.load_dotenv() def watch(request, anime_id, episode=None): - # store anime history anime_history = get_anime_user_history(request.user, anime_id) watched_episodes = [h.episode for h in anime_history] @@ -23,44 +22,34 @@ def watch(request, anime_id, episode=None): mode = request.GET.get("mode", request.user.preferences.default_language) - anime_data_cached = get_from_redis_cache(anime_id) + anime_data_cached = get_from_redis_cache(f"anime_{anime_id}_anime_data") + anime_selected_cached = get_from_redis_cache(f"anime_{anime_id}_anime_selected") + anime_episodes_cached = get_from_redis_cache(f"anime_{anime_id}_anime_episodes") if not anime_data_cached: - base_url = f"{os.getenv("CONSUMET_URL")}/meta/anilist/data/{anime_id}?provider=zoro" + base_url = f"{os.getenv("CONSUMET_URL")}/meta/anilist/info/{anime_id}?provider=zoro" response = requests.get(base_url) anime_data = response.json() + store_in_redis_cache(f"anime_{anime_id}_anime_data", json.dumps(anime_data)) + else: + anime_data = json.loads(anime_data_cached) - base_url = f"{os.getenv("ZORO_URL")}/anime/search?q={anime_data["title"]["english"]}&page=1" + if not anime_selected_cached: + z_anime_id = anime_data["episodes"][0]["id"].split("$")[0] + base_url = f"{os.getenv("ZORO_URL")}/anime/info?id={z_anime_id}" response = requests.get(base_url) - anime_search_result = response.json() - anime_selected = [a for a in anime_search_result["animes"] if a["name"].lower() == anime_data["title"]["english"].lower()] - - if not anime_selected: - anime_selected = anime_search_result["animes"][0] - else: - anime_selected = anime_selected[0] + anime_selected = response.json() + store_in_redis_cache(f"anime_{anime_id}_anime_selected", json.dumps(anime_selected)) + else: + anime_selected = json.loads(anime_selected_cached) - base_url = f"{os.getenv("ZORO_URL")}/anime/episodes/{anime_selected["id"]}" + if not anime_episodes_cached: + base_url = f"{os.getenv("ZORO_URL")}/anime/episodes/{anime_selected["anime"]["info"]["id"]}" response = requests.get(base_url) anime_episodes = response.json() + store_in_redis_cache(f"anime_{anime_id}_anime_episodes", json.dumps(anime_episodes)) - anime_data_to_cache = { - "anime_data": anime_data, - "anime_selected": anime_selected, - "anime_episodes": anime_episodes, - } - - store_in_redis_cache(anime_id, json.dumps(anime_data_to_cache)) - else: - anime_data_cached = json.loads(anime_data_cached) - anime_data = anime_data_cached["anime_data"] - anime_selected = anime_data_cached["anime_selected"] - anime_episodes = anime_data_cached["anime_episodes"] - - if mode == "dub" and not anime_selected["episodes"]["dub"]: - mode = "sub" - - if not anime_selected["episodes"][mode] or anime_selected["episodes"][mode] < episode: + if not anime_selected["anime"]["info"]["stats"]["episodes"][mode] or anime_selected["anime"]["info"]["stats"]["episodes"][mode] < episode: mode = "sub" if episode > anime_episodes["totalEpisodes"]: @@ -81,7 +70,6 @@ def watch(request, anime_id, episode=None): if not any(t["kind"] == "captions" for t in episode_data["tracks"]) and mode == "dub" and request.user.preferences.ingrain_sub_subtitles_in_dub: base_url = f"{os.getenv("ZORO_URL")}/anime/episode-srcs?id={episode_d["episodeId"]}?server&category=sub" response = requests.get(base_url).json() - # attach the sub captions to the dub episode data, append - do not replace captions = [t for t in response["tracks"] if t["kind"] == "captions"] if captions: episode_data["tracks"].extend(captions) @@ -90,12 +78,15 @@ def watch(request, anime_id, episode=None): update_anime_user_history(request.user, anime_id, episode, current_watched_time) + current_episode_data = [e for e in anime_episodes["episodes"] if e["number"] == episode][0] + context = { "anime_data": anime_data, "anime_selected": anime_selected, "anime_episodes": anime_episodes, "episode_data": episode_data, "current_episode": episode, + "current_episode_data": current_episode_data, "stream_url": episode_data["sources"][0]["url"], "anime_id": anime_id, "current_episode_name": current_episode_name, |
