base site for phantas.ma
This commit is contained in:
parent
1e082c6786
commit
d27f4ac0c0
32 changed files with 50 additions and 710 deletions
|
@ -5,6 +5,5 @@ python3 -m venv venv
|
|||
./venv/bin/pip install -r requirements.txt
|
||||
./manage.py migrate
|
||||
./manage.py update_geoip
|
||||
./manage.py load_titles
|
||||
./mange.py runserver
|
||||
```
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.conf import settings
|
||||
from django.core.mail import EmailMessage
|
||||
from django.shortcuts import render
|
||||
from app.video.utils import render_to_json_response
|
||||
from ..utils import render_to_json_response
|
||||
from . import forms
|
||||
from . import models
|
||||
|
||||
|
@ -11,7 +11,7 @@ def index(request):
|
|||
form = forms.ContactForm(request.POST)
|
||||
if form.is_valid():
|
||||
message = 'From: %s\n\n%s' % (form.cleaned_data['email'], form.cleaned_data['message'])
|
||||
subject = 'njp.ma contact message'
|
||||
subject = 'phantas.ma contact message'
|
||||
from_ = settings.CONTACT_FROM_EMAIL
|
||||
to = [settings.CONTACT_TO_EMAIL]
|
||||
msg = EmailMessage(subject, message, from_, to, reply_to=[form.cleaned_data['email']])
|
||||
|
|
|
@ -39,7 +39,6 @@ INSTALLED_APPS = [
|
|||
|
||||
'app',
|
||||
'app.user',
|
||||
'app.video',
|
||||
'app.text',
|
||||
'app.contact',
|
||||
|
||||
|
@ -145,13 +144,11 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|||
|
||||
GEOIP_PATH = BASE_DIR / 'geo'
|
||||
|
||||
DEFAULT_PANDORA_API = "https://archive.njp.ma/api/"
|
||||
DEFAULT_PANDORA_API = "https://pad.ma/api/"
|
||||
TIMELINE_QUERY = {'conditions': []}
|
||||
|
||||
URL_PREFIX = ''
|
||||
|
||||
CONTACT_TO_EMAIL = 'contact@njp.ma'
|
||||
CONTACT_FROM_EMAIL = 'contact@njp.ma'
|
||||
CONTACT_TO_EMAIL = 'contact@phantas.ma'
|
||||
CONTACT_FROM_EMAIL = 'contact@phantas.ma'
|
||||
|
||||
try:
|
||||
from .local_settings import *
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}About{% endblock title %}
|
||||
{% block body_class%}body--about animated animated-text{% endblock %}
|
||||
{% block main %}
|
||||
<div class="about">
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block end %}
|
||||
<script>
|
||||
var pandoraURL = "{{ pandora_url }}";
|
||||
</script>
|
||||
<script src="{% static 'js/api.js' %}"></script>
|
||||
<script src="{% static 'js/about.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>
|
||||
{% block title %}
|
||||
njp.ma
|
||||
phantas.ma
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<link href="{% sass_src 'css/style.scss' %}" rel="stylesheet" type="text/css">
|
||||
|
@ -15,14 +15,16 @@
|
|||
<div class="topnav">
|
||||
<a href="/" class="title font-family-wrong">
|
||||
<div class="title-text text">
|
||||
njp.ma
|
||||
phantas.ma{{text.get_absolute_url}}
|
||||
</div>
|
||||
</a>
|
||||
<div class="item-title"></div>
|
||||
<nav>
|
||||
{% comment %}
|
||||
<a href="{% url 'films' %}" class="animated-title"><span class="text">videos 비디오</span></a>
|
||||
<a href="{% url 'texts' %}" class="animated-title"><span class="text">cuts 장면들</span></a>
|
||||
<a href="{% url 'about' %}" class="animated-title"><span class="text">about 소개</span></a>
|
||||
{% endcomment %}
|
||||
</nav>
|
||||
</div>
|
||||
<main>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block main %}
|
||||
some overview page with stuff
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block main %}
|
||||
<h1>{{ edit.data.title }}</h1>
|
||||
{% if edit.vimeo_id %}
|
||||
<iframe src="https://player.vimeo.com/video/{{ edit.vimeo_id }}" width="640" height="564" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,4 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block main %}
|
||||
some overview page with stuff
|
||||
{% endblock %}
|
|
@ -67,12 +67,12 @@
|
|||
|
||||
|
||||
</style>
|
||||
<title>njp.ma</title>
|
||||
<title>phantas.ma</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="box">
|
||||
<div id="shadowBox">
|
||||
<h1 class="rainbow rainbow_text_animated">njp.ma</h1>
|
||||
<h1 class="rainbow rainbow_text_animated">phantas.ma</h1>
|
||||
<h2></h2>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,7 +81,7 @@
|
|||
document.location.href = '/'
|
||||
path = document.location.pathname.slice(1)
|
||||
if (path) {
|
||||
document.querySelector('h1').innerHTML = 'njp.ma/' + path
|
||||
document.querySelector('h1').innerHTML = 'phantas.ma/' + path
|
||||
if (path == 'polis') {
|
||||
setTimeout(() => {
|
||||
document.querySelector('h2').innerHTML = 'Oct 30, 2021 – Mar 6, 2022'
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block head %}
|
||||
<meta name="title" content="{{ film.data.title }}"/>
|
||||
<meta name="description" content="{% for director in film.data.director %}{{ director|safe }}{% endfor %}"/>
|
||||
|
||||
<meta property="og:title" content="{{ film.data.title }} {{ film.data.tile.zh }}"/>
|
||||
<meta property="og:type" content="movie"/>
|
||||
<meta property="og:url" content="https://njp.ma{{ film.get_absolute_url }}"/>
|
||||
<meta property="og:site_name" content="njp.ma"/>
|
||||
<meta property="og:description" content="{% for director in film.data.director %}{{ director|safe }}{% endfor %}"/>
|
||||
|
||||
<meta property="twitter:card" content="summary_large_image"/>
|
||||
<meta property="twitter:title" content="{{ film.data.title }} {{ film.data.tile.zh }}"/>
|
||||
<meta property="twitter:description" content="{% for director in film.data.director %}{{ director|safe }}{% endfor %}"/>
|
||||
|
||||
{% endblock head %}
|
||||
{% block body_class%}animated{% endblock %}
|
||||
{% block main %}
|
||||
{% comment %}
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-1: {{ film.color_1 }};
|
||||
--bg-color-2: {{ film.color_2 }};
|
||||
--bg-color-3: {{ film.color_2 }};
|
||||
--bg-color-4: {{ film.color_4 }};
|
||||
}
|
||||
|
||||
body {
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
{% endcomment %}
|
||||
<div class="film">
|
||||
<div class="info">
|
||||
<div class="info-meta">
|
||||
<h1>
|
||||
<span class="font-bold">{{ film.data.title | safe }}</span>
|
||||
</h1>
|
||||
{% if film.data.date %}
|
||||
<div class="date">
|
||||
{{ film.data.date | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="country">
|
||||
{{ film.data.country|default:''|join:', ' }}
|
||||
</div>
|
||||
<div class="type">
|
||||
{{ film.data.type|default:''|join:', ' }}
|
||||
</div>
|
||||
<div class="details">
|
||||
{{ film.data.description|default:''|safe }}
|
||||
</div>
|
||||
<div class="featuring">
|
||||
Featuring:
|
||||
{{ film.data.featuring|default:''|join:', ' }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="video-block">
|
||||
<img src="{{ stream_prefix }}/{{ film.data.items.0.id }}/timelineslitscan64pframe.jpg" class="video-fallback-block">
|
||||
<video
|
||||
id="timeline-video"
|
||||
src="{{ stream_prefix }}/{{ film.data.items.0.id }}/timelineslitscan64p.mp4"
|
||||
poster="{{ stream_prefix }}/{{ film.data.items.0.id }}/timelineslitscan64pframe.jpg"
|
||||
controlsList="nodownload"
|
||||
autoplay
|
||||
loop
|
||||
muted
|
||||
playsinline>
|
||||
</video>
|
||||
</div>
|
||||
<div class="items">
|
||||
{% for item in film.data.items %}
|
||||
<div class="item">
|
||||
<a href="play/{{ item.id }}">
|
||||
<figure>
|
||||
<div class="overlay">
|
||||
<img src="{% static 'svg/play.svg' %}">
|
||||
</div>
|
||||
<img src="{{ stream_prefix }}/{{ item.id }}/480p.jpg">
|
||||
<figcaption>
|
||||
{{ item.title | safe }}
|
||||
{% if item.date %}({{item.date | safe }}){% endif %}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="play">
|
||||
<div class="film-play-pandora"><a href="{{ film.pandora_url }}" target="_blank">Open in Archive</a></div>
|
||||
</div>
|
||||
{% if film.related_texts.exists %}
|
||||
<div class="texts">
|
||||
<h2>Related Assemblies</h2>
|
||||
{% for text in film.related_texts %}
|
||||
<div class="text">
|
||||
<a href="{{ text.get_absolute_url }}">
|
||||
{{ text.title | safe }}<br>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block end %}
|
||||
<script>
|
||||
var film = {{ film.json | safe }};
|
||||
film.prefix = "{{ stream_prefix }}";
|
||||
var pandoraURL = "{{ pandora_url }}";
|
||||
</script>
|
||||
{% comment %}
|
||||
<script src="{% static 'js/film.js' %}"></script>
|
||||
{% endcomment %}
|
||||
{% endblock %}
|
|
@ -1,13 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block main %}
|
||||
<div id="ascroll"></div>
|
||||
{% endblock %}
|
||||
{% block end %}
|
||||
<script>
|
||||
var config = {{ config | safe }};
|
||||
var pandoraURL = "{{ pandora_url }}";
|
||||
var streamPrefix = "{{ stream_prefix }}";
|
||||
</script>
|
||||
<script src="{% static 'js/api.js' %}"></script>
|
||||
<script src="{% static 'js/play.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -1,54 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block body_class%}animated animated-text{% endblock %}
|
||||
{% block main %}
|
||||
|
||||
<div class="films">
|
||||
<div class="actions">
|
||||
<strong>Filter:</strong>
|
||||
<select id="filter-select">
|
||||
<option value="" selected>All Types</option>
|
||||
{% for type in types %}
|
||||
<option value="{{ type.value }}">{{ type.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<strong>Sort:</strong>
|
||||
<select id="sort-select">
|
||||
<option value="title">Title</option>
|
||||
<option value="year">Year</option>
|
||||
<option value="duration">Duration</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="films-list">
|
||||
{% for film in films %}
|
||||
<div class="film"
|
||||
data-position="{{ film.position }}"
|
||||
data-title="{{ film.data.title }}"
|
||||
data-year="{{ film.data.year|default:"9999" }}"
|
||||
data-duration="{{ film.duration_seconds }}"
|
||||
data-type="{{ film.data.type|join:"," }}"
|
||||
>
|
||||
<a href="{{ film.get_absolute_url }}">
|
||||
<figure>
|
||||
<div>
|
||||
{% for item in film.data.items %}
|
||||
<img src="{{ stream_prefix }}/{{ item.id }}/480p.jpg">
|
||||
{% endfor %}
|
||||
</div>
|
||||
<figcaption>
|
||||
<img src="{{ stream_prefix }}/{{ film.data.items.0.id }}/timeline64p.jpg">
|
||||
</figcaption>
|
||||
<figcaption>
|
||||
{{ film.data.title | safe }}
|
||||
{% if film.data.date %}({{ film.data.date|safe}}){% endif %}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block end %}
|
||||
<script src="{% static 'js/films.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -1,17 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
{% block head %}
|
||||
<meta name="title" content="njp.ma"/>
|
||||
<meta name="description" content="This archive was initiated in the context of CAMP After Media Promises"/>
|
||||
<meta property="og:title" content="njp.ma"/>
|
||||
<meta name="title" content="phantas.ma"/>
|
||||
<meta name="description" content="phantas.ma"/>
|
||||
<meta property="og:title" content="phantas.ma"/>
|
||||
|
||||
<meta property="og:url" content="https://njp.ma"/>
|
||||
<meta property="og:url" content="https://phantas.ma"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:title" content="njp.ma" />
|
||||
<meta property="og:description" content="This archive was initiated in the context of CAMP After Media Promises"/>
|
||||
|
||||
<meta property="og:title" content="phantas.ma" />
|
||||
<meta property="og:description" content="phantas.ma"/>
|
||||
<meta property="twitter:card" content="summary_large_image"/>
|
||||
<meta property="twitter:title" content="njp.ma" />
|
||||
<meta property="twitter:description" content="This archive was initiated in the context of CAMP After Media Promises"/>
|
||||
<meta property="twitter:title" content="phantas.ma" />
|
||||
<meta property="twitter:description" content="phantas.ma"/>
|
||||
{% endblock head %}
|
||||
{% block body_class%}animated animated-text body--home{% endblock %}
|
||||
{% block main %}
|
||||
|
@ -33,11 +32,11 @@
|
|||
{% endif %}
|
||||
<div class="home">
|
||||
<div class="box">
|
||||
<h1 class="h1-en-home">njp.ma</h1>
|
||||
<h1 class="h1-en-home">Phantas.ma</h1>
|
||||
<div class="font-size-sm">
|
||||
<div class="h3"><a href="{% url 'films'%}" class="font-bold">videos 비디오</a></div>
|
||||
<div class="h3"><a href="{% url 'texts' %}" class="font-bold">cuts 장면들</a></div>
|
||||
<div class="h3"><a href="{% url 'about' %}" class="font-bold">about 소개</a></div>
|
||||
{% for text in texts %}
|
||||
<div class="h3"><a href="{{ text.get_absolute_url }}" class="font-bold">{{ text.title }}</a></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
<meta name="description" content="{{ text.byline|striptags }}"/>
|
||||
<meta property="og:title" content="{{ text.title }}"/>
|
||||
<meta property="og:type" content="movie"/>
|
||||
<meta property="og:url" content="https://njp.ma{{ text.get_absolute_url }}"/>
|
||||
<meta property="og:site_name" content="njp.ma"/>
|
||||
<meta property="og:url" content="https://phantas.ma{{ text.get_absolute_url }}"/>
|
||||
<meta property="og:site_name" content="phantas.ma"/>
|
||||
<meta property="og:description" content="{{ text.byline|striptags }}"/>
|
||||
|
||||
|
||||
<meta name="title" content="{{ text.title }}"/>
|
||||
<meta name="description" content="{{ text.byline|striptags }}"/>
|
||||
|
||||
<meta property="og:site_name" content="njp.ma"/>
|
||||
<meta property="og:site_name" content="phantas.ma"/>
|
||||
<meta property="og:description" content="{% for director in text.data.director %}{{ director|safe }}{% endfor %}"/>
|
||||
|
||||
<meta property="twitter:card" content="summary_large_image"/>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<meta property="twitter:description" content="{{ text.byline|striptags }}"/>
|
||||
|
||||
{% endblock head %}
|
||||
{% block title %}{{ text.title }} - njp.ma{% endblock title %}
|
||||
{% block title %}{{ text.title }} - phantas.ma{% endblock title %}
|
||||
{% block main %}
|
||||
<div id="ascroll"></div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}TV{% endblock%}
|
||||
{% block main %}
|
||||
here be TV stuff
|
||||
{% endblock %}
|
|
@ -41,7 +41,7 @@ class Text(models.Model):
|
|||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
language = models.CharField(choices=LANGUAGE_CHOICES, max_length=8, default='en')
|
||||
slug = models.SlugField()
|
||||
slug = models.CharField(max_length=1024)
|
||||
public = models.BooleanField(default=False)
|
||||
listed = models.BooleanField("List this item on overview page", default=True)
|
||||
position = models.IntegerField(default=0)
|
||||
|
@ -50,14 +50,14 @@ class Text(models.Model):
|
|||
byline = models.TextField(default="", blank=True)
|
||||
body = models.TextField(default="", blank=True)
|
||||
|
||||
data = models.JSONField(default=dict)
|
||||
data = models.JSONField(default=dict, blank=True)
|
||||
annotations = models.JSONField(default=dict, blank=True, editable=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return '/' + settings.URL_PREFIX + 'cuts/' + self.slug
|
||||
return '/' + self.slug
|
||||
|
||||
def get_annotations(self):
|
||||
api = ox.api.signin(settings.DEFAULT_PANDORA_API)
|
||||
|
@ -165,21 +165,11 @@ class Text(models.Model):
|
|||
super().save(*args, **kwargs)
|
||||
|
||||
def json(self):
|
||||
from ..video.models import Film
|
||||
data = {}
|
||||
data['title'] = self.title
|
||||
data['byline'] = self.byline
|
||||
data['body'] = self.body
|
||||
data['language'] = self.language
|
||||
item_id = self.data.get('related')
|
||||
if not item_id:
|
||||
item_id = self.data.get('item')
|
||||
if item_id:
|
||||
item = Film.objects.filter(pandora_url=item_id).first()
|
||||
if item:
|
||||
for key in ('title', 'title_zh', 'director'):
|
||||
data['item_' + key] = item.data[key]
|
||||
data['item_url'] = item.get_absolute_url()
|
||||
if isinstance(self.annotations, list) and len(self.annotations) > 0:
|
||||
data['annotations'] = self.annotations
|
||||
data.update(self.data)
|
||||
|
|
|
@ -3,7 +3,6 @@ from django.shortcuts import render, redirect, get_object_or_404
|
|||
from django.conf import settings
|
||||
|
||||
from . import models
|
||||
from ..video.views import get_stream_prefix
|
||||
|
||||
def fallback(request, slug=''):
|
||||
if not slug:
|
||||
|
@ -13,26 +12,17 @@ def fallback(request, slug=''):
|
|||
return render(request, 'fallback.html', context)
|
||||
|
||||
def index(request):
|
||||
from ..video.models import Film
|
||||
from ..text.models import Text
|
||||
context = {}
|
||||
context['films'] = Film.objects.filter(public=True).count()
|
||||
context['texts'] = Text.objects.filter(public=True, listed=True).count()
|
||||
context['stream_prefix'] = get_stream_prefix(request)
|
||||
featured = Film.objects.filter(featured=True)
|
||||
featured_count = featured.count()
|
||||
if featured_count:
|
||||
featured = featured[random.randint(0, featured_count - 1)]
|
||||
try:
|
||||
featured = featured.data['items'][0]['id']
|
||||
except:
|
||||
featured = None
|
||||
context['featured'] = featured
|
||||
context['texts'] = Text.objects.filter(public=True, listed=True).order_by('title')
|
||||
return render(request, 'index.html', context)
|
||||
|
||||
def page(request, slug=''):
|
||||
print('page!!', slug)
|
||||
context = {}
|
||||
if request.user.is_staff:
|
||||
if slug == "":
|
||||
return index(request)
|
||||
elif request.user.is_staff:
|
||||
page = models.Page.objects.filter(slug=slug).first()
|
||||
else:
|
||||
page = models.Page.objects.filter(slug=slug, public=True).first()
|
||||
|
@ -40,7 +30,7 @@ def page(request, slug=''):
|
|||
context['page'] = page
|
||||
return render(request, 'page.html', context)
|
||||
else:
|
||||
return render(request, 'fallback.html', context)
|
||||
return text(request, slug)
|
||||
|
||||
def about(request):
|
||||
context = {}
|
||||
|
@ -54,11 +44,11 @@ def texts(request):
|
|||
return render(request, 'texts.html', context)
|
||||
|
||||
def text(request, slug):
|
||||
print('find text', slug)
|
||||
context = {}
|
||||
if request.user.is_staff:
|
||||
context['text'] = get_object_or_404(models.Text, slug=slug)
|
||||
else:
|
||||
context['text'] = get_object_or_404(models.Text, slug=slug, public=True)
|
||||
context['stream_prefix'] = get_stream_prefix(request)
|
||||
context['pandora_url'] = settings.DEFAULT_PANDORA_API.replace('/api/', '')
|
||||
return render(request, 'text.html', context)
|
||||
|
|
22
app/urls.py
22
app/urls.py
|
@ -17,27 +17,13 @@ from django.contrib import admin
|
|||
from django.urls import path
|
||||
from django.conf import settings
|
||||
|
||||
from .video import views as video
|
||||
from .text import views as text
|
||||
from .contact.views import index as contact
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
#path('pandoraAPI/', video.pandoraAPI, name='pandoraAPI'),
|
||||
path(settings.URL_PREFIX + 'videos/', video.films, name='films'),
|
||||
path(settings.URL_PREFIX + 'video/<str:slug>/play/<str:id>', video.film_play, name='film_play'),
|
||||
path(settings.URL_PREFIX + 'video/<str:slug>/', video.film, name='film'),
|
||||
path(settings.URL_PREFIX + 'edits/', video.edits, name='edits'),
|
||||
path(settings.URL_PREFIX + 'edit/<str:slug>/play/<str:lang>', video.edit_play, name='edit_play'),
|
||||
path(settings.URL_PREFIX + 'edit/<str:slug>/', video.edit, name='edit'),
|
||||
path(settings.URL_PREFIX + 'tv/', video.tv, name='tv'),
|
||||
path(settings.URL_PREFIX + 'cuts/', text.texts, name='texts'),
|
||||
path(settings.URL_PREFIX + 'cuts/<str:slug>', text.text, name='text'),
|
||||
#path(settings.URL_PREFIX + 'index-alt/', text.index_alt, name='index_alt'),
|
||||
path(settings.URL_PREFIX + 'about/', text.about, name='about'),
|
||||
path(settings.URL_PREFIX + 'contact/', contact, name='contact'),
|
||||
path(settings.URL_PREFIX + '<str:slug>/', text.page, name='page'),
|
||||
path(settings.URL_PREFIX[:-1], text.index, name='index'),
|
||||
path('', text.fallback, name='fallback'),
|
||||
path('<str:slug>', text.fallback, name='fallback'),
|
||||
path('about/', text.about, name='about'),
|
||||
path('contact/', contact, name='contact'),
|
||||
path('<str:slug>', text.page, name='page'),
|
||||
path('', text.index, name='index'),
|
||||
]
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import logging
|
||||
import json
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db.models import JSONField
|
||||
|
||||
from . import models
|
||||
from ..widgets import PrettyJSONWidget
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@admin.decorators.register(models.Film)
|
||||
class FilmAdmin(admin.ModelAdmin):
|
||||
formfield_overrides = {
|
||||
JSONField: {'widget': PrettyJSONWidget}
|
||||
}
|
||||
list_display = (
|
||||
'__str__',
|
||||
'position',
|
||||
'slug',
|
||||
'public',
|
||||
)
|
||||
list_editable = ['public', 'position']
|
||||
list_filter = ['public', 'featured']
|
||||
|
||||
#@admin.decorators.register(models.Edit)
|
||||
#class EditAdmin(admin.ModelAdmin):
|
||||
# pass
|
|
@ -1,6 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class VideoConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'app.video'
|
|
@ -1,80 +0,0 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
import ox
|
||||
from ... import models
|
||||
|
||||
|
||||
def escape(key):
|
||||
return key.replace('%', '%25').replace('&', '%26').replace('_', '%09').replace(' ', '_').replace('<', '%0E').replace('>', '%0F')
|
||||
|
||||
def escape_slug(key):
|
||||
key = ox.decode_html(key).replace('%', '').replace('&', '-').replace('_', '-').replace(' ', '-').replace('<', '').replace('>', '').lower()
|
||||
key = key.replace("'", '').replace('"', '').strip()
|
||||
return key
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'import titles from pan.do/ra'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--api", dest="api", type=str, default=settings.DEFAULT_PANDORA_API),
|
||||
parser.add_argument("--group", dest="group", type=str, default='Asian Art Biennial 2021'),
|
||||
|
||||
def handle(self, *args, **options):
|
||||
api = ox.api.signin(options['api'])
|
||||
|
||||
keys = [
|
||||
'id', 'title', 'director', 'summary', 'source', 'sourcedescription', 'date', 'location',
|
||||
'country', 'type', 'year',
|
||||
'duration', 'featuring', 'cinematographer',
|
||||
'hue', 'saturation', 'lightness',
|
||||
'folder', 'folderdescription', 'rightslevel'
|
||||
]
|
||||
|
||||
query = {
|
||||
'query': {
|
||||
},
|
||||
'keys': ['id'],
|
||||
'sort': [{'key': 'duration', 'operator': '-'}],
|
||||
'range': [0, 1000]
|
||||
}
|
||||
folders = {}
|
||||
for item in api.find(**query)['data']['items']:
|
||||
item = api.get(id=item['id'], keys=keys)['data']
|
||||
if item['rightslevel'] > 0:
|
||||
continue
|
||||
if isinstance(item['folder'], list):
|
||||
print(item['id'])
|
||||
|
||||
if item['folder'] not in folders:
|
||||
description = item['folderdescription'] or item.get('summary', '')
|
||||
folders[item['folder']] = {
|
||||
'title': item['folder'],
|
||||
'date': item.get('date', ''),
|
||||
'year': item.get('year', ''),
|
||||
'country': item.get('country', []),
|
||||
'featuring': item.get('featuring', []),
|
||||
'type': item['type'],
|
||||
'description': description,
|
||||
'url': api.url.replace('/api/', '/grid/folder==' + escape(ox.decode_html(item['folder']))),
|
||||
'items': [],
|
||||
}
|
||||
|
||||
del item['folderdescription']
|
||||
if 'summary' in item and item['summary'] == folders[item['folder']]['description']:
|
||||
item['summary'] = ''
|
||||
folders[item['folder']]['items'].append(item)
|
||||
|
||||
slugs = []
|
||||
for item in folders.values():
|
||||
slug = escape_slug(item['title'].split(' / ')[0])
|
||||
f, c = models.Film.objects.get_or_create(slug=slug)
|
||||
f.pandora_url = item['url']
|
||||
for key, value in item.items():
|
||||
if key != 'url':
|
||||
f.data[{
|
||||
}.get(key, key)] = value
|
||||
if c:
|
||||
f.public = True
|
||||
f.save()
|
||||
slugs.append(slug)
|
||||
models.Film.objects.exclude(slug__in=slugs).delete()
|
|
@ -1,29 +0,0 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
import ox
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
"""
|
||||
help = 'update geoip database'
|
||||
args = ''
|
||||
|
||||
def handle(self, **options):
|
||||
force = False
|
||||
path = settings.GEOIP_PATH / 'GeoLite2-City.mmdb'
|
||||
index = ox.net.read_url('https://db-ip.com/db/download/ip-to-city-lite').decode()
|
||||
match = re.compile('href=[\'"](http.*.mmdb.gz)').findall(index)
|
||||
if match:
|
||||
url = match[0]
|
||||
print('download', url)
|
||||
ox.net.save_url(url, "%s.gz" % path)
|
||||
if os.path.exists(path):
|
||||
os.unlink(path)
|
||||
os.system('gunzip "%s.gz"' % path)
|
||||
else:
|
||||
print('failed to download GeoLite2-City.mmdb')
|
|
@ -1,27 +0,0 @@
|
|||
# Generated by Django 3.2.9 on 2021-11-12 09:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Film',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('modified', models.DateTimeField(auto_now=True)),
|
||||
('slug', models.SlugField()),
|
||||
('public', models.BooleanField(default=False)),
|
||||
('position', models.IntegerField(default=0)),
|
||||
('pandora_url', models.CharField(max_length=1024)),
|
||||
('data', models.JSONField(blank=True, default=dict)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.9 on 2021-11-22 12:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('video', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='film',
|
||||
name='featured',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -1,83 +0,0 @@
|
|||
import logging
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
|
||||
import ox
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class Film(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
slug = models.SlugField()
|
||||
public = models.BooleanField(default=False)
|
||||
featured = models.BooleanField(default=False)
|
||||
position = models.IntegerField(default=0)
|
||||
|
||||
pandora_url = models.CharField(max_length=1024)
|
||||
|
||||
data = models.JSONField(default=dict, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.data.get('title', self.slug)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return '/' + settings.URL_PREFIX + 'video/' + self.slug
|
||||
|
||||
def related_texts(self):
|
||||
from ..text.models import Text
|
||||
folder = self.data.get('title')
|
||||
if folder:
|
||||
return Text.objects.filter(Q(data__folder=folder) | Q(data__related=folder))
|
||||
|
||||
def duration_seconds(self):
|
||||
duration = 0
|
||||
for item in self.data.get('items', []):
|
||||
duration += item['duration']
|
||||
return duration
|
||||
|
||||
def duration(self):
|
||||
return ox.format_duration(self.data['duration'] * 1000, verbosity=1, milliseconds=False)
|
||||
|
||||
def color_1(self):
|
||||
hue = self.data['hue']
|
||||
saturation = self.data['saturation'] * 100
|
||||
saturation = 70
|
||||
lightness = self.data['lightness'] * 100
|
||||
return "hsl({}, {}%, {}%)".format(hue, saturation, lightness)
|
||||
|
||||
def color_2(self):
|
||||
hue = self.data['hue']
|
||||
saturation = self.data['saturation'] * 100
|
||||
saturation = 70
|
||||
lightness = self.data['lightness'] * 50
|
||||
return "hsl({}, {}%, {}%)".format(hue, saturation, lightness)
|
||||
|
||||
def color_3(self):
|
||||
hue = self.data['hue']
|
||||
saturation = self.data['saturation'] * 100
|
||||
saturation = 70
|
||||
lightness = self.data['lightness'] * 110
|
||||
return "hsl({}, {}%, {}%)".format(hue, saturation, lightness)
|
||||
|
||||
def color_4(self):
|
||||
hue = self.data['hue']
|
||||
saturation = self.data['saturation'] * 100
|
||||
saturation = 70
|
||||
lightness = self.data['lightness'] * 80
|
||||
return "hsl({}, {}%, {}%)".format(hue, saturation, lightness)
|
||||
|
||||
def json(self):
|
||||
data = {}
|
||||
data['url'] = self.pandora_url
|
||||
#data.update(self.data)
|
||||
return json.dumps(data)
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -1,128 +0,0 @@
|
|||
import logging
|
||||
import json
|
||||
from collections import Counter
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.conf import settings
|
||||
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
|
||||
import ox.geo
|
||||
|
||||
from . import models
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
stream_prefix_cache = {}
|
||||
|
||||
def get_ip(request):
|
||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||
ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
||||
else:
|
||||
ip = request.META['REMOTE_ADDR']
|
||||
if ip.startswith('::ffff:'):
|
||||
ip = ip[len('::ffff:'):]
|
||||
return ip
|
||||
|
||||
def get_stream_prefix(request):
|
||||
#domain = settings.DEFAULT_PANDORA_API.split('/')[2]
|
||||
domain = 'njp.ma'
|
||||
prefix = "https://media.v1.%s" % domain
|
||||
cdn = {
|
||||
'Eastern Asia': "https://media.v2.%s" % domain,
|
||||
'Southern Asia': "https://media.v2.%s" % domain,
|
||||
'Asia': "https://media.v2.%s" % domain,
|
||||
}
|
||||
ip = get_ip(request)
|
||||
if ip in stream_prefix_cache:
|
||||
return stream_prefix_cache[ip]
|
||||
try:
|
||||
g = GeoIP2()
|
||||
country = g.country(ip)
|
||||
if country:
|
||||
country = ox.get_country_name(country['country_code'])
|
||||
info = ox.geo.get_country(country)
|
||||
for key in ('name', 'region', 'continent'):
|
||||
location = info.get(key)
|
||||
#print(location)
|
||||
if location in cdn:
|
||||
stream_prefix_cache[ip] = cdn[location]
|
||||
return cdn[location]
|
||||
except:
|
||||
logger.error('using default prefix, no geoip data found, run ./mange.py update_geoio', exc_info=True)
|
||||
stream_prefix_cache[ip] = prefix
|
||||
return prefix
|
||||
stream_prefix_cache[ip] = prefix
|
||||
return prefix
|
||||
|
||||
def films(request):
|
||||
context = {}
|
||||
context['films'] = models.Film.objects.filter(public=True).order_by('position', 'data__title')
|
||||
types = []
|
||||
for f in context['films']:
|
||||
types += [t.capitalize() for t in f.data['type']]
|
||||
types = Counter(types)
|
||||
context['types'] = []
|
||||
for t in sorted(types):
|
||||
context['types'].append({
|
||||
'title': '%s (%s)' % (t, types[t]), 'value': t
|
||||
})
|
||||
|
||||
context['stream_prefix'] = get_stream_prefix(request)
|
||||
context['settings'] = settings
|
||||
return render(request, 'films.html', context)
|
||||
|
||||
def film(request, slug):
|
||||
context = {}
|
||||
context['film'] = get_object_or_404(models.Film, slug=slug)
|
||||
context['settings'] = settings
|
||||
context['stream_prefix'] = get_stream_prefix(request)
|
||||
context['pandora_url'] = settings.DEFAULT_PANDORA_API.replace('/api/', '')
|
||||
return render(request, 'film.html', context)
|
||||
|
||||
def film_play(request, slug, id):
|
||||
context = {}
|
||||
context['settings'] = settings
|
||||
context['config'] = json.dumps({
|
||||
'layer': 'transcripts',
|
||||
'item': id
|
||||
})
|
||||
context['stream_prefix'] = get_stream_prefix(request)
|
||||
context['pandora_url'] = settings.DEFAULT_PANDORA_API.replace('/api/', '')
|
||||
return render(request, 'film_play.html', context)
|
||||
|
||||
def edits(request):
|
||||
context = {}
|
||||
context['edits'] = models.Edit.objects.filter(public=True).order_by('created')
|
||||
return render(request, 'edits.html', context)
|
||||
|
||||
def edit(request, slug):
|
||||
context = {}
|
||||
context['edit'] = get_object_or_404(models.Edit, slug=slug)
|
||||
context['settings'] = settings
|
||||
return render(request, 'edit.html', context)
|
||||
|
||||
def edit_play(request, slug, lang):
|
||||
context = {}
|
||||
context['edit'] = get_object_or_404(models.Edit, slug=slug)
|
||||
context['lang'] = lang
|
||||
context['settings'] = settings
|
||||
return render(request, 'edit_play.html', context)
|
||||
|
||||
def tv(request):
|
||||
context = {}
|
||||
context['settings'] = settings
|
||||
return render(request, 'tv.html', context)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def pandoraAPI(request):
|
||||
import ox
|
||||
from .utils import render_to_json_response
|
||||
import json
|
||||
data = json.loads(request.body.decode())
|
||||
print('pandora request', data)
|
||||
api = ox.api.signin('https://pad.ma/api/')
|
||||
data = getattr(api, data['action'])(**data['data'])
|
||||
print('response', data)
|
||||
return render_to_json_response(data)
|
||||
|
|
@ -9,19 +9,19 @@ server {
|
|||
root /var/www/html;
|
||||
autoindex off;
|
||||
}
|
||||
rewrite ^/(.*) https://njp.ma/$1 permanent;
|
||||
rewrite ^/(.*) https://phantas.ma/$1 permanent;
|
||||
}
|
||||
|
||||
server {
|
||||
server_tokens off;
|
||||
|
||||
server_name njp.ma;
|
||||
server_name phantas.ma;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/njp.ma/chain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/njp.ma/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/njp.ma/privkey.pem;
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/phantas.ma/chain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/phantas.ma/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/phantas.ma/privkey.pem;
|
||||
#include /etc/letsencrypt/nginx.conf;
|
||||
|
||||
location /favicon.ico {
|
|
@ -1,14 +1,14 @@
|
|||
[Unit]
|
||||
Description=njpma
|
||||
Description=phantasma
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
User=pandora
|
||||
Group=pandora
|
||||
WorkingDirectory=/srv/njpma
|
||||
WorkingDirectory=/srv/phantasma
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
ExecStart=/srv/njpma/venv/bin/gunicorn app.wsgi:application --reuse-port -b [::]:8080
|
||||
ExecStart=/srv/phantasma/venv/bin/gunicorn app.wsgi:application --reuse-port -b [::]:8085
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Reference in a new issue