1
0
Fork 0
mirror of https://github.com/MatomoCamp/live-platform.git synced 2024-09-09 04:33:44 +02:00

peertube api and more data

This commit is contained in:
Lukas Winkler 2022-11-23 17:12:24 +01:00
parent 7e4ac0f245
commit 1ad7f4e529
Signed by: lukas
GPG key ID: 54DE4D798D244853
10 changed files with 497 additions and 45 deletions

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ static/main.css.map
venv/
runtime/
static/tag-manager/files/
token.json

2
app.py
View file

@ -9,7 +9,7 @@ from utils import get_css
app = Flask(__name__)
not_yet_published_message = "The recording for this talk has not yet been published." \
not_yet_published_message = "The recording for this talk has not yet been published. " \
"Please come back later or follow us on social media to be notified."

45
data.py
View file

@ -3,11 +3,12 @@ from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from random import randint, random
from typing import List, Dict, Tuple, Optional
from urllib.parse import quote_plus
import pytz
from dateutil.parser import parse
from urls import chat_rooms, workshop_urls, recording_ids, archive_names
from urls import chat_rooms, workshop_urls, recording_ids, archive_names, recording_ids_drafts
from utils import translated_dict_to_string, time_plusminus15min
STREAM_FALLBACKS = False
@ -97,17 +98,38 @@ class Talk:
def chat_room_id(self) -> str:
return f"#{self.chat_room}:matomocamp.org"
@property
def archive_name(self) -> Optional[str]:
try:
return archive_names[self.id]
except KeyError:
return None
@property
def archive_name_encoded(self) -> Optional[str]:
try:
return quote_plus(archive_names[self.id]).replace("+", "%20")
except KeyError:
return None
@property
def chat_room_url(self) -> str:
try:
return f"https://archive.matomocamp.org/{self.year}/{archive_names[self.id]}/chat/"
return f"https://archive.matomocamp.org/{self.year}/{self.archive_name_encoded}/chat/"
except KeyError:
return "https://chat.matomocamp.org/#/room/" + self.chat_room_id
@property
def archive_url(self) -> Optional[str]:
try:
return f"https://archive.matomocamp.org/{self.year}/{archive_names[self.id]}/"
return f"https://archive.matomocamp.org/{self.year}/{self.archive_name_encoded}/"
except KeyError:
return None
@property
def subtitle_edit_url(self) -> Optional[str]:
try:
return f"https://github.com/MatomoCamp/recording-subtitles/tree/main/{self.year}/{self.archive_name_encoded}/"
except KeyError:
return None
@ -152,6 +174,12 @@ class Talk:
return recording_ids[self.id]
return None
@property
def recording_id_drafts(self) -> Optional[str]:
if self.id in recording_ids_drafts:
return recording_ids_drafts[self.id]
return None
@property
def recording_url(self) -> Optional[str]:
if self.recording_id:
@ -273,3 +301,14 @@ if __name__ == '__main__':
for talk in talks:
if talk.id not in chat_rooms:
print(f"missing chatroom: {talk.id} ({talk.title})")
with open("/home/lukas/tmp/stats.json") as f:
data = json.load(f)
data_dict = {}
for e in data:
data_dict[e["label"].lstrip("/")] = e
for talk in talks:
if talk.year != 2022 or talk.id == "389UYH":
continue
num_pageviews = data_dict[talk.id]["nb_visits"]
print(f"{talk.title};{', '.join(talk.speaker_names)};{num_pageviews}")

1
peertube/__init__.py Normal file
View file

@ -0,0 +1 @@
from .api import PeertubeAPI

131
peertube/api.py Normal file
View file

@ -0,0 +1,131 @@
import inspect
import json
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Tuple, Literal, Dict, List, Union
from requests import Session
technology_category = 15
license_id_sa = 2
class PeertubeAPI:
def __init__(self, base_url: str):
self.base_url = base_url.rstrip("/")
self.api_url = self.base_url + "/api/v1"
self.s = Session()
self.s.max_redirects = 0
try:
with open("token.json") as f:
data = json.load(f)
self.access_token = data["access_token"]
except FileNotFoundError:
self.access_token = None
def get_client(self) -> Tuple[str, str]:
r = self.s.get(self.api_url + "/oauth-clients/local")
r.raise_for_status()
data = r.json()
return data["client_id"], data["client_secret"]
def login(self, username: str, password: str):
client_id, client_secret = self.get_client()
now = datetime.now().timestamp()
r = self.s.post(self.api_url + "/users/token", data={
"client_id": client_id,
"client_secret": client_secret,
"username": username,
"password": password,
"grant_type": "password"
})
r.raise_for_status()
data = r.json()
data["expires_in_ts"] = now + data["expires_in"]
data["refresh_token_expires_in_ts"] = now + data["refresh_token_expires_in"]
with open("token.json", "w") as f:
json.dump(data, f)
@property
def headers(self):
return {
"Authorization": f"Bearer {self.access_token}"
}
def get_video(self, id: str) -> "Video":
r = self.s.get(self.api_url + f"/videos/{id}")
r.raise_for_status()
data = r.json()
r2 = self.s.get(self.api_url + f"/videos/{id}/description")
r2.raise_for_status()
data["description"] = r2.json()["description"]
return Video.from_dict(data, self)
def get_captions(self, id: str) -> Dict[str, Dict[str, Union[str, int]]]:
r = self.s.get(self.api_url + f"/videos/{id}/captions")
r.raise_for_status()
subtitles = {}
for entry in r.json()["data"]:
entry["timestamp"] = datetime.fromisoformat(entry["updatedAt"].replace("Z", "+00:00")).timestamp()
subtitles[entry["language"]["id"]] = entry
return subtitles
def upload_caption(self, id: str, lang: str, caption_file: Path):
r = self.s.put(
self.api_url + f"/videos/{id}/captions/{lang}",
headers=self.headers,
files={
"captionfile": caption_file.open("rb")
}
)
r.raise_for_status()
def update_video(self, id: str, data: Dict) -> None:
del data["api"]
r = self.s.put(self.api_url + f"/videos/{id}", json=data, headers=self.headers)
# print(r.json())
r.raise_for_status()
def update_thumbnail(self, id: str, file: Path) -> None:
print(file)
r = self.s.put(self.api_url + f"/videos/{id}", files={
"thumbnailfile": ("thumb.png", file.open("rb"), "image/png")
}, headers=self.headers)
r.raise_for_status()
@dataclass
class Video:
shortUUID: str
api: PeertubeAPI
category: int
description: str
language: str
licence: int
name: str
originallyPublishedAt: str
privacy: Literal[1, 2, 3, 4]
support: str
tags: List[str] = field()
@classmethod
def from_dict(cls, env, api: PeertubeAPI):
"""
https://stackoverflow.com/a/55096964/4398037
"""
bla = {}
for k, v in env.items():
if k not in inspect.signature(cls).parameters:
continue
if isinstance(v, dict):
v = v["id"]
bla[k] = v
return cls(api=api, **bla)
def save(self) -> None:
self.api.update_video(self.shortUUID, self.__dict__)

80
poetry.lock generated
View file

@ -9,6 +9,25 @@ python-versions = ">=3.6"
[package.dependencies]
pytz = ">=2015.7"
[[package]]
name = "certifi"
version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "charset-normalizer"
version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.6.0"
[package.extras]
unicode-backport = ["unicodedata2"]
[[package]]
name = "click"
version = "8.1.3"
@ -64,6 +83,14 @@ gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
version = "5.0.0"
@ -140,6 +167,24 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "requests"
version = "2.28.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7, <4"
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<3"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "setproctitle"
version = "1.3.2"
@ -172,6 +217,19 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "urllib3"
version = "1.26.12"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "werkzeug"
version = "2.2.2"
@ -201,13 +259,21 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "654a73bf99aa07317e8feb8664836eb1159a6bb9f04ce370c5f8a8c85c0a861a"
content-hash = "56c965d24edfb55874c9fccb0cc9c43f1959d69e3a9be1d02fcdf0adf9aa4fd4"
[metadata.files]
babel = [
{file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"},
{file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"},
]
certifi = [
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
{file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
charset-normalizer = [
{file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
{file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
@ -224,6 +290,10 @@ gunicorn = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
]
idna = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
{file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
{file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
@ -298,6 +368,10 @@ pytz = [
{file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"},
{file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"},
]
requests = [
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
setproctitle = [
{file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:288943dec88e178bb2fd868adf491197cc0fc8b6810416b1c6775e686bab87fe"},
{file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:630f6fe5e24a619ccf970c78e084319ee8be5be253ecc9b5b216b0f474f5ef18"},
@ -368,6 +442,10 @@ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
urllib3 = [
{file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
{file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
werkzeug = [
{file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"},
{file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"},

View file

@ -12,6 +12,7 @@ Babel = "^2.9.1"
libsass = "^0.21.0"
gunicorn = "^20.1.0"
setproctitle = "^1.2.2"
requests = "^2.28.1"
[tool.poetry.dev-dependencies]

169
urls.py
View file

@ -34,48 +34,46 @@ chat_rooms = {
# 2022
"H7YHTG": "event-introduction-2022", # Event introduction by the MatomoCamp organizers
"JSPF8U": "multi-server-and-docker", # Yet to be named support talk
"PYQY3A": "monter-une-infrastructure-matomo", # Monter une infrastructure Matomo qui gère des millions de hits par mois nest pas si simple
"H7YHTG": "event-introduction-2022",
"JSPF8U": "multi-server-and-docker",
"PYQY3A": "monter-une-infrastructure-matomo",
"3DBQCM": "developing-a-plugin",
# How to create your own Matomo plugin? A technical introduction to developing for Matomo.
"3LRN3P": "ideal-matomo", # Imagine Ideal Matomo !
"EMG79M": "reussir-limplementation-de-matomo", # Réussir l'implémentationde Matomo - SOS Villages d'enfants x Elevate
"MWQUT7": "future-of-matomo", # An Inside Look at the Future of Matomo: User Experience + User Connection
"S37GBU": "gestion-de-projet-web-analytics", # Gestion de projet Web Analytics avec Matomo
"PU7SJR": "contributing-back", # Contribute back
"33QBWR": "importance-of-privacy", # Is Matomo only for European companies and authorities?
"AXGTWL": "analysez-vos-performances", # Analysez vos performances digitales avec Matomo
"SXLAJL": "matomo-in-a-data-stack", # Integrating Matomo in a data stack
"R393MD": "external-dashboards-data-visualization", # Matomo: External Dashboards & Data Visualization
"SNEUXP": "migration-de-vos-datas-ga-vers-matomo", # Migration de vos datas Google Analytics UA vers Matomo
"QCGUJB": "security-hardening", # Linux security hardening on a Matomo installation
"ADJLAW": "dashboards-with-apache-superset", # Open source dashboards ontop of Matomo with Apache Superset
"PYT37H": "matomo-et-wordpress", # Matomo et WordPress comment les connecter ?
"3LRN3P": "ideal-matomo",
"EMG79M": "reussir-limplementation-de-matomo",
"MWQUT7": "future-of-matomo",
"S37GBU": "gestion-de-projet-web-analytics",
"PU7SJR": "contributing-back",
"33QBWR": "importance-of-privacy",
"AXGTWL": "analysez-vos-performances",
"SXLAJL": "matomo-in-a-data-stack",
"R393MD": "external-dashboards-data-visualization",
"SNEUXP": "migration-de-vos-datas-ga-vers-matomo",
"QCGUJB": "security-hardening",
"ADJLAW": "dashboards-with-apache-superset",
"PYT37H": "matomo-et-wordpress",
"QB8VY8": "using-kubernetes-and-docker",
# Running Matomo with open source technologies such as Kubernetes and docker
"XGPBTU": "matomo-and-us-distributors", # Why Wholesale Distributors in the US should be using Matomo
"XMGR83": "les-traductions-dans-matomo", # Les traductions dans Matomo, comment ça marche?
"XGPBTU": "matomo-and-us-distributors",
"XMGR83": "les-traductions-dans-matomo",
"UKTQD8": "matomo-su-kubernetes",
"EDHADW": "tag-manager-and-consent-solutions", # Tag Manager/Consent solutions
"CQ3TRP": "vanilla-analytics", # Matomo & Vanilla Analytics
"U9GDMC": "vuejs-in-plugins", # Building Matomo plugins with VueJS.
"TPFDWN": "dashboard", # Get a kick ass dashboard in Matomo
"SRDLAJ": "das-problem-zu-viel", # Das Problem "zu viel" - Mehr Insights in Matomo
"KDWXFH": "intranet-analytics", # Get started with intranet analytics in Matomo
"XRVPDS": "das-problem-zu-wenig", # Das Problem "zu wenig" - Mehr Insights in Matomo
"ZXGJYA": "improve-seo", # Improve your SEO with Matomo
"7YDHL7": "tracking-spas", # Tracking SPA applications with Matomo
"97RZGZ": "standortermittlung", # Standortermittlung mit Matomo
"FXLH93": "snowplow", # Data Creation and Snowplow Open Source
"FUD7JR": "log-analytics", # How to use Matomo Log Analysis to retrieve audience data
"BRH8SH": "datenschutzkonformes-performance-marketing", # Datenschutzkonformes Performance Marketing mit Matomo
"N3WJCA": "creating-plugins-as-non-developers", # How to create a plugin for Matomo when you are not a developer?
"EDHADW": "tag-manager-and-consent-solutions",
"CQ3TRP": "vanilla-analytics",
"U9GDMC": "vuejs-in-plugins",
"TPFDWN": "dashboard",
"SRDLAJ": "das-problem-zu-viel",
"KDWXFH": "intranet-analytics",
"XRVPDS": "das-problem-zu-wenig",
"ZXGJYA": "improve-seo",
"7YDHL7": "tracking-spas",
"97RZGZ": "standortermittlung",
"FXLH93": "snowplow",
"FUD7JR": "log-analytics",
"BRH8SH": "datenschutzkonformes-performance-marketing",
"N3WJCA": "creating-plugins-as-non-developers",
"YE9MYB": "plausible-analytics",
"389UYH": "joomla", # Utilisez Matomo avec Joomla
"E893JM": "web-analytics-book", # The big book about web analytics
"WXXXXE": "migrating-matomo", # The ultimate guide to migrate to Matomo: 7 best practices
"ST38HK": "closing-event-2022", # MatomoCamp closing
"389UYH": "joomla",
"E893JM": "web-analytics-book",
"WXXXXE": "migrating-matomo",
"ST38HK": "closing-event-2022",
}
archive_names = {
@ -108,7 +106,49 @@ archive_names = {
"XTF7GX": "Tips and Tricks",
"Y9AAMG": "Host your own Instance",
# "ZBWZHJ": "cleaninsights-workshop",
"QQJ3ZS": "PII"
"QQJ3ZS": "PII",
"UKTQD8": "Matomo su Kubernetes",
"H7YHTG": "Opening",
"JSPF8U": "Multi-Server and Docker",
# "PYQY3A": "monter-une-infrastructure-matomo",
"3DBQCM": "Your own plugin",
"3LRN3P": "Ideal Matomo",
"EMG79M": "Réussir l'implémentation de Matomo",
"MWQUT7": "Future of Matomo",
# "S37GBU": "gestion-de-projet-web-analytics",
# "PU7SJR": "contributing-back",
"33QBWR": "Importance of Privacy",
# "AXGTWL": "analysez-vos-performances",
# "SXLAJL": "matomo-in-a-data-stack",
"R393MD": "External Dashboards & Data Visualization",
# "SNEUXP": "migration-de-vos-datas-ga-vers-matomo",
"QCGUJB": "Security Hardening",
# "ADJLAW": "dashboards-with-apache-superset",
# "PYT37H": "matomo-et-wordpress",
# "QB8VY8": "using-kubernetes-and-docker",
# "XGPBTU": "matomo-and-us-distributors",
# "XMGR83": "les-traductions-dans-matomo",
# "UKTQD8": "matomo-su-kubernetes",
"EDHADW": "Tag Manager and Consent solutions",
# "CQ3TRP": "vanilla-analytics",
"U9GDMC": "VueJS in plugins",
"TPFDWN": "Kick-ass dashboard",
"SRDLAJ": "Das Problem \"zu viel\"",
"KDWXFH": "Intranet Analytics",
# "XRVPDS": "das-problem-zu-wenig",
"ZXGJYA": "Improve your SEO",
# "7YDHL7": "tracking-spas",
"97RZGZ": "Standortermittlung",
"FXLH93": "Snowplow",
"FUD7JR": "Log Analytics",
"BRH8SH": "Performance Marketing",
"N3WJCA": "Creating plugins as non-developers",
"YE9MYB": "Plausible Analytics",
# "389UYH": "joomla",
"E893JM": "Web Analytics Book",
"WXXXXE": "Migrating Matomo",
"ST38HK": "Closing",
}
@ -146,8 +186,57 @@ recording_ids = {
"URWXGR": "9cExMatGbi3xH4hjJgndvt",
"ENHZCR": "dEYt3WxM5V4jBk6BhT5oab",
"GMUXZZ": "7N9bhdSHYq6zvfrt5E8mwN",
"8HSWJD": "cBxLoU5N9g23C7QbBFsAf6"
"8HSWJD": "cBxLoU5N9g23C7QbBFsAf6",
"ZXGJYA": "pJ1C6vLGhcJSfw5ZHvc6YA",
"YE9MYB": "s8kxkasrJr38x4CiZcfSu6",
"R393MD": "a4QnuwVmcDrEnjghxHspYt",
"EDHADW": "55yLTdxSPh4ntJZRr5Gf1Q",
}
recording_ids_drafts = {
"H7YHTG": "uo31Qr3gSdd4zFh2knTpr5",
"JSPF8U": "gbUPYbQSEmpFTn3hgYfuMN",
# "PYQY3A": "monter-une-infrastructure-matomo",
"3DBQCM": "nK3Hyq8hG1MtQ7zFdSQVpD",
"3LRN3P": "pj39SJYfUX9ZVXB6KHoGo7",
"EMG79M": "dQsWaFHLCAqnDhMyW34jDW",
"MWQUT7": "qVqqbK6ezsJnZcbLg2HzPp",
# "S37GBU": "gestion-de-projet-web-analytics",
# "PU7SJR": "contributing-back",
"33QBWR": "mR4XH2d6pJc1PUSokvgGBp",
# "AXGTWL": "analysez-vos-performances",
# "SXLAJL": "matomo-in-a-data-stack",
# "SNEUXP": "migration-de-vos-datas-ga-vers-matomo",
"QCGUJB": "bqUDz21DEBfNB674Hekeu2",
# "ADJLAW": "dashboards-with-apache-superset",
# "PYT37H": "matomo-et-wordpress",
# "QB8VY8": "using-kubernetes-and-docker",
# "XGPBTU": "matomo-and-us-distributors",
# "XMGR83": "les-traductions-dans-matomo",
"UKTQD8": "mmKVHh4uP9dvXCuUX7sKmo",
# "CQ3TRP": "vanilla-analytics",
"U9GDMC": "kazVeZ6x7Tv13CZuJDQwa1",
"TPFDWN": "faxC1Tc6ukrDD7yZUZXaPw",
"SRDLAJ": "dFUE4UsdsAxmCtWLsMVdfS",
"KDWXFH": "1Xed35oAQsDbPafBosMK1W",
# "XRVPDS": "das-problem-zu-wenig",
# "7YDHL7": "tracking-spas",
"97RZGZ": "tFZqwSVPX6nqeXvJedgzsG",
"FXLH93": "jjuoDmucxfLWTtzyfsn21a",
"FUD7JR": "cyVg7Z1RJoDJdEJ5o81k65",
"BRH8SH": "jbQpLLk2q7MEXqAggbinLA",
"N3WJCA": "q2ij3WGyehmkpxxjSqguy1",
# "389UYH": "joomla",
"E893JM": "5opi9rRkxYLv9Fz2VWUAgA",
"WXXXXE": "iWBWDy8DeF3RBSbVZkEnYs",
"ST38HK": "jjzQEvcuw5fgsqmjD4d94J",
}
recording_ids_drafts = {**recording_ids_drafts, **recording_ids}
slides = {
"GDGZXF": "https://schedule.matomocamp.org/media/matomocamp-2021/submissions/GDGZXF/resources/MatomoCamp_opening_ceremony_v2_ln8dBhV.pdf"
}
for name in chat_rooms.values():

View file

@ -1,5 +1,7 @@
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Dict, Set
import sass
@ -48,6 +50,14 @@ def time_plusminus15min(time: datetime) -> Set[datetime]:
times.add(time - min15)
return times
def print_diff_call(str1: str, str2: str, title: str) -> None:
with NamedTemporaryFile(delete=True, mode="w", suffix=title) as tmp1:
with NamedTemporaryFile(delete=True, mode="w", suffix=title) as tmp2:
tmp1.write(str1)
tmp1.flush()
tmp2.write(str2)
tmp2.flush()
subprocess.run(["git", "--no-pager", "diff", "--color-words=.", tmp1.name, tmp2.name])
if __name__ == '__main__':
save_css()

102
video.py Normal file
View file

@ -0,0 +1,102 @@
import sys
import time
from getpass import getpass
from pathlib import Path
from data import talks, Talk
from peertube import PeertubeAPI
from peertube.api import license_id_sa, technology_category
from utils import print_diff_call
a = PeertubeAPI(base_url="https://video.matomocamp.org")
projects_dir = Path("/media/ssd/MatomoCamp Recordings/Projects/")
def description_of_talk(talk: Talk):
text = talk.description
text += "\n\n"
text += f"by {', '.join(talk.speaker_names)}\n"
text += "\n"
text += f"View in Schedule: {talk.schedule_url}\n"
text += f"Audio-only: {talk.archive_url}output.opus\n"
text += f"High-Quality Video: {talk.archive_url}output.mp4\n"
text += f"Help improve the subtitles: {talk.subtitle_edit_url}\n"
return text
if not a.access_token:
username = input("username: ")
password = getpass()
a.login(username, password)
for talk in talks:
if not talk.recording_id_drafts:
continue
if talk.year != 2022:
continue
time.sleep(1)
if sys.argv[1] and talk.id!=sys.argv[1]:
continue
print(talk)
video = a.get_video(talk.recording_id_drafts)
project_dir = projects_dir / str(talk.year) / talk.archive_name
print(project_dir)
assert project_dir.exists()
subtitles = {}
subtitle_en = project_dir / "output.srt"
if subtitle_en.exists():
subtitles["en"] = subtitle_en
for lang in ["de", "en", "it", "fr"]:
subtitle_lang = project_dir / f"output.{lang}.srt"
if subtitle_lang.exists():
subtitles[lang] = subtitle_lang
remote_captions = a.get_captions(talk.recording_id_drafts)
for lang, subtitle in subtitles.items():
if lang not in remote_captions or subtitle.stat().st_mtime > remote_captions[lang]["timestamp"]:
print("upload")
a.upload_caption(talk.recording_id_drafts, lang, subtitle)
if talk.year != 2022:
continue
starttime = talk.start.isoformat().replace('+00:00', '.000Z')
video.tags = ["MatomoCamp", talk.track]
video.language = talk.language
video.licence = license_id_sa
video.category = technology_category
video.originallyPublishedAt = starttime
video.name = talk.title
if video.privacy == 2:
video.name = "Draft - " + video.name
if not video.description:
a.update_thumbnail(talk.recording_id_drafts, project_dir / "title-page.png")
video.description = description_of_talk(talk)
print_diff_call(video.description, description_of_talk(talk), video.shortUUID)
assert talk.year == 2022
video.save()
time.sleep(.8)
# assert talk.title == video.name
# if video.shortUUID == "gqEzAyzDJz4KLR9gwygVK2":
# video.originallyPublishedAt = starttime
# video.name = talk.title
# video.save()
print("\n\n")
# video.licence = license_id_sa
# video.category = technology_category
# if "MatomoCamp" not in video.tags:
# video.tags.append("MatomoCamp")
# if not video.language:
# video.language = "en"
# print(video)
# video.description = "test"
# video.save()