Merge pull request #136 from jr-k/develop

Release v2.4.1
This commit is contained in:
JRK 2024-08-13 12:40:10 +02:00 committed by GitHub
commit e801485ec1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 134 additions and 25 deletions

View File

@ -30,13 +30,21 @@ class PlayerController(ObController):
self._app.add_url_rule('/serve/content/<content_type>/<content_id>/<content_location>', 'serve_content_file', self.serve_content_file, methods=['GET'])
def player(self, playlist_slug_or_id: str = ''):
preview_playlist = request.args.get('preview_playlist')
preview_content_id = request.args.get('preview_content_id')
playlist_slug_or_id = self._get_dynamic_playlist_id(playlist_slug_or_id)
current_playlist = self._model_store.playlist().get_one_by("slug = ? OR id = ?", {
query = " (slug = ? OR id = ?) "
query_args = {
"slug": playlist_slug_or_id,
"id": playlist_slug_or_id
})
"id": playlist_slug_or_id,
}
if not preview_playlist:
query = query + " AND enabled = ? "
query_args["enabled"] = True
current_playlist = self._model_store.playlist().get_one_by(query, query_args)
if playlist_slug_or_id and not current_playlist:
return abort(404)

View File

@ -28,7 +28,7 @@ class ContentManager(ModelManager):
"name CHAR(255)",
"type CHAR(30)",
"location TEXT",
"duration INTEGER",
"duration FLOAT",
"folder_id INTEGER",
"created_by CHAR(255)",
"updated_by CHAR(255)",

View File

@ -3,6 +3,7 @@ import os
from typing import Dict, Optional, List, Tuple, Union
from src.model.entity.Playlist import Playlist
from src.model.enum.ContentType import ContentType
from src.util.utils import get_optional_string, get_yt_video_id, slugify, slugify_next
from src.manager.DatabaseManager import DatabaseManager
from src.manager.SlideManager import SlideManager
@ -69,15 +70,17 @@ class PlaylistManager(ModelManager):
durations = self._db.execute_read_query("""
SELECT
playlist_id,
SUM(CASE
ROUND(SUM(CASE
WHEN s.delegate_duration = 1 THEN c.duration
WHEN c.type = '{}' THEN s.duration
ELSE s.duration
END) AS total_duration
END)) AS total_duration
FROM {} s
LEFT JOIN {} c ON c.id = s.content_id
WHERE cron_schedule IS NULL {}
WHERE cron_schedule IS NULL {} AND s.enabled is TRUE
GROUP BY playlist_id;
""".format(
ContentType.EXTERNAL_STORAGE.value,
SlideManager.TABLE_NAME,
ContentManager.TABLE_NAME,
"{}".format(

View File

@ -9,7 +9,7 @@ from src.util.utils import str_to_enum
class Content:
def __init__(self, uuid: str = '', location: str = '', type: Union[ContentType, str] = ContentType.URL, name: str = 'Untitled', id: Optional[int] = None, duration: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None, folder_id: Optional[int] = None):
def __init__(self, uuid: str = '', location: str = '', type: Union[ContentType, str] = ContentType.URL, name: str = 'Untitled', id: Optional[int] = None, duration: Optional[float] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None, folder_id: Optional[int] = None):
self._uuid = uuid if uuid else self.generate_and_set_uuid()
self._id = id if id else None
self._location = location
@ -88,11 +88,11 @@ class Content:
self._folder_id = value
@property
def duration(self) -> Optional[int]:
def duration(self) -> Optional[float]:
return self._duration
@duration.setter
def duration(self, value: Optional[int]):
def duration(self, value: Optional[float]):
self._duration = value
@property

View File

@ -10,9 +10,9 @@ def mp4_duration_with_ffprobe(filename):
fields = json.loads(result)['streams'][0]
if 'tags' in fields and 'DURATION' in fields['tags']:
return int(float(fields['tags']['DURATION']))
return round(float(fields['tags']['DURATION']), 2)
if 'duration' in fields:
return int(float(fields['duration']))
return round(float(fields['duration']), 2)
return 0

View File

@ -251,6 +251,7 @@ def slugify(value):
def seconds_to_hhmmss(seconds):
seconds = int(seconds)
if not seconds:
return ""
hours = seconds // 3600

View File

@ -1 +1 @@
2.4.0
2.4.1

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,7 @@
{% if current_player_group.playlist_id %}
<div class="preview-holder">
{% set base_url = external_url if external_url else request.scheme ~ '://' ~ request.headers.get('host') %}
{% set preview_url = base_url ~ url_for('player_use', playlist_slug_or_id=current_player_group.playlist_id) %}
{% set preview_url_iframe = base_url ~ url_for('player_use', playlist_slug_or_id=current_player_group.playlist_id, preview_playlist=1, intro=0, animation=0) %}
<h4 class="divide">
Iframe
@ -77,7 +77,7 @@
<div class="preview">
<button type="button" class="btn btn-pixel node-player-group-preview"
title="{{ l.playlist_panel_preview_action }}"
data-url="{{ preview_url }}?intro=0&animation=0">
data-url="{{ preview_url_iframe }}">
<i class="fa fa-play"></i>
</button>
</div>

View File

@ -14,6 +14,7 @@
.slide, iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding-top: 0; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; }
.slide iframe { background: white; }
.slide img, .slide video { height: 100%; }
.slide video { width: 100%; height: 100%; }
</style>
<script type="application/javascript" src="{{ STATIC_PREFIX }}js/utils.js"></script>
<script type="application/javascript" src="{{ STATIC_PREFIX }}js/lib/is-cron-now.js"></script>
@ -82,6 +83,7 @@
let curSlide = secondSlide;
let nextSlide = firstSlide;
let notificationItemIndex = null;
let pausableContent = null;
// Functions
const itemsLoadedProcess = function() {
@ -126,6 +128,10 @@
const resume = function() {
playState = PLAY_STATE_PLAYING;
if (pausableContent) {
pausableContent.play();
}
};
const play = function() {
@ -135,6 +141,10 @@
const pause = function() {
pauseClockValue = clockValue;
playState = PLAY_STATE_PAUSE;
if (pausableContent) {
pausableContent.pause();
}
};
const stop = function() {
@ -203,7 +213,7 @@
let duration = item.duration;
if (durationsOverride[item.id]) {
if (durationsOverride[item.id] !== undefined) {
duration = durationsOverride[item.id];
}
@ -250,17 +260,15 @@
if (i === curItemIndex) {
secondsBeforeNext = accumulatedTime + safe_duration(item) - timeInCurrentLoop;
//console.log("remaining:", secondsBeforeNext, "clock:",clockValue, curItemIndex);
//console.log("id", item.id, "secondsBeforeNext:", secondsBeforeNext, "clock:", clockValue, "clockLoopDration", timeInCurrentLoop, "<", accumulatedTime , '+', safe_duration(item));
}
if (timeInCurrentLoop < accumulatedTime + safe_duration(item)) {
if (curItemIndex !== i) {
//console.log('change to ', i , item.name)
curItemIndex = i;
const emptySlide = getEmptySlide();
if ((emptySlide && !hasMoveOnce) || forcePreload) {
//console.log('init preload');
if (!hasMoveOnce && syncWithTime) {
if (accumulatedTime + safe_duration(item) - timeInCurrentLoop < 1) {
// Prevent glitch when syncWithTime for first init
@ -386,10 +394,13 @@
delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;
video.addEventListener('loadedmetadata', function() {
if (item.duration !== video.duration && item.delegate_duration) {
durationsOverride[item.id] = video.duration;
if (item.duration !== video.duration && !item.delegate_duration) {
console.warn('Given duration ' + item.duration + 's is different from video file ' + Math.ceil(video.duration) + 's');
}
if (item.delegate_duration) {
durationsOverride[item.id] = Math.ceil(video.duration);
}
});
const autoplayLoader = function() {
@ -400,12 +411,14 @@
if (element.innerHTML.match('<video>')) {
if (!previewMode) {
setTimeout(function() {
video.play();
video.play();
pausableContent = video;
}, 1000);
}
}
}
setTimeout(autoplayLoader, delayNoisyContentJIT);
setTimeout(autoplayLoader, delayNoisyContentJIT);
}
const checkAndMoveNotifications = function() {

View File

@ -84,6 +84,7 @@
<div class="preview-holder">
{% set base_url = external_url if external_url else request.scheme ~ '://' ~ request.headers.get('host') %}
{% set preview_url = base_url ~ url_for('player_use', playlist_slug_or_id=current_playlist.slug) %}
{% set preview_url_iframe = base_url ~ url_for('player_use', playlist_slug_or_id=current_playlist.slug, preview_playlist=1, intro=0, animation=0) %}
<h4 class="divide">
URL
@ -123,7 +124,7 @@
<div class="preview">
<button type="button" class="btn btn-pixel playlist-preview"
title="{{ l.playlist_panel_preview_action }}"
data-url="{{ preview_url }}?intro=0&animation=0">
data-url="{{ preview_url_iframe }}">
<i class="fa fa-play"></i>
</button>
</div>