Compare commits

...

139 Commits

Author SHA1 Message Date
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 43b76932c5.

* 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
729 changed files with 263319 additions and 33057 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'

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

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

15
.github/workflows/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

6
.gitignore vendored
View File

@@ -1,6 +1,10 @@
cli-tool/.env
frontend/package-lock.json
media_files/encoded/
media_files/original/
media_files/hls/
media_files/chunks/
media_files/uploads/
postgres_data/
celerybeat-schedule
logs/
@@ -10,4 +14,6 @@ static/ckeditor/
static/debug_toolbar/
static/mptt/
static/rest_framework/
static/drf-yasg
cms/local_settings.py
deploy/docker/local_settings.py

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' ]

View File

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

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,4 +1,4 @@
FROM python:3.8-buster AS compile-image
FROM python:3.11.4-bookworm AS compile-image
SHELL ["/bin/bash", "-c"]
@@ -7,7 +7,7 @@ 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
RUN mkdir -p /home/mediacms.io/mediacms/{logs} && cd /home/mediacms.io && python3 -m venv $VIRTUAL_ENV
# Install dependencies:
COPY requirements.txt .
@@ -16,21 +16,18 @@ 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 && \
unzip Bento4-SDK-1-6-0-632.x86_64-unknown-linux.zip -d ../bento4 && \
mv ../bento4/Bento4-SDK-1-6-0-632.x86_64-unknown-linux/* ../bento4/ && \
rm -rf ../bento4/Bento4-SDK-1-6-0-632.x86_64-unknown-linux && \
RUN wget -q http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip && \
unzip Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip -d ../bento4 && \
mv ../bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/* ../bento4/ && \
rm -rf ../bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux && \
rm -rf ../bento4/docs && \
rm Bento4-SDK-1-6-0-632.x86_64-unknown-linux.zip
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
############ RUNTIME IMAGE ############
FROM python:3.8-slim-buster as runtime-image
FROM python:3.11.4-bookworm as runtime-image
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
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'
@@ -47,14 +44,20 @@ ENV ENABLE_MIGRATIONS='yes'
ENV VIRTUAL_ENV=/home/mediacms.io
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY --from=compile-image /home/mediacms.io /home/mediacms.io
COPY --chown=www-data:www-data --from=compile-image /home/mediacms.io /home/mediacms.io
RUN apt-get update -y && apt-get -y upgrade && apt-get install --no-install-recommends \
supervisor nginx ffmpeg imagemagick procps -y && \
supervisor nginx imagemagick procps wget xz-utils -y && \
rm -rf /var/lib/apt/lists/* && \
apt-get purge --auto-remove && \
apt-get clean
RUN wget -q https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \
mkdir -p ffmpeg-tmp && \
tar -xf ffmpeg-release-amd64-static.tar.xz --strip-components 1 -C ffmpeg-tmp && \
cp -v ffmpeg-tmp/ffmpeg ffmpeg-tmp/ffprobe ffmpeg-tmp/qt-faststart /usr/local/bin && \
rm -rf ffmpeg-tmp ffmpeg-release-amd64-static.tar.xz
WORKDIR /home/mediacms.io/mediacms
EXPOSE 9000 80

71
Dockerfile-dev Normal file
View File

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

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)

129
README.md
View File

@@ -1,4 +1,10 @@
![MediaCMS](static/images/logo_dark.png)
# MediaCMS
[![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.
@@ -9,18 +15,11 @@ A demo is available at https://demo.mediacms.io
## Screenshots
![MediaCMS](docs/images/index.jpg)
Vanilla MediaCMS index page
![MediaCMS](docs/images/video.jpg)
Video page with player different options
![MediaCMS](docs/images/embed.jpg)
Embed video page
<p align="center">
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/index.jpg" width="340">
<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">
</p>
## Features
- **Complete control over your data**: host it yourself!
@@ -41,6 +40,7 @@ Embed video page
- **Subtitles/CC**: support for multilingual subtitle files
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
- **Chunked file uploads**: for pausable/resumable upload of content
- **REST API**: Documented through Swagger
## Example cases
@@ -80,113 +80,44 @@ For a small to medium installation, with a few hours of video uploaded daily, an
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).
## Installation
## Releases
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.
### Docker Compose installation
Install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
Run as root
```bash
git clone https://github.com/mediacms-io/mediacms
cd mediacms
```
The default option to serve MediaCMS is on http://localhost. If you want to set a url and have it served there, set the `FRONTEND_HOST` variable on file `deploy/docker/local_settings.py`.
Now run
```bash
docker-compose build && docker-compose up
```
This will build an image, download and setup necessary Docker images and start all containers. Once it finishes, MediaCMS will be installed and available on http://localhost (or the url you've set as `FRONTEND_HOST` on file `deploy/docker/local_settings.py`)
For more instructions, checkout the docs on the [Docker deployment](docs/Docker_deployment.md) page. Docker Compose support has been contributed by @swiftugandan.
Visit [Releases Page](https://github.com/mediacms-io/mediacms/releases) for detailed Changelog
### Single server installation
## Installation / Maintanance
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.
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:
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.
* [Single Server](docs/admins_docs.md#2-server-installation) page
* [Docker Compose](docs/admins_docs.md#3-docker-installation) page
Automated script - to run on Ubuntu 18 or Ubuntu 20 flavors only!
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
mkdir /home/mediacms.io && cd /home/mediacms.io/
git clone https://github.com/mediacms-io/mediacms
cd /home/mediacms.io/mediacms/ && bash ./install.sh
```
## Configuration
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.
Visit [Configuration](docs/admins_docs.md#5-configuration) page.
## Update
## Documentation
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.
* [Users documentation](docs/user_docs.md) page
* [Administrators documentation](docs/admins_docs.md) page
* [Developers documentation](docs/developers_docs.md) page
## Technology
This software uses the following list of awesome technologies:
- Python
- Django
- Django Rest Framework
- Celery
- PostgreSQL
- Redis
- Nginx
- uWSGI
- React
- Fine Uploader
- video.js
- FFMPEG
- Bento4
This software uses the following list of awesome technologies: Python, Django, Django Rest Framework, Celery, PostgreSQL, Redis, Nginx, uWSGI, React, Fine Uploader, video.js, FFMPEG, Bento4
## Who is using it
- **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
- **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
If you like the project, here's a few things you can do
@@ -198,5 +129,7 @@ If you like the project, here's a few things you can do
- Star the project
- Add functionality, work on a PR, fix an issue!
## Contact
info@mediacms.io

View File

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

View File

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

View File

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

View File

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

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("{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 .celery import app as celery_app
__all__ = ["celery_app"]

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")
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
# 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

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 django.core.paginator import Paginator
from django.utils.functional import cached_property
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class FasterDjangoPaginator(Paginator):
@@ -17,7 +18,6 @@ class FastPaginationWithoutCount(PageNumberPagination):
django_paginator_class = FasterDjangoPaginator
def get_paginated_response(self, data):
return Response(
OrderedDict(
[

View File

@@ -1,5 +1,6 @@
from django.conf import settings
from rest_framework import permissions
from files.methods import is_mediacms_editor, is_mediacms_manager
@@ -24,7 +25,10 @@ class IsUserOrManager(permissions.BasePermission):
if is_mediacms_manager(request.user):
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):

View File

@@ -1,4 +1,5 @@
import os
from celery.schedules import crontab
DEBUG = False
@@ -6,6 +7,7 @@ DEBUG = False
# PORTAL NAME, this is the portal title and
# is also shown on several places as emails
PORTAL_NAME = "MediaCMS"
PORTAL_DESCRIPTION = ""
LANGUAGE_CODE = "en-us"
TIME_ZONE = "Europe/London"
@@ -16,7 +18,8 @@ CAN_ADD_MEDIA = "all"
# valid choices here are 'public', 'private', 'unlisted
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
@@ -42,7 +45,11 @@ ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY = True
# ip of the server should be part of this
ALLOWED_HOSTS = ["*", "mediacms.io", "127.0.0.1", "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
# there's a conversion to https with the SSL_FRONTEND_HOST env
INTERNAL_IPS = "127.0.0.1"
@@ -80,6 +87,8 @@ MAX_MEDIA_PER_PLAYLIST = 70
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
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
RELATED_MEDIA_STRATEGY = "content"
@@ -140,10 +149,13 @@ STATIC_ROOT = BASE_DIR + "/static/"
# where uploaded + encoded media are stored
MEDIA_ROOT = BASE_DIR + "/media_files/"
MEDIA_UPLOAD_DIR = os.path.join(MEDIA_ROOT, "original/")
MEDIA_ENCODING_DIR = os.path.join(MEDIA_ROOT, "encoded/")
THUMBNAIL_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "thumbnails/")
SUBTITLES_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "subtitles/")
# these used to be os.path.join(MEDIA_ROOT, "folder/") but update to
# Django 3.1.9 requires not absolute paths to be utilized...
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/")
FFMPEG_COMMAND = "ffmpeg" # this is the path
@@ -208,9 +220,7 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = ""
CANNOT_ADD_MEDIA_MESSAGE = ""
# mp4hls command, part of Bendo4
MP4HLS_COMMAND = (
"/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/mp4hls"
)
MP4HLS_COMMAND = "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls"
# highly experimental, related with remote workers
ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24"
@@ -288,6 +298,7 @@ INSTALLED_APPS = [
"uploader.apps.UploaderConfig",
"djcelery_email",
"ckeditor",
"drf_yasg",
]
MIDDLEWARE = [
@@ -344,6 +355,16 @@ FILE_UPLOAD_HANDLERS = [
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 = {
"version": 1,
"disable_existing_loggers": False,
@@ -351,7 +372,7 @@ LOGGING = {
"file": {
"level": "ERROR",
"class": "logging.FileHandler",
"filename": os.path.join(LOGS_DIR, "debug.log"),
"filename": error_filename,
},
},
"loggers": {
@@ -419,11 +440,23 @@ CELERY_BEAT_SCHEDULE = {
# TODO: beat, delete chunks from media root
# chunks_dir after xx days...(also uploads_dir)
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
try:
# 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.append(FRONTEND_HOST.replace("http://", "").replace("https://", ""))
@@ -434,9 +467,26 @@ except ImportError:
if "http" not in FRONTEND_HOST:
# FRONTEND_HOST needs a http:// preffix
FRONTEND_HOST = f"http://{FRONTEND_HOST}"
FRONTEND_HOST = f"http://{FRONTEND_HOST}" # noqa
if LOCAL_INSTALL:
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
else:
SSL_FRONTEND_HOST = FRONTEND_HOST
if GLOBAL_LOGIN_REQUIRED:
# this should go after the AuthenticationMiddleware middleware
MIDDLEWARE.insert(5, "login_required.middleware.LoginRequiredMiddleware")
LOGIN_REQUIRED_IGNORE_PATHS = [
r'/accounts/login/$',
r'/accounts/logout/$',
r'/accounts/signup/$',
r'/accounts/password/.*/$',
r'/accounts/confirm-email/.*/$',
r'/api/v[0-9]+/',
]
# if True, only show original, don't perform any action on videos
DO_NOT_TRANSCODE_VIDEO = False
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

View File

@@ -1,13 +1,31 @@
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
import debug_toolbar
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,),
)
urlpatterns = [
url(r"^__debug__/", include(debug_toolbar.urls)),
url(r"^", include("files.urls")),
url(r"^", include("users.urls")),
url(r"^accounts/", include("allauth.urls")),
url(r"^api-auth/", include("rest_framework.urls")),
re_path(r"^__debug__/", include(debug_toolbar.urls)),
path(
"robots.txt",
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
),
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("admin/", 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'),
]

5
conftest.py Normal file
View File

@@ -0,0 +1,5 @@
from pytest_factoryboy import register
from tests.users.factories import UserFactory
register(UserFactory)

View File

@@ -4,15 +4,14 @@ set -e
# 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/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
mkdir -p /home/mediacms.io/mediacms/{logs,pids,media_files/hls}
mkdir -p /home/mediacms.io/mediacms/{logs,media_files/hls}
touch /home/mediacms.io/mediacms/logs/debug.log
# Remove any dangling pids
rm -rf /home/mediacms.io/mediacms/pids/*
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/)
@@ -28,6 +27,9 @@ else
usermod -a -G $GROUP www-data
fi
# We should do this only for folders that have a different owner, since it is an expensive operation
find /home/mediacms.io/ ! \( -user www-data -group $TARGET_GID \) -exec chown www-data:$TARGET_GID {} +
chmod +x /home/mediacms.io/mediacms/deploy/docker/start.sh /home/mediacms.io/mediacms/deploy/docker/prestart.sh
exec "$@"

View File

@@ -29,8 +29,6 @@ CACHES = {
BROKER_URL = REDIS_LOCATION
CELERY_RESULT_BACKEND = BROKER_URL
MP4HLS_COMMAND = (
"/home/mediacms.io/bento4/bin/mp4hls"
)
MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls"
DEBUG = False

View File

@@ -16,6 +16,10 @@ server {
location /media {
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 / {

View File

@@ -4,20 +4,29 @@ RANDOM_ADMIN_PASS=`python -c "import secrets;chars = 'abcdefghijklmnopqrstuvwxyz
ADMIN_PASSWORD=${ADMIN_PASSWORD:-$RANDOM_ADMIN_PASS}
if [ X"$ENABLE_MIGRATIONS" = X"yes" ]; then
echo "Running migrations service"
python manage.py migrate
python manage.py loaddata fixtures/encoding_profiles.json
python manage.py loaddata fixtures/categories.json
EXISTING_INSTALLATION=`echo "from users.models import User; print(User.objects.exists())" |python manage.py shell`
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
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 ..."
# 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

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
priority=300
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
startinorder=true
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
priority=400
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
need-app = true
die-on-term = true
buffer-size=32768

View File

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

View File

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

View File

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

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;
# redirect to https if logged in
if ($http_cookie ~* "sessionid") {
rewrite ^/(.*)$ https://localhost/$1 permanent;
}
# # redirect to https if logged in
# if ($http_cookie ~* "sessionid") {
# rewrite ^/(.*)$ https://localhost/$1 permanent;
# }
# redirect basic forms to https
location ~ (login|login_form|register|mail_password_form)$ {
rewrite ^/(.*)$ https://localhost/$1 permanent;
}
# # redirect basic forms to https
# location ~ (login|login_form|register|mail_password_form)$ {
# rewrite ^/(.*)$ https://localhost/$1 permanent;
# }
location /static {
alias /home/mediacms.io/mediacms/static ;
@@ -46,6 +46,12 @@ server {
ssl_certificate_key /etc/letsencrypt/live/localhost/privkey.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;
access_log /var/log/nginx/mediacms.io.access.log;

View File

@@ -19,10 +19,7 @@ http {
include /etc/nginx/mime.types;
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;
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
disable-logging = true
buffer-size=32768

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

@@ -0,0 +1,56 @@
version: "3"
services:
frontend:
image: node:14
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:
build:
context: .
dockerfile: ./Dockerfile-dev
image: mediacms/mediacms-dev:latest
environment:
ADMIN_USER: 'admin'
ADMIN_PASSWORD: 'admin'
ADMIN_EMAIL: 'admin@localhost'
ports:
- "80:80"
volumes:
- ./:/home/mediacms.io/mediacms/
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
db:
image: postgres:15.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", "--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

View File

@@ -9,7 +9,7 @@ services:
- /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
migrations:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
@@ -18,16 +18,18 @@ services:
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:
build:
context: .
target: runtime-image
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
@@ -41,7 +43,7 @@ services:
depends_on:
- migrations
celery_beat:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
@@ -53,7 +55,7 @@ services:
depends_on:
- redis
celery_worker:
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
@@ -66,7 +68,7 @@ services:
depends_on:
- migrations
db:
image: postgres
image: postgres:15.2-alpine
volumes:
- ../postgres_data/:/var/lib/postgresql/data/
restart: always
@@ -74,8 +76,9 @@ services:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mediacms"]
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5

View File

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

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:15.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", "--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:
migrations:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
environment:
@@ -11,16 +11,18 @@ services:
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:
build:
context: .
target: runtime-image
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
ports:
@@ -37,7 +39,7 @@ services:
depends_on:
- migrations
celery_beat:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
environment:
@@ -49,7 +51,7 @@ services:
depends_on:
- redis
celery_worker:
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
@@ -64,7 +66,7 @@ services:
depends_on:
- migrations
db:
image: postgres
image: postgres:15.2-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
restart: always
@@ -72,8 +74,9 @@ services:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mediacms"]
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 30s
timeout: 10s
retries: 5

View File

@@ -2,7 +2,7 @@ version: "3"
services:
migrations:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
@@ -11,16 +11,18 @@ services:
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:
build:
context: .
target: runtime-image
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
ports:
@@ -35,7 +37,7 @@ services:
depends_on:
- migrations
celery_beat:
image: mediacms:latest
image: mediacms/mediacms:latest
volumes:
- ./:/home/mediacms.io/mediacms/
environment:
@@ -47,7 +49,7 @@ services:
depends_on:
- redis
celery_worker:
image: mediacms:latest
image: mediacms/mediacms:latest
deploy:
replicas: 1
volumes:
@@ -60,7 +62,7 @@ services:
depends_on:
- migrations
db:
image: postgres
image: postgres:15.2-alpine
volumes:
- ../postgres_data:/var/lib/postgresql/data/
restart: always
@@ -68,8 +70,9 @@ services:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: mediacms
POSTGRES_DB: mediacms
TZ: Europe/London
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mediacms"]
test: ["CMD-SHELL", "pg_isready", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
@@ -80,4 +83,4 @@ services:
test: ["CMD", "redis-cli","ping"]
interval: 30s
timeout: 10s
retries: 3
retries: 3

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

773
docs/admins_docs.md Normal file
View File

@@ -0,0 +1,773 @@
# Administrators documentation
## Table of contents
- [1. Welcome](#1-welcome)
- [2. Server Installaton](#2-server-installation)
- [3. Docker Installation](#3-docker-installation)
- [4. Docker Deployment options](#4-docker-deployment-options)
- [5. Configuration](#5-configuration)
- [6. Manage pages](#6-manage-pages)
- [7. Django admin dashboard](#7-django-admin-dashboard)
- [8. On portal workflow](#8-on-portal-workflow)
- [9. On user roles](#9-on-user-roles)
- [10. Adding languages for Captions and subtitles](#10-adding-languages-for-captions-and-subtitles)
- [11. Add/delete categories and tags](#11-adddelete-categories-and-tags)
- [12. Video transcoding](#12-video-transcoding)
- [13. How To Add A Static Page To The Sidebar](#13-how-to-add-a-static-page-to-the-sidebar)
- [14. Add Google Analytics](#14-add-google-analytics)
- [15. Debugging email issues](#15-debugging-email-issues)
- [16. Frequently Asked Questions](#16-frequently-asked-questions)
- [17. Cookie consent code](#17-cookie-consent-code)
- [18. Disable encoding and show only original file](#18-disable-encoding-and-show-only-original-file)
## 1. Welcome
This page is created for MediaCMS administrators that are responsible for setting up the software, maintaining it and making modifications.
## 2. Server Installation
The core dependencies are Python3, Django3, Celery, PostgreSQL, Redis, ffmpeg. Any system that can have these dependencies installed, can run MediaCMS. But we strongly suggest installing on Linux Ubuntu (tested on versions 20, 22).
Installation on an Ubuntu system with git utility installed should be completed in a few minutes with the following steps.
Make sure you run it as user root, on a clear system, since the automatic script will install and configure the following services: Celery/PostgreSQL/Redis/Nginx and will override any existing settings.
Automated script - tested on Ubuntu 20, Ubuntu 22 and Debian Buster
```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
pip install -r requirements.txt -U # run pip install to update
python manage.py migrate # run Django migrations
sudo systemctl restart mediacms celery_long celery_short # restart services
```
### Update from version 2 to version 3
Version 3 is using Django 4 and Celery 5, and needs a recent Python 3.x version. If you are updating from an older version, make sure Python is updated first. Version 2 could run on Python 3.6, but version 3 needs Python3.8 and higher.
The syntax for starting Celery has also changed, so you have to copy the celery related systemctl files and restart
```
# cp deploy/local_install/celery_long.service /etc/systemd/system/celery_long.service
# cp deploy/local_install/celery_short.service /etc/systemd/system/celery_short.service
# cp deploy/local_install/celery_beat.service /etc/systemd/system/celery_beat.service
# systemctl daemon-reload
# systemctl start celery_long celery_short celery_beat
```
### Configuration
Checkout the configuration section here.
### Maintenance
Database can be backed up with pg_dump and media_files on /home/mediacms.io/mediacms/media_files include original files and encoded/transcoded versions
## 3. Docker Installation
## Installation
Install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
For Ubuntu 20/22 systems this is:
```bash
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```
Then run as root
```bash
git clone https://github.com/mediacms-io/mediacms
cd mediacms
```
The default option is to serve MediaCMS on all ips available of the server (including localhost).
If you want to explore more options (including setup of https with letsencrypt certificate) checkout [Docker deployment](/docs/admins_docs.md#4-docker-deployment-options) section for different docker-compose setups to use.
Run
```bash
docker-compose up
```
This will download all MediaCMS related Docker images and start all containers. Once it finishes, MediaCMS will be installed and available on http://localhost or http://ip
A user admin has been created with random password, you should be able to see it at the end of migrations container, eg
```
migrations_1 | Created admin user with password: gwg1clfkwf
```
or if you have set the ADMIN_PASSWORD variable on docker-compose file you have used (example `docker-compose.yaml`), that variable will be set as the admin user's password
### Update
Get latest MediaCMS image and stop/start containers
```bash
cd /path/to/mediacms/installation
docker pull mediacms/mediacms
docker-compose down
docker-compose up
```
### Update from version 2 to version 3
Version 3 is using Python 3.11 and PostgreSQL 15. If you are updating from an older version, that was using PostgreSQL 13, the automatic update will not work, as you will receive the following message when the PostgreSQL container starts:
```
db_1 | 2023-06-27 11:07:42.959 UTC [1] FATAL: database files are incompatible with server
db_1 | 2023-06-27 11:07:42.959 UTC [1] DETAIL: The data directory was initialized by PostgreSQL version 13, which is not compatible with this version 15.2.
```
At this point there are two options: either edit the Docker Compose file and make use of the existing postgres:13 image, or otherwise you have to perform the migration from postgresql 13 to version 15. More notes on https://github.com/mediacms-io/mediacms/pull/749
## Configuration
Checkout the configuration docs here.
### Maintenance
Database is stored on ../postgres_data/ and media_files on media_files/
## 4. Docker Deployment options
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.
### Server with ssl certificate through letsencrypt service, accessed as https://my_domain.com
Before trying this out make sure the ip points to my_domain.com.
With this method [this deployment](../docker-compose-letsencrypt.yaml) is used.
Edit this file and set `VIRTUAL_HOST` as my_domain.com, `LETSENCRYPT_HOST` as my_domain.com, and your email on `LETSENCRYPT_EMAIL`
Edit `deploy/docker/local_settings.py` and set https://my_domain.com as `FRONTEND_HOST`
Now run docker-compose -f docker-compose-letsencrypt.yaml up, when installation finishes you will be able to access https://my_domain.com using a valid Letsencrypt certificate!
### 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)
## 5. Configuration
Several options are available on `cms/settings.py`, most of the things that are allowed or should be disallowed are described there.
It is advisable to override any of them by adding it to `local_settings.py` .
In case of a the single server installation, add to `cms/local_settings.py` .
In case of a docker compose installation, add to `deploy/docker/local_settings.py` . This will automatically overwrite `cms/local_settings.py` .
Any change needs restart of MediaCMS in order to take effect.
Single server installation: edit `cms/local_settings.py`, make a change and restart MediaCMS
```bash
#systemctl restart mediacms
```
Docker Compose installation: edit `deploy/docker/local_settings.py`, make a change and restart MediaCMS containers
```bash
#docker-compose restart web celery_worker celery_beat
```
### 5.1 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`)
### 5.2 Set global portal title
set `PORTAL_NAME`, eg
```
PORTAL_NAME = 'my awesome portal'
```
### 5.3 Control 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.
### 5.4 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)
### 5.5 Show or hide the Sign in button
to show button:
```
LOGIN_ALLOWED = True
```
to hide button:
```
LOGIN_ALLOWED = False
```
### 5.6 Show or hide the Register button
to show button:
```
REGISTER_ALLOWED = True
```
to hide button:
```
REGISTER_ALLOWED = False
```
### 5.7 Show or hide the upload media button
To show:
```
UPLOAD_MEDIA_ALLOWED = True
```
To hide:
```
UPLOAD_MEDIA_ALLOWED = False
```
### 5.8 Show or 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
```
### 5.9 Show or hide the download option on a media
Edit `templates/config/installation/features.html` and set
```
download: false
```
### 5.10 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
### 5.11 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'
```
### 5.12 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']
```
### 5.13 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']
```
### 5.14 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
### 5.15 Specify maximum number of media for a playlist
set a different threshold on variable `MAX_MEDIA_PER_PLAYLIST`
eg
```
MAX_MEDIA_PER_PLAYLIST = 14
```
### 5.16 Specify maximum size of a media that can be uploaded
change `UPLOAD_MAX_SIZE`.
default is 4GB
```
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
```
### 5.17 Specify maximum size of comments
change `MAX_CHARS_FOR_COMMENT`
default:
```
MAX_CHARS_FOR_COMMENT = 10000
```
### 5.18 How many files to upload in parallel
set a different threshold for `UPLOAD_MAX_FILES_NUMBER`
default:
```
UPLOAD_MAX_FILES_NUMBER = 100
```
### 5.18 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'
```
### 5.20 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
```
### 5.21 Disallow user registration
set the following variable to False
```
USERS_CAN_SELF_REGISTER = True
```
### 5.22 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
### 5.23 Configure only member access to media
- Make the portal workflow public, but at the same time set `GLOBAL_LOGIN_REQUIRED = True` so that only logged in users can see content.
- You can either set `REGISTER_ALLOWED = False` if you want to add members yourself or checkout options on "django-allauth settings" that affects registration in `cms/settings.py`. Eg set the portal invite only, or set email confirmation as mandatory, so that you control who registers.
## 6. Manage pages
to be written
## 7. Django admin dashboard
## 8. On portal workflow
Who can publish content, how content appears on public listings.Difference between statuses (private, unlisted, public)
## 9. On user roles
Differences over MediaCMS manager, MediaCMS editor, logged in user
## 10. Adding languages for Captions and subtitles
to be written
## 11. Add/delete categories and tags
Through the admin section - http://your_installation/admin/
## 12. Video transcoding
Add / remove resolutions and profiles by modifying the database table of `Encode profiles` through https://your_installation/admin/files/encodeprofile/
For example, the `Active` state of any profile can be toggled to enable or disable it.
## 13. How To Add A Static Page To The Sidebar
### 1. Create your html page in templates/cms/
e.g. duplicate and rename about.html
```
sudo cp templates/cms/about.html templates/cms/volunteer.html
```
### 2. Create your css file in static/css/
```
touch static/css/volunteer.css
```
### 3. In your html file, update block headermeta to reflect your new page
```
{% block headermeta %}
<meta property="og:title" content="Volunteer - {{PORTAL_NAME}}">
<meta property="og:type" content="website">
<meta property="og:description" content="">
<meta name="twitter:card" content="summary">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [{
"@type": "ListItem",
"position": 1,
"name": "{{PORTAL_NAME}}",
"item": {
"@type": "WebPage",
"@id": "{{FRONTEND_HOST}}"
}
},
{
"@type": "ListItem",
"position": 2,
"name": "Volunteer",
"item": {
"@type": "VolunteerPage",
"@id": "{{FRONTEND_HOST}}/volunteer"
}
}]
}
</script>
<link href="{% static "css/volunteer.css" %}" rel="stylesheet"/>
{% endblock headermeta %}
```
### 4. In your html file, update block innercontent to reflect your actual content
Write whatever you like.
### 5. In your css file, write matching styles for you html file.
Write whatever you like.
### 6. Add your view to files/views.py
```
def volunteer(request):
"""Volunteer view"""
context = {}
return render(request, "cms/volunteer.html", context)
```
### 7. Add your url pattern to files/urls.py
```
urlpatterns = [
url(r"^$", views.index),
url(r"^about", views.about, name="about"),
url(r"^volunteer", views.volunteer, name="volunteer"),
```
### 8. Add your page to the left sidebar
To add a link to your page as a menu item in the left sidebar,
add the following code after the last line in _commons.js
```
/* Checks that a given selector has loaded. */
const checkElement = async selector => {
while ( document.querySelector(selector) === null) {
await new Promise( resolve => requestAnimationFrame(resolve) )
}
return document.querySelector(selector);
};
/* Checks that sidebar nav menu has loaded, then adds menu item. */
checkElement('.nav-menu')
.then((element) => {
(function(){
var a = document.createElement('a');
a.href = "/volunteer";
a.title = "Volunteer";
var s = document.createElement('span');
s.className = "menu-item-icon";
var icon = document.createElement('i');
icon.className = "material-icons";
icon.setAttribute("data-icon", "people");
s.appendChild(icon);
a.appendChild(s);
var linkText = document.createTextNode("Volunteer");
var t = document.createElement('span');
t.appendChild(linkText);
a.appendChild(t);
var listItem = document.createElement('li');
listItem.className = "link-item";
listItem.appendChild(a);
//if signed out use 3rd nav-menu
var elem = document.querySelector(".nav-menu:nth-child(3) nav ul");
var loc = elem.innerText;
if (loc.includes("About")){
elem.insertBefore(listItem, elem.children[2]);
} else { //if signed in use 4th nav-menu
elem = document.querySelector(".nav-menu:nth-child(4) nav ul");
elem.insertBefore(listItem, elem.children[2]);
}
})();
});
```
### 9. Restart the mediacms web server
On docker:
```
sudo docker stop mediacms_web_1 && sudo docker start mediacms_web_1
```
Otherwise
```
sudo systemctl restart mediacms
```
## 14. Add Google Analytics
Instructions contributed by @alberto98fx
1. Create a file:
``` touch $DIR/mediacms/templates/tracking.html ```
2. Add the Gtag/Analytics script
3. Inside ``` $DIR/mediacms/templates/root.html``` you'll see a file like this one:
```
<head>
{% block head %}
<title>{% block headtitle %}{{PORTAL_NAME}}{% endblock headtitle %}</title>
{% include "common/head-meta.html" %}
{% block headermeta %}
<meta property="og:title" content="{{PORTAL_NAME}}">
<meta property="og:type" content="website">
{%endblock headermeta %}
{% block externallinks %}{% endblock externallinks %}
{% include "common/head-links.html" %}
{% block topimports %}{%endblock topimports %}
{% include "config/index.html" %}
{% endblock head %}
</head>
```
4. Add ``` {% include "tracking.html" %} ``` at the end inside the section ```<head>```
5. If you are using Docker and didn't mount the entire dir you need to bind a new volume:
```
web:
image: mediacms/mediacms:latest
restart: unless-stopped
ports:
- "80:80"
deploy:
replicas: 1
volumes:
- ./templates/root.html:/home/mediacms.io/mediacms/templates/root.html
- ./templates/tracking.html://home/mediacms.io/mediacms/templates/tracking.html
```
## 15. Debugging email issues
On the [Configuration](https://github.com/mediacms-io/mediacms/blob/main/docs/admins_docs.md#5-configuration) section of this guide we've see how to edit the email settings.
In case we are yet unable to receive email from MediaCMS, the following may help us debug the issue - in most cases it is an issue of setting the correct username, password or TLS option
Enter the Django shell, example if you're using the Single Server installation:
```bash
source /home/mediacms.io/bin/activate
python manage.py shell
```
and inside the shell
```bash
from django.core.mail import EmailMessage
from django.conf import settings
settings.EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
email = EmailMessage(
'title',
'msg',
settings.DEFAULT_FROM_EMAIL,
['recipient@email.com'],
)
email.send(fail_silently=False)
```
You have the chance to either receive the email (in this case it will be sent to recipient@email.com) otherwise you will see the error.
For example, while specifying wrong password for my Gmail account I get
```
SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8 https://support.google.com/mail/?p=BadCredentials d4sm12687785wrc.34 - gsmtp')
```
## 16. Frequently Asked Questions
Video is playing but preview thumbnails are not showing for large video files
Chances are that the sprites file was not created correctly.
The output of files.tasks.produce_sprite_from_video() function in this case is something like this
```
convert-im6.q16: width or height exceeds limit `/tmp/img001.jpg' @ error/cache.c/OpenPixelCache/3912.
```
Solution: edit file `/etc/ImageMagick-6/policy.xml` and set bigger values for the lines that contain width and height. For example
```
<policy domain="resource" name="height" value="16000KP"/>
<policy domain="resource" name="width" value="16000KP"/>
```
Newly added video files now will be able to produce the sprites file needed for thumbnail previews. To re-run that task on existing videos, enter the Django shell
```
root@8433f923ccf5:/home/mediacms.io/mediacms# source /home/mediacms.io/bin/activate
root@8433f923ccf5:/home/mediacms.io/mediacms# python manage.py shell
Python 3.8.14 (default, Sep 13 2022, 02:23:58)
```
and run
```
In [1]: from files.models import Media
In [2]: from files.tasks import produce_sprite_from_video
In [3]: for media in Media.objects.filter(media_type='video', sprites=''):
...: produce_sprite_from_video(media.friendly_token)
```
this will re-create the sprites for videos that the task failed.
## 17. Cookie consent code
On file `templates/components/header.html` you can find a simple cookie consent code. It is commented, so you have to remove the `{% comment %}` and `{% endcomment %}` lines in order to enable it. Or you can replace that part with your own code that handles cookie consent banners.
![Simple Cookie Consent](images/cookie_consent.png)
## 18. Disable encoding and show only original file
When videos are uploaded, they are getting encoded to multiple resolutions, a procedure called transcoding. Sometimes this is not needed and you only need to show the original file, eg when MediaCMS is running on a low capabilities server. To achieve this, edit settings.py and set
```
DO_NOT_TRANSCODE_VIDEO = True
```
This will disable the transcoding process and only the original file will be shown. Note that this will also disable the sprites file creation, so you will not have the preview thumbnails on the video player.

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

BIN
docs/images/Processing.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
docs/images/Save-File.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
docs/images/Uploading.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

259
docs/user_docs.md Normal file
View File

@@ -0,0 +1,259 @@
# Users documentation
## Table of contents
- [Uploading media](#uploading-media)
- [Downloading media](#downloading-media)
- [Adding captions/subtitles](#adding-captionssubtitles)
- [Search media](#search-media)
- [Using Timestamps for sharing](#using-timestamps-for-sharing)
- [Mentionning users in comments](#Mentionning-users-in-comments)
- [Show comments in the Timebar](#Show-comments-in-the-Timebar)
- [Share media](#share-media)
- [Embed media](#embed-media)
- [Customize my profile options](#customize-my-profile-options)
## Uploading media
### How to Upload Media
Uploading media is as simple as clicking the _Upload Media_ button, waiting for media to upload, and then clicking the media to add metadata (title, description etc.) by filling out a form.
#### Click Upload Button
Click the _Upload Media_ button from the right-side of the screen at the top:
<p align="left">
<img src="./images/Click-Upload-Media-button.png"/>
</p>
#### Upload Page
Clicking the _Upload Media_ button takes you to the upload page at a URL like this:
https://demo.mediacms.io/upload
#### Click Browse Button
Here you should click the _"Browse your files"_ button (or drag and drop a file from your desktop):
<p align="left">
<img src="./images/Click-Browse-button.png"/>
</p>
#### Select media file and click Open button
Select the media file that you want to upload and click the _"Open"_ button:
<p align="left">
<img src="./images/Select-Media-File-Click-Open.png"/>
</p>
#### Wait for file to upload
Wait for the file to finish uploading:
<p align="left">
<img src="./images/Uploading.png"/>
</p>
#### Pause uploading
If you want you can pause upload by clicking _Pause button_:
<p align="left">
<img src="./images/Pause-button.png"/>
</p>
#### Continue uploading
Continue upload by clicking _Continue button_:
<p align="left">
<img src="./images/Continue-button.png"/>
</p>
#### Wait for media to finish Processing
Wait for the media file to finish Processing:
<p align="left">
<img src="./images/Processing.png"/>
</p>
#### Click View media button
Click the View media button to open the media page:
<p align="left">
<img src="./images/Click-View-media-button.png"/>
</p>
#### Media will be in the encoding queue
The media will take some time to finish encoding (MediaCMS will transcode the file to several formats and resolutions). Meanwhile you can edit the media file to add metadata.
#### Click Edit Media button
Click the EDIT MEDIA button to add metadata and configure the poster image:
<p align="left">
<img src="./images/Click-Edit-Media-button.png"/>
</p>
#### Add Metadata (Summary, Description etc.)
Make sure you fill in all the required fields, and try to complete as many of the non-required fields as possible. This ensures the database is populated with useful meta-data to help others access useful information about you and your video.
<p align="left">
<img src="./images/Edit-Media-Metadata-1.png"/>
</p>
<p align="left">
<img src="./images/Edit-Media-Metadata-2.png"/>
</p>
## Downloading media
MediaCMS offers a configurable option whereby users can make their media files available for download. Downloads are available for transcoded files, and the original file.
#### How To Enable Download
Visit the media view page and choose the EDIT MEDIA button.
Select the checkbox for "Allow Downloads"
#### How To Download Media
Visit the media view page and click the DOWNLOAD button below the video player.
<p align="left">
<img src="./images/Click-Download-Button.png">
</p>
Choose the version you wish to download - a transcoded file or the original file:
<p align="left">
<img src="./images/Click-version-download.png">
</p>
Choose Save File and click the OK button.
<p align="left">
<img src="./images/Save-File.png">
</p>
## Adding captions/subtitles
With MediaCMS you can add subtitles/captions to your video by uploading a subtitles file in the popular .vtt format.
### Visit Media Page & Click EDIT SUBTITLE Button
Visit the "single media page" for the media you wish to add subtitles/captions to and click the EDIT SUBTITLES button:
<p align="left">
<img src="./images/Click-EDIT-SUBTITLE.png"/>
</p>
### Upload Subtitles in .vtt Format
Click the Language menu to select the correct language for your Subtitles/Captions file:
<p align="left">
<img src="./images/Click-Subtitle-Language-Menu.png"/>
</p>
Choose the correct Language for your file:
<p align="left">
<img src="./images/Subtitles-captions1.png"/>
</p>
Click Browse to find a subtitles/captions file on your computer (if your file is not in the .vtt format, you may find a conversion service on the Internet):
<p align="left">
<img src="./images/Subtitles-captions2.png"/>
</p>
Choose a .vtt subtitles/captions file from your computer:
<p align="left">
<img src="./images/Subtitles-captions3.png"/>
</p>
Click the Add button to add the file to your media:
<p align="left">
<img src="./images/Click-ADD-button.png"/>
</p>
### View Subtitles/Captions in Video Player
You can now watch the captions/subtitles play back in the video player - and toggle display on/off by clicking the CC button:
<p align="left">
<img src="./images/CC-display.png"/>
</p>
## Using Timestamps for sharing
### Using Timestamp in the URL
An additional GET parameter 't' can be added in video URL's to start the video at the given time. The starting time has to be given in seconds.
<p align="left">
<img src="./images/Demo1.png"/>
</p>
Additionally the share button has an option to generate the URL with the timestamp at current second the video is.
<p align="left">
<img src="./images/Demo2.png"/>
</p>
### Using Timestamp in the comments
Comments can also include timestamps. They are automatically detected upon posting the comment, and will be in the form of an hyperlink link in the comment. The timestamps in the comments have to follow the format HH:MM:SS or MM:SS
<p align="left">
<img src="./images/Demo3.png"/>
</p>
## Mentionning users in comments
Comments can also mention other users by tagging with '@'. This will open suggestion box showing usernames, and the selection will refine as the user continues typing.
Comments send with mentions will contain a link to the user page, and can be setup to send a mail to the mentionned user.
<p align="left">
<img src="./images/Mention1.png"/>
<img src="./images/Mention2.png"/>
<img src="./images/Mention3.png"/>
<img src="./images/Mention4.png"/>
</p>
## Show comments in the Timebar
When enabled, comments including a timestamp will also be displayed in the current video Timebar as a little colorful dot. The comment can be previewed by hovering the dot (left image) and it will be displayed on top of the video when reaching the correct time (right image).
Only comments with correct timestamps formats (HH:MM:SS or MM:SS) will be picked up and appear in the Timebar.
<p align="left">
<img src="./images/TimebarComments_Hover.png" height="180" alt="Comment preview on hover"/>
<img src="./images/TimebarComments_Hit.png" height="180" alt="Comment shown when the timestamp is reached "/>
</p>
## Search media
How search can be used
## Share media
How to share media
## Embed media
How to use the embed media option
## Customize my profile options
Customize profile and channel

View File

@@ -1,14 +1,14 @@
from django.contrib import admin
from .models import (
Media,
Encoding,
EncodeProfile,
Category,
Comment,
Tag,
EncodeProfile,
Encoding,
Language,
Media,
Subtitle,
Tag,
)

View File

@@ -1,9 +1,9 @@
# ffmpeg only backend
from subprocess import PIPE, Popen
import locale
import re
import logging
import re
from subprocess import PIPE, Popen
logger = logging.getLogger(__name__)

View File

@@ -1,22 +1,21 @@
from django.conf import settings
from .methods import is_mediacms_editor, is_mediacms_manager
def stuff(request):
"""Pass settings to the frontend"""
ret = {}
if request.is_secure():
# in case session is https, pass this setting so
# that the frontend uses https too
ret["FRONTEND_HOST"] = settings.SSL_FRONTEND_HOST
else:
ret["FRONTEND_HOST"] = settings.FRONTEND_HOST
ret["FRONTEND_HOST"] = request.build_absolute_uri('/').rstrip('/')
ret["DEFAULT_THEME"] = settings.DEFAULT_THEME
ret["PORTAL_NAME"] = settings.PORTAL_NAME
ret["PORTAL_DESCRIPTION"] = settings.PORTAL_DESCRIPTION
ret["LOAD_FROM_CDN"] = settings.LOAD_FROM_CDN
ret["CAN_LOGIN"] = settings.LOGIN_ALLOWED
ret["CAN_REGISTER"] = settings.REGISTER_ALLOWED
ret["CAN_UPLOAD_MEDIA"] = settings.UPLOAD_MEDIA_ALLOWED
ret["TIMESTAMP_IN_TIMEBAR"] = settings.TIMESTAMP_IN_TIMEBAR
ret["CAN_MENTION_IN_COMMENTS"] = settings.ALLOW_MENTION_IN_COMMENTS
ret["CAN_LIKE_MEDIA"] = settings.CAN_LIKE_MEDIA
ret["CAN_DISLIKE_MEDIA"] = settings.CAN_DISLIKE_MEDIA
ret["CAN_REPORT_MEDIA"] = settings.CAN_REPORT_MEDIA
@@ -24,18 +23,12 @@ def stuff(request):
ret["UPLOAD_MAX_SIZE"] = settings.UPLOAD_MAX_SIZE
ret["UPLOAD_MAX_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER
ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE
ret[
"POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"
] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY
ret["POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY
ret["IS_MEDIACMS_ADMIN"] = request.user.is_superuser
ret["IS_MEDIACMS_EDITOR"] = is_mediacms_editor(request.user)
ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user)
ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS
ret[
"ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"
] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
ret[
"VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"
] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE
ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
ret["VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE
ret["RSS_URL"] = "/rss"
return ret

View File

@@ -1,12 +1,11 @@
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed
from django.urls import reverse
from django.db.models import Q
from django.conf import settings
from django.contrib.postgres.search import SearchQuery
from django.contrib.syndication.views import Feed
from django.urls import reverse
from django.utils.feedgenerator import Rss201rev2Feed
from .models import Media, Category
from . import helpers
from .models import Media
from .stop_words import STOP_WORDS
@@ -103,7 +102,7 @@ class SearchRSSFeed(Feed):
description = "Latest Media RSS feed"
def link(self, obj):
return f"/rss/search"
return "/rss/search"
def get_object(self, request):
category = request.GET.get("c", "")
@@ -119,11 +118,7 @@ class SearchRSSFeed(Feed):
elif query:
# same as on files.views.MediaSearch: move this processing to a prepare_query function
query = helpers.clean_query(query)
q_parts = [
q_part.rstrip("y")
for q_part in query.split()
if q_part not in STOP_WORDS
]
q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS]
if q_parts:
query = SearchQuery(q_parts[0] + ":*", search_type="raw")
for part in q_parts[1:]:

View File

@@ -1,6 +1,7 @@
from django import forms
from .methods import get_next_state, is_mediacms_editor
from .models import Media, Subtitle
from .methods import is_mediacms_editor, get_next_state
class MultipleSelect(forms.CheckboxSelectMultiple):
@@ -8,9 +9,7 @@ class MultipleSelect(forms.CheckboxSelectMultiple):
class MediaForm(forms.ModelForm):
new_tags = forms.CharField(
label="Tags", help_text="a comma separated list of new tags.", required=False
)
new_tags = forms.CharField(label="Tags", help_text="a comma separated list of new tags.", required=False)
class Meta:
model = Media
@@ -27,7 +26,7 @@ class MediaForm(forms.ModelForm):
"thumbnail_time",
"reported_times",
"is_reviewed",
"allow_download"
"allow_download",
)
widgets = {
"tags": MultipleSelect(),
@@ -42,9 +41,7 @@ class MediaForm(forms.ModelForm):
self.fields.pop("featured")
self.fields.pop("reported_times")
self.fields.pop("is_reviewed")
self.fields["new_tags"].initial = ", ".join(
[tag.title for tag in self.instance.tags.all()]
)
self.fields["new_tags"].initial = ", ".join([tag.title for tag in self.instance.tags.all()])
def clean_uploaded_poster(self):
image = self.cleaned_data.get("uploaded_poster", False)
@@ -57,9 +54,7 @@ class MediaForm(forms.ModelForm):
data = self.cleaned_data
state = data.get("state")
if state != self.initial["state"]:
self.instance.state = get_next_state(
self.user, self.initial["state"], self.instance.state
)
self.instance.state = get_next_state(self.user, self.initial["state"], self.instance.state)
media = super(MediaForm, self).save(*args, **kwargs)
return media

View File

@@ -1,19 +1,18 @@
# Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg
# related content
import os
import math
import shutil
import tempfile
import random
import hashlib
import subprocess
import json
import os
import random
import shutil
import subprocess
import tempfile
from fractions import Fraction
import filetype
from django.conf import settings
CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
CRF_ENCODING_NUM_SECONDS = 2 # 0 * 60 # videos with greater duration will get
@@ -23,6 +22,8 @@ CRF_ENCODING_NUM_SECONDS = 2 # 0 * 60 # videos with greater duration will get
# you should use CRF encoding.
MAX_RATE_MULTIPLIER = 1.5
MIN_RATE_MULTIPLIER = 0.5
BUF_SIZE_MULTIPLIER = 1.5
# in seconds, anything between 2 and 6 makes sense
@@ -168,9 +169,7 @@ def rm_dir(directory):
def url_from_path(filename):
# TODO: find a way to preserver http - https ...
return "{0}{1}".format(
settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, "")
)
return "{0}{1}".format(settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, ""))
def create_temp_file(suffix=None, dir=settings.TEMP_DIRECTORY):
@@ -210,9 +209,7 @@ def run_command(cmd, cwd=None):
cmd = cmd.split()
ret = {}
if cwd:
process = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
else:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
@@ -242,10 +239,12 @@ def media_file_info(input_file):
- `filename`: Filename
- `file_size`: Size of the file in bytes
- `video_duration`: Duration of the video in `s.msec`
- `video_frame_rate`: Framerate in Hz
- `video_frame_rate_d`: Framerate franction denominator
- `video_frame_rate_n`: Framerate fraction nominator
- `video_bitrate`: Bitrate of the video stream in kBit/s
- `video_width`: Width in pixels
- `video_height`: Height in pixels
- `interlaced` : True if the video is interlaced
- `video_codec`: Video codec
- `audio_duration`: Duration of the audio in `s.msec`
- `audio_sample_rate`: Audio sample rate in Hz
@@ -331,9 +330,7 @@ def media_file_info(input_file):
except ValueError:
hms, msec = duration_str.split(",")
total_dur = sum(
int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))
)
total_dur = sum(int(x) * 60**i for i, x in enumerate(reversed(hms.split(":"))))
video_duration = total_dur + float("0." + msec)
else:
# fallback to format, eg for webm
@@ -370,24 +367,40 @@ def media_file_info(input_file):
input_file,
]
stdout = run_command(cmd).get("out")
stream_size = sum([int(l) for l in stdout.split("\n") if l != ""])
stream_size = sum([int(line) for line in stdout.split("\n") if line != ""])
video_bitrate = round((stream_size * 8 / 1024.0) / video_duration, 2)
if "r_frame_rate" in video_info.keys():
video_frame_rate = video_info["r_frame_rate"].partition("/")
video_frame_rate_n = video_frame_rate[0]
video_frame_rate_d = video_frame_rate[2]
interlaced = False
if video_info.get("field_order") in ("tt", "tb", "bt", "bb"):
interlaced = True
ret = {
"filename": input_file,
"file_size": file_size,
"video_duration": video_duration,
"video_frame_rate": float(Fraction(video_info["r_frame_rate"])),
"video_frame_rate_n": video_frame_rate_n,
"video_frame_rate_d": video_frame_rate_d,
"video_bitrate": video_bitrate,
"video_width": video_info["width"],
"video_height": video_info["height"],
"video_codec": video_info["codec_name"],
"has_video": has_video,
"has_audio": has_audio,
"color_range": video_info.get("color_range"),
"color_space": video_info.get("color_space"),
"color_transfer": video_info.get("color_space"),
"color_primaries": video_info.get("color_primaries"),
"interlaced": interlaced,
"display_aspect_ratio": video_info.get("display_aspect_ratio"),
"sample_aspect_ratio": video_info.get("sample_aspect_ratio"),
}
if has_audio:
audio_duration = 1
if "duration" in audio_info.keys():
audio_duration = float(audio_info["duration"])
elif "tags" in audio_info.keys() and "DURATION" in audio_info["tags"]:
@@ -396,9 +409,7 @@ def media_file_info(input_file):
hms, msec = duration_str.split(".")
except ValueError:
hms, msec = duration_str.split(",")
total_dur = sum(
int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))
)
total_dur = sum(int(x) * 60**i for i, x in enumerate(reversed(hms.split(":"))))
audio_duration = total_dur + float("0." + msec)
else:
# fallback to format, eg for webm
@@ -432,7 +443,8 @@ def media_file_info(input_file):
input_file,
]
stdout = run_command(cmd).get("out")
stream_size = sum([int(l) for l in stdout.split("\n") if l != ""])
# ffprobe appends a pipe at the end of the output, thus we have to remove it
stream_size = sum([int(line.replace("|", "")) for line in stdout.split("\n") if line != ""])
audio_bitrate = round((stream_size * 8 / 1024.0) / audio_duration, 2)
ret.update(
@@ -484,6 +496,7 @@ def get_base_ffmpeg_command(
encoder,
audio_encoder,
target_fps,
interlaced,
target_height,
target_rate,
target_rate_audio,
@@ -501,7 +514,8 @@ def get_base_ffmpeg_command(
codec {str} -- video codec
encoder {str} -- video encoder
audio_encoder {str} -- audio encoder
target_fps {int} -- target FPS
target_fps {fractions.Fraction} -- target FPS
interlaced {bool} -- true if interlaced
target_height {int} -- height
target_rate {int} -- target bitrate in kbps
target_rate_audio {int} -- audio target bitrate
@@ -510,10 +524,33 @@ def get_base_ffmpeg_command(
enc_type {str} -- encoding type (twopass or crf)
"""
target_fps = int(target_fps)
# avoid Frame rate very high for a muxer not efficiently supporting it.
if target_fps > 90:
target_fps = 90
# avoid very high frame rates
while target_fps > 60:
target_fps = target_fps / 2
if target_fps < 1:
target_fps = 1
filters = []
if interlaced:
filters.append("yadif")
target_width = round(target_height * 16 / 9)
scale_filter_opts = [
f"if(lt(iw\\,ih)\\,{target_height}\\,{target_width})", # noqa
f"if(lt(iw\\,ih)\\,{target_width}\\,{target_height})", # noqa
"force_original_aspect_ratio=decrease",
"force_divisible_by=2",
"flags=lanczos",
]
scale_filter_str = "scale=" + ":".join(scale_filter_opts)
filters.append(scale_filter_str)
fps_str = f"fps=fps={target_fps}"
filters.append(fps_str)
filters_str = ",".join(filters)
base_cmd = [
settings.FFMPEG_COMMAND,
@@ -523,9 +560,7 @@ def get_base_ffmpeg_command(
"-c:v",
encoder,
"-filter:v",
"scale=-2:" + str(target_height) + ",fps=fps=" + str(target_fps),
# always convert to 4:2:0 -- FIXME: this could be also 4:2:2
# but compatibility will suffer
filters_str,
"-pix_fmt",
"yuv420p",
]
@@ -631,6 +666,8 @@ def get_base_ffmpeg_command(
str(keyframe_distance),
"-maxrate",
str(int(int(target_rate) * MAX_RATE_MULTIPLIER)) + "k",
"-minrate",
str(int(int(target_rate) * MIN_RATE_MULTIPLIER)) + "k",
"-bufsize",
str(int(int(target_rate) * BUF_SIZE_MULTIPLIER)) + "k",
"-speed",
@@ -660,9 +697,7 @@ def get_base_ffmpeg_command(
return cmd
def produce_ffmpeg_commands(
media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False
):
def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False):
try:
media_info = json.loads(media_info)
except BaseException:
@@ -670,18 +705,18 @@ def produce_ffmpeg_commands(
if codec == "h264":
encoder = "libx264"
ext = "mp4"
# ext = "mp4"
elif codec in ["h265", "hevc"]:
encoder = "libx265"
ext = "mp4"
# ext = "mp4"
elif codec == "vp9":
encoder = "libvpx-vp9"
ext = "webm"
# ext = "webm"
else:
return False
src_framerate = media_info.get("video_frame_rate", 30)
if src_framerate <= 30:
target_fps = Fraction(int(media_info.get("video_frame_rate_n", 30)), int(media_info.get("video_frame_rate_d", 1)))
if target_fps <= 30:
target_rate = VIDEO_BITRATES[codec][25].get(resolution)
else:
target_rate = VIDEO_BITRATES[codec][60].get(resolution)
@@ -698,11 +733,6 @@ def produce_ffmpeg_commands(
# target_fps = 25
# else:
# adjust the target frame rate if the input is fractional
target_fps = (
src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate)
)
if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS:
enc_type = "crf"
else:
@@ -713,6 +743,8 @@ def produce_ffmpeg_commands(
elif enc_type == "crf":
passes = [2]
interlaced = media_info.get("interlaced")
cmds = []
for pass_number in passes:
cmds.append(
@@ -724,6 +756,7 @@ def produce_ffmpeg_commands(
encoder=encoder,
audio_encoder=AUDIO_ENCODERS[codec],
target_fps=target_fps,
interlaced=interlaced,
target_height=resolution,
target_rate=target_rate,
target_rate_audio=AUDIO_BITRATES[codec],
@@ -752,3 +785,11 @@ def clean_query(query):
query = query.replace(char, "")
return query.lower()
def get_alphanumeric_only(string):
"""Returns a query that contains only alphanumeric characters
This include characters other than the English alphabet too
"""
string = "".join([char for char in string if char.isalnum()])
return string.lower()

View File

@@ -1,16 +1,18 @@
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser
from rest_framework.settings import api_settings
from rest_framework.response import Response
from drf_yasg import openapi as openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView
from users.models import User
from users.serializers import UserSerializer
from .permissions import IsMediacmsEditor
from .models import Media, Comment
from .methods import is_mediacms_manager
from .serializers import MediaSerializer, CommentSerializer
from .methods import is_mediacms_manager
from .models import Comment, Media
from .permissions import IsMediacmsEditor
from .serializers import CommentSerializer, MediaSerializer
class MediaList(APIView):
@@ -23,6 +25,17 @@ class MediaList(APIView):
permission_classes = (IsMediacmsEditor,)
parser_classes = (JSONParser,)
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(name='sort_by', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Sort by any of: title, add_date, edit_date, views, likes, reported_times'),
openapi.Parameter(name='ordering', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Order by: asc, desc'),
openapi.Parameter(name='state', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Media state, options: private", "public", "unlisted'),
openapi.Parameter(name='encoding_status', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Encoding status, options "pending", "running", "fail", "success"'),
],
tags=['Manage'],
operation_summary='Manage Media',
operation_description='Manage media for MediaCMS managers and reviewers',
)
def get(self, request, format=None):
params = self.request.query_params
ordering = params.get("ordering", "").strip()
@@ -94,6 +107,12 @@ class MediaList(APIView):
serializer = MediaSerializer(page, many=True, context={"request": request})
return paginator.get_paginated_response(serializer.data)
@swagger_auto_schema(
manual_parameters=[],
tags=['Manage'],
operation_summary='Delete Media',
operation_description='Delete media for MediaCMS managers and reviewers',
)
def delete(self, request, format=None):
tokens = request.GET.get("tokens")
if tokens:
@@ -112,6 +131,12 @@ class CommentList(APIView):
permission_classes = (IsMediacmsEditor,)
parser_classes = (JSONParser,)
@swagger_auto_schema(
manual_parameters=[],
tags=['Manage'],
operation_summary='Manage Comments',
operation_description='Manage comments for MediaCMS managers and reviewers',
)
def get(self, request, format=None):
params = self.request.query_params
ordering = params.get("ordering", "").strip()
@@ -137,6 +162,12 @@ class CommentList(APIView):
serializer = CommentSerializer(page, many=True, context={"request": request})
return paginator.get_paginated_response(serializer.data)
@swagger_auto_schema(
manual_parameters=[],
tags=['Manage'],
operation_summary='Delete Comments',
operation_description='Delete comments for MediaCMS managers and reviewers',
)
def delete(self, request, format=None):
comment_ids = request.GET.get("comment_ids")
if comment_ids:
@@ -156,6 +187,12 @@ class UserList(APIView):
permission_classes = (IsMediacmsEditor,)
parser_classes = (JSONParser,)
@swagger_auto_schema(
manual_parameters=[],
tags=['Manage'],
operation_summary='Manage Users',
operation_description='Manage users for MediaCMS managers and reviewers',
)
def get(self, request, format=None):
params = self.request.query_params
ordering = params.get("ordering", "").strip()
@@ -187,11 +224,15 @@ class UserList(APIView):
serializer = UserSerializer(page, many=True, context={"request": request})
return paginator.get_paginated_response(serializer.data)
@swagger_auto_schema(
manual_parameters=[],
tags=['Manage'],
operation_summary='Delete Users',
operation_description='Delete users for MediaCMS managers',
)
def delete(self, request, format=None):
if not is_mediacms_manager(request.user):
return Response(
{"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
)
return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
tokens = request.GET.get("tokens")
if tokens:

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