From a937d1f31367496b57f4488c07856bfdcccca71e Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 14 Oct 2021 01:24:48 -0500
Subject: [PATCH 01/12] Video cntrl udates
---
src/core/static/css/main.css | 5 +-
src/core/static/css/overrides.css | 9 +-
src/core/static/js/context-menu.js | 21 ++++-
src/core/static/js/ui-logic.js | 16 ++--
src/core/static/js/video-events.js | 85 +++++++------------
src/core/templates/modals/file-modal.html | 37 ++++----
.../shellfm/windows/view/utils/Settings.py | 1 +
src/user_config/webfm/settings.json | 5 +-
8 files changed, 87 insertions(+), 92 deletions(-)
diff --git a/src/core/static/css/main.css b/src/core/static/css/main.css
index 6ee4ea1..bd9dd02 100644
--- a/src/core/static/css/main.css
+++ b/src/core/static/css/main.css
@@ -20,14 +20,13 @@
}
#master-container {
- height: 90vh;
+ height: 80vh;
overflow-x: hidden;
overflow-y: auto;
}
#video-controls {
- position: relative;
- bottom: 2.5em;
+ background-color: rgba(0, 0, 0, 0.64);
}
diff --git a/src/core/static/css/overrides.css b/src/core/static/css/overrides.css
index 58fc8da..e038c3f 100644
--- a/src/core/static/css/overrides.css
+++ b/src/core/static/css/overrides.css
@@ -47,10 +47,13 @@ input[type=range][orient=vertical] {
+.modal-header {
+ background-color: rgba(0, 0, 0, 0.64);
+}
.modal-content {
- background-color: #32383e74;
- border-color: #f8940674;
+ background-color: rgba(50, 56, 62, 0.74);
+ border-color: rgba(249, 148, 6, 0.74);
}
.sticky-top,
@@ -59,7 +62,7 @@ input[type=range][orient=vertical] {
}
.card-body {
- min-height: 326px;
+ min-height: 326px;
font-size: x-large;
vertical-align: middle !important;
align-items: center;
diff --git a/src/core/static/js/context-menu.js b/src/core/static/js/context-menu.js
index d39d22c..487e99a 100644
--- a/src/core/static/js/context-menu.js
+++ b/src/core/static/js/context-menu.js
@@ -20,8 +20,6 @@ window.addEventListener("click", e => {
});
window.addEventListener("contextmenu", e => {
- e.preventDefault();
-
let target = e.target;
let elm = target;
while (elm.nodeName != "BODY") {
@@ -33,9 +31,24 @@ window.addEventListener("contextmenu", e => {
}
}
+ let posY = e.pageY;
+ let posX = e.pageX - 165;
+
+ if (e.pageY > (window.innerHeight - 120)) {
+ posY = window.innerHeight - 220;
+ }
+
+ if (e.pageX > (window.innerWidth - 80)) {
+ posX = window.innerWidth - 320;
+ }
+
+ if (e.pageX < 80) {
+ posX = e.pageX + 180;
+ }
+
const origin = {
- left: e.pageX,
- top: e.pageY
+ left: posX,
+ top: posY
};
setPosition(origin);
return false;
diff --git a/src/core/static/js/ui-logic.js b/src/core/static/js/ui-logic.js
index 99f99d2..398508d 100644
--- a/src/core/static/js/ui-logic.js
+++ b/src/core/static/js/ui-logic.js
@@ -10,7 +10,14 @@ const downloadItem = (eve) => {
}
const deleteItem = (eve) => {
- let elm = active_card.querySelector('[hash]'); // With attribute named "hash"
+ if (active_card == null) {
+ let text = "No card hovered over to delete!";
+ let type = "danger";
+ displayMessage(text, type, 3);
+ return ;
+ }
+
+ let elm = active_card.querySelector('[hash]'); // With attribute named "hash"
let elm2 = active_card.querySelector('[title]'); // With attribute named "title"
const hash = elm.getAttribute("hash");
const title = elm2.getAttribute("title");
@@ -49,7 +56,6 @@ const closeFile = () => {
document.getElementById("image-viewer").style.display = "none";
document.getElementById("text-viewer").style.display = "none";
document.getElementById("pdf-viewer").style.display = "none";
- document.getElementById("video-controls").style.display = "none";
title.innerText = "";
video.style.display = "none";
@@ -62,7 +68,6 @@ const showFile = async (title, hash, extension, type) => {
document.getElementById("text-viewer").style.display = "none";
document.getElementById("pdf-viewer").style.display = "none";
document.getElementById("video").style.display = "none";
- document.getElementById("video-controls").style.display = "none";
let titleElm = document.getElementById("selectedFile");
titleElm.innerText = title;
@@ -77,7 +82,6 @@ const showFile = async (title, hash, extension, type) => {
const setupVideo = async (hash, extension) => {
let video = document.getElementById("video");
- let controls = document.getElementById("video-controls");
video.poster = "static/imgs/icons/loading.gif";
video.style.display = "";
video.src = "#"
@@ -102,9 +106,9 @@ const setupVideo = async (hash, extension) => {
}
}
+
+ video.src = video_path;
modal.show();
- controls.style.display = "none";
- video.src = video_path;
} catch (e) {
video.style.display = "none";
console.log(e);
diff --git a/src/core/static/js/video-events.js b/src/core/static/js/video-events.js
index ecd065a..8c5a429 100644
--- a/src/core/static/js/video-events.js
+++ b/src/core/static/js/video-events.js
@@ -2,7 +2,6 @@ let fullScreenState = 0;
let clicktapwait = 200;
let shouldPlay = null;
let controlsTimeout = null;
-let canHideControls = true;
const getTimeFormatted = (duration = null) => {
@@ -19,45 +18,17 @@ const getTimeFormatted = (duration = null) => {
const togglePlay = (video) => {
shouldPlay = setTimeout(function () {
- let controls = document.getElementById("video-controls");
- shouldPlay = null;
+ shouldPlay = null;
if (video.paused) {
- video.style.cursor = 'none';
- controls.style.display = "none";
+ video.style.cursor = 'none';
video.play();
} else {
- video.style.cursor = '';
- controls.style.display = "";
+ video.style.cursor = '';
video.pause();
}
}, 300);
}
-const showControls = () => {
- const video = document.getElementById("video");
- const controls = document.getElementById("video-controls");
-
- video.style.cursor = '';
- controls.style.display = "";
- if (controlsTimeout) {
- clearTimeout(controlsTimeout);
- }
-
- controlsTimeout = setTimeout(function () {
- if (!video.paused) {
- if (canHideControls) {
- video.style.cursor = 'none';
- controls.style.display = "none";
- controlsTimeout = null;
- } else {
- showControls();
- }
- }
- }, 3000);
-}
-
-
-
const setFullscreenSettings = (parentElm, video) => {
parentElm.requestFullscreen();
@@ -73,8 +44,8 @@ const unsetFullscreenSettings = (video) => {
}
const toggleFullscreen = (video) => {
- containerElm = document.getElementById("video-container");
- parentElm = video.parentElement;
+ let containerElm = document.getElementById("video-container");
+ let parentElm = video.parentElement;
if (video.requestFullscreen || video.webkitRequestFullscreen || video.msRequestFullscreen) {
setFullscreenSettings(parentElm, video);
@@ -92,7 +63,7 @@ const toggleFullscreen = (video) => {
unsetFullscreenSettings(video);
}
- fullScreenState = 0;
+ fullScreenState = 0;
}
fullScreenState += 1;
@@ -109,7 +80,6 @@ const toggleVolumeControl = () => {
-
$("#video").on("loadedmetadata", function(eve){
const video = eve.target;
let videoDuration = document.getElementById("videoDuration");
@@ -117,13 +87,13 @@ $("#video").on("loadedmetadata", function(eve){
});
-$("#video").on("keyup", function(eve) {
+
+$("#video").on("keydown", function(eve) {
+ event.preventDefault();
const key = eve.keyCode;
const video = eve.target;
- if (key === 32 || key === 80) { // Spacebar for pausing
- togglePlay(video);
- } else if (key === 37) { // Left key for back tracking 5 sec
+ if (key === 37) { // Left key for back tracking 5 sec
video.currentTime -= 5;
} else if (key === 39) { // Right key for fast forward 5 sec
video.currentTime += 5;
@@ -135,6 +105,17 @@ $("#video").on("keyup", function(eve) {
if (video.volume >= 0.0) {
video.volume -= 0.05;
}
+ }
+
+});
+
+
+$("#video").on("keyup", function(eve) {
+ const key = eve.keyCode;
+ const video = eve.target;
+
+ if (key === 32 || key === 80) { // Spacebar for pausing
+ togglePlay(video);
} else if (key === 70) { // f key for toggling full screen
toggleFullscreen(video);
} else if (key === 76) { // l key for toggling loop
@@ -195,17 +176,6 @@ $( "#video" ).bind( "timeupdate", async function(eve) {
videoDuration.innerText = getTimeFormatted(video.currentTime);
});
-// $( "#video" ).bind( "ended", async function(eve) {
-// alert("Hello...")
-// // let videoDuration = document.getElementById("videoCurrentTime");
-// // const video = eve.target;
-// // const seekto = document.getElementById("seek-slider");
-// // const vt = video.currentTime * (100 / video.duration);
-// //
-// // seekto.value = vt;
-// // videoDuration.innerText = getTimeFormatted(video.currentTime);
-// });
-
$( "#seek-slider" ).bind( "change", async function(eve) {
const slider = eve.target;
let video = document.getElementById("video");
@@ -220,5 +190,14 @@ $( "#volume-slider" ).bind( "change", async function(eve) {
video.volume = volumeto;
});
-$( "#video-controls" ).bind( "mouseenter", async function(eve) { canHideControls = false; });
-$( "#video-controls" ).bind( "mouseleave", async function(eve) { canHideControls = true; });
+
+// $( "#video" ).bind( "ended", async function(eve) {
+// alert("Hello...")
+// // let videoDuration = document.getElementById("videoCurrentTime");
+// // const video = eve.target;
+// // const seekto = document.getElementById("seek-slider");
+// // const vt = video.currentTime * (100 / video.duration);
+// //
+// // seekto.value = vt;
+// // videoDuration.innerText = getTimeFormatted(video.currentTime);
+// });
diff --git a/src/core/templates/modals/file-modal.html b/src/core/templates/modals/file-modal.html
index d88ecd6..7ea1a65 100644
--- a/src/core/templates/modals/file-modal.html
+++ b/src/core/templates/modals/file-modal.html
@@ -19,28 +19,9 @@
autoplay=""
volume="0.75"
poster="{{ url_for('static', filename='imgs/icons/loading.gif')}}"
- onmousemove="showControls()">
+ >
-
-
-
-
-
-
-
- /
-
-
-
- 🔈
-
-
-
-
-
-
+
-
{% endblock file_modal %}
diff --git a/src/core/utils/shellfm/windows/view/utils/Settings.py b/src/core/utils/shellfm/windows/view/utils/Settings.py
index a6e3ea8..2760ce4 100644
--- a/src/core/utils/shellfm/windows/view/utils/Settings.py
+++ b/src/core/utils/shellfm/windows/view/utils/Settings.py
@@ -57,6 +57,7 @@ class Settings:
subpath = settings["base_of_home"]
HIDE_HIDDEN_FILES = True if settings["hide_hidden_files"] == "true" else False
+ FFMPG_THUMBNLR = FFMPG_THUMBNLR if settings["thumbnailer_path"] == "" else settings["thumbnailer_path"]
go_past_home = True if settings["go_past_home"] == "true" else False
lock_folder = True if settings["lock_folder"] == "true" else False
locked_folders = settings["locked_folders"].split("::::")
diff --git a/src/user_config/webfm/settings.json b/src/user_config/webfm/settings.json
index 2ba7a28..43a0f84 100644
--- a/src/user_config/webfm/settings.json
+++ b/src/user_config/webfm/settings.json
@@ -2,9 +2,10 @@
"settings": {
"base_of_home": "/LazyShare",
"hide_hidden_files": "true",
- "go_past_home": "true",
+ "thumbnailer_path": "ffmpegthumbnailer",
+ "go_past_home": "false",
"lock_folder": "true",
- "locked_folders": "Synced Backup::::venv::::flasks::::Cryptomator",
+ "locked_folders": "Synced Backup::::venv::::flasks::::Encrypted Vault::::Cryptomator",
"mplayer_options": "-quiet -really-quiet -xy 1600 -geometry 50%:50%",
"music_app": "/opt/deadbeef/bin/deadbeef",
"media_app": "mpv",
From 57d52fcddf73f97934561014eb7b112966a022d7 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 11 Nov 2021 23:32:59 -0600
Subject: [PATCH 02/12] Added cover art and background art scraper
---
src/core/routes/Routes.py | 44 ++++-
src/core/utils/tmdbscraper/scraper.py | 22 +++
src/core/utils/tmdbscraper/scraper_config.py | 111 +++++++++++
.../utils/tmdbscraper/scraper_datahelper.py | 54 ++++++
src/core/utils/tmdbscraper/scraper_xbmc.py | 175 ++++++++++++++++++
5 files changed, 404 insertions(+), 2 deletions(-)
create mode 100644 src/core/utils/tmdbscraper/scraper.py
create mode 100644 src/core/utils/tmdbscraper/scraper_config.py
create mode 100644 src/core/utils/tmdbscraper/scraper_datahelper.py
create mode 100644 src/core/utils/tmdbscraper/scraper_xbmc.py
diff --git a/src/core/routes/Routes.py b/src/core/routes/Routes.py
index 306996b..61e3f25 100644
--- a/src/core/routes/Routes.py
+++ b/src/core/routes/Routes.py
@@ -1,5 +1,5 @@
# Python imports
-import os, json, secrets, re, shutil
+import os, json, secrets, requests, shutil, re
# Lib imports
from flask import request, session, render_template, send_from_directory, redirect
@@ -11,9 +11,11 @@ from flask_login import current_user
from core import app, logger, oidc, db, Favorites # Get from __init__
from core.utils import MessageHandler # Get simple message processor
from core.utils.shellfm import WindowController # Get file manager controller
+from core.utils.tmdbscraper import scraper # Get media art scraper
msgHandler = MessageHandler()
+tmdb = scraper.get_tmdb_scraper()
window_controllers = {}
# valid_fname_pat = re.compile(r"/^[a-zA-Z0-9-_\[\]\(\)| ]+$/")
valid_fname_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]{4,20}")
@@ -81,16 +83,54 @@ def listFiles(_hash = None):
return msgHandler.createMessageJSON("danger", error_msg)
- sub_path = view.get_current_sub_path()
+ sub_path = view.get_current_sub_path()
+ current_directory = sub_path.split("/")[-1]
+ if "(" in current_directory and ")" in current_directory:
+ title = current_directory.split("(")[0].strip()
+ startIndex = current_directory.index('(') + 1
+ endIndex = current_directory.index(')')
+ date = current_directory[startIndex:endIndex]
+ video_data = tmdb.search(title, date)
+ background_url = video_data[0]["backdrop_path"]
+ background_pth = view.get_current_directory() + "/" + "000.jpg"
+
+ if not os.path.isfile(background_pth):
+ r = requests.get(background_url, stream = True)
+
+ if r.status_code == 200:
+ r.raw.decode_content = True
+ with open(background_pth,'wb') as f:
+ shutil.copyfileobj(r.raw, f)
+
+ view.load_directory()
+ print('Cover Background Image sucessfully retreived...')
+ else:
+ print('Cover Background Image Couldn\'t be retreived...')
+
+
files = view.get_files_formatted()
fave = db.session.query(Favorites).filter_by(link = sub_path).first()
in_fave = "true" if fave else "false"
+
files.update({'in_fave': in_fave})
return files
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
+@app.route('/api/get-posters', methods=['GET', 'POST'])
+def getPosters():
+ if request.method == 'POST':
+ view = get_window_controller().get_window(1).get_view(0)
+ videos = view.get_videos()
+
+ print(videos)
+ # tmdb.search("Matrix")
+ return videos
+ else:
+ msg = "Can't manage the request type..."
+ return msgHandler.createMessageJSON("danger", msg)
+
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
def fileManagerAction(_type, _hash = None):
view = get_window_controller().get_window(1).get_view(0)
diff --git a/src/core/utils/tmdbscraper/scraper.py b/src/core/utils/tmdbscraper/scraper.py
new file mode 100644
index 0000000..db25e2b
--- /dev/null
+++ b/src/core/utils/tmdbscraper/scraper.py
@@ -0,0 +1,22 @@
+import json
+import sys
+
+from .lib.tmdbscraper.tmdb import TMDBMovieScraper
+from .lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork
+from .lib.tmdbscraper.imdbratings import get_details as get_imdb_details
+from .lib.tmdbscraper.traktratings import get_trakt_ratinginfo
+from .scraper_datahelper import combine_scraped_details_info_and_ratings, \
+ combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params
+from .scraper_config import configure_scraped_details, PathSpecificSettings, \
+ configure_tmdb_artwork, is_fanarttv_configured
+
+
+
+
+
+
+def get_tmdb_scraper():
+ language = 'en-US'
+ certcountry = 'us'
+ ADDON_SETTINGS = None
+ return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry)
diff --git a/src/core/utils/tmdbscraper/scraper_config.py b/src/core/utils/tmdbscraper/scraper_config.py
new file mode 100644
index 0000000..76a042b
--- /dev/null
+++ b/src/core/utils/tmdbscraper/scraper_config.py
@@ -0,0 +1,111 @@
+def configure_scraped_details(details, settings):
+ details = _configure_rating_prefix(details, settings)
+ details = _configure_keeporiginaltitle(details, settings)
+ details = _configure_trailer(details, settings)
+ details = _configure_multiple_studios(details, settings)
+ details = _configure_default_rating(details, settings)
+ details = _configure_tags(details, settings)
+ return details
+
+def configure_tmdb_artwork(details, settings):
+ if 'available_art' not in details:
+ return details
+
+ art = details['available_art']
+ fanart_enabled = settings.getSettingBool('fanart')
+ if not fanart_enabled:
+ if 'fanart' in art:
+ del art['fanart']
+ if 'set.fanart' in art:
+ del art['set.fanart']
+ if not settings.getSettingBool('landscape'):
+ if 'landscape' in art:
+ if fanart_enabled:
+ art['fanart'] = art.get('fanart', []) + art['landscape']
+ del art['landscape']
+ if 'set.landscape' in art:
+ if fanart_enabled:
+ art['set.fanart'] = art.get('set.fanart', []) + art['set.landscape']
+ del art['set.landscape']
+
+ return details
+
+def is_fanarttv_configured(settings):
+ return settings.getSettingBool('enable_fanarttv_artwork')
+
+def _configure_rating_prefix(details, settings):
+ if details['info'].get('mpaa'):
+ details['info']['mpaa'] = settings.getSettingString('certprefix') + details['info']['mpaa']
+ return details
+
+def _configure_keeporiginaltitle(details, settings):
+ if settings.getSettingBool('keeporiginaltitle'):
+ details['info']['title'] = details['info']['originaltitle']
+ return details
+
+def _configure_trailer(details, settings):
+ if details['info'].get('trailer') and not settings.getSettingBool('trailer'):
+ del details['info']['trailer']
+ return details
+
+def _configure_multiple_studios(details, settings):
+ if not settings.getSettingBool('multiple_studios'):
+ details['info']['studio'] = details['info']['studio'][:1]
+ return details
+
+def _configure_default_rating(details, settings):
+ imdb_default = bool(details['ratings'].get('imdb')) and settings.getSettingString('RatingS') == 'IMDb'
+ trakt_default = bool(details['ratings'].get('trakt')) and settings.getSettingString('RatingS') == 'Trakt'
+ default_rating = 'themoviedb'
+ if imdb_default:
+ default_rating = 'imdb'
+ elif trakt_default:
+ default_rating = 'trakt'
+ if default_rating not in details['ratings']:
+ default_rating = list(details['ratings'].keys())[0] if details['ratings'] else None
+ for rating_type in details['ratings'].keys():
+ details['ratings'][rating_type]['default'] = rating_type == default_rating
+ return details
+
+def _configure_tags(details, settings):
+ if not settings.getSettingBool('add_tags'):
+ del details['info']['tag']
+ return details
+
+# pylint: disable=invalid-name
+try:
+ basestring
+except NameError: # py2 / py3
+ basestring = str
+
+#pylint: disable=redefined-builtin
+class PathSpecificSettings(object):
+ # read-only shim for typed `xbmcaddon.Addon().getSetting*` methods
+ def __init__(self, settings_dict, log_fn):
+ self.data = settings_dict
+ self.log = log_fn
+
+ def getSettingBool(self, id):
+ return self._inner_get_setting(id, bool, False)
+
+ def getSettingInt(self, id):
+ return self._inner_get_setting(id, int, 0)
+
+ def getSettingNumber(self, id):
+ return self._inner_get_setting(id, float, 0.0)
+
+ def getSettingString(self, id):
+ return self._inner_get_setting(id, basestring, '')
+
+ def _inner_get_setting(self, setting_id, setting_type, default):
+ value = self.data.get(setting_id)
+ if isinstance(value, setting_type):
+ return value
+ self._log_bad_value(value, setting_id)
+ return default
+
+ def _log_bad_value(self, value, setting_id):
+ if value is None:
+ self.log("requested setting ({0}) was not found.".format(setting_id))
+ else:
+ self.log('failed to load value "{0}" for setting {1}'.format(value, setting_id))
diff --git a/src/core/utils/tmdbscraper/scraper_datahelper.py b/src/core/utils/tmdbscraper/scraper_datahelper.py
new file mode 100644
index 0000000..23504e0
--- /dev/null
+++ b/src/core/utils/tmdbscraper/scraper_datahelper.py
@@ -0,0 +1,54 @@
+import re
+try:
+ from urlparse import parse_qsl
+except ImportError: # py2 / py3
+ from urllib.parse import parse_qsl
+
+# get addon params from the plugin path querystring
+def get_params(argv):
+ result = {'handle': int(argv[0])}
+ if len(argv) < 2 or not argv[1]:
+ return result
+
+ result.update(parse_qsl(argv[1].lstrip('?')))
+ return result
+
+def combine_scraped_details_info_and_ratings(original_details, additional_details):
+ def update_or_set(details, key, value):
+ if key in details:
+ details[key].update(value)
+ else:
+ details[key] = value
+
+ if additional_details:
+ if additional_details.get('info'):
+ update_or_set(original_details, 'info', additional_details['info'])
+ if additional_details.get('ratings'):
+ update_or_set(original_details, 'ratings', additional_details['ratings'])
+ return original_details
+
+def combine_scraped_details_available_artwork(original_details, additional_details):
+ if additional_details and additional_details.get('available_art'):
+ available_art = additional_details['available_art']
+ if not original_details.get('available_art'):
+ original_details['available_art'] = available_art
+ else:
+ for arttype, artlist in available_art.items():
+ original_details['available_art'][arttype] = \
+ artlist + original_details['available_art'].get(arttype, [])
+
+ return original_details
+
+def find_uniqueids_in_text(input_text):
+ result = {}
+ res = re.search(r'(themoviedb.org/movie/)([0-9]+)', input_text)
+ if (res):
+ result['tmdb'] = res.group(2)
+ res = re.search(r'imdb....?/title/tt([0-9]+)', input_text)
+ if (res):
+ result['imdb'] = 'tt' + res.group(1)
+ else:
+ res = re.search(r'imdb....?/Title\?t{0,2}([0-9]+)', input_text)
+ if (res):
+ result['imdb'] = 'tt' + res.group(1)
+ return result
diff --git a/src/core/utils/tmdbscraper/scraper_xbmc.py b/src/core/utils/tmdbscraper/scraper_xbmc.py
new file mode 100644
index 0000000..c976407
--- /dev/null
+++ b/src/core/utils/tmdbscraper/scraper_xbmc.py
@@ -0,0 +1,175 @@
+import json
+import sys
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+
+from lib.tmdbscraper.tmdb import TMDBMovieScraper
+from lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork
+from lib.tmdbscraper.imdbratings import get_details as get_imdb_details
+from lib.tmdbscraper.traktratings import get_trakt_ratinginfo
+from scraper_datahelper import combine_scraped_details_info_and_ratings, \
+ combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params
+from scraper_config import configure_scraped_details, PathSpecificSettings, \
+ configure_tmdb_artwork, is_fanarttv_configured
+
+ADDON_SETTINGS = xbmcaddon.Addon()
+ID = ADDON_SETTINGS.getAddonInfo('id')
+
+def log(msg, level=xbmc.LOGDEBUG):
+ xbmc.log(msg='[{addon}]: {msg}'.format(addon=ID, msg=msg), level=level)
+
+def get_tmdb_scraper(settings):
+ language = settings.getSettingString('language')
+ certcountry = settings.getSettingString('tmdbcertcountry')
+ return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry)
+
+def search_for_movie(title, year, handle, settings):
+ log("Find movie with title '{title}' from year '{year}'".format(title=title, year=year), xbmc.LOGINFO)
+ title = _strip_trailing_article(title)
+ search_results = get_tmdb_scraper(settings).search(title, year)
+ if not search_results:
+ return
+ if 'error' in search_results:
+ header = "The Movie Database Python error searching with web service TMDB"
+ xbmcgui.Dialog().notification(header, search_results['error'], xbmcgui.NOTIFICATION_WARNING)
+ log(header + ': ' + search_results['error'], xbmc.LOGWARNING)
+ return
+
+ for movie in search_results:
+ listitem = _searchresult_to_listitem(movie)
+ uniqueids = {'tmdb': str(movie['id'])}
+ xbmcplugin.addDirectoryItem(handle=handle, url=build_lookup_string(uniqueids),
+ listitem=listitem, isFolder=True)
+
+_articles = [prefix + article for prefix in (', ', ' ') for article in ("the", "a", "an")]
+def _strip_trailing_article(title):
+ title = title.lower()
+ for article in _articles:
+ if title.endswith(article):
+ return title[:-len(article)]
+ return title
+
+def _searchresult_to_listitem(movie):
+ movie_info = {'title': movie['title']}
+ movie_label = movie['title']
+
+ movie_year = movie['release_date'].split('-')[0] if movie.get('release_date') else None
+ if movie_year:
+ movie_label += ' ({})'.format(movie_year)
+ movie_info['year'] = movie_year
+
+ listitem = xbmcgui.ListItem(movie_label, offscreen=True)
+
+ listitem.setInfo('video', movie_info)
+ if movie['poster_path']:
+ listitem.setArt({'thumb': movie['poster_path']})
+
+ return listitem
+
+# Low limit because a big list of artwork can cause trouble in some cases
+# (a column can be too large for the MySQL integration),
+# and how useful is a big list anyway? Not exactly rhetorical, this is an experiment.
+IMAGE_LIMIT = 10
+
+def add_artworks(listitem, artworks):
+ for arttype, artlist in artworks.items():
+ if arttype == 'fanart':
+ continue
+ for image in artlist[:IMAGE_LIMIT]:
+ listitem.addAvailableArtwork(image['url'], arttype)
+
+ fanart_to_set = [{'image': image['url'], 'preview': image['preview']}
+ for image in artworks['fanart'][:IMAGE_LIMIT]]
+ listitem.setAvailableFanart(fanart_to_set)
+
+def get_details(input_uniqueids, handle, settings):
+ if not input_uniqueids:
+ return False
+ details = get_tmdb_scraper(settings).get_details(input_uniqueids)
+ if not details:
+ return False
+ if 'error' in details:
+ header = "The Movie Database Python error with web service TMDB"
+ xbmcgui.Dialog().notification(header, details['error'], xbmcgui.NOTIFICATION_WARNING)
+ log(header + ': ' + details['error'], xbmc.LOGWARNING)
+ return False
+
+ details = configure_tmdb_artwork(details, settings)
+
+ if settings.getSettingString('RatingS') == 'IMDb' or settings.getSettingBool('imdbanyway'):
+ imdbinfo = get_imdb_details(details['uniqueids'])
+ if 'error' in imdbinfo:
+ header = "The Movie Database Python error with website IMDB"
+ log(header + ': ' + imdbinfo['error'], xbmc.LOGWARNING)
+ else:
+ details = combine_scraped_details_info_and_ratings(details, imdbinfo)
+
+ if settings.getSettingString('RatingS') == 'Trakt' or settings.getSettingBool('traktanyway'):
+ traktinfo = get_trakt_ratinginfo(details['uniqueids'])
+ details = combine_scraped_details_info_and_ratings(details, traktinfo)
+
+ if is_fanarttv_configured(settings):
+ fanarttv_info = get_fanarttv_artwork(details['uniqueids'],
+ settings.getSettingString('fanarttv_clientkey'),
+ settings.getSettingString('fanarttv_language'),
+ details['_info']['set_tmdbid'])
+ details = combine_scraped_details_available_artwork(details, fanarttv_info)
+
+ details = configure_scraped_details(details, settings)
+
+ listitem = xbmcgui.ListItem(details['info']['title'], offscreen=True)
+ listitem.setInfo('video', details['info'])
+ listitem.setCast(details['cast'])
+ listitem.setUniqueIDs(details['uniqueids'], 'tmdb')
+ add_artworks(listitem, details['available_art'])
+
+ for rating_type, value in details['ratings'].items():
+ if 'votes' in value:
+ listitem.setRating(rating_type, value['rating'], value['votes'], value['default'])
+ else:
+ listitem.setRating(rating_type, value['rating'], defaultt=value['default'])
+
+ xbmcplugin.setResolvedUrl(handle=handle, succeeded=True, listitem=listitem)
+ return True
+
+def find_uniqueids_in_nfo(nfo, handle):
+ uniqueids = find_uniqueids_in_text(nfo)
+ if uniqueids:
+ listitem = xbmcgui.ListItem(offscreen=True)
+ xbmcplugin.addDirectoryItem(
+ handle=handle, url=build_lookup_string(uniqueids), listitem=listitem, isFolder=True)
+
+def build_lookup_string(uniqueids):
+ return json.dumps(uniqueids)
+
+def parse_lookup_string(uniqueids):
+ try:
+ return json.loads(uniqueids)
+ except ValueError:
+ log("Can't parse this lookup string, is it from another add-on?\n" + uniqueids, xbmc.LOGWARNING)
+ return None
+
+def run():
+ params = get_params(sys.argv[1:])
+ enddir = True
+ if 'action' in params:
+ settings = ADDON_SETTINGS if not params.get('pathSettings') else \
+ PathSpecificSettings(json.loads(params['pathSettings']), lambda msg: log(msg, xbmc.LOGWARNING))
+ action = params["action"]
+ if action == 'find' and 'title' in params:
+ search_for_movie(params["title"], params.get("year"), params['handle'], settings)
+ elif action == 'getdetails' and 'url' in params:
+ enddir = not get_details(parse_lookup_string(params["url"]), params['handle'], settings)
+ elif action == 'NfoUrl' and 'nfo' in params:
+ find_uniqueids_in_nfo(params["nfo"], params['handle'])
+ else:
+ log("unhandled action: " + action, xbmc.LOGWARNING)
+ else:
+ log("No action in 'params' to act on", xbmc.LOGWARNING)
+ if enddir:
+ xbmcplugin.endOfDirectory(params['handle'])
+
+if __name__ == '__main__':
+ run()
From 978d4de21223c50168bc820ef6d5413994076f96 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Fri, 12 Nov 2021 01:42:08 -0600
Subject: [PATCH 03/12] Updated video container responsiveness
---
src/core/static/css/main.css | 11 +++++++++--
src/core/static/js/video-events.js | 15 +++++++++++----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/src/core/static/css/main.css b/src/core/static/css/main.css
index bd9dd02..df670ca 100644
--- a/src/core/static/css/main.css
+++ b/src/core/static/css/main.css
@@ -48,6 +48,12 @@
}
+.page-alert-zone-container {
+ position: sticky;
+ top: 0em;
+ z-index: 2;
+}
+
.card-title-text {
white-space: nowrap;
@@ -68,11 +74,12 @@
}
.viewer {
- max-width: 55em;
+ max-width: 100% !important;
+ height: auto !important;
+ max-height: 100% !important;
}
-
/* Other message text colors */
.errorTxt { color: rgb(170, 18, 18); }
.warningTxt { color: rgb(255, 168, 0); }
diff --git a/src/core/static/js/video-events.js b/src/core/static/js/video-events.js
index 8c5a429..a4ee4fd 100644
--- a/src/core/static/js/video-events.js
+++ b/src/core/static/js/video-events.js
@@ -33,14 +33,21 @@ const togglePlay = (video) => {
const setFullscreenSettings = (parentElm, video) => {
parentElm.requestFullscreen();
video.classList.remove("viewer");
- video.style.cursor = 'none';
- video.style.height = 'inherit';
+ video.style.cursor = 'none';
+ video.style.height = 'inherit';
+ video.style.width = 'inherit';
+
+ if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){
+ video.style.transform = 'rotate(90deg)';
+ }
}
const unsetFullscreenSettings = (video) => {
video.classList.add("viewer");
- video.style.cursor = '';
- video.style.height = '';
+ video.style.transform = '';
+ video.style.cursor = '';
+ video.style.height = '';
+ video.style.width = '';
}
const toggleFullscreen = (video) => {
From b30a8f4b447e59cfd5682821716f6c22d4e885aa Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Fri, 18 Mar 2022 17:18:13 -0500
Subject: [PATCH 04/12] added size info, new sort logic
---
src/core/static/css/main.css | 5 +++
src/core/static/js/react-ui-logic.js | 16 +++++--
src/core/utils/shellfm/windows/view/View.py | 46 +++++++++++++++++----
3 files changed, 56 insertions(+), 11 deletions(-)
diff --git a/src/core/static/css/main.css b/src/core/static/css/main.css
index df670ca..7e471bb 100644
--- a/src/core/static/css/main.css
+++ b/src/core/static/css/main.css
@@ -79,6 +79,11 @@
max-height: 100% !important;
}
+.float-right {
+ float: right;
+ text-align: right;
+ font-size: initial;
+}
/* Other message text colors */
.errorTxt { color: rgb(170, 18, 18); }
diff --git a/src/core/static/js/react-ui-logic.js b/src/core/static/js/react-ui-logic.js
index 8121825..57fc44c 100644
--- a/src/core/static/js/react-ui-logic.js
+++ b/src/core/static/js/react-ui-logic.js
@@ -54,6 +54,7 @@ class FilesList extends React.Component {
for (let file of files) {
const name = file[0];
const hash = file[1];
+ const fsize = file[2];
let extension = re.exec( name.toLowerCase() )[1] ? name : "file.dir";
let data = setFileIconType(extension);
let icon = data[0];
@@ -63,13 +64,22 @@ class FilesList extends React.Component {
if (filetype === "video") {
card_header = name;
- card_body = ;
+ card_body =
+
+ {fsize}
+ ;
} else if (filetype === "image") {
card_header = name;
- card_body = ;
+ card_body =
+
+ {fsize}
+ ;
} else {
card_header = ;
- card_body = name;
+ card_body =
+ {name}
+ {fsize}
+ ;
}
diff --git a/src/core/utils/shellfm/windows/view/View.py b/src/core/utils/shellfm/windows/view/View.py
index 2a37a5f..70a97d4 100644
--- a/src/core/utils/shellfm/windows/view/View.py
+++ b/src/core/utils/shellfm/windows/view/View.py
@@ -1,6 +1,5 @@
# Python imports
-import hashlib
-import os
+import hashlib, os, re
from os import listdir
from os.path import isdir, isfile, join
@@ -56,11 +55,11 @@ class View(Settings, Launcher, Path):
else:
self.dirs.append(f)
- self.dirs.sort()
- self.vids.sort()
- self.images.sort()
- self.desktop.sort()
- self.ungrouped.sort()
+ self.dirs.sort(key=self._natural_keys)
+ self.vids.sort(key=self._natural_keys)
+ self.images.sort(key=self._natural_keys)
+ self.desktop.sort(key=self._natural_keys)
+ self.ungrouped.sort(key=self._natural_keys)
self.files = self.dirs + self.vids + self.images + self.desktop + self.ungrouped
@@ -68,9 +67,12 @@ class View(Settings, Launcher, Path):
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
def hashSet(self, arry):
+ path = self.get_path()
data = []
for arr in arry:
- data.append([arr, self.hashText(arr)])
+ file = f"{path}/{arr}"
+ size = "4K" if isdir(file) else self.sizeof_fmt(os.path.getsize(file))
+ data.append([arr, self.hashText(arr), size])
return data
def get_path_part_from_hash(self, hash):
@@ -167,3 +169,31 @@ class View(Settings, Launcher, Path):
def get_ungrouped(self):
return self.hashSet(self.ungrouped)
+
+ def sizeof_fmt(self, num, suffix="B"):
+ for unit in ["", "K", "M", "G", "T", "Pi", "Ei", "Zi"]:
+ if abs(num) < 1024.0:
+ return f"{num:3.1f} {unit}{suffix}"
+ num /= 1024.0
+ return f"{num:.1f} Yi{suffix}"
+
+ def get_dir_size(self, sdir):
+ """Get the size of a directory. Based on code found online."""
+ size = os.path.getsize(sdir)
+
+ for item in listdir(sdir):
+ item = join(sdir, item)
+
+ if isfile(item):
+ size = size + os.path.getsize(item)
+ elif isdir(item):
+ size = size + self.get_dir_size(item)
+
+ return size
+
+
+ def _atoi(self, text):
+ return int(text) if text.isdigit() else text
+
+ def _natural_keys(self, text):
+ return [ self._atoi(c) for c in re.split('(\d+)',text) ]
From 22c437e8edbe7ee045ff91825abd95f3e18b1dcd Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Mon, 13 Jun 2022 21:51:51 -0500
Subject: [PATCH 05/12] addedd rtsp streamer inferastructure, applied fstrings,
updated method names to align with standards in project
---
src/core/config.py | 10 +-
src/core/routes/Routes.py | 86 +++---
src/core/utils/MessageHandler.py | 2 +-
.../utils/shellfm/windows/WindowController.py | 12 +-
src/core/utils/shellfm/windows/view/View.py | 2 +-
.../shellfm/windows/view/utils/Launcher.py | 25 +-
.../shellfm/windows/view/utils/Settings.py | 22 +-
.../windows/view/utils/rtsp-simple-server | Bin 0 -> 20226064 bytes
.../windows/view/utils/rtsp-simple-server.yml | 281 ++++++++++++++++++
9 files changed, 367 insertions(+), 73 deletions(-)
create mode 100755 src/core/utils/shellfm/windows/view/utils/rtsp-simple-server
create mode 100644 src/core/utils/shellfm/windows/view/utils/rtsp-simple-server.yml
diff --git a/src/core/config.py b/src/core/config.py
index 7f2472e..acd6569 100644
--- a/src/core/config.py
+++ b/src/core/config.py
@@ -29,7 +29,7 @@ class Config(object):
LOGIN_PATH = "OIDC" # Value can be OIDC or FLASK_LOGIN
OIDC_TOKEN_TYPE_HINT = 'access_token'
APP_REDIRECT_URI = "https%3A%2F%2Fwww.webfm.com%2F" # This path is submitted as the redirect URI in certain code flows
- OIDC_CLIENT_SECRETS = ROOT_FILE_PTH + '/client_secrets.json'
+ OIDC_CLIENT_SECRETS = f'{ROOT_FILE_PTH}/client_secrets.json'
OIDC_ID_TOKEN_COOKIE_SECURE = True
OIDC_REQUIRE_VERIFIED_EMAIL = False
OIDC_USER_INFO_ENABLED = True
@@ -38,14 +38,14 @@ class Config(object):
'https://www.ssoapps.com/auth/realms/apps'
]
- STATIC_FPTH = ROOT_FILE_PTH + "/static"
+ STATIC_FPTH = f"{ROOT_FILE_PTH}/static"
REL_THUMBS_PTH = "static/imgs/thumbnails" # Used for flask thumbnail return
# We are overiding some of the the shellmen view settings with these to make it all work with flask.
# These are passed along to the shellmen view from the Routes file upon the window controller creation.
- ABS_THUMBS_PTH = STATIC_FPTH + "/imgs/thumbnails" # Used for thumbnail generation
- REMUX_FOLDER = STATIC_FPTH + "/remuxs" # Remuxed files folder
- FFMPG_THUMBNLR = STATIC_FPTH + "/ffmpegthumbnailer" # Thumbnail generator binary
+ ABS_THUMBS_PTH = f"{STATIC_FPTH}/imgs/thumbnails" # Used for thumbnail generation
+ REMUX_FOLDER = f"{STATIC_FPTH}/remuxs" # Remuxed files folder
+ FFMPG_THUMBNLR = f"{STATIC_FPTH}/ffmpegthumbnailer" # Thumbnail generator binary
diff --git a/src/core/routes/Routes.py b/src/core/routes/Routes.py
index 61e3f25..ad9e526 100644
--- a/src/core/routes/Routes.py
+++ b/src/core/routes/Routes.py
@@ -67,11 +67,11 @@ def listFiles(_hash = None):
msg = "Log in with an Admin privlidged user to view the requested path!"
is_locked = view.is_folder_locked(_hash)
if is_locked and not oidc.user_loggedin:
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
elif is_locked and oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
if dot_dots[0][1] != _hash and dot_dots[1][1] != _hash:
path = view.get_path_part_from_hash(_hash)
@@ -80,7 +80,7 @@ def listFiles(_hash = None):
error_msg = view.get_error_message()
if error_msg != None:
view.unset_error_message()
- return msgHandler.createMessageJSON("danger", error_msg)
+ return msgHandler.create_JSON_message("danger", error_msg)
sub_path = view.get_current_sub_path()
@@ -92,7 +92,7 @@ def listFiles(_hash = None):
date = current_directory[startIndex:endIndex]
video_data = tmdb.search(title, date)
background_url = video_data[0]["backdrop_path"]
- background_pth = view.get_current_directory() + "/" + "000.jpg"
+ background_pth = f"{view.get_current_directory()}/000.jpg"
if not os.path.isfile(background_pth):
r = requests.get(background_url, stream = True)
@@ -116,20 +116,17 @@ def listFiles(_hash = None):
return files
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/get-posters', methods=['GET', 'POST'])
def getPosters():
if request.method == 'POST':
view = get_window_controller().get_window(1).get_view(0)
videos = view.get_videos()
-
- print(videos)
- # tmdb.search("Matrix")
return videos
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
def fileManagerAction(_type, _hash = None):
@@ -138,7 +135,7 @@ def fileManagerAction(_type, _hash = None):
if _type == "reset-path" and _hash == "None":
view.set_to_home()
msg = "Returning to home directory..."
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
folder = view.get_current_directory()
file = view.get_path_part_from_hash(_hash)
@@ -146,34 +143,39 @@ def fileManagerAction(_type, _hash = None):
logger.debug(fpath)
if _type == "files":
- return send_from_directory(folder, file)
+ logger.debug(f"Downloading:\n\tDirectory: {folder}\n\tFile: {file}")
+ return send_from_directory(directory=folder, filename=file)
if _type == "remux":
# NOTE: Need to actually implimint a websocket to communicate back to client that remux has completed.
# As is, the remux thread hangs until completion and client tries waiting until server reaches connection timeout.
# I.E....this is stupid but for now works better than nothing
- good_result = view.remuxVideo(_hash, fpath)
+ good_result = view.remux_video(_hash, fpath)
if good_result:
return '{"path":"static/remuxs/' + _hash + '.mp4"}'
else:
msg = "Remuxing: Remux failed or took too long; please, refresh the page and try again..."
- return msgHandler.createMessageJSON("success", msg)
- if _type == "run-locally":
- msg = "Opened media..."
- view.openFilelocally(fpath)
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
+
+ if _type == "remux":
+ stream_target = view.remux_video(_hash, fpath)
# NOTE: Positionally protecting actions further down that are privlidged
# Be aware of ordering!
msg = "Log in with an Admin privlidged user to do this action!"
if not oidc.user_loggedin:
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
+ if _type == "run-locally":
+ msg = "Opened media..."
+ view.open_file_locally(fpath)
+ return msgHandler.create_JSON_message("success", msg)
+
if _type == "delete":
try:
msg = f"[Success] Deleted the file/folder -->: {file} !"
@@ -181,10 +183,10 @@ def fileManagerAction(_type, _hash = None):
os.unlink(fpath)
else:
shutil.rmtree(fpath)
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
except Exception as e:
msg = "[Error] Unable to delete the file/folder...."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/list-favorites', methods=['GET', 'POST'])
@@ -198,7 +200,7 @@ def listFavorites():
return '{"faves_list":' + json.dumps(faves) + '}'
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/load-favorite/<_id>', methods=['GET', 'POST'])
def loadFavorite(_id):
@@ -212,10 +214,10 @@ def loadFavorite(_id):
except Exception as e:
print(repr(e))
msg = "Incorrect Favorites ID..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/manage-favorites/<_action>', methods=['GET', 'POST'])
@@ -235,13 +237,13 @@ def manageFavorites(_action):
msg = "Deleted from Favorites successfully..."
else:
msg = "Couldn't handle action for favorites item..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
db.session.commit()
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/api/create/<_type>', methods=['GET', 'POST'])
@@ -249,42 +251,42 @@ def create_item(_type = None):
if request.method == 'POST':
msg = "Log in with an Admin privlidged user to upload files!"
if not oidc.user_loggedin:
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
TYPE = _type.strip()
FNAME = str(request.values['fname']).strip()
if not re.fullmatch(valid_fname_pat, FNAME):
msg = "A new item name can only contain alphanumeric, -, _, |, [], (), or spaces and must be minimum of 4 and max of 20 characters..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
view = get_window_controller().get_window(1).get_view(0)
folder = view.get_current_directory()
- new_item = folder + '/' + FNAME
+ new_item = f"{folder}/{FNAME}"
try:
if TYPE == "dir":
os.mkdir(new_item)
elif TYPE == "file":
- open(new_item + ".txt", 'a').close()
+ open(f"{new_item}.txt", 'a').close()
else:
msg = "Couldn't handle action type for api create..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
except Exception as e:
print(repr(e))
msg = "Couldn't create file/folder. An unexpected error occured..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
msg = "[Success] created the file/dir..."
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
@app.route('/upload', methods=['GET', 'POST'])
@@ -292,15 +294,15 @@ def upload():
if request.method == 'POST' and len(request.files) > 0:
msg = "Log in with an Admin privlidged user to upload files!"
if not oidc.user_loggedin:
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
view = get_window_controller().get_window(1).get_view(0)
folder = view.get_current_directory()
- UPLOADS_PTH = folder + '/'
+ UPLOADS_PTH = f'{folder}/'
files = UploadSet('files', ALL, default_dest=lambda x: UPLOADS_PTH)
configure_uploads(app, files)
@@ -310,10 +312,10 @@ def upload():
except Exception as e:
print(repr(e))
msg = "[Error] Failed to upload some or all of the file(s)..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
msg = "[Success] Uploaded file(s)..."
- return msgHandler.createMessageJSON("success", msg)
+ return msgHandler.create_JSON_message("success", msg)
else:
msg = "Can't manage the request type..."
- return msgHandler.createMessageJSON("danger", msg)
+ return msgHandler.create_JSON_message("danger", msg)
diff --git a/src/core/utils/MessageHandler.py b/src/core/utils/MessageHandler.py
index f6538f2..9bf6368 100644
--- a/src/core/utils/MessageHandler.py
+++ b/src/core/utils/MessageHandler.py
@@ -10,5 +10,5 @@ class MessageHandler:
print("MessageHandler initialized...")
- def createMessageJSON(self, type, text):
+ def create_JSON_message(self, type, text):
return '{"message": { "type": "' + type + '", "text": "' + text + '" } }'
diff --git a/src/core/utils/shellfm/windows/WindowController.py b/src/core/utils/shellfm/windows/WindowController.py
index 30cc30d..4c81aa0 100644
--- a/src/core/utils/shellfm/windows/WindowController.py
+++ b/src/core/utils/shellfm/windows/WindowController.py
@@ -11,7 +11,7 @@ class WindowController:
if window.id == win_id:
return window
- raise("No Window by ID {} found!".format(win_id))
+ raise(f"No Window by ID {win_id} found!")
def get_windows(self):
return self.windows
@@ -19,7 +19,7 @@ class WindowController:
def add_window(self):
window = Window()
window.id = len(self.windows) + 1
- window.name = "window_" + str(window.id)
+ window.name = f"window_{window.id}"
window.create_view()
self.windows.append(window)
@@ -48,10 +48,10 @@ class WindowController:
def list_windows(self):
for window in self.windows:
print("\n[ Window ]")
- print("ID: " + str(window.id))
- print("Name: " + window.name)
- print("Nickname: " + window.nickname)
- print("View Count: " + str( len(window.views) ))
+ print("ID: {window.id}")
+ print("Name: {window.name}")
+ print("Nickname: {window.nickname}")
+ print("View Count: {len(window.views)}")
def list_views_from_window(self, win_id):
diff --git a/src/core/utils/shellfm/windows/view/View.py b/src/core/utils/shellfm/windows/view/View.py
index 70a97d4..1f1001a 100644
--- a/src/core/utils/shellfm/windows/view/View.py
+++ b/src/core/utils/shellfm/windows/view/View.py
@@ -157,7 +157,7 @@ class View(Settings, Launcher, Path):
if not os.path.exists(hashImgPth) :
fullPath = join(current_directory, video[0])
self.logger.debug(f"Hash Path: {hashImgPth}\nFile Path: {fullPath}")
- self.generateVideoThumbnail(fullPath, hashImgPth)
+ self.generate_video_thumbnail(fullPath, hashImgPth)
return videos_set
diff --git a/src/core/utils/shellfm/windows/view/utils/Launcher.py b/src/core/utils/shellfm/windows/view/utils/Launcher.py
index 6e3dcac..cc09d61 100644
--- a/src/core/utils/shellfm/windows/view/utils/Launcher.py
+++ b/src/core/utils/shellfm/windows/view/utils/Launcher.py
@@ -9,7 +9,7 @@ import os, subprocess, threading
class Launcher:
- def openFilelocally(self, file):
+ def open_file_locally(self, file):
lowerName = file.lower()
command = []
@@ -38,14 +38,25 @@ class Launcher:
subprocess.Popen(command, start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
- def remuxVideo(self, hash, file):
- remux_vid_pth = self.REMUX_FOLDER + "/" + hash + ".mp4"
+ def create_stream(self, hash, file):
+ # ffmpeg -re -stream_loop -1 -i "