diff --git a/db.sqlite3 b/db.sqlite3 index 080ea3f6..7f81c064 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/filme/admin.py b/filme/admin.py index 23043cc1..655d2eba 100644 --- a/filme/admin.py +++ b/filme/admin.py @@ -1,6 +1,17 @@ from django.contrib import admin -from .models import Filme, Episodio +from .models import Filme, Episodio, Usuario +from django.contrib.auth.admin import UserAdmin + +# Os campos do UserAdmin são tuplas, desta forma transformei em lista python para incluir o novo campo +# de filmes vistos. +campos = list(UserAdmin.fieldsets) +campos.append( + ("Histórico", {'fields': ('filmes_vistos',)}) +) + +UserAdmin.fieldsets = tuple(campos) # Register your models here. admin.site.register(Filme) admin.site.register(Episodio) +admin.site.register(Usuario, UserAdmin) diff --git a/filme/migrations/0001_initial.py b/filme/migrations/0001_initial.py index 4ea379f5..9bd6067a 100644 --- a/filme/migrations/0001_initial.py +++ b/filme/migrations/0001_initial.py @@ -1,6 +1,9 @@ -# Generated by Django 4.2.5 on 2023-09-06 01:07 +# Generated by Django 4.2.5 on 2023-09-24 16:11 +import django.contrib.auth.models +import django.contrib.auth.validators from django.db import migrations, models +import django.db.models.deletion import django.utils.timezone @@ -9,19 +12,56 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ migrations.CreateModel( - name='filme', + name='Filme', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('titulo', models.CharField(max_length=100)), ('thumb', models.ImageField(upload_to='thumb_filmes')), ('descricao', models.TextField(max_length=1000)), - ('categoria', models.CharField(choices=[('ANALISES', 'Análises'), ('PROGRAMACAO', 'Programação'), ('APRESENTACAO', 'Apresentação'), ('OUTROS', 'Outros')], max_length=15)), + ('categoria', models.CharField(choices=[('ANALISES', 'Análises'), ('PROGRAMACAO', 'Programação'), ('APRESENTACAO', 'Apresentação'), ('OUTROS', 'Outros'), {'ANIMES', 'Animes'}], max_length=15)), ('visualizacoes', models.IntegerField(default=0)), ('data_criacao', models.DateTimeField(default=django.utils.timezone.now)), ], ), + migrations.CreateModel( + name='Episodio', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('titulo', models.CharField(max_length=100)), + ('video', models.URLField()), + ('filme', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='episodios', to='filme.filme')), + ], + ), + migrations.CreateModel( + name='Usuario', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('filmes_vistos', models.ManyToManyField(to='filme.filme')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), ] diff --git a/filme/migrations/0002_alter_filme_categoria_episodio.py b/filme/migrations/0002_alter_filme_categoria_episodio.py deleted file mode 100644 index dbd39b23..00000000 --- a/filme/migrations/0002_alter_filme_categoria_episodio.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-17 17:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('filme', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='filme', - name='categoria', - field=models.CharField(choices=[('ANALISES', 'Análises'), ('PROGRAMACAO', 'Programação'), ('APRESENTACAO', 'Apresentação'), ('OUTROS', 'Outros'), {'ANIMES', 'Animes'}], max_length=15), - ), - migrations.CreateModel( - name='Episodio', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('titulo', models.CharField(max_length=100)), - ('video', models.URLField()), - ('filme', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='episodios', to='filme.filme')), - ], - ), - ] diff --git a/filme/models.py b/filme/models.py index 7324a693..93ecc84a 100644 --- a/filme/models.py +++ b/filme/models.py @@ -1,16 +1,17 @@ from django.db import models from django.utils import timezone +from django.contrib.auth.models import AbstractUser + LISTA_CATEGORIAS = ( ('ANALISES', 'Análises'), ('PROGRAMACAO', 'Programação'), ('APRESENTACAO', 'Apresentação'), ('OUTROS', 'Outros'), - {'ANIMES', 'Animes'} + {'ANIMES', 'Animes'}, + ['FILME_A', 'Filme de Ação'], ) -# Create o film - class Filme(models.Model): titulo = models.CharField(max_length=100) @@ -26,8 +27,6 @@ class Filme(models.Model): def __str__(self): return self.titulo -# Criar os episódios - class Episodio(models.Model): # parametos do ForeignKey, "nome_da_tabela", related_name relaciona os episodios ao filme, on_delete se deletar o filme os episodios também sao deletados @@ -39,4 +38,6 @@ class Episodio(models.Model): def __str__(self): return self.filme.titulo + ' - ' + self.titulo -# Criar o usuário + +class Usuario(AbstractUser): + filmes_vistos = models.ManyToManyField('Filme') diff --git a/filme/novos_context.py b/filme/novos_context.py index 7c3a1350..e1bd594c 100644 --- a/filme/novos_context.py +++ b/filme/novos_context.py @@ -11,3 +11,12 @@ def lista_filmes_recentes(request): def lista_filmes_emalta(request): lista_filmes = Filme.objects.all().order_by('-visualizacoes')[0:10] return {"lista_filmes_emalta": lista_filmes} + + +def filme_destaque(request): + filme = Filme.objects.order_by('-data_criacao') + if filme: + filme = filme[0] + else: + filme = None + return {"filme_destaque": filme} diff --git a/filme/templates/homefilmes.html b/filme/templates/homefilmes.html index 844a9795..339f88de 100644 --- a/filme/templates/homefilmes.html +++ b/filme/templates/homefilmes.html @@ -12,24 +12,15 @@ {% block content %}
-

Esta é a HomeFilmes

- - - -
+
- {{ object.titulo }} + {{ filme_destaque.titulo }}
- {{ object.descricao }} + {{ filme_destaque.descricao }}
- Play + Play
@@ -78,6 +69,28 @@

Esta é a HomeFilmes

+ +
+
Filmes Vistos
+
+ + + +
+
diff --git a/filme/templates/login.html b/filme/templates/login.html new file mode 100644 index 00000000..3920204e --- /dev/null +++ b/filme/templates/login.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} + +{% block title %} + Login Hashflix +{% endblock %} + +{% block head %} +{% load static %} + +{% endblock %} + +{% block content %} +
+

Login

+
+{% endblock %} diff --git a/filme/templates/logout.html b/filme/templates/logout.html new file mode 100644 index 00000000..5f28d069 --- /dev/null +++ b/filme/templates/logout.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} + +{% block title %} + Logout Hashflix +{% endblock %} + +{% block head %} +{% load static %} + +{% endblock %} + +{% block content %} +
+

Logout

+
+{% endblock %} diff --git a/filme/templates/pesquisa.html b/filme/templates/pesquisa.html new file mode 100644 index 00000000..6da2c0c0 --- /dev/null +++ b/filme/templates/pesquisa.html @@ -0,0 +1,26 @@ +{% extends 'base.html' %} + +{% block title %} + Pesquisa de Filmes +{% endblock %} + +{% block head %} +{% load static %} + +{% endblock %} + +{% block content %} +
+

Resultados da busca

+ +
+{% endblock %} diff --git a/filme/urls.py b/filme/urls.py index 3899fb76..fb5c5a29 100644 --- a/filme/urls.py +++ b/filme/urls.py @@ -1,5 +1,6 @@ from django.urls import path, include -from .views import Homepage, HomeFilmes, DetalhesFilme +from .views import Homepage, HomeFilmes, DetalhesFilme, PesquisaFilme +from django.contrib.auth import views as auth_view app_name = 'filme' @@ -9,4 +10,9 @@ path('', Homepage.as_view(), name='homepage'), path('filmes/', HomeFilmes.as_view(), name='homefilmes'), path('filmes/', DetalhesFilme.as_view(), name='detalhesfilme'), + path('pesquisa', PesquisaFilme.as_view(), name='pesquisafilme'), + path('login', auth_view.LoginView.as_view( + template_name='login.html'), name='login'), + path('logout', auth_view.LogoutView.as_view( + template_name='logout.html'), name='logout'), ] diff --git a/filme/views.py b/filme/views.py index ed39274d..a2afddf7 100644 --- a/filme/views.py +++ b/filme/views.py @@ -1,6 +1,7 @@ from django.shortcuts import render from .models import Filme from django.views.generic import TemplateView, ListView, DetailView +from django.contrib.auth.mixins import LoginRequiredMixin # Create your views here. @@ -9,21 +10,21 @@ class Homepage(TemplateView): template_name = 'homepage.html' -class HomeFilmes(ListView): +class HomeFilmes(LoginRequiredMixin, ListView): template_name = "homefilmes.html" model = Filme -class DetalhesFilme(DetailView): +class DetalhesFilme(LoginRequiredMixin, DetailView): template_name = "detalhesfilme.html" model = Filme def get(self, request, *args, **kwargs): - # Descobrir o filme acessado, Somar 1 nas visualizações do filme e atualizar no banco filme = self.get_object() - filme.visualizacoes += 1 # Editando o campo do banco de dados - filme.save() # Salvando a atualização no banco de dados - + filme.visualizacoes += 1 + filme.save() + usuario = request.user # current user + usuario.filmes_vistos.add(filme) # .add para adicionar no banco # Redireciona o usuário para a url final return super().get(request, *args, **kwargs) @@ -36,6 +37,24 @@ def get_context_data(self, **kwargs): context['filmes_relacionados'] = filmes_relacionados return context + +class PesquisaFilme(LoginRequiredMixin, ListView): + template_name = "pesquisa.html" + model = Filme + + # Alterando o object_list + + def get_queryset(self): + # Quando for feita uma pesquisa quero pegar o termo digitado + termo_pesquisa = self.request.GET.get( + 'query') # é o name do input da pesquisa + if termo_pesquisa: + object_list = self.model.objects.filter( + titulo__icontains=termo_pesquisa) + return object_list + else: + return None + # def homepage(request): # return render(request, "homepage.html") diff --git a/hashflix/settings.py b/hashflix/settings.py index 1a83906a..7cafbfdd 100644 --- a/hashflix/settings.py +++ b/hashflix/settings.py @@ -66,6 +66,7 @@ 'django.contrib.messages.context_processors.messages', 'filme.novos_context.lista_filmes_recentes', 'filme.novos_context.lista_filmes_emalta', + 'filme.novos_context.filme_destaque', ], }, }, @@ -87,7 +88,7 @@ # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators - +AUTH_USER_MODEL = 'filme.Usuario' AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', @@ -138,3 +139,8 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Gerenciamento de Users +LOGIN_REDIRECT_URL = 'filme:homefilmes' + +LOGIN_URL = 'filme:login' diff --git a/media/thumb_filmes/EXCEL_IMPRESSIONADOR_1280X725.png b/media/thumb_filmes/EXCEL_IMPRESSIONADOR_1280X725.png deleted file mode 100644 index 252373fd..00000000 Binary files a/media/thumb_filmes/EXCEL_IMPRESSIONADOR_1280X725.png and /dev/null differ diff --git a/media/thumb_filmes/JOGADOR1.jpg b/media/thumb_filmes/JOGADOR1.jpg new file mode 100644 index 00000000..6ea6805f Binary files /dev/null and b/media/thumb_filmes/JOGADOR1.jpg differ diff --git a/media/thumb_filmes/blackclover.jpe b/media/thumb_filmes/blackclover.jpe new file mode 100644 index 00000000..de81c7d3 Binary files /dev/null and b/media/thumb_filmes/blackclover.jpe differ diff --git a/media/thumb_filmes/blackclover_Es5ps7i.jpe b/media/thumb_filmes/blackclover_Es5ps7i.jpe new file mode 100644 index 00000000..de81c7d3 Binary files /dev/null and b/media/thumb_filmes/blackclover_Es5ps7i.jpe differ diff --git a/media/thumb_filmes/deathnote.jpg b/media/thumb_filmes/deathnote.jpg index f54caa18..aae0c27e 100644 Binary files a/media/thumb_filmes/deathnote.jpg and b/media/thumb_filmes/deathnote.jpg differ diff --git a/media/thumb_filmes/deathnote_ydWhu4g.jpg b/media/thumb_filmes/deathnote_ydWhu4g.jpg deleted file mode 100644 index aae0c27e..00000000 Binary files a/media/thumb_filmes/deathnote_ydWhu4g.jpg and /dev/null differ diff --git a/media/thumb_filmes/demonslayer.jpg b/media/thumb_filmes/demonslayer.jpg deleted file mode 100644 index cdc4833c..00000000 Binary files a/media/thumb_filmes/demonslayer.jpg and /dev/null differ diff --git a/media/thumb_filmes/dragonball.png b/media/thumb_filmes/dragonball.png deleted file mode 100644 index 53544aa4..00000000 Binary files a/media/thumb_filmes/dragonball.png and /dev/null differ diff --git a/media/thumb_filmes/fairytail.jpe b/media/thumb_filmes/fairytail.jpe new file mode 100644 index 00000000..3966b407 Binary files /dev/null and b/media/thumb_filmes/fairytail.jpe differ diff --git a/media/thumb_filmes/fairytail.png b/media/thumb_filmes/fairytail.png deleted file mode 100644 index af31034d..00000000 Binary files a/media/thumb_filmes/fairytail.png and /dev/null differ diff --git a/media/thumb_filmes/kimetsu.jpe b/media/thumb_filmes/kimetsu.jpe new file mode 100644 index 00000000..d6d5d30b Binary files /dev/null and b/media/thumb_filmes/kimetsu.jpe differ diff --git a/static/css/style_base.css b/static/css/style_base.css index 3fdf84a1..38afb167 100644 --- a/static/css/style_base.css +++ b/static/css/style_base.css @@ -15,15 +15,15 @@ a { color: #f5f4f1; font-family: "Poppins", sans-serif; } - +/* Style navbar */ .navbar { position: fixed; padding: 1rem; width: 100%; display: flex; justify-content: space-between; - align-items: center; background-color: transparent; + align-items: center; z-index: 50; } @@ -67,3 +67,13 @@ input[type="search"] { padding: 0rem 0.3rem; color: rgb(15, 15, 15); } + + +.nav-submit, +.invisible { + display: none; +} + +.navbar-bg { + background-color: rgb(0, 0, 0, 0.95); +} diff --git a/static/css/style_carousel.css b/static/css/style_carousel.css index d0473a68..dc8eae07 100644 --- a/static/css/style_carousel.css +++ b/static/css/style_carousel.css @@ -15,6 +15,7 @@ gap: 0.5rem; } +.arrow-left3, .arrow-left2, .arrow-left, .arrow-right { @@ -45,12 +46,14 @@ ); } +.arrow-left3:hover, .arrow-left2:hover, .arrow-right:hover, .arrow-left:hover { opacity: 1; } +.item3, .item2, .item { height: 15rem; @@ -60,6 +63,7 @@ transition: all 200ms ease-in-out; } +.current-item3, .current-item2, .current-item { opacity: 1; diff --git a/static/css/style_homefilmes.css b/static/css/style_homefilmes.css index e29eff71..c4ebeedd 100644 --- a/static/css/style_homefilmes.css +++ b/static/css/style_homefilmes.css @@ -1,5 +1,6 @@ .content { - padding-top: 4.68rem; /*Ajuste isso de acordo com a altura da sua barra de navegação */ + display: flex; + flex-direction: column; } /* Inicio sec-filme */ @@ -24,6 +25,7 @@ padding: 1rem 1rem; max-width: 30rem; max-height: 5rem; + background-color: rgba(0, 0, 0, 0.4); overflow: hidden; /* ... */ text-overflow: ellipsis; white-space: nowrap; @@ -34,7 +36,7 @@ margin: 1rem; width: 5rem; text-align: center; - background-color: rgba(189, 16, 16, 0.527); + background-color: rgba(189, 16, 16); color: rgba(255, 255, 255); border-radius: 0.5rem; transition: 1.02; @@ -46,6 +48,7 @@ } /* Outras sections */ +.sec-assistidos, .sec-novo, .sec-emalta { padding: 2rem; diff --git a/static/css/style_login.css b/static/css/style_login.css new file mode 100644 index 00000000..e69de29b diff --git a/static/css/style_logout.css b/static/css/style_logout.css new file mode 100644 index 00000000..e69de29b diff --git a/static/css/style_pesquisa.css b/static/css/style_pesquisa.css new file mode 100644 index 00000000..46b411ec --- /dev/null +++ b/static/css/style_pesquisa.css @@ -0,0 +1,22 @@ +.content{ + padding-top: 4.68rem; + min-height: 100vh; +} + +.search{ + display: flex; + flex-flow: row wrap; + gap: 1rem; +} + +.search-item{ + display: flex; + flex-direction: column; +} + +.item { + height: 15rem; + width: auto; + flex-shrink: 0; /* Para não diminuir para caber*/ + opacity: 0.6; +} diff --git a/static/js/carousel.js b/static/js/carousel.js index c2ad54e7..535d5621 100644 --- a/static/js/carousel.js +++ b/static/js/carousel.js @@ -63,3 +63,36 @@ controls2.forEach((control2) => { items2[currentItem2].classList.add("current-item2"); }); }); + +const controls3 = document.querySelectorAll(".control3"); +let currentItem3 = 0; +const items3 = document.querySelectorAll(".item3"); +const maxItems3 = items3.length; + +controls3.forEach((control3) => { + control3.addEventListener("click", () => { + const isLeft3 = control3.classList.contains("arrow-left3"); + + if (isLeft3) { + currentItem3 -= 1; + } else { + currentItem3 += 1; + } + + if (currentItem3 >= maxItems3) { + currentItem3 = 0; + } + // currentItem não pode ser < 0 + if (currentItem3 < 0) { + currentItem3 = maxItems3 - 1; + } + + items3.forEach((item3) => item3.classList.remove("current-item3")); + items3[currentItem3].scrollIntoView({ + inline: "center", + behavior: "smooth", + }); + + items3[currentItem3].classList.add("current-item3"); + }); +}); diff --git a/static/js/navbar.js b/static/js/navbar.js new file mode 100644 index 00000000..0ea24e15 --- /dev/null +++ b/static/js/navbar.js @@ -0,0 +1,15 @@ +const nav = document.querySelector("nav"); //PEga a tag nav + +document.addEventListener("scroll", (e) => { + if (scrollY > 100) { + if (scrollY > window.innerHeight) { + nav.classList.add("invisible"); + } else { + nav.classList.add("navbar-bg"); + nav.classList.remove("invisible"); + } + } else { + nav.classList.remove("navbar-bg"); + nav.classList.remove("invisible"); + } +}); diff --git a/templates/base.html b/templates/base.html index c56500c1..02918067 100644 --- a/templates/base.html +++ b/templates/base.html @@ -25,5 +25,7 @@ {% block content %} {% endblock %} {% include 'footer.html' %} + + diff --git a/templates/navbar.html b/templates/navbar.html index 6f160e77..d273a350 100644 --- a/templates/navbar.html +++ b/templates/navbar.html @@ -6,9 +6,10 @@
- +