mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-21 13:57:57 -05:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdff0811a1 | ||
|
|
92c0ff579a | ||
|
|
847cff2b5c | ||
|
|
e8d3ff25be | ||
|
|
15d217453b | ||
|
|
029665145e | ||
|
|
487e098b96 | ||
|
|
fe7427a1f2 | ||
|
|
4bf41fe80e |
@@ -1,4 +1,4 @@
|
||||
FROM python:3.8-buster AS compile-image
|
||||
FROM python:3.11.4-bookworm AS compile-image
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
@@ -24,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
|
||||
|
||||
############ RUNTIME IMAGE ############
|
||||
FROM python:3.8-slim-buster as runtime-image
|
||||
FROM python:3.11.4-bookworm as runtime-image
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mediacms/mediacms:latest
|
||||
FROM python:3.11.4-bookworm AS compile-image
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
@@ -7,10 +7,65 @@ ENV VIRTUAL_ENV=/home/mediacms.io
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
ENV PIP_NO_CACHE_DIR=1
|
||||
|
||||
RUN cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV
|
||||
RUN mkdir -p /home/mediacms.io/mediacms/{logs} && cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV
|
||||
|
||||
# Install dependencies:
|
||||
COPY requirements.txt .
|
||||
COPY requirements-dev.txt .
|
||||
RUN pip install -r requirements-dev.txt
|
||||
|
||||
|
||||
COPY . /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"]
|
||||
|
||||
10
HISTORY.md
10
HISTORY.md
@@ -1,5 +1,15 @@
|
||||
# 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
|
||||
|
||||
14
README.md
14
README.md
@@ -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.
|
||||
|
||||
@@ -56,15 +56,15 @@ A demo is available at https://demo.mediacms.io
|
||||
|
||||
## 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
|
||||
|
||||
MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt).
|
||||
Copyright Markos Gogoulos and Yiannis Stergiou
|
||||
MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt).
|
||||
Copyright Markos Gogoulos.
|
||||
|
||||
|
||||
## Support and paid services
|
||||
@@ -75,7 +75,7 @@ We provide custom installations, development of extra functionality, migration f
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -127,7 +127,7 @@ If you like the project, here's a few things you can do
|
||||
- Share on social media about the project
|
||||
- Open issues, participate on discussions, report bugs, suggest ideas
|
||||
- Star the project
|
||||
- Add functionality, work on a PR, fix an issue!
|
||||
- Add functionality, work on a PR, fix an issue!
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
@@ -59,7 +59,7 @@ def login():
|
||||
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:")
|
||||
else:
|
||||
print(f'Error: {"non_field_errors":["User not found."]}')
|
||||
print(f'Error: {"non_field_errors": ["User not found."]}')
|
||||
|
||||
|
||||
@apis.command()
|
||||
@@ -73,7 +73,7 @@ def upload_media():
|
||||
if os.path.isdir(path):
|
||||
for filename in os.listdir(path):
|
||||
files = {}
|
||||
abs = os.path.abspath("{path}/{filename}")
|
||||
abs = os.path.abspath(f"{path}/{filename}")
|
||||
files['media_file'] = open(f'{abs}', 'rb')
|
||||
response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files)
|
||||
if response.status_code == 201:
|
||||
|
||||
@@ -93,6 +93,9 @@ ALLOW_MENTION_IN_COMMENTS = False # allowing to mention other users with @ in t
|
||||
# valid options: content, author
|
||||
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_L10N = True
|
||||
USE_TZ = True
|
||||
@@ -467,7 +470,7 @@ except ImportError:
|
||||
|
||||
if "http" not in FRONTEND_HOST:
|
||||
# FRONTEND_HOST needs a http:// preffix
|
||||
FRONTEND_HOST = f"http://{FRONTEND_HOST}"
|
||||
FRONTEND_HOST = f"http://{FRONTEND_HOST}" # noqa
|
||||
|
||||
if LOCAL_INSTALL:
|
||||
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
|
||||
@@ -485,3 +488,8 @@ if GLOBAL_LOGIN_REQUIRED:
|
||||
r'/accounts/confirm-email/.*/$',
|
||||
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'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import debug_toolbar
|
||||
from django.conf.urls import include, re_path
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path, re_path
|
||||
from django.views.generic.base import TemplateView
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
|
||||
@@ -8,15 +8,13 @@ User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
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_LOG_FILE="/home/mediacms.io/mediacms/logs/beat%N.log"
|
||||
Environment=CELERYD_LOG_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
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}'
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms beat --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL}'
|
||||
ExecStop=/bin/kill -s TERM $MAINPID
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -8,23 +8,21 @@ User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
Environment=CELERYD_NODES="long1"
|
||||
Environment=CELERY_QUEUE="long_tasks"
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
Environment=CELERY_APP="cms"
|
||||
Environment=CELERYD_MULTI="multi"
|
||||
Environment=CELERYD_OPTS="-Ofair --prefetch-multiplier=1"
|
||||
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_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
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}'
|
||||
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}'
|
||||
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
|
||||
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}'
|
||||
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}'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -8,14 +8,13 @@ User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
Environment=CELERYD_NODES="short1 short2"
|
||||
Environment=CELERY_QUEUE="short_tasks"
|
||||
# Absolute or relative path to the 'celery' command:
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
# App instance to use
|
||||
# comment out this line if you don't use an app
|
||||
Environment=CELERY_APP="cms"
|
||||
# or fully qualified:
|
||||
#CELERY_APP="proj.tasks:app"
|
||||
# How to call manage.py
|
||||
@@ -28,13 +27,12 @@ Environment=CELERYD_OPTS="--soft-time-limit=300 -c10"
|
||||
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_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
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}'
|
||||
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}'
|
||||
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
|
||||
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}'
|
||||
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}'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
db:
|
||||
condition: service_healthy
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -40,8 +40,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -68,7 +68,7 @@ services:
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data/:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -76,8 +76,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -70,7 +70,7 @@ services:
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data/:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -78,8 +78,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -90,7 +90,7 @@ services:
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -98,8 +98,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
@@ -66,7 +66,7 @@ services:
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -74,8 +74,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
@@ -62,7 +62,7 @@ services:
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
@@ -70,8 +70,9 @@ services:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mediacms"]
|
||||
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
- [15. Debugging email issues](#15-debugging-email-issues)
|
||||
- [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
|
||||
This page is created for MediaCMS administrators that are responsible for setting up the software, maintaining it and making modifications.
|
||||
|
||||
## 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 18 or 20 versions.
|
||||
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).
|
||||
|
||||
Installation on a Ubuntu 18 or 20 system with git utility installed should be completed in a few minutes with the following steps.
|
||||
Installation on an Ubuntu 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.
|
||||
|
||||
Automated script - tested on Ubuntu 18, Ubuntu 20, and Debian Buster
|
||||
Automated script - tested on Ubuntu 20, Ubuntu 22 and Debian Buster
|
||||
|
||||
```bash
|
||||
mkdir /home/mediacms.io && cd /home/mediacms.io/
|
||||
@@ -49,10 +49,25 @@ If you've used the above way to install MediaCMS, update with the following:
|
||||
cd /home/mediacms.io/mediacms # enter mediacms directory
|
||||
source /home/mediacms.io/bin/activate # use virtualenv
|
||||
git pull # update code
|
||||
pip install -r requirements.txt -U # run pip install to update
|
||||
python manage.py migrate # run Django migrations
|
||||
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
|
||||
Checkout the configuration section here.
|
||||
|
||||
@@ -66,7 +81,7 @@ Database can be backed up with pg_dump and media_files on /home/mediacms.io/medi
|
||||
## Installation
|
||||
Install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
|
||||
|
||||
For Ubuntu 18/20 systems this is:
|
||||
For Ubuntu 20/22 systems this is:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
@@ -112,6 +127,18 @@ docker-compose down
|
||||
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
|
||||
Checkout the configuration docs here.
|
||||
|
||||
@@ -443,6 +470,14 @@ 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.
|
||||
- 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
|
||||
to be written
|
||||
|
||||
@@ -735,3 +770,12 @@ this will re-create the sprites for videos that the task failed.
|
||||
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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
@@ -538,8 +538,8 @@ def get_base_ffmpeg_command(
|
||||
|
||||
target_width = round(target_height * 16 / 9)
|
||||
scale_filter_opts = [
|
||||
f"if(lt(iw\\,ih)\\,{target_height}\\,{target_width})",
|
||||
f"if(lt(iw\\,ih)\\,{target_width}\\,{target_height})",
|
||||
f"if(lt(iw\\,ih)\\,{target_height}\\,{target_width})", # noqa
|
||||
f"if(lt(iw\\,ih)\\,{target_width}\\,{target_height})", # noqa
|
||||
"force_original_aspect_ratio=decrease",
|
||||
"force_divisible_by=2",
|
||||
"flags=lanczos",
|
||||
|
||||
@@ -427,12 +427,16 @@ class Media(models.Model):
|
||||
Performs all related tasks, as check for media type,
|
||||
video duration, encode
|
||||
"""
|
||||
|
||||
self.set_media_type()
|
||||
if self.media_type == "video":
|
||||
self.set_thumbnail(force=True)
|
||||
self.produce_sprite_from_video()
|
||||
self.encode()
|
||||
if settings.DO_NOT_TRANSCODE_VIDEO:
|
||||
self.encoding_status = "success"
|
||||
self.save()
|
||||
self.produce_sprite_from_video()
|
||||
else:
|
||||
self.produce_sprite_from_video()
|
||||
self.encode()
|
||||
elif self.media_type == "image":
|
||||
self.set_thumbnail(force=True)
|
||||
return True
|
||||
@@ -477,7 +481,10 @@ class Media(models.Model):
|
||||
self.duration = int(round(float(ret.get("video_duration", 0))))
|
||||
self.video_height = int(ret.get("video_height"))
|
||||
if ret.get("video_info", {}).get("codec_name", {}) in ["mjpeg"]:
|
||||
audio_file_with_thumb = True
|
||||
# best guess that this is an audio file with a thumbnail
|
||||
# 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:
|
||||
self.media_type = "audio"
|
||||
@@ -665,6 +672,13 @@ class Media(models.Model):
|
||||
return ret
|
||||
for key in ENCODE_RESOLUTIONS_KEYS:
|
||||
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):
|
||||
if encoding.profile.extension == "gif":
|
||||
continue
|
||||
@@ -816,6 +830,7 @@ class Media(models.Model):
|
||||
"""
|
||||
|
||||
res = {}
|
||||
valid_resolutions = [240, 360, 480, 720, 1080, 1440, 2160]
|
||||
if self.hls_file:
|
||||
if os.path.exists(self.hls_file):
|
||||
hls_file = self.hls_file
|
||||
@@ -827,11 +842,20 @@ class Media(models.Model):
|
||||
uri = os.path.join(p, iframe_playlist.uri)
|
||||
if os.path.exists(uri):
|
||||
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)
|
||||
for playlist in m3u8_obj.playlists:
|
||||
uri = os.path.join(p, playlist.uri)
|
||||
if os.path.exists(uri):
|
||||
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)
|
||||
return res
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ import tempfile
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from celery import Task
|
||||
from celery.decorators import task
|
||||
from celery import shared_task as task
|
||||
from celery.exceptions import SoftTimeLimitExceeded
|
||||
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 django.conf import settings
|
||||
from django.core.cache import cache
|
||||
@@ -460,10 +461,11 @@ def check_running_states():
|
||||
if (now - encoding.update_date).seconds > settings.RUNNING_STATE_STALE:
|
||||
media = encoding.media
|
||||
profile = encoding.profile
|
||||
task_id = encoding.task_id
|
||||
# task_id = encoding.task_id
|
||||
# terminate task
|
||||
if task_id:
|
||||
revoke(task_id, terminate=True)
|
||||
# TODO: not imported
|
||||
# if task_id:
|
||||
# revoke(task_id, terminate=True)
|
||||
encoding.delete()
|
||||
media.encode(profiles=[profile])
|
||||
# TODO: allign with new code + chunksize...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, re_path
|
||||
from django.conf.urls import include
|
||||
from django.conf.urls.static import static
|
||||
from django.urls import path
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import management_views, views
|
||||
from .feeds import IndexRSSFeed, SearchRSSFeed
|
||||
@@ -89,3 +89,6 @@ urlpatterns = [
|
||||
re_path(r"^manage/media$", views.manage_media, name="manage_media"),
|
||||
re_path(r"^manage/users$", views.manage_users, name="manage_users"),
|
||||
] + 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"))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from celery.task.control import revoke
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@@ -293,6 +292,16 @@ def search(request):
|
||||
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):
|
||||
"""List tags view"""
|
||||
|
||||
@@ -1396,5 +1405,6 @@ class TaskDetail(APIView):
|
||||
permission_classes = (permissions.IsAdminUser,)
|
||||
|
||||
def delete(self, request, uid, format=None):
|
||||
revoke(uid, terminate=True)
|
||||
# This is not imported!
|
||||
# revoke(uid, terminate=True)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# should be run as root and only on Ubuntu 18/20, Debian 10/11 (Buster/Bullseye) versions!
|
||||
# should be run as root and only on Ubuntu 20/22, Debian 10/11 (Buster/Bullseye) versions!
|
||||
echo "Welcome to the MediacMS installation!";
|
||||
|
||||
if [ `id -u` -ne 0 ]
|
||||
@@ -22,11 +22,11 @@ done
|
||||
|
||||
|
||||
osVersion=$(lsb_release -d)
|
||||
if [[ $osVersion == *"Ubuntu 20"* ]] || [[ $osVersion == *"Ubuntu 18"* ]] || [[ $osVersion == *"buster"* ]] || [[ $osVersion == *"bullseye"* ]]; then
|
||||
if [[ $osVersion == *"Ubuntu 20"* ]] || [[ $osVersion == *"Ubuntu 22"* ]] || [[ $osVersion == *"buster"* ]] || [[ $osVersion == *"bullseye"* ]]; then
|
||||
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
|
||||
else
|
||||
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"
|
||||
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"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
Django==3.1.12
|
||||
djangorestframework==3.12.2
|
||||
django-allauth==0.44.0
|
||||
psycopg2-binary==2.8.6
|
||||
uwsgi==2.0.19.1
|
||||
django-redis==4.12.1
|
||||
celery==4.4.7
|
||||
drf-yasg==1.20.0
|
||||
Pillow==8.2.0
|
||||
Django==4.2.2
|
||||
djangorestframework==3.14.0
|
||||
django-allauth==0.54.0
|
||||
psycopg==3.1.9
|
||||
uwsgi==2.0.21
|
||||
django-redis==5.3.0
|
||||
celery==5.3.1
|
||||
drf-yasg==1.21.6
|
||||
Pillow==9.5.0
|
||||
django-imagekit==4.1.0
|
||||
markdown==3.3.6
|
||||
django-filter==21.1
|
||||
filetype==1.0.10
|
||||
django-mptt==0.13.4
|
||||
markdown==3.4.3
|
||||
django-filter==23.2
|
||||
filetype==1.2.0
|
||||
django-mptt==0.14.0
|
||||
django-crispy-forms==1.13.0
|
||||
requests==2.25.0
|
||||
requests==2.31.0
|
||||
django-celery-email==3.0.0
|
||||
m3u8==1.0.0
|
||||
django-ckeditor==6.2.0
|
||||
django-debug-toolbar==3.2.4
|
||||
django-login-required-middleware==0.6.1
|
||||
m3u8==3.5.0
|
||||
django-ckeditor==6.6.1
|
||||
django-debug-toolbar==4.1.0
|
||||
django-login-required-middleware==0.9.0
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
Sitemap: {{ FRONTEND_HOST }}/sitemap.xml
|
||||
|
||||
66
templates/sitemap.xml
Normal file
66
templates/sitemap.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?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>
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.conf.urls import re_path
|
||||
from django.urls import re_path
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.conf.urls import re_path
|
||||
from django.urls import path
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION = "2.0.0"
|
||||
VERSION = "3.0.0"
|
||||
|
||||
Reference in New Issue
Block a user