Compare commits

..

2 commits

Author SHA1 Message Date
Markos Gogoulos 94dd2d4af9 videojs 2023-03-24 11:44:15 +02:00
Markos Gogoulos 80a90aca7b video.js to 8.2.0 update 2023-03-14 15:42:47 +02:00
83 changed files with 43809 additions and 61433 deletions

View file

@ -1,20 +0,0 @@
---
name: "CI"
on:
pull_request:
push:
branches:
- main
paths-ignore:
- '**/README.md'
jobs:
pre-commit:
uses: ./.github/workflows/pre-commit.yml
test:
uses: ./.github/workflows/python.yml
needs: [pre-commit]
release:
uses: ./.github/workflows/docker-build-push.yml
secrets: inherit # pass all secrets
needs: [test]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'

View file

@ -1,52 +0,0 @@
name: Docker build and push
on:
workflow_call:
push:
tags:
- v*.*.*
jobs:
release:
name: Build & release to DockerHub
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
# List of Docker images to use as base name for tags
images: |
mediacms/mediacms
# Generate Docker tags based on the following events/attributes
# Set latest tag for default branch
tags: |
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
labels: |
org.opencontainers.image.title=MediaCMS
org.opencontainers.image.description=MediaCMS is a modern, fully featured open source video and media CMS, written in Python/Django and React, featuring a REST API.
org.opencontainers.image.vendor=MediaCMS
org.opencontainers.image.url=https://mediacms.io/
org.opencontainers.image.source=https://github.com/mediacms-io/mediacms
org.opencontainers.image.licenses=AGPL-3.0
- name: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

15
.github/workflows/lint_test.yml vendored Normal file
View file

@ -0,0 +1,15 @@
on:
pull_request:
push:
branches:
- main
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,11 +1,13 @@
name: pre-commit name: pre-commit
on: on:
workflow_call: pull_request:
push:
branches:
- main
jobs: jobs:
pre-commit: pre-commit:
name: Pre-Commit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View file

@ -1,11 +1,14 @@
name: Python Tests name: Python Tests
on: on:
workflow_call: pull_request:
push:
branches:
- main
jobs: jobs:
build: build:
name: Build & test via docker-compose
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -26,7 +29,7 @@ jobs:
shell: bash shell: bash
- name: Run Django Tests - name: Run Django Tests
run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest
# Run with coverage, saves report on htmlcov dir # Run with coverage, saves report on htmlcov dir
# run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest --cov --cov-report=html --cov-config=.coveragerc # run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest --cov --cov-report=html --cov-config=.coveragerc

1
.mailmap Normal file
View file

@ -0,0 +1 @@
Swift Ugandan <swiftugandan@gmail.com> <swiftugandan@gmail.com>

View file

@ -1 +1,5 @@
Yiannis Stergiou - ys.stergiou@gmail.com
Markos Gogoulos - mgogoulos@gmail.com
Swift Ugandan - swiftugandan@gmail.com
Please see https://github.com/mediacms-io/mediacms/graphs/contributors for complete list of contributors to this repository! Please see https://github.com/mediacms-io/mediacms/graphs/contributors for complete list of contributors to this repository!

View file

@ -1,4 +1,4 @@
FROM python:3.11.4-bookworm AS compile-image FROM python:3.8-buster AS compile-image
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
@ -11,7 +11,6 @@ RUN mkdir -p /home/mediacms.io/mediacms/{logs} && cd /home/mediacms.io && python
# Install dependencies: # Install dependencies:
COPY requirements.txt . COPY requirements.txt .
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
COPY . /home/mediacms.io/mediacms COPY . /home/mediacms.io/mediacms
@ -25,7 +24,7 @@ RUN wget -q http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-u
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
############ RUNTIME IMAGE ############ ############ RUNTIME IMAGE ############
FROM python:3.11.4-bookworm as runtime-image FROM python:3.8-slim-buster as runtime-image
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1

View file

@ -1,4 +1,4 @@
FROM python:3.11.4-bookworm AS compile-image FROM mediacms/mediacms:latest
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
@ -7,67 +7,10 @@ ENV VIRTUAL_ENV=/home/mediacms.io
ENV PATH="$VIRTUAL_ENV/bin:$PATH" ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV PIP_NO_CACHE_DIR=1 ENV PIP_NO_CACHE_DIR=1
RUN mkdir -p /home/mediacms.io/mediacms/{logs} && cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV RUN cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV
# Install dependencies: COPY requirements.txt .
COPY requirements.txt .
COPY requirements-dev.txt . COPY requirements-dev.txt .
RUN pip install -r requirements.txt
RUN pip install -r requirements-dev.txt RUN pip install -r requirements-dev.txt
COPY . /home/mediacms.io/mediacms
WORKDIR /home/mediacms.io/mediacms WORKDIR /home/mediacms.io/mediacms
RUN wget -q http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip && \
unzip Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip -d ../bento4 && \
mv ../bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/* ../bento4/ && \
rm -rf ../bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux && \
rm -rf ../bento4/docs && \
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
############ RUNTIME IMAGE ############
FROM python:3.11.4-bookworm as runtime-image
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# See: https://github.com/celery/celery/issues/6285#issuecomment-715316219
ENV CELERY_APP='cms'
# Use these to toggle which processes supervisord should run
ENV ENABLE_UWSGI='yes'
ENV ENABLE_NGINX='yes'
ENV ENABLE_CELERY_BEAT='yes'
ENV ENABLE_CELERY_SHORT='yes'
ENV ENABLE_CELERY_LONG='yes'
ENV ENABLE_MIGRATIONS='yes'
# Set up virtualenv
ENV VIRTUAL_ENV=/home/mediacms.io
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY --chown=www-data:www-data --from=compile-image /home/mediacms.io /home/mediacms.io
RUN apt-get update -y && apt-get -y upgrade && apt-get install --no-install-recommends \
supervisor nginx imagemagick procps wget xz-utils -y && \
rm -rf /var/lib/apt/lists/* && \
apt-get purge --auto-remove && \
apt-get clean
RUN wget -q https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \
mkdir -p ffmpeg-tmp && \
tar -xf ffmpeg-release-amd64-static.tar.xz --strip-components 1 -C ffmpeg-tmp && \
cp -v ffmpeg-tmp/ffmpeg ffmpeg-tmp/ffprobe ffmpeg-tmp/qt-faststart /usr/local/bin && \
rm -rf ffmpeg-tmp ffmpeg-release-amd64-static.tar.xz
WORKDIR /home/mediacms.io/mediacms
EXPOSE 9000 80
RUN chmod +x ./deploy/docker/entrypoint.sh
ENTRYPOINT ["./deploy/docker/entrypoint.sh"]
CMD ["./deploy/docker/start.sh"]

View file

@ -1,23 +0,0 @@
# History
## 3.0.0
### Features
- Updates Python/Django requirements and Dockerfile to use latest 3.11 Python - https://github.com/mediacms-io/mediacms/pull/826/files. This update requires some manual steps, for existing (not new) installations. Check the update section under the [Admin docs](https://github.com/mediacms-io/mediacms/blob/main/docs/admins_docs.md#2-server-installation), either for single server or for Docker Compose installations
- Upgrade postgres on Docker Compose - https://github.com/mediacms-io/mediacms/pull/749
### Fixes
- video player options for HLS - https://github.com/mediacms-io/mediacms/pull/832
- AVI videos not correctly recognised as videos - https://github.com/mediacms-io/mediacms/pull/833
## 2.1.0
### Fixes
- Increase uwsgi buffer-size parameter. This prevents an error by uwsgi with large headers - [#5b60](https://github.com/mediacms-io/mediacms/commit/5b601698a41ad97f08c1830e14b1c18f73ab8315)
- Fix issues with comments. These were not reported on the tracker but it is certain that they would not show comments on media files (non videos but also videos). Unfortunately this reverts work done with Timestamps on comments + Mentions on comments, more on PR [#802](https://github.com/mediacms-io/mediacms/pull/802)
### Features
- Allow tags to contains other characters too, not only English alphabet ones [#801](https://github.com/mediacms-io/mediacms/pull/801)
- Add simple cookie consent code [#799](https://github.com/mediacms-io/mediacms/pull/799)
- Allow password reset & email verify pages on global login required [#790](https://github.com/mediacms-io/mediacms/pull/790)
- Add api_url field to search api [#692](https://github.com/mediacms-io/mediacms/pull/692)

View file

@ -6,7 +6,7 @@
MediaCMS is a modern, fully featured open source video and media CMS. It is developed to meet the needs of modern web platforms for viewing and sharing media. It can be used to build a small to medium video and media portal within minutes. MediaCMS is a modern, fully featured open source video and media CMS. It is developed to meet the needs of modern web platforms for viewing and sharing media. It can be used to build a small to medium video and media portal within minutes.
It is built mostly using the modern stack Django + React and includes a REST API. It is built mostly using the modern stack Django + React and includes a REST API.
@ -56,15 +56,15 @@ A demo is available at https://demo.mediacms.io
## Philosophy ## Philosophy
We believe there's a need for quality open source web applications that can be used to build community portals and support collaboration. We believe there's a need for quality open source web applications that can be used to build community portals and support collaboration.
We have three goals for MediaCMS: a) deliver all functionality one would expect from a modern system, b) allow for easy installation and maintenance, c) allow easy customization and addition of features. We have three goals for MediaCMS: a) deliver all functionality one would expect from a modern system, b) allow for easy installation and maintenance, c) allow easy customization and addition of features.
## License ## License
MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt). MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt).
Copyright Markos Gogoulos. Copyright Markos Gogoulos and Yiannis Stergiou
## Support and paid services ## Support and paid services
@ -73,9 +73,9 @@ We provide custom installations, development of extra functionality, migration f
## Hardware considerations ## Hardware dependencies
For a small to medium installation, with a few hours of video uploaded daily, and a few hundreds of active daily users viewing content, 4GB Ram / 2-4 CPUs as minimum is ok. For a larger installation with many hours of video uploaded daily, consider adding more CPUs and more Ram. For a small to medium installation, with a few hours of video uploaded daily, and a few hundreds of active daily users viewing content, 4GB Ram / 2-4 CPUs as minimum is ok. For a larger installation with many hours of video uploaded daily, consider adding more CPUs and more Ram.
In terms of disk space, think of what the needs will be. A general rule is to multiply by three the size of the expected uploaded videos (since the system keeps original versions, encoded versions plus HLS), so if you receive 1G of videos daily and maintain all of them, you should consider a 1T disk across a year (1G * 3 * 365). In terms of disk space, think of what the needs will be. A general rule is to multiply by three the size of the expected uploaded videos (since the system keeps original versions, encoded versions plus HLS), so if you receive 1G of videos daily and maintain all of them, you should consider a 1T disk across a year (1G * 3 * 365).
@ -92,26 +92,18 @@ There are two ways to run MediaCMS, through Docker Compose and through installin
* [Single Server](docs/admins_docs.md#2-server-installation) page * [Single Server](docs/admins_docs.md#2-server-installation) page
* [Docker Compose](docs/admins_docs.md#3-docker-installation) page * [Docker Compose](docs/admins_docs.md#3-docker-installation) page
A complete guide can be found on the blog post [How to self-host and share your videos in 2021](https://medium.com/@MediaCMS.io/how-to-self-host-and-share-your-videos-in-2021-14067e3b291b).
## Configuration ## Configuration
Visit [Configuration](docs/admins_docs.md#5-configuration) page. Visit [Configuration](docs/admins_docs.md#5-configuration) page.
## Information for developers
Check out the new section on the [Developer Experience](docs/dev_exp.md) page
## Documentation ## Documentation
* [Users documentation](docs/user_docs.md) page * [Users documentation](docs/user_docs.md) page
* [Administrators documentation](docs/admins_docs.md) page * [Administrators documentation](docs/admins_docs.md) page
* [Developers documentation](docs/developers_docs.md) page * [Developers documentation](docs/developers_docs.md) page
## Technology ## Technology
This software uses the following list of awesome technologies: Python, Django, Django Rest Framework, Celery, PostgreSQL, Redis, Nginx, uWSGI, React, Fine Uploader, video.js, FFMPEG, Bento4 This software uses the following list of awesome technologies: Python, Django, Django Rest Framework, Celery, PostgreSQL, Redis, Nginx, uWSGI, React, Fine Uploader, video.js, FFMPEG, Bento4
@ -119,7 +111,7 @@ This software uses the following list of awesome technologies: Python, Django, D
- **Cinemata** non-profit media, technology and culture organization - https://cinemata.org - **Cinemata** non-profit media, technology and culture organization - https://cinemata.org
- **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org - **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org
- **American Association of Gynecologic Laparoscopists** - https://surgeryu.aagl.org/ - **Heritales** International Heritage Film Festival - https://stage.heritales.org
## How to contribute ## How to contribute
@ -129,12 +121,10 @@ If you like the project, here's a few things you can do
- Suggest us to others that are interested to hire us - Suggest us to others that are interested to hire us
- Write a blog post/article about MediaCMS - Write a blog post/article about MediaCMS
- Share on social media about the project - Share on social media about the project
- Open issues, participate on [discussions](https://github.com/mediacms-io/mediacms/discussions), report bugs, suggest ideas - Open issues, participate on discussions, report bugs, suggest ideas
- [Show and tell](https://github.com/mediacms-io/mediacms/discussions/categories/show-and-tell) how you are using the project
- Star the project - Star the project
- Add functionality, work on a PR, fix an issue! - Add functionality, work on a PR, fix an issue!
## Contact ## Contact
info@mediacms.io info@mediacms.io

View file

@ -59,7 +59,7 @@ def login():
file.writelines(f'USERNAME={json.loads(response.text)["username"]}\n') file.writelines(f'USERNAME={json.loads(response.text)["username"]}\n')
print(f"Welcome to MediaCMS [bold blue]{username}[/bold blue]. Your auth creds have been suceesfully stored in the .env file", ":v:") print(f"Welcome to MediaCMS [bold blue]{username}[/bold blue]. Your auth creds have been suceesfully stored in the .env file", ":v:")
else: else:
print(f'Error: {"non_field_errors": ["User not found."]}') print(f'Error: {"non_field_errors":["User not found."]}')
@apis.command() @apis.command()
@ -73,7 +73,7 @@ def upload_media():
if os.path.isdir(path): if os.path.isdir(path):
for filename in os.listdir(path): for filename in os.listdir(path):
files = {} files = {}
abs = os.path.abspath(f"{path}/{filename}") abs = os.path.abspath("{path}/{filename}")
files['media_file'] = open(f'{abs}', 'rb') files['media_file'] = open(f'{abs}', 'rb')
response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files) response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files)
if response.status_code == 201: if response.status_code == 201:

View file

@ -1,48 +0,0 @@
# Development settings, used in docker-compose-dev.yaml
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'allauth',
'allauth.account',
'allauth.socialaccount',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'imagekit',
'files.apps.FilesConfig',
'users.apps.UsersConfig',
'actions.apps.ActionsConfig',
'debug_toolbar',
'mptt',
'crispy_forms',
'uploader.apps.UploaderConfig',
'djcelery_email',
'ckeditor',
'drf_yasg',
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
DEBUG = True
CORS_ORIGIN_ALLOW_ALL = True
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static/'),)
STATIC_ROOT = None

View file

@ -93,9 +93,6 @@ ALLOW_MENTION_IN_COMMENTS = False # allowing to mention other users with @ in t
# valid options: content, author # valid options: content, author
RELATED_MEDIA_STRATEGY = "content" RELATED_MEDIA_STRATEGY = "content"
# Whether or not to generate a sitemap.xml listing the pages on the site (default: False)
GENERATE_SITEMAP = False
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
USE_TZ = True USE_TZ = True
@ -470,7 +467,7 @@ except ImportError:
if "http" not in FRONTEND_HOST: if "http" not in FRONTEND_HOST:
# FRONTEND_HOST needs a http:// preffix # FRONTEND_HOST needs a http:// preffix
FRONTEND_HOST = f"http://{FRONTEND_HOST}" # noqa FRONTEND_HOST = f"http://{FRONTEND_HOST}"
if LOCAL_INSTALL: if LOCAL_INSTALL:
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https") SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
@ -484,22 +481,5 @@ if GLOBAL_LOGIN_REQUIRED:
r'/accounts/login/$', r'/accounts/login/$',
r'/accounts/logout/$', r'/accounts/logout/$',
r'/accounts/signup/$', r'/accounts/signup/$',
r'/accounts/password/.*/$',
r'/accounts/confirm-email/.*/$',
r'/api/v[0-9]+/', r'/api/v[0-9]+/',
] ]
# if True, only show original, don't perform any action on videos
DO_NOT_TRANSCODE_VIDEO = False
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# the following is related to local development using docker
# and docker-compose-dev.yaml
try:
DEVELOPMENT_MODE = os.environ.get("DEVELOPMENT_MODE")
if DEVELOPMENT_MODE:
# keep a dev_settings.py file for local overrides
from .dev_settings import * # noqa
except ImportError:
pass

View file

@ -1,7 +1,7 @@
import debug_toolbar import debug_toolbar
from django.conf.urls import include from django.conf.urls import include, re_path
from django.contrib import admin from django.contrib import admin
from django.urls import path, re_path from django.urls import path
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.views import get_schema_view from drf_yasg.views import get_schema_view

View file

@ -7,7 +7,7 @@ if [ X"$ENABLE_MIGRATIONS" = X"yes" ]; then
echo "Running migrations service" echo "Running migrations service"
python manage.py migrate python manage.py migrate
EXISTING_INSTALLATION=`echo "from users.models import User; print(User.objects.exists())" |python manage.py shell` EXISTING_INSTALLATION=`echo "from users.models import User; print(User.objects.exists())" |python manage.py shell`
if [ "$EXISTING_INSTALLATION" = "True" ]; then if [ "$EXISTING_INSTALLATION" = "True" ]; then
echo "Loaddata has already run" echo "Loaddata has already run"
else else
echo "Running loaddata and creating admin user" echo "Running loaddata and creating admin user"
@ -67,5 +67,4 @@ fi
if [ X"$ENABLE_CELERY_LONG" = X"yes" ] ; then if [ X"$ENABLE_CELERY_LONG" = X"yes" ] ; then
echo "Enabling celery-long task worker" echo "Enabling celery-long task worker"
cp deploy/docker/supervisord/supervisord-celery_long.conf /etc/supervisor/conf.d/supervisord-celery_long.conf cp deploy/docker/supervisord/supervisord-celery_long.conf /etc/supervisor/conf.d/supervisord-celery_long.conf
rm /var/run/mediacms/* -f # remove any stale id, so that on forced restarts of celery workers there are no stale processes that prevent new ones
fi fi

View file

@ -21,4 +21,3 @@ vacuum = true
hook-master-start = unix_signal:15 gracefully_kill_them_all hook-master-start = unix_signal:15 gracefully_kill_them_all
need-app = true need-app = true
die-on-term = true die-on-term = true
buffer-size=32768

View file

@ -8,13 +8,15 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
WorkingDirectory=/home/mediacms.io/mediacms Environment=APP_DIR="/home/mediacms.io/mediacms"
Environment=CELERY_BIN="/home/mediacms.io/bin/celery" Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
Environment=CELERY_APP="cms"
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/beat%n.pid" Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/beat%n.pid"
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/beat%N.log" Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/beat%N.log"
Environment=CELERYD_LOG_LEVEL="INFO" Environment=CELERYD_LOG_LEVEL="INFO"
Environment=APP_DIR="/home/mediacms.io/mediacms"
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms beat --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL}' ExecStart=/bin/sh -c '${CELERY_BIN} beat -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR}'
ExecStop=/bin/kill -s TERM $MAINPID ExecStop=/bin/kill -s TERM $MAINPID
[Install] [Install]

View file

@ -8,21 +8,23 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
WorkingDirectory=/home/mediacms.io/mediacms Environment=APP_DIR="/home/mediacms.io/mediacms"
Environment=CELERYD_NODES="long1" Environment=CELERYD_NODES="long1"
Environment=CELERY_QUEUE="long_tasks" Environment=CELERY_QUEUE="long_tasks"
Environment=CELERY_BIN="/home/mediacms.io/bin/celery" Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
Environment=CELERY_APP="cms"
Environment=CELERYD_MULTI="multi" Environment=CELERYD_MULTI="multi"
Environment=CELERYD_OPTS="-Ofair --prefetch-multiplier=1" Environment=CELERYD_OPTS="-Ofair --prefetch-multiplier=1"
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid" Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid"
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log" Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log"
Environment=CELERYD_LOG_LEVEL="INFO" Environment=CELERYD_LOG_LEVEL="INFO"
Environment=APP_DIR="/home/mediacms.io/mediacms"
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms multi start ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}' ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}' ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
ExecReload=/bin/sh -c '${CELERY_BIN} -A cms multi restart ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}' ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -8,13 +8,14 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
WorkingDirectory=/home/mediacms.io/mediacms Environment=APP_DIR="/home/mediacms.io/mediacms"
Environment=CELERYD_NODES="short1 short2" Environment=CELERYD_NODES="short1 short2"
Environment=CELERY_QUEUE="short_tasks" Environment=CELERY_QUEUE="short_tasks"
# Absolute or relative path to the 'celery' command: # Absolute or relative path to the 'celery' command:
Environment=CELERY_BIN="/home/mediacms.io/bin/celery" Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
# App instance to use # App instance to use
# comment out this line if you don't use an app # comment out this line if you don't use an app
Environment=CELERY_APP="cms"
# or fully qualified: # or fully qualified:
#CELERY_APP="proj.tasks:app" #CELERY_APP="proj.tasks:app"
# How to call manage.py # How to call manage.py
@ -27,12 +28,13 @@ Environment=CELERYD_OPTS="--soft-time-limit=300 -c10"
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid" Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid"
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log" Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log"
Environment=CELERYD_LOG_LEVEL="INFO" Environment=CELERYD_LOG_LEVEL="INFO"
Environment=APP_DIR="/home/mediacms.io/mediacms"
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms multi start ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}' ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}' ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
ExecReload=/bin/sh -c '${CELERY_BIN} -A cms multi restart ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}' ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -1,34 +0,0 @@
module selinux-mediacms 1.0;
require {
type init_t;
type var_t;
type redis_port_t;
type postgresql_port_t;
type httpd_t;
type httpd_sys_content_t;
type httpd_sys_rw_content_t;
class file { append create execute execute_no_trans getattr ioctl lock open read rename setattr unlink write };
class dir { add_name remove_name rmdir };
class tcp_socket name_connect;
class lnk_file read;
}
#============= httpd_t ==============
allow httpd_t var_t:file { getattr open read };
#============= init_t ==============
allow init_t postgresql_port_t:tcp_socket name_connect;
allow init_t redis_port_t:tcp_socket name_connect;
allow init_t httpd_sys_content_t:dir rmdir;
allow init_t httpd_sys_content_t:file { append create execute execute_no_trans ioctl lock open read rename setattr unlink write };
allow init_t httpd_sys_content_t:lnk_file read;
allow init_t httpd_sys_rw_content_t:dir { add_name remove_name rmdir };
allow init_t httpd_sys_rw_content_t:file { create ioctl lock open read setattr unlink write };

View file

@ -24,4 +24,4 @@ vacuum = true
logto = /home/mediacms.io/mediacms/logs/errorlog.txt logto = /home/mediacms.io/mediacms/logs/errorlog.txt
disable-logging = true disable-logging = true
buffer-size=32768

View file

@ -1,22 +1,6 @@
version: "3" version: "3"
services: services:
migrations:
build:
context: .
dockerfile: ./Dockerfile-dev
image: mediacms/mediacms-dev:latest
volumes:
- ./:/home/mediacms.io/mediacms/
command: "python manage.py migrate"
environment:
DEVELOPMENT_MODE: "True"
restart: on-failure
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
frontend: frontend:
image: node:14 image: node:14
volumes: volumes:
@ -34,9 +18,7 @@ services:
context: . context: .
dockerfile: ./Dockerfile-dev dockerfile: ./Dockerfile-dev
image: mediacms/mediacms-dev:latest image: mediacms/mediacms-dev:latest
command: "python manage.py runserver 0.0.0.0:80"
environment: environment:
DEVELOPMENT_MODE: "True"
ADMIN_USER: 'admin' ADMIN_USER: 'admin'
ADMIN_PASSWORD: 'admin' ADMIN_PASSWORD: 'admin'
ADMIN_EMAIL: 'admin@localhost' ADMIN_EMAIL: 'admin@localhost'
@ -45,9 +27,12 @@ services:
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
depends_on: depends_on:
- migrations redis:
condition: service_healthy
db:
condition: service_healthy
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- ../postgres_data:/var/lib/postgresql/data/ - ../postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -55,9 +40,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
@ -69,16 +53,3 @@ services:
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
celery_worker:
image: mediacms/mediacms-dev:latest
deploy:
replicas: 1
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_BEAT: 'no'
ENABLE_MIGRATIONS: 'no'
depends_on:
- web

View file

@ -68,7 +68,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- ../postgres_data/:/var/lib/postgresql/data/ - ../postgres_data/:/var/lib/postgresql/data/
restart: always restart: always
@ -76,9 +76,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View file

@ -70,7 +70,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- ../postgres_data/:/var/lib/postgresql/data/ - ../postgres_data/:/var/lib/postgresql/data/
restart: always restart: always
@ -78,9 +78,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View file

@ -90,7 +90,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- ../postgres_data:/var/lib/postgresql/data/ - ../postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -98,9 +98,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 5 retries: 5

View file

@ -66,7 +66,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- postgres_data:/var/lib/postgresql/data/ - postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -74,9 +74,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 5 retries: 5

View file

@ -62,7 +62,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres:15.2-alpine image: postgres:13
volumes: volumes:
- ../postgres_data:/var/lib/postgresql/data/ - ../postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -70,9 +70,8 @@ services:
POSTGRES_USER: mediacms POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"] test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View file

@ -4,7 +4,7 @@
- [1. Welcome](#1-welcome) - [1. Welcome](#1-welcome)
- [2. Server Installaton](#2-server-installation) - [2. Server Installaton](#2-server-installation)
- [3. Docker Installation](#3-docker-installation) - [3. Docker Installation](#3-docker-installation)
- [4. Docker Deployment options](#4-docker-deployment-options) - [4. Docker Deployement options](#4-docker-deployment-options)
- [5. Configuration](#5-configuration) - [5. Configuration](#5-configuration)
- [6. Manage pages](#6-manage-pages) - [6. Manage pages](#6-manage-pages)
- [7. Django admin dashboard](#7-django-admin-dashboard) - [7. Django admin dashboard](#7-django-admin-dashboard)
@ -17,20 +17,19 @@
- [14. Add Google Analytics](#14-add-google-analytics) - [14. Add Google Analytics](#14-add-google-analytics)
- [15. Debugging email issues](#15-debugging-email-issues) - [15. Debugging email issues](#15-debugging-email-issues)
- [16. Frequently Asked Questions](#16-frequently-asked-questions) - [16. Frequently Asked Questions](#16-frequently-asked-questions)
- [17. Cookie consent code](#17-cookie-consent-code)
- [18. Disable encoding and show only original file](#18-disable-encoding-and-show-only-original-file)
## 1. Welcome ## 1. Welcome
This page is created for MediaCMS administrators that are responsible for setting up the software, maintaining it and making modifications. This page is created for MediaCMS administrators that are responsible for setting up the software, maintaining it and making modifications.
## 2. Server Installation ## 2. Server Installation
The core dependencies are Python3, Django3, Celery, PostgreSQL, Redis, ffmpeg. Any system that can have these dependencies installed, can run MediaCMS. But we strongly suggest installing on Linux Ubuntu (tested on versions 20, 22). The core dependencies are Python3, Django3, Celery, PostgreSQL, Redis, ffmpeg. Any system that can have these dependencies installed, can run MediaCMS. But we strongly suggest installing on Linux Ubuntu 18 or 20 versions.
Installation on an Ubuntu system with git utility installed should be completed in a few minutes with the following steps. Installation on a Ubuntu 18 or 20 system with git utility installed should be completed in a few minutes with the following steps.
Make sure you run it as user root, on a clear system, since the automatic script will install and configure the following services: Celery/PostgreSQL/Redis/Nginx and will override any existing settings. Make sure you run it as user root, on a clear system, since the automatic script will install and configure the following services: Celery/PostgreSQL/Redis/Nginx and will override any existing settings.
Automated script - tested on Ubuntu 20, Ubuntu 22 and Debian Buster Automated script - tested on Ubuntu 18, Ubuntu 20, and Debian Buster
```bash ```bash
mkdir /home/mediacms.io && cd /home/mediacms.io/ mkdir /home/mediacms.io && cd /home/mediacms.io/
@ -38,7 +37,7 @@ git clone https://github.com/mediacms-io/mediacms
cd /home/mediacms.io/mediacms/ && bash ./install.sh cd /home/mediacms.io/mediacms/ && bash ./install.sh
``` ```
The script will ask if you have a URL where you want to deploy MediaCMS, otherwise it will use localhost. If you provide a URL, it will use Let's Encrypt service to install a valid ssl certificate. The script will ask if you have a URL where you want to deploy MediaCMS, otherwise it will use localhost. If you provide a URL, it will use Let's Encrypt service to install a valid ssl certificate.
### Update ### Update
@ -49,25 +48,10 @@ If you've used the above way to install MediaCMS, update with the following:
cd /home/mediacms.io/mediacms # enter mediacms directory cd /home/mediacms.io/mediacms # enter mediacms directory
source /home/mediacms.io/bin/activate # use virtualenv source /home/mediacms.io/bin/activate # use virtualenv
git pull # update code git pull # update code
pip install -r requirements.txt -U # run pip install to update
python manage.py migrate # run Django migrations python manage.py migrate # run Django migrations
sudo systemctl restart mediacms celery_long celery_short # restart services sudo systemctl restart mediacms celery_long celery_short # restart services
``` ```
### Update from version 2 to version 3
Version 3 is using Django 4 and Celery 5, and needs a recent Python 3.x version. If you are updating from an older version, make sure Python is updated first. Version 2 could run on Python 3.6, but version 3 needs Python3.8 and higher.
The syntax for starting Celery has also changed, so you have to copy the celery related systemctl files and restart
```
# cp deploy/local_install/celery_long.service /etc/systemd/system/celery_long.service
# cp deploy/local_install/celery_short.service /etc/systemd/system/celery_short.service
# cp deploy/local_install/celery_beat.service /etc/systemd/system/celery_beat.service
# systemctl daemon-reload
# systemctl start celery_long celery_short celery_beat
```
### Configuration ### Configuration
Checkout the configuration section here. Checkout the configuration section here.
@ -81,7 +65,7 @@ Database can be backed up with pg_dump and media_files on /home/mediacms.io/medi
## Installation ## Installation
Install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/). Install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
For Ubuntu 20/22 systems this is: For Ubuntu 18/20 systems this is:
```bash ```bash
curl -fsSL https://get.docker.com -o get-docker.sh curl -fsSL https://get.docker.com -o get-docker.sh
@ -127,18 +111,6 @@ docker-compose down
docker-compose up docker-compose up
``` ```
### Update from version 2 to version 3
Version 3 is using Python 3.11 and PostgreSQL 15. If you are updating from an older version, that was using PostgreSQL 13, the automatic update will not work, as you will receive the following message when the PostgreSQL container starts:
```
db_1 | 2023-06-27 11:07:42.959 UTC [1] FATAL: database files are incompatible with server
db_1 | 2023-06-27 11:07:42.959 UTC [1] DETAIL: The data directory was initialized by PostgreSQL version 13, which is not compatible with this version 15.2.
```
At this point there are two options: either edit the Docker Compose file and make use of the existing postgres:13 image, or otherwise you have to perform the migration from postgresql 13 to version 15. More notes on https://github.com/mediacms-io/mediacms/pull/749
## Configuration ## Configuration
Checkout the configuration docs here. Checkout the configuration docs here.
@ -173,9 +145,9 @@ The main container runs migrations, mediacms_web, celery_beat, celery_workers (c
The FRONTEND_HOST in `deploy/docker/local_settings.py` is configured as http://localhost, on the docker host machine. The FRONTEND_HOST in `deploy/docker/local_settings.py` is configured as http://localhost, on the docker host machine.
### Server with ssl certificate through letsencrypt service, accessed as https://my_domain.com ### Server with ssl certificate through letsencrypt service, accessed as https://my_domain.com
Before trying this out make sure the ip points to my_domain.com. Before trying this out make sure the ip points to my_domain.com.
With this method [this deployment](../docker-compose-letsencrypt.yaml) is used. With this method [this deployment](../docker-compose-letsencrypt.yaml) is used.
Edit this file and set `VIRTUAL_HOST` as my_domain.com, `LETSENCRYPT_HOST` as my_domain.com, and your email on `LETSENCRYPT_EMAIL` Edit this file and set `VIRTUAL_HOST` as my_domain.com, `LETSENCRYPT_HOST` as my_domain.com, and your email on `LETSENCRYPT_EMAIL`
@ -205,15 +177,15 @@ The architecture below generalises all the deployment scenarios above, and provi
## 5. Configuration ## 5. Configuration
Several options are available on `cms/settings.py`, most of the things that are allowed or should be disallowed are described there. Several options are available on `cms/settings.py`, most of the things that are allowed or should be disallowed are described there.
It is advisable to override any of them by adding it to `local_settings.py` . It is advisable to override any of them by adding it to `local_settings.py` .
In case of a the single server installation, add to `cms/local_settings.py` . In case of a the single server installation, add to `cms/local_settings.py` .
In case of a docker compose installation, add to `deploy/docker/local_settings.py` . This will automatically overwrite `cms/local_settings.py` . In case of a docker compose installation, add to `deploy/docker/local_settings.py` . This will automatically overwrite `cms/local_settings.py` .
Any change needs restart of MediaCMS in order to take effect. Any change needs restart of MediaCMS in order to take effect.
Single server installation: edit `cms/local_settings.py`, make a change and restart MediaCMS Single server installation: edit `cms/local_settings.py`, make a change and restart MediaCMS
```bash ```bash
#systemctl restart mediacms #systemctl restart mediacms
@ -241,7 +213,7 @@ PORTAL_NAME = 'my awesome portal'
By default `CAN_ADD_MEDIA = "all"` means that all registered users can add media. Other valid options are: By default `CAN_ADD_MEDIA = "all"` means that all registered users can add media. Other valid options are:
- **email_verified**, a user not only has to register an account but also verify the email (by clicking the link sent upon registration). Apparently email configuration need to work, otherise users won't receive emails. - **email_verified**, a user not only has to register an account but also verify the email (by clicking the link sent upon registration). Apparently email configuration need to work, otherise users won't receive emails.
- **advancedUser**, only users that are marked as advanced users can add media. Admins or MediaCMS managers can make users advanced users by editing their profile and selecting advancedUser. - **advancedUser**, only users that are marked as advanced users can add media. Admins or MediaCMS managers can make users advanced users by editing their profile and selecting advancedUser.
@ -310,7 +282,7 @@ Make changes (True/False) to any of the following:
### 5.9 Show or hide the download option on a media ### 5.9 Show or hide the download option on a media
Edit `templates/config/installation/features.html` and set Edit `templates/config/installation/features.html` and set
``` ```
download: false download: false
@ -319,7 +291,7 @@ download: false
### 5.10 Automatically hide media upon being reported ### 5.10 Automatically hide media upon being reported
set a low number for variable `REPORTED_TIMES_THRESHOLD` set a low number for variable `REPORTED_TIMES_THRESHOLD`
eg eg
``` ```
REPORTED_TIMES_THRESHOLD = 2 REPORTED_TIMES_THRESHOLD = 2
@ -367,7 +339,7 @@ set value
MEDIA_IS_REVIEWED = False MEDIA_IS_REVIEWED = False
``` ```
any uploaded media now needs to be reviewed before it can appear to the listings. any uploaded media now needs to be reviewed before it can appear to the listings.
MediaCMS editors/managers/admins can visit the media page and edit it, where they can see the option to mark media as reviewed. By default this is set to True, so all media don't require to be reviewed MediaCMS editors/managers/admins can visit the media page and edit it, where they can see the option to mark media as reviewed. By default this is set to True, so all media don't require to be reviewed
### 5.15 Specify maximum number of media for a playlist ### 5.15 Specify maximum number of media for a playlist
@ -382,7 +354,7 @@ MAX_MEDIA_PER_PLAYLIST = 14
### 5.16 Specify maximum size of a media that can be uploaded ### 5.16 Specify maximum size of a media that can be uploaded
change `UPLOAD_MAX_SIZE`. change `UPLOAD_MAX_SIZE`.
default is 4GB default is 4GB
@ -445,7 +417,7 @@ Global notifications that are implemented are controlled by the following option
``` ```
USERS_NOTIFICATIONS = { USERS_NOTIFICATIONS = {
'MEDIA_ADDED': True, 'MEDIA_ADDED': True,
} }
``` ```
@ -470,14 +442,6 @@ ADMINS_NOTIFICATIONS = {
- Make the portal workflow public, but at the same time set `GLOBAL_LOGIN_REQUIRED = True` so that only logged in users can see content. - Make the portal workflow public, but at the same time set `GLOBAL_LOGIN_REQUIRED = True` so that only logged in users can see content.
- You can either set `REGISTER_ALLOWED = False` if you want to add members yourself or checkout options on "django-allauth settings" that affects registration in `cms/settings.py`. Eg set the portal invite only, or set email confirmation as mandatory, so that you control who registers. - You can either set `REGISTER_ALLOWED = False` if you want to add members yourself or checkout options on "django-allauth settings" that affects registration in `cms/settings.py`. Eg set the portal invite only, or set email confirmation as mandatory, so that you control who registers.
### 5.24 Enable the sitemap
Whether or not to enable generation of a sitemap file at http://your_installation/sitemap.xml (default: False)
```
GENERATE_SITEMAP = False
```
## 6. Manage pages ## 6. Manage pages
to be written to be written
@ -502,13 +466,13 @@ For example, the `Active` state of any profile can be toggled to enable or disab
## 13. How To Add A Static Page To The Sidebar ## 13. How To Add A Static Page To The Sidebar
### 1. Create your html page in templates/cms/ ### 1. Create your html page in templates/cms/
e.g. duplicate and rename about.html e.g. duplicate and rename about.html
``` ```
sudo cp templates/cms/about.html templates/cms/volunteer.html sudo cp templates/cms/about.html templates/cms/volunteer.html
``` ```
### 2. Create your css file in static/css/ ### 2. Create your css file in static/css/
``` ```
touch static/css/volunteer.css touch static/css/volunteer.css
``` ```
@ -572,24 +536,24 @@ urlpatterns = [
### 8. Add your page to the left sidebar ### 8. Add your page to the left sidebar
To add a link to your page as a menu item in the left sidebar, To add a link to your page as a menu item in the left sidebar,
add the following code after the last line in _commons.js add the following code after the last line in _commons.js
``` ```
/* Checks that a given selector has loaded. */ /* Checks that a given selector has loaded. */
const checkElement = async selector => { const checkElement = async selector => {
while ( document.querySelector(selector) === null) { while ( document.querySelector(selector) === null) {
await new Promise( resolve => requestAnimationFrame(resolve) ) await new Promise( resolve => requestAnimationFrame(resolve) )
} }
return document.querySelector(selector); return document.querySelector(selector);
}; };
/* Checks that sidebar nav menu has loaded, then adds menu item. */ /* Checks that sidebar nav menu has loaded, then adds menu item. */
checkElement('.nav-menu') checkElement('.nav-menu')
.then((element) => { .then((element) => {
(function(){ (function(){
var a = document.createElement('a'); var a = document.createElement('a');
a.href = "/volunteer"; a.href = "/volunteer";
a.title = "Volunteer"; a.title = "Volunteer";
var s = document.createElement('span'); var s = document.createElement('span');
s.className = "menu-item-icon"; s.className = "menu-item-icon";
@ -599,7 +563,7 @@ checkElement('.nav-menu')
s.appendChild(icon); s.appendChild(icon);
a.appendChild(s); a.appendChild(s);
var linkText = document.createTextNode("Volunteer"); var linkText = document.createTextNode("Volunteer");
var t = document.createElement('span'); var t = document.createElement('span');
@ -611,14 +575,14 @@ checkElement('.nav-menu')
listItem.appendChild(a); listItem.appendChild(a);
//if signed out use 3rd nav-menu //if signed out use 3rd nav-menu
var elem = document.querySelector(".nav-menu:nth-child(3) nav ul"); var elem = document.querySelector(".nav-menu:nth-child(3) nav ul");
var loc = elem.innerText; var loc = elem.innerText;
if (loc.includes("About")){ if (loc.includes("About")){
elem.insertBefore(listItem, elem.children[2]); elem.insertBefore(listItem, elem.children[2]);
} else { //if signed in use 4th nav-menu } else { //if signed in use 4th nav-menu
elem = document.querySelector(".nav-menu:nth-child(4) nav ul"); elem = document.querySelector(".nav-menu:nth-child(4) nav ul");
elem.insertBefore(listItem, elem.children[2]); elem.insertBefore(listItem, elem.children[2]);
} }
})(); })();
}); });
``` ```
@ -644,7 +608,7 @@ Instructions contributed by @alberto98fx
2. Add the Gtag/Analytics script 2. Add the Gtag/Analytics script
3. Inside ``` $DIR/mediacms/templates/root.html``` you'll see a file like this one: 3. Inside ``` $DIR/mediacms/templates/root.html``` you'll see a file like this one:
``` ```
<head> <head>
@ -655,7 +619,7 @@ Instructions contributed by @alberto98fx
{% include "common/head-meta.html" %} {% include "common/head-meta.html" %}
{% block headermeta %} {% block headermeta %}
<meta property="og:title" content="{{PORTAL_NAME}}"> <meta property="og:title" content="{{PORTAL_NAME}}">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
@ -668,17 +632,17 @@ Instructions contributed by @alberto98fx
{% block topimports %}{%endblock topimports %} {% block topimports %}{%endblock topimports %}
{% include "config/index.html" %} {% include "config/index.html" %}
{% endblock head %} {% endblock head %}
</head> </head>
``` ```
4. Add ``` {% include "tracking.html" %} ``` at the end inside the section ```<head>``` 4. Add ``` {% include "tracking.html" %} ``` at the end inside the section ```<head>```
5. If you are using Docker and didn't mount the entire dir you need to bind a new volume: 5. If you are using Docker and didn't mount the entire dir you need to bind a new volume:
``` ```
web: web:
image: mediacms/mediacms:latest image: mediacms/mediacms:latest
restart: unless-stopped restart: unless-stopped
@ -689,7 +653,7 @@ Instructions contributed by @alberto98fx
volumes: volumes:
- ./templates/root.html:/home/mediacms.io/mediacms/templates/root.html - ./templates/root.html:/home/mediacms.io/mediacms/templates/root.html
- ./templates/tracking.html://home/mediacms.io/mediacms/templates/tracking.html - ./templates/tracking.html://home/mediacms.io/mediacms/templates/tracking.html
``` ```
## 15. Debugging email issues ## 15. Debugging email issues
@ -720,7 +684,7 @@ email = EmailMessage(
email.send(fail_silently=False) email.send(fail_silently=False)
``` ```
You have the chance to either receive the email (in this case it will be sent to recipient@email.com) otherwise you will see the error. You have the chance to either receive the email (in this case it will be sent to recipient@email.com) otherwise you will see the error.
For example, while specifying wrong password for my Gmail account I get For example, while specifying wrong password for my Gmail account I get
``` ```
@ -730,7 +694,7 @@ SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn
## 16. Frequently Asked Questions ## 16. Frequently Asked Questions
Video is playing but preview thumbnails are not showing for large video files Video is playing but preview thumbnails are not showing for large video files
Chances are that the sprites file was not created correctly. Chances are that the sprites file was not created correctly.
The output of files.tasks.produce_sprite_from_video() function in this case is something like this The output of files.tasks.produce_sprite_from_video() function in this case is something like this
``` ```
@ -744,16 +708,16 @@ Solution: edit file `/etc/ImageMagick-6/policy.xml` and set bigger values for th
<policy domain="resource" name="width" value="16000KP"/> <policy domain="resource" name="width" value="16000KP"/>
``` ```
Newly added video files now will be able to produce the sprites file needed for thumbnail previews. To re-run that task on existing videos, enter the Django shell Newly added video files now will be able to produce the sprites file needed for thumbnail previews. To re-run that task on existing videos, enter the Django shell
``` ```
root@8433f923ccf5:/home/mediacms.io/mediacms# source /home/mediacms.io/bin/activate root@8433f923ccf5:/home/mediacms.io/mediacms# source /home/mediacms.io/bin/activate
root@8433f923ccf5:/home/mediacms.io/mediacms# python manage.py shell root@8433f923ccf5:/home/mediacms.io/mediacms# python manage.py shell
Python 3.8.14 (default, Sep 13 2022, 02:23:58) Python 3.8.14 (default, Sep 13 2022, 02:23:58)
``` ```
and run and run
``` ```
In [1]: from files.models import Media In [1]: from files.models import Media
@ -763,19 +727,4 @@ In [3]: for media in Media.objects.filter(media_type='video', sprites=''):
...: produce_sprite_from_video(media.friendly_token) ...: produce_sprite_from_video(media.friendly_token)
``` ```
this will re-create the sprites for videos that the task failed. this will re-create the sprites for videos that the task failed.
## 17. Cookie consent code
On file `templates/components/header.html` you can find a simple cookie consent code. It is commented, so you have to remove the `{% comment %}` and `{% endcomment %}` lines in order to enable it. Or you can replace that part with your own code that handles cookie consent banners.
![Simple Cookie Consent](images/cookie_consent.png)
## 18. Disable encoding and show only original file
When videos are uploaded, they are getting encoded to multiple resolutions, a procedure called transcoding. Sometimes this is not needed and you only need to show the original file, eg when MediaCMS is running on a low capabilities server. To achieve this, edit settings.py and set
```
DO_NOT_TRANSCODE_VIDEO = True
```
This will disable the transcoding process and only the original file will be shown. Note that this will also disable the sprites file creation, so you will not have the preview thumbnails on the video player.

View file

@ -1,60 +0,0 @@
# Developer Experience
There is ongoing effort to provide a better developer experience and document it.
## How to develop locally with Docker
First install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
Then run `docker-compose -f docker-compose-dev.yaml up`
```
user@user:~/mediacms$ docker-compose -f docker-compose-dev.yaml up
```
In a few minutes the app will be available at http://localhost . Login via admin/admin
### What does docker-compose-dev.yaml do?
It build the two images used for backend and frontend.
* Backend: `mediacms/mediacms-dev:latest`
* Frontend: `frontend`
and will start all services required for MediaCMS, as Celery/Redis for asynchronous tasks, PostgreSQL database, Django and React
For Django, the changes from the image produced by docker-compose.yaml are these:
* Django runs in debug mode, with `python manage.py runserver`
* uwsgi and nginx are not run
* Django runs in Debug mode, with Debug Toolbar
* Static files (js/css) are loaded from static/ folder
* corsheaders is installed and configured to allow all origins
For React, it will run `npm start` in the frontend folder, which will start the development server.
Check it on http://localhost:8088/
### How to develop in Django
Django starts at http://localhost and is reloading automatically. Making any change to the python code should refresh Django.
### How to develop in React
React is started on http://localhost:8088/ , code is located in frontend/ , so making changes there should have instant effect on the page. Keep in mind that React is loading data from Django, and that it has to be built so that Django can serve it.
### Making changes to the frontend
The way React is added is more complicated than the usual SPA project and this is because React is used as a library loaded by Django Templates, so it is not a standalone project and is not handling routes etc.
The two directories to consider are:
* frontend/src , for the React files
* templates/, for the Django templates.
Django is using a highly intuitive hierarchical templating system (https://docs.djangoproject.com/en/4.2/ref/templates/), where the base template is templates/root.html and all other templates are extending it.
React is called through the Django templates, eg templates/cms/media.html is loading js/media.js
In order to make changes to React code, edit code on frontend/src and check it's effect on http://localhost:8088/ . Once ready, build it and copy it to the Django static folder, so that it is served by Django.
### Development workflow with the frontend
1. Edit frontend/src/ files
2. Check changes on http://localhost:8088/
3. Build frontend with `docker-compose -f docker-compose-dev.yaml exec frontend npm run dist`
4. Copy static files to Django static folder with`cp -r frontend/dist/static/* static/`
5. Restart Django - `docker-compose -f docker-compose-dev.yaml restart web` so that it uses the new static files
6. Commit the changes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 KiB

View file

@ -40,12 +40,6 @@ class MediaAdmin(admin.ModelAdmin):
def get_comments_count(self, obj): def get_comments_count(self, obj):
return obj.comments.count() return obj.comments.count()
@admin.action(description="Generate missing encoding(s)", permissions=["change"])
def generate_missing_encodings(modeladmin, request, queryset):
for m in queryset:
m.encode(force=False)
actions = [generate_missing_encodings]
get_comments_count.short_description = "Comments count" get_comments_count.short_description = "Comments count"
@ -80,18 +74,7 @@ class SubtitleAdmin(admin.ModelAdmin):
class EncodingAdmin(admin.ModelAdmin): class EncodingAdmin(admin.ModelAdmin):
list_display = ["get_title", "chunk", "profile", "progress", "status", "has_file"] pass
list_filter = ["chunk", "profile", "status"]
def get_title(self, obj):
return str(obj)
get_title.short_description = "Encoding"
def has_file(self, obj):
return obj.media_encoding_url is not None
has_file.short_description = "Has file"
admin.site.register(EncodeProfile, EncodeProfileAdmin) admin.site.register(EncodeProfile, EncodeProfileAdmin)

View file

@ -538,8 +538,8 @@ def get_base_ffmpeg_command(
target_width = round(target_height * 16 / 9) target_width = round(target_height * 16 / 9)
scale_filter_opts = [ scale_filter_opts = [
f"if(lt(iw\\,ih)\\,{target_height}\\,{target_width})", # noqa f"if(lt(iw\\,ih)\\,{target_height}\\,{target_width})",
f"if(lt(iw\\,ih)\\,{target_width}\\,{target_height})", # noqa f"if(lt(iw\\,ih)\\,{target_width}\\,{target_height})",
"force_original_aspect_ratio=decrease", "force_original_aspect_ratio=decrease",
"force_divisible_by=2", "force_divisible_by=2",
"flags=lanczos", "flags=lanczos",
@ -785,11 +785,3 @@ def clean_query(query):
query = query.replace(char, "") query = query.replace(char, "")
return query.lower() return query.lower()
def get_alphanumeric_only(string):
"""Returns a query that contains only alphanumeric characters
This include characters other than the English alphabet too
"""
string = "".join([char for char in string if char.isalnum()])
return string.lower()

View file

@ -16,6 +16,7 @@ from django.core.files import File
from django.db import connection, models from django.db import connection, models
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.template.defaultfilters import slugify
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.html import strip_tags from django.utils.html import strip_tags
@ -427,16 +428,12 @@ class Media(models.Model):
Performs all related tasks, as check for media type, Performs all related tasks, as check for media type,
video duration, encode video duration, encode
""" """
self.set_media_type() self.set_media_type()
if self.media_type == "video": if self.media_type == "video":
self.set_thumbnail(force=True) self.set_thumbnail(force=True)
if settings.DO_NOT_TRANSCODE_VIDEO: self.produce_sprite_from_video()
self.encoding_status = "success" self.encode()
self.save()
self.produce_sprite_from_video()
else:
self.produce_sprite_from_video()
self.encode()
elif self.media_type == "image": elif self.media_type == "image":
self.set_thumbnail(force=True) self.set_thumbnail(force=True)
return True return True
@ -481,10 +478,7 @@ class Media(models.Model):
self.duration = int(round(float(ret.get("video_duration", 0)))) self.duration = int(round(float(ret.get("video_duration", 0))))
self.video_height = int(ret.get("video_height")) self.video_height = int(ret.get("video_height"))
if ret.get("video_info", {}).get("codec_name", {}) in ["mjpeg"]: if ret.get("video_info", {}).get("codec_name", {}) in ["mjpeg"]:
# best guess that this is an audio file with a thumbnail audio_file_with_thumb = True
# in other cases, it is not (eg it can be an AVI file)
if ret.get("video_info", {}).get("avg_frame_rate", "") == '0/0':
audio_file_with_thumb = True
if ret.get("is_audio") or audio_file_with_thumb: if ret.get("is_audio") or audio_file_with_thumb:
self.media_type = "audio" self.media_type = "audio"
@ -672,13 +666,6 @@ class Media(models.Model):
return ret return ret
for key in ENCODE_RESOLUTIONS_KEYS: for key in ENCODE_RESOLUTIONS_KEYS:
ret[key] = {} ret[key] = {}
# if this is enabled, return original file on a way
# that video.js can consume
if settings.DO_NOT_TRANSCODE_VIDEO:
ret['0-original'] = {"h264": {"url": helpers.url_from_path(self.media_file.path), "status": "success", "progress": 100}}
return ret
for encoding in self.encodings.select_related("profile").filter(chunk=False): for encoding in self.encodings.select_related("profile").filter(chunk=False):
if encoding.profile.extension == "gif": if encoding.profile.extension == "gif":
continue continue
@ -830,7 +817,6 @@ class Media(models.Model):
""" """
res = {} res = {}
valid_resolutions = [240, 360, 480, 720, 1080, 1440, 2160]
if self.hls_file: if self.hls_file:
if os.path.exists(self.hls_file): if os.path.exists(self.hls_file):
hls_file = self.hls_file hls_file = self.hls_file
@ -842,20 +828,11 @@ class Media(models.Model):
uri = os.path.join(p, iframe_playlist.uri) uri = os.path.join(p, iframe_playlist.uri)
if os.path.exists(uri): if os.path.exists(uri):
resolution = iframe_playlist.iframe_stream_info.resolution[1] resolution = iframe_playlist.iframe_stream_info.resolution[1]
# most probably video is vertical, getting the first value to
# be the resolution
if resolution not in valid_resolutions:
resolution = iframe_playlist.iframe_stream_info.resolution[0]
res["{}_iframe".format(resolution)] = helpers.url_from_path(uri) res["{}_iframe".format(resolution)] = helpers.url_from_path(uri)
for playlist in m3u8_obj.playlists: for playlist in m3u8_obj.playlists:
uri = os.path.join(p, playlist.uri) uri = os.path.join(p, playlist.uri)
if os.path.exists(uri): if os.path.exists(uri):
resolution = playlist.stream_info.resolution[1] resolution = playlist.stream_info.resolution[1]
# same as above
if resolution not in valid_resolutions:
resolution = playlist.stream_info.resolution[0]
res["{}_playlist".format(resolution)] = helpers.url_from_path(uri) res["{}_playlist".format(resolution)] = helpers.url_from_path(uri)
return res return res
@ -1023,8 +1000,10 @@ class Tag(models.Model):
return True return True
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.title = helpers.get_alphanumeric_only(self.title) self.title = slugify(self.title[:99])
self.title = self.title[:99] strip_text_items = ["title"]
for item in strip_text_items:
setattr(self, item, strip_tags(getattr(self, item, None)))
super(Tag, self).save(*args, **kwargs) super(Tag, self).save(*args, **kwargs)
@property @property

View file

@ -150,14 +150,10 @@ class SingleMediaSerializer(serializers.ModelSerializer):
class MediaSearchSerializer(serializers.ModelSerializer): class MediaSearchSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField() url = serializers.SerializerMethodField()
api_url = serializers.SerializerMethodField()
def get_url(self, obj): def get_url(self, obj):
return self.context["request"].build_absolute_uri(obj.get_absolute_url()) return self.context["request"].build_absolute_uri(obj.get_absolute_url())
def get_api_url(self, obj):
return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True))
class Meta: class Meta:
model = Media model = Media
fields = ( fields = (
@ -171,7 +167,6 @@ class MediaSearchSerializer(serializers.ModelSerializer):
"friendly_token", "friendly_token",
"duration", "duration",
"url", "url",
"api_url",
"media_type", "media_type",
"preview_url", "preview_url",
"categories_info", "categories_info",

View file

@ -7,11 +7,10 @@ import tempfile
from datetime import datetime, timedelta from datetime import datetime, timedelta
from celery import Task from celery import Task
from celery import shared_task as task from celery.decorators import task
from celery.exceptions import SoftTimeLimitExceeded from celery.exceptions import SoftTimeLimitExceeded
from celery.signals import task_revoked from celery.signals import task_revoked
from celery.task.control import revoke
# from celery.task.control import revoke
from celery.utils.log import get_task_logger from celery.utils.log import get_task_logger
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
@ -461,11 +460,10 @@ def check_running_states():
if (now - encoding.update_date).seconds > settings.RUNNING_STATE_STALE: if (now - encoding.update_date).seconds > settings.RUNNING_STATE_STALE:
media = encoding.media media = encoding.media
profile = encoding.profile profile = encoding.profile
# task_id = encoding.task_id task_id = encoding.task_id
# terminate task # terminate task
# TODO: not imported if task_id:
# if task_id: revoke(task_id, terminate=True)
# revoke(task_id, terminate=True)
encoding.delete() encoding.delete()
media.encode(profiles=[profile]) media.encode(profiles=[profile])
# TODO: allign with new code + chunksize... # TODO: allign with new code + chunksize...
@ -644,11 +642,7 @@ def save_user_action(user_or_session, friendly_token=None, action="watch", extra
if action == "watch": if action == "watch":
media.views += 1 media.views += 1
Media.objects.filter(friendly_token=friendly_token).update(views=media.views) media.save(update_fields=["views"])
# update field without calling save, to avoid post_save signals being triggered
# same in other actions
elif action == "report": elif action == "report":
media.reported_times += 1 media.reported_times += 1
@ -663,10 +657,10 @@ def save_user_action(user_or_session, friendly_token=None, action="watch", extra
) )
elif action == "like": elif action == "like":
media.likes += 1 media.likes += 1
Media.objects.filter(friendly_token=friendly_token).update(likes=media.likes) media.save(update_fields=["likes"])
elif action == "dislike": elif action == "dislike":
media.dislikes += 1 media.dislikes += 1
Media.objects.filter(friendly_token=friendly_token).update(dislikes=media.dislikes) media.save(update_fields=["dislikes"])
return True return True

View file

@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django.conf.urls import include from django.conf.urls import include, re_path
from django.conf.urls.static import static from django.conf.urls.static import static
from django.urls import path, re_path from django.urls import path
from . import management_views, views from . import management_views, views
from .feeds import IndexRSSFeed, SearchRSSFeed from .feeds import IndexRSSFeed, SearchRSSFeed
@ -89,6 +89,3 @@ urlpatterns = [
re_path(r"^manage/media$", views.manage_media, name="manage_media"), re_path(r"^manage/media$", views.manage_media, name="manage_media"),
re_path(r"^manage/users$", views.manage_users, name="manage_users"), re_path(r"^manage/users$", views.manage_users, name="manage_users"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if hasattr(settings, "GENERATE_SITEMAP") and settings.GENERATE_SITEMAP:
urlpatterns.append(path("sitemap.xml", views.sitemap, name="sitemap"))

View file

@ -1,5 +1,6 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from celery.task.control import revoke
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
@ -8,6 +9,7 @@ from django.core.mail import EmailMessage
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.template.defaultfilters import slugify
from drf_yasg import openapi as openapi from drf_yasg import openapi as openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework import permissions, status from rest_framework import permissions, status
@ -28,7 +30,7 @@ from cms.permissions import IsAuthorizedToAdd, IsUserOrEditor, user_allowed_to_u
from users.models import User from users.models import User
from .forms import ContactForm, MediaForm, SubtitleForm from .forms import ContactForm, MediaForm, SubtitleForm
from .helpers import clean_query, get_alphanumeric_only, produce_ffmpeg_commands from .helpers import clean_query, produce_ffmpeg_commands
from .methods import ( from .methods import (
check_comment_for_mention, check_comment_for_mention,
get_user_or_session, get_user_or_session,
@ -180,8 +182,7 @@ def edit_media(request):
media.tags.remove(tag) media.tags.remove(tag)
if form.cleaned_data.get("new_tags"): if form.cleaned_data.get("new_tags"):
for tag in form.cleaned_data.get("new_tags").split(","): for tag in form.cleaned_data.get("new_tags").split(","):
tag = get_alphanumeric_only(tag) tag = slugify(tag)
tag = tag[:99]
if tag: if tag:
try: try:
tag = Tag.objects.get(title=tag) tag = Tag.objects.get(title=tag)
@ -292,16 +293,6 @@ def search(request):
return render(request, "cms/search.html", context) return render(request, "cms/search.html", context)
def sitemap(request):
"""Sitemap"""
context = {}
context["media"] = list(Media.objects.filter(Q(listable=True)).order_by("-add_date"))
context["playlists"] = list(Playlist.objects.filter().order_by("-add_date"))
context["users"] = list(User.objects.filter())
return render(request, "sitemap.xml", context, content_type="application/xml")
def tags(request): def tags(request):
"""List tags view""" """List tags view"""
@ -1405,6 +1396,5 @@ class TaskDetail(APIView):
permission_classes = (permissions.IsAdminUser,) permission_classes = (permissions.IsAdminUser,)
def delete(self, request, uid, format=None): def delete(self, request, uid, format=None):
# This is not imported! revoke(uid, terminate=True)
# revoke(uid, terminate=True)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)

View file

@ -20,11 +20,11 @@ const formatPage = (page) => {
? templates.renderPageContent({ page: { id: pageContentId, component: page.component } }) ? templates.renderPageContent({ page: { id: pageContentId, component: page.component } })
: undefined; : undefined;
const headLinks = [ const headLinks = [
{ rel: 'preload', href: './static/lib/video-js/7.20.2/video.min.js', as: 'script' }, { rel: 'preload', href: './static/lib/video-js/8.2.0/video.min.js', as: 'script' },
...(page.headLinks ? page.headLinks : []), ...(page.headLinks ? page.headLinks : []),
]; ];
const bodyScripts = [ const bodyScripts = [
{ src: './static/lib/video-js/7.20.2/video.min.js' }, { src: './static/lib/video-js/8.2.0/video.min.js' },
...(page.bodyScripts ? page.bodyScripts : []), ...(page.bodyScripts ? page.bodyScripts : []),
]; ];

View file

@ -2579,9 +2579,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001239", "version": "1.0.30001466",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001466.tgz",
"integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", "integrity": "sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==",
"dev": true "dev": true
}, },
"caseless": { "caseless": {

View file

@ -80,7 +80,7 @@ function CommentForm(props) {
function onChangeWithMention(event, newValue, newPlainTextValue, mentions) { function onChangeWithMention(event, newValue, newPlainTextValue, mentions) {
textareaRef.current.style.height = ''; textareaRef.current.style.height = '';
setValue(newValue); setValue(newValue);
setMadeChanges(true); setMadeChanges(true);
@ -144,8 +144,8 @@ function CommentForm(props) {
<UserThumbnail /> <UserThumbnail />
<div className="form"> <div className="form">
<div className={'form-textarea-wrap' + (textareaFocused ? ' focused' : '')}> <div className={'form-textarea-wrap' + (textareaFocused ? ' focused' : '')}>
{ MediaCMS.features.media.actions.comment_mention ? { MediaCMS.features.media.actions.comment_mention ?
<MentionsInput <MentionsInput
inputRef={textareaRef} inputRef={textareaRef}
className="form-textarea" className="form-textarea"
rows="1" rows="1"
@ -430,37 +430,31 @@ export default function CommentsList(props) {
function onCommentsLoad() { function onCommentsLoad() {
const retrievedComments = [...MediaPageStore.get('media-comments')]; const retrievedComments = [...MediaPageStore.get('media-comments')];
const video = videojs('vjs_video_3');
if (MediaCMS.features.media.actions.timestampTimebar)
{
enableMarkers(video);
}
if (MediaCMS.features.media.actions.comment_mention === true)
{
retrievedComments.forEach(comment => {
comment.text = setMentions(comment.text);
});
}
video.one('loadedmetadata', () => {
retrievedComments.forEach(comment => {
comment.text = setTimestampAnchorsAndMarkers(comment.text, video);
});
displayCommentsRelatedAlert();
setComments([...retrievedComments]);
});
setComments([...retrievedComments]); setComments([...retrievedComments]);
// TODO: this code is breaking, beed ti debug, until then removing the extra
// functionality related with video/timestamp/user mentions
// const video = videojs('vjs_video_3');
// if (MediaCMS.features.media.actions.timestampTimebar)
//{
// enableMarkers(video);
//}
//if (MediaCMS.features.media.actions.comment_mention === true)
//{
// retrievedComments.forEach(comment => {
// comment.text = setMentions(comment.text);
// });
//}
// TODO: this code is breaking
// video.one('loadedmetadata', () => {
// retrievedComments.forEach(comment => {
// comment.text = setTimestampAnchorsAndMarkers(comment.text, video);
// });
// displayCommentsRelatedAlert();
// setComments([...retrievedComments]);
//});
//setComments([...retrievedComments]);
} }
function setMentions(text) function setMentions(text)
{ {
let sanitizedComment = text.split('@(_').join("<a href=\"/user/"); let sanitizedComment = text.split('@(_').join("<a href=\"/user/");
@ -470,7 +464,7 @@ export default function CommentsList(props) {
function setTimestampAnchorsAndMarkers(text, videoPlayer) function setTimestampAnchorsAndMarkers(text, videoPlayer)
{ {
function wrapTimestampWithAnchor(match, string) function wrapTimestampWithAnchor(match, string)
{ {
let split = match.split(':'), s = 0, m = 1; let split = match.split(':'), s = 0, m = 1;
let searchParameters = new URLSearchParams(window.location.search); let searchParameters = new URLSearchParams(window.location.search);

View file

@ -22,7 +22,7 @@ function downloadOptions(mediaData, allowDownload) {
if (Object.keys(encodingsInfo[k]).length) { if (Object.keys(encodingsInfo[k]).length) {
for (g in encodingsInfo[k]) { for (g in encodingsInfo[k]) {
if (encodingsInfo[k].hasOwnProperty(g)) { if (encodingsInfo[k].hasOwnProperty(g)) {
if ('success' === encodingsInfo[k][g].status && 100 === encodingsInfo[k][g].progress && null !== encodingsInfo[k][g].url) { if ('success' === encodingsInfo[k][g].status && 100 === encodingsInfo[k][g].progress) {
options[encodingsInfo[k][g].title] = { options[encodingsInfo[k][g].title] = {
text: k + ' - ' + g.toUpperCase() + ' (' + encodingsInfo[k][g].size + ')', text: k + ' - ' + g.toUpperCase() + ' (' + encodingsInfo[k][g].size + ')',
link: formatInnerLink(encodingsInfo[k][g].url, site.url), link: formatInnerLink(encodingsInfo[k][g].url, site.url),

View file

@ -19,7 +19,7 @@ function downloadOptionsList() {
if (Object.keys(encodings_info[k]).length) { if (Object.keys(encodings_info[k]).length) {
for (g in encodings_info[k]) { for (g in encodings_info[k]) {
if (encodings_info[k].hasOwnProperty(g)) { if (encodings_info[k].hasOwnProperty(g)) {
if ('success' === encodings_info[k][g].status && 100 === encodings_info[k][g].progress && null !== encodings_info[k][g].url) { if ('success' === encodings_info[k][g].status && 100 === encodings_info[k][g].progress) {
optionsList[encodings_info[k][g].title] = { optionsList[encodings_info[k][g].title] = {
text: k + ' - ' + g.toUpperCase() + ' (' + encodings_info[k][g].size + ')', text: k + ' - ' + g.toUpperCase() + ' (' + encodings_info[k][g].size + ')',
link: formatInnerLink(encodings_info[k][g].url, SiteContext._currentValue.url), link: formatInnerLink(encodings_info[k][g].url, SiteContext._currentValue.url),

View file

@ -1,5 +1,5 @@
@use "sass:math"; @use "sass:math";
@import '../../../lib/video-js/7.20.2/video-js.min.css'; @import '../../../lib/video-js/8.2.0/video-js.min.css';
@import '../../../css/includes/_variables.scss'; @import '../../../css/includes/_variables.scss';
@keyframes up-next-circle-countdown { @keyframes up-next-circle-countdown {

View file

@ -194,9 +194,7 @@ class MediaPageStore extends EventEmitter {
} }
this.loadPlaylists(); this.loadPlaylists();
if (MediaCMS.features.media.actions.comment_mention === true) { this.loadUsers();
this.loadUsers();
}
if (this.mediacms_config.member.can.readComment) { if (this.mediacms_config.member.can.readComment) {
this.loadComments(); this.loadComments();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,302 +0,0 @@
#!/bin/bash
# should be run as root on a rhel8-like system
function update_permissions
{
# fix permissions of /srv/mediacms directory
chown -R nginx:root $1
}
echo "Welcome to the MediacMS installation!";
if [ `id -u` -ne 0 ]; then
echo "Please run as root user"
exit
fi
while true; do
read -p "
This script will attempt to perform a system update, install required dependencies, and configure PostgreSQL, NGINX, Redis and a few other utilities.
It is expected to run on a new system **with no running instances of any these services**. Make sure you check the script before you continue. Then enter y or n
" yn
case $yn in
[Yy]* ) echo "OK!"; break;;
[Nn]* ) echo "Have a great day"; exit;;
* ) echo "Please answer y or n.";;
esac
done
# update configuration files
sed -i 's/\/home\/mediacms\.io\/mediacms\/Bento4-SDK-1-6-0-637\.x86_64-unknown-linux\/bin\/mp4hls/\/srv\/mediacms\/bento4\/bin\/mp4hls/g' cms/settings.py
sed -i 's/www-data/nginx/g;s/\/home\/mediacms\.io\/mediacms\/logs/\/var\/log\/mediacms/g;s/\/home\/mediacms\.io\/mediacms/\/srv\/mediacms/g;s/\/home\/mediacms\.io\/bin/\/srv\/mediacms\/virtualenv\/bin/g' deploy/local_install/celery_*.service
sed -i 's/\/home\/mediacms\.io\/mediacms/\/srv\/mediacms/g' deploy/local_install/mediacms.io
sed -i 's/\/home\/mediacms\.io\/bin/\/srv\/mediacms\/virtualenv\/bin/g;s/\/home\/mediacms\.io\/mediacms/\/srv\/mediacms/g' deploy/local_install/mediacms.service
sed -i 's/\/home\/mediacms\.io\/mediacms/\/var\/log\/mediacms/g' deploy/local_install/mediacms_logrorate
sed -i 's/www-data/nginx/g' deploy/local_install/nginx.conf
sed -i 's/www-data/nginx/g;s/\/home\/mediacms\.io\/mediacms\/logs/\/var\/log\/mediacms/g;s/\/home\/mediacms\.io\/mediacms/\/srv\/mediacms/g;s/\/home\/mediacms\.io/\/srv\/mediacms\/virtualenv/g' deploy/local_install/uwsgi.ini
osVersion=
if [[ -f /etc/os-release ]]; then
osVersion=$(grep ^ID /etc/os-release)
fi
if [[ $osVersion == *"fedora"* ]] || [[ $osVersion == *"rhel"* ]] || [[ $osVersion == *"centos"* ]] || [[ *"rocky"* ]]; then
dnf install -y epel-release https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm yum-utils
yum-config-manager --enable powertools
dnf install -y python3-virtualenv python39-devel redis postgresql postgresql-server nginx git gcc vim unzip ImageMagick python3-certbot-nginx certbot wget xz ffmpeg policycoreutils-devel cmake gcc gcc-c++ wget git bsdtar
else
echo "unsupported or unknown os"
exit -1
fi
# fix permissions of /srv/mediacms directory
update_permissions /srv/mediacms/
read -p "Enter portal URL, or press enter for localhost : " FRONTEND_HOST
read -p "Enter portal name, or press enter for 'MediaCMS : " PORTAL_NAME
[ -z "$PORTAL_NAME" ] && PORTAL_NAME='MediaCMS'
[ -z "$FRONTEND_HOST" ] && FRONTEND_HOST='localhost'
echo "Configuring postgres"
if [ ! command -v postgresql-setup > /dev/null 2>&1 ]; then
echo "Something went wrong, the command 'postgresql-setup' was not found in the system path."
exit -1
fi
postgresql-setup --initdb
# set authentication method for mediacms user to scram-sha-256
sed -i 's/.*password_encryption.*/password_encryption = scram-sha-256/' /var/lib/pgsql/data/postgresql.conf
sed -i '/# IPv4 local connections:/a host\tmediacms\tmediacms\t127.0.0.1/32\tscram-sha-256' /var/lib/pgsql/data/pg_hba.conf
systemctl enable postgresql.service --now
su -c "psql -c \"CREATE DATABASE mediacms\"" postgres
su -c "psql -c \"CREATE USER mediacms WITH ENCRYPTED PASSWORD 'mediacms'\"" postgres
su -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE mediacms TO mediacms\"" postgres
echo 'Creating python virtualenv on /srv/mediacms/virtualenv/'
mkdir /srv/mediacms/virtualenv/
cd /srv/mediacms/virtualenv/
virtualenv . --python=python3
source /srv/mediacms/virtualenv/bin/activate
cd /srv/mediacms/
pip install -r requirements.txt
systemctl enable redis.service --now
SECRET_KEY=`python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'`
# remove http or https prefix
FRONTEND_HOST=`echo "$FRONTEND_HOST" | sed -r 's/http:\/\///g'`
FRONTEND_HOST=`echo "$FRONTEND_HOST" | sed -r 's/https:\/\///g'`
FRONTEND_HOST_HTTP_PREFIX='http://'$FRONTEND_HOST
echo 'FRONTEND_HOST='\'"$FRONTEND_HOST_HTTP_PREFIX"\' >> cms/local_settings.py
echo 'PORTAL_NAME='\'"$PORTAL_NAME"\' >> cms/local_settings.py
echo "SSL_FRONTEND_HOST = FRONTEND_HOST.replace('http', 'https')" >> cms/local_settings.py
echo 'SECRET_KEY='\'"$SECRET_KEY"\' >> cms/local_settings.py
echo "LOCAL_INSTALL = True" >> cms/local_settings.py
mkdir /var/log/mediacms/
mkdir pids
update_permissions /var/log/mediacms/
python manage.py migrate
python manage.py loaddata fixtures/encoding_profiles.json
python manage.py loaddata fixtures/categories.json
python manage.py collectstatic --noinput
ADMIN_PASS=`python -c "import secrets;chars = 'abcdefghijklmnopqrstuvwxyz0123456789';print(''.join(secrets.choice(chars) for i in range(10)))"`
echo "from users.models import User; User.objects.create_superuser('admin', 'admin@example.com', '$ADMIN_PASS')" | python manage.py shell
echo "from django.contrib.sites.models import Site; Site.objects.update(name='$FRONTEND_HOST', domain='$FRONTEND_HOST')" | python manage.py shell
update_permissions /srv/mediacms/
cp deploy/local_install/celery_long.service /etc/systemd/system/celery_long.service
cp deploy/local_install/celery_short.service /etc/systemd/system/celery_short.service
cp deploy/local_install/celery_beat.service /etc/systemd/system/celery_beat.service
cp deploy/local_install/mediacms.service /etc/systemd/system/mediacms.service
mkdir -p /etc/letsencrypt/live/$FRONTEND_HOST
mkdir -p /etc/nginx/sites-enabled
mkdir -p /etc/nginx/sites-available
mkdir -p /etc/nginx/dhparams/
rm -rf /etc/nginx/conf.d/default.conf
rm -rf /etc/nginx/sites-enabled/default
cp deploy/local_install/mediacms.io_fullchain.pem /etc/letsencrypt/live/$FRONTEND_HOST/fullchain.pem
cp deploy/local_install/mediacms.io_privkey.pem /etc/letsencrypt/live/$FRONTEND_HOST/privkey.pem
cp deploy/local_install/mediacms.io /etc/nginx/sites-available/mediacms.io
ln -s /etc/nginx/sites-available/mediacms.io /etc/nginx/sites-enabled/mediacms.io
cp deploy/local_install/uwsgi_params /etc/nginx/sites-enabled/uwsgi_params
cp deploy/local_install/nginx.conf /etc/nginx/
# attempt to get a valid certificate for specified domain
while true ; do
echo "Would you like to run [c]ertbot, or [s]kip?"
read -p " : " certbotConfig
case $certbotConfig in
[cC*] )
if [ "$FRONTEND_HOST" != "localhost" ]; then
systemctl start
echo 'attempt to get a valid certificate for specified url $FRONTEND_HOST'
certbot --nginx -n --agree-tos --register-unsafely-without-email -d $FRONTEND_HOST
certbot --nginx -n --agree-tos --register-unsafely-without-email -d $FRONTEND_HOST
# unfortunately for some reason it needs to be run two times in order to create the entries
# and directory structure!!!
systemctl stop nginx
# Generate individual DH params
openssl dhparam -out /etc/nginx/dhparams/dhparams.pem 4096
fi
break
;;
[sS*] )
echo "will not call certbot utility to update ssl certificate for url 'localhost', using default ssl certificate"
cp deploy/local_install/dhparams.pem /etc/nginx/dhparams/dhparams.pem
break
;;
* )
echo "Unknown option: $certbotConfig"
;;
esac
done
# configure bento4 utility installation, for HLS
while true ; do
echo "Configuring Bento4"
echo "Would you like to [d]ownload a pre-compiled bento4 binary, or [b]uild it now?"
read -p "b/d : " bentoConfig
case $bentoConfig in
[bB*] )
echo "Building bento4 from source"
git clone -b v1.6.0-640 https://github.com/axiomatic-systems/Bento4 /srv/mediacms/bento4
cd /srv/mediacms/bento4/
mkdir bin
cd /srv/mediacms/bento4/bin/
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc)
chmod +x ../Source/Python/utils/mp4-hls.py
echo -e '#!/bin/bash' >> mp4hls
echo -e 'BASEDIR=$(pwd)' >> mp4hls
echo -e 'exec python3 "$BASEDIR/../Source/Python/utils/mp4-hls.py"' >> mp4hls
chmod +x mp4hls
break
;;
[dD*] )
cd /srv/mediacms/
wget http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
bsdtar -xf Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip -s '/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bento4/'
break
;;
* )
echo "Unknown option: $bentoConfig"
;;
esac
done
mkdir /srv/mediacms/media_files/hls
# update permissions
update_permissions /srv/mediacms/
# configure selinux
while true ; do
echo "Configuring SELinux"
echo "Would you like to [d]isable SELinux until next reboot, [c]onfigure our SELinux module, or [s]kip and not do any SELinux confgiguration?"
read -p "d/c/s : " seConfig
case $seConfig in
[Dd]* )
echo "Disabling SELinux until next reboot"
break
;;
[Cc]* )
echo "Configuring custom mediacms selinux module"
semanage fcontext -a -t bin_t /srv/mediacms/virtualenv/bin/
semanage fcontext -a -t httpd_sys_content_t "/srv/mediacms(/.*)?"
restorecon -FRv /srv/mediacms/
sebools=(httpd_can_network_connect httpd_graceful_shutdown httpd_can_network_relay nis_enabled httpd_setrlimit domain_can_mmap_files)
for bool in "${sebools[@]}"
do
setsebool -P $bool 1
done
cd /srv/mediacms/deploy/local_install/
make -f /usr/share/selinux/devel/Makefile selinux-mediacms.pp
semodule -i selinux-mediacms.pp
break
;;
[Ss]* )
echo "Skipping SELinux configuration"
break
;;
* )
echo "Unknown option: $seConfig"
;;
esac
done
# configure firewall
if command -v firewall-cmd > /dev/null 2>&1 ; then
while true ; do
echo "Configuring firewall"
echo "Would you like to configure http, https, or skip and not do any firewall configuration?"
read -p "http/https/skip : " fwConfig
case $fwConfig in
http )
echo "Opening port 80 until next reboot"
firewall-cmd --add-port=80/tcp
break
;;
https )
echo "Opening port 443 permanently"
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload
break
;;
skip )
echo "Skipping firewall configuration"
break
;;
* )
echo "Unknown option: $fwConfig"
;;
esac
done
fi
systemctl daemon-reload
systemctl start celery_long.service
systemctl start celery_short.service
systemctl start celery_beat.service
systemctl start mediacms.service
systemctl start nginx.service
echo 'MediaCMS installation completed, open browser on http://'"$FRONTEND_HOST"' and login with user admin and password '"$ADMIN_PASS"''

View file

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# should be run as root and only on Ubuntu 20/22, Debian 10/11 (Buster/Bullseye) versions! # should be run as root and only on Ubuntu 18/20, Debian 10/11 (Buster/Bullseye) versions!
echo "Welcome to the MediacMS installation!"; echo "Welcome to the MediacMS installation!";
if [ `id -u` -ne 0 ] if [ `id -u` -ne 0 ]
@ -22,11 +22,11 @@ done
osVersion=$(lsb_release -d) osVersion=$(lsb_release -d)
if [[ $osVersion == *"Ubuntu 20"* ]] || [[ $osVersion == *"Ubuntu 22"* ]] || [[ $osVersion == *"buster"* ]] || [[ $osVersion == *"bullseye"* ]]; then if [[ $osVersion == *"Ubuntu 20"* ]] || [[ $osVersion == *"Ubuntu 18"* ]] || [[ $osVersion == *"buster"* ]] || [[ $osVersion == *"bullseye"* ]]; then
echo 'Performing system update and dependency installation, this will take a few minutes' echo 'Performing system update and dependency installation, this will take a few minutes'
apt-get update && apt-get -y upgrade && apt-get install python3-venv python3-dev virtualenv redis-server postgresql nginx git gcc vim unzip imagemagick python3-certbot-nginx certbot wget xz-utils -y apt-get update && apt-get -y upgrade && apt-get install python3-venv python3-dev virtualenv redis-server postgresql nginx git gcc vim unzip imagemagick python3-certbot-nginx certbot wget xz-utils -y
else else
echo "This script is tested for Ubuntu 20/22 versions only, if you want to try MediaCMS on another system you have to perform the manual installation" echo "This script is tested for Ubuntu 18 and 20 versions only, if you want to try MediaCMS on another system you have to perform the manual installation"
exit exit
fi fi

View file

@ -12,5 +12,3 @@ pytest-cov
pytest-django pytest-django
pytest-factoryboy pytest-factoryboy
Faker Faker
django-cors-headers

View file

@ -1,21 +1,21 @@
Django==4.2.2 Django==3.1.12
djangorestframework==3.14.0 djangorestframework==3.12.2
django-allauth==0.54.0 django-allauth==0.44.0
psycopg==3.1.9 psycopg2-binary==2.8.6
uwsgi==2.0.21 uwsgi==2.0.19.1
django-redis==5.3.0 django-redis==4.12.1
celery==5.3.1 celery==4.4.7
drf-yasg==1.21.6 drf-yasg==1.20.0
Pillow==9.5.0 Pillow==8.2.0
django-imagekit==4.1.0 django-imagekit==4.1.0
markdown==3.4.3 markdown==3.3.6
django-filter==23.2 django-filter==21.1
filetype==1.2.0 filetype==1.0.10
django-mptt==0.14.0 django-mptt==0.13.4
django-crispy-forms==1.13.0 django-crispy-forms==1.13.0
requests==2.31.0 requests==2.25.0
django-celery-email==3.0.0 django-celery-email==3.0.0
m3u8==3.5.0 m3u8==1.0.0
django-ckeditor==6.6.1 django-ckeditor==6.2.0
django-debug-toolbar==4.1.0 django-debug-toolbar==3.2.4
django-login-required-middleware==0.9.0 django-login-required-middleware==0.6.1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
/*! @name @videojs/http-streaming @version 2.14.2 @license Apache-2.0 */
/*! @name aes-decrypter @version 3.1.3 @license Apache-2.0 */
/*! @name pkcs7 @version 1.0.4 @license Apache-2.0 */
/**
* @license
* Video.js 7.20.2 <http://videojs.com/>
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
* Available under Apache License Version 2.0
* <https://github.com/videojs/video.js/blob/main/LICENSE>
*
* Includes vtt.js <https://github.com/mozilla/vtt.js>
* Available under Apache License Version 2.0
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
*/

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
/*! @name @videojs/http-streaming @version 2.14.2 @license Apache-2.0 */
/*! @name aes-decrypter @version 3.1.3 @license Apache-2.0 */
/*! @name pkcs7 @version 1.0.4 @license Apache-2.0 */
/**
* @license
* Video.js 7.20.2 <http://videojs.com/>
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
* Available under Apache License Version 2.0
* <https://github.com/videojs/video.js/blob/main/LICENSE>
*
* Includes vtt.js <https://github.com/mozilla/vtt.js>
* Available under Apache License Version 2.0
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
*/

File diff suppressed because one or more lines are too long

View file

@ -1,29 +0,0 @@
/*!
* Programatically add the following
*/
/*! @name aes-decrypter @version 3.1.3 @license Apache-2.0 */
/*! @name m3u8-parser @version 4.7.1 @license Apache-2.0 */
/*! @name pkcs7 @version 1.0.4 @license Apache-2.0 */
/**
* @license
* slighly modified parse-headers 2.0.2 <https://github.com/kesla/parse-headers/>
* Copyright (c) 2014 David Björklund
* Available under the MIT license
* <https://github.com/kesla/parse-headers/blob/master/LICENCE>
*/
/**
* @license
* Video.js 7.20.2 <http://videojs.com/>
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
* Available under Apache License Version 2.0
* <https://github.com/videojs/video.js/blob/main/LICENSE>
*
* Includes vtt.js <https://github.com/mozilla/vtt.js>
* Available under Apache License Version 2.0
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
*/

File diff suppressed because one or more lines are too long

View file

@ -1,25 +0,0 @@
/*! @name aes-decrypter @version 3.1.3 @license Apache-2.0 */
/*! @name m3u8-parser @version 4.7.1 @license Apache-2.0 */
/*! @name pkcs7 @version 1.0.4 @license Apache-2.0 */
/**
* @license
* slighly modified parse-headers 2.0.2 <https://github.com/kesla/parse-headers/>
* Copyright (c) 2014 David Björklund
* Available under the MIT license
* <https://github.com/kesla/parse-headers/blob/master/LICENCE>
*/
/**
* @license
* Video.js 7.20.2 <http://videojs.com/>
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
* Available under Apache License Version 2.0
* <https://github.com/videojs/video.js/blob/main/LICENSE>
*
* Includes vtt.js <https://github.com/mozilla/vtt.js>
* Available under Apache License Version 2.0
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,20 +1 @@
<div id="app-header"></div> <div id="app-header"></div>
{% comment %}
Uncomment, or replace with your own cookie consent code.
<script src="https://cdn.websitepolicies.io/lib/cookieconsent/cookieconsent.min.js" defer></script>
<script>
window.addEventListener("load", function () {
window.wpcc.init({
border: "normal",
corners: "normal",
colors: { popup: { background: "#222222", text: "#ffffff", border: "#FF8000" }, button: { background: "#FF8000", text: "#000000" } },
position: "top-right",
content: { message: "Hi there, we are using cookies on this website. We don't use tracking or analytics here, just the essentials for the Website to work.\n", button: "Understood! Yum!", link: "Click here to learn more." },
});
});
</script>
{% endcomment %}

View file

@ -1,3 +1,2 @@
User-Agent: * User-Agent: *
Allow: / Allow: /
Sitemap: {{ FRONTEND_HOST }}/sitemap.xml

View file

@ -39,9 +39,9 @@
{% block aftercontent %}{% endblock %} {% block aftercontent %}{% endblock %}
{% if LOAD_FROM_CDN %} {% if LOAD_FROM_CDN %}
<script src="https://vjs.zencdn.net/7.20.2/video.min.js"></script> <script src="https://vjs.zencdn.net/8.2.0/video.min.js"></script>
{% else %} {% else %}
<script src="{% static "lib/video-js/7.20.2/video.min.js" %}"></script> <script src="{% static "lib/video-js/8.2.0/video.min.js" %}"></script>
{% endif %} {% endif %}
{% block externalscripts %}{% endblock externalscripts %} {% block externalscripts %}{% endblock externalscripts %}

View file

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% load static %}
<url>
<loc>{{ FRONTEND_HOST }}</loc>
<changefreq>always</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/featured</loc>
<changefreq>daily</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/recommended</loc>
<changefreq>always</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/latest</loc>
<changefreq>hourly</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/members</loc>
<changefreq>daily</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/tags</loc>
<changefreq>daily</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/categories</loc>
<changefreq>weekly</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/history</loc>
<changefreq>always</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/liked</loc>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/about</loc>
<changefreq>monthly</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/tos</loc>
<changefreq>monthly</changefreq>
</url>
<url>
<loc>{{ FRONTEND_HOST }}/contact</loc>
<changefreq>never</changefreq>
</url>
{% for media_object in media %}
<url>
<loc>{{ FRONTEND_HOST}}/view?m={{ media_object.friendly_token }}</loc>
</url>
{% endfor %}
{% for playlist_object in playlists %}
<url>
<loc>{{ FRONTEND_HOST}}/playlists/{{ playlist_object.friendly_token }}</loc>
</url>
{% endfor %}
{% for user_object in users %}
<url>
<loc>{{ FRONTEND_HOST}}/user/{{ user_object.username }}/</loc>
</url>
{% endfor %}
</urlset>

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.urls import re_path from django.conf.urls import re_path
from . import views from . import views

View file

@ -1,4 +1,5 @@
from django.urls import path, re_path from django.conf.urls import re_path
from django.urls import path
from . import views from . import views

View file

@ -1 +1 @@
VERSION = "3.0.0" 2.0