diff --git a/app/item/migrations/0011_week_use_hue_alter_week_break_notice.py b/app/item/migrations/0011_week_use_hue_alter_week_break_notice.py new file mode 100644 index 0000000..fac43e1 --- /dev/null +++ b/app/item/migrations/0011_week_use_hue_alter_week_break_notice.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.5 on 2025-02-13 15:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("item", "0010_week_break_notice_week_is_break"), + ] + + operations = [ + migrations.AddField( + model_name="week", + name="use_hue", + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name="week", + name="break_notice", + field=models.TextField(blank=True, default=""), + ), + ] diff --git a/app/item/models.py b/app/item/models.py index 73fb505..d660c5c 100644 --- a/app/item/models.py +++ b/app/item/models.py @@ -1,3 +1,4 @@ +import urllib.parse from django.utils.timezone import datetime, timedelta from django.utils import timezone import json @@ -11,6 +12,8 @@ from django.db.models.functions import ExtractWeek, ExtractYear from django.urls import reverse from django.utils.timesince import timesince +import ox + User = get_user_model() @@ -27,7 +30,6 @@ class Item(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - title = models.CharField(max_length=1024) url = models.CharField(max_length=1024, unique=True) description = models.TextField(default="", blank=True, editable=False) @@ -40,6 +42,10 @@ class Item(models.Model): #if self.url and not self.data.get("url") == self.url: if self.url: self.update_data() + if self.use_hue: + if "hue" in self.data: + del self.data["hue"] + self.get_hue() super().save(*args, **kwargs) def __str__(self): @@ -104,6 +110,14 @@ class Item(models.Model): d = '%s-W%s' % (self.get_year(), self.get_week()) return datetime.strptime(d + '-1', "%Y-W%W-%w").strftime('%Y-%m-%d') + @property + def use_hue(self): + monday = self.get_monday() + week = Week.objects.filter(monday=monday).first() + if week: + return week.use_hue + return False + def get_absolute_url(self): return reverse('item', kwargs={'id': self.id}) @@ -112,6 +126,8 @@ class Item(models.Model): def update_data(self): self.data.update(self.parse_url()) + if "hue" in self.data: + del self.data["hue"] def parse_url(self): content = requests.get(self.url).text @@ -133,6 +149,84 @@ class Item(models.Model): data["thumbnail"] = data["thumbnail"].replace('/512p', '/1024p') return data + def get_hue(self, update=False): + if "hue" in self.data: + return self.data["hue"] + if update: + self.save() + return self.data.get("hue") + + hue = None + parts = self.url.split('/') + url = '/'.join(parts[:3]) + '/api/' + if parts[4] == 'edits': + edit = urllib.parse.unquote(parts[5]).replace('_', ' ') + request = { + "action": "getEdit", + "data": { + "id": edit, + "keys": [] + } + } + response = requests.post(url, json=request).json() + clips = response["data"]["clips"] + if clips: + hue = clips[int(len(clips)/2)]['hue'] + else: + item = parts[4] + parts = parts[5:] + if parts and parts[0] in ("editor", "player"): + parts = parts[1:] + args = {} + if parts and "?" in parts[-1]: + part, arguments = parts[-1].split('?') + parts[-1] = part + args = dict( + kv.split('=', 1) + for kv in urllib.parse.unquote(arguments).split("&") + ) + if ',' in parts[0]: + start, end = [ox.parse_timecode(p) for p in parts[0].split(',')] + request = { + "action": "findClips", + "data": { + "query": { + "conditions": [ + {"key": "out", "value": start, "operator": ">"}, + {"key": "in", "value": end, "operator": "<"}, + ] + }, + "itemsQuery": { + "conditions": [{"key": "id", "value": item, "operator": "=="}] + }, + "keys": ["id", "in", "out", "hue"], + "range": [0, 5000], + "sort": [ + {"key": "in", "operator": "+"}, + {"key": "out", "operator": "+"}, + ] + } + } + response = requests.post(url, json=request).json() + items = response["data"]["items"] + if items: + hue = items[int(len(items)/2)]["hue"] + else: + annotation = '%s/%s' % (item, parts[0]) + request = { + "action": "getAnnotation", + "data": { + "id": annotation, + "keys": ["hue"] + } + } + response = requests.post(url, json=request).json() + hue = response["data"]["hue"] + + if hue is not None: + self.data["hue"] = hue + return hue + class Comment(models.Model): created = models.DateTimeField(auto_now_add=True) @@ -198,8 +292,30 @@ class Week(models.Model): title = models.CharField(max_length=2048, blank=True, default="") byline = models.CharField(max_length=2048, blank=True, default="") published = models.DateTimeField(null=True, default=None, blank=True, editable=False) + use_hue = models.BooleanField(default=False) is_break = models.BooleanField(default=False) break_notice = models.TextField(default="", blank=True) def __str__(self): return "%s (%s)" % (self.title, self.monday) + + def items(self): + from datetime import date + monday = timezone.make_aware(datetime.combine(self.monday, datetime.min.time()), timezone.get_default_timezone()) + monday += timedelta(days=7) + items, _ = Item.public(monday) + return items.order_by('published') + + @property + def background(self): + if self.use_hue: + colors = [] + for item in self.items(): + color = item.get_hue(update=True) + if color: + if not colors: + colors.append(f'hsl({color}, 100%, 15%, 0.8)') + else: + colors.append(f'hsl({color}, 60%, 15%, 0.8)') + return 'linear-gradient(to bottom, %s)' % ', '.join(colors) + return '' diff --git a/app/item/views.py b/app/item/views.py index 3ce69e1..f289c87 100644 --- a/app/item/views.py +++ b/app/item/views.py @@ -109,6 +109,7 @@ def archive(request, year=None, month=None, day=None, week=None): if extra: context['week_title'] = extra.title context['week_byline'] = extra.byline + context['week_background'] = extra.background elif week: week = int(week) year = int(year) diff --git a/app/static/css/site.scss b/app/static/css/site.scss index 93de924..25b8292 100644 --- a/app/static/css/site.scss +++ b/app/static/css/site.scss @@ -6,6 +6,12 @@ --title: rgb(240, 240, 240); } +html, body { + overscroll-behavior: none; + background-color: var(--bg); + +} + @font-face { font-family: "wrong font"; src: url("https://files.pad.ma/ttf/wrongfont.woff") format("woff"); diff --git a/app/static/js/edits.js b/app/static/js/edits.js index f691d61..99752e7 100644 --- a/app/static/js/edits.js +++ b/app/static/js/edits.js @@ -190,6 +190,15 @@ async function loadEdit(id, args) { previous = annotation }) } + if (window.useHue && data.edit.clips && data.edit.clips.length) { + const color1 = data.edit.clips[0].hue + const color2 = data.edit.clips[parseInt(data.edit.clips.length/2)].hue + const color3 = data.edit.clips[data.edit.clips.length-1].hue + document.documentElement.style.setProperty('--color1', `hsl(${color1}, 100%, 15%, 0.8)`); + document.documentElement.style.setProperty('--color2', `hsl(${color2}, 60%, 15%, 0.8)`); + document.documentElement.style.setProperty('--color3', `hsl(${color3}, 60%, 15%, 0.8)`); + } + var value = [] pandora.layerKeys.forEach(layer => { if (!data.layers[layer]) { diff --git a/app/static/js/item.js b/app/static/js/item.js index 42c4d2c..1b7928f 100644 --- a/app/static/js/item.js +++ b/app/static/js/item.js @@ -14,6 +14,7 @@ async function loadData(id, args) { "date", "source", "summary", + "hue", "streams", "duration", "durations", @@ -30,6 +31,8 @@ async function loadData(id, args) { } } data.item = response['data'] + + if (data.item.rightslevel > pandora.site.capabilities.canPlayClips['guest']) { return { site: data.site, @@ -79,6 +82,35 @@ async function loadData(id, args) { data['in'] = data.annotation['in'] data.out = data.annotation['out'] } + if (window.useHue) { + var clips = await pandoraAPI('findClips', { + "query": { + "conditions": [ + {"key": "out", "value": data["in"], "operator": ">"}, + {"key": "in", "value": data["out"], "operator": "<"}, + ] + }, + "itemsQuery": { + "conditions": [{"key": "id", "value": data.id.split('/')[0], "operator": "=="}] + }, + "keys":["id", "in", "out", "hue"], + "range": [0, 5000], + "sort": [ + {"key": "in", "operator": "+"}, + {"key": "out", "operator": "+"}, + ] + }) + clips = clips.data?.items + if (clips && clips.length) { + const color1 = clips[0].hue + const color2 = clips[parseInt(clips.length/2)].hue + const color3 = clips[clips.length-1].hue + document.documentElement.style.setProperty('--color1', `hsl(${color1}, 100%, 15%, 0.8)`); + document.documentElement.style.setProperty('--color2', `hsl(${color2}, 60%, 15%, 0.8)`); + document.documentElement.style.setProperty('--color3', `hsl(${color3}, 60%, 15%, 0.8)`); + } + } + data.layers = {} diff --git a/app/templates/archive.html b/app/templates/archive.html index 2760125..df57c29 100644 --- a/app/templates/archive.html +++ b/app/templates/archive.html @@ -1,6 +1,16 @@ {% extends "base.html" %} {% block head %}