Compare commits

...

251 Commits
v1.1.1 ... main

Author SHA1 Message Date
Yiannis Christodoulou
2a0cb977f2
Video.js fixes and improvements after major upgrade (#1413) 2025-10-27 11:53:35 +02:00
Yiannis Christodoulou
a5e6e7b9ca
feat: Major Upgrade to Video.js v8 — Chapters Functionality, Fixes and Improvements 2025-10-20 15:30:00 +03:00
Markos Gogoulos
b39072c8ae feat: disapprove user 2025-10-09 17:29:29 +03:00
Markos Gogoulos
f4ab60e894 fix: extend gitignore 2025-09-26 11:33:44 +03:00
Markos Gogoulos
8656b40c5b fix: url 2025-09-25 08:51:26 +03:00
Markos Gogoulos
553a25a86f
feat: pass extra css (#1392) 2025-09-24 19:01:06 +03:00
Markos Gogoulos
1c1af489f1
feat: allow portal logo override (#1391) 2025-09-24 16:26:45 +03:00
Markos Gogoulos
c4c5ecf06a update version 2025-09-21 16:18:31 +03:00
Markos Gogoulos
725cc71960 fix migration 2025-09-21 16:17:18 +03:00
Markos Gogoulos
0c1c5bbb09 add new migration 2025-09-21 16:15:46 +03:00
Markos Gogoulos
56182f0a6d feat: allow customizable about page 2025-09-21 15:47:04 +03:00
Markos Gogoulos
208f0b338b
feat: update versions for python packages, add Pages functionality (#1386)
This PR updates Django core version and also brings html pages support (that admins can create)
2025-09-21 15:38:43 +03:00
Markos Gogoulos
cbef629baf
feat: approve users, edit users through manage users page (#1383) 2025-09-20 15:16:52 +03:00
Bret.S (AKA: CyberGladius)
8e8454d8c2
Docs Update with SAML Deployement Guide and Troubleshooting (#1377)
* Docs update with SAML deployment guide.

* Docs update with SAML deployment guide. URL Fix

---------

Co-authored-by: root <git@tdcme.com>
2025-09-16 14:51:05 +03:00
Markos Gogoulos
8d982ace92
Feat whisper opts (#1368) 2025-09-04 13:39:41 +03:00
Meet Dholakia
6cee02085c
Updated the title splicing length to 100 in playlist, category and media models (#1364) 2025-09-02 19:43:46 +03:00
Markos Gogoulos
e33aa17911 fix version 2025-09-02 11:32:02 +03:00
Markos Gogoulos
a8db23f204 docs: add whisper section 2025-09-02 11:00:17 +03:00
Markos Gogoulos
d6428e3334 version bump 2025-09-01 20:34:15 +03:00
Markos Gogoulos
fd342fd1d6 version bump 2025-09-01 20:32:11 +03:00
Markos Gogoulos
7a1b32f1ba
fix: delete media (#1366) 2025-09-01 20:06:49 +03:00
Markos Gogoulos
817e16ac60
feat: whisper STT and record screen (#1363) 2025-09-01 15:11:38 +03:00
Markos Gogoulos
8cbeb72dd2
feat: add fily type and max user media uplod limits 2025-08-19 11:35:49 +03:00
Markos Gogoulos
e9f862a0ff
feat: 3 small fixes (#1347)
* fix: datetime input

* show message on upload user only

* show all media of user for editors/managers/admins
2025-08-17 19:18:47 +03:00
Markos Gogoulos
02eac68b51
fix: swagger (#1345)
* fix: swagger

* fix: swagger
2025-08-12 12:24:26 +03:00
Markos Gogoulos
e790795bfd
feat: bulk actions API 2025-08-07 13:21:12 +03:00
Markos Gogoulos
de99d84c18 feat: revert ffmpeg install 2025-07-07 13:33:45 +03:00
Markos Gogoulos
8aa89c0958 feat: use apt for installing ffmpeg 2025-07-07 13:00:33 +03:00
Markos Gogoulos
df98b65704
feat: pass version on static files (#1318) 2025-07-07 11:54:02 +03:00
Markos Gogoulos
a607996bfa
feat: adds minimum resolution of 144p 2025-07-07 11:34:02 +03:00
Markos Gogoulos
79f2e2bb11
feat: replace format with fstrings 2025-07-07 11:26:08 +03:00
Markos Gogoulos
d54732040a
feat: add DB connection pooling 2025-07-07 11:18:40 +03:00
Andy
e8520bc7cd
fix: date picker in edit media (#1297)
By default, Django uses type=text for the date input, which respects
the locale settings. However, when changing the input type to date,
it should only accept YYYY-MM-DD format so the input field can be
properly handled.
2025-07-06 11:44:44 +03:00
Markos Gogoulos
b6e46e7b62
feat: replace login middleware (#1314) 2025-07-06 11:25:50 +03:00
Adam Stradovnik
36eab954bd
feat: Adds support for Slovenian frontend translations (#1306) 2025-07-06 11:05:07 +03:00
Markos Gogoulos
610716533b fix formatting 2025-07-01 15:46:34 +03:00
Yiannis Christodoulou
4f1c4a2b4c
fix: Disable Segment Tools and Reset Preview State During Playback (#1305)
* fix: Disable Segment Tools and Reset Preview State During Playback

* chore: remove some unnecessary comments

* chore: build assets

* fix: do not display the handles (left/right) on preview mode

* fix: Disable all tools on preview mode (undo, redo, reset, etc.)

* Update README.md

* feat: Prettier configuration for video editor

* Update README.md

* Update .prettierrc

* style: Format entire codebase (video-editor) with Prettier

* fix: During segments playback mode, disable button interactions but keep hover working

* feat: Add yarn format

* prettier format

* Update package.json

* feat: Install prettier and improve formatting

* build assets

* Update version.py 6.2.0
2025-07-01 15:33:39 +03:00
Markos Gogoulos
83f3eec940
feat: enable editing of media slug, show categories on manage media 2025-06-24 11:13:33 +03:00
Markos Gogoulos
a5acce4ab1
fix: single server install issues 2025-06-15 11:19:29 +03:00
Ofek
a4e9309350
(FEAT) Add Hebrew Translation (#1295) 2025-06-14 11:08:17 +03:00
Casper Tollund
6beaf0bbe2
Add support for Danish translation (#1293)
Co-authored-by: Casper Tollund <casto@mac.ait.clients.local>
2025-06-12 17:35:37 +03:00
Andy
70168299ba
Adds support for Traditional Chinese translation (#1282)
* Adds support for Traditional Chinese translation

* Reformats the style to match black formatter
2025-06-11 15:10:49 +03:00
Markos Gogoulos
b28c2d8271
feat: Video Trimmer and more 2025-06-11 14:48:30 +03:00
angy91m
d34fc328bf
feat: IT traslation added (#1267)
* Create it.py

* feat: IT traslation added
2025-05-15 13:30:07 +03:00
Markos Gogoulos
ab4d9d67df
fix: issue with import (#1245) 2025-04-07 18:36:38 +03:00
Markos Gogoulos
f7a2f049bd
Update README.md (#1244) 2025-04-05 12:52:29 +03:00
Markos Gogoulos
05414f66c7
feat: RBAC + SAML support 2025-04-05 12:44:21 +03:00
Markos Gogoulos
8fecccce1c feat: move docker compose files 2025-03-18 19:21:39 +02:00
Markos Gogoulos
2a7123ca0b
feat: move docker compose files in dir (#1231) 2025-03-18 19:05:04 +02:00
Yiannis Christodoulou
20f305e69e
feat: Frontend Dependencies Upgrade +Fix Timestamps in videos 2025-03-18 19:01:50 +02:00
Markos Gogoulos
d1fda05fdc fix: flake8 2025-03-09 20:50:07 +02:00
Markos Gogoulos
a02e0a8a66 fix: flake8 2025-03-09 20:48:09 +02:00
Markos Gogoulos
21f76dbb6e
feat: playlist optimizations (#1216) 2025-03-09 20:44:04 +02:00
Markos Gogoulos
50e9f3103f
feat: better support for subtitles (#1215) 2025-03-09 20:29:26 +02:00
Markos Gogoulos
0b9a203123 revert head changes 2025-02-13 20:31:19 +02:00
Sven-Thorsten Dietrich
5cbd815496
fix: Fix Docker WARN: FromAsCasing (#1196)
Fixes: L27 'as' and 'FROM' keywords' casing do not match

Signed-off-by: Sven-Thorsten Dietrich <thebigcorporation@gmail.com>
2025-02-13 13:57:14 +02:00
Markos Gogoulos
3a8cacc847
feat: Bulk fixes (#1195)
remove ckeditor - not in use
add more strict default password validators
set Django admin as configurable URL
add nginx HSTS and CSP headers
enable moving from private to unlisted in the PORTAL_WORKFLOW private
on default comments listing, show only comments for public media
in case of a private media, dont expose any unneeded metadata
2025-02-13 13:41:53 +02:00
Markos Gogoulos
5402ee7bc5
fix: crispy forms (#1194) 2025-02-12 14:27:27 +02:00
Markos Gogoulos
a6a2b50c8d remove redundant message 2025-02-10 22:25:24 +02:00
Markos Gogoulos
23e48a8bb7 remove redundant message 2025-02-10 22:24:42 +02:00
Markos Gogoulos
313cd9cbc6 fix issue with static files in dev 2025-02-10 22:04:14 +02:00
Markos Gogoulos
0392dbe1ed
feat: lib updates and more (#1187)
This PR performs the following changes:

- update python in Dockerfile
- updates all python libraries (including Django)
- makes md5sum calculation redundant by default
- updates Postgresql used in Docker. For new installations this is ok. For existing ones this is backwards incompatible, and restore through pg_dump+pg_restore has to be performed in order to utilize Postgres 17 (since its not compatible with previous versions and will complain)
- fixes issues with HLS, and adds test to ensure they won't happen again
- allows . and @ in usernames
2025-02-10 11:34:00 +02:00
Markos Gogoulos
a7562c244e
fix: black (#1164) 2025-01-22 20:16:21 +02:00
Sonu Kumar
d2ee12087c
feat: Re-Arranged Subtitles by first letter of language (#863) 2025-01-22 19:25:47 +02:00
aleensd
6db01932e1
feat: fullscreen images slideshow (#1120) 2025-01-22 19:02:01 +02:00
Markos Gogoulos
53d8215346
feat: comment URL (#1163) 2025-01-22 14:55:43 +02:00
David
1b960b28f8
feat: Update insert login_required middleware to allow auth middleware (#1082) 2025-01-22 14:54:25 +02:00
Markos Gogoulos
02d9188aa1
feat: increase task limit (#1161) 2025-01-22 11:22:07 +02:00
Meng Sen
8d9a4618f0
feat: support for reading environment variables on Docker 2024-11-22 18:35:47 +02:00
aleensd
cf93a77802
feat: integrate react-pdf-viewer for PDF display (#1112) 2024-11-21 12:15:25 +02:00
Markos Gogoulos
5a1e4f25ed fix: issue with create_hls 2024-11-20 15:04:27 +02:00
Markos Gogoulos
9fc7597e73 fix: GH action 2024-11-20 13:47:29 +02:00
Markos Gogoulos
9b3e0250d4 fix: GH action 2024-11-20 13:36:58 +02:00
Markos Gogoulos
1384471745 fix: flake8 2024-11-20 13:29:37 +02:00
Markos Gogoulos
29b362c8ce fix: flake8 2024-11-20 13:27:57 +02:00
Markos Gogoulos
b8ee2e9fb8 fix: pre-commit 2024-11-20 13:17:25 +02:00
Markos Gogoulos
99be0f07dd feat: edit slideshow_url results 2024-11-18 15:48:02 +02:00
Markos Gogoulos
27d1660192
feat: provide slideshow media for images (#1108) 2024-11-14 12:22:23 +02:00
Nathan Hyde
98adb22205
Fixes mediacms-io/mediacms#1046 - submition => submission (#1094) 2024-10-30 19:49:26 +02:00
yatesdr
673ddeb5bd
feat: Set option for allowing only certain domains to register (#1086) 2024-10-19 14:51:20 +03:00
Markos Gogoulos
aa8a2d92dc
Feat/check input (#1089)
* docs: instructions to set frames per seconds on sprites

* feat: add more validation

* remove reduntant line
2024-10-19 14:17:19 +03:00
Kaiwalya Koparkar
6bbd4c2809
feat: Added Elestio as one-click deploy option (#1055) 2024-10-08 10:44:44 +03:00
Markos Gogoulos
c4148bd504 feat: semantic release 2024-10-07 09:10:21 +03:00
Markos Gogoulos
ea8b2af26f fix: remove duplicate setting 2024-10-04 16:40:53 +03:00
Markos Gogoulos
5aa899cef0
Feat translations improvements v1 (#1076) 2024-10-04 13:39:28 +03:00
Markos Gogoulos
4992cc425c
feat: translations support 2024-10-04 13:17:40 +03:00
Tudorel Oprisan
ef4067cbdd
fix: replaced pipe with empty string on helper function 2024-10-02 19:16:18 +03:00
Markos Gogoulos
8cc3513a8a docs 2024-10-02 15:57:19 +03:00
Kyle Maas
90e593946d
feat: allow commenting by regular users when posting media requires advanced permissions (#1023) 2024-10-02 15:52:30 +03:00
Markos Gogoulos
f7136e2a11 isort 2024-10-02 12:54:47 +03:00
Markos Gogoulos
0151e834a1 black formatting 2024-10-02 12:53:48 +03:00
Markos Gogoulos
5fe4d3a9fc
feat: rounded corners 2024-10-02 11:33:17 +03:00
Markos Gogoulos
94c646fdb8 update metadata only, on API call 2024-09-20 19:26:13 +03:00
Markos Gogoulos
d665058b80 speed up docker start 2024-09-20 13:02:00 +03:00
Markos Gogoulos
986c7d1074
add validation to files uploading to avoid client side pushing arbitrary data (#1057) 2024-09-20 13:01:33 +03:00
thau0x01
1adee8c156
fix #943 (#1052) 2024-09-20 12:53:56 +03:00
makerduck
ffd7a52863
Fix postgres role output (#1029) 2024-09-20 12:52:50 +03:00
Kyle Maas
c5047d8df8
Fix null bug in More Options button (#913) 2023-11-14 09:24:05 +02:00
Markos Gogoulos
dcbfaca91c
Developer Experience (#911)
local dev environment
2023-11-13 11:13:08 +02:00
Kyle Maas
918df010f5
Fix bug that crashes page if an encoding has a null URL (#912) 2023-11-13 11:09:16 +02:00
Markos Gogoulos
e9739bab45
Feat celery run (#860)
* avoid calling post save signals
* remove stale celery ids that prevent new services from starting
2023-11-10 16:06:17 +02:00
Kyle Maas
e7ce9ef5c0
Add admin action to generate missing encodings for a particular Media (#883)
* Add admin action to generate missing encodings for a particular Media
* Only regenerate the encodings that are missing
2023-11-10 15:41:20 +02:00
Kyle Maas
4829adf110
Add useful fields to the Encodings admin screen (#885) 2023-11-10 15:37:40 +02:00
lavirez
fdff0811a1
cli.py missing f string (#877) 2023-11-10 15:09:22 +02:00
Kyle Maas
92c0ff579a
Add sitemap (#572)
add sitemap.xml
2023-11-10 15:03:36 +02:00
Markos Gogoulos
847cff2b5c license section 2023-11-10 14:30:14 +02:00
Markos Gogoulos
e8d3ff25be
Disable encoding and show only original file (#829)
Disable encoding and show only original file #829
2023-11-10 14:25:10 +02:00
Markos Gogoulos
15d217453b
Update admins_docs.md (#842) 2023-07-17 16:47:06 +03:00
Markos Gogoulos
029665145e
Python requirements and Docker version upgrades (#826)
v3.0.0: Python, Django, Celery and other version upgrades
2023-07-03 13:40:39 +03:00
Adi
487e098b96
Upgrade postgres docker compose (#749)
* Upgrade PG to latest stable version alpine
2023-07-03 13:39:15 +03:00
Markos Gogoulos
fe7427a1f2
check resolution for HLS (#832) 2023-07-03 12:19:23 +03:00
Markos Gogoulos
4bf41fe80e
fix issue with AVI not being recognised as videos (#833) 2023-07-03 12:18:24 +03:00
Markos Gogoulos
1fd04ca947 pass secrets to workflow 2023-06-28 15:32:32 +03:00
Markos Gogoulos
a1962d4b32 add secret 2023-06-27 18:22:37 +03:00
Markos Gogoulos
6e9c9ed81f add secret 2023-06-27 18:20:30 +03:00
Markos Gogoulos
51186e3253 add secret 2023-06-27 18:16:01 +03:00
Markos Gogoulos
150967b342 add secret 2023-06-27 18:14:56 +03:00
Markos Gogoulos
bb6244d862 trigger build 2023-06-27 18:07:14 +03:00
Markos Gogoulos
a002422b77 update version for workflow 2023-06-27 17:50:16 +03:00
Markos Gogoulos
24167b9624 CI fix branch 2023-06-27 17:30:51 +03:00
Markos Gogoulos
b9db1a5e2e
Update README.md (#823)
* Update README.md
2023-06-27 17:26:54 +03:00
Markos Gogoulos
296aeac567
Update admins_docs.md 2023-06-27 13:41:58 +03:00
Markos Gogoulos
10c386f886
Update README.md (#822) 2023-06-27 13:02:21 +03:00
Adi
367faaddd1
Add workflow for docker build and push (#750)
* Add workflow for docker build and push
2023-06-26 09:49:37 +03:00
nmlsdev
3d59b87f09
add rhel8 installation script (#792)
* add rhel8 installation script
2023-06-14 15:18:12 +03:00
Markos Gogoulos
5dee41de39 version 2.0.0 2023-06-13 22:45:03 +03:00
Markos Gogoulos
08bba5fc05 remove redundant file 2023-06-13 21:21:27 +03:00
Markos Gogoulos
102414b514
fix issues with comments (#802)
* fix issues with comments
2023-06-13 19:01:52 +03:00
Markos Gogoulos
c866fdd6ba
allow tags to contain chars, not only English alphabet (#801)
* allow tags to contain chars, not only English alphabet
2023-06-13 15:41:13 +03:00
Markos Gogoulos
5b601698a4 increase uwsgi buffer-size para, 2023-06-13 12:44:31 +03:00
Markos Gogoulos
f040f73f51 black formatting 2023-06-12 17:22:39 +03:00
Markos Gogoulos
b7a70d92fa fix typo 2023-06-12 17:13:44 +03:00
mostafa hosseini
2f43cef8da
add api_url field to search api (#692)
Co-authored-by: Mostafa Hosseini <mostafa.h@rahgosahgroup.com>
2023-06-12 16:42:30 +03:00
Markos Gogoulos
ad633e6fdf
simple cookie consent code (#799)
* simple cookie consent code
2023-06-12 16:40:53 +03:00
Stella
cd8d0ea49a
Allow password reset & email verify pages on global login required (#790) 2023-05-31 16:36:31 +03:00
Markos Gogoulos
a3997bfb1c
update versions for pre-commit (#741) 2023-03-14 14:09:52 +02:00
Markos Gogoulos
4b0718c43f version 2.0 2023-03-14 13:30:26 +02:00
Markos Gogoulos
91d8179fa0 improve formating on Readme 2023-03-14 13:15:17 +02:00
Markos Gogoulos
6532b19849
Update README.md 2023-03-14 13:13:55 +02:00
Markos Gogoulos
6ea8fd12a3
fix issue with uninitialized video player 2023-03-14 13:13:15 +02:00
Markos Gogoulos
d971bb955f
pin versions (#718) 2023-02-17 12:46:45 +02:00
Markos Gogoulos
b52b008f89
enable cors for media dir (#701) 2023-02-17 11:51:48 +02:00
MICRUFUN
30cf5d7176
How to modify encode profiles (#682) 2023-01-03 12:33:38 +02:00
masavini
6fd9a7d37f
remove zombie thumbnails (#657)
remove zombie thumbnails
2022-12-05 12:31:31 +02:00
Markos Gogoulos
9c6d13559b update doc 2022-11-29 17:24:13 +02:00
Markos Gogoulos
8ec97a8219 pre-commit yaml 2022-11-29 15:27:15 +02:00
Markos Gogoulos
de8f9ca718 fix pre-commit version 2022-11-29 14:55:00 +02:00
Markos Gogoulos
a4bedca4db fix pre-commit version 2022-11-29 14:53:05 +02:00
Kyle Maas
da565b3bfc
Fix double slashes in URIs (#558) 2022-11-29 11:22:30 +02:00
Kyle Maas
239ff6cb60
Add meta description tag for search engines (#551) 2022-11-29 11:02:16 +02:00
masavini
da840b156d
add api path to LOGIN_REQUIRED_IGNORE_PATHS (#483) 2022-11-29 10:59:36 +02:00
Markos Gogoulos
b08d493823 static files 2022-09-20 12:27:33 +00:00
masavini
25eaa35758
Fix admin user creation (#472)
add ADMIN_USER, ADMIN_PASSWORD and ADMIN_EMAIL
2022-09-20 15:18:25 +03:00
MrPercheul
cba2ed75ed
Show comments in the Timebar (#442)
Show comments in the Timebar
2022-09-20 15:16:16 +03:00
Markos Gogoulos
de5bd07a90
Feat videojs update (#519)
update video.js to 7.20.2
2022-09-07 12:33:11 +03:00
Markos Gogoulos
34c1a4e33e
static files (#506) 2022-08-22 17:40:54 +03:00
Markos Gogoulos
80be0d06e5
correctly identify audio (#505)
* correctly identify audio
2022-08-22 17:35:11 +03:00
masavini
a6322fae6d
missing f-strings (#490) 2022-08-12 15:45:35 +03:00
Markos Gogoulos
3d4dd27220
Update user_docs.md 2022-06-01 14:23:42 +03:00
MrPercheul
f6a78dd0b4
Mention users in comments (#429)
Mention users in comments (#429)
2022-06-01 14:23:21 +03:00
DecaTec
cbc9633fe2
Fix pre-commit (#440) 2022-06-01 13:51:46 +03:00
DecaTec
3e7b106482
Vp9 fixes (#438)
* Fix fo ffprobe appending pipe
* Fix media remains pending when only encoding to webm
2022-06-01 13:42:35 +03:00
DecaTec
0f182c2b15
Fixed duplicate 'pk' on fixtures
Fixed duplicate 'pk' on fixtures
2022-06-01 13:21:22 +03:00
Markos Gogoulos
1ddfae7c95
update black (#452) 2022-05-10 13:46:22 +03:00
Markos Gogoulos
f1969e4637 pin version of debug toolbar 2022-05-09 21:05:25 +03:00
Markos Gogoulos
9e7a7a7482
Update developers_docs.md 2022-04-27 21:53:11 +03:00
Markos Gogoulos
6e478e6e82 fix typo on documentation 2022-03-23 20:48:26 +02:00
Behnam B.Marandi
e06deed3b8
Add configuration for only member access to media (#424)
I added an entry under 'Configuration 'per discussion in the Q&A section:
https://github.com/mediacms-io/mediacms/discussions/422
2022-03-23 20:47:06 +02:00
Markos Gogoulos
f8376c5c58
static files (#432) 2022-03-23 19:04:59 +02:00
MrPercheul
e7ae2833d9
Timestamp (#430)
* Test

Messing around to figure out docker and whatnot

* Revert "Test"

This reverts commit 43b76932c5874a70d3ec86d653961ab8c734e0a0.

* Update docker dev yaml

Update the docker to the environement with the latest version of selenium (and their new arguments), and to be on firefox

* Timestamp on play

Adds the a one time event that jumps to a timestamp if there is a t parameter in the URL

* Timestamp when sharing DRAFT

First draft to show the timestamp when sharing. Missing checkbox and appending the get param to shown link

* Checkbox and update share url

Checkbox show a clean timestamp and the media URL updates with the correct timestamp upon selection

* Cleaning before PR

removing un-necessary modified  files

* Clean up before PR - remove statics

* Timestamp in comments

Parse the comments to wrap timestamps with an appropriate anchor

* Forgotten comments and console.logs

* Last touch for PR

- Cleaning media.js for PR
- Using  MediaPageStore instead of window.location when wrapping the timestamp in comments

* Screenshot

Adding the screenshot for the user_docs

* PR amends

Amending VideoPlayer componnent to take check if the Get param 't' is a number, and to keep it within the duration of the video.
Required to change the listener from 'play' to 'loadedmetadata' to have access to the video duration (otherwise it was too early)

Also changed the User_doc file to inform users of the timestamp function
2022-03-23 17:38:02 +02:00
Markos Gogoulos
fb0f3ee739
dislike button fix (#411) 2022-02-03 18:44:56 +02:00
Markos Gogoulos
c0701de047 reverting image logo 2022-01-31 21:44:43 +02:00
Shubhank Saxena
0d4918a715
CLI API Tool (#273)
CLI tool
2022-01-31 21:12:29 +02:00
Megidd
8093c4ccb5
Fix logo height (#348)
* Set logo height to 100%
2022-01-31 21:07:02 +02:00
Megidd
2dbd97cb22
Fix dislike count (#388) 2022-01-31 21:05:37 +02:00
Markos Gogoulos
6b6662420f
adds instructions to get coverage (#299)
Co-authored-by: Markos Gogoulos <markos@orfium.com>
2022-01-31 20:53:57 +02:00
sthierolf
f1a1e342db
Update install.sh (#303)
Tested with newly release Debian 11 Bullseye, added to OS Version check.
2021-10-14 10:24:07 +03:00
Markos Gogoulos
738247c32b
pin PG version on docker compose files (#300) 2021-10-12 21:07:45 +03:00
Markos Gogoulos
f974d78270
improve validation on text indexing, for search (#294) 2021-10-01 17:49:41 +03:00
multiflexi
28031f07e5
Filters (#289)
* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod

* Deinterlacing and better filter logic
2021-10-01 17:48:52 +03:00
Markos Gogoulos
4480fa7de1
documentation, admin section, plus fix of a typo on models (#293)
* documentation, admin section, plus fix of a typo on models
2021-09-27 15:07:17 +03:00
Markos Gogoulos
32e07035f3
add tests for new uploads (#291)
* add tests for new uploads
2021-09-23 18:34:27 +03:00
Markos Gogoulos
2ce8dba163
Feat fix precommit (#280)
* add version file, fix pre-commit
2021-08-20 14:16:19 +03:00
multiflexi
48838ac406
Framerate fix (#246)
* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod
2021-08-20 14:04:50 +03:00
Markos Gogoulos
062e4be6c2
small typo (#277) 2021-08-20 13:57:55 +03:00
Markos Gogoulos
fb00f94bfa
add more tests (#268)
* add more tests as STUBS
2021-08-17 00:01:52 +03:00
Markos Gogoulos
755df50c41
pin Django 3.1.12 (#260)
* pin Django 3.1.12, this requires to remove absolute paths for upload dirs
2021-08-16 23:46:25 +03:00
Markos Gogoulos
ba94989e6a
Feat urls (#257)
add new URLS, add swaggger doc, add tests
2021-08-05 13:25:25 +03:00
Markos Gogoulos
86cc0442d8
move envs from Dockerfile to docker-compose files (#264) 2021-08-02 22:03:32 +03:00
Markos Gogoulos
2dde4257f7
remove smoke test that breaks (#263) 2021-08-02 19:14:23 +03:00
swiftugandan
3afff52ebf
restore /tmp that was deleted while adding static ffmpeg (#262) 2021-08-02 18:40:12 +03:00
Markos Gogoulos
c27e3caff6
documentation improvements (#261) 2021-08-01 19:47:35 +03:00
Markos Gogoulos
16e2c32d17
adds documentation (#250)
* adds documentation

* updates in user docs

* documentation

Co-authored-by: styiannis <1515939+styiannis@users.noreply.github.com>
2021-08-01 19:31:12 +03:00
Markos Gogoulos
f4f6fa5962
optimize migrations service to not run all actions every time (#229)
* optimize migrations service to not run all actions every time

* set random pass for Docker setup, but also allow for env set
2021-08-01 18:56:38 +03:00
swiftugandan
b9e35c66a3
shutdown migrations container when migrations have completed (#228) 2021-08-01 18:54:13 +03:00
Werner Robitza
360a647eb5
install more recent ffmpeg (#259) 2021-08-01 15:16:13 +03:00
Markos Gogoulos
7237040777
add Releases section on Readme (#248)
* add Releases section on Readme
2021-07-11 18:18:20 +03:00
Yiannis Stergiou
aa6520daac
Frontent dev env (#247)
* Added frontend development files/environment

* More items-categories related removals

* Improvements in pages templates (inc. static pages)

* Improvements in video player

* Added empty home page message + cta

* Updates in media, playlist and management pages

* Improvements in material icons font loading

* Replaced media & playlists links in frontend dev-env

* frontend package version update

* chnaged frontend dev url port

* static files update

* Changed default position of theme switcher

* enabled frontend docker container
2021-07-11 18:01:34 +03:00
Markos Gogoulos
060bb45725
Feat robots.txt (#237)
* add robots.txt

* removes robots.txt manual steps
2021-07-02 17:19:02 +03:00
Alb
1f0cc4ff87
Create robots_and_analytics.md (#234)
Added instructions to add Google Analytics and robots.txt
2021-07-02 17:07:20 +03:00
Shubhank Saxena
c28a39fa47
Segregation of Dev and Prod envs (#218)
Segregation of Dev and Prod envs, addition of tests

Co-authored-by: Markos Gogoulos <mgogoulos@gmail.com>
Co-authored-by: Ubuntu <shubhank@my-hostings.nxfutj5b2tlubjykddwgszqteb.bx.internal.cloudapp.net>
2021-07-01 18:05:43 +03:00
DieProgrammIDE
d17b3b4153
Fix or Playback Issues for iPad + GET-Parameters (autoplay,muted,time) for Embed (#179)
* Remove Loading Video Visibility Bug

* Quality Y-Overflow for Mobile

* Update _commons.js

* Oneliner

* Reset _common.js

* Use OnLoad

* Use Observer

* No Max-Height
2021-06-23 11:25:45 +03:00
DecaTec
950adcdd9d
Webserver/setup optimizations (#220)
* Webserver security

* Create vHost dirs during install; link vHost to sites-enabled

* Remove default vHosts during install

* Only generate new DH params when also using real certificates

* Removed duplicate ssl_ecdh_curve
2021-06-18 16:56:45 +03:00
swiftugandan
235efbe151
keep pids in container scope (#225)
* keep pids in container scope

* increase client_max_body_size through nginx proxy deployments to 5.8G
2021-06-18 15:39:52 +03:00
Markos Gogoulos
8145ba0914 add missing validation for logging 2021-06-18 15:37:09 +03:00
Markos Gogoulos
f74d3c4b57 instructions for Docker setup update 2021-06-18 14:49:36 +03:00
Markos Gogoulos
9b9a718a18
Docker compose docs (#227) 2021-06-18 14:44:58 +03:00
Markos Gogoulos
26804dce40
stub testing (#226)
adds github action for tests
2021-06-17 23:11:14 +03:00
Markos Gogoulos
de30fe68f1 Readme 2021-06-16 20:54:00 +03:00
Markos Gogoulos
4f72c00598 fixes wrong path on Readme 2021-06-16 20:48:42 +03:00
Markos Gogoulos
d3a3934ce7
cleanup docs and add some more regarding Docker installation (#223)
* cleanup docs and add some more regarding Docker installation
2021-06-16 20:46:40 +03:00
Markos Gogoulos
ddbaa51285 pin Django 2021-06-16 20:31:59 +03:00
dependabot[bot]
7fae5992e7
Bump django from 3.1.8 to 3.1.12 (#222)
Bumps [django](https://github.com/django/django) from 3.1.8 to 3.1.12.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.1.8...3.1.12)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-16 19:29:20 +03:00
dependabot[bot]
46384a3c49
Bump pillow from 8.1.1 to 8.2.0 (#221)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 8.1.1 to 8.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/8.1.1...8.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-16 19:29:06 +03:00
swiftugandan
cdcf148b72
adding docker letsencrypt example (#178) 2021-06-16 19:27:33 +03:00
Markos Gogoulos
09e565e99b
add shields (#212)
add shields
2021-06-04 12:26:37 +03:00
Markos Gogoulos
7bda0acd8b
Feat flake8 strength (#209)
* remove warnings from flake
2021-06-03 18:26:53 +03:00
Markos Gogoulos
748d4bae4b
Feat login required (#204)
* global login required option

* add option to require global login
2021-06-02 17:18:06 +03:00
Markos Gogoulos
171b9f84d7
Feat swagger (#203)
Swagger docs
2021-05-31 22:24:05 +03:00
Dan1ell
853b28130d
Short tutorial on how to add a static page to the sidebar (#188)
* Create Add A Static Page To The Sidebar

Short tutorial on adding a static page to the sidebar.
2021-05-31 22:23:37 +03:00
Markos Gogoulos
5602422d29
adds drf-yasg and automated generation of Swagger Schemas (#165)
* adds drf-yasg and automated generation of Swagger Schemas

* swagger url

* swagger docs

* adds swagger url on Readme

* swagger API

* Code of Conduct file

* doc
2021-05-29 16:34:36 +03:00
Markos Gogoulos
110695ae2f
improvements on flake8 (#200) 2021-05-27 17:40:52 +03:00
Markos Gogoulos
6df942ac4e
format content (#198) 2021-05-26 18:35:21 +03:00
Markos Gogoulos
2d49b1df29
adds PR template (#197) 2021-05-26 18:33:26 +03:00
Shubhank Saxena
8f228d6844
Add pre-commit (#140)
* Add pre-commit config files

* Add linting test on github action
2021-05-26 17:58:17 +03:00
Markos Gogoulos
94b26a8781 add Debian Buster info on Readme 2021-05-26 17:53:13 +03:00
sthierolf
9002930994
Update install.sh (#128)
added check for Debian 10 (buster)
2021-05-26 17:50:50 +03:00
Markos Gogoulos
1e6ee280ca adds drf_yasg dependency 2021-05-25 09:26:15 +03:00
dependabot[bot]
cf278211fb
Bump django from 3.1.4 to 3.1.8 (#164)
Bumps [django](https://github.com/django/django) from 3.1.4 to 3.1.8.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.1.4...3.1.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-16 16:54:04 +03:00
Markos Gogoulos
2226b6afbf
add is_stuff to admin (#156) 2021-04-27 21:12:44 +03:00
Markos Gogoulos
adf3d4377f
allow media of all states to be added to playlist (#154) 2021-04-26 16:32:18 +03:00
Dan1ell
be41c6876d
Set default theme in settings (#132)
* Update DEFAULT_THEME variable

* Update site.html to use DEFAULT_THEME variable
2021-04-20 22:05:31 +03:00
Markos Gogoulos
784a18ad27
add users url with ending slash (#129) 2021-04-20 21:53:38 +03:00
Markos Gogoulos
6a5c57f2b2
fix permission for user deletion (#127) 2021-04-20 21:52:09 +03:00
Dan1ell
10f198fff3
Add deploy/docker/local_settings.py to .gitignore (#142) 2021-04-20 21:48:43 +03:00
Markos Gogoulos
6b89d9722b
Update FUNDING.yml 2021-04-07 22:16:29 +03:00
Markos Gogoulos
04f59ffbb8
add FUNDING.yml file 2021-03-23 19:12:48 +02:00
Markos Gogoulos
632db06ca3 command for mp4hls path 2021-03-22 21:35:32 +02:00
swiftugandan
0129ab6732
Fix docker chown recursion performance and bento4 upgrade (#95)
* optimize docker chown performance

* upgrades bento4
2021-03-22 20:04:12 +02:00
dependabot[bot]
2b65afc8dd
Bump pillow from 8.0.1 to 8.1.1 (#108)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 8.0.1 to 8.1.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/8.0.1...8.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-22 20:02:59 +02:00
Dan1ell
b27e3ca6f6
Update Configuration.md (#86)
Clarifying which local_settings.py file to use.
2021-03-21 20:38:40 +02:00
Markos Gogoulos
1a7adb80da
Update Configuration.md (#93)
add option
2021-03-21 20:38:18 +02:00
Markos Gogoulos
2552551662
removes redundant usage of FRONTEND_HOST variable (#102) 2021-03-21 20:36:32 +02:00
Markos Gogoulos
3b35ce0262
use mediacms image from docker hub (#69)
* use mediacms image from docker hub
2021-03-08 21:54:09 +02:00
Markos Gogoulos
883af9bb4a
on Readme page resize screenshots (#68)
* image formating on Readme
2021-03-08 21:36:41 +02:00
swiftugandan
47f2279098
docker remove dangling pids (#58)
Co-authored-by: Munaawa Philip <munaawap@kainos.com>
2021-02-18 21:25:40 +02:00
Markos Gogoulos
c3d411ede1
set http prefix to variable on installation (#56) 2021-02-17 22:32:30 +02:00
swiftugandan
4450350dba
fixes hls generation for docker builds (#55)
Co-authored-by: Munaawa Philip <munaawap@kainos.com>
2021-02-17 22:32:18 +02:00
Markos Gogoulos
ba1bf7d263
Feat doc docker (#57)
* updated Readme, unset variabla

* unset variabla

* documentation
2021-02-17 22:32:06 +02:00
Markos Gogoulos
41c66469e2
Updated material icons version (#54)
Co-authored-by: root <1515939+styiannis@users.noreply.github.com>
2021-02-17 22:30:34 +02:00
Markos Gogoulos
9d1a22e4a9 reset variable 2021-02-11 17:38:09 +02:00
1283 changed files with 152020 additions and 72345 deletions

4
.coveragerc Normal file
View File

@ -0,0 +1,4 @@
[run]
omit =
*bento4*
*/migrations/*

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
npm-debug.log

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [mgogoulos]

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Issue report
about: Create a report to help us improve MediaCMS
title: ''
labels: 'issue: bug'
assignees: mgogoulos
---
**Describe the issue**
A clear and concise description of what the issue is.
**To Reproduce**
Steps to reproduce the issue:
1. Go to ...
2. Perform action ...
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS: [e.g. Ubuntu Linux]
- Installation method: [Docker install, or single server install]
- Browser, if applicable
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea
title: ''
labels: 'issue: enhancement'
assignees: mgogoulos
---
**Describe the feature you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

10
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,10 @@
## Description
<!-- Describe the changes introduced by this PR for the reviewers to fully understand. -->
## Steps
<!-- Actions to be done pre and post deployment -->
*Pre-deploy*
*Post-deploy*

20
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,20 @@
---
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'

78
.github/workflows/docker-build-push.yml vendored Normal file
View File

@ -0,0 +1,78 @@
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: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta for base image
id: meta-base
uses: docker/metadata-action@v4
with:
images: |
mediacms/mediacms
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: Docker meta for full image
id: meta-full
uses: docker/metadata-action@v4
with:
images: |
mediacms/mediacms
tags: |
type=raw,value=full,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=semver,pattern={{version}}-full
type=semver,pattern={{major}}.{{minor}}-full
type=semver,pattern={{major}}-full
labels: |
org.opencontainers.image.title=MediaCMS Full
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. This is the full version with additional dependencies.
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: Build and push full image
uses: docker/build-push-action@v4
with:
context: .
target: full
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta-full.outputs.tags }}
labels: ${{ steps.meta-full.outputs.labels }}
- name: Build and push base image
uses: docker/build-push-action@v4
with:
context: .
target: base
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta-base.outputs.tags }}
labels: ${{ steps.meta-base.outputs.labels }}

15
.github/workflows/pre-commit.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: pre-commit
on:
workflow_call:
jobs:
pre-commit:
name: 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 }}

35
.github/workflows/python.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: Python Tests
on:
workflow_call:
jobs:
build:
name: Build & test via docker-compose
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build the Stack
run: docker compose -f docker-compose-dev.yaml build
- name: Start containers
run: docker compose -f docker-compose-dev.yaml up -d
- name: List containers
run: docker ps
- name: Sleep for 60 seconds
run: sleep 60s
shell: bash
- name: Run Django Tests
run: docker compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest
# 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
- name: Tear down the Stack
run: docker compose -f docker-compose-dev.yaml down

25
.gitignore vendored
View File

@ -1,5 +1,11 @@
cli-tool/.env
frontend/package-lock.json
media_files/encoded/ media_files/encoded/
media_files/original/ media_files/original/
media_files/hls/
media_files/chunks/
media_files/uploads/
media_files/tinymce_media/
postgres_data/ postgres_data/
celerybeat-schedule celerybeat-schedule
logs/ logs/
@ -9,4 +15,23 @@ static/ckeditor/
static/debug_toolbar/ static/debug_toolbar/
static/mptt/ static/mptt/
static/rest_framework/ static/rest_framework/
static/drf-yasg
cms/local_settings.py cms/local_settings.py
deploy/docker/local_settings.py
yt.readme.md
/frontend-tools/video-editor/node_modules
/frontend-tools/video-editor/client/node_modules
/static_collected
/frontend-tools/video-editor-v1
frontend-tools/.DS_Store
static/video_editor/videos/sample-video-30s.mp4
static/video_editor/videos/sample-video-37s.mp4
/frontend-tools/video-editor-v2
.DS_Store
static/video_editor/videos/sample-video-10m.mp4
static/video_editor/videos/sample-video-10s.mp4
frontend-tools/video-js/public/videos/sample-video-white.mp4
frontend-tools/video-editor/client/public/videos/sample-video.mp3
frontend-tools/chapters-editor/client/public/videos/sample-video.mp3
static/chapters_editor/videos/sample-video.mp3
static/video_editor/videos/sample-video.mp3

View File

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

16
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,16 @@
repos:
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
language_version: python3
additional_dependencies: [ 'click==8.0.4' ]

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
/templates/cms/*
/templates/*.html
*.scss

21
.prettierrc Normal file
View File

@ -0,0 +1,21 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "lf",
"embeddedLanguageFormatting": "auto",
"overrides": [
{
"files": ["*.css", "*.scss"],
"options": {
"singleQuote": false
}
}
]
}

View File

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

13
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,13 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html

View File

@ -1,64 +1,113 @@
FROM python:3.8-buster AS compile-image FROM python:3.13.5-slim-bookworm AS build-image
SHELL ["/bin/bash", "-c"] # Install system dependencies needed for downloading and extracting
RUN apt-get update -y && \
# Set up virtualenv apt-get install -y --no-install-recommends wget xz-utils unzip && \
ENV VIRTUAL_ENV=/home/mediacms.io
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV PIP_NO_CACHE_DIR=1
RUN mkdir -p /home/mediacms.io/mediacms/{logs,pids} && cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV
# Install dependencies:
COPY requirements.txt .
RUN pip install -r requirements.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-632.x86_64-unknown-linux.zip && \
mkdir -p /home/mediacms.io/mediacms/media_files/hls Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/ && \
unzip -j Bento4-SDK-1-6-0-632.x86_64-unknown-linux.zip Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/mp4hls -d Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/ && \
rm Bento4-SDK-1-6-0-632.x86_64-unknown-linux.zip
############ RUNTIME IMAGE ############
FROM python:3.8-slim-buster as runtime-image
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV ADMIN_USER='admin'
ENV ADMIN_PASSWORD='mediacms'
ENV ADMIN_EMAIL='admin@localhost'
# 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 --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 ffmpeg imagemagick procps -y && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \
apt-get purge --auto-remove && \ apt-get purge --auto-remove && \
apt-get clean apt-get clean
RUN wget -q https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
RUN 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
# Install Bento4 in the specified location
RUN mkdir -p /home/mediacms.io/bento4 && \
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 /home/mediacms.io/bento4 && \
mv /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/* /home/mediacms.io/bento4/ && \
rm -rf /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux && \
rm -rf /home/mediacms.io/bento4/docs && \
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
############ BASE RUNTIME IMAGE ############
FROM python:3.13.5-slim-bookworm AS base
SHELL ["/bin/bash", "-c"]
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV CELERY_APP='cms'
ENV VIRTUAL_ENV=/home/mediacms.io
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# Install system dependencies first
RUN apt-get update -y && \
apt-get -y upgrade && \
apt-get install --no-install-recommends -y \
supervisor \
nginx \
imagemagick \
procps \
build-essential \
pkg-config \
zlib1g-dev \
zlib1g \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
libpq-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Set up virtualenv first
RUN mkdir -p /home/mediacms.io/mediacms/{logs} && \
cd /home/mediacms.io && \
python3 -m venv $VIRTUAL_ENV
# Copy requirements files
COPY requirements.txt requirements-dev.txt ./
# Install Python dependencies using pip (within virtualenv)
ARG DEVELOPMENT_MODE=False
RUN pip install --no-cache-dir uv && \
uv pip install --no-binary lxml --no-binary xmlsec -r requirements.txt && \
if [ "$DEVELOPMENT_MODE" = "True" ]; then \
echo "Installing development dependencies..." && \
uv pip install -r requirements-dev.txt; \
fi && \
apt-get purge -y --auto-remove \
build-essential \
pkg-config \
libxml2-dev \
libxmlsec1-dev \
libpq-dev
# Copy ffmpeg and Bento4 from build image
COPY --from=build-image /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
COPY --from=build-image /usr/local/bin/ffprobe /usr/local/bin/ffprobe
COPY --from=build-image /usr/local/bin/qt-faststart /usr/local/bin/qt-faststart
COPY --from=build-image /home/mediacms.io/bento4 /home/mediacms.io/bento4
# Copy application files
COPY . /home/mediacms.io/mediacms
WORKDIR /home/mediacms.io/mediacms WORKDIR /home/mediacms.io/mediacms
# required for sprite thumbnail generation for large video files
COPY deploy/docker/policy.xml /etc/ImageMagick-6/policy.xml
# Set process control environment variables
ENV ENABLE_UWSGI='yes' \
ENABLE_NGINX='yes' \
ENABLE_CELERY_BEAT='yes' \
ENABLE_CELERY_SHORT='yes' \
ENABLE_CELERY_LONG='yes' \
ENABLE_MIGRATIONS='yes'
EXPOSE 9000 80 EXPOSE 9000 80
RUN chmod +x ./deploy/docker/entrypoint.sh RUN chmod +x ./deploy/docker/entrypoint.sh
ENTRYPOINT ["./deploy/docker/entrypoint.sh"] ENTRYPOINT ["./deploy/docker/entrypoint.sh"]
CMD ["./deploy/docker/start.sh"] CMD ["./deploy/docker/start.sh"]
############ FULL IMAGE ############
FROM base AS full
COPY requirements-full.txt ./
RUN mkdir -p /root/.cache/ && \
chmod go+rwx /root/ && \
chmod go+rwx /root/.cache/
RUN uv pip install -r requirements-full.txt

23
HISTORY.md Normal file
View File

@ -0,0 +1,23 @@
# 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)

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
.PHONY: admin-shell build-frontend
admin-shell:
@container_id=$$(docker compose ps -q web); \
if [ -z "$$container_id" ]; then \
echo "Web container not found"; \
exit 1; \
else \
docker exec -it $$container_id /bin/bash; \
fi
build-frontend:
docker compose -f docker-compose-dev.yaml exec frontend npm run dist
cp -r frontend/dist/static/* static/
docker compose -f docker-compose-dev.yaml restart web
test:
docker compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest

172
README.md
View File

@ -1,6 +1,12 @@
![MediaCMS](static/images/logo_dark.png) # MediaCMS
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. [![GitHub license](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://raw.githubusercontent.com/mediacms-io/mediacms/main/LICENSE.txt)
[![Releases](https://img.shields.io/github/v/release/mediacms-io/mediacms?color=green)](https://github.com/mediacms-io/mediacms/releases/)
[![DockerHub](https://img.shields.io/docker/pulls/mediacms/mediacms)](https://hub.docker.com/r/mediacms/mediacms)
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.
@ -9,26 +15,23 @@ A demo is available at https://demo.mediacms.io
## Screenshots ## Screenshots
![MediaCMS](docs/images/index.jpg) <p align="center">
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/index.jpg" width="340">
Vanilla MediaCMS index page <img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/video.jpg" width="340">
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/embed.jpg" width="340">
![MediaCMS](docs/images/video.jpg) </p>
Video page with player different options
![MediaCMS](docs/images/embed.jpg)
Embed video page
## Features ## Features
- **Complete control over your data**: host it yourself! - **Complete control over your data**: host it yourself!
- **Support for multiple publishing workflows**: public, private, unlisted and custom
- **Modern technologies**: Django/Python/Celery, React. - **Modern technologies**: Django/Python/Celery, React.
- **Support for multiple publishing workflows**: public, private, unlisted and custom
- **Role-Based Access Control (RBAC)**: create RBAC categories and connect users to groups with view/edit access on their media
- **Automatic transcription**: through integration with Whisper running locally
- **Multiple media types support**: video, audio, image, pdf - **Multiple media types support**: video, audio, image, pdf
- **Multiple media classification options**: categories, tags and custom - **Multiple media classification options**: categories, tags and custom
- **Multiple media sharing options**: social media share, videos embed code generation - **Multiple media sharing options**: social media share, videos embed code generation
- **Video Trimmer**: trim video, replace, save as new or create segments
- **SAML support**: with ability to add mappings to system roles and groups
- **Easy media searching**: enriched with live search functionality - **Easy media searching**: enriched with live search functionality
- **Playlists for audio and video content**: create playlists, add and reorder content - **Playlists for audio and video content**: create playlists, add and reorder content
- **Responsive design**: including light and dark themes - **Responsive design**: including light and dark themes
@ -36,147 +39,83 @@ Embed video page
- **Configurable actions**: allow download, add comments, add likes, dislikes, report media - **Configurable actions**: allow download, add comments, add likes, dislikes, report media
- **Configuration options**: change logos, fonts, styling, add more pages - **Configuration options**: change logos, fonts, styling, add more pages
- **Enhanced video player**: customized video.js player with multiple resolution and playback speed options - **Enhanced video player**: customized video.js player with multiple resolution and playback speed options
- **Multiple transcoding profiles**: sane defaults for multiple dimensions (240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9) - **Multiple transcoding profiles**: sane defaults for multiple dimensions (144p, 240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9)
- **Adaptive video streaming**: possible through HLS protocol - **Adaptive video streaming**: possible through HLS protocol
- **Subtitles/CC**: support for multilingual subtitle files - **Subtitles/CC**: support for multilingual subtitle files
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers - **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
- **Chunked file uploads**: for pausable/resumable upload of content - **Chunked file uploads**: for pausable/resumable upload of content
- **REST API**: Documented through Swagger
- **Translation**: Most of the CMS is translated to a number of languages
## Example cases ## Example cases
- **Schools, education.** Administrators and editors keep what content will be published, students are not distracted with advertisements and irrelevant content, plus they have the ability to select either to stream or download content. - **Universities, schools, education.** Administrators and editors keep what content will be published, students are not distracted with advertisements and irrelevant content, plus they have the ability to select either to stream or download content.
- **Organization sensitive content.** In cases where content is sensitive and cannot be uploaded to external sites. - **Organization sensitive content.** In cases where content is sensitive and cannot be uploaded to external sites.
- **Build a great community.** MediaCMS can be customized (URLs, logos, fonts, aesthetics) so that you create a highly customized video portal for your community! - **Build a great community.** MediaCMS can be customized (URLs, logos, fonts, aesthetics) so that you create a highly customized video portal for your community!
- **Personal portal.** Organize, categorize and host your content the way you prefer. - **Personal portal.** Organize, categorize and host your content the way you prefer.
## 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 and Yiannis Stergiou Copyright Markos Gogoulos.
## Support and paid services ## Support and paid services
We provide custom installations, development of extra functionality, migration from existing systems, integrations with legacy systems, training and support. Contact us at info@mediacms.io for more information. We provide custom installations, development of extra functionality, migration from existing systems, integrations with legacy systems, training and support. Contact us at info@mediacms.io for more information.
### Commercial Hostings
**Elestio**
You can deploy MediaCMS on Elestio using one-click deployment. Elestio supports MediaCMS by providing revenue share so go ahead and click below to deploy and use MediaCMS.
## Hardware dependencies [![Deploy on Elestio](https://elest.io/images/logos/deploy-to-elestio-btn.png)](https://elest.io/open-source/mediacms)
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. ## Hardware considerations
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).
In order to support automatic transcriptions through Whisper, consider more CPUs.
## Installation ## Installation / Maintanance
MediaCMS can be installed through an automated script that installs and configures all needed services on a single server, and through Docker Compose. There are two ways to run MediaCMS, through Docker Compose and through installing it on a server via an automation script that installs and configures all needed services. Find the related pages:
### Docker Compose installation - [Single Server](docs/admins_docs.md#2-server-installation) page
With a recent version of Docker Compose installed, run as root - [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).
```bash ## Documentation
git clone https://github.com/mediacms-io/mediacms
cd mediacms
docker-compose build && docker-compose up
```
This will build an image, download and setup necessary images and start all containers. Once it finishes, MediaCMS will be installed on http://localhost * [Users documentation](docs/user_docs.md) page
* [Administrators documentation](docs/admins_docs.md) page
For more instructions, checkout the docs on the [Docker deployment](docs/Docker_deployment.md) page. Docker Compose support has been contributed by @swiftugandan. * [Developers documentation](docs/developers_docs.md) page
* [Configuration](docs/admins_docs.md#5-configuration) page
* [Transcoding](docs/transcoding.md) page
### Single server installation * [Developer Experience](docs/dev_exp.md) page
* [Media Permissions](docs/media_permissions.md) page
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 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.
Automated script - to run on Ubuntu 18 or Ubuntu 20 flavors only!
```bash
mkdir /home/mediacms.io && cd /home/mediacms.io/
git clone https://github.com/mediacms-io/mediacms
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.
## Update
If you've used the above way to install MediaCMS, update with the following:
```bash
cd /home/mediacms.io/mediacms # enter mediacms directory
source /home/mediacms.io/bin/activate # use virtualenv
git pull # update code
python manage.py migrate # run Django migrations
sudo systemctl restart mediacms celery_long celery_short # restart services
```
## Configure
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 cms/local_settings.py. All configuration options will be documented gradually on the [Configuration](docs/Configuration.md) page.
## Authors
MediaCMS is developed by Yiannis Stergiou and Markos Gogoulos. We are Wordgames - https://wordgames.gr.
## Technology ## Technology
This software uses the following list of awesome technologies:
- Python 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
- Django
- Django Rest Framework
- Celery
- PostgreSQL
- Redis
- Nginx
- uWSGI
- React
- Fine Uploader
- video.js
- FFMPEG
- Bento4
## Who is using it ## Who is using it
- **Multiple Universities** for hosting educational videos
- **EngageMedia** non-profit media, technology and culture organization - https://video.engagemedia.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.org/
- **Heritales** International Heritage Film Festival - https://stage.heritales.org
## Thanks To
- **Anna Helme**, for such a great partnership all these years!
- **Steve Anderson**, for trusting us and helping the Wordgames team make this real.
- **Andrew Lowenthal, King Catoy, Rezwan Islam** and the rest of the great team of [Engage Media](https://engagemedia.org).
- **Ioannis Korovesis, Ioannis Maistros, Diomidis Spinellis and Theodoros Karounos**, for their mentorship all these years, their contribution to science and the promotion of open source and free software technologies.
- **Antonis Ikonomou**, for hosting us on the excellent [Innovathens](https://www.innovathens.gr) space.
- **Werner Robitza**, for helping us with ffmpeg related stuff.
## How to contribute ## How to contribute
@ -186,9 +125,12 @@ 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, report bugs, suggest ideas - Open issues, participate on [discussions](https://github.com/mediacms-io/mediacms/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

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = []

View File

@ -1,11 +1,10 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [

View File

@ -1,12 +1,11 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
@ -35,8 +34,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddIndex( migrations.AddIndex(
model_name="mediaaction", model_name="mediaaction",
index=models.Index( index=models.Index(fields=["session_key", "action"], name="actions_med_session_fac55a_idx"),
fields=["session_key", "action"], name="actions_med_session_fac55a_idx"
),
), ),
] ]

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from users.models import User
from files.models import Media from files.models import Media
from users.models import User
USER_MEDIA_ACTIONS = ( USER_MEDIA_ACTIONS = (
("like", "Like"), ("like", "Like"),
@ -30,15 +31,11 @@ class MediaAction(models.Model):
help_text="for not logged in users", help_text="for not logged in users",
) )
action = models.CharField( action = models.CharField(max_length=20, choices=USER_MEDIA_ACTIONS, default="watch")
max_length=20, choices=USER_MEDIA_ACTIONS, default="watch"
)
# keeps extra info, eg on report action, why it is reported # keeps extra info, eg on report action, why it is reported
extra_info = models.TextField(blank=True, null=True) extra_info = models.TextField(blank=True, null=True)
media = models.ForeignKey( media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="mediaactions")
Media, on_delete=models.CASCADE, related_name="mediaactions"
)
action_date = models.DateTimeField(auto_now_add=True) action_date = models.DateTimeField(auto_now_add=True)
remote_ip = models.CharField(max_length=40, blank=True, null=True) remote_ip = models.CharField(max_length=40, blank=True, null=True)

View File

View File

@ -0,0 +1,86 @@
from django.apps import AppConfig
from django.conf import settings
from django.contrib import admin
class AdminCustomizationsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'admin_customizations'
def ready(self):
original_get_app_list = admin.AdminSite.get_app_list
def get_app_list(self, request, app_label=None):
"""Custom get_app_list"""
app_list = original_get_app_list(self, request, app_label)
# To see the list:
# print([a.get('app_label') for a in app_list])
email_model = None
rbac_group_model = None
identity_providers_user_log_model = None
identity_providers_login_option = None
auth_app = None
rbac_app = None
socialaccount_app = None
for app in app_list:
if app['app_label'] == 'users':
auth_app = app
elif app['app_label'] == 'account':
for model in app['models']:
if model['object_name'] == 'EmailAddress':
email_model = model
elif app['app_label'] == 'rbac':
if not getattr(settings, 'USE_RBAC', False):
continue
rbac_app = app
for model in app['models']:
if model['object_name'] == 'RBACGroup':
rbac_group_model = model
elif app['app_label'] == 'identity_providers':
if not getattr(settings, 'USE_IDENTITY_PROVIDERS', False):
continue
models_to_check = list(app['models'])
for model in models_to_check:
if model['object_name'] == 'IdentityProviderUserLog':
identity_providers_user_log_model = model
if model['object_name'] == 'LoginOption':
identity_providers_login_option = model
elif app['app_label'] == 'socialaccount':
socialaccount_app = app
if email_model and auth_app:
auth_app['models'].append(email_model)
if rbac_group_model and rbac_app and auth_app:
auth_app['models'].append(rbac_group_model)
if identity_providers_login_option and socialaccount_app:
socialaccount_app['models'].append(identity_providers_login_option)
if identity_providers_user_log_model and socialaccount_app:
socialaccount_app['models'].append(identity_providers_user_log_model)
# 2. don't include the following apps
apps_to_hide = ['authtoken', 'auth', 'account', 'saml_auth', 'rbac']
if not getattr(settings, 'USE_RBAC', False):
apps_to_hide.append('rbac')
if not getattr(settings, 'USE_IDENTITY_PROVIDERS', False):
apps_to_hide.append('socialaccount')
app_list = [app for app in app_list if app['app_label'] not in apps_to_hide]
# 3. change the ordering
app_order = {
'files': 1,
'users': 2,
'socialaccount': 3,
'rbac': 5,
}
app_list.sort(key=lambda x: app_order.get(x['app_label'], 999))
return app_list
admin.AdminSite.get_app_list = get_app_list

View File

View File

View File

10
cli-tool/README.md Normal file
View File

@ -0,0 +1,10 @@
## MediaCMS CLI Tool
This is the CLI tool to interact with the API of your installation/instance of MediaCMS.
### How to configure and use the tools
- Make sure that you have all the required installations (`cli-tool/requirements.txt`)installed. To install it -
- Create a new virtualenv using any python virtualenv manager.
- Then activate the virtualenv and enter `pip install -r requirements.txt`.
- Create an .env file in this folder (`mediacms/cli-tool/`)
- Run the cli tool using the command `python cli.py login`. This will authenticate you and store necessary creds for further authentications.
- To check the credentials and necessary setup, run `python cli.py whoami`. This will show your details.

167
cli-tool/cli.py Normal file
View File

@ -0,0 +1,167 @@
import json
import os
import click
import requests
from decouple import config
from rich import print
from rich.console import Console
from rich.table import Table
console = Console()
print("Welcome to the CLI Tool of [bold blue]MediaCMS![/bold blue]", ":thumbs_up:")
BASE_URL = 'https://demo.mediacms.io/api/v1'
AUTH_KEY = ''
USERNAME = ''
EMAIL = ''
def set_envs():
with open('.env', 'r') as file:
if not file.read(1):
print("Use the Login command to set your credential environment variables")
else:
global AUTH_KEY, USERNAME, EMAIL
AUTH_KEY = config('AUTH_KEY')
USERNAME = config('USERNAME')
EMAIL = config('EMAIL')
set_envs()
@click.group()
def apis():
"""A CLI wrapper for the MediaCMS API endpoints."""
@apis.command()
def login():
"""Login to your account."""
email = input('Enter your email address: ')
password = input('Enter your password: ')
data = {
"email": f"{email}",
"password": f"{password}",
}
response = requests.post(url=f'{BASE_URL}/login', data=data)
if response.status_code == 200:
username = json.loads(response.text)["username"]
with open(".env", "w") as file:
file.writelines(f'AUTH_KEY={json.loads(response.text)["token"]}\n')
file.writelines(f'EMAIL={json.loads(response.text)["email"]}\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:")
else:
print(f'Error: {"non_field_errors": ["User not found."]}')
@apis.command()
def upload_media():
"""Upload media to the server"""
headers = {'authorization': f'Token {AUTH_KEY}'}
path = input('Enter the location of the file or directory where multiple files are present: ')
if os.path.isdir(path):
for filename in os.listdir(path):
files = {}
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:
print(f"[bold blue]{filename}[/bold blue] successfully uploaded!")
else:
print(f'Error: {response.text}')
else:
files = {}
files['media_file'] = open(f'{os.path.abspath(path)}', 'rb')
response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files)
if response.status_code == 201:
print(f"[bold blue]{filename}[/bold blue] successfully uploaded!")
else:
print(f'Error: {response.text}')
@apis.command()
def my_media():
"""List all my media"""
headers = {'authorization': f'Token {AUTH_KEY}'}
response = requests.get(url=f'{BASE_URL}/media?author={USERNAME}', headers=headers)
if response.status_code == 200:
data_json = json.loads(response.text)
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Name of the media")
table.add_column("Media Type")
table.add_column("State")
for data in data_json['results']:
table.add_row(data['title'], data['media_type'], data['state'])
console.print(table)
else:
print(f'Could not get the media: {response.text}')
@apis.command()
def whoami():
"""Shows the details of the authorized user"""
headers = {'authorization': f'Token {AUTH_KEY}'}
response = requests.get(url=f'{BASE_URL}/whoami', headers=headers)
for data, value in json.loads(response.text).items():
print(data, ' : ', value)
@apis.command()
def categories():
"""List all categories."""
response = requests.get(url=f'{BASE_URL}/categories')
if response.status_code == 200:
data_json = json.loads(response.text)
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Category")
table.add_column("Description")
for data in data_json:
table.add_row(data['title'], data['description'])
console.print(table)
else:
print(f'Could not get the categories: {response.text}')
@apis.command()
def encodings():
"""List all encoding profiles"""
response = requests.get(url=f'{BASE_URL}/encode_profiles/')
if response.status_code == 200:
data_json = json.loads(response.text)
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Name")
table.add_column("Extension")
table.add_column("Resolution")
table.add_column("Codec")
table.add_column("Description")
for data in data_json:
table.add_row(data['name'], data['extension'], str(data['resolution']), data['codec'], data['description'])
console.print(table)
else:
print(f'Could not get the encodings: {response.text}')
if __name__ == '__main__':
apis()

View File

@ -0,0 +1,4 @@
click
python-decouple
requests
rich

View File

@ -1,4 +1,5 @@
from __future__ import absolute_import from __future__ import absolute_import
from .celery import app as celery_app from .celery import app as celery_app
__all__ = ["celery_app"] __all__ = ["celery_app"]

10
cms/auth_backends.py Normal file
View File

@ -0,0 +1,10 @@
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
class ApprovalBackend(ModelBackend):
def user_can_authenticate(self, user):
can_authenticate = super().user_can_authenticate(user)
if can_authenticate and settings.USERS_NEEDS_TO_BE_APPROVED and not user.is_superuser:
return getattr(user, 'is_approved', False)
return can_authenticate

View File

@ -1,6 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
import os import os
from celery import Celery from celery import Celery
from django.conf import settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")
app = Celery("cms") app = Celery("cms")
@ -12,5 +15,8 @@ app.conf.beat_schedule = app.conf.CELERY_BEAT_SCHEDULE
app.conf.broker_transport_options = {"visibility_timeout": 60 * 60 * 24} # 1 day app.conf.broker_transport_options = {"visibility_timeout": 60 * 60 * 24} # 1 day
# http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#redis-caveats # http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#redis-caveats
# setting this to settings.py file only is not respected. Setting here too
app.conf.task_always_eager = settings.CELERY_TASK_ALWAYS_EAGER
app.conf.worker_prefetch_multiplier = 1 app.conf.worker_prefetch_multiplier = 1

View File

@ -1,8 +1,9 @@
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from collections import OrderedDict # requires Python 2.7 or later from collections import OrderedDict # requires Python 2.7 or later
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils.functional import cached_property from django.utils.functional import cached_property
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class FasterDjangoPaginator(Paginator): class FasterDjangoPaginator(Paginator):
@ -17,7 +18,6 @@ class FastPaginationWithoutCount(PageNumberPagination):
django_paginator_class = FasterDjangoPaginator django_paginator_class = FasterDjangoPaginator
def get_paginated_response(self, data): def get_paginated_response(self, data):
return Response( return Response(
OrderedDict( OrderedDict(
[ [

57
cms/dev_settings.py Normal file
View File

@ -0,0 +1,57 @@
# Development settings, used in docker-compose-dev.yaml
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTALLED_APPS = [
"admin_customizations",
"django.contrib.auth",
"allauth",
"allauth.account",
"allauth.socialaccount",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"jazzmin",
"django.contrib.admin",
"django.contrib.sites",
"rest_framework",
"rest_framework.authtoken",
"imagekit",
"files.apps.FilesConfig",
"users.apps.UsersConfig",
"actions.apps.ActionsConfig",
"rbac.apps.RbacConfig",
"identity_providers.apps.IdentityProvidersConfig",
"debug_toolbar",
"mptt",
"crispy_forms",
"crispy_bootstrap5",
"uploader.apps.UploaderConfig",
"djcelery_email",
"drf_yasg",
"allauth.socialaccount.providers.saml",
"saml_auth.apps.SamlAuthConfig",
"corsheaders",
"tinymce",
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
"django.middleware.locale.LocaleMiddleware",
'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',
"allauth.account.middleware.AccountMiddleware",
]
DEBUG = True
CORS_ORIGIN_ALLOW_ALL = True
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
STATIC_ROOT = os.path.join(BASE_DIR, 'static_collected')

23
cms/middleware.py Normal file
View File

@ -0,0 +1,23 @@
from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import redirect
from django.urls import reverse
class ApprovalMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if settings.USERS_NEEDS_TO_BE_APPROVED and request.user.is_authenticated and not request.user.is_superuser and not getattr(request.user, 'is_approved', False):
allowed_paths = [
reverse('approval_required'),
reverse('account_logout'),
]
if request.path not in allowed_paths:
if request.path.startswith('/api/'):
return JsonResponse({'detail': 'User account not approved.'}, status=403)
return redirect('approval_required')
response = self.get_response(request)
return response

View File

@ -1,13 +1,29 @@
from django.conf import settings from django.conf import settings
from rest_framework import permissions from rest_framework import permissions
from files.methods import is_mediacms_editor, is_mediacms_manager from rest_framework.exceptions import PermissionDenied
from files.methods import (
is_mediacms_editor,
is_mediacms_manager,
user_allowed_to_upload,
)
class IsAuthorizedToAdd(permissions.BasePermission): class IsAuthorizedToAdd(permissions.BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS: if request.method in permissions.SAFE_METHODS:
return True return True
return user_allowed_to_upload(request) if not user_allowed_to_upload(request):
raise PermissionDenied("You don't have permission to upload media, or have reached max number of media uploads.")
return True
class IsAuthorizedToAddComment(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return user_allowed_to_comment(request)
class IsUserOrManager(permissions.BasePermission): class IsUserOrManager(permissions.BasePermission):
@ -24,7 +40,10 @@ class IsUserOrManager(permissions.BasePermission):
if is_mediacms_manager(request.user): if is_mediacms_manager(request.user):
return True return True
return obj.user == request.user if hasattr(obj, 'user'):
return obj.user == request.user
else:
return obj == request.user
class IsUserOrEditor(permissions.BasePermission): class IsUserOrEditor(permissions.BasePermission):
@ -44,21 +63,22 @@ class IsUserOrEditor(permissions.BasePermission):
return obj.user == request.user return obj.user == request.user
def user_allowed_to_upload(request): def user_allowed_to_comment(request):
"""Any custom logic for whether a user is allowed """Any custom logic for whether a user is allowed
to upload content lives here to comment lives here
""" """
if request.user.is_anonymous: if request.user.is_anonymous:
return False return False
if request.user.is_superuser: if request.user.is_superuser:
return True return True
if settings.CAN_ADD_MEDIA == "all": # Default is "all"
if not hasattr(settings, "CAN_COMMENT") or settings.CAN_COMMENT == "all":
return True return True
elif settings.CAN_ADD_MEDIA == "email_verified": elif settings.CAN_COMMENT == "email_verified":
if request.user.email_is_verified: if request.user.email_is_verified:
return True return True
elif settings.CAN_ADD_MEDIA == "advancedUser": elif settings.CAN_COMMENT == "advancedUser":
if request.user.advancedUser: if request.user.advancedUser:
return True return True
return False return False

View File

@ -1,22 +1,29 @@
import os import os
from celery.schedules import crontab from celery.schedules import crontab
from django.utils.translation import gettext_lazy as _
DEBUG = False DEBUG = False
# PORTAL NAME, this is the portal title and # PORTAL NAME, this is the portal title and
# is also shown on several places as emails # is also shown on several places as emails
PORTAL_NAME = "MediaCMS" PORTAL_NAME = "MediaCMS"
LANGUAGE_CODE = "en-us" PORTAL_DESCRIPTION = ""
TIME_ZONE = "Europe/London" TIME_ZONE = "Europe/London"
# who can add media # who can add media
# valid options include 'all', 'email_verified', 'advancedUser' # valid options include 'all', 'email_verified', 'advancedUser'
CAN_ADD_MEDIA = "all" CAN_ADD_MEDIA = "all"
# who can comment
# valid options include 'all', 'email_verified', 'advancedUser'
CAN_COMMENT = "all"
# valid choices here are 'public', 'private', 'unlisted # valid choices here are 'public', 'private', 'unlisted
PORTAL_WORKFLOW = "public" PORTAL_WORKFLOW = "public"
DEFAULT_THEME = "black" # this is not taken under consideration currently # valid values: 'light', 'dark'.
DEFAULT_THEME = "light"
# These are passed on every request # These are passed on every request
@ -42,7 +49,11 @@ ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY = True
# ip of the server should be part of this # ip of the server should be part of this
ALLOWED_HOSTS = ["*", "mediacms.io", "127.0.0.1", "localhost"] ALLOWED_HOSTS = ["*", "mediacms.io", "127.0.0.1", "localhost"]
FRONTEND_HOST = "http://localhost" FRONTEND_HOST = "http://localhost"
# this variable - along with SSL_FRONTEND_HOST is used on several places
# as email where a URL need appear etc
# FRONTEND_HOST needs an http prefix - at the end of the file # FRONTEND_HOST needs an http prefix - at the end of the file
# there's a conversion to https with the SSL_FRONTEND_HOST env # there's a conversion to https with the SSL_FRONTEND_HOST env
INTERNAL_IPS = "127.0.0.1" INTERNAL_IPS = "127.0.0.1"
@ -80,26 +91,48 @@ MAX_MEDIA_PER_PLAYLIST = 70
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5 UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
MAX_CHARS_FOR_COMMENT = 10000 # so that it doesn't end up huge MAX_CHARS_FOR_COMMENT = 10000 # so that it doesn't end up huge
TIMESTAMP_IN_TIMEBAR = False # shows timestamped comments in the timebar for videos
ALLOW_MENTION_IN_COMMENTS = False # allowing to mention other users with @ in the comments
# 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
SITE_ID = 1 SITE_ID = 1
# these are the portal logos (dark and light)
# set new paths for svg or png if you want to override
# svg has priority over png, so if you want to use
# custom pngs and not svgs, remove the lines with svgs
# or set as empty strings
# example:
# PORTAL_LOGO_DARK_SVG = ""
# PORTAL_LOGO_LIGHT_SVG = ""
# place the files on static/images folder
PORTAL_LOGO_DARK_SVG = "/static/images/logo_dark.svg"
PORTAL_LOGO_DARK_PNG = "/static/images/logo_dark.png"
PORTAL_LOGO_LIGHT_SVG = "/static/images/logo_light.svg"
PORTAL_LOGO_LIGHT_PNG = "/static/images/logo_dark.png"
# paths to extra css files to be included, eg "/static/css/custom.css"
# place css inside static/css folder
EXTRA_CSS_PATHS = []
# protection agains anonymous users # protection agains anonymous users
# per ip address limit, for actions as like/dislike/report # per ip address limit, for actions as like/dislike/report
TIME_TO_ACTION_ANONYMOUS = 10 * 60 TIME_TO_ACTION_ANONYMOUS = 10 * 60
# django-allauth settings # django-allauth settings
ACCOUNT_SESSION_REMEMBER = True ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_AUTHENTICATION_METHOD = "username_email" ACCOUNT_LOGIN_METHODS = {"username", "email"}
ACCOUNT_EMAIL_REQUIRED = True # new users need to specify email ACCOUNT_EMAIL_REQUIRED = True # new users need to specify email
ACCOUNT_EMAIL_VERIFICATION = "optional" # 'mandatory' 'none' ACCOUNT_EMAIL_VERIFICATION = "optional" # 'mandatory' 'none'
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_USERNAME_MIN_LENGTH = "4" ACCOUNT_USERNAME_MIN_LENGTH = 4
ACCOUNT_ADAPTER = "users.adapter.MyAccountAdapter" ACCOUNT_ADAPTER = "users.adapter.MyAccountAdapter"
ACCOUNT_SIGNUP_FORM_CLASS = "users.forms.SignupForm" ACCOUNT_SIGNUP_FORM_CLASS = "users.forms.SignupForm"
ACCOUNT_USERNAME_VALIDATORS = "users.validators.custom_username_validators" ACCOUNT_USERNAME_VALIDATORS = "users.validators.custom_username_validators"
@ -107,13 +140,19 @@ ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
ACCOUNT_USERNAME_REQUIRED = True ACCOUNT_USERNAME_REQUIRED = True
ACCOUNT_LOGIN_ON_PASSWORD_RESET = True ACCOUNT_LOGIN_ON_PASSWORD_RESET = True
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1 ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 20
ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 5
# registration won't be open, might also consider to remove links for register # registration won't be open, might also consider to remove links for register
USERS_CAN_SELF_REGISTER = True USERS_CAN_SELF_REGISTER = True
RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = ["xxx.com", "emaildomainwhatever.com"] RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = ["xxx.com", "emaildomainwhatever.com"]
# by default users do not need to be approved. If this is set to True, then new users
# will have to be approved before they can login successfully
USERS_NEEDS_TO_BE_APPROVED = False
# Comma separated list of domains: ["organization.com", "private.organization.com", "org2.com"]
# Empty list disables.
ALLOWED_DOMAINS_FOR_USER_REGISTRATION = []
# django rest settings # django rest settings
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ( "DEFAULT_AUTHENTICATION_CLASSES": (
@ -140,10 +179,13 @@ STATIC_ROOT = BASE_DIR + "/static/"
# where uploaded + encoded media are stored # where uploaded + encoded media are stored
MEDIA_ROOT = BASE_DIR + "/media_files/" MEDIA_ROOT = BASE_DIR + "/media_files/"
MEDIA_UPLOAD_DIR = os.path.join(MEDIA_ROOT, "original/") # these used to be os.path.join(MEDIA_ROOT, "folder/") but update to
MEDIA_ENCODING_DIR = os.path.join(MEDIA_ROOT, "encoded/") # Django 3.1.9 requires not absolute paths to be utilized...
THUMBNAIL_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "thumbnails/")
SUBTITLES_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "subtitles/") MEDIA_UPLOAD_DIR = "original/"
MEDIA_ENCODING_DIR = "encoded/"
THUMBNAIL_UPLOAD_DIR = f"{MEDIA_UPLOAD_DIR}/thumbnails/"
SUBTITLES_UPLOAD_DIR = f"{MEDIA_UPLOAD_DIR}/subtitles/"
HLS_DIR = os.path.join(MEDIA_ROOT, "hls/") HLS_DIR = os.path.join(MEDIA_ROOT, "hls/")
FFMPEG_COMMAND = "ffmpeg" # this is the path FFMPEG_COMMAND = "ffmpeg" # this is the path
@ -165,7 +207,7 @@ CHUNKIZE_VIDEO_DURATION = 60 * 5
VIDEO_CHUNKS_DURATION = 60 * 4 VIDEO_CHUNKS_DURATION = 60 * 4
# always get these two, even if upscaling # always get these two, even if upscaling
MINIMUM_RESOLUTIONS_TO_ENCODE = [240, 360] MINIMUM_RESOLUTIONS_TO_ENCODE = [144, 240]
# default settings for notifications # default settings for notifications
# not all of them are implemented # not all of them are implemented
@ -205,15 +247,13 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = ""
# only in case where unlisted workflow is used and no commentary # only in case where unlisted workflow is used and no commentary
# exists # exists
CANNOT_ADD_MEDIA_MESSAGE = "" CANNOT_ADD_MEDIA_MESSAGE = "User cannot add media, or maximum number of media uploads has been reached."
# mp4hls command, part of Bendo4 # mp4hls command, part of Bento4
MP4HLS_COMMAND = ( MP4HLS_COMMAND = "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls"
"/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/mp4hls"
)
# highly experimental, related with remote workers # highly experimental, related with remote workers
ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24" ADMIN_TOKEN = ""
# this is used by remote workers to push # this is used by remote workers to push
# encodings once they are done # encodings once they are done
# USE_BASIC_HTTP = True # USE_BASIC_HTTP = True
@ -228,35 +268,6 @@ ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24"
# uncomment the two lines related to htpasswd # uncomment the two lines related to htpasswd
CKEDITOR_CONFIGS = {
"default": {
"toolbar": "Custom",
"width": "100%",
"toolbar_Custom": [
["Styles"],
["Format"],
["Bold", "Italic", "Underline"],
["HorizontalRule"],
[
"NumberedList",
"BulletedList",
"-",
"Outdent",
"Indent",
"-",
"JustifyLeft",
"JustifyCenter",
"JustifyRight",
"JustifyBlock",
],
["Link", "Unlink"],
["Image"],
["RemoveFormat", "Source"],
],
}
}
AUTH_USER_MODEL = "users.User" AUTH_USER_MODEL = "users.User"
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"
@ -266,7 +277,7 @@ AUTHENTICATION_BACKENDS = (
) )
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.admin", "admin_customizations",
"django.contrib.auth", "django.contrib.auth",
"allauth", "allauth",
"allauth.account", "allauth.account",
@ -275,6 +286,8 @@ INSTALLED_APPS = [
"django.contrib.sessions", "django.contrib.sessions",
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
"jazzmin",
"django.contrib.admin",
"django.contrib.sites", "django.contrib.sites",
"rest_framework", "rest_framework",
"rest_framework.authtoken", "rest_framework.authtoken",
@ -282,23 +295,31 @@ INSTALLED_APPS = [
"files.apps.FilesConfig", "files.apps.FilesConfig",
"users.apps.UsersConfig", "users.apps.UsersConfig",
"actions.apps.ActionsConfig", "actions.apps.ActionsConfig",
"rbac.apps.RbacConfig",
"identity_providers.apps.IdentityProvidersConfig",
"debug_toolbar", "debug_toolbar",
"mptt", "mptt",
"crispy_forms", "crispy_forms",
"crispy_bootstrap5",
"uploader.apps.UploaderConfig", "uploader.apps.UploaderConfig",
"djcelery_email", "djcelery_email",
"ckeditor", "drf_yasg",
"allauth.socialaccount.providers.saml",
"saml_auth.apps.SamlAuthConfig",
"tinymce",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware", "debug_toolbar.middleware.DebugToolbarMiddleware",
"allauth.account.middleware.AccountMiddleware",
] ]
ROOT_URLCONF = "cms.urls" ROOT_URLCONF = "cms.urls"
@ -326,11 +347,15 @@ WSGI_APPLICATION = "cms.wsgi.application"
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
"OPTIONS": {
"user_attributes": ("username", "email", "first_name", "last_name"),
"max_similarity": 0.7,
},
}, },
{ {
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": { "OPTIONS": {
"min_length": 5, "min_length": 7,
}, },
}, },
{ {
@ -344,6 +369,16 @@ FILE_UPLOAD_HANDLERS = [
LOGS_DIR = os.path.join(BASE_DIR, "logs") LOGS_DIR = os.path.join(BASE_DIR, "logs")
error_filename = os.path.join(LOGS_DIR, "debug.log")
if not os.path.exists(LOGS_DIR):
try:
os.mkdir(LOGS_DIR)
except PermissionError:
pass
if not os.path.isfile(error_filename):
open(error_filename, 'a').close()
LOGGING = { LOGGING = {
"version": 1, "version": 1,
"disable_existing_loggers": False, "disable_existing_loggers": False,
@ -351,7 +386,7 @@ LOGGING = {
"file": { "file": {
"level": "ERROR", "level": "ERROR",
"class": "logging.FileHandler", "class": "logging.FileHandler",
"filename": os.path.join(LOGS_DIR, "debug.log"), "filename": error_filename,
}, },
}, },
"loggers": { "loggers": {
@ -363,16 +398,7 @@ LOGGING = {
}, },
} }
DATABASES = { DATABASES = {"default": {"ENGINE": "django.db.backends.postgresql", "NAME": "mediacms", "HOST": "127.0.0.1", "PORT": "5432", "USER": "mediacms", "PASSWORD": "mediacms", "OPTIONS": {'pool': True}}}
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "mediacms",
"HOST": "127.0.0.1",
"PORT": "5432",
"USER": "mediacms",
"PASSWORD": "mediacms",
}
}
REDIS_LOCATION = "redis://127.0.0.1:6379/1" REDIS_LOCATION = "redis://127.0.0.1:6379/1"
@ -419,11 +445,155 @@ CELERY_BEAT_SCHEDULE = {
# TODO: beat, delete chunks from media root # TODO: beat, delete chunks from media root
# chunks_dir after xx days...(also uploads_dir) # chunks_dir after xx days...(also uploads_dir)
LOCAL_INSTALL = False LOCAL_INSTALL = False
# this is an option to make the whole portal available to logged in users only
# it is placed here so it can be overrided on local_settings.py
GLOBAL_LOGIN_REQUIRED = False
# TODO: separate settings on production/development more properly, for now
# this should be ok
CELERY_TASK_ALWAYS_EAGER = False
if os.environ.get("TESTING"):
CELERY_TASK_ALWAYS_EAGER = True
# if True, only show original, don't perform any action on videos
DO_NOT_TRANSCODE_VIDEO = False
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
LANGUAGES = [
('ar', _('Arabic')),
('bn', _('Bengali')),
('da', _('Danish')),
('nl', _('Dutch')),
('en', _('English')),
('fr', _('French')),
('de', _('German')),
('hi', _('Hindi')),
('id', _('Indonesian')),
('it', _('Italian')),
('ja', _('Japanese')),
('ko', _('Korean')),
('pt', _('Portuguese')),
('ru', _('Russian')),
('zh-hans', _('Simplified Chinese')),
('sl', _('Slovenian')),
('zh-hant', _('Traditional Chinese')),
('es', _('Spanish')),
('tr', _('Turkish')),
('el', _('Greek')),
('ur', _('Urdu')),
('he', _('Hebrew')),
]
LANGUAGE_CODE = 'en' # default language
TINYMCE_DEFAULT_CONFIG = {
"theme": "silver",
"height": 500,
"resize": "both",
"menubar": "file edit view insert format tools table help",
"menu": {
"format": {
"title": "Format",
"items": "blocks | bold italic underline strikethrough superscript subscript code | " "fontfamily fontsize align lineheight | " "forecolor backcolor removeformat",
},
},
"plugins": "advlist,autolink,autosave,lists,link,image,charmap,print,preview,anchor,"
"searchreplace,visualblocks,code,fullscreen,insertdatetime,media,table,paste,directionality,"
"code,help,wordcount,emoticons,file,image,media",
"toolbar": "undo redo | code preview | blocks | "
"bold italic | alignleft aligncenter "
"alignright alignjustify ltr rtl | bullist numlist outdent indent | "
"removeformat | restoredraft help | image media",
"branding": False, # remove branding
"promotion": False, # remove promotion
"body_class": "page-main-inner custom-page-wrapper", # class of the body element in tinymce
"block_formats": "Paragraph=p; Heading 1=h1; Heading 2=h2; Heading 3=h3;",
"formats": { # customize h2 to always have emphasis-large class
"h2": {"block": "h2", "classes": "emphasis-large"},
},
"font_size_formats": "16px 18px 24px 32px",
"images_upload_url": "/tinymce/upload/",
"images_upload_handler": "tinymce.views.upload_image",
"automatic_uploads": True,
"file_picker_types": "image",
"paste_data_images": True,
"paste_as_text": False,
"paste_enable_default_filters": True,
"paste_word_valid_elements": "b,strong,i,em,h1,h2,h3,h4,h5,h6,p,br,a,ul,ol,li",
"paste_retain_style_properties": "all",
"paste_remove_styles": False,
"paste_merge_formats": True,
"sandbox_iframes": False,
}
SPRITE_NUM_SECS = 10
# number of seconds for sprite image.
# If you plan to change this, you must also follow the instructions on admins_docs.md
# to change the equivalent value in ./frontend/src/static/js/components/media-viewer/VideoViewer/index.js and then re-build frontend
# how many images will be shown on the slideshow
SLIDESHOW_ITEMS = 30
# this calculation is redundant most probably, setting as an option
CALCULATE_MD5SUM = False
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5"
# allow option to override the default admin url
# keep the trailing slash
DJANGO_ADMIN_URL = "admin/"
# this are used around a number of places and will need to be well documented!!!
USE_SAML = False
USE_RBAC = False
USE_IDENTITY_PROVIDERS = False
JAZZMIN_UI_TWEAKS = {"theme": "flatly"}
USE_ROUNDED_CORNERS = True
ALLOW_VIDEO_TRIMMER = True
ALLOW_CUSTOM_MEDIA_URLS = False
# Whether to allow anonymous users to list all users
ALLOW_ANONYMOUS_USER_LISTING = True
# Who can see the members page
# valid choices are all, editors, admins
CAN_SEE_MEMBERS_PAGE = "all"
# Maximum number of media a user can upload
NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100
# ffmpeg options
FFMPEG_DEFAULT_PRESET = "medium" # see https://trac.ffmpeg.org/wiki/Encode/H.264
# If 'all' is in the list, no check is performed
ALLOWED_MEDIA_UPLOAD_TYPES = ["video", "audio", "image", "pdf"]
# transcription options
# the mediacms-full docker image needs to be used in order to be able to use transcription
# if you are using the mediacms-full image, change USE_WHISPER_TRANSCRIBE to True
USE_WHISPER_TRANSCRIBE = False
# by default all users can request a video to be transcribed. If you want to
# allow only editors, set this to False
USER_CAN_TRANSCRIBE_VIDEO = True
# Whisper transcribe options - https://github.com/openai/whisper
WHISPER_MODEL = "base"
# show a custom text in the sidebar footer, otherwise the default will be shown if this is empty
SIDEBAR_FOOTER_TEXT = ""
try: try:
# keep a local_settings.py file for local overrides # keep a local_settings.py file for local overrides
from .local_settings import * from .local_settings import * # noqa
# ALLOWED_HOSTS needs a url/ip # ALLOWED_HOSTS needs a url/ip
ALLOWED_HOSTS.append(FRONTEND_HOST.replace("http://", "").replace("https://", "")) ALLOWED_HOSTS.append(FRONTEND_HOST.replace("http://", "").replace("https://", ""))
@ -431,12 +601,43 @@ except ImportError:
# local_settings not in use # local_settings not in use
pass pass
# Don't add new settings below that could be overridden in local_settings.py!!!
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}" FRONTEND_HOST = f"http://{FRONTEND_HOST}" # noqa
if LOCAL_INSTALL: if LOCAL_INSTALL:
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https") SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
else: else:
SSL_FRONTEND_HOST = FRONTEND_HOST SSL_FRONTEND_HOST = FRONTEND_HOST
# CSRF_COOKIE_SECURE = True
# SESSION_COOKIE_SECURE = True
PYSUBS_COMMAND = "pysubs2"
# 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
if GLOBAL_LOGIN_REQUIRED:
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
MIDDLEWARE.insert(auth_index + 1, "django.contrib.auth.middleware.LoginRequiredMiddleware")
if USERS_NEEDS_TO_BE_APPROVED:
AUTHENTICATION_BACKENDS = (
'cms.auth_backends.ApprovalBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
MIDDLEWARE.insert(auth_index + 1, "cms.middleware.ApprovalMiddleware")

View File

@ -1,13 +1,38 @@
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
import debug_toolbar import debug_toolbar
from django.conf import settings
from django.conf.urls import include
from django.contrib import admin
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
from rest_framework.permissions import AllowAny
schema_view = get_schema_view(
openapi.Info(title="MediaCMS API", default_version='v1', contact=openapi.Contact(url="https://mediacms.io"), x_logo={"url": "../../static/images/logo_dark.svg"}),
public=True,
permission_classes=(AllowAny,),
)
# refactor seriously
urlpatterns = [ urlpatterns = [
url(r"^__debug__/", include(debug_toolbar.urls)), re_path(r"^__debug__/", include(debug_toolbar.urls)),
url(r"^", include("files.urls")), path(
url(r"^", include("users.urls")), "robots.txt",
url(r"^accounts/", include("allauth.urls")), TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
url(r"^api-auth/", include("rest_framework.urls")), ),
path("admin/", admin.site.urls), re_path(r"^", include("files.urls")),
re_path(r"^", include("users.urls")),
re_path(r"^accounts/", include("allauth.urls")),
re_path(r"^api-auth/", include("rest_framework.urls")),
path(settings.DJANGO_ADMIN_URL, admin.site.urls),
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('docs/api/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
path("tinymce/", include("tinymce.urls")),
] ]
admin.site.site_header = "MediaCMS Admin"
admin.site.site_title = "MediaCMS"
admin.site.index_title = "Admin"

1
cms/version.py Normal file
View File

@ -0,0 +1 @@
VERSION = "7.0.1-beta.8"

75
deic_setup_notes.md Normal file
View File

@ -0,0 +1,75 @@
# MediaCMS: Document Changes for DEIC
## Configuration Changes
The following changes are required in `deploy/docker/local_settings.py`:
```python
# default workflow
PORTAL_WORKFLOW = 'private'
# Authentication Settings
# these two are necessary so that users cannot register through system accounts. They can only register through identity providers
REGISTER_ALLOWED = False
USERS_CAN_SELF_REGISTER = False
USE_RBAC = True
USE_SAML = True
USE_IDENTITY_PROVIDERS = True
# Proxy and SSL Settings
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
# SAML Configuration
SOCIALACCOUNT_ADAPTER = 'saml_auth.adapter.SAMLAccountAdapter'
ACCOUNT_USERNAME_VALIDATORS = "users.validators.less_restrictive_username_validators"
SOCIALACCOUNT_PROVIDERS = {
"saml": {
"provider_class": "saml_auth.custom.provider.CustomSAMLProvider",
}
}
SOCIALACCOUNT_AUTO_SIGNUP = True
SOCIALACCOUNT_EMAIL_REQUIRED = False
# if set to strict, user is created with the email from the saml provider without
# checking if the email is already on the system
# however if this is ommited, and user tries to login with an email that already exists on
# the system, then they get to the ugly form where it suggests they add a username/email/name
ACCOUNT_PREVENT_ENUMERATION = 'strict'
```
## SAML Configuration Steps
### Step 1: Add SAML Identity Provider
1. Navigate to Admin panel
2. Select "Identity Provider"
3. Configure as follows:
- **Provider**: saml # ensure this is set with lower case!
- **Provider ID**: `wayf.wayf.dk`
- **IDP Config Name**: `Deic` (or preferred name)
- **Client ID**: `wayf_dk` (important: defines the URL, e.g., `https://deic.mediacms.io/accounts/saml/wayf_dk`)
- **Site**: Set the default one
### Step 2: Add SAML Configuration
Can be set through the SAML Configurations tab:
1. **IDP ID**: Must be a URL, e.g., `https://wayf.wayf.dk`
2. **IDP Certificate**: x509cert from your SAML provider
3. **SSO URL**: `https://wayf.wayf.dk/saml2/idp/SSOService2.php`
4. **SLO URL**: `https://wayf.wayf.dk/saml2/idp/SingleLogoutService.php`
5. **SP Metadata URL**: The metadata URL set for the SP, e.g., `https://deic.mediacms.io/saml/metadata`. This should point to the URL of the SP and is autogenerated
### Step 3: Set the other Options
1. **Email Settings**:
- `verified_email`: When enabled, emails from SAML responses will be marked as verified
- `Remove from groups`: When enabled, user is removed from a group after login, if they have been removed from the group on the IDP
2. **Global Role Mapping**: Maps the role returned by SAML (as set in the SAML Configuration tab) with the role in MediaCMS
3. **Group Role Mapping**: Maps the role returned by SAML (as set in the SAML Configuration tab) with the role in groups that user will be added
4. **Group mapping**: This creates groups associated with this IDP. Group ids as they come from SAML, associated with MediaCMS groups
5. **Category Mapping**: This maps a group id (from SAML response) with a category in MediaCMS

View File

@ -4,12 +4,15 @@ set -e
# forward request and error logs to docker log collector # forward request and error logs to docker log collector
ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log && \ ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log && \
ln -sf /dev/stdout /var/log/nginx/mediacms.io.access.log && ln -sf /dev/stderr /var/log/nginx/mediacms.io.error.log ln -sf /dev/stdout /var/log/nginx/mediacms.io.access.log && ln -sf /dev/stderr /var/log/nginx/mediacms.io.error.log
cp /home/mediacms.io/mediacms/deploy/docker/local_settings.py /home/mediacms.io/mediacms/cms/local_settings.py cp /home/mediacms.io/mediacms/deploy/docker/local_settings.py /home/mediacms.io/mediacms/cms/local_settings.py
mkdir -p /home/mediacms.io/mediacms/{logs,pids}
mkdir -p /home/mediacms.io/mediacms/{logs,media_files/hls}
touch /home/mediacms.io/mediacms/logs/debug.log touch /home/mediacms.io/mediacms/logs/debug.log
chown -R www-data. /home/mediacms.io/ mkdir -p /var/run/mediacms
chown www-data:www-data /var/run/mediacms
TARGET_GID=$(stat -c "%g" /home/mediacms.io/mediacms/) TARGET_GID=$(stat -c "%g" /home/mediacms.io/mediacms/)
@ -25,6 +28,11 @@ else
usermod -a -G $GROUP www-data usermod -a -G $GROUP www-data
fi fi
# We should do this only for folders that have a different owner, since it is an expensive operation
# Also ignoring .git folder to fix this issue https://github.com/mediacms-io/mediacms/issues/934
# Exclude package-lock.json files that may not exist or be removed during frontend setup
find /home/mediacms.io/mediacms ! \( -path "*.git*" -o -name "package-lock.json" \) -exec chown www-data:$TARGET_GID {} + 2>/dev/null || true
chmod +x /home/mediacms.io/mediacms/deploy/docker/start.sh /home/mediacms.io/mediacms/deploy/docker/prestart.sh chmod +x /home/mediacms.io/mediacms/deploy/docker/start.sh /home/mediacms.io/mediacms/deploy/docker/prestart.sh
exec "$@" exec "$@"

View File

@ -1,17 +1,19 @@
FRONTEND_HOST = 'http://localhost' import os
PORTAL_NAME = 'MediaCMS'
SECRET_KEY = 'ma!s3^b-cw!f#7s6s0m3*jx77a@riw(7701**(r=ww%w!2+yk2' FRONTEND_HOST = os.getenv('FRONTEND_HOST', 'http://localhost')
POSTGRES_HOST = 'db' PORTAL_NAME = os.getenv('PORTAL_NAME', 'MediaCMS')
REDIS_LOCATION = "redis://redis:6379/1" SECRET_KEY = os.getenv('SECRET_KEY', 'ma!s3^b-cw!f#7s6s0m3*jx77a@riw(7701**(r=ww%w!2+yk2')
REDIS_LOCATION = os.getenv('REDIS_LOCATION', 'redis://redis:6379/1')
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.postgresql", "ENGINE": "django.db.backends.postgresql",
"NAME": "mediacms", "NAME": os.getenv('POSTGRES_NAME', 'mediacms'),
"HOST": POSTGRES_HOST, "HOST": os.getenv('POSTGRES_HOST', 'db'),
"PORT": "5432", "PORT": os.getenv('POSTGRES_PORT', '5432'),
"USER": "mediacms", "USER": os.getenv('POSTGRES_USER', 'mediacms'),
"PASSWORD": "mediacms", "PASSWORD": os.getenv('POSTGRES_PASSWORD', 'mediacms'),
"OPTIONS": {'pool': True},
} }
} }
@ -29,4 +31,6 @@ CACHES = {
BROKER_URL = REDIS_LOCATION BROKER_URL = REDIS_LOCATION
CELERY_RESULT_BACKEND = BROKER_URL CELERY_RESULT_BACKEND = BROKER_URL
DEBUG = False MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls"
DEBUG = os.getenv('DEBUG', 'False') == 'True'

View File

@ -16,6 +16,10 @@ server {
location /media { location /media {
alias /home/mediacms.io/mediacms/media_files ; alias /home/mediacms.io/mediacms/media_files ;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
} }
location / { location / {

99
deploy/docker/policy.xml Normal file
View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)*>
<!ATTLIST policymap xmlns CDATA #FIXED ''>
<!ELEMENT policy EMPTY>
<!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
]>
<!--
Configure ImageMagick policies.
Domains include system, delegate, coder, filter, path, or resource.
Rights include none, read, write, execute and all. Use | to combine them,
for example: "read | write" to permit read from, or write to, a path.
Use a glob expression as a pattern.
Suppose we do not want users to process MPEG video images:
<policy domain="delegate" rights="none" pattern="mpeg:decode" />
Here we do not want users reading images from HTTP:
<policy domain="coder" rights="none" pattern="HTTP" />
The /repository file system is restricted to read only. We use a glob
expression to match all paths that start with /repository:
<policy domain="path" rights="read" pattern="/repository/*" />
Lets prevent users from executing any image filters:
<policy domain="filter" rights="none" pattern="*" />
Any large image is cached to disk rather than memory:
<policy domain="resource" name="area" value="1GP"/>
Use the default system font unless overwridden by the application:
<policy domain="system" name="font" value="/usr/share/fonts/favorite.ttf"/>
Define arguments for the memory, map, area, width, height and disk resources
with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
exceeds policy maximum so memory limit is 1GB).
Rules are processed in order. Here we want to restrict ImageMagick to only
read or write a small subset of proven web-safe image types:
<policy domain="delegate" rights="none" pattern="*" />
<policy domain="filter" rights="none" pattern="*" />
<policy domain="coder" rights="none" pattern="*" />
<policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
-->
<policymap>
<!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
<policy domain="resource" name="memory" value="1GiB"/>
<policy domain="resource" name="map" value="30GiB"/>
<policy domain="resource" name="width" value="16MP"/>
<policy domain="resource" name="height" value="16MP"/>
<!-- <policy domain="resource" name="list-length" value="128"/> -->
<policy domain="resource" name="area" value="40GP"/>
<policy domain="resource" name="disk" value="100GiB"/>
<!-- <policy domain="resource" name="file" value="768"/> -->
<!-- <policy domain="resource" name="thread" value="4"/> -->
<!-- <policy domain="resource" name="throttle" value="0"/> -->
<!-- <policy domain="resource" name="time" value="3600"/> -->
<!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
<!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
<!-- <policy domain="path" rights="none" pattern="@*" /> -->
<!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
<!-- <policy domain="cache" name="synchronize" value="True"/> -->
<!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/>
<!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
<!-- <policy domain="system" name="shred" value="2"/> -->
<!-- <policy domain="system" name="precision" value="6"/> -->
<!-- <policy domain="system" name="font" value="/path/to/font.ttf"/> -->
<!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
<!-- <policy domain="system" name="shred" value="2"/> -->
<!-- <policy domain="system" name="precision" value="6"/> -->
<!-- not needed due to the need to use explicitly by mvg: -->
<!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
<!-- use curl -->
<policy domain="delegate" rights="none" pattern="URL" />
<policy domain="delegate" rights="none" pattern="HTTPS" />
<policy domain="delegate" rights="none" pattern="HTTP" />
<!-- in order to avoid to get image with password text -->
<policy domain="path" rights="none" pattern="@*"/>
<!-- disable ghostscript format types -->
<policy domain="coder" rights="none" pattern="PS" />
<policy domain="coder" rights="none" pattern="PS2" />
<policy domain="coder" rights="none" pattern="PS3" />
<policy domain="coder" rights="none" pattern="EPS" />
<policy domain="coder" rights="none" pattern="PDF" />
<policy domain="coder" rights="none" pattern="XPS" />
</policymap>

View File

@ -4,20 +4,29 @@ RANDOM_ADMIN_PASS=`python -c "import secrets;chars = 'abcdefghijklmnopqrstuvwxyz
ADMIN_PASSWORD=${ADMIN_PASSWORD:-$RANDOM_ADMIN_PASS} ADMIN_PASSWORD=${ADMIN_PASSWORD:-$RANDOM_ADMIN_PASS}
if [ X"$ENABLE_MIGRATIONS" = X"yes" ]; then if [ X"$ENABLE_MIGRATIONS" = X"yes" ]; then
echo "Running migrations service"
python manage.py migrate python manage.py migrate
python manage.py loaddata fixtures/encoding_profiles.json EXISTING_INSTALLATION=`echo "from users.models import User; print(User.objects.exists())" |python manage.py shell`
python manage.py loaddata fixtures/categories.json if [ "$EXISTING_INSTALLATION" = "True" ]; then
echo "Loaddata has already run"
else
echo "Running loaddata and creating admin user"
python manage.py loaddata fixtures/encoding_profiles.json
python manage.py loaddata fixtures/categories.json
# post_save, needs redis to succeed (ie. migrate depends on redis)
DJANGO_SUPERUSER_PASSWORD=$ADMIN_PASSWORD python manage.py createsuperuser \
--no-input \
--username=$ADMIN_USER \
--email=$ADMIN_EMAIL \
--database=default || true
echo "Created admin user with password: $ADMIN_PASSWORD"
fi
echo "RUNNING COLLECTSTATIC"
python manage.py collectstatic --noinput python manage.py collectstatic --noinput
echo "Admin Password: $ADMIN_PASSWORD"
# post_save, needs redis to succeed (ie. migrate depends on redis)
DJANGO_SUPERUSER_PASSWORD=$ADMIN_PASSWORD python manage.py createsuperuser \
--no-input \
--username=$ADMIN_USER \
--email=$ADMIN_EMAIL \
--database=default || true
# echo "Updating hostname ..." # echo "Updating hostname ..."
# TODO: Get the FRONTEND_HOST from cms/local_settings.py # TODO: Get the FRONTEND_HOST from cms/local_settings.py
# echo "from django.contrib.sites.models import Site; Site.objects.update(name='$FRONTEND_HOST', domain='$FRONTEND_HOST')" | 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
@ -58,4 +67,5 @@ 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

@ -1 +1 @@
client_max_body_size 1g; client_max_body_size 5800M;

View File

@ -9,4 +9,4 @@ user=www-data
directory=/home/mediacms.io/mediacms directory=/home/mediacms.io/mediacms
priority=300 priority=300
startinorder=true startinorder=true
command=/home/mediacms.io/bin/celery beat --pidfile=/home/mediacms.io/mediacms/pids/beat%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_beat.log command=/home/mediacms.io/bin/celery beat --pidfile=/var/run/mediacms/beat%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_beat.log

View File

@ -10,4 +10,4 @@ directory=/home/mediacms.io/mediacms
priority=500 priority=500
startinorder=true startinorder=true
startsecs=0 startsecs=0
command=/home/mediacms.io/bin/celery multi start long1 --pidfile=/home/mediacms.io/mediacms/pids/%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_long.log -Ofair --prefetch-multiplier=1 -Q long_tasks command=/home/mediacms.io/bin/celery multi start long1 --pidfile=/var/run/mediacms/%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_long.log -Ofair --prefetch-multiplier=1 -Q long_tasks

View File

@ -9,4 +9,4 @@ user=www-data
directory=/home/mediacms.io/mediacms directory=/home/mediacms.io/mediacms
priority=400 priority=400
startinorder=true startinorder=true
command=/home/mediacms.io/bin/celery multi start short1 short2 --pidfile=/home/mediacms.io/mediacms/pids/%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_short.log --soft-time-limit=300 -c10 -Q short_tasks command=/home/mediacms.io/bin/celery multi start short1 short2 --pidfile=/var/run/mediacms/%%n.pid --loglevel=INFO --logfile=/home/mediacms.io/mediacms/logs/celery_short.log --soft-time-limit=300 -c10 -Q short_tasks

View File

@ -21,3 +21,4 @@ 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,15 +8,13 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
Environment=APP_DIR="/home/mediacms.io/mediacms" WorkingDirectory=/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} 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 ExecStop=/bin/kill -s TERM $MAINPID
[Install] [Install]

View File

@ -8,23 +8,21 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
Environment=APP_DIR="/home/mediacms.io/mediacms" WorkingDirectory=/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} 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -8,14 +8,13 @@ User=www-data
Group=www-data Group=www-data
Restart=always Restart=always
RestartSec=10 RestartSec=10
Environment=APP_DIR="/home/mediacms.io/mediacms" WorkingDirectory=/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
@ -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_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} 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -0,0 +1,13 @@
-----BEGIN DH PARAMETERS-----
MIICCAKCAgEAo3MMiEY/fNbu+usIM0cDi6x8G3JBApv0Lswta4kiyedWT1WN51iQ
9zhOFpmcu6517f/fR9MUdyhVKHxxSqWQTcmTEFtz4P3VLTS/W1N5VbKE2VEMLpIi
wr350aGvV1Er0ujcp5n4O4h0I1tn4/fNyDe7+pHCdwM+hxe8hJ3T0/tKtad4fnIs
WHDjl4f7m7KuFfheiK7Efb8MsT64HDDAYXn+INjtDZrbE5XPw20BqyWkrf07FcPx
8o9GW50Ox7/FYq7jVMI/skEu0BRc8u6uUD9+UOuWUQpdeHeFcvLOgW53Z03XwWuX
RXosUKzBPuGtUDAaKD/HsGW6xmGr2W9yRmu27jKpfYLUb/eWbbnRJwCw04LdzPqv
jmtq02Gioo3lf5H5wYV9IYF6M8+q/slpbttsAcKERimD1273FBRt5VhSugkXWKjr
XDhoXu6vZgj8Opei38qPa8pI1RUFoXHFlCe6WpZQmU8efL8gAMrJr9jUIY8eea1n
u20t5B9ueb9JMjrNafcq6QkKhZLi6fRDDTUyeDvc0dN9R/3Yts97SXfdi1/lX7HS
Ht4zXd5hEkvjo8GcnjsfZpAC39QfHWkDaeUGEqsl3jXjVMfkvoVY51OuokPWZzrJ
M5+wyXNpfGbH67dPk7iHgN7VJvgX0SYscDPTtms50Vk7RwEzLeGuSHMCAQI=
-----END DH PARAMETERS-----

View File

@ -7,15 +7,15 @@ server {
error_log /var/log/nginx/mediacms.io.error.log warn; error_log /var/log/nginx/mediacms.io.error.log warn;
# redirect to https if logged in # # redirect to https if logged in
if ($http_cookie ~* "sessionid") { # if ($http_cookie ~* "sessionid") {
rewrite ^/(.*)$ https://localhost/$1 permanent; # rewrite ^/(.*)$ https://localhost/$1 permanent;
} # }
# redirect basic forms to https # # redirect basic forms to https
location ~ (login|login_form|register|mail_password_form)$ { # location ~ (login|login_form|register|mail_password_form)$ {
rewrite ^/(.*)$ https://localhost/$1 permanent; # rewrite ^/(.*)$ https://localhost/$1 permanent;
} # }
location /static { location /static {
alias /home/mediacms.io/mediacms/static ; alias /home/mediacms.io/mediacms/static ;
@ -46,6 +46,12 @@ server {
ssl_certificate_key /etc/letsencrypt/live/localhost/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/localhost/privkey.pem;
ssl_certificate /etc/letsencrypt/live/localhost/fullchain.pem; ssl_certificate /etc/letsencrypt/live/localhost/fullchain.pem;
ssl_dhparam /etc/nginx/dhparams/dhparams.pem;
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_ecdh_curve secp521r1:secp384r1;
ssl_prefer_server_ciphers on;
gzip on; gzip on;
access_log /var/log/nginx/mediacms.io.access.log; access_log /var/log/nginx/mediacms.io.access.log;

View File

@ -19,10 +19,7 @@ http {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log; error_log /var/log/nginx/error.log;

View File

@ -0,0 +1,34 @@
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

@ -0,0 +1,40 @@
#!/bin/bash
# This script builds the video editor package and deploys the frontend assets to the static directory.
# How to run: sh deploy/scripts/build_and_deploy.sh
# Exit on any error
set -e
echo "Starting build process..."
# Build video editor package
echo "Building video editor package..."
cd frontend-tools/video-editor
yarn build:django
cd ../../
# Build chapter editor package
echo "Building chapters editor package..."
cd frontend-tools/chapters-editor
yarn build:django
cd ../../
# Build video js package
echo "Building video js package..."
cd frontend-tools/video-js
yarn build:django
cd ../../
# Run npm build in the frontend container
echo "Building frontend assets..."
docker compose -f docker-compose/docker-compose-dev-updated.yaml exec frontend npm run dist
# Copy static assets to the static directory
echo "Copying static assets..."
cp -r frontend/dist/static/* static/
# Restart the web service
echo "Restarting web service..."
docker compose -f docker-compose/docker-compose-dev-updated.yaml restart web
echo "Build and deployment completed successfully!"

89
docker-compose-dev.yaml Normal file
View File

@ -0,0 +1,89 @@
version: "3"
services:
migrations:
build:
context: .
dockerfile: ./Dockerfile
target: base
args:
- DEVELOPMENT_MODE=True
image: mediacms/mediacms-dev:latest
volumes:
- ./:/home/mediacms.io/mediacms/
command: "./deploy/docker/prestart.sh"
environment:
DEVELOPMENT_MODE: True
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
ADMIN_PASSWORD: 'admin'
restart: on-failure
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
frontend:
image: node:20
volumes:
- ${PWD}/frontend:/home/mediacms.io/mediacms/frontend/
working_dir: /home/mediacms.io/mediacms/frontend/
command: bash -c "npm install && npm run start"
env_file:
- ${PWD}/frontend/.env
ports:
- "8088:8088"
depends_on:
- web
web:
image: mediacms/mediacms-dev:latest
command: "python manage.py runserver 0.0.0.0:80"
environment:
DEVELOPMENT_MODE: True
ports:
- "80:80"
volumes:
- ./:/home/mediacms.io/mediacms/
depends_on:
- migrations
db:
image: postgres:17.2-alpine
volumes:
- ../postgres_data:/var/lib/postgresql/data/
restart: always
environment:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: "redis:alpine"
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
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

5
docker-compose.full.yaml Normal file
View File

@ -0,0 +1,5 @@
version: "3"
services:
celery_worker:
image: mediacms/mediacms:full

View File

@ -2,7 +2,7 @@ version: "3"
services: services:
migrations: migrations:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -11,16 +11,18 @@ services:
ENABLE_CELERY_SHORT: 'no' ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no' ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no' ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
# ADMIN_PASSWORD: 'uncomment_and_set_password_here'
command: "./deploy/docker/prestart.sh"
restart: on-failure
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
db: db:
condition: service_healthy condition: service_healthy
web: web:
build: image: mediacms/mediacms:latest
context: .
target: runtime-image
image: mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
ports: ports:
@ -35,7 +37,7 @@ services:
depends_on: depends_on:
- migrations - migrations
celery_beat: celery_beat:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -47,7 +49,7 @@ services:
depends_on: depends_on:
- redis - redis
celery_worker: celery_worker:
image: mediacms:latest image: mediacms/mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
volumes: volumes:
@ -60,7 +62,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres image: postgres:17.2-alpine
volumes: volumes:
- ../postgres_data:/var/lib/postgresql/data/ - ../postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -68,8 +70,9 @@ 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 -U mediacms"] test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
@ -78,6 +81,6 @@ services:
restart: always restart: always
healthcheck: healthcheck:
test: ["CMD", "redis-cli","ping"] test: ["CMD", "redis-cli","ping"]
interval: 30s interval: 10s
timeout: 10s timeout: 5s
retries: 3 retries: 3

View File

@ -0,0 +1,124 @@
name: mediacms-dev
services:
migrations:
platform: linux/amd64
build:
context: ..
dockerfile: Dockerfile
args:
- DEVELOPMENT_MODE=True
image: mediacms/mediacms:latest
volumes:
- ../:/home/mediacms.io/mediacms/
command: "/home/mediacms.io/mediacms/deploy/docker/prestart.sh"
environment:
DEVELOPMENT_MODE: True
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
ADMIN_PASSWORD: 'admin'
restart: on-failure
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
frontend:
image: node:20
user: "root"
volumes:
- ${PWD}/frontend:/home/mediacms.io/mediacms/frontend/
- frontend_node_modules:/home/mediacms.io/mediacms/frontend/node_modules
- scripts_node_modules:/home/mediacms.io/mediacms/frontend/packages/scripts/node_modules
- npm_cache:/home/node/.npm
working_dir: /home/mediacms.io/mediacms/frontend/
command: >
bash -c "
echo 'Checking dependencies...' &&
if [ ! -f node_modules/.install-complete ]; then
echo 'First-time setup or dependencies changed, installing...' &&
npm install --legacy-peer-deps --cache /home/node/.npm &&
cd packages/scripts &&
npm install --legacy-peer-deps --cache /home/node/.npm &&
npm run build &&
cd ../.. &&
touch node_modules/.install-complete &&
echo 'Dependencies installed successfully'
else
echo 'Dependencies already installed, skipping installation...' &&
if [ ! -d packages/scripts/dist ]; then
echo 'Building scripts package...' &&
cd packages/scripts &&
npm run build &&
cd ../..
fi
fi &&
echo 'Starting development server...' &&
npm run start
"
env_file:
- ${PWD}/frontend/.env
ports:
- "8088:8088"
depends_on:
- web
restart: unless-stopped
web:
platform: linux/amd64
image: mediacms/mediacms:latest
command: "python manage.py runserver 0.0.0.0:80"
environment:
DEVELOPMENT_MODE: True
ports:
- "80:80"
volumes:
- ../:/home/mediacms.io/mediacms/
depends_on:
- migrations
db:
image: postgres:17.2-alpine
volumes:
- ../postgres_data:/var/lib/postgresql/data/
restart: always
environment:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: "redis:alpine"
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
celery_worker:
platform: linux/amd64
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
- ../:/home/mediacms.io/mediacms/
environment:
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_BEAT: 'no'
ENABLE_MIGRATIONS: 'no'
DEVELOPMENT_MODE: True
depends_on:
- web
volumes:
frontend_node_modules:
scripts_node_modules:
npm_cache:

View File

@ -9,7 +9,7 @@ services:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro - ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
migrations: migrations:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -18,16 +18,18 @@ services:
ENABLE_CELERY_SHORT: 'no' ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no' ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no' ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
command: "./deploy/docker/prestart.sh"
restart: on-failure
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
db: db:
condition: service_healthy condition: service_healthy
web: web:
build: image: mediacms/mediacms:latest
context: .
target: runtime-image
image: mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
volumes: volumes:
@ -41,7 +43,7 @@ services:
depends_on: depends_on:
- migrations - migrations
celery_beat: celery_beat:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -53,7 +55,7 @@ services:
depends_on: depends_on:
- redis - redis
celery_worker: celery_worker:
image: mediacms:latest image: mediacms/mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
volumes: volumes:
@ -66,7 +68,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres image: postgres:17.2-alpine
volumes: volumes:
- ../postgres_data/:/var/lib/postgresql/data/ - ../postgres_data/:/var/lib/postgresql/data/
restart: always restart: always
@ -74,8 +76,9 @@ 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 -U mediacms"] test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View File

@ -11,7 +11,7 @@ services:
- ./deploy/docker/reverse_proxy/certs/:/etc/nginx/certs/ - ./deploy/docker/reverse_proxy/certs/:/etc/nginx/certs/
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro - ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
migrations: migrations:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -20,16 +20,15 @@ services:
ENABLE_CELERY_SHORT: 'no' ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no' ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no' ENABLE_CELERY_BEAT: 'no'
command: "./deploy/docker/prestart.sh"
restart: on-failure
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
db: db:
condition: service_healthy condition: service_healthy
web: web:
build: image: mediacms/mediacms:latest
context: .
target: runtime-image
image: mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
volumes: volumes:
@ -39,11 +38,14 @@ services:
ENABLE_CELERY_SHORT: 'no' ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no' ENABLE_CELERY_LONG: 'no'
ENABLE_MIGRATIONS: 'no' ENABLE_MIGRATIONS: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
VIRTUAL_HOST: localhost VIRTUAL_HOST: localhost
depends_on: depends_on:
- migrations - migrations
celery_beat: celery_beat:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./:/home/mediacms.io/mediacms/ - ./:/home/mediacms.io/mediacms/
environment: environment:
@ -55,7 +57,7 @@ services:
depends_on: depends_on:
- redis - redis
celery_worker: celery_worker:
image: mediacms:latest image: mediacms/mediacms:latest
deploy: deploy:
replicas: 2 replicas: 2
volumes: volumes:
@ -68,7 +70,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres image: postgres:17.2-alpine
volumes: volumes:
- ../postgres_data/:/var/lib/postgresql/data/ - ../postgres_data/:/var/lib/postgresql/data/
restart: always restart: always
@ -76,8 +78,9 @@ 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 -U mediacms"] test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View File

@ -0,0 +1,121 @@
version: "3"
# Uses https://github.com/nginx-proxy/acme-companion
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- dhparam:/etc/nginx/dhparam
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
acme-companion:
image: nginxproxy/acme-companion
container_name: nginx-proxy-acme
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
migrations:
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
command: "./deploy/docker/prestart.sh"
restart: on-failure
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
web:
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
ENABLE_CELERY_BEAT: 'no'
ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no'
ENABLE_MIGRATIONS: 'no'
VIRTUAL_HOST: 'mediacms.52.209.5.113.nip.io'
LETSENCRYPT_HOST: 'mediacms.52.209.5.113.nip.io'
LETSENCRYPT_EMAIL: 'email@example.com'
depends_on:
- migrations
celery_beat:
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
ENABLE_UWSGI: 'no'
ENABLE_NGINX: 'no'
ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no'
ENABLE_MIGRATIONS: 'no'
depends_on:
- redis
celery_worker:
image: mediacms/mediacms: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:
- migrations
db:
image: postgres:17.2-alpine
volumes:
- ../postgres_data:/var/lib/postgresql/data/
restart: always
environment:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 30s
timeout: 10s
retries: 5
redis:
image: "redis:alpine"
restart: always
healthcheck:
test: ["CMD", "redis-cli","ping"]
interval: 30s
timeout: 10s
retries: 3
volumes:
conf:
vhost:
html:
dhparam:
certs:
acme:

View File

@ -2,7 +2,7 @@ version: "3"
services: services:
migrations: migrations:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py - ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
environment: environment:
@ -11,16 +11,18 @@ services:
ENABLE_CELERY_SHORT: 'no' ENABLE_CELERY_SHORT: 'no'
ENABLE_CELERY_LONG: 'no' ENABLE_CELERY_LONG: 'no'
ENABLE_CELERY_BEAT: 'no' ENABLE_CELERY_BEAT: 'no'
ADMIN_USER: 'admin'
ADMIN_EMAIL: 'admin@localhost'
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
command: "./deploy/docker/prestart.sh"
restart: on-failure
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
db: db:
condition: service_healthy condition: service_healthy
web: web:
build: image: mediacms/mediacms:latest
context: .
target: runtime-image
image: mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
ports: ports:
@ -37,7 +39,7 @@ services:
depends_on: depends_on:
- migrations - migrations
celery_beat: celery_beat:
image: mediacms:latest image: mediacms/mediacms:latest
volumes: volumes:
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py - ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
environment: environment:
@ -49,7 +51,7 @@ services:
depends_on: depends_on:
- redis - redis
celery_worker: celery_worker:
image: mediacms:latest image: mediacms/mediacms:latest
deploy: deploy:
replicas: 1 replicas: 1
volumes: volumes:
@ -64,7 +66,7 @@ services:
depends_on: depends_on:
- migrations - migrations
db: db:
image: postgres image: postgres:17.2-alpine
volumes: volumes:
- postgres_data:/var/lib/postgresql/data/ - postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -72,8 +74,9 @@ 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 -U mediacms"] test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 5 retries: 5

View File

@ -1,244 +0,0 @@
## Configuration
A number of options are available on `cms/settings.py`.
It is advisable to override any of them by adding it to `cms/local_settings.py` .
Any change needs restart of MediaCMS in order to take effect. So edit `cms/local_settings.py`, make a change and restart MediaCMS
```
#systemctl restart mediacms
```
### change portal logo
Set a new svg file for the white theme (`static/images/logo_dark.svg`) or the dark theme (`static/images/logo_light.svg`)
### set global portal title
set `PORTAL_NAME`, eg
```
PORTAL_NAME = 'my awesome portal'
```
### who can add media
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.
- **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.
### what is the portal workflow
The `PORTAL_WORKFLOW` variable specifies what happens to newly uploaded media, whether they appear on listings (as the index page, or search)
- **public** is the default option and means that a media can appear on listings. If media type is video, it will appear once at least a task that produces an encoded version of the file has finished succesfully. For other type of files, as image/audio they appear instantly
- **private** means that newly uploaded content is private - only users can see it or MediaCMS editors, managers and admins. Those can also set the status to public or unlisted
- **unlisted** means that items are unlisted. However if a user visits the url of an unlisted media, it will be shown (as opposed to private)
### show/hide the Sign in button
to show button:
```
LOGIN_ALLOWED = True
```
to hide button:
```
LOGIN_ALLOWED = False
```
### show/hide the Register button
to show button:
```
REGISTER_ALLOWED = True
```
to hide button:
```
REGISTER_ALLOWED = False
```
### show/hide the upload media button
To show:
```
UPLOAD_MEDIA_ALLOWED = True
```
To hide:
```
UPLOAD_MEDIA_ALLOWED = False
```
### show/hide the actions buttons (like/dislike/report)
Make changes (True/False) to any of the following:
```
- CAN_LIKE_MEDIA = True # whether the like media appears
- CAN_DISLIKE_MEDIA = True # whether the dislike media appears
- CAN_REPORT_MEDIA = True # whether the report media appears
- CAN_SHARE_MEDIA = True # whether the share media appears
```
### automatically hide media upon being reported
set a low number for variable `REPORTED_TIMES_THRESHOLD`
eg
```
REPORTED_TIMES_THRESHOLD = 2
```
once the limit is reached, media goes to private state and an email is sent to admins
### set a custom message on the media upload page
this message will appear below the media drag and drop form
```
PRE_UPLOAD_MEDIA_MESSAGE = 'custom message'
```
### set email settings
Set correct settings per provider
```
DEFAULT_FROM_EMAIL = 'info@mediacms.io'
EMAIL_HOST_PASSWORD = 'xyz'
EMAIL_HOST_USER = 'info@mediacms.io'
EMAIL_USE_TLS = True
SERVER_EMAIL = DEFAULT_FROM_EMAIL
EMAIL_HOST = 'mediacms.io'
EMAIL_PORT = 587
ADMIN_EMAIL_LIST = ['info@mediacms.io']
```
### disallow user registrations from specific domains
set domains that are not valid for registration via this variable:
```
RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = [
'xxx.com', 'emaildomainwhatever.com']
```
### require a review by MediaCMS editors/managers/admins
set value
```
MEDIA_IS_REVIEWED = False
```
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
### specify maximum number of media for a playlist
set a different threshold on variable `MAX_MEDIA_PER_PLAYLIST`
eg
```
MAX_MEDIA_PER_PLAYLIST = 14
```
### specify maximum size of a media that can be uploaded
change `UPLOAD_MAX_SIZE`.
default is 4GB
```
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
```
### specify maximum size of comments
change `MAX_CHARS_FOR_COMMENT`
default:
```
MAX_CHARS_FOR_COMMENT = 10000
```
### how many files to upload in parallel
set a different threshold for `UPLOAD_MAX_FILES_NUMBER`
default:
```
UPLOAD_MAX_FILES_NUMBER = 100
```
### force users confirm their email upon registrations
default option for email confirmation is optional. Set this to mandatory in order to force users confirm their email before they can login
```
ACCOUNT_EMAIL_VERIFICATION = 'optional'
```
### rate limit account login attempts
after this number is reached
```
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 20
```
sets a timeout (in seconds)
```
ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 5
```
### disallow user registration
set the following variable to False
```
USERS_CAN_SELF_REGISTER = True
```
### configure notifications
Global notifications that are implemented are controlled by the following options:
```
USERS_NOTIFICATIONS = {
'MEDIA_ADDED': True,
}
```
If you want to disable notification for new media, set to False
Admins also receive notifications on different events, set any of the following to False to disable
```
ADMINS_NOTIFICATIONS = {
'NEW_USER': True,
'MEDIA_ADDED': True,
'MEDIA_REPORTED': True,
}
```
- NEW_USER: a new user is added
- MEDIA_ADDED: a media is added
- MEDIA_REPORTED: the report for a media was hit

View File

@ -1,40 +0,0 @@
# MediaCMS on Docker
The mediacms image is built to use supervisord as the main process, which manages one or more services required to run mediacms. We can toggle which services are run in a given container by setting the environment variables below to `yes` or `no`:
* ENABLE_UWSGI
* ENABLE_NGINX
* ENABLE_CELERY_BEAT
* ENABLE_CELERY_SHORT
* ENABLE_CELERY_LONG
* ENABLE_MIGRATIONS
By default, all these services are enabled, but in order to create a scaleable deployment, some of them can be disabled, splitting the service up into smaller services.
Also see the `Dockerfile` for other environment variables which you may wish to override. Application settings, eg. `FRONTEND_HOST` can also be overridden by updating the `deploy/docker/local_settings.py` file.
See example deployments in the sections below. These example deployments have been tested on `docker-compose version 1.27.4` running on `Docker version 19.03.13`
To run, update the configs above if necessary, build the image by running `docker-compose build`, then run `docker-compose run`
## Simple Deployment, accessed as http://localhost
The main container runs migrations, mediacms_web, celery_beat, celery_workers (celery_short and celery_long services), exposed on port 80 supported by redis and postgres database. The FRONTEND_HOST in `deploy/docker/local_settings.py` is configured as http://localhost, on the docker host machine.
## Advanced Deployment, accessed as http://localhost:8000
Here we can run 1 mediacms_web instance, with the FRONTEND_HOST in `deploy/docker/local_settings.py` configured as http://localhost:8000. This is bootstrapped by a single migrations instance and supported by a single celery_beat instance and 1 or more celery_worker instances. Redis and postgres containers are also used for persistence. Clients can access the service on http://localhost:8000, on the docker host machine. This is similar to [this deployment](../docker-compose.yaml), with a `port` defined in FRONTEND_HOST.
## Advanced Deployment, with reverse proxy, accessed as http://mediacms.io
Here we can use `jwilder/nginx-proxy` to reverse proxy to 1 or more instances of mediacms_web supported by other services as mentioned in the previous deployment. The FRONTEND_HOST in `deploy/docker/local_settings.py` is configured as http://mediacms.io, nginx-proxy has port 80 exposed. Clients can access the service on http://mediacms.io (Assuming DNS or the hosts file is setup correctly to point to the IP of the nginx-proxy instance). This is similar to [this deployment](../docker-compose-http-proxy.yaml).
## Advanced Deployment, with reverse proxy, accessed as https://localhost
The reverse proxy (`jwilder/nginx-proxy`) can be configured to provide SSL termination using self-signed certificates, letsencrypt or CA signed certificates (see: https://hub.docker.com/r/jwilder/nginx-proxy or [LetsEncrypt Example](https://www.singularaspect.com/use-nginx-proxy-and-letsencrypt-companion-to-host-multiple-websites/) ). In this case the FRONTEND_HOST should be set to https://mediacms.io. This is similar to [this deployment](../docker-compose-http-proxy.yaml).
## A Scaleable Deployment Architecture (Docker, Swarm, Kubernetes)
The architecture below generalises all the deployment scenarios above, and provides a conceptual design for other deployments based on kubernetes and docker swarm. It allows for horizontal scaleability through the use of multiple mediacms_web instances and celery_workers. For large deployments, managed postgres, redis and storage may be adopted.
![MediaCMS](images/architecture.png)

View File

@ -1,20 +0,0 @@
## User scenarios to test
## test video media + image
try uploading a video + image, make sure they get encoded well and check they appear on index/search/category/author page
try editing/setting metadata, confirm action is performed, also that are searchable
try adding custom poster, confirm it loads well on video page/listings
try specifying different thumbnail time, confirm an automatic screenshot is taken
## portal workflow
change workflow to unlisted, check they don't appear on index/search/category/author page
## users management
create an admin, a MediaCMS editor and MediaCMS manager. All should see edit/delete on a media and also comments, and action should work.
For users edit and delete, only MediaCMS manager and admin should see edit/delete and these actions should work.
## test subtitle
add language and test subtitling

1040
docs/admins_docs.md Normal file

File diff suppressed because it is too large Load Diff

89
docs/dev_exp.md Normal file
View File

@ -0,0 +1,89 @@
# 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.
If Django breaks due to an error (eg SyntaxError, while editing the code), you might have to restart it
```
docker compose -f docker-compose-dev.yaml restart web
```
### 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
### Helper commands
There is ongoing effort to provide helper commands, check the Makefile for what it supports. Eg
Bash into the web container:
```
user@user:~/mediacms$ make admin-shell
root@ca8c1096726b:/home/mediacms.io/mediacms# ./manage.py shell
```
Build the frontend:
```
user@user:~/mediacms$ make build-frontend
docker compose -f docker-compose-dev.yaml exec frontend npm run dist
> mediacms-frontend@0.9.1 dist /home/mediacms.io/mediacms/frontend
> mediacms-scripts rimraf ./dist && mediacms-scripts build --config=./config/mediacms.config.js --env=dist
...
```

155
docs/developers_docs.md Normal file
View File

@ -0,0 +1,155 @@
# Developers documentation
## Table of contents
- [1. Welcome](#1-welcome)
- [2. System architecture](#2-system-architecture)
- [3. API documentation](#3-api-documentation)
- [4. How to contribute](#4-how-to-contribute)
- [5. Working with Docker tips](#5-working-with-docker-tips)
- [6. Working with the automated tests](#6-working-with-the-automated-tests)
- [7. How video is transcoded](#7-how-video-is-transcoded)
## 1. Welcome
This page is created for MediaCMS developers and contains related information.
## 2. System architecture
to be written
## 3. API documentation
API is documented using Swagger - checkout ot http://your_installation/swagger - example https://demo.mediacms.io/swagger/
This page allows you to login to perform authenticated actions - it will also use your session if logged in.
An example of working with Python requests library:
```
import requests
auth = ('user' ,'password')
upload_url = "https://domain/api/v1/media"
title = 'x title'
description = 'x description'
media_file = '/tmp/file.mp4'
requests.post(
url=upload_url,
files={'media_file': open(media_file,'rb')},
data={'title': title, 'description': description},
auth=auth
)
```
## 4. How to contribute
Before you send a PR, make sure your code is properly formatted. For that, use `pre-commit install` to install a pre-commit hook and run `pre-commit run --all` and fix everything before you commit. This pre-commit will check for your code lint everytime you commit a code.
Checkout the [Code of conduct page](../CODE_OF_CONDUCT.md) if you want to contribute to this repository
## 5. Working with Docker tips
To perform the Docker installation, follow instructions to install Docker + Docker compose (docs/Docker_Compose.md) and then build/start docker-compose-dev.yaml . This will run the frontend application on port 8088 on top of all other containers (including the Django web application on port 80)
```
docker compose -f docker-compose-dev.yaml build
docker compose -f docker-compose-dev.yaml up
```
An `admin` user is created during the installation process. Its attributes are defined in `docker-compose-dev.yaml`:
```
ADMIN_USER: 'admin'
ADMIN_PASSWORD: 'admin'
ADMIN_EMAIL: 'admin@localhost'
```
### Frontend application changes
Eg change `frontend/src/static/js/pages/HomePage.tsx` , dev application refreshes in a number of seconds (hot reloading) and I see the changes, once I'm happy I can run
```
docker compose -f docker-compose-dev.yaml exec -T frontend npm run dist
```
And then in order for the changes to be visible on the application while served through nginx,
```
cp -r frontend/dist/static/* static/
```
POST calls: cannot be performed through the dev server, you have to make through the normal application (port 80) and then see changes on the dev application on port 8088.
Make sure the urls are set on `frontend/.env` if different than localhost
Media page: need to upload content through the main application (nginx/port 80), and then use an id for page media.html, for example `http://localhost:8088/media.html?m=nc9rotyWP`
There are some issues with CORS too to resolve, in order for some pages to function, eg the manage comments page
```
http://localhost:8088/manage-media.html manage_media
```
### Backend application changes
After I make changes to the django application (eg make a change on `files/forms.py`) in order to see the changes I have to restart the web container
```
docker compose -f docker-compose-dev.yaml restart web
```
## How video is transcoded
Original files get uploaded to the application server, and they get stored there as FileFields.
If files are videos and the duration is greater than a number (defined on settings, I think 4minutes), they are also broken in chunks, so one Encode object per chunk, for all enabled EncodeProfiles.
Then the workers start picking Encode objects and they transcode the chunks, so if a chunk gets transcoded correctly, the original file (the small chunk) gets replaced by the transcoded file, and the Encode object status is marked as 'success'.
original.mp4 (1G, 720px)--> Encode1 (100MB, 240px, chunk=True), Encode2 (100MB, 240px, chunk=True)...EncodeXX (100MB, 720px, chunk=True) ---> when all Encode objects are success, for a resolution, they get concatenated to the original_resolution.mp4 file and this gets stored as Encode object (chunk=False). This is what is available for download.
Apparently the Encode object is used to store Encoded files that are served eventually (chunk=False, status='success'), but also files while they are on their way to get transcoded (chunk=True, status='pending/etc')
(Parenthesis opening)
there is also an experimental small service (not commited to the repo currently) that speaks only through API and a) gets tasks to run, b) returns results. So it makes a request and receives an ffmpeg command, plus a file, it runs the ffmpeg command, and returns the result.I've used this mechanism on a number of installations to migrate existing videos through more servers/cpu and has worked with only one problem, some temporary files needed to be removed from the servers (through a periodic task, not so big problem)
(Parenthesis closing)
When the Encode object is marked as success and chunk=False, and thus is available for download/stream, there is a task that gets started and saves an HLS version of the file (1 mp4-->x number of small .ts chunks). This would be FILES_C
This mechanism allows for workers that have access on the same filesystem (either localhost, or through a shared network filesystem, eg NFS/EFS) to work on the same time and produce results.
## 6. Working with the automated tests
This instructions assume that you're using the docker installation
1. start docker-compose
```
docker compose up
```
2. Install the requirements on `requirements-dev.txt ` on web container (we'll use the web container for this)
```
docker compose exec -T web pip install -r requirements-dev.txt
```
3. Now you can run the existing tests
```
docker compose exec --env TESTING=True -T web pytest
```
The `TESTING=True` is passed for Django to be aware this is a testing environment (so that it runs Celery tasks as functions for example and not as background tasks, since Celery is not started in the case of pytest)
4. You may try a single test, by specifying the path, for example
```
docker compose exec --env TESTING=True -T web pytest tests/test_fixtures.py
```
5. You can also see the coverage
```
docker compose exec --env TESTING=True -T web pytest --cov=. --cov-report=html
```
and of course...you are very welcome to help us increase it ;)

BIN
docs/images/CC-display.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 KiB

BIN
docs/images/Click-ADD-button.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/images/Continue-button.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/images/Demo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

BIN
docs/images/Demo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/images/Demo3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/images/Mention1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
docs/images/Mention2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
docs/images/Mention3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/images/Mention4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/images/Pause-button.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show More